diff --git a/patches/helium/ui/layout/vertical.patch b/patches/helium/ui/layout/vertical.patch index 63e216e3f..84dbee34c 100644 --- a/patches/helium/ui/layout/vertical.patch +++ b/patches/helium/ui/layout/vertical.patch @@ -188,7 +188,17 @@ // TODO(crbug.com/465833741): Determine snapping behavior. static constexpr int kCollapseSnapWidth = (kUncollapsedMinWidth + kCollapsedWidth) / 2; -@@ -134,6 +134,7 @@ class VerticalTabStripRegionView final : +@@ -98,6 +98,9 @@ class VerticalTabStripRegionView final : + void AddedToWidget() override; + void Layout(PassKey) override; + views::View* GetDefaultFocusableChild() override; ++ void OnMouseMoved(const ui::MouseEvent& event) override; ++ void OnMouseEntered(const ui::MouseEvent& event) override; ++ void OnMouseExited(const ui::MouseEvent& event) override; + + // TabStripRegionView + void InitializeTabStrip() override; +@@ -134,6 +137,7 @@ class VerticalTabStripRegionView final : // the leading, top corner. void SetToolbarHeightForLayout(const int toolbar_height); void SetExclusionWidthForLayout(const int exclusion_width); @@ -196,22 +206,28 @@ TabDragTarget* GetTabDragTarget(const gfx::Point& point_in_screen); -@@ -141,6 +142,8 @@ class VerticalTabStripRegionView final : +@@ -141,6 +145,11 @@ class VerticalTabStripRegionView final : views::View* SetTabStripView(std::unique_ptr view); void ClearTabStripView(views::View* view); + void UpdateInteriorMargin(); ++ ++ void MaybeUpdateHoverStatus(const ui::MouseEvent& event); ++ void OnHoverStatusUpdated(); + void OnCollapsedStateChanged( tabs::VerticalTabStripStateController* state_controller); void UpdateCollapseState(tabs::VerticalTabStripState new_state); -@@ -194,6 +197,9 @@ class VerticalTabStripRegionView final : +@@ -194,6 +203,12 @@ class VerticalTabStripRegionView final : // The width of the exclusion zone. This is used to determine when to toggle // the collapse state of the state controller. std::optional exclusion_width_ = std::nullopt; + + // Whether a leading exclusion exists due to window controls. + bool has_leading_exclusion_ = false; ++ ++ // Is the tab strip currently hovered ++ bool is_hovered_ = false; }; #endif // CHROME_BROWSER_UI_VIEWS_FRAME_VERTICAL_TAB_STRIP_REGION_VIEW_H_ @@ -253,7 +269,16 @@ resize_animation_.SetTweenType(gfx::Tween::Type::EASE_IN_OUT_EMPHASIZED); resize_animation_.Reset(!state_controller_->IsCollapsed()); -@@ -148,8 +149,11 @@ void VerticalTabStripRegionView::Layout( +@@ -116,6 +117,8 @@ VerticalTabStripRegionView::VerticalTabS + + SetProperty(views::kElementIdentifierKey, kVerticalTabStripRegionElementId); + ++ SetNotifyEnterExitOnChild(true); ++ + GetViewAccessibility().SetRole(ax::mojom::Role::kTabList); + + SetBackground(std::make_unique( +@@ -148,8 +151,35 @@ void VerticalTabStripRegionView::Layout( // Manually position the resize area as it overlaps views handled by the flex // layout. @@ -264,10 +289,34 @@ + : bounds().right() - kResizeAreaWidth; + resize_area_->SetBoundsRect(gfx::Rect(resize_area_x, 0, kResizeAreaWidth, + bounds().height())); ++} ++ ++void VerticalTabStripRegionView::OnMouseMoved(const ui::MouseEvent& event) { ++ MaybeUpdateHoverStatus(event); ++} ++void VerticalTabStripRegionView::OnMouseEntered(const ui::MouseEvent& event) { ++ MaybeUpdateHoverStatus(event); ++} ++void VerticalTabStripRegionView::OnMouseExited(const ui::MouseEvent& event) { ++ is_hovered_ = false; ++ OnHoverStatusUpdated(); ++} ++ ++void VerticalTabStripRegionView::MaybeUpdateHoverStatus(const ui::MouseEvent& event) { ++ if (is_hovered_ || !GetWidget()->IsMouseEventsEnabled() || TabDragController::IsActive()) { ++ return; ++ } ++ ++ is_hovered_ = true; ++ OnHoverStatusUpdated(); ++} ++ ++void VerticalTabStripRegionView::OnHoverStatusUpdated() { ++ state_controller_->SetHovered(is_hovered_); } views::View* VerticalTabStripRegionView::GetDefaultFocusableChild() { -@@ -332,7 +336,10 @@ void VerticalTabStripRegionView::OnResiz +@@ -332,7 +362,10 @@ void VerticalTabStripRegionView::OnResiz if (!starting_width_on_resize_.has_value()) { starting_width_on_resize_ = width(); } @@ -279,7 +328,7 @@ if (done_resizing) { starting_width_on_resize_ = std::nullopt; } -@@ -342,6 +349,19 @@ void VerticalTabStripRegionView::OnResiz +@@ -342,6 +375,19 @@ void VerticalTabStripRegionView::OnResiz new_state.collapsed = false; new_state.uncollapsed_width = std::clamp(proposed_width, kUncollapsedMinWidth, kUncollapsedMaxWidth); @@ -299,7 +348,7 @@ if (done_resizing) { // We only want to save the uncollapsed width to the state controller if // the user has lifted their mouse, otherwise dragging the resize area to -@@ -410,6 +430,15 @@ void VerticalTabStripRegionView::SetExcl +@@ -410,6 +456,15 @@ void VerticalTabStripRegionView::SetExcl top_button_container_->SetExclusionWidthForLayout(exclusion_width); } @@ -315,7 +364,7 @@ VerticalPinnedTabContainerView* VerticalTabStripRegionView::GetPinnedTabsContainer() { return tab_strip_view_->GetPinnedTabsContainer(); -@@ -431,11 +460,12 @@ views::View* VerticalTabStripRegionView: +@@ -431,11 +486,12 @@ views::View* VerticalTabStripRegionView: views::FlexSpecification(views::MinimumFlexSizeRule::kScaleToZero, views::MaximumFlexSizeRule::kPreferred)); tab_strip_view_->SetProperty(views::kMarginsKey, @@ -332,7 +381,7 @@ return tab_strip_view_; } -@@ -445,6 +475,23 @@ void VerticalTabStripRegionView::ClearTa +@@ -445,6 +501,23 @@ void VerticalTabStripRegionView::ClearTa RemoveChildViewT(std::exchange(tab_strip_view_, nullptr)); } @@ -356,7 +405,7 @@ void VerticalTabStripRegionView::OnCollapsedStateChanged( tabs::VerticalTabStripStateController* state_controller) { if (target_collapse_state_.collapsed != state_controller->IsCollapsed()) { -@@ -460,16 +507,15 @@ void VerticalTabStripRegionView::OnColla +@@ -460,16 +533,15 @@ void VerticalTabStripRegionView::OnColla state_controller_->IsCollapsed() ? LayoutConstant::kVerticalTabStripCollapsedPadding : LayoutConstant::kVerticalTabStripUncollapsedPadding); @@ -376,7 +425,7 @@ if (tab_strip_view_) { tab_strip_view_->SetCollapsedState(state_controller->IsCollapsed()); -@@ -510,10 +556,6 @@ void VerticalTabStripRegionView::ResizeT +@@ -510,10 +582,6 @@ void VerticalTabStripRegionView::ResizeT } void VerticalTabStripRegionView::UpdateBackgroundColors() { @@ -1262,3 +1311,306 @@ views::FocusRing::Get(this)->SetColorId(kColorNewTabButtonFocusRing); ConfigureInkDropForToolbar(this); } +--- a/chrome/browser/ui/tabs/vertical_tab_strip_state_controller.cc ++++ b/chrome/browser/ui/tabs/vertical_tab_strip_state_controller.cc +@@ -6,14 +6,9 @@ + + #include + +-#include "base/strings/string_number_conversions.h" +-#include "base/strings/to_string.h" + #include "chrome/browser/profiles/profile.h" +-#include "chrome/browser/sessions/session_service.h" +-#include "chrome/browser/sessions/session_service_factory.h" + #include "chrome/browser/ui/actions/chrome_action_id.h" + #include "chrome/browser/ui/browser_actions.h" +-#include "chrome/browser/ui/browser_list.h" + #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" + #include "chrome/browser/ui/helium/helium_layout_state_controller.h" + #include "chrome/common/pref_names.h" +@@ -33,59 +28,33 @@ VerticalTabStripStateController::Vertica + BrowserWindowInterface* browser_window, + PrefService* pref_service, + actions::ActionItem* root_action_item, +- SessionService* session_service, +- SessionID session_id, +- std::optional restored_state_collapsed, +- std::optional restored_state_uncollapsed_width) ++ SessionService* /*session_service*/, ++ SessionID /*session_id*/, ++ std::optional /*restored_state_collapsed*/, ++ std::optional /*restored_state_uncollapsed_width*/) + : pref_service_(pref_service), + root_action_item_(root_action_item), +- session_service_(session_service), +- session_id_(session_id), +- browser_window_(browser_window), + scoped_unowned_user_data_(browser_window->GetUnownedUserDataHost(), + *this) { + pref_change_registrar_.Init(pref_service_); + +- pref_change_registrar_.Add( +- prefs::kHeliumLayout, ++ pref_change_registrar_.AddMultiple( ++ {prefs::kHeliumLayout, prefs::kHeliumVerticalRightAligned}, + base::BindRepeating(&VerticalTabStripStateController::NotifyStateChanged, + base::Unretained(this))); + +- if (restored_state_collapsed.has_value()) { +- SetCollapsed(restored_state_collapsed.value()); +- } +- if (restored_state_uncollapsed_width.has_value()) { +- SetUncollapsedWidth(restored_state_uncollapsed_width.value()); +- } ++ state_.collapsed = ++ pref_service_->GetBoolean(prefs::kHeliumVerticalCollapsed); ++ state_.uncollapsed_width = pref_service_->GetInteger( ++ prefs::kHeliumVerticalUncollapsedWidth); + + UpdateCollapseActionItem(); +- +- if (session_service_) { +- session_service_->AddObserver(this); +- +- bool is_browser_ready = false; +- for (Browser* browser : *BrowserList::GetInstance()) { +- if (browser->session_id() == session_id_) { +- is_browser_ready = true; +- break; +- } +- } +- +- if (!is_browser_ready) { +- browser_list_observation_.Observe(BrowserList::GetInstance()); +- } +- } +- + // TODO(crbug.com/455559992): Add uncollapsed text logic for collapse button. + } + + VerticalTabStripStateController::~VerticalTabStripStateController() { +- if (session_service_) { +- session_service_->RemoveObserver(this); +- session_service_ = nullptr; +- } ++ delayed_uncollapse_timer_.Stop(); + } +- + // static + VerticalTabStripStateController* VerticalTabStripStateController::From( + BrowserWindowInterface* browser_window) { +@@ -103,6 +72,16 @@ void VerticalTabStripStateController::Se + pref_service_->SetInteger(prefs::kHeliumLayout, std::to_underlying(layout)); + } + ++bool VerticalTabStripStateController::IsTabStripRightAligned() const { ++ return pref_service_->GetBoolean(prefs::kHeliumVerticalRightAligned); ++} ++ ++void VerticalTabStripStateController::SetTabStripRightAligned( ++ bool right_aligned) { ++ pref_service_->SetBoolean(prefs::kHeliumVerticalRightAligned, ++ right_aligned); ++} ++ + bool VerticalTabStripStateController::IsCollapsed() const { + return state_.collapsed; + } +@@ -114,6 +93,34 @@ void VerticalTabStripStateController::Se + } + } + ++void VerticalTabStripStateController::SetHovered(bool hovered) { ++ if (hovered) { ++ if (delayed_uncollapse_timer_.IsRunning()) { ++ return; ++ } ++ ++ delayed_uncollapse_timer_.Start(FROM_HERE, base::Milliseconds(400), ++ base::BindOnce(&VerticalTabStripStateController::SetCollapsedOnHover, ++ weak_ptr_factory_.GetWeakPtr(), false)); ++ } else { ++ delayed_uncollapse_timer_.Stop(); ++ SetCollapsedOnHover(true); ++ } ++} ++void VerticalTabStripStateController::SetCollapsedOnHover(bool collapsed) { ++ if (state_.collapsed == collapsed) { ++ return; ++ } ++ ++ // collapsing a view that's not held by hovering over it is incorrect ++ if (collapsed && !held_by_hover_) { ++ return; ++ } ++ ++ held_by_hover_ = !collapsed; ++ SetCollapsed(collapsed); ++} ++ + int VerticalTabStripStateController::GetUncollapsedWidth() const { + return state_.uncollapsed_width; + } +@@ -141,24 +148,23 @@ VerticalTabStripStateController::Registe + } + + void VerticalTabStripStateController::NotifyStateChanged() { +- UpdateSessionService(); ++ UpdatePrefs(); + UpdateCollapseActionItem(); + on_state_changed_callback_list_.Notify(this); + } + +-void VerticalTabStripStateController::UpdateSessionService() { +- if (session_service_ && !browser_list_observation_.IsObserving()) { +- session_service_->AddWindowExtraData(session_id_, kCollapsedKey, +- base::ToString(state_.collapsed)); +- session_service_->AddWindowExtraData( +- session_id_, kUncollapsedWidthKey, +- base::NumberToString(state_.uncollapsed_width)); +- } ++void VerticalTabStripStateController::UpdatePrefs() { ++ pref_service_->SetBoolean(prefs::kHeliumVerticalCollapsed, ++ state_.collapsed); ++ pref_service_->SetInteger(prefs::kHeliumVerticalUncollapsedWidth, ++ state_.uncollapsed_width); + } + + void VerticalTabStripStateController::UpdateCollapseActionItem() { ++ // In right-aligned mode, the directional icon has to be flipped. + const gfx::VectorIcon& icon = +- IsCollapsed() ? views::kMenuCloseIcon : views::kMenuOpenIcon; ++ IsCollapsed() == IsTabStripRightAligned() ? views::kMenuOpenIcon ++ : views::kMenuCloseIcon; + const auto& text = + IsCollapsed() ? IDS_EXPAND_VERTICAL_TABS : IDS_COLLAPSE_VERTICAL_TABS; + +@@ -175,19 +181,4 @@ void VerticalTabStripStateController::Up + } + } + +-void VerticalTabStripStateController::OnDestroying( +- SessionServiceBase* service) { +- if (service == session_service_) { +- session_service_->RemoveObserver(this); +- session_service_ = nullptr; +- } +-} +- +-void VerticalTabStripStateController::OnBrowserAdded(Browser* browser) { +- if (browser == browser_window_->GetBrowserForMigrationOnly()) { +- browser_list_observation_.Reset(); +- UpdateSessionService(); +- } +-} +- + } // namespace tabs +--- a/chrome/browser/ui/tabs/vertical_tab_strip_state_controller.h ++++ b/chrome/browser/ui/tabs/vertical_tab_strip_state_controller.h +@@ -6,16 +6,15 @@ + #define CHROME_BROWSER_UI_TABS_VERTICAL_TAB_STRIP_STATE_CONTROLLER_H_ + + #include "base/callback_list.h" ++#include "base/time/time.h" ++#include "base/timer/timer.h" ++#include "base/memory/weak_ptr.h" + #include "base/memory/raw_ptr.h" +-#include "base/scoped_observation.h" +-#include "chrome/browser/sessions/session_service_base_observer.h" +-#include "chrome/browser/ui/browser_list_observer.h" + #include "chrome/browser/ui/tabs/vertical_tab_strip_state.h" + #include "components/prefs/pref_change_registrar.h" + #include "components/sessions/core/session_id.h" + #include "ui/base/unowned_user_data/scoped_unowned_user_data.h" + +-class BrowserList; + class BrowserWindowInterface; + class PrefService; + class SessionService; +@@ -26,8 +25,7 @@ class ActionItem; + + namespace tabs { + +-class VerticalTabStripStateController : public SessionServiceBaseObserver, +- public BrowserListObserver { ++class VerticalTabStripStateController { + public: + DECLARE_USER_DATA(VerticalTabStripStateController); + +@@ -43,13 +41,15 @@ class VerticalTabStripStateController : + delete; + VerticalTabStripStateController& operator=( + const VerticalTabStripStateController&) = delete; +- ~VerticalTabStripStateController() override; ++ ~VerticalTabStripStateController(); + + static VerticalTabStripStateController* From( + BrowserWindowInterface* browser_window); + + bool ShouldDisplayVerticalTabs() const; + void SetVerticalTabsEnabled(bool enabled); ++ bool IsTabStripRightAligned() const; ++ void SetTabStripRightAligned(bool right_aligned); + + bool IsCollapsed() const; + void SetCollapsed(bool collapsed); +@@ -57,6 +57,8 @@ class VerticalTabStripStateController : + int GetUncollapsedWidth() const; + void SetUncollapsedWidth(int width); + ++ void SetHovered(bool hovered); ++ + const VerticalTabStripState& GetState() const { return state_; } + void SetState(const VerticalTabStripState& state); + +@@ -65,42 +67,33 @@ class VerticalTabStripStateController : + base::CallbackListSubscription RegisterOnStateChanged( + StateChangedCallback callback); + +- static constexpr char kCollapsedKey[] = "vertical_tab_strip_collapsed"; +- static constexpr char kUncollapsedWidthKey[] = +- "vertical_tab_strip_uncollapsed_width"; +- + private: + void NotifyStateChanged(); + +- // Updates the SessionService with the current state (collapsed status and +- // uncollapsed width) for the associated session ID. +- void UpdateSessionService(); ++ // Updates pref-backed state (collapsed status and uncollapsed width). ++ void UpdatePrefs(); + + // Update the Collapse Button's Action Item (kActionToggleCollapseVertical) + // based on the Vertical Tab Strip's Collapse State. + void UpdateCollapseActionItem(); + +- // SessionServiceBase::SessionServiceBaseObserver: +- void OnDestroying(SessionServiceBase* service) override; +- +- // BrowserListObserver: +- void OnBrowserAdded(Browser* browser) override; ++ void SetCollapsedOnHover(bool collapsed); + + const raw_ptr pref_service_; + PrefChangeRegistrar pref_change_registrar_; + raw_ptr root_action_item_; +- raw_ptr session_service_; +- const SessionID session_id_; +- raw_ptr browser_window_; + + VerticalTabStripState state_; + + base::RepeatingCallbackList + on_state_changed_callback_list_; +- base::ScopedObservation +- browser_list_observation_{this}; + ui::ScopedUnownedUserData + scoped_unowned_user_data_; ++ ++ // Ensure that this timer is destroyed before anything else is cleaned up ++ bool held_by_hover_ = false; ++ base::OneShotTimer delayed_uncollapse_timer_; ++ base::WeakPtrFactory weak_ptr_factory_{this}; + }; + + } // namespace tabs