Skip to content

Commit 0ce65c3

Browse files
committed
App: save/restore application UI state
Relates to GitHub #277
1 parent 3b4d7e6 commit 0ce65c3

18 files changed

+228
-16
lines changed

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -632,6 +632,7 @@ list(
632632
APPEND MayoConv_HeaderFiles
633633
${PROJECT_SOURCE_DIR}/src/app/app_module_properties.h
634634
${PROJECT_SOURCE_DIR}/src/app/app_module.h
635+
${PROJECT_SOURCE_DIR}/src/app/app_ui_state.h
635636
${PROJECT_SOURCE_DIR}/src/app/library_info.h
636637
${PROJECT_SOURCE_DIR}/src/app/recent_files.h
637638
)
@@ -646,6 +647,7 @@ list(
646647
APPEND MayoConv_SourceFiles
647648
${PROJECT_SOURCE_DIR}/src/app/app_module_properties.cpp
648649
${PROJECT_SOURCE_DIR}/src/app/app_module.cpp
650+
${PROJECT_SOURCE_DIR}/src/app/app_ui_state.cpp
649651
${PROJECT_SOURCE_DIR}/src/app/library_info.cpp
650652
${PROJECT_SOURCE_DIR}/src/app/recent_files.cpp
651653
)

src/app/app_context.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ AppContext::AppContext(MainWindow* wnd)
2424
assert(m_wnd->widgetPageDocuments() != nullptr);
2525

2626
QObject::connect(
27-
m_wnd->widgetPageDocuments(), &WidgetMainControl::currentDocumentIndexChanged,
28-
this, &AppContext::onCurrentDocumentIndexChanged
29-
);
27+
m_wnd->widgetPageDocuments(), &WidgetMainControl::currentDocumentIndexChanged,
28+
this, &AppContext::onCurrentDocumentIndexChanged
29+
);
3030
}
3131

3232
GuiApplication* AppContext::guiApp() const

src/app/app_module.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,12 @@ Settings::Variant AppModule::toVariant(const Property& prop) const
169169
varBlob.setByteArray(true);
170170
return varBlob;
171171
}
172+
else if (isType<PropertyAppUiState>(prop)) {
173+
const QByteArray blob = AppUiState::toBlob(constRef<PropertyAppUiState>(prop));
174+
Variant varBlob(blob.toStdString());
175+
varBlob.setByteArray(true);
176+
return varBlob;
177+
}
172178
else {
173179
return PropertyValueConversion::toVariant(prop);
174180
}
@@ -184,6 +190,13 @@ bool AppModule::fromVariant(Property* prop, const Settings::Variant& variant) co
184190
ptr<PropertyRecentFiles>(prop)->setValue(recentFiles);
185191
return stream.status() == QDataStream::Ok;
186192
}
193+
else if (isType<PropertyAppUiState>(prop)) {
194+
bool ok = false;
195+
auto blob = QtCoreUtils::QByteArray_frowRawData(variant.toConstRefString());
196+
auto uiState = AppUiState::fromBlob(blob, &ok);
197+
ptr<PropertyAppUiState>(prop)->setValue(uiState);
198+
return ok;
199+
}
187200
else {
188201
return PropertyValueConversion::fromVariant(prop, variant);
189202
}

src/app/app_module_properties.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,11 @@ AppModuleProperties::AppModuleProperties(Settings* settings)
5050
settings->addSetting(&this->actionOnDocumentFileChange, groupId_application);
5151
settings->addSetting(&this->linkWithDocumentSelector, groupId_application);
5252
settings->addSetting(&this->forceOpenGlFallbackWidget, groupId_application);
53+
settings->addSetting(&this->appUiState, groupId_application);
5354
this->recentFiles.setUserVisible(false);
5455
this->lastOpenDir.setUserVisible(false);
5556
this->lastSelectedFormatFilter.setUserVisible(false);
57+
this->appUiState.setUserVisible(false);
5658

5759
// Meshing
5860
this->meshingQuality.mutableEnumeration().changeTrContext(AppModuleProperties::textIdContext());
@@ -88,6 +90,7 @@ AppModuleProperties::AppModuleProperties(Settings* settings)
8890
this->lastSelectedFormatFilter.setValue({});
8991
this->actionOnDocumentFileChange.setValue(ActionOnDocumentFileChange::None);
9092
this->linkWithDocumentSelector.setValue(true);
93+
this->appUiState.setValue({});
9194
#ifndef MAYO_OS_MAC
9295
this->forceOpenGlFallbackWidget.setValue(false);
9396
#else

src/app/app_module_properties.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
#pragma once
88

9+
#include "app_ui_state.h"
910
#include "recent_files.h"
1011

