chromium/chrome/browser/ui/tabs/existing_window_sub_menu_model_chromeos.cc

// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ui/tabs/existing_window_sub_menu_model_chromeos.h"

#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/tabs/tab_menu_model_delegate.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/tabs/tab_strip_model_delegate.h"
#include "chrome/grit/generated_resources.h"
#include "chromeos/ui/wm/desks/desks_helper.h"
#include "ui/aura/window.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/models/menu_model.h"

namespace chromeos {

namespace {

int GetDeskIndexForBrowser(Browser* browser, int num_desks) {
  const std::string& workspace = browser->window()->GetWorkspace();
  int desk_index;
  // If the window is visible on all workspaces or unassigned
  // (aura::client::kWindowWorkspaceUnassignedWorkspace),
  // we should get the active desk index.
  if (workspace.empty() || browser->window()->IsVisibleOnAllWorkspaces()) {
    desk_index = DesksHelper::Get(browser->window()->GetNativeWindow())
                     ->GetActiveDeskIndex();
  } else {
    CHECK(base::StringToInt(workspace, &desk_index));
  }
  CHECK_LT(desk_index, num_desks);
  return desk_index;
}

// Returns true if there are at least 2 desks.
bool ShouldGroupByDesk(const DesksHelper* desks_helper) {
  constexpr int kMinNumOfDesks = 2;
  return desks_helper->GetNumberOfDesks() >= kMinNumOfDesks;
}

DesksHelper* GetDesksHelper(const std::vector<Browser*>& existing_browsers) {
  DCHECK_GT(existing_browsers.size(), 0UL);
  // It is OK to get DesksHelper from the window of the first existing browser
  // since the APIs (GetNumberOfDesks, GetDeskName(index)) used by this class
  // doesn't depend on the specific aura::Window.
  return DesksHelper::Get(
      (*existing_browsers.begin())->window()->GetNativeWindow());
}

}  // namespace

ExistingWindowSubMenuModelChromeOS::ExistingWindowSubMenuModelChromeOS(
    base::PassKey<ExistingWindowSubMenuModel> passkey,
    ui::SimpleMenuModel::Delegate* parent_delegate,
    TabMenuModelDelegate* tab_menu_model_delegate,
    TabStripModel* model,
    int context_index)
    : ExistingWindowSubMenuModel(ExistingWindowSubMenuModel::GetPassKey(),
                                 parent_delegate,
                                 tab_menu_model_delegate,
                                 model,
                                 context_index) {
  // If we shouldn't group by desk, ExistingWindowSubMenuModel's ctor has
  // already built the menu.
  std::vector<Browser*> tabbed_browser_windows =
      tab_menu_model_delegate->GetOtherBrowserWindows(
          model->delegate()->IsForWebApp());
  if (!ShouldGroupByDesk(GetDesksHelper(tabbed_browser_windows))) {
    return;
  }

  ClearMenu();
  BuildMenuGroupedByDesk(tabbed_browser_windows);
}

ExistingWindowSubMenuModelChromeOS::~ExistingWindowSubMenuModelChromeOS() =
    default;

void ExistingWindowSubMenuModelChromeOS::BuildMenuGroupedByDesk(
    const std::vector<Browser*>& existing_browsers) {
  // Get the vector of MenuItemInfo for |existing_browsers| and then group them
  // by desk.
  const DesksHelper* desks_helper = GetDesksHelper(existing_browsers);
  const int num_desks = desks_helper->GetNumberOfDesks();
  std::vector<std::vector<ExistingBaseSubMenuModel::MenuItemInfo>>
      grouped_by_desk_menu_item_infos(num_desks);
  for (ExistingBaseSubMenuModel::MenuItemInfo item :
       BuildMenuItemInfoVectorForBrowsers(existing_browsers)) {
    const int desk = GetDeskIndexForBrowser(
        existing_browsers[item.target_index.value()], num_desks);
    item.accessible_name = l10n_util::GetStringFUTF16(
        IDS_TAB_CXMENU_GROUPED_BY_DESK_MENU_ITEM_A11Y, item.text,
        desks_helper->GetDeskName(desk));
    grouped_by_desk_menu_item_infos[desk].push_back(std::move(item));
  }

  // Now flatten the 2D vector based on its grouping and insert the desk
  // headings before each group.
  std::vector<MenuItemInfo> sorted_menu_items_with_headings;
  for (size_t desk = 0; desk < grouped_by_desk_menu_item_infos.size(); ++desk) {
    const std::vector<MenuItemInfo>& desk_items =
        grouped_by_desk_menu_item_infos[desk];
    if (desk_items.empty())
      continue;

    // Create a MenuItemInfo for this desk for `desk_name`.
    std::u16string desk_name;
    if (desk == static_cast<size_t>(desks_helper->GetActiveDeskIndex())) {
      desk_name = l10n_util::GetStringFUTF16(
          IDS_TAB_CXMENU_GROUPED_BY_DESK_CURRENT_DESK_LABEL,
          desks_helper->GetDeskName(desk));
    } else {
      desk_name = desks_helper->GetDeskName(desk);
    }
    ExistingBaseSubMenuModel::MenuItemInfo desk_heading(desk_name);
    desk_heading.may_have_mnemonics = false;
    desk_heading.accessible_name = l10n_util::GetStringFUTF16(
        IDS_TAB_CXMENU_GROUPED_BY_DESK_DESK_HEADING_A11Y, desk_name,
        base::NumberToString16(desk_items.size()));

    sorted_menu_items_with_headings.push_back(std::move(desk_heading));
    sorted_menu_items_with_headings.insert(
        sorted_menu_items_with_headings.end(), desk_items.begin(),
        desk_items.end());
  }

  Build(IDS_TAB_CXMENU_MOVETOANOTHERNEWWINDOW, sorted_menu_items_with_headings);
}

}  // namespace chromeos