modules/controls/src/main/java/javafx/scene/control/skin/SpinnerSkin.java

Print this page
rev 9240 : 8076423: JEP 253: Prepare JavaFX UI Controls & CSS APIs for Modularization

*** 20,35 **** * * 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.scene.control.skin; ! import com.sun.javafx.scene.control.behavior.SpinnerBehavior; import com.sun.javafx.scene.traversal.Algorithm; import com.sun.javafx.scene.traversal.Direction; import com.sun.javafx.scene.traversal.ParentTraversalEngine; import com.sun.javafx.scene.traversal.TraversalContext; import javafx.collections.ListChangeListener; import javafx.css.PseudoClass; import javafx.geometry.HPos; import javafx.geometry.VPos; --- 20,41 ---- * * 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 javafx.scene.control.skin; ! import com.sun.javafx.scene.control.behavior.BehaviorBase; import com.sun.javafx.scene.traversal.Algorithm; + import com.sun.javafx.scene.control.FakeFocusTextField; import com.sun.javafx.scene.traversal.Direction; import com.sun.javafx.scene.traversal.ParentTraversalEngine; + import javafx.scene.control.Accordion; + import javafx.scene.control.Button; + import javafx.scene.control.Control; + import javafx.scene.control.SkinBase; + import com.sun.javafx.scene.control.behavior.SpinnerBehavior; import com.sun.javafx.scene.traversal.TraversalContext; import javafx.collections.ListChangeListener; import javafx.css.PseudoClass; import javafx.geometry.HPos; import javafx.geometry.VPos;
*** 43,53 **** import javafx.scene.layout.Region; import javafx.scene.layout.StackPane; import java.util.List; ! public class SpinnerSkin<T> extends BehaviorSkinBase<Spinner<T>, SpinnerBehavior<T>> { private TextField textField; private Region incrementArrow; private StackPane incrementArrowButton; --- 49,71 ---- import javafx.scene.layout.Region; import javafx.scene.layout.StackPane; import java.util.List; ! /** ! * Default skin implementation for the {@link Spinner} control. ! * ! * @see Spinner ! * @since 9 ! */ ! public class SpinnerSkin<T> extends SkinBase<Spinner<T>> { ! ! /*************************************************************************** ! * * ! * Private fields * ! * * ! **************************************************************************/ private TextField textField; private Region incrementArrow; private StackPane incrementArrowButton;
*** 63,80 **** private static final int SPLIT_ARROWS_VERTICAL = 4; private static final int SPLIT_ARROWS_HORIZONTAL = 5; private int layoutMode = 0; ! public SpinnerSkin(Spinner<T> spinner) { ! super(spinner, new SpinnerBehavior<T>(spinner)); ! textField = spinner.getEditor(); getChildren().add(textField); updateStyleClass(); ! spinner.getStyleClass().addListener((ListChangeListener<String>) c -> updateStyleClass()); // increment / decrement arrows incrementArrow = new Region(); incrementArrow.setFocusTraversable(false); incrementArrow.getStyleClass().setAll("increment-arrow"); --- 81,119 ---- private static final int SPLIT_ARROWS_VERTICAL = 4; private static final int SPLIT_ARROWS_HORIZONTAL = 5; private int layoutMode = 0; ! private final SpinnerBehavior behavior; ! ! ! ! /*************************************************************************** ! * * ! * Constructors * ! * * ! **************************************************************************/ ! ! /** ! * Creates a new SpinnerSkin instance, installing the necessary child ! * nodes into the Control {@link Control#getChildren() children} list, as ! * well as the necessary input mappings for handling key, mouse, etc events. ! * ! * @param control The control that this skin should be installed onto. ! */ ! public SpinnerSkin(Spinner<T> control) { ! super(control); ! ! // install default input map for the Button control ! behavior = new SpinnerBehavior<>(control); ! // control.setInputMap(behavior.getInputMap()); ! textField = control.getEditor(); getChildren().add(textField); updateStyleClass(); ! control.getStyleClass().addListener((ListChangeListener<String>) c -> updateStyleClass()); // increment / decrement arrows incrementArrow = new Region(); incrementArrow.setFocusTraversable(false); incrementArrow.getStyleClass().setAll("increment-arrow");
*** 94,106 **** incrementArrowButton.setFocusTraversable(false); incrementArrowButton.getStyleClass().setAll("increment-arrow-button"); incrementArrowButton.getChildren().add(incrementArrow); incrementArrowButton.setOnMousePressed(e -> { getSkinnable().requestFocus(); ! getBehavior().startSpinning(true); }); ! incrementArrowButton.setOnMouseReleased(e -> getBehavior().stopSpinning()); decrementArrow = new Region(); decrementArrow.setFocusTraversable(false); decrementArrow.getStyleClass().setAll("decrement-arrow"); decrementArrow.setMaxWidth(Region.USE_PREF_SIZE); --- 133,145 ---- incrementArrowButton.setFocusTraversable(false); incrementArrowButton.getStyleClass().setAll("increment-arrow-button"); incrementArrowButton.getChildren().add(incrementArrow); incrementArrowButton.setOnMousePressed(e -> { getSkinnable().requestFocus(); ! behavior.startSpinning(true); }); ! incrementArrowButton.setOnMouseReleased(e -> behavior.stopSpinning()); decrementArrow = new Region(); decrementArrow.setFocusTraversable(false); decrementArrow.getStyleClass().setAll("decrement-arrow"); decrementArrow.setMaxWidth(Region.USE_PREF_SIZE);
*** 119,144 **** decrementArrowButton.setFocusTraversable(false); decrementArrowButton.getStyleClass().setAll("decrement-arrow-button"); decrementArrowButton.getChildren().add(decrementArrow); decrementArrowButton.setOnMousePressed(e -> { getSkinnable().requestFocus(); ! getBehavior().startSpinning(false); }); ! decrementArrowButton.setOnMouseReleased(e -> getBehavior().stopSpinning()); getChildren().addAll(incrementArrowButton, decrementArrowButton); // Fixes in the same vein as ComboBoxListViewSkin // move fake focus in to the textfield if the spinner is editable ! spinner.focusedProperty().addListener((ov, t, hasFocus) -> { // Fix for the regression noted in a comment in RT-29885. ! ((ComboBoxListViewSkin.FakeFocusTextField)textField).setFakeFocus(hasFocus); }); ! spinner.addEventFilter(KeyEvent.ANY, ke -> { ! if (spinner.isEditable()) { // This prevents a stack overflow from our rebroadcasting of the // event to the textfield that occurs in the final else statement // of the conditions below. if (ke.getTarget().equals(textField)) return; --- 158,183 ---- decrementArrowButton.setFocusTraversable(false); decrementArrowButton.getStyleClass().setAll("decrement-arrow-button"); decrementArrowButton.getChildren().add(decrementArrow); decrementArrowButton.setOnMousePressed(e -> { getSkinnable().requestFocus(); ! behavior.startSpinning(false); }); ! decrementArrowButton.setOnMouseReleased(e -> behavior.stopSpinning()); getChildren().addAll(incrementArrowButton, decrementArrowButton); // Fixes in the same vein as ComboBoxListViewSkin // move fake focus in to the textfield if the spinner is editable ! control.focusedProperty().addListener((ov, t, hasFocus) -> { // Fix for the regression noted in a comment in RT-29885. ! ((FakeFocusTextField)textField).setFakeFocus(hasFocus); }); ! control.addEventFilter(KeyEvent.ANY, ke -> { ! if (control.isEditable()) { // This prevents a stack overflow from our rebroadcasting of the // event to the textfield that occurs in the final else statement // of the conditions below. if (ke.getTarget().equals(textField)) return;
*** 157,175 **** // spinner when the user has mouse clicked into the TextField area of the // Spinner control. Without this the up/down/left/right arrow keys don't // work when you click inside the TextField area (but they do in the case // of tabbing in). textField.addEventFilter(KeyEvent.ANY, ke -> { ! if (! spinner.isEditable()) { ! spinner.fireEvent(ke.copyFor(spinner, spinner)); ke.consume(); } }); textField.focusedProperty().addListener((ov, t, hasFocus) -> { // Fix for RT-29885 ! spinner.getProperties().put("FOCUSED", hasFocus); // --- end of RT-29885 // RT-21454 starts here if (! hasFocus) { pseudoClassStateChanged(CONTAINS_FOCUS_PSEUDOCLASS_STATE, false); --- 196,214 ---- // spinner when the user has mouse clicked into the TextField area of the // Spinner control. Without this the up/down/left/right arrow keys don't // work when you click inside the TextField area (but they do in the case // of tabbing in). textField.addEventFilter(KeyEvent.ANY, ke -> { ! if (! control.isEditable()) { ! control.fireEvent(ke.copyFor(control, control)); ke.consume(); } }); textField.focusedProperty().addListener((ov, t, hasFocus) -> { // Fix for RT-29885 ! control.getProperties().put("FOCUSED", hasFocus); // --- end of RT-29885 // RT-21454 starts here if (! hasFocus) { pseudoClassStateChanged(CONTAINS_FOCUS_PSEUDOCLASS_STATE, false);
*** 179,195 **** // --- end of RT-21454 }); // end of comboBox-esque fixes ! textField.focusTraversableProperty().bind(spinner.editableProperty()); // Following code borrowed from ComboBoxPopupControl, to resolve the // issue initially identified in RT-36902, but specifically (for Spinner) // identified in RT-40625 ! spinner.setImpl_traversalEngine(new ParentTraversalEngine(spinner, new Algorithm() { @Override public Node select(Node owner, Direction dir, TraversalContext context) { return null; } @Override public Node selectFirst(TraversalContext context) { --- 218,234 ---- // --- end of RT-21454 }); // end of comboBox-esque fixes ! textField.focusTraversableProperty().bind(control.editableProperty()); // Following code borrowed from ComboBoxPopupControl, to resolve the // issue initially identified in RT-36902, but specifically (for Spinner) // identified in RT-40625 ! control.setImpl_traversalEngine(new ParentTraversalEngine(control, new Algorithm() { @Override public Node select(Node owner, Direction dir, TraversalContext context) { return null; } @Override public Node selectFirst(TraversalContext context) {
*** 200,227 **** return null; } })); } - private void updateStyleClass() { - final List<String> styleClass = getSkinnable().getStyleClass(); ! if (styleClass.contains(Spinner.STYLE_CLASS_ARROWS_ON_LEFT_VERTICAL)) { ! layoutMode = ARROWS_ON_LEFT_VERTICAL; ! } else if (styleClass.contains(Spinner.STYLE_CLASS_ARROWS_ON_LEFT_HORIZONTAL)) { ! layoutMode = ARROWS_ON_LEFT_HORIZONTAL; ! } else if (styleClass.contains(Spinner.STYLE_CLASS_ARROWS_ON_RIGHT_HORIZONTAL)) { ! layoutMode = ARROWS_ON_RIGHT_HORIZONTAL; ! } else if (styleClass.contains(Spinner.STYLE_CLASS_SPLIT_ARROWS_VERTICAL)) { ! layoutMode = SPLIT_ARROWS_VERTICAL; ! } else if (styleClass.contains(Spinner.STYLE_CLASS_SPLIT_ARROWS_HORIZONTAL)) { ! layoutMode = SPLIT_ARROWS_HORIZONTAL; ! } else { ! layoutMode = ARROWS_ON_RIGHT_VERTICAL; } } @Override protected void layoutChildren(final double x, final double y, final double w, final double h) { final double incrementArrowButtonWidth = incrementArrowButton.snappedLeftInset() + snapSize(incrementArrow.prefWidth(-1)) + incrementArrowButton.snappedRightInset(); --- 239,266 ---- return null; } })); } ! ! /*************************************************************************** ! * * ! * Public API * ! * * ! **************************************************************************/ ! ! /** {@inheritDoc} */ ! @Override public void dispose() { ! super.dispose(); ! ! if (behavior != null) { ! behavior.dispose(); } } + /** {@inheritDoc} */ @Override protected void layoutChildren(final double x, final double y, final double w, final double h) { final double incrementArrowButtonWidth = incrementArrowButton.snappedLeftInset() + snapSize(incrementArrow.prefWidth(-1)) + incrementArrowButton.snappedRightInset();
*** 300,318 **** --- 339,360 ---- positionInArea(incrementArrowButton, w - widestArrowButton, y, widestArrowButton, h, 0, HPos.CENTER, VPos.CENTER); } } + /** {@inheritDoc} */ @Override protected double computeMinHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) { return computePrefHeight(width, topInset, rightInset, bottomInset, leftInset); } + /** {@inheritDoc} */ @Override protected double computePrefWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) { final double textfieldWidth = textField.prefWidth(height); return leftInset + textfieldWidth + rightInset; } + /** {@inheritDoc} */ @Override protected double computePrefHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) { double ph; double textFieldHeight = textField.prefHeight(width); if (layoutMode == SPLIT_ARROWS_VERTICAL) {
*** 323,347 **** --- 365,419 ---- } return ph; } + /** {@inheritDoc} */ @Override protected double computeMaxWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) { return getSkinnable().prefWidth(height); } + /** {@inheritDoc} */ @Override protected double computeMaxHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) { return getSkinnable().prefHeight(width); } // Overridden so that we use the textfield as the baseline, rather than the arrow. // See RT-30754 for more information. + /** {@inheritDoc} */ @Override protected double computeBaselineOffset(double topInset, double rightInset, double bottomInset, double leftInset) { return textField.getLayoutBounds().getMinY() + textField.getLayoutY() + textField.getBaselineOffset(); } + + /*************************************************************************** + * * + * Private implementation * + * * + **************************************************************************/ + + private void updateStyleClass() { + final List<String> styleClass = getSkinnable().getStyleClass(); + + if (styleClass.contains(Spinner.STYLE_CLASS_ARROWS_ON_LEFT_VERTICAL)) { + layoutMode = ARROWS_ON_LEFT_VERTICAL; + } else if (styleClass.contains(Spinner.STYLE_CLASS_ARROWS_ON_LEFT_HORIZONTAL)) { + layoutMode = ARROWS_ON_LEFT_HORIZONTAL; + } else if (styleClass.contains(Spinner.STYLE_CLASS_ARROWS_ON_RIGHT_HORIZONTAL)) { + layoutMode = ARROWS_ON_RIGHT_HORIZONTAL; + } else if (styleClass.contains(Spinner.STYLE_CLASS_SPLIT_ARROWS_VERTICAL)) { + layoutMode = SPLIT_ARROWS_VERTICAL; + } else if (styleClass.contains(Spinner.STYLE_CLASS_SPLIT_ARROWS_HORIZONTAL)) { + layoutMode = SPLIT_ARROWS_HORIZONTAL; + } else { + layoutMode = ARROWS_ON_RIGHT_VERTICAL; + } + } + + + /*************************************************************************** * * * Stylesheet Handling * * * **************************************************************************/