booru-viewer/themes/everforest-square.qss
pax baa910ac81 Popout: fix first-fit aspect lock race, fill images to window, tighten combo/button padding across all themes
Three fixes that all surfaced from the bookmark/library decoupling
shake-out:

  - Popout first-image aspect-lock race: _fit_to_content used to call
    _is_hypr_floating which returned None for both "not Hyprland" and
    "Hyprland but the window isn't visible to hyprctl yet". The latter
    happens on the very first popout open because the wm:openWindow
    event hasn't been processed when set_media fires. The method then
    fell through to a plain Qt resize and skipped the
    keep_aspect_ratio setprop, so the first image always opened
    unlocked and only subsequent navigations got the right shape. Now
    we inline the env-var check, distinguish the two None cases, and
    retry on Hyprland with a 40ms backoff (capped at 5 attempts /
    200ms total) when the window isn't registered yet.

  - Image fill in popout (and embedded preview): ImageViewer._fit_to_view
    used min(scale_w, scale_h, 1.0) which clamped the zoom at native
    pixel size, so a smaller image in a larger window centered with
    letterbox space around it. Dropped the 1.0 cap so images scale up
    to fill the available view, matching how the video player fills
    its widget. Combined with the popout's keep_aspect_ratio, the
    window matches the image's aspect AND the image fills it cleanly.
    Tiled popouts with mismatched aspect still letterbox (intentional —
    the layout owns the window shape).

  - Combo + button padding tightening across all 12 bundled themes
    and Library sort combo: QPushButton padding 2px 8px → 2px 6px,
    QComboBox padding 2px 6px → 2px 4px, QComboBox::drop-down width
    18px → 14px. Saves 8px non-text width per combo and 4px per
    button, so the new "Post ID" sort entry fits in 75px instead of
    needing 90. Library sort combo bumped from "Name" (lexicographic)
    to "Post ID" with a numeric stem sort that handles non-digit
    stems gracefully.
2026-04-07 20:48:09 -05:00

610 lines
14 KiB
CSS

