[prev in list] [next in list] [prev in thread] [next in thread] 

List:       openjdk-openjfx-dev
Subject:    Re: [External] : Re: Looing for feedback: Behavior API Proposal V2
From:       Andy Goryachev <andy.goryachev () oracle ! com>
Date:       2023-11-28 16:53:09
Message-ID: DM5PR1001MB217298419BFD9E236C2007C0E5BCA () DM5PR1001MB2172 ! namprd10 ! prod ! outlook ! com
[Download RAW message or body]

> 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 beh=
avior.

Right, sorry.  More detailed proposal to follow.

-andy


From: John Hendrikx <john.hendrikx@gmail.com>
Date: Tuesday, November 28, 2023 at 00:44
To: Andy Goryachev <andy.goryachev@oracle.com>, openjfx-dev@openjdk.org <op=
enjfx-dev@openjdk.org>, Michael Strau=DF <michaelstrau2@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 sep=
aration 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 requireme=
nt.  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 even=
ts are not in a first iteration.

Semantic events would still have value I think as an indirection between Ke=
yBinding and its function (see document), unless we go the FunctionTag rout=
e here.

I also have a few comments in regards to the strict rules e.g. =93C. never =
modifies its own publicly writable properties=94 and the reasoning behind t=
hose, 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 a=
ppear to be in control at all times. So changing a writable property in res=
ponse 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 a=
n InputMap (a skin/behavior InputMap),

But it isn't an InputMap, I explained clearly what its purpose is, to isola=
te the changes that Behaviors makes so Control can remain in control, not o=
nly 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 rest=
rict 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 of=
fer the user options they should not be using, as you'll regret it later wh=
en users inevitably don't follow the rules that you set out.  Also remember=
 that the Behavior implementor may be a different developer than the develo=
per using a Control.  The developer using the Control should have some conf=
idence that the act of installing a 3rd party Behavior will not adversely a=
ffect the Control, and that confidence can be severely boosted if the Behav=
ior 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 p=
erhaps later public) input map though.

I've done a concession here to the API to allow for KeyBindings so that Beh=
aviors 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 f=
ew bytes by using a singleton key map is where the control either has no st=
ate, or the state is fully encapsulated within control=92s properties.  I a=
gree we should support those (rare?) cases should developers want it.

I think you sort of answered yourself here. However there is IMHO a far mor=
e 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#setBehavio=
r() -- it just always works, just like setting a color would (no ColorAlrea=
dyInUseException :)).

A Behavior does not get "used up" when installed, instead it is "applied", =
like a Color.  The only reason for the complicated construction / install p=
rocedure of Skins (and a BehaviorBase solution) is an internal one: some st=
ate 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 worke=
d like a Color or Paint, fully reusable.

The intent never was to save a few bytes (at least not in this part).  Of c=
ourse I did recognize pretty early on that the current internal Behavior im=
plementation is wasteful (duplicating about 25 kB in KeyBindings and mutabl=
e InputMaps per TextField instance which are exactly the same), and that th=
e Control reference can be gleaned from the callbacks (so callbacks can als=
o 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 refere=
nce passed to the Behavior directly for easy access) and saving memory (mak=
ing the callback reusable by extracting the Control from the Event).  Given=
 that most handlers will be KeyBindings, which are lighter weight, it may n=
ot be worth it to deduplicate the few extra handlers that deal with the mou=
se or need special key handling.

What I am getting at here is that if we provide an InputMap and a SkinInput=
Map instead, then we can have the freedom to implement stateful and statele=
ss behaviors as well as provide key mapping functionality as well as the pr=
ioritization 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 S=
kinInputMap a contradiction?  Skins are about visuals, input is about behav=
ior.

The way I see it currently:

