diff --git a/browser/about_flags.cc b/browser/about_flags.cc index 7a4b6fc57e27..e9d869d5b269 100644 --- a/browser/about_flags.cc +++ b/browser/about_flags.cc @@ -361,16 +361,24 @@ #endif // BUILDFLAG(IS_ANDROID) #if !BUILDFLAG(IS_ANDROID) -#define BRAVE_SHARED_PINNED_TABS \ - EXPAND_FEATURE_ENTRIES({ \ - "brave-shared-pinned-tabs", \ - "Shared pinned tab", \ - "Pinned tabs are shared across windows", \ - kOsWin | kOsMac | kOsLinux, \ - FEATURE_VALUE_TYPE(tabs::features::kBraveSharedPinnedTabs), \ - }) +#define BRAVE_TABS_FEATURE_ENTRIES \ + EXPAND_FEATURE_ENTRIES( \ + { \ + "brave-shared-pinned-tabs", \ + "Shared pinned tab", \ + "Pinned tabs are shared across windows", \ + kOsWin | kOsMac | kOsLinux, \ + FEATURE_VALUE_TYPE(tabs::features::kBraveSharedPinnedTabs), \ + }, \ + { \ + "brave-horizontal-tabs-update", \ + "Updated horizontal tabs design", \ + "Updates the look and feel or horizontal tabs", \ + kOsWin | kOsMac | kOsLinux, \ + FEATURE_VALUE_TYPE(tabs::features::kBraveHorizontalTabsUpdate), \ + }) #else -#define BRAVE_SHARED_PINNED_TABS +#define BRAVE_TABS_FEATURE_ENTRIES #endif #if BUILDFLAG(ENABLE_AI_CHAT) @@ -887,7 +895,7 @@ BRAVE_BACKGROUND_VIDEO_PLAYBACK_ANDROID \ BRAVE_SAFE_BROWSING_ANDROID \ BRAVE_CHANGE_ACTIVE_TAB_ON_SCROLL_EVENT_FEATURE_ENTRIES \ - BRAVE_SHARED_PINNED_TABS \ + BRAVE_TABS_FEATURE_ENTRIES \ BRAVE_AI_CHAT \ BRAVE_AI_CHAT_HISTORY \ LAST_BRAVE_FEATURE_ENTRIES_ITEM // Keep it as the last item. diff --git a/browser/ui/brave_layout_constants.cc b/browser/ui/brave_layout_constants.cc index e4c88b495b59..5ff10143c759 100644 --- a/browser/ui/brave_layout_constants.cc +++ b/browser/ui/brave_layout_constants.cc @@ -5,9 +5,12 @@ #include "brave/browser/ui/brave_layout_constants.h" +#include "brave/browser/ui/tabs/features.h" #include "chrome/browser/ui/layout_constants.h" #include "ui/base/pointer/touch_ui_controller.h" +using tabs::features::HorizontalTabsUpdateEnabled; + // Returns a |nullopt| if the UI color is not handled by Brave. absl::optional GetBraveLayoutConstant(LayoutConstant constant) { const bool touch = ui::TouchUiController::Get()->touch_ui(); @@ -18,7 +21,15 @@ absl::optional GetBraveLayoutConstant(LayoutConstant constant) { // ui::MaterialDesignController::IsNewerMaterialUi(); switch (constant) { case TAB_HEIGHT: { - return (touch ? 41 : 30) + GetLayoutConstant(TABSTRIP_TOOLBAR_OVERLAP); + const int tab_height = HorizontalTabsUpdateEnabled() ? 36 : 30; + return (touch ? 41 : tab_height) + + GetLayoutConstant(TABSTRIP_TOOLBAR_OVERLAP); + } + case TABSTRIP_TOOLBAR_OVERLAP: { + if (!HorizontalTabsUpdateEnabled()) { + return absl::nullopt; + } + return 0; } case TAB_SEPARATOR_HEIGHT: { return 24; diff --git a/browser/ui/color/brave_color_mixer.cc b/browser/ui/color/brave_color_mixer.cc index 0c60bcc2d725..6573338db8e2 100644 --- a/browser/ui/color/brave_color_mixer.cc +++ b/browser/ui/color/brave_color_mixer.cc @@ -185,6 +185,8 @@ void AddChromeLightThemeColorMixer(ui::ColorProvider* provider, mixer[kColorTabForegroundActiveFrameActive] = {kLightToolbarIcon}; mixer[kColorTabForegroundInactiveFrameActive] = { kColorTabForegroundActiveFrameActive}; + mixer[kColorTabStrokeFrameActive] = {SkColorSetA(SK_ColorBLACK, 0.07 * 255)}; + mixer[kColorTabStrokeFrameInactive] = {kColorTabStrokeFrameActive}; mixer[kColorToolbar] = {kLightToolbar}; mixer[kColorToolbarButtonIcon] = {kColorTabForegroundActiveFrameActive}; mixer[kColorToolbarButtonIconInactive] = { diff --git a/browser/ui/tabs/BUILD.gn b/browser/ui/tabs/BUILD.gn index eab12d66faf2..bae672d87615 100644 --- a/browser/ui/tabs/BUILD.gn +++ b/browser/ui/tabs/BUILD.gn @@ -12,12 +12,14 @@ source_set("tabs") { if (!is_android) { sources = [ + "brave_tab_layout_constants.h", "brave_tab_menu_model.cc", "brave_tab_menu_model.h", "brave_tab_prefs.cc", "brave_tab_prefs.h", "brave_tab_strip_model.cc", "brave_tab_strip_model.h", + "brave_tab_style.h", "brave_vertical_tab_color_mixer.cc", "brave_vertical_tab_color_mixer.h", "features.cc", diff --git a/browser/ui/tabs/brave_tab_layout_constants.h b/browser/ui/tabs/brave_tab_layout_constants.h new file mode 100644 index 000000000000..fc625c716a28 --- /dev/null +++ b/browser/ui/tabs/brave_tab_layout_constants.h @@ -0,0 +1,54 @@ +/* Copyright (c) 2023 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/. */ + +#ifndef BRAVE_BROWSER_UI_TABS_BRAVE_TAB_LAYOUT_CONSTANTS_H_ +#define BRAVE_BROWSER_UI_TABS_BRAVE_TAB_LAYOUT_CONSTANTS_H_ + +namespace brave_tabs { + +// Horizontal tab layout: +// +// The upstream tab implemenation assumes that tab view bounds overlap. In order +// to create a gap between tabs without violating these assumptions, tabs views +// are given a small overlap. Rounded tab rectangles are drawn centered and +// inset horizontally by an amount that will create the required visual gap. + +// The amount of space before the first tab. +constexpr int kHorizontalTabStripLeftMargin = 8; + +// The amount of vertical spacing between the top and bottom of tabs and the +// bounds of the tab strip region. The portion of this space below tabs will be +// occupied by tab group underlines. +constexpr int kHorizontalTabStripVerticalSpacing = 4; + +// The visual gap between tabs. +constexpr int kHorizontalTabGap = 4; + +// The amount of overlap between tabs. Based on upstream assumptions, tab views +// must have a non-negative overlap. Furthermore, tab separators will not render +// correctly if the tab overlap is zero. +constexpr int kHorizontalTabOverlap = 2; + +// The horizontal difference between the edge of the tab view and the visual +// edge of the rendered tab. +constexpr int kHorizontalTabInset = + (kHorizontalTabGap + kHorizontalTabOverlap) / 2; + +// The content padding within a tab. +constexpr int kHorizontalTabPadding = 6; + +// The horizontal difference between the visual edge of a tab group and the +// bounds of the group underline. +constexpr int kHorizontalGroupUnderlineInset = 2; + +// The tab border radius. +constexpr int kTabBorderRadius = 8; + +// The size of the group header slot when the title is empty. +constexpr int kEmptyGroupTitleSize = 22; + +} // namespace brave_tabs + +#endif // BRAVE_BROWSER_UI_TABS_BRAVE_TAB_LAYOUT_CONSTANTS_H_ diff --git a/browser/ui/tabs/brave_tab_style.h b/browser/ui/tabs/brave_tab_style.h new file mode 100644 index 000000000000..34783f946f13 --- /dev/null +++ b/browser/ui/tabs/brave_tab_style.h @@ -0,0 +1,57 @@ +/* Copyright (c) 2023 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/. */ + +#ifndef BRAVE_BROWSER_UI_TABS_BRAVE_TAB_STYLE_H_ +#define BRAVE_BROWSER_UI_TABS_BRAVE_TAB_STYLE_H_ + +#include "brave/browser/ui/tabs/brave_tab_layout_constants.h" +#include "brave/browser/ui/tabs/features.h" +#include "chrome/browser/ui/layout_constants.h" +#include "ui/gfx/geometry/insets.h" + +// A subclass of TabStyle used to customize tab layout and visuals. It is +// implemented as a template because it must be included in the source file +// override before the base class definition. +template +class BraveTabStyle : public TabStyleBase { + public: + int GetTabOverlap() const override { + if (!tabs::features::HorizontalTabsUpdateEnabled()) { + return TabStyleBase::GetTabOverlap(); + } + return brave_tabs::kHorizontalTabOverlap; + } + + int GetTopCornerRadius() const override { + if (!tabs::features::HorizontalTabsUpdateEnabled()) { + return TabStyleBase::GetTopCornerRadius(); + } + return brave_tabs::kTabBorderRadius; + } + + int GetBottomCornerRadius() const override { + if (!tabs::features::HorizontalTabsUpdateEnabled()) { + return TabStyleBase::GetBottomCornerRadius(); + } + return brave_tabs::kTabBorderRadius; + } + + gfx::Insets GetContentsInsets() const override { + if (!tabs::features::HorizontalTabsUpdateEnabled()) { + return TabStyleBase::GetContentsInsets(); + } + return gfx::Insets::VH( + 0, brave_tabs::kHorizontalTabPadding + brave_tabs::kHorizontalTabInset); + } + + int GetPinnedWidth() const override { + if (!tabs::features::HorizontalTabsUpdateEnabled()) { + return TabStyleBase::GetPinnedWidth(); + } + return GetLayoutConstant(TAB_HEIGHT) + brave_tabs::kHorizontalTabInset * 2; + } +}; + +#endif // BRAVE_BROWSER_UI_TABS_BRAVE_TAB_STYLE_H_ diff --git a/browser/ui/tabs/features.cc b/browser/ui/tabs/features.cc index 70cbd8c613d1..c1c543967648 100644 --- a/browser/ui/tabs/features.cc +++ b/browser/ui/tabs/features.cc @@ -17,4 +17,12 @@ BASE_FEATURE(kBraveSharedPinnedTabs, "BraveSharedPinnedTabs", base::FEATURE_DISABLED_BY_DEFAULT); +BASE_FEATURE(kBraveHorizontalTabsUpdate, + "BraveHorizontalTabsUpdate", + base::FEATURE_DISABLED_BY_DEFAULT); + +bool HorizontalTabsUpdateEnabled() { + return base::FeatureList::IsEnabled(kBraveHorizontalTabsUpdate); +} + } // namespace tabs::features diff --git a/browser/ui/tabs/features.h b/browser/ui/tabs/features.h index f3708f4e66b3..4665294bf487 100644 --- a/browser/ui/tabs/features.h +++ b/browser/ui/tabs/features.h @@ -17,6 +17,10 @@ BASE_DECLARE_FEATURE(kBraveChangeActiveTabOnScrollEvent); BASE_DECLARE_FEATURE(kBraveSharedPinnedTabs); +BASE_DECLARE_FEATURE(kBraveHorizontalTabsUpdate); + +bool HorizontalTabsUpdateEnabled(); + } // namespace tabs::features #endif // BRAVE_BROWSER_UI_TABS_FEATURES_H_ diff --git a/browser/ui/views/frame/brave_browser_non_client_frame_view_mac.mm b/browser/ui/views/frame/brave_browser_non_client_frame_view_mac.mm index 7b20d4adacf2..56e4e684001a 100644 --- a/browser/ui/views/frame/brave_browser_non_client_frame_view_mac.mm +++ b/browser/ui/views/frame/brave_browser_non_client_frame_view_mac.mm @@ -8,6 +8,7 @@ #include "brave/browser/ui/views/frame/brave_browser_non_client_frame_view_mac.h" #include "brave/browser/ui/tabs/brave_tab_prefs.h" +#include "brave/browser/ui/tabs/features.h" #include "brave/browser/ui/views/frame/brave_non_client_hit_test_helper.h" #include "brave/browser/ui/views/frame/brave_window_frame_graphic.h" #include "brave/browser/ui/views/tabs/vertical_tab_utils.h" @@ -66,7 +67,13 @@ return 30; } - return BrowserNonClientFrameViewMac::GetTopInset(restored); + if (!tabs::features::HorizontalTabsUpdateEnabled()) { + return BrowserNonClientFrameViewMac::GetTopInset(restored); + } + + // The tab region view maintains its own padding, but insert a small gap to + // give a bit more room for the frame resize handle. + return 2; } bool BraveBrowserNonClientFrameViewMac::ShouldShowWindowTitleForVerticalTabs() diff --git a/browser/ui/views/frame/vertical_tab_strip_region_view.cc b/browser/ui/views/frame/vertical_tab_strip_region_view.cc index 83711347622d..13a1956e74b4 100644 --- a/browser/ui/views/frame/vertical_tab_strip_region_view.cc +++ b/browser/ui/views/frame/vertical_tab_strip_region_view.cc @@ -309,7 +309,7 @@ class VerticalTabNewTabButton : public BraveNewTabButton { float scale, bool extend_to_top) const override { auto contents_bounds = GetContentsBounds(); - const float radius = tabs::kUnpinnedTabBorderRadius * scale; + const float radius = GetCornerRadius() * scale; SkPath path; const gfx::Rect path_rect(origin.x(), origin.y(), contents_bounds.width() * scale, diff --git a/browser/ui/views/tabs/brave_compound_tab_container.cc b/browser/ui/views/tabs/brave_compound_tab_container.cc index 4dc735227daf..3aa9e9dbf1a0 100644 --- a/browser/ui/views/tabs/brave_compound_tab_container.cc +++ b/browser/ui/views/tabs/brave_compound_tab_container.cc @@ -138,6 +138,7 @@ void BraveCompoundTabContainer::SetScrollEnabled(bool enabled) { if (enabled) { scroll_view_ = AddChildView(std::make_unique()); + scroll_view_->SetBackgroundThemeColorId(kColorToolbar); auto* contents_view = scroll_view_->SetContents(std::make_unique(this)); contents_view->AddChildView(base::to_address(unpinned_tab_container_)); diff --git a/browser/ui/views/tabs/brave_new_tab_button.cc b/browser/ui/views/tabs/brave_new_tab_button.cc index 6a5131547e61..adc9144f3202 100644 --- a/browser/ui/views/tabs/brave_new_tab_button.cc +++ b/browser/ui/views/tabs/brave_new_tab_button.cc @@ -6,19 +6,26 @@ #include "brave/browser/ui/views/tabs/brave_new_tab_button.h" #include -#include #include -#include "brave/browser/ui/color/brave_color_id.h" -#include "chrome/app/chrome_command_ids.h" -#include "chrome/browser/ui/layout_constants.h" +#include "brave/browser/ui/tabs/features.h" +#include "brave/components/vector_icons/vector_icons.h" #include "chrome/browser/ui/views/tabs/new_tab_button.h" #include "chrome/browser/ui/views/tabs/tab_strip.h" #include "ui/gfx/geometry/skia_conversions.h" +#include "ui/gfx/paint_vector_icon.h" #include "ui/gfx/scoped_canvas.h" +#include "ui/views/view_class_properties.h" + +using tabs::features::HorizontalTabsUpdateEnabled; // static -const gfx::Size BraveNewTabButton::kButtonSize{24, 24}; +gfx::Size BraveNewTabButton::GetButtonSize() { + if (!HorizontalTabsUpdateEnabled()) { + return {24, 24}; + } + return {28, 28}; +} // static SkPath BraveNewTabButton::GetBorderPath(const gfx::Point& origin, @@ -44,7 +51,7 @@ SkPath BraveNewTabButton::GetBorderPath(const gfx::Point& origin, gfx::Size BraveNewTabButton::CalculatePreferredSize() const { // Overriden so that we use Brave's custom button size - gfx::Size size = kButtonSize; + gfx::Size size = GetButtonSize(); const auto insets = GetInsets(); size.Enlarge(insets.width(), insets.height()); return size; @@ -59,12 +66,31 @@ SkPath BraveNewTabButton::GetBorderPath(const gfx::Point& origin, BraveNewTabButton::BraveNewTabButton(TabStrip* tab_strip, PressedCallback callback) - : NewTabButton(tab_strip, std::move(callback)) {} + : NewTabButton(tab_strip, std::move(callback)) { + if (HorizontalTabsUpdateEnabled()) { + // Ensure that the new tab button is vertically centered within its flex + // layout container. + SetProperty(views::kCrossAxisAlignmentKey, views::LayoutAlignment::kCenter); + } +} BraveNewTabButton::~BraveNewTabButton() = default; void BraveNewTabButton::PaintIcon(gfx::Canvas* canvas) { gfx::ScopedCanvas scoped_canvas(canvas); + + if (HorizontalTabsUpdateEnabled()) { + // Instead of letting `NewTabButton` draw a "plus", paint a vector icon to + // the canvas in the center of the view. + constexpr int kIconSize = 16; + gfx::Rect bounds = GetContentsBounds(); + canvas->Translate(gfx::Vector2d((bounds.width() - kIconSize) / 2, + (bounds.height() - kIconSize) / 2)); + gfx::PaintVectorIcon(canvas, kLeoPlusAddIcon, kIconSize, + GetForegroundColor()); + return; + } + // Shim base implementation's painting // Overriden to fix chromium assumption that border radius // will be 50% of width. diff --git a/browser/ui/views/tabs/brave_new_tab_button.h b/browser/ui/views/tabs/brave_new_tab_button.h index 24fccea58c31..76b7632283ff 100644 --- a/browser/ui/views/tabs/brave_new_tab_button.h +++ b/browser/ui/views/tabs/brave_new_tab_button.h @@ -21,15 +21,14 @@ class BraveNewTabButton : public NewTabButton { // These static members are shared with BraveTabSearchButton // TODO(sko) If we could make TabSearchButton inherit BraveNewTabButton, // we might not need these any more. - static const gfx::Size kButtonSize; + static gfx::Size GetButtonSize(); static SkPath GetBorderPath(const gfx::Point& origin, float scale, bool extend_to_top, int border_radius, const gfx::Size& contents_bounds); + BraveNewTabButton(TabStrip* tab_strip, PressedCallback callback); - BraveNewTabButton(const BraveNewTabButton&) = delete; - BraveNewTabButton& operator=(const BraveNewTabButton&) = delete; ~BraveNewTabButton() override; protected: diff --git a/browser/ui/views/tabs/brave_tab.cc b/browser/ui/views/tabs/brave_tab.cc index 7388604f3911..416bb1a99027 100644 --- a/browser/ui/views/tabs/brave_tab.cc +++ b/browser/ui/views/tabs/brave_tab.cc @@ -8,6 +8,7 @@ #include #include "brave/browser/ui/tabs/brave_tab_prefs.h" +#include "brave/browser/ui/tabs/features.h" #include "brave/browser/ui/views/frame/brave_browser_view.h" #include "brave/browser/ui/views/frame/vertical_tab_strip_region_view.h" #include "brave/browser/ui/views/frame/vertical_tab_strip_widget_delegate_view.h" @@ -90,10 +91,8 @@ class ShadowLayer : public ui::Layer, public ui::LayerDelegate { // bounds. gfx::Rect shadow_bounds(size()); shadow_bounds.Inset(GetBlurRegionInsets()); - const int kCornerRadius = - (tab_->data().pinned ? tabs::kPinnedTabBorderRadius - : tabs::kUnpinnedTabBorderRadius); - recorder.canvas()->DrawRoundRect(shadow_bounds, kCornerRadius, flags); + const int radius = tabs::GetTabCornerRadius(*tab_); + recorder.canvas()->DrawRoundRect(shadow_bounds, radius, flags); } void OnDeviceScaleFactorChanged(float old_device_scale_factor, @@ -160,7 +159,12 @@ absl::optional BraveTab::GetGroupColor() const { return {}; } - return Tab::GetGroupColor(); + if (!tabs::features::HorizontalTabsUpdateEnabled()) { + return Tab::GetGroupColor(); + } + + // Unlike upstream, tabs that are within a group are not given a border color. + return {}; } void BraveTab::UpdateIconVisibility() { @@ -288,8 +292,11 @@ bool BraveTab::IsAtMinWidthForVerticalTabStrip() const { } void BraveTab::UpdateShadowForActiveTab() { - if (IsActive() && - tabs::utils::ShouldShowVerticalTabs(controller()->GetBrowser())) { + bool can_render_shadows = + tabs::features::HorizontalTabsUpdateEnabled() || + tabs::utils::ShouldShowVerticalTabs(controller()->GetBrowser()); + + if (IsActive() && can_render_shadows) { shadow_layer_ = CreateShadowLayer(); AddLayerToBelowThis(); LayoutShadowLayer(); diff --git a/browser/ui/views/tabs/brave_tab_container.cc b/browser/ui/views/tabs/brave_tab_container.cc index 9ad4b399f548..1ec78239ba8d 100644 --- a/browser/ui/views/tabs/brave_tab_container.cc +++ b/browser/ui/views/tabs/brave_tab_container.cc @@ -11,6 +11,7 @@ #include "base/check_is_test.h" #include "base/containers/flat_map.h" +#include "brave/browser/ui/tabs/brave_tab_layout_constants.h" #include "brave/browser/ui/tabs/brave_tab_prefs.h" #include "brave/browser/ui/tabs/features.h" #include "brave/browser/ui/views/frame/brave_browser_view.h" @@ -32,6 +33,20 @@ #include "ui/gfx/skbitmap_operations.h" #include "ui/views/view_utils.h" +namespace { + +gfx::Size AddHorizontalTabStripSpacing(gfx::Size size) { + if (!tabs::features::HorizontalTabsUpdateEnabled()) { + return size; + } + // Allow for a small space at the top and bottom of the tab strip. Tab group + // underlines will partially occupy the space below tabs. + size.Enlarge(0, brave_tabs::kHorizontalTabStripVerticalSpacing * 2); + return size; +} + +} // namespace + BraveTabContainer::BraveTabContainer( TabContainerController& controller, TabHoverCardController* hover_card_controller, @@ -91,6 +106,14 @@ base::OnceClosure BraveTabContainer::LockLayout() { base::Unretained(this)); } +gfx::Size BraveTabContainer::GetMinimumSize() const { + gfx::Size size = TabContainerImpl::GetMinimumSize(); + if (tabs::utils::ShouldShowVerticalTabs(tab_slot_controller_->GetBrowser())) { + return size; + } + return AddHorizontalTabStripSpacing(size); +} + gfx::Size BraveTabContainer::CalculatePreferredSize() const { // Note that we check this before checking currently we're in vertical tab // strip mode. We might be in the middle of changing orientation. @@ -100,7 +123,8 @@ gfx::Size BraveTabContainer::CalculatePreferredSize() const { if (!tabs::utils::ShouldShowVerticalTabs( tab_slot_controller_->GetBrowser())) { - return TabContainerImpl::CalculatePreferredSize(); + return AddHorizontalTabStripSpacing( + TabContainerImpl::CalculatePreferredSize()); } const int tab_count = tabs_view_model_.view_size(); @@ -277,16 +301,6 @@ void BraveTabContainer::CompleteAnimationAndLayout() { base::ranges::for_each(children(), &views::View::Layout); } -void BraveTabContainer::OnPaintBackground(gfx::Canvas* canvas) { - if (!tabs::utils::ShouldShowVerticalTabs( - tab_slot_controller_->GetBrowser())) { - TabContainerImpl::OnPaintBackground(canvas); - return; - } - - canvas->DrawColor(GetColorProvider()->GetColor(kColorToolbar)); -} - void BraveTabContainer::PaintChildren(const views::PaintInfo& paint_info) { // Exclude tabs that own layer. std::vector orderable_children; diff --git a/browser/ui/views/tabs/brave_tab_container.h b/browser/ui/views/tabs/brave_tab_container.h index 1c4b18e31d98..655e6c943576 100644 --- a/browser/ui/views/tabs/brave_tab_container.h +++ b/browser/ui/views/tabs/brave_tab_container.h @@ -32,6 +32,7 @@ class BraveTabContainer : public TabContainerImpl { base::OnceClosure LockLayout(); // TabContainerImpl: + gfx::Size GetMinimumSize() const override; gfx::Size CalculatePreferredSize() const override; void UpdateClosingModeOnRemovedTab(int model_index, bool was_active) override; gfx::Rect GetTargetBoundsForClosingTab(Tab* tab, @@ -43,7 +44,6 @@ class BraveTabContainer : public TabContainerImpl { void RemoveTab(int index, bool was_active) override; void OnTabCloseAnimationCompleted(Tab* tab) override; void CompleteAnimationAndLayout() override; - void OnPaintBackground(gfx::Canvas* canvas) override; void PaintChildren(const views::PaintInfo& paint_info) override; // BrowserRootView::DropTarget diff --git a/browser/ui/views/tabs/brave_tab_group_header.cc b/browser/ui/views/tabs/brave_tab_group_header.cc index 93ceb84f6fb2..e6e4a6b364f9 100644 --- a/browser/ui/views/tabs/brave_tab_group_header.cc +++ b/browser/ui/views/tabs/brave_tab_group_header.cc @@ -5,6 +5,7 @@ #include "brave/browser/ui/views/tabs/brave_tab_group_header.h" +#include "brave/browser/ui/tabs/features.h" #include "brave/browser/ui/views/tabs/vertical_tab_utils.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/tabs/tab_group_model.h" @@ -20,9 +21,8 @@ namespace { -SkColor GetGroupBackgroundColorForVerticalTabs( - const tab_groups::TabGroupId& group_id, - TabSlotController* controller) { +SkColor GetGroupBackgroundColor(const tab_groups::TabGroupId& group_id, + TabSlotController* controller) { if (!controller->GetBrowser() ->tab_strip_model() ->group_model() @@ -60,11 +60,13 @@ void BraveTabGroupHeader::AddedToWidget() { void BraveTabGroupHeader::VisualsChanged() { TabGroupHeader::VisualsChanged(); - if (!ShouldShowVerticalTabs()) { + + if (!tabs::features::HorizontalTabsUpdateEnabled() && + !ShouldShowVerticalTabs()) { return; } - title_->SetEnabledColor(GetGroupBackgroundColorForVerticalTabs( + title_->SetEnabledColor(GetGroupBackgroundColor( group().value(), base::to_address(tab_slot_controller_))); title_->SetSubpixelRenderingEnabled(false); @@ -72,19 +74,18 @@ void BraveTabGroupHeader::VisualsChanged() { title_->SetFontList(font_list.DeriveWithWeight(gfx::Font::Weight::MEDIUM) .DeriveWithSizeDelta(13 - font_list.GetFontSize())); - // We don't draw background for vertical tabs. title_chip_->SetBackground(nullptr); - LayoutTitleChip(); + if (ShouldShowVerticalTabs()) { + LayoutTitleChipForVerticalTabs(); + } } void BraveTabGroupHeader::Layout() { TabGroupHeader::Layout(); - if (!ShouldShowVerticalTabs()) { - return; + if (ShouldShowVerticalTabs()) { + LayoutTitleChipForVerticalTabs(); } - - LayoutTitleChip(); } bool BraveTabGroupHeader::ShouldShowVerticalTabs() const { @@ -92,7 +93,7 @@ bool BraveTabGroupHeader::ShouldShowVerticalTabs() const { tab_slot_controller_->GetBrowser()); } -void BraveTabGroupHeader::LayoutTitleChip() { +void BraveTabGroupHeader::LayoutTitleChipForVerticalTabs() { auto title_bounds = GetContentsBounds(); title_bounds.Inset(gfx::Insets(kPaddingForGroup * 2)); title_chip_->SetBoundsRect(title_bounds); diff --git a/browser/ui/views/tabs/brave_tab_group_header.h b/browser/ui/views/tabs/brave_tab_group_header.h index 5ff10675106c..00635389ac95 100644 --- a/browser/ui/views/tabs/brave_tab_group_header.h +++ b/browser/ui/views/tabs/brave_tab_group_header.h @@ -28,7 +28,7 @@ class BraveTabGroupHeader : public TabGroupHeader { private: bool ShouldShowVerticalTabs() const; - void LayoutTitleChip(); + void LayoutTitleChipForVerticalTabs(); }; #endif // BRAVE_BROWSER_UI_VIEWS_TABS_BRAVE_TAB_GROUP_HEADER_H_ diff --git a/browser/ui/views/tabs/brave_tab_group_highlight.cc b/browser/ui/views/tabs/brave_tab_group_highlight.cc index 9a620817e990..8cd2e33c20ca 100644 --- a/browser/ui/views/tabs/brave_tab_group_highlight.cc +++ b/browser/ui/views/tabs/brave_tab_group_highlight.cc @@ -5,6 +5,8 @@ #include "brave/browser/ui/views/tabs/brave_tab_group_highlight.h" +#include "brave/browser/ui/tabs/brave_tab_layout_constants.h" +#include "brave/browser/ui/tabs/features.h" #include "brave/browser/ui/views/tabs/brave_tab_group_header.h" #include "brave/browser/ui/views/tabs/vertical_tab_utils.h" #include "chrome/browser/ui/views/tabs/tab_group_views.h" @@ -12,10 +14,24 @@ BraveTabGroupHighlight::~BraveTabGroupHighlight() = default; SkPath BraveTabGroupHighlight::GetPath() const { - if (!tabs::utils::ShouldShowVerticalTabs(tab_group_views_->GetBrowser())) { + // We don't have to paint a highlight for vertical tabs. + if (tabs::utils::ShouldShowVerticalTabs(tab_group_views_->GetBrowser())) { + return {}; + } + + if (!tabs::features::HorizontalTabsUpdateEnabled()) { return TabGroupHighlight::GetPath(); } - // We don't have to paint highlight for vertical tabs - return {}; + // Draw a rounded rect that encloses the header and all tabs within the + // group. + float tab_top = 0; + float tab_left = brave_tabs::kHorizontalTabInset; + float tab_right = bounds().width() - brave_tabs::kHorizontalTabInset; + float tab_bottom = bounds().height(); + float radius = brave_tabs::kTabBorderRadius; + + SkPath path; + path.addRoundRect({tab_left, tab_top, tab_right, tab_bottom}, radius, radius); + return path; } diff --git a/browser/ui/views/tabs/brave_tab_group_underline.cc b/browser/ui/views/tabs/brave_tab_group_underline.cc index 35a992d79344..cf4061f7fac5 100644 --- a/browser/ui/views/tabs/brave_tab_group_underline.cc +++ b/browser/ui/views/tabs/brave_tab_group_underline.cc @@ -7,6 +7,8 @@ #include +#include "brave/browser/ui/tabs/brave_tab_layout_constants.h" +#include "brave/browser/ui/tabs/features.h" #include "brave/browser/ui/views/tabs/brave_tab_group_header.h" #include "brave/browser/ui/views/tabs/vertical_tab_utils.h" #include "cc/paint/paint_flags.h" @@ -14,6 +16,7 @@ #include "chrome/browser/ui/views/tabs/tab_group_views.h" #include "ui/gfx/canvas.h" #include "ui/gfx/geometry/skia_conversions.h" +#include "ui/views/view_utils.h" BraveTabGroupUnderline::BraveTabGroupUnderline( TabGroupViews* tab_group_views, @@ -44,10 +47,21 @@ void BraveTabGroupUnderline::UpdateBounds(const views::View* leading_view, gfx::Insets BraveTabGroupUnderline::GetInsetsForUnderline( const views::View* sibling_view) const { - if (!ShouldShowVerticalTabs()) + if (ShouldShowVerticalTabs()) { + return {}; + } + + if (!tabs::features::HorizontalTabsUpdateEnabled()) { return TabGroupUnderline::GetInsetsForUnderline(sibling_view); + } - return {}; + // For horizontal tabs, the underline should be inset slightly within the + // visual edges of the tab. + int horizontal_inset = TabGroupUnderline::kStrokeThickness + + brave_tabs::kHorizontalTabInset + + brave_tabs::kHorizontalGroupUnderlineInset; + + return gfx::Insets::VH(0, horizontal_inset); } gfx::Rect BraveTabGroupUnderline::CalculateTabGroupUnderlineBounds( @@ -55,8 +69,17 @@ gfx::Rect BraveTabGroupUnderline::CalculateTabGroupUnderlineBounds( const views::View* const leading_view, const views::View* const trailing_view) const { if (!ShouldShowVerticalTabs()) { - return TabGroupUnderline::CalculateTabGroupUnderlineBounds( + auto bounds = TabGroupUnderline::CalculateTabGroupUnderlineBounds( underline_view, leading_view, trailing_view); + + if (tabs::features::HorizontalTabsUpdateEnabled()) { + // Upstream places the underline at the bottom tab border. Push the + // underline down to the bottom of the tab strip, so that it will appear + // below the tabs. + bounds.Offset(0, brave_tabs::kHorizontalTabStripVerticalSpacing); + } + + return bounds; } // override bounds for vertical tabs mode. diff --git a/browser/ui/views/tabs/brave_tab_search_button.cc b/browser/ui/views/tabs/brave_tab_search_button.cc index 6189ccf89950..195a55aa5b15 100644 --- a/browser/ui/views/tabs/brave_tab_search_button.cc +++ b/browser/ui/views/tabs/brave_tab_search_button.cc @@ -5,11 +5,12 @@ #include "brave/browser/ui/views/tabs/brave_tab_search_button.h" -#include #include +#include "brave/browser/ui/tabs/features.h" #include "brave/browser/ui/views/brave_tab_search_bubble_host.h" #include "brave/browser/ui/views/tabs/brave_new_tab_button.h" +#include "brave/components/vector_icons/vector_icons.h" #include "chrome/browser/ui/views/chrome_layout_provider.h" #include "chrome/browser/ui/views/tabs/new_tab_button.h" #include "chrome/browser/ui/views/tabs/tab_strip_controller.h" @@ -29,7 +30,12 @@ BraveTabSearchButton::BraveTabSearchButton(TabStrip* tab_strip) BraveTabSearchButton::~BraveTabSearchButton() = default; gfx::Size BraveTabSearchButton::CalculatePreferredSize() const { - return BraveNewTabButton::kButtonSize; + auto size = BraveNewTabButton::GetButtonSize(); + if (tabs::features::HorizontalTabsUpdateEnabled()) { + auto insets = GetInsets(); + size.Enlarge(insets.width(), insets.height()); + } + return size; } void BraveTabSearchButton::SetBubbleArrow(views::BubbleBorder::Arrow arrow) { @@ -37,6 +43,26 @@ void BraveTabSearchButton::SetBubbleArrow(views::BubbleBorder::Arrow arrow) { ->SetBubbleArrow(arrow); } +void BraveTabSearchButton::UpdateColors() { + TabSearchButton::UpdateColors(); + + if (!tabs::features::HorizontalTabsUpdateEnabled()) { + return; + } + + // Use a custom icon for tab search. + constexpr int kIconSize = 16; + SetImageModel(views::Button::STATE_NORMAL, + ui::ImageModel::FromVectorIcon( + kLeoSearchIcon, GetForegroundColor(), kIconSize)); + SetImageModel(views::Button::STATE_HOVERED, ui::ImageModel()); + SetImageModel(views::Button::STATE_PRESSED, ui::ImageModel()); + + // Unset any backgrounds or borders. + SetBorder(nullptr); + SetBackground(nullptr); +} + int BraveTabSearchButton::GetCornerRadius() const { return ChromeLayoutProvider::Get()->GetCornerRadiusMetric( views::Emphasis::kMaximum, GetContentsBounds().size()); diff --git a/browser/ui/views/tabs/brave_tab_search_button.h b/browser/ui/views/tabs/brave_tab_search_button.h index b18818d2d21e..39479abe7941 100644 --- a/browser/ui/views/tabs/brave_tab_search_button.h +++ b/browser/ui/views/tabs/brave_tab_search_button.h @@ -21,7 +21,8 @@ class BraveTabSearchButton : public TabSearchButton { void SetBubbleArrow(views::BubbleBorder::Arrow arrow); - // TabSearchButton overrides: + // TabSearchButton: + void UpdateColors() override; gfx::Size CalculatePreferredSize() const override; int GetCornerRadius() const override; }; diff --git a/browser/ui/views/tabs/brave_tab_strip.cc b/browser/ui/views/tabs/brave_tab_strip.cc index caee32658121..8fcbe3fe1487 100644 --- a/browser/ui/views/tabs/brave_tab_strip.cc +++ b/browser/ui/views/tabs/brave_tab_strip.cc @@ -10,6 +10,7 @@ #include "brave/browser/profiles/profile_util.h" #include "brave/browser/themes/brave_dark_mode_utils.h" #include "brave/browser/ui/color/brave_color_id.h" +#include "brave/browser/ui/tabs/brave_tab_layout_constants.h" #include "brave/browser/ui/tabs/brave_tab_prefs.h" #include "brave/browser/ui/tabs/features.h" #include "brave/browser/ui/tabs/shared_pinned_tab_service.h" @@ -37,10 +38,20 @@ #include "third_party/skia/include/core/SkColor.h" #include "ui/base/metadata/metadata_impl_macros.h" #include "ui/compositor/layer.h" +#include "ui/gfx/canvas.h" #include "ui/views/layout/flex_layout.h" +using tabs::features::HorizontalTabsUpdateEnabled; + BraveTabStrip::BraveTabStrip(std::unique_ptr controller) - : TabStrip(std::move(controller)) {} + : TabStrip(std::move(controller)) { + if (HorizontalTabsUpdateEnabled()) { + // In order for active tab shadow effects to be applied consistently across + // platforms, an ancestor of the active tab and shadow need to be painted to + // a layer. + SetPaintToLayer(); + } +} BraveTabStrip::~BraveTabStrip() = default; @@ -80,6 +91,12 @@ bool BraveTabStrip::ShouldDrawStrokes() const { return false; } + if (HorizontalTabsUpdateEnabled()) { + // We never automatically draw strokes around tabs. For pinned tabs, we draw + // the stroke when generating the tab drawing path. + return false; + } + if (!TabStrip::ShouldDrawStrokes()) { return false; } @@ -105,7 +122,7 @@ bool BraveTabStrip::ShouldDrawStrokes() const { } int BraveTabStrip::GetStrokeThickness() const { - if (ShouldShowVerticalTabs()) { + if (ShouldShowVerticalTabs() || HorizontalTabsUpdateEnabled()) { // Bypass checking ShouldDrawStrokes(). return 1; } @@ -163,6 +180,8 @@ void BraveTabStrip::MaybeStartDrag( void BraveTabStrip::AddedToWidget() { TabStrip::AddedToWidget(); + UpdateTabStripMargins(); + if (BrowserView::GetBrowserViewForBrowser(GetBrowser())) { UpdateTabContainer(); } else { @@ -375,6 +394,29 @@ void BraveTabStrip::UpdateTabContainer() { } } +void BraveTabStrip::UpdateTabStripMargins() { + if (!HorizontalTabsUpdateEnabled()) { + return; + } + + gfx::Insets margins; + + if (!ShouldShowVerticalTabs()) { + // There should be a medium size gap between the left edge of the tabstrip + // and the visual left edge of the first tab. Set a left margin that takes + // into account the visual tab inset. + margins.set_left(brave_tabs::kHorizontalTabStripLeftMargin - + brave_tabs::kHorizontalTabInset); + DCHECK_GE(margins.left(), 0); + + // Set a top margin to match the space under tabs (where the group underline + // is rendered), so that everything remains centered. + margins.set_top(brave_tabs::kHorizontalTabStripVerticalSpacing); + } + + SetProperty(views::kMarginsKey, margins); +} + bool BraveTabStrip::ShouldShowVerticalTabs() const { return tabs::utils::ShouldShowVerticalTabs(GetBrowser()); } @@ -397,5 +439,17 @@ void BraveTabStrip::Layout() { TabStrip::Layout(); } +void BraveTabStrip::OnPaintBackground(gfx::Canvas* canvas) { + // Unlike upstream, we are painting this view to an opaque layer in order to + // support layer-based shadows under the active tab. Paint a background so + // that all pixels are painted appropriately. + ui::ColorId color_id = ShouldShowVerticalTabs() ? kColorToolbar + : GetWidget()->ShouldPaintAsActive() + ? kColorTabBackgroundInactiveFrameActive + : kColorTabBackgroundInactiveFrameInactive; + + canvas->DrawColor(GetColorProvider()->GetColor(color_id)); +} + BEGIN_METADATA(BraveTabStrip, TabStrip) END_METADATA diff --git a/browser/ui/views/tabs/brave_tab_strip.h b/browser/ui/views/tabs/brave_tab_strip.h index 2382c7a384c4..fee0d06262ef 100644 --- a/browser/ui/views/tabs/brave_tab_strip.h +++ b/browser/ui/views/tabs/brave_tab_strip.h @@ -37,6 +37,7 @@ class BraveTabStrip : public TabStrip { FRIEND_TEST_ALL_PREFIXES(ColorPaletteTest, LightThemeMinimumContrast); void UpdateTabContainer(); + void UpdateTabStripMargins(); bool ShouldShowVerticalTabs() const; // TabStrip overrides: @@ -44,6 +45,7 @@ class BraveTabStrip : public TabStrip { bool ShouldDrawStrokes() const override; int GetStrokeThickness() const override; void Layout() override; + void OnPaintBackground(gfx::Canvas* canvas) override; // Exposed for testing. static constexpr float kBraveMinimumContrastRatioForOutlines = 1.2797f; diff --git a/browser/ui/views/tabs/brave_tab_strip_layout_helper.cc b/browser/ui/views/tabs/brave_tab_strip_layout_helper.cc index 477320131e6e..fa49eb2fabd0 100644 --- a/browser/ui/views/tabs/brave_tab_strip_layout_helper.cc +++ b/browser/ui/views/tabs/brave_tab_strip_layout_helper.cc @@ -7,6 +7,8 @@ #include +#include "brave/browser/ui/tabs/brave_tab_layout_constants.h" +#include "brave/browser/ui/tabs/features.h" #include "brave/browser/ui/views/tabs/brave_tab_group_header.h" #include "brave/browser/ui/views/tabs/brave_tab_strip.h" #include "chrome/browser/ui/layout_constants.h" @@ -109,6 +111,14 @@ void CalculateVerticalLayout(const TabLayoutConstants& layout_constants, } // namespace +int GetTabCornerRadius(const Tab& tab) { + if (!tabs::features::HorizontalTabsUpdateEnabled()) { + return tab.data().pinned ? 8 : 4; + } + + return brave_tabs::kTabBorderRadius; +} + std::vector CalculateVerticalTabBounds( const TabLayoutConstants& layout_constants, const std::vector& tabs, @@ -127,6 +137,22 @@ std::vector CalculateVerticalTabBounds( return bounds; } +std::vector CalculateBoundsForHorizontalDraggedViews( + const std::vector& views, + TabStrip* tab_strip) { + // Chromium aligns the dragged tabs to the bottom of the tab strip, whereas we + // need to keep the tabs aligned to the top. + std::vector bounds; + const int overlap = TabStyle::Get()->GetTabOverlap(); + int x = 0; + for (const TabSlotView* view : views) { + const int width = view->width(); + bounds.emplace_back(x, 0, width, view->height()); + x += width - overlap; + } + return bounds; +} + std::vector CalculateBoundsForVerticalDraggedViews( const std::vector& views, TabStrip* tab_strip) { diff --git a/browser/ui/views/tabs/brave_tab_strip_layout_helper.h b/browser/ui/views/tabs/brave_tab_strip_layout_helper.h index 63e54e35ccbe..6cdf8f28dea0 100644 --- a/browser/ui/views/tabs/brave_tab_strip_layout_helper.h +++ b/browser/ui/views/tabs/brave_tab_strip_layout_helper.h @@ -16,6 +16,7 @@ namespace gfx { class Rect; } // namespace gfx +class Tab; class TabStripLayoutHelper; class TabWidthConstraints; class TabStripController; @@ -31,8 +32,7 @@ constexpr int kVerticalTabMinWidth = kVerticalTabHeight; constexpr int kVerticalTabsSpacing = 4; constexpr int kMarginForVerticalTabContainers = kVerticalTabsSpacing; -constexpr int kUnpinnedTabBorderRadius = 4; -constexpr int kPinnedTabBorderRadius = 8; +int GetTabCornerRadius(const Tab& tab); std::vector CalculateVerticalTabBounds( const TabLayoutConstants& layout_constants, @@ -40,6 +40,10 @@ std::vector CalculateVerticalTabBounds( absl::optional width, bool is_floating_mode); +std::vector CalculateBoundsForHorizontalDraggedViews( + const std::vector& views, + TabStrip* tab_strip); + std::vector CalculateBoundsForVerticalDraggedViews( const std::vector& views, TabStrip* tab_strip); diff --git a/browser/ui/views/tabs/brave_tab_style_views.inc.cc b/browser/ui/views/tabs/brave_tab_style_views.inc.cc index 92de8fe5ddbb..fb65bbaee4f1 100644 --- a/browser/ui/views/tabs/brave_tab_style_views.inc.cc +++ b/browser/ui/views/tabs/brave_tab_style_views.inc.cc @@ -8,6 +8,8 @@ namespace { +using tabs::features::HorizontalTabsUpdateEnabled; + //////////////////////////////////////////////////////////////////////////////// // BraveGM2TabStyle // @@ -21,9 +23,6 @@ class BraveGM2TabStyle : public GM2TabStyleViews { protected: TabStyle::TabColors CalculateTargetColors() const override; - Tab* tab() { return base::to_address(tab_); } - const Tab* tab() const { return base::to_address(tab_); } - private: raw_ptr tab_; }; @@ -63,12 +62,14 @@ class BraveVerticalTabStyle : public BraveGM2TabStyle { bool force_active = false, TabStyle::RenderUnits render_units = TabStyle::RenderUnits::kPixels) const override; + + gfx::Insets GetContentsInsets() const override; TabStyle::SeparatorBounds GetSeparatorBounds(float scale) const override; + float GetSeparatorOpacity(bool for_layout, bool leading) const override; void PaintTab(gfx::Canvas* canvas) const override; private: bool ShouldShowVerticalTabs() const; - bool IsInGroupAndNotActive() const; SkColor GetTargetTabBackgroundColor( TabStyle::TabSelectionState selection_state, bool hovered) const override; @@ -82,7 +83,7 @@ SkPath BraveVerticalTabStyle::GetPath( float scale, bool force_active, TabStyle::RenderUnits render_units) const { - if (!ShouldShowVerticalTabs()) { + if (!HorizontalTabsUpdateEnabled() && !ShouldShowVerticalTabs()) { return BraveGM2TabStyle::GetPath(path_type, scale, force_active, render_units); } @@ -94,6 +95,15 @@ SkPath BraveVerticalTabStyle::GetPath( return {}; } + // Horizontal tabs should have a visual gap between them, even if their view + // bounds are touching or slightly overlapping. Create a visual gap by + // insetting the bounds of the tab by the required gap plus overlap before + // drawing the rectangle. + if (!ShouldShowVerticalTabs()) { + aligned_bounds.Inset( + gfx::InsetsF::VH(0, brave_tabs::kHorizontalTabInset * scale)); + } + #if DCHECK_IS_ON() if (tab()->bounds().height() != std::round(aligned_bounds.height() / scale)) { DLOG(ERROR) << "We don't want it to be off by 1 dip\n|height|: " @@ -109,8 +119,7 @@ SkPath BraveVerticalTabStyle::GetPath( float tab_left = aligned_bounds.x(); float tab_right = aligned_bounds.right(); float tab_bottom = aligned_bounds.bottom(); - int radius = - is_pinned ? tabs::kPinnedTabBorderRadius : tabs::kUnpinnedTabBorderRadius; + int radius = tabs::GetTabCornerRadius(*tab()); if (is_pinned) { // Only pinned tabs have border @@ -152,28 +161,111 @@ SkPath BraveVerticalTabStyle::GetPath( return path; } +gfx::Insets BraveVerticalTabStyle::GetContentsInsets() const { + if (!HorizontalTabsUpdateEnabled()) { + return BraveGM2TabStyle::GetContentsInsets(); + } + + // Ignore any stroke widths when determining the horizontal contents insets. + return tab_style()->GetContentsInsets(); +} + TabStyle::SeparatorBounds BraveVerticalTabStyle::GetSeparatorBounds( float scale) const { if (ShouldShowVerticalTabs()) { return {}; } - return BraveGM2TabStyle::GetSeparatorBounds(scale); + if (!HorizontalTabsUpdateEnabled()) { + return BraveGM2TabStyle::GetSeparatorBounds(scale); + } + + gfx::SizeF size(tab_style()->GetSeparatorSize()); + size.Scale(scale); + const gfx::RectF aligned_bounds = + ScaleAndAlignBounds(tab()->bounds(), scale, GetStrokeThickness()); + + // Note: `leading` bounds are used for rect corner radius calculation and so + // must be non-empty, even if we don't want to show it. + TabStyle::SeparatorBounds bounds; + bounds.leading = gfx::RectF(aligned_bounds.right(), + (aligned_bounds.height() - size.height()) / 2, + size.width(), size.height()); + bounds.trailing = bounds.leading; + bounds.trailing.set_x(aligned_bounds.right() - size.width()); + + gfx::PointF origin(tab()->bounds().origin()); + origin.Scale(scale); + bounds.trailing.Offset(-origin.x(), -origin.y()); + + return bounds; +} + +float BraveVerticalTabStyle::GetSeparatorOpacity(bool for_layout, + bool leading) const { + if (ShouldShowVerticalTabs() || !HorizontalTabsUpdateEnabled()) { + return BraveGM2TabStyle::GetSeparatorOpacity(for_layout, leading); + } + + if (leading) { + return 0; + } + + if (tab()->data().pinned) { + return 0; + } + + const auto has_visible_background = [](const Tab* const tab) { + return tab->IsActive() || tab->IsSelected() || tab->IsMouseHovered(); + }; + + if (has_visible_background(tab())) { + return 0; + } + + const Tab* const next_tab = tab()->controller()->GetAdjacentTab(tab(), 1); + + const float visible_opacity = + GetHoverInterpolatedSeparatorOpacity(for_layout, next_tab); + + // Show separator if this is the last tab (and is therefore followed by the + // new tab icon). + if (!next_tab) { + return visible_opacity; + } + + // Show separator if there is a group header between this tab and the next. + if (next_tab->group().has_value() && tab()->group() != next_tab->group()) { + return visible_opacity; + } + + if (has_visible_background(next_tab)) { + return 0; + } + + return visible_opacity; } void BraveVerticalTabStyle::PaintTab(gfx::Canvas* canvas) const { BraveGM2TabStyle::PaintTab(canvas); - if (!ShouldShowVerticalTabs()) { + + if (!HorizontalTabsUpdateEnabled() && !ShouldShowVerticalTabs()) { return; } if (tab()->data().pinned) { const auto* widget = tab()->GetWidget(); CHECK(widget); - const SkColor tab_stroke_color = - widget->GetColorProvider()->GetColor(kColorBraveVerticalTabSeparator); + + ui::ColorId color_id = widget->ShouldPaintAsActive() + ? kColorTabStrokeFrameActive + : kColorTabStrokeFrameInactive; + if (ShouldShowVerticalTabs()) { + color_id = kColorBraveVerticalTabSeparator; + } + PaintBackgroundStroke(canvas, TabStyle::TabSelectionState::kActive, - tab_stroke_color); + widget->GetColorProvider()->GetColor(color_id)); } } diff --git a/chromium_src/chrome/browser/ui/tabs/tab_style.cc b/chromium_src/chrome/browser/ui/tabs/tab_style.cc new file mode 100644 index 000000000000..98fe3ddb3e51 --- /dev/null +++ b/chromium_src/chrome/browser/ui/tabs/tab_style.cc @@ -0,0 +1,10 @@ +/* Copyright (c) 2023 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/tabs/brave_tab_style.h" + +#define BRAVE_TAB_STYLE_GET return new BraveTabStyle(); +#include "src/chrome/browser/ui/tabs/tab_style.cc" +#undef BRAVE_TAB_STYLE_GET diff --git a/chromium_src/chrome/browser/ui/views/frame/browser_frame_view_win.cc b/chromium_src/chrome/browser/ui/views/frame/browser_frame_view_win.cc new file mode 100644 index 000000000000..c909aca6abde --- /dev/null +++ b/chromium_src/chrome/browser/ui/views/frame/browser_frame_view_win.cc @@ -0,0 +1,17 @@ +/* Copyright (c) 2023 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/tabs/features.h" + +// The tab region view maintains its own padding; the frame view does not need +// to reserve an extra top margin for it. +#define BRAVE_BROWSER_FRAME_VIEW_WIN_TOP_AREA_HEIGHT \ + if (tabs::features::HorizontalTabsUpdateEnabled()) { \ + return top; \ + } + +#include "src/chrome/browser/ui/views/frame/browser_frame_view_win.cc" // IWYU pragma: export + +#undef BRAVE_BROWSER_FRAME_VIEW_WIN_TOP_AREA_HEIGHT diff --git a/chromium_src/chrome/browser/ui/views/frame/tab_strip_region_view.cc b/chromium_src/chrome/browser/ui/views/frame/tab_strip_region_view.cc index 8eb14116f21c..844e43a854a7 100644 --- a/chromium_src/chrome/browser/ui/views/frame/tab_strip_region_view.cc +++ b/chromium_src/chrome/browser/ui/views/frame/tab_strip_region_view.cc @@ -6,8 +6,17 @@ #include "brave/browser/ui/views/tabs/brave_new_tab_button.h" #include "brave/browser/ui/views/tabs/brave_tab_search_button.h" +// Currently, `NewTabButton::kButtonSize` is used to calculate tab strip button +// borders. Since the size of buttons varies depending upon the tabs update +// feature flag, replace the `kButtonSize` identifier with a call to the static +// `BraveNewTabButton::GetButtonSize` function. +#define kButtonSize GetButtonSize() + #define NewTabButton BraveNewTabButton #define TabSearchButton BraveTabSearchButton + #include "src/chrome/browser/ui/views/frame/tab_strip_region_view.cc" + #undef TabSearchButton #undef NewTabButton +#undef kButtonSize diff --git a/chromium_src/chrome/browser/ui/views/tabs/tab_group_style.cc b/chromium_src/chrome/browser/ui/views/tabs/tab_group_style.cc index 8f1eee8aac46..bd78646c6f6e 100644 --- a/chromium_src/chrome/browser/ui/views/tabs/tab_group_style.cc +++ b/chromium_src/chrome/browser/ui/views/tabs/tab_group_style.cc @@ -5,6 +5,8 @@ #include "chrome/browser/ui/views/tabs/tab_group_style.h" +#include "brave/browser/ui/tabs/brave_tab_layout_constants.h" +#include "brave/browser/ui/tabs/features.h" #include "brave/browser/ui/views/tabs/vertical_tab_utils.h" #define TabGroupStyle TabGroupStyle_ChromiumImpl @@ -48,10 +50,28 @@ SkPath TabGroupStyle::GetUnderlinePath(gfx::Rect local_bounds) const { return path; } +gfx::Insets TabGroupStyle::GetInsetsForHeaderChip() const { + auto insets = TabGroupStyle_ChromiumImpl::GetInsetsForHeaderChip(); + if (!tabs::features::HorizontalTabsUpdateEnabled()) { + return insets; + } + if (!ShouldShowVerticalTabs()) { + insets += gfx::Insets::VH(0, brave_tabs::kHorizontalTabInset); + } + return insets; +} + bool TabGroupStyle::ShouldShowVerticalTabs() const { return tabs::utils::ShouldShowVerticalTabs(tab_group_views_->GetBrowser()); } +float TabGroupStyle::GetEmptyChipSize() const { + if (!tabs::features::HorizontalTabsUpdateEnabled()) { + return TabGroupStyle_ChromiumImpl::GetEmptyChipSize(); + } + return brave_tabs::kEmptyGroupTitleSize; +} + int ChromeRefresh2023TabGroupStyle::GetTabGroupOverlapAdjustment() { return ChromeRefresh2023TabGroupStyle_ChromiumImpl:: GetTabGroupOverlapAdjustment(); diff --git a/chromium_src/chrome/browser/ui/views/tabs/tab_group_style.h b/chromium_src/chrome/browser/ui/views/tabs/tab_group_style.h index 7b35fcf3f323..432645d836be 100644 --- a/chromium_src/chrome/browser/ui/views/tabs/tab_group_style.h +++ b/chromium_src/chrome/browser/ui/views/tabs/tab_group_style.h @@ -22,6 +22,10 @@ class TabGroupStyle : public TabGroupStyle_ChromiumImpl { SkPath GetUnderlinePath(gfx::Rect local_bounds) const override; + gfx::Insets GetInsetsForHeaderChip() const override; + + float GetEmptyChipSize() const override; + private: bool ShouldShowVerticalTabs() const; }; diff --git a/chromium_src/chrome/browser/ui/views/tabs/tab_strip.cc b/chromium_src/chrome/browser/ui/views/tabs/tab_strip.cc index 5bafabdef260..49bf083fc432 100644 --- a/chromium_src/chrome/browser/ui/views/tabs/tab_strip.cc +++ b/chromium_src/chrome/browser/ui/views/tabs/tab_strip.cc @@ -7,6 +7,7 @@ #include +#include "brave/browser/ui/tabs/features.h" #include "brave/browser/ui/views/tabs/brave_compound_tab_container.h" #include "brave/browser/ui/views/tabs/brave_tab.h" #include "brave/browser/ui/views/tabs/brave_tab_group_header.h" @@ -43,19 +44,20 @@ continue; \ } -#define BRAVE_TAB_DRAG_CONTEXT_IMPL_CALCULATE_BOUNDS_FOR_DRAGGED_VIEWS \ - if (tabs::utils::ShouldShowVerticalTabs(tab_strip_->GetBrowser())) { \ - return tabs::CalculateBoundsForVerticalDraggedViews(views, tab_strip_); \ +#define BRAVE_TAB_DRAG_CONTEXT_IMPL_CALCULATE_BOUNDS_FOR_DRAGGED_VIEWS \ + if (tabs::utils::ShouldShowVerticalTabs(tab_strip_->GetBrowser())) { \ + return tabs::CalculateBoundsForVerticalDraggedViews(views, tab_strip_); \ + } \ + if (tabs::features::HorizontalTabsUpdateEnabled()) { \ + return tabs::CalculateBoundsForHorizontalDraggedViews(views, tab_strip_); \ } -#define BRAVE_TAB_DRAG_CONTEXT_IMPL_PAINT_CHILDREN \ - if (tabs::utils::ShouldShowVerticalTabs(tab_strip_->GetBrowser())) { \ - for (const ZOrderableTabContainerElement& child : orderable_children) \ - if (!child.view()->layer()) { \ - child.view()->Paint(paint_info); \ - } \ - return; \ - } +#define BRAVE_TAB_DRAG_CONTEXT_IMPL_PAINT_CHILDREN \ + for (const ZOrderableTabContainerElement& child : orderable_children) \ + if (!child.view()->layer()) { \ + child.view()->Paint(paint_info); \ + } \ + return; #include "src/chrome/browser/ui/views/tabs/tab_strip.cc" diff --git a/chromium_src/chrome/browser/ui/views/tabs/tab_style_views.cc b/chromium_src/chrome/browser/ui/views/tabs/tab_style_views.cc index 15a78ec814eb..fbd4bff6e420 100644 --- a/chromium_src/chrome/browser/ui/views/tabs/tab_style_views.cc +++ b/chromium_src/chrome/browser/ui/views/tabs/tab_style_views.cc @@ -5,6 +5,8 @@ #include "chrome/browser/ui/views/tabs/tab_style_views.h" #include "brave/browser/ui/color/brave_color_id.h" +#include "brave/browser/ui/tabs/brave_tab_layout_constants.h" +#include "brave/browser/ui/tabs/features.h" #include "brave/browser/ui/views/tabs/brave_tab_group_header.h" #include "brave/browser/ui/views/tabs/vertical_tab_utils.h" #include "chrome/browser/ui/views/tabs/tab_slot_controller.h" diff --git a/patches/chrome-browser-ui-tabs-tab_style.cc.patch b/patches/chrome-browser-ui-tabs-tab_style.cc.patch new file mode 100644 index 000000000000..ebf2e43fcbd7 --- /dev/null +++ b/patches/chrome-browser-ui-tabs-tab_style.cc.patch @@ -0,0 +1,12 @@ +diff --git a/chrome/browser/ui/tabs/tab_style.cc b/chrome/browser/ui/tabs/tab_style.cc +index aa9cd303743116892d65f71f66759bc4cefe05f5..25a252b713af07ecc4cf3ecd0312d48b9f9eba3a 100644 +--- a/chrome/browser/ui/tabs/tab_style.cc ++++ b/chrome/browser/ui/tabs/tab_style.cc +@@ -275,6 +275,7 @@ SkColor ChromeRefresh2023TabStyle::GetTabBackgroundColor( + + // static + const TabStyle* TabStyle::Get() { ++ BRAVE_TAB_STYLE_GET + static TabStyle* const tab_style = + features::IsChromeRefresh2023() + ? static_cast(new ChromeRefresh2023TabStyle()) diff --git a/patches/chrome-browser-ui-views-frame-browser_frame_view_win.cc.patch b/patches/chrome-browser-ui-views-frame-browser_frame_view_win.cc.patch new file mode 100644 index 000000000000..0816ed9c34a7 --- /dev/null +++ b/patches/chrome-browser-ui-views-frame-browser_frame_view_win.cc.patch @@ -0,0 +1,12 @@ +diff --git a/chrome/browser/ui/views/frame/browser_frame_view_win.cc b/chrome/browser/ui/views/frame/browser_frame_view_win.cc +index a4c7824a16a504f577668cfb2b40428c1b63d1c1..bde9e6ede746e52cd90c7417917b503c635eb79e 100644 +--- a/chrome/browser/ui/views/frame/browser_frame_view_win.cc ++++ b/chrome/browser/ui/views/frame/browser_frame_view_win.cc +@@ -531,6 +531,7 @@ int BrowserFrameViewWin::TopAreaHeight(bool restored) const { + : caption_button_container_->GetPreferredSize().height(); + return top; + } ++ BRAVE_BROWSER_FRAME_VIEW_WIN_TOP_AREA_HEIGHT + + // In Refresh, the tabstrip controls its own top padding. + if (features::IsChromeRefresh2023()) {