[libcalamares] Handle async QML loading

- The component isn't ready immediately, so instatiate
   once it is fully loaded and ready
 - Edge case if the execution view step is already visible, then
   start the show (because a previous call to onActivate() will
   have missed it).
This commit is contained in:
Adriaan de Groot 2019-06-17 11:47:25 +02:00
parent 193bcbde71
commit f9bd0fba10
2 changed files with 53 additions and 34 deletions

View File

@ -42,6 +42,35 @@
#include <QQuickWidget> #include <QQuickWidget>
#include <QVBoxLayout> #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
{ {
@ -74,8 +103,8 @@ ExecutionViewStep::ExecutionViewStep( QObject* parent )
cDebug() << "QML import paths:" << Logger::DebugList( m_qmlShow->engine()->importPathList() ); cDebug() << "QML import paths:" << Logger::DebugList( m_qmlShow->engine()->importPathList() );
if ( Branding::instance()->slideshowAPI() == 2 ) if ( Branding::instance()->slideshowAPI() == 2 )
{ {
cDebug() << "QML load on startup."; cDebug() << "QML load on startup, API 2.";
loadQml(); loadQmlV2();
} }
connect( JobQueue::instance(), &JobQueue::progress, this, &ExecutionViewStep::updateFromJobQueue ); connect( JobQueue::instance(), &JobQueue::progress, this, &ExecutionViewStep::updateFromJobQueue );
@ -138,7 +167,7 @@ ExecutionViewStep::isAtEnd() const
} }
void void
ExecutionViewStep::loadQml() ExecutionViewStep::loadQmlV2()
{ {
if ( !m_qmlComponent && !Calamares::Branding::instance()->slideshowPath().isEmpty() ) if ( !m_qmlComponent && !Calamares::Branding::instance()->slideshowPath().isEmpty() )
{ {
@ -146,9 +175,19 @@ ExecutionViewStep::loadQml()
QUrl::fromLocalFile( Calamares::Branding::instance()->slideshowPath() ), QUrl::fromLocalFile( Calamares::Branding::instance()->slideshowPath() ),
QQmlComponent::CompilationMode::Asynchronous QQmlComponent::CompilationMode::Asynchronous
); );
connect( m_qmlComponent, &QQmlComponent::statusChanged, this, &ExecutionViewStep::loadQmlV2Complete );
} }
if ( m_qmlComponent && !m_qmlObject ) }
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(); QObject* o = m_qmlComponent->create();
m_qmlObject = qobject_cast< QQuickItem* >( o ); m_qmlObject = qobject_cast< QQuickItem* >( o );
if ( !m_qmlObject ) if ( !m_qmlObject )
@ -160,39 +199,16 @@ ExecutionViewStep::loadQml()
// what is needed: sets up visual parent by replacing the root // what is needed: sets up visual parent by replacing the root
// item, and handling resizes. // item, and handling resizes.
m_qmlShow->setContent( QUrl::fromLocalFile( Calamares::Branding::instance()->slideshowPath() ), m_qmlComponent, m_qmlObject ); 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" );
}
} }
} }
} }
/** @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).
*/
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.";
}
}
void void
ExecutionViewStep::onActivate() ExecutionViewStep::onActivate()
{ {

View File

@ -60,6 +60,9 @@ public:
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;
@ -70,7 +73,7 @@ private:
QStringList m_jobInstanceKeys; QStringList m_jobInstanceKeys;
void loadQml(); //< Loads the slideshow QML (from branding) 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 );
}; };