diff --git a/CHANGES b/CHANGES index e5958750f..3a6ebd0f3 100644 --- a/CHANGES +++ b/CHANGES @@ -3,6 +3,25 @@ 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.22 (unreleased) # + +This release contains contributions from (alphabetically by first name): + - Anke Boersma + +## Core ## + - Both the sidebar (on the left) and the navigation buttons (along the + bottom of the window) can now be configured to use the traditional + *widgets*, to use *qml*, or *hidden* from view (hiding the navigation + is not recommended unless you have a pure-QML UI to run inside + Calamares). The example QML that is compiled into Calamares has + been improved. To use your own QML, put files `calamares-sidebar.qml` + or `calamares-navigation.qml` into the branding directory. + +## Modules ## + - The *welcomeq* module has been improved with better layout and + nicer buttons in the example QML form. (Thanks to Anke Boersma) + + # 3.2.21 (2020-03-27) # This release contains contributions from (alphabetically by first name): diff --git a/CMakeLists.txt b/CMakeLists.txt index 837010be4..d70863929 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,10 +40,10 @@ cmake_minimum_required( VERSION 3.3 FATAL_ERROR ) project( CALAMARES - VERSION 3.2.21 + VERSION 3.2.22 LANGUAGES C CXX ) -set( CALAMARES_VERSION_RC 0 ) # Set to 0 during release cycle, 1 during development +set( CALAMARES_VERSION_RC 1 ) # Set to 0 during release cycle, 1 during development ### OPTIONS # diff --git a/src/branding/default/branding.desc b/src/branding/default/branding.desc index e7b9d9898..365af30e9 100644 --- a/src/branding/default/branding.desc +++ b/src/branding/default/branding.desc @@ -44,9 +44,15 @@ windowPlacement: center # Kind of sidebar (panel on the left, showing progress). # - "widget" or unset, use traditional sidebar (logo, items) # - "none", hide it entirely -# - "qml", use sidebar.qml from branding folder +# - "qml", use calamares-sidebar.qml from branding folder sidebar: widget +# Kind of navigation (button panel on the bottom). +# - "widget" or unset, use traditional navigation +# - "none", hide it entirely +# - "qml", use calamares-navigation.qml from branding folder +navigation: widget + # These are strings shown to the user in the user interface. # There is no provision for translating them -- since they # are names, the string is included as-is. diff --git a/src/calamares/CalamaresWindow.cpp b/src/calamares/CalamaresWindow.cpp index f2ff42aa8..5d4565406 100644 --- a/src/calamares/CalamaresWindow.cpp +++ b/src/calamares/CalamaresWindow.cpp @@ -138,11 +138,119 @@ CalamaresWindow::getQmlSidebar( int desiredWidth ) QQuickWidget* w = new QQuickWidget( this ); w->setFixedWidth( desiredWidth ); w->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); + w->setResizeMode( QQuickWidget::SizeRootObjectToView ); w->setSource( QUrl( CalamaresUtils::searchQmlFile( CalamaresUtils::QmlSearch::Both, QStringLiteral( "calamares-sidebar" ) ) ) ); return w; } +/** @brief Get a button-sized icon. */ +static inline QPixmap +getButtonIcon( const QString& name ) +{ + return Calamares::Branding::instance()->image( name, QSize( 22, 22 ) ); +} + +static inline void +setButtonIcon( QPushButton* button, const QString& name ) +{ + auto icon = getButtonIcon( name ); + if ( button && !icon.isNull() ) + { + button->setIcon( icon ); + } +} + +QWidget* +CalamaresWindow::getWidgetNavigation() +{ + QWidget* navigation = new QWidget( this ); + QBoxLayout* bottomLayout = new QHBoxLayout; + bottomLayout->addStretch(); + + // Create buttons and sets an initial icon; the icons may change + { + auto* back = new QPushButton( getButtonIcon( QStringLiteral( "go-previous" ) ), tr( "&Back" ), navigation ); + back->setObjectName( "view-button-back" ); + back->setEnabled( m_viewManager->backEnabled() ); + connect( back, &QPushButton::clicked, m_viewManager, &Calamares::ViewManager::back ); + connect( m_viewManager, &Calamares::ViewManager::backEnabledChanged, back, &QPushButton::setEnabled ); + connect( m_viewManager, &Calamares::ViewManager::backLabelChanged, back, &QPushButton::setText ); + connect( m_viewManager, &Calamares::ViewManager::backIconChanged, this, [=]( QString n ) { + setButtonIcon( back, n ); + } ); + bottomLayout->addWidget( back ); + } + { + auto* next = new QPushButton( getButtonIcon( QStringLiteral( "go-next" ) ), tr( "&Next" ), navigation ); + next->setObjectName( "view-button-next" ); + next->setEnabled( m_viewManager->nextEnabled() ); + connect( next, &QPushButton::clicked, m_viewManager, &Calamares::ViewManager::next ); + connect( m_viewManager, &Calamares::ViewManager::nextEnabledChanged, next, &QPushButton::setEnabled ); + connect( m_viewManager, &Calamares::ViewManager::nextLabelChanged, next, &QPushButton::setText ); + connect( m_viewManager, &Calamares::ViewManager::nextIconChanged, this, [=]( QString n ) { + setButtonIcon( next, n ); + } ); + bottomLayout->addWidget( next ); + } + bottomLayout->addSpacing( 12 ); + { + auto* quit = new QPushButton( getButtonIcon( QStringLiteral( "dialog-cancel" ) ), tr( "&Cancel" ), navigation ); + quit->setObjectName( "view-button-cancel" ); + connect( quit, &QPushButton::clicked, m_viewManager, &Calamares::ViewManager::quit ); + connect( m_viewManager, &Calamares::ViewManager::quitEnabledChanged, quit, &QPushButton::setEnabled ); + connect( m_viewManager, &Calamares::ViewManager::quitLabelChanged, quit, &QPushButton::setText ); + connect( m_viewManager, &Calamares::ViewManager::quitIconChanged, this, [=]( QString n ) { + setButtonIcon( quit, n ); + } ); + connect( m_viewManager, &Calamares::ViewManager::quitTooltipChanged, quit, &QPushButton::setToolTip ); + connect( m_viewManager, &Calamares::ViewManager::quitVisibleChanged, quit, &QPushButton::setVisible ); + bottomLayout->addWidget( quit ); + } + + navigation->setLayout( bottomLayout ); + return navigation; +} + +QWidget* +CalamaresWindow::getQmlNavigation() +{ + CalamaresUtils::registerCalamaresModels(); + QQuickWidget* w = new QQuickWidget( this ); + w->setFixedHeight( 64 ); + w->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); + w->setResizeMode( QQuickWidget::SizeRootObjectToView ); + w->setSource( QUrl( + CalamaresUtils::searchQmlFile( CalamaresUtils::QmlSearch::Both, QStringLiteral( "calamares-navigation" ) ) ) ); + return w; +} + +/**@brief Picks one of two methods to call + * + * Calls method (member function) @p widget or @p qml with arguments @p a + * on the given window, based on the flavor. + */ +template < typename widgetMaker, typename... args > +QWidget* +flavoredWidget( Calamares::Branding::PanelFlavor flavor, + CalamaresWindow* w, + widgetMaker widget, + widgetMaker qml, + args... a ) +{ + // Member-function calling syntax is (object.*member)(args) + switch ( flavor ) + { + case Calamares::Branding::PanelFlavor::Widget: + return ( w->*widget )( a... ); + case Calamares::Branding::PanelFlavor::Qml: + return ( w->*qml )( a... ); + case Calamares::Branding::PanelFlavor::None: + return nullptr; + } + NOTREACHED return nullptr; // All enum values handled above +} + CalamaresWindow::CalamaresWindow( QWidget* parent ) : QWidget( parent ) , m_debugWindow( nullptr ) @@ -188,20 +296,12 @@ CalamaresWindow::CalamaresWindow( QWidget* parent ) QBoxLayout* mainLayout = new QHBoxLayout; setLayout( mainLayout ); - QWidget* sideBox = nullptr; - switch ( branding->sidebarFlavor() ) - { - case Calamares::Branding::SidebarFlavor::Widget: - sideBox = getWidgetSidebar( - qBound( 100, CalamaresUtils::defaultFontHeight() * 12, w < windowPreferredWidth ? 100 : 190 ) ); - break; - case Calamares::Branding::SidebarFlavor::Qml: - sideBox = getQmlSidebar( - qBound( 100, CalamaresUtils::defaultFontHeight() * 12, w < windowPreferredWidth ? 100 : 190 ) ); - break; - case Calamares::Branding::SidebarFlavor::None: - sideBox = nullptr; - } + QWidget* sideBox = flavoredWidget( + branding->sidebarFlavor(), + this, + &CalamaresWindow::getWidgetSidebar, + &CalamaresWindow::getQmlSidebar, + qBound( 100, CalamaresUtils::defaultFontHeight() * 12, w < windowPreferredWidth ? 100 : 190 ) ); if ( sideBox ) { mainLayout->addWidget( sideBox ); @@ -219,9 +319,19 @@ CalamaresWindow::CalamaresWindow( QWidget* parent ) // and requires an extra show() (at least with KWin/X11) which // is too annoying. Instead, leave it up to ignoring-the-quit- // event, which is also the ViewManager's responsibility. + QBoxLayout* contentsLayout = new QVBoxLayout; + contentsLayout->addWidget( m_viewManager->centralWidget() ); + QWidget* navigation = flavoredWidget( + branding->navigationFlavor(), this, &CalamaresWindow::getWidgetNavigation, &CalamaresWindow::getQmlNavigation ); + if ( navigation ) + { + contentsLayout->addWidget( navigation ); + } + + mainLayout->addLayout( contentsLayout ); - mainLayout->addWidget( m_viewManager->centralWidget() ); CalamaresUtils::unmarginLayout( mainLayout ); + CalamaresUtils::unmarginLayout( contentsLayout ); setStyleSheet( Calamares::Branding::instance()->stylesheet() ); } diff --git a/src/calamares/CalamaresWindow.h b/src/calamares/CalamaresWindow.h index 5cbbdfca6..d6592c99a 100644 --- a/src/calamares/CalamaresWindow.h +++ b/src/calamares/CalamaresWindow.h @@ -51,9 +51,14 @@ protected: virtual void closeEvent( QCloseEvent* e ) override; private: + // Two variations on sidebar (the progress view) QWidget* getWidgetSidebar( int desiredWidth ); QWidget* getQmlSidebar( int desiredWidth ); + // Two variations on navigation (buttons at bottom) + QWidget* getWidgetNavigation(); + QWidget* getQmlNavigation(); + QPointer< Calamares::DebugWindow > m_debugWindow; // Managed by self Calamares::ViewManager* m_viewManager; }; diff --git a/src/calamares/calamares-navigation.qml b/src/calamares/calamares-navigation.qml new file mode 100644 index 000000000..c7cd91835 --- /dev/null +++ b/src/calamares/calamares-navigation.qml @@ -0,0 +1,57 @@ +import io.calamares.ui 1.0 +import io.calamares.core 1.0 + +import QtQuick 2.3 +import QtQuick.Controls 2.10 +import QtQuick.Layouts 1.3 + +Rectangle { + id: navigationBar; + color: Branding.styleString( Branding.SidebarBackground ); + + RowLayout { + id: buttonBar + height: 64; + anchors.fill: parent; + + Item + { + Layout.fillWidth: true; + } + + Button + { + text: ViewManager.backLabel; + icon.name: ViewManager.backIcon; + + enabled: ViewManager.backEnabled; + visible: true; + onClicked: { ViewManager.back(); } + } + Button + { + text: ViewManager.nextLabel; + icon.name: ViewManager.nextIcon; + + enabled: ViewManager.nextEnabled; + visible: true; + onClicked: { ViewManager.next(); } + } + Button + { + Layout.leftMargin: 3 * buttonBar.spacing; // little gap from back/next + Layout.rightMargin: 2 * buttonBar.spacing + text: ViewManager.quitLabel; + icon.name: ViewManager.quitIcon; + + ToolTip.visible: hovered + ToolTip.timeout: 5000 + ToolTip.delay: 1000 + ToolTip.text: ViewManager.quitTooltip; + + enabled: ViewManager.quitEnabled; + visible: ViewManager.quitVisible; + onClicked: { ViewManager.quit(); } + } + } +} diff --git a/src/calamares/calamares-sidebar.qml b/src/calamares/calamares-sidebar.qml index a486bdb17..183a9acb2 100644 --- a/src/calamares/calamares-sidebar.qml +++ b/src/calamares/calamares-sidebar.qml @@ -1,35 +1,49 @@ -import QtQuick 2.3 import io.calamares.ui 1.0 import io.calamares.core 1.0 -Column { +import QtQuick 2.3 +import QtQuick.Layouts 1.3 Rectangle { - id: hello - width: 200 - height: 100 - color: "red" + id: sideBar; + color: Branding.styleString( Branding.SidebarBackground ); - Text { - anchors.centerIn: parent - text: Branding.string(Branding.VersionedName) - } -} + ColumnLayout { + anchors.fill: parent; + spacing: 0; -/* perhaps we could show a branding image here */ + Image { + Layout.topMargin: 12; + Layout.bottomMargin: 12; + Layout.alignment: Qt.AlignHCenter | Qt.AlignTop + id: logo; + width: 80; + height: width; // square + source: "file:/" + Branding.imagePath(Branding.ProductLogo); + sourceSize.width: width; + sourceSize.height: height; + } -Repeater { - model: ViewManager - Rectangle { - width: 200 - height: 75 - color: "black" + Repeater { + model: ViewManager + Rectangle { + Layout.leftMargin: 12; + width: parent.width - 24; + height: 35; + radius: 6; + color: Branding.styleString( index == ViewManager.currentStepIndex ? Branding.SidebarTextHighlight : Branding.SidebarBackground ); - Text { - color: completed ? "green" : "yellow" - text: display + Text { + anchors.verticalCenter: parent.verticalCenter; + x: parent.x + 12; + color: Branding.styleString( index == ViewManager.currentStepIndex ? Branding.SidebarTextSelect : Branding.SidebarText ); + text: display; + } + } + } + + Item { + Layout.fillHeight: true; } } } - -} diff --git a/src/calamares/calamares.qrc b/src/calamares/calamares.qrc index fdcd5e05d..17db2e08a 100644 --- a/src/calamares/calamares.qrc +++ b/src/calamares/calamares.qrc @@ -1,5 +1,6 @@ calamares-sidebar.qml + calamares-navigation.qml diff --git a/src/calamares/progresstree/ProgressTreeDelegate.cpp b/src/calamares/progresstree/ProgressTreeDelegate.cpp index e7041d1b9..7b7101f5d 100644 --- a/src/calamares/progresstree/ProgressTreeDelegate.cpp +++ b/src/calamares/progresstree/ProgressTreeDelegate.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 @@ -19,10 +19,10 @@ #include "ProgressTreeDelegate.h" +#include "Branding.h" #include "CalamaresApplication.h" #include "CalamaresWindow.h" #include "ViewManager.h" -#include "Branding.h" #include "utils/CalamaresUtilsGui.h" #include @@ -85,10 +85,7 @@ ProgressTreeDelegate::paintViewStep( QPainter* painter, font.setBold( false ); painter->setFont( font ); - bool isCurrent = false; - isCurrent = index.data( Calamares::ViewManager::ProgressTreeItemCurrentRole ).toBool(); - - if ( isCurrent ) + if ( index.row() == index.data( Calamares::ViewManager::ProgressTreeItemCurrentIndex ).toInt() ) { painter->setPen( Calamares::Branding::instance()->styleString( Calamares::Branding::SidebarTextSelect ) ); QString textHighlight diff --git a/src/calamares/testmain.cpp b/src/calamares/testmain.cpp index 0845218eb..8dcf41faa 100644 --- a/src/calamares/testmain.cpp +++ b/src/calamares/testmain.cpp @@ -183,7 +183,7 @@ load_module( const ModuleConfig& moduleConfig ) cDebug() << "Module" << moduleName << "job-configuration:" << configFile; - Calamares::Module* module = Calamares::Module::fromDescriptor( descriptor, name, configFile, moduleDirectory ); + Calamares::Module* module = Calamares::moduleFromDescriptor( descriptor, name, configFile, moduleDirectory ); return module; } diff --git a/src/libcalamares/CMakeLists.txt b/src/libcalamares/CMakeLists.txt index 608768a97..91dce96cd 100644 --- a/src/libcalamares/CMakeLists.txt +++ b/src/libcalamares/CMakeLists.txt @@ -39,6 +39,10 @@ set( libSources # Modules modulesystem/InstanceKey.cpp + modulesystem/Module.cpp + modulesystem/Requirement.cpp + modulesystem/RequirementsChecker.cpp + modulesystem/RequirementsModel.cpp # Network service network/Manager.cpp diff --git a/src/libcalamares/PythonJobApi.cpp b/src/libcalamares/PythonJobApi.cpp index cf7984c87..ecca466fe 100644 --- a/src/libcalamares/PythonJobApi.cpp +++ b/src/libcalamares/PythonJobApi.cpp @@ -65,9 +65,9 @@ mount( const std::string& device_path, const std::string& options ) { return CalamaresUtils::Partition::mount( QString::fromStdString( device_path ), - QString::fromStdString( mount_point ), - QString::fromStdString( filesystem_name ), - QString::fromStdString( options ) ); + QString::fromStdString( mount_point ), + QString::fromStdString( filesystem_name ), + QString::fromStdString( options ) ); } diff --git a/src/libcalamares/modulesystem/InstanceKey.h b/src/libcalamares/modulesystem/InstanceKey.h index 495401903..724827330 100644 --- a/src/libcalamares/modulesystem/InstanceKey.h +++ b/src/libcalamares/modulesystem/InstanceKey.h @@ -94,8 +94,7 @@ private: } }; -QDebug& -operator <<( QDebug& s, const Calamares::ModuleSystem::InstanceKey& i ); +QDebug& operator<<( QDebug& s, const Calamares::ModuleSystem::InstanceKey& i ); } // namespace ModuleSystem } // namespace Calamares diff --git a/src/libcalamaresui/modulesystem/Module.cpp b/src/libcalamares/modulesystem/Module.cpp similarity index 64% rename from src/libcalamaresui/modulesystem/Module.cpp rename to src/libcalamares/modulesystem/Module.cpp index 35b1508f1..9620299ec 100644 --- a/src/libcalamaresui/modulesystem/Module.cpp +++ b/src/libcalamares/modulesystem/Module.cpp @@ -20,30 +20,18 @@ #include "Module.h" #include "CalamaresConfig.h" -#include "CppJobModule.h" -#include "ProcessJobModule.h" #include "Settings.h" -#include "ViewModule.h" #include "utils/Dirs.h" #include "utils/Logger.h" #include "utils/NamedEnum.h" #include "utils/Yaml.h" -#ifdef WITH_PYTHON -#include "PythonJobModule.h" -#endif - -#ifdef WITH_PYTHONQT -#include "PythonQtViewModule.h" -#endif - #include #include #include #include - static const char EMERGENCY[] = "emergency"; namespace Calamares @@ -66,111 +54,6 @@ Module::initFrom( const Calamares::ModuleSystem::Descriptor& moduleDescriptor, c } } -Module* -Module::fromDescriptor( const Calamares::ModuleSystem::Descriptor& moduleDescriptor, - const QString& instanceId, - const QString& configFileName, - const QString& moduleDirectory ) -{ - std::unique_ptr< Module > m; - - QString typeString = moduleDescriptor.value( "type" ).toString(); - QString intfString = moduleDescriptor.value( "interface" ).toString(); - - if ( typeString.isEmpty() || intfString.isEmpty() ) - { - cError() << "Bad module descriptor format" << instanceId; - return nullptr; - } - if ( ( typeString == "view" ) || ( typeString == "viewmodule" ) ) - { - if ( intfString == "qtplugin" ) - { - m.reset( new ViewModule() ); - } - else if ( intfString == "pythonqt" ) - { -#ifdef WITH_PYTHONQT - m.reset( new PythonQtViewModule() ); -#else - cError() << "PythonQt view modules are not supported in this version of Calamares."; -#endif - } - else - { - cError() << "Bad interface" << intfString << "for module type" << typeString; - } - } - else if ( typeString == "job" ) - { - if ( intfString == "qtplugin" ) - { - m.reset( new CppJobModule() ); - } - else if ( intfString == "process" ) - { - m.reset( new ProcessJobModule() ); - } - else if ( intfString == "python" ) - { -#ifdef WITH_PYTHON - m.reset( new PythonJobModule() ); -#else - cError() << "Python modules are not supported in this version of Calamares."; -#endif - } - else - { - cError() << "Bad interface" << intfString << "for module type" << typeString; - } - } - else - { - cError() << "Bad module type" << typeString; - } - - if ( !m ) - { - cError() << "Bad module type (" << typeString << ") or interface string (" << intfString << ") for module " - << instanceId; - return nullptr; - } - - QDir moduleDir( moduleDirectory ); - if ( moduleDir.exists() && moduleDir.isReadable() ) - { - m->m_directory = moduleDir.absolutePath(); - } - else - { - cError() << "Bad module directory" << moduleDirectory << "for" << instanceId; - return nullptr; - } - - m->initFrom( moduleDescriptor, instanceId ); - if ( !m->m_key.isValid() ) - { - cError() << "Module" << instanceId << "invalid ID"; - return nullptr; - } - - m->initFrom( moduleDescriptor ); - if ( !configFileName.isEmpty() ) - { - try - { - m->loadConfigurationFile( configFileName ); - } - catch ( YAML::Exception& e ) - { - cError() << "YAML parser error " << e.what(); - return nullptr; - } - } - return m.release(); -} - - static QStringList moduleConfigurationCandidates( bool assumeBuildDir, const QString& moduleName, const QString& configFileName ) { @@ -211,7 +94,8 @@ moduleConfigurationCandidates( bool assumeBuildDir, const QString& moduleName, c return paths; } -void Module::loadConfigurationFile( const QString& configFileName ) //throws YAML::Exception +void +Module::loadConfigurationFile( const QString& configFileName ) //throws YAML::Exception { QStringList configCandidates = moduleConfigurationCandidates( Settings::instance()->debugMode(), name(), configFileName ); diff --git a/src/libcalamaresui/modulesystem/Module.h b/src/libcalamares/modulesystem/Module.h similarity index 87% rename from src/libcalamaresui/modulesystem/Module.h rename to src/libcalamares/modulesystem/Module.h index 0891f8a25..ba4533fae 100644 --- a/src/libcalamaresui/modulesystem/Module.h +++ b/src/libcalamares/modulesystem/Module.h @@ -20,12 +20,12 @@ #ifndef CALAMARES_MODULE_H #define CALAMARES_MODULE_H -#include "Job.h" -#include "Requirement.h" #include "DllMacro.h" +#include "Job.h" #include "modulesystem/Descriptor.h" #include "modulesystem/InstanceKey.h" +#include "modulesystem/Requirement.h" #include #include @@ -33,6 +33,12 @@ namespace Calamares { +class Module; +Module* moduleFromDescriptor( const ModuleSystem::Descriptor& moduleDescriptor, + const QString& instanceId, + const QString& configFileName, + const QString& moduleDirectory ); + /** * @brief The Module class is a common supertype for Calamares modules. @@ -40,7 +46,7 @@ namespace Calamares * takes care of creating an object of the correct type starting from a module * descriptor structure. */ -class UIDLLEXPORT Module +class DLLEXPORT Module { public: /** @@ -68,18 +74,6 @@ public: PythonQt // Views only, available as enum even if PythonQt isn't used }; - /** - * @brief fromDescriptor creates a new Module object of the correct type. - * @param moduleDescriptor a module descriptor, already parsed into a variant map. - * @param instanceId the instance id of the new module instance. - * @param configFileName the name of the configuration file to read. - * @param moduleDirectory the path to the directory with this module's files. - * @return a pointer to an object of a subtype of Module. - */ - static Module* fromDescriptor( const ModuleSystem::Descriptor& moduleDescriptor, - const QString& instanceId, - const QString& configFileName, - const QString& moduleDirectory ); virtual ~Module(); /** @@ -193,6 +187,11 @@ private: QString m_directory; ModuleSystem::InstanceKey m_key; + + friend Module* Calamares::moduleFromDescriptor( const ModuleSystem::Descriptor& moduleDescriptor, + const QString& instanceId, + const QString& configFileName, + const QString& moduleDirectory ); }; } // namespace Calamares diff --git a/src/libcalamaresui/modulesystem/Requirement.cpp b/src/libcalamares/modulesystem/Requirement.cpp similarity index 100% rename from src/libcalamaresui/modulesystem/Requirement.cpp rename to src/libcalamares/modulesystem/Requirement.cpp diff --git a/src/libcalamaresui/modulesystem/Requirement.h b/src/libcalamares/modulesystem/Requirement.h similarity index 98% rename from src/libcalamaresui/modulesystem/Requirement.h rename to src/libcalamares/modulesystem/Requirement.h index 3f8d1a54b..da3cf29dd 100644 --- a/src/libcalamaresui/modulesystem/Requirement.h +++ b/src/libcalamares/modulesystem/Requirement.h @@ -18,6 +18,8 @@ #ifndef CALAMARES_REQUIREMENT_H #define CALAMARES_REQUIREMENT_H +#include "DllMacro.h" + #include #include #include diff --git a/src/libcalamaresui/modulesystem/RequirementsChecker.cpp b/src/libcalamares/modulesystem/RequirementsChecker.cpp similarity index 98% rename from src/libcalamaresui/modulesystem/RequirementsChecker.cpp rename to src/libcalamares/modulesystem/RequirementsChecker.cpp index 41281c9b9..97a4c912f 100644 --- a/src/libcalamaresui/modulesystem/RequirementsChecker.cpp +++ b/src/libcalamares/modulesystem/RequirementsChecker.cpp @@ -18,18 +18,16 @@ #include "RequirementsChecker.h" -#include "Module.h" -#include "Requirement.h" - +#include "modulesystem/Module.h" +#include "modulesystem/Requirement.h" #include "utils/Logger.h" -#include - #include #include #include #include +#include namespace Calamares { diff --git a/src/libcalamaresui/modulesystem/RequirementsChecker.h b/src/libcalamares/modulesystem/RequirementsChecker.h similarity index 97% rename from src/libcalamaresui/modulesystem/RequirementsChecker.h rename to src/libcalamares/modulesystem/RequirementsChecker.h index 2e1708016..450495dc1 100644 --- a/src/libcalamaresui/modulesystem/RequirementsChecker.h +++ b/src/libcalamares/modulesystem/RequirementsChecker.h @@ -18,14 +18,13 @@ #ifndef CALAMARES_REQUIREMENTSCHECKER_H #define CALAMARES_REQUIREMENTSCHECKER_H -#include "Requirement.h" +#include "modulesystem/Requirement.h" #include #include #include #include - namespace Calamares { @@ -44,7 +43,7 @@ public: RequirementsChecker( QVector< Module* > modules, QObject* parent = nullptr ); virtual ~RequirementsChecker() override; -public slots: +public Q_SLOTS: /// @brief Start checking all the requirements void run(); diff --git a/src/libcalamares/modulesystem/RequirementsModel.cpp b/src/libcalamares/modulesystem/RequirementsModel.cpp new file mode 100644 index 000000000..4001d2d81 --- /dev/null +++ b/src/libcalamares/modulesystem/RequirementsModel.cpp @@ -0,0 +1,81 @@ +/* === This file is part of Calamares - === + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Calamares is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Calamares. If not, see . + */ + +#include "RequirementsModel.h" + +namespace Calamares +{ + +void +RequirementsModel::setRequirementsList( const Calamares::RequirementsList& requirements ) +{ + emit beginResetModel(); + m_requirements = requirements; + + auto isUnSatisfied = []( const Calamares::RequirementEntry& e ) { return !e.satisfied; }; + auto isMandatoryAndUnSatisfied = []( const Calamares::RequirementEntry& e ) { return e.mandatory && !e.satisfied; }; + + m_satisfiedRequirements = std::none_of( m_requirements.begin(), m_requirements.end(), isUnSatisfied ); + m_satisfiedMandatory = std::none_of( m_requirements.begin(), m_requirements.end(), isMandatoryAndUnSatisfied ); + + emit satisfiedRequirementsChanged( m_satisfiedRequirements ); + emit satisfiedMandatoryChanged( m_satisfiedMandatory ); + emit endResetModel(); +} + +int +RequirementsModel::rowCount( const QModelIndex& ) const +{ + return m_requirements.count(); +} + +QVariant +RequirementsModel::data( const QModelIndex& index, int role ) const +{ + const auto requirement = m_requirements.at( index.row() ); + + switch ( role ) + { + case Roles::Name: + return requirement.name; + case Roles::Details: + return requirement.enumerationText(); + case Roles::NegatedText: + return requirement.negatedText(); + case Roles::Satisfied: + return requirement.satisfied; + case Roles::Mandatory: + return requirement.mandatory; + default: + return QVariant(); + } +} + +QHash< int, QByteArray > +RequirementsModel::roleNames() const +{ + static QHash< int, QByteArray > roles; + roles[ Roles::Name ] = "name"; + roles[ Roles::Details ] = "details"; + roles[ Roles::NegatedText ] = "negatedText"; + roles[ Roles::Satisfied ] = "satisfied"; + roles[ Roles::Mandatory ] = "mandatory"; + return roles; +} + +} // namespace Calamares diff --git a/src/libcalamares/modulesystem/RequirementsModel.h b/src/libcalamares/modulesystem/RequirementsModel.h new file mode 100644 index 000000000..2acf785e7 --- /dev/null +++ b/src/libcalamares/modulesystem/RequirementsModel.h @@ -0,0 +1,81 @@ +/* === This file is part of Calamares - === + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Calamares is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Calamares. If not, see . + */ + +#ifndef CALAMARES_REQUIREMENTSMODEL_H +#define CALAMARES_REQUIREMENTSMODEL_H + +#include "Requirement.h" + +#include "DllMacro.h" + +#include + +namespace Calamares +{ + +class DLLEXPORT RequirementsModel : public QAbstractListModel +{ + Q_OBJECT + Q_PROPERTY( bool satisfiedRequirements READ satisfiedRequirements NOTIFY satisfiedRequirementsChanged FINAL ) + Q_PROPERTY( bool satisfiedMandatory READ satisfiedMandatory NOTIFY satisfiedMandatoryChanged FINAL ) + +public: + using QAbstractListModel::QAbstractListModel; + + enum Roles : short + { + Name, + Satisfied, + Mandatory, + Details, + NegatedText, + HasDetails + }; + // No Q_ENUM because these are exposed through roleNames() + + bool satisfiedRequirements() const { return m_satisfiedRequirements; } + bool satisfiedMandatory() const { return m_satisfiedMandatory; } + + const Calamares::RequirementEntry& getEntry( int index ) const + { + return m_requirements.at( index ); + } + + void setRequirementsList( const Calamares::RequirementsList& requirements ); + + QVariant data( const QModelIndex& index, int role ) const override; + int rowCount( const QModelIndex& ) const override; + int count() const { return m_requirements.count(); } + +signals: + void satisfiedRequirementsChanged( bool value ); + void satisfiedMandatoryChanged( bool value ); + +protected: + QHash< int, QByteArray > roleNames() const override; + +private: + Calamares::RequirementsList m_requirements; + bool m_satisfiedRequirements = false; + bool m_satisfiedMandatory = false; + +}; + +} // namespace Calamares + +#endif diff --git a/src/libcalamares/modulesystem/Tests.cpp b/src/libcalamares/modulesystem/Tests.cpp index e7301a0be..b1fab7ffc 100644 --- a/src/libcalamares/modulesystem/Tests.cpp +++ b/src/libcalamares/modulesystem/Tests.cpp @@ -138,4 +138,5 @@ ModuleSystemTests::testBadFromStringCases() QTEST_GUILESS_MAIN( ModuleSystemTests ) #include "utils/moc-warnings.h" + #include "Tests.moc" diff --git a/src/libcalamares/network/Manager.cpp b/src/libcalamares/network/Manager.cpp index 1d58efba9..6e8a1e93d 100644 --- a/src/libcalamares/network/Manager.cpp +++ b/src/libcalamares/network/Manager.cpp @@ -286,4 +286,5 @@ Manager::asynchronousGet( const QUrl& url, const CalamaresUtils::Network::Reques } // namespace CalamaresUtils #include "utils/moc-warnings.h" + #include "Manager.moc" diff --git a/src/libcalamares/network/Tests.cpp b/src/libcalamares/network/Tests.cpp index 830545b96..dc893a9c9 100644 --- a/src/libcalamares/network/Tests.cpp +++ b/src/libcalamares/network/Tests.cpp @@ -47,6 +47,7 @@ NetworkTests::testPing() using namespace CalamaresUtils::Network; Logger::setupLogLevel( Logger::LOGVERBOSE ); auto& nam = Manager::instance(); - auto canPing_www_kde_org = nam.synchronousPing( QUrl( "https://www.kde.org" ), RequestOptions( RequestOptions::FollowRedirect ) ); + auto canPing_www_kde_org + = nam.synchronousPing( QUrl( "https://www.kde.org" ), RequestOptions( RequestOptions::FollowRedirect ) ); QVERIFY( canPing_www_kde_org ); } diff --git a/src/libcalamares/utils/RAII.h b/src/libcalamares/utils/RAII.h new file mode 100644 index 000000000..4d8210a25 --- /dev/null +++ b/src/libcalamares/utils/RAII.h @@ -0,0 +1,43 @@ +/* === This file is part of Calamares - === + * + * Copyright 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Calamares is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Calamares. If not, see . + */ + +#ifndef UTILS_RAII_H +#define UTILS_RAII_H + +#include + +#include + +/// @brief Convenience to zero out and deleteLater of any QObject-derived-class +template < typename T > +struct cqDeleter +{ + T*& p; + + ~cqDeleter() + { + static_assert( std::is_base_of< QObject, T >::value, "Not a QObject-class" ); + if ( p ) + { + p->deleteLater(); + } + p = nullptr; + } +}; + +#endif diff --git a/src/libcalamaresui/Branding.cpp b/src/libcalamaresui/Branding.cpp index 5c41f5ea2..25fab307e 100644 --- a/src/libcalamaresui/Branding.cpp +++ b/src/libcalamaresui/Branding.cpp @@ -419,10 +419,11 @@ Branding::initSimpleSettings( const YAML::Node& doc ) { QStringLiteral( "free" ), WindowPlacement::Free }, { QStringLiteral( "center" ), WindowPlacement::Center } }; - static const NamedEnumTable< SidebarFlavor > sidebarFlavorNames { - { QStringLiteral( "widget" ), SidebarFlavor::Widget }, - { QStringLiteral( "none" ), SidebarFlavor::None }, - { QStringLiteral( "qml" ), SidebarFlavor::Qml } + static const NamedEnumTable< PanelFlavor > sidebarFlavorNames { + { QStringLiteral( "widget" ), PanelFlavor::Widget }, + { QStringLiteral( "none" ), PanelFlavor::None }, + { QStringLiteral( "hidden" ), PanelFlavor::None }, + { QStringLiteral( "qml" ), PanelFlavor::Qml } }; // clang-format on // *INDENT-ON* @@ -448,6 +449,12 @@ Branding::initSimpleSettings( const YAML::Node& doc ) cWarning() << "Branding module-setting *sidebar* interpreted as" << sidebarFlavorNames.find( m_sidebarFlavor, ok ); } + m_navigationFlavor = sidebarFlavorNames.find( getString( doc, "navigation" ), ok); + if ( !ok ) + { + cWarning() << "Branding module-setting *navigation* interpreted as" + << sidebarFlavorNames.find( m_navigationFlavor, ok ); + } QString windowSize = getString( doc, "windowSize" ); if ( !windowSize.isEmpty() ) diff --git a/src/libcalamaresui/Branding.h b/src/libcalamaresui/Branding.h index 88f658473..b7ba637d6 100644 --- a/src/libcalamaresui/Branding.h +++ b/src/libcalamaresui/Branding.h @@ -124,13 +124,13 @@ public: }; Q_ENUM( WindowPlacement ) ///@brief What kind of sidebar to use in the main window - enum class SidebarFlavor + enum class PanelFlavor { None, Widget, Qml }; - Q_ENUM( SidebarFlavor ) + Q_ENUM( PanelFlavor ) static Branding* instance(); @@ -185,7 +185,9 @@ public: bool windowPlacementCentered() const { return m_windowPlacement == WindowPlacement::Center; } ///@brief Which sidebar flavor is configured - SidebarFlavor sidebarFlavor() const { return m_sidebarFlavor; } + PanelFlavor sidebarFlavor() const { return m_sidebarFlavor; } + ///@brief Which navigation flavor is configured + PanelFlavor navigationFlavor() const { return m_navigationFlavor; } /** * Creates a map called "branding" in the global storage, and inserts an @@ -227,7 +229,8 @@ private: WindowDimension m_windowHeight, m_windowWidth; WindowPlacement m_windowPlacement; - SidebarFlavor m_sidebarFlavor = SidebarFlavor::Widget; + PanelFlavor m_sidebarFlavor = PanelFlavor::Widget; + PanelFlavor m_navigationFlavor = PanelFlavor::Widget; }; template < typename U > diff --git a/src/libcalamaresui/CMakeLists.txt b/src/libcalamaresui/CMakeLists.txt index c603ca22d..e813b0009 100644 --- a/src/libcalamaresui/CMakeLists.txt +++ b/src/libcalamaresui/CMakeLists.txt @@ -6,11 +6,9 @@ include_directories( ${CMAKE_SOURCE_DIR}/src/libcalamares ${CMAKE_BINARY_DIR}/sr set( calamaresui_SOURCES modulesystem/CppJobModule.cpp - modulesystem/Module.cpp + modulesystem/ModuleFactory.cpp modulesystem/ModuleManager.cpp modulesystem/ProcessJobModule.cpp - modulesystem/Requirement.cpp - modulesystem/RequirementsChecker.cpp modulesystem/ViewModule.cpp utils/CalamaresUtilsGui.cpp diff --git a/src/libcalamaresui/ViewManager.cpp b/src/libcalamaresui/ViewManager.cpp index 9972c3af9..9bfb31e43 100644 --- a/src/libcalamaresui/ViewManager.cpp +++ b/src/libcalamaresui/ViewManager.cpp @@ -3,7 +3,7 @@ * Copyright 2019, Dominic Hayes * Copyright 2019, Gabriel Craciunescu * Copyright 2014-2015, Teo Mrnjavac - * Copyright 2017-2018, Adriaan de Groot + * Copyright 2017-2018, 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 @@ -38,6 +38,12 @@ #include #include +#define UPDATE_BUTTON_PROPERTY( name, value ) \ + { \ + m_##name = value; \ + emit name##Changed( m_##name ); \ + } + namespace Calamares { @@ -88,43 +94,12 @@ ViewManager::ViewManager( QObject* parent ) m_stack->setContentsMargins( 0, 0, 0, 0 ); mainLayout->addWidget( m_stack ); - // Create buttons and sets an initial icon; the icons may change - m_back = new QPushButton( getButtonIcon( QStringLiteral( "go-previous" ) ), tr( "&Back" ), m_widget ); - m_back->setObjectName( "view-button-back" ); - m_next = new QPushButton( getButtonIcon( QStringLiteral( "go-next" ) ), tr( "&Next" ), m_widget ); - m_next->setObjectName( "view-button-next" ); - m_quit = new QPushButton( getButtonIcon( QStringLiteral( "dialog-cancel" ) ), tr( "&Cancel" ), m_widget ); - m_quit->setObjectName( "view-button-cancel" ); + updateButtonLabels(); - CALAMARES_RETRANSLATE_SLOT( &ViewManager::updateButtonLabels ) - - QBoxLayout* bottomLayout = new QHBoxLayout; - mainLayout->addLayout( bottomLayout ); - bottomLayout->addStretch(); - bottomLayout->addWidget( m_back ); - bottomLayout->addWidget( m_next ); - bottomLayout->addSpacing( 12 ); - bottomLayout->addWidget( m_quit ); - - connect( m_next, &QPushButton::clicked, this, &ViewManager::next ); - connect( m_back, &QPushButton::clicked, this, &ViewManager::back ); - m_back->setEnabled( false ); - - connect( m_quit, &QPushButton::clicked, this, [this]() { - if ( this->confirmCancelInstallation() ) - { - qApp->quit(); - } - } ); connect( JobQueue::instance(), &JobQueue::failed, this, &ViewManager::onInstallationFailed ); connect( JobQueue::instance(), &JobQueue::finished, this, &ViewManager::next ); - if ( Calamares::Settings::instance()->disableCancel() ) - { - m_quit->setVisible( false ); - } - - // onInstallationFailed( "Title of Failure", "Body of Failure"); // for testing paste functionality + CALAMARES_RETRANSLATE_SLOT( &ViewManager::updateButtonLabels ) } @@ -149,7 +124,8 @@ ViewManager::addViewStep( ViewStep* step ) // If this is the first inserted view step, update status of "Next" button if ( m_steps.count() == 1 ) { - m_next->setEnabled( step->isNextEnabled() ); + m_nextEnabled = step->isNextEnabled(); + emit nextEnabledChanged( m_nextEnabled ); } } @@ -160,13 +136,15 @@ ViewManager::insertViewStep( int before, ViewStep* step ) emit beginInsertRows( QModelIndex(), before, before ); m_steps.insert( before, step ); connect( step, &ViewStep::enlarge, this, &ViewManager::enlarge ); + // TODO: this can be a regular slot connect( step, &ViewStep::nextStatusChanged, this, [this]( bool status ) { ViewStep* vs = qobject_cast< ViewStep* >( sender() ); if ( vs ) { if ( vs == m_steps.at( m_currentStep ) ) { - m_next->setEnabled( status ); + m_nextEnabled = status; + emit nextEnabledChanged( m_nextEnabled ); } } } ); @@ -371,8 +349,8 @@ ViewManager::next() { // Reached the end in a weird state (e.g. no finished step after an exec) executing = false; - m_next->setEnabled( false ); - m_back->setEnabled( false ); + UPDATE_BUTTON_PROPERTY( nextEnabled, false ) + UPDATE_BUTTON_PROPERTY( backEnabled, false ) } updateCancelEnabled( !settings->disableCancel() && !( executing && settings->disableCancelDuringExec() ) ); } @@ -383,8 +361,8 @@ ViewManager::next() if ( m_currentStep < m_steps.count() ) { - m_next->setEnabled( !executing && m_steps.at( m_currentStep )->isNextEnabled() ); - m_back->setEnabled( !executing && m_steps.at( m_currentStep )->isBackEnabled() ); + UPDATE_BUTTON_PROPERTY( nextEnabled, !executing && m_steps.at( m_currentStep )->isNextEnabled() ) + UPDATE_BUTTON_PROPERTY( backEnabled, !executing && m_steps.at( m_currentStep )->isBackEnabled() ) } updateButtonLabels(); @@ -406,43 +384,44 @@ ViewManager::updateButtonLabels() // If we're going into the execution step / install phase, other message if ( stepIsExecute( m_steps, m_currentStep + 1 ) ) { - m_next->setText( nextIsInstallationStep ); - setButtonIcon( m_next, "run-install" ); + UPDATE_BUTTON_PROPERTY( nextLabel, nextIsInstallationStep ) + UPDATE_BUTTON_PROPERTY( nextIcon, "run-install" ) } else { - m_next->setText( tr( "&Next" ) ); - setButtonIcon( m_next, "go-next" ); + UPDATE_BUTTON_PROPERTY( nextLabel, tr( "&Next" ) ) + UPDATE_BUTTON_PROPERTY( nextIcon, "go-next" ) } // Going back is always simple - m_back->setText( tr( "&Back" ) ); + UPDATE_BUTTON_PROPERTY( backLabel, tr( "&Back" ) ) + UPDATE_BUTTON_PROPERTY( backIcon, "go-back" ) // Cancel button changes label at the end if ( isAtVeryEnd( m_steps, m_currentStep ) ) { - m_quit->setText( tr( "&Done" ) ); - m_quit->setToolTip( quitOnCompleteTooltip ); - m_quit->setVisible( true ); // At end, always visible and enabled. - setButtonIcon( m_quit, "dialog-ok-apply" ); + UPDATE_BUTTON_PROPERTY( quitLabel, tr( "&Done" ) ) + UPDATE_BUTTON_PROPERTY( quitTooltip, quitOnCompleteTooltip ) + UPDATE_BUTTON_PROPERTY( quitVisible, true ) + UPDATE_BUTTON_PROPERTY( quitIcon, "dialog-ok-apply" ) updateCancelEnabled( true ); if ( settings->quitAtEnd() ) { - m_quit->click(); + quit(); } } else { if ( settings->disableCancel() ) { - m_quit->setVisible( false ); // In case we went back from final + UPDATE_BUTTON_PROPERTY( quitVisible, false ) } updateCancelEnabled( !settings->disableCancel() && !( stepIsExecute( m_steps, m_currentStep ) && settings->disableCancelDuringExec() ) ); - m_quit->setText( tr( "&Cancel" ) ); - m_quit->setToolTip( cancelBeforeInstallationTooltip ); - setButtonIcon( m_quit, "dialog-cancel" ); + UPDATE_BUTTON_PROPERTY( quitLabel, tr( "&Cancel" ) ) + UPDATE_BUTTON_PROPERTY( quitTooltip, cancelBeforeInstallationTooltip ) + UPDATE_BUTTON_PROPERTY( quitIcon, "dialog-cancel" ) } } @@ -467,17 +446,25 @@ ViewManager::back() return; } - m_next->setEnabled( m_steps.at( m_currentStep )->isNextEnabled() ); - m_back->setEnabled( m_steps.at( m_currentStep )->isBackEnabled() ); - - if ( m_currentStep == 0 && m_steps.first()->isAtBeginning() ) - { - m_back->setEnabled( false ); - } + UPDATE_BUTTON_PROPERTY( nextEnabled, m_steps.at( m_currentStep )->isNextEnabled() ) + UPDATE_BUTTON_PROPERTY( backEnabled, + ( m_currentStep == 0 && m_steps.first()->isAtBeginning() ) + ? false + : m_steps.at( m_currentStep )->isBackEnabled() ) updateButtonLabels(); } + +void +ViewManager::quit() +{ + if ( confirmCancelInstallation() ) + { + qApp->quit(); + } +} + bool ViewManager::confirmCancelInstallation() { @@ -516,7 +503,7 @@ ViewManager::confirmCancelInstallation() void ViewManager::updateCancelEnabled( bool enabled ) { - m_quit->setEnabled( enabled ); + UPDATE_BUTTON_PROPERTY( quitEnabled, enabled ) emit cancelEnabled( enabled ); } @@ -559,23 +546,8 @@ ViewManager::data( const QModelIndex& index, int role ) const { return QVariant(); } - case ProgressTreeItemCurrentRole: - return currentStep() == step; - case ProgressTreeItemCompletedRole: - // Every step *before* the current step is considered "complete" - for ( const auto* otherstep : m_steps ) - { - if ( otherstep == currentStep() ) - { - break; - } - if ( otherstep == step ) - { - return true; - } - } - // .. and the others (including current) are not. - return false; + case ProgressTreeItemCurrentIndex: + return m_currentStep; default: return QVariant(); } @@ -592,13 +564,4 @@ ViewManager::rowCount( const QModelIndex& parent ) const return m_steps.length(); } -QHash< int, QByteArray > -ViewManager::roleNames() const -{ - auto h = QAbstractListModel::roleNames(); - h.insert( ProgressTreeItemCurrentRole, "current" ); - h.insert( ProgressTreeItemCompletedRole, "completed" ); - return h; -} - } // namespace Calamares diff --git a/src/libcalamaresui/ViewManager.h b/src/libcalamaresui/ViewManager.h index a4bedd7bc..ad9376f1a 100644 --- a/src/libcalamaresui/ViewManager.h +++ b/src/libcalamaresui/ViewManager.h @@ -37,6 +37,23 @@ namespace Calamares class UIDLLEXPORT ViewManager : public QAbstractListModel { Q_OBJECT + Q_PROPERTY( int currentStepIndex READ currentStepIndex NOTIFY currentStepChanged FINAL ) + + Q_PROPERTY( bool nextEnabled READ nextEnabled NOTIFY nextEnabledChanged FINAL ) + Q_PROPERTY( QString nextLabel READ nextLabel NOTIFY nextLabelChanged FINAL ) + Q_PROPERTY( QString nextIcon READ nextIcon NOTIFY nextIconChanged FINAL ) + + Q_PROPERTY( bool backEnabled READ backEnabled NOTIFY backEnabledChanged FINAL ) + Q_PROPERTY( QString backLabel READ backLabel NOTIFY backLabelChanged FINAL ) + Q_PROPERTY( QString backIcon READ backIcon NOTIFY backIconChanged FINAL ) + + Q_PROPERTY( bool quitEnabled READ quitEnabled NOTIFY quitEnabledChanged FINAL ) + Q_PROPERTY( QString quitLabel READ quitLabel NOTIFY quitLabelChanged FINAL ) + Q_PROPERTY( QString quitIcon READ quitIcon NOTIFY quitIconChanged FINAL ) + Q_PROPERTY( QString quitTooltip READ quitTooltip NOTIFY quitTooltipChanged FINAL ) + + Q_PROPERTY( bool quitVisible READ quitVisible NOTIFY quitVisibleChanged FINAL ) + public: /** * @brief instance access to the ViewManager singleton. @@ -90,13 +107,25 @@ public: */ bool confirmCancelInstallation(); -public slots: +public Q_SLOTS: /** * @brief next moves forward to the next page of the current ViewStep (if any), * or to the first page of the next ViewStep if the current ViewStep doesn't * have any more pages. */ void next(); + bool nextEnabled() const + { + return m_nextEnabled; ///< Is the next-button to be enabled + } + QString nextLabel() const + { + return m_nextLabel; ///< What should be displayed on the next-button + } + QString nextIcon() const + { + return m_nextIcon; ///< Name of the icon to show + } /** * @brief back moves backward to the previous page of the current ViewStep (if any), @@ -104,6 +133,42 @@ public slots: * have any pages before the current one. */ void back(); + bool backEnabled() const + { + return m_backEnabled; ///< Is the back-button to be enabled + } + QString backLabel() const + { + return m_backLabel; ///< What should be displayed on the back-button + } + QString backIcon() const + { + return m_backIcon; ///< Name of the icon to show + } + + /** + * @brief Probably quit + * + * Asks for confirmation if necessary. Terminates the application. + */ + void quit(); + bool quitEnabled() const + { + return m_quitEnabled; ///< Is the quit-button to be enabled + } + QString quitLabel() const + { + return m_quitLabel; ///< What should be displayed on the quit-button + } + QString quitIcon() const + { + return m_quitIcon; ///< Name of the icon to show + } + bool quitVisible() const + { + return m_quitVisible; ///< Should the quit-button be visible + } + QString quitTooltip() const { return m_quitTooltip; } /** * @brief onInstallationFailed displays an error message when a fatal failure @@ -124,6 +189,20 @@ signals: void enlarge( QSize enlarge ) const; // See ViewStep::enlarge() void cancelEnabled( bool enabled ) const; + void nextEnabledChanged( bool ) const; + void nextLabelChanged( QString ) const; + void nextIconChanged( QString ) const; + + void backEnabledChanged( bool ) const; + void backLabelChanged( QString ) const; + void backIconChanged( QString ) const; + + void quitEnabledChanged( bool ) const; + void quitLabelChanged( QString ) const; + void quitIconChanged( QString ) const; + void quitVisibleChanged( bool ) const; + void quitTooltipChanged( QString ) const; + private: explicit ViewManager( QObject* parent = nullptr ); virtual ~ViewManager() override; @@ -139,9 +218,20 @@ private: QWidget* m_widget; QStackedWidget* m_stack; - QPushButton* m_back; - QPushButton* m_next; - QPushButton* m_quit; + + bool m_nextEnabled = false; + QString m_nextLabel; + QString m_nextIcon; ///< Name of icon to show on button + + bool m_backEnabled = false; + QString m_backLabel; + QString m_backIcon; + + bool m_quitEnabled = false; + QString m_quitLabel; + QString m_quitIcon; + QString m_quitTooltip; + bool m_quitVisible = true; public: /** @section Model @@ -151,14 +241,11 @@ public: */ enum Role { - ProgressTreeItemCurrentRole = Qt::UserRole + 11, ///< Is this the *current* step? - ProgressTreeItemCompletedRole = Qt::UserRole + 12 ///< Are we past this one? + ProgressTreeItemCurrentIndex = Qt::UserRole + 13 ///< Index (row) of the current step }; QVariant data( const QModelIndex& index, int role = Qt::DisplayRole ) const override; int rowCount( const QModelIndex& parent = QModelIndex() ) const override; - - QHash< int, QByteArray > roleNames() const override; }; } // namespace Calamares diff --git a/src/libcalamaresui/modulesystem/CppJobModule.h b/src/libcalamaresui/modulesystem/CppJobModule.h index d97443a8a..2fd82433c 100644 --- a/src/libcalamaresui/modulesystem/CppJobModule.h +++ b/src/libcalamaresui/modulesystem/CppJobModule.h @@ -21,8 +21,8 @@ #ifndef CALAMARES_CPPJOBMODULE_H #define CALAMARES_CPPJOBMODULE_H -#include "Module.h" #include "DllMacro.h" +#include "modulesystem/Module.h" class QPluginLoader; @@ -42,12 +42,16 @@ protected: void initFrom( const QVariantMap& moduleDescriptor ) override; private: - friend class Module; //so only the superclass can instantiate explicit CppJobModule(); virtual ~CppJobModule() override; QPluginLoader* m_loader; job_ptr m_job; + + friend Module* Calamares::moduleFromDescriptor( const ModuleSystem::Descriptor& moduleDescriptor, + const QString& instanceId, + const QString& configFileName, + const QString& moduleDirectory ); }; } // namespace Calamares diff --git a/src/libcalamaresui/modulesystem/ModuleFactory.cpp b/src/libcalamaresui/modulesystem/ModuleFactory.cpp new file mode 100644 index 000000000..f3b46eab7 --- /dev/null +++ b/src/libcalamaresui/modulesystem/ModuleFactory.cpp @@ -0,0 +1,154 @@ +/* === This file is part of Calamares - === + * + * Copyright 2014-2015, Teo Mrnjavac + * Copyright 2017-2018, 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Calamares is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Calamares. If not, see . + */ + +#include "ModuleFactory.h" + +#include "CalamaresConfig.h" +#include "CppJobModule.h" +#include "ProcessJobModule.h" +#include "ViewModule.h" + +#include "utils/Dirs.h" +#include "utils/Logger.h" +#include "utils/NamedEnum.h" +#include "utils/Yaml.h" + +#ifdef WITH_PYTHON +#include "PythonJobModule.h" +#endif + +#ifdef WITH_PYTHONQT +#include "PythonQtViewModule.h" +#endif + +#include +#include +#include +#include + + +namespace Calamares +{ + +Module* +moduleFromDescriptor( const Calamares::ModuleSystem::Descriptor& moduleDescriptor, + const QString& instanceId, + const QString& configFileName, + const QString& moduleDirectory ) +{ + std::unique_ptr< Module > m; + + QString typeString = moduleDescriptor.value( "type" ).toString(); + QString intfString = moduleDescriptor.value( "interface" ).toString(); + + if ( typeString.isEmpty() || intfString.isEmpty() ) + { + cError() << "Bad module descriptor format" << instanceId; + return nullptr; + } + if ( ( typeString == "view" ) || ( typeString == "viewmodule" ) ) + { + if ( intfString == "qtplugin" ) + { + m.reset( new ViewModule() ); + } + else if ( intfString == "pythonqt" ) + { +#ifdef WITH_PYTHONQT + m.reset( new PythonQtViewModule() ); +#else + cError() << "PythonQt view modules are not supported in this version of Calamares."; +#endif + } + else + { + cError() << "Bad interface" << intfString << "for module type" << typeString; + } + } + else if ( typeString == "job" ) + { + if ( intfString == "qtplugin" ) + { + m.reset( new CppJobModule() ); + } + else if ( intfString == "process" ) + { + m.reset( new ProcessJobModule() ); + } + else if ( intfString == "python" ) + { +#ifdef WITH_PYTHON + m.reset( new PythonJobModule() ); +#else + cError() << "Python modules are not supported in this version of Calamares."; +#endif + } + else + { + cError() << "Bad interface" << intfString << "for module type" << typeString; + } + } + else + { + cError() << "Bad module type" << typeString; + } + + if ( !m ) + { + cError() << "Bad module type (" << typeString << ") or interface string (" << intfString << ") for module " + << instanceId; + return nullptr; + } + + QDir moduleDir( moduleDirectory ); + if ( moduleDir.exists() && moduleDir.isReadable() ) + { + m->m_directory = moduleDir.absolutePath(); + } + else + { + cError() << "Bad module directory" << moduleDirectory << "for" << instanceId; + return nullptr; + } + + m->initFrom( moduleDescriptor, instanceId ); + if ( !m->m_key.isValid() ) + { + cError() << "Module" << instanceId << "invalid ID"; + return nullptr; + } + + m->initFrom( moduleDescriptor ); + if ( !configFileName.isEmpty() ) + { + try + { + m->loadConfigurationFile( configFileName ); + } + catch ( YAML::Exception& e ) + { + cError() << "YAML parser error " << e.what(); + return nullptr; + } + } + return m.release(); +} + + +} // namespace Calamares diff --git a/src/libcalamaresui/modulesystem/ModuleFactory.h b/src/libcalamaresui/modulesystem/ModuleFactory.h new file mode 100644 index 000000000..8184967d2 --- /dev/null +++ b/src/libcalamaresui/modulesystem/ModuleFactory.h @@ -0,0 +1,47 @@ +/* === This file is part of Calamares - === + * + * Copyright 2014-2015, Teo Mrnjavac + * Copyright 2017, 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Calamares is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Calamares. If not, see . + */ + +#ifndef CALAMARES_MODULEFACTORY_H +#define CALAMARES_MODULEFACTORY_H + +#include "DllMacro.h" + +#include "modulesystem/Descriptor.h" +#include "modulesystem/Module.h" + +#include + +namespace Calamares +{ + +/** + * @brief fromDescriptor creates a new Module object of the correct type. + * @param moduleDescriptor a module descriptor, already parsed into a variant map. + * @param instanceId the instance id of the new module instance. + * @param configFileName the name of the configuration file to read. + * @param moduleDirectory the path to the directory with this module's files. + * @return a pointer to an object of a subtype of Module. + */ +UIDLLEXPORT Module* moduleFromDescriptor( const ModuleSystem::Descriptor& moduleDescriptor, + const QString& instanceId, + const QString& configFileName, + const QString& moduleDirectory ); +} // namespace Calamares + +#endif // CALAMARES_MODULEFACTORY_H diff --git a/src/libcalamaresui/modulesystem/ModuleManager.cpp b/src/libcalamaresui/modulesystem/ModuleManager.cpp index f88d5999d..8d4b2342f 100644 --- a/src/libcalamaresui/modulesystem/ModuleManager.cpp +++ b/src/libcalamaresui/modulesystem/ModuleManager.cpp @@ -19,11 +19,11 @@ #include "ModuleManager.h" -#include "Module.h" -#include "RequirementsChecker.h" -#include "Settings.h" #include "ViewManager.h" +#include "Settings.h" +#include "modulesystem/Module.h" +#include "modulesystem/RequirementsChecker.h" #include "utils/Logger.h" #include "utils/Yaml.h" #include "viewpages/ExecutionViewStep.h" @@ -285,10 +285,11 @@ ModuleManager::loadModules() } else { - thisModule = Module::fromDescriptor( descriptor, - instanceKey.id(), - configFileName, - m_moduleDirectoriesByModuleName.value( instanceKey.module() ) ); + thisModule + = Calamares::moduleFromDescriptor( descriptor, + instanceKey.id(), + configFileName, + m_moduleDirectoriesByModuleName.value( instanceKey.module() ) ); if ( !thisModule ) { cError() << "Module" << instanceKey.toString() << "cannot be created from descriptor" diff --git a/src/libcalamaresui/modulesystem/ModuleManager.h b/src/libcalamaresui/modulesystem/ModuleManager.h index be485c01d..fdb63cd87 100644 --- a/src/libcalamaresui/modulesystem/ModuleManager.h +++ b/src/libcalamaresui/modulesystem/ModuleManager.h @@ -22,8 +22,7 @@ #include "modulesystem/Descriptor.h" #include "modulesystem/InstanceKey.h" - -#include "Requirement.h" +#include "modulesystem/Requirement.h" #include #include diff --git a/src/libcalamaresui/modulesystem/ProcessJobModule.h b/src/libcalamaresui/modulesystem/ProcessJobModule.h index da2badbd0..87c6e2da8 100644 --- a/src/libcalamaresui/modulesystem/ProcessJobModule.h +++ b/src/libcalamaresui/modulesystem/ProcessJobModule.h @@ -20,9 +20,8 @@ #ifndef CALAMARES_PROCESSJOBMODULE_H #define CALAMARES_PROCESSJOBMODULE_H -#include "Module.h" - #include "DllMacro.h" +#include "modulesystem/Module.h" #include @@ -42,7 +41,6 @@ protected: void initFrom( const QVariantMap& moduleDescriptor ) override; private: - friend class Module; explicit ProcessJobModule(); virtual ~ProcessJobModule() override; @@ -51,6 +49,11 @@ private: std::chrono::seconds m_secondsTimeout; bool m_runInChroot; job_ptr m_job; + + friend Module* Calamares::moduleFromDescriptor( const ModuleSystem::Descriptor& moduleDescriptor, + const QString& instanceId, + const QString& configFileName, + const QString& moduleDirectory ); }; } // namespace Calamares diff --git a/src/libcalamaresui/modulesystem/PythonJobModule.h b/src/libcalamaresui/modulesystem/PythonJobModule.h index 295ab5942..85f25ab74 100644 --- a/src/libcalamaresui/modulesystem/PythonJobModule.h +++ b/src/libcalamaresui/modulesystem/PythonJobModule.h @@ -19,9 +19,8 @@ #ifndef CALAMARES_PYTHONJOBMODULE_H #define CALAMARES_PYTHONJOBMODULE_H -#include "Module.h" - #include "DllMacro.h" +#include "modulesystem/Module.h" namespace Calamares { @@ -39,13 +38,17 @@ protected: void initFrom( const QVariantMap& moduleDescriptor ) override; private: - friend class Module; explicit PythonJobModule(); virtual ~PythonJobModule() override; QString m_scriptFileName; QString m_workingPath; job_ptr m_job; + + friend Module* Calamares::moduleFromDescriptor( const ModuleSystem::Descriptor& moduleDescriptor, + const QString& instanceId, + const QString& configFileName, + const QString& moduleDirectory ); }; } // namespace Calamares diff --git a/src/libcalamaresui/modulesystem/PythonQtViewModule.h b/src/libcalamaresui/modulesystem/PythonQtViewModule.h index 33b8d041b..64cc0f8a9 100644 --- a/src/libcalamaresui/modulesystem/PythonQtViewModule.h +++ b/src/libcalamaresui/modulesystem/PythonQtViewModule.h @@ -19,8 +19,8 @@ #ifndef CALAMARES_PYTHONQTVIEWMODULE_H #define CALAMARES_PYTHONQTVIEWMODULE_H -#include "Module.h" #include "DllMacro.h" +#include "Module.h" namespace Calamares { @@ -40,7 +40,6 @@ protected: void initFrom( const QVariantMap& moduleDescriptor ) override; private: - friend class Module; //so only the superclass can instantiate explicit PythonQtViewModule(); virtual ~PythonQtViewModule(); @@ -48,6 +47,11 @@ private: QString m_scriptFileName; QString m_workingPath; + + friend Module* Calamares::moduleFromDescriptor( const ModuleSystem::Descriptor& moduleDescriptor, + const QString& instanceId, + const QString& configFileName, + const QString& moduleDirectory ); }; } // namespace Calamares diff --git a/src/libcalamaresui/modulesystem/ViewModule.h b/src/libcalamaresui/modulesystem/ViewModule.h index c1ee8ff69..1d24ca811 100644 --- a/src/libcalamaresui/modulesystem/ViewModule.h +++ b/src/libcalamaresui/modulesystem/ViewModule.h @@ -20,8 +20,8 @@ #ifndef CALAMARES_VIEWMODULE_H #define CALAMARES_VIEWMODULE_H -#include "Module.h" #include "DllMacro.h" +#include "modulesystem/Module.h" class QPluginLoader; @@ -45,12 +45,16 @@ protected: void initFrom( const QVariantMap& moduleDescriptor ) override; private: - friend class Module; //so only the superclass can instantiate explicit ViewModule(); virtual ~ViewModule() override; QPluginLoader* m_loader; ViewStep* m_viewStep = nullptr; + + friend Module* Calamares::moduleFromDescriptor( const ModuleSystem::Descriptor& moduleDescriptor, + const QString& instanceId, + const QString& configFileName, + const QString& moduleDirectory ); }; } // namespace Calamares diff --git a/src/modules/netinstall/Config.cpp b/src/modules/netinstall/Config.cpp index 78718add1..556cb1cf9 100644 --- a/src/modules/netinstall/Config.cpp +++ b/src/modules/netinstall/Config.cpp @@ -23,6 +23,7 @@ #include "network/Manager.h" #include "utils/Logger.h" +#include "utils/RAII.h" #include "utils/Yaml.h" #include @@ -96,21 +97,6 @@ Config::loadGroupList( const QUrl& url ) } } -/// @brief Convenience to zero out and deleteLater on the reply, used in dataIsHere -struct ReplyDeleter -{ - QNetworkReply*& p; - - ~ReplyDeleter() - { - if ( p ) - { - p->deleteLater(); - } - p = nullptr; - } -}; - void Config::receivedGroupData() { @@ -123,7 +109,7 @@ Config::receivedGroupData() cDebug() << "NetInstall group data received" << m_reply->size() << "bytes from" << m_reply->url(); - ReplyDeleter d { m_reply }; + cqDeleter< QNetworkReply > d{ m_reply }; // If m_required is *false* then we still say we're ready // even if the reply is corrupt or missing. diff --git a/src/modules/netinstall/README.md b/src/modules/netinstall/README.md index cda4b6c88..cb6159d95 100644 --- a/src/modules/netinstall/README.md +++ b/src/modules/netinstall/README.md @@ -19,7 +19,9 @@ least should contain a *groupsUrl* key: The URL must point to a YAML file, the *groups* file. See below for the format of that groups file. The URL may be a local file (e.g. -scheme `file:///`) or a regular HTTP(s) URL. +scheme `file:///`) or a regular HTTP(s) URL. The URL has one special +case: the literal string `local` is used to indicate that the groups +data is contained in the `netinstall.conf` file itself. ## Groups Configuration @@ -47,13 +49,16 @@ More keys (per group) are supported: - *selected*: if true, display the group as selected. Defaults to false. - *critical*: if true, make the installation process fail if installing any of the packages in the group fails. Otherwise, just log a warning. - Defaults to false. + Defaults to false. If not set in a subgroup (see below), inherits from + the parent group. - *immutable*: if true, the state of the group (and all its subgroups) cannot be changed; it really only makes sense in combination with *selected* set to true. This only affects the user-interface. - *expanded*: if true, the group is shown in an expanded form (that is, not-collapsed) in the treeview on start. This only affects the user- - interface. + interface. Only top-level groups are show expanded-initially. + - *immutable*: if true, the group cannot be changed (packages selected + or deselected) and no checkboxes are shown for the group. - *subgroups*: if present this follows the same structure as the top level of the YAML file, allowing there to be sub-groups of packages to an arbitary depth diff --git a/src/modules/netinstall/netinstall.conf b/src/modules/netinstall/netinstall.conf index 82e12d558..5eeef905c 100644 --- a/src/modules/netinstall/netinstall.conf +++ b/src/modules/netinstall/netinstall.conf @@ -54,6 +54,8 @@ label: # If, and only if, *groupsUrl* is set to the literal string `local`, # groups data is read from this file. The value of *groups* must be # a list, with the same format as the regular `netinstall.yaml` file. +# See the `README.md` file in the *netinstall* module for documentation +# on the format of groups data. # # This is recommended only for small static package lists. groups: diff --git a/src/modules/partition/gui/PartitionViewStep.cpp b/src/modules/partition/gui/PartitionViewStep.cpp index 6a1cde3a6..ed35fafa4 100644 --- a/src/modules/partition/gui/PartitionViewStep.cpp +++ b/src/modules/partition/gui/PartitionViewStep.cpp @@ -471,7 +471,7 @@ PartitionViewStep::onLeave() "

