Files
Simon Hong cc17846ef6 [Sidebar V2] Repurpose toolbar button as a temporary sidebar pin (#36199)
Resolves https://github.com/brave/brave-browser/issues/55282

In V2, the sidebar toolbar button no longer toggles the side panel; it
controls only sidebar visibility, acting as a session-only pin.
  
Behavior:
  - kShowAlways: button is hidden — pinning has no meaning when the
    sidebar is always visible.
  - kShowOnMouseOver / kShowNever: button is visible. Clicking it pins
    the sidebar visible regardless of the show option. Clicking again,
    changing the show option, or closing the browser window clears the
    pinned state.
  - Button is highlighted while pinned. Highlight is driven by the
    controller's pinned state via UpdateSidebarVisibility() (on toggle)
    and UpdateToolbarButtonVisibility() (on show-option change), so it
    stays in sync regardless of which path triggered the update.

  Implementation:
  - SidebarController: adds session-only sidebar_pinned_ state and
    ToggleSidebarPinning(); OnShowSidebarOptionChanged() resets pinned.
  - Sidebar: new UpdateSidebarVisibility() interface entry point.
  - SidebarContainerView: pinned takes priority over kShowOnMouseOver /
    kShowNever and ShouldForceShowSidebar(); ShowSidebar() gains a
    suppress_animation flag so pin/unpin snaps without sliding (mirrors
    kShowAlways).
  - BraveSidePanelCoordinator::UpdateToolbarButtonHighlight is gated off
    in V2 so panel open/close no longer fights the pinned highlight.
  - SidePanelButton::ButtonPressed: V2 calls ToggleSidebarPinning()
    instead of side_panel_ui()->Toggle().

TEST=SidebarBrowserTest.SidebarV2ToolbarButtonPinning
2026-05-08 13:11:21 +09:00

128 lines
5.0 KiB
C++

/* Copyright (c) 2021 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 http://mozilla.org/MPL/2.0/. */
#ifndef BRAVE_BROWSER_UI_SIDEBAR_SIDEBAR_CONTROLLER_H_
#define BRAVE_BROWSER_UI_SIDEBAR_SIDEBAR_CONTROLLER_H_
#include <memory>
#include <optional>
#include <string>
#include "base/memory/raw_ptr.h"
#include "base/scoped_observation.h"
#include "brave/components/sidebar/browser/sidebar_item.h"
#include "brave/components/sidebar/browser/sidebar_service.h"
#include "ui/base/window_open_disposition.h"
class Browser;
class GURL;
class Profile;
class SidePanelUI;
class TabStripModel;
namespace sidebar {
class Sidebar;
class SidebarModel;
class SidebarWebPanelController;
// This controls the sidebar. Each browser could have different runtime sidebar
// state and it's stored in the model. Model initializes with persisted data
// that stored in user data. That persisted data is per-profile data and
// SidebarService manages. That data will include installed sidebar item list,
// order and etc. Browser object will be the owner of this controller.
// This will observe SidebarService to know per-profile sidebar data changing
// such as adding new item or deleting existing item.
// Controller will request about add/delete items to SidebarService.
// TODO(https://github.com/brave/brave-browser/issues/45977): Avoid direct
// Browser dependency. We should pass what we need like TabStripModel.
class SidebarController : public SidebarService::Observer {
public:
SidebarController(Browser* browser, Profile* profile);
~SidebarController() override;
SidebarController(const SidebarController&) = delete;
SidebarController& operator=(const SidebarController&) = delete;
// NOTE: Don't call this directly for panel item. Use ActivatePanelItem().
// This should be called as a result of SidePanelCoordinator's entry
// opening/closing event. If this method is called directly for activating
// panel, SidePanelCoordinator doesn't know about it.
// |disposition| is only valid for shortcut type. If |disposition| is not
// CURRENT_TAB, item at |index| is handled based on |disposition|.
void ActivateItemAt(
std::optional<size_t> index,
WindowOpenDisposition disposition = WindowOpenDisposition::CURRENT_TAB);
void AddItemWithCurrentTab();
void UpdateActiveItemState(std::optional<SidebarItem::BuiltInItemType>
active_panel_item = std::nullopt);
// Ask panel item activation state change to SidePanelUI.
void ActivatePanelItem(SidebarItem::BuiltInItemType panel_item);
void DeactivateCurrentPanel();
// Toggles a session-only "pin" that forces the sidebar control view
// visible regardless of the current show option. Pinned state is cleared
// when the show option changes (via OnShowSidebarOptionChanged) and when
// the browser window closes; it is not persisted. V2 only — invoked by
// the toolbar SidePanelButton when V2 is enabled.
void ToggleSidebarPinning();
bool sidebar_pinned() const { return sidebar_pinned_; }
void SetSidePanelUIForTesting(SidePanelUI* side_panel_ui) {
side_panel_ui_for_testing_ = side_panel_ui;
}
// If current browser doesn't have a tab for |url|, active tab will load
// |url|. Otherwise, existing tab will be activated.
// ShowSingletonTab() has similar functionality but it loads url in the
// new tab.
void LoadAtTab(const GURL& url);
bool IsActiveIndex(std::optional<size_t> index) const;
bool DoesBrowserHaveOpenedTabForItem(const SidebarItem& item) const;
void TearDownPreBrowserWindowDestruction();
void SetSidebar(Sidebar* sidebar);
Sidebar* sidebar() const { return sidebar_; }
SidebarModel* model() const { return sidebar_model_.get(); }
SidebarWebPanelController* GetWebPanelController();
// SidebarService::Observer overrides:
void OnShowSidebarOptionChanged(
SidebarService::ShowSidebarOption option) override;
private:
void OnPreferenceChanged(const std::string& pref_name);
// Iterate tabs by host (if tabs with host of URL exist).
// Otherwise, load URL in the active tab.
void IterateOrLoadAtActiveTab(const GURL& url);
// Try to find a tab that loads |url| from other browsers
// and activate it if found.
bool ActiveTabFromOtherBrowsersForHost(const GURL& url);
// Sidebar is visible when pinned regardless of show option.
bool sidebar_pinned_ = false;
raw_ptr<TabStripModel> tab_strip_model_ = nullptr;
raw_ptr<Profile> profile_ = nullptr;
raw_ptr<Browser> browser_ = nullptr;
raw_ptr<Sidebar> sidebar_ = nullptr;
raw_ptr<SidePanelUI> side_panel_ui_for_testing_ = nullptr;
std::unique_ptr<SidebarModel> sidebar_model_;
std::unique_ptr<SidebarWebPanelController> web_panel_controller_;
base::ScopedObservation<SidebarService, SidebarService::Observer>
sidebar_service_observed_{this};
};
} // namespace sidebar
#endif // BRAVE_BROWSER_UI_SIDEBAR_SIDEBAR_CONTROLLER_H_