Polish tab UI styling with enhanced visual design

Document tabs (header bar):
- Add rounded corners with subtle backgrounds
- Highlight active tabs with accent color and bottom border
- Improve hover states with smooth 200ms transitions
- Enhance close button visibility and hover effects
- Reduce spacing for more compact appearance
- Increase tab name truncation limit to 25 chars

Stack switchers (Headers/Body tabs):
- Add pill-style rounded buttons (6px radius)
- Style active tabs with accent colors and bold text
- Add smooth crossfade transitions (150ms)
- Improve spacing with 8px top/bottom margins

All styling uses GNOME color tokens for consistent theming across
light and dark modes.
This commit is contained in:
vesp 2025-12-23 10:27:40 +01:00
parent 3bc155b127
commit 6031f4319d

View File

@ -83,6 +83,9 @@ class RosterWindow(Adw.ApplicationWindow):
# Create window actions # Create window actions
self._create_actions() self._create_actions()
# Setup custom CSS
self._setup_custom_css()
# Setup UI # Setup UI
self._setup_method_dropdown() self._setup_method_dropdown()
self._setup_request_tabs() self._setup_request_tabs()
@ -158,6 +161,88 @@ class RosterWindow(Adw.ApplicationWindow):
# User confirmed - close the window # User confirmed - close the window
self.destroy() self.destroy()
def _setup_custom_css(self):
"""Setup custom CSS for enhanced tab styling."""
css_provider = Gtk.CssProvider()
css_provider.load_from_data(b"""
/* Document tabs in header bar */
.tab-button {
margin: 0 2px;
padding: 0;
border-radius: 6px;
background: alpha(@window_fg_color, 0.08);
transition: all 200ms ease;
}
.tab-button:hover {
background: alpha(@window_fg_color, 0.12);
}
.tab-button-active {
background: alpha(@accent_bg_color, 0.2);
box-shadow: inset 0 -2px 0 0 @accent_bg_color;
}
.tab-button-active:hover {
background: alpha(@accent_bg_color, 0.25);
}
/* Tab label button */
.tab-label-btn {
padding: 4px 12px;
min-height: 28px;
border-radius: 6px 0 0 6px;
}
.tab-button-active .tab-label-btn {
font-weight: 600;
}
/* Tab close button */
.tab-close-btn {
padding: 4px 8px;
min-width: 24px;
min-height: 24px;
margin: 2px 4px 2px 0;
border-radius: 0 6px 6px 0;
opacity: 0.7;
}
.tab-close-btn:hover {
opacity: 1;
background: alpha(@window_fg_color, 0.1);
}
.tab-button-active .tab-close-btn:hover {
background: alpha(@accent_bg_color, 0.3);
}
/* Stack switchers styling (Headers/Body tabs) */
stackswitcher button {
padding: 6px 16px;
min-height: 32px;
border-radius: 6px;
margin: 0 2px;
}
stackswitcher button:checked {
background: @accent_bg_color;
color: @accent_fg_color;
font-weight: 600;
}
/* Add some polish to the tab bar container */
#tab_bar_container {
margin: 0 6px;
}
""")
Gtk.StyleContext.add_provider_for_display(
self.get_display(),
css_provider,
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
)
def _setup_sourceview_theme(self): def _setup_sourceview_theme(self):
"""Set up GtkSourceView theme based on system color scheme.""" """Set up GtkSourceView theme based on system color scheme."""
# Get the style manager to detect dark mode # Get the style manager to detect dark mode
@ -355,18 +440,22 @@ class RosterWindow(Adw.ApplicationWindow):
# Add tab buttons with close button # Add tab buttons with close button
for tab in self.tab_manager.tabs: for tab in self.tab_manager.tabs:
# Create a box for tab label + close button # Create a box for tab label + close button
tab_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6) tab_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=0)
tab_box.add_css_class("tab-button")
# Add active styling if this is the current tab
if tab.id == self.current_tab_id:
tab_box.add_css_class("tab-button-active")
# Tab label with modified indicator # Tab label with modified indicator
tab_label = tab.name[:20] # Truncate long names tab_label = tab.name[:25] # Truncate long names
if tab.is_modified(): if tab.is_modified():
tab_label += "" # Add dot for unsaved changes tab_label += "" # Add dot for unsaved changes
# Tab label button # Tab label button
tab_label_btn = Gtk.Button(label=tab_label) tab_label_btn = Gtk.Button(label=tab_label)
tab_label_btn.add_css_class("flat") tab_label_btn.add_css_class("flat")
if tab.id == self.current_tab_id: tab_label_btn.add_css_class("tab-label-btn")
tab_label_btn.add_css_class("suggested-action")
tab_label_btn.connect("clicked", lambda btn, tid=tab.id: self._switch_to_tab(tid)) tab_label_btn.connect("clicked", lambda btn, tid=tab.id: self._switch_to_tab(tid))
tab_box.append(tab_label_btn) tab_box.append(tab_label_btn)
@ -375,6 +464,7 @@ class RosterWindow(Adw.ApplicationWindow):
close_btn.set_icon_name("window-close-symbolic") close_btn.set_icon_name("window-close-symbolic")
close_btn.add_css_class("flat") close_btn.add_css_class("flat")
close_btn.add_css_class("circular") close_btn.add_css_class("circular")
close_btn.add_css_class("tab-close-btn")
close_btn.set_tooltip_text("Close tab") close_btn.set_tooltip_text("Close tab")
close_btn.connect("clicked", lambda btn, tid=tab.id: self._close_tab(tid)) close_btn.connect("clicked", lambda btn, tid=tab.id: self._close_tab(tid))
tab_box.append(close_btn) tab_box.append(close_btn)
@ -532,11 +622,15 @@ class RosterWindow(Adw.ApplicationWindow):
# Create stack for switching between pages # Create stack for switching between pages
self.request_stack = Gtk.Stack() self.request_stack = Gtk.Stack()
self.request_stack.set_vexpand(True) self.request_stack.set_vexpand(True)
self.request_stack.set_transition_type(Gtk.StackTransitionType.CROSSFADE)
self.request_stack.set_transition_duration(150)
# Create stack switcher for the tabs # Create stack switcher for the tabs
request_switcher = Gtk.StackSwitcher() request_switcher = Gtk.StackSwitcher()
request_switcher.set_stack(self.request_stack) request_switcher.set_stack(self.request_stack)
request_switcher.set_halign(Gtk.Align.CENTER) request_switcher.set_halign(Gtk.Align.CENTER)
request_switcher.set_margin_top(8)
request_switcher.set_margin_bottom(8)
# Add switcher and stack to container # Add switcher and stack to container
self.request_tabs_container.append(request_switcher) self.request_tabs_container.append(request_switcher)
@ -634,11 +728,15 @@ class RosterWindow(Adw.ApplicationWindow):
# Create stack for switching between pages # Create stack for switching between pages
self.response_stack = Gtk.Stack() self.response_stack = Gtk.Stack()
self.response_stack.set_vexpand(True) self.response_stack.set_vexpand(True)
self.response_stack.set_transition_type(Gtk.StackTransitionType.CROSSFADE)
self.response_stack.set_transition_duration(150)
# Create stack switcher for the tabs # Create stack switcher for the tabs
response_switcher = Gtk.StackSwitcher() response_switcher = Gtk.StackSwitcher()
response_switcher.set_stack(self.response_stack) response_switcher.set_stack(self.response_stack)
response_switcher.set_halign(Gtk.Align.CENTER) response_switcher.set_halign(Gtk.Align.CENTER)
response_switcher.set_margin_top(8)
response_switcher.set_margin_bottom(8)
# Add switcher and stack to container # Add switcher and stack to container
self.response_tabs_container.append(response_switcher) self.response_tabs_container.append(response_switcher)