Merge branch 'fix-retranslate' into calamares

Improve the Retranslator framework so it also affects Config objects.
main
Adriaan de Groot 4 years ago
commit 243c4e6b02

@ -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):

@ -134,7 +134,7 @@ getWidgetSidebar( Calamares::DebugWindowManager* debug,
{
QPushButton* debugWindowBtn = new QPushButton;
debugWindowBtn->setObjectName( "debugButton" );
CALAMARES_RETRANSLATE_WIDGET(
CALAMARES_RETRANSLATE_FOR(
debugWindowBtn,
debugWindowBtn->setText( QCoreApplication::translate( CalamaresWindow::staticMetaObject.className(),
"Show debug information" ) ); );
@ -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() )
{

@ -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 );

@ -211,55 +211,34 @@ 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 Retranslator::attach(QObject* o, std::function<void ()> f)
{
connect( instance(), &Retranslator::languageChanged, o, f );
f();
}
void
setAllowLocalTranslation( bool allow )
{

@ -64,43 +64,79 @@ 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 );
/// @brief Gets the global (single) Retranslator object
static Retranslator* instance();
/// @brief Call @p retranslateFunc when the language changes
void addRetranslateFunc( std::function< void( void ) > retranslateFunc );
/// @brief Helper function for attaching lambdas
static void attach( QObject* o, std::function< void( void ) > f);
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 } )
/** @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.
*
* NOTE: unlike plain QObject::connect(), the body is **also** called
* immediately after setting up the connection. This allows
* setup and translation code to be mixed together.
*/
#define CALAMARES_RETRANSLATE( body ) CalamaresUtils::Retranslator::attach( 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 object, not this object.
*
* NOTE: unlike plain QObject::connect(), the body is **also** called
* immediately after setting up the connection. This allows
* setup and translation code to be mixed together.
*/
#define CALAMARES_RETRANSLATE_FOR( object, body ) CalamaresUtils::Retranslator::attach( object, [=] { 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.
*
* NOTE: unlike plain QObject::connect(), the slot is **also** called
* immediately after setting up the connection. This allows
* setup and translation code to be mixed together.
*/
#define CALAMARES_RETRANSLATE_SLOT( slotfunc ) \
do \
{ \
this->connect( CalamaresUtils::Retranslator::retranslatorFor( this ), \
&CalamaresUtils::Retranslator::languageChange, \
this, \
slotfunc ); \
} while ( 0 )
connect( CalamaresUtils::Retranslator::instance(), &CalamaresUtils::Retranslator::languageChanged, this, slotfunc ); \
(this->*slotfunc)(); \
} while ( false )
#endif

@ -48,7 +48,7 @@ PythonQtViewStep::PythonQtViewStep( PythonQtObjectPtr cxt, QObject* parent )
CalamaresUtils::unmarginLayout( m_widget->layout() );
m_cxt.addObject( "_calamares_module_basewidget", m_widget );
CALAMARES_RETRANSLATE_WIDGET(
CALAMARES_RETRANSLATE_FOR(
m_widget,
CalamaresUtils::lookupAndCall( m_obj, { "retranslate" }, { CalamaresUtils::translatorLocaleName() } ); )
}

Loading…
Cancel
Save