summaryrefslogtreecommitdiffstats
path: root/ash/system/toast/toast_overlay.cc
diff options
context:
space:
mode:
Diffstat (limited to 'ash/system/toast/toast_overlay.cc')
-rw-r--r--ash/system/toast/toast_overlay.cc275
1 files changed, 275 insertions, 0 deletions
diff --git a/ash/system/toast/toast_overlay.cc b/ash/system/toast/toast_overlay.cc
new file mode 100644
index 0000000..3864c9c
--- /dev/null
+++ b/ash/system/toast/toast_overlay.cc
@@ -0,0 +1,275 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/system/toast/toast_overlay.h"
+
+#include "ash/screen_util.h"
+#include "ash/shelf/shelf.h"
+#include "ash/shelf/shelf_layout_manager.h"
+#include "ash/shell.h"
+#include "ash/shell_window_ids.h"
+#include "ash/wm/window_animations.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/thread_task_runner_handle.h"
+#include "grit/ash_strings.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/font_list.h"
+#include "ui/views/border.h"
+#include "ui/views/controls/button/label_button.h"
+#include "ui/views/controls/label.h"
+#include "ui/views/layout/box_layout.h"
+#include "ui/views/view.h"
+#include "ui/views/widget/widget.h"
+#include "ui/views/widget/widget_delegate.h"
+
+namespace ash {
+
+namespace {
+
+// Horizontal offset of the overlay from the bottom of the screen.
+const int kVerticalOffset = 5;
+
+// Font style used for modifier key labels.
+const ui::ResourceBundle::FontStyle kTextFontStyle =
+ ui::ResourceBundle::MediumFont;
+
+// Duration of slide animation when overlay is shown or hidden.
+const int kSlideAnimationDurationMs = 100;
+
+// Colors for the dismiss button.
+const SkColor kButtonBackgroundColor = SkColorSetARGB(0xFF, 0x32, 0x32, 0x32);
+const SkColor kNormalButtonColor = SkColorSetARGB(0xFF, 0x64, 0xA5, 0xF5);
+const SkColor kHoveredButtonColor = SkColorSetARGB(0xFF, 0xE3, 0xF2, 0xFD);
+const SkColor kPressedButtonColor = SkColorSetARGB(0xFF, 0x64, 0xA5, 0xF5);
+
+} // anonymous namespace
+
+///////////////////////////////////////////////////////////////////////////////
+// ToastOverlayLabel
+class ToastOverlayLabel : public views::Label {
+ public:
+ explicit ToastOverlayLabel(const std::string& label);
+ ~ToastOverlayLabel() override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ToastOverlayLabel);
+};
+
+ToastOverlayLabel::ToastOverlayLabel(const std::string& label) {
+ ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
+
+ SetText(base::UTF8ToUTF16(label));
+ SetHorizontalAlignment(gfx::ALIGN_LEFT);
+ SetFontList(rb->GetFontList(kTextFontStyle));
+ SetAutoColorReadabilityEnabled(false);
+ SetFocusable(false);
+ SetEnabledColor(SK_ColorWHITE);
+ SetDisabledColor(SK_ColorWHITE);
+ SetSubpixelRenderingEnabled(false);
+}
+
+ToastOverlayLabel::~ToastOverlayLabel() {}
+
+///////////////////////////////////////////////////////////////////////////////
+// ToastOverlayButton
+class ToastOverlayButton : public views::LabelButton {
+ public:
+ explicit ToastOverlayButton(views::ButtonListener* listener,
+ const base::string16& label);
+ ~ToastOverlayButton() override {}
+
+ private:
+ friend class ToastOverlay; // for ToastOverlay::ClickDismissButtonForTesting.
+
+ DISALLOW_COPY_AND_ASSIGN(ToastOverlayButton);
+};
+
+ToastOverlayButton::ToastOverlayButton(views::ButtonListener* listener,
+ const base::string16& label)
+ : views::LabelButton(listener, label) {
+ ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
+
+ SetTextColor(STATE_NORMAL, kNormalButtonColor);
+ SetTextColor(STATE_HOVERED, kHoveredButtonColor);
+ SetTextColor(STATE_PRESSED, kPressedButtonColor);
+ SetFontList(rb->GetFontList(kTextFontStyle));
+ SetBorder(views::Border::NullBorder());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// ToastOverlayView
+class ToastOverlayView : public views::View, public views::ButtonListener {
+ public:
+ // This object is not owned by the views hiearchy or by the widget.
+ ToastOverlayView(ToastOverlay* overlay, const std::string& text);
+ ~ToastOverlayView() override;
+
+ // views::View overrides:
+ void OnPaint(gfx::Canvas* canvas) override;
+
+ ToastOverlayButton* button() { return button_; }
+
+ private:
+ ToastOverlay* overlay_; // weak
+ ToastOverlayButton* button_; // weak
+
+ void ButtonPressed(views::Button* sender, const ui::Event& event) override;
+
+ DISALLOW_COPY_AND_ASSIGN(ToastOverlayView);
+};
+
+ToastOverlayView::ToastOverlayView(ToastOverlay* overlay,
+ const std::string& text)
+ : overlay_(overlay),
+ button_(new ToastOverlayButton(
+ this,
+ l10n_util::GetStringUTF16(IDS_ASH_TOAST_DISMISS_BUTTON))) {
+ const gfx::Font& font =
+ ui::ResourceBundle::GetSharedInstance().GetFont(kTextFontStyle);
+ int font_size = font.GetFontSize();
+
+ // Text should have a margin of 0.5 times the font size on each side, so
+ // the spacing between two labels will be the same as the font size.
+ int horizontal_spacing = font_size * 2;
+ int vertical_spacing = font_size;
+ int child_spacing = font_size * 4;
+
+ SetLayoutManager(new views::BoxLayout(views::BoxLayout::kHorizontal,
+ horizontal_spacing, vertical_spacing,
+ child_spacing));
+
+ ToastOverlayLabel* label = new ToastOverlayLabel(text);
+ AddChildView(label);
+ label->SetVisible(true);
+
+ AddChildView(button_);
+}
+
+ToastOverlayView::~ToastOverlayView() {}
+
+void ToastOverlayView::OnPaint(gfx::Canvas* canvas) {
+ SkPaint paint;
+ paint.setStyle(SkPaint::kFill_Style);
+ paint.setColor(kButtonBackgroundColor);
+ canvas->DrawRoundRect(GetLocalBounds(), 2, paint);
+ views::View::OnPaint(canvas);
+}
+
+void ToastOverlayView::ButtonPressed(views::Button* sender,
+ const ui::Event& event) {
+ overlay_->Show(false);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// ToastOverlay
+ToastOverlay::ToastOverlay(Delegate* delegate, const std::string& text)
+ : delegate_(delegate),
+ text_(text),
+ overlay_view_(new ToastOverlayView(this, text)),
+ widget_size_(overlay_view_->GetPreferredSize()) {
+ views::Widget::InitParams params;
+ params.type = views::Widget::InitParams::TYPE_POPUP;
+ params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
+ params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+ params.accept_events = true;
+ params.keep_on_top = true;
+ params.remove_standard_frame = true;
+ params.bounds = CalculateOverlayBounds();
+ // Show toasts above the app list and below the lock screen.
+ params.parent = Shell::GetContainer(Shell::GetTargetRootWindow(),
+ kShellWindowId_SystemModalContainer);
+ overlay_widget_.reset(new views::Widget);
+ overlay_widget_->Init(params);
+ overlay_widget_->SetVisibilityChangedAnimationsEnabled(true);
+ overlay_widget_->SetContentsView(overlay_view_.get());
+ overlay_widget_->SetBounds(CalculateOverlayBounds());
+ overlay_widget_->GetNativeView()->SetName("ToastOverlay");
+
+ gfx::NativeWindow native_view = overlay_widget_->GetNativeView();
+ wm::SetWindowVisibilityAnimationType(
+ native_view, wm::WINDOW_VISIBILITY_ANIMATION_TYPE_VERTICAL);
+ wm::SetWindowVisibilityAnimationDuration(
+ native_view,
+ base::TimeDelta::FromMilliseconds(kSlideAnimationDurationMs));
+}
+
+ToastOverlay::~ToastOverlay() {
+ gfx::NativeWindow native_view = overlay_widget_->GetNativeView();
+ wm::SetWindowVisibilityAnimationTransition(native_view, wm::ANIMATE_NONE);
+
+ // Remove ourself from the animator to avoid being re-entrantly called in
+ // |overlay_widget_|'s destructor.
+ ui::Layer* layer = overlay_widget_->GetLayer();
+ if (layer) {
+ ui::LayerAnimator* animator = layer->GetAnimator();
+ if (animator)
+ animator->RemoveObserver(this);
+ }
+
+ overlay_widget_->Close();
+}
+
+void ToastOverlay::Show(bool visible) {
+ if (is_visible_ == visible)
+ return;
+
+ is_visible_ = visible;
+
+ overlay_widget_->GetLayer()->GetAnimator()->AddObserver(this);
+
+ if (is_visible_)
+ overlay_widget_->Show();
+ else
+ overlay_widget_->Hide();
+}
+
+gfx::Rect ToastOverlay::CalculateOverlayBounds() {
+ ShelfLayoutManager* shelf_layout_manager =
+ Shelf::ForPrimaryDisplay()->shelf_layout_manager();
+ gfx::Rect work_area_bounds = shelf_layout_manager->user_work_area_bounds();
+
+ gfx::Rect bounds = shelf_layout_manager->user_work_area_bounds();
+ int target_y = bounds.bottom() - widget_size_.height() - kVerticalOffset;
+ bounds.ClampToCenteredSize(widget_size_);
+ bounds.set_y(target_y);
+ return bounds;
+}
+
+void ToastOverlay::OnLayerAnimationEnded(ui::LayerAnimationSequence* sequence) {
+ ui::LayerAnimator* animator = overlay_widget_->GetLayer()->GetAnimator();
+ if (animator)
+ animator->RemoveObserver(this);
+ if (!is_visible_) {
+ // Acync operation, since delegate may remove this instance and removing
+ // this here causes crash.
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(&Delegate::OnClosed,
+ base::Unretained(delegate_) /* |delegate| lives longer */));
+ }
+}
+
+void ToastOverlay::OnLayerAnimationAborted(
+ ui::LayerAnimationSequence* sequence) {
+ ui::LayerAnimator* animator = overlay_widget_->GetLayer()->GetAnimator();
+ if (animator)
+ animator->RemoveObserver(this);
+}
+
+void ToastOverlay::OnLayerAnimationScheduled(
+ ui::LayerAnimationSequence* sequence) {}
+
+views::Widget* ToastOverlay::widget_for_testing() {
+ return overlay_widget_.get();
+}
+
+void ToastOverlay::ClickDismissButtonForTesting(const ui::Event& event) {
+ overlay_view_->button()->NotifyClick(event);
+}
+
+} // namespace ash