Improve tab bar visual integration with modern browser-like styling

Enhanced tab widget design:
- Unified tab widget with integrated close button (no visual separation)
- Close button on the right with rounded right corners
- Tab button with rounded left corners for seamless integration
- Custom CSS styling for professional appearance
- Hover effects: close button highlights in warning color
- Tab highlights as suggested-action when active

Visual improvements:
- Tabs have rounded corners (4px radius)
- Proper borders matching theme colors
- Better padding and spacing
- Flat buttons by default except active tab
- Close button marked with ✕ instead of ×
- Better visual feedback on hover

UX improvements:
- Tooltips on all buttons (tab URL, close tab, new tab)
- Small square close button (32px) for easy clicking
- Active tab uses blue suggested-action styling
- Tabs show full URL on hover
- New tab uses about:startpage instead of example.com

CSS styling:
- Theme-aware colors using @view_bg_color, @borders, @warning_color
- Proper border-radius on tab and close button
- Visual separation between tab and close button
- Hover state for better feedback

All tests passing (15/15)
This commit is contained in:
Benedikt Willi 2026-01-09 14:34:48 +01:00
parent 3bad301bcc
commit a9d52e49c8
2 changed files with 90 additions and 20 deletions

View file

@ -131,32 +131,102 @@ class Chrome:
return
self._clear_children(self.tabs_box)
# Add a button per tab with a close '×' control
# Add a button per tab with integrated close button
for i, tab in enumerate(self.browser.tabs):
# Composite tab widget: label button + inline close button in one container
tab_container = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=0)
tab_container.add_css_class("linked") # visual integration
label = f"{i+1}: {tab.title}"
tab_btn = Gtk.Button(label=label)
tab_btn.set_hexpand(False)
if tab is self.browser.active_tab:
tab_btn.add_css_class("suggested-action")
tab_btn.connect("clicked", partial(self.browser.set_active_tab, tab))
close_btn = Gtk.Button(label="×")
close_btn.add_css_class("flat")
close_btn.connect("clicked", partial(self.browser.close_tab, tab))
tab_container.append(tab_btn)
tab_container.append(close_btn)
self.tabs_box.append(tab_container)
# Create a custom tab widget with better visual integration
tab_widget = self._create_tab_widget(tab, i)
self.tabs_box.append(tab_widget)
# New tab '+' button at the end
plus_btn = Gtk.Button(label="+")
plus_btn.connect("clicked", lambda _b: self.browser.new_tab("https://example.com"))
plus_btn.set_tooltip_text("New Tab")
plus_btn.add_css_class("flat")
plus_btn.connect("clicked", lambda _b: self.browser.new_tab("about:startpage"))
self.tabs_box.append(plus_btn)
def _create_tab_widget(self, tab, index: int) -> Gtk.Widget:
"""Create a visually integrated tab widget with close button."""
# Main container for the tab
tab_container = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=0)
tab_container.add_css_class("tab-widget")
# Determine styling
is_active = tab is self.browser.active_tab
# Tab button - shows title and handles activation
label = f"{index+1}: {tab.title}"
tab_btn = Gtk.Button(label=label)
tab_btn.set_hexpand(False)
tab_btn.add_css_class("tab-button")
if is_active:
tab_btn.add_css_class("suggested-action")
else:
tab_btn.add_css_class("flat")
tab_btn.set_tooltip_text(str(tab.current_url) if tab.current_url else "New Tab")
tab_btn.connect("clicked", partial(self.browser.set_active_tab, tab))
tab_container.append(tab_btn)
# Close button - appears inline, flat styling
close_btn = Gtk.Button(label="")
close_btn.set_size_request(32, -1) # Small, square button
close_btn.add_css_class("tab-close-button")
close_btn.add_css_class("flat")
close_btn.set_tooltip_text("Close tab")
close_btn.connect("clicked", partial(self._on_close_tab_clicked, tab, tab_container))
tab_container.append(close_btn)
# Apply CSS styling for better visual integration
css_provider = Gtk.CssProvider()
css_provider.load_from_data(b"""
.tab-widget {
border-radius: 4px;
margin-right: 2px;
padding: 0px;
background-color: @view_bg_color;
border: 1px solid @borders;
}
.tab-widget:hover {
background-color: mix(@view_bg_color, @theme_fg_color, 0.95);
}
.tab-button {
border-radius: 4px 0px 0px 4px;
padding: 4px 8px;
border: 0px;
font-weight: 500;
min-width: 80px;
}
.tab-button:focus {
outline: none;
}
.tab-close-button {
border-radius: 0px 4px 4px 0px;
padding: 4px 4px;
border: 0px;
margin-left: -1px;
min-width: 32px;
font-size: 0.9em;
}
.tab-close-button:hover {
background-color: @warning_color;
color: white;
}
""")
context = tab_container.get_style_context()
context.add_provider(css_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
return tab_container
def _on_close_tab_clicked(self, tab, tab_widget: Gtk.Widget):
"""Handle tab close button click."""
self.browser.close_tab(tab)
# The widget will be removed when rebuild_tab_bar is called by browser
def update_address_bar(self):
if not self.address_bar:
return