Files
jail-launcher/jlauncher/ui/flow_layout.py
T
JailDesigner e0a93a9c28 Estil targeta tipus web a les files + tema seleccionable (system/clar/fosc)
UI:
- Files amb estil de targeta: icona arrodonida, títol gran, subtítol atenuat i
  'pills' amb estat, versió, data de llançament, jugadors, autor i topics. Els
  pills envolten amb un FlowLayout nou quan no caben.
- Submenú Opcions > Tema amb Sistema/Clar/Fosc; persisteix a settings.json
  (theme) i s'aplica a l'instant. El watcher del SO només actua en mode Sistema.

Dades:
- GameMeta guarda topics i created_at, llegits de la resposta de Gitea que ja
  demanàvem (gratis, auto-sincronitzats).
- games.toml: camps opcionals players i author per joc (la resta surt de Gitea).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 10:13:32 +02:00

72 lines
2.7 KiB
Python

"""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()