/**
* 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.widgets;
import java.util.ArrayList;
import org.jagatoo.datatypes.Enableable;
import org.jagatoo.input.devices.components.DeviceComponent;
import org.jagatoo.input.devices.components.MouseButton;
import org.openmali.vecmath2.Colorf;
import org.openmali.vecmath2.Point2f;
import org.xith3d.scenegraph.Texture2D;
import org.xith3d.scenegraph.Texture2DCanvas;
import org.xith3d.ui.hud.HUD;
import org.xith3d.ui.hud.__HUD_PrivilegedAccess;
import org.xith3d.ui.hud.base.AbstractButton;
import org.xith3d.ui.hud.base.AbstractList;
import org.xith3d.ui.hud.base.ListModel;
import org.xith3d.ui.hud.base.TextWidget;
import org.xith3d.ui.hud.base.Widget;
import org.xith3d.ui.hud.base.__HUD_base_PrivilegedAccess;
import org.xith3d.ui.hud.listeners.ListSelectionListener;
import org.xith3d.ui.hud.listeners.WidgetEventsReceiverAdapter;
import org.xith3d.ui.hud.listmodels.TextListModel;
import org.xith3d.ui.hud.utils.HUDFont;
import org.xith3d.ui.hud.utils.HUDTextureUtils;
import org.xith3d.ui.hud.utils.PopUpable;
import org.xith3d.ui.hud.utils.ScrollMode;
import org.xith3d.ui.text2d.TextAlignment;
/**
* A ComboBox is a bordered Label with a Button on the right, that pops up a
* List widget and displays the current selected Item's text.
*
* @author Marvin Froehlich (aka Qudus)
*/
public class ComboBox extends AbstractList implements TextWidget, Enableable, PopUpable
{
private static final boolean DEFAULT_HEAVYWEIGHT = false;
public static class Description extends Widget.DescriptionBase
{
private TextField.Description textFieldDesc;
private List.Description listDesc;
private Texture2D buttonSymbol;
public void setTextFieldDescription( TextField.Description textFieldDesc )
{
this.textFieldDesc = textFieldDesc;
}
public TextField.Description getTextFieldDescription()
{
return ( textFieldDesc );
}
public void setListDescription( List.Description listDesc )
{
this.listDesc = listDesc;
}
public List.Description getListDescription()
{
return ( listDesc );
}
public Texture2D getButtonSymbol()
{
return ( buttonSymbol );
}
public void setButtonSymbol( Texture2D texture )
{
buttonSymbol = texture;
}
public void setButtonSymbol( String texture )
{
setButtonSymbol( HUDTextureUtils.getTexture( texture, true ) );
}
public void set( Description template )
{
this.textFieldDesc = template.textFieldDesc.clone();
this.listDesc = template.listDesc.clone();
this.buttonSymbol = template.buttonSymbol;
}
@Override
public Description clone()
{
return ( new Description( this ) );
}
private Description( Description template )
{
this.set( template );
}
public Description( TextField.Description textFieldDesc, List.Description listDesc, Texture2D buttonSymbol )
{
this.textFieldDesc = textFieldDesc;
this.listDesc = listDesc;
this.buttonSymbol = buttonSymbol;
}
public Description( TextField.Description textFieldDesc, List.Description listDesc, String buttonSymbol )
{
this( textFieldDesc, listDesc, HUDTextureUtils.getTexture( buttonSymbol, true ) );
}
}
private class AssemblerEventsReceiver extends WidgetEventsReceiverAdapter
{
public boolean selectionChanged = false;
@Override
public void onButtonClicked( AbstractButton button, Object userObject )
{
if ( ( button == ComboBox.this.button ) && ComboBox.this.isEnabled() )
{
popUp( !isPoppedUp() );
}
}
@Override
public void onListSelectionChanged( AbstractList list, Object oldSelectedItem, Object newSelectedItem, int oldSelectedIndex, int newSelectedIndex )
{
if ( textField != null )
{
textField.setText( ( newSelectedItem != null ) ? String.valueOf( newSelectedItem ) : "" );
}
selectionChanged = true;
for ( int i = 0; i < selectionListeners.size(); i++ )
selectionListeners.get( i ).onListSelectionChanged( ComboBox.this, oldSelectedItem, newSelectedItem, oldSelectedIndex, newSelectedIndex );
}
@Override
public void onMouseButtonReleased( Widget widget, MouseButton button, float x, float y, long when, long lastWhen, boolean isTopMost, boolean hasFocus )
{
if ( selectionChanged )
popUp( false );
}
@Override
public void onListItemClicked( AbstractList list, Object item, int itemIndex )
{
for ( int i = 0; i < selectionListeners.size(); i++ )
selectionListeners.get( i ).onListItemClicked( ComboBox.this, item, itemIndex );
}
}
private final TextField textField;
private final Button button;
private final List list;
private int maxListHeightByItems = -1;
private final AssemblerEventsReceiver assemblerEventsReceiver = new AssemblerEventsReceiver();
private final ArrayList<ListSelectionListener> selectionListeners = new ArrayList<ListSelectionListener>( 1 );
/**
* {@inheritDoc}
*/
@Override
protected float getMinWidth()
{
return ( 50f );
}
/**
* {@inheritDoc}
*/
@Override
protected float getMinHeight()
{
return ( 10f );
}
/**
* {@inheritDoc}
*/
@Override
public void setModel( ListModel model )
{
if ( list != null )
list.setModel( model );
}
/**
* {@inheritDoc}
*/
@Override
public ListModel getModel()
{
return ( list.getModel() );
}
/**
* {@inheritDoc}
*/
public boolean setPadding( int paddingBottom, int paddingRight, int paddingTop, int paddingLeft )
{
boolean b1 = textField.setPadding( 0, paddingRight, 0, paddingLeft );
boolean b2 = list.setPadding( paddingBottom, paddingRight, paddingTop, paddingLeft );
return ( b1 || b2 );
}
/**
* {@inheritDoc}
*/
public final int getPaddingBottom()
{
return ( list.getPaddingBottom() );
}
/**
* {@inheritDoc}
*/
public final int getPaddingRight()
{
return ( textField.getPaddingRight() );
}
/**
* {@inheritDoc}
*/
public final int getPaddingTop()
{
return ( list.getPaddingTop() );
}
/**
* {@inheritDoc}
*/
public final int getPaddingLeft()
{
return ( textField.getPaddingLeft() );
}
/**
* {@inheritDoc}
*/
@Override
public void setHoverBackgroundColor( Colorf color )
{
list.setHoverBackgroundColor( color );
}
/**
* {@inheritDoc}
*/
@Override
public final Colorf getHoverBackgroundColor()
{
return ( list.getHoverBackgroundColor() );
}
/**
* {@inheritDoc}
*/
@Override
public void setSelectionBackgroundColor( Colorf color )
{
list.setSelectionBackgroundColor( color );
}
/**
* {@inheritDoc}
*/
@Override
public final Colorf getSelectionBackgroundColor()
{
return ( list.getSelectionBackgroundColor() );
}
/**
* {@inheritDoc}
*/
@Override
public void setAlignment( TextAlignment alignment )
{
textField.setAlignment( alignment );
list.setAlignment( alignment );
}
/**
* {@inheritDoc}
*/
@Override
public final TextAlignment getAlignment()
{
return ( textField.getAlignment() );
}
/**
* {@inheritDoc}
*/
@Override
public void addSelectionListener( ListSelectionListener l )
{
selectionListeners.add( l );
}
/**
* {@inheritDoc}
*/
@Override
public void removeSelectionListener( ListSelectionListener l )
{
selectionListeners.remove( l );
}
/**
* {@inheritDoc}
*/
@Override
public void setSelectedIndex( int itemIndex )
{
super.setSelectedIndex( itemIndex );
textField.setText( String.valueOf( getSelectedItem() ) );
}
/**
* {@inheritDoc}
*/
@Override
public void setAddItemSetsSelectedItem( boolean b )
{
list.setAddItemSetsSelectedItem( b );
}
/**
* {@inheritDoc}
*/
@Override
public final boolean addItemSetsSelectedItem()
{
return ( list.addItemSetsSelectedItem() );
}
/**
* {@inheritDoc}
*/
@Override
public Object addItem( int index, Object item )
{
list.addItem( index, item );
if ( addItemSetsSelectedItem() )
textField.setText( String.valueOf( item ) );
setMaxListHeightByItems( getMaxListHeightByItems() );
return ( item );
}
public void addItem( int index, String text, HUDFont font, Colorf color )
{
list.addItem( index, text, font, color );
setMaxListHeightByItems( getMaxListHeightByItems() );
}
public final void addItem( String text, HUDFont font, Colorf color )
{
addItem( getItemsCount(), text, font, color );
}
public final void addItem( int index, String text, Colorf color )
{
addItem( index, text, null, color );
}
public final void addItem( String text, Colorf color )
{
addItem( getItemsCount(), text, null, color );
}
public final void addItem( int index, String text )
{
addItem( index, text, null, null );
}
public final void addItem( String text )
{
addItem( getItemsCount(), text, null, null );
}
/**
* {@inheritDoc}
*/
@Override
public String removeItem( int index )
{
String item = (String)super.removeItem( index );
setMaxListHeightByItems( getMaxListHeightByItems() );
return ( item );
}
/**
* {@inheritDoc}
*/
@Override
@SuppressWarnings( "unchecked" )
public void addItems( java.util.List items )
{
super.addItems( items );
setMaxListHeightByItems( getMaxListHeightByItems() );
}
/**
* {@inheritDoc}
*/
@Override
public void addItems( Object[] items )
{
super.addItems( items );
setMaxListHeightByItems( getMaxListHeightByItems() );
}
/**
* {@inheritDoc}
*/
@Override
public void clear()
{
super.clear();
textField.setText( "" );
setMaxListHeightByItems( getMaxListHeightByItems() );
}
/**
* {@inheritDoc}
*/
@Override
public void setTopIndex( int topIndex )
{
list.setTopIndex( topIndex );
}
/**
* {@inheritDoc}
*/
@Override
public int getTopIndex()
{
return ( list.getTopIndex() );
}
/**
* {@inheritDoc}
*/
@Override
public final int getBottomIndex()
{
return ( list.getBottomIndex() );
}
/**
* {@inheritDoc}
*/
@Override
public void scrollSelectedItemIntoView()
{
list.scrollSelectedItemIntoView();
}
/**
* Sets the list's height by items count.
*
* @param numItems the new height by items unit
*/
public void setListHeightByItems( int numItems )
{
list.setHeightByItems( numItems );
}
/**
* Gets the list's height by items count.
*
* @return the height by items count.
*/
public int getListHeightByItems()
{
return ( list.getHeightByItems() );
}
/**
* Sets the maximum list's height by items.<br>
* If the list contains less items, it is sized appropriately.
*
* @param maxHeight
*/
public void setMaxListHeightByItems( int maxHeight )
{
this.maxListHeightByItems = maxHeight;
if ( ( maxListHeightByItems > 0 ) && ( getHUD() != null ) )
{
setListHeightByItems( Math.min( getItemsCount(), maxListHeightByItems ) );
}
}
/**
* Gets the maximum list's height by items.
*
* @return the maximum list's height by items.
*/
public int getMaxListHeightByItems()
{
return ( maxListHeightByItems );
}
/**
* {@inheritDoc}
*/
@Override
protected boolean blocksFocusMoveDeviceComponent( DeviceComponent dc )
{
return ( textField.blocksFocusMoveDeviceComponent( dc ) || list.blocksFocusMoveDeviceComponent( dc ) );
}
/**
* {@inheritDoc}
*/
public void setText( String text )
{
throw new UnsupportedOperationException( "You cannot set the Text of a ComboBox." );
}
/**
* {@inheritDoc}
*/
public String getText()
{
return ( textField.getText() );
}
/**
* {@inheritDoc}
*/
public HUDFont getFont()
{
return ( textField.getFont() );
}
/**
* {@inheritDoc}
*/
public void setFont( HUDFont font )
{
textField.setFont( font );
}
/**
* {@inheritDoc}
*/
public void setFontColor( Colorf color )
{
textField.setFontColor( color );
}
/**
* {@inheritDoc}
*/
public Colorf getFontColor()
{
return ( textField.getFontColor() );
}
/**
* {@inheritDoc}
*/
public final void setEnabled( boolean enabled )
{
textField.setEnabled( enabled );
}
/**
* {@inheritDoc}
*/
public final boolean isEnabled()
{
return ( textField.isEnabled() );
}
/**
* {@inheritDoc}
*/
@Override
protected void onSizeChanged( float oldWidth, float oldHeight, float newWidth, float newHeight )
{
super.onSizeChanged( oldWidth, oldHeight, newWidth, newHeight );
final float buttonWidth = ( button != null ) ? newHeight : 0f;
if ( textField != null )
textField.setSize( newWidth - buttonWidth, newHeight );
if ( button != null )
button.setSize( buttonWidth, newHeight );
list.setSize( newWidth, list.getHeight() );
if ( button != null )
getWidgetAssembler().reposition( button, newWidth - buttonWidth, 0f );
}
/**
* @return the height on which to pick. By default this is exactly getHeight().
*/
@Override
protected float getPickHeight()
{
/*
if ( isPoppedUp() )
return ( super.getPickHeight() + list.getHeight() );
else
*/
return ( super.getPickHeight() );
}
/**
* {@inheritDoc}
*/
public void popUp( boolean p )
{
if ( p == isPoppedUp() )
return;
final HUD hud = getHUD();
if ( hud == null )
return;
if ( p )
{
Point2f listPos = Point2f.fromPool();
getAbsoluteLocationOnHUD_( listPos );
listPos.addY( this.getHeight() );
__HUD_PrivilegedAccess.addVolatilePopup( hud, list, this, listPos.getX(), listPos.getY() );
Point2f.toPool( listPos );
assemblerEventsReceiver.selectionChanged = false;
}
else if ( list.getHUD() != null )
{
__HUD_PrivilegedAccess.removeVolatilePopup( hud );
}
}
/**
* {@inheritDoc}
*/
public boolean isPoppedUp()
{
//return ( list.isVisible() );
//return ( list.getContainer() != null );
return ( list.getHUD() != null );
}
/**
* {@inheritDoc}
*/
@Override
public void onFocusLost()
{
super.onFocusLost();
/*
if ( isPoppedUp() )
popUp( false );
*/
}
/**
* {@inheritDoc}
*/
@Override
public void onMouseExited( boolean isTopMost, boolean hasFocus )
{
super.onMouseExited( isTopMost, hasFocus );
__HUD_base_PrivilegedAccess.onMouseExited( button, isTopMost, hasFocus );
}
/**
* {@inheritDoc}
*/
@Override
public void onAttachedToHUD( HUD hud )
{
super.onAttachedToHUD( hud );
setMaxListHeightByItems( maxListHeightByItems );
}
/**
* {@inheritDoc}
*/
@Override
protected void drawWidget( Texture2DCanvas texCanvas, int offsetX, int offsetY, int width, int height, boolean drawsSelf )
{
}
/**
* {@inheritDoc}
*/
@Override
protected void init()
{
if ( getSelectedIndex() != -1 )
{
textField.setText( String.valueOf( getSelectedItem() ) );
}
getWidgetAssembler().addWidget( this.textField, 0f, 0f );
getWidgetAssembler().addWidget( button, this.textField.getWidth(), 0f );
}
/**
* Creates a new ComboBox.
*
* @param isHeavyWeight
* @param width the new width
* @param height the new height
* @param model the ListModel (null for auto-generation)
* @param desc
*/
public ComboBox( boolean isHeavyWeight, float width, float height, ListModel listModel, Description desc )
{
super( isHeavyWeight, width, height, null, null, null, listModel );
TextField tf = new TextField( getWidth() - getHeight(), getHeight(), "", desc.getTextFieldDescription() );
tf.setAlignment( TextAlignment.CENTER_LEFT );
tf.setEditable( false );
this.textField = tf;
this.button = new Button( getHeight(), getHeight(), "" );
button.setIcon( desc.buttonSymbol );
button.addButtonListener( assemblerEventsReceiver );
this.list = new List( true, getWidth(), getHeight() * 5f, listModel, desc.getListDescription() );
list.addSelectionListener( this.assemblerEventsReceiver );
list.addMouseListener( assemblerEventsReceiver );
getWidgetAssembler().setKeyEventsDispatched( true );
getWidgetAssembler().setPickDispatched( true );
setMaxListHeightByItems( 5 );
list.setScrollMode( ScrollMode.AUTO );
}
/**
* Creates a new ComboBox.
*
* @param isHeavyWeight
* @param width the new width
* @param height the new height
* @param model the ListModel (null for auto-generation)
*/
public ComboBox( boolean isHeavyWeight, float width, float height, ListModel listModel )
{
this( isHeavyWeight, width, height, listModel, HUD.getTheme().getComboBoxDescription() );
}
/**
* Creates a new ComboBox.
*
* @param width the new width
* @param model the ListModel (null for auto-generation)
* @param height the new height
* @param desc
*/
public ComboBox( float width, float height, ListModel listModel, Description desc )
{
this( DEFAULT_HEAVYWEIGHT, width, height, listModel, desc );
}
/**
* Creates a new ComboBox.
*
* @param width the new width
* @param height the new height
* @param model the ListModel (null for auto-generation)
*/
public ComboBox( float width, float height, ListModel listModel )
{
this( DEFAULT_HEAVYWEIGHT, width, height, listModel );
}
/**
* Creates a new ComboBox with a {@link TextListModel}.
*
* @param isHeavyWeight
* @param width the new width
* @param height the new height
* @param desc
*/
public static final ComboBox newTextCombo( boolean isHeavyWeight, float width, float height, Description desc )
{
return ( new ComboBox( isHeavyWeight, width, height, new TextListModel(), desc ) );
}
/**
* Creates a new ComboBox with a {@link TextListModel}.
*
* @param isHeavyWeight
* @param width the new width
* @param height the new height
*/
public static final ComboBox newTextCombo( boolean isHeavyWeight, float width, float height )
{
return ( new ComboBox( isHeavyWeight, width, height, new TextListModel() ) );
}
/**
* Creates a new ComboBox with a {@link TextListModel}.
*
* @param width the new width
* @param model the ListModel (null for auto-generation)
* @param height the new height
* @param desc
*/
public static final ComboBox newTextCombo( float width, float height, Description desc )
{
return ( new ComboBox( width, height, new TextListModel(), desc ) );
}
/**
* Creates a new ComboBox with a {@link TextListModel}.
*
* @param width the new width
* @param height the new height
* @param model the ListModel (null for auto-generation)
*/
public static final ComboBox newTextCombo( float width, float height )
{
return ( new ComboBox( width, height, new TextListModel() ) );
}
}