feat(dankbar): corner style & bar length padding (#3085)

* feat(dock): attach bar to screen edge

* feat(dock): add bar length padding

* feat(dock): expand bar padding range

* refactor(dock): group bar corner settings

* fix(i18n): avoid attached term variant

* fix(settings): group corner style with goth corners

* fix(dankbar): attach flush bars to the screen edge

---------

Co-authored-by: bbedward <[email protected]>
This commit is contained in:
Huỳnh Thiện Lộc
2026-08-17 13:22:09 -04:00
committed by GitHub
co-authored by bbedward
parent 8485d66139
commit 9abaf4051e
6 changed files with 177 additions and 87 deletions
+8 -1
View File
@@ -1028,7 +1028,9 @@ Singleton {
"spacing": 4,
"innerPadding": 4,
"barInsetPadding": -1,
"barLengthPadding": 0,
"bottomGap": 0,
"attachToScreenEdge": false,
"transparency": 1.0,
"widgetTransparency": 1.0,
"squareCorners": false,
@@ -1804,6 +1806,7 @@ Singleton {
return {
"shadowIntensity": config?.shadowIntensity ?? 0,
"squareCorners": config?.squareCorners ?? false,
"attachToScreenEdge": config?.attachToScreenEdge ?? false,
"gothCornersEnabled": config?.gothCornersEnabled ?? false,
"borderEnabled": config?.borderEnabled ?? false
};
@@ -1877,7 +1880,7 @@ Singleton {
updateBarConfigs();
}
// Zeroes out connected-mode-hostile fields (shadow, square/goth corners, border).
// Zeroes out connected-mode-hostile fields (shadow, square/goth corners, edge attach, border).
// Returns { configs, changed } — `configs` is the same ref when no change.
function _sanitizeBarConfigsForConnectedFrame(configs) {
if (!connectedFrameModeActive || !Array.isArray(configs))
@@ -1900,6 +1903,10 @@ Singleton {
s.squareCorners = false;
dirty = true;
}
if (s.attachToScreenEdge ?? false) {
s.attachToScreenEdge = false;
dirty = true;
}
if (s.gothCornersEnabled ?? false) {
s.gothCornersEnabled = false;
dirty = true;
@@ -577,7 +577,9 @@ var SPEC = {
rightWidgets: ["systemTray", "clipboard", "cpuUsage", "memUsage", "notificationButton", "battery", "controlCenterButton"],
spacing: 4,
innerPadding: 4,
barLengthPadding: 0,
bottomGap: 0,
attachToScreenEdge: false,
transparency: 1.0,
widgetTransparency: 1.0,
squareCorners: false,
+41 -36
View File
@@ -29,6 +29,7 @@ Item {
readonly property bool isBottom: barPos === SettingsData.Position.Bottom
readonly property bool isLeft: barPos === SettingsData.Position.Left
readonly property bool isRight: barPos === SettingsData.Position.Right
readonly property bool edgeAttached: (barConfig?.attachToScreenEdge ?? false) && !frameShapesBar
property real wing: gothEnabled ? barWindow._wingR : 0
@@ -204,10 +205,10 @@ Item {
// wing-side body corners are square where the gothic fillets attach;
// rounding the shadow there leaves a shadow-filled notch under the
// translucent bar fill (#2975)
topLeftRadius: root.gothEnabled && (root.isBottom || root.isRight) ? 0 : root.rt
topRightRadius: root.gothEnabled && (root.isBottom || root.isLeft) ? 0 : root.rt
bottomLeftRadius: root.gothEnabled && (root.isTop || root.isRight) ? 0 : root.rt
bottomRightRadius: root.gothEnabled && (root.isTop || root.isLeft) ? 0 : root.rt
topLeftRadius: root.edgeAttached && (root.isTop || root.isLeft) ? 0 : (root.gothEnabled && (root.isBottom || root.isRight) ? 0 : root.rt)
topRightRadius: root.edgeAttached && (root.isTop || root.isRight) ? 0 : (root.gothEnabled && (root.isBottom || root.isLeft) ? 0 : root.rt)
bottomLeftRadius: root.edgeAttached && (root.isBottom || root.isLeft) ? 0 : (root.gothEnabled && (root.isTop || root.isRight) ? 0 : root.rt)
bottomRightRadius: root.edgeAttached && (root.isBottom || root.isRight) ? 0 : (root.gothEnabled && (root.isTop || root.isLeft) ? 0 : root.rt)
// barShape below is the sole painter of the bar; a fill here doubles
// the alpha of translucent bars while the wings stay single-painted
targetColor: "transparent"
@@ -298,7 +299,7 @@ Item {
h = h - wing;
const r = wing;
const cr = rt;
const crE = frameShapesBar ? 0 : cr;
const crE = frameShapesBar || edgeAttached ? 0 : cr;
let d = `M ${crE} 0`;
d += ` L ${w - crE} 0`;
@@ -329,7 +330,7 @@ Item {
h = h - wing;
const r = wing;
const cr = rt;
const crE = frameShapesBar ? 0 : cr;
const crE = frameShapesBar || edgeAttached ? 0 : cr;
let d = `M ${crE} ${fullH}`;
d += ` L ${w - crE} ${fullH}`;
@@ -359,7 +360,7 @@ Item {
w = w - wing;
const r = wing;
const cr = rt;
const crE = frameShapesBar ? 0 : cr;
const crE = frameShapesBar || edgeAttached ? 0 : cr;
let d = `M 0 ${crE}`;
d += ` L 0 ${h - crE}`;
@@ -390,7 +391,7 @@ Item {
w = w - wing;
const r = wing;
const cr = rt;
const crE = frameShapesBar ? 0 : cr;
const crE = frameShapesBar || edgeAttached ? 0 : cr;
let d = `M ${fullW} ${crE}`;
d += ` L ${fullW} ${h - crE}`;
@@ -514,11 +515,12 @@ Item {
if (isTop) {
const w = fullW - i * 2;
const h = fullH - wing - i * 2;
const crE = edgeAttached ? 0 : cr;
let d = `M ${i + cr} ${i}`;
d += ` L ${i + w - cr} ${i}`;
if (cr > 0)
d += ` A ${cr} ${cr} 0 0 1 ${i + w} ${i + cr}`;
let d = `M ${i + crE} ${i}`;
d += ` L ${i + w - crE} ${i}`;
if (crE > 0)
d += ` A ${crE} ${crE} 0 0 1 ${i + w} ${i + crE}`;
if (r > 0) {
d += ` L ${i + w} ${fullH - i}`;
d += ` A ${r} ${r} 0 0 0 ${i + w - r} ${i + h}`;
@@ -532,9 +534,9 @@ Item {
if (cr > 0)
d += ` A ${cr} ${cr} 0 0 1 ${i} ${i + h - cr}`;
}
d += ` L ${i} ${i + cr}`;
if (cr > 0)
d += ` A ${cr} ${cr} 0 0 1 ${i + cr} ${i}`;
d += ` L ${i} ${i + crE}`;
if (crE > 0)
d += ` A ${crE} ${crE} 0 0 1 ${i + crE} ${i}`;
d += " Z";
return d;
}
@@ -542,11 +544,12 @@ Item {
if (isBottom) {
const w = fullW - i * 2;
const h = fullH - wing - i * 2;
const crE = edgeAttached ? 0 : cr;
let d = `M ${i + cr} ${fullH - i}`;
d += ` L ${i + w - cr} ${fullH - i}`;
if (cr > 0)
d += ` A ${cr} ${cr} 0 0 0 ${i + w} ${fullH - i - cr}`;
let d = `M ${i + crE} ${fullH - i}`;
d += ` L ${i + w - crE} ${fullH - i}`;
if (crE > 0)
d += ` A ${crE} ${crE} 0 0 0 ${i + w} ${fullH - i - crE}`;
if (r > 0) {
d += ` L ${i + w} ${i}`;
d += ` A ${r} ${r} 0 0 1 ${i + w - r} ${i + r}`;
@@ -560,9 +563,9 @@ Item {
if (cr > 0)
d += ` A ${cr} ${cr} 0 0 0 ${i} ${i + cr}`;
}
d += ` L ${i} ${fullH - i - cr}`;
if (cr > 0)
d += ` A ${cr} ${cr} 0 0 0 ${i + cr} ${fullH - i}`;
d += ` L ${i} ${fullH - i - crE}`;
if (crE > 0)
d += ` A ${crE} ${crE} 0 0 0 ${i + crE} ${fullH - i}`;
d += " Z";
return d;
}
@@ -570,11 +573,12 @@ Item {
if (isLeft) {
const w = fullW - wing - i * 2;
const h = fullH - i * 2;
const crE = edgeAttached ? 0 : cr;
let d = `M ${i} ${i + cr}`;
d += ` L ${i} ${i + h - cr}`;
if (cr > 0)
d += ` A ${cr} ${cr} 0 0 0 ${i + cr} ${i + h}`;
let d = `M ${i} ${i + crE}`;
d += ` L ${i} ${i + h - crE}`;
if (crE > 0)
d += ` A ${crE} ${crE} 0 0 0 ${i + crE} ${i + h}`;
if (r > 0) {
d += ` L ${fullW - i} ${i + h}`;
d += ` A ${r} ${r} 0 0 1 ${i + w} ${i + h - r}`;
@@ -588,9 +592,9 @@ Item {
if (cr > 0)
d += ` A ${cr} ${cr} 0 0 0 ${i + w - cr} ${i}`;
}
d += ` L ${i + cr} ${i}`;
if (cr > 0)
d += ` A ${cr} ${cr} 0 0 0 ${i} ${i + cr}`;
d += ` L ${i + crE} ${i}`;
if (crE > 0)
d += ` A ${crE} ${crE} 0 0 0 ${i} ${i + crE}`;
d += " Z";
return d;
}
@@ -598,11 +602,12 @@ Item {
if (isRight) {
const w = fullW - wing - i * 2;
const h = fullH - i * 2;
const crE = edgeAttached ? 0 : cr;
let d = `M ${fullW - i} ${i + cr}`;
d += ` L ${fullW - i} ${i + h - cr}`;
if (cr > 0)
d += ` A ${cr} ${cr} 0 0 1 ${fullW - i - cr} ${i + h}`;
let d = `M ${fullW - i} ${i + crE}`;
d += ` L ${fullW - i} ${i + h - crE}`;
if (crE > 0)
d += ` A ${crE} ${crE} 0 0 1 ${fullW - i - crE} ${i + h}`;
if (r > 0) {
d += ` L ${i} ${i + h}`;
d += ` A ${r} ${r} 0 0 0 ${i + r} ${i + h - r}`;
@@ -616,9 +621,9 @@ Item {
if (cr > 0)
d += ` A ${cr} ${cr} 0 0 1 ${wing + i + cr} ${i}`;
}
d += ` L ${fullW - i - cr} ${i}`;
if (cr > 0)
d += ` A ${cr} ${cr} 0 0 1 ${fullW - i} ${i + cr}`;
d += ` L ${fullW - i - crE} ${i}`;
if (crE > 0)
d += ` A ${crE} ${crE} 0 0 1 ${fullW - i} ${i + crE}`;
d += " Z";
return d;
}
+19 -9
View File
@@ -559,7 +559,8 @@ Item {
shouldHideForWindows = filtered.length > 0;
}
property real effectiveSpacing: (FrameTransitionState.effectiveFrameEnabled && usesFrameBarChrome) ? 0 : ((flattenForMaximizedWindow && hasMaximizedToplevel) ? 0 : (barConfig?.spacing ?? 4))
readonly property bool edgeAttached: (barConfig?.attachToScreenEdge ?? false) && !(FrameTransitionState.effectiveFrameEnabled && usesFrameBarChrome)
property real effectiveSpacing: (FrameTransitionState.effectiveFrameEnabled && usesFrameBarChrome) ? 0 : ((edgeAttached || (flattenForMaximizedWindow && hasMaximizedToplevel)) ? 0 : (barConfig?.spacing ?? 4))
Behavior on effectiveSpacing {
enabled: barWindow.hostWindow?.visible ?? false
@@ -571,6 +572,13 @@ 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 effectiveBarLengthPadding: {
if ((FrameTransitionState.effectiveFrameEnabled && usesFrameBarChrome) || (flattenForMaximizedWindow && hasMaximizedToplevel))
return 0;
const pad = Math.max(0, barConfig?.barLengthPadding ?? 0);
const length = isVertical ? height : width;
return length > 0 ? Math.min(pad, Math.max(0, length / 2 - effectiveSpacing)) : pad;
}
readonly property bool effectiveOpenOnOverview: FrameTransitionState.effectiveFrameEnabled ? SettingsData.frameShowOnOverview : (barConfig?.openOnOverview ?? false)
readonly property real widgetThickness: Theme.snap(Math.max(20, 26 + (barConfig?.innerPadding ?? 4) * 0.6), _dpr)
@@ -766,6 +774,7 @@ Item {
id: inputMask
readonly property int barThickness: Theme.px(barWindow.effectiveBarThickness + barWindow.effectiveSpacing, barWindow._dpr)
readonly property int lengthPaddingPx: Theme.px(barWindow.effectiveBarLengthPadding, barWindow._dpr)
readonly property bool inOverviewWithShow: CompositorService.isNiri && NiriService.inOverview && barWindow.effectiveOpenOnOverview
readonly property bool effectiveVisible: (barConfig?.visible ?? true) || inOverviewWithShow
@@ -775,7 +784,7 @@ Item {
x: {
if (!axis.isVertical) {
return 0;
return lengthPaddingPx;
} else {
switch (barPos) {
case SettingsData.Position.Left:
@@ -789,7 +798,7 @@ Item {
}
y: {
if (axis.isVertical) {
return 0;
return lengthPaddingPx;
} else {
switch (barPos) {
case SettingsData.Position.Top:
@@ -801,8 +810,8 @@ Item {
}
}
}
width: axis.isVertical ? maskThickness : parent.width
height: axis.isVertical ? parent.height : maskThickness
width: axis.isVertical ? maskThickness : parent.width - lengthPaddingPx * 2
height: axis.isVertical ? parent.height - lengthPaddingPx * 2 : maskThickness
}
readonly property bool clickThroughEnabled: barConfig?.clickThrough ?? false
@@ -1074,11 +1083,12 @@ Item {
Item {
id: barUnitInset
property int spacingPx: Theme.px(barWindow.effectiveSpacing, barWindow._dpr)
property int lengthPaddingPx: Theme.px(barWindow.effectiveBarLengthPadding, barWindow._dpr)
anchors.fill: parent
anchors.leftMargin: !barWindow.isVertical ? spacingPx : (axis.edge === "left" ? spacingPx : 0)
anchors.rightMargin: !barWindow.isVertical ? spacingPx : (axis.edge === "right" ? spacingPx : 0)
anchors.topMargin: barWindow.isVertical ? (barWindow.hasAdjacentTopBar ? 0 : spacingPx) : (axis.outerVisualEdge() === "bottom" ? 0 : spacingPx)
anchors.bottomMargin: barWindow.isVertical ? (barWindow.hasAdjacentBottomBar ? 0 : spacingPx) : (axis.outerVisualEdge() === "bottom" ? spacingPx : 0)
anchors.leftMargin: !barWindow.isVertical ? spacingPx + lengthPaddingPx : (axis.edge === "left" ? spacingPx : 0)
anchors.rightMargin: !barWindow.isVertical ? spacingPx + lengthPaddingPx : (axis.edge === "right" ? spacingPx : 0)
anchors.topMargin: barWindow.isVertical ? (barWindow.hasAdjacentTopBar ? 0 : spacingPx) + lengthPaddingPx : (axis.outerVisualEdge() === "bottom" ? 0 : spacingPx)
anchors.bottomMargin: barWindow.isVertical ? (barWindow.hasAdjacentBottomBar ? 0 : spacingPx) + lengthPaddingPx : (axis.outerVisualEdge() === "bottom" ? spacingPx : 0)
BarCanvas {
id: barBackground
+50 -12
View File
@@ -136,7 +136,9 @@ Item {
rightWidgets: defaultBar.rightWidgets || [],
spacing: defaultBar.spacing ?? 4,
innerPadding: defaultBar.innerPadding ?? 4,
barLengthPadding: defaultBar.barLengthPadding ?? 0,
bottomGap: defaultBar.bottomGap ?? 0,
attachToScreenEdge: defaultBar.attachToScreenEdge ?? false,
transparency: defaultBar.transparency ?? 1.0,
widgetTransparency: defaultBar.widgetTransparency ?? 1.0,
squareCorners: defaultBar.squareCorners ?? false,
@@ -1029,6 +1031,30 @@ Item {
}
}
SettingsSliderRow {
id: barLengthPaddingSlider
settingKey: "barLengthPadding"
visible: !SettingsData.frameEnabled
text: I18n.tr("Bar Length Padding")
description: I18n.tr("Reduce the visible bar length from both ends")
tags: ["bar", "length", "padding", "size", "shorter"]
unit: "px"
minimum: 0
maximum: 512
defaultValue: 0
value: selectedBarConfig?.barLengthPadding ?? 0
onSliderDragFinished: finalValue => SettingsData.updateBarConfig(selectedBarId, {
barLengthPadding: finalValue
})
Binding {
target: barLengthPaddingSlider
property: "value"
value: selectedBarConfig?.barLengthPadding ?? 0
restoreMode: Binding.RestoreBinding
}
}
SettingsToggleRow {
visible: !SettingsData.frameEnabled
text: I18n.tr("Sync Bar Inset Padding")
@@ -1166,18 +1192,6 @@ Item {
reason: I18n.tr("Managed by Frame")
}
SettingsToggleRow {
settingKey: "barSquareCorners"
tags: ["square", "corners", "rounding"]
text: I18n.tr("Square Corners")
description: I18n.tr("Remove corner rounding from the bar")
visible: !SettingsData.frameEnabled
checked: selectedBarConfig?.squareCorners ?? false
onToggled: checked => SettingsData.updateBarConfig(selectedBarId, {
squareCorners: checked
})
}
SettingsToggleRow {
settingKey: "barNoBackground"
tags: ["transparent", "background", "invisible"]
@@ -1230,6 +1244,30 @@ Item {
opacity: 0.15
}
SettingsButtonGroupRow {
settingKey: "barCornerStyle"
tags: ["rounded", "attached", "square", "corners", "edge", "screen", "flush"]
text: I18n.tr("Corner Style")
description: I18n.tr("Choose how the bar meets the screen edge")
visible: !SettingsData.frameEnabled
model: [I18n.tr("Rounded", "bar corner style option"), I18n.tr("Flush", "bar corner style option"), I18n.tr("Square", "bar corner style option")]
currentIndex: {
if (selectedBarConfig?.squareCorners ?? false)
return 2;
if (selectedBarConfig?.attachToScreenEdge ?? false)
return 1;
return 0;
}
onSelectionChanged: (index, selected) => {
if (!selected)
return;
SettingsData.updateBarConfig(selectedBarId, {
squareCorners: index === 2,
attachToScreenEdge: index === 1
});
}
}
SettingsToggleRow {
settingKey: "barGothCorners"
tags: ["goth", "corners", "concave", "cutout"]
@@ -787,6 +787,29 @@
],
"description": "Automatically hide the bar when the pointer moves away"
},
{
"section": "barLengthPadding",
"label": "Bar Length Padding",
"tabIndex": 3,
"category": "Dank Bar",
"keywords": [
"bar",
"both",
"dank",
"ends",
"length",
"padding",
"panel",
"reduce",
"shorter",
"size",
"statusbar",
"taskbar",
"topbar",
"visible"
],
"description": "Reduce the visible bar length from both ends"
},
{
"section": "barClickThrough",
"label": "Click Through",
@@ -831,6 +854,34 @@
],
"icon": "dashboard"
},
{
"section": "barCornerStyle",
"label": "Corner Style",
"tabIndex": 3,
"category": "Dank Bar",
"keywords": [
"attached",
"bar",
"choose",
"corner",
"corners",
"dank",
"edge",
"flush",
"meets",
"panel",
"radius",
"round",
"rounded",
"screen",
"square",
"statusbar",
"style",
"taskbar",
"topbar"
],
"description": "Choose how the bar meets the screen edge"
},
{
"section": "barDisplay",
"label": "Display Assignment",
@@ -1199,29 +1250,6 @@
],
"description": "Adjust the bar height via inner padding"
},
{
"section": "barSquareCorners",
"label": "Square Corners",
"tabIndex": 3,
"category": "Dank Bar",
"keywords": [
"bar",
"corner",
"corners",
"dank",
"panel",
"radius",
"remove",
"round",
"rounded",
"rounding",
"square",
"statusbar",
"taskbar",
"topbar"
],
"description": "Remove corner rounding from the bar"
},
{
"section": "barAutoHideStrict",
"label": "Strict auto-hide",
@@ -2425,22 +2453,22 @@
"keywords": [
"background",
"bar",
"corner",
"corners",
"dank",
"fully",
"invisible",
"make",
"panel",
"radius",
"remove",
"round",
"rounded",
"rounding",
"square",
"statusbar",
"taskbar",
"topbar"
"topbar",
"transparent"
],
"icon": "rounded_corner",
"description": "Remove corner rounding from the bar"
"description": "Make the bar background fully transparent"
},
{
"section": "barAppearance",