<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=us-ascii">
<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: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;}
/* Style Definitions */
p.MsoNormal, li.MsoNormal, div.MsoNormal
{margin:0in;
font-size:10.0pt;
font-family:"Calibri",sans-serif;}
span.EmailStyle19
{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="#0563C1" vlink="#954F72" style="word-wrap:break-word">
<div class="WordSection1">
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">Thank you, John, for a detailed writeup.<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""><o:p> </o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">Before going into details, I would like to ask you to clarify some of the aspects of the alternative proposal:<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""><o:p> </o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">1. What is the API for setting user event handlers vs. behavior event handlers?<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">2. Is there a way to conditionally invoke the default (behavior) event handler if the user event handler decides to do so?<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">3. How is the key bindings registered / unregistered / reverted to default?<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">4. How is the functions mapped to key bindings are registered / unregistered / reverted?<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">5. Propagating the new events (TextInputControl.SELECT_ALL) up to unsuspecting parents represents a bit of departure, how will that impact all the existing applications? Probably
not much since they will be ignored, with a bit of overhead due to memory allocation, right?<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">6. If I read it right, it is impossible to redefine the behavior of SomeControl.someFunction() except by subclassing, correct?<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">7. I wonder if this will require a more drastic re-write of all the skins?<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""><o:p> </o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">Thank you<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">-andy
<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""><o:p> </o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""><o:p> </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">openjfx-dev <openjfx-dev-retn@openjdk.org> on behalf of John Hendrikx <john.hendrikx@gmail.com><br>
<b>Date: </b>Monday, October 16, 2023 at 04:51<br>
<b>To: </b>openjfx-dev@openjdk.org <openjfx-dev@openjdk.org><br>
<b>Subject: </b>Alternative approach for behaviors, leveraging existing event system<o:p></o:p></span></p>
</div>
<div>
<p class="MsoNormal" style="margin-bottom:12.0pt"><span style="font-size:11.0pt">Hi Andy, hi list,<br>
<br>
I've had the weekend to think about the proposal made by Andy Goryachev <br>
to make some of the API's surrounding InputMap / Behaviors public.<br>
<br>
I'm having some nagging doubts if that proposal is really the way <br>
forward, and I'd like to explore a different approach which leverages <br>
more of FX's existing event infrastructure.<br>
<br>
First, let me repeat an earlier observation; I think event handlers <br>
installed by users should always have priority over handlers installed <br>
by FX behaviors. The reasoning here is that the user (the developer in <br>
this case) should be in control. Just like CSS will back off when the <br>
user changes values directly, so should default behaviors. For this <br>
proposal to have merit, this needs to be addressed.<br>
<br>
One thing that I think Andy's proposal addresses very nicely is the need <br>
for an indirection between low level key and mouse events and their <br>
associated behavior. Depending on the platform, or even platform <br>
configuration, certain keys and mouse events will result in certain high <br>
level actions. Which keys and mouse events is platform specific. A <br>
user wishing to change this behavior should not need to be aware of how <br>
these key and mouse events are mapped to a behavior.<br>
<br>
I however think this can be addressed in a different way, and I will use <br>
the Button control to illustrate this, as it is already doing something <br>
similar out of the box.<br>
<br>
The Button control will trigger itself when a specific combination of <br>
key/mouse events occurs. In theory, a user could install event handlers <br>
to check if the mouse was released over the button, and then perform <br>
some kind of action that the button is supposed to perform. In practice <br>
however, this is tricky, and would require mimicing the whole process to <br>
ensure the mouse was also first **pressed** on that button, if it wasn't <br>
moved outside the clickable area, etc.<br>
<br>
Obviously expecting a user to install the necessary event handlers to <br>
detect button presses based on key and mouse events is a ridiculous <br>
expectation, and so Button offers a much simpler alternative: the <br>
ActionEvent; this is a high level event that encapsulates several other <br>
events, and translates it to a new concept. It is triggered when all <br>
the criteria to fire the button have been met without the user needing <br>
to be aware of what those are.<br>
<br>
I think the strategy of translating low level events to high level <br>
events, is a really good one, and suitable for reusing for other purposes.<br>
<br>
One such purpose is converting platform dependent events into platform <br>
independent ones. Instead of needing to know the exact key press that <br>
would fire a Button, there can be an event that can fire a button. Such <br>
a specific event can be filtered and listened for as usual, it can be <br>
redirected, blocked and it can be triggered by anyone for any reason.<br>
<br>
For a Button, the sequence of events is normally this:<br>
<br>
- User presses SPACE, resulting in a KeyEvent<br>
- Behavior receives KeyEvent and arms the button<br>
- User releases SPACE, resulting in a KeyEvent<br>
- Behavior receives KeyEvent, disarms and fires the button<br>
- Control fires an ActionEvent<br>
<br>
What I'm proposing is to change it to:<br>
<br>
- User presses SPACE, resulting in a KeyEvent<br>
- Behavior receives KeyEvent, and sends out ButtonEvent.BUTTON_ARM<br>
- Control receives BUTTON_ARM, and arms the button<br>
- User releases SPACE, resulting in a KeyEvent<br>
- Behavior receives KeyEvent and sends out ButtonEvent.BUTTON_FIRE<br>
- Control receives BUTTON_FIRE, disarms the button and fires an ActionEvent<br>
<br>
The above basically adds an event based indirection. Normally it is <br>
KeyEvent -> ActionEvent, but now it would be KeyEvent -> ButtonEvent -> <br>
ActionEvent. The user now has the option of hooking into the mechanics <br>
of a Button at several different levels:<br>
<br>
- The "raw" level, listening for raw key/mouse events, useful for <br>
creating custom behavior that can be platform specific<br>
- The "interpreted" level, listening for things like ARM, DISARM, FIRE, <br>
SELECT_NEXT_WORD, SELECT_ALL, etc...; these are platform independent<br>
- The "application" level, primarily action type events<br>
<br>
There is sufficient precedence for such a system. Action events are a <br>
good example, but another example are the DnD events which are created <br>
by looking at raw mouse events, effectively interpreting magic mouse <br>
movements and presses into more useful DnD events.<br>
<br>
The event based indirection here is very similar to the FunctionTag <br>
indirection in Andy's proposal. Instead of FunctionTags, there would be <br>
new events defined:<br>
<br>
ButtonEvent {<br>
public static final EventType<ButtonEvent> ANY = ... ;<br>
public static final EventType<ButtonEvent> BUTTON_ARM = ... ;<br>
public static final EventType<ButtonEvent> BUTTON_DISARM = ... ;<br>
public static final EventType<ButtonEvent> BUTTON_FIRE = ... ;<br>
}<br>
<br>
TextFieldEvent {<br>
public static final EventType<TextFieldEvent> ANY = ... ;<br>
public static final EventType<TextFieldEvent> SELECT_ALL = ... ;<br>
public static final EventType<TextFieldEvent> SELECT_NEXT_WORD <br>
= ... ;<br>
}<br>
<br>
These events are similarly publically accessible and static as <br>
FunctionTags would be.<br>
<br>
The internal Behavior classes would shift from translating + executing a <br>
behavior to only translating it. The Control would be actually <br>
executing the behavior.<br>
<br>
This also simplifies the role of Behaviors, and maybe even clarifies it; <br>
a Behavior's purpose is to translate platform dependent to platform <br>
independent events, but not to act on those events. Acting upon the <br>
events will be squarely the domain of the control. As this pinpoints <br>
better what Behavior's purpose it, and as it simplifies their <br>
implementation (event translation only) it may be the way that leads to <br>
them becoming public as well.<br>
<br>
---<br>
<br>
I've used a similar mechanism as described above in one of my FX <br>
Applications; key bindings are defined in a configuration file:<br>
<br>
BACKSPACE: navigateBack<br>
LEFT: player.position:subtract(10000)<br>
RIGHT: player.position:add(10000)<br>
P: player.paused:toggle<br>
SPACE: player.paused:toggle<br>
I:<br>
- overlayVisible:toggle<br>
- showInfo:trigger<br>
<br>
When the right key is pressed (and it is not consumed by anything), it <br>
is translated to a new higher level event by a generic key binding <br>
system. This event is fired to the same target (the focused node). If <br>
the high level event is consumed, the action was succesfully triggered; <br>
if not, and a key has more than one mapping, another event is sent out <br>
that may get consumed or not. If none of the high level events were <br>
consumed, the low level event that triggered it is allowed to propogate <br>
as usual.<br>
<br>
The advantage of this system is obvious; the controls involved can keep <br>
the action that needs to be performed separate from the exact key (or <br>
something else) that may trigger it. For "navigateBack" for example, it <br>
is also an option to use the mouse; controls need not be aware of this <br>
at all. These events also bubble up; a nested control that has several <br>
states may consume "navigateBack" until it has reached its local "top <br>
level", and only then let it bubble up for one of its parents to act on.<br>
<br>
--John<o:p></o:p></span></p>
</div>
</div>
</div>
</div>
</body>
</html>