[go: nahoru, domu]

blob: cac8bba55d1c2a0e39b777114e1aeceb04d1dacc [file] [log] [blame]
// Copyright 2018 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/unified/unified_system_tray_view.h"
#include <numeric>
#include "ash/public/cpp/ash_features.h"
#include "ash/public/cpp/shelf_config.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shell.h"
#include "ash/system/media/unified_media_controls_container.h"
#include "ash/system/message_center/ash_message_center_lock_screen_controller.h"
#include "ash/system/message_center/unified_message_center_view.h"
#include "ash/system/tray/interacted_by_tap_recorder.h"
#include "ash/system/tray/tray_constants.h"
#include "ash/system/unified/detailed_view_controller.h"
#include "ash/system/unified/feature_pod_button.h"
#include "ash/system/unified/feature_pods_container_view.h"
#include "ash/system/unified/notification_hidden_view.h"
#include "ash/system/unified/page_indicator_view.h"
#include "ash/system/unified/top_shortcuts_view.h"
#include "ash/system/unified/unified_system_info_view.h"
#include "ash/system/unified/unified_system_tray_controller.h"
#include "ash/system/unified/unified_system_tray_model.h"
#include "ash/wm/tablet_mode/tablet_mode_controller.h"
#include "media/base/media_switches.h"
#include "ui/accessibility/ax_enums.mojom.h"
#include "ui/accessibility/ax_node_data.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/scoped_canvas.h"
#include "ui/message_center/message_center.h"
#include "ui/message_center/public/cpp/message_center_constants.h"
#include "ui/views/background.h"
#include "ui/views/border.h"
#include "ui/views/focus/focus_search.h"
#include "ui/views/layout/box_layout.h"
#include "ui/views/painter.h"
namespace ash {
namespace {
class DetailedViewContainer : public views::View {
public:
DetailedViewContainer() = default;
~DetailedViewContainer() override = default;
// views::View:
void Layout() override {
for (auto* child : children())
child->SetBoundsRect(GetContentsBounds());
views::View::Layout();
}
const char* GetClassName() const override { return "DetailedViewContainer"; }
private:
DISALLOW_COPY_AND_ASSIGN(DetailedViewContainer);
};
class AccessibilityFocusHelperView : public views::View {
public:
AccessibilityFocusHelperView(UnifiedSystemTrayController* controller)
: controller_(controller) {}
bool HandleAccessibleAction(const ui::AXActionData& action_data) override {
GetFocusManager()->ClearFocus();
GetFocusManager()->SetStoredFocusView(nullptr);
controller_->FocusOut(false);
return true;
}
// views::View:
void GetAccessibleNodeData(ui::AXNodeData* node_data) override {
node_data->role = ax::mojom::Role::kListItem;
}
private:
UnifiedSystemTrayController* controller_;
};
} // namespace
UnifiedSlidersContainerView::UnifiedSlidersContainerView(
bool initially_expanded)
: expanded_amount_(initially_expanded ? 1.0 : 0.0) {
SetVisible(initially_expanded);
}
UnifiedSlidersContainerView::~UnifiedSlidersContainerView() = default;
void UnifiedSlidersContainerView::SetExpandedAmount(double expanded_amount) {
DCHECK(0.0 <= expanded_amount && expanded_amount <= 1.0);
SetVisible(expanded_amount > 0.0);
expanded_amount_ = expanded_amount;
InvalidateLayout();
UpdateOpacity();
}
int UnifiedSlidersContainerView::GetExpandedHeight() const {
return std::accumulate(children().cbegin(), children().cend(), 0,
[](int height, const auto* v) {
return height + v->GetHeightForWidth(kTrayMenuWidth);
});
}
void UnifiedSlidersContainerView::Layout() {
int y = 0;
for (auto* child : children()) {
int height = child->GetHeightForWidth(kTrayMenuWidth);
child->SetBounds(0, y, kTrayMenuWidth, height);
y += height;
}
}
gfx::Size UnifiedSlidersContainerView::CalculatePreferredSize() const {
return gfx::Size(kTrayMenuWidth, GetExpandedHeight() * expanded_amount_);
}
const char* UnifiedSlidersContainerView::GetClassName() const {
return "UnifiedSlidersContainerView";
}
void UnifiedSlidersContainerView::UpdateOpacity() {
const int height = GetPreferredSize().height();
for (auto* child : children()) {
double opacity = 1.0;
if (child->y() > height) {
opacity = 0.0;
} else if (child->bounds().bottom() < height) {
opacity = 1.0;
} else {
const double ratio =
static_cast<double>(height - child->y()) / child->height();
// TODO(tetsui): Confirm the animation curve with UX.
opacity = std::max(0., 2. * ratio - 1.);
}
child->layer()->SetOpacity(opacity);
}
}
// The container view for the system tray, i.e. the panel containing settings
// buttons and sliders (e.g. sign out, lock, volume slider, etc.).
class UnifiedSystemTrayView::SystemTrayContainer : public views::View {
public:
SystemTrayContainer()
: layout_manager_(SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kVertical))) {}
SystemTrayContainer(const SystemTrayContainer&) = delete;
SystemTrayContainer& operator=(const SystemTrayContainer&) = delete;
~SystemTrayContainer() override = default;
void SetFlexForView(views::View* view) {
DCHECK_EQ(view->parent(), this);
layout_manager_->SetFlexForView(view, 1);
}
// views::View:
void ChildPreferredSizeChanged(views::View* child) override {
PreferredSizeChanged();
}
const char* GetClassName() const override { return "SystemTrayContainer"; }
private:
views::BoxLayout* const layout_manager_;
};
UnifiedSystemTrayView::UnifiedSystemTrayView(
UnifiedSystemTrayController* controller,
bool initially_expanded)
: expanded_amount_(initially_expanded ? 1.0 : 0.0),
controller_(controller),
notification_hidden_view_(new NotificationHiddenView()),
top_shortcuts_view_(new TopShortcutsView(controller_)),
feature_pods_container_(
new FeaturePodsContainerView(controller_, initially_expanded)),
page_indicator_view_(
new PageIndicatorView(controller_, initially_expanded)),
sliders_container_(new UnifiedSlidersContainerView(initially_expanded)),
system_info_view_(new UnifiedSystemInfoView(controller_)),
system_tray_container_(new SystemTrayContainer()),
detailed_view_container_(new DetailedViewContainer()),
focus_search_(std::make_unique<views::FocusSearch>(this, false, false)),
interacted_by_tap_recorder_(
std::make_unique<InteractedByTapRecorder>(this)) {
DCHECK(controller_);
auto add_layered_child = [](views::View* parent, views::View* child) {
parent->AddChildView(child);
child->SetPaintToLayer();
child->layer()->SetFillsBoundsOpaquely(false);
};
SessionControllerImpl* session_controller =
Shell::Get()->session_controller();
notification_hidden_view_->SetVisible(
session_controller->GetUserSession(0) &&
session_controller->IsScreenLocked() &&
!AshMessageCenterLockScreenController::IsEnabled());
add_layered_child(system_tray_container_, notification_hidden_view_);
AddChildView(system_tray_container_);
add_layered_child(system_tray_container_, top_shortcuts_view_);
system_tray_container_->AddChildView(feature_pods_container_);
system_tray_container_->AddChildView(page_indicator_view_);
if (base::FeatureList::IsEnabled(media::kGlobalMediaControlsForChromeOS)) {
media_controls_container_ = new UnifiedMediaControlsContainer();
system_tray_container_->AddChildView(media_controls_container_);
media_controls_container_->SetExpandedAmount(expanded_amount_);
}
system_tray_container_->AddChildView(sliders_container_);
add_layered_child(system_tray_container_, system_info_view_);
system_tray_container_->SetFlexForView(page_indicator_view_);
detailed_view_container_->SetVisible(false);
add_layered_child(this, detailed_view_container_);
top_shortcuts_view_->SetExpandedAmount(expanded_amount_);
system_tray_container_->AddChildView(
new AccessibilityFocusHelperView(controller_));
}
UnifiedSystemTrayView::~UnifiedSystemTrayView() = default;
void UnifiedSystemTrayView::SetMaxHeight(int max_height) {
max_height_ = max_height;
int media_controls_container_height =
media_controls_container_ ? media_controls_container_->GetExpandedHeight()
: 0;
// FeaturePodsContainer can adjust it's height by reducing the number of rows
// it uses. It will calculate how many rows to use based on the max height
// passed here.
feature_pods_container_->SetMaxHeight(
max_height - top_shortcuts_view_->GetPreferredSize().height() -
page_indicator_view_->GetPreferredSize().height() -
media_controls_container_height -
sliders_container_->GetExpandedHeight() -
system_info_view_->GetPreferredSize().height());
}
void UnifiedSystemTrayView::AddFeaturePodButton(FeaturePodButton* button) {
feature_pods_container_->AddFeaturePodButton(button);
}
void UnifiedSystemTrayView::AddSliderView(views::View* slider_view) {
slider_view->SetPaintToLayer();
slider_view->layer()->SetFillsBoundsOpaquely(false);
sliders_container_->AddChildView(slider_view);
}
void UnifiedSystemTrayView::AddMediaControlsView(views::View* media_controls) {
DCHECK(media_controls);
DCHECK(media_controls_container_);
media_controls->SetPaintToLayer();
media_controls->layer()->SetFillsBoundsOpaquely(false);
media_controls_container_->AddChildView(media_controls);
}
void UnifiedSystemTrayView::ShowMediaControls() {
media_controls_container_->SetShouldShowMediaControls(true);
if (detailed_view_container_->GetVisible())
return;
if (media_controls_container_->MaybeShowMediaControls())
PreferredSizeChanged();
}
void UnifiedSystemTrayView::SetDetailedView(views::View* detailed_view) {
auto system_tray_size = system_tray_container_->GetPreferredSize();
system_tray_container_->SetVisible(false);
detailed_view_container_->RemoveAllChildViews(true /* delete_children */);
detailed_view_container_->AddChildView(detailed_view);
detailed_view_container_->SetVisible(true);
detailed_view_container_->SetPreferredSize(system_tray_size);
detailed_view->InvalidateLayout();
Layout();
}
void UnifiedSystemTrayView::ResetDetailedView() {
detailed_view_container_->RemoveAllChildViews(true /* delete_children */);
detailed_view_container_->SetVisible(false);
if (media_controls_container_)
media_controls_container_->MaybeShowMediaControls();
system_tray_container_->SetVisible(true);
sliders_container_->UpdateOpacity();
PreferredSizeChanged();
Layout();
}
void UnifiedSystemTrayView::SaveFocus() {
auto* focus_manager = GetFocusManager();
if (!focus_manager)
return;
saved_focused_view_ = focus_manager->GetFocusedView();
}
void UnifiedSystemTrayView::RestoreFocus() {
if (saved_focused_view_)
saved_focused_view_->RequestFocus();
}
void UnifiedSystemTrayView::SetExpandedAmount(double expanded_amount) {
DCHECK(0.0 <= expanded_amount && expanded_amount <= 1.0);
expanded_amount_ = expanded_amount;
top_shortcuts_view_->SetExpandedAmount(expanded_amount);
feature_pods_container_->SetExpandedAmount(expanded_amount);
page_indicator_view_->SetExpandedAmount(expanded_amount);
if (media_controls_container_)
media_controls_container_->SetExpandedAmount(expanded_amount);
sliders_container_->SetExpandedAmount(expanded_amount);
PreferredSizeChanged();
// It is possible that the ratio between |message_center_view_| and others
// can change while the bubble size remain unchanged.
Layout();
}
int UnifiedSystemTrayView::GetExpandedSystemTrayHeight() const {
int media_controls_container_height =
media_controls_container_ ? media_controls_container_->GetExpandedHeight()
: 0;
return (notification_hidden_view_->GetVisible()
? notification_hidden_view_->GetPreferredSize().height()
: 0) +
top_shortcuts_view_->GetPreferredSize().height() +
feature_pods_container_->GetExpandedHeight() +
page_indicator_view_->GetExpandedHeight() +
sliders_container_->GetExpandedHeight() +
media_controls_container_height +
system_info_view_->GetPreferredSize().height();
}
int UnifiedSystemTrayView::GetCollapsedSystemTrayHeight() const {
return (notification_hidden_view_->GetVisible()
? notification_hidden_view_->GetPreferredSize().height()
: 0) +
top_shortcuts_view_->GetPreferredSize().height() +
feature_pods_container_->GetCollapsedHeight() +
system_info_view_->GetPreferredSize().height();
}
int UnifiedSystemTrayView::GetCurrentHeight() const {
return GetPreferredSize().height();
}
int UnifiedSystemTrayView::GetVisibleFeaturePodCount() const {
return feature_pods_container_->GetVisibleCount();
}
std::u16string UnifiedSystemTrayView::GetDetailedViewAccessibleName() const {
return controller_->detailed_view_controller()->GetAccessibleName();
}
bool UnifiedSystemTrayView::IsDetailedViewShown() const {
return detailed_view_container_->GetVisible();
}
views::View* UnifiedSystemTrayView::GetFirstFocusableChild() {
FocusTraversable* focus_traversable = GetFocusTraversable();
views::View* focus_traversable_view = this;
return focus_search_->FindNextFocusableView(
nullptr, views::FocusSearch::SearchDirection::kForwards,
views::FocusSearch::TraversalDirection::kDown,
views::FocusSearch::StartingViewPolicy::kSkipStartingView,
views::FocusSearch::AnchoredDialogPolicy::kCanGoIntoAnchoredDialog,
&focus_traversable, &focus_traversable_view);
}
views::View* UnifiedSystemTrayView::GetLastFocusableChild() {
FocusTraversable* focus_traversable = GetFocusTraversable();
views::View* focus_traversable_view = this;
return focus_search_->FindNextFocusableView(
nullptr, views::FocusSearch::SearchDirection::kBackwards,
views::FocusSearch::TraversalDirection::kDown,
views::FocusSearch::StartingViewPolicy::kSkipStartingView,
views::FocusSearch::AnchoredDialogPolicy::kCanGoIntoAnchoredDialog,
&focus_traversable, &focus_traversable_view);
}
void UnifiedSystemTrayView::FocusEntered(bool reverse) {
views::View* focus_view =
reverse ? GetLastFocusableChild() : GetFirstFocusableChild();
GetFocusManager()->ClearFocus();
GetFocusManager()->SetFocusedView(focus_view);
}
gfx::Size UnifiedSystemTrayView::CalculatePreferredSize() const {
int expanded_height = GetExpandedSystemTrayHeight();
int collapsed_height = GetCollapsedSystemTrayHeight();
return gfx::Size(kTrayMenuWidth,
collapsed_height + ((expanded_height - collapsed_height) *
expanded_amount_));
}
void UnifiedSystemTrayView::OnGestureEvent(ui::GestureEvent* event) {
gfx::PointF screen_location = event->root_location_f();
switch (event->type()) {
case ui::ET_GESTURE_SCROLL_BEGIN:
controller_->BeginDrag(screen_location);
event->SetHandled();
break;
case ui::ET_GESTURE_SCROLL_UPDATE:
controller_->UpdateDrag(screen_location);
event->SetHandled();
break;
case ui::ET_GESTURE_END:
controller_->EndDrag(screen_location);
event->SetHandled();
break;
case ui::ET_SCROLL_FLING_START:
controller_->Fling(event->details().velocity_y());
break;
default:
break;
}
}
void UnifiedSystemTrayView::Layout() {
if (system_tray_container_->GetVisible())
system_tray_container_->SetBoundsRect(GetContentsBounds());
else if (detailed_view_container_->GetVisible())
detailed_view_container_->SetBoundsRect(GetContentsBounds());
}
void UnifiedSystemTrayView::ChildPreferredSizeChanged(views::View* child) {
// The size change is not caused by SetExpandedAmount(), because they don't
// trigger PreferredSizeChanged().
PreferredSizeChanged();
}
const char* UnifiedSystemTrayView::GetClassName() const {
return "UnifiedSystemTrayView";
}
void UnifiedSystemTrayView::AddedToWidget() {
focus_manager_ = GetFocusManager();
if (focus_manager_)
focus_manager_->AddFocusChangeListener(this);
}
void UnifiedSystemTrayView::RemovedFromWidget() {
if (!focus_manager_)
return;
focus_manager_->RemoveFocusChangeListener(this);
focus_manager_ = nullptr;
}
views::FocusTraversable* UnifiedSystemTrayView::GetFocusTraversable() {
return this;
}
views::FocusSearch* UnifiedSystemTrayView::GetFocusSearch() {
return focus_search_.get();
}
views::FocusTraversable* UnifiedSystemTrayView::GetFocusTraversableParent() {
return nullptr;
}
views::View* UnifiedSystemTrayView::GetFocusTraversableParentView() {
return this;
}
void UnifiedSystemTrayView::OnWillChangeFocus(views::View* before,
views::View* now) {}
void UnifiedSystemTrayView::OnDidChangeFocus(views::View* before,
views::View* now) {
if (feature_pods_container_->Contains(now)) {
feature_pods_container_->EnsurePageWithButton(now);
}
views::View* first_view = GetFirstFocusableChild();
views::View* last_view = GetLastFocusableChild();
bool focused_out = false;
if (before == last_view && now == first_view)
focused_out = controller_->FocusOut(false);
else if (before == first_view && now == last_view)
focused_out = controller_->FocusOut(true);
if (focused_out) {
GetFocusManager()->ClearFocus();
GetFocusManager()->SetStoredFocusView(nullptr);
}
}
} // namespace ash