[go: nahoru, domu]

blob: d47a33505af768c1302ca3a41986cb06445921f2 [file] [log] [blame]
// 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/keyboard/ui/keyboard_ui_controller.h"
#include "ash/public/cpp/ash_typography.h"
#include "ash/public/cpp/shell_window_ids.h"
#include "ash/resources/vector_icons/vector_icons.h"
#include "ash/root_window_controller.h"
#include "ash/shelf/hotseat_widget.h"
#include "ash/shelf/shelf.h"
#include "ash/shell.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/style/ash_color_provider.h"
#include "ash/style/pill_button.h"
#include "ash/style/system_toast_style.h"
#include "ash/wm/work_area_insets.h"
#include "base/bind.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_task_runner_handle.h"
#include "ui/accessibility/ax_enums.mojom.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/compositor/layer.h"
#include "ui/display/display_observer.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/font_list.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/views/animation/ink_drop.h"
#include "ui/views/animation/ink_drop_highlight.h"
#include "ui/views/background.h"
#include "ui/views/border.h"
#include "ui/views/controls/button/label_button.h"
#include "ui/views/controls/highlight_path_generator.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"
#include "ui/wm/core/window_animations.h"
namespace ash {
namespace {
// Duration of slide animation when overlay is shown or hidden.
constexpr int kSlideAnimationDurationMs = 100;
// Returns the work area bounds for the root window where new windows are added
// (including new toasts).
gfx::Rect GetUserWorkAreaBounds(aura::Window* window) {
return WorkAreaInsets::ForWindow(window)->user_work_area_bounds();
}
// Offsets the bottom of bounds for toast to accommodate the hotseat, based on
// the current hotseat state
void AdjustWorkAreaBoundsForHotseatState(gfx::Rect& bounds,
const HotseatWidget* hotseat_widget) {
if (hotseat_widget->state() == HotseatState::kExtended)
bounds.set_height(bounds.height() - hotseat_widget->GetHotseatSize());
if (hotseat_widget->state() == HotseatState::kShownHomeLauncher)
bounds.set_height(hotseat_widget->GetTargetBounds().y() - bounds.y());
}
} // namespace
///////////////////////////////////////////////////////////////////////////////
// ToastDisplayObserver
class ToastOverlay::ToastDisplayObserver : public display::DisplayObserver {
public:
ToastDisplayObserver(ToastOverlay* overlay) : overlay_(overlay) {}
ToastDisplayObserver(const ToastDisplayObserver&) = delete;
ToastDisplayObserver& operator=(const ToastDisplayObserver&) = delete;
~ToastDisplayObserver() override {}
void OnDisplayMetricsChanged(const display::Display& display,
uint32_t changed_metrics) override {
overlay_->UpdateOverlayBounds();
}
private:
ToastOverlay* const overlay_;
display::ScopedDisplayObserver display_observer_{this};
};
///////////////////////////////////////////////////////////////////////////////
// ToastOverlay
ToastOverlay::ToastOverlay(Delegate* delegate,
const std::u16string& text,
absl::optional<std::u16string> dismiss_text,
bool show_on_lock_screen,
bool is_managed,
base::RepeatingClosure dismiss_callback)
: delegate_(delegate),
text_(text),
dismiss_text_(dismiss_text),
overlay_widget_(new views::Widget),
overlay_view_(new SystemToastStyle(
base::BindRepeating(&ToastOverlay::OnButtonClicked,
base::Unretained(this)),
text,
dismiss_text,
is_managed)),
display_observer_(std::make_unique<ToastDisplayObserver>(this)),
dismiss_callback_(std::move(dismiss_callback)),
widget_size_(overlay_view_->GetPreferredSize()) {
views::Widget::InitParams params;
params.type = views::Widget::InitParams::TYPE_POPUP;
params.name = "ToastOverlay";
params.opacity = views::Widget::InitParams::WindowOpacity::kTranslucent;
params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
params.accept_events = true;
params.z_order = ui::ZOrderLevel::kFloatingUIElement;
params.bounds = CalculateOverlayBounds();
// Show toasts above the app list and below the lock screen.
params.parent = Shell::GetRootWindowForNewWindows()->GetChildById(
show_on_lock_screen ? kShellWindowId_LockSystemModalContainer
: kShellWindowId_SystemModalContainer);
overlay_widget_->Init(std::move(params));
overlay_widget_->SetVisibilityChangedAnimationsEnabled(true);
overlay_widget_->SetContentsView(overlay_view_.get());
UpdateOverlayBounds();
aura::Window* overlay_window = overlay_widget_->GetNativeWindow();
::wm::SetWindowVisibilityAnimationType(
overlay_window, ::wm::WINDOW_VISIBILITY_ANIMATION_TYPE_VERTICAL);
::wm::SetWindowVisibilityAnimationDuration(
overlay_window, base::Milliseconds(kSlideAnimationDurationMs));
keyboard::KeyboardUIController::Get()->AddObserver(this);
}
ToastOverlay::~ToastOverlay() {
keyboard::KeyboardUIController::Get()->RemoveObserver(this);
overlay_widget_->Close();
}
void ToastOverlay::Show(bool visible) {
if (overlay_widget_->GetLayer()->GetTargetVisibility() == visible)
return;
ui::LayerAnimator* animator = overlay_widget_->GetLayer()->GetAnimator();
DCHECK(animator);
base::TimeDelta original_duration = animator->GetTransitionDuration();
ui::ScopedLayerAnimationSettings animation_settings(animator);
// ScopedLayerAnimationSettings ctor changes the transition duration, so
// change it back to the original value (should be zero).
animation_settings.SetTransitionDuration(original_duration);
animation_settings.AddObserver(this);
if (visible) {
overlay_widget_->Show();
// Notify accessibility about the overlay.
overlay_view_->NotifyAccessibilityEvent(ax::mojom::Event::kAlert, false);
} else {
overlay_widget_->Hide();
}
}
void ToastOverlay::UpdateOverlayBounds() {
overlay_widget_->SetBounds(CalculateOverlayBounds());
}
const std::u16string ToastOverlay::GetText() {
return text_;
}
gfx::Rect ToastOverlay::CalculateOverlayBounds() {
// If the native window has not been initialized, as in the first call, get
// the default root window. Otherwise get the window for this overlay_widget
// to handle multiple monitors properly.
auto* window = overlay_widget_->IsNativeWidgetInitialized()
? overlay_widget_->GetNativeWindow()
: Shell::GetRootWindowForNewWindows();
auto* window_controller = RootWindowController::ForWindow(window);
auto* hotseat_widget = window_controller->shelf()->hotseat_widget();
gfx::Rect bounds = GetUserWorkAreaBounds(window);
if (hotseat_widget)
AdjustWorkAreaBoundsForHotseatState(bounds, hotseat_widget);
int target_y =
bounds.bottom() - widget_size_.height() - ToastOverlay::kOffset;
bounds.ClampToCenteredSize(widget_size_);
bounds.set_y(target_y);
return bounds;
}
void ToastOverlay::OnButtonClicked() {
if (dismiss_callback_) {
dismiss_callback_.Run();
}
Show(/*visible=*/false);
}
void ToastOverlay::OnImplicitAnimationsScheduled() {}
void ToastOverlay::OnImplicitAnimationsCompleted() {
if (!overlay_widget_->GetLayer()->GetTargetVisibility())
delegate_->OnClosed();
}
void ToastOverlay::OnKeyboardOccludedBoundsChanged(
const gfx::Rect& new_bounds_in_screen) {
// TODO(https://crbug.com/943446): Observe changes in user work area bounds
// directly instead of listening for keyboard bounds changes.
UpdateOverlayBounds();
}
views::Widget* ToastOverlay::widget_for_testing() {
return overlay_widget_.get();
}
views::LabelButton* ToastOverlay::dismiss_button_for_testing() {
return overlay_view_->button();
}
} // namespace ash