Skip to content
10 changes: 10 additions & 0 deletions HMCL/src/main/java/com/jfoenix/controls/JFXProgressBar.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,16 @@ protected Skin<?> createDefaultSkin() {
return new JFXProgressBarSkin(this);
}

private boolean smoothProgress = true;

public boolean isSmoothProgress() {
return smoothProgress;
}

public void setSmoothProgress(boolean smoothProgress) {
this.smoothProgress = smoothProgress;
}
Comment on lines +52 to +60

private void initialize() {
setPrefWidth(200);
getStyleClass().add(DEFAULT_STYLE_CLASS);
Expand Down
186 changes: 105 additions & 81 deletions HMCL/src/main/java/com/jfoenix/skins/JFXProgressBarSkin.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,11 @@
import com.jfoenix.utils.TreeShowingProperty;
import javafx.animation.*;
import javafx.scene.Node;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.control.SkinBase;
import javafx.scene.layout.*;
import javafx.scene.shape.Rectangle;
import javafx.util.Duration;
import org.jackhuang.hmcl.util.Lang;
import org.jackhuang.hmcl.ui.animation.AnimationUtils;

/// # Material Design ProgressBar Skin
///
Expand All @@ -37,21 +36,22 @@
/// @since 2017-10-06
public class JFXProgressBarSkin extends SkinBase<JFXProgressBar> {

private static final double ARC_HEIGHT = 4;
private static final double DEFAULT_HEIGHT = 4;

private final StackPane track;
private final StackPane bar;
private Timeline indeterminateTransition;
private final Rectangle clip;
private Animation transition;
private final TreeShowingProperty treeShowingProperty;
private double fullWidth;

public JFXProgressBarSkin(JFXProgressBar control) {
super(control);

this.treeShowingProperty = new TreeShowingProperty(control);

registerChangeListener(treeShowingProperty, obs -> updateAnimation());
registerChangeListener(control.progressProperty(), obs -> updateProgress());
registerChangeListener(treeShowingProperty, obs -> updateProgress(false));
registerChangeListener(control.progressProperty(), obs -> updateProgress(true));

track = new StackPane();
track.getStyleClass().setAll("track");
Expand All @@ -60,9 +60,8 @@ public JFXProgressBarSkin(JFXProgressBar control) {
bar.getStyleClass().setAll("bar");

clip = new Rectangle();
clip.setManaged(false);
clip.setArcWidth(ARC_HEIGHT);
clip.setArcHeight(ARC_HEIGHT);
clip.setArcWidth(DEFAULT_HEIGHT);
clip.setArcHeight(DEFAULT_HEIGHT);
bar.setClip(clip);

getChildren().setAll(track, bar);
Expand All @@ -82,7 +81,7 @@ protected double computePrefWidth(double height, double topInset, double rightIn

@Override
protected double computePrefHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {
return topInset + bar.prefHeight(width) + bottomInset;
return topInset + DEFAULT_HEIGHT + bottomInset;
}

@Override
Expand All @@ -97,98 +96,117 @@ protected double computeMaxHeight(double width, double topInset, double rightIns

@Override
protected void layoutChildren(double x, double y, double w, double h) {
boolean indeterminate = getSkinnable().isIndeterminate();

track.resizeRelocate(x, y, w, h);
bar.resizeRelocate(x, y, w, h);

clip.relocate(0, 0);
clip.setTranslateX(0);
clip.setWidth(0);
clip.setHeight(h);
clip.setTranslateX(0);

if (indeterminate) {
if (treeShowingProperty.get()) {
createIndeterminateTimeline();
indeterminateTransition.play();
} else {
clearAnimation();
}
} else {
clearAnimation();

double progress = Lang.clamp(0.0, getSkinnable().getProgress(), 1.0);
double barWidth = ((int) w * 2 * progress) / 2.0;
if (progress > 0) {
barWidth = Math.max(barWidth, ARC_HEIGHT);
}
fullWidth = w;

clip.setWidth(barWidth);
}
clearAnimation();
updateProgress(false);
}

boolean wasIndeterminate = false;
private boolean wasIndeterminate = false;

private void updateProgress(boolean playProgressAnimation) {
double progress = Math.min(getSkinnable().getProgress(), 1.0);
boolean isIndeterminate = progress < 0.0;
boolean isTreeShowing = treeShowingProperty.get();

protected void pauseTimeline(boolean pause) {
if (getSkinnable().isIndeterminate()) {
if (pause) {
if (indeterminateTransition != null) {
indeterminateTransition.pause();
if (isIndeterminate != wasIndeterminate) {
wasIndeterminate = isIndeterminate;
clearAnimation();
clip.setTranslateX(0);
}

if (isIndeterminate) { // indeterminate
if (isTreeShowing) {
if (transition == null) {
transition = createIndeterminateTransition();
transition.playFromStart();
} else {
transition.play();
}
} else if (transition != null) {
transition.pause();
}
} else { // determinate
clearAnimation();
if (isTreeShowing && playProgressAnimation
&& AnimationUtils.isAnimationEnabled()
&& getSkinnable().isSmoothProgress()) {
transition = createDeterminateTransition(progress);
transition.playFromStart();
} else {
if (indeterminateTransition == null) {
createIndeterminateTimeline();
}
indeterminateTransition.play();
clip.setWidth(computeBarWidth(progress));
}
}
Comment on lines +138 to 147
}

private void updateAnimation() {
final boolean isTreeShowing = treeShowingProperty.get();
if (indeterminateTransition != null) {
pauseTimeline(!isTreeShowing);
}
}
private static final Duration INDETERMINATE_DURATION = Duration.seconds(1);

private void updateProgress() {
final ProgressIndicator control = getSkinnable();
final boolean isIndeterminate = control.isIndeterminate();
if (!(isIndeterminate && wasIndeterminate)) {
control.requestLayout();
}
wasIndeterminate = isIndeterminate;
}
private Transition createIndeterminateTransition() {
double minWidth = 0;
double maxWidth = fullWidth * 0.4;
Transition transition = new Transition() {
{
setInterpolator(Interpolator.LINEAR);
setCycleDuration(INDETERMINATE_DURATION);
}

private static final Duration DURATION = Duration.seconds(1);
@Override
protected void interpolate(double frac) {
double currentWidth;

private void createIndeterminateTimeline() {
clearAnimation();
ProgressIndicator control = getSkinnable();
final double w = control.getWidth() - snappedLeftInset() - snappedRightInset();
indeterminateTransition = new Timeline(
new KeyFrame(
Duration.ZERO,
new KeyValue(clip.widthProperty(), 0.0, Interpolator.EASE_IN),
new KeyValue(clip.translateXProperty(), 0, Interpolator.LINEAR)
),
new KeyFrame(
DURATION.multiply(0.5),
new KeyValue(clip.widthProperty(), w * 0.4, Interpolator.LINEAR)
),
new KeyFrame(
DURATION.multiply(0.9),
new KeyValue(clip.translateXProperty(), w, Interpolator.LINEAR)
),
new KeyFrame(
DURATION,
new KeyValue(clip.widthProperty(), 0.0, Interpolator.EASE_OUT)
));
indeterminateTransition.setCycleCount(Timeline.INDEFINITE);
if (frac <= 0.5) {
currentWidth = Interpolator.EASE_IN.interpolate(minWidth, maxWidth, frac / 0.5);
} else {
currentWidth = Interpolator.EASE_OUT.interpolate(maxWidth, minWidth, (frac - 0.5) / 0.5);
}

double targetCenter;
if (frac <= 0.1) {
targetCenter = 0.0;
} else if (frac >= 0.9) {
targetCenter = fullWidth;
} else {
targetCenter = ((frac - 0.1) / 0.8) * fullWidth;
}

clip.setWidth(currentWidth);
clip.setTranslateX(targetCenter - currentWidth / 2.0);
}
};

transition.setCycleCount(Timeline.INDEFINITE);
return transition;
}

private static final Duration DETERMINATE_DURATION = Duration.seconds(0.2);

private Timeline createDeterminateTransition(double targetProgress) {
Timeline timeline = new Timeline(
new KeyFrame(Duration.ZERO,
new KeyValue(clip.widthProperty(), clip.getWidth(), Interpolator.EASE_OUT)),
new KeyFrame(DETERMINATE_DURATION,
new KeyValue(clip.widthProperty(), computeBarWidth(targetProgress), Interpolator.EASE_OUT))
);
timeline.setOnFinished(e -> {
if (transition == timeline) {
transition = null;
}
});
return timeline;
}

private void clearAnimation() {
if (indeterminateTransition != null) {
indeterminateTransition.stop();
indeterminateTransition = null;
if (transition != null) {
transition.stop();
transition = null;
}
}

Expand All @@ -198,4 +216,10 @@ public void dispose() {
treeShowingProperty.dispose();
clearAnimation();
}

private double computeBarWidth(double progress) {
assert progress >= 0 && progress <= 1;
double barWidth = ((int) fullWidth * 2 * progress) / 2.0;
return progress > 0 ? Math.max(barWidth, DEFAULT_HEIGHT) : barWidth;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -327,8 +327,9 @@ protected void updateItem(Node item, boolean empty) {
pane.paddingProperty().unbind();
title.textProperty().unbind();
message.textProperty().unbind();
bar.progressProperty().unbind();

bar.setSmoothProgress(false);
bar.progressProperty().unbind();
StageNode prevStageNode;
if (prevStageNodeRef != null && (prevStageNode = prevStageNodeRef.get()) != null)
prevStageNode.status.removeListener(statusChangeListener);
Expand Down Expand Up @@ -366,6 +367,8 @@ protected void updateItem(Node item, boolean empty) {
pane.setRight(null);
pane.setBottom(null);
}

bar.setSmoothProgress(true);
}
}

Expand Down
5 changes: 2 additions & 3 deletions HMCL/src/main/resources/assets/css/root.css
Original file line number Diff line number Diff line change
Expand Up @@ -913,12 +913,12 @@
* *
*******************************************************************************/


.jfx-progress-bar > .track {
-fx-background-color: -monet-secondary-container;
}

.jfx-progress-bar > .bar,
.jfx-progress-bar:indeterminate > .bar {
.jfx-progress-bar > .bar {
-fx-background-color: -monet-primary;
-fx-padding: 2;
}
Expand All @@ -929,7 +929,6 @@
-fx-background-insets: 0;
}


/*******************************************************************************
* *
* JFX Textfield *
Expand Down