Fix notification popup placement

This commit is contained in:
Ryan Hughes
2026-06-16 01:25:04 -04:00
parent 2a7602a084
commit fd5063dc30
3 changed files with 66 additions and 10 deletions
@@ -165,6 +165,24 @@ function dumpRows(rows) {
return out return out
} }
function popupPlacement(barPosition, barClearance, gapsOut) {
var position = String(barPosition || "top")
var clearance = Number(barClearance)
var gap = Number(gapsOut)
if (!isFinite(clearance)) clearance = 0
if (!isFinite(gap)) gap = 0
return {
anchors: { top: true, bottom: false, left: false, right: true },
margins: {
top: position === "top" ? clearance : gap,
bottom: gap,
left: gap,
right: position === "right" ? clearance : gap
}
}
}
function imageExtension(srcPath) { function imageExtension(srcPath) {
var lower = String(srcPath || "").toLowerCase() var lower = String(srcPath || "").toLowerCase()
var dot = lower.lastIndexOf(".") var dot = lower.lastIndexOf(".")
@@ -188,6 +206,7 @@ if (typeof module !== "undefined") {
dedupeByOriginalId: dedupeByOriginalId, dedupeByOriginalId: dedupeByOriginalId,
parseHistory: parseHistory, parseHistory: parseHistory,
dumpRows: dumpRows, dumpRows: dumpRows,
popupPlacement: popupPlacement,
imageExtension: imageExtension imageExtension: imageExtension
} }
} }
+14 -10
View File
@@ -32,8 +32,9 @@ Item {
// Corner radius is shared with the menu and shell panels. // Corner radius is shared with the menu and shell panels.
// It mirrors Hyprland's current decoration:rounding value. // It mirrors Hyprland's current decoration:rounding value.
readonly property int cornerRadius: Style.cornerRadius readonly property int cornerRadius: Style.cornerRadius
// Surfaces anchor relative to the omarchy bar so popups and history land // Toasts are fixed to the top-right corner. They only clear the omarchy bar
// alongside the other shell panels rather than on top of the bar itself. // when the bar occupies the top or right edge, so left/bottom bars do not
// pull notification popups away from the expected top-right location.
// Falls back to the bar's default size (26 horizontal / 28 vertical) when // Falls back to the bar's default size (26 horizontal / 28 vertical) when
// shell.bar isn't reachable so the popup never lands on top of the bar. // 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 string barPosition: shell && shell.barConfig ? String(shell.barConfig.position || "top") : "top"
@@ -738,17 +739,20 @@ Item {
exclusionMode: ExclusionMode.Ignore exclusionMode: ExclusionMode.Ignore
color: "transparent" color: "transparent"
readonly property var popupPlacement: NotificationLogic.popupPlacement(
service.barPosition, service.barClearance, Style.gapsOut)
anchors { anchors {
top: service.barPosition !== "bottom" top: popupWindow.popupPlacement.anchors.top
bottom: service.barPosition === "bottom" bottom: popupWindow.popupPlacement.anchors.bottom
left: service.barPosition === "left" left: popupWindow.popupPlacement.anchors.left
right: service.barPosition !== "left" right: popupWindow.popupPlacement.anchors.right
} }
margins { margins {
top: service.barPosition === "top" ? service.barClearance : Style.gapsOut top: popupWindow.popupPlacement.margins.top
bottom: service.barPosition === "bottom" ? service.barClearance : Style.gapsOut bottom: popupWindow.popupPlacement.margins.bottom
left: service.barPosition === "left" ? service.barClearance : Style.gapsOut left: popupWindow.popupPlacement.margins.left
right: service.barPosition === "right" ? service.barClearance : Style.gapsOut right: popupWindow.popupPlacement.margins.right
} }
implicitWidth: popupColumn.implicitWidth implicitWidth: popupColumn.implicitWidth
+33
View File
@@ -47,6 +47,39 @@ assert(!notifications.shouldBypassDnd({ appName: 'Slack', urgency: 2 }, 2), 'cri
assert(!notifications.shouldBypassDnd({ appName: 'omarchy-menu-keybindings', urgency: 1 }, 2), 'omarchy command app names do not bypass DND') assert(!notifications.shouldBypassDnd({ appName: 'omarchy-menu-keybindings', urgency: 1 }, 2), 'omarchy command app names do not bypass DND')
assert(!notifications.isEphemeralApp('omarchy-menu-keybindings'), 'notifications treat omarchy command app names as normal apps') assert(!notifications.isEphemeralApp('omarchy-menu-keybindings'), 'notifications treat omarchy command app names as normal apps')
assertDeepEqual(
notifications.popupPlacement('top', 32, 6),
{
anchors: { top: true, bottom: false, left: false, right: true },
margins: { top: 32, bottom: 6, left: 6, right: 6 }
},
'notifications clear a top bar while staying anchored top-right'
)
assertDeepEqual(
notifications.popupPlacement('right', 32, 6),
{
anchors: { top: true, bottom: false, left: false, right: true },
margins: { top: 6, bottom: 6, left: 6, right: 32 }
},
'notifications clear a right bar while staying anchored top-right'
)
assertDeepEqual(
notifications.popupPlacement('bottom', 32, 6),
{
anchors: { top: true, bottom: false, left: false, right: true },
margins: { top: 6, bottom: 6, left: 6, right: 6 }
},
'notifications ignore a bottom bar for popup placement'
)
assertDeepEqual(
notifications.popupPlacement('left', 32, 6),
{
anchors: { top: true, bottom: false, left: false, right: true },
margins: { top: 6, bottom: 6, left: 6, right: 6 }
},
'notifications ignore a left bar for popup placement'
)
const notification = { const notification = {
id: 12, id: 12,
appName: 'Mail', appName: 'Mail',