49 lines
1.8 KiB
Python
49 lines
1.8 KiB
Python
"""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
|