nosignal-shell/modules/bar/popouts/ClipWrapper.qml
28allday 8bada1d61a feat(bar): popouts drop down from the top bar — Phase B
The bar hover fly-outs (wifi/audio/bluetooth/tray/active-window) slid out
sideways from the old left edge; now they drop DOWN from the top bar:

- ClipWrapper.qml: the collapsing/slide axis is now height (was width); the
  popout is positioned along the bar by x (from the hovered widget's x-centre,
  via currentCenter) and docks at the top edge; the dock<->detach animation
  guard moves to the x (along-bar) axis. The content slides in via a negative
  top margin instead of left margin.
- Wrapper.qml: mirror the size-transition animation guards (height is now the
  always-animated extent axis, width the gated along-bar axis).

Verified live in the dev VM: all popouts open downward under their widget.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 23:34:44 +01:00

68 lines
1.8 KiB
QML

pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
import qs.components
import qs.modules.bar.popouts // Need to import this module so the Wrapper type is the same as others
Item {
id: root
required property ShellScreen screen
required property real borderThickness
readonly property alias content: content
property real offsetScale: y > 0 || content.hasCurrent ? 0 : 1
visible: width > 0 && height > 0
clip: true
// NoSignal top bar: popouts drop DOWN from the bar. The collapsing axis is
// now height (was width for the vertical left bar); position along the bar
// is x (from the hovered widget's x-centre), the docked edge is the top.
implicitWidth: content.implicitWidth
implicitHeight: content.implicitHeight * (1 - offsetScale)
y: content.isDetached ? (parent.height - content.nonAnimHeight) / 2 : 0
x: {
if (content.isDetached)
return (parent.width - content.nonAnimWidth) / 2;
const off = content.currentCenter - borderThickness - content.nonAnimWidth / 2;
const diff = parent.width - Math.floor(off + content.nonAnimWidth);
if (diff < 0)
return off + diff;
return Math.max(off, 0);
}
Behavior on offsetScale {
Anim {}
}
Behavior on y {
Anim {
duration: content.animLength
easing: content.animCurve
}
}
Behavior on x {
enabled: root.offsetScale < 1
Anim {
duration: content.animLength
easing: content.animCurve
}
}
Wrapper {
id: content
screen: root.screen
offsetScale: root.offsetScale
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
anchors.topMargin: (-implicitHeight - 5) * root.offsetScale
}
}