From 00c2c16f31fc674dfa579042baf8c18a76cfb237 Mon Sep 17 00:00:00 2001 From: 95x8x9 Date: Thu, 30 Apr 2026 00:37:15 +0900 Subject: [PATCH 1/4] [Application] Add back button in top bar --- src/application/ScenarioAuthoringWidget.cpp | 8 ++- src/application/ScenarioAuthoringWidget.h | 3 + src/application/ScenarioResultWidget.cpp | 7 ++- src/application/ScenarioResultWidget.h | 2 + src/application/ScenarioRunWidget.cpp | 7 ++- src/application/ScenarioRunWidget.h | 2 + src/application/WorkspaceShell.cpp | 66 +++++++++++++++++---- src/application/WorkspaceShell.h | 5 ++ 8 files changed, 85 insertions(+), 15 deletions(-) diff --git a/src/application/ScenarioAuthoringWidget.cpp b/src/application/ScenarioAuthoringWidget.cpp index 2cd99d9..b4e11f6 100644 --- a/src/application/ScenarioAuthoringWidget.cpp +++ b/src/application/ScenarioAuthoringWidget.cpp @@ -246,12 +246,14 @@ ScenarioAuthoringWidget::ScenarioAuthoringWidget( const safecrowd::domain::FacilityLayout2D& layout, std::function saveProjectHandler, std::function openProjectHandler, + std::function backToLayoutReviewHandler, QWidget* parent) : QWidget(parent), projectName_(projectName), layout_(layout), saveProjectHandler_(std::move(saveProjectHandler)), - openProjectHandler_(std::move(openProjectHandler)) { + openProjectHandler_(std::move(openProjectHandler)), + backToLayoutReviewHandler_(std::move(backToLayoutReviewHandler)) { initializeUi(true); } @@ -261,12 +263,14 @@ ScenarioAuthoringWidget::ScenarioAuthoringWidget( InitialState initialState, std::function saveProjectHandler, std::function openProjectHandler, + std::function backToLayoutReviewHandler, QWidget* parent) : QWidget(parent), projectName_(projectName), layout_(layout), saveProjectHandler_(std::move(saveProjectHandler)), openProjectHandler_(std::move(openProjectHandler)), + backToLayoutReviewHandler_(std::move(backToLayoutReviewHandler)), scenarios_(std::move(initialState.scenarios)), currentScenarioIndex_(initialState.currentScenarioIndex), navigationView_(initialState.navigationView), @@ -283,6 +287,7 @@ void ScenarioAuthoringWidget::initializeUi(bool promptForScenario) { shell_->setTools({"Project"}); shell_->setSaveProjectHandler(saveProjectHandler_); shell_->setOpenProjectHandler(openProjectHandler_); + shell_->setBackHandler(backToLayoutReviewHandler_); shell_->setTopBarTrailingWidget(createTopBarTogglePanel()); refreshRightPanel(); rootLayout->addWidget(shell_); @@ -541,6 +546,7 @@ void ScenarioAuthoringWidget::runFirstStagedBaselineScenario() { scenario->draft, saveProjectHandler_, openProjectHandler_, + backToLayoutReviewHandler_, this); rootLayout->replaceWidget(shell_, runWidget); shell_->hide(); diff --git a/src/application/ScenarioAuthoringWidget.h b/src/application/ScenarioAuthoringWidget.h index 4f8387b..f23a473 100644 --- a/src/application/ScenarioAuthoringWidget.h +++ b/src/application/ScenarioAuthoringWidget.h @@ -25,6 +25,7 @@ class ScenarioAuthoringWidget : public QWidget { const safecrowd::domain::FacilityLayout2D& layout, std::function saveProjectHandler, std::function openProjectHandler, + std::function backToLayoutReviewHandler, QWidget* parent = nullptr); enum class NavigationView { @@ -62,6 +63,7 @@ class ScenarioAuthoringWidget : public QWidget { InitialState initialState, std::function saveProjectHandler, std::function openProjectHandler, + std::function backToLayoutReviewHandler, QWidget* parent = nullptr); private: @@ -92,6 +94,7 @@ class ScenarioAuthoringWidget : public QWidget { safecrowd::domain::FacilityLayout2D layout_{}; std::function saveProjectHandler_{}; std::function openProjectHandler_{}; + std::function backToLayoutReviewHandler_{}; std::vector scenarios_{}; int currentScenarioIndex_{-1}; NavigationView navigationView_{NavigationView::Layout}; diff --git a/src/application/ScenarioResultWidget.cpp b/src/application/ScenarioResultWidget.cpp index 29e71b4..f85e692 100644 --- a/src/application/ScenarioResultWidget.cpp +++ b/src/application/ScenarioResultWidget.cpp @@ -336,6 +336,7 @@ ScenarioResultWidget::ScenarioResultWidget( safecrowd::domain::ScenarioRiskSnapshot risk, std::function saveProjectHandler, std::function openProjectHandler, + std::function backToLayoutReviewHandler, QWidget* parent) : QWidget(parent), projectName_(std::move(projectName)), @@ -344,7 +345,8 @@ ScenarioResultWidget::ScenarioResultWidget( frame_(std::move(frame)), risk_(std::move(risk)), saveProjectHandler_(std::move(saveProjectHandler)), - openProjectHandler_(std::move(openProjectHandler)) { + openProjectHandler_(std::move(openProjectHandler)), + backToLayoutReviewHandler_(std::move(backToLayoutReviewHandler)) { auto* rootLayout = new QVBoxLayout(this); rootLayout->setContentsMargins(0, 0, 0, 0); rootLayout->setSpacing(0); @@ -358,6 +360,7 @@ ScenarioResultWidget::ScenarioResultWidget( shell_->setTools({"Project"}); shell_->setSaveProjectHandler(saveProjectHandler_); shell_->setOpenProjectHandler(openProjectHandler_); + shell_->setBackHandler(backToLayoutReviewHandler_); auto* canvas = new SimulationCanvasWidget(layout_, shell_); canvas->setFrame(frame_); @@ -406,6 +409,7 @@ void ScenarioResultWidget::rerunScenario() { scenario_, saveProjectHandler_, openProjectHandler_, + backToLayoutReviewHandler_, this); rootLayout->replaceWidget(shell_, runWidget); @@ -434,6 +438,7 @@ void ScenarioResultWidget::navigateToAuthoring(bool showRunPanel) { std::move(initial), saveProjectHandler_, openProjectHandler_, + backToLayoutReviewHandler_, this); rootLayout->replaceWidget(shell_, authoringWidget); diff --git a/src/application/ScenarioResultWidget.h b/src/application/ScenarioResultWidget.h index f23fcf2..983911a 100644 --- a/src/application/ScenarioResultWidget.h +++ b/src/application/ScenarioResultWidget.h @@ -24,6 +24,7 @@ class ScenarioResultWidget : public QWidget { safecrowd::domain::ScenarioRiskSnapshot risk, std::function saveProjectHandler, std::function openProjectHandler, + std::function backToLayoutReviewHandler, QWidget* parent = nullptr); private: @@ -37,6 +38,7 @@ class ScenarioResultWidget : public QWidget { safecrowd::domain::ScenarioRiskSnapshot risk_{}; std::function saveProjectHandler_{}; std::function openProjectHandler_{}; + std::function backToLayoutReviewHandler_{}; WorkspaceShell* shell_{nullptr}; }; diff --git a/src/application/ScenarioRunWidget.cpp b/src/application/ScenarioRunWidget.cpp index e495786..75d2b67 100644 --- a/src/application/ScenarioRunWidget.cpp +++ b/src/application/ScenarioRunWidget.cpp @@ -168,6 +168,7 @@ ScenarioRunWidget::ScenarioRunWidget( const safecrowd::domain::ScenarioDraft& scenario, std::function saveProjectHandler, std::function openProjectHandler, + std::function backToLayoutReviewHandler, QWidget* parent) : QWidget(parent), projectName_(projectName), @@ -175,7 +176,8 @@ ScenarioRunWidget::ScenarioRunWidget( scenario_(scenario), runner_(layout_, scenario_), saveProjectHandler_(std::move(saveProjectHandler)), - openProjectHandler_(std::move(openProjectHandler)) { + openProjectHandler_(std::move(openProjectHandler)), + backToLayoutReviewHandler_(std::move(backToLayoutReviewHandler)) { auto* rootLayout = new QVBoxLayout(this); rootLayout->setContentsMargins(0, 0, 0, 0); rootLayout->setSpacing(0); @@ -189,6 +191,7 @@ ScenarioRunWidget::ScenarioRunWidget( shell_->setTools({"Project"}); shell_->setSaveProjectHandler(saveProjectHandler_); shell_->setOpenProjectHandler(openProjectHandler_); + shell_->setBackHandler(backToLayoutReviewHandler_); canvas_ = new SimulationCanvasWidget(layout_, shell_); canvas_->setFrame(runner_.frame()); shell_->setCanvas(canvas_); @@ -334,6 +337,7 @@ void ScenarioRunWidget::returnToAuthoring() { std::move(initial), saveProjectHandler_, openProjectHandler_, + backToLayoutReviewHandler_, this); rootLayout->replaceWidget(shell_, authoringWidget); @@ -450,6 +454,7 @@ void ScenarioRunWidget::showResults() { openProjectHandler_(); } }, + backToLayoutReviewHandler_, this); rootLayout->replaceWidget(shell_, resultWidget); shell_->hide(); diff --git a/src/application/ScenarioRunWidget.h b/src/application/ScenarioRunWidget.h index 2e86f7f..067398e 100644 --- a/src/application/ScenarioRunWidget.h +++ b/src/application/ScenarioRunWidget.h @@ -27,6 +27,7 @@ class ScenarioRunWidget : public QWidget { const safecrowd::domain::ScenarioDraft& scenario, std::function saveProjectHandler, std::function openProjectHandler, + std::function backToLayoutReviewHandler, QWidget* parent = nullptr); private: @@ -44,6 +45,7 @@ class ScenarioRunWidget : public QWidget { safecrowd::domain::ScenarioSimulationRunner runner_{}; std::function saveProjectHandler_{}; std::function openProjectHandler_{}; + std::function backToLayoutReviewHandler_{}; WorkspaceShell* shell_{nullptr}; SimulationCanvasWidget* canvas_{nullptr}; QTimer* timer_{nullptr}; diff --git a/src/application/WorkspaceShell.cpp b/src/application/WorkspaceShell.cpp index 9047745..3f32e1f 100644 --- a/src/application/WorkspaceShell.cpp +++ b/src/application/WorkspaceShell.cpp @@ -57,6 +57,26 @@ QPushButton* createFlatTopBarButton(QWidget* parent, const QString& text) { return button; } +QPushButton* createFlatTopBarIconButton(QWidget* parent, const QString& text) { + auto* button = new QPushButton(text, parent); + button->setFont(ui::font(ui::FontRole::Body)); + button->setFixedSize(32, 32); + button->setCursor(Qt::PointingHandCursor); + button->setStyleSheet( + "QPushButton {" + " background: transparent;" + " border: 0;" + " border-radius: 10px;" + " color: #16202b;" + " font-size: 18px;" + " font-weight: 700;" + "}" + "QPushButton:hover {" + " background: #eef3f8;" + "}"); + return button; +} + } // namespace WorkspaceShell::WorkspaceShell(QWidget* parent) @@ -173,9 +193,42 @@ void WorkspaceShell::setFixedWidthVisible(QWidget* widget, bool visible, int wid } void WorkspaceShell::setTools(const QStringList& tools) { + tools_ = tools; + rebuildTopBar(); +} + +void WorkspaceShell::setBackHandler(std::function handler) { + backHandler_ = std::move(handler); + rebuildTopBar(); +} + +void WorkspaceShell::clearTopBar() { + while (auto* item = topBarLayout_->takeAt(0)) { + delete item->widget(); + delete item; + } + + openProjectAction_ = nullptr; + saveProjectAction_ = nullptr; + backButton_ = nullptr; +} + +void WorkspaceShell::rebuildTopBar() { clearTopBar(); - for (const auto& tool : tools) { + if (backHandler_) { + backButton_ = createFlatTopBarIconButton(this, "<"); + backButton_->setToolTip("Back"); + backButton_->setAccessibleName("Back"); + connect(backButton_, &QPushButton::clicked, this, [this]() { + if (backHandler_) { + backHandler_(); + } + }); + topBarLayout_->addWidget(backButton_); + } + + for (const auto& tool : tools_) { auto* button = createTopBarButton(tool); if (tool == "Project") { auto* menu = new QMenu(button); @@ -195,17 +248,6 @@ void WorkspaceShell::setTools(const QStringList& tools) { } topBarLayout_->addWidget(button); } - -} - -void WorkspaceShell::clearTopBar() { - while (auto* item = topBarLayout_->takeAt(0)) { - delete item->widget(); - delete item; - } - - openProjectAction_ = nullptr; - saveProjectAction_ = nullptr; } QPushButton* WorkspaceShell::createTopBarButton(const QString& text) { diff --git a/src/application/WorkspaceShell.h b/src/application/WorkspaceShell.h index 8dc0a0b..0284c90 100644 --- a/src/application/WorkspaceShell.h +++ b/src/application/WorkspaceShell.h @@ -35,6 +35,7 @@ class WorkspaceShell : public QWidget { explicit WorkspaceShell(WorkspaceShellOptions options, QWidget* parent = nullptr); void setTools(const QStringList& tools); + void setBackHandler(std::function handler); void setNavigationRail(QWidget* rail); void setNavigationPanel(QWidget* panel); void setNavigationVisible(bool visible); @@ -50,6 +51,7 @@ class WorkspaceShell : public QWidget { void initialize(const WorkspaceShellOptions& options); void setFixedWidthVisible(QWidget* widget, bool visible, int width); void clearTopBar(); + void rebuildTopBar(); QPushButton* createTopBarButton(const QString& text); QFrame* topBar_{nullptr}; @@ -70,6 +72,9 @@ class WorkspaceShell : public QWidget { QAction* saveProjectAction_{nullptr}; std::function openProjectHandler_{}; std::function saveProjectHandler_{}; + std::function backHandler_{}; + QStringList tools_{}; + QPushButton* backButton_{nullptr}; }; } // namespace safecrowd::application From d4b0162acc5de7c626f7760977ab33d3ed1ddc3d Mon Sep 17 00:00:00 2001 From: 95x8x9 Date: Thu, 30 Apr 2026 00:37:32 +0900 Subject: [PATCH 2/4] [Application] Preserve approved layout when returning to review --- src/application/MainWindow.cpp | 18 ++++++++++++++++++ src/application/MainWindow.h | 4 ++++ 2 files changed, 22 insertions(+) diff --git a/src/application/MainWindow.cpp b/src/application/MainWindow.cpp index 0d91c19..06c615a 100644 --- a/src/application/MainWindow.cpp +++ b/src/application/MainWindow.cpp @@ -161,6 +161,7 @@ void MainWindow::saveCurrentProject() { void MainWindow::showLayoutReview(const ProjectMetadata& metadata) { currentProject_ = metadata; hasCurrentProject_ = true; + lastApprovedImportResult_.reset(); auto importResult = metadata.isBuiltInDemo() ? makeDemoImportResult() @@ -177,6 +178,13 @@ void MainWindow::showLayoutReview(const ProjectMetadata& metadata) { applySavedReviewState(metadata, &importResult); + showLayoutReview(metadata, std::move(importResult)); +} + +void MainWindow::showLayoutReview(const ProjectMetadata& metadata, safecrowd::domain::ImportResult importResult) { + currentProject_ = metadata; + hasCurrentProject_ = true; + setCentralWidget(new LayoutReviewWidget( metadata.name, importResult, @@ -189,6 +197,7 @@ void MainWindow::showLayoutReview(const ProjectMetadata& metadata) { showProjectNavigator(); }, [this](const safecrowd::domain::ImportResult& approvedImportResult) { + lastApprovedImportResult_ = approvedImportResult; showScenarioAuthoring(approvedImportResult); }, this)); @@ -200,6 +209,8 @@ void MainWindow::showScenarioAuthoring(const safecrowd::domain::ImportResult& im return; } + lastApprovedImportResult_ = importResult; + setCentralWidget(new ScenarioAuthoringWidget( currentProject_.name, *importResult.layout, @@ -211,6 +222,13 @@ void MainWindow::showScenarioAuthoring(const safecrowd::domain::ImportResult& im currentProject_ = {}; showProjectNavigator(); }, + [this]() { + if (lastApprovedImportResult_.has_value()) { + showLayoutReview(currentProject_, *lastApprovedImportResult_); + } else { + showLayoutReview(currentProject_); + } + }, this)); } diff --git a/src/application/MainWindow.h b/src/application/MainWindow.h index aa235ff..f35d16b 100644 --- a/src/application/MainWindow.h +++ b/src/application/MainWindow.h @@ -1,5 +1,7 @@ #pragma once +#include + #include #include "application/ProjectMetadata.h" @@ -26,11 +28,13 @@ class MainWindow : public QMainWindow { void openProject(const ProjectMetadata& metadata); void saveCurrentProject(); void showLayoutReview(const ProjectMetadata& metadata); + void showLayoutReview(const ProjectMetadata& metadata, safecrowd::domain::ImportResult importResult); void showScenarioAuthoring(const safecrowd::domain::ImportResult& importResult); safecrowd::domain::SafeCrowdDomain& domain_; ProjectMetadata currentProject_{}; bool hasCurrentProject_{false}; + std::optional lastApprovedImportResult_{}; }; } // namespace safecrowd::application From b5c871cd19d25bffa763667eba8930706b053544 Mon Sep 17 00:00:00 2001 From: 95x8x9 Date: Thu, 30 Apr 2026 00:47:55 +0900 Subject: [PATCH 3/4] [Application] Make back button go to previous screen --- src/application/ScenarioResultWidget.cpp | 4 +++- src/application/ScenarioRunWidget.cpp | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/application/ScenarioResultWidget.cpp b/src/application/ScenarioResultWidget.cpp index f85e692..afe2b8d 100644 --- a/src/application/ScenarioResultWidget.cpp +++ b/src/application/ScenarioResultWidget.cpp @@ -360,7 +360,9 @@ ScenarioResultWidget::ScenarioResultWidget( shell_->setTools({"Project"}); shell_->setSaveProjectHandler(saveProjectHandler_); shell_->setOpenProjectHandler(openProjectHandler_); - shell_->setBackHandler(backToLayoutReviewHandler_); + shell_->setBackHandler([this]() { + navigateToAuthoring(true); + }); auto* canvas = new SimulationCanvasWidget(layout_, shell_); canvas->setFrame(frame_); diff --git a/src/application/ScenarioRunWidget.cpp b/src/application/ScenarioRunWidget.cpp index 75d2b67..06bc69e 100644 --- a/src/application/ScenarioRunWidget.cpp +++ b/src/application/ScenarioRunWidget.cpp @@ -191,7 +191,9 @@ ScenarioRunWidget::ScenarioRunWidget( shell_->setTools({"Project"}); shell_->setSaveProjectHandler(saveProjectHandler_); shell_->setOpenProjectHandler(openProjectHandler_); - shell_->setBackHandler(backToLayoutReviewHandler_); + shell_->setBackHandler([this]() { + returnToAuthoring(); + }); canvas_ = new SimulationCanvasWidget(layout_, shell_); canvas_->setFrame(runner_.frame()); shell_->setCanvas(canvas_); From 842c59e1e9667f35fce88663ecb3c27b84fe5974 Mon Sep 17 00:00:00 2001 From: 95x8x9 Date: Thu, 30 Apr 2026 01:00:59 +0900 Subject: [PATCH 4/4] [Application] Add back navigation on layout review and clean up run back UI --- src/application/LayoutReviewWidget.cpp | 4 +++- src/application/LayoutReviewWidget.h | 1 + src/application/ScenarioRunWidget.cpp | 31 -------------------------- src/application/ScenarioRunWidget.h | 1 - 4 files changed, 4 insertions(+), 33 deletions(-) diff --git a/src/application/LayoutReviewWidget.cpp b/src/application/LayoutReviewWidget.cpp index 62d36e4..7f1f0d4 100644 --- a/src/application/LayoutReviewWidget.cpp +++ b/src/application/LayoutReviewWidget.cpp @@ -381,6 +381,7 @@ LayoutReviewWidget::LayoutReviewWidget( : QWidget(parent), projectName_(projectName), importResult_(importResult), + openProjectHandler_(std::move(openProjectHandler)), approvalHandler_(std::move(approvalHandler)) { auto* layout = new QVBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); @@ -404,7 +405,8 @@ LayoutReviewWidget::LayoutReviewWidget( shell_->setTools({"Project", "Tool"}); shell_->setSaveProjectHandler(std::move(saveProjectHandler)); - shell_->setOpenProjectHandler(std::move(openProjectHandler)); + shell_->setOpenProjectHandler(openProjectHandler_); + shell_->setBackHandler(openProjectHandler_); shell_->setCanvas(preview_); shell_->setReviewPanel(reviewPanel); diff --git a/src/application/LayoutReviewWidget.h b/src/application/LayoutReviewWidget.h index 865cc64..3533404 100644 --- a/src/application/LayoutReviewWidget.h +++ b/src/application/LayoutReviewWidget.h @@ -50,6 +50,7 @@ class LayoutReviewWidget : public QWidget { QString projectName_{}; safecrowd::domain::ImportResult importResult_{}; + std::function openProjectHandler_{}; std::function approvalHandler_{}; std::vector undoHistory_{}; WorkspaceShell* shell_{nullptr}; diff --git a/src/application/ScenarioRunWidget.cpp b/src/application/ScenarioRunWidget.cpp index 06bc69e..2dd7efa 100644 --- a/src/application/ScenarioRunWidget.cpp +++ b/src/application/ScenarioRunWidget.cpp @@ -200,7 +200,6 @@ ScenarioRunWidget::ScenarioRunWidget( shell_->setReviewPanel(createRunPanel()); shell_->setReviewPanelVisible(true); rootLayout->addWidget(shell_); - addBackToAuthoringButton(); timer_ = new QTimer(this); timer_->setInterval(33); @@ -287,36 +286,6 @@ QWidget* ScenarioRunWidget::createRunPanel() { return panel; } -void ScenarioRunWidget::addBackToAuthoringButton() { - if (canvas_ == nullptr) { - return; - } - - auto* button = new QPushButton("<", canvas_); - button->setToolTip("Back to scenario editor"); - button->setAccessibleName("Back to scenario editor"); - button->setFixedSize(40, 36); - button->move(16, 16); - button->raise(); - button->setStyleSheet( - "QPushButton {" - " background: rgba(255, 255, 255, 232);" - " border: 1px solid #c9d5e2;" - " border-radius: 10px;" - " color: #16202b;" - " font-size: 18px;" - " font-weight: 700;" - " padding-bottom: 2px;" - "}" - "QPushButton:hover {" - " background: #eef3f8;" - " border-color: #b8c6d6;" - "}"); - connect(button, &QPushButton::clicked, this, [this]() { - returnToAuthoring(); - }); -} - void ScenarioRunWidget::returnToAuthoring() { if (timer_ != nullptr) { timer_->stop(); diff --git a/src/application/ScenarioRunWidget.h b/src/application/ScenarioRunWidget.h index 067398e..81663b8 100644 --- a/src/application/ScenarioRunWidget.h +++ b/src/application/ScenarioRunWidget.h @@ -32,7 +32,6 @@ class ScenarioRunWidget : public QWidget { private: QWidget* createRunPanel(); - void addBackToAuthoringButton(); void returnToAuthoring(); void refreshStatus(); void showResults();