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) {
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
AccessibilityController::AccessibilityController(
......@@ -703,17 +715,15 @@ void AccessibilityController::SetCaretBounds(
accessibility_highlight_controller_->SetCaretBounds(bounds_in_screen);
}
void AccessibilityController::SetAccessibilityPanelFullscreen(bool fullscreen) {
// 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 ChromeVox fullscreen updates.
AccessibilityPanelLayoutManager* layout =
static_cast<AccessibilityPanelLayoutManager*>(
container->layout_manager());
layout->SetPanelFullscreen(fullscreen);
void AccessibilityController::SetAccessibilityPanelAlwaysVisible(
bool always_visible) {
GetLayoutManager()->SetAlwaysVisible(always_visible);
}
void AccessibilityController::SetAccessibilityPanelBounds(
const gfx::Rect& bounds,
mojom::AccessibilityPanelState state) {
GetLayoutManager()->SetPanelBounds(bounds, state);
}
void AccessibilityController::OnSigninScreenPrefServiceInitialized(
......
......@@ -163,7 +163,10 @@ class ASH_EXPORT AccessibilityController
void BrailleDisplayStateChanged(bool connected) override;
void SetFocusHighlightRect(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;
// SessionObserver:
......
......@@ -26,11 +26,20 @@ AccessibilityPanelLayoutManager::~AccessibilityPanelLayoutManager() {
display::Screen::GetScreen()->RemoveObserver(this);
}
void AccessibilityPanelLayoutManager::SetPanelFullscreen(bool fullscreen) {
panel_fullscreen_ = fullscreen;
void AccessibilityPanelLayoutManager::SetAlwaysVisible(bool always_visible) {
always_visible_ = always_visible;
UpdateWindowBounds();
}
void AccessibilityPanelLayoutManager::SetPanelBounds(
const gfx::Rect& bounds,
mojom::AccessibilityPanelState state) {
panel_bounds_ = bounds;
panel_state_ = state;
UpdateWindowBounds();
UpdateWorkArea();
}
void AccessibilityPanelLayoutManager::OnWindowAddedToLayout(
aura::Window* child) {
panel_window_ = child;
......@@ -90,29 +99,44 @@ void AccessibilityPanelLayoutManager::UpdateWindowBounds() {
RootWindowController* root_controller =
RootWindowController::ForWindow(root_window);
// By default the panel sits at the top of the screen.
DCHECK(panel_window_->bounds().origin().IsOrigin());
gfx::Rect bounds(0, 0, root_window->bounds().width(), kPanelHeight);
gfx::Rect bounds = panel_bounds_;
// The panel can make itself fill the screen (including covering the shelf).
if (panel_fullscreen_)
bounds.set_height(root_window->bounds().height());
if (panel_state_ == mojom::AccessibilityPanelState::FULLSCREEN) {
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
// unless it's active.
if (root_controller->GetWindowForFullscreenMode() &&
// unless it's active or always_visible_ is true.
if (!always_visible_ && root_controller->GetWindowForFullscreenMode() &&
!::wm::IsActiveWindow(panel_window_)) {
bounds.set_height(0);
}
// Make sure the ChromeVox panel is always below the Docked Magnifier viewport
// so it shows up and gets magnified.
bounds.Offset(0, root_controller->shelf()->GetDockedMagnifierHeight());
// Make sure the accessibility panel is always below the Docked Magnifier
// viewport so it shows up and gets magnified.
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);
}
void AccessibilityPanelLayoutManager::UpdateWorkArea() {
if (panel_window_ && panel_window_->bounds().y() != 0)
return;
if (panel_state_ == mojom::AccessibilityPanelState::FULLSCREEN)
return;
Shell::GetPrimaryRootWindowController()->shelf()->SetAccessibilityPanelHeight(
panel_window_ ? panel_window_->bounds().height() : 0);
}
......
......@@ -6,10 +6,12 @@
#define ASH_ACCESSIBILITY_ACCESSIBILITY_PANEL_LAYOUT_MANAGER_H_
#include "ash/ash_export.h"
#include "ash/public/interfaces/accessibility_controller.mojom.h"
#include "ash/shell_observer.h"
#include "base/macros.h"
#include "ui/aura/layout_manager.h"
#include "ui/display/display_observer.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/wm/public/activation_change_observer.h"
namespace aura {
......@@ -30,13 +32,15 @@ class ASH_EXPORT AccessibilityPanelLayoutManager
public ash::ShellObserver {
public:
// Height of the panel in DIPs. Public for test.
static constexpr int kPanelHeight = 35;
static constexpr int kDefaultPanelHeight = 35;
AccessibilityPanelLayoutManager();
~AccessibilityPanelLayoutManager() override;
// Sets whether the panel covers the entire display.
void SetPanelFullscreen(bool fullscreen);
// Controls the panel's visibility and location.
void SetAlwaysVisible(bool always_visible);
void SetPanelBounds(const gfx::Rect& bounds,
mojom::AccessibilityPanelState state);
// aura::LayoutManager:
void OnWindowResized() override {}
......@@ -75,8 +79,14 @@ class ASH_EXPORT AccessibilityPanelLayoutManager
// The panel being managed (e.g. the ChromeVoxPanel's native aura window).
aura::Window* panel_window_ = nullptr;
// Whether the panel itself is filling the display.
bool panel_fullscreen_ = false;
// Window bounds when not in fullscreen
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);
};
......
......@@ -17,7 +17,8 @@ namespace ash {
namespace {
// Shorten the name for better line wrapping.
constexpr int kPanelHeight = AccessibilityPanelLayoutManager::kPanelHeight;
constexpr int kDefaultPanelHeight =
AccessibilityPanelLayoutManager::kDefaultPanelHeight;
AccessibilityPanelLayoutManager* GetLayoutManager() {
aura::Window* container =
......@@ -74,27 +75,6 @@ TEST_F(AccessibilityPanelLayoutManagerTest, 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) {
AccessibilityPanelLayoutManager* layout_manager = GetLayoutManager();
display::Screen* screen = display::Screen::GetScreen();
......@@ -102,38 +82,56 @@ TEST_F(AccessibilityPanelLayoutManagerTest, PanelFullscreen) {
std::unique_ptr<views::Widget> widget = CreateChromeVoxPanel();
widget->Show();
layout_manager->SetPanelBounds(gfx::Rect(0, 0, 0, kDefaultPanelHeight),
mojom::AccessibilityPanelState::FULL_WIDTH);
gfx::Rect expected_work_area = screen->GetPrimaryDisplay().work_area();
// When the panel is fullscreen it fills the display and does not change the
// work area.
layout_manager->SetPanelFullscreen(true);
layout_manager->SetPanelBounds(gfx::Rect(),
mojom::AccessibilityPanelState::FULLSCREEN);
EXPECT_EQ(widget->GetNativeWindow()->bounds(),
screen->GetPrimaryDisplay().bounds());
EXPECT_EQ(screen->GetPrimaryDisplay().work_area(), expected_work_area);
// Restoring the panel to default size restores the bounds and does not change
// 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(),
kPanelHeight);
kDefaultPanelHeight);
EXPECT_EQ(widget->GetNativeWindow()->bounds(), expected_bounds);
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) {
std::unique_ptr<views::Widget> widget = CreateChromeVoxPanel();
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
// screen.
UpdateDisplay("1234,567");
display::Screen* screen = display::Screen::GetScreen();
gfx::Rect expected_bounds(0, 0, screen->GetPrimaryDisplay().bounds().width(),
kPanelHeight);
kDefaultPanelHeight);
EXPECT_EQ(widget->GetNativeWindow()->bounds(), expected_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);
}
......
......@@ -36,6 +36,17 @@ enum AccessibilityAlert {
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 {
// Select to Speak is not actively selecting text or speaking.
kSelectToSpeakStateInactive,
......@@ -70,9 +81,14 @@ interface AccessibilityController {
// Setting off-screen or empty bounds suppresses the highlight.
SetCaretBounds(gfx.mojom.Rect bounds_in_screen);
// Sets whether the accessibility panel is filling the entire screen (e.g. to
// show the expanded UI for the ChromeVox spoken feedback extension).
SetAccessibilityPanelFullscreen(bool fullscreen);
// Sets whether the accessibility panel should always be visible, regardless
// of whether the window is 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-
// to-Speak extension to inform ash of its updated state.
......
......@@ -18,6 +18,16 @@ const char kDisableSpokenFeedbackURLFragment[] = "close";
const char kFocusURLFragment[] = "focus";
const char kFullscreenURLFragment[] = "fullscreen";
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
......@@ -54,6 +64,8 @@ ChromeVoxPanel::ChromeVoxPanel(content::BrowserContext* browser_context)
: AccessibilityPanel(browser_context, GetUrlForContent(), kWidgetName) {
web_contents_observer_.reset(
new ChromeVoxPanelWebContentsObserver(GetWebContents(), this));
SetAccessibilityPanelFullscreen(false);
}
ChromeVoxPanel::~ChromeVoxPanel() {}
......@@ -76,12 +88,10 @@ void ChromeVoxPanel::Focus() {
}
void ChromeVoxPanel::SetAccessibilityPanelFullscreen(bool fullscreen) {
// Connect to the accessibility mojo interface in ash.
ash::mojom::AccessibilityControllerPtr accessibility_controller;
content::ServiceManagerConnection::GetForProcess()
->GetConnector()
->BindInterface(ash::mojom::kServiceName, &accessibility_controller);
accessibility_controller->SetAccessibilityPanelFullscreen(fullscreen);
gfx::Rect bounds(0, 0, 0, kPanelHeight);
auto state = fullscreen ? ash::mojom::AccessibilityPanelState::FULLSCREEN
: ash::mojom::AccessibilityPanelState::FULL_WIDTH;
GetAccessibilityController()->SetAccessibilityPanelBounds(bounds, state);
}
std::string ChromeVoxPanel::GetUrlForContent() {
......
......@@ -38,7 +38,10 @@ class TestAccessibilityController : ash::mojom::AccessibilityController {
void BrailleDisplayStateChanged(bool connected) override {}
void SetFocusHighlightRect(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 {}
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