SELECTION_BG/TEXT pin the app-wide selection blue to the banner color (was Qt's undeclared palette default); FRAME_L1/L2/L3 knobs put window, panel, and data-view borders on three tunable levels (panel etching off by default); QTabBar tabs and filter chips go borderless with a hover underline; QSlider matches the scrollbar look; scrollbar tracks turn square with a track-colored corner; SAMPLE_STATUS_* and SAMPLE_ROW_ALT_BG drive the sample-table status tints. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1228 lines
37 KiB
Python
1228 lines
37 KiB
Python
from __future__ import annotations
|
|
|
|
from string import Template
|
|
|
|
THEME_ORIGINAL = "original"
|
|
THEME_PORTRAIT = "portrait"
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Color palette. Change values HERE to try a different look — the QSS below
|
|
# and the widgets that import these use them everywhere. Every UPPERCASE
|
|
# constant is exposed to the QSS templates as $lowercase_name (see _palette),
|
|
# so no color literals live inside the stylesheets themselves.
|
|
# Any value can be "transparent" (e.g. to hide a border or a fill).
|
|
# string.Template ($name) instead of f-strings so the QSS braces stay literal.
|
|
|
|
# -- Light theme ------------------------------------------------------------
|
|
BACKGROUND = "#e2e7ee"
|
|
BANNER = "#a2b7e4"
|
|
BANNER_TEXT = "#F8F8FC" # must contrast with BANNER
|
|
BANNER_TEXT_SHADOW = "#000000" # soft shadow under banner titles; alpha in TitleLabel
|
|
TEXT = "#263043"
|
|
SURFACE = "#f2f2f2" # input fields, cards, group boxes
|
|
|
|
# Borders (all can be "transparent" to hide the line):
|
|
BORDER = "transparent" # main dividers, e.g. the beamline state bar top line
|
|
CARD_BORDER = "transparent" # cards / group boxes (Local Contact, automation)
|
|
COMPACT_BORDER = "transparent" # compact-automation cards and buttons
|
|
|
|
# -- Box-frame borders by nesting level -------------------------------------
|
|
# One knob per level so the amount of "boxing" is tunable in one place:
|
|
# L1: top-level pop-out window edge (painted by PopoutWindow)
|
|
# L2: panel frames inside a dock/tab (TELL sample changer, reference
|
|
# tools, sample queue) — transparent by default: banner + table
|
|
# already delimit the panel, extra boxes read as clutter
|
|
# L3: data views (tables, log text) — a light line so scrollable content
|
|
# keeps a visible edge
|
|
# (The tab-widget pane is a fourth box, already governed by BORDER above.)
|
|
FRAME_L1_WIDTH = "0px" # default off — the pop-out reads as one clean surface
|
|
FRAME_L1_COLOR = "#bdbdbd"
|
|
FRAME_L2_WIDTH = "0px"
|
|
FRAME_L2_COLOR = "transparent"
|
|
FRAME_L3_WIDTH = "1px"
|
|
FRAME_L3_COLOR = "#c9cfd8"
|
|
|
|
# Left inset (px, int — used in code, not QSS) for bottom-dock content so its
|
|
# left edge lines up with the left-column panels above (Loop centering).
|
|
DOCK_CONTENT_LEFT_PAD = 10
|
|
|
|
# Tab face fill: the selected Dewar/Auxiliary tab AND the unchecked
|
|
# All/Queued/Flagged/Measured filter buttons share this background.
|
|
TAB_FACE_BG = "#ffffff"
|
|
|
|
# Selection highlight (combo popups, menus, list rows, text selections).
|
|
# Qt's style palette used to supply its own blue here; now it is the banner
|
|
# blue, and THIS is the knob for it. (Table-row selection has its own paler
|
|
# knob: SAMPLE_STATUS_SELECTED_BG.)
|
|
SELECTION_BG = BANNER
|
|
SELECTION_TEXT = "#263043" # same as TEXT — readable on the banner blue
|
|
|
|
# Compact-automation page:
|
|
COMPACT_CARD_BG = "#e6eefc"
|
|
COMPACT_TITLE = "#17324d" # section titles + menu/secondary button text
|
|
COMPACT_HINT = "#51657d" # secondary text: hints, queue titles
|
|
COMPACT_VALUE = "#10263a"
|
|
COMPACT_MENU_BG = "#cbdcf8"
|
|
COMPACT_MENU_BG_HOVER = "#bfd4f6"
|
|
PRIMARY = "#2563eb" # main action button
|
|
PRIMARY_HOVER = "#1d4ed8"
|
|
PRIMARY_TEXT = "white"
|
|
SECONDARY_BG = "#dfe9fb"
|
|
SECONDARY_BG_HOVER = "#d3e1f8"
|
|
|
|
# Alert banners (alertKind: error / success / waiting=warning):
|
|
ERROR_BG = "#fbe4e6"
|
|
ERROR_BORDER = "#d97a84"
|
|
ERROR_TEXT = "#8f1d2c"
|
|
SUCCESS_BG = "#e7f6ea"
|
|
SUCCESS_BORDER = "#7bbf8e"
|
|
SUCCESS_TEXT = "#1f6a3a"
|
|
WARNING_BG = "#fff8e1"
|
|
WARNING_BORDER = "#ffb300"
|
|
WARNING_TEXT = "#e65100"
|
|
|
|
# Axis video status + beamline state bar:
|
|
STATUS_IDLE_BG = "#d9e2f2"
|
|
STATUS_LABEL_TEXT = "#2f3b52"
|
|
STATE_TOGGLE_TEXT = "white" # collapse glyph sitting on the BANNER strip
|
|
STATE_CURRENT_TEXT = "#1e293b" # same slate as HEADING_TEXT, separate knob
|
|
STATE_TELL_TEXT = "#374357" # also the status-bar TELL line
|
|
|
|
# -- Portrait (dark) theme: Catppuccin Macchiato ----------------------------
|
|
# https://catppuccin.com/palette — token names in comments. DARK_BG was set to
|
|
# Macchiato mantle by hand, so the rest follows that flavor.
|
|
DARK_BG = "#1e2030" # mantle
|
|
DARK_TEXT = "#cad3f5" # text
|
|
DARK_SURFACE = "#24273a" # base — cards, state panel, scrollbar track
|
|
DARK_ELEVATED = "#363a4f" # surface0 — buttons, title strip, idle status pill
|
|
DARK_BORDER = "#494d64" # surface1 — card borders, scrollbar handle, button hover fill
|
|
DARK_ACCENT = "#8bd5ca" # teal
|
|
DARK_ACCENT_HOVER = "#a2ddd5" # teal +20% white — palette has no lighter teal step
|
|
DARK_MUTED = "#a5adcb" # subtext0 — secondary text
|
|
# Alert banners: full-strength color for border/text, 25%-over-DARK_BG tint
|
|
# for bg (Catppuccin defines no alert backgrounds, so these are blends).
|
|
DARK_ERROR_BG = "#523a4a" # red 25% over mantle
|
|
DARK_ERROR_BORDER = "#ed8796" # red
|
|
DARK_ERROR_TEXT = "#ed8796" # red
|
|
DARK_SUCCESS_BG = "#404e49" # green 25% over mantle
|
|
DARK_SUCCESS_BORDER = "#a6da95" # green
|
|
DARK_SUCCESS_TEXT = "#a6da95" # green
|
|
DARK_WARNING_BG = "#524d4c" # yellow 25% over mantle
|
|
DARK_WARNING_BORDER = "#eed49f" # yellow
|
|
DARK_WARNING_TEXT = "#eed49f" # yellow
|
|
|
|
# -- Shared chrome (light-theme widgets) ------------------------------------
|
|
# Extracted from per-widget literals so the whole app is themeable from this
|
|
# file. The same hex may appear under two names when the roles differ —
|
|
# separate knobs on purpose.
|
|
WHITE = "#ffffff"
|
|
DEFAULT_TEXT = "#000000" # labels that reset to plain black
|
|
NOTE_TEXT = "#555555" # tutorial hints, TELL sample details
|
|
DIM_TEXT = "#666666" # baton dialog timers
|
|
HINT_TEXT = "#999999" # baton dialog fine print
|
|
HEADING_TEXT = "#1e293b" # card headings (slate-800)
|
|
SUBTLE_TEXT = "#334155" # card body text (slate-700)
|
|
MUTED_TEXT = "#475569" # neutral chip / idle step text (slate-600)
|
|
FAINT_TEXT = "#64748b" # pending/skipped step text (slate-500)
|
|
SHADOW = "#000000" # drop shadows & tutorial scrim; alpha stays at call site
|
|
|
|
# -- Semantic action colors -------------------------------------------------
|
|
GO_TEXT = "#4e9a06" # green start/run/measure button text
|
|
ABORT_TEXT = "#a40000" # abort button text
|
|
ALERT_TEXT = "#ff0000" # out-of-range motor labels
|
|
PATH_WARN_TEXT = "#c80000" # file-exists warning in path panel
|
|
DANGER_ACCENT = "#d9534f" # invalid p-group border + message text
|
|
|
|
# -- Status chips (local contact status) — "good" reuses SUCCESS_BG/TEXT ----
|
|
CHIP_WARN_BG = "#fff3cd"
|
|
CHIP_WARN_TEXT = "#7a4b00"
|
|
CHIP_BAD_BG = "#fdeaea"
|
|
CHIP_BAD_TEXT = "#8b1e1e"
|
|
CHIP_NEUTRAL_BG = "#e9eef5" # text uses MUTED_TEXT
|
|
CHIP_INFO_BG = "#e8f1ff"
|
|
CHIP_INFO_TEXT = "#12406a"
|
|
|
|
# -- Status cards (beamline recovery, local contact error frame) ------------
|
|
# TODO: recovery-card colors are inherited from the old ad-hoc design and
|
|
# stand out against the app palette — retheme them here when ready.
|
|
WARN_CARD_BORDER = "#f0c36d"
|
|
BAD_CARD_BORDER = "#e6a8a8"
|
|
INFO_CARD_BG = "#eef6ff"
|
|
INFO_CARD_BORDER = "#a8c7e6"
|
|
PENDING_CARD_BG = "#fff7db"
|
|
PENDING_CARD_BORDER = "#e7cb73"
|
|
|
|
# -- Log panel --------------------------------------------------------------
|
|
# TODO: console-log notification colors are inherited from the old ad-hoc
|
|
# design and stand out against the app palette — retheme them here when ready.
|
|
LOG_BORDER = "#8a8a8a"
|
|
LOG_PANEL_BG = "#fff4f4"
|
|
LOG_ERROR_BG = "#fff1f1"
|
|
LOG_ERROR_BORDER = "#dd6666"
|
|
LOG_WARN_BG = "#fff8e8"
|
|
LOG_WARN_BORDER = "#d7aa42"
|
|
LOG_SUCCESS_BG = "#eefaf0"
|
|
LOG_SUCCESS_BORDER = "#6cb37a"
|
|
LOG_INFO_BG = "#eef5ff"
|
|
LOG_INFO_BORDER = "#6b9bd6"
|
|
|
|
# -- Automation panel + progress steps --------------------------------------
|
|
AUTOMATION_TITLE_TEXT = "#1f2937"
|
|
AUTOMATION_HINT_TEXT = "#374151"
|
|
STEP_RUNNING_TEXT = "#2563eb" # same blue as PRIMARY, separate knob
|
|
STEP_SUCCESS_TEXT = "#15803d"
|
|
STEP_FAILED_TEXT = "#b91c1c"
|
|
STEP_PAUSED_TEXT = "#c2410c"
|
|
STEP_DONE_BG = "#ecfdf3"
|
|
STEP_DONE_TEXT = "#166534"
|
|
STEP_DONE_BORDER = "#a7f3d0"
|
|
STEP_ACTIVE_BG = "#eff6ff"
|
|
STEP_ACTIVE_TEXT = "#1d4ed8"
|
|
STEP_ACTIVE_BORDER = "#bfdbfe"
|
|
STEP_FAILED_BG = "#fef2f2"
|
|
STEP_FAILED_BORDER = "#fecaca"
|
|
STEP_PAUSED_BG = "#fff7ed"
|
|
STEP_PAUSED_BORDER = "#fed7aa"
|
|
STEP_IDLE_BG = "#f8fafc"
|
|
STEP_IDLE_BORDER = "#e2e8f0"
|
|
|
|
# -- Baton request dialog ---------------------------------------------------
|
|
BATON_OK_BG = "#4caf50"
|
|
BATON_OK_HOVER = "#45a049"
|
|
BATON_OK_PRESSED = "#3d8b40"
|
|
BATON_DANGER_BG = "#f44336"
|
|
BATON_DANGER_HOVER = "#da190b"
|
|
BATON_DANGER_PRESSED = "#c41000"
|
|
BATON_WARN = "#ff9800"
|
|
BATON_INFO = "#2196f3"
|
|
LIGHT_BORDER = "#cccccc"
|
|
PROGRESS_TRACK_BG = "#f0f0f0"
|
|
|
|
# -- Splash screen ----------------------------------------------------------
|
|
SPLASH_BG = "#222222"
|
|
SPLASH_BORDER = "#444444"
|
|
SPLASH_ACCENT = "#0078d7"
|
|
|
|
# -- Numeric inputs ---------------------------------------------------------
|
|
INPUT_BG = "#ffffff"
|
|
INPUT_INVALID_BG = "#ffd5d5"
|
|
INPUT_DISABLED_BG = "#f0f0f0"
|
|
INPUT_DISABLED_INVALID_BG = "#f0e1e1"
|
|
|
|
# -- Status bar flags (hex equivalents of the old CSS named colors) ---------
|
|
STATUS_OK = "#008000" # closed / idle / owned / tell ok (was "green")
|
|
STATUS_ALERT = "#ff0000" # open / busy / other-owner / hot cryo (was "red")
|
|
STATUS_WARN = "#ffa500" # baton waiting / warming cryo / tell busy (was "orange")
|
|
STATUS_INFO = "#0000ff" # cold cryo (was "blue")
|
|
STATUS_VACANT = "#ffff00" # baton vacant (was "yellow")
|
|
STATUS_REQUEST = "#00ffff" # baton request (was "cyan")
|
|
|
|
# -- Beamline state panel ---------------------------------------------------
|
|
STATE_AVAILABLE = "#ed8936"
|
|
STATE_UNAVAILABLE = "#8c96a5"
|
|
STATE_MSG_ERROR = "#c81e1e"
|
|
STATE_MSG_INFO = "#005caa"
|
|
|
|
# -- Sample tables + raster grid --------------------------------------------
|
|
SAMPLE_ROW_ACTIVE_BG = "#ff6600"
|
|
SAMPLE_ROW_QUEUED_BG = "#729fcf" # mounted/current-sample row (legacy name)
|
|
SAMPLE_ROW_HIGHLIGHT_BG = "#d8e4fd"
|
|
RASTER_GRID_LINE = "#729fcf"
|
|
TABLE_SHADE_BG = "#e0e0e0"
|
|
|
|
# -- Sample status row tints (dewar/queue view) -----------------------------
|
|
# Status colors now fill the rounded dot in the table's status column; rows
|
|
# themselves alternate WHITE / SAMPLE_ROW_ALT_BG. Tune the palette here.
|
|
SAMPLE_ROW_ALT_BG = "#eef1f5" # staggered row grey (alternates with white)
|
|
SAMPLE_STATUS_QUEUED_BG = "#ffe4c4" # pale orange — waiting in the automation queue
|
|
SAMPLE_STATUS_FLAGGED_BG = "#ffd9d9" # pale red — automation failed on this sample
|
|
SAMPLE_STATUS_MEASURED_BG = "#dcf2e0" # pale green — already has collected data
|
|
SAMPLE_STATUS_SELECTED_BG = "#d8e8fd" # pale blue — table selection highlight
|
|
|
|
# -- Camera / video overlay (painter colors, alpha at call site) ------------
|
|
BEAM_OPEN = "#00ff00" # beam marker: shutter open
|
|
BEAM_IDLE = "#f57900" # beam marker: idle
|
|
BEAM_BUSY = "#ff0000" # beam marker: busy
|
|
BEAM_MARKING = "#663399" # beam marker: marking mode
|
|
MARKER_GREEN = "#32cd32" # loop-centering click marker
|
|
PATH_START = "#008000" # raster path gradient start + start circle
|
|
PATH_END = "#ff0000" # raster path gradient end + end circle
|
|
LEGEND_BG = "#141414"
|
|
LEGEND_TEXT = "#f0f0f0"
|
|
TOOLTIP_TEXT = "#e6e6e6" # camera coords tooltip pen — NOT the QToolTip popup
|
|
MARK_TOOLTIP_GOLD = "#ffd700"
|
|
MARK_TOOLTIP_ORANGE = "#ffa500"
|
|
MARK_TOOLTIP_RED = "#ff0000"
|
|
MARK_BADGE_BG = "#b43c00"
|
|
|
|
# Prediction class overlay colors. The chart variant historically used pure
|
|
# green (#00ff00) while the overlay used CSS green (#008000) — both kept.
|
|
CLASS_COLORS = {
|
|
"pin": "#ff0000",
|
|
"loop_all": "#008000",
|
|
"loop_face": "#ffff00",
|
|
"crystal": "#0000ff",
|
|
"needle": "#ff00ff",
|
|
"ice": "#00ffff",
|
|
}
|
|
CHART_CLASS_COLORS = {
|
|
"Pin": "#ff0000",
|
|
"Loop_all": "#00ff00",
|
|
"Loop_face": "#ffff00",
|
|
"Crystal": "#0000ff",
|
|
"Needle": "#ff00ff",
|
|
"Ice": "#00ffff",
|
|
}
|
|
TARGET_COLORS = {"Cyan": "#00ffff", "Dark Blue": "#0046a0", "Dark Red": "#8c1919"}
|
|
BOOKMARK_COLORS = {
|
|
"red": "#ff0000",
|
|
"green": "#008000",
|
|
"blue": "#0000ff",
|
|
"indigo": "#4b0082",
|
|
"lime": "#00ff00",
|
|
}
|
|
|
|
# -- Busy overlay (per-source color coding) ---------------------------------
|
|
BUSY_YELLOW = "#f1c40f"
|
|
BUSY_YELLOW_BORDER = "#fff8d2"
|
|
BUSY_YELLOW_DOT = "#fff6bf"
|
|
BUSY_YELLOW_TEXT_DARK = "#3b2f00"
|
|
BUSY_PURPLE = "#8e44ad"
|
|
BUSY_PURPLE_BORDER = "#ebdcf5"
|
|
BUSY_PURPLE_DOT = "#f0dfff"
|
|
BUSY_RED_BADGE = "#d64545"
|
|
BUSY_RED_FILL = "#be2828"
|
|
BUSY_RED_BORDER = "#ffdcdc"
|
|
BUSY_RED_DOT = "#ffdddd"
|
|
BUSY_ORANGE = "#e67e22"
|
|
BUSY_ORANGE_BORDER = "#ffead6"
|
|
BUSY_ORANGE_DOT = "#fff0db"
|
|
BUSY_BLUE = "#3498db"
|
|
BUSY_BLUE_BORDER = "#dcf0ff"
|
|
BUSY_BLUE_DOT = "#dff2ff"
|
|
BUSY_PSI_RED = "#e04f39"
|
|
BUSY_PSI_RED_BORDER = "#ffe1dc"
|
|
BUSY_PSI_RED_DOT = "#ffd8d1"
|
|
|
|
# -- Charts (prediction metrics, target stability, fluorescence) ------------
|
|
CHART_BLUE = "#1f77b4"
|
|
CHART_BLUE_LIGHT = "#6baed6"
|
|
CHART_BLUE_PALE = "#9ecae1"
|
|
CHART_RED = "#d62728"
|
|
CHART_RED_LIGHT = "#ff9896"
|
|
CHART_RED_DARK = "#c43c39"
|
|
CHART_ORANGE = "#ff7f0e"
|
|
CHART_ORANGE_PALE = "#ffbb78"
|
|
CHART_GREEN = "#2ca02c"
|
|
CHART_GREEN_PALE = "#98df8a"
|
|
CHART_CYAN = "#17becf"
|
|
CHART_PURPLE = "#9467bd"
|
|
CHART_MUTED = "#888888"
|
|
CONFIDENCE_BIN_COLORS = [CHART_RED, CHART_ORANGE, CHART_ORANGE_PALE, CHART_GREEN_PALE, CHART_GREEN]
|
|
SPECTRUM_LINE = "#cc0000"
|
|
|
|
# -- Generic panels (developer help, raster table) --------------------------
|
|
PANEL_BG_SOFT = "#f6f6f6"
|
|
PANEL_BG_FAINT = "#fafafa"
|
|
PANEL_BORDER = "#d0d0d0"
|
|
PANEL_BORDER_DARK = "#bdbdbd"
|
|
PANEL_BORDER_LIGHT = "#e0e0e0"
|
|
TUTORIAL_BORDER = "#555555"
|
|
TUTORIAL_HIGHLIGHT = "#ffff00" # widget spotlight pen (was Qt.yellow)
|
|
|
|
# -- Typography ladder (role-named; tune sizes here, not in widgets) --------
|
|
FONT_HERO = "28px" # portrait main action button
|
|
FONT_ALERT = "18px" # alert banner text
|
|
FONT_VALUE = "18px" # prominent values, big glyph buttons
|
|
FONT_TITLE = "16px" # section / panel titles
|
|
FONT_BODY_LG = "14px" # primary buttons, emphasized body text — always bold
|
|
FONT_BODY = "14px" # default UI text
|
|
FONT_LABEL = "14px" # form labels, secondary text — always bold
|
|
FONT_HINT = "12px" # hints
|
|
FONT_FINE = "11px" # fine print, queue titles
|
|
|
|
# -- Hover tooltips (the QToolTip popup; styled borderless) -----------------
|
|
TOOLTIP_BG = "#f7f9fc"
|
|
TOOLTIP_FG = "#263043"
|
|
DARK_TOOLTIP_BG = "#363a4f" # surface0
|
|
DARK_TOOLTIP_FG = "#cad3f5" # text
|
|
|
|
# -- Sliders ----------------------------------------------------------------
|
|
# Own knob instead of PRIMARY: full-saturation button blue was too loud for a
|
|
# passive fill (illumination panel). Muted slate-blue, tweak freely.
|
|
SLIDER_FILL = "#8ba3c7"
|
|
|
|
# -- Scrollbars (rounded, no arrows: grey track, darker draggable handle) ---
|
|
SCROLLBAR_TRACK = "#d8dde5"
|
|
SCROLLBAR_HANDLE = "#a8b2c0"
|
|
SCROLLBAR_HANDLE_HOVER = "#8794a6"
|
|
|
|
# -- Cards ------------------------------------------------------------------
|
|
CARD_RADIUS = "12px" # one radius for every card-shaped frame
|
|
FLAT_CARD_RADIUS = "0px" # recovery + console-log cards stay square
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def _palette() -> dict[str, str]:
|
|
# Why: one substitution mapping instead of ~40 kwargs per stylesheet, and
|
|
# a renamed/missing constant fails loudly (KeyError) instead of silently.
|
|
return {k.lower(): v for k, v in globals().items() if k.isupper() and isinstance(v, str)}
|
|
|
|
|
|
def qcolor(color: str, alpha: int | None = None):
|
|
"""QColor from a palette hex string, optional 0-255 alpha.
|
|
|
|
Hue lives here (theme), alpha stays at the call site (behavior). Lazy Qt
|
|
import so this module — and its self-check — stay importable without
|
|
PySide6.
|
|
"""
|
|
from PySide6.QtGui import QColor
|
|
|
|
c = QColor(color)
|
|
if alpha is not None:
|
|
c.setAlpha(alpha)
|
|
return c
|
|
|
|
|
|
def card_style(
|
|
bg: str,
|
|
border: str,
|
|
text: str | None = None,
|
|
*,
|
|
selector: str = "QFrame",
|
|
border_px: int = 1,
|
|
radius: str = CARD_RADIUS,
|
|
extra: str = "",
|
|
) -> str:
|
|
"""One QSS rule for every card-shaped frame: geometry stays uniform
|
|
(CARD_RADIUS unless overridden), only the role colors vary. `extra`
|
|
appends declarations (padding, font-weight, …) inside the same rule."""
|
|
color = f" color: {text};" if text else ""
|
|
tail = f" {extra}" if extra else ""
|
|
return (
|
|
f"{selector} {{ background: {bg};{color}"
|
|
f" border: {border_px}px solid {border}; border-radius: {radius};{tail} }}"
|
|
)
|
|
|
|
|
|
def build_app_stylesheet(theme: str) -> str:
|
|
if theme == THEME_PORTRAIT:
|
|
return _portrait_stylesheet()
|
|
return _original_stylesheet()
|
|
|
|
|
|
def _original_stylesheet() -> str:
|
|
return Template("""
|
|
QMainWindow, QWidget {
|
|
background-color: $background;
|
|
color: $text;
|
|
}
|
|
|
|
QWidget#mainContentRoot,
|
|
QWidget#standardMainPage,
|
|
QWidget#compactAutomationPage {
|
|
background-color: $background;
|
|
}
|
|
|
|
QWidget#portraitModePage {
|
|
background-color: $dark_bg;
|
|
}
|
|
|
|
/* Borderless hover hints. The transparent border is required — QToolTip
|
|
only honours the stylesheet background once a border is set. */
|
|
QToolTip {
|
|
background-color: $tooltip_bg;
|
|
color: $tooltip_fg;
|
|
border: 1px solid transparent;
|
|
padding: 4px 6px;
|
|
}
|
|
|
|
QFrame#compactAutomationPanel {
|
|
background: $background;
|
|
border: none;
|
|
border-radius: 18px;
|
|
}
|
|
|
|
QFrame#compactCameraCard,
|
|
QFrame#compactControlsCard,
|
|
QFrame#compactProgressCard,
|
|
QFrame#compactQueueCard,
|
|
QFrame#compactQueueItem {
|
|
background: $compact_card_bg;
|
|
border: 1px solid $compact_border;
|
|
border-radius: 16px;
|
|
}
|
|
|
|
QLabel#compactSectionTitle {
|
|
background: transparent;
|
|
color: $compact_title;
|
|
font-size: $font_body;
|
|
font-weight: 700;
|
|
}
|
|
|
|
QLabel#compactSectionHint {
|
|
background: transparent;
|
|
color: $compact_hint;
|
|
font-size: $font_hint;
|
|
}
|
|
|
|
QLabel#compactQueueTitle {
|
|
background: transparent;
|
|
color: $compact_hint;
|
|
font-size: $font_fine;
|
|
font-weight: 700;
|
|
}
|
|
|
|
QLabel#compactQueueValue {
|
|
background: transparent;
|
|
color: $compact_value;
|
|
font-size: $font_body;
|
|
font-weight: 700;
|
|
}
|
|
|
|
QToolButton#compactMenuButton {
|
|
background: $compact_menu_bg;
|
|
color: $compact_title;
|
|
border: 1px solid $compact_border;
|
|
border-radius: 14px;
|
|
padding: 10px 14px;
|
|
font-size: $font_value;
|
|
font-weight: 700;
|
|
}
|
|
|
|
QToolButton#compactMenuButton:hover {
|
|
background: $compact_menu_bg_hover;
|
|
}
|
|
|
|
QPushButton#compactPrimaryButton {
|
|
background: $primary;
|
|
color: $primary_text;
|
|
border: none;
|
|
border-radius: 14px;
|
|
padding: 14px 18px;
|
|
font-size: $font_body_lg;
|
|
font-weight: 700;
|
|
}
|
|
|
|
QPushButton#compactPrimaryButton:hover {
|
|
background: $primary_hover;
|
|
}
|
|
|
|
QPushButton#compactSecondaryButton,
|
|
QToolButton#compactSecondaryButton {
|
|
background: $secondary_bg;
|
|
color: $compact_title;
|
|
border: 1px solid $compact_border;
|
|
border-radius: 14px;
|
|
padding: 14px 18px;
|
|
font-size: $font_body;
|
|
font-weight: 700;
|
|
}
|
|
|
|
QPushButton#compactSecondaryButton:hover,
|
|
QToolButton#compactSecondaryButton:hover {
|
|
background: $secondary_bg_hover;
|
|
}
|
|
|
|
QFrame#alertBanner[alertKind="error"] {
|
|
background-color: $error_bg;
|
|
border: 2px solid $error_border;
|
|
border-radius: $card_radius;
|
|
margin: 8px 12px 8px 12px;
|
|
}
|
|
|
|
QFrame#alertBanner[alertKind="success"] {
|
|
background-color: $success_bg;
|
|
border: 2px solid $success_border;
|
|
border-radius: $card_radius;
|
|
margin: 8px 12px 8px 12px;
|
|
}
|
|
|
|
QFrame#alertBanner[alertKind="waiting"] {
|
|
background-color: $warning_bg;
|
|
border: 2px solid $warning_border;
|
|
border-radius: $card_radius;
|
|
margin: 8px 12px 8px 12px;
|
|
}
|
|
|
|
QFrame#alertBanner QLabel {
|
|
font-weight: 700;
|
|
font-size: $font_alert;
|
|
padding: 2px 6px 2px 6px;
|
|
}
|
|
|
|
QFrame#alertBanner[alertKind="error"] QLabel {
|
|
color: $error_text;
|
|
}
|
|
|
|
QFrame#alertBanner[alertKind="success"] QLabel {
|
|
color: $success_text;
|
|
}
|
|
|
|
QFrame#alertBanner[alertKind="waiting"] QLabel {
|
|
color: $warning_text;
|
|
}
|
|
|
|
QWidget#axisVideoStatusContainer[busyState="idle"] {
|
|
border-radius: $card_radius;
|
|
background-color: $status_idle_bg;
|
|
}
|
|
|
|
QWidget#axisVideoStatusContainer[busyState="active"] {
|
|
border-radius: $card_radius;
|
|
}
|
|
|
|
QLabel#axisVideoStatusDot {
|
|
min-width: 10px;
|
|
max-width: 10px;
|
|
min-height: 10px;
|
|
max-height: 10px;
|
|
border-radius: 5px;
|
|
background-color: transparent;
|
|
}
|
|
|
|
QLabel#axisVideoStatusLabel {
|
|
background-color: transparent;
|
|
color: $status_label_text;
|
|
font-weight: bold;
|
|
}
|
|
|
|
/* Disabled = baton-gated ("watch only"): the explicit colors above mask
|
|
Qt's native grey, so spell the greyed state out. */
|
|
QPushButton:disabled, QCheckBox:disabled, QRadioButton:disabled,
|
|
QLabel:disabled, QComboBox:disabled, QLineEdit:disabled,
|
|
QAbstractSpinBox:disabled, QTabBar::tab:disabled {
|
|
color: $faint_text;
|
|
}
|
|
|
|
QLineEdit:disabled, QAbstractSpinBox:disabled, QComboBox:disabled {
|
|
background: $scrollbar_track;
|
|
}
|
|
|
|
QSlider::sub-page:horizontal:disabled {
|
|
background: $scrollbar_handle;
|
|
}
|
|
|
|
QTabWidget::pane {
|
|
border: 1px solid $border;
|
|
}
|
|
|
|
/* Exp. Config. pane: hand-rolled QTabBar+QStackedWidget (button row sits
|
|
between bar and pages), so it needs the pane border by hand too. */
|
|
#expConfigPane {
|
|
border: 1px solid $border;
|
|
}
|
|
|
|
/* Borderless tabs: the pane isn't outlined, so the native 3-side tab
|
|
boxes hung in the air. Selection shows via fill + the raised effect
|
|
(unselected tabs sit 3px lower). */
|
|
QTabBar::tab {
|
|
background: transparent;
|
|
border: none;
|
|
border-top-left-radius: 4px;
|
|
border-top-right-radius: 4px;
|
|
padding: 4px 14px;
|
|
margin-top: 3px;
|
|
color: $muted_text;
|
|
}
|
|
|
|
QTabBar::tab:selected {
|
|
background: $tab_face_bg;
|
|
color: $text;
|
|
margin-top: 0px;
|
|
padding: 6px 14px 5px 14px;
|
|
}
|
|
|
|
/* Menu-bar-style hover hint: text-decoration underlines only the text
|
|
itself (short, close to the baseline) instead of the full tab box. */
|
|
QTabBar::tab:hover:!selected {
|
|
color: $text;
|
|
text-decoration: underline;
|
|
}
|
|
|
|
QMainWindow::separator {
|
|
background: $border;
|
|
width: 4px;
|
|
height: 4px;
|
|
}
|
|
|
|
QFrame#beamlineControls,
|
|
QFrame#dataCollectionSettings {
|
|
border: 1px solid $border;
|
|
}
|
|
|
|
QFrame#beamlineStatePanel {
|
|
background: $background;
|
|
border-top: 1px solid $border;
|
|
}
|
|
|
|
QLabel#beamlineStateTitle {
|
|
background-color: $banner;
|
|
color: $banner_text;
|
|
font-size: $font_title;
|
|
font-weight: 700;
|
|
}
|
|
|
|
/* Bare glyph to match the TitleLabel toggles: no pill background. */
|
|
QPushButton#beamlineStateToggleButton {
|
|
border: none;
|
|
background: transparent;
|
|
color: $state_toggle_text;
|
|
font-size: $font_body;
|
|
font-weight: 700;
|
|
}
|
|
|
|
QLabel#beamlineStateCurrentLabel {
|
|
color: $state_current_text;
|
|
font-size: $font_value;
|
|
font-weight: 700;
|
|
padding-left: 4px;
|
|
background: transparent;
|
|
}
|
|
|
|
QLabel#beamlineStateTellLabel {
|
|
color: $state_tell_text;
|
|
font-size: $font_body_lg;
|
|
font-weight: 700;
|
|
padding-left: 4px;
|
|
background: transparent;
|
|
}
|
|
|
|
/* Plain scroll containers stay frameless. */
|
|
QScrollArea {
|
|
border: none;
|
|
}
|
|
|
|
/* Box-frame levels — weights/colors are knobs in styles.py. L2: panel
|
|
frames (replaces their native StyledPanel etching); L3: data views. */
|
|
TellSamplePanel, ReferenceToolsPanel, SampleQueuePanel {
|
|
border: $frame_l2_width solid $frame_l2_color;
|
|
}
|
|
|
|
QTableView, QPlainTextEdit {
|
|
border: $frame_l3_width solid $frame_l3_color;
|
|
}
|
|
|
|
/* Pale-blue selection with readable dark text in every sample table;
|
|
staggered grey/white rows where alternation is enabled. */
|
|
QTableView {
|
|
background: $white;
|
|
alternate-background-color: $sample_row_alt_bg;
|
|
selection-background-color: $sample_status_selected_bg;
|
|
selection-color: $text;
|
|
}
|
|
|
|
/* Selection highlight: the banner blue (SELECTION_BG in styles.py)
|
|
instead of the style palette's own blue. Tables keep their paler
|
|
selection rule below. */
|
|
QListView, QTreeView,
|
|
QComboBox QAbstractItemView {
|
|
selection-background-color: $selection_bg;
|
|
selection-color: $selection_text;
|
|
}
|
|
|
|
QMenu::item:selected {
|
|
background: $selection_bg;
|
|
color: $selection_text;
|
|
}
|
|
|
|
QLineEdit, QAbstractSpinBox, QTextEdit, QPlainTextEdit {
|
|
selection-background-color: $selection_bg;
|
|
selection-color: $selection_text;
|
|
}
|
|
|
|
/* Sliders: rounded groove ends (like the scrollbar handles) + round
|
|
handle. Wheel adjustment needs the right mouse button held — see
|
|
WheelValueGuard. */
|
|
QSlider::groove:horizontal {
|
|
height: 6px;
|
|
background: $scrollbar_track;
|
|
border-radius: 3px;
|
|
}
|
|
|
|
QSlider::sub-page:horizontal {
|
|
background: $slider_fill;
|
|
border-radius: 3px;
|
|
}
|
|
|
|
QSlider::handle:horizontal {
|
|
background: $white;
|
|
border: 1px solid $scrollbar_handle;
|
|
width: 14px;
|
|
margin: -5px 0;
|
|
border-radius: 7px;
|
|
}
|
|
|
|
/* Soft scrollbars: square track band (runs flush to the widget edges —
|
|
rounded ends left corner notches in the tables) + rounded draggable
|
|
handle, no end arrows. */
|
|
QScrollBar:vertical {
|
|
background: $scrollbar_track;
|
|
width: 10px;
|
|
border: none;
|
|
margin: 0px;
|
|
}
|
|
|
|
QScrollBar:horizontal {
|
|
background: $scrollbar_track;
|
|
height: 10px;
|
|
border: none;
|
|
margin: 0px;
|
|
}
|
|
|
|
QScrollBar::handle:vertical {
|
|
background: $scrollbar_handle;
|
|
border-radius: 5px;
|
|
min-height: 24px;
|
|
}
|
|
|
|
QScrollBar::handle:horizontal {
|
|
background: $scrollbar_handle;
|
|
border-radius: 5px;
|
|
min-width: 24px;
|
|
}
|
|
|
|
QScrollBar::handle:vertical:hover,
|
|
QScrollBar::handle:vertical:pressed,
|
|
QScrollBar::handle:horizontal:hover,
|
|
QScrollBar::handle:horizontal:pressed {
|
|
background: $scrollbar_handle_hover;
|
|
}
|
|
|
|
QScrollBar::add-line, QScrollBar::sub-line {
|
|
width: 0px;
|
|
height: 0px;
|
|
}
|
|
|
|
QScrollBar::add-page, QScrollBar::sub-page {
|
|
background: transparent;
|
|
}
|
|
|
|
/* The two scrollbar bands meet in a track-colored corner — no white
|
|
square, the bands read as one continuous edge. */
|
|
QAbstractScrollArea::corner {
|
|
background: $scrollbar_track;
|
|
border: none;
|
|
}
|
|
|
|
QWidget#portraitRoot,
|
|
QWidget#portraitRoot QWidget {
|
|
background: $dark_bg;
|
|
color: $dark_text;
|
|
font-family: 'Inter', 'SF Pro Display', Arial, sans-serif;
|
|
font-size: $font_body;
|
|
}
|
|
|
|
QWidget#portraitRoot QScrollArea {
|
|
border: none;
|
|
background: transparent;
|
|
}
|
|
|
|
QWidget#portraitRoot QScrollBar:vertical {
|
|
background: $dark_surface;
|
|
width: 4px;
|
|
border-radius: 2px;
|
|
}
|
|
|
|
QWidget#portraitRoot QScrollBar::handle:vertical {
|
|
background: $dark_border;
|
|
border-radius: 2px;
|
|
min-height: 20px;
|
|
}
|
|
|
|
QWidget#portraitRoot QScrollBar::add-line:vertical,
|
|
QWidget#portraitRoot QScrollBar::sub-line:vertical {
|
|
height: 0px;
|
|
}
|
|
""").substitute(_palette())
|
|
|
|
|
|
def _portrait_stylesheet() -> str:
|
|
return Template("""
|
|
QMainWindow, QWidget {
|
|
background: $dark_bg;
|
|
color: $dark_text;
|
|
}
|
|
|
|
QWidget#mainContentRoot,
|
|
QWidget#standardMainPage,
|
|
QWidget#compactAutomationPage,
|
|
QWidget#portraitModePage {
|
|
background: $dark_bg;
|
|
}
|
|
|
|
QTabWidget::pane,
|
|
QScrollArea,
|
|
QDockWidget,
|
|
QDockWidget > QWidget {
|
|
background: $dark_bg;
|
|
color: $dark_text;
|
|
}
|
|
|
|
/* Borderless tabs, dark flavor — see the light-theme note. */
|
|
QTabBar::tab {
|
|
background: transparent;
|
|
border: none;
|
|
border-top-left-radius: 4px;
|
|
border-top-right-radius: 4px;
|
|
padding: 4px 14px;
|
|
margin-top: 3px;
|
|
color: $dark_muted;
|
|
}
|
|
|
|
QTabBar::tab:selected {
|
|
background: $dark_elevated;
|
|
color: $dark_text;
|
|
margin-top: 0px;
|
|
padding: 6px 14px 5px 14px;
|
|
}
|
|
|
|
/* Menu-bar-style hover hint — see the light-theme note. */
|
|
QTabBar::tab:hover:!selected {
|
|
color: $dark_text;
|
|
text-decoration: underline;
|
|
}
|
|
|
|
/* Borderless hover hints. The transparent border is required — QToolTip
|
|
only honours the stylesheet background once a border is set. */
|
|
QToolTip {
|
|
background-color: $dark_tooltip_bg;
|
|
color: $dark_tooltip_fg;
|
|
border: 1px solid transparent;
|
|
padding: 4px 6px;
|
|
}
|
|
|
|
QFrame#compactAutomationPanel {
|
|
background: $dark_bg;
|
|
border: none;
|
|
border-radius: 18px;
|
|
}
|
|
|
|
QFrame#compactCameraCard,
|
|
QFrame#compactControlsCard,
|
|
QFrame#compactProgressCard,
|
|
QFrame#compactQueueCard,
|
|
QFrame#compactQueueItem {
|
|
background: $dark_surface;
|
|
border: 1px solid $dark_border;
|
|
border-radius: 16px;
|
|
}
|
|
|
|
QLabel#compactSectionTitle {
|
|
background: transparent;
|
|
color: $dark_accent;
|
|
font-size: $font_body;
|
|
font-weight: 700;
|
|
}
|
|
|
|
QLabel#compactSectionHint {
|
|
background: transparent;
|
|
color: $dark_muted;
|
|
font-size: $font_hint;
|
|
}
|
|
|
|
QLabel#compactQueueTitle {
|
|
background: transparent;
|
|
color: $dark_muted;
|
|
font-size: $font_fine;
|
|
font-weight: 700;
|
|
}
|
|
|
|
QLabel#compactQueueValue {
|
|
background: transparent;
|
|
color: $dark_text;
|
|
font-size: $font_body;
|
|
font-weight: 700;
|
|
}
|
|
|
|
QToolButton#compactMenuButton {
|
|
background: $dark_elevated;
|
|
color: $dark_accent;
|
|
border: 1px solid $dark_border;
|
|
border-radius: 14px;
|
|
padding: 10px 14px;
|
|
font-size: $font_value;
|
|
font-weight: 700;
|
|
}
|
|
|
|
QToolButton#compactMenuButton:hover {
|
|
background: $dark_border;
|
|
}
|
|
|
|
QPushButton#compactPrimaryButton {
|
|
background: $dark_accent;
|
|
color: $dark_bg;
|
|
border: none;
|
|
border-radius: 14px;
|
|
padding: 14px 18px;
|
|
font-size: $font_body_lg;
|
|
font-weight: 700;
|
|
}
|
|
|
|
QPushButton#compactPrimaryButton:hover {
|
|
background: $dark_accent_hover;
|
|
}
|
|
|
|
QPushButton#compactSecondaryButton,
|
|
QToolButton#compactSecondaryButton {
|
|
background: $dark_elevated;
|
|
color: $dark_text;
|
|
border: 1px solid $dark_border;
|
|
border-radius: 14px;
|
|
padding: 14px 18px;
|
|
font-size: $font_body;
|
|
font-weight: 700;
|
|
}
|
|
|
|
QPushButton#compactSecondaryButton:hover,
|
|
QToolButton#compactSecondaryButton:hover {
|
|
background: $dark_border;
|
|
}
|
|
|
|
QFrame#alertBanner[alertKind="error"] {
|
|
background: $dark_error_bg;
|
|
border: 2px solid $dark_error_border;
|
|
border-radius: $card_radius;
|
|
margin: 8px 12px 8px 12px;
|
|
}
|
|
|
|
QFrame#alertBanner[alertKind="success"] {
|
|
background: $dark_success_bg;
|
|
border: 2px solid $dark_success_border;
|
|
border-radius: $card_radius;
|
|
margin: 8px 12px 8px 12px;
|
|
}
|
|
|
|
QFrame#alertBanner[alertKind="waiting"] {
|
|
background: $dark_warning_bg;
|
|
border: 2px solid $dark_warning_border;
|
|
border-radius: $card_radius;
|
|
margin: 8px 12px 8px 12px;
|
|
}
|
|
|
|
QFrame#alertBanner QLabel {
|
|
font-weight: 700;
|
|
font-size: $font_alert;
|
|
padding: 2px 6px 2px 6px;
|
|
}
|
|
|
|
QFrame#alertBanner[alertKind="error"] QLabel {
|
|
color: $dark_error_text;
|
|
}
|
|
|
|
QFrame#alertBanner[alertKind="success"] QLabel {
|
|
color: $dark_success_text;
|
|
}
|
|
|
|
QFrame#alertBanner[alertKind="waiting"] QLabel {
|
|
color: $dark_warning_text;
|
|
}
|
|
|
|
QWidget#axisVideoStatusContainer[busyState="idle"] {
|
|
border-radius: $card_radius;
|
|
background-color: $dark_elevated;
|
|
}
|
|
|
|
QWidget#axisVideoStatusContainer[busyState="active"] {
|
|
border-radius: $card_radius;
|
|
}
|
|
|
|
QLabel#axisVideoStatusDot {
|
|
min-width: 10px;
|
|
max-width: 10px;
|
|
min-height: 10px;
|
|
max-height: 10px;
|
|
border-radius: 5px;
|
|
background-color: transparent;
|
|
}
|
|
|
|
QLabel#axisVideoStatusLabel {
|
|
background-color: transparent;
|
|
color: $dark_muted;
|
|
font-weight: bold;
|
|
}
|
|
|
|
QFrame#beamlineStatePanel {
|
|
background: $dark_surface;
|
|
border-top: 1px solid $dark_border;
|
|
}
|
|
|
|
QLabel#beamlineStateTitle {
|
|
background-color: $dark_elevated;
|
|
color: $dark_text;
|
|
font-size: $font_title;
|
|
font-weight: 700;
|
|
}
|
|
|
|
/* Bare glyph to match the TitleLabel toggles: no pill background. */
|
|
QPushButton#beamlineStateToggleButton {
|
|
border: none;
|
|
background: transparent;
|
|
color: $dark_text;
|
|
font-size: $font_body;
|
|
font-weight: 700;
|
|
}
|
|
|
|
QLabel#beamlineStateCurrentLabel {
|
|
color: $dark_text;
|
|
font-size: $font_value;
|
|
font-weight: 700;
|
|
padding-left: 4px;
|
|
background: transparent;
|
|
}
|
|
|
|
QLabel#beamlineStateTellLabel {
|
|
color: $dark_muted;
|
|
font-size: $font_body_lg;
|
|
font-weight: 700;
|
|
padding-left: 4px;
|
|
background: transparent;
|
|
}
|
|
|
|
/* Plain scroll containers stay frameless. */
|
|
QScrollArea {
|
|
border: none;
|
|
}
|
|
|
|
/* Box-frame levels — weights/colors are knobs in styles.py. */
|
|
TellSamplePanel, ReferenceToolsPanel, SampleQueuePanel {
|
|
border: $frame_l2_width solid $frame_l2_color;
|
|
}
|
|
|
|
QTableView, QPlainTextEdit {
|
|
border: $frame_l3_width solid $frame_l3_color;
|
|
}
|
|
|
|
/* Selection + staggered rows, dark flavor. */
|
|
QTableView {
|
|
background: $dark_bg;
|
|
alternate-background-color: $dark_surface;
|
|
selection-background-color: $sample_status_selected_bg;
|
|
selection-color: $text;
|
|
}
|
|
|
|
/* Selection highlight — same banner-blue knob as the light theme. */
|
|
QListView, QTreeView,
|
|
QComboBox QAbstractItemView {
|
|
selection-background-color: $selection_bg;
|
|
selection-color: $selection_text;
|
|
}
|
|
|
|
QMenu::item:selected {
|
|
background: $selection_bg;
|
|
color: $selection_text;
|
|
}
|
|
|
|
QLineEdit, QAbstractSpinBox, QTextEdit, QPlainTextEdit {
|
|
selection-background-color: $selection_bg;
|
|
selection-color: $selection_text;
|
|
}
|
|
|
|
/* Sliders, dark flavor — see the light-theme note. */
|
|
QSlider::groove:horizontal {
|
|
height: 6px;
|
|
background: $dark_border;
|
|
border-radius: 3px;
|
|
}
|
|
|
|
QSlider::sub-page:horizontal {
|
|
background: $dark_accent;
|
|
border-radius: 3px;
|
|
}
|
|
|
|
QSlider::handle:horizontal {
|
|
background: $dark_elevated;
|
|
border: 1px solid $dark_muted;
|
|
width: 14px;
|
|
margin: -5px 0;
|
|
border-radius: 7px;
|
|
}
|
|
|
|
/* Soft scrollbars: square track band + rounded handle, no end arrows
|
|
(see light-theme note on the corner notches). */
|
|
QScrollBar:vertical {
|
|
background: $dark_surface;
|
|
width: 10px;
|
|
border: none;
|
|
margin: 0px;
|
|
}
|
|
|
|
QScrollBar:horizontal {
|
|
background: $dark_surface;
|
|
height: 10px;
|
|
border: none;
|
|
margin: 0px;
|
|
}
|
|
|
|
QScrollBar::handle:vertical {
|
|
background: $dark_border;
|
|
border-radius: 5px;
|
|
min-height: 24px;
|
|
}
|
|
|
|
QScrollBar::handle:horizontal {
|
|
background: $dark_border;
|
|
border-radius: 5px;
|
|
min-width: 24px;
|
|
}
|
|
|
|
QScrollBar::handle:vertical:hover,
|
|
QScrollBar::handle:vertical:pressed,
|
|
QScrollBar::handle:horizontal:hover,
|
|
QScrollBar::handle:horizontal:pressed {
|
|
background: $dark_muted;
|
|
}
|
|
|
|
QScrollBar::add-line, QScrollBar::sub-line {
|
|
width: 0px;
|
|
height: 0px;
|
|
}
|
|
|
|
QScrollBar::add-page, QScrollBar::sub-page {
|
|
background: transparent;
|
|
}
|
|
|
|
/* The two scrollbar bands meet in a track-colored corner. */
|
|
QAbstractScrollArea::corner {
|
|
background: $dark_surface;
|
|
border: none;
|
|
}
|
|
|
|
QWidget#portraitRoot,
|
|
QWidget#portraitRoot QWidget {
|
|
background: $dark_bg;
|
|
color: $dark_text;
|
|
font-family: 'Inter', 'SF Pro Display', Arial, sans-serif;
|
|
font-size: $font_body;
|
|
}
|
|
|
|
QWidget#portraitRoot QScrollArea {
|
|
border: none;
|
|
background: transparent;
|
|
}
|
|
|
|
QWidget#portraitRoot QScrollBar:vertical {
|
|
background: $dark_surface;
|
|
width: 4px;
|
|
border-radius: 2px;
|
|
}
|
|
|
|
QWidget#portraitRoot QScrollBar::handle:vertical {
|
|
background: $dark_border;
|
|
border-radius: 2px;
|
|
min-height: 20px;
|
|
}
|
|
|
|
QWidget#portraitRoot QScrollBar::add-line:vertical,
|
|
QWidget#portraitRoot QScrollBar::sub-line:vertical {
|
|
height: 0px;
|
|
}
|
|
""").substitute(_palette())
|
|
|
|
|
|
if __name__ == "__main__":
|
|
# ponytail: smallest check that fails if a $name has no matching constant
|
|
for _theme in (THEME_ORIGINAL, THEME_PORTRAIT):
|
|
assert "$" not in build_app_stylesheet(_theme)
|
|
print("gude")
|