Commit a9661d0a authored by Cattalyya Nuengsigkapian's avatar Cattalyya Nuengsigkapian Committed by Chromium LUCI CQ

[Reland] Bento: Support switching between two Alt-Tab modes.

This is a reland of http://crrev.com/c/2581684, which was reverted by
http://crrev.com/c/2601580 due to flakiness of the test
NoWindowInActiveDesk. The issue is that CompleteCycling() activates
a window in another desk, so deskSwitchAnimation takes a screenshot
and sometimes fail if the test ends before it is taken.

TBR=afakhry@chromium.org

Original CL description:

- Add a tab slider and update windows shown in Alt-Tab so that
"Current desk" mode only display windows in the active desk, while
"All desks" display all windows.
- Handle special cases: As before, entering Alt-Tab with less than two
windows aren't allowed. However, if users switch from the all-desks mode
to the current-desk mode resulting in zero or one window,
it should show "no recent items" or show a window consecutively.
- Hide behind Bento flag and make sure to overwrite
kLimitAltTabToActiveDesk in case of conflict, so the old flag works.
- Disable remapping Alt+LeftClick to a right click when using Alt-Tab.
- Ash unittests for switching between modes and special cases.

Bug: 1142726
Test: A manual test (a video in crbug) and unit tests
`ash_unittests --gtest_filter=ModeSelectionWindowCycleControllerTest.*`

Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2581684Reviewed-by: default avatarAhmed Fakhry <afakhry@chromium.org>
Reviewed-by: default avatarXiaoqian Dai <xdai@chromium.org>
Commit-Queue: Cattalyya Nuengsigkapian <cattalyya@chromium.org>
Cr-Original-Commit-Position: refs/heads/master@{#838709}
Change-Id: I52c67cbb878fc33dac5ee0d8b59cc4310eb08f00
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2601480
Cr-Commit-Position: refs/heads/master@{#839175}
parent cf4cb88a
...@@ -1678,6 +1678,10 @@ component("ash") { ...@@ -1678,6 +1678,10 @@ component("ash") {
"wm/window_cycle_event_filter.h", "wm/window_cycle_event_filter.h",
"wm/window_cycle_list.cc", "wm/window_cycle_list.cc",
"wm/window_cycle_list.h", "wm/window_cycle_list.h",
"wm/window_cycle_tab_slider.cc",
"wm/window_cycle_tab_slider.h",
"wm/window_cycle_tab_slider_button.cc",
"wm/window_cycle_tab_slider_button.h",
"wm/window_dimmer.cc", "wm/window_dimmer.cc",
"wm/window_dimmer.h", "wm/window_dimmer.h",
"wm/window_finder.cc", "wm/window_finder.cc",
......
...@@ -3131,6 +3131,14 @@ Here are some things you can try to get started. ...@@ -3131,6 +3131,14 @@ Here are some things you can try to get started.
Window <ph name="WINDOW_TITLE">$1<ex>Files</ex></ph> moved from Desk <ph name="ACTIVE_DESK">$2</ph> to Desk <ph name="TARGET_DESK">$3</ph> Window <ph name="WINDOW_TITLE">$1<ex>Files</ex></ph> moved from Desk <ph name="ACTIVE_DESK">$2</ph> to Desk <ph name="TARGET_DESK">$3</ph>
</message> </message>
<!-- Alt Tab -->
<message name="IDS_ASH_ALT_TAB_ALL_DESKS_MODE" desc="Alt-tab shows windows from all desks.">
All desks
</message>
<message name="IDS_ASH_ALT_TAB_CURRENT_DESK_MODE" desc="Alt-tab shows only windows from the current desk.">
Current desk
</message>
<!-- Back gesture --> <!-- Back gesture -->
<message name="IDS_ASH_BACK_GESTURE_CONTEXTUAL_NUDGE" desc="Information shown in the suggestion label for the contextual nudge of the back gesture in tablet mode"> <message name="IDS_ASH_BACK_GESTURE_CONTEXTUAL_NUDGE" desc="Information shown in the suggestion label for the contextual nudge of the back gesture in tablet mode">
Swipe from the left to go back Swipe from the left to go back
......
a497fea9619c636ff78f2f5bba3724d5e1ad84d4
\ No newline at end of file
df4b84a84d9cad8a10afc284876e6a4d279435e0
\ No newline at end of file
...@@ -60,6 +60,7 @@ void EventRewriterControllerImpl::Initialize( ...@@ -60,6 +60,7 @@ void EventRewriterControllerImpl::Initialize(
std::make_unique<ui::EventRewriterChromeOS>( std::make_unique<ui::EventRewriterChromeOS>(
event_rewriter_delegate, Shell::Get()->sticky_keys_controller(), event_rewriter_delegate, Shell::Get()->sticky_keys_controller(),
privacy_screen_supported); privacy_screen_supported);
event_rewriter_chromeos_ = event_rewriter_chromeos.get();
std::unique_ptr<AccessibilityEventRewriter> accessibility_event_rewriter = std::unique_ptr<AccessibilityEventRewriter> accessibility_event_rewriter =
std::make_unique<AccessibilityEventRewriter>( std::make_unique<AccessibilityEventRewriter>(
...@@ -112,6 +113,12 @@ void EventRewriterControllerImpl::SetSendMouseEvents(bool value) { ...@@ -112,6 +113,12 @@ void EventRewriterControllerImpl::SetSendMouseEvents(bool value) {
accessibility_event_rewriter_->set_send_mouse_events(value); accessibility_event_rewriter_->set_send_mouse_events(value);
} }
void EventRewriterControllerImpl::SetAltLeftClickRemappingEnabled(
bool enabled) {
if (event_rewriter_chromeos_)
event_rewriter_chromeos_->set_alt_left_click_remapping_enabled(enabled);
}
void EventRewriterControllerImpl::OnHostInitialized( void EventRewriterControllerImpl::OnHostInitialized(
aura::WindowTreeHost* host) { aura::WindowTreeHost* host) {
for (const auto& rewriter : rewriters_) for (const auto& rewriter : rewriters_)
......
...@@ -45,6 +45,11 @@ class ASH_EXPORT EventRewriterControllerImpl : public EventRewriterController, ...@@ -45,6 +45,11 @@ class ASH_EXPORT EventRewriterControllerImpl : public EventRewriterController,
// aura::EnvObserver: // aura::EnvObserver:
void OnHostInitialized(aura::WindowTreeHost* host) override; void OnHostInitialized(aura::WindowTreeHost* host) override;
// Enable/disable Alt + left click mapping in EventRewriterChromeOS. This
// only applies if the feature
// `chromeos::features::kUseSearchClickForRightClick` is not enabled.
void SetAltLeftClickRemappingEnabled(bool enabled);
private: private:
// The |EventRewriter|s managed by this controller. // The |EventRewriter|s managed by this controller.
std::vector<std::unique_ptr<ui::EventRewriter>> rewriters_; std::vector<std::unique_ptr<ui::EventRewriter>> rewriters_;
...@@ -52,6 +57,7 @@ class ASH_EXPORT EventRewriterControllerImpl : public EventRewriterController, ...@@ -52,6 +57,7 @@ class ASH_EXPORT EventRewriterControllerImpl : public EventRewriterController,
// Owned by |rewriters_|. // Owned by |rewriters_|.
AccessibilityEventRewriter* accessibility_event_rewriter_ = nullptr; AccessibilityEventRewriter* accessibility_event_rewriter_ = nullptr;
KeyboardDrivenEventRewriter* keyboard_driven_event_rewriter_ = nullptr; KeyboardDrivenEventRewriter* keyboard_driven_event_rewriter_ = nullptr;
ui::EventRewriterChromeOS* event_rewriter_chromeos_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(EventRewriterControllerImpl); DISALLOW_COPY_AND_ASSIGN(EventRewriterControllerImpl);
}; };
......
...@@ -697,10 +697,9 @@ void DesksController::ActivateDeskInternal(const Desk* desk, ...@@ -697,10 +697,9 @@ void DesksController::ActivateDeskInternal(const Desk* desk,
// If in the middle of a window cycle gesture, reset the window cycle list // If in the middle of a window cycle gesture, reset the window cycle list
// contents so it contains the new active desk's windows. // contents so it contains the new active desk's windows.
auto* shell = Shell::Get(); auto* shell = Shell::Get();
if (features::IsAltTabLimitedToActiveDesk()) {
auto* window_cycle_controller = shell->window_cycle_controller(); auto* window_cycle_controller = shell->window_cycle_controller();
if (window_cycle_controller->IsAltTabPerActiveDesk())
window_cycle_controller->MaybeResetCycleList(); window_cycle_controller->MaybeResetCycleList();
}
for (auto& observer : observers_) for (auto& observer : observers_)
observer.OnDeskActivationChanged(active_desk_, old_active); observer.OnDeskActivationChanged(active_desk_, old_active);
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "ash/wm/window_cycle_controller.h" #include "ash/wm/window_cycle_controller.h"
#include "ash/events/event_rewriter_controller_impl.h"
#include "ash/metrics/task_switch_metrics_recorder.h" #include "ash/metrics/task_switch_metrics_recorder.h"
#include "ash/metrics/task_switch_source.h" #include "ash/metrics/task_switch_source.h"
#include "ash/metrics/user_metrics_recorder.h" #include "ash/metrics/user_metrics_recorder.h"
...@@ -88,6 +89,8 @@ void WindowCycleController::HandleCycleWindow(Direction direction) { ...@@ -88,6 +89,8 @@ void WindowCycleController::HandleCycleWindow(Direction direction) {
if (!IsCycling()) if (!IsCycling())
StartCycling(); StartCycling();
// TODO(crbug.com/1157100): Handle window highlighting after switching
// alt-tab mode.
Step(direction); Step(direction);
} }
...@@ -107,6 +110,8 @@ void WindowCycleController::StartCycling() { ...@@ -107,6 +110,8 @@ void WindowCycleController::StartCycling() {
// the window view item for the preview is transparent // the window view item for the preview is transparent
// (http://crbug.com/895265). // (http://crbug.com/895265).
Shell::Get()->wallpaper_controller()->MaybeClosePreviewWallpaper(); Shell::Get()->wallpaper_controller()->MaybeClosePreviewWallpaper();
Shell::Get()->event_rewriter_controller()->SetAltLeftClickRemappingEnabled(
false);
WindowCycleController::WindowList window_list = CreateWindowList(); WindowCycleController::WindowList window_list = CreateWindowList();
SaveCurrentActiveDeskAndWindow(window_list); SaveCurrentActiveDeskAndWindow(window_list);
...@@ -160,7 +165,7 @@ bool WindowCycleController::IsWindowListVisible() { ...@@ -160,7 +165,7 @@ bool WindowCycleController::IsWindowListVisible() {
WindowCycleController::WindowList WindowCycleController::CreateWindowList() { WindowCycleController::WindowList WindowCycleController::CreateWindowList() {
WindowCycleController::WindowList window_list = WindowCycleController::WindowList window_list =
Shell::Get()->mru_window_tracker()->BuildWindowForCycleWithPipList( Shell::Get()->mru_window_tracker()->BuildWindowForCycleWithPipList(
features::IsAltTabLimitedToActiveDesk() ? kActiveDesk : kAllDesks); IsAltTabPerActiveDesk() ? kActiveDesk : kAllDesks);
// Window cycle list windows will handle showing their transient related // Window cycle list windows will handle showing their transient related
// windows, so if a window in |window_list| has a transient root also in // windows, so if a window in |window_list| has a transient root also in
// |window_list|, we can remove it as the transient root will handle showing // |window_list|, we can remove it as the transient root will handle showing
...@@ -169,6 +174,18 @@ WindowCycleController::WindowList WindowCycleController::CreateWindowList() { ...@@ -169,6 +174,18 @@ WindowCycleController::WindowList WindowCycleController::CreateWindowList() {
return window_list; return window_list;
} }
void WindowCycleController::SetAltTabMode(DesksMruType alt_tab_mode) {
if (alt_tab_mode_ == alt_tab_mode)
return;
alt_tab_mode_ = alt_tab_mode;
MaybeResetCycleList();
}
bool WindowCycleController::IsAltTabPerActiveDesk() {
return features::IsBentoEnabled() ? alt_tab_mode_ == kActiveDesk
: features::IsAltTabLimitedToActiveDesk();
}
void WindowCycleController::SaveCurrentActiveDeskAndWindow( void WindowCycleController::SaveCurrentActiveDeskAndWindow(
const WindowCycleController::WindowList& window_list) { const WindowCycleController::WindowList& window_list) {
active_desk_container_id_before_cycle_ = active_desk_container_id_before_cycle_ =
...@@ -204,6 +221,8 @@ void WindowCycleController::StopCycling() { ...@@ -204,6 +221,8 @@ void WindowCycleController::StopCycling() {
active_window_before_window_cycle_ = nullptr; active_window_before_window_cycle_ = nullptr;
active_desk_container_id_before_cycle_ = kShellWindowId_Invalid; active_desk_container_id_before_cycle_ = kShellWindowId_Invalid;
Shell::Get()->event_rewriter_controller()->SetAltLeftClickRemappingEnabled(
true);
} }
} // namespace ash } // namespace ash
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "ash/ash_export.h" #include "ash/ash_export.h"
#include "ash/public/cpp/shell_window_ids.h" #include "ash/public/cpp/shell_window_ids.h"
#include "ash/wm/mru_window_tracker.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/time/time.h" #include "base/time/time.h"
...@@ -86,6 +87,14 @@ class ASH_EXPORT WindowCycleController { ...@@ -86,6 +87,14 @@ class ASH_EXPORT WindowCycleController {
return window_cycle_list_.get(); return window_cycle_list_.get();
} }
// Sets alt-tab mode to all desks (default) or active desk.
void SetAltTabMode(DesksMruType alt_tab_mode_);
// Checks if alt-tab should be per active desk. If Bento is enabled, alt-tab
// mode depends on users' alt_tab_mode_ selection. Otherwise, it'll default
// to all desk unless LimitAltTabToActiveDesk flag is explicitly enabled.
bool IsAltTabPerActiveDesk();
private: private:
// Gets a list of windows from the currently open windows, removing windows // Gets a list of windows from the currently open windows, removing windows
// with transient roots already in the list. The returned list of windows // with transient roots already in the list. The returned list of windows
...@@ -115,6 +124,12 @@ class ASH_EXPORT WindowCycleController { ...@@ -115,6 +124,12 @@ class ASH_EXPORT WindowCycleController {
// Non-null while actively cycling. // Non-null while actively cycling.
std::unique_ptr<WindowCycleEventFilter> event_filter_; std::unique_ptr<WindowCycleEventFilter> event_filter_;
// Tracks alt-tab mode to display all windows from all desks by default.
// An alternative mode is active desk where only windows from the current desk
// are shown in alt-tab.
// TODO(crbug.com/1157105): Store per-desk mode in primary user pref.
DesksMruType alt_tab_mode_ = DesksMruType::kAllDesks;
DISALLOW_COPY_AND_ASSIGN(WindowCycleController); DISALLOW_COPY_AND_ASSIGN(WindowCycleController);
}; };
......
...@@ -124,6 +124,20 @@ class WindowCycleControllerTest : public AshTestBase { ...@@ -124,6 +124,20 @@ class WindowCycleControllerTest : public AshTestBase {
->GetWindowCycleItemViewsForTesting(); ->GetWindowCycleItemViewsForTesting();
} }
const views::View::Views& GetWindowCycleTabSliderViews() const {
return Shell::Get()
->window_cycle_controller()
->window_cycle_list()
->GetWindowCycleTabSliderViewsForTesting();
}
const views::Label* GetWindowCycleNoRecentItemsLabel() const {
return Shell::Get()
->window_cycle_controller()
->window_cycle_list()
->GetWindowCycleNoRecentItemsLabelForTesting();
}
const aura::Window* GetTargetWindow() const { const aura::Window* GetTargetWindow() const {
return Shell::Get() return Shell::Get()
->window_cycle_controller() ->window_cycle_controller()
...@@ -1337,4 +1351,191 @@ TEST_F(WindowCycleControllerTest, DoubleAltTabWithDeskSwitch) { ...@@ -1337,4 +1351,191 @@ TEST_F(WindowCycleControllerTest, DoubleAltTabWithDeskSwitch) {
EXPECT_EQ(win0.get(), window_util::GetActiveWindow()); EXPECT_EQ(win0.get(), window_util::GetActiveWindow());
} }
class ModeSelectionWindowCycleControllerTest
: public WindowCycleControllerTest {
public:
ModeSelectionWindowCycleControllerTest() = default;
ModeSelectionWindowCycleControllerTest(
const ModeSelectionWindowCycleControllerTest&) = delete;
ModeSelectionWindowCycleControllerTest& operator=(
const ModeSelectionWindowCycleControllerTest&) = delete;
~ModeSelectionWindowCycleControllerTest() override = default;
// WindowCycleControllerTest:
void SetUp() override {
scoped_feature_list_.InitAndEnableFeature(features::kBento);
WindowCycleControllerTest::SetUp();
generator_ = GetEventGenerator();
}
void SwitchPerDeskAltTabMode(bool per_desk_mode) {
gfx::Point button_center =
GetWindowCycleTabSliderViews()[per_desk_mode ? 1 : 0]
->GetBoundsInScreen()
.CenterPoint();
generator_->MoveMouseTo(button_center);
generator_->ClickLeftButton();
EXPECT_EQ(per_desk_mode,
Shell::Get()->window_cycle_controller()->IsAltTabPerActiveDesk());
}
private:
base::test::ScopedFeatureList scoped_feature_list_;
ui::test::EventGenerator* generator_;
};
// Tests that alt-tab shows all windows in an all-desk mode by default and
// shows only windows in the current desk in a current-desk mode. Switching
// between two modes should refresh the window list, while re-entering alt-tab
// should display the most recently selected mode.
TEST_F(ModeSelectionWindowCycleControllerTest, CycleShowsWindowsPerMode) {
WindowCycleController* cycle_controller =
Shell::Get()->window_cycle_controller();
// Create two windows for desk1 and three windows for desk2.
auto win0 = CreateAppWindow(gfx::Rect(0, 0, 250, 100));
auto win1 = CreateAppWindow(gfx::Rect(50, 50, 200, 200));
auto* desks_controller = DesksController::Get();
desks_controller->NewDesk(DesksCreationRemovalSource::kButton);
ASSERT_EQ(2u, desks_controller->desks().size());
const Desk* desk_2 = desks_controller->desks()[1].get();
ActivateDesk(desk_2);
EXPECT_EQ(desk_2, desks_controller->active_desk());
auto win2 = CreateAppWindow(gfx::Rect(0, 0, 300, 200));
auto win3 = CreateAppWindow(gfx::Rect(10, 30, 400, 200));
auto win4 = CreateAppWindow(gfx::Rect(10, 30, 400, 200));
// By default should contain windows from all desks.
auto* generator = GetEventGenerator();
// Press and hold an alt key to test that alt + left clicking a button works.
generator->PressKey(ui::VKEY_MENU, ui::EF_NONE);
cycle_controller->StartCycling();
EXPECT_FALSE(cycle_controller->IsAltTabPerActiveDesk());
auto cycle_windows = GetWindows(cycle_controller);
EXPECT_EQ(5u, cycle_windows.size());
EXPECT_EQ(cycle_windows.size(), GetWindowCycleItemViews().size());
EXPECT_TRUE(base::Contains(cycle_windows, win0.get()));
EXPECT_TRUE(base::Contains(cycle_windows, win1.get()));
EXPECT_TRUE(base::Contains(cycle_windows, win2.get()));
EXPECT_TRUE(base::Contains(cycle_windows, win3.get()));
EXPECT_TRUE(base::Contains(cycle_windows, win4.get()));
// Switching alt-tab to the current-desk mode should show windows in the
// active desk.
SwitchPerDeskAltTabMode(true);
cycle_windows = GetWindows(cycle_controller);
EXPECT_EQ(3u, GetWindowCycleItemViews().size());
EXPECT_EQ(cycle_windows.size(), GetWindowCycleItemViews().size());
EXPECT_TRUE(base::Contains(cycle_windows, win2.get()));
EXPECT_TRUE(base::Contains(cycle_windows, win3.get()));
EXPECT_TRUE(base::Contains(cycle_windows, win4.get()));
cycle_controller->CompleteCycling();
// Activate desk1 and start alt-tab.
const Desk* desk_1 = desks_controller->desks()[0].get();
ActivateDesk(desk_1);
cycle_controller->StartCycling();
// Should start alt-tab with the current-desk mode and show only two windows
// from desk1.
EXPECT_TRUE(cycle_controller->IsAltTabPerActiveDesk());
cycle_windows = GetWindows(cycle_controller);
EXPECT_EQ(2u, GetWindowCycleItemViews().size());
EXPECT_EQ(cycle_windows.size(), GetWindowCycleItemViews().size());
EXPECT_TRUE(base::Contains(cycle_windows, win0.get()));
EXPECT_TRUE(base::Contains(cycle_windows, win1.get()));
// Switch to the all-desks mode, check and stop alt-tab.
SwitchPerDeskAltTabMode(false);
cycle_windows = GetWindows(cycle_controller);
EXPECT_EQ(5u, cycle_windows.size());
EXPECT_EQ(cycle_windows.size(), GetWindowCycleItemViews().size());
cycle_controller->CompleteCycling();
generator->ReleaseKey(ui::VKEY_MENU, ui::EF_NONE);
}
// For one window display, tests that alt-tab does not show up if there is only
// one window to be shown, but would continue to show a window in alt-tab if
// switching from the all-desks mode with multiple windows.
TEST_F(ModeSelectionWindowCycleControllerTest, OneWindowInActiveDesk) {
WindowCycleController* cycle_controller =
Shell::Get()->window_cycle_controller();
// Create two windows for desk1 and one window for desk2.
auto win0 = CreateAppWindow(gfx::Rect(0, 0, 250, 100));
auto win1 = CreateAppWindow(gfx::Rect(50, 50, 200, 200));
auto* desks_controller = DesksController::Get();
desks_controller->NewDesk(DesksCreationRemovalSource::kButton);
ASSERT_EQ(2u, desks_controller->desks().size());
const Desk* desk_2 = desks_controller->desks()[1].get();
ActivateDesk(desk_2);
EXPECT_EQ(desk_2, desks_controller->active_desk());
auto win2 = CreateAppWindow(gfx::Rect(0, 0, 300, 200));
// Starting alt-tab should shows all desks.
cycle_controller->StartCycling();
auto cycle_windows = GetWindows(cycle_controller);
EXPECT_EQ(3u, GetWindowCycleItemViews().size());
EXPECT_EQ(cycle_windows.size(), GetWindowCycleItemViews().size());
// Switching to an active desk mode should shows a single window in desk2.
SwitchPerDeskAltTabMode(true);
EXPECT_TRUE(cycle_controller->IsCycling());
cycle_windows = GetWindows(cycle_controller);
EXPECT_EQ(1u, GetWindowCycleItemViews().size());
EXPECT_EQ(cycle_windows.size(), GetWindowCycleItemViews().size());
EXPECT_TRUE(base::Contains(cycle_windows, win2.get()));
cycle_controller->CompleteCycling();
// Closing alt-tab and trying to re-open again in the current-desk mode
// should not work because there's only one window.
cycle_controller->StartCycling();
EXPECT_TRUE(cycle_controller->IsCycling());
EXPECT_FALSE(CycleViewExists());
cycle_controller->CompleteCycling();
}
// Similar to OneWindowInActiveDesk, tests that alt-tab will not work if there
// is no window to be shown. However, if users already open alt-tab with
// multiple windows in the all-desks mode and later switch to the current-
// desk mode, which has no window, it'll display "No recent items".
TEST_F(ModeSelectionWindowCycleControllerTest, NoWindowInActiveDesk) {
WindowCycleController* cycle_controller =
Shell::Get()->window_cycle_controller();
// Create two desks with all two windows in desk1.
auto win0 = CreateAppWindow(gfx::Rect(0, 0, 250, 100));
auto win1 = CreateAppWindow(gfx::Rect(50, 50, 200, 200));
auto* desks_controller = DesksController::Get();
desks_controller->NewDesk(DesksCreationRemovalSource::kButton);
ASSERT_EQ(2u, desks_controller->desks().size());
const Desk* desk_2 = desks_controller->desks()[1].get();
// Activate desk2.
ActivateDesk(desk_2);
EXPECT_EQ(desk_2, desks_controller->active_desk());
// Starting alt-tab should show all windows from all desks.
cycle_controller->StartCycling();
auto cycle_windows = GetWindows(cycle_controller);
EXPECT_EQ(2u, GetWindowCycleItemViews().size());
EXPECT_EQ(cycle_windows.size(), GetWindowCycleItemViews().size());
EXPECT_FALSE(GetWindowCycleNoRecentItemsLabel()->GetVisible());
// Switching to an active-desk mode should not show any mirror window
// and should display "no recent items" label.
SwitchPerDeskAltTabMode(true);
EXPECT_TRUE(cycle_controller->IsCycling());
cycle_windows = GetWindows(cycle_controller);
EXPECT_EQ(0u, GetWindowCycleItemViews().size());
EXPECT_EQ(cycle_windows.size(), GetWindowCycleItemViews().size());
EXPECT_TRUE(GetWindowCycleNoRecentItemsLabel()->GetVisible());
// Switching back to an all-desks mode should hide the label.
SwitchPerDeskAltTabMode(false);
EXPECT_FALSE(GetWindowCycleNoRecentItemsLabel()->GetVisible());
DeskSwitchAnimationWaiter waiter;
cycle_controller->CompleteCycling();
waiter.Wait();
}
} // namespace ash } // namespace ash
...@@ -17,7 +17,11 @@ ...@@ -17,7 +17,11 @@
#include "ash/public/cpp/window_properties.h" #include "ash/public/cpp/window_properties.h"
#include "ash/shell.h" #include "ash/shell.h"
#include "ash/shell_delegate.h" #include "ash/shell_delegate.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/style/ash_color_provider.h" #include "ash/style/ash_color_provider.h"
#include "ash/style/default_colors.h"
#include "ash/wm/window_cycle_tab_slider.h"
#include "ash/wm/window_cycle_tab_slider_button.h"
#include "ash/wm/window_mini_view.h" #include "ash/wm/window_mini_view.h"
#include "ash/wm/window_preview_view.h" #include "ash/wm/window_preview_view.h"
#include "ash/wm/window_state.h" #include "ash/wm/window_state.h"
...@@ -29,12 +33,15 @@ ...@@ -29,12 +33,15 @@
#include "ui/aura/scoped_window_targeter.h" #include "ui/aura/scoped_window_targeter.h"
#include "ui/aura/window.h" #include "ui/aura/window.h"
#include "ui/aura/window_targeter.h" #include "ui/aura/window_targeter.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/compositor/animation_throughput_reporter.h" #include "ui/compositor/animation_throughput_reporter.h"
#include "ui/compositor/layer_animation_sequence.h" #include "ui/compositor/layer_animation_sequence.h"
#include "ui/compositor/scoped_layer_animation_settings.h" #include "ui/compositor/scoped_layer_animation_settings.h"
#include "ui/display/display.h" #include "ui/display/display.h"
#include "ui/gfx/geometry/insets.h" #include "ui/gfx/geometry/insets.h"
#include "ui/views/animation/bounds_animator.h" #include "ui/views/animation/bounds_animator.h"
#include "ui/views/background.h"
#include "ui/views/controls/button/label_button.h"
#include "ui/views/layout/box_layout.h" #include "ui/views/layout/box_layout.h"
#include "ui/views/view.h" #include "ui/views/view.h"
#include "ui/views/widget/widget.h" #include "ui/views/widget/widget.h"
...@@ -70,9 +77,19 @@ constexpr int kMaxPreviewWidthDp = kFixedPreviewHeightDp * 2; ...@@ -70,9 +77,19 @@ constexpr int kMaxPreviewWidthDp = kFixedPreviewHeightDp * 2;
constexpr int kInsideBorderHorizontalPaddingDp = 64; constexpr int kInsideBorderHorizontalPaddingDp = 64;
constexpr int kInsideBorderVerticalPaddingDp = 60; constexpr int kInsideBorderVerticalPaddingDp = 60;
// Padding between the alt-tab bandshield and the tab slider container.
constexpr int kMirrorContainerVerticalPaddingDp = 24;
// Padding between the window previews within the alt-tab bandshield. // Padding between the window previews within the alt-tab bandshield.
constexpr int kBetweenChildPaddingDp = 10; constexpr int kBetweenChildPaddingDp = 10;
// Padding between the tab slider button and the tab slider container.
constexpr int kTabSliderContainerVerticalPaddingDp = 32;
// The font size of "No recent items" string when there's no window in the
// window cycle list.
constexpr int kNoRecentItemsLabelFontSizeDp = 14;
// The UMA histogram that logs smoothness of the fade-in animation. // The UMA histogram that logs smoothness of the fade-in animation.
constexpr char kShowAnimationSmoothness[] = constexpr char kShowAnimationSmoothness[] =
"Ash.WindowCycleView.AnimationSmoothness.Show"; "Ash.WindowCycleView.AnimationSmoothness.Show";
...@@ -262,6 +279,30 @@ class WindowCycleView : public views::WidgetDelegateView, ...@@ -262,6 +279,30 @@ class WindowCycleView : public views::WidgetDelegateView,
layer->SetBackdropFilterQuality(kBackgroundBlurQuality); layer->SetBackdropFilterQuality(kBackgroundBlurQuality);
layer->SetName("WindowCycleView"); layer->SetName("WindowCycleView");
if (features::IsBentoEnabled()) {
tab_slider_container_ =
AddChildView(std::make_unique<WindowCycleTabSlider>());
no_recent_items_label_ = AddChildView(std::make_unique<views::Label>(
l10n_util::GetStringUTF16(IDS_ASH_OVERVIEW_NO_RECENT_ITEMS)));
no_recent_items_label_->SetPaintToLayer();
no_recent_items_label_->layer()->SetFillsBoundsOpaquely(false);
no_recent_items_label_->SetHorizontalAlignment(gfx::ALIGN_CENTER);
no_recent_items_label_->SetVerticalAlignment(gfx::ALIGN_MIDDLE);
no_recent_items_label_->SetEnabledColor(
AshColorProvider::Get()->GetContentLayerColor(
AshColorProvider::ContentLayerType::kIconColorSecondary));
no_recent_items_label_->SetFontList(
no_recent_items_label_->font_list()
.DeriveWithSizeDelta(
kNoRecentItemsLabelFontSizeDp -
no_recent_items_label_->font_list().GetFontSize())
.DeriveWithWeight(gfx::Font::Weight::NORMAL));
no_recent_items_label_->SetVisible(false);
}
// |mirror_container_| may be larger than |this|. In this case, it will be // |mirror_container_| may be larger than |this|. In this case, it will be
// shifted along the x-axis when the user tabs through. It is a container // shifted along the x-axis when the user tabs through. It is a container
// for the previews and has no rendered content. // for the previews and has no rendered content.
...@@ -271,7 +312,11 @@ class WindowCycleView : public views::WidgetDelegateView, ...@@ -271,7 +312,11 @@ class WindowCycleView : public views::WidgetDelegateView,
views::BoxLayout* layout = views::BoxLayout* layout =
mirror_container_->SetLayoutManager(std::make_unique<views::BoxLayout>( mirror_container_->SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kHorizontal, views::BoxLayout::Orientation::kHorizontal,
gfx::Insets(kInsideBorderVerticalPaddingDp, gfx::Insets(features::IsBentoEnabled()
? kMirrorContainerVerticalPaddingDp
: kInsideBorderVerticalPaddingDp,
kInsideBorderHorizontalPaddingDp,
kInsideBorderVerticalPaddingDp,
kInsideBorderHorizontalPaddingDp), kInsideBorderHorizontalPaddingDp),
kBetweenChildPaddingDp)); kBetweenChildPaddingDp));
layout->set_cross_axis_alignment( layout->set_cross_axis_alignment(
...@@ -301,6 +346,15 @@ class WindowCycleView : public views::WidgetDelegateView, ...@@ -301,6 +346,15 @@ class WindowCycleView : public views::WidgetDelegateView,
~WindowCycleView() override = default; ~WindowCycleView() override = default;
void UpdateWindows(const WindowCycleList::WindowList& windows) { void UpdateWindows(const WindowCycleList::WindowList& windows) {
const bool no_windows = windows.empty();
if (features::IsBentoEnabled()) {
no_recent_items_label_->SetVisible(no_windows);
}
if (no_windows)
return;
if (features::IsBentoEnabled())
no_recent_items_label_->SetVisible(false);
for (auto* window : windows) { for (auto* window : windows) {
auto* view = mirror_container_->AddChildView( auto* view = mirror_container_->AddChildView(
std::make_unique<WindowCycleItemView>(window)); std::make_unique<WindowCycleItemView>(window));
...@@ -394,7 +448,12 @@ class WindowCycleView : public views::WidgetDelegateView, ...@@ -394,7 +448,12 @@ class WindowCycleView : public views::WidgetDelegateView,
// views::WidgetDelegateView: // views::WidgetDelegateView:
gfx::Size CalculatePreferredSize() const override { gfx::Size CalculatePreferredSize() const override {
return mirror_container_->GetPreferredSize(); gfx::Size size = mirror_container_->GetPreferredSize();
if (features::IsBentoEnabled()) {
size.Enlarge(0, tab_slider_container_->GetPreferredSize().height() +
kTabSliderContainerVerticalPaddingDp);
}
return size;
} }
void Layout() override { void Layout() override {
...@@ -436,6 +495,32 @@ class WindowCycleView : public views::WidgetDelegateView, ...@@ -436,6 +495,32 @@ class WindowCycleView : public views::WidgetDelegateView,
} }
container_bounds.set_x(x_offset); container_bounds.set_x(x_offset);
// Layout a tab slider if Bento is enabled.
if (features::IsBentoEnabled()) {
// Layout the tab slider.
const gfx::Size tab_slider_size =
tab_slider_container_->GetPreferredSize();
const gfx::Rect tab_slider_container_bounds(
(width() - tab_slider_size.width()) / 2,
kTabSliderContainerVerticalPaddingDp, tab_slider_size.width(),
tab_slider_size.height());
tab_slider_container_->SetBoundsRect(tab_slider_container_bounds);
// Move window cycle container down.
container_bounds.set_y(tab_slider_container_->y() +
tab_slider_container_->height());
// Unlike the bounds of scrollable mirror container, the bounds of label
// should not overflow out of the screen.
if (no_previews_set_.empty()) {
const gfx::Rect no_recent_item_bounds_(
std::max(0, container_bounds.x()), container_bounds.y(),
std::min(width(), container_bounds.width()),
container_bounds.height());
no_recent_items_label_->SetBoundsRect(no_recent_item_bounds_);
}
}
// Enable animations only after the first Layout() pass. // Enable animations only after the first Layout() pass.
std::unique_ptr<ui::ScopedLayerAnimationSettings> settings; std::unique_ptr<ui::ScopedLayerAnimationSettings> settings;
base::Optional<ui::AnimationThroughputReporter> reporter; base::Optional<ui::AnimationThroughputReporter> reporter;
...@@ -478,6 +563,13 @@ class WindowCycleView : public views::WidgetDelegateView, ...@@ -478,6 +563,13 @@ class WindowCycleView : public views::WidgetDelegateView,
return mirror_container_->children(); return mirror_container_->children();
} }
const views::View::Views& GetTabSliderViewsForTesting() const {
return tab_slider_container_->children();
}
const views::Label* GetNoRecentItemsLabelForTesting() const {
return no_recent_items_label_;
}
const aura::Window* GetTargetWindowForTesting() const { const aura::Window* GetTargetWindowForTesting() const {
return target_window_; return target_window_;
} }
...@@ -491,6 +583,10 @@ class WindowCycleView : public views::WidgetDelegateView, ...@@ -491,6 +583,10 @@ class WindowCycleView : public views::WidgetDelegateView,
std::map<aura::Window*, WindowCycleItemView*> window_view_map_; std::map<aura::Window*, WindowCycleItemView*> window_view_map_;
views::View* mirror_container_ = nullptr; views::View* mirror_container_ = nullptr;
// Tab slider and no recent items are only used when Bento is enabled.
views::View* tab_slider_container_ = nullptr;
views::Label* no_recent_items_label_ = nullptr;
// The |target_window_| is the window that has the focus ring. When the user // The |target_window_| is the window that has the focus ring. When the user
// completes cycling the |target_window_| is activated. // completes cycling the |target_window_| is activated.
aura::Window* target_window_ = nullptr; aura::Window* target_window_ = nullptr;
...@@ -572,8 +668,6 @@ WindowCycleList::~WindowCycleList() { ...@@ -572,8 +668,6 @@ WindowCycleList::~WindowCycleList() {
} }
void WindowCycleList::ReplaceWindows(const WindowList& windows) { void WindowCycleList::ReplaceWindows(const WindowList& windows) {
if (windows.empty())
return;
RemoveAllWindows(); RemoveAllWindows();
windows_ = windows; windows_ = windows;
...@@ -581,7 +675,7 @@ void WindowCycleList::ReplaceWindows(const WindowList& windows) { ...@@ -581,7 +675,7 @@ void WindowCycleList::ReplaceWindows(const WindowList& windows) {
for (auto* new_window : windows_) for (auto* new_window : windows_)
new_window->AddObserver(this); new_window->AddObserver(this);
if (ShouldShowUi() && cycle_view_) if (cycle_view_)
cycle_view_->UpdateWindows(windows_); cycle_view_->UpdateWindows(windows_);
} }
...@@ -819,11 +913,21 @@ int WindowCycleList::GetOffsettedWindowIndex(int offset) const { ...@@ -819,11 +913,21 @@ int WindowCycleList::GetOffsettedWindowIndex(int offset) const {
const views::View::Views& WindowCycleList::GetWindowCycleItemViewsForTesting() const views::View::Views& WindowCycleList::GetWindowCycleItemViewsForTesting()
const { const {
return cycle_view_->GetPreviewViewsForTesting(); return cycle_view_->GetPreviewViewsForTesting(); // IN-TEST
}
const views::View::Views&
WindowCycleList::GetWindowCycleTabSliderViewsForTesting() const {
return cycle_view_->GetTabSliderViewsForTesting(); // IN-TEST
}
const views::Label*
WindowCycleList::GetWindowCycleNoRecentItemsLabelForTesting() const {
return cycle_view_->GetNoRecentItemsLabelForTesting(); // IN-TEST
} }
const aura::Window* WindowCycleList::GetTargetWindowForTesting() const { const aura::Window* WindowCycleList::GetTargetWindowForTesting() const {
return cycle_view_->GetTargetWindowForTesting(); return cycle_view_->GetTargetWindowForTesting(); // IN-TEST
} }
} // namespace ash } // namespace ash
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include "ui/aura/window_observer.h" #include "ui/aura/window_observer.h"
#include "ui/display/display_observer.h" #include "ui/display/display_observer.h"
#include "ui/display/screen.h" #include "ui/display/screen.h"
#include "ui/views/controls/label.h"
#include "ui/views/view.h" #include "ui/views/view.h"
namespace aura { namespace aura {
...@@ -115,6 +116,12 @@ class ASH_EXPORT WindowCycleList : public aura::WindowObserver, ...@@ -115,6 +116,12 @@ class ASH_EXPORT WindowCycleList : public aura::WindowObserver,
// Returns the views for the window cycle list. // Returns the views for the window cycle list.
const views::View::Views& GetWindowCycleItemViewsForTesting() const; const views::View::Views& GetWindowCycleItemViewsForTesting() const;
// Returns the views for the window cycle tab slider buttons.
const views::View::Views& GetWindowCycleTabSliderViewsForTesting() const;
// Returns no recent items label.
const views::Label* GetWindowCycleNoRecentItemsLabelForTesting() const;
// Returns the window cycle list's target window. // Returns the window cycle list's target window.
const aura::Window* GetTargetWindowForTesting() const; const aura::Window* GetTargetWindowForTesting() const;
......
// Copyright 2020 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/wm/window_cycle_tab_slider.h"
#include "ash/shell.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/style/ash_color_provider.h"
#include "ash/style/default_colors.h"
#include "ash/wm/mru_window_tracker.h"
#include "ash/wm/window_cycle_controller.h"
#include "base/strings/utf_string_conversions.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/canvas.h"
#include "ui/views/background.h"
#include "ui/views/layout/box_layout.h"
#include "ui/views/metadata/metadata_impl_macros.h"
namespace ash {
WindowCycleTabSlider::WindowCycleTabSlider()
: all_desks_tab_slider_button_(
AddChildView(std::make_unique<WindowCycleTabSliderButton>(
base::BindRepeating(&WindowCycleTabSlider::OnModeChanged,
base::Unretained(this),
false),
l10n_util::GetStringUTF16(IDS_ASH_ALT_TAB_ALL_DESKS_MODE)))),
current_desk_tab_slider_button_(
AddChildView(std::make_unique<WindowCycleTabSliderButton>(
base::BindRepeating(&WindowCycleTabSlider::OnModeChanged,
base::Unretained(this),
true),
l10n_util::GetStringUTF16(IDS_ASH_ALT_TAB_CURRENT_DESK_MODE)))) {
SetPaintToLayer();
layer()->SetFillsBoundsOpaquely(false);
SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kHorizontal, gfx::Insets(0, 0), 0));
// All buttons should have the same width and height.
gfx::Size common_size = all_desks_tab_slider_button_->GetPreferredSize();
common_size.SetToMax(current_desk_tab_slider_button_->GetPreferredSize());
all_desks_tab_slider_button_->SetPreferredSize(common_size);
current_desk_tab_slider_button_->SetPreferredSize(common_size);
const int tab_slider_round_radius = int{common_size.height() / 2};
SetBackground(views::CreateRoundedRectBackground(
AshColorProvider::Get()->GetControlsLayerColor(
AshColorProvider::ControlsLayerType::kControlBackgroundColorInactive),
tab_slider_round_radius));
per_desk_mode_ =
Shell::Get()->window_cycle_controller()->IsAltTabPerActiveDesk();
all_desks_tab_slider_button_->SetToggled(!per_desk_mode_);
current_desk_tab_slider_button_->SetToggled(per_desk_mode_);
}
void WindowCycleTabSlider::OnModeChanged(bool per_desk) {
if (per_desk_mode_ == per_desk)
return;
per_desk_mode_ = per_desk;
all_desks_tab_slider_button_->SetToggled(!per_desk_mode_);
current_desk_tab_slider_button_->SetToggled(per_desk_mode_);
Shell::Get()->window_cycle_controller()->SetAltTabMode(
per_desk_mode_ ? DesksMruType::kActiveDesk : DesksMruType::kAllDesks);
}
BEGIN_METADATA(WindowCycleTabSlider, views::View)
END_METADATA
} // namespace ash
// Copyright 2020 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.
#ifndef ASH_WM_WINDOW_CYCLE_TAB_SLIDER_H_
#define ASH_WM_WINDOW_CYCLE_TAB_SLIDER_H_
#include "ash/ash_export.h"
#include "ash/wm/window_cycle_tab_slider_button.h"
#include "ui/views/metadata/metadata_header_macros.h"
namespace ash {
// A WindowCycleTabSlider containing two buttons to switch between
// all desks and current desks mode.
class ASH_EXPORT WindowCycleTabSlider : public views::View {
public:
METADATA_HEADER(WindowCycleTabSlider);
WindowCycleTabSlider();
WindowCycleTabSlider(const WindowCycleTabSlider&) = delete;
WindowCycleTabSlider& operator=(const WindowCycleTabSlider&) = delete;
~WindowCycleTabSlider() override = default;
void OnModeChanged(bool per_desk);
// TODO(crbug.com/1157087): Add tab slider animation.
private:
bool per_desk_mode_ = false;
WindowCycleTabSliderButton* all_desks_tab_slider_button_;
WindowCycleTabSliderButton* current_desk_tab_slider_button_;
};
} // namespace ash
#endif // ASH_WM_WINDOW_CYCLE_TAB_SLIDER_H_
// Copyright 2020 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/wm/window_cycle_tab_slider_button.h"
#include "ash/style/ash_color_provider.h"
#include "base/strings/utf_string_conversions.h"
#include "ui/gfx/canvas.h"
#include "ui/views/background.h"
#include "ui/views/controls/label.h"
#include "ui/views/layout/box_layout.h"
#include "ui/views/metadata/metadata_impl_macros.h"
namespace ash {
namespace {
// The height of the tab slider button.
constexpr int kTabSliderButtonHeight = 32;
// The round radius of the slider, which is half of its diameter (height).
constexpr int kTabSliderRoundRadius = int{kTabSliderButtonHeight / 2};
// The horizontal insets between the label and the button.
constexpr int kTabSliderButtonHorizontalInsets = 20;
// The font size of the button label.
constexpr int kTabSliderButtonLabelFontSizeDp = 13;
} // namespace
WindowCycleTabSliderButton::WindowCycleTabSliderButton(
views::Button::PressedCallback callback,
const base::string16& label_text)
: LabelButton(std::move(callback), label_text) {
SetHorizontalAlignment(gfx::ALIGN_CENTER);
label()->SetFontList(
label()
->font_list()
.DeriveWithSizeDelta(kTabSliderButtonLabelFontSizeDp -
label()->font_list().GetFontSize())
.DeriveWithWeight(gfx::Font::Weight::MEDIUM));
SetEnabledTextColors(AshColorProvider::Get()->GetContentLayerColor(
AshColorProvider::ContentLayerType::kTextColorPrimary));
SetBorder(views::CreateEmptyBorder(gfx::Insets()));
}
void WindowCycleTabSliderButton::SetToggled(bool is_toggled) {
if (is_toggled == toggled_)
return;
toggled_ = is_toggled;
SetEnabledTextColors(AshColorProvider::Get()->GetContentLayerColor(
toggled_ ? AshColorProvider::ContentLayerType::kButtonLabelColorPrimary
: AshColorProvider::ContentLayerType::kTextColorPrimary));
// SchedulePaint triggers OnPaintBackground
SchedulePaint();
}
void WindowCycleTabSliderButton::OnPaintBackground(gfx::Canvas* canvas) {
if (!toggled_)
return;
cc::PaintFlags flags;
flags.setAntiAlias(true);
flags.setStyle(cc::PaintFlags::kFill_Style);
flags.setColor(AshColorProvider::Get()->GetControlsLayerColor(
AshColorProvider::ControlsLayerType::kControlBackgroundColorActive));
canvas->DrawRoundRect(GetContentsBounds(), kTabSliderRoundRadius, flags);
}
gfx::Size WindowCycleTabSliderButton::CalculatePreferredSize() const {
return gfx::Size(label()->GetPreferredSize().width() +
2 * kTabSliderButtonHorizontalInsets,
kTabSliderButtonHeight);
}
BEGIN_METADATA(WindowCycleTabSliderButton, views::LabelButton)
END_METADATA
} // namespace ash
// Copyright 2020 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.
#ifndef ASH_WM_WINDOW_CYCLE_TAB_SLIDER_BUTTON_H_
#define ASH_WM_WINDOW_CYCLE_TAB_SLIDER_BUTTON_H_
#include "ash/ash_export.h"
#include "ui/views/controls/button/label_button.h"
#include "ui/views/metadata/metadata_header_macros.h"
namespace ash {
// A button used in the WindowCycleTabSlider to choose between
// all desks and current desks mode.
class ASH_EXPORT WindowCycleTabSliderButton : public views::LabelButton {
public:
METADATA_HEADER(WindowCycleTabSliderButton);
WindowCycleTabSliderButton(views::Button::PressedCallback callback,
const base::string16& label);
WindowCycleTabSliderButton(const WindowCycleTabSliderButton&) = delete;
WindowCycleTabSliderButton& operator=(const WindowCycleTabSliderButton&) =
delete;
~WindowCycleTabSliderButton() override = default;
// views::View:
void OnPaintBackground(gfx::Canvas* canvas) override;
gfx::Size CalculatePreferredSize() const override;
void SetToggled(bool is_toggled);
private:
bool toggled_ = false;
};
} // namespace ash
#endif // ASH_WM_WINDOW_CYCLE_TAB_SLIDER_BUTTON_H_
...@@ -965,7 +965,8 @@ bool EventRewriterChromeOS::ShouldRemapToRightClick( ...@@ -965,7 +965,8 @@ bool EventRewriterChromeOS::ShouldRemapToRightClick(
*matched_mask = kSearchLeftButton; *matched_mask = kSearchLeftButton;
} }
} else { } else {
if (AreFlagsSet(flags, kAltLeftButton)) { if (AreFlagsSet(flags, kAltLeftButton) &&
is_alt_left_click_remapping_enabled_) {
*matched_mask = kAltLeftButton; *matched_mask = kAltLeftButton;
} }
} }
......
...@@ -150,6 +150,13 @@ class EventRewriterChromeOS : public EventRewriter { ...@@ -150,6 +150,13 @@ class EventRewriterChromeOS : public EventRewriter {
privacy_screen_supported_ = supported; privacy_screen_supported_ = supported;
} }
// Enable/disable alt + left click mapping to a right click. This only
// applies if the feature `chromeos::features::kUseSearchClickForRightClick`
// is not enabled.
void set_alt_left_click_remapping_enabled(bool enabled) {
is_alt_left_click_remapping_enabled_ = enabled;
}
// EventRewriter overrides: // EventRewriter overrides:
EventDispatchDetails RewriteEvent(const Event& event, EventDispatchDetails RewriteEvent(const Event& event,
const Continuation continuation) override; const Continuation continuation) override;
...@@ -215,7 +222,8 @@ class EventRewriterChromeOS : public EventRewriter { ...@@ -215,7 +222,8 @@ class EventRewriterChromeOS : public EventRewriter {
// Returns true if this event should be remapped to a right-click. // Returns true if this event should be remapped to a right-click.
// |matched_mask| will be set to the variant (Alt+Click or Search+Click) // |matched_mask| will be set to the variant (Alt+Click or Search+Click)
// that was used to match based on flag/feature settings. |matched_mask| // that was used to match based on flag/feature settings. |matched_mask|
// only has a valid value when returning true. // only has a valid value when returning true. However, Alt+Click will not
// be remapped if |is_alt_left_click_remapping_enabled_| is false.
bool ShouldRemapToRightClick(const MouseEvent& mouse_event, bool ShouldRemapToRightClick(const MouseEvent& mouse_event,
int flags, int flags,
int* matched_mask) const; int* matched_mask) const;
...@@ -325,6 +333,11 @@ class EventRewriterChromeOS : public EventRewriter { ...@@ -325,6 +333,11 @@ class EventRewriterChromeOS : public EventRewriter {
int latched_modifier_latches_; int latched_modifier_latches_;
int used_modifier_latches_; int used_modifier_latches_;
// Alt + left click maps to a right click by default. In some scenario, such
// as clicking a button in the Alt-Tab UI, this remapping undesirably prevents
// button clicking.
bool is_alt_left_click_remapping_enabled_ = true;
DISALLOW_COPY_AND_ASSIGN(EventRewriterChromeOS); DISALLOW_COPY_AND_ASSIGN(EventRewriterChromeOS);
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment