/**
* 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.NamableObject;
import org.jagatoo.input.devices.components.ControllerAxis;
import org.jagatoo.input.devices.components.ControllerButton;
import org.jagatoo.input.devices.components.DeviceComponent;
import org.jagatoo.input.devices.components.Key;
import org.jagatoo.input.devices.components.MouseButton;
import org.jagatoo.opengl.enums.TestFunction;
import org.openmali.types.primitives.MutableLong;
import org.openmali.types.twodee.Dim2f;
import org.openmali.types.twodee.Dim2i;
import org.openmali.types.twodee.Positioned2f;
import org.openmali.types.twodee.Rect2i;
import org.openmali.types.twodee.Sized2f;
import org.openmali.types.twodee.Sized2fRO;
import org.openmali.vecmath2.Colorf;
import org.openmali.vecmath2.Point2f;
import org.openmali.vecmath2.Tuple2f;
import org.openmali.vecmath2.Tuple2i;
import org.xith3d.scenegraph.Node;
import org.xith3d.scenegraph.RenderingAttributes;
import org.xith3d.scenegraph.Shape3D;
import org.xith3d.scenegraph.Texture2DCanvas;
import org.xith3d.scenegraph.Transform3D;
import org.xith3d.scenegraph.TransformGroup;
import org.xith3d.scenegraph.Geometry.Optimization;
import org.xith3d.scenegraph.Texture2DCanvas.DrawCallback2D;
import org.xith3d.scenegraph.primitives.DrawRectangle;
import org.xith3d.scenegraph.utils.ShapeUtils;
import org.xith3d.ui.hud.HUD;
import org.xith3d.ui.hud.__HUD_PrivilegedAccess;
import org.xith3d.ui.hud.borders.BorderFactory;
import org.xith3d.ui.hud.contextmenu.ContextMenu;
import org.xith3d.ui.hud.listeners.WidgetContainerListener;
import org.xith3d.ui.hud.listeners.WidgetControllerListener;
import org.xith3d.ui.hud.listeners.WidgetFocusListener;
import org.xith3d.ui.hud.listeners.WidgetInputListener;
import org.xith3d.ui.hud.listeners.WidgetKeyboardListener;
import org.xith3d.ui.hud.listeners.WidgetLocationListener;
import org.xith3d.ui.hud.listeners.WidgetMouseListener;
import org.xith3d.ui.hud.listeners.WidgetSizeListener;
import org.xith3d.ui.hud.listeners.WidgetVisibilityListener;
import org.xith3d.ui.hud.utils.Cursor;
import org.xith3d.ui.hud.utils.DrawUtils;
import org.xith3d.ui.hud.utils.DropShadowFactory;
import org.xith3d.ui.hud.utils.HUDPickResult;
import org.xith3d.ui.hud.utils.HUDPickResult.HUDPickReason;
/**
* All Widgets to be added to a HUD must extend this class.
*
* @author Marvin Froehlich (aka Qudus)
*/
public abstract class Widget implements Positioned2f, Sized2f, NamableObject
{
protected static abstract class DescriptionBase
{
/**
* {@inheritDoc}
*/
@Override
public String toString()
{
String s = this.getClass().getName() + "\n{\n";
//final java.lang.reflect.Field[] fields = this.getClass().getDeclaredFields();
final java.lang.reflect.Method[] methods = this.getClass().getMethods();
for ( int i = 0; i < methods.length; i++ )
{
String name = methods[ i ].getName();
if ( name.startsWith( "get" ) )
name = name.substring( 3 );
else if ( name.startsWith( "is" ) )
name = name.substring( 2 );
else
continue;
String value;
try
{
Object valueObj = methods[ i ].invoke( this, (Object[])null );
if ( valueObj == null )
value = null;
else
value = valueObj.toString();
}
catch ( Throwable t )
{
//t.printStackTrace();
value = "[N/A]";
}
s += " " + name + " = " + value + "\n";
}
s += "}";
return ( s );
}
}
private static final MutableLong MIN_UPDATE_DELAY = new MutableLong( 50000000L );
private long forcedUpdateDelay = -1L;
private static final RenderingAttributes RENDERING_ATTRIBUTES = new RenderingAttributes();
static
{
RENDERING_ATTRIBUTES.setDepthTestFunction( TestFunction.ALWAYS );
}
private final boolean isHeavyWeight;
private Widget hostWidget = null;
private boolean isPassive = false;
private Boolean hierarchyOK = null;
private final TransformGroup transformGroup;
private final WidgetAssembler widgetAssembler;
private String name = "";
private Object userObject = null;
private Widget assembly = null;
private Border border = null;
private int widthPX = -1, heightPX = -1;
private int contentWidthPX = -1, contentHeightPX = -1;
protected float transformWidth_Pixels2HUD = -1f, transformHeight_Pixels2HUD = -1f;
private HUD hud = null;
private WidgetContainer container = null;
private ContextMenu contextMenu = null;
private String tooltip = null;
private Widget cachedTooltipWidget = null;
private final Dim2f size = new Dim2f( 0f, 0f );
private final Tuple2f location = new Point2f( 0f, 0f );
private int zIndex = 0;
private boolean hovered = false;
private boolean isVisible = true;
private boolean isClickable = true;
private boolean isPickable = true;
private boolean isDraggable;
private boolean isInitialized = false;
private boolean isInitializing = false;
private Cursor.Type cursorType = null;
private Point2f dragStart = null;
private final Point2f tmpDragStart = new Point2f();
private Tuple2f dragStartWidget = null;
private final Tuple2f tmpDragStartWidget = new Point2f();
private boolean focussable = true;
private boolean focusRequested = false;
private float transparency = 0.0f;
private DrawRectangle shape = null;
private final DrawCallback2D drawCallback;
boolean isThisWidgetDirty = true;
private boolean isAHostedWidgetDirty = true;
private boolean hasDropShadow = false;
private final Rect2i oldClip = new Rect2i();
private final Rect2i clip = new Rect2i();
private final ArrayList<WidgetKeyboardListener> keyboardListeners = new ArrayList<WidgetKeyboardListener>( 1 );
private final ArrayList<WidgetMouseListener> mouseListeners = new ArrayList<WidgetMouseListener>( 1 );
private final ArrayList<WidgetControllerListener> controllerListeners = new ArrayList<WidgetControllerListener>( 1 );
private final ArrayList<WidgetFocusListener> focusListeners = new ArrayList<WidgetFocusListener>( 1 );
private final ArrayList<WidgetLocationListener> locationListeners = new ArrayList<WidgetLocationListener>( 1 );
private final ArrayList<WidgetSizeListener> sizeListeners = new ArrayList<WidgetSizeListener>( 1 );
private final ArrayList<WidgetVisibilityListener> visibilityListeners = new ArrayList<WidgetVisibilityListener>( 1 );
private final ArrayList<WidgetContainerListener> containerListeners = new ArrayList<WidgetContainerListener>( 1 );
/**
* Returns true, if the widget has a TransformGroup and DrawTexture.<br />
* Lightweight Widgets can only be use to add to heavyweight WidgetContainers or to containers,
* that are added to heavyweight containers, etc.
*
* @return is this Widget heavyweight or lightweight.
*/
public final boolean isHeavyWeight()
{
return ( isHeavyWeight );
}
/**
* @return this Widget's Node to be added to the scenegraph
*/
final Node getSGNode()
{
return ( transformGroup );
}
/**
* Gets, the Window, of which this is the content pane.
*
* @return the parent Window.
*/
public Window getParentWindow()
{
if ( getContainer() == null )
return ( null );
return ( getContainer().getParentWindow() );
}
/**
* Sets this Widget's name
*/
public void setName( String name )
{
this.name = name;
if ( transformGroup != null )
transformGroup.setName( name );
}
/**
* @return this widget's name
*/
public final String getName()
{
return ( name );
}
/**
* Sets this Widget's user-Object.
*
* @param userObject the new user-Object
*/
public void setUserObject( Object userObject )
{
this.userObject = userObject;
}
/**
* @return this Widget's user-Object
*/
public final Object getUserObject()
{
return ( userObject );
}
/**
* Sets the ContextMenu for this Widget and inherits it to all children,
* if this is a container.
*
* @param contextMenu
*/
public void setContextMenu( ContextMenu contextMenu )
{
this.contextMenu = contextMenu;
}
/**
* @return the (inherited) ContextMenu.
*/
public ContextMenu getContextMenu()
{
if ( contextMenu != null )
{
if ( contextMenu.getHUD() == null )
{
contextMenu.setHUD( this.getHUD() );
}
return ( contextMenu );
}
final WidgetContainer container = getContainer();
if ( container != null )
{
/*
if ( container instanceof HUD )
return ( null );
*/
return ( container.getContextMenu() );
}
return ( null );
}
/**
* Sets the tooltip to be displayed when the mouse stopps over this Widget.
* Please see {@link HUD#setToolTipFactory(org.xith3d.ui.hud.utils.ToolTipFactory)}
* and {@link HUD#getToolTipFactory()}.
*
* @param tooltip
*/
public void setToolTip( String tooltip )
{
this.tooltip = tooltip;
this.cachedTooltipWidget = null;
}
/**
* @return the tooltip to be displayed when the mouse stopps over this Widget.
* Please see {@link HUD#setToolTipFactory(org.xith3d.ui.hud.utils.ToolTipFactory)}
* and {@link HUD#getToolTipFactory()}.
*/
public final String getToolTip()
{
return ( tooltip );
}
/**
* @return whether this Widget has a tooltip (!= null and not empty String).
*/
public final boolean hasToolTip()
{
return ( ( tooltip != null ) && ( tooltip.length() > 0 ) );
}
void setCachedToolTipWidget( Widget tooltipWidget )
{
this.cachedTooltipWidget = tooltipWidget;
}
final Widget getCachedToolTipWidget()
{
return ( cachedTooltipWidget );
}
/**
* Sets, if this Widgets has a drop shadow.
*
* @see DropShadowFactory
* @see HUD#setDropShadowFactory(DropShadowFactory)
* @see HUD#getDropShadowFactory()
*
* @param b
*/
public void setHasDropShadow( boolean b )
{
if ( this.hasDropShadow == b )
return;
this.hasDropShadow = b;
this.setSize( getWidth(), getHeight(), true );
}
/**
* Gets, if this Widget has a drop shadow.
*
* @see DropShadowFactory
* @see HUD#setDropShadowFactory(DropShadowFactory)
* @see HUD#getDropShadowFactory()
*
* @return if this Widget has a drop shadow.
*/
public final boolean hasDropShadow()
{
return ( hasDropShadow );
}
/**
* @return the WidgetAssembler attached to this Widget
*/
protected final WidgetAssembler getWidgetAssembler()
{
return ( widgetAssembler );
}
/**
* @return a width that's visually equal to the given height
*
* @param height the height to calculate a visually equal height
*/
protected final float getEqualWidth_( float height )
{
if ( getContainer() != null )
return ( getContainer().getEqualWidth( height ) );
if ( getHUD() != null )
return ( getHUD().getCoordinatesConverter().getEqualWidth( height ) );
throw new Error( "This method can't be executed on a widget, that is not attached to the HUD." );
}
/**
* Calculates a height that's visually equal to the given width.
*
* @param width the width to calculate a visually equal height
*
* @return the buffer back again
*/
protected final float getEqualHeight_( float width )
{
if ( getContainer() != null )
return ( getContainer().getEqualHeight( width ) );
if ( getHUD() != null )
return ( getHUD().getCoordinatesConverter().getEqualHeight( width ) );
throw new Error( "This method can't be executed on a widget, that is not attached to the HUD." );
}
/**
* Calculates HUD size from these pixel-values.
*
* @param x the canvas-x-value to transform
* @param y the canvas-y-value to transform
* @param buffer the buffer to write the values to
*
* @return the buffer back again
*/
protected final <Dim2f_ extends Dim2f> Dim2f_ getSizePixels2HUD_( int x, int y, Dim2f_ buffer )
{
if ( getContainer() != null )
return ( getContainer().getSizePixels2HUD( x, y, buffer ) );
if ( getHUD() != null )
return ( getHUD().getCoordinatesConverter().getSizePixels2HUD( x, y, buffer ) );
throw new Error( "This method can't be executed on a widget, that is not attached to the HUD." );
}
/**
* Calculates HUD location from these pixel-values.
*
* @param x the canvas-x-value to transform
* @param y the canvas-y-value to transform
* @param buffer the buffer to write the values to
*
* @return the buffer back again
*/
protected final <Tuple2f_ extends Tuple2f> Tuple2f_ getLocationPixels2HUD_( int x, int y, Tuple2f_ buffer )
{
x -= getContentLeftPX();
y -= getContentTopPX();
if ( getContainer() != null )
{
getContainer().getLocationPixels2HUD( x, y, buffer );
buffer.sub( getLeft(), getTop() );
return ( buffer );
}
if ( getHUD() != null )
{
getHUD().getCoordinatesConverter().getLocationPixels2HUD( x, y, buffer );
buffer.sub( getLeft(), getTop() );
return ( buffer );
}
throw new Error( "This method can't be executed on a widget, that is not attached to the HUD." );
}
/**
* Calculates pixel size from these HUD-values.
*
* @param x the HUD-x-value to transform
* @param y the HUD-y-value to transform
* @param buffer the buffer to write the values to
*
* @return the buffer back again
*/
protected final <Dim2i_ extends Dim2i> Dim2i_ getSizeHUD2Pixels_( float x, float y, Dim2i_ buffer )
{
if ( getContainer() != null )
return ( getContainer().getSizeHUD2Pixels( x, y, buffer ) );
if ( getHUD() != null )
return ( getHUD().getCoordinatesConverter().getSizeHUD2Pixels( x, y, buffer ) );
throw new Error( "This method can't be executed on a widget, that is not attached to the HUD." );
}
/**
* Calculates pixel size from these HUD-values.
*
* @param x the HUD-x-value to transform
* @param y the HUD-y-value to transform
* @param buffer the buffer to write the values to
*
* @return the buffer back again
*/
protected final <Tuple2i_ extends Tuple2i> Tuple2i_ getRelLocationHUD2Pixels_( float x, float y, Tuple2i_ buffer )
{
if ( getContainer() != null )
return ( getContainer().getRelLocationHUD2Pixels( x, y, buffer ) );
if ( getHUD() != null )
return ( getHUD().getCoordinatesConverter().getLocationHUD2Pixels( x, y, buffer ) );
throw new Error( "This method can't be executed on a widget, that is not attached to the HUD." );
}
/**
* Calculates pixel location from these HUD-values.
*
* @param x the HUD-x-value to transform
* @param y the HUD-y-value to transform
* @param buffer the buffer to write the values to
*
* @return the buffer back again
*/
protected final <Tuple2i_ extends Tuple2i> Tuple2i_ getLocationHUD2Pixels_( float x, float y, Tuple2i_ buffer )
{
if ( getContainer() != null )
return ( getContainer().getLocationHUD2Pixels( x, y, buffer ) );
if ( getHUD() != null )
return ( getHUD().getCoordinatesConverter().getLocationHUD2Pixels( x, y, buffer ) );
throw new Error( "This method can't be executed on a widget, that is not attached to the HUD." );
}
/**
* Calculates scenegraph width and height from these HUD-values.
*
* @param x the HUD-x-value to transform
* @param y the HUD-y-value to transform
* @param buffer the buffer to write the values to
*
* @return the buffer back again
*/
protected final <Dim2f_ extends Dim2f> Dim2f_ getSizeHUD2SG_( float x, float y, Dim2f_ buffer )
{
if ( getContainer() != null )
return ( getContainer().getSizeHUD2SG( x, y, buffer ) );
if ( getHUD() != null )
return ( getHUD().getCoordinatesConverter().getSizeHUD2SG( x, y, buffer ) );
throw new Error( "This method can't be executed on a widget, that is not attached to the HUD." );
}
/**
* Calculates scenegraph location from these HUD-values.
*
* @param x the HUD-x-value to transform
* @param y the HUD-y-value to transform
* @param buffer the buffer to write the values to
*
* @return the buffer back again
*/
protected final <Tuple2f_ extends Tuple2f> Tuple2f_ getLocationHUD2SG_( float x, float y, Tuple2f_ buffer )
{
if ( getContainer() != null )
return ( getContainer().getLocationHUD2SG( x, y, buffer ) );
if ( getHUD() != null )
return ( getHUD().getCoordinatesConverter().getLocationHUD2SG( x, y, buffer ) );
throw new Error( "This method can't be executed on a widget, that is not attached to the HUD." );
}
/**
* Calculates HUD size from these scenegraph-values.
*
* @param x the scenegraph-x-value to transform
* @param y the scenegraph-y-value to transform
* @param buffer the buffer to write the values to
*
* @return the buffer back again
*/
protected final <Dim2f_ extends Dim2f> Dim2f_ getSizeSG2HUD_( float x, float y, Dim2f_ buffer )
{
if ( getContainer() != null )
return ( getContainer().getSizeSG2HUD( x, y, buffer ) );
if ( getHUD() != null )
return ( getHUD().getCoordinatesConverter().getSizeSG2HUD( x, y, buffer ) );
throw new Error( "This method can't be executed on a widget, that is not attached to the HUD." );
}
/**
* Calculates HUD location from these scenegraph-values.
*
* @param x the scenegraph-x-value to transform
* @param y the scenegraph-y-value to transform
* @param buffer the buffer to write the values to
*
* @return the buffer back again
*/
protected final <Tuple2f_ extends Tuple2f> Tuple2f_ getLocationSG2HUD_( float x, float y, Tuple2f_ buffer )
{
if ( getContainer() != null )
{
getContainer().getLocationSG2HUD( x, y, buffer );
/*
if ( getAssembly() != null )
{
buffer.sub( getAssembly().getLeft(), getAssembly().getTop() );
}
*/
return ( buffer );
}
if ( getHUD() != null )
{
getHUD().getCoordinatesConverter().getLocationSG2HUD( x, y, buffer );
/*
if ( getAssembly() != null )
{
buffer.sub( getAssembly().getLeft(), getAssembly().getTop() );
}
*/
return ( buffer );
}
throw new Error( "This method can't be executed on a widget, that is not attached to the HUD." );
}
/**
* Retrieves the size these pixels have on this WidgetContainer.
*
* @param x the x-count of pixels
* @param y the y-count of pixels
* @param buffer the buffer to write the values to
*
* @return the buffer back again
*/
protected final <Dim2f_ extends Dim2f> Dim2f_ getSizeOfPixels_( int x, int y, Dim2f_ buffer )
{
if ( getContainer() != null )
return ( getContainer().getSizeOfPixels( x, y, buffer ) );
if ( getHUD() != null )
return ( getHUD().getCoordinatesConverter().getSizeOfPixels( x, y, buffer ) );
throw new Error( "This method can't be executed on a widget, that is not attached to the HUD." );
}
/**
* Computes the absolute position of the given Widget on the HUD.
*
* @param buffer
*/
protected final <Tuple2f_ extends Tuple2f> Tuple2f_ getAbsoluteLocationOnHUD_( Tuple2f_ buffer )
{
if ( getHUD() != null )
{
getHUD().getCoordinatesConverter().getAbsoluteLocationOnHUD( this, buffer );
return ( buffer );
}
throw new Error( "This method can't be executed on a widget, that is not attached to the HUD." );
}
/**
* Adds a new {@link WidgetKeyboardListener}.
*
* @param l
*/
public void addKeyboardListener( WidgetKeyboardListener l )
{
keyboardListeners.add( l );
}
/**
* Removes a {@link WidgetKeyboardListener}.
*
* @param l
*/
public void removeKeyboardListener( WidgetKeyboardListener l )
{
keyboardListeners.remove( l );
}
/**
* Adds a new {@link WidgetMouseListener}.
*
* @param l
*/
public void addMouseListener( WidgetMouseListener l )
{
mouseListeners.add( l );
}
/**
* Removes a {@link WidgetMouseListener}.
*
* @param l
*/
public void removeMouseListener( WidgetMouseListener l )
{
mouseListeners.remove( l );
}
/**
* Adds a new {@link WidgetControllerListener}.
*
* @param l
*/
public void addControllerListener( WidgetControllerListener l )
{
controllerListeners.add( l );
}
/**
* Removes a {@link WidgetControllerListener}.
*
* @param l
*/
public void removeControllerListener( WidgetControllerListener l )
{
controllerListeners.remove( l );
}
/**
* Adds a new WidgetInputListener.
*
* @param l
*/
public final void addInputListener( WidgetInputListener l )
{
addKeyboardListener( l );
addMouseListener( l );
addControllerListener( l );
}
/**
* Removes a WidgetInputListener.
*
* @param l
*/
public final void removeInputListener( WidgetInputListener l )
{
removeKeyboardListener( l );
removeMouseListener( l );
removeControllerListener( l );
}
/**
* Adds a new WidgetFocusListener.
*
* @param l
*/
public void addFocusListener( WidgetFocusListener l )
{
focusListeners.add( l );
}
/**
* Removes a WidgetFocusListener.
*
* @param l
*/
public void removeFocusListener( WidgetFocusListener l )
{
focusListeners.remove( l );
}
/**
* Adds a new WidgetLocationListener.
*
* @param l
*/
public void addLocationListener( WidgetLocationListener l )
{
locationListeners.add( l );
}
/**
* Removes a WidgetFocusListener.
*
* @param l
*/
public void removeLocationListener( WidgetLocationListener l )
{
locationListeners.remove( l );
}
/**
* Adds a new WidgetSizeListener.
*
* @param l
*/
public void addSizeListener( WidgetSizeListener l )
{
sizeListeners.add( l );
}
/**
* Removes a WidgetSizeListener.
*
* @param l
*/
public void removeSizeListener( WidgetSizeListener l )
{
sizeListeners.remove( l );
}
/**
* Adds a new WidgetVisibilityListener.
*
* @param l
*/
public void addVisibilityListener( WidgetVisibilityListener l )
{
visibilityListeners.add( l );
}
/**
* Removes a WidgetVisibilityListener.
*
* @param l
*/
public void removeVisibilityListener( WidgetVisibilityListener l )
{
visibilityListeners.remove( l );
}
/**
* Adds a new WidgetContainerListener.
*
* @param l
*/
public void addContainerListener( WidgetContainerListener l )
{
containerListeners.add( l );
}
/**
* Removes a WidgetContainerListener.
*
* @param l
*/
public void removeContainerListener( WidgetContainerListener l )
{
containerListeners.remove( l );
}
void notifyContainerListenersAboutAttachedWidget( Widget widget, WidgetContainer container )
{
for ( int i = 0; i < containerListeners.size(); i++ )
containerListeners.get( i ).onWidgetAttachedToContainer( widget, container );
}
void notifyContainerListenersAboutDetachedWidget( Widget widget, WidgetContainer container )
{
for ( int i = 0; i < containerListeners.size(); i++ )
containerListeners.get( i ).onWidgetDetachedFromContainer( widget, container );
}
/**
* Sets whether this Widget can get the focus or not.
*
* @param focussable
*/
public void setFocussable( boolean focussable )
{
this.focussable = focussable;
}
/**
* @return whether this Widget can get the focus or not.
*/
public final boolean isFocussable()
{
return ( focussable );
}
/**
* Requests the focus from the HUD system.<br>
* The focus will be gained on next loop iteration.
*/
public void requestFocus()
{
if ( !isFocussable() )
{
return;
}
if ( isInitialized() && ( getHUD() != null ) )
{
if ( getContainer() != null )
getContainer().focus( this );
else
__HUD_PrivilegedAccess.focus( getHUD(), this );
}
else
{
focusRequested = true;
}
}
/**
* This event is fired, when the focus is gained to a Widget.
*/
protected void onFocusGained()
{
for ( int i = 0; i < focusListeners.size(); i++ )
focusListeners.get( i ).onFocusGained( this );
}
/**
* This event is fired, when the focus is lost by a Widget.
*/
protected void onFocusLost()
{
for ( int i = 0; i < focusListeners.size(); i++ )
focusListeners.get( i ).onFocusLost( this );
}
/**
* Is this Widget focused?
*
* @param testLeaf only returns true, if this Widget is focused and is a Leaf
*/
public final boolean hasFocus( boolean testLeaf )
{
if ( getHUD() != null )
{
final Widget cfw = __HUD_PrivilegedAccess.getCurrentFocusedWidget( getHUD(), testLeaf );
if ( cfw == null )
return ( false );
if ( cfw == this )
return ( true );
if ( ( this.getAssembly() == cfw ) && ( getAssembly().getWidgetAssembler().getCurrentFocussedWidget() == this ) )
return ( true );
}
return ( false );
}
/**
* Is this Widget focused?
*/
public final boolean hasFocus()
{
return ( hasFocus( false ) );
}
/**
* Checks, if this Widget blocks the given DeviceComponent for focus-moves.
*
* @param dc
*
* @return Widget blocks the queried DeviceComponent?
*/
protected boolean blocksFocusMoveDeviceComponent( DeviceComponent dc )
{
return ( false );
}
protected final void bindToGlobalMouseMovement()
{
__HUD_PrivilegedAccess.bindMouseMovement( getHUD(), this );
}
/**
* This method is called when the mouse entered the Widget area.
*
* @param isTopMost is this Widget topMost
* @param hasFocus is this Widget focused
*/
protected void onMouseEntered( boolean isTopMost, boolean hasFocus )
{
if ( isTopMost )
{
this.hovered = true;
}
for ( int i = 0; i < mouseListeners.size(); i++ )
mouseListeners.get( i ).onMouseEntered( this, isTopMost, hasFocus );
}
/**
* This method is called when the mouse exited the Widget area.
*
* @param isTopMost is this Widget topMost
* @param hasFocus is this Widget focused
*/
protected void onMouseExited( boolean isTopMost, boolean hasFocus )
{
if ( isTopMost )
{
this.hovered = false;
}
for ( int i = 0; i < mouseListeners.size(); i++ )
mouseListeners.get( i ).onMouseExited( this, isTopMost, hasFocus );
}
/**
*
* @param canvasX
* @param canvasY
* @param widgetX
* @param widgetY
*/
protected void startDragging( int canvasX, int canvasY, float widgetX, float widgetY )
{
/*
Tuple2f pos = getContainer().getLocationCanvas2HUD( x, y );
pos.sub( this.getLocation() );
*/
dragStart = tmpDragStart;
Tuple2f tmp = Tuple2f.fromPool();
getAbsoluteLocationOnHUD_( tmp );
dragStart.set( tmp.getX() + widgetX, tmp.getY() + widgetY );
Tuple2f.toPool( tmp );
tmpDragStartWidget.set( getLeft(), getTop() );
dragStartWidget = tmpDragStartWidget;
bindToGlobalMouseMovement();
for ( int i = 0; i < locationListeners.size(); i++ )
locationListeners.get( i ).onWidgetDragStarted( this );
}
/**
* This event is fired, when a mouse button is pressed on a focused Widget.
*
* @param button the button that was pressed
* @param x the current mouse x position
* @param y the current mouse y position
* @param when
* @param lastWhen
* @param isTopMost is this Widget topMost
* @param hasFocus is this Widget focused
*
* @see net.jtank.input.MouseCode
*/
protected void onMouseButtonPressed( MouseButton button, float x, float y, long when, long lastWhen, boolean isTopMost, boolean hasFocus )
{
for ( int i = 0; i < mouseListeners.size(); i++ )
mouseListeners.get( i ).onMouseButtonPressed( this, button, x, y, when, lastWhen, isTopMost, hasFocus );
if ( isDraggable() && isTopMost )
{
Tuple2i tmp = Tuple2i.fromPool();
getLocationHUD2Pixels_( x, y, tmp );
int xpx = tmp.getX();
int ypx = tmp.getY();
Tuple2i.toPool( tmp );
startDragging( xpx, ypx, x, y );
}
}
/**
* This method is called to notify all atteched {@link WidgetLocationListener}s about this event.
*/
protected void notifyOnDragStopped()
{
for ( int i = 0; i < locationListeners.size(); i++ )
locationListeners.get( i ).onWidgetDragStopped( this );
}
/**
* This event is fired, when a mouse button is released on a focused Widget.
*
* @param button the button that was released
* @param x the current mouse x position
* @param y the current mouse y position
* @param when
* @param lastWhen
* @param isTopMost is this Widget topMost
* @param hasFocus is this Widget focused
*
* @see net.jtank.input.MouseCode
*/
protected void onMouseButtonReleased( MouseButton button, float x, float y, long when, long lastWhen, boolean isTopMost, boolean hasFocus )
{
for ( int i = 0; i < mouseListeners.size(); i++ )
mouseListeners.get( i ).onMouseButtonReleased( this, button, x, y, when, lastWhen, isTopMost, hasFocus );
final boolean wasDragging = ( dragStart != null );
dragStart = null;
dragStartWidget = null;
if ( wasDragging )
{
notifyOnDragStopped();
}
}
/**
* This event is fired, when the mouse is moved on a Widget.
*
* @param x the new X coordinate
* @param y the new Y coordinate
* @param buttonsState
* @param when
* @param isTopMost is this Widget topMost
* @param hasFocus is this Widget focused
*/
protected void onMouseMoved( float x, float y, int buttonsState, long when, boolean isTopMost, boolean hasFocus )
{
for ( int i = 0; i < mouseListeners.size(); i++ )
mouseListeners.get( i ).onMouseMoved( this, x, y, buttonsState, when, isTopMost, hasFocus );
if ( dragStart != null )
{
Tuple2f tmp = Tuple2f.fromPool();
getAbsoluteLocationOnHUD_( tmp );
float absX = tmp.getX() + x;
float absY = tmp.getY() + y;
Tuple2f.toPool( tmp );
float widgetPosX = dragStartWidget.getX() + ( absX - dragStart.getX() );
float widgetPosY = dragStartWidget.getY() + ( absY - dragStart.getY() );
float parentResX = 0f;
float parentResY = 0f;
if ( getContainer() != null )
{
parentResX = getContainer().getResX();
parentResY = getContainer().getResY();
}
else if ( getHUD() != null )
{
parentResX = getHUD().getResX();
parentResY = getHUD().getResY();
}
// it must not be possible to drag the Window out of the viewport
widgetPosX = Math.max( widgetPosX, 0f - this.getWidth() );
widgetPosX = Math.min( widgetPosX, parentResX );
widgetPosY = Math.max( widgetPosY, 0f );
widgetPosY = Math.min( widgetPosY, parentResY );
this.setLocation( widgetPosX, widgetPosY );
}
}
/**
* This event is fired, when the mouse position has not been changed on this
* Widget for a certain amount of time.
*
* @param x the new X coordinate
* @param y the new Y coordinate
* @param when
* @param isTopMost is this Widget topMost
* @param hasFocus is this Widget focused
*/
protected void onMouseStopped( float x, float y, long when, boolean isTopMost, boolean hasFocus )
{
for ( int i = 0; i < mouseListeners.size(); i++ )
mouseListeners.get( i ).onMouseStopped( this, x, y, when, isTopMost, hasFocus );
}
/**
* This event is fired, when the mouse wheel is moved on a Widget.
*
* @param delta a positive value when the wheel was moved up
* @param isPageMove
* @param x the current mouse x position
* @param y the current mouse y position
* @param when
* @param isTopMost is this Widget topMost
*/
protected void onMouseWheelMoved( int delta, boolean isPageMove, float x, float y, long when, boolean isTopMost )
{
for ( int i = 0; i < mouseListeners.size(); i++ )
mouseListeners.get( i ).onMouseWheelMoved( this, delta, isPageMove, x, y, when, isTopMost );
}
/**
* This event is fired, when a key is pressed on a focused Widget.
*
* @param key the key that was pressed
* @param modifierMask the mask of modifier keys
* @param when the keyevent's timestamp
*/
protected void onKeyPressed( Key key, int modifierMask, long when )
{
for ( int i = 0; i < keyboardListeners.size(); i++ )
keyboardListeners.get( i ).onKeyPressed( this, key, modifierMask, when );
if ( getWidgetAssembler() != null )
getWidgetAssembler().onKeyPressed( key, modifierMask, when );
}
/**
* This event is fired, when a key is released on a focused Widget.
*
* @param key the key that was released
* @param modifierMask the mask of modifier keys
* @param when the keyevent's timestamp
*/
protected void onKeyReleased( Key key, int modifierMask, long when )
{
for ( int i = 0; i < keyboardListeners.size(); i++ )
keyboardListeners.get( i ).onKeyReleased( this, key, modifierMask, when );
if ( getWidgetAssembler() != null )
getWidgetAssembler().onKeyReleased( key, modifierMask, when );
}
/**
* This event is fired when a key is typed on the keyboard.
*
* @param ch the typed key's character
* @param modifierMask the mask of modifier keys
* @param when the keyevent's timestamp
*/
protected void onKeyTyped( char ch, int modifierMask, long when )
{
for ( int i = 0; i < keyboardListeners.size(); i++ )
keyboardListeners.get( i ).onKeyTyped( this, ch, modifierMask, when );
if ( getWidgetAssembler() != null )
getWidgetAssembler().onKeyTyped( ch, modifierMask, when );
}
/**
* This event is fired when a ControllerButton has been pressed
* and this Widget is the currently focussed one.
*
* @param button the pressed button
* @param when the gameTime of the event
*/
protected void onControllerButtonPressed( ControllerButton button, long when )
{
for ( int i = 0; i < controllerListeners.size(); i++ )
controllerListeners.get( i ).onControllerButtonPressed( this, button, when );
if ( getWidgetAssembler() != null )
getWidgetAssembler().onControllerButtonPressed( button, when );
}
/**
* This event is fired when a ControllerButton has been released
* and this Widget is the currently focussed one.
*
* @param button the released button
* @param when the gameTime of the event
*/
protected void onControllerButtonReleased( ControllerButton button, long when )
{
for ( int i = 0; i < controllerListeners.size(); i++ )
controllerListeners.get( i ).onControllerButtonReleased( this, button, when );
if ( getWidgetAssembler() != null )
getWidgetAssembler().onControllerButtonReleased( button, when );
}
/**
* This event is fired when a ControllerAxis has changed
* and this Widget is the currently focussed one.
*
* @param axis the changed axis
* @param axisDelta
* @param when the gameTime of the event
*/
protected void onControllerAxisChanged( ControllerAxis axis, int axisDelta, long when )
{
for ( int i = 0; i < controllerListeners.size(); i++ )
controllerListeners.get( i ).onControllerAxisChanged( this, axis, axisDelta, when );
if ( getWidgetAssembler() != null )
getWidgetAssembler().onControllerAxisChanged( axis, axisDelta, when );
}
/**
* This event is fired when the state of any DeviceComponent has changed.
*
* @param comp
* @param delta
* @param state
* @param when the gameTime of the event
* @param isTopMost
* @param hasFocus
*/
protected void onInputStateChanged( DeviceComponent comp, int delta, int state, long when, boolean isTopMost, boolean hasFocus )
{
// If comp is null, we assume a mouse moved event.
if ( ( comp == null ) || ( comp.getType().isMouseComponent() ) )
{
for ( int i = 0; i < mouseListeners.size(); i++ )
mouseListeners.get( i ).onInputStateChanged( this, comp, delta, state, when, isTopMost, hasFocus );
}
else if ( comp.getType().isKeyboardComponent() )
{
for ( int i = 0; i < keyboardListeners.size(); i++ )
keyboardListeners.get( i ).onInputStateChanged( this, comp, delta, state, when, isTopMost, hasFocus );
}
else if ( comp.getType().isControllerComponent() )
{
for ( int i = 0; i < controllerListeners.size(); i++ )
controllerListeners.get( i ).onInputStateChanged( this, comp, delta, state, when, isTopMost, hasFocus );
}
if ( getWidgetAssembler() != null )
getWidgetAssembler().onInputStateChanged( comp, delta, state, when, isTopMost, hasFocus );
}
protected final boolean isHovered()
{
return ( hovered );
}
/**
* This method is triggered when the visibility state has eeffectively changed.
*
* @param visible
*/
protected void onVisibilityChanged( boolean visible )
{
}
/**
* Sets wheather this Widget is visible or not
*
* @param visible visible?
*/
public final void setVisible( boolean visible )
{
if ( visible == isVisible() )
return;
this.isVisible = visible;
if ( getSGNode() != null )
this.getSGNode().setRenderable( visible );
onVisibilityChanged( visible );
if ( !isHeavyWeight() )
setTextureDirty();
for ( int i = 0; i < visibilityListeners.size(); i++ )
{
visibilityListeners.get( i ).onWidgetVisibilityChanged( this, visible );
}
}
/**
* @return wheather this Widget is visible or not
*/
public final boolean isVisible()
{
return ( isVisible );
}
/**
* Sets the whole Widget's transparency.
*
* @param transparency
* @param childrenToo
*/
protected void setTransparency( float transparency, boolean childrenToo )
{
if ( shape != null )
ShapeUtils.setShapesTransparency( shape, transparency, false, false );
if ( getWidgetAssembler() != null )
getWidgetAssembler().setTransparency( transparency, childrenToo );
this.transparency = transparency;
}
/**
* Sets the whole Widget's transparency.
*
* @param transparency
*/
public final void setTransparency( float transparency )
{
setTransparency( transparency, true );
}
/**
* Gets the whole Widget's transparency.
*
* @return the transparency.
*/
public final float getTransparency()
{
return ( transparency );
}
/**
* Sets wheather this Widget is clickable.
* If it is not clickable, clicks are sent to
* the next Widget under this one, if any.
*/
public void setClickable( boolean isClickable )
{
this.isClickable = isClickable;
}
/**
* @return wheather this Widget is clickable.
* If it is not clickable, clicks are sent to
* the next Widget under this one, if any.
*/
public final boolean isClickable()
{
return ( isClickable );
}
/**
* Sets wheather this Widget is clickable.
* If it is not clickable, clicks are sent to
* the next Widget under this one, if any.
*/
public void setDraggable( boolean draggable )
{
this.isDraggable = draggable;
}
/**
* @return wheather this Widget is draggable.
*/
public final boolean isDraggable()
{
return ( isDraggable );
}
/**
* Sets wheather this Widget is pickable.
* If it is not pickable, the pick() method
* will always return <i>null</i>.
*/
public void setPickable( boolean isPickable )
{
this.isPickable = isPickable;
}
/**
* @return wheather this Widget is pickable.
* If it is not pickable, the pick() method
* will always return <i>null</i>.
*/
public final boolean isPickable()
{
return ( isPickable );
}
/**
* Sets the Cursor type to be used when the cursor is over this Widget
* and which is inherited to the Children, if this is a container.
*
* @param cursor
*/
public final void setCursor( Cursor.Type cursor )
{
this.cursorType = cursor;
}
/**
* @return the Cursor type to be used when the cursor is over this Widget
* and which is inherited to the Children, if this is a container.
*/
public final Cursor.Type getCursor()
{
return ( cursorType );
}
/**
* @return the "computed" Cursor type to be used when the cursor is over
* this Widget.
*/
final Cursor.Type getInheritedCursor()
{
if ( this.cursorType != null )
return ( this.cursorType );
if ( getContainer() != null )
return ( getContainer().getInheritedCursor() );
if ( getHUD() != null )
//return ( getHUD().getInheritedCursor() );
return ( Cursor.Type.POINTER1 );
return ( null );
}
/**
* Sets the Border to use for this BorderSettable Widget.
*
* @param border the new Border (<i>null</i> for no border)
*/
public void setBorder( Border border )
{
if ( this.border == border )
return;
this.border = border;
updateSizeFactors();
setTextureDirty();
}
/**
* Creates a new Border from the given Border.Desctiption and invokes
* setBorder(Border).
*
* @see #setBorder(Border)
*
* @param borderDesc the Border.Description to create the new Border from (<i>null</i> for no border)
*/
public final void setBorder( Border.Description borderDesc )
{
if ( borderDesc == null )
setBorder( (Border)null );
else
setBorder( BorderFactory.createBorder( borderDesc ) );
}
/**
* @return the Border used for this BorderSettable Widget
*/
public final Border getBorder()
{
return ( border );
}
/**
* Gets the left coordinate of where content (0, 0) is (in pixels).
*
* @return the content left.
*/
protected int getContentLeftPX()
{
int result = 0;
if ( this instanceof PaddingSettable )
{
result += ( (PaddingSettable)this ).getPaddingLeft();
}
if ( getBorder() != null )
{
result += getBorder().getLeftWidth();
}
if ( getWidgetAssembler() != null )
{
result += getWidgetAssembler().getAdditionalContentLeft();
}
return ( result );
}
/**
* Gets the top coordinate of where content (0, 0) is (in pixels).
*
* @return the content left.
*/
protected int getContentTopPX()
{
int result = 0;
if ( this instanceof PaddingSettable )
{
result += ( (PaddingSettable)this ).getPaddingTop();
}
if ( getBorder() != null )
{
result += getBorder().getTopHeight();
}
if ( getWidgetAssembler() != null )
{
result += getWidgetAssembler().getAdditionalContentTop();
}
return ( result );
}
protected final <Dim2f_ extends Dim2f> Dim2f_ getContentOffset( Dim2f_ buffer )
{
return ( getSizePixels2HUD_( getContentLeftPX(), getContentTopPX(), buffer ) );
}
/**
* Gets the width of the widget's content area (minus border and padding) (in pixels).
*
* @return the content width.
*/
protected int getContentWidthPX()
{
if ( getHUD() == null )
throw new Error( "This widget is not attached to the HUD." );
return ( contentWidthPX );
}
/**
* Gets the height of the widget's content area (minus border and padding) (in pixels).
*
* @return the content width.
*/
protected int getContentHeightPX()
{
if ( getHUD() == null )
throw new Error( "This widget is not attached to the HUD." );
return ( contentHeightPX );
}
/**
* @return the width without the Border (if any) and padding
*/
public final float getContentWidth()
{
/*
int contentWidthPX = getContentWidthPX();
if ( contentWidthPX == -1 )
throw new Error();
return ( transformWidth_Pixels2HUD * contentWidthPX );
*/
int nonContentWidthPX = 0;
if ( getBorder() != null )
{
nonContentWidthPX += getBorder().getLeftWidth() + getBorder().getRightWidth();
}
if ( this instanceof PaddingSettable )
{
PaddingSettable ps = (PaddingSettable)this;
nonContentWidthPX += ps.getPaddingLeft() + ps.getPaddingRight();
}
if ( getWidgetAssembler() != null )
{
nonContentWidthPX -= getWidgetAssembler().getAdditionalContentWidth();
}
Dim2f buffer = Dim2f.fromPool();
getSizePixels2HUD_( nonContentWidthPX, 0, buffer );
float contentWidth = getWidth() - buffer.getWidth();
Dim2f.toPool( buffer );
return ( contentWidth );
}
/**
* @return the height without the Border (if any) and padding
*/
public final float getContentHeight()
{
/*
int contentHeightPX = getContentHeightPX();
if ( contentHeightPX == -1 )
throw new Error();
return ( transformHeight_Pixels2HUD * contentHeightPX );
*/
int nonContentHeightPX = 0;
if ( getBorder() != null )
{
nonContentHeightPX += getBorder().getTopHeight() + getBorder().getBottomHeight();
}
if ( this instanceof PaddingSettable )
{
PaddingSettable ps = (PaddingSettable)this;
nonContentHeightPX += ps.getPaddingTop() + ps.getPaddingBottom();
}
if ( getWidgetAssembler() != null )
{
nonContentHeightPX -= getWidgetAssembler().getAdditionalContentHeight();
}
Dim2f buffer = Dim2f.fromPool();
getSizePixels2HUD_( 0, nonContentHeightPX, buffer );
float contentHeight = getHeight() - buffer.getHeight();
Dim2f.toPool( buffer );
return ( contentHeight );
}
/**
* @return the aspect ratio of the inner size
*/
public final float getContentAspect()
{
return ( getContentWidth() / getContentHeight() );
}
protected final int getLevel()
{
if ( getContainer() == null )
return ( 0 );
return ( getContainer().getLevel() + 1 );
}
/**
* Updates the Widget's internals.<br>
* Called by the Widget system and can be called from outside.
*/
public void update()
{
this.setLocation( getLeft(), getTop(), true, true );
this.setSize( getWidth(), getHeight(), true );
if ( getWidgetAssembler() != null )
getWidgetAssembler().update();
}
private static final class ZIndexComparable implements Comparable<Object>
{
private int[] zIndices = new int[ 1 ];
public final void update( Widget widget )
{
int level = widget.getLevel();
if ( zIndices.length != level + 1 )
{
zIndices = new int[ level + 1 ];
}
while ( widget != null )
{
zIndices[ level-- ] = widget.getZIndex();
widget = widget.getContainer();
}
}
public int compareTo( Object o2 )
{
if ( !( o2 instanceof ZIndexComparable ) )
return ( +1 );
ZIndexComparable that = (ZIndexComparable)o2;
int n = Math.min( this.zIndices.length, that.zIndices.length );
for ( int i = 0; i < n; i++ )
{
if ( this.zIndices[i] < that.zIndices[i] )
return ( -1 );
if ( this.zIndices[i] > that.zIndices[i] )
return ( +1 );
}
if ( this.zIndices.length < that.zIndices.length )
return ( -1 );
if ( this.zIndices.length > that.zIndices.length )
return ( +1 );
return ( 0 );
}
}
private final ZIndexComparable zIndexComparable = new ZIndexComparable();
protected void updateAbsZIndex()
{
//if ( shape != null )
{
zIndexComparable.update( this );
}
}
/**
* @param contentWidth
*/
protected float calculateTransformWidth_Pixels2HUD( float contentWidth )
{
return ( 1f );
}
/**
* @param contentHeight
*/
protected float calculateTransformHeight_Pixels2HUD( float contentHeight )
{
return ( 1f );
}
protected void updateSizeFactors()
{
if ( getHUD() == null )
return;
int nonContentWidthPX = 0;
int nonContentHeightPX = 0;
if ( getBorder() != null )
{
nonContentWidthPX += getBorder().getLeftWidth() + getBorder().getRightWidth();
nonContentHeightPX += getBorder().getTopHeight() + getBorder().getBottomHeight();
}
if ( this instanceof PaddingSettable )
{
PaddingSettable ps = (PaddingSettable)this;
nonContentWidthPX += ps.getPaddingLeft() + ps.getPaddingRight();
nonContentHeightPX += ps.getPaddingTop() + ps.getPaddingBottom();
}
if ( getWidgetAssembler() != null )
{
nonContentWidthPX -= getWidgetAssembler().getAdditionalContentWidth();
nonContentHeightPX -= getWidgetAssembler().getAdditionalContentHeight();
}
Dim2f buffer = Dim2f.fromPool();
getSizePixels2HUD_( nonContentWidthPX, nonContentHeightPX, buffer );
float contentWidth = getWidth() - buffer.getWidth();
float contentHeight = getHeight() - buffer.getHeight();
Dim2f.toPool( buffer );
Dim2i buffer2 = Dim2i.fromPool();
getSizeHUD2Pixels_( contentWidth, contentHeight, buffer2 );
contentWidthPX = buffer2.getWidth();
contentHeightPX = buffer2.getHeight();
//if ( this instanceof TextField )
//Thread.dumpStack();
//System.out.println( contentWidthPX + ", " + contentHeightPX + ", " + contentWidth + ", " + contentHeight );
getSizeHUD2Pixels_( getWidth(), getHeight(), buffer2 );
widthPX = buffer2.getWidth();
heightPX = buffer2.getHeight();
Dim2i.toPool( buffer2 );
transformWidth_Pixels2HUD = calculateTransformWidth_Pixels2HUD( contentWidth );
transformHeight_Pixels2HUD = calculateTransformHeight_Pixels2HUD( contentHeight );
}
/**
* Effectively changes the translation of this Widget (location and z-index)
*/
protected void updateTranslation()
{
if ( getHUD() == null )
return;
updateAbsZIndex();
if ( transformGroup == null )
return;
final Transform3D t3d = transformGroup.getTransform();
final Tuple2f loc2 = Tuple2f.fromPool();
getLocationHUD2SG_( getLeft(), getTop(), loc2 );
t3d.setTranslation( loc2.getX(), loc2.getY(), 0f );
Tuple2f.toPool( loc2 );
transformGroup.setTransform( t3d );
}
/**
*
* @param oldLeft
* @param oldTop
* @param newLeft
* @param newTop
*/
protected void onLocationChanged( float oldLeft, float oldTop, float newLeft, float newTop )
{
}
protected final boolean setLocation( float locX, float locY, boolean forced, boolean needsTextureRefresh )
{
final float oldLeft = getLeft();
final float oldTop = getTop();
final boolean result = ( ( oldLeft != locX ) || ( oldTop != locY ) );
if ( !result && !forced )
{
return ( false );
}
if ( result || forced )
{
this.location.set( locX, locY );
if ( getHUD() != null )
{
updateTranslation();
}
if ( getWidgetAssembler() != null )
getWidgetAssembler().onOwnerMoved( locX - oldLeft, locY - oldTop, needsTextureRefresh );
}
if ( result )
{
if ( !isHeavyWeight() && needsTextureRefresh )
{
setTextureDirty();
Widget rootHost = getRootHostWidget();
if ( rootHost != null )
rootHost.setWidgetDirty();
}
onLocationChanged( oldLeft, oldTop, locX, locY );
for ( int i = 0; i < locationListeners.size(); i++ )
{
locationListeners.get( i ).onWidgetLocationChanged( this, oldLeft, oldTop, locX, locY );
}
if ( getContainer() != null )
{
getContainer().onChildMovedOrResized( this );
}
}
return ( result );
}
/**
* Sets this Widget's location relative to the upper-left corner of it's WidgetContainer
*
* @param locX the new x-location
* @param locY the new y-location
*
* @return true, if the location actually has changed
*/
public final Widget setLocation( float locX, float locY )
{
setLocation( locX, locY, false, true );
return ( this );
}
/**
* Sets this Widget's location relative to the upper-left corner of it's WidgetContainer
*
* @param loc the new location
*
* @return true, if the location actually has changed
*/
public final Widget setLocation( Tuple2f loc )
{
return ( setLocation( loc.getX(), loc.getY() ) );
}
/**
* @return this Widget's location relative to the upper-left corner of it's WidgetContainer
*/
public final Tuple2f getLocation()
{
return ( location.getReadOnly() );
}
/**
* @return this Widget's location relative to the upper-left corner of it's WidgetContainer
*/
public <Tuple2f_ extends Tuple2f> Tuple2f_ getLocation( Tuple2f_ loc )
{
location.get( loc );
return ( loc );
}
/**
* @return this Widget's left location relative to the left borderline of it's WidgetContainer
*/
public final float getLeft()
{
return ( location.getX() );
}
/**
* @return this Widget's top location relative to the upper borderline of it's WidgetContainer
*/
public final float getTop()
{
return ( location.getY() );
}
protected float getMinWidth()
{
return ( -1f );
}
protected float getMinHeight()
{
return ( -1f );
}
/**
*
* @param oldWidth
* @param oldHeight
* @param newWidth
* @param newHeight
*/
protected void onSizeChanged( float oldWidth, float oldHeight, float newWidth, float newHeight )
{
updateSizeFactors();
}
protected final boolean setSize( float width, float height, boolean forced )
{
final float oldWidth = getWidth();
final float oldHeight = getHeight();
width = Math.max( getMinWidth(), width );
height = Math.max( getMinHeight(), height );
final boolean result = ( ( oldWidth != width ) || ( oldHeight != height ) );
if ( !result && !forced )
{
return ( false );
}
if ( result || forced )
{
this.size.set( width, height );
if ( ( shape != null ) && ( getHUD() != null ) )
{
float shapeWidth = width;
float shapeHeight = height;
Dim2f s = Dim2f.fromPool();
DropShadowFactory dsf = getHUD().getDropShadowFactory();
if ( ( dsf != null ) && hasDropShadow() )
{
getSizePixels2HUD_( dsf.getDropShadowWidth(), dsf.getDropShadowHeight(), s );
shapeWidth += s.getWidth();
shapeHeight += s.getHeight();
}
getSizeHUD2SG_( shapeWidth, shapeHeight, s );
shape.resize( s.getWidth(), s.getHeight() );
Dim2f.toPool( s );
}
//}
//if ( result )
//{
setTextureDirty();
if ( getContainer() != null )
{
getContainer().setTextureDirty();
}
onSizeChanged( oldWidth, oldHeight, width, height );
for ( int i = 0; i < sizeListeners.size(); i++ )
{
sizeListeners.get( i ).onWidgetSizeChanged( this, oldWidth, oldHeight, width, height );
}
if ( getContainer() != null )
{
getContainer().onChildMovedOrResized( this );
}
}
return ( result );
}
/**
* Resizes this Widget to the given width and height.
*
* @param width the new width of this Widget
* @param height the new height of this Widget
*
* @return true, if the size actually has changed
*/
public final Widget setSize( float width, float height )
{
setSize( width, height, false );
return ( this );
}
/**
* Resizes this Widget to the given width and height.
*
* @param size the new size of this Widget
*
* @return true, if the size actually has changed
*/
public final Widget setSize( Sized2fRO size )
{
return ( setSize( size.getWidth(), size.getHeight() ) );
}
/**
* Resizes this Widget to the given width and height.
*
* @param size the new size of this Widget
*
* @return true, if the size actually has changed
*/
public final Widget setSize( Tuple2f size )
{
return ( setSize( size.getX(), size.getY() ) );
}
/**
* {@inheritDoc}
*/
public final void setWidth( float width )
{
setSize( width, getHeight() );
}
/**
* {@inheritDoc}
*/
public final void setHeight( float height )
{
setSize( getWidth(), height );
}
/**
* @return this Widget's width
*/
public final float getWidth()
{
return ( size.getWidth() );
}
/**
* Gets the width of the widget's area (in pixels).
*
* @return the width.
*/
protected final int getWidthPX()
{
if ( getHUD() == null )
throw new Error( "This widget is not attached to the HUD." );
return ( widthPX );
}
/**
* @return this Widget's height
*/
public final float getHeight()
{
return ( size.getHeight() );
}
/**
* Gets the height of the widget's area (in pixels).
*
* @return the width.
*/
protected final int getHeightPX()
{
if ( getHUD() == null )
throw new Error( "This widget is not attached to the HUD." );
return ( heightPX );
}
/**
* @return the size of this Widget (in container meatures)
*/
public final Sized2fRO getSize()
{
return ( new Dim2f( size ) );
}
/**
* @return the aspect ratio (width / height) of this Widget
*/
public final float getAspect()
{
if ( size.getHeight() == 0f )
return ( 0f );
return ( size.getWidth() / size.getHeight() );
}
/**
* Sets the z-index of this Widget.
* Larger values bring are nearer.
*/
public void setZIndex( int zIndex )
{
if ( zIndex == this.zIndex )
return;
this.zIndex = zIndex;
if ( getContainer() != null )
{
getContainer().setZIndexSortingDirty();
if ( !isHeavyWeight() )
getContainer().setTextureDirty();
}
updateTranslation();
}
/**
* @return the z-index of this Widget.
* Larger values bring are nearer.
*/
public final int getZIndex()
{
return ( zIndex );
}
public final int compareAbsZIndex( Widget widget2 )
{
return ( this.zIndexComparable.compareTo( widget2.zIndexComparable ) );
}
/**
* @return the width on which to pick. By default this is exactly getWidth().
*/
protected float getPickWidth()
{
return ( getWidth() );
}
/**
* @return the height on which to pick. By default this is exactly getHeight().
*/
protected float getPickHeight()
{
return ( getHeight() );
}
protected boolean pickConditionsMatch( HUDPickReason pickReason )
{
if ( getHUD() == null )
return ( false );
if ( !isPickable() || !isVisible() )
return ( false );
if ( ( ( pickReason == HUDPickReason.BUTTON_PRESSED ) || ( pickReason == HUDPickReason.BUTTON_RELEASED ) ) && !isClickable() )
return ( false );
return ( true );
}
/**
* Dispatches the picking to the WidgetAssembler.
*
* @return the pickresult of the WidgetAssembler or null
*/
private boolean pickWidgetAssembler( int canvasX, int canvasY, float widgetX, float widgetY, HUDPickReason pickReason, MouseButton button, long when, long meta, int flags )
{
if ( ( getWidgetAssembler() == null ) || !getWidgetAssembler().isPickingDispatched() )
return ( false );
return ( getWidgetAssembler().pick( canvasX, canvasY, widgetX, widgetY, pickReason, button, when, meta, flags ) );
}
/**
* Tests whether a Widget is under the cursor and runs the approriate methods if true.
*
* @param canvasX the x position of the mouse on the Canvas3D
* @param canvasY the y position of the mouse on the Canvas3D
* @param pickReason the action which caused this pick operation
* @param button the mouse-button, that caused the picking
* @param when the timestamp of the picking
* @param meta this could be either the lastPressTime, lastReleaseTime, buttonsState mask or the page-move-boolean. (depends on the pickReason)
* @param flags
*
* @return an instance of HUDPickResult holding the picked Widget and absolute and relative picking positions or null.
*/
protected HUDPickResult pick( int canvasX, int canvasY, HUDPickReason pickReason, MouseButton button, long when, long meta, int flags )
{
if ( !pickConditionsMatch( pickReason ) )
return ( null );
final Tuple2f locP = Tuple2f.fromPool();
getLocationPixels2HUD_( canvasX, canvasY, locP );
float pickXHUD = locP.getX();
float pickYHUD = locP.getY();
Tuple2f.toPool( locP );
if ( ( 0f > pickXHUD ) || ( pickXHUD > getPickWidth() ) || ( 0f > pickYHUD ) || ( pickYHUD > getPickHeight() ) )
return ( null );
HUDPickResult hpr = HUDPickResult.fromPool();
hpr.set( this, this.getCursor(), pickXHUD + getLeft(), pickYHUD + getTop(), pickXHUD, pickYHUD, pickReason, button );
if ( ( flags & HUDPickResult.HUD_PICK_FLAG_IS_INTERNAL ) != 0 ) // TODO: Check, if this is the correct condition!
{
pickWidgetAssembler( canvasX, canvasY, pickXHUD, pickYHUD, pickReason, button, when, meta, flags );
}
return ( hpr );
}
/**
* Is the init method currently being executed?
*/
protected final boolean isInitializing()
{
return ( isInitializing );
}
/**
* Has the init method been executed once?
*/
protected final boolean isInitialized()
{
return ( isInitialized );
}
protected void initSize()
{
}
protected void createShape()
{
if ( !isHeavyWeight )
return;
final Dim2f size2 = Dim2f.fromPool();
float shapeWidth = getWidth();
float shapeHeight = getHeight();
DropShadowFactory dsf = getHUD().getDropShadowFactory();
if ( ( dsf != null ) && hasDropShadow() )
{
getSizePixels2HUD_( dsf.getDropShadowWidth(), dsf.getDropShadowHeight(), size2 );
shapeWidth += size2.getWidth();
shapeHeight += size2.getHeight();
}
getSizeHUD2SG_( shapeWidth, shapeHeight, size2 );
Node.pushGlobalIgnoreBounds( true );
this.shape = new DrawRectangle( size2.getWidth(), size2.getHeight(), false, true, true );
Node.popGlobalIgnoreBounds();
shape.getAppearance().setRenderingAttributes( RENDERING_ATTRIBUTES );
shape.setCustomComparable( zIndexComparable );
shape.getGeometry().setOptimization( Optimization.USE_VBOS );
transformGroup.addChild( shape );
Dim2f.toPool( size2 );
shape.getTextureCanvas().addDrawCallback( drawCallback );
}
/**
* This method is called when the WidgetContainer is set.
*/
protected abstract void init();
boolean checkHiearchyIntegrity()
{
if ( hierarchyOK == Boolean.TRUE )
return ( true );
if ( isHeavyWeight() )
{
hierarchyOK = Boolean.TRUE;
return ( true );
}
if ( getContainer() == null )
{
hierarchyOK = ( getAssembly() != null );
}
else
{
hierarchyOK = getContainer().checkHiearchyIntegrity();
}
if ( !hierarchyOK )
throw new Error( "The hiearchy of a Widget is not ok. Any lightweight Widget must be in a hierarchy, where a (grand-)parent Widget is heavyweight. Check the HUD's ContentPane for being heavyweight." );
return ( true );
}
/**
* This event is fired, when this Widget is added to the HUD live Widget hierarchy.
*
* @param hud the HUD, the Widget is added to
*/
protected void onAttachedToHUD( HUD hud )
{
checkHiearchyIntegrity();
updateSizeFactors();
updateTranslation();
if ( !isInitialized() )
{
isInitializing = true;
initSize();
createShape();
init();
isInitializing = false;
if ( getWidgetAssembler() != null )
{
getWidgetAssembler().update();
}
isInitialized = true;
}
setSize( getWidth(), getHeight(), true );
//if ( getWidgetAssembler() != null )
// getWidgetAssembler().updateLocations( getLeft(), getTop() );
if ( focusRequested )
{
container.focus( this );
focusRequested = false;
}
for ( int i = 0; i < containerListeners.size(); i++ )
containerListeners.get( i ).onWidgetAttachedToHUD( this, hud );
}
/**
* This event is fired, when this Widget is removed from the HUD live Widget hierarchy.
*
* @param hud the HUD, the Widget is removed from
*/
protected void onDetachedFromHUD( HUD hud )
{
hierarchyOK = null;
for ( int i = 0; i < containerListeners.size(); i++ )
containerListeners.get( i ).onWidgetDetachedFromHUD( this, hud );
}
void setHUD( HUD hud )
{
if ( this.hud == hud )
return;
HUD oldHUD = this.hud;
this.hud = hud;
if ( getWidgetAssembler() != null )
getWidgetAssembler().setHUD( hud );
if ( hud != null )
onAttachedToHUD( hud );
else
onDetachedFromHUD( oldHUD );
}
/**
* @return the HUD this Widget belongs to
*/
public final HUD getHUD()
{
return ( hud );
}
/**
* This event is fired, when this Widget is added to a WidgetContainer.
*
* @param container the WidgetContainer, the Widget is added to
*/
protected void onAttachedToContainer( WidgetContainer container )
{
if ( ( container.getSGGroup() != null ) && ( this.getSGNode() != null ) )
{
container.getSGGroup().addChild( this.getSGNode() );
}
container.notifyContainerListenersAboutAttachedWidget( this, container );
this.notifyContainerListenersAboutAttachedWidget( this, container );
}
/**
* This event is fired, when this Widget is removed from a WidgetContainer.
*
* @param container the WidgetContainer, the Widget is removed from
*/
protected void onDetachedFromContainer( WidgetContainer container )
{
if ( ( container.getSGGroup() != null ) && ( this.getSGNode() != null ) )
{
container.getSGGroup().removeChild( this.getSGNode() );
}
container.notifyContainerListenersAboutDetachedWidget( this, container );
this.notifyContainerListenersAboutDetachedWidget( this, container );
}
/**
* Sets this Widget's container.
*
* @param container the new Container
* @param assembly
*/
final void setContainer( WidgetContainer container, Widget assembly )
{
boolean containerChanged = ( this.container != container );
boolean assemblyChanged = ( this.assembly != assembly );
if ( !containerChanged && !assemblyChanged )
return;
WidgetContainer oldContainer = this.container;
this.container = container;
this.assembly = assembly;
if ( containerChanged )
{
if ( getWidgetAssembler() != null )
getWidgetAssembler().setContainer( container );
if ( container != null )
onAttachedToContainer( container );
else
onDetachedFromContainer( oldContainer );
}
}
/**
* The Container which contains this Widget
*/
public final WidgetContainer getContainer()
{
return ( container );
}
/**
* @return the Widget, which uses this one to assemle itself, if any.
*/
protected final Widget getAssembly()
{
return ( assembly );
}
/**
* Removes the Widget from its Container.
*/
public void detach()
{
if ( getAssembly() != null )
{
getAssembly().getWidgetAssembler().removeWidget( this );
}
else if ( getContainer() != null )
{
getContainer().removeWidget( this );
}
}
/**
* {@inheritDoc}
*/
@Override
public String toString()
{
if ( this instanceof TextWidget )
{
String text = String.valueOf( ( (TextWidget)this ).getText() ).replaceAll( "\n", "\\\\n" );
if ( ( getName() != null ) && ( getName().length() > 0 ) )
return ( this.getClass().getSimpleName() + "( \"" + getName() + "\", " + text + "\" )" );
return ( this.getClass().getSimpleName() + "( \"" + text + "\" )" );
}
if ( ( getName() != null ) && ( getName().length() > 0 ) )
{
return ( this.getClass().getSimpleName() + "( \"" + getName() + "\" )" );
}
return ( super.toString() );
}
/**
* Returns the untilized {@link Shape3D} to display the {@link Widget}.<br>
* For most widget types, this will be a {@link DrawRectangle}.<br>
* If this Widget is lightweight, this method will return null.
*
* @return the untilized {@link Shape3D} to display the {@link Widget}.
*/
public Shape3D getShape()
{
return ( shape );
}
protected void setHostWidget( Widget widget )
{
this.hostWidget = widget;
}
/**
* Returns the Widget, that this lightweight Widget draws on.
*
* @return the host Widget.
*/
protected final Widget getRootHostWidget()
{
if ( hostWidget == null )
{
if ( isHeavyWeight() )
return ( this );
return ( null );
}
return ( hostWidget.getRootHostWidget() );
}
final void setPassive( boolean passive )
{
this.isPassive = passive;
if ( getWidgetAssembler() != null )
{
getWidgetAssembler().setWidgetsPassive( passive );
}
}
protected void setHostedWidgetDirty()
{
isAHostedWidgetDirty = true;
}
protected void setWidgetDirty()
{
isThisWidgetDirty = true;
if ( getWidgetAssembler() != null )
{
getWidgetAssembler().setWidgetsDirty();
}
}
protected void resetWidgetDirty()
{
this.isThisWidgetDirty = false;
this.isAHostedWidgetDirty = false;
}
protected final boolean isThisWidgetDirty()
{
return ( isThisWidgetDirty );
}
protected final boolean isAHostedWidgetDirty()
{
return ( isAHostedWidgetDirty );
}
/**
*
* @param flags
*/
protected void setHostTextureDirty( int flags )
{
if ( isPassive )
return;
setHostedWidgetDirty();
if ( isHeavyWeight() )
{
this.drawCallback.setDirty( true );
return;
}
Widget hostWidget = this.hostWidget;
if ( hostWidget != null )
{
hostWidget.setHostTextureDirty( flags );
return;
}
/*
if ( getHUD() != null )
{
throw new Error( "A lightweight Widget must have a (grand-, ...)parent heavyweight Widget!" );
}
*/
}
protected final void setHostTextureDirty()
{
setHostTextureDirty( 0 );
}
/**
*
* @param flags
*/
protected void setTextureDirty( int flags )
{
if ( isPassive )
return;
if ( getAssembly() != null )
{
getAssembly().setTextureDirty( flags );
return;
}
setWidgetDirty();
if ( isHeavyWeight() )
{
this.drawCallback.setDirty( true );
return;
}
//Widget hostWidget = getHostWidget();
Widget hostWidget = this.hostWidget;
if ( hostWidget != null )
{
//hostWidget.setTextureDirty();
hostWidget.setHostTextureDirty( flags );
return;
}
/*
if ( getHUD() != null )
{
throw new Error( "A lightweight Widget must have a (grand-, ...)parent heavyweight Widget!" );
}
*/
}
protected final void setTextureDirty()
{
setTextureDirty( 0 );
}
/**
* Draws the Widget's background.
*
* @param texCanvas
* @param offsetX
* @param offsetY
* @param width
* @param height
* @param needsClearForNullBackground
*/
protected void drawBackground( Texture2DCanvas texCanvas, int offsetX, int offsetY, int width, int height )
{
if ( isHeavyWeight() )
{
DrawUtils.clearImage( Colorf.BLACK_TRANSPARENT, null, null, texCanvas, offsetX, offsetY, width, height );
}
else if ( ( getContainer() != null ) && !getContainer().isThisWidgetDirty() && ( getAssembly() == null ) )
{
DropShadowFactory dsf = getHUD().getDropShadowFactory();
if ( ( dsf == null ) || !hasDropShadow() )
{
getContainer().drawParentBackground( this, texCanvas, offsetX, offsetY, width, height );
}
else
{
getContainer().drawParentBackground( this, texCanvas, offsetX, offsetY, width + dsf.getDropShadowWidth(), height + dsf.getDropShadowHeight() );
}
}
}
/**
*
* @param texCanvas
* @param offsetX
* @param offsetY
* @param width
* @param height
* @param drawsSelf
*/
protected abstract void drawWidget( Texture2DCanvas texCanvas, int offsetX, int offsetY, int width, int height, boolean drawsSelf );
/**
* Draws the part of the Widget, that needs to be drawn after the WidgetAssembler.
*
* @param texCanvas
* @param offsetX
* @param offsetY
* @param width
* @param height
* @param drawsSelf
*/
protected void drawWidgetAfterWidgetAssembler( Texture2DCanvas texCanvas, int offsetX, int offsetY, int width, int height, boolean drawsSelf )
{
}
protected void drawBorder( Border border, Texture2DCanvas texCanvas, int offsetX, int offsetY, int width, int height )
{
border.drawBorder( texCanvas, offsetX, offsetY, width, height, this );
}
protected void setContentClipRect( Texture2DCanvas texCanvas, int offsetX, int offsetY, int width, int height )
{
texCanvas.setClip( offsetX, offsetY, width, height );
}
protected void drawWidgetContents( Texture2DCanvas texCanvas, int offsetX, int offsetY, int width, int height, boolean drawsSelf )
{
int contentLeft = offsetX + getContentLeftPX();
int contentTop = offsetY + getContentTopPX();
int contentWidth = getContentWidthPX();
int contentHeight = getContentHeightPX();
clip.set( contentLeft, contentTop, contentWidth, contentHeight );
clip.clamp( oldClip );
setContentClipRect( texCanvas, clip.getLeft(), clip.getTop(), clip.getWidth(), clip.getHeight() );
drawWidget( texCanvas, contentLeft, contentTop, contentWidth, contentHeight, drawsSelf );
if ( getWidgetAssembler() != null )
{
clip.set( offsetX, offsetY, width, height );
clip.clamp( oldClip );
texCanvas.setClip( clip );
getWidgetAssembler().draw( texCanvas, offsetX, offsetY );
clip.set( contentLeft, contentTop, contentWidth, contentHeight );
clip.clamp( oldClip );
setContentClipRect( texCanvas, clip.getLeft(), clip.getTop(), clip.getWidth(), clip.getHeight() );
}
drawWidgetAfterWidgetAssembler( texCanvas, contentLeft, contentTop, contentWidth, contentHeight, drawsSelf );
}
public void drawAndUpdateWidget( Texture2DCanvas texCanvas, int offsetX, int offsetY, int width, int height, boolean drawsSelf )
{
if ( isThisWidgetDirty || isPassive )
{
texCanvas.getClip( oldClip );
DropShadowFactory dsf = getHUD().getDropShadowFactory();
if ( ( dsf == null ) || !hasDropShadow() )
{
clip.set( offsetX, offsetY, width, height );
clip.clamp( oldClip );
texCanvas.setClip( clip );
texCanvas.beginUpdateRegion( offsetX, offsetY, width, height );
}
else
{
clip.set( offsetX, offsetY, width + dsf.getDropShadowWidth(), height + dsf.getDropShadowHeight() );
clip.clamp( oldClip );
texCanvas.setClip( clip );
texCanvas.beginUpdateRegion( offsetX, offsetY, width + dsf.getDropShadowWidth(), height + dsf.getDropShadowHeight() );
dsf.drawDropShadow( offsetX + width, offsetY + height, width, height, getZIndex(), texCanvas );
clip.set( offsetX, offsetY, width, height );
clip.clamp( oldClip );
texCanvas.setClip( clip );
}
drawBackground( texCanvas, offsetX, offsetY, width, height );
drawWidgetContents( texCanvas, offsetX, offsetY, width, height, drawsSelf );
if ( getBorder() != null )
{
clip.set( offsetX, offsetY, width, height );
clip.clamp( oldClip );
texCanvas.setClip( clip );
drawBorder( getBorder(), texCanvas, offsetX, offsetY, width, height );
}
texCanvas.finishUpdateRegion();
texCanvas.setClip( oldClip );
}
else if ( isAHostedWidgetDirty )
{
drawWidgetContents( texCanvas, offsetX, offsetY, width, height, drawsSelf );
}
resetWidgetDirty();
}
/**
* Sets the maximum frequency, at which a Widget can be redrawn.
*
* @param freq frequency in Hz
*/
public static final void setMaxRedrawFrequency( float freq )
{
MIN_UPDATE_DELAY.setValue( (long)( 1000000000f / freq ) );
}
/**
* Gets the maximum frequency, at which a Widget can be redrawn.
*
* @return the frequency in Hz.
*/
public static final float getMaxRedrawFrequency()
{
return ( 1000000000f / MIN_UPDATE_DELAY.floatValue() );
}
/**
* Sets the forced frequency, at which a Widget is redrawn.
*
* @param freq frequency in Hz (-1 for off)
*/
public void setForcedRedrawFrequency( float freq )
{
if ( !isHeavyWeight() )
throw new Error( "A lightweight Widget cannot be forced redrawn." );
if ( freq <= 0f )
{
this.forcedUpdateDelay = -1L;
return;
}
this.forcedUpdateDelay = (long)( 1000000000f / freq );
}
/**
* Gets the forced frequency, at which a Widget is redrawn.
*
* @return the frequency in Hz (-1 for off).
*/
public final float getForcedRedrawFrequency()
{
if ( forcedUpdateDelay <= 0L )
return ( -1f );
return ( 1000000000f / forcedUpdateDelay );
}
/**
* Creates a new Widget.
*
* @param isHeavyWeight
* @param hasWidgetAssembler
*/
protected Widget( boolean isHeavyWeight, boolean hasWidgetAssembler )
{
this.isHeavyWeight = isHeavyWeight;
//if ( isHeavyWeight )
{
Node.pushGlobalIgnoreBounds( true );
this.transformGroup = new TransformGroup();
Node.popGlobalIgnoreBounds();
}
/*
else
{
this.transformGroup = null;
}
*/
this.widgetAssembler = hasWidgetAssembler ? new WidgetAssembler( this ) : null;
//this.setVisible( true );
if ( isHeavyWeight )
{
this.drawCallback = new DrawCallback2D()
{
private boolean dirty = true;
private long nextAllowedRedrawTime = -1L;
private long nextForcedRedrawTime = -1L;
@Override
public void setDirty( boolean dirty )
{
this.dirty = dirty;
}
@Override
public boolean needsRedraw( long nanoTime )
{
//if ( true ) { isThisWidgetDirty = true; return ( true ); }
if ( dirty )
{
if ( nanoTime < nextAllowedRedrawTime )
return ( false );
nextAllowedRedrawTime += MIN_UPDATE_DELAY.longValue();
}
else if ( ( forcedUpdateDelay > 0L ) && ( nanoTime >= nextForcedRedrawTime ) )
{
nextForcedRedrawTime += forcedUpdateDelay;
setWidgetDirty();
return ( true );
}
boolean result = dirty;
dirty = false;
return ( result );
}
@Override
public void drawTexture( Texture2DCanvas texCanvas, int texWidth, int texHeight )
{
texCanvas.setClip( 0, 0, texWidth, texHeight );
DropShadowFactory dsf = getHUD().getDropShadowFactory();
if ( ( dsf != null ) && hasDropShadow() )
{
texWidth -= dsf.getDropShadowWidth();
texHeight -= dsf.getDropShadowHeight();
}
drawAndUpdateWidget( texCanvas, 0, 0, texWidth, texHeight, true );
}
};
}
else
{
this.drawCallback = null;
}
}
/**
* Creates a new Widget with the given width and height.
*
* @param isHeavyWeight
* @param hasWidgetAssembler
* @param width the new width of this Widget
* @param height the new height of this Widget
* @param zIndex the z-index of this Widget
*/
protected Widget( boolean isHeavyWeight, boolean hasWidgetAssembler, float width, float height )
{
this( isHeavyWeight, hasWidgetAssembler );
this.size.set( Math.max( getMinWidth(), width ), Math.max( getMinHeight(), height ) );
}
}