1
0
Fork 0
mirror of synced 2024-06-23 08:40:45 +12:00

Merge pull request #214 from loathingKernel/library_layout

LibraryLayout from #196
This commit is contained in:
Dummerle 2022-06-20 22:25:14 +02:00 committed by GitHub
commit 903b8fd5de
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 159 additions and 18 deletions

View file

@ -13,8 +13,7 @@ from rare.shared import (
)
from rare.shared.image_manager import ImageManagerSingleton
from rare.ui.components.tabs.games.games_tab import Ui_GamesTab
from rare.utils.extra_widgets import FlowLayout
from .cloud_save_utils import CloudSaveUtils
from rare.widgets.library_layout import LibraryLayout
from .cloud_save_utils import CloudSaveUtils
from .game_info import GameInfoTabs
from .game_info.uninstalled_info import UninstalledInfoTabs
@ -181,7 +180,7 @@ class GamesTab(QStackedWidget, Ui_GamesTab):
def setup_game_list(self):
self.icon_view = QWidget()
self.icon_view.setLayout(FlowLayout())
self.icon_view.setLayout(LibraryLayout())
self.list_view = QWidget()
self.list_view.setLayout(QVBoxLayout())

View file

@ -33,6 +33,7 @@ class GameProcess(QObject):
self.app_name = app_name
self.on_startup = on_startup
self.game = LegendaryCoreSingleton().get_game(app_name)
self.game_meta = RareGameMeta()
self.socket = QLocalSocket()
self.socket.connected.connect(self._socket_connected)
self.socket.errorOccurred.connect(self._error_occurred)

View file

@ -4,7 +4,7 @@ from logging import getLogger
from PyQt5.QtCore import pyqtSignal, QProcess, QSettings, QStandardPaths, Qt, QByteArray
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import QGroupBox, QMessageBox, QAction
from PyQt5.QtWidgets import QFrame, QMessageBox, QAction
from rare.components.tabs.games.game_utils import GameUtils
from rare.shared import LegendaryCoreSingleton, GlobalSignalsSingleton, ArgumentsSingleton
@ -15,7 +15,7 @@ from rare.widgets.image_widget import ImageWidget
logger = getLogger("Game")
class BaseInstalledWidget(QGroupBox):
class BaseInstalledWidget(QFrame):
launch_signal = pyqtSignal(str, QProcess, list)
show_info = pyqtSignal(str)
finish_signal = pyqtSignal(str, int)
@ -83,7 +83,6 @@ class BaseInstalledWidget(QGroupBox):
self.update_available = True
self.data = QByteArray()
self.setContentsMargins(0, 0, 0, 0)
self.settings = QSettings()
self.setContextMenuPolicy(Qt.ActionsContextMenu)

View file

@ -1,7 +1,7 @@
from logging import getLogger
from PyQt5.QtCore import pyqtSignal, Qt
from PyQt5.QtWidgets import QGroupBox, QAction
from PyQt5.QtWidgets import QFrame, QAction
from legendary.models.game import Game
from rare.shared.image_manager import ImageManagerSingleton, ImageSize
@ -10,7 +10,7 @@ from rare.widgets.image_widget import ImageWidget
logger = getLogger("Uninstalled")
class BaseUninstalledWidget(QGroupBox):
class BaseUninstalledWidget(QFrame):
show_uninstalled_info = pyqtSignal(Game)
def __init__(self, game, core, pixmap):
@ -27,7 +27,6 @@ class BaseUninstalledWidget(QGroupBox):
self.image.setPixmap(pixmap)
self.installing = False
self.setContextMenuPolicy(Qt.ActionsContextMenu)
self.setContentsMargins(0, 0, 0, 0)
reload_image = QAction(self.tr("Reload Image"), self)
reload_image.triggered.connect(self.reload_image)

View file

@ -24,6 +24,8 @@ class InstalledIconWidget(BaseInstalledWidget):
self.setContextMenuPolicy(Qt.ActionsContextMenu)
layout = QVBoxLayout()
layout.setContentsMargins(0, 0, 0, 0)
self.setFixedWidth(ImageSize.Display.size.width())
self.core = LegendaryCoreSingleton()
if self.update_available:
@ -42,6 +44,7 @@ class InstalledIconWidget(BaseInstalledWidget):
miniwidget.setLayout(minilayout)
self.title_label = ElideLabel(f"<h4>{self.game.app_title}</h4>", parent=miniwidget)
self.title_label.setAlignment(Qt.AlignTop)
self.title_label.setObjectName("game_widget")
minilayout.addWidget(self.title_label, stretch=2)

View file

