diff --git a/quickshell/Common/SettingsData.qml b/quickshell/Common/SettingsData.qml index 3619aca81..291e8342f 100644 --- a/quickshell/Common/SettingsData.qml +++ b/quickshell/Common/SettingsData.qml @@ -2305,7 +2305,7 @@ Singleton { continue; const otherSpacing = other.spacing !== undefined ? other.spacing : (defaultBar?.spacing ?? 4); const otherPadding = other.innerPadding !== undefined ? other.innerPadding : (defaultBar?.innerPadding ?? 4); - const otherThickness = Math.max(26 + otherPadding * 0.6, Theme.barHeight - 4 - (8 - otherPadding)) + otherSpacing; + const otherThickness = Theme.barThickness(otherPadding, CompositorService.getScreenScale(screen)) + otherSpacing; const useAutoGaps = other.popupGapsAuto !== undefined ? other.popupGapsAuto : (defaultBar?.popupGapsAuto ?? true); const manualGap = other.popupGapsManual !== undefined ? other.popupGapsManual : (defaultBar?.popupGapsManual ?? 4); @@ -2381,7 +2381,7 @@ Singleton { continue; const otherSpacing = other.spacing !== undefined ? other.spacing : (defaultBar?.spacing ?? 4); const otherPadding = other.innerPadding !== undefined ? other.innerPadding : (defaultBar?.innerPadding ?? 4); - const otherThickness = Math.max(26 + otherPadding * 0.6, Theme.barHeight - 4 - (8 - otherPadding)) + otherSpacing + wingSize; + const otherThickness = Theme.barThickness(otherPadding, CompositorService.getScreenScale(screen)) + otherSpacing + wingSize; const otherBottomGap = isConnected ? 0 : (other.bottomGap !== undefined ? other.bottomGap : (defaultBar?.bottomGap ?? 0)); switch (other.position) { diff --git a/quickshell/Common/Theme.qml b/quickshell/Common/Theme.qml index a9450049e..bf7c67b86 100644 --- a/quickshell/Common/Theme.qml +++ b/quickshell/Common/Theme.qml @@ -1517,7 +1517,7 @@ Singleton { const size = (maximizeIcon ?? false) ? iconSizeLarge : iconSize; const s = iconScale !== undefined ? iconScale : 1.0; - return Math.round((barThickness / 48) * (size + defaultOffset) * s); + return 2 * Math.round((barThickness / 48) * (size + defaultOffset) * s / 2); } function barTextSize(barThickness, fontScale, maximizeText) { @@ -1525,10 +1525,30 @@ Singleton { const dankBarScale = fontScale !== undefined ? fontScale : 1.0; const maxScale = (maximizeText ?? false) ? 1.5 : 1.0; if (scale <= 0.75) - return Math.round(fontSizeSmall * 0.9 * dankBarScale * maxScale); + return evenLineBoxSize(Math.round(fontSizeSmall * 0.9 * dankBarScale * maxScale)); if (scale >= 1.25) - return Math.round(fontSizeMedium * dankBarScale * maxScale); - return Math.round(fontSizeSmall * dankBarScale * maxScale); + return evenLineBoxSize(Math.round(fontSizeMedium * dankBarScale * maxScale)); + return evenLineBoxSize(Math.round(fontSizeSmall * dankBarScale * maxScale)); + } + + FontMetrics { + id: lineBoxProbe + } + + // Text.implicitHeight is ceil(FontMetrics.height); probe.font is written whole and read via boundingRect() so bindings capture no dependency on the probe + function lineBoxHeight(pixelSize) { + lineBoxProbe.font = Qt.font({ + family: fontFamily, + weight: fontWeight, + pixelSize: pixelSize + }); + return Math.ceil(lineBoxProbe.boundingRect("0").height); + } + + function evenLineBoxSize(pixelSize) { + if (lineBoxHeight(pixelSize) % 2 === 0) + return pixelSize; + return lineBoxHeight(pixelSize + 1) % 2 === 0 ? pixelSize + 1 : pixelSize; } function getBatteryIcon(level, isCharging, batteryAvailable) { @@ -2080,11 +2100,25 @@ Singleton { return Math.round(value * s) / s; } + // Qt rounds a centred anchor offset to whole pixels, so a box must share its content's parity + function snapEven(value, dpr) { + const s = dpr || 1; + return 2 * Math.round(value * s / 2) / s; + } + function px(value, dpr) { const s = dpr || 1; return Math.round(value * s) / s; } + function barWidgetThickness(innerPadding, dpr) { + return snapEven(Math.max(20, 26 + innerPadding * 0.6), dpr); + } + + function barThickness(innerPadding, dpr) { + return snapEven(Math.max(barWidgetThickness(innerPadding, dpr) + innerPadding + 4, barHeight - 4 - (8 - innerPadding)), dpr); + } + function hairline(dpr) { return 1 / (dpr || 1); } diff --git a/quickshell/Modals/PowerMenuModal.qml b/quickshell/Modals/PowerMenuModal.qml index 45fe255ae..474c5898b 100644 --- a/quickshell/Modals/PowerMenuModal.qml +++ b/quickshell/Modals/PowerMenuModal.qml @@ -1,6 +1,7 @@ import QtQuick import qs.Common import qs.Modals.Common +import qs.Services import qs.Modules.PowerMenu DankModal { @@ -38,8 +39,7 @@ DankModal { customPosition: { if (parentBounds.width > 0) { const bar = SettingsData.getPrimaryBarConfig(); - const barPadding = bar?.innerPadding ?? 4; - const effectiveBarThickness = Math.max(26 + barPadding * 0.6 + barPadding + 4, Theme.barHeight - 4 - (8 - barPadding)); + const effectiveBarThickness = Theme.barThickness(bar?.innerPadding ?? 4, CompositorService.getScreenScale(parentScreen)); const barExclusionZone = effectiveBarThickness + (bar?.spacing ?? 4) + (bar?.bottomGap ?? 0); const barPosition = bar?.position ?? SettingsData.Position.Top; const screenW = parentScreen?.width ?? 1920; diff --git a/quickshell/Modules/DankBar/DankBarBody.qml b/quickshell/Modules/DankBar/DankBarBody.qml index 15266749a..28b74b088 100644 --- a/quickshell/Modules/DankBar/DankBarBody.qml +++ b/quickshell/Modules/DankBar/DankBarBody.qml @@ -602,7 +602,7 @@ Item { } readonly property int notificationCount: NotificationService.notifications.length - readonly property real effectiveBarThickness: (FrameTransitionState.effectiveFrameEnabled && usesFrameBarChrome) ? SettingsData.frameBarSize : Theme.snap(Math.max(barWindow.widgetThickness + (barConfig?.innerPadding ?? 4) + 4, Theme.barHeight - 4 - (8 - (barConfig?.innerPadding ?? 4))), _dpr) + readonly property real effectiveBarThickness: (FrameTransitionState.effectiveFrameEnabled && usesFrameBarChrome) ? SettingsData.frameBarSize : Theme.barThickness(barConfig?.innerPadding ?? 4, _dpr) readonly property real effectiveBarLengthPadding: { if ((FrameTransitionState.effectiveFrameEnabled && usesFrameBarChrome) || (flattenForMaximizedWindow && hasMaximizedToplevel)) return 0; @@ -611,7 +611,7 @@ Item { return length > 0 ? Math.min(pad, Math.max(0, length / 2 - effectiveSpacing)) : pad; } readonly property bool effectiveOpenOnOverview: (FrameTransitionState.effectiveFrameEnabled && usesFrameBarChrome) ? SettingsData.frameShowOnOverview : (barConfig?.openOnOverview ?? false) - readonly property real widgetThickness: Theme.snap(Math.max(20, 26 + (barConfig?.innerPadding ?? 4) * 0.6), _dpr) + readonly property real widgetThickness: Theme.barWidgetThickness(barConfig?.innerPadding ?? 4, _dpr) readonly property bool hasAdjacentTopBar: { if (barConfig?.autoHide ?? false) diff --git a/quickshell/Modules/DankBar/Widgets/AppsDock.qml b/quickshell/Modules/DankBar/Widgets/AppsDock.qml index 830572143..d7fa6fc33 100644 --- a/quickshell/Modules/DankBar/Widgets/AppsDock.qml +++ b/quickshell/Modules/DankBar/Widgets/AppsDock.qml @@ -39,9 +39,7 @@ BasePill { if (barThickness > 0 && barSpacing > 0) { return barThickness + barSpacing; } - const innerPadding = barConfig?.innerPadding ?? 4; - const spacing = barConfig?.spacing ?? 4; - return Math.max(26 + innerPadding * 0.6, Theme.barHeight - 4 - (8 - innerPadding)) + spacing; + return Theme.barThickness(barConfig?.innerPadding ?? 4, CompositorService.getScreenScale(parentScreen)) + (barConfig?.spacing ?? 4); } readonly property var barBounds: { diff --git a/quickshell/Modules/DankBar/Widgets/RunningApps.qml b/quickshell/Modules/DankBar/Widgets/RunningApps.qml index 92e0fc91d..22f044831 100644 --- a/quickshell/Modules/DankBar/Widgets/RunningApps.qml +++ b/quickshell/Modules/DankBar/Widgets/RunningApps.qml @@ -34,9 +34,7 @@ BasePill { if (barThickness > 0 && barSpacing > 0) { return barThickness + barSpacing; } - const innerPadding = barConfig?.innerPadding ?? 4; - const spacing = barConfig?.spacing ?? 4; - return Math.max(26 + innerPadding * 0.6, Theme.barHeight - 4 - (8 - innerPadding)) + spacing; + return Theme.barThickness(barConfig?.innerPadding ?? 4, CompositorService.getScreenScale(parentScreen)) + (barConfig?.spacing ?? 4); } readonly property var barBounds: { @@ -784,7 +782,7 @@ BasePill { if (triggerBarThickness > 0 && triggerBarSpacing > 0) { return triggerBarThickness + triggerBarSpacing; } - return Math.max(26 + (barConfig?.innerPadding ?? 4) * 0.6, Theme.barHeight - 4 - (8 - (barConfig?.innerPadding ?? 4))) + (barConfig?.spacing ?? 4); + return Theme.barThickness(barConfig?.innerPadding ?? 4, CompositorService.getScreenScale(contextMenuWindow.screen)) + (barConfig?.spacing ?? 4); } property var barBounds: { diff --git a/quickshell/Modules/DankIsland/IslandSatelliteHost.qml b/quickshell/Modules/DankIsland/IslandSatelliteHost.qml index e42b620be..6741b8a05 100644 --- a/quickshell/Modules/DankIsland/IslandSatelliteHost.qml +++ b/quickshell/Modules/DankIsland/IslandSatelliteHost.qml @@ -35,8 +35,8 @@ Item { readonly property string scrollYBehavior: satelliteConfig?.scrollYBehavior ?? "workspace" readonly property real screenScale: CompositorService.getScreenScale(root.targetScreen) readonly property real innerPadding: root.satelliteConfig?.innerPadding ?? 4 - readonly property real widgetThickness: Theme.snap(Math.max(20, 26 + root.innerPadding * 0.6), root.screenScale) - readonly property real barThickness: Theme.snap(Math.max(root.widgetThickness + root.innerPadding + 4, Theme.barHeight - 4 - (8 - root.innerPadding)), root.screenScale) + readonly property real widgetThickness: Theme.barWidgetThickness(root.innerPadding, root.screenScale) + readonly property real barThickness: Theme.barThickness(root.innerPadding, root.screenScale) readonly property real satelliteSpacing: root.satelliteConfig?.spacing ?? 4 readonly property bool edgeAligned: SettingsData.dankIslandSatellitePosition === "edges" readonly property real edgeBaseMargin: Math.max(Theme.spacingXS, root.innerPadding * 0.8) diff --git a/quickshell/Modules/Dock/DockBody.qml b/quickshell/Modules/Dock/DockBody.qml index 77bdf3d91..f410a553b 100644 --- a/quickshell/Modules/Dock/DockBody.qml +++ b/quickshell/Modules/Dock/DockBody.qml @@ -44,9 +44,7 @@ Item { function getBarHeight(barConfig) { if (!barConfig) return 0; - const innerPadding = barConfig.innerPadding ?? 4; - const widgetThickness = Math.max(20, 26 + innerPadding * 0.6); - const barThickness = Math.max(widgetThickness + innerPadding + 4, Theme.barHeight - 4 - (8 - innerPadding)); + const barThickness = Theme.barThickness(barConfig.innerPadding ?? 4, CompositorService.getScreenScale(dock.screen)); const spacing = barConfig.spacing ?? 4; const bottomGap = barConfig.bottomGap ?? 0; return barThickness + spacing + bottomGap; diff --git a/quickshell/Widgets/DankOSD.qml b/quickshell/Widgets/DankOSD.qml index 7fbfa9c99..ea2e91ce7 100644 --- a/quickshell/Widgets/DankOSD.qml +++ b/quickshell/Widgets/DankOSD.qml @@ -140,8 +140,7 @@ PanelWindow { if (!SettingsData.barConfigCoversScreen(bc, screen)) continue; const innerPadding = bc.innerPadding ?? (defaultBar?.innerPadding ?? 4); - const widgetThickness = Math.max(20, 26 + innerPadding * 0.6); - const thickness = Math.max(widgetThickness + innerPadding + 4, Theme.barHeight - 4 - (8 - innerPadding)); + const thickness = Theme.barThickness(innerPadding, dpr); const spacing = bc.spacing ?? (defaultBar?.spacing ?? 4); const bottomGap = bc.bottomGap ?? (defaultBar?.bottomGap ?? 0); const offset = thickness + spacing + bottomGap; diff --git a/quickshell/Widgets/DankPopoutConnected.qml b/quickshell/Widgets/DankPopoutConnected.qml index 441a1945c..90389265c 100644 --- a/quickshell/Widgets/DankPopoutConnected.qml +++ b/quickshell/Widgets/DankPopoutConnected.qml @@ -72,8 +72,7 @@ Item { readonly property real effectiveBarThickness: { if (root.usesConnectedSurfaceChrome) return Math.max(0, storedBarThickness); - const padding = storedBarConfig ? (storedBarConfig.innerPadding !== undefined ? storedBarConfig.innerPadding : 4) : 4; - return Math.max(26 + padding * 0.6, Theme.barHeight - 4 - (8 - padding)) + storedBarSpacing; + return Theme.barThickness(storedBarConfig?.innerPadding ?? 4, dpr) + storedBarSpacing; } readonly property var barBounds: { diff --git a/quickshell/Widgets/DankPopoutStandalone.qml b/quickshell/Widgets/DankPopoutStandalone.qml index 8e4e76c5c..5653a57bb 100644 --- a/quickshell/Widgets/DankPopoutStandalone.qml +++ b/quickshell/Widgets/DankPopoutStandalone.qml @@ -104,8 +104,7 @@ Item { } readonly property real effectiveBarThickness: { - const padding = storedBarConfig ? (storedBarConfig.innerPadding !== undefined ? storedBarConfig.innerPadding : 4) : 4; - return Math.max(26 + padding * 0.6, Theme.barHeight - 4 - (8 - padding)) + storedBarSpacing; + return Theme.barThickness(storedBarConfig?.innerPadding ?? 4, dpr) + storedBarSpacing; } readonly property var barBounds: {