/*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: {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.Form {id: formLayoutenabled: root.device !== null && root.backend.inputDevices.length > 0Kirigami.FormGroup {// DeviceKirigami.FormEntry {enabled: deviceSelector.count > 1contentItem: QQC2.ComboBox {id: deviceSelectorKirigami.FormData.label: i18ndc("kcmmouse", "@title:listbox select device", "Device:")Layout.fillWidth: truemodel: root.backend.inputDevicestextRole: "name"Component.onCompleted: {currentIndex = Qt.binding(() => root.KCMUtils.ConfigModule.currentDeviceIndex);}onActivated: {root.KCMUtils.ConfigModule.currentDeviceIndex = currentIndex;}}}Kirigami.FormSeparator {}// GeneralKirigami.FormEntry {enabled: root.device?.supportsDisableEvents ?? falsecontentItem: QQC2.CheckBox {id: deviceEnabledKirigami.FormData.label: i18ndc("kcmmouse", "@title:group", "General:")text: i18ndc("kcmmouse", "@option:check enable this mouse device", "Enable device")checked: root.device && (!root.device.supportsDisableEvents || root.device.enabled)onToggled: {if (root.device) {root.device.enabled = checked}}}}Kirigami.FormSeparator {}Kirigami.FormEntry {contentItem: QQC2.CheckBox {Kirigami.FormData.label: i18ndc("kcmmouse", "@title:group", "Left and right buttons:")text: i18ndc("kcmmouse", "@option:check Left and right buttons", "Swap (left-handed mode)")enabled: root.device?.supportsLeftHanded ?? falsechecked: enabled && (root.device?.leftHanded ?? false)onToggled: {if (root.device) {root.device.leftHanded = checked}}}}Kirigami.FormEntry {contentItem: QQC2.CheckBox {id: middleEmulationtext: i18ndc("kcmmouse", "@option:check 'both' refers to the left and right buttons'", "Press both simultaneously to middle-click")enabled: root.device?.supportsMiddleEmulation ?? falsechecked: enabled && (root.device?.middleEmulation ?? false)onToggled: {if (root.device) {root.device.middleEmulation = checked}}}trailingItems: 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.")}}}Kirigami.FormGroup {// AccelerationKirigami.FormEntry {contentItem: RowLayout {id: accelSpeedKirigami.FormData.label: i18ndc("kcmmouse", "@label:slider and @label:spinbox", "Pointer speed:")Layout.fillWidth: truespacing: Kirigami.Units.smallSpacingfunction onAccelSpeedChanged(value: int): void {const newPointerAcceleration = value / 100if (root.device && newPointerAcceleration !== root.device.pointerAcceleration) {root.device.pointerAcceleration = newPointerAcceleration}}QQC2.Slider {id: accelSpeedSliderLayout.fillWidth: truefrom: 1to: 11stepSize: 1Kirigami.StyleHints.tickMarkStepSize: 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}}}}Kirigami.FormEntry {enabled: root.device?.supportsPointerAccelerationProfileAdaptive ?? falsevisible: enabledcontentItem: QQC2.CheckBox {id: accelProfileEnabledtext: i18ndc("kcmmouse", "@option:check", "Enable pointer acceleration")checked: enabled && !(root.device?.pointerAccelerationProfileFlat ?? false)onToggled: {if (root.device) {root.device.pointerAccelerationProfileFlat = !checkedroot.device.pointerAccelerationProfileAdaptive = checked}}}trailingItems: Kirigami.ContextualHelpButton {toolTipText: i18ndc("kcmmouse", "@info:whatsthis ContextualHelpButton tooltip", "When enabled, pointer travel distance increases with faster movement speed.")}}Kirigami.FormSeparator {}Kirigami.FormEntry {// Scroll Speed aka scroll Factorid: scrollProfilereadonly property list<real> values: [0.1,0.3,0.5,0.75,1, // default1.5,2,3,4,5,7,9,12,15,20,]contentItem: GridLayout {Kirigami.FormData.label: i18ndc("kcmmouse", "@label:slider and @label:spinbox", "Scrolling speed:")Kirigami.FormData.buddyFor: scrollFactorLayout.fillWidth: truecolumns: 4QQC2.Slider {id: scrollFactorLayout.fillWidth: trueLayout.columnSpan: 3from: 0to: 14stepSize: 1Kirigami.StyleHints.tickMarkStepSize: 1enabled: root.device !== nullfunction indexOf(val: real): int {const index = scrollProfile.values.indexOf(val)return index === -1 ? scrollProfile.values.indexOf(1) : index}value: indexOf(root.device?.scrollFactor ?? 1)onMoved: {if (root.device) {root.device.scrollFactor = scrollProfile.values[value]}}}QQC2.SpinBox {id: scrollFactorSpinBoxLayout.minimumWidth: Kirigami.Units.gridUnit * 4from: 0to: 14stepSize: 1function indexOf(val: real): int {const index = scrollProfile.values.indexOf(val)return index === -1 ? scrollProfile.values.indexOf(1) : index}value: indexOf(root.device?.scrollFactor ?? 1)onValueChanged: {if (root.device) {root.device.scrollFactor = scrollProfile.values[value]}}textFromValue: function(val, locale) {return Number(val + 1).toLocaleString(locale, "d", 0)}valueFromText: function(text, locale) {return Number.fromLocaleString(locale, text) - 1}}//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}}}// ScrollingKirigami.FormEntry {contentItem: QQC2.CheckBox {id: naturalScrollKirigami.FormData.label: i18ndc("kcmmouse", "@title:group", "Scrolling:")text: i18ndc("kcmmouse", "@option:check", "Invert scroll direction")enabled: root.device?.supportsNaturalScroll ?? falsechecked: enabled && (root.device?.naturalScroll ?? false)onToggled: {if (root.device) {root.device.naturalScroll = checked}}}}Kirigami.FormEntry {contentItem: QQC2.CheckBox {id: scrollButtontext: i18ndc("kcmmouse", "@option:check", "Scroll when moving the pointer")enabled: root.device?.supportsScrollOnButtonDown ?? falsechecked: enabled && (root.device?.scrollOnButtonDown ?? false)onToggled: {if (root.device) {root.device.scrollOnButtonDown = checked}}}}QQC2.ButtonGroup { id: autoScrollRadioGroup }Kirigami.FormEntry {contentItem: QQC2.RadioButton {id: scrollOnButtonDowntext: i18ndc("kcmmouse", "@option:radio", "while pressing the middle button")enabled: scrollButton.checkedchecked: !(root.device?.scrollButtonLock ?? false)leftPadding: mirrored ? scrollOnButtonDown.padding : (scrollButton.indicator.width + scrollButton.spacing)rightPadding: mirrored ? (scrollButton.indicator.width + scrollButton.spacing) : scrollOnButtonDown.paddingQQC2.ButtonGroup.group: autoScrollRadioGrouponToggled: {if (root.device) {root.device.scrollButtonLock = false}}}trailingItems: Kirigami.ContextualHelpButton {toolTipText: i18ndc("kcmmouse", "@info:whatsthis ContextualHelpButton tooltip", "This will interfere with applications that use middle-button drag, such as some image editors, document viewers, or video games. It may be used on any device, but is intended primarily as a substitute for scroll wheels on devices that do not have any.")}}Kirigami.FormEntry {contentItem: QQC2.RadioButton {id: scrollButtonLocktext: i18ndc("kcmmouse", "@option:radio", "after clicking the middle button")enabled: scrollButton.checkedchecked: root.device?.scrollButtonLock ?? falseleftPadding: mirrored ? scrollButtonLock.padding : (scrollButton.indicator.width + scrollButton.spacing)rightPadding: mirrored ? (scrollButton.indicator.width + scrollButton.spacing) : scrollButtonLock.paddingQQC2.ButtonGroup.group: autoScrollRadioGrouponToggled: {if (root.device) {root.device.scrollButtonLock = true}}}trailingItems: Kirigami.ContextualHelpButton {toolTipText: i18ndc("kcmmouse", "@info:whatsthis ContextualHelpButton tooltip", "This will interfere with middle-clicks in general; middle-click twice when you want the application to receive a single middle-click. This includes middle-click dragging.")}}}}}