/*SPDX-FileCopyrightText: 2018 Roman Gilg <subdiff@gmail.com>SPDX-FileCopyrightText: 2018 Furkan Tokac <furkantokac34@gmail.com>SPDX-FileCopyrightText: 2024 Jakob Petsovits <jpetso@petsovits.com>SPDX-FileCopyrightText: 2024 ivan tkachenko <me@ratijas.tk>SPDX-License-Identifier: GPL-2.0-or-later*/pragma ComponentBehavior: Boundimport QtQuickimport QtQuick.Controls as QQC2import QtQuick.Layoutsimport org.kde.kcmutils as KCMUtilsimport org.kde.kirigami as Kirigamiimport org.kde.kquickcontrols as KQuickControlsimport org.kde.plasma.private.kcm_mouse as MouseKCMUtils.SimpleKCM {id: root// Would be nice to use a `required` qualifier, but KQuickManagedConfigModule doesn't// have an API to provide a mapping of initial properties for the root page.readonly property Mouse.InputBackend backend: KCMUtils.ConfigModule.inputBackendreadonly property Mouse.InputDevice device: backend.inputDevices[KCMUtils.ConfigModule.currentDeviceIndex] ?? nullfunction supportsExtraButtons(device: Mouse.InputDevice): bool {return (device?.supportedButtons ?? 0) & ~(Qt.LeftButton | Qt.RightButton | Qt.MiddleButton);}actions: Kirigami.Action {icon.name: "input-mouse-click-left-symbolic"text: i18ndc("kcmmouse", "@action:button", "Configure Extra Buttonsβ¦")visible: {if (root.backend.isAnonymousInputDevice) {return false;}return root.backend.buttonMappingCount > 0|| root.backend.inputDevices.some(root.supportsExtraButtons);}onTriggered: source => {root.KCMUtils.ConfigModule.push("bindings.qml");}}header: Header {saveLoadMessage: root.KCMUtils.ConfigModule.saveLoadMessagehotplugMessage: root.KCMUtils.ConfigModule.hotplugMessage}Kirigami.FormLayout {id: formLayoutenabled: root.device !== null && root.backend.inputDevices.length > 0// DeviceQQC2.ComboBox {id: deviceSelectorKirigami.FormData.label: i18nd("kcmmouse", "Device:")visible: !root.backend.isAnonymousInputDeviceenabled: count > 1Layout.fillWidth: truemodel: root.backend.inputDevicestextRole: "name"Component.onCompleted: {currentIndex = Qt.binding(() => root.KCMUtils.ConfigModule.currentDeviceIndex);}onActivated: {root.KCMUtils.ConfigModule.currentDeviceIndex = currentIndex;}}Item {Kirigami.FormData.isSection: false}// GeneralQQC2.CheckBox {id: deviceEnabledKirigami.FormData.label: i18nd("kcmmouse", "General:")text: i18nd("kcmmouse", "Device enabled")visible: !root.backend.isAnonymousInputDeviceenabled: root.device?.supportsDisableEvents ?? falsechecked: enabled && (root.device?.enabled ?? false)onToggled: {if (root.device) {root.device.enabled = checked}}QQC2.ToolTip.delay: 1000QQC2.ToolTip.visible: hoveredQQC2.ToolTip.text: i18nd("kcmmouse", "Accept input through this device.")}QQC2.CheckBox {id: leftHandedKirigami.FormData.label: deviceEnabled.visible ? null : deviceEnabled.Kirigami.FormData.labeltext: i18nd("kcmmouse", "Left-handed mode")enabled: root.device?.supportsLeftHanded ?? falsechecked: enabled && (root.device?.leftHanded ?? false)onToggled: {if (root.device) {root.device.leftHanded = checked}}QQC2.ToolTip.delay: 1000QQC2.ToolTip.visible: hoveredQQC2.ToolTip.text: i18nd("kcmmouse", "Swap left and right buttons.")}RowLayout {spacing: Kirigami.Units.smallSpacingQQC2.CheckBox {id: middleEmulationtext: i18ndc("kcmmouse", "@option:check", "Press left and right buttons for middle-click")enabled: root.device?.supportsMiddleEmulation ?? falsechecked: enabled && (root.device?.middleEmulation ?? false)onToggled: {if (root.device) {root.device.middleEmulation = checked}}QQC2.ToolTip.delay: 1000QQC2.ToolTip.visible: hoveredQQC2.ToolTip.text: i18ndc("kcmmouse", "@info:tooltip" ,"Pressing the left and right button simultaneously acts as middle-click.")}Kirigami.ContextualHelpButton {toolTipText: i18ndc("kcmmouse", "@info:tooltip from ContextualHelpButton", "Activating this setting increases click latency by 50ms. The extra delay is needed to correctly detect simultaneous left and right button presses.")}}Item {Kirigami.FormData.isSection: false}// AccelerationRowLayout {id: accelSpeedKirigami.FormData.label: i18nd("kcmmouse", "Pointer speed:")Layout.fillWidth: truespacing: Kirigami.Units.smallSpacingfunction onAccelSpeedChanged(value: int): void {if (root.device && (value / 1000) !== root.device.pointerAcceleration) {root.device.pointerAcceleration = value / 100}}QQC2.Slider {id: accelSpeedSliderLayout.fillWidth: truefrom: 1to: 11stepSize: 1enabled: root.device?.supportsPointerAcceleration ?? false// convert libinput pointer acceleration range [-1, 1] to slider range [1, 11]value: enabled && root.device ? Math.round(6 + root.device.pointerAcceleration / 0.2) : 0onMoved: {if (root.device) {// convert slider range [1, 11] to accelSpeedValue range [-100, 100]const accelSpeedValue = Math.round(((value - 6) * 0.2) * 100)accelSpeed.onAccelSpeedChanged(accelSpeedValue)}}}QQC2.SpinBox {id: accelSpeedSpinboxLayout.minimumWidth: Kirigami.Units.gridUnit * 5from: -100to: 100stepSize: 1editable: trueenabled: root.device?.supportsPointerAcceleration ?? false// if existing configuration or another application set a value with more than 2 decimals// we reduce the precision to 2value: enabled && root.device ? Math.round(root.device.pointerAcceleration * 100) : 0validator: DoubleValidator {bottom: accelSpeedSpinbox.fromtop: accelSpeedSpinbox.to}onValueModified: {if (root.device) {accelSpeed.onAccelSpeedChanged(value)// Keyboard input breaks SpinBox value bindings with current Qt.// Restore the binding so clicking "Reset" will update it correctly.value = Qt.binding(() => accelSpeedSpinbox.enabled && root.device? Math.round(root.device.pointerAcceleration * 100): 0);}}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: accelProfileFlatenabled: root.device?.supportsPointerAccelerationProfileAdaptive ?? falseQQC2.ButtonGroup {buttons: [accelProfileFlat, accelProfileAdaptive]onClicked: {if (root.device) {root.device.pointerAccelerationProfileFlat = accelProfileFlat.checkedroot.device.pointerAccelerationProfileAdaptive = accelProfileAdaptive.checked}}}QQC2.RadioButton {id: accelProfileFlattext: i18nd("kcmmouse", "None")checked: accelProfile.enabled && (root.device?.pointerAccelerationProfileFlat ?? false)QQC2.ToolTip.delay: 1000QQC2.ToolTip.visible: hoveredQQC2.ToolTip.text: i18nd("kcmmouse", "Cursor moves the same distance as the mouse movement.")}QQC2.RadioButton {id: accelProfileAdaptivetext: i18nd("kcmmouse", "Standard")checked: accelProfile.enabled && (root.device?.pointerAccelerationProfileAdaptive ?? false)QQC2.ToolTip.delay: 1000QQC2.ToolTip.visible: hoveredQQC2.ToolTip.text: i18nd("kcmmouse", "Cursor travel distance depends on the mouse movement speed.")}}Item {Kirigami.FormData.isSection: false}// Scroll Speed aka scroll FactorGridLayout {Kirigami.FormData.label: i18nd("kcm_touchpad", "Scrolling speed:")Kirigami.FormData.buddyFor: scrollFactorLayout.fillWidth: truevisible: !root.backend.isAnonymousInputDevicecolumns: 3QQC2.Slider {id: scrollFactorLayout.fillWidth: trueLayout.columnSpan: 3from: 0to: 14stepSize: 1enabled: root.device !== nullreadonly property list<real> values: [0.1,0.3,0.5,0.75,1, // default1.5,2,3,4,5,7,9,12,15,20,]function indexOf(val: real): int {const index = values.indexOf(val)return index === -1 ? values.indexOf(1) : index}value: indexOf(root.device?.scrollFactor ?? 1)onMoved: {if (root.device) {root.device.scrollFactor = values[value]}}}//row 2QQC2.Label {text: i18ndc("kcmmouse", "Slower Scroll", "Slower")textFormat: Text.PlainText}Item {Layout.fillWidth: true}QQC2.Label {text: i18ndc("kcmmouse", "Faster Scroll Speed", "Faster")textFormat: Text.PlainText}}// ScrollingQQC2.CheckBox {id: naturalScrollKirigami.FormData.label: i18nd("kcmmouse", "Scrolling:")text: i18nd("kcmmouse", "Invert scroll direction")enabled: root.device?.supportsNaturalScroll ?? falsechecked: enabled && (root.device?.naturalScroll ?? false)onToggled: {if (root.device) {root.device.naturalScroll = checked}}QQC2.ToolTip.delay: 1000QQC2.ToolTip.visible: hoveredQQC2.ToolTip.text: i18nd("kcmmouse", "Touchscreen-like scrolling.")}QQC2.CheckBox {id: scrollOnButtonDowntext: i18nd("kcmmouse", "Press middle button and move mouse to scroll")enabled: root.device?.supportsScrollOnButtonDown ?? falsechecked: enabled && (root.device?.scrollOnButtonDown ?? false)onToggled: {if (root.device) {root.device.scrollOnButtonDown = checked}}QQC2.ToolTip.delay: 1000QQC2.ToolTip.visible: hoveredQQC2.ToolTip.text: i18nd("kcmmouse", "Scrolling with the mouse while the middle button is pressed.")}}}