001/* 002 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. Oracle designates this 008 * particular file as subject to the "Classpath" exception as provided 009 * by Oracle in the LICENSE file that accompanied this code. 010 * 011 * This code is distributed in the hope that it will be useful, but WITHOUT 012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 014 * version 2 for more details (a copy is included in the LICENSE file that 015 * accompanied this code). 016 * 017 * You should have received a copy of the GNU General Public License version 018 * 2 along with this work; if not, write to the Free Software Foundation, 019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 020 * 021 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 022 * or visit www.oracle.com if you need additional information or have any 023 * questions. 024 */ 025 026package javafx.scene.control; 027 028import javafx.beans.property.BooleanProperty; 029import javafx.beans.property.BooleanPropertyBase; 030import javafx.beans.property.ObjectProperty; 031import javafx.beans.property.ObjectPropertyBase; 032import javafx.event.ActionEvent; 033import javafx.geometry.Pos; 034import javafx.scene.Node; 035import javafx.css.CssMetaData; 036import javafx.css.PseudoClass; 037import com.sun.javafx.scene.control.skin.ToggleButtonSkin; 038import javafx.css.StyleableProperty; 039 040/** 041 * A {@code ToggleButton} is a specialized control which has the ability to be 042 * selected. Typically a {@code ToggleButton} is rendered similarly to a Button. 043 * However, they are two different types of Controls. A Button is a "command" 044 * button which invokes a function when clicked. A {@code ToggleButton} on the 045 * other hand is simply a control with a Boolean indicating whether it has been 046 * selected. 047 * <p> 048 * {@code ToggleButton} can also be placed in groups. By default, a 049 * {@code ToggleButton} is not in a group. When in groups, only one 050 * {@code ToggleButton} at a time within that group can be selected. To put two 051 * {@code ToggleButtons} in the same group, simply assign them both the same 052 * value for {@link ToggleGroup}. 053 * </p> 054 * <p> 055 * Unlike {@link RadioButton RadioButtons}, {@code ToggleButtons} in a 056 * {@code ToggleGroup} do not attempt to force at least one selected 057 * {@code ToggleButton} in the group. That is, if a {@code ToggleButton} is 058 * selected, clicking on it will cause it to become unselected. With 059 * {@code RadioButton}, clicking on the selected button in the group will have 060 * no effect. 061 * </p> 062 * 063 * <p>Example:</p> 064 * <pre><code> 065 * ToggleButton tb1 = new ToggleButton("toggle button 1"); 066 * ToggleButton tb2 = new ToggleButton("toggle button 2"); 067 * ToggleButton tb3 = new ToggleButton("toggle button 3"); 068 * ToggleGroup group = new ToggleGroup(); 069 * tb1.setToggleGroup(group); 070 * tb2.setToggleGroup(group); 071 * tb3.setToggleGroup(group); 072 * </code></pre> 073 * 074 * <p> 075 * MnemonicParsing is enabled by default for ToggleButton. 076 * </p> 077 */ 078 079// TODO Mention the semantics when binding "selected" on multiple toggle buttons 080// which are all on the same toggle group, and how the selected state on the 081// toggle group is affected or not in such a case. 082 083 public class ToggleButton extends ButtonBase implements Toggle { 084 085 /*************************************************************************** 086 * * 087 * Constructors * 088 * * 089 **************************************************************************/ 090 091 /** 092 * Creates a toggle button with an empty string for its label. 093 */ 094 public ToggleButton() { 095 initialize(); 096 } 097 098 /** 099 * Creates a toggle button with the specified text as its label. 100 * 101 * @param text A text string for its label. 102 */ 103 public ToggleButton(String text) { 104 setText(text); 105 initialize(); 106 } 107 108 /** 109 * Creates a toggle button with the specified text and icon for its label. 110 * 111 * @param text A text string for its label. 112 * @param graphic the icon for its label. 113 */ 114 public ToggleButton(String text, Node graphic) { 115 setText(text); 116 setGraphic(graphic); 117 initialize(); 118 } 119 120 private void initialize() { 121 getStyleClass().setAll(DEFAULT_STYLE_CLASS); 122 // alignment is styleable through css. Calling setAlignment 123 // makes it look to css like the user set the value and css will not 124 // override. Initializing alignment by calling set on the 125 // CssMetaData ensures that css will be able to override the value. 126 ((StyleableProperty)alignmentProperty()).applyStyle(null, Pos.CENTER); 127 setMnemonicParsing(true); // enable mnemonic auto-parsing by default 128 } 129 /*************************************************************************** 130 * * 131 * Properties * 132 * * 133 **************************************************************************/ 134 /** 135 * Indicates whether this toggle button is selected. This can be manipulated 136 * programmatically. 137 */ 138 private BooleanProperty selected; 139 public final void setSelected(boolean value) { 140 selectedProperty().set(value); 141 } 142 143 public final boolean isSelected() { 144 return selected == null ? false : selected.get(); 145 } 146 147 public final BooleanProperty selectedProperty() { 148 if (selected == null) { 149 selected = new BooleanPropertyBase() { 150 @Override protected void invalidated() { 151 if (getToggleGroup() != null) { 152 if (get()) { 153 getToggleGroup().selectToggle(ToggleButton.this); 154 } else if (getToggleGroup().getSelectedToggle() == ToggleButton.this) { 155 getToggleGroup().clearSelectedToggle(); 156 } 157 } 158 pseudoClassStateChanged(PSEUDO_CLASS_SELECTED, get()); 159 } 160 161 @Override 162 public Object getBean() { 163 return ToggleButton.this; 164 } 165 166 @Override 167 public String getName() { 168 return "selected"; 169 } 170 }; 171 } 172 return selected; 173 } 174 /** 175 * The {@link ToggleGroup} to which this {@code ToggleButton} belongs. A 176 * {@code ToggleButton} can only be in one group at any one time. If the 177 * group is changed, then the button is removed from the old group prior to 178 * being added to the new group. 179 */ 180 private ObjectProperty<ToggleGroup> toggleGroup; 181 public final void setToggleGroup(ToggleGroup value) { 182 toggleGroupProperty().set(value); 183 } 184 185 public final ToggleGroup getToggleGroup() { 186 return toggleGroup == null ? null : toggleGroup.get(); 187 } 188 189 public final ObjectProperty<ToggleGroup> toggleGroupProperty() { 190 if (toggleGroup == null) { 191 toggleGroup = new ObjectPropertyBase<ToggleGroup>() { 192 private ToggleGroup old; 193 @Override protected void invalidated() { 194 final ToggleGroup tg = get(); 195 if (tg != null && !tg.getToggles().contains(ToggleButton.this)) { 196 if (old != null) { 197 old.getToggles().remove(ToggleButton.this); 198 } 199 tg.getToggles().add(ToggleButton.this); 200 } else if (tg == null) { 201 old.getToggles().remove(ToggleButton.this); 202 } 203 old = tg; 204 } 205 206 @Override 207 public Object getBean() { 208 return ToggleButton.this; 209 } 210 211 @Override 212 public String getName() { 213 return "toggleGroup"; 214 } 215 }; 216 } 217 return toggleGroup; 218 } 219 220 /*************************************************************************** 221 * * 222 * Methods * 223 * * 224 **************************************************************************/ 225 226 /** {@inheritDoc} */ 227 @Override public void fire() { 228 // TODO (aruiz): if (!isReadOnly(isSelected()) { 229 setSelected(!isSelected()); 230 fireEvent(new ActionEvent()); 231 } 232 233 /** {@inheritDoc} */ 234 @Override protected Skin<?> createDefaultSkin() { 235 return new ToggleButtonSkin(this); 236 } 237 238 239 /*************************************************************************** 240 * * 241 * Stylesheet Handling * 242 * * 243 **************************************************************************/ 244 245 private static final String DEFAULT_STYLE_CLASS = "toggle-button"; 246 private static final PseudoClass PSEUDO_CLASS_SELECTED = 247 PseudoClass.getPseudoClass("selected"); 248 249 /** 250 * Not everything uses the default value of false for alignment. 251 * This method provides a way to have them return the correct initial value. 252 * @treatAsPrivate implementation detail 253 */ 254 @Deprecated @Override 255 protected Pos impl_cssGetAlignmentInitialValue() { 256 return Pos.CENTER; 257 } 258 259}