diff --git a/CHANGES b/CHANGES index ae86c42f9..f2682e1f4 100644 --- a/CHANGES +++ b/CHANGES @@ -7,6 +7,24 @@ contributors are listed. Note that Calamares does not have a historical changelog -- this log starts with version 3.2.0. The release notes on the website will have to do for older versions. +# 3.2.41 (unreleased) # + +This release contains contributions from (alphabetically by first name): + - Anke Boersma + +## Core ## + - The (re)translation framework has been internally re-vamped to be + less resource-intensive and to work with all QObjects, not just + widgets. Consumers of the translations framework are expected to + set up the event filter on the top-level widget(s) manually. + +## Modules ## + - The *usersq* module has had a fair bit of QML rewritten to make it easier + to customize the colors used by the module in a consistent way. + (Thanks Anke) + + + # 3.2.40 (2021-07-14) # This release contains contributions from (alphabetically by first name): diff --git a/src/calamares/CalamaresWindow.cpp b/src/calamares/CalamaresWindow.cpp index d85d0de74..63d145e32 100644 --- a/src/calamares/CalamaresWindow.cpp +++ b/src/calamares/CalamaresWindow.cpp @@ -357,6 +357,8 @@ CalamaresWindow::CalamaresWindow( QWidget* parent ) , m_debugManager( new Calamares::DebugWindowManager( this ) ) , m_viewManager( nullptr ) { + installEventFilter( CalamaresUtils::Retranslator::instance() ); + // If we can never cancel, don't show the window-close button if ( Calamares::Settings::instance()->disableCancel() ) { diff --git a/src/calamares/testmain.cpp b/src/calamares/testmain.cpp index 0e7f4ab42..6d75a4fbb 100644 --- a/src/calamares/testmain.cpp +++ b/src/calamares/testmain.cpp @@ -24,6 +24,7 @@ #include "modulesystem/ModuleManager.h" #include "modulesystem/ViewModule.h" #include "utils/Logger.h" +#include "utils/Retranslator.h" #include "utils/Yaml.h" #include "viewpages/ExecutionViewStep.h" @@ -489,6 +490,10 @@ main( int argc, char* argv[] ) aw = replace_app; } mw = module.m_ui ? new QMainWindow() : nullptr; + if ( mw ) + { + mw->installEventFilter( CalamaresUtils::Retranslator::instance() ); + } (void)new Calamares::Branding( module.m_branding ); auto* modulemanager = new Calamares::ModuleManager( QStringList(), nullptr ); diff --git a/src/libcalamares/utils/Retranslator.cpp b/src/libcalamares/utils/Retranslator.cpp index 7f0d89ef9..a63e24bad 100644 --- a/src/libcalamares/utils/Retranslator.cpp +++ b/src/libcalamares/utils/Retranslator.cpp @@ -211,55 +211,29 @@ loadTranslator( const QLocale& locale, const QString& prefix, QTranslator* trans return ::tryLoad( translator, prefix, locale.name() ); } -Retranslator* -Retranslator::retranslatorFor( QObject* parent ) -{ - Retranslator* r = nullptr; - for ( QObject* child : parent->children() ) - { - r = qobject_cast< Retranslator* >( child ); - if ( r ) - { - return r; - } - } - - return new Retranslator( parent ); -} - -void -Retranslator::attachRetranslator( QObject* parent, std::function< void( void ) > retranslateFunc ) -{ - retranslatorFor( parent )->m_retranslateFuncList.append( retranslateFunc ); - retranslateFunc(); -} - - Retranslator::Retranslator( QObject* parent ) : QObject( parent ) { - parent->installEventFilter( this ); } - bool Retranslator::eventFilter( QObject* obj, QEvent* e ) { - if ( obj == parent() ) + if ( e->type() == QEvent::LanguageChange ) { - if ( e->type() == QEvent::LanguageChange ) - { - foreach ( std::function< void() > func, m_retranslateFuncList ) - { - func(); - } - emit languageChange(); - } + emit languageChanged(); } // pass the event on to the base return QObject::eventFilter( obj, e ); } +Retranslator* Retranslator::instance() +{ + static Retranslator s_instance(nullptr); + return &s_instance; +} + + void setAllowLocalTranslation( bool allow ) { diff --git a/src/libcalamares/utils/Retranslator.h b/src/libcalamares/utils/Retranslator.h index 1b3801d16..da0cc5723 100644 --- a/src/libcalamares/utils/Retranslator.h +++ b/src/libcalamares/utils/Retranslator.h @@ -64,40 +64,64 @@ DLLEXPORT bool loadTranslator( const QLocale& locale, const QString& prefix, QTr */ DLLEXPORT void setAllowLocalTranslation( bool allow ); + +/** @brief Handles change-of-language events + * + * There is one single Retranslator object. Use `instance()` to get it. + * The top-level widget of the application should call + * `installEventFilter( Retranslator::instance() )` + * to set up event-handling for translation events. The Retranslator + * will emit signal `languageChanged()` if there is such an event. + * + * Normal consumers should not have to use the Retranslator directly, + * but use the macros `CALAMARES_RETRANSLATE*` to set things up + * in code -- the macros will connect to the Retranslator's signals. + */ class Retranslator : public QObject { Q_OBJECT public: - /// @brief Call @p retranslateFunc when the language changes - static void attachRetranslator( QObject* parent, std::function< void( void ) > retranslateFunc ); - /// @brief What retranslator belongs to @p parent (may create one) - static Retranslator* retranslatorFor( QObject* parent ); + static Retranslator* instance(); signals: - void languageChange(); + void languageChanged(); protected: bool eventFilter( QObject* obj, QEvent* e ) override; private: explicit Retranslator( QObject* parent ); - - QList< std::function< void( void ) > > m_retranslateFuncList; }; } // namespace CalamaresUtils -#define CALAMARES_RETRANSLATE( body ) CalamaresUtils::Retranslator::attachRetranslator( this, [=] { body } ) -#define CALAMARES_RETRANSLATE_WIDGET( widget, body ) \ - CalamaresUtils::Retranslator::attachRetranslator( widget, [=] { body } ) -#define CALAMARES_RETRANSLATE_SLOT( slotfunc ) \ - do \ - { \ - this->connect( CalamaresUtils::Retranslator::retranslatorFor( this ), \ - &CalamaresUtils::Retranslator::languageChange, \ - this, \ - slotfunc ); \ - } while ( 0 ) +// Implementation detail: connects the retranslator to a slot or function +#define CALAMARES_RETRANSLATE_FOR( object, body ) \ + QObject::connect( CalamaresUtils::Retranslator::instance(), &CalamaresUtils::Retranslator::languageChanged, object, body ) + +/** @brief Call code for this object when language changes + * + * @p body should be a code block (it does not need braces) that can be wrapped + * up as a lambda. When the language changes, the lambda is called. Note that + * this macro should be used in constructors or other code that is run only + * once, since otherwise you will end up with multiple calls to @p body. + */ +#define CALAMARES_RETRANSLATE( body ) CALAMARES_RETRANSLATE_FOR( this, [=] { body } ) +/** @brief Call code for the given object (widget) when language changes + * + * This is identical to CALAMARES_RETRANSLATE, except the @p body is called + * for the given widget, not this object. + * + * NOTE: this macro is deprecated. + */ +#define CALAMARES_RETRANSLATE_WIDGET( widget, body ) CALAMARES_RETRANSLATE_FOR( widget, [=] { body } ) +/** @brief Call a slot in this object when language changes + * + * Given a slot (in method-function-pointer notation), call that slot when the + * language changes. This is shorthand for connecting the Retranslator's + * signal to the given slot. + */ +#define CALAMARES_RETRANSLATE_SLOT( slotfunc ) CALAMARES_RETRANSLATE_FOR( this, slotfunc ) #endif