/* * Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. * * * * * * * * * * * * * * * * * * * * */ package com.sun.javafx.scene.control.behavior; import com.sun.javafx.util.Utils; import javafx.animation.KeyFrame; import javafx.animation.Timeline; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.event.EventType; import javafx.geometry.NodeOrientation; import javafx.geometry.Orientation; import javafx.scene.control.Control; import javafx.scene.control.ScrollBar; import javafx.scene.control.Skin; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyEvent; import javafx.util.Duration; import java.util.ArrayList; import java.util.List; import static javafx.scene.input.KeyCode.*; import static javafx.scene.input.KeyEvent.KEY_RELEASED; /** * A Behavior implementation for ScrollBars. * */ public class ScrollBarBehavior extends BehaviorBase { /*************************************************************************** * * * Constructors * * * **************************************************************************/ public ScrollBarBehavior(ScrollBar scrollBar) { super(scrollBar, SCROLL_BAR_BINDINGS); } /*************************************************************************** * * * Functions * * * **************************************************************************/ void home() { getControl().setValue(getControl().getMin()); } void decrementValue() { getControl().adjustValue(0); } void end() { getControl().setValue(getControl().getMax()); } void incrementValue() { getControl().adjustValue(1); } /*************************************************************************** * * * Key event handling * * * **************************************************************************/ /* We manually specify the focus traversal keys because Slider has * different usage for up/down arrow keys. */ protected static final List SCROLL_BAR_BINDINGS = new ArrayList<>(); static { SCROLL_BAR_BINDINGS.add(new KeyBinding(F4, "TraverseDebug").alt().ctrl().shift()); SCROLL_BAR_BINDINGS.add(new ScrollBarKeyBinding(LEFT, "DecrementValue")); SCROLL_BAR_BINDINGS.add(new ScrollBarKeyBinding(KP_LEFT, "DecrementValue")); SCROLL_BAR_BINDINGS.add(new ScrollBarKeyBinding(UP, "DecrementValue").vertical()); SCROLL_BAR_BINDINGS.add(new ScrollBarKeyBinding(KP_UP, "DecrementValue").vertical()); SCROLL_BAR_BINDINGS.add(new ScrollBarKeyBinding(RIGHT, "IncrementValue")); SCROLL_BAR_BINDINGS.add(new ScrollBarKeyBinding(KP_RIGHT, "IncrementValue")); SCROLL_BAR_BINDINGS.add(new ScrollBarKeyBinding(DOWN, "IncrementValue").vertical()); SCROLL_BAR_BINDINGS.add(new ScrollBarKeyBinding(KP_DOWN, "IncrementValue").vertical()); SCROLL_BAR_BINDINGS.add(new KeyBinding(HOME, KEY_RELEASED, "Home")); SCROLL_BAR_BINDINGS.add(new KeyBinding(END, KEY_RELEASED, "End")); } protected /*final*/ String matchActionForEvent(KeyEvent e) { String action = super.matchActionForEvent(e); if (action != null) { if (e.getCode() == LEFT || e.getCode() == KP_LEFT) { if (getControl().getEffectiveNodeOrientation() == NodeOrientation.RIGHT_TO_LEFT) { action = getControl().getOrientation() == Orientation.HORIZONTAL ? "IncrementValue" : "DecrementValue"; } } else if (e.getCode() == RIGHT || e.getCode() == KP_RIGHT) { if (getControl().getEffectiveNodeOrientation() == NodeOrientation.RIGHT_TO_LEFT) { action = getControl().getOrientation() == Orientation.HORIZONTAL ? "DecrementValue" : "IncrementValue"; } } } return action; } @Override protected void callAction(String name) { if ("Home".equals(name)) home(); else if ("End".equals(name)) end(); else if ("IncrementValue".equals(name)) incrementValue(); else if ("DecrementValue".equals(name)) decrementValue(); else super.callAction(name); super.callAction(name); } /*************************************************************************** * * * Mouse event handling * * * **************************************************************************/ /** * This timeline is used to adjust the value of the bar when the * track has been pressed but not released. */ Timeline timeline; /** * Invoked by the ScrollBar {@link Skin} implementation whenever a mouse * press occurs on the "track" of the bar. This will cause the thumb to * be moved by some amount. * * @param position The mouse position on track with 0.0 being beginning of track and 1.0 being the end */ public void trackPress(double position) { /* We can get a press if someone presses an end button. In that * case, we don't want to start a timeline because the end button * will have already done so. We can detect that because the timeline * will not be null. */ if (timeline != null) return; // determine the percentage of the way between min and max // represented by this mouse event final ScrollBar bar = getControl(); if (!bar.isFocused() && bar.isFocusTraversable()) bar.requestFocus(); final double pos = position; final boolean incrementing = (pos > ((bar.getValue() - bar.getMin())/(bar.getMax() - bar.getMin()))); timeline = new Timeline(); timeline.setCycleCount(Timeline.INDEFINITE); final EventHandler step = event -> { boolean i = (pos > ((bar.getValue() - bar.getMin())/(bar.getMax() - bar.getMin()))); if (incrementing == i) { // we started incrementing and still are, or we // started decrementing and still are bar.adjustValue(pos); } else { stopTimeline(); } }; final KeyFrame kf = new KeyFrame(Duration.millis(200), step); timeline.getKeyFrames().add(kf); // do the first step immediately timeline.play(); step.handle(null); } /** */ public void trackRelease() { stopTimeline(); } /** * Invoked by the ScrollBar {@link Skin} implementation whenever a mouse * press occurs on the decrement button of the bar. */ public void decButtonPressed() { final ScrollBar bar = getControl(); if (!bar.isFocused() && bar.isFocusTraversable()) bar.requestFocus(); stopTimeline(); timeline = new Timeline(); timeline.setCycleCount(Timeline.INDEFINITE); final EventHandler dec = event -> { if (bar.getValue() > bar.getMin()) { bar.decrement(); } else { stopTimeline(); } }; final KeyFrame kf = new KeyFrame(Duration.millis(200), dec); timeline.getKeyFrames().add(kf); // do the first step immediately timeline.play(); dec.handle(null); } /** */ public void decButtonReleased() { stopTimeline(); } /** * Invoked by the ScrollBar {@link Skin} implementation whenever a mouse * press occurs on the increment button of the bar. */ public void incButtonPressed() { final ScrollBar bar = getControl(); if (!bar.isFocused() && bar.isFocusTraversable()) bar.requestFocus(); stopTimeline(); timeline = new Timeline(); timeline.setCycleCount(Timeline.INDEFINITE); final EventHandler inc = event -> { if (bar.getValue() < bar.getMax()) { bar.increment(); } else { stopTimeline(); } }; final KeyFrame kf = new KeyFrame(Duration.millis(200), inc); timeline.getKeyFrames().add(kf); // do the first step immediately timeline.play(); inc.handle(null); } /** */ public void incButtonReleased() { stopTimeline(); } /** * @param position The mouse position on track with 0.0 being begining of track and 1.0 being the end */ //public function thumbPressed(e:MouseEvent, position:Number):Void { //} /** * @param position The mouse position on track with 0.0 being begining of track and 1.0 being the end */ public void thumbDragged(double position) { final ScrollBar scrollbar = getControl(); // Stop the timeline for continuous increments as drags take precedence stopTimeline(); if (!scrollbar.isFocused() && scrollbar.isFocusTraversable()) scrollbar.requestFocus(); double newValue = (position * (scrollbar.getMax() - scrollbar.getMin())) + scrollbar.getMin(); if (!Double.isNaN(newValue)) { scrollbar.setValue(Utils.clamp(scrollbar.getMin(), newValue, scrollbar.getMax())); } } private void stopTimeline() { if (timeline != null) { timeline.stop(); timeline = null; } } /** * Class to handle key bindings based upon the orientation of the control. */ public static class ScrollBarKeyBinding extends OrientedKeyBinding { public ScrollBarKeyBinding(KeyCode code, String action) { super(code, action); } public ScrollBarKeyBinding(KeyCode code, EventType type, String action) { super(code, type, action); } public @Override boolean getVertical(Control control) { return ((ScrollBar)control).getOrientation() == Orientation.VERTICAL; } } }