diff --git a/src/libcalamaresui/CMakeLists.txt b/src/libcalamaresui/CMakeLists.txt index e813b0009..ee7992710 100644 --- a/src/libcalamaresui/CMakeLists.txt +++ b/src/libcalamaresui/CMakeLists.txt @@ -19,6 +19,7 @@ set( calamaresui_SOURCES viewpages/BlankViewStep.cpp viewpages/ExecutionViewStep.cpp viewpages/QmlViewStep.cpp + viewpages/Slideshow.cpp viewpages/ViewStep.cpp widgets/ClickableLabel.cpp diff --git a/src/libcalamaresui/viewpages/ExecutionViewStep.cpp b/src/libcalamaresui/viewpages/ExecutionViewStep.cpp index e794d6dd4..db73d16b1 100644 --- a/src/libcalamaresui/viewpages/ExecutionViewStep.cpp +++ b/src/libcalamaresui/viewpages/ExecutionViewStep.cpp @@ -49,7 +49,7 @@ ExecutionViewStep::ExecutionViewStep( QObject* parent ) , m_widget( new QWidget ) , m_progressBar( new QProgressBar ) , m_label( new QLabel ) - , m_slideshow( nullptr ) + , m_slideshow( new SlideshowQML( m_widget ) ) { QVBoxLayout* layout = new QVBoxLayout( m_widget ); QVBoxLayout* innerLayout = new QVBoxLayout; @@ -64,12 +64,6 @@ ExecutionViewStep::ExecutionViewStep( QObject* parent ) innerLayout->addWidget( m_progressBar ); innerLayout->addWidget( m_label ); - if ( Branding::instance()->slideshowAPI() == 2 ) - { - cDebug() << "QML load on startup, API 2."; - loadQmlV2(); - } - connect( JobQueue::instance(), &JobQueue::progress, this, &ExecutionViewStep::updateFromJobQueue ); } @@ -127,108 +121,10 @@ ExecutionViewStep::isAtEnd() const return !JobQueue::instance()->isRunning(); } -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 ); - } -} - -/// @brief State-change of the slideshow, for changeSlideShowState() -enum class Slideshow -{ - Start, - Stop -}; - -/** @brief Tells the slideshow we activated or left the show. - * - * If @p state is @c Slideshow::Start, calls suitable activation procedures. - * If @p state is @c Slideshow::Stop, calls deactivation procedures. - * - * Applies V1 and V2 QML activation / deactivation: - * - V1 loads the QML in @p widget on activation. Sets root object property - * *activatedInCalamares* as appropriate. - * - V2 calls onActivate() or onLeave() in the QML as appropriate. Also - * sets the *activatedInCalamares* property. - */ -static void -changeSlideShowState( Slideshow state, QQuickItem* slideshow, QQuickWidget* widget ) -{ - bool activate = state == Slideshow::Start; - - if ( Branding::instance()->slideshowAPI() == 2 ) - { - // The QML was already loaded in the constructor, need to start it - CalamaresUtils::callQMLFunction( slideshow, activate ? "onActivate" : "onLeave" ); - } - else if ( !Calamares::Branding::instance()->slideshowPath().isEmpty() ) - { - // API version 1 assumes onCompleted is the trigger - if ( activate ) - { - widget->setSource( QUrl::fromLocalFile( Calamares::Branding::instance()->slideshowPath() ) ); - } - // needs the root object for property setting, below - slideshow = widget->rootObject(); - } - - // V1 API has picked up the root object for use, V2 passed it in. - if ( slideshow ) - { - static const char propertyName[] = "activatedInCalamares"; - auto property = slideshow->property( propertyName ); - if ( property.isValid() && ( property.type() == QVariant::Bool ) && ( property.toBool() != activate ) ) - { - slideshow->setProperty( propertyName, activate ); - } - } -} - -void -ExecutionViewStep::loadQmlV2Complete() -{ - if ( m_qmlComponent && m_qmlComponent->isReady() && !m_qmlObject ) - { - cDebug() << "QML component 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 - { - cDebug() << Logger::SubEntry << "Loading" << Calamares::Branding::instance()->slideshowPath(); - - // 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. - changeSlideShowState( Slideshow::Start, m_qmlObject, m_qmlShow ); - } - } - } -} - void ExecutionViewStep::onActivate() { - changeSlideShowState( Slideshow::Start, m_qmlObject, m_qmlShow ); + m_slideshow->changeSlideShowState( Slideshow::Start ); JobQueue* queue = JobQueue::instance(); foreach ( const QString& instanceKey, m_jobInstanceKeys ) @@ -276,13 +172,7 @@ ExecutionViewStep::updateFromJobQueue( qreal percent, const QString& message ) void ExecutionViewStep::onLeave() { - changeSlideShowState( Slideshow::Stop, m_qmlObject, m_qmlShow ); - // API version 2 is explicitly stopped; version 1 keeps running - if ( Branding::instance()->slideshowAPI() == 2 ) - { - delete m_qmlObject; - m_qmlObject = nullptr; - } + m_slideshow->changeSlideShowState( Slideshow::Stop ); } } // namespace Calamares diff --git a/src/libcalamaresui/viewpages/ExecutionViewStep.h b/src/libcalamaresui/viewpages/ExecutionViewStep.h index 340f96745..48604fe93 100644 --- a/src/libcalamaresui/viewpages/ExecutionViewStep.h +++ b/src/libcalamaresui/viewpages/ExecutionViewStep.h @@ -59,9 +59,6 @@ public: void appendJobModuleInstanceKey( const QString& instanceKey ); -public slots: - void loadQmlV2Complete(); - private: QWidget* m_widget; QProgressBar* m_progressBar; @@ -70,7 +67,6 @@ private: QStringList m_jobInstanceKeys; - void loadQmlV2(); ///< Loads the slideshow QML (from branding) for API version 2 void updateFromJobQueue( qreal percent, const QString& message ); }; diff --git a/src/libcalamaresui/viewpages/Slideshow.cpp b/src/libcalamaresui/viewpages/Slideshow.cpp index f0069cd9d..5b264a5dd 100644 --- a/src/libcalamaresui/viewpages/Slideshow.cpp +++ b/src/libcalamaresui/viewpages/Slideshow.cpp @@ -20,22 +20,24 @@ #include "Slideshow.h" +#include "Branding.h" #include "utils/Dirs.h" #include "utils/Logger.h" +#include "utils/Qml.h" +#include "utils/Retranslator.h" -#include +#include #include #include #include +#include namespace Calamares { -Slideshow::~Slideshow() -{ -} +Slideshow::~Slideshow() {} -SlideshowQML::SlideshowQML(QWidget* parent) +SlideshowQML::SlideshowQML( QWidget* parent ) : Slideshow( parent ) , m_qmlShow( new QQuickWidget ) , m_qmlComponent( nullptr ) @@ -47,9 +49,121 @@ SlideshowQML::SlideshowQML(QWidget* parent) cDebug() << "QML import paths:" << Logger::DebugList( m_qmlShow->engine()->importPathList() ); #if QT_VERSION >= QT_VERSION_CHECK( 5, 10, 0 ) - CALAMARES_RETRANSLATE( m_qmlShow->engine()->retranslate(); ) + CALAMARES_RETRANSLATE( if ( m_qmlShow ) { m_qmlShow->engine()->retranslate(); } ) #endif + if ( Branding::instance()->slideshowAPI() == 2 ) + { + cDebug() << "QML load on startup, API 2."; + loadQmlV2(); + } } +SlideshowQML::~SlideshowQML() +{ } + +QWidget * SlideshowQML::widget() +{ + return m_qmlShow; +} + +void +SlideshowQML::loadQmlV2() +{ + QMutexLocker l( &m_mutex ); + 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, &SlideshowQML::loadQmlV2Complete ); + } +} + +void +SlideshowQML::loadQmlV2Complete() +{ + QMutexLocker l( &m_mutex ); + if ( m_qmlComponent && m_qmlComponent->isReady() && !m_qmlObject ) + { + cDebug() << "QML component complete, API 2"; + // Don't do this again + disconnect( m_qmlComponent, &QQmlComponent::statusChanged, this, &SlideshowQML::loadQmlV2Complete ); + + QObject* o = m_qmlComponent->create(); + m_qmlObject = qobject_cast< QQuickItem* >( o ); + if ( !m_qmlObject ) + { + delete o; + } + else + { + cDebug() << Logger::SubEntry << "Loading" << Calamares::Branding::instance()->slideshowPath(); + + // 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 ( isActive() ) + { + // We're alreay visible! Must have been slow QML loading, and we + // passed onActivate already. + changeSlideShowState( Slideshow::Start ); + } + } + } +} + +/* + * Applies V1 and V2 QML activation / deactivation: + * - V1 loads the QML in @p widget on activation. Sets root object property + * *activatedInCalamares* as appropriate. + * - V2 calls onActivate() or onLeave() in the QML as appropriate. Also + * sets the *activatedInCalamares* property. + */ +void +SlideshowQML::changeSlideShowState( Action state ) +{ + QMutexLocker l( &m_mutex ); + bool activate = state == Slideshow::Start; + + if ( Branding::instance()->slideshowAPI() == 2 ) + { + // The QML was already loaded in the constructor, need to start it + CalamaresUtils::callQMLFunction( m_qmlObject, activate ? "onActivate" : "onLeave" ); + } + else if ( !Calamares::Branding::instance()->slideshowPath().isEmpty() ) + { + // API version 1 assumes onCompleted is the trigger + if ( activate ) + { + m_qmlShow->setSource( QUrl::fromLocalFile( Calamares::Branding::instance()->slideshowPath() ) ); + } + // needs the root object for property setting, below + m_qmlObject = m_qmlShow->rootObject(); + } + + // V1 API has picked up the root object for use, V2 passed it in. + if ( m_qmlObject ) + { + static const char propertyName[] = "activatedInCalamares"; + auto property = m_qmlObject->property( propertyName ); + if ( property.isValid() && ( property.type() == QVariant::Bool ) && ( property.toBool() != activate ) ) + { + m_qmlObject->setProperty( propertyName, activate ); + } + } + + if ( ( Branding::instance()->slideshowAPI() == 2 ) && ( state == Slideshow::Stop ) ) + { + delete m_qmlObject; + m_qmlObject = nullptr; + } + + m_state = state; +} + +} // namespace diff --git a/src/libcalamaresui/viewpages/Slideshow.h b/src/libcalamaresui/viewpages/Slideshow.h index 5f3612f7e..428422db5 100644 --- a/src/libcalamaresui/viewpages/Slideshow.h +++ b/src/libcalamaresui/viewpages/Slideshow.h @@ -21,6 +21,7 @@ #ifndef LIBCALAMARESUI_SLIDESHOW_H #define LIBCALAMARESUI_SLIDESHOW_H +#include #include class QQmlComponent; @@ -30,22 +31,58 @@ class QQuickWidget; namespace Calamares { -class Slideshow +class Slideshow : public QObject { + Q_OBJECT public: - Slideshow( QWidget* parent ) {}; + /// @brief State-change of the slideshow, for changeSlideShowState() + enum Action + { + Start, + Stop + }; + + Slideshow( QWidget* parent = nullptr ) + : QObject( parent ) + { + } virtual ~Slideshow(); + ///@brief Is the slideshow being shown **right now**? + bool isActive() const { return m_state == Start; } + + /** @brief The actual widget to show the user. + * + * Depending on the style of slideshow, this might be a QQuickWidget, + * or a QLabel, or something else entirely. + */ virtual QWidget* widget() = 0; + + /** @brief Tells the slideshow we activated or left the show. + * + * If @p state is @c Slideshow::Start, calls suitable activation procedures. + * If @p state is @c Slideshow::Stop, calls deactivation procedures. + */ + virtual void changeSlideShowState( Action a ) = 0; + +protected: + QMutex m_mutex; + Action m_state = Stop; }; class SlideshowQML : public Slideshow { + Q_OBJECT public: SlideshowQML( QWidget* parent ); - virtual ~SlideshowQML(); + virtual ~SlideshowQML() override; QWidget* widget() override; + void changeSlideShowState( Action a ) override; + +public slots: + void loadQmlV2Complete(); + void loadQmlV2(); ///< Loads the slideshow QML (from branding) for API version 2 private: QQuickWidget* m_qmlShow; @@ -57,10 +94,10 @@ class SlideshowPictures : public Slideshow { public: SlideshowPictures( QWidget* parent ); - virtual ~SlideshowPictures(); + virtual ~SlideshowPictures() override; QWidget* widget() override; }; -} +} // namespace Calamares #endif