feat: improve popouts

Fix bg tracking, use slow spatial for detach, default spatial for
everything else, adjust deform, move instead of width scale
This commit is contained in:
2 * r + 2 * t 2026-04-08 17:06:37 +10:00
parent 293b6f4b2b
commit df27c93224
8 changed files with 101 additions and 79 deletions

View file

@ -0,0 +1,55 @@
pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
import qs.components
import qs.config
Item {
id: root
required property ShellScreen screen
readonly property alias content: content
property real offsetScale: x > 0 || content.hasCurrent ? 0 : 1
visible: width > 0 && height > 0
clip: true
implicitWidth: content.implicitWidth * (1 - offsetScale)
implicitHeight: content.implicitHeight
Behavior on offsetScale {
Anim {
duration: Appearance.anim.durations.expressiveDefaultSpatial
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
}
}
Behavior on x {
Anim {
duration: content.animLength
easing.bezierCurve: content.animCurve
}
}
Behavior on y {
enabled: root.offsetScale < 1
Anim {
duration: content.animLength
easing.bezierCurve: content.animCurve
}
}
Wrapper {
id: content
screen: root.screen
offsetScale: root.offsetScale
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: (-implicitWidth - 5) * root.offsetScale
}
}

View file

@ -14,46 +14,50 @@ Item {
id: root
required property ShellScreen screen
required property real offsetScale
readonly property real shownWidth: children.find(c => c.shouldBeActive)?.implicitWidth ?? content.implicitWidth
readonly property real nonAnimWidth: x > 0 || hasCurrent ? shownWidth : 0
readonly property alias content: content
readonly property alias winfo: winfo
readonly property alias controlCenter: controlCenter
readonly property real nonAnimWidth: children.find(c => c.shouldBeActive)?.implicitWidth ?? content.implicitWidth
readonly property real nonAnimHeight: children.find(c => c.shouldBeActive)?.implicitHeight ?? content.implicitHeight
readonly property Item current: (content.item as Content)?.current ?? null
readonly property bool isDetached: detachedMode.length > 0
property alias currentName: popoutState.currentName
property real currentCenter
property alias hasCurrent: popoutState.hasCurrent
readonly property PopoutState popState: popoutState
property real currentCenter
property string detachedMode
property string queuedMode
readonly property bool isDetached: detachedMode.length > 0
property int animLength: Appearance.anim.durations.normal
property list<real> animCurve: Appearance.anim.curves.emphasized
property int animLength: Appearance.anim.durations.expressiveDefaultSpatial
property list<real> animCurve: Appearance.anim.curves.expressiveDefaultSpatial
function setAnims(detach: bool): void {
const type = `expressive${detach ? "Slow" : "Default"}Spatial`;
animLength = Appearance.anim.durations[type];
animCurve = Appearance.anim.curves[type];
}
function detach(mode: string): void {
animLength = Appearance.anim.durations.large;
setAnims(true);
if (mode === "winfo") {
detachedMode = mode;
} else {
queuedMode = mode;
detachedMode = "any";
}
setAnims(false);
focus = true;
}
function close(): void {
hasCurrent = false;
animCurve = Appearance.anim.curves.emphasizedAccel;
animLength = Appearance.anim.durations.normal;
detachedMode = "";
animCurve = Appearance.anim.curves.emphasized;
}
visible: width > 0 && height > 0
clip: true
implicitWidth: nonAnimWidth
implicitHeight: nonAnimHeight
@ -90,15 +94,7 @@ Item {
}
Binding {
when: root.isDetached
target: QsWindow.window
property: "WlrLayershell.keyboardFocus"
value: WlrKeyboardFocus.OnDemand
}
Binding {
when: root.hasCurrent && root.currentName === "wirelesspassword"
when: root.isDetached || (root.hasCurrent && root.currentName === "wirelesspassword")
target: QsWindow.window
property: "WlrLayershell.keyboardFocus"
@ -118,6 +114,8 @@ Item {
}
Comp {
id: winfo
shouldBeActive: root.detachedMode === "winfo"
anchors.centerIn: parent
@ -128,32 +126,15 @@ Item {
}
Comp {
id: controlCenter
shouldBeActive: root.detachedMode === "any"
anchors.centerIn: parent
sourceComponent: ControlCenter {
function close(): void {
root.close();
}
screen: root.screen
active: root.queuedMode
}
}
Behavior on x {
Anim {
duration: root.animLength
easing.bezierCurve: root.animCurve
}
}
Behavior on y {
enabled: root.implicitWidth > 0
Anim {
duration: root.animLength
easing.bezierCurve: root.animCurve
onClose: root.close()
}
}
@ -165,7 +146,7 @@ Item {
}
Behavior on implicitHeight {
enabled: root.implicitWidth > 0
enabled: root.offsetScale < 1
Anim {
duration: root.animLength
@ -181,6 +162,7 @@ Item {
active: false
opacity: 0
// Makes the loader load on the same frame shouldBeActive becomes true, which ensures size is set
states: State {
name: "active"
when: comp.shouldBeActive

View file

@ -25,8 +25,7 @@ Item {
root: root
}
function close(): void {
}
signal close
implicitWidth: implicitHeight * Config.controlCenter.sizes.ratio
implicitHeight: screen.height * Config.controlCenter.sizes.heightMult

View file

@ -45,12 +45,9 @@ Singleton {
ControlCenter {
id: cc
function close(): void {
win.destroy();
}
anchors.fill: parent
screen: win.screen
onClose: win.destroy()
floating: true
}

View file

@ -211,23 +211,10 @@ StyledWindow {
PanelBg {
id: popoutBg
panel: panels.popouts
x: bar.implicitWidth - (panels.popouts.isDetached ? -(root.width - panels.popouts.shownWidth) / 2 : panels.popouts.hasCurrent ? 0 : panels.popouts.shownWidth + 5)
implicitWidth: panels.popouts.shownWidth
Behavior on x {
Anim {
duration: Appearance.anim.durations.expressiveDefaultSpatial
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
}
}
Behavior on implicitWidth {
Anim {
duration: Appearance.anim.durations.expressiveDefaultSpatial
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
}
}
panel: panels.popoutsWrapper
deformAmount: panels.popouts.isDetached ? 0.05 : panels.popouts.hasCurrent ? 0.15 : 0.1
x: panels.popoutsWrapper.x + panels.popouts.x + bar.implicitWidth
implicitWidth: panels.popouts.width
}
}

View file

@ -211,7 +211,7 @@ CustomMouseArea {
// Show popouts on hover
if (x < bar.implicitWidth) {
bar.checkPopout(y);
} else if ((!popouts.currentName.startsWith("traymenu") || ((popouts.current as StackView)?.depth ?? 0) <= 1) && !inLeftPanel(panels.popouts, x, y)) {
} else if ((!popouts.currentName.startsWith("traymenu") || ((popouts.current as StackView)?.depth ?? 0) <= 1) && !inLeftPanel(panels.popoutsWrapper, x, y)) {
popouts.hasCurrent = false;
bar.closeTray();
}

View file

@ -28,13 +28,14 @@ Item {
readonly property alias sessionWrapper: sessionWrapper
readonly property alias launcher: launcher
readonly property alias dashboard: dashboard
readonly property alias popouts: popouts
readonly property alias popouts: popoutsWrapper.content
readonly property alias popoutsWrapper: popoutsWrapper
readonly property alias utilities: utilities
readonly property alias toasts: toasts
readonly property alias sidebar: sidebar
anchors.fill: parent
anchors.margins: root.borderThickness
anchors.margins: borderThickness
anchors.leftMargin: bar.implicitWidth
Item {
@ -114,18 +115,18 @@ Item {
anchors.top: parent.top
}
BarPopouts.Wrapper {
id: popouts
BarPopouts.ClipWrapper {
id: popoutsWrapper
screen: root.screen
x: isDetached ? (root.width - nonAnimWidth) / 2 : 0
x: content.isDetached ? (root.width - content.nonAnimWidth) / 2 : 0
y: {
if (isDetached)
return (root.height - nonAnimHeight) / 2;
if (content.isDetached)
return (root.height - content.nonAnimHeight) / 2;
const off = currentCenter - root.borderThickness - nonAnimHeight / 2;
const diff = root.height - Math.floor(off + nonAnimHeight);
const off = content.currentCenter - root.borderThickness - content.nonAnimHeight / 2;
const diff = root.height - Math.floor(off + content.nonAnimHeight);
if (diff < 0)
return off + diff;
return Math.max(off, 0);
@ -137,7 +138,7 @@ Item {
visibilities: root.visibilities
sidebar: sidebar
popouts: popouts
popouts: popoutsWrapper.content
anchors.bottom: parent.bottom
anchors.right: parent.right

View file

@ -65,7 +65,8 @@ Region {
}
R {
panel: root.panels.popouts
panel: root.panels.popoutsWrapper
width: panel.width * (1 - root.panels.popoutsWrapper.offsetScale)
}
component R: Region {