Bläddra i källkod

feat: 增加工程未保存变更保护

main
suyu 1 månad sedan
förälder
incheckning
5923bcd5dd
3 ändrade filer med 118 tillägg och 1 borttagningar
  1. +68
    -1
      app/src/ui/main_window.cpp
  2. +4
    -0
      app/src/ui/main_window.h
  3. +46
    -0
      app/tests/main_window_tests.cpp

+ 68
- 1
app/src/ui/main_window.cpp Visa fil

@@ -26,6 +26,7 @@
#include <QActionGroup> #include <QActionGroup>
#include <QAbstractSpinBox> #include <QAbstractSpinBox>
#include <QApplication> #include <QApplication>
#include <QCloseEvent>
#include <QEvent> #include <QEvent>
#include <QFileDialog> #include <QFileDialog>
#include <QGraphicsScene> #include <QGraphicsScene>
@@ -315,6 +316,16 @@ bool MainWindow::eventFilter(QObject *watched, QEvent *event)
return QMainWindow::eventFilter(watched, event); return QMainWindow::eventFilter(watched, event);
} }


void MainWindow::closeEvent(QCloseEvent *event)
{
if (!confirmSaveBeforeDestructiveAction())
{
event->ignore();
return;
}
QMainWindow::closeEvent(event);
}

