Merge branch 'issue-1152'

This commit is contained in:
Adriaan de Groot 2019-06-17 11:54:01 +02:00
commit 79bd7b6b89
7 changed files with 166 additions and 27 deletions

12
CHANGES
View File

@ -7,6 +7,10 @@ website will have to do for older versions.
This release contains contributions from (alphabetically by first name): This release contains contributions from (alphabetically by first name):
Distributions are **advised** to check the slideshow they use for the
installation step; changes in loading and translation mechanisms may
require changes in the slideshow.
## Core ## ## Core ##
- With this release, option *WITH_PYTHONQT* changes default to **off**. - With this release, option *WITH_PYTHONQT* changes default to **off**.
@ -18,6 +22,14 @@ This release contains contributions from (alphabetically by first name):
configured after the last *exec* section of the sequence has been configured after the last *exec* section of the sequence has been
solved. The *finished* page can be left out (but then you don't get solved. The *finished* page can be left out (but then you don't get
the restart-now functionality). #1168 the restart-now functionality). #1168
- The *slideshow* which is run during installation is now loaded on
startup (while requirements checking is being done). This should
improve responsiveness when the slideshow starts. If the slideshow
has methods `onActivate()` and `onLeave()` those will be called
when the installation step is activated (e.g. the installation
starts and the slideshow becomes visible) or is finished (and the
slideshow becomes hidden).
- The example slideshow now starts its timers when it becomes visible.
## Modules ## ## Modules ##

View File

@ -93,6 +93,17 @@ images:
# The slideshow is displayed during execution steps (e.g. when the # The slideshow is displayed during execution steps (e.g. when the
# installer is actually writing to disk and doing other slow things). # installer is actually writing to disk and doing other slow things).
slideshow: "show.qml" slideshow: "show.qml"
# There are two available APIs for the slideshow:
# - 1 (the default) loads the entire slideshow when the installation-
# slideshow page is shown and starts the QML then. The QML
# is never stopped (after installation is done, times etc.
# continue to fire).
# - 2 loads the slideshow on startup and calls onActivate() and
# onLeave() in the root object. After the installation is done,
# the show is stopped (first by calling onLeave(), then destroying
# the QML components).
slideshowAPI: 2
# Colors for text and background components. # Colors for text and background components.
# #

View File

@ -68,8 +68,9 @@ Presentation
centeredText: qsTr("This is a third Slide element.") centeredText: qsTr("This is a third Slide element.")
} }
Component.onCompleted: { function onActivate() {
advanceTimer.running = true; presentation.currentSlide = 0;
console.log("Component complete"); advanceTimer.running = true
console.log("Component activated");
} }
} }

View File

