blob: f5e8e2cc4f6acba8ec181badc520df20d5bc4e06 [file] [log] [blame]
// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/accelerators/accelerator_commands.h"
#include "accelerator_notifications.h"
#include "ash/accessibility/accessibility_controller_impl.h"
#include "ash/accessibility/magnifier/docked_magnifier_controller.h"
#include "ash/accessibility/magnifier/fullscreen_magnifier_controller.h"
#include "ash/ambient/ambient_controller.h"
#include "ash/app_list/app_list_controller_impl.h"
#include "ash/assistant/assistant_controller_impl.h"
#include "ash/capture_mode/capture_mode_camera_controller.h"
#include "ash/capture_mode/capture_mode_controller.h"
#include "ash/clipboard/clipboard_history_controller_impl.h"
#include "ash/constants/ash_features.h"
#include "ash/constants/ash_pref_names.h"
#include "ash/display/display_configuration_controller.h"
#include "ash/display/display_move_window_util.h"
#include "ash/display/privacy_screen_controller.h"
#include "ash/display/screen_orientation_controller.h"
#include "ash/focus_cycler.h"
#include "ash/frame/non_client_frame_view_ash.h"
#include "ash/ime/ime_controller_impl.h"
#include "ash/keyboard/keyboard_controller_impl.h"
#include "ash/media/media_controller_impl.h"
#include "ash/public/cpp/assistant/assistant_state.h"
#include "ash/public/cpp/new_window_delegate.h"
#include "ash/public/cpp/projector/projector_controller.h"
#include "ash/public/cpp/system/toast_data.h"
#include "ash/root_window_controller.h"
#include "ash/rotator/window_rotation.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shelf/shelf.h"
#include "ash/shelf/shelf_focus_cycler.h"
#include "ash/shell.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/system/accessibility/floating_accessibility_controller.h"
#include "ash/system/brightness_control_delegate.h"
#include "ash/system/ime_menu/ime_menu_tray.h"
#include "ash/system/keyboard_brightness_control_delegate.h"
#include "ash/system/model/system_tray_model.h"
#include "ash/system/notification_center/notification_center_tray.h"
#include "ash/system/palette/palette_tray.h"
#include "ash/system/power/power_button_controller.h"
#include "ash/system/status_area_widget.h"
#include "ash/system/time/calendar_metrics.h"
#include "ash/system/time/calendar_model.h"
#include "ash/system/toast/toast_manager_impl.h"
#include "ash/system/tray/system_tray_notifier.h"
#include "ash/system/unified/date_tray.h"
#include "ash/system/unified/unified_system_tray.h"
#include "ash/system/unified/unified_system_tray_bubble.h"
#include "ash/touch/touch_hud_debug.h"
#include "ash/wm/desks/desks_animations.h"
#include "ash/wm/desks/desks_util.h"
#include "ash/wm/float/float_controller.h"
#include "ash/wm/mru_window_tracker.h"
#include "ash/wm/overview/overview_controller.h"
#include "ash/wm/overview/overview_session.h"
#include "ash/wm/screen_pinning_controller.h"
#include "ash/wm/window_cycle/window_cycle_controller.h"
#include "ash/wm/window_state.h"
#include "ash/wm/window_util.h"
#include "ash/wm/wm_event.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/metrics/user_metrics.h"
#include "base/ranges/algorithm.h"
#include "chromeos/ash/components/audio/cras_audio_handler.h"
#include "chromeos/ash/services/assistant/public/cpp/assistant_enums.h"
#include "chromeos/dbus/power/power_manager_client.h"
#include "chromeos/ui/base/display_util.h"
#include "chromeos/ui/base/window_properties.h"
#include "chromeos/ui/frame/caption_buttons/frame_size_button.h"
#include "chromeos/ui/wm/desks/chromeos_desks_histogram_enums.h"
#include "chromeos/ui/wm/window_util.h"
#include "components/prefs/pref_service.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/base/emoji/emoji_panel_helper.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/layer_animation_sequence.h"
#include "ui/compositor/layer_animator.h"
#include "ui/display/display.h"
#include "ui/display/manager/display_manager.h"
#include "ui/display/manager/managed_display_info.h"
#include "ui/display/screen.h"
#include "ui/display/util/display_util.h"
#include "ui/gfx/geometry/point.h"
#include "ui/views/widget/widget.h"
#include "ui/views/window/frame_caption_button.h"
#include "ui/wm/core/window_util.h"
// Keep the functions in this file in alphabetical order.
namespace ash {
const char kAccessibilityHighContrastShortcut[] =
"Accessibility.Shortcuts.CrosHighContrast";
const char kAccessibilitySpokenFeedbackShortcut[] =
"Accessibility.Shortcuts.CrosSpokenFeedback";
const char kAccessibilityScreenMagnifierShortcut[] =
"Accessibility.Shortcuts.CrosScreenMagnifier";
const char kAccessibilityDockedMagnifierShortcut[] =
"Accessibility.Shortcuts.CrosDockedMagnifier";
const char kAccelWindowSnap[] = "Ash.Accelerators.WindowSnap";
const char kAccelRotation[] = "Ash.Accelerators.Rotation.Usage";
const char kAccelActivateDeskByIndex[] = "Ash.Accelerators.ActivateDeskByIndex";
namespace accelerators {
namespace {
using ::base::UserMetricsAction;
using ::chromeos::WindowStateType;
// Percent by which the volume should be changed when a volume key is pressed.
constexpr double kStepPercentage = 4.0;
constexpr char kVirtualDesksToastId[] = "virtual_desks_toast";
// Toast id for Assistant shortcuts.
constexpr char kAssistantErrorToastId[] = "assistant_error";
// Toast ID for the notification center tray "No notifications" toast.
constexpr char kNotificationCenterTrayNoNotificationsToastId[] =
"notification_center_tray_toast_ids.no_notifications";
// These values are written to logs. New enum values can be added, but existing
// enums must never be renumbered or deleted and reused.
// Records the result of triggering the rotation accelerator.
enum class RotationAcceleratorAction {
kCancelledDialog = 0,
kAcceptedDialog = 1,
kAlreadyAcceptedDialog = 2,
kMaxValue = kAlreadyAcceptedDialog,
};
// Record which desk is activated.
enum class ActivateDeskAcceleratorAction {
kDesk1 = 0,
kDesk2 = 1,
kDesk3 = 2,
kDesk4 = 3,
kDesk5 = 4,
kDesk6 = 5,
kDesk7 = 6,
kDesk8 = 7,
kMaxValue = kDesk8,
};
void RecordRotationAcceleratorAction(const RotationAcceleratorAction& action) {
UMA_HISTOGRAM_ENUMERATION(kAccelRotation, action);
}
void RecordActivateDeskByIndexAcceleratorAction(
const ActivateDeskAcceleratorAction& action) {
UMA_HISTOGRAM_ENUMERATION(kAccelActivateDeskByIndex, action);
}
void RecordWindowSnapAcceleratorAction(
const WindowSnapAcceleratorAction& action) {
UMA_HISTOGRAM_ENUMERATION(kAccelWindowSnap, action);
}
display::Display::Rotation GetNextRotationInClamshell(
display::Display::Rotation current) {
switch (current) {
case display::Display::ROTATE_0:
return display::Display::ROTATE_90;
case display::Display::ROTATE_90:
return display::Display::ROTATE_180;
case display::Display::ROTATE_180:
return display::Display::ROTATE_270;
case display::Display::ROTATE_270:
return display::Display::ROTATE_0;
}
NOTREACHED() << "Unknown rotation:" << current;
return display::Display::ROTATE_0;
}
display::Display::Rotation GetNextRotationInTabletMode(
int64_t display_id,
display::Display::Rotation current) {
Shell* shell = Shell::Get();
DCHECK(shell->tablet_mode_controller()->InTabletMode());
if (!display::HasInternalDisplay() ||
display_id != display::Display::InternalDisplayId()) {
return GetNextRotationInClamshell(current);
}
const chromeos::OrientationType app_requested_lock =
shell->screen_orientation_controller()
->GetCurrentAppRequestedOrientationLock();
bool add_180_degrees = false;
switch (app_requested_lock) {
case chromeos::OrientationType::kCurrent:
case chromeos::OrientationType::kLandscapePrimary:
case chromeos::OrientationType::kLandscapeSecondary:
case chromeos::OrientationType::kPortraitPrimary:
case chromeos::OrientationType::kPortraitSecondary:
case chromeos::OrientationType::kNatural:
// Do not change the current orientation.
return current;
case chromeos::OrientationType::kLandscape:
case chromeos::OrientationType::kPortrait:
// App allows both primary and secondary orientations in either landscape
// or portrait, therefore switch to the next one by adding 180 degrees.
add_180_degrees = true;
break;
default:
break;
}
switch (current) {
case display::Display::ROTATE_0:
return add_180_degrees ? display::Display::ROTATE_180
: display::Display::ROTATE_90;
case display::Display::ROTATE_90:
return add_180_degrees ? display::Display::ROTATE_270
: display::Display::ROTATE_180;
case display::Display::ROTATE_180:
return add_180_degrees ? display::Display::ROTATE_0
: display::Display::ROTATE_270;
case display::Display::ROTATE_270:
return add_180_degrees ? display::Display::ROTATE_90
: display::Display::ROTATE_0;
}
NOTREACHED() << "Unknown rotation:" << current;
return display::Display::ROTATE_0;
}
views::Widget* FindPipWidget() {
return Shell::Get()->focus_cycler()->FindWidget(
base::BindRepeating([](views::Widget* widget) {
return WindowState::Get(widget->GetNativeWindow())->IsPip();
}));
}
PaletteTray* GetPaletteTray() {
return Shelf::ForWindow(Shell::GetRootWindowForNewWindows())
->GetStatusAreaWidget()
->palette_tray();
}
bool ShouldLockRotation(int64_t display_id) {
return display::HasInternalDisplay() &&
display_id == display::Display::InternalDisplayId() &&
Shell::Get()->screen_orientation_controller()->IsAutoRotationAllowed();
}
int64_t GetDisplayIdForRotation() {
const gfx::Point point = display::Screen::GetScreen()->GetCursorScreenPoint();
return display::Screen::GetScreen()->GetDisplayNearestPoint(point).id();
}
void RotateScreenImpl() {
auto* shell = Shell::Get();
const bool in_tablet_mode =
Shell::Get()->tablet_mode_controller()->InTabletMode();
const int64_t display_id = GetDisplayIdForRotation();
const display::ManagedDisplayInfo& display_info =
shell->display_manager()->GetDisplayInfo(display_id);
const auto active_rotation = display_info.GetActiveRotation();
const auto next_rotation =
in_tablet_mode ? GetNextRotationInTabletMode(display_id, active_rotation)
: GetNextRotationInClamshell(active_rotation);
if (active_rotation == next_rotation)
return;
// When the auto-rotation is allowed in the device, display rotation requests
// of the internal display are treated as requests to lock the user rotation.
if (ShouldLockRotation(display_id)) {
shell->screen_orientation_controller()->SetLockToRotation(next_rotation);
return;
}
shell->display_configuration_controller()->SetDisplayRotation(
display_id, next_rotation, display::Display::RotationSource::USER);
}
void OnRotationDialogAccepted() {
RecordRotationAcceleratorAction(RotationAcceleratorAction::kAcceptedDialog);
RotateScreenImpl();
Shell::Get()
->accessibility_controller()
->SetDisplayRotationAcceleratorDialogBeenAccepted();
}
void OnRotationDialogCancelled() {
RecordRotationAcceleratorAction(RotationAcceleratorAction::kCancelledDialog);
}
// Return false if the accessibility shortcuts have been disabled, or if
// the accessibility feature itself associated with |accessibility_pref_name|
// is being enforced by the administrator.
bool IsAccessibilityShortcutEnabled(
const std::string& accessibility_pref_name) {
Shell* shell = Shell::Get();
return shell->accessibility_controller()->accessibility_shortcuts_enabled() &&
!shell->session_controller()
->GetActivePrefService()
->IsManagedPreference(accessibility_pref_name);
}
void SetDockedMagnifierEnabled(bool enabled) {
Shell* shell = Shell::Get();
// Check that the attempt to change the value of the accessibility feature
// will be done only when the accessibility shortcuts are enabled, and
// the feature isn't being enforced by the administrator.
DCHECK(IsAccessibilityShortcutEnabled(prefs::kDockedMagnifierEnabled));
shell->docked_magnifier_controller()->SetEnabled(enabled);
RemoveDockedMagnifierNotification();
if (shell->docked_magnifier_controller()->GetEnabled()) {
ShowDockedMagnifierNotification();
}
}
void SetFullscreenMagnifierEnabled(bool enabled) {
// TODO (afakhry): Move the below into a single call (crbug/817157).
// Necessary to make magnification controller in ash observe changes to the
// prefs itself.
Shell* shell = Shell::Get();
// Check that the attempt to change the value of the accessibility feature
// will be done only when the accessibility shortcuts are enabled, and
// the feature isn't being enforced by the administrator.
DCHECK(IsAccessibilityShortcutEnabled(
prefs::kAccessibilityScreenMagnifierEnabled));
shell->accessibility_controller()->fullscreen_magnifier().SetEnabled(enabled);
RemoveFullscreenMagnifierNotification();
if (shell->fullscreen_magnifier_controller()->IsEnabled()) {
ShowFullscreenMagnifierNotification();
}
}
void SetHighContrastEnabled(bool enabled) {
Shell* shell = Shell::Get();
// Check that the attempt to change the value of the accessibility feature
// will be done only when the accessibility shortcuts are enabled, and
// the feature isn't being enforced by the administrator.
DCHECK(
IsAccessibilityShortcutEnabled(prefs::kAccessibilityHighContrastEnabled));
shell->accessibility_controller()->high_contrast().SetEnabled(enabled);
RemoveHighContrastNotification();
if (shell->accessibility_controller()->high_contrast().enabled()) {
ShowHighContrastNotification();
}
}
void ShowToast(const std::string& id,
ToastCatalogName catalog_name,
const std::u16string& text) {
ToastData toast(id, catalog_name, text, ToastData::kDefaultToastDuration,
/*visible_on_lock_screen=*/true);
Shell::Get()->toast_manager()->Show(std::move(toast));
}
void HandleToggleSystemTrayBubbleInternal(bool focus_message_center) {
aura::Window* target_root = Shell::GetRootWindowForNewWindows();
UnifiedSystemTray* tray = RootWindowController::ForWindow(target_root)
->GetStatusAreaWidget()
->unified_system_tray();
if (tray->IsBubbleShown()) {
tray->CloseBubble();
} else {
tray->ShowBubble();
tray->ActivateBubble();
if (focus_message_center)
tray->FocusMessageCenter(false, true);
}
}
// Enters capture mode image type with |source|.
void EnterImageCaptureMode(CaptureModeSource source,
CaptureModeEntryType entry_type) {
auto* capture_mode_controller = CaptureModeController::Get();
capture_mode_controller->SetSource(source);
capture_mode_controller->SetType(CaptureModeType::kImage);
capture_mode_controller->Start(entry_type);
}
// Get the window's frame size button, or nullptr if there isn't one.
chromeos::FrameSizeButton* GetFrameSizeButton(aura::Window* window) {
if (!window) {
return nullptr;
}
auto* frame_view = NonClientFrameViewAsh::Get(window);
if (!frame_view) {
return nullptr;
}
return static_cast<chromeos::FrameSizeButton*>(
frame_view->GetHeaderView()->caption_button_container()->size_button());
}
} // namespace
bool CanActivateTouchHud() {
return RootWindowController::ForTargetRootWindow()->touch_hud_debug();
}
bool CanCreateNewIncognitoWindow() {
// Guest mode does not use incognito windows. The browser may have other
// restrictions on incognito mode (e.g. enterprise policy) but those are rare.
// For non-guest mode, consume the key and defer the decision to the browser.
absl::optional<user_manager::UserType> user_type =
Shell::Get()->session_controller()->GetUserType();
return user_type && *user_type != user_manager::USER_TYPE_GUEST;
}
bool CanCycleInputMethod() {
return Shell::Get()->ime_controller()->CanSwitchIme();
}
bool CanCycleMru() {
// Don't do anything when Alt+Tab is hit while a virtual keyboard is showing.
// Touchscreen users have better window switching options. It would be
// preferable if we could tell whether this event actually came from a virtual
// keyboard, but there's no easy way to do so, thus we block Alt+Tab when the
// virtual keyboard is showing, even if it came from a real keyboard. See
// http://crbug.com/638269
return !keyboard::KeyboardUIController::Get()->IsKeyboardVisible();
}
bool CanCycleSameAppWindows() {
return features::IsSameAppWindowCycleEnabled() && CanCycleMru();
}
bool CanCycleUser() {
return Shell::Get()->session_controller()->NumberOfLoggedInUsers() > 1;
}
bool CanFindPipWidget() {
return !!FindPipWidget();
}
bool CanFocusCameraPreview() {
auto* controller = CaptureModeController::Get();
// Only use the shortcut to focus the camera preview while video recording is
// in progress. As focus traversal of the camera preview in the capture
// session will be handled by CaptureModeSessionFocusCycler instead.
if (controller->IsActive() || !controller->is_recording_in_progress())
return false;
auto* camera_controller = controller->camera_controller();
DCHECK(camera_controller);
auto* preview_widget = camera_controller->camera_preview_widget();
return preview_widget && preview_widget->IsVisible();
}
bool CanLock() {
return Shell::Get()->session_controller()->CanLockScreen();
}
bool CanMinimizeTopWindowOnBack() {
return window_util::ShouldMinimizeTopWindowOnBack();
}
bool CanMoveActiveWindowBetweenDisplays() {
return display_move_window_util::CanHandleMoveActiveWindowBetweenDisplays();
}
bool CanPerformMagnifierZoom() {
return Shell::Get()->fullscreen_magnifier_controller()->IsEnabled() ||
Shell::Get()->docked_magnifier_controller()->GetEnabled();
}
bool CanScreenshot(bool take_screenshot) {
// |TAKE_SCREENSHOT| is allowed when user session is blocked.
return take_screenshot ||
!Shell::Get()->session_controller()->IsUserSessionBlocked();
}
bool CanShowStylusTools() {
return GetPaletteTray()->ShouldShowPalette();
}
bool CanStartAmbientMode() {
return features::IsAmbientModeEnabled();
}
bool CanSwapPrimaryDisplay() {
return display::Screen::GetScreen()->GetNumDisplays() > 1;
}
bool CanToggleCalendar() {
return features::IsCalendarViewEnabled();
}
bool CanToggleDictation() {
return Shell::Get()->accessibility_controller()->dictation().enabled();
}
bool CanToggleFloatingWindow() {
if (!chromeos::wm::features::IsFloatWindowEnabled()) {
return false;
}
aura::Window* window = window_util::GetActiveWindow();
return window && chromeos::wm::CanFloatWindow(window);
}
bool CanToggleMultitaskMenu() {
if (!chromeos::wm::features::IsFloatWindowEnabled() ||
Shell::Get()->tablet_mode_controller()->InTabletMode()) {
return false;
}
aura::Window* window = window_util::GetActiveWindow();
if (!window) {
return false;
}
// If the active window has a visible size button, the menu can be opened.
if (auto* size_button = GetFrameSizeButton(window);
size_button && size_button->GetVisible()) {
return true;
}
// Else if the transient parent is showing the multitask menu, the menu can be
// closed.
auto* transient_parent = wm::GetTransientParent(window);
auto* size_button = GetFrameSizeButton(transient_parent);
return size_button && size_button->IsMultitaskMenuShown();
}
bool CanToggleOverview() {
auto windows =
Shell::Get()->mru_window_tracker()->BuildMruWindowList(kActiveDesk);
// Do not toggle overview if there is a window being dragged.
for (auto* window : windows) {
if (WindowState::Get(window)->is_dragged())
return false;
}
return true;
}
bool CanTogglePrivacyScreen() {
return Shell::Get()->privacy_screen_controller()->IsSupported();
}
bool CanToggleProjectorMarker() {
auto* projector_controller = ProjectorController::Get();
if (projector_controller) {
return projector_controller->GetAnnotatorAvailability();
}
return false;
}
bool CanToggleResizeLockMenu() {
aura::Window* active_window = window_util::GetActiveWindow();
if (!active_window)
return false;
auto* frame_view = NonClientFrameViewAsh::Get(active_window);
return frame_view && frame_view->GetToggleResizeLockMenuCallback();
}
bool CanUnpinWindow() {
// WindowStateType::kTrustedPinned does not allow the user to press a key to
// exit pinned mode.
WindowState* window_state = WindowState::ForActiveWindow();
return window_state &&
window_state->GetStateType() == WindowStateType::kPinned;
}
bool CanWindowSnap() {
aura::Window* active_window = window_util::GetActiveWindow();
if (!active_window)
return false;
WindowState* window_state = WindowState::Get(active_window);
return window_state && window_state->IsUserPositionable();
}
void ActivateDesk(bool activate_left) {
auto* desks_controller = DesksController::Get();
const bool success = desks_controller->ActivateAdjacentDesk(
activate_left, DesksSwitchSource::kDeskSwitchShortcut);
if (!success)
return;
if (activate_left) {
base::RecordAction(base::UserMetricsAction("Accel_Desks_ActivateLeft"));
} else {
base::RecordAction(base::UserMetricsAction("Accel_Desks_ActivateRight"));
}
}
void ActivateDeskAtIndex(AcceleratorAction action) {
DCHECK_GE(action, DESKS_ACTIVATE_0);
DCHECK_LE(action, DESKS_ACTIVATE_7);
const size_t target_index = action - DESKS_ACTIVATE_0;
auto* desks_controller = DesksController::Get();
// Only 1 desk animation can occur at a time so ignore this action if there's
// an ongoing desk animation.
if (desks_controller->AreDesksBeingModified())
return;
const auto& desks = desks_controller->desks();
if (target_index < desks.size()) {
// Record which desk users switch to.
RecordActivateDeskByIndexAcceleratorAction(
static_cast<ActivateDeskAcceleratorAction>(target_index));
desks_controller->ActivateDesk(
desks[target_index].get(),
DesksSwitchSource::kIndexedDeskSwitchShortcut);
} else {
for (auto* root : Shell::GetAllRootWindows())
desks_animations::PerformHitTheWallAnimation(root, /*going_left=*/false);
}
}
void ActiveMagnifierZoom(int delta_index) {
if (Shell::Get()->fullscreen_magnifier_controller()->IsEnabled()) {
Shell::Get()->fullscreen_magnifier_controller()->StepToNextScaleValue(
delta_index);
return;
}
if (Shell::Get()->docked_magnifier_controller()->GetEnabled()) {
Shell::Get()->docked_magnifier_controller()->StepToNextScaleValue(
delta_index);
}
}
void BrightnessDown() {
BrightnessControlDelegate* delegate =
Shell::Get()->brightness_control_delegate();
if (delegate)
delegate->HandleBrightnessDown();
}
void BrightnessUp() {
BrightnessControlDelegate* delegate =
Shell::Get()->brightness_control_delegate();
if (delegate)
delegate->HandleBrightnessUp();
}
void CycleBackwardMru(bool same_app_only) {
Shell::Get()->window_cycle_controller()->HandleCycleWindow(
WindowCycleController::WindowCyclingDirection::kBackward, same_app_only);
}
void CycleForwardMru(bool same_app_only) {
Shell::Get()->window_cycle_controller()->HandleCycleWindow(
WindowCycleController::WindowCyclingDirection::kForward, same_app_only);
}
void CycleUser(CycleUserDirection direction) {
Shell::Get()->session_controller()->CycleActiveUser(direction);
}
void DisableCapsLock() {
Shell::Get()->ime_controller()->SetCapsLockEnabled(false);
}
void DumpCalendarModel() {
Shell::Get()->system_tray_model()->calendar_model()->DebugDump();
}
void FocusCameraPreview() {
auto* camera_controller = CaptureModeController::Get()->camera_controller();
DCHECK(camera_controller);
camera_controller->PseudoFocusCameraPreview();
}
void FocusPip() {
auto* widget = FindPipWidget();
if (widget)
Shell::Get()->focus_cycler()->FocusWidget(widget);
}
void FocusShelf() {
if (Shell::Get()->session_controller()->IsRunningInAppMode()) {
// If floating accessibility menu is shown, focus on it instead of the
// shelf.
FloatingAccessibilityController* floating_menu =
Shell::Get()->accessibility_controller()->GetFloatingMenuController();
if (floating_menu) {
floating_menu->FocusOnMenu();
}
return;
}
// TODO(jamescook): Should this be GetRootWindowForNewWindows()?
// Focus the home button.
Shelf* shelf = Shelf::ForWindow(Shell::GetPrimaryRootWindow());
shelf->shelf_focus_cycler()->FocusNavigation(false /* lastElement */);
}
void KeyboardBrightnessDown() {
KeyboardBrightnessControlDelegate* delegate =
Shell::Get()->keyboard_brightness_control_delegate();
if (delegate)
delegate->HandleKeyboardBrightnessDown();
}
void KeyboardBrightnessUp() {
KeyboardBrightnessControlDelegate* delegate =
Shell::Get()->keyboard_brightness_control_delegate();
if (delegate)
delegate->HandleKeyboardBrightnessUp();
}
void LaunchAppN(int n) {
Shelf::LaunchShelfItem(n);
}
void LaunchLastApp() {
Shelf::LaunchShelfItem(-1);
}
void LockPressed(bool pressed) {
Shell::Get()->power_button_controller()->OnLockButtonEvent(pressed,
base::TimeTicks());
}
void LockScreen() {
Shell::Get()->session_controller()->LockScreen();
}
void MaybeTakePartialScreenshot() {
// If a capture mode session is already running, this shortcut will be treated
// as a no-op.
if (CaptureModeController::Get()->IsActive())
return;
base::RecordAction(base::UserMetricsAction("Accel_Take_Partial_Screenshot"));
EnterImageCaptureMode(CaptureModeSource::kRegion,
CaptureModeEntryType::kAccelTakePartialScreenshot);
}
void MaybeTakeWindowScreenshot() {
// If a capture mode session is already running, this shortcut will be treated
// as a no-op.
if (CaptureModeController::Get()->IsActive())
return;
base::RecordAction(base::UserMetricsAction("Accel_Take_Window_Screenshot"));
EnterImageCaptureMode(CaptureModeSource::kWindow,
CaptureModeEntryType::kAccelTakeWindowScreenshot);
}
void MediaFastForward() {
Shell::Get()->media_controller()->HandleMediaSeekForward();
}
void MediaNextTrack() {
Shell::Get()->media_controller()->HandleMediaNextTrack();
}
void MediaPause() {
Shell::Get()->media_controller()->HandleMediaPause();
}
void MediaPlay() {
Shell::Get()->media_controller()->HandleMediaPlay();
}
void MediaPlayPause() {
Shell::Get()->media_controller()->HandleMediaPlayPause();
}
void MediaPrevTrack() {
Shell::Get()->media_controller()->HandleMediaPrevTrack();
}
void MediaRewind() {
Shell::Get()->media_controller()->HandleMediaSeekBackward();
}
void MediaStop() {
Shell::Get()->media_controller()->HandleMediaStop();
}
void MicrophoneMuteToggle() {
auto* const audio_handler = CrasAudioHandler::Get();
const bool mute = !audio_handler->IsInputMuted();
if (mute)
base::RecordAction(base::UserMetricsAction("Keyboard_Microphone_Muted"));
else
base::RecordAction(base::UserMetricsAction("Keyboard_Microphone_Unmuted"));
audio_handler->SetInputMute(
mute, CrasAudioHandler::InputMuteChangeMethod::kKeyboardButton);
}
void MoveActiveItem(bool going_left) {
auto* desks_controller = DesksController::Get();
if (desks_controller->AreDesksBeingModified())
return;
aura::Window* window_to_move = nullptr;
auto* overview_controller = Shell::Get()->overview_controller();
const bool in_overview = overview_controller->InOverviewSession();
if (in_overview) {
window_to_move =
overview_controller->overview_session()->GetHighlightedWindow();
} else {
window_to_move = window_util::GetActiveWindow();
}
if (!window_to_move || !desks_util::BelongsToActiveDesk(window_to_move))
return;
Desk* target_desk = nullptr;
if (going_left) {
target_desk = desks_controller->GetPreviousDesk();
base::RecordAction(base::UserMetricsAction("Accel_Desks_MoveWindowLeft"));
} else {
target_desk = desks_controller->GetNextDesk();
base::RecordAction(base::UserMetricsAction("Accel_Desks_MoveWindowRight"));
}
if (!target_desk)
return;
if (!in_overview) {
desks_animations::PerformWindowMoveToDeskAnimation(window_to_move,
going_left);
}
if (!desks_controller->MoveWindowFromActiveDeskTo(
window_to_move, target_desk, window_to_move->GetRootWindow(),
DesksMoveWindowFromActiveDeskSource::kShortcut)) {
return;
}
if (in_overview) {
// We should not exit overview as a result of this shortcut.
DCHECK(overview_controller->InOverviewSession());
overview_controller->overview_session()->PositionWindows(/*animate=*/true);
}
}
void MoveActiveWindowBetweenDisplays() {
display_move_window_util::HandleMoveActiveWindowBetweenDisplays();
}
void NewDesk() {
auto* desks_controller = DesksController::Get();
if (!desks_controller->CanCreateDesks()) {
ShowToast(kVirtualDesksToastId, ToastCatalogName::kVirtualDesksLimitMax,
l10n_util::GetStringUTF16(IDS_ASH_DESKS_MAX_NUM_REACHED));
return;
}
if (desks_controller->AreDesksBeingModified())
return;
// Add a new desk and switch to it.
const size_t new_desk_index = desks_controller->desks().size();
desks_controller->NewDesk(DesksCreationRemovalSource::kKeyboard);
const Desk* desk = desks_controller->desks()[new_desk_index].get();
desks_controller->ActivateDesk(desk, DesksSwitchSource::kNewDeskShortcut);
base::RecordAction(base::UserMetricsAction("Accel_Desks_NewDesk"));
}
void NewIncognitoWindow() {
NewWindowDelegate::GetPrimary()->NewWindow(
/*is_incognito=*/true,
/*should_trigger_session_restore=*/false);
}
void NewTab() {
NewWindowDelegate::GetPrimary()->NewTab();
}
void NewWindow() {
NewWindowDelegate::GetPrimary()->NewWindow(
/*is_incognito=*/false,
/*should_trigger_session_restore=*/false);
}
void OpenCalculator() {
NewWindowDelegate::GetInstance()->OpenCalculator();
}
void OpenCrosh() {
NewWindowDelegate::GetInstance()->OpenCrosh();
}
void OpenDiagnostics() {
NewWindowDelegate::GetInstance()->OpenDiagnostics();
}
void OpenFeedbackPage() {
NewWindowDelegate::GetInstance()->OpenFeedbackPage();
}
void OpenFileManager() {
NewWindowDelegate::GetInstance()->OpenFileManager();
}
void OpenHelp() {
NewWindowDelegate::GetInstance()->OpenGetHelp();
}
void PowerPressed(bool pressed) {
Shell::Get()->power_button_controller()->OnPowerButtonEvent(
pressed, base::TimeTicks());
}
void RemoveCurrentDesk() {
if (window_util::IsAnyWindowDragged())
return;
auto* desks_controller = DesksController::Get();
if (!desks_controller->CanRemoveDesks()) {
ShowToast(kVirtualDesksToastId, ToastCatalogName::kVirtualDesksLimitMin,
l10n_util::GetStringUTF16(IDS_ASH_DESKS_MIN_NUM_REACHED));
return;
}
if (desks_controller->AreDesksBeingModified())
return;
// TODO(afakhry): Finalize the desk removal animation outside of overview with
// UX. https://crbug.com/977434.
desks_controller->RemoveDesk(desks_controller->active_desk(),
DesksCreationRemovalSource::kKeyboard,
DeskCloseType::kCombineDesks);
base::RecordAction(base::UserMetricsAction("Accel_Desks_RemoveDesk"));
}
void ResetDisplayZoom() {
base::RecordAction(base::UserMetricsAction("Accel_Scale_Ui_Reset"));
display::DisplayManager* display_manager = Shell::Get()->display_manager();
gfx::Point point = display::Screen::GetScreen()->GetCursorScreenPoint();
display::Display display =
display::Screen::GetScreen()->GetDisplayNearestPoint(point);
display_manager->ResetDisplayZoom(display.id());
}
void RestoreTab() {
NewWindowDelegate::GetPrimary()->RestoreTab();
}
void RotateActiveWindow() {
aura::Window* active_window = window_util::GetActiveWindow();
if (!active_window)
return;
// The rotation animation bases its target transform on the current
// rotation and position. Since there could be an animation in progress
// right now, queue this animation so when it starts it picks up a neutral
// rotation and position. Use replace so we only enqueue one at a time.
active_window->layer()->GetAnimator()->set_preemption_strategy(
ui::LayerAnimator::REPLACE_QUEUED_ANIMATIONS);
active_window->layer()->GetAnimator()->StartAnimation(
new ui::LayerAnimationSequence(
std::make_unique<WindowRotation>(360, active_window->layer())));
}
void RotatePaneFocus(FocusCycler::Direction direction) {
Shell::Get()->focus_cycler()->RotateFocus(direction);
}
void RotateScreen() {
if (Shell::Get()->display_manager()->IsInUnifiedMode())
return;
base::RecordAction(UserMetricsAction("Accel_Rotate_Screen"));
const bool dialog_ever_accepted =
Shell::Get()
->accessibility_controller()
->HasDisplayRotationAcceleratorDialogBeenAccepted();
if (!dialog_ever_accepted) {
Shell::Get()->accessibility_controller()->ShowConfirmationDialog(
l10n_util::GetStringUTF16(IDS_ASH_ROTATE_SCREEN_TITLE),
l10n_util::GetStringUTF16(IDS_ASH_ROTATE_SCREEN_BODY),
base::BindOnce(&OnRotationDialogAccepted),
base::BindOnce(&OnRotationDialogCancelled),
/*on_close_callback=*/base::DoNothing());
} else {
RecordRotationAcceleratorAction(
RotationAcceleratorAction::kAlreadyAcceptedDialog);
RotateScreenImpl();
}
}
void ShiftPrimaryDisplay() {
display::DisplayManager* display_manager = Shell::Get()->display_manager();
CHECK_GE(display_manager->GetNumDisplays(), 2U);
const int64_t primary_display_id =
display::Screen::GetScreen()->GetPrimaryDisplay().id();
const display::Displays& active_display_list =
display_manager->active_display_list();
auto primary_display_iter = base::ranges::find(
active_display_list, primary_display_id, &display::Display::id);
DCHECK(primary_display_iter != active_display_list.end());
++primary_display_iter;
// If we've reach the end of |active_display_list|, wrap back around to the
// front.
if (primary_display_iter == active_display_list.end())
primary_display_iter = active_display_list.begin();
Shell::Get()->display_configuration_controller()->SetPrimaryDisplayId(
primary_display_iter->id(), true /* throttle */);
}
void ShowEmojiPicker() {
ui::ShowEmojiPanel();
}
void ShowKeyboardShortcutViewer() {
NewWindowDelegate::GetInstance()->ShowKeyboardShortcutViewer();
}
void ShowStylusTools() {
GetPaletteTray()->ShowBubble();
}
void ShowTaskManager() {
NewWindowDelegate::GetInstance()->ShowTaskManager();
}
void Suspend() {
chromeos::PowerManagerClient::Get()->RequestSuspend();
}
void SwitchToNextIme() {
Shell::Get()->ime_controller()->SwitchToNextIme();
}
void ToggleAppList(AppListShowSource show_source,
base::TimeTicks event_time_stamp) {
aura::Window* const root_window = Shell::GetRootWindowForNewWindows();
Shell::Get()->app_list_controller()->ToggleAppList(
display::Screen::GetScreen()->GetDisplayNearestWindow(root_window).id(),
show_source, event_time_stamp);
}
void TakeScreenshot(bool from_snapshot_key) {
// If it is the snip key, toggle capture mode unless the session is blocked,
// in which case, it behaves like a fullscreen screenshot.
auto* capture_mode_controller = CaptureModeController::Get();
if (from_snapshot_key &&
!Shell::Get()->session_controller()->IsUserSessionBlocked()) {
if (capture_mode_controller->IsActive())
capture_mode_controller->Stop();
else
capture_mode_controller->Start(CaptureModeEntryType::kSnipKey);
return;
}
capture_mode_controller->CaptureScreenshotsOfAllDisplays();
}
void ToggleAmbientMode() {
Shell::Get()->ambient_controller()->ToggleInSessionUi();
}
void ToggleAssignToAllDesk() {
auto* active_window = window_util::GetActiveWindow();
if (!active_window)
return;
// Only children of the desk container should have their assigned to all
// desks state toggled to avoid interfering with special windows like
// always-on-top windows, floated windows, etc.
if (desks_util::IsActiveDeskContainer(active_window->parent())) {
const bool is_already_visible_on_all_desks =
desks_util::IsWindowVisibleOnAllWorkspaces(active_window);
if (!is_already_visible_on_all_desks) {
UMA_HISTOGRAM_ENUMERATION(
chromeos::kDesksAssignToAllDesksSourceHistogramName,
chromeos::DesksAssignToAllDesksSource::kKeyboardShortcut);
}
active_window->SetProperty(
aura::client::kWindowWorkspaceKey,
is_already_visible_on_all_desks
? aura::client::kWindowWorkspaceUnassignedWorkspace
: aura::client::kWindowWorkspaceVisibleOnAllWorkspaces);
}
}
void ToggleAssistant() {
using assistant::AssistantAllowedState;
switch (AssistantState::Get()->allowed_state().value_or(
AssistantAllowedState::ALLOWED)) {
case AssistantAllowedState::DISALLOWED_BY_NONPRIMARY_USER:
// Show a toast if the active user is not primary.
ShowToast(kAssistantErrorToastId, ToastCatalogName::kAssistantError,
l10n_util::GetStringUTF16(
IDS_ASH_ASSISTANT_SECONDARY_USER_TOAST_MESSAGE));
return;
case AssistantAllowedState::DISALLOWED_BY_LOCALE:
// Show a toast if the Assistant is disabled due to unsupported
// locales.
ShowToast(kAssistantErrorToastId, ToastCatalogName::kAssistantError,
l10n_util::GetStringUTF16(
IDS_ASH_ASSISTANT_LOCALE_UNSUPPORTED_TOAST_MESSAGE));
return;
case AssistantAllowedState::DISALLOWED_BY_POLICY:
// Show a toast if the Assistant is disabled due to enterprise policy.
ShowToast(kAssistantErrorToastId, ToastCatalogName::kAssistantError,
l10n_util::GetStringUTF16(
IDS_ASH_ASSISTANT_DISABLED_BY_POLICY_MESSAGE));
return;
case AssistantAllowedState::DISALLOWED_BY_DEMO_MODE:
// Show a toast if the Assistant is disabled due to being in Demo
// Mode.
ShowToast(kAssistantErrorToastId, ToastCatalogName::kAssistantError,
l10n_util::GetStringUTF16(
IDS_ASH_ASSISTANT_DISABLED_IN_DEMO_MODE_MESSAGE));
return;
case AssistantAllowedState::DISALLOWED_BY_PUBLIC_SESSION:
// Show a toast if the Assistant is disabled due to being in public
// session.
ShowToast(kAssistantErrorToastId, ToastCatalogName::kAssistantError,
l10n_util::GetStringUTF16(
IDS_ASH_ASSISTANT_DISABLED_IN_PUBLIC_SESSION_MESSAGE));
return;
case AssistantAllowedState::DISALLOWED_BY_INCOGNITO:
// Show a toast if the Assistant is disabled due to being in Incognito
// mode.
ShowToast(kAssistantErrorToastId, ToastCatalogName::kAssistantError,
l10n_util::GetStringUTF16(
IDS_ASH_ASSISTANT_DISABLED_IN_GUEST_MESSAGE));
return;
case AssistantAllowedState::DISALLOWED_BY_ACCOUNT_TYPE:
// Show a toast if the Assistant is disabled due to the account type.
ShowToast(kAssistantErrorToastId, ToastCatalogName::kAssistantError,
l10n_util::GetStringUTF16(
IDS_ASH_ASSISTANT_DISABLED_BY_ACCOUNT_MESSAGE));
return;
case AssistantAllowedState::DISALLOWED_BY_KIOSK_MODE:
// No need to show toast in KIOSK mode.
return;
case AssistantAllowedState::ALLOWED:
// Nothing need to do if allowed.
break;
}
AssistantUiController::Get()->ToggleUi(
/*entry_point=*/assistant::AssistantEntryPoint::kHotkey,
/*exit_point=*/assistant::AssistantExitPoint::kHotkey);
}
void ToggleCalendar() {
aura::Window* target_root = Shell::GetRootWindowForNewWindows();
StatusAreaWidget* status_area_widget =
RootWindowController::ForWindow(target_root)->GetStatusAreaWidget();
UnifiedSystemTray* tray = status_area_widget->unified_system_tray();
// If currently showing the calendar view, close it.
if (tray->IsShowingCalendarView()) {
tray->CloseBubble();
return;
}
// If currently not showing the calendar view, show the bubble if needed then
// show the calendar view.
if (!tray->IsBubbleShown()) {
// Set `DateTray` to be active prior to showing the bubble, this prevents
// flashing of the status area. See crbug.com/1332603.
status_area_widget->date_tray()->SetIsActive(true);
tray->ShowBubble();
}
tray->bubble()->ShowCalendarView(
calendar_metrics::CalendarViewShowSource::kAccelerator,
calendar_metrics::CalendarEventSource::kKeyboard);
}
void ToggleCapsLock() {
ImeControllerImpl* ime_controller = Shell::Get()->ime_controller();
ime_controller->SetCapsLockEnabled(!ime_controller->IsCapsLockEnabled());
}
void ToggleClipboardHistory(bool is_plain_text_paste) {
DCHECK(Shell::Get()->clipboard_history_controller());
Shell::Get()->clipboard_history_controller()->ToggleMenuShownByAccelerator(
is_plain_text_paste);
}
void ToggleDictation() {
Shell::Get()->accessibility_controller()->ToggleDictationFromSource(
DictationToggleSource::kKeyboard);
}
void ToggleDockedMagnifier() {
const bool is_shortcut_enabled =
IsAccessibilityShortcutEnabled(prefs::kDockedMagnifierEnabled);
base::UmaHistogramBoolean(kAccessibilityDockedMagnifierShortcut,
is_shortcut_enabled);
Shell* shell = Shell::Get();
RemoveDockedMagnifierNotification();
if (!is_shortcut_enabled) {
ShowDockedMagnifierDisabledByAdminNotification(
shell->docked_magnifier_controller()->GetEnabled());
return;
}
DockedMagnifierController* docked_magnifier_controller =
shell->docked_magnifier_controller();
AccessibilityControllerImpl* accessibility_controller =
shell->accessibility_controller();
const bool current_enabled = docked_magnifier_controller->GetEnabled();
const bool dialog_ever_accepted =
accessibility_controller->docked_magnifier().WasDialogAccepted();
if (!current_enabled && !dialog_ever_accepted) {
accessibility_controller->ShowConfirmationDialog(
l10n_util::GetStringUTF16(IDS_ASH_DOCKED_MAGNIFIER_TITLE),
l10n_util::GetStringUTF16(IDS_ASH_DOCKED_MAGNIFIER_BODY),
base::BindOnce([]() {
Shell::Get()
->accessibility_controller()
->docked_magnifier()
.SetDialogAccepted();
SetDockedMagnifierEnabled(true);
}),
/*on_cancel_callback=*/base::DoNothing(),
/*on_close_callback=*/base::DoNothing());
} else {
SetDockedMagnifierEnabled(!current_enabled);
}
}
void ToggleFloating() {
DCHECK(chromeos::wm::features::IsFloatWindowEnabled());
aura::Window* window = window_util::GetActiveWindow();
DCHECK(window);
DCHECK(chromeos::wm::CanFloatWindow(window));
Shell::Get()->float_controller()->ToggleFloat(window);
base::RecordAction(base::UserMetricsAction("Accel_Toggle_Floating"));
}
void ToggleFullscreen() {
OverviewController* overview_controller = Shell::Get()->overview_controller();
// Disable fullscreen while overview animation is running due to
// http://crbug.com/1094739
if (overview_controller->IsInStartAnimation())
return;
aura::Window* active_window = window_util::GetActiveWindow();
if (!active_window)
return;
const WMEvent event(WM_EVENT_TOGGLE_FULLSCREEN);
WindowState::Get(active_window)->OnWMEvent(&event);
}
void ToggleFullscreenMagnifier() {
const bool is_shortcut_enabled = IsAccessibilityShortcutEnabled(
prefs::kAccessibilityScreenMagnifierEnabled);
base::UmaHistogramBoolean(kAccessibilityScreenMagnifierShortcut,
is_shortcut_enabled);
Shell* shell = Shell::Get();
RemoveFullscreenMagnifierNotification();
if (!is_shortcut_enabled) {
ShowFullscreenMagnifierDisabledByAdminNotification(
shell->fullscreen_magnifier_controller()->IsEnabled());
return;
}
FullscreenMagnifierController* magnification_controller =
shell->fullscreen_magnifier_controller();
AccessibilityControllerImpl* accessibility_controller =
shell->accessibility_controller();
const bool current_enabled = magnification_controller->IsEnabled();
const bool dialog_ever_accepted =
accessibility_controller->fullscreen_magnifier().WasDialogAccepted();
if (!current_enabled && !dialog_ever_accepted) {
accessibility_controller->ShowConfirmationDialog(
l10n_util::GetStringUTF16(IDS_ASH_SCREEN_MAGNIFIER_TITLE),
l10n_util::GetStringUTF16(IDS_ASH_SCREEN_MAGNIFIER_BODY),
base::BindOnce([]() {
Shell::Get()
->accessibility_controller()
->fullscreen_magnifier()
.SetDialogAccepted();
SetFullscreenMagnifierEnabled(true);
}),
/*on_cancel_callback=*/base::DoNothing(),
/*on_close_callback=*/base::DoNothing());
} else {
SetFullscreenMagnifierEnabled(!current_enabled);
}
}
void ToggleHighContrast() {
const bool is_shortcut_enabled =
IsAccessibilityShortcutEnabled(prefs::kAccessibilityHighContrastEnabled);
base::UmaHistogramBoolean(kAccessibilityHighContrastShortcut,
is_shortcut_enabled);
Shell* shell = Shell::Get();
RemoveHighContrastNotification();
if (!is_shortcut_enabled) {
ShowHighContrastDisabledByAdminNotification(
shell->accessibility_controller()->high_contrast().enabled());
return;
}
AccessibilityControllerImpl* controller = shell->accessibility_controller();
const bool current_enabled = controller->high_contrast().enabled();
const bool dialog_ever_accepted =
controller->high_contrast().WasDialogAccepted();
if (!current_enabled && !dialog_ever_accepted) {
controller->ShowConfirmationDialog(
l10n_util::GetStringUTF16(IDS_ASH_HIGH_CONTRAST_TITLE),
l10n_util::GetStringUTF16(IDS_ASH_HIGH_CONTRAST_BODY),
base::BindOnce([]() {
Shell::Get()
->accessibility_controller()
->high_contrast()
.SetDialogAccepted();
SetHighContrastEnabled(true);
}),
/*on_cancel_callback=*/base::DoNothing(),
/*on_close_callback=*/base::DoNothing());
} else {
SetHighContrastEnabled(!current_enabled);
}
}
void ToggleSpokenFeedback() {
const bool is_shortcut_enabled = IsAccessibilityShortcutEnabled(
prefs::kAccessibilitySpokenFeedbackEnabled);
base::UmaHistogramBoolean(kAccessibilitySpokenFeedbackShortcut,
is_shortcut_enabled);
Shell* shell = Shell::Get();
const bool old_value =
shell->accessibility_controller()->spoken_feedback().enabled();
RemoveSpokenFeedbackNotification();
if (!is_shortcut_enabled) {
ShowSpokenFeedbackDisabledByAdminNotification(old_value);
return;
}
shell->accessibility_controller()->SetSpokenFeedbackEnabled(
!old_value, A11Y_NOTIFICATION_SHOW);
}
void ToggleImeMenuBubble() {
StatusAreaWidget* status_area_widget =
Shelf::ForWindow(Shell::GetPrimaryRootWindow())->GetStatusAreaWidget();
if (status_area_widget) {
ImeMenuTray* ime_menu_tray = status_area_widget->ime_menu_tray();
if (!ime_menu_tray || !ime_menu_tray->GetVisible()) {
// Do nothing when Ime tray is not being shown.
return;
}
if (ime_menu_tray->GetBubbleView()) {
ime_menu_tray->CloseBubble();
} else {
ime_menu_tray->ShowBubble();
}
}
}
void ToggleKeyboardBacklight() {
KeyboardBrightnessControlDelegate* delegate =
Shell::Get()->keyboard_brightness_control_delegate();
delegate->HandleToggleKeyboardBacklight();
}
void ToggleMaximized() {
aura::Window* active_window = window_util::GetActiveWindow();
if (!active_window)
return;
base::RecordAction(base::UserMetricsAction("Accel_Toggle_Maximized"));
WMEvent event(WM_EVENT_TOGGLE_MAXIMIZE);
WindowState::Get(active_window)->OnWMEvent(&event);
}
bool ToggleMinimized() {
aura::Window* window = window_util::GetActiveWindow();
// Attempt to restore the window that would be cycled through next from
// the launcher when there is no active window.
if (!window) {
// Do not unminimize a window on an inactive desk, since this will cause
// desks to switch and that will be unintentional for the user.
MruWindowTracker::WindowList mru_windows(
Shell::Get()->mru_window_tracker()->BuildMruWindowList(kActiveDesk));
if (!mru_windows.empty())
WindowState::Get(mru_windows.front())->Activate();
return true;
}
WindowState* window_state = WindowState::Get(window);
if (!window_state->CanMinimize())
return false;
window_state->Minimize();
return true;
}
void ToggleResizeLockMenu() {
aura::Window* active_window = window_util::GetActiveWindow();
auto* frame_view = NonClientFrameViewAsh::Get(active_window);
frame_view->GetToggleResizeLockMenuCallback().Run();
}
void ToggleMessageCenterBubble() {
if (!features::IsQsRevampEnabled()) {
HandleToggleSystemTrayBubbleInternal(/*focus_message_center=*/true);
return;
}
aura::Window* target_root = Shell::GetRootWindowForNewWindows();
NotificationCenterTray* tray = RootWindowController::ForWindow(target_root)
->GetStatusAreaWidget()
->notification_center_tray();
// Show a toast if there are no notifications.
if (!tray->GetVisible()) {
ShowToast(kNotificationCenterTrayNoNotificationsToastId,
ash::ToastCatalogName::kNotificationCenterTrayNoNotifications,
l10n_util::GetStringUTF16(
IDS_ASH_MESSAGE_CENTER_ACCELERATOR_NO_NOTIFICATIONS));
return;
}
if (tray->GetBubbleWidget()) {
tray->CloseBubble();
} else {
tray->ShowBubble();
}
}
void ToggleMirrorMode() {
bool mirror = !Shell::Get()->display_manager()->IsInMirrorMode();
Shell::Get()->display_configuration_controller()->SetMirrorMode(
mirror, true /* throttle */);
}
void ToggleMultitaskMenu() {
DCHECK(chromeos::wm::features::IsFloatWindowEnabled());
aura::Window* window = window_util::GetActiveWindow();
DCHECK(window);
auto* frame_view = NonClientFrameViewAsh::Get(window);
if (!frame_view) {
// If `window` doesn't have a frame, it must be the multitask menu and have
// a transient parent for `CanToggleMultitaskMenu()` to arrive here.
auto* transient_parent = wm::GetTransientParent(window);
DCHECK(transient_parent);
frame_view = NonClientFrameViewAsh::Get(transient_parent);
}
DCHECK(frame_view);
auto* size_button =
frame_view->GetHeaderView()->caption_button_container()->size_button();
static_cast<chromeos::FrameSizeButton*>(size_button)->ToggleMultitaskMenu();
}
void ToggleOverview() {
OverviewController* overview_controller = Shell::Get()->overview_controller();
if (overview_controller->InOverviewSession())
overview_controller->EndOverview(OverviewEndAction::kAccelerator);
else
overview_controller->StartOverview(OverviewStartAction::kAccelerator);
}
void TogglePrivacyScreen() {
PrivacyScreenController* controller =
Shell::Get()->privacy_screen_controller();
controller->SetEnabled(
!controller->GetEnabled(),
PrivacyScreenController::kToggleUISurfaceKeyboardShortcut);
}
void ToggleProjectorMarker() {
auto* projector_controller = ProjectorController::Get();
if (projector_controller) {
projector_controller->ToggleAnnotationTray();
}
}
void ToggleSystemTrayBubble() {
HandleToggleSystemTrayBubbleInternal(false /*focus_message_center*/);
}
void ToggleUnifiedDesktop() {
Shell::Get()->display_manager()->SetUnifiedDesktopEnabled(
!Shell::Get()->display_manager()->unified_desktop_enabled());
}
void ToggleWifi() {
Shell::Get()->system_tray_notifier()->NotifyRequestToggleWifi();
}
void TopWindowMinimizeOnBack() {
WindowState::Get(window_util::GetTopWindow())->Minimize();
}
void TouchHudClear() {
RootWindowController::ForTargetRootWindow()->touch_hud_debug()->Clear();
}
void TouchHudModeChange() {
RootWindowController* controller =
RootWindowController::ForTargetRootWindow();
controller->touch_hud_debug()->ChangeToNextMode();
}
void UnpinWindow() {
aura::Window* pinned_window =
Shell::Get()->screen_pinning_controller()->pinned_window();
if (pinned_window)
WindowState::Get(pinned_window)->Restore();
}
void VolumeDown() {
auto* audio_handler = CrasAudioHandler::Get();
if (audio_handler->IsOutputMuted()) {
audio_handler->SetOutputVolumePercent(0);
} else {
if (audio_handler->IsOutputVolumeBelowDefaultMuteLevel())
audio_handler->SetOutputMute(true);
else
AcceleratorController::PlayVolumeAdjustmentSound();
if (features::IsAudioPeripheralVolumeGranularityEnabled())
audio_handler->DecreaseOutputVolumeByOneStep(kStepPercentage);
else
audio_handler->AdjustOutputVolumeByPercent(-kStepPercentage);
}
}
void VolumeMute() {
CrasAudioHandler::Get()->SetOutputMute(true);
}
void VolumeUp() {
auto* audio_handler = CrasAudioHandler::Get();
bool play_sound = false;
if (audio_handler->IsOutputMuted()) {
audio_handler->SetOutputMute(false);
audio_handler->AdjustOutputVolumeToAudibleLevel();
play_sound = true;
} else {
play_sound = audio_handler->GetOutputVolumePercent() != 100;
if (features::IsAudioPeripheralVolumeGranularityEnabled())
audio_handler->IncreaseOutputVolumeByOneStep(kStepPercentage);
else
audio_handler->AdjustOutputVolumeByPercent(kStepPercentage);
}
if (play_sound)
AcceleratorController::PlayVolumeAdjustmentSound();
}
void WindowMinimize() {
ToggleMinimized();
}
void WindowSnap(AcceleratorAction action) {
Shell* shell = Shell::Get();
const bool in_tablet = shell->tablet_mode_controller()->InTabletMode();
const bool in_overview = shell->overview_controller()->InOverviewSession();
if (action == WINDOW_CYCLE_SNAP_LEFT) {
if (in_tablet) {
RecordWindowSnapAcceleratorAction(
WindowSnapAcceleratorAction::kCycleLeftSnapInTablet);
} else if (in_overview) {
RecordWindowSnapAcceleratorAction(
WindowSnapAcceleratorAction::kCycleLeftSnapInClamshellOverview);
} else {
RecordWindowSnapAcceleratorAction(
WindowSnapAcceleratorAction::kCycleLeftSnapInClamshellNoOverview);
}
} else {
if (in_tablet) {
RecordWindowSnapAcceleratorAction(
WindowSnapAcceleratorAction::kCycleRightSnapInTablet);
} else if (in_overview) {
RecordWindowSnapAcceleratorAction(
WindowSnapAcceleratorAction::kCycleRightSnapInClamshellOverview);
} else {
RecordWindowSnapAcceleratorAction(
WindowSnapAcceleratorAction::kCycleRightSnapInClamshellNoOverview);
}
}
const WMEvent event(action == WINDOW_CYCLE_SNAP_LEFT
? WM_EVENT_CYCLE_SNAP_PRIMARY
: WM_EVENT_CYCLE_SNAP_SECONDARY);
aura::Window* active_window = window_util::GetActiveWindow();
DCHECK(active_window);
auto* window_state = WindowState::Get(active_window);
window_state->set_snap_action_source(
WindowSnapActionSource::kKeyboardShortcutToSnap);
window_state->OnWMEvent(&event);
}
bool ZoomDisplay(bool up) {
if (up)
base::RecordAction(base::UserMetricsAction("Accel_Scale_Ui_Up"));
else
base::RecordAction(base::UserMetricsAction("Accel_Scale_Ui_Down"));
display::DisplayManager* display_manager = Shell::Get()->display_manager();
gfx::Point point = display::Screen::GetScreen()->GetCursorScreenPoint();
display::Display display =
display::Screen::GetScreen()->GetDisplayNearestPoint(point);
return display_manager->ZoomDisplay(display.id(), up);
}
} // namespace accelerators
} // namespace ash