diff --git a/kcms/mouse/backends/kwin_wl/kwin_wl_device.cpp b/kcms/mouse/backends/kwin_wl/kwin_wl_device.cpp index b4f421e09..e945a3101 100644 --- a/kcms/mouse/backends/kwin_wl/kwin_wl_device.cpp +++ b/kcms/mouse/backends/kwin_wl/kwin_wl_device.cpp @@ -1,192 +1,198 @@ /* * Copyright 2018 Roman Gilg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kwin_wl_device.h" #include #include #include #include "logging.h" namespace { template T valueLoaderPart(QVariant const &reply) { Q_UNUSED(reply); return T(); } template<> bool valueLoaderPart(QVariant const &reply) { return reply.toBool(); } template<> int valueLoaderPart(QVariant const &reply) { return reply.toInt(); } template<> quint32 valueLoaderPart(QVariant const &reply) { return reply.toInt(); } template<> qreal valueLoaderPart(QVariant const &reply) { return reply.toReal(); } template<> QString valueLoaderPart(QVariant const &reply) { return reply.toString(); } template<> Qt::MouseButtons valueLoaderPart(QVariant const &reply) { return static_cast(reply.toInt()); } } KWinWaylandDevice::KWinWaylandDevice(QString dbusName) { m_iface = new QDBusInterface(QStringLiteral("org.kde.KWin"), QStringLiteral("/org/kde/KWin/InputDevice/") + dbusName, QStringLiteral("org.kde.KWin.InputDevice"), QDBusConnection::sessionBus(), this); } KWinWaylandDevice::~KWinWaylandDevice() { delete m_iface; } bool KWinWaylandDevice::init() { // need to do it here in order to populate combobox and handle events return valueLoader(m_name) && valueLoader(m_sysName); } bool KWinWaylandDevice::getConfig() { bool success = true; // general success &= valueLoader(m_supportsDisableEvents); success &= valueLoader(m_enabled); // advanced success &= valueLoader(m_supportedButtons); success &= valueLoader(m_supportsLeftHanded); success &= valueLoader(m_leftHandedEnabledByDefault); success &= valueLoader(m_leftHanded); success &= valueLoader(m_supportsMiddleEmulation); success &= valueLoader(m_middleEmulationEnabledByDefault); success &= valueLoader(m_middleEmulation); // acceleration success &= valueLoader(m_supportsPointerAcceleration); success &= valueLoader(m_supportsPointerAccelerationProfileFlat); success &= valueLoader(m_supportsPointerAccelerationProfileAdaptive); success &= valueLoader(m_defaultPointerAcceleration); success &= valueLoader(m_defaultPointerAccelerationProfileFlat); success &= valueLoader(m_defaultPointerAccelerationProfileAdaptive); success &= valueLoader(m_pointerAcceleration); success &= valueLoader(m_pointerAccelerationProfileFlat); success &= valueLoader(m_pointerAccelerationProfileAdaptive); // natural scroll success &= valueLoader(m_supportsNaturalScroll); success &= valueLoader(m_naturalScrollEnabledByDefault); success &= valueLoader(m_naturalScroll); + success &= valueLoader(m_scrollFactor); + return success; } bool KWinWaylandDevice::getDefaultConfig() { m_enabled.set(true); m_leftHanded.set(false); m_pointerAcceleration.set(m_defaultPointerAcceleration); m_pointerAccelerationProfileFlat.set(m_defaultPointerAccelerationProfileFlat); m_pointerAccelerationProfileAdaptive.set(m_defaultPointerAccelerationProfileAdaptive); m_middleEmulation.set(m_middleEmulationEnabledByDefault); m_naturalScroll.set(m_naturalScrollEnabledByDefault); + m_scrollFactor.set(1.0); + return true; } bool KWinWaylandDevice::applyConfig() { QVector msgs; msgs << valueWriter(m_enabled) << valueWriter(m_leftHanded) << valueWriter(m_pointerAcceleration) << valueWriter(m_pointerAccelerationProfileFlat) << valueWriter(m_pointerAccelerationProfileAdaptive) << valueWriter(m_middleEmulation) - << valueWriter(m_naturalScroll); + << valueWriter(m_naturalScroll) + << valueWriter(m_scrollFactor); bool success = true; QString error_msg; for (QString m : msgs) { if (!m.isNull()) { qCCritical(KCM_MOUSE) << "in error:" << m; if (!success) { error_msg.append("\n"); } error_msg.append(m); success = false; } } if (!success) { qCCritical(KCM_MOUSE) << error_msg; } return success; } bool KWinWaylandDevice::isChangedConfig() const { return m_enabled.changed() || m_leftHanded.changed() || m_pointerAcceleration.changed() || m_pointerAccelerationProfileFlat.changed() || m_pointerAccelerationProfileAdaptive.changed() || m_middleEmulation.changed() || + m_scrollFactor.changed() || m_naturalScroll.changed(); } template QString KWinWaylandDevice::valueWriter(const Prop &prop) { if (!prop.changed()) { return QString(); } m_iface->setProperty(prop.dbus, prop.val); QDBusError error = m_iface->lastError(); if (error.isValid()) { qCCritical(KCM_MOUSE) << error.message(); return error.message(); } return QString(); } template bool KWinWaylandDevice::valueLoader(Prop &prop) { QVariant reply = m_iface->property(prop.dbus); if (!reply.isValid()) { qCCritical(KCM_MOUSE) << "Error on d-bus read of" << prop.dbus; prop.avail = false; return false; } prop.avail = true; T replyValue = valueLoaderPart(reply); prop.old = replyValue; prop.val = replyValue; return true; } diff --git a/kcms/mouse/backends/kwin_wl/kwin_wl_device.h b/kcms/mouse/backends/kwin_wl/kwin_wl_device.h index 5da165e8d..35bf101c9 100644 --- a/kcms/mouse/backends/kwin_wl/kwin_wl_device.h +++ b/kcms/mouse/backends/kwin_wl/kwin_wl_device.h @@ -1,264 +1,275 @@ /* * Copyright 2018 Roman Gilg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KWINWAYLANDDEVICE_H #define KWINWAYLANDDEVICE_H #include #include class QDBusInterface; class KWinWaylandDevice : public QObject { Q_OBJECT // // general Q_PROPERTY(QString name READ name CONSTANT) Q_PROPERTY(bool supportsDisableEvents READ supportsDisableEvents CONSTANT) Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged) // // advanced Q_PROPERTY(Qt::MouseButtons supportedButtons READ supportedButtons CONSTANT) Q_PROPERTY(bool supportsLeftHanded READ supportsLeftHanded CONSTANT) Q_PROPERTY(bool leftHandedEnabledByDefault READ leftHandedEnabledByDefault CONSTANT) Q_PROPERTY(bool leftHanded READ isLeftHanded WRITE setLeftHanded NOTIFY leftHandedChanged) Q_PROPERTY(bool supportsMiddleEmulation READ supportsMiddleEmulation CONSTANT) Q_PROPERTY(bool middleEmulationEnabledByDefault READ middleEmulationEnabledByDefault CONSTANT) Q_PROPERTY(bool middleEmulation READ isMiddleEmulation WRITE setMiddleEmulation NOTIFY middleEmulationChanged) // // acceleration speed and profile Q_PROPERTY(bool supportsPointerAcceleration READ supportsPointerAcceleration CONSTANT) Q_PROPERTY(qreal pointerAcceleration READ pointerAcceleration WRITE setPointerAcceleration NOTIFY pointerAccelerationChanged) Q_PROPERTY(bool supportsPointerAccelerationProfileFlat READ supportsPointerAccelerationProfileFlat CONSTANT) Q_PROPERTY(bool defaultPointerAccelerationProfileFlat READ defaultPointerAccelerationProfileFlat CONSTANT) Q_PROPERTY(bool pointerAccelerationProfileFlat READ pointerAccelerationProfileFlat WRITE setPointerAccelerationProfileFlat NOTIFY pointerAccelerationProfileChanged) Q_PROPERTY(bool supportsPointerAccelerationProfileAdaptive READ supportsPointerAccelerationProfileAdaptive CONSTANT) Q_PROPERTY(bool defaultPointerAccelerationProfileAdaptive READ defaultPointerAccelerationProfileAdaptive CONSTANT) Q_PROPERTY(bool pointerAccelerationProfileAdaptive READ pointerAccelerationProfileAdaptive WRITE setPointerAccelerationProfileAdaptive NOTIFY pointerAccelerationProfileChanged) // // scrolling Q_PROPERTY(bool supportsNaturalScroll READ supportsNaturalScroll CONSTANT) Q_PROPERTY(bool naturalScrollEnabledByDefault READ naturalScrollEnabledByDefault CONSTANT) Q_PROPERTY(bool naturalScroll READ isNaturalScroll WRITE setNaturalScroll NOTIFY naturalScrollChanged) + Q_PROPERTY(qreal scrollFactor READ scrollFactor WRITE setScrollFactor NOTIFY scrollFactorChanged) public: KWinWaylandDevice(QString dbusName); ~KWinWaylandDevice() override; bool init(); bool getConfig(); bool getDefaultConfig(); bool applyConfig(); bool isChangedConfig() const; // // general QString name() const { return m_name.val; } QString sysName() const { return m_sysName.val; } bool supportsDisableEvents() const { return m_supportsDisableEvents.val; } void setEnabled(bool enabled) { m_enabled.set(enabled); } bool isEnabled() const { return m_enabled.val; } Qt::MouseButtons supportedButtons() const { return m_supportedButtons.val; } // // advanced bool supportsLeftHanded() const { return m_supportsLeftHanded.val; } bool leftHandedEnabledByDefault() const { return m_leftHandedEnabledByDefault.val; } bool isLeftHanded() const { return m_leftHanded.val; } void setLeftHanded(bool set) { m_leftHanded.set(set); } bool supportsMiddleEmulation() const { return m_supportsMiddleEmulation.val; } bool middleEmulationEnabledByDefault() const { return m_middleEmulationEnabledByDefault.val; } bool isMiddleEmulation() const { return m_middleEmulation.val; } void setMiddleEmulation(bool set) { m_middleEmulation.set(set); } // // acceleration speed and profile bool supportsPointerAcceleration() const { return m_supportsPointerAcceleration.val; } qreal pointerAcceleration() const { return m_pointerAcceleration.val; } void setPointerAcceleration(qreal acceleration) { m_pointerAcceleration.set(acceleration); } bool supportsPointerAccelerationProfileFlat() const { return m_supportsPointerAccelerationProfileFlat.val; } bool defaultPointerAccelerationProfileFlat() const { return m_defaultPointerAccelerationProfileFlat.val; } bool pointerAccelerationProfileFlat() const { return m_pointerAccelerationProfileFlat.val; } void setPointerAccelerationProfileFlat(bool set) { m_pointerAccelerationProfileFlat.set(set); } bool supportsPointerAccelerationProfileAdaptive() const { return m_supportsPointerAccelerationProfileAdaptive.val; } bool defaultPointerAccelerationProfileAdaptive() const { return m_defaultPointerAccelerationProfileAdaptive.val; } bool pointerAccelerationProfileAdaptive() const { return m_pointerAccelerationProfileAdaptive.val; } void setPointerAccelerationProfileAdaptive(bool set) { m_pointerAccelerationProfileAdaptive.set(set); } // // scrolling bool supportsNaturalScroll() const { return m_supportsNaturalScroll.val; } bool naturalScrollEnabledByDefault() const { return m_naturalScrollEnabledByDefault.val; } bool isNaturalScroll() const { return m_naturalScroll.val; } void setNaturalScroll(bool set) { m_naturalScroll.set(set); } + qreal scrollFactor() const { + return m_scrollFactor.val; + } + void setScrollFactor(qreal set) { + m_scrollFactor.set(set); + } + + Q_SIGNALS: void leftHandedChanged(); void pointerAccelerationChanged(); void pointerAccelerationProfileChanged(); void enabledChanged(); void middleEmulationChanged(); void naturalScrollChanged(); + void scrollFactorChanged(); private: template struct Prop { explicit Prop(const QByteArray &dbusName) : dbus(dbusName) {} void set(T newVal) { if (avail && val != newVal) { val = newVal; } } void set(const Prop &p) { if (avail && val != p.val) { val = p.val; } } bool changed() const { return avail && (old != val); } QByteArray dbus; bool avail; T old; T val; }; template bool valueLoader(Prop &prop); template QString valueWriter(const Prop &prop); // // general Prop m_name = Prop("name"); Prop m_sysName = Prop("sysName"); Prop m_supportsDisableEvents = Prop("supportsDisableEvents"); Prop m_enabled = Prop("enabled"); // // advanced Prop m_supportedButtons = Prop("supportedButtons"); Prop m_supportsLeftHanded = Prop("supportsLeftHanded"); Prop m_leftHandedEnabledByDefault = Prop("leftHandedEnabledByDefault"); Prop m_leftHanded = Prop("leftHanded"); Prop m_supportsMiddleEmulation = Prop("supportsMiddleEmulation"); Prop m_middleEmulationEnabledByDefault = Prop("middleEmulationEnabledByDefault"); Prop m_middleEmulation = Prop("middleEmulation"); // // acceleration speed and profile Prop m_supportsPointerAcceleration = Prop("supportsPointerAcceleration"); Prop m_defaultPointerAcceleration = Prop("defaultPointerAcceleration"); Prop m_pointerAcceleration = Prop("pointerAcceleration"); Prop m_supportsPointerAccelerationProfileFlat = Prop("supportsPointerAccelerationProfileFlat"); Prop m_defaultPointerAccelerationProfileFlat = Prop("defaultPointerAccelerationProfileFlat"); Prop m_pointerAccelerationProfileFlat = Prop("pointerAccelerationProfileFlat"); Prop m_supportsPointerAccelerationProfileAdaptive = Prop("supportsPointerAccelerationProfileAdaptive"); Prop m_defaultPointerAccelerationProfileAdaptive = Prop("defaultPointerAccelerationProfileAdaptive"); Prop m_pointerAccelerationProfileAdaptive = Prop("pointerAccelerationProfileAdaptive"); // // scrolling Prop m_supportsNaturalScroll = Prop("supportsNaturalScroll"); Prop m_naturalScrollEnabledByDefault = Prop("naturalScrollEnabledByDefault"); Prop m_naturalScroll = Prop("naturalScroll"); + Prop m_scrollFactor = Prop("scrollFactor"); QDBusInterface *m_iface; }; #endif // KWINWAYLANDDEVICE_H diff --git a/kcms/mouse/kcm/libinput/main.qml b/kcms/mouse/kcm/libinput/main.qml index 08652a664..e8118d374 100644 --- a/kcms/mouse/kcm/libinput/main.qml +++ b/kcms/mouse/kcm/libinput/main.qml @@ -1,301 +1,364 @@ /* * Copyright 2018 Roman Gilg * Copyright 2018 Furkan Tokac * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.7 import QtQuick.Controls 2.0 as Controls import QtQuick.Layouts 1.3 as Layouts import org.kde.kcm 1.1 as KCM import org.kde.kirigami 2.4 as Kirigami import "components" // TODO: Change ScrollablePage as KCM.SimpleKCM // after rewrite the KCM in KConfigModule. Kirigami.ScrollablePage { id: root spacing: Kirigami.Units.smallSpacing property size sizeHint: Qt.size(formLayout.width, Math.round(1.3 * formLayout.height)) property size minimumSizeHint: Qt.size(formLayout.width/2, Math.round(1.3 * formLayout.height)) property alias deviceIndex: deviceSelector.currentIndex signal changeSignal() property QtObject device property int deviceCount: backend.deviceCount property bool loading: false + enabled: deviceCount > 0 + function resetModel(index) { deviceCount = backend.deviceCount formLayout.enabled = deviceCount deviceSelector.enabled = deviceCount > 1 loading = true if (deviceCount) { device = deviceModel[index] deviceSelector.model = deviceModel deviceSelector.currentIndex = index console.log("Configuration of device '" + (index + 1) + " : " + device.name + "' opened") } else { deviceSelector.model = [""] console.log("No device found") } loading = false } function syncValuesFromBackend() { loading = true deviceEnabled.load() leftHanded.load() middleEmulation.load() accelSpeed.load() accelProfile.load() naturalScroll.load() + scrollFactor.load() loading = false } Kirigami.FormLayout { id: formLayout enabled: deviceCount // Device Controls.ComboBox { Kirigami.FormData.label: i18nd("kcmmouse", "Device:") id: deviceSelector enabled: deviceCount > 1 Layouts.Layout.fillWidth: true model: deviceModel textRole: "name" onCurrentIndexChanged: { if (deviceCount) { device = deviceModel[currentIndex] if (!loading) { changeSignal() } console.log("Configuration of device '" + (currentIndex+1) + " : " + device.name + "' opened") } root.syncValuesFromBackend() } } Item { Kirigami.FormData.isSection: false } // General Controls.CheckBox { Kirigami.FormData.label: i18nd("kcmmouse", "General:") id: deviceEnabled text: i18nd("kcmmouse", "Device enabled") function load() { if (!formLayout.enabled) { checked = false return } enabled = device.supportsDisableEvents checked = enabled && device.enabled } onCheckedChanged: { if (enabled && !root.loading) { device.enabled = checked root.changeSignal() } } ToolTip { text: i18nd("kcmmouse", "Accept input through this device.") } } Controls.CheckBox { id: leftHanded text: i18nd("kcmmouse", "Left handed mode") function load() { if (!formLayout.enabled) { checked = false return } enabled = device.supportsLeftHanded checked = enabled && device.leftHanded } onCheckedChanged: { if (enabled && !root.loading) { device.leftHanded = checked root.changeSignal() } } ToolTip { text: i18nd("kcmmouse", "Swap left and right buttons.") } } Controls.CheckBox { id: middleEmulation text: i18nd("kcmmouse", "Press left and right buttons for middle-click") function load() { if (!formLayout.enabled) { checked = false return } enabled = device.supportsMiddleEmulation checked = enabled && device.middleEmulation } onCheckedChanged: { if (enabled && !root.loading) { device.middleEmulation = checked root.changeSignal() } } ToolTip { text: i18nd("kcmmouse", "Clicking left and right button simultaneously sends middle button click.") } } Item { Kirigami.FormData.isSection: false } // Acceleration Controls.Slider { Kirigami.FormData.label: i18nd("kcmmouse", "Pointer speed:") id: accelSpeed from: 1 to: 11 stepSize: 1 function load() { enabled = device.supportsPointerAcceleration if (!enabled) { value = 0.2 return } // transform libinput's pointer acceleration range [-1, 1] to slider range [1, 11] //value = 4.5 * device.pointerAcceleration + 5.5 value = 6 + device.pointerAcceleration / 0.2 } onValueChanged: { if (device != undefined && enabled && !root.loading) { // transform slider range [1, 10] to libinput's pointer acceleration range [-1, 1] // by *10 and /10, we ignore the floating points after 1 digit. This prevents from // having a libinput value like 0.60000001 device.pointerAcceleration = Math.round(((value-6) * 0.2) * 10) / 10 root.changeSignal() } } } Layouts.ColumnLayout { id: accelProfile spacing: Kirigami.Units.smallSpacing Kirigami.FormData.label: i18nd("kcmmouse", "Acceleration profile:") Kirigami.FormData.buddyFor: accelProfileFlat function load() { enabled = device.supportsPointerAccelerationProfileAdaptive if (!enabled) { accelProfileAdaptive.checked = false accelProfileFlat.checked = false return } if(device.pointerAccelerationProfileAdaptive) { accelProfileAdaptive.checked = true accelProfileFlat.checked = false } else { accelProfileAdaptive.checked = false accelProfileFlat.checked = true } } function syncCurrent() { if (enabled && !root.loading) { device.pointerAccelerationProfileFlat = accelProfileFlat.checked device.pointerAccelerationProfileAdaptive = accelProfileAdaptive.checked root.changeSignal() } } Controls.RadioButton { id: accelProfileFlat text: i18nd("kcmmouse", "Flat") ToolTip { text: i18nd("kcmmouse", "Cursor moves the same distance as the mouse movement.") } onCheckedChanged: accelProfile.syncCurrent() } Controls.RadioButton { id: accelProfileAdaptive text: i18nd("kcmmouse", "Adaptive") ToolTip { text: i18nd("kcmmouse", "Cursor travel distance depends on the mouse movement speed.") } onCheckedChanged: accelProfile.syncCurrent() } } Item { Kirigami.FormData.isSection: false } // Scrolling Controls.CheckBox { Kirigami.FormData.label: i18nd("kcmmouse", "Scrolling:") id: naturalScroll text: i18nd("kcmmouse", "Invert scroll direction") function load() { enabled = device.supportsNaturalScroll checked = enabled && device.naturalScroll } onCheckedChanged: { if (enabled && !root.loading) { device.naturalScroll = checked root.changeSignal() } } ToolTip { text: i18nd("kcmmouse", "Touchscreen like scrolling.") } } + + // Scroll Speed aka scroll Factor + Layouts.GridLayout { + Kirigami.FormData.label: i18nd("kcm_touchpad", "Scrolling speed:") + Kirigami.FormData.buddyFor: scrollFactor + + columns: 3 + + Controls.Slider { + id: scrollFactor + + from: 0 + to: 14 + stepSize: 1 + + property variant values : [ + 0.1, + 0.3, + 0.5, + 0.75, + 1, // default + 1.5, + 2, + 3, + 4, + 5, + 7, + 9, + 12, + 15, + 20 + ] + + Layouts.Layout.columnSpan: 3 + + function load() { + let index = values.indexOf(device.scrollFactor) + if (index === -1) { + index = values.indexOf(1); + } + value = index + } + + onMoved: { + device.scrollFactor = values[value] + root.changeSignal() + } + } + + //row 2 + Controls.Label { + text: i18nc("Slower Scroll", "Slower") + } + Item { + Layouts.Layout.fillWidth: true + } + Controls.Label { + text: i18nc("Faster Scroll Speed", "Faster") + } + } } } diff --git a/kcms/touchpad/src/backends/kwin_wayland/kwinwaylandtouchpad.cpp b/kcms/touchpad/src/backends/kwin_wayland/kwinwaylandtouchpad.cpp index 795a775bf..3391ee4c6 100644 --- a/kcms/touchpad/src/backends/kwin_wayland/kwinwaylandtouchpad.cpp +++ b/kcms/touchpad/src/backends/kwin_wayland/kwinwaylandtouchpad.cpp @@ -1,243 +1,248 @@ /* * Copyright 2017 Roman Gilg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kwinwaylandtouchpad.h" #include #include #include #include #include "logging.h" KWinWaylandTouchpad::KWinWaylandTouchpad(QString dbusName) : LibinputCommon() { m_iface = new QDBusInterface(QStringLiteral("org.kde.KWin"), QStringLiteral("/org/kde/KWin/InputDevice/") + dbusName, QStringLiteral("org.kde.KWin.InputDevice"), QDBusConnection::sessionBus(), this); } KWinWaylandTouchpad::~KWinWaylandTouchpad() { delete m_iface; } bool KWinWaylandTouchpad::init() { // need to do it here in order to populate combobox and handle events return valueLoader(m_name) && valueLoader(m_sysName); } bool KWinWaylandTouchpad::getConfig() { bool success = true; // general success &= valueLoader(m_supportsDisableEvents); success &= valueLoader(m_supportsLeftHanded); success &= valueLoader(m_supportedButtons); success &= valueLoader(m_leftHandedEnabledByDefault); success &= valueLoader(m_enabled); success &= valueLoader(m_leftHanded); // advanced success &= valueLoader(m_supportsPointerAcceleration); success &= valueLoader(m_supportsPointerAccelerationProfileFlat); success &= valueLoader(m_supportsPointerAccelerationProfileAdaptive); success &= valueLoader(m_supportsDisableWhileTyping); success &= valueLoader(m_supportsDisableEventsOnExternalMouse); success &= valueLoader(m_defaultPointerAcceleration); success &= valueLoader(m_defaultPointerAccelerationProfileFlat); success &= valueLoader(m_defaultPointerAccelerationProfileAdaptive); success &= valueLoader(m_disableWhileTypingEnabledByDefault); success &= valueLoader(m_leftHandedEnabledByDefault); success &= valueLoader(m_pointerAcceleration); success &= valueLoader(m_pointerAccelerationProfileFlat); success &= valueLoader(m_pointerAccelerationProfileAdaptive); success &= valueLoader(m_disableWhileTyping); // tapping success &= valueLoader(m_tapFingerCount); success &= valueLoader(m_supportsMiddleEmulation); success &= valueLoader(m_tapToClickEnabledByDefault); success &= valueLoader(m_tapAndDragEnabledByDefault); success &= valueLoader(m_tapDragLockEnabledByDefault); success &= valueLoader(m_middleEmulationEnabledByDefault); success &= valueLoader(m_tapToClick); success &= valueLoader(m_tapAndDrag); success &= valueLoader(m_tapDragLock); success &= valueLoader(m_middleEmulation); success &= valueLoader(m_lmrTapButtonMapEnabledByDefault); success &= valueLoader(m_lmrTapButtonMap); // scrolling modes avail success &= valueLoader(m_supportsNaturalScroll); success &= valueLoader(m_supportsScrollTwoFinger); success &= valueLoader(m_supportsScrollEdge); success &= valueLoader(m_supportsScrollOnButtonDown); // default scrolling modes success &= valueLoader(m_naturalScrollEnabledByDefault); success &= valueLoader(m_scrollTwoFingerEnabledByDefault); success &= valueLoader(m_scrollEdgeEnabledByDefault); success &= valueLoader(m_scrollOnButtonDownEnabledByDefault); success &= valueLoader(m_defaultScrollButton); // current scrolling mode success &= valueLoader(m_naturalScroll); success &= valueLoader(m_isScrollTwoFinger); success &= valueLoader(m_isScrollEdge); success &= valueLoader(m_isScrollOnButtonDown); success &= valueLoader(m_scrollButton); + // scroll speed + success &= valueLoader(m_scrollFactor); // click methods success &= valueLoader(m_supportsClickMethodAreas); success &= valueLoader(m_supportsClickMethodClickfinger); success &= valueLoader(m_defaultClickMethodAreas); success &= valueLoader(m_defaultClickMethodClickfinger); success &= valueLoader(m_clickMethodAreas); success &= valueLoader(m_clickMethodClickfinger); return success; } bool KWinWaylandTouchpad::getDefaultConfig() { m_enabled.set(true); m_leftHanded.set(false); m_pointerAcceleration.set(m_defaultPointerAcceleration); m_pointerAccelerationProfileFlat.set(m_defaultPointerAccelerationProfileFlat); m_pointerAccelerationProfileAdaptive.set(m_defaultPointerAccelerationProfileAdaptive); m_disableWhileTyping.set(m_disableWhileTypingEnabledByDefault); m_tapToClick.set(m_tapToClickEnabledByDefault); m_tapAndDrag.set(m_tapAndDragEnabledByDefault); m_tapDragLock.set(m_tapDragLockEnabledByDefault); m_middleEmulation.set(m_middleEmulationEnabledByDefault); m_naturalScroll.set(m_naturalScrollEnabledByDefault); m_isScrollTwoFinger.set(m_scrollTwoFingerEnabledByDefault); m_isScrollEdge.set(m_scrollEdgeEnabledByDefault); m_isScrollOnButtonDown.set(m_scrollOnButtonDownEnabledByDefault); m_clickMethodAreas.set(m_defaultClickMethodAreas); m_clickMethodClickfinger.set(m_defaultClickMethodClickfinger); + m_scrollFactor.set(1.0); return true; } bool KWinWaylandTouchpad::applyConfig() { QVector msgs; msgs << valueWriter(m_enabled) << valueWriter(m_leftHanded) << valueWriter(m_pointerAcceleration) << valueWriter(m_defaultPointerAccelerationProfileFlat) << valueWriter(m_defaultPointerAccelerationProfileAdaptive) << valueWriter(m_disableWhileTyping) << valueWriter(m_middleEmulation) << valueWriter(m_tapToClick) << valueWriter(m_tapAndDrag) << valueWriter(m_tapDragLock) << valueWriter(m_lmrTapButtonMap) << valueWriter(m_naturalScroll) << valueWriter(m_isScrollTwoFinger) << valueWriter(m_isScrollEdge) << valueWriter(m_isScrollOnButtonDown) << valueWriter(m_scrollButton) + << valueWriter(m_scrollFactor) << valueWriter(m_clickMethodAreas) << valueWriter(m_clickMethodClickfinger); bool success = true; QString error_msg; for (QString m : msgs) { if (!m.isNull()) { qCCritical(KCM_TOUCHPAD) << "in error:" << m; if (!success) { error_msg.append("\n"); } error_msg.append(m); success = false; } } if (!success) { qCCritical(KCM_TOUCHPAD) << error_msg; } return success; } bool KWinWaylandTouchpad::isChangedConfig() const { return m_enabled.changed() || m_leftHanded.changed() || m_pointerAcceleration.changed() || m_pointerAccelerationProfileFlat.changed() || m_pointerAccelerationProfileAdaptive.changed() || m_disableWhileTyping.changed() || m_middleEmulation.changed() || m_tapToClick.changed() || m_tapAndDrag.changed() || m_tapDragLock.changed() || m_lmrTapButtonMap.changed() || m_naturalScroll.changed() || m_isScrollTwoFinger.changed() || m_isScrollEdge.changed() || m_isScrollOnButtonDown.changed() || + m_scrollFactor.changed() || m_scrollButton.changed() || m_clickMethodAreas.changed() || m_clickMethodClickfinger.changed(); } template QString KWinWaylandTouchpad::valueWriter(const Prop &prop) { if (!prop.changed()) { return QString(); } m_iface->setProperty(prop.name, prop.val); QDBusError error = m_iface->lastError(); if (error.isValid()) { qCCritical(KCM_TOUCHPAD) << error.message(); return error.message(); } return QString(); } template bool KWinWaylandTouchpad::valueLoader(Prop &prop) { QVariant reply = m_iface->property(prop.name); if (!reply.isValid()) { qCCritical(KCM_TOUCHPAD) << "Error on d-bus read of" << prop.name; prop.avail = false; return false; } prop.avail = true; T replyValue = valueLoaderPart(reply); prop.old = replyValue; prop.val = replyValue; return true; } diff --git a/kcms/touchpad/src/backends/kwin_wayland/kwinwaylandtouchpad.h b/kcms/touchpad/src/backends/kwin_wayland/kwinwaylandtouchpad.h index 9f6197ced..ff3eb591e 100644 --- a/kcms/touchpad/src/backends/kwin_wayland/kwinwaylandtouchpad.h +++ b/kcms/touchpad/src/backends/kwin_wayland/kwinwaylandtouchpad.h @@ -1,144 +1,162 @@ /* * Copyright 2017 Roman Gilg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KWINWAYLANDTOUCHPAD_H #define KWINWAYLANDTOUCHPAD_H #include #include class QDBusInterface; class KWinWaylandTouchpad : public LibinputCommon { Q_OBJECT + Q_PROPERTY(qreal scrollFactor READ scrollFactor WRITE setScrollFactor NOTIFY scrollFactorChanged) + public: KWinWaylandTouchpad(QString dbusName); ~KWinWaylandTouchpad() override; bool init(); bool getConfig(); bool getDefaultConfig(); bool applyConfig(); bool isChangedConfig() const; // // general QString name() const override { return m_name.val; } QString sysName() const { return m_sysName.val; } bool supportsDisableEvents() const override { return m_supportsDisableEvents.val; } void setEnabled(bool enabled) override { m_enabled.set(enabled); } bool isEnabled() const override { return m_enabled.val; } // // advanced bool supportsLeftHanded() const override { return m_supportsLeftHanded.val; } bool supportsDisableEventsOnExternalMouse() const override { return m_supportsDisableEventsOnExternalMouse.val; } bool supportsDisableWhileTyping() const override { return m_supportsDisableWhileTyping.val; } bool supportsMiddleEmulation() const override { return m_supportsMiddleEmulation.val; } // // tapping void setLmrTapButtonMap(bool set) override { m_lmrTapButtonMap.set(set); } // // acceleration speed and profile bool supportsPointerAcceleration() const override { return m_supportsPointerAcceleration.val; } bool supportsPointerAccelerationProfileFlat() const override { return m_supportsPointerAccelerationProfileFlat.val; } bool supportsPointerAccelerationProfileAdaptive() const override { return m_supportsPointerAccelerationProfileAdaptive.val; } // // scrolling bool supportsNaturalScroll() const override { return m_supportsNaturalScroll.val; } bool supportsHorizontalScrolling() const override { return false; } bool supportsScrollTwoFinger() const override { return m_supportsScrollTwoFinger.val; } bool supportsScrollEdge() const override { return m_supportsScrollEdge.val; } bool supportsScrollOnButtonDown() const override { return m_supportsScrollOnButtonDown.val; } + + // + // Scroll Factor + qreal scrollFactor() const + { + return m_scrollFactor.val; + } + void setScrollFactor(qreal factor) + { + return m_scrollFactor.set(factor); + } + // // Click method bool supportsClickMethodAreas() const override { return m_supportsClickMethodAreas.val; } bool supportsClickMethodClickfinger() const override { return m_supportsClickMethodClickfinger.val; } +Q_SIGNALS: + void scrollFactorChanged(); + private: template bool valueLoader(Prop &prop); template QString valueWriter(const Prop &prop); // // general Prop m_name = Prop("name"); Prop m_sysName = Prop("sysName"); // // advanced Prop m_supportsLeftHanded = Prop("supportsLeftHanded"); Prop m_supportsDisableWhileTyping = Prop("supportsDisableWhileTyping"); Prop m_supportsMiddleEmulation = Prop("supportsMiddleEmulation"); // // acceleration speed and profile Prop m_supportsPointerAcceleration = Prop("supportsPointerAcceleration"); // // scrolling Prop m_supportsNaturalScroll = Prop("supportsNaturalScroll"); + Prop m_scrollFactor = Prop("scrollFactor"); QDBusInterface *m_iface; }; #endif diff --git a/kcms/touchpad/src/kcm/libinput/touchpad.qml b/kcms/touchpad/src/kcm/libinput/touchpad.qml index b48070668..ef82d0190 100644 --- a/kcms/touchpad/src/kcm/libinput/touchpad.qml +++ b/kcms/touchpad/src/kcm/libinput/touchpad.qml @@ -1,744 +1,807 @@ /* * Copyright 2017 Roman Gilg * Copyright 2018 Furkan Tokac * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.7 import QtQuick.Controls 2.0 as Controls import QtQuick.Layouts 1.3 as Layouts import org.kde.kcm 1.1 as KCM import org.kde.kirigami 2.4 as Kirigami // TODO: Change ScrollablePage as KCM.SimpleKCM // after rewrite the KCM in KConfigModule. Kirigami.ScrollablePage { id: root spacing: Kirigami.Units.smallSpacing property size minimumSizeHint: Qt.size(formLayout.width/2, deviceSelector.height) property alias deviceIndex: deviceSelector.currentIndex signal changeSignal() property QtObject touchpad property int touchpadCount: backend.touchpadCount property bool loading: false + enabled: touchpadCount > 0 + function resetModel(index) { touchpadCount = backend.touchpadCount formLayout.enabled = touchpadCount deviceSelector.enabled = touchpadCount > 1 loading = true if (touchpadCount) { touchpad = deviceModel[index] deviceSelector.model = deviceModel deviceSelector.currentIndex = index console.log("Touchpad configuration of device '" + (index + 1) + " : " + touchpad.name + "' opened") } else { deviceSelector.model = [""] console.log("No touchpad found") } loading = false } function syncValuesFromBackend() { loading = true deviceEnabled.load() dwt.load() leftHanded.load() middleEmulation.load() accelSpeed.load() accelProfile.load() tapToClick.load() tapAndDrag.load() tapAndDragLock.load() multiTap.load() scrollMethod.load() naturalScroll.load() + scrollFactor.load() rightClickMethod.load() middleClickMethod.load() disableHorizontalScrolling.load() loading = false } Kirigami.FormLayout { id: formLayout // Device Controls.ComboBox { Kirigami.FormData.label: i18nd("kcm_touchpad", "Device:") id: deviceSelector enabled: touchpadCount > 1 Layouts.Layout.fillWidth: true model: deviceModel textRole: "name" onCurrentIndexChanged: { if (touchpadCount) { touchpad = deviceModel[currentIndex] if (!loading) { changeSignal() } console.log("Touchpad configuration of device '" + (currentIndex+1) + " : " + touchpad.name + "' opened") } root.syncValuesFromBackend() } } Item { Kirigami.FormData.isSection: false } // General settings Controls.CheckBox { Kirigami.FormData.label: i18nd("kcm_touchpad", "General:") id: deviceEnabled text: i18nd("kcm_touchpad", "Device enabled") hoverEnabled: true Controls.ToolTip { text: i18nd("kcm_touchpad", "Accept input through this device.") visible: parent.hovered delay: 1000 } function load() { if (!formLayout.enabled) { checked = false return } enabled = touchpad.supportsDisableEvents checked = enabled && touchpad.enabled } onCheckedChanged: { if (enabled && !root.loading) { touchpad.enabled = checked root.changeSignal() } } } Controls.CheckBox { id: dwt text: i18nd("kcm_touchpad", "Disable while typing") hoverEnabled: true Controls.ToolTip { text: i18nd("kcm_touchpad", "Disable touchpad while typing to prevent accidental inputs.") visible: parent.hovered delay: 1000 } function load() { if (!formLayout.enabled) { checked = false return } enabled = touchpad.supportsDisableWhileTyping checked = enabled && touchpad.disableWhileTyping } onCheckedChanged: { if (enabled && !root.loading) { touchpad.disableWhileTyping = checked root.changeSignal() } } } Controls.CheckBox { id: leftHanded text: i18nd("kcm_touchpad", "Left handed mode") hoverEnabled: true Controls.ToolTip { text: i18nd("kcm_touchpad", "Swap left and right buttons.") visible: parent.hovered delay: 1000 } function load() { if (!formLayout.enabled) { checked = false return } enabled = touchpad.supportsLeftHanded checked = enabled && touchpad.leftHanded } onCheckedChanged: { if (enabled && !root.loading) { touchpad.leftHanded = checked root.changeSignal() } } } Controls.CheckBox { id: middleEmulation text: i18nd("kcm_touchpad", "Press left and right buttons for middle click") hoverEnabled: true Controls.ToolTip { text: i18nd("kcm_touchpad", "Clicking left and right button simultaneously sends middle button click.") visible: parent.hovered delay: 1000 } function load() { if (!formLayout.enabled) { checked = false return } enabled = touchpad.supportsMiddleEmulation checked = enabled && touchpad.middleEmulation } onCheckedChanged: { if (enabled && !root.loading) { touchpad.middleEmulation = checked root.changeSignal() } loading = true middleClickMethod.load() loading = false } } Item { Kirigami.FormData.isSection: false } // Acceleration Controls.Slider { Kirigami.FormData.label: i18nd("kcm_touchpad", "Pointer speed:") id: accelSpeed from: 1 to: 11 stepSize: 1 function load() { enabled = touchpad.supportsPointerAcceleration if (!enabled) { value = 0.1 return } // transform libinput's pointer acceleration range [-1, 1] to slider range [1, 11] value = 6 + touchpad.pointerAcceleration / 0.2 } onValueChanged: { if (touchpad != undefined && enabled && !root.loading) { // transform slider range [1, 11] to libinput's pointer acceleration range [-1, 1] // by *10 and /10, we ignore the floating points after 1 digit. This prevents from // having a libinput value like 0.60000001 touchpad.pointerAcceleration = Math.round(((value-6) * 0.2) * 10) / 10 root.changeSignal() } } } Layouts.ColumnLayout { Kirigami.FormData.label: i18nd("kcm_touchpad", "Acceleration profile:") Kirigami.FormData.buddyFor: accelProfileFlat id: accelProfile spacing: Kirigami.Units.smallSpacing function load() { enabled = touchpad.supportsPointerAccelerationProfileAdaptive if (!enabled) { accelProfile.visible = false accelProfileFlat.checked = false accelProfileAdaptive.checked = false return } if(touchpad.pointerAccelerationProfileAdaptive) { accelProfileAdaptive.checked = true } else { accelProfileFlat.checked = true } } function syncCurrent() { if (enabled && !root.loading) { touchpad.pointerAccelerationProfileFlat = accelProfileFlat.checked touchpad.pointerAccelerationProfileAdaptive = accelProfileAdaptive.checked root.changeSignal() } } Controls.RadioButton { id: accelProfileFlat text: i18nd("kcm_touchpad", "Flat") hoverEnabled: true Controls.ToolTip { text: i18nd("kcm_touchpad", "Cursor moves the same distance as finger.") visible: parent.hovered delay: 1000 } onCheckedChanged: accelProfile.syncCurrent() } Controls.RadioButton { id: accelProfileAdaptive text: i18nd("kcm_touchpad", "Adaptive") hoverEnabled: true Controls.ToolTip { text: i18nd("kcm_touchpad", "Cursor travel distance depends on movement speed of finger.") visible: parent.hovered delay: 1000 } onCheckedChanged: accelProfile.syncCurrent() } } Item { Kirigami.FormData.isSection: false } // Tapping Controls.CheckBox { Kirigami.FormData.label: i18nd("kcm_touchpad", "Tapping:") id: tapToClick text: i18nd("kcm_touchpad", "Tap-to-click") hoverEnabled: true Controls.ToolTip { text: i18nd("kcm_touchpad", "Single tap is left button click.") visible: parent.hovered delay: 1000 } function load() { enabled = touchpad.tapFingerCount > 0 checked = enabled && touchpad.tapToClick } function updateDependents() { loading = true tapAndDrag.load() tapAndDragLock.load() multiTap.load() loading = false } onCheckedChanged: { if (enabled && !root.loading) { touchpad.tapToClick = checked updateDependents() root.changeSignal() } } } Controls.CheckBox { id: tapAndDrag text: i18nd("kcm_touchpad", "Tap-and-drag") hoverEnabled: true Controls.ToolTip { text: i18nd("kcm_touchpad", "Sliding over touchpad directly after tap drags.") visible: parent.hovered delay: 1000 } function load() { enabled = touchpad.tapFingerCount > 0 && tapToClick.checked checked = enabled && touchpad.tapAndDrag } function updateDependents() { loading = true tapAndDragLock.load() loading = false } onCheckedChanged: { if (enabled && !root.loading) { touchpad.tapAndDrag = checked updateDependents() root.changeSignal() } } } Controls.CheckBox { id: tapAndDragLock text: i18nd("kcm_touchpad", "Tap-and-drag lock") hoverEnabled: true Controls.ToolTip { text: i18nd("kcm_touchpad", "Dragging continues after a short finger lift.") visible: parent.hovered delay: 1000 } function load() { enabled = touchpad.tapFingerCount > 0 && tapAndDrag.checked checked = enabled && touchpad.tapDragLock } onCheckedChanged: { if (enabled && !root.loading) { touchpad.tapDragLock = checked root.changeSignal() } } } Layouts.ColumnLayout { Kirigami.FormData.label: i18nd("kcm_touchpad", "Two-finger tap:") Kirigami.FormData.buddyFor: multiTapRightClick id: multiTap spacing: Kirigami.Units.smallSpacing function load() { enabled = touchpad.supportsLmrTapButtonMap && tapToClick.checked if (touchpad.tapFingerCount > 2) { multiTapRightClick.text = i18nd("kcm_touchpad", "Right-click (three-finger tap to middle-click)") multiTapRightClickToolTip.text = i18nd("kcm_touchpad", "Tap with two fingers to right-click, tap with three fingers to middle-click.") multiTapMiddleClick.text = i18nd("kcm_touchpad", "Middle-click (three-finger tap right-click)") multiTapMiddleClickToolTip.text = i18nd("kcm_touchpad", "Tap with two fingers to middle-click, tap with three fingers to right-click.") } else { multiTapRightClick.text = i18nd("kcm_touchpad", "Right-click") multiTapRightClickToolTip.text = i18nd("kcm_touchpad", "Tap with two fingers to right-click.") multiTapMiddleClick.text = i18nd("kcm_touchpad", "Middle-click") multiTapMiddleClickToolTip.text = i18nd("kcm_touchpad", "Tap with two fingers to middle-click.") } if (!enabled) { multiTapRightClick.checked = false multiTapMiddleClick.checked = false return } if(touchpad.lmrTapButtonMap) { multiTapMiddleClick.checked = true } else { multiTapRightClick.checked = true } } function syncCurrent() { if (enabled && !root.loading) { touchpad.lmrTapButtonMap = multiTapMiddleClick.checked root.changeSignal() } } Controls.RadioButton { id: multiTapRightClick // text: is handled dynamically on load. hoverEnabled: true Controls.ToolTip { id: multiTapRightClickToolTip visible: parent.hovered delay: 1000 // text: is handled dynamically on load. } onCheckedChanged: multiTap.syncCurrent() } Controls.RadioButton { id: multiTapMiddleClick // text: is handled dynamically on load. hoverEnabled: true Controls.ToolTip { id: multiTapMiddleClickToolTip visible: parent.hovered delay: 1000 // text: is handled dynamically on load. } onCheckedChanged: multiTap.syncCurrent() } } Item { Kirigami.FormData.isSection: false } // Scrolling Layouts.ColumnLayout { Kirigami.FormData.label: i18nd("kcm_touchpad", "Scrolling:") Kirigami.FormData.buddyFor: scrollMethodTwoFingers id: scrollMethod spacing: Kirigami.Units.smallSpacing function load() { scrollMethodTwoFingers.enabled = touchpad.supportsScrollTwoFinger scrollMethodTouchpadEdges.enabled = touchpad.supportsScrollEdge if(scrollMethodTouchpadEdges.enabled && touchpad.scrollEdge) { scrollMethodTouchpadEdges.checked = formLayout.enabled } else { scrollMethodTwoFingers.checked = formLayout.enabled } } function syncCurrent() { if (enabled && !root.loading) { touchpad.scrollTwoFinger = scrollMethodTwoFingers.checked touchpad.scrollEdge = scrollMethodTouchpadEdges.checked root.changeSignal() } loading = true naturalScroll.load() loading = false } Controls.RadioButton { id: scrollMethodTwoFingers text: i18nd("kcm_touchpad", "Two fingers") hoverEnabled: true Controls.ToolTip { text: i18nd("kcm_touchpad", "Slide with two fingers scrolls.") visible: parent.hovered delay: 1000 } } Controls.RadioButton { id: scrollMethodTouchpadEdges text: i18nd("kcm_touchpad", "Touchpad edges") hoverEnabled: true Controls.ToolTip { text: i18nd("kcm_touchpad", "Slide on the touchpad edges scrolls.") visible: parent.hovered delay: 1000 } onCheckedChanged: scrollMethod.syncCurrent() } } Controls.CheckBox { id: naturalScroll text: i18nd("kcm_touchpad", "Invert scroll direction (Natural scrolling)") function load() { enabled = touchpad.supportsNaturalScroll checked = enabled && touchpad.naturalScroll } onCheckedChanged: { if (enabled && !root.loading) { touchpad.naturalScroll = checked root.changeSignal() } } hoverEnabled: true Controls.ToolTip { text: i18nd("kcm_touchpad", "Touchscreen like scrolling.") visible: parent.hovered delay: 1000 } } Controls.CheckBox { id: disableHorizontalScrolling text: i18nd("kcm_touchpad", "Disable horizontal scrolling") function load() { visible = touchpad.supportsHorizontalScrolling enabled = touchpad.supportsHorizontalScrolling checked = enabled && !touchpad.horizontalScrolling } onCheckedChanged: { if (enabled && !root.loading) { touchpad.horizontalScrolling = !checked root.changeSignal() } } hoverEnabled: true Controls.ToolTip { text: i18nd("kcm_touchpad", "Disable horizontal scrolling") visible: parent.hovered delay: 1000 } } + // Scroll Speed aka scroll Factor + Layouts.GridLayout { + Kirigami.FormData.label: i18nd("kcm_touchpad", "Scrolling speed:") + Kirigami.FormData.buddyFor: scrollFactor + + columns: 3 + + Controls.Slider { + id: scrollFactor + + from: 0 + to: 14 + stepSize: 1 + + property variant values : [ + 0.1, + 0.3, + 0.5, + 0.75, + 1, // default + 1.5, + 2, + 3, + 4, + 5, + 7, + 9, + 12, + 15, + 20 + ] + + Layouts.Layout.columnSpan: 3 + + function load() { + let index = values.indexOf(touchpad.scrollFactor) + if (index === -1) { + index = values.indexOf(1); + } + value = index + } + + onMoved: { + touchpad.scrollFactor = values[value] + root.changeSignal() + } + } + + //row 2 + Controls.Label { + text: i18nc("Slower Scroll", "Slower") + } + Item { + Layouts.Layout.fillWidth: true + } + Controls.Label { + text: i18nc("Faster Scroll Speed", "Faster") + } + } + Item { Kirigami.FormData.isSection: false } Layouts.ColumnLayout { Kirigami.FormData.label: i18nd("kcm_touchpad", "Right-click:") Kirigami.FormData.buddyFor: rightClickMethodAreas id: rightClickMethod enabled: touchpad.supportsClickMethodAreas && touchpad.supportsClickMethodClickfinger spacing: Kirigami.Units.smallSpacing function load() { visible = (touchpad.supportedButtons & Qt.LeftButton) if (!visible) { rightClickMethodAreas.checked = false rightClickMethodClickfinger.checked = false return; } rightClickMethodAreas.enabled = touchpad.supportsClickMethodAreas rightClickMethodClickfinger.enabled = touchpad.supportsClickMethodClickfinger if (rightClickMethodAreas.enabled && touchpad.clickMethodAreas) { rightClickMethodAreas.checked = true } else if (rightClickMethodClickfinger.enabled && touchpad.clickMethodClickfinger) { rightClickMethodClickfinger.checked = true } } function syncCurrent() { if (enabled && !root.loading) { touchpad.clickMethodAreas = rightClickMethodAreas.checked touchpad.clickMethodClickfinger = rightClickMethodClickfinger.checked root.changeSignal() } loading = true middleClickMethod.load() loading = false } Controls.RadioButton { id: rightClickMethodAreas text: i18nd("kcm_touchpad", "Press bottom-right corner") hoverEnabled: true Controls.ToolTip { text: i18nd("kcm_touchpad", "Software enabled buttons will be added to bottom portion of your touchpad.") visible: parent.hovered delay: 1000 } } Controls.RadioButton { id: rightClickMethodClickfinger text: i18nd("kcm_touchpad", "Press anywhere with two fingers") hoverEnabled: true Controls.ToolTip { text: i18nd("kcm_touchpad", "Tap with two finger to enable right click.") visible: parent.hovered delay: 1000 } onCheckedChanged: rightClickMethod.syncCurrent() } } Item { Kirigami.FormData.isSection: false } Layouts.ColumnLayout { Kirigami.FormData.label: i18nd("kcm_touchpad", "Middle-click: ") Kirigami.FormData.buddyFor: middleSoftwareEmulation id: middleClickMethod spacing: Kirigami.Units.smallSpacing function load() { visible = rightClickMethod.visible if (!visible) { enabled = false return; } enabled = touchpad.supportsMiddleEmulation if (enabled && touchpad.middleEmulation) { middleSoftwareEmulation.checked = true } else { noMiddleSoftwareEmulation.checked = true } } function syncCurrent() { if (enabled && !root.loading) { touchpad.middleEmulation = middleSoftwareEmulation.checked root.changeSignal() } loading = true middleEmulation.load() loading = false } Controls.RadioButton { id: noMiddleSoftwareEmulation text: i18nd("kcm_touchpad", "Press bottom-middle") visible: rightClickMethodAreas.checked hoverEnabled: true Controls.ToolTip { text: i18nd("kcm_touchpad", "Software enabled middle-button will be added to bottom portion of your touchpad.") visible: parent.hovered delay: 1000 } } Controls.RadioButton { id: middleSoftwareEmulation text: i18nd("kcm_touchpad", "Press bottom left and bottom right corners simultaneously") visible: rightClickMethodAreas.checked hoverEnabled: true Controls.ToolTip { text: i18nd("kcm_touchpad", "Clicking left and right button simultaneously sends middle button click.") visible: parent.hovered delay: 1000 } onCheckedChanged: middleClickMethod.syncCurrent() } Controls.CheckBox { id: clickfingerMiddleInfoBox text: i18nd("kcm_touchpad", "Press anywhere with three fingers") checked: true enabled: false visible: rightClickMethodClickfinger.checked hoverEnabled: true Controls.ToolTip { text: i18nd("kcm_touchpad", "Press anywhere with three fingers.") visible: parent.hovered delay: 1000 } } } } // END Kirigami.FormLayout } // END Kirigami.ScrollablePage