QSS @palette/${} preprocessor + theme overhaul: themable popout overlays, slider square, mpv letterbox via QPalette, embedded controls under media, compact toolbar buttons

This commit is contained in:
pax 2026-04-07 14:41:00 -05:00
parent 1712fc5836
commit 6c1a98a827
9 changed files with 1662 additions and 1023 deletions

View File

@ -516,7 +516,11 @@ class BooruApp(QMainWindow):
self._preview.fullscreen_requested.connect(self._open_fullscreen_preview) self._preview.fullscreen_requested.connect(self._open_fullscreen_preview)
self._preview.set_folders_callback(self._db.get_folders) self._preview.set_folders_callback(self._db.get_folders)
self._fullscreen_window = None self._fullscreen_window = None
self._preview.setMinimumWidth(300) # Wide enough that the preview toolbar (Bookmark, Save, BL Tag,
# 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)
right.addWidget(self._preview) right.addWidget(self._preview)
self._dl_progress = QProgressBar() self._dl_progress = QProgressBar()
@ -2946,6 +2950,69 @@ def _apply_windows_dark_mode(app: QApplication) -> None:
log.warning(f"Operation failed: {e}") log.warning(f"Operation failed: {e}")
def _load_user_qss(path: Path) -> str:
"""Load a QSS file with optional @palette variable substitution.
Qt's QSS dialect has no native variables, so we add a tiny preprocessor:
/* @palette
accent: #cba6f7
bg: #1e1e2e
text: #cdd6f4
*/
QWidget {
background-color: ${bg};
color: ${text};
selection-background-color: ${accent};
}
The header comment block is parsed for `name: value` pairs and any
`${name}` reference elsewhere in the file is substituted with the
corresponding value before the QSS is handed to Qt. This lets users
recolor a bundled theme by editing the palette block alone, without
hunting through the body for every hex literal.
Backward compatibility: a file without an @palette block is returned
as-is, so plain hand-written Qt-standard QSS still loads unchanged.
Unknown ${name} references are left in place verbatim and logged as
warnings so typos are visible in the log.
"""
import re
text = path.read_text()
palette_match = re.search(r'/\*\s*@palette\b(.*?)\*/', text, re.DOTALL)
if not palette_match:
return text
palette: dict[str, str] = {}
for raw_line in palette_match.group(1).splitlines():
# Strip leading whitespace and any leading * from C-style continuation
line = raw_line.strip().lstrip('*').strip()
if not line or ':' not in line:
continue
key, value = line.split(':', 1)
key = key.strip()
value = value.strip().rstrip(';').strip()
# Allow trailing comments on the same line
if '/*' in value:
value = value.split('/*', 1)[0].strip()
if key and value:
palette[key] = value
refs = set(re.findall(r'\$\{([a-zA-Z_][a-zA-Z0-9_]*)\}', text))
missing = refs - palette.keys()
if missing:
log.warning(
f"QSS @palette: unknown vars {sorted(missing)} in {path.name} "
f"— left in place verbatim, fix the @palette block to define them"
)
def replace(m):
return palette.get(m.group(1), m.group(0))
return re.sub(r'\$\{([a-zA-Z_][a-zA-Z0-9_]*)\}', replace, text)
def run() -> None: def run() -> None:
from ..core.config import data_dir from ..core.config import data_dir
@ -2977,7 +3044,10 @@ def run() -> None:
from PySide6.QtCore import QPoint as _QP from PySide6.QtCore import QPoint as _QP
import re import re
css_text = custom_css.read_text() # Run through the @palette preprocessor (see _load_user_qss
# for the dialect). Plain Qt-standard QSS files without an
# @palette block are returned unchanged.
css_text = _load_user_qss(custom_css)
# Extract text color for arrows # Extract text color for arrows
m = re.search(r'QWidget\s*\{[^}]*?(?:^|\s)color\s*:\s*(#[0-9a-fA-F]{3,8})', css_text, re.MULTILINE) m = re.search(r'QWidget\s*\{[^}]*?(?:^|\s)color\s*:\s*(#[0-9a-fA-F]{3,8})', css_text, re.MULTILINE)

View File

