quickshell: snap dropdowns to frame by proximity, toast dodges open menus
alignTop/alignBottom were per-dropdown flags, so the battery menu pinned to the bottom wherever its icon sat. Both replaced by a distance test against Theme.snapDist, which the SDF already used to decide melting — panels now fuse with a band only when they actually reach it, and a menu that gains or loses a card re-decides on its own. Toast steps clear of an open dropdown instead of drawing over it, and stops melting into the frame band once it has.
This commit is contained in:
parent
ba4c32d852
commit
ddcc5c63be
1 changed files with 49 additions and 21 deletions
|
|
@ -625,6 +625,11 @@ in
|
||||||
readonly property int radiusTiny: Math.round(radius * 0.5) // hover rows, action chips
|
readonly property int radiusTiny: Math.round(radius * 0.5) // hover rows, action chips
|
||||||
readonly property int cardPad: 8 // card inner padding (inset = 2×)
|
readonly property int cardPad: 8 // card inner padding (inset = 2×)
|
||||||
readonly property int panelGap: 8 // gap between panel border and cards (all dropdowns/menus/toast)
|
readonly property int panelGap: 8 // gap between panel border and cards (all dropdowns/menus/toast)
|
||||||
|
// How near a frame band a surface has to land before it fuses
|
||||||
|
// with it. Read by BarDropdown (where the panel is placed) and
|
||||||
|
// by the SDF (where it is melted) — they must agree, or the
|
||||||
|
// content sits a few px off the surface it is drawn inside.
|
||||||
|
readonly property int snapDist: panelGap + 4
|
||||||
|
|
||||||
// ── Animation duration tokens (ms) ──
|
// ── Animation duration tokens (ms) ──
|
||||||
readonly property int animMorph: 280 // panel grow / slide (OutExpo)
|
readonly property int animMorph: 280 // panel grow / slide (OutExpo)
|
||||||
|
|
@ -2266,10 +2271,9 @@ in
|
||||||
readonly property real meltTop: Theme.frameWidth - meltInto
|
readonly property real meltTop: Theme.frameWidth - meltInto
|
||||||
readonly property real meltBot: bar.height - Theme.frameWidth + meltInto
|
readonly property real meltBot: bar.height - Theme.frameWidth + meltInto
|
||||||
// Trigger distance is about proximity, not burial depth.
|
// Trigger distance is about proximity, not burial depth.
|
||||||
readonly property real snapDist: Theme.panelGap + 4
|
readonly property real panelTop: rawTop <= Theme.frameWidth + Theme.snapDist
|
||||||
readonly property real panelTop: rawTop <= Theme.frameWidth + snapDist
|
|
||||||
? meltTop : rawTop
|
? meltTop : rawTop
|
||||||
readonly property real panelBottom: rawBottom >= bar.height - Theme.frameWidth - snapDist
|
readonly property real panelBottom: rawBottom >= bar.height - Theme.frameWidth - Theme.snapDist
|
||||||
? meltBot : rawBottom
|
? meltBot : rawBottom
|
||||||
// Left edge lands at (barWidth − meltInto): the surface
|
// Left edge lands at (barWidth − meltInto): the surface
|
||||||
// buries a full corner radius inside the bar column.
|
// buries a full corner radius inside the bar column.
|
||||||
|
|
@ -2285,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
|
readonly property real toastTop: toastItem.atBottom || toastItem.floating
|
||||||
? toastItem.y + 8 : meltTop
|
? toastItem.y + 8 : meltTop
|
||||||
readonly property real toastBot: toastItem.atBottom
|
readonly property real toastBot: toastItem.atBottom && !toastItem.floating
|
||||||
? 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,
|
||||||
|
|
@ -4980,19 +4984,20 @@ 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 panel whose opener sits high on the bar looks adrift
|
// Where the panel lands when it just tracks its widget.
|
||||||
// when it merely centres on it — pin it to the frame, the
|
readonly property real yTracked: Math.max(yMin, Math.min(yMax, dropdownY - height / 2))
|
||||||
// way the bottom cluster's menus already land on yMax.
|
|
||||||
property bool alignTop: false
|
|
||||||
// Mirror for the bottom cluster: a tall panel opened from a
|
|
||||||
// low widget reads better fused to the bottom frame than
|
|
||||||
// floating a few px off it. max(yMin, ...) keeps a panel
|
|
||||||
// taller than the screen pinned to the top instead.
|
|
||||||
property bool alignBottom: false
|
|
||||||
x: Theme.barWidth
|
x: Theme.barWidth
|
||||||
y: alignTop ? Math.round(yMin)
|
// Snap to whichever band is within reach, and only then.
|
||||||
: alignBottom ? Math.round(Math.max(yMin, yMax))
|
// Proximity rather than an alignTop/alignBottom flag per
|
||||||
: Math.round(Math.max(yMin, Math.min(yMax, dropdownY - height / 2)))
|
// dropdown: a panel that gains a card later starts fusing
|
||||||
|
// with the frame on its own, and one that loses a card
|
||||||
|
// 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.
|
||||||
|
y: Math.round(
|
||||||
|
yTracked - yMin <= Theme.snapDist ? yMin
|
||||||
|
: yMax - yTracked <= Theme.snapDist ? Math.max(yMin, yMax)
|
||||||
|
: yTracked)
|
||||||
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
|
||||||
|
|
@ -5359,7 +5364,6 @@ in
|
||||||
// no rebuild and no restart.
|
// no rebuild and no restart.
|
||||||
BarDropdown {
|
BarDropdown {
|
||||||
id: wallDropdown
|
id: wallDropdown
|
||||||
alignTop: true
|
|
||||||
fullWidth: wallCard.width + 2 * Theme.panelGap
|
fullWidth: wallCard.width + 2 * Theme.panelGap
|
||||||
fullHeight: wallCard.height + 2 * Theme.panelGap
|
fullHeight: wallCard.height + 2 * Theme.panelGap
|
||||||
|
|
||||||
|
|
@ -6011,7 +6015,6 @@ in
|
||||||
id: batteryDropdown
|
id: batteryDropdown
|
||||||
fullWidth: batteryDropdownCol.width + 2 * Theme.panelGap
|
fullWidth: batteryDropdownCol.width + 2 * Theme.panelGap
|
||||||
fullHeight: batteryDropdownCol.height + 2 * Theme.panelGap
|
fullHeight: batteryDropdownCol.height + 2 * Theme.panelGap
|
||||||
alignBottom: true
|
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
id: batteryDropdownCol
|
id: batteryDropdownCol
|
||||||
|
|
@ -7607,9 +7610,34 @@ 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.
|
||||||
y: Math.round(atBottom
|
readonly property real baseY: 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
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue