Merge branch 'issue-1152'

main
Adriaan de Groot 5 years ago
commit 79bd7b6b89

@ -7,6 +7,10 @@ website will have to do for older versions.
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 ##
- 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
solved. The *finished* page can be left out (but then you don't get
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 ##

@ -93,6 +93,17 @@ images:
# The slideshow is displayed during execution steps (e.g. when the
# installer is actually writing to disk and doing other slow things).
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.
#

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

@ -127,6 +127,7 @@ Branding::Branding( const QString& brandingFilePath,
, m_descriptorPath( brandingFilePath )
, m_welcomeStyleCalamares( false )
, m_welcomeExpandingLogo( true )
, m_slideshowAPI( 1 )
{
cDebug() << "Using Calamares branding file at" << brandingFilePath;
@ -234,6 +235,14 @@ Branding::Branding( const QString& brandingFilePath,
}
else
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 )
{

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

@ -36,10 +36,40 @@
#include <QDir>
#include <QLabel>
#include <QProgressBar>
#include <QVBoxLayout>
#include <QtQuickWidgets/QQuickWidget>
#include <QQmlComponent>
#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
{
@ -47,31 +77,38 @@ namespace Calamares
ExecutionViewStep::ExecutionViewStep( QObject* parent )
: ViewStep( parent )
, 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* innerLayout = new QVBoxLayout;
m_slideShow = new QQuickWidget;
layout->addWidget( m_slideShow );
CalamaresUtils::unmarginLayout( layout );
m_progressBar->setMaximum( 10000 );
layout->addLayout( innerLayout );
m_slideShow->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
m_slideShow->setResizeMode( QQuickWidget::SizeRootObjectToView );
m_qmlShow->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
m_qmlShow->setResizeMode( QQuickWidget::SizeRootObjectToView );
m_qmlShow->engine()->addImportPath( CalamaresUtils::qmlModulesDir().absolutePath() );
m_slideShow->engine()->addImportPath( CalamaresUtils::qmlModulesDir().absolutePath() );
layout->addWidget( m_qmlShow );
CalamaresUtils::unmarginLayout( layout );
layout->addLayout( innerLayout );
innerLayout->addSpacing( CalamaresUtils::defaultFontHeight() / 2 );
innerLayout->addWidget( m_progressBar );
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,
this, &ExecutionViewStep::updateFromJobQueue );
connect( JobQueue::instance(), &JobQueue::progress, this, &ExecutionViewStep::updateFromJobQueue );
CALAMARES_RETRANSLATE( m_qmlShow->engine()->retranslate(); )
}
@ -130,22 +167,67 @@ ExecutionViewStep::isAtEnd() const
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
ExecutionViewStep::onActivate()
{
CALAMARES_RETRANSLATE_WIDGET( m_widget,
if ( !Calamares::Branding::instance()->slideshowPath().isEmpty() )
m_slideShow->setSource( QUrl::fromLocalFile( Calamares::Branding::instance()
->slideshowPath() ) );
)
if ( Branding::instance()->slideshowAPI() == 2 )
{
// The QML was already loaded in the constructor, need to start it
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();
foreach ( const QString& instanceKey, m_jobInstanceKeys )
{
Calamares::Module* module = Calamares::ModuleManager::instance()
->moduleInstance( instanceKey );
Calamares::Module* module = Calamares::ModuleManager::instance()->moduleInstance( instanceKey );
if ( module )
{
auto jl = module->jobs();
@ -183,4 +265,16 @@ ExecutionViewStep::updateFromJobQueue( qreal percent, const QString& 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

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

Loading…
Cancel
Save