@ -19,6 +19,7 @@ class InstalledListWidget(BaseInstalledWidget):
def __init__(self, app_name, pixmap, game_utils):
super(InstalledListWidget, self).__init__(app_name, pixmap, game_utils)
self.setFrameStyle(self.StyledPanel)
self.dev = self.game.metadata["developer"]
if self.igame:
self.size = self.igame.install_size

View file

@ -1,6 +1,6 @@
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import QVBoxLayout, QHBoxLayout, QGroupBox, QWidget
from PyQt5.QtWidgets import QVBoxLayout, QHBoxLayout, QWidget, QFrame
from legendary.models.game import Game
from rare.shared import LegendaryCoreSingleton
@ -9,14 +9,15 @@ from rare.widgets.elide_label import ElideLabel
from .library_widget import LibraryWidget
class InstallingGameWidget(QGroupBox):
class InstallingGameWidget(QFrame):
game: Game = None
def __init__(self):
super(InstallingGameWidget, self).__init__()
layout = QVBoxLayout()
layout.setContentsMargins(0, 0, 0, 0)
self.setFixedWidth(ImageSize.Display.size.width())
self.setObjectName("game_widget_icon")
self.setContentsMargins(0, 0, 0, 0)
self.core = LegendaryCoreSingleton()
self.image_manager = ImageManagerSingleton()
@ -34,6 +35,7 @@ class InstallingGameWidget(QGroupBox):
miniwidget.setLayout(minilayout)
self.title_label = ElideLabel(f"<h4>Error</h4>", parent=miniwidget)
self.title_label.setAlignment(Qt.AlignTop)
self.title_label.setObjectName("game_widget")
minilayout.addWidget(self.title_label, stretch=2)

View file

@ -18,6 +18,7 @@ class IconWidgetUninstalled(BaseUninstalledWidget):
def __init__(self, game: Game, core: LegendaryCore, pixmap):
super(IconWidgetUninstalled, self).__init__(game, core, pixmap)
layout = QVBoxLayout()
layout.setContentsMargins(0, 0, 0, 0)
self.setObjectName("game_widget_icon")
layout.addWidget(self.image)
@ -29,6 +30,7 @@ class IconWidgetUninstalled(BaseUninstalledWidget):
miniwidget.setLayout(minilayout)
self.title_label = ElideLabel(f"<h4>{game.app_title}</h4>", parent=miniwidget)
self.title_label.setAlignment(Qt.AlignTop)
self.title_label.setObjectName("game_widget")
minilayout.addWidget(self.title_label, stretch=2)

View file

@ -14,12 +14,13 @@ logger = getLogger("Game")
class ListWidgetUninstalled(BaseUninstalledWidget):
def __init__(self, core: LegendaryCore, game, pixmap):
super(ListWidgetUninstalled, self).__init__(game, core, pixmap)
self.layout = QHBoxLayout()
self.setLayout(self.layout)
self.layout.addWidget(self.image)
self.setFrameStyle(self.StyledPanel)
layout = QHBoxLayout()
self.setLayout(layout)
layout.addWidget(self.image)
self.child_layout = QVBoxLayout()
self.layout.addLayout(self.child_layout)
layout.addLayout(self.child_layout)
self.title_label = QLabel(f"<h2>{self.game.app_title}</h2>")
self.app_name_label = QLabel(f"App Name: {self.game.app_name}")
@ -31,5 +32,5 @@ class ListWidgetUninstalled(BaseUninstalledWidget):
self.child_layout.addWidget(self.app_name_label)
self.child_layout.addWidget(self.install_button)
self.layout.setAlignment(Qt.AlignLeft)
layout.setAlignment(Qt.AlignLeft)
self.child_layout.setAlignment(Qt.AlignTop)

View file

@ -4,7 +4,6 @@ from typing import Callable, Tuple
from PyQt5.QtCore import (
Qt,
QEvent,
QCoreApplication,
QRect,
QSize,

View file

@ -0,0 +1,135 @@
from typing import Callable
from PyQt5.QtCore import (
Qt,
QRect,
QPoint,
)
from PyQt5.QtWidgets import (
QSizePolicy,
)
from rare.utils.extra_widgets import FlowLayout
class LibraryLayout(FlowLayout):
def __init__(self, parent=None, margin=6, spacing=11):
super(LibraryLayout, self).__init__(parent=parent, margin=margin, hspacing=spacing, vspacing=spacing)
# def event(self, e: QEvent) -> None:
# if e.type() == QEvent.ShowToParent or e.type() == QEvent.HideToParent:
# self.doLayout(self.parent().rect(), False)
# e.accept()
def expandingDirections(self):
return Qt.Orientations(Qt.Horizontal | Qt.Vertical)
def setGeometry(self, rect):
super(FlowLayout, self).setGeometry(rect)
self.doLayout(rect, 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):
self._items.sort(key=key)
self.setGeometry(self.parent().rect())