From cc3ab110eed1f9762e0f264f065c92ceccfba6c9 Mon Sep 17 00:00:00 2001 From: Victor Fuentes Date: Tue, 12 Apr 2022 23:38:51 -0400 Subject: [PATCH 01/33] [users] add fullname to global storage --- src/modules/users/Config.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/modules/users/Config.cpp b/src/modules/users/Config.cpp index cebe45452..eedfd274d 100644 --- a/src/modules/users/Config.cpp +++ b/src/modules/users/Config.cpp @@ -445,6 +445,15 @@ Config::setFullName( const QString& name ) if ( name != m_fullName ) { m_fullName = name; + Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage(); + if ( name.isEmpty() ) + { + gs->remove( "fullname" ); + } + else + { + gs->insert( "fullname", name ); + } emit fullNameChanged( name ); // Build login and hostname, if needed From b0837b1a98260898b717af5ec19a0c1f445c94e7 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Wed, 13 Apr 2022 14:40:00 +0200 Subject: [PATCH 02/33] Docs: reformat table of GS keys - add fullname key - make table a little easier-on-the-eyes in text mode While here, merge some 3.3 updates; just don't talk about PythonQt any more. --- src/modules/README.md | 49 ++++++++++++++++++------------------------- 1 file changed, 20 insertions(+), 29 deletions(-) diff --git a/src/modules/README.md b/src/modules/README.md index f18e7f6bd..e5bd2e7b6 100644 --- a/src/modules/README.md +++ b/src/modules/README.md @@ -58,10 +58,10 @@ Module descriptors for C++ modules **may** have the following key: Module descriptors for Python modules **must** have the following key: - *script* (the name of the Python script to load, nearly always `main.py`) -Module descriptors for process modules **must** have the following key: -- *command* (the command to run) +Module descriptors for process modules **must** have the following key: +- *command* (the command to run) -Module descriptors for process modules **may** have the following keys: +Module descriptors for process modules **may** have the following keys: - *timeout* (how long, in seconds, to wait for the command to run) - *chroot* (if true, run the command in the target system rather than the host) Note that process modules are not recommended. @@ -181,23 +181,25 @@ for determining the relative weights there. ## Global storage keys + Some modules place values in global storage so that they can be referenced later by other modules or even other parts of the same module. The following table represents a partial list of the values available as well as where they originate from and which module consume them. -Key|Source|Consumers|Description ----|---|---|--- -btrfsSubvolumes|mount|fstab|List of maps containing the mountpoint and btrtfs subvolume -btrfsRootSubvolume|mount|bootloader, luksopenswaphook|String containing the subvolume mounted at root -efiSystemPartition|partition|bootloader, fstab|String containing the path to the ESP relative to the installed system -extraMounts|mount|unpackfs|List of maps holding metadata for the temporary mountpoints used by the installer -hostname|users||A string containing the hostname of the new system -netinstallAdd|packagechooser|netinstall|Data to add to netinstall tree. Same format as netinstall.yaml -netinstallSelect|packagechooser|netinstall|List of group names to select in the netinstall tree -partitions|partition, rawfs|numerous modules|List of maps of metadata about each partition -rootMountPoint|mount|numerous modules|A string with the absolute path to the root mountpoint -username|users|networkcfg, plasmainf, preservefiles|A string containing the username of the new user -zfsDatasets|zfs|bootloader, grubcfg, mount|List of maps of zfs datasets including the name and mount information -zfsInfo|partition|mount, zfs|List of encrypted zfs partitions and the encription info -zfsPoolInfo|zfs|mount, umount|List of maps of zfs pool info including the name and mountpoint +Key |Source |Consumers|Description +------------------|----------------|---|--- +btrfsSubvolumes |mount |fstab|List of maps containing the mountpoint and btrtfs subvolume +btrfsRootSubvolume|mount |bootloader, luksopenswaphook|String containing the subvolume mounted at root +efiSystemPartition|partition |bootloader, fstab|String containing the path to the ESP relative to the installed system +extraMounts |mount |unpackfs|List of maps holding metadata for the temporary mountpoints used by the installer +fullname |users ||The full username (e.g. "Jane Q. Public") +hostname |users ||A string containing the hostname of the new system +netinstallAdd |packagechooser |netinstall|Data to add to netinstall tree. Same format as netinstall.yaml +netinstallSelect |packagechooser |netinstall|List of group names to select in the netinstall tree +partitions |partition, rawfs|numerous modules|List of maps of metadata about each partition +rootMountPoint |mount |numerous modules|A string with the absolute path to the root mountpoint +username |users |networkcfg, plasmainf, preservefiles|A string containing the username of the new user +zfsDatasets |zfs |bootloader, grubcfg, mount|List of maps of zfs datasets including the name and mount information +zfsInfo |partition |mount, zfs|List of encrypted zfs partitions and the encription info +zfsPoolInfo |zfs |mount, umount|List of maps of zfs pool info including the name and mountpoint ## C++ modules @@ -478,17 +480,6 @@ all be considered deprecated by the callback-enabled functions, above. All of the API functions for running commands set the environment LC_ALL and LANG to "C" for the called command. - -## PythonQt modules (deprecated) - -> Type: viewmodule, jobmodule -> Interface: pythonqt - -The PythonQt modules are deprecated and will be removed in Calamares 3.3. -Their documentation is also almost completely lacking. - - - ## Process modules Use of this kind of module is **not** recommended. From 69e00d181a00c375542f88e8d7fb32021575232f Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Wed, 13 Apr 2022 16:24:26 +0200 Subject: [PATCH 03/33] Changes: post-release housekeeping --- CHANGES-3.2 | 14 ++++++++++++++ CMakeLists.txt | 4 ++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/CHANGES-3.2 b/CHANGES-3.2 index 82a342ecb..ffc8e0010 100644 --- a/CHANGES-3.2 +++ b/CHANGES-3.2 @@ -8,6 +8,20 @@ contributors are listed. Note that Calamares does not have a historical changelog -- this log starts with version 3.2.0. The release notes on the website will have to do for older versions. +# 3.2.56 (unreleased) # + +This release contains contributions from (alphabetically by first name): + - Victor Fuentes (new contributor! Welcome!) + +## Core ## + - No core changes yet + +## Modules ## + - *users* module sets global storage key *fullname* to the full name + of the user (e.g. what is entered in the "your full name" box on the + users page). #1923 (Thanks Victor) + + # 3.2.55 (2022-04-11) # This release contains contributions from (alphabetically by first name): diff --git a/CMakeLists.txt b/CMakeLists.txt index 6f438d776..472c17fb0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,11 +41,11 @@ # TODO:3.3: Require CMake 3.12 cmake_minimum_required( VERSION 3.3 FATAL_ERROR ) project( CALAMARES - VERSION 3.2.55 + VERSION 3.2.56 LANGUAGES C CXX ) -set( CALAMARES_VERSION_RC 0 ) # Set to 0 during release cycle, 1 during development +set( CALAMARES_VERSION_RC 1 ) # Set to 0 during release cycle, 1 during development if( CALAMARES_VERSION_RC EQUAL 1 AND CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR ) message( FATAL_ERROR "Do not build development versions in the source-directory." ) endif() From f4117881cd17b66ecad95b8c92a59f65f60a5d8f Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Wed, 13 Apr 2022 16:45:32 +0200 Subject: [PATCH 04/33] [welcome] Add some more development-only checks --- .../welcome/checker/GeneralRequirements.cpp | 55 +++++++++++++++---- src/modules/welcome/welcome.conf | 11 ++++ 2 files changed, 55 insertions(+), 11 deletions(-) diff --git a/src/modules/welcome/checker/GeneralRequirements.cpp b/src/modules/welcome/checker/GeneralRequirements.cpp index ca7219ca4..b9e5db83d 100644 --- a/src/modules/welcome/checker/GeneralRequirements.cpp +++ b/src/modules/welcome/checker/GeneralRequirements.cpp @@ -15,6 +15,9 @@ #include "CheckerContainer.h" #include "partman_devices.h" +#include "CalamaresVersion.h" // For development-or-not +#include "GlobalStorage.h" +#include "JobQueue.h" #include "Settings.h" #include "modulesystem/Requirement.h" #include "network/Manager.h" @@ -26,9 +29,6 @@ #include "utils/Variant.h" #include "widgets/WaitingWidget.h" -#include "GlobalStorage.h" -#include "JobQueue.h" - #include #include #include @@ -152,10 +152,10 @@ GeneralRequirements::checkRequirements() { checkEntries.append( { entry, - [req = m_requiredStorageGiB] { return tr( "has at least %1 GiB available drive space" ).arg( req ); }, - [req = m_requiredStorageGiB] { - return tr( "There is not enough drive space. At least %1 GiB is required." ).arg( req ); - }, + [ req = m_requiredStorageGiB ] + { return tr( "has at least %1 GiB available drive space" ).arg( req ); }, + [ req = m_requiredStorageGiB ] + { return tr( "There is not enough drive space. At least %1 GiB is required." ).arg( req ); }, enoughStorage, m_entriesToRequire.contains( entry ) } ); } @@ -163,8 +163,8 @@ GeneralRequirements::checkRequirements() { checkEntries.append( { entry, - [req = m_requiredRamGiB] { return tr( "has at least %1 GiB working memory" ).arg( req ); }, - [req = m_requiredRamGiB] { + [ req = m_requiredRamGiB ] { return tr( "has at least %1 GiB working memory" ).arg( req ); }, + [ req = m_requiredRamGiB ] { return tr( "The system does not have enough working memory. At least %1 GiB is required." ) .arg( req ); }, @@ -191,7 +191,8 @@ GeneralRequirements::checkRequirements() { checkEntries.append( { entry, [] { return tr( "is running the installer as an administrator (root)" ); }, - [] { + [] + { return Calamares::Settings::instance()->isSetupMode() ? tr( "The setup program is not running with administrator rights." ) : tr( "The installer is not running with administrator rights." ); @@ -203,7 +204,8 @@ GeneralRequirements::checkRequirements() { checkEntries.append( { entry, [] { return tr( "has a screen large enough to show the whole installer" ); }, - [] { + [] + { return Calamares::Settings::instance()->isSetupMode() ? tr( "The screen is too small to display the setup program." ) : tr( "The screen is too small to display the installer." ); @@ -211,6 +213,37 @@ GeneralRequirements::checkRequirements() enoughScreen, false } ); } +#ifdef CALAMARES_VERSION_RC + if ( entry == "false" ) + { + checkEntries.append( { entry, + [] { return tr( "is always false" ); }, + [] { return tr( "is always false" ); }, + false, + m_entriesToRequire.contains( entry ) } ); + } + if ( entry == "true" ) + { + checkEntries.append( { entry, + [] { return tr( "is always true" ); }, + [] { return tr( "is always true" ); }, + true, + m_entriesToRequire.contains( entry ) } ); + } + if ( entry == "snark" ) + { + static unsigned int snark_count = 0; + checkEntries.append( { entry, + [] { return tr( "is checked three times." ); }, + [] + { + return tr( "The snark has not been checked three times.", + "The (some mythological beast) has not been checked three times." ); + }, + ++snark_count > 3, + m_entriesToRequire.contains( entry ) } ); + } +#endif } return checkEntries; } diff --git a/src/modules/welcome/welcome.conf b/src/modules/welcome/welcome.conf index 6e11817bf..b86231c3f 100644 --- a/src/modules/welcome/welcome.conf +++ b/src/modules/welcome/welcome.conf @@ -64,6 +64,14 @@ requirements: # the host system satisfying the condition. # # This sample file lists all the conditions that are known. + # + # Note that the last three checks are for testing-purposes only, + # and shouldn't be used in production (they are only available + # when building Calamares in development mode): + # - *false* is a check that is always false (unsatisfied) + # - *true* is a check that is always true (satisfied) + # - *snark* is a check that is only satisfied once it has been checked + # at least three times ("what I tell you three times is true"). check: - storage - ram @@ -71,6 +79,9 @@ requirements: - internet - root - screen + - false + - true + - snark # List conditions that **must** be satisfied (from the list # of conditions, above) for installation to proceed. # If any of these conditions are not met, the user cannot From cbd4bd9bbe57de0cc7a894c20947bede8146fa05 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Wed, 13 Apr 2022 16:47:54 +0200 Subject: [PATCH 05/33] [welcome] Factor out is-this-check-required While here, make it possible for the "screen" (screen-size) check to be mandatory; there's no reason it shouldn't follow the same logic as all the others (although denying users an install because they have a VGA monitor seems a bit weak). --- .../welcome/checker/GeneralRequirements.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/modules/welcome/checker/GeneralRequirements.cpp b/src/modules/welcome/checker/GeneralRequirements.cpp index b9e5db83d..6233eae75 100644 --- a/src/modules/welcome/checker/GeneralRequirements.cpp +++ b/src/modules/welcome/checker/GeneralRequirements.cpp @@ -148,6 +148,7 @@ GeneralRequirements::checkRequirements() Calamares::RequirementsList checkEntries; foreach ( const QString& entry, m_entriesToCheck ) { + const bool required = m_entriesToRequire.contains( entry ); if ( entry == "storage" ) { checkEntries.append( @@ -157,7 +158,7 @@ GeneralRequirements::checkRequirements() [ req = m_requiredStorageGiB ] { return tr( "There is not enough drive space. At least %1 GiB is required." ).arg( req ); }, enoughStorage, - m_entriesToRequire.contains( entry ) } ); + required } ); } else if ( entry == "ram" ) { @@ -169,7 +170,7 @@ GeneralRequirements::checkRequirements() .arg( req ); }, enoughRam, - m_entriesToRequire.contains( entry ) } ); + required } ); } else if ( entry == "power" ) { @@ -177,7 +178,7 @@ GeneralRequirements::checkRequirements() [] { return tr( "is plugged in to a power source" ); }, [] { return tr( "The system is not plugged in to a power source." ); }, hasPower, - m_entriesToRequire.contains( entry ) } ); + required } ); } else if ( entry == "internet" ) { @@ -185,7 +186,7 @@ GeneralRequirements::checkRequirements() [] { return tr( "is connected to the Internet" ); }, [] { return tr( "The system is not connected to the Internet." ); }, hasInternet, - m_entriesToRequire.contains( entry ) } ); + required } ); } else if ( entry == "root" ) { @@ -198,7 +199,7 @@ GeneralRequirements::checkRequirements() : tr( "The installer is not running with administrator rights." ); }, isRoot, - m_entriesToRequire.contains( entry ) } ); + required } ); } else if ( entry == "screen" ) { @@ -211,7 +212,7 @@ GeneralRequirements::checkRequirements() : tr( "The screen is too small to display the installer." ); }, enoughScreen, - false } ); + required } ); } #ifdef CALAMARES_VERSION_RC if ( entry == "false" ) @@ -220,7 +221,7 @@ GeneralRequirements::checkRequirements() [] { return tr( "is always false" ); }, [] { return tr( "is always false" ); }, false, - m_entriesToRequire.contains( entry ) } ); + required } ); } if ( entry == "true" ) { @@ -228,7 +229,7 @@ GeneralRequirements::checkRequirements() [] { return tr( "is always true" ); }, [] { return tr( "is always true" ); }, true, - m_entriesToRequire.contains( entry ) } ); + required } ); } if ( entry == "snark" ) { @@ -241,7 +242,7 @@ GeneralRequirements::checkRequirements() "The (some mythological beast) has not been checked three times." ); }, ++snark_count > 3, - m_entriesToRequire.contains( entry ) } ); + required } ); } #endif } From 8f5c3fa3026b3cb6ea0c68e43a1854241e0618df Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Wed, 13 Apr 2022 16:51:21 +0200 Subject: [PATCH 06/33] [welcome] Improve messages a little for bogus-checks. Follow the guidelines for checker-messages, even though these should never be displayed to real users. --- src/modules/welcome/checker/GeneralRequirements.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/welcome/checker/GeneralRequirements.cpp b/src/modules/welcome/checker/GeneralRequirements.cpp index 6233eae75..7780b8753 100644 --- a/src/modules/welcome/checker/GeneralRequirements.cpp +++ b/src/modules/welcome/checker/GeneralRequirements.cpp @@ -219,7 +219,7 @@ GeneralRequirements::checkRequirements() { checkEntries.append( { entry, [] { return tr( "is always false" ); }, - [] { return tr( "is always false" ); }, + [] { return tr( "The computer says no." ); }, false, required } ); } @@ -227,7 +227,7 @@ GeneralRequirements::checkRequirements() { checkEntries.append( { entry, [] { return tr( "is always true" ); }, - [] { return tr( "is always true" ); }, + [] { return tr( "The computer says yes." ); }, true, required } ); } From a9e098b0b4162786ba59afbe92c61f8c2fe8b91c Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Wed, 13 Apr 2022 17:08:17 +0200 Subject: [PATCH 07/33] [libcalamares] Allow resetting the requirements model - When a requirements checker starts, it can reset the model to clear it of previous entries. --- .../modulesystem/RequirementsChecker.cpp | 26 +++++++++++-------- .../modulesystem/RequirementsModel.cpp | 10 +++++++ .../modulesystem/RequirementsModel.h | 4 +++ 3 files changed, 29 insertions(+), 11 deletions(-) diff --git a/src/libcalamares/modulesystem/RequirementsChecker.cpp b/src/libcalamares/modulesystem/RequirementsChecker.cpp index 32c7254de..d0d6e74fe 100644 --- a/src/libcalamares/modulesystem/RequirementsChecker.cpp +++ b/src/libcalamares/modulesystem/RequirementsChecker.cpp @@ -32,6 +32,7 @@ RequirementsChecker::RequirementsChecker( QVector< Module* > modules, Requiremen , m_progressTimer( nullptr ) , m_progressTimeouts( 0 ) { + m_model->clear(); m_watchers.reserve( m_modules.count() ); connect( this, &RequirementsChecker::requirementsProgress, model, &RequirementsModel::setProgressMessage ); } @@ -63,9 +64,9 @@ RequirementsChecker::finished() static QMutex finishedMutex; QMutexLocker lock( &finishedMutex ); - if ( m_progressTimer && std::all_of( m_watchers.cbegin(), m_watchers.cend(), []( const Watcher* w ) { - return w && w->isFinished(); - } ) ) + if ( m_progressTimer + && std::all_of( + m_watchers.cbegin(), m_watchers.cend(), []( const Watcher* w ) { return w && w->isFinished(); } ) ) { cDebug() << "All requirements have been checked."; if ( m_progressTimer ) @@ -100,14 +101,17 @@ RequirementsChecker::reportProgress() m_progressTimeouts++; QStringList remainingNames; - auto remaining = std::count_if( m_watchers.cbegin(), m_watchers.cend(), [&]( const Watcher* w ) { - if ( w && !w->isFinished() ) - { - remainingNames << w->objectName(); - return true; - } - return false; - } ); + auto remaining = std::count_if( m_watchers.cbegin(), + m_watchers.cend(), + [ & ]( const Watcher* w ) + { + if ( w && !w->isFinished() ) + { + remainingNames << w->objectName(); + return true; + } + return false; + } ); if ( remaining > 0 ) { cDebug() << "Remaining modules:" << remaining << Logger::DebugList( remainingNames ); diff --git a/src/libcalamares/modulesystem/RequirementsModel.cpp b/src/libcalamares/modulesystem/RequirementsModel.cpp index 6a7e0a5b4..f21f7051c 100644 --- a/src/libcalamares/modulesystem/RequirementsModel.cpp +++ b/src/libcalamares/modulesystem/RequirementsModel.cpp @@ -15,6 +15,16 @@ namespace Calamares { +void +RequirementsModel::clear() +{ + QMutexLocker l( &m_addLock ); + emit beginResetModel(); + m_requirements.clear(); + changeRequirementsList(); + emit endResetModel(); +} + void RequirementsModel::addRequirementsList( const Calamares::RequirementsList& requirements ) { diff --git a/src/libcalamares/modulesystem/RequirementsModel.h b/src/libcalamares/modulesystem/RequirementsModel.h index 5f3e13cbb..d1842760c 100644 --- a/src/libcalamares/modulesystem/RequirementsModel.h +++ b/src/libcalamares/modulesystem/RequirementsModel.h @@ -77,6 +77,10 @@ signals: protected: QHash< int, QByteArray > roleNames() const override; + + ///@brief Clears the requirements; resets the model + void clear(); + ///@brief Append some requirements; resets the model void addRequirementsList( const Calamares::RequirementsList& requirements ); From 6348309936a1b1ffbe3247de55131925c51166ae Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Wed, 13 Apr 2022 17:09:43 +0200 Subject: [PATCH 08/33] [libcalamaresui] If requirements are not satisfied, recheck in 5 seconds --- src/libcalamaresui/modulesystem/ModuleManager.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/libcalamaresui/modulesystem/ModuleManager.cpp b/src/libcalamaresui/modulesystem/ModuleManager.cpp index 1e397b340..1233b1115 100644 --- a/src/libcalamaresui/modulesystem/ModuleManager.cpp +++ b/src/libcalamaresui/modulesystem/ModuleManager.cpp @@ -349,7 +349,18 @@ ModuleManager::checkRequirements() connect( rq, &RequirementsChecker::done, this, - [ = ]() { this->requirementsComplete( m_requirementsModel->satisfiedMandatory() ); } ); + [ = ]() + { + if ( m_requirementsModel->satisfiedMandatory() ) + { + /* we're done */ this->requirementsComplete( true ); + } + else + { + this->requirementsComplete( false ); + QTimer::singleShot( std::chrono::seconds( 5 ), this, &ModuleManager::checkRequirements ); + } + } ); QTimer::singleShot( 0, rq, &RequirementsChecker::run ); } From ee925492d4dc829316c52be97bdbe941c130dd99 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Wed, 13 Apr 2022 17:12:25 +0200 Subject: [PATCH 09/33] [welcome] Don't duplicate widgets --- .../welcome/checker/CheckerContainer.cpp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/modules/welcome/checker/CheckerContainer.cpp b/src/modules/welcome/checker/CheckerContainer.cpp index 23055c2e0..dd5a6680f 100644 --- a/src/modules/welcome/checker/CheckerContainer.cpp +++ b/src/modules/welcome/checker/CheckerContainer.cpp @@ -65,13 +65,18 @@ CheckerContainer::requirementsComplete( bool ok ) } } - layout()->removeWidget( m_waitingWidget ); - m_waitingWidget->deleteLater(); - m_waitingWidget = nullptr; // Don't delete in destructor - - m_checkerWidget = new ResultsListWidget( m_config, this ); - m_checkerWidget->setObjectName( "requirementsChecker" ); - layout()->addWidget( m_checkerWidget ); + if ( m_waitingWidget ) + { + layout()->removeWidget( m_waitingWidget ); + m_waitingWidget->deleteLater(); + m_waitingWidget = nullptr; // Don't delete in destructor + } + if ( !m_checkerWidget ) + { + m_checkerWidget = new ResultsListWidget( m_config, this ); + m_checkerWidget->setObjectName( "requirementsChecker" ); + layout()->addWidget( m_checkerWidget ); + } m_verdict = ok; } From fc653adcc0f699828228eeb8faf4044d6f4402b8 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Wed, 13 Apr 2022 18:15:21 +0200 Subject: [PATCH 10/33] [welcome] Don't duplicate result widgets --- .../welcome/checker/ResultsListWidget.cpp | 152 +++++++++++------- .../welcome/checker/ResultsListWidget.h | 14 ++ 2 files changed, 104 insertions(+), 62 deletions(-) diff --git a/src/modules/welcome/checker/ResultsListWidget.cpp b/src/modules/welcome/checker/ResultsListWidget.cpp index acbb48e42..57e2eb4ae 100644 --- a/src/modules/welcome/checker/ResultsListWidget.cpp +++ b/src/modules/welcome/checker/ResultsListWidget.cpp @@ -110,10 +110,11 @@ ResultsListDialog::ResultsListDialog( const Calamares::RequirementsModel& model, m_title = new QLabel( this ); m_title->setObjectName( "resultDialogTitle" ); - createResultWidgets( - entriesLayout, m_resultWidgets, model, []( const Calamares::RequirementsModel& m, QModelIndex i ) { - return m.data( i, Calamares::RequirementsModel::HasDetails ).toBool(); - } ); + createResultWidgets( entriesLayout, + m_resultWidgets, + model, + []( const Calamares::RequirementsModel& m, QModelIndex i ) + { return m.data( i, Calamares::RequirementsModel::HasDetails ).toBool(); } ); QDialogButtonBox* buttonBox = new QDialogButtonBox( QDialogButtonBox::Close, Qt::Horizontal, this ); buttonBox->setObjectName( "resultDialogButtons" ); @@ -154,75 +155,35 @@ ResultsListWidget::ResultsListWidget( Config* config, QWidget* parent ) { setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); - QBoxLayout* mainLayout = new QVBoxLayout; - QBoxLayout* entriesLayout = new QVBoxLayout; + m_mainLayout = new QVBoxLayout; + m_entriesLayout = new QVBoxLayout; - setLayout( mainLayout ); + setLayout( m_mainLayout ); int paddingSize = qBound( 32, CalamaresUtils::defaultFontHeight() * 4, 128 ); QHBoxLayout* spacerLayout = new QHBoxLayout; - mainLayout->addLayout( spacerLayout ); + m_mainLayout->addLayout( spacerLayout ); spacerLayout->addSpacing( paddingSize ); - spacerLayout->addLayout( entriesLayout ); + spacerLayout->addLayout( m_entriesLayout ); spacerLayout->addSpacing( paddingSize ); CalamaresUtils::unmarginLayout( spacerLayout ); - auto* explanation = new QLabel( m_config->warningMessage() ); - explanation->setWordWrap( true ); - explanation->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred ); - explanation->setOpenExternalLinks( false ); - explanation->setObjectName( "resultsExplanation" ); - entriesLayout->addWidget( explanation ); + m_explanation = new QLabel( m_config->warningMessage() ); + m_explanation->setWordWrap( true ); + m_explanation->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred ); + m_explanation->setOpenExternalLinks( false ); + m_explanation->setObjectName( "resultsExplanation" ); + m_entriesLayout->addWidget( m_explanation ); - connect( config, &Config::warningMessageChanged, explanation, &QLabel::setText ); - connect( explanation, &QLabel::linkActivated, this, &ResultsListWidget::linkClicked ); + requirementsChanged(); - // Check that all are satisfied (gives warnings if not) and - // all *mandatory* entries are satisfied (gives errors if not). - - const bool requirementsSatisfied = config->requirementsModel()->satisfiedRequirements(); - auto isUnSatisfied = []( const Calamares::RequirementsModel& m, QModelIndex i ) { - return !m.data( i, Calamares::RequirementsModel::Satisfied ).toBool(); - }; - - createResultWidgets( entriesLayout, m_resultWidgets, *( config->requirementsModel() ), isUnSatisfied ); - - if ( !requirementsSatisfied ) - { - entriesLayout->insertSpacing( 1, CalamaresUtils::defaultFontHeight() / 2 ); - mainLayout->addStretch(); - } - else - { - if ( !Calamares::Branding::instance()->imagePath( Calamares::Branding::ProductWelcome ).isEmpty() ) - { - QPixmap theImage - = QPixmap( Calamares::Branding::instance()->imagePath( Calamares::Branding::ProductWelcome ) ); - if ( !theImage.isNull() ) - { - QLabel* imageLabel; - if ( Calamares::Branding::instance()->welcomeExpandingLogo() ) - { - FixedAspectRatioLabel* p = new FixedAspectRatioLabel; - p->setPixmap( theImage ); - imageLabel = p; - } - else - { - imageLabel = new QLabel; - imageLabel->setPixmap( theImage ); - } - - imageLabel->setContentsMargins( 4, CalamaresUtils::defaultFontHeight() * 3 / 4, 4, 4 ); - imageLabel->setAlignment( Qt::AlignCenter ); - imageLabel->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); - imageLabel->setObjectName( "welcomeLogo" ); - mainLayout->addWidget( imageLabel ); - } - } - explanation->setAlignment( Qt::AlignCenter ); - } + connect( config, &Config::warningMessageChanged, m_explanation, &QLabel::setText ); + connect( m_explanation, &QLabel::linkActivated, this, &ResultsListWidget::linkClicked ); + connect( config->requirementsModel(), + &Calamares::RequirementsModel::modelReset, + this, + &ResultsListWidget::requirementsChanged ); CALAMARES_RETRANSLATE_SLOT( &ResultsListWidget::retranslate ); } @@ -253,6 +214,73 @@ ResultsListWidget::retranslate() } } +void +ResultsListWidget::requirementsChanged() +{ + if ( m_config->requirementsModel()->count() < m_requirementsSeen ) + { + return; + } + m_requirementsSeen = m_config->requirementsModel()->count(); + + // Check that all are satisfied (gives warnings if not) and + // all *mandatory* entries are satisfied (gives errors if not). + + const bool requirementsSatisfied = m_config->requirementsModel()->satisfiedRequirements(); + auto isUnSatisfied = []( const Calamares::RequirementsModel& m, QModelIndex i ) + { return !m.data( i, Calamares::RequirementsModel::Satisfied ).toBool(); }; + + + std::for_each( m_resultWidgets.begin(), + m_resultWidgets.end(), + []( QWidget* w ) + { + if ( w ) + { + w->deleteLater(); + } + } ); + createResultWidgets( m_entriesLayout, m_resultWidgets, *( m_config->requirementsModel() ), isUnSatisfied ); + + if ( !requirementsSatisfied ) + { + m_entriesLayout->insertSpacing( 1, CalamaresUtils::defaultFontHeight() / 2 ); + m_mainLayout->addStretch(); + } + else + { + if ( !Calamares::Branding::instance()->imagePath( Calamares::Branding::ProductWelcome ).isEmpty() ) + { + QPixmap theImage + = QPixmap( Calamares::Branding::instance()->imagePath( Calamares::Branding::ProductWelcome ) ); + if ( !theImage.isNull() ) + { + QLabel* imageLabel; + if ( Calamares::Branding::instance()->welcomeExpandingLogo() ) + { + FixedAspectRatioLabel* p = new FixedAspectRatioLabel; + p->setPixmap( theImage ); + imageLabel = p; + } + else + { + imageLabel = new QLabel; + imageLabel->setPixmap( theImage ); + } + + imageLabel->setContentsMargins( 4, CalamaresUtils::defaultFontHeight() * 3 / 4, 4, 4 ); + imageLabel->setAlignment( Qt::AlignCenter ); + imageLabel->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); + imageLabel->setObjectName( "welcomeLogo" ); + m_mainLayout->addWidget( imageLabel ); + } + } + m_explanation->setAlignment( Qt::AlignCenter ); + } + + retranslate(); +} + #include "utils/moc-warnings.h" #include "ResultsListWidget.moc" diff --git a/src/modules/welcome/checker/ResultsListWidget.h b/src/modules/welcome/checker/ResultsListWidget.h index 5e96b74a0..e5e64052f 100644 --- a/src/modules/welcome/checker/ResultsListWidget.h +++ b/src/modules/welcome/checker/ResultsListWidget.h @@ -17,6 +17,7 @@ #include +class QBoxLayout; class QLabel; class ResultsListWidget : public QWidget { @@ -27,10 +28,23 @@ public: private: /// @brief A link in the explanatory text has been clicked void linkClicked( const QString& link ); + /// @brief The model of requirements changed + void requirementsChanged(); + void retranslate(); QList< ResultWidget* > m_resultWidgets; ///< One widget for each unsatisfied entry Config* m_config = nullptr; + + // UI parts, which need updating when the model changes + QLabel* m_explanation = nullptr; + QBoxLayout* m_mainLayout = nullptr; + QBoxLayout* m_entriesLayout = nullptr; + // We count how many requirements we have seen; since the model + // does not shrink, we can avoid reacting to model-is-cleared + // events because the size of the model is then (briefly) smaller + // than what we expect. + int m_requirementsSeen = 0; }; #endif // CHECKER_RESULTSLISTWIDGET_H From 11d7870d686e2778544c45ca70023742f493c946 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Thu, 14 Apr 2022 21:48:55 +0200 Subject: [PATCH 11/33] [welcome] Factor out check for a filled requirements-model --- .../welcome/checker/ResultsListWidget.cpp | 15 ++++++++++-- .../welcome/checker/ResultsListWidget.h | 24 +++++++++++++++---- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/src/modules/welcome/checker/ResultsListWidget.cpp b/src/modules/welcome/checker/ResultsListWidget.cpp index 57e2eb4ae..6d4465f81 100644 --- a/src/modules/welcome/checker/ResultsListWidget.cpp +++ b/src/modules/welcome/checker/ResultsListWidget.cpp @@ -217,11 +217,10 @@ ResultsListWidget::retranslate() void ResultsListWidget::requirementsChanged() { - if ( m_config->requirementsModel()->count() < m_requirementsSeen ) + if ( !isModelFilled() ) { return; } - m_requirementsSeen = m_config->requirementsModel()->count(); // Check that all are satisfied (gives warnings if not) and // all *mandatory* entries are satisfied (gives errors if not). @@ -281,6 +280,18 @@ ResultsListWidget::requirementsChanged() retranslate(); } +bool +ResultsListWidget::isModelFilled() +{ + if ( m_config->requirementsModel()->count() < m_requirementsSeen ) + { + return false; + } + m_requirementsSeen = m_config->requirementsModel()->count(); + return true; +} + + #include "utils/moc-warnings.h" #include "ResultsListWidget.moc" diff --git a/src/modules/welcome/checker/ResultsListWidget.h b/src/modules/welcome/checker/ResultsListWidget.h index e5e64052f..f4f23293b 100644 --- a/src/modules/welcome/checker/ResultsListWidget.h +++ b/src/modules/welcome/checker/ResultsListWidget.h @@ -33,17 +33,31 @@ private: void retranslate(); - QList< ResultWidget* > m_resultWidgets; ///< One widget for each unsatisfied entry + /** @brief The model can be reset and re-filled, is it full yet? + * + * We count how many requirements we have seen; since the model + * does not shrink, we can avoid reacting to model-is-cleared + * events because the size of the model is then (briefly) smaller + * than what we expect. + * + * Returns true if the model contains at least m_requirementsSeen + * elements, and updates m_requirementsSeen. (Which is why the + * method is not const) + */ + bool isModelFilled(); + + /** @brief A list of widgets, one per entry in the requirements model + * + * Unsatisfied entries have a non-null widget pointer, while requirements + * entries that **are** satisfied have no widget. + */ + QList< ResultWidget* > m_resultWidgets; Config* m_config = nullptr; // UI parts, which need updating when the model changes QLabel* m_explanation = nullptr; QBoxLayout* m_mainLayout = nullptr; QBoxLayout* m_entriesLayout = nullptr; - // We count how many requirements we have seen; since the model - // does not shrink, we can avoid reacting to model-is-cleared - // events because the size of the model is then (briefly) smaller - // than what we expect. int m_requirementsSeen = 0; }; From e79f29dc0ef3ff185439f2b148f7dabfce7454ac Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Thu, 14 Apr 2022 21:51:43 +0200 Subject: [PATCH 12/33] [welcome] Update the explanation only when all the results are in. --- src/modules/welcome/checker/ResultsListWidget.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/modules/welcome/checker/ResultsListWidget.cpp b/src/modules/welcome/checker/ResultsListWidget.cpp index 6d4465f81..4a3280622 100644 --- a/src/modules/welcome/checker/ResultsListWidget.cpp +++ b/src/modules/welcome/checker/ResultsListWidget.cpp @@ -178,7 +178,15 @@ ResultsListWidget::ResultsListWidget( Config* config, QWidget* parent ) requirementsChanged(); - connect( config, &Config::warningMessageChanged, m_explanation, &QLabel::setText ); + connect( config, + &Config::warningMessageChanged, + [ = ]( QString s ) + { + if ( isModelFilled() ) + { + m_explanation->setText( s ); + } + } ); connect( m_explanation, &QLabel::linkActivated, this, &ResultsListWidget::linkClicked ); connect( config->requirementsModel(), &Calamares::RequirementsModel::modelReset, From fcb893cd12f4125d35bb0e0de6cdf1b5b9135993 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Thu, 14 Apr 2022 22:06:36 +0200 Subject: [PATCH 13/33] [welcome] Avoid growing more spacers Insert spacers only once; avoid crashing when all the requirements are satisfied immediately. --- src/modules/welcome/checker/ResultsListWidget.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/modules/welcome/checker/ResultsListWidget.cpp b/src/modules/welcome/checker/ResultsListWidget.cpp index 4a3280622..eb0c624c8 100644 --- a/src/modules/welcome/checker/ResultsListWidget.cpp +++ b/src/modules/welcome/checker/ResultsListWidget.cpp @@ -175,6 +175,8 @@ ResultsListWidget::ResultsListWidget( Config* config, QWidget* parent ) m_explanation->setOpenExternalLinks( false ); m_explanation->setObjectName( "resultsExplanation" ); m_entriesLayout->addWidget( m_explanation ); + m_entriesLayout->insertSpacing( 1, CalamaresUtils::defaultFontHeight() / 2 ); + m_mainLayout->addStretch(); requirementsChanged(); @@ -212,7 +214,9 @@ void ResultsListWidget::retranslate() { const auto& model = *( m_config->requirementsModel() ); - for ( auto i = 0; i < model.count(); i++ ) + // Retranslate the widgets that there **are**; + // these remain in-order relative to the model. + for ( auto i = 0; i < model.count() && i < m_resultWidgets.count(); i++ ) { if ( m_resultWidgets[ i ] ) { @@ -247,12 +251,10 @@ ResultsListWidget::requirementsChanged() w->deleteLater(); } } ); - createResultWidgets( m_entriesLayout, m_resultWidgets, *( m_config->requirementsModel() ), isUnSatisfied ); if ( !requirementsSatisfied ) { - m_entriesLayout->insertSpacing( 1, CalamaresUtils::defaultFontHeight() / 2 ); - m_mainLayout->addStretch(); + createResultWidgets( m_entriesLayout, m_resultWidgets, *( m_config->requirementsModel() ), isUnSatisfied ); } else { From 9a3d9feb308d734b4fcbfa4438211433d42331dd Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Fri, 15 Apr 2022 12:13:16 +0200 Subject: [PATCH 14/33] [libcalamaresui] Add a countdown widget for limited waiting. --- src/libcalamaresui/widgets/WaitingWidget.cpp | 91 ++++++++++++++++++++ src/libcalamaresui/widgets/WaitingWidget.h | 49 ++++++++++- 2 files changed, 139 insertions(+), 1 deletion(-) diff --git a/src/libcalamaresui/widgets/WaitingWidget.cpp b/src/libcalamaresui/widgets/WaitingWidget.cpp index aef5aecf5..671f140c5 100644 --- a/src/libcalamaresui/widgets/WaitingWidget.cpp +++ b/src/libcalamaresui/widgets/WaitingWidget.cpp @@ -16,6 +16,7 @@ #include #include +#include WaitingWidget::WaitingWidget( const QString& text, QWidget* parent ) : QWidget( parent ) @@ -55,3 +56,93 @@ WaitingWidget::setText( const QString& text ) { m_waitingLabel->setText( text ); } + +struct CountdownWaitingWidget::Private +{ + std::chrono::seconds duration; + // int because we count down, need to be able to show a 0, + // and then wrap around to duration a second later. + int count = 0; + WaitingSpinnerWidget* spinner = nullptr; + QLabel* label = nullptr; + QTimer* timer = nullptr; + + Private( std::chrono::seconds seconds, QWidget* parent ) + : duration( seconds ) + , spinner( new WaitingSpinnerWidget( parent ) ) + , label( new QLabel( parent ) ) + , timer( new QTimer( parent ) ) + { + } +}; + +CountdownWaitingWidget::CountdownWaitingWidget( std::chrono::seconds duration, QWidget* parent ) + : QWidget( parent ) + , d( std::make_unique< Private >( duration, this ) ) +{ + // Set up the label first for sizing + const int labelHeight = d->label->fontMetrics().height() * 3 / 2; + + // Set up the spinner + d->spinner->setFixedSize( labelHeight, labelHeight ); + + // Overall UI layout + QBoxLayout* box = new QHBoxLayout; + box->addWidget( d->spinner ); + box->addWidget( d->label ); + setLayout( box ); + + // Last because it updates the text + setInterval( duration ); + + d->timer->setInterval( std::chrono::seconds( 1 ) ); + connect( d->timer, &QTimer::timeout, this, &CountdownWaitingWidget::tick ); +} + +CountdownWaitingWidget::~CountdownWaitingWidget() +{ + d->timer->stop(); +} + +void +CountdownWaitingWidget::setInterval( std::chrono::seconds duration ) +{ + d->duration = duration; + d->count = int( duration.count() ); + tick(); +} + +void +CountdownWaitingWidget::start() +{ + // start it from the top + if ( d->count <= 0 ) + { + d->count = int( d->duration.count() ); + tick(); + } + d->timer->start(); +} + +void +CountdownWaitingWidget::stop() +{ + d->timer->stop(); +} + +void +CountdownWaitingWidget::tick() +{ + // We do want to **display** a 0 which is why we wrap around only + // after counting down from 0. + d->count--; + if ( d->count < 0 ) + { + d->count = int( d->duration.count() ); + } + d->label->setText( QString::number( d->count ) ); + if ( d->count == 0 ) + { + timeout(); + } +} diff --git a/src/libcalamaresui/widgets/WaitingWidget.h b/src/libcalamaresui/widgets/WaitingWidget.h index 850b81ca9..867529641 100644 --- a/src/libcalamaresui/widgets/WaitingWidget.h +++ b/src/libcalamaresui/widgets/WaitingWidget.h @@ -12,18 +12,65 @@ #include -class QLabel; +#include +#include +class QLabel; +class QTimer; + +/** @brief A spinner and a label below it + * + * The spinner has a fixed size of 4* the font height, + * and the text is displayed centered below it. Use this + * to display a long-term waiting situation with a status report. + */ class WaitingWidget : public QWidget { Q_OBJECT public: + /// Create a WaitingWidget with initial @p text label. explicit WaitingWidget( const QString& text, QWidget* parent = nullptr ); + /// Update the @p text displayed in the label. void setText( const QString& text ); private: QLabel* m_waitingLabel; }; +/** @brief A spinner and a countdown next to it + * + * The spinner is sized to the text-height and displays a + * numeric countdown next to it. The countdown is updated + * every second. The signal timeout() is sent every time + * the countdown reaches 0. + */ +class CountdownWaitingWidget : public QWidget +{ + Q_OBJECT +public: + /// Create a countdown widget with a given @p duration + explicit CountdownWaitingWidget( std::chrono::seconds duration = std::chrono::seconds( 5 ), + QWidget* parent = nullptr ); + ~CountdownWaitingWidget() override; + + /// Changes the duration used and resets the countdown + void setInterval( std::chrono::seconds duration ); + + /// Start the countdown, resets to the full duration + void start(); + /// Stop the countdown + void stop(); + +Q_SIGNALS: + void timeout(); + +protected Q_SLOTS: + void tick(); + +private: + struct Private; + std::unique_ptr< Private > d; +}; + #endif // WAITINGWIDGET_H From 514b7284492fdadb13bfeb78393b405343450625 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Fri, 15 Apr 2022 12:41:29 +0200 Subject: [PATCH 15/33] [libcalamaresui] Tweak widget display for countdown --- src/libcalamaresui/widgets/WaitingWidget.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/libcalamaresui/widgets/WaitingWidget.cpp b/src/libcalamaresui/widgets/WaitingWidget.cpp index 671f140c5..0e32f4a92 100644 --- a/src/libcalamaresui/widgets/WaitingWidget.cpp +++ b/src/libcalamaresui/widgets/WaitingWidget.cpp @@ -81,10 +81,14 @@ CountdownWaitingWidget::CountdownWaitingWidget( std::chrono::seconds duration, Q , d( std::make_unique< Private >( duration, this ) ) { // Set up the label first for sizing - const int labelHeight = d->label->fontMetrics().height() * 3 / 2; + const int labelHeight = qBound( 16, d->label->fontMetrics().height() * 3 / 2, 64 ); // Set up the spinner d->spinner->setFixedSize( labelHeight, labelHeight ); + d->spinner->setRevolutionsPerSecond( 1 ); + d->spinner->setInnerRadius( labelHeight / 2 ); + d->spinner->setLineLength( labelHeight / 2 ); + d->spinner->setLineWidth( labelHeight / 8 ); // Overall UI layout QBoxLayout* box = new QHBoxLayout; @@ -122,12 +126,14 @@ CountdownWaitingWidget::start() tick(); } d->timer->start(); + d->spinner->start(); } void CountdownWaitingWidget::stop() { d->timer->stop(); + d->spinner->stop(); } void From 075185547a067b6daaf41b8e830ed093eeeea32a Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Fri, 15 Apr 2022 12:47:12 +0200 Subject: [PATCH 16/33] [welcome] Display a countdown while (re)checking requirements --- src/modules/welcome/checker/ResultsListWidget.cpp | 11 ++++++++++- src/modules/welcome/checker/ResultsListWidget.h | 4 ++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/modules/welcome/checker/ResultsListWidget.cpp b/src/modules/welcome/checker/ResultsListWidget.cpp index eb0c624c8..4c802acfc 100644 --- a/src/modules/welcome/checker/ResultsListWidget.cpp +++ b/src/modules/welcome/checker/ResultsListWidget.cpp @@ -18,6 +18,7 @@ #include "utils/Logger.h" #include "utils/Retranslator.h" #include "widgets/FixedAspectRatioLabel.h" +#include "widgets/WaitingWidget.h" #include #include @@ -169,12 +170,18 @@ ResultsListWidget::ResultsListWidget( Config* config, QWidget* parent ) spacerLayout->addSpacing( paddingSize ); CalamaresUtils::unmarginLayout( spacerLayout ); + QHBoxLayout* explanationLayout = new QHBoxLayout; m_explanation = new QLabel( m_config->warningMessage() ); m_explanation->setWordWrap( true ); m_explanation->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred ); m_explanation->setOpenExternalLinks( false ); m_explanation->setObjectName( "resultsExplanation" ); - m_entriesLayout->addWidget( m_explanation ); + explanationLayout->addWidget( m_explanation ); + m_countdown = new CountdownWaitingWidget; + explanationLayout->addWidget( m_countdown ); + m_countdown->start(); + + m_entriesLayout->addLayout( explanationLayout ); m_entriesLayout->insertSpacing( 1, CalamaresUtils::defaultFontHeight() / 2 ); m_mainLayout->addStretch(); @@ -258,6 +265,8 @@ ResultsListWidget::requirementsChanged() } else { + m_countdown->stop(); + m_countdown->hide(); if ( !Calamares::Branding::instance()->imagePath( Calamares::Branding::ProductWelcome ).isEmpty() ) { QPixmap theImage diff --git a/src/modules/welcome/checker/ResultsListWidget.h b/src/modules/welcome/checker/ResultsListWidget.h index f4f23293b..ca47b3a13 100644 --- a/src/modules/welcome/checker/ResultsListWidget.h +++ b/src/modules/welcome/checker/ResultsListWidget.h @@ -17,8 +17,11 @@ #include +class CountdownWaitingWidget; + class QBoxLayout; class QLabel; + class ResultsListWidget : public QWidget { Q_OBJECT @@ -56,6 +59,7 @@ private: // UI parts, which need updating when the model changes QLabel* m_explanation = nullptr; + CountdownWaitingWidget* m_countdown = nullptr; QBoxLayout* m_mainLayout = nullptr; QBoxLayout* m_entriesLayout = nullptr; int m_requirementsSeen = 0; From e351b1dafa0e4cd5e136641a8655e3377e3a5bd7 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Fri, 15 Apr 2022 12:50:18 +0200 Subject: [PATCH 17/33] [libcalamaresui] Drop the countdown label --- src/libcalamaresui/widgets/WaitingWidget.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/libcalamaresui/widgets/WaitingWidget.cpp b/src/libcalamaresui/widgets/WaitingWidget.cpp index 0e32f4a92..37cc4726e 100644 --- a/src/libcalamaresui/widgets/WaitingWidget.cpp +++ b/src/libcalamaresui/widgets/WaitingWidget.cpp @@ -64,13 +64,11 @@ struct CountdownWaitingWidget::Private // and then wrap around to duration a second later. int count = 0; WaitingSpinnerWidget* spinner = nullptr; - QLabel* label = nullptr; QTimer* timer = nullptr; Private( std::chrono::seconds seconds, QWidget* parent ) : duration( seconds ) , spinner( new WaitingSpinnerWidget( parent ) ) - , label( new QLabel( parent ) ) , timer( new QTimer( parent ) ) { } @@ -81,11 +79,11 @@ CountdownWaitingWidget::CountdownWaitingWidget( std::chrono::seconds duration, Q , d( std::make_unique< Private >( duration, this ) ) { // Set up the label first for sizing - const int labelHeight = qBound( 16, d->label->fontMetrics().height() * 3 / 2, 64 ); + const int labelHeight = qBound( 16, CalamaresUtils::defaultFontHeight() * 3 / 2, 64 ); // Set up the spinner d->spinner->setFixedSize( labelHeight, labelHeight ); - d->spinner->setRevolutionsPerSecond( 1 ); + d->spinner->setRevolutionsPerSecond( 1.0 / double(duration.count()) ); d->spinner->setInnerRadius( labelHeight / 2 ); d->spinner->setLineLength( labelHeight / 2 ); d->spinner->setLineWidth( labelHeight / 8 ); @@ -93,7 +91,6 @@ CountdownWaitingWidget::CountdownWaitingWidget( std::chrono::seconds duration, Q // Overall UI layout QBoxLayout* box = new QHBoxLayout; box->addWidget( d->spinner ); - box->addWidget( d->label ); setLayout( box ); // Last because it updates the text @@ -146,7 +143,6 @@ CountdownWaitingWidget::tick() { d->count = int( d->duration.count() ); } - d->label->setText( QString::number( d->count ) ); if ( d->count == 0 ) { timeout(); From 978661491732833eb889fb7ed3e117481a189557 Mon Sep 17 00:00:00 2001 From: huxingyi Date: Wed, 7 Feb 2018 21:06:05 +0800 Subject: [PATCH 18/33] Add text support. Implement the setText function. The text will be displayed under the spinner image. --- 3rdparty/waitingspinnerwidget.cpp | 34 ++++++++++++++++++++++++++++++- 3rdparty/waitingspinnerwidget.h | 6 ++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/3rdparty/waitingspinnerwidget.cpp b/3rdparty/waitingspinnerwidget.cpp index 98931a6ad..a52cd17e5 100644 --- a/3rdparty/waitingspinnerwidget.cpp +++ b/3rdparty/waitingspinnerwidget.cpp @@ -65,6 +65,7 @@ WaitingSpinnerWidget::WaitingSpinnerWidget(Qt::WindowModality modality, void WaitingSpinnerWidget::initialize() { _color = Qt::black; + _textColor = Qt::white; _roundness = 100.0; _minimumTrailOpacity = 3.14159265358979323846; _trailFadePercentage = 80.0; @@ -98,6 +99,7 @@ void WaitingSpinnerWidget::paintEvent(QPaintEvent *) { painter.save(); painter.translate(_innerRadius + _lineLength, _innerRadius + _lineLength); + painter.translate((width() - _imageSize.width()) / 2, 0); qreal rotateAngle = static_cast(360 * i) / static_cast(_numberOfLines); painter.rotate(rotateAngle); @@ -114,6 +116,12 @@ void WaitingSpinnerWidget::paintEvent(QPaintEvent *) { _roundness, Qt::RelativeSize); painter.restore(); } + + if (!_text.isEmpty()) { + painter.setPen(QPen(_textColor)); + painter.drawText(QRect(0, _imageSize.height(), width(), height() - _imageSize.height()), + Qt::AlignBottom | Qt::AlignHCenter, _text); + } } void WaitingSpinnerWidget::start() { @@ -166,10 +174,23 @@ void WaitingSpinnerWidget::setInnerRadius(int radius) { updateSize(); } +void WaitingSpinnerWidget::setText(QString text) { + _text = text; + updateSize(); +} + QColor WaitingSpinnerWidget::color() { return _color; } +QColor WaitingSpinnerWidget::textColor() { + return _textColor; +} + +QString WaitingSpinnerWidget::text() { + return _text; +} + qreal WaitingSpinnerWidget::roundness() { return _roundness; } @@ -214,6 +235,10 @@ void WaitingSpinnerWidget::setColor(QColor color) { _color = color; } +void WaitingSpinnerWidget::setTextColor(QColor color) { + _textColor = color; +} + void WaitingSpinnerWidget::setRevolutionsPerSecond(qreal revolutionsPerSecond) { _revolutionsPerSecond = revolutionsPerSecond; updateTimer(); @@ -237,7 +262,14 @@ void WaitingSpinnerWidget::rotate() { void WaitingSpinnerWidget::updateSize() { int size = (_innerRadius + _lineLength) * 2; - setFixedSize(size, size); + _imageSize = QSize(size, size); + if (_text.isEmpty()) { + setFixedSize(size, size); + } else { + QFontMetrics fm(font()); + QSize textSize = QSize(fm.width(_text), fm.height()); + setFixedSize(std::max(size, textSize.width()), size + size / 4 + textSize.height()); + } } void WaitingSpinnerWidget::updateTimer() { diff --git a/3rdparty/waitingspinnerwidget.h b/3rdparty/waitingspinnerwidget.h index d171e9beb..5bf96488c 100644 --- a/3rdparty/waitingspinnerwidget.h +++ b/3rdparty/waitingspinnerwidget.h @@ -59,6 +59,7 @@ public slots: public: void setColor(QColor color); + void setTextColor(QColor color); void setRoundness(qreal roundness); void setMinimumTrailOpacity(qreal minimumTrailOpacity); void setTrailFadePercentage(qreal trail); @@ -70,6 +71,8 @@ public: void setText(QString text); QColor color(); + QColor textColor(); + QString text(); qreal roundness(); qreal minimumTrailOpacity(); qreal trailFadePercentage(); @@ -109,6 +112,9 @@ private: int _lineLength; int _lineWidth; int _innerRadius; + QString _text; + QSize _imageSize; + QColor _textColor; private: WaitingSpinnerWidget(const WaitingSpinnerWidget&); From 022f04355af5a32b744ac9c54f8d105f8440e292 Mon Sep 17 00:00:00 2001 From: huxingyi Date: Wed, 7 Feb 2018 21:13:21 +0800 Subject: [PATCH 19/33] Change default text color to black. --- 3rdparty/waitingspinnerwidget.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdparty/waitingspinnerwidget.cpp b/3rdparty/waitingspinnerwidget.cpp index a52cd17e5..8630c6c6b 100644 --- a/3rdparty/waitingspinnerwidget.cpp +++ b/3rdparty/waitingspinnerwidget.cpp @@ -65,7 +65,7 @@ WaitingSpinnerWidget::WaitingSpinnerWidget(Qt::WindowModality modality, void WaitingSpinnerWidget::initialize() { _color = Qt::black; - _textColor = Qt::white; + _textColor = Qt::black; _roundness = 100.0; _minimumTrailOpacity = 3.14159265358979323846; _trailFadePercentage = 80.0; From 171a59735550a8136dd7ebe5ed1078392f6e9573 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Fri, 15 Apr 2022 22:02:55 +0200 Subject: [PATCH 20/33] [3rdparty] Add SPDX tags for setText() support The setText() support was submitted upstream at https://github.com/snowwlex/QtWaitingSpinner/pull/14 by `huxingyi `, but not merged. The commits from that PR were merged into Calamares' copy of the waitingspinnerwidget files. Add relevant tags (which aren't in the PR, but Calamares does chase SPDX) --- 3rdparty/waitingspinnerwidget.cpp | 1 + 3rdparty/waitingspinnerwidget.h | 1 + 2 files changed, 2 insertions(+) diff --git a/3rdparty/waitingspinnerwidget.cpp b/3rdparty/waitingspinnerwidget.cpp index 8630c6c6b..3f98969d5 100644 --- a/3rdparty/waitingspinnerwidget.cpp +++ b/3rdparty/waitingspinnerwidget.cpp @@ -2,6 +2,7 @@ * SPDX-FileCopyrightText: 2012-2014 Alexander Turkin * SPDX-FileCopyrightText: 2014 William Hallatt * SPDX-FileCopyrightText: 2015 Jacob Dawid + * SPDX-FileCopyrightText: 2018 huxingyi * SPDX-License-Identifier: MIT */ diff --git a/3rdparty/waitingspinnerwidget.h b/3rdparty/waitingspinnerwidget.h index 5bf96488c..b600d6727 100644 --- a/3rdparty/waitingspinnerwidget.h +++ b/3rdparty/waitingspinnerwidget.h @@ -2,6 +2,7 @@ * SPDX-FileCopyrightText: 2012-2014 Alexander Turkin * SPDX-FileCopyrightText: 2014 William Hallatt * SPDX-FileCopyrightText: 2015 Jacob Dawid + * SPDX-FileCopyrightText: 2018 huxingyi * SPDX-License-Identifier: MIT */ From 15dc47555ad71beec827ebb7c56e4d596c42ecce Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Fri, 15 Apr 2022 22:01:43 +0200 Subject: [PATCH 21/33] [3rdparty] Constness for waitingspinner The accessors can be (should be!) const. --- 3rdparty/waitingspinnerwidget.cpp | 26 +++++++++++++------------- 3rdparty/waitingspinnerwidget.h | 24 ++++++++++++------------ 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/3rdparty/waitingspinnerwidget.cpp b/3rdparty/waitingspinnerwidget.cpp index 3f98969d5..cd5e1b07e 100644 --- a/3rdparty/waitingspinnerwidget.cpp +++ b/3rdparty/waitingspinnerwidget.cpp @@ -120,7 +120,7 @@ void WaitingSpinnerWidget::paintEvent(QPaintEvent *) { if (!_text.isEmpty()) { painter.setPen(QPen(_textColor)); - painter.drawText(QRect(0, _imageSize.height(), width(), height() - _imageSize.height()), + painter.drawText(QRect(0, _imageSize.height(), width(), height() - _imageSize.height()), Qt::AlignBottom | Qt::AlignHCenter, _text); } } @@ -175,52 +175,52 @@ void WaitingSpinnerWidget::setInnerRadius(int radius) { updateSize(); } -void WaitingSpinnerWidget::setText(QString text) { +void WaitingSpinnerWidget::setText(const QString& text) { _text = text; updateSize(); } -QColor WaitingSpinnerWidget::color() { +QColor WaitingSpinnerWidget::color() const { return _color; } -QColor WaitingSpinnerWidget::textColor() { +QColor WaitingSpinnerWidget::textColor() const { return _textColor; } -QString WaitingSpinnerWidget::text() { +QString WaitingSpinnerWidget::text() const { return _text; } -qreal WaitingSpinnerWidget::roundness() { +qreal WaitingSpinnerWidget::roundness() const { return _roundness; } -qreal WaitingSpinnerWidget::minimumTrailOpacity() { +qreal WaitingSpinnerWidget::minimumTrailOpacity() const { return _minimumTrailOpacity; } -qreal WaitingSpinnerWidget::trailFadePercentage() { +qreal WaitingSpinnerWidget::trailFadePercentage() const { return _trailFadePercentage; } -qreal WaitingSpinnerWidget::revolutionsPersSecond() { +qreal WaitingSpinnerWidget::revolutionsPersSecond() const { return _revolutionsPerSecond; } -int WaitingSpinnerWidget::numberOfLines() { +int WaitingSpinnerWidget::numberOfLines() const { return _numberOfLines; } -int WaitingSpinnerWidget::lineLength() { +int WaitingSpinnerWidget::lineLength() const { return _lineLength; } -int WaitingSpinnerWidget::lineWidth() { +int WaitingSpinnerWidget::lineWidth() const { return _lineWidth; } -int WaitingSpinnerWidget::innerRadius() { +int WaitingSpinnerWidget::innerRadius() const { return _innerRadius; } diff --git a/3rdparty/waitingspinnerwidget.h b/3rdparty/waitingspinnerwidget.h index b600d6727..b180e268a 100644 --- a/3rdparty/waitingspinnerwidget.h +++ b/3rdparty/waitingspinnerwidget.h @@ -69,19 +69,19 @@ public: void setLineLength(int length); void setLineWidth(int width); void setInnerRadius(int radius); - void setText(QString text); + void setText(const QString& text); - QColor color(); - QColor textColor(); - QString text(); - qreal roundness(); - qreal minimumTrailOpacity(); - qreal trailFadePercentage(); - qreal revolutionsPersSecond(); - int numberOfLines(); - int lineLength(); - int lineWidth(); - int innerRadius(); + QColor color() const; + QColor textColor() const; + QString text() const; + qreal roundness() const; + qreal minimumTrailOpacity() const; + qreal trailFadePercentage() const; + qreal revolutionsPersSecond() const; + int numberOfLines() const; + int lineLength() const; + int lineWidth() const; + int innerRadius() const; bool isSpinning() const; From fac8662387691f3e6ae11a3769a66d25d546e710 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Fri, 15 Apr 2022 23:08:58 +0200 Subject: [PATCH 22/33] [3rdparty] Tidy up access and API dox --- 3rdparty/waitingspinnerwidget.h | 35 ++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/3rdparty/waitingspinnerwidget.h b/3rdparty/waitingspinnerwidget.h index b180e268a..7e540dd40 100644 --- a/3rdparty/waitingspinnerwidget.h +++ b/3rdparty/waitingspinnerwidget.h @@ -38,27 +38,30 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. class WaitingSpinnerWidget : public QWidget { Q_OBJECT public: - /*! Constructor for "standard" widget behaviour - use this - * constructor if you wish to, e.g. embed your widget in another. */ + /** @brief Constructor for "standard" widget behaviour + * + * Use this constructor if you wish to, e.g. embed your widget in another. + */ WaitingSpinnerWidget(QWidget *parent = nullptr, bool centerOnParent = true, bool disableParentWhenSpinning = true); - /*! Constructor - use this constructor to automatically create a modal - * ("blocking") spinner on top of the calling widget/window. If a valid - * parent widget is provided, "centreOnParent" will ensure that - * QtWaitingSpinner automatically centres itself on it, if not, - * "centreOnParent" is ignored. */ + /** @brief Constructor + * + * Use this constructor to automatically create a modal + * ("blocking") spinner on top of the calling widget/window. If a valid + * parent widget is provided, "centreOnParent" will ensure that + * QtWaitingSpinner automatically centres itself on it, if not, + * @p centerOnParent is ignored. + */ WaitingSpinnerWidget(Qt::WindowModality modality, QWidget *parent = nullptr, bool centerOnParent = true, bool disableParentWhenSpinning = true); -public slots: - void start(); - void stop(); + WaitingSpinnerWidget(const WaitingSpinnerWidget&) = delete; + WaitingSpinnerWidget& operator=(const WaitingSpinnerWidget&) = delete; -public: void setColor(QColor color); void setTextColor(QColor color); void setRoundness(qreal roundness); @@ -85,7 +88,11 @@ public: bool isSpinning() const; -private slots: +public Q_SLOTS: + void start(); + void stop(); + +private Q_SLOTS: void rotate(); protected: @@ -117,10 +124,6 @@ private: QSize _imageSize; QColor _textColor; -private: - WaitingSpinnerWidget(const WaitingSpinnerWidget&); - WaitingSpinnerWidget& operator=(const WaitingSpinnerWidget&); - QTimer *_timer; bool _centerOnParent; bool _disableParentWhenSpinning; From bef6b2fffd16de7d7ccbcb49eded53f9de6e550e Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Sat, 16 Apr 2022 00:04:32 +0200 Subject: [PATCH 23/33] [3rdparty] Introduce alignment flag for text --- 3rdparty/waitingspinnerwidget.cpp | 22 +++++++++++++++++++--- 3rdparty/waitingspinnerwidget.h | 17 +++++++++++++++++ 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/3rdparty/waitingspinnerwidget.cpp b/3rdparty/waitingspinnerwidget.cpp index cd5e1b07e..a55062b54 100644 --- a/3rdparty/waitingspinnerwidget.cpp +++ b/3rdparty/waitingspinnerwidget.cpp @@ -39,6 +39,11 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include +static bool isAlignCenter(Qt::AlignmentFlag a) +{ + return a == Qt::AlignmentFlag::AlignVCenter; +} + WaitingSpinnerWidget::WaitingSpinnerWidget(QWidget *parent, bool centerOnParent, bool disableParentWhenSpinning) @@ -120,8 +125,13 @@ void WaitingSpinnerWidget::paintEvent(QPaintEvent *) { if (!_text.isEmpty()) { painter.setPen(QPen(_textColor)); - painter.drawText(QRect(0, _imageSize.height(), width(), height() - _imageSize.height()), - Qt::AlignBottom | Qt::AlignHCenter, _text); + if (isAlignCenter(alignment())) { + painter.drawText(QRect(0, 0, width(), height()), + Qt::AlignVCenter | Qt::AlignHCenter, _text); + } else { + painter.drawText(QRect(0, _imageSize.height(), width(), height() - _imageSize.height()), + Qt::AlignBottom | Qt::AlignHCenter, _text); + } } } @@ -180,6 +190,12 @@ void WaitingSpinnerWidget::setText(const QString& text) { updateSize(); } +void WaitingSpinnerWidget::setAlignment(Qt::AlignmentFlag align) +{ + _alignment = align; + updateSize(); +} + QColor WaitingSpinnerWidget::color() const { return _color; } @@ -264,7 +280,7 @@ void WaitingSpinnerWidget::rotate() { void WaitingSpinnerWidget::updateSize() { int size = (_innerRadius + _lineLength) * 2; _imageSize = QSize(size, size); - if (_text.isEmpty()) { + if (_text.isEmpty() || isAlignCenter(alignment())) { setFixedSize(size, size); } else { QFontMetrics fm(font()); diff --git a/3rdparty/waitingspinnerwidget.h b/3rdparty/waitingspinnerwidget.h index 7e540dd40..d5e8620d4 100644 --- a/3rdparty/waitingspinnerwidget.h +++ b/3rdparty/waitingspinnerwidget.h @@ -72,11 +72,27 @@ public: void setLineLength(int length); void setLineWidth(int width); void setInnerRadius(int radius); + + /** @brief Sets the text displayed in or below the spinner + * + * If the text is empty, no text is displayed. The text is displayed + * in or below the spinner depending on the value of alignment(). + * With AlignBottom, the text is displayed below the spinner, + * centered horizontally relative to the spinner; any other alignment + * will put the text in the middle of the spinner itself. + */ void setText(const QString& text); + /** @brief Sets the alignment of text for the spinner + * + * The only meaningful values are AlignBottom and AlignVCenter, + * for text below the spinner and text in the middle. + */ + void setAlignment(Qt::AlignmentFlag align); QColor color() const; QColor textColor() const; QString text() const; + Qt::AlignmentFlag alignment() const { return _alignment; } qreal roundness() const; qreal minimumTrailOpacity() const; qreal trailFadePercentage() const; @@ -120,6 +136,7 @@ private: int _lineLength; int _lineWidth; int _innerRadius; + Qt::AlignmentFlag _alignment = Qt::AlignmentFlag::AlignBottom; QString _text; QSize _imageSize; QColor _textColor; From 82d721f4557cc50156c07f521978d04a82f20057 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Sat, 16 Apr 2022 00:05:01 +0200 Subject: [PATCH 24/33] [libcalamaresui] Display countdown --- src/libcalamaresui/widgets/WaitingWidget.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libcalamaresui/widgets/WaitingWidget.cpp b/src/libcalamaresui/widgets/WaitingWidget.cpp index 37cc4726e..e8504ff94 100644 --- a/src/libcalamaresui/widgets/WaitingWidget.cpp +++ b/src/libcalamaresui/widgets/WaitingWidget.cpp @@ -143,6 +143,7 @@ CountdownWaitingWidget::tick() { d->count = int( d->duration.count() ); } + d->spinner->setText( QString::number(d->count) ); if ( d->count == 0 ) { timeout(); From 3c5ac535f156e5a73cf646eb0eac95870964031f Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Sat, 16 Apr 2022 11:28:10 +0200 Subject: [PATCH 25/33] [libcalamaresui] The waiting spinner now supports text, no need for extra label --- src/libcalamaresui/widgets/WaitingWidget.cpp | 50 +++++--------------- src/libcalamaresui/widgets/WaitingWidget.h | 12 ++--- 2 files changed, 15 insertions(+), 47 deletions(-) diff --git a/src/libcalamaresui/widgets/WaitingWidget.cpp b/src/libcalamaresui/widgets/WaitingWidget.cpp index e8504ff94..13ae9fd03 100644 --- a/src/libcalamaresui/widgets/WaitingWidget.cpp +++ b/src/libcalamaresui/widgets/WaitingWidget.cpp @@ -12,50 +12,24 @@ #include "utils/CalamaresUtilsGui.h" -#include "3rdparty/waitingspinnerwidget.h" - #include #include #include WaitingWidget::WaitingWidget( const QString& text, QWidget* parent ) - : QWidget( parent ) + : WaitingSpinnerWidget( parent ) { - QBoxLayout* waitingLayout = new QVBoxLayout; - setLayout( waitingLayout ); - waitingLayout->addStretch(); - QBoxLayout* pbLayout = new QHBoxLayout; - waitingLayout->addLayout( pbLayout ); - pbLayout->addStretch(); - - WaitingSpinnerWidget* spnr = new WaitingSpinnerWidget(); - pbLayout->addWidget( spnr ); - - pbLayout->addStretch(); - - m_waitingLabel = new QLabel( text ); - - int spnrSize = m_waitingLabel->fontMetrics().height() * 4; - spnr->setFixedSize( spnrSize, spnrSize ); - spnr->setInnerRadius( spnrSize / 2 ); - spnr->setLineLength( spnrSize / 2 ); - spnr->setLineWidth( spnrSize / 8 ); - spnr->start(); - - m_waitingLabel->setAlignment( Qt::AlignCenter ); - waitingLayout->addSpacing( spnrSize / 2 ); - waitingLayout->addWidget( m_waitingLabel ); - waitingLayout->addStretch(); - - CalamaresUtils::unmarginLayout( waitingLayout ); + int spnrSize = CalamaresUtils::defaultFontHeight() * 4; + setFixedSize( spnrSize, spnrSize ); + setInnerRadius( spnrSize / 2 ); + setLineLength( spnrSize / 2 ); + setLineWidth( spnrSize / 8 ); + setAlignment( Qt::AlignmentFlag::AlignBottom ); + setText( text ); + start(); } - -void -WaitingWidget::setText( const QString& text ) -{ - m_waitingLabel->setText( text ); -} +WaitingWidget::~WaitingWidget() {} struct CountdownWaitingWidget::Private { @@ -83,7 +57,7 @@ CountdownWaitingWidget::CountdownWaitingWidget( std::chrono::seconds duration, Q // Set up the spinner d->spinner->setFixedSize( labelHeight, labelHeight ); - d->spinner->setRevolutionsPerSecond( 1.0 / double(duration.count()) ); + d->spinner->setRevolutionsPerSecond( 1.0 / double( duration.count() ) ); d->spinner->setInnerRadius( labelHeight / 2 ); d->spinner->setLineLength( labelHeight / 2 ); d->spinner->setLineWidth( labelHeight / 8 ); @@ -143,7 +117,7 @@ CountdownWaitingWidget::tick() { d->count = int( d->duration.count() ); } - d->spinner->setText( QString::number(d->count) ); + d->spinner->setText( QString::number( d->count ) ); if ( d->count == 0 ) { timeout(); diff --git a/src/libcalamaresui/widgets/WaitingWidget.h b/src/libcalamaresui/widgets/WaitingWidget.h index 867529641..cb0e15545 100644 --- a/src/libcalamaresui/widgets/WaitingWidget.h +++ b/src/libcalamaresui/widgets/WaitingWidget.h @@ -10,7 +10,7 @@ #ifndef WAITINGWIDGET_H #define WAITINGWIDGET_H -#include +#include "3rdparty/waitingspinnerwidget.h" #include #include @@ -24,18 +24,12 @@ class QTimer; * and the text is displayed centered below it. Use this * to display a long-term waiting situation with a status report. */ -class WaitingWidget : public QWidget +class WaitingWidget : public WaitingSpinnerWidget { - Q_OBJECT public: /// Create a WaitingWidget with initial @p text label. explicit WaitingWidget( const QString& text, QWidget* parent = nullptr ); - - /// Update the @p text displayed in the label. - void setText( const QString& text ); - -private: - QLabel* m_waitingLabel; + ~WaitingWidget() override; }; /** @brief A spinner and a countdown next to it From 5060a66d4e8bcd2e5c269cce54486098dfdebb6d Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Sat, 16 Apr 2022 11:29:49 +0200 Subject: [PATCH 26/33] [locale] Remove unused include --- src/modules/locale/LocaleViewStep.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/modules/locale/LocaleViewStep.cpp b/src/modules/locale/LocaleViewStep.cpp index 2145ad201..03d1d4f5e 100644 --- a/src/modules/locale/LocaleViewStep.cpp +++ b/src/modules/locale/LocaleViewStep.cpp @@ -11,7 +11,6 @@ #include "LocaleViewStep.h" #include "LocalePage.h" -#include "widgets/WaitingWidget.h" #include "GlobalStorage.h" #include "JobQueue.h" From 3bd610a8388deb4157d849685daf922c1c30c5ed Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Sat, 16 Apr 2022 11:53:03 +0200 Subject: [PATCH 27/33] [libcalamaresui] Port Countdown to spinner widget alone --- src/libcalamaresui/widgets/WaitingWidget.cpp | 26 ++++++++------------ src/libcalamaresui/widgets/WaitingWidget.h | 2 +- 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/src/libcalamaresui/widgets/WaitingWidget.cpp b/src/libcalamaresui/widgets/WaitingWidget.cpp index 13ae9fd03..118d219e4 100644 --- a/src/libcalamaresui/widgets/WaitingWidget.cpp +++ b/src/libcalamaresui/widgets/WaitingWidget.cpp @@ -37,35 +37,29 @@ struct CountdownWaitingWidget::Private // int because we count down, need to be able to show a 0, // and then wrap around to duration a second later. int count = 0; - WaitingSpinnerWidget* spinner = nullptr; QTimer* timer = nullptr; Private( std::chrono::seconds seconds, QWidget* parent ) : duration( seconds ) - , spinner( new WaitingSpinnerWidget( parent ) ) , timer( new QTimer( parent ) ) { } }; CountdownWaitingWidget::CountdownWaitingWidget( std::chrono::seconds duration, QWidget* parent ) - : QWidget( parent ) + : WaitingSpinnerWidget( parent, false, false ) , d( std::make_unique< Private >( duration, this ) ) { // Set up the label first for sizing const int labelHeight = qBound( 16, CalamaresUtils::defaultFontHeight() * 3 / 2, 64 ); // Set up the spinner - d->spinner->setFixedSize( labelHeight, labelHeight ); - d->spinner->setRevolutionsPerSecond( 1.0 / double( duration.count() ) ); - d->spinner->setInnerRadius( labelHeight / 2 ); - d->spinner->setLineLength( labelHeight / 2 ); - d->spinner->setLineWidth( labelHeight / 8 ); - - // Overall UI layout - QBoxLayout* box = new QHBoxLayout; - box->addWidget( d->spinner ); - setLayout( box ); + setFixedSize( labelHeight, labelHeight ); + setRevolutionsPerSecond( 1.0 / double( duration.count() ) ); + setInnerRadius( labelHeight / 2 ); + setLineLength( labelHeight / 2 ); + setLineWidth( labelHeight / 8 ); + setAlignment( Qt::AlignmentFlag::AlignVCenter ); // Last because it updates the text setInterval( duration ); @@ -97,14 +91,14 @@ CountdownWaitingWidget::start() tick(); } d->timer->start(); - d->spinner->start(); + WaitingSpinnerWidget::start(); } void CountdownWaitingWidget::stop() { d->timer->stop(); - d->spinner->stop(); + WaitingSpinnerWidget::stop(); } void @@ -117,7 +111,7 @@ CountdownWaitingWidget::tick() { d->count = int( d->duration.count() ); } - d->spinner->setText( QString::number( d->count ) ); + setText( QString::number( d->count ) ); if ( d->count == 0 ) { timeout(); diff --git a/src/libcalamaresui/widgets/WaitingWidget.h b/src/libcalamaresui/widgets/WaitingWidget.h index cb0e15545..009f17f49 100644 --- a/src/libcalamaresui/widgets/WaitingWidget.h +++ b/src/libcalamaresui/widgets/WaitingWidget.h @@ -39,7 +39,7 @@ public: * every second. The signal timeout() is sent every time * the countdown reaches 0. */ -class CountdownWaitingWidget : public QWidget +class CountdownWaitingWidget : public WaitingSpinnerWidget { Q_OBJECT public: From 2ccd59e90b9eecc042f064ab430e5862288c3af7 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Sat, 16 Apr 2022 11:59:31 +0200 Subject: [PATCH 28/33] [libcalamaresui] Move waitingspinnerwidget.* into calamaresui Although this is 3rd-party code, it now diverges -- by merging the stale PR from upstream, and from adding features of our own -- enough that we should not pretend that it is the original 3rdparty code. Chase a couple of include paths that called this from 3rdparty/ --- src/libcalamaresui/CMakeLists.txt | 7 +------ src/libcalamaresui/widgets/WaitingWidget.h | 2 +- .../libcalamaresui/widgets}/waitingspinnerwidget.cpp | 0 .../libcalamaresui/widgets}/waitingspinnerwidget.h | 0 src/modules/partition/gui/ScanningDialog.cpp | 2 +- 5 files changed, 3 insertions(+), 8 deletions(-) rename {3rdparty => src/libcalamaresui/widgets}/waitingspinnerwidget.cpp (100%) rename {3rdparty => src/libcalamaresui/widgets}/waitingspinnerwidget.h (100%) diff --git a/src/libcalamaresui/CMakeLists.txt b/src/libcalamaresui/CMakeLists.txt index 48e4c4b4d..48ac201d3 100644 --- a/src/libcalamaresui/CMakeLists.txt +++ b/src/libcalamaresui/CMakeLists.txt @@ -33,17 +33,12 @@ set( calamaresui_SOURCES widgets/LogWidget.cpp widgets/TranslationFix.cpp widgets/WaitingWidget.cpp - ${CMAKE_SOURCE_DIR}/3rdparty/waitingspinnerwidget.cpp + widgets/waitingspinnerwidget.cpp Branding.cpp ViewManager.cpp ) -# Don't warn about third-party sources -mark_thirdparty_code( - ${CMAKE_SOURCE_DIR}/3rdparty/waitingspinnerwidget.cpp -) - if( WITH_PYTHON ) list( APPEND calamaresui_SOURCES modulesystem/PythonJobModule.cpp diff --git a/src/libcalamaresui/widgets/WaitingWidget.h b/src/libcalamaresui/widgets/WaitingWidget.h index 009f17f49..1b78809de 100644 --- a/src/libcalamaresui/widgets/WaitingWidget.h +++ b/src/libcalamaresui/widgets/WaitingWidget.h @@ -10,7 +10,7 @@ #ifndef WAITINGWIDGET_H #define WAITINGWIDGET_H -#include "3rdparty/waitingspinnerwidget.h" +#include "widgets/waitingspinnerwidget.h" #include #include diff --git a/3rdparty/waitingspinnerwidget.cpp b/src/libcalamaresui/widgets/waitingspinnerwidget.cpp similarity index 100% rename from 3rdparty/waitingspinnerwidget.cpp rename to src/libcalamaresui/widgets/waitingspinnerwidget.cpp diff --git a/3rdparty/waitingspinnerwidget.h b/src/libcalamaresui/widgets/waitingspinnerwidget.h similarity index 100% rename from 3rdparty/waitingspinnerwidget.h rename to src/libcalamaresui/widgets/waitingspinnerwidget.h diff --git a/src/modules/partition/gui/ScanningDialog.cpp b/src/modules/partition/gui/ScanningDialog.cpp index 56133e21f..7dd85ff86 100644 --- a/src/modules/partition/gui/ScanningDialog.cpp +++ b/src/modules/partition/gui/ScanningDialog.cpp @@ -10,7 +10,7 @@ #include "ScanningDialog.h" -#include "3rdparty/waitingspinnerwidget.h" +#include "widgets/waitingspinnerwidget.h" #include #include From 1f7dd2fcd59f40b527d6bb8a13f481efce4e0f2d Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Sat, 16 Apr 2022 12:02:56 +0200 Subject: [PATCH 29/33] [libcalamaresui] Convenience API --- src/libcalamaresui/widgets/waitingspinnerwidget.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libcalamaresui/widgets/waitingspinnerwidget.h b/src/libcalamaresui/widgets/waitingspinnerwidget.h index d5e8620d4..531d19273 100644 --- a/src/libcalamaresui/widgets/waitingspinnerwidget.h +++ b/src/libcalamaresui/widgets/waitingspinnerwidget.h @@ -88,6 +88,8 @@ public: * for text below the spinner and text in the middle. */ void setAlignment(Qt::AlignmentFlag align); + /// Convenience to set text-in-the-middle (@c true) or text-at-bottom (@c false) + void setCenteredText(bool centered) { setAlignment(centered ? Qt::AlignmentFlag::AlignVCenter : Qt::AlignmentFlag::AlignBottom ); } QColor color() const; QColor textColor() const; From 78a8993f388836d72589aa997252188956a8fcda Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Sat, 16 Apr 2022 12:11:52 +0200 Subject: [PATCH 30/33] [libcalamaresui] Move to more modern-style initializations --- .../widgets/waitingspinnerwidget.cpp | 13 ------- .../widgets/waitingspinnerwidget.h | 37 ++++++++++++------- 2 files changed, 23 insertions(+), 27 deletions(-) diff --git a/src/libcalamaresui/widgets/waitingspinnerwidget.cpp b/src/libcalamaresui/widgets/waitingspinnerwidget.cpp index a55062b54..a41a8a3f7 100644 --- a/src/libcalamaresui/widgets/waitingspinnerwidget.cpp +++ b/src/libcalamaresui/widgets/waitingspinnerwidget.cpp @@ -70,19 +70,6 @@ WaitingSpinnerWidget::WaitingSpinnerWidget(Qt::WindowModality modality, } void WaitingSpinnerWidget::initialize() { - _color = Qt::black; - _textColor = Qt::black; - _roundness = 100.0; - _minimumTrailOpacity = 3.14159265358979323846; - _trailFadePercentage = 80.0; - _revolutionsPerSecond = 1.57079632679489661923; - _numberOfLines = 20; - _lineLength = 10; - _lineWidth = 2; - _innerRadius = 10; - _currentCounter = 0; - _isSpinning = false; - _timer = new QTimer(this); connect(_timer, SIGNAL(timeout()), this, SLOT(rotate())); updateSize(); diff --git a/src/libcalamaresui/widgets/waitingspinnerwidget.h b/src/libcalamaresui/widgets/waitingspinnerwidget.h index 531d19273..d223dcf27 100644 --- a/src/libcalamaresui/widgets/waitingspinnerwidget.h +++ b/src/libcalamaresui/widgets/waitingspinnerwidget.h @@ -129,23 +129,32 @@ private: void updatePosition(); private: - QColor _color; - qreal _roundness; // 0..100 - qreal _minimumTrailOpacity; - qreal _trailFadePercentage; - qreal _revolutionsPerSecond; - int _numberOfLines; - int _lineLength; - int _lineWidth; - int _innerRadius; + // PI, leading to a full fade in one whole revolution + static constexpr const auto radian = 3.14159265358979323846; + + // Spinner-wheel related settings + QColor _color = Qt::black; + qreal _roundness = 100.0; // 0..100 + qreal _minimumTrailOpacity = radian; + qreal _trailFadePercentage = 80.0; + qreal _revolutionsPerSecond = radian / 2; + int _numberOfLines = 20; + int _lineLength = 10; + int _lineWidth = 2; + int _innerRadius = 10; + QSize _imageSize; + + // Text-related settings Qt::AlignmentFlag _alignment = Qt::AlignmentFlag::AlignBottom; QString _text; - QSize _imageSize; - QColor _textColor; + QColor _textColor = Qt::black; - QTimer *_timer; + // Environment settings bool _centerOnParent; bool _disableParentWhenSpinning; - int _currentCounter; - bool _isSpinning; + + // Internal bits + QTimer *_timer = nullptr; + int _currentCounter = 0; + bool _isSpinning = false; }; From 497422e72c2b766aacb6457e10517c0d8204b0cc Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Sat, 16 Apr 2022 12:18:49 +0200 Subject: [PATCH 31/33] [libcalamaresui] Don't disable parent while waiting --- src/libcalamaresui/widgets/WaitingWidget.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcalamaresui/widgets/WaitingWidget.cpp b/src/libcalamaresui/widgets/WaitingWidget.cpp index 118d219e4..18acc11b7 100644 --- a/src/libcalamaresui/widgets/WaitingWidget.cpp +++ b/src/libcalamaresui/widgets/WaitingWidget.cpp @@ -17,7 +17,7 @@ #include WaitingWidget::WaitingWidget( const QString& text, QWidget* parent ) - : WaitingSpinnerWidget( parent ) + : WaitingSpinnerWidget( parent, false, false ) { int spnrSize = CalamaresUtils::defaultFontHeight() * 4; setFixedSize( spnrSize, spnrSize ); From 18f8633dd93646723ca189fab5506a6a9e611557 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Sat, 16 Apr 2022 12:20:02 +0200 Subject: [PATCH 32/33] [libcalamaresui] Also modern-style initialize center and disable flags - All constructors explicitly initialize these, so there's no real change here. --- src/libcalamaresui/widgets/waitingspinnerwidget.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libcalamaresui/widgets/waitingspinnerwidget.h b/src/libcalamaresui/widgets/waitingspinnerwidget.h index d223dcf27..d66a2cea0 100644 --- a/src/libcalamaresui/widgets/waitingspinnerwidget.h +++ b/src/libcalamaresui/widgets/waitingspinnerwidget.h @@ -150,8 +150,8 @@ private: QColor _textColor = Qt::black; // Environment settings - bool _centerOnParent; - bool _disableParentWhenSpinning; + bool _centerOnParent = true; + bool _disableParentWhenSpinning = true; // Internal bits QTimer *_timer = nullptr; From e11a0ee44885f45e7907b7508b8b8eac3e7c014b Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Sat, 16 Apr 2022 12:25:28 +0200 Subject: [PATCH 33/33] [libcalamaresui] Use delegating-constructor, drop initialize() --- .../widgets/waitingspinnerwidget.cpp | 33 +++++++++---------- .../widgets/waitingspinnerwidget.h | 1 - 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/src/libcalamaresui/widgets/waitingspinnerwidget.cpp b/src/libcalamaresui/widgets/waitingspinnerwidget.cpp index a41a8a3f7..a4bb9b79c 100644 --- a/src/libcalamaresui/widgets/waitingspinnerwidget.cpp +++ b/src/libcalamaresui/widgets/waitingspinnerwidget.cpp @@ -47,34 +47,33 @@ static bool isAlignCenter(Qt::AlignmentFlag a) WaitingSpinnerWidget::WaitingSpinnerWidget(QWidget *parent, bool centerOnParent, bool disableParentWhenSpinning) - : QWidget(parent), - _centerOnParent(centerOnParent), - _disableParentWhenSpinning(disableParentWhenSpinning) { - initialize(); -} + : WaitingSpinnerWidget(Qt::WindowModality::NonModal, parent, centerOnParent, disableParentWhenSpinning) +{} WaitingSpinnerWidget::WaitingSpinnerWidget(Qt::WindowModality modality, QWidget *parent, bool centerOnParent, bool disableParentWhenSpinning) - : QWidget(parent, Qt::Dialog | Qt::FramelessWindowHint), + : QWidget(parent, modality == Qt::WindowModality::NonModal ? Qt::WindowFlags() : Qt::Dialog | Qt::FramelessWindowHint), _centerOnParent(centerOnParent), - _disableParentWhenSpinning(disableParentWhenSpinning){ - initialize(); - - // We need to set the window modality AFTER we've hidden the - // widget for the first time since changing this property while - // the widget is visible has no effect. - setWindowModality(modality); - setAttribute(Qt::WA_TranslucentBackground); -} - -void WaitingSpinnerWidget::initialize() { + _disableParentWhenSpinning(disableParentWhenSpinning) +{ _timer = new QTimer(this); connect(_timer, SIGNAL(timeout()), this, SLOT(rotate())); updateSize(); updateTimer(); hide(); + + // We need to set the window modality AFTER we've hidden the + // widget for the first time since changing this property while + // the widget is visible has no effect. + // + // Non-modal windows don't need any work + if ( modality != Qt::WindowModality::NonModal ) + { + setWindowModality(modality); + setAttribute(Qt::WA_TranslucentBackground); + } } void WaitingSpinnerWidget::paintEvent(QPaintEvent *) { diff --git a/src/libcalamaresui/widgets/waitingspinnerwidget.h b/src/libcalamaresui/widgets/waitingspinnerwidget.h index d66a2cea0..1ecc33a87 100644 --- a/src/libcalamaresui/widgets/waitingspinnerwidget.h +++ b/src/libcalamaresui/widgets/waitingspinnerwidget.h @@ -123,7 +123,6 @@ private: qreal trailFadePerc, qreal minOpacity, QColor color); - void initialize(); void updateSize(); void updateTimer(); void updatePosition();