" "To configure a GPT partition table on BIOS, " "(if not done so already) go back " - "and set the partion table to GPT, next create a 8 MB " + "and set the partition table to GPT, next create a 8 MB " "unformatted partition with the " "bios_grub flag enabled.

" "An unformatted 8 MB partition is necessary " diff --git a/src/modules/welcome/Config.cpp b/src/modules/welcome/Config.cpp index 43987f4b2..1c2b17c38 100644 --- a/src/modules/welcome/Config.cpp +++ b/src/modules/welcome/Config.cpp @@ -23,71 +23,15 @@ #include "utils/Logger.h" #include "utils/Retranslator.h" -void -RequirementsModel::setRequirementsList( const Calamares::RequirementsList& requirements ) -{ - CALAMARES_RETRANSLATE_SLOT( &RequirementsModel::retranslate ) - - emit beginResetModel(); - m_requirements = requirements; - - auto isUnSatisfied = []( const Calamares::RequirementEntry& e ) { return !e.satisfied; }; - auto isMandatoryAndUnSatisfied = []( const Calamares::RequirementEntry& e ) { return e.mandatory && !e.satisfied; }; - - m_satisfiedRequirements = std::none_of( m_requirements.begin(), m_requirements.end(), isUnSatisfied ); - m_satisfiedMandatory = std::none_of( m_requirements.begin(), m_requirements.end(), isMandatoryAndUnSatisfied ); - - emit satisfiedRequirementsChanged( m_satisfiedRequirements ); - emit satisfiedMandatoryChanged(); - emit endResetModel(); -} - -int -RequirementsModel::rowCount( const QModelIndex& ) const -{ - return m_requirements.count(); -} - -QVariant -RequirementsModel::data( const QModelIndex& index, int role ) const -{ - const auto requirement = m_requirements.at( index.row() ); - - switch ( role ) - { - case Roles::Name: - return requirement.name; - case Roles::Details: - return requirement.enumerationText(); - case Roles::NegatedText: - return requirement.negatedText(); - case Roles::Satisfied: - return requirement.satisfied; - case Roles::Mandatory: - return requirement.mandatory; - default: - return QVariant(); - } -} - -QHash< int, QByteArray > -RequirementsModel::roleNames() const -{ - static QHash< int, QByteArray > roles; - roles[ Roles::Name ] = "name"; - roles[ Roles::Details ] = "details"; - roles[ Roles::NegatedText ] = "negatedText"; - roles[ Roles::Satisfied ] = "satisfied"; - roles[ Roles::Mandatory ] = "mandatory"; - return roles; -} - Config::Config( QObject* parent ) : QObject( parent ) - , m_requirementsModel( new RequirementsModel( this ) ) + , m_requirementsModel( new Calamares::RequirementsModel( this ) ) , m_languages( CalamaresUtils::Locale::availableTranslations() ) { - connect( m_requirementsModel, &RequirementsModel::satisfiedRequirementsChanged, this, &Config::setIsNextEnabled ); + connect( m_requirementsModel, + &Calamares::RequirementsModel::satisfiedRequirementsChanged, + this, + &Config::setIsNextEnabled ); initLanguages(); @@ -98,9 +42,46 @@ void Config::retranslate() { m_genericWelcomeMessage = genericWelcomeMessage().arg( *Calamares::Branding::VersionedName ); - emit genericWelcomeMessageChanged(); + emit genericWelcomeMessageChanged( m_genericWelcomeMessage ); - m_requirementsModel->retranslate(); + if ( !m_requirementsModel->satisfiedRequirements() ) + { + QString message; + const bool setup = Calamares::Settings::instance()->isSetupMode(); + + if ( !m_requirementsModel->satisfiedMandatory() ) + { + 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_warningMessage = message.arg( *Calamares::Branding::ShortVersionedName ); + } + else + { + m_warningMessage = tr( "This program will ask you some questions and " + "set up %2 on your computer." ) + .arg( *Calamares::Branding::ProductName ); + } + + emit warningMessageChanged( m_warningMessage ); } CalamaresUtils::Locale::LabelModel* @@ -178,7 +159,7 @@ Config::setLanguageIcon( const QString& languageIcon ) } void -Config::setLocaleIndex( const int& index ) +Config::setLocaleIndex( int index ) { if ( index == m_localeIndex || index > CalamaresUtils::Locale::availableTranslations()->rowCount( QModelIndex() ) || index < 0 ) @@ -189,7 +170,7 @@ Config::setLocaleIndex( const int& index ) m_localeIndex = index; const auto& selectedLocale = m_languages->locale( m_localeIndex ).locale(); - cDebug() << "Selected locale" << selectedLocale; + cDebug() << "Index" << index << "Selected locale" << selectedLocale; QLocale::setDefault( selectedLocale ); CalamaresUtils::installTranslator( selectedLocale, Calamares::Branding::instance()->translationsDirectory() ); @@ -197,14 +178,14 @@ Config::setLocaleIndex( const int& index ) emit localeIndexChanged( m_localeIndex ); } -RequirementsModel& +Calamares::RequirementsModel& Config::requirementsModel() const { return *m_requirementsModel; } void -Config::setIsNextEnabled( const bool& isNextEnabled ) +Config::setIsNextEnabled( bool isNextEnabled ) { m_isNextEnabled = isNextEnabled; emit isNextEnabledChanged( m_isNextEnabled ); @@ -262,51 +243,8 @@ Config::setSupportUrl( const QString& url ) emit supportUrlChanged(); } -void -RequirementsModel::retranslate() -{ - if ( !m_satisfiedRequirements ) - { - QString message; - const bool setup = Calamares::Settings::instance()->isSetupMode(); - - if ( !m_satisfiedMandatory ) - { - 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_warningMessage = message.arg( *Calamares::Branding::ShortVersionedName ); - } - else - { - m_warningMessage = tr( "This program will ask you some questions and " - "set up %2 on your computer." ) - .arg( *Calamares::Branding::ProductName ); - } - - emit warningMessageChanged(); -} - QString -Config::genericWelcomeMessage() +Config::genericWelcomeMessage() const { QString message; @@ -325,3 +263,9 @@ Config::genericWelcomeMessage() return message; } + +QString +Config::warningMessage() const +{ + return m_warningMessage; +} diff --git a/src/modules/welcome/Config.h b/src/modules/welcome/Config.h index 389a45eec..80e4eeea9 100644 --- a/src/modules/welcome/Config.h +++ b/src/modules/welcome/Config.h @@ -19,79 +19,18 @@ #ifndef WELCOME_CONFIG_H #define WELCOME_CONFIG_H -#include "modulesystem/Requirement.h" #include "locale/LabelModel.h" +#include "modulesystem/Requirement.h" +#include "modulesystem/RequirementsModel.h" #include #include -// TODO: move this (and modulesystem/Requirement) to libcalamares -class RequirementsModel : public QAbstractListModel -{ - Q_OBJECT - Q_PROPERTY( bool satisfiedRequirements READ satisfiedRequirements NOTIFY satisfiedRequirementsChanged FINAL ) - Q_PROPERTY( bool satisfiedMandatory READ satisfiedMandatory NOTIFY satisfiedMandatoryChanged FINAL ) - Q_PROPERTY( QString warningMessage READ warningMessage NOTIFY warningMessageChanged FINAL ) - -public: - using QAbstractListModel::QAbstractListModel; - - enum Roles : short - { - Name, - Satisfied, - Mandatory, - Details, - NegatedText, - HasDetails - }; - - bool satisfiedRequirements() const { return m_satisfiedRequirements; } - - bool satisfiedMandatory() const { return m_satisfiedMandatory; } - - const Calamares::RequirementEntry& getEntry( const int& index ) const - { - if ( index > count() || index < 0 ) - { - return *( new Calamares::RequirementEntry() ); - } - - return m_requirements.at( index ); - } - - void setRequirementsList( const Calamares::RequirementsList& requirements ); - int rowCount( const QModelIndex& ) const override; - int count() const { return m_requirements.count(); } - - QString warningMessage() const { return m_warningMessage; } - - void retranslate(); - - QVariant data( const QModelIndex& index, int role ) const override; - -protected: - QHash< int, QByteArray > roleNames() const override; - -private: - Calamares::RequirementsList m_requirements; - bool m_satisfiedRequirements = false; - bool m_satisfiedMandatory = false; - - QString m_warningMessage; - -signals: - void satisfiedRequirementsChanged( bool value ); - void satisfiedMandatoryChanged(); - void warningMessageChanged(); -}; - - class Config : public QObject { Q_OBJECT Q_PROPERTY( CalamaresUtils::Locale::LabelModel* languagesModel READ languagesModel CONSTANT FINAL ) - Q_PROPERTY( RequirementsModel* requirementsModel MEMBER m_requirementsModel CONSTANT FINAL ) + Q_PROPERTY( Calamares::RequirementsModel* requirementsModel MEMBER m_requirementsModel CONSTANT FINAL ) Q_PROPERTY( QString languageIcon READ languageIcon CONSTANT FINAL ) @@ -99,6 +38,7 @@ class Config : public QObject Q_PROPERTY( int localeIndex READ localeIndex WRITE setLocaleIndex NOTIFY localeIndexChanged ) Q_PROPERTY( QString genericWelcomeMessage MEMBER m_genericWelcomeMessage NOTIFY genericWelcomeMessageChanged FINAL ) + Q_PROPERTY( QString warningMessage READ warningMessage NOTIFY warningMessageChanged FINAL ) Q_PROPERTY( QString supportUrl MEMBER m_supportUrl NOTIFY supportUrlChanged FINAL ) Q_PROPERTY( QString knownIssuesUrl MEMBER m_knownIssuesUrl NOTIFY knownIssuesUrlChanged FINAL ) @@ -109,13 +49,17 @@ class Config : public QObject public: Config( QObject* parent = nullptr ); + + Calamares::RequirementsModel& requirementsModel() const; + void setCountryCode( const QString& countryCode ); + + QString languageIcon() const; void setLanguageIcon( const QString& languageIcon ); - RequirementsModel& requirementsModel() const; - void setIsNextEnabled( const bool& isNextEnabled ); + void setIsNextEnabled( bool isNextEnabled ); - void setLocaleIndex( const int& index ); + void setLocaleIndex( int index ); int localeIndex() const { return m_localeIndex; } QString supportUrl() const; @@ -130,40 +74,44 @@ public: QString donateUrl() const; void setDonateUrl( const QString& url ); - QString genericWelcomeMessage(); - + QString genericWelcomeMessage() const; + QString warningMessage() const; public slots: CalamaresUtils::Locale::LabelModel* languagesModel() const; void retranslate(); - QString languageIcon() const; - -private: - void initLanguages(); - QVariantMap m_configurationMap; - RequirementsModel* m_requirementsModel; - QString m_languageIcon; - QString m_countryCode; - int m_localeIndex = 0; - bool m_isNextEnabled = false; - CalamaresUtils::Locale::LabelModel* m_languages; - - QString m_genericWelcomeMessage; - - QString m_supportUrl; - QString m_knownIssuesUrl; - QString m_releaseNotesUrl; - QString m_donateUrl; signals: void countryCodeChanged( QString countryCode ); void localeIndexChanged( int localeIndex ); void isNextEnabledChanged( bool isNextEnabled ); - void genericWelcomeMessageChanged(); + + void genericWelcomeMessageChanged( QString message ); + void warningMessageChanged( QString message ); + void supportUrlChanged(); void knownIssuesUrlChanged(); void releaseNotesUrlChanged(); void donateUrlChanged(); + +private: + void initLanguages(); + + Calamares::RequirementsModel* m_requirementsModel; + CalamaresUtils::Locale::LabelModel* m_languages; + + QString m_languageIcon; + QString m_countryCode; + int m_localeIndex = 0; + bool m_isNextEnabled = false; + + QString m_genericWelcomeMessage; + QString m_warningMessage; + + QString m_supportUrl; + QString m_knownIssuesUrl; + QString m_releaseNotesUrl; + QString m_donateUrl; }; #endif diff --git a/src/modules/welcome/WelcomeViewStep.cpp b/src/modules/welcome/WelcomeViewStep.cpp index 848d319db..3a0bef18f 100644 --- a/src/modules/welcome/WelcomeViewStep.cpp +++ b/src/modules/welcome/WelcomeViewStep.cpp @@ -47,6 +47,7 @@ WelcomeViewStep::WelcomeViewStep( QObject* parent ) // the instance of the qqc2 or qwidgets page m_widget = new WelcomePage( m_conf ); + connect( m_conf, &Config::localeIndexChanged, m_widget, &WelcomePage::externallySelectedLanguage ); } WelcomeViewStep::~WelcomeViewStep() diff --git a/src/modules/welcome/checker/CheckerContainer.cpp b/src/modules/welcome/checker/CheckerContainer.cpp index 0e790fbb4..10da425ab 100644 --- a/src/modules/welcome/checker/CheckerContainer.cpp +++ b/src/modules/welcome/checker/CheckerContainer.cpp @@ -31,7 +31,7 @@ #include -CheckerContainer::CheckerContainer( const RequirementsModel &model, QWidget* parent ) +CheckerContainer::CheckerContainer( const Calamares::RequirementsModel &model, QWidget* parent ) : QWidget( parent ) , m_waitingWidget( new WaitingWidget( QString(), this ) ) , m_checkerWidget( nullptr ) diff --git a/src/modules/welcome/checker/CheckerContainer.h b/src/modules/welcome/checker/CheckerContainer.h index f38f198ea..5ebefa36e 100644 --- a/src/modules/welcome/checker/CheckerContainer.h +++ b/src/modules/welcome/checker/CheckerContainer.h @@ -40,7 +40,7 @@ class CheckerContainer : public QWidget { Q_OBJECT public: - explicit CheckerContainer(const RequirementsModel &model, QWidget* parent = nullptr ); + explicit CheckerContainer(const Calamares::RequirementsModel &model, QWidget* parent = nullptr ); virtual ~CheckerContainer(); bool verdict() const; @@ -58,7 +58,7 @@ protected: bool m_verdict; private: - const RequirementsModel &m_model; + const Calamares::RequirementsModel &m_model; } ; #endif diff --git a/src/modules/welcome/checker/ResultsListWidget.cpp b/src/modules/welcome/checker/ResultsListWidget.cpp index 275010b2f..c16cda4c4 100644 --- a/src/modules/welcome/checker/ResultsListWidget.cpp +++ b/src/modules/welcome/checker/ResultsListWidget.cpp @@ -47,7 +47,7 @@ static void createResultWidgets( QLayout* layout, QList< ResultWidget* >& resultWidgets, - const RequirementsModel &model, + const Calamares::RequirementsModel &model, std::function< bool( const Calamares::RequirementEntry& ) > predicate ) { @@ -94,18 +94,18 @@ public: * The list must continue to exist for the lifetime of the dialog, * or UB happens. */ - ResultsListDialog( const RequirementsModel& model, QWidget* parent ); + ResultsListDialog( const Calamares::RequirementsModel& model, QWidget* parent ); virtual ~ResultsListDialog(); private: QLabel* m_title; QList< ResultWidget* > m_resultWidgets; ///< One widget for each entry with details available - const RequirementsModel& m_model; + const Calamares::RequirementsModel& m_model; void retranslate(); }; -ResultsListDialog::ResultsListDialog( const RequirementsModel& model, QWidget* parent) +ResultsListDialog::ResultsListDialog( const Calamares::RequirementsModel& model, QWidget* parent) : QDialog( parent ) , m_model( model ) { @@ -151,7 +151,7 @@ ResultsListDialog::retranslate() } -ResultsListWidget::ResultsListWidget( const RequirementsModel &model, QWidget* parent ) +ResultsListWidget::ResultsListWidget( const Calamares::RequirementsModel &model, QWidget* parent ) : QWidget( parent ) , m_model( model ) { diff --git a/src/modules/welcome/checker/ResultsListWidget.h b/src/modules/welcome/checker/ResultsListWidget.h index eb1bdb611..05a8adfa4 100644 --- a/src/modules/welcome/checker/ResultsListWidget.h +++ b/src/modules/welcome/checker/ResultsListWidget.h @@ -30,7 +30,7 @@ class ResultsListWidget : public QWidget { Q_OBJECT public: - explicit ResultsListWidget(const RequirementsModel &model, QWidget* parent); + explicit ResultsListWidget(const Calamares::RequirementsModel &model, QWidget* parent); private: /// @brief A link in the explanatory text has been clicked @@ -38,7 +38,7 @@ private: void retranslate(); QLabel* m_explanation = nullptr; ///< Explanatory text above the list, with link - const RequirementsModel &m_model; + const Calamares::RequirementsModel &m_model; QList< ResultWidget* > m_resultWidgets; ///< One widget for each unsatisfied entry }; diff --git a/src/modules/welcomeq/WelcomeQmlViewStep.cpp b/src/modules/welcomeq/WelcomeQmlViewStep.cpp index 0961ce67f..c1046b506 100644 --- a/src/modules/welcomeq/WelcomeQmlViewStep.cpp +++ b/src/modules/welcomeq/WelcomeQmlViewStep.cpp @@ -139,10 +139,10 @@ WelcomeQmlViewStep::setConfigurationMap( const QVariantMap& configurationMap ) m_config->setReleaseNotesUrl( jobOrBrandingSetting( Branding::ReleaseNotesUrl, configurationMap, "showReleaseNotesUrl" ) ); m_config->setDonateUrl( CalamaresUtils::getString( configurationMap, "showDonateUrl" ) ); - // TODO: expand Config class and set the remaining fields // with the configurationMap all those properties can be accesed withouth having to declare a property, get and setter for each + // TODO: expand Config class and set the remaining fields // with the configurationMap all those properties can be accessed without having to declare a property, get and setter for each // TODO: figure out how the requirements (held by ModuleManager) should be accessible - // to QML as a odel. //will be model as a qvariantmap containing a alert level and the message string + // to QML as a model. //will be model as a qvariantmap containing a alert level and the message string if ( configurationMap.contains( "requirements" ) && configurationMap.value( "requirements" ).type() == QVariant::Map ) { diff --git a/src/modules/welcomeq/about.qml b/src/modules/welcomeq/about.qml new file mode 100644 index 000000000..f301c6659 --- /dev/null +++ b/src/modules/welcomeq/about.qml @@ -0,0 +1,121 @@ +/* === This file is part of Calamares - === + * + * Copyright 2020, Anke Boersma + * + * Calamares is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Calamares is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Calamares. If not, see . + */ + +import io.calamares.ui 1.0 + +import QtQuick 2.7 +import QtQuick.Controls 2.0 +import QtQuick.Layouts 1.3 + +Item { + width: parent.width + height: parent.height + focus: true + + property var appName: "Calamares" + property var appVersion: "3.2.22" + + Rectangle { + id: textArea + x: 28 + y: 14 + anchors.fill: parent + color: "#f2f2f2" + + Column { + id: column + x: 130 + y: 40 + + + Rectangle { + width: 560 + height: 250 + radius: 10 + border.width: 0 + + Text { + width: 400 + height: 250 + anchors.centerIn: parent + text: qsTr("

