From fa5852b8db761058474e97e5fe844eaa43feff71 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Fri, 13 Feb 2026 17:29:04 -0700 Subject: [PATCH] refactor: Introduce ApplicationController to centralize logic Co-authored-by: aider (gemini/gemini-2.5-pro) --- src/ApplicationController.cpp | 122 ++++++++++++++++++++++++++++++ src/ApplicationController.h | 46 ++++++++++++ src/MainWindow.cpp | 136 +++++++++++++--------------------- src/MainWindow.h | 8 +- src/ViewportWidget.cpp | 4 +- src/ViewportWidget.h | 6 +- 6 files changed, 230 insertions(+), 92 deletions(-) create mode 100644 src/ApplicationController.cpp create mode 100644 src/ApplicationController.h diff --git a/src/ApplicationController.cpp b/src/ApplicationController.cpp new file mode 100644 index 0000000..77b85dc --- /dev/null +++ b/src/ApplicationController.cpp @@ -0,0 +1,122 @@ +#include "ApplicationController.h" +#include "Document.h" +#include "SketchFeature.h" +#include "MainWindow.h" + +#include +#include +#include +#include +#include + +ApplicationController* ApplicationController::instance() +{ + static ApplicationController instance; + return &instance; +} + +ApplicationController::ApplicationController(QObject *parent) + : QObject(parent) +{ + m_document = new Document(this); +} + +ApplicationController::~ApplicationController() +{ +} + +void ApplicationController::setMainWindow(MainWindow* mainWindow) +{ + m_mainWindow = mainWindow; +} + +Document* ApplicationController::document() const +{ + return m_document; +} + +void ApplicationController::newDocument() +{ + m_document->clear(); + setCurrentFile(QString()); +} + +bool ApplicationController::openDocument() +{ + const QString fileName = QFileDialog::getOpenFileName(m_mainWindow); + if (!fileName.isEmpty()) { + if (!m_document->load(fileName)) { + QMessageBox::warning(m_mainWindow, tr("OpenCAD"), + tr("Cannot read file %1").arg(QDir::toNativeSeparators(fileName))); + return false; + } + setCurrentFile(fileName); + return true; + } + return false; +} + +bool ApplicationController::saveDocument() +{ + if (m_currentFile.isEmpty()) { + return saveDocumentAs(); + } else { + if (!m_document->save(m_currentFile)) { + QMessageBox::warning(m_mainWindow, tr("OpenCAD"), + tr("Cannot write file %1").arg(QDir::toNativeSeparators(m_currentFile))); + return false; + } + return true; + } +} + +bool ApplicationController::saveDocumentAs() +{ + QFileDialog dialog(m_mainWindow); + dialog.setWindowModality(Qt::WindowModal); + dialog.setAcceptMode(QFileDialog::AcceptSave); + if (dialog.exec() != QDialog::Accepted) + return false; + + const QString fileName = dialog.selectedFiles().first(); + setCurrentFile(fileName); + return saveDocument(); +} + +void ApplicationController::beginSketchCreation() +{ + QStringList items; + items << "XY-Plane" << "XZ-Plane" << "YZ-Plane"; + + bool ok; + QString item = QInputDialog::getItem(m_mainWindow, "Select Sketch Plane", + "Plane:", items, 0, false, &ok); + if (ok && !item.isEmpty()) { + auto feature = new SketchFeature("Sketch"); + ViewportWidget::SketchPlane plane; + if (item == "XY-Plane") { + plane = ViewportWidget::SketchPlane::XY; + feature->setPlane(SketchFeature::SketchPlane::XY); + } else if (item == "XZ-Plane") { + plane = ViewportWidget::SketchPlane::XZ; + feature->setPlane(SketchFeature::SketchPlane::XZ); + } else { // "YZ-Plane" + plane = ViewportWidget::SketchPlane::YZ; + feature->setPlane(SketchFeature::SketchPlane::YZ); + } + m_document->addFeature(feature); + emit sketchModeStarted(plane); + } +} + +void ApplicationController::endSketch() +{ + emit sketchModeEnded(); +} + +void ApplicationController::setCurrentFile(const QString& fileName) +{ + m_currentFile = fileName; + m_document->setFileName(fileName); + emit currentFileChanged(m_currentFile); +} diff --git a/src/ApplicationController.h b/src/ApplicationController.h new file mode 100644 index 0000000..680c5a7 --- /dev/null +++ b/src/ApplicationController.h @@ -0,0 +1,46 @@ +#ifndef APPLICATIONCONTROLLER_H +#define APPLICATIONCONTROLLER_H + +#include +#include "ViewportWidget.h" // For SketchPlane enum + +class Document; +class MainWindow; + +class ApplicationController : public QObject +{ + Q_OBJECT +public: + static ApplicationController* instance(); + + void setMainWindow(MainWindow* mainWindow); + Document* document() const; + +public slots: + void newDocument(); + bool openDocument(); + bool saveDocument(); + bool saveDocumentAs(); + + void beginSketchCreation(); + void endSketch(); + +signals: + void sketchModeStarted(ViewportWidget::SketchPlane plane); + void sketchModeEnded(); + void currentFileChanged(const QString& path); + +private: + explicit ApplicationController(QObject *parent = nullptr); + ~ApplicationController(); + ApplicationController(const ApplicationController&) = delete; + ApplicationController& operator=(const ApplicationController&) = delete; + + void setCurrentFile(const QString& fileName); + + Document* m_document; + QString m_currentFile; + MainWindow* m_mainWindow = nullptr; +}; + +#endif // APPLICATIONCONTROLLER_H diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index d39fa5e..2ce9c84 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -3,6 +3,7 @@ #include "Document.h" #include "SketchFeature.h" #include "Feature.h" +#include "ApplicationController.h" #include #include @@ -116,107 +117,74 @@ MainWindow::MainWindow(QWidget *parent) m_viewport = new ViewportWidget; setCentralWidget(m_viewport); - m_document = new Document(this); - m_viewport->setDocument(m_document); - connect(m_document, &Document::featureAdded, m_viewport, QOverload<>::of(&QWidget::update)); - connect(m_document, &Document::cleared, m_viewport, QOverload<>::of(&QWidget::update)); + ApplicationController::instance()->setMainWindow(this); + Document* document = ApplicationController::instance()->document(); - setCurrentFile(QString()); + m_viewport->setDocument(document); + connect(document, &Document::featureAdded, m_viewport, QOverload<>::of(&QWidget::update)); + connect(document, &Document::cleared, m_viewport, QOverload<>::of(&QWidget::update)); + + connect(ApplicationController::instance(), &ApplicationController::sketchModeStarted, this, &MainWindow::enterSketchMode); + connect(ApplicationController::instance(), &ApplicationController::sketchModeEnded, this, &MainWindow::exitSketchMode); + connect(ApplicationController::instance(), &ApplicationController::sketchModeStarted, m_viewport, &ViewportWidget::onSketchModeStarted); + connect(ApplicationController::instance(), &ApplicationController::sketchModeEnded, m_viewport, &ViewportWidget::onSketchModeEnded); + connect(ApplicationController::instance(), &ApplicationController::currentFileChanged, this, &MainWindow::updateWindowTitle); + + updateWindowTitle(QString()); } void MainWindow::createSketch() { - QStringList items; - items << "XY-Plane" << "XZ-Plane" << "YZ-Plane"; - - bool ok; - QString item = QInputDialog::getItem(this, "Select Sketch Plane", - "Plane:", items, 0, false, &ok); - if (ok && !item.isEmpty()) { - auto feature = new SketchFeature("Sketch"); - if (item == "XY-Plane") { - m_viewport->startSketch(ViewportWidget::SketchPlane::XY); - feature->setPlane(SketchFeature::SketchPlane::XY); - } else if (item == "XZ-Plane") { - m_viewport->startSketch(ViewportWidget::SketchPlane::XZ); - feature->setPlane(SketchFeature::SketchPlane::XZ); - } else if (item == "YZ-Plane") { - m_viewport->startSketch(ViewportWidget::SketchPlane::YZ); - feature->setPlane(SketchFeature::SketchPlane::YZ); - } - m_document->addFeature(feature); - - m_tabWidget->removeTab(m_tabWidget->indexOf(m_toolsTab)); - m_tabWidget->removeTab(m_tabWidget->indexOf(m_surfaceTab)); - m_tabWidget->removeTab(m_tabWidget->indexOf(m_solidTab)); - m_tabWidget->addTab(m_sketchTab, "SKETCH"); - } + ApplicationController::instance()->beginSketchCreation(); } void MainWindow::saveSketch() { - m_viewport->saveSketch(); + ApplicationController::instance()->endSketch(); +} + +void MainWindow::newFile() +{ + ApplicationController::instance()->newDocument(); +} + +void MainWindow::open() +{ + ApplicationController::instance()->openDocument(); +} + +bool MainWindow::save() +{ + return ApplicationController::instance()->saveDocument(); +} + +bool MainWindow::saveAs() +{ + return ApplicationController::instance()->saveDocumentAs(); +} + +void MainWindow::enterSketchMode() +{ + m_tabWidget->removeTab(m_tabWidget->indexOf(m_toolsTab)); + m_tabWidget->removeTab(m_tabWidget->indexOf(m_surfaceTab)); + m_tabWidget->removeTab(m_tabWidget->indexOf(m_solidTab)); + m_tabWidget->addTab(m_sketchTab, "SKETCH"); +} + +void MainWindow::exitSketchMode() +{ m_tabWidget->removeTab(m_tabWidget->indexOf(m_sketchTab)); m_tabWidget->addTab(m_solidTab, "SOLID"); m_tabWidget->addTab(m_surfaceTab, "SURFACE"); m_tabWidget->addTab(m_toolsTab, "TOOLS"); } -void MainWindow::newFile() +void MainWindow::updateWindowTitle(const QString& filePath) { - m_document->clear(); - setCurrentFile(QString()); -} + setWindowFilePath(filePath); -void MainWindow::open() -{ - const QString fileName = QFileDialog::getOpenFileName(this); - if (!fileName.isEmpty()) { - if (!m_document->load(fileName)) { - QMessageBox::warning(this, tr("OpenCAD"), - tr("Cannot read file %1").arg(QDir::toNativeSeparators(fileName))); - return; - } - setCurrentFile(fileName); - } -} - -bool MainWindow::save() -{ - if (m_currentFile.isEmpty()) { - return saveAs(); - } else { - if (!m_document->save(m_currentFile)) { - QMessageBox::warning(this, tr("OpenCAD"), - tr("Cannot write file %1").arg(QDir::toNativeSeparators(m_currentFile))); - return false; - } - return true; - } -} - -bool MainWindow::saveAs() -{ - QFileDialog dialog(this); - dialog.setWindowModality(Qt::WindowModal); - dialog.setAcceptMode(QFileDialog::AcceptSave); - if (dialog.exec() != QDialog::Accepted) - return false; - - const QString fileName = dialog.selectedFiles().first(); - setCurrentFile(fileName); - return save(); -} - - -void MainWindow::setCurrentFile(const QString &fileName) -{ - m_currentFile = fileName; - m_document->setFileName(fileName); - setWindowFilePath(m_currentFile); - - QString shownName = m_currentFile; - if (m_currentFile.isEmpty()) + QString shownName = filePath; + if (filePath.isEmpty()) shownName = "Untitled"; setWindowTitle(tr("%1[*] - %2").arg(QFileInfo(shownName).fileName(), tr("OpenCAD"))); } diff --git a/src/MainWindow.h b/src/MainWindow.h index 197c616..bd9a8da 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h @@ -24,12 +24,12 @@ private slots: void createSketch(); void saveSketch(); -private: - void setCurrentFile(const QString &fileName); + void enterSketchMode(); + void exitSketchMode(); + void updateWindowTitle(const QString& filePath); +private: ViewportWidget *m_viewport; - Document *m_document; - QString m_currentFile; QTabWidget *m_tabWidget; QWidget *m_solidTab; diff --git a/src/ViewportWidget.cpp b/src/ViewportWidget.cpp index 2234d3a..1ccb859 100644 --- a/src/ViewportWidget.cpp +++ b/src/ViewportWidget.cpp @@ -168,7 +168,7 @@ void ViewportWidget::wheelEvent(QWheelEvent *event) update(); } -void ViewportWidget::startSketch(SketchPlane plane) +void ViewportWidget::onSketchModeStarted(SketchPlane plane) { m_currentPlane = plane; @@ -237,7 +237,7 @@ void ViewportWidget::startSketch(SketchPlane plane) animGroup->start(QAbstractAnimation::DeleteWhenStopped); } -void ViewportWidget::saveSketch() +void ViewportWidget::onSketchModeEnded() { auto* animGroup = new QParallelAnimationGroup(this); diff --git a/src/ViewportWidget.h b/src/ViewportWidget.h index 7b609fd..04c8999 100644 --- a/src/ViewportWidget.h +++ b/src/ViewportWidget.h @@ -34,10 +34,12 @@ public: explicit ViewportWidget(QWidget *parent = nullptr); ~ViewportWidget(); - void startSketch(SketchPlane plane); - void saveSketch(); void setDocument(Document* document); +public slots: + void onSketchModeStarted(SketchPlane plane); + void onSketchModeEnded(); + float xRotation() const; void setXRotation(float angle);