[go: nahoru, domu]

blob: fe52fe871fd5829a242c754fd1a42d5f1802cd4c [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/display/screen_orientation_controller.h"
#include "ash/login/login_screen_controller.h"
#include "ash/public/cpp/ash_features.h"
#include "ash/public/cpp/new_window_delegate.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/scoped_light_mode_as_default.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 "ui/base/l10n/l10n_util.h"
#include "ui/compositor/layer_animator.h"
#include "ui/compositor/scoped_layer_animation_settings.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 amount of rounding applied to the corners of the menu view.
constexpr gfx::RoundedCornersF kMenuViewRoundRectRadiusDp{16.f};
// Horizontal padding between two menu items.
constexpr int kPaddingBetweenMenuItems = 8;
} // namespace
using PowerButtonPosition = PowerButtonController::PowerButtonPosition;
constexpr base::TimeDelta PowerButtonMenuView::kMenuAnimationDuration;
PowerButtonMenuView::PowerButtonMenuView(
PowerButtonPosition power_button_position)
: power_button_position_(power_button_position) {
SetFocusBehavior(FocusBehavior::ALWAYS);
SetPaintToLayer();
layer()->SetFillsBoundsOpaquely(false);
layer()->SetRoundedCornerRadius(kMenuViewRoundRectRadiusDp);
layer()->SetBackgroundBlur(
static_cast<float>(AshColorProvider::LayerBlurSigma::kBlurDefault));
GetViewAccessibility().OverrideRole(ax::mojom::Role::kMenu);
GetViewAccessibility().OverrideName(
l10n_util::GetStringUTF16(IDS_ASH_POWER_BUTTON_MENU_ACCESSIBLE));
RecreateItems();
}
PowerButtonMenuView::~PowerButtonMenuView() = default;
void PowerButtonMenuView::FocusPowerOffButton() {
power_off_item_->RequestFocus();
}
void PowerButtonMenuView::ScheduleShowHideAnimation(bool show) {
// Set initial state.
SetVisible(true);
layer()->GetAnimator()->AbortAllAnimations();
ui::ScopedLayerAnimationSettings animation(layer()->GetAnimator());
animation.AddObserver(this);
animation.SetTweenType(show ? gfx::Tween::EASE_IN
: gfx::Tween::FAST_OUT_LINEAR_IN);
animation.SetTransitionDuration(kMenuAnimationDuration);
animation.SetPreemptionStrategy(
ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
layer()->SetOpacity(show ? 1.0f : 0.f);
// Animation of the menu view bounds change.
if (show) {
gfx::Transform transform;
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);
layer()->SetTransform(transform);
} else {
layer()->SetTransform(gfx::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;
}
OrientationLockType screen_orientation =
Shell::Get()->screen_orientation_controller()->GetCurrentOrientation();
bool is_left_or_right = power_button_position_ == PowerButtonPosition::LEFT ||
power_button_position_ == PowerButtonPosition::RIGHT;
if (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 = IsPrimaryOrientation(screen_orientation);
positive_transform = power_button_position_ == PowerButtonPosition::LEFT
? is_primary
: !is_primary;
} else {
bool is_landscape_primary_or_portrait_secondary =
screen_orientation == OrientationLockType::kLandscapePrimary ||
screen_orientation == OrientationLockType::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 capture_mode_enabled = features::IsCaptureModeEnabled();
const bool create_capture_mode =
capture_mode_enabled &&
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()),
ShutdownReason::POWER_BUTTON),
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,
capture_mode_enabled
? base::BindRepeating(&CaptureModeController::Start,
base::Unretained(CaptureModeController::Get()),
CaptureModeEntryType::kPowerMenu)
: base::DoNothing(),
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