Commit 5addce7c authored by Anatoliy Potapchuk's avatar Anatoliy Potapchuk Committed by Commit Bot

[Ash] Add detailed view for floating accessibility menu

This cl replaces placeholder logic with the display of actual menu with
the list of features users can enable/disable.

PRD: go/kiosk-a11y
UX: go/kiosk-a11y-ux

Bug: 1049566
Change-Id: If67d7ef3ce41b888cbed7fae59c8cadd8afba498
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2151565
Commit-Queue: Anatoliy Potapchuk <apotapchuk@chromium.org>
Reviewed-by: default avatarXiyuan Xia <xiyuan@chromium.org>
Reviewed-by: default avatarKatie Dektar <katie@chromium.org>
Cr-Commit-Position: refs/heads/master@{#761910}
parent 8c8a59a9
......@@ -739,10 +739,14 @@ component("ash") {
"system/accessibility/dictation_button_tray.h",
"system/accessibility/floating_accessibility_controller.cc",
"system/accessibility/floating_accessibility_controller.h",
"system/accessibility/floating_accessibility_detailed_controller.cc",
"system/accessibility/floating_accessibility_detailed_controller.h",
"system/accessibility/floating_accessibility_view.cc",
"system/accessibility/floating_accessibility_view.h",
"system/accessibility/floating_menu_button.cc",
"system/accessibility/floating_menu_button.h",
"system/accessibility/floating_menu_utils.cc",
"system/accessibility/floating_menu_utils.h",
"system/accessibility/select_to_speak_tray.cc",
"system/accessibility/select_to_speak_tray.h",
"system/accessibility/switch_access_back_button_bubble_controller.cc",
......
......@@ -528,7 +528,9 @@ AccessibilityControllerImpl::AccessibilityControllerImpl()
CreateAccessibilityFeatures();
}
AccessibilityControllerImpl::~AccessibilityControllerImpl() = default;
AccessibilityControllerImpl::~AccessibilityControllerImpl() {
floating_menu_controller_.reset();
}
void AccessibilityControllerImpl::CreateAccessibilityFeatures() {
DCHECK(VerifyFeaturesData());
......@@ -1156,7 +1158,7 @@ void AccessibilityControllerImpl::ShowFloatingMenuIfEnabled() {
if (floating_menu().enabled()) {
DCHECK(!floating_menu_controller_);
floating_menu_controller_ =
std::make_unique<FloatingAccessibilityController>();
std::make_unique<FloatingAccessibilityController>(this);
floating_menu_controller_->Show(GetFloatingMenuPosition());
}
}
......
......@@ -8,6 +8,7 @@
#include "ash/shelf/shelf.h"
#include "ash/shell.h"
#include "ash/system/accessibility/autoclick_scroll_bubble_controller.h"
#include "ash/system/accessibility/floating_menu_utils.h"
#include "ash/system/tray/tray_background_view.h"
#include "ash/system/tray/tray_bubble_view.h"
#include "ash/system/tray/tray_constants.h"
......@@ -28,42 +29,6 @@ namespace {
// Autoclick menu constants.
const int kAutoclickMenuWidth = 369;
const int kAutoclickMenuHeight = 64;
FloatingMenuPosition DefaultSystemPosition() {
return base::i18n::IsRTL() ? FloatingMenuPosition::kBottomLeft
: FloatingMenuPosition::kBottomRight;
}
views::BubbleBorder::Arrow GetScrollAnchorAlignmentForPosition(
FloatingMenuPosition position) {
// If this is the default system position, pick the position based on the
// language direction.
if (position == FloatingMenuPosition::kSystemDefault) {
position = DefaultSystemPosition();
}
// Mirror arrow in RTL languages so that it always stays near the screen
// edge.
switch (position) {
case FloatingMenuPosition::kBottomLeft:
return base::i18n::IsRTL() ? views::BubbleBorder::Arrow::TOP_RIGHT
: views::BubbleBorder::Arrow::TOP_LEFT;
case FloatingMenuPosition::kTopLeft:
return base::i18n::IsRTL() ? views::BubbleBorder::Arrow::BOTTOM_RIGHT
: views::BubbleBorder::Arrow::BOTTOM_LEFT;
case FloatingMenuPosition::kBottomRight:
return base::i18n::IsRTL() ? views::BubbleBorder::Arrow::TOP_LEFT
: views::BubbleBorder::Arrow::TOP_RIGHT;
case FloatingMenuPosition::kTopRight:
return base::i18n::IsRTL() ? views::BubbleBorder::Arrow::BOTTOM_LEFT
: views::BubbleBorder::Arrow::BOTTOM_RIGHT;
case FloatingMenuPosition::kSystemDefault:
// It's not possible for position to be kSystemDefault here because we've
// set it via DefaultSystemPosition() above if it was kSystemDefault.
NOTREACHED();
return views::BubbleBorder::Arrow::TOP_LEFT;
}
}
} // namespace
AutoclickMenuBubbleController::AutoclickMenuBubbleController() {
......@@ -92,7 +57,7 @@ void AutoclickMenuBubbleController::SetEventType(AutoclickEventType type) {
anchor_rect.Inset(-kCollisionWindowWorkAreaInsetsDp,
-kCollisionWindowWorkAreaInsetsDp);
scroll_bubble_controller_->ShowBubble(
anchor_rect, GetScrollAnchorAlignmentForPosition(position_));
anchor_rect, GetAnchorAlignmentForFloatingMenuPosition(position_));
} else if (scroll_bubble_controller_) {
scroll_bubble_controller_ = nullptr;
// Update the bubble menu's position in case it had moved out of the way
......@@ -117,46 +82,12 @@ void AutoclickMenuBubbleController::SetPosition(
// If this is the default system position, pick the position based on the
// language direction.
if (new_position == FloatingMenuPosition::kSystemDefault)
new_position = DefaultSystemPosition();
new_position = DefaultSystemFloatingMenuPosition();
// 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 (new_position) {
case FloatingMenuPosition::kBottomRight:
new_bounds = gfx::Rect(work_area.right() - kAutoclickMenuWidth,
work_area.bottom() - kAutoclickMenuHeight,
kAutoclickMenuWidth, kAutoclickMenuHeight);
break;
case FloatingMenuPosition::kBottomLeft:
new_bounds =
gfx::Rect(work_area.x(), work_area.bottom() - kAutoclickMenuHeight,
kAutoclickMenuWidth, kAutoclickMenuHeight);
break;
case FloatingMenuPosition::kTopLeft:
// Because there is no inset at the top of the widget, add
// 2 * kCollisionWindowWorkAreaInsetsDp to the top of the work area.
// to ensure correct padding.
new_bounds = gfx::Rect(
work_area.x(), work_area.y() + 2 * kCollisionWindowWorkAreaInsetsDp,
kAutoclickMenuWidth, kAutoclickMenuHeight);
break;
case FloatingMenuPosition::kTopRight:
// Because there is no inset at the top of the widget, add
// 2 * kCollisionWindowWorkAreaInsetsDp to the top of the work area.
// to ensure correct padding.
new_bounds =
gfx::Rect(work_area.right() - kAutoclickMenuWidth,
work_area.y() + 2 * kCollisionWindowWorkAreaInsetsDp,
kAutoclickMenuWidth, kAutoclickMenuHeight);
break;
case FloatingMenuPosition::kSystemDefault:
return;
}
gfx::Rect new_bounds = GetOnScreenBoundsForFloatingMenuPosition(
gfx::Size(kAutoclickMenuWidth, kAutoclickMenuHeight), new_position);
// Update the preferred bounds based on other system windows.
gfx::Rect resting_bounds = CollisionDetectionUtils::GetRestingPosition(
......@@ -187,7 +118,7 @@ void AutoclickMenuBubbleController::SetPosition(
// Position the scroll bubble with respect to the menu.
scroll_bubble_controller_->UpdateAnchorRect(
resting_bounds, GetScrollAnchorAlignmentForPosition(new_position));
resting_bounds, GetAnchorAlignmentForFloatingMenuPosition(new_position));
}
void AutoclickMenuBubbleController::SetScrollPosition(
......
......@@ -68,6 +68,7 @@ class ASH_EXPORT AutoclickMenuBubbleController
private:
friend class AutoclickMenuBubbleControllerTest;
friend class AutoclickTest;
friend class FloatingAccessibilityControllerTest;
// Owned by views hierarchy.
TrayBubbleView* bubble_view_ = nullptr;
......
......@@ -4,9 +4,11 @@
#include "ash/system/accessibility/floating_accessibility_controller.h"
#include "ash/accessibility/accessibility_controller_impl.h"
#include "ash/public/cpp/shell_window_ids.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shell.h"
#include "ash/system/accessibility/floating_menu_utils.h"
#include "ash/system/tray/tray_background_view.h"
#include "ash/system/tray/tray_constants.h"
#include "ash/wm/collision_detection/collision_detection_utils.h"
......@@ -22,18 +24,17 @@ constexpr int kFloatingMenuHeight = 64;
constexpr base::TimeDelta kAnimationDuration =
base::TimeDelta::FromMilliseconds(150);
FloatingMenuPosition DefaultSystemPosition() {
return base::i18n::IsRTL() ? FloatingMenuPosition::kBottomLeft
: FloatingMenuPosition::kBottomRight;
}
} // namespace
FloatingAccessibilityController::FloatingAccessibilityController() {
FloatingAccessibilityController::FloatingAccessibilityController(
AccessibilityControllerImpl* accessibility_controller)
: accessibility_controller_(accessibility_controller) {
Shell::Get()->locale_update_controller()->AddObserver(this);
accessibility_controller_->AddObserver(this);
}
FloatingAccessibilityController::~FloatingAccessibilityController() {
Shell::Get()->locale_update_controller()->RemoveObserver(this);
accessibility_controller_->RemoveObserver(this);
if (bubble_widget_ && !bubble_widget_->IsClosed())
bubble_widget_->CloseNow();
}
......@@ -48,25 +49,24 @@ void FloatingAccessibilityController::Show(FloatingMenuPosition position) {
DCHECK(!bubble_view_);
position_ = position;
TrayBubbleView::InitParams init_params;
init_params.delegate = this;
// We need our view to be on the same level as the autoclicks menu, so neither
// of them is overlapping each other.
// Our view uses SettingsBubbleContainer since it is activatable and is
// included in the collision detection logic.
init_params.parent_window = Shell::GetContainer(
Shell::GetPrimaryRootWindow(), kShellWindowId_AutoclickContainer);
Shell::GetPrimaryRootWindow(), kShellWindowId_SettingBubbleContainer);
init_params.anchor_mode = TrayBubbleView::AnchorMode::kRect;
// The widget's shadow is drawn below and on the sides of the view, with a
// width of kCollisionWindowWorkAreaInsetsDp. Set the top inset to 0 to ensure
// the scroll view is drawn at kCollisionWindowWorkAreaInsetsDp above the
// the detailed view is drawn at kCollisionWindowWorkAreaInsetsDp above the
// bubble menu when the position is at the bottom of the screen. The space
// between the bubbles belongs to the scroll view bubble's shadow.
// between the bubbles belongs to the detailed view bubble's shadow.
init_params.insets = gfx::Insets(0, kCollisionWindowWorkAreaInsetsDp,
kCollisionWindowWorkAreaInsetsDp,
kCollisionWindowWorkAreaInsetsDp);
init_params.max_height = kFloatingMenuHeight;
init_params.corner_radius = kUnifiedTrayCornerRadius;
init_params.has_shadow = false;
init_params.max_height = kFloatingMenuHeight;
init_params.translucent = true;
bubble_view_ = new FloatingAccessibilityBubbleView(init_params);
......@@ -82,7 +82,7 @@ void FloatingAccessibilityController::Show(FloatingMenuPosition position) {
TrayBackgroundView::InitializeBubbleAnimations(bubble_widget_);
bubble_view_->InitializeAndShowBubble();
SetMenuPosition(position_);
SetMenuPosition(position);
}
void FloatingAccessibilityController::SetMenuPosition(
......@@ -101,48 +101,10 @@ void FloatingAccessibilityController::SetMenuPosition(
// If this is the default system position, pick the position based on the
// language direction.
if (new_position == FloatingMenuPosition::kSystemDefault)
new_position = DefaultSystemPosition();
// 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;
gfx::Size preferred_size = menu_view_->GetPreferredSize();
switch (new_position) {
case FloatingMenuPosition::kBottomRight:
new_bounds = gfx::Rect(work_area.right() - preferred_size.width(),
work_area.bottom() - preferred_size.height(),
preferred_size.width(), preferred_size.height());
break;
case FloatingMenuPosition::kBottomLeft:
new_bounds =
gfx::Rect(work_area.x(), work_area.bottom() - preferred_size.height(),
preferred_size.width(), preferred_size.height());
break;
case FloatingMenuPosition::kTopLeft:
// Because there is no inset at the top of the widget, add
// 2 * kCollisionWindowWorkAreaInsetsDp to the top of the work area.
// to ensure correct padding.
new_bounds = gfx::Rect(
work_area.x(), work_area.y() + 2 * kCollisionWindowWorkAreaInsetsDp,
preferred_size.width(), preferred_size.height());
break;
case FloatingMenuPosition::kTopRight:
// Because there is no inset at the top of the widget, add
// 2 * kCollisionWindowWorkAreaInsetsDp to the top of the work area.
// to ensure correct padding.
new_bounds =
gfx::Rect(work_area.right() - preferred_size.width(),
work_area.y() + 2 * kCollisionWindowWorkAreaInsetsDp,
preferred_size.width(), preferred_size.height());
break;
case FloatingMenuPosition::kSystemDefault:
return;
}
new_position = DefaultSystemFloatingMenuPosition();
gfx::Rect new_bounds = GetOnScreenBoundsForFloatingMenuPosition(
menu_view_->GetPreferredSize(), new_position);
gfx::Rect resting_bounds =
CollisionDetectionUtils::AdjustToFitMovementAreaByGravity(
......@@ -154,6 +116,7 @@ void FloatingAccessibilityController::SetMenuPosition(
resting_bounds.Inset(-kCollisionWindowWorkAreaInsetsDp, 0,
-kCollisionWindowWorkAreaInsetsDp,
-kCollisionWindowWorkAreaInsetsDp);
if (bubble_widget_->GetWindowBoundsInScreen() == resting_bounds)
return;
......@@ -164,11 +127,39 @@ void FloatingAccessibilityController::SetMenuPosition(
settings.SetTransitionDuration(kAnimationDuration);
settings.SetTweenType(gfx::Tween::EASE_OUT);
bubble_widget_->SetBounds(resting_bounds);
if (detailed_menu_controller_) {
detailed_menu_controller_->UpdateAnchorRect(
resting_bounds, GetAnchorAlignmentForFloatingMenuPosition(position_));
}
}
void FloatingAccessibilityController::OnDetailedMenuEnabled(bool enabled) {
// TODO(crbug.com/1061068): Implement detailed menu view logic.
detailed_view_shown_ = enabled;
if (enabled) {
detailed_menu_controller_ =
std::make_unique<FloatingAccessibilityDetailedController>(this);
gfx::Rect anchor_rect = bubble_view_->GetBoundsInScreen();
anchor_rect.Inset(-kCollisionWindowWorkAreaInsetsDp, 0,
-kCollisionWindowWorkAreaInsetsDp,
-kCollisionWindowWorkAreaInsetsDp);
detailed_menu_controller_->Show(
anchor_rect, GetAnchorAlignmentForFloatingMenuPosition(position_));
menu_view_->SetDetailedViewShown(true);
} else {
detailed_menu_controller_.reset();
// We may need to update the autoclick bounds.
Shell::Get()
->accessibility_controller()
->UpdateAutoclickMenuBoundsIfNeeded();
}
}
void FloatingAccessibilityController::OnDetailedMenuClosed() {
detailed_menu_controller_.reset();
if (!menu_view_)
return;
menu_view_->SetDetailedViewShown(false);
}
void FloatingAccessibilityController::BubbleViewDestroyed() {
......@@ -184,4 +175,12 @@ void FloatingAccessibilityController::OnLocaleChanged() {
SetMenuPosition(position_);
}
void FloatingAccessibilityController::OnAccessibilityStatusChanged() {
// Some features may change the available screen area(docked magnifier), we
// will update the location of the menu in such cases.
SetMenuPosition(position_);
if (detailed_menu_controller_)
detailed_menu_controller_->OnAccessibilityStatusChanged();
}
} // namespace ash
......@@ -5,22 +5,28 @@
#ifndef ASH_SYSTEM_ACCESSIBILITY_FLOATING_ACCESSIBILITY_CONTROLLER_H_
#define ASH_SYSTEM_ACCESSIBILITY_FLOATING_ACCESSIBILITY_CONTROLLER_H_
#include "ash/accessibility/accessibility_observer.h"
#include "ash/ash_export.h"
#include "ash/public/cpp/accessibility_controller_enums.h"
#include "ash/public/cpp/ash_constants.h"
#include "ash/system/accessibility/floating_accessibility_detailed_controller.h"
#include "ash/system/accessibility/floating_accessibility_view.h"
#include "ash/system/locale/locale_update_controller_impl.h"
namespace ash {
class AccessibilityControllerImpl;
class FloatingAccessibilityView;
// Controls the floating accessibility menu.
class FloatingAccessibilityController
class ASH_EXPORT FloatingAccessibilityController
: public FloatingAccessibilityView::Delegate,
public FloatingAccessibilityDetailedController::Delegate,
public TrayBubbleView::Delegate,
public LocaleChangeObserver {
public LocaleChangeObserver,
public AccessibilityObserver {
public:
FloatingAccessibilityController();
FloatingAccessibilityController(AccessibilityControllerImpl* accessibility_controller);
FloatingAccessibilityController(const FloatingAccessibilityController&) =
delete;
FloatingAccessibilityController& operator=(
......@@ -31,10 +37,15 @@ class FloatingAccessibilityController
void Show(FloatingMenuPosition position);
void SetMenuPosition(FloatingMenuPosition new_position);
// AccessibilityObserver:
void OnAccessibilityStatusChanged() override;
private:
friend class FloatingAccessibilityControllerTest;
// FloatingAccessibilityView::Delegate:
void OnDetailedMenuEnabled(bool enabled) override;
// FloatingAccessibilityDetailedController::Delegate:
void OnDetailedMenuClosed() override;
// TrayBubbleView::Delegate:
void BubbleViewDestroyed() override;
// LocaleChangeObserver:
......@@ -46,7 +57,13 @@ class FloatingAccessibilityController
bool detailed_view_shown_ = false;
// Controller for the detailed view, exists only when visible.
std::unique_ptr<FloatingAccessibilityDetailedController>
detailed_menu_controller_;
FloatingMenuPosition position_ = kDefaultFloatingMenuPosition;
AccessibilityControllerImpl* const accessibility_controller_; // Owns us.
};
} // namespace ash
......
......@@ -5,9 +5,13 @@
#include "ash/system/accessibility/floating_accessibility_controller.h"
#include "ash/accessibility/accessibility_controller_impl.h"
#include "ash/autoclick/autoclick_controller.h"
#include "ash/public/cpp/session/session_types.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shell.h"
#include "ash/system/accessibility/autoclick_menu_bubble_controller.h"
#include "ash/system/accessibility/autoclick_menu_view.h"
#include "ash/system/accessibility/tray_accessibility.h"
#include "ash/test/ash_test_base.h"
namespace ash {
......@@ -42,7 +46,17 @@ class FloatingAccessibilityControllerTest : public AshTestBase {
return controller() ? controller()->menu_view_ : nullptr;
}
bool detailed_view_shown() { return controller()->detailed_view_shown_; }
AutoclickMenuView* autoclick_menu_view() {
AutoclickMenuBubbleController* controller =
Shell::Get()
->autoclick_controller()
->GetMenuBubbleControllerForTesting();
return controller ? controller->menu_view_ : nullptr;
}
bool detailed_view_shown() {
return controller() && controller()->detailed_menu_controller_.get();
}
views::View* GetMenuButton(FloatingAccessibilityView::ButtonId button_id) {
FloatingAccessibilityView* view = menu_view();
......@@ -63,6 +77,20 @@ class FloatingAccessibilityControllerTest : public AshTestBase {
: gfx::Rect(-kMenuViewBoundsBuffer, -kMenuViewBoundsBuffer);
}
gfx::Rect GetDetailedMenuBounds() {
FloatingAccessibilityDetailedController* detailed_controller =
controller() ? controller()->detailed_menu_controller_.get() : nullptr;
return detailed_controller
? detailed_controller->detailed_view_->GetBoundsInScreen()
: gfx::Rect(-kMenuViewBoundsBuffer, -kMenuViewBoundsBuffer);
}
gfx::Rect GetAutoclickMenuBounds() {
return autoclick_menu_view()
? autoclick_menu_view()->GetBoundsInScreen()
: gfx::Rect(-kMenuViewBoundsBuffer, -kMenuViewBoundsBuffer);
}
void Show() { accessibility_controller()->ShowFloatingMenuIfEnabled(); }
void SetUpVisibleMenu() {
......@@ -176,4 +204,117 @@ TEST_F(FloatingAccessibilityControllerTest, LocaleChangeObserver) {
kMenuViewBoundsBuffer);
}
// The detailed view has to be anchored to the floating menu.
TEST_F(FloatingAccessibilityControllerTest, DetailedViewPosition) {
SetUpVisibleMenu();
views::View* button =
GetMenuButton(FloatingAccessibilityView::ButtonId::kSettingsList);
ui::GestureEvent event = CreateTapEvent();
button->OnGestureEvent(&event);
const struct { bool is_RTL; } kTestCases[] = {{true}, {false}};
for (auto& test : kTestCases) {
SCOPED_TRACE(
base::StringPrintf("Testing rtl=#[%d]", test.is_RTL));
// These positions should be relative to the corners of the screen
// whether we are in RTL or LTR.
base::i18n::SetRTLForTesting(test.is_RTL);
// When the menu is in the top right, the detailed should be directly
// under it and along the right side of the screen.
controller()->SetMenuPosition(FloatingMenuPosition::kTopRight);
EXPECT_LT(GetDetailedMenuBounds().ManhattanDistanceToPoint(
GetMenuViewBounds().bottom_center()),
kMenuViewBoundsBuffer);
EXPECT_EQ(GetDetailedMenuBounds().right(), GetMenuViewBounds().right());
// When the menu is in the bottom right, the detailed view is directly above
// it and along the right side of the screen.
controller()->SetMenuPosition(FloatingMenuPosition::kBottomRight);
EXPECT_LT(GetDetailedMenuBounds().ManhattanDistanceToPoint(
GetMenuViewBounds().top_center()),
kMenuViewBoundsBuffer);
EXPECT_EQ(GetDetailedMenuBounds().right(), GetMenuViewBounds().right());
// When the menu is on the bottom left, the detailed view is directly above
// it and along the left side of the screen.
controller()->SetMenuPosition(FloatingMenuPosition::kBottomLeft);
EXPECT_LT(GetDetailedMenuBounds().ManhattanDistanceToPoint(
GetMenuViewBounds().top_center()),
kMenuViewBoundsBuffer);
EXPECT_EQ(GetDetailedMenuBounds().x(), GetMenuViewBounds().x());
// When the menu is on the top left, the detailed view is directly below it
// and along the left side of the screen.
controller()->SetMenuPosition(FloatingMenuPosition::kTopLeft);
EXPECT_LT(GetDetailedMenuBounds().ManhattanDistanceToPoint(
GetMenuViewBounds().bottom_center()),
kMenuViewBoundsBuffer);
EXPECT_EQ(GetDetailedMenuBounds().x(), GetMenuViewBounds().x());
}
}
TEST_F(FloatingAccessibilityControllerTest, CollisionWithAutoclicksMenu) {
// We expect floating accessibility menu not to move when there is autoclick
// menu present, but to push it to avoid collision. This test is exactly the
// same as CanChangePosition, but the autoclicks are enabled.
SetUpVisibleMenu();
accessibility_controller()->SetFloatingMenuPosition(
FloatingMenuPosition::kTopRight);
accessibility_controller()->SetAutoclickEnabled(true);
// Get the full root window bounds to test the position.
gfx::Rect window_bounds = Shell::GetPrimaryRootWindow()->bounds();
// Test cases rotate clockwise.
const struct {
gfx::Point expected_location;
FloatingMenuPosition expected_position;
} kTestCases[] = {
{gfx::Point(window_bounds.right(), window_bounds.bottom()),
FloatingMenuPosition::kBottomRight},
{gfx::Point(0, window_bounds.bottom()),
FloatingMenuPosition::kBottomLeft},
{gfx::Point(0, 0), FloatingMenuPosition::kTopLeft},
{gfx::Point(window_bounds.right(), 0), FloatingMenuPosition::kTopRight},
};
// Find the autoclick menu position button.
views::View* button =
GetMenuButton(FloatingAccessibilityView::ButtonId::kPosition);
ASSERT_TRUE(button) << "No position button found.";
// Loop through all positions twice.
for (int i = 0; i < 2; i++) {
for (const auto& test : kTestCases) {
SCOPED_TRACE(
base::StringPrintf("Testing position #[%d]", test.expected_position));
// Tap the position button.
ui::GestureEvent event = CreateTapEvent();
button->OnGestureEvent(&event);
// Pref change happened.
EXPECT_EQ(test.expected_position, menu_position());
// Rotate around the autoclicks menu.
for (int j = 0; j < 4; j++) {
// The position button on autoclicks view.
ui::GestureEvent event = CreateTapEvent();
autoclick_menu_view()
->GetViewByID(
static_cast<int>(AutoclickMenuView::ButtonId::kPosition))
->OnGestureEvent(&event);
// Menu is in generally the correct screen location.
EXPECT_LT(GetMenuViewBounds().ManhattanDistanceToPoint(
test.expected_location),
kMenuViewBoundsBuffer);
EXPECT_FALSE(GetMenuViewBounds().Intersects(GetAutoclickMenuBounds()));
}
}
}
}
} // 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.
#include "ash/system/accessibility/floating_accessibility_detailed_controller.h"
#include "ash/public/cpp/shell_window_ids.h"
#include "ash/shell.h"
#include "ash/system/accessibility/tray_accessibility.h"
#include "ash/system/tray/tray_background_view.h"
#include "ash/system/tray/tray_constants.h"
#include "ash/wm/collision_detection/collision_detection_utils.h"
#include "ui/compositor/scoped_layer_animation_settings.h"
namespace ash {
namespace {
constexpr base::TimeDelta kAnimationDuration =
base::TimeDelta::FromMilliseconds(150);
constexpr int kDetailedViewHeightDip = 350;
} // namespace
class FloatingAccessibilityDetailedController::DetailedBubbleView
: public TrayBubbleView {
public:
explicit DetailedBubbleView(TrayBubbleView::InitParams init_params)
: TrayBubbleView(init_params) {}
void UpdateAnchorRect(gfx::Rect anchor_rect,
views::BubbleBorder::Arrow alignment) {
SetArrowWithoutResizing(alignment);
SetAnchorRect(anchor_rect);
}
// views::View:
const char* GetClassName() const override {
return "FloatingAccessibilityDetailedView";
}
};
FloatingAccessibilityDetailedController::
FloatingAccessibilityDetailedController(Delegate* delegate)
: DetailedViewDelegate(/*tray_controller*/ nullptr), delegate_(delegate) {}
FloatingAccessibilityDetailedController::
~FloatingAccessibilityDetailedController() {
if (!bubble_widget_ || bubble_widget_->IsClosed())
return;
bubble_widget_->CloseNow();
}
void FloatingAccessibilityDetailedController::Show(
gfx::Rect anchor_rect,
views::BubbleBorder::Arrow alignment) {
if (bubble_view_)
return;
TrayBubbleView::InitParams init_params;
init_params.delegate = this;
init_params.parent_window = Shell::GetContainer(
Shell::GetPrimaryRootWindow(), kShellWindowId_SettingBubbleContainer);
init_params.anchor_mode = TrayBubbleView::AnchorMode::kRect;
init_params.anchor_rect = anchor_rect;
init_params.insets = gfx::Insets(0, kUnifiedMenuPadding, kUnifiedMenuPadding,
kUnifiedMenuPadding);
init_params.corner_radius = kUnifiedTrayCornerRadius;
init_params.has_shadow = false;
init_params.translucent = true;
init_params.show_by_click = true;
bubble_view_ = new DetailedBubbleView(init_params);
bubble_view_->SetArrowWithoutResizing(alignment);
detailed_view_ = bubble_view_->AddChildView(
std::make_unique<tray::AccessibilityDetailedView>(this));
bubble_view_->SetPreferredSize(
gfx::Size(kTrayMenuWidth, kDetailedViewHeightDip));
detailed_view_->SetFocusBehavior(ActionableView::FocusBehavior::ALWAYS);
detailed_view_->SetPaintToLayer();
detailed_view_->layer()->SetFillsBoundsOpaquely(false);
bubble_widget_ = views::BubbleDialogDelegateView::CreateBubble(bubble_view_);
TrayBackgroundView::InitializeBubbleAnimations(bubble_widget_);
bubble_view_->InitializeAndShowBubble();
// Focus on the bubble whenever it is shown.
detailed_view_->RequestFocus();
}
void FloatingAccessibilityDetailedController::UpdateAnchorRect(
gfx::Rect anchor_rect,
views::BubbleBorder::Arrow alignment) {
ui::ScopedLayerAnimationSettings settings(
bubble_widget_->GetLayer()->GetAnimator());
settings.SetPreemptionStrategy(
ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
settings.SetTransitionDuration(kAnimationDuration);
settings.SetTweenType(gfx::Tween::EASE_OUT);
bubble_view_->UpdateAnchorRect(anchor_rect, alignment);
}
void FloatingAccessibilityDetailedController::CloseBubble() {
if (!bubble_widget_ || bubble_widget_->IsClosed())
return;
bubble_widget_->Close();
}
void FloatingAccessibilityDetailedController::TransitionToMainView(
bool restore_focus) {
CloseBubble();
}
views::Button* FloatingAccessibilityDetailedController::CreateHelpButton(
views::ButtonListener* listener) {
auto* button = DetailedViewDelegate::CreateHelpButton(listener);
button->SetEnabled(false);
return button;
}
views::Button* FloatingAccessibilityDetailedController::CreateSettingsButton(
views::ButtonListener* listener,
int setting_accessible_name_id) {
auto* button = DetailedViewDelegate::CreateSettingsButton(
listener, setting_accessible_name_id);
// TODO(crbug.com/1061068): Enable when the settings UI is ready.
button->SetEnabled(false);
return button;
}
void FloatingAccessibilityDetailedController::BubbleViewDestroyed() {
detailed_view_ = nullptr;
bubble_view_ = nullptr;
bubble_widget_ = nullptr;
delegate_->OnDetailedMenuClosed();
// Hammer time, |this| is destroyed in the previous call.
}
void FloatingAccessibilityDetailedController::OnAccessibilityStatusChanged() {
if (detailed_view_)
detailed_view_->OnAccessibilityStatusChanged();
}
} // 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_SYSTEM_ACCESSIBILITY_FLOATING_ACCESSIBILITY_DETAILED_CONTROLLER_H_
#define ASH_SYSTEM_ACCESSIBILITY_FLOATING_ACCESSIBILITY_DETAILED_CONTROLLER_H_
#include "ash/ash_export.h"
#include "ash/system/tray/detailed_view_delegate.h"
#include "ash/system/tray/tray_bubble_view.h"
#include "ui/views/bubble/bubble_border.h"
namespace ash {
namespace tray {
class AccessibilityDetailedView;
}
// Controller for the detailed view of accessibility floating menu.
class ASH_EXPORT FloatingAccessibilityDetailedController
: public TrayBubbleView::Delegate,
public DetailedViewDelegate {
public:
class Delegate {
public:
virtual void OnDetailedMenuClosed() {}
virtual ~Delegate() = default;
};
explicit FloatingAccessibilityDetailedController(Delegate* delegate);
~FloatingAccessibilityDetailedController() override;
void Show(gfx::Rect anchor_rect, views::BubbleBorder::Arrow alignment);
void UpdateAnchorRect(gfx::Rect anchor_rect,
views::BubbleBorder::Arrow alignment);
// DetailedViewDelegate:
void CloseBubble() override;
void TransitionToMainView(bool restore_focus) override;
void OnAccessibilityStatusChanged();
private:
friend class FloatingAccessibilityControllerTest;
class DetailedBubbleView;
// DetailedViewDelegate:
views::Button* CreateHelpButton(views::ButtonListener* listener) override;
views::Button* CreateSettingsButton(views::ButtonListener* listener,
int setting_accessible_name_id) override;
// TrayBubbleView::Delegate:
void BubbleViewDestroyed() override;
DetailedBubbleView* bubble_view_ = nullptr;
views::Widget* bubble_widget_ = nullptr;
tray::AccessibilityDetailedView* detailed_view_ = nullptr;
Delegate* const delegate_; // Owns us.
};
} // namespace ash
#endif // ASH_SYSTEM_ACCESSIBILITY_FLOATING_ACCESSIBILITY_DETAILED_CONTROLLER_H
......@@ -22,7 +22,8 @@ namespace ash {
namespace {
constexpr int kPanelPositionButtonPadding = 16;
// These constants are defined in DIP.
constexpr int kPanelPositionButtonPadding = 14;
constexpr int kPanelPositionButtonSize = 36;
constexpr int kSeparatorHeight = 16;
......@@ -39,13 +40,11 @@ std::unique_ptr<views::Separator> CreateSeparator() {
return separator;
}
std::unique_ptr<views::View> CreateButtonRowContainer() {
std::unique_ptr<views::View> CreateButtonRowContainer(int padding) {
auto button_container = std::make_unique<views::View>();
button_container->SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kHorizontal,
gfx::Insets(0, kPanelPositionButtonPadding, kPanelPositionButtonPadding,
kPanelPositionButtonPadding),
kPanelPositionButtonPadding));
gfx::Insets(0, padding, padding, padding), padding));
return button_container;
}
......@@ -74,7 +73,7 @@ FloatingAccessibilityView::FloatingAccessibilityView(Delegate* delegate)
// TODO(crbug.com/1061068): Add buttons view that represents enabled features.
std::unique_ptr<views::View> tray_button_container =
CreateButtonRowContainer();
CreateButtonRowContainer(kUnifiedTopShortcutSpacing);
a11y_tray_button_ =
tray_button_container->AddChildView(std::make_unique<FloatingMenuButton>(
this, kUnifiedMenuAccessibilityIcon,
......@@ -82,7 +81,7 @@ FloatingAccessibilityView::FloatingAccessibilityView(Delegate* delegate)
/*flip_for_rtl*/ true, kTrayItemSize));
std::unique_ptr<views::View> position_button_container =
CreateButtonRowContainer();
CreateButtonRowContainer(kPanelPositionButtonPadding);
position_button_ = position_button_container->AddChildView(
std::make_unique<FloatingMenuButton>(
this, kAutoclickPositionBottomLeftIcon,
......@@ -122,11 +121,14 @@ void FloatingAccessibilityView::SetMenuPosition(FloatingMenuPosition position) {
}
}
void FloatingAccessibilityView::SetDetailedViewShown(bool shown) {
a11y_tray_button_->SetToggled(shown);
}
void FloatingAccessibilityView::ButtonPressed(views::Button* sender,
const ui::Event& event) {
if (sender == a11y_tray_button_) {
delegate_->OnDetailedMenuEnabled(!a11y_tray_button_->IsToggled());
a11y_tray_button_->SetToggled(!a11y_tray_button_->IsToggled());
return;
}
......
......@@ -64,6 +64,7 @@ class FloatingAccessibilityView : public views::View,
FloatingAccessibilityView(const FloatingAccessibilityView&) = delete;
void SetMenuPosition(FloatingMenuPosition position);
void SetDetailedViewShown(bool shown);
private:
// views::ButtonListener:
......
// 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/system/accessibility/floating_menu_utils.h"
#include "ash/public/cpp/accessibility_controller_enums.h"
#include "ash/shell.h"
#include "ash/wm/collision_detection/collision_detection_utils.h"
#include "ash/wm/work_area_insets.h"
#include "base/i18n/rtl.h"
namespace ash {
FloatingMenuPosition DefaultSystemFloatingMenuPosition() {
return base::i18n::IsRTL() ? FloatingMenuPosition::kBottomLeft
: FloatingMenuPosition::kBottomRight;
}
gfx::Rect GetOnScreenBoundsForFloatingMenuPosition(
const gfx::Size& menu_bounds,
FloatingMenuPosition position) {
// Calculates the ideal bounds.
aura::Window* window = Shell::GetPrimaryRootWindow();
gfx::Rect work_area =
WorkAreaInsets::ForWindow(window)->user_work_area_bounds();
switch (position) {
case FloatingMenuPosition::kBottomRight:
return gfx::Rect(work_area.right() - menu_bounds.width(),
work_area.bottom() - menu_bounds.height(),
menu_bounds.width(), menu_bounds.height());
case FloatingMenuPosition::kBottomLeft:
return gfx::Rect(work_area.x(), work_area.bottom() - menu_bounds.height(),
menu_bounds.width(), menu_bounds.height());
case FloatingMenuPosition::kTopLeft:
// Because there is no inset at the top of the widget, add
// 2 * kCollisionWindowWorkAreaInsetsDp to the top of the work area.
// to ensure correct padding.
return gfx::Rect(work_area.x(),
work_area.y() + 2 * kCollisionWindowWorkAreaInsetsDp,
menu_bounds.width(), menu_bounds.height());
case FloatingMenuPosition::kTopRight:
// Because there is no inset at the top of the widget, add
// 2 * kCollisionWindowWorkAreaInsetsDp to the top of the work area.
// to ensure correct padding.
return gfx::Rect(work_area.right() - menu_bounds.width(),
work_area.y() + 2 * kCollisionWindowWorkAreaInsetsDp,
menu_bounds.width(), menu_bounds.height());
case FloatingMenuPosition::kSystemDefault:
NOTREACHED();
return gfx::Rect();
}
}
views::BubbleBorder::Arrow GetAnchorAlignmentForFloatingMenuPosition(
FloatingMenuPosition position) {
// If this is the default system position, pick the position based on the
// language direction.
if (position == FloatingMenuPosition::kSystemDefault) {
position = DefaultSystemFloatingMenuPosition();
}
// Mirror arrow in RTL languages so that it always stays near the screen
// edge.
switch (position) {
case FloatingMenuPosition::kBottomLeft:
return base::i18n::IsRTL() ? views::BubbleBorder::Arrow::TOP_RIGHT
: views::BubbleBorder::Arrow::TOP_LEFT;
case FloatingMenuPosition::kTopLeft:
return base::i18n::IsRTL() ? views::BubbleBorder::Arrow::BOTTOM_RIGHT
: views::BubbleBorder::Arrow::BOTTOM_LEFT;
case FloatingMenuPosition::kBottomRight:
return base::i18n::IsRTL() ? views::BubbleBorder::Arrow::TOP_LEFT
: views::BubbleBorder::Arrow::TOP_RIGHT;
case FloatingMenuPosition::kTopRight:
return base::i18n::IsRTL() ? views::BubbleBorder::Arrow::BOTTOM_LEFT
: views::BubbleBorder::Arrow::BOTTOM_RIGHT;
case FloatingMenuPosition::kSystemDefault:
// It's not possible for position to be kSystemDefault here because we've
// set it via DefaultSystemPosition() above if it was kSystemDefault.
NOTREACHED();
return views::BubbleBorder::Arrow::TOP_LEFT;
}
}
} // 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_SYSTEM_ACCESSIBILITY_FLOATING_MENU_UTILS_H_
#define ASH_SYSTEM_ACCESSIBILITY_FLOATING_MENU_UTILS_H_
#include "ui/views/bubble/bubble_border.h"
namespace gfx {
class Size;
}
namespace ash {
enum class FloatingMenuPosition;
// Helper functions that are used by floating menus.
// Default position for the floating menu. This depends on whether the user's
// language is LTR or RTL.
FloatingMenuPosition DefaultSystemFloatingMenuPosition();
// Determines bounds for the floating menu depending on the desired menu
// position.
gfx::Rect GetOnScreenBoundsForFloatingMenuPosition(
const gfx::Size& menu_size,
FloatingMenuPosition position);
// Determines the position for the view anchored to the floating menu.
views::BubbleBorder::Arrow GetAnchorAlignmentForFloatingMenuPosition(
FloatingMenuPosition position);
} // namespace ash
#endif // ASH_SYSTEM_ACCESSIBILITY_FLOATING_MENU_UTILS_H_
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