[go: nahoru, domu]

blob: 01bcccd9014cbb2e942ba11c1e96475cdca0d5eb [file] [log] [blame]
// 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/shelf/shelf.h"
#include "ash/shell.h"
#include "ash/strings/grit/ash_strings.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_view.h"
#include "ui/base/l10n/l10n_util.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() 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, kUnifiedTrayCornerRadius, flags);
}
void OnDeviceScaleFactorChanged(float old_device_scale_factor,
float new_device_scale_factor) override {}
ui::Layer layer_;
DISALLOW_COPY_AND_ASSIGN(Border);
};
UnifiedMessageCenterBubble::UnifiedMessageCenterBubble(UnifiedSystemTray* tray)
: tray_(tray), border_(std::make_unique<Border>()) {
TrayBubbleView::InitParams init_params;
init_params.delegate = this;
// 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;
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_);
tray_->tray_event_filter()->AddBubble(this);
tray_->bubble()->unified_view()->AddObserver(this);
ui::Layer* widget_layer = bubble_widget_->GetLayer();
int radius = kUnifiedTrayCornerRadius;
widget_layer->SetRoundedCornerRadius({radius, radius, radius, radius});
widget_layer->SetIsFastRoundedCorner(true);
widget_layer->Add(border_->layer());
bubble_view_->InitializeAndShowBubble();
// Check if the message center bubble should be collapsed or expanded
// when it is initially opened.
if (CalculateAvailableHeight() < kMessageCenterCollapseThreshold &&
message_center_view_->GetPreferredSize().height()) {
if (tray_->IsQuickSettingsExplicitlyExpanded()) {
message_center_view_->SetCollapsed(false /*animate*/);
} else {
message_center_view_->SetExpanded();
tray_->EnsureQuickSettingsCollapsed(false /*animate*/);
}
}
UpdatePosition();
}
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_->CloseNow();
}
CHECK(!views::WidgetObserver::IsInObserverList());
}
int UnifiedMessageCenterBubble::CalculateAvailableHeight() {
return tray_->bubble()->CalculateMaxHeight() -
tray_->bubble()->GetCurrentTrayHeight() -
kUnifiedMessageCenterBubbleSpacing;
}
void UnifiedMessageCenterBubble::CollapseMessageCenter() {
if (message_center_view_->collapsed())
return;
message_center_view_->SetCollapsed(true /*animate*/);
}
void UnifiedMessageCenterBubble::ExpandMessageCenter() {
if (!message_center_view_->collapsed())
return;
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);
// Note: It's tempting to set the insets for TrayBubbleView in order to
// achieve the padding, but that enlarges the layer bounds and breaks rounded
// corner clipping for ARC notifications. This approach only modifies the
// position of the layer.
gfx::Rect anchor_rect = tray_->shelf()->GetSystemTrayAnchorRect();
gfx::Insets tray_bubble_insets = GetTrayBubbleInsets();
int left_offset = (tray_->shelf()->alignment() == ShelfAlignment::kLeft ||
base::i18n::IsRTL())
? tray_bubble_insets.left()
: -tray_bubble_insets.right();
anchor_rect.set_x(anchor_rect.x() + left_offset);
anchor_rect.set_y(anchor_rect.y() - tray_->bubble()->GetCurrentTrayHeight() -
tray_bubble_insets.bottom() -
kUnifiedMessageCenterBubbleSpacing);
bubble_view_->ChangeAnchorRect(anchor_rect);
bubble_widget_->GetLayer()->StackAtTop(border_->layer());
border_->layer()->SetBounds(message_center_view_->GetContentsBounds());
}
void UnifiedMessageCenterBubble::FocusEntered(bool reverse) {
message_center_view_->FocusEntered(reverse);
}
bool UnifiedMessageCenterBubble::FocusOut(bool reverse) {
return tray_->FocusQuickSettings(reverse);
}
void UnifiedMessageCenterBubble::ActivateQuickSettingsBubble() {
tray_->ActivateBubble();
}
void UnifiedMessageCenterBubble::FocusFirstNotification() {
// Move focus to first notification from notification bar if it is visible.
if (message_center_view_->IsNotificationBarVisible())
message_center_view_->GetFocusManager()->AdvanceFocus(false /*reverse*/);
}
bool UnifiedMessageCenterBubble::IsMessageCenterVisible() {
return !!bubble_widget_ && 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::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;
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::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