Merge branch 'issue-1924' into work-3.3
This is a rather clunky implementation of re-check requirements. "Clunky" because the UI parts are re-created each time, rather than fishing from a model of checked (or unchecked) requirements. The Widgets parts should be updated to use a full model, rather than the recreate-list-of-Widgets implementation they have now. Unrelated changes pull in a bunch of improvements to the waiting spinner widget.
This commit is contained in:
commit
7e5df42fc0
14
CHANGES-3.2
14
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
|
changelog -- this log starts with version 3.2.0. The release notes on the
|
||||||
website will have to do for older versions.
|
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) #
|
# 3.2.55 (2022-04-11) #
|
||||||
|
|
||||||
This release contains contributions from (alphabetically by first name):
|
This release contains contributions from (alphabetically by first name):
|
||||||
|
@ -32,6 +32,7 @@ RequirementsChecker::RequirementsChecker( QVector< Module* > modules, Requiremen
|
|||||||
, m_progressTimer( nullptr )
|
, m_progressTimer( nullptr )
|
||||||
, m_progressTimeouts( 0 )
|
, m_progressTimeouts( 0 )
|
||||||
{
|
{
|
||||||
|
m_model->clear();
|
||||||
m_watchers.reserve( m_modules.count() );
|
m_watchers.reserve( m_modules.count() );
|
||||||
connect( this, &RequirementsChecker::requirementsProgress, model, &RequirementsModel::setProgressMessage );
|
connect( this, &RequirementsChecker::requirementsProgress, model, &RequirementsModel::setProgressMessage );
|
||||||
}
|
}
|
||||||
@ -63,9 +64,9 @@ RequirementsChecker::finished()
|
|||||||
static QMutex finishedMutex;
|
static QMutex finishedMutex;
|
||||||
QMutexLocker lock( &finishedMutex );
|
QMutexLocker lock( &finishedMutex );
|
||||||
|
|
||||||
if ( m_progressTimer && std::all_of( m_watchers.cbegin(), m_watchers.cend(), []( const Watcher* w ) {
|
if ( m_progressTimer
|
||||||
return w && w->isFinished();
|
&& std::all_of(
|
||||||
} ) )
|
m_watchers.cbegin(), m_watchers.cend(), []( const Watcher* w ) { return w && w->isFinished(); } ) )
|
||||||
{
|
{
|
||||||
cDebug() << "All requirements have been checked.";
|
cDebug() << "All requirements have been checked.";
|
||||||
if ( m_progressTimer )
|
if ( m_progressTimer )
|
||||||
@ -100,14 +101,17 @@ RequirementsChecker::reportProgress()
|
|||||||
m_progressTimeouts++;
|
m_progressTimeouts++;
|
||||||
|
|
||||||
QStringList remainingNames;
|
QStringList remainingNames;
|
||||||
auto remaining = std::count_if( m_watchers.cbegin(), m_watchers.cend(), [&]( const Watcher* w ) {
|
auto remaining = std::count_if( m_watchers.cbegin(),
|
||||||
if ( w && !w->isFinished() )
|
m_watchers.cend(),
|
||||||
{
|
[ & ]( const Watcher* w )
|
||||||
remainingNames << w->objectName();
|
{
|
||||||
return true;
|
if ( w && !w->isFinished() )
|
||||||
}
|
{
|
||||||
return false;
|
remainingNames << w->objectName();
|
||||||
} );
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} );
|
||||||
if ( remaining > 0 )
|
if ( remaining > 0 )
|
||||||
{
|
{
|
||||||
cDebug() << "Remaining modules:" << remaining << Logger::DebugList( remainingNames );
|
cDebug() << "Remaining modules:" << remaining << Logger::DebugList( remainingNames );
|
||||||
|
@ -15,6 +15,16 @@
|
|||||||
namespace Calamares
|
namespace Calamares
|
||||||
{
|
{
|
||||||
|
|
||||||
|
void
|
||||||
|
RequirementsModel::clear()
|
||||||
|
{
|
||||||
|
QMutexLocker l( &m_addLock );
|
||||||
|
emit beginResetModel();
|
||||||
|
m_requirements.clear();
|
||||||
|
changeRequirementsList();
|
||||||
|
emit endResetModel();
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
RequirementsModel::addRequirementsList( const Calamares::RequirementsList& requirements )
|
RequirementsModel::addRequirementsList( const Calamares::RequirementsList& requirements )
|
||||||
{
|
{
|
||||||
|
@ -77,6 +77,10 @@ signals:
|
|||||||
protected:
|
protected:
|
||||||
QHash< int, QByteArray > roleNames() const override;
|
QHash< int, QByteArray > roleNames() const override;
|
||||||
|
|
||||||
|
|
||||||
|
///@brief Clears the requirements; resets the model
|
||||||
|
void clear();
|
||||||
|
|
||||||
///@brief Append some requirements; resets the model
|
///@brief Append some requirements; resets the model
|
||||||
void addRequirementsList( const Calamares::RequirementsList& requirements );
|
void addRequirementsList( const Calamares::RequirementsList& requirements );
|
||||||
|
|
||||||
|
@ -30,16 +30,11 @@ set(calamaresui_SOURCES
|
|||||||
widgets/LogWidget.cpp
|
widgets/LogWidget.cpp
|
||||||
widgets/TranslationFix.cpp
|
widgets/TranslationFix.cpp
|
||||||
widgets/WaitingWidget.cpp
|
widgets/WaitingWidget.cpp
|
||||||
${CMAKE_SOURCE_DIR}/3rdparty/waitingspinnerwidget.cpp
|
widgets/waitingspinnerwidget.cpp
|
||||||
Branding.cpp
|
Branding.cpp
|
||||||
ViewManager.cpp
|
ViewManager.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
# Don't warn about third-party sources
|
|
||||||
mark_thirdparty_code(
|
|
||||||
${CMAKE_SOURCE_DIR}/3rdparty/waitingspinnerwidget.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
if(WITH_PYTHON)
|
if(WITH_PYTHON)
|
||||||
list(APPEND calamaresui_SOURCES modulesystem/PythonJobModule.cpp)
|
list(APPEND calamaresui_SOURCES modulesystem/PythonJobModule.cpp)
|
||||||
endif()
|
endif()
|
||||||
|
@ -349,7 +349,18 @@ ModuleManager::checkRequirements()
|
|||||||
connect( rq,
|
connect( rq,
|
||||||
&RequirementsChecker::done,
|
&RequirementsChecker::done,
|
||||||
this,
|
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 );
|
QTimer::singleShot( 0, rq, &RequirementsChecker::run );
|
||||||
}
|
}
|
||||||
|
@ -12,46 +12,108 @@
|
|||||||
|
|
||||||
#include "utils/CalamaresUtilsGui.h"
|
#include "utils/CalamaresUtilsGui.h"
|
||||||
|
|
||||||
#include "3rdparty/waitingspinnerwidget.h"
|
|
||||||
|
|
||||||
#include <QBoxLayout>
|
#include <QBoxLayout>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
WaitingWidget::WaitingWidget( const QString& text, QWidget* parent )
|
WaitingWidget::WaitingWidget( const QString& text, QWidget* parent )
|
||||||
: QWidget( parent )
|
: WaitingSpinnerWidget( parent, false, false )
|
||||||
{
|
{
|
||||||
QBoxLayout* waitingLayout = new QVBoxLayout;
|
int spnrSize = CalamaresUtils::defaultFontHeight() * 4;
|
||||||
setLayout( waitingLayout );
|
setFixedSize( spnrSize, spnrSize );
|
||||||
waitingLayout->addStretch();
|
setInnerRadius( spnrSize / 2 );
|
||||||
QBoxLayout* pbLayout = new QHBoxLayout;
|
setLineLength( spnrSize / 2 );
|
||||||
waitingLayout->addLayout( pbLayout );
|
setLineWidth( spnrSize / 8 );
|
||||||
pbLayout->addStretch();
|
setAlignment( Qt::AlignmentFlag::AlignBottom );
|
||||||
|
setText( text );
|
||||||
WaitingSpinnerWidget* spnr = new WaitingSpinnerWidget();
|
start();
|
||||||
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 );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WaitingWidget::~WaitingWidget() {}
|
||||||
|
|
||||||
|
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;
|
||||||
|
QTimer* timer = nullptr;
|
||||||
|
|
||||||
|
Private( std::chrono::seconds seconds, QWidget* parent )
|
||||||
|
: duration( seconds )
|
||||||
|
, timer( new QTimer( parent ) )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
CountdownWaitingWidget::CountdownWaitingWidget( std::chrono::seconds duration, 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
|
||||||
|
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 );
|
||||||
|
|
||||||
|
d->timer->setInterval( std::chrono::seconds( 1 ) );
|
||||||
|
connect( d->timer, &QTimer::timeout, this, &CountdownWaitingWidget::tick );
|
||||||
|
}
|
||||||
|
|
||||||
|
CountdownWaitingWidget::~CountdownWaitingWidget()
|
||||||
|
{
|
||||||
|
d->timer->stop();
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
WaitingWidget::setText( const QString& text )
|
CountdownWaitingWidget::setInterval( std::chrono::seconds duration )
|
||||||
{
|
{
|
||||||
m_waitingLabel->setText( text );
|
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();
|
||||||
|
WaitingSpinnerWidget::start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CountdownWaitingWidget::stop()
|
||||||
|
{
|
||||||
|
d->timer->stop();
|
||||||
|
WaitingSpinnerWidget::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() );
|
||||||
|
}
|
||||||
|
setText( QString::number( d->count ) );
|
||||||
|
if ( d->count == 0 )
|
||||||
|
{
|
||||||
|
timeout();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,20 +10,61 @@
|
|||||||
#ifndef WAITINGWIDGET_H
|
#ifndef WAITINGWIDGET_H
|
||||||
#define WAITINGWIDGET_H
|
#define WAITINGWIDGET_H
|
||||||
|
|
||||||
#include <QWidget>
|
#include "widgets/waitingspinnerwidget.h"
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
class QLabel;
|
class QLabel;
|
||||||
|
class QTimer;
|
||||||
|
|
||||||
class WaitingWidget : public QWidget
|
/** @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 WaitingSpinnerWidget
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// Create a WaitingWidget with initial @p text label.
|
||||||
|
explicit WaitingWidget( const QString& text, QWidget* parent = nullptr );
|
||||||
|
~WaitingWidget() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @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 WaitingSpinnerWidget
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit WaitingWidget( const QString& text, QWidget* parent = nullptr );
|
/// Create a countdown widget with a given @p duration
|
||||||
|
explicit CountdownWaitingWidget( std::chrono::seconds duration = std::chrono::seconds( 5 ),
|
||||||
|
QWidget* parent = nullptr );
|
||||||
|
~CountdownWaitingWidget() override;
|
||||||
|
|
||||||
void setText( const QString& text );
|
/// 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:
|
private:
|
||||||
QLabel* m_waitingLabel;
|
struct Private;
|
||||||
|
std::unique_ptr< Private > d;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // WAITINGWIDGET_H
|
#endif // WAITINGWIDGET_H
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
* SPDX-FileCopyrightText: 2012-2014 Alexander Turkin
|
* SPDX-FileCopyrightText: 2012-2014 Alexander Turkin
|
||||||
* SPDX-FileCopyrightText: 2014 William Hallatt
|
* SPDX-FileCopyrightText: 2014 William Hallatt
|
||||||
* SPDX-FileCopyrightText: 2015 Jacob Dawid
|
* SPDX-FileCopyrightText: 2015 Jacob Dawid
|
||||||
|
* SPDX-FileCopyrightText: 2018 huxingyi
|
||||||
* SPDX-License-Identifier: MIT
|
* SPDX-License-Identifier: MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -38,49 +39,41 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
|
||||||
|
static bool isAlignCenter(Qt::AlignmentFlag a)
|
||||||
|
{
|
||||||
|
return a == Qt::AlignmentFlag::AlignVCenter;
|
||||||
|
}
|
||||||
|
|
||||||
WaitingSpinnerWidget::WaitingSpinnerWidget(QWidget *parent,
|
WaitingSpinnerWidget::WaitingSpinnerWidget(QWidget *parent,
|
||||||
bool centerOnParent,
|
bool centerOnParent,
|
||||||
bool disableParentWhenSpinning)
|
bool disableParentWhenSpinning)
|
||||||
: QWidget(parent),
|
: WaitingSpinnerWidget(Qt::WindowModality::NonModal, parent, centerOnParent, disableParentWhenSpinning)
|
||||||
_centerOnParent(centerOnParent),
|
{}
|
||||||
_disableParentWhenSpinning(disableParentWhenSpinning) {
|
|
||||||
initialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
WaitingSpinnerWidget::WaitingSpinnerWidget(Qt::WindowModality modality,
|
WaitingSpinnerWidget::WaitingSpinnerWidget(Qt::WindowModality modality,
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
bool centerOnParent,
|
bool centerOnParent,
|
||||||
bool disableParentWhenSpinning)
|
bool disableParentWhenSpinning)
|
||||||
: QWidget(parent, Qt::Dialog | Qt::FramelessWindowHint),
|
: QWidget(parent, modality == Qt::WindowModality::NonModal ? Qt::WindowFlags() : Qt::Dialog | Qt::FramelessWindowHint),
|
||||||
_centerOnParent(centerOnParent),
|
_centerOnParent(centerOnParent),
|
||||||
_disableParentWhenSpinning(disableParentWhenSpinning){
|
_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() {
|
|
||||||
_color = 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);
|
_timer = new QTimer(this);
|
||||||
connect(_timer, SIGNAL(timeout()), this, SLOT(rotate()));
|
connect(_timer, SIGNAL(timeout()), this, SLOT(rotate()));
|
||||||
updateSize();
|
updateSize();
|
||||||
updateTimer();
|
updateTimer();
|
||||||
hide();
|
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 *) {
|
void WaitingSpinnerWidget::paintEvent(QPaintEvent *) {
|
||||||
@ -98,6 +91,7 @@ void WaitingSpinnerWidget::paintEvent(QPaintEvent *) {
|
|||||||
painter.save();
|
painter.save();
|
||||||
painter.translate(_innerRadius + _lineLength,
|
painter.translate(_innerRadius + _lineLength,
|
||||||
_innerRadius + _lineLength);
|
_innerRadius + _lineLength);
|
||||||
|
painter.translate((width() - _imageSize.width()) / 2, 0);
|
||||||
qreal rotateAngle =
|
qreal rotateAngle =
|
||||||
static_cast<qreal>(360 * i) / static_cast<qreal>(_numberOfLines);
|
static_cast<qreal>(360 * i) / static_cast<qreal>(_numberOfLines);
|
||||||
painter.rotate(rotateAngle);
|
painter.rotate(rotateAngle);
|
||||||
@ -114,6 +108,17 @@ void WaitingSpinnerWidget::paintEvent(QPaintEvent *) {
|
|||||||
_roundness, Qt::RelativeSize);
|
_roundness, Qt::RelativeSize);
|
||||||
painter.restore();
|
painter.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!_text.isEmpty()) {
|
||||||
|
painter.setPen(QPen(_textColor));
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WaitingSpinnerWidget::start() {
|
void WaitingSpinnerWidget::start() {
|
||||||
@ -166,39 +171,58 @@ void WaitingSpinnerWidget::setInnerRadius(int radius) {
|
|||||||
updateSize();
|
updateSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
QColor WaitingSpinnerWidget::color() {
|
void WaitingSpinnerWidget::setText(const QString& text) {
|
||||||
|
_text = text;
|
||||||
|
updateSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WaitingSpinnerWidget::setAlignment(Qt::AlignmentFlag align)
|
||||||
|
{
|
||||||
|
_alignment = align;
|
||||||
|
updateSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor WaitingSpinnerWidget::color() const {
|
||||||
return _color;
|
return _color;
|
||||||
}
|
}
|
||||||
|
|
||||||
qreal WaitingSpinnerWidget::roundness() {
|
QColor WaitingSpinnerWidget::textColor() const {
|
||||||
|
return _textColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString WaitingSpinnerWidget::text() const {
|
||||||
|
return _text;
|
||||||
|
}
|
||||||
|
|
||||||
|
qreal WaitingSpinnerWidget::roundness() const {
|
||||||
return _roundness;
|
return _roundness;
|
||||||
}
|
}
|
||||||
|
|
||||||
qreal WaitingSpinnerWidget::minimumTrailOpacity() {
|
qreal WaitingSpinnerWidget::minimumTrailOpacity() const {
|
||||||
return _minimumTrailOpacity;
|
return _minimumTrailOpacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
qreal WaitingSpinnerWidget::trailFadePercentage() {
|
qreal WaitingSpinnerWidget::trailFadePercentage() const {
|
||||||
return _trailFadePercentage;
|
return _trailFadePercentage;
|
||||||
}
|
}
|
||||||
|
|
||||||
qreal WaitingSpinnerWidget::revolutionsPersSecond() {
|
qreal WaitingSpinnerWidget::revolutionsPersSecond() const {
|
||||||
return _revolutionsPerSecond;
|
return _revolutionsPerSecond;
|
||||||
}
|
}
|
||||||
|
|
||||||
int WaitingSpinnerWidget::numberOfLines() {
|
int WaitingSpinnerWidget::numberOfLines() const {
|
||||||
return _numberOfLines;
|
return _numberOfLines;
|
||||||
}
|
}
|
||||||
|
|
||||||
int WaitingSpinnerWidget::lineLength() {
|
int WaitingSpinnerWidget::lineLength() const {
|
||||||
return _lineLength;
|
return _lineLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
int WaitingSpinnerWidget::lineWidth() {
|
int WaitingSpinnerWidget::lineWidth() const {
|
||||||
return _lineWidth;
|
return _lineWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
int WaitingSpinnerWidget::innerRadius() {
|
int WaitingSpinnerWidget::innerRadius() const {
|
||||||
return _innerRadius;
|
return _innerRadius;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -214,6 +238,10 @@ void WaitingSpinnerWidget::setColor(QColor color) {
|
|||||||
_color = color;
|
_color = color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WaitingSpinnerWidget::setTextColor(QColor color) {
|
||||||
|
_textColor = color;
|
||||||
|
}
|
||||||
|
|
||||||
void WaitingSpinnerWidget::setRevolutionsPerSecond(qreal revolutionsPerSecond) {
|
void WaitingSpinnerWidget::setRevolutionsPerSecond(qreal revolutionsPerSecond) {
|
||||||
_revolutionsPerSecond = revolutionsPerSecond;
|
_revolutionsPerSecond = revolutionsPerSecond;
|
||||||
updateTimer();
|
updateTimer();
|
||||||
@ -237,7 +265,14 @@ void WaitingSpinnerWidget::rotate() {
|
|||||||
|
|
||||||
void WaitingSpinnerWidget::updateSize() {
|
void WaitingSpinnerWidget::updateSize() {
|
||||||
int size = (_innerRadius + _lineLength) * 2;
|
int size = (_innerRadius + _lineLength) * 2;
|
||||||
setFixedSize(size, size);
|
_imageSize = QSize(size, size);
|
||||||
|
if (_text.isEmpty() || isAlignCenter(alignment())) {
|
||||||
|
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() {
|
void WaitingSpinnerWidget::updateTimer() {
|
@ -2,6 +2,7 @@
|
|||||||
* SPDX-FileCopyrightText: 2012-2014 Alexander Turkin
|
* SPDX-FileCopyrightText: 2012-2014 Alexander Turkin
|
||||||
* SPDX-FileCopyrightText: 2014 William Hallatt
|
* SPDX-FileCopyrightText: 2014 William Hallatt
|
||||||
* SPDX-FileCopyrightText: 2015 Jacob Dawid
|
* SPDX-FileCopyrightText: 2015 Jacob Dawid
|
||||||
|
* SPDX-FileCopyrightText: 2018 huxingyi
|
||||||
* SPDX-License-Identifier: MIT
|
* SPDX-License-Identifier: MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -37,28 +38,32 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|||||||
class WaitingSpinnerWidget : public QWidget {
|
class WaitingSpinnerWidget : public QWidget {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
/*! Constructor for "standard" widget behaviour - use this
|
/** @brief Constructor for "standard" widget behaviour
|
||||||
* constructor if you wish to, e.g. embed your widget in another. */
|
*
|
||||||
|
* Use this constructor if you wish to, e.g. embed your widget in another.
|
||||||
|
*/
|
||||||
WaitingSpinnerWidget(QWidget *parent = nullptr,
|
WaitingSpinnerWidget(QWidget *parent = nullptr,
|
||||||
bool centerOnParent = true,
|
bool centerOnParent = true,
|
||||||
bool disableParentWhenSpinning = true);
|
bool disableParentWhenSpinning = true);
|
||||||
|
|
||||||
/*! Constructor - use this constructor to automatically create a modal
|
/** @brief Constructor
|
||||||
* ("blocking") spinner on top of the calling widget/window. If a valid
|
*
|
||||||
* parent widget is provided, "centreOnParent" will ensure that
|
* Use this constructor to automatically create a modal
|
||||||
* QtWaitingSpinner automatically centres itself on it, if not,
|
* ("blocking") spinner on top of the calling widget/window. If a valid
|
||||||
* "centreOnParent" is ignored. */
|
* parent widget is provided, "centreOnParent" will ensure that
|
||||||
|
* QtWaitingSpinner automatically centres itself on it, if not,
|
||||||
|
* @p centerOnParent is ignored.
|
||||||
|
*/
|
||||||
WaitingSpinnerWidget(Qt::WindowModality modality,
|
WaitingSpinnerWidget(Qt::WindowModality modality,
|
||||||
QWidget *parent = nullptr,
|
QWidget *parent = nullptr,
|
||||||
bool centerOnParent = true,
|
bool centerOnParent = true,
|
||||||
bool disableParentWhenSpinning = true);
|
bool disableParentWhenSpinning = true);
|
||||||
|
|
||||||
public slots:
|
WaitingSpinnerWidget(const WaitingSpinnerWidget&) = delete;
|
||||||
void start();
|
WaitingSpinnerWidget& operator=(const WaitingSpinnerWidget&) = delete;
|
||||||
void stop();
|
|
||||||
|
|
||||||
public:
|
|
||||||
void setColor(QColor color);
|
void setColor(QColor color);
|
||||||
|
void setTextColor(QColor color);
|
||||||
void setRoundness(qreal roundness);
|
void setRoundness(qreal roundness);
|
||||||
void setMinimumTrailOpacity(qreal minimumTrailOpacity);
|
void setMinimumTrailOpacity(qreal minimumTrailOpacity);
|
||||||
void setTrailFadePercentage(qreal trail);
|
void setTrailFadePercentage(qreal trail);
|
||||||
@ -67,21 +72,45 @@ public:
|
|||||||
void setLineLength(int length);
|
void setLineLength(int length);
|
||||||
void setLineWidth(int width);
|
void setLineWidth(int width);
|
||||||
void setInnerRadius(int radius);
|
void setInnerRadius(int radius);
|
||||||
void setText(QString text);
|
|
||||||
|
|
||||||
QColor color();
|
/** @brief Sets the text displayed in or below the spinner
|
||||||
qreal roundness();
|
*
|
||||||
qreal minimumTrailOpacity();
|
* If the text is empty, no text is displayed. The text is displayed
|
||||||
qreal trailFadePercentage();
|
* in or below the spinner depending on the value of alignment().
|
||||||
qreal revolutionsPersSecond();
|
* With AlignBottom, the text is displayed below the spinner,
|
||||||
int numberOfLines();
|
* centered horizontally relative to the spinner; any other alignment
|
||||||
int lineLength();
|
* will put the text in the middle of the spinner itself.
|
||||||
int lineWidth();
|
*/
|
||||||
int innerRadius();
|
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);
|
||||||
|
/// 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;
|
||||||
|
QString text() const;
|
||||||
|
Qt::AlignmentFlag alignment() const { return _alignment; }
|
||||||
|
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;
|
bool isSpinning() const;
|
||||||
|
|
||||||
private slots:
|
public Q_SLOTS:
|
||||||
|
void start();
|
||||||
|
void stop();
|
||||||
|
|
||||||
|
private Q_SLOTS:
|
||||||
void rotate();
|
void rotate();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@ -94,29 +123,37 @@ private:
|
|||||||
qreal trailFadePerc, qreal minOpacity,
|
qreal trailFadePerc, qreal minOpacity,
|
||||||
QColor color);
|
QColor color);
|
||||||
|
|
||||||
void initialize();
|
|
||||||
void updateSize();
|
void updateSize();
|
||||||
void updateTimer();
|
void updateTimer();
|
||||||
void updatePosition();
|
void updatePosition();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QColor _color;
|
// PI, leading to a full fade in one whole revolution
|
||||||
qreal _roundness; // 0..100
|
static constexpr const auto radian = 3.14159265358979323846;
|
||||||
qreal _minimumTrailOpacity;
|
|
||||||
qreal _trailFadePercentage;
|
|
||||||
qreal _revolutionsPerSecond;
|
|
||||||
int _numberOfLines;
|
|
||||||
int _lineLength;
|
|
||||||
int _lineWidth;
|
|
||||||
int _innerRadius;
|
|
||||||
|
|
||||||
private:
|
// Spinner-wheel related settings
|
||||||
WaitingSpinnerWidget(const WaitingSpinnerWidget&);
|
QColor _color = Qt::black;
|
||||||
WaitingSpinnerWidget& operator=(const WaitingSpinnerWidget&);
|
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;
|
||||||
|
|
||||||
QTimer *_timer;
|
// Text-related settings
|
||||||
bool _centerOnParent;
|
Qt::AlignmentFlag _alignment = Qt::AlignmentFlag::AlignBottom;
|
||||||
bool _disableParentWhenSpinning;
|
QString _text;
|
||||||
int _currentCounter;
|
QColor _textColor = Qt::black;
|
||||||
bool _isSpinning;
|
|
||||||
|
// Environment settings
|
||||||
|
bool _centerOnParent = true;
|
||||||
|
bool _disableParentWhenSpinning = true;
|
||||||
|
|
||||||
|
// Internal bits
|
||||||
|
QTimer *_timer = nullptr;
|
||||||
|
int _currentCounter = 0;
|
||||||
|
bool _isSpinning = false;
|
||||||
};
|
};
|
@ -58,10 +58,10 @@ Module descriptors for C++ modules **may** have the following key:
|
|||||||
Module descriptors for Python modules **must** 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`)
|
- *script* (the name of the Python script to load, nearly always `main.py`)
|
||||||
|
|
||||||
Module descriptors for process modules **must** have the following key:
|
Module descriptors for process modules **must** have the following key:
|
||||||
- *command* (the command to run)
|
- *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)
|
- *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)
|
- *chroot* (if true, run the command in the target system rather than the host)
|
||||||
Note that process modules are not recommended.
|
Note that process modules are not recommended.
|
||||||
@ -181,23 +181,25 @@ for determining the relative weights there.
|
|||||||
|
|
||||||
|
|
||||||
## Global storage keys
|
## 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.
|
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
|
Key |Source |Consumers|Description
|
||||||
---|---|---|---
|
------------------|----------------|---|---
|
||||||
btrfsSubvolumes|mount|fstab|List of maps containing the mountpoint and btrtfs subvolume
|
btrfsSubvolumes |mount |fstab|List of maps containing the mountpoint and btrtfs subvolume
|
||||||
btrfsRootSubvolume|mount|bootloader, luksopenswaphook|String containing the subvolume mounted at root
|
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
|
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
|
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
|
fullname |users ||The full username (e.g. "Jane Q. Public")
|
||||||
netinstallAdd|packagechooser|netinstall|Data to add to netinstall tree. Same format as netinstall.yaml
|
hostname |users ||A string containing the hostname of the new system
|
||||||
netinstallSelect|packagechooser|netinstall|List of group names to select in the netinstall tree
|
netinstallAdd |packagechooser |netinstall|Data to add to netinstall tree. Same format as netinstall.yaml
|
||||||
partitions|partition, rawfs|numerous modules|List of maps of metadata about each partition
|
netinstallSelect |packagechooser |netinstall|List of group names to select in the netinstall tree
|
||||||
rootMountPoint|mount|numerous modules|A string with the absolute path to the root mountpoint
|
partitions |partition, rawfs|numerous modules|List of maps of metadata about each partition
|
||||||
username|users|networkcfg, plasmainf, preservefiles|A string containing the username of the new user
|
rootMountPoint |mount |numerous modules|A string with the absolute path to the root mountpoint
|
||||||
zfsDatasets|zfs|bootloader, grubcfg, mount|List of maps of zfs datasets including the name and mount information
|
username |users |networkcfg, plasmainf, preservefiles|A string containing the username of the new user
|
||||||
zfsInfo|partition|mount, zfs|List of encrypted zfs partitions and the encription info
|
zfsDatasets |zfs |bootloader, grubcfg, mount|List of maps of zfs datasets including the name and mount information
|
||||||
zfsPoolInfo|zfs|mount, umount|List of maps of zfs pool info including the name and mountpoint
|
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
|
## C++ modules
|
||||||
|
@ -11,7 +11,6 @@
|
|||||||
#include "LocaleViewStep.h"
|
#include "LocaleViewStep.h"
|
||||||
|
|
||||||
#include "LocalePage.h"
|
#include "LocalePage.h"
|
||||||
#include "widgets/WaitingWidget.h"
|
|
||||||
|
|
||||||
#include "GlobalStorage.h"
|
#include "GlobalStorage.h"
|
||||||
#include "JobQueue.h"
|
#include "JobQueue.h"
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
#include "ScanningDialog.h"
|
#include "ScanningDialog.h"
|
||||||
|
|
||||||
#include "3rdparty/waitingspinnerwidget.h"
|
#include "widgets/waitingspinnerwidget.h"
|
||||||
|
|
||||||
#include <QBoxLayout>
|
#include <QBoxLayout>
|
||||||
#include <QFutureWatcher>
|
#include <QFutureWatcher>
|
||||||
|
@ -445,6 +445,15 @@ Config::setFullName( const QString& name )
|
|||||||
if ( name != m_fullName )
|
if ( name != m_fullName )
|
||||||
{
|
{
|
||||||
m_fullName = name;
|
m_fullName = name;
|
||||||
|
Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage();
|
||||||
|
if ( name.isEmpty() )
|
||||||
|
{
|
||||||
|
gs->remove( "fullname" );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gs->insert( "fullname", name );
|
||||||
|
}
|
||||||
emit fullNameChanged( name );
|
emit fullNameChanged( name );
|
||||||
|
|
||||||
// Build login and hostname, if needed
|
// Build login and hostname, if needed
|
||||||
|
@ -65,13 +65,18 @@ CheckerContainer::requirementsComplete( bool ok )
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
layout()->removeWidget( m_waitingWidget );
|
if ( m_waitingWidget )
|
||||||
m_waitingWidget->deleteLater();
|
{
|
||||||
m_waitingWidget = nullptr; // Don't delete in destructor
|
layout()->removeWidget( m_waitingWidget );
|
||||||
|
m_waitingWidget->deleteLater();
|
||||||
m_checkerWidget = new ResultsListWidget( m_config, this );
|
m_waitingWidget = nullptr; // Don't delete in destructor
|
||||||
m_checkerWidget->setObjectName( "requirementsChecker" );
|
}
|
||||||
layout()->addWidget( m_checkerWidget );
|
if ( !m_checkerWidget )
|
||||||
|
{
|
||||||
|
m_checkerWidget = new ResultsListWidget( m_config, this );
|
||||||
|
m_checkerWidget->setObjectName( "requirementsChecker" );
|
||||||
|
layout()->addWidget( m_checkerWidget );
|
||||||
|
}
|
||||||
|
|
||||||
m_verdict = ok;
|
m_verdict = ok;
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,9 @@
|
|||||||
#include "CheckerContainer.h"
|
#include "CheckerContainer.h"
|
||||||
#include "partman_devices.h"
|
#include "partman_devices.h"
|
||||||
|
|
||||||
|
#include "CalamaresVersion.h" // For development-or-not
|
||||||
|
#include "GlobalStorage.h"
|
||||||
|
#include "JobQueue.h"
|
||||||
#include "Settings.h"
|
#include "Settings.h"
|
||||||
#include "modulesystem/Requirement.h"
|
#include "modulesystem/Requirement.h"
|
||||||
#include "network/Manager.h"
|
#include "network/Manager.h"
|
||||||
@ -26,9 +29,6 @@
|
|||||||
#include "utils/Variant.h"
|
#include "utils/Variant.h"
|
||||||
#include "widgets/WaitingWidget.h"
|
#include "widgets/WaitingWidget.h"
|
||||||
|
|
||||||
#include "GlobalStorage.h"
|
|
||||||
#include "JobQueue.h"
|
|
||||||
|
|
||||||
#include <QDBusConnection>
|
#include <QDBusConnection>
|
||||||
#include <QDBusInterface>
|
#include <QDBusInterface>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
@ -148,28 +148,29 @@ GeneralRequirements::checkRequirements()
|
|||||||
Calamares::RequirementsList checkEntries;
|
Calamares::RequirementsList checkEntries;
|
||||||
foreach ( const QString& entry, m_entriesToCheck )
|
foreach ( const QString& entry, m_entriesToCheck )
|
||||||
{
|
{
|
||||||
|
const bool required = m_entriesToRequire.contains( entry );
|
||||||
if ( entry == "storage" )
|
if ( entry == "storage" )
|
||||||
{
|
{
|
||||||
checkEntries.append(
|
checkEntries.append(
|
||||||
{ entry,
|
{ entry,
|
||||||
[req = m_requiredStorageGiB] { return tr( "has at least %1 GiB available drive space" ).arg( req ); },
|
[ req = m_requiredStorageGiB ]
|
||||||
[req = m_requiredStorageGiB] {
|
{ return tr( "has at least %1 GiB available drive space" ).arg( req ); },
|
||||||
return tr( "There is not enough drive space. At least %1 GiB is required." ).arg( req );
|
[ req = m_requiredStorageGiB ]
|
||||||
},
|
{ return tr( "There is not enough drive space. At least %1 GiB is required." ).arg( req ); },
|
||||||
enoughStorage,
|
enoughStorage,
|
||||||
m_entriesToRequire.contains( entry ) } );
|
required } );
|
||||||
}
|
}
|
||||||
else if ( entry == "ram" )
|
else if ( entry == "ram" )
|
||||||
{
|
{
|
||||||
checkEntries.append(
|
checkEntries.append(
|
||||||
{ entry,
|
{ entry,
|
||||||
[req = m_requiredRamGiB] { return tr( "has at least %1 GiB working memory" ).arg( req ); },
|
[ req = m_requiredRamGiB ] { return tr( "has at least %1 GiB working memory" ).arg( req ); },
|
||||||
[req = m_requiredRamGiB] {
|
[ req = m_requiredRamGiB ] {
|
||||||
return tr( "The system does not have enough working memory. At least %1 GiB is required." )
|
return tr( "The system does not have enough working memory. At least %1 GiB is required." )
|
||||||
.arg( req );
|
.arg( req );
|
||||||
},
|
},
|
||||||
enoughRam,
|
enoughRam,
|
||||||
m_entriesToRequire.contains( entry ) } );
|
required } );
|
||||||
}
|
}
|
||||||
else if ( entry == "power" )
|
else if ( entry == "power" )
|
||||||
{
|
{
|
||||||
@ -177,7 +178,7 @@ GeneralRequirements::checkRequirements()
|
|||||||
[] { return tr( "is plugged in to a power source" ); },
|
[] { return tr( "is plugged in to a power source" ); },
|
||||||
[] { return tr( "The system is not plugged in to a power source." ); },
|
[] { return tr( "The system is not plugged in to a power source." ); },
|
||||||
hasPower,
|
hasPower,
|
||||||
m_entriesToRequire.contains( entry ) } );
|
required } );
|
||||||
}
|
}
|
||||||
else if ( entry == "internet" )
|
else if ( entry == "internet" )
|
||||||
{
|
{
|
||||||
@ -185,32 +186,65 @@ GeneralRequirements::checkRequirements()
|
|||||||
[] { return tr( "is connected to the Internet" ); },
|
[] { return tr( "is connected to the Internet" ); },
|
||||||
[] { return tr( "The system is not connected to the Internet." ); },
|
[] { return tr( "The system is not connected to the Internet." ); },
|
||||||
hasInternet,
|
hasInternet,
|
||||||
m_entriesToRequire.contains( entry ) } );
|
required } );
|
||||||
}
|
}
|
||||||
else if ( entry == "root" )
|
else if ( entry == "root" )
|
||||||
{
|
{
|
||||||
checkEntries.append( { entry,
|
checkEntries.append( { entry,
|
||||||
[] { return tr( "is running the installer as an administrator (root)" ); },
|
[] { return tr( "is running the installer as an administrator (root)" ); },
|
||||||
[] {
|
[]
|
||||||
|
{
|
||||||
return Calamares::Settings::instance()->isSetupMode()
|
return Calamares::Settings::instance()->isSetupMode()
|
||||||
? tr( "The setup program is not running with administrator rights." )
|
? tr( "The setup program is not running with administrator rights." )
|
||||||
: tr( "The installer is not running with administrator rights." );
|
: tr( "The installer is not running with administrator rights." );
|
||||||
},
|
},
|
||||||
isRoot,
|
isRoot,
|
||||||
m_entriesToRequire.contains( entry ) } );
|
required } );
|
||||||
}
|
}
|
||||||
else if ( entry == "screen" )
|
else if ( entry == "screen" )
|
||||||
{
|
{
|
||||||
checkEntries.append( { entry,
|
checkEntries.append( { entry,
|
||||||
[] { return tr( "has a screen large enough to show the whole installer" ); },
|
[] { return tr( "has a screen large enough to show the whole installer" ); },
|
||||||
[] {
|
[]
|
||||||
|
{
|
||||||
return Calamares::Settings::instance()->isSetupMode()
|
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 setup program." )
|
||||||
: tr( "The screen is too small to display the installer." );
|
: tr( "The screen is too small to display the installer." );
|
||||||
},
|
},
|
||||||
enoughScreen,
|
enoughScreen,
|
||||||
false } );
|
required } );
|
||||||
}
|
}
|
||||||
|
#ifdef CALAMARES_VERSION_RC
|
||||||
|
if ( entry == "false" )
|
||||||
|
{
|
||||||
|
checkEntries.append( { entry,
|
||||||
|
[] { return tr( "is always false" ); },
|
||||||
|
[] { return tr( "The computer says no." ); },
|
||||||
|
false,
|
||||||
|
required } );
|
||||||
|
}
|
||||||
|
if ( entry == "true" )
|
||||||
|
{
|
||||||
|
checkEntries.append( { entry,
|
||||||
|
[] { return tr( "is always true" ); },
|
||||||
|
[] { return tr( "The computer says yes." ); },
|
||||||
|
true,
|
||||||
|
required } );
|
||||||
|
}
|
||||||
|
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,
|
||||||
|
required } );
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
return checkEntries;
|
return checkEntries;
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
#include "utils/Logger.h"
|
#include "utils/Logger.h"
|
||||||
#include "utils/Retranslator.h"
|
#include "utils/Retranslator.h"
|
||||||
#include "widgets/FixedAspectRatioLabel.h"
|
#include "widgets/FixedAspectRatioLabel.h"
|
||||||
|
#include "widgets/WaitingWidget.h"
|
||||||
|
|
||||||
#include <QAbstractButton>
|
#include <QAbstractButton>
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
@ -110,10 +111,11 @@ ResultsListDialog::ResultsListDialog( const Calamares::RequirementsModel& model,
|
|||||||
m_title = new QLabel( this );
|
m_title = new QLabel( this );
|
||||||
m_title->setObjectName( "resultDialogTitle" );
|
m_title->setObjectName( "resultDialogTitle" );
|
||||||
|
|
||||||
createResultWidgets(
|
createResultWidgets( entriesLayout,
|
||||||
entriesLayout, m_resultWidgets, model, []( const Calamares::RequirementsModel& m, QModelIndex i ) {
|
m_resultWidgets,
|
||||||
return m.data( i, Calamares::RequirementsModel::HasDetails ).toBool();
|
model,
|
||||||
} );
|
[]( const Calamares::RequirementsModel& m, QModelIndex i )
|
||||||
|
{ return m.data( i, Calamares::RequirementsModel::HasDetails ).toBool(); } );
|
||||||
|
|
||||||
QDialogButtonBox* buttonBox = new QDialogButtonBox( QDialogButtonBox::Close, Qt::Horizontal, this );
|
QDialogButtonBox* buttonBox = new QDialogButtonBox( QDialogButtonBox::Close, Qt::Horizontal, this );
|
||||||
buttonBox->setObjectName( "resultDialogButtons" );
|
buttonBox->setObjectName( "resultDialogButtons" );
|
||||||
@ -154,47 +156,117 @@ ResultsListWidget::ResultsListWidget( Config* config, QWidget* parent )
|
|||||||
{
|
{
|
||||||
setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
|
setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
|
||||||
|
|
||||||
QBoxLayout* mainLayout = new QVBoxLayout;
|
m_mainLayout = new QVBoxLayout;
|
||||||
QBoxLayout* entriesLayout = new QVBoxLayout;
|
m_entriesLayout = new QVBoxLayout;
|
||||||
|
|
||||||
setLayout( mainLayout );
|
setLayout( m_mainLayout );
|
||||||
|
|
||||||
int paddingSize = qBound( 32, CalamaresUtils::defaultFontHeight() * 4, 128 );
|
int paddingSize = qBound( 32, CalamaresUtils::defaultFontHeight() * 4, 128 );
|
||||||
|
|
||||||
QHBoxLayout* spacerLayout = new QHBoxLayout;
|
QHBoxLayout* spacerLayout = new QHBoxLayout;
|
||||||
mainLayout->addLayout( spacerLayout );
|
m_mainLayout->addLayout( spacerLayout );
|
||||||
spacerLayout->addSpacing( paddingSize );
|
spacerLayout->addSpacing( paddingSize );
|
||||||
spacerLayout->addLayout( entriesLayout );
|
spacerLayout->addLayout( m_entriesLayout );
|
||||||
spacerLayout->addSpacing( paddingSize );
|
spacerLayout->addSpacing( paddingSize );
|
||||||
CalamaresUtils::unmarginLayout( spacerLayout );
|
CalamaresUtils::unmarginLayout( spacerLayout );
|
||||||
|
|
||||||
auto* explanation = new QLabel( m_config->warningMessage() );
|
QHBoxLayout* explanationLayout = new QHBoxLayout;
|
||||||
explanation->setWordWrap( true );
|
m_explanation = new QLabel( m_config->warningMessage() );
|
||||||
explanation->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred );
|
m_explanation->setWordWrap( true );
|
||||||
explanation->setOpenExternalLinks( false );
|
m_explanation->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred );
|
||||||
explanation->setObjectName( "resultsExplanation" );
|
m_explanation->setOpenExternalLinks( false );
|
||||||
entriesLayout->addWidget( explanation );
|
m_explanation->setObjectName( "resultsExplanation" );
|
||||||
|
explanationLayout->addWidget( m_explanation );
|
||||||
|
m_countdown = new CountdownWaitingWidget;
|
||||||
|
explanationLayout->addWidget( m_countdown );
|
||||||
|
m_countdown->start();
|
||||||
|
|
||||||
connect( config, &Config::warningMessageChanged, explanation, &QLabel::setText );
|
m_entriesLayout->addLayout( explanationLayout );
|
||||||
connect( explanation, &QLabel::linkActivated, this, &ResultsListWidget::linkClicked );
|
m_entriesLayout->insertSpacing( 1, CalamaresUtils::defaultFontHeight() / 2 );
|
||||||
|
m_mainLayout->addStretch();
|
||||||
|
|
||||||
|
requirementsChanged();
|
||||||
|
|
||||||
|
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,
|
||||||
|
this,
|
||||||
|
&ResultsListWidget::requirementsChanged );
|
||||||
|
|
||||||
|
CALAMARES_RETRANSLATE_SLOT( &ResultsListWidget::retranslate );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
ResultsListWidget::linkClicked( const QString& link )
|
||||||
|
{
|
||||||
|
if ( link == "#details" )
|
||||||
|
{
|
||||||
|
auto* dialog = new ResultsListDialog( *( m_config->requirementsModel() ), this );
|
||||||
|
dialog->exec();
|
||||||
|
dialog->deleteLater();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ResultsListWidget::retranslate()
|
||||||
|
{
|
||||||
|
const auto& model = *( m_config->requirementsModel() );
|
||||||
|
// 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 ] )
|
||||||
|
{
|
||||||
|
m_resultWidgets[ i ]->setText(
|
||||||
|
model.data( model.index( i ), Calamares::RequirementsModel::NegatedText ).toString() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ResultsListWidget::requirementsChanged()
|
||||||
|
{
|
||||||
|
if ( !isModelFilled() )
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Check that all are satisfied (gives warnings if not) and
|
// Check that all are satisfied (gives warnings if not) and
|
||||||
// all *mandatory* entries are satisfied (gives errors if not).
|
// all *mandatory* entries are satisfied (gives errors if not).
|
||||||
|
|
||||||
const bool requirementsSatisfied = config->requirementsModel()->satisfiedRequirements();
|
const bool requirementsSatisfied = m_config->requirementsModel()->satisfiedRequirements();
|
||||||
auto isUnSatisfied = []( const Calamares::RequirementsModel& m, QModelIndex i ) {
|
auto isUnSatisfied = []( const Calamares::RequirementsModel& m, QModelIndex i )
|
||||||
return !m.data( i, Calamares::RequirementsModel::Satisfied ).toBool();
|
{ return !m.data( i, Calamares::RequirementsModel::Satisfied ).toBool(); };
|
||||||
};
|
|
||||||
|
|
||||||
createResultWidgets( entriesLayout, m_resultWidgets, *( config->requirementsModel() ), isUnSatisfied );
|
|
||||||
|
std::for_each( m_resultWidgets.begin(),
|
||||||
|
m_resultWidgets.end(),
|
||||||
|
[]( QWidget* w )
|
||||||
|
{
|
||||||
|
if ( w )
|
||||||
|
{
|
||||||
|
w->deleteLater();
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
if ( !requirementsSatisfied )
|
if ( !requirementsSatisfied )
|
||||||
{
|
{
|
||||||
entriesLayout->insertSpacing( 1, CalamaresUtils::defaultFontHeight() / 2 );
|
createResultWidgets( m_entriesLayout, m_resultWidgets, *( m_config->requirementsModel() ), isUnSatisfied );
|
||||||
mainLayout->addStretch();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
m_countdown->stop();
|
||||||
|
m_countdown->hide();
|
||||||
if ( !Calamares::Branding::instance()->imagePath( Calamares::Branding::ProductWelcome ).isEmpty() )
|
if ( !Calamares::Branding::instance()->imagePath( Calamares::Branding::ProductWelcome ).isEmpty() )
|
||||||
{
|
{
|
||||||
QPixmap theImage
|
QPixmap theImage
|
||||||
@ -218,40 +290,26 @@ ResultsListWidget::ResultsListWidget( Config* config, QWidget* parent )
|
|||||||
imageLabel->setAlignment( Qt::AlignCenter );
|
imageLabel->setAlignment( Qt::AlignCenter );
|
||||||
imageLabel->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
|
imageLabel->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
|
||||||
imageLabel->setObjectName( "welcomeLogo" );
|
imageLabel->setObjectName( "welcomeLogo" );
|
||||||
mainLayout->addWidget( imageLabel );
|
m_mainLayout->addWidget( imageLabel );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
explanation->setAlignment( Qt::AlignCenter );
|
m_explanation->setAlignment( Qt::AlignCenter );
|
||||||
}
|
}
|
||||||
|
|
||||||
CALAMARES_RETRANSLATE_SLOT( &ResultsListWidget::retranslate );
|
retranslate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
void
|
ResultsListWidget::isModelFilled()
|
||||||
ResultsListWidget::linkClicked( const QString& link )
|
|
||||||
{
|
{
|
||||||
if ( link == "#details" )
|
if ( m_config->requirementsModel()->count() < m_requirementsSeen )
|
||||||
{
|
{
|
||||||
auto* dialog = new ResultsListDialog( *( m_config->requirementsModel() ), this );
|
return false;
|
||||||
dialog->exec();
|
|
||||||
dialog->deleteLater();
|
|
||||||
}
|
}
|
||||||
|
m_requirementsSeen = m_config->requirementsModel()->count();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
ResultsListWidget::retranslate()
|
|
||||||
{
|
|
||||||
const auto& model = *( m_config->requirementsModel() );
|
|
||||||
for ( auto i = 0; i < model.count(); i++ )
|
|
||||||
{
|
|
||||||
if ( m_resultWidgets[ i ] )
|
|
||||||
{
|
|
||||||
m_resultWidgets[ i ]->setText(
|
|
||||||
model.data( model.index( i ), Calamares::RequirementsModel::NegatedText ).toString() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#include "utils/moc-warnings.h"
|
#include "utils/moc-warnings.h"
|
||||||
|
|
||||||
|
@ -17,7 +17,11 @@
|
|||||||
|
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
|
||||||
|
class CountdownWaitingWidget;
|
||||||
|
|
||||||
|
class QBoxLayout;
|
||||||
class QLabel;
|
class QLabel;
|
||||||
|
|
||||||
class ResultsListWidget : public QWidget
|
class ResultsListWidget : public QWidget
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -27,10 +31,38 @@ public:
|
|||||||
private:
|
private:
|
||||||
/// @brief A link in the explanatory text has been clicked
|
/// @brief A link in the explanatory text has been clicked
|
||||||
void linkClicked( const QString& link );
|
void linkClicked( const QString& link );
|
||||||
|
/// @brief The model of requirements changed
|
||||||
|
void requirementsChanged();
|
||||||
|
|
||||||
void retranslate();
|
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;
|
Config* m_config = nullptr;
|
||||||
|
|
||||||
|
// 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;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // CHECKER_RESULTSLISTWIDGET_H
|
#endif // CHECKER_RESULTSLISTWIDGET_H
|
||||||
|
@ -64,6 +64,14 @@ requirements:
|
|||||||
# the host system satisfying the condition.
|
# the host system satisfying the condition.
|
||||||
#
|
#
|
||||||
# This sample file lists all the conditions that are known.
|
# 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:
|
check:
|
||||||
- storage
|
- storage
|
||||||
- ram
|
- ram
|
||||||
@ -71,6 +79,9 @@ requirements:
|
|||||||
- internet
|
- internet
|
||||||
- root
|
- root
|
||||||
- screen
|
- screen
|
||||||
|
- false
|
||||||
|
- true
|
||||||
|
- snark
|
||||||
# List conditions that **must** be satisfied (from the list
|
# List conditions that **must** be satisfied (from the list
|
||||||
# of conditions, above) for installation to proceed.
|
# of conditions, above) for installation to proceed.
|
||||||
# If any of these conditions are not met, the user cannot
|
# If any of these conditions are not met, the user cannot
|
||||||
|
Loading…
Reference in New Issue
Block a user