bouquin/bouquin/flow_layout.py
2025-11-14 14:54:04 +11:00

88 lines
2.6 KiB
Python

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