001package org.gwtbootstrap3.client.ui; 002 003/* 004 * #%L 005 * GwtBootstrap3 006 * %% 007 * Copyright (C) 2013 GwtBootstrap3 008 * %% 009 * Licensed under the Apache License, Version 2.0 (the "License"); 010 * you may not use this file except in compliance with the License. 011 * You may obtain a copy of the License at 012 * 013 * http://www.apache.org/licenses/LICENSE-2.0 014 * 015 * Unless required by applicable law or agreed to in writing, software 016 * distributed under the License is distributed on an "AS IS" BASIS, 017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 018 * See the License for the specific language governing permissions and 019 * limitations under the License. 020 * #L% 021 */ 022 023import org.gwtbootstrap3.client.ui.base.HasActive; 024import org.gwtbootstrap3.client.ui.base.HasIcon; 025import org.gwtbootstrap3.client.ui.base.HasIconPosition; 026import org.gwtbootstrap3.client.ui.base.HasSize; 027import org.gwtbootstrap3.client.ui.base.HasType; 028import org.gwtbootstrap3.client.ui.base.helper.StyleHelper; 029import org.gwtbootstrap3.client.ui.base.mixin.ActiveMixin; 030import org.gwtbootstrap3.client.ui.constants.ButtonSize; 031import org.gwtbootstrap3.client.ui.constants.ButtonType; 032import org.gwtbootstrap3.client.ui.constants.IconFlip; 033import org.gwtbootstrap3.client.ui.constants.IconPosition; 034import org.gwtbootstrap3.client.ui.constants.IconRotate; 035import org.gwtbootstrap3.client.ui.constants.IconSize; 036import org.gwtbootstrap3.client.ui.constants.IconType; 037import org.gwtbootstrap3.client.ui.constants.Styles; 038 039import com.google.gwt.core.client.Scheduler; 040import com.google.gwt.core.client.Scheduler.ScheduledCommand; 041import com.google.gwt.dom.client.Document; 042import com.google.gwt.dom.client.InputElement; 043import com.google.gwt.event.dom.client.ClickEvent; 044import com.google.gwt.event.dom.client.ClickHandler; 045import com.google.gwt.event.logical.shared.ValueChangeEvent; 046import com.google.gwt.i18n.client.HasDirection.Direction; 047import com.google.gwt.i18n.shared.DirectionEstimator; 048import com.google.gwt.safehtml.shared.SafeHtml; 049import com.google.gwt.uibinder.client.UiConstructor; 050import com.google.gwt.user.client.DOM; 051import com.google.gwt.user.client.Event; 052 053/** 054 * Button representing a radio button used within a {@link ButtonGroup} that has 055 * toggle set to {@code Toogle.BUTTONS}. 056 * <p/> 057 * If you are looking for a classic radio button see {@link RadioButton}. 058 * 059 * @author Sven Jacobs 060 */ 061public class RadioButton extends Radio implements HasActive, 062 HasType<ButtonType>, HasSize<ButtonSize>, HasIcon, HasIconPosition { 063 064 private final ActiveMixin<RadioButton> activeMixin = new ActiveMixin<RadioButton>(this); 065 066 private IconPosition iconPosition = IconPosition.LEFT; 067 private Icon icon; 068 069 070 /** 071 * Creates a new radio associated with a particular group, and initialized 072 * with the given HTML label. All radio buttons associated with the same 073 * group name belong to a mutually-exclusive set. 074 * 075 * Radio buttons are grouped by their name attribute, so changing their name 076 * using the setName() method will also change their associated group. 077 * 078 * @param name 079 * the group name with which to associate the radio button 080 * @param label 081 * this radio button's html label 082 */ 083 public RadioButton(String name, SafeHtml label) { 084 this(name, label.asString(), true); 085 } 086 087 /** 088 * @see #RadioButtonToggle(String, SafeHtml) 089 * 090 * @param name 091 * the group name with which to associate the radio button 092 * @param label 093 * this radio button's html label 094 * @param dir 095 * the text's direction. Note that {@code DEFAULT} means 096 * direction should be inherited from the widget's parent 097 * element. 098 */ 099 public RadioButton(String name, SafeHtml label, Direction dir) { 100 this(name); 101 setHTML(label, dir); 102 } 103 104 /** 105 * @see #RadioButtonToggle(String, SafeHtml) 106 * 107 * @param name 108 * the group name with which to associate the radio button 109 * @param label 110 * this radio button's html label 111 * @param directionEstimator 112 * A DirectionEstimator object used for automatic direction 113 * adjustment. For convenience, 114 * {@link #DEFAULT_DIRECTION_ESTIMATOR} can be used. 115 */ 116 public RadioButton(String name, SafeHtml label, DirectionEstimator directionEstimator) { 117 this(name); 118 setDirectionEstimator(directionEstimator); 119 setHTML(label.asString()); 120 } 121 122 /** 123 * Creates a new radio associated with a particular group, and initialized 124 * with the given HTML label. All radio buttons associated with the same 125 * group name belong to a mutually-exclusive set. 126 * 127 * Radio buttons are grouped by their name attribute, so changing their name 128 * using the setName() method will also change their associated group. 129 * 130 * @param name 131 * the group name with which to associate the radio button 132 * @param label 133 * this radio button's label 134 */ 135 public RadioButton(String name, String label) { 136 this(name); 137 setText(label); 138 } 139 140 /** 141 * @see #RadioButtonToggle(String, SafeHtml) 142 * 143 * @param name 144 * the group name with which to associate the radio button 145 * @param label 146 * this radio button's label 147 * @param dir 148 * the text's direction. Note that {@code DEFAULT} means 149 * direction should be inherited from the widget's parent 150 * element. 151 */ 152 public RadioButton(String name, String label, Direction dir) { 153 this(name); 154 setText(label, dir); 155 } 156 157 /** 158 * @see #RadioButtonToggle(String, SafeHtml) 159 * 160 * @param name 161 * the group name with which to associate the radio button 162 * @param label 163 * this radio button's label 164 * @param directionEstimator 165 * A DirectionEstimator object used for automatic direction 166 * adjustment. For convenience, 167 * {@link #DEFAULT_DIRECTION_ESTIMATOR} can be used. 168 */ 169 public RadioButton(String name, String label, DirectionEstimator directionEstimator) { 170 this(name); 171 setDirectionEstimator(directionEstimator); 172 setText(label); 173 } 174 175 /** 176 * Creates a new radio button associated with a particular group, and 177 * initialized with the given label (optionally treated as HTML). All radio 178 * buttons associated with the same group name belong to a 179 * mutually-exclusive set. 180 * 181 * Radio buttons are grouped by their name attribute, so changing their name 182 * using the setName() method will also change their associated group. 183 * 184 * @param name 185 * name the group with which to associate the radio button 186 * @param label 187 * this radio button's label 188 * @param asHTML 189 * <code>true</code> to treat the specified label as HTML 190 */ 191 public RadioButton(String name, String label, boolean asHTML) { 192 this(name); 193 if (asHTML) { 194 setHTML(label); 195 } else { 196 setText(label); 197 } 198 } 199 200 @UiConstructor 201 public RadioButton(String name) { 202 this(Document.get().createRadioInputElement(name)); 203 } 204 205 protected RadioButton(InputElement element) { 206 super(DOM.createLabel(), element); 207 208 setStyleName(Styles.BTN); 209 setType(ButtonType.DEFAULT); 210 211 getElement().appendChild(inputElem); 212 getElement().appendChild(Document.get().createTextNode(" ")); 213 getElement().appendChild(labelElem); 214 getElement().appendChild(Document.get().createTextNode(" ")); 215 } 216 217 @Override 218 protected void ensureDomEventHandlers() { 219 // Use a ClickHandler since Bootstrap's jQuery does not trigger native 220 // change events: 221 // http://learn.jquery.com/events/triggering-event-handlers/ 222 addClickHandler(new ClickHandler() { 223 224 @Override 225 public void onClick(ClickEvent event) { 226 final boolean oldValue = getValue(); 227 228 Scheduler.get().scheduleDeferred(new ScheduledCommand() { 229 @Override 230 public void execute() { 231 ValueChangeEvent.fireIfNotEqual(RadioButton.this, 232 oldValue, getValue()); 233 } 234 }); 235 } 236 237 }); 238 } 239 240 @Override 241 public void sinkEvents(int eventBitsToAdd) { 242 // Sink on the actual element because that's what gets clicked 243 if (isOrWasAttached()) { 244 Event.sinkEvents(getElement(), 245 eventBitsToAdd | Event.getEventsSunk(getElement())); 246 } else { 247 super.sinkEvents(eventBitsToAdd); 248 } 249 } 250 251 @Override 252 public void setSize(ButtonSize size) { 253 StyleHelper.addUniqueEnumStyleName(this, ButtonSize.class, size); 254 } 255 256 @Override 257 public ButtonSize getSize() { 258 return ButtonSize.fromStyleName(getStyleName()); 259 } 260 261 @Override 262 public void setType(ButtonType type) { 263 StyleHelper.addUniqueEnumStyleName(this, ButtonType.class, type); 264 } 265 266 @Override 267 public ButtonType getType() { 268 return ButtonType.fromStyleName(getStyleName()); 269 } 270 271 @Override 272 public void setActive(boolean active) { 273 setValue(active); 274 activeMixin.setActive(active); 275 } 276 277 @Override 278 public boolean isActive() { 279 return activeMixin.isActive(); 280 } 281 282 @Override 283 public void setIconPosition(IconPosition iconPosition) { 284 this.iconPosition = iconPosition; 285 render(); 286 } 287 288 @Override 289 public IconPosition getIconPosition() { 290 return iconPosition; 291 } 292 293 @Override 294 public void setIcon(IconType iconType) { 295 getActualIcon().setType(iconType); 296 } 297 298 @Override 299 public IconType getIcon() { 300 return getActualIcon().getType(); 301 } 302 303 @Override 304 public void setIconSize(IconSize iconSize) { 305 getActualIcon().setSize(iconSize); 306 } 307 308 @Override 309 public IconSize getIconSize() { 310 return getActualIcon().getSize(); 311 } 312 313 @Override 314 public void setIconFlip(IconFlip iconFlip) { 315 getActualIcon().setFlip(iconFlip); 316 } 317 318 @Override 319 public IconFlip getIconFlip() { 320 return getActualIcon().getFlip(); 321 } 322 323 @Override 324 public void setIconRotate(IconRotate iconRotate) { 325 getActualIcon().setRotate(iconRotate); 326 } 327 328 @Override 329 public IconRotate getIconRotate() { 330 return getActualIcon().getRotate(); 331 } 332 333 @Override 334 public void setIconBordered(boolean iconBordered) { 335 getActualIcon().setBorder(iconBordered); 336 } 337 338 @Override 339 public boolean isIconBordered() { 340 return getActualIcon().isBorder(); 341 } 342 343 /** {@inheritDoc} */ 344 @Override 345 public void setIconInverse(final boolean iconInverse) { 346 getActualIcon().setInverse(iconInverse); 347 } 348 349 /** {@inheritDoc} */ 350 @Override 351 public boolean isIconInverse() { 352 return getActualIcon().isInverse(); 353 } 354 355 @Override 356 public void setIconSpin(boolean iconSpin) { 357 getActualIcon().setSpin(iconSpin); 358 } 359 360 @Override 361 public boolean isIconSpin() { 362 return getActualIcon().isSpin(); 363 } 364 365 @Override 366 public void setIconPulse(boolean iconPulse) { 367 getActualIcon().setPulse(iconPulse); 368 } 369 370 @Override 371 public boolean isIconPulse() { 372 return getActualIcon().isPulse(); 373 } 374 375 @Override 376 public void setIconFixedWidth(boolean iconFixedWidth) { 377 getActualIcon().setFixedWidth(iconFixedWidth); 378 } 379 380 @Override 381 public boolean isIconFixedWidth() { 382 return getActualIcon().isFixedWidth(); 383 } 384 385 private Icon getActualIcon() { 386 if (icon == null) { 387 icon = new Icon(); 388 render(); 389 } 390 return icon; 391 } 392 393 private void render() { 394 if (iconPosition == IconPosition.LEFT) { 395 getElement().insertAfter(icon.getElement(), inputElem); 396 } else { 397 getElement().insertAfter(icon.getElement(), null); 398 } 399 } 400 401}