1
0
Fork 0
mirror of synced 2024-05-19 12:02:54 +12:00
Rare/rare/widgets/library_layout.py

126 lines
5.5 KiB
Python

from typing import Callable
from PyQt5.QtCore import Qt, QRect, QPoint
from PyQt5.QtWidgets import QSizePolicy
from .flow_layout import FlowLayout
class LibraryLayout(FlowLayout):
def __init__(self, parent=None):
super(LibraryLayout, self).__init__(parent)
def expandingDirections(self) -> Qt.Orientations:
return Qt.Orientations(Qt.Orientation(0))
# return Qt.Horizontal | Qt.Vertical
def setGeometry(self, a0: QRect) -> None:
super(FlowLayout, self).setGeometry(a0)
self.doLayout(a0, False)
def doLayout(self, rect, testonly):
"""!
@brief Arranges the widgets for this layout
<pre>
Layout
+-----------------------------------------------+
| margin (above first row only) |
|-----------------------------------------------|
| vspace (hspace) |
|-----------------------------------------------|
| hpadding |
|-----------------------------------------------|
| _ _ __ +--------+ _ __ +--------+ _ __ _ |
||m||h|| h|| Widget ||h|| h|| Widget ||h|| h||m||
||a||s|| p|| ||s|| p|| ||s|| p||a||
||r||p|| a|| ||p|| a|| ||p|| a||r||
||g||a|| d|| ||a|| d|| ||a|| d||g||
||i||c|| d|| ||c|| d|| ||c|| d||i||
||n||e|| i|| ||e|| i|| ||e|| i||n||
|| || || n|| || || n|| || || n|| ||
|| || || g|| || || g|| || || g|| ||
| - - -- +--------+ - -- +--------+ - -- - |
|-----------------------------------------------|
| vspace (hspace) |
|-----------------------------------------------|
| hpadding |
|-----------------------------------------------|
| margin (below last row only) |
+-----------------------------------------------+
margin: doesn't play a role in the code below, it only affects the
effective rectangle inside which we can layout
hspace: static padding between widgets (minimum distance between them)
hpadding: dynamic padding to fill the space when resizing
</pre>
@param self: object self-reference
@param rect: the area the widgets should occupy
@param testonly: only test the layout, don't arrange the items
@return: the height of the layout
"""
left, top, right, bottom = self.getContentsMargins()
effective = rect.adjusted(+left, +top, -right, -bottom)
x = effective.x()
y = effective.y()
lineheight = 0
if not self._items:
return y + lineheight - rect.y() + bottom
widget = self._items[0].widget()
hspace = self.horizontalSpacing()
if hspace == -1:
hspace = widget.style().layoutSpacing(
QSizePolicy.PushButton, QSizePolicy.PushButton, Qt.Horizontal
)
vspace = self.verticalSpacing()
if vspace == -1:
vspace = widget.style().layoutSpacing(QSizePolicy.PushButton, QSizePolicy.PushButton, Qt.Vertical)
# lk: get the remaining space after subtracting the space required for each widget and its static padding
# lk: also reserve space for the leading static padding
rem_hspace = (effective.width() - hspace) % (widget.size().width() + hspace)
# lk: the number of items and their static spacing that can be in the layout
hspace_items = (effective.width() - hspace) // (widget.size().width() + hspace)
# lk: in case the visible items are less than the maximum possible widgets
visible_items = len([item for item in self._items if not item.isEmpty()])
if visible_items < hspace_items:
hspace_items = visible_items
rem_hspace = (effective.width() - hspace) - ((widget.size().width() + hspace) * hspace_items)
try:
# lk: the dynamic padding between each item, also account for the leading dynamic padding
hpadding = rem_hspace // (hspace_items + 1)
except ZeroDivisionError:
hpadding = 0
for item in self._items:
if item.isEmpty():
continue
# lk: compute the location of the next widget
next_x = x + item.sizeHint().width() + hspace + hpadding
# lk: find out if there is enough space for the widget in this row
# lk: account for the leading static and dynamic padding too (at the start)
if next_x - hspace * 2 - hpadding * 2 > effective.right() and lineheight > 0:
x = effective.x()
# lk: find next vertical position, add static and dynamic padding
y = y + lineheight + vspace + hpadding
next_x = x + item.sizeHint().width() + hspace + hpadding
lineheight = 0
# lk: add static and dynamic padding to the current widget
x = x + hspace + hpadding
if not testonly:
item.setGeometry(QRect(QPoint(x, y), item.sizeHint()))
x = next_x
lineheight = max(lineheight, item.sizeHint().height())
return y + lineheight - rect.y() + bottom
def sort(self, key: Callable, reverse=False) -> None:
self._items.sort(key=key, reverse=reverse)
self.setGeometry(self.parent().contentsRect().adjusted(*self.parent().getContentsMargins()))