1112
#include "../base/io_format.h"
@@ -60,6 +61,7 @@ class AppModuleProperties : public PropertyGroup {
6061
PropertyEnum<ActionOnDocumentFileChange> actionOnDocumentFileChange{ this, textId("actionOnDocumentFileChange") };
6162
PropertyBool linkWithDocumentSelector{ this, textId("linkWithDocumentSelector") };
6263
PropertyBool forceOpenGlFallbackWidget{ this, textId("forceOpenGlFallbackWidget") };
64+
PropertyAppUiState appUiState{ this, textId("appUiState") };
6365
// Meshing
6466
const Settings::GroupIndex groupId_meshing;
6567
enum class BRepMeshQuality { VeryCoarse, Coarse, Normal, Precise, VeryPrecise, UserDefined };

src/app/app_ui_state.cpp

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#include "app_ui_state.h"
2+
3+
#include "../qtcommon/qtcore_utils.h"
4+
#include <QtCore/QDataStream>
5+
#include <QtCore/QIODevice>
6+
7+
namespace Mayo {
8+
9+
template<> const char PropertyAppUiState::TypeName[] = "Mayo::PropertyAppUiState";
10+
11+
QByteArray AppUiState::toBlob(const AppUiState& state)
12+
{
13+
QByteArray blob;
14+
QDataStream stream(&blob, QIODevice::WriteOnly);
15+
stream << QtCoreUtils::QByteArray_frowRawData(std::string_view{PropertyAppUiState::TypeName});
16+
constexpr uint32_t version = 1;
17+
stream << version;
18+
stream << state.mainWindowGeometry;
19+
stream << state.pageDocuments_isLeftSideBarVisible;
20+
return blob;
21+
22+
}
23+
24+
AppUiState AppUiState::fromBlob(const QByteArray& blob, bool* ok)
25+
{
26+
auto fnSetOk = [=](bool v) {
27+
if (ok)
28+
*ok = v;
29+
};
30+
31+
fnSetOk(false);
32+
AppUiState state;
33+
QDataStream stream(blob);
34+
QByteArray identifier;
35+
stream >> identifier;
36+
if (identifier == PropertyAppUiState::TypeName) {
37+
uint32_t version = 0;
38+
stream >> version;
39+
if (version == 1) {
40+
stream >> state.mainWindowGeometry;
41+
stream >> state.pageDocuments_isLeftSideBarVisible;
42+
fnSetOk(true);
43+
}
44+
}
45+
46+
return state;
47+
}
48+
49+
} // namespace Mayo

src/app/app_ui_state.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/****************************************************************************
2+
** Copyright (c) 2024, Fougue Ltd. <https://www.fougue.pro>
3+
** All rights reserved.
4+
** See license at https://github.com/fougue/mayo/blob/master/LICENSE.txt
5+
****************************************************************************/
6+
7+
#pragma once
8+
9+
#include "../base/property_builtins.h"
10+
#include <QtCore/QByteArray>
11+
12+
namespace Mayo {
13+
14+
// Stores the UI state of the main application widgets
15+
struct AppUiState {
16+
QByteArray mainWindowGeometry; // Provided by QWidget::saveGeometry()
17+
bool pageDocuments_isLeftSideBarVisible = true;
18+
19+
// Serialization functions
20+
static QByteArray toBlob(const AppUiState& state);
21+
static AppUiState fromBlob(const QByteArray& blob, bool* ok = nullptr);
22+
};
23+
using PropertyAppUiState = GenericProperty<AppUiState>;
24+
25+
} // namespace Mayo

src/app/main.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,7 @@ static int runApp(QCoreApplication* qtApp)
438438
// Create MainWindow
439439
MainWindow mainWindow(guiApp);
440440
mainWindow.setWindowTitle(QCoreApplication::applicationName());
441+
appModule->settings()->loadProperty(&appModule->properties()->appUiState);
441442
mainWindow.show();
442443
if (!args.listFilepathToOpen.empty()) {
443444
QTimer::singleShot(0, qtApp, [&]{ mainWindow.openDocumentsFromList(args.listFilepathToOpen); });

src/app/mainwindow.cpp

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,13 @@ MainWindow::~MainWindow()
7373

7474
void MainWindow::showEvent(QShowEvent* event)
7575
{
76+
const auto& uiState = AppModule::get()->properties()->appUiState.value();
77+
if (!uiState.mainWindowGeometry.isEmpty())
78+
this->restoreGeometry(uiState.mainWindowGeometry);
79+
80+
if (this->widgetPageDocuments())
81+
this->widgetPageDocuments()->widgetLeftSideBar()->setVisible(uiState.pageDocuments_isLeftSideBarVisible);
82+
7683
QMainWindow::showEvent(event);
7784
#if defined(Q_OS_WIN) && QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
7885
constexpr Qt::FindChildOption findMode = Qt::FindDirectChildrenOnly;
@@ -84,15 +91,26 @@ void MainWindow::showEvent(QShowEvent* event)
8491
#endif
8592
}
8693

94+
void MainWindow::closeEvent(QCloseEvent* event)
95+
{
96+
AppUiState uiState = AppModule::get()->properties()->appUiState;
97+
uiState.mainWindowGeometry = this->saveGeometry();
98+
if (this->widgetPageDocuments())
99+
uiState.pageDocuments_isLeftSideBarVisible = this->widgetPageDocuments()->widgetLeftSideBar()->isVisible();
100+
101+
AppModule::get()->properties()->appUiState.setValue(uiState);
102+
QMainWindow::closeEvent(event);
103+
}
104+
87105
void MainWindow::addPage(IAppContext::Page page, IWidgetMainPage* pageWidget)
88106
{
89107
assert(m_mapWidgetPage.find(page) == m_mapWidgetPage.cend());
90108
assert(m_ui->stack_Main->indexOf(pageWidget) == -1);
91109
m_mapWidgetPage.insert({ page, pageWidget });
92110
m_ui->stack_Main->addWidget(pageWidget);
93111
QObject::connect(
94-
pageWidget, &IWidgetMainPage::updateGlobalControlsActivationRequired,
95-
this, &MainWindow::updateControlsActivation
112+
pageWidget, &IWidgetMainPage::updateGlobalControlsActivationRequired,
113+
this, &MainWindow::updateControlsActivation
96114
);
97115
}
98116

src/app/mainwindow.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ class MainWindow : public QMainWindow {
3535

3636
protected:
3737
void showEvent(QShowEvent* event) override;
38+
void closeEvent(QCloseEvent* event) override;
3839

3940
private:
4041
void addPage(IAppContext::Page page, IWidgetMainPage* pageWidget);

0 commit comments

Comments
 (0)