// Copyright (c) 2025 The Brave Authors. All rights reserved. // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this file, // You can obtain one at https://mozilla.org/MPL/2.0/. #include "brave/browser/ui/containers/containers_menu_model.h" #include #include #include "base/strings/utf_string_conversions.h" #include "brave/app/brave_command_ids.h" #include "brave/browser/ui/containers/container_model.h" #include "brave/grit/brave_generated_resources.h" #include "chrome/browser/ui/chrome_pages.h" #include "chrome/grit/generated_resources.h" #if BUILDFLAG(IS_MAC) #include "ui/gfx/image/image_skia.h" #endif namespace containers { ContainersMenuModel::ContainersMenuModel(Delegate& delegate, const ContainersService& service) : ui::SimpleMenuModel(this), delegate_(delegate), current_container_ids_(delegate_->GetCurrentContainerIds()), items_(GetContainerModels(service, current_container_ids_, delegate_->GetScaleFactor())) { BuildMenuItems(); } ContainersMenuModel::ContainersMenuModel(Delegate& delegate, std::vector items) : ui::SimpleMenuModel(this), delegate_(delegate), current_container_ids_(delegate_->GetCurrentContainerIds()), items_(std::move(items)) { BuildMenuItems(); } ContainersMenuModel::~ContainersMenuModel() = default; void ContainersMenuModel::BuildMenuItems() { // Trim the items to fit within the command ID range. const auto max_items = static_cast(IDC_OPEN_IN_CONTAINER_END - IDC_OPEN_IN_CONTAINER_START); if (items_.size() > max_items) { items_.erase(items_.begin() + max_items, items_.end()); LOG(WARNING) << "Too many containers for the current menu model. " << "Trimming to fit within command ID range."; } // 1. Add an item to indicate "No container" state. AddCheckItemWithStringId(IDC_OPEN_IN_CONTAINER_NO_CONTAINER, IDS_CXMENU_NO_CONTAINER); // 2. Add items for each container. int index = 0; for (const auto& item : items_) { AddCheckItem(ItemIndexToCommandId(index), base::UTF8ToUTF16(item.name())); SetIcon(index + 1, #if BUILDFLAG(IS_MAC) // On macOS, vector icon version of menu items are not supported. // For the reference, we tried fix this with adhoc patch in // https://github.com/brave/brave-core/pull/21835/files but was // imperfect in some cases. // So we just rasterize the icon right away and convert it to // ImageSkia version model. ui::ImageModel::FromImageSkia( item.icon().Rasterize(/*color_provider=*/nullptr))); #else item.icon()); #endif index++; } // 3. Add a separator. AddSeparator(ui::NORMAL_SEPARATOR); // 4. Add an item to create a new temporary container. AddItemWithStringId(IDC_NEW_TEMPORARY_CONTAINER, IDS_CXMENU_NEW_TEMPORARY_CONTAINER); // 5. Add an item to open settings page. AddItemWithStringId(IDC_OPEN_CONTAINERS_SETTING, IDS_CXMENU_OPEN_CONTAINERS_SETTINGS); } void ContainersMenuModel::ExecuteCommand(int command_id, int event_flags) { if (command_id == IDC_OPEN_IN_CONTAINER_START) { // "No container" is selected. delegate_->OnNoContainerSelected(); return; } if (command_id == IDC_NEW_TEMPORARY_CONTAINER) { delegate_->OnNewTemporaryContainerSelected(); return; } if (command_id == IDC_OPEN_CONTAINERS_SETTING) { // Open the containers settings page OpenContainerSettingsPage(); return; } // Set the selected container ID. ContainerSelected(command_id); } void ContainersMenuModel::OpenContainerSettingsPage() { chrome::ShowSettingsSubPage(delegate_->GetBrowserToOpenSettings(), "braveContent"); } void ContainersMenuModel::ContainerSelected(int command_id) { delegate_->OnContainerSelected( items_[CommandIdToItemIndex(command_id)].container()); } bool ContainersMenuModel::IsCommandIdChecked(int command_id) const { if (command_id == IDC_OPEN_IN_CONTAINER_START) { // IDC_OPEN_IN_CONTAINER_START is the command ID for the "No container" return current_container_ids_.empty(); } return current_container_ids_.contains( items_[CommandIdToItemIndex(command_id)].container()->id); } bool ContainersMenuModel::IsCommandIdEnabled(int command_id) const { return true; } int ContainersMenuModel::CommandIdToItemIndex(int command_id) const { const auto item_index = command_id - IDC_OPEN_IN_CONTAINER_START - 1; CHECK(item_index >= 0 && item_index < static_cast(items_.size())) << "Command ID " << command_id << " is out of range for the current menu model."; return item_index; } int ContainersMenuModel::ItemIndexToCommandId(int item_index) const { CHECK(item_index >= 0 && item_index < static_cast(items_.size())) << "Item index " << item_index << " is out of range for the current menu model."; return item_index + IDC_OPEN_IN_CONTAINER_START + 1; } } // namespace containers