diff --git a/settings/quickshell.nix b/settings/quickshell.nix index 0fe5c9d..23a482a 100644 --- a/settings/quickshell.nix +++ b/settings/quickshell.nix @@ -4989,8 +4989,12 @@ in // 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 - 16 - toastMerge) : yMin + // Only fuse with a toast that will match this width. + readonly property bool canFuse: fullWidth >= toastItem.minFuseW + readonly property real lo: !toastBlocks || toastItem.atBottom ? yMin + : Math.max(yMin, canFuse + ? toastItem.y + toastItem.height - 16 - toastMerge + : toastItem.y + toastItem.height + Theme.panelGap) // 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 @@ -4998,8 +5002,10 @@ in // 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 + 16 + toastMerge - height) : yMax + readonly property real hi: !toastBlocks || !toastItem.atBottom ? yMax + : Math.min(yMax, canFuse + ? toastItem.y + 16 + toastMerge - height + : toastItem.y - Theme.panelGap - height) // 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 @@ -7627,6 +7633,28 @@ in readonly property var mutedApps: ["discord", "Discord", "Vesktop", "vesktop", "Spotify", "spotify", "vlc", "mpv"] readonly property bool isPrimary: bar.screen === Quickshell.screens[0] + // The toast and an open dropdown are both left-anchored to + // the bar, so any width difference between them is a step + // on the shared right edge — and smin fillets a step into + // an S-curve waist, which is the warp at the junction. + // Take the panel's drawn width (fullWidth + 4, what chrome + // renders) and the fused outline is a straight edge. + // + // Keyed off the dropdown's DECLARED fullWidth and nothing + // else. Reading its live y or height here would close a + // loop: a panel's y depends on this toast's height, which + // depends on its width, which would depend on that y. + // + // Below the floor there is no width worth matching — the + // power menu is a 64px icon strip — so those keep their + // own width and stand off instead (BarDropdown.canFuse). + readonly property real minFuseW: 200 + readonly property real fuseW: { + const d = bar.activeDropdown; + if (!d || !d.visible || d.fullWidth < minFuseW) return 320; + return d.fullWidth + 4; + } + // Which frame band the toast lives against: the one the // clock sits nearest, so it lands on the same side as the // calendar popup the clock opens. Move the clock in the @@ -7720,7 +7748,7 @@ in // Reveals rightward now, so width is the animated axis // and the card inside keeps a fixed width (otherwise it // would squash instead of being wiped in). - width: toastItem.toastOpen ? 320 : 0 + width: toastItem.toastOpen ? toastItem.fuseW : 0 height: toastCard.height + 2 * Theme.panelGap clip: true @@ -7735,7 +7763,7 @@ in anchors.top: parent.top anchors.left: parent.left anchors.margins: Theme.panelGap - width: 320 - 2 * Theme.panelGap + width: toastItem.fuseW - 2 * Theme.panelGap height: Math.max(toastPreview.visible ? 48 : 0, toastCol.height) + 16 radius: Theme.radiusCard color: Theme.cardBg