Shortcuts you invent, rules you write, gestures you own - all still just data
Claude-Session: https://claude.ai/code/session_01Ms2FbjQy31TVf3CEvQhGM8
This commit is contained in:
@@ -11,10 +11,11 @@
|
||||
// with no members, so the user would be toggling settings that can never affect
|
||||
// anything with nothing to say so.
|
||||
//
|
||||
// The gestures that were a card of their own now sit at the bottom of the
|
||||
// touchpad card. They are touchpad settings -- a three-finger swipe has nowhere
|
||||
// else to happen -- and a card holding two sliders was a heading standing in
|
||||
// for a section.
|
||||
// Gestures are a card again. They were folded into the touchpad card when the
|
||||
// only thing to say about them was how far a swipe travels and which way round
|
||||
// it goes -- a heading standing in for a section. Four assignable four-finger
|
||||
// directions is a subject, so the heading has content now, and the two feel
|
||||
// knobs come back up with it: they are about gestures, not about the touchpad.
|
||||
|
||||
import QtQuick
|
||||
import qs.config
|
||||
@@ -126,10 +127,132 @@ SettingsPage {
|
||||
}
|
||||
}
|
||||
|
||||
// ── Gestures ────────────────────────────────────────────────────────────
|
||||
// Three fingers are the desktop's and stay put: they are registered at
|
||||
// config time, they reproduce GNOME's muscle memory, and a preference that
|
||||
// needed a reload to turn them off would be worse than the nothing they
|
||||
// cost. Four fingers are the user's, from the same named-action vocabulary
|
||||
// custom shortcuts use -- an enum kind and a validated target, never a
|
||||
// command. Assigning one is read at config time too, so it reloads.
|
||||
|
||||
readonly property var gestureKeys: [
|
||||
{ key: "gestureFourUp", label: "Swipe up" },
|
||||
{ key: "gestureFourDown", label: "Swipe down" },
|
||||
{ key: "gestureFourLeft", label: "Swipe left" },
|
||||
{ key: "gestureFourRight", label: "Swipe right" }
|
||||
]
|
||||
|
||||
// A named action is two fields; OptionPickerRow picks one value. They are
|
||||
// joined on the first colon, which `window` targets already use for
|
||||
// "workspace:4", so the split has to take the first one only.
|
||||
function gestureValue(key: string): string {
|
||||
const stored = DesktopPreferences.get(key);
|
||||
if (Keybinds.describeAction(stored) === "")
|
||||
return "";
|
||||
return String(stored.kind) + ":" + String(stored.target);
|
||||
}
|
||||
|
||||
function assignGesture(key: string, value: string): void {
|
||||
const at = String(value).indexOf(":");
|
||||
if (at < 0) {
|
||||
DesktopPreferences.set(key, ({}));
|
||||
Keybinds.applyReload();
|
||||
return;
|
||||
}
|
||||
const kind = String(value).slice(0, at);
|
||||
const target = String(value).slice(at + 1);
|
||||
const label = kind === "shell"
|
||||
? Keybinds.shellActionLabel(target)
|
||||
: Keybinds.windowActionLabel(target);
|
||||
if (Keybinds.describeAction({ kind: kind, target: target }) === "" || label === "")
|
||||
return;
|
||||
DesktopPreferences.set(key, { kind: kind, target: target, label: label });
|
||||
Keybinds.applyReload();
|
||||
}
|
||||
|
||||
// Nothing, then the shell's own actions, then the compositor's window
|
||||
// verbs -- the same lists the shortcut editor offers, read from Keybinds so
|
||||
// this page never becomes a second opinion on what resolves.
|
||||
//
|
||||
// Applications are deliberately absent: assigning one means searching
|
||||
// several hundred desktop entries, which a picker of this shape cannot do.
|
||||
// A gesture that already holds an application (written by the shortcut
|
||||
// vocabulary elsewhere) still shows, so it can be read and cleared.
|
||||
function gestureOptions(key: string): var {
|
||||
const out = [{ value: "", label: "Nothing", detail: "The swipe passes through to whatever is under it" }];
|
||||
const stored = DesktopPreferences.get(key);
|
||||
if (stored && stored.kind === "app" && Keybinds.describeAction(stored) !== "")
|
||||
out.push({
|
||||
value: "app:" + String(stored.target),
|
||||
label: String(stored.label),
|
||||
detail: "Application · launch-or-focus"
|
||||
});
|
||||
for (const action of Keybinds.shellActions)
|
||||
out.push({ value: "shell:" + action.target, label: action.label, detail: "Shell action" });
|
||||
for (const action of Keybinds.windowActions)
|
||||
out.push({ value: "window:" + action.target, label: action.label, detail: "Window & workspace" });
|
||||
return out;
|
||||
}
|
||||
|
||||
SettingsCard {
|
||||
visible: InputDevices.hasTouchpad
|
||||
title: "Gestures"
|
||||
subtitle: "Three fingers are Panama's — they drive workspaces and Mission Control everywhere and stay put. Four fingers are yours: give each direction a job from the same actions your shortcuts use, or leave it unassigned."
|
||||
|
||||
SectionLabel { text: "Three fingers"; count: "· shipped" }
|
||||
|
||||
TextRow {
|
||||
label: "Swipe left or right"
|
||||
detail: "Moves between workspaces, following your fingers"
|
||||
value: "Switch workspace"
|
||||
}
|
||||
TextRow {
|
||||
label: "Swipe up"
|
||||
value: "Open Mission Control"
|
||||
}
|
||||
TextRow {
|
||||
label: "Swipe down"
|
||||
detail: "Open and close rather than one toggle, so a swipe never undoes itself mid-gesture"
|
||||
value: "Close Mission Control"
|
||||
divider: false
|
||||
}
|
||||
|
||||
SectionLabel { text: "Four fingers"; count: "· yours" }
|
||||
|
||||
Repeater {
|
||||
model: root.gestureKeys
|
||||
|
||||
OptionPickerRow {
|
||||
id: gestureRow
|
||||
|
||||
required property var modelData
|
||||
required property int index
|
||||
|
||||
label: String(gestureRow.modelData.label)
|
||||
detail: gestureRow.index === root.gestureKeys.length - 1
|
||||
? "Assigning one reloads the compositor — a beat of black, then it works"
|
||||
: ""
|
||||
options: root.gestureOptions(String(gestureRow.modelData.key))
|
||||
current: root.gestureValue(String(gestureRow.modelData.key))
|
||||
enabled: !Keybinds.reloading
|
||||
divider: gestureRow.index < root.gestureKeys.length - 1
|
||||
onPicked: value => root.assignGesture(String(gestureRow.modelData.key), String(value))
|
||||
}
|
||||
}
|
||||
|
||||
SectionLabel { text: "Feel" }
|
||||
|
||||
// How far a swipe has to travel and which way round it goes: the two
|
||||
// knobs that are about gestures rather than about the touchpad, which
|
||||
// is why they moved up out of the Touchpad card.
|
||||
SliderRow { setting: "swipeDistance" }
|
||||
ToggleRow { setting: "swipeInvert"; divider: false }
|
||||
}
|
||||
|
||||
SettingsCard {
|
||||
visible: InputDevices.hasTouchpad
|
||||
title: "Touchpad"
|
||||
subtitle: "Separate from the mouse on purpose: libinput keeps them apart, and a touchpad and a mouse usually want to scroll in opposite directions."
|
||||
subtitle: "Separate from the mouse on purpose: libinput keeps them apart, and a touchpad and a mouse usually want to scroll in opposite directions. The two swipe knobs moved up into Gestures, which is what they are about."
|
||||
|
||||
ToggleRow { setting: "touchpadTapToClick" }
|
||||
ToggleRow { setting: "touchpadClickfinger" }
|
||||
@@ -138,13 +261,7 @@ SettingsPage {
|
||||
ToggleRow { setting: "touchpadNaturalScroll" }
|
||||
SliderRow { setting: "touchpadScrollFactor" }
|
||||
ToggleRow { setting: "touchpadDisableWhileTyping" }
|
||||
ToggleRow { setting: "touchpadMiddleButtonEmulation" }
|
||||
|
||||
// Which gestures exist is fixed by the compositor at startup -- three
|
||||
// fingers sideways moves between workspaces, up opens the overview.
|
||||
// What they feel like is here.
|
||||
SliderRow { setting: "swipeDistance" }
|
||||
ToggleRow { setting: "swipeInvert"; divider: false }
|
||||
ToggleRow { setting: "touchpadMiddleButtonEmulation"; divider: false }
|
||||
}
|
||||
|
||||
SettingsCard {
|
||||
|
||||
Reference in New Issue
Block a user