// 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_menu_view.h" #include "ash/accessibility/accessibility_controller_impl.h" #include "ash/resources/vector_icons/vector_icons.h" #include "ash/shell.h" #include "ash/strings/grit/ash_strings.h" #include "ash/system/accessibility/switch_access_menu_button.h" #include "ash/system/tray/tray_constants.h" #include "base/containers/flat_map.h" #include "base/no_destructor.h" #include "ui/accessibility/accessibility_switches.h" #include "ui/accessibility/ax_node_data.h" #include "ui/accessibility/mojom/ax_node_data.mojom-shared.h" #include "ui/events/event.h" #include "ui/views/layout/grid_layout.h" namespace ash { namespace { constexpr int kMaxColumns = 3; struct ButtonInfo { const gfx::VectorIcon* icon; int label_id; }; // These strings must match the values of // accessibility_private::SwitchAccessMenuAction. const base::flat_map& GetMenuButtonDetails() { static base::NoDestructor> menu_button_details({}); if (menu_button_details->empty()) { base::flat_map kBaseMenuButtonDetails({ {"copy", {&kSwitchAccessCopyIcon, IDS_ASH_SWITCH_ACCESS_COPY}}, {"cut", {&kSwitchAccessCutIcon, IDS_ASH_SWITCH_ACCESS_CUT}}, {"decrement", {&kSwitchAccessDecrementIcon, IDS_ASH_SWITCH_ACCESS_DECREMENT}}, {"dictation", {&kDictationOnNewuiIcon, IDS_ASH_SWITCH_ACCESS_DICTATION}}, {"endTextSelection", {&kSwitchAccessEndTextSelectionIcon, IDS_ASH_SWITCH_ACCESS_END_TEXT_SELECTION}}, {"increment", {&kSwitchAccessIncrementIcon, IDS_ASH_SWITCH_ACCESS_INCREMENT}}, {"jumpToBeginningOfText", {&kSwitchAccessJumpToBeginningOfTextIcon, IDS_ASH_SWITCH_ACCESS_JUMP_TO_BEGINNING_OF_TEXT}}, {"jumpToEndOfText", {&kSwitchAccessJumpToEndOfTextIcon, IDS_ASH_SWITCH_ACCESS_JUMP_TO_END_OF_TEXT}}, {"keyboard", {&kSwitchAccessKeyboardIcon, IDS_ASH_SWITCH_ACCESS_KEYBOARD}}, {"moveBackwardOneCharOfText", {&kSwitchAccessMoveBackwardOneCharOfTextIcon, IDS_ASH_SWITCH_ACCESS_MOVE_BACKWARD_ONE_CHAR_OF_TEXT}}, {"moveBackwardOneWordOfText", {&kSwitchAccessMoveBackwardOneWordOfTextIcon, IDS_ASH_SWITCH_ACCESS_MOVE_BACKWARD_ONE_WORD_OF_TEXT}}, {"moveCursor", {&kSwitchAccessMoveCursorIcon, IDS_ASH_SWITCH_ACCESS_MOVE_CURSOR}}, {"moveDownOneLineOfText", {&kSwitchAccessMoveDownOneLineOfTextIcon, IDS_ASH_SWITCH_ACCESS_MOVE_DOWN_ONE_LINE_OF_TEXT}}, {"moveForwardOneCharOfText", {&kSwitchAccessMoveForwardOneCharOfTextIcon, IDS_ASH_SWITCH_ACCESS_MOVE_FORWARD_ONE_CHAR_OF_TEXT}}, {"moveForwardOneWordOfText", {&kSwitchAccessMoveForwardOneWordOfTextIcon, IDS_ASH_SWITCH_ACCESS_MOVE_FORWARD_ONE_WORD_OF_TEXT}}, {"moveUpOneLineOfText", {&kSwitchAccessMoveUpOneLineOfTextIcon, IDS_ASH_SWITCH_ACCESS_MOVE_UP_ONE_LINE_OF_TEXT}}, {"paste", {&kSwitchAccessPasteIcon, IDS_ASH_SWITCH_ACCESS_PASTE}}, {"scrollDown", {&kSwitchAccessScrollDownIcon, IDS_ASH_SWITCH_ACCESS_SCROLL_DOWN}}, {"scrollLeft", {&kSwitchAccessScrollLeftIcon, IDS_ASH_SWITCH_ACCESS_SCROLL_LEFT}}, {"scrollRight", {&kSwitchAccessScrollRightIcon, IDS_ASH_SWITCH_ACCESS_SCROLL_RIGHT}}, {"scrollUp", {&kSwitchAccessScrollUpIcon, IDS_ASH_SWITCH_ACCESS_SCROLL_UP}}, {"select", {&kSwitchAccessSelectIcon, IDS_ASH_SWITCH_ACCESS_SELECT}}, {"settings", {&kSwitchAccessSettingsIcon, IDS_ASH_SWITCH_ACCESS_SETTINGS}}, {"startTextSelection", {&kSwitchAccessStartTextSelectionIcon, IDS_ASH_SWITCH_ACCESS_START_TEXT_SELECTION}}, }); menu_button_details->swap(kBaseMenuButtonDetails); if (::switches::IsSwitchAccessPointScanningEnabled()) { menu_button_details->insert( {"pointScan", {&kSwitchAccessPointScanIcon, IDS_ASH_SWITCH_ACCESS_POINT_SCAN}}); } } return *menu_button_details; } } // namespace SwitchAccessMenuView::SwitchAccessMenuView() = default; SwitchAccessMenuView::~SwitchAccessMenuView() = default; void SwitchAccessMenuView::SetActions(std::vector actions) { RemoveAllChildViews(/*delete_children=*/true); views::GridLayout* layout = SetLayoutManager(std::make_unique()); views::ColumnSet* columns = layout->AddColumnSet(0); columns->AddPaddingColumn(0 /* resize_percent */, kUnifiedMenuPadding); for (int i = 0; i < kMaxColumns; i++) { columns->AddColumn(views::GridLayout::CENTER, views::GridLayout::CENTER, 0, /* resize_percent */ views::GridLayout::ColumnSize::kFixed, SwitchAccessMenuButton::kWidthDip, 0); columns->AddPaddingColumn(0 /* resize_percent */, kUnifiedMenuPadding); } int button_count = 0; for (std::string action : actions) { auto it = GetMenuButtonDetails().find(action); if (it == GetMenuButtonDetails().end()) continue; ButtonInfo info = it->second; // If this is the first button of a new row, tell the layout to start a // new row. if (button_count % kMaxColumns == 0) layout->StartRowWithPadding(0, 0, 0, kUnifiedMenuPadding); layout->AddView(std::make_unique(action, *info.icon, info.label_id)); button_count++; } layout->AddPaddingRow(0, kUnifiedMenuPadding); InvalidateLayout(); } int SwitchAccessMenuView::GetBubbleWidthDip() const { // In the future this will vary with the number of menu items displayed. return (kMaxColumns * SwitchAccessMenuButton::kWidthDip) + ((kMaxColumns - 1) * kUnifiedMenuPadding) + kUnifiedMenuItemPadding.left() + kUnifiedMenuItemPadding.right(); } void SwitchAccessMenuView::GetAccessibleNodeData(ui::AXNodeData* node_data) { node_data->role = ax::mojom::Role::kMenu; } const char* SwitchAccessMenuView::GetClassName() const { return "SwitchAccessMenuView"; } } // namespace ash