From 9a575ad42eaf98b1e35e28296f7b7d01ce6456f3 Mon Sep 17 00:00:00 2001 From: Devin Lin Date: Thu, 5 May 2022 21:02:18 -0400 Subject: [PATCH] kcm: Add vibration intensity and duration --- .../mobileshell/mobileshellsettings.cpp | 28 ++++ components/mobileshell/mobileshellsettings.h | 86 ++++++++++ .../mobileshell/qml/components/Haptics.qml | 4 +- .../package/contents/ui/VibrationForm.qml | 150 ++++++++++++++++++ kcms/mobileshell/package/contents/ui/main.qml | 19 ++- .../ui/mobileform/FormComboBoxDelegate.qml | 20 +++ 6 files changed, 297 insertions(+), 10 deletions(-) create mode 100644 kcms/mobileshell/package/contents/ui/VibrationForm.qml diff --git a/components/mobileshell/mobileshellsettings.cpp b/components/mobileshell/mobileshellsettings.cpp index ea8f7eae..79b1df05 100644 --- a/components/mobileshell/mobileshellsettings.cpp +++ b/components/mobileshell/mobileshellsettings.cpp @@ -27,6 +27,8 @@ MobileShellSettings::MobileShellSettings(QObject *parent) connect(m_configWatcher.data(), &KConfigWatcher::configChanged, this, [this](const KConfigGroup &group, const QByteArrayList &names) -> void { if (group.name() == GENERAL_CONFIG_GROUP) { Q_EMIT vibrationsEnabledChanged(); + Q_EMIT vibrationIntensityChanged(); + Q_EMIT vibrationDurationChanged(); Q_EMIT animationsEnabledChanged(); Q_EMIT navigationPanelEnabledChanged(); } else if (group.name() == QUICKSETTINGS_CONFIG_GROUP) { @@ -49,6 +51,32 @@ void MobileShellSettings::setVibrationsEnabled(bool vibrationsEnabled) m_config->sync(); } +int MobileShellSettings::vibrationDuration() const +{ + auto group = KConfigGroup{m_config, GENERAL_CONFIG_GROUP}; + return group.readEntry("vibrationDuration", 100); +} + +void MobileShellSettings::setVibrationDuration(int vibrationDuration) +{ + auto group = KConfigGroup{m_config, GENERAL_CONFIG_GROUP}; + group.writeEntry("vibrationDuration", vibrationDuration, KConfigGroup::Notify); + m_config->sync(); +} + +qreal MobileShellSettings::vibrationIntensity() const +{ + auto group = KConfigGroup{m_config, GENERAL_CONFIG_GROUP}; + return group.readEntry("vibrationDuration", 0.5); +} + +void MobileShellSettings::setVibrationIntensity(qreal vibrationIntensity) +{ + auto group = KConfigGroup{m_config, GENERAL_CONFIG_GROUP}; + group.writeEntry("vibrationDuration", vibrationIntensity, KConfigGroup::Notify); + m_config->sync(); +} + bool MobileShellSettings::animationsEnabled() const { auto group = KConfigGroup{m_config, GENERAL_CONFIG_GROUP}; diff --git a/components/mobileshell/mobileshellsettings.h b/components/mobileshell/mobileshellsettings.h index 1bc5d2bc..8ab2f5f7 100644 --- a/components/mobileshell/mobileshellsettings.h +++ b/components/mobileshell/mobileshellsettings.h @@ -11,10 +11,17 @@ #include #include +/** + * @short Wrapper class to access and control mobile shell specific settings. + * + * @author Devin Lin + */ class MobileShellSettings : public QObject { Q_OBJECT Q_PROPERTY(bool vibrationsEnabled READ vibrationsEnabled WRITE setVibrationsEnabled NOTIFY vibrationsEnabledChanged) + Q_PROPERTY(int vibrationDuration READ vibrationDuration WRITE setVibrationDuration NOTIFY vibrationDurationChanged) + Q_PROPERTY(qreal vibrationIntensity READ vibrationIntensity WRITE setVibrationIntensity NOTIFY vibrationIntensityChanged) Q_PROPERTY(bool animationsEnabled READ animationsEnabled WRITE setAnimationsEnabled NOTIFY animationsEnabledChanged) Q_PROPERTY(bool navigationPanelEnabled READ navigationPanelEnabled WRITE setNavigationPanelEnabled NOTIFY navigationPanelEnabledChanged) @@ -23,23 +30,102 @@ public: MobileShellSettings(QObject *parent = nullptr); + /** + * Get whether shell vibrations are enabled. + */ bool vibrationsEnabled() const; + + /** + * Set whether shell vibrations should be enabled. + * + * @param vibrationsEnabled Whether vibrations are enabled. + */ void setVibrationsEnabled(bool vibrationsEnabled); + /** + * Get the duration of a standard vibration event, in milliseconds. + * Different types of vibration events may be calculated off of this. + */ + int vibrationDuration() const; + + /** + * Set the duration of a standard vibration event, in milliseconds. + * + * @param vibrationDuration The duration of a standard vibration event. + */ + void setVibrationDuration(int vibrationDuration); + + /** + * Get the intensity of a standard vibration event, which is a value between + * zero and one. + */ + qreal vibrationIntensity() const; + + /** + * Set the intensity of a standard vibration event. + * + * @param vibrationIntensity The intensity of a standard vibration event, between zero and one. + */ + void setVibrationIntensity(qreal vibrationIntensity); + + /** + * Whether animations are enabled in the shell. + * + * If false, vibrations will either be disabled or minimized as much as possible. + * TODO: integrate with animation speed (in settings at "Workspace Behaviour->General Behaviour"), + * which affects applications as well. + */ bool animationsEnabled() const; + + /** + * Set whether animations are enabled in the shell. + * + * @param animationsEnabled Whether animations should be enabled in the shell. + */ void setAnimationsEnabled(bool animationsEnabled); + /** + * Whether the navigation panel is enabled. + * + * If this is false, then gesture based navigation is used. + */ bool navigationPanelEnabled() const; + + /** + * Set whether the navigation panel is enabled. + * + * @param navigationPanelEnabled Whether the navigation panel should be enabled. + */ void setNavigationPanelEnabled(bool navigationPanelEnabled); + /** + * Get the list of IDs of quick settings that are enabled. + */ QList enabledQuickSettings() const; + + /** + * Set the list of quick settings that are enabled. + * + * @param list A list of quick setting IDs. + */ void setEnabledQuickSettings(QList &list); + /** + * Get the list of IDs of quick settings that are disabled. + */ QList disabledQuickSettings() const; + + /** + * Set the list of quick settings that are disabled. + * + * @param list A list of quick setting IDs. + */ void setDisabledQuickSettings(QList &list); Q_SIGNALS: void vibrationsEnabledChanged(); + void vibrationIntensityChanged(); + void vibrationDurationChanged(); void navigationPanelEnabledChanged(); void animationsEnabledChanged(); void enabledQuickSettingsChanged(); diff --git a/components/mobileshell/qml/components/Haptics.qml b/components/mobileshell/qml/components/Haptics.qml index b8b80cb4..9bfec952 100644 --- a/components/mobileshell/qml/components/Haptics.qml +++ b/components/mobileshell/qml/components/Haptics.qml @@ -16,8 +16,8 @@ QtObject { function buttonVibrate() { if (MobileShell.MobileShellSettings.vibrationsEnabled) { if (hapticsEffect.status == Loader.Ready) { - hapticsEffect.item.intensity = 0.5; - hapticsEffect.item.duration = 100; + hapticsEffect.item.intensity = MobileShell.MobileShellSettings.vibrationIntensity; + hapticsEffect.item.duration = MobileShell.MobileShellSettings.vibrationDuration; hapticsEffect.item.start(); } } diff --git a/kcms/mobileshell/package/contents/ui/VibrationForm.qml b/kcms/mobileshell/package/contents/ui/VibrationForm.qml new file mode 100644 index 00000000..986d9414 --- /dev/null +++ b/kcms/mobileshell/package/contents/ui/VibrationForm.qml @@ -0,0 +1,150 @@ +/* + * SPDX-FileCopyrightText: 2022 Devin Lin + * SPDX-License-Identifier: LGPL-2.0-or-later + */ + +import QtQuick 2.15 +import QtQuick.Layouts 1.15 +import QtQuick.Controls 2.15 as QQC2 + +import org.kde.kirigami 2.19 as Kirigami +import org.kde.kcm 1.3 as KCM +import org.kde.plasma.private.mobileshell 1.0 as MobileShell + +import "mobileform" as MobileForm + +Kirigami.ScrollablePage { + id: root + title: i18n("Shell Vibrations") + leftPadding: 0 + rightPadding: 0 + topPadding: Kirigami.Units.gridUnit + bottomPadding: Kirigami.Units.gridUnit + + ColumnLayout { + spacing: 0 + width: root.width + + MobileForm.FormCard { + Layout.fillWidth: true + + contentItem: ColumnLayout { + spacing: 0 + + MobileForm.FormSwitchDelegate { + id: shellVibrationsSwitch + text: i18n("Shell Vibrations") + description: i18n("Whether to have vibrations enabled in the shell.") + checked: MobileShell.MobileShellSettings.vibrationsEnabled + onCheckedChanged: { + if (checked != MobileShell.MobileShellSettings.vibrationsEnabled) { + MobileShell.MobileShellSettings.vibrationsEnabled = checked; + } + } + } + + Kirigami.Separator { + Layout.leftMargin: Kirigami.Units.largeSpacing + Layout.rightMargin: Kirigami.Units.largeSpacing + Layout.fillWidth: true + opacity: (!shellVibrationsSwitch.controlHovered && !vibrationIntensityDelegate.controlHovered) ? 0.5 : 0 + } + + MobileForm.FormComboBoxDelegate { + id: vibrationIntensityDelegate + text: i18n("Vibration Intensity") + description: i18n("How intense shell vibrations should be.") + + property string lowIntensityString: i18nc("Low intensity", "Low") + property string mediumIntensityString: i18nc("Medium intensity", "Medium") + property string highIntensityString: i18nc("High intensity", "High") + + currentValue: { + let intensity = MobileShell.MobileShellSettings.vibrationIntensity; + if (intensity <= 0.2) { + return lowIntensityString; + } else if (intensity <= 0.5) { + return mediumIntensityString; + } else { + return highIntensityString; + } + } + model: ListModel { + // we can't use i18n with ListElement + Component.onCompleted: { + append({"name": vibrationIntensityDelegate.highIntensityString, "value": 1.0}); + append({"name": vibrationIntensityDelegate.mediumIntensityString, "value": 0.5}); + append({"name": vibrationIntensityDelegate.lowIntensityString, "value": 0.2}); + } + } + dialogDelegate: QQC2.RadioDelegate { + implicitWidth: Kirigami.Units.gridUnit * 16 + topPadding: Kirigami.Units.smallSpacing * 2 + bottomPadding: Kirigami.Units.smallSpacing * 2 + + text: name + checked: vibrationIntensityDelegate.currentValue === name + onCheckedChanged: { + if (checked) { + MobileShell.MobileShellSettings.vibrationIntensity = value; + } + } + } + } + + Kirigami.Separator { + Layout.leftMargin: Kirigami.Units.largeSpacing + Layout.rightMargin: Kirigami.Units.largeSpacing + Layout.fillWidth: true + opacity: (!vibrationIntensityDelegate.controlHovered && !vibrationDurationDelegate.controlHovered) ? 0.5 : 0 + } + + MobileForm.FormComboBoxDelegate { + id: vibrationDurationDelegate + text: i18n("Vibration Duration") + description: i18n("How long shell vibrations should be.") + + property string longString: i18nc("Long duration", "Long") + property string mediumString: i18nc("Medium duration", "Medium") + property string shortString: i18nc("Short duration", "Short") + + currentValue: { + let duration = MobileShell.MobileShellSettings.vibrationDuration; + if (duration >= 100) { + return longString; + } else if (duration >= 50) { + return mediumString; + } else { + return shortString; + } + } + model: ListModel { + // we can't use i18n with ListElement + Component.onCompleted: { + append({"name": vibrationDurationDelegate.longString, "value": 100}); + append({"name": vibrationDurationDelegate.mediumString, "value": 50}); + append({"name": vibrationDurationDelegate.shortString, "value": 15}); + } + } + dialogDelegate: QQC2.RadioDelegate { + implicitWidth: Kirigami.Units.gridUnit * 16 + topPadding: Kirigami.Units.smallSpacing * 2 + bottomPadding: Kirigami.Units.smallSpacing * 2 + + text: name + checked: vibrationDurationDelegate.currentValue === name + onCheckedChanged: { + if (checked) { + MobileShell.MobileShellSettings.vibrationDuration = value; + } + } + } + } + } + } + + MobileForm.FormSectionText { + text: i18n("Keyboard vibrations are controlled separately in the keyboard settings module.") + } + } +} diff --git a/kcms/mobileshell/package/contents/ui/main.qml b/kcms/mobileshell/package/contents/ui/main.qml index 9fca0166..a25f2080 100644 --- a/kcms/mobileshell/package/contents/ui/main.qml +++ b/kcms/mobileshell/package/contents/ui/main.qml @@ -37,18 +37,21 @@ KCM.SimpleKCM { title: i18n("General") } - MobileForm.FormSwitchDelegate { + MobileForm.FormButtonDelegate { + id: shellVibrationsButton text: i18n("Shell Vibrations") - description: i18n("Whether to have vibrations enabled in the shell.") - checked: MobileShell.MobileShellSettings.vibrationsEnabled - onCheckedChanged: { - if (checked != MobileShell.MobileShellSettings.vibrationsEnabled) { - MobileShell.MobileShellSettings.vibrationsEnabled = checked; - } - } + onClicked: kcm.push("VibrationForm.qml") + } + + Kirigami.Separator { + Layout.leftMargin: Kirigami.Units.largeSpacing + Layout.rightMargin: Kirigami.Units.largeSpacing + Layout.fillWidth: true + opacity: (!shellVibrationsButton.controlHovered && !animationsSwitch.controlHovered) ? 0.5 : 0 } MobileForm.FormSwitchDelegate { + id: animationsSwitch text: i18n("Animations") description: i18n("If this is off, animations will be reduced as much as possible.") checked: MobileShell.MobileShellSettings.animationsEnabled diff --git a/kcms/mobileshell/package/contents/ui/mobileform/FormComboBoxDelegate.qml b/kcms/mobileshell/package/contents/ui/mobileform/FormComboBoxDelegate.qml index 068fcc66..1d921bc5 100644 --- a/kcms/mobileshell/package/contents/ui/mobileform/FormComboBoxDelegate.qml +++ b/kcms/mobileshell/package/contents/ui/mobileform/FormComboBoxDelegate.qml @@ -17,9 +17,29 @@ AbstractFormDelegate { // TODO property string currentValue: "" + property alias dialogDelegate: repeater.delegate + property alias model: repeater.model Layout.fillWidth: true + onClicked: dialog.open() + + Kirigami.Dialog { + id: dialog + showCloseButton: false + title: root.text + + ColumnLayout { + Kirigami.Theme.inherit: false + Kirigami.Theme.colorSet: Kirigami.Theme.View + spacing: 0 + + Repeater { + id: repeater + } + } + } + contentItem: RowLayout { ColumnLayout { Layout.fillWidth: true