mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-08-01 19:18:28 -04:00
75eac83b47
* feat(settings): add Mouse & Trackpad settings tab * style(settings): clarify touchpad natural scroll description * chore(settings): update settings search index * refactor(settings): unify Mouse & Trackpad terminology to Mouse & Touchpad * feat(setup): deploy default dms/input.kdl on setup * style(niri): restore include section comment in base config template * refactor(niri): remove redundant input block from base config template * refactor(niri): restore keyboard and non-mouse input config to base template * feat(settings): add touchpad disable-on-external-mouse toggle * feat(settings): add left-handed, scroll-factor and dwt options * feat(settings): add middle-emulation, scroll-method, tap-and-drag and drag-lock options * refactor(settings): re-order mouse and touchpad UI options logically
413 lines
18 KiB
QML
413 lines
18 KiB
QML
import QtCore
|
|
import QtQuick
|
|
import qs.Common
|
|
import qs.Services
|
|
import qs.Widgets
|
|
import qs.Modules.Settings.Widgets
|
|
import "../../Common/ConfigIncludeResolve.js" as ConfigIncludeResolve
|
|
|
|
Item {
|
|
id: root
|
|
|
|
LayoutMirroring.enabled: I18n.isRtl
|
|
LayoutMirroring.childrenInherit: true
|
|
|
|
property var inputIncludeStatus: ({
|
|
"exists": false,
|
|
"included": false,
|
|
"configFormat": "",
|
|
"readOnly": false
|
|
})
|
|
property bool checkingInclude: false
|
|
property bool fixingInclude: false
|
|
|
|
function getInputConfigPaths() {
|
|
const configDir = Paths.strip(StandardPaths.writableLocation(StandardPaths.ConfigLocation));
|
|
if (CompositorService.compositor !== "niri")
|
|
return null;
|
|
return {
|
|
"configFile": configDir + "/niri/config.kdl",
|
|
"layoutFile": configDir + "/niri/dms/input.kdl",
|
|
"grepPattern": 'include.*"dms/input.kdl"',
|
|
"includeLine": 'include "dms/input.kdl"'
|
|
};
|
|
}
|
|
|
|
function checkInputIncludeStatus() {
|
|
if (CompositorService.compositor !== "niri") {
|
|
inputIncludeStatus = {
|
|
"exists": false,
|
|
"included": false,
|
|
"configFormat": "",
|
|
"readOnly": false
|
|
};
|
|
return;
|
|
}
|
|
|
|
checkingInclude = true;
|
|
Proc.runCommand("check-input-include", [Proc.dmsBin, "config", "resolve-include", "niri", "input.kdl"], (output, exitCode) => {
|
|
checkingInclude = false;
|
|
if (exitCode !== 0) {
|
|
inputIncludeStatus = {
|
|
"exists": false,
|
|
"included": false,
|
|
"configFormat": "",
|
|
"readOnly": false
|
|
};
|
|
return;
|
|
}
|
|
try {
|
|
inputIncludeStatus = JSON.parse(output.trim());
|
|
} catch (e) {
|
|
inputIncludeStatus = {
|
|
"exists": false,
|
|
"included": false,
|
|
"configFormat": "",
|
|
"readOnly": false
|
|
};
|
|
}
|
|
});
|
|
}
|
|
|
|
function fixInputInclude() {
|
|
const paths = getInputConfigPaths();
|
|
if (!paths)
|
|
return;
|
|
|
|
fixingInclude = true;
|
|
const unixTime = Math.floor(Date.now() / 1000);
|
|
const backupFile = paths.configFile + ".backup" + unixTime;
|
|
const script = ConfigIncludeResolve.buildRepairScript({
|
|
configFile: paths.configFile,
|
|
backupFile: backupFile,
|
|
fragmentFile: paths.layoutFile,
|
|
grepPattern: paths.grepPattern,
|
|
includeLine: paths.includeLine
|
|
});
|
|
Proc.runCommand("fix-input-include", ["sh", "-c", script], (output, exitCode) => {
|
|
fixingInclude = false;
|
|
if (exitCode !== 0)
|
|
return;
|
|
checkInputIncludeStatus();
|
|
SettingsData.updateCompositorInput();
|
|
});
|
|
}
|
|
|
|
Component.onCompleted: {
|
|
if (CompositorService.isNiri) {
|
|
checkInputIncludeStatus();
|
|
}
|
|
}
|
|
|
|
DankFlickable {
|
|
anchors.fill: parent
|
|
clip: true
|
|
contentHeight: settingsColumn.height + Theme.spacingXL
|
|
contentWidth: width
|
|
|
|
Column {
|
|
id: settingsColumn
|
|
|
|
topPadding: 4
|
|
width: Math.min(550, parent.width - Theme.spacingL * 2)
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
spacing: Theme.spacingXL
|
|
|
|
StyledRect {
|
|
id: warningBox
|
|
width: parent.width
|
|
height: warningContent.implicitHeight + Theme.spacingL * 2
|
|
radius: Theme.cornerRadius
|
|
|
|
readonly property bool showSetup: !root.inputIncludeStatus.included
|
|
|
|
color: showSetup ? Theme.withAlpha(Theme.primary, 0.15) : Theme.withAlpha(Theme.primary, 0)
|
|
border.color: showSetup ? Theme.withAlpha(Theme.primary, 0.3) : Theme.withAlpha(Theme.primary, 0)
|
|
border.width: 1
|
|
visible: showSetup && !root.checkingInclude && CompositorService.isNiri
|
|
|
|
Row {
|
|
id: warningContent
|
|
anchors.fill: parent
|
|
anchors.margins: Theme.spacingL
|
|
spacing: Theme.spacingM
|
|
|
|
DankIcon {
|
|
name: "warning"
|
|
size: Theme.iconSize
|
|
color: Theme.primary
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
}
|
|
|
|
Column {
|
|
width: parent.width - Theme.iconSize - (fixButton.visible ? fixButton.width + Theme.spacingM : 0) - Theme.spacingM
|
|
spacing: Theme.spacingXS
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
|
|
StyledText {
|
|
text: I18n.tr("First Time Setup")
|
|
font.pixelSize: Theme.fontSizeMedium
|
|
font.weight: Font.Medium
|
|
color: Theme.primary
|
|
width: parent.width
|
|
horizontalAlignment: Text.AlignLeft
|
|
}
|
|
|
|
StyledText {
|
|
text: I18n.tr("Click 'Setup' to create %1 and add include to your compositor config.").arg("dms/input")
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
color: Theme.surfaceVariantText
|
|
wrapMode: Text.WordWrap
|
|
width: parent.width
|
|
horizontalAlignment: Text.AlignLeft
|
|
}
|
|
}
|
|
|
|
DankButton {
|
|
id: fixButton
|
|
visible: warningBox.showSetup
|
|
text: root.fixingInclude ? I18n.tr("Setting up...") : I18n.tr("Setup")
|
|
backgroundColor: Theme.primary
|
|
textColor: Theme.primaryText
|
|
enabled: !root.fixingInclude
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
onClicked: root.fixInputInclude()
|
|
}
|
|
}
|
|
}
|
|
|
|
SettingsCard {
|
|
width: parent.width
|
|
tags: ["mouse", "input", "sensitivity", "acceleration", "pointer"]
|
|
title: I18n.tr("Mouse Settings")
|
|
settingKey: "mouseSettings"
|
|
iconName: "mouse"
|
|
|
|
SettingsToggleRow {
|
|
tags: ["mouse", "left", "handed", "button", "swap"]
|
|
settingKey: "mouseLeftHanded"
|
|
text: I18n.tr("Left-Handed Mode")
|
|
description: I18n.tr("Swap primary and secondary mouse buttons")
|
|
checked: SettingsData.mouseLeftHanded
|
|
onToggled: checked => SettingsData.set("mouseLeftHanded", checked)
|
|
}
|
|
|
|
SettingsSliderRow {
|
|
tags: ["mouse", "sensitivity", "speed", "accel"]
|
|
settingKey: "mouseAccelSpeed"
|
|
text: I18n.tr("Pointer Speed")
|
|
description: I18n.tr("Adjust pointer sensitivity speed")
|
|
value: Math.round(SettingsData.mouseAccelSpeed * 10)
|
|
minimum: -10
|
|
maximum: 10
|
|
step: 1
|
|
defaultValue: 0
|
|
onSliderValueChanged: newValue => SettingsData.set("mouseAccelSpeed", newValue / 10.0)
|
|
}
|
|
|
|
SettingsButtonGroupRow {
|
|
tags: ["mouse", "acceleration", "profile"]
|
|
settingKey: "mouseAccelProfile"
|
|
text: I18n.tr("Acceleration Profile")
|
|
description: I18n.tr("Flat uses constant speed; Adaptive scales with movement speed")
|
|
model: [I18n.tr("Default"), I18n.tr("Flat"), I18n.tr("Adaptive")]
|
|
currentIndex: {
|
|
if (SettingsData.mouseAccelProfile === "flat") return 1;
|
|
if (SettingsData.mouseAccelProfile === "adaptive") return 2;
|
|
return 0;
|
|
}
|
|
onSelectionChanged: (index, selected) => {
|
|
if (!selected) return;
|
|
const profiles = ["default", "flat", "adaptive"];
|
|
SettingsData.set("mouseAccelProfile", profiles[index]);
|
|
}
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
tags: ["mouse", "natural", "scroll", "direction"]
|
|
settingKey: "mouseNaturalScroll"
|
|
text: I18n.tr("Natural Scrolling")
|
|
description: I18n.tr("Reverse mouse wheel scrolling direction")
|
|
checked: SettingsData.mouseNaturalScroll
|
|
onToggled: checked => SettingsData.set("mouseNaturalScroll", checked)
|
|
}
|
|
|
|
SettingsSliderRow {
|
|
tags: ["mouse", "scroll", "speed", "factor"]
|
|
settingKey: "mouseScrollFactor"
|
|
text: I18n.tr("Scrolling Speed")
|
|
description: I18n.tr("Adjust scrolling sensitivity multiplier")
|
|
value: Math.round(SettingsData.mouseScrollFactor * 10)
|
|
minimum: 1
|
|
maximum: 30
|
|
step: 1
|
|
defaultValue: 10
|
|
onSliderValueChanged: newValue => SettingsData.set("mouseScrollFactor", newValue / 10.0)
|
|
}
|
|
|
|
SettingsButtonGroupRow {
|
|
tags: ["mouse", "scroll", "method"]
|
|
settingKey: "mouseScrollMethod"
|
|
text: I18n.tr("Scroll Method")
|
|
description: I18n.tr("Choose when to generate scrolling events")
|
|
model: [I18n.tr("Default"), I18n.tr("No Scroll"), I18n.tr("On Button Down")]
|
|
currentIndex: {
|
|
if (SettingsData.mouseScrollMethod === "no-scroll") return 1;
|
|
if (SettingsData.mouseScrollMethod === "on-button-down") return 2;
|
|
return 0;
|
|
}
|
|
onSelectionChanged: (index, selected) => {
|
|
if (!selected) return;
|
|
const methods = ["default", "no-scroll", "on-button-down"];
|
|
SettingsData.set("mouseScrollMethod", methods[index]);
|
|
}
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
tags: ["mouse", "middle", "click", "emulation"]
|
|
settingKey: "mouseMiddleEmulation"
|
|
text: I18n.tr("Middle Click Emulation")
|
|
description: I18n.tr("Emulate middle click by pressing left and right buttons")
|
|
checked: SettingsData.mouseMiddleEmulation
|
|
onToggled: checked => SettingsData.set("mouseMiddleEmulation", checked)
|
|
}
|
|
}
|
|
|
|
SettingsCard {
|
|
width: parent.width
|
|
tags: ["touchpad", "input", "sensitivity", "tap", "click", "natural"]
|
|
title: I18n.tr("Touchpad Settings")
|
|
settingKey: "touchpadSettings"
|
|
iconName: "trackpad_input_2"
|
|
|
|
SettingsToggleRow {
|
|
tags: ["touchpad", "tap", "click"]
|
|
settingKey: "touchpadTapToClick"
|
|
text: I18n.tr("Tap to Click")
|
|
description: I18n.tr("Tap the touchpad surface to trigger left click clicks")
|
|
checked: SettingsData.touchpadTapToClick
|
|
onToggled: checked => SettingsData.set("touchpadTapToClick", checked)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
tags: ["touchpad", "tap", "drag"]
|
|
settingKey: "touchpadTapAndDrag"
|
|
text: I18n.tr("Tap and Drag")
|
|
description: I18n.tr("Tap and drag on the touchpad to move items")
|
|
checked: SettingsData.touchpadTapAndDrag
|
|
onToggled: checked => SettingsData.set("touchpadTapAndDrag", checked)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
tags: ["touchpad", "drag", "lock"]
|
|
settingKey: "touchpadDragLock"
|
|
text: I18n.tr("Drag Lock")
|
|
description: I18n.tr("Keep dragging when finger is briefly lifted")
|
|
checked: SettingsData.touchpadDragLock
|
|
onToggled: checked => SettingsData.set("touchpadDragLock", checked)
|
|
}
|
|
|
|
SettingsSliderRow {
|
|
tags: ["touchpad", "sensitivity", "speed", "accel"]
|
|
settingKey: "touchpadAccelSpeed"
|
|
text: I18n.tr("Touchpad Speed")
|
|
description: I18n.tr("Adjust touchpad pointer speed")
|
|
value: Math.round(SettingsData.touchpadAccelSpeed * 10)
|
|
minimum: -10
|
|
maximum: 10
|
|
step: 1
|
|
defaultValue: 0
|
|
onSliderValueChanged: newValue => SettingsData.set("touchpadAccelSpeed", newValue / 10.0)
|
|
}
|
|
|
|
SettingsButtonGroupRow {
|
|
tags: ["touchpad", "acceleration", "profile"]
|
|
settingKey: "touchpadAccelProfile"
|
|
text: I18n.tr("Acceleration Profile")
|
|
description: I18n.tr("Flat uses constant speed; Adaptive scales with movement speed")
|
|
model: [I18n.tr("Default"), I18n.tr("Flat"), I18n.tr("Adaptive")]
|
|
currentIndex: {
|
|
if (SettingsData.touchpadAccelProfile === "flat") return 1;
|
|
if (SettingsData.touchpadAccelProfile === "adaptive") return 2;
|
|
return 0;
|
|
}
|
|
onSelectionChanged: (index, selected) => {
|
|
if (!selected) return;
|
|
const profiles = ["default", "flat", "adaptive"];
|
|
SettingsData.set("touchpadAccelProfile", profiles[index]);
|
|
}
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
tags: ["touchpad", "natural", "scroll", "direction"]
|
|
settingKey: "touchpadNaturalScroll"
|
|
text: I18n.tr("Natural Scrolling")
|
|
description: I18n.tr("Reverse two-finger scrolling direction")
|
|
checked: SettingsData.touchpadNaturalScroll
|
|
onToggled: checked => SettingsData.set("touchpadNaturalScroll", checked)
|
|
}
|
|
|
|
SettingsSliderRow {
|
|
tags: ["touchpad", "scroll", "speed", "factor"]
|
|
settingKey: "touchpadScrollFactor"
|
|
text: I18n.tr("Scrolling Speed")
|
|
description: I18n.tr("Adjust scrolling sensitivity multiplier")
|
|
value: Math.round(SettingsData.touchpadScrollFactor * 10)
|
|
minimum: 1
|
|
maximum: 30
|
|
step: 1
|
|
defaultValue: 10
|
|
onSliderValueChanged: newValue => SettingsData.set("touchpadScrollFactor", newValue / 10.0)
|
|
}
|
|
|
|
SettingsButtonGroupRow {
|
|
tags: ["touchpad", "scroll", "method"]
|
|
settingKey: "touchpadScrollMethod"
|
|
text: I18n.tr("Scroll Method")
|
|
description: I18n.tr("Choose when to generate scrolling events")
|
|
model: [I18n.tr("Default"), I18n.tr("Two Finger"), I18n.tr("Edge"), I18n.tr("No Scroll"), I18n.tr("On Button Down")]
|
|
currentIndex: {
|
|
if (SettingsData.touchpadScrollMethod === "two-finger") return 1;
|
|
if (SettingsData.touchpadScrollMethod === "edge") return 2;
|
|
if (SettingsData.touchpadScrollMethod === "no-scroll") return 3;
|
|
if (SettingsData.touchpadScrollMethod === "on-button-down") return 4;
|
|
return 0;
|
|
}
|
|
onSelectionChanged: (index, selected) => {
|
|
if (!selected) return;
|
|
const methods = ["default", "two-finger", "edge", "no-scroll", "on-button-down"];
|
|
SettingsData.set("touchpadScrollMethod", methods[index]);
|
|
}
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
tags: ["touchpad", "disable", "typing", "dwt"]
|
|
settingKey: "touchpadDisableWhileTyping"
|
|
text: I18n.tr("Disable While Typing")
|
|
description: I18n.tr("Prevent accidental cursor jumps while typing")
|
|
checked: SettingsData.touchpadDisableWhileTyping
|
|
onToggled: checked => SettingsData.set("touchpadDisableWhileTyping", checked)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
tags: ["touchpad", "disable", "external", "mouse"]
|
|
settingKey: "touchpadDisableOnExternalMouse"
|
|
text: I18n.tr("Disable on External Mouse")
|
|
description: I18n.tr("Disable touchpad when an external mouse is connected")
|
|
checked: SettingsData.touchpadDisableOnExternalMouse
|
|
onToggled: checked => SettingsData.set("touchpadDisableOnExternalMouse", checked)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
tags: ["touchpad", "middle", "click", "emulation"]
|
|
settingKey: "touchpadMiddleEmulation"
|
|
text: I18n.tr("Middle Click Emulation")
|
|
description: I18n.tr("Emulate middle click by pressing left and right buttons")
|
|
checked: SettingsData.touchpadMiddleEmulation
|
|
onToggled: checked => SettingsData.set("touchpadMiddleEmulation", checked)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|