Add toggle group support for ToggleSwitch

This commit is contained in:
mkpaz 2023-05-22 13:54:36 +04:00
parent 113b3e9c77
commit 9c5936515c
2 changed files with 125 additions and 7 deletions

@ -35,6 +35,7 @@ import java.util.List;
import javafx.beans.property.BooleanProperty; import javafx.beans.property.BooleanProperty;
import javafx.beans.property.BooleanPropertyBase; import javafx.beans.property.BooleanPropertyBase;
import javafx.beans.property.ObjectProperty; import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ObjectPropertyBase;
import javafx.beans.value.WritableValue; import javafx.beans.value.WritableValue;
import javafx.css.CssMetaData; import javafx.css.CssMetaData;
import javafx.css.PseudoClass; import javafx.css.PseudoClass;
@ -44,11 +45,14 @@ import javafx.css.StyleableProperty;
import javafx.css.converter.EnumConverter; import javafx.css.converter.EnumConverter;
import javafx.event.ActionEvent; import javafx.event.ActionEvent;
import javafx.geometry.HorizontalDirection; import javafx.geometry.HorizontalDirection;
import javafx.scene.AccessibleAttribute;
import javafx.scene.control.Labeled; import javafx.scene.control.Labeled;
import javafx.scene.control.Skin; import javafx.scene.control.Skin;
import javafx.scene.control.Toggle;
import javafx.scene.control.ToggleGroup;
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class ToggleSwitch extends Labeled { public class ToggleSwitch extends Labeled implements Toggle {
protected static final String DEFAULT_STYLE_CLASS = "toggle-switch"; 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_SELECTED = PseudoClass.getPseudoClass("selected");
@ -96,6 +100,39 @@ public class ToggleSwitch extends Labeled {
if (selected == null) { if (selected == null) {
selected = new BooleanPropertyBase() { selected = new BooleanPropertyBase() {
@Override
protected void invalidated() {
final boolean selected = get();
final ToggleGroup tg = getToggleGroup();
pseudoClassStateChanged(PSEUDO_CLASS_SELECTED, selected);
notifyAccessibleAttributeChanged(AccessibleAttribute.SELECTED);
if (tg != null) {
if (selected) {
tg.selectToggle(ToggleSwitch.this);
} else if (tg.getSelectedToggle() == ToggleSwitch.this) {
// This code was copied from ToggleButton, and originally
// it should use the following method, which is like almost
// everything in JavaFX is private. Probably it fixes some
// internal toggle group state.
// tg.clearSelectedToggle();
// This is kind of an equivalent code even though
// "!tg.getSelectedToggle().isSelected()"
// looks like absurd and should always return false.
if (!tg.getSelectedToggle().isSelected()) {
for (Toggle toggle: tg.getToggles()) {
if (toggle.isSelected()) {
return;
}
}
}
tg.selectToggle(null);
}
}
}
@Override @Override
public Object getBean() { public Object getBean() {
return ToggleSwitch.this; return ToggleSwitch.this;
@ -105,18 +142,65 @@ public class ToggleSwitch extends Labeled {
public String getName() { public String getName() {
return "selected"; return "selected";
} }
@Override
protected void invalidated() {
final boolean v = get();
pseudoClassStateChanged(PSEUDO_CLASS_SELECTED, v);
}
}; };
} }
return selected; return selected;
} }
/**
* The {@link ToggleGroup} to which this {@code ToggleSwitch} belongs.
* A {@code ToggleSwitch} can only be in one group at any one time. If the
* group is changed, then the button is removed from the old group prior to
* being added to the new group.
*/
private ObjectProperty<ToggleGroup> toggleGroup;
@Override
public final void setToggleGroup(ToggleGroup value) {
toggleGroupProperty().set(value);
}
@Override
public final ToggleGroup getToggleGroup() {
return toggleGroup == null ? null : toggleGroup.get();
}
@Override
public final ObjectProperty<ToggleGroup> toggleGroupProperty() {
if (toggleGroup == null) {
toggleGroup = new ObjectPropertyBase<>() {
private ToggleGroup old;
@Override
protected void invalidated() {
final ToggleGroup tg = get();
if (tg != null && !tg.getToggles().contains(ToggleSwitch.this)) {
if (old != null) {
old.getToggles().remove(ToggleSwitch.this);
}
tg.getToggles().add(ToggleSwitch.this);
} else if (tg == null) {
old.getToggles().remove(ToggleSwitch.this);
}
old = tg;
}
@Override
public Object getBean() {
return ToggleSwitch.this;
}
@Override
public String getName() {
return "toggleGroup";
}
};
}
return toggleGroup;
}
// ~ // ~
private ObjectProperty<HorizontalDirection> labelPosition; private ObjectProperty<HorizontalDirection> labelPosition;

@ -11,6 +11,9 @@ import atlantafx.sampler.page.Snippet;
import java.net.URI; import java.net.URI;
import javafx.geometry.HorizontalDirection; import javafx.geometry.HorizontalDirection;
import javafx.geometry.Pos; import javafx.geometry.Pos;
import javafx.scene.control.Label;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox; import javafx.scene.layout.HBox;
public final class ToggleSwitchPage extends OutlinePage { public final class ToggleSwitchPage extends OutlinePage {
@ -38,6 +41,7 @@ public final class ToggleSwitchPage extends OutlinePage {
); );
addSection("Usage", usageExample()); addSection("Usage", usageExample());
addSection("Color", colorExample()); addSection("Color", colorExample());
addSection("Toggle Group", toggleGroupExample());
} }
private ExampleBox usageExample() { private ExampleBox usageExample() {
@ -97,4 +101,34 @@ public final class ToggleSwitchPage extends OutlinePage {
return new ExampleBox(box, new Snippet(getClass(), 2), description); return new ExampleBox(box, new Snippet(getClass(), 2), description);
} }
private ExampleBox toggleGroupExample() {
//snippet_3:start
var group = new ToggleGroup();
var toggle1 = new ToggleSwitch();
toggle1.setToggleGroup(group);
toggle1.setSelected(true);
var toggle2 = new ToggleSwitch();
toggle2.setToggleGroup(group);
var toggle3 = new ToggleSwitch();
toggle3.setToggleGroup(group);
//snippet_3:end
var grid = new GridPane();
grid.setHgap(HGAP_20);
grid.setVgap(VGAP_10);
grid.addRow(0, new Label("Option 1"), toggle1);
grid.addRow(1, new Label("Option 2"), toggle2);
grid.addRow(2, new Label("Option 3"), toggle3);
var description = BBCodeParser.createFormattedText("""
Toggles can optionally be combined into a group where only one switch \
at a time can be selected."""
);
return new ExampleBox(grid, new Snippet(getClass(), 3), description);
}
} }