quickshell: merge menu and toast into one surface, drop the double ease

The bump was two animations stacked: _dropdownRect is positioned off
chrome.y, so chrome already eased the panel to wherever y resolved, and
the Behavior added last commit put an OutExpo behind an OutExpo.

Panels now land ON the toast rather than a panelGap short of it, buried
half a radius so the shader smins them into one outline with a shared
waist. Cards inside still clear each other by ~14px.
This commit is contained in:
rope 2026-08-30 13:56:28 +01:00
parent 6d58055723
commit e45c9644a1

View file

@ -4988,10 +4988,18 @@ in
// 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 toastMerge: Theme.radius / 2
readonly property real lo: toastBlocks && !toastItem.atBottom
? Math.max(yMin, toastItem.y + toastItem.height + Theme.panelGap) : yMin
? Math.max(yMin, toastItem.y + toastItem.height - 16 - toastMerge) : yMin
// Panel surface spans [y + 8, y + height - 8]; the toast's
// starts at its y + 8. Landing the first ON the second (plus
// a half-radius of burial, the fillet scale the shader smins
// at) merges them into one outline with a shared waist,
// rather than parking two surfaces a hairline apart. The
// cards inside still clear each other by ~14px: 12 of panel
// padding, 8 of toast inset, less the burial.
readonly property real hi: toastBlocks && toastItem.atBottom
? Math.min(yMax, toastItem.y - Theme.panelGap - height) : yMax
? Math.min(yMax, toastItem.y + 16 + toastMerge - height) : yMax
// Where the panel lands when it just tracks its widget.
// hi < lo means the panel is taller than what the toast
// leaves nothing to be done, so it takes the frame and
@ -5005,22 +5013,19 @@ in
// stops no flag to remember to flip. A panel taller than
// the screen has yMax < yMin, so yTracked pins to yMin and
// 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.
// Snapping still tests against the FRAME. A panel held off
// a band by a toast is already fused to the toast, which is
// itself fused to that band one outline either way.
y: Math.round(
yTracked - yMin <= Theme.snapDist ? yMin
: yMax - yTracked <= Theme.snapDist ? Math.max(yMin, yMax)
: 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 }
}
// No Behavior here on purpose: _dropdownRect is positioned
// off chrome.y, and chrome already eases to whatever this
// resolves to. Animating y as well put an OutExpo behind an
// OutExpo, which is what made a toast arriving under an
// open menu look like it physically shoved it.
width: fullWidth + 4
// +24, not +16: the shader insets the panel 8px top and
// bottom, so +16 left the panel edge flush with the content