Reimplement tab bar from scratch with simplified, working design

Simplified tab bar implementation:
- Remove complex CSS provider logic that was causing issues
- Simple box-based layout with tab label + close button per tab
- Direct button click handlers with tab reference stored on widget
- Much simpler and more reliable event handling

Tab bar features:
- Each tab shown as: [tab label button] [close button ✕]
- Active tab highlighted with suggested-action style
- Inactive tabs use flat style for cleaner look
- Close button is small and flat (36px width)
- New tab button with + symbol (48px width)

Event handling:
- _on_tab_clicked: Activates the clicked tab
- _on_close_clicked: Closes the clicked tab
- _on_new_tab_clicked: Opens new tab to about:startpage
- Tab reference stored directly on button widget for easy access

Benefits:
- Much simpler codebase
- No complex CSS provider per widget
- Reliable event handling
- Easy to debug and maintain
- No style context issues

All tests passing (15/15)
This commit is contained in:
Benedikt Willi 2026-01-09 14:36:49 +01:00
parent a9d52e49c8
commit cb6103ce04
2 changed files with 54 additions and 91 deletions

View file

@ -129,103 +129,66 @@ class Chrome:
"""Recreate tab buttons to reflect current tabs and active tab."""
if not self.tabs_box:
return
# Clear existing tabs
self._clear_children(self.tabs_box)
# Add a button per tab with integrated close button
# Add each tab as a simple button
for i, tab in enumerate(self.browser.tabs):
# Create a custom tab widget with better visual integration
tab_widget = self._create_tab_widget(tab, i)
self.tabs_box.append(tab_widget)
is_active = tab is self.browser.active_tab
# Simple container for tab label + close button
tab_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=0)
tab_box.set_homogeneous(False)
# Tab label button
tab_label = f"{i+1}: {tab.title}"
tab_btn = Gtk.Button(label=tab_label)
tab_btn.set_hexpand(True)
tab_btn.set_relief(Gtk.ReliefStyle.NORMAL)
if is_active:
tab_btn.add_css_class("suggested-action")
else:
tab_btn.add_css_class("flat")
# Store tab reference on the button for handler
tab_btn.tab = tab
tab_btn.connect("clicked", self._on_tab_clicked)
tab_box.append(tab_btn)
# Close button
close_btn = Gtk.Button(label="")
close_btn.set_size_request(36, -1)
close_btn.set_relief(Gtk.ReliefStyle.FLAT)
close_btn.add_css_class("flat")
close_btn.tab = tab
close_btn.connect("clicked", self._on_close_clicked)
tab_box.append(close_btn)
self.tabs_box.append(tab_box)
# New tab '+' button at the end
plus_btn = Gtk.Button(label="+")
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)
# New tab button
new_tab_btn = Gtk.Button(label="")
new_tab_btn.set_size_request(48, -1)
new_tab_btn.add_css_class("flat")
new_tab_btn.set_tooltip_text("New Tab")
new_tab_btn.connect("clicked", self._on_new_tab_clicked)
self.tabs_box.append(new_tab_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_tab_clicked(self, btn: Gtk.Button):
"""Handle tab button click - set as active."""
if hasattr(btn, 'tab'):
self.browser.set_active_tab(btn.tab)
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 _on_close_clicked(self, btn: Gtk.Button):
"""Handle close button click - close the tab."""
if hasattr(btn, 'tab'):
self.browser.close_tab(btn.tab)
def _on_new_tab_clicked(self, btn: Gtk.Button):
"""Handle new tab button click."""
self.browser.new_tab("about:startpage")
def update_address_bar(self):
if not self.address_bar: