Gui: Add window title

This commit is contained in:
Chris Hennes
2025-09-11 16:58:18 -05:00
parent 7949f8a172
commit 40bc9962d3
3 changed files with 171 additions and 110 deletions

View File

@@ -21,22 +21,22 @@
* *
**************************************************************************/
#include "PreCompiled.h"
#ifndef _PreComp_
#include <QDesktopServices>
#include <QDialog>
#include <QLocale>
#include <QMenu>
#include <QMessageBox>
#include <QOpenGLFunctions>
#include <QProcess>
#include <QThread>
#include <QTimer>
#include <QToolButton>
#include <QWindow>
#include <fstream>
#include <memory>
#include <set>
#include <string>
#include <ranges>
#endif
#include "DlgVersionMigrator.h"
#include "ui_DlgVersionMigrator.h"
@@ -51,56 +51,58 @@
using namespace Gui::Dialog;
namespace fs = std::filesystem;
std::set<std::string> getKnownVersions() {
auto splitCommas = [](const std::string &input) {
std::set<std::string> result;
std::stringstream ss(input);
std::string token;
while (std::getline(ss, token, ',')) {
result.insert(token);
}
return result;
bool isCurrentVersionKnown()
{
std::set<fs::path> paths = {
App::Application::directories()->getUserAppDataDir(),
App::Application::directories()->getUserConfigPath()
};
auto prefGroup = App::GetApplication().GetParameterGroupByPath(
"User parameter:BaseApp/Preferences/Migration");
// Split our comma-separated list of already-migrated-to version directories into a set for easy
// searching
std::string offeredToMigrateToVersionedConfig =
prefGroup->GetASCII("OfferedToMigrateToVersionedConfig", "");
std::set<std::string> knownVersions;
if (!offeredToMigrateToVersionedConfig.empty()) {
knownVersions = splitCommas(offeredToMigrateToVersionedConfig);
}
return knownVersions;
}
void setKnownVersions(const std::set<std::string> &knownVersions) {
auto joinCommas = [](const std::set<std::string> &s) {
std::ostringstream oss;
for (auto it = s.begin(); it != s.end(); ++it) {
if (it != s.begin()) {
oss << ',';
}
oss << *it;
}
return oss.str();
};
auto prefGroup = App::GetApplication().GetParameterGroupByPath(
"User parameter:BaseApp/Preferences/Migration");
prefGroup->SetASCII("OfferedToMigrateToVersionedConfig", joinCommas(knownVersions));
}
void markCurrentVersionAsKnown() {
int major = std::stoi(App::Application::Config()["BuildVersionMajor"]);
int minor = std::stoi(App::Application::Config()["BuildVersionMinor"]);
std::string currentVersionedDirName = App::ApplicationDirectories::versionStringForPath(major, minor);
std::set<std::string> knownVersions = getKnownVersions();
knownVersions.insert(currentVersionedDirName);
setKnownVersions(knownVersions);
for (auto &path : paths) {
if (App::Application::directories()->usingCurrentVersionConfig(path)) {
return true;
}
fs::path markerPath = path;
if (App::Application::directories()->isVersionedPath(path)) {
markerPath = path.parent_path();
}
markerPath /= currentVersionedDirName + ".do_not_migrate";
if (fs::exists(markerPath)) {
return true;
}
}
return false;
}
void markCurrentVersionAsDoNotMigrate() {
std::set<fs::path> paths = {
App::Application::directories()->getUserAppDataDir(),
App::Application::directories()->getUserConfigPath()
};
int major = std::stoi(App::Application::Config()["BuildVersionMajor"]);
int minor = std::stoi(App::Application::Config()["BuildVersionMinor"]);
std::string currentVersionedDirName = App::ApplicationDirectories::versionStringForPath(major, minor);
for (auto &path : paths) {
if (App::Application::directories()->usingCurrentVersionConfig(path)) {
// No action to take: the migration is done, so this call doesn't need to do anything
continue;
}
fs::path markerPath = path;
if (App::Application::directories()->isVersionedPath(path)) {
markerPath = path.parent_path();
}
markerPath /= currentVersionedDirName + ".do_not_migrate";
std::ofstream markerFile(markerPath);
if (!markerFile.is_open()) {
Base::Console().error("Unable to open marker file %s\n", Base::FileInfo::pathToString(markerPath).c_str());
continue;
}
markerFile << "Migration to version " << currentVersionedDirName << " was declined. "
<< "To request migration again, delete this file.";
markerFile.close();
}
}
@@ -123,21 +125,31 @@ DlgVersionMigrator::DlgVersionMigrator(MainWindow *mw) :
// NOTE: All rich-text strings are generated programmatically so that translators don't have to deal with the
// markup. The two strings in the middle of the dialog are set in the UI file.
auto welcomeString = QStringLiteral("<b>") + QObject::tr("Welcome to %1 %2.%3\n\n").arg(
programName, QString::number(major), QString::number(minor)) + QStringLiteral("</b>");
auto programNameString = tr("Welcome to %1 %2.%3\n\n").arg(
programName, QString::number(major), QString::number(minor));
auto welcomeString = QStringLiteral("<b>") + programNameString + QStringLiteral("</b>");
auto calculatingSizeString = QStringLiteral("<b>") + QObject::tr("Calculating size…") + QStringLiteral("</b>");
auto calculatingSizeString = QStringLiteral("<b>") + tr("Calculating size…") + QStringLiteral("</b>");
auto shareConfigurationString = QStringLiteral("<a href='#shareConfiguration'>") +
QObject::tr("Share configuration between versions") + QStringLiteral("</a>");
tr("Share configuration between versions") + QStringLiteral("</a>");
setWindowTitle(programNameString);
ui->welcomeLabel->setText(welcomeString);
ui->sizeLabel->setText(calculatingSizeString);
ui->shareLinkLabel->setText(shareConfigurationString);
ui->shareLinkLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
connect(ui->copyButton, &QPushButton::clicked, this, &DlgVersionMigrator::migrateToCurrentVersion);
connect(ui->shareLinkLabel, &QLabel::linkActivated, this, &DlgVersionMigrator::doNotMigrate);
connect(ui->copyButton, &QPushButton::clicked, this, &DlgVersionMigrator::migrate);
connect(ui->helpButton, &QPushButton::clicked, this, &DlgVersionMigrator::help);
// Set up the menu actions for the two hidden options
auto *menu = new QMenu(ui->menuButton);
QAction *share = menu->addAction(tr("Share configuration with previous version"));
QAction *reset = menu->addAction(tr("Use a new default configuration"));
ui->menuButton->setMenu(menu);
ui->menuButton->setPopupMode(QToolButton::InstantPopup);
ui->menuButton->setStyleSheet(QStringLiteral("QToolButton::menu-indicator { image: none; width: 0px; }"));
connect(share, &QAction::triggered, this, &DlgVersionMigrator::share);
connect(reset, &QAction::triggered, this, &DlgVersionMigrator::freshStart);
}
DlgVersionMigrator::~DlgVersionMigrator() = default;
@@ -147,14 +159,9 @@ int DlgVersionMigrator::exec() {
if (App::Application::directories()->usingCustomDirectories()) {
return 0;
}
std::set<std::string> knownVersions = getKnownVersions();
int major = std::stoi(App::Application::Config()["BuildVersionMajor"]);
int minor = std::stoi(App::Application::Config()["BuildVersionMinor"]);
std::string currentVersionedDirName = App::ApplicationDirectories::versionStringForPath(major, minor);
if (!knownVersions.contains(currentVersionedDirName) && !App::Application::directories()->usingCurrentVersionConfig(
App::Application::directories()->getUserAppDataDir())) {
if (!isCurrentVersionKnown()) {
calculateMigrationSize();
auto result = QDialog::exec();
QDialog::exec();
if (sizeCalculationWorkerThread && sizeCalculationWorkerThread->isRunning()) {
sizeCalculationWorkerThread->quit();
}
@@ -162,6 +169,7 @@ int DlgVersionMigrator::exec() {
return 0;
}
class DirectorySizeCalculationWorker : public QObject {
Q_OBJECT
@@ -240,10 +248,9 @@ void DlgVersionMigrator::calculateMigrationSize() {
sizeCalculationWorkerThread->start();
}
void DlgVersionMigrator::doNotMigrate(const QString &linkText)
void DlgVersionMigrator::share()
{
Q_UNUSED(linkText);
markCurrentVersionAsKnown();
markCurrentVersionAsDoNotMigrate();
if (sizeCalculationWorkerThread && sizeCalculationWorkerThread->isRunning()) {
sizeCalculationWorkerThread->quit();
}
@@ -259,10 +266,8 @@ void DlgVersionMigrator::showSizeOfMigration(uintmax_t size) {
sizeCalculationWorkerThread = nullptr; // Deleted via a previously-configured deleteLater()
}
void DlgVersionMigrator::migrateToCurrentVersion() {
void DlgVersionMigrator::migrate() {
hide();
auto oldKnownVersions = getKnownVersions();
markCurrentVersionAsKnown(); // This MUST be done before the migration, or it won't get remembered
auto *workerThread = new QThread(mainWindow);
auto *worker = new PathMigrationWorker();
worker->moveToThread(workerThread);
@@ -282,38 +287,79 @@ void DlgVersionMigrator::migrateToCurrentVersion() {
migrationRunning->exec();
if (migrationRunning->result() == QDialog::Accepted) {
App::GetApplication().GetUserParameter().SaveDocument(); // Flush to disk before restarting
auto *restarting = new QMessageBox(this);
restarting->setText(
QObject::tr("Migration complete. Restarting…"));
restarting->setWindowTitle(QObject::tr("Restarting"));
restarting->setStandardButtons(QMessageBox::NoButton);
auto closeNotice = [restarting]() {
restarting->reject();
};
// Insert a short delay before restart so the user can see the success message and
// knows it's a restart and not a crash...
constexpr int delayRestartMillis{2000};
QTimer::singleShot(delayRestartMillis, closeNotice);
restarting->exec();
connect(qApp, &QCoreApplication::aboutToQuit, [=] {
if (getMainWindow()->close()) {
auto args = QApplication::arguments();
args.removeFirst();
QProcess::startDetached(QApplication::applicationFilePath(),
args,
QApplication::applicationDirPath());
}
});
QCoreApplication::exit(0);
_exit(0); // No really. Die.
restart();
} else {
setKnownVersions(oldKnownVersions); // Reset, we didn't migrate after all
QMessageBox::critical(mainWindow, QObject::tr("Migration failed"),
QObject::tr("Migration failed. See the Report View for details."));
}
}
void DlgVersionMigrator::freshStart()
{
// Create the versioned directories, but don't put anything in them
std::set<fs::path> paths = {
App::Application::directories()->getUserAppDataDir(),
App::Application::directories()->getUserConfigPath()
};
int major = std::stoi(App::Application::Config()["BuildVersionMajor"]);
int minor = std::stoi(App::Application::Config()["BuildVersionMinor"]);
std::string currentVersionedDirName = App::ApplicationDirectories::versionStringForPath(major, minor);
for (auto &path : paths) {
if (App::Application::directories()->usingCurrentVersionConfig(path)) {
continue;
}
fs::path versionDir = path;
if (App::Application::directories()->isVersionedPath(path)) {
versionDir = path.parent_path();
}
versionDir /= currentVersionedDirName;
if (fs::exists(versionDir)) {
continue;
}
fs::create_directory(versionDir);
}
restart();
}
void DlgVersionMigrator::help()
{
auto helpPage = QStringLiteral("https://wiki.freecad.org/Version_migration");
QDesktopServices::openUrl(QUrl(helpPage));
}
void DlgVersionMigrator::restart()
{
App::GetApplication().GetUserParameter().SaveDocument(); // Flush to disk before restarting
auto *restarting = new QMessageBox(this);
restarting->setText(
QObject::tr("Migration complete. Restarting…"));
restarting->setWindowTitle(QObject::tr("Restarting"));
restarting->setStandardButtons(QMessageBox::NoButton);
auto closeNotice = [restarting]() {
restarting->reject();
};
// Insert a short delay before restart so the user can see the success message and
// knows it's a restart and not a crash...
constexpr int delayRestartMillis{2000};
QTimer::singleShot(delayRestartMillis, closeNotice);
restarting->exec();
connect(qApp, &QCoreApplication::aboutToQuit, [=] {
if (getMainWindow()->close()) {
auto args = QApplication::arguments();
args.removeFirst();
QProcess::startDetached(QApplication::applicationFilePath(),
args,
QApplication::applicationDirPath());
}
});
QCoreApplication::exit(0);
_exit(0); // No really. Die.
}
#include "DlgVersionMigrator.moc"

View File

@@ -51,13 +51,17 @@ namespace Gui {
void calculateMigrationSize(); // Async -> this starts the process and immediately returns
void showSizeOfMigration(uintmax_t size);
void migrateToCurrentVersion();
void doNotMigrate(const QString &linkText);
void migrate();
void share();
void freshStart();
void help();
private:
MainWindow* mainWindow;
QThread* sizeCalculationWorkerThread;
std::unique_ptr<class Ui_DlgVersionMigrator> ui;
void restart();
};
}

View File

@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Gui::Dialog::DlgVersionMigrator</class>
<widget class="QDialog" name="Gui::Dialog::DlgVersionMigrator">
<property name="windowModality">
@@ -10,7 +9,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>527</width>
<width>556</width>
<height>233</height>
</rect>
</property>
@@ -69,6 +68,13 @@
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="helpButton">
<property name="text">
<string>Help</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
@@ -83,20 +89,25 @@
</spacer>
</item>
<item>
<widget class="QLabel" name="shareLinkLabel">
<widget class="QPushButton" name="copyButton">
<property name="text">
<!--Don't translate this here, it's replaced in code-->
<string notr="true">Share configuration between versions</string>
<string>Copy Configuration (Recommended)</string>
</property>
<property name="textFormat">
<enum>Qt::AutoText</enum>
<property name="default">
<bool>true</bool>
</property>
<property name="flat">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="copyButton">
<widget class="QToolButton" name="menuButton">
<property name="text">
<string>Copy Configuration</string>
<string notr="true">⋮</string>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextOnly</enum>
</property>
</widget>
</item>