0

[a11y] Add mouse keys to the status tray.

This adds a button in the status tray that can be used to turn mouse keys on/off.  The logic to toggle mouse keys is not added in this CL and will be added in a follow-up.

https://screenshot.googleplex.com/4Ykomm4SvXcuTAZ.png

BUG=b:259372916
TEST=ash_unittests --gtest_filter=MouseKeysTrayTest.*

Change-Id: Iedccbdfc5535571da7e39248129a00222bbe50f2
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5713956
Reviewed-by: Akihiro Ota <akihiroota@chromium.org>
Commit-Queue: Tzarial <zork@chromium.org>
Reviewed-by: Xiaoqian Dai <xdai@chromium.org>
Reviewed-by: Alex Newcomer <newcomer@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1354358}
This commit is contained in:
Tzarial
2024-09-12 05:35:10 +00:00
committed by Chromium LUCI CQ
parent 82424e7486
commit 8737f84bff
12 changed files with 371 additions and 1 deletions

@ -1585,6 +1585,8 @@ component("ash") {
"system/accessibility/floating_menu_button.h",
"system/accessibility/floating_menu_utils.cc",
"system/accessibility/floating_menu_utils.h",
"system/accessibility/mouse_keys/mouse_keys_tray.cc",
"system/accessibility/mouse_keys/mouse_keys_tray.h",
"system/accessibility/select_to_speak/select_to_speak_constants.h",
"system/accessibility/select_to_speak/select_to_speak_menu_bubble_controller.cc",
"system/accessibility/select_to_speak/select_to_speak_menu_bubble_controller.h",
@ -4032,6 +4034,7 @@ test("ash_unittests") {
"system/accessibility/dictation_bubble_controller_unittest.cc",
"system/accessibility/dictation_button_tray_unittest.cc",
"system/accessibility/floating_accessibility_controller_unittest.cc",
"system/accessibility/mouse_keys/mouse_keys_tray_unittest.cc",
"system/accessibility/select_to_speak/select_to_speak_menu_bubble_controller_unittest.cc",
"system/accessibility/select_to_speak/select_to_speak_speed_bubble_controller_unittest.cc",
"system/accessibility/select_to_speak/select_to_speak_tray_unittest.cc",

@ -1236,6 +1236,9 @@ Style notes:
system tray to toggle on/off spoken feedback feature.">
ChromeVox (spoken feedback)
</message>
<message name="IDS_ASH_STATUS_TRAY_ACCESSIBILITY_MOUSE_KEYS" desc="The label used in the accessibility menu of the system tray to pause/resume the mouse keys feature.">
Mouse keys
</message>
<message name="IDS_ASH_STATUS_TRAY_ACCESSIBILITY_SELECT_TO_SPEAK" desc="The label used in the accessibility menu of the system tray to toggle on/off the select-to-speak feature.">
Select-to-speak
</message>
@ -4062,6 +4065,9 @@ No devices connected.
<message name="IDS_ASH_IME_MENU_ACCESSIBLE_NAME" desc="The accessible text for opt-in IME menu icon in status tray.">
IME menu button
</message>
<message name="IDS_ASH_MOUSE_KEYS_TRAY_ACCESSIBLE_NAME" desc="The accessible text for the mouse keys menu icon in the status tray.">
Mouse keys button
</message>
<message name="IDS_ASH_SELECT_TO_SPEAK_TRAY_ACCESSIBLE_NAME" desc="The accessible text for the select-to-speak menu icon in the status tray.">
Select-to-speak button
</message>

@ -0,0 +1 @@
5de6b877847d89bf6bf59640fcdde02e5be3d7d3

@ -0,0 +1 @@
5de6b877847d89bf6bf59640fcdde02e5be3d7d3

@ -38,7 +38,8 @@ enum class TrayBackgroundViewCatalogName {
kVideoConferenceTray = 22,
kFocusMode = 23,
kPodsOverflow = 24,
kMaxValue = kPodsOverflow,
kMouseKeysStatusArea = 25,
kMaxValue = kMouseKeysStatusArea,
};
} // namespace ash

@ -555,6 +555,7 @@ aggregate_vector_icons("ash_vector_icons") {
"system_tray_cast.icon",
"system_tray_do_not_disturb.icon",
"system_tray_managed.icon",
"system_tray_mouse_keys.icon",
"system_tray_notification_counter_plus.icon",
"system_tray_recording.icon",
"system_tray_rotation_lock_auto.icon",

@ -0,0 +1,85 @@
// Copyright 2024 The Chromium Authors
// 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, 3.5f, 3,
CUBIC_TO, 2.95f, 3, 2.5f, 3.45f, 2.5f, 4,
V_LINE_TO, 6,
CUBIC_TO, 2.5f, 6.55f, 2.95f, 7, 3.5f, 7,
H_LINE_TO, 5.5f,
CUBIC_TO, 6.05f, 7, 6.5f, 6.55f, 6.5f, 6,
V_LINE_TO, 4,
CUBIC_TO, 6.5f, 3.45f, 6.05f, 3, 5.5f, 3,
H_LINE_TO, 3.5f,
CLOSE,
MOVE_TO, 8.5f, 3,
CUBIC_TO, 7.95f, 3, 7.5f, 3.45f, 7.5f, 4,
V_LINE_TO, 6,
CUBIC_TO, 7.5f, 6.36f, 7.69f, 6.68f, 7.98f, 6.86f,
CUBIC_TO, 8.65f, 5.46f, 9.94f, 4.42f, 11.5f, 4.1f,
V_LINE_TO, 4,
CUBIC_TO, 11.5f, 3.45f, 11.05f, 3, 10.5f, 3,
H_LINE_TO, 8.5f,
CLOSE,
MOVE_TO, 16.5f, 4,
V_LINE_TO, 5,
CUBIC_TO, 15.66f, 4.37f, 14.63f, 4, 13.5f, 4,
H_LINE_TO, 12.5f,
CUBIC_TO, 12.5f, 3.45f, 12.95f, 3, 13.5f, 3,
H_LINE_TO, 15.5f,
CUBIC_TO, 16.05f, 3, 16.5f, 3.45f, 16.5f, 4,
CLOSE,
MOVE_TO, 3.5f, 8,
CUBIC_TO, 2.95f, 8, 2.5f, 8.45f, 2.5f, 9,
V_LINE_TO, 11,
CUBIC_TO, 2.5f, 11.55f, 2.95f, 12, 3.5f, 12,
H_LINE_TO, 5.5f,
CUBIC_TO, 6.05f, 12, 6.5f, 11.55f, 6.5f, 11,
V_LINE_TO, 9,
CUBIC_TO, 6.5f, 8.45f, 6.05f, 8, 5.5f, 8,
H_LINE_TO, 3.5f,
CLOSE,
MOVE_TO, 2.5f, 14,
CUBIC_TO, 2.5f, 13.45f, 2.95f, 13, 3.5f, 13,
H_LINE_TO, 5.5f,
CUBIC_TO, 6.05f, 13, 6.5f, 13.45f, 6.5f, 14,
V_LINE_TO, 16,
CUBIC_TO, 6.5f, 16.55f, 6.05f, 17, 5.5f, 17,
H_LINE_TO, 3.5f,
CUBIC_TO, 2.95f, 17, 2.5f, 16.55f, 2.5f, 16,
V_LINE_TO, 14,
CLOSE,
MOVE_TO, 15.17f, 14.6f,
CUBIC_TO, 14.6f, 15.17f, 13.9f, 15.46f, 13.08f, 15.46f,
CUBIC_TO, 12.28f, 15.46f, 11.58f, 15.17f, 11, 14.6f,
CUBIC_TO, 10.43f, 14.02f, 10.15f, 13.33f, 10.15f, 12.52f,
V_LINE_TO, 11.69f,
H_LINE_TO, 16.02f,
V_LINE_TO, 12.52f,
CUBIC_TO, 16.02f, 13.33f, 15.74f, 14.02f, 15.17f, 14.6f,
CLOSE,
MOVE_TO, 12.27f, 10.06f,
H_LINE_TO, 10.19f,
CUBIC_TO, 10.27f, 9.48f, 10.5f, 8.98f, 10.88f, 8.56f,
CUBIC_TO, 11.25f, 8.15f, 11.72f, 7.85f, 12.27f, 7.69f,
V_LINE_TO, 10.06f,
CLOSE,
MOVE_TO, 15.98f, 10.06f,
H_LINE_TO, 13.9f,
V_LINE_TO, 7.69f,
CUBIC_TO, 14.45f, 7.85f, 14.91f, 8.15f, 15.27f, 8.56f,
CUBIC_TO, 15.65f, 8.98f, 15.88f, 9.48f, 15.98f, 10.06f,
CLOSE,
MOVE_TO, 9.81f, 15.79f,
CUBIC_TO, 10.7f, 16.67f, 11.79f, 17.1f, 13.08f, 17.1f,
CUBIC_TO, 14.38f, 17.1f, 15.46f, 16.67f, 16.33f, 15.79f,
CUBIC_TO, 17.22f, 14.9f, 17.67f, 13.81f, 17.67f, 12.52f,
V_LINE_TO, 10.58f,
CUBIC_TO, 17.67f, 9.29f, 17.22f, 8.21f, 16.33f, 7.33f,
CUBIC_TO, 15.46f, 6.45f, 14.38f, 6, 13.08f, 6,
CUBIC_TO, 11.79f, 6, 10.7f, 6.45f, 9.81f, 7.33f,
CUBIC_TO, 8.94f, 8.21f, 8.5f, 9.29f, 8.5f, 10.58f,
V_LINE_TO, 12.52f,
CUBIC_TO, 8.5f, 13.81f, 8.94f, 14.9f, 9.81f, 15.79f,
CLOSE

@ -0,0 +1,113 @@
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/system/accessibility/mouse_keys/mouse_keys_tray.h"
#include "ash/accessibility/accessibility_controller.h"
#include "ash/constants/tray_background_view_catalog.h"
#include "ash/resources/vector_icons/vector_icons.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shelf/shelf.h"
#include "ash/shell.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/style/ash_color_id.h"
#include "ash/system/tray/tray_constants.h"
#include "ash/system/tray/tray_container.h"
#include "ash/system/tray/tray_utils.h"
#include "ui/accessibility/accessibility_features.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/base/models/image_model.h"
#include "ui/color/color_id.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/views/border.h"
#include "ui/views/controls/image_view.h"
namespace ash {
namespace {
// Arbitrary ID for the icon.
const int kMouseKeysTrayIconID = 11;
ui::ImageModel GetMouseKeysIcon() {
return ui::ImageModel::FromVectorIcon(
kSystemTrayMouseKeysIcon,
static_cast<ui::ColorId>(cros_tokens::kCrosSysOnSurface));
}
} // namespace
MouseKeysTray::MouseKeysTray(Shelf* shelf,
TrayBackgroundViewCatalogName catalog_name)
: TrayBackgroundView(shelf, catalog_name) {
const ui::ImageModel image = GetMouseKeysIcon();
const int vertical_padding = (kTrayItemSize - image.Size().height()) / 2;
const int horizontal_padding = (kTrayItemSize - image.Size().width()) / 2;
tray_container()->AddChildView(
views::Builder<views::ImageView>()
.SetID(kMouseKeysTrayIconID)
.SetTooltipText(l10n_util::GetStringUTF16(
IDS_ASH_STATUS_TRAY_ACCESSIBILITY_MOUSE_KEYS))
.SetImage(image)
.SetBorder(views::CreateEmptyBorder(
gfx::Insets::VH(vertical_padding, horizontal_padding)))
.Build());
// Observe the accessibility controller state changes to know when mouse keys
// state is updated or when it is disabled/enabled.
Shell::Get()->accessibility_controller()->AddObserver(this);
}
MouseKeysTray::~MouseKeysTray() {
// This may be called during shutdown in which case some of the
// ash objects may already be destroyed.
auto* shell = Shell::Get();
if (!shell) {
return;
}
auto* accessibility_controller = shell->accessibility_controller();
if (accessibility_controller) {
accessibility_controller->RemoveObserver(this);
}
}
void MouseKeysTray::Initialize() {
TrayBackgroundView::Initialize();
OnAccessibilityStatusChanged();
HandleLocaleChange();
}
std::u16string MouseKeysTray::GetAccessibleNameForTray() {
return l10n_util::GetStringUTF16(IDS_ASH_MOUSE_KEYS_TRAY_ACCESSIBLE_NAME);
}
void MouseKeysTray::HandleLocaleChange() {
GetIcon()->SetTooltipText(
l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_ACCESSIBILITY_MOUSE_KEYS));
}
void MouseKeysTray::UpdateTrayItemColor(bool is_active) {
SetIsActive(is_active);
}
void MouseKeysTray::OnAccessibilityStatusChanged() {
auto* accessibility_controller = Shell::Get()->accessibility_controller();
SetVisiblePreferred(::features::IsAccessibilityMouseKeysEnabled() &&
accessibility_controller->mouse_keys().enabled());
}
void MouseKeysTray::OnSessionStateChanged(session_manager::SessionState state) {
GetIcon()->SetImage(GetMouseKeysIcon());
}
views::ImageView* MouseKeysTray::GetIcon() {
return static_cast<views::ImageView*>(
tray_container()->GetViewByID(kMouseKeysTrayIconID));
}
BEGIN_METADATA(MouseKeysTray);
END_METADATA
} // namespace ash

@ -0,0 +1,63 @@
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ASH_SYSTEM_ACCESSIBILITY_MOUSE_KEYS_MOUSE_KEYS_TRAY_H_
#define ASH_SYSTEM_ACCESSIBILITY_MOUSE_KEYS_MOUSE_KEYS_TRAY_H_
#include <string>
#include "ash/accessibility/accessibility_observer.h"
#include "ash/ash_export.h"
#include "ash/constants/tray_background_view_catalog.h"
#include "ash/public/cpp/session/session_observer.h"
#include "ash/system/tray/tray_background_view.h"
#include "base/memory/raw_ptr.h"
#include "ui/base/metadata/metadata_header_macros.h"
namespace views {
class ImageView;
} // namespace views
namespace ash {
class Shelf;
// A button in the tray that lets users start/stop mouse keys.
class ASH_EXPORT MouseKeysTray : public TrayBackgroundView,
public AccessibilityObserver,
public SessionObserver {
METADATA_HEADER(MouseKeysTray, TrayBackgroundView)
public:
MouseKeysTray(Shelf* shelf, TrayBackgroundViewCatalogName catalog_name);
MouseKeysTray(const MouseKeysTray&) = delete;
MouseKeysTray& operator=(const MouseKeysTray&) = delete;
~MouseKeysTray() override;
// TrayBackgroundView:
void Initialize() override;
std::u16string GetAccessibleNameForTray() override;
void HandleLocaleChange() override;
void HideBubbleWithView(const TrayBubbleView* bubble_view) override {}
void HideBubble(const TrayBubbleView* bubble_view) override {}
void ClickedOutsideBubble(const ui::LocatedEvent& event) override {}
void UpdateTrayItemColor(bool is_active) override;
// AccessibilityObserver:
void OnAccessibilityStatusChanged() override;
// SessionObserver:
void OnSessionStateChanged(session_manager::SessionState state) override;
private:
friend class MouseKeysTrayTest;
views::ImageView* GetIcon();
ScopedSessionObserver session_observer_{this};
};
} // namespace ash
#endif // ASH_SYSTEM_ACCESSIBILITY_MOUSE_KEYS_MOUSE_KEYS_TRAY_H_

@ -0,0 +1,88 @@
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/system/accessibility/mouse_keys/mouse_keys_tray.h"
#include "ash/accessibility/accessibility_controller.h"
#include "ash/shell.h"
#include "ash/system/status_area_widget.h"
#include "ash/system/status_area_widget_test_helper.h"
#include "ash/test/ash_test_base.h"
#include "base/test/scoped_feature_list.h"
#include "ui/accessibility/accessibility_features.h"
#include "ui/events/base_event_utils.h"
namespace ash {
namespace {
MouseKeysTray* GetTray() {
return StatusAreaWidgetTestHelper::GetStatusAreaWidget()->mouse_keys_tray();
}
// Returns true if the Mouse keys tray is visible.
bool IsVisible() {
return GetTray()->GetVisible();
}
} // namespace
class MouseKeysTrayTest : public AshTestBase {
public:
MouseKeysTrayTest() = default;
MouseKeysTrayTest(const MouseKeysTrayTest&) = delete;
MouseKeysTrayTest& operator=(const MouseKeysTrayTest&) = delete;
~MouseKeysTrayTest() override = default;
void SetUp() override {
scoped_feature_list_.InitAndEnableFeature(
::features::kAccessibilityMouseKeys);
AshTestBase::SetUp();
Shell::Get()->accessibility_controller()->mouse_keys().SetEnabled(true);
EXPECT_TRUE(GetImageView());
EXPECT_TRUE(IsVisible());
}
private:
// Gets the current tray image view.
views::ImageView* GetImageView() { return GetTray()->GetIcon(); }
base::test::ScopedFeatureList scoped_feature_list_;
};
// Tests the icon disappears when mouse keys is disabled and re-appears
// when it is enabled.
TEST_F(MouseKeysTrayTest, ShowsAndHidesWithMouseKeysEnabled) {
Shell::Get()->accessibility_controller()->mouse_keys().SetEnabled(false);
EXPECT_FALSE(IsVisible());
Shell::Get()->accessibility_controller()->mouse_keys().SetEnabled(true);
EXPECT_TRUE(IsVisible());
}
// Trivial test to increase coverage of mouse_keys_tray.h. The
// MouseKeysTray does not have a bubble, so these are empty functions.
// Without this test, coverage of mouse_keys_tray.h is 0%.
TEST_F(MouseKeysTrayTest, OverriddenFunctionsDoNothing) {
GetTray()->HideBubbleWithView(nullptr);
const ui::MouseEvent event(ui::EventType::kMousePressed, gfx::Point(),
gfx::Point(), ui::EventTimeForNow(), 0, 0);
GetTray()->ClickedOutsideBubble(event);
}
using MouseKeysTrayTestFeatureDisabled = AshTestBase;
TEST_F(MouseKeysTrayTestFeatureDisabled, TrayHidden) {
Shell::Get()->accessibility_controller()->mouse_keys().SetEnabled(true);
// Tray should exist even when the feature is disabled.
EXPECT_TRUE(GetTray());
// However, it shouldn't be visible.
EXPECT_FALSE(IsVisible());
Shell::Get()->accessibility_controller()->mouse_keys().SetEnabled(false);
EXPECT_FALSE(IsVisible());
}
} // namespace ash

@ -21,6 +21,7 @@
#include "ash/shelf/shelf_widget.h"
#include "ash/shell.h"
#include "ash/system/accessibility/dictation_button_tray.h"
#include "ash/system/accessibility/mouse_keys/mouse_keys_tray.h"
#include "ash/system/accessibility/select_to_speak/select_to_speak_tray.h"
#include "ash/system/eche/eche_tray.h"
#include "ash/system/focus_mode/focus_mode_tray.h"
@ -99,6 +100,8 @@ void StatusAreaWidget::Initialize() {
AddTrayButton(std::make_unique<LogoutButtonTray>(shelf_));
dictation_button_tray_ = AddTrayButton(std::make_unique<DictationButtonTray>(
shelf_, TrayBackgroundViewCatalogName::kDictationStatusArea));
mouse_keys_tray_ = AddTrayButton(std::make_unique<MouseKeysTray>(
shelf_, TrayBackgroundViewCatalogName::kMouseKeysStatusArea));
select_to_speak_tray_ = AddTrayButton(std::make_unique<SelectToSpeakTray>(
shelf_, TrayBackgroundViewCatalogName::kSelectToSpeakStatusArea));
ime_menu_tray_ = AddTrayButton(std::make_unique<ImeMenuTray>(shelf_));
@ -298,6 +301,7 @@ void StatusAreaWidget::LogVisiblePodCountMetric() {
case TrayBackgroundViewCatalogName::kWmMode:
case TrayBackgroundViewCatalogName::kVideoConferenceTray:
case TrayBackgroundViewCatalogName::kFocusMode:
case TrayBackgroundViewCatalogName::kMouseKeysStatusArea:
if (!tray_button->GetVisible()) {
continue;
}

@ -37,6 +37,7 @@ class PaletteTray;
class PhoneHubTray;
class PodsOverflowTray;
class AnnotationTray;
class MouseKeysTray;
class SelectToSpeakTray;
class Shelf;
class StatusAreaAnimationController;
@ -143,6 +144,7 @@ class ASH_EXPORT StatusAreaWidget : public SessionObserver,
PhoneHubTray* phone_hub_tray() { return phone_hub_tray_; }
EcheTray* eche_tray() { return eche_tray_; }
MouseKeysTray* mouse_keys_tray() { return mouse_keys_tray_; }
SelectToSpeakTray* select_to_speak_tray() { return select_to_speak_tray_; }
WmModeButtonTray* wm_mode_button_tray() { return wm_mode_button_tray_; }
@ -301,6 +303,8 @@ class ASH_EXPORT StatusAreaWidget : public SessionObserver,
raw_ptr<VirtualKeyboardTray, DanglingUntriaged> virtual_keyboard_tray_ =
nullptr;
raw_ptr<ImeMenuTray, DanglingUntriaged> ime_menu_tray_ = nullptr;
raw_ptr<MouseKeysTray, DisableDanglingPtrDetection> mouse_keys_tray_ =
nullptr;
raw_ptr<SelectToSpeakTray, DanglingUntriaged> select_to_speak_tray_ = nullptr;
raw_ptr<HoldingSpaceTray, DanglingUntriaged> holding_space_tray_ = nullptr;
raw_ptr<WmModeButtonTray, DanglingUntriaged> wm_mode_button_tray_ = nullptr;