2023-09-06 00:31:20 +08:00

127 lines
4.0 KiB
Python

# Adapted from https://doc.qt.io/qt-6/qtwidgets-layouts-flowlayout-example.html
from PySide6.QtCore import QPoint, QRect, QSize, Qt
from PySide6.QtWidgets import QLayout, QLayoutItem, QSizePolicy, QStyle, QWidget
class FlowLayout(QLayout):
def __init__(self, parent=None, margin=0, hSpacing=0, vSpacing=0):
super().__init__(parent)
self.hSpace = hSpacing
self.vSpace = vSpacing
self.setContentsMargins(margin, margin, margin, margin)
self.itemList: list[QLayoutItem] = []
def __del__(self):
item = self.takeAt(0)
while item:
del item
item = self.takeAt(0)
def addItem(self, item: QLayoutItem):
self.itemList.append(item)
def horizontalSpacing(self):
return (
self.hSpace
if self.hSpace >= 0
else self.smartSpacing(QStyle.PixelMetric.PM_LayoutHorizontalSpacing)
)
def verticalSpacing(self):
return (
self.vSpace
if self.vSpace >= 0
else self.smartSpacing(QStyle.PixelMetric.PM_LayoutVerticalSpacing)
)
def count(self):
return len(self.itemList)
def itemAt(self, index: int):
return self.itemList[index] if 0 <= index < len(self.itemList) else None
def takeAt(self, index):
return self.itemList.pop(index) if 0 <= index < len(self.itemList) else None
# Qt::Orientations FlowLayout::expandingDirections() const
# {
# return { };
# }
def expandingDirections(self):
return Qt.Orientations(0)
def hasHeightForWidth(self):
return True
def heightForWidth(self, width: int):
return self.doLayout(QRect(0, 0, width, 0), True)
def setGeometry(self, rect: QRect):
super().setGeometry(rect)
self.doLayout(rect, False)
def sizeHint(self):
return self.minimumSize()
def minimumSize(self):
size = QSize()
for item in self.itemList:
size = size.expandedTo(item.minimumSize())
margins = self.contentsMargins()
size += QSize(
margins.left() + margins.right(), margins.top() + margins.bottom()
)
return size
def doLayout(self, rect: QRect, testOnly: bool):
margins = self.contentsMargins()
left = margins.left()
top = margins.top()
right = margins.right()
bottom = margins.bottom()
effectiveRect = rect.adjusted(+left, +top, -right, -bottom)
x = effectiveRect.x()
y = effectiveRect.y()
lineHeight = 0
for item in self.itemList:
widget = item.widget()
spaceX = self.horizontalSpacing()
if spaceX == -1:
spaceX = widget.style().layoutSpacing(
QSizePolicy.ControlType.PushButton,
QSizePolicy.ControlType.PushButton,
)
spaceY = self.verticalSpacing()
if spaceY == -1:
spaceY = widget.style().layoutSpacing(
QSizePolicy.ControlType.PushButton,
QSizePolicy.ControlType.PushButton,
)
nextX = x + item.sizeHint().width() + spaceX
if nextX - spaceX > effectiveRect.right() and lineHeight > 0:
x = effectiveRect.x()
y = y + lineHeight + spaceY
nextX = x + item.sizeHint().width() + spaceX
lineHeight = 0
if not testOnly:
item.setGeometry(QRect(QPoint(x, y), item.sizeHint()))
x = nextX
lineHeight = max(lineHeight, item.sizeHint().height())
return y + lineHeight - rect.y() + bottom
def smartSpacing(self, pm: QStyle.PixelMetric):
parent = self.parent()
if not parent:
return -1
elif parent.isWidgetType():
parent: QWidget
return parent.style().pixelMetric(pm, None, parent)
else:
parent: QLayout
return parent.spacing()