From 93459dfff645fd414d7c3f9e5e725d4de2c25105 Mon Sep 17 00:00:00 2001 From: pax Date: Fri, 10 Apr 2026 19:58:11 -0500 Subject: [PATCH] UI overhaul: icon buttons, video controls, popout anchor, layout flip, compact top bar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Preview/popout toolbar: icon buttons (☆/★, ↓/✕, ⊘, ⊗, ⧉) with QSS object names (#_tb_bookmark, #_tb_save, etc.) for theme targeting - Video controls: QPainter-drawn icons for play/pause, volume/mute; text labels for loop/once/next and autoplay - Popout anchor setting: resize pivot (center/tl/tr/bl/br) controls which corner stays fixed on aspect change, works on all platforms - Hyprland monitor reserved areas: reads waybar exclusive zones from hyprctl monitors -j for correct edge positioning - Layout flip setting: swap grid and preview sides - Compact top bar: AdjustToContents combos, tighter spacing, named containers (#_top_bar, #_nav_bar) for QSS targeting - Reduced main window minimum size from 900x600 to 740x400 - Trimmed bundled QSS: removed 12 unused widget selectors, added popout overlay font-weight/size, regenerated all 12 theme files - Updated themes/README.md with icon button reference --- booru_viewer/gui/app_runtime.py | 2 + booru_viewer/gui/main_window.py | 51 ++++--- booru_viewer/gui/media/video_player.py | 157 ++++++++++++++----- booru_viewer/gui/popout/hyprland.py | 40 +++++ booru_viewer/gui/popout/viewport.py | 34 ++++- booru_viewer/gui/popout/window.py | 137 +++++++++++------ booru_viewer/gui/popout_controller.py | 3 +- booru_viewer/gui/preview_pane.py | 49 +++--- booru_viewer/gui/settings.py | 18 +++ themes/README.md | 39 +++-- themes/catppuccin-mocha-rounded.qss | 200 ++++++------------------ themes/catppuccin-mocha-square.qss | 197 ++++++------------------ themes/everforest-rounded.qss | 202 ++++++------------------- themes/everforest-square.qss | 199 ++++++------------------ themes/gruvbox-rounded.qss | 202 ++++++------------------- themes/gruvbox-square.qss | 199 ++++++------------------ themes/nord-rounded.qss | 200 ++++++------------------ themes/nord-square.qss | 197 ++++++------------------ themes/solarized-dark-rounded.qss | 200 ++++++------------------ themes/solarized-dark-square.qss | 197 ++++++------------------ themes/tokyo-night-rounded.qss | 200 ++++++------------------ themes/tokyo-night-square.qss | 197 ++++++------------------ 22 files changed, 895 insertions(+), 2025 deletions(-) diff --git a/booru_viewer/gui/app_runtime.py b/booru_viewer/gui/app_runtime.py index 6087c0b..80a592b 100644 --- a/booru_viewer/gui/app_runtime.py +++ b/booru_viewer/gui/app_runtime.py @@ -119,6 +119,8 @@ QWidget#_slideshow_controls QPushButton { color: white; border: 1px solid rgba(255, 255, 255, 80); padding: 2px 6px; + font-size: 15px; + font-weight: bold; } QWidget#_slideshow_toolbar QPushButton:hover, QWidget#_slideshow_controls QPushButton:hover { diff --git a/booru_viewer/gui/main_window.py b/booru_viewer/gui/main_window.py index f9e85b5..4e33239 100644 --- a/booru_viewer/gui/main_window.py +++ b/booru_viewer/gui/main_window.py @@ -63,7 +63,7 @@ class BooruApp(QMainWindow): def __init__(self) -> None: super().__init__() self.setWindowTitle("booru-viewer") - self.setMinimumSize(900, 600) + self.setMinimumSize(740, 400) self.resize(1200, 800) self._db = Database() @@ -224,17 +224,22 @@ class BooruApp(QMainWindow): layout.setSpacing(6) # Top bar: site selector + rating + search - top = QHBoxLayout() + _top_bar = QWidget() + _top_bar.setObjectName("_top_bar") + top = QHBoxLayout(_top_bar) + top.setContentsMargins(0, 0, 0, 0) + top.setSpacing(3) self._site_combo = QComboBox() - self._site_combo.setMinimumWidth(150) + self._site_combo.setMinimumWidth(80) + self._site_combo.setSizeAdjustPolicy(QComboBox.SizeAdjustPolicy.AdjustToContents) self._site_combo.currentIndexChanged.connect(self._on_site_changed) top.addWidget(self._site_combo) # Rating filter self._rating_combo = QComboBox() self._rating_combo.addItems(["All", "General", "Sensitive", "Questionable", "Explicit"]) - self._rating_combo.setMinimumWidth(100) + self._rating_combo.setSizeAdjustPolicy(QComboBox.SizeAdjustPolicy.AdjustToContents) self._rating_combo.currentTextChanged.connect(self._on_rating_changed) top.addWidget(self._rating_combo) @@ -242,23 +247,17 @@ class BooruApp(QMainWindow): self._media_filter = QComboBox() self._media_filter.addItems(["All", "Animated", "Video", "GIF", "Audio"]) self._media_filter.setToolTip("Filter by media type") - self._media_filter.setFixedWidth(90) + self._media_filter.setSizeAdjustPolicy(QComboBox.SizeAdjustPolicy.AdjustToContents) top.addWidget(self._media_filter) - # Score filter — type the value directly. Spinbox arrows hidden - # since the field is small enough to type into and the +/- buttons - # were just visual noise. setFixedHeight(23) overrides Qt's - # QSpinBox sizeHint which still reserves vertical space for the - # arrow buttons internally even when `setButtonSymbols(NoButtons)` - # is set, leaving the spinbox 3px taller than the surrounding - # combos/inputs/buttons in the top toolbar (26 vs 23). - score_label = QLabel("Score≥") + # Score filter + score_label = QLabel("Score\u2265") top.addWidget(score_label) self._score_spin = QSpinBox() self._score_spin.setRange(0, 99999) self._score_spin.setValue(0) - self._score_spin.setFixedWidth(40) - self._score_spin.setFixedHeight(23) + self._score_spin.setFixedWidth(36) + self._score_spin.setFixedHeight(21) self._score_spin.setButtonSymbols(QSpinBox.ButtonSymbols.NoButtons) top.addWidget(self._score_spin) @@ -267,8 +266,8 @@ class BooruApp(QMainWindow): self._page_spin = QSpinBox() self._page_spin.setRange(1, 99999) self._page_spin.setValue(1) - self._page_spin.setFixedWidth(40) - self._page_spin.setFixedHeight(23) # match the surrounding 23px row + self._page_spin.setFixedWidth(36) + self._page_spin.setFixedHeight(21) self._page_spin.setButtonSymbols(QSpinBox.ButtonSymbols.NoButtons) top.addWidget(self._page_spin) @@ -277,10 +276,15 @@ class BooruApp(QMainWindow): self._search_bar.autocomplete_requested.connect(self._search_ctrl.request_autocomplete) top.addWidget(self._search_bar, stretch=1) - layout.addLayout(top) + layout.addWidget(_top_bar) # Nav bar - nav = QHBoxLayout() + _nav_bar = QWidget() + _nav_bar.setObjectName("_nav_bar") + nav = QHBoxLayout(_nav_bar) + nav.setContentsMargins(0, 0, 0, 0) + nav.setSpacing(3) + self._browse_btn = QPushButton("Browse") self._browse_btn.setCheckable(True) self._browse_btn.setChecked(True) @@ -294,11 +298,10 @@ class BooruApp(QMainWindow): self._library_btn = QPushButton("Library") self._library_btn.setCheckable(True) - self._library_btn.setFixedWidth(80) self._library_btn.clicked.connect(lambda: self._switch_view(2)) nav.addWidget(self._library_btn) - layout.addLayout(nav) + layout.addWidget(_nav_bar) # Main content self._splitter = QSplitter(Qt.Orientation.Horizontal) @@ -359,7 +362,7 @@ class BooruApp(QMainWindow): # BL Post, [stretch], Popout) has room to lay out all five buttons # at their fixed widths plus spacing without clipping the rightmost # one or compressing the row visually. - self._preview.setMinimumWidth(380) + self._preview.setMinimumWidth(200) right.addWidget(self._preview) self._dl_progress = QProgressBar() @@ -405,6 +408,10 @@ class BooruApp(QMainWindow): self._splitter.addWidget(right) + # Flip layout: preview on the left, grid on the right + if self._db.get_setting_bool("flip_layout"): + self._splitter.insertWidget(0, right) + # Restore the persisted main-splitter sizes if present, otherwise # fall back to the historic default. The sizes are saved as a # comma-separated string in the settings table — same format as diff --git a/booru_viewer/gui/media/video_player.py b/booru_viewer/gui/media/video_player.py index e27e2f5..5d1189b 100644 --- a/booru_viewer/gui/media/video_player.py +++ b/booru_viewer/gui/media/video_player.py @@ -6,12 +6,91 @@ import logging import os from pathlib import Path -from PySide6.QtCore import Qt, QTimer, Signal, Property -from PySide6.QtGui import QColor +from PySide6.QtCore import Qt, QTimer, Signal, Property, QPoint +from PySide6.QtGui import QColor, QIcon, QPixmap, QPainter, QPen, QBrush, QPolygon, QPainterPath from PySide6.QtWidgets import ( QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QSlider, QStyle, + QApplication, ) + +def _paint_icon(shape: str, color: QColor, size: int = 16) -> QIcon: + """Paint a media control icon using the given color.""" + pix = QPixmap(size, size) + pix.fill(Qt.GlobalColor.transparent) + p = QPainter(pix) + p.setRenderHint(QPainter.RenderHint.Antialiasing) + p.setPen(Qt.PenStyle.NoPen) + p.setBrush(color) + s = size + + if shape == "play": + p.drawPolygon(QPolygon([QPoint(3, 2), QPoint(3, s - 2), QPoint(s - 2, s // 2)])) + + elif shape == "pause": + w = max(2, s // 4) + p.drawRect(2, 2, w, s - 4) + p.drawRect(s - 2 - w, 2, w, s - 4) + + elif shape == "volume": + # Speaker cone + p.drawPolygon(QPolygon([ + QPoint(1, s // 2 - 2), QPoint(4, s // 2 - 2), + QPoint(8, 2), QPoint(8, s - 2), + QPoint(4, s // 2 + 2), QPoint(1, s // 2 + 2), + ])) + # Sound waves + p.setPen(QPen(color, 1.5)) + p.setBrush(Qt.BrushStyle.NoBrush) + path = QPainterPath() + path.arcMoveTo(8, 3, 6, s - 6, 45) + path.arcTo(8, 3, 6, s - 6, 45, -90) + p.drawPath(path) + + elif shape == "muted": + p.drawPolygon(QPolygon([ + QPoint(1, s // 2 - 2), QPoint(4, s // 2 - 2), + QPoint(8, 2), QPoint(8, s - 2), + QPoint(4, s // 2 + 2), QPoint(1, s // 2 + 2), + ])) + p.setPen(QPen(color, 2)) + p.drawLine(10, 4, s - 2, s - 4) + p.drawLine(10, s - 4, s - 2, 4) + + elif shape == "loop": + p.setPen(QPen(color, 1.5)) + p.setBrush(Qt.BrushStyle.NoBrush) + path = QPainterPath() + path.arcMoveTo(2, 2, s - 4, s - 4, 30) + path.arcTo(2, 2, s - 4, s - 4, 30, 300) + p.drawPath(path) + # Arrowhead + p.setPen(Qt.PenStyle.NoPen) + p.setBrush(color) + end = path.currentPosition().toPoint() + p.drawPolygon(QPolygon([ + end, QPoint(end.x() - 4, end.y() - 3), QPoint(end.x() + 1, end.y() - 4), + ])) + + elif shape == "once": + p.setPen(QPen(color, 2)) + p.setBrush(Qt.BrushStyle.NoBrush) + mid = s // 2 + p.drawLine(mid, 3, mid, s - 3) + p.drawLine(mid - 2, 5, mid, 3) + + elif shape == "next": + p.drawPolygon(QPolygon([QPoint(2, 2), QPoint(2, s - 2), QPoint(s - 5, s // 2)])) + p.drawRect(s - 4, 2, 2, s - 4) + + elif shape == "auto": + mid = s // 2 + p.drawPolygon(QPolygon([QPoint(1, 3), QPoint(1, s - 3), QPoint(mid - 1, s // 2)])) + p.drawPolygon(QPolygon([QPoint(mid, 3), QPoint(mid, s - 3), QPoint(s - 2, s // 2)])) + + p.end() + return QIcon(pix) + import mpv as mpvlib log = logging.getLogger(__name__) @@ -119,15 +198,22 @@ class VideoPlayer(QWidget): controls = QHBoxLayout(self._controls_bar) controls.setContentsMargins(4, 2, 4, 2) - # Compact-padding override matches the top preview toolbar so the - # bottom controls bar reads as part of the same panel rather than - # as a stamped-in overlay. Bundled themes' default `padding: 5px 12px` - # is too wide for short labels in narrow button slots. - _ctrl_btn_style = "padding: 2px 6px;" + _btn_sz = 24 + _fg = self.palette().buttonText().color() - self._play_btn = QPushButton("Play") - self._play_btn.setMaximumWidth(65) - self._play_btn.setStyleSheet(_ctrl_btn_style) + def _icon_btn(shape: str, name: str, tip: str) -> QPushButton: + btn = QPushButton() + btn.setObjectName(name) + btn.setIcon(_paint_icon(shape, _fg)) + btn.setFixedSize(_btn_sz, _btn_sz) + btn.setToolTip(tip) + return btn + + self._icon_fg = _fg + self._play_icon = _paint_icon("play", _fg) + self._pause_icon = _paint_icon("pause", _fg) + + self._play_btn = _icon_btn("play", "_ctrl_play", "Play / Pause (Space)") self._play_btn.clicked.connect(self._toggle_play) controls.addWidget(self._play_btn) @@ -152,28 +238,29 @@ class VideoPlayer(QWidget): self._vol_slider.valueChanged.connect(self._set_volume) controls.addWidget(self._vol_slider) - self._mute_btn = QPushButton("Mute") - self._mute_btn.setMaximumWidth(80) - self._mute_btn.setStyleSheet(_ctrl_btn_style) + self._vol_icon = _paint_icon("volume", _fg) + self._muted_icon = _paint_icon("muted", _fg) + + self._mute_btn = _icon_btn("volume", "_ctrl_mute", "Mute / Unmute") self._mute_btn.clicked.connect(self._toggle_mute) controls.addWidget(self._mute_btn) self._autoplay = True - self._autoplay_btn = QPushButton("Auto") - self._autoplay_btn.setMaximumWidth(70) - self._autoplay_btn.setStyleSheet(_ctrl_btn_style) + self._auto_icon = _paint_icon("auto", _fg) + self._autoplay_btn = _icon_btn("auto", "_ctrl_autoplay", "Auto-play videos when selected") self._autoplay_btn.setCheckable(True) self._autoplay_btn.setChecked(True) - self._autoplay_btn.setToolTip("Auto-play videos when selected") self._autoplay_btn.clicked.connect(self._toggle_autoplay) self._autoplay_btn.hide() controls.addWidget(self._autoplay_btn) + self._loop_icons = { + 0: _paint_icon("loop", _fg), + 1: _paint_icon("once", _fg), + 2: _paint_icon("next", _fg), + } self._loop_state = 0 # 0=Loop, 1=Once, 2=Next - self._loop_btn = QPushButton("Loop") - self._loop_btn.setMaximumWidth(60) - self._loop_btn.setStyleSheet(_ctrl_btn_style) - self._loop_btn.setToolTip("Loop: repeat / Once: stop at end / Next: advance") + self._loop_btn = _icon_btn("loop", "_ctrl_loop", "Loop / Once / Next") self._loop_btn.clicked.connect(self._cycle_loop) controls.addWidget(self._loop_btn) @@ -295,7 +382,7 @@ class VideoPlayer(QWidget): self._pending_mute = val if self._mpv: self._mpv.mute = val - self._mute_btn.setText("Unmute" if val else "Mute") + self._mute_btn.setIcon(self._muted_icon if val else self._vol_icon) @property def autoplay(self) -> bool: @@ -314,8 +401,9 @@ class VideoPlayer(QWidget): @loop_state.setter def loop_state(self, val: int) -> None: self._loop_state = val - labels = ["Loop", "Once", "Next"] - self._loop_btn.setText(labels[val]) + tips = ["Loop: repeat", "Once: stop at end", "Next: advance"] + self._loop_btn.setIcon(self._loop_icons[val]) + self._loop_btn.setToolTip(tips[val]) self._autoplay_btn.setVisible(val == 2) self._apply_loop_to_mpv() @@ -386,7 +474,7 @@ class VideoPlayer(QWidget): m.pause = False else: m.pause = True - self._play_btn.setText("Pause" if not m.pause else "Play") + self._play_btn.setIcon(self._pause_icon if not m.pause else self._play_icon) self._poll_timer.start() def stop(self) -> None: @@ -397,17 +485,17 @@ class VideoPlayer(QWidget): self._time_label.setText("0:00") self._duration_label.setText("0:00") self._seek_slider.setRange(0, 0) - self._play_btn.setText("Play") + self._play_btn.setIcon(self._play_icon) def pause(self) -> None: if self._mpv: self._mpv.pause = True - self._play_btn.setText("Play") + self._play_btn.setIcon(self._play_icon) def resume(self) -> None: if self._mpv: self._mpv.pause = False - self._play_btn.setText("Pause") + self._play_btn.setIcon(self._pause_icon) # -- Internal controls -- @@ -415,11 +503,12 @@ class VideoPlayer(QWidget): if not self._mpv: return self._mpv.pause = not self._mpv.pause - self._play_btn.setText("Play" if self._mpv.pause else "Pause") + self._play_btn.setIcon(self._play_icon if self._mpv.pause else self._pause_icon) def _toggle_autoplay(self, checked: bool = True) -> None: self._autoplay = self._autoplay_btn.isChecked() - self._autoplay_btn.setText("Autoplay" if self._autoplay else "Manual") + self._autoplay_btn.setIcon(self._auto_icon if self._autoplay else self._play_icon) + self._autoplay_btn.setToolTip("Autoplay on" if self._autoplay else "Autoplay off") def _cycle_loop(self) -> None: self.loop_state = (self._loop_state + 1) % 3 @@ -463,7 +552,7 @@ class VideoPlayer(QWidget): if self._mpv: self._mpv.mute = not self._mpv.mute self._pending_mute = bool(self._mpv.mute) - self._mute_btn.setText("Unmute" if self._mpv.mute else "Mute") + self._mute_btn.setIcon(self._muted_icon if self._mpv.mute else self._vol_icon) # -- mpv callbacks (called from mpv thread) -- @@ -528,9 +617,9 @@ class VideoPlayer(QWidget): # Pause state paused = self._mpv.pause - expected_text = "Play" if paused else "Pause" - if self._play_btn.text() != expected_text: - self._play_btn.setText(expected_text) + expected_icon = self._play_icon if paused else self._pause_icon + if self._play_btn.icon().cacheKey() != expected_icon.cacheKey(): + self._play_btn.setIcon(expected_icon) # Video size (set by observer on mpv thread, emitted here on main thread) if self._pending_video_size is not None: diff --git a/booru_viewer/gui/popout/hyprland.py b/booru_viewer/gui/popout/hyprland.py index 729946f..5ceaafd 100644 --- a/booru_viewer/gui/popout/hyprland.py +++ b/booru_viewer/gui/popout/hyprland.py @@ -171,8 +171,48 @@ def _dispatch_batch(cmds: list[str]) -> None: pass +def get_monitor_available_rect(monitor_id: int | None = None) -> tuple[int, int, int, int] | None: + """Return (x, y, w, h) of a monitor's usable area, accounting for + exclusive zones (Waybar, etc.) via the ``reserved`` field. + + Falls back to the first monitor if *monitor_id* is None or not found. + Returns None if not on Hyprland or the query fails. + """ + if not _on_hyprland(): + return None + try: + result = subprocess.run( + ["hyprctl", "monitors", "-j"], + capture_output=True, text=True, timeout=1, + ) + monitors = json.loads(result.stdout) + if not monitors: + return None + mon = None + if monitor_id is not None: + mon = next((m for m in monitors if m.get("id") == monitor_id), None) + if mon is None: + mon = monitors[0] + mx = mon.get("x", 0) + my = mon.get("y", 0) + mw = mon.get("width", 0) + mh = mon.get("height", 0) + # reserved: [left, top, right, bottom] + res = mon.get("reserved", [0, 0, 0, 0]) + left, top, right, bottom = res[0], res[1], res[2], res[3] + return ( + mx + left, + my + top, + mw - left - right, + mh - top - bottom, + ) + except Exception: + return None + + __all__ = [ "get_window", + "get_monitor_available_rect", "resize", "resize_and_move", ] diff --git a/booru_viewer/gui/popout/viewport.py b/booru_viewer/gui/popout/viewport.py index 7972e8a..7864b99 100644 --- a/booru_viewer/gui/popout/viewport.py +++ b/booru_viewer/gui/popout/viewport.py @@ -8,19 +8,45 @@ from typing import NamedTuple class Viewport(NamedTuple): """Where and how large the user wants popout content to appear. - Three numbers, no aspect. Aspect is a property of the currently- - displayed post and is recomputed from actual content on every - navigation. The viewport stays put across navigations; the window - rect is a derived projection (Viewport, content_aspect) → (x,y,w,h). + Three numbers + an anchor mode, no aspect. Aspect is a property of + the currently-displayed post and is recomputed from actual content + on every navigation. The viewport stays put across navigations; the + window rect is a derived projection (Viewport, content_aspect) → + (x,y,w,h). `long_side` is the binding edge length: for landscape it becomes width, for portrait it becomes height. Symmetric across the two orientations, which is the property that breaks the width-anchor ratchet that the previous `_fit_to_content` had. + + `anchor` controls which point of the window stays fixed across + navigations as the window size changes with aspect ratio: + ``"center"`` (default) pins the window center; ``"tl"``/``"tr"``/ + ``"bl"``/``"br"`` pin the corresponding corner. The window + grows/shrinks away from the anchored corner. The user can drag the + window anywhere — the anchor only affects resize direction, not + screen position. + + `center_x`/`center_y` hold the anchor point coordinates (center + of the window in center mode, the pinned corner in corner modes). """ center_x: float center_y: float long_side: float + anchor: str = "center" + + +def anchor_point(x: float, y: float, w: float, h: float, anchor: str) -> tuple[float, float]: + """Extract the anchor point from a window rect based on anchor mode.""" + if anchor == "tl": + return (x, y) + if anchor == "tr": + return (x + w, y) + if anchor == "bl": + return (x, y + h) + if anchor == "br": + return (x + w, y + h) + return (x + w / 2, y + h / 2) # Maximum drift between our last-dispatched window rect and the current diff --git a/booru_viewer/gui/popout/window.py b/booru_viewer/gui/popout/window.py index 7676c5a..48d84df 100644 --- a/booru_viewer/gui/popout/window.py +++ b/booru_viewer/gui/popout/window.py @@ -54,7 +54,7 @@ from .state import ( WindowMoved, WindowResized, ) -from .viewport import Viewport, _DRIFT_TOLERANCE +from .viewport import Viewport, _DRIFT_TOLERANCE, anchor_point # Adapter logger — separate from the popout's main `booru` logger so @@ -121,10 +121,11 @@ class FullscreenPreview(QMainWindow): privacy_requested = Signal() closed = Signal() - def __init__(self, grid_cols: int = 3, show_actions: bool = True, monitor: str = "", parent=None) -> None: + def __init__(self, grid_cols: int = 3, show_actions: bool = True, monitor: str = "", anchor: str = "center", parent=None) -> None: super().__init__(parent, Qt.WindowType.Window) self.setWindowTitle("booru-viewer — Popout") self._grid_cols = grid_cols + self._anchor = anchor # Central widget — media fills the entire window central = QWidget() @@ -181,10 +182,6 @@ class FullscreenPreview(QMainWindow): toolbar.setContentsMargins(8, 4, 8, 4) # Same compact-padding override as the embedded preview toolbar — - # bundled themes' default `padding: 5px 12px` is too wide for these - # short labels in narrow fixed slots. - _tb_btn_style = "padding: 2px 6px;" - # Bookmark folders for the popout's Bookmark-as submenu — wired # by app.py via set_bookmark_folders_callback after construction. self._bookmark_folders_callback = None @@ -195,30 +192,29 @@ class FullscreenPreview(QMainWindow): # are independent name spaces and need separate callbacks. self._folders_callback = None - self._bookmark_btn = QPushButton("Bookmark") - self._bookmark_btn.setMaximumWidth(90) - self._bookmark_btn.setStyleSheet(_tb_btn_style) + _tb_sz = 24 + + def _icon_btn(text: str, name: str, tip: str) -> QPushButton: + btn = QPushButton(text) + btn.setObjectName(name) + btn.setFixedSize(_tb_sz, _tb_sz) + btn.setToolTip(tip) + return btn + + self._bookmark_btn = _icon_btn("\u2606", "_tb_bookmark", "Bookmark (B)") self._bookmark_btn.clicked.connect(self._on_bookmark_clicked) toolbar.addWidget(self._bookmark_btn) - self._save_btn = QPushButton("Save") - self._save_btn.setMaximumWidth(70) - self._save_btn.setStyleSheet(_tb_btn_style) + self._save_btn = _icon_btn("\u2193", "_tb_save", "Save to library (S)") self._save_btn.clicked.connect(self._on_save_clicked) toolbar.addWidget(self._save_btn) self._is_saved = False - self._bl_tag_btn = QPushButton("BL Tag") - self._bl_tag_btn.setMaximumWidth(65) - self._bl_tag_btn.setStyleSheet(_tb_btn_style) - self._bl_tag_btn.setToolTip("Blacklist a tag") + self._bl_tag_btn = _icon_btn("\u2298", "_tb_bl_tag", "Blacklist a tag") self._bl_tag_btn.clicked.connect(self._show_bl_tag_menu) toolbar.addWidget(self._bl_tag_btn) - self._bl_post_btn = QPushButton("BL Post") - self._bl_post_btn.setMaximumWidth(70) - self._bl_post_btn.setStyleSheet(_tb_btn_style) - self._bl_post_btn.setToolTip("Blacklist this post") + self._bl_post_btn = _icon_btn("\u2297", "_tb_bl_post", "Blacklist this post") self._bl_post_btn.clicked.connect(self.blacklist_post_requested) toolbar.addWidget(self._bl_post_btn) @@ -642,10 +638,11 @@ class FullscreenPreview(QMainWindow): def update_state(self, bookmarked: bool, saved: bool) -> None: self._is_bookmarked = bookmarked - self._bookmark_btn.setText("Unbookmark" if bookmarked else "Bookmark") - self._bookmark_btn.setMaximumWidth(90 if bookmarked else 80) + self._bookmark_btn.setText("\u2605" if bookmarked else "\u2606") # ★ / ☆ + self._bookmark_btn.setToolTip("Unbookmark (B)" if bookmarked else "Bookmark (B)") self._is_saved = saved - self._save_btn.setText("Unsave" if saved else "Save") + self._save_btn.setText("\u2715" if saved else "\u2193") # ✕ / ⤓ + self._save_btn.setToolTip("Unsave from library" if saved else "Save to library (S)") # ------------------------------------------------------------------ # Public method interface (commit 15) @@ -1054,7 +1051,8 @@ class FullscreenPreview(QMainWindow): @staticmethod def _compute_window_rect( - viewport: Viewport, content_aspect: float, screen + viewport: Viewport, content_aspect: float, screen, + avail_override: tuple[int, int, int, int] | None = None, ) -> tuple[int, int, int, int]: """Project a viewport onto a window rect for the given content aspect. @@ -1064,6 +1062,16 @@ class FullscreenPreview(QMainWindow): if either would exceed its 0.90-of-screen ceiling, preserving aspect exactly. Pure function — no side effects, no widget access, all inputs explicit so it's trivial to reason about. + + ``viewport.center_x``/``center_y`` hold the anchor point — the + window center in ``"center"`` mode, or the pinned corner in + corner modes. The anchor stays fixed; the window grows/shrinks + away from it. + + *avail_override* is an (x, y, w, h) tuple that replaces + ``screen.availableGeometry()`` — used on Hyprland where Qt + doesn't see Waybar's exclusive zone but ``hyprctl monitors -j`` + reports it via the ``reserved`` array. """ if content_aspect >= 1.0: # landscape or square w = viewport.long_side @@ -1072,19 +1080,37 @@ class FullscreenPreview(QMainWindow): h = viewport.long_side w = viewport.long_side * content_aspect - avail = screen.availableGeometry() - cap_w = avail.width() * 0.90 - cap_h = avail.height() * 0.90 + if avail_override: + ax, ay, aw, ah = avail_override + else: + _a = screen.availableGeometry() + ax, ay, aw, ah = _a.x(), _a.y(), _a.width(), _a.height() + cap_w = aw * 0.90 + cap_h = ah * 0.90 scale = min(1.0, cap_w / w, cap_h / h) w *= scale h *= scale - x = viewport.center_x - w / 2 - y = viewport.center_y - h / 2 + anchor = viewport.anchor + if anchor == "tl": + x = viewport.center_x + y = viewport.center_y + elif anchor == "tr": + x = viewport.center_x - w + y = viewport.center_y + elif anchor == "bl": + x = viewport.center_x + y = viewport.center_y - h + elif anchor == "br": + x = viewport.center_x - w + y = viewport.center_y - h + else: + x = viewport.center_x - w / 2 + y = viewport.center_y - h / 2 - # Nudge onto screen if the projected rect would land off-edge. - x = max(avail.x(), min(x, avail.right() - w)) - y = max(avail.y(), min(y, avail.bottom() - h)) + # Nudge onto screen if the window would land off-edge. + x = max(ax, min(x, ax + aw - w)) + y = max(ay, min(y, ay + ah - h)) return (round(x), round(y), round(w), round(h)) @@ -1110,18 +1136,20 @@ class FullscreenPreview(QMainWindow): if win and win.get("at") and win.get("size"): wx, wy = win["at"] ww, wh = win["size"] + ax, ay = anchor_point(wx, wy, ww, wh, self._anchor) return Viewport( - center_x=wx + ww / 2, - center_y=wy + wh / 2, + center_x=ax, center_y=ay, long_side=float(max(ww, wh)), + anchor=self._anchor, ) if floating is None: rect = self.geometry() if rect.width() > 0 and rect.height() > 0: + ax, ay = anchor_point(rect.x(), rect.y(), rect.width(), rect.height(), self._anchor) return Viewport( - center_x=rect.x() + rect.width() / 2, - center_y=rect.y() + rect.height() / 2, + center_x=ax, center_y=ay, long_side=float(max(rect.width(), rect.height())), + anchor=self._anchor, ) return None @@ -1162,10 +1190,11 @@ class FullscreenPreview(QMainWindow): if self._first_fit_pending and self._pending_size and self._pending_position_restore: pw, ph = self._pending_size px, py = self._pending_position_restore + ax, ay = anchor_point(px, py, pw, ph, self._anchor) self._viewport = Viewport( - center_x=px + pw / 2, - center_y=py + ph / 2, + center_x=ax, center_y=ay, long_side=float(max(pw, ph)), + anchor=self._anchor, ) return self._viewport @@ -1192,10 +1221,11 @@ class FullscreenPreview(QMainWindow): ) if drift > _DRIFT_TOLERANCE: # External move/resize detected. Adopt current as intent. + ax, ay = anchor_point(cur_x, cur_y, cur_w, cur_h, self._anchor) self._viewport = Viewport( - center_x=cur_x + cur_w / 2, - center_y=cur_y + cur_h / 2, + center_x=ax, center_y=ay, long_side=float(max(cur_w, cur_h)), + anchor=self._anchor, ) return self._viewport @@ -1260,7 +1290,10 @@ class FullscreenPreview(QMainWindow): # the one-shots would lose the saved position; leaving them # set lets a subsequent fit retry. return - x, y, w, h = self._compute_window_rect(viewport, aspect, screen) + avail_rect = None + if on_hypr and win: + avail_rect = hyprland.get_monitor_available_rect(win.get("monitor")) + x, y, w, h = self._compute_window_rect(viewport, aspect, screen, avail_override=avail_rect) # Identical-rect skip. If the computed rect is exactly what # we last dispatched, the window is already in that state and # there's nothing for hyprctl (or setGeometry) to do. Skipping @@ -1472,19 +1505,21 @@ class FullscreenPreview(QMainWindow): x, y = win["at"] w, h = win["size"] self._windowed_geometry = QRect(x, y, w, h) + ax, ay = anchor_point(x, y, w, h, self._anchor) self._viewport = Viewport( - center_x=x + w / 2, - center_y=y + h / 2, + center_x=ax, center_y=ay, long_side=float(max(w, h)), + anchor=self._anchor, ) else: self._windowed_geometry = self.frameGeometry() rect = self._windowed_geometry if rect.width() > 0 and rect.height() > 0: + ax, ay = anchor_point(rect.x(), rect.y(), rect.width(), rect.height(), self._anchor) self._viewport = Viewport( - center_x=rect.x() + rect.width() / 2, - center_y=rect.y() + rect.height() / 2, + center_x=ax, center_y=ay, long_side=float(max(rect.width(), rect.height())), + anchor=self._anchor, ) self.showFullScreen() @@ -1581,10 +1616,11 @@ class FullscreenPreview(QMainWindow): return rect = self.geometry() if rect.width() > 0 and rect.height() > 0: + ax, ay = anchor_point(rect.x(), rect.y(), rect.width(), rect.height(), self._anchor) self._viewport = Viewport( - center_x=rect.x() + rect.width() / 2, - center_y=rect.y() + rect.height() / 2, + center_x=ax, center_y=ay, long_side=float(max(rect.width(), rect.height())), + anchor=self._anchor, ) # Parallel state machine dispatch for the same event. self._dispatch_and_apply(WindowResized(rect=( @@ -1611,11 +1647,12 @@ class FullscreenPreview(QMainWindow): rect = self.geometry() if rect.width() > 0 and rect.height() > 0: # Move-only update: keep the existing long_side, just - # update the center to where the window now sits. + # update the anchor point to where the window now sits. + ax, ay = anchor_point(rect.x(), rect.y(), rect.width(), rect.height(), self._anchor) self._viewport = Viewport( - center_x=rect.x() + rect.width() / 2, - center_y=rect.y() + rect.height() / 2, + center_x=ax, center_y=ay, long_side=self._viewport.long_side, + anchor=self._anchor, ) # Parallel state machine dispatch for the same event. self._dispatch_and_apply(WindowMoved(rect=( diff --git a/booru_viewer/gui/popout_controller.py b/booru_viewer/gui/popout_controller.py index b9a524f..8ebd2ad 100644 --- a/booru_viewer/gui/popout_controller.py +++ b/booru_viewer/gui/popout_controller.py @@ -90,7 +90,8 @@ class PopoutController: cols = self._app._grid._flow.columns show_actions = self._app._stack.currentIndex() != 2 monitor = self._app._db.get_setting("slideshow_monitor") - self._fullscreen_window = FullscreenPreview(grid_cols=cols, show_actions=show_actions, monitor=monitor, parent=self._app) + anchor = self._app._db.get_setting("popout_anchor") or "center" + self._fullscreen_window = FullscreenPreview(grid_cols=cols, show_actions=show_actions, monitor=monitor, anchor=anchor, parent=self._app) self._fullscreen_window.navigate.connect(self.navigate) self._fullscreen_window.play_next_requested.connect(self._app._on_video_end_next) from ..core.config import library_folders diff --git a/booru_viewer/gui/preview_pane.py b/booru_viewer/gui/preview_pane.py index c29050d..7cf7806 100644 --- a/booru_viewer/gui/preview_pane.py +++ b/booru_viewer/gui/preview_pane.py @@ -64,50 +64,34 @@ class ImagePreview(QWidget): tb.setContentsMargins(4, 1, 4, 1) tb.setSpacing(4) - # Compact toolbar buttons. The bundled themes set - # `QPushButton { padding: 5px 12px }` which eats 24px of horizontal - # space — too much for these short labels in fixed-width slots. - # Override with tighter padding inline so the labels (Unbookmark, - # Unsave, BL Tag, BL Post, Popout) fit cleanly under any theme. - # Same pattern as the search-bar score buttons in app.py and the - # settings dialog spinbox +/- buttons. - _tb_btn_style = "padding: 2px 6px;" + _tb_sz = 24 - self._bookmark_btn = QPushButton("Bookmark") - self._bookmark_btn.setFixedWidth(100) - self._bookmark_btn.setStyleSheet(_tb_btn_style) + def _icon_btn(text: str, name: str, tip: str) -> QPushButton: + btn = QPushButton(text) + btn.setObjectName(name) + btn.setFixedSize(_tb_sz, _tb_sz) + btn.setToolTip(tip) + return btn + + self._bookmark_btn = _icon_btn("\u2606", "_tb_bookmark", "Bookmark (B)") self._bookmark_btn.clicked.connect(self._on_bookmark_clicked) tb.addWidget(self._bookmark_btn) - self._save_btn = QPushButton("Save") - # 75 fits "Unsave" (6 chars) cleanly across every bundled theme. - # The previous 60 was tight enough that some themes clipped the - # last character on library files where the label flips to Unsave. - self._save_btn.setFixedWidth(75) - self._save_btn.setStyleSheet(_tb_btn_style) + self._save_btn = _icon_btn("\u2193", "_tb_save", "Save to library (S)") self._save_btn.clicked.connect(self._on_save_clicked) tb.addWidget(self._save_btn) - self._bl_tag_btn = QPushButton("BL Tag") - self._bl_tag_btn.setFixedWidth(60) - self._bl_tag_btn.setStyleSheet(_tb_btn_style) - self._bl_tag_btn.setToolTip("Blacklist a tag") + self._bl_tag_btn = _icon_btn("\u2298", "_tb_bl_tag", "Blacklist a tag") self._bl_tag_btn.clicked.connect(self._show_bl_tag_menu) tb.addWidget(self._bl_tag_btn) - self._bl_post_btn = QPushButton("BL Post") - self._bl_post_btn.setFixedWidth(65) - self._bl_post_btn.setStyleSheet(_tb_btn_style) - self._bl_post_btn.setToolTip("Blacklist this post") + self._bl_post_btn = _icon_btn("\u2297", "_tb_bl_post", "Blacklist this post") self._bl_post_btn.clicked.connect(self.blacklist_post_requested) tb.addWidget(self._bl_post_btn) tb.addStretch() - self._popout_btn = QPushButton("Popout") - self._popout_btn.setFixedWidth(65) - self._popout_btn.setStyleSheet(_tb_btn_style) - self._popout_btn.setToolTip("Open in popout") + self._popout_btn = _icon_btn("\u29c9", "_tb_popout", "Popout") self._popout_btn.clicked.connect(self.fullscreen_requested) tb.addWidget(self._popout_btn) @@ -239,12 +223,13 @@ class ImagePreview(QWidget): def update_bookmark_state(self, bookmarked: bool) -> None: self._is_bookmarked = bookmarked - self._bookmark_btn.setText("Unbookmark" if bookmarked else "Bookmark") - self._bookmark_btn.setFixedWidth(90 if bookmarked else 80) + self._bookmark_btn.setText("\u2605" if bookmarked else "\u2606") # ★ / ☆ + self._bookmark_btn.setToolTip("Unbookmark (B)" if bookmarked else "Bookmark (B)") def update_save_state(self, saved: bool) -> None: self._is_saved = saved - self._save_btn.setText("Unsave" if saved else "Save") + self._save_btn.setText("\u2715" if saved else "\u2193") # ✕ / ⤓ + self._save_btn.setToolTip("Unsave from library" if saved else "Save to library (S)") diff --git a/booru_viewer/gui/settings.py b/booru_viewer/gui/settings.py index f19fb34..0b36169 100644 --- a/booru_viewer/gui/settings.py +++ b/booru_viewer/gui/settings.py @@ -197,6 +197,11 @@ class SettingsDialog(QDialog): self._search_history.setChecked(self._db.get_setting_bool("search_history_enabled")) form.addRow("", self._search_history) + # Flip layout + self._flip_layout = QCheckBox("Preview on left (restart required)") + self._flip_layout.setChecked(self._db.get_setting_bool("flip_layout")) + form.addRow("", self._flip_layout) + # Slideshow monitor from PySide6.QtWidgets import QApplication self._monitor_combo = QComboBox() @@ -210,6 +215,16 @@ class SettingsDialog(QDialog): self._monitor_combo.setCurrentIndex(idx) form.addRow("Popout monitor:", self._monitor_combo) + # Popout anchor — resize pivot point + self._popout_anchor = QComboBox() + self._popout_anchor.addItems(["Center", "Top-left", "Top-right", "Bottom-left", "Bottom-right"]) + _anchor_map = {"center": "Center", "tl": "Top-left", "tr": "Top-right", "bl": "Bottom-left", "br": "Bottom-right"} + current_anchor = self._db.get_setting("popout_anchor") or "center" + idx = self._popout_anchor.findText(_anchor_map.get(current_anchor, "Center")) + if idx >= 0: + self._popout_anchor.setCurrentIndex(idx) + form.addRow("Popout anchor:", self._popout_anchor) + # File dialog platform (Linux only) self._file_dialog_combo = None if not IS_WINDOWS: @@ -791,7 +806,10 @@ class SettingsDialog(QDialog): self._db.set_setting("infinite_scroll", "1" if self._infinite_scroll.isChecked() else "0") self._db.set_setting("unbookmark_on_save", "1" if self._unbookmark_on_save.isChecked() else "0") self._db.set_setting("search_history_enabled", "1" if self._search_history.isChecked() else "0") + self._db.set_setting("flip_layout", "1" if self._flip_layout.isChecked() else "0") self._db.set_setting("slideshow_monitor", self._monitor_combo.currentText()) + _anchor_rmap = {"Center": "center", "Top-left": "tl", "Top-right": "tr", "Bottom-left": "bl", "Bottom-right": "br"} + self._db.set_setting("popout_anchor", _anchor_rmap.get(self._popout_anchor.currentText(), "center")) self._db.set_setting("library_dir", self._library_dir.text().strip()) self._db.set_setting("library_filename_template", self._library_filename_template.text().strip()) self._db.set_setting("max_cache_mb", str(self._max_cache.value())) diff --git a/themes/README.md b/themes/README.md index 55b276b..0ebb1ca 100644 --- a/themes/README.md +++ b/themes/README.md @@ -118,15 +118,7 @@ QPushButton:pressed { background-color: ${bg_active}; } QPushButton:checked { background-color: ${accent}; } /* Active tab (Browse/Bookmarks/Library), Autoplay, Loop toggles */ ``` -**Note:** Qt's QSS does not support the CSS `content` property, so you cannot replace button text (e.g. "Play" → "") via stylesheet alone. However, you can use a Nerd Font to change how unicode characters render: - -```css -QPushButton { - font-family: "JetBrainsMono Nerd Font", monospace; -} -``` - -To use icon buttons, you would need to modify the Python source code directly — the button labels are set in `preview.py` via `QPushButton("Play")` etc. +**Note:** Qt's QSS does not support the CSS `content` property, so you cannot replace button text (e.g. swap icon symbols) via stylesheet alone. The toolbar icon buttons use hardcoded Unicode symbols — to change which symbols appear, modify the Python source directly (see `preview_pane.py` and `popout/window.py`). ### Text Inputs @@ -312,11 +304,34 @@ QWidget#_slideshow_controls QPushButton { } ``` -### Preview Toolbar +### Preview & Popout Toolbar Icon Buttons -The preview panel has an action toolbar (Bookmark, Save, BL Tag, BL Post, Popout) that appears above the media when a post is active. This toolbar uses the app's default button styling. +The preview and popout toolbars use 24x24 icon buttons with Unicode symbols. Each button has an object name for QSS targeting: -The toolbar does not have a named object ID — it inherits the app's `QPushButton` styles directly. +| Object Name | Symbol | Action | +|-------------|--------|--------| +| `#_tb_bookmark` | ☆ / ★ | Bookmark / Unbookmark | +| `#_tb_save` | ⤓ / ✕ | Save / Unsave | +| `#_tb_bl_tag` | ⊘ | Blacklist a tag | +| `#_tb_bl_post` | ⊗ | Blacklist this post | +| `#_tb_popout` | ⧉ | Open popout (preview only) | + +```css +/* Style all toolbar icon buttons */ +QPushButton#_tb_bookmark, +QPushButton#_tb_save, +QPushButton#_tb_bl_tag, +QPushButton#_tb_bl_post, +QPushButton#_tb_popout { + background: transparent; + border: 1px solid ${border}; + color: ${text}; + padding: 0px; +} + +``` + +The same object names are used in both the preview pane and the popout overlay, so one rule targets both. The symbols themselves are hardcoded in Python — QSS can style the buttons but cannot change which symbol is displayed. ### Progress Bar (Download) diff --git a/themes/catppuccin-mocha-rounded.qss b/themes/catppuccin-mocha-rounded.qss index 07e3ecc..b9a5808 100644 --- a/themes/catppuccin-mocha-rounded.qss +++ b/themes/catppuccin-mocha-rounded.qss @@ -9,26 +9,25 @@ */ /* @palette - bg: #1e1e2e - bg_alt: #181825 - bg_subtle: #313244 - bg_hover: #45475a - bg_active: #585b70 - text: #cdd6f4 - text_dim: #a6adc8 - text_disabled: #6c7086 - border: #313244 - border_strong: #45475a - accent: #cba6f7 - accent_text: #1e1e2e - accent_dim: #b4befe - link: #89b4fa - danger: #f38ba8 - success: #a6e3a1 - warning: #f9e2af - overlay_bg: rgba(30, 30, 46, 200) + bg: #1e1e2e + bg_alt: #181825 + bg_subtle: #313244 + bg_hover: #45475a + bg_active: #585b70 + text: #cdd6f4 + text_dim: #a6adc8 + text_disabled: #6c7086 + border: #313244 + border_strong: #45475a + accent: #cba6f7 + accent_text: #1e1e2e + accent_dim: #b4befe + link: #89b4fa + danger: #f38ba8 + success: #a6e3a1 + warning: #f9e2af + overlay_bg: rgba(30, 30, 46, 200) */ - /* ---------- Base ---------- */ QWidget { @@ -43,8 +42,6 @@ QWidget:disabled { color: ${text_disabled}; } -/* Labels should never paint an opaque background — they sit on top of - * other widgets in many places (toolbars, info panels, overlays). */ QLabel { background: transparent; } @@ -92,49 +89,26 @@ QPushButton:flat:hover { background-color: ${bg_hover}; } -QToolButton { - background-color: transparent; - color: ${text}; - border: 1px solid transparent; - border-radius: 4px; - padding: 4px; -} -QToolButton:hover { - background-color: ${bg_hover}; - border-color: ${border_strong}; -} -QToolButton:pressed, QToolButton:checked { - background-color: ${bg_active}; -} - /* ---------- Inputs ---------- */ -QLineEdit, QSpinBox, QDoubleSpinBox, QTextEdit, QPlainTextEdit { +QLineEdit, QSpinBox, QTextEdit { background-color: ${bg_subtle}; color: ${text}; border: 1px solid ${border_strong}; border-radius: 4px; padding: 2px 6px; - /* min-height ensures the painted text fits inside the widget bounds - * even when a parent layout (e.g. QFormLayout inside a QGroupBox) - * compresses the natural sizeHint. Without this, spinboxes in dense - * forms render with the top of the value text clipped. */ min-height: 16px; selection-background-color: ${accent}; selection-color: ${accent_text}; } QLineEdit:focus, QSpinBox:focus, -QDoubleSpinBox:focus, -QTextEdit:focus, -QPlainTextEdit:focus { +QTextEdit:focus { border-color: ${accent}; } QLineEdit:disabled, QSpinBox:disabled, -QDoubleSpinBox:disabled, -QTextEdit:disabled, -QPlainTextEdit:disabled { +QTextEdit:disabled { background-color: ${bg_alt}; color: ${text_disabled}; border-color: ${border}; @@ -315,19 +289,6 @@ QSlider::handle:horizontal:hover { background: ${accent_dim}; } -QSlider::groove:vertical { - background: ${bg_subtle}; - width: 4px; - border-radius: 2px; -} -QSlider::handle:vertical { - background: ${accent}; - width: 12px; - height: 12px; - margin: 0 -5px; - border-radius: 6px; -} - /* ---------- Progress ---------- */ QProgressBar { @@ -343,33 +304,28 @@ QProgressBar::chunk { border-radius: 3px; } -/* ---------- Checkboxes & radio buttons ---------- */ +/* ---------- Checkboxes ---------- */ -QCheckBox, QRadioButton { +QCheckBox { background: transparent; color: ${text}; spacing: 6px; } -QCheckBox::indicator, QRadioButton::indicator { +QCheckBox::indicator { width: 14px; height: 14px; background-color: ${bg_subtle}; border: 1px solid ${border_strong}; -} -QCheckBox::indicator { border-radius: 3px; } -QRadioButton::indicator { - border-radius: 7px; -} -QCheckBox::indicator:hover, QRadioButton::indicator:hover { +QCheckBox::indicator:hover { border-color: ${accent}; } -QCheckBox::indicator:checked, QRadioButton::indicator:checked { +QCheckBox::indicator:checked { background-color: ${accent}; border-color: ${accent}; } -QCheckBox::indicator:disabled, QRadioButton::indicator:disabled { +QCheckBox::indicator:disabled { background-color: ${bg_alt}; border-color: ${border}; } @@ -384,9 +340,9 @@ QToolTip { border-radius: 3px; } -/* ---------- Item views (lists, trees, tables) ---------- */ +/* ---------- Lists ---------- */ -QListView, QListWidget, QTreeView, QTreeWidget, QTableView, QTableWidget { +QListView, QListWidget { background-color: ${bg}; alternate-background-color: ${bg_alt}; color: ${text}; @@ -395,35 +351,18 @@ QListView, QListWidget, QTreeView, QTreeWidget, QTableView, QTableWidget { selection-color: ${accent_text}; outline: none; } -QListView::item, QListWidget::item, -QTreeView::item, QTreeWidget::item, -QTableView::item, QTableWidget::item { +QListView::item, QListWidget::item { padding: 4px; } -QListView::item:hover, QListWidget::item:hover, -QTreeView::item:hover, QTreeWidget::item:hover, -QTableView::item:hover, QTableWidget::item:hover { +QListView::item:hover, QListWidget::item:hover { background-color: ${bg_hover}; } -QListView::item:selected, QListWidget::item:selected, -QTreeView::item:selected, QTreeWidget::item:selected, -QTableView::item:selected, QTableWidget::item:selected { +QListView::item:selected, QListWidget::item:selected { background-color: ${accent}; color: ${accent_text}; } -QHeaderView::section { - background-color: ${bg_subtle}; - color: ${text}; - border: none; - border-right: 1px solid ${border}; - padding: 4px 8px; -} -QHeaderView::section:hover { - background-color: ${bg_hover}; -} - -/* ---------- Tabs ---------- */ +/* ---------- Tabs (settings dialog) ---------- */ QTabWidget::pane { border: 1px solid ${border}; @@ -448,7 +387,7 @@ QTabBar::tab:hover:!selected { color: ${text}; } -/* ---------- Group boxes ---------- */ +/* ---------- Group boxes (settings dialog) ---------- */ QGroupBox { background: transparent; @@ -465,63 +404,14 @@ QGroupBox::title { color: ${text_dim}; } -/* ---------- Frames ---------- */ - -QFrame[frameShape="4"], /* HLine */ -QFrame[frameShape="5"] /* VLine */ { - background: ${border}; - color: ${border}; -} - -/* ---------- Toolbars ---------- */ - -QToolBar { - background: ${bg}; - border: none; - spacing: 4px; - padding: 2px; -} -QToolBar::separator { - background: ${border}; - width: 1px; - margin: 4px 4px; -} - -/* ---------- Dock widgets ---------- */ - -QDockWidget { - color: ${text}; - titlebar-close-icon: none; -} -QDockWidget::title { - background: ${bg_subtle}; - padding: 4px; - border: 1px solid ${border}; -} - -/* ---------- Rubber band (multi-select drag rectangle) ---------- */ +/* ---------- Rubber band (multi-select drag) ---------- */ QRubberBand { background: ${accent}; border: 1px solid ${accent}; - /* Qt blends rubber band at ~30% so this reads as a translucent - * accent-tinted rectangle without needing rgba(). */ } - - - /* ---------- Library count label states ---------- */ -/* - * The library tab's count label switches between three visual states - * depending on what refresh() found. The state is exposed as a Qt - * dynamic property `libraryCountState` so users can override these - * rules in their custom.qss without touching the Python. - * - * normal N files — default text color, no rule needed - * empty no items — dim text (no items found, search miss) - * error bad/unreachable — danger color + bold (real error) - */ QLabel[libraryCountState="empty"] { color: ${text_dim}; @@ -531,18 +421,18 @@ QLabel[libraryCountState="error"] { font-weight: bold; } -/* ---------- Thumbnail dot indicators (Qt properties on ThumbnailWidget) ---------- */ +/* ---------- Thumbnail indicators ---------- */ ThumbnailWidget { - qproperty-savedColor: #22cc22; /* green dot: saved to library — universal "confirmed" feel */ - qproperty-bookmarkedColor: #ffcc00; /* yellow star: bookmarked */ + qproperty-savedColor: #22cc22; + qproperty-bookmarkedColor: #ffcc00; qproperty-selectionColor: ${accent}; qproperty-multiSelectColor: ${accent_dim}; qproperty-hoverColor: ${accent}; qproperty-idleColor: ${border_strong}; } -/* ---------- Info panel tag category colors ---------- */ +/* ---------- Info panel tag colors ---------- */ InfoPanel { qproperty-tagArtistColor: ${warning}; @@ -553,19 +443,13 @@ InfoPanel { qproperty-tagLoreColor: ${text_dim}; } -/* ---------- Video player letterbox / pillarbox color (mpv background) ---------- */ +/* ---------- Video player letterbox ---------- */ VideoPlayer { qproperty-letterboxColor: ${bg}; } -/* ---------- Popout overlay bars (slideshow toolbar + slideshow controls + embedded preview controls) ---------- */ -/* - * The popout window's translucent toolbar (top) and transport controls - * (bottom) float over the video content. The bg color comes from the - * @palette overlay_bg slot. Children get the classic overlay treatment: - * transparent backgrounds, near-white text, hairline borders. - */ +/* ---------- Popout overlay bars ---------- */ QWidget#_slideshow_toolbar, QWidget#_slideshow_controls, @@ -588,6 +472,8 @@ QWidget#_preview_controls QPushButton { color: white; border: 1px solid rgba(255, 255, 255, 80); padding: 2px 6px; + font-size: 15px; + font-weight: bold; } QWidget#_slideshow_toolbar QPushButton:hover, QWidget#_slideshow_controls QPushButton:hover, diff --git a/themes/catppuccin-mocha-square.qss b/themes/catppuccin-mocha-square.qss index 963063c..c64111b 100644 --- a/themes/catppuccin-mocha-square.qss +++ b/themes/catppuccin-mocha-square.qss @@ -9,26 +9,25 @@ */ /* @palette - bg: #1e1e2e - bg_alt: #181825 - bg_subtle: #313244 - bg_hover: #45475a - bg_active: #585b70 - text: #cdd6f4 - text_dim: #a6adc8 - text_disabled: #6c7086 - border: #313244 - border_strong: #45475a - accent: #cba6f7 - accent_text: #1e1e2e - accent_dim: #b4befe - link: #89b4fa - danger: #f38ba8 - success: #a6e3a1 - warning: #f9e2af - overlay_bg: rgba(30, 30, 46, 200) + bg: #1e1e2e + bg_alt: #181825 + bg_subtle: #313244 + bg_hover: #45475a + bg_active: #585b70 + text: #cdd6f4 + text_dim: #a6adc8 + text_disabled: #6c7086 + border: #313244 + border_strong: #45475a + accent: #cba6f7 + accent_text: #1e1e2e + accent_dim: #b4befe + link: #89b4fa + danger: #f38ba8 + success: #a6e3a1 + warning: #f9e2af + overlay_bg: rgba(30, 30, 46, 200) */ - /* ---------- Base ---------- */ QWidget { @@ -43,8 +42,6 @@ QWidget:disabled { color: ${text_disabled}; } -/* Labels should never paint an opaque background — they sit on top of - * other widgets in many places (toolbars, info panels, overlays). */ QLabel { background: transparent; } @@ -91,47 +88,25 @@ QPushButton:flat:hover { background-color: ${bg_hover}; } -QToolButton { - background-color: transparent; - color: ${text}; - border: 1px solid transparent; - padding: 4px; -} -QToolButton:hover { - background-color: ${bg_hover}; - border-color: ${border_strong}; -} -QToolButton:pressed, QToolButton:checked { - background-color: ${bg_active}; -} - /* ---------- Inputs ---------- */ -QLineEdit, QSpinBox, QDoubleSpinBox, QTextEdit, QPlainTextEdit { +QLineEdit, QSpinBox, QTextEdit { background-color: ${bg_subtle}; color: ${text}; border: 1px solid ${border_strong}; padding: 2px 6px; - /* min-height ensures the painted text fits inside the widget bounds - * even when a parent layout (e.g. QFormLayout inside a QGroupBox) - * compresses the natural sizeHint. Without this, spinboxes in dense - * forms render with the top of the value text clipped. */ min-height: 16px; selection-background-color: ${accent}; selection-color: ${accent_text}; } QLineEdit:focus, QSpinBox:focus, -QDoubleSpinBox:focus, -QTextEdit:focus, -QPlainTextEdit:focus { +QTextEdit:focus { border-color: ${accent}; } QLineEdit:disabled, QSpinBox:disabled, -QDoubleSpinBox:disabled, -QTextEdit:disabled, -QPlainTextEdit:disabled { +QTextEdit:disabled { background-color: ${bg_alt}; color: ${text_disabled}; border-color: ${border}; @@ -309,17 +284,6 @@ QSlider::handle:horizontal:hover { background: ${accent_dim}; } -QSlider::groove:vertical { - background: ${bg_subtle}; - width: 4px; -} -QSlider::handle:vertical { - background: ${accent}; - width: 12px; - height: 12px; - margin: 0 -5px; -} - /* ---------- Progress ---------- */ QProgressBar { @@ -333,32 +297,27 @@ QProgressBar::chunk { background-color: ${accent}; } -/* ---------- Checkboxes & radio buttons ---------- */ +/* ---------- Checkboxes ---------- */ -QCheckBox, QRadioButton { +QCheckBox { background: transparent; color: ${text}; spacing: 6px; } -QCheckBox::indicator, QRadioButton::indicator { +QCheckBox::indicator { width: 14px; height: 14px; background-color: ${bg_subtle}; border: 1px solid ${border_strong}; } -QCheckBox::indicator { -} -QRadioButton::indicator { - border-radius: 7px; -} -QCheckBox::indicator:hover, QRadioButton::indicator:hover { +QCheckBox::indicator:hover { border-color: ${accent}; } -QCheckBox::indicator:checked, QRadioButton::indicator:checked { +QCheckBox::indicator:checked { background-color: ${accent}; border-color: ${accent}; } -QCheckBox::indicator:disabled, QRadioButton::indicator:disabled { +QCheckBox::indicator:disabled { background-color: ${bg_alt}; border-color: ${border}; } @@ -372,9 +331,9 @@ QToolTip { padding: 4px 6px; } -/* ---------- Item views (lists, trees, tables) ---------- */ +/* ---------- Lists ---------- */ -QListView, QListWidget, QTreeView, QTreeWidget, QTableView, QTableWidget { +QListView, QListWidget { background-color: ${bg}; alternate-background-color: ${bg_alt}; color: ${text}; @@ -383,35 +342,18 @@ QListView, QListWidget, QTreeView, QTreeWidget, QTableView, QTableWidget { selection-color: ${accent_text}; outline: none; } -QListView::item, QListWidget::item, -QTreeView::item, QTreeWidget::item, -QTableView::item, QTableWidget::item { +QListView::item, QListWidget::item { padding: 4px; } -QListView::item:hover, QListWidget::item:hover, -QTreeView::item:hover, QTreeWidget::item:hover, -QTableView::item:hover, QTableWidget::item:hover { +QListView::item:hover, QListWidget::item:hover { background-color: ${bg_hover}; } -QListView::item:selected, QListWidget::item:selected, -QTreeView::item:selected, QTreeWidget::item:selected, -QTableView::item:selected, QTableWidget::item:selected { +QListView::item:selected, QListWidget::item:selected { background-color: ${accent}; color: ${accent_text}; } -QHeaderView::section { - background-color: ${bg_subtle}; - color: ${text}; - border: none; - border-right: 1px solid ${border}; - padding: 4px 8px; -} -QHeaderView::section:hover { - background-color: ${bg_hover}; -} - -/* ---------- Tabs ---------- */ +/* ---------- Tabs (settings dialog) ---------- */ QTabWidget::pane { border: 1px solid ${border}; @@ -436,7 +378,7 @@ QTabBar::tab:hover:!selected { color: ${text}; } -/* ---------- Group boxes ---------- */ +/* ---------- Group boxes (settings dialog) ---------- */ QGroupBox { background: transparent; @@ -452,63 +394,14 @@ QGroupBox::title { color: ${text_dim}; } -/* ---------- Frames ---------- */ - -QFrame[frameShape="4"], /* HLine */ -QFrame[frameShape="5"] /* VLine */ { - background: ${border}; - color: ${border}; -} - -/* ---------- Toolbars ---------- */ - -QToolBar { - background: ${bg}; - border: none; - spacing: 4px; - padding: 2px; -} -QToolBar::separator { - background: ${border}; - width: 1px; - margin: 4px 4px; -} - -/* ---------- Dock widgets ---------- */ - -QDockWidget { - color: ${text}; - titlebar-close-icon: none; -} -QDockWidget::title { - background: ${bg_subtle}; - padding: 4px; - border: 1px solid ${border}; -} - -/* ---------- Rubber band (multi-select drag rectangle) ---------- */ +/* ---------- Rubber band (multi-select drag) ---------- */ QRubberBand { background: ${accent}; border: 1px solid ${accent}; - /* Qt blends rubber band at ~30% so this reads as a translucent - * accent-tinted rectangle without needing rgba(). */ } - - - /* ---------- Library count label states ---------- */ -/* - * The library tab's count label switches between three visual states - * depending on what refresh() found. The state is exposed as a Qt - * dynamic property `libraryCountState` so users can override these - * rules in their custom.qss without touching the Python. - * - * normal N files — default text color, no rule needed - * empty no items — dim text (no items found, search miss) - * error bad/unreachable — danger color + bold (real error) - */ QLabel[libraryCountState="empty"] { color: ${text_dim}; @@ -518,18 +411,18 @@ QLabel[libraryCountState="error"] { font-weight: bold; } -/* ---------- Thumbnail dot indicators (Qt properties on ThumbnailWidget) ---------- */ +/* ---------- Thumbnail indicators ---------- */ ThumbnailWidget { - qproperty-savedColor: #22cc22; /* green dot: saved to library — universal "confirmed" feel */ - qproperty-bookmarkedColor: #ffcc00; /* yellow star: bookmarked */ + qproperty-savedColor: #22cc22; + qproperty-bookmarkedColor: #ffcc00; qproperty-selectionColor: ${accent}; qproperty-multiSelectColor: ${accent_dim}; qproperty-hoverColor: ${accent}; qproperty-idleColor: ${border_strong}; } -/* ---------- Info panel tag category colors ---------- */ +/* ---------- Info panel tag colors ---------- */ InfoPanel { qproperty-tagArtistColor: ${warning}; @@ -540,19 +433,13 @@ InfoPanel { qproperty-tagLoreColor: ${text_dim}; } -/* ---------- Video player letterbox / pillarbox color (mpv background) ---------- */ +/* ---------- Video player letterbox ---------- */ VideoPlayer { qproperty-letterboxColor: ${bg}; } -/* ---------- Popout overlay bars (slideshow toolbar + slideshow controls + embedded preview controls) ---------- */ -/* - * The popout window's translucent toolbar (top) and transport controls - * (bottom) float over the video content. The bg color comes from the - * @palette overlay_bg slot. Children get the classic overlay treatment: - * transparent backgrounds, near-white text, hairline borders. - */ +/* ---------- Popout overlay bars ---------- */ QWidget#_slideshow_toolbar, QWidget#_slideshow_controls, @@ -575,6 +462,8 @@ QWidget#_preview_controls QPushButton { color: white; border: 1px solid rgba(255, 255, 255, 80); padding: 2px 6px; + font-size: 15px; + font-weight: bold; } QWidget#_slideshow_toolbar QPushButton:hover, QWidget#_slideshow_controls QPushButton:hover, diff --git a/themes/everforest-rounded.qss b/themes/everforest-rounded.qss index ed678ce..ce4e45c 100644 --- a/themes/everforest-rounded.qss +++ b/themes/everforest-rounded.qss @@ -1,4 +1,4 @@ -/* booru-viewer — Everforest Dark +/* booru-viewer — Everforest * * Edit the @palette block below to recolor this rounded variant. The body uses * ${...} placeholders that the app's _load_user_qss preprocessor @@ -9,26 +9,25 @@ */ /* @palette - bg: #2d353b - bg_alt: #232a2e - bg_subtle: #343f44 - bg_hover: #3d484d - bg_active: #4f585e - text: #d3c6aa - text_dim: #9da9a0 - text_disabled: #7a8478 - border: #343f44 - border_strong: #3d484d - accent: #a7c080 - accent_text: #2d353b - accent_dim: #83c092 - link: #7fbbb3 - danger: #e67e80 - success: #a7c080 - warning: #dbbc7f - overlay_bg: rgba(45, 53, 59, 200) + bg: #2d353b + bg_alt: #232a2e + bg_subtle: #343f44 + bg_hover: #3d484d + bg_active: #4f585e + text: #d3c6aa + text_dim: #9da9a0 + text_disabled: #7a8478 + border: #343f44 + border_strong: #3d484d + accent: #a7c080 + accent_text: #2d353b + accent_dim: #83c092 + link: #7fbbb3 + danger: #e67e80 + success: #a7c080 + warning: #dbbc7f + overlay_bg: rgba(45, 53, 59, 200) */ - /* ---------- Base ---------- */ QWidget { @@ -43,8 +42,6 @@ QWidget:disabled { color: ${text_disabled}; } -/* Labels should never paint an opaque background — they sit on top of - * other widgets in many places (toolbars, info panels, overlays). */ QLabel { background: transparent; } @@ -92,49 +89,26 @@ QPushButton:flat:hover { background-color: ${bg_hover}; } -QToolButton { - background-color: transparent; - color: ${text}; - border: 1px solid transparent; - border-radius: 4px; - padding: 4px; -} -QToolButton:hover { - background-color: ${bg_hover}; - border-color: ${border_strong}; -} -QToolButton:pressed, QToolButton:checked { - background-color: ${bg_active}; -} - /* ---------- Inputs ---------- */ -QLineEdit, QSpinBox, QDoubleSpinBox, QTextEdit, QPlainTextEdit { +QLineEdit, QSpinBox, QTextEdit { background-color: ${bg_subtle}; color: ${text}; border: 1px solid ${border_strong}; border-radius: 4px; padding: 2px 6px; - /* min-height ensures the painted text fits inside the widget bounds - * even when a parent layout (e.g. QFormLayout inside a QGroupBox) - * compresses the natural sizeHint. Without this, spinboxes in dense - * forms render with the top of the value text clipped. */ min-height: 16px; selection-background-color: ${accent}; selection-color: ${accent_text}; } QLineEdit:focus, QSpinBox:focus, -QDoubleSpinBox:focus, -QTextEdit:focus, -QPlainTextEdit:focus { +QTextEdit:focus { border-color: ${accent}; } QLineEdit:disabled, QSpinBox:disabled, -QDoubleSpinBox:disabled, -QTextEdit:disabled, -QPlainTextEdit:disabled { +QTextEdit:disabled { background-color: ${bg_alt}; color: ${text_disabled}; border-color: ${border}; @@ -315,19 +289,6 @@ QSlider::handle:horizontal:hover { background: ${accent_dim}; } -QSlider::groove:vertical { - background: ${bg_subtle}; - width: 4px; - border-radius: 2px; -} -QSlider::handle:vertical { - background: ${accent}; - width: 12px; - height: 12px; - margin: 0 -5px; - border-radius: 6px; -} - /* ---------- Progress ---------- */ QProgressBar { @@ -343,33 +304,28 @@ QProgressBar::chunk { border-radius: 3px; } -/* ---------- Checkboxes & radio buttons ---------- */ +/* ---------- Checkboxes ---------- */ -QCheckBox, QRadioButton { +QCheckBox { background: transparent; color: ${text}; spacing: 6px; } -QCheckBox::indicator, QRadioButton::indicator { +QCheckBox::indicator { width: 14px; height: 14px; background-color: ${bg_subtle}; border: 1px solid ${border_strong}; -} -QCheckBox::indicator { border-radius: 3px; } -QRadioButton::indicator { - border-radius: 7px; -} -QCheckBox::indicator:hover, QRadioButton::indicator:hover { +QCheckBox::indicator:hover { border-color: ${accent}; } -QCheckBox::indicator:checked, QRadioButton::indicator:checked { +QCheckBox::indicator:checked { background-color: ${accent}; border-color: ${accent}; } -QCheckBox::indicator:disabled, QRadioButton::indicator:disabled { +QCheckBox::indicator:disabled { background-color: ${bg_alt}; border-color: ${border}; } @@ -384,9 +340,9 @@ QToolTip { border-radius: 3px; } -/* ---------- Item views (lists, trees, tables) ---------- */ +/* ---------- Lists ---------- */ -QListView, QListWidget, QTreeView, QTreeWidget, QTableView, QTableWidget { +QListView, QListWidget { background-color: ${bg}; alternate-background-color: ${bg_alt}; color: ${text}; @@ -395,35 +351,18 @@ QListView, QListWidget, QTreeView, QTreeWidget, QTableView, QTableWidget { selection-color: ${accent_text}; outline: none; } -QListView::item, QListWidget::item, -QTreeView::item, QTreeWidget::item, -QTableView::item, QTableWidget::item { +QListView::item, QListWidget::item { padding: 4px; } -QListView::item:hover, QListWidget::item:hover, -QTreeView::item:hover, QTreeWidget::item:hover, -QTableView::item:hover, QTableWidget::item:hover { +QListView::item:hover, QListWidget::item:hover { background-color: ${bg_hover}; } -QListView::item:selected, QListWidget::item:selected, -QTreeView::item:selected, QTreeWidget::item:selected, -QTableView::item:selected, QTableWidget::item:selected { +QListView::item:selected, QListWidget::item:selected { background-color: ${accent}; color: ${accent_text}; } -QHeaderView::section { - background-color: ${bg_subtle}; - color: ${text}; - border: none; - border-right: 1px solid ${border}; - padding: 4px 8px; -} -QHeaderView::section:hover { - background-color: ${bg_hover}; -} - -/* ---------- Tabs ---------- */ +/* ---------- Tabs (settings dialog) ---------- */ QTabWidget::pane { border: 1px solid ${border}; @@ -448,7 +387,7 @@ QTabBar::tab:hover:!selected { color: ${text}; } -/* ---------- Group boxes ---------- */ +/* ---------- Group boxes (settings dialog) ---------- */ QGroupBox { background: transparent; @@ -465,63 +404,14 @@ QGroupBox::title { color: ${text_dim}; } -/* ---------- Frames ---------- */ - -QFrame[frameShape="4"], /* HLine */ -QFrame[frameShape="5"] /* VLine */ { - background: ${border}; - color: ${border}; -} - -/* ---------- Toolbars ---------- */ - -QToolBar { - background: ${bg}; - border: none; - spacing: 4px; - padding: 2px; -} -QToolBar::separator { - background: ${border}; - width: 1px; - margin: 4px 4px; -} - -/* ---------- Dock widgets ---------- */ - -QDockWidget { - color: ${text}; - titlebar-close-icon: none; -} -QDockWidget::title { - background: ${bg_subtle}; - padding: 4px; - border: 1px solid ${border}; -} - -/* ---------- Rubber band (multi-select drag rectangle) ---------- */ +/* ---------- Rubber band (multi-select drag) ---------- */ QRubberBand { background: ${accent}; border: 1px solid ${accent}; - /* Qt blends rubber band at ~30% so this reads as a translucent - * accent-tinted rectangle without needing rgba(). */ } - - - /* ---------- Library count label states ---------- */ -/* - * The library tab's count label switches between three visual states - * depending on what refresh() found. The state is exposed as a Qt - * dynamic property `libraryCountState` so users can override these - * rules in their custom.qss without touching the Python. - * - * normal N files — default text color, no rule needed - * empty no items — dim text (no items found, search miss) - * error bad/unreachable — danger color + bold (real error) - */ QLabel[libraryCountState="empty"] { color: ${text_dim}; @@ -531,18 +421,18 @@ QLabel[libraryCountState="error"] { font-weight: bold; } -/* ---------- Thumbnail dot indicators (Qt properties on ThumbnailWidget) ---------- */ +/* ---------- Thumbnail indicators ---------- */ ThumbnailWidget { - qproperty-savedColor: #22cc22; /* green dot: saved to library — universal "confirmed" feel */ - qproperty-bookmarkedColor: #ffcc00; /* yellow star: bookmarked */ + qproperty-savedColor: #22cc22; + qproperty-bookmarkedColor: #ffcc00; qproperty-selectionColor: ${accent}; qproperty-multiSelectColor: ${accent_dim}; qproperty-hoverColor: ${accent}; qproperty-idleColor: ${border_strong}; } -/* ---------- Info panel tag category colors ---------- */ +/* ---------- Info panel tag colors ---------- */ InfoPanel { qproperty-tagArtistColor: ${warning}; @@ -553,19 +443,13 @@ InfoPanel { qproperty-tagLoreColor: ${text_dim}; } -/* ---------- Video player letterbox / pillarbox color (mpv background) ---------- */ +/* ---------- Video player letterbox ---------- */ VideoPlayer { qproperty-letterboxColor: ${bg}; } -/* ---------- Popout overlay bars (slideshow toolbar + slideshow controls + embedded preview controls) ---------- */ -/* - * The popout window's translucent toolbar (top) and transport controls - * (bottom) float over the video content. The bg color comes from the - * @palette overlay_bg slot. Children get the classic overlay treatment: - * transparent backgrounds, near-white text, hairline borders. - */ +/* ---------- Popout overlay bars ---------- */ QWidget#_slideshow_toolbar, QWidget#_slideshow_controls, @@ -588,6 +472,8 @@ QWidget#_preview_controls QPushButton { color: white; border: 1px solid rgba(255, 255, 255, 80); padding: 2px 6px; + font-size: 15px; + font-weight: bold; } QWidget#_slideshow_toolbar QPushButton:hover, QWidget#_slideshow_controls QPushButton:hover, diff --git a/themes/everforest-square.qss b/themes/everforest-square.qss index 1bc2769..4346534 100644 --- a/themes/everforest-square.qss +++ b/themes/everforest-square.qss @@ -1,4 +1,4 @@ -/* booru-viewer — Everforest Dark +/* booru-viewer — Everforest * * Edit the @palette block below to recolor this square variant. The body uses * ${...} placeholders that the app's _load_user_qss preprocessor @@ -9,26 +9,25 @@ */ /* @palette - bg: #2d353b - bg_alt: #232a2e - bg_subtle: #343f44 - bg_hover: #3d484d - bg_active: #4f585e - text: #d3c6aa - text_dim: #9da9a0 - text_disabled: #7a8478 - border: #343f44 - border_strong: #3d484d - accent: #a7c080 - accent_text: #2d353b - accent_dim: #83c092 - link: #7fbbb3 - danger: #e67e80 - success: #a7c080 - warning: #dbbc7f - overlay_bg: rgba(45, 53, 59, 200) + bg: #2d353b + bg_alt: #232a2e + bg_subtle: #343f44 + bg_hover: #3d484d + bg_active: #4f585e + text: #d3c6aa + text_dim: #9da9a0 + text_disabled: #7a8478 + border: #343f44 + border_strong: #3d484d + accent: #a7c080 + accent_text: #2d353b + accent_dim: #83c092 + link: #7fbbb3 + danger: #e67e80 + success: #a7c080 + warning: #dbbc7f + overlay_bg: rgba(45, 53, 59, 200) */ - /* ---------- Base ---------- */ QWidget { @@ -43,8 +42,6 @@ QWidget:disabled { color: ${text_disabled}; } -/* Labels should never paint an opaque background — they sit on top of - * other widgets in many places (toolbars, info panels, overlays). */ QLabel { background: transparent; } @@ -91,47 +88,25 @@ QPushButton:flat:hover { background-color: ${bg_hover}; } -QToolButton { - background-color: transparent; - color: ${text}; - border: 1px solid transparent; - padding: 4px; -} -QToolButton:hover { - background-color: ${bg_hover}; - border-color: ${border_strong}; -} -QToolButton:pressed, QToolButton:checked { - background-color: ${bg_active}; -} - /* ---------- Inputs ---------- */ -QLineEdit, QSpinBox, QDoubleSpinBox, QTextEdit, QPlainTextEdit { +QLineEdit, QSpinBox, QTextEdit { background-color: ${bg_subtle}; color: ${text}; border: 1px solid ${border_strong}; padding: 2px 6px; - /* min-height ensures the painted text fits inside the widget bounds - * even when a parent layout (e.g. QFormLayout inside a QGroupBox) - * compresses the natural sizeHint. Without this, spinboxes in dense - * forms render with the top of the value text clipped. */ min-height: 16px; selection-background-color: ${accent}; selection-color: ${accent_text}; } QLineEdit:focus, QSpinBox:focus, -QDoubleSpinBox:focus, -QTextEdit:focus, -QPlainTextEdit:focus { +QTextEdit:focus { border-color: ${accent}; } QLineEdit:disabled, QSpinBox:disabled, -QDoubleSpinBox:disabled, -QTextEdit:disabled, -QPlainTextEdit:disabled { +QTextEdit:disabled { background-color: ${bg_alt}; color: ${text_disabled}; border-color: ${border}; @@ -309,17 +284,6 @@ QSlider::handle:horizontal:hover { background: ${accent_dim}; } -QSlider::groove:vertical { - background: ${bg_subtle}; - width: 4px; -} -QSlider::handle:vertical { - background: ${accent}; - width: 12px; - height: 12px; - margin: 0 -5px; -} - /* ---------- Progress ---------- */ QProgressBar { @@ -333,32 +297,27 @@ QProgressBar::chunk { background-color: ${accent}; } -/* ---------- Checkboxes & radio buttons ---------- */ +/* ---------- Checkboxes ---------- */ -QCheckBox, QRadioButton { +QCheckBox { background: transparent; color: ${text}; spacing: 6px; } -QCheckBox::indicator, QRadioButton::indicator { +QCheckBox::indicator { width: 14px; height: 14px; background-color: ${bg_subtle}; border: 1px solid ${border_strong}; } -QCheckBox::indicator { -} -QRadioButton::indicator { - border-radius: 7px; -} -QCheckBox::indicator:hover, QRadioButton::indicator:hover { +QCheckBox::indicator:hover { border-color: ${accent}; } -QCheckBox::indicator:checked, QRadioButton::indicator:checked { +QCheckBox::indicator:checked { background-color: ${accent}; border-color: ${accent}; } -QCheckBox::indicator:disabled, QRadioButton::indicator:disabled { +QCheckBox::indicator:disabled { background-color: ${bg_alt}; border-color: ${border}; } @@ -372,9 +331,9 @@ QToolTip { padding: 4px 6px; } -/* ---------- Item views (lists, trees, tables) ---------- */ +/* ---------- Lists ---------- */ -QListView, QListWidget, QTreeView, QTreeWidget, QTableView, QTableWidget { +QListView, QListWidget { background-color: ${bg}; alternate-background-color: ${bg_alt}; color: ${text}; @@ -383,35 +342,18 @@ QListView, QListWidget, QTreeView, QTreeWidget, QTableView, QTableWidget { selection-color: ${accent_text}; outline: none; } -QListView::item, QListWidget::item, -QTreeView::item, QTreeWidget::item, -QTableView::item, QTableWidget::item { +QListView::item, QListWidget::item { padding: 4px; } -QListView::item:hover, QListWidget::item:hover, -QTreeView::item:hover, QTreeWidget::item:hover, -QTableView::item:hover, QTableWidget::item:hover { +QListView::item:hover, QListWidget::item:hover { background-color: ${bg_hover}; } -QListView::item:selected, QListWidget::item:selected, -QTreeView::item:selected, QTreeWidget::item:selected, -QTableView::item:selected, QTableWidget::item:selected { +QListView::item:selected, QListWidget::item:selected { background-color: ${accent}; color: ${accent_text}; } -QHeaderView::section { - background-color: ${bg_subtle}; - color: ${text}; - border: none; - border-right: 1px solid ${border}; - padding: 4px 8px; -} -QHeaderView::section:hover { - background-color: ${bg_hover}; -} - -/* ---------- Tabs ---------- */ +/* ---------- Tabs (settings dialog) ---------- */ QTabWidget::pane { border: 1px solid ${border}; @@ -436,7 +378,7 @@ QTabBar::tab:hover:!selected { color: ${text}; } -/* ---------- Group boxes ---------- */ +/* ---------- Group boxes (settings dialog) ---------- */ QGroupBox { background: transparent; @@ -452,63 +394,14 @@ QGroupBox::title { color: ${text_dim}; } -/* ---------- Frames ---------- */ - -QFrame[frameShape="4"], /* HLine */ -QFrame[frameShape="5"] /* VLine */ { - background: ${border}; - color: ${border}; -} - -/* ---------- Toolbars ---------- */ - -QToolBar { - background: ${bg}; - border: none; - spacing: 4px; - padding: 2px; -} -QToolBar::separator { - background: ${border}; - width: 1px; - margin: 4px 4px; -} - -/* ---------- Dock widgets ---------- */ - -QDockWidget { - color: ${text}; - titlebar-close-icon: none; -} -QDockWidget::title { - background: ${bg_subtle}; - padding: 4px; - border: 1px solid ${border}; -} - -/* ---------- Rubber band (multi-select drag rectangle) ---------- */ +/* ---------- Rubber band (multi-select drag) ---------- */ QRubberBand { background: ${accent}; border: 1px solid ${accent}; - /* Qt blends rubber band at ~30% so this reads as a translucent - * accent-tinted rectangle without needing rgba(). */ } - - - /* ---------- Library count label states ---------- */ -/* - * The library tab's count label switches between three visual states - * depending on what refresh() found. The state is exposed as a Qt - * dynamic property `libraryCountState` so users can override these - * rules in their custom.qss without touching the Python. - * - * normal N files — default text color, no rule needed - * empty no items — dim text (no items found, search miss) - * error bad/unreachable — danger color + bold (real error) - */ QLabel[libraryCountState="empty"] { color: ${text_dim}; @@ -518,18 +411,18 @@ QLabel[libraryCountState="error"] { font-weight: bold; } -/* ---------- Thumbnail dot indicators (Qt properties on ThumbnailWidget) ---------- */ +/* ---------- Thumbnail indicators ---------- */ ThumbnailWidget { - qproperty-savedColor: #22cc22; /* green dot: saved to library — universal "confirmed" feel */ - qproperty-bookmarkedColor: #ffcc00; /* yellow star: bookmarked */ + qproperty-savedColor: #22cc22; + qproperty-bookmarkedColor: #ffcc00; qproperty-selectionColor: ${accent}; qproperty-multiSelectColor: ${accent_dim}; qproperty-hoverColor: ${accent}; qproperty-idleColor: ${border_strong}; } -/* ---------- Info panel tag category colors ---------- */ +/* ---------- Info panel tag colors ---------- */ InfoPanel { qproperty-tagArtistColor: ${warning}; @@ -540,19 +433,13 @@ InfoPanel { qproperty-tagLoreColor: ${text_dim}; } -/* ---------- Video player letterbox / pillarbox color (mpv background) ---------- */ +/* ---------- Video player letterbox ---------- */ VideoPlayer { qproperty-letterboxColor: ${bg}; } -/* ---------- Popout overlay bars (slideshow toolbar + slideshow controls + embedded preview controls) ---------- */ -/* - * The popout window's translucent toolbar (top) and transport controls - * (bottom) float over the video content. The bg color comes from the - * @palette overlay_bg slot. Children get the classic overlay treatment: - * transparent backgrounds, near-white text, hairline borders. - */ +/* ---------- Popout overlay bars ---------- */ QWidget#_slideshow_toolbar, QWidget#_slideshow_controls, @@ -575,6 +462,8 @@ QWidget#_preview_controls QPushButton { color: white; border: 1px solid rgba(255, 255, 255, 80); padding: 2px 6px; + font-size: 15px; + font-weight: bold; } QWidget#_slideshow_toolbar QPushButton:hover, QWidget#_slideshow_controls QPushButton:hover, diff --git a/themes/gruvbox-rounded.qss b/themes/gruvbox-rounded.qss index add390a..fb125d8 100644 --- a/themes/gruvbox-rounded.qss +++ b/themes/gruvbox-rounded.qss @@ -1,4 +1,4 @@ -/* booru-viewer — Gruvbox Dark +/* booru-viewer — Gruvbox * * Edit the @palette block below to recolor this rounded variant. The body uses * ${...} placeholders that the app's _load_user_qss preprocessor @@ -9,26 +9,25 @@ */ /* @palette - bg: #282828 - bg_alt: #1d2021 - bg_subtle: #3c3836 - bg_hover: #504945 - bg_active: #665c54 - text: #ebdbb2 - text_dim: #d5c4a1 - text_disabled: #928374 - border: #3c3836 - border_strong: #504945 - accent: #d79921 - accent_text: #282828 - accent_dim: #fabd2f - link: #83a598 - danger: #fb4934 - success: #b8bb26 - warning: #fabd2f - overlay_bg: rgba(40, 40, 40, 200) + bg: #282828 + bg_alt: #1d2021 + bg_subtle: #3c3836 + bg_hover: #504945 + bg_active: #665c54 + text: #ebdbb2 + text_dim: #d5c4a1 + text_disabled: #928374 + border: #3c3836 + border_strong: #504945 + accent: #d79921 + accent_text: #282828 + accent_dim: #fabd2f + link: #83a598 + danger: #fb4934 + success: #b8bb26 + warning: #fabd2f + overlay_bg: rgba(40, 40, 40, 200) */ - /* ---------- Base ---------- */ QWidget { @@ -43,8 +42,6 @@ QWidget:disabled { color: ${text_disabled}; } -/* Labels should never paint an opaque background — they sit on top of - * other widgets in many places (toolbars, info panels, overlays). */ QLabel { background: transparent; } @@ -92,49 +89,26 @@ QPushButton:flat:hover { background-color: ${bg_hover}; } -QToolButton { - background-color: transparent; - color: ${text}; - border: 1px solid transparent; - border-radius: 4px; - padding: 4px; -} -QToolButton:hover { - background-color: ${bg_hover}; - border-color: ${border_strong}; -} -QToolButton:pressed, QToolButton:checked { - background-color: ${bg_active}; -} - /* ---------- Inputs ---------- */ -QLineEdit, QSpinBox, QDoubleSpinBox, QTextEdit, QPlainTextEdit { +QLineEdit, QSpinBox, QTextEdit { background-color: ${bg_subtle}; color: ${text}; border: 1px solid ${border_strong}; border-radius: 4px; padding: 2px 6px; - /* min-height ensures the painted text fits inside the widget bounds - * even when a parent layout (e.g. QFormLayout inside a QGroupBox) - * compresses the natural sizeHint. Without this, spinboxes in dense - * forms render with the top of the value text clipped. */ min-height: 16px; selection-background-color: ${accent}; selection-color: ${accent_text}; } QLineEdit:focus, QSpinBox:focus, -QDoubleSpinBox:focus, -QTextEdit:focus, -QPlainTextEdit:focus { +QTextEdit:focus { border-color: ${accent}; } QLineEdit:disabled, QSpinBox:disabled, -QDoubleSpinBox:disabled, -QTextEdit:disabled, -QPlainTextEdit:disabled { +QTextEdit:disabled { background-color: ${bg_alt}; color: ${text_disabled}; border-color: ${border}; @@ -315,19 +289,6 @@ QSlider::handle:horizontal:hover { background: ${accent_dim}; } -QSlider::groove:vertical { - background: ${bg_subtle}; - width: 4px; - border-radius: 2px; -} -QSlider::handle:vertical { - background: ${accent}; - width: 12px; - height: 12px; - margin: 0 -5px; - border-radius: 6px; -} - /* ---------- Progress ---------- */ QProgressBar { @@ -343,33 +304,28 @@ QProgressBar::chunk { border-radius: 3px; } -/* ---------- Checkboxes & radio buttons ---------- */ +/* ---------- Checkboxes ---------- */ -QCheckBox, QRadioButton { +QCheckBox { background: transparent; color: ${text}; spacing: 6px; } -QCheckBox::indicator, QRadioButton::indicator { +QCheckBox::indicator { width: 14px; height: 14px; background-color: ${bg_subtle}; border: 1px solid ${border_strong}; -} -QCheckBox::indicator { border-radius: 3px; } -QRadioButton::indicator { - border-radius: 7px; -} -QCheckBox::indicator:hover, QRadioButton::indicator:hover { +QCheckBox::indicator:hover { border-color: ${accent}; } -QCheckBox::indicator:checked, QRadioButton::indicator:checked { +QCheckBox::indicator:checked { background-color: ${accent}; border-color: ${accent}; } -QCheckBox::indicator:disabled, QRadioButton::indicator:disabled { +QCheckBox::indicator:disabled { background-color: ${bg_alt}; border-color: ${border}; } @@ -384,9 +340,9 @@ QToolTip { border-radius: 3px; } -/* ---------- Item views (lists, trees, tables) ---------- */ +/* ---------- Lists ---------- */ -QListView, QListWidget, QTreeView, QTreeWidget, QTableView, QTableWidget { +QListView, QListWidget { background-color: ${bg}; alternate-background-color: ${bg_alt}; color: ${text}; @@ -395,35 +351,18 @@ QListView, QListWidget, QTreeView, QTreeWidget, QTableView, QTableWidget { selection-color: ${accent_text}; outline: none; } -QListView::item, QListWidget::item, -QTreeView::item, QTreeWidget::item, -QTableView::item, QTableWidget::item { +QListView::item, QListWidget::item { padding: 4px; } -QListView::item:hover, QListWidget::item:hover, -QTreeView::item:hover, QTreeWidget::item:hover, -QTableView::item:hover, QTableWidget::item:hover { +QListView::item:hover, QListWidget::item:hover { background-color: ${bg_hover}; } -QListView::item:selected, QListWidget::item:selected, -QTreeView::item:selected, QTreeWidget::item:selected, -QTableView::item:selected, QTableWidget::item:selected { +QListView::item:selected, QListWidget::item:selected { background-color: ${accent}; color: ${accent_text}; } -QHeaderView::section { - background-color: ${bg_subtle}; - color: ${text}; - border: none; - border-right: 1px solid ${border}; - padding: 4px 8px; -} -QHeaderView::section:hover { - background-color: ${bg_hover}; -} - -/* ---------- Tabs ---------- */ +/* ---------- Tabs (settings dialog) ---------- */ QTabWidget::pane { border: 1px solid ${border}; @@ -448,7 +387,7 @@ QTabBar::tab:hover:!selected { color: ${text}; } -/* ---------- Group boxes ---------- */ +/* ---------- Group boxes (settings dialog) ---------- */ QGroupBox { background: transparent; @@ -465,63 +404,14 @@ QGroupBox::title { color: ${text_dim}; } -/* ---------- Frames ---------- */ - -QFrame[frameShape="4"], /* HLine */ -QFrame[frameShape="5"] /* VLine */ { - background: ${border}; - color: ${border}; -} - -/* ---------- Toolbars ---------- */ - -QToolBar { - background: ${bg}; - border: none; - spacing: 4px; - padding: 2px; -} -QToolBar::separator { - background: ${border}; - width: 1px; - margin: 4px 4px; -} - -/* ---------- Dock widgets ---------- */ - -QDockWidget { - color: ${text}; - titlebar-close-icon: none; -} -QDockWidget::title { - background: ${bg_subtle}; - padding: 4px; - border: 1px solid ${border}; -} - -/* ---------- Rubber band (multi-select drag rectangle) ---------- */ +/* ---------- Rubber band (multi-select drag) ---------- */ QRubberBand { background: ${accent}; border: 1px solid ${accent}; - /* Qt blends rubber band at ~30% so this reads as a translucent - * accent-tinted rectangle without needing rgba(). */ } - - - /* ---------- Library count label states ---------- */ -/* - * The library tab's count label switches between three visual states - * depending on what refresh() found. The state is exposed as a Qt - * dynamic property `libraryCountState` so users can override these - * rules in their custom.qss without touching the Python. - * - * normal N files — default text color, no rule needed - * empty no items — dim text (no items found, search miss) - * error bad/unreachable — danger color + bold (real error) - */ QLabel[libraryCountState="empty"] { color: ${text_dim}; @@ -531,18 +421,18 @@ QLabel[libraryCountState="error"] { font-weight: bold; } -/* ---------- Thumbnail dot indicators (Qt properties on ThumbnailWidget) ---------- */ +/* ---------- Thumbnail indicators ---------- */ ThumbnailWidget { - qproperty-savedColor: #22cc22; /* green dot: saved to library — universal "confirmed" feel */ - qproperty-bookmarkedColor: #ffcc00; /* yellow star: bookmarked */ + qproperty-savedColor: #22cc22; + qproperty-bookmarkedColor: #ffcc00; qproperty-selectionColor: ${accent}; qproperty-multiSelectColor: ${accent_dim}; qproperty-hoverColor: ${accent}; qproperty-idleColor: ${border_strong}; } -/* ---------- Info panel tag category colors ---------- */ +/* ---------- Info panel tag colors ---------- */ InfoPanel { qproperty-tagArtistColor: ${warning}; @@ -553,19 +443,13 @@ InfoPanel { qproperty-tagLoreColor: ${text_dim}; } -/* ---------- Video player letterbox / pillarbox color (mpv background) ---------- */ +/* ---------- Video player letterbox ---------- */ VideoPlayer { qproperty-letterboxColor: ${bg}; } -/* ---------- Popout overlay bars (slideshow toolbar + slideshow controls + embedded preview controls) ---------- */ -/* - * The popout window's translucent toolbar (top) and transport controls - * (bottom) float over the video content. The bg color comes from the - * @palette overlay_bg slot. Children get the classic overlay treatment: - * transparent backgrounds, near-white text, hairline borders. - */ +/* ---------- Popout overlay bars ---------- */ QWidget#_slideshow_toolbar, QWidget#_slideshow_controls, @@ -588,6 +472,8 @@ QWidget#_preview_controls QPushButton { color: white; border: 1px solid rgba(255, 255, 255, 80); padding: 2px 6px; + font-size: 15px; + font-weight: bold; } QWidget#_slideshow_toolbar QPushButton:hover, QWidget#_slideshow_controls QPushButton:hover, diff --git a/themes/gruvbox-square.qss b/themes/gruvbox-square.qss index 3653d4a..d06aa72 100644 --- a/themes/gruvbox-square.qss +++ b/themes/gruvbox-square.qss @@ -1,4 +1,4 @@ -/* booru-viewer — Gruvbox Dark +/* booru-viewer — Gruvbox * * Edit the @palette block below to recolor this square variant. The body uses * ${...} placeholders that the app's _load_user_qss preprocessor @@ -9,26 +9,25 @@ */ /* @palette - bg: #282828 - bg_alt: #1d2021 - bg_subtle: #3c3836 - bg_hover: #504945 - bg_active: #665c54 - text: #ebdbb2 - text_dim: #d5c4a1 - text_disabled: #928374 - border: #3c3836 - border_strong: #504945 - accent: #d79921 - accent_text: #282828 - accent_dim: #fabd2f - link: #83a598 - danger: #fb4934 - success: #b8bb26 - warning: #fabd2f - overlay_bg: rgba(40, 40, 40, 200) + bg: #282828 + bg_alt: #1d2021 + bg_subtle: #3c3836 + bg_hover: #504945 + bg_active: #665c54 + text: #ebdbb2 + text_dim: #d5c4a1 + text_disabled: #928374 + border: #3c3836 + border_strong: #504945 + accent: #d79921 + accent_text: #282828 + accent_dim: #fabd2f + link: #83a598 + danger: #fb4934 + success: #b8bb26 + warning: #fabd2f + overlay_bg: rgba(40, 40, 40, 200) */ - /* ---------- Base ---------- */ QWidget { @@ -43,8 +42,6 @@ QWidget:disabled { color: ${text_disabled}; } -/* Labels should never paint an opaque background — they sit on top of - * other widgets in many places (toolbars, info panels, overlays). */ QLabel { background: transparent; } @@ -91,47 +88,25 @@ QPushButton:flat:hover { background-color: ${bg_hover}; } -QToolButton { - background-color: transparent; - color: ${text}; - border: 1px solid transparent; - padding: 4px; -} -QToolButton:hover { - background-color: ${bg_hover}; - border-color: ${border_strong}; -} -QToolButton:pressed, QToolButton:checked { - background-color: ${bg_active}; -} - /* ---------- Inputs ---------- */ -QLineEdit, QSpinBox, QDoubleSpinBox, QTextEdit, QPlainTextEdit { +QLineEdit, QSpinBox, QTextEdit { background-color: ${bg_subtle}; color: ${text}; border: 1px solid ${border_strong}; padding: 2px 6px; - /* min-height ensures the painted text fits inside the widget bounds - * even when a parent layout (e.g. QFormLayout inside a QGroupBox) - * compresses the natural sizeHint. Without this, spinboxes in dense - * forms render with the top of the value text clipped. */ min-height: 16px; selection-background-color: ${accent}; selection-color: ${accent_text}; } QLineEdit:focus, QSpinBox:focus, -QDoubleSpinBox:focus, -QTextEdit:focus, -QPlainTextEdit:focus { +QTextEdit:focus { border-color: ${accent}; } QLineEdit:disabled, QSpinBox:disabled, -QDoubleSpinBox:disabled, -QTextEdit:disabled, -QPlainTextEdit:disabled { +QTextEdit:disabled { background-color: ${bg_alt}; color: ${text_disabled}; border-color: ${border}; @@ -309,17 +284,6 @@ QSlider::handle:horizontal:hover { background: ${accent_dim}; } -QSlider::groove:vertical { - background: ${bg_subtle}; - width: 4px; -} -QSlider::handle:vertical { - background: ${accent}; - width: 12px; - height: 12px; - margin: 0 -5px; -} - /* ---------- Progress ---------- */ QProgressBar { @@ -333,32 +297,27 @@ QProgressBar::chunk { background-color: ${accent}; } -/* ---------- Checkboxes & radio buttons ---------- */ +/* ---------- Checkboxes ---------- */ -QCheckBox, QRadioButton { +QCheckBox { background: transparent; color: ${text}; spacing: 6px; } -QCheckBox::indicator, QRadioButton::indicator { +QCheckBox::indicator { width: 14px; height: 14px; background-color: ${bg_subtle}; border: 1px solid ${border_strong}; } -QCheckBox::indicator { -} -QRadioButton::indicator { - border-radius: 7px; -} -QCheckBox::indicator:hover, QRadioButton::indicator:hover { +QCheckBox::indicator:hover { border-color: ${accent}; } -QCheckBox::indicator:checked, QRadioButton::indicator:checked { +QCheckBox::indicator:checked { background-color: ${accent}; border-color: ${accent}; } -QCheckBox::indicator:disabled, QRadioButton::indicator:disabled { +QCheckBox::indicator:disabled { background-color: ${bg_alt}; border-color: ${border}; } @@ -372,9 +331,9 @@ QToolTip { padding: 4px 6px; } -/* ---------- Item views (lists, trees, tables) ---------- */ +/* ---------- Lists ---------- */ -QListView, QListWidget, QTreeView, QTreeWidget, QTableView, QTableWidget { +QListView, QListWidget { background-color: ${bg}; alternate-background-color: ${bg_alt}; color: ${text}; @@ -383,35 +342,18 @@ QListView, QListWidget, QTreeView, QTreeWidget, QTableView, QTableWidget { selection-color: ${accent_text}; outline: none; } -QListView::item, QListWidget::item, -QTreeView::item, QTreeWidget::item, -QTableView::item, QTableWidget::item { +QListView::item, QListWidget::item { padding: 4px; } -QListView::item:hover, QListWidget::item:hover, -QTreeView::item:hover, QTreeWidget::item:hover, -QTableView::item:hover, QTableWidget::item:hover { +QListView::item:hover, QListWidget::item:hover { background-color: ${bg_hover}; } -QListView::item:selected, QListWidget::item:selected, -QTreeView::item:selected, QTreeWidget::item:selected, -QTableView::item:selected, QTableWidget::item:selected { +QListView::item:selected, QListWidget::item:selected { background-color: ${accent}; color: ${accent_text}; } -QHeaderView::section { - background-color: ${bg_subtle}; - color: ${text}; - border: none; - border-right: 1px solid ${border}; - padding: 4px 8px; -} -QHeaderView::section:hover { - background-color: ${bg_hover}; -} - -/* ---------- Tabs ---------- */ +/* ---------- Tabs (settings dialog) ---------- */ QTabWidget::pane { border: 1px solid ${border}; @@ -436,7 +378,7 @@ QTabBar::tab:hover:!selected { color: ${text}; } -/* ---------- Group boxes ---------- */ +/* ---------- Group boxes (settings dialog) ---------- */ QGroupBox { background: transparent; @@ -452,63 +394,14 @@ QGroupBox::title { color: ${text_dim}; } -/* ---------- Frames ---------- */ - -QFrame[frameShape="4"], /* HLine */ -QFrame[frameShape="5"] /* VLine */ { - background: ${border}; - color: ${border}; -} - -/* ---------- Toolbars ---------- */ - -QToolBar { - background: ${bg}; - border: none; - spacing: 4px; - padding: 2px; -} -QToolBar::separator { - background: ${border}; - width: 1px; - margin: 4px 4px; -} - -/* ---------- Dock widgets ---------- */ - -QDockWidget { - color: ${text}; - titlebar-close-icon: none; -} -QDockWidget::title { - background: ${bg_subtle}; - padding: 4px; - border: 1px solid ${border}; -} - -/* ---------- Rubber band (multi-select drag rectangle) ---------- */ +/* ---------- Rubber band (multi-select drag) ---------- */ QRubberBand { background: ${accent}; border: 1px solid ${accent}; - /* Qt blends rubber band at ~30% so this reads as a translucent - * accent-tinted rectangle without needing rgba(). */ } - - - /* ---------- Library count label states ---------- */ -/* - * The library tab's count label switches between three visual states - * depending on what refresh() found. The state is exposed as a Qt - * dynamic property `libraryCountState` so users can override these - * rules in their custom.qss without touching the Python. - * - * normal N files — default text color, no rule needed - * empty no items — dim text (no items found, search miss) - * error bad/unreachable — danger color + bold (real error) - */ QLabel[libraryCountState="empty"] { color: ${text_dim}; @@ -518,18 +411,18 @@ QLabel[libraryCountState="error"] { font-weight: bold; } -/* ---------- Thumbnail dot indicators (Qt properties on ThumbnailWidget) ---------- */ +/* ---------- Thumbnail indicators ---------- */ ThumbnailWidget { - qproperty-savedColor: #22cc22; /* green dot: saved to library — universal "confirmed" feel */ - qproperty-bookmarkedColor: #ffcc00; /* yellow star: bookmarked */ + qproperty-savedColor: #22cc22; + qproperty-bookmarkedColor: #ffcc00; qproperty-selectionColor: ${accent}; qproperty-multiSelectColor: ${accent_dim}; qproperty-hoverColor: ${accent}; qproperty-idleColor: ${border_strong}; } -/* ---------- Info panel tag category colors ---------- */ +/* ---------- Info panel tag colors ---------- */ InfoPanel { qproperty-tagArtistColor: ${warning}; @@ -540,19 +433,13 @@ InfoPanel { qproperty-tagLoreColor: ${text_dim}; } -/* ---------- Video player letterbox / pillarbox color (mpv background) ---------- */ +/* ---------- Video player letterbox ---------- */ VideoPlayer { qproperty-letterboxColor: ${bg}; } -/* ---------- Popout overlay bars (slideshow toolbar + slideshow controls + embedded preview controls) ---------- */ -/* - * The popout window's translucent toolbar (top) and transport controls - * (bottom) float over the video content. The bg color comes from the - * @palette overlay_bg slot. Children get the classic overlay treatment: - * transparent backgrounds, near-white text, hairline borders. - */ +/* ---------- Popout overlay bars ---------- */ QWidget#_slideshow_toolbar, QWidget#_slideshow_controls, @@ -575,6 +462,8 @@ QWidget#_preview_controls QPushButton { color: white; border: 1px solid rgba(255, 255, 255, 80); padding: 2px 6px; + font-size: 15px; + font-weight: bold; } QWidget#_slideshow_toolbar QPushButton:hover, QWidget#_slideshow_controls QPushButton:hover, diff --git a/themes/nord-rounded.qss b/themes/nord-rounded.qss index 96f6d58..ea3ffb1 100644 --- a/themes/nord-rounded.qss +++ b/themes/nord-rounded.qss @@ -9,26 +9,25 @@ */ /* @palette - bg: #2e3440 - bg_alt: #272b36 - bg_subtle: #3b4252 - bg_hover: #434c5e - bg_active: #4c566a - text: #eceff4 - text_dim: #d8dee9 - text_disabled: #4c566a - border: #3b4252 - border_strong: #4c566a - accent: #88c0d0 - accent_text: #2e3440 - accent_dim: #81a1c1 - link: #8fbcbb - danger: #bf616a - success: #a3be8c - warning: #ebcb8b - overlay_bg: rgba(46, 52, 64, 200) + bg: #2e3440 + bg_alt: #272b36 + bg_subtle: #3b4252 + bg_hover: #434c5e + bg_active: #4c566a + text: #eceff4 + text_dim: #d8dee9 + text_disabled: #4c566a + border: #3b4252 + border_strong: #4c566a + accent: #88c0d0 + accent_text: #2e3440 + accent_dim: #81a1c1 + link: #8fbcbb + danger: #bf616a + success: #a3be8c + warning: #ebcb8b + overlay_bg: rgba(46, 52, 64, 200) */ - /* ---------- Base ---------- */ QWidget { @@ -43,8 +42,6 @@ QWidget:disabled { color: ${text_disabled}; } -/* Labels should never paint an opaque background — they sit on top of - * other widgets in many places (toolbars, info panels, overlays). */ QLabel { background: transparent; } @@ -92,49 +89,26 @@ QPushButton:flat:hover { background-color: ${bg_hover}; } -QToolButton { - background-color: transparent; - color: ${text}; - border: 1px solid transparent; - border-radius: 4px; - padding: 4px; -} -QToolButton:hover { - background-color: ${bg_hover}; - border-color: ${border_strong}; -} -QToolButton:pressed, QToolButton:checked { - background-color: ${bg_active}; -} - /* ---------- Inputs ---------- */ -QLineEdit, QSpinBox, QDoubleSpinBox, QTextEdit, QPlainTextEdit { +QLineEdit, QSpinBox, QTextEdit { background-color: ${bg_subtle}; color: ${text}; border: 1px solid ${border_strong}; border-radius: 4px; padding: 2px 6px; - /* min-height ensures the painted text fits inside the widget bounds - * even when a parent layout (e.g. QFormLayout inside a QGroupBox) - * compresses the natural sizeHint. Without this, spinboxes in dense - * forms render with the top of the value text clipped. */ min-height: 16px; selection-background-color: ${accent}; selection-color: ${accent_text}; } QLineEdit:focus, QSpinBox:focus, -QDoubleSpinBox:focus, -QTextEdit:focus, -QPlainTextEdit:focus { +QTextEdit:focus { border-color: ${accent}; } QLineEdit:disabled, QSpinBox:disabled, -QDoubleSpinBox:disabled, -QTextEdit:disabled, -QPlainTextEdit:disabled { +QTextEdit:disabled { background-color: ${bg_alt}; color: ${text_disabled}; border-color: ${border}; @@ -315,19 +289,6 @@ QSlider::handle:horizontal:hover { background: ${accent_dim}; } -QSlider::groove:vertical { - background: ${bg_subtle}; - width: 4px; - border-radius: 2px; -} -QSlider::handle:vertical { - background: ${accent}; - width: 12px; - height: 12px; - margin: 0 -5px; - border-radius: 6px; -} - /* ---------- Progress ---------- */ QProgressBar { @@ -343,33 +304,28 @@ QProgressBar::chunk { border-radius: 3px; } -/* ---------- Checkboxes & radio buttons ---------- */ +/* ---------- Checkboxes ---------- */ -QCheckBox, QRadioButton { +QCheckBox { background: transparent; color: ${text}; spacing: 6px; } -QCheckBox::indicator, QRadioButton::indicator { +QCheckBox::indicator { width: 14px; height: 14px; background-color: ${bg_subtle}; border: 1px solid ${border_strong}; -} -QCheckBox::indicator { border-radius: 3px; } -QRadioButton::indicator { - border-radius: 7px; -} -QCheckBox::indicator:hover, QRadioButton::indicator:hover { +QCheckBox::indicator:hover { border-color: ${accent}; } -QCheckBox::indicator:checked, QRadioButton::indicator:checked { +QCheckBox::indicator:checked { background-color: ${accent}; border-color: ${accent}; } -QCheckBox::indicator:disabled, QRadioButton::indicator:disabled { +QCheckBox::indicator:disabled { background-color: ${bg_alt}; border-color: ${border}; } @@ -384,9 +340,9 @@ QToolTip { border-radius: 3px; } -/* ---------- Item views (lists, trees, tables) ---------- */ +/* ---------- Lists ---------- */ -QListView, QListWidget, QTreeView, QTreeWidget, QTableView, QTableWidget { +QListView, QListWidget { background-color: ${bg}; alternate-background-color: ${bg_alt}; color: ${text}; @@ -395,35 +351,18 @@ QListView, QListWidget, QTreeView, QTreeWidget, QTableView, QTableWidget { selection-color: ${accent_text}; outline: none; } -QListView::item, QListWidget::item, -QTreeView::item, QTreeWidget::item, -QTableView::item, QTableWidget::item { +QListView::item, QListWidget::item { padding: 4px; } -QListView::item:hover, QListWidget::item:hover, -QTreeView::item:hover, QTreeWidget::item:hover, -QTableView::item:hover, QTableWidget::item:hover { +QListView::item:hover, QListWidget::item:hover { background-color: ${bg_hover}; } -QListView::item:selected, QListWidget::item:selected, -QTreeView::item:selected, QTreeWidget::item:selected, -QTableView::item:selected, QTableWidget::item:selected { +QListView::item:selected, QListWidget::item:selected { background-color: ${accent}; color: ${accent_text}; } -QHeaderView::section { - background-color: ${bg_subtle}; - color: ${text}; - border: none; - border-right: 1px solid ${border}; - padding: 4px 8px; -} -QHeaderView::section:hover { - background-color: ${bg_hover}; -} - -/* ---------- Tabs ---------- */ +/* ---------- Tabs (settings dialog) ---------- */ QTabWidget::pane { border: 1px solid ${border}; @@ -448,7 +387,7 @@ QTabBar::tab:hover:!selected { color: ${text}; } -/* ---------- Group boxes ---------- */ +/* ---------- Group boxes (settings dialog) ---------- */ QGroupBox { background: transparent; @@ -465,63 +404,14 @@ QGroupBox::title { color: ${text_dim}; } -/* ---------- Frames ---------- */ - -QFrame[frameShape="4"], /* HLine */ -QFrame[frameShape="5"] /* VLine */ { - background: ${border}; - color: ${border}; -} - -/* ---------- Toolbars ---------- */ - -QToolBar { - background: ${bg}; - border: none; - spacing: 4px; - padding: 2px; -} -QToolBar::separator { - background: ${border}; - width: 1px; - margin: 4px 4px; -} - -/* ---------- Dock widgets ---------- */ - -QDockWidget { - color: ${text}; - titlebar-close-icon: none; -} -QDockWidget::title { - background: ${bg_subtle}; - padding: 4px; - border: 1px solid ${border}; -} - -/* ---------- Rubber band (multi-select drag rectangle) ---------- */ +/* ---------- Rubber band (multi-select drag) ---------- */ QRubberBand { background: ${accent}; border: 1px solid ${accent}; - /* Qt blends rubber band at ~30% so this reads as a translucent - * accent-tinted rectangle without needing rgba(). */ } - - - /* ---------- Library count label states ---------- */ -/* - * The library tab's count label switches between three visual states - * depending on what refresh() found. The state is exposed as a Qt - * dynamic property `libraryCountState` so users can override these - * rules in their custom.qss without touching the Python. - * - * normal N files — default text color, no rule needed - * empty no items — dim text (no items found, search miss) - * error bad/unreachable — danger color + bold (real error) - */ QLabel[libraryCountState="empty"] { color: ${text_dim}; @@ -531,18 +421,18 @@ QLabel[libraryCountState="error"] { font-weight: bold; } -/* ---------- Thumbnail dot indicators (Qt properties on ThumbnailWidget) ---------- */ +/* ---------- Thumbnail indicators ---------- */ ThumbnailWidget { - qproperty-savedColor: #22cc22; /* green dot: saved to library — universal "confirmed" feel */ - qproperty-bookmarkedColor: #ffcc00; /* yellow star: bookmarked */ + qproperty-savedColor: #22cc22; + qproperty-bookmarkedColor: #ffcc00; qproperty-selectionColor: ${accent}; qproperty-multiSelectColor: ${accent_dim}; qproperty-hoverColor: ${accent}; qproperty-idleColor: ${border_strong}; } -/* ---------- Info panel tag category colors ---------- */ +/* ---------- Info panel tag colors ---------- */ InfoPanel { qproperty-tagArtistColor: ${warning}; @@ -553,19 +443,13 @@ InfoPanel { qproperty-tagLoreColor: ${text_dim}; } -/* ---------- Video player letterbox / pillarbox color (mpv background) ---------- */ +/* ---------- Video player letterbox ---------- */ VideoPlayer { qproperty-letterboxColor: ${bg}; } -/* ---------- Popout overlay bars (slideshow toolbar + slideshow controls + embedded preview controls) ---------- */ -/* - * The popout window's translucent toolbar (top) and transport controls - * (bottom) float over the video content. The bg color comes from the - * @palette overlay_bg slot. Children get the classic overlay treatment: - * transparent backgrounds, near-white text, hairline borders. - */ +/* ---------- Popout overlay bars ---------- */ QWidget#_slideshow_toolbar, QWidget#_slideshow_controls, @@ -588,6 +472,8 @@ QWidget#_preview_controls QPushButton { color: white; border: 1px solid rgba(255, 255, 255, 80); padding: 2px 6px; + font-size: 15px; + font-weight: bold; } QWidget#_slideshow_toolbar QPushButton:hover, QWidget#_slideshow_controls QPushButton:hover, diff --git a/themes/nord-square.qss b/themes/nord-square.qss index 21791bc..021fa26 100644 --- a/themes/nord-square.qss +++ b/themes/nord-square.qss @@ -9,26 +9,25 @@ */ /* @palette - bg: #2e3440 - bg_alt: #272b36 - bg_subtle: #3b4252 - bg_hover: #434c5e - bg_active: #4c566a - text: #eceff4 - text_dim: #d8dee9 - text_disabled: #4c566a - border: #3b4252 - border_strong: #4c566a - accent: #88c0d0 - accent_text: #2e3440 - accent_dim: #81a1c1 - link: #8fbcbb - danger: #bf616a - success: #a3be8c - warning: #ebcb8b - overlay_bg: rgba(46, 52, 64, 200) + bg: #2e3440 + bg_alt: #272b36 + bg_subtle: #3b4252 + bg_hover: #434c5e + bg_active: #4c566a + text: #eceff4 + text_dim: #d8dee9 + text_disabled: #4c566a + border: #3b4252 + border_strong: #4c566a + accent: #88c0d0 + accent_text: #2e3440 + accent_dim: #81a1c1 + link: #8fbcbb + danger: #bf616a + success: #a3be8c + warning: #ebcb8b + overlay_bg: rgba(46, 52, 64, 200) */ - /* ---------- Base ---------- */ QWidget { @@ -43,8 +42,6 @@ QWidget:disabled { color: ${text_disabled}; } -/* Labels should never paint an opaque background — they sit on top of - * other widgets in many places (toolbars, info panels, overlays). */ QLabel { background: transparent; } @@ -91,47 +88,25 @@ QPushButton:flat:hover { background-color: ${bg_hover}; } -QToolButton { - background-color: transparent; - color: ${text}; - border: 1px solid transparent; - padding: 4px; -} -QToolButton:hover { - background-color: ${bg_hover}; - border-color: ${border_strong}; -} -QToolButton:pressed, QToolButton:checked { - background-color: ${bg_active}; -} - /* ---------- Inputs ---------- */ -QLineEdit, QSpinBox, QDoubleSpinBox, QTextEdit, QPlainTextEdit { +QLineEdit, QSpinBox, QTextEdit { background-color: ${bg_subtle}; color: ${text}; border: 1px solid ${border_strong}; padding: 2px 6px; - /* min-height ensures the painted text fits inside the widget bounds - * even when a parent layout (e.g. QFormLayout inside a QGroupBox) - * compresses the natural sizeHint. Without this, spinboxes in dense - * forms render with the top of the value text clipped. */ min-height: 16px; selection-background-color: ${accent}; selection-color: ${accent_text}; } QLineEdit:focus, QSpinBox:focus, -QDoubleSpinBox:focus, -QTextEdit:focus, -QPlainTextEdit:focus { +QTextEdit:focus { border-color: ${accent}; } QLineEdit:disabled, QSpinBox:disabled, -QDoubleSpinBox:disabled, -QTextEdit:disabled, -QPlainTextEdit:disabled { +QTextEdit:disabled { background-color: ${bg_alt}; color: ${text_disabled}; border-color: ${border}; @@ -309,17 +284,6 @@ QSlider::handle:horizontal:hover { background: ${accent_dim}; } -QSlider::groove:vertical { - background: ${bg_subtle}; - width: 4px; -} -QSlider::handle:vertical { - background: ${accent}; - width: 12px; - height: 12px; - margin: 0 -5px; -} - /* ---------- Progress ---------- */ QProgressBar { @@ -333,32 +297,27 @@ QProgressBar::chunk { background-color: ${accent}; } -/* ---------- Checkboxes & radio buttons ---------- */ +/* ---------- Checkboxes ---------- */ -QCheckBox, QRadioButton { +QCheckBox { background: transparent; color: ${text}; spacing: 6px; } -QCheckBox::indicator, QRadioButton::indicator { +QCheckBox::indicator { width: 14px; height: 14px; background-color: ${bg_subtle}; border: 1px solid ${border_strong}; } -QCheckBox::indicator { -} -QRadioButton::indicator { - border-radius: 7px; -} -QCheckBox::indicator:hover, QRadioButton::indicator:hover { +QCheckBox::indicator:hover { border-color: ${accent}; } -QCheckBox::indicator:checked, QRadioButton::indicator:checked { +QCheckBox::indicator:checked { background-color: ${accent}; border-color: ${accent}; } -QCheckBox::indicator:disabled, QRadioButton::indicator:disabled { +QCheckBox::indicator:disabled { background-color: ${bg_alt}; border-color: ${border}; } @@ -372,9 +331,9 @@ QToolTip { padding: 4px 6px; } -/* ---------- Item views (lists, trees, tables) ---------- */ +/* ---------- Lists ---------- */ -QListView, QListWidget, QTreeView, QTreeWidget, QTableView, QTableWidget { +QListView, QListWidget { background-color: ${bg}; alternate-background-color: ${bg_alt}; color: ${text}; @@ -383,35 +342,18 @@ QListView, QListWidget, QTreeView, QTreeWidget, QTableView, QTableWidget { selection-color: ${accent_text}; outline: none; } -QListView::item, QListWidget::item, -QTreeView::item, QTreeWidget::item, -QTableView::item, QTableWidget::item { +QListView::item, QListWidget::item { padding: 4px; } -QListView::item:hover, QListWidget::item:hover, -QTreeView::item:hover, QTreeWidget::item:hover, -QTableView::item:hover, QTableWidget::item:hover { +QListView::item:hover, QListWidget::item:hover { background-color: ${bg_hover}; } -QListView::item:selected, QListWidget::item:selected, -QTreeView::item:selected, QTreeWidget::item:selected, -QTableView::item:selected, QTableWidget::item:selected { +QListView::item:selected, QListWidget::item:selected { background-color: ${accent}; color: ${accent_text}; } -QHeaderView::section { - background-color: ${bg_subtle}; - color: ${text}; - border: none; - border-right: 1px solid ${border}; - padding: 4px 8px; -} -QHeaderView::section:hover { - background-color: ${bg_hover}; -} - -/* ---------- Tabs ---------- */ +/* ---------- Tabs (settings dialog) ---------- */ QTabWidget::pane { border: 1px solid ${border}; @@ -436,7 +378,7 @@ QTabBar::tab:hover:!selected { color: ${text}; } -/* ---------- Group boxes ---------- */ +/* ---------- Group boxes (settings dialog) ---------- */ QGroupBox { background: transparent; @@ -452,63 +394,14 @@ QGroupBox::title { color: ${text_dim}; } -/* ---------- Frames ---------- */ - -QFrame[frameShape="4"], /* HLine */ -QFrame[frameShape="5"] /* VLine */ { - background: ${border}; - color: ${border}; -} - -/* ---------- Toolbars ---------- */ - -QToolBar { - background: ${bg}; - border: none; - spacing: 4px; - padding: 2px; -} -QToolBar::separator { - background: ${border}; - width: 1px; - margin: 4px 4px; -} - -/* ---------- Dock widgets ---------- */ - -QDockWidget { - color: ${text}; - titlebar-close-icon: none; -} -QDockWidget::title { - background: ${bg_subtle}; - padding: 4px; - border: 1px solid ${border}; -} - -/* ---------- Rubber band (multi-select drag rectangle) ---------- */ +/* ---------- Rubber band (multi-select drag) ---------- */ QRubberBand { background: ${accent}; border: 1px solid ${accent}; - /* Qt blends rubber band at ~30% so this reads as a translucent - * accent-tinted rectangle without needing rgba(). */ } - - - /* ---------- Library count label states ---------- */ -/* - * The library tab's count label switches between three visual states - * depending on what refresh() found. The state is exposed as a Qt - * dynamic property `libraryCountState` so users can override these - * rules in their custom.qss without touching the Python. - * - * normal N files — default text color, no rule needed - * empty no items — dim text (no items found, search miss) - * error bad/unreachable — danger color + bold (real error) - */ QLabel[libraryCountState="empty"] { color: ${text_dim}; @@ -518,18 +411,18 @@ QLabel[libraryCountState="error"] { font-weight: bold; } -/* ---------- Thumbnail dot indicators (Qt properties on ThumbnailWidget) ---------- */ +/* ---------- Thumbnail indicators ---------- */ ThumbnailWidget { - qproperty-savedColor: #22cc22; /* green dot: saved to library — universal "confirmed" feel */ - qproperty-bookmarkedColor: #ffcc00; /* yellow star: bookmarked */ + qproperty-savedColor: #22cc22; + qproperty-bookmarkedColor: #ffcc00; qproperty-selectionColor: ${accent}; qproperty-multiSelectColor: ${accent_dim}; qproperty-hoverColor: ${accent}; qproperty-idleColor: ${border_strong}; } -/* ---------- Info panel tag category colors ---------- */ +/* ---------- Info panel tag colors ---------- */ InfoPanel { qproperty-tagArtistColor: ${warning}; @@ -540,19 +433,13 @@ InfoPanel { qproperty-tagLoreColor: ${text_dim}; } -/* ---------- Video player letterbox / pillarbox color (mpv background) ---------- */ +/* ---------- Video player letterbox ---------- */ VideoPlayer { qproperty-letterboxColor: ${bg}; } -/* ---------- Popout overlay bars (slideshow toolbar + slideshow controls + embedded preview controls) ---------- */ -/* - * The popout window's translucent toolbar (top) and transport controls - * (bottom) float over the video content. The bg color comes from the - * @palette overlay_bg slot. Children get the classic overlay treatment: - * transparent backgrounds, near-white text, hairline borders. - */ +/* ---------- Popout overlay bars ---------- */ QWidget#_slideshow_toolbar, QWidget#_slideshow_controls, @@ -575,6 +462,8 @@ QWidget#_preview_controls QPushButton { color: white; border: 1px solid rgba(255, 255, 255, 80); padding: 2px 6px; + font-size: 15px; + font-weight: bold; } QWidget#_slideshow_toolbar QPushButton:hover, QWidget#_slideshow_controls QPushButton:hover, diff --git a/themes/solarized-dark-rounded.qss b/themes/solarized-dark-rounded.qss index 07e107a..1d1edf1 100644 --- a/themes/solarized-dark-rounded.qss +++ b/themes/solarized-dark-rounded.qss @@ -9,26 +9,25 @@ */ /* @palette - bg: #002b36 - bg_alt: #001f27 - bg_subtle: #073642 - bg_hover: #0d4654 - bg_active: #586e75 - text: #93a1a1 - text_dim: #839496 - text_disabled: #586e75 - border: #073642 - border_strong: #0d4654 - accent: #268bd2 - accent_text: #002b36 - accent_dim: #2aa198 - link: #2aa198 - danger: #dc322f - success: #859900 - warning: #b58900 - overlay_bg: rgba(0, 43, 54, 200) + bg: #002b36 + bg_alt: #001f27 + bg_subtle: #073642 + bg_hover: #0d4654 + bg_active: #586e75 + text: #93a1a1 + text_dim: #839496 + text_disabled: #586e75 + border: #073642 + border_strong: #0d4654 + accent: #268bd2 + accent_text: #002b36 + accent_dim: #2aa198 + link: #2aa198 + danger: #dc322f + success: #859900 + warning: #b58900 + overlay_bg: rgba(0, 43, 54, 200) */ - /* ---------- Base ---------- */ QWidget { @@ -43,8 +42,6 @@ QWidget:disabled { color: ${text_disabled}; } -/* Labels should never paint an opaque background — they sit on top of - * other widgets in many places (toolbars, info panels, overlays). */ QLabel { background: transparent; } @@ -92,49 +89,26 @@ QPushButton:flat:hover { background-color: ${bg_hover}; } -QToolButton { - background-color: transparent; - color: ${text}; - border: 1px solid transparent; - border-radius: 4px; - padding: 4px; -} -QToolButton:hover { - background-color: ${bg_hover}; - border-color: ${border_strong}; -} -QToolButton:pressed, QToolButton:checked { - background-color: ${bg_active}; -} - /* ---------- Inputs ---------- */ -QLineEdit, QSpinBox, QDoubleSpinBox, QTextEdit, QPlainTextEdit { +QLineEdit, QSpinBox, QTextEdit { background-color: ${bg_subtle}; color: ${text}; border: 1px solid ${border_strong}; border-radius: 4px; padding: 2px 6px; - /* min-height ensures the painted text fits inside the widget bounds - * even when a parent layout (e.g. QFormLayout inside a QGroupBox) - * compresses the natural sizeHint. Without this, spinboxes in dense - * forms render with the top of the value text clipped. */ min-height: 16px; selection-background-color: ${accent}; selection-color: ${accent_text}; } QLineEdit:focus, QSpinBox:focus, -QDoubleSpinBox:focus, -QTextEdit:focus, -QPlainTextEdit:focus { +QTextEdit:focus { border-color: ${accent}; } QLineEdit:disabled, QSpinBox:disabled, -QDoubleSpinBox:disabled, -QTextEdit:disabled, -QPlainTextEdit:disabled { +QTextEdit:disabled { background-color: ${bg_alt}; color: ${text_disabled}; border-color: ${border}; @@ -315,19 +289,6 @@ QSlider::handle:horizontal:hover { background: ${accent_dim}; } -QSlider::groove:vertical { - background: ${bg_subtle}; - width: 4px; - border-radius: 2px; -} -QSlider::handle:vertical { - background: ${accent}; - width: 12px; - height: 12px; - margin: 0 -5px; - border-radius: 6px; -} - /* ---------- Progress ---------- */ QProgressBar { @@ -343,33 +304,28 @@ QProgressBar::chunk { border-radius: 3px; } -/* ---------- Checkboxes & radio buttons ---------- */ +/* ---------- Checkboxes ---------- */ -QCheckBox, QRadioButton { +QCheckBox { background: transparent; color: ${text}; spacing: 6px; } -QCheckBox::indicator, QRadioButton::indicator { +QCheckBox::indicator { width: 14px; height: 14px; background-color: ${bg_subtle}; border: 1px solid ${border_strong}; -} -QCheckBox::indicator { border-radius: 3px; } -QRadioButton::indicator { - border-radius: 7px; -} -QCheckBox::indicator:hover, QRadioButton::indicator:hover { +QCheckBox::indicator:hover { border-color: ${accent}; } -QCheckBox::indicator:checked, QRadioButton::indicator:checked { +QCheckBox::indicator:checked { background-color: ${accent}; border-color: ${accent}; } -QCheckBox::indicator:disabled, QRadioButton::indicator:disabled { +QCheckBox::indicator:disabled { background-color: ${bg_alt}; border-color: ${border}; } @@ -384,9 +340,9 @@ QToolTip { border-radius: 3px; } -/* ---------- Item views (lists, trees, tables) ---------- */ +/* ---------- Lists ---------- */ -QListView, QListWidget, QTreeView, QTreeWidget, QTableView, QTableWidget { +QListView, QListWidget { background-color: ${bg}; alternate-background-color: ${bg_alt}; color: ${text}; @@ -395,35 +351,18 @@ QListView, QListWidget, QTreeView, QTreeWidget, QTableView, QTableWidget { selection-color: ${accent_text}; outline: none; } -QListView::item, QListWidget::item, -QTreeView::item, QTreeWidget::item, -QTableView::item, QTableWidget::item { +QListView::item, QListWidget::item { padding: 4px; } -QListView::item:hover, QListWidget::item:hover, -QTreeView::item:hover, QTreeWidget::item:hover, -QTableView::item:hover, QTableWidget::item:hover { +QListView::item:hover, QListWidget::item:hover { background-color: ${bg_hover}; } -QListView::item:selected, QListWidget::item:selected, -QTreeView::item:selected, QTreeWidget::item:selected, -QTableView::item:selected, QTableWidget::item:selected { +QListView::item:selected, QListWidget::item:selected { background-color: ${accent}; color: ${accent_text}; } -QHeaderView::section { - background-color: ${bg_subtle}; - color: ${text}; - border: none; - border-right: 1px solid ${border}; - padding: 4px 8px; -} -QHeaderView::section:hover { - background-color: ${bg_hover}; -} - -/* ---------- Tabs ---------- */ +/* ---------- Tabs (settings dialog) ---------- */ QTabWidget::pane { border: 1px solid ${border}; @@ -448,7 +387,7 @@ QTabBar::tab:hover:!selected { color: ${text}; } -/* ---------- Group boxes ---------- */ +/* ---------- Group boxes (settings dialog) ---------- */ QGroupBox { background: transparent; @@ -465,63 +404,14 @@ QGroupBox::title { color: ${text_dim}; } -/* ---------- Frames ---------- */ - -QFrame[frameShape="4"], /* HLine */ -QFrame[frameShape="5"] /* VLine */ { - background: ${border}; - color: ${border}; -} - -/* ---------- Toolbars ---------- */ - -QToolBar { - background: ${bg}; - border: none; - spacing: 4px; - padding: 2px; -} -QToolBar::separator { - background: ${border}; - width: 1px; - margin: 4px 4px; -} - -/* ---------- Dock widgets ---------- */ - -QDockWidget { - color: ${text}; - titlebar-close-icon: none; -} -QDockWidget::title { - background: ${bg_subtle}; - padding: 4px; - border: 1px solid ${border}; -} - -/* ---------- Rubber band (multi-select drag rectangle) ---------- */ +/* ---------- Rubber band (multi-select drag) ---------- */ QRubberBand { background: ${accent}; border: 1px solid ${accent}; - /* Qt blends rubber band at ~30% so this reads as a translucent - * accent-tinted rectangle without needing rgba(). */ } - - - /* ---------- Library count label states ---------- */ -/* - * The library tab's count label switches between three visual states - * depending on what refresh() found. The state is exposed as a Qt - * dynamic property `libraryCountState` so users can override these - * rules in their custom.qss without touching the Python. - * - * normal N files — default text color, no rule needed - * empty no items — dim text (no items found, search miss) - * error bad/unreachable — danger color + bold (real error) - */ QLabel[libraryCountState="empty"] { color: ${text_dim}; @@ -531,18 +421,18 @@ QLabel[libraryCountState="error"] { font-weight: bold; } -/* ---------- Thumbnail dot indicators (Qt properties on ThumbnailWidget) ---------- */ +/* ---------- Thumbnail indicators ---------- */ ThumbnailWidget { - qproperty-savedColor: #22cc22; /* green dot: saved to library — universal "confirmed" feel */ - qproperty-bookmarkedColor: #ffcc00; /* yellow star: bookmarked */ + qproperty-savedColor: #22cc22; + qproperty-bookmarkedColor: #ffcc00; qproperty-selectionColor: ${accent}; qproperty-multiSelectColor: ${accent_dim}; qproperty-hoverColor: ${accent}; qproperty-idleColor: ${border_strong}; } -/* ---------- Info panel tag category colors ---------- */ +/* ---------- Info panel tag colors ---------- */ InfoPanel { qproperty-tagArtistColor: ${warning}; @@ -553,19 +443,13 @@ InfoPanel { qproperty-tagLoreColor: ${text_dim}; } -/* ---------- Video player letterbox / pillarbox color (mpv background) ---------- */ +/* ---------- Video player letterbox ---------- */ VideoPlayer { qproperty-letterboxColor: ${bg}; } -/* ---------- Popout overlay bars (slideshow toolbar + slideshow controls + embedded preview controls) ---------- */ -/* - * The popout window's translucent toolbar (top) and transport controls - * (bottom) float over the video content. The bg color comes from the - * @palette overlay_bg slot. Children get the classic overlay treatment: - * transparent backgrounds, near-white text, hairline borders. - */ +/* ---------- Popout overlay bars ---------- */ QWidget#_slideshow_toolbar, QWidget#_slideshow_controls, @@ -588,6 +472,8 @@ QWidget#_preview_controls QPushButton { color: white; border: 1px solid rgba(255, 255, 255, 80); padding: 2px 6px; + font-size: 15px; + font-weight: bold; } QWidget#_slideshow_toolbar QPushButton:hover, QWidget#_slideshow_controls QPushButton:hover, diff --git a/themes/solarized-dark-square.qss b/themes/solarized-dark-square.qss index 8d6a17b..561d166 100644 --- a/themes/solarized-dark-square.qss +++ b/themes/solarized-dark-square.qss @@ -9,26 +9,25 @@ */ /* @palette - bg: #002b36 - bg_alt: #001f27 - bg_subtle: #073642 - bg_hover: #0d4654 - bg_active: #586e75 - text: #93a1a1 - text_dim: #839496 - text_disabled: #586e75 - border: #073642 - border_strong: #0d4654 - accent: #268bd2 - accent_text: #002b36 - accent_dim: #2aa198 - link: #2aa198 - danger: #dc322f - success: #859900 - warning: #b58900 - overlay_bg: rgba(0, 43, 54, 200) + bg: #002b36 + bg_alt: #001f27 + bg_subtle: #073642 + bg_hover: #0d4654 + bg_active: #586e75 + text: #93a1a1 + text_dim: #839496 + text_disabled: #586e75 + border: #073642 + border_strong: #0d4654 + accent: #268bd2 + accent_text: #002b36 + accent_dim: #2aa198 + link: #2aa198 + danger: #dc322f + success: #859900 + warning: #b58900 + overlay_bg: rgba(0, 43, 54, 200) */ - /* ---------- Base ---------- */ QWidget { @@ -43,8 +42,6 @@ QWidget:disabled { color: ${text_disabled}; } -/* Labels should never paint an opaque background — they sit on top of - * other widgets in many places (toolbars, info panels, overlays). */ QLabel { background: transparent; } @@ -91,47 +88,25 @@ QPushButton:flat:hover { background-color: ${bg_hover}; } -QToolButton { - background-color: transparent; - color: ${text}; - border: 1px solid transparent; - padding: 4px; -} -QToolButton:hover { - background-color: ${bg_hover}; - border-color: ${border_strong}; -} -QToolButton:pressed, QToolButton:checked { - background-color: ${bg_active}; -} - /* ---------- Inputs ---------- */ -QLineEdit, QSpinBox, QDoubleSpinBox, QTextEdit, QPlainTextEdit { +QLineEdit, QSpinBox, QTextEdit { background-color: ${bg_subtle}; color: ${text}; border: 1px solid ${border_strong}; padding: 2px 6px; - /* min-height ensures the painted text fits inside the widget bounds - * even when a parent layout (e.g. QFormLayout inside a QGroupBox) - * compresses the natural sizeHint. Without this, spinboxes in dense - * forms render with the top of the value text clipped. */ min-height: 16px; selection-background-color: ${accent}; selection-color: ${accent_text}; } QLineEdit:focus, QSpinBox:focus, -QDoubleSpinBox:focus, -QTextEdit:focus, -QPlainTextEdit:focus { +QTextEdit:focus { border-color: ${accent}; } QLineEdit:disabled, QSpinBox:disabled, -QDoubleSpinBox:disabled, -QTextEdit:disabled, -QPlainTextEdit:disabled { +QTextEdit:disabled { background-color: ${bg_alt}; color: ${text_disabled}; border-color: ${border}; @@ -309,17 +284,6 @@ QSlider::handle:horizontal:hover { background: ${accent_dim}; } -QSlider::groove:vertical { - background: ${bg_subtle}; - width: 4px; -} -QSlider::handle:vertical { - background: ${accent}; - width: 12px; - height: 12px; - margin: 0 -5px; -} - /* ---------- Progress ---------- */ QProgressBar { @@ -333,32 +297,27 @@ QProgressBar::chunk { background-color: ${accent}; } -/* ---------- Checkboxes & radio buttons ---------- */ +/* ---------- Checkboxes ---------- */ -QCheckBox, QRadioButton { +QCheckBox { background: transparent; color: ${text}; spacing: 6px; } -QCheckBox::indicator, QRadioButton::indicator { +QCheckBox::indicator { width: 14px; height: 14px; background-color: ${bg_subtle}; border: 1px solid ${border_strong}; } -QCheckBox::indicator { -} -QRadioButton::indicator { - border-radius: 7px; -} -QCheckBox::indicator:hover, QRadioButton::indicator:hover { +QCheckBox::indicator:hover { border-color: ${accent}; } -QCheckBox::indicator:checked, QRadioButton::indicator:checked { +QCheckBox::indicator:checked { background-color: ${accent}; border-color: ${accent}; } -QCheckBox::indicator:disabled, QRadioButton::indicator:disabled { +QCheckBox::indicator:disabled { background-color: ${bg_alt}; border-color: ${border}; } @@ -372,9 +331,9 @@ QToolTip { padding: 4px 6px; } -/* ---------- Item views (lists, trees, tables) ---------- */ +/* ---------- Lists ---------- */ -QListView, QListWidget, QTreeView, QTreeWidget, QTableView, QTableWidget { +QListView, QListWidget { background-color: ${bg}; alternate-background-color: ${bg_alt}; color: ${text}; @@ -383,35 +342,18 @@ QListView, QListWidget, QTreeView, QTreeWidget, QTableView, QTableWidget { selection-color: ${accent_text}; outline: none; } -QListView::item, QListWidget::item, -QTreeView::item, QTreeWidget::item, -QTableView::item, QTableWidget::item { +QListView::item, QListWidget::item { padding: 4px; } -QListView::item:hover, QListWidget::item:hover, -QTreeView::item:hover, QTreeWidget::item:hover, -QTableView::item:hover, QTableWidget::item:hover { +QListView::item:hover, QListWidget::item:hover { background-color: ${bg_hover}; } -QListView::item:selected, QListWidget::item:selected, -QTreeView::item:selected, QTreeWidget::item:selected, -QTableView::item:selected, QTableWidget::item:selected { +QListView::item:selected, QListWidget::item:selected { background-color: ${accent}; color: ${accent_text}; } -QHeaderView::section { - background-color: ${bg_subtle}; - color: ${text}; - border: none; - border-right: 1px solid ${border}; - padding: 4px 8px; -} -QHeaderView::section:hover { - background-color: ${bg_hover}; -} - -/* ---------- Tabs ---------- */ +/* ---------- Tabs (settings dialog) ---------- */ QTabWidget::pane { border: 1px solid ${border}; @@ -436,7 +378,7 @@ QTabBar::tab:hover:!selected { color: ${text}; } -/* ---------- Group boxes ---------- */ +/* ---------- Group boxes (settings dialog) ---------- */ QGroupBox { background: transparent; @@ -452,63 +394,14 @@ QGroupBox::title { color: ${text_dim}; } -/* ---------- Frames ---------- */ - -QFrame[frameShape="4"], /* HLine */ -QFrame[frameShape="5"] /* VLine */ { - background: ${border}; - color: ${border}; -} - -/* ---------- Toolbars ---------- */ - -QToolBar { - background: ${bg}; - border: none; - spacing: 4px; - padding: 2px; -} -QToolBar::separator { - background: ${border}; - width: 1px; - margin: 4px 4px; -} - -/* ---------- Dock widgets ---------- */ - -QDockWidget { - color: ${text}; - titlebar-close-icon: none; -} -QDockWidget::title { - background: ${bg_subtle}; - padding: 4px; - border: 1px solid ${border}; -} - -/* ---------- Rubber band (multi-select drag rectangle) ---------- */ +/* ---------- Rubber band (multi-select drag) ---------- */ QRubberBand { background: ${accent}; border: 1px solid ${accent}; - /* Qt blends rubber band at ~30% so this reads as a translucent - * accent-tinted rectangle without needing rgba(). */ } - - - /* ---------- Library count label states ---------- */ -/* - * The library tab's count label switches between three visual states - * depending on what refresh() found. The state is exposed as a Qt - * dynamic property `libraryCountState` so users can override these - * rules in their custom.qss without touching the Python. - * - * normal N files — default text color, no rule needed - * empty no items — dim text (no items found, search miss) - * error bad/unreachable — danger color + bold (real error) - */ QLabel[libraryCountState="empty"] { color: ${text_dim}; @@ -518,18 +411,18 @@ QLabel[libraryCountState="error"] { font-weight: bold; } -/* ---------- Thumbnail dot indicators (Qt properties on ThumbnailWidget) ---------- */ +/* ---------- Thumbnail indicators ---------- */ ThumbnailWidget { - qproperty-savedColor: #22cc22; /* green dot: saved to library — universal "confirmed" feel */ - qproperty-bookmarkedColor: #ffcc00; /* yellow star: bookmarked */ + qproperty-savedColor: #22cc22; + qproperty-bookmarkedColor: #ffcc00; qproperty-selectionColor: ${accent}; qproperty-multiSelectColor: ${accent_dim}; qproperty-hoverColor: ${accent}; qproperty-idleColor: ${border_strong}; } -/* ---------- Info panel tag category colors ---------- */ +/* ---------- Info panel tag colors ---------- */ InfoPanel { qproperty-tagArtistColor: ${warning}; @@ -540,19 +433,13 @@ InfoPanel { qproperty-tagLoreColor: ${text_dim}; } -/* ---------- Video player letterbox / pillarbox color (mpv background) ---------- */ +/* ---------- Video player letterbox ---------- */ VideoPlayer { qproperty-letterboxColor: ${bg}; } -/* ---------- Popout overlay bars (slideshow toolbar + slideshow controls + embedded preview controls) ---------- */ -/* - * The popout window's translucent toolbar (top) and transport controls - * (bottom) float over the video content. The bg color comes from the - * @palette overlay_bg slot. Children get the classic overlay treatment: - * transparent backgrounds, near-white text, hairline borders. - */ +/* ---------- Popout overlay bars ---------- */ QWidget#_slideshow_toolbar, QWidget#_slideshow_controls, @@ -575,6 +462,8 @@ QWidget#_preview_controls QPushButton { color: white; border: 1px solid rgba(255, 255, 255, 80); padding: 2px 6px; + font-size: 15px; + font-weight: bold; } QWidget#_slideshow_toolbar QPushButton:hover, QWidget#_slideshow_controls QPushButton:hover, diff --git a/themes/tokyo-night-rounded.qss b/themes/tokyo-night-rounded.qss index 60e1637..8d5b94f 100644 --- a/themes/tokyo-night-rounded.qss +++ b/themes/tokyo-night-rounded.qss @@ -9,26 +9,25 @@ */ /* @palette - bg: #1a1b26 - bg_alt: #16161e - bg_subtle: #24283b - bg_hover: #292e42 - bg_active: #3b4261 - text: #c0caf5 - text_dim: #a9b1d6 - text_disabled: #565f89 - border: #24283b - border_strong: #292e42 - accent: #7aa2f7 - accent_text: #1a1b26 - accent_dim: #7dcfff - link: #7dcfff - danger: #f7768e - success: #9ece6a - warning: #e0af68 - overlay_bg: rgba(26, 27, 38, 200) + bg: #1a1b26 + bg_alt: #16161e + bg_subtle: #24283b + bg_hover: #292e42 + bg_active: #3b4261 + text: #c0caf5 + text_dim: #a9b1d6 + text_disabled: #565f89 + border: #24283b + border_strong: #292e42 + accent: #7aa2f7 + accent_text: #1a1b26 + accent_dim: #7dcfff + link: #7dcfff + danger: #f7768e + success: #9ece6a + warning: #e0af68 + overlay_bg: rgba(26, 27, 38, 200) */ - /* ---------- Base ---------- */ QWidget { @@ -43,8 +42,6 @@ QWidget:disabled { color: ${text_disabled}; } -/* Labels should never paint an opaque background — they sit on top of - * other widgets in many places (toolbars, info panels, overlays). */ QLabel { background: transparent; } @@ -92,49 +89,26 @@ QPushButton:flat:hover { background-color: ${bg_hover}; } -QToolButton { - background-color: transparent; - color: ${text}; - border: 1px solid transparent; - border-radius: 4px; - padding: 4px; -} -QToolButton:hover { - background-color: ${bg_hover}; - border-color: ${border_strong}; -} -QToolButton:pressed, QToolButton:checked { - background-color: ${bg_active}; -} - /* ---------- Inputs ---------- */ -QLineEdit, QSpinBox, QDoubleSpinBox, QTextEdit, QPlainTextEdit { +QLineEdit, QSpinBox, QTextEdit { background-color: ${bg_subtle}; color: ${text}; border: 1px solid ${border_strong}; border-radius: 4px; padding: 2px 6px; - /* min-height ensures the painted text fits inside the widget bounds - * even when a parent layout (e.g. QFormLayout inside a QGroupBox) - * compresses the natural sizeHint. Without this, spinboxes in dense - * forms render with the top of the value text clipped. */ min-height: 16px; selection-background-color: ${accent}; selection-color: ${accent_text}; } QLineEdit:focus, QSpinBox:focus, -QDoubleSpinBox:focus, -QTextEdit:focus, -QPlainTextEdit:focus { +QTextEdit:focus { border-color: ${accent}; } QLineEdit:disabled, QSpinBox:disabled, -QDoubleSpinBox:disabled, -QTextEdit:disabled, -QPlainTextEdit:disabled { +QTextEdit:disabled { background-color: ${bg_alt}; color: ${text_disabled}; border-color: ${border}; @@ -315,19 +289,6 @@ QSlider::handle:horizontal:hover { background: ${accent_dim}; } -QSlider::groove:vertical { - background: ${bg_subtle}; - width: 4px; - border-radius: 2px; -} -QSlider::handle:vertical { - background: ${accent}; - width: 12px; - height: 12px; - margin: 0 -5px; - border-radius: 6px; -} - /* ---------- Progress ---------- */ QProgressBar { @@ -343,33 +304,28 @@ QProgressBar::chunk { border-radius: 3px; } -/* ---------- Checkboxes & radio buttons ---------- */ +/* ---------- Checkboxes ---------- */ -QCheckBox, QRadioButton { +QCheckBox { background: transparent; color: ${text}; spacing: 6px; } -QCheckBox::indicator, QRadioButton::indicator { +QCheckBox::indicator { width: 14px; height: 14px; background-color: ${bg_subtle}; border: 1px solid ${border_strong}; -} -QCheckBox::indicator { border-radius: 3px; } -QRadioButton::indicator { - border-radius: 7px; -} -QCheckBox::indicator:hover, QRadioButton::indicator:hover { +QCheckBox::indicator:hover { border-color: ${accent}; } -QCheckBox::indicator:checked, QRadioButton::indicator:checked { +QCheckBox::indicator:checked { background-color: ${accent}; border-color: ${accent}; } -QCheckBox::indicator:disabled, QRadioButton::indicator:disabled { +QCheckBox::indicator:disabled { background-color: ${bg_alt}; border-color: ${border}; } @@ -384,9 +340,9 @@ QToolTip { border-radius: 3px; } -/* ---------- Item views (lists, trees, tables) ---------- */ +/* ---------- Lists ---------- */ -QListView, QListWidget, QTreeView, QTreeWidget, QTableView, QTableWidget { +QListView, QListWidget { background-color: ${bg}; alternate-background-color: ${bg_alt}; color: ${text}; @@ -395,35 +351,18 @@ QListView, QListWidget, QTreeView, QTreeWidget, QTableView, QTableWidget { selection-color: ${accent_text}; outline: none; } -QListView::item, QListWidget::item, -QTreeView::item, QTreeWidget::item, -QTableView::item, QTableWidget::item { +QListView::item, QListWidget::item { padding: 4px; } -QListView::item:hover, QListWidget::item:hover, -QTreeView::item:hover, QTreeWidget::item:hover, -QTableView::item:hover, QTableWidget::item:hover { +QListView::item:hover, QListWidget::item:hover { background-color: ${bg_hover}; } -QListView::item:selected, QListWidget::item:selected, -QTreeView::item:selected, QTreeWidget::item:selected, -QTableView::item:selected, QTableWidget::item:selected { +QListView::item:selected, QListWidget::item:selected { background-color: ${accent}; color: ${accent_text}; } -QHeaderView::section { - background-color: ${bg_subtle}; - color: ${text}; - border: none; - border-right: 1px solid ${border}; - padding: 4px 8px; -} -QHeaderView::section:hover { - background-color: ${bg_hover}; -} - -/* ---------- Tabs ---------- */ +/* ---------- Tabs (settings dialog) ---------- */ QTabWidget::pane { border: 1px solid ${border}; @@ -448,7 +387,7 @@ QTabBar::tab:hover:!selected { color: ${text}; } -/* ---------- Group boxes ---------- */ +/* ---------- Group boxes (settings dialog) ---------- */ QGroupBox { background: transparent; @@ -465,63 +404,14 @@ QGroupBox::title { color: ${text_dim}; } -/* ---------- Frames ---------- */ - -QFrame[frameShape="4"], /* HLine */ -QFrame[frameShape="5"] /* VLine */ { - background: ${border}; - color: ${border}; -} - -/* ---------- Toolbars ---------- */ - -QToolBar { - background: ${bg}; - border: none; - spacing: 4px; - padding: 2px; -} -QToolBar::separator { - background: ${border}; - width: 1px; - margin: 4px 4px; -} - -/* ---------- Dock widgets ---------- */ - -QDockWidget { - color: ${text}; - titlebar-close-icon: none; -} -QDockWidget::title { - background: ${bg_subtle}; - padding: 4px; - border: 1px solid ${border}; -} - -/* ---------- Rubber band (multi-select drag rectangle) ---------- */ +/* ---------- Rubber band (multi-select drag) ---------- */ QRubberBand { background: ${accent}; border: 1px solid ${accent}; - /* Qt blends rubber band at ~30% so this reads as a translucent - * accent-tinted rectangle without needing rgba(). */ } - - - /* ---------- Library count label states ---------- */ -/* - * The library tab's count label switches between three visual states - * depending on what refresh() found. The state is exposed as a Qt - * dynamic property `libraryCountState` so users can override these - * rules in their custom.qss without touching the Python. - * - * normal N files — default text color, no rule needed - * empty no items — dim text (no items found, search miss) - * error bad/unreachable — danger color + bold (real error) - */ QLabel[libraryCountState="empty"] { color: ${text_dim}; @@ -531,18 +421,18 @@ QLabel[libraryCountState="error"] { font-weight: bold; } -/* ---------- Thumbnail dot indicators (Qt properties on ThumbnailWidget) ---------- */ +/* ---------- Thumbnail indicators ---------- */ ThumbnailWidget { - qproperty-savedColor: #22cc22; /* green dot: saved to library — universal "confirmed" feel */ - qproperty-bookmarkedColor: #ffcc00; /* yellow star: bookmarked */ + qproperty-savedColor: #22cc22; + qproperty-bookmarkedColor: #ffcc00; qproperty-selectionColor: ${accent}; qproperty-multiSelectColor: ${accent_dim}; qproperty-hoverColor: ${accent}; qproperty-idleColor: ${border_strong}; } -/* ---------- Info panel tag category colors ---------- */ +/* ---------- Info panel tag colors ---------- */ InfoPanel { qproperty-tagArtistColor: ${warning}; @@ -553,19 +443,13 @@ InfoPanel { qproperty-tagLoreColor: ${text_dim}; } -/* ---------- Video player letterbox / pillarbox color (mpv background) ---------- */ +/* ---------- Video player letterbox ---------- */ VideoPlayer { qproperty-letterboxColor: ${bg}; } -/* ---------- Popout overlay bars (slideshow toolbar + slideshow controls + embedded preview controls) ---------- */ -/* - * The popout window's translucent toolbar (top) and transport controls - * (bottom) float over the video content. The bg color comes from the - * @palette overlay_bg slot. Children get the classic overlay treatment: - * transparent backgrounds, near-white text, hairline borders. - */ +/* ---------- Popout overlay bars ---------- */ QWidget#_slideshow_toolbar, QWidget#_slideshow_controls, @@ -588,6 +472,8 @@ QWidget#_preview_controls QPushButton { color: white; border: 1px solid rgba(255, 255, 255, 80); padding: 2px 6px; + font-size: 15px; + font-weight: bold; } QWidget#_slideshow_toolbar QPushButton:hover, QWidget#_slideshow_controls QPushButton:hover, diff --git a/themes/tokyo-night-square.qss b/themes/tokyo-night-square.qss index 9728361..2c874f0 100644 --- a/themes/tokyo-night-square.qss +++ b/themes/tokyo-night-square.qss @@ -9,26 +9,25 @@ */ /* @palette - bg: #1a1b26 - bg_alt: #16161e - bg_subtle: #24283b - bg_hover: #292e42 - bg_active: #3b4261 - text: #c0caf5 - text_dim: #a9b1d6 - text_disabled: #565f89 - border: #24283b - border_strong: #292e42 - accent: #7aa2f7 - accent_text: #1a1b26 - accent_dim: #7dcfff - link: #7dcfff - danger: #f7768e - success: #9ece6a - warning: #e0af68 - overlay_bg: rgba(26, 27, 38, 200) + bg: #1a1b26 + bg_alt: #16161e + bg_subtle: #24283b + bg_hover: #292e42 + bg_active: #3b4261 + text: #c0caf5 + text_dim: #a9b1d6 + text_disabled: #565f89 + border: #24283b + border_strong: #292e42 + accent: #7aa2f7 + accent_text: #1a1b26 + accent_dim: #7dcfff + link: #7dcfff + danger: #f7768e + success: #9ece6a + warning: #e0af68 + overlay_bg: rgba(26, 27, 38, 200) */ - /* ---------- Base ---------- */ QWidget { @@ -43,8 +42,6 @@ QWidget:disabled { color: ${text_disabled}; } -/* Labels should never paint an opaque background — they sit on top of - * other widgets in many places (toolbars, info panels, overlays). */ QLabel { background: transparent; } @@ -91,47 +88,25 @@ QPushButton:flat:hover { background-color: ${bg_hover}; } -QToolButton { - background-color: transparent; - color: ${text}; - border: 1px solid transparent; - padding: 4px; -} -QToolButton:hover { - background-color: ${bg_hover}; - border-color: ${border_strong}; -} -QToolButton:pressed, QToolButton:checked { - background-color: ${bg_active}; -} - /* ---------- Inputs ---------- */ -QLineEdit, QSpinBox, QDoubleSpinBox, QTextEdit, QPlainTextEdit { +QLineEdit, QSpinBox, QTextEdit { background-color: ${bg_subtle}; color: ${text}; border: 1px solid ${border_strong}; padding: 2px 6px; - /* min-height ensures the painted text fits inside the widget bounds - * even when a parent layout (e.g. QFormLayout inside a QGroupBox) - * compresses the natural sizeHint. Without this, spinboxes in dense - * forms render with the top of the value text clipped. */ min-height: 16px; selection-background-color: ${accent}; selection-color: ${accent_text}; } QLineEdit:focus, QSpinBox:focus, -QDoubleSpinBox:focus, -QTextEdit:focus, -QPlainTextEdit:focus { +QTextEdit:focus { border-color: ${accent}; } QLineEdit:disabled, QSpinBox:disabled, -QDoubleSpinBox:disabled, -QTextEdit:disabled, -QPlainTextEdit:disabled { +QTextEdit:disabled { background-color: ${bg_alt}; color: ${text_disabled}; border-color: ${border}; @@ -309,17 +284,6 @@ QSlider::handle:horizontal:hover { background: ${accent_dim}; } -QSlider::groove:vertical { - background: ${bg_subtle}; - width: 4px; -} -QSlider::handle:vertical { - background: ${accent}; - width: 12px; - height: 12px; - margin: 0 -5px; -} - /* ---------- Progress ---------- */ QProgressBar { @@ -333,32 +297,27 @@ QProgressBar::chunk { background-color: ${accent}; } -/* ---------- Checkboxes & radio buttons ---------- */ +/* ---------- Checkboxes ---------- */ -QCheckBox, QRadioButton { +QCheckBox { background: transparent; color: ${text}; spacing: 6px; } -QCheckBox::indicator, QRadioButton::indicator { +QCheckBox::indicator { width: 14px; height: 14px; background-color: ${bg_subtle}; border: 1px solid ${border_strong}; } -QCheckBox::indicator { -} -QRadioButton::indicator { - border-radius: 7px; -} -QCheckBox::indicator:hover, QRadioButton::indicator:hover { +QCheckBox::indicator:hover { border-color: ${accent}; } -QCheckBox::indicator:checked, QRadioButton::indicator:checked { +QCheckBox::indicator:checked { background-color: ${accent}; border-color: ${accent}; } -QCheckBox::indicator:disabled, QRadioButton::indicator:disabled { +QCheckBox::indicator:disabled { background-color: ${bg_alt}; border-color: ${border}; } @@ -372,9 +331,9 @@ QToolTip { padding: 4px 6px; } -/* ---------- Item views (lists, trees, tables) ---------- */ +/* ---------- Lists ---------- */ -QListView, QListWidget, QTreeView, QTreeWidget, QTableView, QTableWidget { +QListView, QListWidget { background-color: ${bg}; alternate-background-color: ${bg_alt}; color: ${text}; @@ -383,35 +342,18 @@ QListView, QListWidget, QTreeView, QTreeWidget, QTableView, QTableWidget { selection-color: ${accent_text}; outline: none; } -QListView::item, QListWidget::item, -QTreeView::item, QTreeWidget::item, -QTableView::item, QTableWidget::item { +QListView::item, QListWidget::item { padding: 4px; } -QListView::item:hover, QListWidget::item:hover, -QTreeView::item:hover, QTreeWidget::item:hover, -QTableView::item:hover, QTableWidget::item:hover { +QListView::item:hover, QListWidget::item:hover { background-color: ${bg_hover}; } -QListView::item:selected, QListWidget::item:selected, -QTreeView::item:selected, QTreeWidget::item:selected, -QTableView::item:selected, QTableWidget::item:selected { +QListView::item:selected, QListWidget::item:selected { background-color: ${accent}; color: ${accent_text}; } -QHeaderView::section { - background-color: ${bg_subtle}; - color: ${text}; - border: none; - border-right: 1px solid ${border}; - padding: 4px 8px; -} -QHeaderView::section:hover { - background-color: ${bg_hover}; -} - -/* ---------- Tabs ---------- */ +/* ---------- Tabs (settings dialog) ---------- */ QTabWidget::pane { border: 1px solid ${border}; @@ -436,7 +378,7 @@ QTabBar::tab:hover:!selected { color: ${text}; } -/* ---------- Group boxes ---------- */ +/* ---------- Group boxes (settings dialog) ---------- */ QGroupBox { background: transparent; @@ -452,63 +394,14 @@ QGroupBox::title { color: ${text_dim}; } -/* ---------- Frames ---------- */ - -QFrame[frameShape="4"], /* HLine */ -QFrame[frameShape="5"] /* VLine */ { - background: ${border}; - color: ${border}; -} - -/* ---------- Toolbars ---------- */ - -QToolBar { - background: ${bg}; - border: none; - spacing: 4px; - padding: 2px; -} -QToolBar::separator { - background: ${border}; - width: 1px; - margin: 4px 4px; -} - -/* ---------- Dock widgets ---------- */ - -QDockWidget { - color: ${text}; - titlebar-close-icon: none; -} -QDockWidget::title { - background: ${bg_subtle}; - padding: 4px; - border: 1px solid ${border}; -} - -/* ---------- Rubber band (multi-select drag rectangle) ---------- */ +/* ---------- Rubber band (multi-select drag) ---------- */ QRubberBand { background: ${accent}; border: 1px solid ${accent}; - /* Qt blends rubber band at ~30% so this reads as a translucent - * accent-tinted rectangle without needing rgba(). */ } - - - /* ---------- Library count label states ---------- */ -/* - * The library tab's count label switches between three visual states - * depending on what refresh() found. The state is exposed as a Qt - * dynamic property `libraryCountState` so users can override these - * rules in their custom.qss without touching the Python. - * - * normal N files — default text color, no rule needed - * empty no items — dim text (no items found, search miss) - * error bad/unreachable — danger color + bold (real error) - */ QLabel[libraryCountState="empty"] { color: ${text_dim}; @@ -518,18 +411,18 @@ QLabel[libraryCountState="error"] { font-weight: bold; } -/* ---------- Thumbnail dot indicators (Qt properties on ThumbnailWidget) ---------- */ +/* ---------- Thumbnail indicators ---------- */ ThumbnailWidget { - qproperty-savedColor: #22cc22; /* green dot: saved to library — universal "confirmed" feel */ - qproperty-bookmarkedColor: #ffcc00; /* yellow star: bookmarked */ + qproperty-savedColor: #22cc22; + qproperty-bookmarkedColor: #ffcc00; qproperty-selectionColor: ${accent}; qproperty-multiSelectColor: ${accent_dim}; qproperty-hoverColor: ${accent}; qproperty-idleColor: ${border_strong}; } -/* ---------- Info panel tag category colors ---------- */ +/* ---------- Info panel tag colors ---------- */ InfoPanel { qproperty-tagArtistColor: ${warning}; @@ -540,19 +433,13 @@ InfoPanel { qproperty-tagLoreColor: ${text_dim}; } -/* ---------- Video player letterbox / pillarbox color (mpv background) ---------- */ +/* ---------- Video player letterbox ---------- */ VideoPlayer { qproperty-letterboxColor: ${bg}; } -/* ---------- Popout overlay bars (slideshow toolbar + slideshow controls + embedded preview controls) ---------- */ -/* - * The popout window's translucent toolbar (top) and transport controls - * (bottom) float over the video content. The bg color comes from the - * @palette overlay_bg slot. Children get the classic overlay treatment: - * transparent backgrounds, near-white text, hairline borders. - */ +/* ---------- Popout overlay bars ---------- */ QWidget#_slideshow_toolbar, QWidget#_slideshow_controls, @@ -575,6 +462,8 @@ QWidget#_preview_controls QPushButton { color: white; border: 1px solid rgba(255, 255, 255, 80); padding: 2px 6px; + font-size: 15px; + font-weight: bold; } QWidget#_slideshow_toolbar QPushButton:hover, QWidget#_slideshow_controls QPushButton:hover,