chromium/ash/wm/overview/birch/birch_bar_menu_model_adapter.cc

// 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/wm/overview/birch/birch_bar_menu_model_adapter.h"

#include "ash/app_menu/app_menu_model_adapter.h"
#include "ash/shell.h"
#include "ash/style/checkbox.h"
#include "ash/style/switch.h"
#include "ash/style/typography.h"
#include "ash/wm/overview/birch/birch_bar_constants.h"
#include "ash/wm/overview/birch/birch_bar_context_menu_model.h"
#include "ash/wm/overview/birch/birch_bar_controller.h"
#include "ash/wm/overview/birch/birch_bar_util.h"
#include "base/types/cxx23_to_underlying.h"
#include "ui/chromeos/styles/cros_tokens_color_mappings.h"
#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/controls/menu/menu_config.h"
#include "ui/views/controls/menu/menu_controller.h"
#include "ui/views/controls/menu/menu_item_view.h"
#include "ui/views/controls/menu/submenu_view.h"
#include "ui/views/layout/box_layout_view.h"
#include "ui/views/layout/layout_provider.h"
#include "ui/views/metadata/view_factory_internal.h"
#include "ui/views/style/typography_provider.h"

namespace ash {

constexpr gfx::Size kShowSuggestionsItemSize(304, 32);

using CommandId = BirchBarContextMenuModel::CommandId;

BirchBarMenuModelAdapter::BirchBarMenuModelAdapter(
    std::unique_ptr<ui::SimpleMenuModel> birch_menu_model,
    views::Widget* widget_owner,
    ui::MenuSourceType source_type,
    base::OnceClosure on_menu_closed_callback,
    bool is_tablet_mode,
    bool for_chip_menu)
    : AppMenuModelAdapter(std::string(),
                          std::move(birch_menu_model),
                          widget_owner,
                          source_type,
                          std::move(on_menu_closed_callback),
                          is_tablet_mode),
      for_chip_menu_(for_chip_menu) {}

BirchBarMenuModelAdapter::~BirchBarMenuModelAdapter() = default;

void BirchBarMenuModelAdapter::OnButtonSelected(OptionButtonBase* button) {}

void BirchBarMenuModelAdapter::OnButtonClicked(OptionButtonBase* button) {
  button->SetSelected(!button->selected());
}

views::MenuItemView* BirchBarMenuModelAdapter::AppendMenuItem(
    views::MenuItemView* menu,
    ui::MenuModel* model,
    size_t index) {
  const int command_id = model->GetCommandIdAt(index);
  const std::u16string label = model->GetLabelAt(index);
  const gfx::FontList font_list = views::TypographyProvider::Get().GetFont(
      views::style::CONTEXT_TOUCH_MENU, views::style::STYLE_PRIMARY);
  const int menu_item_padding =
      views::MenuConfig::instance().touchable_item_horizontal_padding;
  switch (command_id) {
    case base::to_underlying(CommandId::kShowSuggestions): {
      // By default, all menu item labels will start after the icon column. To
      // make the show suggestions label left aligned, we cannot use the menu
      // item label but create a new label and add it in a container with the
      // switch button.
      views::MenuItemView* item_view = menu->AppendMenuItem(command_id);
      item_view->SetTitle(std::u16string());
      item_view->SetHighlightWhenSelectedWithChildViews(true);

      // Set the name so that this is compatible with
      // `MenuItemView::GetAccessibleNodeData()`.
      item_view->GetViewAccessibility().SetName(label);

      // Create a container with the show suggestions label, a spacer, and the
      // switch button.
      views::View* spacer;
      auto* switch_container = item_view->AddChildView(
          views::Builder<views::BoxLayoutView>()
              .SetPreferredSize(kShowSuggestionsItemSize)
              .SetInsideBorderInsets(
                  gfx::Insets::TLBR(0, menu_item_padding, 0, 0))
              .AddChildren(
                  views::Builder<views::Label>().SetText(label).SetFontList(
                      font_list),
                  views::Builder<views::View>().CopyAddressTo(&spacer))
              .Build());

      // Make the spacer fill in the middle space to make the label left aligned
      // and the switch button right aligned.
      switch_container->SetFlexForView(spacer, 1);

      auto switch_button = std::make_unique<Switch>(base::BindRepeating(
          [](bool from_chip) {
            auto* birch_bar_controller = BirchBarController::Get();
            CHECK(birch_bar_controller);
            birch_bar_controller->ExecuteMenuCommand(
                base::to_underlying(CommandId::kShowSuggestions), from_chip);
          },
          for_chip_menu_));
      switch_button->SetIsOn(
          BirchBarController::Get()->GetShowBirchSuggestions());
      switch_button->GetViewAccessibility().SetName(label);

      switch_container->AddChildView(std::move(switch_button));
      return item_view;
    }
    case base::to_underlying(CommandId::kWeatherSuggestions):
    case base::to_underlying(CommandId::kCalendarSuggestions):
    case base::to_underlying(CommandId::kDriveSuggestions):
    case base::to_underlying(CommandId::kChromeTabSuggestions):
    case base::to_underlying(CommandId::kMediaSuggestions):
    case base::to_underlying(CommandId::kCoralSuggestions): {
      views::MenuItemView* item_view = menu->AppendMenuItem(command_id);
      // Note that we cannot directly added a checkbox, since `MenuItemView`
      // will align the newly added children to the right side of its label. We
      // should add a checkbox with the label text and remove menu's label by
      // explicitly setting an empty title.
      item_view->SetTitle(std::u16string());
      // Since the checkbox is the only child, `MenuItemView` will treat the
      // current item view as a container and add container margins to the item.
      // To keep the checkbox preferred height, we should set the vertical
      // margins to 0.
      item_view->set_vertical_margin(0);
      item_view->SetHighlightWhenSelectedWithChildViews(true);
      item_view->GetViewAccessibility().SetName(label);

      // Creates a checkbox. The argument `button_width` is the minimum width of
      // the checkbox button. Since we are not going to limit the minimum size,
      // so it is set to 0.
      auto* checkbox = item_view->AddChildView(std::make_unique<Checkbox>(
          /*button_width=*/0,
          base::BindRepeating(
              [](int command_id, bool from_chip) {
                BirchBarController::Get()->ExecuteMenuCommand(command_id,
                                                              from_chip);
              },
              command_id, for_chip_menu_),
          label, gfx::Insets::VH(0, menu_item_padding), menu_item_padding));
      bool enabled = item_view->GetEnabled();
      checkbox->SetEnabled(enabled);
      checkbox->SetSelected(
          enabled &&
          BirchBarController::Get()->GetShowSuggestionType(
              birch_bar_util::CommandIdToSuggestionType(command_id)));
      checkbox->set_delegate(this);
      checkbox->GetViewAccessibility().SetName(label);
      checkbox->SetLabelFontList(font_list);
      checkbox->SetLabelColorId(cros_tokens::kCrosSysOnSurface);
      // Checkboxes don't support minor text, so we use minor text for tooltip.
      // Note that most commands do not have minor text / tooltips.
      checkbox->SetTooltipText(model->GetMinorTextAt(index));
      return item_view;
    }
    default:
      break;
  }

  return AppMenuModelAdapter::AppendMenuItem(menu, model, index);
}

void BirchBarMenuModelAdapter::RecordHistogramOnMenuClosed() {
  // TODO(zxdan): add metrics later.
}

}  // namespace ash