diff --git a/kcm/config_handler.cpp b/kcm/config_handler.cpp index ce1ce0f18e1097b8040fb2cbd28bc9b533da8bce..3aea6b15a6e5f083a5a2912f16bd713ec2d23033 100644 --- a/kcm/config_handler.cpp +++ b/kcm/config_handler.cpp @@ -23,6 +23,7 @@ along with this program. If not, see . #include #include +#include #include using namespace KScreen; @@ -97,6 +98,7 @@ void ConfigHandler::initOutput(const KScreen::OutputPtr &output) void ConfigHandler::updateInitialData() { + m_previousConfig = m_initialConfig->clone(); m_initialRetention = getRetention(); connect(new GetConfigOperation(), &GetConfigOperation::finished, this, [this](ConfigOperation *op) { if (op->hasError()) { @@ -111,6 +113,11 @@ void ConfigHandler::updateInitialData() }); } +bool ConfigHandler::shouldTestNewSettings() +{ + return checkSaveandTestCommon(false); +} + void ConfigHandler::checkNeedsSave() { if (m_config->supportedFeatures() & KScreen::Config::Feature::PrimaryDisplay) { @@ -129,40 +136,44 @@ void ConfigHandler::checkNeedsSave() Q_EMIT needsSaveChecked(true); return; } + Q_EMIT needsSaveChecked(checkSaveandTestCommon(true)); +} - for (const auto &output : m_config->connectedOutputs()) { +bool ConfigHandler::checkSaveandTestCommon(bool isSaveCheck) +{ + const auto outputs = m_config->connectedOutputs(); + for (const auto &output : outputs) { const QString hash = output->hashMd5(); - for (const auto &initialOutput : m_initialConfig->outputs()) { - if (hash != initialOutput->hashMd5()) { + const auto configs = m_initialConfig->outputs(); + for (const auto &config : configs) { + if (hash != config->hashMd5()) { continue; } - bool needsSave = false; - if (output->isEnabled() != initialOutput->isEnabled()) { - needsSave = true; + + if (output->isEnabled() != config->isEnabled()) { + return true; } + // clang-format off if (output->isEnabled()) { - needsSave |= output->currentModeId() != - initialOutput->currentModeId() - || output->pos() != initialOutput->pos() - || output->scale() != initialOutput->scale() - || output->rotation() != initialOutput->rotation() - || output->replicationSource() != initialOutput->replicationSource() - || autoRotate(output) != m_initialControl->getAutoRotate(output) - || autoRotateOnlyInTabletMode(output) - != m_initialControl->getAutoRotateOnlyInTabletMode(output) - || output->overscan() != initialOutput->overscan() - || output->vrrPolicy() != initialOutput->vrrPolicy(); + bool realScaleChange = output->scale() != config->scale(); + bool scaleChanged = isSaveCheck ? realScaleChange : qGuiApp->platformName() == QLatin1String("wayland") ? realScaleChange : false; + if ( output->currentModeId() != config->currentModeId() + || output->pos() != config->pos() + || scaleChanged + || output->rotation() != config->rotation() + || output->replicationSource() != config->replicationSource() + || autoRotate(output) != m_initialControl->getAutoRotate(output) + || autoRotateOnlyInTabletMode(output) != m_initialControl->getAutoRotateOnlyInTabletMode(output) + || output->overscan() != config->overscan() + || output->vrrPolicy() != config->vrrPolicy()) { + return true; + } } // clang-format on - if (needsSave) { - Q_EMIT needsSaveChecked(true); - return; - } - break; } } - Q_EMIT needsSaveChecked(false); + return false; } QSize ConfigHandler::screenSize() const diff --git a/kcm/config_handler.h b/kcm/config_handler.h index ad3fb11b451850e5772f2df2879c1704fe42e705..c8dfd8694295c771d98aaf705bc7efcf53fdd283 100644 --- a/kcm/config_handler.h +++ b/kcm/config_handler.h @@ -51,6 +51,11 @@ public: return m_initialConfig; } + void revertConfig() + { + m_config = m_previousConfig->clone(); + } + int retention() const; void setRetention(int retention); @@ -74,6 +79,7 @@ public: void writeControl(); void checkNeedsSave(); + bool shouldTestNewSettings(); Q_SIGNALS: void outputModelChanged(); @@ -91,9 +97,17 @@ private: void primaryOutputChanged(const KScreen::OutputPtr &output); void initOutput(const KScreen::OutputPtr &output); void resetScale(const KScreen::OutputPtr &output); + /** + * @brief checkSaveandTestCommon - compairs common config changes that would make the config dirty and needed to have the config checked when applied. + * @param isSaveCheck - True if your checking to see if the changes should request a save. + * False if you want to check if you should test the config when applied. + * @return true, if you should check for a save or test the new configuration + */ + bool checkSaveandTestCommon(bool isSaveCheck); KScreen::ConfigPtr m_config = nullptr; KScreen::ConfigPtr m_initialConfig; + KScreen::ConfigPtr m_previousConfig = nullptr; OutputModel *m_outputs = nullptr; std::unique_ptr m_control; diff --git a/kcm/kcm.cpp b/kcm/kcm.cpp index 90bc0dd0c3df8af9706d7b227bc36a5b868f223d..bd5ff04adf40fd2ee263c9c81ce373d232dc4840 100644 --- a/kcm/kcm.cpp +++ b/kcm/kcm.cpp @@ -100,6 +100,21 @@ void KCMKScreen::save() doSave(false); } +void KCMKScreen::revertSettings() +{ + if (!m_config) { + setNeedsSave(false); + return; + } + if (!m_settingsReverted) { + m_config->revertConfig(); + m_settingsReverted = true; + doSave(true); + load(); // reload the configuration + Q_EMIT settingsReverted(); + } +} + void KCMKScreen::doSave(bool force) { if (!m_config) { @@ -158,6 +173,12 @@ void KCMKScreen::doSave(bool force) return; } m_config->updateInitialData(); + + if (!m_settingsReverted && m_config->shouldTestNewSettings()) { + Q_EMIT showRevertWarning(); + } else { + m_settingsReverted = false; + } }); } diff --git a/kcm/kcm.h b/kcm/kcm.h index b33eaa7c17acaf26698877e5e291d72a80ebf965..e29a30ddaf56b403e512b393305678be6f16b1ae 100644 --- a/kcm/kcm.h +++ b/kcm/kcm.h @@ -77,6 +77,7 @@ public: Q_INVOKABLE void forceSave(); void doSave(bool force); + Q_INVOKABLE void revertSettings(); Q_SIGNALS: void backendReadyChanged(); @@ -96,6 +97,8 @@ Q_SIGNALS: void errorOnSave(); void globalScaleWritten(); void outputConnect(bool connected); + void settingsReverted(); + void showRevertWarning(); private: void setBackendReady(bool error); @@ -112,6 +115,7 @@ private: OrientationSensor *m_orientationSensor; bool m_backendReady = false; bool m_screenNormalized = true; + bool m_settingsReverted = false; double m_globalScale = 1.; double m_initialGlobalScale = 1.; diff --git a/kcm/package/contents/ui/main.qml b/kcm/package/contents/ui/main.qml index 8cb3375d7268821f5f8952eb70ca215ca636ef5b..15bb16f017f1ebcd21b318adf2929dcbbcf970ea 100644 --- a/kcm/package/contents/ui/main.qml +++ b/kcm/package/contents/ui/main.qml @@ -16,9 +16,9 @@ along with this program. If not, see . *********************************************************************/ import QtQuick 2.15 import QtQuick.Layouts 1.1 -import QtQuick.Controls 2.3 as Controls -import org.kde.kirigami 2.4 as Kirigami +import QtQuick.Controls 2.15 as Controls +import org.kde.kirigami 2.7 as Kirigami import org.kde.kcm 1.2 as KCM KCM.SimpleKCM { @@ -28,6 +28,7 @@ KCM.SimpleKCM { implicitHeight: Kirigami.Units.gridUnit * 38 property int selectedOutput: 0 + property int revertCountdown: 30 ColumnLayout { Kirigami.InlineMessage { @@ -86,6 +87,74 @@ KCM.SimpleKCM { visible: false showCloseButton: true } + Kirigami.InlineMessage { + id: revertMsg + Layout.fillWidth: true + type: Kirigami.MessageType.Information + text: i18n("Display configuration reverted.") + visible: false + showCloseButton: true + } + Kirigami.OverlaySheet { + id: confirmMsg + property bool keepConfig: false + property bool userInteraction: false + parent: root.parent + title: i18n("Keep display configuration?") + onSheetOpenChanged: { + if (sheetOpen) { + revertButton.forceActiveFocus() + confirmMsg.keepConfig = false + confirmMsg.userInteraction = false + } else { + if (!confirmMsg.keepConfig) { + kcm.revertSettings() + if (!confirmMsg.userInteraction) { + revertMsg.visible = true + } + } + revertTimer.stop() + } + } + showCloseButton: false + contentItem: Controls.Label { + text: i18np("Will revert to previous configuration in %1 second.", + "Will revert to previous configuration in %1 seconds.", + revertCountdown); + wrapMode: Qt.WordWrap + } + footer: RowLayout { + Controls.Button { + id: acceptButton + Layout.fillWidth: true + action: Controls.Action { + icon.name: "dialog-ok" + text: i18n("&Keep") + shortcut: "Return" + onTriggered: { + confirmMsg.keepConfig = true + confirmMsg.userInteraction = true + confirmMsg.close() + } + } + } + Controls.Button { + id: revertButton + Layout.fillWidth: true + KeyNavigation.left: acceptButton + focus: true + action: Controls.Action { + icon.name: "edit-undo" + text: i18n("&Revert") + shortcut: "Escape" + onTriggered: { + confirmMsg.userInteraction = true + confirmMsg.close() + } + } + } + } + } Connections { target: kcm @@ -109,11 +178,19 @@ KCM.SimpleKCM { function onBackendError() { errBackendMsg.visible = true; } - + function onSettingsReverted() { + confirmMsg.close(); + } + function onShowRevertWarning() { + revertCountdown = 30; + confirmMsg.open(); + revertTimer.restart(); + } function onChanged() { dangerousSaveMsg.visible = false; errSaveMsg.visible = false; scaleMsg.visible = false; + revertMsg.visible = false; } } @@ -133,5 +210,21 @@ KCM.SimpleKCM { enabled: kcm.outputModel && kcm.backendReady Layout.fillWidth: true } + + Timer { + id: revertTimer + interval: 1000 + running: false + repeat: true + + onTriggered: { + revertCountdown -= 1; + if (revertCountdown < 1) { + this.stop(); + kcm.revertSettings(); + return; + } + } + } } }