1. InputMaps would make sense to live at Control level; the user can examin=
e it, and make overrides.  The API is limited enough to allow these InputMa=
ps 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 leav=
es all options open for Control to incorporate this into a "final" input ma=
p, 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 alw=
ays override what a behavior is doing.  BehaviorContext indirection enforce=
s this, as the Control decides how to "install" those handlers, and well be=
haved controls will install them with the lowest priority, and in such a wa=
y that they never conflict with what the user wants to do.

4. About event handler priorities as API: There is no real point in allowin=
g users to install event handlers with a lower priority than Behavior handl=
ers, as such handlers would have no use cases (they either always work or n=
ever work depending on the behavior, if you want it to always work, just in=
stall 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 priori=
ties 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 fi=
rst, Behaviors last".

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


[Attachment #3 (text/html)]

<html xmlns:o="urn:schemas-microsoft-com:office:office" \
xmlns:w="urn:schemas-microsoft-com:office:word" \
xmlns:m="http://schemas.microsoft.com/office/2004/12/omml" \
xmlns="http://www.w3.org/TR/REC-html40"> <head>
<meta http-equiv="Content-Type" content="text/html; charset=Windows-1252">
<meta name="Generator" content="Microsoft Word 15 (filtered medium)">
<style><!--
/* Font Definitions */
@font-face
	{font-family:"Cambria Math";
	panose-1:2 4 5 3 5 4 6 3 2 4;}
@font-face
	{font-family:"Yu Gothic";
	panose-1:2 11 4 0 0 0 0 0 0 0;}
@font-face
	{font-family:Calibri;
	panose-1:2 15 5 2 2 2 4 3 2 4;}
@font-face
	{font-family:"Iosevka Fixed SS16";
	panose-1:2 0 5 9 3 0 0 0 0 4;}
@font-face
	{font-family:"Times New Roman \(Body CS\)";
	panose-1:2 11 6 4 2 2 2 2 2 4;}
@font-face
	{font-family:"\@Yu Gothic";
	panose-1:2 11 4 0 0 0 0 0 0 0;}
@font-face
	{font-family:"Iosevka Fixed SS16 ";
	panose-1:2 0 5 9 3 0 0 0 0 4;}
/* Style Definitions */
p.MsoNormal, li.MsoNormal, div.MsoNormal
	{margin:0in;
	font-size:10.0pt;
	font-family:"Calibri",sans-serif;}
span.EmailStyle20
	{mso-style-type:personal-reply;
	font-family:"Iosevka Fixed SS16";
	color:windowtext;}
.MsoChpDefault
	{mso-style-type:export-only;
	font-size:10.0pt;
	mso-ligatures:none;}
@page WordSection1
	{size:8.5in 11.0in;
	margin:1.0in 1.0in 1.0in 1.0in;}
div.WordSection1
	{page:WordSection1;}
--></style>
</head>
<body lang="EN-US" link="blue" vlink="purple" style="word-wrap:break-word">
<div class="WordSection1">
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:&quot;Iosevka Fixed \
SS16&quot;">&gt; </span><span style="font-size:12.0pt;color:#212121">I don't quite \
understand how you would want to do this, and also, isn't a SkinInputMap a \
contradiction?&nbsp; Skins are about visuals, input is about behavior.</span><span \
style="font-size:11.0pt;font-family:&quot;Iosevka Fixed \
SS16&quot;"><o:p></o:p></span></p> <p class="MsoNormal"><span \
style="font-size:11.0pt;font-family:&quot;Iosevka Fixed \
SS16&quot;"><o:p>&nbsp;</o:p></span></p> <p class="MsoNormal"><span \
style="font-size:11.0pt;font-family:&quot;Iosevka Fixed SS16&quot;">Right, \
sorry.&nbsp; More detailed proposal to follow.<o:p></o:p></span></p> <p \
class="MsoNormal"><span style="font-size:11.0pt;font-family:&quot;Iosevka Fixed \
SS16&quot;"><o:p>&nbsp;</o:p></span></p> <p class="MsoNormal"><span \
style="font-size:11.0pt;font-family:&quot;Iosevka Fixed \
SS16&quot;">-andy<o:p></o:p></span></p> <p class="MsoNormal"><span \
style="font-size:11.0pt;font-family:&quot;Iosevka Fixed \
SS16&quot;"><o:p>&nbsp;</o:p></span></p> <p class="MsoNormal"><span \
style="font-size:11.0pt;font-family:&quot;Iosevka Fixed \
SS16&quot;"><o:p>&nbsp;</o:p></span></p> <div \
id="mail-editor-reference-message-container"> <div>
<div style="border:none;border-top:solid #B5C4DF 1.0pt;padding:3.0pt 0in 0in 0in">
<p class="MsoNormal" style="margin-bottom:12.0pt"><b><span \
style="font-size:12.0pt;color:black">From: </span></b><span \
style="font-size:12.0pt;color:black">John Hendrikx \
&lt;john.hendrikx@gmail.com&gt;<br> <b>Date: </b>Tuesday, November 28, 2023 at \
00:44<br> <b>To: </b>Andy Goryachev &lt;andy.goryachev@oracle.com&gt;, \
openjfx-dev@openjdk.org &lt;openjfx-dev@openjdk.org&gt;, Michael Strauß \
&lt;michaelstrau2@gmail.com&gt;<br> <b>Subject: </b>[External] : Re: Looing for \
feedback: Behavior API Proposal V2<o:p></o:p></span></p> </div>
<div>
<p class="MsoNormal"><span style="font-size:11.0pt">On 27/11/2023 23:02, Andy \
Goryachev wrote:<o:p></o:p></span></p> </div>
<blockquote style="margin-top:5.0pt;margin-bottom:5.0pt">
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:&quot;Iosevka Fixed \
SS16 &quot;">Dear John:</span><o:p></o:p></p> <p class="MsoNormal"><span \
style="font-size:11.0pt;font-family:&quot;Iosevka Fixed SS16 \
&quot;">&nbsp;</span><o:p></o:p></p> <p class="MsoNormal"><span \
style="font-size:11.0pt;font-family:&quot;Iosevka Fixed SS16 &quot;">Thank you for \
further development of this idea, especially defining the separation between Control, \
Skin, and Behavior.</span><o:p></o:p></p> <p class="MsoNormal"><span \
style="font-size:11.0pt;font-family:&quot;Iosevka Fixed SS16 \
&quot;">&nbsp;</span><o:p></o:p></p> <p class="MsoNormal"><span \
style="font-size:11.0pt;font-family:&quot;Iosevka Fixed SS16 &quot;">Do I read \
correctly that semantic events are currently off the table?&nbsp; Or, at least, \
either an implementation detail or some future enhancement?</span><o:p></o:p></p> \
</blockquote> <p>I think for a first public Behavior API semantic events are not a \
requirement.&nbsp; They were earlier a requirement because we envisioned Skins \
generating semantic events to communicate with Behaviors (in order to cut the Skin \
&lt;-&gt; Behavior dependency), but as  that has been solved differently, these \
events are not in a first iteration.<o:p></o:p></p> <p>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.<o:p></o:p></p> <blockquote \
style="margin-top:5.0pt;margin-bottom:5.0pt"> <p class="MsoNormal"><span \
style="font-size:11.0pt;font-family:&quot;Iosevka Fixed SS16 \
&quot;">&nbsp;</span><o:p></o:p></p> <p class="MsoNormal"><span \
style="font-size:11.0pt;font-family:&quot;Iosevka Fixed SS16 &quot;">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.</span><o:p></o:p></p> </blockquote>
<p>Perhaps I worded this too strict.&nbsp; 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 &quot;Button#arm&quot; results in the armed \
property changing) is fine.&nbsp; <o:p></o:p></p>
<blockquote style="margin-top:5.0pt;margin-bottom:5.0pt">
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:&quot;Iosevka Fixed \
SS16 &quot;">&nbsp;</span><o:p></o:p></p> <p class="MsoNormal"><span \
style="font-size:11.0pt;font-family:&quot;Iosevka Fixed SS16 &quot;">For now, I just \
want to note that BehaviorContext looks suspiciously like an InputMap (a \
skin/behavior InputMap), </span><o:p></o:p></p>
</blockquote>
<p>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.&nbsp; 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.<o:p></o:p></p> <p>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.&nbsp; Also remember that the Behavior implementor  may be a \
different developer than the developer using a Control.&nbsp; 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.<o:p></o:p></p> <p>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.<o:p></o:p></p> <p>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.<o:p></o:p></p> <blockquote \
style="margin-top:5.0pt;margin-bottom:5.0pt"> <p class="MsoNormal"><span \
style="font-size:11.0pt;font-family:&quot;Iosevka Fixed SS16 &quot;">and the fact \
that you invent a State class indicates that, at least in this example, we are \
dealing with a stateful behavior.&nbsp; In other words, why not have a \
BehaviorBase?</span><o:p></o:p></p> </blockquote>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:&quot;Iosevka Fixed \
SS16 &quot;">&nbsp;</span><span style="font-size:11.0pt"> <o:p></o:p></span></p>
<blockquote style="margin-top:5.0pt;margin-bottom:5.0pt">
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:&quot;Iosevka Fixed \
SS16 &quot;">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.&nbsp; I agree we should \
support those (rare?) cases should developers want it.</span><o:p></o:p></p> \
</blockquote> <p>I think you sort of answered yourself here. However there is IMHO a \
far more compelling reason:<o:p></o:p></p> <p>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 :)).&nbsp; <o:p></o:p></p>
<p>A Behavior does not get &quot;used up&quot; when installed, instead it is \
&quot;applied&quot;, like a Color.&nbsp; 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.&nbsp; From the outside it could and \
should have worked like a Color or Paint, fully reusable.<o:p></o:p></p> <p>The \
intent never was to save a few bytes (at least not in this part).&nbsp; 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).&nbsp; 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).&nbsp; 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.<o:p></o:p></p> <blockquote \
style="margin-top:5.0pt;margin-bottom:5.0pt"> <p class="MsoNormal"><span \
style="font-size:11.0pt;font-family:&quot;Iosevka Fixed SS16 \
&quot;">&nbsp;</span><o:p></o:p></p> <p class="MsoNormal"><span \
style="font-size:11.0pt;font-family:&quot;Iosevka Fixed SS16 &quot;">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).</span><o:p></o:p></p> </blockquote>
<p>I don't quite understand how you would want to do this, and also, isn't a \
SkinInputMap a contradiction?&nbsp; Skins are about visuals, input is about \
behavior.<o:p></o:p></p> <p>The way I see it currently:<o:p></o:p></p>
<p>1. InputMaps would make sense to live at Control level; the user can examine it, \
and make overrides.&nbsp; The API is limited enough to allow these InputMaps to be \
deduplicated.&nbsp; I would not expose it as a MapProperty or anything like that, or \
offer any kind  of bindings/listeners/interceptors, certainly not \
initially.<o:p></o:p></p> <p>2. Behaviors provide standard mappings.&nbsp; Going \
through BehaviorContext leaves all options open for Control to incorporate this into \
a &quot;final&quot; input map, taking user overrides into account.&nbsp; 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.<o:p></o:p></p> <p>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.&nbsp; BehaviorContext \
indirection enforces this, as the Control decides  how to &quot;install&quot; 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.<o:p></o:p></p> \
<p>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).&nbsp; 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 &quot;Users first, Behaviors last&quot;.<o:p></o:p></p> <p>Using \
handler priorities to solve &quot;internal&quot; 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.<o:p></o:p></p>
<p class="MsoNormal"><span style="font-size:11.0pt">--John<br>
<br>
<o:p></o:p></span></p>
</div>
</div>
</div>
</body>
</html>



[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic