Hello all. Not sure if anyone is interested in this, but I found a need to have radio buttons in my interface and couldn’t wait for the eventual release of the new gui. So I created this little class called UIRadio which works like your typical radio buttons where you can only have one selected at a time.
You just create the UIRadio object first then add the buttons as you desire. You can also set a default button to be checked. If not, the first button added gets set as the default. You can also use addAction to set actions that will be fired every time a new checkbox is selected.
The addition of this class necessitated a small change (2 lines) to UICheck, which appeared to have a little issue with the way it rendered changes when the state changed.
/*
* Copyright (c) 2003-2004, jMonkeyEngine - Mojo Monkey Coding
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the Mojo Monkey Coding, jME, jMonkey Engine, nor the
* names of its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
/*
* Created on Mar 26, 2005
*
*/
package com.jme.ui;
import com.jme.input.InputHandler;
import com.jme.input.action.UIInputAction;
import com.jme.ui.UICheck;
import com.jme.ui.UIActiveObject;
import java.util.*;
/**
* UIRadio groups UICheck objects together to form radio button like
* behavior in which only one may be selected at a time.
*
* A default selection state may be passed when attaching individual checkboxes. If
* no default is passed for any checkboxes in a radio group, the first checkbox
* will be designated as the default.
*
* @author Scott Oehlerking
*
*/
public class UIRadio extends UIObject {
/**
* @param name
* @param x
* @param y
* @param width
* @param height
* @param scheme
* @param flags
*/
//An array of all checkbox buttons attached to this radio object
private ArrayList checks;
//The currently selected checkbox
private UICheck selectedCheck;
public UIRadio(String name, int x, int y, int width, int height,
UIColorScheme scheme, int flags) {
super(name, x, y, width, height, scheme, flags);
checks = new ArrayList();
}
public void addMember(String name, int x, int y, int width, int height,
InputHandler inputHandler, String upfile, String overfile,
String downfile, String selectedfile, int flags, boolean selected) {
UICheck newCheck = new UICheck(name, x, y, width, height, inputHandler,
upfile, overfile, downfile, selectedfile, flags);
initMember(newCheck, selected);
}
public void addMember(String name, int x, int y, int width, int height,
InputHandler inputHandler, UIColorScheme scheme, int flags,
boolean selected) {
UICheck newCheck = new UICheck(name, x, y, width, height, inputHandler,
scheme, flags);
initMember(newCheck, selected);
}
public void addMember(String name, int x, int y, int width, int height,
InputHandler inputHandler, UIColorScheme scheme, String upfile,
String overfile, String downfile, String selectedfile, int flags,
boolean useClassLoader, boolean selected) {
UICheck newCheck = new UICheck(name, x, y, width, height, inputHandler,
scheme, upfile, overfile, downfile, selectedfile, flags,
useClassLoader);
initMember(newCheck, selected);
}
private void initMember(UICheck newCheck, boolean selected) {
if (selected == true || checks.size() == 0) {
for (Iterator i = this.checks.iterator(); i.hasNext();) {
UICheck ic = (UICheck) i.next();
ic.setSelected(false);
}
newCheck.setSelected(true);
selectedCheck = newCheck;
}
checks.add(newCheck);
this.attachChild(newCheck);
}
public UICheck getSelected() {
return selectedCheck;
}
/**
* Adds actions to the individual checkboxes. All boxes receive the same action.
*/
public void addAction(UIInputAction action) {
for (Iterator i = this.checks.iterator(); i.hasNext();) {
UICheck ic = (UICheck) i.next();
ic.addAction(action);
}
}
/**
* Update all attached checkboxes and insure that only one is checked at a time.
*/
public boolean update(float time) {
boolean retval = false;
for (Iterator i = this.checks.iterator(); i.hasNext();) {
UICheck ic = (UICheck) i.next();
ic.update(time);
if (ic.getState() == UIActiveObject.SELECTED && selectedCheck != ic) {
selectedCheck.setSelected(false);
selectedCheck = ic;
ic.setSelected(true);
//Only fire actions if there was a state change.
ic.fireActions();
retval = true;
}
}
return retval;
}
}
/*
* Copyright (c) 2003-2004, jMonkeyEngine - Mojo Monkey Coding
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the Mojo Monkey Coding, jME, jMonkey Engine, nor the
* names of its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
/*
* Created on Jun 9, 2004
*
*/
package com.jme.ui;
import com.jme.image.Texture;
import com.jme.input.MouseButtonStateType;
import com.jme.scene.state.TextureState;
import com.jme.system.DisplaySystem;
import com.jme.util.TextureManager;
import com.jme.input.*;
import com.jme.math.Vector3f;
/**
* UIObject derrived object that adds another state beyond the UIButton, a
* checked state.
*
* The UICheck may be Up, Over, Down or Checked. When the state is Checked, then
* the other states are disabled. When UnChecked the Up, Over and Down states
* apply.
*
* @author schustej
*
*/
public class UICheck extends UIActiveObject {
protected boolean _selected = false;
UIText _text = null;
public UICheck( String name,
int x, int y, int width, int height,
InputHandler inputHandler, UIColorScheme scheme, int flags) {
this( name, x, y, width, height, inputHandler, scheme, null, null, null, null, flags, false );
/**
* Constructor, give unique name, inputhandler with the mouse attached to check for
* hit tests, the files for the different states and location and scale. This will default
* to using the classloader to get the resources by name
*/
}
public UICheck(String name, int x, int y, int width, int height,
InputHandler inputHandler,
String upfile, String overfile, String downfile, String selectedfile,
int flags) {
this( name, x, y, width, height, inputHandler, null, upfile, overfile, downfile, selectedfile, flags, true);
}
/**
* Alternate constructer allow developer to load directly from file system
*/
public UICheck(String name, int x, int y, int width, int height,
InputHandler inputHandler,
UIColorScheme scheme,
String upfile, String overfile, String downfile, String selectedfile,
int flags, boolean useClassLoader) {
super(name, x, y, width, height, inputHandler, scheme, flags);
if( upfile != null && overfile != null && downfile != null && selectedfile != null) {
TextureState[] textureStates = new TextureState[4];
for (int i = 0; i < 4; i++) {
textureStates[i] = DisplaySystem.getDisplaySystem().getRenderer().createTextureState();
textureStates[i].setEnabled(true);
}
if( useClassLoader) {
textureStates[0].setTexture(TextureManager.loadTexture(UIObject.class.getClassLoader().getResource(
upfile), Texture.MM_LINEAR_LINEAR, Texture.FM_LINEAR));
textureStates[1].setTexture(TextureManager.loadTexture(UIObject.class.getClassLoader().getResource(
overfile), Texture.MM_LINEAR_LINEAR, Texture.FM_LINEAR));
textureStates[2].setTexture(TextureManager.loadTexture(UIObject.class.getClassLoader().getResource(
downfile), Texture.MM_LINEAR_LINEAR, Texture.FM_LINEAR));
textureStates[3].setTexture(TextureManager.loadTexture(UIObject.class.getClassLoader().getResource(
selectedfile), Texture.MM_LINEAR_LINEAR, Texture.FM_LINEAR));
} else {
textureStates[0].setTexture(TextureManager.loadTexture(upfile, Texture.MM_NEAREST,
Texture.FM_NEAREST));
textureStates[1].setTexture(TextureManager.loadTexture(overfile, Texture.MM_NEAREST,
Texture.FM_NEAREST));
textureStates[2].setTexture(TextureManager.loadTexture(downfile, Texture.MM_NEAREST,
Texture.FM_NEAREST));
textureStates[3].setTexture(TextureManager.loadTexture(selectedfile, Texture.MM_NEAREST,
Texture.FM_NEAREST));
}
textureStates[0].apply();
textureStates[1].apply();
textureStates[2].apply();
textureStates[3].apply();
_textureStates.add( textureStates[0]);
_textureStates.add( textureStates[1]);
_textureStates.add( textureStates[2]);
_textureStates.add( textureStates[3]);
}
setup();
}
/**
* Checks the state of the mouse against the state of the control.
*/
public boolean update( float time) {
boolean retval = false;
if (hitTest()) {
if (_inputHandler.getMouse().getMouseInput().getButtonState().equals(
MouseButtonStateType.MOUSE_BUTTON_1)) {
// button is down
if (_state != DOWN) {
if (!_selected) {
if( usingTexture()) {
if( (DRAW_DOWN & _flags) == DRAW_DOWN) {
_quad.setRenderState(((TextureState)_textureStates.elementAt(DOWN)));
_quad.updateRenderState();
}
}
if( usingBorders()) {
if( (DRAW_DOWN & _flags) == DRAW_DOWN) {
setAltBorderColors();
if( _text != null && _state == OVER) {
_text.setLocalTranslation( new Vector3f( 1.0f, -1.0f, 0.0f));
_text.updateGeometricState(0.0f, true);
_text.updateRenderState();
}
}
}
}
_state = DOWN;
}
} else {
// button is up
if (_state == DOWN) {
// Fire action. here
retval = true;
if (!_selected) {
if( usingTexture()) {
_quad.setRenderState(((TextureState)_textureStates.elementAt(SELECTED)));
_quad.updateRenderState();
}
_selected = true;
_state = OVER;
} else {
_selected = false;
}
}
if (!_selected && _state != OVER) {
if( usingTexture()) {
if( (DRAW_OVER & _flags) == DRAW_OVER) {
_quad.setRenderState(((TextureState)_textureStates.elementAt(OVER)));
_quad.updateRenderState();
}
}
if( usingBorders()) {
setBaseBorderColors();
if( (DRAW_OVER & _flags) == DRAW_OVER) {
setHighlightColors();
if( _text != null && _state == DOWN) {
_text.setLocalTranslation( new Vector3f( -1.0f, 1.0f, 0.0f));
_text.updateGeometricState(0.0f, true);
_text.updateRenderState();
}
}
}
_state = OVER;
}
}
} else {
if (!_selected && _state != UP) {
if( usingTexture()) {
_quad.setRenderState(((TextureState)_textureStates.elementAt(UP)));
_quad.updateRenderState();
}
if( usingBorders()) {
setBaseBorderColors();
if( (DRAW_OVER & _flags) == DRAW_OVER) {
setBaseColors();
}
}
_state = UP;
}
}
return retval;
}
/**
* Override of the UIObject getState() which returns also the checked or selected
* state.
*/
public int getState() {
if (_selected) {
return SELECTED;
} else {
return _state;
}
}
/**
* sets the current state of the button.
*/
public void setSelected(boolean sel) {
int newState = sel ? SELECTED : UP;
_selected = sel;
if (_state != newState) {
_state = newState;
_quad.setRenderState( (TextureState) _textureStates.elementAt(_state));
_quad.updateRenderState();
}
}
public void setText( UIFonts fonts, String fontName, String text) {
_text = new UIText( name+"text", fonts, fontName, text,
_x, _y, 65.0f, 0.0f, _height, _width, _scheme, 0);
_text.setLocalTranslation( new Vector3f( (this.getWidth() - _text.getWidth()) / 2,
(this.getHeight() - _text.getHeight()) / 2, 0.0f ));
this.attachChild( _text);
}
}
Fixed a minor bug with setting of default checkbox.