diff options
| author | Danilo M. <danix@danix.xyz> | 2026-08-03 16:00:38 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-08-04 12:53:35 +0200 |
| commit | 98c1c615d147bfee3f17d4f420e1512af5a79436 (patch) | |
| tree | 82037d456544d6128324d85d5d572208f84c06a2 /tests/test_mainwindow.cpp | |
| parent | a5b2151ef4b602350bab6ecfcd05b4bae56fd6af (diff) | |
| download | qtmaildir-98c1c615d147bfee3f17d4f420e1512af5a79436.tar.gz qtmaildir-98c1c615d147bfee3f17d4f420e1512af5a79436.zip | |
feat: persist window, splitter and column widths
Resizing the window, the splitter or a thread-list column was undone by
the next launch. State now round-trips through a separate settings file
at ~/.local/state/qtmaildir/uistate.conf, written on close and read at
startup.
The state file is deliberately not the user's config: a base64 geometry
blob does not belong in a hand-edited file, and rewriting that file on
exit would drop its comments and key order, which QSettings does not
preserve.
Two details that are easy to get wrong:
QStandardPaths::StateLocation appends both the organization and the
application name, and both are "qtmaildir" here, so it resolves to
~/.local/state/qtmaildir/qtmaildir. The path is built from
GenericStateLocation instead, matching Config::defaultPath().
Restore runs after buildMenus() rather than at the end of buildUi():
QMainWindow::restoreState() matches toolbars by object name and silently
drops the position of one that does not exist yet.
Every restore is conditional on a non-empty blob, so a missing or
rejected state file leaves the built-in defaults instead of producing a
zero-size window.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Diffstat (limited to 'tests/test_mainwindow.cpp')
| -rw-r--r-- | tests/test_mainwindow.cpp | 61 |
1 files changed, 61 insertions, 0 deletions
diff --git a/tests/test_mainwindow.cpp b/tests/test_mainwindow.cpp index 6bfa925..628472b 100644 --- a/tests/test_mainwindow.cpp +++ b/tests/test_mainwindow.cpp @@ -20,7 +20,9 @@ #include <QAction> #include <QDir> +#include <QFile> #include <QSettings> +#include <QStandardPaths> #include <QTemporaryDir> #include "config.h" @@ -41,6 +43,9 @@ private slots: void configuredBindingReachesTheAction(); void cidPrefixesAreBangFree(); void cidPrefixesAreDistinctPerMessage(); + void uiStateIsNotWrittenIntoTheUserConfig(); + void uiStateSurvivesARestart(); + void missingUiStateLeavesTheDefaults(); }; void TestMainWindow::everyKnownActionIsRegistered() @@ -158,6 +163,62 @@ void TestMainWindow::cidPrefixesAreDistinctPerMessage() } } +void TestMainWindow::uiStateIsNotWrittenIntoTheUserConfig() +{ + // The config file is hand-edited and must never gain a base64 geometry + // blob, nor be rewritten on exit: QSettings preserves neither comments nor + // key order, so writing it would quietly destroy the user's formatting. + QVERIFY(MainWindow::uiStatePath() != Config::defaultPath()); + + // One qtmaildir component, not two. QStandardPaths::StateLocation appends + // both the organization and the application name, and here both are + // "qtmaildir", so using it nests the directory inside itself. + QCOMPARE(MainWindow::uiStatePath().count(QStringLiteral("/qtmaildir/")), 1); + QVERIFY(MainWindow::uiStatePath().endsWith( + QStringLiteral("/qtmaildir/uistate.conf"))); +} + +void TestMainWindow::uiStateSurvivesARestart() +{ + // Test mode redirects QStandardPaths at the process level, so the state + // file lands in a scratch directory rather than the real ~/.local/state. + QStandardPaths::setTestModeEnabled(true); + QFile::remove(MainWindow::uiStatePath()); + + const QSize resized(940, 620); + { + const Config config; + MainWindow window(config); + window.resize(resized); + window.close(); // closeEvent() is what persists the state + } + + QVERIFY2(QFile::exists(MainWindow::uiStatePath()), + qPrintable(QStringLiteral("no state file at %1") + .arg(MainWindow::uiStatePath()))); + + const Config config; + MainWindow reopened(config); + QCOMPARE(reopened.size(), resized); + + QFile::remove(MainWindow::uiStatePath()); + QStandardPaths::setTestModeEnabled(false); +} + +void TestMainWindow::missingUiStateLeavesTheDefaults() +{ + // A restore that silently succeeded on an empty blob would give a + // zero-size window on first launch. Absent state must be a no-op. + QStandardPaths::setTestModeEnabled(true); + QFile::remove(MainWindow::uiStatePath()); + + const Config config; + MainWindow window(config); + QCOMPARE(window.size(), QSize(1200, 800)); + + QStandardPaths::setTestModeEnabled(false); +} + // Constructing a MainWindow needs a QApplication and a platform plugin. The // test has no display under ctest, so it runs offscreen unless the caller // asked for something else. |
