From 875191fbac8243423d09d51b3acabadd5a26b790 Mon Sep 17 00:00:00 2001 From: Pablo Gil Date: Thu, 6 Aug 2026 11:10:43 +0200 Subject: [PATCH] Expressions: add $parent binding paths --- .../GUI/Expressions/expressionhighlighter.cpp | 6 ++++- src/core/Animators/complexanimator.cpp | 27 +++++++++++++++++++ src/core/Boxes/boundingbox.cpp | 1 + src/core/Boxes/boundingbox.h | 2 ++ src/core/Expressions/propertybinding.cpp | 2 ++ .../Expressions/propertybindingparser.cpp | 3 ++- 6 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/app/GUI/Expressions/expressionhighlighter.cpp b/src/app/GUI/Expressions/expressionhighlighter.cpp index 5e4534f1c..b618807fc 100644 --- a/src/app/GUI/Expressions/expressionhighlighter.cpp +++ b/src/app/GUI/Expressions/expressionhighlighter.cpp @@ -44,7 +44,10 @@ ExpressionHighlighter::ExpressionHighlighter( mErrorFormat.setForeground(Qt::red); mAssignFormat.setForeground(QColor(255, 128, 128)); - const QString propPath = "([a-zA-Z0-9_\\.]+[a-zA-Z0-9_ \\.]*[a-zA-Z0-9_\\.]+)"; + const QString propPath = "((?:\\$parent\\.)*" + "[a-zA-Z0-9_\\.]+" + "[a-zA-Z0-9_ \\.]*" + "[a-zA-Z0-9_\\.]+)"; mPropSetRegex = QRegularExpression("^\\s*" "([A-Za-z_][A-Za-z0-9_]*)" "\\s*=\\s*" + propPath); @@ -62,6 +65,7 @@ ExpressionHighlighter::ExpressionHighlighter( const QStringList specs = { QStringLiteral("$value"), QStringLiteral("$frame"), + QStringLiteral("$parent"), QStringLiteral("$scene.fps"), QStringLiteral("$scene.width"), QStringLiteral("$scene.height"), diff --git a/src/core/Animators/complexanimator.cpp b/src/core/Animators/complexanimator.cpp index b3136afc1..1b31bec7c 100644 --- a/src/core/Animators/complexanimator.cpp +++ b/src/core/Animators/complexanimator.cpp @@ -28,6 +28,7 @@ #include "Sound/esound.h" #include #include "Boxes/boundingbox.h" +#include "Animators/transformanimator.h" ComplexAnimator::ComplexAnimator(const QString &name) : Animator(name) { @@ -205,6 +206,32 @@ void ComplexAnimator::ca_updateDescendatKeyFrame(Key* key) { Property *ComplexAnimator::ca_findPropertyWithPathRec( const int id, const QStringList &path, QStringList * const completions) const { + if(id < path.count() && path.at(id) == QStringLiteral("$parent")) { + const auto currentBox = getFirstAncestor(); + const auto findParentBox = [](const BoundingBox* const box) { + const auto parentTransform = box ? box->getParentTransform() : nullptr; + return parentTransform ? + parentTransform->getFirstAncestor() : nullptr; + }; + auto parentBox = findParentBox(currentBox); + int nextId = id + 1; + while(parentBox && nextId < path.count() && + path.at(nextId) == QStringLiteral("$parent")) { + parentBox = findParentBox(parentBox); + ++nextId; + } + if(!parentBox) return nullptr; + if(nextId == path.count()) { + if(completions) { + parentBox->ca_findPropertyWithPath( + 0, QStringList() << QString(), completions); + } + return parentBox; + } + return parentBox->ca_findPropertyWithPath( + nextId, path, completions); + } + Property* found = nullptr; const ComplexAnimator* acestorIter = this; while(acestorIter && (completions || !found)) { diff --git a/src/core/Boxes/boundingbox.cpp b/src/core/Boxes/boundingbox.cpp index e02d28762..42a503e45 100644 --- a/src/core/Boxes/boundingbox.cpp +++ b/src/core/Boxes/boundingbox.cpp @@ -476,6 +476,7 @@ void BoundingBox::setParentTransform(BasicTransformAnimator *parent) { if(parent == mParentTransform) return; mParentTransform = parent; mTransformAnimator->setParentTransformAnimator(mParentTransform); + emit prp_pathChanged(); } void BoundingBox::afterTotalTransformChanged(const UpdateReason reason) { diff --git a/src/core/Boxes/boundingbox.h b/src/core/Boxes/boundingbox.h index 44263cd71..d98aee11c 100644 --- a/src/core/Boxes/boundingbox.h +++ b/src/core/Boxes/boundingbox.h @@ -330,6 +330,8 @@ class CORE_EXPORT BoundingBox : public eBoxOrSound { void clearParent(); void setParentTransform(BasicTransformAnimator *parent); + BasicTransformAnimator* getParentTransform() const + { return mParentTransform; } bool isContainedIn(const QRectF &absRect) const; diff --git a/src/core/Expressions/propertybinding.cpp b/src/core/Expressions/propertybinding.cpp index 3092f6149..304f3d7fd 100644 --- a/src/core/Expressions/propertybinding.cpp +++ b/src/core/Expressions/propertybinding.cpp @@ -127,6 +127,8 @@ Property *PropertyBinding::sFindPropertyToBind(const QString& binding, void PropertyBinding::updateBindPath() { if(!mBindProperty || !mContext) return; + if(mPath == QStringLiteral("$parent") || + mPath.startsWith(QStringLiteral("$parent."))) return; QStringList prntPath; mContext->prp_getFullPath(prntPath); QStringList srcPath; diff --git a/src/core/Expressions/propertybindingparser.cpp b/src/core/Expressions/propertybindingparser.cpp index 085000dfb..5ac6b726c 100644 --- a/src/core/Expressions/propertybindingparser.cpp +++ b/src/core/Expressions/propertybindingparser.cpp @@ -126,7 +126,8 @@ bool parseSceneRangeMin(const QString& exp, int& pos) { void parseBinding(const QString& exp, int& pos, QString& binding) { while(pos < exp.count()) { const auto& c = exp.at(pos++); - if(!c.isLetterOrNumber() && c != ' ' && c != '.' && c != '_') break; + if(!c.isLetterOrNumber() && c != ' ' && c != '.' && + c != '_' && c != '$') break; binding.append(c); } }