Standardize shell UI theme tokens
This commit is contained in:
@@ -17,6 +17,10 @@ hl.window_rule({ match = { class = "^org.quickshell$", title = "^Omarchy Bar Set
|
||||
hl.window_rule({ match = { class = "^org.quickshell$", title = "^Omarchy Bar Settings$" }, center = true })
|
||||
hl.window_rule({ match = { class = "^org.quickshell$", title = "^Omarchy Bar Settings$" }, size = { 760, 620 } })
|
||||
|
||||
-- Dev gallery is the main shell workbench; open it maximized like
|
||||
-- SUPER+ALT+F so component previews have the whole workspace.
|
||||
hl.window_rule({ match = { class = "^org.quickshell$", title = "^Omarchy shell – dev gallery$" }, maximize = true })
|
||||
|
||||
-- Per-widget settings dialog opens as a smaller FloatingWindow off the
|
||||
-- bar settings panel; keep it floating with its own default size.
|
||||
hl.window_rule({ match = { class = "^org.quickshell$", title = "^Widget settings " }, float = true })
|
||||
|
||||
@@ -13,21 +13,46 @@ size-horizontal = 26
|
||||
size-vertical = 28
|
||||
|
||||
[style]
|
||||
# State alphas used by every interactive surface in the kit (Button,
|
||||
# Toggle, TextField, etc.). Foreground-tinted unless noted.
|
||||
border-width = 1 # idle 1px border on inputs and bordered buttons
|
||||
focus-border-width = 3 # accent ring on Tab focus
|
||||
idle-border-alpha = 0.4 # alpha for the idle 1px foreground border
|
||||
hot-fill-alpha = 0.08 # cursor / hover fill
|
||||
selected-fill-alpha = 0.18 # selected / active / current fill
|
||||
pressed-fill-alpha = 0.22 # button pressed
|
||||
focus-fill-alpha = 0.22 # accent fill behind Tab focus ring
|
||||
# Shared control state tokens. See docs/omarchy-shell.md#interactive-states.
|
||||
# Colors accept palette roles (foreground/accent/urgent/background) or hex.
|
||||
|
||||
# Normal: idle control chrome.
|
||||
normal-color = "foreground"
|
||||
normal-fill-alpha = 0.04
|
||||
normal-border-width = 1
|
||||
normal-border-alpha = 0.4
|
||||
|
||||
# Hover-cursor: mouse hover and the panel keyboard cursor.
|
||||
hover-cursor-color = "foreground"
|
||||
hover-cursor-fill-alpha = 0.08
|
||||
hover-cursor-border-width = 1
|
||||
hover-cursor-border-alpha = 1.0
|
||||
|
||||
# Selected: persistent chosen/current state.
|
||||
selected-color = "foreground"
|
||||
selected-fill-alpha = 0.18
|
||||
selected-border-width = 0
|
||||
selected-border-alpha = 1.0
|
||||
|
||||
# Focus: Qt activeFocus; inherit hover-cursor unless intentionally different.
|
||||
focus-color = "hover-cursor"
|
||||
focus-fill-alpha = "hover-cursor"
|
||||
focus-border-width = "hover-cursor"
|
||||
focus-border-alpha = "hover-cursor"
|
||||
|
||||
# Momentary fills.
|
||||
pressed-fill-alpha = 0.22
|
||||
selection-fill-alpha = 0.35
|
||||
|
||||
[spacing]
|
||||
# Multiplies shared margins, gaps, and padding. See docs/omarchy-shell.md#spacing.
|
||||
scale = 1.0
|
||||
|
||||
[font]
|
||||
# base-size is the rem root for the type scale. Every Style.font.<token>
|
||||
# derives from it (e.g. body = base, subtitle ≈ base * 1.083,
|
||||
# heading ≈ base * 1.333). Clamped 11..13 by the shell — row heights are
|
||||
# fixed until we ship matching spacing tokens, so growth would clip.
|
||||
# heading ≈ base * 1.333). Clamped 11..13 by the shell because some
|
||||
# row heights remain fixed, so larger type can clip.
|
||||
base-size = 12
|
||||
# Per-token overrides, in px. Uncomment any to pin a specific size without
|
||||
# affecting the rest of the scale. Useful for stylistic emphasis (a
|
||||
|
||||
+90
-3
@@ -112,10 +112,97 @@ The shell exposes these tokens to QML via two singletons in
|
||||
- `Color` — palette (`foreground`, `background`, `accent`, `urgent`)
|
||||
and per-surface roles (`Color.bar.*`, `Color.popups.*`,
|
||||
`Color.notifications.*`, `Color.menu.*`, `Color.imagePicker.*`).
|
||||
- `Style` — structural tokens (`cornerRadius`, focus affordances),
|
||||
- `Style` — structural tokens (`cornerRadius`), shared interactive
|
||||
state tokens/helpers, spacing (`Style.spacing.*` / `Style.space(px)`),
|
||||
the type scale (`Style.font.*`), and bar dimensions
|
||||
(`Style.bar.sizeHorizontal` / `Style.bar.sizeVertical`).
|
||||
|
||||
### Interactive states
|
||||
|
||||
`[style]` standardizes reusable control chrome around four states:
|
||||
`normal`, `hover-cursor`, `selected`, and `focus`. State colors can be
|
||||
palette roles (`foreground`, `accent`, `urgent`, `background`) or hex
|
||||
strings. Fill/border alphas are applied to that state's color.
|
||||
|
||||
Focus inherits `hover-cursor` by default so mouse hover, the panel
|
||||
keyboard cursor, and Qt activeFocus read as the same state. Use the
|
||||
literal value `"hover-cursor"` on `focus-*` tokens to keep that
|
||||
inheritance; set an explicit color/number only when a theme intentionally
|
||||
wants focus to differ.
|
||||
|
||||
| State | Color token | Fill alpha | Border width | Border alpha |
|
||||
|-------|-------------|------------|--------------|--------------|
|
||||
| Normal idle chrome | `normal-color` | `normal-fill-alpha` | `normal-border-width` | `normal-border-alpha` |
|
||||
| Hover / keyboard cursor | `hover-cursor-color` | `hover-cursor-fill-alpha` | `hover-cursor-border-width` | `hover-cursor-border-alpha` |
|
||||
| Persistent selected/current | `selected-color` | `selected-fill-alpha` | `selected-border-width` | `selected-border-alpha` |
|
||||
| Qt activeFocus | `focus-color` | `focus-fill-alpha` | `focus-border-width` | `focus-border-alpha` |
|
||||
|
||||
Border widths are the theme-level on/off switches for state borders; set
|
||||
a width to `0` to keep the fill while removing that state border. The
|
||||
default theme keeps selected borders off globally (`selected-border-width
|
||||
= 0`); explicitly bordered controls can still keep their normal border
|
||||
when selected.
|
||||
|
||||
```toml
|
||||
[style]
|
||||
# Accent-tinted cursor/focus, foreground-tinted selected state.
|
||||
hover-cursor-color = "accent"
|
||||
focus-color = "hover-cursor"
|
||||
selected-color = "foreground"
|
||||
|
||||
# Keep selected fills but remove selected-state borders.
|
||||
selected-border-width = 0
|
||||
```
|
||||
|
||||
Momentary fills use `pressed-fill-alpha` for button press feedback and
|
||||
`selection-fill-alpha` for text selection. Themes may also provide
|
||||
`pressed-color` or `selection-color`; they fall back to hover-cursor and
|
||||
foreground respectively.
|
||||
|
||||
### Spacing
|
||||
|
||||
`[spacing] scale` multiplies the shell's shared margins, gaps, and
|
||||
padding. The default is `1.0`; values above `1.0` create more breathing
|
||||
room while values below `1.0` make controls denser.
|
||||
|
||||
```toml
|
||||
[spacing]
|
||||
scale = 1.15 # roomier panels and controls
|
||||
```
|
||||
|
||||
QML components should prefer semantic tokens where possible:
|
||||
|
||||
| Token | Default use |
|
||||
|-------|-------------|
|
||||
| `Style.spacing.controlPaddingX` / `controlPaddingY` | Button and tooltip padding |
|
||||
| `Style.spacing.inputPaddingY` | Text-field vertical padding |
|
||||
| `Style.spacing.controlHeight` / `popupRowHeight` | Dropdown and number-field row heights |
|
||||
| `Style.spacing.dropdownWidth` / `searchableDropdownWidth` / `numberFieldWidth` | Default field widths |
|
||||
| `Style.spacing.searchablePopupMinHeight` | Minimum searchable dropdown popup height |
|
||||
| `Style.spacing.controlGap` | Gap between icon and label inside controls |
|
||||
| `Style.spacing.labelGap` | Label-to-control and compact list gaps |
|
||||
| `Style.spacing.rowGap` / `rowPaddingX` | Form rows and list row content |
|
||||
| `Style.spacing.panelGap` / `panelPadding` | Panel section spacing and interior padding |
|
||||
| `Style.spacing.popupPadding` | Popout interior padding |
|
||||
|
||||
Popout placement deliberately follows Hyprland's `general:gaps_out`
|
||||
(`Style.gapsOut`) so panels align with tiled windows. Use a theme's
|
||||
`hyprland.lua` to change that outer alignment; `[spacing]` controls the
|
||||
interior breathing room.
|
||||
|
||||
For one-off proportional constants, use `Style.space(px)` to preserve the
|
||||
old default at scale `1.0` while still responding to the theme scale. Use
|
||||
`Style.spaceReal(px)` only for fractional geometry that should not be
|
||||
rounded, such as bar widget text margins. Themes can override any semantic
|
||||
token directly in `[spacing]`, e.g.:
|
||||
|
||||
```toml
|
||||
[spacing]
|
||||
scale = 1.0
|
||||
panel-padding = 22
|
||||
row-gap = 10
|
||||
```
|
||||
|
||||
### Typography
|
||||
|
||||
`[font] base-size` is the rem root for the scale. Every
|
||||
@@ -157,8 +244,8 @@ Recognized override keys: `base-size`, `caption`, `body-small`,
|
||||
`body`, `subtitle`, `title`, `heading`, `display`, `display-large`,
|
||||
`icon-small`, `icon`, `icon-large`.
|
||||
|
||||
`base-size` is clamped to **11..13** because row heights and the bar
|
||||
cross-axis size are fixed; per-token overrides aren't clamped. The
|
||||
`base-size` is clamped to **11..13** because some row heights and the bar
|
||||
cross-axis size remain fixed; per-token overrides aren't clamped. The
|
||||
shell font family is the fontconfig `monospace` alias — themes don't
|
||||
set it, the user does via `omarchy font set <name>`.
|
||||
|
||||
|
||||
+335
-60
@@ -5,8 +5,9 @@ import Quickshell.Io
|
||||
|
||||
// Shared structural style tokens for the shell. Color is the palette
|
||||
// singleton; Style holds everything else themes can influence — corner
|
||||
// rounding, focus affordances, typography scale, and bar dimensions —
|
||||
// so panels and qs.Ui components have a single source of truth.
|
||||
// rounding, state affordances, spacing, typography scale, and bar
|
||||
// dimensions — so panels and qs.Ui components have a single source of
|
||||
// truth.
|
||||
//
|
||||
// `cornerRadius` mirrors Hyprland's `decoration:rounding`. Themes ship
|
||||
// their own rounding via theme/hyprland.lua; the user toggle via
|
||||
@@ -15,58 +16,268 @@ import Quickshell.Io
|
||||
// up the change here by re-running `hyprctl getoption` whenever either
|
||||
// of those input files changes.
|
||||
//
|
||||
// Typography and bar size come from `theme/shell.toml`. `[font] base-size`
|
||||
// is the rem root; every `Style.font.<token>` derives from it via the
|
||||
// scale multipliers below unless the theme pins that specific token.
|
||||
// `[bar] size-horizontal` / `size-vertical` set the cross-axis dimension
|
||||
// for top/bottom and left/right bars respectively.
|
||||
// Typography, spacing, and bar size come from `theme/shell.toml`.
|
||||
// `[font] base-size` is the rem root; every `Style.font.<token>` derives
|
||||
// from it via the scale multipliers below unless the theme pins that
|
||||
// specific token. `[spacing] scale` multiplies shared margins, gaps, and
|
||||
// padding while preserving each component's proportions. `[bar]
|
||||
// size-horizontal` / `size-vertical` set the cross-axis dimension for
|
||||
// top/bottom and left/right bars respectively.
|
||||
QtObject {
|
||||
id: root
|
||||
|
||||
property int cornerRadius: 0
|
||||
property int gapsOut: 10
|
||||
|
||||
// ---------------------------------------------------------- state alphas
|
||||
// ---------------------------------------------------------- state tokens
|
||||
//
|
||||
// Foreground-tinted fills + border alphas used by every interactive
|
||||
// surface in the kit. Themes can override these via [style] in
|
||||
// shell.toml; otherwise the kit defaults below apply.
|
||||
// Shared interactive-state tokens for every reusable surface in the kit.
|
||||
// The vocabulary is intentionally small:
|
||||
// normal — idle control chrome
|
||||
// hover-cursor — mouse hover OR panel keyboard cursor (`hasCursor`)
|
||||
// selected — persistent chosen/current state
|
||||
// focus — actual Qt activeFocus, defaulting to hover-cursor
|
||||
//
|
||||
// Each state has a color token plus fill/border alphas. Color tokens may
|
||||
// be palette roles (`foreground`, `accent`, `urgent`, `background`) or
|
||||
// hex colors. Border widths are the on/off switch themes can use: set a
|
||||
// state width to 0 to remove that state border everywhere. Legacy
|
||||
// `border-width`, `idle-border-alpha`, `hover-*`, and `hot-fill-alpha`
|
||||
// remain supported aliases for existing theme shell.toml files.
|
||||
property var styleOverrides: ({})
|
||||
|
||||
function styleNum(key, fallback) {
|
||||
function keyList(keys) {
|
||||
return (typeof keys === "string") ? [keys] : keys
|
||||
}
|
||||
|
||||
function styleRawNum(key) {
|
||||
var v = styleOverrides[key]
|
||||
var n = Number(v)
|
||||
return isFinite(n) ? n : fallback
|
||||
return isFinite(n) ? n : null
|
||||
}
|
||||
|
||||
readonly property int borderWidth: Math.max(0, Math.round(styleNum("border-width", 1)))
|
||||
readonly property int focusBorderWidth: Math.max(1, Math.round(styleNum("focus-border-width", 3)))
|
||||
|
||||
readonly property real idleBorderAlpha: styleNum("idle-border-alpha", 0.4)
|
||||
readonly property real hotFillAlpha: styleNum("hot-fill-alpha", 0.08)
|
||||
readonly property real selectedFillAlpha: styleNum("selected-fill-alpha", 0.18)
|
||||
readonly property real pressedFillAlpha: styleNum("pressed-fill-alpha", 0.22)
|
||||
readonly property real focusFillAlpha: styleNum("focus-fill-alpha", 0.22)
|
||||
|
||||
function alphaForeground(a) {
|
||||
return Qt.rgba(Color.foreground.r, Color.foreground.g, Color.foreground.b, a)
|
||||
function styleRawNumAny(keys) {
|
||||
var list = keyList(keys)
|
||||
for (var i = 0; i < list.length; i++) {
|
||||
var n = styleRawNum(list[i])
|
||||
if (n !== null) return n
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
function alphaAccent(a) {
|
||||
return Qt.rgba(Color.accent.r, Color.accent.g, Color.accent.b, a)
|
||||
function styleNum(keys, fallback) {
|
||||
var n = styleRawNumAny(keys)
|
||||
return n === null ? fallback : n
|
||||
}
|
||||
|
||||
// Focus affordances. Deliberately distinct from "selected" (which uses an
|
||||
// accent fill) so the keyboard cursor never reads as the chosen value.
|
||||
readonly property color focusBorderColor: Color.accent
|
||||
readonly property color focusFillColor: alphaAccent(focusFillAlpha)
|
||||
function clampAlpha(value) {
|
||||
var n = Number(value)
|
||||
if (!isFinite(n)) return 0
|
||||
return Math.max(0, Math.min(1, n))
|
||||
}
|
||||
|
||||
// Convenience fills used by panel rows and pills. Hot is hover/keyboard
|
||||
// cursor; selected/active is the persistent chosen/current state.
|
||||
readonly property color hotFill: alphaForeground(hotFillAlpha)
|
||||
readonly property color selectedFill: alphaForeground(selectedFillAlpha)
|
||||
readonly property color pressedFill: alphaForeground(pressedFillAlpha)
|
||||
readonly property color idleBorderColor: alphaForeground(idleBorderAlpha)
|
||||
readonly property color selectedAccentFill: alphaAccent(selectedFillAlpha)
|
||||
function styleAlpha(keys, fallback) {
|
||||
return clampAlpha(styleNum(keys, fallback))
|
||||
}
|
||||
|
||||
function styleString(keys, fallback) {
|
||||
var list = keyList(keys)
|
||||
for (var i = 0; i < list.length; i++) {
|
||||
var v = styleOverrides[list[i]]
|
||||
if (typeof v !== "string") continue
|
||||
v = v.replace(/^\s+|\s+$/g, "")
|
||||
if (v.length > 0) return v
|
||||
}
|
||||
return fallback
|
||||
}
|
||||
|
||||
readonly property string normalColorToken: styleString("normal-color", "foreground")
|
||||
readonly property string hoverColorToken: styleString(["hover-cursor-color", "hover-color"], "foreground")
|
||||
readonly property string selectedColorToken: styleString("selected-color", "foreground")
|
||||
readonly property string pressedColorToken: styleString("pressed-color", hoverColorToken)
|
||||
readonly property string focusColorToken: styleString("focus-color", hoverColorToken)
|
||||
readonly property string selectionColorToken: styleString("selection-color", "foreground")
|
||||
|
||||
readonly property int normalBorderWidth: Math.max(0, Math.round(styleNum(["normal-border-width", "border-width"], 1)))
|
||||
readonly property int hoverBorderWidth: Math.max(0, Math.round(styleNum(["hover-cursor-border-width", "hover-border-width"], normalBorderWidth)))
|
||||
readonly property int selectedBorderWidth: Math.max(0, Math.round(styleNum("selected-border-width", 0)))
|
||||
readonly property int focusBorderWidth: Math.max(0, Math.round(styleNum("focus-border-width", hoverBorderWidth)))
|
||||
|
||||
// Back-compat names used by older components / third-party plugins.
|
||||
readonly property int borderWidth: normalBorderWidth
|
||||
readonly property int hoverCursorBorderWidth: hoverBorderWidth
|
||||
|
||||
readonly property real normalFillAlpha: styleAlpha("normal-fill-alpha", 0.04)
|
||||
readonly property real hoverFillAlpha: styleAlpha(["hover-cursor-fill-alpha", "hover-fill-alpha", "hot-fill-alpha"], 0.08)
|
||||
readonly property real selectedFillAlpha: styleAlpha("selected-fill-alpha", 0.18)
|
||||
readonly property real pressedFillAlpha: styleAlpha("pressed-fill-alpha", 0.22)
|
||||
readonly property real focusFillAlpha: styleAlpha("focus-fill-alpha", hoverFillAlpha)
|
||||
readonly property real selectionFillAlpha: styleAlpha("selection-fill-alpha", 0.35)
|
||||
|
||||
readonly property real normalBorderAlpha: styleAlpha(["normal-border-alpha", "idle-border-alpha"], 0.4)
|
||||
readonly property real hoverBorderAlpha: styleAlpha(["hover-cursor-border-alpha", "hover-border-alpha"], 1.0)
|
||||
readonly property real selectedBorderAlpha: styleAlpha("selected-border-alpha", 1.0)
|
||||
readonly property real focusBorderAlpha: styleAlpha("focus-border-alpha", hoverBorderAlpha)
|
||||
|
||||
// Back-compat names used by older components / third-party plugins.
|
||||
readonly property real idleBorderAlpha: normalBorderAlpha
|
||||
readonly property real hotFillAlpha: hoverFillAlpha
|
||||
readonly property real hoverCursorFillAlpha: hoverFillAlpha
|
||||
readonly property real hoverCursorBorderAlpha: hoverBorderAlpha
|
||||
|
||||
function alpha(c, opacity) {
|
||||
var a = clampAlpha(opacity)
|
||||
if (!c) return Qt.rgba(0, 0, 0, a)
|
||||
return Qt.rgba(c.r, c.g, c.b, a)
|
||||
}
|
||||
|
||||
function colorFromHex(value, fallback) {
|
||||
var s = String(value || "").replace(/^\s+|\s+$/g, "")
|
||||
var shortHex = s.match(/^#([0-9A-Fa-f]{3})$/)
|
||||
if (shortHex) {
|
||||
var sh = shortHex[1]
|
||||
return Qt.rgba(
|
||||
parseInt(sh.charAt(0) + sh.charAt(0), 16) / 255,
|
||||
parseInt(sh.charAt(1) + sh.charAt(1), 16) / 255,
|
||||
parseInt(sh.charAt(2) + sh.charAt(2), 16) / 255,
|
||||
1)
|
||||
}
|
||||
var hex = s.match(/^#([0-9A-Fa-f]{6})([0-9A-Fa-f]{2})?$/)
|
||||
if (!hex) return fallback
|
||||
var h = hex[1]
|
||||
return Qt.rgba(
|
||||
parseInt(h.substr(0, 2), 16) / 255,
|
||||
parseInt(h.substr(2, 2), 16) / 255,
|
||||
parseInt(h.substr(4, 2), 16) / 255,
|
||||
hex[2] ? parseInt(hex[2], 16) / 255 : 1)
|
||||
}
|
||||
|
||||
function resolveStateColor(token, foreground, accent, urgent, fallback) {
|
||||
var fb = fallback || foreground || Color.foreground
|
||||
var s = String(token || "").replace(/^\s+|\s+$/g, "")
|
||||
var role = s.toLowerCase()
|
||||
if (role === "foreground" || role === "text") return foreground || Color.foreground
|
||||
if (role === "accent") return accent || Color.accent
|
||||
if (role === "urgent") return urgent || Color.urgent
|
||||
if (role === "background") return Color.background
|
||||
if (role === "transparent") return Qt.rgba(0, 0, 0, 0)
|
||||
return colorFromHex(s, fb)
|
||||
}
|
||||
|
||||
function normalStateColor(foreground, accent, urgent) {
|
||||
return resolveStateColor(normalColorToken, foreground, accent, urgent, foreground || Color.foreground)
|
||||
}
|
||||
|
||||
function hoverStateColor(foreground, accent, urgent) {
|
||||
return resolveStateColor(hoverColorToken, foreground, accent, urgent, foreground || Color.foreground)
|
||||
}
|
||||
|
||||
function selectedStateColor(foreground, accent, urgent) {
|
||||
return resolveStateColor(selectedColorToken, foreground, accent, urgent, foreground || Color.foreground)
|
||||
}
|
||||
|
||||
function pressedStateColor(foreground, accent, urgent) {
|
||||
return resolveStateColor(pressedColorToken, foreground, accent, urgent, hoverStateColor(foreground, accent, urgent))
|
||||
}
|
||||
|
||||
function focusStateColor(foreground, accent, urgent) {
|
||||
var role = String(focusColorToken || "").replace(/^\s+|\s+$/g, "").toLowerCase()
|
||||
if (role === "hover" || role === "hover-cursor" || role === "inherit")
|
||||
return hoverStateColor(foreground, accent, urgent)
|
||||
return resolveStateColor(focusColorToken, foreground, accent, urgent, hoverStateColor(foreground, accent, urgent))
|
||||
}
|
||||
|
||||
function selectionStateColor(foreground, accent, urgent) {
|
||||
return resolveStateColor(selectionColorToken, foreground, accent, urgent, foreground || Color.foreground)
|
||||
}
|
||||
|
||||
function normalFillFor(foreground, accent, urgent) { return alpha(normalStateColor(foreground, accent, urgent), normalFillAlpha) }
|
||||
function hoverFillFor(foreground, accent, urgent) { return alpha(hoverStateColor(foreground, accent, urgent), hoverFillAlpha) }
|
||||
function selectedFillFor(foreground, accent, urgent) { return alpha(selectedStateColor(foreground, accent, urgent), selectedFillAlpha) }
|
||||
function pressedFillFor(foreground, accent, urgent) { return alpha(pressedStateColor(foreground, accent, urgent), pressedFillAlpha) }
|
||||
function focusFillFor(foreground, accent, urgent) { return alpha(focusStateColor(foreground, accent, urgent), focusFillAlpha) }
|
||||
function selectionFillFor(foreground, accent, urgent) { return alpha(selectionStateColor(foreground, accent, urgent), selectionFillAlpha) }
|
||||
|
||||
function normalBorderFor(foreground, accent, urgent) { return alpha(normalStateColor(foreground, accent, urgent), normalBorderAlpha) }
|
||||
function hoverBorderFor(foreground, accent, urgent) { return alpha(hoverStateColor(foreground, accent, urgent), hoverBorderAlpha) }
|
||||
function selectedBorderFor(foreground, accent, urgent) { return alpha(selectedStateColor(foreground, accent, urgent), selectedBorderAlpha) }
|
||||
function focusBorderFor(foreground, accent, urgent) { return alpha(focusStateColor(foreground, accent, urgent), focusBorderAlpha) }
|
||||
|
||||
// Convenience colors used by panel rows and pills. `hot*` remains as a
|
||||
// compatibility alias for the hover/cursor state.
|
||||
readonly property color normalFill: normalFillFor(Color.foreground, Color.accent, Color.urgent)
|
||||
readonly property color hoverFill: hoverFillFor(Color.foreground, Color.accent, Color.urgent)
|
||||
readonly property color hotFill: hoverFill
|
||||
readonly property color selectedFill: selectedFillFor(Color.foreground, Color.accent, Color.urgent)
|
||||
readonly property color pressedFill: pressedFillFor(Color.foreground, Color.accent, Color.urgent)
|
||||
readonly property color focusFillColor: focusFillFor(Color.foreground, Color.accent, Color.urgent)
|
||||
readonly property color normalBorderColor: normalBorderFor(Color.foreground, Color.accent, Color.urgent)
|
||||
readonly property color idleBorderColor: normalBorderColor
|
||||
readonly property color hoverBorderColor: hoverBorderFor(Color.foreground, Color.accent, Color.urgent)
|
||||
readonly property color selectedBorderColor: selectedBorderFor(Color.foreground, Color.accent, Color.urgent)
|
||||
readonly property color focusBorderColor: focusBorderFor(Color.foreground, Color.accent, Color.urgent)
|
||||
readonly property color selectedAccentFill: alpha(Color.accent, selectedFillAlpha)
|
||||
readonly property color selectionFill: selectionFillFor(Color.foreground, Color.accent, Color.urgent)
|
||||
|
||||
// ---------------------------------------------------------- spacing
|
||||
//
|
||||
// The spacing scale is the shell equivalent of rem for margins, gaps,
|
||||
// and padding. Components keep their existing proportions by asking for
|
||||
// the old pixel value through `Style.space(px)` (or `spaceReal(px)` for
|
||||
// fractional geometry); themes can make the shell denser or roomier with
|
||||
// a single `[spacing] scale` value.
|
||||
property real spacingScale: 1.0
|
||||
property var spacingOverrides: ({})
|
||||
|
||||
function spaceReal(px) {
|
||||
var n = Number(px)
|
||||
if (!isFinite(n) || n <= 0) return 0
|
||||
return n * spacingScale
|
||||
}
|
||||
|
||||
function space(px) {
|
||||
var n = spaceReal(px)
|
||||
if (n <= 0) return 0
|
||||
return Math.max(1, Math.round(n))
|
||||
}
|
||||
|
||||
function spacingToken(key, fallback) {
|
||||
var v = spacingOverrides[key]
|
||||
var n = Number(v)
|
||||
return (isFinite(n) && n >= 0) ? Math.round(n) : space(fallback)
|
||||
}
|
||||
|
||||
readonly property QtObject spacing: QtObject {
|
||||
readonly property real scale: root.spacingScale
|
||||
|
||||
readonly property int hairline: root.space(1)
|
||||
readonly property int xxs: root.spacingToken("xxs", 2)
|
||||
readonly property int xs: root.spacingToken("xs", 3)
|
||||
readonly property int sm: root.spacingToken("sm", 4)
|
||||
readonly property int md: root.spacingToken("md", 6)
|
||||
readonly property int lg: root.spacingToken("lg", 8)
|
||||
readonly property int xl: root.spacingToken("xl", 10)
|
||||
readonly property int xxl: root.spacingToken("xxl", 12)
|
||||
readonly property int xxxl: root.spacingToken("xxxl", 14)
|
||||
readonly property int huge: root.spacingToken("huge", 18)
|
||||
|
||||
readonly property int controlGap: root.spacingToken("control-gap", 8)
|
||||
readonly property int controlPaddingX: root.spacingToken("control-padding-x", 10)
|
||||
readonly property int controlPaddingY: root.spacingToken("control-padding-y", 6)
|
||||
readonly property int inputPaddingY: root.spacingToken("input-padding-y", 7)
|
||||
readonly property int controlHeight: root.spacingToken("control-height", 28)
|
||||
readonly property int popupRowHeight: root.spacingToken("popup-row-height", 28)
|
||||
readonly property int dropdownWidth: root.spacingToken("dropdown-width", 240)
|
||||
readonly property int searchableDropdownWidth: root.spacingToken("searchable-dropdown-width", 260)
|
||||
readonly property int numberFieldWidth: root.spacingToken("number-field-width", 120)
|
||||
readonly property int searchablePopupMinHeight: root.spacingToken("searchable-popup-min-height", 220)
|
||||
readonly property int rowGap: root.spacingToken("row-gap", 8)
|
||||
readonly property int rowPaddingX: root.spacingToken("row-padding-x", 12)
|
||||
readonly property int labelGap: root.spacingToken("label-gap", 4)
|
||||
readonly property int panelGap: root.spacingToken("panel-gap", 14)
|
||||
readonly property int panelPadding: root.spacingToken("panel-padding", 18)
|
||||
readonly property int popupPadding: root.spacingToken("popup-padding", 14)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------- typography
|
||||
//
|
||||
@@ -82,13 +293,13 @@ QtObject {
|
||||
// read `resolvedFontFamily` when you want to *display* what's drawing.
|
||||
property string resolvedFontFamily: "monospace"
|
||||
|
||||
// Clamped 11..13 by loadShell — bar height and row heights are fixed
|
||||
// until we ship matching spacing tokens, so unbounded growth clips.
|
||||
// Clamped 11..13 by loadShell — some row heights remain fixed, so
|
||||
// unbounded type growth can still clip even with scalable spacing.
|
||||
property int fontBaseSize: 12
|
||||
|
||||
// Parsed maps populated by loadShell. Keep them as plain dicts so
|
||||
// reassigning the whole property fires reactive bindings. styleOverrides
|
||||
// is declared up top next to the helpers that consume it.
|
||||
// and spacingOverrides are declared near the helpers that consume them.
|
||||
property var fontOverrides: ({})
|
||||
property var barOverrides: ({})
|
||||
|
||||
@@ -134,6 +345,28 @@ QtObject {
|
||||
|
||||
function refresh() {
|
||||
hyprctlProc.running = true
|
||||
gapsOutProc.running = true
|
||||
}
|
||||
|
||||
property bool themeReloadSuspended: false
|
||||
|
||||
function suspendThemeReloads() {
|
||||
themeReloadSuspended = true
|
||||
}
|
||||
|
||||
function resumeThemeReloads() {
|
||||
themeReloadSuspended = false
|
||||
}
|
||||
|
||||
function scheduleRefresh() {
|
||||
if (themeReloadSuspended) return
|
||||
refreshTimer.restart()
|
||||
}
|
||||
|
||||
function reloadTheme() {
|
||||
if (themeReloadSuspended) return
|
||||
shellTomlFile.reload()
|
||||
scheduleRefresh()
|
||||
}
|
||||
|
||||
function applyRoundingJson(raw) {
|
||||
@@ -146,15 +379,30 @@ QtObject {
|
||||
}
|
||||
}
|
||||
|
||||
// Parse [font] base-size + per-token overrides and [bar] size-* keys
|
||||
// out of shell.toml. Color.qml owns the quoted-string side of the same
|
||||
// file; we handle the unquoted numeric tokens for [font], [bar] sizes,
|
||||
// and [style] (alphas + border widths).
|
||||
function applyGapsOutJson(raw) {
|
||||
try {
|
||||
var json = JSON.parse(raw || "{}")
|
||||
var css = String(json.css || "")
|
||||
var parts = css.match(/-?\d+(?:\.\d+)?/g) || []
|
||||
var n = parts.length > 0 ? Number(parts[0]) : Number(json.int)
|
||||
if (isFinite(n) && n >= 0) gapsOut = Math.round(n)
|
||||
} catch (e) {
|
||||
// hyprctl missing / Hyprland not running — leave the previous value.
|
||||
}
|
||||
}
|
||||
|
||||
// Parse [font] base-size + per-token overrides, [bar] size-* keys,
|
||||
// [style] state colors / alphas / border widths, and [spacing] scale +
|
||||
// token overrides out of shell.toml. Color.qml owns the quoted-string
|
||||
// side of the surface color sections; Style owns quoted strings only
|
||||
// inside [style].
|
||||
function loadShell(raw) {
|
||||
var fontOut = {}
|
||||
var barOut = {}
|
||||
var styleOut = {}
|
||||
var spacingOut = {}
|
||||
var nextBase = 12
|
||||
var nextSpacingScale = 1.0
|
||||
var text = String(raw || "")
|
||||
if (text) {
|
||||
var lines = text.split("\n")
|
||||
@@ -164,19 +412,28 @@ QtObject {
|
||||
if (!line || line.charAt(0) === "#") continue
|
||||
var sectionMatch = line.match(/^\[([A-Za-z0-9_-]+)\]\s*(#.*)?$/)
|
||||
if (sectionMatch) { section = sectionMatch[1]; continue }
|
||||
// Accept ints OR floats (alphas are 0..1).
|
||||
var kv = line.match(/^([A-Za-z0-9_-]+)\s*=\s*(-?\d+(?:\.\d+)?)\s*(#.*)?$/)
|
||||
// Accept ints/floats for numeric tokens and quoted/bare words for
|
||||
// [style] color roles / inheritance sentinels (e.g. "foreground",
|
||||
// "accent", "hover-cursor", "#c0caf5").
|
||||
var numKv = line.match(/^([A-Za-z0-9_-]+)\s*=\s*(-?\d+(?:\.\d+)?)\s*(#.*)?$/)
|
||||
var stringKv = line.match(/^([A-Za-z0-9_-]+)\s*=\s*["']([^"']+)["']\s*(#.*)?$/)
|
||||
var bareKv = line.match(/^([A-Za-z0-9_-]+)\s*=\s*([A-Za-z][A-Za-z0-9_-]*)\s*(#.*)?$/)
|
||||
var kv = numKv || stringKv || bareKv
|
||||
if (!kv) continue
|
||||
var key = kv[1]
|
||||
var raw = kv[2]
|
||||
if (section === "font") {
|
||||
var ival = parseInt(raw, 10)
|
||||
var rawValue = kv[2]
|
||||
if (section === "font" && numKv) {
|
||||
var ival = parseInt(rawValue, 10)
|
||||
if (key === "base-size") nextBase = ival
|
||||
else fontOut[key] = ival
|
||||
} else if (section === "bar" && (key === "size-horizontal" || key === "size-vertical")) {
|
||||
barOut[key] = parseInt(raw, 10)
|
||||
} else if (section === "bar" && numKv && (key === "size-horizontal" || key === "size-vertical")) {
|
||||
barOut[key] = parseInt(rawValue, 10)
|
||||
} else if (section === "spacing" && numKv) {
|
||||
var fval = parseFloat(rawValue)
|
||||
if (key === "scale") nextSpacingScale = fval
|
||||
else spacingOut[key] = fval
|
||||
} else if (section === "style") {
|
||||
styleOut[key] = parseFloat(raw)
|
||||
styleOut[key] = numKv ? parseFloat(rawValue) : rawValue
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -184,9 +441,12 @@ QtObject {
|
||||
// that wants display-large = 64 should be allowed to ship it.
|
||||
if (nextBase < 11) nextBase = 11
|
||||
if (nextBase > 13) nextBase = 13
|
||||
if (!isFinite(nextSpacingScale) || nextSpacingScale < 0) nextSpacingScale = 1.0
|
||||
spacingScale = nextSpacingScale
|
||||
fontBaseSize = nextBase
|
||||
fontOverrides = fontOut
|
||||
barOverrides = barOut
|
||||
spacingOverrides = spacingOut
|
||||
styleOverrides = styleOut
|
||||
}
|
||||
|
||||
@@ -199,6 +459,15 @@ QtObject {
|
||||
}
|
||||
}
|
||||
|
||||
property Process gapsOutProc: Process {
|
||||
id: gapsOutProc
|
||||
command: ["hyprctl", "-j", "getoption", "general:gaps_out"]
|
||||
stdout: StdioCollector {
|
||||
waitForEnd: true
|
||||
onStreamFinished: root.applyGapsOutJson(text)
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve the fontconfig alias to a concrete family name. `omarchy font
|
||||
// set <name>` rewrites ~/.config/fontconfig/fonts.conf and restarts the
|
||||
// shell, but rerun on file change anyway so manual edits propagate too.
|
||||
@@ -247,15 +516,12 @@ QtObject {
|
||||
path: Quickshell.env("HOME") + "/.config/omarchy/current/theme.name"
|
||||
watchChanges: true
|
||||
printErrors: false
|
||||
onFileChanged: {
|
||||
refreshTimer.restart()
|
||||
shellTomlFile.reload()
|
||||
}
|
||||
onFileChanged: root.reloadTheme()
|
||||
}
|
||||
|
||||
// `omarchy style corners <round|sharp>` creates or removes this flag file.
|
||||
// Hyprland reloads its config when sourced files change, then hyprctl
|
||||
// reflects the new effective rounding value.
|
||||
// `omarchy style corners <round|sharp>` and `omarchy toggle window-gaps`
|
||||
// create/remove these flag files. Hyprland reloads its config when sourced
|
||||
// files change, then hyprctl reflects the new effective values.
|
||||
property FileView roundedCornersToggle: FileView {
|
||||
path: Quickshell.env("HOME") + "/.local/state/omarchy/toggles/hypr/rounded-corners.lua"
|
||||
watchChanges: true
|
||||
@@ -265,6 +531,15 @@ QtObject {
|
||||
onLoadFailed: refreshTimer.restart()
|
||||
}
|
||||
|
||||
property FileView windowNoGapsToggle: FileView {
|
||||
path: Quickshell.env("HOME") + "/.local/state/omarchy/toggles/hypr/window-no-gaps.lua"
|
||||
watchChanges: true
|
||||
printErrors: false
|
||||
onFileChanged: refreshTimer.restart()
|
||||
onLoaded: refreshTimer.restart()
|
||||
onLoadFailed: refreshTimer.restart()
|
||||
}
|
||||
|
||||
property FileView shellTomlFile: FileView {
|
||||
id: shellTomlFile
|
||||
path: Quickshell.env("HOME") + "/.config/omarchy/current/theme/shell.toml"
|
||||
@@ -272,7 +547,7 @@ QtObject {
|
||||
printErrors: false
|
||||
onLoaded: root.loadShell(text())
|
||||
onLoadFailed: root.loadShell("")
|
||||
onFileChanged: reload()
|
||||
onFileChanged: root.reloadTheme()
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
|
||||
+36
-36
@@ -6,11 +6,11 @@ import qs.Commons
|
||||
// States compose independently and are applied in priority order:
|
||||
//
|
||||
// pressed (mouse down) pressed fill
|
||||
// activeFocus (Tab focus) accent ring + accent fill
|
||||
// selected accent fill + accent border
|
||||
// active foreground tint fill (highlighted)
|
||||
// hasCursor || hover hot fill
|
||||
// idle transparent or 1px border if `bordered`
|
||||
// activeFocus (Tab focus) focus fill + focus border token
|
||||
// hasCursor || hover hover-cursor fill (+ border if `bordered`)
|
||||
// selected selected fill + optional selected border
|
||||
// active selected fill
|
||||
// idle transparent or normal border if `bordered`
|
||||
//
|
||||
// All fills/borders come from `qs.Commons.Style` tokens, so themes
|
||||
// control the look via [style] in shell.toml.
|
||||
@@ -41,8 +41,8 @@ Rectangle {
|
||||
property real fontSize: Style.font.body
|
||||
property real iconSize: Style.font.icon
|
||||
property real iconRotation: 0
|
||||
property real horizontalPadding: 10
|
||||
property real verticalPadding: 6
|
||||
property real horizontalPadding: Style.spacing.controlPaddingX
|
||||
property real verticalPadding: Style.spacing.controlPaddingY
|
||||
property bool leftAlign: false
|
||||
|
||||
// Tooltip palette. Auto-rendered if tooltipText is set.
|
||||
@@ -64,32 +64,32 @@ Rectangle {
|
||||
|
||||
readonly property bool hot: mouseArea.containsMouse || hasCursor
|
||||
readonly property bool _showFocusRing: focusable && activeFocus
|
||||
readonly property color _selectedColor: Style.selectedStateColor(root.foreground, root.accent)
|
||||
|
||||
color: mouseArea.pressed ? Style.pressedFill
|
||||
: _showFocusRing ? Style.focusFillColor
|
||||
: selected ? Style.selectedAccentFill
|
||||
: hot ? Style.hotFill
|
||||
: active ? Style.selectedFill
|
||||
color: mouseArea.pressed ? Style.pressedFillFor(root.foreground, root.accent)
|
||||
: _showFocusRing ? Style.focusFillFor(root.foreground, root.accent)
|
||||
: hot ? Style.hoverFillFor(root.foreground, root.accent)
|
||||
: selected ? Style.selectedFillFor(root.foreground, root.accent)
|
||||
: active ? Style.selectedFillFor(root.foreground, root.accent)
|
||||
: background
|
||||
|
||||
// Border color follows the same precedence as fill: focus ring wins,
|
||||
// then selected, then cursor on bordered (paints accent so the chip
|
||||
// structure clearly reads as "cursor is here"), then plain bordered
|
||||
// (foreground), then nothing.
|
||||
border.color: _showFocusRing ? Style.focusBorderColor
|
||||
: selected ? accent
|
||||
: (bordered && hot) ? Style.focusBorderColor
|
||||
: bordered ? foreground
|
||||
: Style.idleBorderColor
|
||||
// Border follows the same state precedence as fill. Buttons stay
|
||||
// borderless at rest unless `bordered` is set, but hover-cursor/focus
|
||||
// always use the shared cursor border so the keyboard target is visible
|
||||
// and consistent with the rest of the kit. Selected borders are off by
|
||||
// default for plain buttons; explicitly bordered buttons keep their
|
||||
// normal border when selected unless selected-border-width opts in to a
|
||||
// dedicated selected border.
|
||||
border.color: _showFocusRing ? Style.focusBorderFor(root.foreground, root.accent)
|
||||
: hot ? Style.hoverBorderFor(root.foreground, root.accent)
|
||||
: selected ? (Style.selectedBorderWidth > 0 ? Style.selectedBorderFor(root.foreground, root.accent) : Style.normalBorderFor(root.foreground, root.accent))
|
||||
: bordered ? Style.normalBorderFor(root.foreground, root.accent)
|
||||
: "transparent"
|
||||
|
||||
// selected+hot thickens to the focus-ring width so the cursor remains
|
||||
// visible on the chosen option (otherwise selected's accent fill+border
|
||||
// masks any hot fill). bordered+hot also thickens so the chip cursor
|
||||
// reads as a deliberate state change rather than a faint tint.
|
||||
border.width: _showFocusRing ? Style.focusBorderWidth
|
||||
: selected ? (hot ? Style.focusBorderWidth : Math.max(Style.borderWidth, 2))
|
||||
: (bordered && hot) ? Style.focusBorderWidth
|
||||
: bordered ? Style.borderWidth
|
||||
: hot ? Style.hoverBorderWidth
|
||||
: selected ? (Style.selectedBorderWidth > 0 ? Style.selectedBorderWidth : (bordered ? Style.normalBorderWidth : 0))
|
||||
: bordered ? Style.normalBorderWidth
|
||||
: 0
|
||||
|
||||
Behavior on color { ColorAnimation { duration: 120 } }
|
||||
@@ -102,7 +102,7 @@ Rectangle {
|
||||
background: Rectangle {
|
||||
color: root.tooltipBackground
|
||||
border.color: root.tooltipForeground
|
||||
border.width: 1
|
||||
border.width: Math.max(1, Style.normalBorderWidth)
|
||||
radius: 0
|
||||
opacity: 0.97
|
||||
}
|
||||
@@ -111,10 +111,10 @@ Rectangle {
|
||||
color: root.tooltipForeground
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.bodySmall
|
||||
leftPadding: 10
|
||||
rightPadding: 10
|
||||
topPadding: 6
|
||||
bottomPadding: 6
|
||||
leftPadding: Style.spacing.controlPaddingX
|
||||
rightPadding: Style.spacing.controlPaddingX
|
||||
topPadding: Style.spacing.controlPaddingY
|
||||
bottomPadding: Style.spacing.controlPaddingY
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,12 +124,12 @@ Rectangle {
|
||||
anchors.left: root.leftAlign ? parent.left : undefined
|
||||
anchors.leftMargin: root.leftAlign ? root.horizontalPadding : 0
|
||||
anchors.horizontalCenter: root.leftAlign ? undefined : parent.horizontalCenter
|
||||
spacing: 8
|
||||
spacing: Style.spacing.controlGap
|
||||
|
||||
Text {
|
||||
visible: root.iconText !== ""
|
||||
text: root.iconText
|
||||
color: root.selected ? root.accent : root.foreground
|
||||
color: root.selected ? root._selectedColor : root.foreground
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: root.iconSize
|
||||
rotation: root.iconRotation
|
||||
@@ -140,7 +140,7 @@ Rectangle {
|
||||
Text {
|
||||
visible: root.text !== ""
|
||||
text: root.text
|
||||
color: root.selected ? root.accent : root.foreground
|
||||
color: root.selected ? root._selectedColor : root.foreground
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: root.fontSize
|
||||
font.bold: root.selected
|
||||
|
||||
+70
-12
@@ -8,10 +8,18 @@ import qs.Commons
|
||||
// `options` is either a plain string[] (label == value) or an array of
|
||||
// { value, label, icon?, tooltip? } objects. Mixing is fine.
|
||||
//
|
||||
// Panels with their own keyboard cursor model bind `cursorIndex` to the
|
||||
// currently-focused option (-1 = no cursor) and listen on `hovered` to
|
||||
// keep that state synced with the mouse. Forms that don't care about
|
||||
// the panel cursor model can leave both alone.
|
||||
// Keyboard navigation. The group itself is a single Tab stop, not one
|
||||
// stop per chip — so in a form that walks `activeFocusOnTab` items with
|
||||
// Tab / j / k, the cursor enters the group as a unit. Once focused,
|
||||
// h / l / Left / Right walks between chips and Enter / Space activates
|
||||
// the current one. The selected chip is the default landing point so
|
||||
// users see their existing choice on arrival.
|
||||
//
|
||||
// Panel-cursor consumers (the bar widget panels) drive `cursorIndex`
|
||||
// directly and listen on `hovered` to sync the mouse — Tab focus and
|
||||
// `cursorIndex` are independent; either one paints the chip's hot
|
||||
// state, and the bar widget panels never give Tab focus to a
|
||||
// ButtonGroup so they only see the cursorIndex path.
|
||||
Row {
|
||||
id: root
|
||||
|
||||
@@ -22,16 +30,24 @@ Row {
|
||||
property color accent: Color.accent
|
||||
property string fontFamily: Style.font.family
|
||||
property real fontSize: Style.font.body
|
||||
property bool focusable: false
|
||||
property bool focusable: true
|
||||
|
||||
// -1 disables the cursor highlight (the form case). Set from a panel
|
||||
// to drive Button.hasCursor on the matching index.
|
||||
// -1 disables the external cursor highlight (the panel-cursor case).
|
||||
// Driven by a containing panel; the group's own Tab-focus h/l
|
||||
// tracking is internal and lives in _focusedIndex.
|
||||
property int cursorIndex: -1
|
||||
|
||||
// Internal: which chip h / l / Left / Right is currently sitting on
|
||||
// when the group itself has Tab focus. Reset to the selected option
|
||||
// each time focus arrives so the user sees their existing choice.
|
||||
property int _focusedIndex: -1
|
||||
|
||||
signal changed(string value)
|
||||
signal hovered(int index, bool isHovered)
|
||||
|
||||
spacing: 6
|
||||
spacing: Style.spacing.md
|
||||
|
||||
activeFocusOnTab: focusable
|
||||
|
||||
function optionValue(o) {
|
||||
return (o && typeof o === "object") ? String(o.value) : String(o)
|
||||
@@ -46,6 +62,46 @@ Row {
|
||||
return (o && typeof o === "object" && o.tooltip) ? String(o.tooltip) : ""
|
||||
}
|
||||
|
||||
function selectedOptionIndex() {
|
||||
for (var i = 0; i < options.length; i++)
|
||||
if (optionValue(options[i]) === value) return i
|
||||
return -1
|
||||
}
|
||||
|
||||
function activateFocused() {
|
||||
if (_focusedIndex < 0 || _focusedIndex >= options.length) return
|
||||
var v = optionValue(options[_focusedIndex])
|
||||
root.changed(v)
|
||||
}
|
||||
|
||||
onActiveFocusChanged: {
|
||||
if (activeFocus) {
|
||||
var idx = selectedOptionIndex()
|
||||
_focusedIndex = idx < 0 ? 0 : idx
|
||||
} else {
|
||||
_focusedIndex = -1
|
||||
}
|
||||
}
|
||||
|
||||
Keys.priority: Keys.BeforeItem
|
||||
Keys.onPressed: function(event) {
|
||||
if (event.key === Qt.Key_Left || event.key === Qt.Key_H
|
||||
|| event.text === "h") {
|
||||
_focusedIndex = Math.max(0, (_focusedIndex < 0 ? 0 : _focusedIndex) - 1)
|
||||
event.accepted = true
|
||||
} else if (event.key === Qt.Key_Right || event.key === Qt.Key_L
|
||||
|| event.text === "l") {
|
||||
var max = options.length - 1
|
||||
var next = (_focusedIndex < 0 ? 0 : _focusedIndex) + 1
|
||||
_focusedIndex = Math.min(max, next)
|
||||
event.accepted = true
|
||||
} else if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter
|
||||
|| event.key === Qt.Key_Space) {
|
||||
activateFocused()
|
||||
event.accepted = true
|
||||
}
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: root.options
|
||||
|
||||
@@ -56,17 +112,19 @@ Row {
|
||||
iconText: root.optionIcon(modelData)
|
||||
tooltipText: root.optionTooltip(modelData)
|
||||
selected: root.optionValue(modelData) === root.value
|
||||
// Chip lights up when either the external panel cursor lands here
|
||||
// or the group has Tab focus and h/l has walked to this index.
|
||||
hasCursor: root.cursorIndex === index
|
||||
// Every chip carries an idle border so the group reads as a row of
|
||||
// distinct options. selected paints accent; the cursor recolors the
|
||||
// chip's border to accent via Button's bordered+hot path.
|
||||
|| (root.activeFocus && root._focusedIndex === index)
|
||||
// Every chip carries the standard bordered-button chrome so the
|
||||
// group reads as a row of distinct options. selected / hover-cursor /
|
||||
// focus are all painted by Button from Style's shared state tokens.
|
||||
bordered: true
|
||||
foreground: root.foreground
|
||||
background: root.background
|
||||
accent: root.accent
|
||||
fontFamily: root.fontFamily
|
||||
fontSize: root.fontSize
|
||||
focusable: root.focusable
|
||||
onClicked: root.changed(root.optionValue(modelData))
|
||||
onHovered: function(h) { root.hovered(index, h) }
|
||||
}
|
||||
|
||||
+21
-17
@@ -7,35 +7,39 @@ import qs.Commons
|
||||
// `hasCursor` / `current`. That's what guarantees a single highlight on
|
||||
// screen at any time across both keyboard and mouse interaction.
|
||||
//
|
||||
// Two cursor visuals are supported:
|
||||
//
|
||||
// default (outline: false) — paint a tinted fill across the row when
|
||||
// hasCursor is true. Use for narrow text rows (wifi networks, audio
|
||||
// devices, menu items) where fill reads cleanly.
|
||||
//
|
||||
// outline: true — paint an accent border instead of a fill. Use for
|
||||
// wide content rows where a fill would obscure the row's chrome
|
||||
// (slider rows in audio / monitor panels). The fill / currentFill
|
||||
// props are ignored in this mode.
|
||||
// Cursor paint is always the shared hover-cursor fill plus optional
|
||||
// hover-cursor border. `outline` remains as a compatibility flag for
|
||||
// callers that used to request border-only rows, but slider rows still
|
||||
// receive the same hover-cursor background as every other row.
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property bool hasCursor: false
|
||||
property bool current: false
|
||||
property bool outline: false
|
||||
property bool bordered: false
|
||||
|
||||
property color foreground: Color.foreground
|
||||
property color fill: Qt.rgba(foreground.r, foreground.g, foreground.b, 0.08)
|
||||
property color currentFill: Qt.rgba(foreground.r, foreground.g, foreground.b, 0.18)
|
||||
property color accent: Color.accent
|
||||
property color fill: Style.hoverFillFor(foreground, accent)
|
||||
property color currentFill: Style.selectedFillFor(foreground, accent)
|
||||
|
||||
radius: Style.cornerRadius
|
||||
|
||||
color: root.outline
|
||||
? "transparent"
|
||||
: (hasCursor ? fill : (current ? currentFill : "transparent"))
|
||||
color: hasCursor ? fill : (current ? currentFill : "transparent")
|
||||
|
||||
border.color: root.outline && hasCursor ? Style.focusBorderColor : foreground
|
||||
border.width: root.outline && hasCursor ? Style.focusBorderWidth : 0
|
||||
border.color: root.hasCursor
|
||||
? Style.hoverBorderFor(root.foreground, root.accent)
|
||||
: (root.current
|
||||
? Style.selectedBorderFor(root.foreground, root.accent)
|
||||
: (root.bordered
|
||||
? Style.normalBorderFor(root.foreground, root.accent)
|
||||
: "transparent"))
|
||||
border.width: root.hasCursor
|
||||
? Style.hoverBorderWidth
|
||||
: (root.current
|
||||
? Style.selectedBorderWidth
|
||||
: (root.bordered ? Style.normalBorderWidth : 0))
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation { duration: 60 }
|
||||
|
||||
+35
-26
@@ -27,12 +27,12 @@ Item {
|
||||
property color popupBorder: Color.popups.border
|
||||
property color accent: Color.accent
|
||||
property string fontFamily: Style.font.family
|
||||
property int rowHeight: 28
|
||||
property int popupRowHeight: 28
|
||||
property int rowHeight: Style.spacing.controlHeight
|
||||
property int popupRowHeight: Style.spacing.popupRowHeight
|
||||
property bool showLabel: true
|
||||
|
||||
// Panel-cursor flag. When true, the trigger renders the same focus ring
|
||||
// as Tab-focus so a panel's keyboard cursor lands here identically.
|
||||
// Panel-cursor flag. When true, the trigger renders the shared
|
||||
// hover-cursor state. Active Qt focus defaults to the same visuals.
|
||||
// Emits `hovered(bool)` on pointer enter/leave so the panel can keep
|
||||
// its cursor state in sync with the mouse.
|
||||
property bool hasCursor: false
|
||||
@@ -62,12 +62,12 @@ Item {
|
||||
return value
|
||||
}
|
||||
|
||||
implicitWidth: 240
|
||||
implicitHeight: showLabel && label !== "" ? rowHeight + 18 : rowHeight
|
||||
implicitWidth: Style.spacing.dropdownWidth
|
||||
implicitHeight: showLabel && label !== "" ? rowHeight + Style.spacing.huge : rowHeight
|
||||
|
||||
Column {
|
||||
anchors.fill: parent
|
||||
spacing: 4
|
||||
spacing: Style.spacing.labelGap
|
||||
|
||||
Text {
|
||||
visible: root.showLabel && root.label !== ""
|
||||
@@ -84,18 +84,27 @@ Item {
|
||||
height: root.rowHeight
|
||||
radius: Style.cornerRadius
|
||||
|
||||
readonly property bool _focused: trigger.activeFocus || root.hasCursor
|
||||
readonly property bool _focused: trigger.activeFocus
|
||||
readonly property bool _hot: triggerHover.hovered || root.hasCursor
|
||||
|
||||
color: Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b,
|
||||
trigger._focused ? 0.08 : 0.04)
|
||||
color: trigger._focused
|
||||
? Style.focusFillFor(root.foreground, root.accent)
|
||||
: (trigger._hot
|
||||
? Style.hoverFillFor(root.foreground, root.accent)
|
||||
: Style.normalFillFor(root.foreground, root.accent))
|
||||
border.color: trigger._focused
|
||||
? Style.focusBorderColor
|
||||
: Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, 0.4)
|
||||
border.width: trigger._focused ? Style.focusBorderWidth : 1
|
||||
? Style.focusBorderFor(root.foreground, root.accent)
|
||||
: (trigger._hot
|
||||
? Style.hoverBorderFor(root.foreground, root.accent)
|
||||
: Style.normalBorderFor(root.foreground, root.accent))
|
||||
border.width: trigger._focused
|
||||
? Style.focusBorderWidth
|
||||
: (trigger._hot ? Style.hoverBorderWidth : Style.normalBorderWidth)
|
||||
|
||||
activeFocusOnTab: true
|
||||
|
||||
HoverHandler {
|
||||
id: triggerHover
|
||||
onHoveredChanged: root.hovered(hovered)
|
||||
}
|
||||
|
||||
@@ -113,8 +122,8 @@ Item {
|
||||
anchors.left: parent.left
|
||||
anchors.right: chevron.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.leftMargin: 10
|
||||
anchors.rightMargin: 6
|
||||
anchors.leftMargin: Style.spacing.controlPaddingX
|
||||
anchors.rightMargin: Style.spacing.md
|
||||
text: root.currentLabel()
|
||||
color: root.foreground
|
||||
font.family: root.fontFamily
|
||||
@@ -126,7 +135,7 @@ Item {
|
||||
id: chevron
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.rightMargin: 8
|
||||
anchors.rightMargin: Style.spacing.controlGap
|
||||
text: ""
|
||||
color: Qt.darker(root.foreground, 1.2)
|
||||
font.family: root.fontFamily
|
||||
@@ -145,17 +154,17 @@ Item {
|
||||
Popup {
|
||||
id: popup
|
||||
x: 0
|
||||
y: trigger.height + 2
|
||||
y: trigger.height + Style.spacing.xxs
|
||||
width: trigger.width
|
||||
implicitHeight: Math.min(root.options.length * root.popupRowHeight + Math.max(0, root.options.length - 1) * 4 + 2,
|
||||
root.popupRowHeight * 8 + 7 * 4 + 2)
|
||||
padding: 1
|
||||
implicitHeight: Math.min(root.options.length * root.popupRowHeight + Math.max(0, root.options.length - 1) * Style.spacing.labelGap + Style.spacing.xxs,
|
||||
root.popupRowHeight * 8 + 7 * Style.spacing.labelGap + Style.spacing.xxs)
|
||||
padding: Style.spacing.hairline
|
||||
focus: true
|
||||
|
||||
background: Rectangle {
|
||||
color: root.background
|
||||
border.color: root.popupBorder
|
||||
border.width: 1
|
||||
border.width: Style.normalBorderWidth
|
||||
radius: Style.cornerRadius
|
||||
}
|
||||
|
||||
@@ -166,7 +175,7 @@ Item {
|
||||
|
||||
contentItem: ListView {
|
||||
id: optionList
|
||||
spacing: 4
|
||||
spacing: Style.spacing.labelGap
|
||||
|
||||
Keys.priority: Keys.BeforeItem
|
||||
Keys.onPressed: function(event) {
|
||||
@@ -207,17 +216,17 @@ Item {
|
||||
width: optionList.width
|
||||
height: root.popupRowHeight
|
||||
color: index === optionList.currentIndex
|
||||
? Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, 0.14)
|
||||
? Style.hoverFillFor(root.foreground, root.accent)
|
||||
: "transparent"
|
||||
|
||||
Text {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.leftMargin: 10
|
||||
anchors.rightMargin: 10
|
||||
anchors.leftMargin: Style.spacing.controlPaddingX
|
||||
anchors.rightMargin: Style.spacing.controlPaddingX
|
||||
text: root.optionLabel(modelData)
|
||||
color: index === optionList.currentIndex ? root.accent : root.foreground
|
||||
color: index === optionList.currentIndex ? Style.hoverStateColor(root.foreground, root.accent) : root.foreground
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.body
|
||||
elide: Text.ElideRight
|
||||
|
||||
@@ -38,12 +38,12 @@ PanelWindow {
|
||||
required property Item anchorItem
|
||||
required property QtObject bar
|
||||
property var owner: null
|
||||
property int margin: 10
|
||||
property int padding: 14
|
||||
property int margin: Style.gapsOut
|
||||
property int padding: Style.spacing.popupPadding
|
||||
property int contentWidth: 280
|
||||
property int contentHeight: 200
|
||||
property bool open: false
|
||||
property int gap: 10 // distance between bar edge and panel
|
||||
property int gap: Style.gapsOut // distance between bar edge and panel
|
||||
|
||||
default property alias contentItem: contentHolder.children
|
||||
|
||||
@@ -123,6 +123,32 @@ PanelWindow {
|
||||
readonly property real anchorH: anchorItem ? anchorItem.height : 0
|
||||
readonly property real screenW: screen ? screen.width : 0
|
||||
readonly property real screenH: screen ? screen.height : 0
|
||||
readonly property real availableCardWidth: screenW > 0
|
||||
? Math.max(120, screenW - ((barPos === "left" || barPos === "right") ? barW + gap + margin : margin * 2))
|
||||
: 0
|
||||
readonly property real availableCardHeight: screenH > 0
|
||||
? Math.max(120, screenH - ((barPos === "top" || barPos === "bottom") ? barH + gap + margin : margin * 2))
|
||||
: 0
|
||||
|
||||
function fittedContentWidth(width, cap) {
|
||||
var desired = Math.max(1, Number(width) || 1)
|
||||
var maxWidth = root.availableCardWidth > 0 ? root.availableCardWidth : desired
|
||||
if (cap !== undefined && Number(cap) > 0) maxWidth = Math.min(maxWidth, Number(cap))
|
||||
return Math.round(Math.min(desired, maxWidth))
|
||||
}
|
||||
|
||||
function fittedContentHeight(implicitHeight, cap) {
|
||||
var desired = Math.max(root.padding * 2, (Number(implicitHeight) || 0) + root.padding * 2)
|
||||
var maxHeight = root.availableCardHeight > 0 ? root.availableCardHeight : desired
|
||||
if (cap !== undefined && Number(cap) > 0) maxHeight = Math.min(maxHeight, Number(cap))
|
||||
return Math.round(Math.min(desired, maxHeight))
|
||||
}
|
||||
|
||||
function cappedContentHeight(height) {
|
||||
var desired = Math.max(root.padding * 2, Number(height) || root.padding * 2)
|
||||
var maxHeight = root.availableCardHeight > 0 ? root.availableCardHeight : desired
|
||||
return Math.round(Math.min(desired, maxHeight))
|
||||
}
|
||||
|
||||
// Desired top-left of the card in screen coordinates. For the
|
||||
// perpendicular axis (away-from-bar) we anchor to the bar window's edge
|
||||
@@ -192,7 +218,7 @@ PanelWindow {
|
||||
height: root.contentHeight
|
||||
color: Color.popups.background
|
||||
border.color: Color.popups.border
|
||||
border.width: 2
|
||||
border.width: Math.max(1, Style.space(2))
|
||||
radius: Style.cornerRadius
|
||||
opacity: root.open ? 1.0 : 0
|
||||
Behavior on opacity {
|
||||
|
||||
+25
-10
@@ -14,14 +14,15 @@ Column {
|
||||
property color accent: Color.accent
|
||||
property string fontFamily: Style.font.family
|
||||
property real fontSize: Style.font.body
|
||||
property real fieldWidth: 120
|
||||
property real fieldWidth: Style.spacing.numberFieldWidth
|
||||
property bool hasCursor: false
|
||||
property bool _hovered: false
|
||||
property alias field: spin
|
||||
|
||||
signal modified(int value)
|
||||
signal hovered(bool on)
|
||||
|
||||
spacing: 6
|
||||
spacing: Style.spacing.md
|
||||
|
||||
Text {
|
||||
visible: root.label !== ""
|
||||
@@ -34,6 +35,7 @@ Column {
|
||||
QQC.SpinBox {
|
||||
id: spin
|
||||
width: root.fieldWidth
|
||||
implicitHeight: Math.max(Style.spacing.controlHeight, root.fontSize + Style.spacing.controlPaddingY * 2)
|
||||
from: root.from
|
||||
to: root.to
|
||||
stepSize: root.stepSize
|
||||
@@ -45,16 +47,29 @@ Column {
|
||||
onValueModified: root.modified(value)
|
||||
|
||||
background: Rectangle {
|
||||
readonly property bool _hot: spin.activeFocus || (root.hasCursor && !spin.activeFocus)
|
||||
color: Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, _hot ? 0.10 : 0.05)
|
||||
border.color: _hot
|
||||
? Style.focusBorderColor
|
||||
: Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, 0.3)
|
||||
border.width: _hot ? Style.focusBorderWidth : 1
|
||||
readonly property bool _focused: spin.activeFocus
|
||||
readonly property bool _hot: root._hovered || root.hasCursor
|
||||
|
||||
color: _focused
|
||||
? Style.focusFillFor(root.foreground, root.accent)
|
||||
: (_hot
|
||||
? Style.hoverFillFor(root.foreground, root.accent)
|
||||
: Style.normalFillFor(root.foreground, root.accent))
|
||||
border.color: _focused
|
||||
? Style.focusBorderFor(root.foreground, root.accent)
|
||||
: (_hot
|
||||
? Style.hoverBorderFor(root.foreground, root.accent)
|
||||
: Style.normalBorderFor(root.foreground, root.accent))
|
||||
border.width: _focused
|
||||
? Style.focusBorderWidth
|
||||
: (_hot ? Style.hoverBorderWidth : Style.normalBorderWidth)
|
||||
radius: Style.cornerRadius
|
||||
|
||||
HoverHandler {
|
||||
onHoveredChanged: root.hovered(hovered)
|
||||
onHoveredChanged: {
|
||||
root._hovered = hovered
|
||||
root.hovered(hovered)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +77,7 @@ Column {
|
||||
text: spin.displayText
|
||||
font: spin.font
|
||||
color: root.foreground
|
||||
selectionColor: Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, 0.35)
|
||||
selectionColor: Style.selectionFillFor(root.foreground, root.accent)
|
||||
selectedTextColor: root.foreground
|
||||
horizontalAlignment: Qt.AlignHCenter
|
||||
verticalAlignment: Qt.AlignVCenter
|
||||
|
||||
@@ -13,13 +13,13 @@ import qs.Commons
|
||||
// here because action buttons are not cursor targets — the row they live
|
||||
// in is.
|
||||
//
|
||||
// Set `focusable: true` to make the button keyboard-tabbable with an
|
||||
// accent focus ring (Style.focusBorderColor / FillColor / Width). Use this
|
||||
// Set `focusable: true` to make the button keyboard-tabbable with the
|
||||
// shared hover-cursor/focus tokens. Use this
|
||||
// in form contexts (the bar settings widget cards) where Tab walks a list
|
||||
// of controls; leave it false for the right-edge actions on panel rows
|
||||
// where the row's CursorSurface owns the keyboard cursor.
|
||||
//
|
||||
// Set `hasCursor: true` to have the button render the same fill as a
|
||||
// Set `hasCursor: true` to have the button render the same hover state as
|
||||
// mouse hover — so a panel's keyboard cursor lands on it identically.
|
||||
// Use this when a PanelActionButton is itself the cursor target (rather
|
||||
// than living inside a CursorSurface row). Emits `hovered(bool)` on
|
||||
@@ -34,10 +34,11 @@ Rectangle {
|
||||
property color panelBackground: Color.background
|
||||
property string fontFamily: Style.font.family
|
||||
property real fontSize: Style.font.icon
|
||||
property real size: 22
|
||||
property real size: Math.max(Style.space(22), fontSize + Style.spacing.sm * 2)
|
||||
|
||||
property bool focusable: false
|
||||
property bool hasCursor: false
|
||||
property bool bordered: false
|
||||
|
||||
signal clicked()
|
||||
signal hovered(bool isHovered)
|
||||
@@ -55,12 +56,20 @@ Rectangle {
|
||||
readonly property bool _hot: (mouse.containsMouse || root.hasCursor) && root.enabled
|
||||
|
||||
color: _showFocusRing
|
||||
? Style.focusFillColor
|
||||
? Style.focusFillFor(hoverColor, hoverColor)
|
||||
: (_hot
|
||||
? Qt.rgba(hoverColor.r, hoverColor.g, hoverColor.b, 0.20)
|
||||
? Style.hoverFillFor(hoverColor, hoverColor)
|
||||
: "transparent")
|
||||
border.width: _showFocusRing ? Style.focusBorderWidth : 0
|
||||
border.color: _showFocusRing ? Style.focusBorderColor : "transparent"
|
||||
border.width: _showFocusRing
|
||||
? Style.focusBorderWidth
|
||||
: (_hot && bordered
|
||||
? Style.hoverBorderWidth
|
||||
: (bordered ? Style.normalBorderWidth : 0))
|
||||
border.color: _showFocusRing
|
||||
? Style.focusBorderFor(hoverColor, hoverColor)
|
||||
: (_hot && bordered
|
||||
? Style.hoverBorderFor(hoverColor, hoverColor)
|
||||
: Style.normalBorderFor(foreground, Color.accent))
|
||||
|
||||
Behavior on color { ColorAnimation { duration: 60 } }
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import QtQuick
|
||||
import qs.Commons
|
||||
|
||||
Item {
|
||||
id: root
|
||||
@@ -9,11 +10,12 @@ Item {
|
||||
property real maximum: 1
|
||||
property real step: 0.05
|
||||
property bool integer: false
|
||||
property color trackColor: bar ? Qt.rgba(bar.foreground.r, bar.foreground.g, bar.foreground.b, 0.18) : "#333"
|
||||
property color trackColor: bar ? Style.selectedFillFor(bar.foreground, Color.accent) : "#333"
|
||||
property color fillColor: bar ? bar.foreground : "#cacccc"
|
||||
property color knobColor: bar ? bar.foreground : "#cacccc"
|
||||
property bool dragging: false
|
||||
property real trackHeight: 4
|
||||
property real trackHeight: Math.max(4, Math.round(Style.spacing.controlHeight * 0.11))
|
||||
property real knobSize: Math.max(14, Math.round(Style.spacing.controlHeight * 0.38))
|
||||
property real liveValue: value
|
||||
|
||||
onValueChanged: if (!dragging) liveValue = value
|
||||
@@ -21,8 +23,8 @@ Item {
|
||||
signal moved(real value)
|
||||
signal released(real value)
|
||||
|
||||
implicitWidth: 200
|
||||
implicitHeight: 22
|
||||
implicitWidth: Style.space(200)
|
||||
implicitHeight: Math.max(Style.space(22), knobSize + Style.spacing.md)
|
||||
|
||||
readonly property real range: Math.max(0.0001, maximum - minimum)
|
||||
readonly property real progress: Math.max(0, Math.min(1, (liveValue - minimum) / range))
|
||||
@@ -54,12 +56,12 @@ Item {
|
||||
|
||||
Rectangle {
|
||||
id: knob
|
||||
width: 14
|
||||
height: 14
|
||||
radius: 7
|
||||
width: root.knobSize
|
||||
height: root.knobSize
|
||||
radius: root.knobSize / 2
|
||||
color: root.knobColor
|
||||
border.color: root.bar ? root.bar.background : "#101315"
|
||||
border.width: 2
|
||||
border.width: Math.max(1, Style.space(2))
|
||||
anchors.verticalCenter: track.verticalCenter
|
||||
x: Math.max(0, Math.min(track.width - width, track.width * root.progress - width / 2))
|
||||
scale: mouseArea.containsMouse || root.dragging ? 1.15 : 1.0
|
||||
|
||||
@@ -27,8 +27,8 @@ ToolTip {
|
||||
|
||||
background: Rectangle {
|
||||
color: root.panelBackground
|
||||
border.color: root.panelForeground
|
||||
border.width: 1
|
||||
border.color: Style.normalBorderFor(root.panelForeground, Color.accent)
|
||||
border.width: Style.normalBorderWidth
|
||||
radius: 0
|
||||
opacity: 0.97
|
||||
}
|
||||
@@ -38,9 +38,9 @@ ToolTip {
|
||||
color: root.panelForeground
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: root.fontSize
|
||||
leftPadding: 10
|
||||
rightPadding: 10
|
||||
topPadding: 6
|
||||
bottomPadding: 6
|
||||
leftPadding: Style.spacing.controlPaddingX
|
||||
rightPadding: Style.spacing.controlPaddingX
|
||||
topPadding: Style.spacing.controlPaddingY
|
||||
bottomPadding: Style.spacing.controlPaddingY
|
||||
}
|
||||
}
|
||||
|
||||
+34
-3
@@ -9,8 +9,8 @@ PopupWindow {
|
||||
required property Item anchorItem
|
||||
required property QtObject bar
|
||||
property var owner: null
|
||||
property int margin: 10
|
||||
property int padding: 14
|
||||
property int margin: Style.gapsOut
|
||||
property int padding: Style.spacing.popupPadding
|
||||
property int contentWidth: 280
|
||||
property int contentHeight: 200
|
||||
property color borderColor: Color.popups.border
|
||||
@@ -22,7 +22,38 @@ PopupWindow {
|
||||
|
||||
readonly property var coordinatorKey: owner || root
|
||||
readonly property var anchorWindow: anchorItem ? anchorItem.QsWindow.window : null
|
||||
readonly property var popupScreen: anchorWindow ? anchorWindow.screen : null
|
||||
readonly property bool containsMouse: cardHover.hovered
|
||||
readonly property real screenW: popupScreen ? popupScreen.width : 0
|
||||
readonly property real screenH: popupScreen ? popupScreen.height : 0
|
||||
readonly property real barW: anchorWindow ? anchorWindow.width : 0
|
||||
readonly property real barH: anchorWindow ? anchorWindow.height : 0
|
||||
readonly property real availableCardWidth: screenW > 0
|
||||
? Math.max(120, screenW - ((bar && (bar.position === "left" || bar.position === "right")) ? barW : 0) - root.margin * 2)
|
||||
: 0
|
||||
readonly property real availableCardHeight: screenH > 0
|
||||
? Math.max(120, screenH - ((bar && (bar.position === "top" || bar.position === "bottom")) ? barH : 0) - root.margin * 2)
|
||||
: 0
|
||||
|
||||
function fittedContentWidth(width, cap) {
|
||||
var desired = Math.max(1, Number(width) || 1)
|
||||
var maxWidth = root.availableCardWidth > 0 ? root.availableCardWidth : desired
|
||||
if (cap !== undefined && Number(cap) > 0) maxWidth = Math.min(maxWidth, Number(cap))
|
||||
return Math.round(Math.min(desired, maxWidth))
|
||||
}
|
||||
|
||||
function fittedContentHeight(implicitHeight, cap) {
|
||||
var desired = Math.max(root.padding * 2, (Number(implicitHeight) || 0) + root.padding * 2)
|
||||
var maxHeight = root.availableCardHeight > 0 ? root.availableCardHeight : desired
|
||||
if (cap !== undefined && Number(cap) > 0) maxHeight = Math.min(maxHeight, Number(cap))
|
||||
return Math.round(Math.min(desired, maxHeight))
|
||||
}
|
||||
|
||||
function cappedContentHeight(height) {
|
||||
var desired = Math.max(root.padding * 2, Number(height) || root.padding * 2)
|
||||
var maxHeight = root.availableCardHeight > 0 ? root.availableCardHeight : desired
|
||||
return Math.round(Math.min(desired, maxHeight))
|
||||
}
|
||||
|
||||
function closePopout() {
|
||||
if (owner && "closePopout" in owner) owner.closePopout()
|
||||
@@ -119,7 +150,7 @@ PopupWindow {
|
||||
anchors.fill: parent
|
||||
color: Color.popups.background
|
||||
border.color: root.borderColor
|
||||
border.width: 2
|
||||
border.width: Math.max(1, Style.space(2))
|
||||
radius: Style.cornerRadius
|
||||
opacity: root.open ? 1.0 : 0
|
||||
|
||||
|
||||
@@ -30,13 +30,13 @@ Item {
|
||||
property color popupBorder: Color.popups.border
|
||||
property color accent: Color.accent
|
||||
property string fontFamily: Style.font.family
|
||||
property int rowHeight: 28
|
||||
property int popupRowHeight: 28
|
||||
property int popupMinHeight: 220
|
||||
property int rowHeight: Style.spacing.controlHeight
|
||||
property int popupRowHeight: Style.spacing.popupRowHeight
|
||||
property int popupMinHeight: Style.spacing.searchablePopupMinHeight
|
||||
property bool showLabel: true
|
||||
|
||||
// Panel-cursor flag. When true, the trigger renders the same focus ring
|
||||
// as Tab-focus so a panel's keyboard cursor lands here identically.
|
||||
// Panel-cursor flag. When true, the trigger renders the shared
|
||||
// hover-cursor state. Active Qt focus defaults to the same visuals.
|
||||
// Emits `hovered(bool)` on pointer enter/leave so the panel can keep
|
||||
// its cursor state in sync with the mouse.
|
||||
property bool hasCursor: false
|
||||
@@ -84,12 +84,12 @@ Item {
|
||||
|
||||
onOptionsChanged: recomputeFiltered()
|
||||
|
||||
implicitWidth: 260
|
||||
implicitHeight: showLabel && label !== "" ? rowHeight + 18 : rowHeight
|
||||
implicitWidth: Style.spacing.searchableDropdownWidth
|
||||
implicitHeight: showLabel && label !== "" ? rowHeight + Style.spacing.huge : rowHeight
|
||||
|
||||
Column {
|
||||
anchors.fill: parent
|
||||
spacing: 4
|
||||
spacing: Style.spacing.labelGap
|
||||
|
||||
Text {
|
||||
visible: root.showLabel && root.label !== ""
|
||||
@@ -106,18 +106,27 @@ Item {
|
||||
height: root.rowHeight
|
||||
radius: Style.cornerRadius
|
||||
|
||||
readonly property bool _focused: trigger.activeFocus || root.hasCursor
|
||||
readonly property bool _focused: trigger.activeFocus
|
||||
readonly property bool _hot: triggerHover.hovered || root.hasCursor
|
||||
|
||||
color: Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b,
|
||||
trigger._focused ? 0.08 : 0.04)
|
||||
color: trigger._focused
|
||||
? Style.focusFillFor(root.foreground, root.accent)
|
||||
: (trigger._hot
|
||||
? Style.hoverFillFor(root.foreground, root.accent)
|
||||
: Style.normalFillFor(root.foreground, root.accent))
|
||||
border.color: trigger._focused
|
||||
? Style.focusBorderColor
|
||||
: Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, 0.4)
|
||||
border.width: trigger._focused ? Style.focusBorderWidth : 1
|
||||
? Style.focusBorderFor(root.foreground, root.accent)
|
||||
: (trigger._hot
|
||||
? Style.hoverBorderFor(root.foreground, root.accent)
|
||||
: Style.normalBorderFor(root.foreground, root.accent))
|
||||
border.width: trigger._focused
|
||||
? Style.focusBorderWidth
|
||||
: (trigger._hot ? Style.hoverBorderWidth : Style.normalBorderWidth)
|
||||
|
||||
activeFocusOnTab: true
|
||||
|
||||
HoverHandler {
|
||||
id: triggerHover
|
||||
onHoveredChanged: root.hovered(hovered)
|
||||
}
|
||||
|
||||
@@ -135,8 +144,8 @@ Item {
|
||||
anchors.left: parent.left
|
||||
anchors.right: chevron.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.leftMargin: 10
|
||||
anchors.rightMargin: 6
|
||||
anchors.leftMargin: Style.spacing.controlPaddingX
|
||||
anchors.rightMargin: Style.spacing.md
|
||||
text: root.currentLabel() || root.triggerLabel || root.placeholderText
|
||||
color: (root.currentLabel() || root.triggerLabel) ? root.foreground : Qt.darker(root.foreground, 1.5)
|
||||
font.family: root.fontFamily
|
||||
@@ -148,7 +157,7 @@ Item {
|
||||
id: chevron
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.rightMargin: 8
|
||||
anchors.rightMargin: Style.spacing.controlGap
|
||||
text: ""
|
||||
color: Qt.darker(root.foreground, 1.2)
|
||||
font.family: root.fontFamily
|
||||
@@ -167,18 +176,18 @@ Item {
|
||||
QQC.Popup {
|
||||
id: popup
|
||||
x: 0
|
||||
y: trigger.height + 2
|
||||
y: trigger.height + Style.spacing.xxs
|
||||
width: trigger.width
|
||||
implicitHeight: Math.max(root.popupMinHeight,
|
||||
Math.min(resultList.contentHeight + 50,
|
||||
root.popupRowHeight * 6 + 5 * 4 + 50))
|
||||
padding: 1
|
||||
Math.min(resultList.contentHeight + Style.space(50),
|
||||
root.popupRowHeight * 6 + 5 * Style.spacing.labelGap + Style.space(50)))
|
||||
padding: Style.spacing.hairline
|
||||
focus: true
|
||||
|
||||
background: Rectangle {
|
||||
color: root.background
|
||||
border.color: root.popupBorder
|
||||
border.width: 1
|
||||
border.width: Style.normalBorderWidth
|
||||
radius: Style.cornerRadius
|
||||
}
|
||||
|
||||
@@ -193,13 +202,14 @@ Item {
|
||||
spacing: 0
|
||||
|
||||
Item {
|
||||
id: searchHeader
|
||||
width: parent.width
|
||||
height: 38
|
||||
height: root.popupRowHeight + Style.spacing.controlPaddingX
|
||||
|
||||
TextField {
|
||||
id: searchField
|
||||
anchors.fill: parent
|
||||
anchors.margins: 6
|
||||
anchors.margins: Style.spacing.md
|
||||
placeholderText: root.placeholderText
|
||||
foreground: root.foreground
|
||||
accent: root.accent
|
||||
@@ -234,12 +244,12 @@ Item {
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: 1
|
||||
color: Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, 0.10)
|
||||
color: Style.alpha(root.foreground, 0.10)
|
||||
}
|
||||
|
||||
Item {
|
||||
width: parent.width
|
||||
height: popup.height - 38 - 2 - 1
|
||||
height: popup.height - searchHeader.height - Style.spacing.xxs - 1
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
@@ -253,7 +263,7 @@ Item {
|
||||
ListView {
|
||||
id: resultList
|
||||
anchors.fill: parent
|
||||
spacing: 4
|
||||
spacing: Style.spacing.labelGap
|
||||
clip: true
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
model: root.filtered
|
||||
@@ -294,9 +304,9 @@ Item {
|
||||
required property var modelData
|
||||
required property int index
|
||||
width: resultList.width
|
||||
height: Math.max(root.popupRowHeight, rowContent.implicitHeight + 12)
|
||||
height: Math.max(root.popupRowHeight, rowContent.implicitHeight + Style.spacing.rowPaddingX)
|
||||
color: index === resultList.currentIndex
|
||||
? Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, 0.14)
|
||||
? Style.hoverFillFor(root.foreground, root.accent)
|
||||
: "transparent"
|
||||
|
||||
Column {
|
||||
@@ -304,13 +314,13 @@ Item {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.leftMargin: 10
|
||||
anchors.rightMargin: 10
|
||||
spacing: 2
|
||||
anchors.leftMargin: Style.spacing.controlPaddingX
|
||||
anchors.rightMargin: Style.spacing.controlPaddingX
|
||||
spacing: Style.spacing.xxs
|
||||
|
||||
Text {
|
||||
text: root.optionLabel(modelData)
|
||||
color: index === resultList.currentIndex ? root.accent : root.foreground
|
||||
color: index === resultList.currentIndex ? Style.hoverStateColor(root.foreground, root.accent) : root.foreground
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.body
|
||||
elide: Text.ElideRight
|
||||
|
||||
+20
-14
@@ -9,9 +9,8 @@ import qs.Commons
|
||||
//
|
||||
// Defaults bind to qs.Commons.Color so a caller with no theme overrides
|
||||
// just works; foreground / accent / selectionTint can be overridden per
|
||||
// instance. Focus styling uses Style.focusBorderColor (the same accent
|
||||
// ring Toggle and Button paint) so keyboard cursor and form focus
|
||||
// chrome stay consistent across the shell.
|
||||
// instance. activeFocus and mouse hover / panel cursor use the same
|
||||
// hover-cursor defaults, so text inputs match Button, Toggle, and Dropdown.
|
||||
//
|
||||
// Sizing is driven by font.pixelSize + verticalPadding. The default 30px
|
||||
// implicitHeight fits dialog forms; inline callers (wifi's row-embedded
|
||||
@@ -21,20 +20,20 @@ TextField {
|
||||
|
||||
property color foreground: Color.foreground
|
||||
property color accent: Color.accent
|
||||
property color selectionTint: Qt.rgba(foreground.r, foreground.g, foreground.b, 0.35)
|
||||
property color selectionTint: Style.selectionFillFor(foreground, accent)
|
||||
property bool password: false
|
||||
property real horizontalPadding: 10
|
||||
property real verticalPadding: 7
|
||||
property real horizontalPadding: Style.spacing.controlPaddingX
|
||||
property real verticalPadding: Style.spacing.inputPaddingY
|
||||
|
||||
// Panel-cursor flag. When true (and the field isn't already focused),
|
||||
// the background paints the same accent ring as activeFocus so the
|
||||
// panel's keyboard cursor lands here identically to a mouse hover.
|
||||
// the background paints the shared hover/cursor state.
|
||||
// For mouse-enter/leave the consumer reads QQC TextField's inherited
|
||||
// `hovered` property (via onHoveredChanged) — we don't add a sibling
|
||||
// signal because the inherited property would shadow it.
|
||||
property bool hasCursor: false
|
||||
|
||||
readonly property bool _focused: activeFocus || hasCursor
|
||||
readonly property bool _focused: activeFocus
|
||||
readonly property bool _hot: hovered || hasCursor
|
||||
|
||||
echoMode: password ? TextInput.Password : TextInput.Normal
|
||||
font.family: Style.font.family
|
||||
@@ -50,12 +49,19 @@ TextField {
|
||||
bottomPadding: verticalPadding
|
||||
|
||||
background: Rectangle {
|
||||
color: Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b,
|
||||
root._focused ? 0.08 : 0.04)
|
||||
color: root._focused
|
||||
? Style.focusFillFor(root.foreground, root.accent)
|
||||
: (root._hot
|
||||
? Style.hoverFillFor(root.foreground, root.accent)
|
||||
: Style.normalFillFor(root.foreground, root.accent))
|
||||
border.color: root._focused
|
||||
? Style.focusBorderColor
|
||||
: Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, 0.18)
|
||||
border.width: root._focused ? Style.focusBorderWidth : 1
|
||||
? Style.focusBorderFor(root.foreground, root.accent)
|
||||
: (root._hot
|
||||
? Style.hoverBorderFor(root.foreground, root.accent)
|
||||
: Style.normalBorderFor(root.foreground, root.accent))
|
||||
border.width: root._focused
|
||||
? Style.focusBorderWidth
|
||||
: (root._hot ? Style.hoverBorderWidth : Style.normalBorderWidth)
|
||||
radius: Style.cornerRadius
|
||||
}
|
||||
}
|
||||
|
||||
+40
-30
@@ -6,10 +6,8 @@ import qs.Commons
|
||||
// flip `checked` in response (the component is stateless about the actual
|
||||
// value so it composes cleanly with model-driven UI).
|
||||
//
|
||||
// Cursor and focus styling match the rest of the kit: hasCursor (panel
|
||||
// keyboard cursor / mouse hover) is a fill only, mirroring CursorSurface;
|
||||
// activeFocus (Tab focus) adds the accent border ring on top via the
|
||||
// shared Style tokens.
|
||||
// Cursor and focus styling match the rest of the kit: hasCursor / mouse
|
||||
// hover and activeFocus share the hover-cursor defaults.
|
||||
//
|
||||
// `rounded` auto-detects from Style.cornerRadius so the switch follows
|
||||
// the theme: pill shape on round-corners themes, square on sharp.
|
||||
@@ -23,8 +21,7 @@ Rectangle {
|
||||
|
||||
// Panel-cursor flag. Same role as Button.hasCursor:
|
||||
// panels with their own keyboard cursor bind this to drive the highlight
|
||||
// separately from activeFocus. Renders as a tinted fill only — the accent
|
||||
// border ring is reserved for Tab focus (activeFocus).
|
||||
// separately from activeFocus. Visuals use the same hover-cursor tokens.
|
||||
property bool hasCursor: false
|
||||
|
||||
// Switch shape follows the theme by default: pill on round, square on sharp.
|
||||
@@ -45,17 +42,30 @@ Rectangle {
|
||||
Keys.onEnterPressed: root.clicked()
|
||||
Keys.onSpacePressed: root.clicked()
|
||||
|
||||
implicitHeight: Math.max(54, content.implicitHeight + 18)
|
||||
implicitWidth: 240
|
||||
readonly property int trackHeight: Math.max(22, Math.round(Style.spacing.controlHeight * 0.55))
|
||||
readonly property int trackWidth: Math.max(42, Math.round(trackHeight * 1.9))
|
||||
readonly property int knobSize: Math.max(16, Math.round(trackHeight * 0.72))
|
||||
readonly property int knobInset: Math.max(2, Math.round((trackHeight - knobSize) / 2))
|
||||
|
||||
implicitHeight: Math.max(54, content.implicitHeight + Style.spacing.huge)
|
||||
implicitWidth: Style.space(240)
|
||||
radius: Style.cornerRadius
|
||||
|
||||
readonly property bool _hot: hasCursor || mouse.containsMouse
|
||||
|
||||
color: activeFocus
|
||||
? Style.focusFillColor
|
||||
: ((hasCursor || mouse.containsMouse) ? Qt.rgba(foreground.r, foreground.g, foreground.b, 0.08) : Qt.rgba(foreground.r, foreground.g, foreground.b, 0.03))
|
||||
? Style.focusFillFor(foreground, accent)
|
||||
: (_hot
|
||||
? Style.hoverFillFor(foreground, accent)
|
||||
: Style.normalFillFor(foreground, accent))
|
||||
border.color: activeFocus
|
||||
? Style.focusBorderColor
|
||||
: Qt.rgba(foreground.r, foreground.g, foreground.b, 0.12)
|
||||
border.width: activeFocus ? Style.focusBorderWidth : 1
|
||||
? Style.focusBorderFor(foreground, accent)
|
||||
: (_hot
|
||||
? Style.hoverBorderFor(foreground, accent)
|
||||
: Style.normalBorderFor(foreground, accent))
|
||||
border.width: activeFocus
|
||||
? Style.focusBorderWidth
|
||||
: (_hot ? Style.hoverBorderWidth : Style.normalBorderWidth)
|
||||
|
||||
Behavior on color { ColorAnimation { duration: 100 } }
|
||||
|
||||
@@ -64,13 +74,13 @@ Rectangle {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.leftMargin: 12
|
||||
anchors.rightMargin: 12
|
||||
spacing: 12
|
||||
anchors.leftMargin: Style.spacing.rowPaddingX
|
||||
anchors.rightMargin: Style.spacing.rowPaddingX
|
||||
spacing: Style.spacing.rowPaddingX
|
||||
|
||||
Column {
|
||||
width: parent.width - track.width - parent.spacing
|
||||
spacing: 3
|
||||
spacing: Style.spacing.xs
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
Text {
|
||||
@@ -96,27 +106,27 @@ Rectangle {
|
||||
|
||||
Rectangle {
|
||||
id: track
|
||||
width: 42
|
||||
height: 22
|
||||
width: root.trackWidth
|
||||
height: root.trackHeight
|
||||
radius: root.rounded ? height / 2 : 0
|
||||
color: root.checked
|
||||
? Qt.rgba(root.accent.r, root.accent.g, root.accent.b, 0.35)
|
||||
: Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, 0.12)
|
||||
? Style.selectedFillFor(root.foreground, root.accent)
|
||||
: Style.normalFillFor(root.foreground, root.accent)
|
||||
border.color: root.checked
|
||||
? root.accent
|
||||
: Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, 0.28)
|
||||
border.width: 1
|
||||
? Style.selectedBorderFor(root.foreground, root.accent)
|
||||
: Style.normalBorderFor(root.foreground, root.accent)
|
||||
border.width: root.checked ? Style.selectedBorderWidth : Style.normalBorderWidth
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
Behavior on color { ColorAnimation { duration: 120 } }
|
||||
|
||||
Rectangle {
|
||||
width: 16
|
||||
height: 16
|
||||
radius: root.rounded ? 8 : 0
|
||||
x: root.checked ? track.width - width - 3 : 3
|
||||
y: 3
|
||||
color: root.checked ? root.accent : Qt.darker(root.foreground, 1.25)
|
||||
width: root.knobSize
|
||||
height: root.knobSize
|
||||
radius: root.rounded ? height / 2 : 0
|
||||
x: root.checked ? track.width - width - root.knobInset : root.knobInset
|
||||
y: root.knobInset
|
||||
color: root.checked ? Style.selectedStateColor(root.foreground, root.accent) : Qt.darker(root.foreground, 1.25)
|
||||
|
||||
Behavior on x { NumberAnimation { duration: 120; easing.type: Easing.OutCubic } }
|
||||
Behavior on color { ColorAnimation { duration: 120 } }
|
||||
|
||||
@@ -25,11 +25,14 @@ Item {
|
||||
|
||||
readonly property bool vertical: bar ? bar.vertical : false
|
||||
readonly property int barSize: bar ? bar.barSize : Style.bar.sizeHorizontal
|
||||
readonly property real scaledHorizontalMargin: Style.spaceReal(horizontalMargin)
|
||||
readonly property real scaledRightExtraMargin: Style.spaceReal(rightExtraMargin)
|
||||
readonly property real scaledVerticalPadding: Style.spaceReal(verticalPadding)
|
||||
|
||||
visible: text !== "" || keepSpace
|
||||
opacity: text === "" ? 0 : 1
|
||||
implicitWidth: fixedWidth > 0 ? fixedWidth : (vertical ? barSize : Math.max(12, label.implicitWidth + horizontalMargin * 2 + rightExtraMargin))
|
||||
implicitHeight: fixedHeight > 0 ? fixedHeight : (vertical ? Math.max(12, label.implicitHeight + verticalPadding * 2) : barSize)
|
||||
implicitWidth: fixedWidth > 0 ? fixedWidth : (vertical ? barSize : Math.max(12, label.implicitWidth + scaledHorizontalMargin * 2 + scaledRightExtraMargin))
|
||||
implicitHeight: fixedHeight > 0 ? fixedHeight : (vertical ? Math.max(12, label.implicitHeight + scaledVerticalPadding * 2) : barSize)
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation { duration: 140; easing.type: Easing.OutCubic }
|
||||
@@ -38,7 +41,7 @@ Item {
|
||||
Text {
|
||||
id: label
|
||||
anchors.centerIn: parent
|
||||
anchors.horizontalCenterOffset: root.vertical ? 0 : -root.rightExtraMargin / 2
|
||||
anchors.horizontalCenterOffset: root.vertical ? 0 : -root.scaledRightExtraMargin / 2
|
||||
text: root.text
|
||||
color: root.active ? root.activeColor : root.foreground
|
||||
font.family: root.fontFamily
|
||||
|
||||
+18
-18
@@ -731,13 +731,13 @@ Item {
|
||||
|
||||
LeftModules {
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 8
|
||||
anchors.leftMargin: Style.space(8)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
RightModules {
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 8
|
||||
anchors.rightMargin: Style.space(8)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
@@ -753,13 +753,13 @@ Item {
|
||||
|
||||
LeftModules {
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 8
|
||||
anchors.topMargin: Style.space(8)
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
RightModules {
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 8
|
||||
anchors.bottomMargin: Style.space(8)
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
}
|
||||
@@ -1136,8 +1136,8 @@ Item {
|
||||
|
||||
component WorkspacesModule: GridLayout {
|
||||
columns: root.vertical ? 1 : root.workspaceIds().length
|
||||
columnSpacing: root.vertical ? 0 : 2
|
||||
rowSpacing: root.vertical ? 2 : 0
|
||||
columnSpacing: root.vertical ? 0 : Style.space(2)
|
||||
rowSpacing: root.vertical ? Style.space(2) : 0
|
||||
|
||||
Repeater {
|
||||
model: root.workspaceIds()
|
||||
@@ -1357,7 +1357,7 @@ Item {
|
||||
id: trayIcons
|
||||
x: trayRoot.drawerExtent - trayRoot.revealExtent
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 17
|
||||
spacing: Style.space(17)
|
||||
layer.enabled: true
|
||||
|
||||
Repeater {
|
||||
@@ -1372,8 +1372,8 @@ Item {
|
||||
id: pinnedRow
|
||||
x: drawerArea.x + horizontalTrayRoot.drawerBlockWidth
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 17
|
||||
leftPadding: trayRoot.pinnedItems.length > 0 && trayRoot.allItems.length > 0 ? 6 : 0
|
||||
spacing: Style.space(17)
|
||||
leftPadding: trayRoot.pinnedItems.length > 0 && trayRoot.allItems.length > 0 ? Style.space(6) : 0
|
||||
Repeater {
|
||||
model: trayRoot.pinnedItems
|
||||
TrayItem {}
|
||||
@@ -1441,7 +1441,7 @@ Item {
|
||||
id: trayIcons
|
||||
y: trayRoot.drawerExtent - trayRoot.revealExtent
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
spacing: 17
|
||||
spacing: Style.space(17)
|
||||
layer.enabled: true
|
||||
|
||||
Repeater {
|
||||
@@ -1456,8 +1456,8 @@ Item {
|
||||
id: pinnedCol
|
||||
y: drawerArea.y + verticalTrayRoot.drawerBlockHeight
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
spacing: 17
|
||||
topPadding: trayRoot.pinnedItems.length > 0 && trayRoot.allItems.length > 0 ? 6 : 0
|
||||
spacing: Style.space(17)
|
||||
topPadding: trayRoot.pinnedItems.length > 0 && trayRoot.allItems.length > 0 ? Style.space(6) : 0
|
||||
Repeater {
|
||||
model: trayRoot.pinnedItems
|
||||
TrayItem {}
|
||||
@@ -1472,13 +1472,13 @@ Item {
|
||||
owner: trayRoot
|
||||
bar: root
|
||||
open: trayRoot.managePopupOpen
|
||||
contentWidth: 300
|
||||
contentHeight: manageColumn.implicitHeight + 28
|
||||
contentWidth: managePopup.fittedContentWidth(Style.space(300))
|
||||
contentHeight: managePopup.fittedContentHeight(manageColumn.implicitHeight)
|
||||
|
||||
Column {
|
||||
id: manageColumn
|
||||
anchors.fill: parent
|
||||
spacing: 8
|
||||
spacing: Style.space(8)
|
||||
|
||||
Text {
|
||||
text: "Tray icons"
|
||||
@@ -1541,9 +1541,9 @@ Item {
|
||||
Text {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: rowIcon.right
|
||||
anchors.leftMargin: 10
|
||||
anchors.leftMargin: Style.space(10)
|
||||
anchors.right: rowHideBtn.left
|
||||
anchors.rightMargin: 8
|
||||
anchors.rightMargin: Style.space(8)
|
||||
text: rowRoot.displayName
|
||||
color: root.foreground
|
||||
font.family: root.fontFamily
|
||||
@@ -1569,7 +1569,7 @@ Item {
|
||||
id: rowHideBtn
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: rowPinBtn.left
|
||||
anchors.rightMargin: 6
|
||||
anchors.rightMargin: Style.space(6)
|
||||
iconText: ""
|
||||
text: rowRoot.isHidden ? "Show" : "Hide"
|
||||
foreground: root.foreground
|
||||
|
||||
@@ -22,7 +22,7 @@ Item {
|
||||
readonly property bool vertical: bar ? bar.vertical : false
|
||||
|
||||
visible: title !== "" && !vertical
|
||||
implicitWidth: visible ? Math.min(maxLabelWidth, labelText.implicitWidth) + 16 : 0
|
||||
implicitWidth: visible ? Math.min(maxLabelWidth, labelText.implicitWidth) + Style.spacing.controlPaddingX * 2 : 0
|
||||
implicitHeight: bar ? bar.barSize : 26
|
||||
|
||||
Behavior on implicitWidth {
|
||||
@@ -31,8 +31,8 @@ Item {
|
||||
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: 8
|
||||
anchors.rightMargin: 8
|
||||
anchors.leftMargin: Style.space(8)
|
||||
anchors.rightMargin: Style.space(8)
|
||||
clip: true
|
||||
|
||||
Text {
|
||||
|
||||
@@ -98,10 +98,10 @@ Item {
|
||||
property int selectedIndex: -1
|
||||
|
||||
readonly property color hoverFill: bar
|
||||
? Qt.rgba(bar.foreground.r, bar.foreground.g, bar.foreground.b, 0.08)
|
||||
? Style.hoverFillFor(bar.foreground, Color.accent)
|
||||
: "transparent"
|
||||
readonly property color selectedFill: bar
|
||||
? Qt.rgba(bar.foreground.r, bar.foreground.g, bar.foreground.b, 0.18)
|
||||
? Style.selectedFillFor(bar.foreground, Color.accent)
|
||||
: "transparent"
|
||||
|
||||
function sectionCount(section) {
|
||||
@@ -376,8 +376,8 @@ Item {
|
||||
owner: root
|
||||
bar: root.bar
|
||||
open: root.popupOpen
|
||||
contentWidth: 370
|
||||
contentHeight: Math.min(560, panelColumn.implicitHeight + 28)
|
||||
contentWidth: panel.fittedContentWidth(Style.space(370))
|
||||
contentHeight: panel.fittedContentHeight(panelColumn.implicitHeight, Style.space(560))
|
||||
|
||||
PanelKeyCatcher {
|
||||
id: keyCatcher
|
||||
@@ -414,16 +414,16 @@ Item {
|
||||
Column {
|
||||
id: panelColumn
|
||||
width: scrollArea.availableWidth
|
||||
spacing: 14
|
||||
spacing: Style.space(14)
|
||||
|
||||
// ---- Output ----
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: 6
|
||||
spacing: Style.space(6)
|
||||
|
||||
Row {
|
||||
width: parent.width
|
||||
spacing: 8
|
||||
spacing: Style.space(8)
|
||||
|
||||
PanelSectionHeader {
|
||||
text: "Output"
|
||||
@@ -439,7 +439,7 @@ Item {
|
||||
font.family: root.bar.fontFamily
|
||||
font.pixelSize: Style.font.bodySmall
|
||||
elide: Text.ElideRight
|
||||
width: parent.width - 70
|
||||
width: parent.width - Style.space(70)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
@@ -450,7 +450,7 @@ Item {
|
||||
CursorSurface {
|
||||
id: outputSliderRow
|
||||
width: parent.width
|
||||
height: outputSliderInner.implicitHeight + 8
|
||||
height: outputSliderInner.implicitHeight + Style.spacing.controlGap
|
||||
hasCursor: root.focusSection === "output" && root.selectedIndex === -1
|
||||
onHasCursorChanged: if (hasCursor) root.ensureCursorVisible(outputSliderRow)
|
||||
foreground: root.bar.foreground
|
||||
@@ -459,9 +459,9 @@ Item {
|
||||
Row {
|
||||
id: outputSliderInner
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: 6
|
||||
anchors.rightMargin: 6
|
||||
spacing: 8
|
||||
anchors.leftMargin: Style.space(6)
|
||||
anchors.rightMargin: Style.space(6)
|
||||
spacing: Style.space(8)
|
||||
|
||||
Text {
|
||||
id: outputIconText
|
||||
@@ -469,7 +469,7 @@ Item {
|
||||
color: root.bar.foreground
|
||||
font.family: root.bar.fontFamily
|
||||
font.pixelSize: Style.font.heading
|
||||
width: 22
|
||||
width: Style.space(22)
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
opacity: root.outputMuted ? 0.5 : 1.0
|
||||
@@ -484,7 +484,7 @@ Item {
|
||||
PanelSlider {
|
||||
id: outputSlider
|
||||
bar: root.bar
|
||||
width: parent.width - outputIconText.width - outputPercent.width - 16
|
||||
width: parent.width - outputIconText.width - outputPercent.width - Style.space(16)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
minimum: 0
|
||||
maximum: 1
|
||||
@@ -502,7 +502,7 @@ Item {
|
||||
color: root.bar.foreground
|
||||
font.family: root.bar.fontFamily
|
||||
font.pixelSize: Style.font.bodySmall
|
||||
width: 36
|
||||
width: Style.space(36)
|
||||
horizontalAlignment: Text.AlignRight
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
opacity: root.outputMuted ? 0.5 : 1.0
|
||||
@@ -537,12 +537,12 @@ Item {
|
||||
// ---- Input ----
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: 6
|
||||
spacing: Style.space(6)
|
||||
visible: root.audioSources.length > 0 || !!root.source
|
||||
|
||||
Row {
|
||||
width: parent.width
|
||||
spacing: 8
|
||||
spacing: Style.space(8)
|
||||
|
||||
PanelSectionHeader {
|
||||
text: "Input"
|
||||
@@ -558,7 +558,7 @@ Item {
|
||||
font.family: root.bar.fontFamily
|
||||
font.pixelSize: Style.font.bodySmall
|
||||
elide: Text.ElideRight
|
||||
width: parent.width - 56
|
||||
width: parent.width - Style.space(56)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
@@ -567,7 +567,7 @@ Item {
|
||||
id: inputSliderRow
|
||||
visible: !!root.source
|
||||
width: parent.width
|
||||
height: inputSliderInner.implicitHeight + 8
|
||||
height: inputSliderInner.implicitHeight + Style.spacing.controlGap
|
||||
hasCursor: root.focusSection === "input" && root.selectedIndex === -1
|
||||
onHasCursorChanged: if (hasCursor) root.ensureCursorVisible(inputSliderRow)
|
||||
foreground: root.bar.foreground
|
||||
@@ -576,9 +576,9 @@ Item {
|
||||
Row {
|
||||
id: inputSliderInner
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: 6
|
||||
anchors.rightMargin: 6
|
||||
spacing: 8
|
||||
anchors.leftMargin: Style.space(6)
|
||||
anchors.rightMargin: Style.space(6)
|
||||
spacing: Style.space(8)
|
||||
|
||||
Text {
|
||||
id: inputIconText
|
||||
@@ -586,7 +586,7 @@ Item {
|
||||
color: root.bar.foreground
|
||||
font.family: root.bar.fontFamily
|
||||
font.pixelSize: Style.font.heading
|
||||
width: 22
|
||||
width: Style.space(22)
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
opacity: root.inputMuted ? 0.5 : 1.0
|
||||
@@ -601,7 +601,7 @@ Item {
|
||||
PanelSlider {
|
||||
id: inputSlider
|
||||
bar: root.bar
|
||||
width: parent.width - inputIconText.width - inputPercent.width - 16
|
||||
width: parent.width - inputIconText.width - inputPercent.width - Style.space(16)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
minimum: 0
|
||||
maximum: 1
|
||||
@@ -619,7 +619,7 @@ Item {
|
||||
color: root.bar.foreground
|
||||
font.family: root.bar.fontFamily
|
||||
font.pixelSize: Style.font.bodySmall
|
||||
width: 36
|
||||
width: Style.space(36)
|
||||
horizontalAlignment: Text.AlignRight
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
opacity: root.inputMuted ? 0.5 : 1.0
|
||||
@@ -654,7 +654,7 @@ Item {
|
||||
// ---- Per-app streams ----
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: 6
|
||||
spacing: Style.space(6)
|
||||
visible: root.audioStreams.length > 0
|
||||
|
||||
PanelSectionHeader {
|
||||
@@ -698,23 +698,23 @@ Item {
|
||||
foreground: root.bar.foreground
|
||||
fill: root.hoverFill
|
||||
currentFill: root.selectedFill
|
||||
implicitHeight: sinkInner.implicitHeight + 10
|
||||
implicitHeight: sinkInner.implicitHeight + Style.spacing.xl
|
||||
|
||||
Row {
|
||||
id: sinkInner
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.leftMargin: 6
|
||||
anchors.rightMargin: 6
|
||||
spacing: 8
|
||||
anchors.leftMargin: Style.space(6)
|
||||
anchors.rightMargin: Style.space(6)
|
||||
spacing: Style.space(8)
|
||||
|
||||
Text {
|
||||
text: root.sinkGlyph(sinkRow.node)
|
||||
color: root.bar.foreground
|
||||
font.family: root.bar.fontFamily
|
||||
font.pixelSize: Style.font.title
|
||||
width: 22
|
||||
width: Style.space(22)
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
@@ -725,7 +725,7 @@ Item {
|
||||
font.family: root.bar.fontFamily
|
||||
font.pixelSize: Style.font.body
|
||||
elide: Text.ElideRight
|
||||
width: parent.width - 22 - 14 - 16
|
||||
width: parent.width - Style.space(22) - Style.space(14) - Style.space(16)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
@@ -734,7 +734,7 @@ Item {
|
||||
color: root.bar.foreground
|
||||
font.family: root.bar.fontFamily
|
||||
font.pixelSize: Style.font.subtitle
|
||||
width: 14
|
||||
width: Style.space(14)
|
||||
horizontalAlignment: Text.AlignRight
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
@@ -765,23 +765,23 @@ Item {
|
||||
foreground: root.bar.foreground
|
||||
fill: root.hoverFill
|
||||
currentFill: root.selectedFill
|
||||
implicitHeight: sourceInner.implicitHeight + 10
|
||||
implicitHeight: sourceInner.implicitHeight + Style.spacing.xl
|
||||
|
||||
Row {
|
||||
id: sourceInner
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.leftMargin: 6
|
||||
anchors.rightMargin: 6
|
||||
spacing: 8
|
||||
anchors.leftMargin: Style.space(6)
|
||||
anchors.rightMargin: Style.space(6)
|
||||
spacing: Style.space(8)
|
||||
|
||||
Text {
|
||||
text: root.sourceGlyph(sourceRow.node)
|
||||
color: root.bar.foreground
|
||||
font.family: root.bar.fontFamily
|
||||
font.pixelSize: Style.font.title
|
||||
width: 22
|
||||
width: Style.space(22)
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
@@ -792,7 +792,7 @@ Item {
|
||||
font.family: root.bar.fontFamily
|
||||
font.pixelSize: Style.font.body
|
||||
elide: Text.ElideRight
|
||||
width: parent.width - 22 - 14 - 16
|
||||
width: parent.width - Style.space(22) - Style.space(14) - Style.space(16)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
@@ -801,7 +801,7 @@ Item {
|
||||
color: root.bar.foreground
|
||||
font.family: root.bar.fontFamily
|
||||
font.pixelSize: Style.font.subtitle
|
||||
width: 14
|
||||
width: Style.space(14)
|
||||
horizontalAlignment: Text.AlignRight
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
@@ -836,20 +836,20 @@ Item {
|
||||
foreground: root.bar.foreground
|
||||
fill: root.hoverFill
|
||||
currentFill: root.selectedFill
|
||||
implicitHeight: streamColumn.implicitHeight + 8
|
||||
implicitHeight: streamColumn.implicitHeight + Style.spacing.rowGap
|
||||
|
||||
Column {
|
||||
id: streamColumn
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.leftMargin: 6
|
||||
anchors.rightMargin: 6
|
||||
spacing: 2
|
||||
anchors.leftMargin: Style.space(6)
|
||||
anchors.rightMargin: Style.space(6)
|
||||
spacing: Style.space(2)
|
||||
|
||||
Row {
|
||||
width: parent.width
|
||||
spacing: 6
|
||||
spacing: Style.space(6)
|
||||
|
||||
Text {
|
||||
id: streamMuteIcon
|
||||
@@ -857,7 +857,7 @@ Item {
|
||||
color: root.bar.foreground
|
||||
font.family: root.bar.fontFamily
|
||||
font.pixelSize: Style.font.body
|
||||
width: 14
|
||||
width: Style.space(14)
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
opacity: streamRow.streamMuted ? 0.5 : 1.0
|
||||
@@ -878,7 +878,7 @@ Item {
|
||||
font.family: root.bar.fontFamily
|
||||
font.pixelSize: Style.font.bodySmall
|
||||
elide: Text.ElideRight
|
||||
width: parent.width - streamMuteIcon.width - streamPct.width - 12
|
||||
width: parent.width - streamMuteIcon.width - streamPct.width - Style.space(12)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
@@ -888,7 +888,7 @@ Item {
|
||||
color: Qt.darker(root.bar.foreground, 1.5)
|
||||
font.family: root.bar.fontFamily
|
||||
font.pixelSize: Style.font.bodySmall
|
||||
width: 36
|
||||
width: Style.space(36)
|
||||
horizontalAlignment: Text.AlignRight
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
@@ -110,10 +110,10 @@ Item {
|
||||
property string focusedKnownAddress: ""
|
||||
|
||||
readonly property color hoverFill: bar
|
||||
? Qt.rgba(bar.foreground.r, bar.foreground.g, bar.foreground.b, 0.08)
|
||||
? Style.hoverFillFor(bar.foreground, Color.accent)
|
||||
: "transparent"
|
||||
readonly property color selectedFill: bar
|
||||
? Qt.rgba(bar.foreground.r, bar.foreground.g, bar.foreground.b, 0.18)
|
||||
? Style.selectedFillFor(bar.foreground, Color.accent)
|
||||
: "transparent"
|
||||
|
||||
function sectionCount(section) {
|
||||
@@ -362,8 +362,8 @@ Item {
|
||||
owner: root
|
||||
bar: root.bar
|
||||
open: root.popupOpen
|
||||
contentWidth: 320
|
||||
contentHeight: column.implicitHeight + 28
|
||||
contentWidth: panel.fittedContentWidth(Style.space(320))
|
||||
contentHeight: panel.fittedContentHeight(column.implicitHeight)
|
||||
|
||||
PanelKeyCatcher {
|
||||
id: keyCatcher
|
||||
@@ -379,7 +379,7 @@ Item {
|
||||
Column {
|
||||
id: column
|
||||
anchors.fill: parent
|
||||
spacing: 10
|
||||
spacing: Style.space(10)
|
||||
|
||||
// Header: title left, on/off toggle + actions right.
|
||||
Item {
|
||||
@@ -389,7 +389,7 @@ Item {
|
||||
Row {
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 8
|
||||
spacing: Style.space(8)
|
||||
|
||||
PanelSectionHeader {
|
||||
id: titleText
|
||||
@@ -412,7 +412,7 @@ Item {
|
||||
Row {
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 4
|
||||
spacing: Style.space(4)
|
||||
|
||||
HeaderPill {
|
||||
pillIndex: 0
|
||||
@@ -447,7 +447,7 @@ Item {
|
||||
Flickable {
|
||||
id: deviceFlick
|
||||
width: parent.width
|
||||
height: Math.min(deviceList.implicitHeight, 400)
|
||||
height: Math.min(deviceList.implicitHeight, Style.space(400))
|
||||
contentWidth: width
|
||||
contentHeight: deviceList.implicitHeight
|
||||
clip: true
|
||||
@@ -458,7 +458,7 @@ Item {
|
||||
Column {
|
||||
id: deviceList
|
||||
width: parent.width
|
||||
spacing: 10
|
||||
spacing: Style.space(10)
|
||||
|
||||
// Paired / known devices.
|
||||
Repeater {
|
||||
@@ -526,8 +526,8 @@ Item {
|
||||
tooltipForeground: root.bar.foreground
|
||||
foreground: root.bar.foreground
|
||||
fontFamily: root.bar.fontFamily
|
||||
horizontalPadding: 6
|
||||
verticalPadding: 4
|
||||
horizontalPadding: Style.spacing.md
|
||||
verticalPadding: Style.spacing.labelGap
|
||||
iconSize: 14
|
||||
enabled: pillEnabled
|
||||
opacity: pillEnabled ? 1 : 0.4
|
||||
@@ -622,7 +622,7 @@ Item {
|
||||
return Qt.darker(root.bar.foreground, 1.5)
|
||||
}
|
||||
|
||||
implicitHeight: rowContent.implicitHeight + 12
|
||||
implicitHeight: rowContent.implicitHeight + Style.spacing.rowPaddingX
|
||||
|
||||
MouseArea {
|
||||
id: rowMouse
|
||||
@@ -664,8 +664,8 @@ Item {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.leftMargin: 10
|
||||
anchors.rightMargin: 10
|
||||
anchors.leftMargin: Style.space(10)
|
||||
anchors.rightMargin: Style.space(10)
|
||||
implicitHeight: Math.max(deviceIcon.implicitHeight, info.implicitHeight, disconnectBtn.implicitHeight)
|
||||
|
||||
Text {
|
||||
@@ -705,11 +705,11 @@ Item {
|
||||
|
||||
Column {
|
||||
id: info
|
||||
spacing: 1
|
||||
spacing: Style.space(1)
|
||||
anchors.left: deviceIcon.right
|
||||
anchors.leftMargin: 10
|
||||
anchors.leftMargin: Style.space(10)
|
||||
anchors.right: disconnectBtn.visible ? disconnectBtn.left : parent.right
|
||||
anchors.rightMargin: disconnectBtn.visible ? 8 : 0
|
||||
anchors.rightMargin: disconnectBtn.visible ? Style.space(8) : 0
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
Text {
|
||||
|
||||
@@ -83,12 +83,12 @@ Item {
|
||||
bar: root.bar
|
||||
owner: root
|
||||
open: root.popupOpen
|
||||
contentWidth: 300
|
||||
contentHeight: header.implicitHeight + grid.implicitHeight + 36
|
||||
contentWidth: popup.fittedContentWidth(Style.space(300))
|
||||
contentHeight: popup.fittedContentHeight(header.implicitHeight + grid.implicitHeight + Style.spacing.rowGap)
|
||||
|
||||
Column {
|
||||
anchors.fill: parent
|
||||
spacing: 8
|
||||
spacing: Style.space(8)
|
||||
|
||||
Item {
|
||||
id: header
|
||||
@@ -101,8 +101,8 @@ Item {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
iconText: ""
|
||||
foreground: root.bar.foreground
|
||||
horizontalPadding: 8
|
||||
verticalPadding: 4
|
||||
horizontalPadding: Style.spacing.controlGap
|
||||
verticalPadding: Style.spacing.labelGap
|
||||
onClicked: root.shiftMonth(-1)
|
||||
}
|
||||
|
||||
@@ -121,8 +121,8 @@ Item {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
iconText: ""
|
||||
foreground: root.bar.foreground
|
||||
horizontalPadding: 8
|
||||
verticalPadding: 4
|
||||
horizontalPadding: Style.spacing.controlGap
|
||||
verticalPadding: Style.spacing.labelGap
|
||||
onClicked: root.shiftMonth(1)
|
||||
}
|
||||
}
|
||||
@@ -130,8 +130,8 @@ Item {
|
||||
Grid {
|
||||
id: grid
|
||||
columns: 7
|
||||
rowSpacing: 4
|
||||
columnSpacing: 4
|
||||
rowSpacing: Style.space(4)
|
||||
columnSpacing: Style.space(4)
|
||||
width: parent.width
|
||||
|
||||
Repeater {
|
||||
@@ -140,7 +140,7 @@ Item {
|
||||
Item {
|
||||
required property string modelData
|
||||
width: (grid.width - grid.columnSpacing * 6) / 7
|
||||
height: 18
|
||||
height: Math.max(Style.space(18), Style.font.caption + Style.spacing.xxs)
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
@@ -177,7 +177,7 @@ Item {
|
||||
}
|
||||
|
||||
width: (grid.width - grid.columnSpacing * 6) / 7
|
||||
height: 28
|
||||
height: Math.max(Style.space(28), Style.font.body + Style.spacing.sm)
|
||||
radius: 4
|
||||
color: isToday ? root.bar.foreground : "transparent"
|
||||
border.color: isToday ? root.bar.foreground : "transparent"
|
||||
|
||||
@@ -61,8 +61,8 @@ Item {
|
||||
|
||||
readonly property bool vertical: bar ? bar.vertical : false
|
||||
|
||||
implicitWidth: vertical ? (bar ? bar.barSize : 28) : (lay.item ? lay.item.implicitWidth + 8 : 0)
|
||||
implicitHeight: vertical ? (lay.item ? lay.item.implicitHeight + 8 : 0) : (bar ? bar.barSize : 26)
|
||||
implicitWidth: vertical ? (bar ? bar.barSize : Style.bar.sizeVertical) : (lay.item ? lay.item.implicitWidth + Style.spacing.controlGap : 0)
|
||||
implicitHeight: vertical ? (lay.item ? lay.item.implicitHeight + Style.spacing.controlGap : 0) : (bar ? bar.barSize : Style.bar.sizeHorizontal)
|
||||
|
||||
Loader {
|
||||
id: lay
|
||||
@@ -73,7 +73,7 @@ Item {
|
||||
Component {
|
||||
id: rowLayout
|
||||
Row {
|
||||
spacing: 4
|
||||
spacing: Style.space(4)
|
||||
LockGlyph { glyph: "A"; active: root.capsOn; visible: !root.hideWhenOff || root.capsOn }
|
||||
LockGlyph { glyph: "1"; active: root.numOn; visible: !root.hideWhenOff || root.numOn }
|
||||
LockGlyph { glyph: "S"; active: root.scrollOn; visible: !root.hideWhenOff || root.scrollOn }
|
||||
@@ -83,7 +83,7 @@ Item {
|
||||
Component {
|
||||
id: colLayout
|
||||
Column {
|
||||
spacing: 2
|
||||
spacing: Style.space(2)
|
||||
LockGlyph { glyph: "A"; active: root.capsOn; visible: !root.hideWhenOff || root.capsOn }
|
||||
LockGlyph { glyph: "1"; active: root.numOn; visible: !root.hideWhenOff || root.numOn }
|
||||
LockGlyph { glyph: "S"; active: root.scrollOn; visible: !root.hideWhenOff || root.scrollOn }
|
||||
|
||||
@@ -39,13 +39,13 @@ Item {
|
||||
property real maxLabelWidth: 180
|
||||
|
||||
visible: hasMedia
|
||||
implicitWidth: hasMedia ? row.implicitWidth + 14 : 0
|
||||
implicitWidth: hasMedia ? row.implicitWidth + Style.space(14) : 0
|
||||
implicitHeight: bar ? bar.barSize : 26
|
||||
|
||||
Row {
|
||||
id: row
|
||||
anchors.centerIn: parent
|
||||
spacing: 6
|
||||
spacing: Style.space(6)
|
||||
|
||||
Text {
|
||||
id: glyph
|
||||
@@ -119,29 +119,29 @@ Item {
|
||||
bar: root.bar
|
||||
owner: root
|
||||
open: root.popupOpen
|
||||
contentWidth: 320
|
||||
contentHeight: column.implicitHeight + 28
|
||||
contentWidth: popup.fittedContentWidth(Style.space(320))
|
||||
contentHeight: popup.fittedContentHeight(column.implicitHeight)
|
||||
|
||||
Column {
|
||||
id: column
|
||||
anchors.fill: parent
|
||||
spacing: 10
|
||||
spacing: Style.space(10)
|
||||
|
||||
Row {
|
||||
spacing: 10
|
||||
spacing: Style.space(10)
|
||||
width: parent.width
|
||||
|
||||
Rectangle {
|
||||
width: 64
|
||||
height: 64
|
||||
radius: 4
|
||||
color: Qt.rgba(root.bar.foreground.r, root.bar.foreground.g, root.bar.foreground.b, 0.08)
|
||||
border.color: Qt.rgba(root.bar.foreground.r, root.bar.foreground.g, root.bar.foreground.b, 0.2)
|
||||
border.width: 1
|
||||
width: Style.space(64)
|
||||
height: Style.space(64)
|
||||
radius: Style.spacing.labelGap
|
||||
color: Style.normalFillFor(root.bar.foreground, Color.accent)
|
||||
border.color: Style.normalBorderFor(root.bar.foreground, Color.accent)
|
||||
border.width: Style.normalBorderWidth
|
||||
|
||||
Image {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 2
|
||||
anchors.margins: Style.space(2)
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
asynchronous: true
|
||||
source: root.activePlayer && root.activePlayer.trackArtUrl ? root.activePlayer.trackArtUrl : ""
|
||||
@@ -159,8 +159,8 @@ Item {
|
||||
}
|
||||
|
||||
Column {
|
||||
spacing: 4
|
||||
width: parent.width - 74
|
||||
spacing: Style.space(4)
|
||||
width: parent.width - Style.space(74)
|
||||
|
||||
Text {
|
||||
text: root.title || "Nothing playing"
|
||||
@@ -196,13 +196,13 @@ Item {
|
||||
|
||||
Row {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
spacing: 6
|
||||
spacing: Style.space(6)
|
||||
|
||||
Button {
|
||||
iconText: ""
|
||||
foreground: root.bar.foreground
|
||||
horizontalPadding: 10
|
||||
verticalPadding: 6
|
||||
horizontalPadding: Style.spacing.controlPaddingX
|
||||
verticalPadding: Style.spacing.controlPaddingY
|
||||
enabled: root.activePlayer && root.activePlayer.canGoPrevious
|
||||
opacity: enabled ? 1.0 : 0.4
|
||||
onClicked: if (root.activePlayer) root.activePlayer.previous()
|
||||
@@ -211,9 +211,9 @@ Item {
|
||||
Button {
|
||||
iconText: root.activePlayer && root.activePlayer.isPlaying ? "" : ""
|
||||
foreground: root.bar.foreground
|
||||
horizontalPadding: 14
|
||||
verticalPadding: 6
|
||||
iconSize: 18
|
||||
horizontalPadding: Style.spacing.panelGap
|
||||
verticalPadding: Style.spacing.controlPaddingY
|
||||
iconSize: Style.font.iconLarge
|
||||
enabled: root.activePlayer && root.activePlayer.canTogglePlaying
|
||||
opacity: enabled ? 1.0 : 0.4
|
||||
onClicked: if (root.activePlayer) root.activePlayer.togglePlaying()
|
||||
@@ -222,8 +222,8 @@ Item {
|
||||
Button {
|
||||
iconText: ""
|
||||
foreground: root.bar.foreground
|
||||
horizontalPadding: 10
|
||||
verticalPadding: 6
|
||||
horizontalPadding: Style.spacing.controlPaddingX
|
||||
verticalPadding: Style.spacing.controlPaddingY
|
||||
enabled: root.activePlayer && root.activePlayer.canGoNext
|
||||
opacity: enabled ? 1.0 : 0.4
|
||||
onClicked: if (root.activePlayer) root.activePlayer.next()
|
||||
|
||||
@@ -367,8 +367,8 @@ Item {
|
||||
owner: root
|
||||
bar: root.bar
|
||||
open: root.popupOpen
|
||||
contentWidth: 320
|
||||
contentHeight: Math.min(560, panelColumn.implicitHeight + 28)
|
||||
contentWidth: panel.fittedContentWidth(Style.space(320))
|
||||
contentHeight: panel.fittedContentHeight(panelColumn.implicitHeight, Style.space(560))
|
||||
|
||||
PanelKeyCatcher {
|
||||
id: keyCatcher
|
||||
@@ -393,12 +393,12 @@ Item {
|
||||
Column {
|
||||
id: panelColumn
|
||||
width: scrollArea.availableWidth
|
||||
spacing: 14
|
||||
spacing: Style.space(14)
|
||||
|
||||
// ---- Brightness ----
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: 6
|
||||
spacing: Style.space(6)
|
||||
|
||||
PanelSectionHeader {
|
||||
text: "Brightness"
|
||||
@@ -411,7 +411,7 @@ Item {
|
||||
id: brightnessRow
|
||||
visible: root.brightnessAvailable
|
||||
width: parent.width
|
||||
height: brightnessInner.implicitHeight + 8
|
||||
height: brightnessInner.implicitHeight + Style.spacing.controlGap
|
||||
hasCursor: root.focusSection === "brightness" && root.selectedIndex === -1
|
||||
onHasCursorChanged: if (hasCursor) root.ensureCursorVisible(brightnessRow)
|
||||
foreground: root.bar.foreground
|
||||
@@ -420,16 +420,16 @@ Item {
|
||||
Row {
|
||||
id: brightnessInner
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: 6
|
||||
anchors.rightMargin: 6
|
||||
spacing: 8
|
||||
anchors.leftMargin: Style.space(6)
|
||||
anchors.rightMargin: Style.space(6)
|
||||
spacing: Style.space(8)
|
||||
|
||||
Text {
|
||||
text: ""
|
||||
color: root.bar.foreground
|
||||
font.family: root.bar.fontFamily
|
||||
font.pixelSize: Style.font.heading
|
||||
width: 22
|
||||
width: Style.space(22)
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
@@ -437,7 +437,7 @@ Item {
|
||||
PanelSlider {
|
||||
id: brightnessSlider
|
||||
bar: root.bar
|
||||
width: parent.width - 22 - brightnessLabel.width - 16
|
||||
width: parent.width - Style.space(22) - brightnessLabel.width - Style.space(16)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
minimum: 1
|
||||
maximum: 100
|
||||
@@ -457,7 +457,7 @@ Item {
|
||||
color: root.bar.foreground
|
||||
font.family: root.bar.fontFamily
|
||||
font.pixelSize: Style.font.bodySmall
|
||||
width: 36
|
||||
width: Style.space(36)
|
||||
horizontalAlignment: Text.AlignRight
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
@@ -483,7 +483,7 @@ Item {
|
||||
// ---- Scale ----
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: 6
|
||||
spacing: Style.space(6)
|
||||
|
||||
PanelSectionHeader {
|
||||
text: "Scale"
|
||||
@@ -494,7 +494,7 @@ Item {
|
||||
|
||||
Row {
|
||||
width: parent.width
|
||||
spacing: 6
|
||||
spacing: Style.space(6)
|
||||
|
||||
Repeater {
|
||||
model: root.scaleValues
|
||||
@@ -503,7 +503,7 @@ Item {
|
||||
required property string modelData
|
||||
required property int index
|
||||
|
||||
width: (panelColumn.width - 30) / 6
|
||||
width: (panelColumn.width - Style.space(30)) / 6
|
||||
text: modelData + "x"
|
||||
foreground: root.bar.foreground
|
||||
background: "transparent"
|
||||
@@ -512,7 +512,7 @@ Item {
|
||||
fontFamily: root.bar.fontFamily
|
||||
fontSize: Style.font.bodySmall
|
||||
horizontalPadding: 0
|
||||
verticalPadding: 6
|
||||
verticalPadding: Style.spacing.controlPaddingY
|
||||
active: root.normalizeScale(root.monitorScale) === root.normalizeScale(modelData)
|
||||
hasCursor: root.focusSection === "scale" && root.selectedIndex === index
|
||||
onClicked: root.setScale(modelData)
|
||||
@@ -530,7 +530,7 @@ Item {
|
||||
// ---- Monitors ----
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: 6
|
||||
spacing: Style.space(6)
|
||||
visible: root.displays.length > 0
|
||||
|
||||
PanelSectionHeader {
|
||||
|
||||
@@ -69,8 +69,8 @@ Item {
|
||||
// Mouse hover and keyboard nav both mutate this state at the root; items
|
||||
// never read containsMouse for visuals. See CursorSurface for the
|
||||
// shared chrome (fill / border) shared by NetworkRow and DnsProviderPill.
|
||||
readonly property color hoverFill: bar ? Qt.rgba(bar.foreground.r, bar.foreground.g, bar.foreground.b, 0.08) : "transparent"
|
||||
readonly property color selectedFill: bar ? Qt.rgba(bar.foreground.r, bar.foreground.g, bar.foreground.b, 0.18) : "transparent"
|
||||
readonly property color hoverFill: bar ? Style.hoverFillFor(bar.foreground, Color.accent) : "transparent"
|
||||
readonly property color selectedFill: bar ? Style.selectedFillFor(bar.foreground, Color.accent) : "transparent"
|
||||
|
||||
// The panel below is its own layer-shell with Exclusive keyboard focus,
|
||||
// so Hyprland grants focus when the surface is mapped (popupOpen flips
|
||||
@@ -599,8 +599,8 @@ iwctl station "$station" get-networks rssi-dbms 2>/dev/null \\
|
||||
owner: root
|
||||
bar: root.bar
|
||||
open: root.popupOpen
|
||||
contentWidth: 340
|
||||
contentHeight: column.implicitHeight + 28
|
||||
contentWidth: panel.fittedContentWidth(Style.space(340))
|
||||
contentHeight: panel.fittedContentHeight(column.implicitHeight)
|
||||
|
||||
// Catches all unhandled keys for keyboard navigation. AfterItem priority
|
||||
// lets the passphrase TextField (a child via focus chain) get its keys
|
||||
@@ -647,7 +647,7 @@ iwctl station "$station" get-networks rssi-dbms 2>/dev/null \\
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
spacing: 12
|
||||
spacing: Style.space(12)
|
||||
|
||||
// Header — interface name + type, refresh on the right.
|
||||
Item {
|
||||
@@ -658,7 +658,7 @@ iwctl station "$station" get-networks rssi-dbms 2>/dev/null \\
|
||||
id: headerInfo
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 10
|
||||
spacing: Style.space(10)
|
||||
|
||||
Text {
|
||||
text: root.icon
|
||||
@@ -669,7 +669,7 @@ iwctl station "$station" get-networks rssi-dbms 2>/dev/null \\
|
||||
}
|
||||
|
||||
Column {
|
||||
spacing: 2
|
||||
spacing: Style.space(2)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
Text {
|
||||
@@ -706,9 +706,9 @@ iwctl station "$station" get-networks rssi-dbms 2>/dev/null \\
|
||||
tooltipBackground: root.bar.background
|
||||
tooltipForeground: root.bar.foreground
|
||||
foreground: root.bar.foreground
|
||||
horizontalPadding: 8
|
||||
verticalPadding: 4
|
||||
iconSize: 14
|
||||
horizontalPadding: Style.spacing.controlGap
|
||||
verticalPadding: Style.spacing.labelGap
|
||||
iconSize: Style.font.icon
|
||||
active: root.scanning
|
||||
onClicked: root.refresh()
|
||||
}
|
||||
@@ -724,8 +724,8 @@ iwctl station "$station" get-networks rssi-dbms 2>/dev/null \\
|
||||
visible: !!root.info.iface
|
||||
width: parent.width
|
||||
columns: 2
|
||||
columnSpacing: 14
|
||||
rowSpacing: 4
|
||||
columnSpacing: Style.space(14)
|
||||
rowSpacing: Style.space(4)
|
||||
|
||||
// IP address.
|
||||
Text {
|
||||
@@ -815,7 +815,7 @@ iwctl station "$station" get-networks rssi-dbms 2>/dev/null \\
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: 8
|
||||
spacing: Style.space(8)
|
||||
|
||||
PanelSectionHeader {
|
||||
text: "DNS provider"
|
||||
@@ -825,7 +825,7 @@ iwctl station "$station" get-networks rssi-dbms 2>/dev/null \\
|
||||
|
||||
Row {
|
||||
width: parent.width
|
||||
spacing: 6
|
||||
spacing: Style.space(6)
|
||||
|
||||
DnsProviderPill {
|
||||
provider: "DHCP"
|
||||
@@ -879,8 +879,8 @@ iwctl station "$station" get-networks rssi-dbms 2>/dev/null \\
|
||||
id: networkList
|
||||
visible: root.wifiStationAvailable
|
||||
width: parent.width
|
||||
height: Math.min(contentHeight, 240)
|
||||
spacing: 4
|
||||
height: Math.min(contentHeight, Style.space(240))
|
||||
spacing: Style.space(4)
|
||||
clip: true
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
interactive: contentHeight > height
|
||||
@@ -925,8 +925,8 @@ iwctl station "$station" get-networks rssi-dbms 2>/dev/null \\
|
||||
tooltipBackground: root.bar.background
|
||||
tooltipForeground: root.bar.foreground
|
||||
fontFamily: root.bar.fontFamily
|
||||
horizontalPadding: 10
|
||||
verticalPadding: 6
|
||||
horizontalPadding: Style.spacing.controlPaddingX
|
||||
verticalPadding: Style.spacing.controlPaddingY
|
||||
|
||||
// Map the panel's domain semantics onto Button's structural props:
|
||||
// `current DNS` is the pill's `active` fill; the keyboard cursor lights
|
||||
@@ -982,7 +982,7 @@ iwctl station "$station" get-networks rssi-dbms 2>/dev/null \\
|
||||
return Qt.darker(root.bar.foreground, 1.5)
|
||||
}
|
||||
|
||||
implicitHeight: rowBody.implicitHeight + (isPasswordOpen ? passwordPanel.implicitHeight + 6 : 0)
|
||||
implicitHeight: rowBody.implicitHeight + (isPasswordOpen ? passwordPanel.implicitHeight + Style.spacing.md : 0)
|
||||
|
||||
MouseArea {
|
||||
id: rowMouse
|
||||
@@ -1040,9 +1040,9 @@ iwctl known-networks list 2>/dev/null \\
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.leftMargin: 10
|
||||
anchors.rightMargin: 10
|
||||
implicitHeight: Math.max(networkIcon.implicitHeight, networkInfo.implicitHeight, forgetBtn.implicitHeight) + 12
|
||||
anchors.leftMargin: Style.space(10)
|
||||
anchors.rightMargin: Style.space(10)
|
||||
implicitHeight: Math.max(networkIcon.implicitHeight, networkInfo.implicitHeight, forgetBtn.implicitHeight) + Style.spacing.rowPaddingX
|
||||
|
||||
Text {
|
||||
id: networkIcon
|
||||
@@ -1072,12 +1072,12 @@ iwctl known-networks list 2>/dev/null \\
|
||||
// Shows a lock glyph on the right for protected networks that
|
||||
// aren't currently connected. Once connected, the forget X takes
|
||||
// its place (and 'protected' is implied by the fact we're on it).
|
||||
// Same 22-wide right-anchored centered geometry as forgetBtn so the
|
||||
// glyph centers line up across rows.
|
||||
// Same action-sized right-anchored centered geometry as forgetBtn so
|
||||
// the glyph centers line up across rows.
|
||||
Text {
|
||||
id: lockIndicator
|
||||
visible: row.isProtected && !row.isConnected
|
||||
width: 22
|
||||
width: Style.space(22)
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
@@ -1089,13 +1089,13 @@ iwctl known-networks list 2>/dev/null \\
|
||||
|
||||
Column {
|
||||
id: networkInfo
|
||||
spacing: 1
|
||||
spacing: Style.space(1)
|
||||
anchors.left: networkIcon.right
|
||||
anchors.leftMargin: 10
|
||||
anchors.leftMargin: Style.space(10)
|
||||
anchors.right: forgetBtn.visible ? forgetBtn.left
|
||||
: lockIndicator.visible ? lockIndicator.left
|
||||
: parent.right
|
||||
anchors.rightMargin: (forgetBtn.visible || lockIndicator.visible) ? 8 : 0
|
||||
anchors.rightMargin: (forgetBtn.visible || lockIndicator.visible) ? Style.space(8) : 0
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
Text {
|
||||
@@ -1133,24 +1133,24 @@ iwctl known-networks list 2>/dev/null \\
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: rowMouse.bottom
|
||||
anchors.leftMargin: 10
|
||||
anchors.rightMargin: 10
|
||||
anchors.topMargin: 4
|
||||
implicitHeight: pwField.implicitHeight + 8
|
||||
anchors.leftMargin: Style.space(10)
|
||||
anchors.rightMargin: Style.space(10)
|
||||
anchors.topMargin: Style.space(4)
|
||||
implicitHeight: pwField.implicitHeight + Style.spacing.rowGap
|
||||
|
||||
TextField {
|
||||
id: pwField
|
||||
anchors.left: parent.left
|
||||
anchors.right: connectPwBtn.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.rightMargin: 6
|
||||
anchors.rightMargin: Style.space(6)
|
||||
password: true
|
||||
placeholderText: "Passphrase"
|
||||
font.family: root.bar.fontFamily
|
||||
font.pixelSize: Style.font.body
|
||||
foreground: root.bar.foreground
|
||||
horizontalPadding: 8
|
||||
verticalPadding: 6
|
||||
horizontalPadding: Style.spacing.controlGap
|
||||
verticalPadding: Style.spacing.controlPaddingY
|
||||
enabled: !row.isBusy
|
||||
|
||||
onAccepted: {
|
||||
|
||||
@@ -64,8 +64,8 @@ Item {
|
||||
// rest of the notification stack).
|
||||
readonly property color colForeground: Color.foreground
|
||||
readonly property color colDim: Qt.darker(Color.foreground, 1.4)
|
||||
readonly property color colBorder: Qt.rgba(Color.foreground.r, Color.foreground.g, Color.foreground.b, 0.18)
|
||||
readonly property color colSurface: Qt.rgba(Color.foreground.r, Color.foreground.g, Color.foreground.b, 0.06)
|
||||
readonly property color colBorder: Style.normalBorderFor(Color.foreground, Color.accent)
|
||||
readonly property color colSurface: Style.normalFillFor(Color.foreground, Color.accent)
|
||||
readonly property color colAccent: Color.accent
|
||||
readonly property int cardRadius: notificationService ? notificationService.cornerRadius : 0
|
||||
|
||||
@@ -109,17 +109,17 @@ Item {
|
||||
bar: root.bar
|
||||
owner: root
|
||||
open: root.popupOpen
|
||||
contentWidth: 440
|
||||
contentHeight: 540
|
||||
contentWidth: popup.fittedContentWidth(Style.space(440))
|
||||
contentHeight: popup.cappedContentHeight(Style.space(540))
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
spacing: 10
|
||||
spacing: Style.space(10)
|
||||
|
||||
// ----------------------------------------- header
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 8
|
||||
spacing: Style.space(8)
|
||||
|
||||
Text {
|
||||
text: "Notifications"
|
||||
@@ -133,18 +133,18 @@ Item {
|
||||
|
||||
Rectangle {
|
||||
id: dndPill
|
||||
Layout.preferredHeight: 24
|
||||
Layout.preferredWidth: dndLabel.implicitWidth + dndGlyph.implicitWidth + 18
|
||||
radius: Math.min(12, root.cardRadius + 6)
|
||||
Layout.preferredHeight: Math.max(Style.space(24), Style.font.bodySmall + Style.spacing.controlPaddingY * 2)
|
||||
Layout.preferredWidth: dndLabel.implicitWidth + dndGlyph.implicitWidth + Style.space(18)
|
||||
radius: Math.min(Style.space(12), root.cardRadius + Style.space(6))
|
||||
color: dndOn ? root.colAccent : root.colSurface
|
||||
border.color: dndOn ? root.colAccent : root.colBorder
|
||||
border.width: 1
|
||||
border.width: Style.normalBorderWidth
|
||||
|
||||
readonly property bool dndOn: !!root.notificationService && root.notificationService.doNotDisturb
|
||||
|
||||
Row {
|
||||
anchors.centerIn: parent
|
||||
spacing: 4
|
||||
spacing: Style.space(4)
|
||||
|
||||
Text {
|
||||
id: dndGlyph
|
||||
@@ -190,7 +190,7 @@ Item {
|
||||
readonly property bool isActive: root.activeTab === modelData.key
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 30
|
||||
Layout.preferredHeight: Math.max(Style.space(30), Style.font.body + Style.spacing.controlPaddingY * 2)
|
||||
color: "transparent"
|
||||
|
||||
Text {
|
||||
@@ -206,7 +206,7 @@ Item {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
height: 2
|
||||
height: Math.max(1, Style.space(2))
|
||||
color: parent.isActive ? root.colAccent : root.colBorder
|
||||
opacity: parent.isActive ? 1 : 0.4
|
||||
}
|
||||
@@ -225,17 +225,17 @@ Item {
|
||||
Layout.fillWidth: true
|
||||
visible: (root.activeTab === "pending" && root.pendingCount > 0)
|
||||
|| (root.activeTab === "past" && root.pastCount > 0)
|
||||
spacing: 8
|
||||
spacing: Style.space(8)
|
||||
|
||||
Item { Layout.fillWidth: true }
|
||||
|
||||
Rectangle {
|
||||
Layout.preferredWidth: actionLabel.implicitWidth + 16
|
||||
Layout.preferredHeight: 22
|
||||
radius: Math.min(6, root.cardRadius)
|
||||
Layout.preferredWidth: actionLabel.implicitWidth + Style.space(16)
|
||||
Layout.preferredHeight: Math.max(Style.space(22), Style.font.bodySmall + Style.spacing.controlPaddingY * 2)
|
||||
radius: Math.min(Style.space(6), root.cardRadius)
|
||||
color: actionArea.containsMouse ? root.colBorder : "transparent"
|
||||
border.color: root.colBorder
|
||||
border.width: 1
|
||||
border.width: Style.normalBorderWidth
|
||||
|
||||
Text {
|
||||
id: actionLabel
|
||||
@@ -266,7 +266,7 @@ Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
clip: true
|
||||
spacing: 8
|
||||
spacing: Style.space(8)
|
||||
|
||||
readonly property bool onPending: root.activeTab === "pending"
|
||||
model: !root.notificationService ? null
|
||||
@@ -291,11 +291,11 @@ Item {
|
||||
readonly property string sanitizedBody: root.sanitizeBody(body, app, appIcon)
|
||||
|
||||
width: listView.width
|
||||
implicitHeight: rowContent.implicitHeight + 20
|
||||
implicitHeight: rowContent.implicitHeight + Style.spacing.panelGap
|
||||
radius: root.cardRadius
|
||||
color: "transparent"
|
||||
border.color: root.colBorder
|
||||
border.width: 1
|
||||
border.width: Style.normalBorderWidth
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
@@ -308,13 +308,14 @@ Item {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.leftMargin: 12
|
||||
anchors.rightMargin: 12
|
||||
spacing: 10
|
||||
anchors.leftMargin: Style.space(12)
|
||||
anchors.rightMargin: Style.space(12)
|
||||
spacing: Style.space(10)
|
||||
|
||||
Item {
|
||||
Layout.preferredWidth: 32
|
||||
Layout.preferredHeight: 32
|
||||
id: imageSlot
|
||||
Layout.preferredWidth: Style.space(32)
|
||||
Layout.preferredHeight: Style.space(32)
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
// Hide on icon load failure so unresolved themed-icon names
|
||||
// don't render Qt's broken-image placeholder.
|
||||
@@ -325,8 +326,8 @@ Item {
|
||||
anchors.fill: parent
|
||||
source: rowCard.hasMedia ? rowCard.image : rowCard.smallIconSource
|
||||
fillMode: rowCard.hasMedia ? Image.PreserveAspectCrop : Image.PreserveAspectFit
|
||||
sourceSize.width: 32 * Screen.devicePixelRatio
|
||||
sourceSize.height: 32 * Screen.devicePixelRatio
|
||||
sourceSize.width: imageSlot.width * Screen.devicePixelRatio
|
||||
sourceSize.height: imageSlot.height * Screen.devicePixelRatio
|
||||
asynchronous: true
|
||||
smooth: true
|
||||
}
|
||||
@@ -334,7 +335,7 @@ Item {
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 2
|
||||
spacing: Style.space(2)
|
||||
|
||||
Text {
|
||||
Layout.fillWidth: true
|
||||
@@ -364,8 +365,8 @@ Item {
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.preferredWidth: 18
|
||||
Layout.preferredHeight: 18
|
||||
Layout.preferredWidth: Style.space(18)
|
||||
Layout.preferredHeight: Style.space(18)
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
radius: Math.min(4, root.cardRadius)
|
||||
color: rowCloseArea.containsMouse ? root.colBorder : "transparent"
|
||||
@@ -402,16 +403,14 @@ Item {
|
||||
|
||||
ColumnLayout {
|
||||
anchors.centerIn: parent
|
||||
spacing: 6
|
||||
spacing: Style.space(6)
|
||||
|
||||
Text {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: ""
|
||||
font.family: root.bar ? root.bar.fontFamily : ""
|
||||
color: root.colBorder
|
||||
// Deliberately oversized empty-state glyph; doesn't follow the
|
||||
// Style.font.* scale because it's a one-off decorative element.
|
||||
font.pixelSize: 36
|
||||
font.pixelSize: Style.font.displayLarge
|
||||
}
|
||||
|
||||
Text {
|
||||
@@ -420,8 +419,6 @@ Item {
|
||||
? "Nothing waiting for you"
|
||||
: "Nothing recent"
|
||||
font.family: root.bar ? root.bar.fontFamily : ""
|
||||
? "Nothing waiting for you"
|
||||
: "No past notifications"
|
||||
color: root.colDim
|
||||
font.pixelSize: Style.font.body
|
||||
}
|
||||
|
||||
@@ -177,13 +177,13 @@ Item {
|
||||
bar: root.bar
|
||||
open: root.popupOpen
|
||||
triggerMode: "hover"
|
||||
contentWidth: 320
|
||||
contentHeight: detailColumn.implicitHeight + 28
|
||||
contentWidth: popup.fittedContentWidth(Style.space(320))
|
||||
contentHeight: popup.fittedContentHeight(detailColumn.implicitHeight)
|
||||
|
||||
Column {
|
||||
id: detailColumn
|
||||
anchors.fill: parent
|
||||
spacing: 10
|
||||
spacing: Style.space(10)
|
||||
|
||||
Text {
|
||||
text: "System"
|
||||
@@ -213,7 +213,7 @@ Item {
|
||||
|
||||
Row {
|
||||
width: parent.width
|
||||
spacing: 6
|
||||
spacing: Style.space(6)
|
||||
Text {
|
||||
text: "Load"
|
||||
color: Qt.darker(root.bar.foreground, 1.5)
|
||||
@@ -239,7 +239,7 @@ Item {
|
||||
property color barFg: "#cacccc"
|
||||
property string fontFamily: "JetBrainsMono Nerd Font"
|
||||
|
||||
spacing: 4
|
||||
spacing: Style.space(4)
|
||||
|
||||
Row {
|
||||
width: parent.width
|
||||
@@ -261,7 +261,7 @@ Item {
|
||||
Canvas {
|
||||
id: detailCanvas
|
||||
width: parent.width
|
||||
height: 40
|
||||
height: Style.space(40)
|
||||
property var history: detail.history
|
||||
onHistoryChanged: requestPaint()
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ Item {
|
||||
readonly property string reportHumidity: current ? (current.humidity + "%") : ""
|
||||
|
||||
visible: label !== ""
|
||||
implicitWidth: button.implicitWidth + 8
|
||||
implicitWidth: button.implicitWidth + Style.spacing.controlGap
|
||||
implicitHeight: button.implicitHeight
|
||||
|
||||
function setting(name, fallback) {
|
||||
@@ -322,15 +322,22 @@ Item {
|
||||
open: root.popupOpen
|
||||
centerOnBar: true
|
||||
triggerMode: "click"
|
||||
contentWidth: 480
|
||||
contentHeight: card.implicitHeight + 28
|
||||
margin: 24
|
||||
contentWidth: popup.fittedContentWidth(Style.space(480))
|
||||
contentHeight: popup.fittedContentHeight(weatherColumn.implicitHeight)
|
||||
borderColor: Color.notifications.border
|
||||
|
||||
Column {
|
||||
id: card
|
||||
Flickable {
|
||||
id: weatherScroll
|
||||
anchors.fill: parent
|
||||
spacing: 14
|
||||
contentWidth: width
|
||||
contentHeight: weatherColumn.implicitHeight
|
||||
clip: true
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
|
||||
Column {
|
||||
id: weatherColumn
|
||||
width: weatherScroll.width
|
||||
spacing: Style.space(14)
|
||||
|
||||
// ---- Hero row: big icon + temp on the left; location and stats stacked on the right.
|
||||
Item {
|
||||
@@ -340,9 +347,9 @@ Item {
|
||||
Row {
|
||||
id: heroLeft
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 16
|
||||
anchors.leftMargin: Style.space(16)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 16
|
||||
spacing: Style.space(16)
|
||||
|
||||
Text {
|
||||
id: heroIcon
|
||||
@@ -358,7 +365,7 @@ Item {
|
||||
|
||||
Row {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 2
|
||||
spacing: Style.space(2)
|
||||
|
||||
Text {
|
||||
id: tempBig
|
||||
@@ -376,7 +383,7 @@ Item {
|
||||
font.family: root.bar.fontFamily
|
||||
font.pixelSize: Style.font.display
|
||||
anchors.top: tempBig.top
|
||||
anchors.topMargin: 10
|
||||
anchors.topMargin: Style.space(10)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -384,13 +391,13 @@ Item {
|
||||
Column {
|
||||
id: heroRight
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 20
|
||||
anchors.rightMargin: Style.space(20)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 12
|
||||
spacing: Style.space(12)
|
||||
|
||||
Row {
|
||||
visible: root.reportLocation !== ""
|
||||
spacing: 6
|
||||
spacing: Style.space(6)
|
||||
|
||||
Text {
|
||||
text: "" // nf-fa-map_marker
|
||||
@@ -411,10 +418,10 @@ Item {
|
||||
|
||||
Row {
|
||||
visible: !!root.current
|
||||
spacing: 36
|
||||
spacing: Style.space(36)
|
||||
|
||||
Column {
|
||||
spacing: 5
|
||||
spacing: Style.space(5)
|
||||
Text {
|
||||
text: "FEELS"
|
||||
color: Qt.darker(root.bar.foreground, 1.5)
|
||||
@@ -431,7 +438,7 @@ Item {
|
||||
}
|
||||
|
||||
Column {
|
||||
spacing: 5
|
||||
spacing: Style.space(5)
|
||||
Text {
|
||||
text: "WIND"
|
||||
color: Qt.darker(root.bar.foreground, 1.5)
|
||||
@@ -448,7 +455,7 @@ Item {
|
||||
}
|
||||
|
||||
Column {
|
||||
spacing: 5
|
||||
spacing: Style.space(5)
|
||||
Text {
|
||||
text: "HUMID"
|
||||
color: Qt.darker(root.bar.foreground, 1.5)
|
||||
@@ -480,7 +487,7 @@ Item {
|
||||
Rectangle {
|
||||
visible: root.forecastDays.length > 0
|
||||
width: parent.width
|
||||
height: 1
|
||||
height: Style.spacing.hairline
|
||||
color: root.bar.foreground
|
||||
opacity: 0.12
|
||||
}
|
||||
@@ -495,7 +502,7 @@ Item {
|
||||
Row {
|
||||
id: forecastRow
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
spacing: 44
|
||||
spacing: Style.space(44)
|
||||
|
||||
Repeater {
|
||||
model: root.forecastDays
|
||||
@@ -503,7 +510,7 @@ Item {
|
||||
Row {
|
||||
required property var modelData
|
||||
required property int index
|
||||
spacing: 10
|
||||
spacing: Style.space(10)
|
||||
|
||||
Text {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
@@ -515,7 +522,7 @@ Item {
|
||||
|
||||
Column {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 2
|
||||
spacing: Style.space(2)
|
||||
|
||||
Text {
|
||||
text: root.dayName(modelData.date).toUpperCase()
|
||||
@@ -526,7 +533,7 @@ Item {
|
||||
}
|
||||
|
||||
Row {
|
||||
spacing: 6
|
||||
spacing: Style.space(6)
|
||||
|
||||
Text {
|
||||
text: root.bareTempForDay(modelData, "max")
|
||||
@@ -548,6 +555,7 @@ Item {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Poll the weather pill text/class every minute. Local to this widget.
|
||||
Process {
|
||||
|
||||
@@ -23,12 +23,12 @@ Item {
|
||||
property color border: foreground
|
||||
readonly property int cornerRadius: Style.cornerRadius
|
||||
property string fontFamily: Quickshell.env("OMARCHY_MENU_FONT") || "monospace"
|
||||
property int contentMargin: 18
|
||||
property int headerHeight: 34
|
||||
property int contentSpacing: 6
|
||||
property int cardWidth: 800
|
||||
property int cardHeight: 600
|
||||
property int rowHeight: 50
|
||||
property int contentMargin: Style.spacing.panelPadding
|
||||
property int headerHeight: Math.max(Style.space(34), Style.font.title + Style.spacing.controlPaddingY * 2)
|
||||
property int contentSpacing: Style.spacing.md
|
||||
property int cardWidth: Math.min(Style.space(800), panel.width - Style.gapsOut * 2)
|
||||
property int cardHeight: Math.min(Style.space(600), panel.height - Style.gapsOut * 2)
|
||||
property int rowHeight: Math.max(Style.space(50), Style.font.body + Style.font.caption + Style.spacing.rowPaddingX * 2)
|
||||
|
||||
function open(payloadJson) {
|
||||
root.opened = true
|
||||
@@ -181,7 +181,7 @@ Item {
|
||||
anchors.centerIn: parent
|
||||
color: root.background
|
||||
border.color: root.border
|
||||
border.width: 2
|
||||
border.width: Math.max(1, Style.space(2))
|
||||
|
||||
MouseArea { anchors.fill: parent; onClicked: {} }
|
||||
|
||||
@@ -260,7 +260,7 @@ Item {
|
||||
height: parent.height
|
||||
model: displayModel
|
||||
clip: true
|
||||
spacing: 4
|
||||
spacing: Style.space(4)
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
|
||||
delegate: Rectangle {
|
||||
@@ -273,31 +273,33 @@ Item {
|
||||
width: ListView.view.width
|
||||
height: root.rowHeight
|
||||
radius: root.cornerRadius
|
||||
color: index === root.selectedIndex ? root.withAlpha(root.foreground, 0.08) : root.withAlpha(root.foreground, mouseArea.containsMouse ? 0.045 : 0)
|
||||
color: index === root.selectedIndex ? Style.hoverFillFor(root.foreground, root.accent) : "transparent"
|
||||
border.color: index === root.selectedIndex ? Style.hoverBorderFor(root.foreground, root.accent) : "transparent"
|
||||
border.width: index === root.selectedIndex ? Style.hoverBorderWidth : 0
|
||||
|
||||
Rectangle {
|
||||
visible: false
|
||||
width: 4
|
||||
height: parent.height - 18
|
||||
radius: Math.min(root.cornerRadius, 4)
|
||||
width: Style.space(4)
|
||||
height: parent.height - Style.space(18)
|
||||
radius: Math.min(root.cornerRadius, Style.space(4))
|
||||
color: root.accent
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 8
|
||||
anchors.leftMargin: Style.space(8)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: 12
|
||||
anchors.rightMargin: 12
|
||||
anchors.topMargin: 8
|
||||
anchors.bottomMargin: 8
|
||||
anchors.leftMargin: Style.space(12)
|
||||
anchors.rightMargin: Style.space(12)
|
||||
anchors.topMargin: Style.space(8)
|
||||
anchors.bottomMargin: Style.space(8)
|
||||
|
||||
Text {
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
text: parent.parent.isPassword ? "••••••••" : (parent.parent.previewType === "text" ? parent.parent.previewText : "Image")
|
||||
color: index === root.selectedIndex ? root.accent : root.foreground
|
||||
color: index === root.selectedIndex ? Style.hoverStateColor(root.foreground, root.accent) : root.foreground
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.title
|
||||
font.italic: parent.parent.previewType === "file" || parent.parent.isPassword
|
||||
@@ -313,6 +315,7 @@ Item {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onContainsMouseChanged: if (containsMouse) root.selectedIndex = index
|
||||
onClicked: {
|
||||
root.selectedIndex = index
|
||||
root.activateIndex(index)
|
||||
@@ -327,7 +330,7 @@ Item {
|
||||
radius: root.cornerRadius
|
||||
color: root.withAlpha(root.background, 0.5)
|
||||
border.color: root.withAlpha(root.border, 0.1)
|
||||
border.width: 1
|
||||
border.width: Style.normalBorderWidth
|
||||
clip: true
|
||||
|
||||
property var activeRow: displayModel.count > 0 && root.selectedIndex >= 0 && root.selectedIndex < displayModel.count ? displayModel.get(root.selectedIndex) : null
|
||||
@@ -335,7 +338,7 @@ Item {
|
||||
Text {
|
||||
visible: parent.activeRow && parent.activeRow.previewType === "text"
|
||||
anchors.fill: parent
|
||||
anchors.margins: 16
|
||||
anchors.margins: Style.space(16)
|
||||
text: parent.activeRow ? (parent.activeRow.isPassword ? "••••••••" : parent.activeRow.previewText) : ""
|
||||
color: root.foreground
|
||||
font.family: root.fontFamily
|
||||
@@ -348,7 +351,7 @@ Item {
|
||||
Image {
|
||||
visible: parent.activeRow && parent.activeRow.previewType === "file"
|
||||
anchors.fill: parent
|
||||
anchors.margins: 16
|
||||
anchors.margins: Style.space(16)
|
||||
source: parent.activeRow ? parent.activeRow.previewImage : ""
|
||||
fillMode: Image.PreserveAspectFit
|
||||
}
|
||||
@@ -357,7 +360,7 @@ Item {
|
||||
|
||||
Column {
|
||||
anchors.centerIn: parent
|
||||
spacing: 8
|
||||
spacing: Style.space(8)
|
||||
visible: displayModel.count === 0
|
||||
|
||||
Text {
|
||||
|
||||
@@ -334,18 +334,18 @@ Item {
|
||||
ScrollView {
|
||||
id: scrollArea
|
||||
anchors.fill: parent
|
||||
anchors.margins: 18
|
||||
anchors.margins: Style.space(18)
|
||||
clip: true
|
||||
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||
|
||||
Column {
|
||||
width: scrollArea.availableWidth
|
||||
spacing: 22
|
||||
spacing: Style.space(22)
|
||||
|
||||
// ---- Header ------------------------------------------------------
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: 4
|
||||
spacing: Style.space(4)
|
||||
|
||||
Text {
|
||||
text: "Omarchy shell · dev gallery"
|
||||
@@ -369,7 +369,7 @@ Item {
|
||||
// ---- Kit conventions ---------------------------------------------
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: 8
|
||||
spacing: Style.space(8)
|
||||
|
||||
Text {
|
||||
text: "Conventions"
|
||||
@@ -381,7 +381,7 @@ Item {
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
implicitHeight: conventionsCol.implicitHeight + 24
|
||||
implicitHeight: conventionsCol.implicitHeight + Style.spacing.rowPaddingX * 2
|
||||
color: Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, 0.04)
|
||||
radius: Style.cornerRadius
|
||||
border.color: Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, 0.10)
|
||||
@@ -392,9 +392,9 @@ Item {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.leftMargin: 14
|
||||
anchors.rightMargin: 14
|
||||
spacing: 8
|
||||
anchors.leftMargin: Style.space(14)
|
||||
anchors.rightMargin: Style.space(14)
|
||||
spacing: Style.space(8)
|
||||
|
||||
Text {
|
||||
width: parent.width
|
||||
@@ -402,7 +402,7 @@ Item {
|
||||
color: Qt.darker(root.foreground, 1.4)
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.bodySmall
|
||||
text: "Theme. qs.Commons.Style exposes cornerRadius (mirrored from Hyprland's decoration:rounding), focusBorderColor / focusFillColor / focusBorderWidth (derived from Color.accent), and hotFill (the shared hover/cursor tint). qs.Commons.Color exposes foreground / background / accent / urgent plus per-surface roles. Components default-bind to these so a caller with no overrides matches the active theme."
|
||||
text: "Theme. qs.Commons.Style exposes cornerRadius plus shared normal / hover-cursor / selected / focus state tokens (state colors, fill alphas, border widths, and border alphas), spacing tokens, typography, and bar dimensions. Focus defaults to hover-cursor; selected borders are off by default. Border widths are the theme-level on/off switch for state borders. qs.Commons.Color exposes foreground / background / accent / urgent plus per-surface roles. Components default-bind to these so a caller with no overrides matches the active theme."
|
||||
}
|
||||
Text {
|
||||
width: parent.width
|
||||
@@ -410,7 +410,7 @@ Item {
|
||||
color: Qt.darker(root.foreground, 1.4)
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.bodySmall
|
||||
text: "Single cursor. Most reusable panel primitives expose hasCursor: bool and emit hovered(bool); a few (like PanelSlider) defer to their own focus handling. The panel root owns focusSection + selectedIndex; each element binds hasCursor: root.focusSection === 'X' && root.selectedIndex === N, and onHovered updates the same state. One highlight on screen, keyboard and mouse always agree. See plugins/bar/widgets/audioPanel.qml for the canonical recipe."
|
||||
text: "Single cursor. Most reusable panel primitives expose hasCursor: bool and emit hovered(bool); composed rows (including sliders) wrap their content in CursorSurface. The panel root owns focusSection + selectedIndex; each element binds hasCursor: root.focusSection === 'X' && root.selectedIndex === N, and onHovered updates the same state. One highlight on screen, keyboard and mouse always agree. See plugins/bar/widgets/audioPanel.qml for the canonical recipe."
|
||||
}
|
||||
Text {
|
||||
width: parent.width
|
||||
@@ -437,7 +437,7 @@ Item {
|
||||
// ---- Typography --------------------------------------------------
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: 8
|
||||
spacing: Style.space(8)
|
||||
|
||||
Text {
|
||||
text: "Typography"
|
||||
@@ -462,7 +462,7 @@ Item {
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
implicitHeight: typeCol.implicitHeight + 24
|
||||
implicitHeight: typeCol.implicitHeight + Style.spacing.rowPaddingX * 2
|
||||
color: Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, 0.04)
|
||||
radius: Style.cornerRadius
|
||||
border.color: Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, 0.10)
|
||||
@@ -473,9 +473,9 @@ Item {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.leftMargin: 14
|
||||
anchors.rightMargin: 14
|
||||
spacing: 10
|
||||
anchors.leftMargin: Style.space(14)
|
||||
anchors.rightMargin: Style.space(14)
|
||||
spacing: Style.space(10)
|
||||
|
||||
Text {
|
||||
text: "Scale"
|
||||
@@ -511,8 +511,8 @@ Item {
|
||||
id: metaCol
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: 140
|
||||
spacing: 1
|
||||
width: Style.space(140)
|
||||
spacing: Style.space(1)
|
||||
Text {
|
||||
text: "Style.font." + modelData.key
|
||||
color: root.foreground
|
||||
@@ -532,7 +532,7 @@ Item {
|
||||
anchors.left: metaCol.right
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.leftMargin: 16
|
||||
anchors.leftMargin: Style.space(16)
|
||||
text: modelData.sample
|
||||
color: root.foreground
|
||||
font.family: root.fontFamily
|
||||
@@ -558,8 +558,8 @@ Item {
|
||||
|
||||
Grid {
|
||||
columns: 2
|
||||
columnSpacing: 16
|
||||
rowSpacing: 4
|
||||
columnSpacing: Style.space(16)
|
||||
rowSpacing: Style.space(4)
|
||||
width: parent.width
|
||||
|
||||
Text {
|
||||
@@ -626,6 +626,32 @@ Item {
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.bodySmall
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "Style.spacing.scale"
|
||||
color: Qt.darker(root.foreground, 1.5)
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.bodySmall
|
||||
}
|
||||
Text {
|
||||
text: Style.spacing.scale.toFixed(2)
|
||||
color: root.foreground
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.bodySmall
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "Style.spacing.panelPadding"
|
||||
color: Qt.darker(root.foreground, 1.5)
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.bodySmall
|
||||
}
|
||||
Text {
|
||||
text: Style.spacing.panelPadding + " px"
|
||||
color: root.foreground
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.bodySmall
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -636,7 +662,7 @@ Item {
|
||||
// ---- PanelSectionHeader ------------------------------------------
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: 8
|
||||
spacing: Style.space(8)
|
||||
|
||||
Text {
|
||||
text: "PanelSectionHeader"
|
||||
@@ -654,7 +680,7 @@ Item {
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
implicitHeight: shCol.implicitHeight + 24
|
||||
implicitHeight: shCol.implicitHeight + Style.spacing.rowPaddingX * 2
|
||||
color: Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, 0.04)
|
||||
radius: Style.cornerRadius
|
||||
border.color: Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, 0.10)
|
||||
@@ -665,9 +691,9 @@ Item {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.leftMargin: 14
|
||||
anchors.rightMargin: 14
|
||||
spacing: 6
|
||||
anchors.leftMargin: Style.space(14)
|
||||
anchors.rightMargin: Style.space(14)
|
||||
spacing: Style.space(6)
|
||||
|
||||
PanelSectionHeader {
|
||||
text: "DNS provider"
|
||||
@@ -692,7 +718,7 @@ Item {
|
||||
// ---- PanelSeparator ----------------------------------------------
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: 8
|
||||
spacing: Style.space(8)
|
||||
|
||||
Text {
|
||||
text: "PanelSeparator"
|
||||
@@ -710,7 +736,7 @@ Item {
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
implicitHeight: sepCol.implicitHeight + 24
|
||||
implicitHeight: sepCol.implicitHeight + Style.spacing.rowPaddingX * 2
|
||||
color: Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, 0.04)
|
||||
radius: Style.cornerRadius
|
||||
border.color: Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, 0.10)
|
||||
@@ -721,9 +747,9 @@ Item {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.leftMargin: 14
|
||||
anchors.rightMargin: 14
|
||||
spacing: 12
|
||||
anchors.leftMargin: Style.space(14)
|
||||
anchors.rightMargin: Style.space(14)
|
||||
spacing: Style.space(12)
|
||||
|
||||
PanelSeparator { foreground: root.foreground }
|
||||
PanelSeparator { foreground: root.foreground; strength: 0.25 }
|
||||
@@ -735,7 +761,7 @@ Item {
|
||||
// ---- CursorSurface -----------------------------------------------
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: 8
|
||||
spacing: Style.space(8)
|
||||
|
||||
Text {
|
||||
text: "CursorSurface"
|
||||
@@ -755,7 +781,7 @@ Item {
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
implicitHeight: csCol.implicitHeight + 24
|
||||
implicitHeight: csCol.implicitHeight + Style.spacing.rowPaddingX * 2
|
||||
color: Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, 0.04)
|
||||
radius: Style.cornerRadius
|
||||
border.color: Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, 0.10)
|
||||
@@ -766,9 +792,9 @@ Item {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.leftMargin: 14
|
||||
anchors.rightMargin: 14
|
||||
spacing: 6
|
||||
anchors.leftMargin: Style.space(14)
|
||||
anchors.rightMargin: Style.space(14)
|
||||
spacing: Style.space(6)
|
||||
|
||||
Repeater {
|
||||
model: [
|
||||
@@ -781,11 +807,11 @@ Item {
|
||||
required property var modelData
|
||||
required property int index
|
||||
width: parent.width
|
||||
implicitHeight: csLabel.implicitHeight + 16
|
||||
implicitHeight: csLabel.implicitHeight + Style.spacing.controlGap * 2
|
||||
hasCursor: root.focusSection === "cursor-surface" && root.selectedIndex === index
|
||||
current: index === 1
|
||||
foreground: root.foreground
|
||||
fill: Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, 0.18)
|
||||
fill: Style.hoverFillFor(root.foreground, root.accent)
|
||||
onHasCursorChanged: if (hasCursor) root.ensureCursorVisible(this)
|
||||
|
||||
Text {
|
||||
@@ -793,8 +819,8 @@ Item {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.leftMargin: 10
|
||||
anchors.rightMargin: 10
|
||||
anchors.leftMargin: Style.space(10)
|
||||
anchors.rightMargin: Style.space(10)
|
||||
text: modelData.label
|
||||
color: root.foreground
|
||||
font.family: root.fontFamily
|
||||
@@ -820,7 +846,7 @@ Item {
|
||||
// ---- Button ------------------------------------------------------
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: 8
|
||||
spacing: Style.space(8)
|
||||
|
||||
Text {
|
||||
text: "Button"
|
||||
@@ -830,7 +856,7 @@ Item {
|
||||
font.bold: true
|
||||
}
|
||||
Text {
|
||||
text: "The kit's only button. State flags compose: hasCursor (panel cursor / hover) paints a tinted fill; active adds a persistent highlight; bordered draws a 1px idle ring for primary form buttons; focusable enables Tab focus with the accent ring. Click below or press h/l to walk the demo cursor."
|
||||
text: "The kit's only button. State flags compose from shared tokens: hasCursor / hover paints the hover-cursor fill; active/selected add the selected fill; selected borders are off by default; bordered opts into normal/hover-cursor borders; focusable uses the same defaults as hover-cursor. Click below or press h/l to walk the demo cursor."
|
||||
color: Qt.darker(root.foreground, 1.5)
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.caption
|
||||
@@ -840,7 +866,7 @@ Item {
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
implicitHeight: buttonRow.implicitHeight + 24
|
||||
implicitHeight: buttonRow.implicitHeight + Style.spacing.rowPaddingX * 2
|
||||
color: Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, 0.04)
|
||||
radius: Style.cornerRadius
|
||||
border.color: Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, 0.10)
|
||||
@@ -850,15 +876,15 @@ Item {
|
||||
id: buttonRow
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.leftMargin: 14
|
||||
spacing: 16
|
||||
anchors.leftMargin: Style.space(14)
|
||||
spacing: Style.space(16)
|
||||
|
||||
// Each demo Button is paired with a caption labeling the
|
||||
// state(s) it exercises so the section reads as one Button
|
||||
// showing its flag combinations side by side.
|
||||
|
||||
Column {
|
||||
spacing: 6
|
||||
spacing: Style.space(6)
|
||||
Button {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: "DHCP"
|
||||
@@ -879,7 +905,7 @@ Item {
|
||||
}
|
||||
|
||||
Column {
|
||||
spacing: 6
|
||||
spacing: Style.space(6)
|
||||
Button {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: "Cloudflare"
|
||||
@@ -901,13 +927,13 @@ Item {
|
||||
}
|
||||
|
||||
Column {
|
||||
spacing: 6
|
||||
spacing: Style.space(6)
|
||||
Button {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
iconText: ""
|
||||
tooltipText: "Refresh"
|
||||
horizontalPadding: 8
|
||||
verticalPadding: 4
|
||||
horizontalPadding: Style.spacing.controlGap
|
||||
verticalPadding: Style.spacing.labelGap
|
||||
hasCursor: root.focusSection === "button" && root.selectedIndex === 2
|
||||
onHovered: function(h) {
|
||||
if (h) { root.focusSection = "button"; root.selectedIndex = 2 }
|
||||
@@ -924,7 +950,7 @@ Item {
|
||||
}
|
||||
|
||||
Column {
|
||||
spacing: 6
|
||||
spacing: Style.space(6)
|
||||
Button {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
iconText: ""
|
||||
@@ -947,7 +973,7 @@ Item {
|
||||
}
|
||||
|
||||
Column {
|
||||
spacing: 6
|
||||
spacing: Style.space(6)
|
||||
Button {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: "Apply"
|
||||
@@ -975,7 +1001,7 @@ Item {
|
||||
Column {
|
||||
id: buttonGroupSection
|
||||
width: parent.width
|
||||
spacing: 8
|
||||
spacing: Style.space(8)
|
||||
readonly property bool focused: root.focusSection === "button-group"
|
||||
onFocusedChanged: if (focused) root.ensureCursorVisible(this)
|
||||
|
||||
@@ -987,7 +1013,7 @@ Item {
|
||||
font.bold: true
|
||||
}
|
||||
Text {
|
||||
text: "Mutually-exclusive row of Buttons. Selected option paints the accent fill+border so the chosen value stands out from non-selected options the cursor may pass through. Click to pick or press h/l + Enter."
|
||||
text: "Mutually-exclusive row of Buttons. Each chip uses Button's bordered chrome; selected and cursor states come from the same shared Style tokens as every other control. Click to pick or press h/l + Enter."
|
||||
color: Qt.darker(root.foreground, 1.5)
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.caption
|
||||
@@ -997,7 +1023,7 @@ Item {
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
implicitHeight: choiceRow.implicitHeight + 24
|
||||
implicitHeight: choiceRow.implicitHeight + Style.spacing.rowPaddingX * 2
|
||||
color: Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, 0.04)
|
||||
radius: Style.cornerRadius
|
||||
border.color: Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, 0.10)
|
||||
@@ -1007,7 +1033,7 @@ Item {
|
||||
id: choiceRow
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.leftMargin: 14
|
||||
anchors.leftMargin: Style.space(14)
|
||||
options: ["top", "right", "bottom", "left"]
|
||||
value: root.choiceDemoValue
|
||||
cursorIndex: root.focusSection === "button-group" ? root.selectedIndex : -1
|
||||
@@ -1028,7 +1054,7 @@ Item {
|
||||
// ---- PanelActionButton -------------------------------------------
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: 8
|
||||
spacing: Style.space(8)
|
||||
|
||||
Text {
|
||||
text: "PanelActionButton"
|
||||
@@ -1048,7 +1074,7 @@ Item {
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
implicitHeight: pabCol.implicitHeight + 24
|
||||
implicitHeight: pabCol.implicitHeight + Style.spacing.rowPaddingX * 2
|
||||
color: Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, 0.04)
|
||||
radius: Style.cornerRadius
|
||||
border.color: Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, 0.10)
|
||||
@@ -1058,8 +1084,8 @@ Item {
|
||||
id: pabCol
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.leftMargin: 14
|
||||
spacing: 18
|
||||
anchors.leftMargin: Style.space(14)
|
||||
spacing: Style.space(18)
|
||||
|
||||
PanelActionButton {
|
||||
iconText: ""
|
||||
@@ -1109,7 +1135,7 @@ Item {
|
||||
panelBackground: root.background
|
||||
fontFamily: root.fontFamily
|
||||
fontSize: Style.font.subtitle
|
||||
size: 26
|
||||
size: Style.space(26)
|
||||
focusable: true
|
||||
hasCursor: root.focusSection === "panel-action-button" && root.selectedIndex === 3
|
||||
onHovered: function(h) {
|
||||
@@ -1124,7 +1150,7 @@ Item {
|
||||
// ---- PanelToolTip ------------------------------------------------
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: 8
|
||||
spacing: Style.space(8)
|
||||
|
||||
Text {
|
||||
text: "PanelToolTip"
|
||||
@@ -1144,16 +1170,16 @@ Item {
|
||||
|
||||
Rectangle {
|
||||
id: tipSwatch
|
||||
width: 140
|
||||
height: 36
|
||||
width: Style.space(140)
|
||||
height: Style.space(36)
|
||||
readonly property bool focused: root.focusSection === "panel-tool-tip"
|
||||
color: tipMouse.containsMouse || focused
|
||||
? Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, 0.18)
|
||||
: Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, 0.10)
|
||||
? Style.hoverFillFor(root.foreground, root.accent)
|
||||
: Style.normalFillFor(root.foreground, root.accent)
|
||||
border.color: focused
|
||||
? Style.focusBorderColor
|
||||
: Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, 0.35)
|
||||
border.width: focused ? Style.focusBorderWidth : 1
|
||||
? Style.hoverBorderFor(root.foreground, root.accent)
|
||||
: Style.normalBorderFor(root.foreground, root.accent)
|
||||
border.width: focused ? Style.hoverBorderWidth : Style.normalBorderWidth
|
||||
radius: Style.cornerRadius
|
||||
onFocusedChanged: if (focused) root.ensureCursorVisible(this)
|
||||
|
||||
@@ -1190,7 +1216,7 @@ Item {
|
||||
// ---- Slider ------------------------------------------------------
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: 8
|
||||
spacing: Style.space(8)
|
||||
|
||||
Text {
|
||||
text: "Slider"
|
||||
@@ -1211,7 +1237,7 @@ Item {
|
||||
CursorSurface {
|
||||
id: sliderWrapper
|
||||
width: parent.width
|
||||
implicitHeight: sliderRow.implicitHeight + 24
|
||||
implicitHeight: sliderRow.implicitHeight + Style.spacing.rowPaddingX * 2
|
||||
outline: true
|
||||
foreground: root.foreground
|
||||
hasCursor: root.focusSection === "slider"
|
||||
@@ -1229,9 +1255,9 @@ Item {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.leftMargin: 14
|
||||
anchors.rightMargin: 14
|
||||
spacing: 10
|
||||
anchors.leftMargin: Style.space(14)
|
||||
anchors.rightMargin: Style.space(14)
|
||||
spacing: Style.space(10)
|
||||
property real demoVolume: 0.45
|
||||
|
||||
Text {
|
||||
@@ -1269,7 +1295,7 @@ Item {
|
||||
// ---- TextField -----------------------------------------------------
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: 8
|
||||
spacing: Style.space(8)
|
||||
|
||||
Text {
|
||||
text: "TextField"
|
||||
@@ -1289,7 +1315,7 @@ Item {
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
implicitHeight: tfCol.implicitHeight + 24
|
||||
implicitHeight: tfCol.implicitHeight + Style.spacing.rowPaddingX * 2
|
||||
color: Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, 0.04)
|
||||
radius: Style.cornerRadius
|
||||
border.color: Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, 0.10)
|
||||
@@ -1300,9 +1326,9 @@ Item {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.leftMargin: 14
|
||||
anchors.rightMargin: 14
|
||||
spacing: 10
|
||||
anchors.leftMargin: Style.space(14)
|
||||
anchors.rightMargin: Style.space(14)
|
||||
spacing: Style.space(10)
|
||||
|
||||
TextField {
|
||||
id: demoTextField
|
||||
@@ -1353,7 +1379,7 @@ Item {
|
||||
// ---- NumberField ---------------------------------------------------
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: 8
|
||||
spacing: Style.space(8)
|
||||
|
||||
Text {
|
||||
text: "NumberField"
|
||||
@@ -1373,7 +1399,7 @@ Item {
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
implicitHeight: numberDemo.implicitHeight + 24
|
||||
implicitHeight: numberDemo.implicitHeight + Style.spacing.rowPaddingX * 2
|
||||
color: Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, 0.04)
|
||||
border.color: Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, 0.12)
|
||||
border.width: 1
|
||||
@@ -1383,7 +1409,7 @@ Item {
|
||||
id: numberDemo
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.leftMargin: 12
|
||||
anchors.leftMargin: Style.space(12)
|
||||
label: "Auto-refresh interval (minutes)"
|
||||
from: 1
|
||||
to: 1440
|
||||
@@ -1404,7 +1430,7 @@ Item {
|
||||
// ---- Toggle --------------------------------------------------------
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: 8
|
||||
spacing: Style.space(8)
|
||||
|
||||
Text {
|
||||
text: "Toggle"
|
||||
@@ -1414,7 +1440,7 @@ Item {
|
||||
font.bold: true
|
||||
}
|
||||
Text {
|
||||
text: "Title + description + switch. Click anywhere on the row to flip; caller updates `checked` in response. Same focus tokens as Button."
|
||||
text: "Title + description + switch. Click anywhere on the row to flip; caller updates `checked` in response. Uses the same normal / hover-cursor / focus tokens as Button and the checked switch track uses selected tokens."
|
||||
color: Qt.darker(root.foreground, 1.5)
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.caption
|
||||
@@ -1465,7 +1491,7 @@ Item {
|
||||
// ---- Dropdown -----------------------------------------------------
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: 8
|
||||
spacing: Style.space(8)
|
||||
|
||||
Text {
|
||||
text: "Dropdown"
|
||||
@@ -1485,7 +1511,7 @@ Item {
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
implicitHeight: ddCol.implicitHeight + 24
|
||||
implicitHeight: ddCol.implicitHeight + Style.spacing.rowPaddingX * 2
|
||||
color: Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, 0.04)
|
||||
radius: Style.cornerRadius
|
||||
border.color: Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, 0.10)
|
||||
@@ -1496,13 +1522,13 @@ Item {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.leftMargin: 14
|
||||
anchors.rightMargin: 14
|
||||
spacing: 6
|
||||
anchors.leftMargin: Style.space(14)
|
||||
anchors.rightMargin: Style.space(14)
|
||||
spacing: Style.space(6)
|
||||
|
||||
Dropdown {
|
||||
id: demoDropdown
|
||||
width: 260
|
||||
width: Style.spacing.dropdownWidth
|
||||
label: "Center anchor"
|
||||
fontFamily: root.fontFamily
|
||||
options: ["calendar", "weather", "clock", "battery"]
|
||||
@@ -1521,7 +1547,7 @@ Item {
|
||||
// ---- SearchableDropdown -------------------------------------------
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: 8
|
||||
spacing: Style.space(8)
|
||||
|
||||
Text {
|
||||
text: "SearchableDropdown"
|
||||
@@ -1541,7 +1567,7 @@ Item {
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
implicitHeight: sddCol.implicitHeight + 24
|
||||
implicitHeight: sddCol.implicitHeight + Style.spacing.rowPaddingX * 2
|
||||
color: Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, 0.04)
|
||||
radius: Style.cornerRadius
|
||||
border.color: Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, 0.10)
|
||||
@@ -1552,13 +1578,13 @@ Item {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.leftMargin: 14
|
||||
anchors.rightMargin: 14
|
||||
spacing: 6
|
||||
anchors.leftMargin: Style.space(14)
|
||||
anchors.rightMargin: Style.space(14)
|
||||
spacing: Style.space(6)
|
||||
|
||||
SearchableDropdown {
|
||||
id: demoSearchableDropdown
|
||||
width: 280
|
||||
width: Style.spacing.searchableDropdownWidth
|
||||
label: "Add widget"
|
||||
fontFamily: root.fontFamily
|
||||
placeholderText: "Search widgets..."
|
||||
@@ -1594,7 +1620,7 @@ Item {
|
||||
// ---- Composed example -------------------------------------------
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: 8
|
||||
spacing: Style.space(8)
|
||||
|
||||
Text {
|
||||
text: "Composed example"
|
||||
@@ -1614,7 +1640,7 @@ Item {
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
implicitHeight: composedCol.implicitHeight + 24
|
||||
implicitHeight: composedCol.implicitHeight + Style.spacing.rowPaddingX * 2
|
||||
color: Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, 0.04)
|
||||
radius: Style.cornerRadius
|
||||
border.color: Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, 0.10)
|
||||
@@ -1625,9 +1651,9 @@ Item {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.leftMargin: 14
|
||||
anchors.rightMargin: 14
|
||||
spacing: 6
|
||||
anchors.leftMargin: Style.space(14)
|
||||
anchors.rightMargin: Style.space(14)
|
||||
spacing: Style.space(6)
|
||||
|
||||
PanelSectionHeader {
|
||||
text: "Wi-Fi networks"
|
||||
@@ -1637,10 +1663,10 @@ Item {
|
||||
|
||||
CursorSurface {
|
||||
width: parent.width
|
||||
implicitHeight: composedRow.implicitHeight + 12
|
||||
implicitHeight: composedRow.implicitHeight + Style.spacing.md * 2
|
||||
current: true
|
||||
foreground: root.foreground
|
||||
fill: Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, 0.18)
|
||||
fill: Style.hoverFillFor(root.foreground, root.accent)
|
||||
hasCursor: root.focusSection === "composed" && root.selectedIndex === 0
|
||||
onHasCursorChanged: if (hasCursor) root.ensureCursorVisible(this)
|
||||
|
||||
@@ -1655,8 +1681,8 @@ Item {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.leftMargin: 10
|
||||
anchors.rightMargin: 10
|
||||
anchors.leftMargin: Style.space(10)
|
||||
anchors.rightMargin: Style.space(10)
|
||||
implicitHeight: 36
|
||||
|
||||
Text {
|
||||
@@ -1682,11 +1708,11 @@ Item {
|
||||
}
|
||||
|
||||
Column {
|
||||
spacing: 1
|
||||
spacing: Style.space(1)
|
||||
anchors.left: composedIcon.right
|
||||
anchors.leftMargin: 10
|
||||
anchors.leftMargin: Style.space(10)
|
||||
anchors.right: composedForget.left
|
||||
anchors.rightMargin: 8
|
||||
anchors.rightMargin: Style.space(8)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
Text {
|
||||
@@ -1713,9 +1739,9 @@ Item {
|
||||
|
||||
CursorSurface {
|
||||
width: parent.width
|
||||
implicitHeight: idleRow.implicitHeight + 12
|
||||
implicitHeight: idleRow.implicitHeight + Style.spacing.md * 2
|
||||
foreground: root.foreground
|
||||
fill: Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, 0.18)
|
||||
fill: Style.hoverFillFor(root.foreground, root.accent)
|
||||
hasCursor: root.focusSection === "composed" && root.selectedIndex === 1
|
||||
onHasCursorChanged: if (hasCursor) root.ensureCursorVisible(this)
|
||||
|
||||
@@ -1730,8 +1756,8 @@ Item {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.leftMargin: 10
|
||||
anchors.rightMargin: 10
|
||||
anchors.leftMargin: Style.space(10)
|
||||
anchors.rightMargin: Style.space(10)
|
||||
implicitHeight: 36
|
||||
|
||||
Text {
|
||||
@@ -1745,7 +1771,7 @@ Item {
|
||||
|
||||
Text {
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 24
|
||||
anchors.leftMargin: Style.space(24)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: "HughesATT"
|
||||
color: root.foreground
|
||||
@@ -1758,7 +1784,7 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
Item { width: 1; height: 12 }
|
||||
Item { width: 1; height: Style.spacing.rowPaddingX }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,14 +24,14 @@ Item {
|
||||
property color border: foreground
|
||||
readonly property int cornerRadius: Style.cornerRadius
|
||||
property string fontFamily: Quickshell.env("OMARCHY_MENU_FONT") || "monospace"
|
||||
property int contentMargin: 18
|
||||
property int headerHeight: 34
|
||||
property int contentSpacing: 6
|
||||
property int cardWidth: 400
|
||||
property int cardHeight: 500
|
||||
property int contentMargin: Style.spacing.panelPadding
|
||||
property int headerHeight: Math.max(Style.space(34), Style.font.title + Style.spacing.controlPaddingY * 2)
|
||||
property int contentSpacing: Style.spacing.md
|
||||
property int cardWidth: Math.min(Style.space(400), panel.width - Style.gapsOut * 2)
|
||||
property int cardHeight: Math.min(Style.space(500), panel.height - Style.gapsOut * 2)
|
||||
|
||||
property int cellWidth: 44
|
||||
property int cellHeight: 44
|
||||
property int cellWidth: Math.max(Style.space(44), Style.font.display + Style.spacing.md)
|
||||
property int cellHeight: Math.max(Style.space(44), Style.font.display + Style.spacing.md)
|
||||
property int columns: Math.floor((cardWidth - contentMargin * 2) / cellWidth)
|
||||
|
||||
function open(payloadJson) {
|
||||
@@ -183,7 +183,7 @@ Item {
|
||||
anchors.centerIn: parent
|
||||
color: root.background
|
||||
border.color: root.border
|
||||
border.width: 2
|
||||
border.width: Math.max(1, Style.space(2))
|
||||
|
||||
MouseArea { anchors.fill: parent; onClicked: {} }
|
||||
|
||||
@@ -274,7 +274,9 @@ Item {
|
||||
width: root.cellWidth
|
||||
height: root.cellHeight
|
||||
radius: root.cornerRadius
|
||||
color: index === root.selectedIndex ? root.withAlpha(root.foreground, 0.08) : root.withAlpha(root.foreground, mouseArea.containsMouse ? 0.045 : 0)
|
||||
color: index === root.selectedIndex ? Style.hoverFillFor(root.foreground, root.accent) : "transparent"
|
||||
border.color: index === root.selectedIndex ? Style.hoverBorderFor(root.foreground, root.accent) : "transparent"
|
||||
border.width: index === root.selectedIndex ? Style.hoverBorderWidth : 0
|
||||
|
||||
Text {
|
||||
text: parent.emoji
|
||||
@@ -290,6 +292,7 @@ Item {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onContainsMouseChanged: if (containsMouse) root.selectedIndex = index
|
||||
onClicked: {
|
||||
root.selectedIndex = index
|
||||
root.activateIndex(index)
|
||||
@@ -300,7 +303,7 @@ Item {
|
||||
|
||||
Column {
|
||||
anchors.centerIn: parent
|
||||
spacing: 8
|
||||
spacing: Style.space(8)
|
||||
visible: displayModel.count === 0
|
||||
|
||||
Text {
|
||||
|
||||
@@ -473,7 +473,7 @@ Item {
|
||||
id: card
|
||||
visible: root.opened && root.imagesLoaded && root.layoutSettled && root.imageArray.length > 0
|
||||
width: Math.min(parent.width - 80, root.expandedWidth + 13 * (root.sliceWidth + root.sliceSpacing) + 40)
|
||||
height: root.expandedHeight + 30 + root.bottomChromeHeight
|
||||
height: root.expandedHeight + Style.space(30) + root.bottomChromeHeight
|
||||
anchors.centerIn: parent
|
||||
|
||||
MouseArea { anchors.fill: parent; onClicked: {} }
|
||||
@@ -481,7 +481,7 @@ Item {
|
||||
Item {
|
||||
id: carousel
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 30
|
||||
anchors.topMargin: Style.space(30)
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: root.bottomChromeHeight
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
@@ -635,7 +635,7 @@ Item {
|
||||
id: selectedLabel
|
||||
visible: root.showLabels
|
||||
anchors.top: carousel.bottom
|
||||
anchors.topMargin: 16
|
||||
anchors.topMargin: Style.space(16)
|
||||
anchors.horizontalCenter: carousel.horizontalCenter
|
||||
width: root.expandedWidth
|
||||
text: root.currentLabel()
|
||||
@@ -651,7 +651,7 @@ Item {
|
||||
Text {
|
||||
visible: root.filterable && root.filterText
|
||||
anchors.top: selectedLabel.bottom
|
||||
anchors.topMargin: 8
|
||||
anchors.topMargin: Style.space(8)
|
||||
anchors.horizontalCenter: carousel.horizontalCenter
|
||||
width: root.expandedWidth
|
||||
text: root.filterText
|
||||
|
||||
+34
-33
@@ -57,18 +57,18 @@ Item {
|
||||
property color foreground: Color.menu.text
|
||||
property color border: foreground
|
||||
readonly property int cornerRadius: Style.cornerRadius
|
||||
property int contentMargin: 18
|
||||
property int headerHeight: 34
|
||||
property int contentSpacing: 6
|
||||
property int baseRowHeight: 50
|
||||
property int detailRowHeight: 58
|
||||
property int rowSpacing: 3
|
||||
property int dividerHeight: 17
|
||||
property int contentMargin: Style.spacing.panelPadding
|
||||
property int headerHeight: Math.max(Style.space(34), Style.font.title + Style.spacing.controlPaddingY * 2)
|
||||
property int contentSpacing: Style.spacing.md
|
||||
property int baseRowHeight: Math.max(Style.space(50), Style.font.body + Style.spacing.rowPaddingX * 2)
|
||||
property int detailRowHeight: Math.max(Style.space(58), Style.font.body + Style.font.caption + Style.spacing.rowPaddingX * 2)
|
||||
property int rowSpacing: Style.spacing.xs
|
||||
property int dividerHeight: Style.space(17)
|
||||
property bool searchDivider: false
|
||||
property int layoutSerial: 0
|
||||
property int cardWidth: Math.min((root.activeMenu === "trigger.capture.screenrecord" || root.activeMenu === "style.font") ? 520 : 300, panel.width - 48)
|
||||
property int cardWidth: Math.min((root.activeMenu === "trigger.capture.screenrecord" || root.activeMenu === "style.font") ? Style.space(520) : Style.space(300), panel.width - Style.gapsOut * 2)
|
||||
property int visibleRowsHeight: rowListHeight(layoutSerial, displayModel.count, filterText, searchDivider)
|
||||
property int cardHeight: Math.min(Math.max(220, contentMargin * 2 + headerHeight + contentSpacing + visibleRowsHeight), panel.height - 48)
|
||||
property int cardHeight: Math.min(Math.max(Style.space(220), contentMargin * 2 + headerHeight + contentSpacing + visibleRowsHeight), panel.height - Style.gapsOut * 2)
|
||||
|
||||
function shellQuote(value) {
|
||||
return "'" + String(value).replace(/'/g, "'\\''") + "'"
|
||||
@@ -807,7 +807,7 @@ Item {
|
||||
anchors.centerIn: parent
|
||||
color: root.background
|
||||
border.color: root.border
|
||||
border.width: 2
|
||||
border.width: Math.max(1, Style.space(2))
|
||||
|
||||
MouseArea { anchors.fill: parent; onClicked: {} }
|
||||
|
||||
@@ -900,11 +900,11 @@ Item {
|
||||
|
||||
Rectangle {
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 4
|
||||
anchors.leftMargin: Style.space(4)
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 4
|
||||
anchors.rightMargin: Style.space(4)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
height: 1
|
||||
height: Style.spacing.hairline
|
||||
color: root.withAlpha(root.foreground, 0.2)
|
||||
}
|
||||
}
|
||||
@@ -925,50 +925,50 @@ Item {
|
||||
width: ListView.view.width
|
||||
height: root.rowHeightForDetail(row.detail)
|
||||
radius: root.cornerRadius
|
||||
color: index === root.selectedIndex ? root.withAlpha(root.foreground, 0.08) : root.withAlpha(root.foreground, mouseArea.containsMouse ? 0.045 : 0)
|
||||
border.color: "transparent"
|
||||
border.width: 0
|
||||
color: index === root.selectedIndex ? Style.hoverFillFor(root.foreground, root.accent) : "transparent"
|
||||
border.color: index === root.selectedIndex ? Style.hoverBorderFor(root.foreground, root.accent) : "transparent"
|
||||
border.width: index === root.selectedIndex ? Style.hoverBorderWidth : 0
|
||||
|
||||
Rectangle {
|
||||
visible: false
|
||||
width: 4
|
||||
height: parent.height - 18
|
||||
radius: Math.min(root.cornerRadius, 4)
|
||||
width: Style.space(4)
|
||||
height: parent.height - Style.space(18)
|
||||
radius: Math.min(root.cornerRadius, Style.space(4))
|
||||
color: root.accent
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 8
|
||||
anchors.leftMargin: Style.space(8)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Text {
|
||||
id: iconText
|
||||
text: row.icon
|
||||
color: index === root.selectedIndex ? root.accent : root.foreground
|
||||
color: index === root.selectedIndex ? Style.hoverStateColor(root.foreground, root.accent) : root.foreground
|
||||
opacity: row.kind === "back" ? 0.7 : 1
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.iconLarge
|
||||
width: 36
|
||||
width: Style.space(36)
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 8
|
||||
anchors.leftMargin: Style.space(8)
|
||||
y: contentColumn.y + labelText.y + (labelText.height - height) / 2
|
||||
}
|
||||
|
||||
Column {
|
||||
id: contentColumn
|
||||
anchors.left: iconText.right
|
||||
anchors.leftMargin: 6
|
||||
anchors.leftMargin: Style.space(6)
|
||||
anchors.right: trail.left
|
||||
anchors.rightMargin: 6
|
||||
anchors.rightMargin: Style.space(6)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 3
|
||||
spacing: Style.space(3)
|
||||
|
||||
Text {
|
||||
id: labelText
|
||||
width: parent.width
|
||||
text: row.label
|
||||
color: index === root.selectedIndex ? root.accent : root.foreground
|
||||
color: index === root.selectedIndex ? Style.hoverStateColor(root.foreground, root.accent) : root.foreground
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.heading
|
||||
font.weight: Font.Medium
|
||||
@@ -989,9 +989,9 @@ Item {
|
||||
|
||||
Row {
|
||||
id: trail
|
||||
width: 14
|
||||
width: Style.space(14)
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 8
|
||||
anchors.rightMargin: Style.space(8)
|
||||
y: contentColumn.y + labelText.y + (labelText.height - height) / 2
|
||||
spacing: 0
|
||||
|
||||
@@ -1007,7 +1007,7 @@ Item {
|
||||
|
||||
Text {
|
||||
text: row.kind === "menu" || row.kind === "link" ? "›" : ""
|
||||
color: index === root.selectedIndex ? root.accent : root.foreground
|
||||
color: index === root.selectedIndex ? Style.hoverStateColor(root.foreground, root.accent) : root.foreground
|
||||
opacity: row.kind === "menu" || row.kind === "link" ? 0.36 : 0
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.heading
|
||||
@@ -1021,6 +1021,7 @@ Item {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onContainsMouseChanged: if (containsMouse) root.selectedIndex = row.index
|
||||
onClicked: root.activateIndex(row.index)
|
||||
}
|
||||
}
|
||||
@@ -1028,7 +1029,7 @@ Item {
|
||||
|
||||
Column {
|
||||
anchors.centerIn: parent
|
||||
spacing: 8
|
||||
spacing: Style.space(8)
|
||||
visible: displayModel.count === 0
|
||||
|
||||
Text {
|
||||
@@ -1038,7 +1039,7 @@ Item {
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.displayLarge
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
width: 320
|
||||
width: Style.space(320)
|
||||
}
|
||||
|
||||
Text {
|
||||
@@ -1048,7 +1049,7 @@ Item {
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.title
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
width: 320
|
||||
width: Style.space(320)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,9 +42,9 @@ Item {
|
||||
// shell.bar isn't reachable so the popup never lands on top of the bar.
|
||||
readonly property string barPosition: shell && shell.barConfig ? String(shell.barConfig.position || "top") : "top"
|
||||
readonly property bool barVertical: barPosition === "left" || barPosition === "right"
|
||||
readonly property int defaultBarSize: barVertical ? 28 : 26
|
||||
readonly property int defaultBarSize: barVertical ? Style.bar.sizeVertical : Style.bar.sizeHorizontal
|
||||
readonly property int liveBarSize: shell && shell.bar && !shell.bar.barHidden ? Math.max(0, shell.bar.barSize) : defaultBarSize
|
||||
readonly property int barClearance: liveBarSize + 12
|
||||
readonly property int barClearance: liveBarSize + Style.gapsOut
|
||||
|
||||
// Fired by IPC (`omarchy-shell notifications showHistory`) so the
|
||||
// bar widget can drop its PopupCard from the same anchor a click would.
|
||||
@@ -806,10 +806,10 @@ Item {
|
||||
right: service.barPosition !== "left"
|
||||
}
|
||||
margins {
|
||||
top: service.barPosition === "top" ? service.barClearance + 12 : 20
|
||||
bottom: service.barPosition === "bottom" ? service.barClearance + 12 : 20
|
||||
left: service.barPosition === "left" ? service.barClearance + 12 : 20
|
||||
right: service.barPosition === "right" ? service.barClearance + 12 : 20
|
||||
top: service.barPosition === "top" ? service.barClearance : Style.gapsOut
|
||||
bottom: service.barPosition === "bottom" ? service.barClearance : Style.gapsOut
|
||||
left: service.barPosition === "left" ? service.barClearance : Style.gapsOut
|
||||
right: service.barPosition === "right" ? service.barClearance : Style.gapsOut
|
||||
}
|
||||
|
||||
implicitWidth: popupColumn.implicitWidth
|
||||
@@ -819,7 +819,7 @@ Item {
|
||||
id: popupColumn
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
spacing: 8
|
||||
spacing: Style.space(8)
|
||||
|
||||
Repeater {
|
||||
model: popupModel
|
||||
|
||||
@@ -90,7 +90,7 @@ Rectangle {
|
||||
.replace(/^\s*(?:https?:\/\/|www\.)?(?:[a-z0-9-]+\.)+[a-z]{2,}(?::\d+)?(?:\/\S*)?\s+/i, "")
|
||||
}
|
||||
|
||||
implicitWidth: 380
|
||||
implicitWidth: Style.space(380)
|
||||
// Add 2 * border.width so mainColumn (inset by border.width on top/left/right)
|
||||
// doesn't push content under the bottom edge. The bottom edge is also inset
|
||||
// for symmetry except when the progress bar replaces it.
|
||||
@@ -98,7 +98,7 @@ Rectangle {
|
||||
radius: cornerRadius
|
||||
color: Color.notifications.background
|
||||
border.color: urgency === 2 ? Color.urgent : Color.notifications.border
|
||||
border.width: 2
|
||||
border.width: Math.max(1, Style.space(2))
|
||||
clip: true
|
||||
|
||||
HoverHandler { id: hoverTracker }
|
||||
@@ -126,7 +126,7 @@ Rectangle {
|
||||
// the preview looks like a clean banner without dark letterboxing.
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 140
|
||||
Layout.preferredHeight: Style.space(140)
|
||||
visible: root.mediaMode
|
||||
clip: true
|
||||
|
||||
@@ -164,16 +164,16 @@ Rectangle {
|
||||
// the summary/body ("Screenshot saved" etc) under the hero image.
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 12
|
||||
Layout.rightMargin: 12
|
||||
Layout.topMargin: 10
|
||||
Layout.bottomMargin: 10
|
||||
spacing: 12
|
||||
Layout.leftMargin: Style.space(12)
|
||||
Layout.rightMargin: Style.space(12)
|
||||
Layout.topMargin: Style.space(10)
|
||||
Layout.bottomMargin: Style.space(10)
|
||||
spacing: Style.space(12)
|
||||
|
||||
Item {
|
||||
id: smallIconSlot
|
||||
Layout.preferredWidth: 40
|
||||
Layout.preferredHeight: 40
|
||||
Layout.preferredWidth: Style.space(40)
|
||||
Layout.preferredHeight: Style.space(40)
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
// Hide the slot when the icon failed to resolve (themed-icon name
|
||||
// not in the user's icon theme) AND we don't have a glyph fallback
|
||||
@@ -184,8 +184,8 @@ Rectangle {
|
||||
id: smallIconImage
|
||||
anchors.fill: parent
|
||||
source: root.smallIconSource
|
||||
sourceSize.width: 40 * Screen.devicePixelRatio
|
||||
sourceSize.height: 40 * Screen.devicePixelRatio
|
||||
sourceSize.width: smallIconSlot.width * Screen.devicePixelRatio
|
||||
sourceSize.height: smallIconSlot.height * Screen.devicePixelRatio
|
||||
fillMode: Image.PreserveAspectFit
|
||||
asynchronous: true
|
||||
smooth: true
|
||||
@@ -207,7 +207,7 @@ Rectangle {
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
spacing: 2
|
||||
spacing: Style.space(2)
|
||||
|
||||
Text {
|
||||
Layout.fillWidth: true
|
||||
@@ -224,7 +224,7 @@ Rectangle {
|
||||
|
||||
Text {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 2
|
||||
Layout.topMargin: Style.space(2)
|
||||
visible: root.sanitizedBody.length > 0
|
||||
text: root.sanitizedBody
|
||||
textFormat: Text.StyledText
|
||||
@@ -246,7 +246,7 @@ Rectangle {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
height: 3
|
||||
height: Math.max(1, Style.space(3))
|
||||
color: Color.notifications.border
|
||||
visible: false
|
||||
|
||||
|
||||
+11
-11
@@ -88,24 +88,24 @@ Item {
|
||||
|
||||
Rectangle {
|
||||
id: card
|
||||
width: 269
|
||||
height: 68
|
||||
width: Style.space(269)
|
||||
height: Math.max(Style.space(68), Style.font.displayLarge + Style.spacing.panelGap)
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 67
|
||||
anchors.bottomMargin: Style.space(67)
|
||||
color: Color.alpha(Color.background, 0.97)
|
||||
border.color: Color.foreground
|
||||
border.width: 2
|
||||
border.width: Math.max(1, Style.space(2))
|
||||
radius: Style.cornerRadius
|
||||
opacity: root.opened ? 1 : 0
|
||||
|
||||
Row {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: 16
|
||||
anchors.rightMargin: 16
|
||||
spacing: 16
|
||||
anchors.leftMargin: Style.space(16)
|
||||
anchors.rightMargin: Style.space(16)
|
||||
spacing: Style.space(16)
|
||||
Text {
|
||||
width: 28
|
||||
width: Style.space(28)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: root.icon
|
||||
@@ -115,8 +115,8 @@ Item {
|
||||
}
|
||||
Rectangle {
|
||||
visible: root.hasProgress
|
||||
width: visible ? 142 : 0
|
||||
height: 6
|
||||
width: visible ? Style.space(142) : 0
|
||||
height: Math.max(Style.space(6), Style.spacing.sm)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: Color.alpha(Color.foreground, 0.45)
|
||||
Rectangle {
|
||||
@@ -126,7 +126,7 @@ Item {
|
||||
}
|
||||
}
|
||||
Text {
|
||||
width: root.hasProgress ? 41 : 190
|
||||
width: root.hasProgress ? Style.space(41) : Style.space(190)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: root.message
|
||||
font.family: Style.font.family
|
||||
|
||||
@@ -18,9 +18,9 @@ Item {
|
||||
property color foreground: Color.menu.text
|
||||
property color border: foreground
|
||||
readonly property int cornerRadius: Style.cornerRadius
|
||||
property int contentMargin: 18
|
||||
property int contentSpacing: 12
|
||||
property int fieldHeight: 42
|
||||
property int contentMargin: Style.spacing.panelPadding
|
||||
property int contentSpacing: Style.spacing.rowPaddingX
|
||||
property int fieldHeight: Math.max(Style.space(42), Style.spacing.controlHeight)
|
||||
|
||||
property bool closing: false
|
||||
property bool submitted: false
|
||||
@@ -36,8 +36,8 @@ Item {
|
||||
|
||||
readonly property bool dialogVisible: polkitAgent.isActive || closing
|
||||
readonly property bool fingerprintWaiting: dialogVisible && !responseRequired && !submitted && (fingerprintFirst || promptLooksFingerprint(currentPrompt + " " + currentSupplementary))
|
||||
readonly property int cardWidth: Math.min(312, Math.max(260, panel.width - 48))
|
||||
readonly property int cardHeight: panel.height > 0 ? Math.min(fieldHeight + contentMargin * 2, panel.height - 48) : fieldHeight + contentMargin * 2
|
||||
readonly property int cardWidth: Math.min(Style.space(312), Math.max(Style.space(260), panel.width - Style.gapsOut * 2))
|
||||
readonly property int cardHeight: panel.height > 0 ? Math.min(fieldHeight + contentMargin * 2, panel.height - Style.gapsOut * 2) : fieldHeight + contentMargin * 2
|
||||
|
||||
function withAlpha(color, alpha) {
|
||||
return Qt.rgba(color.r, color.g, color.b, alpha)
|
||||
@@ -238,7 +238,7 @@ Item {
|
||||
anchors.horizontalCenterOffset: root.shakeOffset
|
||||
color: root.background
|
||||
border.color: root.accent
|
||||
border.width: 2
|
||||
border.width: Math.max(1, Style.space(2))
|
||||
|
||||
MouseArea { anchors.fill: parent; onClicked: root.refocus() }
|
||||
|
||||
@@ -263,21 +263,21 @@ Item {
|
||||
id: cardRow
|
||||
anchors.fill: parent
|
||||
anchors.margins: root.contentMargin
|
||||
spacing: 14
|
||||
spacing: Style.space(14)
|
||||
|
||||
Text {
|
||||
text: "\uf023"
|
||||
color: root.errorFlash ? Color.urgent : root.accent
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.iconLarge
|
||||
width: 26
|
||||
width: Style.space(26)
|
||||
height: root.fieldHeight
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
||||
Item {
|
||||
width: parent.width - 40
|
||||
width: parent.width - Style.space(40)
|
||||
height: root.fieldHeight
|
||||
|
||||
TextInput {
|
||||
@@ -320,8 +320,8 @@ Item {
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: 2
|
||||
height: 24
|
||||
width: Math.max(1, Style.space(2))
|
||||
height: Style.space(24)
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: root.errorFlash ? Color.urgent : root.foreground
|
||||
|
||||
@@ -122,7 +122,7 @@ Item {
|
||||
function ensureBodyItemVisible(item) {
|
||||
if (!item || typeof bodyScroll === "undefined" || !bodyScroll || !bodyScroll.contentItem) return
|
||||
var pos = item.mapToItem(bodyScroll.contentItem, 0, 0)
|
||||
var pad = 24
|
||||
var pad = Style.space(24)
|
||||
var top = pos.y - pad
|
||||
var bottom = pos.y + item.height + pad
|
||||
if (top < bodyScroll.contentY) {
|
||||
@@ -161,6 +161,27 @@ Item {
|
||||
property int draftRevision: 0
|
||||
property bool suppressReload: false
|
||||
|
||||
// When a widget action moves an entry, the Repeater rebuilds / reindexes
|
||||
// cards. Remember the action group position so focus follows the moved
|
||||
// widget instead of falling back to the first action on the old/new row.
|
||||
property string pendingActionFocusSection: ""
|
||||
property int pendingActionFocusIndex: -1
|
||||
property int pendingActionFocusAction: 0
|
||||
property int pendingActionFocusRevision: 0
|
||||
|
||||
function scheduleActionFocus(section, index, action) {
|
||||
pendingActionFocusSection = section
|
||||
pendingActionFocusIndex = index
|
||||
pendingActionFocusAction = action
|
||||
pendingActionFocusRevision++
|
||||
}
|
||||
|
||||
function clearPendingActionFocus() {
|
||||
pendingActionFocusSection = ""
|
||||
pendingActionFocusIndex = -1
|
||||
pendingActionFocusAction = 0
|
||||
}
|
||||
|
||||
// ---------------- draft helpers ------------------------------------------
|
||||
function cloneJson(value) { return JSON.parse(JSON.stringify(value || null)) }
|
||||
function isPlainObject(value) { return value !== null && typeof value === "object" && !Array.isArray(value) }
|
||||
@@ -283,7 +304,7 @@ Item {
|
||||
markDirty()
|
||||
}
|
||||
|
||||
function moveEntry(section, fromIndex, toIndex) {
|
||||
function moveEntry(section, fromIndex, toIndex, focusActionIndex) {
|
||||
var arr = sectionArray(section)
|
||||
if (toIndex < 0 || toIndex >= arr.length) return
|
||||
mutateSection(section, function(a) {
|
||||
@@ -291,6 +312,7 @@ Item {
|
||||
a.splice(fromIndex, 1)
|
||||
a.splice(toIndex, 0, item)
|
||||
})
|
||||
if (focusActionIndex !== undefined) scheduleActionFocus(section, toIndex, focusActionIndex)
|
||||
}
|
||||
|
||||
function removeEntry(section, index) {
|
||||
@@ -498,9 +520,9 @@ Item {
|
||||
id: window
|
||||
title: "Omarchy Bar Settings"
|
||||
color: root.background
|
||||
implicitWidth: 760
|
||||
implicitHeight: 620
|
||||
minimumSize: Qt.size(620, 480)
|
||||
implicitWidth: Style.space(760)
|
||||
implicitHeight: Style.space(620)
|
||||
minimumSize: Qt.size(Style.space(620), Style.space(480))
|
||||
|
||||
onVisibleChanged: {
|
||||
if (!visible && !root.closingFromHost && root.shell && typeof root.shell.hide === "function")
|
||||
@@ -541,71 +563,71 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: root.background
|
||||
// No explicit border — the Hyprland window decoration already draws one.
|
||||
|
||||
ColumnLayout {
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
color: root.background
|
||||
// No explicit border — the Hyprland window decoration already draws one.
|
||||
|
||||
// Header
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 48
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
|
||||
Text {
|
||||
text: "Omarchy Bar Settings"
|
||||
color: root.foreground
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.heading
|
||||
font.bold: true
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 18
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
// Header
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: Math.max(Style.space(48), Style.font.heading + Style.spacing.controlPaddingY * 2)
|
||||
|
||||
Text {
|
||||
text: "Omarchy Bar Settings"
|
||||
color: root.foreground
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.heading
|
||||
font.bold: true
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Style.spacing.panelPadding
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "~/.config/omarchy/shell.json"
|
||||
color: Qt.darker(root.foreground, 1.8)
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.caption
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Style.spacing.panelPadding
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "~/.config/omarchy/shell.json"
|
||||
color: Qt.darker(root.foreground, 1.8)
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.caption
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 18
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: Style.spacing.hairline
|
||||
color: Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, 0.18)
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 1
|
||||
color: Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, 0.18)
|
||||
}
|
||||
// Content
|
||||
Flickable {
|
||||
id: bodyScroll
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
Layout.margins: Style.spacing.panelPadding
|
||||
clip: true
|
||||
contentWidth: width
|
||||
contentHeight: contentColumn.implicitHeight
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
flickableDirection: Flickable.VerticalFlick
|
||||
|
||||
// Content
|
||||
Flickable {
|
||||
id: bodyScroll
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
Layout.margins: 18
|
||||
clip: true
|
||||
contentWidth: width
|
||||
contentHeight: contentColumn.implicitHeight
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
flickableDirection: Flickable.VerticalFlick
|
||||
ColumnLayout {
|
||||
id: contentColumn
|
||||
width: bodyScroll.width
|
||||
spacing: Style.spacing.panelGap
|
||||
|
||||
ColumnLayout {
|
||||
id: contentColumn
|
||||
width: bodyScroll.width
|
||||
spacing: 14
|
||||
|
||||
BarCategory { Layout.fillWidth: true }
|
||||
BarCategory { Layout.fillWidth: true }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- per-widget settings overlay -----------------------------------
|
||||
Rectangle {
|
||||
@@ -633,19 +655,19 @@ Item {
|
||||
|
||||
Rectangle {
|
||||
anchors.centerIn: parent
|
||||
width: 420
|
||||
height: Math.min(parent.height - 60, 380)
|
||||
width: Math.min(Style.space(420), parent.width - Style.gapsOut * 2)
|
||||
height: Math.min(parent.height - Style.space(60), Style.space(380))
|
||||
color: root.background
|
||||
radius: Style.cornerRadius
|
||||
border.color: Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, 0.2)
|
||||
border.width: 1
|
||||
border.color: Style.normalBorderFor(root.foreground, root.accent)
|
||||
border.width: Style.normalBorderWidth
|
||||
|
||||
MouseArea { anchors.fill: parent }
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 18
|
||||
spacing: 12
|
||||
anchors.margins: Style.spacing.panelPadding
|
||||
spacing: Style.spacing.rowPaddingX
|
||||
|
||||
Text {
|
||||
text: root.widgetName(root.widgetDialogEntry.id || "")
|
||||
@@ -679,7 +701,7 @@ Item {
|
||||
|
||||
Row {
|
||||
Layout.alignment: Qt.AlignRight
|
||||
spacing: 8
|
||||
spacing: Style.spacing.rowGap
|
||||
Button {
|
||||
text: "Cancel"
|
||||
foreground: root.foreground
|
||||
@@ -703,7 +725,7 @@ Item {
|
||||
|
||||
// ===================== bar category ======================================
|
||||
component BarCategory: ColumnLayout {
|
||||
spacing: 14
|
||||
spacing: Style.spacing.panelGap
|
||||
|
||||
Text {
|
||||
text: "Bar"
|
||||
@@ -724,10 +746,10 @@ Item {
|
||||
|
||||
Row {
|
||||
Layout.fillWidth: true
|
||||
spacing: 14
|
||||
spacing: Style.spacing.panelGap
|
||||
|
||||
Column {
|
||||
spacing: 4
|
||||
spacing: Style.spacing.labelGap
|
||||
|
||||
Text {
|
||||
text: "Position"
|
||||
@@ -799,7 +821,7 @@ Item {
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 1
|
||||
Layout.preferredHeight: Style.spacing.hairline
|
||||
color: Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, 0.12)
|
||||
}
|
||||
|
||||
@@ -824,8 +846,8 @@ Item {
|
||||
property string sectionLabel: ""
|
||||
property var entries: root.sectionArray(section.sectionKey)
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 8
|
||||
spacing: 8
|
||||
Layout.topMargin: Style.spacing.rowGap
|
||||
spacing: Style.spacing.rowGap
|
||||
|
||||
Connections {
|
||||
target: root
|
||||
@@ -834,7 +856,7 @@ Item {
|
||||
|
||||
RowLayout {
|
||||
width: section.width
|
||||
spacing: 8
|
||||
spacing: Style.spacing.rowGap
|
||||
|
||||
Text {
|
||||
text: section.sectionLabel
|
||||
@@ -862,7 +884,7 @@ Item {
|
||||
value: ""
|
||||
placeholderText: "Search widgets..."
|
||||
emptyText: "No widgets to add"
|
||||
Layout.preferredWidth: 220
|
||||
Layout.preferredWidth: Style.spacing.searchableDropdownWidth
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
options: {
|
||||
var list = root.availableToAdd(section.sectionKey)
|
||||
@@ -890,7 +912,7 @@ Item {
|
||||
Column {
|
||||
Layout.fillWidth: true
|
||||
width: section.width
|
||||
spacing: 4
|
||||
spacing: Style.spacing.labelGap
|
||||
|
||||
Repeater {
|
||||
model: section.entries
|
||||
@@ -907,11 +929,11 @@ Item {
|
||||
Rectangle {
|
||||
visible: section.entries.length === 0
|
||||
width: parent.width
|
||||
height: 32
|
||||
height: Math.max(Style.space(32), Style.font.bodySmall + Style.spacing.controlPaddingY * 2)
|
||||
radius: root.cornerRadius
|
||||
color: Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, 0.04)
|
||||
border.color: Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, 0.12)
|
||||
border.width: 1
|
||||
color: Style.normalFillFor(root.foreground, root.accent)
|
||||
border.color: Style.normalBorderFor(root.foreground, root.accent)
|
||||
border.width: Style.normalBorderWidth
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
@@ -934,56 +956,154 @@ Item {
|
||||
readonly property string description: root.widgetDescription(entryId)
|
||||
readonly property bool hasSettings: root.widgetHasSettings(entryId)
|
||||
|
||||
implicitHeight: 50
|
||||
implicitHeight: Style.space(50)
|
||||
radius: root.cornerRadius
|
||||
color: cardArea.containsMouse ? Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, 0.08) : Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, 0.03)
|
||||
border.color: Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, 0.12)
|
||||
border.width: 1
|
||||
color: cardArea.containsMouse || actionRow.activeFocus
|
||||
? Style.hoverFillFor(root.foreground, root.accent)
|
||||
: Style.normalFillFor(root.foreground, root.accent)
|
||||
border.color: cardArea.containsMouse || actionRow.activeFocus
|
||||
? Style.hoverBorderFor(root.foreground, root.accent)
|
||||
: Style.normalBorderFor(root.foreground, root.accent)
|
||||
border.width: cardArea.containsMouse || actionRow.activeFocus ? Style.hoverBorderWidth : Style.normalBorderWidth
|
||||
|
||||
Behavior on color { ColorAnimation { duration: 100 } }
|
||||
|
||||
function maybeRestoreActionFocus() {
|
||||
if (root.pendingActionFocusSection !== card.sectionKey) return
|
||||
if (root.pendingActionFocusIndex !== card.entryIndex) return
|
||||
|
||||
actionRow.actionIndex = root.pendingActionFocusAction
|
||||
actionRow.clampActionIndex()
|
||||
root.clearPendingActionFocus()
|
||||
|
||||
Qt.callLater(function() {
|
||||
actionRow.forceActiveFocus()
|
||||
root.ensureBodyItemVisible(card)
|
||||
})
|
||||
}
|
||||
|
||||
onEntryIndexChanged: maybeRestoreActionFocus()
|
||||
Component.onCompleted: maybeRestoreActionFocus()
|
||||
|
||||
Connections {
|
||||
target: root
|
||||
function onPendingActionFocusRevisionChanged() { card.maybeRestoreActionFocus() }
|
||||
}
|
||||
|
||||
Row {
|
||||
id: actionRow
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 8
|
||||
anchors.rightMargin: Style.spacing.controlGap
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 4
|
||||
spacing: Style.spacing.labelGap
|
||||
activeFocusOnTab: true
|
||||
|
||||
property int actionIndex: 0
|
||||
|
||||
onActiveFocusChanged: if (activeFocus) {
|
||||
clampActionIndex()
|
||||
root.ensureBodyItemVisible(card)
|
||||
}
|
||||
|
||||
function actionVisible(index) {
|
||||
switch (index) {
|
||||
case 0: return moveUpButton.visible && moveUpButton.enabled
|
||||
case 1: return moveDownButton.visible && moveDownButton.enabled
|
||||
case 2: return settingsButton.visible && settingsButton.enabled
|
||||
case 3: return removeButton.visible && removeButton.enabled
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
function firstActionIndex() {
|
||||
for (var i = 0; i < 4; i++) if (actionVisible(i)) return i
|
||||
return 0
|
||||
}
|
||||
|
||||
function clampActionIndex() {
|
||||
if (actionVisible(actionIndex)) return
|
||||
actionIndex = firstActionIndex()
|
||||
}
|
||||
|
||||
function moveAction(delta) {
|
||||
clampActionIndex()
|
||||
var next = actionIndex
|
||||
while (true) {
|
||||
next += delta
|
||||
if (next < 0 || next > 3) return
|
||||
if (actionVisible(next)) { actionIndex = next; return }
|
||||
}
|
||||
}
|
||||
|
||||
function activateAction() {
|
||||
clampActionIndex()
|
||||
switch (actionIndex) {
|
||||
case 0: root.moveEntry(card.sectionKey, card.entryIndex, card.entryIndex - 1, actionIndex); return
|
||||
case 1: root.moveEntry(card.sectionKey, card.entryIndex, card.entryIndex + 1, actionIndex); return
|
||||
case 2: root.openWidgetSettings(card.sectionKey, card.entryIndex, card.entry); return
|
||||
case 3: root.removeEntry(card.sectionKey, card.entryIndex); return
|
||||
}
|
||||
}
|
||||
|
||||
Keys.priority: Keys.BeforeItem
|
||||
Keys.onPressed: function(event) {
|
||||
if (event.key === Qt.Key_Left || event.text === "h") {
|
||||
moveAction(-1); event.accepted = true; return
|
||||
}
|
||||
if (event.key === Qt.Key_Right || event.text === "l") {
|
||||
moveAction(1); event.accepted = true; return
|
||||
}
|
||||
if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter || event.key === Qt.Key_Space) {
|
||||
activateAction(); event.accepted = true; return
|
||||
}
|
||||
}
|
||||
|
||||
PanelActionButton {
|
||||
id: moveUpButton
|
||||
iconText: ""
|
||||
tooltipText: "Move up"
|
||||
foreground: root.foreground
|
||||
panelBackground: root.background
|
||||
fontFamily: root.fontFamily
|
||||
fontSize: Style.font.subtitle
|
||||
size: 26
|
||||
focusable: true
|
||||
onClicked: root.moveEntry(card.sectionKey, card.entryIndex, card.entryIndex - 1)
|
||||
size: Style.space(26)
|
||||
hasCursor: actionRow.activeFocus && actionRow.actionIndex === 0
|
||||
bordered: hasCursor
|
||||
onHovered: function(h) { if (h) actionRow.actionIndex = 0 }
|
||||
onClicked: root.moveEntry(card.sectionKey, card.entryIndex, card.entryIndex - 1, 0)
|
||||
}
|
||||
PanelActionButton {
|
||||
id: moveDownButton
|
||||
iconText: ""
|
||||
tooltipText: "Move down"
|
||||
foreground: root.foreground
|
||||
panelBackground: root.background
|
||||
fontFamily: root.fontFamily
|
||||
fontSize: Style.font.subtitle
|
||||
size: 26
|
||||
focusable: true
|
||||
onClicked: root.moveEntry(card.sectionKey, card.entryIndex, card.entryIndex + 1)
|
||||
size: Style.space(26)
|
||||
hasCursor: actionRow.activeFocus && actionRow.actionIndex === 1
|
||||
bordered: hasCursor
|
||||
onHovered: function(h) { if (h) actionRow.actionIndex = 1 }
|
||||
onClicked: root.moveEntry(card.sectionKey, card.entryIndex, card.entryIndex + 1, 1)
|
||||
}
|
||||
PanelActionButton {
|
||||
id: settingsButton
|
||||
iconText: ""
|
||||
tooltipText: "Settings"
|
||||
foreground: root.foreground
|
||||
panelBackground: root.background
|
||||
fontFamily: root.fontFamily
|
||||
fontSize: Style.font.subtitle
|
||||
size: 26
|
||||
focusable: true
|
||||
size: Style.space(26)
|
||||
visible: card.hasSettings
|
||||
hasCursor: actionRow.activeFocus && actionRow.actionIndex === 2
|
||||
bordered: hasCursor
|
||||
onVisibleChanged: if (!visible && actionRow.actionIndex === 2) actionRow.clampActionIndex()
|
||||
onHovered: function(h) { if (h) actionRow.actionIndex = 2 }
|
||||
onClicked: root.openWidgetSettings(card.sectionKey, card.entryIndex, card.entry)
|
||||
}
|
||||
PanelActionButton {
|
||||
id: removeButton
|
||||
iconText: ""
|
||||
tooltipText: "Remove"
|
||||
foreground: root.urgent
|
||||
@@ -991,8 +1111,10 @@ Item {
|
||||
panelBackground: root.background
|
||||
fontFamily: root.fontFamily
|
||||
fontSize: Style.font.subtitle
|
||||
size: 26
|
||||
focusable: true
|
||||
size: Style.space(26)
|
||||
hasCursor: actionRow.activeFocus && actionRow.actionIndex === 3
|
||||
bordered: hasCursor
|
||||
onHovered: function(h) { if (h) actionRow.actionIndex = 3 }
|
||||
onClicked: root.removeEntry(card.sectionKey, card.entryIndex)
|
||||
}
|
||||
}
|
||||
@@ -1000,10 +1122,10 @@ Item {
|
||||
Column {
|
||||
anchors.left: parent.left
|
||||
anchors.right: actionRow.left
|
||||
anchors.leftMargin: 12
|
||||
anchors.rightMargin: 12
|
||||
anchors.leftMargin: Style.spacing.rowPaddingX
|
||||
anchors.rightMargin: Style.spacing.rowPaddingX
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 2
|
||||
spacing: Style.spacing.xxs
|
||||
|
||||
Text {
|
||||
text: card.displayName
|
||||
@@ -1064,7 +1186,7 @@ Item {
|
||||
signal fieldChanged(string key, var value)
|
||||
property var entry: ({})
|
||||
|
||||
spacing: 8
|
||||
spacing: Style.spacing.rowGap
|
||||
width: parent ? parent.width : 0
|
||||
|
||||
NumberField {
|
||||
@@ -1088,7 +1210,7 @@ Item {
|
||||
signal fieldChanged(string key, var value)
|
||||
property var entry: ({})
|
||||
|
||||
spacing: 8
|
||||
spacing: Style.spacing.rowGap
|
||||
width: parent ? parent.width : 0
|
||||
|
||||
component CalendarField: TextField {
|
||||
@@ -1144,7 +1266,7 @@ Item {
|
||||
signal fieldChanged(string key, var value)
|
||||
property var entry: ({})
|
||||
|
||||
spacing: 8
|
||||
spacing: Style.spacing.rowGap
|
||||
width: parent ? parent.width : 0
|
||||
|
||||
NumberField {
|
||||
|
||||
@@ -16,18 +16,18 @@ Item {
|
||||
property color accent: Color.accent
|
||||
property string fontFamily: Style.font.family
|
||||
property int cornerRadius: 0
|
||||
property int rowHeight: 28
|
||||
property int popupRowHeight: 28
|
||||
property int rowHeight: Style.spacing.controlHeight
|
||||
property int popupRowHeight: Style.spacing.popupRowHeight
|
||||
property bool showLabel: true
|
||||
|
||||
signal changed(string value)
|
||||
|
||||
implicitWidth: 240
|
||||
implicitHeight: showLabel ? rowHeight + 18 : rowHeight
|
||||
implicitWidth: Style.spacing.dropdownWidth
|
||||
implicitHeight: showLabel ? rowHeight + Style.spacing.huge : rowHeight
|
||||
|
||||
Column {
|
||||
anchors.fill: parent
|
||||
spacing: 4
|
||||
spacing: Style.spacing.labelGap
|
||||
|
||||
Text {
|
||||
visible: root.showLabel && root.label !== ""
|
||||
@@ -54,17 +54,25 @@ Item {
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
color: root.background
|
||||
color: combo.activeFocus
|
||||
? Style.focusFillFor(root.foreground, root.accent)
|
||||
: (combo.hovered
|
||||
? Style.hoverFillFor(root.foreground, root.accent)
|
||||
: Style.normalFillFor(root.foreground, root.accent))
|
||||
border.color: combo.activeFocus
|
||||
? root.accent
|
||||
: Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, 0.4)
|
||||
border.width: 1
|
||||
? Style.focusBorderFor(root.foreground, root.accent)
|
||||
: (combo.hovered
|
||||
? Style.hoverBorderFor(root.foreground, root.accent)
|
||||
: Style.normalBorderFor(root.foreground, root.accent))
|
||||
border.width: combo.activeFocus
|
||||
? Style.focusBorderWidth
|
||||
: (combo.hovered ? Style.hoverBorderWidth : Style.normalBorderWidth)
|
||||
radius: root.cornerRadius
|
||||
}
|
||||
|
||||
contentItem: Text {
|
||||
leftPadding: 8
|
||||
rightPadding: 24
|
||||
leftPadding: Style.spacing.controlGap
|
||||
rightPadding: Style.space(24)
|
||||
text: combo.displayText
|
||||
color: root.foreground
|
||||
font: combo.font
|
||||
@@ -72,7 +80,7 @@ Item {
|
||||
}
|
||||
|
||||
indicator: Text {
|
||||
x: combo.width - width - 8
|
||||
x: combo.width - width - Style.spacing.controlGap
|
||||
y: combo.topPadding + (combo.availableHeight - height) / 2
|
||||
text: "▾"
|
||||
color: Qt.darker(root.foreground, 1.2)
|
||||
@@ -86,12 +94,12 @@ Item {
|
||||
y: combo.height
|
||||
width: combo.width
|
||||
implicitHeight: Math.min(contentItem.implicitHeight, root.popupRowHeight * 8)
|
||||
padding: 1
|
||||
padding: Style.spacing.hairline
|
||||
|
||||
background: Rectangle {
|
||||
color: root.background
|
||||
border.color: root.foreground
|
||||
border.width: 1
|
||||
border.color: Style.normalBorderFor(root.foreground, root.accent)
|
||||
border.width: Style.normalBorderWidth
|
||||
radius: root.cornerRadius
|
||||
}
|
||||
|
||||
@@ -114,18 +122,18 @@ Item {
|
||||
|
||||
contentItem: Text {
|
||||
text: String(modelData)
|
||||
color: index === combo.highlightedIndex ? root.accent : root.foreground
|
||||
color: index === combo.highlightedIndex ? Style.hoverStateColor(root.foreground, root.accent) : root.foreground
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.body
|
||||
leftPadding: 10
|
||||
rightPadding: 10
|
||||
leftPadding: Style.spacing.controlPaddingX
|
||||
rightPadding: Style.spacing.controlPaddingX
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
color: index === combo.highlightedIndex
|
||||
? Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, 0.12)
|
||||
? Style.hoverFillFor(root.foreground, root.accent)
|
||||
: "transparent"
|
||||
radius: 0
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ import QtQml.Models
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
|
||||
import qs.Commons as NoctaliaCommons
|
||||
|
||||
import "plugins/bar"
|
||||
import "services"
|
||||
|
||||
@@ -598,18 +600,23 @@ ShellRoot {
|
||||
try { colorsRaw = Qt.atob(String(colorsB64 || "")) } catch (e) { colorsRaw = "" }
|
||||
try { shellRaw = Qt.atob(String(shellB64 || "")) } catch (e2) { shellRaw = "" }
|
||||
NoctaliaCommons.Color.resumeThemeReloads()
|
||||
NoctaliaCommons.Style.resumeThemeReloads()
|
||||
NoctaliaCommons.Color.loadColors(colorsRaw)
|
||||
NoctaliaCommons.Color.loadShell(shellRaw)
|
||||
NoctaliaCommons.Style.loadShell(shellRaw)
|
||||
NoctaliaCommons.Style.scheduleRefresh()
|
||||
return "ok"
|
||||
}
|
||||
|
||||
function suspendThemeReloads(): string {
|
||||
NoctaliaCommons.Color.suspendThemeReloads()
|
||||
NoctaliaCommons.Style.suspendThemeReloads()
|
||||
return "ok"
|
||||
}
|
||||
|
||||
function reloadTheme(): string {
|
||||
NoctaliaCommons.Color.reloadTheme()
|
||||
NoctaliaCommons.Style.reloadTheme()
|
||||
return "ok"
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ Column {
|
||||
property var entry: ({})
|
||||
property color foregroundColor: Color.foreground
|
||||
property string fontFamilyName: Style.font.family
|
||||
spacing: 10
|
||||
spacing: Style.spacing.xl
|
||||
width: parent ? parent.width : 0
|
||||
|
||||
function currentValue(field) {
|
||||
@@ -40,7 +40,7 @@ Column {
|
||||
Column {
|
||||
required property var modelData
|
||||
width: root.width
|
||||
spacing: 4
|
||||
spacing: Style.spacing.labelGap
|
||||
|
||||
Text {
|
||||
text: modelData && modelData.label ? modelData.label : (modelData && modelData.key ? modelData.key : "")
|
||||
@@ -153,7 +153,7 @@ Column {
|
||||
property string fieldKey: ""
|
||||
property var field: ({})
|
||||
width: parent.width
|
||||
spacing: 8
|
||||
spacing: Style.spacing.rowGap
|
||||
property real currentNumber: {
|
||||
var v = root.currentValue(field)
|
||||
var n = typeof v === "number" ? v : parseFloat(String(v || 0))
|
||||
@@ -161,7 +161,7 @@ Column {
|
||||
}
|
||||
Slider {
|
||||
id: slider
|
||||
width: parent.width - readout.width - 8
|
||||
width: parent.width - readout.width - Style.spacing.rowGap
|
||||
from: field && field.min !== undefined ? field.min : 0
|
||||
to: field && field.max !== undefined ? field.max : 1
|
||||
stepSize: field && field.step !== undefined ? field.step : 0.01
|
||||
|
||||
Reference in New Issue
Block a user