javafx.scene.control.ToggleGroup Maven / Gradle / Ivy
/*
* Copyright (c) 2010, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javafx.scene.control;
import java.util.HashMap;
import java.util.List;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.collections.ListChangeListener.Change;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.ObservableMap;
import com.sun.javafx.collections.VetoableListDecorator;
import com.sun.javafx.collections.TrackableObservableList;
/**
* A class which contains a reference to all {@code Toggles} whose
* {@code selected} variables should be managed such that only a single
* {@link Toggle}
within the {@code ToggleGroup} may be selected at
* any one time.
*
* Generally {@code ToggleGroups} are managed automatically simply by specifying
* the name of a {@code ToggleGroup} on the {@link Toggle}
, but in
* some situations it is desirable to explicitly manage which
* {@code ToggleGroup} is used by {@link Toggle Toggles}
.
*
* @since JavaFX 2.0
*/
public class ToggleGroup {
/**
* Creates a default ToggleGroup instance.
*/
public ToggleGroup() {
}
/**
* The list of toggles within the ToggleGroup.
* @return the list of toggles within the ToggleGroup
*/
public final ObservableList getToggles() {
return toggles;
}
private final ObservableList toggles = new VetoableListDecorator<>(new TrackableObservableList() {
@Override protected void onChanged(Change c) {
while (c.next()) {
final List addedToggles = c.getAddedSubList();
// Look through the removed toggles.
for (Toggle t : c.getRemoved()) {
// If any of them was the one and only selected toggle,
// then we will clear the selected toggle property.
if (t.isSelected()) {
selectToggle(null);
}
// If the toggle is not added again (below) remove
// the group association.
if (!addedToggles.contains(t)) {
t.setToggleGroup(null);
}
}
// A Toggle can only be in one group at any one time. If the
// group is changed, then the toggle is removed from the old group prior to
// being added to the new group.
for (Toggle t: addedToggles) {
if (!ToggleGroup.this.equals(t.getToggleGroup())) {
if (t.getToggleGroup() != null) {
t.getToggleGroup().getToggles().remove(t);
}
t.setToggleGroup(ToggleGroup.this);
}
}
// Look through all the added toggles and the very first selected
// toggle we encounter will become the one we make the selected
// toggle for this group.
for (Toggle t : addedToggles) {
if (t.isSelected()) {
selectToggle(t);
break;
}
}
}
}
}) {
@Override protected void onProposedChange(List toBeAdded, int... indexes) {
for (Toggle t: toBeAdded) {
if (indexes[0] == 0 && indexes[1] == size()) {
// we don't need to check for duplicates because this is a setAll.
break;
}
if (toggles.contains(t)) {
throw new IllegalArgumentException("Duplicate toggles are not allow in a ToggleGroup.");
}
}
}
};
private final ReadOnlyObjectWrapper selectedToggle = new ReadOnlyObjectWrapper<>() {
// Note: "set" is really what I want here. If the selectedToggle property
// is bound, then this whole chunk of code is bypassed, which is exactly
// what I want to do.
@Override public void set(final Toggle newSelectedToggle) {
if (isBound()) {
throw new java.lang.RuntimeException("A bound value cannot be set.");
}
final Toggle old = get();
if (old == newSelectedToggle) {
return;
}
if (setSelected(newSelectedToggle, true) ||
(newSelectedToggle != null && newSelectedToggle.getToggleGroup() == ToggleGroup.this) ||
(newSelectedToggle == null)) {
if (old == null || old.getToggleGroup() == ToggleGroup.this || !old.isSelected()) {
setSelected(old, false);
}
super.set(newSelectedToggle);
}
}
};
/**
* Selects the toggle.
*
* @param value The {@code Toggle} that is to be selected.
*/
// Note that since selectedToggle is a read-only property, the selectToggle method is some
// other method than setSelectedToggle, even though it is in essence doing the same thing
public final void selectToggle(Toggle value) { selectedToggle.set(value); }
/**
* Gets the selected {@code Toggle}.
* @return Toggle The selected toggle.
*/
public final Toggle getSelectedToggle() { return selectedToggle.get(); }
/**
* The selected toggle.
* @return the selected toggle
*/
public final ReadOnlyObjectProperty selectedToggleProperty() { return selectedToggle.getReadOnlyProperty(); }
private boolean setSelected(Toggle toggle, boolean selected) {
if (toggle != null &&
toggle.getToggleGroup() == this &&
!toggle.selectedProperty().isBound()) {
toggle.setSelected(selected);
return true;
}
return false;
}
// Clear the selected toggle only if there are no other toggles selected.
final void clearSelectedToggle() {
if (!selectedToggle.getValue().isSelected()) {
for (Toggle toggle: getToggles()) {
if (toggle.isSelected()) {
return;
}
}
}
selectedToggle.set(null);
}
/* ***********************************************************************
* *
* *
* *
*************************************************************************/
private static final Object USER_DATA_KEY = new Object();
// A map containing a set of properties for this scene
private ObservableMap