From e8fc2ef08f3aad9f6219b85a8ab8884f27fc953a Mon Sep 17 00:00:00 2001 From: Ryan Hughes Date: Fri, 22 May 2026 13:30:53 -0400 Subject: [PATCH] Re-implement better scaling with new icon spacing --- default/themed/shell.toml.tpl | 20 ++++--- docs/omarchy-shell.md | 40 ++++++++----- shell/Commons/Style.qml | 66 +++++++++++++++------- shell/Ui/KeyboardPanel.qml | 4 +- shell/Ui/PopupCard.qml | 4 +- shell/plugins/bar/widgets/Tray.qml | 27 +++++---- shell/plugins/bar/widgets/Workspaces.qml | 2 +- shell/plugins/dev-gallery/GalleryPanel.qml | 11 ++-- 8 files changed, 110 insertions(+), 64 deletions(-) diff --git a/default/themed/shell.toml.tpl b/default/themed/shell.toml.tpl index 3c2a700b..b3d219da 100644 --- a/default/themed/shell.toml.tpl +++ b/default/themed/shell.toml.tpl @@ -9,8 +9,10 @@ background-alpha = 1.0 text = "{{ foreground }}" # Modules calling attention to themselves (recording, voxtype, alerts, updates) active = "{{ color1 }}" -# Cross-axis size in px. size-horizontal is the height of top/bottom bars; -# size-vertical is the width of left/right bars. +# Cross-axis size at font base-size 12. size-horizontal is the height of +# top/bottom bars; size-vertical is the width of left/right bars. With +# scale-with-font enabled, these grow/shrink with [font] base-size. +scale-with-font = true size-horizontal = 26 size-vertical = 28 @@ -49,11 +51,13 @@ pressed-fill-alpha = 0.22 selection-fill-alpha = 0.35 [spacing] -# `scale` multiplies shared margins, gaps, and padding; components keep -# their proportions. Per-token overrides (in px) below pin individual -# values without affecting the rest of the scale. Uncomment any to tune -# a specific surface. +# `scale` multiplies shared margins, gaps, padding, controls, and panel +# dimensions; components keep their proportions. With scale-with-font +# enabled, those dimensions grow/shrink with [font] base-size too. Per-token +# overrides (in px) below pin individual values without affecting the rest +# of the scale. Uncomment any to tune a specific surface. scale = 1.0 +scale-with-font = true # xxs = 2 # xs = 3 # sm = 4 @@ -83,8 +87,8 @@ scale = 1.0 [font] # base-size is the rem root for the type scale. Every Style.font. # derives from it (e.g. body = base, subtitle ≈ base * 1.083, -# heading ≈ base * 1.333). Clamped 11..13 by the shell because some -# row heights remain fixed, so larger type can clip. +# heading ≈ base * 1.333). The shell only floors this at 1px; increase it +# as much as you want. 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 diff --git a/docs/omarchy-shell.md b/docs/omarchy-shell.md index c778e286..afcf2da6 100644 --- a/docs/omarchy-shell.md +++ b/docs/omarchy-shell.md @@ -173,13 +173,17 @@ both `[controls]` and `[style]`. ### 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. +`[spacing] scale` multiplies the shell's shared margins, gaps, padding, +control sizes, and panel dimensions. The default is `1.0`; values above +`1.0` create more breathing room while values below `1.0` make controls +dense. By default `scale-with-font = true`, so increasing `[font] +base-size` also scales buttons, popup widths, row heights, and panel +padding proportionally. ```toml [spacing] -scale = 1.15 # roomier panels and controls +scale = 1.15 +scale-with-font = true # grow controls and panels with [font] base-size ``` QML components should prefer semantic tokens where possible: @@ -203,10 +207,11 @@ Popout placement deliberately follows Hyprland's `general:gaps_out` 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.: +old default at scale `1.0` and `base-size = 12` while still responding to +the theme scale and font 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] @@ -256,22 +261,27 @@ 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 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 `. +`base-size` has no upper clamp; the shell only floors it at **1px** to +avoid nonsensical zero/negative sizes. Per-token overrides aren't clamped +either. The shell font family is the fontconfig `monospace` alias — +themes don't set it, the user does via `omarchy font set `. ### Bar size `[bar] size-horizontal` / `size-vertical` set the cross-axis dimension -of top/bottom and left/right bars respectively (in px): +of top/bottom and left/right bars respectively, measured at the default +12px font base. By default `scale-with-font = true`, so increasing +`[font] base-size` also increases the bar's cross-axis size: ```toml [bar] -size-horizontal = 26 # top/bottom bar height -size-vertical = 28 # left/right bar width +scale-with-font = true +size-horizontal = 26 # top/bottom bar height at base-size 12 +size-vertical = 28 # left/right bar width at base-size 12 ``` +Set `scale-with-font = false` to keep those bar sizes as fixed pixels. + ## Custom bar modules If a full plugin is overkill, declare a one-off module inline in diff --git a/shell/Commons/Style.qml b/shell/Commons/Style.qml index e0d506a1..789b070e 100644 --- a/shell/Commons/Style.qml +++ b/shell/Commons/Style.qml @@ -19,10 +19,12 @@ import Quickshell.Io // Typography, spacing, and bar size come from theme/shell.toml. // `[font] base-size` is the rem root; every `Style.font.` 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] +// specific token. `[spacing] scale` multiplies shared margins, gaps, +// padding, controls, and panel dimensions while preserving each component's +// proportions; by default it also tracks `base-size`. `[bar] // size-horizontal` / `size-vertical` set the cross-axis dimension for -// top/bottom and left/right bars respectively. +// top/bottom and left/right bars at the default 12px font size; by default +// those dimensions scale with `base-size` so larger fonts don't clip. QtObject { id: root @@ -204,12 +206,14 @@ QtObject { // fractional geometry); themes can make the shell denser or roomier // with `[spacing] scale`, or pin individual tokens. property real spacingScale: 1.0 + property bool spacingScaleWithFont: true property var spacingOverrides: ({}) + readonly property real effectiveSpacingScale: spacingScale * (spacingScaleWithFont ? fontScale : 1) function spaceReal(px) { var n = Number(px) if (!isFinite(n) || n <= 0) return 0 - return n * spacingScale + return n * effectiveSpacingScale } function space(px) { @@ -225,7 +229,7 @@ QtObject { } readonly property QtObject spacing: QtObject { - readonly property real scale: root.spacingScale + readonly property real scale: root.effectiveSpacingScale readonly property int hairline: root.space(1) readonly property int xxs: root.spacingToken("xxs", 2) @@ -270,12 +274,14 @@ QtObject { // read `resolvedFontFamily` when you want to *display* what's drawing. property string resolvedFontFamily: "monospace" - // Clamped 11..13 by applyShellValues — some row heights remain fixed, - // so unbounded type growth can still clip even with scalable spacing. + // The only sanity floor is 1px. Themes and users can make this as large + // as they like; if the shell gets ridiculous, that's their call. property int fontBaseSize: 12 property var fontOverrides: ({}) property var barOverrides: ({}) + property bool barScaleWithFont: true + readonly property real fontScale: Math.max(1 / 12, fontBaseSize / 12) function fontPx(mult) { return Math.max(1, Math.round(fontBaseSize * mult)) @@ -290,7 +296,17 @@ QtObject { function barToken(key, fallback) { var v = barOverrides[key] var n = Number(v) - return (isFinite(n) && n > 0) ? Math.round(n) : fallback + var base = (isFinite(n) && n > 0) ? n : fallback + if (barScaleWithFont) base *= fontScale + return Math.max(1, Math.round(base)) + } + + function boolToken(value, fallback) { + if (value === undefined || value === null) return fallback + var s = String(value).replace(/^\s+|\s+$/g, "").toLowerCase() + if (s === "true" || s === "1" || s === "yes" || s === "on") return true + if (s === "false" || s === "0" || s === "no" || s === "off") return false + return fallback } // The launcher, menu, polkit, emojis, and clipboard surfaces honor an @@ -368,6 +384,8 @@ QtObject { var spacingOut = {} var nextBase = 12 var nextSpacingScale = 1.0 + var nextSpacingScaleWithFont = true + var nextBarScaleWithFont = true var v = values || {} for (var fullKey in v) { var dot = fullKey.indexOf(".") @@ -380,28 +398,38 @@ QtObject { if (!isFinite(ival)) continue if (key === "base-size") nextBase = ival else fontOut[key] = ival - } else if (section === "bar" && (key === "size-horizontal" || key === "size-vertical")) { - var b = parseInt(raw, 10) - if (isFinite(b)) barOut[key] = b + } else if (section === "bar") { + if (key === "scale-with-font") { + nextBarScaleWithFont = boolToken(raw, nextBarScaleWithFont) + } else if (key === "size-horizontal" || key === "size-vertical") { + var b = parseInt(raw, 10) + if (isFinite(b)) barOut[key] = b + } } else if (section === "spacing") { - var fval = parseFloat(raw) - if (!isFinite(fval)) continue - if (key === "scale") nextSpacingScale = fval - else spacingOut[key] = fval + if (key === "scale-with-font") { + nextSpacingScaleWithFont = boolToken(raw, nextSpacingScaleWithFont) + } else { + var fval = parseFloat(raw) + if (!isFinite(fval)) continue + if (key === "scale") nextSpacingScale = fval + else spacingOut[key] = fval + } } else if (section === "controls") { // Strings are passed through; styleRawNum/styleString coerce on read. styleOut[key] = raw } } - // Clamp the rem root. Per-token overrides aren't clamped — a theme - // that wants display-large = 64 should be allowed to ship it. - if (nextBase < 11) nextBase = 11 - if (nextBase > 13) nextBase = 13 + // Keep only a 1px sanity floor. Per-token overrides aren't clamped + // either — a theme that wants display-large = 64 should be allowed to + // ship it. + if (!isFinite(nextBase) || nextBase < 1) nextBase = 1 if (!isFinite(nextSpacingScale) || nextSpacingScale < 0) nextSpacingScale = 1.0 spacingScale = nextSpacingScale + spacingScaleWithFont = nextSpacingScaleWithFont fontBaseSize = nextBase fontOverrides = fontOut barOverrides = barOut + barScaleWithFont = nextBarScaleWithFont spacingOverrides = spacingOut styleOverrides = styleOut } diff --git a/shell/Ui/KeyboardPanel.qml b/shell/Ui/KeyboardPanel.qml index 46a460a1..c23d41f3 100644 --- a/shell/Ui/KeyboardPanel.qml +++ b/shell/Ui/KeyboardPanel.qml @@ -40,8 +40,8 @@ PanelWindow { property var owner: null property int margin: Style.gapsOut property int padding: Style.spacing.popupPadding - property int contentWidth: 280 - property int contentHeight: 200 + property int contentWidth: Style.space(280) + property int contentHeight: Style.space(200) property bool open: false property int gap: Style.gapsOut // distance between bar edge and panel property bool popoutSwitching: false diff --git a/shell/Ui/PopupCard.qml b/shell/Ui/PopupCard.qml index 9a4fc9c2..475d7df4 100644 --- a/shell/Ui/PopupCard.qml +++ b/shell/Ui/PopupCard.qml @@ -11,8 +11,8 @@ PopupWindow { property var owner: null property int margin: Style.gapsOut property int padding: Style.spacing.popupPadding - property int contentWidth: 280 - property int contentHeight: 200 + property int contentWidth: Style.space(280) + property int contentHeight: Style.space(200) property color borderColor: Color.popups.border property bool open: false property bool centerOnBar: false diff --git a/shell/plugins/bar/widgets/Tray.qml b/shell/plugins/bar/widgets/Tray.qml index 567bcf01..7ae853ef 100644 --- a/shell/plugins/bar/widgets/Tray.qml +++ b/shell/plugins/bar/widgets/Tray.qml @@ -18,7 +18,10 @@ BarWidget { readonly property var drawerItems: bucket("drawer") readonly property var allItems: bucket("all") readonly property int drawerCount: drawerItems.length - readonly property int drawerExtent: drawerCount > 0 ? drawerCount * 16 + (drawerCount - 1) * 17 : 0 + readonly property int trayItemExtent: Style.space(16) + readonly property int trayItemGap: Style.space(17) + readonly property int trayJoinGap: Style.space(6) + readonly property int drawerExtent: drawerCount > 0 ? drawerCount * trayItemExtent + (drawerCount - 1) * trayItemGap : 0 // Match Waybar's group/tray-expander drawer transition-duration. readonly property int animationDuration: 600 property real revealProgress: expanded ? 1 : 0 @@ -173,7 +176,7 @@ BarWidget { id: trayIcons x: root.drawerExtent - root.revealExtent anchors.verticalCenter: parent.verticalCenter - spacing: Style.space(17) + spacing: root.trayItemGap layer.enabled: true Repeater { @@ -188,8 +191,8 @@ BarWidget { id: pinnedRow x: drawerArea.x + horizontalTrayRoot.drawerBlockWidth anchors.verticalCenter: parent.verticalCenter - spacing: Style.space(17) - leftPadding: root.pinnedItems.length > 0 && root.allItems.length > 0 ? Style.space(6) : 0 + spacing: root.trayItemGap + leftPadding: root.pinnedItems.length > 0 && root.allItems.length > 0 ? root.trayJoinGap : 0 Repeater { model: root.pinnedItems TrayItem {} @@ -258,7 +261,7 @@ BarWidget { id: trayIcons y: root.drawerExtent - root.revealExtent anchors.horizontalCenter: parent.horizontalCenter - spacing: Style.space(17) + spacing: root.trayItemGap layer.enabled: true Repeater { @@ -273,8 +276,8 @@ BarWidget { id: pinnedCol y: drawerArea.y + verticalTrayRoot.drawerBlockHeight anchors.horizontalCenter: parent.horizontalCenter - spacing: Style.space(17) - topPadding: root.pinnedItems.length > 0 && root.allItems.length > 0 ? Style.space(6) : 0 + spacing: root.trayItemGap + topPadding: root.pinnedItems.length > 0 && root.allItems.length > 0 ? root.trayJoinGap : 0 Repeater { model: root.pinnedItems TrayItem {} @@ -407,14 +410,14 @@ BarWidget { required property var modelData visible: modelData.status !== Status.Passive - implicitWidth: visible ? 16 : 0 - implicitHeight: visible ? 16 : 0 + implicitWidth: visible ? root.trayItemExtent : 0 + implicitHeight: visible ? root.trayItemExtent : 0 IconImage { anchors.centerIn: parent - implicitSize: 12 - width: 12 - height: 12 + implicitSize: Style.space(12) + width: Style.space(12) + height: Style.space(12) source: root.trayIconSource(trayItemRoot.modelData.icon) } diff --git a/shell/plugins/bar/widgets/Workspaces.qml b/shell/plugins/bar/widgets/Workspaces.qml index ab134320..a72af41f 100644 --- a/shell/plugins/bar/widgets/Workspaces.qml +++ b/shell/plugins/bar/widgets/Workspaces.qml @@ -60,7 +60,7 @@ BarWidget { opacity: occupied || focused ? 1 : 0.5 horizontalMargin: 6 verticalPadding: 6 - fixedWidth: root.vertical ? root.barSize : 22 + fixedWidth: root.vertical ? root.barSize : Style.space(22) fixedHeight: root.barSize onPressed: function() { root.focusWorkspace(modelData) } } diff --git a/shell/plugins/dev-gallery/GalleryPanel.qml b/shell/plugins/dev-gallery/GalleryPanel.qml index 61d4a77e..96d343da 100644 --- a/shell/plugins/dev-gallery/GalleryPanel.qml +++ b/shell/plugins/dev-gallery/GalleryPanel.qml @@ -445,11 +445,12 @@ Item { } Text { text: "Style.font.* is the shell-wide type scale. Themes ship a single " - + "[font] base-size in shell.toml (clamped 11..13); every token derives from " - + "it via a fixed multiplier, so changing base-size rescales the whole shell " - + "proportionally. Themes can also pin individual tokens (caption, heading, " - + "display, etc.) for stylistic emphasis. The family follows the fontconfig " - + "monospace alias \u2014 set it with `omarchy font set `." + + "[font] base-size in shell.toml; every token derives from it via a " + + "fixed multiplier, so changing base-size rescales the whole shell " + + "proportionally. There is no upper clamp. Themes can also pin " + + "individual tokens (caption, heading, display, etc.) for stylistic " + + "emphasis. The family follows the fontconfig monospace alias \u2014 " + + "set it with `omarchy font set `." color: Qt.darker(root.foreground, 1.5) font.family: root.fontFamily font.pixelSize: Style.font.bodySmall