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