the app scales into multitasking; pill has two destinations
This commit is contained in:
parent
277a0d2b32
commit
0e7dec53d1
3 changed files with 97 additions and 0 deletions
|
|
@ -273,6 +273,12 @@ PanelWindow {
|
||||||
// Square one. `goHome` clears multitasking, the overview, the
|
// Square one. `goHome` clears multitasking, the overview, the
|
||||||
// dock and the OSK before moving, so the zone is not arrived at
|
// dock and the OSK before moving, so the zone is not arrived at
|
||||||
// with the last screen's furniture still up.
|
// with the last screen's furniture still up.
|
||||||
|
//
|
||||||
|
// The pose is released here rather than on arrival: home is a
|
||||||
|
// different zone, and a window left at 0.6 on the zone behind
|
||||||
|
// you is one you would find shrunken next time you swiped back
|
||||||
|
// to it, with no gesture in flight to explain why.
|
||||||
|
ViewtopControl.clearPose()
|
||||||
goHome()
|
goHome()
|
||||||
shortSwipes = 0
|
shortSwipes = 0
|
||||||
return
|
return
|
||||||
|
|
@ -282,6 +288,11 @@ PanelWindow {
|
||||||
// belongs to zone one and is shown by being *there*, not by a
|
// belongs to zone one and is shown by being *there*, not by a
|
||||||
// gesture that reveals it over whatever else is on screen.
|
// gesture that reveals it over whatever else is on screen.
|
||||||
GlobalStates.missionControlOpen = !GlobalStates.missionControlOpen
|
GlobalStates.missionControlOpen = !GlobalStates.missionControlOpen
|
||||||
|
// Multitasking draws its own cards over the scene, so the live
|
||||||
|
// windows go back to full size underneath it. Leaving them
|
||||||
|
// posed would mean two shrunken pictures of the same app —
|
||||||
|
// the card and the window behind it.
|
||||||
|
ViewtopControl.clearPose()
|
||||||
if (GlobalStates.missionControlOpen
|
if (GlobalStates.missionControlOpen
|
||||||
&& !Persistent.states.navigation.missionControlDiscovered)
|
&& !Persistent.states.navigation.missionControlDiscovered)
|
||||||
Persistent.states.navigation.missionControlDiscovered = true
|
Persistent.states.navigation.missionControlDiscovered = true
|
||||||
|
|
@ -302,6 +313,24 @@ PanelWindow {
|
||||||
// Upward travel is positive; downward drags leave travel at 0 so
|
// Upward travel is positive; downward drags leave travel at 0 so
|
||||||
// the handle doesn't chase a dismiss gesture.
|
// the handle doesn't chase a dismiss gesture.
|
||||||
rail.dragTravel = Math.max(0, startY - mouse.y)
|
rail.dragTravel = Math.max(0, startY - mouse.y)
|
||||||
|
|
||||||
|
// The app shrinks under the thumb. Casey, 2026-08-05: *"swipe up
|
||||||
|
// just a bit makes the app sorta scale and you fall into a multi
|
||||||
|
// tasking area"* — and Phosh's home is the same shape, a drag
|
||||||
|
// surface travelling between two states rather than a button that
|
||||||
|
// teleports.
|
||||||
|
//
|
||||||
|
// Scaled against `missionAt`, the multitasking detent, so the app
|
||||||
|
// has visibly become a card by the moment it would commit. It
|
||||||
|
// keeps shrinking past that toward home, which is what makes the
|
||||||
|
// long pull feel like a further degree of the same motion instead
|
||||||
|
// of a second, unrelated gesture.
|
||||||
|
//
|
||||||
|
// Nothing is scaled below 0.6: past that the window is a thumbnail
|
||||||
|
// and its own inverse-mapped touch targets stop being findable if
|
||||||
|
// the drag is abandoned there.
|
||||||
|
const progress = Math.min(1.0, rail.dragTravel / Math.max(1, rail.missionAt))
|
||||||
|
ViewtopControl.poseActiveZone(1.0 - 0.4 * progress)
|
||||||
}
|
}
|
||||||
|
|
||||||
onReleased: mouse => {
|
onReleased: mouse => {
|
||||||
|
|
@ -347,6 +376,12 @@ PanelWindow {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A climb that did not reach the first detent. The app was shrinking
|
||||||
|
// under the thumb and has to spring back, or an abandoned gesture
|
||||||
|
// leaves a permanently smaller window and no way to say so.
|
||||||
|
if (wasDragging)
|
||||||
|
ViewtopControl.clearPose()
|
||||||
|
|
||||||
// Downward: peel the nearest surface (keyboard, then dock).
|
// Downward: peel the nearest surface (keyboard, then dock).
|
||||||
if (deltaY >= rail.revealAt) {
|
if (deltaY >= rail.revealAt) {
|
||||||
singleTapTimer.stop()
|
singleTapTimer.stop()
|
||||||
|
|
@ -412,6 +447,10 @@ PanelWindow {
|
||||||
rail.dragging = false
|
rail.dragging = false
|
||||||
rail.dragTravel = 0
|
rail.dragTravel = 0
|
||||||
lastTapAt = -1
|
lastTapAt = -1
|
||||||
|
// A cancel is the compositor taking the sequence away mid-drag —
|
||||||
|
// the FTS controller does exactly this after a wake. The scale must
|
||||||
|
// still come home; nothing else will do it.
|
||||||
|
ViewtopControl.clearPose()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,11 @@ Scope {
|
||||||
// this is true — it would be covering the thing being moved — so the only
|
// this is true — it would be covering the thing being moved — so the only
|
||||||
// surface up is the Done bar.
|
// surface up is the Done bar.
|
||||||
property bool moving: false
|
property bool moving: false
|
||||||
|
// Which size Resize last applied. Kept on the scope rather than the button
|
||||||
|
// so it survives the sheet closing and reopening on the same window —
|
||||||
|
// tapping Resize twice in two visits should continue the cycle, not restart
|
||||||
|
// it and appear to do nothing.
|
||||||
|
property int sizeStep: 0
|
||||||
|
|
||||||
// How long a hold on Close means kill. Longer than a tap could ever be,
|
// How long a hold on Close means kill. Longer than a tap could ever be,
|
||||||
// short enough that a stuck app does not feel like a negotiation.
|
// short enough that a stuck app does not feel like a negotiation.
|
||||||
|
|
@ -287,6 +292,27 @@ Scope {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SheetButton {
|
||||||
|
icon: "aspect_ratio"
|
||||||
|
label: "Resize"
|
||||||
|
// Cycles the window through sizes rather than opening a
|
||||||
|
// drag-handle mode: Move already owns "follow my finger",
|
||||||
|
// and a second finger-driven mode would need a way to tell
|
||||||
|
// them apart mid-gesture. Tapping through half → two-thirds
|
||||||
|
// → full → back is reachable one-handed and needs no
|
||||||
|
// second gesture vocabulary.
|
||||||
|
//
|
||||||
|
// A real corner handle is still owed; this is the verb
|
||||||
|
// reaching the window, which is what was missing when the
|
||||||
|
// button said Move and there was no Resize at all.
|
||||||
|
onTapped: {
|
||||||
|
scope.sizeStep = (scope.sizeStep + 1) % 3;
|
||||||
|
const frac = [0.5, 0.67, 1.0][scope.sizeStep];
|
||||||
|
ViewtopControl.place(scope.target, 0, 0,
|
||||||
|
win.width, Math.round(win.height * frac));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SheetButton {
|
SheetButton {
|
||||||
icon: "splitscreen"
|
icon: "splitscreen"
|
||||||
label: "Split"
|
label: "Split"
|
||||||
|
|
|
||||||
|
|
@ -156,6 +156,38 @@ Singleton {
|
||||||
root.scene("drop", {});
|
root.scene("drop", {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Scale every window on the zone in front, live, as a drag progresses.
|
||||||
|
//
|
||||||
|
// This is the iOS/Android "the app shrinks into a card while your thumb
|
||||||
|
// climbs" feel, and it is the first thing in the tree to actually drive
|
||||||
|
// `pose`. The verb and its inverse input-mapping have been implemented and
|
||||||
|
// tested in the compositor for weeks with nothing calling them, which is
|
||||||
|
// the whole reason the behaviour has never been seen on the device.
|
||||||
|
//
|
||||||
|
// Throttled on the value, not on a timer: a drag emits a motion event per
|
||||||
|
// frame and each one would otherwise be a socket round-trip. Anything
|
||||||
|
// smaller than a percent of scale is invisible and not worth a write.
|
||||||
|
property real _lastPose: 1.0
|
||||||
|
|
||||||
|
function poseActiveZone(scale) {
|
||||||
|
if (Math.abs(scale - root._lastPose) < 0.01)
|
||||||
|
return;
|
||||||
|
root._lastPose = scale;
|
||||||
|
for (const w of root.windows) {
|
||||||
|
if (w.workspace === root.activeZone)
|
||||||
|
root.pose(w.id, scale, 0.0, 0.5, 0.5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put them back. Called on every path out of a drag — commit, abandon and
|
||||||
|
// cancel — because a window left posed by a gesture that ended is a window
|
||||||
|
// the user cannot restore without knowing a verb exists.
|
||||||
|
function clearPose() {
|
||||||
|
root._lastPose = 1.0;
|
||||||
|
for (const w of root.windows)
|
||||||
|
root.pose(w.id, 1.0, 0.0, 0.5, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
function raise(id) {
|
function raise(id) {
|
||||||
root.scene("raise", { id: id });
|
root.scene("raise", { id: id });
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue