From 4a29e2cccad1e01c1965d807bb4d621cdd648992 Mon Sep 17 00:00:00 2001 From: "tlepoix@localhost" Date: Sun, 25 May 2025 22:51:25 +0200 Subject: [PATCH 01/37] refactor : AboutDialog QPushButton -> QDialogButtonBox --- src/ui/qt/about_dialog.cpp | 2 +- src/ui/qt/about_dialog.hpp | 4 +++- src/ui/qt/about_dialog.ui | 21 +++++---------------- 3 files changed, 9 insertions(+), 18 deletions(-) diff --git a/src/ui/qt/about_dialog.cpp b/src/ui/qt/about_dialog.cpp index 6a10773a..94ad2805 100644 --- a/src/ui/qt/about_dialog.cpp +++ b/src/ui/qt/about_dialog.cpp @@ -47,7 +47,7 @@ AboutDialog::AboutDialog(QWidget* parent) AboutDialog::~AboutDialog() = default; //****************************************************************************** -void AboutDialog::on_pb_ok_clicked() { +void AboutDialog::on_dbb_ok_clicked(QAbstractButton* /*button*/) { accept(); } diff --git a/src/ui/qt/about_dialog.hpp b/src/ui/qt/about_dialog.hpp index d9a45f43..e9586a2d 100644 --- a/src/ui/qt/about_dialog.hpp +++ b/src/ui/qt/about_dialog.hpp @@ -11,6 +11,8 @@ #include +class QAbstractButton; + namespace Ui { class AboutDialog; } // namespace Ui @@ -24,7 +26,7 @@ class AboutDialog : public QDialog { std::unique_ptr ui; private slots: - void on_pb_ok_clicked(); + void on_dbb_ok_clicked(QAbstractButton* button); public: explicit AboutDialog(QWidget* parent = nullptr); diff --git a/src/ui/qt/about_dialog.ui b/src/ui/qt/about_dialog.ui index 416dd97c..1e2a6ee4 100644 --- a/src/ui/qt/about_dialog.ui +++ b/src/ui/qt/about_dialog.ui @@ -127,22 +127,11 @@ the GPL license version 3 or later. - - - - - Qt::Horizontal - - - - - - - OK - - - - + + + QDialogButtonBox::Ok + + From 28434801c0e911838cb9e99421b4088ac020b2e1 Mon Sep 17 00:00:00 2001 From: "tlepoix@localhost" Date: Thu, 5 Jun 2025 12:08:56 +0200 Subject: [PATCH 02/37] add GUI edition capability + EditModelGlobal --- src/CMakeLists.txt | 3 + src/app/openemsh.cpp | 42 +++++++ src/app/openemsh.hpp | 4 + src/ui/qt/edit/edit_dialog.cpp | 44 ++++++++ src/ui/qt/edit/edit_dialog.hpp | 36 ++++++ src/ui/qt/edit/edit_dialog.ui | 36 ++++++ src/ui/qt/edit/edit_model.cpp | 88 +++++++++++++++ src/ui/qt/edit/edit_model.hpp | 41 +++++++ src/ui/qt/edit/edit_model_global.cpp | 63 +++++++++++ src/ui/qt/edit/edit_model_global.hpp | 27 +++++ src/ui/qt/main_window.cpp | 58 +++++++++- src/ui/qt/main_window.hpp | 6 +- src/ui/qt/main_window.ui | 9 ++ .../qt/processing_view/processing_scene.cpp | 73 ++++++++++++ .../qt/processing_view/processing_scene.hpp | 14 ++- src/ui/qt/utils/qlist_utils.hpp | 40 +++++++ test/unit/app/test_openemsh.cpp | 104 ++++++++++++++++++ 17 files changed, 680 insertions(+), 8 deletions(-) create mode 100644 src/ui/qt/edit/edit_dialog.cpp create mode 100644 src/ui/qt/edit/edit_dialog.hpp create mode 100644 src/ui/qt/edit/edit_dialog.ui create mode 100644 src/ui/qt/edit/edit_model.cpp create mode 100644 src/ui/qt/edit/edit_model.hpp create mode 100644 src/ui/qt/edit/edit_model_global.cpp create mode 100644 src/ui/qt/edit/edit_model_global.hpp create mode 100644 src/ui/qt/utils/qlist_utils.hpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index cf914fd3..a93cc15c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -80,6 +80,9 @@ target_sources( openemsh_bin "${CMAKE_CURRENT_SOURCE_DIR}/ui/qt/utils/nodegraph/node.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/ui/qt/utils/nodegraph/container.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/ui/qt/utils/nodegraph/wire.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/ui/qt/edit/edit_dialog.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/ui/qt/edit/edit_model.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/ui/qt/edit/edit_model_global.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/ui/qt/processing_view/processing_axis.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/ui/qt/processing_view/processing_plane.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/ui/qt/processing_view/processing_style.cpp" diff --git a/src/app/openemsh.cpp b/src/app/openemsh.cpp index cdef7659..bda69b63 100644 --- a/src/app/openemsh.cpp +++ b/src/app/openemsh.cpp @@ -43,6 +43,43 @@ optional next(Step step) { } } +//****************************************************************************** +set that_and_after(Step step) { + set out; + switch(step) { + case Step::DETECT_CONFLICT_EIP: + out.emplace(Step::DETECT_CONFLICT_EIP); + [[fallthrough]]; + case Step::DETECT_CONFLICT_CE: + out.emplace(Step::DETECT_CONFLICT_CE); + [[fallthrough]]; + case Step::DETECT_NON_CONFLICTING_EDGES: + out.emplace(Step::DETECT_NON_CONFLICTING_EDGES); + [[fallthrough]]; + case Step::ADD_FIXED_MLP: + out.emplace(Step::ADD_FIXED_MLP); + [[fallthrough]]; + case Step::SOLVE_ALL_EIP: + out.emplace(Step::SOLVE_ALL_EIP); + [[fallthrough]]; + case Step::SOLVE_ALL_CE: + out.emplace(Step::SOLVE_ALL_CE); + [[fallthrough]]; + case Step::DETECT_AND_SOLVE_TCMLP: + out.emplace(Step::DETECT_AND_SOLVE_TCMLP); + [[fallthrough]]; + case Step::DETECT_INTERVALS: + out.emplace(Step::DETECT_INTERVALS); + [[fallthrough]]; + case Step::MESH: + out.emplace(Step::MESH); + break; + default: + unreachable(); + } + return out; +} + //****************************************************************************** OpenEMSH::OpenEMSH(Params params) : params(std::move(params)) @@ -158,6 +195,11 @@ void OpenEMSH::run_next_step() const { } } +//****************************************************************************** +void OpenEMSH::run_from_step(Step step) const { + run(that_and_after(step)); +} + //****************************************************************************** void OpenEMSH::go_before(Step step) const { auto& c = Caretaker::singleton(); diff --git a/src/app/openemsh.hpp b/src/app/openemsh.hpp index f127b9af..33d02316 100644 --- a/src/app/openemsh.hpp +++ b/src/app/openemsh.hpp @@ -64,6 +64,7 @@ class OpenEMSH { void run(std::set const& steps) const; void run_all_steps() const; void run_next_step() const; + void run_from_step(Step step) const; void go_before(Step step) const; void go_before_previous_step() const; void write() const; @@ -85,4 +86,7 @@ class Annotation : public IAnnotation { //****************************************************************************** std::optional next(Step step); +//****************************************************************************** +std::set that_and_after(Step step); + } // namespace app diff --git a/src/ui/qt/edit/edit_dialog.cpp b/src/ui/qt/edit/edit_dialog.cpp new file mode 100644 index 00000000..de6ca167 --- /dev/null +++ b/src/ui/qt/edit/edit_dialog.cpp @@ -0,0 +1,44 @@ +///***************************************************************************** +/// @date Feb 2021 +/// @copyright GPL-3.0-or-later +/// @author Thomas Lepoix +///***************************************************************************** + +#include "edit_model.hpp" + +#include "ui_edit_dialog.h" +#include "edit_dialog.hpp" + +namespace ui::qt { + +using namespace std; + +//****************************************************************************** +EditDialog::EditDialog(EditModel* model, QString const& title, QWidget* parent) +: QDialog(parent, Qt::Dialog) +, ui(make_unique()) { + ui->setupUi(this); + setWindowIcon(QPixmap(":/openemsh.ico")); + setWindowTitle(windowTitle() + " " + title); + + ui->tv_properties->setModel(model); + ui->tv_properties->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); +} + +//****************************************************************************** +EditDialog::~EditDialog() = default; + +//****************************************************************************** +void EditDialog::on_dbb_ok_accepted() { + setCursor(Qt::WaitCursor); + static_cast(ui->tv_properties->model())->commit(); + unsetCursor(); + accept(); +} + +//****************************************************************************** +void EditDialog::on_dbb_ok_rejected() { + reject(); +} + +} // namespace ui::qt diff --git a/src/ui/qt/edit/edit_dialog.hpp b/src/ui/qt/edit/edit_dialog.hpp new file mode 100644 index 00000000..8bbcbf81 --- /dev/null +++ b/src/ui/qt/edit/edit_dialog.hpp @@ -0,0 +1,36 @@ +///***************************************************************************** +/// @date Feb 2021 +/// @copyright GPL-3.0-or-later +/// @author Thomas Lepoix +///***************************************************************************** + +#pragma once + +#include + +#include + +namespace Ui { +class EditDialog; +} // namespace Ui + +namespace ui::qt { + +class EditModel; + +//****************************************************************************** +class EditDialog : public QDialog { + Q_OBJECT +private: + std::unique_ptr ui; + +private slots: + void on_dbb_ok_accepted(); + void on_dbb_ok_rejected(); + +public: + explicit EditDialog(EditModel* model, QString const& title = QString(), QWidget* parent = nullptr); + ~EditDialog() override; +}; + +} // namespace ui::qt diff --git a/src/ui/qt/edit/edit_dialog.ui b/src/ui/qt/edit/edit_dialog.ui new file mode 100644 index 00000000..45d6aa12 --- /dev/null +++ b/src/ui/qt/edit/edit_dialog.ui @@ -0,0 +1,36 @@ + + + EditDialog + + + Edit + + + + QLayout::SetFixedSize + + + + + + QAbstractScrollArea::AdjustToContents + + + true + + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + diff --git a/src/ui/qt/edit/edit_model.cpp b/src/ui/qt/edit/edit_model.cpp new file mode 100644 index 00000000..5ab2490b --- /dev/null +++ b/src/ui/qt/edit/edit_model.cpp @@ -0,0 +1,88 @@ +///***************************************************************************** +/// @date Feb 2021 +/// @copyright GPL-3.0-or-later +/// @author Thomas Lepoix +///***************************************************************************** + +#include "edit_model.hpp" + +namespace ui::qt { + +using namespace std; + +//****************************************************************************** +QStandardItem* EditModel::make_property_item(QString const& str) { + auto* item = new QStandardItem(str); + item->setFlags(item->flags() & ~Qt::ItemIsEditable); + return item; +} + +//****************************************************************************** +QStandardItem* EditModel::make_bool_item(bool in) { + auto* item = new QStandardItem(); + item->setCheckable(true); + if(in) + item->setCheckState(Qt::Checked); + else + item->setCheckState(Qt::Unchecked); + return item; +} + +//****************************************************************************** +bool EditModel::try_to_double(QString const& in, double& out) { + bool does_succeed = false; + auto result = in.toDouble(&does_succeed); + if(does_succeed) + out = result; + return does_succeed; +} + +//****************************************************************************** +bool EditModel::try_to_ulong(QString const& in, std::size_t& out) { + bool does_succeed = false; + auto result = in.toULong(&does_succeed); + if(does_succeed) + out = result; + return does_succeed; +} + +//****************************************************************************** +bool EditModel::try_to_bool(Qt::CheckState const in, bool& out) { + bool does_succeed = false; + switch(in) { + case Qt::Checked: + does_succeed = true; + out = true; + break; + case Qt::Unchecked: + does_succeed = true; + out = false; + break; + default: + break; + } + return does_succeed; +} + +//****************************************************************************** +EditModel* EditModel::make(nodegraph::Node* node, QObject* parent) { + switch(node->type()) { + default: + return nullptr; + } +} + +//****************************************************************************** +EditModel::EditModel(QObject* parent) +: QStandardItemModel(parent) +{ + setColumnCount(2); + // TODO maybe 3 columns { "Property", "Old value", "New value" } would be better for UI + // especially for setting lambda to 2 by default + setHorizontalHeaderLabels({ "Property", "Value" }); +} + +//****************************************************************************** +void EditModel::commit() {} + +} // namespace ui::qt diff --git a/src/ui/qt/edit/edit_model.hpp b/src/ui/qt/edit/edit_model.hpp new file mode 100644 index 00000000..8d78c42e --- /dev/null +++ b/src/ui/qt/edit/edit_model.hpp @@ -0,0 +1,41 @@ +///***************************************************************************** +/// @date Feb 2021 +/// @copyright GPL-3.0-or-later +/// @author Thomas Lepoix +///***************************************************************************** + +#pragma once + +#include + +#include "app/steps.hpp" + +namespace ui::qt { + +namespace nodegraph { +class Node; +} // namespace nodegraph + +//****************************************************************************** +class EditModel : public QStandardItemModel { + Q_OBJECT +public: + static EditModel* make(nodegraph::Node* node, QObject* parent = nullptr); + + explicit EditModel(QObject* parent = nullptr); + virtual void commit(); + +protected: + static QStandardItem* make_property_item(QString const& str); + static QStandardItem* make_bool_item(bool in); + static bool is_true(bool const val) { return val; } + static bool try_to_double(QString const& in, double& out); + static bool try_to_ulong(QString const& in, std::size_t& out); + static bool try_to_bool(Qt::CheckState const in, bool& out); + +signals: + void request_to_go_before(app::Step step); + void edited(app::Step const redo_from); +}; + +} // namespace ui::qt diff --git a/src/ui/qt/edit/edit_model_global.cpp b/src/ui/qt/edit/edit_model_global.cpp new file mode 100644 index 00000000..ce8b84dd --- /dev/null +++ b/src/ui/qt/edit/edit_model_global.cpp @@ -0,0 +1,63 @@ +///***************************************************************************** +/// @date Feb 2021 +/// @copyright GPL-3.0-or-later +/// @author Thomas Lepoix +///***************************************************************************** + +#include +#include + +#include "app/steps.hpp" +#include "domain/global.hpp" + +#include "edit_model_global.hpp" + +namespace ui::qt { + +//****************************************************************************** +EditModelGlobal::EditModelGlobal(domain::GlobalParams* global, QObject* parent) +: EditModel(parent) +, global(global) +{ + setRowCount(6); + for(std::size_t i = 0; auto& str : { + "metal_res", + "substrate_res", + "proximity_limit", + "lambda", + "lmin", + "dmax" + }) { + setItem(i++, 0, make_property_item(str)); + } + + auto const& params = global->get_current_state(); + setItem(0, 1, new QStandardItem(QString::number(params.metal_res))); + setItem(1, 1, new QStandardItem(QString::number(params.substrate_res))); + setItem(2, 1, new QStandardItem(QString::number(params.proximity_limit))); + setItem(3, 1, new QStandardItem(QString::number(params.lambda))); + setItem(4, 1, new QStandardItem(QString::number(params.lmin))); + setItem(5, 1, new QStandardItem(QString::number(params.dmax))); +} + +//****************************************************************************** +void EditModelGlobal::commit() { + domain::Params params; + + std::array does_succeed = { + try_to_double(item(0, 1)->text(), params.metal_res), + try_to_double(item(1, 1)->text(), params.substrate_res), + try_to_double(item(2, 1)->text(), params.proximity_limit), + try_to_double(item(3, 1)->text(), params.lambda), + try_to_ulong(item(4, 1)->text(), params.lmin), + try_to_double(item(5, 1)->text(), params.dmax) + }; + + if(std::ranges::all_of(does_succeed, is_true)) { + emit request_to_go_before(app::Step::DETECT_CONFLICT_EIP); + global->set_next_state(params); + emit edited(app::Step::DETECT_CONFLICT_EIP); + } +} + +} // namespace ui::qt diff --git a/src/ui/qt/edit/edit_model_global.hpp b/src/ui/qt/edit/edit_model_global.hpp new file mode 100644 index 00000000..e1c2f84a --- /dev/null +++ b/src/ui/qt/edit/edit_model_global.hpp @@ -0,0 +1,27 @@ +///***************************************************************************** +/// @date Feb 2021 +/// @copyright GPL-3.0-or-later +/// @author Thomas Lepoix +///***************************************************************************** + +#pragma once + +#include "edit_model.hpp" + +namespace domain { +class GlobalParams; +} // namespace domain + +namespace ui::qt { + +//****************************************************************************** +class EditModelGlobal : public EditModel { +public: + explicit EditModelGlobal(domain::GlobalParams* global, QObject* parent = nullptr); + void commit() override; + +private: + domain::GlobalParams* global; +}; + +} // namespace ui::qt diff --git a/src/ui/qt/main_window.cpp b/src/ui/qt/main_window.cpp index 80dc0362..d4696e89 100644 --- a/src/ui/qt/main_window.cpp +++ b/src/ui/qt/main_window.cpp @@ -8,6 +8,9 @@ #include #include "domain/geometrics/space.hpp" +#include "edit/edit_dialog.hpp" +#include "edit/edit_model.hpp" +#include "edit/edit_model_global.hpp" #include "processing_view/processing_view.hpp" #include "structure_view/structure_view.hpp" #include "utils/state_management.hpp" @@ -40,7 +43,6 @@ MainWindow::MainWindow(app::OpenEMSH& oemsh, QWidget* parent) } oemsh.parse(); - oemsh.run_all_steps(); ui->structure_view->init(&oemsh.get_board()); ui->processing_view->init(&oemsh.get_board()); @@ -235,6 +237,29 @@ void MainWindow::on_tb_processing_zoom_out_clicked() { ui->processing_view->scale(1 / 1.2, 1 / 1.2); } +//****************************************************************************** +void MainWindow::on_a_edit_triggered() { + auto* widget = static_cast(ui->toolBar->widgetForAction(ui->a_edit)); + widget->setDown(true); + ui->processing_view->get_current_state().scene->edit_selected_nodes(widget->mapToGlobal(widget->rect().bottomLeft())); + widget->setDown(false); +} + +//****************************************************************************** +void MainWindow::edit_global_params() { + EditModelGlobal model(oemsh.get_board().global_params.get()); + EditDialog edit(&model, "global parameters"); + connect( + &model, &EditModel::request_to_go_before, + [this](app::Step step) { + this->oemsh.go_before(step); + }); + connect( + &model, &EditModel::edited, + this, &MainWindow::handle_edition); + edit.exec(); +} + //****************************************************************************** void MainWindow::on_a_mesh_prev_triggered() { setCursor(Qt::WaitCursor); @@ -302,15 +327,26 @@ void MainWindow::make_current_state_view() { ui->processing_view->states[t].scene, &ProcessingScene::select_counterparts); } + connect( + ui->processing_view->states[t].scene, &ProcessingScene::edit_global_params, + this, &MainWindow::edit_global_params); + + connect( + ui->processing_view->states[t].scene, &ProcessingScene::request_to_go_before, + [this](app::Step step) { + this->oemsh.go_before(step); + }); + + connect( + ui->processing_view->states[t].scene, &ProcessingScene::edited, + this, &MainWindow::handle_edition); + update_navigation_visibility(); } //****************************************************************************** -void MainWindow::handle_edition(std::set const& to_redo) { -// Caretaker::singleton().remember_current_timepoint(); - -// if(ui->a_mesh_auto->is_checked()) // TODO may not be that useful since editing at once two things from different steps will discard newer objects edits?? - oemsh.run(to_redo); +void MainWindow::handle_edition(app::Step const redo_from) { + oemsh.run_from_step(redo_from); Caretaker::singleton().remember_current_timepoint(); make_current_state_view(); @@ -324,4 +360,14 @@ void MainWindow::update_navigation_visibility() { ui->a_mesh_next->setEnabled(oemsh.can_run_a_next_step()); }; + +//****************************************************************************** +void MainWindow::keyPressEvent(QKeyEvent* event) { + if(event->key() == Qt::Key_E) { + on_a_edit_triggered(); + } else { + QWidget::keyPressEvent(event); + } +} + } // namespace ui::qt diff --git a/src/ui/qt/main_window.hpp b/src/ui/qt/main_window.hpp index da7ba873..d4bece79 100644 --- a/src/ui/qt/main_window.hpp +++ b/src/ui/qt/main_window.hpp @@ -35,6 +35,7 @@ class MainWindow : public QMainWindow { void go_to_current_state(); void make_current_state_view(); void go_to_or_make_current_state(); + void edit_global_params(); private slots: void on_a_about_triggered(); @@ -62,17 +63,20 @@ private slots: void on_tb_structure_zoom_out_clicked(); void on_tb_processing_zoom_in_clicked(); void on_tb_processing_zoom_out_clicked(); + void on_a_edit_triggered(); void on_a_mesh_prev_triggered(); void on_a_mesh_next_triggered(); void on_a_undo_triggered(); void on_a_redo_triggered(); - void handle_edition(std::set const& to_redo = {}); // TODO handle_edition / on_edition + void handle_edition(app::Step const redo_from = app::Step::DETECT_CONFLICT_EIP); public: MainWindow(app::OpenEMSH& oemsh, QWidget* parent = nullptr); ~MainWindow() override; +protected: + void keyPressEvent(QKeyEvent* event) override; }; } // namespace ui::qt diff --git a/src/ui/qt/main_window.ui b/src/ui/qt/main_window.ui index 80e41ded..b3924d3a 100644 --- a/src/ui/qt/main_window.ui +++ b/src/ui/qt/main_window.ui @@ -350,6 +350,7 @@ + @@ -392,6 +393,14 @@ Reset view + + + 🖉 + + + Edit parameters + + diff --git a/src/ui/qt/processing_view/processing_scene.cpp b/src/ui/qt/processing_view/processing_scene.cpp index 925a3e02..1a6ceacf 100644 --- a/src/ui/qt/processing_view/processing_scene.cpp +++ b/src/ui/qt/processing_view/processing_scene.cpp @@ -4,6 +4,9 @@ /// @author Thomas Lepoix ///***************************************************************************** +#include +#include + #include "domain/conflicts/conflict_colinear_edges.hpp" #include "domain/conflicts/conflict_edge_in_polygon.hpp" #include "domain/conflicts/conflict_too_close_meshline_policies.hpp" @@ -14,6 +17,9 @@ #include "domain/mesh/meshline.hpp" #include "domain/mesh/meshline_policy.hpp" #include "ui/qt/data_keys.hpp" +#include "ui/qt/edit/edit_dialog.hpp" +#include "ui/qt/edit/edit_model.hpp" +#include "ui/qt/utils/qlist_utils.hpp" #include "utils/unreachable.hpp" #include "processing_axis.hpp" #include "processing_conflict_colinear_edges.hpp" @@ -430,4 +436,71 @@ void ProcessingScene::select_counterparts(QList foreign_items) { } } +//****************************************************************************** +void ProcessingScene::mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event) { + event->accept(); + edit(dynamic_to_qlist(QGraphicsScene::items(event->scenePos())), event->screenPos()); +} + +//****************************************************************************** +void ProcessingScene::edit_selected_nodes(QPoint const& pos) { + edit(selected_nodes(), pos); +} + +//****************************************************************************** +void ProcessingScene::edit(QList nodes, QPoint const& pos) { + static QList const type_index = { + UserTypes::PROCESSING_EDGE, + UserTypes::PROCESSING_INTERVAL, + UserTypes::PROCESSING_MESHLINE_POLICY, + UserTypes::PROCESSING_CONFLICT_CE, + UserTypes::PROCESSING_CONFLICT_EIP, + UserTypes::PROCESSING_CONFLICT_TCMLP + }; + +// items.removeIf([](auto const it) { return it->type() <= QGraphicsItem::UserType; }); // Wire::Type == UserType + nodes.removeIf([](auto const it) { return !type_index.contains(it->type()); }); + + // TODO isolate in own file + auto const make_title = [](QGraphicsItem const* item) { + QString title(item->data(DataKeys::TYPE).toString() + " - " + item->data(DataKeys::ID).toString()); + if(item->type() == ProcessingPolygon::Type) + title.append(" - " + item->data(DataKeys::NAME).toString()); + return title; + }; + + auto const edit_node = [this](nodegraph::Node* node, QString const& title = QString()) { + if(auto* model = EditModel::make(node); model) { + EditDialog edit(model, title); + model->setParent(&edit); + connect( + model, &EditModel::request_to_go_before, + this, &ProcessingScene::request_to_go_before); + connect( + model, &EditModel::edited, + this, &ProcessingScene::edited); + edit.exec(); + } + }; + + if(nodes.isEmpty()) { + clearSelection(); + emit edit_global_params(); + } else if(nodes.size() == 1) { + edit_node(nodes.first(), make_title(nodes.first())); + } else { + QMenu menu; + for(auto* node : nodes) { +// // TODO add entity icon + auto const title = make_title(node); + auto* action = new QAction(title, &menu); + menu.addAction(action); + QObject::connect(action, &QAction::triggered, [&edit_node, node, title]() { + edit_node(node, title); + }); + } + menu.exec(pos); + } +} + } // namespace ui::qt diff --git a/src/ui/qt/processing_view/processing_scene.hpp b/src/ui/qt/processing_view/processing_scene.hpp index 759dbb06..486c8d57 100644 --- a/src/ui/qt/processing_view/processing_scene.hpp +++ b/src/ui/qt/processing_view/processing_scene.hpp @@ -12,7 +12,9 @@ #include #include +#include +#include "app/steps.hpp" #include "domain/geometrics/space.hpp" #include "ui/qt/utils/nodegraph/wire.hpp" #include "utils/concepts.hpp" @@ -45,6 +47,7 @@ class ProcessingScene : public QGraphicsScene { explicit ProcessingScene(ProcessingStyleSelector& style_selector, QObject* parent = nullptr); ~ProcessingScene() override; + void init(); void set_wire_style(nodegraph::Wire::Style style) const; void fit_containers() const; @@ -73,7 +76,8 @@ class ProcessingScene : public QGraphicsScene { void set_display(DisplayMode mode); void set_display_view_axes(domain::ViewAxisSpace const& axes); void set_display_plane(domain::Plane plane); - void init(); + + void edit_selected_nodes(QPoint const& pos); ProcessingStyleSelector& style_selector; @@ -100,6 +104,10 @@ private slots: signals: void selection_changed(QList items); void requires_fit(); + void edit_global_params(); + void request_to_go_before(app::Step step); + void edited(app::Step const redo_from); + public slots: void select_counterparts(QList foreign_items); @@ -120,6 +128,10 @@ public slots: void display_structure_view(); void display_selected_chain(); + void edit(QList nodes, QPoint const& pos); + +protected: + void mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event) override; }; } // namespace ui::qt diff --git a/src/ui/qt/utils/qlist_utils.hpp b/src/ui/qt/utils/qlist_utils.hpp new file mode 100644 index 00000000..536d6cd7 --- /dev/null +++ b/src/ui/qt/utils/qlist_utils.hpp @@ -0,0 +1,40 @@ +///***************************************************************************** +/// @date Feb 2021 +/// @copyright GPL-3.0-or-later +/// @author Thomas Lepoix +///***************************************************************************** + +#include + +#include + +namespace ui::qt { + +// Downcast +//****************************************************************************** +template +requires std::is_base_of_v< + std::remove_reference_t>, + std::remove_reference_t>> +QList dynamic_to_qlist(QList const& list_i) { + QList list_o; + for(I i : list_i) + if(O o = dynamic_cast(i); o) + list_o.emplace_back(o); + return list_o; +} + +// Upcast +//****************************************************************************** +template +//requires std::is_base_of_v< +// std::remove_reference_t>, +// std::remove_reference_t>> +QList to_qlist(QList const& list_i) { + QList list_o; + for(I i : list_i) + list_o.emplace_back(static_cast(i)); + return list_o; +} + +} // namespace ui::qt diff --git a/test/unit/app/test_openemsh.cpp b/test/unit/app/test_openemsh.cpp index 16dbef74..49a0bca6 100644 --- a/test/unit/app/test_openemsh.cpp +++ b/test/unit/app/test_openemsh.cpp @@ -9,6 +9,7 @@ #include "app/openemsh.hpp" /// @test optional next(Step step) +/// @test set that_and_after(Step step) ///***************************************************************************** using namespace app; @@ -78,3 +79,106 @@ SCENARIO("optional next(Step step)", "[app][openemsh]") { } } } + +//****************************************************************************** +SCENARIO("set that_and_after(Step step)", "[app][openemsh]") { + WHEN("Running for DETECT_CONFLICT_EIP") { + THEN("Should return all Steps except those coming before DETECT_CONFLICT_EIP") { + REQUIRE(that_and_after(Step::DETECT_CONFLICT_EIP) == std::set { + Step::DETECT_CONFLICT_EIP, + Step::DETECT_CONFLICT_CE, + Step::DETECT_NON_CONFLICTING_EDGES, + Step::ADD_FIXED_MLP, + Step::SOLVE_ALL_EIP, + Step::SOLVE_ALL_CE, + Step::DETECT_AND_SOLVE_TCMLP, + Step::DETECT_INTERVALS, + Step::MESH + }); + } + } + WHEN("Running for DETECT_CONFLICT_CE") { + THEN("Should return all Steps except those coming before DETECT_CONFLICT_CE") { + REQUIRE(that_and_after(Step::DETECT_CONFLICT_CE) == std::set { + Step::DETECT_CONFLICT_CE, + Step::DETECT_NON_CONFLICTING_EDGES, + Step::ADD_FIXED_MLP, + Step::SOLVE_ALL_EIP, + Step::SOLVE_ALL_CE, + Step::DETECT_AND_SOLVE_TCMLP, + Step::DETECT_INTERVALS, + Step::MESH + }); + } + } + WHEN("Running for DETECT_NON_CONFLICTING_EDGES") { + THEN("Should return all Steps except those coming before DETECT_NON_CONFLICTING_EDGES") { + REQUIRE(that_and_after(Step::DETECT_NON_CONFLICTING_EDGES) == std::set { + Step::DETECT_NON_CONFLICTING_EDGES, + Step::ADD_FIXED_MLP, + Step::SOLVE_ALL_EIP, + Step::SOLVE_ALL_CE, + Step::DETECT_AND_SOLVE_TCMLP, + Step::DETECT_INTERVALS, + Step::MESH + }); + } + } + WHEN("Running for ADD_FIXED_MLP") { + THEN("Should return all Steps except those coming before ADD_FIXED_MLP") { + REQUIRE(that_and_after(Step::ADD_FIXED_MLP) == std::set { + Step::ADD_FIXED_MLP, + Step::SOLVE_ALL_EIP, + Step::SOLVE_ALL_CE, + Step::DETECT_AND_SOLVE_TCMLP, + Step::DETECT_INTERVALS, + Step::MESH + }); + } + } + WHEN("Running for SOLVE_ALL_EIP") { + THEN("Should return all Steps except those coming before SOLVE_ALL_EIP") { + REQUIRE(that_and_after(Step::SOLVE_ALL_EIP) == std::set { + Step::SOLVE_ALL_EIP, + Step::SOLVE_ALL_CE, + Step::DETECT_AND_SOLVE_TCMLP, + Step::DETECT_INTERVALS, + Step::MESH + }); + } + } + WHEN("Running for SOLVE_ALL_CE") { + THEN("Should return all Steps except those coming before SOLVE_ALL_CE") { + REQUIRE(that_and_after(Step::SOLVE_ALL_CE) == std::set { + Step::SOLVE_ALL_CE, + Step::DETECT_AND_SOLVE_TCMLP, + Step::DETECT_INTERVALS, + Step::MESH + }); + } + } + WHEN("Running for DETECT_AND_SOLVE_TCMLP") { + THEN("Should return all Steps except those coming before DETECT_AND_SOLVE_TCMLP") { + REQUIRE(that_and_after(Step::DETECT_AND_SOLVE_TCMLP) == std::set { + Step::DETECT_AND_SOLVE_TCMLP, + Step::DETECT_INTERVALS, + Step::MESH + }); + } + } + WHEN("Running for DETECT_INTERVALS") { + THEN("Should return all Steps except those coming before DETECT_INTERVALS") { + REQUIRE(that_and_after(Step::DETECT_INTERVALS) == std::set { + Step::DETECT_INTERVALS, + Step::MESH + }); + } + } + WHEN("Running for MESH") { + THEN("Should return all Steps except those coming before MESH") { + REQUIRE(that_and_after(Step::MESH) == std::set { + Step::MESH + }); + } + } +} From 79ec5f39fcb2dea9c90e377ffebe52fd82c39280 Mon Sep 17 00:00:00 2001 From: "tlepoix@localhost" Date: Fri, 6 Jun 2025 02:14:28 +0200 Subject: [PATCH 03/37] add GUI EditModel for Edge, Interval, MeshlinePolicy, ConflictTooCloseMeshlinePolicies --- src/CMakeLists.txt | 4 + src/domain/board.cpp | 8 +- .../conflict_too_close_meshline_policies.cpp | 21 +++-- src/domain/mesh/interval.cpp | 12 +-- src/domain/mesh/meshline_policy.cpp | 6 +- src/domain/mesh/meshline_policy.hpp | 28 +++--- src/domain/meshline_policy_manager.cpp | 2 +- .../serializers/serializer_to_plantuml.cpp | 8 +- .../serializers/serializer_to_prettyprint.cpp | 2 +- src/ui/qt/edit/edit_model.cpp | 19 ++++ src/ui/qt/edit/edit_model.hpp | 15 +++ ...l_conflict_too_close_meshline_policies.cpp | 48 ++++++++++ ...l_conflict_too_close_meshline_policies.hpp | 27 ++++++ src/ui/qt/edit/edit_model_edge.cpp | 48 ++++++++++ src/ui/qt/edit/edit_model_edge.hpp | 27 ++++++ src/ui/qt/edit/edit_model_interval.cpp | 60 ++++++++++++ src/ui/qt/edit/edit_model_interval.hpp | 27 ++++++ src/ui/qt/edit/edit_model_meshline_policy.cpp | 92 +++++++++++++++++++ src/ui/qt/edit/edit_model_meshline_policy.hpp | 27 ++++++ ...g_conflict_too_close_meshline_policies.hpp | 3 + src/ui/qt/processing_view/processing_edge.hpp | 4 + .../processing_view/processing_interval.hpp | 4 + .../processing_meshline_policy.cpp | 4 +- .../processing_meshline_policy.hpp | 3 + .../structure_meshline_policy.cpp | 4 +- .../test_conflict_colinear_edges.cpp | 40 ++++---- ...t_conflict_too_close_meshline_policies.cpp | 36 ++++---- .../domain/test_meshline_policy_manager.cpp | 4 +- 28 files changed, 499 insertions(+), 84 deletions(-) create mode 100644 src/ui/qt/edit/edit_model_conflict_too_close_meshline_policies.cpp create mode 100644 src/ui/qt/edit/edit_model_conflict_too_close_meshline_policies.hpp create mode 100644 src/ui/qt/edit/edit_model_edge.cpp create mode 100644 src/ui/qt/edit/edit_model_edge.hpp create mode 100644 src/ui/qt/edit/edit_model_interval.cpp create mode 100644 src/ui/qt/edit/edit_model_interval.hpp create mode 100644 src/ui/qt/edit/edit_model_meshline_policy.cpp create mode 100644 src/ui/qt/edit/edit_model_meshline_policy.hpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a93cc15c..dc6fbafe 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -83,6 +83,10 @@ target_sources( openemsh_bin "${CMAKE_CURRENT_SOURCE_DIR}/ui/qt/edit/edit_dialog.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/ui/qt/edit/edit_model.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/ui/qt/edit/edit_model_global.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/ui/qt/edit/edit_model_conflict_too_close_meshline_policies.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/ui/qt/edit/edit_model_edge.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/ui/qt/edit/edit_model_interval.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/ui/qt/edit/edit_model_meshline_policy.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/ui/qt/processing_view/processing_axis.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/ui/qt/processing_view/processing_plane.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/ui/qt/processing_view/processing_style.cpp" diff --git a/src/domain/board.cpp b/src/domain/board.cpp index 366121ef..370c40e9 100644 --- a/src/domain/board.cpp +++ b/src/domain/board.cpp @@ -71,8 +71,8 @@ void Board::Builder::add_fixed_meshline_policy(Axis const axis, Coord const coor fixed_meshline_policy_creators[axis].emplace_back([=](Board const* board, Timepoint* t) { if(!contains_that(board->line_policy_manager->get_current_state().line_policies[axis], [&coord](shared_ptr const& policy) { - if(policy->policy == MeshlinePolicy::Policy::ONELINE - && policy->normal == MeshlinePolicy::Normal::NONE + if(policy->get_current_state().policy == MeshlinePolicy::Policy::ONELINE + && policy->get_current_state().normal == MeshlinePolicy::Normal::NONE && policy->coord == coord) return true; return false; @@ -312,7 +312,7 @@ void Board::detect_non_conflicting_edges(Plane const plane) { optional const coord = domain::coord(edge->p0(), edge->axis); optional const axis = transpose(plane, edge->axis); optional const normal = cast(edge->normal); - if(coord && axis && normal && !edge->get_current_state().conflicts.size()) { + if(coord && axis && normal && edge->get_current_state().conflicts.empty()) { auto [t, state_e] = edge->make_next_state(); state_e.meshline_policy = line_policy_manager->add_meshline_policy( { edge }, @@ -320,7 +320,7 @@ void Board::detect_non_conflicting_edges(Plane const plane) { MeshlinePolicy::Policy::THIRDS, normal.value(), coord.value(), - true, + state_e.to_mesh, t); edge->set_state(t, state_e); } diff --git a/src/domain/conflicts/conflict_too_close_meshline_policies.cpp b/src/domain/conflicts/conflict_too_close_meshline_policies.cpp index 729f0517..54a2d539 100644 --- a/src/domain/conflicts/conflict_too_close_meshline_policies.cpp +++ b/src/domain/conflicts/conflict_too_close_meshline_policies.cpp @@ -37,19 +37,22 @@ void ConflictTooCloseMeshlinePolicies::auto_solve(MeshlinePolicyManager& line_po return; auto [policy, normal] = [&]() -> tuple, optional> { - if(a->policy == MeshlinePolicy::Policy::THIRDS && b->policy == MeshlinePolicy::Policy::THIRDS) { - if(a->normal != b->normal) { + auto const& state_a = a->get_current_state(); + auto const& state_b = b->get_current_state(); + + if(state_a.policy == MeshlinePolicy::Policy::THIRDS && state_b.policy == MeshlinePolicy::Policy::THIRDS) { + if(state_a.normal != state_b.normal) { return { MeshlinePolicy::Policy::HALFS, MeshlinePolicy::Normal::NONE }; - } else if(a->normal == MeshlinePolicy::Normal::MIN - && b->normal == MeshlinePolicy::Normal::MIN) { + } else if(state_a.normal == MeshlinePolicy::Normal::MIN + && state_b.normal == MeshlinePolicy::Normal::MIN) { return { MeshlinePolicy::Policy::THIRDS, MeshlinePolicy::Normal::MIN }; - } else if(a->normal == MeshlinePolicy::Normal::MAX - && b->normal == MeshlinePolicy::Normal::MAX) { + } else if(state_a.normal == MeshlinePolicy::Normal::MAX + && state_b.normal == MeshlinePolicy::Normal::MAX) { return { MeshlinePolicy::Policy::THIRDS, MeshlinePolicy::Normal::MAX }; } - } else if((a->policy == MeshlinePolicy::Policy::HALFS && b->policy == MeshlinePolicy::Policy::HALFS) - || (a->policy == MeshlinePolicy::Policy::HALFS && b->policy == MeshlinePolicy::Policy::THIRDS) - || (a->policy == MeshlinePolicy::Policy::THIRDS && b->policy == MeshlinePolicy::Policy::HALFS)) { + } else if((state_a.policy == MeshlinePolicy::Policy::HALFS && state_b.policy == MeshlinePolicy::Policy::HALFS) + || (state_a.policy == MeshlinePolicy::Policy::HALFS && state_b.policy == MeshlinePolicy::Policy::THIRDS) + || (state_a.policy == MeshlinePolicy::Policy::THIRDS && state_b.policy == MeshlinePolicy::Policy::HALFS)) { return { MeshlinePolicy::Policy::HALFS, MeshlinePolicy::Normal::NONE }; } //else if(ONE and *) { // TODO } // TODO should not have been created ? diff --git a/src/domain/mesh/interval.cpp b/src/domain/mesh/interval.cpp index 273817c6..8f34b9a4 100644 --- a/src/domain/mesh/interval.cpp +++ b/src/domain/mesh/interval.cpp @@ -46,11 +46,11 @@ Interval::Interval(MeshlinePolicy* before, MeshlinePolicy* after, Axis axis, Glo : Originator(t, { .dmax = global_params->get_current_state().dmax, .before = Side(before, global_params->get_current_state().lmin, global_params->get_current_state().lambda, calc_h(before->coord, after->coord), [before](double d) noexcept { - switch(before->policy) { + switch(before->get_current_state().policy) { case MeshlinePolicy::Policy::ONELINE: return 0.0; case MeshlinePolicy::Policy::HALFS: return d / 2.0; case MeshlinePolicy::Policy::THIRDS: return [&] { - switch(before->normal) { + switch(before->get_current_state().normal) { case MeshlinePolicy::Normal::MAX: return 2.0/3.0 * d; case MeshlinePolicy::Normal::MIN: @@ -63,11 +63,11 @@ Interval::Interval(MeshlinePolicy* before, MeshlinePolicy* after, Axis axis, Glo } }), .after = Side(after, global_params->get_current_state().lmin, global_params->get_current_state().lambda, calc_h(before->coord, after->coord), [after](double d) noexcept { - switch(after->policy) { + switch(after->get_current_state().policy) { case MeshlinePolicy::Policy::ONELINE: return 0.0; case MeshlinePolicy::Policy::HALFS: return d / 2.0; case MeshlinePolicy::Policy::THIRDS: return [&] { - switch(after->normal) { + switch(after->get_current_state().normal) { case MeshlinePolicy::Normal::MAX: return 1.0/3.0 * d; case MeshlinePolicy::Normal::MIN: @@ -405,7 +405,7 @@ vector> Interval::mesh() const { double const d_init_after = state.after.d_init(); vector> meshlines; - if(state.before.meshline_policy->policy != MeshlinePolicy::Policy::ONELINE) + if(state.before.meshline_policy->get_current_state().policy != MeshlinePolicy::Policy::ONELINE) meshlines.push_back(make_shared( state.before.meshline_policy->coord + d_init_before, this, @@ -425,7 +425,7 @@ vector> Interval::mesh() const { state.after.meshline_policy->coord - d_init_after - (*it), this, state.after.meshline_policy)); - if(state.after.meshline_policy->policy != MeshlinePolicy::Policy::ONELINE) + if(state.after.meshline_policy->get_current_state().policy != MeshlinePolicy::Policy::ONELINE) meshlines.push_back(make_shared( state.after.meshline_policy->coord - d_init_after, this, diff --git a/src/domain/mesh/meshline_policy.cpp b/src/domain/mesh/meshline_policy.cpp index aa06eef1..22c84133 100644 --- a/src/domain/mesh/meshline_policy.cpp +++ b/src/domain/mesh/meshline_policy.cpp @@ -26,21 +26,21 @@ MeshlinePolicy::MeshlinePolicy( bool const is_enabled, double const res_factor) : Originator(t, { + .policy = policy, + .normal = normal, .is_enabled = is_enabled, .res_factor = res_factor, .d = global_params->get_current_state().dmax / res_factor, .origins = origins }) , axis(axis) -, policy(policy) -, normal(normal) , global_params(global_params) , coord(coord) {} //****************************************************************************** optional MeshlinePolicy::mesh() { - if(policy == Policy::ONELINE) + if(get_current_state().policy == Policy::ONELINE) return Meshline(coord, this); else return nullopt; diff --git a/src/domain/mesh/meshline_policy.hpp b/src/domain/mesh/meshline_policy.hpp index 314e7f78..67f3b1d5 100644 --- a/src/domain/mesh/meshline_policy.hpp +++ b/src/domain/mesh/meshline_policy.hpp @@ -28,16 +28,7 @@ namespace domain { class Conflict; class Meshline; -//****************************************************************************** -struct MeshlinePolicyState final -: public IConflictOriginState -, public IConflictSolutionState { - bool is_enabled; - double res_factor; // TODO useful? d directly? come from params - double d; ///< Distance betwen two lines (HALFS and THIRDS only). - std::vector origins; - std::vector meshlines; -}; +struct MeshlinePolicyState; /// This class is an interface between a mesh line and its origin because /// multiples edges can be responsible for the same lines and some lines can @@ -61,13 +52,13 @@ class MeshlinePolicy ONELINE, ///< Place one line on the coord. typically produced by ports. HALFS, ///< Apply halfs rule while meshing : when edges conflict on the direction. THIRDS ///< Apply thirds rule while meshing : normal case for edges. - } const policy; // TODO rename meshing_rule + }; enum class Normal { NONE, MIN, MAX - } const normal; + }; GlobalParams* global_params; Coord const coord; @@ -87,6 +78,19 @@ class MeshlinePolicy std::optional mesh(); }; +//****************************************************************************** +struct MeshlinePolicyState final +: public IConflictOriginState +, public IConflictSolutionState { + MeshlinePolicy::Policy policy; + MeshlinePolicy::Normal normal; + bool is_enabled; + double res_factor; // TODO useful? d directly? come from params + double d; ///< Distance betwen two lines (HALFS and THIRDS only). + std::vector origins; + std::vector meshlines; +}; + //****************************************************************************** std::optional coord(Point const& point, Segment::Axis const axis) noexcept; diff --git a/src/domain/meshline_policy_manager.cpp b/src/domain/meshline_policy_manager.cpp index 9def83ca..9015e7b8 100644 --- a/src/domain/meshline_policy_manager.cpp +++ b/src/domain/meshline_policy_manager.cpp @@ -76,7 +76,7 @@ optional> detect_closest_meshline_policies( erase_if(dimension, [](MeshlinePolicy const* a) { return (!a->get_current_state().is_enabled) - || a->policy == MeshlinePolicy::Policy::ONELINE; + || a->get_current_state().policy == MeshlinePolicy::Policy::ONELINE; }); ranges::sort(dimension, diff --git a/src/infra/serializers/serializer_to_plantuml.cpp b/src/infra/serializers/serializer_to_plantuml.cpp index abd56dd7..09d5f0ed 100644 --- a/src/infra/serializers/serializer_to_plantuml.cpp +++ b/src/infra/serializers/serializer_to_plantuml.cpp @@ -140,8 +140,8 @@ void SerializerToPlantuml::visit(ConflictColinearEdges& conflict) { if(solution) { out += - id + " : Solution.policy = " + to_string(solution->policy) + "\n" + - id + " : Solution.normal = " + to_string(solution->normal) + "\n" + + id + " : Solution.policy = " + to_string(solution->get_current_state().policy) + "\n" + + id + " : Solution.normal = " + to_string(solution->get_current_state().normal) + "\n" + id + "_out ------> " + to_string(solution->id) + "_in\n"; } @@ -200,9 +200,9 @@ void SerializerToPlantuml::visit(MeshlinePolicy& policy) { "state \"MeshlinePolicy\" as " + id + (policy.get_current_state().is_enabled ? " #green" : " #red") + " {\n" "state \" \" as " + id + "_in <>\n" "state \" \" as " + id + "_out <>\n" + - id + " : Normal = " + to_string(policy.normal) + "\n" + + id + " : Normal = " + to_string(policy.get_current_state().normal) + "\n" + id + " : Is enabled = " + (policy.get_current_state().is_enabled ? "true" : "false") + "\n" + - id + " : Policy = " + to_string(policy.policy) + "\n" + + id + " : Policy = " + to_string(policy.get_current_state().policy) + "\n" + id + " : d = " + to_string(policy.get_current_state().d) + "\n"; if(origin) diff --git a/src/infra/serializers/serializer_to_prettyprint.cpp b/src/infra/serializers/serializer_to_prettyprint.cpp index a15161de..0fed69ae 100644 --- a/src/infra/serializers/serializer_to_prettyprint.cpp +++ b/src/infra/serializers/serializer_to_prettyprint.cpp @@ -119,7 +119,7 @@ void SerializerToPrettyprint::visit(ConflictColinearEdges& conflict) { } if(solution) - out += "\tSolution: " + F_D_GREEN + to_string(solution->policy) + S_RESET; + out += "\tSolution: " + F_D_GREEN + to_string(solution->get_current_state().policy) + S_RESET; out += "\n"; } diff --git a/src/ui/qt/edit/edit_model.cpp b/src/ui/qt/edit/edit_model.cpp index 5ab2490b..b95e23d4 100644 --- a/src/ui/qt/edit/edit_model.cpp +++ b/src/ui/qt/edit/edit_model.cpp @@ -4,6 +4,17 @@ /// @author Thomas Lepoix ///***************************************************************************** +#include "edit_model_conflict_too_close_meshline_policies.hpp" +#include "edit_model_edge.hpp" +#include "edit_model_interval.hpp" +#include "edit_model_meshline_policy.hpp" +#include "ui/qt/user_types.hpp" +#include "ui/qt/processing_view/processing_conflict_too_close_meshline_policies.hpp" +#include "ui/qt/processing_view/processing_edge.hpp" +#include "ui/qt/processing_view/processing_interval.hpp" +#include "ui/qt/processing_view/processing_meshline_policy.hpp" +#include "utils/unconst.hpp" + #include "edit_model.hpp" namespace ui::qt { @@ -67,6 +78,14 @@ bool EditModel::try_to_bool(Qt::CheckState const in, bool& out) { //****************************************************************************** EditModel* EditModel::make(nodegraph::Node* node, QObject* parent) { switch(node->type()) { + case UserTypes::PROCESSING_EDGE: + return new EditModelEdge(unconst(static_cast(node)->edge), parent); + case UserTypes::PROCESSING_INTERVAL: + return new EditModelInterval(unconst(static_cast(node)->interval), parent); + case UserTypes::PROCESSING_MESHLINE_POLICY: + return new EditModelMeshlinePolicy(unconst(static_cast(node)->meshline_policy), parent); + case UserTypes::PROCESSING_CONFLICT_TCMLP: + return new EditModelConflictTooCloseMeshlinePolicies(unconst(static_cast(node)->conflict), parent); default: return nullptr; } diff --git a/src/ui/qt/edit/edit_model.hpp b/src/ui/qt/edit/edit_model.hpp index 8d78c42e..d40449cf 100644 --- a/src/ui/qt/edit/edit_model.hpp +++ b/src/ui/qt/edit/edit_model.hpp @@ -8,6 +8,8 @@ #include +#include + #include "app/steps.hpp" namespace ui::qt { @@ -32,10 +34,23 @@ class EditModel : public QStandardItemModel { static bool try_to_double(QString const& in, double& out); static bool try_to_ulong(QString const& in, std::size_t& out); static bool try_to_bool(Qt::CheckState const in, bool& out); + template + static bool try_from_map(std::map const& map, QString const& in, O& out); signals: void request_to_go_before(app::Step step); void edited(app::Step const redo_from); }; +//****************************************************************************** +template +bool EditModel::try_from_map(std::map const& map, QString const& in, O& out) { + bool does_succeed = false; + if(map.contains(in)) { + out = map.at(in); + does_succeed = true; + } + return does_succeed; +} + } // namespace ui::qt diff --git a/src/ui/qt/edit/edit_model_conflict_too_close_meshline_policies.cpp b/src/ui/qt/edit/edit_model_conflict_too_close_meshline_policies.cpp new file mode 100644 index 00000000..60ad2504 --- /dev/null +++ b/src/ui/qt/edit/edit_model_conflict_too_close_meshline_policies.cpp @@ -0,0 +1,48 @@ +///***************************************************************************** +/// @date Feb 2021 +/// @copyright GPL-3.0-or-later +/// @author Thomas Lepoix +///***************************************************************************** + +#include +#include + +#include "app/steps.hpp" +#include "domain/conflicts/conflict_too_close_meshline_policies.hpp" + +#include "edit_model_conflict_too_close_meshline_policies.hpp" + +namespace ui::qt { + +//****************************************************************************** +EditModelConflictTooCloseMeshlinePolicies::EditModelConflictTooCloseMeshlinePolicies(domain::ConflictTooCloseMeshlinePolicies* conflict, QObject* parent) +: EditModel(parent) +, conflict(conflict) +{ + setRowCount(1); + for(std::size_t i = 0; auto& str : { + "is_enabled" + }) { + setItem(i++, 0, make_property_item(str)); + } + + auto const& state = conflict->get_current_state(); + setItem(0, 1, make_bool_item(state.is_enabled)); +} + +//****************************************************************************** +void EditModelConflictTooCloseMeshlinePolicies::commit() { + auto state = conflict->get_current_state(); + + std::array does_succeed = { + try_to_bool(item(0, 1)->checkState(), state.is_enabled) + }; + + if(std::ranges::all_of(does_succeed, is_true)) { + emit request_to_go_before(app::Step::DETECT_AND_SOLVE_TCMLP); + conflict->set_next_state(state); + emit edited(app::Step::DETECT_AND_SOLVE_TCMLP); + } +} + +} // namespace ui::qt diff --git a/src/ui/qt/edit/edit_model_conflict_too_close_meshline_policies.hpp b/src/ui/qt/edit/edit_model_conflict_too_close_meshline_policies.hpp new file mode 100644 index 00000000..900752a4 --- /dev/null +++ b/src/ui/qt/edit/edit_model_conflict_too_close_meshline_policies.hpp @@ -0,0 +1,27 @@ +///***************************************************************************** +/// @date Feb 2021 +/// @copyright GPL-3.0-or-later +/// @author Thomas Lepoix +///***************************************************************************** + +#pragma once + +#include "edit_model.hpp" + +namespace domain { +class ConflictTooCloseMeshlinePolicies; +} // namespace domain + +namespace ui::qt { + +//****************************************************************************** +class EditModelConflictTooCloseMeshlinePolicies : public EditModel { +public: + explicit EditModelConflictTooCloseMeshlinePolicies(domain::ConflictTooCloseMeshlinePolicies* conflict, QObject* parent = nullptr); + void commit() override; + +private: + domain::ConflictTooCloseMeshlinePolicies* conflict; +}; + +} // namespace ui::qt diff --git a/src/ui/qt/edit/edit_model_edge.cpp b/src/ui/qt/edit/edit_model_edge.cpp new file mode 100644 index 00000000..13ae9cd1 --- /dev/null +++ b/src/ui/qt/edit/edit_model_edge.cpp @@ -0,0 +1,48 @@ +///***************************************************************************** +/// @date Feb 2021 +/// @copyright GPL-3.0-or-later +/// @author Thomas Lepoix +///***************************************************************************** + +#include +#include + +#include "app/steps.hpp" +#include "domain/geometrics/edge.hpp" + +#include "edit_model_edge.hpp" + +namespace ui::qt { + +//****************************************************************************** +EditModelEdge::EditModelEdge(domain::Edge* edge, QObject* parent) +: EditModel(parent) +, edge(edge) +{ + setRowCount(1); + for(std::size_t i = 0; auto& str : { + "to_mesh" + }) { + setItem(i++, 0, make_property_item(str)); + } + + auto const& state = edge->get_current_state(); + setItem(0, 1, make_bool_item(state.to_mesh)); +} + +//****************************************************************************** +void EditModelEdge::commit() { + auto state = edge->get_current_state(); + + std::array does_succeed = { + try_to_bool(item(0, 1)->checkState(), state.to_mesh) + }; + + if(std::ranges::all_of(does_succeed, is_true)) { + emit request_to_go_before(app::Step::DETECT_CONFLICT_EIP); + edge->set_next_state(state); + emit edited(app::Step::DETECT_CONFLICT_EIP); + } +} + +} // namespace ui::qt diff --git a/src/ui/qt/edit/edit_model_edge.hpp b/src/ui/qt/edit/edit_model_edge.hpp new file mode 100644 index 00000000..31b7e356 --- /dev/null +++ b/src/ui/qt/edit/edit_model_edge.hpp @@ -0,0 +1,27 @@ +///***************************************************************************** +/// @date Feb 2021 +/// @copyright GPL-3.0-or-later +/// @author Thomas Lepoix +///***************************************************************************** + +#pragma once + +#include "edit_model.hpp" + +namespace domain { +class Edge; +} // namespace domain + +namespace ui::qt { + +//****************************************************************************** +class EditModelEdge : public EditModel { +public: + explicit EditModelEdge(domain::Edge* edge, QObject* parent = nullptr); + void commit() override; + +private: + domain::Edge* edge; +}; + +} // namespace ui::qt diff --git a/src/ui/qt/edit/edit_model_interval.cpp b/src/ui/qt/edit/edit_model_interval.cpp new file mode 100644 index 00000000..155f71ec --- /dev/null +++ b/src/ui/qt/edit/edit_model_interval.cpp @@ -0,0 +1,60 @@ +///***************************************************************************** +/// @date Feb 2021 +/// @copyright GPL-3.0-or-later +/// @author Thomas Lepoix +///***************************************************************************** + +#include +#include + +#include "app/steps.hpp" +#include "domain/mesh/interval.hpp" + +#include "edit_model_interval.hpp" + +namespace ui::qt { + +//****************************************************************************** +EditModelInterval::EditModelInterval(domain::Interval* interval, QObject* parent) +: EditModel(parent) +, interval(interval) +{ + setRowCount(5); + for(std::size_t i = 0; auto& str : { + "dmax", + "before.lmin", + "before.lambda", + "after.lmin", + "after.lambda" + }) { + setItem(i++, 0, make_property_item(str)); + } + + auto const& state = interval->get_current_state(); + setItem(0, 1, new QStandardItem(QString::number(state.dmax))); + setItem(1, 1, new QStandardItem(QString::number(state.before.lmin))); + setItem(2, 1, new QStandardItem(QString::number(state.before.lambda))); + setItem(3, 1, new QStandardItem(QString::number(state.after.lmin))); + setItem(4, 1, new QStandardItem(QString::number(state.after.lambda))); +} + +//****************************************************************************** +void EditModelInterval::commit() { + auto state = interval->get_current_state(); + + std::array does_succeed = { + try_to_double(item(0, 1)->text(), state.dmax), + try_to_ulong(item(1, 1)->text(), state.before.lmin), + try_to_double(item(2, 1)->text(), state.before.lambda), + try_to_ulong(item(3, 1)->text(), state.after.lmin), + try_to_double(item(4, 1)->text(), state.after.lambda) + }; + + if(std::ranges::all_of(does_succeed, is_true)) { + emit request_to_go_before(app::Step::MESH); + interval->set_next_state(state); + emit edited(app::Step::MESH); + } +} + +} // namespace ui::qt diff --git a/src/ui/qt/edit/edit_model_interval.hpp b/src/ui/qt/edit/edit_model_interval.hpp new file mode 100644 index 00000000..53693227 --- /dev/null +++ b/src/ui/qt/edit/edit_model_interval.hpp @@ -0,0 +1,27 @@ +///***************************************************************************** +/// @date Feb 2021 +/// @copyright GPL-3.0-or-later +/// @author Thomas Lepoix +///***************************************************************************** + +#pragma once + +#include "edit_model.hpp" + +namespace domain { +class Interval; +} // namespace domain + +namespace ui::qt { + +//****************************************************************************** +class EditModelInterval : public EditModel { +public: + explicit EditModelInterval(domain::Interval* interval, QObject* parent = nullptr); + void commit() override; + +private: + domain::Interval* interval; +}; + +} // namespace ui::qt diff --git a/src/ui/qt/edit/edit_model_meshline_policy.cpp b/src/ui/qt/edit/edit_model_meshline_policy.cpp new file mode 100644 index 00000000..5ed8ee05 --- /dev/null +++ b/src/ui/qt/edit/edit_model_meshline_policy.cpp @@ -0,0 +1,92 @@ +///***************************************************************************** +/// @date Feb 2021 +/// @copyright GPL-3.0-or-later +/// @author Thomas Lepoix +///***************************************************************************** + +#include +#include + +#include "app/steps.hpp" +#include "domain/mesh/meshline_policy.hpp" +#include "infra/utils/to_string.hpp" +#include "utils/unreachable.hpp" + +#include "edit_model_meshline_policy.hpp" + +namespace ui::qt { + +//****************************************************************************** +EditModelMeshlinePolicy::EditModelMeshlinePolicy(domain::MeshlinePolicy* meshline_policy, QObject* parent) +: EditModel(parent) +, meshline_policy(meshline_policy) +{ + setRowCount(5); + for(std::size_t i = 0; auto& str : { + "policy", + "normal", + "is_enabled", + "res_factor", + "d" + }) { + setItem(i++, 0, make_property_item(str)); + } + + auto const& state = meshline_policy->get_current_state(); + setItem(0, 1, new QStandardItem(QString::fromStdString(to_string(state.policy)))); + setItem(1, 1, new QStandardItem(QString::fromStdString(to_string(state.normal)))); + setItem(2, 1, make_bool_item(state.is_enabled)); + setItem(3, 1, new QStandardItem(QString::number(state.res_factor))); + setItem(4, 1, new QStandardItem(QString::number(state.d))); +} + +//****************************************************************************** +void EditModelMeshlinePolicy::commit() { + auto state = meshline_policy->get_current_state(); + + auto const are_policy_and_normal_compatible = [&state]() { + switch(state.policy) { + case domain::MeshlinePolicy::Policy::ONELINE: [[fallthrough]]; + case domain::MeshlinePolicy::Policy::HALFS: + switch(state.normal) { + case domain::MeshlinePolicy::Normal::MIN: [[fallthrough]]; + case domain::MeshlinePolicy::Normal::MAX: return false; + case domain::MeshlinePolicy::Normal::NONE: return true; + default: unreachable(); + } + case domain::MeshlinePolicy::Policy::THIRDS: + switch(state.normal) { + case domain::MeshlinePolicy::Normal::MIN: [[fallthrough]]; + case domain::MeshlinePolicy::Normal::MAX: return true; + case domain::MeshlinePolicy::Normal::NONE: return false; + default: unreachable(); + } + default:unreachable(); + } + }; + + std::array does_succeed = { + try_from_map({ + { "ONELINE", domain::MeshlinePolicy::Policy::ONELINE }, + { "HALFS", domain::MeshlinePolicy::Policy::HALFS }, + { "THIRDS", domain::MeshlinePolicy::Policy::THIRDS } + }, item(0, 1)->text(), state.policy), + try_from_map({ + { "MIN", domain::MeshlinePolicy::Normal::MIN }, + { "MAX", domain::MeshlinePolicy::Normal::MAX }, + { "NONE", domain::MeshlinePolicy::Normal::NONE } + }, item(1, 1)->text(), state.normal), + are_policy_and_normal_compatible(), + try_to_bool(item(2, 1)->checkState(), state.is_enabled), + try_to_double(item(3, 1)->text(), state.res_factor), + try_to_double(item(4, 1)->text(), state.d) + }; + + if(std::ranges::all_of(does_succeed, is_true)) { + emit request_to_go_before(app::Step::DETECT_INTERVALS); + meshline_policy->set_next_state(state); + emit edited(app::Step::DETECT_INTERVALS); + } +} + +} // namespace ui::qt diff --git a/src/ui/qt/edit/edit_model_meshline_policy.hpp b/src/ui/qt/edit/edit_model_meshline_policy.hpp new file mode 100644 index 00000000..736c5d79 --- /dev/null +++ b/src/ui/qt/edit/edit_model_meshline_policy.hpp @@ -0,0 +1,27 @@ +///***************************************************************************** +/// @date Feb 2021 +/// @copyright GPL-3.0-or-later +/// @author Thomas Lepoix +///***************************************************************************** + +#pragma once + +#include "edit_model.hpp" + +namespace domain { +class MeshlinePolicy; +} // namespace domain + +namespace ui::qt { + +//****************************************************************************** +class EditModelMeshlinePolicy : public EditModel { +public: + explicit EditModelMeshlinePolicy(domain::MeshlinePolicy* meshline_policy, QObject* parent = nullptr); + void commit() override; + +private: + domain::MeshlinePolicy* meshline_policy; +}; + +} // namespace ui::qt diff --git a/src/ui/qt/processing_view/processing_conflict_too_close_meshline_policies.hpp b/src/ui/qt/processing_view/processing_conflict_too_close_meshline_policies.hpp index fc813eb4..c4d35e07 100644 --- a/src/ui/qt/processing_view/processing_conflict_too_close_meshline_policies.hpp +++ b/src/ui/qt/processing_view/processing_conflict_too_close_meshline_policies.hpp @@ -17,6 +17,7 @@ class ConflictTooCloseMeshlinePolicies; namespace ui::qt { +class EditModel; class ProcessingMeshlinePolicy; //****************************************************************************** @@ -40,6 +41,8 @@ class ProcessingConflictTooCloseMeshlinePolicies : public nodegraph::Node { std::size_t count_tcmlp_mlp_deepness() const; private: + friend EditModel; + domain::ConflictTooCloseMeshlinePolicies const* const conflict; }; diff --git a/src/ui/qt/processing_view/processing_edge.hpp b/src/ui/qt/processing_view/processing_edge.hpp index 3aaec559..116759f4 100644 --- a/src/ui/qt/processing_view/processing_edge.hpp +++ b/src/ui/qt/processing_view/processing_edge.hpp @@ -17,6 +17,8 @@ class Edge; namespace ui::qt { +class EditModel; + //****************************************************************************** class ProcessingEdge : public nodegraph::Node { public: @@ -39,6 +41,8 @@ class ProcessingEdge : public nodegraph::Node { int type() const override; private: + friend EditModel; + domain::Edge const* const edge; }; diff --git a/src/ui/qt/processing_view/processing_interval.hpp b/src/ui/qt/processing_view/processing_interval.hpp index dfe55d92..f2623a80 100644 --- a/src/ui/qt/processing_view/processing_interval.hpp +++ b/src/ui/qt/processing_view/processing_interval.hpp @@ -17,6 +17,8 @@ class Interval; namespace ui::qt { +class EditModel; + //****************************************************************************** class ProcessingInterval : public nodegraph::Node { public: @@ -36,6 +38,8 @@ class ProcessingInterval : public nodegraph::Node { int type() const override; private: + friend EditModel; + domain::Interval const* const interval; }; diff --git a/src/ui/qt/processing_view/processing_meshline_policy.cpp b/src/ui/qt/processing_view/processing_meshline_policy.cpp index d4ca9a47..eb5b1f34 100644 --- a/src/ui/qt/processing_view/processing_meshline_policy.cpp +++ b/src/ui/qt/processing_view/processing_meshline_policy.cpp @@ -79,9 +79,9 @@ ProcessingMeshlinePolicy::ProcessingMeshlinePolicy(domain::MeshlinePolicy const* QString policy("Policy: "); QString d("d: "); if(meshline_policy) { - normal += QString::fromStdString(to_string(meshline_policy->normal)); + normal += QString::fromStdString(to_string(meshline_policy->get_current_state().normal)); is_enabled += (meshline_policy->get_current_state().is_enabled ? "true" : "false"); - policy += QString::fromStdString(to_string(meshline_policy->policy)); + policy += QString::fromStdString(to_string(meshline_policy->get_current_state().policy)); d += QString::number(meshline_policy->get_current_state().d); } diff --git a/src/ui/qt/processing_view/processing_meshline_policy.hpp b/src/ui/qt/processing_view/processing_meshline_policy.hpp index ed62d740..540df3d7 100644 --- a/src/ui/qt/processing_view/processing_meshline_policy.hpp +++ b/src/ui/qt/processing_view/processing_meshline_policy.hpp @@ -17,6 +17,7 @@ class MeshlinePolicy; namespace ui::qt { +class EditModel; class ProcessingConflictTooCloseMeshlinePolicies; //****************************************************************************** @@ -44,6 +45,8 @@ class ProcessingMeshlinePolicy : public nodegraph::Node { std::size_t count_mlp_tcmlp_deepness() const; private: + friend EditModel; + domain::MeshlinePolicy const* const meshline_policy; }; diff --git a/src/ui/qt/structure_view/structure_meshline_policy.cpp b/src/ui/qt/structure_view/structure_meshline_policy.cpp index 19bf687f..72875f16 100644 --- a/src/ui/qt/structure_view/structure_meshline_policy.cpp +++ b/src/ui/qt/structure_view/structure_meshline_policy.cpp @@ -43,11 +43,11 @@ static std::array convert_policy_lines(domain::ViewAxis axis, domain: auto const offset = [&]() -> std::array { auto const d = meshline_policy->get_current_state().d; - switch(meshline_policy->policy) { + switch(meshline_policy->get_current_state().policy) { case domain::MeshlinePolicy::Policy::ONELINE: return { 0.0, 0.0 }; case domain::MeshlinePolicy::Policy::HALFS: return { d / 2.0, -d / 2.0 }; case domain::MeshlinePolicy::Policy::THIRDS: - switch(meshline_policy->normal) { + switch(meshline_policy->get_current_state().normal) { case domain::MeshlinePolicy::Normal::MAX: return { 2.0 / 3.0 * d, -1.0 / 3.0 * d }; case domain::MeshlinePolicy::Normal::MIN: diff --git a/test/unit/domain/conflicts/test_conflict_colinear_edges.cpp b/test/unit/domain/conflicts/test_conflict_colinear_edges.cpp index 45851e72..ed29d9e7 100644 --- a/test/unit/domain/conflicts/test_conflict_colinear_edges.cpp +++ b/test/unit/domain/conflicts/test_conflict_colinear_edges.cpp @@ -55,8 +55,8 @@ SCENARIO("void ConflictColinearEdges::auto_solve(MeshlinePolicyManager& line_pol REQUIRE(mpm.get_current_state().line_policies[X][0].get() == cce.get_current_state().solution); REQUIRE(mpm.get_current_state().line_policies[X][0]->get_current_state().origins.size() == 1); REQUIRE(mpm.get_current_state().line_policies[X][0]->get_current_state().origins[0] == &cce); - REQUIRE(mpm.get_current_state().line_policies[X][0]->policy == MeshlinePolicy::Policy::HALFS); - REQUIRE(mpm.get_current_state().line_policies[X][0]->normal == MeshlinePolicy::Normal::NONE); + REQUIRE(mpm.get_current_state().line_policies[X][0]->get_current_state().policy == MeshlinePolicy::Policy::HALFS); + REQUIRE(mpm.get_current_state().line_policies[X][0]->get_current_state().normal == MeshlinePolicy::Normal::NONE); REQUIRE(mpm.get_current_state().line_policies[X][0]->coord == 1); REQUIRE(mpm.get_current_state().line_policies[X][0]->get_current_state().is_enabled); } @@ -82,8 +82,8 @@ SCENARIO("void ConflictColinearEdges::auto_solve(MeshlinePolicyManager& line_pol REQUIRE(mpm.get_current_state().line_policies[X][0].get() == cce.get_current_state().solution); REQUIRE(mpm.get_current_state().line_policies[X][0]->get_current_state().origins.size() == 1); REQUIRE(mpm.get_current_state().line_policies[X][0]->get_current_state().origins[0] == &cce); - REQUIRE(mpm.get_current_state().line_policies[X][0]->policy == MeshlinePolicy::Policy::HALFS); - REQUIRE(mpm.get_current_state().line_policies[X][0]->normal == MeshlinePolicy::Normal::NONE); + REQUIRE(mpm.get_current_state().line_policies[X][0]->get_current_state().policy == MeshlinePolicy::Policy::HALFS); + REQUIRE(mpm.get_current_state().line_policies[X][0]->get_current_state().normal == MeshlinePolicy::Normal::NONE); REQUIRE(mpm.get_current_state().line_policies[X][0]->coord == 1); REQUIRE(mpm.get_current_state().line_policies[X][0]->get_current_state().is_enabled); } @@ -109,8 +109,8 @@ SCENARIO("void ConflictColinearEdges::auto_solve(MeshlinePolicyManager& line_pol REQUIRE(mpm.get_current_state().line_policies[X][0].get() == cce.get_current_state().solution); REQUIRE(mpm.get_current_state().line_policies[X][0]->get_current_state().origins.size() == 1); REQUIRE(mpm.get_current_state().line_policies[X][0]->get_current_state().origins[0] == &cce); - REQUIRE(mpm.get_current_state().line_policies[X][0]->policy == MeshlinePolicy::Policy::THIRDS); - REQUIRE(mpm.get_current_state().line_policies[X][0]->normal == MeshlinePolicy::Normal::MIN); + REQUIRE(mpm.get_current_state().line_policies[X][0]->get_current_state().policy == MeshlinePolicy::Policy::THIRDS); + REQUIRE(mpm.get_current_state().line_policies[X][0]->get_current_state().normal == MeshlinePolicy::Normal::MIN); REQUIRE(mpm.get_current_state().line_policies[X][0]->coord == 1); REQUIRE(mpm.get_current_state().line_policies[X][0]->get_current_state().is_enabled); } @@ -136,8 +136,8 @@ SCENARIO("void ConflictColinearEdges::auto_solve(MeshlinePolicyManager& line_pol REQUIRE(mpm.get_current_state().line_policies[X][0].get() == cce.get_current_state().solution); REQUIRE(mpm.get_current_state().line_policies[X][0]->get_current_state().origins.size() == 1); REQUIRE(mpm.get_current_state().line_policies[X][0]->get_current_state().origins[0] == &cce); - REQUIRE(mpm.get_current_state().line_policies[X][0]->policy == MeshlinePolicy::Policy::HALFS); - REQUIRE(mpm.get_current_state().line_policies[X][0]->normal == MeshlinePolicy::Normal::NONE); + REQUIRE(mpm.get_current_state().line_policies[X][0]->get_current_state().policy == MeshlinePolicy::Policy::HALFS); + REQUIRE(mpm.get_current_state().line_policies[X][0]->get_current_state().normal == MeshlinePolicy::Normal::NONE); REQUIRE(mpm.get_current_state().line_policies[X][0]->coord == 1); REQUIRE(mpm.get_current_state().line_policies[X][0]->get_current_state().is_enabled); } @@ -163,8 +163,8 @@ SCENARIO("void ConflictColinearEdges::auto_solve(MeshlinePolicyManager& line_pol REQUIRE(mpm.get_current_state().line_policies[X][0].get() == cce.get_current_state().solution); REQUIRE(mpm.get_current_state().line_policies[X][0]->get_current_state().origins.size() == 1); REQUIRE(mpm.get_current_state().line_policies[X][0]->get_current_state().origins[0] == &cce); - REQUIRE(mpm.get_current_state().line_policies[X][0]->policy == MeshlinePolicy::Policy::THIRDS); - REQUIRE(mpm.get_current_state().line_policies[X][0]->normal == MeshlinePolicy::Normal::MAX); + REQUIRE(mpm.get_current_state().line_policies[X][0]->get_current_state().policy == MeshlinePolicy::Policy::THIRDS); + REQUIRE(mpm.get_current_state().line_policies[X][0]->get_current_state().normal == MeshlinePolicy::Normal::MAX); REQUIRE(mpm.get_current_state().line_policies[X][0]->coord == 1); REQUIRE(mpm.get_current_state().line_policies[X][0]->get_current_state().is_enabled); } @@ -206,8 +206,8 @@ SCENARIO("void ConflictColinearEdges::auto_solve(MeshlinePolicyManager& line_pol REQUIRE(mpm.get_current_state().line_policies[Y][0].get() == cce.get_current_state().solution); REQUIRE(mpm.get_current_state().line_policies[Y][0]->get_current_state().origins.size() == 1); REQUIRE(mpm.get_current_state().line_policies[Y][0]->get_current_state().origins[0] == &cce); - REQUIRE(mpm.get_current_state().line_policies[Y][0]->policy == MeshlinePolicy::Policy::HALFS); - REQUIRE(mpm.get_current_state().line_policies[Y][0]->normal == MeshlinePolicy::Normal::NONE); + REQUIRE(mpm.get_current_state().line_policies[Y][0]->get_current_state().policy == MeshlinePolicy::Policy::HALFS); + REQUIRE(mpm.get_current_state().line_policies[Y][0]->get_current_state().normal == MeshlinePolicy::Normal::NONE); REQUIRE(mpm.get_current_state().line_policies[Y][0]->coord == 1); REQUIRE(mpm.get_current_state().line_policies[Y][0]->get_current_state().is_enabled); } @@ -233,8 +233,8 @@ SCENARIO("void ConflictColinearEdges::auto_solve(MeshlinePolicyManager& line_pol REQUIRE(mpm.get_current_state().line_policies[Y][0].get() == cce.get_current_state().solution); REQUIRE(mpm.get_current_state().line_policies[Y][0]->get_current_state().origins.size() == 1); REQUIRE(mpm.get_current_state().line_policies[Y][0]->get_current_state().origins[0] == &cce); - REQUIRE(mpm.get_current_state().line_policies[Y][0]->policy == MeshlinePolicy::Policy::HALFS); - REQUIRE(mpm.get_current_state().line_policies[Y][0]->normal == MeshlinePolicy::Normal::NONE); + REQUIRE(mpm.get_current_state().line_policies[Y][0]->get_current_state().policy == MeshlinePolicy::Policy::HALFS); + REQUIRE(mpm.get_current_state().line_policies[Y][0]->get_current_state().normal == MeshlinePolicy::Normal::NONE); REQUIRE(mpm.get_current_state().line_policies[Y][0]->coord == 1); REQUIRE(mpm.get_current_state().line_policies[Y][0]->get_current_state().is_enabled); } @@ -260,8 +260,8 @@ SCENARIO("void ConflictColinearEdges::auto_solve(MeshlinePolicyManager& line_pol REQUIRE(mpm.get_current_state().line_policies[Y][0].get() == cce.get_current_state().solution); REQUIRE(mpm.get_current_state().line_policies[Y][0]->get_current_state().origins.size() == 1); REQUIRE(mpm.get_current_state().line_policies[Y][0]->get_current_state().origins[0] == &cce); - REQUIRE(mpm.get_current_state().line_policies[Y][0]->policy == MeshlinePolicy::Policy::THIRDS); - REQUIRE(mpm.get_current_state().line_policies[Y][0]->normal == MeshlinePolicy::Normal::MIN); + REQUIRE(mpm.get_current_state().line_policies[Y][0]->get_current_state().policy == MeshlinePolicy::Policy::THIRDS); + REQUIRE(mpm.get_current_state().line_policies[Y][0]->get_current_state().normal == MeshlinePolicy::Normal::MIN); REQUIRE(mpm.get_current_state().line_policies[Y][0]->coord == 1); REQUIRE(mpm.get_current_state().line_policies[Y][0]->get_current_state().is_enabled); } @@ -287,8 +287,8 @@ SCENARIO("void ConflictColinearEdges::auto_solve(MeshlinePolicyManager& line_pol REQUIRE(mpm.get_current_state().line_policies[Y][0].get() == cce.get_current_state().solution); REQUIRE(mpm.get_current_state().line_policies[Y][0]->get_current_state().origins.size() == 1); REQUIRE(mpm.get_current_state().line_policies[Y][0]->get_current_state().origins[0] == &cce); - REQUIRE(mpm.get_current_state().line_policies[Y][0]->policy == MeshlinePolicy::Policy::HALFS); - REQUIRE(mpm.get_current_state().line_policies[Y][0]->normal == MeshlinePolicy::Normal::NONE); + REQUIRE(mpm.get_current_state().line_policies[Y][0]->get_current_state().policy == MeshlinePolicy::Policy::HALFS); + REQUIRE(mpm.get_current_state().line_policies[Y][0]->get_current_state().normal == MeshlinePolicy::Normal::NONE); REQUIRE(mpm.get_current_state().line_policies[Y][0]->coord == 1); REQUIRE(mpm.get_current_state().line_policies[Y][0]->get_current_state().is_enabled); } @@ -314,8 +314,8 @@ SCENARIO("void ConflictColinearEdges::auto_solve(MeshlinePolicyManager& line_pol REQUIRE(mpm.get_current_state().line_policies[Y][0].get() == cce.get_current_state().solution); REQUIRE(mpm.get_current_state().line_policies[Y][0]->get_current_state().origins.size() == 1); REQUIRE(mpm.get_current_state().line_policies[Y][0]->get_current_state().origins[0] == &cce); - REQUIRE(mpm.get_current_state().line_policies[Y][0]->policy == MeshlinePolicy::Policy::THIRDS); - REQUIRE(mpm.get_current_state().line_policies[Y][0]->normal == MeshlinePolicy::Normal::MAX); + REQUIRE(mpm.get_current_state().line_policies[Y][0]->get_current_state().policy == MeshlinePolicy::Policy::THIRDS); + REQUIRE(mpm.get_current_state().line_policies[Y][0]->get_current_state().normal == MeshlinePolicy::Normal::MAX); REQUIRE(mpm.get_current_state().line_policies[Y][0]->coord == 1); REQUIRE(mpm.get_current_state().line_policies[Y][0]->get_current_state().is_enabled); } diff --git a/test/unit/domain/conflicts/test_conflict_too_close_meshline_policies.cpp b/test/unit/domain/conflicts/test_conflict_too_close_meshline_policies.cpp index eb054110..9986e3b0 100644 --- a/test/unit/domain/conflicts/test_conflict_too_close_meshline_policies.cpp +++ b/test/unit/domain/conflicts/test_conflict_too_close_meshline_policies.cpp @@ -45,8 +45,8 @@ SCENARIO("void ConflictTooCloseMeshlinePolicies::auto_solve(MeshlinePolicyManage REQUIRE(mpm.get_current_state().line_policies[Y][0].get() == x.get_current_state().solution); REQUIRE(mpm.get_current_state().line_policies[Y][0]->get_current_state().origins.size() == 1); REQUIRE(mpm.get_current_state().line_policies[Y][0]->get_current_state().origins[0] == &x); - REQUIRE(mpm.get_current_state().line_policies[Y][0]->policy == MeshlinePolicy::Policy::HALFS); - REQUIRE(mpm.get_current_state().line_policies[Y][0]->normal == MeshlinePolicy::Normal::NONE); + REQUIRE(mpm.get_current_state().line_policies[Y][0]->get_current_state().policy == MeshlinePolicy::Policy::HALFS); + REQUIRE(mpm.get_current_state().line_policies[Y][0]->get_current_state().normal == MeshlinePolicy::Normal::NONE); REQUIRE(mpm.get_current_state().line_policies[Y][0]->coord == 10.5); REQUIRE(mpm.get_current_state().line_policies[Y][0]->get_current_state().is_enabled); REQUIRE(x.get_current_state().is_solved); @@ -79,15 +79,15 @@ SCENARIO("void ConflictTooCloseMeshlinePolicies::auto_solve(MeshlinePolicyManage REQUIRE(mpm.get_current_state().line_policies[Y][0].get() == x.get_current_state().solution); REQUIRE(mpm.get_current_state().line_policies[Y][0]->get_current_state().origins.size() == 1); REQUIRE(mpm.get_current_state().line_policies[Y][0]->get_current_state().origins[0] == &x); - REQUIRE(mpm.get_current_state().line_policies[Y][0]->policy == MeshlinePolicy::Policy::HALFS); - REQUIRE(mpm.get_current_state().line_policies[Y][0]->normal == MeshlinePolicy::Normal::NONE); + REQUIRE(mpm.get_current_state().line_policies[Y][0]->get_current_state().policy == MeshlinePolicy::Policy::HALFS); + REQUIRE(mpm.get_current_state().line_policies[Y][0]->get_current_state().normal == MeshlinePolicy::Normal::NONE); REQUIRE(mpm.get_current_state().line_policies[Y][0]->coord == 10.5); REQUIRE(mpm.get_current_state().line_policies[Y][0]->get_current_state().is_enabled); REQUIRE(mpm.get_current_state().line_policies[Y][1].get() == y.get_current_state().solution); REQUIRE(mpm.get_current_state().line_policies[Y][1]->get_current_state().origins.size() == 1); REQUIRE(mpm.get_current_state().line_policies[Y][1]->get_current_state().origins[0] == &y); - REQUIRE(mpm.get_current_state().line_policies[Y][1]->policy == MeshlinePolicy::Policy::HALFS); - REQUIRE(mpm.get_current_state().line_policies[Y][1]->normal == MeshlinePolicy::Normal::NONE); + REQUIRE(mpm.get_current_state().line_policies[Y][1]->get_current_state().policy == MeshlinePolicy::Policy::HALFS); + REQUIRE(mpm.get_current_state().line_policies[Y][1]->get_current_state().normal == MeshlinePolicy::Normal::NONE); REQUIRE(mpm.get_current_state().line_policies[Y][1]->coord == 10.5); REQUIRE(mpm.get_current_state().line_policies[Y][1]->get_current_state().is_enabled); REQUIRE(x.get_current_state().is_solved); @@ -136,15 +136,15 @@ SCENARIO("void ConflictTooCloseMeshlinePolicies::auto_solve(MeshlinePolicyManage REQUIRE(mpm.get_current_state().line_policies[Y][0].get() == x.get_current_state().solution); REQUIRE(mpm.get_current_state().line_policies[Y][0]->get_current_state().origins.size() == 1); REQUIRE(mpm.get_current_state().line_policies[Y][0]->get_current_state().origins[0] == &x); - REQUIRE(mpm.get_current_state().line_policies[Y][0]->policy == MeshlinePolicy::Policy::HALFS); - REQUIRE(mpm.get_current_state().line_policies[Y][0]->normal == MeshlinePolicy::Normal::NONE); + REQUIRE(mpm.get_current_state().line_policies[Y][0]->get_current_state().policy == MeshlinePolicy::Policy::HALFS); + REQUIRE(mpm.get_current_state().line_policies[Y][0]->get_current_state().normal == MeshlinePolicy::Normal::NONE); REQUIRE(mpm.get_current_state().line_policies[Y][0]->coord == 10.5); REQUIRE(mpm.get_current_state().line_policies[Y][0]->get_current_state().is_enabled); REQUIRE(mpm.get_current_state().line_policies[X][0].get() == y.get_current_state().solution); REQUIRE(mpm.get_current_state().line_policies[X][0]->get_current_state().origins.size() == 1); REQUIRE(mpm.get_current_state().line_policies[X][0]->get_current_state().origins[0] == &y); - REQUIRE(mpm.get_current_state().line_policies[X][0]->policy == MeshlinePolicy::Policy::HALFS); - REQUIRE(mpm.get_current_state().line_policies[X][0]->normal == MeshlinePolicy::Normal::NONE); + REQUIRE(mpm.get_current_state().line_policies[X][0]->get_current_state().policy == MeshlinePolicy::Policy::HALFS); + REQUIRE(mpm.get_current_state().line_policies[X][0]->get_current_state().normal == MeshlinePolicy::Normal::NONE); REQUIRE(mpm.get_current_state().line_policies[X][0]->coord == 10.5); REQUIRE(mpm.get_current_state().line_policies[X][0]->get_current_state().is_enabled); REQUIRE(x.get_current_state().is_solved); @@ -225,29 +225,29 @@ SCENARIO("void ConflictTooCloseMeshlinePolicies::auto_solve(MeshlinePolicyManage REQUIRE(mpm.get_current_state().line_policies[Y][0].get() == w.get_current_state().solution); REQUIRE(mpm.get_current_state().line_policies[Y][0]->get_current_state().origins.size() == 1); REQUIRE(mpm.get_current_state().line_policies[Y][0]->get_current_state().origins[0] == &w); - REQUIRE(mpm.get_current_state().line_policies[Y][0]->policy == MeshlinePolicy::Policy::THIRDS); - REQUIRE(mpm.get_current_state().line_policies[Y][0]->normal == MeshlinePolicy::Normal::MIN); + REQUIRE(mpm.get_current_state().line_policies[Y][0]->get_current_state().policy == MeshlinePolicy::Policy::THIRDS); + REQUIRE(mpm.get_current_state().line_policies[Y][0]->get_current_state().normal == MeshlinePolicy::Normal::MIN); REQUIRE(mpm.get_current_state().line_policies[Y][0]->coord == 10.5); REQUIRE(mpm.get_current_state().line_policies[Y][0]->get_current_state().is_enabled); REQUIRE(mpm.get_current_state().line_policies[Y][1].get() == x.get_current_state().solution); REQUIRE(mpm.get_current_state().line_policies[Y][1]->get_current_state().origins.size() == 1); REQUIRE(mpm.get_current_state().line_policies[Y][1]->get_current_state().origins[0] == &x); - REQUIRE(mpm.get_current_state().line_policies[Y][1]->policy == MeshlinePolicy::Policy::THIRDS); - REQUIRE(mpm.get_current_state().line_policies[Y][1]->normal == MeshlinePolicy::Normal::MAX); + REQUIRE(mpm.get_current_state().line_policies[Y][1]->get_current_state().policy == MeshlinePolicy::Policy::THIRDS); + REQUIRE(mpm.get_current_state().line_policies[Y][1]->get_current_state().normal == MeshlinePolicy::Normal::MAX); REQUIRE(mpm.get_current_state().line_policies[Y][1]->coord == 10.5); REQUIRE(mpm.get_current_state().line_policies[Y][1]->get_current_state().is_enabled); REQUIRE(mpm.get_current_state().line_policies[X][0].get() == y.get_current_state().solution); REQUIRE(mpm.get_current_state().line_policies[X][0]->get_current_state().origins.size() == 1); REQUIRE(mpm.get_current_state().line_policies[X][0]->get_current_state().origins[0] == &y); - REQUIRE(mpm.get_current_state().line_policies[X][0]->policy == MeshlinePolicy::Policy::THIRDS); - REQUIRE(mpm.get_current_state().line_policies[X][0]->normal == MeshlinePolicy::Normal::MIN); + REQUIRE(mpm.get_current_state().line_policies[X][0]->get_current_state().policy == MeshlinePolicy::Policy::THIRDS); + REQUIRE(mpm.get_current_state().line_policies[X][0]->get_current_state().normal == MeshlinePolicy::Normal::MIN); REQUIRE(mpm.get_current_state().line_policies[X][0]->coord == 10.5); REQUIRE(mpm.get_current_state().line_policies[X][0]->get_current_state().is_enabled); REQUIRE(mpm.get_current_state().line_policies[X][1].get() == z.get_current_state().solution); REQUIRE(mpm.get_current_state().line_policies[X][1]->get_current_state().origins.size() == 1); REQUIRE(mpm.get_current_state().line_policies[X][1]->get_current_state().origins[0] == &z); - REQUIRE(mpm.get_current_state().line_policies[X][1]->policy == MeshlinePolicy::Policy::THIRDS); - REQUIRE(mpm.get_current_state().line_policies[X][1]->normal == MeshlinePolicy::Normal::MAX); + REQUIRE(mpm.get_current_state().line_policies[X][1]->get_current_state().policy == MeshlinePolicy::Policy::THIRDS); + REQUIRE(mpm.get_current_state().line_policies[X][1]->get_current_state().normal == MeshlinePolicy::Normal::MAX); REQUIRE(mpm.get_current_state().line_policies[X][1]->coord == 10.5); REQUIRE(mpm.get_current_state().line_policies[X][1]->get_current_state().is_enabled); REQUIRE(w.get_current_state().is_solved); diff --git a/test/unit/domain/test_meshline_policy_manager.cpp b/test/unit/domain/test_meshline_policy_manager.cpp index 97748a27..b9b236b3 100644 --- a/test/unit/domain/test_meshline_policy_manager.cpp +++ b/test/unit/domain/test_meshline_policy_manager.cpp @@ -92,8 +92,8 @@ bool const is_enabled)", "[meshline_policy_manager]") { REQUIRE(m->get_current_state().origins.size() == 1); REQUIRE(m->get_current_state().origins[0] == &e); REQUIRE(m->axis == X); - REQUIRE(m->policy == MeshlinePolicy::Policy::THIRDS); - REQUIRE(m->normal == MeshlinePolicy::Normal::MAX); + REQUIRE(m->get_current_state().policy == MeshlinePolicy::Policy::THIRDS); + REQUIRE(m->get_current_state().normal == MeshlinePolicy::Normal::MAX); REQUIRE(m->coord == 1); REQUIRE(m->get_current_state().is_enabled); } From 724236139034f0d732f70eaf02dd23d3c65d7b86 Mon Sep 17 00:00:00 2001 From: "tlepoix@localhost" Date: Fri, 6 Jun 2025 18:37:41 +0200 Subject: [PATCH 04/37] refactor : keep Structure and Processing settings at View level to last across following Scenes --- src/ui/qt/main_window.cpp | 23 ++++++++---------- src/ui/qt/processing_view/processing_view.cpp | 24 +++++++++++++++++++ src/ui/qt/processing_view/processing_view.hpp | 8 +++++++ src/ui/qt/structure_view/structure_view.cpp | 8 +++++-- src/ui/qt/structure_view/structure_view.hpp | 6 +++-- 5 files changed, 52 insertions(+), 17 deletions(-) diff --git a/src/ui/qt/main_window.cpp b/src/ui/qt/main_window.cpp index d4696e89..c3828bf6 100644 --- a/src/ui/qt/main_window.cpp +++ b/src/ui/qt/main_window.cpp @@ -48,9 +48,6 @@ MainWindow::MainWindow(app::OpenEMSH& oemsh, QWidget* parent) ui->processing_view->init(&oemsh.get_board()); handle_edition(); - ui->structure_view->set_display_plane(domain::XY); - ui->processing_view->get_current_state().scene->set_display_plane(domain::XY); - ui->processing_view->get_current_state().scene->set_display_view_axes({ true, true }); // ui->structure_view->setup_scale_max(ui->structure_view->scenes[domain::XY].polygons->boundingRect()); // on_tb_reset_clicked(); } @@ -88,7 +85,7 @@ void MainWindow::on_ag_styles_triggered(QAction* const action) { void MainWindow::on_rb_plane_xy_toggled(bool const is_checked) { if(is_checked) { ui->structure_view->set_display_plane(domain::XY); - ui->processing_view->get_current_state().scene->set_display_plane(domain::XY); + ui->processing_view->set_display_plane(domain::XY); } } @@ -96,7 +93,7 @@ void MainWindow::on_rb_plane_xy_toggled(bool const is_checked) { void MainWindow::on_rb_plane_yz_toggled(bool const is_checked) { if(is_checked) { ui->structure_view->set_display_plane(domain::YZ); - ui->processing_view->get_current_state().scene->set_display_plane(domain::YZ); + ui->processing_view->set_display_plane(domain::YZ); } } @@ -104,7 +101,7 @@ void MainWindow::on_rb_plane_yz_toggled(bool const is_checked) { void MainWindow::on_rb_plane_zx_toggled(bool const is_checked) { if(is_checked) { ui->structure_view->set_display_plane(domain::ZX); - ui->processing_view->get_current_state().scene->set_display_plane(domain::ZX); + ui->processing_view->set_display_plane(domain::ZX); } } @@ -154,46 +151,46 @@ void MainWindow::on_a_vertical_layout_triggered() { //****************************************************************************** void MainWindow::on_tb_show_all_mesh_clicked() { ui->structure_view->set_mesh_visibility(StructureScene::MeshVisibility::FULL); - ui->processing_view->get_current_state().scene->set_display_view_axes({ true, true }); + ui->processing_view->set_display_view_axes({ true, true }); ui->processing_view->fit(); } //****************************************************************************** void MainWindow::on_tb_show_horizontal_mesh_clicked() { ui->structure_view->set_mesh_visibility(StructureScene::MeshVisibility::HORIZONTAL); - ui->processing_view->get_current_state().scene->set_display_view_axes({ true, false }); + ui->processing_view->set_display_view_axes({ true, false }); ui->processing_view->fit(); } //****************************************************************************** void MainWindow::on_tb_show_vertical_mesh_clicked() { ui->structure_view->set_mesh_visibility(StructureScene::MeshVisibility::VERTICAL); - ui->processing_view->get_current_state().scene->set_display_view_axes({ false, true }); + ui->processing_view->set_display_view_axes({ false, true }); ui->processing_view->fit(); } //****************************************************************************** void MainWindow::on_tb_show_no_mesh_clicked() { ui->structure_view->set_mesh_visibility(StructureScene::MeshVisibility::NONE); - ui->processing_view->get_current_state().scene->set_display_view_axes({ false, false }); + ui->processing_view->set_display_view_axes({ false, false }); ui->processing_view->fit(); } //****************************************************************************** void MainWindow::on_tb_show_selected_clicked() { - ui->processing_view->get_current_state().scene->set_display(ProcessingScene::DisplayMode::SELECTED_CHAIN); + ui->processing_view->set_display(ProcessingScene::DisplayMode::SELECTED_CHAIN); ui->processing_view->fit(); } //****************************************************************************** void MainWindow::on_tb_show_displayed_clicked() { - ui->processing_view->get_current_state().scene->set_display(ProcessingScene::DisplayMode::STRUCTURE_VIEW); + ui->processing_view->set_display(ProcessingScene::DisplayMode::STRUCTURE_VIEW); ui->processing_view->fit(); } //****************************************************************************** void MainWindow::on_tb_show_everything_clicked() { - ui->processing_view->get_current_state().scene->set_display(ProcessingScene::DisplayMode::EVERYTHING); + ui->processing_view->set_display(ProcessingScene::DisplayMode::EVERYTHING); ui->processing_view->fit(); } diff --git a/src/ui/qt/processing_view/processing_view.cpp b/src/ui/qt/processing_view/processing_view.cpp index 15e023bb..272342f7 100644 --- a/src/ui/qt/processing_view/processing_view.cpp +++ b/src/ui/qt/processing_view/processing_view.cpp @@ -20,6 +20,9 @@ ProcessingView::ProcessingView(QWidget* parent) : QGraphicsView(parent) , board(nullptr) , current_timepoint(nullptr) +, display_mode(ProcessingScene::DisplayMode::SELECTED_CHAIN) +, plane_displayed_on_structure_view(domain::XY) +, axes_displayed_on_structure_view({ true, true }) { setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); setTransformationAnchor(QGraphicsView::AnchorUnderMouse); @@ -58,6 +61,24 @@ void ProcessingView::fit() { fitInView(get_current_state().scene->sceneRect(), Qt::KeepAspectRatio); } +//****************************************************************************** +void ProcessingView::set_display(ProcessingScene::DisplayMode mode) { + display_mode = mode; + get_current_state().scene->set_display(mode); +} + +//****************************************************************************** +void ProcessingView::set_display_view_axes(domain::ViewAxisSpace const& axes) { + axes_displayed_on_structure_view = axes; + get_current_state().scene->set_display_view_axes(axes); +} + +//****************************************************************************** +void ProcessingView::set_display_plane(domain::Plane plane) { + plane_displayed_on_structure_view = plane; + get_current_state().scene->set_display_plane(plane); +} + //****************************************************************************** ProcessingState& ProcessingView::get_current_state() { return states.at(current_timepoint); @@ -76,6 +97,9 @@ void ProcessingView::make_current_state() { populate(scene); scene->init(); + scene->set_display(display_mode); + scene->set_display_view_axes(axes_displayed_on_structure_view); + scene->set_display_plane(plane_displayed_on_structure_view); states.emplace(Caretaker::singleton().get_current_timepoint(), scene); go_to_current_state(); diff --git a/src/ui/qt/processing_view/processing_view.hpp b/src/ui/qt/processing_view/processing_view.hpp index a35bf834..b82f66ae 100644 --- a/src/ui/qt/processing_view/processing_view.hpp +++ b/src/ui/qt/processing_view/processing_view.hpp @@ -36,6 +36,10 @@ class ProcessingView : public QGraphicsView { void make_current_state(); void go_to_current_state(); + void set_display(ProcessingScene::DisplayMode mode); + void set_display_view_axes(domain::ViewAxisSpace const& axes); + void set_display_plane(domain::Plane plane); + public slots: void fit(); @@ -46,6 +50,10 @@ public slots: domain::Board const* board; Timepoint* current_timepoint; + ProcessingScene::DisplayMode display_mode; + domain::Plane plane_displayed_on_structure_view; + domain::ViewAxisSpace axes_displayed_on_structure_view; + void populate(ProcessingScene* scene); }; diff --git a/src/ui/qt/structure_view/structure_view.cpp b/src/ui/qt/structure_view/structure_view.cpp index f91da8bf..7248a1dd 100644 --- a/src/ui/qt/structure_view/structure_view.cpp +++ b/src/ui/qt/structure_view/structure_view.cpp @@ -43,11 +43,12 @@ static QPainterPath create_repair() { //****************************************************************************** StructureView::StructureView(QWidget* parent) : QGraphicsView(parent) +, board(nullptr) +, current_timepoint(nullptr) , repair(std::make_unique(create_repair())) , rotation(0) +, mesh_visibility_on_scene(StructureScene::MeshVisibility::FULL) , displayed_plane(domain::XY) -, board(nullptr) -, current_timepoint(nullptr) { setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); setTransformationAnchor(QGraphicsView::AnchorUnderMouse); @@ -139,6 +140,7 @@ void StructureView::fit() { //****************************************************************************** void StructureView::set_mesh_visibility(StructureScene::MeshVisibility mesh_visibility) { + mesh_visibility_on_scene = mesh_visibility; for(auto const plane : domain::AllPlane) get_current_state().scenes[plane]->set_mesh_visibility(mesh_visibility); } @@ -182,6 +184,8 @@ void StructureView::make_current_state() { std::make_unique(style_selector, this).release() }}; populate(scenes); + for(auto* scene : scenes) + scene->set_mesh_visibility(mesh_visibility_on_scene); states.try_emplace(Caretaker::singleton().get_current_timepoint(), scenes); diff --git a/src/ui/qt/structure_view/structure_view.hpp b/src/ui/qt/structure_view/structure_view.hpp index debaed96..d21808eb 100644 --- a/src/ui/qt/structure_view/structure_view.hpp +++ b/src/ui/qt/structure_view/structure_view.hpp @@ -65,13 +65,15 @@ class StructureView : public QGraphicsView { // + public set_scene() that wrap axis QStrings or repair // qreal scale_max; + domain::Board const* board; + Timepoint* current_timepoint; + std::unique_ptr const repair; using QGraphicsView::rotate; qreal rotation; + StructureScene::MeshVisibility mesh_visibility_on_scene; domain::Plane displayed_plane; - domain::Board const* board; - Timepoint* current_timepoint; void populate(domain::PlaneSpace scenes); }; From cffc10f2b1c1cc4c795ed13c1b5ffc42f659a2f8 Mon Sep 17 00:00:00 2001 From: "tlepoix@localhost" Date: Sun, 8 Jun 2025 20:18:53 +0200 Subject: [PATCH 05/37] fix GUI ProcessingScene selection visual glitch --- src/ui/qt/processing_view/processing_scene.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ui/qt/processing_view/processing_scene.cpp b/src/ui/qt/processing_view/processing_scene.cpp index 1a6ceacf..a42379ba 100644 --- a/src/ui/qt/processing_view/processing_scene.cpp +++ b/src/ui/qt/processing_view/processing_scene.cpp @@ -390,6 +390,7 @@ void ProcessingScene::display_selected_chain() { auto const selected = selected_nodes(); auto const highlighted = highlighted_nodes(); reset_visibility(false); + clearSelection(); for(auto const& list : { selected, highlighted }) for(auto* node : list) From 1bfeb7072826c0707542ca331addded0cc53b8bb Mon Sep 17 00:00:00 2001 From: "tlepoix@localhost" Date: Mon, 9 Jun 2025 02:23:23 +0200 Subject: [PATCH 06/37] add ProcessingView::clear() & StructureView::clear() --- src/ui/qt/processing_view/processing_view.cpp | 10 ++++++++++ src/ui/qt/processing_view/processing_view.hpp | 1 + src/ui/qt/structure_view/structure_view.cpp | 10 ++++++++++ src/ui/qt/structure_view/structure_view.hpp | 1 + 4 files changed, 22 insertions(+) diff --git a/src/ui/qt/processing_view/processing_view.cpp b/src/ui/qt/processing_view/processing_view.cpp index 272342f7..07b2cb72 100644 --- a/src/ui/qt/processing_view/processing_view.cpp +++ b/src/ui/qt/processing_view/processing_view.cpp @@ -36,6 +36,16 @@ ProcessingView::ProcessingView(QWidget* parent) void ProcessingView::init(domain::Board const* _board) { board = _board; } + +//****************************************************************************** +void ProcessingView::clear() { + for(auto [t, state] : states) + delete state.scene; + states.clear(); + board = nullptr; + current_timepoint = nullptr; +} + //****************************************************************************** void ProcessingView::wheelEvent(QWheelEvent* event) { if(event->modifiers() & Qt::ControlModifier) { diff --git a/src/ui/qt/processing_view/processing_view.hpp b/src/ui/qt/processing_view/processing_view.hpp index b82f66ae..04d39dd1 100644 --- a/src/ui/qt/processing_view/processing_view.hpp +++ b/src/ui/qt/processing_view/processing_view.hpp @@ -28,6 +28,7 @@ class ProcessingView : public QGraphicsView { public: explicit ProcessingView(QWidget* parent = nullptr); void init(domain::Board const* _board); + void clear(); ProcessingStyleSelector style_selector; std::map states; diff --git a/src/ui/qt/structure_view/structure_view.cpp b/src/ui/qt/structure_view/structure_view.cpp index 7248a1dd..c38e336d 100644 --- a/src/ui/qt/structure_view/structure_view.cpp +++ b/src/ui/qt/structure_view/structure_view.cpp @@ -65,6 +65,16 @@ void StructureView::init(domain::Board const* _board) { board = _board; } +//****************************************************************************** +void StructureView::clear() { + for(auto [t, state] : states) + for(auto* ptr : state.scenes) + delete ptr; + states.clear(); + board = nullptr; + current_timepoint = nullptr; +} + //****************************************************************************** void StructureView::drawForeground(QPainter* painter, QRectF const& rect) { QGraphicsView::drawForeground(painter, rect); diff --git a/src/ui/qt/structure_view/structure_view.hpp b/src/ui/qt/structure_view/structure_view.hpp index d21808eb..a3a878c3 100644 --- a/src/ui/qt/structure_view/structure_view.hpp +++ b/src/ui/qt/structure_view/structure_view.hpp @@ -37,6 +37,7 @@ class StructureView : public QGraphicsView { explicit StructureView(QWidget* parent = nullptr); ~StructureView() override; void init(domain::Board const* _board); + void clear(); void fit(); void rotate_view(qreal angle); From d65b1e495067c56bb1b9c9f4acacb573078d5ff2 Mon Sep 17 00:00:00 2001 From: "tlepoix@localhost" Date: Tue, 10 Jun 2025 21:07:00 +0200 Subject: [PATCH 07/37] add MainWindow::parse_and_display() decoupled from ctor --- src/main.cpp | 1 + src/ui/qt/main_window.cpp | 17 ++++++++++++----- src/ui/qt/main_window.hpp | 3 +++ 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 14c6d8e1..dfe311d6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -26,6 +26,7 @@ int main(int argc, char* argv[]) { setlocale(LC_NUMERIC, "C"); ui::qt::MainWindow w(oemsh); w.show(); + w.parse_and_display(); return QApplication::exec(); } diff --git a/src/ui/qt/main_window.cpp b/src/ui/qt/main_window.cpp index c3828bf6..a1fa2a44 100644 --- a/src/ui/qt/main_window.cpp +++ b/src/ui/qt/main_window.cpp @@ -41,19 +41,26 @@ MainWindow::MainWindow(app::OpenEMSH& oemsh, QWidget* parent) } ui->m_style->addAction(action); } +} - oemsh.parse(); +//****************************************************************************** +MainWindow::~MainWindow() = default; +//****************************************************************************** +void MainWindow::parse_and_display() { + setCursor(Qt::WaitCursor); + oemsh.parse(); ui->structure_view->init(&oemsh.get_board()); ui->processing_view->init(&oemsh.get_board()); handle_edition(); - -// ui->structure_view->setup_scale_max(ui->structure_view->scenes[domain::XY].polygons->boundingRect()); -// on_tb_reset_clicked(); + unsetCursor(); } //****************************************************************************** -MainWindow::~MainWindow() = default; +void MainWindow::clear() { + ui->structure_view->clear(); + ui->processing_view->clear(); +} //****************************************************************************** void MainWindow::set_style(Style const& style) { diff --git a/src/ui/qt/main_window.hpp b/src/ui/qt/main_window.hpp index d4bece79..a3f11426 100644 --- a/src/ui/qt/main_window.hpp +++ b/src/ui/qt/main_window.hpp @@ -75,6 +75,9 @@ private slots: MainWindow(app::OpenEMSH& oemsh, QWidget* parent = nullptr); ~MainWindow() override; + void parse_and_display(); + void clear(); + protected: void keyPressEvent(QKeyEvent* event) override; }; From da893ba319d61e65e5c1215b13adfa222e925ff2 Mon Sep 17 00:00:00 2001 From: "tlepoix@localhost" Date: Tue, 10 Jun 2025 21:50:51 +0200 Subject: [PATCH 08/37] add GUI 'Open CSX file' button --- src/app/openemsh.cpp | 10 ++++++++++ src/app/openemsh.hpp | 3 +++ src/ui/qt/main_window.cpp | 17 +++++++++++++++++ src/ui/qt/main_window.hpp | 1 + src/ui/qt/main_window.ui | 9 +++++++++ 5 files changed, 40 insertions(+) diff --git a/src/app/openemsh.cpp b/src/app/openemsh.cpp index bda69b63..5a4af80f 100644 --- a/src/app/openemsh.cpp +++ b/src/app/openemsh.cpp @@ -95,6 +95,16 @@ domain::Board const& OpenEMSH::get_board() const { return *board; } +//****************************************************************************** +void OpenEMSH::set_input(std::filesystem::path const& path) { + params.input = path; +} + +//****************************************************************************** +void OpenEMSH::set_output(std::filesystem::path const& path) { + params.output = path; +} + //****************************************************************************** void OpenEMSH::parse() { Caretaker::singleton().reset(); diff --git a/src/app/openemsh.hpp b/src/app/openemsh.hpp index 33d02316..174d4706 100644 --- a/src/app/openemsh.hpp +++ b/src/app/openemsh.hpp @@ -57,6 +57,9 @@ class OpenEMSH { Params const& get_params() const; domain::Board const& get_board() const; + void set_input(std::filesystem::path const& path); + void set_output(std::filesystem::path const& path); + // TODO implement validation checks on params here. // void check_x(); diff --git a/src/ui/qt/main_window.cpp b/src/ui/qt/main_window.cpp index a1fa2a44..13edbe82 100644 --- a/src/ui/qt/main_window.cpp +++ b/src/ui/qt/main_window.cpp @@ -5,6 +5,7 @@ ///***************************************************************************** #include +#include #include #include "domain/geometrics/space.hpp" @@ -241,6 +242,22 @@ void MainWindow::on_tb_processing_zoom_out_clicked() { ui->processing_view->scale(1 / 1.2, 1 / 1.2); } +//****************************************************************************** +void MainWindow::on_a_file_open_triggered() { + static QString from_dir("."); + QString const csx = QFileDialog::getOpenFileName(this, "Open CSX file", from_dir, "OpenEMS CSX file (*.csx *.xml)"); + + if(csx.isEmpty()) + return; // TODO log error + + from_dir = QFileInfo(csx).path(); + + clear(); + oemsh.set_input(csx.toStdString()); + parse_and_display(); + on_a_reset_triggered(); +} + //****************************************************************************** void MainWindow::on_a_edit_triggered() { auto* widget = static_cast(ui->toolBar->widgetForAction(ui->a_edit)); diff --git a/src/ui/qt/main_window.hpp b/src/ui/qt/main_window.hpp index a3f11426..58662281 100644 --- a/src/ui/qt/main_window.hpp +++ b/src/ui/qt/main_window.hpp @@ -63,6 +63,7 @@ private slots: void on_tb_structure_zoom_out_clicked(); void on_tb_processing_zoom_in_clicked(); void on_tb_processing_zoom_out_clicked(); + void on_a_file_open_triggered(); void on_a_edit_triggered(); void on_a_mesh_prev_triggered(); void on_a_mesh_next_triggered(); diff --git a/src/ui/qt/main_window.ui b/src/ui/qt/main_window.ui index b3924d3a..f71dd31d 100644 --- a/src/ui/qt/main_window.ui +++ b/src/ui/qt/main_window.ui @@ -350,6 +350,7 @@ + @@ -393,6 +394,14 @@ Reset view + + + 🗋 + + + Open CSX file + + 🖉 From 0137639afde5a0596813c2a7f44b3a92945de1ec Mon Sep 17 00:00:00 2001 From: "tlepoix@localhost" Date: Wed, 11 Jun 2025 15:42:39 +0200 Subject: [PATCH 09/37] add more GUI keyboard shortcut keys --- src/ui/qt/main_window.cpp | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/ui/qt/main_window.cpp b/src/ui/qt/main_window.cpp index 13edbe82..edf3ce64 100644 --- a/src/ui/qt/main_window.cpp +++ b/src/ui/qt/main_window.cpp @@ -384,8 +384,24 @@ void MainWindow::update_navigation_visibility() { //****************************************************************************** void MainWindow::keyPressEvent(QKeyEvent* event) { - if(event->key() == Qt::Key_E) { + if(event->key() == Qt::Key_E || event->key() == Qt::Key_Space) { on_a_edit_triggered(); + } else if(event->key() == Qt::Key_F) { + on_a_reset_triggered(); + } else if(event->modifiers() & Qt::ControlModifier && event->key() == Qt::Key_O) { + on_a_file_open_triggered(); + } else if(event->modifiers() & Qt::ControlModifier && event->key() == Qt::Key_S) { +// if(event->modifiers() & Qt::ShiftModifier) { +// on_a_file_save_as_triggered(); +// } else { +// on_a_file_save_triggered(); +// } + } else if(event->modifiers() & Qt::ControlModifier && event->key() == Qt::Key_Z) { + if(event->modifiers() & Qt::ShiftModifier) { + on_a_redo_triggered(); + } else { + on_a_undo_triggered(); + } } else { QWidget::keyPressEvent(event); } From 557c4f0e328c64d7d1f9c665c258957cbe550490 Mon Sep 17 00:00:00 2001 From: "tlepoix@localhost" Date: Fri, 13 Jun 2025 01:15:21 +0200 Subject: [PATCH 10/37] refactor : add GUI icon from .ui file --- src/ui/qt/main_window.cpp | 1 - src/ui/qt/main_window.ui | 7 ++++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/ui/qt/main_window.cpp b/src/ui/qt/main_window.cpp index edf3ce64..21b51d9e 100644 --- a/src/ui/qt/main_window.cpp +++ b/src/ui/qt/main_window.cpp @@ -30,7 +30,6 @@ MainWindow::MainWindow(app::OpenEMSH& oemsh, QWidget* parent) , dock_layout_order(false) { setWindowState(Qt::WindowMaximized); - setWindowIcon(QPixmap(":/openemsh.ico")); ui->setupUi(this); for(auto const& style : Style::available_styles) { diff --git a/src/ui/qt/main_window.ui b/src/ui/qt/main_window.ui index f71dd31d..63954205 100644 --- a/src/ui/qt/main_window.ui +++ b/src/ui/qt/main_window.ui @@ -13,6 +13,9 @@ OpenEMSH + + :/openemsh.ico + Qt::WindowMaximized @@ -455,6 +458,8 @@
ui/qt/processing_view/processing_view.hpp
- + + + From 7f915ed7dd88b8ed9d259a6b56f1e7bd9cfb21aa Mon Sep 17 00:00:00 2001 From: "tlepoix@localhost" Date: Tue, 24 Jun 2025 18:20:42 +0200 Subject: [PATCH 11/37] refactor : QWidget::setCursor() -> QGuiApplication::setOverrideCursor() --- src/ui/qt/edit/edit_dialog.cpp | 6 ++++-- src/ui/qt/main_window.cpp | 21 +++++++++++---------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/ui/qt/edit/edit_dialog.cpp b/src/ui/qt/edit/edit_dialog.cpp index de6ca167..8fa1804b 100644 --- a/src/ui/qt/edit/edit_dialog.cpp +++ b/src/ui/qt/edit/edit_dialog.cpp @@ -4,6 +4,8 @@ /// @author Thomas Lepoix ///***************************************************************************** +#include + #include "edit_model.hpp" #include "ui_edit_dialog.h" @@ -30,9 +32,9 @@ EditDialog::~EditDialog() = default; //****************************************************************************** void EditDialog::on_dbb_ok_accepted() { - setCursor(Qt::WaitCursor); + QGuiApplication::setOverrideCursor(Qt::WaitCursor); static_cast(ui->tv_properties->model())->commit(); - unsetCursor(); + QGuiApplication::restoreOverrideCursor(); accept(); } diff --git a/src/ui/qt/main_window.cpp b/src/ui/qt/main_window.cpp index 21b51d9e..7d3cf7aa 100644 --- a/src/ui/qt/main_window.cpp +++ b/src/ui/qt/main_window.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include "domain/geometrics/space.hpp" @@ -48,12 +49,12 @@ MainWindow::~MainWindow() = default; //****************************************************************************** void MainWindow::parse_and_display() { - setCursor(Qt::WaitCursor); + QGuiApplication::setOverrideCursor(Qt::WaitCursor); oemsh.parse(); ui->structure_view->init(&oemsh.get_board()); ui->processing_view->init(&oemsh.get_board()); handle_edition(); - unsetCursor(); + QGuiApplication::restoreOverrideCursor(); } //****************************************************************************** @@ -282,35 +283,35 @@ void MainWindow::edit_global_params() { //****************************************************************************** void MainWindow::on_a_mesh_prev_triggered() { - setCursor(Qt::WaitCursor); + QGuiApplication::setOverrideCursor(Qt::WaitCursor); oemsh.go_before_previous_step(); go_to_or_make_current_state(); - unsetCursor(); + QGuiApplication::restoreOverrideCursor(); } // TODO require some processing fit() //****************************************************************************** void MainWindow::on_a_mesh_next_triggered() { - setCursor(Qt::WaitCursor); + QGuiApplication::setOverrideCursor(Qt::WaitCursor); oemsh.run_next_step(); go_to_or_make_current_state(); - unsetCursor(); + QGuiApplication::restoreOverrideCursor(); } //****************************************************************************** void MainWindow::on_a_undo_triggered() { - setCursor(Qt::WaitCursor); + QGuiApplication::setOverrideCursor(Qt::WaitCursor); Caretaker::singleton().undo(); go_to_or_make_current_state(); - unsetCursor(); + QGuiApplication::restoreOverrideCursor(); } //****************************************************************************** void MainWindow::on_a_redo_triggered() { - setCursor(Qt::WaitCursor); + QGuiApplication::setOverrideCursor(Qt::WaitCursor); Caretaker::singleton().redo(); go_to_or_make_current_state(); - unsetCursor(); + QGuiApplication::restoreOverrideCursor(); } //****************************************************************************** From aea16e547077fafede169542847d9b96f8d010d8 Mon Sep 17 00:00:00 2001 From: "tlepoix@localhost" Date: Tue, 24 Jun 2025 20:29:08 +0200 Subject: [PATCH 12/37] add GUI 'Save' & 'Save as' buttons --- src/app/openemsh.cpp | 5 +++ src/app/openemsh.hpp | 1 + src/ui/qt/main_window.cpp | 88 ++++++++++++++++++++++++++++++++------- src/ui/qt/main_window.hpp | 4 ++ src/ui/qt/main_window.ui | 18 ++++++++ 5 files changed, 102 insertions(+), 14 deletions(-) diff --git a/src/app/openemsh.cpp b/src/app/openemsh.cpp index 5a4af80f..ab0b15f2 100644 --- a/src/app/openemsh.cpp +++ b/src/app/openemsh.cpp @@ -105,6 +105,11 @@ void OpenEMSH::set_output(std::filesystem::path const& path) { params.output = path; } +//****************************************************************************** +void OpenEMSH::set_output_format(Params::OutputFormat format) { + params.output_format = format; +} + //****************************************************************************** void OpenEMSH::parse() { Caretaker::singleton().reset(); diff --git a/src/app/openemsh.hpp b/src/app/openemsh.hpp index 174d4706..0acc91da 100644 --- a/src/app/openemsh.hpp +++ b/src/app/openemsh.hpp @@ -59,6 +59,7 @@ class OpenEMSH { void set_input(std::filesystem::path const& path); void set_output(std::filesystem::path const& path); + void set_output_format(Params::OutputFormat format); // TODO implement validation checks on params here. // void check_x(); diff --git a/src/ui/qt/main_window.cpp b/src/ui/qt/main_window.cpp index 7d3cf7aa..c8825b70 100644 --- a/src/ui/qt/main_window.cpp +++ b/src/ui/qt/main_window.cpp @@ -29,6 +29,9 @@ MainWindow::MainWindow(app::OpenEMSH& oemsh, QWidget* parent) , ui(std::make_unique()) , oemsh(oemsh) , dock_layout_order(false) +, csx_file(oemsh.get_params().input.empty() + ? QString() + : QString::fromStdString(oemsh.get_params().input.generic_string())) { setWindowState(Qt::WindowMaximized); ui->setupUi(this); @@ -50,6 +53,7 @@ MainWindow::~MainWindow() = default; //****************************************************************************** void MainWindow::parse_and_display() { QGuiApplication::setOverrideCursor(Qt::WaitCursor); + update_title(); oemsh.parse(); ui->structure_view->init(&oemsh.get_board()); ui->processing_view->init(&oemsh.get_board()); @@ -57,6 +61,14 @@ void MainWindow::parse_and_display() { QGuiApplication::restoreOverrideCursor(); } +//****************************************************************************** +void MainWindow::update_title() { + static QString const base_title(windowTitle()); + + if(!csx_file.isEmpty()) + setWindowTitle(base_title + " - " + csx_file); +} + //****************************************************************************** void MainWindow::clear() { ui->structure_view->clear(); @@ -242,20 +254,68 @@ void MainWindow::on_tb_processing_zoom_out_clicked() { ui->processing_view->scale(1 / 1.2, 1 / 1.2); } +//****************************************************************************** +static QString const format_filter_csx("OpenEMS CSX file (*.csx *.xml)"); + //****************************************************************************** void MainWindow::on_a_file_open_triggered() { - static QString from_dir("."); - QString const csx = QFileDialog::getOpenFileName(this, "Open CSX file", from_dir, "OpenEMS CSX file (*.csx *.xml)"); + QFileDialog dialog(this, ui->a_file_open->toolTip()); + dialog.setAcceptMode(QFileDialog::AcceptOpen); + dialog.setFileMode(QFileDialog::ExistingFile); + dialog.setNameFilter(format_filter_csx); + dialog.setDirectory(csx_file.isEmpty() ? QString(".") : QFileInfo(csx_file).path()); + if(dialog.exec()) { + QGuiApplication::setOverrideCursor(Qt::WaitCursor); + csx_file = dialog.selectedFiles().first(); + + clear(); + oemsh.set_input(csx_file.toStdString()); + parse_and_display(); + + on_a_reset_triggered(); + QGuiApplication::restoreOverrideCursor(); + } +} + +//****************************************************************************** +void MainWindow::on_a_file_save_triggered() { + QGuiApplication::setOverrideCursor(Qt::WaitCursor); - if(csx.isEmpty()) - return; // TODO log error + if(oemsh.get_params().output.empty()) + oemsh.set_output(csx_file.toStdString()); - from_dir = QFileInfo(csx).path(); + // TODO deduce from csx_file suffix + oemsh.set_output_format(app::OpenEMSH::Params::OutputFormat::CSX); - clear(); - oemsh.set_input(csx.toStdString()); - parse_and_display(); - on_a_reset_triggered(); + // TODO warn for overwrite? + + // TODO be sure in this mode, the XML file is edited and stuff like comments won't be discarded + oemsh.write(); + + QGuiApplication::restoreOverrideCursor(); +} + +//****************************************************************************** +void MainWindow::on_a_file_save_as_triggered() { + QFileDialog dialog(this, ui->a_file_save_as->toolTip()); + dialog.setAcceptMode(QFileDialog::AcceptSave); + dialog.setFileMode(QFileDialog::AnyFile); + dialog.setNameFilter(format_filter_csx); + dialog.setDefaultSuffix(".csx"); + dialog.setDirectory(csx_file.isEmpty() ? QString(".") : QFileInfo(csx_file).path()); + if(dialog.exec()) { + QGuiApplication::setOverrideCursor(Qt::WaitCursor); + csx_file = dialog.selectedFiles().first(); + update_title(); + + // TODO deduce from filter selected by user + // dialog.selectedNameFilter(); // TODO check actual suffix with that ? + oemsh.set_output_format(app::OpenEMSH::Params::OutputFormat::CSX); + oemsh.set_output(csx_file.toStdString()); + oemsh.write(); + + QGuiApplication::restoreOverrideCursor(); + } } //****************************************************************************** @@ -391,11 +451,11 @@ void MainWindow::keyPressEvent(QKeyEvent* event) { } else if(event->modifiers() & Qt::ControlModifier && event->key() == Qt::Key_O) { on_a_file_open_triggered(); } else if(event->modifiers() & Qt::ControlModifier && event->key() == Qt::Key_S) { -// if(event->modifiers() & Qt::ShiftModifier) { -// on_a_file_save_as_triggered(); -// } else { -// on_a_file_save_triggered(); -// } + if(event->modifiers() & Qt::ShiftModifier) { + on_a_file_save_as_triggered(); + } else { + on_a_file_save_triggered(); + } } else if(event->modifiers() & Qt::ControlModifier && event->key() == Qt::Key_Z) { if(event->modifiers() & Qt::ShiftModifier) { on_a_redo_triggered(); diff --git a/src/ui/qt/main_window.hpp b/src/ui/qt/main_window.hpp index 58662281..7580d5d0 100644 --- a/src/ui/qt/main_window.hpp +++ b/src/ui/qt/main_window.hpp @@ -28,9 +28,11 @@ class MainWindow : public QMainWindow { app::OpenEMSH& oemsh; bool dock_layout_order; + QString csx_file; void set_style(Style const& style); + void update_title(); void update_navigation_visibility(); void go_to_current_state(); void make_current_state_view(); @@ -64,6 +66,8 @@ private slots: void on_tb_processing_zoom_in_clicked(); void on_tb_processing_zoom_out_clicked(); void on_a_file_open_triggered(); + void on_a_file_save_triggered(); + void on_a_file_save_as_triggered(); void on_a_edit_triggered(); void on_a_mesh_prev_triggered(); void on_a_mesh_next_triggered(); diff --git a/src/ui/qt/main_window.ui b/src/ui/qt/main_window.ui index 63954205..3ac6b393 100644 --- a/src/ui/qt/main_window.ui +++ b/src/ui/qt/main_window.ui @@ -354,6 +354,8 @@ + + @@ -405,6 +407,22 @@ Open CSX file
+ + + 🗎 + + + Save mesh overwriting input CSX file + + + + + 🗎 + + + Save mesh as... + + 🖉 From 48b3f4f17f910b9db7f07ceed0ad1c032d784f95 Mon Sep 17 00:00:00 2001 From: "tlepoix@localhost" Date: Wed, 25 Jun 2025 15:37:00 +0200 Subject: [PATCH 13/37] Cli : disable -i requirement when -G --- src/ui/cli.cpp | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/ui/cli.cpp b/src/ui/cli.cpp index 7858685e..879c95c5 100644 --- a/src/ui/cli.cpp +++ b/src/ui/cli.cpp @@ -14,6 +14,7 @@ #include #include +#include "utils/concepts.hpp" #include "utils/unreachable.hpp" #include "cli.hpp" @@ -76,6 +77,18 @@ struct FutureConditional : CLI::Validator { } }; +//****************************************************************************** +template F> +struct JustDo : CLI::Validator { + JustDo(F const& func) { + name_ = "JustDo"; + func_ = [func](string const&) { + func(); + return std::string(); + }; + } +}; + //****************************************************************************** template auto make_overrider(auto& overrides_collector) { @@ -99,8 +112,9 @@ app::OpenEMSH::Params cli(int const argc, char* argv[]) { app.set_help_flag("-h,--help", "Display help and exit."); app.set_version_flag("--version", OEMSH_VERSION, "Display version and exit."); app.add_flag("-v,--verbose", params.verbose, "Verbose mode.")->capture_default_str(); - app.add_flag("-G", params.gui, "GUI mode."); - app.add_option("-i,--input", params.input, "Input CSX file.")->check(CLI::ExistingFile)->required(); + auto* g = app.add_flag("-G", params.gui, "GUI mode."); + auto* i = app.add_option("-i,--input", params.input, "Input CSX file.")->check(CLI::ExistingFile)->required(); + g->trigger_on_parse()->check(JustDo([i]() { i->required(false); })); // app.add_option("-o,--output", params.output, "Output CSX file. If different from input, will copy and extend it.")->check((!CLI::ExistingFile)|FutureConditional(params.force,"Cannot overwrite a file without --force")); app.add_option("-o,--output", params.output, "Output CSX file. If different from input, will copy and extend it.")->check(CLI::Validator((!CLI::ExistingFile)|FutureConditional(params.force,"Cannot overwrite a file without --force"), "FILE", "KO")); app.add_flag("-f,--force", params.force, "Allow overwriting a file.")->trigger_on_parse(); From ebf122fa68b2e0eff61eb7ad3cd570e903d31b66 Mon Sep 17 00:00:00 2001 From: "tlepoix@localhost" Date: Thu, 26 Jun 2025 19:32:25 +0200 Subject: [PATCH 14/37] add GUI standard icons for toolBar --- src/ui/qt/main_window.ui | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/ui/qt/main_window.ui b/src/ui/qt/main_window.ui index 3ac6b393..4c9fc503 100644 --- a/src/ui/qt/main_window.ui +++ b/src/ui/qt/main_window.ui @@ -398,6 +398,9 @@ Reset view + + + @@ -406,6 +409,9 @@ Open CSX file + + + @@ -414,6 +420,9 @@ Save mesh overwriting input CSX file + + + @@ -422,6 +431,9 @@ Save mesh as... + + + @@ -430,6 +442,9 @@ Edit parameters + + + @@ -438,6 +453,9 @@ Run the next meshing step + + + @@ -446,6 +464,9 @@ Go back to previous meshing step + + + @@ -454,6 +475,9 @@ Undo + + + @@ -462,6 +486,9 @@ Redo + + + From 75fedfdcb2580ac977b275c8eab0660c39a97e83 Mon Sep 17 00:00:00 2001 From: "tlepoix@localhost" Date: Fri, 27 Jun 2025 17:29:42 +0200 Subject: [PATCH 15/37] add GUI rudimentary Entity icons in dropdown menus --- src/CMakeLists.txt | 1 + src/ui/qt/icons.cpp | 146 ++++++++++++++++++ src/ui/qt/icons.hpp | 35 +++++ .../qt/processing_view/processing_scene.cpp | 3 +- .../qt/structure_view/structure_interval.hpp | 4 +- src/ui/qt/structure_view/structure_scene.cpp | 3 +- 6 files changed, 188 insertions(+), 4 deletions(-) create mode 100644 src/ui/qt/icons.cpp create mode 100644 src/ui/qt/icons.hpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index dc6fbafe..bb4aa3d1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -111,6 +111,7 @@ target_sources( openemsh_bin "${CMAKE_CURRENT_SOURCE_DIR}/ui/qt/structure_view/structure_scene.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/ui/qt/structure_view/structure_view.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/ui/qt/about_dialog.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/ui/qt/icons.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/ui/qt/main_window.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/ui/qt/style.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/ui/qt/resources.qrc" diff --git a/src/ui/qt/icons.cpp b/src/ui/qt/icons.cpp new file mode 100644 index 00000000..138ab37b --- /dev/null +++ b/src/ui/qt/icons.cpp @@ -0,0 +1,146 @@ +///***************************************************************************** +/// @date Feb 2021 +/// @copyright GPL-3.0-or-later +/// @author Thomas Lepoix +///***************************************************************************** + +#include +#include +#include +#include + +#include "structure_view/structure_conflict_colinear_edges.hpp" +#include "structure_view/structure_conflict_too_close_meshline_policies.hpp" +#include "structure_view/structure_interval.hpp" +#include "structure_view/structure_meshline.hpp" +#include "structure_view/structure_meshline_policy.hpp" + +#include "user_types.hpp" +#include "utils/concepts.hpp" +#include "utils/unreachable.hpp" + +#include "icons.hpp" + +namespace ui::qt { + +//****************************************************************************** +auto draw_icon_from_text(auto str) { + return [=](int size, QPainter& painter) { + painter.setFont(QFont("Arial", size / 2)); + painter.drawText(QRect(0, 0, size, size), Qt::AlignCenter, QString::fromUtf8(str)); + }; +} + +//****************************************************************************** +template Drawer> +QPixmap make_pixmap(Drawer const& draw) { + int const size = 24; + QPixmap icon(size, size); + icon.fill(Qt::transparent); + QPainter painter(&icon); + painter.setPen(Qt::black); + draw(size, painter); + return icon; +}; + +//****************************************************************************** +static QTransform const rotate_90_cw = []() { + QTransform t; + t.rotate(90); + return t; +} (); + +//****************************************************************************** +static QTransform const rotate_90_ccw = []() { + QTransform t; + t.rotate(-90); + return t; +} (); + +//****************************************************************************** +QPixmap crop_to_original_size(QPixmap const& to_crop, QPixmap const& orig) { + int xoffset = (to_crop.width() - orig.width()) / 2; + int yoffset = (to_crop.height() - orig.height()) / 2; + return to_crop.copy(xoffset, yoffset, orig.width(), orig.height()); +} + +//****************************************************************************** +QPixmap apply(QTransform const& transform, QPixmap const& pixmap) { + return crop_to_original_size(pixmap.transformed(transform), pixmap); +} + +//****************************************************************************** +#define PIXMAP_MAKER_DEF(NAME, FUNC) \ + QPixmap const& Icons::NAME() { \ + static QPixmap const pixmap = FUNC; \ + return pixmap; \ + } + +//****************************************************************************** +PIXMAP_MAKER_DEF(edge, make_pixmap(draw_icon_from_text("/"))) +PIXMAP_MAKER_DEF(polygon, make_pixmap(draw_icon_from_text("▱"))) +PIXMAP_MAKER_DEF(conflict_ce_v, make_pixmap(draw_icon_from_text("┆"))) +PIXMAP_MAKER_DEF(conflict_tcmlp_v, make_pixmap(draw_icon_from_text("‖"))) +PIXMAP_MAKER_DEF(interval_v, make_pixmap(draw_icon_from_text("▥"))) +PIXMAP_MAKER_DEF(meshline_v, make_pixmap(draw_icon_from_text("|"))) +PIXMAP_MAKER_DEF(meshline_policy_v, make_pixmap(draw_icon_from_text("⟊"))) +PIXMAP_MAKER_DEF(conflict_ce_h, apply(rotate_90_cw, conflict_ce_v())) +PIXMAP_MAKER_DEF(conflict_tcmlp_h, apply(rotate_90_cw, conflict_tcmlp_v())) +PIXMAP_MAKER_DEF(interval_h, apply(rotate_90_cw, interval_v())) +PIXMAP_MAKER_DEF(meshline_h, apply(rotate_90_cw, meshline_v())) +PIXMAP_MAKER_DEF(meshline_policy_h, apply(rotate_90_cw, meshline_policy_v())) + +//****************************************************************************** +QPixmap const& Icons::select(QGraphicsItem const* item) { + using namespace UserTypes; + + switch(item->type()) { + case PROCESSING_EDGE: [[fallthrough]]; + case STRUCTURE_EDGE: return edge(); + case PROCESSING_POLYGON: [[fallthrough]]; + case STRUCTURE_POLYGON: return polygon(); + case PROCESSING_INTERVAL: return interval_v(); + case STRUCTURE_INTERVAL: + switch(static_cast(item)->axis) { + case domain::ViewAxis::H: return interval_h(); + case domain::ViewAxis::V: return interval_v(); + default: unreachable(); + } + case PROCESSING_MESHLINE: return meshline_v(); + case STRUCTURE_MESHLINE: + switch(static_cast(item)->axis) { + case domain::ViewAxis::H: return meshline_h(); + case domain::ViewAxis::V: return meshline_v(); + default: unreachable(); + } + case PROCESSING_MESHLINE_POLICY: return meshline_policy_v(); + case STRUCTURE_MESHLINE_POLICY: + switch(static_cast(item)->axis) { + case domain::ViewAxis::H: return meshline_policy_h(); + case domain::ViewAxis::V: return meshline_policy_v(); + default: unreachable(); + } + case PROCESSING_CONFLICT_CE: return conflict_ce_v(); + case STRUCTURE_CONFLICT_CE: + switch(static_cast(item)->axis) { + case domain::ViewAxis::H: return conflict_ce_h(); + case domain::ViewAxis::V: return conflict_ce_v(); + default: unreachable(); + } + case PROCESSING_CONFLICT_TCMLP: return conflict_tcmlp_v(); + case STRUCTURE_CONFLICT_TCMLP: + switch(static_cast(item)->axis) { + case domain::ViewAxis::H: return conflict_tcmlp_h(); + case domain::ViewAxis::V: return conflict_tcmlp_v(); + default: unreachable(); + } + case PROCESSING_CONFLICT_EIP: [[fallthrough]]; + case PROCESSING_AXIS: [[fallthrough]]; + case PROCESSING_PLANE: [[fallthrough]]; + default: + static auto const empty = QPixmap(); + return empty; + } +} + +} // namespace ui::qt diff --git a/src/ui/qt/icons.hpp b/src/ui/qt/icons.hpp new file mode 100644 index 00000000..5e044122 --- /dev/null +++ b/src/ui/qt/icons.hpp @@ -0,0 +1,35 @@ +///***************************************************************************** +/// @date Feb 2021 +/// @copyright GPL-3.0-or-later +/// @author Thomas Lepoix +///***************************************************************************** + +#pragma once + +#include + +class QGraphicsItem; + +namespace ui::qt { + +//****************************************************************************** +struct Icons { + Icons() = delete; + + static QPixmap const& select(QGraphicsItem const* item); + + static QPixmap const& edge(); + static QPixmap const& polygon(); + static QPixmap const& interval_h(); + static QPixmap const& interval_v(); + static QPixmap const& meshline_policy_h(); + static QPixmap const& meshline_policy_v(); + static QPixmap const& conflict_ce_h(); + static QPixmap const& conflict_ce_v(); + static QPixmap const& conflict_tcmlp_h(); + static QPixmap const& conflict_tcmlp_v(); + static QPixmap const& meshline_h(); + static QPixmap const& meshline_v(); +}; + +} // namespace ui::qt diff --git a/src/ui/qt/processing_view/processing_scene.cpp b/src/ui/qt/processing_view/processing_scene.cpp index a42379ba..3f91c5f6 100644 --- a/src/ui/qt/processing_view/processing_scene.cpp +++ b/src/ui/qt/processing_view/processing_scene.cpp @@ -17,6 +17,7 @@ #include "domain/mesh/meshline.hpp" #include "domain/mesh/meshline_policy.hpp" #include "ui/qt/data_keys.hpp" +#include "ui/qt/icons.hpp" #include "ui/qt/edit/edit_dialog.hpp" #include "ui/qt/edit/edit_model.hpp" #include "ui/qt/utils/qlist_utils.hpp" @@ -494,7 +495,7 @@ void ProcessingScene::edit(QList nodes, QPoint const& pos) { for(auto* node : nodes) { // // TODO add entity icon auto const title = make_title(node); - auto* action = new QAction(title, &menu); + auto* action = new QAction(Icons::select(node), title, &menu); menu.addAction(action); QObject::connect(action, &QAction::triggered, [&edit_node, node, title]() { edit_node(node, title); diff --git a/src/ui/qt/structure_view/structure_interval.hpp b/src/ui/qt/structure_view/structure_interval.hpp index 3715662d..099a9d50 100644 --- a/src/ui/qt/structure_view/structure_interval.hpp +++ b/src/ui/qt/structure_view/structure_interval.hpp @@ -38,12 +38,12 @@ class StructureInterval : public QGraphicsRectItem { int type() const override; + domain::ViewAxis const axis; + protected: void paint(QPainter* painter, QStyleOptionGraphicsItem const* option, QWidget* widget = nullptr) override; QVariant itemChange(GraphicsItemChange change, QVariant const& value) override; - domain::ViewAxis const axis; - private: domain::Interval const* const interval; }; diff --git a/src/ui/qt/structure_view/structure_scene.cpp b/src/ui/qt/structure_view/structure_scene.cpp index 9934e563..93acc5d4 100644 --- a/src/ui/qt/structure_view/structure_scene.cpp +++ b/src/ui/qt/structure_view/structure_scene.cpp @@ -22,6 +22,7 @@ #include "domain/mesh/meshline_policy.hpp" #include "utils/unreachable.hpp" #include "ui/qt/data_keys.hpp" +#include "ui/qt/icons.hpp" #include "ui/qt/user_types.hpp" #include "structure_conflict_colinear_edges.hpp" #include "structure_conflict_too_close_meshline_policies.hpp" @@ -317,7 +318,7 @@ void StructureScene::mousePressEvent(QGraphicsSceneMouseEvent* event) { if(item->type() == StructurePolygon::Type) title.append(" - " + item->data(DataKeys::NAME).toString()); - auto* action = new QAction(title, &menu); + auto* action = new QAction(Icons::select(item), title, &menu); menu.addAction(action); QObject::connect(action, &QAction::triggered, [item]() { item->setSelected(true); From 26edf6feb0264b1ae411a3d6e6d704cf4460c2be Mon Sep 17 00:00:00 2001 From: "tlepoix@localhost" Date: Mon, 30 Jun 2025 18:52:36 +0200 Subject: [PATCH 16/37] refactor GUI button 'Reset view' -> 'Fit view' --- src/ui/qt/main_window.cpp | 6 +++--- src/ui/qt/main_window.hpp | 2 +- src/ui/qt/main_window.ui | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/ui/qt/main_window.cpp b/src/ui/qt/main_window.cpp index c8825b70..3ba0cea1 100644 --- a/src/ui/qt/main_window.cpp +++ b/src/ui/qt/main_window.cpp @@ -135,7 +135,7 @@ void MainWindow::on_tb_anchor_clicked(bool const is_checked) { } //****************************************************************************** -void MainWindow::on_a_reset_triggered() { +void MainWindow::on_a_fit_triggered() { ui->processing_view->get_current_state().scene->fit_containers(); ui->processing_view->get_current_state().scene->fit_scene(); ui->processing_view->fit(); @@ -272,7 +272,7 @@ void MainWindow::on_a_file_open_triggered() { oemsh.set_input(csx_file.toStdString()); parse_and_display(); - on_a_reset_triggered(); + on_a_fit_triggered(); QGuiApplication::restoreOverrideCursor(); } } @@ -447,7 +447,7 @@ void MainWindow::keyPressEvent(QKeyEvent* event) { if(event->key() == Qt::Key_E || event->key() == Qt::Key_Space) { on_a_edit_triggered(); } else if(event->key() == Qt::Key_F) { - on_a_reset_triggered(); + on_a_fit_triggered(); } else if(event->modifiers() & Qt::ControlModifier && event->key() == Qt::Key_O) { on_a_file_open_triggered(); } else if(event->modifiers() & Qt::ControlModifier && event->key() == Qt::Key_S) { diff --git a/src/ui/qt/main_window.hpp b/src/ui/qt/main_window.hpp index 7580d5d0..c8b4b92b 100644 --- a/src/ui/qt/main_window.hpp +++ b/src/ui/qt/main_window.hpp @@ -47,7 +47,7 @@ private slots: void on_rb_plane_yz_toggled(bool const is_checked); void on_rb_plane_zx_toggled(bool const is_checked); void on_tb_anchor_clicked(bool const is_checked); - void on_a_reset_triggered(); + void on_a_fit_triggered(); void on_a_horizontal_layout_triggered(); void on_a_vertical_layout_triggered(); void on_tb_show_all_mesh_clicked(); diff --git a/src/ui/qt/main_window.ui b/src/ui/qt/main_window.ui index 4c9fc503..e81806a6 100644 --- a/src/ui/qt/main_window.ui +++ b/src/ui/qt/main_window.ui @@ -360,7 +360,7 @@ - + @@ -391,12 +391,12 @@ Horizontal layout - + - Reset view + Fit view From 5f3354706955a3a307f3cc0a55b871938de35ac2 Mon Sep 17 00:00:00 2001 From: "tlepoix@localhost" Date: Tue, 8 Jul 2025 04:20:08 +0200 Subject: [PATCH 17/37] Cli : no args is like -G --- src/ui/cli.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/ui/cli.cpp b/src/ui/cli.cpp index 879c95c5..900f72da 100644 --- a/src/ui/cli.cpp +++ b/src/ui/cli.cpp @@ -184,6 +184,11 @@ app::OpenEMSH::Params cli(int const argc, char* argv[]) { app.add_flag("--policy-lines", params.with_meshline_policies, "Include meshline policies in output.")->group("Output options")->default_str(to_string(params.with_meshline_policies)); // app.add_flag("--policy-lines", params.with_meshline_policies, "Include meshline policies in output.")->group("Output options")->capture_default_str(); + app.preparse_callback([g](size_t argc) { + if(argc == 0) + g->force_callback()->default_val(true); + }); + try { app.parse(argc, argv); } catch(CLI::Success const& e) { From e8a053be1c4aef0e5aa1b1f0123e68f1e898e95c Mon Sep 17 00:00:00 2001 From: "tlepoix@localhost" Date: Wed, 9 Jul 2025 08:29:10 +0200 Subject: [PATCH 18/37] refactor GUI buttons : use grouped checkable buttons --- src/ui/qt/main_window.cpp | 27 ++++---- src/ui/qt/main_window.hpp | 6 +- src/ui/qt/main_window.ui | 137 +++++++++++++++++++++++++++++++++++--- 3 files changed, 142 insertions(+), 28 deletions(-) diff --git a/src/ui/qt/main_window.cpp b/src/ui/qt/main_window.cpp index 3ba0cea1..fe639a5d 100644 --- a/src/ui/qt/main_window.cpp +++ b/src/ui/qt/main_window.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include "domain/geometrics/space.hpp" #include "edit/edit_dialog.hpp" @@ -36,6 +37,8 @@ MainWindow::MainWindow(app::OpenEMSH& oemsh, QWidget* parent) setWindowState(Qt::WindowMaximized); ui->setupUi(this); + // TODO Init StructureView & ProcessingView stuff from buttons default values + for(auto const& style : Style::available_styles) { auto* const action = new QAction(style.name, ui->ag_styles); action->setCheckable(true); @@ -102,27 +105,21 @@ void MainWindow::on_ag_styles_triggered(QAction* const action) { } //****************************************************************************** -void MainWindow::on_rb_plane_xy_toggled(bool const is_checked) { - if(is_checked) { - ui->structure_view->set_display_plane(domain::XY); - ui->processing_view->set_display_plane(domain::XY); - } +void MainWindow::on_tb_plane_xy_clicked() { + ui->structure_view->set_display_plane(domain::XY); + ui->processing_view->set_display_plane(domain::XY); } //****************************************************************************** -void MainWindow::on_rb_plane_yz_toggled(bool const is_checked) { - if(is_checked) { - ui->structure_view->set_display_plane(domain::YZ); - ui->processing_view->set_display_plane(domain::YZ); - } +void MainWindow::on_tb_plane_yz_clicked() { + ui->structure_view->set_display_plane(domain::YZ); + ui->processing_view->set_display_plane(domain::YZ); } //****************************************************************************** -void MainWindow::on_rb_plane_zx_toggled(bool const is_checked) { - if(is_checked) { - ui->structure_view->set_display_plane(domain::ZX); - ui->processing_view->set_display_plane(domain::ZX); - } +void MainWindow::on_tb_plane_zx_clicked() { + ui->structure_view->set_display_plane(domain::ZX); + ui->processing_view->set_display_plane(domain::ZX); } //****************************************************************************** diff --git a/src/ui/qt/main_window.hpp b/src/ui/qt/main_window.hpp index c8b4b92b..2282b2e8 100644 --- a/src/ui/qt/main_window.hpp +++ b/src/ui/qt/main_window.hpp @@ -43,9 +43,9 @@ private slots: void on_a_about_triggered(); void on_a_doc_oems_meshing_triggered() const; void on_ag_styles_triggered(QAction* const action); - void on_rb_plane_xy_toggled(bool const is_checked); - void on_rb_plane_yz_toggled(bool const is_checked); - void on_rb_plane_zx_toggled(bool const is_checked); + void on_tb_plane_xy_clicked(); + void on_tb_plane_yz_clicked(); + void on_tb_plane_zx_clicked(); void on_tb_anchor_clicked(bool const is_checked); void on_a_fit_triggered(); void on_a_horizontal_layout_triggered(); diff --git a/src/ui/qt/main_window.ui b/src/ui/qt/main_window.ui index e81806a6..741506fb 100644 --- a/src/ui/qt/main_window.ui +++ b/src/ui/qt/main_window.ui @@ -64,6 +64,16 @@ + + + + QFrame::Shape::HLine + + + QFrame::Shadow::Sunken + + + @@ -72,6 +82,15 @@ Show selected chain only + + true + + + true + + + bg_show_processing + @@ -82,6 +101,12 @@ Show axes/plane currently displayed only + + true + + + bg_show_processing + @@ -92,6 +117,22 @@ Show everything + + true + + + bg_show_processing + + + + + + + QFrame::Shape::HLine + + + QFrame::Shadow::Sunken + @@ -102,6 +143,15 @@ Curved wires + + true + + + true + + + bg_wires + @@ -112,6 +162,12 @@ Direct wires + + true + + + bg_wires + @@ -214,6 +270,16 @@ + + + + QFrame::Shape::HLine + + + QFrame::Shadow::Sunken + + + @@ -222,6 +288,15 @@ Show mesh + + true + + + true + + + bg_show_mesh + @@ -232,6 +307,12 @@ Show vertical meshlines only + + true + + + bg_show_mesh + @@ -242,6 +323,12 @@ Show horizontal meshlines only + + true + + + bg_show_mesh + @@ -252,49 +339,73 @@ Don't show mesh + + true + + + bg_show_mesh + - - + + + QFrame::Shape::HLine + + + QFrame::Shadow::Sunken + + + + + YZ View YZ plane - - Qt::NoFocus + + true + + bg_plane + - + ZX View ZX plane - - Qt::NoFocus + + true + + bg_plane + - + XY View XY plane - - Qt::NoFocus + + true true + + bg_plane + @@ -491,6 +602,12 @@ + + + + + + ui::qt::StructureView From b6ecb3111326255dbceda8ae5724e6c60b450fcd Mon Sep 17 00:00:00 2001 From: "tlepoix@localhost" Date: Wed, 9 Jul 2025 08:55:23 +0200 Subject: [PATCH 19/37] refactor GUI buttons : set common width and autoRaise --- src/ui/qt/main_window.ui | 171 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 171 insertions(+) diff --git a/src/ui/qt/main_window.ui b/src/ui/qt/main_window.ui index 741506fb..29163c5e 100644 --- a/src/ui/qt/main_window.ui +++ b/src/ui/qt/main_window.ui @@ -49,6 +49,15 @@ true + + true + + + + 0 + 0 + + @@ -62,6 +71,15 @@ true + + true + + + + 0 + 0 + + @@ -88,6 +106,15 @@ true + + true + + + + 0 + 0 + + bg_show_processing @@ -104,6 +131,15 @@ true + + true + + + + 0 + 0 + + bg_show_processing @@ -120,6 +156,15 @@ true + + true + + + + 0 + 0 + + bg_show_processing @@ -149,6 +194,15 @@ true + + true + + + + 0 + 0 + + bg_wires @@ -165,6 +219,15 @@ true + + true + + + + 0 + 0 + + bg_wires @@ -216,6 +279,15 @@ true + + true + + + + 0 + 0 + + @@ -229,6 +301,15 @@ true + + true + + + + 0 + 0 + + @@ -242,6 +323,15 @@ true + + true + + + + 0 + 0 + + @@ -255,6 +345,15 @@ true + + true + + + + 0 + 0 + + @@ -268,6 +367,15 @@ true + + true + + + + 0 + 0 + + @@ -294,6 +402,15 @@ true + + true + + + + 0 + 0 + + bg_show_mesh @@ -310,6 +427,15 @@ true + + true + + + + 0 + 0 + + bg_show_mesh @@ -326,6 +452,15 @@ true + + true + + + + 0 + 0 + + bg_show_mesh @@ -342,6 +477,15 @@ true + + true + + + + 0 + 0 + + bg_show_mesh @@ -368,6 +512,15 @@ true + + true + + + + 0 + 0 + + bg_plane @@ -384,6 +537,15 @@ true + + true + + + + 0 + 0 + + bg_plane @@ -403,6 +565,15 @@ true + + true + + + + 0 + 0 + + bg_plane From f90138475074712223ad5deafdea32dc9013b13f Mon Sep 17 00:00:00 2001 From: "tlepoix@localhost" Date: Thu, 10 Jul 2025 22:51:02 +0200 Subject: [PATCH 20/37] fix unwanted Timepoint remember when going back before editing --- src/app/openemsh.cpp | 2 +- src/ui/qt/main_window.cpp | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/app/openemsh.cpp b/src/app/openemsh.cpp index ab0b15f2..2b0541dc 100644 --- a/src/app/openemsh.cpp +++ b/src/app/openemsh.cpp @@ -218,7 +218,7 @@ void OpenEMSH::run_from_step(Step step) const { //****************************************************************************** void OpenEMSH::go_before(Step step) const { auto& c = Caretaker::singleton(); - c.go_and_remember( + c.go_without_remembering( c.find_first_ancestor_with_annotation_that( [&step](IAnnotation const* annotation) { return static_cast(annotation)->before_step == step; diff --git a/src/ui/qt/main_window.cpp b/src/ui/qt/main_window.cpp index fe639a5d..f6e3f185 100644 --- a/src/ui/qt/main_window.cpp +++ b/src/ui/qt/main_window.cpp @@ -425,7 +425,6 @@ void MainWindow::make_current_state_view() { //****************************************************************************** void MainWindow::handle_edition(app::Step const redo_from) { oemsh.run_from_step(redo_from); - Caretaker::singleton().remember_current_timepoint(); make_current_state_view(); } From 87c06204867226ffea24f7d026b4bee15207bb0a Mon Sep 17 00:00:00 2001 From: "tlepoix@localhost" Date: Sat, 12 Jul 2025 21:50:48 +0200 Subject: [PATCH 21/37] refactor GUI buttons : keep in sync while traveling in states --- src/ui/qt/main_window.cpp | 35 ++++++++++++++----- src/ui/qt/main_window.hpp | 3 +- .../qt/processing_view/processing_scene.cpp | 9 +++-- .../qt/processing_view/processing_scene.hpp | 3 +- src/ui/qt/processing_view/processing_view.cpp | 23 +++++++++--- src/ui/qt/processing_view/processing_view.hpp | 6 ++-- src/ui/qt/structure_view/structure_scene.cpp | 7 ++++ src/ui/qt/structure_view/structure_scene.hpp | 2 ++ src/ui/qt/structure_view/structure_view.cpp | 12 ++++--- src/ui/qt/structure_view/structure_view.hpp | 2 +- 10 files changed, 78 insertions(+), 24 deletions(-) diff --git a/src/ui/qt/main_window.cpp b/src/ui/qt/main_window.cpp index f6e3f185..cd2b6ff6 100644 --- a/src/ui/qt/main_window.cpp +++ b/src/ui/qt/main_window.cpp @@ -17,6 +17,7 @@ #include "processing_view/processing_view.hpp" #include "structure_view/structure_view.hpp" #include "utils/state_management.hpp" +#include "utils/unreachable.hpp" #include "about_dialog.hpp" #include "ui_main_window.h" @@ -195,30 +196,30 @@ void MainWindow::on_tb_show_no_mesh_clicked() { //****************************************************************************** void MainWindow::on_tb_show_selected_clicked() { - ui->processing_view->set_display(ProcessingScene::DisplayMode::SELECTED_CHAIN); + ui->processing_view->set_display_mode(ProcessingScene::DisplayMode::SELECTED_CHAIN); ui->processing_view->fit(); } //****************************************************************************** void MainWindow::on_tb_show_displayed_clicked() { - ui->processing_view->set_display(ProcessingScene::DisplayMode::STRUCTURE_VIEW); + ui->processing_view->set_display_mode(ProcessingScene::DisplayMode::STRUCTURE_VIEW); ui->processing_view->fit(); } //****************************************************************************** void MainWindow::on_tb_show_everything_clicked() { - ui->processing_view->set_display(ProcessingScene::DisplayMode::EVERYTHING); + ui->processing_view->set_display_mode(ProcessingScene::DisplayMode::EVERYTHING); ui->processing_view->fit(); } //****************************************************************************** void MainWindow::on_tb_curved_wires_clicked() { - ui->processing_view->get_current_state().scene->set_wire_style(nodegraph::Wire::Style::CURVED); + ui->processing_view->set_wire_style(nodegraph::Wire::Style::CURVED); } //****************************************************************************** void MainWindow::on_tb_direct_wires_clicked() { - ui->processing_view->get_current_state().scene->set_wire_style(nodegraph::Wire::Style::DIRECT); + ui->processing_view->set_wire_style(nodegraph::Wire::Style::DIRECT); } //****************************************************************************** @@ -385,7 +386,8 @@ void MainWindow::go_to_or_make_current_state() { void MainWindow::go_to_current_state() { ui->structure_view->go_to_current_state(); ui->processing_view->go_to_current_state(); - update_navigation_visibility(); + update_navigation_buttons_visibility(); + update_show_buttons_pressing(); // TODO handle passing selection from a scene to its own future couterpart } @@ -419,7 +421,7 @@ void MainWindow::make_current_state_view() { ui->processing_view->states[t].scene, &ProcessingScene::edited, this, &MainWindow::handle_edition); - update_navigation_visibility(); + update_navigation_buttons_visibility(); } //****************************************************************************** @@ -430,13 +432,30 @@ void MainWindow::handle_edition(app::Step const redo_from) { } //****************************************************************************** -void MainWindow::update_navigation_visibility() { +void MainWindow::update_navigation_buttons_visibility() { ui->a_undo->setEnabled(Caretaker::singleton().can_undo()); ui->a_redo->setEnabled(Caretaker::singleton().can_redo()); ui->a_mesh_prev->setEnabled(oemsh.can_go_before()); ui->a_mesh_next->setEnabled(oemsh.can_run_a_next_step()); }; +//****************************************************************************** +void MainWindow::update_show_buttons_pressing() { + switch(ui->structure_view->get_mesh_visibility()) { + case StructureScene::MeshVisibility::NONE: ui->tb_show_no_mesh->setChecked(true); break; + case StructureScene::MeshVisibility::VERTICAL: ui->tb_show_vertical_mesh->setChecked(true); break; + case StructureScene::MeshVisibility::HORIZONTAL: ui->tb_show_horizontal_mesh->setChecked(true); break; + case StructureScene::MeshVisibility::FULL: ui->tb_show_all_mesh->setChecked(true); break; + default: unreachable(); + } + + switch(ui->processing_view->get_display_mode()) { + case ProcessingScene::DisplayMode::EVERYTHING: ui->tb_show_everything->setChecked(true); break; + case ProcessingScene::DisplayMode::STRUCTURE_VIEW: ui->tb_show_displayed->setChecked(true); break; + case ProcessingScene::DisplayMode::SELECTED_CHAIN: ui->tb_show_selected->setChecked(true); break; + default: unreachable(); + } +} //****************************************************************************** void MainWindow::keyPressEvent(QKeyEvent* event) { diff --git a/src/ui/qt/main_window.hpp b/src/ui/qt/main_window.hpp index 2282b2e8..d5bca1c5 100644 --- a/src/ui/qt/main_window.hpp +++ b/src/ui/qt/main_window.hpp @@ -33,7 +33,8 @@ class MainWindow : public QMainWindow { void set_style(Style const& style); void update_title(); - void update_navigation_visibility(); + void update_navigation_buttons_visibility(); + void update_show_buttons_pressing(); void go_to_current_state(); void make_current_state_view(); void go_to_or_make_current_state(); diff --git a/src/ui/qt/processing_view/processing_scene.cpp b/src/ui/qt/processing_view/processing_scene.cpp index 3f91c5f6..4569ed94 100644 --- a/src/ui/qt/processing_view/processing_scene.cpp +++ b/src/ui/qt/processing_view/processing_scene.cpp @@ -61,7 +61,7 @@ ProcessingScene::~ProcessingScene() { //****************************************************************************** void ProcessingScene::init() { - set_display(display_mode); + set_display_mode(display_mode); } //****************************************************************************** @@ -342,7 +342,7 @@ void ProcessingScene::set_display_plane(domain::Plane plane) { } //****************************************************************************** -void ProcessingScene::set_display(DisplayMode mode) { +void ProcessingScene::set_display_mode(DisplayMode mode) { display_mode = mode; switch(display_mode) { case DisplayMode::EVERYTHING: @@ -361,6 +361,11 @@ void ProcessingScene::set_display(DisplayMode mode) { } } +//****************************************************************************** +ProcessingScene::DisplayMode ProcessingScene::get_display_mode() const { + return display_mode; +} + //****************************************************************************** void ProcessingScene::display_structure_view() { reset_visibility(); diff --git a/src/ui/qt/processing_view/processing_scene.hpp b/src/ui/qt/processing_view/processing_scene.hpp index 486c8d57..f4ee1465 100644 --- a/src/ui/qt/processing_view/processing_scene.hpp +++ b/src/ui/qt/processing_view/processing_scene.hpp @@ -73,7 +73,8 @@ class ProcessingScene : public QGraphicsScene { QList selected_nodes() const; QList highlighted_nodes() const; void reset_visibility(bool are_visible = true) const; - void set_display(DisplayMode mode); + DisplayMode get_display_mode() const; + void set_display_mode(DisplayMode mode); void set_display_view_axes(domain::ViewAxisSpace const& axes); void set_display_plane(domain::Plane plane); diff --git a/src/ui/qt/processing_view/processing_view.cpp b/src/ui/qt/processing_view/processing_view.cpp index 07b2cb72..1c7b8ef6 100644 --- a/src/ui/qt/processing_view/processing_view.cpp +++ b/src/ui/qt/processing_view/processing_view.cpp @@ -20,7 +20,7 @@ ProcessingView::ProcessingView(QWidget* parent) : QGraphicsView(parent) , board(nullptr) , current_timepoint(nullptr) -, display_mode(ProcessingScene::DisplayMode::SELECTED_CHAIN) +, wire_style(nodegraph::Wire::Style::CURVED) , plane_displayed_on_structure_view(domain::XY) , axes_displayed_on_structure_view({ true, true }) { @@ -72,9 +72,13 @@ void ProcessingView::fit() { } //****************************************************************************** -void ProcessingView::set_display(ProcessingScene::DisplayMode mode) { - display_mode = mode; - get_current_state().scene->set_display(mode); +ProcessingScene::DisplayMode ProcessingView::get_display_mode() { + return get_current_state().scene->get_display_mode(); +} + +//****************************************************************************** +void ProcessingView::set_display_mode(ProcessingScene::DisplayMode mode) { + get_current_state().scene->set_display_mode(mode); } //****************************************************************************** @@ -89,6 +93,13 @@ void ProcessingView::set_display_plane(domain::Plane plane) { get_current_state().scene->set_display_plane(plane); } +//****************************************************************************** +void ProcessingView::set_wire_style(nodegraph::Wire::Style style) { + wire_style = style; + for(auto& [t, state] : states) + state.scene->set_wire_style(style); +} + //****************************************************************************** ProcessingState& ProcessingView::get_current_state() { return states.at(current_timepoint); @@ -107,9 +118,11 @@ void ProcessingView::make_current_state() { populate(scene); scene->init(); - scene->set_display(display_mode); + if(auto* current_scene = static_cast(this->scene()); current_scene) + scene->set_display_mode(current_scene->get_display_mode()); scene->set_display_view_axes(axes_displayed_on_structure_view); scene->set_display_plane(plane_displayed_on_structure_view); + scene->set_wire_style(wire_style); states.emplace(Caretaker::singleton().get_current_timepoint(), scene); go_to_current_state(); diff --git a/src/ui/qt/processing_view/processing_view.hpp b/src/ui/qt/processing_view/processing_view.hpp index 04d39dd1..7466a6df 100644 --- a/src/ui/qt/processing_view/processing_view.hpp +++ b/src/ui/qt/processing_view/processing_view.hpp @@ -37,9 +37,11 @@ class ProcessingView : public QGraphicsView { void make_current_state(); void go_to_current_state(); - void set_display(ProcessingScene::DisplayMode mode); + ProcessingScene::DisplayMode get_display_mode(); + void set_display_mode(ProcessingScene::DisplayMode mode); void set_display_view_axes(domain::ViewAxisSpace const& axes); void set_display_plane(domain::Plane plane); + void set_wire_style(nodegraph::Wire::Style style); public slots: void fit(); @@ -51,7 +53,7 @@ public slots: domain::Board const* board; Timepoint* current_timepoint; - ProcessingScene::DisplayMode display_mode; + nodegraph::Wire::Style wire_style; domain::Plane plane_displayed_on_structure_view; domain::ViewAxisSpace axes_displayed_on_structure_view; diff --git a/src/ui/qt/structure_view/structure_scene.cpp b/src/ui/qt/structure_view/structure_scene.cpp index 93acc5d4..d3522541 100644 --- a/src/ui/qt/structure_view/structure_scene.cpp +++ b/src/ui/qt/structure_view/structure_scene.cpp @@ -64,6 +64,7 @@ StructureScene::StructureScene(StructureStyleSelector& style_selector, QObject* , intervals{{ new StructureGroup(), new StructureGroup() }} , meshlines{{ new StructureGroup(), new StructureGroup() }} , meshline_policies{{ new StructureGroup(), new StructureGroup() }} +, mesh_visibility_on_view(MeshVisibility::FULL) { // Adding order matters. addItem(edges); @@ -259,6 +260,7 @@ void StructureScene::clear_all() { //****************************************************************************** void StructureScene::set_mesh_visibility(MeshVisibility mesh_visibility) { + mesh_visibility_on_view = mesh_visibility; switch(mesh_visibility) { case MeshVisibility::NONE: meshlines[domain::H]->setVisible(false); @@ -281,6 +283,11 @@ void StructureScene::set_mesh_visibility(MeshVisibility mesh_visibility) { } } +//****************************************************************************** +StructureScene::MeshVisibility StructureScene::get_mesh_visibility() const { + return mesh_visibility_on_view; +} + // TODO use simple click + double click to select / deselect //****************************************************************************** void StructureScene::mousePressEvent(QGraphicsSceneMouseEvent* event) { diff --git a/src/ui/qt/structure_view/structure_scene.hpp b/src/ui/qt/structure_view/structure_scene.hpp index 2d0f04e5..17733b56 100644 --- a/src/ui/qt/structure_view/structure_scene.hpp +++ b/src/ui/qt/structure_view/structure_scene.hpp @@ -73,6 +73,7 @@ class StructureScene : public QGraphicsScene { void clear_all(); void set_mesh_visibility(MeshVisibility mesh_visibility); + MeshVisibility get_mesh_visibility() const; StructureStyleSelector& style_selector; //private: //TODO @@ -96,6 +97,7 @@ public slots: void select_counterparts(QList foreign_items); private: bool is_select_counterparts_locked = false; + MeshVisibility mesh_visibility_on_view; protected: void mousePressEvent(QGraphicsSceneMouseEvent* event) override; diff --git a/src/ui/qt/structure_view/structure_view.cpp b/src/ui/qt/structure_view/structure_view.cpp index c38e336d..c95d5c4a 100644 --- a/src/ui/qt/structure_view/structure_view.cpp +++ b/src/ui/qt/structure_view/structure_view.cpp @@ -47,7 +47,6 @@ StructureView::StructureView(QWidget* parent) , current_timepoint(nullptr) , repair(std::make_unique(create_repair())) , rotation(0) -, mesh_visibility_on_scene(StructureScene::MeshVisibility::FULL) , displayed_plane(domain::XY) { setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); @@ -148,9 +147,13 @@ void StructureView::fit() { fitInView(static_cast(scene())->polygons->boundingRect() + QMarginsF(5, 5, 5, 5), Qt::KeepAspectRatio); } +//****************************************************************************** +StructureScene::MeshVisibility StructureView::get_mesh_visibility() { + return get_current_state().scenes.front()->get_mesh_visibility(); +} + //****************************************************************************** void StructureView::set_mesh_visibility(StructureScene::MeshVisibility mesh_visibility) { - mesh_visibility_on_scene = mesh_visibility; for(auto const plane : domain::AllPlane) get_current_state().scenes[plane]->set_mesh_visibility(mesh_visibility); } @@ -194,8 +197,9 @@ void StructureView::make_current_state() { std::make_unique(style_selector, this).release() }}; populate(scenes); - for(auto* scene : scenes) - scene->set_mesh_visibility(mesh_visibility_on_scene); + if(auto* current_scene = static_cast(scene()); current_scene) + for(auto* scene : scenes) + scene->set_mesh_visibility(current_scene->get_mesh_visibility()); states.try_emplace(Caretaker::singleton().get_current_timepoint(), scenes); diff --git a/src/ui/qt/structure_view/structure_view.hpp b/src/ui/qt/structure_view/structure_view.hpp index a3a878c3..82fb8047 100644 --- a/src/ui/qt/structure_view/structure_view.hpp +++ b/src/ui/qt/structure_view/structure_view.hpp @@ -44,6 +44,7 @@ class StructureView : public QGraphicsView { void reset_rotation(); qreal get_rotation() const; + StructureScene::MeshVisibility get_mesh_visibility(); void set_mesh_visibility(StructureScene::MeshVisibility mesh_visibility); void set_display_plane(domain::Plane plane); @@ -73,7 +74,6 @@ class StructureView : public QGraphicsView { using QGraphicsView::rotate; qreal rotation; - StructureScene::MeshVisibility mesh_visibility_on_scene; domain::Plane displayed_plane; void populate(domain::PlaneSpace scenes); From 82065de3ca35c0af8e7cd7aeea3f62880976d3c0 Mon Sep 17 00:00:00 2001 From: "tlepoix@localhost" Date: Sun, 13 Jul 2025 02:23:27 +0200 Subject: [PATCH 22/37] refactor : merge EditModel:: signals request_to_go_before() & edited() -> edit_from() --- src/ui/qt/edit/edit_model.hpp | 4 +-- ...l_conflict_too_close_meshline_policies.cpp | 6 ++-- src/ui/qt/edit/edit_model_edge.cpp | 6 ++-- src/ui/qt/edit/edit_model_global.cpp | 6 ++-- src/ui/qt/edit/edit_model_interval.cpp | 6 ++-- src/ui/qt/edit/edit_model_meshline_policy.cpp | 6 ++-- src/ui/qt/main_window.cpp | 33 ++++++++----------- src/ui/qt/main_window.hpp | 6 ++-- .../qt/processing_view/processing_scene.cpp | 7 ++-- .../qt/processing_view/processing_scene.hpp | 4 +-- 10 files changed, 39 insertions(+), 45 deletions(-) diff --git a/src/ui/qt/edit/edit_model.hpp b/src/ui/qt/edit/edit_model.hpp index d40449cf..879e8bd5 100644 --- a/src/ui/qt/edit/edit_model.hpp +++ b/src/ui/qt/edit/edit_model.hpp @@ -8,6 +8,7 @@ #include +#include #include #include "app/steps.hpp" @@ -38,8 +39,7 @@ class EditModel : public QStandardItemModel { static bool try_from_map(std::map const& map, QString const& in, O& out); signals: - void request_to_go_before(app::Step step); - void edited(app::Step const redo_from); + void edit_from(app::Step from, std::function const& edit); }; //****************************************************************************** diff --git a/src/ui/qt/edit/edit_model_conflict_too_close_meshline_policies.cpp b/src/ui/qt/edit/edit_model_conflict_too_close_meshline_policies.cpp index 60ad2504..885145fc 100644 --- a/src/ui/qt/edit/edit_model_conflict_too_close_meshline_policies.cpp +++ b/src/ui/qt/edit/edit_model_conflict_too_close_meshline_policies.cpp @@ -39,9 +39,9 @@ void EditModelConflictTooCloseMeshlinePolicies::commit() { }; if(std::ranges::all_of(does_succeed, is_true)) { - emit request_to_go_before(app::Step::DETECT_AND_SOLVE_TCMLP); - conflict->set_next_state(state); - emit edited(app::Step::DETECT_AND_SOLVE_TCMLP); + emit edit_from(app::Step::DETECT_AND_SOLVE_TCMLP, [&]() { + conflict->set_next_state(state); + }); } } diff --git a/src/ui/qt/edit/edit_model_edge.cpp b/src/ui/qt/edit/edit_model_edge.cpp index 13ae9cd1..03be4b29 100644 --- a/src/ui/qt/edit/edit_model_edge.cpp +++ b/src/ui/qt/edit/edit_model_edge.cpp @@ -39,9 +39,9 @@ void EditModelEdge::commit() { }; if(std::ranges::all_of(does_succeed, is_true)) { - emit request_to_go_before(app::Step::DETECT_CONFLICT_EIP); - edge->set_next_state(state); - emit edited(app::Step::DETECT_CONFLICT_EIP); + emit edit_from(app::Step::DETECT_CONFLICT_EIP, [&]() { + edge->set_next_state(state); + }); } } diff --git a/src/ui/qt/edit/edit_model_global.cpp b/src/ui/qt/edit/edit_model_global.cpp index ce8b84dd..4ed7e4a9 100644 --- a/src/ui/qt/edit/edit_model_global.cpp +++ b/src/ui/qt/edit/edit_model_global.cpp @@ -54,9 +54,9 @@ void EditModelGlobal::commit() { }; if(std::ranges::all_of(does_succeed, is_true)) { - emit request_to_go_before(app::Step::DETECT_CONFLICT_EIP); - global->set_next_state(params); - emit edited(app::Step::DETECT_CONFLICT_EIP); + emit edit_from(app::Step::DETECT_CONFLICT_EIP, [&]() { + global->set_next_state(params); + }); } } diff --git a/src/ui/qt/edit/edit_model_interval.cpp b/src/ui/qt/edit/edit_model_interval.cpp index 155f71ec..f42fa137 100644 --- a/src/ui/qt/edit/edit_model_interval.cpp +++ b/src/ui/qt/edit/edit_model_interval.cpp @@ -51,9 +51,9 @@ void EditModelInterval::commit() { }; if(std::ranges::all_of(does_succeed, is_true)) { - emit request_to_go_before(app::Step::MESH); - interval->set_next_state(state); - emit edited(app::Step::MESH); + emit edit_from(app::Step::MESH, [&] { + interval->set_next_state(state); + }); } } diff --git a/src/ui/qt/edit/edit_model_meshline_policy.cpp b/src/ui/qt/edit/edit_model_meshline_policy.cpp index 5ed8ee05..78efc8d9 100644 --- a/src/ui/qt/edit/edit_model_meshline_policy.cpp +++ b/src/ui/qt/edit/edit_model_meshline_policy.cpp @@ -83,9 +83,9 @@ void EditModelMeshlinePolicy::commit() { }; if(std::ranges::all_of(does_succeed, is_true)) { - emit request_to_go_before(app::Step::DETECT_INTERVALS); - meshline_policy->set_next_state(state); - emit edited(app::Step::DETECT_INTERVALS); + emit edit_from(app::Step::DETECT_INTERVALS, [&]() { + meshline_policy->set_next_state(state); + }); } } diff --git a/src/ui/qt/main_window.cpp b/src/ui/qt/main_window.cpp index cd2b6ff6..0f4db083 100644 --- a/src/ui/qt/main_window.cpp +++ b/src/ui/qt/main_window.cpp @@ -61,7 +61,7 @@ void MainWindow::parse_and_display() { oemsh.parse(); ui->structure_view->init(&oemsh.get_board()); ui->processing_view->init(&oemsh.get_board()); - handle_edition(); + run(); QGuiApplication::restoreOverrideCursor(); } @@ -329,13 +329,8 @@ void MainWindow::edit_global_params() { EditModelGlobal model(oemsh.get_board().global_params.get()); EditDialog edit(&model, "global parameters"); connect( - &model, &EditModel::request_to_go_before, - [this](app::Step step) { - this->oemsh.go_before(step); - }); - connect( - &model, &EditModel::edited, - this, &MainWindow::handle_edition); + &model, &EditModel::edit_from, + this, &MainWindow::handle_edition_from); edit.exec(); } @@ -412,25 +407,25 @@ void MainWindow::make_current_state_view() { this, &MainWindow::edit_global_params); connect( - ui->processing_view->states[t].scene, &ProcessingScene::request_to_go_before, - [this](app::Step step) { - this->oemsh.go_before(step); - }); - - connect( - ui->processing_view->states[t].scene, &ProcessingScene::edited, - this, &MainWindow::handle_edition); + ui->processing_view->states[t].scene, &ProcessingScene::edit_from, + this, &MainWindow::handle_edition_from); update_navigation_buttons_visibility(); } //****************************************************************************** -void MainWindow::handle_edition(app::Step const redo_from) { - oemsh.run_from_step(redo_from); - +void MainWindow::run(app::Step from) { + oemsh.run_from_step(from); make_current_state_view(); } +//****************************************************************************** +void MainWindow::handle_edition_from(app::Step from, std::function const& edit) { + oemsh.go_before(from); + edit(); + run(from); +} + //****************************************************************************** void MainWindow::update_navigation_buttons_visibility() { ui->a_undo->setEnabled(Caretaker::singleton().can_undo()); diff --git a/src/ui/qt/main_window.hpp b/src/ui/qt/main_window.hpp index d5bca1c5..adec0955 100644 --- a/src/ui/qt/main_window.hpp +++ b/src/ui/qt/main_window.hpp @@ -6,6 +6,7 @@ #pragma once +#include #include #include @@ -38,7 +39,7 @@ class MainWindow : public QMainWindow { void go_to_current_state(); void make_current_state_view(); void go_to_or_make_current_state(); - void edit_global_params(); + void run(app::Step from = app::Step::DETECT_CONFLICT_EIP); private slots: void on_a_about_triggered(); @@ -75,7 +76,8 @@ private slots: void on_a_undo_triggered(); void on_a_redo_triggered(); - void handle_edition(app::Step const redo_from = app::Step::DETECT_CONFLICT_EIP); + void edit_global_params(); + void handle_edition_from(app::Step from, std::function const& edit); public: MainWindow(app::OpenEMSH& oemsh, QWidget* parent = nullptr); diff --git a/src/ui/qt/processing_view/processing_scene.cpp b/src/ui/qt/processing_view/processing_scene.cpp index 4569ed94..c2c132b7 100644 --- a/src/ui/qt/processing_view/processing_scene.cpp +++ b/src/ui/qt/processing_view/processing_scene.cpp @@ -481,11 +481,8 @@ void ProcessingScene::edit(QList nodes, QPoint const& pos) { EditDialog edit(model, title); model->setParent(&edit); connect( - model, &EditModel::request_to_go_before, - this, &ProcessingScene::request_to_go_before); - connect( - model, &EditModel::edited, - this, &ProcessingScene::edited); + model, &EditModel::edit_from, + this, &ProcessingScene::edit_from); edit.exec(); } }; diff --git a/src/ui/qt/processing_view/processing_scene.hpp b/src/ui/qt/processing_view/processing_scene.hpp index f4ee1465..0e4ec3ea 100644 --- a/src/ui/qt/processing_view/processing_scene.hpp +++ b/src/ui/qt/processing_view/processing_scene.hpp @@ -11,6 +11,7 @@ #include #include +#include #include #include @@ -106,8 +107,7 @@ private slots: void selection_changed(QList items); void requires_fit(); void edit_global_params(); - void request_to_go_before(app::Step step); - void edited(app::Step const redo_from); + void edit_from(app::Step from, std::function const& edit); public slots: void select_counterparts(QList foreign_items); From eace56e510a64fa491d5ec60765c185a0bf5d24f Mon Sep 17 00:00:00 2001 From: "tlepoix@localhost" Date: Mon, 14 Jul 2025 00:19:41 +0200 Subject: [PATCH 23/37] fix unwanted Timepoint remember when going back before global editing --- src/app/openemsh.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/openemsh.cpp b/src/app/openemsh.cpp index 2b0541dc..1fc90768 100644 --- a/src/app/openemsh.cpp +++ b/src/app/openemsh.cpp @@ -114,6 +114,7 @@ void OpenEMSH::set_output_format(Params::OutputFormat format) { void OpenEMSH::parse() { Caretaker::singleton().reset(); board = ParserFromCsx::run(params.input, static_cast(params), params.override_from_cli); + Caretaker::singleton().remember_current_timepoint(); } //****************************************************************************** @@ -142,7 +143,6 @@ void OpenEMSH::run(std::set const& steps) const { }; if(steps.contains(Step::DETECT_CONFLICT_EIP)) { - Caretaker::singleton().remember_current_timepoint(); annotate(Step::DETECT_CONFLICT_EIP); board->detect_edges_in_polygons(); } From 36aef122896792d49b61461aa8dad600f38128ff Mon Sep 17 00:00:00 2001 From: "tlepoix@localhost" Date: Mon, 14 Jul 2025 01:14:12 +0200 Subject: [PATCH 24/37] refactor OpenEMSH::run() --- src/app/openemsh.cpp | 54 +++++++++++++------------------------------- 1 file changed, 16 insertions(+), 38 deletions(-) diff --git a/src/app/openemsh.cpp b/src/app/openemsh.cpp index 1fc90768..af8e1a69 100644 --- a/src/app/openemsh.cpp +++ b/src/app/openemsh.cpp @@ -9,6 +9,7 @@ #include "infra/serializers/serializer_to_plantuml.hpp" #include "infra/serializers/serializer_to_prettyprint.hpp" +#include "utils/concepts.hpp" #include "utils/unreachable.hpp" #include "openemsh.hpp" @@ -138,46 +139,23 @@ void OpenEMSH::write() const { //****************************************************************************** void OpenEMSH::run(std::set const& steps) const { - auto const annotate = [](Step step) { - Caretaker::singleton().annotate_current_timepoint(make_unique(step)); + auto const handle = [&] F>(Step step, F const& func) { + if(steps.contains(step)) { + Caretaker::singleton().annotate_current_timepoint(make_unique(step)); + func(); + } }; - if(steps.contains(Step::DETECT_CONFLICT_EIP)) { - annotate(Step::DETECT_CONFLICT_EIP); - board->detect_edges_in_polygons(); - } - if(steps.contains(Step::DETECT_CONFLICT_CE)) { - annotate(Step::DETECT_CONFLICT_CE); - board->detect_colinear_edges(); - } - if(steps.contains(Step::DETECT_NON_CONFLICTING_EDGES)) { - annotate(Step::DETECT_NON_CONFLICTING_EDGES); - board->detect_non_conflicting_edges(); - } - if(steps.contains(Step::ADD_FIXED_MLP)) { - annotate(Step::ADD_FIXED_MLP); - board->add_fixed_meshline_policies(); - } - if(steps.contains(Step::SOLVE_ALL_EIP)) { - annotate(Step::SOLVE_ALL_EIP); - board->auto_solve_all_edge_in_polygon(); - } - if(steps.contains(Step::SOLVE_ALL_CE)) { - annotate(Step::SOLVE_ALL_CE); - board->auto_solve_all_colinear_edges(); - } - if(steps.contains(Step::DETECT_AND_SOLVE_TCMLP)) { - annotate(Step::DETECT_AND_SOLVE_TCMLP); - board->detect_and_solve_too_close_meshline_policies(); - } - if(steps.contains(Step::DETECT_INTERVALS)) { - annotate(Step::DETECT_INTERVALS); - board->detect_intervals(); - } - if(steps.contains(Step::MESH)) { - annotate(Step::MESH); - board->mesh(); - } + using enum Step; + handle(DETECT_CONFLICT_EIP, [&] { board->detect_edges_in_polygons(); }); + handle(DETECT_CONFLICT_CE, [&] { board->detect_colinear_edges(); }); + handle(DETECT_NON_CONFLICTING_EDGES, [&] { board->detect_non_conflicting_edges(); }); + handle(ADD_FIXED_MLP, [&] { board->add_fixed_meshline_policies(); }); + handle(SOLVE_ALL_EIP, [&] { board->auto_solve_all_edge_in_polygon(); }); + handle(SOLVE_ALL_CE, [&] { board->auto_solve_all_colinear_edges(); }); + handle(DETECT_AND_SOLVE_TCMLP, [&] { board->detect_and_solve_too_close_meshline_policies(); }); + handle(DETECT_INTERVALS, [&] { board->detect_intervals(); }); + handle(MESH, [&] { board->mesh(); }); Caretaker::singleton().remember_current_timepoint(); } From 171a7c6ad3772d42f500da3ed60205f7c005ece2 Mon Sep 17 00:00:00 2001 From: "tlepoix@localhost" Date: Mon, 14 Jul 2025 01:30:44 +0200 Subject: [PATCH 25/37] add GUI keyboard shortcut key ESC -> unselect everything --- src/ui/qt/main_window.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ui/qt/main_window.cpp b/src/ui/qt/main_window.cpp index 0f4db083..a6b2d1f3 100644 --- a/src/ui/qt/main_window.cpp +++ b/src/ui/qt/main_window.cpp @@ -472,6 +472,10 @@ void MainWindow::keyPressEvent(QKeyEvent* event) { } else { on_a_undo_triggered(); } + } else if(event->key() == Qt::Key_Escape) { + // Will be forwarded to the 3 StructureScenes + if(auto* scene = ui->processing_view->scene(); scene) + scene->clearSelection(); } else { QWidget::keyPressEvent(event); } From 71fbb47f5dcb800fdde78ea4b1f490292504b387 Mon Sep 17 00:00:00 2001 From: "tlepoix@localhost" Date: Mon, 14 Jul 2025 03:15:05 +0200 Subject: [PATCH 26/37] add GUI keyboard shortcut key for button groups --- src/ui/qt/main_window.cpp | 40 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/ui/qt/main_window.cpp b/src/ui/qt/main_window.cpp index a6b2d1f3..1338b1c4 100644 --- a/src/ui/qt/main_window.cpp +++ b/src/ui/qt/main_window.cpp @@ -472,6 +472,46 @@ void MainWindow::keyPressEvent(QKeyEvent* event) { } else { on_a_undo_triggered(); } + } else if(event->key() == Qt::Key_Greater) { + on_a_mesh_next_triggered(); + } else if(event->key() == Qt::Key_Less) { + on_a_mesh_prev_triggered(); + } else if(event->key() == Qt::Key_1) { + ui->tb_show_selected->click(); + } else if(event->key() == Qt::Key_2) { + ui->tb_show_displayed->click(); + } else if(event->key() == Qt::Key_3) { + ui->tb_show_everything->click(); + } else if(event->key() == Qt::Key_C) { + ui->tb_curved_wires->click(); + } else if(event->key() == Qt::Key_D) { + ui->tb_direct_wires->click(); + } else if(event->key() == Qt::Key_X) { + ui->tb_show_all_mesh->click(); + } else if(event->key() == Qt::Key_V) { + ui->tb_show_vertical_mesh->click(); + } else if(event->key() == Qt::Key_H) { + ui->tb_show_horizontal_mesh->click(); + } else if(event->key() == Qt::Key_Period) { + ui->tb_show_no_mesh->click(); + } else if(event->key() == Qt::Key_PageUp) { + if(auto* b = ui->bg_plane->checkedButton() + ; b == ui->tb_plane_xy) { + ui->tb_plane_zx->click(); + } else if(b == ui->tb_plane_zx) { + ui->tb_plane_yz->click(); + } else if(b == ui->tb_plane_yz) { + ui->tb_plane_xy->click(); + } + } else if(event->key() == Qt::Key_PageDown) { + if(auto* b = ui->bg_plane->checkedButton() + ; b == ui->tb_plane_xy) { + ui->tb_plane_yz->click(); + } else if(b == ui->tb_plane_zx) { + ui->tb_plane_xy->click(); + } else if(b == ui->tb_plane_yz) { + ui->tb_plane_zx->click(); + } } else if(event->key() == Qt::Key_Escape) { // Will be forwarded to the 3 StructureScenes if(auto* scene = ui->processing_view->scene(); scene) From 07b965737c8a8a92e0dd9c7aa07cb15237c48f74 Mon Sep 17 00:00:00 2001 From: "tlepoix@localhost" Date: Wed, 16 Jul 2025 00:56:24 +0200 Subject: [PATCH 27/37] add dark/light theme support in GUI icons creation/selection --- src/ui/qt/icons.cpp | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/ui/qt/icons.cpp b/src/ui/qt/icons.cpp index 138ab37b..4b6200d1 100644 --- a/src/ui/qt/icons.cpp +++ b/src/ui/qt/icons.cpp @@ -4,9 +4,11 @@ /// @author Thomas Lepoix ///***************************************************************************** +#include #include #include #include +#include #include #include "structure_view/structure_conflict_colinear_edges.hpp" @@ -38,7 +40,11 @@ QPixmap make_pixmap(Drawer const& draw) { QPixmap icon(size, size); icon.fill(Qt::transparent); QPainter painter(&icon); - painter.setPen(Qt::black); + switch(QGuiApplication::styleHints()->colorScheme()) { + default: [[fallthrough]]; + case Qt::ColorScheme::Light: painter.setPen(Qt::black); break; + case Qt::ColorScheme::Dark: painter.setPen(Qt::white); break; + } draw(size, painter); return icon; }; @@ -72,8 +78,17 @@ QPixmap apply(QTransform const& transform, QPixmap const& pixmap) { //****************************************************************************** #define PIXMAP_MAKER_DEF(NAME, FUNC) \ QPixmap const& Icons::NAME() { \ - static QPixmap const pixmap = FUNC; \ - return pixmap; \ + switch(QGuiApplication::styleHints()->colorScheme()) { \ + default: [[fallthrough]]; \ + case Qt::ColorScheme::Light: { \ + static QPixmap const light = FUNC; \ + return light; \ + } \ + case Qt::ColorScheme::Dark: { \ + static QPixmap const dark = FUNC; \ + return dark; \ + } \ + } \ } //****************************************************************************** From d3f8858844b6154dd6a714dbebd0342c57fed2a4 Mon Sep 17 00:00:00 2001 From: "tlepoix@localhost" Date: Fri, 18 Jul 2025 11:16:28 +0200 Subject: [PATCH 28/37] refactor edition : add EditDelegate to present enums as QComboBox & add EditModel::make_row() helper --- src/CMakeLists.txt | 1 + src/ui/qt/edit/edit_delegate.cpp | 185 ++++++++++++++++++ src/ui/qt/edit/edit_delegate.hpp | 27 +++ src/ui/qt/edit/edit_dialog.cpp | 5 +- src/ui/qt/edit/edit_dialog.hpp | 2 + src/ui/qt/edit/edit_model.cpp | 20 -- src/ui/qt/edit/edit_model.hpp | 61 ++++-- ...l_conflict_too_close_meshline_policies.cpp | 9 +- src/ui/qt/edit/edit_model_edge.cpp | 9 +- src/ui/qt/edit/edit_model_global.cpp | 24 +-- src/ui/qt/edit/edit_model_interval.cpp | 21 +- src/ui/qt/edit/edit_model_meshline_policy.cpp | 37 ++-- 12 files changed, 296 insertions(+), 105 deletions(-) create mode 100644 src/ui/qt/edit/edit_delegate.cpp create mode 100644 src/ui/qt/edit/edit_delegate.hpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bb4aa3d1..9042e823 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -80,6 +80,7 @@ target_sources( openemsh_bin "${CMAKE_CURRENT_SOURCE_DIR}/ui/qt/utils/nodegraph/node.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/ui/qt/utils/nodegraph/container.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/ui/qt/utils/nodegraph/wire.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/ui/qt/edit/edit_delegate.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/ui/qt/edit/edit_dialog.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/ui/qt/edit/edit_model.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/ui/qt/edit/edit_model_global.cpp" diff --git a/src/ui/qt/edit/edit_delegate.cpp b/src/ui/qt/edit/edit_delegate.cpp new file mode 100644 index 00000000..7091dbda --- /dev/null +++ b/src/ui/qt/edit/edit_delegate.cpp @@ -0,0 +1,185 @@ +///***************************************************************************** +/// @date Feb 2021 +/// @copyright GPL-3.0-or-later +/// @author Thomas Lepoix +///***************************************************************************** + +#include +#include +#include +#include +#include + +#include + +#include "domain/mesh/meshline_policy.hpp" +#include "infra/utils/to_string.hpp" +#include "utils/concepts.hpp" +#include "utils/unconst.hpp" +#include "utils/unreachable.hpp" + +#include "edit_delegate.hpp" + +Q_DECLARE_METATYPE(domain::MeshlinePolicy::Normal) +Q_DECLARE_METATYPE(domain::MeshlinePolicy::Policy) + +namespace ui::qt { + +//****************************************************************************** +using Normal = domain::MeshlinePolicy::Normal; +using Policy = domain::MeshlinePolicy::Policy; + +//****************************************************************************** +static auto constexpr AllNormal = std::array { + Normal::NONE, + Normal::MIN, + Normal::MAX +}; + +//****************************************************************************** +static auto constexpr AllPolicy = std::array { + Policy::ONELINE, + Policy::HALFS, + Policy::THIRDS +}; + +//****************************************************************************** +template +QString convert(E e) { + return QString::fromStdString(to_string(e)); +} + +//****************************************************************************** +template +QStringList convert(std::array const& in) { + QStringList out; + for(auto& e : in) + out.push_back(convert(e)); + return out; +} + +//****************************************************************************** +template +constexpr std::size_t key(E e, std::array const& all) { + for(std::size_t i = 0; i < all.size(); ++i) + if(all[i] == e) + return i; + unreachable(); +} + +//****************************************************************************** +static constexpr auto key(Normal normal) { return key(normal, AllNormal); } +static constexpr auto key(Policy policy) { return key(policy, AllPolicy); } + +//****************************************************************************** +static_assert(AllNormal[key(Normal::NONE)] == Normal::NONE); +static_assert(AllNormal[key(Normal::MIN)] == Normal::MIN); +static_assert(AllNormal[key(Normal::MAX)] == Normal::MAX); +static_assert(AllPolicy[key(Policy::ONELINE)] == Policy::ONELINE); +static_assert(AllPolicy[key(Policy::HALFS)] == Policy::HALFS); +static_assert(AllPolicy[key(Policy::THIRDS)] == Policy::THIRDS); + +//****************************************************************************** +EditDelegate::EditDelegate(QObject* parent) +: QStyledItemDelegate(parent) +{} + +//****************************************************************************** +QWidget* EditDelegate::createEditor(QWidget* parent, QStyleOptionViewItem const& option, QModelIndex const& index) const { + auto const type = index.data(Qt::UserRole + 1).typeId(); + + auto const handle_enum = [&](std::array const& all) { + auto* widget = new QComboBox(parent); + widget->addItems(convert(all)); + return widget; + }; + + if(type == qMetaTypeId()) { + return handle_enum(AllNormal); + } else if(type == qMetaTypeId()) { + return handle_enum(AllPolicy); + } else { + return QStyledItemDelegate::createEditor(parent, option, index); + } +} + +//****************************************************************************** +void EditDelegate::setEditorData(QWidget* editor, QModelIndex const& index) const { + auto const type = index.data(Qt::UserRole + 1).typeId(); + + auto const handle_enum = [&]() { + auto* cb = static_cast(editor); + cb->setCurrentIndex(key(index.data(Qt::UserRole + 1).value())); + }; + + if(type == qMetaTypeId()) { + handle_enum.operator()(); + } else if(type == qMetaTypeId()) { + handle_enum.operator()(); + } + + QStyledItemDelegate::setEditorData(editor, index); +} + +//****************************************************************************** +void EditDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, QModelIndex const& index) const { + auto const type = index.data(Qt::UserRole + 1).typeId(); + + auto const handle_enum = [&](std::array const& all) { + auto* cb = static_cast(editor); + model->setData(index, QVariant::fromValue(all[cb->currentIndex()]), Qt::UserRole + 1); + model->setData(index, cb->currentText(), Qt::EditRole); + }; + + if(type == qMetaTypeId()) { + handle_enum(AllNormal); + } else if(type == qMetaTypeId()) { + handle_enum(AllPolicy); + } + + QStyledItemDelegate::setModelData(editor, model, index); +} + +//****************************************************************************** +void EditDelegate::paint(QPainter* painter, QStyleOptionViewItem const& option, QModelIndex const& index) const { + auto const type = index.data(Qt::UserRole + 1).typeId(); + + auto const handle_enum = [&]() { + auto* model = unconst(index.model()); + model->setData( + index, + convert(index.data(Qt::UserRole + 1).value()), + Qt::DisplayRole); + }; + + if(type == qMetaTypeId()) { + handle_enum.operator()(); + } else if(type == qMetaTypeId()) { + handle_enum.operator()(); + } + + QStyledItemDelegate::paint(painter, option, index); +} + +//****************************************************************************** +bool EditDelegate::eventFilter(QObject* object, QEvent* event) { + QWidget* editor = qobject_cast(object); + if(!editor) + return QStyledItemDelegate::eventFilter(object, event); + + if(event->type() == QEvent::KeyPress) { + switch(static_cast(event)->key()) { + case Qt::Key_Enter: [[fallthrough]]; + case Qt::Key_Return: + if(qobject_cast(editor)) { + emit commitData(editor); + emit closeEditor(editor); + return true; + } + } + } + + return QStyledItemDelegate::eventFilter(object, event); +} + +} // namespace ui::qt diff --git a/src/ui/qt/edit/edit_delegate.hpp b/src/ui/qt/edit/edit_delegate.hpp new file mode 100644 index 00000000..7afdb3db --- /dev/null +++ b/src/ui/qt/edit/edit_delegate.hpp @@ -0,0 +1,27 @@ +///***************************************************************************** +/// @date Feb 2021 +/// @copyright GPL-3.0-or-later +/// @author Thomas Lepoix +///***************************************************************************** + +#pragma once + +#include + +namespace ui::qt { + +//****************************************************************************** +class EditDelegate : public QStyledItemDelegate { +public: + explicit EditDelegate(QObject* parent = nullptr); + + QWidget* createEditor(QWidget* parent, QStyleOptionViewItem const& option, QModelIndex const& index) const override; + void setEditorData(QWidget* editor, QModelIndex const& index) const override; + void setModelData(QWidget* editor, QAbstractItemModel* model, QModelIndex const& index) const override; + void paint(QPainter* painter, QStyleOptionViewItem const& option, QModelIndex const& index) const override; + +protected: + bool eventFilter(QObject* editor, QEvent* event) override; +}; + +} // namespace ui::qt diff --git a/src/ui/qt/edit/edit_dialog.cpp b/src/ui/qt/edit/edit_dialog.cpp index 8fa1804b..07fc0a87 100644 --- a/src/ui/qt/edit/edit_dialog.cpp +++ b/src/ui/qt/edit/edit_dialog.cpp @@ -6,6 +6,7 @@ #include +#include "edit_delegate.hpp" #include "edit_model.hpp" #include "ui_edit_dialog.h" @@ -18,11 +19,13 @@ using namespace std; //****************************************************************************** EditDialog::EditDialog(EditModel* model, QString const& title, QWidget* parent) : QDialog(parent, Qt::Dialog) -, ui(make_unique()) { +, ui(make_unique()) +, delegate(new EditDelegate(this)) { ui->setupUi(this); setWindowIcon(QPixmap(":/openemsh.ico")); setWindowTitle(windowTitle() + " " + title); + ui->tv_properties->setItemDelegate(delegate); ui->tv_properties->setModel(model); ui->tv_properties->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); } diff --git a/src/ui/qt/edit/edit_dialog.hpp b/src/ui/qt/edit/edit_dialog.hpp index 8bbcbf81..314b6ef8 100644 --- a/src/ui/qt/edit/edit_dialog.hpp +++ b/src/ui/qt/edit/edit_dialog.hpp @@ -16,6 +16,7 @@ class EditDialog; namespace ui::qt { +class EditDelegate; class EditModel; //****************************************************************************** @@ -23,6 +24,7 @@ class EditDialog : public QDialog { Q_OBJECT private: std::unique_ptr ui; + EditDelegate* delegate; private slots: void on_dbb_ok_accepted(); diff --git a/src/ui/qt/edit/edit_model.cpp b/src/ui/qt/edit/edit_model.cpp index b95e23d4..3aa26948 100644 --- a/src/ui/qt/edit/edit_model.cpp +++ b/src/ui/qt/edit/edit_model.cpp @@ -19,26 +19,6 @@ namespace ui::qt { -using namespace std; - -//****************************************************************************** -QStandardItem* EditModel::make_property_item(QString const& str) { - auto* item = new QStandardItem(str); - item->setFlags(item->flags() & ~Qt::ItemIsEditable); - return item; -} - -//****************************************************************************** -QStandardItem* EditModel::make_bool_item(bool in) { - auto* item = new QStandardItem(); - item->setCheckable(true); - if(in) - item->setCheckState(Qt::Checked); - else - item->setCheckState(Qt::Unchecked); - return item; -} - //****************************************************************************** bool EditModel::try_to_double(QString const& in, double& out) { bool does_succeed = false; diff --git a/src/ui/qt/edit/edit_model.hpp b/src/ui/qt/edit/edit_model.hpp index 879e8bd5..6e9620d7 100644 --- a/src/ui/qt/edit/edit_model.hpp +++ b/src/ui/qt/edit/edit_model.hpp @@ -9,9 +9,9 @@ #include #include -#include #include "app/steps.hpp" +#include "utils/concepts.hpp" namespace ui::qt { @@ -29,28 +29,63 @@ class EditModel : public QStandardItemModel { virtual void commit(); protected: - static QStandardItem* make_property_item(QString const& str); - static QStandardItem* make_bool_item(bool in); + template + void make_row(int row, QString const& property, T const& old_value, T const& new_value, QString const& tool_tip); + void make_row(int row, QString const& property, auto const& value, QString const& tool_tip); static bool is_true(bool const val) { return val; } static bool try_to_double(QString const& in, double& out); static bool try_to_ulong(QString const& in, std::size_t& out); static bool try_to_bool(Qt::CheckState const in, bool& out); - template - static bool try_from_map(std::map const& map, QString const& in, O& out); + +private: + template + static void set_content(QStandardItem* item, E e); + static void set_content(QStandardItem* item, QString const& s); + static void set_content(QStandardItem* item, bool b); + static void set_uneditable(QStandardItem* item); signals: void edit_from(app::Step from, std::function const& edit); }; //****************************************************************************** -template -bool EditModel::try_from_map(std::map const& map, QString const& in, O& out) { - bool does_succeed = false; - if(map.contains(in)) { - out = map.at(in); - does_succeed = true; - } - return does_succeed; +template +void EditModel::set_content(QStandardItem* item, E e) { + item->setData(QVariant::fromValue(e)); +} + +//****************************************************************************** +inline void EditModel::set_content(QStandardItem* item, QString const& s) { + item->setText(s); +} + +//****************************************************************************** +inline void EditModel::set_content(QStandardItem* item, bool b) { + item->setCheckable(true); + item->setCheckState(b ? Qt::Checked : Qt::Unchecked); + set_uneditable(item); +} + +//****************************************************************************** +inline void EditModel::set_uneditable(QStandardItem* item) { + item->setFlags(item->flags() & ~Qt::ItemIsEditable); +} + +//****************************************************************************** +template +void EditModel::make_row(int row, QString const& property, T const& old_value, T const& new_value, QString const& tool_tip) { + setItem(row, 0, new QStandardItem(property)); + item(row, 0)->setToolTip(tool_tip); + set_uneditable(item(row, 0)); + + setItem(row, 1, new QStandardItem()); + item(row, 1)->setToolTip(tool_tip); + set_content(item(row, 1), old_value); +} + +//****************************************************************************** +void EditModel::make_row(int row, QString const& property, auto const& value, QString const& tool_tip) { + make_row(row, property, value, value, tool_tip); } } // namespace ui::qt diff --git a/src/ui/qt/edit/edit_model_conflict_too_close_meshline_policies.cpp b/src/ui/qt/edit/edit_model_conflict_too_close_meshline_policies.cpp index 885145fc..82561064 100644 --- a/src/ui/qt/edit/edit_model_conflict_too_close_meshline_policies.cpp +++ b/src/ui/qt/edit/edit_model_conflict_too_close_meshline_policies.cpp @@ -19,15 +19,10 @@ EditModelConflictTooCloseMeshlinePolicies::EditModelConflictTooCloseMeshlinePoli : EditModel(parent) , conflict(conflict) { + auto const& state = conflict->get_current_state(); setRowCount(1); - for(std::size_t i = 0; auto& str : { - "is_enabled" - }) { - setItem(i++, 0, make_property_item(str)); - } - auto const& state = conflict->get_current_state(); - setItem(0, 1, make_bool_item(state.is_enabled)); + make_row(0, "is_enabled", state.is_enabled, ""); } //****************************************************************************** diff --git a/src/ui/qt/edit/edit_model_edge.cpp b/src/ui/qt/edit/edit_model_edge.cpp index 03be4b29..4c55f534 100644 --- a/src/ui/qt/edit/edit_model_edge.cpp +++ b/src/ui/qt/edit/edit_model_edge.cpp @@ -19,15 +19,10 @@ EditModelEdge::EditModelEdge(domain::Edge* edge, QObject* parent) : EditModel(parent) , edge(edge) { + auto const& state = edge->get_current_state(); setRowCount(1); - for(std::size_t i = 0; auto& str : { - "to_mesh" - }) { - setItem(i++, 0, make_property_item(str)); - } - auto const& state = edge->get_current_state(); - setItem(0, 1, make_bool_item(state.to_mesh)); + make_row(0, "to_mesh", state.to_mesh, ""); } //****************************************************************************** diff --git a/src/ui/qt/edit/edit_model_global.cpp b/src/ui/qt/edit/edit_model_global.cpp index 4ed7e4a9..1dd0f4bb 100644 --- a/src/ui/qt/edit/edit_model_global.cpp +++ b/src/ui/qt/edit/edit_model_global.cpp @@ -19,25 +19,15 @@ EditModelGlobal::EditModelGlobal(domain::GlobalParams* global, QObject* parent) : EditModel(parent) , global(global) { + auto const& params = global->get_current_state(); setRowCount(6); - for(std::size_t i = 0; auto& str : { - "metal_res", - "substrate_res", - "proximity_limit", - "lambda", - "lmin", - "dmax" - }) { - setItem(i++, 0, make_property_item(str)); - } - auto const& params = global->get_current_state(); - setItem(0, 1, new QStandardItem(QString::number(params.metal_res))); - setItem(1, 1, new QStandardItem(QString::number(params.substrate_res))); - setItem(2, 1, new QStandardItem(QString::number(params.proximity_limit))); - setItem(3, 1, new QStandardItem(QString::number(params.lambda))); - setItem(4, 1, new QStandardItem(QString::number(params.lmin))); - setItem(5, 1, new QStandardItem(QString::number(params.dmax))); + make_row(0, "metal_res", QString::number(params.metal_res), ""); + make_row(1, "substrate_res", QString::number(params.substrate_res), ""); + make_row(2, "proximity_limit", QString::number(params.proximity_limit), ""); + make_row(3, "lambda", QString::number(params.lambda), ""); + make_row(4, "lmin", QString::number(params.lmin), ""); + make_row(5, "dmax", QString::number(params.dmax), ""); } //****************************************************************************** diff --git a/src/ui/qt/edit/edit_model_interval.cpp b/src/ui/qt/edit/edit_model_interval.cpp index f42fa137..ff3768d6 100644 --- a/src/ui/qt/edit/edit_model_interval.cpp +++ b/src/ui/qt/edit/edit_model_interval.cpp @@ -19,23 +19,14 @@ EditModelInterval::EditModelInterval(domain::Interval* interval, QObject* parent : EditModel(parent) , interval(interval) { + auto const& state = interval->get_current_state(); setRowCount(5); - for(std::size_t i = 0; auto& str : { - "dmax", - "before.lmin", - "before.lambda", - "after.lmin", - "after.lambda" - }) { - setItem(i++, 0, make_property_item(str)); - } - auto const& state = interval->get_current_state(); - setItem(0, 1, new QStandardItem(QString::number(state.dmax))); - setItem(1, 1, new QStandardItem(QString::number(state.before.lmin))); - setItem(2, 1, new QStandardItem(QString::number(state.before.lambda))); - setItem(3, 1, new QStandardItem(QString::number(state.after.lmin))); - setItem(4, 1, new QStandardItem(QString::number(state.after.lambda))); + make_row(0, "dmax", QString::number(state.dmax), ""); + make_row(1, "before.lmin", QString::number(state.before.lmin), ""); + make_row(2, "before.lambda", QString::number(state.before.lambda), QString("2"), ""); + make_row(3, "after.lmin", QString::number(state.after.lmin), ""); + make_row(4, "after.lambda", QString::number(state.after.lambda), QString("2"), ""); } //****************************************************************************** diff --git a/src/ui/qt/edit/edit_model_meshline_policy.cpp b/src/ui/qt/edit/edit_model_meshline_policy.cpp index 78efc8d9..92b2e0cb 100644 --- a/src/ui/qt/edit/edit_model_meshline_policy.cpp +++ b/src/ui/qt/edit/edit_model_meshline_policy.cpp @@ -14,6 +14,9 @@ #include "edit_model_meshline_policy.hpp" +Q_DECLARE_METATYPE(domain::MeshlinePolicy::Policy) +Q_DECLARE_METATYPE(domain::MeshlinePolicy::Normal) + namespace ui::qt { //****************************************************************************** @@ -21,23 +24,14 @@ EditModelMeshlinePolicy::EditModelMeshlinePolicy(domain::MeshlinePolicy* meshlin : EditModel(parent) , meshline_policy(meshline_policy) { + auto const& state = meshline_policy->get_current_state(); setRowCount(5); - for(std::size_t i = 0; auto& str : { - "policy", - "normal", - "is_enabled", - "res_factor", - "d" - }) { - setItem(i++, 0, make_property_item(str)); - } - auto const& state = meshline_policy->get_current_state(); - setItem(0, 1, new QStandardItem(QString::fromStdString(to_string(state.policy)))); - setItem(1, 1, new QStandardItem(QString::fromStdString(to_string(state.normal)))); - setItem(2, 1, make_bool_item(state.is_enabled)); - setItem(3, 1, new QStandardItem(QString::number(state.res_factor))); - setItem(4, 1, new QStandardItem(QString::number(state.d))); + make_row(0, "policy", state.policy, ""); + make_row(1, "normal", state.normal, ""); + make_row(2, "is_enabled", state.is_enabled, ""); + make_row(3, "res_factor", QString::number(state.res_factor), ""); + make_row(4, "d", QString::number(state.d), ""); } //****************************************************************************** @@ -65,17 +59,10 @@ void EditModelMeshlinePolicy::commit() { } }; + state.policy = item(0, 1)->data().value(); + state.normal = item(1, 1)->data().value(); + std::array does_succeed = { - try_from_map({ - { "ONELINE", domain::MeshlinePolicy::Policy::ONELINE }, - { "HALFS", domain::MeshlinePolicy::Policy::HALFS }, - { "THIRDS", domain::MeshlinePolicy::Policy::THIRDS } - }, item(0, 1)->text(), state.policy), - try_from_map({ - { "MIN", domain::MeshlinePolicy::Normal::MIN }, - { "MAX", domain::MeshlinePolicy::Normal::MAX }, - { "NONE", domain::MeshlinePolicy::Normal::NONE } - }, item(1, 1)->text(), state.normal), are_policy_and_normal_compatible(), try_to_bool(item(2, 1)->checkState(), state.is_enabled), try_to_double(item(3, 1)->text(), state.res_factor), From b8a086d72a01eae589553d5f360edeeea5510845 Mon Sep 17 00:00:00 2001 From: "tlepoix@localhost" Date: Fri, 18 Jul 2025 11:41:10 +0200 Subject: [PATCH 29/37] refactor edition : 'Value' column -> 'Old value' + 'New value' --- src/ui/qt/edit/edit_model.cpp | 12 ++++++++---- src/ui/qt/edit/edit_model.hpp | 19 ++++++++++++++++--- ...l_conflict_too_close_meshline_policies.cpp | 2 +- src/ui/qt/edit/edit_model_edge.cpp | 2 +- src/ui/qt/edit/edit_model_global.cpp | 12 ++++++------ src/ui/qt/edit/edit_model_interval.cpp | 10 +++++----- src/ui/qt/edit/edit_model_meshline_policy.cpp | 10 +++++----- 7 files changed, 42 insertions(+), 25 deletions(-) diff --git a/src/ui/qt/edit/edit_model.cpp b/src/ui/qt/edit/edit_model.cpp index 3aa26948..45f39814 100644 --- a/src/ui/qt/edit/edit_model.cpp +++ b/src/ui/qt/edit/edit_model.cpp @@ -75,10 +75,14 @@ EditModel* EditModel::make(nodegraph::Node* node, QObject* parent) { EditModel::EditModel(QObject* parent) : QStandardItemModel(parent) { - setColumnCount(2); - // TODO maybe 3 columns { "Property", "Old value", "New value" } would be better for UI - // especially for setting lambda to 2 by default - setHorizontalHeaderLabels({ "Property", "Value" }); + static_assert(1 <= V && V <= 2, "The Value column index V must be either 1 or 2"); + + setColumnCount(V + 1); + + if constexpr(V == 2) + setHorizontalHeaderLabels({ "Property", "Old value", "New value" }); + else + setHorizontalHeaderLabels({ "Property", "Value" }); } //****************************************************************************** diff --git a/src/ui/qt/edit/edit_model.hpp b/src/ui/qt/edit/edit_model.hpp index 6e9620d7..20b298c7 100644 --- a/src/ui/qt/edit/edit_model.hpp +++ b/src/ui/qt/edit/edit_model.hpp @@ -29,6 +29,8 @@ class EditModel : public QStandardItemModel { virtual void commit(); protected: + static auto constexpr V = 2; // Value column index. + template void make_row(int row, QString const& property, T const& old_value, T const& new_value, QString const& tool_tip); void make_row(int row, QString const& property, auto const& value, QString const& tool_tip); @@ -78,9 +80,20 @@ void EditModel::make_row(int row, QString const& property, T const& old_value, T item(row, 0)->setToolTip(tool_tip); set_uneditable(item(row, 0)); - setItem(row, 1, new QStandardItem()); - item(row, 1)->setToolTip(tool_tip); - set_content(item(row, 1), old_value); + if constexpr(V == 2) { + setItem(row, 1, new QStandardItem()); + item(row, 1)->setToolTip(tool_tip); + set_uneditable(item(row, 1)); + set_content(item(row, 1), old_value); + } + + setItem(row, V, new QStandardItem()); + item(row, V)->setToolTip(tool_tip); + if constexpr(V == 2) { + set_content(item(row, V), new_value); + } else { + set_content(item(row, V), old_value); + } } //****************************************************************************** diff --git a/src/ui/qt/edit/edit_model_conflict_too_close_meshline_policies.cpp b/src/ui/qt/edit/edit_model_conflict_too_close_meshline_policies.cpp index 82561064..82dcc5d1 100644 --- a/src/ui/qt/edit/edit_model_conflict_too_close_meshline_policies.cpp +++ b/src/ui/qt/edit/edit_model_conflict_too_close_meshline_policies.cpp @@ -30,7 +30,7 @@ void EditModelConflictTooCloseMeshlinePolicies::commit() { auto state = conflict->get_current_state(); std::array does_succeed = { - try_to_bool(item(0, 1)->checkState(), state.is_enabled) + try_to_bool(item(0, V)->checkState(), state.is_enabled) }; if(std::ranges::all_of(does_succeed, is_true)) { diff --git a/src/ui/qt/edit/edit_model_edge.cpp b/src/ui/qt/edit/edit_model_edge.cpp index 4c55f534..245e7d77 100644 --- a/src/ui/qt/edit/edit_model_edge.cpp +++ b/src/ui/qt/edit/edit_model_edge.cpp @@ -30,7 +30,7 @@ void EditModelEdge::commit() { auto state = edge->get_current_state(); std::array does_succeed = { - try_to_bool(item(0, 1)->checkState(), state.to_mesh) + try_to_bool(item(0, V)->checkState(), state.to_mesh) }; if(std::ranges::all_of(does_succeed, is_true)) { diff --git a/src/ui/qt/edit/edit_model_global.cpp b/src/ui/qt/edit/edit_model_global.cpp index 1dd0f4bb..584a60d9 100644 --- a/src/ui/qt/edit/edit_model_global.cpp +++ b/src/ui/qt/edit/edit_model_global.cpp @@ -35,12 +35,12 @@ void EditModelGlobal::commit() { domain::Params params; std::array does_succeed = { - try_to_double(item(0, 1)->text(), params.metal_res), - try_to_double(item(1, 1)->text(), params.substrate_res), - try_to_double(item(2, 1)->text(), params.proximity_limit), - try_to_double(item(3, 1)->text(), params.lambda), - try_to_ulong(item(4, 1)->text(), params.lmin), - try_to_double(item(5, 1)->text(), params.dmax) + try_to_double(item(0, V)->text(), params.metal_res), + try_to_double(item(1, V)->text(), params.substrate_res), + try_to_double(item(2, V)->text(), params.proximity_limit), + try_to_double(item(3, V)->text(), params.lambda), + try_to_ulong(item(4, V)->text(), params.lmin), + try_to_double(item(5, V)->text(), params.dmax) }; if(std::ranges::all_of(does_succeed, is_true)) { diff --git a/src/ui/qt/edit/edit_model_interval.cpp b/src/ui/qt/edit/edit_model_interval.cpp index ff3768d6..8ebc2649 100644 --- a/src/ui/qt/edit/edit_model_interval.cpp +++ b/src/ui/qt/edit/edit_model_interval.cpp @@ -34,11 +34,11 @@ void EditModelInterval::commit() { auto state = interval->get_current_state(); std::array does_succeed = { - try_to_double(item(0, 1)->text(), state.dmax), - try_to_ulong(item(1, 1)->text(), state.before.lmin), - try_to_double(item(2, 1)->text(), state.before.lambda), - try_to_ulong(item(3, 1)->text(), state.after.lmin), - try_to_double(item(4, 1)->text(), state.after.lambda) + try_to_double(item(0, V)->text(), state.dmax), + try_to_ulong(item(1, V)->text(), state.before.lmin), + try_to_double(item(2, V)->text(), state.before.lambda), + try_to_ulong(item(3, V)->text(), state.after.lmin), + try_to_double(item(4, V)->text(), state.after.lambda) }; if(std::ranges::all_of(does_succeed, is_true)) { diff --git a/src/ui/qt/edit/edit_model_meshline_policy.cpp b/src/ui/qt/edit/edit_model_meshline_policy.cpp index 92b2e0cb..035739d5 100644 --- a/src/ui/qt/edit/edit_model_meshline_policy.cpp +++ b/src/ui/qt/edit/edit_model_meshline_policy.cpp @@ -59,14 +59,14 @@ void EditModelMeshlinePolicy::commit() { } }; - state.policy = item(0, 1)->data().value(); - state.normal = item(1, 1)->data().value(); + state.policy = item(0, V)->data().value(); + state.normal = item(1, V)->data().value(); std::array does_succeed = { are_policy_and_normal_compatible(), - try_to_bool(item(2, 1)->checkState(), state.is_enabled), - try_to_double(item(3, 1)->text(), state.res_factor), - try_to_double(item(4, 1)->text(), state.d) + try_to_bool(item(2, V)->checkState(), state.is_enabled), + try_to_double(item(3, V)->text(), state.res_factor), + try_to_double(item(4, V)->text(), state.d) }; if(std::ranges::all_of(does_succeed, is_true)) { From ae8af601052b79991d635c57620bb21fdae64dfb Mon Sep 17 00:00:00 2001 From: "tlepoix@localhost" Date: Tue, 22 Jul 2025 20:31:41 +0200 Subject: [PATCH 30/37] bound EditDelegate Normal QComboBox by current Policy --- src/ui/qt/edit/edit_delegate.cpp | 47 +++++++++++++++++++++++++++++++- src/ui/qt/edit/edit_delegate.hpp | 5 ++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/src/ui/qt/edit/edit_delegate.cpp b/src/ui/qt/edit/edit_delegate.cpp index 7091dbda..48390044 100644 --- a/src/ui/qt/edit/edit_delegate.cpp +++ b/src/ui/qt/edit/edit_delegate.cpp @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include #include @@ -82,6 +84,8 @@ static_assert(AllPolicy[key(Policy::THIRDS)] == Policy::THIRDS); //****************************************************************************** EditDelegate::EditDelegate(QObject* parent) : QStyledItemDelegate(parent) +, normal_index(QModelIndex()) // Init at first paint(). +, policy_index(QModelIndex()) // Init at first paint(). {} //****************************************************************************** @@ -95,7 +99,27 @@ QWidget* EditDelegate::createEditor(QWidget* parent, QStyleOptionViewItem const& }; if(type == qMetaTypeId()) { - return handle_enum(AllNormal); + auto* cb = handle_enum(AllNormal); + if(policy_index.isValid()) { + auto policy = policy_index.data(Qt::UserRole + 1).value(); + switch(policy) { + case Policy::ONELINE: [[fallthrough]]; + case Policy::HALFS: { + auto* m = static_cast(cb->model()); + m->item(key(Normal::NONE))->setEnabled(true); + m->item(key(Normal::MIN))->setEnabled(false); + m->item(key(Normal::MAX))->setEnabled(false); + } break; + case Policy::THIRDS: { + auto* m = static_cast(cb->model()); + m->item(key(Normal::NONE))->setEnabled(false); + m->item(key(Normal::MIN))->setEnabled(true); + m->item(key(Normal::MAX))->setEnabled(true); + } break; + default: break; + } + } + return cb; } else if(type == qMetaTypeId()) { return handle_enum(AllPolicy); } else { @@ -135,6 +159,25 @@ void EditDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, QMod handle_enum(AllNormal); } else if(type == qMetaTypeId()) { handle_enum(AllPolicy); + if(normal_index.isValid()) { + auto policy = model->data(index, Qt::UserRole + 1).value(); + switch(policy) { + case Policy::ONELINE: [[fallthrough]]; + case Policy::HALFS: { + model->setData(normal_index, QVariant::fromValue(Normal::NONE), Qt::UserRole + 1); + auto* item = static_cast(static_cast(model)->itemFromIndex(normal_index)); + item->setEditable(false); + } break; + case Policy::THIRDS: { + if(model->data(normal_index, Qt::UserRole + 1).value() == Normal::NONE) { + model->setData(normal_index, QVariant::fromValue(Normal::MIN), Qt::UserRole + 1); + auto* item = static_cast(static_cast(model)->itemFromIndex(normal_index)); + item->setEditable(true); + } + } break; + default: break; + } + } } QStyledItemDelegate::setModelData(editor, model, index); @@ -154,8 +197,10 @@ void EditDelegate::paint(QPainter* painter, QStyleOptionViewItem const& option, if(type == qMetaTypeId()) { handle_enum.operator()(); + unconst(this)->normal_index = index; } else if(type == qMetaTypeId()) { handle_enum.operator()(); + unconst(this)->policy_index = index; } QStyledItemDelegate::paint(painter, option, index); diff --git a/src/ui/qt/edit/edit_delegate.hpp b/src/ui/qt/edit/edit_delegate.hpp index 7afdb3db..b0910abf 100644 --- a/src/ui/qt/edit/edit_delegate.hpp +++ b/src/ui/qt/edit/edit_delegate.hpp @@ -6,6 +6,7 @@ #pragma once +#include #include namespace ui::qt { @@ -22,6 +23,10 @@ class EditDelegate : public QStyledItemDelegate { protected: bool eventFilter(QObject* editor, QEvent* event) override; + +private: + QPersistentModelIndex normal_index; + QPersistentModelIndex policy_index; }; } // namespace ui::qt From 521ccd6f0c4b7b2fdc8af9c21c568d4af28c4120 Mon Sep 17 00:00:00 2001 From: "tlepoix@localhost" Date: Tue, 22 Jul 2025 20:36:11 +0200 Subject: [PATCH 31/37] propagate EditDelegate enums tooltips to edition QComboBox --- src/ui/qt/edit/edit_delegate.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ui/qt/edit/edit_delegate.cpp b/src/ui/qt/edit/edit_delegate.cpp index 48390044..c205a494 100644 --- a/src/ui/qt/edit/edit_delegate.cpp +++ b/src/ui/qt/edit/edit_delegate.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -95,6 +96,8 @@ QWidget* EditDelegate::createEditor(QWidget* parent, QStyleOptionViewItem const& auto const handle_enum = [&](std::array const& all) { auto* widget = new QComboBox(parent); widget->addItems(convert(all)); + widget->setToolTip(index.data(Qt::ToolTipRole).toString()); + static_cast(widget->view())->setToolTip(index.data(Qt::ToolTipRole).toString()); return widget; }; From 8846584831e053b19804f10e6e2954064742885f Mon Sep 17 00:00:00 2001 From: "tlepoix@localhost" Date: Tue, 22 Jul 2025 21:57:39 +0200 Subject: [PATCH 32/37] add edition tooltips & refactor 'lambda' -> 'smoothness' & hide some unused stuff --- src/ui/cli.cpp | 18 +++++----- ...l_conflict_too_close_meshline_policies.cpp | 2 +- src/ui/qt/edit/edit_model_edge.cpp | 2 +- src/ui/qt/edit/edit_model_global.cpp | 36 +++++++++++-------- src/ui/qt/edit/edit_model_interval.cpp | 19 +++++++--- src/ui/qt/edit/edit_model_meshline_policy.cpp | 26 +++++++++----- .../processing_view/processing_interval.cpp | 8 ++--- 7 files changed, 69 insertions(+), 42 deletions(-) diff --git a/src/ui/cli.cpp b/src/ui/cli.cpp index 900f72da..0355c363 100644 --- a/src/ui/cli.cpp +++ b/src/ui/cli.cpp @@ -142,20 +142,20 @@ app::OpenEMSH::Params cli(int const argc, char* argv[]) { // app.add_flag("--step-mesh", params.with_step_mesh, "Do intervals meshing step.")->group("Processing options"); // Mesher options - app.add_option_function("--metal_res", - make_overrider<&domain::Params::metal_res>(domain_overrides), - "Desired mesh resolution for metal regions." - )->group("Mesher options"); +// app.add_option_function("--metal_res", +// make_overrider<&domain::Params::metal_res>(domain_overrides), +// "Desired mesh resolution for metal regions." +// )->group("Mesher options"); // app.add_option_function("--air_res", // make_overrider<&domain::Params::air_res>(domain_overrides), // "Desired mesh resolution for air regions." // )->group("Mesher options"); - app.add_option_function("--substrate_res", - make_overrider<&domain::Params::substrate_res>(domain_overrides), - "Desired mesh resolution for substrate / ground plane regions." - )->group("Mesher options"); +// app.add_option_function("--substrate_res", +// make_overrider<&domain::Params::substrate_res>(domain_overrides), +// "Desired mesh resolution for substrate / ground plane regions." +// )->group("Mesher options"); app.add_option_function("--proximity_limit", make_overrider<&domain::Params::proximity_limit>(domain_overrides), @@ -172,7 +172,7 @@ app::OpenEMSH::Params cli(int const argc, char* argv[]) { "Minimum line number per interval half." )->group("Mesher options"); - app.add_option_function("--lambda", + app.add_option_function("--smoothness", make_overrider<&domain::Params::lambda>(domain_overrides), "Smoothness factor ]1;2]." )->group("Mesher options")->check(BoundExclusiveInclusive(1.0, 2.0)); diff --git a/src/ui/qt/edit/edit_model_conflict_too_close_meshline_policies.cpp b/src/ui/qt/edit/edit_model_conflict_too_close_meshline_policies.cpp index 82dcc5d1..e3149fb4 100644 --- a/src/ui/qt/edit/edit_model_conflict_too_close_meshline_policies.cpp +++ b/src/ui/qt/edit/edit_model_conflict_too_close_meshline_policies.cpp @@ -22,7 +22,7 @@ EditModelConflictTooCloseMeshlinePolicies::EditModelConflictTooCloseMeshlinePoli auto const& state = conflict->get_current_state(); setRowCount(1); - make_row(0, "is_enabled", state.is_enabled, ""); + make_row(0, "Enabled", state.is_enabled, "Take into account in the meshing process."); } //****************************************************************************** diff --git a/src/ui/qt/edit/edit_model_edge.cpp b/src/ui/qt/edit/edit_model_edge.cpp index 245e7d77..a854b5c5 100644 --- a/src/ui/qt/edit/edit_model_edge.cpp +++ b/src/ui/qt/edit/edit_model_edge.cpp @@ -22,7 +22,7 @@ EditModelEdge::EditModelEdge(domain::Edge* edge, QObject* parent) auto const& state = edge->get_current_state(); setRowCount(1); - make_row(0, "to_mesh", state.to_mesh, ""); + make_row(0, "To mesh", state.to_mesh, "Take into account in the meshing process."); } //****************************************************************************** diff --git a/src/ui/qt/edit/edit_model_global.cpp b/src/ui/qt/edit/edit_model_global.cpp index 584a60d9..19ab5198 100644 --- a/src/ui/qt/edit/edit_model_global.cpp +++ b/src/ui/qt/edit/edit_model_global.cpp @@ -14,20 +14,28 @@ namespace ui::qt { +// https://doc.qt.io/qt-6/qitemeditorfactory.html#registerEditor +// https://www.bogotobogo.com/Qt/Qt5_QTableView_QItemDelegate_ModelView_MVC.php +// https://stackoverflow.com/questions/60767527/creating-item-with-a-integer-qstandarditem-doesnt-provide-qspinbox +// https://stackoverflow.com/questions/3135505/qstandarditem-qcombobox //****************************************************************************** EditModelGlobal::EditModelGlobal(domain::GlobalParams* global, QObject* parent) : EditModel(parent) , global(global) { auto const& params = global->get_current_state(); - setRowCount(6); - - make_row(0, "metal_res", QString::number(params.metal_res), ""); - make_row(1, "substrate_res", QString::number(params.substrate_res), ""); - make_row(2, "proximity_limit", QString::number(params.proximity_limit), ""); - make_row(3, "lambda", QString::number(params.lambda), ""); - make_row(4, "lmin", QString::number(params.lmin), ""); - make_row(5, "dmax", QString::number(params.dmax), ""); + setRowCount(4); + +// make_row(0, "metal_res", QString::number(params.metal_res), ""); +// make_row(1, "substrate_res", QString::number(params.substrate_res), ""); + make_row(0, "Proximity limit", QString::number(params.proximity_limit), + "Distance below which two MeshlinePolicies will be merged."); + make_row(1, "Smoothness", QString::number(params.lambda), + "Smoothness factor ]1;2]. Meshing algorithm will decrease it, better to start high."); + make_row(2, "lmin", QString::number(params.lmin), + "Minimum line number per Interval half."); + make_row(3, "dmax", QString::number(params.dmax), + "Maximum distance between two adjacent lines."); } //****************************************************************************** @@ -35,12 +43,12 @@ void EditModelGlobal::commit() { domain::Params params; std::array does_succeed = { - try_to_double(item(0, V)->text(), params.metal_res), - try_to_double(item(1, V)->text(), params.substrate_res), - try_to_double(item(2, V)->text(), params.proximity_limit), - try_to_double(item(3, V)->text(), params.lambda), - try_to_ulong(item(4, V)->text(), params.lmin), - try_to_double(item(5, V)->text(), params.dmax) +// try_to_double(item(0, V)->text(), params.metal_res), +// try_to_double(item(1, V)->text(), params.substrate_res), + try_to_double(item(0, V)->text(), params.proximity_limit), + try_to_double(item(1, V)->text(), params.lambda), + try_to_ulong(item(2, V)->text(), params.lmin), + try_to_double(item(3, V)->text(), params.dmax) }; if(std::ranges::all_of(does_succeed, is_true)) { diff --git a/src/ui/qt/edit/edit_model_interval.cpp b/src/ui/qt/edit/edit_model_interval.cpp index 8ebc2649..8786b781 100644 --- a/src/ui/qt/edit/edit_model_interval.cpp +++ b/src/ui/qt/edit/edit_model_interval.cpp @@ -22,11 +22,20 @@ EditModelInterval::EditModelInterval(domain::Interval* interval, QObject* parent auto const& state = interval->get_current_state(); setRowCount(5); - make_row(0, "dmax", QString::number(state.dmax), ""); - make_row(1, "before.lmin", QString::number(state.before.lmin), ""); - make_row(2, "before.lambda", QString::number(state.before.lambda), QString("2"), ""); - make_row(3, "after.lmin", QString::number(state.after.lmin), ""); - make_row(4, "after.lambda", QString::number(state.after.lambda), QString("2"), ""); + make_row(0, "dmax", QString::number(state.dmax), + "Maximum distance between two adjacent lines."); + make_row(1, "Before.lmin", QString::number(state.before.lmin), + "Minimum line number in the minimal interval half. " + "Note a line will always be placed at the interval center."); + make_row(2, "Before.Smoothness", QString::number(state.before.lambda), QString("2"), + "Smoothness factor ]1;2] around the minimal side. " + "Meshing algorithm will decrease it, better to start high."); + make_row(3, "After.lmin", QString::number(state.after.lmin), + "Minimum line number in the maximal interval half. " + "Note a line will always be placed at the interval center."); + make_row(4, "After.Smoothness", QString::number(state.after.lambda), QString("2"), + "Smoothness factor ]1;2] around the maximal side. " + "Meshing algorithm will decrease it, better to start high."); } //****************************************************************************** diff --git a/src/ui/qt/edit/edit_model_meshline_policy.cpp b/src/ui/qt/edit/edit_model_meshline_policy.cpp index 035739d5..3f57d82f 100644 --- a/src/ui/qt/edit/edit_model_meshline_policy.cpp +++ b/src/ui/qt/edit/edit_model_meshline_policy.cpp @@ -14,6 +14,8 @@ #include "edit_model_meshline_policy.hpp" +#define BR "
" + Q_DECLARE_METATYPE(domain::MeshlinePolicy::Policy) Q_DECLARE_METATYPE(domain::MeshlinePolicy::Normal) @@ -25,13 +27,21 @@ EditModelMeshlinePolicy::EditModelMeshlinePolicy(domain::MeshlinePolicy* meshlin , meshline_policy(meshline_policy) { auto const& state = meshline_policy->get_current_state(); - setRowCount(5); + setRowCount(4); - make_row(0, "policy", state.policy, ""); - make_row(1, "normal", state.normal, ""); - make_row(2, "is_enabled", state.is_enabled, ""); - make_row(3, "res_factor", QString::number(state.res_factor), ""); - make_row(4, "d", QString::number(state.d), ""); + make_row(0, "Policy", state.policy, + "ONELINE: One meshline at the policy position." BR + "HALFS: Two lines centered around the policy position." BR + "THIRDS: Two lines placed around (2d/3 in Normal direction, d/3 the other side) the policy position."); + make_row(1, "Normal", state.normal, + "Direction associated with Policy."); + make_row(2, "Enabled", state.is_enabled, + "Take into account in the meshing process."); +// make_row(3, "res_factor", QString::number(state.res_factor), ""); + make_row(3, "d", QString::number(state.d), + "Desired distance between policy lines (HALFS|THIRDS) or " + "between policy line and adjacent lines (ONELINE)." BR + "Can be decreased by the meshing algorithm."); } //****************************************************************************** @@ -65,8 +75,8 @@ void EditModelMeshlinePolicy::commit() { std::array does_succeed = { are_policy_and_normal_compatible(), try_to_bool(item(2, V)->checkState(), state.is_enabled), - try_to_double(item(3, V)->text(), state.res_factor), - try_to_double(item(4, V)->text(), state.d) +// try_to_double(item(3, V)->text(), state.res_factor), + try_to_double(item(3, V)->text(), state.d) }; if(std::ranges::all_of(does_succeed, is_true)) { diff --git a/src/ui/qt/processing_view/processing_interval.cpp b/src/ui/qt/processing_view/processing_interval.cpp index 0fa4de85..8dbafa2e 100644 --- a/src/ui/qt/processing_view/processing_interval.cpp +++ b/src/ui/qt/processing_view/processing_interval.cpp @@ -69,10 +69,10 @@ ProcessingInterval::ProcessingInterval(domain::Interval const* interval, QGraphi }; QString dmax("dmax: "); - QString before_lmin ("before.lmin: "); - QString before_lambda ("before.lambda: "); - QString after_lmin("after.lmin: "); - QString after_lambda("after.lambda: "); + QString before_lmin ("Before.lmin: "); + QString before_lambda ("Before.Smoothness: "); + QString after_lmin("After.lmin: "); + QString after_lambda("After.Smoothness: "); if(interval) { auto const& state = interval->get_current_state(); dmax += QString::number(state.dmax); From 1df0482ac06cbb0d88650c5970b6b2517a9e4d71 Mon Sep 17 00:00:00 2001 From: "tlepoix@localhost" Date: Tue, 22 Jul 2025 22:32:40 +0200 Subject: [PATCH 33/37] refactor AboutDialog : use UI connections --- src/ui/qt/about_dialog.cpp | 5 ----- src/ui/qt/about_dialog.hpp | 5 ----- src/ui/qt/about_dialog.ui | 9 ++++++++- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/ui/qt/about_dialog.cpp b/src/ui/qt/about_dialog.cpp index 94ad2805..83a1bccb 100644 --- a/src/ui/qt/about_dialog.cpp +++ b/src/ui/qt/about_dialog.cpp @@ -46,9 +46,4 @@ AboutDialog::AboutDialog(QWidget* parent) //****************************************************************************** AboutDialog::~AboutDialog() = default; -//****************************************************************************** -void AboutDialog::on_dbb_ok_clicked(QAbstractButton* /*button*/) { - accept(); -} - } // namespace ui::qt diff --git a/src/ui/qt/about_dialog.hpp b/src/ui/qt/about_dialog.hpp index e9586a2d..0064eac2 100644 --- a/src/ui/qt/about_dialog.hpp +++ b/src/ui/qt/about_dialog.hpp @@ -7,7 +7,6 @@ #pragma once #include -#include #include @@ -21,13 +20,9 @@ namespace ui::qt { //****************************************************************************** class AboutDialog : public QDialog { - Q_OBJECT private: std::unique_ptr ui; -private slots: - void on_dbb_ok_clicked(QAbstractButton* button); - public: explicit AboutDialog(QWidget* parent = nullptr); ~AboutDialog() override; diff --git a/src/ui/qt/about_dialog.ui b/src/ui/qt/about_dialog.ui index 1e2a6ee4..2807f998 100644 --- a/src/ui/qt/about_dialog.ui +++ b/src/ui/qt/about_dialog.ui @@ -139,5 +139,12 @@ the GPL license version 3 or later. - + + + dbb_ok + accepted() + Dialog + accept() + + From af06b5cb2787a11b0fd9f1d1f02354a58a9f4165 Mon Sep 17 00:00:00 2001 From: "tlepoix@localhost" Date: Tue, 22 Jul 2025 23:26:42 +0200 Subject: [PATCH 34/37] refactor : fix some badsmells --- src/ui/cli.cpp | 2 +- src/ui/qt/main_window.cpp | 4 ++-- src/ui/qt/processing_view/processing_view.cpp | 4 ++-- src/ui/qt/structure_view/structure_view.cpp | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/ui/cli.cpp b/src/ui/cli.cpp index 0355c363..7b3b893c 100644 --- a/src/ui/cli.cpp +++ b/src/ui/cli.cpp @@ -80,7 +80,7 @@ struct FutureConditional : CLI::Validator { //****************************************************************************** template F> struct JustDo : CLI::Validator { - JustDo(F const& func) { + explicit JustDo(F const& func) { name_ = "JustDo"; func_ = [func](string const&) { func(); diff --git a/src/ui/qt/main_window.cpp b/src/ui/qt/main_window.cpp index 1338b1c4..97273c68 100644 --- a/src/ui/qt/main_window.cpp +++ b/src/ui/qt/main_window.cpp @@ -495,7 +495,7 @@ void MainWindow::keyPressEvent(QKeyEvent* event) { } else if(event->key() == Qt::Key_Period) { ui->tb_show_no_mesh->click(); } else if(event->key() == Qt::Key_PageUp) { - if(auto* b = ui->bg_plane->checkedButton() + if(auto const* b = ui->bg_plane->checkedButton() ; b == ui->tb_plane_xy) { ui->tb_plane_zx->click(); } else if(b == ui->tb_plane_zx) { @@ -504,7 +504,7 @@ void MainWindow::keyPressEvent(QKeyEvent* event) { ui->tb_plane_xy->click(); } } else if(event->key() == Qt::Key_PageDown) { - if(auto* b = ui->bg_plane->checkedButton() + if(auto const* b = ui->bg_plane->checkedButton() ; b == ui->tb_plane_xy) { ui->tb_plane_yz->click(); } else if(b == ui->tb_plane_zx) { diff --git a/src/ui/qt/processing_view/processing_view.cpp b/src/ui/qt/processing_view/processing_view.cpp index 1c7b8ef6..26b2a810 100644 --- a/src/ui/qt/processing_view/processing_view.cpp +++ b/src/ui/qt/processing_view/processing_view.cpp @@ -96,7 +96,7 @@ void ProcessingView::set_display_plane(domain::Plane plane) { //****************************************************************************** void ProcessingView::set_wire_style(nodegraph::Wire::Style style) { wire_style = style; - for(auto& [t, state] : states) + for(auto const& [t, state] : states) state.scene->set_wire_style(style); } @@ -118,7 +118,7 @@ void ProcessingView::make_current_state() { populate(scene); scene->init(); - if(auto* current_scene = static_cast(this->scene()); current_scene) + if(auto const* current_scene = static_cast(this->scene()); current_scene) scene->set_display_mode(current_scene->get_display_mode()); scene->set_display_view_axes(axes_displayed_on_structure_view); scene->set_display_plane(plane_displayed_on_structure_view); diff --git a/src/ui/qt/structure_view/structure_view.cpp b/src/ui/qt/structure_view/structure_view.cpp index c95d5c4a..24685a88 100644 --- a/src/ui/qt/structure_view/structure_view.cpp +++ b/src/ui/qt/structure_view/structure_view.cpp @@ -197,7 +197,7 @@ void StructureView::make_current_state() { std::make_unique(style_selector, this).release() }}; populate(scenes); - if(auto* current_scene = static_cast(scene()); current_scene) + if(auto const* current_scene = static_cast(scene()); current_scene) for(auto* scene : scenes) scene->set_mesh_visibility(current_scene->get_mesh_visibility()); From fbddb5a83678a945ea98b3ee2c5b3f34033b21cc Mon Sep 17 00:00:00 2001 From: "tlepoix@localhost" Date: Tue, 22 Jul 2025 23:56:23 +0200 Subject: [PATCH 35/37] CI : drop manual Catch2 installation --- .github/workflows/test_sonarcloud.yml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/.github/workflows/test_sonarcloud.yml b/.github/workflows/test_sonarcloud.yml index abdd847b..11d0d4b7 100644 --- a/.github/workflows/test_sonarcloud.yml +++ b/.github/workflows/test_sonarcloud.yml @@ -22,6 +22,7 @@ jobs: sudo apt update sudo apt install -y \ qt6-base-dev \ + catch2 \ gcovr \ texlive-xetex \ cairosvg \ @@ -49,13 +50,6 @@ jobs: # curl https://report.ci/report.py \ # > /tmp/report.py - - name: "Dependencies: Catch2" - run: | - git clone https://github.com/catchorg/Catch2.git - cd Catch2 - cmake -Bbuild -H. -DBUILD_TESTING=OFF - sudo cmake --build build/ --target install - - name: Checkout uses: actions/checkout@v3 with: From e91ed819e63b23562aa81bad3d425bef789abd3e Mon Sep 17 00:00:00 2001 From: "tlepoix@localhost" Date: Wed, 23 Jul 2025 16:04:47 +0200 Subject: [PATCH 36/37] CI : sonar : fix outdated Qt dependency : nixify --- .github/workflows/test_sonarcloud.yml | 53 ++++++++++++++++----------- 1 file changed, 32 insertions(+), 21 deletions(-) diff --git a/.github/workflows/test_sonarcloud.yml b/.github/workflows/test_sonarcloud.yml index 11d0d4b7..279aa4d7 100644 --- a/.github/workflows/test_sonarcloud.yml +++ b/.github/workflows/test_sonarcloud.yml @@ -17,27 +17,29 @@ jobs: runs-on: ubuntu-latest steps: - - name: "Dependencies: APT" - run: | - sudo apt update - sudo apt install -y \ - qt6-base-dev \ - catch2 \ - gcovr \ - texlive-xetex \ - cairosvg \ - imagemagick +# - name: "Dependencies: APT" +# run: | +# sudo apt update +# sudo apt install -y \ +# qt6-base-dev \ +# catch2 \ +# gcovr \ +# texlive-xetex \ +# cairosvg \ +# imagemagick # libxml2-utils + - name: 'Dependencies: Nix' + uses: cachix/install-nix-action@v31 + with: + nix_path: nixpkgs=channel:nixos-unstable + + - name: Setup Nix cache + uses: DeterminateSystems/magic-nix-cache-action@v13 + - name: "Dependencies: Sonar" run: | mkdir -p /tmp/sonar - wget \ - https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-${{ env.SONAR_SCANNER_VERSION }}-linux-x64.zip \ - -O /tmp/sonar/sonar-scanner.zip - unzip \ - -o /tmp/sonar/sonar-scanner.zip \ - -d /tmp/sonar/ wget \ https://sonarcloud.io/static/cpp/build-wrapper-linux-x86.zip \ -O /tmp/sonar/build-wrapper-linux-x86.zip @@ -51,12 +53,16 @@ jobs: # > /tmp/report.py - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 + - name: Build Nix environment + run: nix develop --no-update-lock-file --command true + - name: Configure run: | + nix develop --no-update-lock-file --command \ cmake \ -S. \ -Bbuild \ @@ -67,16 +73,20 @@ jobs: - name: Build run: | + nix develop --no-update-lock-file --command \ /tmp/sonar/build-wrapper-linux-x86/build-wrapper-linux-x86-64 \ --out-dir build_wrapper_output_directory \ cmake --build build --target all unittest - name: Coverage - run: cmake --build build --target coverage + run: | + nix develop --no-update-lock-file --command \ + cmake --build build --target coverage - name: Scan run: | - /tmp/sonar/sonar-scanner-${{ env.SONAR_SCANNER_VERSION }}-linux-x64/bin/sonar-scanner \ + nix develop --no-update-lock-file --command \ + sonar-scanner \ -Dproject.settings=.sonar-project.properties \ -Dsonar.cfamily.build-wrapper-output=build_wrapper_output_directory \ -Dsonar.host.url="https://sonarcloud.io" \ @@ -117,12 +127,13 @@ jobs: - name: Produce test report run: | + nix develop --no-update-lock-file --command bash -c '\ build/test/unit/openemsh_unittest \ -r junit::out=build/test/unit/openemsh_unittest_result.xml \ - || true + || true' - name: Export test report - uses: dorny/test-reporter@v1 + uses: dorny/test-reporter@v2 with: name: Unit tests path: build/test/unit/openemsh_unittest_result.xml From 02e1204419ff2530de27866a08bb33e8247d2daa Mon Sep 17 00:00:00 2001 From: "tlepoix@localhost" Date: Wed, 23 Jul 2025 18:52:59 +0200 Subject: [PATCH 37/37] refactor : fix some badsmells --- src/app/openemsh.cpp | 42 ++++++------------- src/domain/mesh/interval.cpp | 2 +- src/domain/mesh/interval.hpp | 2 +- src/ui/qt/edit/edit_delegate.cpp | 34 +++++++++------ src/ui/qt/edit/edit_model_meshline_policy.cpp | 34 ++++++++------- src/ui/qt/main_window.cpp | 32 +++++++------- test/unit/domain/mesh/test_interval.cpp | 4 +- 7 files changed, 75 insertions(+), 75 deletions(-) diff --git a/src/app/openemsh.cpp b/src/app/openemsh.cpp index af8e1a69..a4f84257 100644 --- a/src/app/openemsh.cpp +++ b/src/app/openemsh.cpp @@ -47,37 +47,21 @@ optional next(Step step) { //****************************************************************************** set that_and_after(Step step) { set out; + + using enum Step; switch(step) { - case Step::DETECT_CONFLICT_EIP: - out.emplace(Step::DETECT_CONFLICT_EIP); - [[fallthrough]]; - case Step::DETECT_CONFLICT_CE: - out.emplace(Step::DETECT_CONFLICT_CE); - [[fallthrough]]; - case Step::DETECT_NON_CONFLICTING_EDGES: - out.emplace(Step::DETECT_NON_CONFLICTING_EDGES); - [[fallthrough]]; - case Step::ADD_FIXED_MLP: - out.emplace(Step::ADD_FIXED_MLP); - [[fallthrough]]; - case Step::SOLVE_ALL_EIP: - out.emplace(Step::SOLVE_ALL_EIP); - [[fallthrough]]; - case Step::SOLVE_ALL_CE: - out.emplace(Step::SOLVE_ALL_CE); - [[fallthrough]]; - case Step::DETECT_AND_SOLVE_TCMLP: - out.emplace(Step::DETECT_AND_SOLVE_TCMLP); - [[fallthrough]]; - case Step::DETECT_INTERVALS: - out.emplace(Step::DETECT_INTERVALS); - [[fallthrough]]; - case Step::MESH: - out.emplace(Step::MESH); - break; - default: - unreachable(); + case DETECT_CONFLICT_EIP: out.emplace(DETECT_CONFLICT_EIP); [[fallthrough]]; + case DETECT_CONFLICT_CE: out.emplace(DETECT_CONFLICT_CE); [[fallthrough]]; + case DETECT_NON_CONFLICTING_EDGES: out.emplace(DETECT_NON_CONFLICTING_EDGES); [[fallthrough]]; + case ADD_FIXED_MLP: out.emplace(ADD_FIXED_MLP); [[fallthrough]]; + case SOLVE_ALL_EIP: out.emplace(SOLVE_ALL_EIP); [[fallthrough]]; + case SOLVE_ALL_CE: out.emplace(SOLVE_ALL_CE); [[fallthrough]]; + case DETECT_AND_SOLVE_TCMLP: out.emplace(DETECT_AND_SOLVE_TCMLP); [[fallthrough]]; + case DETECT_INTERVALS: out.emplace(DETECT_INTERVALS); [[fallthrough]]; + case MESH: out.emplace(MESH); break; + default: unreachable(); } + return out; } diff --git a/src/domain/mesh/interval.cpp b/src/domain/mesh/interval.cpp index 8f34b9a4..57135615 100644 --- a/src/domain/mesh/interval.cpp +++ b/src/domain/mesh/interval.cpp @@ -162,7 +162,7 @@ vector find_ls(double d, double lambda, double dmax, Coord s) { /// - The last space between adjacent lines should not be less than dmax/lambda. ///***************************************************************************** -bool is_ls_valid_for_dmax_lmin_lambda(vector ls, double d, double lambda, double dmax, size_t lmin) { +bool is_ls_valid_for_dmax_lmin_lambda(vector const& ls, double d, double lambda, double dmax, size_t lmin) { if(d > dmax || ls.size() < lmin || ls.empty()) diff --git a/src/domain/mesh/interval.hpp b/src/domain/mesh/interval.hpp index 61172b8c..94b22f20 100644 --- a/src/domain/mesh/interval.hpp +++ b/src/domain/mesh/interval.hpp @@ -100,6 +100,6 @@ double find_dmax(Interval::Side const& a, Interval::Side const& b, double dmax); std::vector find_ls(double d, double lambda, double dmax, Coord s); //****************************************************************************** -bool is_ls_valid_for_dmax_lmin_lambda(std::vector ls, double d, double lambda, double dmax, size_t lmin); +bool is_ls_valid_for_dmax_lmin_lambda(std::vector const& ls, double d, double lambda, double dmax, size_t lmin); } // namespace domain diff --git a/src/ui/qt/edit/edit_delegate.cpp b/src/ui/qt/edit/edit_delegate.cpp index c205a494..8d0d9a01 100644 --- a/src/ui/qt/edit/edit_delegate.cpp +++ b/src/ui/qt/edit/edit_delegate.cpp @@ -101,20 +101,21 @@ QWidget* EditDelegate::createEditor(QWidget* parent, QStyleOptionViewItem const& return widget; }; - if(type == qMetaTypeId()) { - auto* cb = handle_enum(AllNormal); + auto const bound_normal_choice_by_current_policy = [&](auto const* cb) { if(policy_index.isValid()) { auto policy = policy_index.data(Qt::UserRole + 1).value(); switch(policy) { case Policy::ONELINE: [[fallthrough]]; case Policy::HALFS: { - auto* m = static_cast(cb->model()); + // Actually made uneditable since there is no choice, + // in enforce_coherent_normal_regarding_current_policy(). + auto const* m = static_cast(cb->model()); m->item(key(Normal::NONE))->setEnabled(true); m->item(key(Normal::MIN))->setEnabled(false); m->item(key(Normal::MAX))->setEnabled(false); } break; case Policy::THIRDS: { - auto* m = static_cast(cb->model()); + auto const* m = static_cast(cb->model()); m->item(key(Normal::NONE))->setEnabled(false); m->item(key(Normal::MIN))->setEnabled(true); m->item(key(Normal::MAX))->setEnabled(true); @@ -122,6 +123,11 @@ QWidget* EditDelegate::createEditor(QWidget* parent, QStyleOptionViewItem const& default: break; } } + }; + + if(type == qMetaTypeId()) { + auto* cb = handle_enum(AllNormal); + bound_normal_choice_by_current_policy(cb); return cb; } else if(type == qMetaTypeId()) { return handle_enum(AllPolicy); @@ -136,7 +142,7 @@ void EditDelegate::setEditorData(QWidget* editor, QModelIndex const& index) cons auto const handle_enum = [&]() { auto* cb = static_cast(editor); - cb->setCurrentIndex(key(index.data(Qt::UserRole + 1).value())); + cb->setCurrentIndex((int) key(index.data(Qt::UserRole + 1).value())); }; if(type == qMetaTypeId()) { @@ -153,34 +159,38 @@ void EditDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, QMod auto const type = index.data(Qt::UserRole + 1).typeId(); auto const handle_enum = [&](std::array const& all) { - auto* cb = static_cast(editor); + auto const* cb = static_cast(editor); model->setData(index, QVariant::fromValue(all[cb->currentIndex()]), Qt::UserRole + 1); model->setData(index, cb->currentText(), Qt::EditRole); }; - if(type == qMetaTypeId()) { - handle_enum(AllNormal); - } else if(type == qMetaTypeId()) { - handle_enum(AllPolicy); + auto const enforce_coherent_normal_regarding_current_policy = [&]() { if(normal_index.isValid()) { auto policy = model->data(index, Qt::UserRole + 1).value(); switch(policy) { case Policy::ONELINE: [[fallthrough]]; case Policy::HALFS: { model->setData(normal_index, QVariant::fromValue(Normal::NONE), Qt::UserRole + 1); - auto* item = static_cast(static_cast(model)->itemFromIndex(normal_index)); + auto* item = static_cast(model)->itemFromIndex(normal_index); item->setEditable(false); } break; case Policy::THIRDS: { if(model->data(normal_index, Qt::UserRole + 1).value() == Normal::NONE) { model->setData(normal_index, QVariant::fromValue(Normal::MIN), Qt::UserRole + 1); - auto* item = static_cast(static_cast(model)->itemFromIndex(normal_index)); + auto* item = static_cast(model)->itemFromIndex(normal_index); item->setEditable(true); } } break; default: break; } } + }; + + if(type == qMetaTypeId()) { + handle_enum(AllNormal); + } else if(type == qMetaTypeId()) { + handle_enum(AllPolicy); + enforce_coherent_normal_regarding_current_policy(); } QStyledItemDelegate::setModelData(editor, model, index); diff --git a/src/ui/qt/edit/edit_model_meshline_policy.cpp b/src/ui/qt/edit/edit_model_meshline_policy.cpp index 3f57d82f..2541a2c9 100644 --- a/src/ui/qt/edit/edit_model_meshline_policy.cpp +++ b/src/ui/qt/edit/edit_model_meshline_policy.cpp @@ -14,13 +14,15 @@ #include "edit_model_meshline_policy.hpp" -#define BR "
" - Q_DECLARE_METATYPE(domain::MeshlinePolicy::Policy) Q_DECLARE_METATYPE(domain::MeshlinePolicy::Normal) namespace ui::qt { +//****************************************************************************** +using Normal = domain::MeshlinePolicy::Normal; +using Policy = domain::MeshlinePolicy::Policy; + //****************************************************************************** EditModelMeshlinePolicy::EditModelMeshlinePolicy(domain::MeshlinePolicy* meshline_policy, QObject* parent) : EditModel(parent) @@ -30,8 +32,8 @@ EditModelMeshlinePolicy::EditModelMeshlinePolicy(domain::MeshlinePolicy* meshlin setRowCount(4); make_row(0, "Policy", state.policy, - "ONELINE: One meshline at the policy position." BR - "HALFS: Two lines centered around the policy position." BR + "ONELINE: One meshline at the policy position.
" + "HALFS: Two lines centered around the policy position.
" "THIRDS: Two lines placed around (2d/3 in Normal direction, d/3 the other side) the policy position."); make_row(1, "Normal", state.normal, "Direction associated with Policy."); @@ -40,7 +42,7 @@ EditModelMeshlinePolicy::EditModelMeshlinePolicy(domain::MeshlinePolicy* meshlin // make_row(3, "res_factor", QString::number(state.res_factor), ""); make_row(3, "d", QString::number(state.d), "Desired distance between policy lines (HALFS|THIRDS) or " - "between policy line and adjacent lines (ONELINE)." BR + "between policy line and adjacent lines (ONELINE).
" "Can be decreased by the meshing algorithm."); } @@ -50,27 +52,27 @@ void EditModelMeshlinePolicy::commit() { auto const are_policy_and_normal_compatible = [&state]() { switch(state.policy) { - case domain::MeshlinePolicy::Policy::ONELINE: [[fallthrough]]; - case domain::MeshlinePolicy::Policy::HALFS: + case Policy::ONELINE: [[fallthrough]]; + case Policy::HALFS: switch(state.normal) { - case domain::MeshlinePolicy::Normal::MIN: [[fallthrough]]; - case domain::MeshlinePolicy::Normal::MAX: return false; - case domain::MeshlinePolicy::Normal::NONE: return true; + case Normal::MIN: [[fallthrough]]; + case Normal::MAX: return false; + case Normal::NONE: return true; default: unreachable(); } - case domain::MeshlinePolicy::Policy::THIRDS: + case Policy::THIRDS: switch(state.normal) { - case domain::MeshlinePolicy::Normal::MIN: [[fallthrough]]; - case domain::MeshlinePolicy::Normal::MAX: return true; - case domain::MeshlinePolicy::Normal::NONE: return false; + case Normal::MIN: [[fallthrough]]; + case Normal::MAX: return true; + case Normal::NONE: return false; default: unreachable(); } default:unreachable(); } }; - state.policy = item(0, V)->data().value(); - state.normal = item(1, V)->data().value(); + state.policy = item(0, V)->data().value(); + state.normal = item(1, V)->data().value(); std::array does_succeed = { are_policy_and_normal_compatible(), diff --git a/src/ui/qt/main_window.cpp b/src/ui/qt/main_window.cpp index 97273c68..f57157e6 100644 --- a/src/ui/qt/main_window.cpp +++ b/src/ui/qt/main_window.cpp @@ -25,6 +25,10 @@ namespace ui::qt { +//****************************************************************************** +using DisplayMode = ProcessingScene::DisplayMode; +using MeshVisibility = StructureScene::MeshVisibility; + //****************************************************************************** MainWindow::MainWindow(app::OpenEMSH& oemsh, QWidget* parent) : QMainWindow(parent) @@ -168,47 +172,47 @@ void MainWindow::on_a_vertical_layout_triggered() { //****************************************************************************** void MainWindow::on_tb_show_all_mesh_clicked() { - ui->structure_view->set_mesh_visibility(StructureScene::MeshVisibility::FULL); + ui->structure_view->set_mesh_visibility(MeshVisibility::FULL); ui->processing_view->set_display_view_axes({ true, true }); ui->processing_view->fit(); } //****************************************************************************** void MainWindow::on_tb_show_horizontal_mesh_clicked() { - ui->structure_view->set_mesh_visibility(StructureScene::MeshVisibility::HORIZONTAL); + ui->structure_view->set_mesh_visibility(MeshVisibility::HORIZONTAL); ui->processing_view->set_display_view_axes({ true, false }); ui->processing_view->fit(); } //****************************************************************************** void MainWindow::on_tb_show_vertical_mesh_clicked() { - ui->structure_view->set_mesh_visibility(StructureScene::MeshVisibility::VERTICAL); + ui->structure_view->set_mesh_visibility(MeshVisibility::VERTICAL); ui->processing_view->set_display_view_axes({ false, true }); ui->processing_view->fit(); } //****************************************************************************** void MainWindow::on_tb_show_no_mesh_clicked() { - ui->structure_view->set_mesh_visibility(StructureScene::MeshVisibility::NONE); + ui->structure_view->set_mesh_visibility(MeshVisibility::NONE); ui->processing_view->set_display_view_axes({ false, false }); ui->processing_view->fit(); } //****************************************************************************** void MainWindow::on_tb_show_selected_clicked() { - ui->processing_view->set_display_mode(ProcessingScene::DisplayMode::SELECTED_CHAIN); + ui->processing_view->set_display_mode(DisplayMode::SELECTED_CHAIN); ui->processing_view->fit(); } //****************************************************************************** void MainWindow::on_tb_show_displayed_clicked() { - ui->processing_view->set_display_mode(ProcessingScene::DisplayMode::STRUCTURE_VIEW); + ui->processing_view->set_display_mode(DisplayMode::STRUCTURE_VIEW); ui->processing_view->fit(); } //****************************************************************************** void MainWindow::on_tb_show_everything_clicked() { - ui->processing_view->set_display_mode(ProcessingScene::DisplayMode::EVERYTHING); + ui->processing_view->set_display_mode(DisplayMode::EVERYTHING); ui->processing_view->fit(); } @@ -437,17 +441,17 @@ void MainWindow::update_navigation_buttons_visibility() { //****************************************************************************** void MainWindow::update_show_buttons_pressing() { switch(ui->structure_view->get_mesh_visibility()) { - case StructureScene::MeshVisibility::NONE: ui->tb_show_no_mesh->setChecked(true); break; - case StructureScene::MeshVisibility::VERTICAL: ui->tb_show_vertical_mesh->setChecked(true); break; - case StructureScene::MeshVisibility::HORIZONTAL: ui->tb_show_horizontal_mesh->setChecked(true); break; - case StructureScene::MeshVisibility::FULL: ui->tb_show_all_mesh->setChecked(true); break; + case MeshVisibility::NONE: ui->tb_show_no_mesh->setChecked(true); break; + case MeshVisibility::VERTICAL: ui->tb_show_vertical_mesh->setChecked(true); break; + case MeshVisibility::HORIZONTAL: ui->tb_show_horizontal_mesh->setChecked(true); break; + case MeshVisibility::FULL: ui->tb_show_all_mesh->setChecked(true); break; default: unreachable(); } switch(ui->processing_view->get_display_mode()) { - case ProcessingScene::DisplayMode::EVERYTHING: ui->tb_show_everything->setChecked(true); break; - case ProcessingScene::DisplayMode::STRUCTURE_VIEW: ui->tb_show_displayed->setChecked(true); break; - case ProcessingScene::DisplayMode::SELECTED_CHAIN: ui->tb_show_selected->setChecked(true); break; + case DisplayMode::EVERYTHING: ui->tb_show_everything->setChecked(true); break; + case DisplayMode::STRUCTURE_VIEW: ui->tb_show_displayed->setChecked(true); break; + case DisplayMode::SELECTED_CHAIN: ui->tb_show_selected->setChecked(true); break; default: unreachable(); } } diff --git a/test/unit/domain/mesh/test_interval.cpp b/test/unit/domain/mesh/test_interval.cpp index 103ece44..2c37de98 100644 --- a/test/unit/domain/mesh/test_interval.cpp +++ b/test/unit/domain/mesh/test_interval.cpp @@ -18,7 +18,7 @@ /// @test Coord Interval::s(Interval::Side const& side) const @todo /// @test Coord Interval::s(Interval::Side const& side, double d) const @todo /// @test std::vector find_ls(double d, double lambda, double dmax, Coord s) -/// @test bool is_ls_valid_for_dmax_lmin_lambda(std::vector ls, double d, double lambda, double dmax, size_t lmin) +/// @test bool is_ls_valid_for_dmax_lmin_lambda(std::vector const& ls, double d, double lambda, double dmax, size_t lmin) /// @test void Interval::update_ls() @todo /// @test void Interval::update_ls(Interval::Side& side) @todo /// @test double find_dmax(Interval::Side const& side, double dmax) @@ -246,7 +246,7 @@ SCENARIO("std::vector find_ls(double d, double lambda, double dmax, Coord } //****************************************************************************** -SCENARIO("bool is_ls_valid_for_dmax_lmin_lambda(std::vector ls, double d, double lambda, double dmax, size_t lmin)", "[interval]") { +SCENARIO("bool is_ls_valid_for_dmax_lmin_lambda(std::vector const& ls, double d, double lambda, double dmax, size_t lmin)", "[interval]") { GIVEN("An empty ls vector") { THEN("Should not be valid, disregarding lmin") { REQUIRE_FALSE(is_ls_valid_for_dmax_lmin_lambda({}, 1.0, 2.0, 3.0, 2));