test popouts

This commit is contained in:
2026-04-16 01:50:29 +02:00
parent f85bd82943
commit 9a606f3e58
19 changed files with 1981 additions and 71 deletions
+72 -59
View File
@@ -8,100 +8,88 @@ import qs.Config
Item {
id: root
property list<real> animCurve: MaterialEasing.emphasized
property int animLength: MaterialEasing.emphasizedDecelTime
readonly property Item current: content.item?.current ?? null
property list<real> animCurve: Appearance.anim.curves.expressiveDefaultSpatial
property int animLength: Appearance.anim.durations.expressiveDefaultSpatial
readonly property alias content: content
readonly property alias controlCenter: controlCenter
readonly property Item current: (content.item as Content)?.current ?? null
property real currentCenter
property string currentName
property alias currentName: popoutState.currentName
property string detachedMode
property bool hasCurrent
property alias hasCurrent: popoutState.hasCurrent
readonly property bool isDetached: detachedMode.length > 0
readonly property real nonAnimHeight: hasCurrent ? children.find(c => c.shouldBeActive)?.implicitHeight ?? content.implicitHeight : 0
readonly property real nonAnimHeight: children.find(c => c.shouldBeActive)?.implicitHeight ?? content.implicitHeight
readonly property real nonAnimWidth: children.find(c => c.shouldBeActive)?.implicitWidth ?? content.implicitWidth
required property real offsetScale
property string queuedMode
required property ShellScreen screen
readonly property alias winfo: winfo
function close(): void {
hasCurrent = false;
animCurve = MaterialEasing.emphasizedDecel;
animLength = MaterialEasing.emphasizedDecelTime;
detachedMode = "";
animCurve = MaterialEasing.emphasized;
}
function detach(mode: string): void {
animLength = 600;
setAnims(true);
if (mode === "winfo") {
detachedMode = mode;
} else {
detachedMode = "any";
queuedMode = mode;
detachedMode = "any";
}
setAnims(false);
focus = true;
}
clip: true
function setAnims(detach: bool): void {
const type = `expressive${detach ? "Slow" : "Default"}Spatial`;
animLength = Appearance.anim.durations[type];
animCurve = Appearance.anim.curves[type];
}
focus: hasCurrent
implicitHeight: nonAnimHeight
implicitWidth: nonAnimWidth
visible: width > 0 && height > 0
Behavior on implicitHeight {
enabled: root.offsetScale < 1
Anim {
duration: root.animLength
easing.bezierCurve: root.animCurve
}
}
Behavior on implicitWidth {
enabled: root.implicitHeight > 0
Anim {
duration: root.animLength
easing.bezierCurve: root.animCurve
}
}
// Comp {
// shouldBeActive: root.detachedMode === "winfo"
// asynchronous: true
// anchors.centerIn: parent
//
// sourceComponent: WindowInfo {
// screen: root.screen
// client: Hypr.activeToplevel
// }
// }
// Comp {
// shouldBeActive: root.detachedMode === "any"
// asynchronous: true
// anchors.centerIn: parent
//
// sourceComponent: ControlCenter {
// screen: root.screen
// active: root.queuedMode
//
// function close(): void {
// root.close();
// }
// }
// }
Behavior on x {
enabled: root.implicitHeight > 0
Anim {
duration: root.animLength
easing.bezierCurve: root.animCurve
Keys.onEscapePressed: {
// Forward escape to password popout if active, otherwise close
if (currentName === "wirelesspassword" && content.item) {
const passwordPopout = (content.item as Content)?.children.find(c => c.name === "wirelesspassword");
if (passwordPopout && passwordPopout.item) {
passwordPopout.item.closeDialog();
return;
}
}
close();
}
Behavior on y {
Anim {
duration: root.animLength
easing.bezierCurve: root.animCurve
Keys.onPressed: event => {
// Don't intercept keys when password popout is active - let it handle them
if (currentName === "wirelesspassword") {
event.accepted = false;
}
}
Keys.onEscapePressed: close()
PopoutState {
id: popoutState
onDetachRequested: mode => root.detach(mode)
}
HyprlandFocusGrab {
active: root.isDetached
@@ -114,19 +102,44 @@ Item {
property: "WlrLayershell.keyboardFocus"
target: QsWindow.window
value: WlrKeyboardFocus.OnDemand
when: root.isDetached
when: root.isDetached || (root.hasCurrent && root.currentName === "wirelesspassword")
}
Comp {
id: content
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
asynchronous: true
shouldBeActive: root.hasCurrent
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
shouldBeActive: root.hasCurrent && !root.detachedMode
sourceComponent: Content {
wrapper: root
popouts: popoutState
}
}
Comp {
id: winfo
anchors.centerIn: parent
shouldBeActive: root.detachedMode === "winfo"
sourceComponent: WindowInfo {
client: Hypr.activeToplevel
screen: root.screen
}
}
Comp {
id: controlCenter
anchors.centerIn: parent
shouldBeActive: root.detachedMode === "any"
sourceComponent: ControlCenter {
active: root.queuedMode
screen: root.screen
onClose: root.close()
}
}
@@ -136,9 +149,9 @@ Item {
property bool shouldBeActive
active: false
asynchronous: true
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