[go: nahoru, domu]

blob: 50ba8dec34194ef3d998ee0ca534b866ba6acfa2 [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/power/power_button_menu_view.h"
#include <memory>
#include "ash/capture_mode/capture_mode_controller.h"
#include "ash/constants/ash_features.h"
#include "ash/display/screen_orientation_controller.h"
#include "ash/login/login_screen_controller.h"
#include "ash/public/cpp/new_window_delegate.h"
#include "ash/public/cpp/style/scoped_light_mode_as_default.h"
#include "ash/public/cpp/view_shadow.h"
#include "ash/resources/vector_icons/vector_icons.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shell.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/style/ash_color_provider.h"
#include "ash/style/highlight_border.h"
#include "ash/style/system_shadow.h"
#include "ash/system/power/power_button_menu_item_view.h"
#include "ash/system/power/power_button_menu_metrics_type.h"
#include "ash/system/user/login_status.h"
#include "ash/wm/lock_state_controller.h"
#include "ash/wm/tablet_mode/tablet_mode_controller.h"
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/time/time.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/layer_animator.h"
#include "ui/compositor/scoped_layer_animation_settings.h"
#include "ui/compositor_extra/shadow.h"
#include "ui/gfx/vector_icon_types.h"
#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/background.h"
namespace ash {
namespace {
// Horizontal and vertical padding of the menu item view.
constexpr int kMenuItemHorizontalPadding = 16;
constexpr int kMenuItemVerticalPadding = 16;
// The rounded corner radius of menu.
constexpr int kMenuCornerRadius = 16;
// Horizontal padding between two menu items.
constexpr int kPaddingBetweenMenuItems = 8;
// Set show/hide animation for layer.
void SetLayerAnimation(ui::Layer* layer,
ui::ImplicitAnimationObserver* observer,
bool show,
const gfx::Transform& transform) {
DCHECK(layer);
auto* animator = layer->GetAnimator();
animator->AbortAllAnimations();
ui::ScopedLayerAnimationSettings animation_settings(animator);
animation_settings.SetTweenType(show ? gfx::Tween::EASE_IN
: gfx::Tween::FAST_OUT_LINEAR_IN);
animation_settings.SetTransitionDuration(
PowerButtonMenuView::kMenuAnimationDuration);
animation_settings.SetPreemptionStrategy(
ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
if (observer)
animation_settings.AddObserver(observer);
layer->SetOpacity(show ? 1.0f : 0.f);
layer->SetTransform(transform);
}
} // namespace
using PowerButtonPosition = PowerButtonController::PowerButtonPosition;
constexpr base::TimeDelta PowerButtonMenuView::kMenuAnimationDuration;
PowerButtonMenuView::PowerButtonMenuView(
ShutdownReason shutdown_reason,
PowerButtonPosition power_button_position)
: shutdown_reason_(shutdown_reason),
power_button_position_(power_button_position) {
SetFocusBehavior(FocusBehavior::ALWAYS);
SetPaintToLayer();
if (features::IsDarkLightModeEnabled()) {
SetBorder(std::make_unique<HighlightBorder>(
kMenuCornerRadius, HighlightBorder::Type::kHighlightBorder1,
/*use_light_colors=*/false));
}
layer()->SetFillsBoundsOpaquely(false);
layer()->SetRoundedCornerRadius(gfx::RoundedCornersF(kMenuCornerRadius));
layer()->SetBackgroundBlur(ColorProvider::kBackgroundBlurSigma);
GetViewAccessibility().OverrideRole(ax::mojom::Role::kMenu);
GetViewAccessibility().OverrideName(
l10n_util::GetStringUTF16(IDS_ASH_POWER_BUTTON_MENU_ACCESSIBLE));
RecreateItems();
// Create a view shadow.
shadow_ = std::make_unique<ViewShadow>(
this,
SystemShadow::GetElevationFromType(SystemShadow::Type::kElevation12));
shadow_->shadow()->SetShadowStyle(gfx::ShadowStyle::kChromeOSSystemUI);
shadow_->SetRoundedCornerRadius(kMenuCornerRadius);
}
PowerButtonMenuView::~PowerButtonMenuView() = default;
void PowerButtonMenuView::FocusPowerOffButton() {
power_off_item_->RequestFocus();
}
void PowerButtonMenuView::ScheduleShowHideAnimation(bool show) {
// Set initial state.
SetVisible(true);
// Calculate transform of menu view and shadow bounds.
gfx::Transform transform;
if (show) {
TransformDisplacement transform_displacement = GetTransformDisplacement();
if (transform_displacement.direction == TransformDirection::X)
transform.Translate(transform_displacement.distance, 0);
else if (transform_displacement.direction == TransformDirection::Y)
transform.Translate(0, transform_displacement.distance);
}
SetLayerAnimation(layer(), this, show, transform);
SetLayerAnimation(shadow_->shadow()->layer(), nullptr, show, transform);
}
PowerButtonMenuView::TransformDisplacement
PowerButtonMenuView::GetTransformDisplacement() const {
TransformDisplacement transform_displacement;
if (power_button_position_ == PowerButtonPosition::NONE ||
!Shell::Get()->tablet_mode_controller()->InTabletMode()) {
transform_displacement.direction = TransformDirection::Y;
transform_displacement.distance = kMenuViewTransformDistanceDp;
return transform_displacement;
}
chromeos::OrientationType screen_orientation =
Shell::Get()->screen_orientation_controller()->GetCurrentOrientation();
bool is_left_or_right = power_button_position_ == PowerButtonPosition::LEFT ||
power_button_position_ == PowerButtonPosition::RIGHT;
if (chromeos::IsLandscapeOrientation(screen_orientation)) {
transform_displacement.direction =
is_left_or_right ? TransformDirection::X : TransformDirection::Y;
} else {
transform_displacement.direction =
is_left_or_right ? TransformDirection::Y : TransformDirection::X;
}
bool positive_transform = false;
if (is_left_or_right) {
bool is_primary = chromeos::IsPrimaryOrientation(screen_orientation);
positive_transform = power_button_position_ == PowerButtonPosition::LEFT
? is_primary
: !is_primary;
} else {
bool is_landscape_primary_or_portrait_secondary =
screen_orientation == chromeos::OrientationType::kLandscapePrimary ||
screen_orientation == chromeos::OrientationType::kPortraitSecondary;
positive_transform = power_button_position_ == PowerButtonPosition::TOP
? is_landscape_primary_or_portrait_secondary
: !is_landscape_primary_or_portrait_secondary;
}
transform_displacement.distance = positive_transform
? kMenuViewTransformDistanceDp
: -kMenuViewTransformDistanceDp;
return transform_displacement;
}
void PowerButtonMenuView::RecreateItems() {
// Helper to add or remove a menu item from |this|. Stores weak pointer to
// |out_item_ptr|.
auto add_remove_item =
[this](bool create, PowerButtonMenuActionType action,
base::RepeatingClosure callback, const gfx::VectorIcon& icon,
const std::u16string& string,
PowerButtonMenuItemView** out_item_ptr) -> void {
// If an item needs to be created and exists, or needs to be destroyed but
// does not exist, there is nothing to be done.
if (create && *out_item_ptr)
return;
if (!create && !*out_item_ptr)
return;
if (create) {
*out_item_ptr = AddChildView(std::make_unique<PowerButtonMenuItemView>(
base::BindRepeating(&PowerButtonMenuView::ButtonPressed,
base::Unretained(this), action,
std::move(callback)),
icon, string));
} else {
std::unique_ptr<PowerButtonMenuItemView> to_delete =
RemoveChildViewT(*out_item_ptr);
*out_item_ptr = nullptr;
}
};
const SessionControllerImpl* const session_controller =
Shell::Get()->session_controller();
const LoginStatus login_status = session_controller->login_status();
const bool create_sign_out = login_status != LoginStatus::NOT_LOGGED_IN;
const bool create_lock_screen = login_status != LoginStatus::LOCKED &&
session_controller->CanLockScreen();
const bool create_capture_mode =
Shell::Get()->tablet_mode_controller()->InTabletMode() &&
!session_controller->IsUserSessionBlocked();
const bool create_feedback = login_status != LoginStatus::LOCKED &&
login_status != LoginStatus::KIOSK_APP;
add_remove_item(
true, PowerButtonMenuActionType::kPowerOff,
base::BindRepeating(
&LockStateController::StartShutdownAnimation,
base::Unretained(Shell::Get()->lock_state_controller()),
shutdown_reason_),
kSystemPowerButtonMenuPowerOffIcon,
l10n_util::GetStringUTF16(IDS_ASH_POWER_BUTTON_MENU_POWER_OFF_BUTTON),
&power_off_item_);
add_remove_item(
create_sign_out, PowerButtonMenuActionType::kSignOut,
base::BindRepeating(&SessionControllerImpl::RequestSignOut,
base::Unretained(Shell::Get()->session_controller())),
kSystemPowerButtonMenuSignOutIcon,
user::GetLocalizedSignOutStringForStatus(login_status, false),
&sign_out_item_);
add_remove_item(
create_lock_screen, PowerButtonMenuActionType::kLockScreen,
base::BindRepeating(&SessionControllerImpl::LockScreen,
base::Unretained(Shell::Get()->session_controller())),
kSystemPowerButtonMenuLockScreenIcon,
l10n_util::GetStringUTF16(IDS_ASH_POWER_BUTTON_MENU_LOCK_SCREEN_BUTTON),
&lock_screen_item_);
add_remove_item(
create_capture_mode, PowerButtonMenuActionType::kCaptureMode,
base::BindRepeating(&CaptureModeController::Start,
base::Unretained(CaptureModeController::Get()),
CaptureModeEntryType::kPowerMenu),
kCaptureModeIcon,
l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_CAPTURE_MODE_BUTTON_LABEL),
&capture_mode_item_);
add_remove_item(
create_feedback, PowerButtonMenuActionType::kFeedback,
base::BindRepeating(
[](Shell* shell) {
if (shell->session_controller()->login_status() ==
LoginStatus::NOT_LOGGED_IN) {
// There is a special flow for feedback while in login screen,
// therefore we trigger the same handler associated with the
// feedback accelerator from the login screen to bring up the
// feedback dialog.
shell->login_screen_controller()->HandleAccelerator(
LoginAcceleratorAction::kShowFeedback);
} else {
NewWindowDelegate::GetInstance()->OpenFeedbackPage();
}
},
Shell::Get()),
kSystemPowerButtonMenuFeedbackIcon,
l10n_util::GetStringUTF16(IDS_ASH_POWER_BUTTON_MENU_FEEDBACK_BUTTON),
&feedback_item_);
}
const char* PowerButtonMenuView::GetClassName() const {
return "PowerButtonMenuView";
}
void PowerButtonMenuView::Layout() {
gfx::Rect rect(GetContentsBounds().origin(),
power_off_item_->GetPreferredSize());
const int y_offset =
kMenuItemVerticalPadding - PowerButtonMenuItemView::kItemBorderThickness;
int x_offset = kMenuItemHorizontalPadding -
PowerButtonMenuItemView::kItemBorderThickness;
rect.Offset(x_offset, y_offset);
power_off_item_->SetBoundsRect(rect);
const int padding_between_items_with_border =
kPaddingBetweenMenuItems -
2 * PowerButtonMenuItemView::kItemBorderThickness;
x_offset = rect.width() + padding_between_items_with_border;
if (sign_out_item_) {
rect.Offset(x_offset, 0);
sign_out_item_->SetBoundsRect(rect);
if (lock_screen_item_) {
rect.Offset(x_offset, 0);
lock_screen_item_->SetBoundsRect(rect);
}
}
if (capture_mode_item_) {
rect.Offset(x_offset, 0);
capture_mode_item_->SetBoundsRect(rect);
}
if (feedback_item_) {
rect.Offset(x_offset, 0);
feedback_item_->SetBoundsRect(rect);
}
}
gfx::Size PowerButtonMenuView::CalculatePreferredSize() const {
gfx::Size menu_size;
DCHECK(power_off_item_);
menu_size = gfx::Size(0, PowerButtonMenuItemView::kMenuItemHeight +
2 * kMenuItemVerticalPadding);
int width =
PowerButtonMenuItemView::kMenuItemWidth + 2 * kMenuItemHorizontalPadding;
const int one_item_x_offset =
PowerButtonMenuItemView::kMenuItemWidth + kPaddingBetweenMenuItems;
if (sign_out_item_) {
width += one_item_x_offset;
if (lock_screen_item_)
width += one_item_x_offset;
}
if (capture_mode_item_)
width += one_item_x_offset;
if (feedback_item_)
width += one_item_x_offset;
menu_size.set_width(width);
return menu_size;
}
void PowerButtonMenuView::OnThemeChanged() {
views::View::OnThemeChanged();
ScopedLightModeAsDefault scoped_light_mode_as_default;
SetBackground(
views::CreateSolidBackground(AshColorProvider::Get()->GetBaseLayerColor(
AshColorProvider::BaseLayerType::kTransparent80)));
}
void PowerButtonMenuView::OnImplicitAnimationsCompleted() {
if (layer()->opacity() == 0.f)
SetVisible(false);
if (layer()->opacity() == 1.0f)
RequestFocus();
}
void PowerButtonMenuView::ButtonPressed(PowerButtonMenuActionType action,
base::RepeatingClosure callback) {
RecordMenuActionHistogram(action);
std::move(callback).Run();
Shell::Get()->power_button_controller()->DismissMenu();
}
} // namespace ash