/** * 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 java.util.Collections; 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.openmali.types.twodee.Dim2i; import org.openmali.vecmath2.Tuple2i; import org.xith3d.scenegraph.Texture2DCanvas; import org.xith3d.ui.hud.HUD; import org.xith3d.ui.hud.utils.HUDPickResult; import org.xith3d.ui.hud.utils.LocalZIndexComparator; import org.xith3d.ui.hud.utils.HUDPickResult.HUDPickReason; /** * An instance of this class is hold by each Widget. * It can be used to add Widgets to to create a new * Widget from existing ones. * * @author Marvin Froehlich (aka Qudus) */ public class WidgetAssembler { private static final LocalZIndexComparator Z_INDEX_COMPARATOR = new LocalZIndexComparator(); private final Widget owner; private final ArrayList<Widget> managedWidgets = new ArrayList<Widget>(); private final ArrayList<Widget> widgets = new ArrayList<Widget>(); private boolean widgetsSorted = true; private int additionalContentLeft = 0, additionalContentTop = 0, additionalContentWidth = 0, additionalContentHeight = 0; private ArrayList<HUDPickResult> pickedWidgets = null; private Widget currentHoveredWidget = null; private Widget currentFocussedWidget = null; private boolean keyEventsDispatched = false; private boolean pickDispatched = false; final void setHUD( HUD hud ) { for ( int i = 0; i < widgets.size(); i++ ) { widgets.get( i ).setHUD( hud ); } } final void setContainer( WidgetContainer container ) { for ( int i = 0; i < widgets.size(); i++ ) { widgets.get( i ).setContainer( container, owner ); } } public void setAdditionalContentSize( int additionalContentLeft, int additionalContentTop, int additionalContentWidth, int additionalContentHeight ) { this.additionalContentLeft = additionalContentLeft; this.additionalContentTop = additionalContentTop; this.additionalContentWidth = additionalContentWidth; this.additionalContentHeight = additionalContentHeight; owner.updateSizeFactors(); } public final int getAdditionalContentLeft() { return ( additionalContentLeft ); } public final int getAdditionalContentTop() { return ( additionalContentTop ); } public final int getAdditionalContentWidth() { return ( additionalContentWidth ); } public final int getAdditionalContentHeight() { return ( additionalContentHeight ); } public final Widget getCurrentFocussedWidget() { return ( currentFocussedWidget ); } /** * Sets the whole Widget's Transparency. * * @param transparency * @param childrenToo */ public void setTransparency( float transparency, boolean childrenToo ) { for ( int i = 0; i < widgets.size(); i++ ) { final Widget widget = widgets.get( i ); if ( widget instanceof WidgetContainer ) ( (WidgetContainer)widget ).setTransparency( transparency, childrenToo ); else widget.setTransparency( transparency ); } } /** * Enables or disables dispatching of key events to the assemble Widgets. * * @param b */ public void setKeyEventsDispatched( boolean b ) { this.keyEventsDispatched = b; } /** * @return whether dispatching of key events to the assemble Widgets is * enabled. */ public final boolean areKeyEventsDispatched() { return ( keyEventsDispatched ); } /** * 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 */ public void onKeyPressed( Key key, int modifierMask, long when ) { if ( areKeyEventsDispatched() && ( currentFocussedWidget != null ) ) { currentFocussedWidget.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 */ public void onKeyReleased( Key key, int modifierMask, long when ) { if ( areKeyEventsDispatched() && ( currentFocussedWidget != null ) ) { currentFocussedWidget.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 */ public void onKeyTyped( char ch, int modifierMask, long when ) { if ( areKeyEventsDispatched() && ( currentFocussedWidget != null ) ) { currentFocussedWidget.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 */ public void onControllerButtonPressed( ControllerButton button, long when ) { if ( areKeyEventsDispatched() && ( currentFocussedWidget != null ) ) { currentFocussedWidget.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 */ public void onControllerButtonReleased( ControllerButton button, long when ) { if ( areKeyEventsDispatched() && ( currentFocussedWidget != null ) ) { currentFocussedWidget.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 */ public void onControllerAxisChanged( ControllerAxis axis, int axisDelta, long when ) { if ( areKeyEventsDispatched() && ( currentFocussedWidget != null ) ) { currentFocussedWidget.onControllerAxisChanged( axis, axisDelta, when ); } } /** * This event is fired when the state of any DeviceComponent has changed. * * @param comp * @param delta * @param state * @param when * @param isTopMost * @param hasFocus */ public void onInputStateChanged( DeviceComponent comp, int delta, int state, long when, boolean isTopMost, boolean hasFocus ) { if ( areKeyEventsDispatched() && ( currentFocussedWidget != null ) ) { currentFocussedWidget.onInputStateChanged( comp, delta, state, when, isTopMost, hasFocus ); } } /** * Enables or disables dispatching of pickings to the assemble Widgets. * * @param b */ public void setPickDispatched( boolean b ) { this.pickDispatched = b; } /** * @return whether dispatching of pickings to the assemble Widgets is * enabled. */ public final boolean isPickingDispatched() { return ( pickDispatched ); } /** * Dispatches the picking to the assembling Widgets. * * @param canvasX the absolute canvas-x-position of the picking * @param canvasY the absolute canvas-y-position of the picking * @param widgetX * @param widgetY * @param pickReason the reson of this picking * @param button the mouse-button, that caused the picking * @param when * @param meta * @param flags * * @return the picked Widget or null */ public boolean pick( int canvasX, int canvasY, float widgetX, float widgetY, HUDPickReason pickReason, MouseButton button, long when, long meta, int flags ) { ensureWidgetsSortedByZIndex(); //final boolean isInternal = ( flags & HUDPickResult.HUD_PICK_FLAG_IS_INTERNAL ) != 0; final boolean eventsSuppressed = ( flags & HUDPickResult.HUD_PICK_FLAG_EVENTS_SUPPRESSED ) != 0; HUDPickResult tmpHPR = null; HUDPickResult topMost = null; if ( pickedWidgets != null ) pickedWidgets.clear(); else pickedWidgets = new ArrayList<HUDPickResult>(); for ( int i = managedWidgets.size() - 1; i >= 0; i-- ) { final Widget widget = managedWidgets.get( i ); if ( widget.isVisible() && widget.isPickable() ) { tmpHPR = widget.pick( canvasX, canvasY, pickReason, button, when, meta, flags ); if ( tmpHPR != null ) { pickedWidgets.add( tmpHPR ); if ( ( topMost == null ) || ( topMost.getWidget().getZIndex() <= tmpHPR.getWidget().getZIndex() ) ) { topMost = tmpHPR; } } } } if ( ( currentHoveredWidget != null ) && ( ( topMost == null ) || ( topMost.getWidget() != currentHoveredWidget ) ) ) { currentHoveredWidget.onMouseExited( true, false ); currentHoveredWidget = null; } if ( !eventsSuppressed ) { for ( int i = 0; i < pickedWidgets.size(); i++ ) { final HUDPickResult hpr = pickedWidgets.get( i ); final Widget pickedWidget = hpr.getWidget(); final boolean isTopMost = ( pickedWidget == topMost.getWidget() ); boolean hasFocus = ( pickedWidget == currentFocussedWidget ); float pickXHUD_ = widgetX - pickedWidget.getLeft() + owner.getLeft(); float pickYHUD_ = widgetY - pickedWidget.getTop() + owner.getTop(); switch ( pickReason ) { case BUTTON_PRESSED: if ( isTopMost ) { if ( ( currentFocussedWidget != pickedWidget ) && pickedWidget.isFocussable() ) { if ( currentFocussedWidget != null ) currentFocussedWidget.onFocusLost(); currentFocussedWidget = pickedWidget; currentFocussedWidget.onFocusGained(); hasFocus = true; } } pickedWidget.onMouseButtonPressed( button, pickXHUD_, pickYHUD_, when, meta, isTopMost, hasFocus ); break; case BUTTON_RELEASED: pickedWidget.onMouseButtonReleased( button, pickXHUD_, pickYHUD_, when, meta, isTopMost, hasFocus ); break; case MOUSE_MOVED: pickedWidget.onMouseMoved( pickXHUD_, pickYHUD_, (int)meta, when, isTopMost, hasFocus ); if ( ( currentHoveredWidget == null ) && ( isTopMost ) ) { currentHoveredWidget = pickedWidget; currentHoveredWidget.onMouseEntered( isTopMost, hasFocus ); } break; case MOUSE_WHEEL_MOVED_UP: pickedWidget.onMouseWheelMoved( +1, meta != 0L, pickXHUD_, pickYHUD_, when, isTopMost ); break; case MOUSE_WHEEL_MOVED_DOWN: pickedWidget.onMouseWheelMoved( -1, meta != 0L, pickXHUD_, pickYHUD_, when, isTopMost ); break; } //if ( !isTopMost ) HUDPickResult.toPool( hpr ); } } else { for ( int i = 0; i < pickedWidgets.size(); i++ ) { HUDPickResult.toPool( pickedWidgets.get( i ) ); } } pickedWidgets.clear(); return ( topMost != null ); } /** * Retrieves the SubWidget which is actually picked. * * @param relX the mouse-x-position relative to the owner Widget's position * @param relY the mouse-y-position relative to the owner Widget's position */ public Widget pick( float relX, float relY ) { ensureWidgetsSortedByZIndex(); Widget topMost = null; float ownerLeft = owner.getLeft(); float ownerTop = owner.getTop(); for ( int i = 0; i < managedWidgets.size(); i++ ) { final Widget widget = managedWidgets.get( i ); if ( widget.isVisible() && widget.isPickable() ) { if ( ( ( widget.getLeft() - ownerLeft <= relX ) && ( relX <= widget.getLeft() - ownerLeft + widget.getWidth() ) ) && ( ( widget.getTop() - ownerTop <= relY ) && ( relY <= widget.getTop() - ownerTop + widget.getHeight() ) ) ) { //if ( ( topMost == null ) || ( topMost.getSGZPosition() <= widget.getSGZPosition() ) ) if ( ( topMost == null ) || ( topMost.getZIndex() <= widget.getZIndex() ) ) { topMost = widget; } } } } return ( topMost ); } /** * Repositions the Widget relative to the owner Widget * * @param widget the Widget to reposition * @param posX the new x-position * @param posY the new y-position */ public void reposition( Widget widget, float posX, float posY ) { widget.setLocation( owner.getLeft() + posX, owner.getTop() + posY ); } /** * Called by the owner when its location has changed. * * @param deltaX * @param deltaY */ public void onOwnerMoved( float deltaX, float deltaY, boolean needsTextureRefresh ) { for ( int i = 0; i < managedWidgets.size(); i++ ) { Widget widget = managedWidgets.get( i ); widget.setLocation( widget.getLeft() + deltaX, widget.getTop() + deltaY, false, needsTextureRefresh ); } } /** * @return the x-position relative to the owner Widget * * @param widget the Widget in question */ public final float getPositionX( Widget widget ) { return ( widget.getLeft() - owner.getLeft() ); } /** * @return the y-position relative to the owner Widget * * @param widget the Widget in question */ public final float getPositionY( Widget widget ) { return ( widget.getTop() - owner.getTop() ); } /** * @return <i>true</i>, if the Widget is in the List of Assemble-Widgets */ public boolean contains( Widget widget ) { return ( widgets.contains( widget ) ); } private void ensureWidgetsSortedByZIndex() { if ( widgetsSorted ) return; Collections.sort( widgets, Z_INDEX_COMPARATOR ); widgetsSorted = true; } /** * Adds a Widget to this assembler. * * @param widget the Widget to add * @param locX the x-location to add the Widget at (relative to the owner's position) * @param locY the y-location to add the Widget at (relative to the owner's position) */ public void addWidget( Widget widget, float locX, float locY ) { if ( widget.isHeavyWeight() ) throw new IllegalArgumentException( "You cannot add a heavyweight Widget to the WidgetAssembler." ); widgets.add( widget ); managedWidgets.add( widget ); reposition( widget, locX, locY ); widget.setContainer( owner.getContainer(), owner ); widget.setHUD( owner.getHUD() ); widget.setHostWidget( owner ); widgetsSorted = false; } /** * Adds a Widget to this assembler at location(0, 0). * * @param widget the Widget to add */ public final void addWidget( Widget widget ) { addWidget( widget, widget.getLeft(), widget.getTop() ); } /** * Adds an unmanaged Widget to this assembler. * * @param widget the Widget to add */ public void addUnmanagedWidget( Widget widget ) { if ( widget.isHeavyWeight() ) throw new IllegalArgumentException( "You cannot add a heavyweight Widget to the WidgetAssembler." ); widgets.add( widget ); widget.setPassive( true ); widget.setContainer( owner.getContainer(), owner ); widget.setHUD( owner.getHUD() ); } /** * Removes a Widget from this assembler. * * @param widget the Widget to be removed */ public void removeWidget( Widget widget ) { if ( widget.getAssembly() != owner ) throw new Error( "The given Widget doesn't belong to this owner Widget." ); managedWidgets.remove( widget ); widgets.remove( widget ); widget.setPassive( false ); widget.setHostWidget( null ); widget.setHUD( null ); widget.setContainer( null, null ); } public void update() { for ( int i = 0; i < widgets.size(); i++ ) { //widgets.get( i ).setContainer( owner.getContainer() ); widgets.get( i ).update(); } //updateLocations( owner.getLeft(), owner.getTop() ); } final void setWidgetsPassive( boolean passive ) { for ( int i = 0; i < widgets.size(); i++ ) { widgets.get( i ).setPassive( passive ); } } protected void setWidgetsDirty() { for ( int i = 0; i < widgets.size(); i++ ) { //if ( !widgets.get( i ).isHeavyWeight() ) widgets.get( i ).setWidgetDirty(); } } protected void draw( Texture2DCanvas texCanvas, int offsetX, int offsetY ) { ensureWidgetsSortedByZIndex(); Tuple2i tmpLoc = Tuple2i.fromPool(); Dim2i tmpSize = Dim2i.fromPool(); float ownerLeft = owner.getLeft(); float ownerTop = owner.getTop(); for ( int i = 0; i < managedWidgets.size(); i++ ) { Widget widget = managedWidgets.get( i ); if ( widget.isVisible() ) { widget.getRelLocationHUD2Pixels_( widget.getLeft() - ownerLeft, widget.getTop() - ownerTop, tmpLoc ); widget.getSizeHUD2Pixels_( widget.getWidth(), widget.getHeight(), tmpSize ); widget.drawAndUpdateWidget( texCanvas, offsetX + tmpLoc.getX(), offsetY + tmpLoc.getY(), tmpSize.getWidth(), tmpSize.getHeight(), false ); } } Dim2i.toPool( tmpSize ); Tuple2i.toPool( tmpLoc ); } public WidgetAssembler( Widget owner ) { this.owner = owner; } }