Compare commits
11 Commits
Author | SHA1 | Date | |
---|---|---|---|
ff960df77c
|
|||
a57f509295
|
|||
32bbe89911
|
|||
593db00166
|
|||
79a78c785b
|
|||
0b92d058a9
|
|||
9df22edfc9
|
|||
4559231712
|
|||
18dbd42369
|
|||
76c0e607c5
|
|||
a91c9dacd8
|
@ -40,7 +40,7 @@ jobs:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
fedora_version: [40, 41, 42, rawhide]
|
||||
fedora_version: [41, 42, rawhide]
|
||||
|
||||
container:
|
||||
image: fedora:${{ matrix.fedora_version }}
|
||||
|
@ -97,7 +97,7 @@ jobs:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
fedora_version: [40, 41, 42, rawhide]
|
||||
fedora_version: [41, 42, rawhide]
|
||||
|
||||
container:
|
||||
image: fedora:${{ matrix.fedora_version }}
|
||||
|
14
CHANGELOG.md
14
CHANGELOG.md
@ -3,6 +3,20 @@
|
||||
Все заметные изменения в этом проекте фиксируются в этом файле.
|
||||
Формат основан на [Keep a Changelog](https://keepachangelog.com/) и придерживается принципов [Semantic Versioning](https://semver.org/).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
### Changed
|
||||
- Удалены сборки для Fedora 40
|
||||
- Перенесены параметры анимации GameCard в `styles.py` с подробной документацией для поддержки кастомизации тем.
|
||||
- Статус выделения и наведения на карточки теперь взаимоисключают друг друга
|
||||
|
||||
### Fixed
|
||||
- Дублирование обводки выделения карточек при быстром перемешении мыши
|
||||
|
||||
---
|
||||
|
||||
## [0.1.2] - 2025-06-15
|
||||
|
||||
### Added
|
||||
|
@ -28,19 +28,19 @@ BuildRequires: git
|
||||
%package -n python3-%{pypi_name}-git
|
||||
Summary: %{summary}
|
||||
%{?python_provide:%python_provide python3-%{pypi_name}}
|
||||
Requires: python3dist(babel)
|
||||
Requires: python3dist(evdev)
|
||||
Requires: python3dist(icoextract)
|
||||
Requires: python3dist(numpy)
|
||||
Requires: python3dist(orjson)
|
||||
Requires: python3dist(psutil)
|
||||
Requires: python3dist(pyside6)
|
||||
Requires: python3dist(pyudev)
|
||||
Requires: python3dist(requests)
|
||||
Requires: python3dist(tqdm)
|
||||
Requires: python3dist(vdf)
|
||||
Requires: python3dist(pefile)
|
||||
Requires: python3dist(pillow)
|
||||
Requires: python3-babel
|
||||
Requires: python3-evdev
|
||||
Requires: python3-icoextract
|
||||
Requires: python3-numpy
|
||||
Requires: python3-orjson
|
||||
Requires: python3-psutil
|
||||
Requires: python3-pyside6
|
||||
Requires: python3-pyudev
|
||||
Requires: python3-requests
|
||||
Requires: python3-tqdm
|
||||
Requires: python3-vdf
|
||||
Requires: python3-pefile
|
||||
Requires: python3-pillow
|
||||
Requires: perl-Image-ExifTool
|
||||
Requires: xdg-utils
|
||||
|
||||
|
@ -25,19 +25,19 @@ BuildRequires: git
|
||||
%package -n python3-%{pypi_name}
|
||||
Summary: %{summary}
|
||||
%{?python_provide:%python_provide python3-%{pypi_name}}
|
||||
Requires: python3dist(babel)
|
||||
Requires: python3dist(evdev)
|
||||
Requires: python3dist(icoextract)
|
||||
Requires: python3dist(numpy)
|
||||
Requires: python3dist(orjson)
|
||||
Requires: python3dist(psutil)
|
||||
Requires: python3dist(pyside6)
|
||||
Requires: python3dist(pyudev)
|
||||
Requires: python3dist(requests)
|
||||
Requires: python3dist(tqdm)
|
||||
Requires: python3dist(vdf)
|
||||
Requires: python3dist(pefile)
|
||||
Requires: python3dist(pillow)
|
||||
Requires: python3-babel
|
||||
Requires: python3-evdev
|
||||
Requires: python3-icoextract
|
||||
Requires: python3-numpy
|
||||
Requires: python3-orjson
|
||||
Requires: python3-psutil
|
||||
Requires: python3-pyside6
|
||||
Requires: python3-pyudev
|
||||
Requires: python3-requests
|
||||
Requires: python3-tqdm
|
||||
Requires: python3-vdf
|
||||
Requires: python3-pefile
|
||||
Requires: python3-pillow
|
||||
Requires: perl-Image-ExifTool
|
||||
Requires: xdg-utils
|
||||
|
||||
|
@ -25,6 +25,8 @@ class GameCard(QFrame):
|
||||
addToSteamRequested = Signal(str, str, str) # name, exec_line, cover_path
|
||||
removeFromSteamRequested = Signal(str, str) # name, exec_line
|
||||
openGameFolderRequested = Signal(str, str) # name, exec_line
|
||||
hoverChanged = Signal(str, bool)
|
||||
focusChanged = Signal(str, bool)
|
||||
|
||||
def __init__(self, name, description, cover_path, appid, controller_support, exec_line,
|
||||
last_launch, formatted_playtime, protondb_tier, anticheat_status, last_launch_ts, playtime_seconds, game_source,
|
||||
@ -66,14 +68,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
|
||||
|
||||
@ -447,10 +449,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))
|
||||
@ -467,22 +467,25 @@ 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):
|
||||
self._hovered = True
|
||||
self.hoverChanged.emit(self.name, True)
|
||||
self.setFocus(Qt.FocusReason.MouseFocusReason)
|
||||
|
||||
self.thickness_anim.stop()
|
||||
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()
|
||||
@ -490,9 +493,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()
|
||||
|
||||
@ -500,66 +503,71 @@ class GameCard(QFrame):
|
||||
|
||||
def leaveEvent(self, event):
|
||||
self._hovered = False
|
||||
if not self._focused: # Сохраняем анимацию, если есть фокус
|
||||
self.hoverChanged.emit(self.name, False)
|
||||
if not self._focused:
|
||||
if self.gradient_anim:
|
||||
self.gradient_anim.stop()
|
||||
self.gradient_anim = None
|
||||
if self.pulse_anim:
|
||||
self.pulse_anim.stop()
|
||||
self.pulse_anim = None
|
||||
if self.thickness_anim:
|
||||
self.thickness_anim.stop()
|
||||
if self._isPulseAnimationConnected:
|
||||
self.thickness_anim.finished.disconnect(self.startPulseAnimation)
|
||||
self._isPulseAnimationConnected = False
|
||||
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):
|
||||
if not self._hovered:
|
||||
self._focused = True
|
||||
self.focusChanged.emit(self.name, True)
|
||||
|
||||
self.thickness_anim.stop()
|
||||
if self._isPulseAnimationConnected:
|
||||
self.thickness_anim.finished.disconnect(self.startPulseAnimation)
|
||||
self._isPulseAnimationConnected = False
|
||||
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"]]))
|
||||
self.thickness_anim.setStartValue(self._borderWidth)
|
||||
self.thickness_anim.setEndValue(2)
|
||||
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()
|
||||
|
||||
super().leaveEvent(event)
|
||||
|
||||
def focusInEvent(self, event):
|
||||
self._focused = True
|
||||
self.thickness_anim.stop()
|
||||
if self._isPulseAnimationConnected:
|
||||
self.thickness_anim.finished.disconnect(self.startPulseAnimation)
|
||||
self._isPulseAnimationConnected = False
|
||||
self.thickness_anim.setEasingCurve(QEasingCurve(QEasingCurve.Type.OutBack))
|
||||
self.thickness_anim.setStartValue(self._borderWidth)
|
||||
self.thickness_anim.setEndValue(12)
|
||||
self.thickness_anim.finished.connect(self.startPulseAnimation)
|
||||
self._isPulseAnimationConnected = True
|
||||
self.thickness_anim.start()
|
||||
|
||||
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.setLoopCount(-1)
|
||||
self.gradient_anim.start()
|
||||
if self.gradient_anim:
|
||||
self.gradient_anim.stop()
|
||||
self.gradient_anim = QPropertyAnimation(self, QByteArray(b"gradientAngle"))
|
||||
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()
|
||||
|
||||
super().focusInEvent(event)
|
||||
|
||||
def focusOutEvent(self, event):
|
||||
self._focused = False
|
||||
if not self._hovered: # Сохраняем анимацию, если есть наведение
|
||||
self.focusChanged.emit(self.name, False)
|
||||
if not self._hovered:
|
||||
if self.gradient_anim:
|
||||
self.gradient_anim.stop()
|
||||
self.gradient_anim = None
|
||||
self.thickness_anim.stop()
|
||||
if self._isPulseAnimationConnected:
|
||||
self.thickness_anim.finished.disconnect(self.startPulseAnimation)
|
||||
self._isPulseAnimationConnected = False
|
||||
if self.pulse_anim:
|
||||
self.pulse_anim.stop()
|
||||
self.pulse_anim = None
|
||||
self.thickness_anim.setEasingCurve(QEasingCurve(QEasingCurve.Type.InBack))
|
||||
if self.thickness_anim:
|
||||
self.thickness_anim.stop()
|
||||
if self._isPulseAnimationConnected:
|
||||
self.thickness_anim.finished.disconnect(self.startPulseAnimation)
|
||||
self._isPulseAnimationConnected = False
|
||||
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):
|
||||
|
@ -56,6 +56,7 @@ class MainWindow(QMainWindow):
|
||||
self.current_exec_line = None
|
||||
self.currentDetailPage = None
|
||||
self.current_play_button = None
|
||||
self.current_focused_card = None
|
||||
self.pending_games = []
|
||||
self.game_card_cache = {}
|
||||
self.pending_images = {}
|
||||
@ -65,6 +66,7 @@ class MainWindow(QMainWindow):
|
||||
self.games_load_timer.timeout.connect(self.finalize_game_loading)
|
||||
self.games_loaded.connect(self.on_games_loaded)
|
||||
self.current_add_game_dialog = None
|
||||
self.current_hovered_card = None
|
||||
|
||||
# Добавляем таймер для дебаунсинга сохранения настроек
|
||||
self.settingsDebounceTimer = QTimer(self)
|
||||
@ -241,6 +243,65 @@ class MainWindow(QMainWindow):
|
||||
self.updateGameGrid()
|
||||
self.progress_bar.setVisible(False)
|
||||
|
||||
def _on_card_focused(self, game_name: str, is_focused: bool):
|
||||
"""Обработчик сигнала focusChanged от GameCard."""
|
||||
card_key = None
|
||||
for key, card in self.game_card_cache.items():
|
||||
if card.name == game_name:
|
||||
card_key = key
|
||||
break
|
||||
|
||||
if not card_key:
|
||||
return
|
||||
|
||||
card = self.game_card_cache[card_key]
|
||||
|
||||
if is_focused:
|
||||
# Если карточка получила фокус
|
||||
if self.current_hovered_card and self.current_hovered_card != card:
|
||||
# Сбрасываем текущую hovered карточку
|
||||
self.current_hovered_card._hovered = False
|
||||
self.current_hovered_card.leaveEvent(None)
|
||||
self.current_hovered_card = None
|
||||
if self.current_focused_card and self.current_focused_card != card:
|
||||
# Сбрасываем текущую focused карточку
|
||||
self.current_focused_card._focused = False
|
||||
self.current_focused_card.clearFocus()
|
||||
self.current_focused_card = card
|
||||
else:
|
||||
# Если карточка потеряла фокус
|
||||
if self.current_focused_card == card:
|
||||
self.current_focused_card = None
|
||||
|
||||
def _on_card_hovered(self, game_name: str, is_hovered: bool):
|
||||
"""Обработчик сигнала hoverChanged от GameCard."""
|
||||
card_key = None
|
||||
for key, card in self.game_card_cache.items():
|
||||
if card.name == game_name:
|
||||
card_key = key
|
||||
break
|
||||
|
||||
if not card_key:
|
||||
return
|
||||
|
||||
card = self.game_card_cache[card_key]
|
||||
|
||||
if is_hovered:
|
||||
# Если мышь наведена на карточку
|
||||
if self.current_focused_card and self.current_focused_card != card:
|
||||
# Сбрасываем текущую focused карточку
|
||||
self.current_focused_card._focused = False
|
||||
self.current_focused_card.clearFocus()
|
||||
if self.current_hovered_card and self.current_hovered_card != card:
|
||||
# Сбрасываем предыдущую hovered карточку
|
||||
self.current_hovered_card._hovered = False
|
||||
self.current_hovered_card.leaveEvent(None)
|
||||
self.current_hovered_card = card
|
||||
else:
|
||||
# Если мышь покинула карточку
|
||||
if self.current_hovered_card == card:
|
||||
self.current_hovered_card = None
|
||||
|
||||
def loadGames(self):
|
||||
display_filter = read_display_filter()
|
||||
favorites = read_favorites()
|
||||
@ -681,6 +742,8 @@ class MainWindow(QMainWindow):
|
||||
card_width=self.card_width,
|
||||
context_menu_manager=self.context_menu_manager
|
||||
)
|
||||
card.hoverChanged.connect(self._on_card_hovered)
|
||||
card.focusChanged.connect(self._on_card_focused)
|
||||
# Подключаем сигналы контекстного меню
|
||||
card.editShortcutRequested.connect(self.context_menu_manager.edit_game_shortcut)
|
||||
card.deleteGameRequested.connect(self.context_menu_manager.delete_game)
|
||||
|
@ -8,6 +8,76 @@ current_theme_name = read_theme_from_config()
|
||||
favoriteLabelSize = 48, 48
|
||||
pixmapsScaledSize = 60, 60
|
||||
|
||||
GAME_CARD_ANIMATION = {
|
||||
# Ширина обводки карточки в состоянии покоя (без наведения или фокуса).
|
||||
# Влияет на толщину рамки вокруг карточки, когда она не выделена.
|
||||
# Значение в пикселях.
|
||||
"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 {
|
||||
|
@ -8,6 +8,76 @@ current_theme_name = read_theme_from_config()
|
||||
favoriteLabelSize = 48, 48
|
||||
pixmapsScaledSize = 60, 60
|
||||
|
||||
GAME_CARD_ANIMATION = {
|
||||
# Ширина обводки карточки в состоянии покоя (без наведения или фокуса).
|
||||
# Влияет на толщину рамки вокруг карточки, когда она не выделена.
|
||||
# Значение в пикселях.
|
||||
"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;;
|
||||
|
Reference in New Issue
Block a user