> I don't quite understand how you would want to do this, and also, isn't a 
> SkinInputMap a contradiction?  Skins are about visuals, input is about 
> behavior.

Right, sorry.  More detailed proposal to follow.

-andy


From: John Hendrikx <john.hendr...@gmail.com>
Date: Tuesday, November 28, 2023 at 00:44
To: Andy Goryachev <andy.goryac...@oracle.com>, openjfx-dev@openjdk.org 
<openjfx-dev@openjdk.org>, Michael Strauß <michaelstr...@gmail.com>
Subject: [External] : Re: Looing for feedback: Behavior API Proposal V2
On 27/11/2023 23:02, Andy Goryachev wrote:
Dear John:

Thank you for further development of this idea, especially defining the 
separation between Control, Skin, and Behavior.

Do I read correctly that semantic events are currently off the table?  Or, at 
least, either an implementation detail or some future enhancement?

I think for a first public Behavior API semantic events are not a requirement.  
They were earlier a requirement because we envisioned Skins generating semantic 
events to communicate with Behaviors (in order to cut the Skin <-> Behavior 
dependency), but as that has been solved differently, these events are not in a 
first iteration.

Semantic events would still have value I think as an indirection between 
KeyBinding and its function (see document), unless we go the FunctionTag route 
here.

I also have a few comments in regards to the strict rules e.g. “C. never 
modifies its own publicly writable properties” and the reasoning behind those, 
but that deserves a separate email.

Perhaps I worded this too strict.  The idea is simply that Controls should not 
make unexpected changes to their writable properties as the user must appear to 
be in control at all times. So changing a writable property in response to some 
(user) triggered action (like calling "Button#arm" results in the armed 
property changing) is fine.

For now, I just want to note that BehaviorContext looks suspiciously like an 
InputMap (a skin/behavior InputMap),

But it isn't an InputMap, I explained clearly what its purpose is, to isolate 
the changes that Behaviors makes so Control can remain in control, not only for 
when the event handlers are called (and at what priority) but also at 
uninstallation time.  Behaviors being a public API means that users can start 
doing crazy things, and it would serve us to as much as possible restrict those 
crazy things to a confined space, hence BehaviorContext offering limited 
options to interact with the Control during Behavior installation.

This is pretty standard practice when designing user facing API's: don't offer 
the user options they should not be using, as you'll regret it later when users 
inevitably don't follow the rules that you set out.  Also remember that the 
Behavior implementor may be a different developer than the developer using a 
Control.  The developer using the Control should have some confidence that the 
act of installing a 3rd party Behavior will not adversely affect the Control, 
and that confidence can be severely boosted if the Behavior has limited access.

It indeed also happens to be a good place to offer KeyBinding based methods so 
Behaviors are easy to construct (and again so Control can track changes) does 
not make that an input map; it may *delegate* to some internal (or perhaps 
later public) input map though.

I've done a concession here to the API to allow for KeyBindings so that 
Behaviors can clearly separate a generic KEY_PRESSED handler (which you could 
never remap to do anything else) from things that might be remappable later.
and the fact that you invent a State class indicates that, at least in this 
example, we are dealing with a stateful behavior.  In other words, why not have 
a BehaviorBase?

So the only case where we might have a stateless behavior and thus save a few 
bytes by using a singleton key map is where the control either has no state, or 
the state is fully encapsulated within control’s properties.  I agree we should 
support those (rare?) cases should developers want it.

I think you sort of answered yourself here. However there is IMHO a far more 
compelling reason:

Ease of use. Behaviors can be constants like Colors and Borders. There are no 
checks and balances that need to be done when calling Control#setBehavior() -- 
it just always works, just like setting a color would (no 
ColorAlreadyInUseException :)).

A Behavior does not get "used up" when installed, instead it is "applied", like 
a Color.  The only reason for the complicated construction / install procedure 
of Skins (and a BehaviorBase solution) is an internal one: some state needed to 
be tracked, and unfortunately this internal reason has leaked to be part of 
public API.  From the outside it could and should have worked like a Color or 
Paint, fully reusable.

The intent never was to save a few bytes (at least not in this part).  Of 
course I did recognize pretty early on that the current internal Behavior 
implementation is wasteful (duplicating about 25 kB in KeyBindings and mutable 
InputMaps per TextField instance which are exactly the same), and that the 
Control reference can be gleaned from the callbacks (so callbacks can also be 
fully deduplicated).  This last part I haven't yet fully incorporated, as it is 
a balance to strike between ease of use (getting a Control reference passed to 
the Behavior directly for easy access) and saving memory (making the callback 
reusable by extracting the Control from the Event).  Given that most handlers 
will be KeyBindings, which are lighter weight, it may not be worth it to 
deduplicate the few extra handlers that deal with the mouse or need special key 
handling.

What I am getting at here is that if we provide an InputMap and a SkinInputMap 
instead, then we can have the freedom to implement stateful and stateless 
behaviors as well as provide key mapping functionality as well as the 
prioritization of event handlers (if registered via the input maps).

I don't quite understand how you would want to do this, and also, isn't a 
SkinInputMap a contradiction?  Skins are about visuals, input is about behavior.

The way I see it currently:

1. InputMaps would make sense to live at Control level; the user can examine 
it, and make overrides.  The API is limited enough to allow these InputMaps to 
be deduplicated.  I would not expose it as a MapProperty or anything like that, 
or offer any kind of bindings/listeners/interceptors, certainly not initially.

2. Behaviors provide standard mappings.  Going through BehaviorContext leaves 
all options open for Control to incorporate this into a "final" input map, 
taking user overrides into account.  By not allowing direct modification of the 
input map by Behaviors, Controls can also ensure they know exactly what the 
Behavior did so it can be cleanly uninstalled.

3. Event handler priority (or my alternative) should not be up to Behaviors. A 
Behavior always has the lowest priority, so that a user handler can always 
override what a behavior is doing.  BehaviorContext indirection enforces this, 
as the Control decides how to "install" those handlers, and well behaved 
controls will install them with the lowest priority, and in such a way that 
they never conflict with what the user wants to do.

4. About event handler priorities as API: There is no real point in allowing 
users to install event handlers with a lower priority than Behavior handlers, 
as such handlers would have no use cases (they either always work or never work 
depending on the behavior, if you want it to always work, just install it at a 
higher priority, if you want it to never work... well, don't install it).  This 
is also the reason why I prefer not using handler priorities as part of the API 
-- I think it just complicates things for the user, and there is no compelling 
reason for it to ever work other than "Users first, Behaviors last".

Using handler priorities to solve "internal" ordering of handlers is not needed 
(we've lived without it so far); internal problems can be solved by installing 
handlers in the correct order, or not installing two different handlers for the 
same event in the first place.
--John

Reply via email to