com.sun.javafx.scene.control.skin.ProgressBarSkin Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of openjfx-78-backport Show documentation
Show all versions of openjfx-78-backport Show documentation
This is a backport of OpenJFX 8 to run on Java 7.
The newest version!
/*
* Copyright (c) 2010, 2013, 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* 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 java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.DoubleProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.css.CssMetaData;
import javafx.css.StyleableBooleanProperty;
import javafx.css.StyleableDoubleProperty;
import javafx.css.StyleableProperty;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.control.ProgressBar;
import javafx.scene.control.SkinBase;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.util.Duration;
import com.sun.javafx.css.converters.BooleanConverter;
import com.sun.javafx.css.converters.SizeConverter;
import com.sun.javafx.scene.control.behavior.ProgressBarBehavior;
import javafx.css.Styleable;
public class ProgressBarSkin extends BehaviorSkinBase> {
/***************************************************************************
* *
* UI Subcomponents *
* *
**************************************************************************/
private Bar bar;
private StackPane track;
private Region clipRegion;
// clean up progress so we never go out of bounds or update graphics more than twice per pixel
private double barWidth;
/** The length of the bouncing progress bar in indeterminate state */
private DoubleProperty indeterminateBarLength = null;
private DoubleProperty indeterminateBarLengthProperty() {
if (indeterminateBarLength == null) {
indeterminateBarLength = new StyleableDoubleProperty(60.0) {
@Override
public Object getBean() {
return ProgressBarSkin.this;
}
@Override
public String getName() {
return "indeterminateBarLength";
}
@Override
public CssMetaData getCssMetaData() {
return StyleableProperties.INDETERMINATE_BAR_LENGTH;
}
};
}
return indeterminateBarLength;
}
private Double getIndeterminateBarLength() {
return indeterminateBarLength == null ? 60.0 : indeterminateBarLength.get();
}
/** If the progress bar should escape the ends of the progress bar region in indeterminate state*/
private BooleanProperty indeterminateBarEscape = null;
private BooleanProperty indeterminateBarEscapeProperty() {
if (indeterminateBarEscape == null) {
indeterminateBarEscape = new StyleableBooleanProperty(true) {
@Override
public Object getBean() {
return ProgressBarSkin.this;
}
@Override
public String getName() {
return "indeterminateBarEscape";
}
@Override
public CssMetaData getCssMetaData() {
return StyleableProperties.INDETERMINATE_BAR_ESCAPE;
}
};
}
return indeterminateBarEscape;
}
private Boolean getIndeterminateBarEscape() {
return indeterminateBarEscape == null ? true : indeterminateBarEscape.get();
}
/** If the progress bar should flip when it gets to the ends in indeterminate state*/
private BooleanProperty indeterminateBarFlip = null;
private BooleanProperty indeterminateBarFlipProperty() {
if (indeterminateBarFlip == null) {
indeterminateBarFlip = new StyleableBooleanProperty(true) {
@Override
public Object getBean() {
return ProgressBarSkin.this;
}
@Override
public String getName() {
return "indeterminateBarFlip";
}
@Override
public CssMetaData getCssMetaData() {
return StyleableProperties.INDETERMINATE_BAR_FLIP;
}
};
}
return indeterminateBarFlip;
}
private Boolean getIndeterminateBarFlip() {
return indeterminateBarFlip == null ? true : indeterminateBarFlip.get();
}
/**
* How many seconds it should take for the indeterminate bar to go from
* one edge to the other
*/
private DoubleProperty indeterminateBarAnimationTime = null;
private DoubleProperty indeterminateBarAnimationTimeProperty() {
if (indeterminateBarAnimationTime == null) {
indeterminateBarAnimationTime = new StyleableDoubleProperty(2.0) {
@Override
public Object getBean() {
return ProgressBarSkin.this;
}
@Override
public String getName() {
return "indeterminateBarAnimationTime";
}
@Override
public CssMetaData getCssMetaData() {
return StyleableProperties.INDETERMINATE_BAR_ANIMATION_TIME;
}
};
}
return indeterminateBarAnimationTime;
}
private Double getIndeterminateBarAnimationTime() {
return indeterminateBarAnimationTime == null ? 2.0 : indeterminateBarAnimationTime.get();
};
private Timeline indeterminateTimeline;
private boolean timelineNulled = false;
/***************************************************************************
* *
* Constructors *
* *
**************************************************************************/
public ProgressBarSkin(ProgressBar control) {
super(control, new ProgressBarBehavior(control));
InvalidationListener indeterminateListener = new InvalidationListener() {
@Override public void invalidated(Observable valueModel) {
initialize();
}
};
control.indeterminateProperty().addListener(indeterminateListener);
InvalidationListener visibilityListener = new InvalidationListener() {
@Override public void invalidated(Observable valueModel) {
if (getSkinnable().isIndeterminate() && timelineNulled && indeterminateTimeline == null) {
timelineNulled = false;
createIndeterminateTimeline();
}
if (indeterminateTimeline != null) {
if (getSkinnable().impl_isTreeVisible() && getSkinnable().getScene() != null) {
indeterminateTimeline.play();
}
else {
indeterminateTimeline.pause();
indeterminateTimeline = null;
timelineNulled = true;
}
}
}
};
control.visibleProperty().addListener(visibilityListener);
control.parentProperty().addListener(visibilityListener);
InvalidationListener sceneListener = new InvalidationListener() {
@Override public void invalidated(Observable valueModel) {
if (indeterminateTimeline != null) {
if (getSkinnable().getScene() == null) {
indeterminateTimeline.pause();
indeterminateTimeline = null;
timelineNulled = true;
}
}
else {
if (getSkinnable().getScene() != null && getSkinnable().isIndeterminate()) {
timelineNulled = false;
createIndeterminateTimeline();
if (getSkinnable().impl_isTreeVisible()) {
indeterminateTimeline.play();
}
getSkinnable().requestLayout();
}
}
}
};
control.sceneProperty().addListener(sceneListener);
barWidth = ((int) (control.getWidth() - snappedLeftInset() - snappedRightInset()) * 2 * Math.min(1, Math.max(0, control.getProgress()))) / 2.0F;
InvalidationListener listener = new InvalidationListener() {
@Override public void invalidated(Observable valueModel) {
updateProgress();
}
};
control.widthProperty().addListener(listener);
control.progressProperty().addListener(listener);
initialize();
getSkinnable().requestLayout();
}
private void initialize() {
ProgressBar control = getSkinnable();
track = new StackPane();
track.getStyleClass().setAll("track");
bar = new Bar(this);
bar.getStyleClass().setAll("bar");
getChildren().setAll(track, bar);
// create a region to use as the clip for skin for animated indeterminate state
clipRegion = new Region();
// listen to the backgrounds on the bar and apply them to the clip but making them solid black for 100%
// solid anywhere the bar draws
bar.backgroundProperty().addListener(
new ChangeListener() {
@Override public void changed(ObservableValue observable, Background oldValue, Background newValue) {
if (newValue != null && !newValue.getFills().isEmpty()) {
final BackgroundFill[] fills = new BackgroundFill[newValue.getFills().size()];
for (int i = 0; i < newValue.getFills().size(); i++) {
BackgroundFill bf = newValue.getFills().get(i);
fills[i] = new BackgroundFill(Color.BLACK,bf.getRadii(),bf.getInsets());
}
clipRegion.setBackground(new Background(fills));
}
}
});
}
void pauseBar(boolean pause) {
if (indeterminateTimeline != null) {
if (pause) {
indeterminateTimeline.pause();
}
else {
indeterminateTimeline.play();
}
}
}
class Bar extends StackPane {
ProgressBarSkin pbSkin;
Bar(ProgressBarSkin pb) {
super();
pbSkin = pb;
InvalidationListener treeVisibilityListener = new InvalidationListener() {
@Override public void invalidated(Observable valueModel) {
if (getSkinnable().impl_isTreeVisible()) {
pbSkin.pauseBar(false);
}
else {
pbSkin.pauseBar(true);
}
}
};
impl_treeVisibleProperty().addListener(treeVisibilityListener);
}
}
private void createIndeterminateTimeline() {
if (indeterminateTimeline != null) indeterminateTimeline.stop();
ProgressBar control = getSkinnable();
final double w = control.getWidth() - (snappedLeftInset() + snappedRightInset());
final double startX = getIndeterminateBarEscape()? -getIndeterminateBarLength() : 0;
final double endX = getIndeterminateBarEscape()? w : w - getIndeterminateBarLength();
// Set up the timeline. We do not want to reverse if we are not flipping.
indeterminateTimeline = new Timeline();
indeterminateTimeline.setCycleCount(Timeline.INDEFINITE);
if (getIndeterminateBarFlip()) {
indeterminateTimeline.getKeyFrames().addAll(
new KeyFrame(
Duration.millis(0),
new EventHandler() {
@Override public void handle(ActionEvent event) {
bar.setScaleX(-1);
}
},
new KeyValue(clipRegion.translateXProperty(), startX-(w - getIndeterminateBarLength())),
new KeyValue(bar.translateXProperty(), startX)
),
new KeyFrame(
Duration.millis(getIndeterminateBarAnimationTime() * 1000),
new EventHandler() {
@Override public void handle(ActionEvent event) {
bar.setScaleX(1); }
},
new KeyValue(clipRegion.translateXProperty(), endX-(w - getIndeterminateBarLength())),
new KeyValue(bar.translateXProperty(), endX)
),
new KeyFrame(
Duration.millis((getIndeterminateBarAnimationTime() * 1000)+1),
new KeyValue(clipRegion.translateXProperty(), -endX)
),
new KeyFrame(
Duration.millis(getIndeterminateBarAnimationTime() * 2000),
new KeyValue(clipRegion.translateXProperty(), -startX),
new KeyValue(bar.translateXProperty(), startX)
)
);
} else {
indeterminateTimeline.getKeyFrames().addAll(
new KeyFrame(
Duration.millis(0),
new KeyValue(clipRegion.translateXProperty(), startX-(w - getIndeterminateBarLength())),
new KeyValue(bar.translateXProperty(), startX)
),
new KeyFrame(
Duration.millis(getIndeterminateBarAnimationTime() * 1000*2),
new KeyValue(clipRegion.translateXProperty(), endX-(w - getIndeterminateBarLength())),
new KeyValue(bar.translateXProperty(), endX)
)
);
}
}
private void updateProgress() {
ProgressBar control = getSkinnable();
barWidth = ((int) (control.getWidth() - snappedLeftInset() - snappedRightInset()) * 2 * Math.min(1, Math.max(0, control.getProgress()))) / 2.0F;
getSkinnable().requestLayout();
}
@Override
public double computeBaselineOffset(double topInset, double rightInset, double bottomInset, double leftInset) {
double height = getSkinnable().getHeight();
return topInset + height;
}
@Override public void dispose() {
super.dispose();
if (indeterminateTimeline != null) {
indeterminateTimeline.stop();
indeterminateTimeline.getKeyFrames().clear();
indeterminateTimeline = null;
}
}
/***************************************************************************
* *
* Layout *
* *
**************************************************************************/
@Override protected double computePrefWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
return Math.max(100, leftInset + bar.prefWidth(getSkinnable().getWidth()) + rightInset);
}
@Override protected double computePrefHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {
return topInset + bar.prefHeight(width) + bottomInset;
}
@Override protected double computeMaxWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
return getSkinnable().prefWidth(height);
}
@Override protected double computeMaxHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {
return getSkinnable().prefHeight(width);
}
@Override protected void layoutChildren(final double x, final double y,
final double w, final double h) {
final ProgressBar control = getSkinnable();
boolean isIndeterminate = control.isIndeterminate();
// resize clip
clipRegion.resizeRelocate(0,0, w, h);
track.resizeRelocate(x, y, w, h);
bar.resizeRelocate(x, y, isIndeterminate? getIndeterminateBarLength() : barWidth,h);
// things should be invisible only when well below minimum length
track.setVisible(true);
// width might have changed so recreate our animation if needed
if (isIndeterminate) {
createIndeterminateTimeline();
if (getSkinnable().impl_isTreeVisible()) {
indeterminateTimeline.play();
}
// apply clip
if (getIndeterminateBarFlip()) {
bar.setClip(clipRegion);
} else {
bar.setClip(null);
}
} else if (indeterminateTimeline != null) {
indeterminateTimeline.stop();
indeterminateTimeline = null;
// remove clip
bar.setClip(null);
}
}
/***************************************************************************
* *
* Stylesheet Handling *
* *
**************************************************************************/
/**
* Super-lazy instantiation pattern from Bill Pugh.
* @treatAsPrivate implementation detail
*/
private static class StyleableProperties {
private static final CssMetaData INDETERMINATE_BAR_LENGTH =
new CssMetaData("-fx-indeterminate-bar-length",
SizeConverter.getInstance(), 60.0) {
@Override
public boolean isSettable(ProgressBar n) {
final ProgressBarSkin skin = (ProgressBarSkin) n.getSkin();
return skin.indeterminateBarLength == null ||
!skin.indeterminateBarLength.isBound();
}
@Override
public StyleableProperty getStyleableProperty(ProgressBar n) {
final ProgressBarSkin skin = (ProgressBarSkin) n.getSkin();
return (StyleableProperty)skin.indeterminateBarLengthProperty();
}
};
private static final CssMetaData INDETERMINATE_BAR_ESCAPE =
new CssMetaData("-fx-indeterminate-bar-escape",
BooleanConverter.getInstance(), Boolean.TRUE) {
@Override
public boolean isSettable(ProgressBar n) {
final ProgressBarSkin skin = (ProgressBarSkin) n.getSkin();
return skin.indeterminateBarEscape == null ||
!skin.indeterminateBarEscape.isBound();
}
@Override
public StyleableProperty getStyleableProperty(ProgressBar n) {
final ProgressBarSkin skin = (ProgressBarSkin) n.getSkin();
return (StyleableProperty)skin.indeterminateBarEscapeProperty();
}
};
private static final CssMetaData INDETERMINATE_BAR_FLIP =
new CssMetaData("-fx-indeterminate-bar-flip",
BooleanConverter.getInstance(), Boolean.TRUE) {
@Override
public boolean isSettable(ProgressBar n) {
final ProgressBarSkin skin = (ProgressBarSkin) n.getSkin();
return skin.indeterminateBarFlip == null ||
!skin.indeterminateBarFlip.isBound();
}
@Override
public StyleableProperty getStyleableProperty(ProgressBar n) {
final ProgressBarSkin skin = (ProgressBarSkin) n.getSkin();
return (StyleableProperty)skin.indeterminateBarFlipProperty();
}
};
private static final CssMetaData INDETERMINATE_BAR_ANIMATION_TIME =
new CssMetaData("-fx-indeterminate-bar-animation-time",
SizeConverter.getInstance(), 2.0) {
@Override
public boolean isSettable(ProgressBar n) {
final ProgressBarSkin skin = (ProgressBarSkin) n.getSkin();
return skin.indeterminateBarAnimationTime == null ||
!skin.indeterminateBarAnimationTime.isBound();
}
@Override
public StyleableProperty getStyleableProperty(ProgressBar n) {
final ProgressBarSkin skin = (ProgressBarSkin) n.getSkin();
return (StyleableProperty)skin.indeterminateBarAnimationTimeProperty();
}
};
private static final List> STYLEABLES;
static {
final List> styleables =
new ArrayList>(SkinBase.getClassCssMetaData());
styleables.add(INDETERMINATE_BAR_LENGTH);
styleables.add(INDETERMINATE_BAR_ESCAPE);
styleables.add(INDETERMINATE_BAR_FLIP);
styleables.add(INDETERMINATE_BAR_ANIMATION_TIME);
STYLEABLES = Collections.unmodifiableList(styleables);
}
}
/**
* @return The CssMetaData associated with this class, which may include the
* CssMetaData of its super classes.
*/
public static List> getClassCssMetaData() {
return StyleableProperties.STYLEABLES;
}
/**
* {@inheritDoc}
*/
@Override
public List> getCssMetaData() {
return getClassCssMetaData();
}
}