%1


+ %2
+ for %3


+ Copyright 2014-2017 Teo Mrnjavac <teo@kde.org>
+ Copyright 2017-2020 Adriaan de Groot <groot@kde.org>
+ Thanks to the Calamares team + and the Calamares + translators team.

+ Calamares + development is sponsored by
+ Blue Systems - + Liberating Software." ) + .arg(appName) + .arg(appVersion) + .arg(Branding.string(Branding.VersionedName)) + + onLinkActivated: Qt.openUrlExternally(link) + + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.NoButton + cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor + } + + font.pointSize: 10 + anchors.verticalCenterOffset: 10 + anchors.horizontalCenterOffset: 40 + wrapMode: Text.WordWrap + } + + Image { + id: image + x: 8 + y: 12 + height: 100 + fillMode: Image.PreserveAspectFit + source: "img/squid.png" + } + + } + + } + + ToolButton { + id: toolButton + x: 19 + y: 29 + width: 105 + height: 48 + text: qsTr("Back") + hoverEnabled: true + onClicked: load.source = "" + + Image { + id: image1 + x: 0 + y: 13 + width: 22 + height: 22 + source: "img/chevron-left-solid.svg" + fillMode: Image.PreserveAspectFit + } + } + } + +} diff --git a/src/modules/welcomeq/img/chevron-left-solid.svg b/src/modules/welcomeq/img/chevron-left-solid.svg new file mode 100644 index 000000000..41061c287 --- /dev/null +++ b/src/modules/welcomeq/img/chevron-left-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/modules/welcomeq/img/squid.png b/src/modules/welcomeq/img/squid.png new file mode 100644 index 000000000..452e4450c Binary files /dev/null and b/src/modules/welcomeq/img/squid.png differ diff --git a/src/modules/welcomeq/welcomeq.qml b/src/modules/welcomeq/welcomeq.qml index 3275836a7..729b61b28 100644 --- a/src/modules/welcomeq/welcomeq.qml +++ b/src/modules/welcomeq/welcomeq.qml @@ -1,6 +1,7 @@ /* === This file is part of Calamares - === * * Copyright 2020, Adriaan de Groot + * Copyright 2020, Anke Boersma * * Calamares is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -31,7 +32,7 @@ Page header: Item { width: parent.width - height: 150 + height: parent.height Text { @@ -39,7 +40,7 @@ Page anchors.horizontalCenter: parent.horizontalCenter anchors.top: parent.top // In QML, QString::arg() only takes one argument - text: qsTr("

