shell: the overview arrives as one surface, and mission control finally lands
Everything drove off a visible/not-visible flag, so the cards appeared rather than the overview opening. One shared `progress` now: the strip slides 24px, each card takes its own share of it staggered by index and capped at the fifth, and scale runs 0.86 to 1.0 — the reference shell's numbers, driven from the same clock instead of a timer per card. The cap is what stops the tenth card starting half a second after the first, which reads as loading rather than opening. This is also SHELL-ECOSYSTEM's stated model, taken from Phosh: state gates visibility, never the reverse. `missionControlOpen` has been set by the pill's second-stage swipe since July and consumed by NOTHING — TASK-14 records the Auxo-like card surface it was meant to raise as never built. WindowOverview is that surface. The state reaches something now instead of being set and dropped. Mission control is the cards alone: no search, and no keyboard focus, because it is "switch to what is running" and not "find something" — and taking the keyboard would summon the OSK over a surface with no field.
This commit is contained in:
parent
133daa94ef
commit
cc4bb83573
2 changed files with 57 additions and 17 deletions
|
|
@ -40,17 +40,27 @@ Scope {
|
|||
property string searchingText: ""
|
||||
readonly property HyprlandMonitor monitor: Hyprland.monitorFor(panelWindow.screen)
|
||||
property bool monitorIsFocused: (Hyprland.focusedMonitor?.id == monitor?.id)
|
||||
visible: GlobalStates.overviewOpen
|
||||
// Two ways in, one surface. The pill's second-stage swipe has set
|
||||
// `missionControlOpen` since 2026-07, and TASK-14 records that NO
|
||||
// SURFACE CONSUMES IT — the Auxo-like card surface it was meant to
|
||||
// raise was never built. WindowOverview is that surface, so the state
|
||||
// finally reaches something instead of being set and dropped.
|
||||
//
|
||||
// Mission control is the cards alone: no search, because it is a
|
||||
// "switch to what is running" gesture, not a "find something" one.
|
||||
visible: GlobalStates.overviewOpen || GlobalStates.missionControlOpen
|
||||
|
||||
WlrLayershell.namespace: "quickshell:overview"
|
||||
// Overlay, not Top: the second-stage edge swipe must bring the
|
||||
// overview up over a fullscreen app (fullscreen renders above Top).
|
||||
WlrLayershell.layer: WlrLayer.Overlay
|
||||
// Only the search half wants the keyboard. Mission control taking it
|
||||
// would summon the OSK over the cards for a surface with no text field.
|
||||
WlrLayershell.keyboardFocus: GlobalStates.overviewOpen ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None
|
||||
color: "transparent"
|
||||
|
||||
mask: Region {
|
||||
item: GlobalStates.overviewOpen ? columnLayout : null
|
||||
item: (GlobalStates.overviewOpen || GlobalStates.missionControlOpen) ? columnLayout : null
|
||||
}
|
||||
|
||||
anchors {
|
||||
|
|
@ -160,7 +170,8 @@ Scope {
|
|||
Loader {
|
||||
id: overviewLoader
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
active: GlobalStates.overviewOpen && (Config?.options.overview.enable ?? true)
|
||||
active: (GlobalStates.overviewOpen || GlobalStates.missionControlOpen)
|
||||
&& (Config?.options.overview.enable ?? true)
|
||||
// WindowOverview, not OverviewWidget. The latter draws a grid
|
||||
// of workspaces from HyprlandData; viewtop has neither, so it
|
||||
// rendered an empty frame and read as the overview being
|
||||
|
|
|
|||
|
|
@ -44,6 +44,26 @@ Item {
|
|||
signal activated(var toplevel)
|
||||
signal closed(var toplevel)
|
||||
|
||||
// How far open, 0..1. The reference shell drives its whole overview from
|
||||
// one `progress` — cards read it, offset their own entry from it, and the
|
||||
// strip slides on it — rather than each piece animating independently on a
|
||||
// visible/not-visible flag. That is what makes it read as one surface
|
||||
// arriving instead of several things appearing at once.
|
||||
//
|
||||
// It is also Phosh's model, which SHELL-ECOSYSTEM names as the one we
|
||||
// study: state gates visibility, and the canonical line is
|
||||
// `use_top_layer = !locked`. Visibility follows state; state is never set
|
||||
// from a visibility handler.
|
||||
property real progress: (GlobalStates.overviewOpen || GlobalStates.missionControlOpen) ? 1 : 0
|
||||
Behavior on progress {
|
||||
NumberAnimation {
|
||||
duration: 260
|
||||
// Standard-curve: quick to commit, slow to settle. A symmetric
|
||||
// ease makes an overview feel like it is deciding.
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
|
||||
implicitWidth: parent ? parent.width : 540
|
||||
implicitHeight: parent ? parent.height : 800
|
||||
|
||||
|
|
@ -61,6 +81,10 @@ Item {
|
|||
id: list
|
||||
anchors.fill: parent
|
||||
anchors.margins: 12
|
||||
// The whole strip slides up as it opens, which is what ties the cards
|
||||
// together into one surface. 24px is small on purpose — a long throw
|
||||
// reads as a modal, and this is a place you pass through.
|
||||
transform: Translate { y: (1 - root.progress) * 24 }
|
||||
visible: root.count > 0
|
||||
model: root.windows
|
||||
// Horizontal, per TASK-14 ("horizontally swiped live app cards") and
|
||||
|
|
@ -104,20 +128,25 @@ Item {
|
|||
property real dismissY: 0
|
||||
property bool dismissed: false
|
||||
readonly property real dismissDistance: height * 0.32
|
||||
readonly property real introDelay: Math.min(index, 4) * 45
|
||||
|
||||
opacity: dismissed ? 0 : 1 - Math.min(1, Math.abs(dismissY) / (height * 0.8))
|
||||
// Each card's own share of the opening, staggered by index and
|
||||
// capped at the fifth — `interval(progress, i*0.045, 1)` in the
|
||||
// reference. The cap is the part that matters: uncapped, the tenth
|
||||
// card starts half a second after the first and the overview reads
|
||||
// as loading rather than opening.
|
||||
readonly property real intro: {
|
||||
const begin = Math.min(index, 4) * 0.045;
|
||||
if (root.progress <= begin) return 0;
|
||||
return Math.min(1, (root.progress - begin) / (1 - begin));
|
||||
}
|
||||
|
||||
opacity: dismissed
|
||||
? 0
|
||||
: root.progress * (1 - Math.min(1, Math.abs(dismissY) / (height * 0.8)))
|
||||
transform: Translate { y: card.dismissY }
|
||||
scale: 0.86
|
||||
Component.onCompleted: introTimer.start()
|
||||
Timer {
|
||||
id: introTimer
|
||||
interval: card.introDelay
|
||||
onTriggered: card.scale = 1.0
|
||||
}
|
||||
Behavior on scale {
|
||||
NumberAnimation { duration: 220; easing.type: Easing.OutCubic }
|
||||
}
|
||||
// 0.86 -> 1.0, the reference's numbers, driven by the shared
|
||||
// progress rather than a timer per card.
|
||||
scale: 0.86 + 0.14 * card.intro
|
||||
Behavior on opacity {
|
||||
NumberAnimation { duration: 160 }
|
||||
}
|
||||
|
|
@ -167,8 +196,8 @@ Item {
|
|||
// a render of that window every frame, and leaving them running
|
||||
// behind a closed overview is the battery cost of drawing
|
||||
// things nobody can see.
|
||||
captureSource: GlobalStates.overviewOpen ? card.modelData : null
|
||||
live: GlobalStates.overviewOpen
|
||||
captureSource: root.progress > 0 ? card.modelData : null
|
||||
live: root.progress > 0
|
||||
|
||||
// The preview is the target: tapping the picture of a window is
|
||||
// how you go to it.
|
||||
|
|
|
|||
Loading…
Reference in a new issue