[External] : [Proposal] Managing event handler properties with EventHandlerManager
Glavo
zjx001202 at gmail.com
Thu Feb 26 08:14:43 UTC 2026
Hi,
I created a PR to optimize the memory footprint of event handler properties
and simplify the code: https://github.com/openjdk/jfx/pull/2088
I'd like to hear everyone's opinions.
## Summary
This PR moves the event handler properties of `Node` and its subclasses to
`EventHandlerManager` for storage.
## Goals
- More lazy allocation of property objects.
- Simplify the way to define event handler properties.
- Maintain consistency between the actual event handler value and the
property value.
## Motivation
In JavaFX, many controls define event handler properties.
For example, `ButtonBase` defines an `onAction` property:
```java
public final ObjectProperty<EventHandler<ActionEvent>> onActionProperty() {
return onAction; }
public final void setOnAction(EventHandler<ActionEvent> value) {
onActionProperty().set(value); }
public final EventHandler<ActionEvent> getOnAction() { return
onActionProperty().get(); }
private ObjectProperty<EventHandler<ActionEvent>> onAction = new
ObjectPropertyBase<>() {
@Override protected void invalidated() {
setEventHandler(ActionEvent.ACTION, get());
}
@Override
public Object getBean() {
return ButtonBase.this;
}
@Override
public String getName() {
return "onAction";
}
};
```
However, there are several issues with defining properties in this way.
### Unnecessary allocation of `ObjectProperty` objects
Users often call the `setOnAction` method to set the event handler, but
rarely call `onActionProperty()` to add listeners or bind it to other
values.
The actual effect of `setOnAction` is to call
`EventHandlerManager#setEventHandler(...)` to install the event handler in
the `EventHandlerManager`, and the installed event handler can also be
retrieved by calling `EventHandlerManager#getEventHandler(...)`.
Therefore, allocating the `ObjectProperty` object before the user
explicitly calls `onActionProperty()` wastes memory and serves no real
purpose. Most of these objects can be optimized away.
### The property value and EventHandlerManager may be out of sync
Consider the following code:
```java
EventHandler<ActionEvent> handler = event -> IO.println("On Action");
var button = new Button() {
{
setOnAction(handler);
setEventHandler(ActionEvent.ACTION, null);
}
};
assert button.getOnAction() == handler;
button.fire(); // Nothing happened
```
As you can see, the handler obtained by `getOnAction()` may not be
triggered,
because the value of the `onActionProperty()` property is actually not
associated with the handler stored in the `EventHandlerManager`.
Users can override the value of `onActionProperty()` by calling
`setEventHandler(...)`, which may lead to unexpected behavior.
### Defining event handler properties is verbose and cumbersome
Each time a user defines an event handler property, they need to declare a
subclass of `ObjectPropertyBase` and repeat some boilerplate code.
This is cumbersome to write and prone to errors.
## Description
This PR adds the following APIs to the `Node` class:
```java
protected final <T extends Event> EventHandler<? super T> getEventHandler(
final EventType<T> eventType) {
return
getInternalEventDispatcher().getEventHandlerManager().getEventHandler(eventType);
}
protected final <T extends Event> ObjectProperty<EventHandler<? super T>>
eventHandlerProperty(
final EventType<T> eventType,
final String name) {
return getInternalEventDispatcher().getEventHandlerManager()
.eventHandlerProperty(eventType,
name);
}
```
After this, defining event handler properties in the `Node` class and its
subclasses only requires the following:
```java
public final void setOnMouseClicked(EventHandler<? super MouseEvent> value)
{
setEventHandler(MouseEvent.MOUSE_CLICKED, value);
}
public final EventHandler<? super MouseEvent> getOnMouseClicked() {
return getEventHandler(MouseEvent.MOUSE_CLICKED);
}
public final ObjectProperty<EventHandler<? super MouseEvent>>
onMouseClickedProperty() {
return eventHandlerProperty(MouseEvent.MOUSE_CLICKED, "onMouseClicked");
}
```
In this way, calling `setOnMouseClicked(...)` will not invoke
`onMouseClickedProperty()`, and the corresponding `ObjectProperty` will not
be allocated.
The `ObjectProperty` will only be allocated and stored inside the
`EventHandlerManager` when the user explicitly calls
`onMouseClickedProperty()`.
The three methods `eventHandlerProperty(...)`, `getEventHandler(...)`, and
`setEventHandler(...)` follow the JavaFX property conventions.
Calling `setEventHandler(...)` will also trigger listeners added to
`eventHandlerProperty(...)`.
## Risks and Assumptions
This PR adds `getEventHandler(...)` and `eventHandlerProperty(...)` methods
to the `Node` class.
If a user defines methods with the same signatures in a subclass of `Node`,
the subclass will fail JVM verification.
This PR changes the behavior of the `Node#setEventHandler(...)` method.
Now it's behavior is equivalent to
`node.eventHandlerProperty(...).set(...)`.
Calling it will affect the corresponding property value, trigger listeners
added to the property, and throw an exception if the property is bound.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/openjfx-dev/attachments/20260226/a0c0cec3/attachment.htm>
More information about the openjfx-dev
mailing list