"""TabSwitcher — a row of mutually-exclusive toggle buttons driving a Gtk.Stack, styled as a segmented control (see .sb-tabbar/.sb-tab in style/style.css). Plain manual mutual exclusion rather than Gtk.CheckButton grouping, since that's simplest for a small fixed set of tabs.""" from __future__ import annotations from typing import Callable import gi gi.require_version("Gtk", "4.0") from gi.repository import Gtk # noqa: E402 class TabSwitcher(Gtk.Box): def __init__(self, tabs: list[tuple[str, str, Gtk.Widget]], stack: Gtk.Stack, on_changed: Callable[[str], None]) -> None: super().__init__(orientation=Gtk.Orientation.HORIZONTAL, spacing=4) self.add_css_class("sb-tabbar") self._stack = stack self._on_changed = on_changed self._buttons: dict[str, Gtk.ToggleButton] = {} first = None for name, label, _widget in tabs: btn = Gtk.ToggleButton(label=label) btn.add_css_class("sb-tab") btn.connect("toggled", self._make_handler(name)) self.append(btn) self._buttons[name] = btn if first is None: first = name if first is not None: self._buttons[first].set_active(True) def _make_handler(self, name: str): def handler(btn: Gtk.ToggleButton) -> None: if not btn.get_active(): if not any(b.get_active() for b in self._buttons.values()): btn.set_active(True) # one tab must always stay selected return for other_name, other_btn in self._buttons.items(): if other_name != name and other_btn.get_active(): other_btn.set_active(False) self._stack.set_visible_child_name(name) self._on_changed(name) return handler