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 *
* *
**************************************************************************/