diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c076b6f..bfd9cbd4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 it returns only the modules that are part of the data model of the current project. - #630 - Fixed a crash when creating a calculator with missing author information in a Python Task - #627 +### Changed + - Python Task and Calculator wizards now automatically transfer changes from the editor to + the corresponding data model object whenever the wizard loses focus or is closed. + - Removed the `Save` and `Cancel` buttons as well as the "Do you want to save..." message box, + since manual saving is no longer required. All changes can be undone via GTlab's Undo/Redo system. - #86 + - The script editor in the wizard now updates automatically when Undo/Redo is performed in the main GUI. + ## [1.8.0] - 2025-07-11 ### Fixed diff --git a/src/module/processcomponents/gtpy_abstractscriptcomponent.cpp b/src/module/processcomponents/gtpy_abstractscriptcomponent.cpp index 7ab7005c..1ea595c4 100644 --- a/src/module/processcomponents/gtpy_abstractscriptcomponent.cpp +++ b/src/module/processcomponents/gtpy_abstractscriptcomponent.cpp @@ -226,6 +226,12 @@ GtpyAbstractScriptComponent::outputArg(const QString& argName) const } #endif +const GtStringProperty& +GtpyAbstractScriptComponent::scriptProp() const +{ + return m_script; +} + bool GtpyAbstractScriptComponent::evalScript(int contextId) { diff --git a/src/module/processcomponents/gtpy_abstractscriptcomponent.h b/src/module/processcomponents/gtpy_abstractscriptcomponent.h index 53e88635..fc603984 100644 --- a/src/module/processcomponents/gtpy_abstractscriptcomponent.h +++ b/src/module/processcomponents/gtpy_abstractscriptcomponent.h @@ -108,6 +108,12 @@ class GtpyAbstractScriptComponent QVariant outputArg(const QString& argName) const; #endif + /** + * @brief Returns the script property. + * @return Constant reference to the script property. + */ + const GtStringProperty& scriptProp() const; + protected: /** * @brief GtpyComponentAssistant diff --git a/src/module/wizards/gtpy_abstractscriptingwizardpage.cpp b/src/module/wizards/gtpy_abstractscriptingwizardpage.cpp index 0064e00c..c9de4bfa 100644 --- a/src/module/wizards/gtpy_abstractscriptingwizardpage.cpp +++ b/src/module/wizards/gtpy_abstractscriptingwizardpage.cpp @@ -34,13 +34,13 @@ #include "gtpy_editorsettingsdialog.h" #include "gtpy_packageiteration.h" #include "gtpy_transfer.h" +#include "gtpy_abstractscriptcomponent.h" // GTlab framework includes #include "gt_object.h" #include "gt_project.h" #include "gt_datamodel.h" #include "gt_filedialog.h" -#include "gt_saveprojectmessagebox.h" #include "gt_pyhighlighter.h" #include "gt_searchwidget.h" @@ -57,7 +57,6 @@ GtpyAbstractScriptingWizardPage::GtpyAbstractScriptingWizardPage( m_editorSettings(nullptr), m_isEvaluating(false), m_runnable(nullptr), - m_savingEnabled(true), m_componentUuid(QString()) { setTitle(tr("Python Script Editor")); @@ -156,7 +155,6 @@ GtpyAbstractScriptingWizardPage::GtpyAbstractScriptingWizardPage( fontClearOutput.setItalic(true); fontClearOutput.setPointSize(7); shortCutClearOutput->setFont(fontClearOutput); - shortCutClearOutput->installEventFilter(this); QVBoxLayout* clearButtonLay = new QVBoxLayout; @@ -185,25 +183,6 @@ GtpyAbstractScriptingWizardPage::GtpyAbstractScriptingWizardPage( toolBarLayout->addStretch(1); - //Save Button - m_shortCutSave = new QLabel(" Ctrl+S"); - QFont fontSave = m_shortCutSave->font(); - fontSave.setItalic(true); - fontSave.setPointSize(7); - m_shortCutSave->setFont(fontSave); - - m_saveButton = new QPushButton; - m_saveButton->setIcon(GTPY_ICON(save)); - m_saveButton->setToolTip(tr("Save Script")); - - QVBoxLayout* saveButtonLay = new QVBoxLayout; - saveButtonLay->addWidget(m_saveButton); - saveButtonLay->addWidget(m_shortCutSave); - - toolBarLayout->addLayout(saveButtonLay); - - enableSaveButton(false); - //Import Button QLabel* shortCutImport = new QLabel(""); QFont fontImport = shortCutImport->font(); @@ -311,12 +290,11 @@ void GtpyAbstractScriptingWizardPage::initializePage() { // we want to react, when the wizard should be closed - if (wizard()) wizard()->installEventFilter(this); - - /// Can not be connected in the constructor because onSaveButtonClicked() - /// calls the pure virtual function saveScript() - connect(m_saveButton, SIGNAL(clicked(bool)), this, - SLOT(onSaveButtonClicked())); + if (auto* wiz = wizard()) + { + wiz->setOption(QWizard::NoCancelButton, true); + wiz->installEventFilter(this); + } setWizardNonModal(); @@ -332,20 +310,22 @@ GtpyAbstractScriptingWizardPage::initializePage() loadPackages(); - GtObject* component = gtDataModel->objectByUuid(m_componentUuid); + GtObject* obj = gtDataModel->objectByUuid(m_componentUuid); + if (!obj) return; - if (!component) - { - enableSaving(false); - } - else - { - setTitle(component->objectName()); - connect(component, SIGNAL(objectNameChanged(QString)), this, - SLOT(componentRenamed(QString))); - } + setTitle(obj->objectName()); + + connect(obj, &GtObject::objectNameChanged, this, + &GtpyAbstractScriptingWizardPage::componentRenamed); + + auto* comp = dynamic_cast(obj); + if (!comp) return; - connect(m_editor, SIGNAL(textChanged()), this, SLOT(onTextChanged())); + using DataChanged = void (GtObject::*)(GtObject*, GtAbstractProperty*); + connect(obj, static_cast(&GtObject::dataChanged), + this, [this, c = comp](GtObject* obj, GtAbstractProperty* prop) { + if (c && prop == &c->scriptProp()) setPlainTextToEditor(c->script()); + }); } bool @@ -365,16 +345,6 @@ GtpyAbstractScriptingWizardPage::keyPressEvent(QKeyEvent* e) // Ignore return return; - case Qt::Key_Escape: - - if (m_saveButton->isEnabled()) - { - saveMessageBox(); - return; - } - - break; - default: break; } @@ -400,14 +370,6 @@ GtpyAbstractScriptingWizardPage::keyPressEvent(QKeyEvent* e) return; } - // Save shortcut - if (((e->modifiers() & Qt::ControlModifier) && - e->key() == Qt::Key_S)) - { - onSaveButtonClicked(); - return; - } - GtProcessWizardPage::keyPressEvent(e); } @@ -457,16 +419,16 @@ GtpyAbstractScriptingWizardPage::defaultFrameStyle() void GtpyAbstractScriptingWizardPage::setPlainTextToEditor(const QString& text) { - if (!m_editor) - { - return; - } + assert(m_editor); + assert(m_searchWidget); m_editor->setPlainText(text); + m_editor->setExtraSelections({}); + m_editor->searchHighlighting(m_searchWidget->text()); } QString -GtpyAbstractScriptingWizardPage::editorText() +GtpyAbstractScriptingWizardPage::editorText() const { if (!m_editor) { @@ -710,21 +672,6 @@ GtpyAbstractScriptingWizardPage::onEvalShortCutTriggered() } } -void -GtpyAbstractScriptingWizardPage::onSaveButtonClicked() -{ - connect(m_editor, SIGNAL(textChanged()), this, SLOT(onTextChanged())); - saveScript(); - enableSaveButton(false); -} - -void -GtpyAbstractScriptingWizardPage::onTextChanged() -{ - enableSaveButton(true); - disconnect(m_editor, SIGNAL(textChanged()), this, SLOT(onTextChanged())); -} - void GtpyAbstractScriptingWizardPage::initialization() { @@ -738,66 +685,26 @@ GtpyAbstractScriptingWizardPage::validation() } void -GtpyAbstractScriptingWizardPage::enableSaveButton(bool enable) +GtpyAbstractScriptingWizardPage::saveScript() { - if (!m_savingEnabled) - { - enable = false; - } - - m_saveButton->setEnabled(enable); - - if (enable) - { - m_shortCutSave->setText(" Ctrl+S"); - } - else - { - m_shortCutSave->setText(""); - } + storeScriptInDataModel(); } -int -GtpyAbstractScriptingWizardPage::saveMessageBox() +void +GtpyAbstractScriptingWizardPage::storeScriptInDataModel() const { - QWidget* wiz = wizard(); - - if (!wiz) - { - return QMessageBox::Cancel; - } - - QString text = - tr("Do you want to ") + - tr("save your changes before closing the wizard?"); - - GtSaveProjectMessageBox mb(text); - - mb.setWindowFlags(mb.windowFlags() | Qt::WindowStaysOnTopHint); + GtObject* obj = gtDataModel->objectByUuid(componentUuid()); + if (!obj) return; - int ret; + auto* comp = dynamic_cast(obj); + if (!comp) return; - ret = mb.exec(); + const QString script = editorText(); + if (comp->script() == script) return; - switch (ret) - { - case QMessageBox::Yes: - onSaveButtonClicked(); - wiz->close(); - break; - - case QMessageBox::No: - wiz->close(); - break; - - case QMessageBox::Cancel: - break; - - default: - break; - } - - return ret; + auto _ = gtApp->makeCommand(obj, tr("%1 code changed") + .arg(obj->objectName())); + comp->setScript(script); } void @@ -961,24 +868,26 @@ GtpyAbstractScriptingWizardPage::cursorToNewLine() m_editor->setTextCursor(cur); } -void -GtpyAbstractScriptingWizardPage::enableSaving(bool enable) -{ - m_savingEnabled = enable; -} - - bool -GtpyAbstractScriptingWizardPage::eventFilter(QObject *watched, QEvent *event) +GtpyAbstractScriptingWizardPage::eventFilter(QObject* watched, QEvent* event) { - if (event && event->type() == QEvent::Close && m_saveButton->isEnabled()) + if (!watched || !event) + { + return GtProcessWizardPage::eventFilter(watched, event); + } + + switch (event->type()) { - if (saveMessageBox() == QMessageBox::Cancel) event->ignore(); + // this event occurs when the wizard loses focus + case QEvent::WindowDeactivate: + if (watched == wizard()) storeScriptInDataModel(); + break; - return true; + default: + break; } - return QObject::eventFilter(watched, event); + return GtProcessWizardPage::eventFilter(watched, event); } void diff --git a/src/module/wizards/gtpy_abstractscriptingwizardpage.h b/src/module/wizards/gtpy_abstractscriptingwizardpage.h index a0fbe72c..fb59c0ca 100644 --- a/src/module/wizards/gtpy_abstractscriptingwizardpage.h +++ b/src/module/wizards/gtpy_abstractscriptingwizardpage.h @@ -107,7 +107,7 @@ class GT_PYTHON_EXPORT GtpyAbstractScriptingWizardPage : * @brief Returns current text in editor widget. * @return current text in editor widget */ - QString editorText(); + QString editorText() const; /** * @brief Replaces the calculator settings between header and caption with @@ -198,20 +198,13 @@ class GT_PYTHON_EXPORT GtpyAbstractScriptingWizardPage : */ void cursorToNewLine(); - /** - * @brief Enable or disable the saving functionality. - * @param enable Whether the saving functionality should be enabled or - * disabled. - */ - void enableSaving(bool enable = true); - /// Python Context id int m_contextId; /** * To intercept closing events */ - bool eventFilter(QObject *watched, QEvent *event) override; + bool eventFilter(QObject* watched, QEvent* event) override; /// Python Context type GtpyContextManager::Context m_contextType; @@ -242,10 +235,14 @@ protected slots: virtual bool validation(); /** - * @brief In this pure virtual function the routine for saving a script - * must be implemented. + * @brief Calls storeScriptInDataModel() to apply the current script + * from the editor to the data model. + * + * This method is retained for backward compatibility. + * Use storeScriptInDataModel() directly instead. */ - virtual void saveScript() = 0; + [[deprecated("Use storeScriptInDataModel() instead")]] + virtual void saveScript() final; /** * @brief This pure virtual function must return the uuid of the restored @@ -276,16 +273,9 @@ protected slots: virtual void saveSettings(GtpyEditorSettings* pref) = 0; /** - * @brief Enables of disables the save button. - * @param enable If true, the Save button is enabled, otherwise it is - * disabled. - */ - void enableSaveButton(bool enable = true); - - /** - * @brief saveMesssageBox + * @brief Applies the current script from the editor to the data model. */ - int saveMessageBox(); + void storeScriptInDataModel() const; /** * @brief Sets the window modality of the wizard to non modal. This allows @@ -341,12 +331,6 @@ protected slots: /// Interrupt shortcut lable QLabel* m_shortCutInterrupt; - /// Save Button - QPushButton* m_saveButton; - - /// Save shortcut label - QLabel* m_shortCutSave; - /// Console Clear Button QPushButton* m_consoleClearButton; @@ -373,9 +357,6 @@ protected slots: /// Script runnable for evaluation QPointer m_runnable; - /// Saving the script - bool m_savingEnabled; - /// Process component uuid QString m_componentUuid; @@ -443,16 +424,6 @@ private slots: */ void onEvalShortCutTriggered(); - /** - * @brief It saves the current script. - */ - void onSaveButtonClicked(); - - /** - * @brief Enables the save button. - */ - void onTextChanged(); - /** * @brief Checks if the evaluation was successful. */ diff --git a/src/module/wizards/python_task/gtpy_taskwizardpage.cpp b/src/module/wizards/python_task/gtpy_taskwizardpage.cpp index c8e3ea0b..b7384245 100644 --- a/src/module/wizards/python_task/gtpy_taskwizardpage.cpp +++ b/src/module/wizards/python_task/gtpy_taskwizardpage.cpp @@ -38,7 +38,6 @@ #include "gt_project.h" #include "gt_calculatorprovider.h" #include "gt_processwizard.h" -#include "gt_command.h" #include "gt_application.h" #include "gt_deleteitemmessagebox.h" #include "gt_calculator.h" @@ -156,11 +155,6 @@ GtpyTaskWizardPage::initialization() gtpy::transfer::propStructToPython(m_contextId, m_task->outputArgs()); #endif - if (!gtDataModel->objectByUuid(m_task->uuid())) - { - enableSaving(false); - } - enableCalculators(m_task); m_task->setFlag(GtObject::NewlyCreated, false); @@ -235,36 +229,6 @@ GtpyTaskWizardPage::validation() return true; } -void -GtpyTaskWizardPage::saveScript() -{ - if (!m_task) - { - return; - } - - m_task->setScript(editorText()); - - GtObjectMemento memento = m_task->toMemento(); - - if (memento.isNull()) - { - return; - } - - GtObject* obj = gtDataModel->objectByUuid(m_task->uuid()); - - if (GtTask* task = qobject_cast(obj)) - { - GtCommand command = - gtApp->startCommand(gtApp->currentProject(), - task->objectName() + - tr(" configuration changed")); - task->fromMemento(memento); - gtApp->endCommand(command); - } -} - QString GtpyTaskWizardPage::componentUuid() const { diff --git a/src/module/wizards/python_task/gtpy_taskwizardpage.h b/src/module/wizards/python_task/gtpy_taskwizardpage.h index fefdded4..16f7b97f 100644 --- a/src/module/wizards/python_task/gtpy_taskwizardpage.h +++ b/src/module/wizards/python_task/gtpy_taskwizardpage.h @@ -57,11 +57,6 @@ class GT_PYTHON_EXPORT GtpyTaskWizardPage : */ virtual bool validation() override; - /** - * @brief Saves the script into the task instance. - */ - virtual void saveScript() override; - /** * @brief Returns the uuid of the restored GtpyScriptCalculator. * @return uuid of the restored GtpyScriptCalculator diff --git a/src/module/wizards/script_calculator/gtpy_scriptcalculatorwizardpage.cpp b/src/module/wizards/script_calculator/gtpy_scriptcalculatorwizardpage.cpp index f6f09ef3..3c1444e5 100644 --- a/src/module/wizards/script_calculator/gtpy_scriptcalculatorwizardpage.cpp +++ b/src/module/wizards/script_calculator/gtpy_scriptcalculatorwizardpage.cpp @@ -10,10 +10,6 @@ // GTlab framework includes -#include "gt_datamodel.h" -#include "gt_command.h" -#include "gt_application.h" -#include "gt_project.h" #include "gt_processfactory.h" #include "gt_objectmemento.h" #include "gt_abstractprocessprovider.h" @@ -58,11 +54,6 @@ GtpyScriptCalculatorWizardPage::initialization() gtpy::transfer::propStructToPython(m_contextId, m_calc->outputArgs()); #endif - if (!gtDataModel->objectByUuid(m_calc->uuid())) - { - enableSaving(false); - } - setPlainTextToEditor(m_calc->script()); } @@ -80,36 +71,6 @@ GtpyScriptCalculatorWizardPage::validation() return true; } -void -GtpyScriptCalculatorWizardPage::saveScript() -{ - if (!m_calc) - { - return; - } - - m_calc->setScript(editorText()); - - GtObjectMemento memento = m_calc->toMemento(); - - if (memento.isNull()) - { - return; - } - - auto* obj = gtDataModel->objectByUuid(m_calc->uuid()); - - if (auto* calc = qobject_cast(obj)) - { - GtCommand command = - gtApp->startCommand(gtApp->currentProject(), - calc->objectName() + - tr(" configuration changed")); - calc->fromMemento(memento); - gtApp->endCommand(command); - } -} - QString GtpyScriptCalculatorWizardPage::componentUuid() const { diff --git a/src/module/wizards/script_calculator/gtpy_scriptcalculatorwizardpage.h b/src/module/wizards/script_calculator/gtpy_scriptcalculatorwizardpage.h index 3f304279..068057d7 100644 --- a/src/module/wizards/script_calculator/gtpy_scriptcalculatorwizardpage.h +++ b/src/module/wizards/script_calculator/gtpy_scriptcalculatorwizardpage.h @@ -39,11 +39,6 @@ class GtpyScriptCalculatorWizardPage : public GtpyAbstractScriptingWizardPage */ virtual bool validation() override; - /** - * @brief Saves the script into the calculator instance. - */ - virtual void saveScript() override; - /** * @brief Returns the uuid of the restored GtpyScriptCalculator. * @return uuid of the restored GtpyScriptCalculator