From 9c5936515cdbf5176a97c2ad2c24c8ab36bb899d Mon Sep 17 00:00:00 2001 From: mkpaz Date: Mon, 22 May 2023 13:54:36 +0400 Subject: [PATCH] Add toggle group support for ToggleSwitch --- .../atlantafx/base/controls/ToggleSwitch.java | 98 +++++++++++++++++-- .../page/components/ToggleSwitchPage.java | 34 +++++++ 2 files changed, 125 insertions(+), 7 deletions(-) diff --git a/base/src/main/java/atlantafx/base/controls/ToggleSwitch.java b/base/src/main/java/atlantafx/base/controls/ToggleSwitch.java index 939e047..d245860 100755 --- a/base/src/main/java/atlantafx/base/controls/ToggleSwitch.java +++ b/base/src/main/java/atlantafx/base/controls/ToggleSwitch.java @@ -35,6 +35,7 @@ import java.util.List; import javafx.beans.property.BooleanProperty; import javafx.beans.property.BooleanPropertyBase; import javafx.beans.property.ObjectProperty; +import javafx.beans.property.ObjectPropertyBase; import javafx.beans.value.WritableValue; import javafx.css.CssMetaData; import javafx.css.PseudoClass; @@ -44,11 +45,14 @@ import javafx.css.StyleableProperty; import javafx.css.converter.EnumConverter; import javafx.event.ActionEvent; import javafx.geometry.HorizontalDirection; +import javafx.scene.AccessibleAttribute; import javafx.scene.control.Labeled; import javafx.scene.control.Skin; +import javafx.scene.control.Toggle; +import javafx.scene.control.ToggleGroup; @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 PseudoClass PSEUDO_CLASS_SELECTED = PseudoClass.getPseudoClass("selected"); @@ -96,6 +100,39 @@ public class ToggleSwitch extends Labeled { if (selected == null) { 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 public Object getBean() { return ToggleSwitch.this; @@ -105,18 +142,65 @@ 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; } + /** + * 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; + + @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 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 labelPosition; diff --git a/sampler/src/main/java/atlantafx/sampler/page/components/ToggleSwitchPage.java b/sampler/src/main/java/atlantafx/sampler/page/components/ToggleSwitchPage.java index 7159e62..9190394 100644 --- a/sampler/src/main/java/atlantafx/sampler/page/components/ToggleSwitchPage.java +++ b/sampler/src/main/java/atlantafx/sampler/page/components/ToggleSwitchPage.java @@ -11,6 +11,9 @@ import atlantafx.sampler.page.Snippet; import java.net.URI; import javafx.geometry.HorizontalDirection; import javafx.geometry.Pos; +import javafx.scene.control.Label; +import javafx.scene.control.ToggleGroup; +import javafx.scene.layout.GridPane; import javafx.scene.layout.HBox; public final class ToggleSwitchPage extends OutlinePage { @@ -38,6 +41,7 @@ public final class ToggleSwitchPage extends OutlinePage { ); addSection("Usage", usageExample()); addSection("Color", colorExample()); + addSection("Toggle Group", toggleGroupExample()); } private ExampleBox usageExample() { @@ -97,4 +101,34 @@ public final class ToggleSwitchPage extends OutlinePage { 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); + } }