diff --git a/src/modules/welcome/checker/CheckerContainer.cpp b/src/modules/welcome/checker/CheckerContainer.cpp index 0524bddb0..248b0481c 100644 --- a/src/modules/welcome/checker/CheckerContainer.cpp +++ b/src/modules/welcome/checker/CheckerContainer.cpp @@ -1,7 +1,7 @@ /* === This file is part of Calamares - === * * Copyright 2014-2017, Teo Mrnjavac - * Copyright 2017, 2019, Adriaan de Groot + * Copyright 2017, 2019-2020, Adriaan de Groot * Copyright 2017, Gabriel Craciunescu * * Calamares is free software: you can redistribute it and/or modify @@ -29,6 +29,8 @@ #include "utils/Retranslator.h" #include "widgets/WaitingWidget.h" +#include + CheckerContainer::CheckerContainer( QWidget* parent ) : QWidget( parent ) , m_waitingWidget( new WaitingWidget( QString(), this ) ) @@ -40,10 +42,7 @@ CheckerContainer::CheckerContainer( QWidget* parent ) CalamaresUtils::unmarginLayout( mainLayout ); mainLayout->addWidget( m_waitingWidget ); - CALAMARES_RETRANSLATE( - if ( m_waitingWidget ) - m_waitingWidget->setText( tr( "Gathering system information..." ) ); - ) + CALAMARES_RETRANSLATE( if ( m_waitingWidget ) m_waitingWidget->setText( tr( "Gathering system information..." ) ); ) } CheckerContainer::~CheckerContainer() @@ -52,32 +51,37 @@ CheckerContainer::~CheckerContainer() delete m_checkerWidget; } -void CheckerContainer::requirementsComplete( bool ok ) +void +CheckerContainer::requirementsComplete( bool ok ) { layout()->removeWidget( m_waitingWidget ); m_waitingWidget->deleteLater(); m_waitingWidget = nullptr; // Don't delete in destructor - m_checkerWidget = new ResultsListWidget( this ); - m_checkerWidget->init( m_requirements ); + m_checkerWidget = new ResultsListWidget( this, m_requirements ); layout()->addWidget( m_checkerWidget ); m_verdict = ok; } -void CheckerContainer::requirementsChecked(const Calamares::RequirementsList& l) +void +CheckerContainer::requirementsChecked( const Calamares::RequirementsList& l ) { m_requirements.append( l ); } -void CheckerContainer::requirementsProgress(const QString& message) +void +CheckerContainer::requirementsProgress( const QString& message ) { if ( m_waitingWidget ) + { m_waitingWidget->setText( message ); + } } -bool CheckerContainer::verdict() const +bool +CheckerContainer::verdict() const { return m_verdict; } diff --git a/src/modules/welcome/checker/ResultsListWidget.cpp b/src/modules/welcome/checker/ResultsListWidget.cpp index b72b91452..f5877707f 100644 --- a/src/modules/welcome/checker/ResultsListWidget.cpp +++ b/src/modules/welcome/checker/ResultsListWidget.cpp @@ -1,7 +1,7 @@ /* === This file is part of Calamares - === * * Copyright 2014-2015, Teo Mrnjavac - * Copyright 2017, 2019, Adriaan de Groot + * Copyright 2017, 2019-2020, Adriaan de Groot * * Calamares is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,120 +24,176 @@ #include "Branding.h" #include "Settings.h" #include "utils/CalamaresUtilsGui.h" +#include "utils/Logger.h" #include "utils/Retranslator.h" #include "widgets/FixedAspectRatioLabel.h" #include -#include #include #include #include +#include +/** @brief Add widgets to @p layout for the list @p checkEntries + * + * The @p resultWidgets is filled with pointers to the widgets; + * for each entry in @p checkEntries that satisfies @p predicate, + * a widget is created, otherwise a nullptr is added instead. + * + * Adds all the widgets to the given @p layout. + * + * Afterwards, @p resultWidgets has a length equal to @p checkEntries. + */ +static void +createResultWidgets( QLayout* layout, + QList< ResultWidget* >& resultWidgets, + const Calamares::RequirementsList& checkEntries, + std::function< bool( const Calamares::RequirementEntry& ) > predicate ) +{ + resultWidgets.clear(); + resultWidgets.reserve( checkEntries.count() ); + for ( const auto& entry : checkEntries ) + { + if ( !predicate( entry ) ) + { + resultWidgets.append( nullptr ); + continue; + } -ResultsListWidget::ResultsListWidget( QWidget* parent ) - : QWidget( parent ) + ResultWidget* ciw = new ResultWidget( entry.satisfied, entry.mandatory ); + layout->addWidget( ciw ); + ciw->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred ); + + ciw->setAutoFillBackground( true ); + QPalette pal( ciw->palette() ); + QColor bgColor = pal.window().color(); + int bgHue = ( entry.satisfied ) ? bgColor.hue() : ( entry.mandatory ) ? 0 : 60; + bgColor.setHsv( bgHue, 64, bgColor.value() ); + pal.setColor( QPalette::Window, bgColor ); + ciw->setPalette( pal ); + + resultWidgets.append( ciw ); + } +} + +/** @brief A "details" dialog for the results-list + * + * This displays the same RequirementsList as ResultsListWidget, + * but the *details* part rather than the show description. + * + * This is an internal-to-the-widget class. + */ +class ResultsListDialog : public QDialog { - setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); +public: + /** @brief Create a dialog for the given @p checkEntries list of requirements. + * + * The list must continue to exist for the lifetime of the dialog, + * or UB happens. + */ + ResultsListDialog( QWidget* parent, const Calamares::RequirementsList& checkEntries ); + virtual ~ResultsListDialog(); - m_mainLayout = new QVBoxLayout; - setLayout( m_mainLayout ); +private: + QLabel* m_title; + QList< ResultWidget* > m_resultWidgets; ///< One widget for each entry with details available + const Calamares::RequirementsList& m_entries; - QHBoxLayout* spacerLayout = new QHBoxLayout; - m_mainLayout->addLayout( spacerLayout ); - m_paddingSize = qBound( 32, CalamaresUtils::defaultFontHeight() * 4, 128 ); - spacerLayout->addSpacing( m_paddingSize ); - m_entriesLayout = new QVBoxLayout; - spacerLayout->addLayout( m_entriesLayout ); - spacerLayout->addSpacing( m_paddingSize ); - CalamaresUtils::unmarginLayout( spacerLayout ); + void retranslate(); +}; + +ResultsListDialog::ResultsListDialog( QWidget* parent, const Calamares::RequirementsList& checkEntries ) + : QDialog( parent ) + , m_entries( checkEntries ) +{ + auto* mainLayout = new QVBoxLayout; + auto* entriesLayout = new QVBoxLayout; + + m_title = new QLabel( this ); + + createResultWidgets( entriesLayout, m_resultWidgets, checkEntries, []( const Calamares::RequirementEntry& e ) { + return e.hasDetails(); + } ); + + QDialogButtonBox* buttonBox = new QDialogButtonBox( QDialogButtonBox::Close, Qt::Horizontal, this ); + + mainLayout->addWidget( m_title ); + mainLayout->addLayout( entriesLayout ); + mainLayout->addWidget( buttonBox ); + + setLayout( mainLayout ); + + connect( buttonBox, &QDialogButtonBox::clicked, this, &QDialog::close ); + + CALAMARES_RETRANSLATE_SLOT( &ResultsListDialog::retranslate ) + retranslate(); // Do it now to fill in the texts } +ResultsListDialog::~ResultsListDialog() {} void -ResultsListWidget::init( const Calamares::RequirementsList& checkEntries ) +ResultsListDialog::retranslate() { - bool allChecked = true; - bool requirementsSatisfied = true; + m_title->setText( tr( "For best results, please ensure that this computer:" ) ); + setWindowTitle( tr( "System requirements" ) ); - for ( const auto& entry : checkEntries ) + int i = 0; + for ( const auto& entry : m_entries ) { - if ( !entry.satisfied ) + if ( m_resultWidgets[ i ] ) { - ResultWidget* ciw = new ResultWidget( entry.satisfied, entry.mandatory ); - CALAMARES_RETRANSLATE( ciw->setText( entry.negatedText() ); ) - m_entriesLayout->addWidget( ciw ); - ciw->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred ); - - allChecked = false; - if ( entry.mandatory ) - requirementsSatisfied = false; - - ciw->setAutoFillBackground( true ); - QPalette pal( ciw->palette() ); - QColor bgColor = pal.window().color(); - int bgHue = ( entry.satisfied ) ? bgColor.hue() : ( entry.mandatory ) ? 0 : 60; - bgColor.setHsv( bgHue, 64, bgColor.value() ); - pal.setColor( QPalette::Window, bgColor ); - ciw->setPalette( pal ); + m_resultWidgets[ i ]->setText( entry.enumerationText() ); } + i++; } +} - QLabel* textLabel = new QLabel; - textLabel->setWordWrap( true ); - m_entriesLayout->insertWidget( 0, textLabel ); - textLabel->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred ); +ResultsListWidget::ResultsListWidget( QWidget* parent, const Calamares::RequirementsList& checkEntries ) + : QWidget( parent ) + , m_entries( checkEntries ) +{ + setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); - if ( !allChecked ) - { - m_entriesLayout->insertSpacing( 1, CalamaresUtils::defaultFontHeight() / 2 ); + QBoxLayout* mainLayout = new QVBoxLayout; + QBoxLayout* entriesLayout = new QVBoxLayout; - if ( !requirementsSatisfied ) - { - CALAMARES_RETRANSLATE( - QString message = Calamares::Settings::instance()->isSetupMode() - ? tr( "This computer does not satisfy the minimum " - "requirements for setting up %1.
" - "Setup cannot continue. " - "Details..." ) - : tr( "This computer does not satisfy the minimum " - "requirements for installing %1.
" - "Installation cannot continue. " - "Details..." ); - textLabel->setText( message.arg( *Calamares::Branding::ShortVersionedName ) ); - ) - textLabel->setOpenExternalLinks( false ); - connect( textLabel, &QLabel::linkActivated, - this, [ this, checkEntries ]( const QString& link ) - { - if ( link == "#details" ) - showDetailsDialog( checkEntries ); - } ); - } - else - { - CALAMARES_RETRANSLATE( - QString message = Calamares::Settings::instance()->isSetupMode() - ? tr( "This computer does not satisfy some of the " - "recommended requirements for setting up %1.
" - "Setup can continue, but some features " - "might be disabled." ) - : tr( "This computer does not satisfy some of the " - "recommended requirements for installing %1.
" - "Installation can continue, but some features " - "might be disabled." ); - textLabel->setText( message.arg( *Calamares::Branding::ShortVersionedName ) ); - ) - } - } + setLayout( mainLayout ); + + int paddingSize = qBound( 32, CalamaresUtils::defaultFontHeight() * 4, 128 ); + + QHBoxLayout* spacerLayout = new QHBoxLayout; + mainLayout->addLayout( spacerLayout ); + spacerLayout->addSpacing( paddingSize ); + spacerLayout->addLayout( entriesLayout ); + spacerLayout->addSpacing( paddingSize ); + CalamaresUtils::unmarginLayout( spacerLayout ); - if ( allChecked && requirementsSatisfied ) + m_explanation = new QLabel; + m_explanation->setWordWrap( true ); + m_explanation->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred ); + m_explanation->setOpenExternalLinks( false ); + connect( m_explanation, &QLabel::linkActivated, this, &ResultsListWidget::linkClicked ); + entriesLayout->addWidget( m_explanation ); + + // Check that all are satisfied (gives warnings if not) and + // all *mandatory* entries are satisfied (gives errors if not). + auto isUnSatisfied = []( const Calamares::RequirementEntry& e ) { return !e.satisfied; }; + const bool requirementsSatisfied = std::none_of( checkEntries.begin(), checkEntries.end(), isUnSatisfied ); + + createResultWidgets( entriesLayout, m_resultWidgets, checkEntries, isUnSatisfied ); + + if ( !requirementsSatisfied ) { - if ( !Calamares::Branding::instance()-> - imagePath( Calamares::Branding::ProductWelcome ).isEmpty() ) + entriesLayout->insertSpacing( 1, CalamaresUtils::defaultFontHeight() / 2 ); + mainLayout->addStretch(); + } + else + { + if ( !Calamares::Branding::instance()->imagePath( Calamares::Branding::ProductWelcome ).isEmpty() ) { - QPixmap theImage = QPixmap( Calamares::Branding::instance()-> - imagePath( Calamares::Branding::ProductWelcome ) ); + QPixmap theImage + = QPixmap( Calamares::Branding::instance()->imagePath( Calamares::Branding::ProductWelcome ) ); if ( !theImage.isNull() ) { QLabel* imageLabel; @@ -154,68 +210,82 @@ ResultsListWidget::init( const Calamares::RequirementsList& checkEntries ) } imageLabel->setContentsMargins( 4, CalamaresUtils::defaultFontHeight() * 3 / 4, 4, 4 ); - m_mainLayout->addWidget( imageLabel ); + mainLayout->addWidget( imageLabel ); imageLabel->setAlignment( Qt::AlignCenter ); imageLabel->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); } } - CALAMARES_RETRANSLATE( - textLabel->setText( tr( "This program will ask you some questions and " - "set up %2 on your computer." ) - .arg( *Calamares::Branding::ProductName ) ); - textLabel->setAlignment( Qt::AlignCenter ); - ) + m_explanation->setAlignment( Qt::AlignCenter ); } - else - m_mainLayout->addStretch(); + + CALAMARES_RETRANSLATE_SLOT( &ResultsListWidget::retranslate ) + retranslate(); } void -ResultsListWidget::showDetailsDialog( const Calamares::RequirementsList& checkEntries ) +ResultsListWidget::linkClicked( const QString& link ) { - QDialog* detailsDialog = new QDialog( this ); - QBoxLayout* mainLayout = new QVBoxLayout; - detailsDialog->setLayout( mainLayout ); - - QLabel* textLabel = new QLabel; - mainLayout->addWidget( textLabel ); - CALAMARES_RETRANSLATE( - textLabel->setText( tr( "For best results, please ensure that this computer:" ) ); - ) - QBoxLayout* entriesLayout = new QVBoxLayout; - CalamaresUtils::unmarginLayout( entriesLayout ); - mainLayout->addLayout( entriesLayout ); - - for ( const auto& entry : checkEntries ) + if ( link == "#details" ) { - if ( !entry.hasDetails() ) - continue; - - ResultWidget* ciw = new ResultWidget( entry.satisfied, entry.mandatory ); - CALAMARES_RETRANSLATE( ciw->setText( entry.enumerationText() ); ) - entriesLayout->addWidget( ciw ); - ciw->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred ); - - ciw->setAutoFillBackground( true ); - QPalette pal( ciw->palette() ); - QColor bgColor = pal.window().color(); - int bgHue = ( entry.satisfied ) ? bgColor.hue() : ( entry.mandatory ) ? 0 : 60; - bgColor.setHsv( bgHue, 64, bgColor.value() ); - pal.setColor( QPalette::Window, bgColor ); - ciw->setPalette( pal ); + auto* dialog = new ResultsListDialog( this, m_entries ); + dialog->exec(); + dialog->deleteLater(); } +} - QDialogButtonBox* buttonBox = new QDialogButtonBox( QDialogButtonBox::Close, - Qt::Horizontal, - this ); - mainLayout->addWidget( buttonBox ); +void +ResultsListWidget::retranslate() +{ + int i = 0; + for ( const auto& entry : m_entries ) + { + if ( m_resultWidgets[ i ] ) + { + m_resultWidgets[ i ]->setText( entry.negatedText() ); + } + i++; + } - detailsDialog->setModal( true ); - detailsDialog->setWindowTitle( tr( "System requirements" ) ); + // Check that all are satisfied (gives warnings if not) and + // all *mandatory* entries are satisfied (gives errors if not). + auto isUnSatisfied = []( const Calamares::RequirementEntry& e ) { return !e.satisfied; }; + auto isMandatoryAndUnSatisfied = []( const Calamares::RequirementEntry& e ) { return e.mandatory && !e.satisfied; }; + const bool requirementsSatisfied = std::none_of( m_entries.begin(), m_entries.end(), isUnSatisfied ); + const bool mandatorySatisfied = std::none_of( m_entries.begin(), m_entries.end(), isMandatoryAndUnSatisfied ); - connect( buttonBox, &QDialogButtonBox::clicked, - detailsDialog, &QDialog::close ); - detailsDialog->exec(); - detailsDialog->deleteLater(); + if ( !requirementsSatisfied ) + { + QString message; + const bool setup = Calamares::Settings::instance()->isSetupMode(); + if ( !mandatorySatisfied ) + { + message = setup ? tr( "This computer does not satisfy the minimum " + "requirements for setting up %1.
" + "Setup cannot continue. " + "Details..." ) + : tr( "This computer does not satisfy the minimum " + "requirements for installing %1.
" + "Installation cannot continue. " + "Details..." ); + } + else + { + message = setup ? tr( "This computer does not satisfy some of the " + "recommended requirements for setting up %1.
" + "Setup can continue, but some features " + "might be disabled." ) + : tr( "This computer does not satisfy some of the " + "recommended requirements for installing %1.
" + "Installation can continue, but some features " + "might be disabled." ); + } + m_explanation->setText( message.arg( *Calamares::Branding::ShortVersionedName ) ); + } + else + { + m_explanation->setText( tr( "This program will ask you some questions and " + "set up %2 on your computer." ) + .arg( *Calamares::Branding::ProductName ) ); + } } diff --git a/src/modules/welcome/checker/ResultsListWidget.h b/src/modules/welcome/checker/ResultsListWidget.h index 3be02b0d0..25eae8b3e 100644 --- a/src/modules/welcome/checker/ResultsListWidget.h +++ b/src/modules/welcome/checker/ResultsListWidget.h @@ -1,7 +1,7 @@ /* === This file is part of Calamares - === * * Copyright 2014-2015, Teo Mrnjavac - * Copyright 2019, Adriaan de Groot + * Copyright 2019-2020, Adriaan de Groot * * Calamares is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,25 +20,28 @@ #ifndef CHECKER_RESULTSLISTWIDGET_H #define CHECKER_RESULTSLISTWIDGET_H +#include "ResultWidget.h" + #include "modulesystem/Requirement.h" -#include #include +class QLabel; + class ResultsListWidget : public QWidget { Q_OBJECT public: - explicit ResultsListWidget( QWidget* parent = nullptr ); - - void init( const Calamares::RequirementsList& checkEntries ); + explicit ResultsListWidget( QWidget* parent, const Calamares::RequirementsList& checkEntries ); private: - void showDetailsDialog( const Calamares::RequirementsList& checkEntries ); + /// @brief A link in the explanatory text has been clicked + void linkClicked( const QString& link ); + void retranslate(); - QBoxLayout* m_mainLayout; - QBoxLayout* m_entriesLayout; - int m_paddingSize; + QLabel* m_explanation = nullptr; ///< Explanatory text above the list, with link + const Calamares::RequirementsList& m_entries; + QList< ResultWidget* > m_resultWidgets; ///< One widget for each unsatisfied entry }; -#endif // CHECKER_RESULTSLISTWIDGET_H +#endif // CHECKER_RESULTSLISTWIDGET_H