On 11/06/2013 07:31 PM, John Hendrikx wrote:
On 5/11/2013 20:10, Jonathan Giles wrote:
You're right in that controls don't tend to use ScenePulseListener. This
may be due to an oversight on our part, or just that our requirements
differ (I don't honestly know).

You're also right that it is important to batch up property changes and
do them once per pulse, rather than as they come in. If this is not done
you do indeed get a severe performance hit (I remember back in the day
before I optimised many of the controls in this way, the performance
impact of not doing this was staggering).

The way I do it is simple: in places where I receive events from
properties / bindings / listeners, where I know that there is
potentially a lot of changes coming through, I create a boolean (e.g.
'refreshView' or somesuch). I then set this boolean to true, and in the
appropriate place in the code (most commonly layoutChildren(), but
sometimes in the computePref*() methods), I start the method with code
like this:

if (refreshView) {
     doExpensiveOperationThatShouldHappenOnlyOncePerPulse();
     refreshView = false;
}

The reason why I sometimes use layoutChildren() and sometimes the
computePref*() methods comes down to the (new) rule in JavaFX 8.0 that
you can not change the children list of a node within the
layoutChildren() method. Because of this, if I need to modify the
children list, I put the above code in the computePref*() method.

I'm not sure in which circumstances layoutChildren() will be called or if I can mark my control as needing layout, since the properties I want to trigger on are not directly attached to anything that JavaFX will be monitoring. The ScenePulseListener therefore seems like a good choice -- the reason I'm asking about is that I cannot find any examples and the documentation for it is also pretty lean.
If you call requestLayout(), the layoutChildren() will be called on the next pulse. The ScenePulseListener is not a public API and the layout is the only way how Controls can react on pulse. Can you be more specific on what you need to do on pulse? If it's not related to the layout, maybe it would be reasonable to introduce some API that would allow you to execute code on each pulse.

-Martin


I'll experiment with a ScenePulseListener and see how it pans out, it looks like it would be an elegant solution.

Thanks Jonathan!



I hope that helps.

-- Jonathan

On 6/11/2013 3:58 a.m., John Hendrikx wrote:
Hi List,

I'm considering using a ScenePulseListener to avoid expensive
recalculations when multiple bindings trigger an action.

My problem is this:

I like to build Views (Controls) that are dumb and expose properties
that can be manipulated by whatever is using the View.  The View
listens to its own Properties and adjusts its state accordingly.  Some
of these adjustments are related (like width + height) and can be
expensive when calculated immediately.  So I would like to mark the
View as "invalid" and recalculate its state (if invalid) on the next
Pulse.

My current use case I'm looking at is a View that wraps (amongst
others) a TreeView.  The View exposes an ObservableList and a
BooleanProperty that decides whether the first level of the Tree
should be displayed as Tabs or as Nodes (which has an impact on what
Nodes actually are added to the TreeView, and which are added as
Tabs).  User code will thus often set a new list of nodes + change the
boolean to show tabs or nodes.  The View currently naively has
InvalidationListeners on both of these properties which cause
TreeNodes to be created after the first change... then discarded and
recreated after the second change by the user code, ie:

   view.nodesProperty().setAll(nodes);   // Recreates all
Tabs/TreeNodes with the current value of expand top level, as we donot
know another change might be incoming...
   view.expandTopLevelProperty().set(false);   // Recreates all
Tabs/TreeNodes again if expand top level was changed...

This specific problem might be done in a better way, but the point
remains, how do I avoid expensive calculations when multiple
properties get changed one after the other by the user code? I'm
assuming that JavaFX controls already avoid these kinds of things, and
I'd like to know whether using a ScenePulseListener is the way to go,
or that it can/should be done in a different way -- examining the code
for TreeView (and its superclasses), I couldn't find uses of
ScenePulseListener...

--John






Reply via email to