From 0b92d058a983c6f53514eb7ac0004938a4771519 Mon Sep 17 00:00:00 2001 From: Boris Yumankulov Date: Tue, 17 Jun 2025 14:20:45 +0500 Subject: [PATCH] feat: move GameCard animation properties to styles Signed-off-by: Boris Yumankulov --- portprotonqt/game_card.py | 51 +++++++------- portprotonqt/themes/standart-light/styles.py | 70 ++++++++++++++++++++ portprotonqt/themes/standart/styles.py | 70 ++++++++++++++++++++ 3 files changed, 165 insertions(+), 26 deletions(-) diff --git a/portprotonqt/game_card.py b/portprotonqt/game_card.py index bb76a63..90668f4 100644 --- a/portprotonqt/game_card.py +++ b/portprotonqt/game_card.py @@ -67,14 +67,14 @@ class GameCard(QFrame): self.setStyleSheet(self.theme.GAME_CARD_WINDOW_STYLE) # Параметры анимации обводки - self._borderWidth = 2 - self._gradientAngle = 0.0 + self._borderWidth = self.theme.GAME_CARD_ANIMATION["default_border_width"] + self._gradientAngle = self.theme.GAME_CARD_ANIMATION["gradient_start_angle"] self._hovered = False self._focused = False # Анимации self.thickness_anim = QPropertyAnimation(self, QByteArray(b"borderWidth")) - self.thickness_anim.setDuration(300) + self.thickness_anim.setDuration(self.theme.GAME_CARD_ANIMATION["thickness_anim_duration"]) self.gradient_anim = None self.pulse_anim = None @@ -448,10 +448,8 @@ class GameCard(QFrame): if self._hovered or self._focused: center = self.rect().center() gradient = QConicalGradient(center, self._gradientAngle) - gradient.setColorAt(0, QColor("#00fff5")) - gradient.setColorAt(0.33, QColor("#FF5733")) - gradient.setColorAt(0.66, QColor("#9B59B6")) - gradient.setColorAt(1, QColor("#00fff5")) + for stop in self.theme.GAME_CARD_ANIMATION["gradient_colors"]: + gradient.setColorAt(stop["position"], QColor(stop["color"])) pen.setBrush(QBrush(gradient)) else: pen.setColor(QColor(0, 0, 0, 0)) @@ -468,11 +466,11 @@ class GameCard(QFrame): if self.pulse_anim: self.pulse_anim.stop() self.pulse_anim = QPropertyAnimation(self, QByteArray(b"borderWidth")) - self.pulse_anim.setDuration(800) + self.pulse_anim.setDuration(self.theme.GAME_CARD_ANIMATION["pulse_anim_duration"]) self.pulse_anim.setLoopCount(0) - self.pulse_anim.setKeyValueAt(0, 8) - self.pulse_anim.setKeyValueAt(0.5, 10) - self.pulse_anim.setKeyValueAt(1, 8) + self.pulse_anim.setKeyValueAt(0, self.theme.GAME_CARD_ANIMATION["pulse_min_border_width"]) + self.pulse_anim.setKeyValueAt(0.5, self.theme.GAME_CARD_ANIMATION["pulse_max_border_width"]) + self.pulse_anim.setKeyValueAt(1, self.theme.GAME_CARD_ANIMATION["pulse_min_border_width"]) self.pulse_anim.start() def enterEvent(self, event): @@ -482,9 +480,9 @@ class GameCard(QFrame): if self._isPulseAnimationConnected: self.thickness_anim.finished.disconnect(self.startPulseAnimation) self._isPulseAnimationConnected = False - self.thickness_anim.setEasingCurve(QEasingCurve(QEasingCurve.Type.OutBack)) + self.thickness_anim.setEasingCurve(QEasingCurve(QEasingCurve.Type[self.theme.GAME_CARD_ANIMATION["thickness_easing_curve"]])) self.thickness_anim.setStartValue(self._borderWidth) - self.thickness_anim.setEndValue(8) + self.thickness_anim.setEndValue(self.theme.GAME_CARD_ANIMATION["hover_border_width"]) self.thickness_anim.finished.connect(self.startPulseAnimation) self._isPulseAnimationConnected = True self.thickness_anim.start() @@ -492,9 +490,9 @@ class GameCard(QFrame): if self.gradient_anim: self.gradient_anim.stop() self.gradient_anim = QPropertyAnimation(self, QByteArray(b"gradientAngle")) - self.gradient_anim.setDuration(3000) - self.gradient_anim.setStartValue(360) - self.gradient_anim.setEndValue(0) + self.gradient_anim.setDuration(self.theme.GAME_CARD_ANIMATION["gradient_anim_duration"]) + self.gradient_anim.setStartValue(self.theme.GAME_CARD_ANIMATION["gradient_start_angle"]) + self.gradient_anim.setEndValue(self.theme.GAME_CARD_ANIMATION["gradient_end_angle"]) self.gradient_anim.setLoopCount(-1) self.gradient_anim.start() @@ -515,8 +513,10 @@ class GameCard(QFrame): if self._isPulseAnimationConnected: self.thickness_anim.finished.disconnect(self.startPulseAnimation) self._isPulseAnimationConnected = False - self.setBorderWidth(2) - self.update() + self.thickness_anim.setEasingCurve(QEasingCurve(QEasingCurve.Type[self.theme.GAME_CARD_ANIMATION["thickness_easing_curve_out"]])) + self.thickness_anim.setStartValue(self._borderWidth) + self.thickness_anim.setEndValue(self.theme.GAME_CARD_ANIMATION["default_border_width"]) + self.thickness_anim.start() super().leaveEvent(event) def focusInEvent(self, event): @@ -525,9 +525,9 @@ class GameCard(QFrame): if self._isPulseAnimationConnected: self.thickness_anim.finished.disconnect(self.startPulseAnimation) self._isPulseAnimationConnected = False - self.thickness_anim.setEasingCurve(QEasingCurve(QEasingCurve.Type.OutBack)) + self.thickness_anim.setEasingCurve(QEasingCurve(QEasingCurve.Type[self.theme.GAME_CARD_ANIMATION["thickness_easing_curve"]])) self.thickness_anim.setStartValue(self._borderWidth) - self.thickness_anim.setEndValue(12) + self.thickness_anim.setEndValue(self.theme.GAME_CARD_ANIMATION["focus_border_width"]) self.thickness_anim.finished.connect(self.startPulseAnimation) self._isPulseAnimationConnected = True self.thickness_anim.start() @@ -535,9 +535,9 @@ class GameCard(QFrame): if self.gradient_anim: self.gradient_anim.stop() self.gradient_anim = QPropertyAnimation(self, QByteArray(b"gradientAngle")) - self.gradient_anim.setDuration(3000) - self.gradient_anim.setStartValue(360) - self.gradient_anim.setEndValue(0) + self.gradient_anim.setDuration(self.theme.GAME_CARD_ANIMATION["gradient_anim_duration"]) + self.gradient_anim.setStartValue(self.theme.GAME_CARD_ANIMATION["gradient_start_angle"]) + self.gradient_anim.setEndValue(self.theme.GAME_CARD_ANIMATION["gradient_end_angle"]) self.gradient_anim.setLoopCount(-1) self.gradient_anim.start() @@ -556,11 +556,10 @@ class GameCard(QFrame): if self.pulse_anim: self.pulse_anim.stop() self.pulse_anim = None - self.thickness_anim.setEasingCurve(QEasingCurve(QEasingCurve.Type.InBack)) + self.thickness_anim.setEasingCurve(QEasingCurve(QEasingCurve.Type[self.theme.GAME_CARD_ANIMATION["thickness_easing_curve_out"]])) self.thickness_anim.setStartValue(self._borderWidth) - self.thickness_anim.setEndValue(2) + self.thickness_anim.setEndValue(self.theme.GAME_CARD_ANIMATION["default_border_width"]) self.thickness_anim.start() - super().focusOutEvent(event) def mousePressEvent(self, event): diff --git a/portprotonqt/themes/standart-light/styles.py b/portprotonqt/themes/standart-light/styles.py index 83f2435..e3b6f02 100644 --- a/portprotonqt/themes/standart-light/styles.py +++ b/portprotonqt/themes/standart-light/styles.py @@ -8,6 +8,76 @@ current_theme_name = read_theme_from_config() favoriteLabelSize = 48, 48 pixmapsScaledSize = 60, 60 +GAME_CARD_ANIMATION_PARAMS = { + # Ширина обводки карточки в состоянии покоя (без наведения или фокуса). + # Влияет на толщину рамки вокруг карточки, когда она не выделена. + # Значение в пикселях. + "default_border_width": 2, + + # Ширина обводки при наведении курсора. + # Увеличивает толщину рамки, когда курсор находится над карточкой. + # Значение в пикселях. + "hover_border_width": 8, + + # Ширина обводки при фокусе (например, при выборе с клавиатуры). + # Увеличивает толщину рамки, когда карточка в фокусе. + # Значение в пикселях. + "focus_border_width": 12, + + # Минимальная ширина обводки во время пульсирующей анимации. + # Определяет минимальную толщину рамки при пульсации (анимация "дыхания"). + # Значение в пикселях. + "pulse_min_border_width": 8, + + # Максимальная ширина обводки во время пульсирующей анимации. + # Определяет максимальную толщину рамки при пульсации. + # Значение в пикселях. + "pulse_max_border_width": 10, + + # Длительность анимации изменения толщины обводки (например, при наведении или фокусе). + # Влияет на скорость перехода от одной ширины обводки к другой. + # Значение в миллисекундах. + "thickness_anim_duration": 300, + + # Длительность одного цикла пульсирующей анимации. + # Определяет, как быстро рамка "пульсирует" между min и max значениями. + # Значение в миллисекундах. + "pulse_anim_duration": 800, + + # Длительность анимации вращения градиента. + # Влияет на скорость, с которой градиентная обводка вращается вокруг карточки. + # Значение в миллисекундах. + "gradient_anim_duration": 3000, + + # Начальный угол градиента (в градусах). + # Определяет начальную точку вращения градиента при старте анимации. + "gradient_start_angle": 360, + + # Конечный угол градиента (в градусах). + # Определяет конечную точку вращения градиента. + # Значение 0 означает полный поворот на 360 градусов. + "gradient_end_angle": 0, + + # Тип кривой сглаживания для анимации увеличения обводки (при наведении/фокусе). + # Влияет на "чувство" анимации (например, плавное ускорение или замедление). + # Возможные значения: строки, соответствующие QEasingCurve.Type (например, "OutBack", "InOutQuad"). + "thickness_easing_curve": "OutBack", + + # Тип кривой сглаживания для анимации уменьшения обводки (при уходе курсора/потере фокуса). + # Влияет на "чувство" возврата к исходной ширине обводки. + "thickness_easing_curve_out": "InBack", + + # Цвета градиента для анимированной обводки. + # Список словарей, где каждый словарь задает позицию (0.0–1.0) и цвет в формате hex. + # Влияет на внешний вид обводки при наведении или фокусе. + "gradient_colors": [ + {"position": 0, "color": "#00fff5"}, # Начальный цвет (циан) + {"position": 0.33, "color": "#FF5733"}, # Цвет на 33% (оранжевый) + {"position": 0.66, "color": "#9B59B6"}, # Цвет на 66% (пурпурный) + {"position": 1, "color": "#00fff5"} # Конечный цвет (возвращение к циану) + ] +} + # СТИЛЬ ШАПКИ ГЛАВНОГО ОКНА MAIN_WINDOW_HEADER_STYLE = """ QFrame { diff --git a/portprotonqt/themes/standart/styles.py b/portprotonqt/themes/standart/styles.py index a09f49d..09fc211 100644 --- a/portprotonqt/themes/standart/styles.py +++ b/portprotonqt/themes/standart/styles.py @@ -8,6 +8,76 @@ current_theme_name = read_theme_from_config() favoriteLabelSize = 48, 48 pixmapsScaledSize = 60, 60 +GAME_CARD_ANIMATION_PARAMS = { + # Ширина обводки карточки в состоянии покоя (без наведения или фокуса). + # Влияет на толщину рамки вокруг карточки, когда она не выделена. + # Значение в пикселях. + "default_border_width": 2, + + # Ширина обводки при наведении курсора. + # Увеличивает толщину рамки, когда курсор находится над карточкой. + # Значение в пикселях. + "hover_border_width": 8, + + # Ширина обводки при фокусе (например, при выборе с клавиатуры). + # Увеличивает толщину рамки, когда карточка в фокусе. + # Значение в пикселях. + "focus_border_width": 12, + + # Минимальная ширина обводки во время пульсирующей анимации. + # Определяет минимальную толщину рамки при пульсации (анимация "дыхания"). + # Значение в пикселях. + "pulse_min_border_width": 8, + + # Максимальная ширина обводки во время пульсирующей анимации. + # Определяет максимальную толщину рамки при пульсации. + # Значение в пикселях. + "pulse_max_border_width": 10, + + # Длительность анимации изменения толщины обводки (например, при наведении или фокусе). + # Влияет на скорость перехода от одной ширины обводки к другой. + # Значение в миллисекундах. + "thickness_anim_duration": 300, + + # Длительность одного цикла пульсирующей анимации. + # Определяет, как быстро рамка "пульсирует" между min и max значениями. + # Значение в миллисекундах. + "pulse_anim_duration": 800, + + # Длительность анимации вращения градиента. + # Влияет на скорость, с которой градиентная обводка вращается вокруг карточки. + # Значение в миллисекундах. + "gradient_anim_duration": 3000, + + # Начальный угол градиента (в градусах). + # Определяет начальную точку вращения градиента при старте анимации. + "gradient_start_angle": 360, + + # Конечный угол градиента (в градусах). + # Определяет конечную точку вращения градиента. + # Значение 0 означает полный поворот на 360 градусов. + "gradient_end_angle": 0, + + # Тип кривой сглаживания для анимации увеличения обводки (при наведении/фокусе). + # Влияет на "чувство" анимации (например, плавное ускорение или замедление). + # Возможные значения: строки, соответствующие QEasingCurve.Type (например, "OutBack", "InOutQuad"). + "thickness_easing_curve": "OutBack", + + # Тип кривой сглаживания для анимации уменьшения обводки (при уходе курсора/потере фокуса). + # Влияет на "чувство" возврата к исходной ширине обводки. + "thickness_easing_curve_out": "InBack", + + # Цвета градиента для анимированной обводки. + # Список словарей, где каждый словарь задает позицию (0.0–1.0) и цвет в формате hex. + # Влияет на внешний вид обводки при наведении или фокусе. + "gradient_colors": [ + {"position": 0, "color": "#00fff5"}, # Начальный цвет (циан) + {"position": 0.33, "color": "#FF5733"}, # Цвет на 33% (оранжевый) + {"position": 0.66, "color": "#9B59B6"}, # Цвет на 66% (пурпурный) + {"position": 1, "color": "#00fff5"} # Конечный цвет (возвращение к циану) + ] +} + CONTEXT_MENU_STYLE = """ QMenu { background: #282a33;;