From 66b4b82d49aa26fa01bde5f3cf47bde828447620 Mon Sep 17 00:00:00 2001
From: Boris Yumankulov <boria138@altlinux.org>
Date: Sat, 14 Jun 2025 11:14:22 +0500
Subject: [PATCH] feat: change game card size only on slider released

Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
---
 portprotonqt/custom_widgets.py | 47 +++++++++++++++++-----------------
 portprotonqt/game_card.py      | 36 ++++++++++++++++++++++++++
 portprotonqt/main_window.py    | 14 +++++-----
 3 files changed, 68 insertions(+), 29 deletions(-)

diff --git a/portprotonqt/custom_widgets.py b/portprotonqt/custom_widgets.py
index 2a362cc..52cf608 100644
--- a/portprotonqt/custom_widgets.py
+++ b/portprotonqt/custom_widgets.py
@@ -1,5 +1,5 @@
 import numpy as np
-from PySide6.QtWidgets import QLabel, QPushButton, QWidget, QLayout, QStyleOption, QLayoutItem
+from PySide6.QtWidgets import QLabel, QPushButton, QWidget, QLayout, QLayoutItem
 from PySide6.QtCore import Qt, Signal, QRect, QPoint, QSize
 from PySide6.QtGui import QFont, QFontMetrics, QPainter
 
@@ -133,18 +133,7 @@ class FlowLayout(QLayout):
 class ClickableLabel(QLabel):
     clicked = Signal()
 
-    def __init__(self, *args, icon=None, icon_size=16, icon_space=5, change_cursor=True, **kwargs):
-        """
-        Поддерживаются вызовы:
-          - ClickableLabel("текст", parent=...) – первый аргумент строка,
-          - ClickableLabel(parent, text="...") – если первым аргументом передается родитель.
-
-        Аргументы:
-          icon: QIcon или None – иконка, которая будет отрисована вместе с текстом.
-          icon_size: int – размер иконки (ширина и высота).
-          icon_space: int – отступ между иконкой и текстом.
-          change_cursor: bool – изменять ли курсор на PointingHandCursor при наведении (по умолчанию True).
-        """
+    def __init__(self, *args, icon=None, icon_size=16, icon_space=5, change_cursor=True, font_scale_factor=0.06, **kwargs):
         if args and isinstance(args[0], str):
             text = args[0]
             parent = kwargs.get("parent", None)
@@ -162,20 +151,38 @@ class ClickableLabel(QLabel):
         self._icon = icon
         self._icon_size = icon_size
         self._icon_space = icon_space
+        self._font_scale_factor = font_scale_factor
+        self._card_width = 250  # Значение по умолчанию
         if change_cursor:
             self.setCursor(Qt.CursorShape.PointingHandCursor)
+        self.updateFontSize()
 
     def setIcon(self, icon):
-        """Устанавливает иконку и перерисовывает виджет."""
         self._icon = icon
         self.update()
 
     def icon(self):
-        """Возвращает текущую иконку."""
         return self._icon
 
+    def setIconSize(self, icon_size: int, icon_space: int):
+        self._icon_size = icon_size
+        self._icon_space = icon_space
+        self.update()
+
+    def setCardWidth(self, card_width: int):
+        """Обновляет ширину карточки и пересчитывает размер шрифта."""
+        self._card_width = card_width
+        self.updateFontSize()
+
+    def updateFontSize(self):
+        """Обновляет размер шрифта на основе card_width и font_scale_factor."""
+        font = self.font()
+        font_size = int(self._card_width * self._font_scale_factor)
+        font.setPointSize(max(8, font_size))  # Минимальный размер шрифта 8
+        self.setFont(font)
+        self.update()
+
     def paintEvent(self, event):
-        """Переопределяем отрисовку: рисуем иконку и текст в одном лейбле."""
         painter = QPainter(self)
         painter.setRenderHint(QPainter.RenderHint.Antialiasing)
 
@@ -190,7 +197,6 @@ class ClickableLabel(QLabel):
         text = self.text()
 
         if self._icon:
