From e0bb53aff4130a2c06cbecd86c2fd7b715c5c4bb Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 27 Aug 2019 13:34:26 +0200 Subject: [PATCH 1/9] [dummycpp] Replace QProcess::execute() - hangs unpredictably during testing - replace with the Calamares process-invocation runCommand(), which is also synchronous but doesn't hang (or, hasn't, in testing so far) --- src/modules/dummycpp/DummyCppJob.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/modules/dummycpp/DummyCppJob.cpp b/src/modules/dummycpp/DummyCppJob.cpp index a38cddc40..5a2ca1803 100644 --- a/src/modules/dummycpp/DummyCppJob.cpp +++ b/src/modules/dummycpp/DummyCppJob.cpp @@ -28,6 +28,7 @@ #include "GlobalStorage.h" #include "JobQueue.h" +#include "utils/CalamaresUtilsSystem.h" #include "utils/Logger.h" DummyCppJob::DummyCppJob( QObject* parent ) @@ -113,9 +114,10 @@ Calamares::JobResult DummyCppJob::exec() { // Ported from dummypython - QProcess::execute( "/bin/sh", - QStringList() << "-c" - << "touch ~/calamares-dummycpp" ); + CalamaresUtils::System::runCommand( CalamaresUtils::System::RunLocation::RunInHost, + QStringList() << "/bin/sh" + << "-c" + << "touch ~/calamares-dummycpp" ); QString accumulator = QDateTime::currentDateTimeUtc().toString( Qt::ISODate ) + '\n'; accumulator += QStringLiteral( "Calamares version: " ) + CALAMARES_VERSION_SHORT + '\n'; accumulator += QStringLiteral( "This job's name: " ) + prettyName() + '\n'; From ac8952f2236b74469f72062be54d94e9c0bf6978 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 27 Aug 2019 11:02:27 +0200 Subject: [PATCH 2/9] [libcalamaresui] Improve QML API v2 debugging - log what is being loaded - distinguish "component complete" from actuial loading --- src/libcalamaresui/ExecutionViewStep.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libcalamaresui/ExecutionViewStep.cpp b/src/libcalamaresui/ExecutionViewStep.cpp index 4dd10bd93..7b6b597a3 100644 --- a/src/libcalamaresui/ExecutionViewStep.cpp +++ b/src/libcalamaresui/ExecutionViewStep.cpp @@ -184,7 +184,7 @@ 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 +196,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 From 34cb777b0ae7465814b8ce1f698b02a25ec6e274 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 27 Aug 2019 11:11:35 +0200 Subject: [PATCH 3/9] [qml] Disable all the key shortcuts via root.activatedInCalamares --- src/qml/calamares/slideshow/Presentation.qml | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/qml/calamares/slideshow/Presentation.qml b/src/qml/calamares/slideshow/Presentation.qml index 4843e15a6..7053fb0ae 100644 --- a/src/qml/calamares/slideshow/Presentation.qml +++ b/src/qml/calamares/slideshow/Presentation.qml @@ -77,6 +77,8 @@ Item { property string fontFamily: "Helvetica" property string codeFontFamily: "Courier New" + property bool activatedInCalamares: false; + // Private API property int _lastShownSlide: 0 @@ -136,17 +138,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 From 0a9d0ddf68e9b42a107f72edc30c36b7fb2340a5 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 27 Aug 2019 11:49:27 +0200 Subject: [PATCH 4/9] [qml] In the default (sample) slideshow, activate key shortcuts - with V2 loading, the key shortcuts should be enabled when the slideshow itself is activated, not when it is loaded. --- src/branding/default/show.qml | 10 ++++++++-- src/qml/calamares/slideshow/Presentation.qml | 3 +++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/branding/default/show.qml b/src/branding/default/show.qml index c989676dd..1a174f0b6 100644 --- a/src/branding/default/show.qml +++ b/src/branding/default/show.qml @@ -25,7 +25,7 @@ Presentation id: presentation function nextSlide() { - console.log("Next slide"); + console.log("QML Component (default slideshow) Next slide"); presentation.goToNextSlide(); } @@ -70,7 +70,13 @@ Presentation function onActivate() { presentation.currentSlide = 0; - advanceTimer.running = true + presentation.activatedInCalamares = true; + advanceTimer.running = true; console.log("QML Component (default slideshow) activated"); } + + function onLeave() { + presentation.activatedInCalamares = true; + } + } diff --git a/src/qml/calamares/slideshow/Presentation.qml b/src/qml/calamares/slideshow/Presentation.qml index 7053fb0ae..a4138d588 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 From 7df6ed31e9547f8a70b6bc3bdf5a672c2c474a47 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 27 Aug 2019 12:01:36 +0200 Subject: [PATCH 5/9] [libcalamaresui] Refactor QML slideshow activation - one function for activation, since we activate from different places and each loader-API may need multiple steps for activation. --- src/libcalamaresui/ExecutionViewStep.cpp | 28 ++++++++++++++---------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/src/libcalamaresui/ExecutionViewStep.cpp b/src/libcalamaresui/ExecutionViewStep.cpp index 7b6b597a3..c131d9c9a 100644 --- a/src/libcalamaresui/ExecutionViewStep.cpp +++ b/src/libcalamaresui/ExecutionViewStep.cpp @@ -179,6 +179,21 @@ ExecutionViewStep::loadQmlV2() } } +static void +activateSlideShow( QQuickItem* slideshow, QQuickWidget* widget) +{ + if ( Branding::instance()->slideshowAPI() == 2 ) + { + // The QML was already loaded in the constructor, need to start it + callQMLFunction( slideshow, "onActivate" ); + } + else if ( !Calamares::Branding::instance()->slideshowPath().isEmpty() ) + { + // API version 1 assumes onCompleted is the trigger + widget->setSource( QUrl::fromLocalFile( Calamares::Branding::instance()->slideshowPath() ) ); + } +} + void ExecutionViewStep::loadQmlV2Complete() { @@ -208,7 +223,7 @@ ExecutionViewStep::loadQmlV2Complete() { // We're alreay visible! Must have been slow QML loading, and we // passed onActivate already. - callQMLFunction( m_qmlObject, "onActivate" ); + activateSlideShow( m_qmlObject, m_qmlShow ); } } } @@ -217,16 +232,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() ) ); - } + activateSlideShow( m_qmlObject, m_qmlShow ); JobQueue* queue = JobQueue::instance(); foreach ( const QString& instanceKey, m_jobInstanceKeys ) From 1e3e53d30a38bd5aa2aec2220a2b806b465ba9ac Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 27 Aug 2019 15:12:47 +0200 Subject: [PATCH 6/9] [libcalamaresui] Set activatedInCalamares special property - when the QML is activated, if the slideshow has this property, set it to true. This enables the keyboard shortcuts. --- src/libcalamaresui/ExecutionViewStep.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/libcalamaresui/ExecutionViewStep.cpp b/src/libcalamaresui/ExecutionViewStep.cpp index c131d9c9a..2b2c47e88 100644 --- a/src/libcalamaresui/ExecutionViewStep.cpp +++ b/src/libcalamaresui/ExecutionViewStep.cpp @@ -191,6 +191,17 @@ activateSlideShow( QQuickItem* slideshow, QQuickWidget* widget) { // API version 1 assumes onCompleted is the trigger widget->setSource( QUrl::fromLocalFile( Calamares::Branding::instance()->slideshowPath() ) ); + slideshow = widget->rootObject(); + } + + if ( slideshow ) + { + static const char propertyName[] = "activatedInCalamares"; + auto active = slideshow->property( propertyName ); + if ( active.isValid() && ( active.type() == QVariant::Bool ) && !active.toBool() ) + { + slideshow->setProperty( propertyName, true ); + } } } From be5388abcd27d2d1825fe6466391cf8369a9814d Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 27 Aug 2019 15:30:45 +0200 Subject: [PATCH 7/9] [libcalamaresui] activate -> change state - since we also need to *disable* the shortcuts, and should tell a V1 slideshow that it no longer is running, - use existing function to set the property to true / false depending. - instead of changeState( true ) or changeStage( false ), use meaningful enum names so that the code at the call site becomes readable; make the boolean part internal to the state-changing method. --- src/libcalamaresui/ExecutionViewStep.cpp | 49 ++++++++++++++++++------ 1 file changed, 37 insertions(+), 12 deletions(-) diff --git a/src/libcalamaresui/ExecutionViewStep.cpp b/src/libcalamaresui/ExecutionViewStep.cpp index 2b2c47e88..501995c07 100644 --- a/src/libcalamaresui/ExecutionViewStep.cpp +++ b/src/libcalamaresui/ExecutionViewStep.cpp @@ -179,28 +179,53 @@ ExecutionViewStep::loadQmlV2() } } -static void -activateSlideShow( QQuickItem* slideshow, QQuickWidget* widget) +/// @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, "onActivate" ); + callQMLFunction( slideshow, activate ? "onActivate" : "onLeave" ); } else if ( !Calamares::Branding::instance()->slideshowPath().isEmpty() ) { // API version 1 assumes onCompleted is the trigger - widget->setSource( QUrl::fromLocalFile( Calamares::Branding::instance()->slideshowPath() ) ); + 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 active = slideshow->property( propertyName ); - if ( active.isValid() && ( active.type() == QVariant::Bool ) && !active.toBool() ) + auto property = slideshow->property( propertyName ); + if ( property.isValid() && ( property.type() == QVariant::Bool ) && ( property.toBool() != activate ) ) { - slideshow->setProperty( propertyName, true ); + slideshow->setProperty( propertyName, activate ); } } } @@ -223,7 +248,7 @@ 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 @@ -234,7 +259,7 @@ ExecutionViewStep::loadQmlV2Complete() { // We're alreay visible! Must have been slow QML loading, and we // passed onActivate already. - activateSlideShow( m_qmlObject, m_qmlShow ); + changeSlideShowState( Slideshow::Start, m_qmlObject, m_qmlShow ); } } } @@ -243,7 +268,7 @@ ExecutionViewStep::loadQmlV2Complete() void ExecutionViewStep::onActivate() { - activateSlideShow( m_qmlObject, m_qmlShow ); + changeSlideShowState( Slideshow::Start, m_qmlObject, m_qmlShow ); JobQueue* queue = JobQueue::instance(); foreach ( const QString& instanceKey, m_jobInstanceKeys ) @@ -291,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; } From 03ac0d2cf0f9701e0edbd2cdf2a0ba7544b865cb Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 27 Aug 2019 15:54:06 +0200 Subject: [PATCH 8/9] [qml] Document the new property and how it updates --- CHANGES | 3 +++ src/branding/README.md | 10 ++++++++++ src/branding/default/show.qml | 14 +++++++++----- src/qml/calamares/slideshow/Presentation.qml | 8 +++++++- 4 files changed, 29 insertions(+), 6 deletions(-) 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 1a174f0b6..dcb0f9257 100644 --- a/src/branding/default/show.qml +++ b/src/branding/default/show.qml @@ -32,7 +32,7 @@ Presentation Timer { id: advanceTimer interval: 1000 - running: false + running: presentation.activatedInCalamares repeat: true onTriggered: nextSlide() } @@ -68,15 +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; - presentation.activatedInCalamares = true; - advanceTimer.running = true; console.log("QML Component (default slideshow) activated"); + presentation.currentSlide = 0; } function onLeave() { - presentation.activatedInCalamares = true; + console.log("QML Component (default slideshow) deactivated"); } } diff --git a/src/qml/calamares/slideshow/Presentation.qml b/src/qml/calamares/slideshow/Presentation.qml index a4138d588..1d2fd9c85 100644 --- a/src/qml/calamares/slideshow/Presentation.qml +++ b/src/qml/calamares/slideshow/Presentation.qml @@ -80,7 +80,13 @@ Item { property string fontFamily: "Helvetica" property string codeFontFamily: "Courier New" - property bool activatedInCalamares: false; + // 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 From 8ab0fb4e5f7b08b1ca4a967cc4ba25a72d9e916c Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 27 Aug 2019 15:58:09 +0200 Subject: [PATCH 9/9] [libcalamaresui] Apply coding style (missed earlier) --- src/libcalamaresui/Branding.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libcalamaresui/Branding.cpp b/src/libcalamaresui/Branding.cpp index c0ee71089..edbced5fa 100644 --- a/src/libcalamaresui/Branding.cpp +++ b/src/libcalamaresui/Branding.cpp @@ -226,7 +226,9 @@ Branding::Branding( const QString& brandingFilePath, QObject* parent ) QString pathString = slideShowPictures[ i ]; QFileInfo imageFi( componentDir.absoluteFilePath( pathString ) ); if ( !imageFi.exists() ) + { bail( QString( "Slideshow file %1 does not exist." ).arg( imageFi.absoluteFilePath() ) ); + } slideShowPictures[ i ] = imageFi.absoluteFilePath(); }