feat: improve library filter controls #195
No files matched your search
@@ -1,9 +1,12 @@
|
||||
from portprotonqt.animations.detail_page import DetailPageAnimations
|
||||
from portprotonqt.animations.game_card import GameCardAnimations
|
||||
from portprotonqt.animations.library_controls import ExpandingSearchAnimation, LibraryControlsAnimation
|
||||
from portprotonqt.animations.virtual_keyboard import VirtualKeyboardAnimations
|
||||
|
||||
__all__ = [
|
||||
"DetailPageAnimations",
|
||||
"ExpandingSearchAnimation",
|
||||
"GameCardAnimations",
|
||||
"LibraryControlsAnimation",
|
||||
"VirtualKeyboardAnimations",
|
||||
]
|
||||
@@ -0,0 +1,117 @@
|
||||
from PySide6.QtCore import (
|
||||
QAbstractAnimation,
|
||||
QByteArray,
|
||||
QEasingCurve,
|
||||
QParallelAnimationGroup,
|
||||
QPropertyAnimation,
|
||||
)
|
||||
from PySide6.QtWidgets import QGraphicsOpacityEffect, QWidget
|
||||
|
||||
|
||||
class ExpandingSearchAnimation:
|
||||
def __init__(self, widget: QWidget, theme: object, fallback_duration: int):
|
||||
self.widget = widget
|
||||
self.theme = theme
|
||||
self.fallback_duration = fallback_duration
|
||||
self.animation = QPropertyAnimation(widget, QByteArray(b"maximumWidth"))
|
||||
|
||||
def setup(self, collapsed_width: int, expanded_width: int) -> None:
|
||||
self.collapsed_width = collapsed_width
|
||||
self.expanded_width = expanded_width
|
||||
self.widget.setMaximumWidth(collapsed_width)
|
||||
|
||||
def expand(self) -> None:
|
||||
self._start(self.expanded_width)
|
||||
|
||||
def collapse(self) -> None:
|
||||
if getattr(self.widget, "text", lambda: "")():
|
||||
return
|
||||
self._start(self.collapsed_width)
|
||||
|
||||
def _start(self, target_width: int) -> None:
|
||||
if self.animation.state() == QAbstractAnimation.State.Running:
|
||||
self.animation.stop()
|
||||
self.animation = QPropertyAnimation(self.widget, QByteArray(b"maximumWidth"))
|
||||
self.animation.setDuration(self._duration())
|
||||
self.animation.setStartValue(self.widget.maximumWidth())
|
||||
self.animation.setEndValue(target_width)
|
||||
self.animation.setEasingCurve(self._easing(target_width == self.expanded_width))
|
||||
self.animation.start()
|
||||
|
||||
def _duration(self) -> int:
|
||||
animation_config = getattr(self.theme, "GAME_CARD_ANIMATION", {})
|
||||
return animation_config.get("scale_anim_duration", self.fallback_duration)
|
||||
|
||||
def _easing(self, opening: bool) -> QEasingCurve:
|
||||
animation_config = getattr(self.theme, "GAME_CARD_ANIMATION", {})
|
||||
key = "scale_easing_curve" if opening else "scale_easing_curve_out"
|
||||
easing_name = animation_config.get(key, QEasingCurve.Type.InOutQuad.name)
|
||||
easing_type = getattr(QEasingCurve.Type, easing_name, QEasingCurve.Type.InOutQuad)
|
||||
return QEasingCurve(easing_type)
|
||||
|
||||
|
||||
class LibraryControlsAnimation:
|
||||
def __init__(self, widget: QWidget, theme: object, fallback_duration: int):
|
||||
self.widget = widget
|
||||
self.theme = theme
|
||||
self.fallback_duration = fallback_duration
|
||||
self.opacity_effect = QGraphicsOpacityEffect(widget)
|
||||
self.opacity_effect.setOpacity(0)
|
||||
self.widget.setGraphicsEffect(self.opacity_effect)
|
||||
self.group = QParallelAnimationGroup(widget)
|
||||
|
||||
def setup_hidden(self) -> None:
|
||||
self.widget.setVisible(False)
|
||||
self.widget.setMaximumHeight(0)
|
||||
|
||||
def toggle(self, opening: bool) -> None:
|
||||
if self.group.state() == QAbstractAnimation.State.Running:
|
||||
self.group.stop()
|
||||
if opening:
|
||||
self.widget.setVisible(True)
|
||||
|
||||
target_height = self.widget.sizeHint().height() if opening else 0
|
||||
self.group = QParallelAnimationGroup(self.widget)
|
||||
height_animation = self._create_widget_animation(
|
||||
b"maximumHeight",
|
||||
self.widget.maximumHeight(),
|
||||
target_height,
|
||||
)
|
||||
opacity_animation = self._create_opacity_animation(opening, height_animation.duration())
|
||||
self.group.addAnimation(height_animation)
|
||||
self.group.addAnimation(opacity_animation)
|
||||
if not opening:
|
||||
self.group.finished.connect(self.widget.hide)
|
||||
self.group.start()
|
||||
|
||||
def _create_widget_animation(
|
||||
self,
|
||||
property_name: bytes,
|
||||
start_value: object,
|
||||
end_value: object,
|
||||
) -> QPropertyAnimation:
|
||||
animation = QPropertyAnimation(self.widget, QByteArray(property_name))
|
||||
animation.setDuration(self._duration())
|
||||
animation.setStartValue(start_value)
|
||||
animation.setEndValue(end_value)
|
||||
animation.setEasingCurve(self._easing(end_value != 0))
|
||||
return animation
|
||||
|
||||
def _create_opacity_animation(self, opening: bool, duration: int) -> QPropertyAnimation:
|
||||
animation = QPropertyAnimation(self.opacity_effect, QByteArray(b"opacity"))
|
||||
animation.setDuration(duration)
|
||||
animation.setStartValue(self.opacity_effect.opacity())
|
||||
animation.setEndValue(1 if opening else 0)
|
||||
animation.setEasingCurve(self._easing(opening))
|
||||
return animation
|
||||
|
||||
def _duration(self) -> int:
|
||||
animation_config = getattr(self.theme, "GAME_CARD_ANIMATION", {})
|
||||
return animation_config.get("scale_anim_duration", self.fallback_duration)
|
||||
|
||||
def _easing(self, opening: bool) -> QEasingCurve:
|
||||
animation_config = getattr(self.theme, "GAME_CARD_ANIMATION", {})
|
||||
key = "scale_easing_curve" if opening else "scale_easing_curve_out"
|
||||
easing_name = animation_config.get(key, QEasingCurve.Type.InOutQuad.name)
|
||||
easing_type = getattr(QEasingCurve.Type, easing_name, QEasingCurve.Type.InOutQuad)
|
||||
return QEasingCurve(easing_type)
|
||||
@@ -1,6 +1,7 @@
|
||||
from typing import Protocol
|
||||
from portprotonqt.game_card import GameCard
|
||||
from portprotonqt.search_utils import SearchOptimizer, ThreadedSearch
|
||||
from PySide6.QtWidgets import QAbstractButton
|
||||
from PySide6.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QScrollArea, QSlider, QScroller, QStackedWidget
|
||||
from PySide6.QtCore import Qt, QTimer
|
||||
from PySide6.QtGui import QRegion
|
||||
@@ -197,6 +198,7 @@ class GameLibraryManager:
|
||||
card = self.game_card_cache[card_key]
|
||||
|
||||
if is_focused:
|
||||
self._collapse_library_filter_controls()
|
||||
if self.main_window.current_hovered_card and self.main_window.current_hovered_card != card:
|
||||
try:
|
||||
self.main_window.current_hovered_card._hovered = False
|
||||
@@ -215,6 +217,15 @@ class GameLibraryManager:
|
||||
if self.main_window.current_focused_card == card:
|
||||
self.main_window.current_focused_card = None
|
||||
|
||||
def _collapse_library_filter_controls(self) -> None:
|
||||
controls_button = getattr(self.main_window, "libraryControlsButton", None)
|
||||
if not isinstance(controls_button, QAbstractButton) or not controls_button.isChecked():
|
||||
return
|
||||
controls_button.setChecked(False)
|
||||
toggle_controls = getattr(self.main_window, "_toggle_library_controls", None)
|
||||
if callable(toggle_controls):
|
||||
toggle_controls()
|
||||
|
||||
def _on_card_hovered(self, game_name: str, is_hovered: bool):
|
||||
"""Handles card hover events."""
|
||||
card_key = None
|
||||
@@ -229,6 +240,7 @@ class GameLibraryManager:
|
||||
card = self.game_card_cache[card_key]
|
||||
|
||||
if is_hovered:
|
||||
self._collapse_library_filter_controls()
|
||||
if self.main_window.current_focused_card and self.main_window.current_focused_card != card:
|
||||
try:
|
||||
if self.main_window.current_focused_card:
|
||||
@@ -255,8 +267,15 @@ class GameLibraryManager:
|
||||
self._pending_update = False
|
||||
self._update_game_grid_immediate()
|
||||
|
||||
def update_game_grid(self, games_list: list[tuple] | None = None, is_filter: bool = False):
|
||||
def update_game_grid(
|
||||
self,
|
||||
games_list: list[tuple] | None = None,
|
||||
is_filter: bool = False,
|
||||
focus_first_card: bool | None = None,
|
||||
):
|
||||
"""Schedules a game grid update with debouncing."""
|
||||
if focus_first_card is not None:
|
||||
self._focus_first_card_after_update = focus_first_card
|
||||
self.layout_mode = str(getattr(self.theme, "LIBRARY_LAYOUT_MODE", "grid")).lower()
|
||||
if self.sizeSlider is not None:
|
||||
self.sizeSlider.setVisible(self.layout_mode != "list")
|
||||
@@ -666,11 +685,11 @@ class GameLibraryManager:
|
||||
self.game_card_cache.clear()
|
||||
self.pending_images.clear()
|
||||
|
||||
def set_games(self, games: list[tuple]):
|
||||
def set_games(self, games: list[tuple], focus_first_card: bool = True):
|
||||
"""Sets the games list and updates the filtered games."""
|
||||
self.games = games
|
||||
self.filtered_games = self.games
|
||||
self._focus_first_card_after_update = bool(games)
|
||||
self._focus_first_card_after_update = bool(games) and focus_first_card
|
||||
|
||||
# Build search indices for fast searching
|
||||
self._build_search_indices(games)
|
||||
|
||||
@@ -3687,12 +3687,44 @@ class InputManager(QObject):
|
||||
|
||||
def _get_library_toolbar_widgets(self) -> list[QWidget]:
|
||||
widgets = []
|
||||
for attr_name in ("quickLaunchButton", "addGameButton", "refreshButton", "searchEdit"):
|
||||
for attr_name in (
|
||||
"quickLaunchButton",
|
||||
"addGameButton",
|
||||
"refreshButton",
|
||||
"searchEdit",
|
||||
"libraryControlsButton",
|
||||
):
|
||||
widget = getattr(self._parent, attr_name, None)
|
||||
if isinstance(widget, QWidget) and widget.isVisible() and widget.isEnabled():
|
||||
widgets.append(widget)
|
||||
return widgets
|
||||
|
||||
def _get_library_filter_widgets(self) -> list[QWidget]:
|
||||
controls_widget = getattr(self._parent, "libraryControlsWidget", None)
|
||||
if not isinstance(controls_widget, QWidget) or not controls_widget.isVisible():
|
||||
return []
|
||||
widgets = []
|
||||
for attr_name in ("gamesSortCombo", "gamesDisplayCombo"):
|
||||
widget = getattr(self._parent, attr_name, None)
|
||||
if isinstance(widget, QWidget) and widget.isVisible() and widget.isEnabled():
|
||||
widgets.append(widget)
|
||||
return widgets
|
||||
|
||||
def _focus_library_filter_controls(self) -> bool:
|
||||
controls_button = getattr(self._parent, "libraryControlsButton", None)
|
||||
if not isinstance(controls_button, AutoSizeButton):
|
||||
return False
|
||||
if not controls_button.isChecked():
|
||||
controls_button.setChecked(True)
|
||||
toggle_controls = getattr(self._parent, "_toggle_library_controls", None)
|
||||
if callable(toggle_controls):
|
||||
toggle_controls()
|
||||
filter_widgets = self._get_library_filter_widgets()
|
||||
if not filter_widgets:
|
||||
return False
|
||||
filter_widgets[0].setFocus(Qt.FocusReason.OtherFocusReason)
|
||||
return True
|
||||
|
||||
def _focus_first_library_card(self) -> bool:
|
||||
container = getattr(self._parent, "gamesListWidget", None)
|
||||
if container is None:
|
||||
@@ -3712,7 +3744,23 @@ class InputManager(QObject):
|
||||
if value == 0 or self._parent.stackedWidget.currentIndex() != 0:
|
||||
return False
|
||||
toolbar_widgets = self._get_library_toolbar_widgets()
|
||||
filter_widgets = self._get_library_filter_widgets()
|
||||
focused = self._focused_widget()
|
||||
if focused in filter_widgets:
|
||||
if code == PAD_DPAD_X:
|
||||
current_index = filter_widgets.index(cast(QWidget, focused))
|
||||
next_index = current_index + (1 if value > 0 else -1)
|
||||
if 0 <= next_index < len(filter_widgets):
|
||||
filter_widgets[next_index].setFocus(Qt.FocusReason.OtherFocusReason)
|
||||
return True
|
||||
if code != PAD_DPAD_Y:
|
||||
return False
|
||||
if value < 0:
|
||||
controls_button = getattr(self._parent, "libraryControlsButton", None)
|
||||
if isinstance(controls_button, QWidget):
|
||||
controls_button.setFocus(Qt.FocusReason.OtherFocusReason)
|
||||
return True
|
||||
return self._focus_first_library_card()
|
||||
if focused not in toolbar_widgets:
|
||||
return False
|
||||
if code == PAD_DPAD_X:
|
||||
@@ -3724,6 +3772,9 @@ class InputManager(QObject):
|
||||
if code != PAD_DPAD_Y:
|
||||
return False
|
||||
if value > 0:
|
||||
controls_button = getattr(self._parent, "libraryControlsButton", None)
|
||||
if focused is controls_button and self._focus_library_filter_controls():
|
||||
return True
|
||||
return self._focus_first_library_card()
|
||||
tab_button = self._parent.tabButtons.get(0)
|
||||
if tab_button is not None and tab_button.isVisible():
|
||||
|
||||
+177
-75
@@ -12,7 +12,7 @@ from portprotonqt.logger import get_logger
|
||||
from portprotonqt.icon_extractor import generate_thumbnail
|
||||
from portprotonqt.dialogs import AddGameDialog, FileExplorer, WinetricksDialog, ExeSettingsDialog
|
||||
from portprotonqt.game_card import GameCard
|
||||
from portprotonqt.animations import DetailPageAnimations
|
||||
from portprotonqt.animations import DetailPageAnimations, ExpandingSearchAnimation, LibraryControlsAnimation
|
||||
from portprotonqt.custom_widgets import ClickableLabel, AutoSizeButton, NavLabel, FlowLayout
|
||||
from portprotonqt.detail_pages import DetailPageManager
|
||||
from portprotonqt.portproton_api import PortProtonAPI, get_user_conf_setting, set_user_conf_setting
|
||||
@@ -75,7 +75,7 @@ from portprotonqt.tabs.workers import MainWindowWorkersMixin
|
||||
from portprotonqt.settings_manager import get_available_prefix_options, get_available_wine_options
|
||||
|
||||
from PySide6.QtWidgets import (QLineEdit, QMainWindow, QWidget, QVBoxLayout, QLabel, QHBoxLayout, QStackedWidget, QComboBox,
|
||||
QDialog, QFormLayout, QMessageBox, QApplication, QPushButton, QProgressBar, QCheckBox, QSizePolicy, QGridLayout, QScrollArea, QScroller, QSlider, QFrame)
|
||||
QDialog, QFormLayout, QMessageBox, QApplication, QPushButton, QProgressBar, QCheckBox, QSizePolicy, QGridLayout, QScrollArea, QScroller, QSlider, QFrame, QToolButton)
|
||||
from PySide6.QtCore import Qt, QAbstractAnimation, QEvent, QUrl, Signal, QTimer, Slot, QProcess, QProcessEnvironment, QFileSystemWatcher, QStandardPaths
|
||||
from PySide6.QtGui import QIcon, QColor, QDesktopServices, QHideEvent, QShowEvent
|
||||
from typing import cast
|
||||
@@ -769,7 +769,9 @@ class MainWindow(MainWindowControlHintsMixin, MainWindowSystemTabMixin, MainWind
|
||||
def on_games_loaded(self, games: list[tuple]):
|
||||
self._loading_games = False
|
||||
self.games = games
|
||||
self.game_library_manager.set_games(games)
|
||||
focus_first_card = not getattr(self, "_preserve_library_focus_after_load", False)
|
||||
self._preserve_library_focus_after_load = False
|
||||
self.game_library_manager.set_games(games, focus_first_card=focus_first_card)
|
||||
self._known_portproton_desktops = self._get_portproton_desktop_files()
|
||||
|
||||
# Clear the refresh in progress flag
|
||||
@@ -1220,39 +1222,81 @@ class MainWindow(MainWindowControlHintsMixin, MainWindowSystemTabMixin, MainWind
|
||||
if index == 0 and self.isVisible() and not self.games:
|
||||
self.loadGames(force_load=True)
|
||||
|
||||
def createSearchWidget(self) -> tuple[QWidget, CustomLineEdit]:
|
||||
self.container = QWidget()
|
||||
self.container.setStyleSheet(self.theme.CONTAINER_STYLE)
|
||||
layout = QHBoxLayout(self.container)
|
||||
layout.setContentsMargins(0, 6, 0, 0)
|
||||
layout.setSpacing(10)
|
||||
def _set_combo_current_key(self, combo: QComboBox, keys: list[str], current: str) -> None:
|
||||
try:
|
||||
idx = keys.index(current)
|
||||
except ValueError:
|
||||
idx = 0
|
||||
combo.setCurrentIndex(idx)
|
||||
|
||||
# Quick Launch button
|
||||
def _create_library_combo(self, labels: list[str], tooltip: str) -> QComboBox:
|
||||
combo = QComboBox()
|
||||
combo.view().window().setWindowFlags(
|
||||
Qt.WindowType.Popup | Qt.WindowType.FramelessWindowHint
|
||||
)
|
||||
combo.view().window().setAttribute(
|
||||
Qt.WidgetAttribute.WA_TranslucentBackground
|
||||
)
|
||||
combo.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)
|
||||
combo.addItems(labels)
|
||||
combo.setToolTip(tooltip)
|
||||
combo_style = getattr(self.theme, "LIBRARY_FILTER_COMBOBOX_STYLE", self.theme.COMBOBOX_STYLE)
|
||||
combo.setStyleSheet(combo_style + self.theme.SCROLL_STYLE)
|
||||
combo.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
|
||||
return combo
|
||||
|
||||
def _on_library_sort_changed(self, index: int) -> None:
|
||||
if index < 0 or index >= len(self.sort_keys):
|
||||
return
|
||||
game_config.set_sort_method(self.sort_keys[index])
|
||||
if hasattr(self, "game_library_manager"):
|
||||
self.game_library_manager.update_game_grid(focus_first_card=False)
|
||||
|
||||
def _on_library_filter_changed(self, index: int) -> None:
|
||||
if index < 0 or index >= len(self.filter_keys):
|
||||
return
|
||||
game_config.set_display_filter(self.filter_keys[index])
|
||||
self.searchEdit.clear()
|
||||
self.games = []
|
||||
self._preserve_library_focus_after_load = True
|
||||
self.loadGames(force_load=True)
|
||||
|
||||
def _toggle_library_controls(self) -> None:
|
||||
self.libraryControlsAnimation.toggle(self.libraryControlsButton.isChecked())
|
||||
|
||||
def _create_library_controls_widget(self) -> QHBoxLayout:
|
||||
self.libraryControlsWidget = QWidget()
|
||||
controls_layout = QHBoxLayout(self.libraryControlsWidget)
|
||||
controls_layout.setContentsMargins(0, 0, 0, 0)
|
||||
controls_layout.setSpacing(10)
|
||||
return controls_layout
|
||||
|
||||
def _add_library_action_buttons(self, buttons_layout: QHBoxLayout) -> None:
|
||||
self.quickLaunchButton = AutoSizeButton(_("Quick Launch"), icon=self.theme_manager.get_icon("play", as_path=True))
|
||||
self.quickLaunchButton.setStyleSheet(self.theme.ADDGAME_BACK_BUTTON_STYLE)
|
||||
self.quickLaunchButton.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
|
||||
self.quickLaunchButton.clicked.connect(self.quickLaunch)
|
||||
layout.addWidget(self.quickLaunchButton)
|
||||
buttons_layout.addWidget(self.quickLaunchButton)
|
||||
|
||||
self.addGameButton = AutoSizeButton(_("Add a shortcut"), icon=self.theme_manager.get_icon("addgame", as_path=True))
|
||||
self.addGameButton.setStyleSheet(self.theme.ADDGAME_BACK_BUTTON_STYLE)
|
||||
self.addGameButton.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
|
||||
self.addGameButton.clicked.connect(self.openAddGameDialog)
|
||||
layout.addWidget(self.addGameButton)
|
||||
buttons_layout.addWidget(self.addGameButton)
|
||||
|
||||
# Refresh button
|
||||
self.refreshButton = AutoSizeButton(_("Refresh Grid"), icon=self.theme_manager.get_icon("update", as_path=True))
|
||||
self.refreshButton.setStyleSheet(self.theme.ADDGAME_BACK_BUTTON_STYLE)
|
||||
self.refreshButton.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
|
||||
self.refreshButton.clicked.connect(self.refreshGames)
|
||||
layout.addWidget(self.refreshButton)
|
||||
|
||||
layout.addStretch() # Add stretch to push search to the right
|
||||
buttons_layout.addWidget(self.refreshButton)
|
||||
buttons_layout.addStretch()
|
||||
|
||||
def _add_library_search(self, buttons_layout: QHBoxLayout) -> None:
|
||||
self.searchEdit = CustomLineEdit(self, theme=self.theme)
|
||||
icon: QIcon = cast(QIcon, self.theme_manager.get_icon("search"))
|
||||
action_pos = cast(QLineEdit.ActionPosition, QLineEdit.ActionPosition.LeadingPosition)
|
||||
self.searchEdit.addAction(icon, action_pos)
|
||||
self.searchIconAction = self.searchEdit.addAction(icon, action_pos)
|
||||
self.searchEdit.setMaximumWidth(200)
|
||||
self.searchEdit.setPlaceholderText(_("Search ..."))
|
||||
self.searchEdit.setClearButtonEnabled(True)
|
||||
@@ -1263,8 +1307,122 @@ class MainWindow(MainWindowControlHintsMixin, MainWindowSystemTabMixin, MainWind
|
||||
self.searchDebounceTimer.setSingleShot(True)
|
||||
self.searchDebounceTimer.setInterval(150) # Reduced debounce time for better responsiveness
|
||||
self.searchDebounceTimer.timeout.connect(self.on_search_changed)
|
||||
self.searchEdit.focusInEvent = self._wrap_search_focus_event(
|
||||
self.searchEdit.focusInEvent,
|
||||
True,
|
||||
)
|
||||
self.searchEdit.focusOutEvent = self._wrap_search_focus_event(
|
||||
self.searchEdit.focusOutEvent,
|
||||
False,
|
||||
)
|
||||
self.searchEdit.resizeEvent = self._wrap_search_resize_event(self.searchEdit.resizeEvent)
|
||||
buttons_layout.addWidget(self.searchEdit)
|
||||
|
||||
layout.addWidget(self.searchEdit)
|
||||
def _add_library_controls_button(self, buttons_layout: QHBoxLayout) -> None:
|
||||
self.libraryControlsButton = AutoSizeButton(
|
||||
icon=self.theme_manager.get_icon("menu", as_path=True)
|
||||
)
|
||||
button_style = getattr(
|
||||
self.theme,
|
||||
"LIBRARY_CONTROLS_BUTTON_STYLE",
|
||||
self.theme.ADDGAME_BACK_BUTTON_STYLE,
|
||||
)
|
||||
self.libraryControlsButton.setStyleSheet(button_style)
|
||||
self.libraryControlsButton.setToolTip(_("Library Settings"))
|
||||
self.libraryControlsButton.setCheckable(True)
|
||||
self.libraryControlsButton.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
|
||||
self.libraryControlsButton.clicked.connect(self._toggle_library_controls)
|
||||
buttons_layout.addWidget(self.libraryControlsButton)
|
||||
|
||||
def _setup_library_search_animation(self) -> None:
|
||||
self.searchAnimation = ExpandingSearchAnimation(
|
||||
self.searchEdit,
|
||||
self.theme,
|
||||
self.searchDebounceTimer.interval(),
|
||||
)
|
||||
collapsed_width = self.libraryControlsButton.sizeHint().width()
|
||||
expanded_width = self.searchEdit.maximumWidth()
|
||||
self.searchAnimation.setup(collapsed_width, expanded_width)
|
||||
QTimer.singleShot(0, self._center_collapsed_search_icon)
|
||||
|
||||
def _wrap_search_focus_event(self, original_event: Callable, expand: bool) -> Callable:
|
||||
def handle_focus_event(event):
|
||||
original_event(event)
|
||||
if not hasattr(self, "searchAnimation"):
|
||||
return
|
||||
if expand:
|
||||
self.searchAnimation.expand()
|
||||
else:
|
||||
self.searchAnimation.collapse()
|
||||
QTimer.singleShot(0, self._center_collapsed_search_icon)
|
||||
return handle_focus_event
|
||||
|
||||
def _wrap_search_resize_event(self, original_event: Callable) -> Callable:
|
||||
def handle_resize_event(event):
|
||||
original_event(event)
|
||||
self._center_collapsed_search_icon()
|
||||
return handle_resize_event
|
||||
|
||||
def _center_collapsed_search_icon(self) -> None:
|
||||
animation = getattr(self, "searchAnimation", None)
|
||||
if animation is None or self.searchEdit.maximumWidth() != animation.collapsed_width:
|
||||
return
|
||||
for button in self.searchEdit.findChildren(QToolButton):
|
||||
if button.defaultAction() is self.searchIconAction:
|
||||
size = button.sizeHint()
|
||||
x = (self.searchEdit.width() - size.width()) // 2
|
||||
y = (self.searchEdit.height() - size.height()) // 2
|
||||
button.setGeometry(x, y, size.width(), size.height())
|
||||
return
|
||||
|
||||
def _add_library_filter_controls(self, controls_layout: QHBoxLayout) -> None:
|
||||
self.sort_keys = ["last_launch", "playtime", "alphabetical"]
|
||||
self.sort_labels = [_("Last launch"), _("Time spent"), _("Alphabetical")]
|
||||
self.gamesSortCombo = self._create_library_combo(self.sort_labels, _("Sort Method:"))
|
||||
self._set_combo_current_key(
|
||||
self.gamesSortCombo,
|
||||
self.sort_keys,
|
||||
game_config.get_sort_method(),
|
||||
)
|
||||
self.gamesSortCombo.currentIndexChanged.connect(self._on_library_sort_changed)
|
||||
controls_layout.addStretch()
|
||||
controls_layout.addWidget(self.gamesSortCombo)
|
||||
|
||||
self.filter_keys = ["all", "steam", "portproton", "favorites"]
|
||||
self.filter_labels = [_("All"), "Steam", "PortProton", _("Favorites")]
|
||||
self.gamesDisplayCombo = self._create_library_combo(self.filter_labels, _("Display Filter:"))
|
||||
self._set_combo_current_key(
|
||||
self.gamesDisplayCombo,
|
||||
self.filter_keys,
|
||||
game_config.get_display_filter(),
|
||||
)
|
||||
self.gamesDisplayCombo.currentIndexChanged.connect(self._on_library_filter_changed)
|
||||
controls_layout.addWidget(self.gamesDisplayCombo)
|
||||
|
||||
def createSearchWidget(self) -> tuple[QWidget, CustomLineEdit]:
|
||||
self.container = QWidget()
|
||||
self.container.setStyleSheet(self.theme.CONTAINER_STYLE)
|
||||
layout = QVBoxLayout(self.container)
|
||||
layout.setContentsMargins(0, 6, 0, 0)
|
||||
layout.setSpacing(10)
|
||||
buttons_layout = QHBoxLayout()
|
||||
buttons_layout.setContentsMargins(0, 0, 0, 0)
|
||||
buttons_layout.setSpacing(10)
|
||||
controls_layout = self._create_library_controls_widget()
|
||||
|
||||
self._add_library_action_buttons(buttons_layout)
|
||||
self._add_library_search(buttons_layout)
|
||||
self.libraryControlsAnimation = LibraryControlsAnimation(
|
||||
self.libraryControlsWidget,
|
||||
self.theme,
|
||||
self.searchDebounceTimer.interval(),
|
||||
)
|
||||
self._add_library_controls_button(buttons_layout)
|
||||
self._setup_library_search_animation()
|
||||
self._add_library_filter_controls(controls_layout)
|
||||
self.libraryControlsAnimation.setup_hidden()
|
||||
layout.addLayout(buttons_layout)
|
||||
layout.addWidget(self.libraryControlsWidget)
|
||||
return self.container, self.searchEdit
|
||||
|
||||
def refreshGames(self):
|
||||
@@ -2377,56 +2535,6 @@ class MainWindow(MainWindowControlHintsMixin, MainWindowSystemTabMixin, MainWind
|
||||
self.timeDetailCombo.setCurrentIndex(idx)
|
||||
genForm.addRow(self.timeDetailTitle, self.timeDetailCombo)
|
||||
|
||||
self.gamesSortCombo = QComboBox()
|
||||
self.gamesSortCombo.view().window().setWindowFlags(
|
||||
Qt.WindowType.Popup | Qt.WindowType.FramelessWindowHint
|
||||
)
|
||||
self.gamesSortCombo.view().window().setAttribute(
|
||||
Qt.WidgetAttribute.WA_TranslucentBackground
|
||||
)
|
||||
self.gamesSortCombo.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
|
||||
self.sort_keys = ["last_launch", "playtime", "alphabetical"]
|
||||
self.sort_labels = [_("Last launch"), _("Time spent"), _("Alphabetical")]
|
||||
self.gamesSortCombo.addItems(self.sort_labels)
|
||||
self.gamesSortCombo.setStyleSheet(self.theme.COMBOBOX_STYLE + self.theme.SCROLL_STYLE)
|
||||
self.gamesSortCombo.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
|
||||
self.gamesSortTitle = QLabel(_("Sort Method:"))
|
||||
self.gamesSortTitle.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)
|
||||
self.gamesSortTitle.setStyleSheet(self.theme.SETTINGS_TITLE_STYLE)
|
||||
self.gamesSortTitle.setFocusPolicy(Qt.FocusPolicy.NoFocus)
|
||||
current = game_config.get_sort_method()
|
||||
try:
|
||||
idx = self.sort_keys.index(current)
|
||||
except ValueError:
|
||||
idx = 0
|
||||
self.gamesSortCombo.setCurrentIndex(idx)
|
||||
genForm.addRow(self.gamesSortTitle, self.gamesSortCombo)
|
||||
|
||||
self.filter_keys = ["all", "steam", "portproton", "favorites"]
|
||||
self.filter_labels = [_("All"), "Steam", "PortProton", _("Favorites")]
|
||||
self.gamesDisplayCombo = QComboBox()
|
||||
self.gamesDisplayCombo.view().window().setWindowFlags(
|
||||
Qt.WindowType.Popup | Qt.WindowType.FramelessWindowHint
|
||||
)
|
||||
self.gamesDisplayCombo.view().window().setAttribute(
|
||||
Qt.WidgetAttribute.WA_TranslucentBackground
|
||||
)
|
||||
self.gamesDisplayCombo.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
|
||||
self.gamesDisplayCombo.addItems(self.filter_labels)
|
||||
self.gamesDisplayCombo.setStyleSheet(self.theme.COMBOBOX_STYLE + self.theme.SCROLL_STYLE)
|
||||
self.gamesDisplayCombo.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
|
||||
self.gamesDisplayTitle = QLabel(_("Display Filter:"))
|
||||
self.gamesDisplayTitle.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)
|
||||
self.gamesDisplayTitle.setStyleSheet(self.theme.SETTINGS_TITLE_STYLE)
|
||||
self.gamesDisplayTitle.setFocusPolicy(Qt.FocusPolicy.NoFocus)
|
||||
current = game_config.get_display_filter()
|
||||
try:
|
||||
idx = self.filter_keys.index(current)
|
||||
except ValueError:
|
||||
idx = 0
|
||||
self.gamesDisplayCombo.setCurrentIndex(idx)
|
||||
genForm.addRow(self.gamesDisplayTitle, self.gamesDisplayCombo)
|
||||
|
||||
self.badge_view_keys = ["detailed", "compact", "hidden"]
|
||||
self.badge_view_labels = [_("Detailed"), _("Compact"), _("Hidden")]
|
||||
self.badgeViewCombo = QComboBox()
|
||||
@@ -2904,16 +3012,9 @@ class MainWindow(MainWindowControlHintsMixin, MainWindowSystemTabMixin, MainWind
|
||||
time_key = self.time_keys[time_idx]
|
||||
ui_config.set_time_detail_level(time_key)
|
||||
|
||||
sort_idx = self.gamesSortCombo.currentIndex()
|
||||
sort_key = self.sort_keys[sort_idx]
|
||||
game_config.set_sort_method(sort_key)
|
||||
|
||||
filter_idx = self.gamesDisplayCombo.currentIndex()
|
||||
filter_key = self.filter_keys[filter_idx]
|
||||
economy_mode = self.economyModeCheckBox.isChecked()
|
||||
ui_config.set_economy_mode(economy_mode)
|
||||
economy_mode_changed = previous_economy_mode != economy_mode
|
||||
game_config.set_display_filter(filter_key)
|
||||
badge_view_idx = self.badgeViewCombo.currentIndex()
|
||||
badge_view_mode = self.badge_view_keys[badge_view_idx]
|
||||
if economy_mode:
|
||||
@@ -2989,8 +3090,9 @@ class MainWindow(MainWindowControlHintsMixin, MainWindowSystemTabMixin, MainWind
|
||||
if self.game_library_manager.gamesListLayout is not None:
|
||||
self.game_library_manager.clear_layout(self.game_library_manager.gamesListLayout)
|
||||
else:
|
||||
display_filter = game_config.get_display_filter()
|
||||
for card in self.game_library_manager.game_card_cache.values():
|
||||
card.update_badge_visibility(filter_key)
|
||||
card.update_badge_visibility(display_filter)
|
||||
card.update_badge_view_mode(badge_view_mode)
|
||||
|
||||
if (
|
||||
|
||||
@@ -38,6 +38,7 @@ install_data(
|
||||
['animations/__init__.py',
|
||||
'animations/detail_page.py',
|
||||
'animations/game_card.py',
|
||||
'animations/library_controls.py',
|
||||
'animations/virtual_keyboard.py',
|
||||
],
|
||||
install_dir: pythondir / meson.project_name() / 'animations',
|
||||
|
||||
@@ -271,6 +271,96 @@ ADDGAME_BACK_BUTTON_STYLE = f"""
|
||||
}}
|
||||
"""
|
||||
|
||||
LIBRARY_CONTROLS_BUTTON_STYLE = f"""
|
||||
QPushButton {{
|
||||
background: qradialgradient(
|
||||
cx:0.5, cy:0.5, radius:0.8,
|
||||
stop:0 #E9ECEF,
|
||||
stop:1 {color_j}
|
||||
);
|
||||
border: {border_b} {color_f};
|
||||
border-radius: {border_radius_a};
|
||||
color: {color_l};
|
||||
font-size: {font_size_a};
|
||||
font-family: '{font_family}';
|
||||
padding: 5px 10px;
|
||||
min-width: 24px;
|
||||
min-height: 25px;
|
||||
}}
|
||||
QPushButton:hover,
|
||||
QPushButton:pressed,
|
||||
QPushButton:focus,
|
||||
QPushButton:checked {{
|
||||
background: qradialgradient(
|
||||
cx:0.5, cy:0.5, radius:0.8,
|
||||
stop:0 #E9ECEF,
|
||||
stop:1 {color_j}
|
||||
);
|
||||
border: {border_b} {color_a};
|
||||
}}
|
||||
"""
|
||||
|
||||
LIBRARY_FILTER_COMBOBOX_STYLE = f"""
|
||||
QComboBox {{
|
||||
background: {color_b};
|
||||
border: {border_b} {color_f};
|
||||
border-radius: {border_radius_a};
|
||||
padding-left: 12px;
|
||||
height: 30px;
|
||||
color: {color_l};
|
||||
font-family: '{font_family}';
|
||||
font-size: {font_size_a};
|
||||
min-width: 120px;
|
||||
combobox-popup: 0;
|
||||
}}
|
||||
QComboBox:hover,
|
||||
QComboBox:focus,
|
||||
QComboBox:on {{
|
||||
background: {color_b};
|
||||
border: {border_b} {color_a};
|
||||
}}
|
||||
QComboBox::drop-down {{
|
||||
subcontrol-origin: padding;
|
||||
subcontrol-position: center right;
|
||||
border-left: {border_b} rgba(255, 255, 255, 0.05);
|
||||
padding: 12px;
|
||||
height: 12px;
|
||||
width: 12px;
|
||||
}}
|
||||
QComboBox::down-arrow {{
|
||||
image: url({theme_manager.get_icon("down", current_theme_name, as_path=True)});
|
||||
padding: 12px;
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
}}
|
||||
QComboBox::down-arrow:on {{
|
||||
image: url({theme_manager.get_icon("up", current_theme_name, as_path=True)});
|
||||
padding: 12px;
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
}}
|
||||
QComboBox QAbstractItemView {{
|
||||
outline: none;
|
||||
background: {color_c};
|
||||
border: {border_b} {color_a};
|
||||
border-radius: {border_radius_a};
|
||||
color: {color_l};
|
||||
selection-background-color: {color_a};
|
||||
selection-color: {color_l};
|
||||
}}
|
||||
QComboBox QAbstractItemView::item {{
|
||||
background: {color_c};
|
||||
color: {color_l};
|
||||
padding: 6px 10px;
|
||||
min-height: 24px;
|
||||
}}
|
||||
QComboBox QAbstractItemView::item:hover,
|
||||
QComboBox QAbstractItemView::item:selected {{
|
||||
background: {color_a};
|
||||
color: {color_l};
|
||||
}}
|
||||
"""
|
||||
|
||||
# SEARCH FIELD STYLE
|
||||
SEARCH_EDIT_STYLE = f"""
|
||||
QLineEdit {{
|
||||
|
||||
@@ -201,6 +201,96 @@ ADDGAME_BACK_BUTTON_STYLE = f"""
|
||||
}}
|
||||
"""
|
||||
|
||||
LIBRARY_CONTROLS_BUTTON_STYLE = f"""
|
||||
QPushButton {{
|
||||
background: qradialgradient(
|
||||
cx:0.5, cy:0.5, radius:0.8,
|
||||
stop:0 #404554,
|
||||
stop:1 #30323d
|
||||
);
|
||||
border: {border_a};
|
||||
border-radius: {border_radius_a};
|
||||
color: {color_f};
|
||||
font-size: {font_size_a};
|
||||
font-family: '{font_family}';
|
||||
padding: 5px 10px;
|
||||
min-width: 24px;
|
||||
min-height: 25px;
|
||||
}}
|
||||
QPushButton:hover,
|
||||
QPushButton:pressed,
|
||||
QPushButton:focus,
|
||||
QPushButton:checked {{
|
||||
background: qradialgradient(
|
||||
cx:0.5, cy:0.5, radius:0.8,
|
||||
stop:0 #404554,
|
||||
stop:1 #30323d
|
||||
);
|
||||
border: {border_b} {color_a};
|
||||
}}
|
||||
"""
|
||||
|
||||
LIBRARY_FILTER_COMBOBOX_STYLE = f"""
|
||||
QComboBox {{
|
||||
background: {color_b};
|
||||
border: {border_a};
|
||||
border-radius: {border_radius_a};
|
||||
padding-left: 12px;
|
||||
height: 30px;
|
||||
color: {color_f};
|
||||
font-family: '{font_family}';
|
||||
font-size: {font_size_a};
|
||||
min-width: 120px;
|
||||
combobox-popup: 0;
|
||||
}}
|
||||
QComboBox:hover,
|
||||
QComboBox:focus,
|
||||
QComboBox:on {{
|
||||
background: {color_b};
|
||||
border: {border_b} {color_a};
|
||||
}}
|
||||
QComboBox::drop-down {{
|
||||
subcontrol-origin: padding;
|
||||
subcontrol-position: center right;
|
||||
border-left: {border_b} rgba(255, 255, 255, 0.05);
|
||||
padding: 12px;
|
||||
height: 12px;
|
||||
width: 12px;
|
||||
}}
|
||||
QComboBox::down-arrow {{
|
||||
image: url({theme_manager.get_icon("down", current_theme_name, as_path=True)});
|
||||
padding: 12px;
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
}}
|
||||
QComboBox::down-arrow:on {{
|
||||
image: url({theme_manager.get_icon("up", current_theme_name, as_path=True)});
|
||||
padding: 12px;
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
}}
|
||||
QComboBox QAbstractItemView {{
|
||||
outline: none;
|
||||
background: {color_c};
|
||||
border: {border_b} {color_a};
|
||||
border-radius: {border_radius_a};
|
||||
color: {color_f};
|
||||
selection-background-color: {color_a};
|
||||
selection-color: {color_f};
|
||||
}}
|
||||
QComboBox QAbstractItemView::item {{
|
||||
background: {color_c};
|
||||
color: {color_f};
|
||||
padding: 6px 10px;
|
||||
min-height: 24px;
|
||||
}}
|
||||
QComboBox QAbstractItemView::item:hover,
|
||||
QComboBox QAbstractItemView::item:selected {{
|
||||
background: {color_a};
|
||||
color: {color_f};
|
||||
}}
|
||||
"""
|
||||
|
||||
# SEARCH FIELD STYLE
|
||||
SEARCH_EDIT_STYLE = f"""
|
||||
QLineEdit {{
|
||||
|
||||
@@ -788,6 +788,96 @@ ADDGAME_BACK_BUTTON_STYLE = f"""
|
||||
}}
|
||||
"""
|
||||
|
||||
LIBRARY_CONTROLS_BUTTON_STYLE = f"""
|
||||
QPushButton {{
|
||||
background: qradialgradient(
|
||||
cx:0.5, cy:0.5, radius:0.8,
|
||||
stop:0 #E9ECEF,
|
||||
stop:1 {color_j}
|
||||
);
|
||||
border: {border_b} {color_f};
|
||||
border-radius: {border_radius_a};
|
||||
color: {color_l};
|
||||
font-size: {font_size_a};
|
||||
font-family: '{font_family}';
|
||||
padding: 5px 10px;
|
||||
min-width: 24px;
|
||||
min-height: 25px;
|
||||
}}
|
||||
QPushButton:hover,
|
||||
QPushButton:pressed,
|
||||
QPushButton:focus,
|
||||
QPushButton:checked {{
|
||||
background: qradialgradient(
|
||||
cx:0.5, cy:0.5, radius:0.8,
|
||||
stop:0 #E9ECEF,
|
||||
stop:1 {color_j}
|
||||
);
|
||||
border: {border_b} {color_a};
|
||||
}}
|
||||
"""
|
||||
|
||||
LIBRARY_FILTER_COMBOBOX_STYLE = f"""
|
||||
QComboBox {{
|
||||
background: {color_b};
|
||||
border: {border_b} {color_f};
|
||||
border-radius: {border_radius_a};
|
||||
padding-left: 12px;
|
||||
height: 34px;
|
||||
color: {color_l};
|
||||
font-family: '{font_family}';
|
||||
font-size: {font_size_a};
|
||||
min-width: 120px;
|
||||
combobox-popup: 0;
|
||||
}}
|
||||
QComboBox:hover,
|
||||
QComboBox:focus,
|
||||
QComboBox:on {{
|
||||
background: {color_b};
|
||||
border: {border_b} {color_a};
|
||||
}}
|
||||
QComboBox::drop-down {{
|
||||
subcontrol-origin: padding;
|
||||
subcontrol-position: center right;
|
||||
border-left: {border_b} rgba(255, 255, 255, 0.05);
|
||||
padding: 12px;
|
||||
height: 12px;
|
||||
width: 12px;
|
||||
}}
|
||||
QComboBox::down-arrow {{
|
||||
image: url({theme_manager.get_icon("down", current_theme_name, as_path=True)});
|
||||
padding: 12px;
|
||||
height: 12px;
|
||||
width: 12px;
|
||||
}}
|
||||
QComboBox::down-arrow:on {{
|
||||
image: url({theme_manager.get_icon("up", current_theme_name, as_path=True)});
|
||||
padding: 12px;
|
||||
height: 12px;
|
||||
width: 12px;
|
||||
}}
|
||||
QComboBox QAbstractItemView {{
|
||||
outline: none;
|
||||
background: {color_c};
|
||||
border: {border_b} {color_a};
|
||||
border-radius: {border_radius_a};
|
||||
color: {color_l};
|
||||
selection-background-color: {color_a};
|
||||
selection-color: {color_l};
|
||||
}}
|
||||
QComboBox QAbstractItemView::item {{
|
||||
background: {color_c};
|
||||
color: {color_l};
|
||||
padding: 6px 10px;
|
||||
min-height: 24px;
|
||||
}}
|
||||
QComboBox QAbstractItemView::item:hover,
|
||||
QComboBox QAbstractItemView::item:selected {{
|
||||
background: {color_a};
|
||||
color: {color_l};
|
||||
}}
|
||||
"""
|
||||
|
||||
# SEARCH FIELD STYLE
|
||||
SEARCH_EDIT_STYLE = f"""
|
||||
QLineEdit {{
|
||||
|
||||
@@ -26,6 +26,87 @@ ADDGAME_BACK_BUTTON_STYLE = f"""
|
||||
}}
|
||||
"""
|
||||
|
||||
LIBRARY_CONTROLS_BUTTON_STYLE = f"""
|
||||
QPushButton {{
|
||||
background: rgba(20, 20, 20, 0.40);
|
||||
border: {border_b} rgba(255, 255, 255, 0.5);
|
||||
border-radius: {border_radius_a};
|
||||
color: {color_f};
|
||||
font-size: {font_size_a};
|
||||
font-family: '{font_family}';
|
||||
padding: 8px 12px;
|
||||
min-width: 24px;
|
||||
}}
|
||||
QPushButton:hover,
|
||||
QPushButton:pressed,
|
||||
QPushButton:focus,
|
||||
QPushButton:checked {{
|
||||
background: rgba(20, 20, 20, 0.40);
|
||||
border: {border_b} {color_a};
|
||||
}}
|
||||
"""
|
||||
|
||||
LIBRARY_FILTER_COMBOBOX_STYLE = f"""
|
||||
QComboBox {{
|
||||
background: rgba(20, 20, 20, 0.40);
|
||||
border: {border_b} rgba(255, 255, 255, 0.5);
|
||||
border-radius: {border_radius_a};
|
||||
padding-left: 12px;
|
||||
height: 34px;
|
||||
color: {color_f};
|
||||
font-family: '{font_family}';
|
||||
font-size: {font_size_a};
|
||||
min-width: 120px;
|
||||
combobox-popup: 0;
|
||||
}}
|
||||
QComboBox:hover,
|
||||
QComboBox:focus,
|
||||
QComboBox:on {{
|
||||
background: rgba(20, 20, 20, 0.40);
|
||||
border: {border_b} {color_a};
|
||||
}}
|
||||
QComboBox::drop-down {{
|
||||
subcontrol-origin: padding;
|
||||
subcontrol-position: center right;
|
||||
border-left: {border_b} rgba(255, 255, 255, 0.05);
|
||||
padding: 12px;
|
||||
height: 12px;
|
||||
width: 12px;
|
||||
}}
|
||||
QComboBox::down-arrow {{
|
||||
image: url({theme_manager.get_icon("down", current_theme_name, as_path=True)});
|
||||
padding: 12px;
|
||||
height: 12px;
|
||||
width: 12px;
|
||||
}}
|
||||
QComboBox::down-arrow:on {{
|
||||
image: url({theme_manager.get_icon("up", current_theme_name, as_path=True)});
|
||||
padding: 12px;
|
||||
height: 12px;
|
||||
width: 12px;
|
||||
}}
|
||||
QComboBox QAbstractItemView {{
|
||||
outline: none;
|
||||
background: {color_c};
|
||||
border: {border_b} {color_a};
|
||||
border-radius: {border_radius_a};
|
||||
color: {color_f};
|
||||
selection-background-color: {color_a};
|
||||
selection-color: {color_f};
|
||||
}}
|
||||
QComboBox QAbstractItemView::item {{
|
||||
background: {color_c};
|
||||
color: {color_f};
|
||||
padding: 6px 10px;
|
||||
min-height: 24px;
|
||||
}}
|
||||
QComboBox QAbstractItemView::item:hover,
|
||||
QComboBox QAbstractItemView::item:selected {{
|
||||
background: {color_a};
|
||||
color: {color_f};
|
||||
}}
|
||||
"""
|
||||
|
||||
# MAIN FRAME FOR GAME DETAILS
|
||||
DETAIL_CONTENT_FRAME_STYLE = f"""
|
||||
QFrame {{
|
||||
|
||||
Reference in new issue
Block a user