/*SPDX-FileCopyrightText: 2018 Roman Gilg <subdiff@gmail.com>SPDX-FileCopyrightText: 2018 Furkan Tokac <furkantokac34@gmail.com>SPDX-License-Identifier: GPL-2.0-or-later*/import QtQuickimport QtQuick.Controls as QQC2import QtQuick.Layoutsimport org.kde.kirigami as Kirigamiimport org.kde.kquickcontrolsKirigami.ApplicationItem {id: rootpageStack.globalToolBar.style: Kirigami.ApplicationHeaderStyle.NonepageStack.columnView.columnResizeMode: Kirigami.ColumnView.SingleColumnpageStack.defaultColumnWidth: Kirigami.Units.gridUnit * 20property alias deviceIndex: deviceSelector.currentIndexsignal changeSignal()property QtObject deviceproperty int deviceCount: backend.deviceCountproperty bool loading: falseenabled: deviceCount > 0function resetModel(index) {deviceCount = backend.deviceCountformLayout.enabled = deviceCountdeviceSelector.enabled = deviceCount > 1loading = trueif (deviceCount) {device = deviceModel[index]deviceSelector.model = deviceModeldeviceSelector.currentIndex = index} else {deviceSelector.model = [""]}loading = false}function syncValuesFromBackend() {loading = truedeviceEnabled.load()leftHanded.load()middleEmulation.load()accelSpeedSpinbox.load()accelSpeedSlider.load()accelProfile.load()naturalScroll.load()scrollFactor.load()buttonMappings.load()loading = false}pageStack.initialPage: Kirigami.ScrollablePage {spacing: Kirigami.Units.smallSpacingKirigami.FormLayout {id: formLayoutenabled: deviceCount// DeviceQQC2.ComboBox {id: deviceSelectorKirigami.FormData.label: i18nd("kcmmouse", "Device:")enabled: deviceCount > 1Layout.fillWidth: truemodel: deviceModeltextRole: "name"onCurrentIndexChanged: {if (deviceCount) {device = deviceModel[currentIndex]if (!loading) {changeSignal()}}root.syncValuesFromBackend()}}Item {Kirigami.FormData.isSection: false}// GeneralQQC2.CheckBox {id: deviceEnabledKirigami.FormData.label: i18nd("kcmmouse", "General:")text: i18nd("kcmmouse", "Device enabled")function load() {if (!formLayout.enabled) {checked = falsereturn}enabled = device.supportsDisableEventschecked = enabled && device.enabled}onCheckedChanged: {if (enabled && !root.loading) {device.enabled = checkedroot.changeSignal()}}QQC2.ToolTip.delay: 1000QQC2.ToolTip.visible: hoveredQQC2.ToolTip.text: i18nd("kcmmouse", "Accept input through this device.")}QQC2.CheckBox {id: leftHandedtext: i18nd("kcmmouse", "Left handed mode")function load() {if (!formLayout.enabled) {checked = falsereturn}enabled = device.supportsLeftHandedchecked = enabled && device.leftHanded}onCheckedChanged: {if (enabled && !root.loading) {device.leftHanded = checkedroot.changeSignal()}}QQC2.ToolTip.delay: 1000QQC2.ToolTip.visible: hoveredQQC2.ToolTip.text: i18nd("kcmmouse", "Swap left and right buttons.")}QQC2.CheckBox {id: middleEmulationtext: i18nd("kcmmouse", "Press left and right buttons for middle-click")function load() {if (!formLayout.enabled) {checked = falsereturn}enabled = device.supportsMiddleEmulationchecked = enabled && device.middleEmulation}onCheckedChanged: {if (enabled && !root.loading) {device.middleEmulation = checkedroot.changeSignal()}}QQC2.ToolTip.delay: 1000QQC2.ToolTip.visible: hoveredQQC2.ToolTip.text: i18nd("kcmmouse", "Clicking left and right button simultaneously sends middle button click.")}Item {Kirigami.FormData.isSection: false}// AccelerationRowLayout {Kirigami.FormData.label: i18nd("kcmmouse", "Pointer speed:")id: accelSpeedLayout.fillWidth: truefunction onAccelSpeedChanged(val) {// check sliderif (val != accelSpeedSlider.accelSpeedValue) {accelSpeedSlider.accelSpeedValue = valaccelSpeedSlider.value = Math.round(6 + (val / 100) / 0.2)}// check spinboxif (val != accelSpeedSpinbox.value) {accelSpeedSpinbox.value = val}// check libinput accelspeedif ((val / 1000) != device.pointerAcceleration) {device.pointerAcceleration = val / 100root.changeSignal()}}QQC2.Slider {id: accelSpeedSliderLayout.fillWidth: truefrom: 1to: 11stepSize: 1property int accelSpeedValue: 0 // [-100, 100]function load() {enabled = device.supportsPointerAccelerationif (!enabled) {return}accelSpeedValue = Math.round(device.pointerAcceleration * 100)// convert libinput pointer acceleration range [-1, 1] to slider range [1, 11]value = Math.round(6 + device.pointerAcceleration / 0.2)}onValueChanged: {if (device != undefined && enabled && !root.loading) {// convert slider range [1, 11] to accelSpeedValue range [-100, 100]accelSpeedValue = Math.round(((value - 6) * 0.2) * 100)accelSpeed.onAccelSpeedChanged(accelSpeedValue)}}}QQC2.SpinBox {id: accelSpeedSpinboxLayout.minimumWidth: Kirigami.Units.gridUnit * 5from: -100to: 100stepSize: 1editable: truevalidator: DoubleValidator {bottom: accelSpeedSpinbox.fromtop: accelSpeedSpinbox.to}function load() {enabled = device.supportsPointerAccelerationif (!enabled) {return}// if existing configuration or another application set a value with more than 2 decimals// we reduce the precision to 2value = Math.round(device.pointerAcceleration * 100)}onValueChanged: {if (device != undefined && enabled && !root.loading) {accelSpeed.onAccelSpeedChanged(value)}}textFromValue: function(val, locale) {return Number(val / 100).toLocaleString(locale, "f", 2)}valueFromText: function(text, locale) {return Number.fromLocaleString(locale, text) * 100}}}ColumnLayout {id: accelProfilespacing: Kirigami.Units.smallSpacingKirigami.FormData.label: i18nd("kcmmouse", "Pointer acceleration:")Kirigami.FormData.buddyFor: accelProfileFlatfunction load() {enabled = device.supportsPointerAccelerationProfileAdaptiveif (!enabled) {accelProfileAdaptive.checked = falseaccelProfileFlat.checked = falsereturn}if(device.pointerAccelerationProfileAdaptive) {accelProfileAdaptive.checked = trueaccelProfileFlat.checked = false} else {accelProfileAdaptive.checked = falseaccelProfileFlat.checked = true}}function syncCurrent() {if (enabled && !root.loading) {device.pointerAccelerationProfileFlat = accelProfileFlat.checkeddevice.pointerAccelerationProfileAdaptive = accelProfileAdaptive.checkedroot.changeSignal()}}QQC2.RadioButton {id: accelProfileFlattext: i18nd("kcmmouse", "None")QQC2.ToolTip.delay: 1000QQC2.ToolTip.visible: hoveredQQC2.ToolTip.text: i18nd("kcmmouse", "Cursor moves the same distance as the mouse movement.")onCheckedChanged: accelProfile.syncCurrent()}QQC2.RadioButton {id: accelProfileAdaptivetext: i18nd("kcmmouse", "Standard")QQC2.ToolTip.delay: 1000QQC2.ToolTip.visible: hoveredQQC2.ToolTip.text: i18nd("kcmmouse", "Cursor travel distance depends on the mouse movement speed.")onCheckedChanged: accelProfile.syncCurrent()}}Item {Kirigami.FormData.isSection: false}// ScrollingQQC2.CheckBox {id: naturalScrollKirigami.FormData.label: i18nd("kcmmouse", "Scrolling:")text: i18nd("kcmmouse", "Invert scroll direction")function load() {enabled = device.supportsNaturalScrollchecked = enabled && device.naturalScroll}onCheckedChanged: {if (enabled && !root.loading) {device.naturalScroll = checkedroot.changeSignal()}}QQC2.ToolTip.delay: 1000QQC2.ToolTip.visible: hoveredQQC2.ToolTip.text: i18nd("kcmmouse", "Touchscreen like scrolling.")}// Scroll Speed aka scroll FactorGridLayout {Kirigami.FormData.label: i18nd("kcm_touchpad", "Scrolling speed:")Kirigami.FormData.buddyFor: scrollFactorLayout.fillWidth: truecolumns: 3QQC2.Slider {id: scrollFactorLayout.fillWidth: truefrom: 0to: 14stepSize: 1readonly property list<real> values: [0.1,0.3,0.5,0.75,1, // default1.5,2,3,4,5,7,9,12,15,20]Layout.columnSpan: 3function load() {let index = values.indexOf(device.scrollFactor)if (index === -1) {index = values.indexOf(1);}value = index}onMoved: {device.scrollFactor = values[value]root.changeSignal()}}//row 2QQC2.Label {text: i18ndc("kcmmouse", "Slower Scroll", "Slower")}Item {Layout.fillWidth: true}QQC2.Label {text: i18ndc("kcmmouse", "Faster Scroll Speed", "Faster")}}Item {Kirigami.FormData.isSection: true}QQC2.Button {text: i18ndc("kcmmouse", "@action:button", "Re-bind Additional Mouse Buttonsβ¦")visible: buttonMappings.model.length > 0 || Array.prototype.some.call(deviceModel, device => device.supportedButtons & ~(Qt.LeftButton | Qt.RightButton | Qt.MiddleButton))onClicked: root.pageStack.push(buttonPage)}}}Kirigami.ScrollablePage {id: buttonPagevisible: falseMouseArea {// Deliberately using MouseArea on the page instead of a TapHandler on the button, so we can capture clicks anywhereid: buttonCaptureproperty var lastButton: {}anchors.fill: parentenabled: newBinding.checkedpreventStealing: trueacceptedButtons: Qt.AllButtons & ~(Qt.LeftButton | Qt.RightButton | Qt.MiddleButton)onClicked: {lastButton = buttonMappings.extraButtons.find(entry => Qt[entry.buttonName] === mouse.button)newBinding.visible = falsenewKeySequenceItem.visible = truenewKeySequenceItem.startCapturing()}}ColumnLayout {Kirigami.FormLayout {id: buttonLayouttwinFormLayouts: otherLayoutRepeater {id: buttonMappingsreadonly property var extraButtons: Array.from({length: 24}, (value, index) => ({buttonName: "ExtraButton" + (index + 1),button: Qt["ExtraButton" + (index + 1)],label: i18ndc("kcmmouse", "@label for assigning an action to a numbered button", "Extra Button %1:", index + 1)}))function load() {model = Qt.binding(() => extraButtons.filter(entry => backend.buttonMapping.hasOwnProperty(entry.buttonName)))}delegate: KeySequenceItem {Kirigami.FormData.label: modelData.labelkeySequence: backend.buttonMapping[modelData.buttonName]modifierlessAllowed: truemultiKeyShortcutsAllowed: falsecheckForConflictsAgainst: ShortcutType.NoneonCaptureFinished: {const copy = backend.buttonMapping;copy[modelData.buttonName] = keySequencebackend.buttonMapping = copyroot.changeSignal()}}}}Kirigami.InlineMessage {id: explanationLabelLayout.fillWidth: truevisible: newBinding.checked || newKeySequenceItem.visibletext: newBinding.visible ? i18ndc("kcmmouse","@action:button", "Press the mouse button for which you want to add a key binding") :i18ndc("kcmmouse","@action:button, %1 is the translation of 'Extra Button %1' from above", "Enter the new key combination for %1", buttonCapture.lastButton.label)actions: [Kirigami.Action {icon.name: "dialog-cancel"text: i18ndc("kcmmouse", "@action:button", "Cancel")onTriggered: {newKeySequenceItem.visible = false;newBinding.visible = truenewBinding.checked = false}}]}Kirigami.FormLayout {id: otherLayouttwinFormLayouts: buttonLayoutQQC2.Button {id: newBindingcheckable: truetext: checked ? i18ndc("kcmmouse", "@action:button", "Press a mouse button ") :i18ndc("kcmmouse", "@action:button, Bind a mousebutton to keyboard key(s)", "Add Bindingβ¦")icon.name: "list-add"}KeySequenceItem {id: newKeySequenceItemvisible: falsemodifierlessAllowed: truemultiKeyShortcutsAllowed: falsecheckForConflictsAgainst: ShortcutType.NoneonCaptureFinished: {visible = falsenewBinding.visible = truenewBinding.checked = falseconst copy = backend.buttonMapping;copy[buttonCapture.lastButton.buttonName] = keySequencebackend.buttonMapping = copyroot.changeSignal()}}Item {Kirigami.FormData.isSection: true}QQC2.Button {onClicked: root.pageStack.pop()text: i18ndc("kcmmouse", "@action:button", "Go back")icon.name: "go-previous"}}}}}