Commit 1f6c21b7 authored by Katie D's avatar Katie D Committed by Commit Bot

Reland "Autoclick menu uses collision detection utils."

This reverts commit e3d0f13d.

Original change description:

Adds plumbing so that window event changes will notify the
autoclick controller to update the menu's bounds if needed. This
allows the menu, like the PIP menu, to re-position itself so as
not to overlap system menus and dialogs.

Bug: 943703
Change-Id: I80f79fb093948a2585a64f849d50759640476d2a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1593454Reviewed-by: default avatarMitsuru Oshima <oshima@chromium.org>
Commit-Queue: Katie Dektar <katie@chromium.org>
Cr-Commit-Position: refs/heads/master@{#656417}
parent 4ed3ae40
......@@ -1026,6 +1026,10 @@ AccessibilityController::GetAutoclickMenuPosition() {
prefs::kAccessibilityAutoclickMenuPosition));
}
void AccessibilityController::UpdateAutoclickMenuBoundsIfNeeded() {
Shell::Get()->autoclick_controller()->UpdateAutoclickMenuBoundsIfNeeded();
}
void AccessibilityController::UpdateCaretHighlightFromPref() {
DCHECK(active_user_prefs_);
const bool enabled = active_user_prefs_->GetBoolean(
......
......@@ -76,6 +76,11 @@ class ASH_EXPORT AccessibilityController
void SetAutoclickMenuPosition(mojom::AutoclickMenuPosition position);
mojom::AutoclickMenuPosition GetAutoclickMenuPosition();
// Update the autoclick menu bounds if necessary. This may need to happen when
// the display work area changes, or if system ui regions change (like the
// virtual keyboard position).
void UpdateAutoclickMenuBoundsIfNeeded();
void SetCaretHighlightEnabled(bool enabled);
bool caret_highlight_enabled() const { return caret_highlight_enabled_; }
......
......@@ -147,6 +147,10 @@ void AutoclickController::SetMovementThreshold(int movement_threshold) {
void AutoclickController::SetMenuPosition(
mojom::AutoclickMenuPosition menu_position) {
menu_position_ = menu_position;
UpdateAutoclickMenuBoundsIfNeeded();
}
void AutoclickController::UpdateAutoclickMenuBoundsIfNeeded() {
if (menu_bubble_controller_)
menu_bubble_controller_->SetPosition(menu_position_);
}
......
......@@ -61,6 +61,9 @@ class ASH_EXPORT AutoclickController : public ui::EventHandler,
// Sets the menu position and updates the UI.
void SetMenuPosition(mojom::AutoclickMenuPosition menu_position);
// Update the bubble menu bounds if necessary to avoid system UI.
void UpdateAutoclickMenuBoundsIfNeeded();
// Sets whether to revert to a left click after any other event type.
void set_revert_to_left_click(bool revert_to_left_click) {
revert_to_left_click_ = revert_to_left_click;
......
......@@ -9,6 +9,8 @@
#include "ash/system/accessibility/autoclick_menu_bubble_controller.h"
#include "ash/system/accessibility/autoclick_menu_view.h"
#include "ash/test/ash_test_base.h"
#include "ash/wm/collision_detection/collision_detection_utils.h"
#include "ash/wm/desks/desks_util.h"
#include "base/run_loop.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/scoped_task_environment.h"
......@@ -757,9 +759,11 @@ TEST_F(AutoclickTest, DoesActionOnBubbleWhenInDifferentModes) {
EXPECT_EQ(0u, events.size());
// If we move over the bubble but not over any button than no real click
// occurs.
// occurs. There is no button at the top of the bubble.
button_location = gfx::ScaleToRoundedPoint(
GetAutoclickMenuView()->GetBoundsInScreen().CenterPoint(), test.scale);
GetAutoclickMenuView()->GetBoundsInScreen().top_center() +
gfx::Vector2d(0, 1),
test.scale);
GetEventGenerator()->MoveMouseTo(button_location);
events = WaitForMouseEvents();
EXPECT_EQ(0u, events.size());
......@@ -841,12 +845,9 @@ TEST_F(AutoclickTest, ShelfAutohidesWithAutoclickBubble) {
Shelf* shelf = GetPrimaryShelf();
// Create a visible window so auto-hide behavior is enforced.
views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW);
params.bounds = gfx::Rect(0, 0, 200, 200);
params.context = CurrentContext();
views::Widget* widget = new views::Widget;
widget->Init(params);
widget->Show();
std::unique_ptr<views::Widget> widget =
CreateTestWidget(nullptr, desks_util::GetActiveDeskContainerId(),
gfx::Rect(0, 0, 200, 200), true /* show */);
// Turn on auto-hide for the shelf.
shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
......@@ -864,4 +865,55 @@ TEST_F(AutoclickTest, ShelfAutohidesWithAutoclickBubble) {
Shell::Get()->accessibility_controller()->SetAutoclickEnabled(false);
}
TEST_F(AutoclickTest, BubbleMovesWithShelfPositionChange) {
UpdateDisplay("800x600");
int screen_width = 800;
int screen_height = 600;
// Create a visible window so WMEvents occur.
std::unique_ptr<views::Widget> widget =
CreateTestWidget(nullptr, desks_util::GetActiveDeskContainerId(),
gfx::Rect(0, 0, 200, 200), true /* show */);
// Set up autoclick and the shelf.
Shell::Get()->accessibility_controller()->SetAutoclickEnabled(true);
Shell::Get()->accessibility_controller()->SetAutoclickMenuPosition(
mojom::AutoclickMenuPosition::kBottomRight);
Shelf* shelf = GetPrimaryShelf();
shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER);
EXPECT_EQ(shelf->GetVisibilityState(), SHELF_VISIBLE);
AutoclickMenuView* menu = GetAutoclickMenuView();
ASSERT_TRUE(menu);
shelf->SetAlignment(SHELF_ALIGNMENT_BOTTOM);
// The menu should be positioned above the shelf, not overlapping.
EXPECT_EQ(menu->GetBoundsInScreen().bottom_right().y(),
screen_height - shelf->GetIdealBounds().height() -
kCollisionWindowWorkAreaInsetsDp);
// And all the way to the right.
EXPECT_EQ(menu->GetBoundsInScreen().bottom_right().x(),
screen_width - kCollisionWindowWorkAreaInsetsDp);
shelf->SetAlignment(SHELF_ALIGNMENT_LEFT);
// The menu should move to the bottom of the screen.
EXPECT_EQ(menu->GetBoundsInScreen().bottom_right().y(),
screen_height - kCollisionWindowWorkAreaInsetsDp);
// Still be at the far right.
EXPECT_EQ(menu->GetBoundsInScreen().bottom_right().x(),
screen_width - kCollisionWindowWorkAreaInsetsDp);
shelf->SetAlignment(SHELF_ALIGNMENT_RIGHT);
// The menu should stay at the bottom of the screen.
EXPECT_EQ(menu->GetBoundsInScreen().bottom_right().y(),
screen_height - kCollisionWindowWorkAreaInsetsDp);
// And should be offset from the far right by the shelf width.
EXPECT_EQ(menu->GetBoundsInScreen().bottom_right().x(),
screen_width - kCollisionWindowWorkAreaInsetsDp -
shelf->GetIdealBounds().width());
// Reset state.
Shell::Get()->accessibility_controller()->SetAutoclickEnabled(false);
shelf->SetAlignment(SHELF_ALIGNMENT_BOTTOM);
}
} // namespace ash
......@@ -6,12 +6,15 @@
#include "ash/public/cpp/app_list/app_list_features.h"
#include "ash/public/cpp/shell_window_ids.h"
#include "ash/shelf/shelf.h"
#include "ash/shell.h"
#include "ash/system/tray/tray_background_view.h"
#include "ash/system/tray/tray_constants.h"
#include "ash/system/unified/unified_system_tray_view.h"
#include "ash/wm/collision_detection/collision_detection_utils.h"
#include "ash/wm/work_area_insets.h"
#include "ash/wm/workspace/workspace_layout_manager.h"
#include "ash/wm/workspace_controller.h"
#include "ui/aura/window_tree_host.h"
#include "ui/events/event_utils.h"
......@@ -38,35 +41,50 @@ void AutoclickMenuBubbleController::SetEventType(
void AutoclickMenuBubbleController::SetPosition(
mojom::AutoclickMenuPosition position) {
if (!menu_view_ || !bubble_view_)
if (!menu_view_ || !bubble_view_ || !bubble_widget_)
return;
menu_view_->UpdatePosition(position);
// TODO(katie): On first load it can display over top of the shelf on Chrome
// OS emulated on Linux (not reproduced on Eve). There must be a race
// condition with the user work area bounds loading.
// No need to update the menu view's UX if it's already in the right
// AutoclickMenuPosition.
if (position_ != position) {
menu_view_->UpdatePosition(position);
position_ = position;
}
// TODO(katie): Support multiple displays.
gfx::Rect work_area = WorkAreaInsets::ForWindow(Shell::GetPrimaryRootWindow())
->user_work_area_bounds();
gfx::Rect new_position;
// Calculates the ideal bounds.
// TODO(katie): Support multiple displays: draw the menu on whichever display
// the cursor is on.
aura::Window* window = Shell::GetPrimaryRootWindow();
gfx::Rect work_area =
WorkAreaInsets::ForWindow(window)->user_work_area_bounds();
gfx::Rect new_bounds;
switch (position) {
case mojom::AutoclickMenuPosition::kBottomRight:
new_position = gfx::Rect(work_area.width(), work_area.height(), 0, 0);
new_bounds = gfx::Rect(work_area.right(), work_area.bottom(), 0, 0);
break;
case mojom::AutoclickMenuPosition::kBottomLeft:
new_position = gfx::Rect(work_area.x(), work_area.height(), 0, 0);
new_bounds = gfx::Rect(work_area.x(), work_area.bottom(), 0, 0);
break;
case mojom::AutoclickMenuPosition::kTopLeft:
// Setting the top to 1 instead of 0 so that the view is drawn on screen.
new_position = gfx::Rect(work_area.x(), 1, 0, 0);
new_bounds = gfx::Rect(work_area.x(), 1, 0, 0);
break;
case mojom::AutoclickMenuPosition::kTopRight:
// Setting the top to 1 instead of 0 so that the view is drawn on screen.
new_position = gfx::Rect(work_area.width(), 1, 0, 0);
new_bounds = gfx::Rect(work_area.right(), 1, 0, 0);
break;
}
bubble_view_->MoveToPosition(new_position);
// Update the preferred bounds based on other system windows.
gfx::Rect resting_bounds = CollisionDetectionUtils::GetRestingPosition(
display::Screen::GetScreen()->GetDisplayNearestWindow(
bubble_widget_->GetNativeWindow()),
new_bounds,
CollisionDetectionUtils::RelativePriority::kAutomaticClicksMenu);
if (bubble_view_->anchor_rect() == resting_bounds)
return;
bubble_view_->MoveToPosition(resting_bounds);
}
void AutoclickMenuBubbleController::ShowBubble(
......@@ -84,9 +102,7 @@ void AutoclickMenuBubbleController::ShowBubble(
init_params.parent_window = Shell::GetContainer(
Shell::GetPrimaryRootWindow(), kShellWindowId_AutoclickContainer);
init_params.anchor_mode = TrayBubbleView::AnchorMode::kRect;
init_params.insets =
gfx::Insets(kUnifiedMenuPadding, kUnifiedMenuPadding,
kUnifiedMenuPadding - 1, kUnifiedMenuPadding - 1);
init_params.insets = gfx::Insets();
init_params.min_width = kAutoclickMenuWidth;
init_params.max_width = kAutoclickMenuWidth;
init_params.corner_radius = kUnifiedTrayCornerRadius;
......
......@@ -5,6 +5,7 @@
#ifndef ASH_SYSTEM_ACCESSIBILITY_AUTOCLICK_MENU_BUBBLE_CONTROLLER_H_
#define ASH_SYSTEM_ACCESSIBILITY_AUTOCLICK_MENU_BUBBLE_CONTROLLER_H_
#include "ash/public/cpp/ash_constants.h"
#include "ash/system/accessibility/autoclick_menu_view.h"
#include "ash/system/tray/tray_bubble_view.h"
......@@ -44,6 +45,7 @@ class AutoclickMenuBubbleController : public TrayBubbleView::Delegate {
// Owned by views hierarchy.
AutoclickMenuBubbleView* bubble_view_ = nullptr;
AutoclickMenuView* menu_view_ = nullptr;
mojom::AutoclickMenuPosition position_ = kDefaultAutoclickMenuPosition;
views::Widget* bubble_widget_ = nullptr;
......
......@@ -4,6 +4,7 @@
#include "ash/wm/base_state.h"
#include "ash/accessibility/accessibility_controller.h"
#include "ash/public/cpp/window_animation_types.h"
#include "ash/public/cpp/window_properties.h"
#include "ash/public/cpp/window_state_type.h"
......@@ -26,6 +27,11 @@ BaseState::~BaseState() = default;
void BaseState::OnWMEvent(WindowState* window_state, const WMEvent* event) {
if (event->IsWorkspaceEvent()) {
HandleWorkspaceEvents(window_state, event);
if (Shell::Get()->accessibility_controller()->autoclick_enabled()) {
Shell::Get()
->accessibility_controller()
->UpdateAutoclickMenuBoundsIfNeeded();
}
if (window_state->IsPip())
window_state->UpdatePipBounds();
return;
......
......@@ -18,7 +18,6 @@ DEFINE_UI_CLASS_PROPERTY_TYPE(ash::CollisionDetectionUtils::RelativePriority)
namespace ash {
namespace {
const int kCollisionWindowWorkAreaInsetsDp = 8;
// A property key to store whether the a window should be ignored for window
// collision detection. For example, StatusBubble windows.
......
......@@ -12,6 +12,10 @@
namespace ash {
// The inset into the work area for a window's resting position. Visible for
// testing.
const static int kCollisionWindowWorkAreaInsetsDp = 8;
// Provides utility functions to compute resting positions for windows which
// wish to avoid other system windows, for example, the PIP and the Automatic
// Clicks bubble menu.
......@@ -109,4 +113,4 @@ class ASH_EXPORT CollisionDetectionUtils {
} // namespace ash
#endif // ASH_WM_COLLISION_DETECTION_COLLISION_DETECTION_UTILS_H_
\ No newline at end of file
#endif // ASH_WM_COLLISION_DETECTION_COLLISION_DETECTION_UTILS_H_
......@@ -39,18 +39,16 @@
namespace ash {
WorkspaceLayoutManager::SettingsBubbleWindowObserver::
SettingsBubbleWindowObserver(
WorkspaceLayoutManager* workspace_layout_manager)
WorkspaceLayoutManager::BubbleWindowObserver::BubbleWindowObserver(
WorkspaceLayoutManager* workspace_layout_manager)
: workspace_layout_manager_(workspace_layout_manager) {}
WorkspaceLayoutManager::SettingsBubbleWindowObserver::
~SettingsBubbleWindowObserver() {
WorkspaceLayoutManager::BubbleWindowObserver::~BubbleWindowObserver() {
for (auto* window : windows_)
window->RemoveObserver(this);
}
void WorkspaceLayoutManager::SettingsBubbleWindowObserver::ObserveWindow(
void WorkspaceLayoutManager::BubbleWindowObserver::ObserveWindow(
aura::Window* window) {
if (!windows_.count(window)) {
windows_.insert(window);
......@@ -58,8 +56,8 @@ void WorkspaceLayoutManager::SettingsBubbleWindowObserver::ObserveWindow(
}
}
void WorkspaceLayoutManager::SettingsBubbleWindowObserver::
OnWindowHierarchyChanged(const HierarchyChangeParams& params) {
void WorkspaceLayoutManager::BubbleWindowObserver::OnWindowHierarchyChanged(
const HierarchyChangeParams& params) {
if (params.new_parent &&
params.new_parent !=
workspace_layout_manager_->settings_bubble_container_) {
......@@ -67,26 +65,27 @@ void WorkspaceLayoutManager::SettingsBubbleWindowObserver::
}
}
void WorkspaceLayoutManager::SettingsBubbleWindowObserver::
OnWindowVisibilityChanged(aura::Window* window, bool visible) {
void WorkspaceLayoutManager::BubbleWindowObserver::OnWindowVisibilityChanged(
aura::Window* window,
bool visible) {
workspace_layout_manager_->NotifySystemUiAreaChanged();
}
void WorkspaceLayoutManager::SettingsBubbleWindowObserver::OnWindowDestroying(
void WorkspaceLayoutManager::BubbleWindowObserver::OnWindowDestroying(
aura::Window* window) {
StopOberservingWindow(window);
}
void WorkspaceLayoutManager::SettingsBubbleWindowObserver::
OnWindowBoundsChanged(aura::Window* window,
const gfx::Rect& old_bounds,
const gfx::Rect& new_bounds,
ui::PropertyChangeReason reason) {
void WorkspaceLayoutManager::BubbleWindowObserver::OnWindowBoundsChanged(
aura::Window* window,
const gfx::Rect& old_bounds,
const gfx::Rect& new_bounds,
ui::PropertyChangeReason reason) {
workspace_layout_manager_->NotifySystemUiAreaChanged();
}
void WorkspaceLayoutManager::SettingsBubbleWindowObserver::
StopOberservingWindow(aura::Window* window) {
void WorkspaceLayoutManager::BubbleWindowObserver::StopOberservingWindow(
aura::Window* window) {
windows_.erase(window);
window->RemoveObserver(this);
}
......@@ -96,6 +95,7 @@ WorkspaceLayoutManager::WorkspaceLayoutManager(aura::Window* window)
root_window_(window->GetRootWindow()),
root_window_controller_(RootWindowController::ForWindow(root_window_)),
settings_bubble_window_observer_(this),
autoclick_bubble_window_observer_(this),
work_area_in_parent_(
screen_util::GetDisplayWorkAreaBoundsInParent(window_)),
is_fullscreen_(wm::GetWindowForFullscreenModeForContext(window) !=
......@@ -109,6 +109,8 @@ WorkspaceLayoutManager::WorkspaceLayoutManager(aura::Window* window)
keyboard::KeyboardController::Get()->AddObserver(this);
settings_bubble_container_ = window->GetRootWindow()->GetChildById(
kShellWindowId_SettingBubbleContainer);
autoclick_bubble_container_ =
window->GetRootWindow()->GetChildById(kShellWindowId_AutoclickContainer);
root_window_controller_->shelf()->AddObserver(this);
}
......@@ -118,6 +120,8 @@ WorkspaceLayoutManager::~WorkspaceLayoutManager() {
root_window_->RemoveObserver(this);
if (settings_bubble_container_)
settings_bubble_container_->RemoveObserver(this);
if (autoclick_bubble_container_)
autoclick_bubble_container_->RemoveObserver(this);
for (aura::Window* window : windows_) {
wm::WindowState* window_state = wm::GetWindowState(window);
window_state->RemoveObserver(this);
......@@ -261,8 +265,12 @@ void WorkspaceLayoutManager::OnKeyboardWorkspaceDisplacingBoundsChanged(
void WorkspaceLayoutManager::OnWindowHierarchyChanged(
const HierarchyChangeParams& params) {
if (params.new_parent && params.new_parent == settings_bubble_container_)
settings_bubble_window_observer_.ObserveWindow(params.target);
if (params.new_parent) {
if (params.new_parent == settings_bubble_container_)
settings_bubble_window_observer_.ObserveWindow(params.target);
if (params.new_parent == autoclick_bubble_container_)
autoclick_bubble_window_observer_.ObserveWindow(params.target);
}
// The window should have a parent (unless it's being removed), so we can
// create WindowState, which requires its parent. (crbug.com/924305)
// TODO(oshima): Change this to |EnsureWindowState|, then change
......@@ -292,6 +300,8 @@ void WorkspaceLayoutManager::OnWindowHierarchyChanged(
void WorkspaceLayoutManager::OnWindowAdded(aura::Window* window) {
if (window->parent() == settings_bubble_container_)
settings_bubble_window_observer_.ObserveWindow(window);
if (window->parent() == autoclick_bubble_container_)
autoclick_bubble_window_observer_.ObserveWindow(window);
}
void WorkspaceLayoutManager::OnWindowPropertyChanged(aura::Window* window,
......@@ -323,6 +333,8 @@ void WorkspaceLayoutManager::OnWindowDestroying(aura::Window* window) {
}
if (settings_bubble_container_ == window)
settings_bubble_container_ = nullptr;
if (autoclick_bubble_container_ == window)
autoclick_bubble_container_ = nullptr;
}
void WorkspaceLayoutManager::OnWindowBoundsChanged(
......
......@@ -112,13 +112,12 @@ class ASH_EXPORT WorkspaceLayoutManager
friend class WorkspaceControllerTestApi;
typedef std::set<aura::Window*> WindowSet;
// Observes changes in windows in the SettingsBubbleWindowObserver, and
// Observes changes in windows in the BubbleWindowObserver, and
// notifies WorkspaceLayoutManager to send out system ui area change events.
class SettingsBubbleWindowObserver : public aura::WindowObserver {
class BubbleWindowObserver : public aura::WindowObserver {
public:
SettingsBubbleWindowObserver(
WorkspaceLayoutManager* workspace_layout_manager);
~SettingsBubbleWindowObserver() override;
BubbleWindowObserver(WorkspaceLayoutManager* workspace_layout_manager);
~BubbleWindowObserver() override;
void ObserveWindow(aura::Window* window);
......@@ -138,7 +137,7 @@ class ASH_EXPORT WorkspaceLayoutManager
void StopOberservingWindow(aura::Window* window);
DISALLOW_COPY_AND_ASSIGN(SettingsBubbleWindowObserver);
DISALLOW_COPY_AND_ASSIGN(BubbleWindowObserver);
};
// Adjusts the bounds of all managed windows when the display area changes.
......@@ -162,16 +161,18 @@ class ASH_EXPORT WorkspaceLayoutManager
void UpdateAlwaysOnTop(aura::Window* active_desk_fullscreen_window);
// Notifies windows about a change in a system ui area. This could be
// the keyboard or any window in the SettingsBubbleContainer. Windows will
// only be notified about changes to system ui areas on the display they are
// on.
// the keyboard or any window in the SettingsBubbleContainer or
// autoclick_menu_bubble_container_. Windows will only be notified about
// changes to system ui areas on the display they are on.
void NotifySystemUiAreaChanged();
aura::Window* window_;
aura::Window* root_window_;
RootWindowController* root_window_controller_;
aura::Window* settings_bubble_container_;
SettingsBubbleWindowObserver settings_bubble_window_observer_;
BubbleWindowObserver settings_bubble_window_observer_;
aura::Window* autoclick_bubble_container_;
BubbleWindowObserver autoclick_bubble_window_observer_;
// Set of windows we're listening to.
WindowSet windows_;
......
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