Commit d1e1df54 authored by Anastasia Helfinstein's avatar Anastasia Helfinstein Committed by Commit Bot

[Switch Access] Add a BubbleController

Currently, the Switch Access back button is drawn by the extension, but
in order to match the visual specification (see bug) the back button
needs to be drawn using native views.

This change creates a bubble controller for Switch Access and adds the
ability to show the back button. This functionality is not yet used.

Relnotes: N/A
Bug: 973719
Change-Id: I6da3772e8183d0cdbd04b554a4764719d12b8e25
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2124234
Commit-Queue: Anastasia Helfinstein <anastasi@google.com>
Reviewed-by: default avatarMitsuru Oshima <oshima@chromium.org>
Reviewed-by: default avatarKatie Dektar <katie@chromium.org>
Reviewed-by: default avatarAbigail Klein <abigailbklein@google.com>
Cr-Commit-Position: refs/heads/master@{#756364}
parent 83da2bae
......@@ -736,6 +736,10 @@ component("ash") {
"system/accessibility/floating_menu_button.h",
"system/accessibility/select_to_speak_tray.cc",
"system/accessibility/select_to_speak_tray.h",
"system/accessibility/switch_access_back_button_view.cc",
"system/accessibility/switch_access_back_button_view.h",
"system/accessibility/switch_access_bubble_controller.cc",
"system/accessibility/switch_access_bubble_controller.h",
"system/accessibility/tray_accessibility.cc",
"system/accessibility/tray_accessibility.h",
"system/accessibility/unified_accessibility_detailed_view_controller.cc",
......@@ -1949,6 +1953,7 @@ test("ash_unittests") {
"system/accessibility/autoclick_menu_bubble_controller_unittest.cc",
"system/accessibility/dictation_button_tray_unittest.cc",
"system/accessibility/select_to_speak_tray_unittest.cc",
"system/accessibility/switch_access_bubble_controller_unittest.cc",
"system/accessibility/tray_accessibility_unittest.cc",
"system/bluetooth/bluetooth_notification_controller_unittest.cc",
"system/bluetooth/bluetooth_power_controller_unittest.cc",
......
......@@ -32,6 +32,7 @@
#include "ash/sticky_keys/sticky_keys_controller.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/system/accessibility/accessibility_feature_disable_dialog.h"
#include "ash/system/accessibility/switch_access_bubble_controller.h"
#include "ash/system/power/backlights_forced_off_setter.h"
#include "ash/system/power/power_status.h"
#include "ash/system/power/scoped_backlights_forced_off.h"
......@@ -1659,6 +1660,7 @@ void AccessibilityControllerImpl::SwitchAccessDisableDialogClosed(
if (disable_dialog_accepted) {
// The pref was already disabled, but we left switch access on until they
// accepted the dialog.
switch_access_bubble_controller_.reset();
switch_access_event_handler_.reset();
NotifyAccessibilityStatusChanged();
} else {
......@@ -1812,7 +1814,7 @@ void AccessibilityControllerImpl::UpdateFeatureFromPref(FeatureType feature) {
ShowAccessibilityNotification(A11yNotificationType::kNone);
if (no_switch_access_disable_confirmation_dialog_for_testing_) {
switch_access_event_handler_.reset();
SwitchAccessDisableDialogClosed(true);
} else {
new AccessibilityFeatureDisableDialog(
IDS_ASH_SWITCH_ACCESS_DISABLE_CONFIRMATION_TITLE,
......@@ -1823,9 +1825,11 @@ void AccessibilityControllerImpl::UpdateFeatureFromPref(FeatureType feature) {
base::BindOnce(
&AccessibilityControllerImpl::SwitchAccessDisableDialogClosed,
weak_ptr_factory_.GetWeakPtr(), false));
return;
}
return;
} else {
switch_access_bubble_controller_ =
std::make_unique<SwitchAccessBubbleController>();
MaybeCreateSwitchAccessEventHandler();
ShowAccessibilityNotification(
A11yNotificationType::kSwitchAccessEnabled);
......
......@@ -38,6 +38,7 @@ class AccessibilityHighlightController;
class AccessibilityObserver;
class ScopedBacklightsForcedOff;
class SelectToSpeakEventHandler;
class SwitchAccessBubbleController;
class SwitchAccessEventHandler;
enum AccessibilityNotificationVisibility {
......@@ -382,6 +383,9 @@ class ASH_EXPORT AccessibilityControllerImpl : public AccessibilityController,
// Test helpers:
SwitchAccessEventHandler* GetSwitchAccessEventHandlerForTest();
SwitchAccessBubbleController* GetSwitchAccessBubbleControllerForTest() {
return switch_access_bubble_controller_.get();
}
void no_switch_access_disable_confirmation_dialog_for_testing(
bool skip_dialog) {
no_switch_access_disable_confirmation_dialog_for_testing_ = skip_dialog;
......@@ -444,6 +448,8 @@ class ASH_EXPORT AccessibilityControllerImpl : public AccessibilityController,
// List of key codes that Switch Access should capture.
std::vector<int> switch_access_keys_to_capture_;
std::unique_ptr<SwitchAccessBubbleController>
switch_access_bubble_controller_;
std::unique_ptr<SwitchAccessEventHandler> switch_access_event_handler_;
SwitchAccessEventHandlerDelegate* switch_access_event_handler_delegate_ =
nullptr;
......
......@@ -676,6 +676,9 @@ This file contains the strings for ash.
<message name="IDS_ASH_AUTOCLICK_DISABLE_CONFIRMATION_BODY" desc="The message in the modal dialog shown when the user disables automatic clicks, to confirm they meant to disable the feature">
Are you sure you want to turn off automatic clicks?
</message>
<message name="IDS_ASH_SWITCH_ACCESS_BACK_BUTTON_DESCRIPTION" desc="The tooltip text for the Switch Access back button. The back button allows users to exit the feature's current focus area.">
Back button
</message>
<message name="IDS_ASH_STATUS_TRAY_ACCESSIBILITY_VIRTUAL_KEYBOARD" desc="The label used in the accessibility menu of the system tray to toggle on/off the onscreen keyboard.">
On-screen keyboard
</message>
......
f2a93c0d02d7dd47a5379b2b7afb9bc8450350ec
\ No newline at end of file
......@@ -146,6 +146,7 @@ aggregate_vector_icons("ash_vector_icons") {
"shelf_sign_out_button.icon",
"shelf_unlock_button.icon",
"switch_access.icon",
"switch_access_back.icon",
"system_menu_accessibility.icon",
"system_menu_accessibility_auto_click.icon",
"system_menu_accessibility_chromevox.icon",
......
// 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.
CANVAS_DIMENSIONS, 20,
MOVE_TO, 4.17f, 7.5f,
LINE_TO, 9.17f, 2.5f,
LINE_TO, 10.35f, 3.68f,
LINE_TO, 7.36f, 6.67f,
LINE_TO, 16.67f, 6.67f,
LINE_TO, 16.67f, 16.67f,
LINE_TO, 15, 16.67f,
LINE_TO, 15, 8.33f,
LINE_TO, 7.36f, 8.33f,
LINE_TO, 10.35f, 11.32f,
LINE_TO, 9.17f, 12.5f,
CLOSE,
MOVE_TO, 4.17f, 7.5f,
CLOSE
// 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/switch_access_back_button_view.h"
#include "ash/resources/vector_icons/vector_icons.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/system/accessibility/floating_menu_button.h"
#include "ash/system/tray/tray_constants.h"
#include "cc/paint/paint_flags.h"
#include "ui/events/event.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/color_palette.h"
#include "ui/gfx/geometry/point_f.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/views/layout/box_layout.h"
namespace ash {
SwitchAccessBackButtonView::SwitchAccessBackButtonView(int diameter)
: diameter_(diameter),
back_button_(
new FloatingMenuButton(this,
kSwitchAccessBackIcon,
IDS_ASH_SWITCH_ACCESS_BACK_BUTTON_DESCRIPTION,
/*flip_for_rtl=*/false,
diameter)) {
std::unique_ptr<views::BoxLayout> layout = std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kHorizontal, gfx::Insets());
SetLayoutManager(std::move(layout));
AddChildView(back_button_);
}
void SwitchAccessBackButtonView::ButtonPressed(views::Button* sender,
const ui::Event& event) {
// This code should not be called presently, as the button is never shown.
// TODO(crbug/973719): Transition to using new back button and menu which
// will be implemented using views/.
LOG(WARNING) << "Implementation of back button pressed not finished.";
}
const char* SwitchAccessBackButtonView::GetClassName() const {
return "SwitchAccessBackButtonView";
}
void SwitchAccessBackButtonView::OnPaint(gfx::Canvas* canvas) {
gfx::Rect rect(GetContentsBounds());
cc::PaintFlags flags;
flags.setAntiAlias(true);
flags.setColor(gfx::kGoogleGrey800);
flags.setStyle(cc::PaintFlags::kFill_Style);
canvas->DrawCircle(gfx::PointF(rect.CenterPoint()), diameter_ / 2.f, flags);
}
} // 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_SWITCH_ACCESS_BACK_BUTTON_VIEW_H_
#define ASH_SYSTEM_ACCESSIBILITY_SWITCH_ACCESS_BACK_BUTTON_VIEW_H_
#include "ui/views/controls/button/button.h"
#include "ui/views/view.h"
namespace ash {
class FloatingMenuButton;
// View for the Switch Access Back Button.
class SwitchAccessBackButtonView : public views::View,
public views::ButtonListener {
public:
explicit SwitchAccessBackButtonView(int button_size);
~SwitchAccessBackButtonView() override = default;
SwitchAccessBackButtonView(const SwitchAccessBackButtonView&) = delete;
SwitchAccessBackButtonView& operator=(const SwitchAccessBackButtonView&) =
delete;
// views::ButtonListener:
void ButtonPressed(views::Button* sender, const ui::Event& event) override;
// views::View:
const char* GetClassName() const override;
void OnPaint(gfx::Canvas* canvas) override;
private:
int diameter_;
// Owned by views hierarchy.
FloatingMenuButton* back_button_;
};
} // namespace ash
#endif // ASH_SYSTEM_ACCESSIBILITY_SWITCH_ACCESS_BACK_BUTTON_VIEW_H_
// 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/switch_access_bubble_controller.h"
#include "ash/public/cpp/shell_window_ids.h"
#include "ash/shell.h"
#include "ash/system/accessibility/switch_access_back_button_view.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"
namespace ash {
namespace {
constexpr int kBackButtonRadiusDip = 18;
constexpr int kBackButtonDiameterDip = 2 * kBackButtonRadiusDip;
} // namespace
SwitchAccessBubbleController::SwitchAccessBubbleController() {}
SwitchAccessBubbleController::~SwitchAccessBubbleController() {
if (back_button_widget_ && !back_button_widget_->IsClosed())
back_button_widget_->CloseNow();
}
void SwitchAccessBubbleController::ShowBackButton(const gfx::Rect& anchor) {
if (back_button_widget_) {
DCHECK(back_button_bubble_view_);
back_button_bubble_view_->ChangeAnchorRect(anchor);
return;
}
TrayBubbleView::InitParams init_params;
init_params.delegate = this;
// Anchor within the overlay container.
init_params.parent_window = Shell::GetContainer(
Shell::GetPrimaryRootWindow(), kShellWindowId_OverlayContainer);
init_params.anchor_mode = TrayBubbleView::AnchorMode::kRect;
init_params.anchor_rect = anchor;
init_params.has_shadow = false;
// The back button is a circle, so the max/min width and height are the
// diameter, and the corner radius is the circle radius.
init_params.corner_radius = kBackButtonRadiusDip;
init_params.min_width = kBackButtonDiameterDip;
init_params.max_width = kBackButtonDiameterDip;
init_params.max_height = kBackButtonDiameterDip;
back_button_bubble_view_ = new TrayBubbleView(init_params);
back_button_view_ = new SwitchAccessBackButtonView(kBackButtonDiameterDip);
back_button_view_->SetBackground(UnifiedSystemTrayView::CreateBackground());
back_button_bubble_view_->AddChildView(back_button_view_);
back_button_bubble_view_->set_color(SK_ColorTRANSPARENT);
back_button_bubble_view_->layer()->SetFillsBoundsOpaquely(false);
back_button_widget_ =
views::BubbleDialogDelegateView::CreateBubble(back_button_bubble_view_);
TrayBackgroundView::InitializeBubbleAnimations(back_button_widget_);
back_button_bubble_view_->InitializeAndShowBubble();
}
void SwitchAccessBubbleController::CloseBubble() {
if (back_button_widget_ && !back_button_widget_->IsClosed())
back_button_widget_->CloseWithReason(
views::Widget::ClosedReason::kLostFocus);
}
void SwitchAccessBubbleController::BubbleViewDestroyed() {
back_button_view_ = nullptr;
back_button_bubble_view_ = nullptr;
back_button_widget_ = nullptr;
}
} // 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_SWITCH_ACCESS_BUBBLE_CONTROLLER_H_
#define ASH_SYSTEM_ACCESSIBILITY_SWITCH_ACCESS_BUBBLE_CONTROLLER_H_
#include "ash/system/tray/tray_bubble_view.h"
namespace ash {
class SwitchAccessBackButtonView;
// Manages the Switch Access back button bubble.
class ASH_EXPORT SwitchAccessBubbleController
: public TrayBubbleView::Delegate {
public:
SwitchAccessBubbleController();
~SwitchAccessBubbleController() override;
SwitchAccessBubbleController(const SwitchAccessBubbleController&) = delete;
SwitchAccessBubbleController& operator=(const SwitchAccessBubbleController&) =
delete;
void ShowBackButton(const gfx::Rect& anchor);
void CloseBubble();
SwitchAccessBackButtonView* GetBackButtonViewForTesting() {
return back_button_view_;
}
// TrayBubbleView::Delegate:
void BubbleViewDestroyed() override;
private:
// Owned by views hierarchy.
SwitchAccessBackButtonView* back_button_view_ = nullptr;
TrayBubbleView* back_button_bubble_view_ = nullptr;
views::Widget* back_button_widget_ = nullptr;
};
} // namespace ash
#endif // ASH_SYSTEM_ACCESSIBILITY_SWITCH_ACCESS_BUBBLE_CONTROLLER_H_
// 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/switch_access_bubble_controller.h"
#include "ash/accessibility/accessibility_controller_impl.h"
#include "ash/shell.h"
#include "ash/system/accessibility/switch_access_back_button_view.h"
#include "ash/test/ash_test_base.h"
#include "base/command_line.h"
#include "ui/accessibility/accessibility_switches.h"
namespace ash {
class SwitchAccessBubbleControllerTest : public AshTestBase {
public:
SwitchAccessBubbleControllerTest() = default;
~SwitchAccessBubbleControllerTest() override = default;
SwitchAccessBubbleControllerTest(const SwitchAccessBubbleControllerTest&) =
delete;
SwitchAccessBubbleControllerTest& operator=(
const SwitchAccessBubbleControllerTest&) = delete;
// testing::Test:
void SetUp() override {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
::switches::kEnableExperimentalAccessibilitySwitchAccess);
AshTestBase::SetUp();
Shell::Get()->accessibility_controller()->SetSwitchAccessEnabled(true);
}
SwitchAccessBubbleController* GetBubbleController() {
return Shell::Get()
->accessibility_controller()
->GetSwitchAccessBubbleControllerForTest();
}
gfx::Rect GetBackButtonBounds() {
SwitchAccessBubbleController* bubble_controller = GetBubbleController();
if (bubble_controller && bubble_controller->GetBackButtonViewForTesting())
return bubble_controller->GetBackButtonViewForTesting()
->GetBoundsInScreen();
return gfx::Rect();
}
};
// TODO(anastasi): Add more tests for closing and repositioning the button.
TEST_F(SwitchAccessBubbleControllerTest, ShowBackButton) {
EXPECT_TRUE(GetBubbleController());
gfx::Rect anchor_rect(100, 100, 0, 0);
GetBubbleController()->ShowBackButton(anchor_rect);
gfx::Rect bounds = GetBackButtonBounds();
EXPECT_EQ(bounds.width(), 36);
EXPECT_EQ(bounds.height(), 36);
}
} // namespace ash
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