const std::string &MainWindow::currentHmiPageId() const const std::string &MainWindow::currentHmiPageId() const
{ {
return current_hmi_page_id_; return current_hmi_page_id_;
@@ -1035,6 +1046,44 @@ void MainWindow::refreshProjectUi()
{ {
project_workspace_controller_->refresh(); project_workspace_controller_->refresh();
updateEditActions(); updateEditActions();
updateWindowTitle();
}

void MainWindow::updateWindowTitle()
{
QString title = tr("综合平台编程器");
if (project_service_.isModified())
{
title += QStringLiteral("*");
}
setWindowTitle(title);
}

bool MainWindow::confirmSaveBeforeDestructiveAction()
{
if (!project_service_.isModified())
{
return true;
}

const QString project_name = fromUtf8(project_service_.project().metadata.name);
const QMessageBox::StandardButton choice = QMessageBox::warning(
this,
tr("工程尚未保存"),
tr("工程“%1”有未保存的修改,是否先保存?").arg(project_name),
QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel,
QMessageBox::Save);
if (choice == QMessageBox::Cancel)
{
return false;
}
if (choice == QMessageBox::Discard)
{
return true;
}

saveProject();
return !project_service_.isModified();
} }


void MainWindow::updateProjectTreeActions() void MainWindow::updateProjectTreeActions()
@@ -1245,6 +1294,10 @@ void MainWindow::deleteSelectedLogicObject()


void MainWindow::createNewProject() void MainWindow::createNewProject()
{ {
if (!confirmSaveBeforeDestructiveAction())
{
return;
}
const QString name = QInputDialog::getText( const QString name = QInputDialog::getText(
this, tr("新建工程"), tr("工程名称")); this, tr("新建工程"), tr("工程名称"));
if (name.isEmpty()) if (name.isEmpty())
@@ -1273,12 +1326,18 @@ void MainWindow::createNewProject()


void MainWindow::saveProject() void MainWindow::saveProject()
{ {
if (!project_service_.hasCurrentFile())
{
saveProjectAs();
return;
}
const ProjectOperationResult result = project_service_.save(); const ProjectOperationResult result = project_service_.save();
if (!result.succeeded) if (!result.succeeded)
{ {
saveProjectAs();
showProjectResult(tr("保存工程"), fromUtf8(result.message), false);
return; return;
} }
updateWindowTitle();
showProjectResult(tr("保存工程"), tr("工程已保存"), true); showProjectResult(tr("保存工程"), tr("工程已保存"), true);
} }


@@ -1291,6 +1350,10 @@ void MainWindow::saveProjectAs()
return; return;
} }
const ProjectOperationResult result = project_service_.saveAs(toUtf8(path)); const ProjectOperationResult result = project_service_.saveAs(toUtf8(path));
if (result.succeeded)
{
updateWindowTitle();
}
showProjectResult( showProjectResult(
tr("保存工程"), tr("保存工程"),
result.succeeded ? tr("工程已保存") : fromUtf8(result.message), result.succeeded ? tr("工程已保存") : fromUtf8(result.message),
@@ -1299,6 +1362,10 @@ void MainWindow::saveProjectAs()


void MainWindow::loadProject() void MainWindow::loadProject()
{ {
if (!confirmSaveBeforeDestructiveAction())
{
return;
}
const QString path = QFileDialog::getOpenFileName( const QString path = QFileDialog::getOpenFileName(
this, tr("加载工程"), {}, tr("工程文件 (*.json)")); this, tr("加载工程"), {}, tr("工程文件 (*.json)"));
if (path.isEmpty()) if (path.isEmpty())


+ 4
- 0
app/src/ui/main_window.h Visa fil

@@ -20,6 +20,7 @@
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QAction; class QAction;
class QActionGroup; class QActionGroup;
class QCloseEvent;
class QEvent; class QEvent;
class QLabel; class QLabel;


@@ -94,6 +95,7 @@ public:


protected: protected:
bool eventFilter(QObject *watched, QEvent *event) override; bool eventFilter(QObject *watched, QEvent *event) override;
void closeEvent(QCloseEvent *event) override;


private: private:
// 配置菜单和工具栏动作 // 配置菜单和工具栏动作
@@ -117,6 +119,8 @@ private:
void clearActiveSelection(); void clearActiveSelection();
void updateEditActions(); void updateEditActions();
void clearEditorHistories(); void clearEditorHistories();
void updateWindowTitle();
bool confirmSaveBeforeDestructiveAction();
// 刷新工程树和当前 HMI 页面信息 // 刷新工程树和当前 HMI 页面信息
void refreshProjectUi(); void refreshProjectUi();
void updateProjectTreeActions(); void updateProjectTreeActions();


+ 46
- 0
app/tests/main_window_tests.cpp Visa fil

@@ -560,6 +560,51 @@ void testRuntimeAlarmListInteraction()
"AlarmList must become visible again after returning to editing"); "AlarmList must become visible again after returning to editing");
} }


void testWindowTitleTracksUnsavedProjectChanges()
{
TestProjectStorage storage;
ProjectService project_service(storage);
HmiEditorService editor_service(project_service);
LogicEditorService logic_editor_service(project_service);
require(editor_service.ensureDefaultPage().succeeded,
"window-title test must create a default HMI page");
require(logic_editor_service.ensureDefaultLogic().succeeded,
"window-title test must create a default control logic");
require(project_service.saveAs("window-title-test.json").succeeded,
"window-title fixture must start from a saved project");

VirtualRegisterRepository repository;
HmiRuntimeService runtime_service(repository);
AlarmEditorService alarm_editor_service(project_service);
AlarmService alarm_service(project_service, repository);
RegisterCommentService register_comment_service(project_service);
OfflineSimulationService simulation_service(repository);
RuntimeModeService mode_service(project_service, simulation_service);
RegisterMonitorService monitor_service(repository);
MainWindow window(
mode_service,
project_service,
editor_service,
logic_editor_service,
runtime_service,
alarm_editor_service,
alarm_service,
register_comment_service,
monitor_service);

require(!window.windowTitle().endsWith(QLatin1Char('*')),
"a saved project title must not show an unsaved marker");
requiredChild<QAction>(window, "addLabelAction")->trigger();
require(project_service.isModified()
&& window.windowTitle().endsWith(QLatin1Char('*')),
"editing the project must immediately add an unsaved marker");

requiredChild<QAction>(window, "saveProjectAction")->trigger();
require(!project_service.isModified()
&& !window.windowTitle().endsWith(QLatin1Char('*')),
"saving the project must immediately remove the unsaved marker");
}

void testModeActionsControlEditingAvailability() void testModeActionsControlEditingAvailability()
{ {
// 验证模式动作会同步禁用编辑入口,并在失败时恢复当前选择 // 验证模式动作会同步禁用编辑入口,并在失败时恢复当前选择
@@ -1482,6 +1527,7 @@ int main(int argc, char *argv[])
testRuntimeProgressBarRendering(); testRuntimeProgressBarRendering();
testRuntimePageJumpDoesNotRequireRegisterWritePermission(); testRuntimePageJumpDoesNotRequireRegisterWritePermission();
testRuntimeAlarmListInteraction(); testRuntimeAlarmListInteraction();
testWindowTitleTracksUnsavedProjectChanges();
testModeActionsControlEditingAvailability(); testModeActionsControlEditingAvailability();
testEdgeTimerPropertyEditingAndParallelMenu(); testEdgeTimerPropertyEditingAndParallelMenu();
testPlcConfigurationUsesDialog(); testPlcConfigurationUsesDialog();


Laddar…
Avbryt
Spara