Give Input keycaps, a shortcut search, and the missing pointer basics
Claude-Session: https://claude.ai/code/session_01Ms2FbjQy31TVf3CEvQhGM8
This commit is contained in:
@@ -10,6 +10,11 @@
|
||||
// preference is stored and the compositor accepts an option for a device class
|
||||
// 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.
|
||||
|
||||
import QtQuick
|
||||
import qs.config
|
||||
@@ -23,18 +28,100 @@ SettingsPage {
|
||||
? "Pointer behavior for your mouse and touchpad."
|
||||
: "Pointer behavior. Touchpad settings appear when a touchpad is attached."
|
||||
|
||||
// The name-filtered device lists InputDevices publishes. Defaulted here so
|
||||
// the card stays a short honest list even before the service has read the
|
||||
// compositor for the first time.
|
||||
readonly property var realKeyboards: InputDevices.realKeyboards ?? []
|
||||
readonly property var realMice: InputDevices.realMice ?? []
|
||||
|
||||
// Keyboards, then mice, then the touchpad if there is one. Built as one
|
||||
// list so the card can draw dividers between rows without each Repeater
|
||||
// having to know what follows it.
|
||||
readonly property var devices: {
|
||||
const out = [];
|
||||
for (const keyboard of root.realKeyboards)
|
||||
out.push({
|
||||
glyph: "\u{F030C}", // md-keyboard
|
||||
label: String(keyboard.pretty ?? keyboard.name ?? ""),
|
||||
detail: InputDevices.mainKeyboardLayout === ""
|
||||
? "Keyboard"
|
||||
: "Keyboard · " + InputDevices.mainKeyboardLayout
|
||||
});
|
||||
for (const mouse of root.realMice)
|
||||
out.push({
|
||||
glyph: "\u{F037D}", // md-mouse
|
||||
label: String(mouse.pretty ?? mouse.name ?? ""),
|
||||
detail: "Mouse"
|
||||
});
|
||||
if (InputDevices.hasTouchpad)
|
||||
out.push({
|
||||
glyph: "\u{F0621}", // md-gesture-tap
|
||||
label: "Touchpad",
|
||||
detail: "Its own card above, because libinput keeps it separate"
|
||||
});
|
||||
return out;
|
||||
}
|
||||
|
||||
readonly property var scrollMethodSpec: PreferenceSchema.spec("scrollMethod")
|
||||
|
||||
// Scroll button only means something while the method is the held-button
|
||||
// one, and only if the schema still offers that method at all -- the value
|
||||
// comes from what Hyprland's own description of input:scroll_method
|
||||
// accepts, so this asks the schema rather than assuming.
|
||||
readonly property bool heldButtonScrolling: {
|
||||
const options = root.scrollMethodSpec && root.scrollMethodSpec.options
|
||||
? root.scrollMethodSpec.options
|
||||
: [];
|
||||
if (!options.some(option => option.value === "on_button_down"))
|
||||
return false;
|
||||
return DesktopPreferences.get("scrollMethod") === "on_button_down";
|
||||
}
|
||||
|
||||
SettingsCard {
|
||||
title: "Mouse"
|
||||
subtitle: "Applied to every pointing device that is not a touchpad."
|
||||
|
||||
SliderRow { setting: "pointerSensitivity" }
|
||||
ChoiceRow { setting: "accelProfile" }
|
||||
ToggleRow { setting: "naturalScroll" }
|
||||
OptionPickerRow {
|
||||
id: accelRow
|
||||
|
||||
readonly property var spec: PreferenceSchema.spec("accelProfile")
|
||||
|
||||
label: accelRow.spec ? accelRow.spec.label : "Acceleration"
|
||||
detail: accelRow.spec ? accelRow.spec.detail : ""
|
||||
options: accelRow.spec && accelRow.spec.options ? accelRow.spec.options : []
|
||||
current: DesktopPreferences.get("accelProfile")
|
||||
onPicked: value => SystemSettings.commitPreference("accelProfile", value)
|
||||
}
|
||||
SliderRow { setting: "scrollFactor" }
|
||||
ToggleRow { setting: "naturalScroll" }
|
||||
ToggleRow { setting: "leftHanded" }
|
||||
ToggleRow {
|
||||
setting: "middleClickPaste"
|
||||
detail: "Paste the primary selection in GTK and native Wayland applications; individual apps may choose not to support it"
|
||||
}
|
||||
|
||||
OptionPickerRow {
|
||||
id: scrollMethodRow
|
||||
|
||||
label: root.scrollMethodSpec ? root.scrollMethodSpec.label : "Scroll method"
|
||||
detail: root.scrollMethodSpec ? root.scrollMethodSpec.detail : ""
|
||||
options: root.scrollMethodSpec && root.scrollMethodSpec.options
|
||||
? root.scrollMethodSpec.options
|
||||
: []
|
||||
current: DesktopPreferences.get("scrollMethod")
|
||||
divider: scrollButtonRow.visible
|
||||
onPicked: value => SystemSettings.commitPreference("scrollMethod", value)
|
||||
}
|
||||
|
||||
// Hidden on every other method: the number stays valid and stored, but
|
||||
// a control that governs nothing is exactly what this page exists to
|
||||
// stop shipping.
|
||||
SliderRow {
|
||||
id: scrollButtonRow
|
||||
|
||||
setting: "scrollButton"
|
||||
visible: root.heldButtonScrolling
|
||||
divider: false
|
||||
}
|
||||
}
|
||||
@@ -45,27 +132,87 @@ SettingsPage {
|
||||
subtitle: "Separate from the mouse on purpose: libinput keeps them apart, and a touchpad and a mouse usually want to scroll in opposite directions."
|
||||
|
||||
ToggleRow { setting: "touchpadTapToClick" }
|
||||
ToggleRow { setting: "touchpadNaturalScroll" }
|
||||
ToggleRow { setting: "touchpadDisableWhileTyping" }
|
||||
SliderRow { setting: "touchpadScrollFactor" }
|
||||
ToggleRow { setting: "touchpadClickfinger" }
|
||||
ToggleRow { setting: "touchpadTapAndDrag" }
|
||||
ChoiceRow { setting: "touchpadDragLock" }
|
||||
ToggleRow { setting: "touchpadMiddleButtonEmulation"; divider: false }
|
||||
}
|
||||
|
||||
SettingsCard {
|
||||
visible: InputDevices.hasTouchpad
|
||||
title: "Gestures"
|
||||
subtitle: "Three fingers sideways moves between workspaces, up opens the overview, and down closes it — the same gestures GNOME used. Which gestures exist is fixed by the compositor at startup; what they feel like is here."
|
||||
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 }
|
||||
}
|
||||
|
||||
SettingsCard {
|
||||
title: "Pointer"
|
||||
title: "Pointer behavior"
|
||||
|
||||
ChoiceRow { setting: "followMouse" }
|
||||
OptionPickerRow {
|
||||
id: followMouseRow
|
||||
|
||||
readonly property var spec: PreferenceSchema.spec("followMouse")
|
||||
|
||||
label: followMouseRow.spec ? followMouseRow.spec.label : "Pointer focus"
|
||||
detail: followMouseRow.spec ? followMouseRow.spec.detail : ""
|
||||
options: followMouseRow.spec && followMouseRow.spec.options ? followMouseRow.spec.options : []
|
||||
current: DesktopPreferences.get("followMouse")
|
||||
onPicked: value => SystemSettings.commitPreference("followMouse", value)
|
||||
}
|
||||
|
||||
OptionPickerRow {
|
||||
id: focusOnCloseRow
|
||||
|
||||
readonly property var spec: PreferenceSchema.spec("focusOnClose")
|
||||
|
||||
label: focusOnCloseRow.spec ? focusOnCloseRow.spec.label : "When a window closes"
|
||||
detail: focusOnCloseRow.spec ? focusOnCloseRow.spec.detail : ""
|
||||
options: focusOnCloseRow.spec && focusOnCloseRow.spec.options ? focusOnCloseRow.spec.options : []
|
||||
current: DesktopPreferences.get("focusOnClose")
|
||||
onPicked: value => SystemSettings.commitPreference("focusOnClose", value)
|
||||
}
|
||||
|
||||
ToggleRow { setting: "cursorHideWhileTyping" }
|
||||
SliderRow { setting: "cursorInactiveTimeout"; zeroLabel: "Never" }
|
||||
ToggleRow { setting: "cursorWarpOnWorkspaceChange" }
|
||||
SliderRow { setting: "cursorSize"; divider: false }
|
||||
}
|
||||
|
||||
SettingsCard {
|
||||
title: "Try it"
|
||||
subtitle: "Every setting above applies live — draw and scroll to feel them. Nothing here is saved."
|
||||
|
||||
InputTestArea {}
|
||||
}
|
||||
|
||||
SettingsCard {
|
||||
title: "Connected devices"
|
||||
subtitle: "What the compositor sees, minus the phantoms: transceiver siblings, virtual keyboards and consumer-control endpoints that are not devices anybody types on."
|
||||
|
||||
Repeater {
|
||||
model: root.devices
|
||||
|
||||
TextRow {
|
||||
id: deviceRow
|
||||
|
||||
required property var modelData
|
||||
required property int index
|
||||
|
||||
width: parent ? parent.width : 620
|
||||
icon: String(deviceRow.modelData.glyph)
|
||||
label: String(deviceRow.modelData.label)
|
||||
detail: String(deviceRow.modelData.detail)
|
||||
divider: deviceRow.index < root.devices.length - 1
|
||||
}
|
||||
}
|
||||
|
||||
TextRow {
|
||||
visible: root.devices.length === 0
|
||||
label: "No input devices reported"
|
||||
detail: "The compositor answered with nothing this page could name"
|
||||
divider: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user