UI overhaul: icon buttons, video controls, popout anchor, layout flip, compact top bar

- Preview/popout toolbar: icon buttons (☆/★, ↓/✕, ⊘, ⊗, ⧉) with QSS
  object names (#_tb_bookmark, #_tb_save, etc.) for theme targeting
- Video controls: QPainter-drawn icons for play/pause, volume/mute;
  text labels for loop/once/next and autoplay
- Popout anchor setting: resize pivot (center/tl/tr/bl/br) controls
  which corner stays fixed on aspect change, works on all platforms
- Hyprland monitor reserved areas: reads waybar exclusive zones from
  hyprctl monitors -j for correct edge positioning
- Layout flip setting: swap grid and preview sides
- Compact top bar: AdjustToContents combos, tighter spacing, named
  containers (#_top_bar, #_nav_bar) for QSS targeting
- Reduced main window minimum size from 900x600 to 740x400
- Trimmed bundled QSS: removed 12 unused widget selectors, added
  popout overlay font-weight/size, regenerated all 12 theme files
- Updated themes/README.md with icon button reference
This commit is contained in:
pax 2026-04-10 19:58:11 -05:00
parent d7b3c304d7
commit 93459dfff6
22 changed files with 895 additions and 2025 deletions

View File

@ -119,6 +119,8 @@ QWidget#_slideshow_controls QPushButton {
color: white;
border: 1px solid rgba(255, 255, 255, 80);
padding: 2px 6px;
font-size: 15px;
font-weight: bold;
}
QWidget#_slideshow_toolbar QPushButton:hover,
QWidget#_slideshow_controls QPushButton:hover {

View File

@ -63,7 +63,7 @@ class BooruApp(QMainWindow):
def __init__(self) -> None:
super().__init__()
self.setWindowTitle("booru-viewer")
self.setMinimumSize(900, 600)
self.setMinimumSize(740, 400)
self.resize(1200, 800)
self._db = Database()
@ -224,17 +224,22 @@ class BooruApp(QMainWindow):
layout.setSpacing(6)
# Top bar: site selector + rating + search
top = QHBoxLayout()
_top_bar = QWidget()
_top_bar.setObjectName("_top_bar")
top = QHBoxLayout(_top_bar)
top.setContentsMargins(0, 0, 0, 0)
top.setSpacing(3)
self._site_combo = QComboBox()
self._site_combo.setMinimumWidth(150)
self._site_combo.setMinimumWidth(80)
self._site_combo.setSizeAdjustPolicy(QComboBox.SizeAdjustPolicy.AdjustToContents)
self._site_combo.currentIndexChanged.connect(self._on_site_changed)
top.addWidget(self._site_combo)
# Rating filter
self._rating_combo = QComboBox()
self._rating_combo.addItems(["All", "General", "Sensitive", "Questionable", "Explicit"])
self._rating_combo.setMinimumWidth(100)
self._rating_combo.setSizeAdjustPolicy(QComboBox.SizeAdjustPolicy.AdjustToContents)
self._rating_combo.currentTextChanged.connect(self._on_rating_changed)
top.addWidget(self._rating_combo)
@ -242,23 +247,17 @@ class BooruApp(QMainWindow):
self._media_filter = QComboBox()
self._media_filter.addItems(["All", "Animated", "Video", "GIF", "Audio"])
self._media_filter.setToolTip("Filter by media type")
self._media_filter.setFixedWidth(90)
self._media_filter.setSizeAdjustPolicy(QComboBox.SizeAdjustPolicy.AdjustToContents)
top.addWidget(self._media_filter)
# Score filter — type the value directly. Spinbox arrows hidden
# since the field is small enough to type into and the +/- buttons
# were just visual noise. setFixedHeight(23) overrides Qt's
# QSpinBox sizeHint which still reserves vertical space for the
# arrow buttons internally even when `setButtonSymbols(NoButtons)`
# is set, leaving the spinbox 3px taller than the surrounding
# combos/inputs/buttons in the top toolbar (26 vs 23).
score_label = QLabel("Score≥")
# Score filter
score_label = QLabel("Score\u2265")
top.addWidget(score_label)
self._score_spin = QSpinBox()
self._score_spin.setRange(0, 99999)
self._score_spin.setValue(0)
self._score_spin.setFixedWidth(40)
self._score_spin.setFixedHeight(23)
self._score_spin.setFixedWidth(36)
self._score_spin.setFixedHeight(21)
self._score_spin.setButtonSymbols(QSpinBox.ButtonSymbols.NoButtons)
top.addWidget(self._score_spin)
@ -267,8 +266,8 @@ class BooruApp(QMainWindow):
self._page_spin = QSpinBox()
self._page_spin.setRange(1, 99999)
self._page_spin.setValue(1)
self._page_spin.setFixedWidth(40)
self._page_spin.setFixedHeight(23) # match the surrounding 23px row
self._page_spin.setFixedWidth(36)
self._page_spin.setFixedHeight(21)
self._page_spin.setButtonSymbols(QSpinBox.ButtonSymbols.NoButtons)
top.addWidget(self._page_spin)
@ -277,10 +276,15 @@ class BooruApp(QMainWindow):
self._search_bar.autocomplete_requested.connect(self._search_ctrl.request_autocomplete)
top.addWidget(self._search_bar, stretch=1)
layout.addLayout(top)
layout.addWidget(_top_bar)
# Nav bar
nav = QHBoxLayout()
_nav_bar = QWidget()
_nav_bar.setObjectName("_nav_bar")
nav = QHBoxLayout(_nav_bar)
nav.setContentsMargins(0, 0, 0, 0)
nav.setSpacing(3)
self._browse_btn = QPushButton("Browse")
self._browse_btn.setCheckable(True)
self._browse_btn.setChecked(True)
@ -294,11 +298,10 @@ class BooruApp(QMainWindow):
self._library_btn = QPushButton("Library")
self._library_btn.setCheckable(True)
self._library_btn.setFixedWidth(80)
self._library_btn.clicked.connect(lambda: self._switch_view(2))
nav.addWidget(self._library_btn)
layout.addLayout(nav)
layout.addWidget(_nav_bar)
# Main content
self._splitter = QSplitter(Qt.Orientation.Horizontal)
@ -359,7 +362,7 @@ class BooruApp(QMainWindow):
# BL Post, [stretch], Popout) has room to lay out all five buttons
# at their fixed widths plus spacing without clipping the rightmost
# one or compressing the row visually.
self._preview.setMinimumWidth(380)
self._preview.setMinimumWidth(200)
right.addWidget(self._preview)
self._dl_progress = QProgressBar()
@ -405,6 +408,10 @@ class BooruApp(QMainWindow):
self._splitter.addWidget(right)
# Flip layout: preview on the left, grid on the right
if self._db.get_setting_bool("flip_layout"):
self._splitter.insertWidget(0, right)
# Restore the persisted main-splitter sizes if present, otherwise
# fall back to the historic default. The sizes are saved as a
# comma-separated string in the settings table — same format as

View File

@ -6,12 +6,91 @@ import logging
import os
from pathlib import Path
from PySide6.QtCore import Qt, QTimer, Signal, Property
from PySide6.QtGui import QColor
from PySide6.QtCore import Qt, QTimer, Signal, Property, QPoint
from PySide6.QtGui import QColor, QIcon, QPixmap, QPainter, QPen, QBrush, QPolygon, QPainterPath
from PySide6.QtWidgets import (
QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QSlider, QStyle,
QApplication,
)
def _paint_icon(shape: str, color: QColor, size: int = 16) -> QIcon:
"""Paint a media control icon using the given color."""
pix = QPixmap(size, size)
pix.fill(Qt.GlobalColor.transparent)
p = QPainter(pix)
p.setRenderHint(QPainter.RenderHint.Antialiasing)
p.setPen(Qt.PenStyle.NoPen)
p.setBrush(color)
s = size
if shape == "play":
p.drawPolygon(QPolygon([QPoint(3, 2), QPoint(3, s - 2), QPoint(s - 2, s // 2)]))
elif shape == "pause":
w = max(2, s // 4)
p.drawRect(2, 2, w, s - 4)
p.drawRect(s - 2 - w, 2, w, s - 4)
elif shape == "volume":
# Speaker cone
p.drawPolygon(QPolygon([
QPoint(1, s // 2 - 2), QPoint(4, s // 2 - 2),
QPoint(8, 2), QPoint(8, s - 2),
QPoint(4, s // 2 + 2), QPoint(1, s // 2 + 2),
]))
# Sound waves
p.setPen(QPen(color, 1.5))
p.setBrush(Qt.BrushStyle.NoBrush)
path = QPainterPath()
path.arcMoveTo(8, 3, 6, s - 6, 45)
path.arcTo(8, 3, 6, s - 6, 45, -90)
p.drawPath(path)
elif shape == "muted":
p.drawPolygon(QPolygon([
QPoint(1, s // 2 - 2), QPoint(4, s // 2 - 2),
QPoint(8, 2), QPoint(8, s - 2),
QPoint(4, s // 2 + 2), QPoint(1, s // 2 + 2),
]))
p.setPen(QPen(color, 2))
p.drawLine(10, 4, s - 2, s - 4)
p.drawLine(10, s - 4, s - 2, 4)
elif shape == "loop":
p.setPen(QPen(color, 1.5))
p.setBrush(Qt.BrushStyle.NoBrush)
path = QPainterPath()
path.arcMoveTo(2, 2, s - 4, s - 4, 30)
path.arcTo(2, 2, s - 4, s - 4, 30, 300)
p.drawPath(path)
# Arrowhead
p.setPen(Qt.PenStyle.NoPen)
p.setBrush(color)
end = path.currentPosition().toPoint()
p.drawPolygon(QPolygon([
end, QPoint(end.x() - 4, end.y() - 3), QPoint(end.x() + 1, end.y() - 4),
]))
elif shape == "once":
p.setPen(QPen(color, 2))
p.setBrush(Qt.BrushStyle.NoBrush)
mid = s // 2
p.drawLine(mid, 3, mid, s - 3)
p.drawLine(mid - 2, 5, mid, 3)
elif shape == "next":
p.drawPolygon(QPolygon([QPoint(2, 2), QPoint(2, s - 2), QPoint(s - 5, s // 2)]))
p.drawRect(s - 4, 2, 2, s - 4)
elif shape == "auto":
mid = s // 2
p.drawPolygon(QPolygon([QPoint(1, 3), QPoint(1, s - 3), QPoint(mid - 1, s // 2)]))
p.drawPolygon(QPolygon([QPoint(mid, 3), QPoint(mid, s - 3), QPoint(s - 2, s // 2)]))
p.end()
return QIcon(pix)
import mpv as mpvlib
log = logging.getLogger(__name__)
@ -119,15 +198,22 @@ class VideoPlayer(QWidget):
controls = QHBoxLayout(self._controls_bar)
controls.setContentsMargins(4, 2, 4, 2)
# Compact-padding override matches the top preview toolbar so the
# bottom controls bar reads as part of the same panel rather than
# as a stamped-in overlay. Bundled themes' default `padding: 5px 12px`
# is too wide for short labels in narrow button slots.
_ctrl_btn_style = "padding: 2px 6px;"
_btn_sz = 24
_fg = self.palette().buttonText().color()
self._play_btn = QPushButton("Play")
self._play_btn.setMaximumWidth(65)
self._play_btn.setStyleSheet(_ctrl_btn_style)
def _icon_btn(shape: str, name: str, tip: str) -> QPushButton:
btn = QPushButton()
btn.setObjectName(name)
btn.setIcon(_paint_icon(shape, _fg))
btn.setFixedSize(_btn_sz, _btn_sz)
btn.setToolTip(tip)
return btn
self._icon_fg = _fg
self._play_icon = _paint_icon("play", _fg)
self._pause_icon = _paint_icon("pause", _fg)
self._play_btn = _icon_btn("play", "_ctrl_play", "Play / Pause (Space)")
self._play_btn.clicked.connect(self._toggle_play)
controls.addWidget(self._play_btn)
@ -152,28 +238,29 @@ class VideoPlayer(QWidget):
self._vol_slider.valueChanged.connect(self._set_volume)
controls.addWidget(self._vol_slider)
self._mute_btn = QPushButton("Mute")
self._mute_btn.setMaximumWidth(80)
self._mute_btn.setStyleSheet(_ctrl_btn_style)
self._vol_icon = _paint_icon("volume", _fg)
self._muted_icon = _paint_icon("muted", _fg)
self._mute_btn = _icon_btn("volume", "_ctrl_mute", "Mute / Unmute")
self._mute_btn.clicked.connect(self._toggle_mute)
controls.addWidget(self._mute_btn)
self._autoplay = True
self._autoplay_btn = QPushButton("Auto")
self._autoplay_btn.setMaximumWidth(70)
self._autoplay_btn.setStyleSheet(_ctrl_btn_style)
self._auto_icon = _paint_icon("auto", _fg)
self._autoplay_btn = _icon_btn("auto", "_ctrl_autoplay", "Auto-play videos when selected")
self._autoplay_btn.setCheckable(True)
self._autoplay_btn.setChecked(True)
self._autoplay_btn.setToolTip("Auto-play videos when selected")
self._autoplay_btn.clicked.connect(self._toggle_autoplay)
self._autoplay_btn.hide()
controls.addWidget(self._autoplay_btn)
self._loop_icons = {
0: _paint_icon("loop", _fg),
1: _paint_icon("once", _fg),
2: _paint_icon("next", _fg),
}
self._loop_state = 0 # 0=Loop, 1=Once, 2=Next
self._loop_btn = QPushButton("Loop")
self._loop_btn.setMaximumWidth(60)
self._loop_btn.setStyleSheet(_ctrl_btn_style)
self._loop_btn.setToolTip("Loop: repeat / Once: stop at end / Next: advance")
self._loop_btn = _icon_btn("loop", "_ctrl_loop", "Loop / Once / Next")
self._loop_btn.clicked.connect(self._cycle_loop)
controls.addWidget(self._loop_btn)
@ -295,7 +382,7 @@ class VideoPlayer(QWidget):
self._pending_mute = val
if self._mpv:
self._mpv.mute = val
self._mute_btn.setText("Unmute" if val else "Mute")
self._mute_btn.setIcon(self._muted_icon if val else self._vol_icon)
@property
def autoplay(self) -> bool:
@ -314,8 +401,9 @@ class VideoPlayer(QWidget):
@loop_state.setter
def loop_state(self, val: int) -> None:
self._loop_state = val
labels = ["Loop", "Once", "Next"]
self._loop_btn.setText(labels[val])
tips = ["Loop: repeat", "Once: stop at end", "Next: advance"]
self._loop_btn.setIcon(self._loop_icons[val])
self._loop_btn.setToolTip(tips[val])
self._autoplay_btn.setVisible(val == 2)
self._apply_loop_to_mpv()
@ -386,7 +474,7 @@ class VideoPlayer(QWidget):
m.pause = False
else:
m.pause = True
self._play_btn.setText("Pause" if not m.pause else "Play")
self._play_btn.setIcon(self._pause_icon if not m.pause else self._play_icon)
self._poll_timer.start()
def stop(self) -> None:
@ -397,17 +485,17 @@ class VideoPlayer(QWidget):
self._time_label.setText("0:00")
self._duration_label.setText("0:00")
self._seek_slider.setRange(0, 0)
self._play_btn.setText("Play")
self._play_btn.setIcon(self._play_icon)
def pause(self) -> None:
if self._mpv:
self._mpv.pause = True
self._play_btn.setText("Play")
self._play_btn.setIcon(self._play_icon)
def resume(self) -> None:
if self._mpv:
self._mpv.pause = False
self._play_btn.setText("Pause")
self._play_btn.setIcon(self._pause_icon)
# -- Internal controls --
@ -415,11 +503,12 @@ class VideoPlayer(QWidget):
if not self._mpv:
return
self._mpv.pause = not self._mpv.pause
self._play_btn.setText("Play" if self._mpv.pause else "Pause")
self._play_btn.setIcon(self._play_icon if self._mpv.pause else self._pause_icon)
def _toggle_autoplay(self, checked: bool = True) -> None:
self._autoplay = self._autoplay_btn.isChecked()
self._autoplay_btn.setText("Autoplay" if self._autoplay else "Manual")
self._autoplay_btn.setIcon(self._auto_icon if self._autoplay else self._play_icon)
self._autoplay_btn.setToolTip("Autoplay on" if self._autoplay else "Autoplay off")
def _cycle_loop(self) -> None:
self.loop_state = (self._loop_state + 1) % 3
@ -463,7 +552,7 @@ class VideoPlayer(QWidget):
if self._mpv:
self._mpv.mute = not self._mpv.mute
self._pending_mute = bool(self._mpv.mute)
self._mute_btn.setText("Unmute" if self._mpv.mute else "Mute")
self._mute_btn.setIcon(self._muted_icon if self._mpv.mute else self._vol_icon)
# -- mpv callbacks (called from mpv thread) --
@ -528,9 +617,9 @@ class VideoPlayer(QWidget):
# Pause state
paused = self._mpv.pause
expected_text = "Play" if paused else "Pause"
if self._play_btn.text() != expected_text:
self._play_btn.setText(expected_text)
expected_icon = self._play_icon if paused else self._pause_icon
if self._play_btn.icon().cacheKey() != expected_icon.cacheKey():
self._play_btn.setIcon(expected_icon)
# Video size (set by observer on mpv thread, emitted here on main thread)
if self._pending_video_size is not None:

View File

@ -171,8 +171,48 @@ def _dispatch_batch(cmds: list[str]) -> None:
pass
def get_monitor_available_rect(monitor_id: int | None = None) -> tuple[int, int, int, int] | None:
"""Return (x, y, w, h) of a monitor's usable area, accounting for
exclusive zones (Waybar, etc.) via the ``reserved`` field.
Falls back to the first monitor if *monitor_id* is None or not found.
Returns None if not on Hyprland or the query fails.
"""
if not _on_hyprland():
return None
try:
result = subprocess.run(
["hyprctl", "monitors", "-j"],
capture_output=True, text=True, timeout=1,
)
monitors = json.loads(result.stdout)
if not monitors:
return None
mon = None
if monitor_id is not None:
mon = next((m for m in monitors if m.get("id") == monitor_id), None)
if mon is None:
mon = monitors[0]
mx = mon.get("x", 0)
my = mon.get("y", 0)
mw = mon.get("width", 0)
mh = mon.get("height", 0)
# reserved: [left, top, right, bottom]
res = mon.get("reserved", [0, 0, 0, 0])
left, top, right, bottom = res[0], res[1], res[2], res[3]
return (
mx + left,
my + top,
mw - left - right,
mh - top - bottom,
)
except Exception:
return None
__all__ = [
"get_window",
"get_monitor_available_rect",
"resize",
"resize_and_move",
]

View File

@ -8,19 +8,45 @@ from typing import NamedTuple
class Viewport(NamedTuple):
"""Where and how large the user wants popout content to appear.
Three numbers, no aspect. Aspect is a property of the currently-
displayed post and is recomputed from actual content on every
navigation. The viewport stays put across navigations; the window
rect is a derived projection (Viewport, content_aspect) (x,y,w,h).
Three numbers + an anchor mode, no aspect. Aspect is a property of
the currently-displayed post and is recomputed from actual content
on every navigation. The viewport stays put across navigations; the
window rect is a derived projection (Viewport, content_aspect)
(x,y,w,h).
`long_side` is the binding edge length: for landscape it becomes
width, for portrait it becomes height. Symmetric across the two
orientations, which is the property that breaks the
width-anchor ratchet that the previous `_fit_to_content` had.
`anchor` controls which point of the window stays fixed across
navigations as the window size changes with aspect ratio:
``"center"`` (default) pins the window center; ``"tl"``/``"tr"``/
``"bl"``/``"br"`` pin the corresponding corner. The window
grows/shrinks away from the anchored corner. The user can drag the
window anywhere the anchor only affects resize direction, not
screen position.
`center_x`/`center_y` hold the anchor point coordinates (center
of the window in center mode, the pinned corner in corner modes).
"""
center_x: float
center_y: float
long_side: float
anchor: str = "center"
def anchor_point(x: float, y: float, w: float, h: float, anchor: str) -> tuple[float, float]:
"""Extract the anchor point from a window rect based on anchor mode."""
if anchor == "tl":
return (x, y)
if anchor == "tr":
return (x + w, y)
if anchor == "bl":
return (x, y + h)
if anchor == "br":
return (x + w, y + h)
return (x + w / 2, y + h / 2)
# Maximum drift between our last-dispatched window rect and the current

View File

@ -54,7 +54,7 @@ from .state import (
WindowMoved,
WindowResized,
)
from .viewport import Viewport, _DRIFT_TOLERANCE
from .viewport import Viewport, _DRIFT_TOLERANCE, anchor_point
# Adapter logger — separate from the popout's main `booru` logger so
@ -121,10 +121,11 @@ class FullscreenPreview(QMainWindow):
privacy_requested = Signal()
closed = Signal()
def __init__(self, grid_cols: int = 3, show_actions: bool = True, monitor: str = "", parent=None) -> None:
def __init__(self, grid_cols: int = 3, show_actions: bool = True, monitor: str = "", anchor: str = "center", parent=None) -> None:
super().__init__(parent, Qt.WindowType.Window)
self.setWindowTitle("booru-viewer — Popout")
self._grid_cols = grid_cols
self._anchor = anchor
# Central widget — media fills the entire window
central = QWidget()
@ -181,10 +182,6 @@ class FullscreenPreview(QMainWindow):
toolbar.setContentsMargins(8, 4, 8, 4)
# Same compact-padding override as the embedded preview toolbar —
# bundled themes' default `padding: 5px 12px` is too wide for these
# short labels in narrow fixed slots.
_tb_btn_style = "padding: 2px 6px;"
# Bookmark folders for the popout's Bookmark-as submenu — wired
# by app.py via set_bookmark_folders_callback after construction.
self._bookmark_folders_callback = None
@ -195,30 +192,29 @@ class FullscreenPreview(QMainWindow):
# are independent name spaces and need separate callbacks.
self._folders_callback = None
self._bookmark_btn = QPushButton("Bookmark")
self._bookmark_btn.setMaximumWidth(90)
self._bookmark_btn.setStyleSheet(_tb_btn_style)
_tb_sz = 24
def _icon_btn(text: str, name: str, tip: str) -> QPushButton:
btn = QPushButton(text)
btn.setObjectName(name)
btn.setFixedSize(_tb_sz, _tb_sz)
btn.setToolTip(tip)
return btn
self._bookmark_btn = _icon_btn("\u2606", "_tb_bookmark", "Bookmark (B)")
self._bookmark_btn.clicked.connect(self._on_bookmark_clicked)
toolbar.addWidget(self._bookmark_btn)
self._save_btn = QPushButton("Save")
self._save_btn.setMaximumWidth(70)
self._save_btn.setStyleSheet(_tb_btn_style)
self._save_btn = _icon_btn("\u2193", "_tb_save", "Save to library (S)")
self._save_btn.clicked.connect(self._on_save_clicked)
toolbar.addWidget(self._save_btn)
self._is_saved = False
self._bl_tag_btn = QPushButton("BL Tag")
self._bl_tag_btn.setMaximumWidth(65)
self._bl_tag_btn.setStyleSheet(_tb_btn_style)
self._bl_tag_btn.setToolTip("Blacklist a tag")
self._bl_tag_btn = _icon_btn("\u2298", "_tb_bl_tag", "Blacklist a tag")
self._bl_tag_btn.clicked.connect(self._show_bl_tag_menu)
toolbar.addWidget(self._bl_tag_btn)
self._bl_post_btn = QPushButton("BL Post")
self._bl_post_btn.setMaximumWidth(70)
self._bl_post_btn.setStyleSheet(_tb_btn_style)
self._bl_post_btn.setToolTip("Blacklist this post")
self._bl_post_btn = _icon_btn("\u2297", "_tb_bl_post", "Blacklist this post")
self._bl_post_btn.clicked.connect(self.blacklist_post_requested)
toolbar.addWidget(self._bl_post_btn)
@ -642,10 +638,11 @@ class FullscreenPreview(QMainWindow):
def update_state(self, bookmarked: bool, saved: bool) -> None:
self._is_bookmarked = bookmarked
self._bookmark_btn.setText("Unbookmark" if bookmarked else "Bookmark")
self._bookmark_btn.setMaximumWidth(90 if bookmarked else 80)
self._bookmark_btn.setText("\u2605" if bookmarked else "\u2606") # ★ / ☆
self._bookmark_btn.setToolTip("Unbookmark (B)" if bookmarked else "Bookmark (B)")
self._is_saved = saved
self._save_btn.setText("Unsave" if saved else "Save")
self._save_btn.setText("\u2715" if saved else "\u2193") # ✕ / ⤓
self._save_btn.setToolTip("Unsave from library" if saved else "Save to library (S)")
# ------------------------------------------------------------------
# Public method interface (commit 15)
@ -1054,7 +1051,8 @@ class FullscreenPreview(QMainWindow):
@staticmethod
def _compute_window_rect(
viewport: Viewport, content_aspect: float, screen
viewport: Viewport, content_aspect: float, screen,
avail_override: tuple[int, int, int, int] | None = None,
) -> tuple[int, int, int, int]:
"""Project a viewport onto a window rect for the given content aspect.
@ -1064,6 +1062,16 @@ class FullscreenPreview(QMainWindow):
if either would exceed its 0.90-of-screen ceiling, preserving
aspect exactly. Pure function no side effects, no widget
access, all inputs explicit so it's trivial to reason about.
``viewport.center_x``/``center_y`` hold the anchor point the
window center in ``"center"`` mode, or the pinned corner in
corner modes. The anchor stays fixed; the window grows/shrinks
away from it.
*avail_override* is an (x, y, w, h) tuple that replaces
``screen.availableGeometry()`` used on Hyprland where Qt
doesn't see Waybar's exclusive zone but ``hyprctl monitors -j``
reports it via the ``reserved`` array.
"""
if content_aspect >= 1.0: # landscape or square
w = viewport.long_side
@ -1072,19 +1080,37 @@ class FullscreenPreview(QMainWindow):
h = viewport.long_side
w = viewport.long_side * content_aspect
avail = screen.availableGeometry()
cap_w = avail.width() * 0.90
cap_h = avail.height() * 0.90
if avail_override:
ax, ay, aw, ah = avail_override
else:
_a = screen.availableGeometry()
ax, ay, aw, ah = _a.x(), _a.y(), _a.width(), _a.height()
cap_w = aw * 0.90
cap_h = ah * 0.90
scale = min(1.0, cap_w / w, cap_h / h)
w *= scale
h *= scale
x = viewport.center_x - w / 2
y = viewport.center_y - h / 2
anchor = viewport.anchor
if anchor == "tl":
x = viewport.center_x
y = viewport.center_y
elif anchor == "tr":
x = viewport.center_x - w
y = viewport.center_y
elif anchor == "bl":
x = viewport.center_x
y = viewport.center_y - h
elif anchor == "br":
x = viewport.center_x - w
y = viewport.center_y - h
else:
x = viewport.center_x - w / 2
y = viewport.center_y - h / 2
# Nudge onto screen if the projected rect would land off-edge.
x = max(avail.x(), min(x, avail.right() - w))
y = max(avail.y(), min(y, avail.bottom() - h))
# Nudge onto screen if the window would land off-edge.
x = max(ax, min(x, ax + aw - w))
y = max(ay, min(y, ay + ah - h))
return (round(x), round(y), round(w), round(h))
@ -1110,18 +1136,20 @@ class FullscreenPreview(QMainWindow):
if win and win.get("at") and win.get("size"):
wx, wy = win["at"]
ww, wh = win["size"]
ax, ay = anchor_point(wx, wy, ww, wh, self._anchor)
return Viewport(
center_x=wx + ww / 2,
center_y=wy + wh / 2,
center_x=ax, center_y=ay,
long_side=float(max(ww, wh)),
anchor=self._anchor,
)
if floating is None:
rect = self.geometry()
if rect.width() > 0 and rect.height() > 0:
ax, ay = anchor_point(rect.x(), rect.y(), rect.width(), rect.height(), self._anchor)
return Viewport(
center_x=rect.x() + rect.width() / 2,
center_y=rect.y() + rect.height() / 2,
center_x=ax, center_y=ay,
long_side=float(max(rect.width(), rect.height())),
anchor=self._anchor,
)
return None
@ -1162,10 +1190,11 @@ class FullscreenPreview(QMainWindow):
if self._first_fit_pending and self._pending_size and self._pending_position_restore:
pw, ph = self._pending_size
px, py = self._pending_position_restore
ax, ay = anchor_point(px, py, pw, ph, self._anchor)
self._viewport = Viewport(
center_x=px + pw / 2,
center_y=py + ph / 2,
center_x=ax, center_y=ay,
long_side=float(max(pw, ph)),
anchor=self._anchor,
)
return self._viewport
@ -1192,10 +1221,11 @@ class FullscreenPreview(QMainWindow):
)
if drift > _DRIFT_TOLERANCE:
# External move/resize detected. Adopt current as intent.
ax, ay = anchor_point(cur_x, cur_y, cur_w, cur_h, self._anchor)
self._viewport = Viewport(
center_x=cur_x + cur_w / 2,
center_y=cur_y + cur_h / 2,
center_x=ax, center_y=ay,
long_side=float(max(cur_w, cur_h)),
anchor=self._anchor,
)
return self._viewport
@ -1260,7 +1290,10 @@ class FullscreenPreview(QMainWindow):
# the one-shots would lose the saved position; leaving them
# set lets a subsequent fit retry.
return
x, y, w, h = self._compute_window_rect(viewport, aspect, screen)
avail_rect = None
if on_hypr and win:
avail_rect = hyprland.get_monitor_available_rect(win.get("monitor"))
x, y, w, h = self._compute_window_rect(viewport, aspect, screen, avail_override=avail_rect)
# Identical-rect skip. If the computed rect is exactly what
# we last dispatched, the window is already in that state and
# there's nothing for hyprctl (or setGeometry) to do. Skipping
@ -1472,19 +1505,21 @@ class FullscreenPreview(QMainWindow):
x, y = win["at"]
w, h = win["size"]
self._windowed_geometry = QRect(x, y, w, h)
ax, ay = anchor_point(x, y, w, h, self._anchor)
self._viewport = Viewport(
center_x=x + w / 2,
center_y=y + h / 2,
center_x=ax, center_y=ay,
long_side=float(max(w, h)),
anchor=self._anchor,
)
else:
self._windowed_geometry = self.frameGeometry()
rect = self._windowed_geometry
if rect.width() > 0 and rect.height() > 0:
ax, ay = anchor_point(rect.x(), rect.y(), rect.width(), rect.height(), self._anchor)
self._viewport = Viewport(
center_x=rect.x() + rect.width() / 2,
center_y=rect.y() + rect.height() / 2,
center_x=ax, center_y=ay,
long_side=float(max(rect.width(), rect.height())),
anchor=self._anchor,
)
self.showFullScreen()
@ -1581,10 +1616,11 @@ class FullscreenPreview(QMainWindow):
return
rect = self.geometry()
if rect.width() > 0 and rect.height() > 0:
ax, ay = anchor_point(rect.x(), rect.y(), rect.width(), rect.height(), self._anchor)
self._viewport = Viewport(
center_x=rect.x() + rect.width() / 2,
center_y=rect.y() + rect.height() / 2,
center_x=ax, center_y=ay,
long_side=float(max(rect.width(), rect.height())),
anchor=self._anchor,
)
# Parallel state machine dispatch for the same event.
self._dispatch_and_apply(WindowResized(rect=(
@ -1611,11 +1647,12 @@ class FullscreenPreview(QMainWindow):
rect = self.geometry()
if rect.width() > 0 and rect.height() > 0:
# Move-only update: keep the existing long_side, just
# update the center to where the window now sits.
# update the anchor point to where the window now sits.
ax, ay = anchor_point(rect.x(), rect.y(), rect.width(), rect.height(), self._anchor)
self._viewport = Viewport(
center_x=rect.x() + rect.width() / 2,
center_y=rect.y() + rect.height() / 2,
center_x=ax, center_y=ay,
long_side=self._viewport.long_side,
anchor=self._anchor,
)
# Parallel state machine dispatch for the same event.
self._dispatch_and_apply(WindowMoved(rect=(

View File

@ -90,7 +90,8 @@ class PopoutController:
cols = self._app._grid._flow.columns
show_actions = self._app._stack.currentIndex() != 2
monitor = self._app._db.get_setting("slideshow_monitor")
self._fullscreen_window = FullscreenPreview(grid_cols=cols, show_actions=show_actions, monitor=monitor, parent=self._app)
anchor = self._app._db.get_setting("popout_anchor") or "center"
self._fullscreen_window = FullscreenPreview(grid_cols=cols, show_actions=show_actions, monitor=monitor, anchor=anchor, parent=self._app)
self._fullscreen_window.navigate.connect(self.navigate)
self._fullscreen_window.play_next_requested.connect(self._app._on_video_end_next)
from ..core.config import library_folders

View File

@ -64,50 +64,34 @@ class ImagePreview(QWidget):
tb.setContentsMargins(4, 1, 4, 1)
tb.setSpacing(4)
# Compact toolbar buttons. The bundled themes set
# `QPushButton { padding: 5px 12px }` which eats 24px of horizontal
# space — too much for these short labels in fixed-width slots.
# Override with tighter padding inline so the labels (Unbookmark,
# Unsave, BL Tag, BL Post, Popout) fit cleanly under any theme.
# Same pattern as the search-bar score buttons in app.py and the
# settings dialog spinbox +/- buttons.
_tb_btn_style = "padding: 2px 6px;"
_tb_sz = 24
self._bookmark_btn = QPushButton("Bookmark")
self._bookmark_btn.setFixedWidth(100)
self._bookmark_btn.setStyleSheet(_tb_btn_style)
def _icon_btn(text: str, name: str, tip: str) -> QPushButton:
btn = QPushButton(text)
btn.setObjectName(name)
btn.setFixedSize(_tb_sz, _tb_sz)
btn.setToolTip(tip)
return btn
self._bookmark_btn = _icon_btn("\u2606", "_tb_bookmark", "Bookmark (B)")
self._bookmark_btn.clicked.connect(self._on_bookmark_clicked)
tb.addWidget(self._bookmark_btn)
self._save_btn = QPushButton("Save")
# 75 fits "Unsave" (6 chars) cleanly across every bundled theme.
# The previous 60 was tight enough that some themes clipped the
# last character on library files where the label flips to Unsave.
self._save_btn.setFixedWidth(75)
self._save_btn.setStyleSheet(_tb_btn_style)
self._save_btn = _icon_btn("\u2193", "_tb_save", "Save to library (S)")
self._save_btn.clicked.connect(self._on_save_clicked)
tb.addWidget(self._save_btn)
self._bl_tag_btn = QPushButton("BL Tag")
self._bl_tag_btn.setFixedWidth(60)
self._bl_tag_btn.setStyleSheet(_tb_btn_style)
self._bl_tag_btn.setToolTip("Blacklist a tag")
self._bl_tag_btn = _icon_btn("\u2298", "_tb_bl_tag", "Blacklist a tag")
self._bl_tag_btn.clicked.connect(self._show_bl_tag_menu)
tb.addWidget(self._bl_tag_btn)
self._bl_post_btn = QPushButton("BL Post")
self._bl_post_btn.setFixedWidth(65)
self._bl_post_btn.setStyleSheet(_tb_btn_style)
self._bl_post_btn.setToolTip("Blacklist this post")
self._bl_post_btn = _icon_btn("\u2297", "_tb_bl_post", "Blacklist this post")
self._bl_post_btn.clicked.connect(self.blacklist_post_requested)
tb.addWidget(self._bl_post_btn)
tb.addStretch()
self._popout_btn = QPushButton("Popout")
self._popout_btn.setFixedWidth(65)
self._popout_btn.setStyleSheet(_tb_btn_style)
self._popout_btn.setToolTip("Open in popout")
self._popout_btn = _icon_btn("\u29c9", "_tb_popout", "Popout")
self._popout_btn.clicked.connect(self.fullscreen_requested)
tb.addWidget(self._popout_btn)
@ -239,12 +223,13 @@ class ImagePreview(QWidget):
def update_bookmark_state(self, bookmarked: bool) -> None:
self._is_bookmarked = bookmarked
self._bookmark_btn.setText("Unbookmark" if bookmarked else "Bookmark")
self._bookmark_btn.setFixedWidth(90 if bookmarked else 80)
self._bookmark_btn.setText("\u2605" if bookmarked else "\u2606") # ★ / ☆
self._bookmark_btn.setToolTip("Unbookmark (B)" if bookmarked else "Bookmark (B)")
def update_save_state(self, saved: bool) -> None:
self._is_saved = saved
self._save_btn.setText("Unsave" if saved else "Save")
self._save_btn.setText("\u2715" if saved else "\u2193") # ✕ / ⤓
self._save_btn.setToolTip("Unsave from library" if saved else "Save to library (S)")

View File

@ -197,6 +197,11 @@ class SettingsDialog(QDialog):
self._search_history.setChecked(self._db.get_setting_bool("search_history_enabled"))
form.addRow("", self._search_history)
# Flip layout
self._flip_layout = QCheckBox("Preview on left (restart required)")
self._flip_layout.setChecked(self._db.get_setting_bool("flip_layout"))
form.addRow("", self._flip_layout)
# Slideshow monitor
from PySide6.QtWidgets import QApplication
self._monitor_combo = QComboBox()
@ -210,6 +215,16 @@ class SettingsDialog(QDialog):
self._monitor_combo.setCurrentIndex(idx)
form.addRow("Popout monitor:", self._monitor_combo)
# Popout anchor — resize pivot point
self._popout_anchor = QComboBox()
self._popout_anchor.addItems(["Center", "Top-left", "Top-right", "Bottom-left", "Bottom-right"])
_anchor_map = {"center": "Center", "tl": "Top-left", "tr": "Top-right", "bl": "Bottom-left", "br": "Bottom-right"}
current_anchor = self._db.get_setting("popout_anchor") or "center"
idx = self._popout_anchor.findText(_anchor_map.get(current_anchor, "Center"))
if idx >= 0:
self._popout_anchor.setCurrentIndex(idx)
form.addRow("Popout anchor:", self._popout_anchor)
# File dialog platform (Linux only)
self._file_dialog_combo = None
if not IS_WINDOWS:
@ -791,7 +806,10 @@ class SettingsDialog(QDialog):
self._db.set_setting("infinite_scroll", "1" if self._infinite_scroll.isChecked() else "0")
self._db.set_setting("unbookmark_on_save", "1" if self._unbookmark_on_save.isChecked() else "0")
self._db.set_setting("search_history_enabled", "1" if self._search_history.isChecked() else "0")
self._db.set_setting("flip_layout", "1" if self._flip_layout.isChecked() else "0")
self._db.set_setting("slideshow_monitor", self._monitor_combo.currentText())
_anchor_rmap = {"Center": "center", "Top-left": "tl", "Top-right": "tr", "Bottom-left": "bl", "Bottom-right": "br"}
self._db.set_setting("popout_anchor", _anchor_rmap.get(self._popout_anchor.currentText(), "center"))
self._db.set_setting("library_dir", self._library_dir.text().strip())
self._db.set_setting("library_filename_template", self._library_filename_template.text().strip())
self._db.set_setting("max_cache_mb", str(self._max_cache.value()))

View File

@ -118,15 +118,7 @@ QPushButton:pressed { background-color: ${bg_active}; }
QPushButton:checked { background-color: ${accent}; } /* Active tab (Browse/Bookmarks/Library), Autoplay, Loop toggles */
```
**Note:** Qt's QSS does not support the CSS `content` property, so you cannot replace button text (e.g. "Play" → "") via stylesheet alone. However, you can use a Nerd Font to change how unicode characters render:
```css
QPushButton {
font-family: "JetBrainsMono Nerd Font", monospace;
}
```
To use icon buttons, you would need to modify the Python source code directly — the button labels are set in `preview.py` via `QPushButton("Play")` etc.
**Note:** Qt's QSS does not support the CSS `content` property, so you cannot replace button text (e.g. swap icon symbols) via stylesheet alone. The toolbar icon buttons use hardcoded Unicode symbols — to change which symbols appear, modify the Python source directly (see `preview_pane.py` and `popout/window.py`).
### Text Inputs
@ -312,11 +304,34 @@ QWidget#_slideshow_controls QPushButton {
}
```
### Preview Toolbar
### Preview & Popout Toolbar Icon Buttons
The preview panel has an action toolbar (Bookmark, Save, BL Tag, BL Post, Popout) that appears above the media when a post is active. This toolbar uses the app's default button styling.
The preview and popout toolbars use 24x24 icon buttons with Unicode symbols. Each button has an object name for QSS targeting:
The toolbar does not have a named object ID — it inherits the app's `QPushButton` styles directly.
| Object Name | Symbol | Action |
|-------------|--------|--------|
| `#_tb_bookmark` | ☆ / ★ | Bookmark / Unbookmark |
| `#_tb_save` | ⤓ / ✕ | Save / Unsave |
| `#_tb_bl_tag` | ⊘ | Blacklist a tag |
| `#_tb_bl_post` | ⊗ | Blacklist this post |
| `#_tb_popout` | ⧉ | Open popout (preview only) |
```css
/* Style all toolbar icon buttons */
QPushButton#_tb_bookmark,
QPushButton#_tb_save,
QPushButton#_tb_bl_tag,
QPushButton#_tb_bl_post,
QPushButton#_tb_popout {
background: transparent;
border: 1px solid ${border};
color: ${text};
padding: 0px;
}
```
The same object names are used in both the preview pane and the popout overlay, so one rule targets both. The symbols themselves are hardcoded in Python — QSS can style the buttons but cannot change which symbol is displayed.
### Progress Bar (Download)

View File

@ -9,26 +9,25 @@
*/
/* @palette
bg: #1e1e2e
bg_alt: #181825
bg_subtle: #313244
bg_hover: #45475a
bg_active: #585b70
text: #cdd6f4
text_dim: #a6adc8
text_disabled: #6c7086
border: #313244
border_strong: #45475a
accent: #cba6f7
accent_text: #1e1e2e
accent_dim: #b4befe
link: #89b4fa
danger: #f38ba8
success: #a6e3a1
warning: #f9e2af
overlay_bg: rgba(30, 30, 46, 200)
bg: #1e1e2e
bg_alt: #181825
bg_subtle: #313244
bg_hover: #45475a
bg_active: #585b70
text: #cdd6f4
text_dim: #a6adc8
text_disabled: #6c7086
border: #313244
border_strong: #45475a
accent: #cba6f7
accent_text: #1e1e2e
accent_dim: #b4befe
link: #89b4fa
danger: #f38ba8
success: #a6e3a1
warning: #f9e2af
overlay_bg: rgba(30, 30, 46, 200)
*/
/* ---------- Base ---------- */
QWidget {
@ -43,8 +42,6 @@ QWidget:disabled {
color: ${text_disabled};
}
/* Labels should never paint an opaque background they sit on top of
* other widgets in many places (toolbars, info panels, overlays). */
QLabel {
background: transparent;
}
@ -92,49 +89,26 @@ QPushButton:flat:hover {
background-color: ${bg_hover};
}
QToolButton {
background-color: transparent;
color: ${text};
border: 1px solid transparent;
border-radius: 4px;
padding: 4px;
}
QToolButton:hover {
background-color: ${bg_hover};
border-color: ${border_strong};
}
QToolButton:pressed, QToolButton:checked {
background-color: ${bg_active};
}
/* ---------- Inputs ---------- */
QLineEdit, QSpinBox, QDoubleSpinBox, QTextEdit, QPlainTextEdit {
QLineEdit, QSpinBox, QTextEdit {
background-color: ${bg_subtle};
color: ${text};
border: 1px solid ${border_strong};
border-radius: 4px;
padding: 2px 6px;
/* min-height ensures the painted text fits inside the widget bounds
* even when a parent layout (e.g. QFormLayout inside a QGroupBox)
* compresses the natural sizeHint. Without this, spinboxes in dense
* forms render with the top of the value text clipped. */
min-height: 16px;
selection-background-color: ${accent};
selection-color: ${accent_text};
}
QLineEdit:focus,
QSpinBox:focus,
QDoubleSpinBox:focus,
QTextEdit:focus,
QPlainTextEdit:focus {
QTextEdit:focus {
border-color: ${accent};
}
QLineEdit:disabled,
QSpinBox:disabled,
QDoubleSpinBox:disabled,
QTextEdit:disabled,
QPlainTextEdit:disabled {
QTextEdit:disabled {
background-color: ${bg_alt};
color: ${text_disabled};
border-color: ${border};
@ -315,19 +289,6 @@ QSlider::handle:horizontal:hover {
background: ${accent_dim};
}
QSlider::groove:vertical {
background: ${bg_subtle};
width: 4px;
border-radius: 2px;
}
QSlider::handle:vertical {
background: ${accent};
width: 12px;
height: 12px;
margin: 0 -5px;
border-radius: 6px;
}
/* ---------- Progress ---------- */
QProgressBar {
@ -343,33 +304,28 @@ QProgressBar::chunk {
border-radius: 3px;
}
/* ---------- Checkboxes & radio buttons ---------- */
/* ---------- Checkboxes ---------- */
QCheckBox, QRadioButton {
QCheckBox {
background: transparent;
color: ${text};
spacing: 6px;
}
QCheckBox::indicator, QRadioButton::indicator {
QCheckBox::indicator {
width: 14px;
height: 14px;
background-color: ${bg_subtle};
border: 1px solid ${border_strong};
}
QCheckBox::indicator {
border-radius: 3px;
}
QRadioButton::indicator {
border-radius: 7px;
}
QCheckBox::indicator:hover, QRadioButton::indicator:hover {
QCheckBox::indicator:hover {
border-color: ${accent};
}
QCheckBox::indicator:checked, QRadioButton::indicator:checked {
QCheckBox::indicator:checked {
background-color: ${accent};
border-color: ${accent};
}
QCheckBox::indicator:disabled, QRadioButton::indicator:disabled {
QCheckBox::indicator:disabled {
background-color: ${bg_alt};
border-color: ${border};
}
@ -384,9 +340,9 @@ QToolTip {
border-radius: 3px;
}
/* ---------- Item views (lists, trees, tables) ---------- */
/* ---------- Lists ---------- */
QListView, QListWidget, QTreeView, QTreeWidget, QTableView, QTableWidget {
QListView, QListWidget {
background-color: ${bg};
alternate-background-color: ${bg_alt};
color: ${text};
@ -395,35 +351,18 @@ QListView, QListWidget, QTreeView, QTreeWidget, QTableView, QTableWidget {
selection-color: ${accent_text};
outline: none;
}
QListView::item, QListWidget::item,
QTreeView::item, QTreeWidget::item,
QTableView::item, QTableWidget::item {
QListView::item, QListWidget::item {
padding: 4px;
}
QListView::item:hover, QListWidget::item:hover,
QTreeView::item:hover, QTreeWidget::item:hover,
QTableView::item:hover, QTableWidget::item:hover {
QListView::item:hover, QListWidget::item:hover {
background-color: ${bg_hover};
}
QListView::item:selected, QListWidget::item:selected,
QTreeView::item:selected, QTreeWidget::item:selected,
QTableView::item:selected, QTableWidget::item:selected {
QListView::item:selected, QListWidget::item:selected {
background-color: ${accent};
color: ${accent_text};
}
QHeaderView::section {
background-color: ${bg_subtle};
color: ${text};
border: none;
border-right: 1px solid ${border};
padding: 4px 8px;
}
QHeaderView::section:hover {
background-color: ${bg_hover};
}
/* ---------- Tabs ---------- */
/* ---------- Tabs (settings dialog) ---------- */
QTabWidget::pane {
border: 1px solid ${border};
@ -448,7 +387,7 @@ QTabBar::tab:hover:!selected {
color: ${text};
}
/* ---------- Group boxes ---------- */
/* ---------- Group boxes (settings dialog) ---------- */
QGroupBox {
background: transparent;
@ -465,63 +404,14 @@ QGroupBox::title {
color: ${text_dim};
}
/* ---------- Frames ---------- */
QFrame[frameShape="4"], /* HLine */
QFrame[frameShape="5"] /* VLine */ {
background: ${border};
color: ${border};
}
/* ---------- Toolbars ---------- */
QToolBar {
background: ${bg};
border: none;
spacing: 4px;
padding: 2px;
}
QToolBar::separator {
background: ${border};
width: 1px;
margin: 4px 4px;
}
/* ---------- Dock widgets ---------- */
QDockWidget {
color: ${text};
titlebar-close-icon: none;
}
QDockWidget::title {
background: ${bg_subtle};
padding: 4px;
border: 1px solid ${border};
}
/* ---------- Rubber band (multi-select drag rectangle) ---------- */
/* ---------- Rubber band (multi-select drag) ---------- */
QRubberBand {
background: ${accent};
border: 1px solid ${accent};
/* Qt blends rubber band at ~30% so this reads as a translucent
* accent-tinted rectangle without needing rgba(). */
}
/* ---------- Library count label states ---------- */
/*
* The library tab's count label switches between three visual states
* depending on what refresh() found. The state is exposed as a Qt
* dynamic property `libraryCountState` so users can override these
* rules in their custom.qss without touching the Python.
*
* normal N files default text color, no rule needed
* empty no items dim text (no items found, search miss)
* error bad/unreachable danger color + bold (real error)
*/
QLabel[libraryCountState="empty"] {
color: ${text_dim};
@ -531,18 +421,18 @@ QLabel[libraryCountState="error"] {
font-weight: bold;
}
/* ---------- Thumbnail dot indicators (Qt properties on ThumbnailWidget) ---------- */
/* ---------- Thumbnail indicators ---------- */
ThumbnailWidget {
qproperty-savedColor: #22cc22; /* green dot: saved to library — universal "confirmed" feel */
qproperty-bookmarkedColor: #ffcc00; /* yellow star: bookmarked */
qproperty-savedColor: #22cc22;
qproperty-bookmarkedColor: #ffcc00;
qproperty-selectionColor: ${accent};
qproperty-multiSelectColor: ${accent_dim};
qproperty-hoverColor: ${accent};
qproperty-idleColor: ${border_strong};
}
/* ---------- Info panel tag category colors ---------- */
/* ---------- Info panel tag colors ---------- */
InfoPanel {
qproperty-tagArtistColor: ${warning};
@ -553,19 +443,13 @@ InfoPanel {
qproperty-tagLoreColor: ${text_dim};
}
/* ---------- Video player letterbox / pillarbox color (mpv background) ---------- */
/* ---------- Video player letterbox ---------- */
VideoPlayer {
qproperty-letterboxColor: ${bg};
}
/* ---------- Popout overlay bars (slideshow toolbar + slideshow controls + embedded preview controls) ---------- */
/*
* The popout window's translucent toolbar (top) and transport controls
* (bottom) float over the video content. The bg color comes from the
* @palette overlay_bg slot. Children get the classic overlay treatment:
* transparent backgrounds, near-white text, hairline borders.
*/
/* ---------- Popout overlay bars ---------- */
QWidget#_slideshow_toolbar,
QWidget#_slideshow_controls,
@ -588,6 +472,8 @@ QWidget#_preview_controls QPushButton {
color: white;
border: 1px solid rgba(255, 255, 255, 80);
padding: 2px 6px;
font-size: 15px;
font-weight: bold;
}
QWidget#_slideshow_toolbar QPushButton:hover,
QWidget#_slideshow_controls QPushButton:hover,

View File

@ -9,26 +9,25 @@
*/
/* @palette
bg: #1e1e2e
bg_alt: #181825
bg_subtle: #313244
bg_hover: #45475a
bg_active: #585b70
text: #cdd6f4
text_dim: #a6adc8
text_disabled: #6c7086
border: #313244
border_strong: #45475a
accent: #cba6f7
accent_text: #1e1e2e
accent_dim: #b4befe
link: #89b4fa
danger: #f38ba8
success: #a6e3a1
warning: #f9e2af
overlay_bg: rgba(30, 30, 46, 200)
bg: #1e1e2e
bg_alt: #181825
bg_subtle: #313244
bg_hover: #45475a
bg_active: #585b70
text: #cdd6f4
text_dim: #a6adc8
text_disabled: #6c7086
border: #313244
border_strong: #45475a
accent: #cba6f7
accent_text: #1e1e2e
accent_dim: #b4befe
link: #89b4fa
danger: #f38ba8
success: #a6e3a1
warning: #f9e2af
overlay_bg: rgba(30, 30, 46, 200)
*/
/* ---------- Base ---------- */
QWidget {
@ -43,8 +42,6 @@ QWidget:disabled {
color: ${text_disabled};
}
/* Labels should never paint an opaque background they sit on top of
* other widgets in many places (toolbars, info panels, overlays). */
QLabel {
background: transparent;
}
@ -91,47 +88,25 @@ QPushButton:flat:hover {
background-color: ${bg_hover};
}
QToolButton {
background-color: transparent;
color: ${text};
border: 1px solid transparent;
padding: 4px;
}
QToolButton:hover {
background-color: ${bg_hover};
border-color: ${border_strong};
}
QToolButton:pressed, QToolButton:checked {
background-color: ${bg_active};
}
/* ---------- Inputs ---------- */
QLineEdit, QSpinBox, QDoubleSpinBox, QTextEdit, QPlainTextEdit {
QLineEdit, QSpinBox, QTextEdit {
background-color: ${bg_subtle};
color: ${text};
border: 1px solid ${border_strong};
padding: 2px 6px;
/* min-height ensures the painted text fits inside the widget bounds
* even when a parent layout (e.g. QFormLayout inside a QGroupBox)
* compresses the natural sizeHint. Without this, spinboxes in dense
* forms render with the top of the value text clipped. */
min-height: 16px;
selection-background-color: ${accent};
selection-color: ${accent_text};
}
QLineEdit:focus,
QSpinBox:focus,
QDoubleSpinBox:focus,
QTextEdit:focus,
QPlainTextEdit:focus {
QTextEdit:focus {
border-color: ${accent};
}
QLineEdit:disabled,
QSpinBox:disabled,
QDoubleSpinBox:disabled,
QTextEdit:disabled,
QPlainTextEdit:disabled {
QTextEdit:disabled {
background-color: ${bg_alt};
color: ${text_disabled};
border-color: ${border};
@ -309,17 +284,6 @@ QSlider::handle:horizontal:hover {
background: ${accent_dim};
}
QSlider::groove:vertical {
background: ${bg_subtle};
width: 4px;
}
QSlider::handle:vertical {
background: ${accent};
width: 12px;
height: 12px;
margin: 0 -5px;
}
/* ---------- Progress ---------- */
QProgressBar {
@ -333,32 +297,27 @@ QProgressBar::chunk {
background-color: ${accent};
}
/* ---------- Checkboxes & radio buttons ---------- */
/* ---------- Checkboxes ---------- */
QCheckBox, QRadioButton {
QCheckBox {
background: transparent;
color: ${text};
spacing: 6px;
}
QCheckBox::indicator, QRadioButton::indicator {
QCheckBox::indicator {
width: 14px;
height: 14px;
background-color: ${bg_subtle};
border: 1px solid ${border_strong};
}
QCheckBox::indicator {
}
QRadioButton::indicator {
border-radius: 7px;
}
QCheckBox::indicator:hover, QRadioButton::indicator:hover {
QCheckBox::indicator:hover {
border-color: ${accent};
}
QCheckBox::indicator:checked, QRadioButton::indicator:checked {
QCheckBox::indicator:checked {
background-color: ${accent};
border-color: ${accent};
}
QCheckBox::indicator:disabled, QRadioButton::indicator:disabled {
QCheckBox::indicator:disabled {
background-color: ${bg_alt};
border-color: ${border};
}
@ -372,9 +331,9 @@ QToolTip {
padding: 4px 6px;
}
/* ---------- Item views (lists, trees, tables) ---------- */
/* ---------- Lists ---------- */
QListView, QListWidget, QTreeView, QTreeWidget, QTableView, QTableWidget {
QListView, QListWidget {
background-color: ${bg};
alternate-background-color: ${bg_alt};
color: ${text};
@ -383,35 +342,18 @@ QListView, QListWidget, QTreeView, QTreeWidget, QTableView, QTableWidget {
selection-color: ${accent_text};
outline: none;
}
QListView::item, QListWidget::item,
QTreeView::item, QTreeWidget::item,
QTableView::item, QTableWidget::item {
QListView::item, QListWidget::item {
padding: 4px;
}
QListView::item:hover, QListWidget::item:hover,
QTreeView::item:hover, QTreeWidget::item:hover,
QTableView::item:hover, QTableWidget::item:hover {
QListView::item:hover, QListWidget::item:hover {
background-color: ${bg_hover};
}
QListView::item:selected, QListWidget::item:selected,
QTreeView::item:selected, QTreeWidget::item:selected,
QTableView::item:selected, QTableWidget::item:selected {
QListView::item:selected, QListWidget::item:selected {
background-color: ${accent};
color: ${accent_text};
}
QHeaderView::section {
background-color: ${bg_subtle};
color: ${text};
border: none;
border-right: 1px solid ${border};
padding: 4px 8px;
}
QHeaderView::section:hover {
background-color: ${bg_hover};
}
/* ---------- Tabs ---------- */
/* ---------- Tabs (settings dialog) ---------- */
QTabWidget::pane {
border: 1px solid ${border};
@ -436,7 +378,7 @@ QTabBar::tab:hover:!selected {
color: ${text};
}
/* ---------- Group boxes ---------- */
/* ---------- Group boxes (settings dialog) ---------- */
QGroupBox {
background: transparent;
@ -452,63 +394,14 @@ QGroupBox::title {
color: ${text_dim};
}
/* ---------- Frames ---------- */
QFrame[frameShape="4"], /* HLine */
QFrame[frameShape="5"] /* VLine */ {
background: ${border};
color: ${border};
}
/* ---------- Toolbars ---------- */
QToolBar {
background: ${bg};
border: none;
spacing: 4px;
padding: 2px;
}
QToolBar::separator {
background: ${border};
width: 1px;
margin: 4px 4px;
}
/* ---------- Dock widgets ---------- */
QDockWidget {
color: ${text};
titlebar-close-icon: none;
}
QDockWidget::title {
background: ${bg_subtle};
padding: 4px;
border: 1px solid ${border};
}
/* ---------- Rubber band (multi-select drag rectangle) ---------- */
/* ---------- Rubber band (multi-select drag) ---------- */
QRubberBand {
background: ${accent};
border: 1px solid ${accent};
/* Qt blends rubber band at ~30% so this reads as a translucent
* accent-tinted rectangle without needing rgba(). */
}
/* ---------- Library count label states ---------- */
/*
* The library tab's count label switches between three visual states
* depending on what refresh() found. The state is exposed as a Qt
* dynamic property `libraryCountState` so users can override these
* rules in their custom.qss without touching the Python.
*
* normal N files default text color, no rule needed
* empty no items dim text (no items found, search miss)
* error bad/unreachable danger color + bold (real error)
*/
QLabel[libraryCountState="empty"] {
color: ${text_dim};
@ -518,18 +411,18 @@ QLabel[libraryCountState="error"] {
font-weight: bold;
}
/* ---------- Thumbnail dot indicators (Qt properties on ThumbnailWidget) ---------- */
/* ---------- Thumbnail indicators ---------- */
ThumbnailWidget {
qproperty-savedColor: #22cc22; /* green dot: saved to library — universal "confirmed" feel */
qproperty-bookmarkedColor: #ffcc00; /* yellow star: bookmarked */
qproperty-savedColor: #22cc22;
qproperty-bookmarkedColor: #ffcc00;
qproperty-selectionColor: ${accent};
qproperty-multiSelectColor: ${accent_dim};
qproperty-hoverColor: ${accent};
qproperty-idleColor: ${border_strong};
}
/* ---------- Info panel tag category colors ---------- */
/* ---------- Info panel tag colors ---------- */
InfoPanel {
qproperty-tagArtistColor: ${warning};
@ -540,19 +433,13 @@ InfoPanel {
qproperty-tagLoreColor: ${text_dim};
}
/* ---------- Video player letterbox / pillarbox color (mpv background) ---------- */
/* ---------- Video player letterbox ---------- */
VideoPlayer {
qproperty-letterboxColor: ${bg};
}
/* ---------- Popout overlay bars (slideshow toolbar + slideshow controls + embedded preview controls) ---------- */
/*
* The popout window's translucent toolbar (top) and transport controls
* (bottom) float over the video content. The bg color comes from the
* @palette overlay_bg slot. Children get the classic overlay treatment:
* transparent backgrounds, near-white text, hairline borders.
*/
/* ---------- Popout overlay bars ---------- */
QWidget#_slideshow_toolbar,
QWidget#_slideshow_controls,
@ -575,6 +462,8 @@ QWidget#_preview_controls QPushButton {
color: white;
border: 1px solid rgba(255, 255, 255, 80);
padding: 2px 6px;
font-size: 15px;
font-weight: bold;
}
QWidget#_slideshow_toolbar QPushButton:hover,
QWidget#_slideshow_controls QPushButton:hover,

View File

@ -1,4 +1,4 @@
/* booru-viewer Everforest Dark
/* booru-viewer Everforest
*
* Edit the @palette block below to recolor this rounded variant. The body uses
* ${...} placeholders that the app's _load_user_qss preprocessor
@ -9,26 +9,25 @@
*/
/* @palette
bg: #2d353b
bg_alt: #232a2e
bg_subtle: #343f44
bg_hover: #3d484d
bg_active: #4f585e
text: #d3c6aa
text_dim: #9da9a0
text_disabled: #7a8478
border: #343f44
border_strong: #3d484d
accent: #a7c080
accent_text: #2d353b
accent_dim: #83c092
link: #7fbbb3
danger: #e67e80
success: #a7c080
warning: #dbbc7f
overlay_bg: rgba(45, 53, 59, 200)
bg: #2d353b
bg_alt: #232a2e
bg_subtle: #343f44
bg_hover: #3d484d
bg_active: #4f585e
text: #d3c6aa
text_dim: #9da9a0
text_disabled: #7a8478
border: #343f44
border_strong: #3d484d
accent: #a7c080
accent_text: #2d353b
accent_dim: #83c092
link: #7fbbb3
danger: #e67e80
success: #a7c080
warning: #dbbc7f
overlay_bg: rgba(45, 53, 59, 200)
*/
/* ---------- Base ---------- */
QWidget {
@ -43,8 +42,6 @@ QWidget:disabled {
color: ${text_disabled};
}
/* Labels should never paint an opaque background they sit on top of
* other widgets in many places (toolbars, info panels, overlays). */
QLabel {
background: transparent;
}
@ -92,49 +89,26 @@ QPushButton:flat:hover {
background-color: ${bg_hover};
}
QToolButton {
background-color: transparent;
color: ${text};
border: 1px solid transparent;
border-radius: 4px;
padding: 4px;
}
QToolButton:hover {
background-color: ${bg_hover};
border-color: ${border_strong};
}
QToolButton:pressed, QToolButton:checked {
background-color: ${bg_active};
}
/* ---------- Inputs ---------- */
QLineEdit, QSpinBox, QDoubleSpinBox, QTextEdit, QPlainTextEdit {
QLineEdit, QSpinBox, QTextEdit {
background-color: ${bg_subtle};
color: ${text};
border: 1px solid ${border_strong};
border-radius: 4px;
padding: 2px 6px;
/* min-height ensures the painted text fits inside the widget bounds
* even when a parent layout (e.g. QFormLayout inside a QGroupBox)
* compresses the natural sizeHint. Without this, spinboxes in dense
* forms render with the top of the value text clipped. */
min-height: 16px;
selection-background-color: ${accent};
selection-color: ${accent_text};
}
QLineEdit:focus,
QSpinBox:focus,
QDoubleSpinBox:focus,
QTextEdit:focus,
QPlainTextEdit:focus {
QTextEdit:focus {
border-color: ${accent};
}
QLineEdit:disabled,
QSpinBox:disabled,
QDoubleSpinBox:disabled,
QTextEdit:disabled,
QPlainTextEdit:disabled {
QTextEdit:disabled {
background-color: ${bg_alt};
color: ${text_disabled};
border-color: ${border};
@ -315,19 +289,6 @@ QSlider::handle:horizontal:hover {
background: ${accent_dim};
}
QSlider::groove:vertical {
background: ${bg_subtle};
width: 4px;
border-radius: 2px;
}
QSlider::handle:vertical {
background: ${accent};
width: 12px;
height: 12px;
margin: 0 -5px;
border-radius: 6px;
}
/* ---------- Progress ---------- */
QProgressBar {
@ -343,33 +304,28 @@ QProgressBar::chunk {
border-radius: 3px;
}
/* ---------- Checkboxes & radio buttons ---------- */
/* ---------- Checkboxes ---------- */
QCheckBox, QRadioButton {
QCheckBox {
background: transparent;
color: ${text};
spacing: 6px;
}
QCheckBox::indicator, QRadioButton::indicator {
QCheckBox::indicator {
width: 14px;
height: 14px;
background-color: ${bg_subtle};
border: 1px solid ${border_strong};
}
QCheckBox::indicator {
border-radius: 3px;
}
QRadioButton::indicator {
border-radius: 7px;
}
QCheckBox::indicator:hover, QRadioButton::indicator:hover {
QCheckBox::indicator:hover {
border-color: ${accent};
}
QCheckBox::indicator:checked, QRadioButton::indicator:checked {
QCheckBox::indicator:checked {
background-color: ${accent};
border-color: ${accent};
}
QCheckBox::indicator:disabled, QRadioButton::indicator:disabled {
QCheckBox::indicator:disabled {
background-color: ${bg_alt};
border-color: ${border};
}
@ -384,9 +340,9 @@ QToolTip {
border-radius: 3px;
}
/* ---------- Item views (lists, trees, tables) ---------- */
/* ---------- Lists ---------- */
QListView, QListWidget, QTreeView, QTreeWidget, QTableView, QTableWidget {
QListView, QListWidget {
background-color: ${bg};
alternate-background-color: ${bg_alt};
color: ${text};
@ -395,35 +351,18 @@ QListView, QListWidget, QTreeView, QTreeWidget, QTableView, QTableWidget {
selection-color: ${accent_text};
outline: none;
}
QListView::item, QListWidget::item,
QTreeView::item, QTreeWidget::item,
QTableView::item, QTableWidget::item {
QListView::item, QListWidget::item {
padding: 4px;
}
QListView::item:hover, QListWidget::item:hover,
QTreeView::item:hover, QTreeWidget::item:hover,
QTableView::item:hover, QTableWidget::item:hover {
QListView::item:hover, QListWidget::item:hover {
background-color: ${bg_hover};
}
QListView::item:selected, QListWidget::item:selected,
QTreeView::item:selected, QTreeWidget::item:selected,
QTableView::item:selected, QTableWidget::item:selected {
QListView::item:selected, QListWidget::item:selected {
background-color: ${accent};
color: ${accent_text};
}
QHeaderView::section {
background-color: ${bg_subtle};
color: ${text};
border: none;
border-right: 1px solid ${border};
padding: 4px 8px;
}
QHeaderView::section:hover {
background-color: ${bg_hover};
}
/* ---------- Tabs ---------- */
/* ---------- Tabs (settings dialog) ---------- */
QTabWidget::pane {
border: 1px solid ${border};
@ -448,7 +387,7 @@ QTabBar::tab:hover:!selected {
color: ${text};
}
/* ---------- Group boxes ---------- */
/* ---------- Group boxes (settings dialog) ---------- */
QGroupBox {
background: transparent;
@ -465,63 +404,14 @@ QGroupBox::title {
color: ${text_dim};
}
/* ---------- Frames ---------- */
QFrame[frameShape="4"], /* HLine */
QFrame[frameShape="5"] /* VLine */ {
background: ${border};
color: ${border};
}
/* ---------- Toolbars ---------- */
QToolBar {
background: ${bg};
border: none;
spacing: 4px;
padding: 2px;
}
QToolBar::separator {
background: ${border};
width: 1px;
margin: 4px 4px;
}
/* ---------- Dock widgets ---------- */
QDockWidget {
color: ${text};
titlebar-close-icon: none;
}
QDockWidget::title {
background: ${bg_subtle};
padding: 4px;
border: 1px solid ${border};
}
/* ---------- Rubber band (multi-select drag rectangle) ---------- */
/* ---------- Rubber band (multi-select drag) ---------- */
QRubberBand {
background: ${accent};
border: 1px solid ${accent};
/* Qt blends rubber band at ~30% so this reads as a translucent
* accent-tinted rectangle without needing rgba(). */
}
/* ---------- Library count label states ---------- */
/*
* The library tab's count label switches between three visual states
* depending on what refresh() found. The state is exposed as a Qt
* dynamic property `libraryCountState` so users can override these
* rules in their custom.qss without touching the Python.
*
* normal N files default text color, no rule needed
* empty no items dim text (no items found, search miss)
* error bad/unreachable danger color + bold (real error)
*/
QLabel[libraryCountState="empty"] {
color: ${text_dim};
@ -531,18 +421,18 @@ QLabel[libraryCountState="error"] {
font-weight: bold;
}
/* ---------- Thumbnail dot indicators (Qt properties on ThumbnailWidget) ---------- */
/* ---------- Thumbnail indicators ---------- */
ThumbnailWidget {
qproperty-savedColor: #22cc22; /* green dot: saved to library — universal "confirmed" feel */
qproperty-bookmarkedColor: #ffcc00; /* yellow star: bookmarked */
qproperty-savedColor: #22cc22;
qproperty-bookmarkedColor: #ffcc00;
qproperty-selectionColor: ${accent};
qproperty-multiSelectColor: ${accent_dim};
qproperty-hoverColor: ${accent};
qproperty-idleColor: ${border_strong};
}
/* ---------- Info panel tag category colors ---------- */
/* ---------- Info panel tag colors ---------- */
InfoPanel {
qproperty-tagArtistColor: ${warning};
@ -553,19 +443,13 @@ InfoPanel {
qproperty-tagLoreColor: ${text_dim};
}
/* ---------- Video player letterbox / pillarbox color (mpv background) ---------- */
/* ---------- Video player letterbox ---------- */
VideoPlayer {
qproperty-letterboxColor: ${bg};
}
/* ---------- Popout overlay bars (slideshow toolbar + slideshow controls + embedded preview controls) ---------- */
/*
* The popout window's translucent toolbar (top) and transport controls
* (bottom) float over the video content. The bg color comes from the
* @palette overlay_bg slot. Children get the classic overlay treatment:
* transparent backgrounds, near-white text, hairline borders.
*/
/* ---------- Popout overlay bars ---------- */
QWidget#_slideshow_toolbar,
QWidget#_slideshow_controls,
@ -588,6 +472,8 @@ QWidget#_preview_controls QPushButton {
color: white;
border: 1px solid rgba(255, 255, 255, 80);
padding: 2px 6px;
font-size: 15px;
font-weight: bold;
}
QWidget#_slideshow_toolbar QPushButton:hover,
QWidget#_slideshow_controls QPushButton:hover,

View File

@ -1,4 +1,4 @@
/* booru-viewer Everforest Dark
/* booru-viewer Everforest
*
* Edit the @palette block below to recolor this square variant. The body uses
* ${...} placeholders that the app's _load_user_qss preprocessor
@ -9,26 +9,25 @@
*/
/* @palette
bg: #2d353b
bg_alt: #232a2e
bg_subtle: #343f44
bg_hover: #3d484d
bg_active: #4f585e
text: #d3c6aa
text_dim: #9da9a0
text_disabled: #7a8478
border: #343f44
border_strong: #3d484d
accent: #a7c080
accent_text: #2d353b
accent_dim: #83c092
link: #7fbbb3
danger: #e67e80
success: #a7c080
warning: #dbbc7f
overlay_bg: rgba(45, 53, 59, 200)
bg: #2d353b
bg_alt: #232a2e
bg_subtle: #343f44
bg_hover: #3d484d
bg_active: #4f585e
text: #d3c6aa
text_dim: #9da9a0
text_disabled: #7a8478
border: #343f44
border_strong: #3d484d
accent: #a7c080
accent_text: #2d353b
accent_dim: #83c092
link: #7fbbb3
danger: #e67e80
success: #a7c080
warning: #dbbc7f
overlay_bg: rgba(45, 53, 59, 200)
*/
/* ---------- Base ---------- */
QWidget {
@ -43,8 +42,6 @@ QWidget:disabled {
color: ${text_disabled};
}
/* Labels should never paint an opaque background they sit on top of
* other widgets in many places (toolbars, info panels, overlays). */
QLabel {
background: transparent;
}
@ -91,47 +88,25 @@ QPushButton:flat:hover {
background-color: ${bg_hover};
}
QToolButton {
background-color: transparent;
color: ${text};
border: 1px solid transparent;
padding: 4px;
}
QToolButton:hover {
background-color: ${bg_hover};
border-color: ${border_strong};
}
QToolButton:pressed, QToolButton:checked {
background-color: ${bg_active};
}
/* ---------- Inputs ---------- */
QLineEdit, QSpinBox, QDoubleSpinBox, QTextEdit, QPlainTextEdit {
QLineEdit, QSpinBox, QTextEdit {
background-color: ${bg_subtle};
color: ${text};
border: 1px solid ${border_strong};
padding: 2px 6px;
/* min-height ensures the painted text fits inside the widget bounds
* even when a parent layout (e.g. QFormLayout inside a QGroupBox)
* compresses the natural sizeHint. Without this, spinboxes in dense
* forms render with the top of the value text clipped. */
min-height: 16px;
selection-background-color: ${accent};
selection-color: ${accent_text};
}
QLineEdit:focus,
QSpinBox:focus,
QDoubleSpinBox:focus,
QTextEdit:focus,
QPlainTextEdit:focus {
QTextEdit:focus {
border-color: ${accent};
}
QLineEdit:disabled,
QSpinBox:disabled,
QDoubleSpinBox:disabled,
QTextEdit:disabled,
QPlainTextEdit:disabled {
QTextEdit:disabled {
background-color: ${bg_alt};
color: ${text_disabled};
border-color: ${border};
@ -309,17 +284,6 @@ QSlider::handle:horizontal:hover {
background: ${accent_dim};
}
QSlider::groove:vertical {
background: ${bg_subtle};
width: 4px;
}
QSlider::handle:vertical {
background: ${accent};
width: 12px;
height: 12px;
margin: 0 -5px;
}
/* ---------- Progress ---------- */
QProgressBar {
@ -333,32 +297,27 @@ QProgressBar::chunk {
background-color: ${accent};
}
/* ---------- Checkboxes & radio buttons ---------- */
/* ---------- Checkboxes ---------- */
QCheckBox, QRadioButton {
QCheckBox {
background: transparent;
color: ${text};
spacing: 6px;
}
QCheckBox::indicator, QRadioButton::indicator {
QCheckBox::indicator {
width: 14px;
height: 14px;
background-color: ${bg_subtle};
border: 1px solid ${border_strong};
}
QCheckBox::indicator {
}
QRadioButton::indicator {
border-radius: 7px;
}
QCheckBox::indicator:hover, QRadioButton::indicator:hover {
QCheckBox::indicator:hover {
border-color: ${accent};
}
QCheckBox::indicator:checked, QRadioButton::indicator:checked {
QCheckBox::indicator:checked {
background-color: ${accent};
border-color: ${accent};
}
QCheckBox::indicator:disabled, QRadioButton::indicator:disabled {
QCheckBox::indicator:disabled {
background-color: ${bg_alt};
border-color: ${border};
}
@ -372,9 +331,9 @@ QToolTip {
padding: 4px 6px;
}
/* ---------- Item views (lists, trees, tables) ---------- */
/* ---------- Lists ---------- */
QListView, QListWidget, QTreeView, QTreeWidget, QTableView, QTableWidget {
QListView, QListWidget {
background-color: ${bg};
alternate-background-color: ${bg_alt};
color: ${text};
@ -383,35 +342,18 @@ QListView, QListWidget, QTreeView, QTreeWidget, QTableView, QTableWidget {
selection-color: ${accent_text};
outline: none;
}
QListView::item, QListWidget::item,
QTreeView::item, QTreeWidget::item,
QTableView::item, QTableWidget::item {
QListView::item, QListWidget::item {
padding: 4px;
}
QListView::item:hover, QListWidget::item:hover,
QTreeView::item:hover, QTreeWidget::item:hover,
QTableView::item:hover, QTableWidget::item:hover {
QListView::item:hover, QListWidget::item:hover {
background-color: ${bg_hover};
}
QListView::item:selected, QListWidget::item:selected,
QTreeView::item:selected, QTreeWidget::item:selected,
QTableView::item:selected, QTableWidget::item:selected {
QListView::item:selected, QListWidget::item:selected {
background-color: ${accent};
color: ${accent_text};
}
QHeaderView::section {
background-color: ${bg_subtle};
color: ${text};
border: none;
border-right: 1px solid ${border};
padding: 4px 8px;
}
QHeaderView::section:hover {
background-color: ${bg_hover};
}
/* ---------- Tabs ---------- */
/* ---------- Tabs (settings dialog) ---------- */
QTabWidget::pane {
border: 1px solid ${border};
@ -436,7 +378,7 @@ QTabBar::tab:hover:!selected {
color: ${text};
}
/* ---------- Group boxes ---------- */
/* ---------- Group boxes (settings dialog) ---------- */
QGroupBox {
background: transparent;
@ -452,63 +394,14 @@ QGroupBox::title {
color: ${text_dim};
}
/* ---------- Frames ---------- */
QFrame[frameShape="4"], /* HLine */
QFrame[frameShape="5"] /* VLine */ {
background: ${border};
color: ${border};
}
/* ---------- Toolbars ---------- */
QToolBar {
background: ${bg};
border: none;
spacing: 4px;
padding: 2px;
}
QToolBar::separator {
background: ${border};
width: 1px;
margin: 4px 4px;
}
/* ---------- Dock widgets ---------- */
QDockWidget {
color: ${text};
titlebar-close-icon: none;
}
QDockWidget::title {
background: ${bg_subtle};
padding: 4px;
border: 1px solid ${border};
}
/* ---------- Rubber band (multi-select drag rectangle) ---------- */
/* ---------- Rubber band (multi-select drag) ---------- */
QRubberBand {
background: ${accent};
border: 1px solid ${accent};
/* Qt blends rubber band at ~30% so this reads as a translucent
* accent-tinted rectangle without needing rgba(). */
}
/* ---------- Library count label states ---------- */
/*
* The library tab's count label switches between three visual states
* depending on what refresh() found. The state is exposed as a Qt
* dynamic property `libraryCountState` so users can override these
* rules in their custom.qss without touching the Python.
*
* normal N files default text color, no rule needed
* empty no items dim text (no items found, search miss)
* error bad/unreachable danger color + bold (real error)
*/
QLabel[libraryCountState="empty"] {
color: ${text_dim};
@ -518,18 +411,18 @@ QLabel[libraryCountState="error"] {
font-weight: bold;
}
/* ---------- Thumbnail dot indicators (Qt properties on ThumbnailWidget) ---------- */
/* ---------- Thumbnail indicators ---------- */
ThumbnailWidget {
qproperty-savedColor: #22cc22; /* green dot: saved to library — universal "confirmed" feel */
qproperty-bookmarkedColor: #ffcc00; /* yellow star: bookmarked */
qproperty-savedColor: #22cc22;
qproperty-bookmarkedColor: #ffcc00;
qproperty-selectionColor: ${accent};
qproperty-multiSelectColor: ${accent_dim};
qproperty-hoverColor: ${accent};
qproperty-idleColor: ${border_strong};
}
/* ---------- Info panel tag category colors ---------- */
/* ---------- Info panel tag colors ---------- */
InfoPanel {
qproperty-tagArtistColor: ${warning};
@ -540,19 +433,13 @@ InfoPanel {
qproperty-tagLoreColor: ${text_dim};
}
/* ---------- Video player letterbox / pillarbox color (mpv background) ---------- */
/* ---------- Video player letterbox ---------- */
VideoPlayer {
qproperty-letterboxColor: ${bg};
}
/* ---------- Popout overlay bars (slideshow toolbar + slideshow controls + embedded preview controls) ---------- */
/*
* The popout window's translucent toolbar (top) and transport controls
* (bottom) float over the video content. The bg color comes from the
* @palette overlay_bg slot. Children get the classic overlay treatment:
* transparent backgrounds, near-white text, hairline borders.
*/
/* ---------- Popout overlay bars ---------- */
QWidget#_slideshow_toolbar,
QWidget#_slideshow_controls,
@ -575,6 +462,8 @@ QWidget#_preview_controls QPushButton {
color: white;
border: 1px solid rgba(255, 255, 255, 80);
padding: 2px 6px;
font-size: 15px;
font-weight: bold;
}
QWidget#_slideshow_toolbar QPushButton:hover,
QWidget#_slideshow_controls QPushButton:hover,

View File

@ -1,4 +1,4 @@
/* booru-viewer Gruvbox Dark
/* booru-viewer Gruvbox
*
* Edit the @palette block below to recolor this rounded variant. The body uses
* ${...} placeholders that the app's _load_user_qss preprocessor
@ -9,26 +9,25 @@
*/
/* @palette
bg: #282828
bg_alt: #1d2021
bg_subtle: #3c3836
bg_hover: #504945
bg_active: #665c54
text: #ebdbb2
text_dim: #d5c4a1
text_disabled: #928374
border: #3c3836
border_strong: #504945
accent: #d79921
accent_text: #282828
accent_dim: #fabd2f
link: #83a598
danger: #fb4934
success: #b8bb26
warning: #fabd2f
overlay_bg: rgba(40, 40, 40, 200)
bg: #282828
bg_alt: #1d2021
bg_subtle: #3c3836
bg_hover: #504945
bg_active: #665c54
text: #ebdbb2
text_dim: #d5c4a1
text_disabled: #928374
border: #3c3836
border_strong: #504945
accent: #d79921
accent_text: #282828
accent_dim: #fabd2f
link: #83a598
danger: #fb4934
success: #b8bb26
warning: #fabd2f
overlay_bg: rgba(40, 40, 40, 200)
*/
/* ---------- Base ---------- */
QWidget {
@ -43,8 +42,6 @@ QWidget:disabled {
color: ${text_disabled};
}
/* Labels should never paint an opaque background they sit on top of
* other widgets in many places (toolbars, info panels, overlays). */
QLabel {
background: transparent;
}
@ -92,49 +89,26 @@ QPushButton:flat:hover {
background-color: ${bg_hover};
}
QToolButton {
background-color: transparent;
color: ${text};
border: 1px solid transparent;
border-radius: 4px;
padding: 4px;
}
QToolButton:hover {
background-color: ${bg_hover};
border-color: ${border_strong};
}
QToolButton:pressed, QToolButton:checked {
background-color: ${bg_active};
}
/* ---------- Inputs ---------- */
QLineEdit, QSpinBox, QDoubleSpinBox, QTextEdit, QPlainTextEdit {
QLineEdit, QSpinBox, QTextEdit {
background-color: ${bg_subtle};
color: ${text};
border: 1px solid ${border_strong};
border-radius: 4px;
padding: 2px 6px;
/* min-height ensures the painted text fits inside the widget bounds
* even when a parent layout (e.g. QFormLayout inside a QGroupBox)
* compresses the natural sizeHint. Without this, spinboxes in dense
* forms render with the top of the value text clipped. */
min-height: 16px;
selection-background-color: ${accent};
selection-color: ${accent_text};
}
QLineEdit:focus,
QSpinBox:focus,
QDoubleSpinBox:focus,
QTextEdit:focus,
QPlainTextEdit:focus {
QTextEdit:focus {
border-color: ${accent};
}
QLineEdit:disabled,
QSpinBox:disabled,
QDoubleSpinBox:disabled,
QTextEdit:disabled,
QPlainTextEdit:disabled {
QTextEdit:disabled {
background-color: ${bg_alt};
color: ${text_disabled};
border-color: ${border};
@ -315,19 +289,6 @@ QSlider::handle:horizontal:hover {
background: ${accent_dim};
}
QSlider::groove:vertical {
background: ${bg_subtle};
width: 4px;
border-radius: 2px;
}
QSlider::handle:vertical {
background: ${accent};
width: 12px;
height: 12px;
margin: 0 -5px;
border-radius: 6px;
}
/* ---------- Progress ---------- */
QProgressBar {
@ -343,33 +304,28 @@ QProgressBar::chunk {
border-radius: 3px;
}
/* ---------- Checkboxes & radio buttons ---------- */
/* ---------- Checkboxes ---------- */
QCheckBox, QRadioButton {
QCheckBox {
background: transparent;
color: ${text};
spacing: 6px;
}
QCheckBox::indicator, QRadioButton::indicator {
QCheckBox::indicator {
width: 14px;
height: 14px;
background-color: ${bg_subtle};
border: 1px solid ${border_strong};
}
QCheckBox::indicator {
border-radius: 3px;
}
QRadioButton::indicator {
border-radius: 7px;
}
QCheckBox::indicator:hover, QRadioButton::indicator:hover {
QCheckBox::indicator:hover {
border-color: ${accent};
}
QCheckBox::indicator:checked, QRadioButton::indicator:checked {
QCheckBox::indicator:checked {
background-color: ${accent};
border-color: ${accent};
}
QCheckBox::indicator:disabled, QRadioButton::indicator:disabled {
QCheckBox::indicator:disabled {
background-color: ${bg_alt};
border-color: ${border};
}
@ -384,9 +340,9 @@ QToolTip {
border-radius: 3px;
}
/* ---------- Item views (lists, trees, tables) ---------- */
/* ---------- Lists ---------- */
QListView, QListWidget, QTreeView, QTreeWidget, QTableView, QTableWidget {
QListView, QListWidget {
background-color: ${bg};
alternate-background-color: ${bg_alt};
color: ${text};
@ -395,35 +351,18 @@ QListView, QListWidget, QTreeView, QTreeWidget, QTableView, QTableWidget {
selection-color: ${accent_text};
outline: none;
}
QListView::item, QListWidget::item,
QTreeView::item, QTreeWidget::item,
QTableView::item, QTableWidget::item {
QListView::item, QListWidget::item {
padding: 4px;
}
QListView::item:hover, QListWidget::item:hover,
QTreeView::item:hover, QTreeWidget::item:hover,
QTableView::item:hover, QTableWidget::item:hover {
QListView::item:hover, QListWidget::item:hover {
background-color: ${bg_hover};
}
QListView::item:selected, QListWidget::item:selected,
QTreeView::item:selected, QTreeWidget::item:selected,
QTableView::item:selected, QTableWidget::item:selected {
QListView::item:selected, QListWidget::item:selected {
background-color: ${accent};
color: ${accent_text};
}
QHeaderView::section {
background-color: ${bg_subtle};
color: ${text};
border: none;
border-right: 1px solid ${border};
padding: 4px 8px;
}
QHeaderView::section:hover {
background-color: ${bg_hover};
}
/* ---------- Tabs ---------- */
/* ---------- Tabs (settings dialog) ---------- */
QTabWidget::pane {
border: 1px solid ${border};
@ -448,7 +387,7 @@ QTabBar::tab:hover:!selected {
color: ${text};
}
/* ---------- Group boxes ---------- */
/* ---------- Group boxes (settings dialog) ---------- */
QGroupBox {
background: transparent;
@ -465,63 +404,14 @@ QGroupBox::title {
color: ${text_dim};
}
/* ---------- Frames ---------- */
QFrame[frameShape="4"], /* HLine */
QFrame[frameShape="5"] /* VLine */ {
background: ${border};
color: ${border};
}
/* ---------- Toolbars ---------- */
QToolBar {
background: ${bg};
border: none;
spacing: 4px;
padding: 2px;
}
QToolBar::separator {
background: ${border};
width: 1px;
margin: 4px 4px;
}
/* ---------- Dock widgets ---------- */
QDockWidget {
color: ${text};
titlebar-close-icon: none;
}
QDockWidget::title {
background: ${bg_subtle};
padding: 4px;
border: 1px solid ${border};
}
/* ---------- Rubber band (multi-select drag rectangle) ---------- */
/* ---------- Rubber band (multi-select drag) ---------- */
QRubberBand {
background: ${accent};
border: 1px solid ${accent};
/* Qt blends rubber band at ~30% so this reads as a translucent
* accent-tinted rectangle without needing rgba(). */
}
/* ---------- Library count label states ---------- */
/*
* The library tab's count label switches between three visual states
* depending on what refresh() found. The state is exposed as a Qt
* dynamic property `libraryCountState` so users can override these
* rules in their custom.qss without touching the Python.
*
* normal N files default text color, no rule needed
* empty no items dim text (no items found, search miss)
* error bad/unreachable danger color + bold (real error)
*/
QLabel[libraryCountState="empty"] {
color: ${text_dim};
@ -531,18 +421,18 @@ QLabel[libraryCountState="error"] {
font-weight: bold;
}
/* ---------- Thumbnail dot indicators (Qt properties on ThumbnailWidget) ---------- */
/* ---------- Thumbnail indicators ---------- */
ThumbnailWidget {
qproperty-savedColor: #22cc22; /* green dot: saved to library — universal "confirmed" feel */
qproperty-bookmarkedColor: #ffcc00; /* yellow star: bookmarked */
qproperty-savedColor: #22cc22;
qproperty-bookmarkedColor: #ffcc00;
qproperty-selectionColor: ${accent};
qproperty-multiSelectColor: ${accent_dim};
qproperty-hoverColor: ${accent};
qproperty-idleColor: ${border_strong};
}
/* ---------- Info panel tag category colors ---------- */
/* ---------- Info panel tag colors ---------- */
InfoPanel {
qproperty-tagArtistColor: ${warning};
@ -553,19 +443,13 @@ InfoPanel {
qproperty-tagLoreColor: ${text_dim};
}
/* ---------- Video player letterbox / pillarbox color (mpv background) ---------- */
/* ---------- Video player letterbox ---------- */
VideoPlayer {
qproperty-letterboxColor: ${bg};
}
/* ---------- Popout overlay bars (slideshow toolbar + slideshow controls + embedded preview controls) ---------- */
/*
* The popout window's translucent toolbar (top) and transport controls
* (bottom) float over the video content. The bg color comes from the
* @palette overlay_bg slot. Children get the classic overlay treatment:
* transparent backgrounds, near-white text, hairline borders.
*/
/* ---------- Popout overlay bars ---------- */
QWidget#_slideshow_toolbar,
QWidget#_slideshow_controls,
@ -588,6 +472,8 @@ QWidget#_preview_controls QPushButton {
color: white;
border: 1px solid rgba(255, 255, 255, 80);
padding: 2px 6px;
font-size: 15px;
font-weight: bold;
}
QWidget#_slideshow_toolbar QPushButton:hover,
QWidget#_slideshow_controls QPushButton:hover,

View File

@ -1,4 +1,4 @@
/* booru-viewer Gruvbox Dark
/* booru-viewer Gruvbox
*
* Edit the @palette block below to recolor this square variant. The body uses
* ${...} placeholders that the app's _load_user_qss preprocessor
@ -9,26 +9,25 @@
*/
/* @palette
bg: #282828
bg_alt: #1d2021
bg_subtle: #3c3836
bg_hover: #504945
bg_active: #665c54
text: #ebdbb2
text_dim: #d5c4a1
text_disabled: #928374
border: #3c3836
border_strong: #504945
accent: #d79921
accent_text: #282828
accent_dim: #fabd2f
link: #83a598
danger: #fb4934
success: #b8bb26
warning: #fabd2f
overlay_bg: rgba(40, 40, 40, 200)
bg: #282828
bg_alt: #1d2021
bg_subtle: #3c3836
bg_hover: #504945
bg_active: #665c54
text: #ebdbb2
text_dim: #d5c4a1
text_disabled: #928374
border: #3c3836
border_strong: #504945
accent: #d79921
accent_text: #282828
accent_dim: #fabd2f
link: #83a598
danger: #fb4934
success: #b8bb26
warning: #fabd2f
overlay_bg: rgba(40, 40, 40, 200)
*/
/* ---------- Base ---------- */
QWidget {
@ -43,8 +42,6 @@ QWidget:disabled {
color: ${text_disabled};
}
/* Labels should never paint an opaque background they sit on top of
* other widgets in many places (toolbars, info panels, overlays). */
QLabel {
background: transparent;
}
@ -91,47 +88,25 @@ QPushButton:flat:hover {
background-color: ${bg_hover};
}
QToolButton {
background-color: transparent;
color: ${text};
border: 1px solid transparent;
padding: 4px;
}
QToolButton:hover {
background-color: ${bg_hover};
border-color: ${border_strong};
}
QToolButton:pressed, QToolButton:checked {
background-color: ${bg_active};
}
/* ---------- Inputs ---------- */
QLineEdit, QSpinBox, QDoubleSpinBox, QTextEdit, QPlainTextEdit {
QLineEdit, QSpinBox, QTextEdit {
background-color: ${bg_subtle};
color: ${text};
border: 1px solid ${border_strong};
padding: 2px 6px;
/* min-height ensures the painted text fits inside the widget bounds
* even when a parent layout (e.g. QFormLayout inside a QGroupBox)
* compresses the natural sizeHint. Without this, spinboxes in dense
* forms render with the top of the value text clipped. */
min-height: 16px;
selection-background-color: ${accent};
selection-color: ${accent_text};
}
QLineEdit:focus,
QSpinBox:focus,
QDoubleSpinBox:focus,
QTextEdit:focus,
QPlainTextEdit:focus {
QTextEdit:focus {
border-color: ${accent};
}
QLineEdit:disabled,
QSpinBox:disabled,
QDoubleSpinBox:disabled,
QTextEdit:disabled,
QPlainTextEdit:disabled {
QTextEdit:disabled {
background-color: ${bg_alt};
color: ${text_disabled};
border-color: ${border};
@ -309,17 +284,6 @@ QSlider::handle:horizontal:hover {
background: ${accent_dim};
}
QSlider::groove:vertical {
background: ${bg_subtle};
width: 4px;
}
QSlider::handle:vertical {
background: ${accent};
width: 12px;
height: 12px;
margin: 0 -5px;
}
/* ---------- Progress ---------- */
QProgressBar {
@ -333,32 +297,27 @@ QProgressBar::chunk {
background-color: ${accent};
}
/* ---------- Checkboxes & radio buttons ---------- */
/* ---------- Checkboxes ---------- */
QCheckBox, QRadioButton {
QCheckBox {
background: transparent;
color: ${text};
spacing: 6px;
}
QCheckBox::indicator, QRadioButton::indicator {
QCheckBox::indicator {
width: 14px;
height: 14px;
background-color: ${bg_subtle};
border: 1px solid ${border_strong};
}
QCheckBox::indicator {
}
QRadioButton::indicator {
border-radius: 7px;
}
QCheckBox::indicator:hover, QRadioButton::indicator:hover {
QCheckBox::indicator:hover {
border-color: ${accent};
}
QCheckBox::indicator:checked, QRadioButton::indicator:checked {
QCheckBox::indicator:checked {
background-color: ${accent};
border-color: ${accent};
}
QCheckBox::indicator:disabled, QRadioButton::indicator:disabled {
QCheckBox::indicator:disabled {
background-color: ${bg_alt};
border-color: ${border};
}
@ -372,9 +331,9 @@ QToolTip {
padding: 4px 6px;
}
/* ---------- Item views (lists, trees, tables) ---------- */
/* ---------- Lists ---------- */
QListView, QListWidget, QTreeView, QTreeWidget, QTableView, QTableWidget {
QListView, QListWidget {
background-color: ${bg};
alternate-background-color: ${bg_alt};
color: ${text};
@ -383,35 +342,18 @@ QListView, QListWidget, QTreeView, QTreeWidget, QTableView, QTableWidget {
selection-color: ${accent_text};
outline: none;
}
QListView::item, QListWidget::item,
QTreeView::item, QTreeWidget::item,
QTableView::item, QTableWidget::item {
QListView::item, QListWidget::item {
padding: 4px;
}
QListView::item:hover, QListWidget::item:hover,
QTreeView::item:hover, QTreeWidget::item:hover,
QTableView::item:hover, QTableWidget::item:hover {
QListView::item:hover, QListWidget::item:hover {
background-color: ${bg_hover};
}
QListView::item:selected, QListWidget::item:selected,
QTreeView::item:selected, QTreeWidget::item:selected,
QTableView::item:selected, QTableWidget::item:selected {
QListView::item:selected, QListWidget::item:selected {
background-color: ${accent};
color: ${accent_text};
}
QHeaderView::section {
background-color: ${bg_subtle};
color: ${text};
border: none;
border-right: 1px solid ${border};
padding: 4px 8px;
}
QHeaderView::section:hover {
background-color: ${bg_hover};
}
/* ---------- Tabs ---------- */
/* ---------- Tabs (settings dialog) ---------- */
QTabWidget::pane {
border: 1px solid ${border};
@ -436,7 +378,7 @@ QTabBar::tab:hover:!selected {
color: ${text};
}
/* ---------- Group boxes ---------- */
/* ---------- Group boxes (settings dialog) ---------- */
QGroupBox {
background: transparent;
@ -452,63 +394,14 @@ QGroupBox::title {
color: ${text_dim};
}
/* ---------- Frames ---------- */
QFrame[frameShape="4"], /* HLine */
QFrame[frameShape="5"] /* VLine */ {
background: ${border};
color: ${border};
}
/* ---------- Toolbars ---------- */
QToolBar {
background: ${bg};
border: none;
spacing: 4px;
padding: 2px;
}
QToolBar::separator {
background: ${border};
width: 1px;
margin: 4px 4px;
}
/* ---------- Dock widgets ---------- */
QDockWidget {
color: ${text};
titlebar-close-icon: none;
}
QDockWidget::title {
background: ${bg_subtle};
padding: 4px;
border: 1px solid ${border};
}
/* ---------- Rubber band (multi-select drag rectangle) ---------- */
/* ---------- Rubber band (multi-select drag) ---------- */
QRubberBand {
background: ${accent};
border: 1px solid ${accent};
/* Qt blends rubber band at ~30% so this reads as a translucent
* accent-tinted rectangle without needing rgba(). */
}
/* ---------- Library count label states ---------- */
/*
* The library tab's count label switches between three visual states
* depending on what refresh() found. The state is exposed as a Qt
* dynamic property `libraryCountState` so users can override these
* rules in their custom.qss without touching the Python.
*
* normal N files default text color, no rule needed
* empty no items dim text (no items found, search miss)
* error bad/unreachable danger color + bold (real error)
*/
QLabel[libraryCountState="empty"] {
color: ${text_dim};
@ -518,18 +411,18 @@ QLabel[libraryCountState="error"] {
font-weight: bold;
}
/* ---------- Thumbnail dot indicators (Qt properties on ThumbnailWidget) ---------- */
/* ---------- Thumbnail indicators ---------- */
ThumbnailWidget {
qproperty-savedColor: #22cc22; /* green dot: saved to library — universal "confirmed" feel */
qproperty-bookmarkedColor: #ffcc00; /* yellow star: bookmarked */
qproperty-savedColor: #22cc22;
qproperty-bookmarkedColor: #ffcc00;
qproperty-selectionColor: ${accent};
qproperty-multiSelectColor: ${accent_dim};
qproperty-hoverColor: ${accent};
qproperty-idleColor: ${border_strong};
}
/* ---------- Info panel tag category colors ---------- */
/* ---------- Info panel tag colors ---------- */
InfoPanel {
qproperty-tagArtistColor: ${warning};
@ -540,19 +433,13 @@ InfoPanel {
qproperty-tagLoreColor: ${text_dim};
}
/* ---------- Video player letterbox / pillarbox color (mpv background) ---------- */
/* ---------- Video player letterbox ---------- */
VideoPlayer {
qproperty-letterboxColor: ${bg};
}
/* ---------- Popout overlay bars (slideshow toolbar + slideshow controls + embedded preview controls) ---------- */
/*
* The popout window's translucent toolbar (top) and transport controls
* (bottom) float over the video content. The bg color comes from the
* @palette overlay_bg slot. Children get the classic overlay treatment:
* transparent backgrounds, near-white text, hairline borders.
*/
/* ---------- Popout overlay bars ---------- */
QWidget#_slideshow_toolbar,
QWidget#_slideshow_controls,
@ -575,6 +462,8 @@ QWidget#_preview_controls QPushButton {
color: white;
border: 1px solid rgba(255, 255, 255, 80);
padding: 2px 6px;
font-size: 15px;
font-weight: bold;
}
QWidget#_slideshow_toolbar QPushButton:hover,
QWidget#_slideshow_controls QPushButton:hover,

View File

@ -9,26 +9,25 @@
*/
/* @palette
bg: #2e3440
bg_alt: #272b36
bg_subtle: #3b4252
bg_hover: #434c5e
bg_active: #4c566a
text: #eceff4
text_dim: #d8dee9
text_disabled: #4c566a
border: #3b4252
border_strong: #4c566a
accent: #88c0d0
accent_text: #2e3440
accent_dim: #81a1c1
link: #8fbcbb
danger: #bf616a
success: #a3be8c
warning: #ebcb8b
overlay_bg: rgba(46, 52, 64, 200)
bg: #2e3440
bg_alt: #272b36
bg_subtle: #3b4252
bg_hover: #434c5e
bg_active: #4c566a
text: #eceff4
text_dim: #d8dee9
text_disabled: #4c566a
border: #3b4252
border_strong: #4c566a
accent: #88c0d0
accent_text: #2e3440
accent_dim: #81a1c1
link: #8fbcbb
danger: #bf616a
success: #a3be8c
warning: #ebcb8b
overlay_bg: rgba(46, 52, 64, 200)
*/
/* ---------- Base ---------- */
QWidget {
@ -43,8 +42,6 @@ QWidget:disabled {
color: ${text_disabled};
}
/* Labels should never paint an opaque background they sit on top of
* other widgets in many places (toolbars, info panels, overlays). */
QLabel {
background: transparent;
}
@ -92,49 +89,26 @@ QPushButton:flat:hover {
background-color: ${bg_hover};
}
QToolButton {
background-color: transparent;
color: ${text};
border: 1px solid transparent;
border-radius: 4px;
padding: 4px;
}
QToolButton:hover {
background-color: ${bg_hover};
border-color: ${border_strong};
}
QToolButton:pressed, QToolButton:checked {
background-color: ${bg_active};
}
/* ---------- Inputs ---------- */
QLineEdit, QSpinBox, QDoubleSpinBox, QTextEdit, QPlainTextEdit {
QLineEdit, QSpinBox, QTextEdit {
background-color: ${bg_subtle};
color: ${text};
border: 1px solid ${border_strong};
border-radius: 4px;
padding: 2px 6px;
/* min-height ensures the painted text fits inside the widget bounds
* even when a parent layout (e.g. QFormLayout inside a QGroupBox)
* compresses the natural sizeHint. Without this, spinboxes in dense
* forms render with the top of the value text clipped. */
min-height: 16px;
selection-background-color: ${accent};
selection-color: ${accent_text};
}
QLineEdit:focus,
QSpinBox:focus,
QDoubleSpinBox:focus,
QTextEdit:focus,
QPlainTextEdit:focus {
QTextEdit:focus {
border-color: ${accent};
}
QLineEdit:disabled,
QSpinBox:disabled,
QDoubleSpinBox:disabled,
QTextEdit:disabled,
QPlainTextEdit:disabled {
QTextEdit:disabled {
background-color: ${bg_alt};
color: ${text_disabled};
border-color: ${border};
@ -315,19 +289,6 @@ QSlider::handle:horizontal:hover {
background: ${accent_dim};
}
QSlider::groove:vertical {
background: ${bg_subtle};
width: 4px;
border-radius: 2px;
}
QSlider::handle:vertical {
background: ${accent};
width: 12px;
height: 12px;
margin: 0 -5px;
border-radius: 6px;
}
/* ---------- Progress ---------- */
QProgressBar {
@ -343,33 +304,28 @@ QProgressBar::chunk {
border-radius: 3px;
}
/* ---------- Checkboxes & radio buttons ---------- */
/* ---------- Checkboxes ---------- */
QCheckBox, QRadioButton {
QCheckBox {
background: transparent;
color: ${text};
spacing: 6px;
}
QCheckBox::indicator, QRadioButton::indicator {
QCheckBox::indicator {
width: 14px;
height: 14px;
background-color: ${bg_subtle};
border: 1px solid ${border_strong};
}
QCheckBox::indicator {
border-radius: 3px;
}
QRadioButton::indicator {
border-radius: 7px;
}
QCheckBox::indicator:hover, QRadioButton::indicator:hover {
QCheckBox::indicator:hover {
border-color: ${accent};
}
QCheckBox::indicator:checked, QRadioButton::indicator:checked {
QCheckBox::indicator:checked {
background-color: ${accent};
border-color: ${accent};
}
QCheckBox::indicator:disabled, QRadioButton::indicator:disabled {
QCheckBox::indicator:disabled {
background-color: ${bg_alt};
border-color: ${border};
}
@ -384,9 +340,9 @@ QToolTip {
border-radius: 3px;
}
/* ---------- Item views (lists, trees, tables) ---------- */
/* ---------- Lists ---------- */
QListView, QListWidget, QTreeView, QTreeWidget, QTableView, QTableWidget {
QListView, QListWidget {
background-color: ${bg};
alternate-background-color: ${bg_alt};
color: ${text};
@ -395,35 +351,18 @@ QListView, QListWidget, QTreeView, QTreeWidget, QTableView, QTableWidget {
selection-color: ${accent_text};
outline: none;
}
QListView::item, QListWidget::item,
QTreeView::item, QTreeWidget::item,
QTableView::item, QTableWidget::item {
QListView::item, QListWidget::item {
padding: 4px;
}
QListView::item:hover, QListWidget::item:hover,
QTreeView::item:hover, QTreeWidget::item:hover,
QTableView::item:hover, QTableWidget::item:hover {
QListView::item:hover, QListWidget::item:hover {
background-color: ${bg_hover};
}
QListView::item:selected, QListWidget::item:selected,
QTreeView::item:selected, QTreeWidget::item:selected,
QTableView::item:selected, QTableWidget::item:selected {
QListView::item:selected, QListWidget::item:selected {
background-color: ${accent};
color: ${accent_text};
}
QHeaderView::section {
background-color: ${bg_subtle};
color: ${text};
border: none;
border-right: 1px solid ${border};
padding: 4px 8px;
}
QHeaderView::section:hover {
background-color: ${bg_hover};
}
/* ---------- Tabs ---------- */
/* ---------- Tabs (settings dialog) ---------- */
QTabWidget::pane {
border: 1px solid ${border};
@ -448,7 +387,7 @@ QTabBar::tab:hover:!selected {
color: ${text};
}
/* ---------- Group boxes ---------- */
/* ---------- Group boxes (settings dialog) ---------- */
QGroupBox {
background: transparent;
@ -465,63 +404,14 @@ QGroupBox::title {
color: ${text_dim};
}
/* ---------- Frames ---------- */
QFrame[frameShape="4"], /* HLine */
QFrame[frameShape="5"] /* VLine */ {
background: ${border};
color: ${border};
}
/* ---------- Toolbars ---------- */
QToolBar {
background: ${bg};
border: none;
spacing: 4px;
padding: 2px;
}
QToolBar::separator {
background: ${border};
width: 1px;
margin: 4px 4px;
}
/* ---------- Dock widgets ---------- */
QDockWidget {
color: ${text};
titlebar-close-icon: none;
}
QDockWidget::title {
background: ${bg_subtle};
padding: 4px;
border: 1px solid ${border};
}
/* ---------- Rubber band (multi-select drag rectangle) ---------- */
/* ---------- Rubber band (multi-select drag) ---------- */
QRubberBand {
background: ${accent};
border: 1px solid ${accent};
/* Qt blends rubber band at ~30% so this reads as a translucent
* accent-tinted rectangle without needing rgba(). */
}
/* ---------- Library count label states ---------- */
/*
* The library tab's count label switches between three visual states
* depending on what refresh() found. The state is exposed as a Qt
* dynamic property `libraryCountState` so users can override these
* rules in their custom.qss without touching the Python.
*
* normal N files default text color, no rule needed
* empty no items dim text (no items found, search miss)
* error bad/unreachable danger color + bold (real error)
*/
QLabel[libraryCountState="empty"] {
color: ${text_dim};
@ -531,18 +421,18 @@ QLabel[libraryCountState="error"] {
font-weight: bold;
}
/* ---------- Thumbnail dot indicators (Qt properties on ThumbnailWidget) ---------- */
/* ---------- Thumbnail indicators ---------- */
ThumbnailWidget {
qproperty-savedColor: #22cc22; /* green dot: saved to library — universal "confirmed" feel */
qproperty-bookmarkedColor: #ffcc00; /* yellow star: bookmarked */
qproperty-savedColor: #22cc22;
qproperty-bookmarkedColor: #ffcc00;
qproperty-selectionColor: ${accent};
qproperty-multiSelectColor: ${accent_dim};
qproperty-hoverColor: ${accent};
qproperty-idleColor: ${border_strong};
}
/* ---------- Info panel tag category colors ---------- */
/* ---------- Info panel tag colors ---------- */
InfoPanel {
qproperty-tagArtistColor: ${warning};
@ -553,19 +443,13 @@ InfoPanel {
qproperty-tagLoreColor: ${text_dim};
}
/* ---------- Video player letterbox / pillarbox color (mpv background) ---------- */
/* ---------- Video player letterbox ---------- */
VideoPlayer {
qproperty-letterboxColor: ${bg};
}
/* ---------- Popout overlay bars (slideshow toolbar + slideshow controls + embedded preview controls) ---------- */
/*
* The popout window's translucent toolbar (top) and transport controls
* (bottom) float over the video content. The bg color comes from the
* @palette overlay_bg slot. Children get the classic overlay treatment:
* transparent backgrounds, near-white text, hairline borders.
*/
/* ---------- Popout overlay bars ---------- */
QWidget#_slideshow_toolbar,
QWidget#_slideshow_controls,
@ -588,6 +472,8 @@ QWidget#_preview_controls QPushButton {
color: white;
border: 1px solid rgba(255, 255, 255, 80);
padding: 2px 6px;
font-size: 15px;
font-weight: bold;
}
QWidget#_slideshow_toolbar QPushButton:hover,
QWidget#_slideshow_controls QPushButton:hover,

View File

@ -9,26 +9,25 @@
*/
/* @palette
bg: #2e3440
bg_alt: #272b36
bg_subtle: #3b4252
bg_hover: #434c5e
bg_active: #4c566a
text: #eceff4
text_dim: #d8dee9
text_disabled: #4c566a
border: #3b4252
border_strong: #4c566a
accent: #88c0d0
accent_text: #2e3440
accent_dim: #81a1c1
link: #8fbcbb
danger: #bf616a
success: #a3be8c
warning: #ebcb8b
overlay_bg: rgba(46, 52, 64, 200)
bg: #2e3440
bg_alt: #272b36
bg_subtle: #3b4252
bg_hover: #434c5e
bg_active: #4c566a
text: #eceff4
text_dim: #d8dee9
text_disabled: #4c566a
border: #3b4252
border_strong: #4c566a
accent: #88c0d0
accent_text: #2e3440
accent_dim: #81a1c1
link: #8fbcbb
danger: #bf616a
success: #a3be8c
warning: #ebcb8b
overlay_bg: rgba(46, 52, 64, 200)
*/
/* ---------- Base ---------- */
QWidget {
@ -43,8 +42,6 @@ QWidget:disabled {
color: ${text_disabled};
}
/* Labels should never paint an opaque background they sit on top of
* other widgets in many places (toolbars, info panels, overlays). */
QLabel {
background: transparent;
}
@ -91,47 +88,25 @@ QPushButton:flat:hover {
background-color: ${bg_hover};
}
QToolButton {
background-color: transparent;
color: ${text};
border: 1px solid transparent;
padding: 4px;
}
QToolButton:hover {
background-color: ${bg_hover};
border-color: ${border_strong};
}
QToolButton:pressed, QToolButton:checked {
background-color: ${bg_active};
}
/* ---------- Inputs ---------- */
QLineEdit, QSpinBox, QDoubleSpinBox, QTextEdit, QPlainTextEdit {
QLineEdit, QSpinBox, QTextEdit {
background-color: ${bg_subtle};
color: ${text};
border: 1px solid ${border_strong};
padding: 2px 6px;
/* min-height ensures the painted text fits inside the widget bounds
* even when a parent layout (e.g. QFormLayout inside a QGroupBox)
* compresses the natural sizeHint. Without this, spinboxes in dense
* forms render with the top of the value text clipped. */
min-height: 16px;
selection-background-color: ${accent};
selection-color: ${accent_text};
}
QLineEdit:focus,
QSpinBox:focus,
QDoubleSpinBox:focus,
QTextEdit:focus,
QPlainTextEdit:focus {
QTextEdit:focus {
border-color: ${accent};
}
QLineEdit:disabled,
QSpinBox:disabled,
QDoubleSpinBox:disabled,
QTextEdit:disabled,
QPlainTextEdit:disabled {
QTextEdit:disabled {
background-color: ${bg_alt};
color: ${text_disabled};
border-color: ${border};
@ -309,17 +284,6 @@ QSlider::handle:horizontal:hover {
background: ${accent_dim};
}
QSlider::groove:vertical {
background: ${bg_subtle};
width: 4px;
}
QSlider::handle:vertical {
background: ${accent};
width: 12px;
height: 12px;
margin: 0 -5px;
}
/* ---------- Progress ---------- */
QProgressBar {
@ -333,32 +297,27 @@ QProgressBar::chunk {
background-color: ${accent};
}
/* ---------- Checkboxes & radio buttons ---------- */
/* ---------- Checkboxes ---------- */
QCheckBox, QRadioButton {
QCheckBox {
background: transparent;
color: ${text};
spacing: 6px;
}
QCheckBox::indicator, QRadioButton::indicator {
QCheckBox::indicator {
width: 14px;
height: 14px;
background-color: ${bg_subtle};
border: 1px solid ${border_strong};
}
QCheckBox::indicator {
}
QRadioButton::indicator {
border-radius: 7px;
}
QCheckBox::indicator:hover, QRadioButton::indicator:hover {
QCheckBox::indicator:hover {
border-color: ${accent};
}
QCheckBox::indicator:checked, QRadioButton::indicator:checked {
QCheckBox::indicator:checked {
background-color: ${accent};
border-color: ${accent};
}
QCheckBox::indicator:disabled, QRadioButton::indicator:disabled {
QCheckBox::indicator:disabled {
background-color: ${bg_alt};
border-color: ${border};
}
@ -372,9 +331,9 @@ QToolTip {
padding: 4px 6px;
}
/* ---------- Item views (lists, trees, tables) ---------- */
/* ---------- Lists ---------- */
QListView, QListWidget, QTreeView, QTreeWidget, QTableView, QTableWidget {
QListView, QListWidget {
background-color: ${bg};
alternate-background-color: ${bg_alt};
color: ${text};
@ -383,35 +342,18 @@ QListView, QListWidget, QTreeView, QTreeWidget, QTableView, QTableWidget {
selection-color: ${accent_text};
outline: none;
}
QListView::item, QListWidget::item,
QTreeView::item, QTreeWidget::item,
QTableView::item, QTableWidget::item {
QListView::item, QListWidget::item {
padding: 4px;
}
QListView::item:hover, QListWidget::item:hover,
QTreeView::item:hover, QTreeWidget::item:hover,
QTableView::item:hover, QTableWidget::item:hover {
QListView::item:hover, QListWidget::item:hover {
background-color: ${bg_hover};
}
QListView::item:selected, QListWidget::item:selected,
QTreeView::item:selected, QTreeWidget::item:selected,
QTableView::item:selected, QTableWidget::item:selected {
QListView::item:selected, QListWidget::item:selected {
background-color: ${accent};
color: ${accent_text};
}
QHeaderView::section {
background-color: ${bg_subtle};
color: ${text};
border: none;
border-right: 1px solid ${border};
padding: 4px 8px;
}
QHeaderView::section:hover {
background-color: ${bg_hover};
}
/* ---------- Tabs ---------- */
/* ---------- Tabs (settings dialog) ---------- */
QTabWidget::pane {
border: 1px solid ${border};
@ -436,7 +378,7 @@ QTabBar::tab:hover:!selected {
color: ${text};
}
/* ---------- Group boxes ---------- */
/* ---------- Group boxes (settings dialog) ---------- */
QGroupBox {
background: transparent;
@ -452,63 +394,14 @@ QGroupBox::title {
color: ${text_dim};
}
/* ---------- Frames ---------- */
QFrame[frameShape="4"], /* HLine */
QFrame[frameShape="5"] /* VLine */ {
background: ${border};
color: ${border};
}
/* ---------- Toolbars ---------- */
QToolBar {
background: ${bg};
border: none;
spacing: 4px;
padding: 2px;
}
QToolBar::separator {
background: ${border};
width: 1px;
margin: 4px 4px;
}
/* ---------- Dock widgets ---------- */
QDockWidget {
color: ${text};
titlebar-close-icon: none;
}
QDockWidget::title {
background: ${bg_subtle};
padding: 4px;
border: 1px solid ${border};
}
/* ---------- Rubber band (multi-select drag rectangle) ---------- */
/* ---------- Rubber band (multi-select drag) ---------- */
QRubberBand {
background: ${accent};
border: 1px solid ${accent};
/* Qt blends rubber band at ~30% so this reads as a translucent
* accent-tinted rectangle without needing rgba(). */
}
/* ---------- Library count label states ---------- */
/*
* The library tab's count label switches between three visual states
* depending on what refresh() found. The state is exposed as a Qt
* dynamic property `libraryCountState` so users can override these
* rules in their custom.qss without touching the Python.
*
* normal N files default text color, no rule needed
* empty no items dim text (no items found, search miss)
* error bad/unreachable danger color + bold (real error)
*/
QLabel[libraryCountState="empty"] {
color: ${text_dim};
@ -518,18 +411,18 @@ QLabel[libraryCountState="error"] {
font-weight: bold;
}
/* ---------- Thumbnail dot indicators (Qt properties on ThumbnailWidget) ---------- */
/* ---------- Thumbnail indicators ---------- */
ThumbnailWidget {
qproperty-savedColor: #22cc22; /* green dot: saved to library — universal "confirmed" feel */
qproperty-bookmarkedColor: #ffcc00; /* yellow star: bookmarked */
qproperty-savedColor: #22cc22;
qproperty-bookmarkedColor: #ffcc00;
qproperty-selectionColor: ${accent};
qproperty-multiSelectColor: ${accent_dim};
qproperty-hoverColor: ${accent};
qproperty-idleColor: ${border_strong};
}
/* ---------- Info panel tag category colors ---------- */
/* ---------- Info panel tag colors ---------- */
InfoPanel {
qproperty-tagArtistColor: ${warning};
@ -540,19 +433,13 @@ InfoPanel {
qproperty-tagLoreColor: ${text_dim};
}
/* ---------- Video player letterbox / pillarbox color (mpv background) ---------- */
/* ---------- Video player letterbox ---------- */
VideoPlayer {
qproperty-letterboxColor: ${bg};
}
/* ---------- Popout overlay bars (slideshow toolbar + slideshow controls + embedded preview controls) ---------- */
/*
* The popout window's translucent toolbar (top) and transport controls
* (bottom) float over the video content. The bg color comes from the
* @palette overlay_bg slot. Children get the classic overlay treatment:
* transparent backgrounds, near-white text, hairline borders.
*/
/* ---------- Popout overlay bars ---------- */
QWidget#_slideshow_toolbar,
QWidget#_slideshow_controls,
@ -575,6 +462,8 @@ QWidget#_preview_controls QPushButton {
color: white;
border: 1px solid rgba(255, 255, 255, 80);
padding: 2px 6px;
font-size: 15px;
font-weight: bold;
}
QWidget#_slideshow_toolbar QPushButton:hover,
QWidget#_slideshow_controls QPushButton:hover,

View File

@ -9,26 +9,25 @@
*/
/* @palette
bg: #002b36
bg_alt: #001f27
bg_subtle: #073642
bg_hover: #0d4654
bg_active: #586e75
text: #93a1a1
text_dim: #839496
text_disabled: #586e75
border: #073642
border_strong: #0d4654
accent: #268bd2
accent_text: #002b36
accent_dim: #2aa198
link: #2aa198
danger: #dc322f
success: #859900
warning: #b58900
overlay_bg: rgba(0, 43, 54, 200)
bg: #002b36
bg_alt: #001f27
bg_subtle: #073642
bg_hover: #0d4654
bg_active: #586e75
text: #93a1a1
text_dim: #839496
text_disabled: #586e75
border: #073642
border_strong: #0d4654
accent: #268bd2
accent_text: #002b36
accent_dim: #2aa198
link: #2aa198
danger: #dc322f
success: #859900
warning: #b58900
overlay_bg: rgba(0, 43, 54, 200)
*/
/* ---------- Base ---------- */
QWidget {
@ -43,8 +42,6 @@ QWidget:disabled {
color: ${text_disabled};
}
/* Labels should never paint an opaque background they sit on top of
* other widgets in many places (toolbars, info panels, overlays). */
QLabel {
background: transparent;
}
@ -92,49 +89,26 @@ QPushButton:flat:hover {
background-color: ${bg_hover};
}
QToolButton {
background-color: transparent;
color: ${text};
border: 1px solid transparent;
border-radius: 4px;
padding: 4px;
}
QToolButton:hover {
background-color: ${bg_hover};
border-color: ${border_strong};
}
QToolButton:pressed, QToolButton:checked {
background-color: ${bg_active};
}
/* ---------- Inputs ---------- */
QLineEdit, QSpinBox, QDoubleSpinBox, QTextEdit, QPlainTextEdit {
QLineEdit, QSpinBox, QTextEdit {
background-color: ${bg_subtle};
color: ${text};
border: 1px solid ${border_strong};
border-radius: 4px;
padding: 2px 6px;
/* min-height ensures the painted text fits inside the widget bounds
* even when a parent layout (e.g. QFormLayout inside a QGroupBox)
* compresses the natural sizeHint. Without this, spinboxes in dense
* forms render with the top of the value text clipped. */
min-height: 16px;
selection-background-color: ${accent};
selection-color: ${accent_text};
}
QLineEdit:focus,
QSpinBox:focus,
QDoubleSpinBox:focus,
QTextEdit:focus,
QPlainTextEdit:focus {
QTextEdit:focus {
border-color: ${accent};
}
QLineEdit:disabled,
QSpinBox:disabled,
QDoubleSpinBox:disabled,
QTextEdit:disabled,
QPlainTextEdit:disabled {
QTextEdit:disabled {
background-color: ${bg_alt};
color: ${text_disabled};
border-color: ${border};
@ -315,19 +289,6 @@ QSlider::handle:horizontal:hover {
background: ${accent_dim};
}
QSlider::groove:vertical {
background: ${bg_subtle};
width: 4px;
border-radius: 2px;
}
QSlider::handle:vertical {
background: ${accent};
width: 12px;
height: 12px;
margin: 0 -5px;
border-radius: 6px;
}
/* ---------- Progress ---------- */
QProgressBar {
@ -343,33 +304,28 @@ QProgressBar::chunk {
border-radius: 3px;
}
/* ---------- Checkboxes & radio buttons ---------- */
/* ---------- Checkboxes ---------- */
QCheckBox, QRadioButton {
QCheckBox {
background: transparent;
color: ${text};
spacing: 6px;
}
QCheckBox::indicator, QRadioButton::indicator {
QCheckBox::indicator {
width: 14px;
height: 14px;
background-color: ${bg_subtle};
border: 1px solid ${border_strong};
}
QCheckBox::indicator {
border-radius: 3px;
}
QRadioButton::indicator {
border-radius: 7px;
}
QCheckBox::indicator:hover, QRadioButton::indicator:hover {
QCheckBox::indicator:hover {
border-color: ${accent};
}
QCheckBox::indicator:checked, QRadioButton::indicator:checked {
QCheckBox::indicator:checked {
background-color: ${accent};
border-color: ${accent};
}
QCheckBox::indicator:disabled, QRadioButton::indicator:disabled {
QCheckBox::indicator:disabled {
background-color: ${bg_alt};
border-color: ${border};
}
@ -384,9 +340,9 @@ QToolTip {
border-radius: 3px;
}
/* ---------- Item views (lists, trees, tables) ---------- */
/* ---------- Lists ---------- */
QListView, QListWidget, QTreeView, QTreeWidget, QTableView, QTableWidget {
QListView, QListWidget {
background-color: ${bg};
alternate-background-color: ${bg_alt};
color: ${text};
@ -395,35 +351,18 @@ QListView, QListWidget, QTreeView, QTreeWidget, QTableView, QTableWidget {
selection-color: ${accent_text};
outline: none;
}
QListView::item, QListWidget::item,
QTreeView::item, QTreeWidget::item,
QTableView::item, QTableWidget::item {
QListView::item, QListWidget::item {
padding: 4px;
}
QListView::item:hover, QListWidget::item:hover,
QTreeView::item:hover, QTreeWidget::item:hover,
QTableView::item:hover, QTableWidget::item:hover {
QListView::item:hover, QListWidget::item:hover {
background-color: ${bg_hover};
}
QListView::item:selected, QListWidget::item:selected,
QTreeView::item:selected, QTreeWidget::item:selected,
QTableView::item:selected, QTableWidget::item:selected {
QListView::item:selected, QListWidget::item:selected {
background-color: ${accent};
color: ${accent_text};
}
QHeaderView::section {
background-color: ${bg_subtle};
color: ${text};
border: none;
border-right: 1px solid ${border};
padding: 4px 8px;
}
QHeaderView::section:hover {
background-color: ${bg_hover};
}
/* ---------- Tabs ---------- */
/* ---------- Tabs (settings dialog) ---------- */
QTabWidget::pane {
border: 1px solid ${border};
@ -448,7 +387,7 @@ QTabBar::tab:hover:!selected {
color: ${text};
}
/* ---------- Group boxes ---------- */
/* ---------- Group boxes (settings dialog) ---------- */
QGroupBox {
background: transparent;
@ -465,63 +404,14 @@ QGroupBox::title {
color: ${text_dim};
}
/* ---------- Frames ---------- */
QFrame[frameShape="4"], /* HLine */
QFrame[frameShape="5"] /* VLine */ {
background: ${border};
color: ${border};
}
/* ---------- Toolbars ---------- */
QToolBar {
background: ${bg};
border: none;
spacing: 4px;
padding: 2px;
}
QToolBar::separator {
background: ${border};
width: 1px;
margin: 4px 4px;
}
/* ---------- Dock widgets ---------- */
QDockWidget {
color: ${text};
titlebar-close-icon: none;
}
QDockWidget::title {
background: ${bg_subtle};
padding: 4px;
border: 1px solid ${border};
}
/* ---------- Rubber band (multi-select drag rectangle) ---------- */
/* ---------- Rubber band (multi-select drag) ---------- */
QRubberBand {
background: ${accent};
border: 1px solid ${accent};
/* Qt blends rubber band at ~30% so this reads as a translucent
* accent-tinted rectangle without needing rgba(). */
}
/* ---------- Library count label states ---------- */
/*
* The library tab's count label switches between three visual states
* depending on what refresh() found. The state is exposed as a Qt
* dynamic property `libraryCountState` so users can override these
* rules in their custom.qss without touching the Python.
*
* normal N files default text color, no rule needed
* empty no items dim text (no items found, search miss)
* error bad/unreachable danger color + bold (real error)
*/
QLabel[libraryCountState="empty"] {
color: ${text_dim};
@ -531,18 +421,18 @@ QLabel[libraryCountState="error"] {
font-weight: bold;
}
/* ---------- Thumbnail dot indicators (Qt properties on ThumbnailWidget) ---------- */
/* ---------- Thumbnail indicators ---------- */
ThumbnailWidget {
qproperty-savedColor: #22cc22; /* green dot: saved to library — universal "confirmed" feel */
qproperty-bookmarkedColor: #ffcc00; /* yellow star: bookmarked */
qproperty-savedColor: #22cc22;
qproperty-bookmarkedColor: #ffcc00;
qproperty-selectionColor: ${accent};
qproperty-multiSelectColor: ${accent_dim};
qproperty-hoverColor: ${accent};
qproperty-idleColor: ${border_strong};
}
/* ---------- Info panel tag category colors ---------- */
/* ---------- Info panel tag colors ---------- */
InfoPanel {
qproperty-tagArtistColor: ${warning};
@ -553,19 +443,13 @@ InfoPanel {
qproperty-tagLoreColor: ${text_dim};
}
/* ---------- Video player letterbox / pillarbox color (mpv background) ---------- */
/* ---------- Video player letterbox ---------- */
VideoPlayer {
qproperty-letterboxColor: ${bg};
}
/* ---------- Popout overlay bars (slideshow toolbar + slideshow controls + embedded preview controls) ---------- */
/*
* The popout window's translucent toolbar (top) and transport controls
* (bottom) float over the video content. The bg color comes from the
* @palette overlay_bg slot. Children get the classic overlay treatment:
* transparent backgrounds, near-white text, hairline borders.
*/
/* ---------- Popout overlay bars ---------- */
QWidget#_slideshow_toolbar,
QWidget#_slideshow_controls,
@ -588,6 +472,8 @@ QWidget#_preview_controls QPushButton {
color: white;
border: 1px solid rgba(255, 255, 255, 80);
padding: 2px 6px;
font-size: 15px;
font-weight: bold;
}
QWidget#_slideshow_toolbar QPushButton:hover,
QWidget#_slideshow_controls QPushButton:hover,

View File

@ -9,26 +9,25 @@
*/
/* @palette
bg: #002b36
bg_alt: #001f27
bg_subtle: #073642
bg_hover: #0d4654
bg_active: #586e75
text: #93a1a1
text_dim: #839496
text_disabled: #586e75
border: #073642
border_strong: #0d4654
accent: #268bd2
accent_text: #002b36
accent_dim: #2aa198
link: #2aa198
danger: #dc322f
success: #859900
warning: #b58900
overlay_bg: rgba(0, 43, 54, 200)
bg: #002b36
bg_alt: #001f27
bg_subtle: #073642
bg_hover: #0d4654
bg_active: #586e75
text: #93a1a1
text_dim: #839496
text_disabled: #586e75
border: #073642
border_strong: #0d4654
accent: #268bd2
accent_text: #002b36
accent_dim: #2aa198
link: #2aa198
danger: #dc322f
success: #859900
warning: #b58900
overlay_bg: rgba(0, 43, 54, 200)
*/
/* ---------- Base ---------- */
QWidget {
@ -43,8 +42,6 @@ QWidget:disabled {
color: ${text_disabled};
}
/* Labels should never paint an opaque background they sit on top of
* other widgets in many places (toolbars, info panels, overlays). */
QLabel {
background: transparent;
}
@ -91,47 +88,25 @@ QPushButton:flat:hover {
background-color: ${bg_hover};
}
QToolButton {
background-color: transparent;
color: ${text};
border: 1px solid transparent;
padding: 4px;
}
QToolButton:hover {
background-color: ${bg_hover};
border-color: ${border_strong};
}
QToolButton:pressed, QToolButton:checked {
background-color: ${bg_active};
}
/* ---------- Inputs ---------- */
QLineEdit, QSpinBox, QDoubleSpinBox, QTextEdit, QPlainTextEdit {
QLineEdit, QSpinBox, QTextEdit {
background-color: ${bg_subtle};
color: ${text};
border: 1px solid ${border_strong};
padding: 2px 6px;
/* min-height ensures the painted text fits inside the widget bounds
* even when a parent layout (e.g. QFormLayout inside a QGroupBox)
* compresses the natural sizeHint. Without this, spinboxes in dense
* forms render with the top of the value text clipped. */
min-height: 16px;
selection-background-color: ${accent};
selection-color: ${accent_text};
}
QLineEdit:focus,
QSpinBox:focus,
QDoubleSpinBox:focus,
QTextEdit:focus,
QPlainTextEdit:focus {
QTextEdit:focus {
border-color: ${accent};
}
QLineEdit:disabled,
QSpinBox:disabled,
QDoubleSpinBox:disabled,
QTextEdit:disabled,
QPlainTextEdit:disabled {
QTextEdit:disabled {
background-color: ${bg_alt};
color: ${text_disabled};
border-color: ${border};
@ -309,17 +284,6 @@ QSlider::handle:horizontal:hover {
background: ${accent_dim};
}
QSlider::groove:vertical {
background: ${bg_subtle};
width: 4px;
}
QSlider::handle:vertical {
background: ${accent};
width: 12px;
height: 12px;
margin: 0 -5px;
}
/* ---------- Progress ---------- */
QProgressBar {
@ -333,32 +297,27 @@ QProgressBar::chunk {
background-color: ${accent};
}
/* ---------- Checkboxes & radio buttons ---------- */
/* ---------- Checkboxes ---------- */
QCheckBox, QRadioButton {
QCheckBox {
background: transparent;
color: ${text};
spacing: 6px;
}
QCheckBox::indicator, QRadioButton::indicator {
QCheckBox::indicator {
width: 14px;
height: 14px;
background-color: ${bg_subtle};
border: 1px solid ${border_strong};
}
QCheckBox::indicator {
}
QRadioButton::indicator {
border-radius: 7px;
}
QCheckBox::indicator:hover, QRadioButton::indicator:hover {
QCheckBox::indicator:hover {
border-color: ${accent};
}
QCheckBox::indicator:checked, QRadioButton::indicator:checked {
QCheckBox::indicator:checked {
background-color: ${accent};
border-color: ${accent};
}
QCheckBox::indicator:disabled, QRadioButton::indicator:disabled {
QCheckBox::indicator:disabled {
background-color: ${bg_alt};
border-color: ${border};
}
@ -372,9 +331,9 @@ QToolTip {
padding: 4px 6px;
}
/* ---------- Item views (lists, trees, tables) ---------- */
/* ---------- Lists ---------- */
QListView, QListWidget, QTreeView, QTreeWidget, QTableView, QTableWidget {
QListView, QListWidget {
background-color: ${bg};
alternate-background-color: ${bg_alt};
color: ${text};
@ -383,35 +342,18 @@ QListView, QListWidget, QTreeView, QTreeWidget, QTableView, QTableWidget {
selection-color: ${accent_text};
outline: none;
}
QListView::item, QListWidget::item,
QTreeView::item, QTreeWidget::item,
QTableView::item, QTableWidget::item {
QListView::item, QListWidget::item {
padding: 4px;
}
QListView::item:hover, QListWidget::item:hover,
QTreeView::item:hover, QTreeWidget::item:hover,
QTableView::item:hover, QTableWidget::item:hover {
QListView::item:hover, QListWidget::item:hover {
background-color: ${bg_hover};
}
QListView::item:selected, QListWidget::item:selected,
QTreeView::item:selected, QTreeWidget::item:selected,
QTableView::item:selected, QTableWidget::item:selected {
QListView::item:selected, QListWidget::item:selected {
background-color: ${accent};
color: ${accent_text};
}
QHeaderView::section {
background-color: ${bg_subtle};
color: ${text};
border: none;
border-right: 1px solid ${border};
padding: 4px 8px;
}
QHeaderView::section:hover {
background-color: ${bg_hover};
}
/* ---------- Tabs ---------- */
/* ---------- Tabs (settings dialog) ---------- */
QTabWidget::pane {
border: 1px solid ${border};
@ -436,7 +378,7 @@ QTabBar::tab:hover:!selected {
color: ${text};
}
/* ---------- Group boxes ---------- */
/* ---------- Group boxes (settings dialog) ---------- */
QGroupBox {
background: transparent;
@ -452,63 +394,14 @@ QGroupBox::title {
color: ${text_dim};
}
/* ---------- Frames ---------- */
QFrame[frameShape="4"], /* HLine */
QFrame[frameShape="5"] /* VLine */ {
background: ${border};
color: ${border};
}
/* ---------- Toolbars ---------- */
QToolBar {
background: ${bg};
border: none;
spacing: 4px;
padding: 2px;
}
QToolBar::separator {
background: ${border};
width: 1px;
margin: 4px 4px;
}
/* ---------- Dock widgets ---------- */
QDockWidget {
color: ${text};
titlebar-close-icon: none;
}
QDockWidget::title {
background: ${bg_subtle};
padding: 4px;
border: 1px solid ${border};
}
/* ---------- Rubber band (multi-select drag rectangle) ---------- */
/* ---------- Rubber band (multi-select drag) ---------- */
QRubberBand {
background: ${accent};
border: 1px solid ${accent};
/* Qt blends rubber band at ~30% so this reads as a translucent
* accent-tinted rectangle without needing rgba(). */
}
/* ---------- Library count label states ---------- */
/*
* The library tab's count label switches between three visual states
* depending on what refresh() found. The state is exposed as a Qt
* dynamic property `libraryCountState` so users can override these
* rules in their custom.qss without touching the Python.
*
* normal N files default text color, no rule needed
* empty no items dim text (no items found, search miss)
* error bad/unreachable danger color + bold (real error)
*/
QLabel[libraryCountState="empty"] {
color: ${text_dim};
@ -518,18 +411,18 @@ QLabel[libraryCountState="error"] {
font-weight: bold;
}
/* ---------- Thumbnail dot indicators (Qt properties on ThumbnailWidget) ---------- */
/* ---------- Thumbnail indicators ---------- */
ThumbnailWidget {
qproperty-savedColor: #22cc22; /* green dot: saved to library — universal "confirmed" feel */
qproperty-bookmarkedColor: #ffcc00; /* yellow star: bookmarked */
qproperty-savedColor: #22cc22;
qproperty-bookmarkedColor: #ffcc00;
qproperty-selectionColor: ${accent};
qproperty-multiSelectColor: ${accent_dim};
qproperty-hoverColor: ${accent};
qproperty-idleColor: ${border_strong};
}
/* ---------- Info panel tag category colors ---------- */
/* ---------- Info panel tag colors ---------- */
InfoPanel {
qproperty-tagArtistColor: ${warning};
@ -540,19 +433,13 @@ InfoPanel {
qproperty-tagLoreColor: ${text_dim};
}
/* ---------- Video player letterbox / pillarbox color (mpv background) ---------- */
/* ---------- Video player letterbox ---------- */
VideoPlayer {
qproperty-letterboxColor: ${bg};
}
/* ---------- Popout overlay bars (slideshow toolbar + slideshow controls + embedded preview controls) ---------- */
/*
* The popout window's translucent toolbar (top) and transport controls
* (bottom) float over the video content. The bg color comes from the
* @palette overlay_bg slot. Children get the classic overlay treatment:
* transparent backgrounds, near-white text, hairline borders.
*/
/* ---------- Popout overlay bars ---------- */
QWidget#_slideshow_toolbar,
QWidget#_slideshow_controls,
@ -575,6 +462,8 @@ QWidget#_preview_controls QPushButton {
color: white;
border: 1px solid rgba(255, 255, 255, 80);
padding: 2px 6px;
font-size: 15px;
font-weight: bold;
}
QWidget#_slideshow_toolbar QPushButton:hover,
QWidget#_slideshow_controls QPushButton:hover,

View File

@ -9,26 +9,25 @@
*/
/* @palette
bg: #1a1b26
bg_alt: #16161e
bg_subtle: #24283b
bg_hover: #292e42
bg_active: #3b4261
text: #c0caf5
text_dim: #a9b1d6
text_disabled: #565f89
border: #24283b
border_strong: #292e42
accent: #7aa2f7
accent_text: #1a1b26
accent_dim: #7dcfff
link: #7dcfff
danger: #f7768e
success: #9ece6a
warning: #e0af68
overlay_bg: rgba(26, 27, 38, 200)
bg: #1a1b26
bg_alt: #16161e
bg_subtle: #24283b
bg_hover: #292e42
bg_active: #3b4261
text: #c0caf5
text_dim: #a9b1d6
text_disabled: #565f89
border: #24283b
border_strong: #292e42
accent: #7aa2f7
accent_text: #1a1b26
accent_dim: #7dcfff
link: #7dcfff
danger: #f7768e
success: #9ece6a
warning: #e0af68
overlay_bg: rgba(26, 27, 38, 200)
*/
/* ---------- Base ---------- */
QWidget {
@ -43,8 +42,6 @@ QWidget:disabled {
color: ${text_disabled};
}
/* Labels should never paint an opaque background they sit on top of
* other widgets in many places (toolbars, info panels, overlays). */
QLabel {
background: transparent;
}
@ -92,49 +89,26 @@ QPushButton:flat:hover {
background-color: ${bg_hover};
}
QToolButton {
background-color: transparent;
color: ${text};
border: 1px solid transparent;
border-radius: 4px;
padding: 4px;
}
QToolButton:hover {
background-color: ${bg_hover};
border-color: ${border_strong};
}
QToolButton:pressed, QToolButton:checked {
background-color: ${bg_active};
}
/* ---------- Inputs ---------- */
QLineEdit, QSpinBox, QDoubleSpinBox, QTextEdit, QPlainTextEdit {
QLineEdit, QSpinBox, QTextEdit {
background-color: ${bg_subtle};
color: ${text};
border: 1px solid ${border_strong};
border-radius: 4px;
padding: 2px 6px;
/* min-height ensures the painted text fits inside the widget bounds
* even when a parent layout (e.g. QFormLayout inside a QGroupBox)
* compresses the natural sizeHint. Without this, spinboxes in dense
* forms render with the top of the value text clipped. */
min-height: 16px;
selection-background-color: ${accent};
selection-color: ${accent_text};
}
QLineEdit:focus,
QSpinBox:focus,
QDoubleSpinBox:focus,
QTextEdit:focus,
QPlainTextEdit:focus {
QTextEdit:focus {
border-color: ${accent};
}
QLineEdit:disabled,
QSpinBox:disabled,
QDoubleSpinBox:disabled,
QTextEdit:disabled,
QPlainTextEdit:disabled {
QTextEdit:disabled {
background-color: ${bg_alt};
color: ${text_disabled};
border-color: ${border};
@ -315,19 +289,6 @@ QSlider::handle:horizontal:hover {
background: ${accent_dim};
}
QSlider::groove:vertical {
background: ${bg_subtle};
width: 4px;
border-radius: 2px;
}
QSlider::handle:vertical {
background: ${accent};
width: 12px;
height: 12px;
margin: 0 -5px;
border-radius: 6px;
}
/* ---------- Progress ---------- */
QProgressBar {
@ -343,33 +304,28 @@ QProgressBar::chunk {
border-radius: 3px;
}
/* ---------- Checkboxes & radio buttons ---------- */
/* ---------- Checkboxes ---------- */
QCheckBox, QRadioButton {
QCheckBox {
background: transparent;
color: ${text};
spacing: 6px;
}
QCheckBox::indicator, QRadioButton::indicator {
QCheckBox::indicator {
width: 14px;
height: 14px;
background-color: ${bg_subtle};
border: 1px solid ${border_strong};
}
QCheckBox::indicator {
border-radius: 3px;
}
QRadioButton::indicator {
border-radius: 7px;
}
QCheckBox::indicator:hover, QRadioButton::indicator:hover {
QCheckBox::indicator:hover {
border-color: ${accent};
}
QCheckBox::indicator:checked, QRadioButton::indicator:checked {
QCheckBox::indicator:checked {
background-color: ${accent};
border-color: ${accent};
}
QCheckBox::indicator:disabled, QRadioButton::indicator:disabled {
QCheckBox::indicator:disabled {
background-color: ${bg_alt};
border-color: ${border};
}
@ -384,9 +340,9 @@ QToolTip {
border-radius: 3px;
}
/* ---------- Item views (lists, trees, tables) ---------- */
/* ---------- Lists ---------- */
QListView, QListWidget, QTreeView, QTreeWidget, QTableView, QTableWidget {
QListView, QListWidget {
background-color: ${bg};
alternate-background-color: ${bg_alt};
color: ${text};
@ -395,35 +351,18 @@ QListView, QListWidget, QTreeView, QTreeWidget, QTableView, QTableWidget {
selection-color: ${accent_text};
outline: none;
}
QListView::item, QListWidget::item,
QTreeView::item, QTreeWidget::item,
QTableView::item, QTableWidget::item {
QListView::item, QListWidget::item {
padding: 4px;
}
QListView::item:hover, QListWidget::item:hover,
QTreeView::item:hover, QTreeWidget::item:hover,
QTableView::item:hover, QTableWidget::item:hover {
QListView::item:hover, QListWidget::item:hover {
background-color: ${bg_hover};
}
QListView::item:selected, QListWidget::item:selected,
QTreeView::item:selected, QTreeWidget::item:selected,
QTableView::item:selected, QTableWidget::item:selected {
QListView::item:selected, QListWidget::item:selected {
background-color: ${accent};
color: ${accent_text};
}
QHeaderView::section {
background-color: ${bg_subtle};
color: ${text};
border: none;
border-right: 1px solid ${border};
padding: 4px 8px;
}
QHeaderView::section:hover {
background-color: ${bg_hover};
}
/* ---------- Tabs ---------- */
/* ---------- Tabs (settings dialog) ---------- */
QTabWidget::pane {
border: 1px solid ${border};
@ -448,7 +387,7 @@ QTabBar::tab:hover:!selected {
color: ${text};
}
/* ---------- Group boxes ---------- */
/* ---------- Group boxes (settings dialog) ---------- */
QGroupBox {
background: transparent;
@ -465,63 +404,14 @@ QGroupBox::title {
color: ${text_dim};
}
/* ---------- Frames ---------- */
QFrame[frameShape="4"], /* HLine */
QFrame[frameShape="5"] /* VLine */ {
background: ${border};
color: ${border};
}
/* ---------- Toolbars ---------- */
QToolBar {
background: ${bg};
border: none;
spacing: 4px;
padding: 2px;
}
QToolBar::separator {
background: ${border};
width: 1px;
margin: 4px 4px;
}
/* ---------- Dock widgets ---------- */
QDockWidget {
color: ${text};
titlebar-close-icon: none;
}
QDockWidget::title {
background: ${bg_subtle};
padding: 4px;
border: 1px solid ${border};
}
/* ---------- Rubber band (multi-select drag rectangle) ---------- */
/* ---------- Rubber band (multi-select drag) ---------- */
QRubberBand {
background: ${accent};
border: 1px solid ${accent};
/* Qt blends rubber band at ~30% so this reads as a translucent
* accent-tinted rectangle without needing rgba(). */
}
/* ---------- Library count label states ---------- */
/*
* The library tab's count label switches between three visual states
* depending on what refresh() found. The state is exposed as a Qt
* dynamic property `libraryCountState` so users can override these
* rules in their custom.qss without touching the Python.
*
* normal N files default text color, no rule needed
* empty no items dim text (no items found, search miss)
* error bad/unreachable danger color + bold (real error)
*/
QLabel[libraryCountState="empty"] {
color: ${text_dim};
@ -531,18 +421,18 @@ QLabel[libraryCountState="error"] {
font-weight: bold;
}
/* ---------- Thumbnail dot indicators (Qt properties on ThumbnailWidget) ---------- */
/* ---------- Thumbnail indicators ---------- */
ThumbnailWidget {
qproperty-savedColor: #22cc22; /* green dot: saved to library — universal "confirmed" feel */
qproperty-bookmarkedColor: #ffcc00; /* yellow star: bookmarked */
qproperty-savedColor: #22cc22;
qproperty-bookmarkedColor: #ffcc00;
qproperty-selectionColor: ${accent};
qproperty-multiSelectColor: ${accent_dim};
qproperty-hoverColor: ${accent};
qproperty-idleColor: ${border_strong};
}
/* ---------- Info panel tag category colors ---------- */
/* ---------- Info panel tag colors ---------- */
InfoPanel {
qproperty-tagArtistColor: ${warning};
@ -553,19 +443,13 @@ InfoPanel {
qproperty-tagLoreColor: ${text_dim};
}
/* ---------- Video player letterbox / pillarbox color (mpv background) ---------- */
/* ---------- Video player letterbox ---------- */
VideoPlayer {
qproperty-letterboxColor: ${bg};
}
/* ---------- Popout overlay bars (slideshow toolbar + slideshow controls + embedded preview controls) ---------- */
/*
* The popout window's translucent toolbar (top) and transport controls
* (bottom) float over the video content. The bg color comes from the
* @palette overlay_bg slot. Children get the classic overlay treatment:
* transparent backgrounds, near-white text, hairline borders.
*/
/* ---------- Popout overlay bars ---------- */
QWidget#_slideshow_toolbar,
QWidget#_slideshow_controls,
@ -588,6 +472,8 @@ QWidget#_preview_controls QPushButton {
color: white;
border: 1px solid rgba(255, 255, 255, 80);
padding: 2px 6px;
font-size: 15px;
font-weight: bold;
}
QWidget#_slideshow_toolbar QPushButton:hover,
QWidget#_slideshow_controls QPushButton:hover,

View File

@ -9,26 +9,25 @@
*/
/* @palette
bg: #1a1b26
bg_alt: #16161e
bg_subtle: #24283b
bg_hover: #292e42
bg_active: #3b4261
text: #c0caf5
text_dim: #a9b1d6
text_disabled: #565f89
border: #24283b
border_strong: #292e42
accent: #7aa2f7
accent_text: #1a1b26
accent_dim: #7dcfff
link: #7dcfff
danger: #f7768e
success: #9ece6a
warning: #e0af68
overlay_bg: rgba(26, 27, 38, 200)
bg: #1a1b26
bg_alt: #16161e
bg_subtle: #24283b
bg_hover: #292e42
bg_active: #3b4261
text: #c0caf5
text_dim: #a9b1d6
text_disabled: #565f89
border: #24283b
border_strong: #292e42
accent: #7aa2f7
accent_text: #1a1b26
accent_dim: #7dcfff
link: #7dcfff
danger: #f7768e
success: #9ece6a
warning: #e0af68
overlay_bg: rgba(26, 27, 38, 200)
*/
/* ---------- Base ---------- */
QWidget {
@ -43,8 +42,6 @@ QWidget:disabled {
color: ${text_disabled};
}
/* Labels should never paint an opaque background they sit on top of
* other widgets in many places (toolbars, info panels, overlays). */
QLabel {
background: transparent;
}
@ -91,47 +88,25 @@ QPushButton:flat:hover {
background-color: ${bg_hover};
}
QToolButton {
background-color: transparent;
color: ${text};
border: 1px solid transparent;
padding: 4px;
}
QToolButton:hover {
background-color: ${bg_hover};
border-color: ${border_strong};
}
QToolButton:pressed, QToolButton:checked {
background-color: ${bg_active};
}
/* ---------- Inputs ---------- */
QLineEdit, QSpinBox, QDoubleSpinBox, QTextEdit, QPlainTextEdit {
QLineEdit, QSpinBox, QTextEdit {
background-color: ${bg_subtle};
color: ${text};
border: 1px solid ${border_strong};
padding: 2px 6px;
/* min-height ensures the painted text fits inside the widget bounds
* even when a parent layout (e.g. QFormLayout inside a QGroupBox)
* compresses the natural sizeHint. Without this, spinboxes in dense
* forms render with the top of the value text clipped. */
min-height: 16px;
selection-background-color: ${accent};
selection-color: ${accent_text};
}
QLineEdit:focus,
QSpinBox:focus,
QDoubleSpinBox:focus,
QTextEdit:focus,
QPlainTextEdit:focus {
QTextEdit:focus {
border-color: ${accent};
}
QLineEdit:disabled,
QSpinBox:disabled,
QDoubleSpinBox:disabled,
QTextEdit:disabled,
QPlainTextEdit:disabled {
QTextEdit:disabled {
background-color: ${bg_alt};
color: ${text_disabled};
border-color: ${border};
@ -309,17 +284,6 @@ QSlider::handle:horizontal:hover {
background: ${accent_dim};
}
QSlider::groove:vertical {
background: ${bg_subtle};
width: 4px;
}
QSlider::handle:vertical {
background: ${accent};
width: 12px;
height: 12px;
margin: 0 -5px;
}
/* ---------- Progress ---------- */
QProgressBar {
@ -333,32 +297,27 @@ QProgressBar::chunk {
background-color: ${accent};
}
/* ---------- Checkboxes & radio buttons ---------- */
/* ---------- Checkboxes ---------- */
QCheckBox, QRadioButton {
QCheckBox {
background: transparent;
color: ${text};
spacing: 6px;
}
QCheckBox::indicator, QRadioButton::indicator {
QCheckBox::indicator {
width: 14px;
height: 14px;
background-color: ${bg_subtle};
border: 1px solid ${border_strong};
}
QCheckBox::indicator {
}
QRadioButton::indicator {
border-radius: 7px;
}
QCheckBox::indicator:hover, QRadioButton::indicator:hover {
QCheckBox::indicator:hover {
border-color: ${accent};
}
QCheckBox::indicator:checked, QRadioButton::indicator:checked {
QCheckBox::indicator:checked {
background-color: ${accent};
border-color: ${accent};
}
QCheckBox::indicator:disabled, QRadioButton::indicator:disabled {
QCheckBox::indicator:disabled {
background-color: ${bg_alt};
border-color: ${border};
}
@ -372,9 +331,9 @@ QToolTip {
padding: 4px 6px;
}
/* ---------- Item views (lists, trees, tables) ---------- */
/* ---------- Lists ---------- */
QListView, QListWidget, QTreeView, QTreeWidget, QTableView, QTableWidget {
QListView, QListWidget {
background-color: ${bg};
alternate-background-color: ${bg_alt};
color: ${text};
@ -383,35 +342,18 @@ QListView, QListWidget, QTreeView, QTreeWidget, QTableView, QTableWidget {
selection-color: ${accent_text};
outline: none;
}
QListView::item, QListWidget::item,
QTreeView::item, QTreeWidget::item,
QTableView::item, QTableWidget::item {
QListView::item, QListWidget::item {
padding: 4px;
}
QListView::item:hover, QListWidget::item:hover,
QTreeView::item:hover, QTreeWidget::item:hover,
QTableView::item:hover, QTableWidget::item:hover {
QListView::item:hover, QListWidget::item:hover {
background-color: ${bg_hover};
}
QListView::item:selected, QListWidget::item:selected,
QTreeView::item:selected, QTreeWidget::item:selected,
QTableView::item:selected, QTableWidget::item:selected {
QListView::item:selected, QListWidget::item:selected {
background-color: ${accent};
color: ${accent_text};
}
QHeaderView::section {
background-color: ${bg_subtle};
color: ${text};
border: none;
border-right: 1px solid ${border};
padding: 4px 8px;
}
QHeaderView::section:hover {
background-color: ${bg_hover};
}
/* ---------- Tabs ---------- */
/* ---------- Tabs (settings dialog) ---------- */
QTabWidget::pane {
border: 1px solid ${border};
@ -436,7 +378,7 @@ QTabBar::tab:hover:!selected {
color: ${text};
}
/* ---------- Group boxes ---------- */
/* ---------- Group boxes (settings dialog) ---------- */
QGroupBox {
background: transparent;
@ -452,63 +394,14 @@ QGroupBox::title {
color: ${text_dim};
}
/* ---------- Frames ---------- */
QFrame[frameShape="4"], /* HLine */
QFrame[frameShape="5"] /* VLine */ {
background: ${border};
color: ${border};
}
/* ---------- Toolbars ---------- */
QToolBar {
background: ${bg};
border: none;
spacing: 4px;
padding: 2px;
}
QToolBar::separator {
background: ${border};
width: 1px;
margin: 4px 4px;
}
/* ---------- Dock widgets ---------- */
QDockWidget {
color: ${text};
titlebar-close-icon: none;
}
QDockWidget::title {
background: ${bg_subtle};
padding: 4px;
border: 1px solid ${border};
}
/* ---------- Rubber band (multi-select drag rectangle) ---------- */
/* ---------- Rubber band (multi-select drag) ---------- */
QRubberBand {
background: ${accent};
border: 1px solid ${accent};
/* Qt blends rubber band at ~30% so this reads as a translucent
* accent-tinted rectangle without needing rgba(). */
}
/* ---------- Library count label states ---------- */
/*
* The library tab's count label switches between three visual states
* depending on what refresh() found. The state is exposed as a Qt
* dynamic property `libraryCountState` so users can override these
* rules in their custom.qss without touching the Python.
*
* normal N files default text color, no rule needed
* empty no items dim text (no items found, search miss)
* error bad/unreachable danger color + bold (real error)
*/
QLabel[libraryCountState="empty"] {
color: ${text_dim};
@ -518,18 +411,18 @@ QLabel[libraryCountState="error"] {
font-weight: bold;
}
/* ---------- Thumbnail dot indicators (Qt properties on ThumbnailWidget) ---------- */
/* ---------- Thumbnail indicators ---------- */
ThumbnailWidget {
qproperty-savedColor: #22cc22; /* green dot: saved to library — universal "confirmed" feel */
qproperty-bookmarkedColor: #ffcc00; /* yellow star: bookmarked */
qproperty-savedColor: #22cc22;
qproperty-bookmarkedColor: #ffcc00;
qproperty-selectionColor: ${accent};
qproperty-multiSelectColor: ${accent_dim};
qproperty-hoverColor: ${accent};
qproperty-idleColor: ${border_strong};
}
/* ---------- Info panel tag category colors ---------- */
/* ---------- Info panel tag colors ---------- */
InfoPanel {
qproperty-tagArtistColor: ${warning};
@ -540,19 +433,13 @@ InfoPanel {
qproperty-tagLoreColor: ${text_dim};
}
/* ---------- Video player letterbox / pillarbox color (mpv background) ---------- */
/* ---------- Video player letterbox ---------- */
VideoPlayer {
qproperty-letterboxColor: ${bg};
}
/* ---------- Popout overlay bars (slideshow toolbar + slideshow controls + embedded preview controls) ---------- */
/*
* The popout window's translucent toolbar (top) and transport controls
* (bottom) float over the video content. The bg color comes from the
* @palette overlay_bg slot. Children get the classic overlay treatment:
* transparent backgrounds, near-white text, hairline borders.
*/
/* ---------- Popout overlay bars ---------- */
QWidget#_slideshow_toolbar,
QWidget#_slideshow_controls,
@ -575,6 +462,8 @@ QWidget#_preview_controls QPushButton {
color: white;
border: 1px solid rgba(255, 255, 255, 80);
padding: 2px 6px;
font-size: 15px;
font-weight: bold;
}
QWidget#_slideshow_toolbar QPushButton:hover,
QWidget#_slideshow_controls QPushButton:hover,