Add label position support to toggle switch
This commit is contained in:
parent
dca4d7c21e
commit
c40ec4e3b3
@ -12,13 +12,17 @@
|
||||
|
||||
### Improvements
|
||||
|
||||
- (Base) Improved Javadoc. See full API reference in [docs](https://mkpaz.github.io/atlantafx/apidocs/atlantafx.base/module-summary.html).
|
||||
- (Base) `ToggleSwitch` label position support (left or right).
|
||||
- (CSS) `Button` shadow support (`-color-button-shadow`). Only for themes compiled with the `button.$use-shadow` flag enabled.
|
||||
- (CSS) Looked-up color variables support: `Separator`.
|
||||
- (CSS) Added border radius/shadow to popup menu for `ComboBox` (and all `ComboBox`-based) controls.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
- (CSS) Added border radius/shadow to popup menu for `ComboBox` (and `ComboBox`-based) controls.
|
||||
- (Base) Fixed incorrect `Slider` progress track length calculation.
|
||||
- (CSS) Fixed `Popover` arrow background color.
|
||||
- (CSS) Fixed `ListView` with `.bordered` class displays borders on empty cells.
|
||||
|
||||
## [1.2.0] - 2023-02-11
|
||||
|
||||
|
@ -249,8 +249,9 @@ public class RingProgressIndicatorSkin extends SkinBase<RingProgressIndicator> {
|
||||
private static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES;
|
||||
|
||||
static {
|
||||
final List<CssMetaData<? extends Styleable, ?>> styleables =
|
||||
new ArrayList<>(SkinBase.getClassCssMetaData());
|
||||
final List<CssMetaData<? extends Styleable, ?>> styleables = new ArrayList<>(
|
||||
SkinBase.getClassCssMetaData()
|
||||
);
|
||||
styleables.add(INDETERMINATE_ANIMATION_TIME);
|
||||
STYLEABLES = Collections.unmodifiableList(styleables);
|
||||
}
|
||||
|
@ -29,10 +29,21 @@
|
||||
|
||||
package atlantafx.base.controls;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.BooleanPropertyBase;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.value.WritableValue;
|
||||
import javafx.css.CssMetaData;
|
||||
import javafx.css.PseudoClass;
|
||||
import javafx.css.Styleable;
|
||||
import javafx.css.StyleableObjectProperty;
|
||||
import javafx.css.StyleableProperty;
|
||||
import javafx.css.converter.EnumConverter;
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.geometry.HorizontalDirection;
|
||||
import javafx.scene.control.Labeled;
|
||||
import javafx.scene.control.Skin;
|
||||
|
||||
@ -41,6 +52,7 @@ public class ToggleSwitch extends Labeled {
|
||||
|
||||
protected static final String DEFAULT_STYLE_CLASS = "toggle-switch";
|
||||
protected static final PseudoClass PSEUDO_CLASS_SELECTED = PseudoClass.getPseudoClass("selected");
|
||||
protected static final PseudoClass PSEUDO_CLASS_RIGHT = PseudoClass.getPseudoClass("right");
|
||||
|
||||
/**
|
||||
* Creates a toggle switch with empty string for its label.
|
||||
@ -67,36 +79,22 @@ public class ToggleSwitch extends Labeled {
|
||||
// Properties //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*
|
||||
* Indicates whether this switch is selected.
|
||||
*/
|
||||
private BooleanProperty selected;
|
||||
|
||||
/**
|
||||
* Sets the selected value.
|
||||
*/
|
||||
public final void setSelected(boolean value) {
|
||||
selectedProperty().set(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this Toggle Switch is selected.
|
||||
*/
|
||||
public final boolean isSelected() {
|
||||
return selected != null && selected.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the selected property.
|
||||
* Returns whether this Toggle Switch is selected.
|
||||
*/
|
||||
public final BooleanProperty selectedProperty() {
|
||||
if (selected == null) {
|
||||
selected = new BooleanPropertyBase() {
|
||||
@Override
|
||||
protected void invalidated() {
|
||||
final boolean v = get();
|
||||
pseudoClassStateChanged(PSEUDO_CLASS_SELECTED, v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getBean() {
|
||||
@ -107,12 +105,67 @@ public class ToggleSwitch extends Labeled {
|
||||
public String getName() {
|
||||
return "selected";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void invalidated() {
|
||||
final boolean v = get();
|
||||
pseudoClassStateChanged(PSEUDO_CLASS_SELECTED, v);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return selected;
|
||||
}
|
||||
|
||||
// ~
|
||||
|
||||
private ObjectProperty<HorizontalDirection> labelPosition;
|
||||
|
||||
public final void setLabelPosition(HorizontalDirection pos) {
|
||||
labelPositionProperty().setValue(pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this Toggle Switch is selected.
|
||||
*/
|
||||
public final HorizontalDirection getLabelPosition() {
|
||||
return labelPosition == null ? HorizontalDirection.LEFT : labelPosition.getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the side where {@link #textProperty()} values should be placed.
|
||||
* Default is {@link HorizontalDirection#LEFT}.
|
||||
*/
|
||||
public final ObjectProperty<HorizontalDirection> labelPositionProperty() {
|
||||
if (labelPosition == null) {
|
||||
labelPosition = new StyleableObjectProperty<>(HorizontalDirection.LEFT) {
|
||||
|
||||
@Override
|
||||
public Object getBean() {
|
||||
return ToggleSwitch.this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "labelPosition";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void invalidated() {
|
||||
final HorizontalDirection v = get();
|
||||
pseudoClassStateChanged(ToggleSwitch.PSEUDO_CLASS_RIGHT, v == HorizontalDirection.RIGHT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CssMetaData<ToggleSwitch, HorizontalDirection> getCssMetaData() {
|
||||
return StyleableProperties.LABEL_POSITION;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return labelPosition;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Methods //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
@ -135,4 +188,43 @@ public class ToggleSwitch extends Labeled {
|
||||
protected Skin<?> createDefaultSkin() {
|
||||
return new ToggleSwitchSkin(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public List<CssMetaData<? extends Styleable, ?>> getControlCssMetaData() {
|
||||
return StyleableProperties.STYLEABLES;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Styleable Properties //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private static class StyleableProperties {
|
||||
|
||||
private static final CssMetaData<ToggleSwitch, HorizontalDirection> LABEL_POSITION = new CssMetaData<>(
|
||||
"-fx-label-position", new EnumConverter<>(HorizontalDirection.class), HorizontalDirection.LEFT
|
||||
) {
|
||||
|
||||
@Override
|
||||
public boolean isSettable(ToggleSwitch c) {
|
||||
return c.labelPositionProperty() == null || !c.labelPositionProperty().isBound();
|
||||
}
|
||||
|
||||
@Override
|
||||
public StyleableProperty<HorizontalDirection> getStyleableProperty(ToggleSwitch c) {
|
||||
var val = (WritableValue<HorizontalDirection>) c.labelPositionProperty();
|
||||
return (StyleableProperty<HorizontalDirection>) val;
|
||||
}
|
||||
};
|
||||
|
||||
private static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES;
|
||||
|
||||
static {
|
||||
final List<CssMetaData<? extends Styleable, ?>> styleables = new ArrayList<>(Labeled.getClassCssMetaData());
|
||||
styleables.add(LABEL_POSITION);
|
||||
STYLEABLES = Collections.unmodifiableList(styleables);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -41,6 +41,7 @@ import javafx.css.Styleable;
|
||||
import javafx.css.StyleableDoubleProperty;
|
||||
import javafx.css.StyleableProperty;
|
||||
import javafx.css.converter.SizeConverter;
|
||||
import javafx.geometry.HorizontalDirection;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.SkinBase;
|
||||
@ -49,6 +50,8 @@ import javafx.util.Duration;
|
||||
|
||||
public class ToggleSwitchSkin extends SkinBase<ToggleSwitch> {
|
||||
|
||||
protected static final Duration DEFAULT_ANIMATION_TIME = Duration.millis(200);
|
||||
|
||||
protected final StackPane thumb;
|
||||
protected final StackPane thumbArea;
|
||||
protected final Label label;
|
||||
@ -59,20 +62,24 @@ public class ToggleSwitchSkin extends SkinBase<ToggleSwitch> {
|
||||
super(control);
|
||||
|
||||
thumb = new StackPane();
|
||||
thumb.getStyleClass().setAll("thumb");
|
||||
|
||||
thumbArea = new StackPane();
|
||||
thumbArea.getStyleClass().setAll("thumb-area");
|
||||
|
||||
label = new Label();
|
||||
|
||||
labelContainer = new StackPane();
|
||||
labelContainer.getStyleClass().add("label-container");
|
||||
transition = new TranslateTransition(Duration.millis(getThumbMoveAnimationTime()), thumb);
|
||||
|
||||
transition = new TranslateTransition(DEFAULT_ANIMATION_TIME, thumb);
|
||||
transition.setFromX(0.0);
|
||||
|
||||
label.textProperty().bind(control.textProperty());
|
||||
getChildren().addAll(labelContainer, thumbArea, thumb);
|
||||
labelContainer.getChildren().addAll(label);
|
||||
StackPane.setAlignment(label, Pos.CENTER_LEFT);
|
||||
|
||||
thumb.getStyleClass().setAll("thumb");
|
||||
thumbArea.getStyleClass().setAll("thumb-area");
|
||||
labelContainer.getChildren().addAll(label);
|
||||
getChildren().addAll(labelContainer, thumbArea, thumb);
|
||||
|
||||
thumbArea.setOnMouseReleased(event -> mousePressedOnToggleSwitch(control));
|
||||
thumb.setOnMouseReleased(event -> mousePressedOnToggleSwitch(control));
|
||||
@ -109,7 +116,7 @@ public class ToggleSwitchSkin extends SkinBase<ToggleSwitch> {
|
||||
|
||||
private DoubleProperty thumbMoveAnimationTimeProperty() {
|
||||
if (thumbMoveAnimationTime == null) {
|
||||
thumbMoveAnimationTime = new StyleableDoubleProperty(200) {
|
||||
thumbMoveAnimationTime = new StyleableDoubleProperty(DEFAULT_ANIMATION_TIME.toMillis()) {
|
||||
|
||||
@Override
|
||||
public Object getBean() {
|
||||
@ -121,22 +128,28 @@ public class ToggleSwitchSkin extends SkinBase<ToggleSwitch> {
|
||||
return "thumbMoveAnimationTime";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void invalidated() {
|
||||
// update duration value
|
||||
transition.setDuration(Duration.millis(getValue()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CssMetaData<ToggleSwitch, Number> getCssMetaData() {
|
||||
return THUMB_MOVE_ANIMATION_TIME;
|
||||
return StyleableProperties.THUMB_MOVE_ANIMATION_TIME;
|
||||
}
|
||||
};
|
||||
}
|
||||
return thumbMoveAnimationTime;
|
||||
}
|
||||
|
||||
protected double getThumbMoveAnimationTime() {
|
||||
return thumbMoveAnimationTime == null ? 200 : thumbMoveAnimationTime.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected void layoutChildren(double contentX, double contentY, double contentWidth, double contentHeight) {
|
||||
ToggleSwitch toggleSwitch = getSkinnable();
|
||||
ToggleSwitch c = getSkinnable();
|
||||
|
||||
double thumbWidth = snapSizeX(thumb.prefWidth(-1));
|
||||
double thumbHeight = snapSizeX(thumb.prefHeight(-1));
|
||||
thumb.resize(thumbWidth, thumbHeight);
|
||||
@ -144,14 +157,18 @@ public class ToggleSwitchSkin extends SkinBase<ToggleSwitch> {
|
||||
double thumbAreaWidth = snapSizeX(thumbArea.prefWidth(-1));
|
||||
double thumbAreaHeight = snapSizeX(thumbArea.prefHeight(-1));
|
||||
double thumbAreaY = snapPositionX(contentY + (contentHeight / 2) - (thumbAreaHeight / 2));
|
||||
double labelContainerWidth = label.getText() != null && !label.getText().isEmpty()
|
||||
? contentWidth - thumbAreaWidth : 0;
|
||||
|
||||
double labelWidth = label.getText() != null && !label.getText().isEmpty() ? contentWidth - thumbAreaWidth : 0;
|
||||
double labelX = c.getLabelPosition() == HorizontalDirection.RIGHT ? thumbAreaWidth : 0;
|
||||
|
||||
double thumbAreaX = c.getLabelPosition() == HorizontalDirection.RIGHT ? 0 : labelWidth;
|
||||
|
||||
thumbArea.resize(thumbAreaWidth, thumbAreaHeight);
|
||||
thumbArea.setLayoutX(labelContainerWidth);
|
||||
thumbArea.setLayoutX(thumbAreaX);
|
||||
thumbArea.setLayoutY(thumbAreaY);
|
||||
|
||||
labelContainer.resize(labelContainerWidth, thumbAreaHeight);
|
||||
labelContainer.resize(labelWidth, thumbAreaHeight);
|
||||
labelContainer.setLayoutX(labelX);
|
||||
labelContainer.setLayoutY(thumbAreaY);
|
||||
|
||||
// layout the thumb on the "unselected" position
|
||||
@ -169,84 +186,104 @@ public class ToggleSwitchSkin extends SkinBase<ToggleSwitch> {
|
||||
transition.playFrom(currentTime);
|
||||
} else {
|
||||
// if the transition is not running, simply apply the translateX value
|
||||
thumb.setTranslateX(toggleSwitch.isSelected() ? thumbTarget : 0.0);
|
||||
thumb.setTranslateX(c.isSelected() ? thumbTarget : 0.0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected double computeMinWidth(double height, double topInset, double rightInset,
|
||||
double bottomInset, double leftInset) {
|
||||
return leftInset + label.prefWidth(-1) + thumbArea.prefWidth(-1) + rightInset;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected double computeMinHeight(double width, double topInset, double rightInset,
|
||||
double bottomInset, double leftInset) {
|
||||
return topInset + Math.max(thumb.prefHeight(-1), label.prefHeight(-1)) + bottomInset;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected double computePrefWidth(double height, double topInset, double rightInset,
|
||||
double bottomInset, double leftInset) {
|
||||
return leftInset + label.prefWidth(-1) + 1 + thumbArea.prefWidth(-1) + rightInset;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected double computePrefHeight(double width, double topInset, double rightInset,
|
||||
double bottomInset, double leftInset) {
|
||||
return computeMinHeight(width, topInset, rightInset, bottomInset, leftInset);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@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);
|
||||
}
|
||||
|
||||
private static final CssMetaData<ToggleSwitch, Number> THUMB_MOVE_ANIMATION_TIME =
|
||||
new CssMetaData<>("-fx-thumb-move-animation-time", SizeConverter.getInstance(), 200) {
|
||||
|
||||
@Override
|
||||
public boolean isSettable(ToggleSwitch toggleSwitch) {
|
||||
final ToggleSwitchSkin skin = (ToggleSwitchSkin) toggleSwitch.getSkin();
|
||||
return skin.thumbMoveAnimationTime == null || skin.thumbMoveAnimationTime.isBound();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("RedundantCast")
|
||||
public StyleableProperty<Number> getStyleableProperty(ToggleSwitch toggleSwitch) {
|
||||
final ToggleSwitchSkin skin = (ToggleSwitchSkin) toggleSwitch.getSkin();
|
||||
return (StyleableProperty<Number>) (WritableValue<Number>) skin.thumbMoveAnimationTimeProperty();
|
||||
}
|
||||
};
|
||||
|
||||
private static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES;
|
||||
|
||||
static {
|
||||
final List<CssMetaData<? extends Styleable, ?>> styleables = new ArrayList<>(SkinBase.getClassCssMetaData());
|
||||
styleables.add(THUMB_MOVE_ANIMATION_TIME);
|
||||
STYLEABLES = Collections.unmodifiableList(styleables);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the CssMetaData associated with this class, which may include the
|
||||
* CssMetaData of its super classes.
|
||||
*/
|
||||
public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() {
|
||||
return STYLEABLES;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public List<CssMetaData<? extends Styleable, ?>> getCssMetaData() {
|
||||
return getClassCssMetaData();
|
||||
return ToggleSwitchSkin.StyleableProperties.STYLEABLES;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Styleable Properties //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static class StyleableProperties {
|
||||
|
||||
private static final CssMetaData<ToggleSwitch, Number> THUMB_MOVE_ANIMATION_TIME = new CssMetaData<>(
|
||||
"-fx-thumb-move-animation-time", SizeConverter.getInstance(), DEFAULT_ANIMATION_TIME.toMillis()
|
||||
) {
|
||||
|
||||
@Override
|
||||
public boolean isSettable(ToggleSwitch toggleSwitch) {
|
||||
final var skin = (ToggleSwitchSkin) toggleSwitch.getSkin();
|
||||
return skin.thumbMoveAnimationTime == null || !skin.thumbMoveAnimationTime.isBound();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("RedundantCast")
|
||||
public StyleableProperty<Number> getStyleableProperty(ToggleSwitch toggleSwitch) {
|
||||
final var skin = (ToggleSwitchSkin) toggleSwitch.getSkin();
|
||||
return (StyleableProperty<Number>) (WritableValue<Number>) skin.thumbMoveAnimationTimeProperty();
|
||||
}
|
||||
};
|
||||
|
||||
private static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES;
|
||||
|
||||
static {
|
||||
final List<CssMetaData<? extends Styleable, ?>> styleables = new ArrayList<>(
|
||||
SkinBase.getClassCssMetaData()
|
||||
);
|
||||
styleables.add(THUMB_MOVE_ANIMATION_TIME);
|
||||
STYLEABLES = Collections.unmodifiableList(styleables);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,9 @@ import atlantafx.base.controls.ToggleSwitch;
|
||||
import atlantafx.sampler.page.AbstractPage;
|
||||
import atlantafx.sampler.page.Page;
|
||||
import atlantafx.sampler.page.SampleBlock;
|
||||
import javafx.geometry.HorizontalDirection;
|
||||
import javafx.scene.layout.FlowPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
|
||||
public class ToggleSwitchPage extends AbstractPage {
|
||||
|
||||
@ -26,9 +28,19 @@ public class ToggleSwitchPage extends AbstractPage {
|
||||
}
|
||||
|
||||
private SampleBlock basicSample() {
|
||||
var toggle = new ToggleSwitch();
|
||||
toggle.selectedProperty().addListener((obs, old, val) -> toggle.setText(val ? "Disable" : "Enable"));
|
||||
toggle.setSelected(true);
|
||||
return new SampleBlock("Basic", toggle);
|
||||
var leftToggle = new ToggleSwitch("Enable");
|
||||
leftToggle.selectedProperty().addListener(
|
||||
(obs, old, val) -> leftToggle.setText(val ? "Enabled" : "Disabled")
|
||||
);
|
||||
leftToggle.setSelected(true);
|
||||
|
||||
var rightToggle = new ToggleSwitch("Disable");
|
||||
rightToggle.selectedProperty().addListener(
|
||||
(obs, old, val) -> rightToggle.setText(val ? "Enabled" : "Disabled")
|
||||
);
|
||||
rightToggle.setLabelPosition(HorizontalDirection.RIGHT);
|
||||
rightToggle.setSelected(false);
|
||||
|
||||
return new SampleBlock("Basic", new VBox(SampleBlock.BLOCK_VGAP, leftToggle, rightToggle));
|
||||
}
|
||||
}
|
||||
|
@ -62,6 +62,14 @@ $thumb-area-padding: 0.85em 1.4em 0.85em 1.4em !default;
|
||||
}
|
||||
}
|
||||
|
||||
&:right {
|
||||
>.label-container {
|
||||
>.label {
|
||||
-fx-padding: cfg.$checkbox-label-padding 0 cfg.$checkbox-label-padding cfg.$graphic-gap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
-fx-opacity: cfg.$opacity-disabled;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user