diff --git a/apps/samples/RichTextAreaDemo/.classpath b/apps/samples/RichTextAreaDemo/.classpath index 713e0b6f9e3..5ca872a8e7e 100644 --- a/apps/samples/RichTextAreaDemo/.classpath +++ b/apps/samples/RichTextAreaDemo/.classpath @@ -57,10 +57,5 @@ - - - - - diff --git a/apps/samples/RichTextAreaDemo/build.xml b/apps/samples/RichTextAreaDemo/build.xml index 9ce624c1ae4..21b733c4321 100644 --- a/apps/samples/RichTextAreaDemo/build.xml +++ b/apps/samples/RichTextAreaDemo/build.xml @@ -39,7 +39,7 @@ unless built as a part of openjfx repository where the default value is sufficie includeantruntime="false" > - + @@ -77,28 +77,28 @@ unless built as a part of openjfx repository where the default value is sufficie - + - + - + - + diff --git a/apps/samples/RichTextAreaDemo/src/com/oracle/demo/richtext/editor/RichEditorDemoPane.java b/apps/samples/RichTextAreaDemo/src/com/oracle/demo/richtext/editor/RichEditorDemoPane.java index 10a9375f845..c9d0978f6c4 100644 --- a/apps/samples/RichTextAreaDemo/src/com/oracle/demo/richtext/editor/RichEditorDemoPane.java +++ b/apps/samples/RichTextAreaDemo/src/com/oracle/demo/richtext/editor/RichEditorDemoPane.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2024, Oracle and/or its affiliates. + * Copyright (c) 2023, 2025, Oracle and/or its affiliates. * All rights reserved. Use is subject to license terms. * * This file is available and licensed under the following license: @@ -37,13 +37,13 @@ import javafx.scene.control.ComboBox; import javafx.scene.control.ContextMenu; import javafx.scene.control.ToolBar; +import javafx.scene.control.input.KeyBinding; import javafx.scene.input.KeyCode; import javafx.scene.layout.BorderPane; import javafx.scene.text.Font; import com.oracle.demo.richtext.common.TextStyle; import com.oracle.demo.richtext.editor.settings.EndKey; import com.oracle.demo.richtext.util.FX; -import jfx.incubator.scene.control.input.KeyBinding; import jfx.incubator.scene.control.richtext.RichTextArea; import jfx.incubator.scene.control.richtext.TextPos; diff --git a/apps/samples/RichTextAreaDemo/src/com/oracle/demo/richtext/rta/RichTextAreaDemoPane.java b/apps/samples/RichTextAreaDemo/src/com/oracle/demo/richtext/rta/RichTextAreaDemoPane.java index f69a5109699..41fcc289834 100644 --- a/apps/samples/RichTextAreaDemo/src/com/oracle/demo/richtext/rta/RichTextAreaDemoPane.java +++ b/apps/samples/RichTextAreaDemo/src/com/oracle/demo/richtext/rta/RichTextAreaDemoPane.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2024, Oracle and/or its affiliates. + * Copyright (c) 2023, 2025, Oracle and/or its affiliates. * All rights reserved. Use is subject to license terms. * * This file is available and licensed under the following license: @@ -57,6 +57,7 @@ import javafx.scene.control.ScrollPane.ScrollBarPolicy; import javafx.scene.control.SeparatorMenuItem; import javafx.scene.control.SplitPane; +import javafx.scene.control.input.KeyBinding; import javafx.scene.input.Clipboard; import javafx.scene.input.DataFormat; import javafx.scene.input.KeyCode; @@ -69,7 +70,6 @@ import javafx.util.Duration; import javafx.util.StringConverter; import com.oracle.demo.richtext.util.FX; -import jfx.incubator.scene.control.input.KeyBinding; import jfx.incubator.scene.control.richtext.LineNumberDecorator; import jfx.incubator.scene.control.richtext.RichTextArea; import jfx.incubator.scene.control.richtext.SideDecorator; diff --git a/apps/samples/RichTextAreaDemo/src/com/oracle/demo/richtext/rta/UsageExamples.java b/apps/samples/RichTextAreaDemo/src/com/oracle/demo/richtext/rta/UsageExamples.java index 7d9d3b1fc00..8a165bcec22 100644 --- a/apps/samples/RichTextAreaDemo/src/com/oracle/demo/richtext/rta/UsageExamples.java +++ b/apps/samples/RichTextAreaDemo/src/com/oracle/demo/richtext/rta/UsageExamples.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2024, Oracle and/or its affiliates. + * Copyright (c) 2023, 2025, Oracle and/or its affiliates. * All rights reserved. Use is subject to license terms. * * This file is available and licensed under the following license: @@ -34,10 +34,10 @@ import javafx.application.Application; import javafx.scene.Scene; +import javafx.scene.control.input.FunctionTag; +import javafx.scene.control.input.KeyBinding; import javafx.scene.input.KeyCode; import javafx.stage.Stage; -import jfx.incubator.scene.control.input.FunctionTag; -import jfx.incubator.scene.control.input.KeyBinding; import jfx.incubator.scene.control.richtext.CodeArea; import jfx.incubator.scene.control.richtext.LineNumberDecorator; import jfx.incubator.scene.control.richtext.RichTextArea; diff --git a/apps/samples/RichTextAreaDemo/src/module-info.java b/apps/samples/RichTextAreaDemo/src/module-info.java index 266c9b3716f..2781513b80c 100644 --- a/apps/samples/RichTextAreaDemo/src/module-info.java +++ b/apps/samples/RichTextAreaDemo/src/module-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2024, Oracle and/or its affiliates. + * Copyright (c) 2023, 2025, Oracle and/or its affiliates. * All rights reserved. Use is subject to license terms. * * This file is available and licensed under the following license: @@ -48,6 +48,5 @@ requires javafx.base; requires javafx.controls; requires javafx.graphics; - requires jfx.incubator.input; requires jfx.incubator.richtext; } diff --git a/build.gradle b/build.gradle index 20543ba1f29..4f19c3fdd38 100644 --- a/build.gradle +++ b/build.gradle @@ -2840,62 +2840,6 @@ project(":controls") { //} // END: incubator placeholder -project(":incubator.input") { - project.ext.buildModule = true - project.ext.includeSources = true - project.ext.moduleRuntime = true - project.ext.moduleName = "jfx.incubator.input" - project.ext.incubating = true - - sourceSets { - main - shims { - java { - compileClasspath += sourceSets.main.output - runtimeClasspath += sourceSets.main.output - } - } - test { - java { - compileClasspath += sourceSets.shims.output - runtimeClasspath += sourceSets.shims.output - } - } - } - - project.ext.moduleSourcePath = defaultModuleSourcePath - project.ext.moduleSourcePathShim = defaultModuleSourcePathShim - - commonModuleSetup(project, [ - 'base', - 'graphics', - 'controls', - 'incubator.input' - ]) - - dependencies { - testImplementation project(":base").sourceSets.test.output - testImplementation project(":graphics").sourceSets.test.output - testImplementation project(":controls").sourceSets.test.output - implementation project(':base') - implementation project(':graphics') - implementation project(':controls') - } - - test { - jvmArgs "-Djavafx.toolkit=test.com.sun.javafx.pgstub.StubToolkit" - } - - def modulePath = "${project.sourceSets.main.java.getDestinationDirectory().get().getAsFile()}" - modulePath += File.pathSeparator + "${rootProject.projectDir}/modules/javafx.controls/build/classes/java/main" - modulePath += File.pathSeparator + "${rootProject.projectDir}/modules/javafx.graphics/build/classes/java/main" - modulePath += File.pathSeparator + "${rootProject.projectDir}/modules/javafx.base/build/classes/java/main" - - addMavenPublication(project, [ 'graphics' , 'controls']) - - addValidateSourceSets(project, sourceSets) -} - project(":incubator.richtext") { project.ext.buildModule = true project.ext.includeSources = true @@ -2926,7 +2870,6 @@ project(":incubator.richtext") { 'base', 'graphics', 'controls', - 'incubator.input', 'incubator.richtext' ]) @@ -2934,11 +2877,9 @@ project(":incubator.richtext") { testImplementation project(":base").sourceSets.test.output testImplementation project(":graphics").sourceSets.test.output testImplementation project(":controls").sourceSets.test.output - testImplementation project(":incubator.input").sourceSets.test.output implementation project(':base') implementation project(':graphics') implementation project(':controls') - implementation project(':incubator.input') } test { @@ -4154,7 +4095,6 @@ project(":systemTests") { // BEGIN: incubator placeholder //'incubator.mymod', // END: incubator placeholder - 'incubator.input', 'incubator.richtext', 'media', 'jsobject', @@ -4618,7 +4558,6 @@ task javadoc(type: Javadoc, dependsOn: createMSPfile) { // BEGIN: incubator placeholder //project(":incubator.mymod"), // END: incubator placeholder - project(":incubator.input"), project(":incubator.richtext"), project(":media"), project(":swing"), diff --git a/modules/javafx.base/src/main/java/module-info.java b/modules/javafx.base/src/main/java/module-info.java index 5732c33fdf5..e9b5de52b7d 100644 --- a/modules/javafx.base/src/main/java/module-info.java +++ b/modules/javafx.base/src/main/java/module-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, 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 @@ -52,7 +52,6 @@ // BEGIN: incubator placeholder //jfx.incubator.mymod, // END: incubator placeholder - jfx.incubator.input, jfx.incubator.richtext, javafx.graphics, javafx.fxml, diff --git a/modules/javafx.controls/src/main/java/com/sun/javafx/scene/control/behavior/ColorPickerBehavior.java b/modules/javafx.controls/src/main/java/com/sun/javafx/scene/control/behavior/ColorPickerBehavior.java index be6cde212f4..2c05913b9b5 100644 --- a/modules/javafx.controls/src/main/java/com/sun/javafx/scene/control/behavior/ColorPickerBehavior.java +++ b/modules/javafx.controls/src/main/java/com/sun/javafx/scene/control/behavior/ColorPickerBehavior.java @@ -27,7 +27,6 @@ import javafx.scene.control.ColorPicker; import javafx.scene.control.PopupControl; - import javafx.scene.paint.Color; public class ColorPickerBehavior extends ComboBoxBaseBehavior { @@ -41,8 +40,8 @@ public class ColorPickerBehavior extends ComboBoxBaseBehavior { /** * */ - public ColorPickerBehavior(final ColorPicker colorPicker) { - super(colorPicker); + public ColorPickerBehavior(ColorPicker c) { + super(c); } /************************************************************************** @@ -54,14 +53,14 @@ public ColorPickerBehavior(final ColorPicker colorPicker) { @Override public void onAutoHide(PopupControl popup) { // when we click on some non interactive part of the // Color Palette - we do not want to hide. - if (!popup.isShowing() && getNode().isShowing()) { + if (!popup.isShowing() && getControl().isShowing()) { // Popup was dismissed. Maybe user clicked outside or typed ESCAPE. // Make sure DatePicker button is in sync. - getNode().hide(); + getControl().hide(); } // if the ColorPicker is no longer showing, then invoke the super method // to keep its show/hide state in sync. - if (!getNode().isShowing()) { + if (!getControl().isShowing()) { super.onAutoHide(popup); } } diff --git a/modules/javafx.controls/src/main/java/com/sun/javafx/scene/control/behavior/ComboBoxBaseBehavior.java b/modules/javafx.controls/src/main/java/com/sun/javafx/scene/control/behavior/ComboBoxBaseBehavior.java index 9bab4cc2936..5ef40224130 100644 --- a/modules/javafx.controls/src/main/java/com/sun/javafx/scene/control/behavior/ComboBoxBaseBehavior.java +++ b/modules/javafx.controls/src/main/java/com/sun/javafx/scene/control/behavior/ComboBoxBaseBehavior.java @@ -25,11 +25,8 @@ package com.sun.javafx.scene.control.behavior; -import com.sun.javafx.scene.control.inputmap.InputMap; - import javafx.beans.InvalidationListener; import javafx.beans.Observable; -import javafx.event.EventHandler; import javafx.event.EventTarget; import javafx.scene.Node; import javafx.scene.control.ComboBox; @@ -37,18 +34,16 @@ import javafx.scene.control.DatePicker; import javafx.scene.control.PopupControl; import javafx.scene.control.TextField; +import javafx.scene.control.input.BehaviorBase; +import javafx.scene.control.input.KeyBinding; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyEvent; +import javafx.scene.input.MouseButton; +import javafx.scene.input.MouseEvent; import com.sun.javafx.scene.control.skin.Utils; -import javafx.scene.input.*; -import com.sun.javafx.scene.control.inputmap.KeyBinding; - -import static javafx.scene.input.KeyCode.*; -import static javafx.scene.input.KeyEvent.*; -import static com.sun.javafx.scene.control.inputmap.InputMap.KeyMapping; -import static com.sun.javafx.scene.control.inputmap.InputMap.MouseMapping; public class ComboBoxBaseBehavior extends BehaviorBase> { - private final InputMap> inputMap; private InvalidationListener focusListener = this::focusChanged; /*************************************************************************** @@ -62,65 +57,47 @@ public class ComboBoxBaseBehavior extends BehaviorBase> { /** * */ - public ComboBoxBaseBehavior(final ComboBoxBase comboBox) { - super(comboBox); - - // create a map for comboBox-specific mappings (this reuses the default - // InputMap installed on the control, if it is non-null, allowing us to pick up any user-specified mappings) - inputMap = createInputMap(); - - final EventHandler togglePopup = e -> { - // If popup is shown, KeyEvent causes popup to close - showPopupOnMouseRelease = true; - - if (getNode().isShowing()) hide(); - else show(); - }; + public ComboBoxBaseBehavior(ComboBoxBase c) { + super(c); + } - // comboBox-specific mappings for key and mouse input - KeyMapping enterPressed, enterReleased; - addDefaultMapping(inputMap, - new KeyMapping(F4, KEY_RELEASED, togglePopup), - new KeyMapping(new KeyBinding(UP).alt(), togglePopup), - new KeyMapping(new KeyBinding(DOWN).alt(), togglePopup), + @Override + protected void populateSkinInputMap() { + // ComboBoxBase also cares about focus + getControl().focusedProperty().addListener(focusListener); - new KeyMapping(SPACE, KEY_PRESSED, this::keyPressed), - new KeyMapping(SPACE, KEY_RELEASED, this::keyReleased), + // Only add this if we're on an embedded platform that supports 5-button navigation + if (Utils.isTwoLevelFocus()) { + tlFocus = new TwoLevelFocusComboBehavior(getControl()); // needs to be last. + } - enterPressed = new KeyMapping(ENTER, KEY_PRESSED, this::keyPressed), - enterReleased = new KeyMapping(ENTER, KEY_RELEASED, this::keyReleased), + registerFunction(ComboBoxBase.TOGGLE_POPUP, this::togglePopup); - // The following keys are forwarded to the parent container - new KeyMapping(ESCAPE, KEY_PRESSED, this::cancelEdit), - new KeyMapping(F10, KEY_PRESSED, this::forwardToParent), + registerKey(KeyBinding.builder(KeyCode.F4).keyReleased().build(), ComboBoxBase.TOGGLE_POPUP); + registerKey(KeyBinding.alt(KeyCode.DOWN), ComboBoxBase.TOGGLE_POPUP); + registerKey(KeyBinding.alt(KeyCode.UP), ComboBoxBase.TOGGLE_POPUP); - new MouseMapping(MouseEvent.MOUSE_PRESSED, this::mousePressed), - new MouseMapping(MouseEvent.MOUSE_RELEASED, this::mouseReleased), - new MouseMapping(MouseEvent.MOUSE_ENTERED, this::mouseEntered), - new MouseMapping(MouseEvent.MOUSE_EXITED, this::mouseExited) - ); + addHandler(KeyBinding.of(KeyCode.SPACE), (ev) -> keyPressed(ev, true)); + addHandler(KeyBinding.builder(KeyCode.SPACE).keyReleased().build(), (ev) -> keyReleased(ev, true)); - // we don't want to consume events on enter press - let them carry on through - enterPressed.setAutoConsume(false); - enterReleased.setAutoConsume(false); + // these two should not consume the event + addHandler(KeyBinding.of(KeyCode.ENTER), (ev) -> keyPressed(ev, false)); + addHandler(KeyBinding.builder(KeyCode.ENTER).keyReleased().build(), (ev) -> keyReleased(ev, false)); - // ComboBoxBase also cares about focus - comboBox.focusedProperty().addListener(focusListener); + addHandler(KeyBinding.of(KeyCode.ESCAPE), this::cancelEdit); + addHandler(KeyBinding.of(KeyCode.F10), this::forwardToParent); - // Only add this if we're on an embedded platform that supports 5-button navigation - if (Utils.isTwoLevelFocus()) { - tlFocus = new TwoLevelFocusComboBehavior(comboBox); // needs to be last. - } + addHandler(MouseEvent.MOUSE_PRESSED, this::mousePressed); + addHandler(MouseEvent.MOUSE_RELEASED, this::mouseReleased); + addHandler(MouseEvent.MOUSE_ENTERED, this::mouseEntered); + addHandler(MouseEvent.MOUSE_EXITED, this::mouseExited); } - @Override public void dispose() { - if (tlFocus != null) tlFocus.dispose(); - getNode().focusedProperty().removeListener(focusListener); - super.dispose(); - } - - @Override public InputMap> getInputMap() { - return inputMap; + public void dispose() { + if (tlFocus != null) { + tlFocus.dispose(); + } + getControl().focusedProperty().removeListener(focusListener); } /*************************************************************************** @@ -132,7 +109,7 @@ public ComboBoxBaseBehavior(final ComboBoxBase comboBox) { protected void focusChanged(Observable o) { // If we did have the key down, but are now not focused, then we must // disarm the box. - final ComboBoxBase box = getNode(); + final ComboBoxBase box = getControl(); if (keyDown && !box.isFocused()) { keyDown = false; box.disarm(); @@ -158,7 +135,7 @@ protected void focusChanged(Observable o) { * causes this button to be armed if it is not already armed by a mouse * press. */ - private void keyPressed(KeyEvent e) { + private void keyPressed(KeyEvent e, boolean consume) { // If popup is shown, KeyEvent causes popup to close showPopupOnMouseRelease = true; @@ -169,34 +146,46 @@ private void keyPressed(KeyEvent e) { } } else { - if (! getNode().isPressed() && ! getNode().isArmed()) { + if (! getControl().isPressed() && ! getControl().isArmed()) { keyDown = true; - getNode().arm(); + getControl().arm(); } } + if (consume) { + e.consume(); + } } /** * Invoked when a valid keystroke release occurs which causes the button * to fire if it was armed by a keyPress. */ - private void keyReleased(KeyEvent e) { + private void keyReleased(KeyEvent e, boolean consume) { // If popup is shown, KeyEvent causes popup to close showPopupOnMouseRelease = true; if (!Utils.isTwoLevelFocus()) { if (keyDown) { keyDown = false; - if (getNode().isArmed()) { - getNode().disarm(); + if (getControl().isArmed()) { + getControl().disarm(); } } } + if (consume) { + e.consume(); + } } private void forwardToParent(KeyEvent event) { - if (getNode().getParent() != null) { - getNode().getParent().fireEvent(event); + try { + if (getControl().getParent() != null) { + getControl().getParent().fireEvent(event); + } + } finally { + // TODO original logic is to always consume the event. + // we may want to change that + event.consume(); } } @@ -205,7 +194,7 @@ private void cancelEdit(KeyEvent event) { * This can be cleaned up if the editor property is moved up * to ComboBoxBase. */ - ComboBoxBase comboBoxBase = getNode(); + ComboBoxBase comboBoxBase = getControl(); TextField textField = null; if (comboBoxBase instanceof DatePicker) { textField = ((DatePicker)comboBoxBase).getEditor(); @@ -218,6 +207,7 @@ private void cancelEdit(KeyEvent event) { } else { forwardToParent(event); } + event.consume(); } @@ -229,6 +219,7 @@ private void cancelEdit(KeyEvent event) { public void mousePressed(MouseEvent e) { arm(e); + e.consume(); } public void mouseReleased(MouseEvent e) { @@ -245,10 +236,11 @@ public void mouseReleased(MouseEvent e) { showPopupOnMouseRelease = true; hide(); } + e.consume(); } public void mouseEntered(MouseEvent e) { - if (!getNode().isEditable()) { + if (!getControl().isEditable()) { mouseInsideButton = true; } else { // This is strongly tied to ComboBoxBaseSkin @@ -256,11 +248,13 @@ public void mouseEntered(MouseEvent e) { mouseInsideButton = (target instanceof Node && "arrow-button".equals(((Node) target).getId())); } arm(); + e.consume(); } public void mouseExited(MouseEvent e) { mouseInsideButton = false; disarm(); + e.consume(); } // private void getFocus() { @@ -274,23 +268,23 @@ private void arm(MouseEvent e) { ! (e.isMiddleButtonDown() || e.isSecondaryButtonDown() || e.isShiftDown() || e.isControlDown() || e.isAltDown() || e.isMetaDown())); - if (! getNode().isArmed() && valid) { - getNode().arm(); + if (! getControl().isArmed() && valid) { + getControl().arm(); } } public void show() { - if (! getNode().isShowing()) { - if (getNode().isFocusTraversable()) { - getNode().requestFocus(); + if (! getControl().isShowing()) { + if (getControl().isFocusTraversable()) { + getControl().requestFocus(); } - getNode().show(); + getControl().show(); } } public void hide() { - if (getNode().isShowing()) { - getNode().hide(); + if (getControl().isShowing()) { + getControl().hide(); } } @@ -307,15 +301,25 @@ public void onAutoHide(PopupControl popup) { } public void arm() { - if (getNode().isPressed()) { - getNode().arm(); + if (getControl().isPressed()) { + getControl().arm(); } } public void disarm() { - if (! keyDown && getNode().isArmed()) { - getNode().disarm(); + if (! keyDown && getControl().isArmed()) { + getControl().disarm(); } } + private void togglePopup() { + // If popup is shown, KeyEvent causes popup to close + showPopupOnMouseRelease = true; + + if (getControl().isShowing()) { + hide(); + } else { + show(); + } + } } diff --git a/modules/javafx.controls/src/main/java/com/sun/javafx/scene/control/behavior/ComboBoxListViewBehavior.java b/modules/javafx.controls/src/main/java/com/sun/javafx/scene/control/behavior/ComboBoxListViewBehavior.java index 3fb031bd3c2..9b70a8f8dea 100644 --- a/modules/javafx.controls/src/main/java/com/sun/javafx/scene/control/behavior/ComboBoxListViewBehavior.java +++ b/modules/javafx.controls/src/main/java/com/sun/javafx/scene/control/behavior/ComboBoxListViewBehavior.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 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 @@ -28,10 +28,7 @@ import javafx.scene.control.ComboBox; import javafx.scene.control.ComboBoxBase; import javafx.scene.control.SelectionModel; -import com.sun.javafx.scene.control.inputmap.InputMap; - -import static javafx.scene.input.KeyCode.DOWN; -import static javafx.scene.input.KeyCode.UP; +import javafx.scene.input.KeyCode; public class ComboBoxListViewBehavior extends ComboBoxBaseBehavior { @@ -44,16 +41,19 @@ public class ComboBoxListViewBehavior extends ComboBoxBaseBehavior { /** * */ - public ComboBoxListViewBehavior(final ComboBox comboBox) { - super(comboBox); + public ComboBoxListViewBehavior(ComboBoxBase c) { + super(c); + } + + @Override + protected void populateSkinInputMap() { + super.populateSkinInputMap(); + + registerFunction(ComboBox.SELECT_PREV, this::selectPrevious); + registerFunction(ComboBox.SELECT_NEXT, this::selectNext); - // Add these bindings as a child input map, so they take precedence - InputMap> comboBoxListViewInputMap = new InputMap<>(comboBox); - comboBoxListViewInputMap.getMappings().addAll( - new InputMap.KeyMapping(UP, e -> selectPrevious()), - new InputMap.KeyMapping(DOWN, e -> selectNext()) - ); - addDefaultChildMap(getInputMap(), comboBoxListViewInputMap); + registerKey(KeyCode.UP, ComboBox.SELECT_PREV); + registerKey(KeyCode.DOWN, ComboBox.SELECT_NEXT); } /*************************************************************************** @@ -63,7 +63,7 @@ public ComboBoxListViewBehavior(final ComboBox comboBox) { **************************************************************************/ private ComboBox getComboBox() { - return (ComboBox) getNode(); + return (ComboBox) getControl(); } private void selectPrevious() { diff --git a/modules/javafx.controls/src/main/java/com/sun/javafx/scene/control/behavior/DatePickerBehavior.java b/modules/javafx.controls/src/main/java/com/sun/javafx/scene/control/behavior/DatePickerBehavior.java index 709d3d15e5b..1b25cdc030f 100644 --- a/modules/javafx.controls/src/main/java/com/sun/javafx/scene/control/behavior/DatePickerBehavior.java +++ b/modules/javafx.controls/src/main/java/com/sun/javafx/scene/control/behavior/DatePickerBehavior.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2023, 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 @@ -25,11 +25,10 @@ package com.sun.javafx.scene.control.behavior; +import java.time.LocalDate; import javafx.scene.control.DatePicker; import javafx.scene.control.PopupControl; -import java.time.LocalDate; - public class DatePickerBehavior extends ComboBoxBaseBehavior { @@ -42,8 +41,8 @@ public class DatePickerBehavior extends ComboBoxBaseBehavior { /** * */ - public DatePickerBehavior(final DatePicker datePicker) { - super(datePicker); + public DatePickerBehavior(DatePicker c) { + super(c); } /*************************************************************************** @@ -56,14 +55,14 @@ public DatePickerBehavior(final DatePicker datePicker) { @Override public void onAutoHide(PopupControl popup) { // when we click on some non-interactive part of the // calendar - we do not want to hide. - if (!popup.isShowing() && getNode().isShowing()) { + if (!popup.isShowing() && getControl().isShowing()) { // Popup was dismissed. Maybe user clicked outside or typed ESCAPE. // Make sure DatePicker button is in sync. - getNode().hide(); + getControl().hide(); } // if the DatePicker is no longer showing, then invoke the super method // to keep its show/hide state in sync. - if (!getNode().isShowing()) { + if (!getControl().isShowing()) { super.onAutoHide(popup); } } diff --git a/modules/javafx.controls/src/main/java/com/sun/javafx/scene/control/behavior/FocusTraversalInputMap.java b/modules/javafx.controls/src/main/java/com/sun/javafx/scene/control/behavior/FocusTraversalInputMap.java index 903c43d59fb..da6c8a6f9c5 100644 --- a/modules/javafx.controls/src/main/java/com/sun/javafx/scene/control/behavior/FocusTraversalInputMap.java +++ b/modules/javafx.controls/src/main/java/com/sun/javafx/scene/control/behavior/FocusTraversalInputMap.java @@ -137,6 +137,14 @@ public static final void traverseNext(KeyEvent e) { traverse(getNode(e), com.sun.javafx.scene.traversal.Direction.NEXT, TraversalMethod.KEY); } + /** + * Calls the focus traversal engine and indicates that traversal should + * go the next focusTraversable Node in the focus traversal cycle. + */ + public static final void traverseNext(Node n) { + traverse(n, com.sun.javafx.scene.traversal.Direction.NEXT, TraversalMethod.KEY); + } + /** * Calls the focus traversal engine and indicates that traversal should * go the previous focusTraversable Node in the focus traversal cycle. @@ -145,6 +153,14 @@ public static final void traversePrevious(KeyEvent e) { traverse(getNode(e), com.sun.javafx.scene.traversal.Direction.PREVIOUS, TraversalMethod.KEY); } + /** + * Calls the focus traversal engine and indicates that traversal should + * go the previous focusTraversable Node in the focus traversal cycle. + */ + public static final void traversePrevious(Node n) { + traverse(n, com.sun.javafx.scene.traversal.Direction.PREVIOUS, TraversalMethod.KEY); + } + private static Node getNode(KeyEvent e) { EventTarget target = e.getTarget(); if (target instanceof Node) { diff --git a/modules/javafx.controls/src/main/java/com/sun/javafx/scene/control/behavior/PasswordFieldBehavior.java b/modules/javafx.controls/src/main/java/com/sun/javafx/scene/control/behavior/PasswordFieldBehavior.java index ddd8da66602..e0df46fdfe9 100644 --- a/modules/javafx.controls/src/main/java/com/sun/javafx/scene/control/behavior/PasswordFieldBehavior.java +++ b/modules/javafx.controls/src/main/java/com/sun/javafx/scene/control/behavior/PasswordFieldBehavior.java @@ -26,6 +26,8 @@ package com.sun.javafx.scene.control.behavior; import javafx.scene.control.PasswordField; +import javafx.scene.control.TextField; +import javafx.scene.control.skin.TextFieldSkin; import javafx.scene.text.HitInfo; /** @@ -33,31 +35,43 @@ */ public class PasswordFieldBehavior extends TextFieldBehavior { - public PasswordFieldBehavior(PasswordField passwordField) { - super(passwordField); + public PasswordFieldBehavior(PasswordField c, TextFieldSkin skin) { + super(c, skin); } // JDK-8127160 & JDK-8127849: Stub out word based navigation and editing // for security reasons. @Override - protected void deletePreviousWord() { } + protected void deletePreviousWord() { + } + @Override - protected void deleteNextWord() { } + protected void deleteNextWord() { + } + @Override - protected void selectPreviousWord() { } + protected void selectPreviousWord() { + } + @Override - public void selectNextWord() { } + public void selectNextWord() { + } + @Override - protected void previousWord() { } + protected void previousWord() { + } + @Override - protected void nextWord() { } + protected void nextWord() { + } + @Override protected void selectWord() { selectAll(); } + @Override protected void mouseDoubleClick(HitInfo hit) { - getNode().selectAll(); + getControl().selectAll(); } - } diff --git a/modules/javafx.controls/src/main/java/com/sun/javafx/scene/control/behavior/TabPaneBehavior.java b/modules/javafx.controls/src/main/java/com/sun/javafx/scene/control/behavior/TabPaneBehavior.java index 785aab9487b..c2cc48a4d98 100644 --- a/modules/javafx.controls/src/main/java/com/sun/javafx/scene/control/behavior/TabPaneBehavior.java +++ b/modules/javafx.controls/src/main/java/com/sun/javafx/scene/control/behavior/TabPaneBehavior.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 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 @@ -25,75 +25,77 @@ package com.sun.javafx.scene.control.behavior; -import com.sun.javafx.scene.control.inputmap.InputMap; +import java.util.List; import javafx.event.Event; +import javafx.geometry.NodeOrientation; +import javafx.scene.Node; import javafx.scene.control.SelectionModel; import javafx.scene.control.Tab; import javafx.scene.control.TabPane; -import javafx.scene.input.*; -import com.sun.javafx.scene.control.inputmap.KeyBinding; - -import java.util.List; - -import static javafx.scene.input.KeyCode.*; -import static com.sun.javafx.scene.control.inputmap.InputMap.KeyMapping; -import static com.sun.javafx.scene.control.inputmap.InputMap.MouseMapping; - -public class TabPaneBehavior extends BehaviorBase { - - private final InputMap tabPaneInputMap; - - public TabPaneBehavior(TabPane tabPane) { - super(tabPane); +import javafx.scene.control.input.KeyBinding; +import javafx.scene.control.input.SkinInputMap; +import javafx.scene.input.KeyCode; +import javafx.scene.input.MouseEvent; - // create a map for TabPane-specific mappings (this reuses the default - // InputMap installed on the control, if it is non-null, allowing us to pick up any user-specified mappings) - tabPaneInputMap = createInputMap(); - - // TabPane-specific mappings for key and mouse input - addDefaultMapping(tabPaneInputMap, - new KeyMapping(UP, e -> selectPreviousTab()), - new KeyMapping(DOWN, e -> selectNextTab()), - new KeyMapping(LEFT, e -> rtl(tabPane, this::selectNextTab, this::selectPreviousTab)), - new KeyMapping(RIGHT, e -> rtl(tabPane, this::selectPreviousTab, this::selectNextTab)), - new KeyMapping(HOME, e -> { - if (getNode().isFocused()) { - moveSelection(-1, 1); - } - }), - new KeyMapping(END, e -> { - if (getNode().isFocused()) { - moveSelection(getNode().getTabs().size(), -1); - } - }), - new KeyMapping(new KeyBinding(PAGE_UP).ctrl(), e -> selectPreviousTab()), - new KeyMapping(new KeyBinding(PAGE_DOWN).ctrl(), e -> selectNextTab()), - new KeyMapping(new KeyBinding(TAB).ctrl(), e -> selectNextTab()), - new KeyMapping(new KeyBinding(TAB).ctrl().shift(), e -> selectPreviousTab()), - new MouseMapping(MouseEvent.MOUSE_PRESSED, e -> getNode().requestFocus()) - ); +/** + * The TabPaneBehavior is stateless. + */ +// The amount of memory saved with stateless (static) behaviors would likely not justify +// more extensive changes that are required to convert the legacy behavior to a stateless one: +// now we have to manually drag the control instance everywhere. +// I don't think this is worth it. +public class TabPaneBehavior { + private static final SkinInputMap.Stateless inputMap = createInputMap(); + + // stateless behavior: one SkinInputMap for all TabPanes + private TabPaneBehavior() { } + + private static SkinInputMap.Stateless createInputMap() { + SkinInputMap.Stateless m = SkinInputMap.createStateless(); + + m.registerFunction(TabPane.Tag.SELECT_FIRST_TAB, TabPaneBehavior::selectFirstTab); + m.registerFunction(TabPane.Tag.SELECT_LAST_TAB, TabPaneBehavior::selectLastTab); + m.registerFunction(TabPane.Tag.SELECT_LEFT_TAB, TabPaneBehavior::selectLeftTab); + m.registerFunction(TabPane.Tag.SELECT_NEXT_TAB, TabPaneBehavior::selectNextTab); + m.registerFunction(TabPane.Tag.SELECT_PREV_TAB, TabPaneBehavior::selectPreviousTab); + m.registerFunction(TabPane.Tag.SELECT_RIGHT_TAB, TabPaneBehavior::selectRightTab); + + m.registerKey(KeyBinding.of(KeyCode.DOWN), TabPane.Tag.SELECT_NEXT_TAB); + m.registerKey(KeyBinding.of(KeyCode.HOME), TabPane.Tag.SELECT_FIRST_TAB); + m.registerKey(KeyBinding.of(KeyCode.END), TabPane.Tag.SELECT_LAST_TAB); + m.registerKey(KeyBinding.of(KeyCode.LEFT), TabPane.Tag.SELECT_LEFT_TAB); + m.registerKey(KeyBinding.of(KeyCode.RIGHT), TabPane.Tag.SELECT_RIGHT_TAB); + m.registerKey(KeyBinding.of(KeyCode.UP), TabPane.Tag.SELECT_PREV_TAB); + + m.registerKey(KeyBinding.control(KeyCode.PAGE_DOWN), TabPane.Tag.SELECT_NEXT_TAB); + m.registerKey(KeyBinding.control(KeyCode.PAGE_UP), TabPane.Tag.SELECT_PREV_TAB); + m.registerKey(KeyBinding.control(KeyCode.TAB), TabPane.Tag.SELECT_NEXT_TAB); + m.registerKey(KeyBinding.controlShift(KeyCode.TAB), TabPane.Tag.SELECT_PREV_TAB); + + m.addHandler(MouseEvent.MOUSE_PRESSED, TabPaneBehavior::requestFocus); + + return m; } - @Override public InputMap getInputMap() { - return tabPaneInputMap; + public static void install(TabPane control) { + control.getInputMap().setSkinInputMap(inputMap); } - public void selectTab(Tab tab) { - getNode().getSelectionModel().select(tab); + public static void selectTab(TabPane c, Tab tab) { + c.getSelectionModel().select(tab); } - public boolean canCloseTab(Tab tab) { - Event event = new Event(tab,tab,Tab.TAB_CLOSE_REQUEST_EVENT); - Event.fireEvent(tab, event); - return ! event.isConsumed(); + public static boolean canCloseTab(Tab tab) { + Event ev = new Event(tab, tab, Tab.TAB_CLOSE_REQUEST_EVENT); + Event.fireEvent(tab, ev); + return !ev.isConsumed(); } - public void closeTab(Tab tab) { - TabPane tabPane = getNode(); + public static void closeTab(TabPane c, Tab tab) { // only switch to another tab if the selected tab is the one we're closing - int index = tabPane.getTabs().indexOf(tab); + int index = c.getTabs().indexOf(tab); if (index != -1) { - tabPane.getTabs().remove(index); + c.getTabs().remove(index); } if (tab.getOnClosed() != null) { Event.fireEvent(tab, new Event(Tab.CLOSED_EVENT)); @@ -102,34 +104,72 @@ public void closeTab(Tab tab) { // Find a tab after the currently selected that is not disabled. Loop around // if no tabs are found after currently selected tab. - public void selectNextTab() { - moveSelection(1); + public static void selectNextTab(TabPane c) { + moveSelection(c, 1); } // Find a tab before the currently selected that is not disabled. - public void selectPreviousTab() { - moveSelection(-1); + public static void selectPreviousTab(TabPane c) { + moveSelection(c, -1); + } + + private static void selectLeftTab(TabPane c) { + if (isRTL(c)) { + selectNextTab(c); + } else { + selectPreviousTab(c); + } + } + + private static void selectRightTab(TabPane c) { + if (isRTL(c)) { + selectPreviousTab(c); + } else { + selectNextTab(c); + } } - private void moveSelection(int delta) { - moveSelection(getNode().getSelectionModel().getSelectedIndex(), delta); + private static boolean isRTL(TabPane c) { + return c.getEffectiveNodeOrientation() == NodeOrientation.RIGHT_TO_LEFT; } - private void moveSelection(int startIndex, int delta) { - final TabPane tabPane = getNode(); - if (tabPane.getTabs().isEmpty()) return; + // TODO a bit of controversy: should this method return boolean to avoid consuming the key event + // when the control is not focused? + private static void selectFirstTab(TabPane c) { + if (c.isFocused()) { + moveSelection(c, -1, 1); + } + } + + // TODO a bit of controversy: should this method return boolean to avoid consuming the key event + // when the control is not focused? + private static void selectLastTab(TabPane c) { + if (c.isFocused()) { + int sz = c.getTabs().size(); + moveSelection(c, sz, -1); + } + } - int tabIndex = findValidTab(startIndex, delta); + private static void moveSelection(TabPane c, int delta) { + int ix = c.getSelectionModel().getSelectedIndex(); + moveSelection(c, ix, delta); + } + + private static void moveSelection(TabPane c, int startIndex, int delta) { + if (c.getTabs().isEmpty()) { + return; + } + + int tabIndex = findValidTab(c, startIndex, delta); if (tabIndex > -1) { - final SelectionModel selectionModel = tabPane.getSelectionModel(); + final SelectionModel selectionModel = c.getSelectionModel(); selectionModel.select(tabIndex); } - tabPane.requestFocus(); + c.requestFocus(); } - private int findValidTab(int startIndex, int delta) { - final TabPane tabPane = getNode(); - final List tabs = tabPane.getTabs(); + private static int findValidTab(TabPane c, int startIndex, int delta) { + final List tabs = c.getTabs(); final int max = tabs.size(); int index = startIndex; @@ -144,7 +184,7 @@ private int findValidTab(int startIndex, int delta) { return -1; } - private int nextIndex(int value, int max) { + private static int nextIndex(int value, int max) { final int min = 0; int r = value % max; if (r > min && max < min) { @@ -154,4 +194,9 @@ private int nextIndex(int value, int max) { } return r; } + + private static void requestFocus(MouseEvent ev) { + ((Node)ev.getSource()).requestFocus(); + ev.consume(); + } } diff --git a/modules/javafx.controls/src/main/java/com/sun/javafx/scene/control/behavior/TextAreaBehavior.java b/modules/javafx.controls/src/main/java/com/sun/javafx/scene/control/behavior/TextAreaBehavior.java index f36ba23144e..932ad19863a 100644 --- a/modules/javafx.controls/src/main/java/com/sun/javafx/scene/control/behavior/TextAreaBehavior.java +++ b/modules/javafx.controls/src/main/java/com/sun/javafx/scene/control/behavior/TextAreaBehavior.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 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 @@ -25,130 +25,132 @@ package com.sun.javafx.scene.control.behavior; -import com.sun.javafx.PlatformUtil; -import com.sun.javafx.scene.control.Properties; -import javafx.scene.control.skin.TextAreaSkin; import javafx.beans.value.ChangeListener; import javafx.geometry.Point2D; import javafx.geometry.Rectangle2D; import javafx.scene.Scene; import javafx.scene.control.TextArea; -import com.sun.javafx.scene.control.skin.Utils; +import javafx.scene.control.input.KeyBinding; +import javafx.scene.control.skin.TextAreaSkin; +import javafx.scene.control.skin.TextInputControlSkin.Direction; +import javafx.scene.control.skin.TextInputControlSkin.TextUnit; import javafx.scene.input.ContextMenuEvent; -import com.sun.javafx.scene.control.inputmap.InputMap; -import com.sun.javafx.scene.control.inputmap.KeyBinding; -import javafx.scene.input.KeyEvent; +import javafx.scene.input.KeyCode; import javafx.scene.input.MouseButton; import javafx.scene.input.MouseEvent; import javafx.scene.text.HitInfo; import javafx.stage.Screen; import javafx.stage.Window; - -import java.util.function.Predicate; - -import static com.sun.javafx.PlatformUtil.isMac; -import static com.sun.javafx.PlatformUtil.isWindows; -import static javafx.scene.control.skin.TextInputControlSkin.TextUnit; -import static javafx.scene.control.skin.TextInputControlSkin.Direction; -import static javafx.scene.input.KeyCode.*; +import com.sun.javafx.PlatformUtil; +import com.sun.javafx.scene.control.Properties; +import com.sun.javafx.scene.control.skin.Utils; /** * Text area behavior. */ public class TextAreaBehavior extends TextInputControlBehavior