diff --git a/modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/ComboBoxBaseBehavior.java b/modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/ComboBoxBaseBehavior.java --- a/modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/ComboBoxBaseBehavior.java +++ b/modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/ComboBoxBaseBehavior.java @@ -99,6 +99,8 @@ // ComboBoxBase also cares about focus comboBox.focusedProperty().addListener(this::focusChanged); + comboBox.showingProperty().addListener(this::showingChanged); + // 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. @@ -271,7 +273,7 @@ } } - public void show() { + private void show() { if (! getNode().isShowing()) { if (getNode().isFocusTraversable()) { getNode().requestFocus(); @@ -280,7 +282,7 @@ } } - public void hide() { + private void hide() { if (getNode().isShowing()) { getNode().hide(); } @@ -310,4 +312,9 @@ } } + private void showingChanged(Observable o) { + if (getNode().isShowing()) { + showPopupOnMouseRelease = false; + } + } } diff --git a/modules/controls/src/main/java/javafx/scene/control/skin/ComboBoxBaseSkin.java b/modules/controls/src/main/java/javafx/scene/control/skin/ComboBoxBaseSkin.java --- a/modules/controls/src/main/java/javafx/scene/control/skin/ComboBoxBaseSkin.java +++ b/modules/controls/src/main/java/javafx/scene/control/skin/ComboBoxBaseSkin.java @@ -58,8 +58,9 @@ private Node displayNode; // this is normally either label or textField - StackPane arrowButton; - Region arrow; +// StackPane arrowButton; +// Region arrow; + private Node arrowButton; /** The mode in which this control will be represented. */ private ComboBoxMode mode = ComboBoxMode.COMBOBOX; @@ -86,32 +87,11 @@ super(control); // open button / arrow - arrow = new Region(); - arrow.setFocusTraversable(false); - arrow.getStyleClass().setAll("arrow"); - arrow.setId("arrow"); - arrow.setMaxWidth(Region.USE_PREF_SIZE); - arrow.setMaxHeight(Region.USE_PREF_SIZE); - arrow.setMouseTransparent(true); - - arrowButton = new StackPane(); - arrowButton.setFocusTraversable(false); - arrowButton.setId("arrow-button"); - arrowButton.getStyleClass().setAll("arrow-button"); - arrowButton.getChildren().add(arrow); - - if (control.isEditable()) { - // - // arrowButton behaves like a button. - // This is strongly tied to the implementation in ComboBoxBaseBehavior. - // - arrowButton.addEventHandler(MouseEvent.MOUSE_ENTERED, (e) -> getBehavior().mouseEntered(e)); - arrowButton.addEventHandler(MouseEvent.MOUSE_PRESSED, (e) -> { getBehavior().mousePressed(e); e.consume(); }); - arrowButton.addEventHandler(MouseEvent.MOUSE_RELEASED, (e) -> { getBehavior().mouseReleased(e); e.consume();}); - arrowButton.addEventHandler(MouseEvent.MOUSE_EXITED, (e) -> getBehavior().mouseExited(e)); - + arrowButton = createArrowButton(); + installArrowButtonMouseHandlers(); + if (arrowButton != null) { + getChildren().add(arrowButton); } - getChildren().add(arrowButton); // When ComboBoxBase focus shifts to another node, it should hide. getSkinnable().focusedProperty().addListener((observable, oldValue, newValue) -> { @@ -158,6 +138,42 @@ */ public abstract void hide(); + protected Node createArrowButton() { + if (arrowButton != null) { + return arrowButton; + } + + Region arrow = new Region(); + arrow.setFocusTraversable(false); + arrow.getStyleClass().setAll("arrow"); + arrow.setId("arrow"); + arrow.setMaxWidth(Region.USE_PREF_SIZE); + arrow.setMaxHeight(Region.USE_PREF_SIZE); + arrow.setMouseTransparent(true); + + StackPane arrowButton = new StackPane(); + arrowButton.setFocusTraversable(false); + arrowButton.setId("arrow-button"); + arrowButton.getStyleClass().setAll("arrow-button"); + arrowButton.getChildren().add(arrow); + + return arrowButton; + } + + protected void installArrowButtonMouseHandlers() { + ComboBoxBaseBehavior behavior = getBehavior(); + if (arrowButton != null && behavior != null && getSkinnable().isEditable()) { + // + // arrowButton behaves like a button. + // This is strongly tied to the implementation in ComboBoxBaseBehavior. + // + arrowButton.addEventHandler(MouseEvent.MOUSE_ENTERED, (e) -> behavior.mouseEntered(e)); + arrowButton.addEventHandler(MouseEvent.MOUSE_PRESSED, (e) -> { behavior.mousePressed(e); e.consume(); }); + arrowButton.addEventHandler(MouseEvent.MOUSE_RELEASED, (e) -> { behavior.mouseReleased(e); e.consume();}); + arrowButton.addEventHandler(MouseEvent.MOUSE_EXITED, (e) -> behavior.mouseExited(e)); + } + } + /** {@inheritDoc} */ @Override protected void layoutChildren(final double x, final double y, final double w, final double h) { @@ -165,10 +181,7 @@ updateDisplayArea(); } - final double arrowWidth = snapSize(arrow.prefWidth(-1)); - final double arrowButtonWidth = (isButton()) ? 0 : - arrowButton.snappedLeftInset() + arrowWidth + - arrowButton.snappedRightInset(); + final double arrowButtonWidth = isButton() ? 0 : arrowButton.prefWidth(-1); if (displayNode != null) { displayNode.resizeRelocate(x, y, w - arrowButtonWidth, h); @@ -188,11 +201,7 @@ updateDisplayArea(); } - final double arrowWidth = snapSize(arrow.prefWidth(-1)); - final double arrowButtonWidth = isButton() ? 0 : - arrowButton.snappedLeftInset() + - arrowWidth + - arrowButton.snappedRightInset(); + final double arrowButtonWidth = isButton() ? 0 : arrowButton.prefWidth(-1); final double displayNodeWidth = displayNode == null ? 0 : displayNode.prefWidth(height); final double totalWidth = displayNodeWidth + arrowButtonWidth; @@ -208,9 +217,8 @@ double ph; if (displayNode == null) { final int DEFAULT_HEIGHT = 21; - double arrowHeight = (isButton()) ? 0 : - (arrowButton.snappedTopInset() + arrow.prefHeight(-1) + arrowButton.snappedBottomInset()); - ph = Math.max(DEFAULT_HEIGHT, arrowHeight); + final double arrowButtonHeight = isButton() ? 0 : arrowButton.prefHeight(-1); + ph = Math.max(DEFAULT_HEIGHT, arrowButtonHeight); } else { ph = displayNode.prefHeight(width); } diff --git a/modules/controls/src/main/java/javafx/scene/control/skin/ComboBoxPopupControl.java b/modules/controls/src/main/java/javafx/scene/control/skin/ComboBoxPopupControl.java --- a/modules/controls/src/main/java/javafx/scene/control/skin/ComboBoxPopupControl.java +++ b/modules/controls/src/main/java/javafx/scene/control/skin/ComboBoxPopupControl.java @@ -461,13 +461,21 @@ popup.setAutoHide(true); popup.setAutoFix(true); popup.setHideOnEscape(true); - popup.setOnAutoHide(e -> getBehavior().onAutoHide(popup)); + popup.setOnAutoHide(e -> { + if (getBehavior() != null) { + getBehavior().onAutoHide(popup); + } + }); popup.addEventHandler(MouseEvent.MOUSE_CLICKED, t -> { // RT-18529: We listen to mouse input that is received by the popup // but that is not consumed, and assume that this is due to the mouse // clicking outside of the node, but in areas such as the // dropshadow. - getBehavior().onAutoHide(popup); + if (getBehavior() != null) { + getBehavior().onAutoHide(popup); + } else { + hide(); + } }); popup.addEventHandler(WindowEvent.WINDOW_HIDDEN, t -> { // Make sure the accessibility focus returns to the combo box diff --git a/modules/controls/src/main/java/javafx/scene/control/skin/DatePickerSkin.java b/modules/controls/src/main/java/javafx/scene/control/skin/DatePickerSkin.java --- a/modules/controls/src/main/java/javafx/scene/control/skin/DatePickerSkin.java +++ b/modules/controls/src/main/java/javafx/scene/control/skin/DatePickerSkin.java @@ -88,26 +88,6 @@ // install default input map for the control this.behavior = new DatePickerBehavior(control); -// control.setInputMap(behavior.getInputMap()); - - // The "arrow" is actually a rectangular svg icon resembling a calendar. - // Round the size of the icon to whole integers to get sharp edges. - arrow.paddingProperty().addListener(new InvalidationListener() { - // This boolean protects against unwanted recursion. - private boolean rounding = false; - @Override public void invalidated(Observable observable) { - if (!rounding) { - Insets padding = arrow.getPadding(); - Insets rounded = new Insets(Math.round(padding.getTop()), Math.round(padding.getRight()), - Math.round(padding.getBottom()), Math.round(padding.getLeft())); - if (!rounded.equals(padding)) { - rounding = true; - arrow.setPadding(rounded); - rounding = false; - } - } - } - }); registerChangeListener(control.chronologyProperty(), e -> { updateDisplayNode();