/**
* Copyright (c) 2003-2009, Xith3D Project Group all rights reserved.
*
* Portions based on the Java3D interface, Copyright by Sun Microsystems.
* Many thanks to the developers of Java3D and Sun Microsystems for their
* innovation and design.
*
* 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 'Xith3D Project Group' 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) A
* RISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE
*/
package org.xith3d.ui.hud.base;
import java.util.ArrayList;
import org.jagatoo.datatypes.Enableable;
import org.jagatoo.input.devices.components.DeviceComponent;
import org.jagatoo.input.devices.components.DigitalDeviceComponent;
import org.jagatoo.input.devices.components.Keys;
import org.jagatoo.input.devices.components.MouseButton;
import org.jagatoo.util.arrays.ArrayUtils;
import org.openmali.types.twodee.Dim2f;
import org.xith3d.ui.hud.listeners.ButtonListener;
/**
* This class is a base for all Buttons on a HUD. You can add
* WidgetActionListeners to it to get notified of a click event.
*
* @see org.xith3d.ui.hud.listeners.ButtonListener
*
* @author Amos Wenger (aka BlueSky)
*/
public abstract class AbstractButton extends Widget implements Enableable
{
public enum ButtonState
{
NORMAL,
HOVERED,
PRESSED,
;
}
protected ButtonState buttonState;
protected boolean isStateChangable = true;
private boolean enabled = true;
private final ArrayList<ButtonListener> listeners = new ArrayList<ButtonListener>();
private static DeviceComponent[] accessors = new DeviceComponent[] { Keys.ENTER, Keys.SPACE };
private static boolean isDefaultFocusResponsive = false;
private boolean isFocusResponsive = isDefaultFocusResponsive;
/**
* Sets whether the Button Widget is focus-responsive by default.
*
* @see #setFocusResponsive(boolean)
*
* @param resp
*/
public static final void setDefaultFocusResponsive( boolean resp )
{
isDefaultFocusResponsive = resp;
}
/**
* @return whether the Button Widget is focus-responsive by default.
*
* @see #isFocusResponsive()
*/
public static final boolean isDefaultFocusResponsive()
{
return ( isDefaultFocusResponsive );
}
/**
* Sets whether this Button Widget is focus-responsive.
* This means, that it displays the HOVERED-state-picture
* when it holds the focus.
*
* @see #isFocusResponsive()
* @see #setDefaultFocusResponsive(boolean)
* @see #isDefaultFocusResponsive()
*
* @param resp
*/
public final void setFocusResponsive( boolean resp )
{
this.isFocusResponsive = resp;
}
/**
* @return whether this Button Widget is focus-responsive.
* This means, that it displays the HOVERED-state-picture
* when it holds the focus.
*
* @see #setFocusResponsive(boolean)
* @see #setDefaultFocusResponsive(boolean)
* @see #isDefaultFocusResponsive()
*/
public final boolean isFocusResponsive()
{
return ( isFocusResponsive );
}
/**
* Binds a DeviceComponent to ALL Buttons, that works as an accessor.
* This means, that a focussed Button is pressed on a positive state-change
* on this DeviceComponent.
*
* @param comp
*/
public static void bindAccessor( DeviceComponent comp )
{
if ( ( accessors == null ) || ( accessors.length == 0 ) )
{
accessors = new DeviceComponent[] { comp };
}
else
{
DeviceComponent[] newArray = new DeviceComponent[ accessors.length + 1 ];
System.arraycopy( accessors, 0, newArray, 0, accessors.length );
newArray[ newArray.length - 1 ] = comp;
accessors = newArray;
}
}
/**
* Unbinds a DeviceComponent from ALL Buttons.
*
* @param comp
*/
public static void unbindAccessor( DeviceComponent comp )
{
if ( accessors == null )
{
return;
}
final int index = ArrayUtils.indexOf( accessors, comp, true );
if ( index < 0 )
{
return;
}
if ( accessors.length == 1 )
{
accessors = null;
return;
}
DeviceComponent[] newArray = new DeviceComponent[ accessors.length - 1 ];
System.arraycopy( accessors, 0, newArray, 0, index );
System.arraycopy( accessors, index + 1, newArray, index, accessors.length - index - 1 );
accessors = newArray;
}
/**
* Adds a ButtonListener.
*/
public void addButtonListener( ButtonListener l )
{
listeners.add( l );
}
/**
* Removes a ButtonListener.
*/
public void removeButtonListener( ButtonListener l )
{
listeners.remove( l );
}
protected void fireButtonClickedEvent()
{
for ( int i = 0; i < listeners.size(); i++ )
{
listeners.get( i ).onButtonClicked( this, this.getUserObject() );
}
}
protected abstract void setEnabledImpl( boolean enabled );
/**
* {@inheritDoc}
*/
public final void setEnabled( boolean enabled )
{
if ( enabled == this.enabled )
return;
this.enabled = enabled;
setEnabledImpl( enabled );
}
/**
* {@inheritDoc}
*/
public final boolean isEnabled()
{
return ( enabled );
}
/**
* Sets the new {@link ButtonState} for this Button.
*
* @param buttonState
*
* @return has state changed?
*/
public boolean setButtonState( ButtonState buttonState )
{
if ( buttonState == null )
throw new IllegalArgumentException( "buttonState must not be null" );
if ( buttonState == this.buttonState )
return ( false );
this.buttonState = buttonState;
return ( true );
}
/**
* @return the current ButtonState of this Button
*/
public final ButtonState getButtonState()
{
return ( buttonState );
}
/**
* {@inheritDoc}
*/
@Override
protected void onMouseEntered( boolean isTopMost, boolean hasFocus )
{
super.onMouseEntered( isTopMost, hasFocus );
if ( isTopMost && isStateChangable && isEnabled() )
setButtonState( ButtonState.HOVERED );
}
/**
* {@inheritDoc}
*/
@Override
protected void onMouseExited( boolean isTopMost, boolean hasFocus )
{
super.onMouseExited( isTopMost, hasFocus );
if ( isTopMost && isStateChangable && isEnabled() )
setButtonState( ButtonState.NORMAL );
}
/**
* {@inheritDoc}
*/
@Override
protected void onMouseButtonPressed( MouseButton button, float x, float y, long when, long lastWhen, boolean isTopMost, boolean hasFocus )
{
super.onMouseButtonPressed( button, x, y, when, lastWhen, isTopMost, hasFocus );
if ( isTopMost && isStateChangable && isEnabled() )
setButtonState( ButtonState.PRESSED );
}
/**
* {@inheritDoc}
*/
@Override
protected void onMouseButtonReleased( MouseButton button, float x, float y, long when, long lastWhen, boolean isTopMost, boolean hasFocus )
{
super.onMouseButtonReleased( button, x, y, when, lastWhen, isTopMost, hasFocus );
if ( isTopMost && isStateChangable && isEnabled() )
{
setButtonState( ButtonState.HOVERED );
fireButtonClickedEvent();
}
}
/**
* {@inheritDoc}
*/
@Override
protected void onInputStateChanged( DeviceComponent comp, int delta, int state, long when, boolean isTopMost, boolean hasFocus )
{
super.onInputStateChanged( comp, delta, state, when, isTopMost, hasFocus );
if ( comp instanceof DigitalDeviceComponent )
{
if ( delta <= 0 )
return;
}
/*
else if ( comp instanceof MouseWheel )
{
//delta = Math.abs( delta );
}
*/
else
{
return;
}
if ( accessors != null )
{
for ( int i = 0; i < accessors.length; i++ )
{
if ( accessors[ i ] == comp )
{
fireButtonClickedEvent();
return;
}
}
}
}
/**
* Calculates implementation dependent optimal size for this Button.
*
* @param buffer
*
* @return the buffer back again.
*/
public abstract <Dim2f_ extends Dim2f> Dim2f_ getOptimalSize( Dim2f_ buffer );
/**
* Creates a new Button for the HUD.
*
* @param isHeavyWeight
* @param hasWidgetAssembler
* @param width the desired width
* @param height the desired height
*/
public AbstractButton( boolean isHeavyWeight, boolean hasWidgetAssembler, float width, float height )
{
super( isHeavyWeight, hasWidgetAssembler, width, height );
this.buttonState = ButtonState.NORMAL;
}
}