/**
* 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.input.devices.components.DeviceComponent;
import org.jagatoo.input.devices.components.DigitalDeviceComponent;
import org.jagatoo.input.devices.components.Key;
import org.jagatoo.input.devices.components.Keys;
import org.jagatoo.input.devices.components.MouseButton;
import org.openmali.types.twodee.Dim2f;
import org.openmali.types.twodee.Dim2i;
import org.openmali.vecmath2.Colorf;
import org.openmali.vecmath2.Vector2f;
import org.openmali.vecmath2.Vector2i;
import org.xith3d.scenegraph.Texture2D;
import org.xith3d.scenegraph.Texture2DCanvas;
import org.xith3d.ui.hud.HUD;
import org.xith3d.ui.hud.base.AbstractList;
import org.xith3d.ui.hud.base.Border;
import org.xith3d.ui.hud.base.ListModel;
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.listmodels.TextListModel;
import org.xith3d.ui.hud.utils.DrawUtils;
import org.xith3d.ui.hud.utils.HUDFont;
import org.xith3d.ui.hud.utils.HUDTextureUtils;
import org.xith3d.ui.hud.utils.ScrollHandler;
import org.xith3d.ui.hud.utils.ScrollMode;
import org.xith3d.ui.hud.utils.TileMode;
import org.xith3d.ui.hud.widgets.Scrollbar.Direction;
import org.xith3d.ui.text2d.TextAlignment;
/**
* A Scrollable List Widget that renders the contents of a ListModel.
*
* @author Marvin Froehlich (aka Qudus)
*/
public class List extends AbstractList
{
/**
* This class is used to describe a List Widget.
* You can pass it to the List constructor.
* Modifications on the used instance after creating the List Widget
* won't have any effect.
*
* @author Marvin Froehlich (aka Qudus)
*/
public static class Description extends Widget.DescriptionBase
{
private Colorf backgroundColor;
private Texture2D backgroundTexture;
private Border.Description borderDesc;
private int paddingBottom, paddingRight, paddingTop, paddingLeft;
private Colorf hoverBackgroundColor;
private Colorf hoverFontColor;
private Colorf selectionBackgroundColor;
private Colorf selectionFontColor;
public void setBorderDescription( Border.Description borderDesc )
{
this.borderDesc = borderDesc;
}
public Border.Description getBorderDescription()
{
return ( borderDesc );
}
public void setPadding( int bottom, int right, int top, int left )
{
this.paddingBottom = bottom;
this.paddingRight = right;
this.paddingTop = top;
this.paddingLeft = left;
}
public void setPadding( int padding )
{
setPadding( padding, padding, padding, padding );
}
public int getPaddingBottom()
{
return ( paddingBottom );
}
public int getPaddingRight()
{
return ( paddingRight );
}
public int getPaddingTop()
{
return ( paddingTop );
}
public int getPaddingLeft()
{
return ( paddingLeft );
}
public void setBackgroundTexture( Texture2D texture )
{
this.backgroundTexture = texture;
}
public void setBackgroundTexture( String texture )
{
this.backgroundTexture = HUDTextureUtils.getTexture( texture, true );
}
public Texture2D getBackgroundTexture()
{
return ( backgroundTexture );
}
public void setBackgroundColor( Colorf color )
{
this.backgroundColor = color;
}
public Colorf getBackgroundColor()
{
return ( backgroundColor );
}
public Colorf getFontColor()
{
return ( HUD.getTheme().getLabelDescription().getFontColor( false ) );
}
public HUDFont getFont()
{
return ( HUD.getTheme().getLabelDescription().getFont( false ) );
}
public void setHoverBackgroundColor( Colorf color )
{
this.hoverBackgroundColor = color;
}
public Colorf getHoverBackgroundColor()
{
return ( hoverBackgroundColor );
}
public void setHoverFontColor( Colorf color )
{
this.hoverFontColor = color;
}
public Colorf getHoverFontColor()
{
return ( hoverFontColor );
}
public void setSelectionBackgroundColor( Colorf color )
{
this.selectionBackgroundColor = color;
}
public Colorf getSelectionBackgroundColor()
{
return ( selectionBackgroundColor );
}
public void setSelectionFontColor( Colorf color )
{
this.selectionFontColor = color;
}
public Colorf getSelectionFontColor()
{
return ( selectionFontColor );
}
public void set( Description template )
{
this.backgroundTexture = template.backgroundTexture;
this.backgroundColor = template.backgroundColor;
this.borderDesc = template.borderDesc;
this.paddingBottom = template.paddingBottom;
this.paddingRight = template.paddingRight;
this.paddingTop = template.paddingTop;
this.paddingLeft = template.paddingLeft;
this.hoverBackgroundColor = template.hoverBackgroundColor;
this.hoverFontColor = template.hoverFontColor;
this.selectionBackgroundColor = template.selectionBackgroundColor;
this.selectionFontColor = template.selectionFontColor;
}
/**
* Clones this instance and returns the clone.
*/
@Override
public Description clone()
{
return ( new Description( this ) );
}
/**
* Clone-Constructor.
*
* @param template the template Description to copy the values from
*/
private Description( Description template )
{
this.set( template );
}
/**
* Creates a new Image.Description
*
* @param
*/
public Description( int paddingBottom, int paddingRight, int paddingTop, int paddingLeft, Border.Description borderDesc, Colorf backgroundColor, Texture2D backgroundTexture, Colorf hoveredBackgroundColor, Colorf hoveredFontColor, Colorf selectedBackgroundColor, Colorf selectedFontColor )
{
this.paddingBottom = paddingBottom;
this.paddingRight = paddingRight;
this.paddingTop = paddingTop;
this.paddingLeft = paddingLeft;
this.backgroundColor = backgroundColor;
this.backgroundTexture = backgroundTexture;
this.borderDesc = borderDesc;
this.hoverBackgroundColor = hoveredBackgroundColor;
this.hoverFontColor = hoveredFontColor;
this.selectionBackgroundColor = selectedBackgroundColor;
this.selectionFontColor = selectedFontColor;
}
/**
* Creates a new Image.Description
*
* @param
*/
public Description( int paddingBottom, int paddingRight, int paddingTop, int paddingLeft, Border.Description borderDesc, Colorf backgroundColor, String backgroundTexture, Colorf hoveredBackgroundColor, Colorf hoveredFontColor, Colorf selectedBackgroundColor, Colorf selectedFontColor )
{
this( paddingBottom, paddingRight, paddingTop, paddingLeft, borderDesc, backgroundColor, HUDTextureUtils.getTexture( backgroundTexture, true ), hoveredBackgroundColor, hoveredFontColor, selectedBackgroundColor, selectedFontColor );
}
}
protected final Vector2f childrenOffset_HUD = new Vector2f( 0f, 0f );
protected final Vector2i childrenOffset_PX = new Vector2i( 0, 0 );
private int paddingBottom = 0;
private int paddingRight = 3;
private int paddingTop = 0;
private int paddingLeft = 3;
private int heightByItems = -1;
private Colorf fontColor;
private Colorf selectionFontColor = null;
private HUDFont font;
private Colorf hoverBackgroundColor = null;
private Colorf selectionBackgroundColor = null;
private int currentHoveredItem = -1;
private int lastDrawnHoveredItem = -1;
private TextAlignment alignment = TextAlignment.CENTER_LEFT;
private ScrollHandler scrollHandler;
private boolean isFixedToBottom = false;
private boolean addItemSetsSelected = false;
private final ArrayList<ListSelectionListener> selectionListeners = new ArrayList<ListSelectionListener>();
private DigitalDeviceComponent[] upComponents = { Keys.UP };
private DigitalDeviceComponent[] downComponents = { Keys.DOWN };
/**
* {@inheritDoc}
*/
@Override
protected void afterModelSetWidthItems( ListModel model )
{
super.afterModelSetWidthItems( model );
scrollHandler.setLineHeight( (int)model.getItemHeight( 0 ) );
}
/**
* {@inheritDoc}
*/
public boolean setPadding( int paddingBottom, int paddingRight, int paddingTop, int paddingLeft )
{
if ( ( this.paddingBottom == paddingBottom ) &&
( this.paddingRight == paddingRight ) &&
( this.paddingTop == paddingTop ) &&
( this.paddingLeft == paddingLeft ) )
{
return ( false );
}
this.paddingBottom = paddingBottom;
this.paddingRight = paddingRight;
this.paddingTop = paddingTop;
this.paddingLeft = paddingLeft;
updateSizeFactors();
setTextureDirty();
return ( true );
}
/**
* {@inheritDoc}
*/
public final int getPaddingBottom()
{
return ( paddingBottom );
}
/**
* {@inheritDoc}
*/
public final int getPaddingRight()
{
return ( paddingRight );
}
/**
* {@inheritDoc}
*/
public final int getPaddingTop()
{
return ( paddingTop );
}
/**
* {@inheritDoc}
*/
public final int getPaddingLeft()
{
return ( paddingLeft );
}
/**
* Sets the text-color of non-selected Items.
*
* @param color
*/
public void setFontColor( Colorf color )
{
this.fontColor = color;
setTextureDirty();
}
/**
* Gets the text-color of non-selected Items.
*
* @return the text-color of non-selected Items.
*/
public final Colorf getFontColor()
{
return ( fontColor );
}
/**
* Sets the Items' font.
*
* @param font
*/
public void setFont( HUDFont font )
{
this.font = font;
setTextureDirty();
}
/**
* Gets the Items' font.
*
* @return the Items' font.
*/
public final HUDFont getFont()
{
return ( font );
}
/**
* Sets the text-color of the selected Item.
*
* @param color
*/
public void setSelectionFontColor( Colorf color )
{
this.selectionFontColor = color;
setTextureDirty();
}
/**
* Gets the text-color of the selected Item.
*
* @return the text-color of the selected Item.
*/
public final Colorf getSelectionFontColor()
{
return ( selectionFontColor );
}
public Object addItem( int index, Object item, HUDFont font, Colorf color )
{
Object result = super.addItem( index, item );
ListModel model = getModel();
if ( model instanceof TextListModel )
{
( (TextListModel)model ).setItemFont( index, font );
( (TextListModel)model ).setItemColor( index, color );
}
return ( result );
}
public final Object addItem( Object item, HUDFont font, Colorf color )
{
return ( addItem( getItemsCount(), item, font, color ) );
}
public final Object addItem( int index, Object item, Colorf color )
{
return ( addItem( index, item, null, color ) );
}
public final Object addItem( Object item, Colorf color )
{
return ( addItem( getItemsCount(), item, null, color ) );
}
/**
* {@inheritDoc}
*/
@Override
public final Object addItem( int index, Object item )
{
return ( addItem( index, item, null, null ) );
}
/**
* {@inheritDoc}
*/
@Override
public void setHoverBackgroundColor( Colorf color )
{
this.hoverBackgroundColor = color;
}
/**
* {@inheritDoc}
*/
@Override
public final Colorf getHoverBackgroundColor()
{
return ( hoverBackgroundColor );
}
/**
* {@inheritDoc}
*/
@Override
public void setSelectionBackgroundColor( Colorf color )
{
this.selectionBackgroundColor = color;
}
/**
* {@inheritDoc}
*/
@Override
public final Colorf getSelectionBackgroundColor()
{
return ( selectionBackgroundColor );
}
/**
* {@inheritDoc}
*/
@Override
public void setAlignment( TextAlignment alignment )
{
this.alignment = alignment;
}
/**
* {@inheritDoc}
*/
@Override
public final TextAlignment getAlignment()
{
return ( alignment );
}
/**
* {@inheritDoc}
*/
@Override
public void addSelectionListener( ListSelectionListener l )
{
selectionListeners.add( l );
}
/**
* {@inheritDoc}
*/
@Override
public void removeSelectionListener( ListSelectionListener l )
{
selectionListeners.remove( l );
}
protected void notifyOnSelectionChanged( Object oldSelObj, Object newSelObj, int oldSelIdx, int newSelIdx )
{
for ( int i = 0; i < selectionListeners.size(); i++ )
{
selectionListeners.get( i ).onListSelectionChanged( this, oldSelObj, newSelObj, oldSelIdx, newSelIdx );
}
}
protected void notifyOnItemClicked( Object item, int index )
{
for ( int i = 0; i < selectionListeners.size(); i++ )
{
selectionListeners.get( i ).onListItemClicked( this, item, index );
}
}
/**
* {@inheritDoc}
*/
@Override
public void setAddItemSetsSelectedItem( boolean b )
{
this.addItemSetsSelected = b;
}
/**
* {@inheritDoc}
*/
@Override
public final boolean addItemSetsSelectedItem()
{
return ( addItemSetsSelected );
}
/**
* {@inheritDoc}
*/
@Override
public void scrollSelectedItemIntoView()
{
int selIndex = getSelectedIndex();
if ( ( selIndex < 0 ) || ( selIndex >= getItemsCount() ) )
return;
ListModel model = getModel();
float selTop = 0f;
if ( model.getUsesFixedHeight() )
{
selTop = selIndex * model.getItemHeight( 0 );
}
else
{
for ( int i = 0; i < selIndex; i++ )
{
selTop += model.getItemHeight( i );
}
}
if ( selTop + childrenOffset_HUD.getY() < 0f )
{
setChildrenOffset( false, 0f, true, -selTop, true );
}
else
{
float selBottom = selTop + ( model.getUsesFixedHeight() ? model.getItemHeight( 0 ) : model.getItemHeight( selIndex ) );
float contentHeight = getContentHeight();
if ( selBottom + childrenOffset_HUD.getY() > contentHeight )
{
setChildrenOffset( false, 0f, true, contentHeight - selBottom, true );
}
}
}
@Override
protected void afterItemAddedToEnd()
{
super.afterItemAddedToEnd();
if ( isFixedToBottom() )
scrollToBottom();
}
@Override
protected void afterFirstItemAdded()
{
super.afterFirstItemAdded();
scrollHandler.setLineHeight( (int)getModel().getItemHeight( 0 ) );
}
/**
* {@inheritDoc}
*/
@Override
public void setTopIndex( int topIndex )
{
if ( ( topIndex < 0 ) || ( topIndex >= getItemsCount() ) )
throw new IllegalArgumentException( "topIndex out of range" );
ListModel model = getModel();
float topTop = 0f;
if ( model.getUsesFixedHeight() )
{
topTop = topIndex * model.getItemHeight( 0 );
}
else
{
for ( int i = 0; i < topIndex; i++ )
{
topTop += model.getItemHeight( i );
}
}
int n = getItemsCount();
float bottomBottom = topTop;
if ( model.getUsesFixedHeight() )
{
bottomBottom += ( n - topIndex ) * model.getItemHeight( 0 );
}
else
{
for ( int i = topIndex; i < n; i++ )
{
bottomBottom += model.getItemHeight( i );
}
}
topTop = Math.max( 0f, Math.min( topTop, bottomBottom - getContentHeight() ) );
setChildrenOffset( false, 0f, true, -topTop, true );
}
/**
* {@inheritDoc}
*/
@Override
public int getTopIndex()
{
if ( getItemsCount() == 0 )
return ( -1 );
ListModel model = getModel();
if ( model.getUsesFixedHeight() )
{
return ( (int)Math.ceil( -childrenOffset_HUD.getY() / model.getItemHeight( 0 ) ) );
}
float tmp = -childrenOffset_HUD.getY();
int i = 0;
while ( tmp > 0f )
{
tmp -= model.getItemHeight( i++ );
}
return ( i );
}
/**
* {@inheritDoc}
*/
@Override
public final int getBottomIndex()
{
if ( getItemsCount() == 0 )
return ( -1 );
ListModel model = getModel();
if ( model.getUsesFixedHeight() )
{
return ( (int)Math.ceil( ( -childrenOffset_HUD.getY() + getContentHeight() ) / model.getItemHeight( 0 ) ) - 1 );
}
float tmp = -childrenOffset_HUD.getY() + getContentHeight();
int i = 0;
while ( tmp > 0f )
{
tmp -= model.getItemHeight( i++ );
}
return ( i - 1 );
}
/**
* Sets the height by items count.
*
* @param numItems the new height by items unit
*/
public void setHeightByItems( int numItems )
{
if ( getHUD() == null )
{
this.heightByItems = numItems;
}
else
{
float height2 = 0f;
int paddingAndBorder = getPaddingTop() + getPaddingBottom();
if ( getBorder() != null )
{
paddingAndBorder += getBorder().getTopHeight() + getBorder().getBottomHeight();
}
if ( paddingAndBorder > 0 )
{
Dim2f buffer = Dim2f.fromPool();
getSizePixels2HUD_( 0, paddingAndBorder, buffer );
height2 += buffer.getHeight();
Dim2f.toPool( buffer );
}
//if ( getItemsCount() > 0 )
{
ListModel model = getModel();
if ( model.getUsesFixedHeight() )
{
height2 += numItems * Math.max( 0f, model.getItemHeight( 0 ) );
}
else
{
int i0 = getTopIndex();
for ( int i = i0; i < i0 + numItems; i++ )
{
height2 += Math.max( 0f, model.getItemHeight( i ) );
}
}
setSize( getWidth(), height2 );
}
}
}
/**
* Gets the height by items count.
*
* @return the height by items count.
*/
public int getHeightByItems()
{
if ( getItemsCount() == 0 )
return ( -1 );
ListModel model = getModel();
if ( model.getUsesFixedHeight() )
{
return ( (int)Math.floor( getHeight() / model.getItemHeight( 0 ) ) );
}
int i = getTopIndex();
int c = 0;
float height2 = getContentHeight();
while ( height2 > 0f )
{
height2 -= model.getItemHeight( i++ );
c++;
}
return ( c );
}
/**
* Sets the ScrollBar's ScrollMode.<br>
* <br>
* The ScrollBar is visible, if (||)
* <ul>
* <li>more items are in the list, than the list can display at once und ScrollMode is AUTO</li>
* <li>ScrollMode is ALWAYS</li>
* </ul>
*
* @param mode the ScrollBar's ScrollMode
*/
public void setScrollMode( ScrollMode mode )
{
scrollHandler.setScrollMode( mode );
setTextureDirty();
}
/**
* Returns the ScrollBar's ScrollMode.<br>
* <br>
* The ScrollBar is visible, if (||)
* <ul>
* <li>more items are in the list, than the list can display at once und ScrollMode is AUTO</li>
* <li>ScrollMode is ALWAYS</li>
* </ul>
*
* @return the ScrollBar's ScrollMode.
*/
public ScrollMode getScrollMode()
{
return ( scrollHandler.getScrollMode() );
}
/**
* @return true, if the List is currently scrollt to the bottom-most item.
*/
public boolean isScrolledToBottom()
{
return ( getBottomIndex() == getItemsCount() - 1 );
}
/**
* Scrolls the list to the bottom-most item.
*/
public void scrollToBottom()
{
ListModel model = getModel();
int n = model.getItemsCount();
if ( n == 0 )
return;
float bottomBottom = 0f;
if ( model.getUsesFixedHeight() )
{
bottomBottom = n * model.getItemHeight( 0 );
}
else
{
for ( int i = 0; i < n; i++ )
{
bottomBottom += model.getItemHeight( i );
}
}
bottomBottom -= getContentHeight();
setChildrenOffset( false, 0f, true, -Math.max( 0f, bottomBottom ), true );
}
/**
* Fixes or releases this List to always scroll to the lowest item,
* when a new Item is added and the List is currently scrolled to bottom.
*
* @param fixed
*/
public void setFixedToBottom( boolean fixed )
{
this.isFixedToBottom = fixed;
}
/**
* If true, the List always scrolls to the lowest item,
* when a new Item is added and the List is currently scrolled to bottom.
*/
public boolean isFixedToBottom()
{
return ( isFixedToBottom );
}
/**
* Gets the minimum list width, that is able to fully display all items.
*
* @return the minimum width.
*/
public float getMinWidthThatFitsItems()
{
float minWidth = 0f;
ListModel model = getModel();
final int n = model.getItemsCount();
for ( int i = 0; i < n; i++ )
{
float minItemWidth = model.getMinItemWidth( i );
if ( minItemWidth > minWidth )
minWidth = minItemWidth;
}
Dim2f buffer = Dim2f.fromPool();
getSizePixels2HUD_( getPaddingLeft() + getPaddingRight(), 0, buffer );
minWidth += buffer.getWidth();
if ( getBorder() != null )
{
getSizePixels2HUD_( getBorder().getLeftWidth() + getBorder().getRightWidth(), 0, buffer );
minWidth += buffer.getWidth();
}
Dim2f.toPool( buffer );
return ( minWidth );
}
/**
* {@inheritDoc}
*/
@Override
protected boolean blocksFocusMoveDeviceComponent( DeviceComponent dc )
{
if ( upComponents != null )
{
for ( int i = 0; i < upComponents.length; i++ )
{
if ( upComponents[i] == dc )
return ( true );
}
}
if ( downComponents != null )
{
for ( int i = 0; i < downComponents.length; i++ )
{
if ( downComponents[i] == dc )
return ( true );
}
}
return ( false );
}
/**
* {@inheritDoc}
*/
@Override
protected void onKeyPressed( Key key, int modifierMask, long when )
{
super.onKeyPressed( key, modifierMask, when );
for ( int i = 0; i < upComponents.length; i++ )
{
if ( key == upComponents[i] )
{
selectPreviousItem();
scrollSelectedItemIntoView();
return;
}
}
for ( int i = 0; i < downComponents.length; i++ )
{
if ( key == downComponents[i] )
{
selectNextItem();
scrollSelectedItemIntoView();
return;
}
}
}
/**
* {@inheritDoc}
*/
@Override
protected void onMouseExited( boolean isTopMost, boolean hasFocus )
{
super.onMouseExited( isTopMost, hasFocus );
currentHoveredItem = -1;
if ( currentHoveredItem != lastDrawnHoveredItem )
{
getModel().markListDirty();
}
}
/**
* {@inheritDoc}
*/
@Override
protected void onMouseMoved( float x, float y, int buttonsState, long when, boolean isTopMost, boolean hasFocus )
{
super.onMouseMoved( x, y, buttonsState, when, isTopMost, hasFocus );
if ( isTopMost )
{
float contentWidth = getContentWidth();
float contentHeight = getContentHeight();
if ( ( x >= 0f ) && ( y >= 0f ) && ( x < contentWidth ) && ( y < contentHeight ) )
{
final ListModel model = getModel();
final boolean fixed = model.getUsesFixedHeight();
final float itemHeight = model.getItemHeight( 0 );
final int n = getItemsCount();
float itemTop = childrenOffset_HUD.getY() + getPaddingTop();
float itemBottom = itemTop + itemHeight;
int i = 0;
while ( ( itemBottom < y ) && ( i < n ) )
{
itemTop = itemBottom;
itemBottom += ( fixed ? itemHeight : model.getItemHeight( i ) );
i++;
}
if ( i < n )
{
currentHoveredItem = i;
}
else
{
currentHoveredItem = -1;
}
if ( currentHoveredItem != lastDrawnHoveredItem )
{
if ( ( getHoverBackgroundColor() != null ) || model.hoverNeedsRedraw() )
{
model.markListDirty();
}
}
}
}
}
/**
* {@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 )
{
final Dim2f siz = Dim2f.fromPool();
float mouseX = x;
float mouseY = y;
float contentLeft = 0f;
if ( getBorder() != null )
{
getSizePixels2HUD_( getBorder().getLeftWidth(), getBorder().getTopHeight(), siz );
//mouseX -= siz.getWidth();
//mouseY -= siz.getHeight();
getSizePixels2HUD_( getContentLeftPX(), 0, siz );
contentLeft = siz.getWidth();
}
Dim2f.toPool( siz );
float contentWidth = getContentWidth();
float contentHeight = getContentHeight();
if ( ( mouseX >= 0f ) && ( mouseY >= 0f ) && ( mouseX < contentWidth ) && ( mouseY < contentHeight ) )
{
final ListModel model = getModel();
final boolean fixed = model.getUsesFixedHeight();
final float itemHeight = model.getItemHeight( 0 );
final int n = getItemsCount();
float itemTop = childrenOffset_HUD.getY() + getPaddingTop();
float itemBottom = itemTop + itemHeight;
int i = 0;
while ( ( itemBottom < mouseY ) && ( i < n ) )
{
itemTop = itemBottom;
itemBottom += ( fixed ? itemHeight : model.getItemHeight( i ) );
i++;
}
if ( i < n )
{
int oldSelIdx = getSelectedIndex();
Object oldSelObj = getSelectedItem();
if ( model.checkOnMouseButtonPressed( i, contentLeft, contentWidth, itemTop, itemBottom, x, y ) )
{
setSelectedIndex( i );
Object newSelObj = getSelectedItem();
notifyOnSelectionChanged( oldSelObj, newSelObj, oldSelIdx, i );
notifyOnItemClicked( newSelObj, i );
}
else
{
notifyOnItemClicked( getItem( i ), i );
}
}
}
}
}
/**
* {@inheritDoc}
*/
@Override
protected void updateSizesAndMarkDirty()
{
super.updateSizesAndMarkDirty();
if ( getHUD() != null )
{
ListModel model = getModel();
float maxRight = 0f;
float maxBottom = 0f;
float firstHeight = model.getItemHeight( 0 );
int n = getItemsCount();
for ( int i = 0; i < n; i++ )
{
/*
if ( widget.getLeft() + widget.getWidth() > maxRight )
{
maxRight = widget.getLeft() + widget.getWidth();
}
*/
if ( model.getUsesFixedHeight() )
maxBottom += firstHeight;
else
maxBottom += model.getItemHeight( i );
}
scrollHandler.setBounds( maxRight, maxBottom );
if ( model.getItemsCount() > 0 )
{
scrollHandler.setLineHeight( (int)model.getItemHeight( 0 ) );
}
}
setTextureDirty();
}
/**
* {@inheritDoc}
*/
@Override
protected void onAttachedToHUD( HUD hud )
{
super.onAttachedToHUD( hud );
if ( heightByItems >= 0 )
{
setHeightByItems( heightByItems );
heightByItems = -1;
}
}
/**
* {@inheritDoc}
*/
@Override
protected void setContentClipRect( Texture2DCanvas texCanvas, int offsetX, int offsetY, int width, int height )
{
texCanvas.setClip( offsetX - getPaddingLeft(), offsetY - getPaddingTop(), width + getPaddingLeft() + getPaddingRight(), height + getPaddingTop() + getPaddingBottom() );
}
/**
* Draws the background of a hovered item.
*
* @param texCanvas
* @param offsetX
* @param offsetY
* @param width
* @param height
*
* @return true, if a hovered background has been drawn.
*/
protected boolean drawHoveredItemBackground( Texture2DCanvas texCanvas, int offsetX, int offsetY, int width, int height )
{
Colorf color = getHoverBackgroundColor();
if ( color == null )
return ( false );
if ( color.hasAlpha() )
DrawUtils.drawImage( color, null, null, texCanvas, offsetX, offsetY, width, height );
else
DrawUtils.clearImage( color, null, null, texCanvas, offsetX, offsetY, width, height );
return ( true );
}
/**
* Draws the background of a selected item.
*
* @param texCanvas
* @param offsetX
* @param offsetY
* @param width
* @param height
*
* @return true, if a selected background has been drawn.
*/
protected boolean drawSelectedItemBackground( Texture2DCanvas texCanvas, int offsetX, int offsetY, int width, int height )
{
Colorf color = getSelectionBackgroundColor();
if ( color == null )
return ( false );
if ( color.hasAlpha() )
DrawUtils.drawImage( color, null, null, texCanvas, offsetX, offsetY, width, height );
else
DrawUtils.clearImage( color, null, null, texCanvas, offsetX, offsetY, width, height );
return ( true );
}
/**
* {@inheritDoc}
*/
@Override
protected void drawWidget( Texture2DCanvas texCanvas, int offsetX, int offsetY, int width, int height, boolean drawsSelf )
{
ListModel model = getModel();
float offsetY_HUD = childrenOffset_HUD.getY();
float contentHeight = getContentHeight();
offsetX -= getPaddingLeft();
//width += getPaddingLeft() + getPaddingRight();
offsetY += childrenOffset_PX.getY();
//offsetY += getPaddingTop();
float contentWidth = getContentWidth();
int contentWidthPX = getContentWidthPX();
int itemBGWidth = contentWidthPX + getPaddingLeft() + getPaddingRight();
int i0 = 0;
if ( model.getUsesFixedHeight() )
{
float itemHeight = model.getItemHeight( 0 );
Dim2i buffer = Dim2i.fromPool();
getSizeHUD2Pixels_( 0f, itemHeight, buffer );
int itemHeight_PX = buffer.getHeight();
Dim2i.toPool( buffer );
i0 = (int)Math.floor( -childrenOffset_HUD.getY() / itemHeight );
offsetY_HUD += i0 * itemHeight;
offsetY += i0 * itemHeight_PX;
}
int n = model.getItemsCount();
for ( int i = i0; i < n; i++ )
{
Widget w = getWidget( contentWidth, i );
if ( offsetY_HUD >= contentHeight )
break;
int itemHeightPX = __HUD_base_PrivilegedAccess.getHeightPX( w );
if ( offsetY_HUD + w.getHeight() > 0f )
{
boolean bg = true;
if ( bg && ( i == currentHoveredItem ) )
{
bg = !drawHoveredItemBackground( texCanvas, offsetX, offsetY, itemBGWidth, itemHeightPX );
}
if ( bg && model.isSelected( i ) )
{
bg = !drawSelectedItemBackground( texCanvas, offsetX, offsetY, itemBGWidth, itemHeightPX );
}
if ( i == currentHoveredItem )
{
__HUD_base_PrivilegedAccess.onMouseEntered( w, true, hasFocus( true ) );
}
w.drawAndUpdateWidget( texCanvas, offsetX + getPaddingLeft(), offsetY, contentWidthPX, itemHeightPX, false );
if ( i == currentHoveredItem )
{
__HUD_base_PrivilegedAccess.onMouseExited( w, true, hasFocus( true ) );
}
}
offsetY += itemHeightPX;
offsetY_HUD += w.getHeight();
}
lastDrawnHoveredItem = currentHoveredItem;
}
/**
* Sets the display offset for the child-Widgets.
*
* @param changeX
* @param offsetX the new offset
* @param changeY
* @param offsetY the new offset
* @param updateScrollbars
*/
private void setChildrenOffset( boolean changeX, float offsetX, boolean changeY, float offsetY, boolean updateScrollbars )
{
if ( getHUD() == null )
return;
Dim2i buffer2 = Dim2i.fromPool();
getSizeHUD2Pixels_( offsetX, offsetY, buffer2 );
if ( changeX )
{
childrenOffset_PX.setX( buffer2.getWidth() );
childrenOffset_HUD.setX( offsetX );
}
if ( changeY )
{
childrenOffset_PX.setY( buffer2.getHeight() );
childrenOffset_HUD.setY( offsetY );
}
Dim2i.toPool( buffer2 );
setTextureDirty();
if ( updateScrollbars )
{
if ( changeX )
scrollHandler.setScrollHValue( -(int)offsetX );
if ( changeY )
scrollHandler.setScrollVValue( -(int)offsetY );
}
}
private void initScrollHandler()
{
this.scrollHandler = new ScrollHandler( this, getWidgetAssembler(), true, true )
{
@Override
public void onScrolled( Direction direction, int newValue )
{
if ( direction == Direction.VERTICAL )
setChildrenOffset( false, 0, true, -newValue, false );
else
setChildrenOffset( true, -newValue, false, 0, false );
}
};
}
/**
* {@inheritDoc}
*/
@Override
protected void init()
{
}
/**
* Creates a new List Widget.
*
* @param isHeavyWeight
* @param width the new width
* @param height the new height
* @param model the ListModel (null for auto-generation)
* @param listDesc the List.Description to describe this new List Widget
*/
public List( boolean isHeavyWeight, float width, float height, ListModel model, Description listDesc )
{
super( isHeavyWeight, width, height, listDesc.getBackgroundColor(), listDesc.getBackgroundTexture(), TileMode.TILE_BOTH, model );
initScrollHandler();
this.setBorder( listDesc.getBorderDescription() );
this.setPadding( listDesc.getPaddingBottom(), listDesc.getBorderDescription().getRightWidth(), listDesc.getPaddingTop(), listDesc.getBorderDescription().getLeftWidth() );
setFontColor( listDesc.getFontColor() );
setSelectionFontColor( listDesc.getSelectionFontColor() );
setFont( listDesc.getFont() );
setHoverBackgroundColor( listDesc.getHoverBackgroundColor() );
setSelectionBackgroundColor( listDesc.getSelectionBackgroundColor() );
}
/**
* Creates a new List Widget.
*
* @param isHeavyWeight
* @param width the new width
* @param height the new height
* @param model the ListModel (null for auto-generation)
*/
public List( boolean isHeavyWeight, float width, float height, ListModel model )
{
this( isHeavyWeight, width, height, model, HUD.getTheme().getListDescription() );
}
/**
* Creates a new List Widget.
*
* @param width the new width
* @param height the new height
* @param model the ListModel (null for auto-generation)
* @param listDesc the List.Description to describe this new List Widget
*/
public List( float width, float height, ListModel model, Description listDesc )
{
this( DEFAULT_HEAVYWEIGHT, width, height, model, listDesc );
}
/**
* Creates a new List Widget.
*
* @param isHeavyWeight
* @param width the new width
* @param height the new height
* @param model the ListModel (null for auto-generation)
*/
public List( float width, float height, ListModel model )
{
this( DEFAULT_HEAVYWEIGHT, width, height, model );
}
/**
* Creates a new List Widget with a {@link TextListModel}.
*
* @param isHeavyWeight
* @param width the new width
* @param height the new height
* @param listDesc the List.Description to describe this new List Widget
*/
public static final List newTextList( boolean isHeavyWeight, float width, float height, Description listDesc )
{
return ( new List( isHeavyWeight, width, height, new TextListModel(), listDesc ) );
}
/**
* Creates a new List Widget with a {@link TextListModel}.
*
* @param width the new width
* @param height the new height
* @param listDesc the List.Description to describe this new List Widget
*/
public static final List newTextList( float width, float height, Description listDesc )
{
return ( new List( width, height, new TextListModel(), listDesc ) );
}
/**
* Creates a new List Widget with a {@link TextListModel}.
*
* @param isHeavyWeight
* @param width the new width
* @param height the new height
*/
public static final List newTextList( boolean isHeavyWeight, float width, float height )
{
return ( new List( isHeavyWeight, width, height, new TextListModel() ) );
}
/**
* Creates a new List Widget with a {@link TextListModel}.
*
* @param width the new width
* @param height the new height
*/
public static final List newTextList( float width, float height )
{
return ( new List( width, height, new TextListModel() ) );
}
}