Commit aa6378ee authored by Angus L. M. McLean IV's avatar Angus L. M. McLean IV Committed by Commit Bot

cros: Focus launcher button when tab is presssed

When no windows are shown, and a user presses tab, focus the
launcher/Home button on the display which has the mouse.

This was based on @newcomer's original CL,
https://crrev.com/c/2128227.

The comments there as well as in the bug have been handled here.

Bug: 1060337
Change-Id: I801b8ecc4e7fefb697cf2ace56736f6e85b3cd43
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2412518
Commit-Queue: Angus McLean <angusmclean@chromium.org>
Reviewed-by: default avatarAhmed Fakhry <afakhry@chromium.org>
Reviewed-by: default avatarXiyuan Xia <xiyuan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#812241}
parent 95f4bf67
...@@ -817,6 +817,8 @@ component("ash") { ...@@ -817,6 +817,8 @@ component("ash") {
"shell.cc", "shell.cc",
"shell_delegate.cc", "shell_delegate.cc",
"shell_init_params.cc", "shell_init_params.cc",
"shell_tab_handler.cc",
"shell_tab_handler.h",
"shutdown_controller_impl.cc", "shutdown_controller_impl.cc",
"shutdown_controller_impl.h", "shutdown_controller_impl.h",
"shutdown_reason.cc", "shutdown_reason.cc",
......
...@@ -97,6 +97,7 @@ ...@@ -97,6 +97,7 @@
#include "ash/shell_delegate.h" #include "ash/shell_delegate.h"
#include "ash/shell_init_params.h" #include "ash/shell_init_params.h"
#include "ash/shell_observer.h" #include "ash/shell_observer.h"
#include "ash/shell_tab_handler.h"
#include "ash/shutdown_controller_impl.h" #include "ash/shutdown_controller_impl.h"
#include "ash/sticky_keys/sticky_keys_controller.h" #include "ash/sticky_keys/sticky_keys_controller.h"
#include "ash/style/ash_color_provider.h" #include "ash/style/ash_color_provider.h"
...@@ -627,6 +628,9 @@ Shell::~Shell() { ...@@ -627,6 +628,9 @@ Shell::~Shell() {
// handler that depends on this shell and some of its members. Destroy early. // handler that depends on this shell and some of its members. Destroy early.
capture_mode_controller_.reset(); capture_mode_controller_.reset();
RemovePreTargetHandler(shell_tab_handler_.get());
shell_tab_handler_.reset();
RemovePreTargetHandler(magnifier_key_scroll_handler_.get()); RemovePreTargetHandler(magnifier_key_scroll_handler_.get());
magnifier_key_scroll_handler_.reset(); magnifier_key_scroll_handler_.reset();
...@@ -1028,6 +1032,8 @@ void Shell::Init( ...@@ -1028,6 +1032,8 @@ void Shell::Init(
shelf_config_ = std::make_unique<ShelfConfig>(); shelf_config_ = std::make_unique<ShelfConfig>();
shelf_controller_ = std::make_unique<ShelfController>(); shelf_controller_ = std::make_unique<ShelfController>();
shell_tab_handler_ = std::make_unique<ShellTabHandler>(this);
AddPreTargetHandler(shell_tab_handler_.get());
magnifier_key_scroll_handler_ = MagnifierKeyScroller::CreateHandler(); magnifier_key_scroll_handler_ = MagnifierKeyScroller::CreateHandler();
AddPreTargetHandler(magnifier_key_scroll_handler_.get()); AddPreTargetHandler(magnifier_key_scroll_handler_.get());
speech_feedback_handler_ = SpokenFeedbackToggler::CreateHandler(); speech_feedback_handler_ = SpokenFeedbackToggler::CreateHandler();
......
...@@ -540,6 +540,7 @@ class ASH_EXPORT Shell : public SessionObserver, ...@@ -540,6 +540,7 @@ class ASH_EXPORT Shell : public SessionObserver,
BackGestureEventHandler* back_gesture_event_handler() { BackGestureEventHandler* back_gesture_event_handler() {
return back_gesture_event_handler_.get(); return back_gesture_event_handler_.get();
} }
ui::EventHandler* shell_tab_handler() { return shell_tab_handler_.get(); }
ToplevelWindowEventHandler* toplevel_window_event_handler() { ToplevelWindowEventHandler* toplevel_window_event_handler() {
return toplevel_window_event_handler_.get(); return toplevel_window_event_handler_.get();
} }
...@@ -764,6 +765,10 @@ class ASH_EXPORT Shell : public SessionObserver, ...@@ -764,6 +765,10 @@ class ASH_EXPORT Shell : public SessionObserver,
// An event filter which handles swiping back from left side of the window. // An event filter which handles swiping back from left side of the window.
std::unique_ptr<BackGestureEventHandler> back_gesture_event_handler_; std::unique_ptr<BackGestureEventHandler> back_gesture_event_handler_;
// An event filter which redirects focus when tab is pressed on a RootWindow
// with no active windows.
std::unique_ptr<ui::EventHandler> shell_tab_handler_;
// An event filter which handles moving and resizing windows. // An event filter which handles moving and resizing windows.
std::unique_ptr<ToplevelWindowEventHandler> toplevel_window_event_handler_; std::unique_ptr<ToplevelWindowEventHandler> toplevel_window_event_handler_;
......
// 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 "shell_tab_handler.h"
#include "ash/focus_cycler.h"
#include "ash/shelf/shelf.h"
#include "ash/shelf/shelf_navigation_widget.h"
#include "ash/shell.h"
#include "ash/system/status_area_widget.h"
#include "ash/wm/tablet_mode/tablet_mode_controller.h"
#include "ash/wm/window_util.h"
#include "ui/events/event.h"
#include "ui/wm/public/activation_client.h"
namespace ash {
void ShellTabHandler::OnKeyEvent(ui::KeyEvent* key_event) {
// Only focus the shelf if the device is in clamshell mode, and the user
// pressed tab.
if (key_event->key_code() != ui::KeyboardCode::VKEY_TAB ||
key_event->type() != ui::EventType::ET_KEY_PRESSED ||
key_event->IsAltDown() || key_event->IsControlDown() ||
key_event->IsCommandDown() ||
shell_->tablet_mode_controller()->InTabletMode()) {
return;
}
aura::Window* root_window_for_new_windows =
Shell::GetRootWindowForNewWindows();
if (!root_window_for_new_windows || window_util::GetActiveWindow())
return;
// If there is no active window, focus the HomeButton or StatusWidget,
// depending on whether this is Tab or Shift + Tab. This will allow the
// users focus to traverse the shelf.
auto* shelf = Shelf::ForWindow(root_window_for_new_windows);
views::Widget* status_area_widget = shelf->status_area_widget();
views::Widget* navigation_widget = shelf->navigation_widget();
shell_->focus_cycler()->FocusWidget(
key_event->IsShiftDown() ? status_area_widget : navigation_widget);
key_event->SetHandled();
key_event->StopPropagation();
}
} // 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_SHELL_TAB_HANDLER_H_
#define ASH_SHELL_TAB_HANDLER_H_
#include "ui/events/event_handler.h"
namespace ash {
class Shell;
// Enables handling of tab when there are no non-minimized windows open in the
// shell. This allows keyboard only users to easily get focus to the shelf when
// no windows are open.
class ShellTabHandler : public ui::EventHandler {
public:
explicit ShellTabHandler(Shell* shell) : shell_(shell) {}
ShellTabHandler(const ShellTabHandler&) = delete;
ShellTabHandler& operator=(const ShellTabHandler) = delete;
~ShellTabHandler() override = default;
// ui::EventHandler:
void OnKeyEvent(ui::KeyEvent* key_event) override;
private:
Shell* const shell_;
};
} // namespace ash
#endif // ASH_SHELL_TAB_HANDLER_H_
...@@ -21,15 +21,19 @@ ...@@ -21,15 +21,19 @@
#include "ash/root_window_controller.h" #include "ash/root_window_controller.h"
#include "ash/session/session_controller_impl.h" #include "ash/session/session_controller_impl.h"
#include "ash/session/test_session_controller_client.h" #include "ash/session/test_session_controller_client.h"
#include "ash/shelf/home_button.h"
#include "ash/shelf/shelf.h" #include "ash/shelf/shelf.h"
#include "ash/shelf/shelf_layout_manager.h" #include "ash/shelf/shelf_layout_manager.h"
#include "ash/shelf/shelf_navigation_widget.h"
#include "ash/shelf/shelf_widget.h" #include "ash/shelf/shelf_widget.h"
#include "ash/system/status_area_widget.h"
#include "ash/test/ash_test_base.h" #include "ash/test/ash_test_base.h"
#include "ash/test/ash_test_helper.h" #include "ash/test/ash_test_helper.h"
#include "ash/test_shell_delegate.h" #include "ash/test_shell_delegate.h"
#include "ash/wallpaper/wallpaper_widget_controller.h" #include "ash/wallpaper/wallpaper_widget_controller.h"
#include "ash/window_factory.h" #include "ash/window_factory.h"
#include "ash/wm/desks/desks_util.h" #include "ash/wm/desks/desks_util.h"
#include "ash/wm/overview/overview_controller.h"
#include "ash/wm/window_util.h" #include "ash/wm/window_util.h"
#include "base/command_line.h" #include "base/command_line.h"
#include "base/containers/flat_set.h" #include "base/containers/flat_set.h"
...@@ -539,6 +543,57 @@ TEST_F(ShellTest, EnvPreTargetHandler) { ...@@ -539,6 +543,57 @@ TEST_F(ShellTest, EnvPreTargetHandler) {
aura::Env::GetInstance()->RemovePreTargetHandler(&event_handler); aura::Env::GetInstance()->RemovePreTargetHandler(&event_handler);
} }
// Verifies that pressing tab on an empty shell (one with no windows visible)
// will put focus on the shelf. This enables keyboard only users to get to the
// shelf without knowing the more obscure accelerators. Tab should move focus to
// the home button, shift + tab to the status widget. From there, normal shelf
// tab behaviour takes over, and the shell no longer catches that event.
TEST_F(ShellTest, NoWindowTabFocus) {
ExpectAllContainers();
auto* generator = GetEventGenerator();
StatusAreaWidget* status_area_widget =
GetPrimaryShelf()->status_area_widget();
ShelfNavigationWidget* home_button = GetPrimaryShelf()->navigation_widget();
// Create a normal window. It is not maximized.
auto widget = CreateTestWidget();
// Hit tab with window open, and expect that focus is not on the navigation
// widget or status widget.
generator->PressKey(ui::VKEY_TAB, ui::EF_NONE);
generator->ReleaseKey(ui::VKEY_TAB, ui::EF_NONE);
EXPECT_FALSE(home_button->GetNativeView()->HasFocus());
EXPECT_FALSE(status_area_widget->GetNativeView()->HasFocus());
// Minimize the window, hit tab and expect that focus is on the launcher.
widget->Minimize();
generator->PressKey(ui::VKEY_TAB, ui::EF_NONE);
generator->ReleaseKey(ui::VKEY_TAB, ui::EF_NONE);
EXPECT_TRUE(home_button->GetNativeView()->HasFocus());
// Show (to steal focus back before continuing testing) and close the window.
widget->Show();
widget->Close();
EXPECT_FALSE(home_button->GetNativeView()->HasFocus());
// Confirm that pressing tab when overview mode is open does not go to home
// button. Tab should be handled by overview mode and not hit the shell event
// handler.
auto* overview_controller = Shell::Get()->overview_controller();
overview_controller->StartOverview();
generator->PressKey(ui::VKEY_TAB, ui::EF_NONE);
generator->ReleaseKey(ui::VKEY_TAB, ui::EF_NONE);
EXPECT_FALSE(home_button->GetNativeView()->HasFocus());
overview_controller->EndOverview();
// Hit shift tab and expect that focus is on status widget.
generator->PressKey(ui::VKEY_TAB, ui::EF_SHIFT_DOWN);
generator->ReleaseKey(ui::VKEY_TAB, ui::EF_SHIFT_DOWN);
EXPECT_TRUE(status_area_widget->GetNativeView()->HasFocus());
}
// This verifies WindowObservers are removed when a window is destroyed after // This verifies WindowObservers are removed when a window is destroyed after
// the Shell is destroyed. This scenario (aura::Windows being deleted after the // the Shell is destroyed. This scenario (aura::Windows being deleted after the
// Shell) occurs if someone is holding a reference to an unparented Window, as // Shell) occurs if someone is holding a reference to an unparented Window, as
......
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