-
Enhancement
-
Resolution: Unresolved
-
P4
-
None
This is a proposal to add methods for composition to the EventHandler interface:
@FunctionalInterface
interface EventHandler<T extends Event> {
void handle(T event);
default <U extends T> EventHandler<? super U> orElse(EventHandler<? super U> alternative) { ... }
default EventHandler<? super T> without(EventHandler<?> subHandler) { ... }
}
SEMANTICS:
h.orElse(g):
Returns a handler that first applies h to the event. If the event was not consumed by h, then it applies g. (*)
h.without(g):
If h and g are equal, returns a no-op handler.
If h is a composite handler previously created by a.orElse(b), returns a.without(g).orElse(b.without(g)). (**)
Otherwise returns h.
(*) Except when either h or g is a no-op handler, in which case the other one is returned.
(**) Up to optimization that if a.without(g) is a no-op handler, then returns b, or if b.without(g) is a no-op handler, returns a.
USE CASE:
Composition (orElse()):
When control's key bindings are handled by the onKeyPressed handler, handler composition allows to override some of the bindings, while keeping the rest in place:
EventHandler<KeyEvent> myCustomBindings = ...;
EventHandler<? super KeyEvent> originalHandler = getOnKeyPressed();
setOnKeyPressed(myCustomBindings.orElse(originalHandler));
Decomposition (without()):
Allows to remove temporarily added partial handler, while still keeping any additional partial handlers that may have been added afterwards:
h = a;
h = b.orElse(h); // add b temporarily
h = c.orElse(h); // h is now c.orElse(b.orElse(a))
h = h.without(b); // remove b, but keep c
assert h.equals(c.orElse(a));
@FunctionalInterface
interface EventHandler<T extends Event> {
void handle(T event);
default <U extends T> EventHandler<? super U> orElse(EventHandler<? super U> alternative) { ... }
default EventHandler<? super T> without(EventHandler<?> subHandler) { ... }
}
SEMANTICS:
h.orElse(g):
Returns a handler that first applies h to the event. If the event was not consumed by h, then it applies g. (*)
h.without(g):
If h and g are equal, returns a no-op handler.
If h is a composite handler previously created by a.orElse(b), returns a.without(g).orElse(b.without(g)). (**)
Otherwise returns h.
(*) Except when either h or g is a no-op handler, in which case the other one is returned.
(**) Up to optimization that if a.without(g) is a no-op handler, then returns b, or if b.without(g) is a no-op handler, returns a.
USE CASE:
Composition (orElse()):
When control's key bindings are handled by the onKeyPressed handler, handler composition allows to override some of the bindings, while keeping the rest in place:
EventHandler<KeyEvent> myCustomBindings = ...;
EventHandler<? super KeyEvent> originalHandler = getOnKeyPressed();
setOnKeyPressed(myCustomBindings.orElse(originalHandler));
Decomposition (without()):
Allows to remove temporarily added partial handler, while still keeping any additional partial handlers that may have been added afterwards:
h = a;
h = b.orElse(h); // add b temporarily
h = c.orElse(h); // h is now c.orElse(b.orElse(a))
h = h.without(b); // remove b, but keep c
assert h.equals(c.orElse(a));