Source code for pyqt_reactive.widgets.shared.action_tabbed_window_body

"""Tabbed window body with active-tab action widgets."""

from __future__ import annotations

from dataclasses import dataclass
from typing import Any

from PyQt6.QtCore import pyqtSignal
from PyQt6.QtWidgets import (
    QHBoxLayout,
    QSizePolicy,
    QTabBar,
    QStackedWidget,
    QVBoxLayout,
    QWidget,
)

from pyqt_reactive.forms.layout_constants import CURRENT_LAYOUT
from pyqt_reactive.widgets.shared.responsive_layout_widgets import ResponsiveTwoRowWidget


[docs] @dataclass(frozen=True, slots=True) class ActionTabSpec: """One tab plus its optional active-tab action widget.""" label: str content: QWidget actions: QWidget | None = None
[docs] class ActionTabbedWindowBody(QWidget): """Render tabs on the left and the active tab's actions on the right.""" current_changed = pyqtSignal(int)
[docs] def __init__( self, *, color_scheme: Any | None = None, width_threshold: int = 600, parent: QWidget | None = None, ) -> None: super().__init__(parent) self.color_scheme = color_scheme self._action_widgets: list[QWidget | None] = [] layout = QVBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) self.tab_row = ResponsiveTwoRowWidget( width_threshold=width_threshold, parent=self, ) self.tab_bar = QTabBar() self.tab_bar.setExpanding(False) self.tab_bar.setUsesScrollButtons(False) self.tab_bar.setFixedHeight(CURRENT_LAYOUT.button_height) self.tab_bar.currentChanged.connect(self._on_current_changed) self.tab_row.add_left_widget(self.tab_bar) self._active_actions_container = QWidget() self._active_actions_layout = QHBoxLayout(self._active_actions_container) self._active_actions_layout.setContentsMargins(0, 0, 0, 0) self._active_actions_layout.setSpacing(0) self.tab_row.add_right_widget(self._active_actions_container) layout.addWidget(self.tab_row) self.content_container = QWidget() self.content_layout = QVBoxLayout(self.content_container) self.content_layout.setContentsMargins(0, 0, 0, 0) self.content_layout.setSpacing(0) layout.addWidget(self.content_container, 1) self.content_stack = QStackedWidget() self.content_layout.addWidget(self.content_stack) self._apply_default_tab_style()
[docs] def add_tab(self, spec: ActionTabSpec) -> int: """Add a tab and its optional action widget.""" index = self.content_stack.addWidget(spec.content) self.tab_bar.addTab(spec.label) self._action_widgets.append(spec.actions) if spec.actions is not None: spec.actions.setSizePolicy( QSizePolicy.Policy.Maximum, QSizePolicy.Policy.Fixed, ) spec.actions.setVisible(False) self._active_actions_layout.addWidget(spec.actions) if self.tab_bar.currentIndex() != self.content_stack.currentIndex(): self.content_stack.setCurrentIndex(self.tab_bar.currentIndex()) self._show_current_actions() return index
[docs] def set_current_index(self, index: int) -> None: self.setCurrentIndex(index)
[docs] def setCurrentIndex(self, index: int) -> None: self.tab_bar.setCurrentIndex(index) self.content_stack.setCurrentIndex(index) self._show_current_actions()
[docs] def current_index(self) -> int: return self.currentIndex()
[docs] def currentIndex(self) -> int: return self.tab_bar.currentIndex()
[docs] def current_widget(self) -> QWidget | None: return self.currentWidget()
[docs] def currentWidget(self) -> QWidget | None: return self.content_stack.currentWidget()
[docs] def widget(self, index: int) -> QWidget | None: return self.content_stack.widget(index)
[docs] def count(self) -> int: return self.content_stack.count()
def _on_current_changed(self, index: int) -> None: if 0 <= index < self.content_stack.count(): self.content_stack.setCurrentIndex(index) self._show_current_actions() self.current_changed.emit(index) def _show_current_actions(self) -> None: current_index = self.tab_bar.currentIndex() for index, action_widget in enumerate(self._action_widgets): if action_widget is not None: action_widget.setVisible(index == current_index) def _apply_default_tab_style(self) -> None: if self.color_scheme is None: return self.tab_bar.setStyleSheet(f""" QTabBar::tab {{ background-color: {self.color_scheme.to_hex(self.color_scheme.input_bg)}; color: {self.color_scheme.to_hex(self.color_scheme.text_primary)}; padding: 0px 16px; margin-right: 2px; border: none; border-radius: 4px 4px 0 0; height: {CURRENT_LAYOUT.button_height}px; }} QTabBar::tab:selected {{ background-color: {self.color_scheme.to_hex(self.color_scheme.selection_bg)}; }} QTabBar::tab:hover {{ background-color: {self.color_scheme.to_hex(self.color_scheme.button_hover_bg)}; }} """)