Skip to content

Commit

Permalink
sync: from linuxdeepin/dtkdeclarative
Browse files Browse the repository at this point in the history
Synchronize source files from linuxdeepin/dtkdeclarative.

Source-pull-request: linuxdeepin/dtkdeclarative#461
  • Loading branch information
deepin-ci-robot committed Feb 20, 2025
1 parent 5f0458a commit be8e575
Show file tree
Hide file tree
Showing 2 changed files with 199 additions and 31 deletions.
208 changes: 178 additions & 30 deletions src/private/dconfigwrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <QLoggingCategory>

Check warning on line 9 in src/private/dconfigwrapper.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QLoggingCategory> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QQmlEngine>

Check warning on line 10 in src/private/dconfigwrapper.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QQmlEngine> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QTimer>

Check warning on line 11 in src/private/dconfigwrapper.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QTimer> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QQmlInfo>

Check warning on line 12 in src/private/dconfigwrapper.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QQmlInfo> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <private/qqmlopenmetaobject_p.h>

Check warning on line 13 in src/private/dconfigwrapper.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <private/qqmlopenmetaobject_p.h> not found. Please note: Cppcheck does not need standard library headers to get proper results.

#include <DConfig>

Check warning on line 15 in src/private/dconfigwrapper.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <DConfig> not found. Please note: Cppcheck does not need standard library headers to get proper results.
Expand All @@ -22,8 +23,7 @@ Q_LOGGING_CATEGORY(cfLog, "dtk.dsg.config");
DCORE_USE_NAMESPACE;

// the properties and previous values.
using DefalutProperties = QMap<QByteArray, QVariant>;
static DefalutProperties propertyAndValues(const QObject* obj)
static DConfigWrapper::DefalutProperties propertyAndValues(const QObject* obj)
{
QMap<QByteArray, QVariant> properties;
const QMetaObject *mo = obj->metaObject();
Expand Down Expand Up @@ -138,12 +138,53 @@ void DConfigWrapper::setSubpath(const QString &subpath)
m_subpath = subpath;
}

QObject *DConfigWrapper::proxyTarget() const
{
return m_proxyTarget;
}

void DConfigWrapper::setProxyTarget(QObject *newProxyTarget)
{
if (m_proxyTarget == newProxyTarget)
return;

if (m_proxyTargetValueChangedConnection) {
disconnect(m_proxyTargetValueChangedConnection);
}

m_proxyTarget = newProxyTarget;

if (m_componentCompleted) {
if (m_proxyTarget)
initWithProxyTarget();
else
initWithDConfig();
}

Q_EMIT proxyTargetChanged();
}

