diff --git a/src/libcalamaresui/CMakeLists.txt b/src/libcalamaresui/CMakeLists.txt index a9d31c2c3..061a401bb 100644 --- a/src/libcalamaresui/CMakeLists.txt +++ b/src/libcalamaresui/CMakeLists.txt @@ -18,6 +18,8 @@ set( calamaresui_SOURCES utils/Paste.cpp viewpages/BlankViewStep.cpp + viewpages/ExecutionViewStep.cpp + viewpages/QmlViewStep.cpp viewpages/ViewStep.cpp widgets/ClickableLabel.cpp @@ -25,7 +27,6 @@ set( calamaresui_SOURCES widgets/WaitingWidget.cpp ${CMAKE_SOURCE_DIR}/3rdparty/waitingspinnerwidget.cpp - ExecutionViewStep.cpp Branding.cpp ViewManager.cpp ) diff --git a/src/libcalamaresui/ViewManager.cpp b/src/libcalamaresui/ViewManager.cpp index 68d918971..7492f1f8b 100644 --- a/src/libcalamaresui/ViewManager.cpp +++ b/src/libcalamaresui/ViewManager.cpp @@ -21,17 +21,16 @@ #include "ViewManager.h" -#include "viewpages/BlankViewStep.h" -#include "viewpages/ViewStep.h" - #include "Branding.h" -#include "ExecutionViewStep.h" #include "JobQueue.h" #include "Settings.h" #include "utils/Logger.h" #include "utils/Paste.h" #include "utils/Retranslator.h" +#include "viewpages/BlankViewStep.h" +#include "viewpages/ViewStep.h" +#include "viewpages/ExecutionViewStep.h" #include #include @@ -159,13 +158,6 @@ void ViewManager::insertViewStep( int before, ViewStep* step ) { m_steps.insert( before, step ); - QLayout* layout = step->widget()->layout(); - if ( layout ) - { - layout->setContentsMargins( 0, 0, 0, 0 ); - } - m_stack->insertWidget( before, step->widget() ); - connect( step, &ViewStep::enlarge, this, &ViewManager::enlarge ); connect( step, &ViewStep::nextStatusChanged, this, [this]( bool status ) { ViewStep* vs = qobject_cast< ViewStep* >( sender() ); @@ -178,6 +170,17 @@ ViewManager::insertViewStep( int before, ViewStep* step ) } } ); + if ( !step->widget() ) + { + cError() << "ViewStep" << step->moduleInstanceKey() << "has no widget."; + } + + QLayout* layout = step->widget()->layout(); + if ( layout ) + { + layout->setContentsMargins( 0, 0, 0, 0 ); + } + m_stack->insertWidget( before, step->widget() ); m_stack->setCurrentIndex( 0 ); step->widget()->setFocus(); } diff --git a/src/libcalamaresui/modulesystem/ModuleManager.cpp b/src/libcalamaresui/modulesystem/ModuleManager.cpp index c3e9569ef..6202493e2 100644 --- a/src/libcalamaresui/modulesystem/ModuleManager.cpp +++ b/src/libcalamaresui/modulesystem/ModuleManager.cpp @@ -19,7 +19,6 @@ #include "ModuleManager.h" -#include "ExecutionViewStep.h" #include "Module.h" #include "RequirementsChecker.h" #include "Settings.h" @@ -27,6 +26,7 @@ #include "utils/Logger.h" #include "utils/Yaml.h" +#include "viewpages/ExecutionViewStep.h" #include #include @@ -129,8 +129,9 @@ ModuleManager::doInit() } // At this point m_availableDescriptorsByModuleName is filled with // the modules that were found in the search paths. - cDebug() << "Found" << m_availableDescriptorsByModuleName.count() << "modules" - << m_moduleDirectoriesByModuleName.count() << "names"; + cDebug() << "Found" + << m_availableDescriptorsByModuleName.count() << "modules" + << m_moduleDirectoriesByModuleName.count() << "names"; emit initDone(); } diff --git a/src/libcalamaresui/viewpages/BlankViewStep.h b/src/libcalamaresui/viewpages/BlankViewStep.h index 17d323c85..ab44205ac 100644 --- a/src/libcalamaresui/viewpages/BlankViewStep.h +++ b/src/libcalamaresui/viewpages/BlankViewStep.h @@ -19,12 +19,7 @@ #ifndef BLANKVIEWSTEP_H #define BLANKVIEWSTEP_H -#include - -#include -#include - -class QWidget; +#include "viewpages/ViewStep.h" namespace Calamares { diff --git a/src/libcalamaresui/ExecutionViewStep.cpp b/src/libcalamaresui/viewpages/ExecutionViewStep.cpp similarity index 99% rename from src/libcalamaresui/ExecutionViewStep.cpp rename to src/libcalamaresui/viewpages/ExecutionViewStep.cpp index 501995c07..e0921910d 100644 --- a/src/libcalamaresui/ExecutionViewStep.cpp +++ b/src/libcalamaresui/viewpages/ExecutionViewStep.cpp @@ -18,16 +18,16 @@ * along with Calamares. If not, see . */ -#include +#include "ExecutionViewStep.h" #include "Branding.h" #include "Job.h" #include "JobQueue.h" #include "Settings.h" #include "ViewManager.h" + #include "modulesystem/Module.h" #include "modulesystem/ModuleManager.h" - #include "utils/CalamaresUtilsGui.h" #include "utils/Dirs.h" #include "utils/Logger.h" diff --git a/src/libcalamaresui/ExecutionViewStep.h b/src/libcalamaresui/viewpages/ExecutionViewStep.h similarity index 100% rename from src/libcalamaresui/ExecutionViewStep.h rename to src/libcalamaresui/viewpages/ExecutionViewStep.h diff --git a/src/libcalamaresui/viewpages/QmlViewStep.cpp b/src/libcalamaresui/viewpages/QmlViewStep.cpp new file mode 100644 index 000000000..b009d1626 --- /dev/null +++ b/src/libcalamaresui/viewpages/QmlViewStep.cpp @@ -0,0 +1,277 @@ +/* === 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 . + */ + +#include "QmlViewStep.h" + +#include "Branding.h" + +#include "utils/Dirs.h" +#include "utils/Logger.h" +#include "utils/NamedEnum.h" +#include "utils/Variant.h" +#include "widgets/WaitingWidget.h" + +#include +#include +#include +#include +#include +#include + +static const NamedEnumTable< Calamares::QmlViewStep::QmlSearch >& +searchNames() +{ + using Search = Calamares::QmlViewStep::QmlSearch; + // *INDENT-OFF* + // clang-format off + static NamedEnumTable< Search > names { + { QStringLiteral( "both" ), Search::Both }, + { QStringLiteral( "qrc" ), Search::QrcOnly }, + { QStringLiteral( "branding" ), Search::BrandingOnly } + }; + // *INDENT-ON* + // clang-format on + + return names; +} + +namespace Calamares +{ + +QmlViewStep::QmlViewStep( const QString& name, QObject* parent ) + : ViewStep( parent ) + , m_name( name ) + , m_widget( new QWidget ) + , m_spinner( new WaitingWidget( tr( "Loading ..." ) ) ) + , m_qmlWidget( new QQuickWidget ) +{ + QVBoxLayout* layout = new QVBoxLayout( m_widget ); + layout->addWidget( m_spinner ); + + m_qmlWidget->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); + m_qmlWidget->setResizeMode( QQuickWidget::SizeRootObjectToView ); + m_qmlWidget->engine()->addImportPath( CalamaresUtils::qmlModulesDir().absolutePath() ); + + // QML Loading starts when the configuration for the module is set. +} + +QmlViewStep::~QmlViewStep() {} + +QString +QmlViewStep::prettyName() const +{ + // TODO: query the QML itself + return tr( "QML Step %1." ).arg( m_name ); +} + + +} // namespace Calamares + +bool +Calamares::QmlViewStep::isAtBeginning() const +{ + return true; +} + +bool +Calamares::QmlViewStep::isAtEnd() const +{ + return true; +} +bool +Calamares::QmlViewStep::isBackEnabled() const +{ + return true; +} + +bool +Calamares::QmlViewStep::isNextEnabled() const +{ + return true; +} + +Calamares::JobList +Calamares::QmlViewStep::jobs() const +{ + return JobList(); +} + +void +Calamares::QmlViewStep::onActivate() +{ + // TODO: call into QML +} + +void +Calamares::QmlViewStep::onLeave() +{ + // TODO: call into QML +} + +QWidget* +Calamares::QmlViewStep::widget() +{ + return m_widget; +} + +void +Calamares::QmlViewStep::loadComplete() +{ + cDebug() << "QML component" << m_qmlFileName << m_qmlComponent->status(); + if ( m_qmlComponent->status() == QQmlComponent::Error ) + { + showFailedQml(); + } + if ( m_qmlComponent->isReady() && !m_qmlObject ) + { + cDebug() << "QML component complete" << m_qmlFileName; + // Don't do this again + disconnect( m_qmlComponent, &QQmlComponent::statusChanged, this, &QmlViewStep::loadComplete ); + + QObject* o = m_qmlComponent->create(); + m_qmlObject = qobject_cast< QQuickItem* >( o ); + if ( !m_qmlObject ) + { + cError() << Logger::SubEntry << "Could not create QML from" << m_qmlFileName; + delete o; + } + else + { + // setContent() is public API, but not documented publicly. + // It is marked \internal in the Qt sources, but does exactly + // what is needed: sets up visual parent by replacing the root + // item, and handling resizes. + m_qmlWidget->setContent( QUrl( m_qmlFileName ), m_qmlComponent, m_qmlObject ); + showQml(); + } + } +} + +void +Calamares::QmlViewStep::showQml() +{ + if ( !m_qmlWidget || !m_qmlObject ) + { + cDebug() << "showQml() called but no QML object"; + return; + } + if ( m_spinner ) + { + m_widget->layout()->removeWidget( m_spinner ); + m_widget->layout()->addWidget( m_qmlWidget ); + delete m_spinner; + m_spinner = nullptr; + } + else + { + cDebug() << "showQml() called twice"; + } +} + + +/** @brief Find a suitable QML file, given the search method and name hints + * + * Returns QString() if nothing is found (which would mean the module + * is badly configured). + */ +QString +searchQmlFile( Calamares::QmlViewStep::QmlSearch method, const QString& configuredName, const QString& moduleName ) +{ + using QmlSearch = Calamares::QmlViewStep::QmlSearch; + + cDebug() << "Looking for QML for" << moduleName; + QStringList candidates; + if ( configuredName.startsWith( '/' ) ) + { + candidates << configuredName; + } + if ( ( method == QmlSearch::Both ) || ( method == QmlSearch::BrandingOnly ) ) + { + QString brandDir = Calamares::Branding::instance()->componentDirectory(); + candidates << ( configuredName.isEmpty() ? QString() + : QStringLiteral( "%1/%2.qml" ).arg( brandDir, configuredName ) ) + << ( moduleName.isEmpty() ? QString() : QStringLiteral( "%1/%2.qml" ).arg( brandDir, moduleName ) ); + } + if ( ( method == QmlSearch::Both ) || ( method == QmlSearch::QrcOnly ) ) + { + candidates << ( configuredName.isEmpty() ? QString() : QStringLiteral( ":/%1.qml" ).arg( configuredName ) ) + << ( moduleName.isEmpty() ? QString() : QStringLiteral( ":/%1.qml" ).arg( moduleName ) ); + } + for ( const QString& candidate : candidates ) + { + if ( candidate.isEmpty() ) + { + continue; + } + cDebug() << Logger::SubEntry << "Looking at QML file" << candidate; + if ( QFile::exists( candidate ) ) + { + if ( candidate.startsWith( ':' ) ) + { + // Inconsistency: QFile only sees the file with :, + // but QML needs an explicit scheme (of qrc:) + return QStringLiteral( "qrc" ) + candidate; + } + return candidate; + } + } + cDebug() << Logger::SubEntry << "None found."; + return QString(); +} + +void +Calamares::QmlViewStep::setConfigurationMap( const QVariantMap& configurationMap ) +{ + bool ok = false; + m_searchMethod = searchNames().find( CalamaresUtils::getString( configurationMap, "search" ), ok ); + if ( !ok ) + { + cDebug() << "Bad QML search mode."; + } + + QString qmlFile = CalamaresUtils::getString( configurationMap, "filename" ); + if ( qmlFile.isEmpty() ) + { + // TODO use the module instance + } + + if ( !m_qmlComponent ) + { + m_qmlFileName = searchQmlFile( m_searchMethod, qmlFile, m_name ); + + cDebug() << "QmlViewStep" << moduleInstanceKey() << "loading" << m_qmlFileName; + m_qmlComponent = new QQmlComponent( + m_qmlWidget->engine(), QUrl( m_qmlFileName ), QQmlComponent::CompilationMode::Asynchronous ); + connect( m_qmlComponent, &QQmlComponent::statusChanged, this, &QmlViewStep::loadComplete ); + if ( m_qmlComponent->status() == QQmlComponent::Error ) + { + showFailedQml(); + } + } + else + { + cWarning() << "QML configuration set after component has loaded."; + } +} + +void +Calamares::QmlViewStep::showFailedQml() +{ + cWarning() << "QmlViewStep" << moduleInstanceKey() << "loading failed."; + m_spinner->setText( prettyName() + ' ' + tr( "Loading failed." ) ); +} diff --git a/src/libcalamaresui/viewpages/QmlViewStep.h b/src/libcalamaresui/viewpages/QmlViewStep.h new file mode 100644 index 000000000..46ba29a53 --- /dev/null +++ b/src/libcalamaresui/viewpages/QmlViewStep.h @@ -0,0 +1,102 @@ +/* === 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 QMLVIEWSTEP_H +#define QMLVIEWSTEP_H + +#include "viewpages/ViewStep.h" + +class QQmlComponent; +class QQuickItem; +class QQuickWidget; +class WaitingWidget; + +namespace Calamares +{ + +/** @brief A viewstep that uses QML for the UI + * + * This is generally a **base** class for other view steps, but + * it can be used stand-alone for viewsteps that don't really have + * any functionality. + */ +class QmlViewStep : public Calamares::ViewStep +{ + Q_OBJECT + +public: + enum class QmlSearch + { + QrcOnly, + BrandingOnly, + Both + }; + + /** @brief Creates a QML view step + * + * The name should not have an extension or schema or anything; + * just the plain name, which will be searched as "/.qml" in + * QRC files, or ".qml" in suitable branding paths. + * The search behavior depends on a QmlSearch value. + */ + QmlViewStep( const QString& name, QObject* parent = nullptr ); + virtual ~QmlViewStep() override; + + virtual QString prettyName() const override; + + virtual QWidget* widget() override; + + virtual bool isNextEnabled() const override; + virtual bool isBackEnabled() const override; + + virtual bool isAtBeginning() const override; + virtual bool isAtEnd() const override; + + virtual void onActivate() override; + virtual void onLeave() override; + + /// @brief QML widgets don't produce jobs by default + virtual JobList jobs() const override; + + /// @brief Configure search paths; subclasses should call this as well + virtual void setConfigurationMap( const QVariantMap& configurationMap ) override; + +private Q_SLOTS: + void loadComplete(); + +private: + /// @brief Swap out the spinner for the QQuickWidget + void showQml(); + /// @brief Show error message in spinner. + void showFailedQml(); + + /// @brief Controls where m_name is searched + QmlSearch m_searchMethod; + + QString m_name; + QString m_qmlFileName; + + QWidget* m_widget = nullptr; + WaitingWidget* m_spinner = nullptr; + QQuickWidget* m_qmlWidget = nullptr; + QQmlComponent* m_qmlComponent = nullptr; + QQuickItem* m_qmlObject = nullptr; +}; + +} // namespace Calamares +#endif diff --git a/src/modules/dummyqml/CMakeLists.txt b/src/modules/dummyqml/CMakeLists.txt new file mode 100644 index 000000000..9a7532e9e --- /dev/null +++ b/src/modules/dummyqml/CMakeLists.txt @@ -0,0 +1,11 @@ +calamares_add_plugin( dummyqml + TYPE viewmodule + EXPORT_MACRO PLUGINDLLEXPORT_PRO + SOURCES + DummyQmlViewStep.cpp + RESOURCES + dummyqml.qrc + LINK_PRIVATE_LIBRARIES + calamaresui + SHARED_LIB +) diff --git a/src/modules/dummyqml/DummyQmlViewStep.cpp b/src/modules/dummyqml/DummyQmlViewStep.cpp new file mode 100644 index 000000000..030779f7f --- /dev/null +++ b/src/modules/dummyqml/DummyQmlViewStep.cpp @@ -0,0 +1,52 @@ +/* === 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 + * 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 "DummyQmlViewStep.h" + +#include + +DummyQmlViewStep::DummyQmlViewStep( QObject* parent ) + : Calamares::QmlViewStep( "dummyqml", parent ) +{ +} + +DummyQmlViewStep::~DummyQmlViewStep() {} + +QString +DummyQmlViewStep::prettyName() const +{ + return m_notesName ? m_notesName->get() : tr( "Notes" ); +} + +void +DummyQmlViewStep::setConfigurationMap( const QVariantMap& configurationMap ) +{ + Calamares::QmlViewStep::setConfigurationMap( configurationMap ); // call parent implementation + + bool qmlLabel_ok = false; + auto qmlLabel = CalamaresUtils::getSubMap( configurationMap, "qmlLabel", qmlLabel_ok ); + + if ( qmlLabel.contains( "notes" ) ) + { + m_notesName = new CalamaresUtils::Locale::TranslatedString( qmlLabel, "notes" ); + } + +} + +CALAMARES_PLUGIN_FACTORY_DEFINITION( DummyQmlViewStepFactory, registerPlugin< DummyQmlViewStep >(); ) diff --git a/src/modules/dummyqml/DummyQmlViewStep.h b/src/modules/dummyqml/DummyQmlViewStep.h new file mode 100644 index 000000000..cf49436b0 --- /dev/null +++ b/src/modules/dummyqml/DummyQmlViewStep.h @@ -0,0 +1,48 @@ +/* === 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 + * 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 DUMMYQMLVIEWSTEP_H +#define DUMMYQMLVIEWSTEP_H + +#include "PluginDllMacro.h" +#include "locale/TranslatableConfiguration.h" +#include "utils/CalamaresUtilsSystem.h" +#include "utils/Variant.h" +#include "utils/PluginFactory.h" +#include "viewpages/QmlViewStep.h" + +class PLUGINDLLEXPORT DummyQmlViewStep : public Calamares::QmlViewStep +{ + Q_OBJECT + +public: + DummyQmlViewStep( QObject* parent = nullptr ); + virtual ~DummyQmlViewStep() override; + + QString prettyName() const override; + + void setConfigurationMap( const QVariantMap& configurationMap ) override; + +private: + CalamaresUtils::Locale::TranslatedString* m_notesName; // As it appears in the sidebar +}; + +CALAMARES_PLUGIN_FACTORY_DECLARATION( DummyQmlViewStepFactory ) + +#endif diff --git a/src/modules/dummyqml/dummyqml.conf b/src/modules/dummyqml/dummyqml.conf new file mode 100644 index 000000000..9e7a83fdd --- /dev/null +++ b/src/modules/dummyqml/dummyqml.conf @@ -0,0 +1,29 @@ +# The dummy QML module just displays a QML page. It doesn't +# have much in the way of own configuration, only where +# the QML file is searched. +# +# QML modules can search for the QML inside the Qt resources +# (QRC) which are compiled into the module, or in the branding +# setup for Calamares, (or both of them, with branding taking +# precedence). This allows the module to ship a default UI and +# branding to optionally introduce a replacement file. +# +# Generally, leave the search method set to "both" because if +# you don't want to brand the UI, just don't ship a branding +# QML file for it. +# +# To support instanced QML modules, searches in the branding +# directory look for the full module@instanceid name as well. +--- +# Search mode. Valid values are "both", "qrc" and "branding" +search: both + +# Name of the QML file. If not set, uses the name of the instance +# of the module (e.g. if you list this module in `settings.conf` +# in the *instances* section, you get *id*, otherwise it would +# normally be "dummyqml"). +# filename: dummyqml + +qmlLabel: + notes: "Release Notes" + notes[nl]: "Opmerkingen" diff --git a/src/modules/dummyqml/dummyqml.qml b/src/modules/dummyqml/dummyqml.qml new file mode 100644 index 000000000..3cbfa65fc --- /dev/null +++ b/src/modules/dummyqml/dummyqml.qml @@ -0,0 +1,73 @@ +/* === 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 QtQuick 2.7 +import QtQuick.Controls 2.2 +import QtQuick.Window 2.2 +import QtQuick.Layouts 1.3 +import QtQuick.Controls.Material 2.1 + +Item { + width: 740 + height: 420 + + Flickable { + id: flick + anchors.fill: parent + contentHeight: 800 + + ScrollBar.vertical: ScrollBar { + width: 10 + policy: ScrollBar.AlwaysOn + } + + TextArea { + id: intro + x: 1 + y: 0 + width: 720 + font.pointSize: 14 + textFormat: Text.RichText + antialiasing: true + activeFocusOnPress: false + wrapMode: Text.WordWrap + + text: qsTr("

Generic GNU/Linux 2017.8 LTS Soapy Sousaphone

+

This an example QML file, showing options in RichText with Flickable content.

+ +

QML with RichText can use HTML tags, Flickable content is useful for touchscreens.

+ +

This is bold text

+

This is italic text

+

This is underlined text

+

This is strikethrough

+ +

Code example: + ls -l /home

+ +

Lists:

+
    +
  • Intel CPU systems
  • +
  • AMD CPU systems
  • +
+ +

The vertical scrollbar is adjustable, current width set to 10.

") + + } + } +} diff --git a/src/modules/dummyqml/dummyqml.qrc b/src/modules/dummyqml/dummyqml.qrc new file mode 100644 index 000000000..85b1da5ca --- /dev/null +++ b/src/modules/dummyqml/dummyqml.qrc @@ -0,0 +1,5 @@ + + + dummyqml.qml + + diff --git a/src/modules/summary/SummaryPage.cpp b/src/modules/summary/SummaryPage.cpp index 61a9c2ec2..7114f27ee 100644 --- a/src/modules/summary/SummaryPage.cpp +++ b/src/modules/summary/SummaryPage.cpp @@ -23,13 +23,13 @@ #include "SummaryViewStep.h" #include "Branding.h" -#include "ExecutionViewStep.h" #include "Settings.h" #include "ViewManager.h" #include "utils/CalamaresUtilsGui.h" #include "utils/Logger.h" #include "utils/Retranslator.h" +#include "viewpages/ExecutionViewStep.h" #include #include diff --git a/src/modules/welcome/CMakeLists.txt b/src/modules/welcome/CMakeLists.txt index e25b7f5d0..b25bb6720 100644 --- a/src/modules/welcome/CMakeLists.txt +++ b/src/modules/welcome/CMakeLists.txt @@ -40,3 +40,13 @@ calamares_add_plugin( welcome Qt5::Network SHARED_LIB ) + +add_executable( welcomeqmltest qmlmain.cpp Config.cpp ) +target_link_libraries( welcomeqmltest PRIVATE calamaresui Qt5::Core ) +set_target_properties( welcomeqmltest + PROPERTIES + ENABLE_EXPORTS TRUE + RUNTIME_OUTPUT_NAME welcomeqmltest +) +calamares_automoc( welcomeqmltest ) +calamares_autouic( welcomeqmltest ) diff --git a/src/modules/welcome/Config.cpp b/src/modules/welcome/Config.cpp new file mode 100644 index 000000000..b46b85bf3 --- /dev/null +++ b/src/modules/welcome/Config.cpp @@ -0,0 +1,28 @@ +/* === This file is part of Calamares - === + * + * Copyright 2019, 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 "Config.h" + +Config::Config() + : m_helpUrl( "https://www.kde.org/" ) +{ +} + +Config::~Config() +{ +} diff --git a/src/modules/welcome/Config.h b/src/modules/welcome/Config.h new file mode 100644 index 000000000..7b0cfd734 --- /dev/null +++ b/src/modules/welcome/Config.h @@ -0,0 +1,40 @@ +/* === This file is part of Calamares - === + * + * Copyright 2019, 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 WELCOME_CONFIG_H +#define WELCOME_CONFIG_H + +#include +#include + +class Config : public QObject +{ + Q_OBJECT + Q_PROPERTY( QUrl helpUrl READ helpUrl WRITE setHelpUrl CONSTANT ) +public: + Config(); + virtual ~Config(); + + QUrl helpUrl() const { return m_helpUrl; } + void setHelpUrl( const QUrl& url ) { m_helpUrl = url; } + +private: + QUrl m_helpUrl; +}; + +#endif diff --git a/src/modules/welcome/qmlmain.cpp b/src/modules/welcome/qmlmain.cpp new file mode 100644 index 000000000..a9dd1875d --- /dev/null +++ b/src/modules/welcome/qmlmain.cpp @@ -0,0 +1,84 @@ +/* Example executable showing a QML page and using the + * models from libcalamares for displaying a welcome. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "Branding.h" +#include "JobQueue.h" +#include "Settings.h" +#include "locale/LabelModel.h" +#include "utils/Logger.h" + +#include "Config.h" + +static Config* theConfig() +{ + static Config* cnf = new Config(); + return cnf; +} + +int main(int argc, char **argv) +{ + QApplication a( argc, argv ); + + KAboutData aboutData( "calamares", + "Calamares", + "0.1", + "Calamares QML Test Application", + KAboutLicense::GPL_V3, + QString(), + QString(), + "https://calamares.io", + "https://github.com/calamares/calamares/issues" ); + KAboutData::setApplicationData( aboutData ); + a.setApplicationDisplayName( QString() ); // To avoid putting an extra "Calamares/" into the log-file + + Logger::setupLogLevel( Logger::LOGVERBOSE ); + + std::unique_ptr< Calamares::Settings > settings_p( Calamares::Settings::init( QString() ) ); + std::unique_ptr< Calamares::JobQueue > jobqueue_p( new Calamares::JobQueue( nullptr ) ); + + Calamares::Branding defaultBrand( "src/branding/default/branding.desc" ); + cDebug() << "Branding @" << (void *)Calamares::Branding::instance(); + + QMainWindow mw; + QWidget background; + QVBoxLayout vl; + QLabel l( "Hello, world", &mw ); + QQuickWidget qqw( &mw ); + vl.addWidget( &qqw ); + vl.addWidget( &l ); + background.setLayout( &vl ); + mw.setCentralWidget( &background ); + mw.resize( QSize( 400, 400 ) ); + mw.show(); + + Config cnf; + if ( argc > 1 ) + { + cnf.setHelpUrl( QUrl( argv[1] ) ); + } + + // TODO: this should put the one config object in the context, rather than adding a factory function to share it everywhere + qmlRegisterSingletonType< Config >( "io.calamares.modules.welcome", 1, 0, "PotatoConfig", [](QQmlEngine*, QJSEngine*) -> QObject* { return theConfig(); }); + + qmlRegisterSingletonType< CalamaresUtils::Locale::LabelModel >( "io.calamares.locale", 1, 0, "LocaleModel", [](QQmlEngine*, QJSEngine*) -> QObject* { return CalamaresUtils::Locale::availableTranslations(); } ); + + qqw.setSource( QUrl::fromLocalFile("../src/modules/welcome/welcome.qml") ); + + return a.exec(); +} diff --git a/src/modules/welcome/welcome.qml b/src/modules/welcome/welcome.qml new file mode 100644 index 000000000..bdb7c4416 --- /dev/null +++ b/src/modules/welcome/welcome.qml @@ -0,0 +1,29 @@ +import QtQuick 2.0; +import QtQuick.Controls 2.3; +import io.calamares.modules.welcome 1.0; +import io.calamares.locale 1.0; + +Rectangle { + width: 200; + height: 200; + color: "pink"; + + Label { + id: label; + anchors.centerIn: parent; + text: "Welcome to Calamares"; + } + + Button { + id: thebutton; + anchors.top: label.bottom; + text: PotatoConfig.helpUrl; + + } + + ListView { + anchors.fill: parent; + model: LocaleModel; + delegate: Label { text: display } + } +} diff --git a/src/modules/welcomeq/CMakeLists.txt b/src/modules/welcomeq/CMakeLists.txt new file mode 100644 index 000000000..7ee0350af --- /dev/null +++ b/src/modules/welcomeq/CMakeLists.txt @@ -0,0 +1,44 @@ +# This is a re-write of the welcome module using QML view steps +# instead of widgets. + +set( _welcome ${CMAKE_CURRENT_SOURCE_DIR}/../welcome ) + +include_directories( ${PROJECT_BINARY_DIR}/src/libcalamaresui ${_welcome} ) + +# DUPLICATED WITH WELCOME MODULE +find_package( Qt5 ${QT_VERSION} CONFIG REQUIRED DBus Network ) + +find_package( LIBPARTED ) +if ( LIBPARTED_FOUND ) + set( PARTMAN_SRC ${_welcome}/checker/partman_devices.c ) + set( CHECKER_LINK_LIBRARIES ${LIBPARTED_LIBRARY} ) +else() + set( PARTMAN_SRC ) + set( CHECKER_LINK_LIBRARIES ) + add_definitions( -DWITHOUT_LIBPARTED ) +endif() + +set( CHECKER_SOURCES + ${_welcome}/checker/CheckerContainer.cpp + ${_welcome}/checker/GeneralRequirements.cpp + ${_welcome}/checker/ResultWidget.cpp + ${_welcome}/checker/ResultsListWidget.cpp + ${PARTMAN_SRC} +) + +calamares_add_plugin( welcomeq + TYPE viewmodule + EXPORT_MACRO PLUGINDLLEXPORT_PRO + SOURCES + ${CHECKER_SOURCES} + WelcomeQmlViewStep.cpp + Config.cpp + RESOURCES + welcomeq.qrc + LINK_PRIVATE_LIBRARIES + calamaresui + ${CHECKER_LINK_LIBRARIES} + Qt5::DBus + Qt5::Network + SHARED_LIB +) diff --git a/src/modules/welcomeq/Config.cpp b/src/modules/welcomeq/Config.cpp new file mode 100644 index 000000000..6d085143c --- /dev/null +++ b/src/modules/welcomeq/Config.cpp @@ -0,0 +1,28 @@ +/* === 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 "Config.h" + +Config::Config() + : m_helpUrl( "https://www.kde.org/" ) +{ +} + +Config::~Config() +{ +} diff --git a/src/modules/welcomeq/Config.h b/src/modules/welcomeq/Config.h new file mode 100644 index 000000000..2295d4ee2 --- /dev/null +++ b/src/modules/welcomeq/Config.h @@ -0,0 +1,40 @@ +/* === 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 WELCOME_CONFIG_H +#define WELCOME_CONFIG_H + +#include +#include + +class Config : public QObject +{ + Q_OBJECT + Q_PROPERTY( QUrl helpUrl READ helpUrl WRITE setHelpUrl CONSTANT ) +public: + Config(); + virtual ~Config(); + + QUrl helpUrl() const { return m_helpUrl; } + void setHelpUrl( const QUrl& url ) { m_helpUrl = url; } + +private: + QUrl m_helpUrl; +}; + +#endif diff --git a/src/modules/welcomeq/WelcomeQmlViewStep.cpp b/src/modules/welcomeq/WelcomeQmlViewStep.cpp new file mode 100644 index 000000000..2a5b0f661 --- /dev/null +++ b/src/modules/welcomeq/WelcomeQmlViewStep.cpp @@ -0,0 +1,243 @@ +/* === This file is part of Calamares - === + * + * Copyright 2014-2015, Teo Mrnjavac + * Copyright 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 + * 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 "WelcomeQmlViewStep.h" + +#include "checker/GeneralRequirements.h" + +#include "geoip/Handler.h" +#include "locale/LabelModel.h" +#include "locale/Lookup.h" +#include "utils/Logger.h" +#include "utils/Variant.h" + +#include "Branding.h" +#include "modulesystem/ModuleManager.h" + +#include +#include +#include + +CALAMARES_PLUGIN_FACTORY_DEFINITION( WelcomeQmlViewStepFactory, registerPlugin< WelcomeQmlViewStep >(); ) + +WelcomeQmlViewStep::WelcomeQmlViewStep( QObject* parent ) + : Calamares::ViewStep( parent ) + , m_requirementsChecker( new GeneralRequirements( this ) ) +{ + connect( Calamares::ModuleManager::instance(), + &Calamares::ModuleManager::requirementsComplete, + this, + &WelcomeQmlViewStep::nextStatusChanged ); +} + + +WelcomeQmlViewStep::~WelcomeQmlViewStep() +{ +} + + +QString +WelcomeQmlViewStep::prettyName() const +{ + return tr( "Welcome" ); +} + + +QWidget* +WelcomeQmlViewStep::widget() +{ + return nullptr; +} + + +bool +WelcomeQmlViewStep::isNextEnabled() const +{ + // TODO: should return true + return false; +} + + +bool +WelcomeQmlViewStep::isBackEnabled() const +{ + // TODO: should return true (it's weird that you are not allowed to have welcome *after* anything + return false; +} + + +bool +WelcomeQmlViewStep::isAtBeginning() const +{ + // TODO: adjust to "pages" in the QML + return true; +} + + +bool +WelcomeQmlViewStep::isAtEnd() const +{ + // TODO: adjust to "pages" in the QML + return true; +} + + +Calamares::JobList +WelcomeQmlViewStep::jobs() const +{ + return Calamares::JobList(); +} + + +/** @brief Look up a URL for a button + * + * Looks up @p key in @p map; if it is a *boolean* value, then + * assume an old-style configuration, and fetch the string from + * the branding settings @p e. If it is a string, not a boolean, + * use it as-is. If not found, or a weird type, returns empty. + * + * This allows switching the showKnownIssuesUrl and similar settings + * in welcome.conf from a boolean (deferring to branding) to an + * actual string for immediate use. Empty strings, as well as + * "false" as a setting, will hide the buttons as before. + */ +static QString +jobOrBrandingSetting( Calamares::Branding::StringEntry e, const QVariantMap& map, const QString& key ) +{ + if ( !map.contains( key ) ) + { + return QString(); + } + auto v = map.value( key ); + if ( v.type() == QVariant::Bool ) + { + return v.toBool() ? ( *e ) : QString(); + } + if ( v.type() == QVariant::String ) + { + return v.toString(); + } + + return QString(); +} + +void +WelcomeQmlViewStep::setConfigurationMap( const QVariantMap& configurationMap ) +{ + using Calamares::Branding; + + m_config.setHelpUrl( jobOrBrandingSetting( Branding::SupportUrl, configurationMap, "showSupportUrl" ) ); + // TODO: expand Config class and set the remaining fields + + // TODO: figure out how the requirements (held by ModuleManager) should be accessible + // to QML as a odel. + if ( configurationMap.contains( "requirements" ) + && configurationMap.value( "requirements" ).type() == QVariant::Map ) + { + m_requirementsChecker->setConfigurationMap( configurationMap.value( "requirements" ).toMap() ); + } + else + cWarning() << "no valid requirements map found in welcome " + "module configuration."; + + bool ok = false; + QVariantMap geoip = CalamaresUtils::getSubMap( configurationMap, "geoip", ok ); + if ( ok ) + { + using FWString = QFutureWatcher< QString >; + + auto* handler = new CalamaresUtils::GeoIP::Handler( CalamaresUtils::getString( geoip, "style" ), + CalamaresUtils::getString( geoip, "url" ), + CalamaresUtils::getString( geoip, "selector" ) ); + if ( handler->type() != CalamaresUtils::GeoIP::Handler::Type::None ) + { + auto* future = new FWString(); + connect( future, &FWString::finished, [view = this, f = future, h = handler]() { + QString countryResult = f->future().result(); + cDebug() << "GeoIP result for welcome=" << countryResult; + view->setCountry( countryResult, h ); + f->deleteLater(); + delete h; + } ); + future->setFuture( handler->queryRaw() ); + } + else + { + // Would not produce useful country code anyway. + delete handler; + } + } + + QString language = CalamaresUtils::getString( configurationMap, "languageIcon" ); + if ( !language.isEmpty() ) + { + auto icon = Calamares::Branding::instance()->image( language, QSize( 48, 48 ) ); + if ( !icon.isNull() ) + { + // TODO: figure out where to set this: Config? + } + } +} + +Calamares::RequirementsList +WelcomeQmlViewStep::checkRequirements() +{ + return m_requirementsChecker->checkRequirements(); +} + +static inline void +logGeoIPHandler( CalamaresUtils::GeoIP::Handler* handler ) +{ + if ( handler ) + { + cDebug() << Logger::SubEntry << "Obtained from" << handler->url() << " (" + << static_cast< int >( handler->type() ) << handler->selector() << ')'; + } +} + +void +WelcomeQmlViewStep::setCountry( const QString& countryCode, CalamaresUtils::GeoIP::Handler* handler ) +{ + if ( countryCode.length() != 2 ) + { + cDebug() << "Unusable country code" << countryCode; + logGeoIPHandler( handler ); + return; + } + + auto c_l = CalamaresUtils::Locale::countryData( countryCode ); + if ( c_l.first == QLocale::Country::AnyCountry ) + { + cDebug() << "Unusable country code" << countryCode; + logGeoIPHandler( handler ); + return; + } + else + { + int r = CalamaresUtils::Locale::availableTranslations()->find( countryCode ); + if ( r < 0 ) + { + cDebug() << "Unusable country code" << countryCode << "(no suitable translation)"; + } + if ( ( r >= 0 ) ) + { + // TODO: update Config to point to selected language + } + } +} diff --git a/src/modules/welcomeq/WelcomeQmlViewStep.h b/src/modules/welcomeq/WelcomeQmlViewStep.h new file mode 100644 index 000000000..5486d8d31 --- /dev/null +++ b/src/modules/welcomeq/WelcomeQmlViewStep.h @@ -0,0 +1,95 @@ +/* === 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 WELCOME_QMLVIEWSTEP_H +#define WELCOME_QMLVIEWSTEP_H + +#include "Config.h" + +#include "modulesystem/Requirement.h" +#include "utils/PluginFactory.h" +#include "viewpages/ViewStep.h" + +#include + +#include +#include + +namespace CalamaresUtils +{ +namespace GeoIP +{ +class Handler; +} +} // namespace CalamaresUtils + +class GeneralRequirements; + +class QQmlComponent; +class QQuickItem; +class QQuickWidget; + +// TODO: Needs a generic Calamares::QmlViewStep as base class +// TODO: refactor and move what makes sense to base class +class PLUGINDLLEXPORT WelcomeQmlViewStep : public Calamares::ViewStep +{ + Q_OBJECT + +public: + explicit WelcomeQmlViewStep( QObject* parent = nullptr ); + virtual ~WelcomeQmlViewStep() override; + + QString prettyName() const override; + + QWidget* widget() override; + + bool isNextEnabled() const override; + bool isBackEnabled() const override; + + bool isAtBeginning() const override; + bool isAtEnd() const override; + + Calamares::JobList jobs() const override; + + void setConfigurationMap( const QVariantMap& configurationMap ) override; + + /** @brief Sets the country that Calamares is running in. + * + * This (ideally) sets up language and locale settings that are right for + * the given 2-letter country code. Uses the handler's information (if + * given) for error reporting. + */ + void setCountry( const QString&, CalamaresUtils::GeoIP::Handler* handler ); + + Calamares::RequirementsList checkRequirements() override; + +private: + // TODO: a generic QML viewstep should return a config object from a method + Config m_config; + GeneralRequirements* m_requirementsChecker; + + // TODO: these need to be in the base class (also a base class of ExecutionViewStep) + QQuickWidget* m_qmlWidget; + QQmlComponent* m_qmlComponent; + QQuickItem* m_qmlItem; + +}; + +CALAMARES_PLUGIN_FACTORY_DECLARATION( WelcomeQmlViewStepFactory ) + +#endif // WELCOME_QMLVIEWSTEP_H