diff --git a/CHANGES b/CHANGES index 66323ff9f..02a7fc4f5 100644 --- a/CHANGES +++ b/CHANGES @@ -20,6 +20,9 @@ This release contains contributions from (alphabetically by first name): (maintained!) upstream version instead. It also gives us KMacroExpander everywhere, which will simplify code for handling substitutions in configuration files. +- *Slideshows* now have a new property *activatedInCalamares* which + controls the keyboard shortcuts (and can control timers and other + properties of the slideshow, too). ## Modules ## diff --git a/src/branding/README.md b/src/branding/README.md index 1b9eb57fd..099163836 100644 --- a/src/branding/README.md +++ b/src/branding/README.md @@ -66,6 +66,16 @@ The setting *slideshowAPI* in `branding.desc` indicates which one to use for a given branding slideshow. Which API to use is really a function of the QML. Expect the version 1 API to be deprecated in the course of Calamares 3.3. +In Calamares 3.2.13 support for activation notification to the QML +parts is improved: + - If the root object has a property *activatedInCalamares* (the examples do), + then that property is set to *true* when the slideshow becomes visible + (activated) and is set to *false* when the slideshow is hidden (e.g. + when the installation phase is done). + - The *actvatedInCalamares* property can be used to set up timers also in V1. + - The keyboard shortcuts in the example slideshow are enabled only while + the slideshow is visible. + ## Translations diff --git a/src/branding/default/show.qml b/src/branding/default/show.qml index c989676dd..dcb0f9257 100644 --- a/src/branding/default/show.qml +++ b/src/branding/default/show.qml @@ -25,14 +25,14 @@ Presentation id: presentation function nextSlide() { - console.log("Next slide"); + console.log("QML Component (default slideshow) Next slide"); presentation.goToNextSlide(); } Timer { id: advanceTimer interval: 1000 - running: false + running: presentation.activatedInCalamares repeat: true onTriggered: nextSlide() } @@ -68,9 +68,19 @@ Presentation centeredText: qsTr("This is a third Slide element.") } + // When this slideshow is loaded as a V1 slideshow, only + // activatedInCalamares is set, which starts the timer (see above). + // + // In V2, also the onActivate() and onLeave() methods are called. + // These example functions log a message (and re-start the slides + // from the first). function onActivate() { - presentation.currentSlide = 0; - advanceTimer.running = true console.log("QML Component (default slideshow) activated"); + presentation.currentSlide = 0; } + + function onLeave() { + console.log("QML Component (default slideshow) deactivated"); + } + } diff --git a/src/libcalamaresui/ExecutionViewStep.cpp b/src/libcalamaresui/ExecutionViewStep.cpp index 4dd10bd93..501995c07 100644 --- a/src/libcalamaresui/ExecutionViewStep.cpp +++ b/src/libcalamaresui/ExecutionViewStep.cpp @@ -179,12 +179,63 @@ ExecutionViewStep::loadQmlV2() } } +/// @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 + 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 loading complete, API 2"; + cDebug() << "QML component complete, API 2"; // Don't do this again disconnect( m_qmlComponent, &QQmlComponent::statusChanged, this, &ExecutionViewStep::loadQmlV2Complete ); @@ -196,6 +247,8 @@ ExecutionViewStep::loadQmlV2Complete() } 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 @@ -206,7 +259,7 @@ ExecutionViewStep::loadQmlV2Complete() { // We're alreay visible! Must have been slow QML loading, and we // passed onActivate already. - callQMLFunction( m_qmlObject, "onActivate" ); + changeSlideShowState( Slideshow::Start, m_qmlObject, m_qmlShow ); } } } @@ -215,16 +268,7 @@ ExecutionViewStep::loadQmlV2Complete() void ExecutionViewStep::onActivate() { - 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() ) ); - } + changeSlideShowState( Slideshow::Start, m_qmlObject, m_qmlShow ); JobQueue* queue = JobQueue::instance(); foreach ( const QString& instanceKey, m_jobInstanceKeys ) @@ -272,10 +316,10 @@ 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 ) { - callQMLFunction( m_qmlObject, "onLeave" ); delete m_qmlObject; m_qmlObject = nullptr; } diff --git a/src/qml/calamares/slideshow/Presentation.qml b/src/qml/calamares/slideshow/Presentation.qml index 4843e15a6..1d2fd9c85 100644 --- a/src/qml/calamares/slideshow/Presentation.qml +++ b/src/qml/calamares/slideshow/Presentation.qml @@ -8,6 +8,9 @@ * (this and the 'c' key make sense in a *presentation* * slideshow, not in a passive slideshow like Calamares) * - remove quit key + * Copyright 2019, Adriaan de Groot + * - Support "V2" loading + * - Disable shortcuts until the content is visible in Calamares * * SPDX-License-Identifier: LGPL-2.1 * License-Filename: LICENSES/LGPLv2.1-Presentation @@ -77,6 +80,14 @@ Item { property string fontFamily: "Helvetica" property string codeFontFamily: "Courier New" + // This is set by the C++ part of Calamares when the slideshow + // becomes visible. You can connect it to a timer, or whatever + // else needs to start only when the slideshow becomes visible. + // + // It is used in this example also to keep the keyboard shortcuts + // enabled only while the slideshow is active. + property bool activatedInCalamares: false + // Private API property int _lastShownSlide: 0 @@ -136,17 +147,17 @@ Item { Keys.onLeftPressed: goToPreviousSlide() // navigate with arrow keys - Shortcut { sequence: StandardKey.MoveToNextLine; enabled: root.arrowNavigation; onActivated: goToNextSlide() } - Shortcut { sequence: StandardKey.MoveToPreviousLine; enabled: root.arrowNavigation; onActivated: goToPreviousSlide() } - Shortcut { sequence: StandardKey.MoveToNextChar; enabled: root.arrowNavigation; onActivated: goToNextSlide() } - Shortcut { sequence: StandardKey.MoveToPreviousChar; enabled: root.arrowNavigation; onActivated: goToPreviousSlide() } + Shortcut { sequence: StandardKey.MoveToNextLine; enabled: root.activatedInCalamares && root .arrowNavigation; onActivated: goToNextSlide() } + Shortcut { sequence: StandardKey.MoveToPreviousLine; enabled: root.activatedInCalamares && root.arrowNavigation; onActivated: goToPreviousSlide() } + Shortcut { sequence: StandardKey.MoveToNextChar; enabled: root.activatedInCalamares && root.arrowNavigation; onActivated: goToNextSlide() } + Shortcut { sequence: StandardKey.MoveToPreviousChar; enabled: root.activatedInCalamares && root.arrowNavigation; onActivated: goToPreviousSlide() } // presentation-specific single-key shortcuts (which interfere with normal typing) - Shortcut { sequence: " "; enabled: root.keyShortcutsEnabled; onActivated: goToNextSlide() } + Shortcut { sequence: " "; enabled: root.activatedInCalamares && root.keyShortcutsEnabled; onActivated: goToNextSlide() } // standard shortcuts - Shortcut { sequence: StandardKey.MoveToNextPage; onActivated: goToNextSlide() } - Shortcut { sequence: StandardKey.MoveToPreviousPage; onActivated: goToPreviousSlide() } + Shortcut { sequence: StandardKey.MoveToNextPage; enabled: root.activatedInCalamares; onActivated: goToNextSlide() } + Shortcut { sequence: StandardKey.MoveToPreviousPage; enabled: root.activatedInCalamares; onActivated: goToPreviousSlide() } MouseArea { id: mouseArea