quickshell: menus yield to the toast, not the other way round

The toast dodging open dropdowns meant it drifted around the column
depending on which menu was open. Invert it: the toast keeps its band
and melts into the frame as before, and a dropdown treats a live toast
as a shortened screen — its clamp ends short of the toast, so a menu
that would collide opens higher instead.

Snapping still tests against the frame, so a panel stopping above a
toast floats clear of it rather than fusing to its edge.
This commit is contained in:
rope 2026-08-30 13:50:15 +01:00
parent ddcc5c63be
commit 6d58055723

View file

@ -2289,9 +2289,9 @@ in
: Qt.vector4d(0, 0, 0, 0) : Qt.vector4d(0, 0, 0, 0)
// Toast melts into the band it sits against toastItem // Toast melts into the band it sits against toastItem
// picks the side, this just buries the matching edge. // picks the side, this just buries the matching edge.
readonly property real toastTop: toastItem.atBottom || toastItem.floating readonly property real toastTop: toastItem.atBottom
? toastItem.y + 8 : meltTop ? toastItem.y + 8 : meltTop
readonly property real toastBot: toastItem.atBottom && !toastItem.floating readonly property real toastBot: toastItem.atBottom
? meltBot : toastItem.y + 8 + _toastRect.height ? meltBot : toastItem.y + 8 + _toastRect.height
property vector4d toast: toastItem.visible && _toastRect.width > 0.5 property vector4d toast: toastItem.visible && _toastRect.width > 0.5
? Qt.vector4d(surfLeftX + _toastRect.width / 2, (toastTop + toastBot) / 2, ? Qt.vector4d(surfLeftX + _toastRect.width / 2, (toastTop + toastBot) / 2,
@ -4984,8 +4984,19 @@ in
// is taller than the space (yMax < yMin). // is taller than the space (yMax < yMin).
readonly property real yMin: Theme.frameWidth readonly property real yMin: Theme.frameWidth
readonly property real yMax: bar.height - Theme.frameWidth - height readonly property real yMax: bar.height - Theme.frameWidth - height
// A visible toast owns its band, so the space a panel has
// to sit in ends short of it. Only the band the toast is
// actually on shrinks; the other one is still the frame.
readonly property bool toastBlocks: toastItem.visible && toastItem.isPrimary
readonly property real lo: toastBlocks && !toastItem.atBottom
? Math.max(yMin, toastItem.y + toastItem.height + Theme.panelGap) : yMin
readonly property real hi: toastBlocks && toastItem.atBottom
? Math.min(yMax, toastItem.y - Theme.panelGap - height) : yMax
// Where the panel lands when it just tracks its widget. // Where the panel lands when it just tracks its widget.
readonly property real yTracked: Math.max(yMin, Math.min(yMax, dropdownY - height / 2)) // hi < lo means the panel is taller than what the toast
// leaves nothing to be done, so it takes the frame and
// the toast rides over it for its five seconds.
readonly property real yTracked: Math.max(lo, Math.min(hi, dropdownY - height / 2))
x: Theme.barWidth x: Theme.barWidth
// Snap to whichever band is within reach, and only then. // Snap to whichever band is within reach, and only then.
// Proximity rather than an alignTop/alignBottom flag per // Proximity rather than an alignTop/alignBottom flag per
@ -4994,10 +5005,22 @@ in
// stops no flag to remember to flip. A panel taller than // stops no flag to remember to flip. A panel taller than
// the screen has yMax < yMin, so yTracked pins to yMin and // the screen has yMax < yMin, so yTracked pins to yMin and
// the first branch catches it. // the first branch catches it.
// Snapping still tests against the FRAME, not against a
// toast-shortened edge: a panel stopping above a toast is
// meant to read as floating clear of it, not fused to it.
y: Math.round( y: Math.round(
yTracked - yMin <= Theme.snapDist ? yMin yTracked - yMin <= Theme.snapDist ? yMin
: yMax - yTracked <= Theme.snapDist ? Math.max(yMin, yMax) : yMax - yTracked <= Theme.snapDist ? Math.max(yMin, yMax)
: yTracked) : yTracked)
// A toast arriving under an open menu moves it. Match the
// chrome's morph, or the content jumps while the surface
// it sits in slides. Off while closed so opening still
// places the panel instantly.
Behavior on y {
enabled: dropdown.open
NumberAnimation { duration: Theme.animMorph; easing.type: Easing.OutExpo }
}
width: fullWidth + 4 width: fullWidth + 4
// +24, not +16: the shader insets the panel 8px top and // +24, not +16: the shader insets the panel 8px top and
// bottom, so +16 left the panel edge flush with the content // bottom, so +16 left the panel edge flush with the content
@ -7610,34 +7633,13 @@ in
// Flush to that band (it melts into it), never centred on // Flush to that band (it melts into it), never centred on
// the clock a short toast floating mid-column reads as a // the clock a short toast floating mid-column reads as a
// stray card instead of a surface growing out of the frame. // stray card instead of a surface growing out of the frame.
readonly property real baseY: atBottom // The toast never moves for a menu: it is the thing the
// eye goes to, and a card that slides around the column
// depending on which dropdown happens to be open reads as
// a glitch. Dropdowns yield to it instead (see BarDropdown).
y: Math.round(atBottom
? bar.height - Theme.frameWidth - height - 8 ? bar.height - Theme.frameWidth - height - 8
: Theme.frameWidth + 8 : Theme.frameWidth + 8)
// Dropdowns share this column, so a toast arriving under an
// open one lands on top of it. Step away from the band far
// enough to clear the panel, capped by the room left before
// the opposite frame a full-height panel pushes the toast
// as far as it goes and no further, rather than off screen.
readonly property real dodge: {
const d = bar.activeDropdown;
if (!d || !d.visible) return 0;
const want = atBottom
? (d.y + d.height + Theme.panelGap) - baseY
: (baseY + height + Theme.panelGap) - d.y;
const room = atBottom
? baseY - Theme.frameWidth - 8
: bar.height - Theme.frameWidth - 8 - height - baseY;
return Math.max(0, Math.min(want, room));
}
// Once it has stepped away it is no longer against the
// band, so the shader draws it as a free-standing card.
readonly property bool floating: dodge > 0
y: Math.round(atBottom ? baseY - dodge : baseY + dodge)
// Opening a dropdown under a live toast moves it; slide.
Behavior on y {
NumberAnimation { duration: Theme.animContent; easing.type: Easing.OutCubic }
}
width: _toastRect.width + 4 width: _toastRect.width + 4
height: _toastRect.height + 16 height: _toastRect.height + 16