From 9af562fcbd28d4d9a873b303e737074ec0986858 Mon Sep 17 00:00:00 2001 From: Kellermorph Date: Thu, 24 Sep 2026 10:30:01 +0200 Subject: [PATCH] Remember the sheet background colour between runs Picking a sheet (folio) background colour in the diagram editor was lost on every restart. Diagram::background_color is a static initialised to white and PaletteGraphicsView's s_custom_bg a static bool, and neither was ever written anywhere -- Diagram::toXml() carries no colour attribute either -- so closing and reopening a project always came back on the default and the choice had to be made again. Store it in QSettings under diagrameditor/sheet_background_* as a pair of values rather than one: the colour, and whether it was picked explicitly. Both halves are needed. "#ffffff, follow the system" and "#ffffff, always white" are the same colour and two behaviours -- the first is what the views invert on a dark palette -- so keeping only the colour would silently turn one into the other on the next start, which is the reported problem one step removed. The colour is written as HexRgb on purpose. The SVG export gives Diagram::background_color an alpha of 0 to render a transparent background and never puts it back, and that transient value must not be persisted as a permanently transparent sheet. Applied from main() after the headless export and scripting branch -- those return before reaching it and must keep rendering on plain white, the rule ProjectPrintWindow already enforces for printing -- and before QETApp is constructed, since that constructor already loads the projects given on the command line. The GUI export dialog is left alone: it renders through drawBackground(), so what you see is what you export, as it already was within a session. Saved at the moment the colour is applied rather than at shutdown, so neither the print window's temporary white nor the SVG export's alpha can reach it. The button's constructor now mirrors the stored state instead of always claiming "system colour", and the "recently used" list is stored alongside it. Covered by tst_sheetbackgroundsetting, which pins the custom flag and the dropped alpha -- the two rules a single stored colour would lose. --- sources/main.cpp | 18 +++ sources/ui/diagrambgcolorbutton.cpp | 43 +++++- sources/ui/diagrambgcolorbutton.h | 1 + sources/utils/qetsettings.cpp | 63 ++++++++ sources/utils/qetsettings.h | 24 +++ tests/qttest/CMakeLists.txt | 11 ++ tests/qttest/tst_sheetbackgroundsetting.cpp | 160 ++++++++++++++++++++ 7 files changed, 318 insertions(+), 2 deletions(-) create mode 100644 tests/qttest/tst_sheetbackgroundsetting.cpp diff --git a/sources/main.cpp b/sources/main.cpp index d38eca388..3c065899f 100644 --- a/sources/main.cpp +++ b/sources/main.cpp @@ -22,6 +22,8 @@ #include "logging/eventloopwatchdog.h" #include "logging/qetlogger.h" #include "machine_info.h" +#include "diagram.h" +#include "palettegraphicsview.h" #include "qet.h" #include "qetapp.h" #include "qetmessagebox.h" @@ -144,6 +146,22 @@ int main(int argc, char **argv) #endif } + // Re-apply the sheet background last picked in the diagram editor, so + // every project opened from here on -- existing or new, whichever one + // it is -- draws that background instead of the built-in default that + // would otherwise force the user to pick it again after each start. + // + // Done here rather than in main()'s first lines on purpose: the + // headless export and scripting runs above return before reaching + // this point and must keep rendering on plain white. It also has to + // happen before QETApp is constructed below, since that constructor + // already loads the projects given on the command line. + { + const QetSettings::SheetBackground sheet_background = QetSettings::sheetBackground(); + PaletteGraphicsView::setCustomBackgroundColor(sheet_background.custom); + Diagram::background_color = sheet_background.color; + } + // Resolve the logger's state (log directory, session filename, open // file handle) explicitly here, immediately before installing the // handler -- not implicitly on whichever thread happens to log diff --git a/sources/ui/diagrambgcolorbutton.cpp b/sources/ui/diagrambgcolorbutton.cpp index 608ccac25..dce538faf 100644 --- a/sources/ui/diagrambgcolorbutton.cpp +++ b/sources/ui/diagrambgcolorbutton.cpp @@ -23,6 +23,7 @@ #include "../qetdiagrameditor.h" #include "../projectview.h" #include "../qetproject.h" +#include "../utils/qetsettings.h" #include #include @@ -62,8 +63,28 @@ DiagramBgColorToolButton::DiagramBgColorToolButton(QETDiagramEditor *editor, QWi setStatusTip(tr("Choisir la couleur de fond du folio", "status bar tip")); - m_is_system_color = true; - m_current = QApplication::palette().color(QPalette::Base); + //main() already restored the stored choice onto the global + //Diagram::background_color and PaletteGraphicsView flag; mirror it + //here so the button opens on what the editor actually draws, rather + //than always claiming "system colour" as it used to. + if (PaletteGraphicsView::customBackgroundColor()) { + m_is_system_color = false; + m_current = Diagram::background_color; + } else { + m_is_system_color = true; + m_current = QApplication::palette().color(QPalette::Base); + } + for (const QString &name : QetSettings::sheetBackgroundRecentColors()) + { + const QColor c(name); + if (c.isValid()) { + m_recent.append(c); + } + } + while (m_recent.size() > MAX_RECENT) { + m_recent.removeLast(); + } + setMenu(new QMenu(this)); rebuildMenu(); setSwatch(m_current); @@ -123,6 +144,7 @@ void DiagramBgColorToolButton::applyColor(const QColor &color) PaletteGraphicsView::setCustomBackgroundColor(true); Diagram::background_color = color; + QetSettings::setSheetBackground(QetSettings::SheetBackground{color, true}); QETDiagramEditor *editor = m_editor; if (!editor) { @@ -146,6 +168,8 @@ void DiagramBgColorToolButton::applySystemColor() PaletteGraphicsView::setCustomBackgroundColor(false); Diagram::background_color = Qt::white; + QetSettings::setSheetBackground( + QetSettings::SheetBackground{QColor(Qt::white), false}); QETDiagramEditor *editor = m_editor; if (!editor) { @@ -187,6 +211,21 @@ void DiagramBgColorToolButton::rememberRecent(const QColor &color) m_recent.removeLast(); } rebuildMenu(); + persistRecent(); +} + +/** + @brief DiagramBgColorToolButton::persistRecent + Write the "recently used" colours back to the settings, so they are + still there on the next start instead of starting out empty. +*/ +void DiagramBgColorToolButton::persistRecent() +{ + QStringList names; + for (const QColor &c : std::as_const(m_recent)) { + names << c.name(); + } + QetSettings::setSheetBackgroundRecentColors(names); } /** diff --git a/sources/ui/diagrambgcolorbutton.h b/sources/ui/diagrambgcolorbutton.h index 6da4d8806..ceb04cb13 100644 --- a/sources/ui/diagrambgcolorbutton.h +++ b/sources/ui/diagrambgcolorbutton.h @@ -52,6 +52,7 @@ class DiagramBgColorToolButton : public QToolButton void applySystemColor(); void chooseOtherColor(); void rememberRecent(const QColor &color); + void persistRecent(); void setSwatch(const QColor &color); static QIcon swatchIcon(const QColor &color); diff --git a/sources/utils/qetsettings.cpp b/sources/utils/qetsettings.cpp index 0c97f9816..d15cdcb3e 100644 --- a/sources/utils/qetsettings.cpp +++ b/sources/utils/qetsettings.cpp @@ -152,4 +152,67 @@ namespace QetSettings QSettings settings; settings.setValue("scripting/enabled", enabled); } + + /** + * @brief setSheetBackground + * Store the sheet background last picked in the diagram editor, so the + * next start opens on it for every project, old or new. @sa sheetBackground + * @param background + */ + void setSheetBackground(const SheetBackground &background) + { + QSettings settings; + //HexRgb, not HexArgb, on purpose: Diagram::background_color is + //temporarily given an alpha of 0 by the SVG export to make the + //background transparent, and that transient value must never end + //up here as a stored transparent sheet. + settings.setValue(QStringLiteral("diagrameditor/sheet_background_color"), + background.color.name(QColor::HexRgb)); + settings.setValue(QStringLiteral("diagrameditor/sheet_background_custom"), + background.custom); + } + + /** + * @brief sheetBackground + * @return the sheet background stored by setSheetBackground(), or the + * built-in default (white, following the system) when there is none. + * @sa setSheetBackground + */ + SheetBackground sheetBackground() + { + QSettings settings; + SheetBackground background; + background.custom = settings.value( + QStringLiteral("diagrameditor/sheet_background_custom"), + false).toBool(); + const QColor stored(settings.value( + QStringLiteral("diagrameditor/sheet_background_color"), + QStringLiteral("#ffffff")).toString()); + background.color = stored.isValid() ? stored : QColor(Qt::white); + return background; + } + + /** + * @brief sheetBackgroundRecentColors + * @return the "recently used" sheet background colours, most recent + * first. Empty until the user has picked a non-preset colour. + * @sa setSheetBackgroundRecentColors + */ + QStringList sheetBackgroundRecentColors() + { + QSettings settings; + return settings.value(QStringLiteral("diagrameditor/sheet_background_recent")).toStringList(); + } + + /** + * @brief setSheetBackgroundRecentColors + * Store the "recently used" sheet background colours, most recent first. + * @sa sheetBackgroundRecentColors + * @param colors + */ + void setSheetBackgroundRecentColors(const QStringList &colors) + { + QSettings settings; + settings.setValue(QStringLiteral("diagrameditor/sheet_background_recent"), colors); + } } diff --git a/sources/utils/qetsettings.h b/sources/utils/qetsettings.h index 0108b0474..5712aaf37 100644 --- a/sources/utils/qetsettings.h +++ b/sources/utils/qetsettings.h @@ -19,6 +19,8 @@ #define QETSETTINGS_H #include +#include +#include /** *Interface to use QSettings in QElectroTech @@ -36,6 +38,28 @@ namespace QetSettings bool scriptingEnabled(); void setScriptingEnabled(bool enabled); bool scriptingForcedByEnvironment(); + + /** + @brief The sheet (folio) background the diagram editors draw. + + Both halves are needed, not just the colour: with custom == false + the sheet follows the system ("Couleur systeme") and the views are + free to invert the lightness on a dark palette, while the very same + #ffffff with custom == true is an explicit "always white" that + must not be inverted. Storing only the colour would collapse the + two on the next start. + */ + struct SheetBackground + { + QColor color; + bool custom = false; + }; + + SheetBackground sheetBackground(); + void setSheetBackground(const SheetBackground &background); + + QStringList sheetBackgroundRecentColors(); + void setSheetBackgroundRecentColors(const QStringList &colors); } #endif // QETSETTINGS_H diff --git a/tests/qttest/CMakeLists.txt b/tests/qttest/CMakeLists.txt index 0e8720c12..c5c13e6ce 100644 --- a/tests/qttest/CMakeLists.txt +++ b/tests/qttest/CMakeLists.txt @@ -170,6 +170,17 @@ add_test(NAME tst_scriptingsetting COMMAND tst_scriptingsetting) target_include_directories(tst_scriptingsetting PRIVATE ${QET_DIR}/sources) target_link_libraries(tst_scriptingsetting PRIVATE Qt::Test Qt::Gui) +# QetSettings::sheetBackground() -- the sheet (folio) background the diagram +# editor remembers between runs. Compiles qetsettings.cpp alone for the same +# reason as the test above; Qt::Gui because the pair carries a QColor. +add_executable( + tst_sheetbackgroundsetting + tst_sheetbackgroundsetting.cpp + ${QET_DIR}/sources/utils/qetsettings.cpp) +add_test(NAME tst_sheetbackgroundsetting COMMAND tst_sheetbackgroundsetting) +target_include_directories(tst_sheetbackgroundsetting PRIVATE ${QET_DIR}/sources) +target_link_libraries(tst_sheetbackgroundsetting PRIVATE Qt::Test Qt::Gui) + # CrashHandler::formatInt() -- the async-signal-safe decimal formatter the # signal handler uses for the "Signal: N" line of a crash dump. Compiles # crashhandler.cpp and logring.cpp alongside; the handler deliberately diff --git a/tests/qttest/tst_sheetbackgroundsetting.cpp b/tests/qttest/tst_sheetbackgroundsetting.cpp new file mode 100644 index 000000000..beb639b8a --- /dev/null +++ b/tests/qttest/tst_sheetbackgroundsetting.cpp @@ -0,0 +1,160 @@ +/* + Copyright 2006-2026 The QElectroTech Team + This file is part of QElectroTech. + + QElectroTech is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + QElectroTech is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with QElectroTech. If not, see . +*/ + +/* + QetSettings::sheetBackground() -- the sheet (folio) background the + diagram editor remembers between runs, so the user does not have to + pick it again on every start. + + Two properties are pinned here, and both are why this is a pair of + values rather than a single colour: + + * The custom flag. "#ffffff" picked explicitly ("always white, never + inverted on a dark palette") and "#ffffff" left to follow the + system (where the views invert the lightness) are one colour and + two behaviours. Storing only the colour would silently turn one + into the other on the next start -- which is exactly the reported + bug, one step removed. + * The alpha being dropped. The SVG export gives + Diagram::background_color an alpha of 0 to render a transparent + background, and that transient value must not be persisted as a + permanently transparent sheet. + + This test owns its own QSettings scope (organization + application + name), so it cannot read or write the configuration of whoever runs + it. +*/ + +#include "utils/qetsettings.h" + +#include +#include + +class TstSheetBackgroundSetting : public QObject +{ + Q_OBJECT + + private slots: + void initTestCase(); + void init(); + + void defaultsToWhiteFollowingTheSystem(); + void colourAndCustomFlagAreStoredTogether(); + void sameColourStillDistinguishesTheTwoModes(); + void alphaIsNotStored(); + void unreadableColourFallsBackToWhite(); + void recentColorsDefaultToEmpty(); + void recentColorsKeepTheirOrder(); +}; + +void TstSheetBackgroundSetting::initTestCase() +{ + // A scope of this test's own: whatever this writes must not land in + // the configuration of the account running the suite. + QCoreApplication::setOrganizationName( + QStringLiteral("QElectroTech-tst_sheetbackgroundsetting")); + QCoreApplication::setApplicationName( + QStringLiteral("tst_sheetbackgroundsetting")); + QSettings settings; + settings.clear(); +} + +void TstSheetBackgroundSetting::init() +{ + QSettings settings; + settings.remove(QStringLiteral("diagrameditor/sheet_background_color")); + settings.remove(QStringLiteral("diagrameditor/sheet_background_custom")); + settings.remove(QStringLiteral("diagrameditor/sheet_background_recent")); + settings.sync(); +} + +void TstSheetBackgroundSetting::defaultsToWhiteFollowingTheSystem() +{ + // An untouched installation, i.e. every existing one after an upgrade: + // white sheet, following the system so the views may still invert it. + const auto background = QetSettings::sheetBackground(); + QVERIFY(background.color.isValid()); + QCOMPARE(background.color, QColor(Qt::white)); + QVERIFY(!background.custom); +} + +void TstSheetBackgroundSetting::colourAndCustomFlagAreStoredTogether() +{ + QetSettings::setSheetBackground(QetSettings::SheetBackground{QColor("#404040"), true}); + + const auto background = QetSettings::sheetBackground(); + QCOMPARE(background.color, QColor("#404040")); + QVERIFY(background.custom); +} + +void TstSheetBackgroundSetting::sameColourStillDistinguishesTheTwoModes() +{ + // The reported bug in miniature: white is white either way, yet one + // of the two must survive a restart as an explicit choice and the + // other as "follow the system". + QetSettings::setSheetBackground(QetSettings::SheetBackground{QColor("#ffffff"), true}); + QVERIFY(QetSettings::sheetBackground().custom); + + QetSettings::setSheetBackground(QetSettings::SheetBackground{QColor("#ffffff"), false}); + QVERIFY(!QetSettings::sheetBackground().custom); +} + +void TstSheetBackgroundSetting::alphaIsNotStored() +{ + // What the SVG export leaves on Diagram::background_color once it has + // asked for a transparent background. + QColor transparent(QStringLiteral("#12ab34")); + transparent.setAlpha(0); + QetSettings::setSheetBackground(QetSettings::SheetBackground{transparent, true}); + + const auto background = QetSettings::sheetBackground(); + QCOMPARE(background.color.name(), QStringLiteral("#12ab34")); + QCOMPARE(background.color.alpha(), 255); +} + +void TstSheetBackgroundSetting::unreadableColourFallsBackToWhite() +{ + // A hand-edited or truncated settings file must not blank the sheet. + QSettings settings; + settings.setValue(QStringLiteral("diagrameditor/sheet_background_color"), + QStringLiteral("not a colour")); + settings.sync(); + + const auto background = QetSettings::sheetBackground(); + QCOMPARE(background.color, QColor(Qt::white)); +} + +void TstSheetBackgroundSetting::recentColorsDefaultToEmpty() +{ + QVERIFY(QetSettings::sheetBackgroundRecentColors().isEmpty()); +} + +void TstSheetBackgroundSetting::recentColorsKeepTheirOrder() +{ + // Most recent first, as the menu lists them. + QetSettings::setSheetBackgroundRecentColors({QStringLiteral("#111111"), + QStringLiteral("#222222")}); + QCOMPARE(QetSettings::sheetBackgroundRecentColors(), + QStringList({QStringLiteral("#111111"), QStringLiteral("#222222")})); + + QetSettings::setSheetBackgroundRecentColors(QStringList()); + QVERIFY(QetSettings::sheetBackgroundRecentColors().isEmpty()); +} + +QTEST_MAIN(TstSheetBackgroundSetting) +#include "tst_sheetbackgroundsetting.moc"