-            # Получаем QPixmap нужного размера
             pixmap = self._icon.pixmap(icon_size, icon_size)
             icon_rect = QRect(0, 0, icon_size, icon_size)
             icon_rect.moveTop(rect.top() + (rect.height() - icon_size) // 2)
@@ -214,13 +220,8 @@ class ClickableLabel(QLabel):
         if pixmap:
             icon_rect.moveLeft(x)
             text_rect = QRect(x + icon_size + spacing, y, text_width, text_height)
-        else:
-            text_rect = QRect(x, y, text_width, text_height)
-
-        option = QStyleOption()
-        option.initFrom(self)
-        if pixmap:
             painter.drawPixmap(icon_rect, pixmap)
+
         self.style().drawItemText(
             painter,
             text_rect,
diff --git a/portprotonqt/game_card.py b/portprotonqt/game_card.py
index ca95cfd..c498e8c 100644
--- a/portprotonqt/game_card.py
+++ b/portprotonqt/game_card.py
@@ -255,6 +255,42 @@ class GameCard(QFrame):
         nameLabel.setStyleSheet(self.theme.GAME_CARD_NAME_LABEL_STYLE)
         layout.addWidget(nameLabel)
 
+    def update_card_size(self, new_width: int):
+        self.card_width = new_width
+        extra_margin = 20
+        self.setFixedSize(new_width + extra_margin, int(new_width * 1.6) + extra_margin)
+
+        if self.coverLabel is None:
+            return
+
+        coverWidget = self.coverLabel.parentWidget()
+        if coverWidget is None:
+            return
+
+        coverWidget.setFixedSize(new_width, int(new_width * 1.2))
+        self.coverLabel.setFixedSize(new_width, int(new_width * 1.2))
+
+        label_ref = weakref.ref(self.coverLabel)
+        def on_cover_loaded(pixmap):
+            label = label_ref()
+            if label:
+                scaled_pixmap = pixmap.scaled(new_width, int(new_width * 1.2), Qt.AspectRatioMode.KeepAspectRatioByExpanding, Qt.TransformationMode.SmoothTransformation)
+                rounded_pixmap = round_corners(scaled_pixmap, 15)
+                label.setPixmap(rounded_pixmap)
+
+        load_pixmap_async(self.cover_path or "", new_width, int(new_width * 1.2), on_cover_loaded)
+
+        badge_width = int(new_width * 2/3)
+        icon_size = int(new_width * 0.06)
+        icon_space = int(new_width * 0.012)
+        for label in [self.steamLabel, self.egsLabel, self.portprotonLabel, self.protondbLabel, self.anticheatLabel]:
+            if label is not None:
+                label.setFixedWidth(badge_width)
+                label.setIconSize(icon_size, icon_space)
+                label.setCardWidth(new_width)
+
+        self.update()
+
     def update_badge_visibility(self, display_filter: str):
         """Update badge visibility based on the provided display_filter."""
         self.display_filter = display_filter
diff --git a/portprotonqt/main_window.py b/portprotonqt/main_window.py
index 2cb38fa..a040ce1 100644
--- a/portprotonqt/main_window.py
+++ b/portprotonqt/main_window.py
@@ -535,11 +535,13 @@ class MainWindow(QMainWindow):
     def startSearchDebounce(self, text):
         self.searchDebounceTimer.start()
 
-    def on_slider_value_changed(self, value: int):
-            self.card_width = value
-            self.sizeSlider.setToolTip(f"{value} px")
-            save_card_size(value)
-            self.updateGameGrid()
+    def on_slider_released(self):
+        self.card_width = self.sizeSlider.value()
+        self.sizeSlider.setToolTip(f"{self.card_width} px")
+        save_card_size(self.card_width)
+        for card in self.game_card_cache.values():
+            card.update_card_size(self.card_width)
+        self.updateGameGrid()
 
     def filterGamesDelayed(self):
         """Filters games based on search text and updates the grid."""
@@ -581,7 +583,7 @@ class MainWindow(QMainWindow):
         self.sizeSlider.setFixedWidth(150)
         self.sizeSlider.setToolTip(f"{self.card_width} px")
         self.sizeSlider.setStyleSheet(self.theme.SLIDER_SIZE_STYLE)
-        self.sizeSlider.valueChanged.connect(self.on_slider_value_changed)
+        self.sizeSlider.sliderReleased.connect(self.on_slider_released)
         sliderLayout.addWidget(self.sizeSlider)
         layout.addLayout(sliderLayout)