From 94fa0708514db15e630f1f26501e6be8d52756fc Mon Sep 17 00:00:00 2001 From: wukko Date: Mon, 24 Nov 2025 20:46:58 +0600 Subject: [PATCH] helium/ui/tabs: tab muting, move indicator to left (#517) - enabled the tab muting feature by default - fixed the UX issue with the alert indicator and the close button swapping places on hover - increased the minimum width for close buttons to prevent mute & close buttons from overlapping each other - removed the fade animation from the indicator closes #324 --- patches/helium/ui/tabs.patch | 171 ++++++++++++++++++++++++++++++++++- 1 file changed, 167 insertions(+), 4 deletions(-) diff --git a/patches/helium/ui/tabs.patch b/patches/helium/ui/tabs.patch index 733df937..ac33c61f 100644 --- a/patches/helium/ui/tabs.patch +++ b/patches/helium/ui/tabs.patch @@ -91,7 +91,71 @@ return TabGroupUnderline::kStrokeThickness; --- a/chrome/browser/ui/views/tabs/tab.cc +++ b/chrome/browser/ui/views/tabs/tab.cc -@@ -1164,7 +1164,7 @@ void Tab::UpdateIconVisibility() { +@@ -129,6 +129,8 @@ constexpr int kTabAlertIndicatorCloseBut + // the discard ring by this amount if there is enough space. + constexpr int kIncreasedDiscardIndicatorRadiusDp = 2; + ++constexpr int kIndicatorPadding = 2; ++ + bool g_show_hover_card_on_mouse_hover = true; + + // Helper functions ------------------------------------------------------------ +@@ -428,19 +430,15 @@ void Tab::Layout(PassKey) { + } + close_button_->SetVisible(showing_close_button_); + ++ int alert_indicator_right = contents_rect.right(); + if (showing_alert_indicator_) { +- int right = contents_rect.right(); +- if (showing_close_button_) { +- right = close_x; +- if (extra_alert_indicator_padding_) { +- right -= ui::TouchUiController::Get()->touch_ui() +- ? kTabAlertIndicatorCloseButtonPaddingAdjustmentTouchUI +- : kTabAlertIndicatorCloseButtonPaddingAdjustment; +- } ++ int alert_left = start; ++ if (showing_icon_) { ++ alert_left = favicon_bounds.right() + kIndicatorPadding; + } + const gfx::Size image_size = alert_indicator_button_->GetPreferredSize(); + gfx::Rect bounds( +- std::max(contents_rect.x(), right - image_size.width()), ++ alert_left, + contents_rect.y() + Center(contents_rect.height(), image_size.height()), + image_size.width(), image_size.height()); + if (center_icon_) { +@@ -451,6 +449,7 @@ void Tab::Layout(PassKey) { + MaybeAdjustLeftForPinnedTab(&bounds, bounds.width()); + } + alert_indicator_button_->SetBoundsRect(bounds); ++ alert_indicator_right = bounds.right(); + } + alert_indicator_button_->SetVisible(showing_alert_indicator_); + +@@ -458,7 +457,9 @@ void Tab::Layout(PassKey) { + bool show_title = ShouldRenderAsNormalTab(); + if (show_title) { + int title_left = start; +- if (showing_icon_) { ++ if (showing_alert_indicator_) { ++ title_left = alert_indicator_right + kIndicatorPadding; ++ } else if (showing_icon_) { + // When computing the spacing from the favicon, don't count the actual + // icon view width (which will include extra room for the alert + // indicator), but rather the normal favicon width which is what it will +@@ -469,9 +470,7 @@ void Tab::Layout(PassKey) { + title_left = std::max(title_left, after_favicon); + } + int title_right = contents_rect.right(); +- if (showing_alert_indicator_) { +- title_right = alert_indicator_button_->x() - after_title_padding; +- } else if (showing_close_button_) { ++ if (showing_close_button_) { + // Allow the title to overlay the close button's empty border padding. + title_right = close_x - after_title_padding; + } +@@ -1164,7 +1163,7 @@ void Tab::UpdateIconVisibility() { // left that needs to be considered. const int close_button_width = GetLayoutConstant(TAB_CLOSE_BUTTON_SIZE) + GetLayoutConstant(TAB_AFTER_TITLE_PADDING); @@ -100,7 +164,7 @@ available_width >= (touch_ui ? kTouchMinimumContentsWidthForCloseButtons : kMinimumContentsWidthForCloseButtons); -@@ -1212,6 +1212,10 @@ void Tab::UpdateIconVisibility() { +@@ -1212,6 +1211,10 @@ void Tab::UpdateIconVisibility() { large_enough_for_close_button; if (base::CommandLine::ForCurrentProcess()->HasSwitch("hide-tab-close-buttons")) showing_close_button_ = false; @@ -111,7 +175,7 @@ if (showing_close_button_) { available_width -= close_button_width; } -@@ -1229,10 +1233,6 @@ void Tab::UpdateIconVisibility() { +@@ -1229,10 +1232,6 @@ void Tab::UpdateIconVisibility() { } } } @@ -122,6 +186,41 @@ } bool Tab::ShouldRenderAsNormalTab() const { +@@ -1253,15 +1252,25 @@ void Tab::UpdateTabIconNeedsAttentionBlo + } + + int Tab::GetWidthOfLargestSelectableRegion() const { +- // Assume the entire region to the left of the alert indicator and/or close +- // buttons is available for click-to-select. If neither are visible, the +- // entire tab region is available. +- const int indicator_left = alert_indicator_button_->GetVisible() +- ? alert_indicator_button_->x() +- : width(); +- const int close_button_left = +- close_button_->GetVisible() ? close_button_->x() : width(); +- return std::min(indicator_left, close_button_left); ++ // Returns the rightmost boundary of the selectable region, aka ++ // the space where the user can click to select the tab. ++ // ++ // This area is between the alert indicator (if visible) and the close ++ // button (if visible). It's used by AlertIndicatorButton to determine ++ // if there's enough safe click area to enable the mute toggle on ++ // inactive tabs. ++ ++ int selectable_region = width(); ++ ++ if (alert_indicator_button_->GetVisible()) { ++ selectable_region = alert_indicator_button_->bounds().right(); ++ } ++ ++ if (close_button_->GetVisible()) { ++ selectable_region = std::min(selectable_region, close_button_->x()); ++ } ++ ++ return selectable_region; + } + + void Tab::UpdateForegroundColors() { --- a/chrome/browser/ui/views/tabs/tab.h +++ b/chrome/browser/ui/views/tabs/tab.h @@ -69,7 +69,7 @@ class Tab : public gfx::AnimationDelegat @@ -129,7 +228,7 @@ // hide the close button on inactive tabs. Any smaller and they're too easy // to hit on accident. - static constexpr int kMinimumContentsWidthForCloseButtons = 68; -+ static constexpr int kMinimumContentsWidthForCloseButtons = 42; ++ static constexpr int kMinimumContentsWidthForCloseButtons = 56; static constexpr int kTouchMinimumContentsWidthForCloseButtons = 100; // Sets whether hover cards should appear on mouse hover. Used in browser @@ -334,3 +433,67 @@ // Set the bounds of the sync icon first, followed by the title. const int start_of_sync_icon = title_chip_insets.left(); +--- a/media/base/media_switches.cc ++++ b/media/base/media_switches.cc +@@ -324,7 +324,7 @@ BASE_FEATURE(kPictureInPictureShowWindow + #endif // !BUILDFLAG(IS_ANDROID) + + // Enables user control over muting tab audio from the tab strip. +-BASE_FEATURE(kEnableTabMuting, base::FEATURE_DISABLED_BY_DEFAULT); ++BASE_FEATURE(kEnableTabMuting, base::FEATURE_ENABLED_BY_DEFAULT); + + #if BUILDFLAG(ENABLE_PLATFORM_HEVC) + // Enables HEVC hardware accelerated decoding. +--- a/chrome/browser/ui/views/tabs/alert_indicator_button.cc ++++ b/chrome/browser/ui/views/tabs/alert_indicator_button.cc +@@ -160,27 +160,8 @@ void AlertIndicatorButton::TransitionToA + UpdateIconForAlertState(next_state.value()); + } + +- if ((alert_state_ == tabs::TabAlert::AUDIO_PLAYING && +- next_state == tabs::TabAlert::AUDIO_MUTING) || +- (alert_state_ == tabs::TabAlert::AUDIO_MUTING && +- next_state == tabs::TabAlert::AUDIO_PLAYING)) { +- // Instant user feedback: No fade animation. +- showing_alert_state_ = next_state; +- fade_animation_.reset(); +- } else { +- if (!next_state) { +- showing_alert_state_ = alert_state_; // Fading-out indicator. +- } else { +- showing_alert_state_ = next_state; // Fading-in to next indicator. +- } +- fade_animation_ = CreateTabAlertIndicatorFadeAnimation(next_state); +- if (!fade_animation_delegate_) { +- fade_animation_delegate_ = std::make_unique(this); +- } +- fade_animation_->set_delegate(fade_animation_delegate_.get()); +- fade_animation_->Start(); +- } +- ++ showing_alert_state_ = next_state; ++ fade_animation_.reset(); + alert_state_ = next_state; + + if (previous_alert_showing_state != showing_alert_state_) { +@@ -289,20 +270,7 @@ bool AlertIndicatorButton::IsTriggerable + } + + void AlertIndicatorButton::PaintButtonContents(gfx::Canvas* canvas) { +- double opaqueness = 1.0; +- if (fade_animation_) { +- opaqueness = fade_animation_->GetCurrentValue(); +- if (!alert_state_) { +- opaqueness = 1.0 - opaqueness; // Fading out, not in. +- } +- } +- if (opaqueness < 1.0) { +- canvas->SaveLayerAlpha(opaqueness * SK_AlphaOPAQUE); +- } + ImageButton::PaintButtonContents(canvas); +- if (opaqueness < 1.0) { +- canvas->Restore(); +- } + } + + gfx::ImageSkia AlertIndicatorButton::GetImageToPaint() {