Sorry, you are right, I realized that on my way out. So a ChangeListener is necessary for my example to be right.
Anyway, the problem of multiple invalidations exists and I hope I managed to demonstrate that the solution with ScenePulseListener doesn't scale. Regards, Tomas On Thu, Nov 7, 2013 at 8:55 PM, Martin Sladecek <[email protected]> wrote: > On 11/07/2013 04:03 PM, Tomas Mikula wrote: >> >> On Thu, Nov 7, 2013 at 3:34 PM, Martin Sladecek >> <[email protected]> wrote: >>> >>> On 11/07/2013 03:18 PM, Tomas Mikula wrote: >>>> >>>> Hi Martin, >>>> >>>> On Thu, Nov 7, 2013 at 2:32 PM, Martin Sladecek >>>> <[email protected]> wrote: >>>>> >>>>> This is something different. When properties depends on each other >>>>> (using >>>>> bindings), the binding computation is deferred to the first query >>>>> (get() >>>>> call) of the dependant. That means, if q depends on p, you can call >>>>> p.set() >>>>> as many times you want, but the recomputation will be triggered just >>>>> before >>>>> first q.get() call. >>>> >>>> The point of my first example was inconsistency. >>>> Suppose this scenario (remember, the invalidation listeners of p are >>>> deferred until the next pulse): >>>> >>>> [pulse] >>>> ... >>>> p.set() >>>> ... >>>> q.get() // returned value not consistent with the value of p >>>> ... >>>> [pulse] >>> >>> It is consistent, providing that you use binding API for the computation. >> >> I probably forgot to state it explicitly that I'm considering an >> implementation of properties/bindings that don't trigger the listeners >> (invalidation/change) right away, but defer them until the next pulse. >> Then again, the state of that hypothetical object above is not >> consistent (until the next pulse). >> >>>> I like my objects to appear consistent to the outside world at all >>>> times, so that the client code doesn't have to deal with the >>>> inconsistencies. >>>> >>>>> Also in your second example, the second invalidation of >>>>> s would be practically a no-op. >>>> >>>> It is not if you have an invalidation (or change) listener registered on >>>> s. >>> >>> Only in case of change listener. Invalidation listener would not >>> validate/recompute s. >> >> The invalidation listener will be executed twice. That itself is bad >> enough. The invalidation listener itself can trigger an expensive >> computation. Whether or not it (indirectly) causes the recomputation >> of s. Consider, for example, N such [p <- s] black boxes, where a >> single invalidation of p causes two invalidations of s, and chain them >> sequentially. Then a single invalidation of p_1 causes 2^N >> invalidations of s_N. That's bad enough, even though no property value >> is being recomputed. This is, in fact, the behavior of the bindings >> API you mentioned. What I'm saying is that deferring invalidation >> listeners until the next pulse doesn't help either (except for simple >> cases). > > > No, the invalidation listener will be executed only once. The property needs > to be validated before the invalidation listener can be called again. > > -Martin > >> >> Cheers, >> Tomas >> >>> -Martin >>> >>>> Tomas >>>> >>>>> Of course, this doesn't work if you have a ChangeListener on q, because >>>>> the >>>>> ChangeListener basically need to do get() in order to compute the new >>>>> property value (which is passed as an argument to it's method). >>>>> >>>>> When it's not a property you want to recompute, but an internal state >>>>> (you >>>>> use to setup the children), layoutChildren() should be the method for >>>>> you. >>>>> >>>>> -Martin >>>>> >>>>> >>>>> On 11/07/2013 02:08 PM, Tomas Mikula wrote: >>>>>> >>>>>> On Thu, Nov 7, 2013 at 11:58 AM, John Hendrikx <[email protected]> >>>>>> wrote: >>>>>>> >>>>>>> Hm, I found it googling, and since it showed up here: >>>>>>> >>>>>>> >>>>>>> >>>>>>> >>>>>>> http://docs.oracle.com/javafx/2/api/javafx/scene/Scene.ScenePulseListener.html >>>>>>> >>>>>>> I figured it was public, but I just noticed the class is defined >>>>>>> package >>>>>>> private. >>>>>> >>>>>> Although not part of the public API, you can use >>>>>> >>>>>> import com.sun.javafx.tk.TKPulseListener; >>>>>> import com.sun.javafx.tk.Toolkit; >>>>>> >>>>>> Toolkit.getToolkit().addSceneTkPulseListener(new >>>>>> TKPulseListener(){...}) >>>>>> >>>>>> >>>>>> Anyway, I don't think deferring property invalidation until the next >>>>>> pulse is very useful in general, for the following reasons: >>>>>> >>>>>> 1) It can lead to inconsistent observable state of your objects. >>>>>> Consider an object with properties p, q, where the value of q depends >>>>>> on the value of p, and consider changing the value of p. Now, right >>>>>> after >>>>>> p.set(x); >>>>>> returns, the state of the object is inconsistent until the next pulse, >>>>>> and this inconsistency is observable to the outside world. >>>>>> >>>>>> 2) It doesn't (in general) avoid recalculations. Consider properties >>>>>> p, q, r, s, whose invalidation listeners are deferred until the next >>>>>> pulse, with bindings >>>>>> p <- q <- r <- s >>>>>> p <- s >>>>>> and consider changing the value of p in pulse 0. The invalidation >>>>>> listeners will fire as follows: >>>>>> pulse 1: p >>>>>> pulse 2: q, s >>>>>> pulse 3: r >>>>>> pulse 4: s >>>>>> As you see, the listeners of s are called twice, causing potentially >>>>>> expensive recalculation. >>>>>> >>>>>> For these reasons, I'm in favor of the approach suggested by Yennick, >>>>>> where you "commit" the properties yourself. >>>>>> >>>>>> Regards, >>>>>> Tomas >>>>> >>>>> >
