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>
This commit is contained in:
28allday 2026-06-13 23:34:44 +01:00
parent b1e2bb3bc6
commit 8bada1d61a
2 changed files with 18 additions and 15 deletions

View file

@ -12,21 +12,24 @@ Item {
required property real borderThickness
readonly property alias content: content
property real offsetScale: x > 0 || content.hasCurrent ? 0 : 1
property real offsetScale: y > 0 || content.hasCurrent ? 0 : 1
visible: width > 0 && height > 0
clip: true
implicitWidth: content.implicitWidth * (1 - offsetScale)
implicitHeight: content.implicitHeight
// 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)
x: content.isDetached ? (parent.width - content.nonAnimWidth) / 2 : 0
y: {
y: content.isDetached ? (parent.height - content.nonAnimHeight) / 2 : 0
x: {
if (content.isDetached)
return (parent.height - content.nonAnimHeight) / 2;
return (parent.width - content.nonAnimWidth) / 2;
const off = content.currentCenter - borderThickness - content.nonAnimHeight / 2;
const diff = parent.height - Math.floor(off + content.nonAnimHeight);
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);
@ -36,14 +39,14 @@ Item {
Anim {}
}
Behavior on x {
Behavior on y {
Anim {
duration: content.animLength
easing: content.animCurve
}
}
Behavior on y {
Behavior on x {
enabled: root.offsetScale < 1
Anim {
@ -58,8 +61,8 @@ Item {
screen: root.screen
offsetScale: root.offsetScale
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: (-implicitWidth - 5) * root.offsetScale
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
anchors.topMargin: (-implicitHeight - 5) * root.offsetScale
}
}

View file

@ -149,14 +149,14 @@ Item {
}
}
Behavior on implicitWidth {
Behavior on implicitHeight {
Anim {
duration: root.animLength
easing: root.animCurve
}
}
Behavior on implicitHeight {
Behavior on implicitWidth {
enabled: root.offsetScale < 1
Anim {