move is a mode: grab, drag, done
This commit is contained in:
parent
b0688bc30b
commit
c3f998f17a
3 changed files with 112 additions and 15 deletions
|
|
@ -241,18 +241,41 @@ PanelWindow {
|
|||
}
|
||||
|
||||
// Commit an upward gesture. `stage` is the stage the drag settled on
|
||||
// (0/1/2). Higher stages imply the lower one, so Mission Control also
|
||||
// reveals the dock beneath it.
|
||||
// (0/1/2).
|
||||
//
|
||||
// Casey, 2026-08-05: "a proper toggle for the multitasking sections,
|
||||
// that was the short swipe up deal, and long swipe up was to zone one
|
||||
// which was to be clear and have widget space."
|
||||
//
|
||||
// So the two swipes are two different destinations rather than degrees
|
||||
// of the same one: **short = multitasking, long = square one**. This
|
||||
// used to be short = dock, long = multitasking, which left Home
|
||||
// reachable only by tap — and the tap was `hyprctl`, so under viewtop
|
||||
// there was no way up and out at all.
|
||||
//
|
||||
// Short *toggles*. Reaching multitasking with a gesture that cannot
|
||||
// also leave it means the way out is a different gesture than the way
|
||||
// in, which is the thing that makes a phone feel stuck.
|
||||
function commitUp(stage: int): void {
|
||||
singleTapTimer.stop()
|
||||
lastTapAt = -1
|
||||
if (stage >= 1) {
|
||||
GlobalStates.dockSuppressed = false
|
||||
GlobalStates.dockRevealed = true
|
||||
}
|
||||
if (stage >= 2) {
|
||||
GlobalStates.missionControlOpen = true
|
||||
if (!Persistent.states.navigation.missionControlDiscovered)
|
||||
// Square one. `goHome` clears multitasking, the overview, the
|
||||
// dock and the OSK before moving, so the zone is not arrived at
|
||||
// with the last screen's furniture still up.
|
||||
goHome()
|
||||
shortSwipes = 0
|
||||
return
|
||||
}
|
||||
if (stage >= 1) {
|
||||
GlobalStates.missionControlOpen = !GlobalStates.missionControlOpen
|
||||
// The dock rides with it, as it did when multitasking lived on
|
||||
// the long swipe: the two were always shown together and
|
||||
// splitting them now would be a second change nobody asked for.
|
||||
GlobalStates.dockSuppressed = false
|
||||
GlobalStates.dockRevealed = GlobalStates.missionControlOpen
|
||||
if (GlobalStates.missionControlOpen
|
||||
&& !Persistent.states.navigation.missionControlDiscovered)
|
||||
Persistent.states.navigation.missionControlDiscovered = true
|
||||
shortSwipes = 0
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,6 +50,10 @@ Scope {
|
|||
property bool sheetOpen: false
|
||||
// Split has pinned a window and is waiting for the other half to be filled.
|
||||
property bool choosing: false
|
||||
// A window is grabbed and following the finger. The sheet is closed while
|
||||
// this is true — it would be covering the thing being moved — so the only
|
||||
// surface up is the Done bar.
|
||||
property bool moving: false
|
||||
|
||||
// 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.
|
||||
|
|
@ -67,6 +71,14 @@ Scope {
|
|||
function dismiss() {
|
||||
scope.sheetOpen = false;
|
||||
scope.choosing = false;
|
||||
// A grab outlives the sheet on purpose — Move closes the sheet to get
|
||||
// out of the way — so dismissing must not orphan one. Anything else
|
||||
// would leave the compositor holding a window for a finger that has
|
||||
// nothing left on screen telling it how to let go.
|
||||
if (scope.moving) {
|
||||
ViewtopControl.drop();
|
||||
scope.moving = false;
|
||||
}
|
||||
scope.target = 0;
|
||||
}
|
||||
|
||||
|
|
@ -112,6 +124,55 @@ Scope {
|
|||
}
|
||||
}
|
||||
|
||||
// The way out of move mode. A separate, small surface rather than part of
|
||||
// the sheet: while moving, the whole screen has to stay reachable by the
|
||||
// finger that is dragging the window, so a full-screen scrim would eat the
|
||||
// gesture it exists to support.
|
||||
Variants {
|
||||
model: Quickshell.screens
|
||||
|
||||
PanelWindow {
|
||||
id: moveBar
|
||||
required property var modelData
|
||||
screen: moveBar.modelData
|
||||
|
||||
anchors { bottom: true; left: true; right: true }
|
||||
implicitHeight: 86
|
||||
color: "transparent"
|
||||
visible: scope.moving
|
||||
WlrLayershell.namespace: "souveraine:windowmove"
|
||||
WlrLayershell.layer: WlrLayer.Overlay
|
||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
|
||||
exclusionMode: ExclusionMode.Ignore
|
||||
|
||||
Rectangle {
|
||||
anchors.centerIn: parent
|
||||
implicitWidth: 220
|
||||
implicitHeight: 58
|
||||
radius: 29
|
||||
color: Appearance.colors.colLayer1
|
||||
border.width: 1
|
||||
border.color: Appearance.colors.colLayer1Active
|
||||
|
||||
StyledText {
|
||||
anchors.centerIn: parent
|
||||
text: "Done moving"
|
||||
color: Appearance.colors.colOnLayer1
|
||||
font.pixelSize: Appearance.font.pixelSize.normal
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
ViewtopControl.drop();
|
||||
scope.moving = false;
|
||||
scope.target = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Variants {
|
||||
model: Quickshell.screens
|
||||
|
||||
|
|
@ -213,15 +274,16 @@ Scope {
|
|||
spacing: 28
|
||||
|
||||
SheetButton {
|
||||
icon: "open_in_full"
|
||||
label: "Resize"
|
||||
icon: "drag_pan"
|
||||
label: "Move"
|
||||
onTapped: {
|
||||
// Half height, full width, parked at the top — the
|
||||
// `place` path, and the one geometry that is always
|
||||
// legal on a phone. A drag-to-size handle is the real
|
||||
// answer and it needs a gesture of its own; this is the
|
||||
// verb reaching the window, which is what was missing.
|
||||
ViewtopControl.place(scope.target, 0, 0, win.width, win.height / 2);
|
||||
// Hands the window to the finger and gets out of the
|
||||
// way: the sheet closes, one-finger drags move the
|
||||
// window, and `drop` ends it. A one-shot `place` here
|
||||
// would be the compositor guessing where you wanted it.
|
||||
ViewtopControl.grab(scope.target);
|
||||
scope.moving = true;
|
||||
scope.sheetOpen = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -121,6 +121,18 @@ Singleton {
|
|||
});
|
||||
}
|
||||
|
||||
// Hand a window to the finger. While grabbed, one-finger drags move it;
|
||||
// `drop` ends the mode. This is Move as a *mode* rather than a computed
|
||||
// geometry — the three-finger carry that used to do it lost a race with
|
||||
// the three-finger tap every time, so it is entered on purpose now.
|
||||
function grab(id) {
|
||||
root.scene("grab", { id: id });
|
||||
}
|
||||
|
||||
function drop() {
|
||||
root.scene("drop", {});
|
||||
}
|
||||
|
||||
function raise(id) {
|
||||
root.scene("raise", { id: id });
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue