123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349 |
- // Copyright 2019 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/message_center/unified_message_center_bubble.h"
- #include <memory>
- #include "ash/accessibility/accessibility_controller_impl.h"
- #include "ash/bubble/bubble_constants.h"
- #include "ash/constants/ash_features.h"
- #include "ash/shelf/shelf.h"
- #include "ash/shell.h"
- #include "ash/strings/grit/ash_strings.h"
- #include "ash/style/system_shadow.h"
- #include "ash/system/message_center/message_center_style.h"
- #include "ash/system/message_center/unified_message_center_view.h"
- #include "ash/system/tray/tray_constants.h"
- #include "ash/system/tray/tray_event_filter.h"
- #include "ash/system/tray/tray_utils.h"
- #include "ash/system/unified/unified_system_tray.h"
- #include "ash/system/unified/unified_system_tray_bubble.h"
- #include "ash/system/unified/unified_system_tray_controller.h"
- #include "ash/system/unified/unified_system_tray_view.h"
- #include "base/i18n/rtl.h"
- #include "ui/base/l10n/l10n_util.h"
- #include "ui/compositor/layer.h"
- #include "ui/compositor/paint_recorder.h"
- #include "ui/views/focus/focus_search.h"
- #include "ui/views/widget/widget.h"
- namespace ash {
- // We need to draw a custom inner border for the message center in a separate
- // layer so we can properly clip ARC notifications. Each ARC notification is
- // contained in its own Window with its own layer, and the border needs to be
- // drawn on top of them all.
- class UnifiedMessageCenterBubble::Border : public ui::LayerDelegate {
- public:
- Border() : layer_(ui::LAYER_TEXTURED) {
- layer_.set_delegate(this);
- layer_.SetFillsBoundsOpaquely(false);
- }
- Border(const Border&) = delete;
- Border& operator=(const Border&) = delete;
- ~Border() override = default;
- ui::Layer* layer() { return &layer_; }
- private:
- // ui::LayerDelegate:
- void OnPaintLayer(const ui::PaintContext& context) override {
- gfx::Rect bounds = layer()->bounds();
- ui::PaintRecorder recorder(context, bounds.size());
- gfx::Canvas* canvas = recorder.canvas();
- // Draw a solid rounded rect as the inner border.
- cc::PaintFlags flags;
- flags.setColor(message_center_style::kSeperatorColor);
- flags.setStyle(cc::PaintFlags::kStroke_Style);
- flags.setStrokeWidth(canvas->image_scale());
- flags.setAntiAlias(true);
- canvas->DrawRoundRect(bounds, kBubbleCornerRadius, flags);
- }
- void OnDeviceScaleFactorChanged(float old_device_scale_factor,
- float new_device_scale_factor) override {}
- ui::Layer layer_;
- };
- UnifiedMessageCenterBubble::UnifiedMessageCenterBubble(UnifiedSystemTray* tray)
- : tray_(tray), border_(std::make_unique<Border>()) {
- TrayBubbleView::InitParams init_params;
- init_params.delegate = GetWeakPtr();
- // Anchor within the overlay container.
- init_params.parent_window = tray->GetBubbleWindowContainer();
- init_params.anchor_mode = TrayBubbleView::AnchorMode::kRect;
- init_params.preferred_width = kTrayMenuWidth;
- init_params.has_shadow = false;
- init_params.close_on_deactivate = false;
- if (features::IsNotificationsRefreshEnabled())
- init_params.translucent = true;
- bubble_view_ = new TrayBubbleView(init_params);
- message_center_view_ =
- bubble_view_->AddChildView(std::make_unique<UnifiedMessageCenterView>(
- nullptr /* parent */, tray->model(), this));
- time_to_click_recorder_ =
- std::make_unique<TimeToClickRecorder>(this, message_center_view_);
- message_center_view_->AddObserver(this);
- }
- void UnifiedMessageCenterBubble::ShowBubble() {
- bubble_widget_ = views::BubbleDialogDelegateView::CreateBubble(bubble_view_);
- bubble_widget_->AddObserver(this);
- TrayBackgroundView::InitializeBubbleAnimations(bubble_widget_);
- // Stack system tray bubble's window above message center's window, such that
- // message center's shadow will not cover on system tray.
- tray_->GetBubbleWindowContainer()->StackChildAbove(
- tray_->bubble()->GetBubbleWidget()->GetNativeWindow(),
- bubble_widget_->GetNativeWindow());
- if (!features::IsNotificationsRefreshEnabled()) {
- ui::Layer* content_layer = bubble_view_->layer();
- float radius = kBubbleCornerRadius;
- content_layer->SetRoundedCornerRadius({radius, radius, radius, radius});
- content_layer->SetIsFastRoundedCorner(true);
- content_layer->Add(border_->layer());
- }
- if (features::IsSystemTrayShadowEnabled()) {
- // Create a shadow for bubble widget.
- shadow_ = SystemShadow::CreateShadowOnNinePatchLayerForWindow(
- bubble_widget_->GetNativeWindow(), SystemShadow::Type::kElevation12);
- shadow_->SetRoundedCornerRadius(kBubbleCornerRadius);
- }
- bubble_view_->InitializeAndShowBubble();
- message_center_view_->Init();
- UpdateBubbleState();
- tray_->tray_event_filter()->AddBubble(this);
- tray_->bubble()->unified_view()->AddObserver(this);
- }
- UnifiedMessageCenterBubble::~UnifiedMessageCenterBubble() {
- if (bubble_widget_) {
- tray_->tray_event_filter()->RemoveBubble(this);
- tray_->bubble()->unified_view()->RemoveObserver(this);
- CHECK(message_center_view_);
- message_center_view_->RemoveObserver(this);
- bubble_view_->ResetDelegate();
- bubble_widget_->RemoveObserver(this);
- bubble_widget_->Close();
- }
- CHECK(!views::WidgetObserver::IsInObserverList());
- }
- gfx::Rect UnifiedMessageCenterBubble::GetBoundsInScreen() const {
- DCHECK(bubble_view_);
- return bubble_view_->GetBoundsInScreen();
- }
- void UnifiedMessageCenterBubble::CollapseMessageCenter() {
- if (message_center_view_->collapsed())
- return;
- message_center_view_->SetCollapsed(true /*animate*/);
- }
- void UnifiedMessageCenterBubble::ExpandMessageCenter() {
- if (!message_center_view_->collapsed())
- return;
- if (tray_->IsShowingCalendarView())
- tray_->bubble()->unified_system_tray_controller()->TransitionToMainView(
- /*restore_focus=*/true);
- message_center_view_->SetExpanded();
- UpdatePosition();
- tray_->EnsureQuickSettingsCollapsed(true /*animate*/);
- }
- void UnifiedMessageCenterBubble::UpdatePosition() {
- int available_height = CalculateAvailableHeight();
- message_center_view_->SetMaxHeight(available_height);
- message_center_view_->SetAvailableHeight(available_height);
- if (!tray_->bubble())
- return;
- // Shelf bubbles need to be offset from the shelf, otherwise they will be
- // flush with the shelf. The bounds can't be shifted via insets because this
- // enlarges the layer bounds and this can break ARC notification rounded
- // corners. Apply the offset to the anchor rect.
- gfx::Rect anchor_rect = tray_->shelf()->GetSystemTrayAnchorRect();
- gfx::Insets tray_bubble_insets = GetTrayBubbleInsets();
- int offset;
- switch (tray_->shelf()->alignment()) {
- case ShelfAlignment::kLeft:
- offset = tray_bubble_insets.left();
- break;
- case ShelfAlignment::kRight:
- offset = -tray_bubble_insets.right();
- break;
- case ShelfAlignment::kBottom:
- case ShelfAlignment::kBottomLocked:
- if (base::i18n::IsRTL()) {
- offset = tray_bubble_insets.left();
- break;
- }
- offset = -tray_bubble_insets.right();
- break;
- }
- anchor_rect.set_x(anchor_rect.x() + offset);
- anchor_rect.set_y(anchor_rect.y() - tray_->bubble()->GetCurrentTrayHeight() -
- tray_bubble_insets.bottom() -
- kUnifiedMessageCenterBubbleSpacing);
- bubble_view_->ChangeAnchorRect(anchor_rect);
- if (!features::IsNotificationsRefreshEnabled()) {
- bubble_view_->layer()->StackAtTop(border_->layer());
- border_->layer()->SetBounds(message_center_view_->GetContentsBounds());
- }
- if (shadow_) {
- // When the last notification is removed, the content bounds of message
- // center may become too small such which makes the shadow's bounds smaller
- // than its blur region. To avoid this, we hide the shadow when the message
- // center has no notifications.
- shadow_->GetLayer()->SetVisible(
- message_center_view_->message_list_view()->GetTotalNotificationCount());
- }
- }
- void UnifiedMessageCenterBubble::FocusEntered(bool reverse) {
- message_center_view_->FocusEntered(reverse);
- }
- bool UnifiedMessageCenterBubble::FocusOut(bool reverse) {
- return tray_->FocusQuickSettings(reverse);
- }
- void UnifiedMessageCenterBubble::ActivateQuickSettingsBubble() {
- tray_->ActivateBubble();
- }
- bool UnifiedMessageCenterBubble::IsMessageCenterVisible() {
- return !!bubble_widget_ && message_center_view_ &&
- message_center_view_->GetVisible();
- }
- bool UnifiedMessageCenterBubble::IsMessageCenterCollapsed() {
- return message_center_view_->collapsed();
- }
- TrayBackgroundView* UnifiedMessageCenterBubble::GetTray() const {
- return tray_;
- }
- TrayBubbleView* UnifiedMessageCenterBubble::GetBubbleView() const {
- return bubble_view_;
- }
- views::Widget* UnifiedMessageCenterBubble::GetBubbleWidget() const {
- return bubble_widget_;
- }
- std::u16string UnifiedMessageCenterBubble::GetAccessibleNameForBubble() {
- return l10n_util::GetStringUTF16(IDS_ASH_MESSAGE_CENTER_ACCESSIBLE_NAME);
- }
- bool UnifiedMessageCenterBubble::ShouldEnableExtraKeyboardAccessibility() {
- return Shell::Get()->accessibility_controller()->spoken_feedback().enabled();
- }
- void UnifiedMessageCenterBubble::OnViewPreferredSizeChanged(
- views::View* observed_view) {
- UpdatePosition();
- bubble_view_->Layout();
- }
- void UnifiedMessageCenterBubble::OnViewVisibilityChanged(
- views::View* observed_view,
- views::View* starting_view) {
- // Hide the message center widget if the message center is not
- // visible. This is to ensure we do not see an empty bubble.
- if (observed_view != message_center_view_)
- return;
- bubble_view_->UpdateBubble();
- }
- void UnifiedMessageCenterBubble::OnWidgetDestroying(views::Widget* widget) {
- CHECK_EQ(bubble_widget_, widget);
- tray_->tray_event_filter()->RemoveBubble(this);
- tray_->bubble()->unified_view()->RemoveObserver(this);
- message_center_view_->RemoveObserver(this);
- bubble_widget_->RemoveObserver(this);
- bubble_widget_ = nullptr;
- shadow_.reset();
- bubble_view_->ResetDelegate();
- // Close the quick settings bubble as well, which may not automatically happen
- // when dismissing the message center bubble by pressing ESC.
- tray_->CloseBubble();
- }
- void UnifiedMessageCenterBubble::OnWidgetActivationChanged(
- views::Widget* widget,
- bool active) {
- if (active)
- tray_->bubble()->OnMessageCenterActivated();
- }
- void UnifiedMessageCenterBubble::OnDisplayConfigurationChanged() {
- UpdateBubbleState();
- }
- void UnifiedMessageCenterBubble::UpdateBubbleState() {
- if (CalculateAvailableHeight() < kMessageCenterCollapseThreshold &&
- message_center_view_->message_list_view()->GetTotalNotificationCount()) {
- if (tray_->IsQuickSettingsExplicitlyExpanded()) {
- message_center_view_->SetCollapsed(false /*animate*/);
- } else {
- message_center_view_->SetExpanded();
- tray_->EnsureQuickSettingsCollapsed(false /*animate*/);
- }
- } else if (message_center_view_->collapsed()) {
- message_center_view_->SetExpanded();
- }
- UpdatePosition();
- }
- int UnifiedMessageCenterBubble::CalculateAvailableHeight() {
- // TODO(crbug/1311738): Temporary fix to prevent crashes in case the quick
- // settings bubble is destroyed before the message center bubble. In the long
- // term we should remove this code altogether and calculate the max height for
- // the message center bubble separately.
- if (!tray_->bubble())
- return 0;
- return tray_->bubble()->CalculateMaxHeight() -
- tray_->bubble()->GetCurrentTrayHeight() -
- GetBubbleInsetHotseatCompensation() -
- kUnifiedMessageCenterBubbleSpacing;
- }
- void UnifiedMessageCenterBubble::RecordTimeToClick() {
- // TODO(tengs): We are currently only using this handler to record the first
- // interaction (i.e. whether the message center or quick settings was clicked
- // first). Maybe log the time to click if it is useful in the future.
- tray_->MaybeRecordFirstInteraction(
- UnifiedSystemTray::FirstInteractionType::kMessageCenter);
- }
- } // namespace ash
|