from __future__ import annotations from PySide6.QtCore import QPoint, QRect, QSize, Qt from PySide6.QtWidgets import QLayout class FlowLayout(QLayout): def __init__( self, parent=None, margin: int = 0, hspacing: int = 4, vspacing: int = 4 ): super().__init__(parent) self._items = [] self._hspace = hspacing self._vspace = vspacing self.setContentsMargins(margin, margin, margin, margin) def addItem(self, item): self._items.append(item) def itemAt(self, index): if 0 <= index < len(self._items): return self._items[index] return None def takeAt(self, index): if 0 <= index < len(self._items): return self._items.pop(index) return None def count(self): return len(self._items) def expandingDirections(self): return Qt.Orientations(Qt.Orientation(0)) def hasHeightForWidth(self): return True def heightForWidth(self, width: int) -> int: return self._do_layout(QRect(0, 0, width, 0), test_only=True) def setGeometry(self, rect: QRect): super().setGeometry(rect) self._do_layout(rect, test_only=False) def sizeHint(self) -> QSize: return self.minimumSize() def minimumSize(self) -> QSize: size = QSize() for item in self._items: size = size.expandedTo(item.minimumSize()) left, top, right, bottom = self.getContentsMargins() size += QSize(left + right, top + bottom) return size def _do_layout(self, rect: QRect, test_only: bool) -> int: x = rect.x() y = rect.y() line_height = 0 left, top, right, bottom = self.getContentsMargins() effective_rect = rect.adjusted(+left, +top, -right, -bottom) x = effective_rect.x() y = effective_rect.y() max_right = effective_rect.right() for item in self._items: wid = item.widget() if wid is None or not wid.isVisible(): continue space_x = self._hspace space_y = self._vspace next_x = x + item.sizeHint().width() + space_x if next_x - space_x > max_right and line_height > 0: # Wrap x = effective_rect.x() y = y + line_height + space_y next_x = x + item.sizeHint().width() + space_x line_height = 0 if not test_only: item.setGeometry(QRect(QPoint(x, y), item.sizeHint())) x = next_x line_height = max(line_height, item.sizeHint().height()) return y + line_height - rect.y() + bottom