Skip to content
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.javafx.event;

import javafx.event.Event;
import javafx.event.EventTarget;

/**
* Provides access to private methods in Event.
*/
public class EventHelper {
static {
// copied from com.sun.javafx.util.Utils in javafx.graphics;
try {
Class<?> c = Event.class;
Class.forName(c.getName(), true, c.getClassLoader());
} catch (ClassNotFoundException e) {
throw new AssertionError(e); // Can't happen
}
}

public interface Accessor {
public void propagateConsume(Event ev);
}

private static Accessor accessor;

private EventHelper() {
}

public static void setAccessor(Accessor a) {
if (accessor != null) {
throw new IllegalStateException();
}
accessor = a;
}

/**
* Causes the {@link Event#consume()} of cloned events to consume the original event.
*
* @param ev the event
*/
public static void propagateConsume(Event ev) {
accessor.propagateConsume(ev);
}
}
44 changes: 41 additions & 3 deletions modules/javafx.base/src/main/java/javafx/event/Event.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,13 @@

package javafx.event;

import java.util.EventObject;

import com.sun.javafx.event.EventUtil;
import java.io.IOException;
import java.util.ArrayList;
import java.util.EventObject;
import java.util.List;
import javafx.beans.NamedArg;
import com.sun.javafx.event.EventHelper;
import com.sun.javafx.event.EventUtil;

// PENDING_DOC_REVIEW
/**
Expand All @@ -54,6 +56,8 @@ public class Event extends EventObject implements Cloneable {
*/
public static final EventType<Event> ANY = EventType.ROOT;

static { initHelper(); }

/**
* Type of the event.
*/
Expand All @@ -70,6 +74,9 @@ public class Event extends EventObject implements Cloneable {
*/
protected boolean consumed;

private transient boolean propagateConsume;
private transient List<Event> consumeListeners;

/**
* Construct a new {@code Event} with the specified event type. The source
* and target of the event is set to {@code NULL_SOURCE_TARGET}.
Expand Down Expand Up @@ -134,6 +141,10 @@ public Event copyFor(final Object newSource, final EventTarget newTarget) {
newEvent.target = (newTarget != null) ? newTarget : NULL_SOURCE_TARGET;
newEvent.consumed = false;

if (propagateConsume) {
newEvent.setNotifyConsumed(this);
}

return newEvent;
}

Expand All @@ -153,6 +164,24 @@ public boolean isConsumed() {
*/
public void consume() {
consumed = true;

// consume parent events when the child is consumed
if (consumeListeners != null) {
List<Event> cs = consumeListeners;
consumeListeners = null;

for (int i = cs.size() - 1; i >= 0; i--) {
Event ev = cs.get(i);
ev.consume();
}
}
}

private void setNotifyConsumed(Event ev) {
if (consumeListeners == null) {
consumeListeners = new ArrayList(1);
}
consumeListeners.add(ev);
}

/**
Expand Down Expand Up @@ -198,4 +227,13 @@ public static void fireEvent(EventTarget eventTarget, Event event) {

EventUtil.fireEvent(eventTarget, event);
}

private static void initHelper() {
EventHelper.setAccessor(new EventHelper.Accessor() {
@Override
public void propagateConsume(Event ev) {
ev.propagateConsume = true;
}
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -156,15 +156,14 @@ public void setTextFieldSkin(TextFieldSkin skin) {

@Override protected void fire(KeyEvent event) {
TextField textField = getNode();
EventHandler<ActionEvent> onAction = textField.getOnAction();
// use textField as target to prevent immediate copy in dispatch
ActionEvent actionEvent = new ActionEvent(textField, textField);

textField.commitValue();
textField.fireEvent(actionEvent);
// fix of JDK-8207759: reverted logic
// mapping not auto-consume and consume if handled by action
if (onAction != null || actionEvent.isConsumed()) {
if (actionEvent.isConsumed()) {
event.consume();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;

import com.sun.javafx.event.EventHelper;
import com.sun.javafx.scene.ParentHelper;
import com.sun.javafx.scene.control.FakeFocusTextField;
import com.sun.javafx.scene.control.ListenerHelper;
Expand Down Expand Up @@ -194,9 +194,15 @@ public void executeAccessibleAction(AccessibleAction action, Object... parameter
// Fix for the regression noted in a comment in RT-29885.
// This forwards the event down into the TextField when
// the key event is actually received by the Spinner.
textField.fireEvent(ke.copyFor(textField, textField));

if (ke.getCode() == KeyCode.ENTER) return;
KeyEvent ev = ke.copyFor(textField, textField);
EventHelper.propagateConsume(ev);
textField.fireEvent(ev);

if (ke.getCode() == KeyCode.ENTER) {
if (!ev.isConsumed()) {
return;
}
}

ke.consume();
}
Expand Down