/*!
* \brief \sa DConfig keyList()
* \return
*/
QStringList DConfigWrapper::keyList() const
{
if (m_proxyTarget) {
const QMetaObject *mo = m_proxyTarget->metaObject();
const int offset = mo->propertyOffset();
const int count = mo->propertyCount();

QStringList keyList = {};
keyList.reserve(count);

for (int i = offset; i < count; ++i) {
const QMetaProperty &property = mo->property(i);
keyList << property.name();
}
return keyList;
}

if (!impl)
return QStringList();

Expand All @@ -156,6 +197,18 @@ QStringList DConfigWrapper::keyList() const
*/
bool DConfigWrapper::isValid() const
{
if (m_proxyTarget) {
int index = m_proxyTarget->metaObject()->indexOfMethod("isInitializeSucceed()");
if (index < 0) {
qmlEngine(this)->throwError(QString("Can't know whether the DConfig is valid, "
"because the proxyTarget doesn't have "
"isInitializeSucceed method"));
return false;
}

return m_proxyTarget->metaObject()->method(index).invoke(m_proxyTarget);
}

if (!impl)
return false;

Expand All @@ -168,6 +221,13 @@ bool DConfigWrapper::isValid() const
*/
QVariant DConfigWrapper::value(const QString &key, const QVariant &fallback) const
{
if (m_proxyTarget) {
if (isDefaultValue(key))
return fallback;

return m_proxyTarget->property(key.toUtf8().constData());
}

if (!impl)
return fallback;

Expand All @@ -180,6 +240,11 @@ QVariant DConfigWrapper::value(const QString &key, const QVariant &fallback) con
*/
void DConfigWrapper::setValue(const QString &key, const QVariant &value)
{
if (m_proxyTarget) {
m_proxyTarget->setProperty(key.toUtf8().constData(), value);
return;
}

if (!impl)
return;

Expand All @@ -188,6 +253,13 @@ void DConfigWrapper::setValue(const QString &key, const QVariant &value)

void DConfigWrapper::resetValue(const QString &key)
{
if (m_proxyTarget) {
int index = m_proxyTarget->metaObject()->indexOfProperty(key.toUtf8().constData());
if (index >= 0)
m_proxyTarget->metaObject()->property(index).reset(m_proxyTarget);
return;
}

if (!impl)
return;

Expand All @@ -196,7 +268,7 @@ void DConfigWrapper::resetValue(const QString &key)

void DConfigWrapper::classBegin()
{

m_componentCompleted = false;
}

/*!
Expand All @@ -207,51 +279,127 @@ void DConfigWrapper::classBegin()
*/
void DConfigWrapper::componentComplete()
{
m_componentCompleted = false;
// Get the dynamic properties and previous values defined in qml.
m_properties = propertyAndValues(this);
qCDebug(cfLog) << "properties" << m_properties;

#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
auto objectType = new QQmlOpenMetaObjectType(&DConfigWrapper::staticMetaObject, qmlEngine(this));
#else
auto objectType = new QQmlOpenMetaObjectType(&DConfigWrapper::staticMetaObject);
#endif
m_metaObject = new DConfigWrapperMetaObject(this, objectType);
m_metaObject->setCached(true);

if (m_proxyTarget) {
initWithProxyTarget();
} else {
initWithDConfig();
}
}

bool DConfigWrapper::isDefaultValue(const QString &key) const {
Q_ASSERT(m_proxyTarget);
if (!f_isDefaultValue.enclosingMetaObject())
return false;

return f_isDefaultValue.invoke(m_proxyTarget, Qt::DirectConnection, Q_ARG(QString, key));
}

void DConfigWrapper::initWithProxyTarget()
{
Q_ASSERT(m_proxyTarget);
if (impl) {
impl->deleteLater();
impl = nullptr;
}

qCInfo(cfLog) << QString("Initialize dconfig with proxy object:") << m_proxyTarget;

auto mo = m_proxyTarget->metaObject();
int index = mo->indexOfMethod(QMetaObject::normalizedSignature("isDefaultValue(const QString&)"));

if (index < 0) {
qmlWarning(this) << "Can't know whether the value is default, because the proxyTarget doesn't have isDefaultValue method.";
} else {
f_isDefaultValue = mo->method(index);
}

for (auto iter = m_properties.begin(); iter != m_properties.end(); iter++) {
// it's need to emit signal, because other qml object maybe read the old value
// when binding the property before the component completed, also it has a performance problem.
// sync backend's value to `Wrapper`, we only use Wrapper's value(defined in qml) as fallback value.
if (isDefaultValue(iter.key()))
m_metaObject->setValue(iter.key(), iter.value());
else
m_metaObject->setValue(iter.key(), m_proxyTarget->property(iter.key()));
}

Q_ASSERT(!m_proxyTargetValueChangedConnection);
m_proxyTargetValueChangedConnection = connect(m_proxyTarget,
SIGNAL(valueChanged(QString, QVariant)),
this, SLOT(onProxyTargetValueChanged(QString,QVariant)));

if (!m_proxyTargetValueChangedConnection) {
if (auto engine = qmlEngine(this))
engine->throwError(QString(QLatin1String("Can't connect to valueChanged signal, proxyTarget is invalid.")));
else
qCWarning(cfLog) << "Can't connect to valueChanged signal, proxyTarget is invalid.";
}
}

void DConfigWrapper::initWithDConfig()
{
Q_ASSERT(!impl);
impl = new DTK_CORE_NAMESPACE::DConfig(m_name, m_subpath, this);
Q_ASSERT(!m_proxyTarget);

if (!impl->isValid()) {
qCWarning(cfLog) << QString("create dconfig failed, valid:%1, name:%2, subpath:%3, backend:%4")
.arg(impl->isValid())
.arg(impl->name())
.arg(impl->subpath())
.arg(impl->backendName());
.arg(impl->isValid())
.arg(impl->name())
.arg(impl->subpath())
.arg(impl->backendName());
impl->deleteLater();
impl = nullptr;
return;
}

qInfo() << QString("create dconfig successful, valid:%1, name:%2, subpath:%3, backend:%4")
.arg(impl->isValid())
.arg(impl->name())
.arg(impl->subpath())
.arg(impl->backendName());
qCInfo(cfLog) << QString("create dconfig successful, valid:%1, name:%2, subpath:%3, backend:%4")
.arg(impl->isValid())
.arg(impl->name())
.arg(impl->subpath())
.arg(impl->backendName());

// Get the dynamic properties and previous values defined in qml.
const DefalutProperties &properties = propertyAndValues(this);
qCDebug(cfLog) << "properties" << properties;

#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
auto objectType = new QQmlOpenMetaObjectType(&DConfigWrapper::staticMetaObject, qmlEngine(this));
#else
auto objectType = new QQmlOpenMetaObjectType(&DConfigWrapper::staticMetaObject);
#endif
auto mo = new DConfigWrapperMetaObject(this, objectType);
mo->setCached(true);

for (auto iter = properties.begin(); iter != properties.end(); iter++) {
for (auto iter = m_properties.begin(); iter != m_properties.end(); iter++) {
// it's need to emit signal, because other qml object maybe read the old value
// when binding the property before the component completed, also it has a performance problem.
// sync backend's value to `Wrapper`, we only use Wrapper's value(defined in qml) as fallback value.
mo->setValue(iter.key(), impl->value(iter.key(), iter.value()));
m_metaObject->setValue(iter.key(), impl->value(iter.key(), iter.value()));
}

// Using QueuedConnection because impl->setValue maybe emit sync signal in `propertyWriteValue`.
connect(impl, &DTK_CORE_NAMESPACE::DConfig::valueChanged, this, [this, mo, properties](const QString &key){
// Using QueuedConnection because impl->setValue maybe emit sync signal in `propertyWriteValue`.
connect(impl, &DTK_CORE_NAMESPACE::DConfig::valueChanged, this, [this](const QString &key){
const QByteArray &proName = key.toLocal8Bit();
if (properties.contains(proName)) {
if (m_properties.contains(proName)) {
qCDebug(cfLog) << "update value from DConfig by 'valueChanged', key:" << proName;
mo->setValue(proName, impl->value(proName, properties.value(proName)));
m_metaObject->setValue(proName, impl->value(proName, m_properties.value(proName)));
}
Q_EMIT valueChanged(key);
}, Qt::QueuedConnection);
}

void DConfigWrapper::onProxyTargetValueChanged(const QString &key, const QVariant &value)
{
const QByteArray &propName = key.toLocal8Bit();
if (m_properties.contains(propName)) {
qCDebug(cfLog) << "update value from:" << m_proxyTarget << "by valueChanged', key:" << propName
<< "value:" << value;
if (isDefaultValue(propName))
m_metaObject->setValue(propName, m_properties.value(propName));
else
m_metaObject->setValue(propName, value);
}
Q_EMIT valueChanged(key);
}
22 changes: 21 additions & 1 deletion src/private/dconfigwrapper_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class DConfigWrapper : public QObject, public QQmlParserStatus
Q_INTERFACES(QQmlParserStatus)
Q_PROPERTY(QString name READ name WRITE setName)
Q_PROPERTY(QString subpath READ subpath WRITE setSubpath)
Q_PROPERTY(QObject *proxyTarget READ proxyTarget WRITE setProxyTarget NOTIFY proxyTargetChanged)
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
QML_NAMED_ELEMENT(Config)
#endif
Expand All @@ -33,6 +34,9 @@ class DConfigWrapper : public QObject, public QQmlParserStatus
QString subpath() const;
void setSubpath(const QString &subpath);

QObject *proxyTarget() const;
void setProxyTarget(QObject *newProxyTarget);

public Q_SLOTS:

Check warning on line 40 in src/private/dconfigwrapper_p.h

View workflow job for this annotation

GitHub Actions / cppcheck

There is an unknown macro here somewhere. Configuration is required. If Q_SLOTS is a macro then please configure it.
QVariant value(const QString &key, const QVariant &fallback = QVariant()) const;
void setValue(const QString &key, const QVariant &value);
Expand All @@ -42,16 +46,32 @@ public Q_SLOTS:

Q_SIGNALS:
void valueChanged(const QString &key);
void proxyTargetChanged();

public:
virtual void classBegin() override;
virtual void componentComplete() override;

using DefalutProperties = QMap<QByteArray, QVariant>;

private:
bool isDefaultValue(const QString &key) const;
void initWithProxyTarget();
void initWithDConfig();
Q_SLOT void onProxyTargetValueChanged(const QString &key, const QVariant &value);

friend DConfigWrapperMetaObject;
DTK_CORE_NAMESPACE::DConfig *impl;
bool m_componentCompleted = false;
DConfigWrapperMetaObject *m_metaObject = nullptr;
DefalutProperties m_properties;

DTK_CORE_NAMESPACE::DConfig *impl = nullptr;
QString m_name;
QString m_subpath;

QObject *m_proxyTarget = nullptr;
QMetaObject::Connection m_proxyTargetValueChangedConnection;
QMetaMethod f_isDefaultValue;
Q_DISABLE_COPY(DConfigWrapper)
};

Expand Down

0 comments on commit be8e575

Please # to comment.