chromium/ash/system/accessibility/switch_access/switch_access_menu_view.cc

// Copyright 2020 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/switch_access/switch_access_menu_view.h"

#include "ash/accessibility/accessibility_controller.h"
#include "ash/bubble/bubble_constants.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/switch_access_menu_button.h"
#include "ash/system/tray/tray_constants.h"
#include "base/containers/flat_map.h"
#include "base/memory/raw_ptr.h"
#include "base/no_destructor.h"
#include "ui/accessibility/accessibility_features.h"
#include "ui/accessibility/ax_node_data.h"
#include "ui/accessibility/mojom/ax_node_data.mojom-shared.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/events/event.h"
#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/layout/table_layout.h"

namespace ash {

namespace {
constexpr int kMaxColumns = 3;

struct ButtonInfo {
  raw_ptr<const gfx::VectorIcon> icon;
  int label_id;
};

// These strings must match the values of
// accessibility_private::SwitchAccessMenuAction.
const base::flat_map<std::string, ButtonInfo>& GetMenuButtonDetails() {
  static base::NoDestructor<base::flat_map<std::string, ButtonInfo>>
      menu_button_details({});

  if (menu_button_details->empty()) {
    base::flat_map<std::string, ButtonInfo> 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}},
         {"drillDown",
          {&kSwitchAccessDrillDownIcon, IDS_ASH_SWITCH_ACCESS_DRILL_DOWN}},
         {"endTextSelection",
          {&kSwitchAccessEndTextSelectionIcon,
           IDS_ASH_SWITCH_ACCESS_END_TEXT_SELECTION}},
         {"increment",
          {&kSwitchAccessIncrementIcon, IDS_ASH_SWITCH_ACCESS_INCREMENT}},
         {"itemScan",
          {&kSwitchAccessItemScanIcon, IDS_ASH_SWITCH_ACCESS_ITEM_SCAN}},
         {"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}},
         {"pointScan",
          {&kSwitchAccessPointScanIcon, IDS_ASH_SWITCH_ACCESS_POINT_SCAN}},
         {"leftClick",
          {&kSwitchAccessLeftClickIcon, IDS_ASH_SWITCH_ACCESS_LEFT_CLICK}},
         {"rightClick",
          {&kSwitchAccessRightClickIcon, IDS_ASH_SWITCH_ACCESS_RIGHT_CLICK}}});

    menu_button_details->swap(kBaseMenuButtonDetails);
  }
  return *menu_button_details;
}

}  // namespace

SwitchAccessMenuView::SwitchAccessMenuView() {
  GetViewAccessibility().SetRole(ax::mojom::Role::kMenu);
}

SwitchAccessMenuView::~SwitchAccessMenuView() = default;

void SwitchAccessMenuView::SetActions(std::vector<std::string> actions) {
  RemoveAllChildViews();

  views::TableLayout* layout =
      SetLayoutManager(std::make_unique<views::TableLayout>());
  layout->AddPaddingColumn(views::TableLayout::kFixedSize, kBubbleMenuPadding);
  for (int i = 0; i < kMaxColumns; i++) {
    layout->AddColumn(
        views::LayoutAlignment::kCenter, views::LayoutAlignment::kCenter,
        views::TableLayout::kFixedSize, views::TableLayout::ColumnSize::kFixed,
        SwitchAccessMenuButton::kWidthDip, 0);
    layout->AddPaddingColumn(views::TableLayout::kFixedSize,
                             kBubbleMenuPadding);
  }

  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->AddPaddingRow(views::TableLayout::kFixedSize, kBubbleMenuPadding);
      layout->AddRows(1, views::TableLayout::kFixedSize);
    }
    AddChildView(std::make_unique<SwitchAccessMenuButton>(action, *info.icon,
                                                          info.label_id));
    ++button_count;
  }
  layout->AddPaddingRow(views::TableLayout::kFixedSize, kBubbleMenuPadding);
}

int SwitchAccessMenuView::GetBubbleWidthDip() const {
  // In the future this will vary with the number of menu items displayed.
  return (kMaxColumns * SwitchAccessMenuButton::kWidthDip) +
         ((kMaxColumns - 1) * kBubbleMenuPadding) +
         kUnifiedMenuItemPadding.left() + kUnifiedMenuItemPadding.right();
}

BEGIN_METADATA(SwitchAccessMenuView)
END_METADATA

}  // namespace ash