@ -5,8 +5,8 @@ from __future__ import annotations
import logging import logging
from pathlib import Path from pathlib import Path
from PySide6.QtCore import Qt, QPointF, Signal, QTimer from PySide6.QtCore import Qt, QPointF, Signal, QTimer, Property
from PySide6.QtGui import QPixmap, QPainter, QWheelEvent, QMouseEvent, QKeyEvent, QMovie from PySide6.QtGui import QPixmap, QPainter, QWheelEvent, QMouseEvent, QKeyEvent, QMovie, QColor
from PySide6.QtWidgets import ( from PySide6.QtWidgets import (
QWidget, QVBoxLayout, QHBoxLayout, QLabel, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QMainWindow,
QStackedWidget, QPushButton, QSlider, QMenu, QInputDialog, QStyle, QStackedWidget, QPushButton, QSlider, QMenu, QInputDialog, QStyle,
@ -23,40 +23,13 @@ def _is_video(path: str) -> bool:
return Path(path).suffix.lower() in VIDEO_EXTENSIONS return Path(path).suffix.lower() in VIDEO_EXTENSIONS
def _overlay_css(obj_name: str) -> str: ## Overlay styling for the popout's translucent toolbar / controls bar
"""Generate overlay CSS scoped to a specific object name for specificity.""" ## now lives in the bundled themes (themes/*.qss). The widgets get their
return f""" ## object names set in code (FullscreenPreview / VideoPlayer) so theme QSS
QWidget#{obj_name} * {{ ## rules can target them via #_slideshow_toolbar / #_slideshow_controls /
background: transparent; ## #_preview_controls. Users can override the look by editing the
color: white; ## overlay_bg slot in their @palette block, or by adding more specific
border: none; ## QSS rules in their custom.qss.
}}
QWidget#{obj_name} QPushButton {{
background: transparent;
color: white;
border: 1px solid rgba(255, 255, 255, 80);
padding: 2px 6px;
}}
QWidget#{obj_name} QPushButton:hover {{
background: rgba(255, 255, 255, 30);
}}
QWidget#{obj_name} QSlider::groove:horizontal {{
background: rgba(255, 255, 255, 40);
height: 4px;
}}
QWidget#{obj_name} QSlider::handle:horizontal {{
background: white;
width: 10px;
margin: -4px 0;
}}
QWidget#{obj_name} QSlider::sub-page:horizontal {{
background: rgba(255, 255, 255, 120);
}}
QWidget#{obj_name} QLabel {{
background: transparent;
color: white;
}}
"""
class FullscreenPreview(QMainWindow): class FullscreenPreview(QMainWindow):
@ -97,30 +70,44 @@ class FullscreenPreview(QMainWindow):
self.setCentralWidget(central) self.setCentralWidget(central)
# Floating toolbar — overlays on top of media, translucent # Floating toolbar — overlays on top of media, translucent.
# Set the object name BEFORE the widget is polished by Qt so that
# the bundled-theme `QWidget#_slideshow_toolbar` selector matches
# on the very first style computation. Setting it later requires
# an explicit unpolish/polish cycle, which we want to avoid.
self._toolbar = QWidget(central) self._toolbar = QWidget(central)
self._toolbar.setObjectName("_slideshow_toolbar")
toolbar = QHBoxLayout(self._toolbar) toolbar = QHBoxLayout(self._toolbar)
toolbar.setContentsMargins(8, 4, 8, 4) 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: 3px 6px;"
self._bookmark_btn = QPushButton("Bookmark") self._bookmark_btn = QPushButton("Bookmark")
self._bookmark_btn.setMaximumWidth(80) self._bookmark_btn.setMaximumWidth(90)
self._bookmark_btn.setStyleSheet(_tb_btn_style)
self._bookmark_btn.clicked.connect(self.bookmark_requested) self._bookmark_btn.clicked.connect(self.bookmark_requested)
toolbar.addWidget(self._bookmark_btn) toolbar.addWidget(self._bookmark_btn)
self._save_btn = QPushButton("Save") self._save_btn = QPushButton("Save")
self._save_btn.setMaximumWidth(70) self._save_btn.setMaximumWidth(70)
self._save_btn.setStyleSheet(_tb_btn_style)
self._save_btn.clicked.connect(self.save_toggle_requested) self._save_btn.clicked.connect(self.save_toggle_requested)
toolbar.addWidget(self._save_btn) toolbar.addWidget(self._save_btn)
self._is_saved = False self._is_saved = False
self._bl_tag_btn = QPushButton("BL Tag") self._bl_tag_btn = QPushButton("BL Tag")
self._bl_tag_btn.setMaximumWidth(60) 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.setToolTip("Blacklist a tag")
self._bl_tag_btn.clicked.connect(self._show_bl_tag_menu) self._bl_tag_btn.clicked.connect(self._show_bl_tag_menu)
toolbar.addWidget(self._bl_tag_btn) toolbar.addWidget(self._bl_tag_btn)
self._bl_post_btn = QPushButton("BL Post") self._bl_post_btn = QPushButton("BL Post")
self._bl_post_btn.setMaximumWidth(65) 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.setToolTip("Blacklist this post")
self._bl_post_btn.clicked.connect(self.blacklist_post_requested) self._bl_post_btn.clicked.connect(self.blacklist_post_requested)
toolbar.addWidget(self._bl_post_btn) toolbar.addWidget(self._bl_post_btn)
@ -138,12 +125,28 @@ class FullscreenPreview(QMainWindow):
self._toolbar.raise_() self._toolbar.raise_()
# Reparent video controls bar to central widget so it overlays properly # Reparent video controls bar to central widget so it overlays properly.
# The translucent overlay styling (background, transparent buttons,
# white-on-dark text) lives in the bundled themes — see the
# `Popout overlay bars` section of any themes/*.qss. The object names
# are what those rules target.
#
# The toolbar's object name is set above, in its constructor block,
# so the first style poll picks it up. The controls bar was already
# polished as a child of VideoPlayer before being reparented here,
# so we have to force an unpolish/polish round-trip after setting
# its object name to make Qt re-evaluate the style with the new
# `#_slideshow_controls` selector.
self._video._controls_bar.setParent(central) self._video._controls_bar.setParent(central)
self._toolbar.setStyleSheet("QWidget#_slideshow_toolbar { background: rgba(0,0,0,160); }" + _overlay_css("_slideshow_toolbar"))
self._toolbar.setObjectName("_slideshow_toolbar")
self._video._controls_bar.setStyleSheet("QWidget#_slideshow_controls { background: rgba(0,0,0,160); }" + _overlay_css("_slideshow_controls"))
self._video._controls_bar.setObjectName("_slideshow_controls") self._video._controls_bar.setObjectName("_slideshow_controls")
cb_style = self._video._controls_bar.style()
cb_style.unpolish(self._video._controls_bar)
cb_style.polish(self._video._controls_bar)
# Same trick on the toolbar — it might have been polished by the
# central widget's parent before our object name took effect.
tb_style = self._toolbar.style()
tb_style.unpolish(self._toolbar)
tb_style.polish(self._toolbar)
self._video._controls_bar.raise_() self._video._controls_bar.raise_()
self._toolbar.raise_() self._toolbar.raise_()
@ -889,8 +892,54 @@ class VideoPlayer(QWidget):
media_ready = Signal() # emitted when media is loaded and duration is known media_ready = Signal() # emitted when media is loaded and duration is known
video_size = Signal(int, int) # (width, height) emitted when video dimensions are known video_size = Signal(int, int) # (width, height) emitted when video dimensions are known
def __init__(self, parent: QWidget | None = None) -> None: # QSS-controllable letterbox / pillarbox color. mpv paints the area
# around the video frame in this color instead of the default black,
# so portrait videos in a landscape preview slot (or vice versa) blend
# into the panel theme instead of sitting in a hard black box.
# Set via `VideoPlayer { qproperty-letterboxColor: ${bg}; }` in a theme.
# The class default below is just a fallback; __init__ replaces it
# with the current palette's Window color so systems without a custom
# QSS (e.g. Windows dark/light mode driven entirely by QPalette) get
# a letterbox that automatically matches the OS background.
_letterbox_color = QColor("#000000")
def _get_letterbox_color(self): return self._letterbox_color
def _set_letterbox_color(self, c):
self._letterbox_color = QColor(c) if isinstance(c, str) else c
self._apply_letterbox_color()
letterboxColor = Property(QColor, _get_letterbox_color, _set_letterbox_color)
def _apply_letterbox_color(self) -> None:
"""Push the current letterbox color into mpv. No-op if mpv hasn't
been initialized yet _ensure_mpv() calls this after creating the
instance so a QSS-set property still takes effect on first use."""
if self._mpv is None:
return
try:
self._mpv['background'] = 'color'
self._mpv['background-color'] = self._letterbox_color.name()
except Exception:
pass
def __init__(self, parent: QWidget | None = None, embed_controls: bool = True) -> None:
"""
embed_controls: When True (default), the transport controls bar is
added to this VideoPlayer's own layout below the video — used by the
popout window which then reparents the bar to its overlay layer.
When False, the controls bar is constructed but never inserted into
any layout, leaving the embedded preview a clean video surface with
no transport controls visible. Use the popout for playback control.
"""
super().__init__(parent) super().__init__(parent)
# Initialize the letterbox color from the current palette's Window
# role so dark/light mode (or any system without a custom QSS)
# gets a sensible default that matches the surrounding panel.
# The QSS qproperty-letterboxColor on the bundled themes still
# overrides this — Qt calls the setter during widget polish,
# which happens AFTER __init__ when the widget is shown.
from PySide6.QtGui import QPalette
self._letterbox_color = self.palette().color(QPalette.ColorRole.Window)
layout = QVBoxLayout(self) layout = QVBoxLayout(self)
layout.setContentsMargins(0, 0, 0, 0) layout.setContentsMargins(0, 0, 0, 0)
layout.setSpacing(0) layout.setSpacing(0)
@ -908,8 +957,15 @@ class VideoPlayer(QWidget):
controls = QHBoxLayout(self._controls_bar) controls = QHBoxLayout(self._controls_bar)
controls.setContentsMargins(4, 2, 4, 2) 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: 3px 6px;"
self._play_btn = QPushButton("Play") self._play_btn = QPushButton("Play")
self._play_btn.setMaximumWidth(65) self._play_btn.setMaximumWidth(65)
self._play_btn.setStyleSheet(_ctrl_btn_style)
self._play_btn.clicked.connect(self._toggle_play) self._play_btn.clicked.connect(self._toggle_play)
controls.addWidget(self._play_btn) controls.addWidget(self._play_btn)
@ -936,12 +992,14 @@ class VideoPlayer(QWidget):
self._mute_btn = QPushButton("Mute") self._mute_btn = QPushButton("Mute")
self._mute_btn.setMaximumWidth(80) self._mute_btn.setMaximumWidth(80)
self._mute_btn.setStyleSheet(_ctrl_btn_style)
self._mute_btn.clicked.connect(self._toggle_mute) self._mute_btn.clicked.connect(self._toggle_mute)
controls.addWidget(self._mute_btn) controls.addWidget(self._mute_btn)
self._autoplay = True self._autoplay = True
self._autoplay_btn = QPushButton("Auto") self._autoplay_btn = QPushButton("Auto")
self._autoplay_btn.setMaximumWidth(70) self._autoplay_btn.setMaximumWidth(70)
self._autoplay_btn.setStyleSheet(_ctrl_btn_style)
self._autoplay_btn.setCheckable(True) self._autoplay_btn.setCheckable(True)
self._autoplay_btn.setChecked(True) self._autoplay_btn.setChecked(True)
self._autoplay_btn.setToolTip("Auto-play videos when selected") self._autoplay_btn.setToolTip("Auto-play videos when selected")
@ -951,18 +1009,21 @@ class VideoPlayer(QWidget):
self._loop_state = 0 # 0=Loop, 1=Once, 2=Next self._loop_state = 0 # 0=Loop, 1=Once, 2=Next
self._loop_btn = QPushButton("Loop") self._loop_btn = QPushButton("Loop")
self._loop_btn.setMaximumWidth(55) 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.setToolTip("Loop: repeat / Once: stop at end / Next: advance")
self._loop_btn.clicked.connect(self._cycle_loop) self._loop_btn.clicked.connect(self._cycle_loop)
controls.addWidget(self._loop_btn) controls.addWidget(self._loop_btn)
# Style controls to match the translucent overlay look # NO styleSheet here. The popout (FullscreenPreview) re-applies its
self._controls_bar.setObjectName("_preview_controls") # own `_slideshow_controls` overlay styling after reparenting the
self._controls_bar.setStyleSheet( # bar to its central widget — see FullscreenPreview.__init__ — so
"QWidget#_preview_controls { background: rgba(0,0,0,160); }" + _overlay_css("_preview_controls") # the popout still gets the floating dark-translucent look. The
) # embedded preview leaves the bar unstyled so it inherits the
# Add to layout — in preview panel this is part of the normal layout. # panel theme and visually matches the Bookmark/Save/BL Tag bar
# FullscreenPreview reparents it to float as an overlay. # at the top of the panel rather than looking like a stamped-in
# overlay box.
if embed_controls:
layout.addWidget(self._controls_bar) layout.addWidget(self._controls_bar)
self._eof_pending = False self._eof_pending = False
@ -992,6 +1053,10 @@ class VideoPlayer(QWidget):
self._mpv.observe_property('eof-reached', self._on_eof_reached) self._mpv.observe_property('eof-reached', self._on_eof_reached)
self._mpv.observe_property('video-params', self._on_video_params) self._mpv.observe_property('video-params', self._on_video_params)
self._pending_video_size: tuple[int, int] | None = None self._pending_video_size: tuple[int, int] | None = None
# Push any QSS-set letterbox color into mpv now that the instance
# exists. The qproperty-letterboxColor setter is a no-op if mpv
# hasn't been initialized yet, so we have to (re)apply on init.
self._apply_letterbox_color()
return self._mpv return self._mpv
# -- Public API (used by app.py for state sync) -- # -- Public API (used by app.py for state sync) --
@ -1247,24 +1312,37 @@ class ImagePreview(QWidget):
tb.setContentsMargins(2, 1, 2, 1) tb.setContentsMargins(2, 1, 2, 1)
tb.setSpacing(4) 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: 3px 6px;"
self._bookmark_btn = QPushButton("Bookmark") self._bookmark_btn = QPushButton("Bookmark")
self._bookmark_btn.setFixedWidth(80) self._bookmark_btn.setFixedWidth(100)
self._bookmark_btn.setStyleSheet(_tb_btn_style)
self._bookmark_btn.clicked.connect(self.bookmark_requested) self._bookmark_btn.clicked.connect(self.bookmark_requested)
tb.addWidget(self._bookmark_btn) tb.addWidget(self._bookmark_btn)
self._save_btn = QPushButton("Save") self._save_btn = QPushButton("Save")
self._save_btn.setFixedWidth(50) self._save_btn.setFixedWidth(60)
self._save_btn.setStyleSheet(_tb_btn_style)
self._save_btn.clicked.connect(self._on_save_clicked) self._save_btn.clicked.connect(self._on_save_clicked)
tb.addWidget(self._save_btn) tb.addWidget(self._save_btn)
self._bl_tag_btn = QPushButton("BL Tag") self._bl_tag_btn = QPushButton("BL Tag")
self._bl_tag_btn.setFixedWidth(55) 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.setToolTip("Blacklist a tag")
self._bl_tag_btn.clicked.connect(self._show_bl_tag_menu) self._bl_tag_btn.clicked.connect(self._show_bl_tag_menu)
tb.addWidget(self._bl_tag_btn) tb.addWidget(self._bl_tag_btn)
self._bl_post_btn = QPushButton("BL Post") self._bl_post_btn = QPushButton("BL Post")
self._bl_post_btn.setFixedWidth(60) 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.setToolTip("Blacklist this post")
self._bl_post_btn.clicked.connect(self.blacklist_post_requested) self._bl_post_btn.clicked.connect(self.blacklist_post_requested)
tb.addWidget(self._bl_post_btn) tb.addWidget(self._bl_post_btn)
@ -1272,7 +1350,8 @@ class ImagePreview(QWidget):
tb.addStretch() tb.addStretch()
self._popout_btn = QPushButton("Popout") self._popout_btn = QPushButton("Popout")
self._popout_btn.setFixedWidth(60) self._popout_btn.setFixedWidth(65)
self._popout_btn.setStyleSheet(_tb_btn_style)
self._popout_btn.setToolTip("Open in popout") self._popout_btn.setToolTip("Open in popout")
self._popout_btn.clicked.connect(self.fullscreen_requested) self._popout_btn.clicked.connect(self.fullscreen_requested)
tb.addWidget(self._popout_btn) tb.addWidget(self._popout_btn)
@ -1289,12 +1368,31 @@ class ImagePreview(QWidget):
self._image_viewer.close_requested.connect(self.close_requested) self._image_viewer.close_requested.connect(self.close_requested)
self._stack.addWidget(self._image_viewer) self._stack.addWidget(self._image_viewer)
# Video player (index 1) # Video player (index 1). embed_controls=False keeps the
self._video_player = VideoPlayer() # transport controls bar out of the VideoPlayer's own layout —
# we reparent it below the stack a few lines down so the controls
# sit *under* the media rather than overlaying it.
self._video_player = VideoPlayer(embed_controls=False)
self._video_player.setFocusPolicy(Qt.FocusPolicy.NoFocus) self._video_player.setFocusPolicy(Qt.FocusPolicy.NoFocus)
self._video_player.play_next.connect(self.play_next_requested) self._video_player.play_next.connect(self.play_next_requested)
self._stack.addWidget(self._video_player) self._stack.addWidget(self._video_player)
# Place the video controls bar in the preview panel's own layout,
# underneath the stack. The bar exists as a child of VideoPlayer
# but is not in any layout (because of embed_controls=False); we
# adopt it here as a sibling of the stack so it lays out cleanly
# below the media rather than floating on top of it. The popout
# uses its own separate VideoPlayer instance and reparents that
# instance's controls bar to its own central widget as an overlay.
self._stack_video_controls = self._video_player._controls_bar
self._stack_video_controls.setParent(self)
layout.addWidget(self._stack_video_controls)
# Only visible when the stack is showing the video player.
self._stack_video_controls.hide()
self._stack.currentChanged.connect(
lambda idx: self._stack_video_controls.setVisible(idx == 1)
)
# Info label # Info label
self._info_label = QLabel() self._info_label = QLabel()
self._info_label.setStyleSheet("padding: 2px 6px;") self._info_label.setStyleSheet("padding: 2px 6px;")

View File

@ -7,6 +7,63 @@ Copy any `.qss` file from this folder to your data directory as `custom.qss`:
Restart the app after changing themes. Restart the app after changing themes.
## Recoloring a theme — `@palette` blocks and `${...}` vars
Qt's QSS dialect has no native variables, so booru-viewer adds a small
preprocessor that runs before the stylesheet is handed to Qt. Each
bundled theme starts with an `@palette` header block listing the colors
the rest of the file uses, and the body references them as `${name}`:
```css
/* @palette
bg: #1e1e2e
accent: #cba6f7
text: #cdd6f4
*/
QWidget {
background-color: ${bg};
color: ${text};
selection-background-color: ${accent};
}
```
To recolor a theme, **edit the `@palette` block at the top — that's the
only place hex literals appear**. The body picks up the new values
automatically. Save and restart the app.
The preprocessor is opt-in: a `custom.qss` without an `@palette` block
loads as plain Qt-standard QSS, so existing hand-written themes still
work unchanged. Unknown `${name}` references are left in place verbatim
and a warning is logged so typos are visible.
### Available palette slots
The bundled themes define 17 standard color slots. You can add more in
your own `@palette` block (or remove ones you don't reference) — only
slots that the body actually uses need to be defined.
| Slot | Used for |
|---|---|
| `bg` | Window background, scroll area, menu bar |
| `bg_alt` | Alternate row stripes in lists/trees, disabled inputs |
| `bg_subtle` | Buttons and inputs at rest, dropdown panels, tooltips |
| `bg_hover` | Surfaces under cursor hover, scrollbar handles |
| `bg_active` | Surfaces while pressed, scrollbar handles on hover |
| `text` | Primary foreground text |
| `text_dim` | Secondary text — status bar, group titles, placeholders |
| `text_disabled` | Disabled control text |
| `border` | Subtle dividers between adjacent surfaces |
| `border_strong` | More visible borders, default focus rings |
| `accent` | Selection background, focused borders, checked buttons |
| `accent_text` | Foreground used on top of accent backgrounds |
| `accent_dim` | Softer accent variant for hover-on-accent surfaces |
| `link` | Hyperlinks (info panel source URL) |
| `danger` | Destructive action color (Clear All button etc.) |
| `success` | Positive action color (also Character tag default) |
| `warning` | Warning color (also Artist tag default) |
| `overlay_bg` | Translucent background for the popout's floating top toolbar and bottom transport controls. Should be `rgba(...)` so video shows through. |
## Included Themes ## Included Themes
| Theme | File | Preview | | Theme | File | Preview |

View File

@ -1,41 +1,46 @@
/* booru-viewer Catppuccin Mocha /* booru-viewer Catppuccin Mocha
* *
* Comprehensive Fusion-style QSS. Mimics the visual feel of Qt's Fusion * Edit the @palette block below to recolor this theme. The body uses
* style + a dark KDE color scheme: subtle borders, modest corner radius, * ${...} placeholders that the app's _load_user_qss preprocessor
* clear hover/pressed/focus states, transparent labels. * substitutes at load time. See themes/README.md for the full list of
* placeholder names and what each one is used for.
* *
* Palette (edit these and the rest of the file together if you fork): * The same dialect works in any custom.qss you put in your data dir.
* bg #1e1e2e */
* bg_alt #181825
* bg_subtle #313244 /* @palette
* bg_hover #45475a bg: #1e1e2e
* bg_active #585b70 bg_alt: #181825
* text #cdd6f4 bg_subtle: #313244
* text_dim #a6adc8 bg_hover: #45475a
* text_disabled #6c7086 bg_active: #585b70
* border #313244 text: #cdd6f4
* border_strong #45475a text_dim: #a6adc8
* accent #cba6f7 text_disabled: #6c7086
* accent_text #1e1e2e border: #313244
* accent_dim #b4befe border_strong: #45475a
* link #89b4fa accent: #cba6f7
* danger #f38ba8 accent_text: #1e1e2e
* success #a6e3a1 accent_dim: #b4befe
* warning #f9e2af link: #89b4fa
danger: #f38ba8
success: #a6e3a1
warning: #f9e2af
overlay_bg: rgba(30, 30, 46, 200)
*/ */
/* ---------- Base ---------- */ /* ---------- Base ---------- */
QWidget { QWidget {
background-color: #1e1e2e; background-color: ${bg};
color: #cdd6f4; color: ${text};
font-size: 13px; font-size: 13px;
selection-background-color: #cba6f7; selection-background-color: ${accent};
selection-color: #1e1e2e; selection-color: ${accent_text};
} }
QWidget:disabled { QWidget:disabled {
color: #6c7086; color: ${text_disabled};
} }
/* Labels should never paint an opaque background they sit on top of /* Labels should never paint an opaque background they sit on top of
@ -45,115 +50,120 @@ QLabel {
} }
QMainWindow, QDialog { QMainWindow, QDialog {
background-color: #1e1e2e; background-color: ${bg};
} }
/* ---------- Buttons ---------- */ /* ---------- Buttons ---------- */
QPushButton { QPushButton {
background-color: #313244; background-color: ${bg_subtle};
color: #cdd6f4; color: ${text};
border: 1px solid #45475a; border: 1px solid ${border_strong};
border-radius: 4px; border-radius: 4px;
padding: 5px 12px; padding: 5px 12px;
min-height: 18px; min-height: 18px;
} }
QPushButton:hover { QPushButton:hover {
background-color: #45475a; background-color: ${bg_hover};
border-color: #cba6f7; border-color: ${accent};
} }
QPushButton:pressed { QPushButton:pressed {
background-color: #585b70; background-color: ${bg_active};
} }
QPushButton:checked { QPushButton:checked {
background-color: #cba6f7; background-color: ${accent};
color: #1e1e2e; color: ${accent_text};
border-color: #cba6f7; border-color: ${accent};
} }
QPushButton:checked:hover { QPushButton:checked:hover {
background-color: #b4befe; background-color: ${accent_dim};
border-color: #b4befe; border-color: ${accent_dim};
} }
QPushButton:disabled { QPushButton:disabled {
background-color: #181825; background-color: ${bg_alt};
color: #6c7086; color: ${text_disabled};
border-color: #313244; border-color: ${border};
} }
QPushButton:flat { QPushButton:flat {
background: transparent; background: transparent;
border: none; border: none;
} }
QPushButton:flat:hover { QPushButton:flat:hover {
background-color: #45475a; background-color: ${bg_hover};
} }
QToolButton { QToolButton {
background-color: transparent; background-color: transparent;
color: #cdd6f4; color: ${text};
border: 1px solid transparent; border: 1px solid transparent;
border-radius: 4px; border-radius: 4px;
padding: 4px; padding: 4px;
} }
QToolButton:hover { QToolButton:hover {
background-color: #45475a; background-color: ${bg_hover};
border-color: #45475a; border-color: ${border_strong};
} }
QToolButton:pressed, QToolButton:checked { QToolButton:pressed, QToolButton:checked {
background-color: #585b70; background-color: ${bg_active};
} }
/* ---------- Inputs ---------- */ /* ---------- Inputs ---------- */
QLineEdit, QSpinBox, QDoubleSpinBox, QTextEdit, QPlainTextEdit { QLineEdit, QSpinBox, QDoubleSpinBox, QTextEdit, QPlainTextEdit {
background-color: #313244; background-color: ${bg_subtle};
color: #cdd6f4; color: ${text};
border: 1px solid #45475a; border: 1px solid ${border_strong};
border-radius: 4px; border-radius: 4px;
padding: 4px 8px; padding: 4px 8px;
selection-background-color: #cba6f7; /* min-height ensures the painted text fits inside the widget bounds
selection-color: #1e1e2e; * 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: 20px;
selection-background-color: ${accent};
selection-color: ${accent_text};
} }
QLineEdit:focus, QLineEdit:focus,
QSpinBox:focus, QSpinBox:focus,
QDoubleSpinBox:focus, QDoubleSpinBox:focus,
QTextEdit:focus, QTextEdit:focus,
QPlainTextEdit:focus { QPlainTextEdit:focus {
border-color: #cba6f7; border-color: ${accent};
} }
QLineEdit:disabled, QLineEdit:disabled,
QSpinBox:disabled, QSpinBox:disabled,
QDoubleSpinBox:disabled, QDoubleSpinBox:disabled,
QTextEdit:disabled, QTextEdit:disabled,
QPlainTextEdit:disabled { QPlainTextEdit:disabled {
background-color: #181825; background-color: ${bg_alt};
color: #6c7086; color: ${text_disabled};
border-color: #313244; border-color: ${border};
} }
QComboBox { QComboBox {
background-color: #313244; background-color: ${bg_subtle};
color: #cdd6f4; color: ${text};
border: 1px solid #45475a; border: 1px solid ${border_strong};
border-radius: 4px; border-radius: 4px;
padding: 4px 8px; padding: 4px 8px;
min-height: 18px; min-height: 20px;
} }
QComboBox:hover { QComboBox:hover {
border-color: #cba6f7; border-color: ${accent};
} }
QComboBox:focus { QComboBox:focus {
border-color: #cba6f7; border-color: ${accent};
} }
QComboBox::drop-down { QComboBox::drop-down {
border: none; border: none;
width: 18px; width: 18px;
} }
QComboBox QAbstractItemView { QComboBox QAbstractItemView {
background-color: #313244; background-color: ${bg_subtle};
color: #cdd6f4; color: ${text};
border: 1px solid #45475a; border: 1px solid ${border_strong};
selection-background-color: #cba6f7; selection-background-color: ${accent};
selection-color: #1e1e2e; selection-color: ${accent_text};
outline: none; outline: none;
padding: 2px; padding: 2px;
} }
@ -161,19 +171,19 @@ QComboBox QAbstractItemView {
/* ---------- Scrollbars ---------- */ /* ---------- Scrollbars ---------- */
QScrollBar:vertical { QScrollBar:vertical {
background: #1e1e2e; background: ${bg};
width: 10px; width: 10px;
border: none; border: none;
margin: 0; margin: 0;
} }
QScrollBar::handle:vertical { QScrollBar::handle:vertical {
background: #45475a; background: ${bg_hover};
border-radius: 4px; border-radius: 4px;
min-height: 24px; min-height: 24px;
margin: 1px; margin: 1px;
} }
QScrollBar::handle:vertical:hover { QScrollBar::handle:vertical:hover {
background: #585b70; background: ${bg_active};
} }
QScrollBar::add-line:vertical, QScrollBar::add-line:vertical,
QScrollBar::sub-line:vertical { QScrollBar::sub-line:vertical {
@ -186,19 +196,19 @@ QScrollBar::sub-page:vertical {
} }
QScrollBar:horizontal { QScrollBar:horizontal {
background: #1e1e2e; background: ${bg};
height: 10px; height: 10px;
border: none; border: none;
margin: 0; margin: 0;
} }
QScrollBar::handle:horizontal { QScrollBar::handle:horizontal {
background: #45475a; background: ${bg_hover};
border-radius: 4px; border-radius: 4px;
min-width: 24px; min-width: 24px;
margin: 1px; margin: 1px;
} }
QScrollBar::handle:horizontal:hover { QScrollBar::handle:horizontal:hover {
background: #585b70; background: ${bg_active};
} }
QScrollBar::add-line:horizontal, QScrollBar::add-line:horizontal,
QScrollBar::sub-line:horizontal { QScrollBar::sub-line:horizontal {
@ -218,26 +228,26 @@ QScrollArea {
/* ---------- Menus ---------- */ /* ---------- Menus ---------- */
QMenuBar { QMenuBar {
background-color: #1e1e2e; background-color: ${bg};
color: #cdd6f4; color: ${text};
border-bottom: 1px solid #313244; border-bottom: 1px solid ${border};
} }
QMenuBar::item { QMenuBar::item {
background: transparent; background: transparent;
padding: 4px 10px; padding: 4px 10px;
} }
QMenuBar::item:selected { QMenuBar::item:selected {
background-color: #45475a; background-color: ${bg_hover};
color: #cdd6f4; color: ${text};
} }
QMenuBar::item:pressed { QMenuBar::item:pressed {
background-color: #585b70; background-color: ${bg_active};
} }
QMenu { QMenu {
background-color: #313244; background-color: ${bg_subtle};
color: #cdd6f4; color: ${text};
border: 1px solid #45475a; border: 1px solid ${border_strong};
padding: 4px 0; padding: 4px 0;
} }
QMenu::item { QMenu::item {
@ -245,15 +255,15 @@ QMenu::item {
padding: 5px 24px 5px 24px; padding: 5px 24px 5px 24px;
} }
QMenu::item:selected { QMenu::item:selected {
background-color: #cba6f7; background-color: ${accent};
color: #1e1e2e; color: ${accent_text};
} }
QMenu::item:disabled { QMenu::item:disabled {
color: #6c7086; color: ${text_disabled};
} }
QMenu::separator { QMenu::separator {
height: 1px; height: 1px;
background: #313244; background: ${border};
margin: 4px 8px; margin: 4px 8px;
} }
QMenu::icon { QMenu::icon {
@ -263,9 +273,9 @@ QMenu::icon {
/* ---------- Status bar ---------- */ /* ---------- Status bar ---------- */
QStatusBar { QStatusBar {
background-color: #1e1e2e; background-color: ${bg};
color: #a6adc8; color: ${text_dim};
border-top: 1px solid #313244; border-top: 1px solid ${border};
} }
QStatusBar::item { QStatusBar::item {
border: none; border: none;
@ -274,7 +284,7 @@ QStatusBar::item {
/* ---------- Splitters ---------- */ /* ---------- Splitters ---------- */
QSplitter::handle { QSplitter::handle {
background: #313244; background: ${border};
} }
QSplitter::handle:horizontal { QSplitter::handle:horizontal {
width: 2px; width: 2px;
@ -283,38 +293,35 @@ QSplitter::handle:vertical {
height: 2px; height: 2px;
} }
QSplitter::handle:hover { QSplitter::handle:hover {
background: #cba6f7; background: ${accent};
} }
/* ---------- Sliders ---------- */ /* ---------- Sliders ---------- */
QSlider::groove:horizontal { QSlider::groove:horizontal {
background: #313244; background: ${bg_subtle};
height: 4px; height: 4px;
border-radius: 2px;
} }
QSlider::sub-page:horizontal { QSlider::sub-page:horizontal {
background: #cba6f7; background: ${accent};
border-radius: 2px;
} }
QSlider::handle:horizontal { QSlider::handle:horizontal {
background: #cba6f7; background: ${accent};
width: 12px; width: 12px;
height: 12px; height: 12px;
margin: -5px 0; margin: -5px 0;
border-radius: 6px;
} }
QSlider::handle:horizontal:hover { QSlider::handle:horizontal:hover {
background: #b4befe; background: ${accent_dim};
} }
QSlider::groove:vertical { QSlider::groove:vertical {
background: #313244; background: ${bg_subtle};
width: 4px; width: 4px;
border-radius: 2px; border-radius: 2px;
} }
QSlider::handle:vertical { QSlider::handle:vertical {
background: #cba6f7; background: ${accent};
width: 12px; width: 12px;
height: 12px; height: 12px;
margin: 0 -5px; margin: 0 -5px;
@ -324,15 +331,15 @@ QSlider::handle:vertical {
/* ---------- Progress ---------- */ /* ---------- Progress ---------- */
QProgressBar { QProgressBar {
background-color: #313244; background-color: ${bg_subtle};
color: #cdd6f4; color: ${text};
border: 1px solid #313244; border: 1px solid ${border};
border-radius: 3px; border-radius: 3px;
text-align: center; text-align: center;
height: 6px; height: 6px;
} }
QProgressBar::chunk { QProgressBar::chunk {
background-color: #cba6f7; background-color: ${accent};
border-radius: 3px; border-radius: 3px;
} }
@ -340,14 +347,14 @@ QProgressBar::chunk {
QCheckBox, QRadioButton { QCheckBox, QRadioButton {
background: transparent; background: transparent;
color: #cdd6f4; color: ${text};
spacing: 6px; spacing: 6px;
} }
QCheckBox::indicator, QRadioButton::indicator { QCheckBox::indicator, QRadioButton::indicator {
width: 14px; width: 14px;
height: 14px; height: 14px;
background-color: #313244; background-color: ${bg_subtle};
border: 1px solid #45475a; border: 1px solid ${border_strong};
} }
QCheckBox::indicator { QCheckBox::indicator {
border-radius: 3px; border-radius: 3px;
@ -356,23 +363,23 @@ QRadioButton::indicator {
border-radius: 7px; border-radius: 7px;
} }
QCheckBox::indicator:hover, QRadioButton::indicator:hover { QCheckBox::indicator:hover, QRadioButton::indicator:hover {
border-color: #cba6f7; border-color: ${accent};
} }
QCheckBox::indicator:checked, QRadioButton::indicator:checked { QCheckBox::indicator:checked, QRadioButton::indicator:checked {
background-color: #cba6f7; background-color: ${accent};
border-color: #cba6f7; border-color: ${accent};
} }
QCheckBox::indicator:disabled, QRadioButton::indicator:disabled { QCheckBox::indicator:disabled, QRadioButton::indicator:disabled {
background-color: #181825; background-color: ${bg_alt};
border-color: #313244; border-color: ${border};
} }
/* ---------- Tooltips ---------- */ /* ---------- Tooltips ---------- */
QToolTip { QToolTip {
background-color: #313244; background-color: ${bg_subtle};
color: #cdd6f4; color: ${text};
border: 1px solid #45475a; border: 1px solid ${border_strong};
padding: 4px 6px; padding: 4px 6px;
border-radius: 3px; border-radius: 3px;
} }
@ -380,12 +387,12 @@ QToolTip {
/* ---------- Item views (lists, trees, tables) ---------- */ /* ---------- Item views (lists, trees, tables) ---------- */
QListView, QListWidget, QTreeView, QTreeWidget, QTableView, QTableWidget { QListView, QListWidget, QTreeView, QTreeWidget, QTableView, QTableWidget {
background-color: #1e1e2e; background-color: ${bg};
alternate-background-color: #181825; alternate-background-color: ${bg_alt};
color: #cdd6f4; color: ${text};
border: 1px solid #313244; border: 1px solid ${border};
selection-background-color: #cba6f7; selection-background-color: ${accent};
selection-color: #1e1e2e; selection-color: ${accent_text};
outline: none; outline: none;
} }
QListView::item, QListWidget::item, QListView::item, QListWidget::item,
@ -396,57 +403,57 @@ QTableView::item, QTableWidget::item {
QListView::item:hover, QListWidget::item:hover, QListView::item:hover, QListWidget::item:hover,
QTreeView::item:hover, QTreeWidget::item:hover, QTreeView::item:hover, QTreeWidget::item:hover,
QTableView::item:hover, QTableWidget::item:hover { QTableView::item:hover, QTableWidget::item:hover {
background-color: #45475a; background-color: ${bg_hover};
} }
QListView::item:selected, QListWidget::item:selected, QListView::item:selected, QListWidget::item:selected,
QTreeView::item:selected, QTreeWidget::item:selected, QTreeView::item:selected, QTreeWidget::item:selected,
QTableView::item:selected, QTableWidget::item:selected { QTableView::item:selected, QTableWidget::item:selected {
background-color: #cba6f7; background-color: ${accent};
color: #1e1e2e; color: ${accent_text};
} }
QHeaderView::section { QHeaderView::section {
background-color: #313244; background-color: ${bg_subtle};
color: #cdd6f4; color: ${text};
border: none; border: none;
border-right: 1px solid #313244; border-right: 1px solid ${border};
padding: 4px 8px; padding: 4px 8px;
} }
QHeaderView::section:hover { QHeaderView::section:hover {
background-color: #45475a; background-color: ${bg_hover};
} }
/* ---------- Tabs ---------- */ /* ---------- Tabs ---------- */
QTabWidget::pane { QTabWidget::pane {
border: 1px solid #313244; border: 1px solid ${border};
top: -1px; top: -1px;
} }
QTabBar::tab { QTabBar::tab {
background: #313244; background: ${bg_subtle};
color: #a6adc8; color: ${text_dim};
border: 1px solid #313244; border: 1px solid ${border};
border-bottom: none; border-bottom: none;
padding: 6px 14px; padding: 6px 14px;
border-top-left-radius: 4px; border-top-left-radius: 4px;
border-top-right-radius: 4px; border-top-right-radius: 4px;
} }
QTabBar::tab:selected { QTabBar::tab:selected {
background: #1e1e2e; background: ${bg};
color: #cdd6f4; color: ${text};
border-color: #45475a; border-color: ${border_strong};
} }
QTabBar::tab:hover:!selected { QTabBar::tab:hover:!selected {
background: #45475a; background: ${bg_hover};
color: #cdd6f4; color: ${text};
} }
/* ---------- Group boxes ---------- */ /* ---------- Group boxes ---------- */
QGroupBox { QGroupBox {
background: transparent; background: transparent;
color: #cdd6f4; color: ${text};
border: 1px solid #313244; border: 1px solid ${border};
border-radius: 4px; border-radius: 4px;
margin-top: 10px; margin-top: 10px;
padding-top: 8px; padding-top: 8px;
@ -455,27 +462,27 @@ QGroupBox::title {
subcontrol-origin: margin; subcontrol-origin: margin;
subcontrol-position: top left; subcontrol-position: top left;
padding: 0 6px; padding: 0 6px;
color: #a6adc8; color: ${text_dim};
} }
/* ---------- Frames ---------- */ /* ---------- Frames ---------- */
QFrame[frameShape="4"], /* HLine */ QFrame[frameShape="4"], /* HLine */
QFrame[frameShape="5"] /* VLine */ { QFrame[frameShape="5"] /* VLine */ {
background: #313244; background: ${border};
color: #313244; color: ${border};
} }
/* ---------- Toolbars ---------- */ /* ---------- Toolbars ---------- */
QToolBar { QToolBar {
background: #1e1e2e; background: ${bg};
border: none; border: none;
spacing: 4px; spacing: 4px;
padding: 2px; padding: 2px;
} }
QToolBar::separator { QToolBar::separator {
background: #313244; background: ${border};
width: 1px; width: 1px;
margin: 4px 4px; margin: 4px 4px;
} }
@ -483,31 +490,25 @@ QToolBar::separator {
/* ---------- Dock widgets ---------- */ /* ---------- Dock widgets ---------- */
QDockWidget { QDockWidget {
color: #cdd6f4; color: ${text};
titlebar-close-icon: none; titlebar-close-icon: none;
} }
QDockWidget::title { QDockWidget::title {
background: #313244; background: ${bg_subtle};
padding: 4px; padding: 4px;
border: 1px solid #313244; border: 1px solid ${border};
} }
/* ---------- Rubber band (multi-select drag rectangle) ---------- */ /* ---------- Rubber band (multi-select drag rectangle) ---------- */
QRubberBand { QRubberBand {
background: #cba6f7; background: ${accent};
border: 1px solid #cba6f7; border: 1px solid ${accent};
/* Qt blends rubber band at ~30% so this reads as a translucent /* Qt blends rubber band at ~30% so this reads as a translucent
* accent-tinted rectangle without needing rgba(). */ * accent-tinted rectangle without needing rgba(). */
} }
/* ---------- Popout & preview overlay controls ---------- */
QWidget#_preview_controls,
QWidget#_slideshow_toolbar,
QWidget#_slideshow_controls {
background: rgba(0, 0, 0, 160);
}
/* ---------- Thumbnail dot indicators (Qt properties on ThumbnailWidget) ---------- */ /* ---------- Thumbnail dot indicators (Qt properties on ThumbnailWidget) ---------- */
@ -519,10 +520,78 @@ ThumbnailWidget {
/* ---------- Info panel tag category colors ---------- */ /* ---------- Info panel tag category colors ---------- */
InfoPanel { InfoPanel {
qproperty-tagArtistColor: #f9e2af; qproperty-tagArtistColor: ${warning};
qproperty-tagCharacterColor: #a6e3a1; qproperty-tagCharacterColor: ${success};
qproperty-tagCopyrightColor: #cba6f7; qproperty-tagCopyrightColor: ${accent};
qproperty-tagSpeciesColor: #f38ba8; qproperty-tagSpeciesColor: ${danger};
qproperty-tagMetaColor: #a6adc8; qproperty-tagMetaColor: ${text_dim};
qproperty-tagLoreColor: #a6adc8; qproperty-tagLoreColor: ${text_dim};
}
/* ---------- Video player letterbox / pillarbox color (mpv background) ---------- */
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.
*/
QWidget#_slideshow_toolbar,
QWidget#_slideshow_controls,
QWidget#_preview_controls {
background: ${overlay_bg};
}
QWidget#_slideshow_toolbar *,
QWidget#_slideshow_controls *,
QWidget#_preview_controls * {
background: transparent;
color: white;
border: none;
}
QWidget#_slideshow_toolbar QPushButton,
QWidget#_slideshow_controls QPushButton,
QWidget#_preview_controls QPushButton {
background: transparent;
color: white;
border: 1px solid rgba(255, 255, 255, 80);
padding: 2px 6px;
}
QWidget#_slideshow_toolbar QPushButton:hover,
QWidget#_slideshow_controls QPushButton:hover,
QWidget#_preview_controls QPushButton:hover {
background: rgba(255, 255, 255, 30);
}
QWidget#_slideshow_toolbar QSlider::groove:horizontal,
QWidget#_slideshow_controls QSlider::groove:horizontal,
QWidget#_preview_controls QSlider::groove:horizontal {
background: rgba(255, 255, 255, 40);
height: 4px;
}
QWidget#_slideshow_toolbar QSlider::handle:horizontal,
QWidget#_slideshow_controls QSlider::handle:horizontal,
QWidget#_preview_controls QSlider::handle:horizontal {
background: ${accent};
width: 10px;
margin: -4px 0;
}
QWidget#_slideshow_toolbar QSlider::sub-page:horizontal,
QWidget#_slideshow_controls QSlider::sub-page:horizontal,
QWidget#_preview_controls QSlider::sub-page:horizontal {
background: ${accent};
}
QWidget#_slideshow_toolbar QLabel,
QWidget#_slideshow_controls QLabel,
QWidget#_preview_controls QLabel {
background: transparent;
color: white;
} }

View File

@ -1,41 +1,46 @@
/* booru-viewer Everforest Dark /* booru-viewer Everforest Dark
* *
* Comprehensive Fusion-style QSS. Mimics the visual feel of Qt's Fusion * Edit the @palette block below to recolor this theme. The body uses
* style + a dark KDE color scheme: subtle borders, modest corner radius, * ${...} placeholders that the app's _load_user_qss preprocessor
* clear hover/pressed/focus states, transparent labels. * substitutes at load time. See themes/README.md for the full list of
* placeholder names and what each one is used for.
* *
* Palette (edit these and the rest of the file together if you fork): * The same dialect works in any custom.qss you put in your data dir.
* bg #2d353b */
* bg_alt #232a2e
* bg_subtle #343f44 /* @palette
* bg_hover #3d484d bg: #2d353b
* bg_active #4f585e bg_alt: #232a2e
* text #d3c6aa bg_subtle: #343f44
* text_dim #9da9a0 bg_hover: #3d484d
* text_disabled #7a8478 bg_active: #4f585e
* border #343f44 text: #d3c6aa
* border_strong #3d484d text_dim: #9da9a0
* accent #a7c080 text_disabled: #7a8478
* accent_text #2d353b border: #343f44
* accent_dim #83c092 border_strong: #3d484d
* link #7fbbb3 accent: #a7c080
* danger #e67e80 accent_text: #2d353b
* success #a7c080 accent_dim: #83c092
* warning #dbbc7f link: #7fbbb3
danger: #e67e80
success: #a7c080
warning: #dbbc7f
overlay_bg: rgba(45, 53, 59, 200)
*/ */
/* ---------- Base ---------- */ /* ---------- Base ---------- */
QWidget { QWidget {
background-color: #2d353b; background-color: ${bg};
color: #d3c6aa; color: ${text};
font-size: 13px; font-size: 13px;
selection-background-color: #a7c080; selection-background-color: ${accent};
selection-color: #2d353b; selection-color: ${accent_text};
} }
QWidget:disabled { QWidget:disabled {
color: #7a8478; color: ${text_disabled};
} }
/* Labels should never paint an opaque background they sit on top of /* Labels should never paint an opaque background they sit on top of
@ -45,115 +50,120 @@ QLabel {
} }
QMainWindow, QDialog { QMainWindow, QDialog {
background-color: #2d353b; background-color: ${bg};
} }
/* ---------- Buttons ---------- */ /* ---------- Buttons ---------- */
QPushButton { QPushButton {
background-color: #343f44; background-color: ${bg_subtle};
color: #d3c6aa; color: ${text};
border: 1px solid #3d484d; border: 1px solid ${border_strong};
border-radius: 4px; border-radius: 4px;
padding: 5px 12px; padding: 5px 12px;
min-height: 18px; min-height: 18px;
} }
QPushButton:hover { QPushButton:hover {
background-color: #3d484d; background-color: ${bg_hover};
border-color: #a7c080; border-color: ${accent};
} }
QPushButton:pressed { QPushButton:pressed {
background-color: #4f585e; background-color: ${bg_active};
} }
QPushButton:checked { QPushButton:checked {
background-color: #a7c080; background-color: ${accent};
color: #2d353b; color: ${accent_text};
border-color: #a7c080; border-color: ${accent};
} }
QPushButton:checked:hover { QPushButton:checked:hover {
background-color: #83c092; background-color: ${accent_dim};
border-color: #83c092; border-color: ${accent_dim};
} }
QPushButton:disabled { QPushButton:disabled {
background-color: #232a2e; background-color: ${bg_alt};
color: #7a8478; color: ${text_disabled};
border-color: #343f44; border-color: ${border};
} }
QPushButton:flat { QPushButton:flat {
background: transparent; background: transparent;
border: none; border: none;
} }
QPushButton:flat:hover { QPushButton:flat:hover {
background-color: #3d484d; background-color: ${bg_hover};
} }
QToolButton { QToolButton {
background-color: transparent; background-color: transparent;
color: #d3c6aa; color: ${text};
border: 1px solid transparent; border: 1px solid transparent;
border-radius: 4px; border-radius: 4px;
padding: 4px; padding: 4px;
} }
QToolButton:hover { QToolButton:hover {
background-color: #3d484d; background-color: ${bg_hover};
border-color: #3d484d; border-color: ${border_strong};
} }
QToolButton:pressed, QToolButton:checked { QToolButton:pressed, QToolButton:checked {
background-color: #4f585e; background-color: ${bg_active};
} }
/* ---------- Inputs ---------- */ /* ---------- Inputs ---------- */
QLineEdit, QSpinBox, QDoubleSpinBox, QTextEdit, QPlainTextEdit { QLineEdit, QSpinBox, QDoubleSpinBox, QTextEdit, QPlainTextEdit {
background-color: #343f44; background-color: ${bg_subtle};
color: #d3c6aa; color: ${text};
border: 1px solid #3d484d; border: 1px solid ${border_strong};
border-radius: 4px; border-radius: 4px;
padding: 4px 8px; padding: 4px 8px;
selection-background-color: #a7c080; /* min-height ensures the painted text fits inside the widget bounds
selection-color: #2d353b; * 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: 20px;
selection-background-color: ${accent};
selection-color: ${accent_text};
} }
QLineEdit:focus, QLineEdit:focus,
QSpinBox:focus, QSpinBox:focus,
QDoubleSpinBox:focus, QDoubleSpinBox:focus,
QTextEdit:focus, QTextEdit:focus,
QPlainTextEdit:focus { QPlainTextEdit:focus {
border-color: #a7c080; border-color: ${accent};
} }
QLineEdit:disabled, QLineEdit:disabled,
QSpinBox:disabled, QSpinBox:disabled,
QDoubleSpinBox:disabled, QDoubleSpinBox:disabled,
QTextEdit:disabled, QTextEdit:disabled,
QPlainTextEdit:disabled { QPlainTextEdit:disabled {
background-color: #232a2e; background-color: ${bg_alt};
color: #7a8478; color: ${text_disabled};
border-color: #343f44; border-color: ${border};
} }
QComboBox { QComboBox {
background-color: #343f44; background-color: ${bg_subtle};
color: #d3c6aa; color: ${text};
border: 1px solid #3d484d; border: 1px solid ${border_strong};
border-radius: 4px; border-radius: 4px;
padding: 4px 8px; padding: 4px 8px;
min-height: 18px; min-height: 20px;
} }
QComboBox:hover { QComboBox:hover {
border-color: #a7c080; border-color: ${accent};
} }
QComboBox:focus { QComboBox:focus {
border-color: #a7c080; border-color: ${accent};
} }
QComboBox::drop-down { QComboBox::drop-down {
border: none; border: none;
width: 18px; width: 18px;
} }
QComboBox QAbstractItemView { QComboBox QAbstractItemView {
background-color: #343f44; background-color: ${bg_subtle};
color: #d3c6aa; color: ${text};
border: 1px solid #3d484d; border: 1px solid ${border_strong};
selection-background-color: #a7c080; selection-background-color: ${accent};
selection-color: #2d353b; selection-color: ${accent_text};
outline: none; outline: none;
padding: 2px; padding: 2px;
} }
@ -161,19 +171,19 @@ QComboBox QAbstractItemView {
/* ---------- Scrollbars ---------- */ /* ---------- Scrollbars ---------- */
QScrollBar:vertical { QScrollBar:vertical {
background: #2d353b; background: ${bg};
width: 10px; width: 10px;
border: none; border: none;
margin: 0; margin: 0;
} }
QScrollBar::handle:vertical { QScrollBar::handle:vertical {
background: #3d484d; background: ${bg_hover};
border-radius: 4px; border-radius: 4px;
min-height: 24px; min-height: 24px;
margin: 1px; margin: 1px;
} }
QScrollBar::handle:vertical:hover { QScrollBar::handle:vertical:hover {
background: #4f585e; background: ${bg_active};
} }
QScrollBar::add-line:vertical, QScrollBar::add-line:vertical,
QScrollBar::sub-line:vertical { QScrollBar::sub-line:vertical {
@ -186,19 +196,19 @@ QScrollBar::sub-page:vertical {
} }
QScrollBar:horizontal { QScrollBar:horizontal {
background: #2d353b; background: ${bg};
height: 10px; height: 10px;
border: none; border: none;
margin: 0; margin: 0;
} }
QScrollBar::handle:horizontal { QScrollBar::handle:horizontal {
background: #3d484d; background: ${bg_hover};
border-radius: 4px; border-radius: 4px;
min-width: 24px; min-width: 24px;
margin: 1px; margin: 1px;
} }
QScrollBar::handle:horizontal:hover { QScrollBar::handle:horizontal:hover {
background: #4f585e; background: ${bg_active};
} }
QScrollBar::add-line:horizontal, QScrollBar::add-line:horizontal,
QScrollBar::sub-line:horizontal { QScrollBar::sub-line:horizontal {
@ -218,26 +228,26 @@ QScrollArea {
/* ---------- Menus ---------- */ /* ---------- Menus ---------- */
QMenuBar { QMenuBar {
background-color: #2d353b; background-color: ${bg};
color: #d3c6aa; color: ${text};
border-bottom: 1px solid #343f44; border-bottom: 1px solid ${border};
} }
QMenuBar::item { QMenuBar::item {
background: transparent; background: transparent;
padding: 4px 10px; padding: 4px 10px;
} }
QMenuBar::item:selected { QMenuBar::item:selected {
background-color: #3d484d; background-color: ${bg_hover};
color: #d3c6aa; color: ${text};
} }
QMenuBar::item:pressed { QMenuBar::item:pressed {
background-color: #4f585e; background-color: ${bg_active};
} }
QMenu { QMenu {
background-color: #343f44; background-color: ${bg_subtle};
color: #d3c6aa; color: ${text};
border: 1px solid #3d484d; border: 1px solid ${border_strong};
padding: 4px 0; padding: 4px 0;
} }
QMenu::item { QMenu::item {
@ -245,15 +255,15 @@ QMenu::item {
padding: 5px 24px 5px 24px; padding: 5px 24px 5px 24px;
} }
QMenu::item:selected { QMenu::item:selected {
background-color: #a7c080; background-color: ${accent};
color: #2d353b; color: ${accent_text};
} }
QMenu::item:disabled { QMenu::item:disabled {
color: #7a8478; color: ${text_disabled};
} }
QMenu::separator { QMenu::separator {
height: 1px; height: 1px;
background: #343f44; background: ${border};
margin: 4px 8px; margin: 4px 8px;
} }
QMenu::icon { QMenu::icon {
@ -263,9 +273,9 @@ QMenu::icon {
/* ---------- Status bar ---------- */ /* ---------- Status bar ---------- */
QStatusBar { QStatusBar {
background-color: #2d353b; background-color: ${bg};
color: #9da9a0; color: ${text_dim};
border-top: 1px solid #343f44; border-top: 1px solid ${border};
} }
QStatusBar::item { QStatusBar::item {
border: none; border: none;
@ -274,7 +284,7 @@ QStatusBar::item {
/* ---------- Splitters ---------- */ /* ---------- Splitters ---------- */
QSplitter::handle { QSplitter::handle {
background: #343f44; background: ${border};
} }
QSplitter::handle:horizontal { QSplitter::handle:horizontal {
width: 2px; width: 2px;
@ -283,38 +293,35 @@ QSplitter::handle:vertical {
height: 2px; height: 2px;
} }
QSplitter::handle:hover { QSplitter::handle:hover {
background: #a7c080; background: ${accent};
} }
/* ---------- Sliders ---------- */ /* ---------- Sliders ---------- */
QSlider::groove:horizontal { QSlider::groove:horizontal {
background: #343f44; background: ${bg_subtle};
height: 4px; height: 4px;
border-radius: 2px;
} }
QSlider::sub-page:horizontal { QSlider::sub-page:horizontal {
background: #a7c080; background: ${accent};
border-radius: 2px;
} }
QSlider::handle:horizontal { QSlider::handle:horizontal {
background: #a7c080; background: ${accent};
width: 12px; width: 12px;
height: 12px; height: 12px;
margin: -5px 0; margin: -5px 0;
border-radius: 6px;
} }
QSlider::handle:horizontal:hover { QSlider::handle:horizontal:hover {
background: #83c092; background: ${accent_dim};
} }
QSlider::groove:vertical { QSlider::groove:vertical {
background: #343f44; background: ${bg_subtle};
width: 4px; width: 4px;
border-radius: 2px; border-radius: 2px;
} }
QSlider::handle:vertical { QSlider::handle:vertical {
background: #a7c080; background: ${accent};
width: 12px; width: 12px;
height: 12px; height: 12px;
margin: 0 -5px; margin: 0 -5px;
@ -324,15 +331,15 @@ QSlider::handle:vertical {
/* ---------- Progress ---------- */ /* ---------- Progress ---------- */
QProgressBar { QProgressBar {
background-color: #343f44; background-color: ${bg_subtle};
color: #d3c6aa; color: ${text};
border: 1px solid #343f44; border: 1px solid ${border};
border-radius: 3px; border-radius: 3px;
text-align: center; text-align: center;
height: 6px; height: 6px;
} }
QProgressBar::chunk { QProgressBar::chunk {
background-color: #a7c080; background-color: ${accent};
border-radius: 3px; border-radius: 3px;
} }
@ -340,14 +347,14 @@ QProgressBar::chunk {
QCheckBox, QRadioButton { QCheckBox, QRadioButton {
background: transparent; background: transparent;
color: #d3c6aa; color: ${text};
spacing: 6px; spacing: 6px;
} }
QCheckBox::indicator, QRadioButton::indicator { QCheckBox::indicator, QRadioButton::indicator {
width: 14px; width: 14px;
height: 14px; height: 14px;
background-color: #343f44; background-color: ${bg_subtle};
border: 1px solid #3d484d; border: 1px solid ${border_strong};
} }
QCheckBox::indicator { QCheckBox::indicator {
border-radius: 3px; border-radius: 3px;
@ -356,23 +363,23 @@ QRadioButton::indicator {
border-radius: 7px; border-radius: 7px;
} }
QCheckBox::indicator:hover, QRadioButton::indicator:hover { QCheckBox::indicator:hover, QRadioButton::indicator:hover {
border-color: #a7c080; border-color: ${accent};
} }
QCheckBox::indicator:checked, QRadioButton::indicator:checked { QCheckBox::indicator:checked, QRadioButton::indicator:checked {
background-color: #a7c080; background-color: ${accent};
border-color: #a7c080; border-color: ${accent};
} }
QCheckBox::indicator:disabled, QRadioButton::indicator:disabled { QCheckBox::indicator:disabled, QRadioButton::indicator:disabled {
background-color: #232a2e; background-color: ${bg_alt};
border-color: #343f44; border-color: ${border};
} }
/* ---------- Tooltips ---------- */ /* ---------- Tooltips ---------- */
QToolTip { QToolTip {
background-color: #343f44; background-color: ${bg_subtle};
color: #d3c6aa; color: ${text};
border: 1px solid #3d484d; border: 1px solid ${border_strong};
padding: 4px 6px; padding: 4px 6px;
border-radius: 3px; border-radius: 3px;
} }
@ -380,12 +387,12 @@ QToolTip {
/* ---------- Item views (lists, trees, tables) ---------- */ /* ---------- Item views (lists, trees, tables) ---------- */
QListView, QListWidget, QTreeView, QTreeWidget, QTableView, QTableWidget { QListView, QListWidget, QTreeView, QTreeWidget, QTableView, QTableWidget {
background-color: #2d353b; background-color: ${bg};
alternate-background-color: #232a2e; alternate-background-color: ${bg_alt};
color: #d3c6aa; color: ${text};
border: 1px solid #343f44; border: 1px solid ${border};
selection-background-color: #a7c080; selection-background-color: ${accent};
selection-color: #2d353b; selection-color: ${accent_text};
outline: none; outline: none;
} }
QListView::item, QListWidget::item, QListView::item, QListWidget::item,
@ -396,57 +403,57 @@ QTableView::item, QTableWidget::item {
QListView::item:hover, QListWidget::item:hover, QListView::item:hover, QListWidget::item:hover,
QTreeView::item:hover, QTreeWidget::item:hover, QTreeView::item:hover, QTreeWidget::item:hover,
QTableView::item:hover, QTableWidget::item:hover { QTableView::item:hover, QTableWidget::item:hover {
background-color: #3d484d; background-color: ${bg_hover};
} }
QListView::item:selected, QListWidget::item:selected, QListView::item:selected, QListWidget::item:selected,
QTreeView::item:selected, QTreeWidget::item:selected, QTreeView::item:selected, QTreeWidget::item:selected,
QTableView::item:selected, QTableWidget::item:selected { QTableView::item:selected, QTableWidget::item:selected {
background-color: #a7c080; background-color: ${accent};
color: #2d353b; color: ${accent_text};
} }
QHeaderView::section { QHeaderView::section {
background-color: #343f44; background-color: ${bg_subtle};
color: #d3c6aa; color: ${text};
border: none; border: none;
border-right: 1px solid #343f44; border-right: 1px solid ${border};
padding: 4px 8px; padding: 4px 8px;
} }
QHeaderView::section:hover { QHeaderView::section:hover {
background-color: #3d484d; background-color: ${bg_hover};
} }
/* ---------- Tabs ---------- */ /* ---------- Tabs ---------- */
QTabWidget::pane { QTabWidget::pane {
border: 1px solid #343f44; border: 1px solid ${border};
top: -1px; top: -1px;
} }
QTabBar::tab { QTabBar::tab {
background: #343f44; background: ${bg_subtle};
color: #9da9a0; color: ${text_dim};
border: 1px solid #343f44; border: 1px solid ${border};
border-bottom: none; border-bottom: none;
padding: 6px 14px; padding: 6px 14px;
border-top-left-radius: 4px; border-top-left-radius: 4px;
border-top-right-radius: 4px; border-top-right-radius: 4px;
} }
QTabBar::tab:selected { QTabBar::tab:selected {
background: #2d353b; background: ${bg};
color: #d3c6aa; color: ${text};
border-color: #3d484d; border-color: ${border_strong};
} }
QTabBar::tab:hover:!selected { QTabBar::tab:hover:!selected {
background: #3d484d; background: ${bg_hover};
color: #d3c6aa; color: ${text};
} }
/* ---------- Group boxes ---------- */ /* ---------- Group boxes ---------- */
QGroupBox { QGroupBox {
background: transparent; background: transparent;
color: #d3c6aa; color: ${text};
border: 1px solid #343f44; border: 1px solid ${border};
border-radius: 4px; border-radius: 4px;
margin-top: 10px; margin-top: 10px;
padding-top: 8px; padding-top: 8px;
@ -455,27 +462,27 @@ QGroupBox::title {
subcontrol-origin: margin; subcontrol-origin: margin;
subcontrol-position: top left; subcontrol-position: top left;
padding: 0 6px; padding: 0 6px;
color: #9da9a0; color: ${text_dim};
} }
/* ---------- Frames ---------- */ /* ---------- Frames ---------- */
QFrame[frameShape="4"], /* HLine */ QFrame[frameShape="4"], /* HLine */
QFrame[frameShape="5"] /* VLine */ { QFrame[frameShape="5"] /* VLine */ {
background: #343f44; background: ${border};
color: #343f44; color: ${border};
} }
/* ---------- Toolbars ---------- */ /* ---------- Toolbars ---------- */
QToolBar { QToolBar {
background: #2d353b; background: ${bg};
border: none; border: none;
spacing: 4px; spacing: 4px;
padding: 2px; padding: 2px;
} }
QToolBar::separator { QToolBar::separator {
background: #343f44; background: ${border};
width: 1px; width: 1px;
margin: 4px 4px; margin: 4px 4px;
} }
@ -483,31 +490,25 @@ QToolBar::separator {
/* ---------- Dock widgets ---------- */ /* ---------- Dock widgets ---------- */
QDockWidget { QDockWidget {
color: #d3c6aa; color: ${text};
titlebar-close-icon: none; titlebar-close-icon: none;
} }
QDockWidget::title { QDockWidget::title {
background: #343f44; background: ${bg_subtle};
padding: 4px; padding: 4px;
border: 1px solid #343f44; border: 1px solid ${border};
} }
/* ---------- Rubber band (multi-select drag rectangle) ---------- */ /* ---------- Rubber band (multi-select drag rectangle) ---------- */
QRubberBand { QRubberBand {
background: #a7c080; background: ${accent};
border: 1px solid #a7c080; border: 1px solid ${accent};
/* Qt blends rubber band at ~30% so this reads as a translucent /* Qt blends rubber band at ~30% so this reads as a translucent
* accent-tinted rectangle without needing rgba(). */ * accent-tinted rectangle without needing rgba(). */
} }
/* ---------- Popout & preview overlay controls ---------- */
QWidget#_preview_controls,
QWidget#_slideshow_toolbar,
QWidget#_slideshow_controls {
background: rgba(0, 0, 0, 160);
}
/* ---------- Thumbnail dot indicators (Qt properties on ThumbnailWidget) ---------- */ /* ---------- Thumbnail dot indicators (Qt properties on ThumbnailWidget) ---------- */
@ -519,10 +520,78 @@ ThumbnailWidget {
/* ---------- Info panel tag category colors ---------- */ /* ---------- Info panel tag category colors ---------- */
InfoPanel { InfoPanel {
qproperty-tagArtistColor: #dbbc7f; qproperty-tagArtistColor: ${warning};
qproperty-tagCharacterColor: #a7c080; qproperty-tagCharacterColor: ${success};
qproperty-tagCopyrightColor: #a7c080; qproperty-tagCopyrightColor: ${accent};
qproperty-tagSpeciesColor: #e67e80; qproperty-tagSpeciesColor: ${danger};
qproperty-tagMetaColor: #9da9a0; qproperty-tagMetaColor: ${text_dim};
qproperty-tagLoreColor: #9da9a0; qproperty-tagLoreColor: ${text_dim};
}
/* ---------- Video player letterbox / pillarbox color (mpv background) ---------- */
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.
*/
QWidget#_slideshow_toolbar,
QWidget#_slideshow_controls,
QWidget#_preview_controls {
background: ${overlay_bg};
}
QWidget#_slideshow_toolbar *,
QWidget#_slideshow_controls *,
QWidget#_preview_controls * {
background: transparent;
color: white;
border: none;
}
QWidget#_slideshow_toolbar QPushButton,
QWidget#_slideshow_controls QPushButton,
QWidget#_preview_controls QPushButton {
background: transparent;
color: white;
border: 1px solid rgba(255, 255, 255, 80);
padding: 2px 6px;
}
QWidget#_slideshow_toolbar QPushButton:hover,
QWidget#_slideshow_controls QPushButton:hover,
QWidget#_preview_controls QPushButton:hover {
background: rgba(255, 255, 255, 30);
}
QWidget#_slideshow_toolbar QSlider::groove:horizontal,
QWidget#_slideshow_controls QSlider::groove:horizontal,
QWidget#_preview_controls QSlider::groove:horizontal {
background: rgba(255, 255, 255, 40);
height: 4px;
}
QWidget#_slideshow_toolbar QSlider::handle:horizontal,
QWidget#_slideshow_controls QSlider::handle:horizontal,
QWidget#_preview_controls QSlider::handle:horizontal {
background: ${accent};
width: 10px;
margin: -4px 0;
}
QWidget#_slideshow_toolbar QSlider::sub-page:horizontal,
QWidget#_slideshow_controls QSlider::sub-page:horizontal,
QWidget#_preview_controls QSlider::sub-page:horizontal {
background: ${accent};
}
QWidget#_slideshow_toolbar QLabel,
QWidget#_slideshow_controls QLabel,
QWidget#_preview_controls QLabel {
background: transparent;
color: white;
} }

View File

@ -1,41 +1,46 @@
/* booru-viewer Gruvbox Dark /* booru-viewer Gruvbox Dark
* *
* Comprehensive Fusion-style QSS. Mimics the visual feel of Qt's Fusion * Edit the @palette block below to recolor this theme. The body uses
* style + a dark KDE color scheme: subtle borders, modest corner radius, * ${...} placeholders that the app's _load_user_qss preprocessor
* clear hover/pressed/focus states, transparent labels. * substitutes at load time. See themes/README.md for the full list of
* placeholder names and what each one is used for.
* *
* Palette (edit these and the rest of the file together if you fork): * The same dialect works in any custom.qss you put in your data dir.
* bg #282828 */
* bg_alt #1d2021
* bg_subtle #3c3836 /* @palette
* bg_hover #504945 bg: #282828
* bg_active #665c54 bg_alt: #1d2021
* text #ebdbb2 bg_subtle: #3c3836
* text_dim #d5c4a1 bg_hover: #504945
* text_disabled #928374 bg_active: #665c54
* border #3c3836 text: #ebdbb2
* border_strong #504945 text_dim: #d5c4a1
* accent #d79921 text_disabled: #928374
* accent_text #282828 border: #3c3836
* accent_dim #fabd2f border_strong: #504945
* link #83a598 accent: #d79921
* danger #fb4934 accent_text: #282828
* success #b8bb26 accent_dim: #fabd2f
* warning #fabd2f link: #83a598
danger: #fb4934
success: #b8bb26
warning: #fabd2f
overlay_bg: rgba(40, 40, 40, 200)
*/ */
/* ---------- Base ---------- */ /* ---------- Base ---------- */
QWidget { QWidget {
background-color: #282828; background-color: ${bg};
color: #ebdbb2; color: ${text};
font-size: 13px; font-size: 13px;
selection-background-color: #d79921; selection-background-color: ${accent};
selection-color: #282828; selection-color: ${accent_text};
} }
QWidget:disabled { QWidget:disabled {
color: #928374; color: ${text_disabled};
} }
/* Labels should never paint an opaque background they sit on top of /* Labels should never paint an opaque background they sit on top of
@ -45,115 +50,120 @@ QLabel {
} }
QMainWindow, QDialog { QMainWindow, QDialog {
background-color: #282828; background-color: ${bg};
} }
/* ---------- Buttons ---------- */ /* ---------- Buttons ---------- */
QPushButton { QPushButton {
background-color: #3c3836; background-color: ${bg_subtle};
color: #ebdbb2; color: ${text};
border: 1px solid #504945; border: 1px solid ${border_strong};
border-radius: 4px; border-radius: 4px;
padding: 5px 12px; padding: 5px 12px;
min-height: 18px; min-height: 18px;
} }
QPushButton:hover { QPushButton:hover {
background-color: #504945; background-color: ${bg_hover};
border-color: #d79921; border-color: ${accent};
} }
QPushButton:pressed { QPushButton:pressed {
background-color: #665c54; background-color: ${bg_active};
} }
QPushButton:checked { QPushButton:checked {
background-color: #d79921; background-color: ${accent};
color: #282828; color: ${accent_text};
border-color: #d79921; border-color: ${accent};
} }
QPushButton:checked:hover { QPushButton:checked:hover {
background-color: #fabd2f; background-color: ${accent_dim};
border-color: #fabd2f; border-color: ${accent_dim};
} }
QPushButton:disabled { QPushButton:disabled {
background-color: #1d2021; background-color: ${bg_alt};
color: #928374; color: ${text_disabled};
border-color: #3c3836; border-color: ${border};
} }
QPushButton:flat { QPushButton:flat {
background: transparent; background: transparent;
border: none; border: none;
} }
QPushButton:flat:hover { QPushButton:flat:hover {
background-color: #504945; background-color: ${bg_hover};
} }
QToolButton { QToolButton {
background-color: transparent; background-color: transparent;
color: #ebdbb2; color: ${text};
border: 1px solid transparent; border: 1px solid transparent;
border-radius: 4px; border-radius: 4px;
padding: 4px; padding: 4px;
} }
QToolButton:hover { QToolButton:hover {
background-color: #504945; background-color: ${bg_hover};
border-color: #504945; border-color: ${border_strong};
} }
QToolButton:pressed, QToolButton:checked { QToolButton:pressed, QToolButton:checked {
background-color: #665c54; background-color: ${bg_active};
} }
/* ---------- Inputs ---------- */ /* ---------- Inputs ---------- */
QLineEdit, QSpinBox, QDoubleSpinBox, QTextEdit, QPlainTextEdit { QLineEdit, QSpinBox, QDoubleSpinBox, QTextEdit, QPlainTextEdit {
background-color: #3c3836; background-color: ${bg_subtle};
color: #ebdbb2; color: ${text};
border: 1px solid #504945; border: 1px solid ${border_strong};
border-radius: 4px; border-radius: 4px;
padding: 4px 8px; padding: 4px 8px;
selection-background-color: #d79921; /* min-height ensures the painted text fits inside the widget bounds
selection-color: #282828; * 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: 20px;
selection-background-color: ${accent};
selection-color: ${accent_text};
} }
QLineEdit:focus, QLineEdit:focus,
QSpinBox:focus, QSpinBox:focus,
QDoubleSpinBox:focus, QDoubleSpinBox:focus,
QTextEdit:focus, QTextEdit:focus,
QPlainTextEdit:focus { QPlainTextEdit:focus {
border-color: #d79921; border-color: ${accent};
} }
QLineEdit:disabled, QLineEdit:disabled,
QSpinBox:disabled, QSpinBox:disabled,
QDoubleSpinBox:disabled, QDoubleSpinBox:disabled,
QTextEdit:disabled, QTextEdit:disabled,
QPlainTextEdit:disabled { QPlainTextEdit:disabled {
background-color: #1d2021; background-color: ${bg_alt};
color: #928374; color: ${text_disabled};
border-color: #3c3836; border-color: ${border};
} }
QComboBox { QComboBox {
background-color: #3c3836; background-color: ${bg_subtle};
color: #ebdbb2; color: ${text};
border: 1px solid #504945; border: 1px solid ${border_strong};
border-radius: 4px; border-radius: 4px;
padding: 4px 8px; padding: 4px 8px;
min-height: 18px; min-height: 20px;
} }
QComboBox:hover { QComboBox:hover {
border-color: #d79921; border-color: ${accent};
} }
QComboBox:focus { QComboBox:focus {
border-color: #d79921; border-color: ${accent};
} }
QComboBox::drop-down { QComboBox::drop-down {
border: none; border: none;
width: 18px; width: 18px;
} }
QComboBox QAbstractItemView { QComboBox QAbstractItemView {
background-color: #3c3836; background-color: ${bg_subtle};
color: #ebdbb2; color: ${text};
border: 1px solid #504945; border: 1px solid ${border_strong};
selection-background-color: #d79921; selection-background-color: ${accent};
selection-color: #282828; selection-color: ${accent_text};
outline: none; outline: none;
padding: 2px; padding: 2px;
} }
@ -161,19 +171,19 @@ QComboBox QAbstractItemView {
/* ---------- Scrollbars ---------- */ /* ---------- Scrollbars ---------- */
QScrollBar:vertical { QScrollBar:vertical {
background: #282828; background: ${bg};
width: 10px; width: 10px;
border: none; border: none;
margin: 0; margin: 0;
} }
QScrollBar::handle:vertical { QScrollBar::handle:vertical {
background: #504945; background: ${bg_hover};
border-radius: 4px; border-radius: 4px;
min-height: 24px; min-height: 24px;
margin: 1px; margin: 1px;
} }
QScrollBar::handle:vertical:hover { QScrollBar::handle:vertical:hover {
background: #665c54; background: ${bg_active};
} }
QScrollBar::add-line:vertical, QScrollBar::add-line:vertical,
QScrollBar::sub-line:vertical { QScrollBar::sub-line:vertical {
@ -186,19 +196,19 @@ QScrollBar::sub-page:vertical {
} }
QScrollBar:horizontal { QScrollBar:horizontal {
background: #282828; background: ${bg};
height: 10px; height: 10px;
border: none; border: none;
margin: 0; margin: 0;
} }
QScrollBar::handle:horizontal { QScrollBar::handle:horizontal {
background: #504945; background: ${bg_hover};
border-radius: 4px; border-radius: 4px;
min-width: 24px; min-width: 24px;
margin: 1px; margin: 1px;
} }
QScrollBar::handle:horizontal:hover { QScrollBar::handle:horizontal:hover {
background: #665c54; background: ${bg_active};
} }
QScrollBar::add-line:horizontal, QScrollBar::add-line:horizontal,
QScrollBar::sub-line:horizontal { QScrollBar::sub-line:horizontal {
@ -218,26 +228,26 @@ QScrollArea {
/* ---------- Menus ---------- */ /* ---------- Menus ---------- */
QMenuBar { QMenuBar {
background-color: #282828; background-color: ${bg};
color: #ebdbb2; color: ${text};
border-bottom: 1px solid #3c3836; border-bottom: 1px solid ${border};
} }
QMenuBar::item { QMenuBar::item {
background: transparent; background: transparent;
padding: 4px 10px; padding: 4px 10px;
} }
QMenuBar::item:selected { QMenuBar::item:selected {
background-color: #504945; background-color: ${bg_hover};
color: #ebdbb2; color: ${text};
} }
QMenuBar::item:pressed { QMenuBar::item:pressed {
background-color: #665c54; background-color: ${bg_active};
} }
QMenu { QMenu {
background-color: #3c3836; background-color: ${bg_subtle};
color: #ebdbb2; color: ${text};
border: 1px solid #504945; border: 1px solid ${border_strong};
padding: 4px 0; padding: 4px 0;
} }
QMenu::item { QMenu::item {
@ -245,15 +255,15 @@ QMenu::item {
padding: 5px 24px 5px 24px; padding: 5px 24px 5px 24px;
} }
QMenu::item:selected { QMenu::item:selected {
background-color: #d79921; background-color: ${accent};
color: #282828; color: ${accent_text};
} }
QMenu::item:disabled { QMenu::item:disabled {
color: #928374; color: ${text_disabled};
} }
QMenu::separator { QMenu::separator {
height: 1px; height: 1px;
background: #3c3836; background: ${border};
margin: 4px 8px; margin: 4px 8px;
} }
QMenu::icon { QMenu::icon {
@ -263,9 +273,9 @@ QMenu::icon {
/* ---------- Status bar ---------- */ /* ---------- Status bar ---------- */
QStatusBar { QStatusBar {
background-color: #282828; background-color: ${bg};
color: #d5c4a1; color: ${text_dim};
border-top: 1px solid #3c3836; border-top: 1px solid ${border};
} }
QStatusBar::item { QStatusBar::item {
border: none; border: none;
@ -274,7 +284,7 @@ QStatusBar::item {
/* ---------- Splitters ---------- */ /* ---------- Splitters ---------- */
QSplitter::handle { QSplitter::handle {
background: #3c3836; background: ${border};
} }
QSplitter::handle:horizontal { QSplitter::handle:horizontal {
width: 2px; width: 2px;
@ -283,38 +293,35 @@ QSplitter::handle:vertical {
height: 2px; height: 2px;
} }
QSplitter::handle:hover { QSplitter::handle:hover {
background: #d79921; background: ${accent};
} }
/* ---------- Sliders ---------- */ /* ---------- Sliders ---------- */
QSlider::groove:horizontal { QSlider::groove:horizontal {
background: #3c3836; background: ${bg_subtle};
height: 4px; height: 4px;
border-radius: 2px;
} }
QSlider::sub-page:horizontal { QSlider::sub-page:horizontal {
background: #d79921; background: ${accent};
border-radius: 2px;
} }
QSlider::handle:horizontal { QSlider::handle:horizontal {
background: #d79921; background: ${accent};
width: 12px; width: 12px;
height: 12px; height: 12px;
margin: -5px 0; margin: -5px 0;
border-radius: 6px;
} }
QSlider::handle:horizontal:hover { QSlider::handle:horizontal:hover {
background: #fabd2f; background: ${accent_dim};
} }
QSlider::groove:vertical { QSlider::groove:vertical {
background: #3c3836; background: ${bg_subtle};
width: 4px; width: 4px;
border-radius: 2px; border-radius: 2px;
} }
QSlider::handle:vertical { QSlider::handle:vertical {
background: #d79921; background: ${accent};
width: 12px; width: 12px;
height: 12px; height: 12px;
margin: 0 -5px; margin: 0 -5px;
@ -324,15 +331,15 @@ QSlider::handle:vertical {
/* ---------- Progress ---------- */ /* ---------- Progress ---------- */
QProgressBar { QProgressBar {
background-color: #3c3836; background-color: ${bg_subtle};
color: #ebdbb2; color: ${text};
border: 1px solid #3c3836; border: 1px solid ${border};
border-radius: 3px; border-radius: 3px;
text-align: center; text-align: center;
height: 6px; height: 6px;
} }
QProgressBar::chunk { QProgressBar::chunk {
background-color: #d79921; background-color: ${accent};
border-radius: 3px; border-radius: 3px;
} }
@ -340,14 +347,14 @@ QProgressBar::chunk {
QCheckBox, QRadioButton { QCheckBox, QRadioButton {
background: transparent; background: transparent;
color: #ebdbb2; color: ${text};
spacing: 6px; spacing: 6px;
} }
QCheckBox::indicator, QRadioButton::indicator { QCheckBox::indicator, QRadioButton::indicator {
width: 14px; width: 14px;
height: 14px; height: 14px;
background-color: #3c3836; background-color: ${bg_subtle};
border: 1px solid #504945; border: 1px solid ${border_strong};
} }
QCheckBox::indicator { QCheckBox::indicator {
border-radius: 3px; border-radius: 3px;
@ -356,23 +363,23 @@ QRadioButton::indicator {
border-radius: 7px; border-radius: 7px;
} }
QCheckBox::indicator:hover, QRadioButton::indicator:hover { QCheckBox::indicator:hover, QRadioButton::indicator:hover {
border-color: #d79921; border-color: ${accent};
} }
QCheckBox::indicator:checked, QRadioButton::indicator:checked { QCheckBox::indicator:checked, QRadioButton::indicator:checked {
background-color: #d79921; background-color: ${accent};
border-color: #d79921; border-color: ${accent};
} }
QCheckBox::indicator:disabled, QRadioButton::indicator:disabled { QCheckBox::indicator:disabled, QRadioButton::indicator:disabled {
background-color: #1d2021; background-color: ${bg_alt};
border-color: #3c3836; border-color: ${border};
} }
/* ---------- Tooltips ---------- */ /* ---------- Tooltips ---------- */
QToolTip { QToolTip {
background-color: #3c3836; background-color: ${bg_subtle};
color: #ebdbb2; color: ${text};
border: 1px solid #504945; border: 1px solid ${border_strong};
padding: 4px 6px; padding: 4px 6px;
border-radius: 3px; border-radius: 3px;
} }
@ -380,12 +387,12 @@ QToolTip {
/* ---------- Item views (lists, trees, tables) ---------- */ /* ---------- Item views (lists, trees, tables) ---------- */
QListView, QListWidget, QTreeView, QTreeWidget, QTableView, QTableWidget { QListView, QListWidget, QTreeView, QTreeWidget, QTableView, QTableWidget {
background-color: #282828; background-color: ${bg};
alternate-background-color: #1d2021; alternate-background-color: ${bg_alt};
color: #ebdbb2; color: ${text};
border: 1px solid #3c3836; border: 1px solid ${border};
selection-background-color: #d79921; selection-background-color: ${accent};
selection-color: #282828; selection-color: ${accent_text};
outline: none; outline: none;
} }
QListView::item, QListWidget::item, QListView::item, QListWidget::item,
@ -396,57 +403,57 @@ QTableView::item, QTableWidget::item {
QListView::item:hover, QListWidget::item:hover, QListView::item:hover, QListWidget::item:hover,
QTreeView::item:hover, QTreeWidget::item:hover, QTreeView::item:hover, QTreeWidget::item:hover,
QTableView::item:hover, QTableWidget::item:hover { QTableView::item:hover, QTableWidget::item:hover {
background-color: #504945; background-color: ${bg_hover};
} }
QListView::item:selected, QListWidget::item:selected, QListView::item:selected, QListWidget::item:selected,
QTreeView::item:selected, QTreeWidget::item:selected, QTreeView::item:selected, QTreeWidget::item:selected,
QTableView::item:selected, QTableWidget::item:selected { QTableView::item:selected, QTableWidget::item:selected {
background-color: #d79921; background-color: ${accent};
color: #282828; color: ${accent_text};
} }
QHeaderView::section { QHeaderView::section {
background-color: #3c3836; background-color: ${bg_subtle};
color: #ebdbb2; color: ${text};
border: none; border: none;
border-right: 1px solid #3c3836; border-right: 1px solid ${border};
padding: 4px 8px; padding: 4px 8px;
} }
QHeaderView::section:hover { QHeaderView::section:hover {
background-color: #504945; background-color: ${bg_hover};
} }
/* ---------- Tabs ---------- */ /* ---------- Tabs ---------- */
QTabWidget::pane { QTabWidget::pane {
border: 1px solid #3c3836; border: 1px solid ${border};
top: -1px; top: -1px;
} }
QTabBar::tab { QTabBar::tab {
background: #3c3836; background: ${bg_subtle};
color: #d5c4a1; color: ${text_dim};
border: 1px solid #3c3836; border: 1px solid ${border};
border-bottom: none; border-bottom: none;
padding: 6px 14px; padding: 6px 14px;
border-top-left-radius: 4px; border-top-left-radius: 4px;
border-top-right-radius: 4px; border-top-right-radius: 4px;
} }
QTabBar::tab:selected { QTabBar::tab:selected {
background: #282828; background: ${bg};
color: #ebdbb2; color: ${text};
border-color: #504945; border-color: ${border_strong};
} }
QTabBar::tab:hover:!selected { QTabBar::tab:hover:!selected {
background: #504945; background: ${bg_hover};
color: #ebdbb2; color: ${text};
} }
/* ---------- Group boxes ---------- */ /* ---------- Group boxes ---------- */
QGroupBox { QGroupBox {
background: transparent; background: transparent;
color: #ebdbb2; color: ${text};
border: 1px solid #3c3836; border: 1px solid ${border};
border-radius: 4px; border-radius: 4px;
margin-top: 10px; margin-top: 10px;
padding-top: 8px; padding-top: 8px;
@ -455,27 +462,27 @@ QGroupBox::title {
subcontrol-origin: margin; subcontrol-origin: margin;
subcontrol-position: top left; subcontrol-position: top left;
padding: 0 6px; padding: 0 6px;
color: #d5c4a1; color: ${text_dim};
} }
/* ---------- Frames ---------- */ /* ---------- Frames ---------- */
QFrame[frameShape="4"], /* HLine */ QFrame[frameShape="4"], /* HLine */
QFrame[frameShape="5"] /* VLine */ { QFrame[frameShape="5"] /* VLine */ {
background: #3c3836; background: ${border};
color: #3c3836; color: ${border};
} }
/* ---------- Toolbars ---------- */ /* ---------- Toolbars ---------- */
QToolBar { QToolBar {
background: #282828; background: ${bg};
border: none; border: none;
spacing: 4px; spacing: 4px;
padding: 2px; padding: 2px;
} }
QToolBar::separator { QToolBar::separator {
background: #3c3836; background: ${border};
width: 1px; width: 1px;
margin: 4px 4px; margin: 4px 4px;
} }
@ -483,31 +490,25 @@ QToolBar::separator {
/* ---------- Dock widgets ---------- */ /* ---------- Dock widgets ---------- */
QDockWidget { QDockWidget {
color: #ebdbb2; color: ${text};
titlebar-close-icon: none; titlebar-close-icon: none;
} }
QDockWidget::title { QDockWidget::title {
background: #3c3836; background: ${bg_subtle};
padding: 4px; padding: 4px;
border: 1px solid #3c3836; border: 1px solid ${border};
} }
/* ---------- Rubber band (multi-select drag rectangle) ---------- */ /* ---------- Rubber band (multi-select drag rectangle) ---------- */
QRubberBand { QRubberBand {
background: #d79921; background: ${accent};
border: 1px solid #d79921; border: 1px solid ${accent};
/* Qt blends rubber band at ~30% so this reads as a translucent /* Qt blends rubber band at ~30% so this reads as a translucent
* accent-tinted rectangle without needing rgba(). */ * accent-tinted rectangle without needing rgba(). */
} }
/* ---------- Popout & preview overlay controls ---------- */
QWidget#_preview_controls,
QWidget#_slideshow_toolbar,
QWidget#_slideshow_controls {
background: rgba(0, 0, 0, 160);
}
/* ---------- Thumbnail dot indicators (Qt properties on ThumbnailWidget) ---------- */ /* ---------- Thumbnail dot indicators (Qt properties on ThumbnailWidget) ---------- */
@ -519,10 +520,78 @@ ThumbnailWidget {
/* ---------- Info panel tag category colors ---------- */ /* ---------- Info panel tag category colors ---------- */
InfoPanel { InfoPanel {
qproperty-tagArtistColor: #fabd2f; qproperty-tagArtistColor: ${warning};
qproperty-tagCharacterColor: #b8bb26; qproperty-tagCharacterColor: ${success};
qproperty-tagCopyrightColor: #d79921; qproperty-tagCopyrightColor: ${accent};
qproperty-tagSpeciesColor: #fb4934; qproperty-tagSpeciesColor: ${danger};
qproperty-tagMetaColor: #d5c4a1; qproperty-tagMetaColor: ${text_dim};
qproperty-tagLoreColor: #d5c4a1; qproperty-tagLoreColor: ${text_dim};
}
/* ---------- Video player letterbox / pillarbox color (mpv background) ---------- */
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.
*/
QWidget#_slideshow_toolbar,
QWidget#_slideshow_controls,
QWidget#_preview_controls {
background: ${overlay_bg};
}
QWidget#_slideshow_toolbar *,
QWidget#_slideshow_controls *,
QWidget#_preview_controls * {
background: transparent;
color: white;
border: none;
}
QWidget#_slideshow_toolbar QPushButton,
QWidget#_slideshow_controls QPushButton,
QWidget#_preview_controls QPushButton {
background: transparent;
color: white;
border: 1px solid rgba(255, 255, 255, 80);
padding: 2px 6px;
}
QWidget#_slideshow_toolbar QPushButton:hover,
QWidget#_slideshow_controls QPushButton:hover,
QWidget#_preview_controls QPushButton:hover {
background: rgba(255, 255, 255, 30);
}
QWidget#_slideshow_toolbar QSlider::groove:horizontal,
QWidget#_slideshow_controls QSlider::groove:horizontal,
QWidget#_preview_controls QSlider::groove:horizontal {
background: rgba(255, 255, 255, 40);
height: 4px;
}
QWidget#_slideshow_toolbar QSlider::handle:horizontal,
QWidget#_slideshow_controls QSlider::handle:horizontal,
QWidget#_preview_controls QSlider::handle:horizontal {
background: ${accent};
width: 10px;
margin: -4px 0;
}
QWidget#_slideshow_toolbar QSlider::sub-page:horizontal,
QWidget#_slideshow_controls QSlider::sub-page:horizontal,
QWidget#_preview_controls QSlider::sub-page:horizontal {
background: ${accent};
}
QWidget#_slideshow_toolbar QLabel,
QWidget#_slideshow_controls QLabel,
QWidget#_preview_controls QLabel {
background: transparent;
color: white;
} }

View File

@ -1,41 +1,46 @@
/* booru-viewer Nord /* booru-viewer Nord
* *
* Comprehensive Fusion-style QSS. Mimics the visual feel of Qt's Fusion * Edit the @palette block below to recolor this theme. The body uses
* style + a dark KDE color scheme: subtle borders, modest corner radius, * ${...} placeholders that the app's _load_user_qss preprocessor
* clear hover/pressed/focus states, transparent labels. * substitutes at load time. See themes/README.md for the full list of
* placeholder names and what each one is used for.
* *
* Palette (edit these and the rest of the file together if you fork): * The same dialect works in any custom.qss you put in your data dir.
* bg #2e3440 */
* bg_alt #272b36
* bg_subtle #3b4252 /* @palette
* bg_hover #434c5e bg: #2e3440
* bg_active #4c566a bg_alt: #272b36
* text #eceff4 bg_subtle: #3b4252
* text_dim #d8dee9 bg_hover: #434c5e
* text_disabled #4c566a bg_active: #4c566a
* border #3b4252 text: #eceff4
* border_strong #4c566a text_dim: #d8dee9
* accent #88c0d0 text_disabled: #4c566a
* accent_text #2e3440 border: #3b4252
* accent_dim #81a1c1 border_strong: #4c566a
* link #8fbcbb accent: #88c0d0
* danger #bf616a accent_text: #2e3440
* success #a3be8c accent_dim: #81a1c1
* warning #ebcb8b link: #8fbcbb
danger: #bf616a
success: #a3be8c
warning: #ebcb8b
overlay_bg: rgba(46, 52, 64, 200)
*/ */
/* ---------- Base ---------- */ /* ---------- Base ---------- */
QWidget { QWidget {
background-color: #2e3440; background-color: ${bg};
color: #eceff4; color: ${text};
font-size: 13px; font-size: 13px;
selection-background-color: #88c0d0; selection-background-color: ${accent};
selection-color: #2e3440; selection-color: ${accent_text};
} }
QWidget:disabled { QWidget:disabled {
color: #4c566a; color: ${text_disabled};
} }
/* Labels should never paint an opaque background they sit on top of /* Labels should never paint an opaque background they sit on top of
@ -45,115 +50,120 @@ QLabel {
} }
QMainWindow, QDialog { QMainWindow, QDialog {
background-color: #2e3440; background-color: ${bg};
} }
/* ---------- Buttons ---------- */ /* ---------- Buttons ---------- */
QPushButton { QPushButton {
background-color: #3b4252; background-color: ${bg_subtle};
color: #eceff4; color: ${text};
border: 1px solid #4c566a; border: 1px solid ${border_strong};
border-radius: 4px; border-radius: 4px;
padding: 5px 12px; padding: 5px 12px;
min-height: 18px; min-height: 18px;
} }
QPushButton:hover { QPushButton:hover {
background-color: #434c5e; background-color: ${bg_hover};
border-color: #88c0d0; border-color: ${accent};
} }
QPushButton:pressed { QPushButton:pressed {
background-color: #4c566a; background-color: ${bg_active};
} }
QPushButton:checked { QPushButton:checked {
background-color: #88c0d0; background-color: ${accent};
color: #2e3440; color: ${accent_text};
border-color: #88c0d0; border-color: ${accent};
} }
QPushButton:checked:hover { QPushButton:checked:hover {
background-color: #81a1c1; background-color: ${accent_dim};
border-color: #81a1c1; border-color: ${accent_dim};
} }
QPushButton:disabled { QPushButton:disabled {
background-color: #272b36; background-color: ${bg_alt};
color: #4c566a; color: ${text_disabled};
border-color: #3b4252; border-color: ${border};
} }
QPushButton:flat { QPushButton:flat {
background: transparent; background: transparent;
border: none; border: none;
} }
QPushButton:flat:hover { QPushButton:flat:hover {
background-color: #434c5e; background-color: ${bg_hover};
} }
QToolButton { QToolButton {
background-color: transparent; background-color: transparent;
color: #eceff4; color: ${text};
border: 1px solid transparent; border: 1px solid transparent;
border-radius: 4px; border-radius: 4px;
padding: 4px; padding: 4px;
} }
QToolButton:hover { QToolButton:hover {
background-color: #434c5e; background-color: ${bg_hover};
border-color: #4c566a; border-color: ${border_strong};
} }
QToolButton:pressed, QToolButton:checked { QToolButton:pressed, QToolButton:checked {
background-color: #4c566a; background-color: ${bg_active};
} }
/* ---------- Inputs ---------- */ /* ---------- Inputs ---------- */
QLineEdit, QSpinBox, QDoubleSpinBox, QTextEdit, QPlainTextEdit { QLineEdit, QSpinBox, QDoubleSpinBox, QTextEdit, QPlainTextEdit {
background-color: #3b4252; background-color: ${bg_subtle};
color: #eceff4; color: ${text};
border: 1px solid #4c566a; border: 1px solid ${border_strong};
border-radius: 4px; border-radius: 4px;
padding: 4px 8px; padding: 4px 8px;
selection-background-color: #88c0d0; /* min-height ensures the painted text fits inside the widget bounds
selection-color: #2e3440; * 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: 20px;
selection-background-color: ${accent};
selection-color: ${accent_text};
} }
QLineEdit:focus, QLineEdit:focus,
QSpinBox:focus, QSpinBox:focus,
QDoubleSpinBox:focus, QDoubleSpinBox:focus,
QTextEdit:focus, QTextEdit:focus,
QPlainTextEdit:focus { QPlainTextEdit:focus {
border-color: #88c0d0; border-color: ${accent};
} }
QLineEdit:disabled, QLineEdit:disabled,
QSpinBox:disabled, QSpinBox:disabled,
QDoubleSpinBox:disabled, QDoubleSpinBox:disabled,
QTextEdit:disabled, QTextEdit:disabled,
QPlainTextEdit:disabled { QPlainTextEdit:disabled {
background-color: #272b36; background-color: ${bg_alt};
color: #4c566a; color: ${text_disabled};
border-color: #3b4252; border-color: ${border};
} }
QComboBox { QComboBox {
background-color: #3b4252; background-color: ${bg_subtle};
color: #eceff4; color: ${text};
border: 1px solid #4c566a; border: 1px solid ${border_strong};
border-radius: 4px; border-radius: 4px;
padding: 4px 8px; padding: 4px 8px;
min-height: 18px; min-height: 20px;
} }
QComboBox:hover { QComboBox:hover {
border-color: #88c0d0; border-color: ${accent};
} }
QComboBox:focus { QComboBox:focus {
border-color: #88c0d0; border-color: ${accent};
} }
QComboBox::drop-down { QComboBox::drop-down {
border: none; border: none;
width: 18px; width: 18px;
} }
QComboBox QAbstractItemView { QComboBox QAbstractItemView {
background-color: #3b4252; background-color: ${bg_subtle};
color: #eceff4; color: ${text};
border: 1px solid #4c566a; border: 1px solid ${border_strong};
selection-background-color: #88c0d0; selection-background-color: ${accent};
selection-color: #2e3440; selection-color: ${accent_text};
outline: none; outline: none;
padding: 2px; padding: 2px;
} }
@ -161,19 +171,19 @@ QComboBox QAbstractItemView {
/* ---------- Scrollbars ---------- */ /* ---------- Scrollbars ---------- */
QScrollBar:vertical { QScrollBar:vertical {
background: #2e3440; background: ${bg};
width: 10px; width: 10px;
border: none; border: none;
margin: 0; margin: 0;
} }
QScrollBar::handle:vertical { QScrollBar::handle:vertical {
background: #434c5e; background: ${bg_hover};
border-radius: 4px; border-radius: 4px;
min-height: 24px; min-height: 24px;
margin: 1px; margin: 1px;
} }
QScrollBar::handle:vertical:hover { QScrollBar::handle:vertical:hover {
background: #4c566a; background: ${bg_active};
} }
QScrollBar::add-line:vertical, QScrollBar::add-line:vertical,
QScrollBar::sub-line:vertical { QScrollBar::sub-line:vertical {
@ -186,19 +196,19 @@ QScrollBar::sub-page:vertical {
} }
QScrollBar:horizontal { QScrollBar:horizontal {
background: #2e3440; background: ${bg};
height: 10px; height: 10px;
border: none; border: none;
margin: 0; margin: 0;
} }
QScrollBar::handle:horizontal { QScrollBar::handle:horizontal {
background: #434c5e; background: ${bg_hover};
border-radius: 4px; border-radius: 4px;
min-width: 24px; min-width: 24px;
margin: 1px; margin: 1px;
} }
QScrollBar::handle:horizontal:hover { QScrollBar::handle:horizontal:hover {
background: #4c566a; background: ${bg_active};
} }
QScrollBar::add-line:horizontal, QScrollBar::add-line:horizontal,
QScrollBar::sub-line:horizontal { QScrollBar::sub-line:horizontal {
@ -218,26 +228,26 @@ QScrollArea {
/* ---------- Menus ---------- */ /* ---------- Menus ---------- */
QMenuBar { QMenuBar {
background-color: #2e3440; background-color: ${bg};
color: #eceff4; color: ${text};
border-bottom: 1px solid #3b4252; border-bottom: 1px solid ${border};
} }
QMenuBar::item { QMenuBar::item {
background: transparent; background: transparent;
padding: 4px 10px; padding: 4px 10px;
} }
QMenuBar::item:selected { QMenuBar::item:selected {
background-color: #434c5e; background-color: ${bg_hover};
color: #eceff4; color: ${text};
} }
QMenuBar::item:pressed { QMenuBar::item:pressed {
background-color: #4c566a; background-color: ${bg_active};
} }
QMenu { QMenu {
background-color: #3b4252; background-color: ${bg_subtle};
color: #eceff4; color: ${text};
border: 1px solid #4c566a; border: 1px solid ${border_strong};
padding: 4px 0; padding: 4px 0;
} }
QMenu::item { QMenu::item {
@ -245,15 +255,15 @@ QMenu::item {
padding: 5px 24px 5px 24px; padding: 5px 24px 5px 24px;
} }
QMenu::item:selected { QMenu::item:selected {
background-color: #88c0d0; background-color: ${accent};
color: #2e3440; color: ${accent_text};
} }
QMenu::item:disabled { QMenu::item:disabled {
color: #4c566a; color: ${text_disabled};
} }
QMenu::separator { QMenu::separator {
height: 1px; height: 1px;
background: #3b4252; background: ${border};
margin: 4px 8px; margin: 4px 8px;
} }
QMenu::icon { QMenu::icon {
@ -263,9 +273,9 @@ QMenu::icon {
/* ---------- Status bar ---------- */ /* ---------- Status bar ---------- */
QStatusBar { QStatusBar {
background-color: #2e3440; background-color: ${bg};
color: #d8dee9; color: ${text_dim};
border-top: 1px solid #3b4252; border-top: 1px solid ${border};
} }
QStatusBar::item { QStatusBar::item {
border: none; border: none;
@ -274,7 +284,7 @@ QStatusBar::item {
/* ---------- Splitters ---------- */ /* ---------- Splitters ---------- */
QSplitter::handle { QSplitter::handle {
background: #3b4252; background: ${border};
} }
QSplitter::handle:horizontal { QSplitter::handle:horizontal {
width: 2px; width: 2px;
@ -283,38 +293,35 @@ QSplitter::handle:vertical {
height: 2px; height: 2px;
} }
QSplitter::handle:hover { QSplitter::handle:hover {
background: #88c0d0; background: ${accent};
} }
/* ---------- Sliders ---------- */ /* ---------- Sliders ---------- */
QSlider::groove:horizontal { QSlider::groove:horizontal {
background: #3b4252; background: ${bg_subtle};
height: 4px; height: 4px;
border-radius: 2px;
} }
QSlider::sub-page:horizontal { QSlider::sub-page:horizontal {
background: #88c0d0; background: ${accent};
border-radius: 2px;
} }
QSlider::handle:horizontal { QSlider::handle:horizontal {
background: #88c0d0; background: ${accent};
width: 12px; width: 12px;
height: 12px; height: 12px;
margin: -5px 0; margin: -5px 0;
border-radius: 6px;
} }
QSlider::handle:horizontal:hover { QSlider::handle:horizontal:hover {
background: #81a1c1; background: ${accent_dim};
} }
QSlider::groove:vertical { QSlider::groove:vertical {
background: #3b4252; background: ${bg_subtle};
width: 4px; width: 4px;
border-radius: 2px; border-radius: 2px;
} }
QSlider::handle:vertical { QSlider::handle:vertical {
background: #88c0d0; background: ${accent};
width: 12px; width: 12px;
height: 12px; height: 12px;
margin: 0 -5px; margin: 0 -5px;
@ -324,15 +331,15 @@ QSlider::handle:vertical {
/* ---------- Progress ---------- */ /* ---------- Progress ---------- */
QProgressBar { QProgressBar {
background-color: #3b4252; background-color: ${bg_subtle};
color: #eceff4; color: ${text};
border: 1px solid #3b4252; border: 1px solid ${border};
border-radius: 3px; border-radius: 3px;
text-align: center; text-align: center;
height: 6px; height: 6px;
} }
QProgressBar::chunk { QProgressBar::chunk {
background-color: #88c0d0; background-color: ${accent};
border-radius: 3px; border-radius: 3px;
} }
@ -340,14 +347,14 @@ QProgressBar::chunk {
QCheckBox, QRadioButton { QCheckBox, QRadioButton {
background: transparent; background: transparent;
color: #eceff4; color: ${text};
spacing: 6px; spacing: 6px;
} }
QCheckBox::indicator, QRadioButton::indicator { QCheckBox::indicator, QRadioButton::indicator {
width: 14px; width: 14px;
height: 14px; height: 14px;
background-color: #3b4252; background-color: ${bg_subtle};
border: 1px solid #4c566a; border: 1px solid ${border_strong};
} }
QCheckBox::indicator { QCheckBox::indicator {
border-radius: 3px; border-radius: 3px;
@ -356,23 +363,23 @@ QRadioButton::indicator {
border-radius: 7px; border-radius: 7px;
} }
QCheckBox::indicator:hover, QRadioButton::indicator:hover { QCheckBox::indicator:hover, QRadioButton::indicator:hover {
border-color: #88c0d0; border-color: ${accent};
} }
QCheckBox::indicator:checked, QRadioButton::indicator:checked { QCheckBox::indicator:checked, QRadioButton::indicator:checked {
background-color: #88c0d0; background-color: ${accent};
border-color: #88c0d0; border-color: ${accent};
} }
QCheckBox::indicator:disabled, QRadioButton::indicator:disabled { QCheckBox::indicator:disabled, QRadioButton::indicator:disabled {
background-color: #272b36; background-color: ${bg_alt};
border-color: #3b4252; border-color: ${border};
} }
/* ---------- Tooltips ---------- */ /* ---------- Tooltips ---------- */
QToolTip { QToolTip {
background-color: #3b4252; background-color: ${bg_subtle};
color: #eceff4; color: ${text};
border: 1px solid #4c566a; border: 1px solid ${border_strong};
padding: 4px 6px; padding: 4px 6px;
border-radius: 3px; border-radius: 3px;
} }
@ -380,12 +387,12 @@ QToolTip {
/* ---------- Item views (lists, trees, tables) ---------- */ /* ---------- Item views (lists, trees, tables) ---------- */
QListView, QListWidget, QTreeView, QTreeWidget, QTableView, QTableWidget { QListView, QListWidget, QTreeView, QTreeWidget, QTableView, QTableWidget {
background-color: #2e3440; background-color: ${bg};
alternate-background-color: #272b36; alternate-background-color: ${bg_alt};
color: #eceff4; color: ${text};
border: 1px solid #3b4252; border: 1px solid ${border};
selection-background-color: #88c0d0; selection-background-color: ${accent};
selection-color: #2e3440; selection-color: ${accent_text};
outline: none; outline: none;
} }
QListView::item, QListWidget::item, QListView::item, QListWidget::item,
@ -396,57 +403,57 @@ QTableView::item, QTableWidget::item {
QListView::item:hover, QListWidget::item:hover, QListView::item:hover, QListWidget::item:hover,
QTreeView::item:hover, QTreeWidget::item:hover, QTreeView::item:hover, QTreeWidget::item:hover,
QTableView::item:hover, QTableWidget::item:hover { QTableView::item:hover, QTableWidget::item:hover {
background-color: #434c5e; background-color: ${bg_hover};
} }
QListView::item:selected, QListWidget::item:selected, QListView::item:selected, QListWidget::item:selected,
QTreeView::item:selected, QTreeWidget::item:selected, QTreeView::item:selected, QTreeWidget::item:selected,
QTableView::item:selected, QTableWidget::item:selected { QTableView::item:selected, QTableWidget::item:selected {
background-color: #88c0d0; background-color: ${accent};
color: #2e3440; color: ${accent_text};
} }
QHeaderView::section { QHeaderView::section {
background-color: #3b4252; background-color: ${bg_subtle};
color: #eceff4; color: ${text};
border: none; border: none;
border-right: 1px solid #3b4252; border-right: 1px solid ${border};
padding: 4px 8px; padding: 4px 8px;
} }
QHeaderView::section:hover { QHeaderView::section:hover {
background-color: #434c5e; background-color: ${bg_hover};
} }
/* ---------- Tabs ---------- */ /* ---------- Tabs ---------- */
QTabWidget::pane { QTabWidget::pane {
border: 1px solid #3b4252; border: 1px solid ${border};
top: -1px; top: -1px;
} }
QTabBar::tab { QTabBar::tab {
background: #3b4252; background: ${bg_subtle};
color: #d8dee9; color: ${text_dim};
border: 1px solid #3b4252; border: 1px solid ${border};
border-bottom: none; border-bottom: none;
padding: 6px 14px; padding: 6px 14px;
border-top-left-radius: 4px; border-top-left-radius: 4px;
border-top-right-radius: 4px; border-top-right-radius: 4px;
} }
QTabBar::tab:selected { QTabBar::tab:selected {
background: #2e3440; background: ${bg};
color: #eceff4; color: ${text};
border-color: #4c566a; border-color: ${border_strong};
} }
QTabBar::tab:hover:!selected { QTabBar::tab:hover:!selected {
background: #434c5e; background: ${bg_hover};
color: #eceff4; color: ${text};
} }
/* ---------- Group boxes ---------- */ /* ---------- Group boxes ---------- */
QGroupBox { QGroupBox {
background: transparent; background: transparent;
color: #eceff4; color: ${text};
border: 1px solid #3b4252; border: 1px solid ${border};
border-radius: 4px; border-radius: 4px;
margin-top: 10px; margin-top: 10px;
padding-top: 8px; padding-top: 8px;
@ -455,27 +462,27 @@ QGroupBox::title {
subcontrol-origin: margin; subcontrol-origin: margin;
subcontrol-position: top left; subcontrol-position: top left;
padding: 0 6px; padding: 0 6px;
color: #d8dee9; color: ${text_dim};
} }
/* ---------- Frames ---------- */ /* ---------- Frames ---------- */
QFrame[frameShape="4"], /* HLine */ QFrame[frameShape="4"], /* HLine */
QFrame[frameShape="5"] /* VLine */ { QFrame[frameShape="5"] /* VLine */ {
background: #3b4252; background: ${border};
color: #3b4252; color: ${border};
} }
/* ---------- Toolbars ---------- */ /* ---------- Toolbars ---------- */
QToolBar { QToolBar {
background: #2e3440; background: ${bg};
border: none; border: none;
spacing: 4px; spacing: 4px;
padding: 2px; padding: 2px;
} }
QToolBar::separator { QToolBar::separator {
background: #3b4252; background: ${border};
width: 1px; width: 1px;
margin: 4px 4px; margin: 4px 4px;
} }
@ -483,31 +490,25 @@ QToolBar::separator {
/* ---------- Dock widgets ---------- */ /* ---------- Dock widgets ---------- */
QDockWidget { QDockWidget {
color: #eceff4; color: ${text};
titlebar-close-icon: none; titlebar-close-icon: none;
} }
QDockWidget::title { QDockWidget::title {
background: #3b4252; background: ${bg_subtle};
padding: 4px; padding: 4px;
border: 1px solid #3b4252; border: 1px solid ${border};
} }
/* ---------- Rubber band (multi-select drag rectangle) ---------- */ /* ---------- Rubber band (multi-select drag rectangle) ---------- */
QRubberBand { QRubberBand {
background: #88c0d0; background: ${accent};
border: 1px solid #88c0d0; border: 1px solid ${accent};
/* Qt blends rubber band at ~30% so this reads as a translucent /* Qt blends rubber band at ~30% so this reads as a translucent
* accent-tinted rectangle without needing rgba(). */ * accent-tinted rectangle without needing rgba(). */
} }
/* ---------- Popout & preview overlay controls ---------- */
QWidget#_preview_controls,
QWidget#_slideshow_toolbar,
QWidget#_slideshow_controls {
background: rgba(0, 0, 0, 160);
}
/* ---------- Thumbnail dot indicators (Qt properties on ThumbnailWidget) ---------- */ /* ---------- Thumbnail dot indicators (Qt properties on ThumbnailWidget) ---------- */
@ -519,10 +520,78 @@ ThumbnailWidget {
/* ---------- Info panel tag category colors ---------- */ /* ---------- Info panel tag category colors ---------- */
InfoPanel { InfoPanel {
qproperty-tagArtistColor: #ebcb8b; qproperty-tagArtistColor: ${warning};
qproperty-tagCharacterColor: #a3be8c; qproperty-tagCharacterColor: ${success};
qproperty-tagCopyrightColor: #88c0d0; qproperty-tagCopyrightColor: ${accent};
qproperty-tagSpeciesColor: #bf616a; qproperty-tagSpeciesColor: ${danger};
qproperty-tagMetaColor: #d8dee9; qproperty-tagMetaColor: ${text_dim};
qproperty-tagLoreColor: #d8dee9; qproperty-tagLoreColor: ${text_dim};
}
/* ---------- Video player letterbox / pillarbox color (mpv background) ---------- */
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.
*/
QWidget#_slideshow_toolbar,
QWidget#_slideshow_controls,
QWidget#_preview_controls {
background: ${overlay_bg};
}
QWidget#_slideshow_toolbar *,
QWidget#_slideshow_controls *,
QWidget#_preview_controls * {
background: transparent;
color: white;
border: none;
}
QWidget#_slideshow_toolbar QPushButton,
QWidget#_slideshow_controls QPushButton,
QWidget#_preview_controls QPushButton {
background: transparent;
color: white;
border: 1px solid rgba(255, 255, 255, 80);
padding: 2px 6px;
}
QWidget#_slideshow_toolbar QPushButton:hover,
QWidget#_slideshow_controls QPushButton:hover,
QWidget#_preview_controls QPushButton:hover {
background: rgba(255, 255, 255, 30);
}
QWidget#_slideshow_toolbar QSlider::groove:horizontal,
QWidget#_slideshow_controls QSlider::groove:horizontal,
QWidget#_preview_controls QSlider::groove:horizontal {
background: rgba(255, 255, 255, 40);
height: 4px;
}
QWidget#_slideshow_toolbar QSlider::handle:horizontal,
QWidget#_slideshow_controls QSlider::handle:horizontal,
QWidget#_preview_controls QSlider::handle:horizontal {
background: ${accent};
width: 10px;
margin: -4px 0;
}
QWidget#_slideshow_toolbar QSlider::sub-page:horizontal,
QWidget#_slideshow_controls QSlider::sub-page:horizontal,
QWidget#_preview_controls QSlider::sub-page:horizontal {
background: ${accent};
}
QWidget#_slideshow_toolbar QLabel,
QWidget#_slideshow_controls QLabel,
QWidget#_preview_controls QLabel {
background: transparent;
color: white;
} }

View File

@ -1,41 +1,46 @@
/* booru-viewer Solarized Dark /* booru-viewer Solarized Dark
* *
* Comprehensive Fusion-style QSS. Mimics the visual feel of Qt's Fusion * Edit the @palette block below to recolor this theme. The body uses
* style + a dark KDE color scheme: subtle borders, modest corner radius, * ${...} placeholders that the app's _load_user_qss preprocessor
* clear hover/pressed/focus states, transparent labels. * substitutes at load time. See themes/README.md for the full list of
* placeholder names and what each one is used for.
* *
* Palette (edit these and the rest of the file together if you fork): * The same dialect works in any custom.qss you put in your data dir.
* bg #002b36 */
* bg_alt #001f27
* bg_subtle #073642 /* @palette
* bg_hover #0d4654 bg: #002b36
* bg_active #586e75 bg_alt: #001f27
* text #93a1a1 bg_subtle: #073642
* text_dim #839496 bg_hover: #0d4654
* text_disabled #586e75 bg_active: #586e75
* border #073642 text: #93a1a1
* border_strong #0d4654 text_dim: #839496
* accent #268bd2 text_disabled: #586e75
* accent_text #002b36 border: #073642
* accent_dim #2aa198 border_strong: #0d4654
* link #2aa198 accent: #268bd2
* danger #dc322f accent_text: #002b36
* success #859900 accent_dim: #2aa198
* warning #b58900 link: #2aa198
danger: #dc322f
success: #859900
warning: #b58900
overlay_bg: rgba(0, 43, 54, 200)
*/ */
/* ---------- Base ---------- */ /* ---------- Base ---------- */
QWidget { QWidget {
background-color: #002b36; background-color: ${bg};
color: #93a1a1; color: ${text};
font-size: 13px; font-size: 13px;
selection-background-color: #268bd2; selection-background-color: ${accent};
selection-color: #002b36; selection-color: ${accent_text};
} }
QWidget:disabled { QWidget:disabled {
color: #586e75; color: ${text_disabled};
} }
/* Labels should never paint an opaque background they sit on top of /* Labels should never paint an opaque background they sit on top of
@ -45,115 +50,120 @@ QLabel {
} }
QMainWindow, QDialog { QMainWindow, QDialog {
background-color: #002b36; background-color: ${bg};
} }
/* ---------- Buttons ---------- */ /* ---------- Buttons ---------- */
QPushButton { QPushButton {
background-color: #073642; background-color: ${bg_subtle};
color: #93a1a1; color: ${text};
border: 1px solid #0d4654; border: 1px solid ${border_strong};
border-radius: 4px; border-radius: 4px;
padding: 5px 12px; padding: 5px 12px;
min-height: 18px; min-height: 18px;
} }
QPushButton:hover { QPushButton:hover {
background-color: #0d4654; background-color: ${bg_hover};
border-color: #268bd2; border-color: ${accent};
} }
QPushButton:pressed { QPushButton:pressed {
background-color: #586e75; background-color: ${bg_active};
} }
QPushButton:checked { QPushButton:checked {
background-color: #268bd2; background-color: ${accent};
color: #002b36; color: ${accent_text};
border-color: #268bd2; border-color: ${accent};
} }
QPushButton:checked:hover { QPushButton:checked:hover {
background-color: #2aa198; background-color: ${accent_dim};
border-color: #2aa198; border-color: ${accent_dim};
} }
QPushButton:disabled { QPushButton:disabled {
background-color: #001f27; background-color: ${bg_alt};
color: #586e75; color: ${text_disabled};
border-color: #073642; border-color: ${border};
} }
QPushButton:flat { QPushButton:flat {
background: transparent; background: transparent;
border: none; border: none;
} }
QPushButton:flat:hover { QPushButton:flat:hover {
background-color: #0d4654; background-color: ${bg_hover};
} }
QToolButton { QToolButton {
background-color: transparent; background-color: transparent;
color: #93a1a1; color: ${text};
border: 1px solid transparent; border: 1px solid transparent;
border-radius: 4px; border-radius: 4px;
padding: 4px; padding: 4px;
} }
QToolButton:hover { QToolButton:hover {
background-color: #0d4654; background-color: ${bg_hover};
border-color: #0d4654; border-color: ${border_strong};
} }
QToolButton:pressed, QToolButton:checked { QToolButton:pressed, QToolButton:checked {
background-color: #586e75; background-color: ${bg_active};
} }
/* ---------- Inputs ---------- */ /* ---------- Inputs ---------- */
QLineEdit, QSpinBox, QDoubleSpinBox, QTextEdit, QPlainTextEdit { QLineEdit, QSpinBox, QDoubleSpinBox, QTextEdit, QPlainTextEdit {
background-color: #073642; background-color: ${bg_subtle};
color: #93a1a1; color: ${text};
border: 1px solid #0d4654; border: 1px solid ${border_strong};
border-radius: 4px; border-radius: 4px;
padding: 4px 8px; padding: 4px 8px;
selection-background-color: #268bd2; /* min-height ensures the painted text fits inside the widget bounds
selection-color: #002b36; * 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: 20px;
selection-background-color: ${accent};
selection-color: ${accent_text};
} }
QLineEdit:focus, QLineEdit:focus,
QSpinBox:focus, QSpinBox:focus,
QDoubleSpinBox:focus, QDoubleSpinBox:focus,
QTextEdit:focus, QTextEdit:focus,
QPlainTextEdit:focus { QPlainTextEdit:focus {
border-color: #268bd2; border-color: ${accent};
} }
QLineEdit:disabled, QLineEdit:disabled,
QSpinBox:disabled, QSpinBox:disabled,
QDoubleSpinBox:disabled, QDoubleSpinBox:disabled,
QTextEdit:disabled, QTextEdit:disabled,
QPlainTextEdit:disabled { QPlainTextEdit:disabled {
background-color: #001f27; background-color: ${bg_alt};
color: #586e75; color: ${text_disabled};
border-color: #073642; border-color: ${border};
} }
QComboBox { QComboBox {
background-color: #073642; background-color: ${bg_subtle};
color: #93a1a1; color: ${text};
border: 1px solid #0d4654; border: 1px solid ${border_strong};
border-radius: 4px; border-radius: 4px;
padding: 4px 8px; padding: 4px 8px;
min-height: 18px; min-height: 20px;
} }
QComboBox:hover { QComboBox:hover {
border-color: #268bd2; border-color: ${accent};
} }
QComboBox:focus { QComboBox:focus {
border-color: #268bd2; border-color: ${accent};
} }
QComboBox::drop-down { QComboBox::drop-down {
border: none; border: none;
width: 18px; width: 18px;
} }
QComboBox QAbstractItemView { QComboBox QAbstractItemView {
background-color: #073642; background-color: ${bg_subtle};
color: #93a1a1; color: ${text};
border: 1px solid #0d4654; border: 1px solid ${border_strong};
selection-background-color: #268bd2; selection-background-color: ${accent};
selection-color: #002b36; selection-color: ${accent_text};
outline: none; outline: none;
padding: 2px; padding: 2px;
} }
@ -161,19 +171,19 @@ QComboBox QAbstractItemView {
/* ---------- Scrollbars ---------- */ /* ---------- Scrollbars ---------- */
QScrollBar:vertical { QScrollBar:vertical {
background: #002b36; background: ${bg};
width: 10px; width: 10px;
border: none; border: none;
margin: 0; margin: 0;
} }
QScrollBar::handle:vertical { QScrollBar::handle:vertical {
background: #0d4654; background: ${bg_hover};
border-radius: 4px; border-radius: 4px;
min-height: 24px; min-height: 24px;
margin: 1px; margin: 1px;
} }
QScrollBar::handle:vertical:hover { QScrollBar::handle:vertical:hover {
background: #586e75; background: ${bg_active};
} }
QScrollBar::add-line:vertical, QScrollBar::add-line:vertical,
QScrollBar::sub-line:vertical { QScrollBar::sub-line:vertical {
@ -186,19 +196,19 @@ QScrollBar::sub-page:vertical {
} }
QScrollBar:horizontal { QScrollBar:horizontal {
background: #002b36; background: ${bg};
height: 10px; height: 10px;
border: none; border: none;
margin: 0; margin: 0;
} }
QScrollBar::handle:horizontal { QScrollBar::handle:horizontal {
background: #0d4654; background: ${bg_hover};
border-radius: 4px; border-radius: 4px;
min-width: 24px; min-width: 24px;
margin: 1px; margin: 1px;
} }
QScrollBar::handle:horizontal:hover { QScrollBar::handle:horizontal:hover {
background: #586e75; background: ${bg_active};
} }
QScrollBar::add-line:horizontal, QScrollBar::add-line:horizontal,
QScrollBar::sub-line:horizontal { QScrollBar::sub-line:horizontal {
@ -218,26 +228,26 @@ QScrollArea {
/* ---------- Menus ---------- */ /* ---------- Menus ---------- */
QMenuBar { QMenuBar {
background-color: #002b36; background-color: ${bg};
color: #93a1a1; color: ${text};
border-bottom: 1px solid #073642; border-bottom: 1px solid ${border};
} }
QMenuBar::item { QMenuBar::item {
background: transparent; background: transparent;
padding: 4px 10px; padding: 4px 10px;
} }
QMenuBar::item:selected { QMenuBar::item:selected {
background-color: #0d4654; background-color: ${bg_hover};
color: #93a1a1; color: ${text};
} }
QMenuBar::item:pressed { QMenuBar::item:pressed {
background-color: #586e75; background-color: ${bg_active};
} }
QMenu { QMenu {
background-color: #073642; background-color: ${bg_subtle};
color: #93a1a1; color: ${text};
border: 1px solid #0d4654; border: 1px solid ${border_strong};
padding: 4px 0; padding: 4px 0;
} }
QMenu::item { QMenu::item {
@ -245,15 +255,15 @@ QMenu::item {
padding: 5px 24px 5px 24px; padding: 5px 24px 5px 24px;
} }
QMenu::item:selected { QMenu::item:selected {
background-color: #268bd2; background-color: ${accent};
color: #002b36; color: ${accent_text};
} }
QMenu::item:disabled { QMenu::item:disabled {
color: #586e75; color: ${text_disabled};
} }
QMenu::separator { QMenu::separator {
height: 1px; height: 1px;
background: #073642; background: ${border};
margin: 4px 8px; margin: 4px 8px;
} }
QMenu::icon { QMenu::icon {
@ -263,9 +273,9 @@ QMenu::icon {
/* ---------- Status bar ---------- */ /* ---------- Status bar ---------- */
QStatusBar { QStatusBar {
background-color: #002b36; background-color: ${bg};
color: #839496; color: ${text_dim};
border-top: 1px solid #073642; border-top: 1px solid ${border};
} }
QStatusBar::item { QStatusBar::item {
border: none; border: none;
@ -274,7 +284,7 @@ QStatusBar::item {
/* ---------- Splitters ---------- */ /* ---------- Splitters ---------- */
QSplitter::handle { QSplitter::handle {
background: #073642; background: ${border};
} }
QSplitter::handle:horizontal { QSplitter::handle:horizontal {
width: 2px; width: 2px;
@ -283,38 +293,35 @@ QSplitter::handle:vertical {
height: 2px; height: 2px;
} }
QSplitter::handle:hover { QSplitter::handle:hover {
background: #268bd2; background: ${accent};
} }
/* ---------- Sliders ---------- */ /* ---------- Sliders ---------- */
QSlider::groove:horizontal { QSlider::groove:horizontal {
background: #073642; background: ${bg_subtle};
height: 4px; height: 4px;
border-radius: 2px;
} }
QSlider::sub-page:horizontal { QSlider::sub-page:horizontal {
background: #268bd2; background: ${accent};
border-radius: 2px;
} }
QSlider::handle:horizontal { QSlider::handle:horizontal {
background: #268bd2; background: ${accent};
width: 12px; width: 12px;
height: 12px; height: 12px;
margin: -5px 0; margin: -5px 0;
border-radius: 6px;
} }
QSlider::handle:horizontal:hover { QSlider::handle:horizontal:hover {
background: #2aa198; background: ${accent_dim};
} }
QSlider::groove:vertical { QSlider::groove:vertical {
background: #073642; background: ${bg_subtle};
width: 4px; width: 4px;
border-radius: 2px; border-radius: 2px;
} }
QSlider::handle:vertical { QSlider::handle:vertical {
background: #268bd2; background: ${accent};
width: 12px; width: 12px;
height: 12px; height: 12px;
margin: 0 -5px; margin: 0 -5px;
@ -324,15 +331,15 @@ QSlider::handle:vertical {
/* ---------- Progress ---------- */ /* ---------- Progress ---------- */
QProgressBar { QProgressBar {
background-color: #073642; background-color: ${bg_subtle};
color: #93a1a1; color: ${text};
border: 1px solid #073642; border: 1px solid ${border};
border-radius: 3px; border-radius: 3px;
text-align: center; text-align: center;
height: 6px; height: 6px;
} }
QProgressBar::chunk { QProgressBar::chunk {
background-color: #268bd2; background-color: ${accent};
border-radius: 3px; border-radius: 3px;
} }
@ -340,14 +347,14 @@ QProgressBar::chunk {
QCheckBox, QRadioButton { QCheckBox, QRadioButton {
background: transparent; background: transparent;
color: #93a1a1; color: ${text};
spacing: 6px; spacing: 6px;
} }
QCheckBox::indicator, QRadioButton::indicator { QCheckBox::indicator, QRadioButton::indicator {
width: 14px; width: 14px;
height: 14px; height: 14px;
background-color: #073642; background-color: ${bg_subtle};
border: 1px solid #0d4654; border: 1px solid ${border_strong};
} }
QCheckBox::indicator { QCheckBox::indicator {
border-radius: 3px; border-radius: 3px;
@ -356,23 +363,23 @@ QRadioButton::indicator {
border-radius: 7px; border-radius: 7px;
} }
QCheckBox::indicator:hover, QRadioButton::indicator:hover { QCheckBox::indicator:hover, QRadioButton::indicator:hover {
border-color: #268bd2; border-color: ${accent};
} }
QCheckBox::indicator:checked, QRadioButton::indicator:checked { QCheckBox::indicator:checked, QRadioButton::indicator:checked {
background-color: #268bd2; background-color: ${accent};
border-color: #268bd2; border-color: ${accent};
} }
QCheckBox::indicator:disabled, QRadioButton::indicator:disabled { QCheckBox::indicator:disabled, QRadioButton::indicator:disabled {
background-color: #001f27; background-color: ${bg_alt};
border-color: #073642; border-color: ${border};
} }
/* ---------- Tooltips ---------- */ /* ---------- Tooltips ---------- */
QToolTip { QToolTip {
background-color: #073642; background-color: ${bg_subtle};
color: #93a1a1; color: ${text};
border: 1px solid #0d4654; border: 1px solid ${border_strong};
padding: 4px 6px; padding: 4px 6px;
border-radius: 3px; border-radius: 3px;
} }
@ -380,12 +387,12 @@ QToolTip {
/* ---------- Item views (lists, trees, tables) ---------- */ /* ---------- Item views (lists, trees, tables) ---------- */
QListView, QListWidget, QTreeView, QTreeWidget, QTableView, QTableWidget { QListView, QListWidget, QTreeView, QTreeWidget, QTableView, QTableWidget {
background-color: #002b36; background-color: ${bg};
alternate-background-color: #001f27; alternate-background-color: ${bg_alt};
color: #93a1a1; color: ${text};
border: 1px solid #073642; border: 1px solid ${border};
selection-background-color: #268bd2; selection-background-color: ${accent};
selection-color: #002b36; selection-color: ${accent_text};
outline: none; outline: none;
} }
QListView::item, QListWidget::item, QListView::item, QListWidget::item,
@ -396,57 +403,57 @@ QTableView::item, QTableWidget::item {
QListView::item:hover, QListWidget::item:hover, QListView::item:hover, QListWidget::item:hover,
QTreeView::item:hover, QTreeWidget::item:hover, QTreeView::item:hover, QTreeWidget::item:hover,
QTableView::item:hover, QTableWidget::item:hover { QTableView::item:hover, QTableWidget::item:hover {
background-color: #0d4654; background-color: ${bg_hover};
} }
QListView::item:selected, QListWidget::item:selected, QListView::item:selected, QListWidget::item:selected,
QTreeView::item:selected, QTreeWidget::item:selected, QTreeView::item:selected, QTreeWidget::item:selected,
QTableView::item:selected, QTableWidget::item:selected { QTableView::item:selected, QTableWidget::item:selected {
background-color: #268bd2; background-color: ${accent};
color: #002b36; color: ${accent_text};
} }
QHeaderView::section { QHeaderView::section {
background-color: #073642; background-color: ${bg_subtle};
color: #93a1a1; color: ${text};
border: none; border: none;
border-right: 1px solid #073642; border-right: 1px solid ${border};
padding: 4px 8px; padding: 4px 8px;
} }
QHeaderView::section:hover { QHeaderView::section:hover {
background-color: #0d4654; background-color: ${bg_hover};
} }
/* ---------- Tabs ---------- */ /* ---------- Tabs ---------- */
QTabWidget::pane { QTabWidget::pane {
border: 1px solid #073642; border: 1px solid ${border};
top: -1px; top: -1px;
} }
QTabBar::tab { QTabBar::tab {
background: #073642; background: ${bg_subtle};
color: #839496; color: ${text_dim};
border: 1px solid #073642; border: 1px solid ${border};
border-bottom: none; border-bottom: none;
padding: 6px 14px; padding: 6px 14px;
border-top-left-radius: 4px; border-top-left-radius: 4px;
border-top-right-radius: 4px; border-top-right-radius: 4px;
} }
QTabBar::tab:selected { QTabBar::tab:selected {
background: #002b36; background: ${bg};
color: #93a1a1; color: ${text};
border-color: #0d4654; border-color: ${border_strong};
} }
QTabBar::tab:hover:!selected { QTabBar::tab:hover:!selected {
background: #0d4654; background: ${bg_hover};
color: #93a1a1; color: ${text};
} }
/* ---------- Group boxes ---------- */ /* ---------- Group boxes ---------- */
QGroupBox { QGroupBox {
background: transparent; background: transparent;
color: #93a1a1; color: ${text};
border: 1px solid #073642; border: 1px solid ${border};
border-radius: 4px; border-radius: 4px;
margin-top: 10px; margin-top: 10px;
padding-top: 8px; padding-top: 8px;
@ -455,27 +462,27 @@ QGroupBox::title {
subcontrol-origin: margin; subcontrol-origin: margin;
subcontrol-position: top left; subcontrol-position: top left;
padding: 0 6px; padding: 0 6px;
color: #839496; color: ${text_dim};
} }
/* ---------- Frames ---------- */ /* ---------- Frames ---------- */
QFrame[frameShape="4"], /* HLine */ QFrame[frameShape="4"], /* HLine */
QFrame[frameShape="5"] /* VLine */ { QFrame[frameShape="5"] /* VLine */ {
background: #073642; background: ${border};
color: #073642; color: ${border};
} }
/* ---------- Toolbars ---------- */ /* ---------- Toolbars ---------- */
QToolBar { QToolBar {
background: #002b36; background: ${bg};
border: none; border: none;
spacing: 4px; spacing: 4px;
padding: 2px; padding: 2px;
} }
QToolBar::separator { QToolBar::separator {
background: #073642; background: ${border};
width: 1px; width: 1px;
margin: 4px 4px; margin: 4px 4px;
} }
@ -483,31 +490,25 @@ QToolBar::separator {
/* ---------- Dock widgets ---------- */ /* ---------- Dock widgets ---------- */
QDockWidget { QDockWidget {
color: #93a1a1; color: ${text};
titlebar-close-icon: none; titlebar-close-icon: none;
} }
QDockWidget::title { QDockWidget::title {
background: #073642; background: ${bg_subtle};
padding: 4px; padding: 4px;
border: 1px solid #073642; border: 1px solid ${border};
} }
/* ---------- Rubber band (multi-select drag rectangle) ---------- */ /* ---------- Rubber band (multi-select drag rectangle) ---------- */
QRubberBand { QRubberBand {
background: #268bd2; background: ${accent};
border: 1px solid #268bd2; border: 1px solid ${accent};
/* Qt blends rubber band at ~30% so this reads as a translucent /* Qt blends rubber band at ~30% so this reads as a translucent
* accent-tinted rectangle without needing rgba(). */ * accent-tinted rectangle without needing rgba(). */
} }
/* ---------- Popout & preview overlay controls ---------- */
QWidget#_preview_controls,
QWidget#_slideshow_toolbar,
QWidget#_slideshow_controls {
background: rgba(0, 0, 0, 160);
}
/* ---------- Thumbnail dot indicators (Qt properties on ThumbnailWidget) ---------- */ /* ---------- Thumbnail dot indicators (Qt properties on ThumbnailWidget) ---------- */
@ -519,10 +520,78 @@ ThumbnailWidget {
/* ---------- Info panel tag category colors ---------- */ /* ---------- Info panel tag category colors ---------- */
InfoPanel { InfoPanel {
qproperty-tagArtistColor: #b58900; qproperty-tagArtistColor: ${warning};
qproperty-tagCharacterColor: #859900; qproperty-tagCharacterColor: ${success};
qproperty-tagCopyrightColor: #268bd2; qproperty-tagCopyrightColor: ${accent};
qproperty-tagSpeciesColor: #dc322f; qproperty-tagSpeciesColor: ${danger};
qproperty-tagMetaColor: #839496; qproperty-tagMetaColor: ${text_dim};
qproperty-tagLoreColor: #839496; qproperty-tagLoreColor: ${text_dim};
}
/* ---------- Video player letterbox / pillarbox color (mpv background) ---------- */
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.
*/
QWidget#_slideshow_toolbar,
QWidget#_slideshow_controls,
QWidget#_preview_controls {
background: ${overlay_bg};
}
QWidget#_slideshow_toolbar *,
QWidget#_slideshow_controls *,
QWidget#_preview_controls * {
background: transparent;
color: white;
border: none;
}
QWidget#_slideshow_toolbar QPushButton,
QWidget#_slideshow_controls QPushButton,
QWidget#_preview_controls QPushButton {
background: transparent;
color: white;
border: 1px solid rgba(255, 255, 255, 80);
padding: 2px 6px;
}
QWidget#_slideshow_toolbar QPushButton:hover,
QWidget#_slideshow_controls QPushButton:hover,
QWidget#_preview_controls QPushButton:hover {
background: rgba(255, 255, 255, 30);
}
QWidget#_slideshow_toolbar QSlider::groove:horizontal,
QWidget#_slideshow_controls QSlider::groove:horizontal,
QWidget#_preview_controls QSlider::groove:horizontal {
background: rgba(255, 255, 255, 40);
height: 4px;
}
QWidget#_slideshow_toolbar QSlider::handle:horizontal,
QWidget#_slideshow_controls QSlider::handle:horizontal,
QWidget#_preview_controls QSlider::handle:horizontal {
background: ${accent};
width: 10px;
margin: -4px 0;
}
QWidget#_slideshow_toolbar QSlider::sub-page:horizontal,
QWidget#_slideshow_controls QSlider::sub-page:horizontal,
QWidget#_preview_controls QSlider::sub-page:horizontal {
background: ${accent};
}
QWidget#_slideshow_toolbar QLabel,
QWidget#_slideshow_controls QLabel,
QWidget#_preview_controls QLabel {
background: transparent;
color: white;
} }

View File

@ -1,41 +1,46 @@
/* booru-viewer Tokyo Night /* booru-viewer Tokyo Night
* *
* Comprehensive Fusion-style QSS. Mimics the visual feel of Qt's Fusion * Edit the @palette block below to recolor this theme. The body uses
* style + a dark KDE color scheme: subtle borders, modest corner radius, * ${...} placeholders that the app's _load_user_qss preprocessor
* clear hover/pressed/focus states, transparent labels. * substitutes at load time. See themes/README.md for the full list of
* placeholder names and what each one is used for.
* *
* Palette (edit these and the rest of the file together if you fork): * The same dialect works in any custom.qss you put in your data dir.
* bg #1a1b26 */
* bg_alt #16161e
* bg_subtle #24283b /* @palette
* bg_hover #292e42 bg: #1a1b26
* bg_active #3b4261 bg_alt: #16161e
* text #c0caf5 bg_subtle: #24283b
* text_dim #a9b1d6 bg_hover: #292e42
* text_disabled #565f89 bg_active: #3b4261
* border #24283b text: #c0caf5
* border_strong #292e42 text_dim: #a9b1d6
* accent #7aa2f7 text_disabled: #565f89
* accent_text #1a1b26 border: #24283b
* accent_dim #7dcfff border_strong: #292e42
* link #7dcfff accent: #7aa2f7
* danger #f7768e accent_text: #1a1b26
* success #9ece6a accent_dim: #7dcfff
* warning #e0af68 link: #7dcfff
danger: #f7768e
success: #9ece6a
warning: #e0af68
overlay_bg: rgba(26, 27, 38, 200)
*/ */
/* ---------- Base ---------- */ /* ---------- Base ---------- */
QWidget { QWidget {
background-color: #1a1b26; background-color: ${bg};
color: #c0caf5; color: ${text};
font-size: 13px; font-size: 13px;
selection-background-color: #7aa2f7; selection-background-color: ${accent};
selection-color: #1a1b26; selection-color: ${accent_text};
} }
QWidget:disabled { QWidget:disabled {
color: #565f89; color: ${text_disabled};
} }
/* Labels should never paint an opaque background they sit on top of /* Labels should never paint an opaque background they sit on top of
@ -45,115 +50,120 @@ QLabel {
} }
QMainWindow, QDialog { QMainWindow, QDialog {
background-color: #1a1b26; background-color: ${bg};
} }
/* ---------- Buttons ---------- */ /* ---------- Buttons ---------- */
QPushButton { QPushButton {
background-color: #24283b; background-color: ${bg_subtle};
color: #c0caf5; color: ${text};
border: 1px solid #292e42; border: 1px solid ${border_strong};
border-radius: 4px; border-radius: 4px;
padding: 5px 12px; padding: 5px 12px;
min-height: 18px; min-height: 18px;
} }
QPushButton:hover { QPushButton:hover {
background-color: #292e42; background-color: ${bg_hover};
border-color: #7aa2f7; border-color: ${accent};
} }
QPushButton:pressed { QPushButton:pressed {
background-color: #3b4261; background-color: ${bg_active};
} }
QPushButton:checked { QPushButton:checked {
background-color: #7aa2f7; background-color: ${accent};
color: #1a1b26; color: ${accent_text};
border-color: #7aa2f7; border-color: ${accent};
} }
QPushButton:checked:hover { QPushButton:checked:hover {
background-color: #7dcfff; background-color: ${accent_dim};
border-color: #7dcfff; border-color: ${accent_dim};
} }
QPushButton:disabled { QPushButton:disabled {
background-color: #16161e; background-color: ${bg_alt};
color: #565f89; color: ${text_disabled};
border-color: #24283b; border-color: ${border};
} }
QPushButton:flat { QPushButton:flat {
background: transparent; background: transparent;
border: none; border: none;
} }
QPushButton:flat:hover { QPushButton:flat:hover {
background-color: #292e42; background-color: ${bg_hover};
} }
QToolButton { QToolButton {
background-color: transparent; background-color: transparent;
color: #c0caf5; color: ${text};
border: 1px solid transparent; border: 1px solid transparent;
border-radius: 4px; border-radius: 4px;
padding: 4px; padding: 4px;
} }
QToolButton:hover { QToolButton:hover {
background-color: #292e42; background-color: ${bg_hover};
border-color: #292e42; border-color: ${border_strong};
} }
QToolButton:pressed, QToolButton:checked { QToolButton:pressed, QToolButton:checked {
background-color: #3b4261; background-color: ${bg_active};
} }
/* ---------- Inputs ---------- */ /* ---------- Inputs ---------- */
QLineEdit, QSpinBox, QDoubleSpinBox, QTextEdit, QPlainTextEdit { QLineEdit, QSpinBox, QDoubleSpinBox, QTextEdit, QPlainTextEdit {
background-color: #24283b; background-color: ${bg_subtle};
color: #c0caf5; color: ${text};
border: 1px solid #292e42; border: 1px solid ${border_strong};
border-radius: 4px; border-radius: 4px;
padding: 4px 8px; padding: 4px 8px;
selection-background-color: #7aa2f7; /* min-height ensures the painted text fits inside the widget bounds
selection-color: #1a1b26; * 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: 20px;
selection-background-color: ${accent};
selection-color: ${accent_text};
} }
QLineEdit:focus, QLineEdit:focus,
QSpinBox:focus, QSpinBox:focus,
QDoubleSpinBox:focus, QDoubleSpinBox:focus,
QTextEdit:focus, QTextEdit:focus,
QPlainTextEdit:focus { QPlainTextEdit:focus {
border-color: #7aa2f7; border-color: ${accent};
} }
QLineEdit:disabled, QLineEdit:disabled,
QSpinBox:disabled, QSpinBox:disabled,
QDoubleSpinBox:disabled, QDoubleSpinBox:disabled,
QTextEdit:disabled, QTextEdit:disabled,
QPlainTextEdit:disabled { QPlainTextEdit:disabled {
background-color: #16161e; background-color: ${bg_alt};
color: #565f89; color: ${text_disabled};
border-color: #24283b; border-color: ${border};
} }
QComboBox { QComboBox {
background-color: #24283b; background-color: ${bg_subtle};
color: #c0caf5; color: ${text};
border: 1px solid #292e42; border: 1px solid ${border_strong};
border-radius: 4px; border-radius: 4px;
padding: 4px 8px; padding: 4px 8px;
min-height: 18px; min-height: 20px;
} }
QComboBox:hover { QComboBox:hover {
border-color: #7aa2f7; border-color: ${accent};
} }
QComboBox:focus { QComboBox:focus {
border-color: #7aa2f7; border-color: ${accent};
} }
QComboBox::drop-down { QComboBox::drop-down {
border: none; border: none;
width: 18px; width: 18px;
} }
QComboBox QAbstractItemView { QComboBox QAbstractItemView {
background-color: #24283b; background-color: ${bg_subtle};
color: #c0caf5; color: ${text};
border: 1px solid #292e42; border: 1px solid ${border_strong};
selection-background-color: #7aa2f7; selection-background-color: ${accent};
selection-color: #1a1b26; selection-color: ${accent_text};
outline: none; outline: none;
padding: 2px; padding: 2px;
} }
@ -161,19 +171,19 @@ QComboBox QAbstractItemView {
/* ---------- Scrollbars ---------- */ /* ---------- Scrollbars ---------- */
QScrollBar:vertical { QScrollBar:vertical {
background: #1a1b26; background: ${bg};
width: 10px; width: 10px;
border: none; border: none;
margin: 0; margin: 0;
} }
QScrollBar::handle:vertical { QScrollBar::handle:vertical {
background: #292e42; background: ${bg_hover};
border-radius: 4px; border-radius: 4px;
min-height: 24px; min-height: 24px;
margin: 1px; margin: 1px;
} }
QScrollBar::handle:vertical:hover { QScrollBar::handle:vertical:hover {
background: #3b4261; background: ${bg_active};
} }
QScrollBar::add-line:vertical, QScrollBar::add-line:vertical,
QScrollBar::sub-line:vertical { QScrollBar::sub-line:vertical {
@ -186,19 +196,19 @@ QScrollBar::sub-page:vertical {
} }
QScrollBar:horizontal { QScrollBar:horizontal {
background: #1a1b26; background: ${bg};
height: 10px; height: 10px;
border: none; border: none;
margin: 0; margin: 0;
} }
QScrollBar::handle:horizontal { QScrollBar::handle:horizontal {
background: #292e42; background: ${bg_hover};
border-radius: 4px; border-radius: 4px;
min-width: 24px; min-width: 24px;
margin: 1px; margin: 1px;
} }
QScrollBar::handle:horizontal:hover { QScrollBar::handle:horizontal:hover {
background: #3b4261; background: ${bg_active};
} }
QScrollBar::add-line:horizontal, QScrollBar::add-line:horizontal,
QScrollBar::sub-line:horizontal { QScrollBar::sub-line:horizontal {
@ -218,26 +228,26 @@ QScrollArea {
/* ---------- Menus ---------- */ /* ---------- Menus ---------- */
QMenuBar { QMenuBar {
background-color: #1a1b26; background-color: ${bg};
color: #c0caf5; color: ${text};
border-bottom: 1px solid #24283b; border-bottom: 1px solid ${border};
} }
QMenuBar::item { QMenuBar::item {
background: transparent; background: transparent;
padding: 4px 10px; padding: 4px 10px;
} }
QMenuBar::item:selected { QMenuBar::item:selected {
background-color: #292e42; background-color: ${bg_hover};
color: #c0caf5; color: ${text};
} }
QMenuBar::item:pressed { QMenuBar::item:pressed {
background-color: #3b4261; background-color: ${bg_active};
} }
QMenu { QMenu {
background-color: #24283b; background-color: ${bg_subtle};
color: #c0caf5; color: ${text};
border: 1px solid #292e42; border: 1px solid ${border_strong};
padding: 4px 0; padding: 4px 0;
} }
QMenu::item { QMenu::item {
@ -245,15 +255,15 @@ QMenu::item {
padding: 5px 24px 5px 24px; padding: 5px 24px 5px 24px;
} }
QMenu::item:selected { QMenu::item:selected {
background-color: #7aa2f7; background-color: ${accent};
color: #1a1b26; color: ${accent_text};
} }
QMenu::item:disabled { QMenu::item:disabled {
color: #565f89; color: ${text_disabled};
} }
QMenu::separator { QMenu::separator {
height: 1px; height: 1px;
background: #24283b; background: ${border};
margin: 4px 8px; margin: 4px 8px;
} }
QMenu::icon { QMenu::icon {
@ -263,9 +273,9 @@ QMenu::icon {
/* ---------- Status bar ---------- */ /* ---------- Status bar ---------- */
QStatusBar { QStatusBar {
background-color: #1a1b26; background-color: ${bg};
color: #a9b1d6; color: ${text_dim};
border-top: 1px solid #24283b; border-top: 1px solid ${border};
} }
QStatusBar::item { QStatusBar::item {
border: none; border: none;
@ -274,7 +284,7 @@ QStatusBar::item {
/* ---------- Splitters ---------- */ /* ---------- Splitters ---------- */
QSplitter::handle { QSplitter::handle {
background: #24283b; background: ${border};
} }
QSplitter::handle:horizontal { QSplitter::handle:horizontal {
width: 2px; width: 2px;
@ -283,38 +293,35 @@ QSplitter::handle:vertical {
height: 2px; height: 2px;
} }
QSplitter::handle:hover { QSplitter::handle:hover {
background: #7aa2f7; background: ${accent};
} }
/* ---------- Sliders ---------- */ /* ---------- Sliders ---------- */
QSlider::groove:horizontal { QSlider::groove:horizontal {
background: #24283b; background: ${bg_subtle};
height: 4px; height: 4px;
border-radius: 2px;
} }
QSlider::sub-page:horizontal { QSlider::sub-page:horizontal {
background: #7aa2f7; background: ${accent};
border-radius: 2px;
} }
QSlider::handle:horizontal { QSlider::handle:horizontal {
background: #7aa2f7; background: ${accent};
width: 12px; width: 12px;
height: 12px; height: 12px;
margin: -5px 0; margin: -5px 0;
border-radius: 6px;
} }
QSlider::handle:horizontal:hover { QSlider::handle:horizontal:hover {
background: #7dcfff; background: ${accent_dim};
} }
QSlider::groove:vertical { QSlider::groove:vertical {
background: #24283b; background: ${bg_subtle};
width: 4px; width: 4px;
border-radius: 2px; border-radius: 2px;
} }
QSlider::handle:vertical { QSlider::handle:vertical {
background: #7aa2f7; background: ${accent};
width: 12px; width: 12px;
height: 12px; height: 12px;
margin: 0 -5px; margin: 0 -5px;
@ -324,15 +331,15 @@ QSlider::handle:vertical {
/* ---------- Progress ---------- */ /* ---------- Progress ---------- */
QProgressBar { QProgressBar {
background-color: #24283b; background-color: ${bg_subtle};
color: #c0caf5; color: ${text};
border: 1px solid #24283b; border: 1px solid ${border};
border-radius: 3px; border-radius: 3px;
text-align: center; text-align: center;
height: 6px; height: 6px;
} }
QProgressBar::chunk { QProgressBar::chunk {
background-color: #7aa2f7; background-color: ${accent};
border-radius: 3px; border-radius: 3px;
} }
@ -340,14 +347,14 @@ QProgressBar::chunk {
QCheckBox, QRadioButton { QCheckBox, QRadioButton {
background: transparent; background: transparent;
color: #c0caf5; color: ${text};
spacing: 6px; spacing: 6px;
} }
QCheckBox::indicator, QRadioButton::indicator { QCheckBox::indicator, QRadioButton::indicator {
width: 14px; width: 14px;
height: 14px; height: 14px;
background-color: #24283b; background-color: ${bg_subtle};
border: 1px solid #292e42; border: 1px solid ${border_strong};
} }
QCheckBox::indicator { QCheckBox::indicator {
border-radius: 3px; border-radius: 3px;
@ -356,23 +363,23 @@ QRadioButton::indicator {
border-radius: 7px; border-radius: 7px;
} }
QCheckBox::indicator:hover, QRadioButton::indicator:hover { QCheckBox::indicator:hover, QRadioButton::indicator:hover {
border-color: #7aa2f7; border-color: ${accent};
} }
QCheckBox::indicator:checked, QRadioButton::indicator:checked { QCheckBox::indicator:checked, QRadioButton::indicator:checked {
background-color: #7aa2f7; background-color: ${accent};
border-color: #7aa2f7; border-color: ${accent};
} }
QCheckBox::indicator:disabled, QRadioButton::indicator:disabled { QCheckBox::indicator:disabled, QRadioButton::indicator:disabled {
background-color: #16161e; background-color: ${bg_alt};
border-color: #24283b; border-color: ${border};
} }
/* ---------- Tooltips ---------- */ /* ---------- Tooltips ---------- */
QToolTip { QToolTip {
background-color: #24283b; background-color: ${bg_subtle};
color: #c0caf5; color: ${text};
border: 1px solid #292e42; border: 1px solid ${border_strong};
padding: 4px 6px; padding: 4px 6px;
border-radius: 3px; border-radius: 3px;
} }
@ -380,12 +387,12 @@ QToolTip {
/* ---------- Item views (lists, trees, tables) ---------- */ /* ---------- Item views (lists, trees, tables) ---------- */
QListView, QListWidget, QTreeView, QTreeWidget, QTableView, QTableWidget { QListView, QListWidget, QTreeView, QTreeWidget, QTableView, QTableWidget {
background-color: #1a1b26; background-color: ${bg};
alternate-background-color: #16161e; alternate-background-color: ${bg_alt};
color: #c0caf5; color: ${text};
border: 1px solid #24283b; border: 1px solid ${border};
selection-background-color: #7aa2f7; selection-background-color: ${accent};
selection-color: #1a1b26; selection-color: ${accent_text};
outline: none; outline: none;
} }
QListView::item, QListWidget::item, QListView::item, QListWidget::item,
@ -396,57 +403,57 @@ QTableView::item, QTableWidget::item {
QListView::item:hover, QListWidget::item:hover, QListView::item:hover, QListWidget::item:hover,
QTreeView::item:hover, QTreeWidget::item:hover, QTreeView::item:hover, QTreeWidget::item:hover,
QTableView::item:hover, QTableWidget::item:hover { QTableView::item:hover, QTableWidget::item:hover {
background-color: #292e42; background-color: ${bg_hover};
} }
QListView::item:selected, QListWidget::item:selected, QListView::item:selected, QListWidget::item:selected,
QTreeView::item:selected, QTreeWidget::item:selected, QTreeView::item:selected, QTreeWidget::item:selected,
QTableView::item:selected, QTableWidget::item:selected { QTableView::item:selected, QTableWidget::item:selected {
background-color: #7aa2f7; background-color: ${accent};
color: #1a1b26; color: ${accent_text};
} }
QHeaderView::section { QHeaderView::section {
background-color: #24283b; background-color: ${bg_subtle};
color: #c0caf5; color: ${text};
border: none; border: none;
border-right: 1px solid #24283b; border-right: 1px solid ${border};
padding: 4px 8px; padding: 4px 8px;
} }
QHeaderView::section:hover { QHeaderView::section:hover {
background-color: #292e42; background-color: ${bg_hover};
} }
/* ---------- Tabs ---------- */ /* ---------- Tabs ---------- */
QTabWidget::pane { QTabWidget::pane {
border: 1px solid #24283b; border: 1px solid ${border};
top: -1px; top: -1px;
} }
QTabBar::tab { QTabBar::tab {
background: #24283b; background: ${bg_subtle};
color: #a9b1d6; color: ${text_dim};
border: 1px solid #24283b; border: 1px solid ${border};
border-bottom: none; border-bottom: none;
padding: 6px 14px; padding: 6px 14px;
border-top-left-radius: 4px; border-top-left-radius: 4px;
border-top-right-radius: 4px; border-top-right-radius: 4px;
} }
QTabBar::tab:selected { QTabBar::tab:selected {
background: #1a1b26; background: ${bg};
color: #c0caf5; color: ${text};
border-color: #292e42; border-color: ${border_strong};
} }
QTabBar::tab:hover:!selected { QTabBar::tab:hover:!selected {
background: #292e42; background: ${bg_hover};
color: #c0caf5; color: ${text};
} }
/* ---------- Group boxes ---------- */ /* ---------- Group boxes ---------- */
QGroupBox { QGroupBox {
background: transparent; background: transparent;
color: #c0caf5; color: ${text};
border: 1px solid #24283b; border: 1px solid ${border};
border-radius: 4px; border-radius: 4px;
margin-top: 10px; margin-top: 10px;
padding-top: 8px; padding-top: 8px;
@ -455,27 +462,27 @@ QGroupBox::title {
subcontrol-origin: margin; subcontrol-origin: margin;
subcontrol-position: top left; subcontrol-position: top left;
padding: 0 6px; padding: 0 6px;
color: #a9b1d6; color: ${text_dim};
} }
/* ---------- Frames ---------- */ /* ---------- Frames ---------- */
QFrame[frameShape="4"], /* HLine */ QFrame[frameShape="4"], /* HLine */
QFrame[frameShape="5"] /* VLine */ { QFrame[frameShape="5"] /* VLine */ {
background: #24283b; background: ${border};
color: #24283b; color: ${border};
} }
/* ---------- Toolbars ---------- */ /* ---------- Toolbars ---------- */
QToolBar { QToolBar {
background: #1a1b26; background: ${bg};
border: none; border: none;
spacing: 4px; spacing: 4px;
padding: 2px; padding: 2px;
} }
QToolBar::separator { QToolBar::separator {
background: #24283b; background: ${border};
width: 1px; width: 1px;
margin: 4px 4px; margin: 4px 4px;
} }
@ -483,31 +490,25 @@ QToolBar::separator {
/* ---------- Dock widgets ---------- */ /* ---------- Dock widgets ---------- */
QDockWidget { QDockWidget {
color: #c0caf5; color: ${text};
titlebar-close-icon: none; titlebar-close-icon: none;
} }
QDockWidget::title { QDockWidget::title {
background: #24283b; background: ${bg_subtle};
padding: 4px; padding: 4px;
border: 1px solid #24283b; border: 1px solid ${border};
} }
/* ---------- Rubber band (multi-select drag rectangle) ---------- */ /* ---------- Rubber band (multi-select drag rectangle) ---------- */
QRubberBand { QRubberBand {
background: #7aa2f7; background: ${accent};
border: 1px solid #7aa2f7; border: 1px solid ${accent};
/* Qt blends rubber band at ~30% so this reads as a translucent /* Qt blends rubber band at ~30% so this reads as a translucent
* accent-tinted rectangle without needing rgba(). */ * accent-tinted rectangle without needing rgba(). */
} }
/* ---------- Popout & preview overlay controls ---------- */
QWidget#_preview_controls,
QWidget#_slideshow_toolbar,
QWidget#_slideshow_controls {
background: rgba(0, 0, 0, 160);
}
/* ---------- Thumbnail dot indicators (Qt properties on ThumbnailWidget) ---------- */ /* ---------- Thumbnail dot indicators (Qt properties on ThumbnailWidget) ---------- */
@ -519,10 +520,78 @@ ThumbnailWidget {
/* ---------- Info panel tag category colors ---------- */ /* ---------- Info panel tag category colors ---------- */
InfoPanel { InfoPanel {
qproperty-tagArtistColor: #e0af68; qproperty-tagArtistColor: ${warning};
qproperty-tagCharacterColor: #9ece6a; qproperty-tagCharacterColor: ${success};
qproperty-tagCopyrightColor: #7aa2f7; qproperty-tagCopyrightColor: ${accent};
qproperty-tagSpeciesColor: #f7768e; qproperty-tagSpeciesColor: ${danger};
qproperty-tagMetaColor: #a9b1d6; qproperty-tagMetaColor: ${text_dim};
qproperty-tagLoreColor: #a9b1d6; qproperty-tagLoreColor: ${text_dim};
}
/* ---------- Video player letterbox / pillarbox color (mpv background) ---------- */
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.
*/
QWidget#_slideshow_toolbar,
QWidget#_slideshow_controls,
QWidget#_preview_controls {
background: ${overlay_bg};
}
QWidget#_slideshow_toolbar *,
QWidget#_slideshow_controls *,
QWidget#_preview_controls * {
background: transparent;
color: white;
border: none;
}
QWidget#_slideshow_toolbar QPushButton,
QWidget#_slideshow_controls QPushButton,
QWidget#_preview_controls QPushButton {
background: transparent;
color: white;
border: 1px solid rgba(255, 255, 255, 80);
padding: 2px 6px;
}
QWidget#_slideshow_toolbar QPushButton:hover,
QWidget#_slideshow_controls QPushButton:hover,
QWidget#_preview_controls QPushButton:hover {
background: rgba(255, 255, 255, 30);
}
QWidget#_slideshow_toolbar QSlider::groove:horizontal,
QWidget#_slideshow_controls QSlider::groove:horizontal,
QWidget#_preview_controls QSlider::groove:horizontal {
background: rgba(255, 255, 255, 40);
height: 4px;
}
QWidget#_slideshow_toolbar QSlider::handle:horizontal,
QWidget#_slideshow_controls QSlider::handle:horizontal,
QWidget#_preview_controls QSlider::handle:horizontal {
background: ${accent};
width: 10px;
margin: -4px 0;
}
QWidget#_slideshow_toolbar QSlider::sub-page:horizontal,
QWidget#_slideshow_controls QSlider::sub-page:horizontal,
QWidget#_preview_controls QSlider::sub-page:horizontal {
background: ${accent};
}
QWidget#_slideshow_toolbar QLabel,
QWidget#_slideshow_controls QLabel,
QWidget#_preview_controls QLabel {
background: transparent;
color: white;
} }