Replies: 8 comments 4 replies
-
The Transform Panel problemI think the problems you describe here are all the result of the one thing: "Two-way data binding is the Devil." Ember.js used to rely on two-way data binding until they figured this out and eventually replaced it with what they call "Data Down, Actions Up" [1] [2]. Effectively, this means having a data binding that goes in one direction ("down") and sending messages ("actions") in the other direction ("up") to trigger data updates. DDAU ExampleConsider a setup with the following components:
The So, whenever the data is updated in Conversely, if a change in a lower component should affect a component further up, an action (basically an event, optionally with data) can be sent upwards. Upon receiving this action, the relevant component can decide to update any data, triggering a flow back down. For example, if the x coordinate of a shape is changed in
BenefitsThis approach has a number of advantages:
Whereas a two-way binding between [1] https://dockyard.com/blog/2015/10/14/best-practices-data-down-actions-up |
Beta Was this translation helpful? Give feedback.
-
Here we go: The canvas problem:
As you can see here, the CanvasItem now only holds components that make it up which allows for cleaner implementations of all things. If a component requires access to another, it simply calls |
Beta Was this translation helpful? Give feedback.
-
@jtrees «Data Down, Actions Up» is to me a simple and safe way of property binding propagation. Thanks for sharing. |
Beta Was this translation helpful? Give feedback.
-
Just a note about object composition – it might be helpful to get inspired by the Sketch file format schema, which gives you an idea of how Sketch composes items: https://github.com/sketch-hq/sketch-file-format |
Beta Was this translation helpful? Give feedback.
-
@jtrees thank you so much for the detailed explanation. @donadigo ah, that's exactly what I was looking for, thank. Thank you all for the suggestions and insights, I love Open Source! |
Beta Was this translation helpful? Give feedback.
-
Sure! |
Beta Was this translation helpful? Give feedback.
-
Just a heads up to the folks participating in this discussion. The "Data Down, Action Up" rework has just landed on Master and holy smokes it makes a huge difference. The transition from inheritance to composition is under way and I should be able to create a WIP PR in a couple of weeks. Stay tuned for more awesome stuff! |
Beta Was this translation helpful? Give feedback.
-
All of things listed above have been done 🎉 ✔️ Thank you all for participating and for your invaluable insights! I'll keep this discussion open just in case something more comes up. |
Beta Was this translation helpful? Give feedback.
-
This is more a of a general issue to discuss basic architecture and future improvements to a substantial area of the application.
The CanvasItem problem
We currently have in place a base abstract class called
CanvasItem
which we use to build all the different items on top of it (Rectangle, Ellipse, Image, etc.).The problems for this approach are starting to show as we implement more and more features and we make the interactions and transformations with the Canvas more complex.
These are few highlights of what we stumbled upon so far:
How can we improve this?
The Transform Panel problem
The Transform panel allows to edit item's attributes (x, y, width, height, etc) through input fields.
These attributes can be also modified directly on the canvas, by moving and resizing the selected items.
These fields need a bidirectional binding with the specific attribute (eg. the X input is binded to the item's X attribute), so when one changes, the other follows accordingly, and proper signals are emitted to allow the UI to react accordingly (eg. the selection bounds follow, the artboards get updated, etc.).
Since items can be manipulated directly from the Canvas, we need to deactivate some of these signals and bindings to not fall into an infinite loop of, the item gets updated, the input field receives the signal which updates the value, the input field emits the signal because the value changed, the item receives the signal to updated the value, the item emits the signal with the new value, the input receives the signal... and it goes on and on and on.
We solved this issue by implementing some conditions and by unbinding properties at the right time, but this solution causes some problems:
These wouldn't be critical issues if we assume the user mainly interact with the canvas and changes those values sporadically, maybe a few pixels at a time, but we can't assume that.
Sometimes the user might focus on the X input and hold down the arrow up key, causing hundreds of triggers consecutively.
How can we improve this?
Beta Was this translation helpful? Give feedback.
All reactions