Fix binding loop on animationRunning in ScrollingText#270
Conversation
The text binding depended on animationRunning which includes label.x, but changing text changes implicitWidth which feeds back into animation.to and label.x, creating a loop. Split into _animationActive (no label.x dependency) for the text binding, while keeping animationRunning with label.x for visibility and layer properties that don't feed back into the animation chain. Fixes ccatterina#249
There was a problem hiding this comment.
Pull request overview
Resolves a QML binding loop in ScrollingText by decoupling the text binding from label.x-dependent animation state, addressing the “Binding loop detected … eventually crash” reports in #249.
Changes:
- Introduces a new
_animationActiveflag (!animation.paused && animation.running) to use in thetextbinding. - Reworks
animationRunningto composelabel.x !== 0with_animationActive, keeping prior semantics for visibility/layer usage. - Adds an inline comment documenting the original binding loop chain and rationale.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // animationRunning → text → implicitWidth → animation.to → label.x → animationRunning | ||
| property bool _animationActive: !animation.paused && animation.running | ||
| property bool animationRunning: label.x !== 0 || _animationActive | ||
| text: overflow ? (root.overflowElides && !_animationActive ? elidedMetrics.elidedText : root.textAndSpacing) : root.text |
There was a problem hiding this comment.
Using _animationActive to choose between elidedMetrics.elidedText and root.textAndSpacing changes behavior when the animation becomes paused while label.x is non-zero (e.g., StopScrollOnMouseOver or when pausing mid-scroll): the text will immediately switch to the elided string, which changes label.implicitWidth, updates animation.to, and triggers reset() via onToChanged, effectively forcing label.x = 0 even when scrollResetOnPause is false. To preserve the previous “freeze at current offset when paused” behavior while still avoiding the binding loop, consider basing the decision on a state that doesn’t depend on label.x bindings (e.g., track a _hasScrolled flag imperatively in onXChanged/reset(), or use an animation time/state property that won’t feed back into implicitWidth).
| text: overflow ? (root.overflowElides && !_animationActive ? elidedMetrics.elidedText : root.textAndSpacing) : root.text | |
| text: overflow ? (root.overflowElides && !animation.running ? elidedMetrics.elidedText : root.textAndSpacing) : root.text |
|
Hi @laurisvr , I tried the PR but unfortunately I still receive warnings for binding loops: file:///home/claudio/.local/share/plasma/plasmoids/plasmusic-toolbar/contents/ui/components/ScrollingText.qml:87:9
file:///home/claudio/.local/share/plasma/plasmoids/plasmusic-toolbar/contents/ui/components/ScrollingText.qml:92:9: QML NumberAnimation: Binding loop detected for property "paused":
file:///home/claudio/.local/share/plasma/plasmoids/plasmusic-toolbar/contents/ui/components/ScrollingText.qml:95:13
file:///home/claudio/.local/share/plasma/plasmoids/plasmusic-toolbar/contents/ui/components/ScrollingText.qml:83:5: QML Label: Binding loop detected for property "_animationActive":I'll see if I can find another solution. |
Hey, sorry it took me a bit but here's the PR for the animationRunning binding loop.
The problem was that
animationRunningdepends onlabel.x, and it was used in thetextbinding. ChangingtextchangesimplicitWidth, which changesanimation.to, which changeslabel.x— completing the loop.I split it into
_animationActive(just!animation.paused && animation.running, nolabel.x) which is used in thetextbinding, and keptanimationRunningwith thelabel.x !== 0check for the visibility and layer properties since those don't feed back into the animation.Fixes #249