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
|
||||
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):
|
||||
|
@ -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 );
|
||||
|
@ -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 )
|
||||
{
|
||||
|
@ -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 );
|
||||
|
||||
|
@ -30,16 +30,11 @@ 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)
|
||||
endif()
|
||||
|
@ -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 );
|
||||
}
|
||||
|
@ -12,46 +12,108 @@
|
||||
|
||||
#include "utils/CalamaresUtilsGui.h"
|
||||
|
||||
#include "3rdparty/waitingspinnerwidget.h"
|
||||
|
||||
#include <QBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QTimer>
|
||||
|
||||
WaitingWidget::WaitingWidget( const QString& text, QWidget* parent )
|
||||
: QWidget( parent )
|
||||
: WaitingSpinnerWidget( parent, false, false )
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
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
|
||||
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
|
||||
#define WAITINGWIDGET_H
|
||||
|
||||
#include <QWidget>
|
||||
#include "widgets/waitingspinnerwidget.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
|
||||
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
|
||||
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:
|
||||
QLabel* m_waitingLabel;
|
||||
struct Private;
|
||||
std::unique_ptr< Private > d;
|
||||
};
|
||||
|
||||
#endif // WAITINGWIDGET_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
|
||||
*/
|
||||
|
||||
@ -38,49 +39,41 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
#include <QPainter>
|
||||
#include <QTimer>
|
||||
|
||||
static bool isAlignCenter(Qt::AlignmentFlag a)
|
||||
{
|
||||
return a == Qt::AlignmentFlag::AlignVCenter;
|
||||
}
|
||||
|
||||
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() {
|
||||
_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;
|
||||
|
||||
_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 *) {
|
||||
@ -98,6 +91,7 @@ void WaitingSpinnerWidget::paintEvent(QPaintEvent *) {
|
||||
painter.save();
|
||||
painter.translate(_innerRadius + _lineLength,
|
||||
_innerRadius + _lineLength);
|
||||
painter.translate((width() - _imageSize.width()) / 2, 0);
|
||||
qreal rotateAngle =
|
||||
static_cast<qreal>(360 * i) / static_cast<qreal>(_numberOfLines);
|
||||
painter.rotate(rotateAngle);
|
||||
@ -114,6 +108,17 @@ void WaitingSpinnerWidget::paintEvent(QPaintEvent *) {
|
||||
_roundness, Qt::RelativeSize);
|
||||
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() {
|
||||
@ -166,39 +171,58 @@ void WaitingSpinnerWidget::setInnerRadius(int radius) {
|
||||
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;
|
||||
}
|
||||
|
||||
qreal WaitingSpinnerWidget::roundness() {
|
||||
QColor WaitingSpinnerWidget::textColor() const {
|
||||
return _textColor;
|
||||
}
|
||||
|
||||
QString WaitingSpinnerWidget::text() const {
|
||||
return _text;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -214,6 +238,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 +265,14 @@ void WaitingSpinnerWidget::rotate() {
|
||||
|
||||
void WaitingSpinnerWidget::updateSize() {
|
||||
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() {
|
@ -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
|
||||
*/
|
||||
|
||||
@ -37,28 +38,32 @@ 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);
|
||||
void setMinimumTrailOpacity(qreal minimumTrailOpacity);
|
||||
void setTrailFadePercentage(qreal trail);
|
||||
@ -67,21 +72,45 @@ public:
|
||||
void setLineLength(int length);
|
||||
void setLineWidth(int width);
|
||||
void setInnerRadius(int radius);
|
||||
void setText(QString text);
|
||||
|
||||
QColor color();
|
||||
qreal roundness();
|
||||
qreal minimumTrailOpacity();
|
||||
qreal trailFadePercentage();
|
||||
qreal revolutionsPersSecond();
|
||||
int numberOfLines();
|
||||
int lineLength();
|
||||
int lineWidth();
|
||||
int innerRadius();
|
||||
/** @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);
|
||||
/// 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;
|
||||
|
||||
private slots:
|
||||
public Q_SLOTS:
|
||||
void start();
|
||||
void stop();
|
||||
|
||||
private Q_SLOTS:
|
||||
void rotate();
|
||||
|
||||
protected:
|
||||
@ -94,29 +123,37 @@ private:
|
||||
qreal trailFadePerc, qreal minOpacity,
|
||||
QColor color);
|
||||
|
||||
void initialize();
|
||||
void updateSize();
|
||||
void updateTimer();
|
||||
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;
|
||||
|
||||
private:
|
||||
WaitingSpinnerWidget(const WaitingSpinnerWidget&);
|
||||
WaitingSpinnerWidget& operator=(const WaitingSpinnerWidget&);
|
||||
// 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;
|
||||
|
||||
QTimer *_timer;
|
||||
bool _centerOnParent;
|
||||
bool _disableParentWhenSpinning;
|
||||
int _currentCounter;
|
||||
bool _isSpinning;
|
||||
// Text-related settings
|
||||
Qt::AlignmentFlag _alignment = Qt::AlignmentFlag::AlignBottom;
|
||||
QString _text;
|
||||
QColor _textColor = Qt::black;
|
||||
|
||||
// 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:
|
||||
- *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
|
||||
|
@ -11,7 +11,6 @@
|
||||
#include "LocaleViewStep.h"
|
||||
|
||||
#include "LocalePage.h"
|
||||
#include "widgets/WaitingWidget.h"
|
||||
|
||||
#include "GlobalStorage.h"
|
||||
#include "JobQueue.h"
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
#include "ScanningDialog.h"
|
||||
|
||||
#include "3rdparty/waitingspinnerwidget.h"
|
||||
#include "widgets/waitingspinnerwidget.h"
|
||||
|
||||
#include <QBoxLayout>
|
||||
#include <QFutureWatcher>
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 <QDBusConnection>
|
||||
#include <QDBusInterface>
|
||||
#include <QDir>
|
||||
@ -148,28 +148,29 @@ GeneralRequirements::checkRequirements()
|
||||
Calamares::RequirementsList checkEntries;
|
||||
foreach ( const QString& entry, m_entriesToCheck )
|
||||
{
|
||||
const bool required = m_entriesToRequire.contains( entry );
|
||||
if ( entry == "storage" )
|
||||
{
|
||||
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 ) } );
|
||||
required } );
|
||||
}
|
||||
else if ( entry == "ram" )
|
||||
{
|
||||
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 );
|
||||
},
|
||||
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,32 +186,65 @@ 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" )
|
||||
{
|
||||
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." );
|
||||
},
|
||||
isRoot,
|
||||
m_entriesToRequire.contains( entry ) } );
|
||||
required } );
|
||||
}
|
||||
else if ( entry == "screen" )
|
||||
{
|
||||
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." );
|
||||
},
|
||||
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;
|
||||
}
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "utils/Logger.h"
|
||||
#include "utils/Retranslator.h"
|
||||
#include "widgets/FixedAspectRatioLabel.h"
|
||||
#include "widgets/WaitingWidget.h"
|
||||
|
||||
#include <QAbstractButton>
|
||||
#include <QDialog>
|
||||
@ -110,10 +111,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,47 +156,117 @@ 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 );
|
||||
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" );
|
||||
explanationLayout->addWidget( m_explanation );
|
||||
m_countdown = new CountdownWaitingWidget;
|
||||
explanationLayout->addWidget( m_countdown );
|
||||
m_countdown->start();
|
||||
|
||||
connect( config, &Config::warningMessageChanged, explanation, &QLabel::setText );
|
||||
connect( explanation, &QLabel::linkActivated, this, &ResultsListWidget::linkClicked );
|
||||
m_entriesLayout->addLayout( explanationLayout );
|
||||
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
|
||||
// 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();
|
||||
};
|
||||
const bool requirementsSatisfied = m_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 );
|
||||
|
||||
std::for_each( m_resultWidgets.begin(),
|
||||
m_resultWidgets.end(),
|
||||
[]( QWidget* w )
|
||||
{
|
||||
if ( w )
|
||||
{
|
||||
w->deleteLater();
|
||||
}
|
||||
} );
|
||||
|
||||
if ( !requirementsSatisfied )
|
||||
{
|
||||
entriesLayout->insertSpacing( 1, CalamaresUtils::defaultFontHeight() / 2 );
|
||||
mainLayout->addStretch();
|
||||
createResultWidgets( m_entriesLayout, m_resultWidgets, *( m_config->requirementsModel() ), isUnSatisfied );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_countdown->stop();
|
||||
m_countdown->hide();
|
||||
if ( !Calamares::Branding::instance()->imagePath( Calamares::Branding::ProductWelcome ).isEmpty() )
|
||||
{
|
||||
QPixmap theImage
|
||||
@ -218,40 +290,26 @@ ResultsListWidget::ResultsListWidget( Config* config, QWidget* parent )
|
||||
imageLabel->setAlignment( Qt::AlignCenter );
|
||||
imageLabel->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ResultsListWidget::linkClicked( const QString& link )
|
||||
bool
|
||||
ResultsListWidget::isModelFilled()
|
||||
{
|
||||
if ( link == "#details" )
|
||||
if ( m_config->requirementsModel()->count() < m_requirementsSeen )
|
||||
{
|
||||
auto* dialog = new ResultsListDialog( *( m_config->requirementsModel() ), this );
|
||||
dialog->exec();
|
||||
dialog->deleteLater();
|
||||
return false;
|
||||
}
|
||||
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"
|
||||
|
||||
|
@ -17,7 +17,11 @@
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
class CountdownWaitingWidget;
|
||||
|
||||
class QBoxLayout;
|
||||
class QLabel;
|
||||
|
||||
class ResultsListWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -27,10 +31,38 @@ 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
|
||||
/** @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;
|
||||
CountdownWaitingWidget* m_countdown = nullptr;
|
||||
QBoxLayout* m_mainLayout = nullptr;
|
||||
QBoxLayout* m_entriesLayout = nullptr;
|
||||
int m_requirementsSeen = 0;
|
||||
};
|
||||
|
||||
#endif // CHECKER_RESULTSLISTWIDGET_H
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user