1 // Copyright 2020 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "ash/system/accessibility/switch_access_menu_view.h"
6
7 #include "ash/accessibility/accessibility_controller_impl.h"
8 #include "ash/resources/vector_icons/vector_icons.h"
9 #include "ash/shell.h"
10 #include "ash/strings/grit/ash_strings.h"
11 #include "ash/system/accessibility/switch_access_menu_button.h"
12 #include "ash/system/tray/tray_constants.h"
13 #include "base/containers/flat_map.h"
14 #include "base/no_destructor.h"
15 #include "ui/accessibility/accessibility_switches.h"
16 #include "ui/accessibility/ax_node_data.h"
17 #include "ui/accessibility/mojom/ax_node_data.mojom-shared.h"
18 #include "ui/events/event.h"
19 #include "ui/views/layout/grid_layout.h"
20
21 namespace ash {
22
23 namespace {
24 constexpr int kMaxColumns = 3;
25
26 struct ButtonInfo {
27 const gfx::VectorIcon* icon;
28 int label_id;
29 };
30
31 // These strings must match the values of
32 // accessibility_private::SwitchAccessMenuAction.
GetMenuButtonDetails()33 const base::flat_map<std::string, ButtonInfo>& GetMenuButtonDetails() {
34 static base::NoDestructor<base::flat_map<std::string, ButtonInfo>>
35 menu_button_details({});
36
37 if (menu_button_details->empty()) {
38 base::flat_map<std::string, ButtonInfo> kBaseMenuButtonDetails({
39 {"copy", {&kSwitchAccessCopyIcon, IDS_ASH_SWITCH_ACCESS_COPY}},
40 {"cut", {&kSwitchAccessCutIcon, IDS_ASH_SWITCH_ACCESS_CUT}},
41 {"decrement",
42 {&kSwitchAccessDecrementIcon, IDS_ASH_SWITCH_ACCESS_DECREMENT}},
43 {"dictation",
44 {&kDictationOnNewuiIcon, IDS_ASH_SWITCH_ACCESS_DICTATION}},
45 {"endTextSelection",
46 {&kSwitchAccessEndTextSelectionIcon,
47 IDS_ASH_SWITCH_ACCESS_END_TEXT_SELECTION}},
48 {"increment",
49 {&kSwitchAccessIncrementIcon, IDS_ASH_SWITCH_ACCESS_INCREMENT}},
50 {"jumpToBeginningOfText",
51 {&kSwitchAccessJumpToBeginningOfTextIcon,
52 IDS_ASH_SWITCH_ACCESS_JUMP_TO_BEGINNING_OF_TEXT}},
53 {"jumpToEndOfText",
54 {&kSwitchAccessJumpToEndOfTextIcon,
55 IDS_ASH_SWITCH_ACCESS_JUMP_TO_END_OF_TEXT}},
56 {"keyboard",
57 {&kSwitchAccessKeyboardIcon, IDS_ASH_SWITCH_ACCESS_KEYBOARD}},
58 {"moveBackwardOneCharOfText",
59 {&kSwitchAccessMoveBackwardOneCharOfTextIcon,
60 IDS_ASH_SWITCH_ACCESS_MOVE_BACKWARD_ONE_CHAR_OF_TEXT}},
61 {"moveBackwardOneWordOfText",
62 {&kSwitchAccessMoveBackwardOneWordOfTextIcon,
63 IDS_ASH_SWITCH_ACCESS_MOVE_BACKWARD_ONE_WORD_OF_TEXT}},
64 {"moveCursor",
65 {&kSwitchAccessMoveCursorIcon, IDS_ASH_SWITCH_ACCESS_MOVE_CURSOR}},
66 {"moveDownOneLineOfText",
67 {&kSwitchAccessMoveDownOneLineOfTextIcon,
68 IDS_ASH_SWITCH_ACCESS_MOVE_DOWN_ONE_LINE_OF_TEXT}},
69 {"moveForwardOneCharOfText",
70 {&kSwitchAccessMoveForwardOneCharOfTextIcon,
71 IDS_ASH_SWITCH_ACCESS_MOVE_FORWARD_ONE_CHAR_OF_TEXT}},
72 {"moveForwardOneWordOfText",
73 {&kSwitchAccessMoveForwardOneWordOfTextIcon,
74 IDS_ASH_SWITCH_ACCESS_MOVE_FORWARD_ONE_WORD_OF_TEXT}},
75 {"moveUpOneLineOfText",
76 {&kSwitchAccessMoveUpOneLineOfTextIcon,
77 IDS_ASH_SWITCH_ACCESS_MOVE_UP_ONE_LINE_OF_TEXT}},
78 {"paste", {&kSwitchAccessPasteIcon, IDS_ASH_SWITCH_ACCESS_PASTE}},
79 {"scrollDown",
80 {&kSwitchAccessScrollDownIcon, IDS_ASH_SWITCH_ACCESS_SCROLL_DOWN}},
81 {"scrollLeft",
82 {&kSwitchAccessScrollLeftIcon, IDS_ASH_SWITCH_ACCESS_SCROLL_LEFT}},
83 {"scrollRight",
84 {&kSwitchAccessScrollRightIcon, IDS_ASH_SWITCH_ACCESS_SCROLL_RIGHT}},
85 {"scrollUp",
86 {&kSwitchAccessScrollUpIcon, IDS_ASH_SWITCH_ACCESS_SCROLL_UP}},
87 {"select", {&kSwitchAccessSelectIcon, IDS_ASH_SWITCH_ACCESS_SELECT}},
88 {"settings",
89 {&kSwitchAccessSettingsIcon, IDS_ASH_SWITCH_ACCESS_SETTINGS}},
90 {"startTextSelection",
91 {&kSwitchAccessStartTextSelectionIcon,
92 IDS_ASH_SWITCH_ACCESS_START_TEXT_SELECTION}},
93 });
94 menu_button_details->swap(kBaseMenuButtonDetails);
95 if (::switches::IsSwitchAccessPointScanningEnabled()) {
96 menu_button_details->insert(
97 {"pointScan",
98 {&kSwitchAccessPointScanIcon, IDS_ASH_SWITCH_ACCESS_POINT_SCAN}});
99 }
100 }
101 return *menu_button_details;
102 }
103
104 } // namespace
105
106 SwitchAccessMenuView::SwitchAccessMenuView() = default;
107 SwitchAccessMenuView::~SwitchAccessMenuView() = default;
108
SetActions(std::vector<std::string> actions)109 void SwitchAccessMenuView::SetActions(std::vector<std::string> actions) {
110 RemoveAllChildViews(/*delete_children=*/true);
111
112 views::GridLayout* layout =
113 SetLayoutManager(std::make_unique<views::GridLayout>());
114 views::ColumnSet* columns = layout->AddColumnSet(0);
115 columns->AddPaddingColumn(0 /* resize_percent */, kUnifiedMenuPadding);
116 for (int i = 0; i < kMaxColumns; i++) {
117 columns->AddColumn(views::GridLayout::CENTER, views::GridLayout::CENTER,
118 0, /* resize_percent */
119 views::GridLayout::ColumnSize::kFixed,
120 SwitchAccessMenuButton::kWidthDip, 0);
121 columns->AddPaddingColumn(0 /* resize_percent */, kUnifiedMenuPadding);
122 }
123
124 int button_count = 0;
125 for (std::string action : actions) {
126 auto it = GetMenuButtonDetails().find(action);
127 if (it == GetMenuButtonDetails().end())
128 continue;
129 ButtonInfo info = it->second;
130 // If this is the first button of a new row, tell the layout to start a
131 // new row.
132 if (button_count % kMaxColumns == 0)
133 layout->StartRowWithPadding(0, 0, 0, kUnifiedMenuPadding);
134 layout->AddView(std::make_unique<SwitchAccessMenuButton>(action, *info.icon,
135 info.label_id));
136 button_count++;
137 }
138 layout->AddPaddingRow(0, kUnifiedMenuPadding);
139 InvalidateLayout();
140 }
141
GetBubbleWidthDip() const142 int SwitchAccessMenuView::GetBubbleWidthDip() const {
143 // In the future this will vary with the number of menu items displayed.
144 return (kMaxColumns * SwitchAccessMenuButton::kWidthDip) +
145 ((kMaxColumns - 1) * kUnifiedMenuPadding) +
146 kUnifiedMenuItemPadding.left() + kUnifiedMenuItemPadding.right();
147 }
148
GetAccessibleNodeData(ui::AXNodeData * node_data)149 void SwitchAccessMenuView::GetAccessibleNodeData(ui::AXNodeData* node_data) {
150 node_data->role = ax::mojom::Role::kMenu;
151 }
152
GetClassName() const153 const char* SwitchAccessMenuView::GetClassName() const {
154 return "SwitchAccessMenuView";
155 }
156
157 } // namespace ash
158