Improve slider
- Add control skin to support changing progress color - Implement control size support (small, medium, large)
This commit is contained in:
parent
a295fd799c
commit
ef930a7907
@ -0,0 +1,52 @@
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
package atlantafx.base.controls;
|
||||
|
||||
import javafx.geometry.Orientation;
|
||||
import javafx.scene.control.Slider;
|
||||
import javafx.scene.control.skin.SliderSkin;
|
||||
import javafx.scene.layout.StackPane;
|
||||
|
||||
/** {@link Slider} skin that supports progress color. */
|
||||
public class ProgressSliderSkin extends SliderSkin {
|
||||
|
||||
protected final StackPane thumb;
|
||||
protected final StackPane track;
|
||||
protected final StackPane progressTrack;
|
||||
|
||||
public ProgressSliderSkin(Slider slider) {
|
||||
super(slider);
|
||||
|
||||
track = (StackPane) getSkinnable().lookup(".track");
|
||||
thumb = (StackPane) getSkinnable().lookup(".thumb");
|
||||
|
||||
progressTrack = new StackPane();
|
||||
progressTrack.getStyleClass().add("progress");
|
||||
progressTrack.setMouseTransparent(true);
|
||||
|
||||
getSkinnable().getStyleClass().add("progress-slider");
|
||||
getChildren().add(getChildren().indexOf(thumb), progressTrack);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void layoutChildren(double x, double y, double w, double h) {
|
||||
super.layoutChildren(x, y, w, h);
|
||||
|
||||
double progressX, progressY, progressWidth, progressHeight;
|
||||
|
||||
// intentionally ignore background radius in calculation,
|
||||
// because slider looks better this way
|
||||
if (getSkinnable().getOrientation() == Orientation.HORIZONTAL) {
|
||||
progressX = track.getLayoutX();
|
||||
progressY = track.getLayoutY();
|
||||
progressWidth = thumb.getLayoutX() - snappedLeftInset();
|
||||
progressHeight = track.getHeight();
|
||||
} else {
|
||||
progressX = track.getLayoutX();
|
||||
progressY = thumb.getLayoutY();
|
||||
progressWidth = track.getWidth();
|
||||
progressHeight = track.getLayoutBounds().getMaxY() + track.getLayoutY() - thumb.getLayoutY() - snappedBottomInset();
|
||||
}
|
||||
|
||||
progressTrack.resizeRelocate(progressX, progressY, progressWidth, progressHeight);
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
package atlantafx.sampler.page.components;
|
||||
|
||||
import atlantafx.base.controls.ProgressSliderSkin;
|
||||
import atlantafx.base.controls.ToggleSwitch;
|
||||
import atlantafx.base.theme.Tweaks;
|
||||
import atlantafx.base.util.IntegerStringConverter;
|
||||
@ -235,6 +236,7 @@ public class OverviewPage extends AbstractPage {
|
||||
tickSlider.setMinorTickCount(5);
|
||||
tickSlider.setSnapToTicks(true);
|
||||
tickSlider.setPrefWidth(BUTTON_WIDTH * 2);
|
||||
tickSlider.setSkin(new ProgressSliderSkin(tickSlider));
|
||||
|
||||
var container = new HBox(BLOCK_HGAP, slider, tickSlider);
|
||||
return new SampleBlock("Sliders", container);
|
||||
|
@ -1,16 +1,16 @@
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
package atlantafx.sampler.page.components;
|
||||
|
||||
import atlantafx.base.controls.ProgressSliderSkin;
|
||||
import atlantafx.base.theme.Styles;
|
||||
import atlantafx.sampler.page.AbstractPage;
|
||||
import atlantafx.sampler.page.Page;
|
||||
import atlantafx.sampler.page.SampleBlock;
|
||||
import javafx.scene.control.Slider;
|
||||
import javafx.scene.layout.FlowPane;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.GridPane;
|
||||
import javafx.scene.layout.Pane;
|
||||
import javafx.scene.layout.VBox;
|
||||
|
||||
import static javafx.geometry.Orientation.HORIZONTAL;
|
||||
import static javafx.geometry.Orientation.VERTICAL;
|
||||
|
||||
public class SliderPage extends AbstractPage {
|
||||
@ -26,39 +26,87 @@ public class SliderPage extends AbstractPage {
|
||||
super();
|
||||
setUserContent(new FlowPane(
|
||||
Page.PAGE_HGAP, Page.PAGE_VGAP,
|
||||
horizontalSample(),
|
||||
verticalSample(),
|
||||
basicSample(),
|
||||
smallSample(),
|
||||
largeSample(),
|
||||
disabledSample()
|
||||
));
|
||||
}
|
||||
|
||||
private SampleBlock horizontalSample() {
|
||||
var slider = new Slider(1, 5, 3);
|
||||
slider.setOrientation(HORIZONTAL);
|
||||
private SampleBlock basicSample() {
|
||||
var hSlider = new Slider(1, 5, 3);
|
||||
|
||||
var tickSlider = createTickSlider();
|
||||
tickSlider.setMinWidth(SLIDER_SIZE);
|
||||
tickSlider.setMaxWidth(SLIDER_SIZE);
|
||||
var hTickSlider = createTickSlider();
|
||||
hTickSlider.setSkin(new ProgressSliderSkin(hTickSlider));
|
||||
|
||||
return new SampleBlock("Horizontal", new VBox(SPACING, slider, tickSlider));
|
||||
var vSlider = new Slider(1, 5, 3);
|
||||
vSlider.setOrientation(VERTICAL);
|
||||
|
||||
var vTickSlider = createTickSlider();
|
||||
vTickSlider.setOrientation(VERTICAL);
|
||||
vTickSlider.setSkin(new ProgressSliderSkin(vTickSlider));
|
||||
|
||||
return new SampleBlock("Basic", createContent(hSlider, hTickSlider, vSlider, vTickSlider));
|
||||
}
|
||||
|
||||
private Pane verticalSample() {
|
||||
var slider = new Slider(1, 5, 3);
|
||||
slider.setOrientation(VERTICAL);
|
||||
private Pane smallSample() {
|
||||
var hSlider = new Slider(1, 5, 3);
|
||||
hSlider.getStyleClass().add(Styles.SMALL);
|
||||
|
||||
var tickSlider = createTickSlider();
|
||||
tickSlider.setOrientation(VERTICAL);
|
||||
tickSlider.setMinHeight(SLIDER_SIZE);
|
||||
tickSlider.setMaxHeight(SLIDER_SIZE);
|
||||
var hTickSlider = createTickSlider();
|
||||
hTickSlider.getStyleClass().add(Styles.SMALL);
|
||||
hTickSlider.setSkin(new ProgressSliderSkin(hTickSlider));
|
||||
|
||||
return new SampleBlock("Vertical", new HBox(SPACING, slider, tickSlider));
|
||||
var vSlider = new Slider(1, 5, 3);
|
||||
vSlider.setOrientation(VERTICAL);
|
||||
vSlider.getStyleClass().add(Styles.SMALL);
|
||||
|
||||
var vTickSlider = createTickSlider();
|
||||
vTickSlider.setOrientation(VERTICAL);
|
||||
vTickSlider.getStyleClass().add(Styles.SMALL);
|
||||
vTickSlider.setSkin(new ProgressSliderSkin(vTickSlider));
|
||||
|
||||
return new SampleBlock("Small", createContent(hSlider, hTickSlider, vSlider, vTickSlider));
|
||||
}
|
||||
|
||||
private Pane largeSample() {
|
||||
var hSlider = new Slider(1, 5, 3);
|
||||
hSlider.getStyleClass().add(Styles.LARGE);
|
||||
|
||||
var hTickSlider = createTickSlider();
|
||||
hTickSlider.getStyleClass().add(Styles.LARGE);
|
||||
hTickSlider.setSkin(new ProgressSliderSkin(hTickSlider));
|
||||
|
||||
var vSlider = new Slider(1, 5, 3);
|
||||
vSlider.setOrientation(VERTICAL);
|
||||
vSlider.getStyleClass().add(Styles.LARGE);
|
||||
|
||||
var vTickSlider = createTickSlider();
|
||||
vTickSlider.setOrientation(VERTICAL);
|
||||
vTickSlider.getStyleClass().add(Styles.LARGE);
|
||||
vTickSlider.setSkin(new ProgressSliderSkin(vTickSlider));
|
||||
|
||||
return new SampleBlock("Large", createContent(hSlider, hTickSlider, vSlider, vTickSlider));
|
||||
}
|
||||
|
||||
private Pane disabledSample() {
|
||||
var disabledSlider = createTickSlider();
|
||||
disabledSlider.setDisable(true);
|
||||
return new SampleBlock("Disabled", new HBox(disabledSlider));
|
||||
var hSlider = new Slider(1, 5, 3);
|
||||
hSlider.setDisable(true);
|
||||
|
||||
var hTickSlider = createTickSlider();
|
||||
hTickSlider.setSkin(new ProgressSliderSkin(hTickSlider));
|
||||
hTickSlider.setDisable(true);
|
||||
|
||||
var vSlider = new Slider(1, 5, 3);
|
||||
vSlider.setOrientation(VERTICAL);
|
||||
vSlider.setDisable(true);
|
||||
|
||||
var vTickSlider = createTickSlider();
|
||||
vTickSlider.setOrientation(VERTICAL);
|
||||
vTickSlider.setSkin(new ProgressSliderSkin(vTickSlider));
|
||||
vTickSlider.setDisable(true);
|
||||
|
||||
return new SampleBlock("Disabled", createContent(hSlider, hTickSlider, vSlider, vTickSlider));
|
||||
}
|
||||
|
||||
private Slider createTickSlider() {
|
||||
@ -71,4 +119,24 @@ public class SliderPage extends AbstractPage {
|
||||
slider.setSnapToTicks(true);
|
||||
return slider;
|
||||
}
|
||||
|
||||
private GridPane createContent(Slider h1, Slider h2, Slider v1, Slider v2) {
|
||||
var grid = new GridPane();
|
||||
grid.setVgap(SPACING);
|
||||
grid.setHgap(SPACING);
|
||||
|
||||
h1.setPrefWidth(SLIDER_SIZE);
|
||||
h2.setPrefWidth(SLIDER_SIZE);
|
||||
|
||||
v1.setPrefHeight(SLIDER_SIZE);
|
||||
v2.setPrefHeight(SLIDER_SIZE);
|
||||
|
||||
grid.add(h1, 0, 0);
|
||||
grid.add(h2, 0, 1);
|
||||
|
||||
grid.add(v1, 1, 0, 1, GridPane.REMAINING);
|
||||
grid.add(v2, 2, 0, 1, GridPane.REMAINING);
|
||||
|
||||
return grid;
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
package atlantafx.sampler.page.showcase.musicplayer;
|
||||
|
||||
import atlantafx.base.controls.Popover;
|
||||
import atlantafx.base.controls.ProgressSliderSkin;
|
||||
import atlantafx.base.controls.Spacer;
|
||||
import javafx.beans.InvalidationListener;
|
||||
import javafx.beans.binding.Bindings;
|
||||
@ -115,6 +116,7 @@ final class PlayerPane extends VBox {
|
||||
// == TIME CONTROLS ==
|
||||
|
||||
timeSlider = new Slider(0, 1, 0);
|
||||
timeSlider.setSkin(new ProgressSliderSkin(timeSlider));
|
||||
timeSlider.getStyleClass().add("time-slider");
|
||||
timeSlider.setMinWidth(PANEL_MAX_WIDTH);
|
||||
timeSlider.setMaxWidth(PANEL_MAX_WIDTH);
|
||||
@ -144,6 +146,8 @@ final class PlayerPane extends VBox {
|
||||
shuffleBtn.setOnAction(e -> model.shuffle());
|
||||
|
||||
volumeSlider = new Slider(0, 1, 0.75);
|
||||
volumeSlider.setSkin(new ProgressSliderSkin(volumeSlider));
|
||||
volumeSlider.getStyleClass().add(SMALL);
|
||||
volumeSlider.setOrientation(VERTICAL);
|
||||
|
||||
var volumeBar = new VBox(5);
|
||||
@ -169,22 +173,7 @@ final class PlayerPane extends VBox {
|
||||
setAlignment(CENTER);
|
||||
setSpacing(5);
|
||||
setMinWidth(300);
|
||||
getChildren().setAll(
|
||||
new Spacer(VERTICAL),
|
||||
new StackPane(coverImage),
|
||||
new Spacer(10, VERTICAL),
|
||||
trackTitle,
|
||||
trackArtist,
|
||||
trackAlbum,
|
||||
new Spacer(20, VERTICAL),
|
||||
mediaControls,
|
||||
new Spacer(10, VERTICAL),
|
||||
timeSlider,
|
||||
timeMarkersBox,
|
||||
new Spacer(10, VERTICAL),
|
||||
extraControls,
|
||||
new Spacer(VERTICAL)
|
||||
);
|
||||
getChildren().setAll(new Spacer(VERTICAL), new StackPane(coverImage), new Spacer(10, VERTICAL), trackTitle, trackArtist, trackAlbum, new Spacer(20, VERTICAL), mediaControls, new Spacer(10, VERTICAL), timeSlider, timeMarkersBox, new Spacer(10, VERTICAL), extraControls, new Spacer(VERTICAL));
|
||||
}
|
||||
|
||||
private void init() {
|
||||
|
@ -3,61 +3,77 @@
|
||||
@use "../settings/config" as cfg;
|
||||
@use "sass:math";
|
||||
|
||||
$color-thumb: if(cfg.$darkMode, -color-fg-default, -color-accent-emphasis) !default;
|
||||
$color-thumb-border: if(cfg.$darkMode, -color-fg-default, -color-accent-emphasis) !default;
|
||||
$color-track: -color-accent-emphasis !default;
|
||||
$color-tick: -color-fg-muted !default;
|
||||
$color-thumb: if(cfg.$darkMode, -color-fg-default, -color-accent-emphasis) !default;
|
||||
$color-thumb-border: $color-thumb !default;
|
||||
$color-track: -color-border-muted !default;
|
||||
$color-track-progress: -color-accent-emphasis !default;
|
||||
$color-tick: -color-fg-muted !default;
|
||||
|
||||
$thumb-size: 8px !default;
|
||||
$thumb-border-width: 2px !default;
|
||||
// this is padding ...
|
||||
$thumb-size: (
|
||||
"small": 8px,
|
||||
"medium": 10px,
|
||||
"large": 12px
|
||||
) !default;
|
||||
|
||||
$track-size: $thumb-size !default; // visual track height (or width)
|
||||
$track-margin: 6px !default; // increases clickable track area
|
||||
// ... in combination with radius it can be converted to square or rectange
|
||||
$thumb-radius: 10em !default;
|
||||
|
||||
$tick-major-size: 5px !default;
|
||||
$tick-minor-size: 3px !default;
|
||||
// visual track height (or width)
|
||||
$track-size: (
|
||||
"small": 2px,
|
||||
"medium": 4px,
|
||||
"large": 12px
|
||||
) !default;
|
||||
|
||||
$_track-padding: math.div($track-size + $track-margin, 2);
|
||||
$track-radius: cfg.$border-radius !default;
|
||||
|
||||
$tick-major-size: 5px !default;
|
||||
$tick-minor-size: 3px !default;
|
||||
|
||||
.slider {
|
||||
|
||||
-color-slider-thumb: $color-thumb;
|
||||
-color-slider-thumb-border: $color-thumb-border;
|
||||
-color-slider-track: $color-track;
|
||||
-color-slider-tick: $color-tick;
|
||||
-color-slider-thumb: $color-thumb;
|
||||
-color-slider-thumb-border: $color-thumb-border;
|
||||
-color-slider-track: $color-track;
|
||||
-color-slider-track-progress: $color-track-progress;
|
||||
-color-slider-tick: $color-tick;
|
||||
|
||||
&.large {
|
||||
-color-slider-thumb: if(cfg.$darkMode, $color-thumb, -color-fg-emphasis);
|
||||
-color-slider-thumb-border: if(cfg.$darkMode, $color-thumb, -color-accent-emphasis);
|
||||
}
|
||||
|
||||
>.thumb {
|
||||
-fx-background-color: -color-slider-thumb-border, -color-slider-thumb;
|
||||
-fx-background-insets: 0, 2px;
|
||||
-fx-background-radius: 50;
|
||||
-fx-padding: $thumb-size;
|
||||
-fx-background-radius: $thumb-radius;
|
||||
-fx-padding: map-get($thumb-size, "medium");
|
||||
}
|
||||
|
||||
&.small {
|
||||
>.thumb {
|
||||
-fx-padding: map-get($thumb-size, "small");
|
||||
}
|
||||
}
|
||||
|
||||
&.large {
|
||||
>.thumb {
|
||||
-fx-padding: map-get($thumb-size, "large");
|
||||
}
|
||||
}
|
||||
|
||||
>.track {
|
||||
// transparent background increases clickable track area without increasing visual track height,
|
||||
// it's also used to center track with thumb
|
||||
-fx-background-color: transparent, -color-slider-track;
|
||||
-fx-background-radius: cfg.$border-radius;
|
||||
-fx-background-radius: $track-radius;
|
||||
}
|
||||
|
||||
// center thumb over track horizontally
|
||||
&:horizontal {
|
||||
>.track {
|
||||
-fx-padding: $_track-padding 0 $_track-padding 0;
|
||||
-fx-background-insets: 0, $track-margin 0 $track-margin 0;
|
||||
}
|
||||
}
|
||||
|
||||
// center thumb over track vertically
|
||||
&:vertical {
|
||||
>.track {
|
||||
-fx-padding: 0 $_track-padding 0 $_track-padding;
|
||||
-fx-background-insets: 0, 0 $track-margin 0 $track-margin;
|
||||
}
|
||||
>.progress {
|
||||
-fx-background-color: transparent, -color-slider-track-progress;
|
||||
}
|
||||
|
||||
// there's slightly noticable difference between axis length and track length,
|
||||
// wontfix this via CSS, because it's probably JavaFX calc problem
|
||||
// because SliderSkin ignores track radius in layoutChildren()
|
||||
>.axis {
|
||||
-fx-tick-label-fill: -color-slider-tick;
|
||||
-fx-tick-length: $tick-major-size;
|
||||
@ -72,4 +88,88 @@ $_track-padding: math.div($track-size + $track-margin, 2);
|
||||
&:disabled {
|
||||
-fx-opacity: cfg.$opacity-disabled;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////
|
||||
// Horizontal //
|
||||
/////////////////////////////////////////////////////////
|
||||
|
||||
// center thumb over track horizontally
|
||||
&:horizontal {
|
||||
>.track {
|
||||
-fx-padding: map-get($thumb-size, "medium") 0 map-get($thumb-size, "medium") 0;
|
||||
-fx-background-insets: 0, calc(map-get($thumb-size, "medium") - map-get($track-size, "medium")) 0
|
||||
calc(map-get($thumb-size, "medium") - map-get($track-size, "medium")) 0;
|
||||
}
|
||||
>.progress {
|
||||
-fx-background-radius: $track-radius;
|
||||
-fx-background-insets: 0, calc(map-get($thumb-size, "medium") - map-get($track-size, "medium")) 0
|
||||
calc(map-get($thumb-size, "medium") - map-get($track-size, "medium")) 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.small:horizontal {
|
||||
>.track {
|
||||
-fx-padding: map-get($thumb-size, "small") 0 map-get($thumb-size, "small") 0;
|
||||
-fx-background-insets: 0, calc(map-get($thumb-size, "small") - map-get($track-size, "small")) 0
|
||||
calc(map-get($thumb-size, "small") - map-get($track-size, "small")) 0;
|
||||
}
|
||||
>.progress {
|
||||
-fx-padding: map-get($thumb-size, "small") 0 map-get($thumb-size, "small") 0;
|
||||
-fx-background-insets: 0, calc(map-get($thumb-size, "small") - map-get($track-size, "small")) 0
|
||||
calc(map-get($thumb-size, "small") - map-get($track-size, "small")) 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.large:horizontal {
|
||||
>.track {
|
||||
-fx-padding: map-get($track-size, "large") 0 map-get($track-size, "large") 0;
|
||||
-fx-background-insets: 0;
|
||||
}
|
||||
>.progress {
|
||||
-fx-padding: map-get($track-size, "large") 0 map-get($track-size, "large") 0;
|
||||
-fx-background-insets: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////
|
||||
// Vertical //
|
||||
/////////////////////////////////////////////////////////
|
||||
|
||||
// center thumb over track vertically
|
||||
&:vertical {
|
||||
>.track {
|
||||
-fx-padding: 0 map-get($thumb-size, "medium") 0 map-get($thumb-size, "medium");
|
||||
-fx-background-insets: 0, 0 calc(map-get($thumb-size, "medium") - map-get($track-size, "medium"))
|
||||
0 calc(map-get($thumb-size, "medium") - map-get($track-size, "medium"));
|
||||
}
|
||||
>.progress {
|
||||
-fx-background-radius: $thumb-radius $thumb-radius $track-radius $track-radius;
|
||||
-fx-background-insets: 0, 0 calc(map-get($thumb-size, "medium") - map-get($track-size, "medium"))
|
||||
0 calc(map-get($thumb-size, "medium") - map-get($track-size, "medium"));
|
||||
}
|
||||
}
|
||||
|
||||
&.small:vertical {
|
||||
>.track {
|
||||
-fx-padding: 0 map-get($thumb-size, "small") 0 map-get($thumb-size, "small");
|
||||
-fx-background-insets: 0, 0 calc(map-get($thumb-size, "small") - map-get($track-size, "small"))
|
||||
0 calc(map-get($thumb-size, "small") - map-get($track-size, "small"));
|
||||
}
|
||||
>.progress {
|
||||
-fx-padding: map-get($thumb-size, "small") 0 map-get($thumb-size, "small") 0;
|
||||
-fx-background-insets: 0, 0 calc(map-get($thumb-size, "small") - map-get($track-size, "small"))
|
||||
0 calc(map-get($thumb-size, "small") - map-get($track-size, "small"));
|
||||
}
|
||||
}
|
||||
|
||||
&.large:vertical {
|
||||
>.track {
|
||||
-fx-padding: 0 map-get($track-size, "large") 0 map-get($track-size, "large");
|
||||
-fx-background-insets: 0;
|
||||
}
|
||||
>.progress {
|
||||
-fx-padding: 0 map-get($track-size, "large") 0 map-get($track-size, "large");
|
||||
-fx-background-insets: 0;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user