"""Responsive title layout for GroupBoxWithHelp."""
import logging
from PyQt6.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout
from PyQt6.QtCore import QTimer, Qt
from pyqt_reactive.widgets.shared.responsive_layout_widgets import StagedWrapLayout
from typing import Optional, List, Tuple
# Global toggle for responsive wrapping
_wrapping_enabled = True
[docs]
def set_wrapping_enabled(enabled: bool):
"""Globally enable or disable responsive wrapping for all GroupBox titles."""
global _wrapping_enabled
_wrapping_enabled = enabled
[docs]
def is_wrapping_enabled() -> bool:
"""Check if responsive wrapping is globally enabled."""
return _wrapping_enabled
[docs]
class ResponsiveGroupBoxTitle(QWidget):
"""
Responsive title widget that switches between 1-row and 2-row layout.
Row 1: [Title] [Help] [inline widgets]
Row 2: [Reset All] [Enabled] etc. - only when narrow
"""
TITLE_GROUP = "title"
HELP_GROUP = "help"
INLINE_GROUP = "inline"
RIGHT_GROUP = "right"
[docs]
def __init__(self, parent=None, width_threshold: int = 300):
super().__init__(parent)
self._threshold = width_threshold
self._is_horizontal = True
# Transparent background so scope-tinted background shows through
self.setAutoFillBackground(False)
self.setStyleSheet("background-color: transparent;")
self._main_layout = QVBoxLayout(self)
self._main_layout.setContentsMargins(0, 0, 0, 0)
self._main_layout.setSpacing(2)
self._staged_layout = StagedWrapLayout(parent=self, spacing=5)
self._main_layout.addWidget(self._staged_layout)
# Storage
self._title_widget: Optional[QWidget] = None
self._help_widget: Optional[QWidget] = None
self._inline_widgets: List[Tuple[QWidget, int]] = []
self._right_widgets: List[Tuple[QWidget, int]] = []
self._title_group = QWidget()
self._title_layout = QHBoxLayout(self._title_group)
self._title_layout.setContentsMargins(0, 0, 0, 0)
self._title_layout.setSpacing(5)
self._help_group = QWidget()
self._help_layout = QHBoxLayout(self._help_group)
self._help_layout.setContentsMargins(0, 0, 0, 0)
self._help_layout.setSpacing(5)
self._inline_group = QWidget()
self._inline_layout = QHBoxLayout(self._inline_group)
self._inline_layout.setContentsMargins(0, 0, 0, 0)
self._inline_layout.setSpacing(5)
self._inline_layout.setAlignment(Qt.AlignmentFlag.AlignLeft)
self._right_group = QWidget()
self._right_layout = QHBoxLayout(self._right_group)
self._right_layout.setContentsMargins(0, 0, 0, 0)
self._right_layout.setSpacing(5)
# Help button stays inline with title (always left aligned)
self._help_inline = True
# Debounce timer
self._timer = QTimer(self)
self._timer.setSingleShot(True)
self._timer.timeout.connect(self._check_switch)
if parent:
parent.installEventFilter(self)
[docs]
def minimumSizeHint(self):
"""Return minimum size - log for debugging."""
from PyQt6.QtCore import QSize
size = super().minimumSizeHint()
return size
def _check_switch(self):
# Skip if wrapping is globally disabled
if not _wrapping_enabled:
return
self._refresh_groups()
def _do_switch(self):
self._refresh_groups()
[docs]
def eventFilter(self, a0, a1):
if a1 is not None and a1.type() == a1.Type.Resize:
self._timer.start(100)
return super().eventFilter(a0, a1)
# Compatibility methods for QHBoxLayout API
[docs]
def count(self):
"""Return total widget count across both rows (for compatibility)."""
return self._title_layout.count() + self._help_layout.count() + self._inline_layout.count() + self._right_layout.count()
[docs]
def itemAt(self, index):
"""Get item at index (for compatibility - from row1)."""
layouts = [self._title_layout, self._help_layout, self._inline_layout, self._right_layout]
current = 0
for layout in layouts:
if index < current + layout.count():
return layout.itemAt(index - current)
current += layout.count()
return None
[docs]
def spacerItem(self):
"""Check if there's a spacer (for compatibility)."""
return None # We don't use spacers in the same way
def _refresh_groups(self):
if self._help_inline:
groups = [
(self.TITLE_GROUP, self._title_group),
(self.INLINE_GROUP, self._inline_group),
(self.RIGHT_GROUP, self._right_group),
]
stay_priority = [self.TITLE_GROUP, self.INLINE_GROUP, self.RIGHT_GROUP]
else:
groups = [
(self.TITLE_GROUP, self._title_group),
(self.HELP_GROUP, self._help_group),
(self.INLINE_GROUP, self._inline_group),
(self.RIGHT_GROUP, self._right_group),
]
stay_priority = [self.TITLE_GROUP, self.HELP_GROUP, self.INLINE_GROUP, self.RIGHT_GROUP]
self._staged_layout.set_groups(
groups,
stay_priority,
right_align_names=[self.RIGHT_GROUP],
)