diff options
author | Tim Jenssen <tim.jenssen@qt.io> | 2020-02-07 08:13:41 +0100 |
---|---|---|
committer | Maximilian Goldstein <max.goldstein@qt.io> | 2021-04-26 13:13:44 +0200 |
commit | 784c62441333de8d13d31c719ac01e6096247c01 (patch) | |
tree | c9916e04f0e7d7ddf1cf6467f0f9ad643267e58a /src/plugins | |
parent | f9b85604179e162c68ac8c42f97b1b3329c79b0a (diff) |
Implement debugtranslationservice
- moves the language feature from the preview service
to an own debugtranslationservice
- with the help of a proxytranslator the service gets all
translate requests
Change-Id: Ic26677bb1706abbea2db23e6aafe7a3f00648962
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Diffstat (limited to 'src/plugins')
12 files changed, 677 insertions, 105 deletions
diff --git a/src/plugins/qmltooling/qmldbg_messages/qdebugmessageservice.h b/src/plugins/qmltooling/qmldbg_messages/qdebugmessageservice.h index 24fd27514b..eb78afe900 100644 --- a/src/plugins/qmltooling/qmldbg_messages/qdebugmessageservice.h +++ b/src/plugins/qmltooling/qmldbg_messages/qdebugmessageservice.h @@ -59,8 +59,6 @@ QT_BEGIN_NAMESPACE -class QDebugMessageServicePrivate; - class QDebugMessageServiceImpl : public QDebugMessageService { Q_OBJECT diff --git a/src/plugins/qmltooling/qmldbg_preview/CMakeLists.txt b/src/plugins/qmltooling/qmldbg_preview/CMakeLists.txt index 2874580a03..2b3611b90d 100644 --- a/src/plugins/qmltooling/qmldbg_preview/CMakeLists.txt +++ b/src/plugins/qmltooling/qmldbg_preview/CMakeLists.txt @@ -8,7 +8,6 @@ qt_internal_add_plugin(QQmlPreviewServiceFactory OUTPUT_NAME qmldbg_preview TYPE qmltooling SOURCES - qqmldebugtranslationservice.cpp qqmldebugtranslationservice.h qqmlpreviewblacklist.cpp qqmlpreviewblacklist.h qqmlpreviewfileengine.cpp qqmlpreviewfileengine.h qqmlpreviewfileloader.cpp qqmlpreviewfileloader.h @@ -29,3 +28,11 @@ qt_internal_add_plugin(QQmlPreviewServiceFactory #### Keys ignored in scope 1:.:.:qmldbg_preview.pro:<TRUE>: # OTHER_FILES = "$$PWD/qqmlpreviewservice.json" + +qt_internal_extend_target(QQmlPreviewServiceFactory CONDITION QT_FEATURE_translation + SOURCES + proxytranslator.cpp proxytranslator.h + qqmldebugtranslationservice.cpp qqmldebugtranslationservice.h + PUBLIC_LIBRARIES + Qt::Gui +) diff --git a/src/plugins/qmltooling/qmldbg_preview/proxytranslator.cpp b/src/plugins/qmltooling/qmldbg_preview/proxytranslator.cpp new file mode 100644 index 0000000000..7f5227296a --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_preview/proxytranslator.cpp @@ -0,0 +1,156 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "proxytranslator.h" + +#include <QtCore/qlibraryinfo.h> + +QT_BEGIN_NAMESPACE + +void ProxyTranslator::addEngine(QQmlEngine *engine) +{ + m_engines.append(engine); +} + +void ProxyTranslator::removeEngine(QQmlEngine *engine) +{ + m_engines.removeOne(engine); +} + +bool ProxyTranslator::hasTranslation(const TranslationBindingInformation &translationBindingInformation) const +{ + resetTranslationFound(); + translationFromInformation(translationBindingInformation); + return translationFound(); +} + +QString ProxyTranslator::translationFromInformation(const TranslationBindingInformation &translationBindingInformation) +{ + return translationBindingInformation.compilationUnit->bindingValueAsString( + translationBindingInformation.compiledBinding); +} + + +QString ProxyTranslator::originStringFromInformation(const TranslationBindingInformation &translationBindingInformation) +{ + return translationBindingInformation.compilationUnit->stringAt( + translationBindingInformation.compiledBinding->stringIndex); +} + +QQmlSourceLocation ProxyTranslator::sourceLocationFromInformation(const TranslationBindingInformation &translationBindingInformation) +{ + return QQmlSourceLocation(translationBindingInformation.compilationUnit->fileName(), + translationBindingInformation.compiledBinding->valueLocation.line, + translationBindingInformation.compiledBinding->valueLocation.column); +} + + +void ProxyTranslator::setLanguage(const QUrl &context, const QLocale &locale) +{ + m_enable = true; + m_currentUILanguages = locale.uiLanguages().join(QLatin1Char(' ')); + + m_qtTranslator.reset(new QTranslator()); + if (!m_qtTranslator->load(locale, QLatin1String("qt"), QLatin1String("_"), + QLibraryInfo::path(QLibraryInfo::TranslationsPath))) { + m_qtTranslator.reset(); + } + + m_qmlTranslator.reset(new QTranslator(this)); + if (!m_qmlTranslator->load(locale, QLatin1String("qml"), QLatin1String("_"), + context.toLocalFile() + QLatin1String("/i18n"))) { + m_qmlTranslator.reset(); + } + + // unfortunately setUiLanguage set new translators, so do this first + for (QQmlEngine *engine : qAsConst(m_engines)) + engine->setUiLanguage(locale.bcp47Name()); + + // make sure proxy translator is the first used translator + QCoreApplication::removeTranslator(this); + QCoreApplication::installTranslator(this); + + for (QQmlEngine *engine : qAsConst(m_engines)) { + // have two retranslate runs to get elided warning even the same language was set + m_enable = false; + engine->retranslate(); + m_enable = true; + engine->retranslate(); + } + emit languageChanged(locale); +} + +QString ProxyTranslator::translate(const char *context, const char *sourceText, const char *disambiguation, int n) const +{ + if (!m_enable) + return {}; + QString result; + if (result.isNull() && m_qtTranslator) + result = m_qtTranslator->translate(context, sourceText, disambiguation, n); + if (result.isNull() && m_qmlTranslator) + result = m_qmlTranslator->translate(context, sourceText, disambiguation, n); + m_translationFound = !result.isNull(); + return result; +} + +void ProxyTranslator::resetTranslationFound() const +{ + m_translationFound = false; +} + +bool ProxyTranslator::translationFound() const +{ + return m_translationFound; +} + +bool ProxyTranslator::isEmpty() const +{ + if (m_qtTranslator && m_qtTranslator->isEmpty()) + return false; + if (m_qmlTranslator && m_qmlTranslator->isEmpty()) + return false; + return true; +} + +QString ProxyTranslator::currentUILanguages() const +{ + return m_currentUILanguages; +} + +QT_END_NAMESPACE diff --git a/src/plugins/qmltooling/qmldbg_preview/proxytranslator.h b/src/plugins/qmltooling/qmldbg_preview/proxytranslator.h new file mode 100644 index 0000000000..b53f81105a --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_preview/proxytranslator.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PROXYTRANSLATOR_H +#define PROXYTRANSLATOR_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qqmldebugserviceinterfaces_p.h> +#include <private/qqmlglobal_p.h> + +#include <QtCore/qstring.h> +#include <QtCore/qurl.h> +#include <QtCore/qtranslator.h> + +#include <memory> + +QT_BEGIN_NAMESPACE + +class ProxyTranslator : public QTranslator +{ + Q_OBJECT +public: + QString translate(const char *context, const char *sourceText, const char *disambiguation, int n) const override; + bool isEmpty() const override; + + QString currentUILanguages() const; + void setLanguage(const QUrl &context, const QLocale &locale); + void addEngine(QQmlEngine *engine); + void removeEngine(QQmlEngine *engine); + + bool hasTranslation(const TranslationBindingInformation &translationBindingInformation) const; + static QString translationFromInformation(const TranslationBindingInformation &translationBindingInformation); + static QString originStringFromInformation(const TranslationBindingInformation &translationBindingInformation); + static QQmlSourceLocation sourceLocationFromInformation(const TranslationBindingInformation &translationBindingInformation); +signals: + void languageChanged(const QLocale &locale); + +private: + void resetTranslationFound() const; + bool translationFound() const; + QList<QQmlEngine *> m_engines; + std::unique_ptr<QTranslator> m_qtTranslator; + std::unique_ptr<QTranslator> m_qmlTranslator; + bool m_enable = false; + QString m_currentUILanguages; + mutable bool m_translationFound = false; +}; + +QT_END_NAMESPACE + +#endif // PROXYTRANSLATOR_H diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmldebugtranslationservice.cpp b/src/plugins/qmltooling/qmldbg_preview/qqmldebugtranslationservice.cpp index 785fca6427..4f9d76432f 100644 --- a/src/plugins/qmltooling/qmldbg_preview/qqmldebugtranslationservice.cpp +++ b/src/plugins/qmltooling/qmldbg_preview/qqmldebugtranslationservice.cpp @@ -38,33 +38,406 @@ ****************************************************************************/ #include "qqmldebugtranslationservice.h" +#include "proxytranslator.h" + +#include <QtCore/qtranslator.h> +#include <QtCore/qdebug.h> +#include <QtCore/qlibraryinfo.h> +#include <QtCore/qdir.h> +#include <QtCore/qfile.h> +#include <QtCore/qtimer.h> +#include <QtCore/qhash.h> +#include <QtCore/qpointer.h> + +#include <private/qqmldebugtranslationprotocol_p.h> +#include <private/qqmldebugconnector_p.h> +#include <private/qversionedpacket_p.h> + +#include <private/qqmlbinding_p.h> +#include <private/qqmlbinding_p.h> +#include <private/qquickstategroup_p.h> +#include <private/qquickitem_p.h> +#include <private/qdebug_p.h> + +#include <QtQuick/qquickitem.h> + +#include <qquickview.h> QT_BEGIN_NAMESPACE -QQmlDebugTranslationServiceImpl::QQmlDebugTranslationServiceImpl(QObject *parent) : - QQmlDebugTranslationService(1, parent) +Q_LOGGING_CATEGORY(lcQmlTooling, "qt.quick.qmltooling.debugtranslation") + +using namespace QQmlDebugTranslation; + +QDebug operator<<(QDebug debug, const TranslationBindingInformation &translationBindingInformation) +{ + QQmlError error; + error.setUrl(translationBindingInformation.compilationUnit->url()); + error.setLine(translationBindingInformation.compiledBinding->valueLocation.line); + error.setColumn(translationBindingInformation.compiledBinding->valueLocation.column); + error.setDescription( + QString(QLatin1String( + "QDebug translation binding" + ))); + return debug << qPrintable(error.toString()); +} + +class QQmlDebugTranslationServicePrivate : public QObject +{ + Q_OBJECT +public: + QQmlDebugTranslationServicePrivate(QQmlDebugTranslationServiceImpl *parent) + : q(parent) + , proxyTranslator(new ProxyTranslator) + { + connect(&translatableTextOccurrenceTimer, &QTimer::timeout, + this, &QQmlDebugTranslationServicePrivate::sendTranslatableTextOccurrences); + } + + void setState(const QString &stateName) + { + if (currentQuickView) { + QQuickItem *rootItem = currentQuickView->rootObject(); + QQuickStateGroup *stateGroup = QQuickItemPrivate::get(rootItem)->_states(); + if (stateGroup->findState(stateName)) { + connect(stateGroup, &QQuickStateGroup::stateChanged, + this, &QQmlDebugTranslationServicePrivate::sendStateChanged, + Qt::ConnectionType(Qt::QueuedConnection | Qt::UniqueConnection)); + stateGroup->setState(stateName); + } + else + qWarning() << QString("Could not switch the state to %1").arg(stateName); + } + } + + void sendStateChanged() + { + QString stateName; + if (QQuickStateGroup *stateGroup = qobject_cast<QQuickStateGroup*>(sender())) + stateName = stateGroup->state(); + QVersionedPacket<QQmlDebugConnector> packet; + packet << Reply::StateChanged << stateName; + emit q->messageToClient(q->name(), packet.data()); + } + + void sendStateList() + { + QVersionedPacket<QQmlDebugConnector> packet; + packet << Reply::StateList; + + QQuickItem *rootItem = currentQuickView->rootObject(); + QQuickStateGroup *stateGroup = QQuickItemPrivate::get(rootItem)->_states(); + + QList<QQuickState *> states = stateGroup->states(); + QVector<QmlState> qmlStates; + + for (QQuickState *state : states) { + QmlState qmlState; + qmlState.name = state->name(); + qmlStates.append(qmlState); + } + + packet << qmlStates; + emit q->messageToClient(q->name(), packet.data()); + } + + void setWatchTextElides(bool s) + { + // TODO: for disabling we need to keep track which one were enabled + if (s == false) + qWarning() << "disable WatchTextElides is not implemented"; + watchTextElides = s; + for (auto &&information : qAsConst(objectTranslationBindingMultiMap)) { + QObject *scopeObject = information.scopeObject; + int elideIndex = scopeObject->metaObject()->indexOfProperty("elide"); + if (elideIndex >= 0) { + auto elideProperty = scopeObject->metaObject()->property(elideIndex); + elideProperty.write(scopeObject, Qt::ElideRight); + } + } + } + + void sendTranslatableTextOccurrences() + { + + QVersionedPacket<QQmlDebugConnector> packet; + packet << Reply::TranslatableTextOccurrences; + + QVector<QmlElement> qmlElements; + + for (auto &&information : qAsConst(objectTranslationBindingMultiMap)) { + + QObject *scopeObject = information.scopeObject; + auto compilationUnit = information.compilationUnit; + auto binding = information.compiledBinding; + auto metaObject = scopeObject->metaObject(); + + const QString propertyName = compilationUnit->stringAt(binding->propertyNameIndex); + + int textIndex = metaObject->indexOfProperty(propertyName.toLatin1()); + if (textIndex >= 0) { + + QmlElement qmlElement; + + qmlElement.codeMarker = codeMarker(information); + + auto textProperty = scopeObject->metaObject()->property(textIndex); + qmlElement.propertyName = textProperty.name(); + qmlElement.translationId = compilationUnit->stringAt( + compilationUnit->data->translations()[binding->value.translationDataIndex] + .stringIndex); + qmlElement.translatedText = textProperty.read(scopeObject).toString(); + qmlElement.elementId = qmlContext(scopeObject)->nameForObject(scopeObject); + + QFont font = scopeObject->property("font").value<QFont>(); + qmlElement.fontFamily = font.family(); + qmlElement.fontPointSize = font.pointSize(); + qmlElement.fontPixelSize = font.pixelSize(); + qmlElement.fontStyleName = font.styleName(); + qmlElement.horizontalAlignment = + scopeObject->property("horizontalAlignment").toInt(); + qmlElement.verticalAlignment = scopeObject->property("verticalAlignment").toInt(); + + QQmlType qmlType = QQmlMetaType::qmlType(metaObject); + qmlElement.elementType = qmlType.qmlTypeName() + "/" + qmlType.typeName(); + qmlElements.append(qmlElement); + + } else { + QString warningMessage = "(QQmlDebugTranslationService can not resolve %1 - %2:"\ + " this should never happen)"; + const QString id = qmlContext(scopeObject)->nameForObject(scopeObject); + qWarning().noquote() << warningMessage.arg(id, propertyName); + } + } + std::sort(qmlElements.begin(), qmlElements.end(), [](const auto &l1, const auto &l2){ + return l1.codeMarker < l2.codeMarker; + }); + + packet << qmlElements; + emit q->messageToClient(q->name(), packet.data()); + } + + void sendLanguageChanged() + { + QVersionedPacket<QQmlDebugConnector> packet; + packet << Reply::LanguageChanged; + emit q->messageToClient(q->name(), packet.data()); + } + + void sendMissingTranslations() + { + QVersionedPacket<QQmlDebugConnector> packet; + packet << Reply::MissingTranslations; + + QVector<TranslationIssue> issues; + for (auto &&information : qAsConst(objectTranslationBindingMultiMap)) { + if (!proxyTranslator->hasTranslation(information)) { + TranslationIssue issue; + issue.type = TranslationIssue::Type::Missing; + issue.codeMarker = codeMarker(information); + issue.language = proxyTranslator->currentUILanguages(); + issues.append(issue); + } + } + std::sort(issues.begin(), issues.end(), [](const auto &l1, const auto &l2){ + return l1.codeMarker < l2.codeMarker; + }); + packet << issues; + emit q->messageToClient(q->name(), packet.data()); + } + + void sendElidedTextWarning(const TranslationBindingInformation &information) + { + QVersionedPacket<QQmlDebugConnector> packet; + packet << Reply::TextElided; + + TranslationIssue issue; + issue.type = TranslationIssue::Type::Elided; + issue.codeMarker = codeMarker(information); + issue.language = proxyTranslator->currentUILanguages(); + + packet << issue; + emit q->messageToClient(q->name(), packet.data()); + } + + QQmlDebugTranslationServiceImpl *q; + + bool watchTextElides = false; + QMultiMap<QObject*, TranslationBindingInformation> objectTranslationBindingMultiMap; + QHash<QObject*, QVector<QMetaObject::Connection>> elideConnections; + ProxyTranslator *proxyTranslator; + + bool enableWatchTranslations = false; + QTimer translatableTextOccurrenceTimer; + QList<QPointer<QQuickItem>> translatableTextOccurrences; + + QPointer<QQuickView> currentQuickView; +private: + CodeMarker codeMarker(const TranslationBindingInformation &information) + { + CodeMarker c; + c.url = information.compilationUnit->url(); + c.line = information.compiledBinding->valueLocation.line; + c.column = information.compiledBinding->valueLocation.column; + return c; + } +}; + +QQmlDebugTranslationServiceImpl::QQmlDebugTranslationServiceImpl(QObject *parent) + : QQmlDebugTranslationService(1, parent) { + d = new QQmlDebugTranslationServicePrivate(this); + + connect(this, &QQmlDebugTranslationServiceImpl::watchTextElides, + d, &QQmlDebugTranslationServicePrivate::setWatchTextElides, + Qt::QueuedConnection); + + connect(this, &QQmlDebugTranslationServiceImpl::language, + d->proxyTranslator, &ProxyTranslator::setLanguage, + Qt::QueuedConnection); + + connect(this, &QQmlDebugTranslationServiceImpl::state, + d, &QQmlDebugTranslationServicePrivate::setState, + Qt::QueuedConnection); + + connect(this, &QQmlDebugTranslationServiceImpl::stateList, d, + &QQmlDebugTranslationServicePrivate::sendStateList, Qt::QueuedConnection); + + connect(d->proxyTranslator, &ProxyTranslator::languageChanged, + d, &QQmlDebugTranslationServicePrivate::sendLanguageChanged); + + connect(this, &QQmlDebugTranslationServiceImpl::missingTranslations, + d, &QQmlDebugTranslationServicePrivate::sendMissingTranslations); + + connect(this, &QQmlDebugTranslationServiceImpl::sendTranslatableTextOccurrences, + d, &QQmlDebugTranslationServicePrivate::sendTranslatableTextOccurrences, + Qt::QueuedConnection); +} + +QQmlDebugTranslationServiceImpl::~QQmlDebugTranslationServiceImpl() +{ + delete d->proxyTranslator; + d->proxyTranslator = {}; } void QQmlDebugTranslationServiceImpl::messageReceived(const QByteArray &message) { - Q_UNUSED(message); + QVersionedPacket<QQmlDebugConnector> packet(message); + QQmlDebugTranslation::Request command; + + packet >> command; + switch (command) { + case QQmlDebugTranslation::Request::ChangeLanguage: { + QUrl context; + QString locale; + packet >> context >> locale; + emit language(context, QLocale(locale)); + break; + } + case QQmlDebugTranslation::Request::ChangeState: { + QString stateName; + packet >> stateName; + emit state(stateName); + break; + } + case QQmlDebugTranslation::Request::StateList: { + emit stateList(); + break; + } + case QQmlDebugTranslation::Request::MissingTranslations: { + emit missingTranslations(); + break; + } + case QQmlDebugTranslation::Request::TranslatableTextOccurrences: { + emit sendTranslatableTextOccurrences(); + break; + } + case QQmlDebugTranslation::Request::WatchTextElides: { + emit watchTextElides(true); + break; + } + case QQmlDebugTranslation::Request::DisableWatchTextElides: { + emit watchTextElides(false); + break; + } + default: { + qWarning() << "DebugTranslationService: received unknown command: " << static_cast<int>(command); + break; + } + } // switch (command) +} + +void QQmlDebugTranslationServiceImpl::engineAboutToBeAdded(QJSEngine *engine) +{ + if (QQmlEngine *qmlEngine = qobject_cast<QQmlEngine *>(engine)) + d->proxyTranslator->addEngine(qmlEngine); + + if (engine->parent()) + d->currentQuickView = qobject_cast<QQuickView*>(engine->parent()); + + emit attachedToEngine(engine); +} + +void QQmlDebugTranslationServiceImpl::engineAboutToBeRemoved(QJSEngine *engine) +{ + if (QQmlEngine *qmlEngine = qobject_cast<QQmlEngine *>(engine)) + d->proxyTranslator->removeEngine(qmlEngine); + emit detachedFromEngine(engine); } QString QQmlDebugTranslationServiceImpl::foundElidedText(QObject *textObject, const QString &layoutText, const QString &elideText) { - Q_UNUSED(textObject); - Q_UNUSED(layoutText); - return elideText; + Q_UNUSED(layoutText) + QString elidedTextResult = elideText; + // do the check only for text objects which have translation bindings + auto it = d->objectTranslationBindingMultiMap.find(textObject); + if (it != d->objectTranslationBindingMultiMap.end()) { + if (QQuickItem* quickItem = qobject_cast<QQuickItem*>(textObject)) { + //const QColor originColor = quickItem->color(); + const TranslationBindingInformation information = d->objectTranslationBindingMultiMap.value(quickItem); + if (d->watchTextElides && d->proxyTranslator->hasTranslation(information)) { + d->sendElidedTextWarning(information); + } + if (!d->elideConnections.contains(quickItem)) { + // add "refresh" elide state connections which remove themself + auto clearElideInformation = [=]() { + //quickItem->setColor(originColor); + for (QMetaObject::Connection connection : d->elideConnections.value(quickItem)) + quickItem->disconnect(connection); + d->elideConnections.remove(quickItem); + }; + + auto connectWithChangedWidthThreshold = [=] () { + return connect(quickItem, &QQuickItem::widthChanged, [=]() { + if (quickItem->implicitWidth() <= quickItem->width()) + clearElideInformation(); + }); + }; + auto connectImplicitWidthChangedThreshold = [=] () { + return connect(quickItem, &QQuickItem::implicitWidthChanged, [=]() { + if (quickItem->implicitWidth() <= quickItem->width()) + clearElideInformation(); + }); + }; + + d->elideConnections.insert(quickItem, + {connectWithChangedWidthThreshold(), + connectImplicitWidthChangedThreshold()}); + } + } + } + return elidedTextResult; } -void QQmlDebugTranslationServiceImpl::foundTranslationBinding( - QQmlTranslationBinding *binding, QObject *scopeObject, - const QQmlRefPointer<QQmlContextData> &contextData) +void QQmlDebugTranslationServiceImpl::foundTranslationBinding(const TranslationBindingInformation &translationBindingInformation) { - Q_UNUSED(binding); - Q_UNUSED(scopeObject); - Q_UNUSED(contextData); + QObject *scopeObject = translationBindingInformation.scopeObject; + connect(scopeObject, &QObject::destroyed, [this, scopeObject] () { + this->d->objectTranslationBindingMultiMap.remove(scopeObject); + }); + d->objectTranslationBindingMultiMap.insert(scopeObject, translationBindingInformation); } QT_END_NAMESPACE + +#include <qqmldebugtranslationservice.moc> diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmldebugtranslationservice.h b/src/plugins/qmltooling/qmldbg_preview/qqmldebugtranslationservice.h index 802f21ecce..72e70f4b29 100644 --- a/src/plugins/qmltooling/qmldbg_preview/qqmldebugtranslationservice.h +++ b/src/plugins/qmltooling/qmldbg_preview/qqmldebugtranslationservice.h @@ -36,9 +36,8 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ - -#ifndef QDEBUGMESSAGESERVICE_H -#define QDEBUGMESSAGESERVICE_H +#ifndef QQMLDEBUGTRANSLATIONSERVICE_H +#define QQMLDEBUGTRANSLATIONSERVICE_H // // W A R N I N G @@ -50,39 +49,44 @@ // // We mean it. // +#include <QtCore/qglobal.h> #include <private/qqmldebugserviceinterfaces_p.h> -#include <QtCore/qlogging.h> -#include <QtCore/qmutex.h> -#include <QtCore/qelapsedtimer.h> +#include <QtCore/qbytearray.h> +#include <QtCore/qstring.h> +#include <QtCore/qurl.h> +#include <QtGui/qcolor.h> QT_BEGIN_NAMESPACE class QQmlDebugTranslationServicePrivate; - class QQmlDebugTranslationServiceImpl : public QQmlDebugTranslationService { Q_OBJECT public: - //needs to be in sync with QQmlDebugTranslationClient in qqmldebugtranslationclient_p.h - enum Command { - ChangeLanguage, - ChangeWarningColor, - ChangeElidedTextWarningString, - SetDebugTranslationServiceLogFile, - EnableElidedTextWarning, - DisableElidedTextWarning, - TestAllLanguages - }; QQmlDebugTranslationServiceImpl(QObject *parent = 0); + ~QQmlDebugTranslationServiceImpl(); QString foundElidedText(QObject *textObject, const QString &layoutText, const QString &elideText) override; - void foundTranslationBinding(QQmlTranslationBinding *binding, QObject *scopeObject, - const QQmlRefPointer<QQmlContextData> &contextData) override; + void foundTranslationBinding(const TranslationBindingInformation &translationBindingInformation) override; + void messageReceived(const QByteArray &message) override; + void engineAboutToBeAdded(QJSEngine *engine) override; + void engineAboutToBeRemoved(QJSEngine *engine) override; + +signals: + void language(const QUrl &context, const QLocale &locale); + void state(const QString &stateName); + void stateList(); + void watchTextElides(bool); + void missingTranslations(); + void sendTranslatableTextOccurrences(); + +private: + QQmlDebugTranslationServicePrivate *d; }; QT_END_NAMESPACE -#endif // QDEBUGMESSAGESERVICE_H +#endif // QQMLDEBUGTRANSLATIONSERVICE_H diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.cpp b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.cpp index c9317edb44..b8453128d8 100644 --- a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.cpp +++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.cpp @@ -42,7 +42,6 @@ #include <QtCore/qtimer.h> #include <QtCore/qsettings.h> #include <QtCore/qlibraryinfo.h> -#include <QtCore/qtranslator.h> #include <QtGui/qwindow.h> #include <QtGui/qguiapplication.h> @@ -93,9 +92,6 @@ QQmlPreviewHandler::QQmlPreviewHandler(QObject *parent) : QObject(parent) QQmlPreviewHandler::~QQmlPreviewHandler() { -#if QT_CONFIG(translation) - removeTranslators(); -#endif clear(); } @@ -225,41 +221,6 @@ void QQmlPreviewHandler::doZoom() m_lastPosition.initLastSavedWindowPosition(m_currentWindow); } -#if QT_CONFIG(translation) -void QQmlPreviewHandler::removeTranslators() -{ - if (!m_qtTranslator.isNull()) { - QCoreApplication::removeTranslator(m_qtTranslator.get()); - m_qtTranslator.reset(); - } - - if (m_qmlTranslator.isNull()) { - QCoreApplication::removeTranslator(m_qmlTranslator.get()); - m_qmlTranslator.reset(); - } -} - -void QQmlPreviewHandler::language(const QUrl &context, const QLocale &locale) -{ - removeTranslators(); - - m_qtTranslator.reset(new QTranslator(this)); - if (m_qtTranslator->load(locale, QLatin1String("qt"), QLatin1String("_"), - QLibraryInfo::path(QLibraryInfo::TranslationsPath))) { - QCoreApplication::installTranslator(m_qtTranslator.get()); - } - - m_qmlTranslator.reset(new QTranslator(this)); - if (m_qmlTranslator->load(locale, QLatin1String("qml"), QLatin1String("_"), - context.toLocalFile() + QLatin1String("/i18n"))) { - QCoreApplication::installTranslator(m_qmlTranslator.get()); - } - - for (QQmlEngine *engine : qAsConst(m_engines)) - engine->retranslate(); -} -#endif - void QQmlPreviewHandler::clear() { qDeleteAll(m_createdObjects); diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.h b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.h index 5dc867b763..b7ee8fa09b 100644 --- a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.h +++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.h @@ -66,7 +66,6 @@ class QQmlEngine; class QQuickItem; class QQmlPreviewUrlInterceptor; class QQuickWindow; -class QTranslator; class QQmlPreviewHandler : public QObject { @@ -81,9 +80,6 @@ public: void loadUrl(const QUrl &url); void rerun(); void zoom(qreal newFactor); -#if QT_CONFIG(translation) - void language(const QUrl &context, const QLocale &locale); -#endif void clear(); @@ -117,9 +113,6 @@ private: void frameSwapped(); void fpsTimerHit(); -#if QT_CONFIG(translation) - void removeTranslators(); -#endif QScopedPointer<QQuickItem> m_dummyItem; QList<QQmlEngine *> m_engines; @@ -148,11 +141,6 @@ private: FrameTime m_rendering; FrameTime m_synchronizing; - -#if QT_CONFIG(translation) - QScopedPointer<QTranslator> m_qtTranslator; - QScopedPointer<QTranslator> m_qmlTranslator; -#endif }; QT_END_NAMESPACE diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.cpp b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.cpp index 2e6aaa5858..b6d6a7a220 100644 --- a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.cpp +++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.cpp @@ -64,9 +64,6 @@ QQmlPreviewServiceImpl::QQmlPreviewServiceImpl(QObject *parent) : connect(this, &QQmlPreviewServiceImpl::load, &m_handler, &QQmlPreviewHandler::loadUrl); connect(this, &QQmlPreviewServiceImpl::rerun, &m_handler, &QQmlPreviewHandler::rerun); connect(this, &QQmlPreviewServiceImpl::zoom, &m_handler, &QQmlPreviewHandler::zoom); -#if QT_CONFIG(translation) - connect(this, &QQmlPreviewServiceImpl::language, &m_handler, &QQmlPreviewHandler::language); -#endif connect(&m_handler, &QQmlPreviewHandler::error, this, &QQmlPreviewServiceImpl::forwardError, Qt::DirectConnection); connect(&m_handler, &QQmlPreviewHandler::fps, this, &QQmlPreviewServiceImpl::forwardFps, @@ -137,16 +134,6 @@ void QQmlPreviewServiceImpl::messageReceived(const QByteArray &data) emit zoom(static_cast<qreal>(factor)); break; } -#if QT_CONFIG(translation) - case Language: { - QUrl context; - QString locale; - packet >> context >> locale; - emit language(context.isEmpty() ? m_currentUrl : context, - locale.isEmpty() ? QLocale() : QLocale(locale)); - break; - } -#endif default: forwardError(QString::fromLatin1("Invalid command: %1").arg(command)); break; diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.h b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.h index de50e6fc61..56deb7f092 100644 --- a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.h +++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.h @@ -73,8 +73,7 @@ public: Directory, ClearCache, Zoom, - Fps, - Language + Fps }; static const QString s_key; @@ -99,9 +98,6 @@ signals: void rerun(); void clearCache(); void zoom(qreal factor); -#if QT_CONFIG(translation) - void language(const QUrl &context, const QLocale &locale); -#endif private: QScopedPointer<QQmlPreviewFileEngineHandler> m_fileEngine; diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservicefactory.cpp b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservicefactory.cpp index 6ff9805bbe..23329dbcf2 100644 --- a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservicefactory.cpp +++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservicefactory.cpp @@ -39,7 +39,9 @@ #include "qqmlpreviewservicefactory.h" #include "qqmlpreviewservice.h" +#if QT_CONFIG(translation) #include "qqmldebugtranslationservice.h" +#endif QT_BEGIN_NAMESPACE @@ -47,9 +49,10 @@ QQmlDebugService *QQmlPreviewServiceFactory::create(const QString &key) { if (key == QQmlPreviewServiceImpl::s_key) return new QQmlPreviewServiceImpl(this); +#if QT_CONFIG(translation) if (key == QQmlDebugTranslationServiceImpl::s_key) return new QQmlDebugTranslationServiceImpl(this); - +#endif return nullptr; } diff --git a/src/plugins/qmltooling/qmldbg_server/qqmldebugserver.cpp b/src/plugins/qmltooling/qmldbg_server/qqmldebugserver.cpp index 13d5292eb2..0e4c90f987 100644 --- a/src/plugins/qmltooling/qmldbg_server/qqmldebugserver.cpp +++ b/src/plugins/qmltooling/qmldbg_server/qqmldebugserver.cpp @@ -422,11 +422,13 @@ void QQmlDebugServerImpl::parseArguments() << tr("Sends qDebug() and similar messages over the QML debug\n" "\t\t connection. QtCreator uses this for showing debug\n" "\t\t messages in the debugger console.") << '\n' +#if QT_CONFIG(translation) << '\n' << QQmlDebugTranslationService::s_key << "\t- " //: Please preserve the line breaks and formatting << tr("helps to see if a translated text\n" "\t\t will result in an elided text\n" "\t\t in QML elements.") << '\n' +#endif //QT_CONFIG(translation) << tr("Other services offered by qmltooling plugins that implement " "QQmlDebugServiceFactory and which can be found in the standard plugin " "paths will also be available and can be specified. If no \"services\" " |