"""Layout que coloca los widgets en fila y salta de línea cuando no caben. Port mínimo del ejemplo FlowLayout de Qt: se usa para los 'pills' de cada fila, que pueden ser muchos (topics) y deben envolver sin desbordar horizontalmente. """ from __future__ import annotations from PySide6.QtCore import QMargins, QPoint, QRect, QSize, Qt from PySide6.QtWidgets import QLayout, QLayoutItem, QSizePolicy, QWidget class FlowLayout(QLayout): def __init__(self, parent: QWidget | None = None, spacing: int = 6) -> None: super().__init__(parent) self._items: list[QLayoutItem] = [] self._spacing = spacing self.setContentsMargins(0, 0, 0, 0) def addItem(self, item: QLayoutItem) -> None: # noqa: N802 - API Qt self._items.append(item) def count(self) -> int: return len(self._items) def itemAt(self, index: int) -> QLayoutItem | None: # noqa: N802 - API Qt return self._items[index] if 0 <= index < len(self._items) else None def takeAt(self, index: int) -> QLayoutItem | None: # noqa: N802 - API Qt return self._items.pop(index) if 0 <= index < len(self._items) else None def expandingDirections(self) -> Qt.Orientations: # noqa: N802 - API Qt return Qt.Orientation(0) def hasHeightForWidth(self) -> bool: # noqa: N802 - API Qt return True def heightForWidth(self, width: int) -> int: # noqa: N802 - API Qt return self._layout(QRect(0, 0, width, 0), apply=False) def setGeometry(self, rect: QRect) -> None: # noqa: N802 - API Qt super().setGeometry(rect) self._layout(rect, apply=True) def sizeHint(self) -> QSize: # noqa: N802 - API Qt return self.minimumSize() def minimumSize(self) -> QSize: # noqa: N802 - API Qt size = QSize() for item in self._items: size = size.expandedTo(item.minimumSize()) m: QMargins = self.contentsMargins() return size + QSize(m.left() + m.right(), m.top() + m.bottom()) def _layout(self, rect: QRect, apply: bool) -> int: m: QMargins = self.contentsMargins() eff = rect.adjusted(m.left(), m.top(), -m.right(), -m.bottom()) x, y, line_h = eff.x(), eff.y(), 0 for item in self._items: hint = item.sizeHint() next_x = x + hint.width() + self._spacing if next_x - self._spacing > eff.right() and line_h > 0: x = eff.x() y = y + line_h + self._spacing next_x = x + hint.width() + self._spacing line_h = 0 if apply: item.setGeometry(QRect(QPoint(x, y), hint)) x = next_x line_h = max(line_h, hint.height()) return y + line_h - rect.y() + m.bottom()