@ -127,6 +127,7 @@ Branding::Branding( const QString& brandingFilePath,
, m_descriptorPath( brandingFilePath ) , m_descriptorPath( brandingFilePath )
, m_welcomeStyleCalamares( false ) , m_welcomeStyleCalamares( false )
, m_welcomeExpandingLogo( true ) , m_welcomeExpandingLogo( true )
, m_slideshowAPI( 1 )
{ {
cDebug() << "Using Calamares branding file at" << brandingFilePath; cDebug() << "Using Calamares branding file at" << brandingFilePath;
@ -234,6 +235,14 @@ Branding::Branding( const QString& brandingFilePath,
} }
else else
bail( "Syntax error in slideshow sequence." ); bail( "Syntax error in slideshow sequence." );
int api = doc[ "slideshowAPI" ].IsScalar() ? doc[ "slideshowAPI" ].as<int>() : -1;
if ( ( api < 1 ) || ( api > 2 ) )
{
cWarning() << "Invalid or missing *slideshowAPI* in branding file.";
api = 1;
}
m_slideshowAPI = api;
} }
catch ( YAML::Exception& e ) catch ( YAML::Exception& e )
{ {

View File

@ -118,6 +118,7 @@ public:
/** @brief Path to the slideshow QML file, if any. */ /** @brief Path to the slideshow QML file, if any. */
QString slideshowPath() const { return m_slideshowPath; } QString slideshowPath() const { return m_slideshowPath; }
int slideshowAPI() const { return m_slideshowAPI; }
QString string( Branding::StringEntry stringEntry ) const; QString string( Branding::StringEntry stringEntry ) const;
QString styleString( Branding::StyleEntry styleEntry ) const; QString styleString( Branding::StyleEntry styleEntry ) const;
@ -172,6 +173,7 @@ private:
QMap< QString, QString > m_images; QMap< QString, QString > m_images;
QMap< QString, QString > m_style; QMap< QString, QString > m_style;
QString m_slideshowPath; QString m_slideshowPath;
int m_slideshowAPI;
QString m_translationsPathPrefix; QString m_translationsPathPrefix;
/** @brief Initialize the simple settings below */ /** @brief Initialize the simple settings below */

View File

@ -36,10 +36,40 @@
#include <QDir> #include <QDir>
#include <QLabel> #include <QLabel>
#include <QProgressBar> #include <QProgressBar>
#include <QVBoxLayout> #include <QQmlComponent>
#include <QtQuickWidgets/QQuickWidget>
#include <QQmlEngine> #include <QQmlEngine>
#include <QQuickItem>
#include <QQuickWidget>
#include <QVBoxLayout>
/** @brief Calls the QML method @p method()
*
* Pass in only the name of the method (e.g. onActivate). This function
* checks if the method exists (with no arguments) before trying to
* call it, so that no warnings are printed due to missing methods.
*
* If there is a return value from the QML method, it is logged (but not otherwise used).
*/
static void
callQMLFunction( QQuickItem* qmlObject, const char* method )
{
QByteArray methodSignature( method );
methodSignature.append( "()" );
if ( qmlObject && qmlObject->metaObject()->indexOfMethod( methodSignature ) >= 0 )
{
QVariant returnValue;
QMetaObject::invokeMethod( qmlObject, method, Q_RETURN_ARG( QVariant, returnValue ) );
if ( !returnValue.isNull() )
{
cDebug() << "QML" << methodSignature << "returned" << returnValue;
}
}
else if ( qmlObject )
{
cDebug() << "QML" << methodSignature << "is missing.";
}
}
namespace Calamares namespace Calamares
{ {
@ -47,31 +77,38 @@ namespace Calamares
ExecutionViewStep::ExecutionViewStep( QObject* parent ) ExecutionViewStep::ExecutionViewStep( QObject* parent )
: ViewStep( parent ) : ViewStep( parent )
, m_widget( new QWidget ) , m_widget( new QWidget )
, m_progressBar( new QProgressBar )
, m_label( new QLabel )
, m_qmlShow( new QQuickWidget )
, m_qmlComponent( nullptr )
, m_qmlObject( nullptr )
{ {
m_progressBar = new QProgressBar;
m_progressBar->setMaximum( 10000 );
m_label = new QLabel;
QVBoxLayout* layout = new QVBoxLayout( m_widget ); QVBoxLayout* layout = new QVBoxLayout( m_widget );
QVBoxLayout* innerLayout = new QVBoxLayout; QVBoxLayout* innerLayout = new QVBoxLayout;
m_slideShow = new QQuickWidget; m_progressBar->setMaximum( 10000 );
layout->addWidget( m_slideShow );
m_qmlShow->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
m_qmlShow->setResizeMode( QQuickWidget::SizeRootObjectToView );
m_qmlShow->engine()->addImportPath( CalamaresUtils::qmlModulesDir().absolutePath() );
layout->addWidget( m_qmlShow );
CalamaresUtils::unmarginLayout( layout ); CalamaresUtils::unmarginLayout( layout );
layout->addLayout( innerLayout ); layout->addLayout( innerLayout );
m_slideShow->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
m_slideShow->setResizeMode( QQuickWidget::SizeRootObjectToView );
m_slideShow->engine()->addImportPath( CalamaresUtils::qmlModulesDir().absolutePath() );
innerLayout->addSpacing( CalamaresUtils::defaultFontHeight() / 2 ); innerLayout->addSpacing( CalamaresUtils::defaultFontHeight() / 2 );
innerLayout->addWidget( m_progressBar ); innerLayout->addWidget( m_progressBar );
innerLayout->addWidget( m_label ); innerLayout->addWidget( m_label );
cDebug() << "QML import paths:" << Logger::DebugList( m_slideShow->engine()->importPathList() ); cDebug() << "QML import paths:" << Logger::DebugList( m_qmlShow->engine()->importPathList() );
if ( Branding::instance()->slideshowAPI() == 2 )
{
cDebug() << "QML load on startup, API 2.";
loadQmlV2();
}
connect( JobQueue::instance(), &JobQueue::progress, connect( JobQueue::instance(), &JobQueue::progress, this, &ExecutionViewStep::updateFromJobQueue );
this, &ExecutionViewStep::updateFromJobQueue ); CALAMARES_RETRANSLATE( m_qmlShow->engine()->retranslate(); )
} }
@ -130,22 +167,67 @@ ExecutionViewStep::isAtEnd() const
return true; return true;
} }
void
ExecutionViewStep::loadQmlV2()
{
if ( !m_qmlComponent && !Calamares::Branding::instance()->slideshowPath().isEmpty() )
{
m_qmlComponent = new QQmlComponent( m_qmlShow->engine(),
QUrl::fromLocalFile( Calamares::Branding::instance()->slideshowPath() ),
QQmlComponent::CompilationMode::Asynchronous
);
connect( m_qmlComponent, &QQmlComponent::statusChanged, this, &ExecutionViewStep::loadQmlV2Complete );
}
}
void
ExecutionViewStep::loadQmlV2Complete()
{
if ( m_qmlComponent && m_qmlComponent->isReady() && !m_qmlObject )
{
cDebug() << "QML loading complete, API 2";
// Don't do this again
disconnect( m_qmlComponent, &QQmlComponent::statusChanged, this, &ExecutionViewStep::loadQmlV2Complete );
QObject* o = m_qmlComponent->create();
m_qmlObject = qobject_cast< QQuickItem* >( o );
if ( !m_qmlObject )
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_qmlShow->setContent( QUrl::fromLocalFile( Calamares::Branding::instance()->slideshowPath() ), m_qmlComponent, m_qmlObject );
if ( ViewManager::instance()->currentStep() == this )
{
// We're alreay visible! Must have been slow QML loading, and we
// passed onActivate already.
callQMLFunction( m_qmlObject, "onActivate" );
}
}
}
}
void void
ExecutionViewStep::onActivate() ExecutionViewStep::onActivate()
{ {
CALAMARES_RETRANSLATE_WIDGET( m_widget, if ( Branding::instance()->slideshowAPI() == 2 )
if ( !Calamares::Branding::instance()->slideshowPath().isEmpty() ) {
m_slideShow->setSource( QUrl::fromLocalFile( Calamares::Branding::instance() // The QML was already loaded in the constructor, need to start it
->slideshowPath() ) ); callQMLFunction( m_qmlObject, "onActivate" );
) }
else if ( !Calamares::Branding::instance()->slideshowPath().isEmpty() )
{
// API version 1 assumes onCompleted is the trigger
m_qmlShow->setSource( QUrl::fromLocalFile( Calamares::Branding::instance()->slideshowPath() ) );
}
JobQueue* queue = JobQueue::instance(); JobQueue* queue = JobQueue::instance();
foreach ( const QString& instanceKey, m_jobInstanceKeys ) foreach ( const QString& instanceKey, m_jobInstanceKeys )
{ {
Calamares::Module* module = Calamares::ModuleManager::instance() Calamares::Module* module = Calamares::ModuleManager::instance()->moduleInstance( instanceKey );
->moduleInstance( instanceKey );
if ( module ) if ( module )
{ {
auto jl = module->jobs(); auto jl = module->jobs();
@ -183,4 +265,16 @@ ExecutionViewStep::updateFromJobQueue( qreal percent, const QString& message )
m_label->setText( message ); m_label->setText( message );
} }
void
ExecutionViewStep::onLeave()
{
// API version 2 is explicitly stopped; version 1 keeps running
if ( Branding::instance()->slideshowAPI() == 2 )
{
callQMLFunction( m_qmlObject, "onLeave" );
delete m_qmlObject;
m_qmlObject = nullptr;
}
}
} // namespace } // namespace

View File

@ -25,7 +25,10 @@
#include <QStringList> #include <QStringList>
class QLabel; class QLabel;
class QObject;
class QProgressBar; class QProgressBar;
class QQmlComponent;
class QQuickItem;
class QQuickWidget; class QQuickWidget;
namespace Calamares namespace Calamares
@ -51,19 +54,26 @@ public:
bool isAtEnd() const override; bool isAtEnd() const override;
void onActivate() override; void onActivate() override;
void onLeave() override;
JobList jobs() const override; JobList jobs() const override;
void appendJobModuleInstanceKey( const QString& instanceKey ); void appendJobModuleInstanceKey( const QString& instanceKey );
public slots:
void loadQmlV2Complete();
private: private:
QWidget* m_widget; QWidget* m_widget;
QProgressBar* m_progressBar; QProgressBar* m_progressBar;
QLabel* m_label; QLabel* m_label;
QQuickWidget* m_slideShow; QQuickWidget* m_qmlShow;
QQmlComponent* m_qmlComponent;
QQuickItem* m_qmlObject; //< The actual show
QStringList m_jobInstanceKeys; QStringList m_jobInstanceKeys;
void loadQmlV2(); //< Loads the slideshow QML (from branding) for API version 2
void updateFromJobQueue( qreal percent, const QString& message ); void updateFromJobQueue( qreal percent, const QString& message );
}; };