/* booru-viewer — Everforest Dark
*
* Edit the @palette block below to recolor this square variant. The body uses
* ${...} placeholders that the app's _load_user_qss preprocessor
* substitutes at load time. See themes/README.md for the full list of
* placeholder names and what each one is used for.
*
* The same dialect works in any custom.qss you put in your data dir.
*/
/* @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)
*/
/* ---------- Base ---------- */
QWidget {
background-color: ${bg};
color: ${text};
font-size: 13px;
selection-background-color: ${accent};
selection-color: ${accent_text};
}
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;
}
QMainWindow, QDialog {
background-color: ${bg};
}
/* ---------- Buttons ---------- */
QPushButton {
background-color: ${bg_subtle};
color: ${text};
border: 1px solid ${border_strong};
padding: 2px 6px;
min-height: 17px;
}
QPushButton:hover {
background-color: ${bg_hover};
border-color: ${accent};
}
QPushButton:pressed {
background-color: ${bg_active};
}
QPushButton:checked {
background-color: ${accent};
color: ${accent_text};
border-color: ${accent};
}
QPushButton:checked:hover {
background-color: ${accent_dim};
border-color: ${accent_dim};
}
QPushButton:disabled {
background-color: ${bg_alt};
color: ${text_disabled};
border-color: ${border};
}
QPushButton:flat {
background: transparent;
border: none;
}
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 {
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 {
border-color: ${accent};
}
QLineEdit:disabled,
QSpinBox:disabled,
QDoubleSpinBox:disabled,
QTextEdit:disabled,
QPlainTextEdit:disabled {
background-color: ${bg_alt};
color: ${text_disabled};
border-color: ${border};
}
QComboBox {
background-color: ${bg_subtle};
color: ${text};
border: 1px solid ${border_strong};
padding: 2px 4px;
min-height: 16px;
}
QComboBox:hover {
border-color: ${accent};
}
QComboBox:focus {
border-color: ${accent};
}
QComboBox::drop-down {
border: none;
width: 14px;
}
QComboBox QAbstractItemView {
background-color: ${bg_subtle};
color: ${text};
border: 1px solid ${border_strong};
selection-background-color: ${accent};
selection-color: ${accent_text};
outline: none;
padding: 2px;
}
/* ---------- Scrollbars ---------- */
QScrollBar:vertical {
background: ${bg};
width: 10px;
border: none;
margin: 0;
}
QScrollBar::handle:vertical {
background: ${bg_hover};
min-height: 24px;
margin: 1px;
}
QScrollBar::handle:vertical:hover {
background: ${bg_active};
}
QScrollBar::add-line:vertical,
QScrollBar::sub-line:vertical {
height: 0;
border: none;
}
QScrollBar::add-page:vertical,
QScrollBar::sub-page:vertical {
background: transparent;
}
QScrollBar:horizontal {
background: ${bg};
height: 10px;
border: none;
margin: 0;
}
QScrollBar::handle:horizontal {
background: ${bg_hover};
min-width: 24px;
margin: 1px;
}
QScrollBar::handle:horizontal:hover {
background: ${bg_active};
}
QScrollBar::add-line:horizontal,
QScrollBar::sub-line:horizontal {
width: 0;
border: none;
}
QScrollBar::add-page:horizontal,
QScrollBar::sub-page:horizontal {
background: transparent;
}
QScrollArea {
background: transparent;
border: none;
}
/* ---------- Menus ---------- */
QMenuBar {
background-color: ${bg};
color: ${text};
border-bottom: 1px solid ${border};
}
QMenuBar::item {
background: transparent;
padding: 4px 10px;
}
QMenuBar::item:selected {
background-color: ${bg_hover};
color: ${text};
}
QMenuBar::item:pressed {
background-color: ${bg_active};
}
QMenu {
background-color: ${bg_subtle};
color: ${text};
border: 1px solid ${border_strong};
padding: 4px 0;
}
QMenu::item {
background: transparent;
padding: 5px 24px 5px 24px;
}
QMenu::item:selected {
background-color: ${accent};
color: ${accent_text};
}
QMenu::item:disabled {
color: ${text_disabled};
}
QMenu::separator {
height: 1px;
background: ${border};
margin: 4px 8px;
}
QMenu::icon {
padding-left: 6px;
}
/* ---------- Status bar ---------- */
QStatusBar {
background-color: ${bg};
color: ${text_dim};
border-top: 1px solid ${border};
}
QStatusBar::item {
border: none;
}
/* ---------- Splitters ---------- */
QSplitter::handle {
background: ${border};
}
QSplitter::handle:horizontal {
width: 2px;
}
QSplitter::handle:vertical {
height: 2px;
}
QSplitter::handle:hover {
background: ${accent};
}
/* ---------- Sliders ---------- */
QSlider::groove:horizontal {
background: ${bg_subtle};
height: 4px;
}
QSlider::sub-page:horizontal {
background: ${accent};
}
QSlider::handle:horizontal {
background: ${accent};
width: 12px;
height: 12px;
margin: -5px 0;
}
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 {
background-color: ${bg_subtle};
color: ${text};
border: 1px solid ${border};
text-align: center;
height: 6px;
}
QProgressBar::chunk {
background-color: ${accent};
}
/* ---------- Checkboxes & radio buttons ---------- */
QCheckBox, QRadioButton {
background: transparent;
color: ${text};
spacing: 6px;
}
QCheckBox::indicator, QRadioButton::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 {
border-color: ${accent};
}
QCheckBox::indicator:checked, QRadioButton::indicator:checked {
background-color: ${accent};
border-color: ${accent};
}
QCheckBox::indicator:disabled, QRadioButton::indicator:disabled {
background-color: ${bg_alt};
border-color: ${border};
}
/* ---------- Tooltips ---------- */
QToolTip {
background-color: ${bg_subtle};
color: ${text};
border: 1px solid ${border_strong};
padding: 4px 6px;
}
/* ---------- Item views (lists, trees, tables) ---------- */
QListView, QListWidget, QTreeView, QTreeWidget, QTableView, QTableWidget {
background-color: ${bg};
alternate-background-color: ${bg_alt};
color: ${text};
border: 1px solid ${border};
selection-background-color: ${accent};
selection-color: ${accent_text};
outline: none;
}
QListView::item, QListWidget::item,
QTreeView::item, QTreeWidget::item,
QTableView::item, QTableWidget::item {
padding: 4px;
}
QListView::item:hover, QListWidget::item:hover,
QTreeView::item:hover, QTreeWidget::item:hover,
QTableView::item:hover, QTableWidget::item:hover {
background-color: ${bg_hover};
}
QListView::item:selected, QListWidget::item:selected,
QTreeView::item:selected, QTreeWidget::item:selected,
QTableView::item:selected, QTableWidget::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 ---------- */
QTabWidget::pane {
border: 1px solid ${border};
top: -1px;
}
QTabBar::tab {
background: ${bg_subtle};
color: ${text_dim};
border: 1px solid ${border};
border-bottom: none;
padding: 6px 14px;
border-top-left-radius: 4px;
border-top-right-radius: 4px;
}
QTabBar::tab:selected {
background: ${bg};
color: ${text};
border-color: ${border_strong};
}
QTabBar::tab:hover:!selected {
background: ${bg_hover};
color: ${text};
}
/* ---------- Group boxes ---------- */
QGroupBox {
background: transparent;
color: ${text};
border: 1px solid ${border};
margin-top: 10px;
padding-top: 8px;
}
QGroupBox::title {
subcontrol-origin: margin;
subcontrol-position: top left;
padding: 0 6px;
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) ---------- */
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};
}
QLabel[libraryCountState="error"] {
color: ${danger};
font-weight: bold;
}
/* ---------- Thumbnail dot indicators (Qt properties on ThumbnailWidget) ---------- */
ThumbnailWidget {
qproperty-savedColor: #22cc22; /* green dot: saved to library — universal "confirmed" feel */
qproperty-bookmarkedColor: #ffcc00; /* yellow star: bookmarked */
qproperty-selectionColor: ${accent};
qproperty-multiSelectColor: ${accent_dim};
qproperty-hoverColor: ${accent};
qproperty-idleColor: ${border_strong};
}
/* ---------- Info panel tag category colors ---------- */
InfoPanel {
qproperty-tagArtistColor: ${warning};
qproperty-tagCharacterColor: ${success};
qproperty-tagCopyrightColor: ${accent};
qproperty-tagSpeciesColor: ${danger};
qproperty-tagMetaColor: ${text_dim};
qproperty-tagLoreColor: ${text_dim};
}
/* ---------- Video player letterbox / pillarbox color (mpv background) ---------- */
VideoPlayer {
qproperty-letterboxColor: ${bg};
}
/* ---------- Popout overlay bars (slideshow toolbar + slideshow controls + embedded preview controls) ---------- */
/*
* The popout window's translucent toolbar (top) and transport controls
* (bottom) float over the video content. The bg color comes from the
* @palette overlay_bg slot. Children get the classic overlay treatment:
* transparent backgrounds, near-white text, hairline borders.
*/
QWidget#_slideshow_toolbar,
QWidget#_slideshow_controls,
QWidget#_preview_controls {
background: ${overlay_bg};
}
QWidget#_slideshow_toolbar *,
QWidget#_slideshow_controls *,
QWidget#_preview_controls * {
background: transparent;
color: white;
border: none;
}
QWidget#_slideshow_toolbar QPushButton,
QWidget#_slideshow_controls QPushButton,
QWidget#_preview_controls QPushButton {
background: transparent;
color: white;
border: 1px solid rgba(255, 255, 255, 80);
padding: 2px 6px;
}
QWidget#_slideshow_toolbar QPushButton:hover,
QWidget#_slideshow_controls QPushButton:hover,
QWidget#_preview_controls QPushButton:hover {
background: rgba(255, 255, 255, 30);
}
QWidget#_slideshow_toolbar QSlider::groove:horizontal,
QWidget#_slideshow_controls QSlider::groove:horizontal,
QWidget#_preview_controls QSlider::groove:horizontal {
background: rgba(255, 255, 255, 40);
height: 4px;
}
QWidget#_slideshow_toolbar QSlider::handle:horizontal,
QWidget#_slideshow_controls QSlider::handle:horizontal,
QWidget#_preview_controls QSlider::handle:horizontal {
background: ${accent};
width: 10px;
margin: -4px 0;
}
QWidget#_slideshow_toolbar QSlider::sub-page:horizontal,
QWidget#_slideshow_controls QSlider::sub-page:horizontal,
QWidget#_preview_controls QSlider::sub-page:horizontal {
background: ${accent};
}
QWidget#_slideshow_toolbar QLabel,
QWidget#_slideshow_controls QLabel,
QWidget#_preview_controls QLabel {
background: transparent;
color: white;
}