Archetype: cpp_qt_detail_dialog.cpp.mustache

Table of Contents

Loads the generated .ui; field get/set per widget kind. Qt UI component: model/view class or dialog wired to the service layer via request/response messages.

See the Template variable reference for the complete list of available variables and their semantics.

Template

The full template source. Edit here and re-tangle with compass build --direct tangle_codegen_templates to regenerate library/templates/cpp_qt_detail_dialog.cpp.mustache.

{{! GENERATED FILE — tangled from projects/ores.codegen/library/templates/cpp_qt.org. Edit the org source. }}
{{! Template to generate Qt detail dialog source for domain entities }}
{{{cpp_license}}}
#include "ores.qt/{{domain_entity.entity_pascal}}DetailDialog.hpp"

#include <QMessageBox>
#include <QtConcurrent>
#include <QFutureWatcher>
{{#domain_entity.qt.has_text_edit_fields}}
#include <QPlainTextEdit>
{{/domain_entity.qt.has_text_edit_fields}}
{{#domain_entity.qt.has_combo_fields}}
#include <QComboBox>
{{/domain_entity.qt.has_combo_fields}}
{{#domain_entity.qt.has_uuid_primary_key}}
#include <boost/uuid/random_generator.hpp>
#include <boost/uuid/uuid_io.hpp>
{{/domain_entity.qt.has_uuid_primary_key}}
{{^domain_entity.qt.has_uuid_primary_key}}
{{#domain_entity.qt.has_uuid_detail_fields}}
#include <boost/uuid/uuid_io.hpp>
{{/domain_entity.qt.has_uuid_detail_fields}}
{{/domain_entity.qt.has_uuid_primary_key}}
#include "ui_{{domain_entity.entity_pascal}}DetailDialog.h"
#include "ores.qt/ChangeReasonDialog.hpp"
#include "ores.qt/IconUtils.hpp"
#include "ores.qt/MessageBoxHelper.hpp"
#include "{{domain_entity.qt.protocol_include}}"

namespace ores::qt {

using namespace ores::logging;

{{domain_entity.entity_pascal}}DetailDialog::{{domain_entity.entity_pascal}}DetailDialog(QWidget* parent)
    : DetailDialogBase(parent),
      ui_(new Ui::{{domain_entity.entity_pascal}}DetailDialog),
      clientManager_(nullptr) {

    ui_->setupUi(this);
    setupUi();
{{#domain_entity.qt.has_combo_fields}}
    setupCombos();
{{/domain_entity.qt.has_combo_fields}}
    setupConnections();
}

{{domain_entity.entity_pascal}}DetailDialog::~{{domain_entity.entity_pascal}}DetailDialog() {
    delete ui_;
}

QTabWidget* {{domain_entity.entity_pascal}}DetailDialog::tabWidget() const {
    return ui_->tabWidget;
}

QWidget* {{domain_entity.entity_pascal}}DetailDialog::provenanceTab() const {
    return ui_->provenanceTab;
}

ProvenanceWidget* {{domain_entity.entity_pascal}}DetailDialog::provenanceWidget() const {
    return ui_->provenanceWidget;
}

void {{domain_entity.entity_pascal}}DetailDialog::setupUi() {
    ui_->saveButton->setIcon(
        IconUtils::createRecoloredIcon(Icon::Save, IconUtils::DefaultIconColor));
    ui_->saveButton->setEnabled(false);

    ui_->deleteButton->setIcon(
        IconUtils::createRecoloredIcon(Icon::Delete, IconUtils::DefaultIconColor));

    ui_->closeButton->setIcon(
        IconUtils::createRecoloredIcon(Icon::Dismiss, IconUtils::DefaultIconColor));
}

{{#domain_entity.qt.has_combo_fields}}
void {{domain_entity.entity_pascal}}DetailDialog::setupCombos() {
{{#domain_entity.qt.detail_fields}}
{{#is_static_combo}}
    ui_->{{widget}}->clear();
{{#combo_values}}
    ui_->{{widget}}->addItem(tr("{{label}}"), QString("{{value}}"));
{{/combo_values}}
{{/is_static_combo}}
{{/domain_entity.qt.detail_fields}}
}

{{/domain_entity.qt.has_combo_fields}}
void {{domain_entity.entity_pascal}}DetailDialog::setupConnections() {
    connect(ui_->saveButton, &QPushButton::clicked, this,
            &{{domain_entity.entity_pascal}}DetailDialog::onSaveClicked);
    connect(ui_->deleteButton, &QPushButton::clicked, this,
            &{{domain_entity.entity_pascal}}DetailDialog::onDeleteClicked);
    connect(ui_->closeButton, &QPushButton::clicked, this,
            &{{domain_entity.entity_pascal}}DetailDialog::onCloseClicked);

{{#domain_entity.qt.detail_fields}}
{{#is_key}}
{{#is_line_edit}}
    connect(ui_->{{widget}}, &QLineEdit::textChanged, this,
            &{{domain_entity.entity_pascal}}DetailDialog::onCodeChanged);
{{/is_line_edit}}
{{/is_key}}
{{^is_key}}
{{#is_line_edit}}
    connect(ui_->{{widget}}, &QLineEdit::textChanged, this,
            &{{domain_entity.entity_pascal}}DetailDialog::onFieldChanged);
{{/is_line_edit}}
{{#is_text_edit}}
    connect(ui_->{{widget}}, &QPlainTextEdit::textChanged, this,
            &{{domain_entity.entity_pascal}}DetailDialog::onFieldChanged);
{{/is_text_edit}}
{{#is_static_combo}}
    connect(ui_->{{widget}}, &QComboBox::currentIndexChanged, this,
            &{{domain_entity.entity_pascal}}DetailDialog::onFieldChanged);
{{/is_static_combo}}
{{#is_dynamic_combo}}
    connect(ui_->{{widget}}, &QComboBox::currentIndexChanged, this,
            &{{domain_entity.entity_pascal}}DetailDialog::onFieldChanged);
{{/is_dynamic_combo}}
{{/is_key}}
{{/domain_entity.qt.detail_fields}}
}

void {{domain_entity.entity_pascal}}DetailDialog::setClientManager(ClientManager* clientManager) {
    clientManager_ = clientManager;
}

void {{domain_entity.entity_pascal}}DetailDialog::setUsername(const std::string& username) {
    username_ = username;
}

void {{domain_entity.entity_pascal}}DetailDialog::set{{domain_entity.entity_pascal_short}}(
    const {{domain_entity.qt.domain_class}}& {{domain_entity.qt.item_var}}) {
    {{domain_entity.qt.item_var}}_ = {{domain_entity.qt.item_var}};
    updateUiFrom{{domain_entity.entity_pascal_short}}();
}

void {{domain_entity.entity_pascal}}DetailDialog::setCreateMode(bool createMode) {
    createMode_ = createMode;
    ui_->{{domain_entity.qt.key_widget}}->setReadOnly(!createMode);
    ui_->deleteButton->setVisible(!createMode);
    setProvenanceEnabled(!createMode);
{{#domain_entity.qt.has_uuid_primary_key}}
    if (createMode) {
        {{domain_entity.qt.item_var}}_.{{#domain_entity.qt.id_access}}{{domain_entity.qt.id_access}}{{/domain_entity.qt.id_access}}{{^domain_entity.qt.id_access}}id{{/domain_entity.qt.id_access}} = boost::uuids::random_generator()();
    }
{{/domain_entity.qt.has_uuid_primary_key}}
    hasChanges_ = false;
    updateSaveButtonState();
}

void {{domain_entity.entity_pascal}}DetailDialog::setReadOnly(bool readOnly) {
    readOnly_ = readOnly;
{{#domain_entity.qt.detail_fields}}
{{#is_key}}
{{#is_line_edit}}
    ui_->{{widget}}->setReadOnly(true);
{{/is_line_edit}}
{{#is_text_edit}}
    ui_->{{widget}}->setReadOnly(true);
{{/is_text_edit}}
{{#is_static_combo}}
    ui_->{{widget}}->setEnabled(false);
{{/is_static_combo}}
{{#is_dynamic_combo}}
    ui_->{{widget}}->setEnabled(false);
{{/is_dynamic_combo}}
{{/is_key}}
{{^is_key}}
{{#is_line_edit}}
    ui_->{{widget}}->setReadOnly(readOnly);
{{/is_line_edit}}
{{#is_text_edit}}
    ui_->{{widget}}->setReadOnly(readOnly);
{{/is_text_edit}}
{{#is_static_combo}}
    ui_->{{widget}}->setEnabled(!readOnly);
{{/is_static_combo}}
{{#is_dynamic_combo}}
    ui_->{{widget}}->setEnabled(!readOnly);
{{/is_dynamic_combo}}
{{/is_key}}
{{/domain_entity.qt.detail_fields}}
    ui_->saveButton->setVisible(!readOnly);
    ui_->deleteButton->setVisible(!readOnly);
}

{{#domain_entity.qt.detail_fields}}
{{#is_dynamic_combo}}
void {{domain_entity.entity_pascal}}DetailDialog::{{combo_setter}}(
    const std::vector<{{combo_type}}>& items) {
    {{combo_items_member}}_ = items;
    populate{{combo_setter_pascal}}();
}

void {{domain_entity.entity_pascal}}DetailDialog::populate{{combo_setter_pascal}}() {
    const auto current = ui_->{{widget}}->currentData();
    ui_->{{widget}}->clear();
{{#combo_is_optional}}
    ui_->{{widget}}->addItem(tr("(None)"), QVariant{});
{{/combo_is_optional}}
    for (const auto& item : {{combo_items_member}}_) {
        ui_->{{widget}}->addItem(
            QString::fromStdString(item.{{combo_display}}),
            QString::fromStdString(item.{{combo_value}}));
    }
    if (current.isValid()) {
        const int idx = ui_->{{widget}}->findData(current);
        if (idx >= 0) ui_->{{widget}}->setCurrentIndex(idx);
    }
}

{{/is_dynamic_combo}}
{{/domain_entity.qt.detail_fields}}
void {{domain_entity.entity_pascal}}DetailDialog::updateUiFrom{{domain_entity.entity_pascal_short}}() {
{{#domain_entity.qt.detail_fields}}
{{#is_line_edit}}
{{#is_optional_uuid}}
    {
        const auto& _opt = {{domain_entity.qt.item_var}}_.{{#field_access}}{{field_access}}{{/field_access}}{{^field_access}}{{field}}{{/field_access}};
        ui_->{{widget}}->setText(_opt
            ? QString::fromStdString(boost::uuids::to_string(*_opt))
            : QString{});
    }
{{/is_optional_uuid}}
{{^is_optional_uuid}}
{{#is_uuid}}
    ui_->{{widget}}->setText(QString::fromStdString(
        boost::uuids::to_string({{domain_entity.qt.item_var}}_.{{#field_access}}{{field_access}}{{/field_access}}{{^field_access}}{{field}}{{/field_access}})));
{{/is_uuid}}
{{^is_uuid}}
{{#is_optional_string}}
    ui_->{{widget}}->setText({{domain_entity.qt.item_var}}_.{{#field_access}}{{field_access}}{{/field_access}}{{^field_access}}{{field}}{{/field_access}}
        ? QString::fromStdString(*{{domain_entity.qt.item_var}}_.{{#field_access}}{{field_access}}{{/field_access}}{{^field_access}}{{field}}{{/field_access}})
        : QString{});
{{/is_optional_string}}
{{^is_optional_string}}
    ui_->{{widget}}->setText(QString::fromStdString({{domain_entity.qt.item_var}}_.{{#field_access}}{{field_access}}{{/field_access}}{{^field_access}}{{field}}{{/field_access}}));
{{/is_optional_string}}
{{/is_uuid}}
{{/is_optional_uuid}}
{{/is_line_edit}}
{{#is_text_edit}}
{{#is_optional_string}}
    ui_->{{widget}}->setPlainText({{domain_entity.qt.item_var}}_.{{#field_access}}{{field_access}}{{/field_access}}{{^field_access}}{{field}}{{/field_access}}
        ? QString::fromStdString(*{{domain_entity.qt.item_var}}_.{{#field_access}}{{field_access}}{{/field_access}}{{^field_access}}{{field}}{{/field_access}})
        : QString{});
{{/is_optional_string}}
{{^is_optional_string}}
    ui_->{{widget}}->setPlainText(QString::fromStdString({{domain_entity.qt.item_var}}_.{{#field_access}}{{field_access}}{{/field_access}}{{^field_access}}{{field}}{{/field_access}}));
{{/is_optional_string}}
{{/is_text_edit}}
{{#is_check_box}}
{{#is_tristate}}
    ui_->{{widget}}->setCheckState({{domain_entity.qt.item_var}}_.{{#field_access}}{{field_access}}{{/field_access}}{{^field_access}}{{field}}{{/field_access}}
        ? (*{{domain_entity.qt.item_var}}_.{{#field_access}}{{field_access}}{{/field_access}}{{^field_access}}{{field}}{{/field_access}} ? Qt::Checked : Qt::Unchecked)
        : Qt::PartiallyChecked);
{{/is_tristate}}
{{^is_tristate}}
    ui_->{{widget}}->setChecked({{domain_entity.qt.item_var}}_.{{#field_access}}{{field_access}}{{/field_access}}{{^field_access}}{{field}}{{/field_access}});
{{/is_tristate}}
{{/is_check_box}}
{{#is_spin_box}}
{{#is_nullable_int}}
    ui_->{{widget}}->setValue({{domain_entity.qt.item_var}}_.{{#field_access}}{{field_access}}{{/field_access}}{{^field_access}}{{field}}{{/field_access}}.value_or(
        ui_->{{widget}}->minimum()));
{{/is_nullable_int}}
{{^is_nullable_int}}
    ui_->{{widget}}->setValue({{domain_entity.qt.item_var}}_.{{#field_access}}{{field_access}}{{/field_access}}{{^field_access}}{{field}}{{/field_access}});
{{/is_nullable_int}}
{{/is_spin_box}}
{{#is_static_combo}}
    {
        const int idx = ui_->{{widget}}->findData(
            QString::fromStdString({{domain_entity.qt.item_var}}_.{{#field_access}}{{field_access}}{{/field_access}}{{^field_access}}{{field}}{{/field_access}}));
        if (idx >= 0) ui_->{{widget}}->setCurrentIndex(idx);
    }
{{/is_static_combo}}
{{#is_dynamic_combo}}
    {
        const auto val = QString::fromStdString({{domain_entity.qt.item_var}}_.{{#field_access}}{{field_access}}{{/field_access}}{{^field_access}}{{field}}{{/field_access}});
        const int idx = ui_->{{widget}}->findData(val);
        if (idx >= 0) ui_->{{widget}}->setCurrentIndex(idx);
    }
{{/is_dynamic_combo}}
{{/domain_entity.qt.detail_fields}}

    populateProvenance({{domain_entity.qt.item_var}}_.{{#domain_entity.qt.version_access}}{{domain_entity.qt.version_access}}{{/domain_entity.qt.version_access}}{{^domain_entity.qt.version_access}}version{{/domain_entity.qt.version_access}},
                       {{domain_entity.qt.item_var}}_.{{#domain_entity.qt.modified_by_access}}{{domain_entity.qt.modified_by_access}}{{/domain_entity.qt.modified_by_access}}{{^domain_entity.qt.modified_by_access}}modified_by{{/domain_entity.qt.modified_by_access}},
                       {{domain_entity.qt.item_var}}_.{{#domain_entity.qt.performed_by_access}}{{domain_entity.qt.performed_by_access}}{{/domain_entity.qt.performed_by_access}}{{^domain_entity.qt.performed_by_access}}performed_by{{/domain_entity.qt.performed_by_access}},
                       {{domain_entity.qt.item_var}}_.{{#domain_entity.qt.recorded_at_access}}{{domain_entity.qt.recorded_at_access}}{{/domain_entity.qt.recorded_at_access}}{{^domain_entity.qt.recorded_at_access}}recorded_at{{/domain_entity.qt.recorded_at_access}},
                       {{domain_entity.qt.item_var}}_.{{#domain_entity.qt.change_reason_code_access}}{{domain_entity.qt.change_reason_code_access}}{{/domain_entity.qt.change_reason_code_access}}{{^domain_entity.qt.change_reason_code_access}}change_reason_code{{/domain_entity.qt.change_reason_code_access}},
                       {{domain_entity.qt.item_var}}_.{{#domain_entity.qt.change_commentary_access}}{{domain_entity.qt.change_commentary_access}}{{/domain_entity.qt.change_commentary_access}}{{^domain_entity.qt.change_commentary_access}}change_commentary{{/domain_entity.qt.change_commentary_access}});

    hasChanges_ = false;
    updateSaveButtonState();
}

void {{domain_entity.entity_pascal}}DetailDialog::update{{domain_entity.entity_pascal_short}}FromUi() {
{{#domain_entity.qt.detail_fields}}
{{#is_key}}
    if (createMode_) {
{{#is_line_edit}}
        {{domain_entity.qt.item_var}}_.{{#field_access}}{{field_access}}{{/field_access}}{{^field_access}}{{field}}{{/field_access}} = ui_->{{widget}}->text().trimmed().toStdString();
{{/is_line_edit}}
{{#is_static_combo}}
        {{domain_entity.qt.item_var}}_.{{#field_access}}{{field_access}}{{/field_access}}{{^field_access}}{{field}}{{/field_access}} = ui_->{{widget}}->currentData().toString().trimmed().toStdString();
{{/is_static_combo}}
{{#is_dynamic_combo}}
        {{domain_entity.qt.item_var}}_.{{#field_access}}{{field_access}}{{/field_access}}{{^field_access}}{{field}}{{/field_access}} = ui_->{{widget}}->currentData().toString().trimmed().toStdString();
{{/is_dynamic_combo}}
    }
{{/is_key}}
{{^is_key}}
{{#is_line_edit}}
{{^is_uuid}}
{{^is_optional_uuid}}
{{#is_optional_string}}
    {
        const auto {{field}}_str = ui_->{{widget}}->text().trimmed().toStdString();
        {{domain_entity.qt.item_var}}_.{{#field_access}}{{field_access}}{{/field_access}}{{^field_access}}{{field}}{{/field_access}} =
            {{field}}_str.empty() ? std::nullopt : std::optional<std::string>({{field}}_str);
    }
{{/is_optional_string}}
{{^is_optional_string}}
    {{domain_entity.qt.item_var}}_.{{#field_access}}{{field_access}}{{/field_access}}{{^field_access}}{{field}}{{/field_access}} = ui_->{{widget}}->text().trimmed().toStdString();
{{/is_optional_string}}
{{/is_optional_uuid}}
{{/is_uuid}}
{{/is_line_edit}}
{{#is_text_edit}}
{{#is_optional_string}}
    {
        const auto {{field}}_str = ui_->{{widget}}->toPlainText().trimmed().toStdString();
        {{domain_entity.qt.item_var}}_.{{#field_access}}{{field_access}}{{/field_access}}{{^field_access}}{{field}}{{/field_access}} =
            {{field}}_str.empty() ? std::nullopt : std::optional<std::string>({{field}}_str);
    }
{{/is_optional_string}}
{{^is_optional_string}}
    {{domain_entity.qt.item_var}}_.{{#field_access}}{{field_access}}{{/field_access}}{{^field_access}}{{field}}{{/field_access}} = ui_->{{widget}}->toPlainText().trimmed().toStdString();
{{/is_optional_string}}
{{/is_text_edit}}
{{#is_check_box}}
{{#is_tristate}}
    switch (ui_->{{widget}}->checkState()) {
    case Qt::Checked:
        {{domain_entity.qt.item_var}}_.{{#field_access}}{{field_access}}{{/field_access}}{{^field_access}}{{field}}{{/field_access}} = std::optional<bool>(true);
        break;
    case Qt::Unchecked:
        {{domain_entity.qt.item_var}}_.{{#field_access}}{{field_access}}{{/field_access}}{{^field_access}}{{field}}{{/field_access}} = std::optional<bool>(false);
        break;
    default:
        {{domain_entity.qt.item_var}}_.{{#field_access}}{{field_access}}{{/field_access}}{{^field_access}}{{field}}{{/field_access}} = std::nullopt;
        break;
    }
{{/is_tristate}}
{{^is_tristate}}
    {{domain_entity.qt.item_var}}_.{{#field_access}}{{field_access}}{{/field_access}}{{^field_access}}{{field}}{{/field_access}} = ui_->{{widget}}->isChecked();
{{/is_tristate}}
{{/is_check_box}}
{{#is_spin_box}}
{{#is_nullable_int}}
    if (ui_->{{widget}}->value() == ui_->{{widget}}->minimum())
        {{domain_entity.qt.item_var}}_.{{#field_access}}{{field_access}}{{/field_access}}{{^field_access}}{{field}}{{/field_access}} = std::nullopt;
    else
        {{domain_entity.qt.item_var}}_.{{#field_access}}{{field_access}}{{/field_access}}{{^field_access}}{{field}}{{/field_access}} = ui_->{{widget}}->value();
{{/is_nullable_int}}
{{^is_nullable_int}}
    {{domain_entity.qt.item_var}}_.{{#field_access}}{{field_access}}{{/field_access}}{{^field_access}}{{field}}{{/field_access}} = ui_->{{widget}}->value();
{{/is_nullable_int}}
{{/is_spin_box}}
{{#is_static_combo}}
    {{domain_entity.qt.item_var}}_.{{#field_access}}{{field_access}}{{/field_access}}{{^field_access}}{{field}}{{/field_access}} =
        ui_->{{widget}}->currentData().toString().toStdString();
{{/is_static_combo}}
{{#is_dynamic_combo}}
    {{domain_entity.qt.item_var}}_.{{#field_access}}{{field_access}}{{/field_access}}{{^field_access}}{{field}}{{/field_access}} =
        ui_->{{widget}}->currentData().toString().toStdString();
{{/is_dynamic_combo}}
{{/is_key}}
{{/domain_entity.qt.detail_fields}}
    {{domain_entity.qt.item_var}}_.{{#domain_entity.qt.modified_by_access}}{{domain_entity.qt.modified_by_access}}{{/domain_entity.qt.modified_by_access}}{{^domain_entity.qt.modified_by_access}}modified_by{{/domain_entity.qt.modified_by_access}} = username_;
}

void {{domain_entity.entity_pascal}}DetailDialog::onCodeChanged(const QString& /* text */) {
    hasChanges_ = true;
    updateSaveButtonState();
}

void {{domain_entity.entity_pascal}}DetailDialog::onFieldChanged() {
    hasChanges_ = true;
    updateSaveButtonState();
}

void {{domain_entity.entity_pascal}}DetailDialog::updateSaveButtonState() {
    bool canSave = hasChanges_ && validateInput() && !readOnly_;
    ui_->saveButton->setEnabled(canSave);
}

bool {{domain_entity.entity_pascal}}DetailDialog::validateInput() {
{{#domain_entity.qt.required_fields}}
    const QString {{field}}_val = ui_->{{widget}}->text().trimmed();
{{/domain_entity.qt.required_fields}}
{{#domain_entity.qt.required_dynamic_combo_fields}}
    const bool {{field}}_selected = ui_->{{widget}}->currentIndex() >= 0;
{{/domain_entity.qt.required_dynamic_combo_fields}}

    return true
{{#domain_entity.qt.required_fields}}
        && !{{field}}_val.isEmpty()
{{/domain_entity.qt.required_fields}}
{{#domain_entity.qt.required_dynamic_combo_fields}}
        && {{field}}_selected
{{/domain_entity.qt.required_dynamic_combo_fields}}
    ;
}

void {{domain_entity.entity_pascal}}DetailDialog::onSaveClicked() {
    if (!clientManager_ || !clientManager_->isConnected()) {
        MessageBoxHelper::warning(this, "Disconnected",
            "Cannot save {{domain_entity.entity_singular_words}} while disconnected from server.");
        return;
    }

    if (!validateInput()) {
        MessageBoxHelper::warning(this, "Invalid Input",
            "Please fill in all required fields.");
        return;
    }

    const auto crOpType = createMode_
        ? ChangeReasonDialog::OperationType::Create
        : ChangeReasonDialog::OperationType::Amend;
    const auto crSel = promptChangeReason(crOpType, hasChanges_,
        createMode_ ? "system" : "common");
    if (!crSel) return;
    {{domain_entity.qt.item_var}}_.change_reason_code = crSel->reason_code;
    {{domain_entity.qt.item_var}}_.change_commentary  = crSel->commentary;

    update{{domain_entity.entity_pascal_short}}FromUi();

    BOOST_LOG_SEV(lg(), info) << "Saving {{domain_entity.entity_singular_words}}: "
        << {{domain_entity.qt.item_var}}_.{{#domain_entity.qt.key_field_access}}{{domain_entity.qt.key_field_access}}{{/domain_entity.qt.key_field_access}}{{^domain_entity.qt.key_field_access}}{{domain_entity.qt.key_field}}{{/domain_entity.qt.key_field_access}};

    QPointer<{{domain_entity.entity_pascal}}DetailDialog> self = this;

    struct SaveResult {
        bool success;
        std::string message;
    };

    auto task = [self, {{domain_entity.qt.item_var}} = {{domain_entity.qt.item_var}}_]() -> SaveResult {
        if (!self || !self->clientManager_) {
            return {false, "Dialog closed"};
        }

        {{domain_entity.qt.save_request_class}} request;
        request.data = {{domain_entity.qt.item_var}};
        auto response_result = self->clientManager_->
            process_authenticated_request(std::move(request));

        if (!response_result) {
            return {false, "Failed to communicate with server"};
        }

        return {response_result->success, response_result->message};
    };

    auto* watcher = new QFutureWatcher<SaveResult>(self);
    connect(watcher, &QFutureWatcher<SaveResult>::finished,
            self, [self, watcher]() {
        auto result = watcher->result();
        watcher->deleteLater();

        if (result.success) {
            BOOST_LOG_SEV(lg(), info) << "{{domain_entity.entity_title}} saved successfully";
            QString code = QString::fromStdString(
                self->{{domain_entity.qt.item_var}}_.{{#domain_entity.qt.key_field_access}}{{domain_entity.qt.key_field_access}}{{/domain_entity.qt.key_field_access}}{{^domain_entity.qt.key_field_access}}{{domain_entity.qt.key_field}}{{/domain_entity.qt.key_field_access}});
            self->hasChanges_ = false;
            self->updateSaveButtonState();
            emit self->{{domain_entity.qt.item_var}}Saved(code);
            self->notifySaveSuccess(tr("{{domain_entity.entity_title}} '%1' saved").arg(code));
        } else {
            BOOST_LOG_SEV(lg(), error) << "Save failed: " << result.message;
            QString errorMsg = QString::fromStdString(result.message);
            emit self->errorMessage(errorMsg);
            MessageBoxHelper::critical(self, "Save Failed", errorMsg);
        }
    });

    QFuture<SaveResult> future = QtConcurrent::run(task);
    watcher->setFuture(future);
}

void {{domain_entity.entity_pascal}}DetailDialog::onDeleteClicked() {
    if (!clientManager_ || !clientManager_->isConnected()) {
        MessageBoxHelper::warning(this, "Disconnected",
            "Cannot delete {{domain_entity.entity_singular_words}} while disconnected from server.");
        return;
    }

    QString code = QString::fromStdString(
        {{domain_entity.qt.item_var}}_.{{#domain_entity.qt.key_field_access}}{{domain_entity.qt.key_field_access}}{{/domain_entity.qt.key_field_access}}{{^domain_entity.qt.key_field_access}}{{domain_entity.qt.key_field}}{{/domain_entity.qt.key_field_access}});
    auto reply = MessageBoxHelper::question(this, "Delete {{domain_entity.entity_title}}",
        QString("Are you sure you want to delete {{domain_entity.entity_singular_words}} '%1'?").arg(code),
        QMessageBox::Yes | QMessageBox::No);

    if (reply != QMessageBox::Yes) {
        return;
    }

    const auto crSel = promptChangeReason(
        ChangeReasonDialog::OperationType::Delete, false);
    if (!crSel) return;

    BOOST_LOG_SEV(lg(), info) << "Deleting {{domain_entity.entity_singular_words}}: "
        << {{domain_entity.qt.item_var}}_.{{#domain_entity.qt.key_field_access}}{{domain_entity.qt.key_field_access}}{{/domain_entity.qt.key_field_access}}{{^domain_entity.qt.key_field_access}}{{domain_entity.qt.key_field}}{{/domain_entity.qt.key_field_access}};

    QPointer<{{domain_entity.entity_pascal}}DetailDialog> self = this;

    struct DeleteResult {
        bool success;
        std::string message;
    };

{{#domain_entity.qt.has_uuid_primary_key}}
    auto task = [self, id_str = boost::uuids::to_string({{domain_entity.qt.item_var}}_.{{#domain_entity.qt.id_access}}{{domain_entity.qt.id_access}}{{/domain_entity.qt.id_access}}{{^domain_entity.qt.id_access}}id{{/domain_entity.qt.id_access}})]() -> DeleteResult {
        if (!self || !self->clientManager_) {
            return {false, "Dialog closed"};
        }

        {{domain_entity.qt.delete_request_class}} request;
{{#domain_entity.qt.delete_request_id_is_plural}}
        request.{{domain_entity.qt.delete_request_id_field}} = {id_str};
{{/domain_entity.qt.delete_request_id_is_plural}}
{{^domain_entity.qt.delete_request_id_is_plural}}
        request.{{domain_entity.qt.delete_request_id_field}} = id_str;
{{/domain_entity.qt.delete_request_id_is_plural}}
        auto response_result = self->clientManager_->
            process_authenticated_request(std::move(request));

        if (!response_result) {
            return {false, "Failed to communicate with server"};
        }

        return {response_result->success, response_result->message};
    };
{{/domain_entity.qt.has_uuid_primary_key}}
{{^domain_entity.qt.has_uuid_primary_key}}
    auto task = [self, code = {{domain_entity.qt.item_var}}_.{{#domain_entity.qt.key_field_access}}{{domain_entity.qt.key_field_access}}{{/domain_entity.qt.key_field_access}}{{^domain_entity.qt.key_field_access}}{{domain_entity.qt.key_field}}{{/domain_entity.qt.key_field_access}}]() -> DeleteResult {
        if (!self || !self->clientManager_) {
            return {false, "Dialog closed"};
        }

        {{domain_entity.qt.delete_request_class}} request;
{{#domain_entity.qt.delete_request_id_field}}
{{#domain_entity.qt.delete_request_id_is_plural}}
        request.{{domain_entity.qt.delete_request_id_field}} = {code};
{{/domain_entity.qt.delete_request_id_is_plural}}
{{^domain_entity.qt.delete_request_id_is_plural}}
        request.{{domain_entity.qt.delete_request_id_field}} = code;
{{/domain_entity.qt.delete_request_id_is_plural}}
{{/domain_entity.qt.delete_request_id_field}}
{{^domain_entity.qt.delete_request_id_field}}
        request.codes = {code};
{{/domain_entity.qt.delete_request_id_field}}
        auto response_result = self->clientManager_->
            process_authenticated_request(std::move(request));

        if (!response_result) {
            return {false, "Failed to communicate with server"};
        }

        return {response_result->success, response_result->message};
    };
{{/domain_entity.qt.has_uuid_primary_key}}

    auto* watcher = new QFutureWatcher<DeleteResult>(self);
    connect(watcher, &QFutureWatcher<DeleteResult>::finished,
            self, [self, code, watcher]() {
        auto result = watcher->result();
        watcher->deleteLater();

        if (result.success) {
            BOOST_LOG_SEV(lg(), info) << "{{domain_entity.entity_title}} deleted successfully";
            emit self->statusMessage(
                QString("{{domain_entity.entity_title}} '%1' deleted").arg(code));
            emit self->{{domain_entity.qt.item_var}}Deleted(code);
            self->requestClose();
        } else {
            BOOST_LOG_SEV(lg(), error) << "Delete failed: " << result.message;
            QString errorMsg = QString::fromStdString(result.message);
            emit self->errorMessage(errorMsg);
            MessageBoxHelper::critical(self, "Delete Failed", errorMsg);
        }
    });

    QFuture<DeleteResult> future = QtConcurrent::run(task);
    watcher->setFuture(future);
}

}

See also

Emacs 29.1 (Org mode 9.6.6)