Commit a011e05f authored by Zach Helfinstein's avatar Zach Helfinstein Committed by Commit Bot

Generalize AccessibilityPanel sizing

Modify AccessibilityPanel sizing to be more flexible, and work for
both the ChromeVox panel and the SwitchAccess context menu.

Also fixes a bug where ChromeVox Panel fullscreen goes offscreen when
Docked Magnifier is enabled.

Bug: 864796
Change-Id: Ib99150f68dcb332aa86a0cc72d7863d6e6b301f7
Reviewed-on: https://chromium-review.googlesource.com/1167886
Commit-Queue: Zach Helfinstein <zhelfins@chromium.org>
Reviewed-by: default avatarRobert Sesek <rsesek@chromium.org>
Reviewed-by: default avatarDavid Tseng <dtseng@chromium.org>
Cr-Commit-Position: refs/heads/master@{#583132}
parent c750293a
...@@ -225,6 +225,18 @@ void ShowAccessibilityNotification(A11yNotificationType type) { ...@@ -225,6 +225,18 @@ void ShowAccessibilityNotification(A11yNotificationType type) {
message_center->AddNotification(std::move(notification)); message_center->AddNotification(std::move(notification));
} }
AccessibilityPanelLayoutManager* GetLayoutManager() {
// The accessibility panel is only shown on the primary display.
aura::Window* root = Shell::GetPrimaryRootWindow();
aura::Window* container =
Shell::GetContainer(root, kShellWindowId_AccessibilityPanelContainer);
// TODO(jamescook): Avoid this cast by moving ash::AccessibilityObserver
// ownership to this class and notifying it on accessibility panel fullscreen
// updates.
return static_cast<AccessibilityPanelLayoutManager*>(
container->layout_manager());
}
} // namespace } // namespace
AccessibilityController::AccessibilityController( AccessibilityController::AccessibilityController(
...@@ -703,17 +715,15 @@ void AccessibilityController::SetCaretBounds( ...@@ -703,17 +715,15 @@ void AccessibilityController::SetCaretBounds(
accessibility_highlight_controller_->SetCaretBounds(bounds_in_screen); accessibility_highlight_controller_->SetCaretBounds(bounds_in_screen);
} }
void AccessibilityController::SetAccessibilityPanelFullscreen(bool fullscreen) { void AccessibilityController::SetAccessibilityPanelAlwaysVisible(
// The accessibility panel is only shown on the primary display. bool always_visible) {
aura::Window* root = Shell::GetPrimaryRootWindow(); GetLayoutManager()->SetAlwaysVisible(always_visible);
aura::Window* container = }
Shell::GetContainer(root, kShellWindowId_AccessibilityPanelContainer);
// TODO(jamescook): Avoid this cast by moving ash::AccessibilityObserver void AccessibilityController::SetAccessibilityPanelBounds(
// ownership to this class and notifying it on ChromeVox fullscreen updates. const gfx::Rect& bounds,
AccessibilityPanelLayoutManager* layout = mojom::AccessibilityPanelState state) {
static_cast<AccessibilityPanelLayoutManager*>( GetLayoutManager()->SetPanelBounds(bounds, state);
container->layout_manager());
layout->SetPanelFullscreen(fullscreen);
} }
void AccessibilityController::OnSigninScreenPrefServiceInitialized( void AccessibilityController::OnSigninScreenPrefServiceInitialized(
......
...@@ -163,7 +163,10 @@ class ASH_EXPORT AccessibilityController ...@@ -163,7 +163,10 @@ class ASH_EXPORT AccessibilityController
void BrailleDisplayStateChanged(bool connected) override; void BrailleDisplayStateChanged(bool connected) override;
void SetFocusHighlightRect(const gfx::Rect& bounds_in_screen) override; void SetFocusHighlightRect(const gfx::Rect& bounds_in_screen) override;
void SetCaretBounds(const gfx::Rect& bounds_in_screen) override; void SetCaretBounds(const gfx::Rect& bounds_in_screen) override;
void SetAccessibilityPanelFullscreen(bool fullscreen) override; void SetAccessibilityPanelAlwaysVisible(bool always_visible) override;
void SetAccessibilityPanelBounds(
const gfx::Rect& bounds,
mojom::AccessibilityPanelState state) override;
void SetSelectToSpeakState(mojom::SelectToSpeakState state) override; void SetSelectToSpeakState(mojom::SelectToSpeakState state) override;
// SessionObserver: // SessionObserver:
......
...@@ -26,11 +26,20 @@ AccessibilityPanelLayoutManager::~AccessibilityPanelLayoutManager() { ...@@ -26,11 +26,20 @@ AccessibilityPanelLayoutManager::~AccessibilityPanelLayoutManager() {
display::Screen::GetScreen()->RemoveObserver(this); display::Screen::GetScreen()->RemoveObserver(this);
} }
void AccessibilityPanelLayoutManager::SetPanelFullscreen(bool fullscreen) { void AccessibilityPanelLayoutManager::SetAlwaysVisible(bool always_visible) {
panel_fullscreen_ = fullscreen; always_visible_ = always_visible;
UpdateWindowBounds(); UpdateWindowBounds();
} }
void AccessibilityPanelLayoutManager::SetPanelBounds(
const gfx::Rect& bounds,
mojom::AccessibilityPanelState state) {
panel_bounds_ = bounds;
panel_state_ = state;
UpdateWindowBounds();
UpdateWorkArea();
}
void AccessibilityPanelLayoutManager::OnWindowAddedToLayout( void AccessibilityPanelLayoutManager::OnWindowAddedToLayout(
aura::Window* child) { aura::Window* child) {
panel_window_ = child; panel_window_ = child;
...@@ -90,29 +99,44 @@ void AccessibilityPanelLayoutManager::UpdateWindowBounds() { ...@@ -90,29 +99,44 @@ void AccessibilityPanelLayoutManager::UpdateWindowBounds() {
RootWindowController* root_controller = RootWindowController* root_controller =
RootWindowController::ForWindow(root_window); RootWindowController::ForWindow(root_window);
// By default the panel sits at the top of the screen. gfx::Rect bounds = panel_bounds_;
DCHECK(panel_window_->bounds().origin().IsOrigin());
gfx::Rect bounds(0, 0, root_window->bounds().width(), kPanelHeight);
// The panel can make itself fill the screen (including covering the shelf). // The panel can make itself fill the screen (including covering the shelf).
if (panel_fullscreen_) if (panel_state_ == mojom::AccessibilityPanelState::FULLSCREEN) {
bounds.set_height(root_window->bounds().height()); bounds = root_window->bounds();
} else if (panel_state_ == mojom::AccessibilityPanelState::FULL_WIDTH) {
bounds.set_x(0);
bounds.set_width(root_window->bounds().width());
}
// If a fullscreen browser window is open, give the panel a height of 0 // If a fullscreen browser window is open, give the panel a height of 0
// unless it's active. // unless it's active or always_visible_ is true.
if (root_controller->GetWindowForFullscreenMode() && if (!always_visible_ && root_controller->GetWindowForFullscreenMode() &&
!::wm::IsActiveWindow(panel_window_)) { !::wm::IsActiveWindow(panel_window_)) {
bounds.set_height(0); bounds.set_height(0);
} }
// Make sure the ChromeVox panel is always below the Docked Magnifier viewport // Make sure the accessibility panel is always below the Docked Magnifier
// so it shows up and gets magnified. // viewport so it shows up and gets magnified.
bounds.Offset(0, root_controller->shelf()->GetDockedMagnifierHeight()); int magnifier_height = root_controller->shelf()->GetDockedMagnifierHeight();
if (bounds.y() < magnifier_height)
bounds.Offset(0, magnifier_height);
// Make sure the accessibility panel doesn't go offscreen when the Docked
// Magnifier is on.
int screen_height = root_window->bounds().height();
int available_height = screen_height - magnifier_height;
if (bounds.height() > available_height)
bounds.set_height(available_height);
panel_window_->SetBounds(bounds); panel_window_->SetBounds(bounds);
} }
void AccessibilityPanelLayoutManager::UpdateWorkArea() { void AccessibilityPanelLayoutManager::UpdateWorkArea() {
if (panel_window_ && panel_window_->bounds().y() != 0)
return;
if (panel_state_ == mojom::AccessibilityPanelState::FULLSCREEN)
return;
Shell::GetPrimaryRootWindowController()->shelf()->SetAccessibilityPanelHeight( Shell::GetPrimaryRootWindowController()->shelf()->SetAccessibilityPanelHeight(
panel_window_ ? panel_window_->bounds().height() : 0); panel_window_ ? panel_window_->bounds().height() : 0);
} }
......
...@@ -6,10 +6,12 @@ ...@@ -6,10 +6,12 @@
#define ASH_ACCESSIBILITY_ACCESSIBILITY_PANEL_LAYOUT_MANAGER_H_ #define ASH_ACCESSIBILITY_ACCESSIBILITY_PANEL_LAYOUT_MANAGER_H_
#include "ash/ash_export.h" #include "ash/ash_export.h"
#include "ash/public/interfaces/accessibility_controller.mojom.h"
#include "ash/shell_observer.h" #include "ash/shell_observer.h"
#include "base/macros.h" #include "base/macros.h"
#include "ui/aura/layout_manager.h" #include "ui/aura/layout_manager.h"
#include "ui/display/display_observer.h" #include "ui/display/display_observer.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/wm/public/activation_change_observer.h" #include "ui/wm/public/activation_change_observer.h"
namespace aura { namespace aura {
...@@ -30,13 +32,15 @@ class ASH_EXPORT AccessibilityPanelLayoutManager ...@@ -30,13 +32,15 @@ class ASH_EXPORT AccessibilityPanelLayoutManager
public ash::ShellObserver { public ash::ShellObserver {
public: public:
// Height of the panel in DIPs. Public for test. // Height of the panel in DIPs. Public for test.
static constexpr int kPanelHeight = 35; static constexpr int kDefaultPanelHeight = 35;
AccessibilityPanelLayoutManager(); AccessibilityPanelLayoutManager();
~AccessibilityPanelLayoutManager() override; ~AccessibilityPanelLayoutManager() override;
// Sets whether the panel covers the entire display. // Controls the panel's visibility and location.
void SetPanelFullscreen(bool fullscreen); void SetAlwaysVisible(bool always_visible);
void SetPanelBounds(const gfx::Rect& bounds,
mojom::AccessibilityPanelState state);
// aura::LayoutManager: // aura::LayoutManager:
void OnWindowResized() override {} void OnWindowResized() override {}
...@@ -75,8 +79,14 @@ class ASH_EXPORT AccessibilityPanelLayoutManager ...@@ -75,8 +79,14 @@ class ASH_EXPORT AccessibilityPanelLayoutManager
// The panel being managed (e.g. the ChromeVoxPanel's native aura window). // The panel being managed (e.g. the ChromeVoxPanel's native aura window).
aura::Window* panel_window_ = nullptr; aura::Window* panel_window_ = nullptr;
// Whether the panel itself is filling the display. // Window bounds when not in fullscreen
bool panel_fullscreen_ = false; gfx::Rect panel_bounds_ = gfx::Rect(0, 0, 0, 0);
// Determines whether panel is hidden when browser is in fullscreen.
bool always_visible_ = false;
// Determines how the panel_bounds_ are used when displaying the panel.
mojom::AccessibilityPanelState panel_state_;
DISALLOW_COPY_AND_ASSIGN(AccessibilityPanelLayoutManager); DISALLOW_COPY_AND_ASSIGN(AccessibilityPanelLayoutManager);
}; };
......
...@@ -17,7 +17,8 @@ namespace ash { ...@@ -17,7 +17,8 @@ namespace ash {
namespace { namespace {
// Shorten the name for better line wrapping. // Shorten the name for better line wrapping.
constexpr int kPanelHeight = AccessibilityPanelLayoutManager::kPanelHeight; constexpr int kDefaultPanelHeight =
AccessibilityPanelLayoutManager::kDefaultPanelHeight;
AccessibilityPanelLayoutManager* GetLayoutManager() { AccessibilityPanelLayoutManager* GetLayoutManager() {
aura::Window* container = aura::Window* container =
...@@ -74,27 +75,6 @@ TEST_F(AccessibilityPanelLayoutManagerTest, Shutdown) { ...@@ -74,27 +75,6 @@ TEST_F(AccessibilityPanelLayoutManagerTest, Shutdown) {
// Ash should not crash if the window is still open at shutdown. // Ash should not crash if the window is still open at shutdown.
} }
TEST_F(AccessibilityPanelLayoutManagerTest, InitialBounds) {
display::Screen* screen = display::Screen::GetScreen();
gfx::Rect initial_work_area = screen->GetPrimaryDisplay().work_area();
// Simulate Chrome creating the ChromeVox window, but don't show it yet.
std::unique_ptr<views::Widget> widget = CreateChromeVoxPanel();
// The layout manager has not adjusted the work area yet.
EXPECT_EQ(screen->GetPrimaryDisplay().work_area(), initial_work_area);
// Showing the panel causes the layout manager to adjust the panel bounds and
// the display work area.
widget->Show();
gfx::Rect expected_bounds(0, 0, screen->GetPrimaryDisplay().bounds().width(),
kPanelHeight);
EXPECT_EQ(widget->GetNativeWindow()->bounds(), expected_bounds);
gfx::Rect expected_work_area = initial_work_area;
expected_work_area.Inset(0, kPanelHeight, 0, 0);
EXPECT_EQ(screen->GetPrimaryDisplay().work_area(), expected_work_area);
}
TEST_F(AccessibilityPanelLayoutManagerTest, PanelFullscreen) { TEST_F(AccessibilityPanelLayoutManagerTest, PanelFullscreen) {
AccessibilityPanelLayoutManager* layout_manager = GetLayoutManager(); AccessibilityPanelLayoutManager* layout_manager = GetLayoutManager();
display::Screen* screen = display::Screen::GetScreen(); display::Screen* screen = display::Screen::GetScreen();
...@@ -102,38 +82,56 @@ TEST_F(AccessibilityPanelLayoutManagerTest, PanelFullscreen) { ...@@ -102,38 +82,56 @@ TEST_F(AccessibilityPanelLayoutManagerTest, PanelFullscreen) {
std::unique_ptr<views::Widget> widget = CreateChromeVoxPanel(); std::unique_ptr<views::Widget> widget = CreateChromeVoxPanel();
widget->Show(); widget->Show();
layout_manager->SetPanelBounds(gfx::Rect(0, 0, 0, kDefaultPanelHeight),
mojom::AccessibilityPanelState::FULL_WIDTH);
gfx::Rect expected_work_area = screen->GetPrimaryDisplay().work_area(); gfx::Rect expected_work_area = screen->GetPrimaryDisplay().work_area();
// When the panel is fullscreen it fills the display and does not change the // When the panel is fullscreen it fills the display and does not change the
// work area. // work area.
layout_manager->SetPanelFullscreen(true); layout_manager->SetPanelBounds(gfx::Rect(),
mojom::AccessibilityPanelState::FULLSCREEN);
EXPECT_EQ(widget->GetNativeWindow()->bounds(), EXPECT_EQ(widget->GetNativeWindow()->bounds(),
screen->GetPrimaryDisplay().bounds()); screen->GetPrimaryDisplay().bounds());
EXPECT_EQ(screen->GetPrimaryDisplay().work_area(), expected_work_area); EXPECT_EQ(screen->GetPrimaryDisplay().work_area(), expected_work_area);
// Restoring the panel to default size restores the bounds and does not change // Restoring the panel to default size restores the bounds and does not change
// the work area. // the work area.
layout_manager->SetPanelFullscreen(false); layout_manager->SetPanelBounds(gfx::Rect(0, 0, 0, kDefaultPanelHeight),
mojom::AccessibilityPanelState::FULL_WIDTH);
gfx::Rect expected_bounds(0, 0, screen->GetPrimaryDisplay().bounds().width(), gfx::Rect expected_bounds(0, 0, screen->GetPrimaryDisplay().bounds().width(),
kPanelHeight); kDefaultPanelHeight);
EXPECT_EQ(widget->GetNativeWindow()->bounds(), expected_bounds); EXPECT_EQ(widget->GetNativeWindow()->bounds(), expected_bounds);
EXPECT_EQ(screen->GetPrimaryDisplay().work_area(), expected_work_area); EXPECT_EQ(screen->GetPrimaryDisplay().work_area(), expected_work_area);
} }
TEST_F(AccessibilityPanelLayoutManagerTest, SetBounds) {
std::unique_ptr<views::Widget> widget = CreateChromeVoxPanel();
widget->Show();
gfx::Rect bounds(0, 0, 100, 100);
GetLayoutManager()->SetPanelBounds(bounds,
mojom::AccessibilityPanelState::BOUNDED);
EXPECT_EQ(widget->GetNativeWindow()->bounds(), bounds);
}
TEST_F(AccessibilityPanelLayoutManagerTest, DisplayBoundsChange) { TEST_F(AccessibilityPanelLayoutManagerTest, DisplayBoundsChange) {
std::unique_ptr<views::Widget> widget = CreateChromeVoxPanel(); std::unique_ptr<views::Widget> widget = CreateChromeVoxPanel();
widget->Show(); widget->Show();
GetLayoutManager()->SetPanelBounds(
gfx::Rect(0, 0, 0, kDefaultPanelHeight),
mojom::AccessibilityPanelState::FULL_WIDTH);
// When the display resolution changes the panel still sits at the top of the // When the display resolution changes the panel still sits at the top of the
// screen. // screen.
UpdateDisplay("1234,567"); UpdateDisplay("1234,567");
display::Screen* screen = display::Screen::GetScreen(); display::Screen* screen = display::Screen::GetScreen();
gfx::Rect expected_bounds(0, 0, screen->GetPrimaryDisplay().bounds().width(), gfx::Rect expected_bounds(0, 0, screen->GetPrimaryDisplay().bounds().width(),
kPanelHeight); kDefaultPanelHeight);
EXPECT_EQ(widget->GetNativeWindow()->bounds(), expected_bounds); EXPECT_EQ(widget->GetNativeWindow()->bounds(), expected_bounds);
gfx::Rect expected_work_area = screen->GetPrimaryDisplay().bounds(); gfx::Rect expected_work_area = screen->GetPrimaryDisplay().bounds();
expected_work_area.Inset(0, kPanelHeight, 0, kShelfSize); expected_work_area.Inset(0, kDefaultPanelHeight, 0, kShelfSize);
EXPECT_EQ(screen->GetPrimaryDisplay().work_area(), expected_work_area); EXPECT_EQ(screen->GetPrimaryDisplay().work_area(), expected_work_area);
} }
......
...@@ -36,6 +36,17 @@ enum AccessibilityAlert { ...@@ -36,6 +36,17 @@ enum AccessibilityAlert {
WINDOW_OVERVIEW_MODE_ENTERED WINDOW_OVERVIEW_MODE_ENTERED
}; };
enum AccessibilityPanelState {
// Window bounds are set explicitly.
BOUNDED,
// Width of panel matches screen width, y_coord and height are set by bounds.
FULL_WIDTH,
// Panel occupies the full screen. Bounds are ignored.
FULLSCREEN
};
enum SelectToSpeakState { enum SelectToSpeakState {
// Select to Speak is not actively selecting text or speaking. // Select to Speak is not actively selecting text or speaking.
kSelectToSpeakStateInactive, kSelectToSpeakStateInactive,
...@@ -70,9 +81,14 @@ interface AccessibilityController { ...@@ -70,9 +81,14 @@ interface AccessibilityController {
// Setting off-screen or empty bounds suppresses the highlight. // Setting off-screen or empty bounds suppresses the highlight.
SetCaretBounds(gfx.mojom.Rect bounds_in_screen); SetCaretBounds(gfx.mojom.Rect bounds_in_screen);
// Sets whether the accessibility panel is filling the entire screen (e.g. to // Sets whether the accessibility panel should always be visible, regardless
// show the expanded UI for the ChromeVox spoken feedback extension). // of whether the window is fullscreen.
SetAccessibilityPanelFullscreen(bool fullscreen); SetAccessibilityPanelAlwaysVisible(bool always_visible);
// Sets the bounds for the accessibility panel. Overrides current
// configuration (i.e. fullscreen, full-width).
SetAccessibilityPanelBounds(gfx.mojom.Rect bounds,
AccessibilityPanelState state);
// Sets the current Select-to-Speak state. This should be used by the Select- // Sets the current Select-to-Speak state. This should be used by the Select-
// to-Speak extension to inform ash of its updated state. // to-Speak extension to inform ash of its updated state.
......
...@@ -18,6 +18,16 @@ const char kDisableSpokenFeedbackURLFragment[] = "close"; ...@@ -18,6 +18,16 @@ const char kDisableSpokenFeedbackURLFragment[] = "close";
const char kFocusURLFragment[] = "focus"; const char kFocusURLFragment[] = "focus";
const char kFullscreenURLFragment[] = "fullscreen"; const char kFullscreenURLFragment[] = "fullscreen";
const char kWidgetName[] = "ChromeVoxPanel"; const char kWidgetName[] = "ChromeVoxPanel";
const int kPanelHeight = 35;
ash::mojom::AccessibilityControllerPtr GetAccessibilityController() {
// Connect to the accessibility mojo interface in ash.
ash::mojom::AccessibilityControllerPtr accessibility_controller;
content::ServiceManagerConnection::GetForProcess()
->GetConnector()
->BindInterface(ash::mojom::kServiceName, &accessibility_controller);
return accessibility_controller;
}
} // namespace } // namespace
...@@ -54,6 +64,8 @@ ChromeVoxPanel::ChromeVoxPanel(content::BrowserContext* browser_context) ...@@ -54,6 +64,8 @@ ChromeVoxPanel::ChromeVoxPanel(content::BrowserContext* browser_context)
: AccessibilityPanel(browser_context, GetUrlForContent(), kWidgetName) { : AccessibilityPanel(browser_context, GetUrlForContent(), kWidgetName) {
web_contents_observer_.reset( web_contents_observer_.reset(
new ChromeVoxPanelWebContentsObserver(GetWebContents(), this)); new ChromeVoxPanelWebContentsObserver(GetWebContents(), this));
SetAccessibilityPanelFullscreen(false);
} }
ChromeVoxPanel::~ChromeVoxPanel() {} ChromeVoxPanel::~ChromeVoxPanel() {}
...@@ -76,12 +88,10 @@ void ChromeVoxPanel::Focus() { ...@@ -76,12 +88,10 @@ void ChromeVoxPanel::Focus() {
} }
void ChromeVoxPanel::SetAccessibilityPanelFullscreen(bool fullscreen) { void ChromeVoxPanel::SetAccessibilityPanelFullscreen(bool fullscreen) {
// Connect to the accessibility mojo interface in ash. gfx::Rect bounds(0, 0, 0, kPanelHeight);
ash::mojom::AccessibilityControllerPtr accessibility_controller; auto state = fullscreen ? ash::mojom::AccessibilityPanelState::FULLSCREEN
content::ServiceManagerConnection::GetForProcess() : ash::mojom::AccessibilityPanelState::FULL_WIDTH;
->GetConnector() GetAccessibilityController()->SetAccessibilityPanelBounds(bounds, state);
->BindInterface(ash::mojom::kServiceName, &accessibility_controller);
accessibility_controller->SetAccessibilityPanelFullscreen(fullscreen);
} }
std::string ChromeVoxPanel::GetUrlForContent() { std::string ChromeVoxPanel::GetUrlForContent() {
......
...@@ -38,7 +38,10 @@ class TestAccessibilityController : ash::mojom::AccessibilityController { ...@@ -38,7 +38,10 @@ class TestAccessibilityController : ash::mojom::AccessibilityController {
void BrailleDisplayStateChanged(bool connected) override {} void BrailleDisplayStateChanged(bool connected) override {}
void SetFocusHighlightRect(const gfx::Rect& bounds_in_screen) override {} void SetFocusHighlightRect(const gfx::Rect& bounds_in_screen) override {}
void SetCaretBounds(const gfx::Rect& bounds_in_screen) override {} void SetCaretBounds(const gfx::Rect& bounds_in_screen) override {}
void SetAccessibilityPanelFullscreen(bool fullscreen) override {} void SetAccessibilityPanelAlwaysVisible(bool always_visible) override {}
void SetAccessibilityPanelBounds(
const gfx::Rect& bounds,
ash::mojom::AccessibilityPanelState state) override {}
void SetSelectToSpeakState(ash::mojom::SelectToSpeakState state) override {} void SetSelectToSpeakState(ash::mojom::SelectToSpeakState state) override {}
bool was_client_set() const { return was_client_set_; } bool was_client_set() const { return was_client_set_; }
......
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