%1 %2

").arg(Branding.string(Branding.ProductName)).arg(Branding.string(Branding.Version)) + text: qsTr("

Welcome to the %1 %2 installer

").arg(Branding.string(Branding.ProductName)).arg(Branding.string(Branding.Version)) } Image { @@ -49,10 +50,9 @@ Page // .. otherwise the path is interpreted relative to the "call site", which // .. might be the QRC file. source: "file:/" + Branding.imagePath(Branding.ProductWelcome) - height: Math.min(100, parent.height) - width: height sourceSize.width: width sourceSize.height: height + fillMode: Image.PreserveAspectFit } RowLayout @@ -73,57 +73,65 @@ Page { Layout.fillWidth: true text: qsTr("About") - icon.name: "documentinfo" + icon.name: "dialog-information" Kirigami.Theme.backgroundColor: Qt.rgba(Kirigami.Theme.backgroundColor.r, Kirigami.Theme.backgroundColor.g, Kirigami.Theme.backgroundColor.b, 0.4) - Kirigami.Theme.textColor: "#fff" - + Kirigami.Theme.textColor: Kirigami.Theme.textColor + visible: false - onClicked: { } // TODO: show an about-Calamares window + onClicked: { + //onClicked: load.source = "file:/usr/share/calamares/branding/kaos_branding/show.qml" + onClicked: load.source = "about.qml" + } } Button { Layout.fillWidth: true text: qsTr("Support") - icon.name: "documentinfo" + icon.name: "system-help" Kirigami.Theme.backgroundColor: Qt.rgba(Kirigami.Theme.backgroundColor.r, Kirigami.Theme.backgroundColor.g, Kirigami.Theme.backgroundColor.b, 0.4) - Kirigami.Theme.textColor: "#fff" + Kirigami.Theme.textColor: Kirigami.Theme.textColor - visible: config.helpUrl.isValid - onClicked: Qt.openUrlExternally(config.helpUrl) + visible: config.supportUrl !== "" + onClicked: Qt.openUrlExternally(config.supportUrl) } Button { Layout.fillWidth: true text: qsTr("Known issues") - icon.name: "documentinfo" + icon.name: "tools-report-bug" Kirigami.Theme.backgroundColor: Qt.rgba(Kirigami.Theme.backgroundColor.r, Kirigami.Theme.backgroundColor.g, Kirigami.Theme.backgroundColor.b, 0.4) - Kirigami.Theme.textColor: "#fff" + Kirigami.Theme.textColor: Kirigami.Theme.textColor - visible: config.issuesUrl.isValid - onClicked: Qt.openUrlExternally(config.issuesUrl) + visible: config.knownIssuesUrl !== "" + onClicked: Qt.openUrlExternally(config.knownIssuesUrl) } Button { Layout.fillWidth: true text: qsTr("Release notes") - icon.name: "documentinfo" + icon.name: "folder-text" Kirigami.Theme.backgroundColor: Qt.rgba(Kirigami.Theme.backgroundColor.r, Kirigami.Theme.backgroundColor.g, Kirigami.Theme.backgroundColor.b, 0.4) - Kirigami.Theme.textColor: "#fff" + Kirigami.Theme.textColor: Kirigami.Theme.textColor - visible: config.notesUrl.isValid - onClicked: Qt.openUrlExternally(config.notesUrl) + visible: config.releaseNotesUrl !== "" + onClicked: Qt.openUrlExternally(config.releaseNotesUrl) } Button { Layout.fillWidth: true text: qsTr("Donate") - icon.name: "documentinfo" + icon.name: "taxes-finances" Kirigami.Theme.backgroundColor: Qt.rgba(Kirigami.Theme.backgroundColor.r, Kirigami.Theme.backgroundColor.g, Kirigami.Theme.backgroundColor.b, 0.4) - Kirigami.Theme.textColor: "#fff" + Kirigami.Theme.textColor: Kirigami.Theme.textColor - visible: config.donateUrl.isValid + visible: config.donateUrl !== "" onClicked: Qt.openUrlExternally(config.donateUrl) } } + Loader + { + id:load + anchors.fill: parent + } } } diff --git a/src/modules/welcomeq/welcomeq.qrc b/src/modules/welcomeq/welcomeq.qrc index 772bf7e88..84e598a27 100644 --- a/src/modules/welcomeq/welcomeq.qrc +++ b/src/modules/welcomeq/welcomeq.qrc @@ -1,5 +1,8 @@ welcomeq.qml + about.qml + img/squid.png + img/chevron-left-solid.svg