[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