public abstract class Button extends Element implements Control, MouseButtonListener, MouseFocusListener, KeyboardListener, TabFocusListener {
	protected String hoverSound, pressedSound;
	protected boolean useHoverSound, usePressedSound;
	protected float hoverSoundVolume, pressedSoundVolume;
	protected Element icon;
	protected Texture hoverImg = null, pressedImg = null;
	protected ColorRGBA hoverFontColor = null, pressedFontColor = null;
	protected boolean isToggleButton = false;
	protected boolean isToggled = false;
	private Spatial spatial;
	protected boolean isStillPressed = false;
	private boolean useInterval = false;
	private float intervalsPerSecond = 4;
	protected float trackInterval = (4/1000), currentTrack = 0;
	protected boolean initClickPause = false;
	protected float initClickInterval = 0.25f, currentInitClickTrack = 0;
	protected RadioButtonGroup radioButtonGroup = null;
	protected boolean isRadioButton = false;
	protected ColorRGBA originalFontColor;
	protected Vector2f hoverImgOffset, pressedImgOffset;
	// Optional LabelElement
	protected boolean useOptionalLabel = false;
	protected TextElement buttonLabel;
	
	/**
	 * Creates a new instance of the Button control
	 * 
	 * @param screen The screen control the Element is to be added to
	 */
	public Button(ElementManager screen) {
		this(screen, UIDUtil.getUID(), Vector2f.ZERO,
			screen.getStyle("Button").getVector2f("defaultSize"),
			screen.getStyle("Button").getVector4f("resizeBorders"),
			screen.getStyle("Button").getString("defaultImg")
		);
	}
	
	/**
	 * Creates a new instance of the Button control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param position A Vector2f containing the x/y position of the Element
	 */
	public Button(ElementManager screen, Vector2f position) {
		this(screen, UIDUtil.getUID(), position,
			screen.getStyle("Button").getVector2f("defaultSize"),
			screen.getStyle("Button").getVector4f("resizeBorders"),
			screen.getStyle("Button").getString("defaultImg")
		);
	}
	
	/**
	 * Creates a new instance of the Button control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param position A Vector2f containing the x/y position of the Element
	 * @param dimensions A Vector2f containing the width/height dimensions of the Element
	 */
	public Button(ElementManager screen, Vector2f position, Vector2f dimensions) {
		this(screen, UIDUtil.getUID(), position, dimensions,
			screen.getStyle("Button").getVector4f("resizeBorders"),
			screen.getStyle("Button").getString("defaultImg")
		);
	}
	
	/**
	 * Creates a new instance of the Button control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param position A Vector2f containing the x/y position of the Element
	 * @param dimensions A Vector2f containing the width/height dimensions of the Element
	 * @param resizeBorders A Vector4f containing the border information used when resizing the default image (x = N, y = W, z = E, w = S)
	 * @param defaultImg The default image to use for the Button
	 */
	public Button(ElementManager screen, Vector2f position, Vector2f dimensions, Vector4f resizeBorders, String defaultImg) {
		this(screen, UIDUtil.getUID(), position, dimensions,resizeBorders,defaultImg);
	}
	
	/**
	 * Creates a new instance of the Button control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param UID A unique String identifier for the Element
	 * @param position A Vector2f containing the x/y position of the Element
	 */
	public Button(ElementManager screen, String UID, Vector2f position) {
		this(screen, UID, position,
			screen.getStyle("Button").getVector2f("defaultSize"),
			screen.getStyle("Button").getVector4f("resizeBorders"),
			screen.getStyle("Button").getString("defaultImg")
		);
	}
	
	/**
	 * Creates a new instance of the Button control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param UID A unique String identifier for the Element
	 * @param position A Vector2f containing the x/y position of the Element
	 * @param dimensions A Vector2f containing the width/height dimensions of the Element
	 */
	public Button(ElementManager screen, String UID, Vector2f position, Vector2f dimensions) {
		this(screen, UID, position, dimensions,
			screen.getStyle("Button").getVector4f("resizeBorders"),
			screen.getStyle("Button").getString("defaultImg")
		);
	}
	
	/**
	 * Creates a new instance of the Button control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param UID A unique String identifier for the Element
	 * @param position A Vector2f containing the x/y position of the Element
	 * @param dimensions A Vector2f containing the width/height dimensions of the Element
	 * @param resizeBorders A Vector4f containing the border information used when resizing the default image (x = N, y = W, z = E, w = S)
	 * @param defaultImg The default image to use for the Button
	 */
	public Button(ElementManager screen, String UID, Vector2f position, Vector2f dimensions, Vector4f resizeBorders, String defaultImg) {
		super(screen, UID, position, dimensions, resizeBorders, defaultImg);
		
		this.setScaleNS(false);
		this.setScaleEW(false);
		this.setFontSize(screen.getStyle("Button").getFloat("fontSize"));
		this.setFontColor(screen.getStyle("Button").getColorRGBA("fontColor"));
		this.setTextVAlign(BitmapFont.VAlign.valueOf(screen.getStyle("Button").getString("textVAlign")));
		this.setTextAlign(BitmapFont.Align.valueOf(screen.getStyle("Button").getString("textAlign")));
		this.setTextWrap(LineWrapMode.valueOf(screen.getStyle("Button").getString("textWrap")));
		setTextPaddingByKey("Button","textPadding");
		
		if (getUseLocalTexture()) {
		//	setColorMap(screen.getStyle("Button").getString("defaultImg"));
			boolean tile = false;
			try {
				tile = screen.getStyle("Button").getBoolean("tileImages");
			} catch (Exception ex) {  }
			this.setTileImage(tile);
		}
		
		this.setMinDimensions(dimensions.clone());
		
		if (screen.getStyle("Button").getString("hoverImg") != null) {
			setButtonHoverInfo(
				screen.getStyle("Button").getString("hoverImg"),
				screen.getStyle("Button").getColorRGBA("hoverColor")
			);
		}
		if (screen.getStyle("Button").getString("pressedImg") != null) {
			setButtonPressedInfo(
				screen.getStyle("Button").getString("pressedImg"),
				screen.getStyle("Button").getColorRGBA("pressedColor")
			);
		}
		
		originalFontColor = fontColor.clone();
		
		hoverSound = screen.getStyle("Button").getString("hoverSound");
		useHoverSound = screen.getStyle("Button").getBoolean("useHoverSound");
		hoverSoundVolume = screen.getStyle("Button").getFloat("hoverSoundVolume");
		pressedSound = screen.getStyle("Button").getString("pressedSound");
		usePressedSound = screen.getStyle("Button").getBoolean("usePressedSound");
		pressedSoundVolume = screen.getStyle("Button").getFloat("pressedSoundVolume");
		
		populateEffects("Button");
		if (Screen.isAndroid()) {
			removeEffect(Effect.EffectEvent.Hover);
			removeEffect(Effect.EffectEvent.TabFocus);
			removeEffect(Effect.EffectEvent.LoseTabFocus);
		}
		
		buttonLabel = new TextElement(screen, Vector2f.ZERO, getDimensions(), screen.getDefaultGUIFont()) {
			@Override
			public void onUpdate(float tpf) {  }
			@Override
			public void onEffectStart() {  }
			@Override
			public void onEffectStop() {  }
		};
		buttonLabel.setIgnoreMouse(true);
		buttonLabel.setIsResizable(false);
		buttonLabel.setIsMovable(false);
		buttonLabel.setScaleEW(true);
		buttonLabel.setScaleNS(true);
		buttonLabel.setFontSize(fontSize);
		buttonLabel.setFontColor(fontColor);
		buttonLabel.setUseTextClipping(true);
		buttonLabel.setTextAlign(textAlign);
		buttonLabel.setTextVAlign(textVAlign);
		buttonLabel.setDocking(Docking.SW);
		buttonLabel.addClippingLayer(this);
		
		/*
		String defaultIcon = screen.getStyle("Button").getString("defaultIcon");
		if (defaultIcon != null) {
			Vector2f size = getDimensions();
			try {
				size.set(screen.getStyle("Button").getVector2f("defaultIconSize"));
			} catch (Exception ex) {  }
			setButtonIcon(size.x, size.y,defaultIcon);
		}
		*/
	}
	
	@Override
	public void controlIsEnabledHook(boolean isEnabled) {
		if (!isEnabled) {
			runPressedEffect(false);
		} else {
			runLoseFocusEffect();
		}
	}
	
	/**
	 * Clears current hover and pressed images set by Style defines
	 */
	public void clearAltImages() {
		setButtonHoverInfo(null, null);
		setButtonPressedInfo(null, null);
	}
	
	/**
	 * Sets if the button is to interact as a Toggle Button
	 * Click once to activate / Click once to deactivate
	 * 
	 * @param isToggleButton boolean
	 */
	public void setIsToggleButton(boolean isToggleButton) {
		this.isToggleButton = isToggleButton;
	}
	
	/**
	 * Returns if the Button is flagged as a Toggle Button
	 * 
	 * @return boolean isToggleButton
	 */
	public boolean getIsToggleButton() {
		return this.isToggleButton;
	}
	
	/**
	 * Sets if the button is to interact as a Radio Button
	 * Click once to activate - stays active
	 * 
	 * @param isRadioButton boolean
	 */
	public void setIsRadioButton(boolean isRadioButton) {
		this.isRadioButton = isRadioButton;
	}
	
	/**
	 * Returns if the Button is flagged as a Toggle Button
	 * 
	 * @return boolean isRadioButton
	 */
	public boolean getIsRadioButton() {
		return this.isRadioButton;
	}
	
	/**
	 * Set a toggle button state to toggled/untoggled
	 * @param isToggled boolean
	 */
	public void setIsToggledNoCallback(boolean isToggled) {
		this.isToggled = isToggled;
		
		if (pressedImg != null && isToggled) {
			runPressedEffect(false);
		} else {
			runResetEffect();
		}
		
		if (radioButtonGroup != null) {
			if (isToggled)
				radioButtonGroup.setSelected(this);
		}
	}
	
	/**
	 * Set a toggle button state to toggled/untoggled and calls the user left mouse button event methods
	 * @param isToggled boolean
	 */
	public void setIsToggled(boolean isToggled) {
		this.isToggled = isToggled;
		
		if (pressedImg != null && isToggled) {
			runPressedEffect(false);
		} else {
			runResetEffect();
		}
		
		MouseButtonEvent evtd = new MouseButtonEvent(0,true,0,0);
		MouseButtonEvent evtu = new MouseButtonEvent(0,false,0,0);
		onButtonMouseLeftDown(evtd, isToggled);
		onButtonMouseLeftUp(evtu, isToggled);
		if (radioButtonGroup != null) {
			if (isToggled)
				radioButtonGroup.setSelected(this);
		}
		evtu.setConsumed();
		evtd.setConsumed();
	}
	
	/**
	 * Returns the current toggle state of the Button if the Button has been flagged as 
	 * isToggle
	 * @return boolean isToggle
	 */
	public boolean getIsToggled() {
		return this.isToggled;
	}
	
	/**
	 * Sets the texture image path and color to use when the button has mouse focus
	 * @param pathHoverImg String path to image for mouse focus event
	 * @param hoverFontColor ColorRGBA to use for mouse focus event
	 */
	public final void setButtonHoverInfo(String pathHoverImg, ColorRGBA hoverFontColor) {
		if (pathHoverImg != null) {
			if ((!screen.getUseTextureAtlas() && !getUseLocalAtlas()) || getUseLocalTexture()) {
				try {
					this.hoverImg = app.getAssetManager().loadTexture(pathHoverImg);
					this.hoverImg.setMinFilter(Texture.MinFilter.BilinearNoMipMaps);
					this.hoverImg.setMagFilter(Texture.MagFilter.Nearest);
					this.hoverImg.setWrap(Texture.WrapMode.Repeat);
				} catch (Exception ex) {  }
			} else {
				this.hoverImg = this.getElementTexture();
				hoverImgOffset = getAtlasTextureOffset(screen.parseAtlasCoords(pathHoverImg));
			}
		} else {
			this.hoverImg = null;
		}
		if (hoverFontColor != null) {
			this.hoverFontColor = hoverFontColor;
		} else {
			this.hoverFontColor = null;
		}
	}
	
	/**
	 * Returns the Texture used when button has mouse focus
	 * 
	 * @return Texture
	 */
	public Texture getButtonHoverImg() {
		return this.hoverImg;
	}
	
	protected void runHoverEffect(boolean audio) {
		if (hoverImg != null) {
			Effect effect = getEffect(Effect.EffectEvent.Hover);
			if (effect != null) {
				if (useHoverSound && screen.getUseUIAudio() && audio) {
					effect.setAudioFile(hoverSound);
					effect.setAudioVolume(hoverSoundVolume);
				}
				effect.setBlendImage(hoverImg);
				if ((screen.getUseTextureAtlas() || getUseLocalAtlas()) && !getUseLocalTexture()) effect.setBlendImageOffset(hoverImgOffset);
				screen.getEffectManager().applyEffect(effect);
			}
		}
		if (hoverFontColor != null) {
			if (!useOptionalLabel)
				setFontColor(hoverFontColor);
			else
				buttonLabel.setFontColor(hoverFontColor);
		}
	}
	
	/**
	 * Sets the image and font color to use when the button is depressed
	 * @param pathPressedImg Path to the image for pressed state
	 * @param pressedFontColor ColorRGBA to use for pressed state
	 */
	public final void setButtonPressedInfo(String pathPressedImg, ColorRGBA pressedFontColor) {
		if (pathPressedImg != null) {
			if ((!screen.getUseTextureAtlas() && !getUseLocalAtlas()) || getUseLocalTexture()) {
				try {
					this.pressedImg = app.getAssetManager().loadTexture(pathPressedImg);
					this.pressedImg.setMinFilter(Texture.MinFilter.BilinearNoMipMaps);
					this.pressedImg.setMagFilter(Texture.MagFilter.Nearest);
					this.pressedImg.setWrap(Texture.WrapMode.Repeat);
				} catch (Exception ex) {  }
			} else {
				this.pressedImg = this.getElementTexture();
				pressedImgOffset = getAtlasTextureOffset(screen.parseAtlasCoords(pathPressedImg));
			}
		} else {
			this.pressedImg = null;
		}
		if (pressedFontColor != null) {
			this.pressedFontColor = pressedFontColor;
		} else {
			this.pressedFontColor = null;
		}
	}
	
	/**
	 * Returns the texture to be used when the button is depressed
	 * 
	 * @return Texture
	 */
	public Texture getButtonPressedImg() {
		return this.pressedImg;
	}
	
	protected void runPressedEffect(boolean audio) {
		if (pressedImg != null) {
			Effect effect = getEffect(Effect.EffectEvent.Press);
			if (effect != null) {
				if (usePressedSound && screen.getUseUIAudio() && audio) {
					effect.setAudioFile(pressedSound);
					effect.setAudioVolume(pressedSoundVolume);
				}
				effect.setBlendImage(pressedImg);
				if ((screen.getUseTextureAtlas() || getUseLocalAtlas()) && !getUseLocalTexture()) effect.setBlendImageOffset(pressedImgOffset);
				screen.getEffectManager().applyEffect(effect);
			}
		}
		if (pressedFontColor != null) {
			if (!useOptionalLabel)
				setFontColor(pressedFontColor);
			else
				buttonLabel.setFontColor(pressedFontColor);
		}
	}
	
	protected void runLoseFocusEffect() {
		Effect effect = getEffect(Effect.EffectEvent.LoseFocus);
		if (effect != null) {
			effect.setBlendImage(getElementTexture());
			if ((screen.getUseTextureAtlas() || getUseLocalAtlas()) && !getUseLocalTexture()) effect.setBlendImageOffset(new Vector2f(0,0));
			screen.getEffectManager().applyEffect(effect);
		}
		if (originalFontColor != null) {
			if (!useOptionalLabel)
				setFontColor(originalFontColor);
			else
				buttonLabel.setFontColor(originalFontColor);
		}
	}
	
	protected void runResetEffect() {
		Effect effect = getEffect(Effect.EffectEvent.Press);
		if (effect != null) {
			effect.setBlendImage(getElementTexture());
			if ((screen.getUseTextureAtlas() || getUseLocalAtlas()) && !getUseLocalTexture()) effect.setBlendImageOffset(new Vector2f(0,0));
			screen.getEffectManager().applyEffect(effect);
		}
		if (originalFontColor != null) {
			if (!useOptionalLabel)
				setFontColor(originalFontColor);
			else
				buttonLabel.setFontColor(originalFontColor);
		}
	}
	
	/**
	 * If called, an overlay icon is added to the button.  This icon is centered by default
	 * 
	 * @param width width to display icon
	 * @param height to display icon
	 * @param texturePath The path of the image to use as the icon overlay
	 */
	public final void setButtonIcon(float width, float height, String texturePath) {
		/*
		if (icon != null) {
			if (icon.getParent() != null) {
				elementChildren.remove(icon.getUID());
				icon.removeFromParent();
			}
		}
		*/
		if (icon == null) {
			icon = new Element(
				screen,
				this.getUID() + ":btnIcon",
				new Vector2f((getWidth()/2)-(width/2),(getHeight()/2)-(height/2)),
				new Vector2f(width,height),
				new Vector4f(0,0,0,0),
				texturePath
			);
			icon.setIgnoreMouse(true);
			icon.setDocking(Docking.SW);
			icon.setScaleEW(false);
			icon.setScaleNS(false);
			this.addChild(icon);
		}
		if (screen.getUseTextureAtlas() || this.getUseLocalAtlas())
			icon.setTextureAtlasImage(icon.getElementTexture(), texturePath);
	}
	
	public Element getButtonIcon() {
		return this.icon;
	}
	
	@Override
	public void onMouseLeftPressed(MouseButtonEvent evt) {
		if (isEnabled) {
			if (isToggleButton) {
				if (isToggled) {
					if (!isRadioButton) isToggled = false;
				} else {
					isToggled = true;
				}
			}
			runPressedEffect(true);
			isStillPressed = true;
			initClickPause = true;
			currentInitClickTrack = 0;
			onButtonMouseLeftDown(evt, isToggled);
		}
		evt.setConsumed();
	}

	@Override
	public void onMouseLeftReleased(MouseButtonEvent evt) {
		if (isEnabled) {
			if (!isToggleButton) {
				if (getHasFocus()) {
					runLoseFocusEffect();
					runHoverEffect(false);
				} else {
					runLoseFocusEffect();
				}
			} else {
				if (!isToggled) {
					runLoseFocusEffect();
					runHoverEffect(false);
				}
			}
			isStillPressed = false;
			initClickPause = false;
			currentInitClickTrack = 0;
			onButtonMouseLeftUp(evt, isToggled);
			if (radioButtonGroup != null)
				radioButtonGroup.setSelected(this);
		}
		evt.setConsumed();
	}
	
	public void setRadioButtonGroup(RadioButtonGroup radioButtonGroup) {
		this.radioButtonGroup = radioButtonGroup;
		this.isToggleButton = true;
		this.isRadioButton = true;
	}
	
	@Override
	public void onMouseRightPressed(MouseButtonEvent evt) {
		if (isEnabled) {
			onButtonMouseRightDown(evt, isToggled);
			if (screen.getUseToolTips()) {
				
			}
		}
		evt.setConsumed();
	}

	@Override
	public void onMouseRightReleased(MouseButtonEvent evt) {
		if (isEnabled) {
			onButtonMouseRightUp(evt, isToggled);
		}
		evt.setConsumed();
	}

	@Override
	public void onGetFocus(MouseMotionEvent evt) {
		if (isEnabled) {
			if (!getHasFocus()) {
				if (!isToggled) {
					runHoverEffect(true);
				}
				screen.setCursor(CursorType.HAND);
				onButtonFocus(evt);
				if (screen.getUseToolTips()) {
					
				}
			}
			setHasFocus(true);
		}
	}

	@Override
	public void onLoseFocus(MouseMotionEvent evt) {
		if (isEnabled) {
			if (getHasFocus()) {
				if (!isToggled) {
					runLoseFocusEffect();
				}
				screen.setCursor(CursorType.POINTER);
				onButtonLostFocus(evt);
			}
			setHasFocus(false);
		}
	}
	
	/**
	 * Enables/disbale hover effect sound
	 * @param useHoverSound 
	 */
	public void setUseButtonHoverSound(boolean useHoverSound) {
		this.useHoverSound = useHoverSound;
	}
	
	/**
	 * Enable/disable pressed effect sound
	 * @param usePressedSound 
	 */
	public void setUseButtonPressedSound(boolean usePressedSound) {
		this.usePressedSound = usePressedSound;
	}
	
	public abstract void onButtonMouseLeftDown(MouseButtonEvent evt, boolean toggled);
	public abstract void onButtonMouseRightDown(MouseButtonEvent evt, boolean toggled);
	public abstract void onButtonMouseLeftUp(MouseButtonEvent evt, boolean toggled);
	public abstract void onButtonMouseRightUp(MouseButtonEvent evt, boolean toggled);
	public abstract void onButtonFocus(MouseMotionEvent evt);
	public abstract void onButtonLostFocus(MouseMotionEvent evt);
	/**
	 * Abstract method for handling interval updates while the button is still pressed
	 * 
	 * NOTE: This is only called if the button's setInterval method has been previously called
	 */
	public void onButtonStillPressedInterval() {  }
	
	/**
	 * Returns if the button is still pressed
	 * @return boolean
	 */
	public boolean getIsStillPressed() {
		return this.isStillPressed;
	}
	
	@Override
	public Control cloneForSpatial(Spatial spatial) {
		return this;
	}

	@Override
	public void setSpatial(Spatial spatial) {
		this.spatial = spatial;
	}

	@Override
	public void render(RenderManager rm, ViewPort vp) {
		
	}
	
	@Override
	public void update(float tpf) {
		if (isEnabled) {
			if (useInterval && isStillPressed) {
				if (initClickPause) {
					currentInitClickTrack += tpf;
					if (currentInitClickTrack >= initClickInterval) {
						initClickPause = false;
						currentInitClickTrack = 0;
					}
				} else {
					currentTrack += tpf;
					if (currentTrack >= trackInterval) {
						onButtonStillPressedInterval();
						currentTrack = 0;
					}
				}
			}
		}
	}
	
	/**
	 * This method registers the button as a JME Control creating an interval event to be
	 * processed every time the interval limit has been reached.
	 * 
	 * See onButtonStillPressedInterval()
	 * 
	 * @param intervalsPerSecond The number of calls to onButtonStillPressedInterval per second.
	 */
	public void setInterval(float intervalsPerSecond) {
		if (intervalsPerSecond > 0) {
			this.useInterval = true;
			this.intervalsPerSecond = intervalsPerSecond;
			this.trackInterval = (float)(1/intervalsPerSecond);
			this.addControl(this);
		} else {
			this.useInterval = false;
			this.intervalsPerSecond = intervalsPerSecond;
			this.removeControl(Button.class);
		}
	}
	
	// Tab focus
	@Override
	public void setTabFocus() {
		screen.setKeyboardElement(this);
		if (isEnabled) {
			Effect effect = getEffect(Effect.EffectEvent.TabFocus);
			if (effect != null) {
				effect.setColor(ColorRGBA.DarkGray);
				screen.getEffectManager().applyEffect(effect);
			}
		}
	}
	
	@Override
	public void resetTabFocus() {
		screen.setKeyboardElement(null);
		if (isEnabled) {
			if (!getIsToggled()) {
				Effect effect = getEffect(Effect.EffectEvent.LoseTabFocus);
				if (effect != null) {
					effect.setColor(ColorRGBA.White);
					screen.getEffectManager().applyEffect(effect);
				}
			}
		}
	}
	
	// Default keyboard interaction
	@Override
	public void onKeyPress(KeyInputEvent evt) {
		if (evt.getKeyCode() == KeyInput.KEY_SPACE) {
			if (isEnabled && getParent() != null)
				onMouseLeftPressed(new MouseButtonEvent(0,true,0,0));
		}
	}
	
	@Override
	public void onKeyRelease(KeyInputEvent evt) {
		if (evt.getKeyCode() == KeyInput.KEY_SPACE) {
			if (isEnabled && getParent() != null)
				onMouseLeftReleased(new MouseButtonEvent(0,false,0,0));
		}
	}
	
	@Override
	public void setText(String text) {
		this.text = text;
		if (textElement == null) {
			textElement = new BitmapText(font, false);
			textElement.setBox(new Rectangle(0,0,getDimensions().x,getDimensions().y));
		//	textElement = new TextElement(screen, Vector2f.ZERO);
		}
		textElement.setLineWrapMode(textWrap);
		textElement.setAlignment(textAlign);
		textElement.setVerticalAlignment(textVAlign);
		textElement.setSize(fontSize);
		textElement.setColor(fontColor);
		
		if (textVAlign == BitmapFont.VAlign.Center) {
			textElement.setVerticalAlignment(BitmapFont.VAlign.Top);
			centerTextVertically(text);
		}
		
		textElement.setText(text);
		updateTextElement();
		if (textElement.getParent() == null) {
			this.attachChild(textElement);
		}
	}
	
	public void setLabelText(String text) {
		this.useOptionalLabel = true;
	//	buttonLabel.setSizeToText(true);
		buttonLabel.setDimensions(getDimensions());
		buttonLabel.getAnimText().setBounds(getDimensions());
		buttonLabel.setText(text);
		if (buttonLabel.getParent() == null) {
			addChild(buttonLabel);
		}
	}
	
	public void setFontColor(ColorRGBA fontColor, boolean makeDefault) {
		if (!useOptionalLabel)
			super.setFontColor(fontColor);
		else
			buttonLabel.setFontColor(fontColor);
		if (makeDefault)
			originalFontColor = fontColor.clone();
	}
	
	public void setStyles(String styleName) {
		Style style = screen.getStyle(styleName);

		// images and state colours
		String img = style.getString("defaultImg");
		
		if (img != null)
			setColorMap(img);
		if (style.getString("hoverImg") != null) {
			setButtonHoverInfo(
				style.getString("hoverImg"),
				style.getColorRGBA("hoverColor"));
		}
		if (style.getString("pressedImg") != null) {
			setButtonPressedInfo(
				style.getString("pressedImg"),
				style.getColorRGBA("pressedColor"));
		}

		// fonts and text
		setFontSize(style.getFloat("fontSize"));
	//	setFont((Screen)screen.getDefaultGUIFont());
		setFontColor(style.getColorRGBA("fontColor"));
		setTextVAlign(BitmapFont.VAlign.valueOf(style.getString("textVAlign")));
		setTextAlign(BitmapFont.Align.valueOf(style.getString("textAlign")));
		setTextWrap(LineWrapMode.valueOf(style.getString("textWrap")));
		setTextPaddingByKey("Button","textPadding");
	//	buttonTextInsets = style.getFloat("buttonTextInsets");

		// borders
		borders.set(style.getVector4f("resizeBorders"));

		// audio
		hoverSound = style.getString("hoverSound");
		useHoverSound = style.getBoolean("useHoverSound");
		hoverSoundVolume = style.getFloat("hoverSoundVolume");
		pressedSound = style.getString("pressedSound");
		usePressedSound = style.getBoolean("usePressedSound");
		pressedSoundVolume = style.getFloat("pressedSoundVolume");

		// Fx
		populateEffects(styleName);
		if (Screen.isAndroid()) {
			removeEffect(Effect.EffectEvent.Hover);
			removeEffect(Effect.EffectEvent.TabFocus);
			removeEffect(Effect.EffectEvent.LoseTabFocus);
		}

	//	defaultSize = style.getVector2f(“defaultSize”);
		setDimensions(getOrgDimensions());
		getModel().updateDimensions(getWidth(), getHeight());

		// TODO
		rebuildModel();
		
		originalFontColor = fontColor.clone();
    }
	
	/**
	 * Fix for BitmapFont.VAlign
	 * @param text 
	 */
	private void centerTextVertically(String text) {
		float height = BitmapTextUtil.getTextLineHeight(this, text);
		setTextPosition(getTextPosition().x, getHeight()/2-((height-(height*.1f))/2));
	}
}
 public class ButtonAdapter extends Button {
	/**
	 * Creates a new instance of the Button control
	 * 
	 * @param screen The screen control the Element is to be added to
	 */
	public ButtonAdapter(ElementManager screen) {
		this(screen, UIDUtil.getUID(), Vector2f.ZERO,
			screen.getStyle("Button").getVector2f("defaultSize"),
			screen.getStyle("Button").getVector4f("resizeBorders"),
			screen.getStyle("Button").getString("defaultImg")
		);
	}
	
	/**
	 * Creates a new instance of the Button control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param position A Vector2f containing the x/y position of the Element
	 */
	public ButtonAdapter(ElementManager screen, Vector2f position) {
		this(screen, UIDUtil.getUID(), position,
			screen.getStyle("Button").getVector2f("defaultSize"),
			screen.getStyle("Button").getVector4f("resizeBorders"),
			screen.getStyle("Button").getString("defaultImg")
		);
	}
	
	/**
	 * Creates a new instance of the Button control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param position A Vector2f containing the x/y position of the Element
	 * @param dimensions A Vector2f containing the width/height dimensions of the Element
	 */
	public ButtonAdapter(ElementManager screen, Vector2f position, Vector2f dimensions) {
		this(screen, UIDUtil.getUID(), position, dimensions,
			screen.getStyle("Button").getVector4f("resizeBorders"),
			screen.getStyle("Button").getString("defaultImg")
		);
	}
	
	/**
	 * Creates a new instance of the Button control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param position A Vector2f containing the x/y position of the Element
	 * @param dimensions A Vector2f containing the width/height dimensions of the Element
	 * @param resizeBorders A Vector4f containing the border information used when resizing the default image (x = N, y = W, z = E, w = S)
	 * @param defaultImg The default image to use for the Button
	 */
	public ButtonAdapter(ElementManager screen, Vector2f position, Vector2f dimensions, Vector4f resizeBorders, String defaultImg) {
		this(screen, UIDUtil.getUID(), position, dimensions,resizeBorders,defaultImg);
	}
	
	/**
	 * Creates a new instance of the Button control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param UID A unique String identifier for the Element
	 * @param position A Vector2f containing the x/y position of the Element
	 */
	public ButtonAdapter(ElementManager screen, String UID, Vector2f position) {
		this(screen, UID, position,
			screen.getStyle("Button").getVector2f("defaultSize"),
			screen.getStyle("Button").getVector4f("resizeBorders"),
			screen.getStyle("Button").getString("defaultImg")
		);
	}
	
	/**
	 * Creates a new instance of the Button control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param UID A unique String identifier for the Element
	 * @param position A Vector2f containing the x/y position of the Element
	 * @param dimensions A Vector2f containing the width/height dimensions of the Element
	 */
	public ButtonAdapter(ElementManager screen, String UID, Vector2f position, Vector2f dimensions) {
		this(screen, UID, position, dimensions,
			screen.getStyle("Button").getVector4f("resizeBorders"),
			screen.getStyle("Button").getString("defaultImg")
		);
	}
	
	/**
	 * Creates a new instance of the Button control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param UID A unique String identifier for the Element
	 * @param position A Vector2f containing the x/y position of the Element
	 * @param dimensions A Vector2f containing the width/height dimensions of the Element
	 * @param resizeBorders A Vector4f containing the border information used when resizing the default image (x = N, y = W, z = E, w = S)
	 * @param defaultImg The default image to use for the Button
	 */
	public ButtonAdapter(ElementManager screen, String UID, Vector2f position, Vector2f dimensions, Vector4f resizeBorders, String defaultImg) {
		super(screen, UID, position, dimensions, resizeBorders, defaultImg);
	}
	
	@Override
	public void onButtonMouseLeftDown(MouseButtonEvent evt, boolean toggled) {  }
	@Override
	public void onButtonMouseRightDown(MouseButtonEvent evt, boolean toggled) {  }
	@Override
	public void onButtonMouseLeftUp(MouseButtonEvent evt, boolean toggled) {  }
	@Override
	public void onButtonMouseRightUp(MouseButtonEvent evt, boolean toggled) {  }
	@Override
	public void onButtonFocus(MouseMotionEvent evt) {  }
	@Override
	public void onButtonLostFocus(MouseMotionEvent evt) {  }
}
 public class CheckBox extends ButtonAdapter {
	
	ClickableLabel label;
	float labelFontSize;
	
	/**
	 * Creates a new instance of the CheckBox control
	 * 
	 * @param screen The screen control the Element is to be added to
	 */
	public CheckBox(ElementManager screen) {
		this(screen, UIDUtil.getUID(), Vector2f.ZERO,
			screen.getStyle("CheckBox").getVector2f("defaultSize"),
			screen.getStyle("CheckBox").getVector4f("resizeBorders"),
			screen.getStyle("CheckBox").getString("defaultImg")
		);
	}
	
	/**
	 * Creates a new instance of the CheckBox control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param position A Vector2f containing the x/y position of the Element
	 */
	public CheckBox(ElementManager screen, Vector2f position) {
		this(screen, UIDUtil.getUID(), position,
			screen.getStyle("CheckBox").getVector2f("defaultSize"),
			screen.getStyle("CheckBox").getVector4f("resizeBorders"),
			screen.getStyle("CheckBox").getString("defaultImg")
		);
	}
	
	/**
	 * Creates a new instance of the CheckBox control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param UID A unique String identifier for the Element
	 * @param position A Vector2f containing the x/y position of the Element
	 * @param dimensions A Vector2f containing the width/height dimensions of the Element
	 */
	public CheckBox(ElementManager screen, Vector2f position, Vector2f dimensions) {
		this(screen, UIDUtil.getUID(), position, dimensions,
			screen.getStyle("CheckBox").getVector4f("resizeBorders"),
			screen.getStyle("CheckBox").getString("defaultImg")
		);
	}
	
	/**
	 * Creates a new instance of the CheckBox control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param UID A unique String identifier for the Element
	 * @param position A Vector2f containing the x/y position of the Element
	 * @param dimensions A Vector2f containing the width/height dimensions of the Element
	 * @param resizeBorders A Vector4f containing the border information used when resizing the default image (x = N, y = W, z = E, w = S)
	 * @param defaultImg The default image to use for the CheckBox
	 */
	public CheckBox(ElementManager screen, Vector2f position, Vector2f dimensions, Vector4f resizeBorders, String defaultImg) {
		this(screen, UIDUtil.getUID(), position, dimensions,resizeBorders,defaultImg);
	}
	
	/**
	 * Creates a new instance of the CheckBox control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param UID A unique String identifier for the Element
	 * @param position A Vector2f containing the x/y position of the Element
	 */
	public CheckBox(ElementManager screen, String UID, Vector2f position) {
		this(screen, UID, position,
			screen.getStyle("CheckBox").getVector2f("defaultSize"),
			screen.getStyle("CheckBox").getVector4f("resizeBorders"),
			screen.getStyle("CheckBox").getString("defaultImg")
		);
	}
	
	/**
	 * Creates a new instance of the CheckBox control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param UID A unique String identifier for the Element
	 * @param position A Vector2f containing the x/y position of the Element
	 * @param dimensions A Vector2f containing the width/height dimensions of the Element
	 */
	public CheckBox(ElementManager screen, String UID, Vector2f position, Vector2f dimensions) {
		this(screen, UID, position, dimensions,
			screen.getStyle("CheckBox").getVector4f("resizeBorders"),
			screen.getStyle("CheckBox").getString("defaultImg")
		);
	}
	
	/**
	 * Creates a new instance of the CheckBox control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param UID A unique String identifier for the Element
	 * @param position A Vector2f containing the x/y position of the Element
	 * @param dimensions A Vector2f containing the width/height dimensions of the Element
	 * @param resizeBorders A Vector4f containing the border information used when resizing the default image (x = N, y = W, z = E, w = S)
	 * @param defaultImg The default image to use for the CheckBox
	 */
	public CheckBox(ElementManager screen, String UID, Vector2f position, Vector2f dimensions, Vector4f resizeBorders, String defaultImg) {
		super(screen, UID, position, dimensions, resizeBorders, defaultImg);
		
		this.clearAltImages();
		this.removeEffect(Effect.EffectEvent.Hover);
		this.removeEffect(Effect.EffectEvent.Press);
		this.removeEffect(Effect.EffectEvent.LoseFocus);
		
		labelFontSize = screen.getStyle("CheckBox").getFloat("fontSize");
		
		label = new ClickableLabel(
			this,
			screen,
			UID + ":Label",
			new Vector2f(getWidth(), 0),
			new Vector2f(100,getHeight())
		);
		label.setDocking(Docking.SW);
		label.setScaleEW(true);
		label.setScaleNS(false);
		label.setIgnoreMouse(false);
		
		label.setFontColor(screen.getStyle("CheckBox").getColorRGBA("fontColor"));
		label.setFontSize(labelFontSize);
		label.setTextAlign(BitmapFont.Align.valueOf(screen.getStyle("CheckBox").getString("textAlign")));
		label.setTextVAlign(BitmapFont.VAlign.valueOf(screen.getStyle("CheckBox").getString("textVAlign")));
		label.setTextWrap(LineWrapMode.valueOf(screen.getStyle("CheckBox").getString("textWrap")));
		
		this.setIsToggleButton(true);
		
		if (screen.getStyle("CheckBox").getString("hoverImg") != null) {
			setButtonHoverInfo(
				screen.getStyle("CheckBox").getString("hoverImg"),
				screen.getStyle("CheckBox").getColorRGBA("hoverColor")
			);
		}
		if (screen.getStyle("CheckBox").getString("pressedImg") != null) {
			setButtonPressedInfo(
				screen.getStyle("CheckBox").getString("pressedImg"),
				screen.getStyle("CheckBox").getColorRGBA("pressedColor")
			);
		}
		
		populateEffects("CheckBox");
		if (Screen.isAndroid()) {
			removeEffect(Effect.EffectEvent.Hover);
			removeEffect(Effect.EffectEvent.TabFocus);
			removeEffect(Effect.EffectEvent.LoseTabFocus);
		}
	}
	
	/**
	 * Sets text for the check Label
	 * @param text 
	 */
	public void setLabelText(String text) {
		if (label.getParent() != null) {
			elementChildren.remove(label.getUID());
			label.removeFromParent();
		}
		
		float width = BitmapTextUtil.getTextWidth(this, text);
		float height = BitmapTextUtil.getTextLineHeight(this, text);
		float nextY = height-getHeight();
		nextY /= 2;
		nextY = (float) Math.ceil(nextY+1);
		
		label.setWidth(width+getWidth()+4);
		label.setX(getWidth()+4);
		label.setY(-nextY);
		label.setDocking(Docking.SW);
		label.setScaleEW(true);
		label.setScaleNS(false);
		label.setFontColor(screen.getStyle("CheckBox").getColorRGBA("fontColor"));
		label.setFontSize(labelFontSize);
		label.setTextAlign(BitmapFont.Align.valueOf(screen.getStyle("CheckBox").getString("textAlign")));
		label.setTextVAlign(BitmapFont.VAlign.valueOf(screen.getStyle("CheckBox").getString("textVAlign")));
		label.setTextWrap(LineWrapMode.valueOf(screen.getStyle("CheckBox").getString("textWrap")));
		
		label.setText(text);
		
		addChild(label);
	}
	
	/**
	 * Checks/unchecks the checkbox
	 * @param isChecked 
	 */
	public void setIsChecked(boolean isChecked) {
		setIsToggled(isChecked);
	}
	
	public void setIsCheckedNoCallback(boolean isChecked) {
		setIsToggledNoCallback(isChecked);
	}
	
	public Label getLabel() {
		return this.label;
	}
	
	/**
	 * Returns if the checkbox is checked/unchecked
	 * @return boolean
	 */
	public boolean getIsChecked() {
		return this.getIsToggled();
	}
	
	public class ClickableLabel extends Label implements MouseButtonListener, MouseFocusListener {
		Button owner;
		
		public ClickableLabel(Button owner, ElementManager screen, String UID, Vector2f position, Vector2f dimensions) {
			super(screen, UID, position, dimensions);
			this.owner = owner;
		}
		
		@Override
		public void onMouseLeftPressed(MouseButtonEvent evt) {
			MouseButtonEvent nEvt = new MouseButtonEvent(
				0,
				true,
				(int)owner.getAbsoluteX(),
				(int)owner.getAbsoluteY()
			);
			owner.onMouseLeftPressed(nEvt);
		}

		@Override
		public void onMouseLeftReleased(MouseButtonEvent evt) {
			MouseButtonEvent nEvt = new MouseButtonEvent(
				0,
				false,
				(int)owner.getAbsoluteX(),
				(int)owner.getAbsoluteY()
			);
			owner.onMouseLeftReleased(nEvt);
		}

		@Override
		public void onMouseRightPressed(MouseButtonEvent evt) {
			MouseButtonEvent nEvt = new MouseButtonEvent(
				1,
				true,
				(int)owner.getAbsoluteX(),
				(int)owner.getAbsoluteY()
			);
			owner.onMouseRightPressed(nEvt);
		}

		@Override
		public void onMouseRightReleased(MouseButtonEvent evt) {
			MouseButtonEvent nEvt = new MouseButtonEvent(
				1,
				false,
				(int)owner.getAbsoluteX(),
				(int)owner.getAbsoluteY()
			);
			owner.onMouseRightReleased(nEvt);
		}

		@Override
		public void onGetFocus(MouseMotionEvent evt) {
			MouseMotionEvent nEvt = new MouseMotionEvent(
				(int)owner.getAbsoluteX(),
				(int)owner.getAbsoluteY(),
				0,0,
				0,0
			);
			owner.onGetFocus(nEvt);
		}

		@Override
		public void onLoseFocus(MouseMotionEvent evt) {
			MouseMotionEvent nEvt = new MouseMotionEvent(
				Screen.isAndroid() ? (int)screen.getTouchXY().x : (int)screen.getMouseXY().x,
				Screen.isAndroid() ? (int)screen.getTouchXY().y : (int)screen.getMouseXY().y,
				0,0,
				0,0
			);
			owner.onLoseFocus(nEvt);
		}
	}
}
 public class RadioButton extends CheckBox {
	
	/**
	 * Creates a new instance of the RadioButton control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param position A Vector2f containing the x/y position of the Element
	 */
	public RadioButton(ElementManager screen) {
		this(screen, UIDUtil.getUID(), Vector2f.ZERO,
			screen.getStyle("RadioButton").getVector2f("defaultSize"),
			screen.getStyle("RadioButton").getVector4f("resizeBorders"),
			screen.getStyle("RadioButton").getString("defaultImg")
		);
	}
	
	/**
	 * Creates a new instance of the RadioButton control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param position A Vector2f containing the x/y position of the Element
	 */
	public RadioButton(ElementManager screen, Vector2f position) {
		this(screen, UIDUtil.getUID(), position,
			screen.getStyle("RadioButton").getVector2f("defaultSize"),
			screen.getStyle("RadioButton").getVector4f("resizeBorders"),
			screen.getStyle("RadioButton").getString("defaultImg")
		);
	}
	
	/**
	 * Creates a new instance of the RadioButton control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param position A Vector2f containing the x/y position of the Element
	 * @param dimensions A Vector2f containing the width/height dimensions of the Element
	 */
	public RadioButton(ElementManager screen, Vector2f position, Vector2f dimensions) {
		this(screen, UIDUtil.getUID(), position, dimensions,
			screen.getStyle("RadioButton").getVector4f("resizeBorders"),
			screen.getStyle("RadioButton").getString("defaultImg")
		);
	}
	
	/**
	 * Creates a new instance of the RadioButton control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param position A Vector2f containing the x/y position of the Element
	 * @param dimensions A Vector2f containing the width/height dimensions of the Element
	 * @param resizeBorders A Vector4f containing the border information used when resizing the default image (x = N, y = W, z = E, w = S)
	 * @param defaultImg The default image to use for the RadioButton
	 */
	public RadioButton(ElementManager screen, Vector2f position, Vector2f dimensions, Vector4f resizeBorders, String defaultImg) {
		this(screen, UIDUtil.getUID(), position, dimensions,resizeBorders,defaultImg);
	}
	
	/**
	 * Creates a new instance of the RadioButton control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param UID A unique String identifier for the Element
	 * @param position A Vector2f containing the x/y position of the Element
	 */
	public RadioButton(ElementManager screen, String UID, Vector2f position) {
		this(screen, UID, position,
			screen.getStyle("RadioButton").getVector2f("defaultSize"),
			screen.getStyle("RadioButton").getVector4f("resizeBorders"),
			screen.getStyle("RadioButton").getString("defaultImg")
		);
	}
	
	/**
	 * Creates a new instance of the RadioButton control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param UID A unique String identifier for the Element
	 * @param position A Vector2f containing the x/y position of the Element
	 * @param dimensions A Vector2f containing the width/height dimensions of the Element
	 */
	public RadioButton(ElementManager screen, String UID, Vector2f position, Vector2f dimensions) {
		this(screen, UID, position, dimensions,
			screen.getStyle("RadioButton").getVector4f("resizeBorders"),
			screen.getStyle("RadioButton").getString("defaultImg")
		);
	}
	
	/**
	 * Creates a new instance of the RadioButton control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param UID A unique String identifier for the Element
	 * @param position A Vector2f containing the x/y position of the Element
	 * @param dimensions A Vector2f containing the width/height dimensions of the Element
	 * @param resizeBorders A Vector4f containing the border information used when resizing the default image (x = N, y = W, z = E, w = S)
	 * @param defaultImg The default image to use for the RadioButton
	 */
	public RadioButton(ElementManager screen, String UID, Vector2f position, Vector2f dimensions, Vector4f resizeBorders, String defaultImg) {
		super(screen, UID, position, dimensions, resizeBorders, defaultImg);
		
		this.clearAltImages();
		this.removeEffect(Effect.EffectEvent.Hover);
		this.removeEffect(Effect.EffectEvent.Press);
		this.removeEffect(Effect.EffectEvent.LoseFocus);
		
		setIsRadioButton(true);
		
		if (screen.getStyle("RadioButton").getString("hoverImg") != null) {
			setButtonHoverInfo(
				screen.getStyle("RadioButton").getString("hoverImg"),
				screen.getStyle("RadioButton").getColorRGBA("hoverColor")
			);
		}
		if (screen.getStyle("RadioButton").getString("pressedImg") != null) {
			setButtonPressedInfo(
				screen.getStyle("RadioButton").getString("pressedImg"),
				screen.getStyle("RadioButton").getColorRGBA("pressedColor")
			);
		}
		
		populateEffects("RadioButton");
		if (Screen.isAndroid()) {
			removeEffect(Effect.EffectEvent.Hover);
			removeEffect(Effect.EffectEvent.TabFocus);
			removeEffect(Effect.EffectEvent.LoseTabFocus);
		}
	}
}
 public abstract class Indicator extends Element {
	private float maxValue = 0, currentValue = 0, percentage = 0;
	private Orientation orientation;
	private ColorRGBA indicatorColor;
	private String alphaMapPath;
	private String overlayImg;
	private Element elIndicator, elOverlay;
	private boolean displayValues = false, displayPercentages = false;
	private Vector2f indDimensions = new Vector2f();
	private Vector4f indPadding = Vector4f.ZERO.clone();
	private Vector4f clipTest = Vector4f.ZERO.clone();
	private boolean reverseDirection = false;
	private ClippingDefine def = null;
	
	/**
	 * Creates a new instance of the Indicator control
	 * 
	 * @param screen The screen control the Element is to be added to
	 */
	public Indicator(ElementManager screen, Orientation orientation) {
		this(screen, UIDUtil.getUID(), Vector2f.ZERO,
			screen.getStyle("Indicator").getVector2f("defaultSize"),
			screen.getStyle("Indicator").getVector4f("resizeBorders"),
			screen.getStyle("Indicator").getString("defaultImg"),
			orientation, true
		);
	}
	
	/**
	 * Creates a new instance of the Indicator control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param position A Vector2f containing the x/y position of the Element
	 */
	public Indicator(ElementManager screen, Vector2f position, Orientation orientation) {
		this(screen, UIDUtil.getUID(), position,
			screen.getStyle("Indicator").getVector2f("defaultSize"),
			screen.getStyle("Indicator").getVector4f("resizeBorders"),
			screen.getStyle("Indicator").getString("defaultImg"),
			orientation, true
		);
	}
	
	/**
	 * Creates a new instance of the Indicator control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param position A Vector2f containing the x/y position of the Element
	 * @param dimensions A Vector2f containing the width/height dimensions of the Element
	 */
	public Indicator(ElementManager screen, Vector2f position, Vector2f dimensions, Orientation orientation, boolean useOverlay) {
		this(screen, UIDUtil.getUID(), position, dimensions,
			screen.getStyle("Indicator").getVector4f("resizeBorders"),
			screen.getStyle("Indicator").getString("defaultImg"),
			orientation, useOverlay
		);
	}
	
	/**
	 * Creates a new instance of the Indicator control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param position A Vector2f containing the x/y position of the Element
	 * @param dimensions A Vector2f containing the width/height dimensions of the Element
	 * @param resizeBorders A Vector4f containing the border information used when resizing the default image (x = N, y = W, z = E, w = S)
	 * @param defaultImg The default image to use for the Element
	 */
	public Indicator(ElementManager screen, Vector2f position, Vector2f dimensions, Vector4f resizeBorders, String defaultImg, Orientation orientation) {
		this(screen, UIDUtil.getUID(), position, dimensions, resizeBorders, defaultImg, orientation, true);
	}
	
	/**
	 * Creates a new instance of the Indicator control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param UID A unique String identifier for the Element
	 * @param position A Vector2f containing the x/y position of the Element
	 */
	public Indicator(ElementManager screen, String UID, Vector2f position, Orientation orientation) {
		this(screen, UID, position,
			screen.getStyle("Indicator").getVector2f("defaultSize"),
			screen.getStyle("Indicator").getVector4f("resizeBorders"),
			screen.getStyle("Indicator").getString("defaultImg"),
			orientation, true
		);
	}
	
	/**
	 * Creates a new instance of the Indicator control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param UID A unique String identifier for the Element
	 * @param position A Vector2f containing the x/y position of the Element
	 * @param dimensions A Vector2f containing the width/height dimensions of the Element
	 */
	public Indicator(ElementManager screen, String UID, Vector2f position, Vector2f dimensions, Orientation orientation) {
		this(screen, UID, position, dimensions,
			screen.getStyle("Indicator").getVector4f("resizeBorders"),
			screen.getStyle("Indicator").getString("defaultImg"),
			orientation, true
		);
	}
	
	/**
	 * Creates a new instance of the Indicator control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param UID A unique String identifier for the Element
	 * @param position A Vector2f containing the x/y position of the Element
	 * @param dimensions A Vector2f containing the width/height dimensions of the Element
	 * @param resizeBorders A Vector4f containing the border information used when resizing the default image (x = N, y = W, z = E, w = S)
	 * @param defaultImg The default image to use for the Indicator
	 */
	public Indicator(ElementManager screen, String UID, Vector2f position, Vector2f dimensions, Vector4f resizeBorders, String defaultImg, Orientation orientation, boolean useOverlay) {
		super(screen, UID, position, dimensions, resizeBorders, null);
		
		indDimensions.set(dimensions);
		
		setScaleEW(true);
		setScaleNS(false);
		
		this.overlayImg = defaultImg;
		this.orientation = orientation;
		
		elIndicator = new Element(
			screen,
			UID + ":Indicator",
			Vector2f.ZERO,
			dimensions,
			resizeBorders,
			null
		) {
			@Override
			public void updateLocalClippingLayer() {
				Indicator ind = ((Indicator)this.getElementParent());
				if (def == null) def = elIndicator.getClippingDefine(elIndicator);
				
				if (getIsVisible()) {
					float clipX = 0, clipY = 0, clipW = 0, clipH = 0;
					if (ind.getOrientation() == Indicator.Orientation.HORIZONTAL) {
						if (reverseDirection) {
							clipX = def.getElement().getWidth()-ind.getCurrentPercentage();
							clipW = def.getElement().getWidth();
						} else {
							clipW = def.getElement().getWidth()-(def.getElement().getWidth()-ind.getCurrentPercentage());
						}
						clipH = def.getElement().getHeight();
					} else {
						if (reverseDirection) {
							clipY = def.getElement().getHeight()-ind.getCurrentPercentage();
							clipH = def.getElement().getHeight();
						} else {
							clipH = def.getElement().getHeight()-(def.getElement().getHeight()-ind.getCurrentPercentage());
						}
						clipW = def.getElement().getWidth();
					}
					def.clip.set(clipX, clipY, clipW, clipH);
					super.updateLocalClippingLayer();
					
				}
			}
		};
		elIndicator.addClippingLayer(elIndicator, new Vector4f(0,0,elIndicator.getWidth(),elIndicator.getHeight()));
		elIndicator.setIgnoreMouse(true);
		elIndicator.setDocking(Docking.SW);
		elIndicator.setScaleEW(true);
		elIndicator.setScaleNS(false);
		addChild(elIndicator);
		
                    elOverlay = new Element(
                            screen,
                            UID + ":Overlay",
                            Vector2f.ZERO,
                            dimensions,
                            resizeBorders,
                            overlayImg
                    );
                    elOverlay.setIgnoreMouse(true);
                    elOverlay.setDocking(Docking.SW);
                    elOverlay.setScaleEW(true);
                    elOverlay.setScaleNS(false);

                    // Load default font info
                    elOverlay.setFontColor(screen.getStyle("Indicator").getColorRGBA("fontColor"));
                    elOverlay.setFontSize(screen.getStyle("Indicator").getFloat("fontSize"));
                    elOverlay.setTextAlign(BitmapFont.Align.valueOf(screen.getStyle("Indicator").getString("textAlign")));
                    elOverlay.setTextVAlign(BitmapFont.VAlign.valueOf(screen.getStyle("Indicator").getString("textVAlign")));
                    elOverlay.setTextWrap(LineWrapMode.valueOf(screen.getStyle("Indicator").getString("textWrap")));
                    elOverlay.setTextClipPaddingByKey("Indicator","textPadding");
                    elOverlay.setTextPaddingByKey("Indicator","textPadding");

                if(useOverlay) {
                    addChild(elOverlay);
                }
		
	}
	
	/**
	 * Use this method in place of setScaleEW and setScaleNE
	 * @param scaleNS
	 * @param scaleEW 
	 */
	public void setScaling(boolean scaleNS, boolean scaleEW) {
		setScaleNS(scaleNS);
		elIndicator.setScaleNS(scaleNS);
		elOverlay.setScaleNS(scaleNS);
		setScaleEW(scaleEW);
		elIndicator.setScaleEW(scaleEW);
		elOverlay.setScaleEW(scaleEW);
	}
	
	@Override
	public void controlResizeHook() {
		setIndicatorPadding(indPadding);
		refactorIndicator();
	}
	
	/**
	 * Returns the Indicator.Orientation of the Indicator instance
	 * @return Indicator.Orientation
	 */
	public Orientation getOrientation() {
		return this.orientation;
	}
	
	/**
	 * Sets the ColorRGBA value of the Indicator
	 * @param indicatorColor 
	 */
	public void setIndicatorColor(ColorRGBA indicatorColor) {
		this.indicatorColor = indicatorColor;
		elIndicator.getElementMaterial().setColor("Color", this.indicatorColor);
	}
	
	/**
	 * Set the maximum value (e.g. float  = 100%)
	 * @param maxValue 
	 */
	public void setMaxValue(float maxValue) {
		this.maxValue = maxValue;
		refactorIndicator();
	}
	
	/**
	 * Returns the maximum value set for the Indicator
	 * @return maxValue
	 */
	public float getMaxValue() {
		return this.maxValue;
	}
	
	/**
	 * Sets the current value of the Indicator
	 * @param currentValue 
	 */
	public void setCurrentValue(float currentValue) {
		this.currentValue = currentValue;
		refactorIndicator();
	}
	
	public void setReverseDirection(boolean reverseDirection) {
		this.reverseDirection = reverseDirection;
	}
	
	/**
	 * Returns the current value of the Indicator
	 * @return currentValue
	 */
	public float getCurrentValue() {
		return this.currentValue;
	}
	
	private void refactorIndicator() {
		if (currentValue > maxValue) {
			currentValue = maxValue;
		} else if (currentValue < 0f) {
			currentValue = 0f;
		}
		percentage = currentValue/maxValue;
		if (orientation == Orientation.HORIZONTAL) {
			percentage *= indDimensions.x;
		} else {
			percentage *= indDimensions.y;
		}
		elIndicator.updateLocalClippingLayer();
		
		if (this.displayValues) {
			elOverlay.setText(String.valueOf((int)this.currentValue) + "/" + String.valueOf((int)this.maxValue));
		} else if (this.displayPercentages) {
			elOverlay.setText(String.valueOf((int)((this.currentValue/this.maxValue)*100f)) + "%");
		} else {
		//	elOverlay.setText("");
		}
		
		onChange(currentValue, currentValue/maxValue*100f);
	}
	
	/**
	 * Returns current value as a percent of the max value
	 * @return percentage
	 */
	public float getCurrentPercentage() {
		return this.percentage;
	}
	
	/**
	 * Use setAlphaMap instead
	 * @param alphaMapPath 
	 */
	@Deprecated
	public void setIndicatorAlphaMap(String alphaMapPath) {
		this.alphaMapPath = alphaMapPath;
		elIndicator.setAlphaMap(this.alphaMapPath);
	}
	
	/**
	 * Applies an alpha map to the indicator, allowing for curved shapes
	 * @param alphaMapPath 
	 */
	@Override
	public void setAlphaMap(String alphaMapPath) {
		this.alphaMapPath = alphaMapPath;
		elIndicator.setAlphaMap(this.alphaMapPath);
	}
	
	public Element getIndicatorElement() {
		return elIndicator;
	}
	
	public Element getOverlayElement() {
		return elOverlay;
	}
	
	/**
	 * Return the element used for displaying overlay text
	 * @return elOverlay
	 */
	public Element getTextDisplayElement() {
		return this.elOverlay;
	}
	
	/**
	 * Sets the display text to format as currentValue / maxValue
	 */
	public void setDisplayValues() {
		this.displayPercentages = false;
		this.displayValues = true;
	}
	
	/**
	 * Sets the display text to current value as percent %
	 */
	public void setDisplayPercentage() {
		this.displayPercentages = true;
		this.displayValues = false;
	}
	
	/**
	 * Hides the overlay display text
	 */
	public void setHideText() {
		this.displayPercentages = false;
		this.displayValues = false;
	}
	
	/**
	 * Set the image to use behind the indicator
	 * @param imgPath 
	 */
	public void setBaseImage(String imgPath) {
		setColorMap(imgPath);
	}
	
	/**
	 * Set the image to use in front of the indicator
	 * @param imgPath
	 */
	public void setOverlayImage(String imgPath) {
		elOverlay.setColorMap(imgPath);
	}
	
	public void setIndicatorImage(String imgPath) {
		elIndicator.setColorMap(imgPath);
	}
	
	public void setIndicatorPadding(Vector4f padding) {
		indPadding.set(padding);
		indDimensions.set(getWidth()-(padding.x+padding.z),getHeight()-(padding.y+padding.w));
		elIndicator.setPosition(padding.x,padding.y);
		elIndicator.setDimensions(indDimensions);
	}
	
	public abstract void onChange(float currentValue, float currentPercentage);
}
 public abstract class ComboBox extends TextField {
	protected ButtonAdapter btnArrowDown;
	private Menu DDList = null;
	float btnHeight;
	String ddUID;
	private int selectedIndex = -1;
	private Object selectedValue;
	private String selectedCaption;
	
	private int hlIndex;
	private Object hlValue;
	private String hlCaption;
	
	private int ssIndex;
	private Object ssValue;
	private String ssCaption;
	
	private boolean DDListIsShowing = false;
	
	protected boolean selectEnabled = true;
	
	/**
	 * Creates a new instance of the ComboBox control
	 * 
	 * @param screen The screen control the Element is to be added to
	 */
	public ComboBox(ElementManager screen) {
		this(screen, UIDUtil.getUID(), Vector2f.ZERO,
			screen.getStyle("TextField").getVector2f("defaultSize"),
			screen.getStyle("TextField").getVector4f("resizeBorders"),
			screen.getStyle("TextField").getString("defaultImg")
		);
	}
	
	/**
	 * Creates a new instance of the ComboBox control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param position A Vector2f containing the x/y position of the Element
	 */
	public ComboBox(ElementManager screen, Vector2f position) {
		this(screen, UIDUtil.getUID(), position,
			screen.getStyle("TextField").getVector2f("defaultSize"),
			screen.getStyle("TextField").getVector4f("resizeBorders"),
			screen.getStyle("TextField").getString("defaultImg")
		);
	}
	
	/**
	 * Creates a new instance of the ComboBox control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param position A Vector2f containing the x/y position of the Element
	 * @param dimensions A Vector2f containing the width/height dimensions of the Element
	 */
	public ComboBox(ElementManager screen, Vector2f position, Vector2f dimensions) {
		this(screen, UIDUtil.getUID(), position, dimensions,
			screen.getStyle("TextField").getVector4f("resizeBorders"),
			screen.getStyle("TextField").getString("defaultImg")
		);
	}
	
	/**
	 * Creates a new instance of the ComboBox control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param position A Vector2f containing the x/y position of the Element
	 * @param dimensions A Vector2f containing the width/height dimensions of the Element
	 * @param resizeBorders A Vector4f containing the border information used when resizing the default image (x = N, y = W, z = E, w = S)
	 * @param defaultImg The default image to use for the ComboBox
	 */
	public ComboBox(ElementManager screen, Vector2f position, Vector2f dimensions, Vector4f resizeBorders, String defaultImg) {
		this(screen, UIDUtil.getUID(), position, dimensions, resizeBorders, defaultImg);
	}
	
	/**
	 * Creates a new instance of the ComboBox control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param UID A unique String identifier for the Element
	 * @param position A Vector2f containing the x/y position of the Element
	 */
	public ComboBox(ElementManager screen, String UID, Vector2f position) {
		this(screen, UID, position,
			screen.getStyle("TextField").getVector2f("defaultSize"),
			screen.getStyle("TextField").getVector4f("resizeBorders"),
			screen.getStyle("TextField").getString("defaultImg")
		);
	}
	
	/**
	 * Creates a new instance of the ComboBox control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param UID A unique String identifier for the Element
	 * @param position A Vector2f containing the x/y position of the Element
	 * @param dimensions A Vector2f containing the width/height dimensions of the Element
	 */
	public ComboBox(ElementManager screen, String UID, Vector2f position, Vector2f dimensions) {
		this(screen, UID, position, dimensions,
			screen.getStyle("TextField").getVector4f("resizeBorders"),
			screen.getStyle("TextField").getString("defaultImg")
		);
	}
	
	/**
	 * Creates a new instance of the ComboBox control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param UID A unique String identifier for the Element
	 * @param position A Vector2f containing the x/y position of the Element
	 * @param dimensions A Vector2f containing the width/height dimensions of the Element
	 * @param resizeBorders A Vector4f containing the border information used when resizing the default image (x = N, y = W, z = E, w = S)
	 * @param defaultImg The default image to use for the ComboBox
	 */
	public ComboBox(ElementManager screen, String UID, Vector2f position, Vector2f dimensions, Vector4f resizeBorders, String defaultImg) {
		super(screen, UID, position, dimensions, resizeBorders, defaultImg);
		
		setScaleNS(false);
		setScaleEW(false);
		
		ddUID = UID + ":ddMenu";
		
		btnHeight = getHeight();
		
		setWidth(getWidth()-btnHeight);
		
	//	layoutHints.setElementPadX(btnHeight);
		
		btnArrowDown = new ButtonAdapter(screen, UID + ":ArrowDown",
			new Vector2f(
				getWidth(),
				0
			),
			new Vector2f(
				btnHeight,
				btnHeight
			)
		) {
			@Override
			public void onButtonMouseLeftUp(MouseButtonEvent evt, boolean toggled) {
				if (validateListSize()) {
					if (screen.getElementById(DDList.getUID()) == null)
						screen.addElement(DDList);
					if (!DDList.getIsVisible()) {
						DDList.showMenu(
							(Menu)null,
							getElementParent().getAbsoluteX(),
							getElementParent().getAbsoluteY()-DDList.getHeight()
						);
					} else {
						DDList.hide();
					}
				}
				screen.setTabFocusElement((ComboBox)getElementParent());
			}
		};
		btnArrowDown.setButtonIcon(18, 18, screen.getStyle("Common").getString("arrowDown"));
		btnArrowDown.setDockS(true);
		btnArrowDown.setDockW(true);
		this.addChild(btnArrowDown);
	}
	
	/**
	 * Adds a new list item to the drop-down list associated with this control
	 * 
	 * @param caption The String to display as the list item
	 * @param value A String value to associate with this list item
	 */
	public void addListItem(String caption, Object value) {
		if (DDList == null) {
			DDList = new Menu(screen, ddUID, new Vector2f(0,0), true) {
				@Override
				public void onMenuItemClicked(int index, Object value, boolean isToggled) {
					((ComboBox)getCallerElement()).setSelectedWithCallback(index, DDList.getMenuItem(index).getCaption(), value);
					screen.setTabFocusElement(((ComboBox)getCallerElement()));
					hide();
				}
			};
			DDList.setCallerElement(this);
			DDList.setPreferredSize(new Vector2f(getWidth()+btnHeight,DDList.getMenuItemHeight()*5));
		}
		DDList.setFontSize(fontSize);
		DDList.getScrollableArea().setFontSize(fontSize);
		DDList.addMenuItem(caption, value, null);
		
		if (screen.getElementById(DDList.getUID()) == null) {
			screen.addElement(DDList);
		}
		pack();
	//	refreshSelectedIndex();
	}
	
	/**
	 * Inserts a new List Item at the specified index
	 * @param index - List index to insert new List Item
	 * @param caption - Caption for new List Item
	 * @param value - Object to store as value
	 */
	public void insertListItem(int index, String caption, Object value) {
		if (DDList != null) {
			DDList.insertMenuItem(index, caption, value, null);
			pack();
			refreshSelectedIndex();
		}
	}
	
	/**
	 * Removes the List Item at the specified index
	 * @param index 
	 */
	public void removeListItem(int index) {
		if (DDList != null) {
			DDList.removeMenuItem(index);
			pack();
			refreshSelectedIndex();
		}
	}
	
	/**
	 * Removes the first instance of a list item with the specified caption
	 * @param caption 
	 */
	public void removeListItem(String caption) {
		if (DDList != null) {
			DDList.removeMenuItem(caption);
		}
	}
	
	/**
	 * Removes the first instance of a list item with the specified value
	 * @param value 
	 */
	public void removeListItem(Object value) {
		if (DDList != null) {
			DDList.removeMenuItem(value);
		}
	}
	
	/**
	 * Removes all list items
	 */
	public void removeAllListItems() {
		if (DDList != null) {
			DDList.removeAllMenuItems();
		}
	}
	
	private void refreshSelectedIndex() {
		if (DDList != null) {
			if (selectedIndex > DDList.getMenuItems().size()-1)
				this.setSelectedIndexWithCallback(DDList.getMenuItems().size()-1);
		//	if (!DDList.getMenuItems().isEmpty())
		//		this.setSelectedIndex(selectedIndex);
		//	else
			if (DDList.getMenuItems().isEmpty())
				setText("");
		} else {
			setText("");
		}
	}
	
	/**
	 * Method needs to be called once last list item has been added.  This eventually
	 * will be updated to automatically be called when a new item is added to, instert into
	 * the list or an item is removed from the list.
	 */
	public void pack() {
		if (selectedIndex == -1) {
			setSelectedIndexWithCallback(0);
		}
		int rIndex = DDList.getMenuItems().size()-selectedIndex;
		float diff = rIndex * DDList.getMenuItemHeight() + (DDList.getMenuPadding()*2);

		DDList.scrollThumbYTo(
			( DDList.getHeight()-diff )
		);
		refreshSelectedIndex();
	}
	
	/**
	 * Returns false if list is empty, true if list contains List Items
	 * @return boolean
	 */
	public boolean validateListSize() {
		if (DDList == null)
			return false;
		else if (DDList.getMenuItems().isEmpty())
			return false;
		else
			return true;
	}
	
	public void setSelectedByCaption(String caption, boolean useCallback) {
		MenuItem mItem = null;
		for (MenuItem mi : DDList.getMenuItems()) {
			if (mi.getCaption().equals(caption)) {
				mItem = mi;
				break;
			}
		}
		
		if (mItem != null) {
			if (useCallback)	setSelectedIndexWithCallback(DDList.getMenuItems().indexOf(mItem));
			else				setSelectedIndex(DDList.getMenuItems().indexOf(mItem));
		}
	}
	
	public void setSelectedByValue(Object value, boolean useCallback) {
		MenuItem mItem = null;
		for (MenuItem mi : DDList.getMenuItems()) {
			if (mi.getValue().equals(value)) {
				mItem = mi;
				break;
			}
		}
		
		if (mItem != null) {
			if (useCallback)	setSelectedIndexWithCallback(DDList.getMenuItems().indexOf(mItem));
			else				setSelectedIndex(DDList.getMenuItems().indexOf(mItem));
		}
	}
	
	/**
	 * Selects the List Item at the specified index and call the onChange event
	 * @param selectedIndex 
	 */
	public void setSelectedIndexWithCallback(int selectedIndex) {
		if (validateListSize()) {
			if (selectedIndex < 0)
				selectedIndex = 0;
			else if (selectedIndex > DDList.getMenuItems().size()-1)
				selectedIndex = DDList.getMenuItems().size()-1;
		
			MenuItem mi = DDList.getMenuItem(selectedIndex);
			String caption = mi.getCaption();
			Object value = mi.getValue();
			setSelectedWithCallback(selectedIndex, caption, value);
		}
	}
	
	protected void setSelectedWithCallback(int index, String caption, Object value) {
		this.hlIndex = index;
		this.selectedIndex = index;
		this.selectedCaption = caption;
		this.selectedValue = value;
		setText(selectedCaption);
		
		int rIndex = DDList.getMenuItems().size()-index;
		float diff = rIndex * DDList.getMenuItemHeight() + (DDList.getMenuPadding()*2);

		DDList.scrollThumbYTo(
			( DDList.getHeight()-diff )
		);
		
		onChange(selectedIndex, selectedValue);
	}
	
	/**
	 * Selects the List Item at the specified index
	 * @param selectedIndex 
	 */
	public void setSelectedIndex(int selectedIndex) {
		if (validateListSize()) {
			if (selectedIndex < 0)
				selectedIndex = 0;
			else if (selectedIndex > DDList.getMenuItems().size()-1)
				selectedIndex = DDList.getMenuItems().size()-1;
		
			MenuItem mi = DDList.getMenuItem(selectedIndex);
			String caption = mi.getCaption();
			Object value = mi.getValue();
			setSelected(selectedIndex, caption, value);
		}
	}
	
	protected void setSelected(int index, String caption, Object value) {
		this.hlIndex = index;
		this.selectedIndex = index;
		this.selectedCaption = caption;
		this.selectedValue = value;
		setText(selectedCaption);
		
		int rIndex = DDList.getMenuItems().size()-index;
		float diff = rIndex * DDList.getMenuItemHeight() + (DDList.getMenuPadding()*2);

		DDList.scrollThumbYTo(
			( DDList.getHeight()-diff )
		);
		
	//	onChange(selectedIndex, selectedValue);
	}
	
	/**
	 * Hides the ComboBox drop-down list
	 */
	public void hideDropDownList() {
		this.DDList.hideMenu();
	}
	
	@Override
	public void controlKeyPressHook(KeyInputEvent evt, String text) {
		if (validateListSize()) {
			if (evt.getKeyCode() != KeyInput.KEY_UP && evt.getKeyCode() != KeyInput.KEY_DOWN && evt.getKeyCode() != KeyInput.KEY_RETURN) {
				int miIndexOf = 0;
				int strIndex = -1;
				for (MenuItem mi : DDList.getMenuItems()) {
					strIndex = mi.getCaption().toLowerCase().indexOf(text.toLowerCase());
					if (strIndex == 0) {
						ssIndex = miIndexOf;
						hlIndex = ssIndex;
						hlCaption = ssCaption = DDList.getMenuItem(miIndexOf).getCaption();
						hlValue = ssValue = DDList.getMenuItem(miIndexOf).getValue();

						int rIndex = DDList.getMenuItems().size()-miIndexOf;
						float diff = rIndex * DDList.getMenuItemHeight() + (DDList.getMenuPadding()*2);

						DDList.scrollThumbYTo(
							( DDList.getHeight()-diff )
						);
						break;
					}
					miIndexOf++;
				}
				if (miIndexOf > -1 && miIndexOf < DDList.getMenuItems().size()-1)
					handleHightlight(miIndexOf);
				if (screen.getElementById(DDList.getUID()) == null)
						screen.addElement(DDList);
				if (!DDList.getIsVisible() && evt.getKeyCode() != KeyInput.KEY_LSHIFT && evt.getKeyCode() != KeyInput.KEY_RSHIFT)
					DDList.showMenu((Menu)null, getAbsoluteX(), getAbsoluteY()-DDList.getHeight());
			} else {
				if (evt.getKeyCode() == KeyInput.KEY_UP) {
					if (hlIndex > 0) {
						hlIndex--;
						hlCaption = DDList.getMenuItem(hlIndex).getCaption();
						hlValue = DDList.getMenuItem(hlIndex).getValue();
						int rIndex = DDList.getMenuItems().size()-hlIndex;
						float diff = rIndex * DDList.getMenuItemHeight() + (DDList.getMenuPadding()*2);

						DDList.scrollThumbYTo(
							( DDList.getHeight()-diff )
						);
						handleHightlight(hlIndex);
						setSelectedWithCallback(hlIndex, hlCaption, hlValue);
					}
				} else if (evt.getKeyCode() == KeyInput.KEY_DOWN) {
					if (hlIndex < DDList.getMenuItems().size()-1) {
						hlIndex++;
						hlCaption = DDList.getMenuItem(hlIndex).getCaption();
						hlValue = DDList.getMenuItem(hlIndex).getValue();
						int rIndex = DDList.getMenuItems().size()-hlIndex;
						float diff = rIndex * DDList.getMenuItemHeight() + (DDList.getMenuPadding()*2);

						DDList.scrollThumbYTo(
							( DDList.getHeight()-diff )
						);
						handleHightlight(hlIndex);
						setSelectedWithCallback(hlIndex, hlCaption, hlValue);
					}
				}
				if (evt.getKeyCode() == KeyInput.KEY_RETURN) {
					updateSelected();
				}
			}
		}
	}
	
	private void updateSelected() {
		setSelectedWithCallback(hlIndex, hlCaption, hlValue);
		if (DDList.getIsVisible()) DDList.hide();
	}
	
	private void handleHightlight(int index) {
		if (DDList.getIsVisible()) DDList.setHighlight(index);
	}
	
	/**
	 * Abstract event method called when a list item is selected/navigated to.
	 * @param selectedIndex
	 * @param value 
	 */
	public abstract void onChange(int selectedIndex, Object value);
	
	/**
	 * Returns the current selected index
	 * @return selectedIndex
	 */
	public int getSelectIndex() {
		return this.selectedIndex;
	}
	
	/**
	 * Returns the object representing the current selected List Item
	 * @return MenuITem
	 */
	public MenuItem getSelectedListItem() {
		return this.DDList.getMenuItem(selectedIndex);
	}
	
	/**
	 * Returns the object representing the list item at the specified index
	 * @param index
	 * @return MenuItem
	 */
	public MenuItem getListItemByIndex(int index) {
		return this.DDList.getMenuItem(index);
	}
	
	/**
	 * Returns a List of all ListItems 
	 * @return List<MenuItem>
	 */
	public List<MenuItem> getListItems() {
                if(DDList != null) {
                    return DDList.getMenuItems();
                }
                return null;
	}
	
	/**
	 * Returns a pointer to the dropdown list (Menu)
	 * @return DDList
	 */
	public Menu getMenu() {
		return this.DDList;
	}
	
	/**
	 * Sorts the associated drop-down list alphanumerically
	 */
	public void sortList() {
		Object[] orgList = DDList.getMenuItems().toArray();
		List<MenuItem> currentList = new ArrayList();
		List<MenuItem> finalList = new ArrayList();
		List<String> map = new ArrayList();
		for (int i = 0; i < orgList.length; i++) {
			currentList.add((MenuItem)orgList[i]);
			map.add(((MenuItem)orgList[i]).getCaption());
		}
		Collections.sort(map);
		for (String caption : map) {
			int index;
			for (MenuItem mi : currentList) {
				if (mi.getCaption().equals(caption)) {
					index = currentList.indexOf(mi);
					finalList.add(mi);
					DDList.removeMenuItem(index);
					currentList.remove(mi);
					break;
				}
			}
		}
		for (MenuItem mi : finalList) {
			addListItem(mi.getCaption(), mi.getValue());
		}
	}
	
	/**
	 * Sorts drop-down list by true numeric values.  This should only be used
	 * with lists that start with numeric values
	 */
	public void sortListNumeric() {
		Object[] orgList = DDList.getMenuItems().toArray();
		List<MenuItem> currentList = new ArrayList();
		List<MenuItem> finalList = new ArrayList();
		List<Integer> map = new ArrayList();
		for (int i = 0; i < orgList.length; i++) {
			currentList.add((MenuItem)orgList[i]);
			
			boolean NaN = true;
			String tempCaption = ((MenuItem)orgList[i]).getCaption();
			while(NaN && tempCaption.length() != 0) {
				try {
					Integer.parseInt(tempCaption);
					NaN = false;
				} catch (Exception ex) {
					tempCaption = tempCaption.substring(0,tempCaption.length()-2);
				}
			}
			map.add(Integer.parseInt(tempCaption));
		}
		Collections.sort(map);
		for (Integer caption : map) {
			int index;
			for (MenuItem mi : currentList) {
				boolean NaN = true;
				String tempCaption = mi.getCaption();
				while(NaN && tempCaption.length() != 0) {
					try {
						Integer.parseInt(tempCaption);
						NaN = false;
					} catch (Exception ex) {
						tempCaption = tempCaption.substring(0,tempCaption.length()-2);
					}
				}
				if (Integer.parseInt(tempCaption) == caption) {
					index = currentList.indexOf(mi);
					finalList.add(mi);
					DDList.removeMenuItem(index);
					currentList.remove(mi);
					break;
				}
			}
		}
		for (MenuItem mi : finalList) {
			addListItem(mi.getCaption(), mi.getValue());
		}
	}
	
	@Override
	public void controlTextFieldResetTabFocusHook() {
	//	DDList.hideMenu();
	}
	
	@Override
	public void controlCleanupHook() {
		if (DDList != null)
			screen.removeElement(DDList);
	}
	
	@Override
	public void setIsEnabled(boolean isEnabled) {
		super.setIsEnabled(isEnabled);
		selectEnabled = isEnabled;
		this.btnArrowDown.setIsEnabled(isEnabled);
	}
	
	@Override
	public void onKeyPress(KeyInputEvent evt) {
		if (selectEnabled) {
			super.onKeyPress(evt);
		}
	}
}
 public abstract class SelectBox extends ComboBox {
	/**
	 * Creates a new instance of the SelectBox control
	 * 
	 * @param screen The screen control the Element is to be added to
	 */
	public SelectBox(ElementManager screen) {
		this(screen, UIDUtil.getUID(), Vector2f.ZERO,
			screen.getStyle("TextField").getVector2f("defaultSize"),
			screen.getStyle("TextField").getVector4f("resizeBorders"),
			screen.getStyle("TextField").getString("defaultImg")
		);
	}
	
	/**
	 * Creates a new instance of the SelectBox control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param position A Vector2f containing the x/y position of the Element
	 */
	public SelectBox(ElementManager screen, Vector2f position) {
		this(screen, UIDUtil.getUID(), position,
			screen.getStyle("TextField").getVector2f("defaultSize"),
			screen.getStyle("TextField").getVector4f("resizeBorders"),
			screen.getStyle("TextField").getString("defaultImg")
		);
	}
	
	/**
	 * Creates a new instance of the SelectBox control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param position A Vector2f containing the x/y position of the Element
	 * @param dimensions A Vector2f containing the width/height dimensions of the Element
	 */
	public SelectBox(ElementManager screen, Vector2f position, Vector2f dimensions) {
		this(screen, UIDUtil.getUID(), position, dimensions,
			screen.getStyle("TextField").getVector4f("resizeBorders"),
			screen.getStyle("TextField").getString("defaultImg")
		);
	}
	
	/**
	 * Creates a new instance of the SelectBox control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param position A Vector2f containing the x/y position of the Element
	 * @param dimensions A Vector2f containing the width/height dimensions of the Element
	 * @param resizeBorders A Vector4f containing the border information used when resizing the default image (x = N, y = W, z = E, w = S)
	 * @param defaultImg The default image to use for the SelectBox
	 */
	public SelectBox(ElementManager screen, Vector2f position, Vector2f dimensions, Vector4f resizeBorders, String defaultImg) {
		this(screen, UIDUtil.getUID(), position, dimensions, resizeBorders, defaultImg);
	}
	
	/**
	 * Creates a new instance of the SelectBox control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param UID A unique String identifier for the Element
	 * @param position A Vector2f containing the x/y position of the Element
	 */
	public SelectBox(ElementManager screen, String UID, Vector2f position) {
		this(screen, UID, position,
			screen.getStyle("TextField").getVector2f("defaultSize"),
			screen.getStyle("TextField").getVector4f("resizeBorders"),
			screen.getStyle("TextField").getString("defaultImg")
		);
	}
	
	/**
	 * Creates a new instance of the SelectBox control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param UID A unique String identifier for the Element
	 * @param position A Vector2f containing the x/y position of the Element
	 * @param dimensions A Vector2f containing the width/height dimensions of the Element
	 */
	public SelectBox(ElementManager screen, String UID, Vector2f position, Vector2f dimensions) {
		this(screen, UID, position, dimensions,
			screen.getStyle("TextField").getVector4f("resizeBorders"),
			screen.getStyle("TextField").getString("defaultImg")
		);
	}
	
	/**
	 * Creates a new instance of the SelectBox control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param UID A unique String identifier for the Element
	 * @param position A Vector2f containing the x/y position of the Element
	 * @param dimensions A Vector2f containing the width/height dimensions of the Element
	 * @param resizeBorders A Vector4f containing the border information used when resizing the default image (x = N, y = W, z = E, w = S)
	 * @param defaultImg The default image to use for the SelectBox
	 */
	public SelectBox(ElementManager screen, String UID, Vector2f position, Vector2f dimensions, Vector4f resizeBorders, String defaultImg) {
		super(screen, UID, position, dimensions, resizeBorders, defaultImg);
		
		setIsEnabled(true);
	}
	
	@Override
	public final void setIsEnabled(boolean isEnabled) {
		super.setIsEnabled(false);
		selectEnabled = isEnabled;
		this.btnArrowDown.setIsEnabled(isEnabled);
	}

        @Override
        public boolean getIsEnabled() {
                return selectEnabled;
        }    
}
 public abstract class SelectList extends ScrollArea implements MouseMovementListener, MouseButtonListener, TabFocusListener, KeyboardListener {
	private List<ListItem> listItems = new ArrayList();
	private List<Integer> selectedIndexes = new ArrayList();
	private List<Element> highlights = new ArrayList();
	
	private boolean isMultiselect = false;
	private float initWidth;
	private float listItemHeight;
	private float listPadding = 1;
	private ColorRGBA highlightColor;
	protected int currentListItemIndex = -1;
	private boolean shift = false, ctrl = false;
	
	/**
	 * Creates a new instance of the SelectList control
	 * 
	 * @param screen The screen control the Element is to be added to
	 */
	public SelectList(ElementManager screen) {
		this(screen, UIDUtil.getUID(), Vector2f.ZERO,
			screen.getStyle("Menu").getVector2f("defaultSize"),
			screen.getStyle("Menu").getVector4f("resizeBorders"),
			screen.getStyle("Menu").getString("defaultImg")
		);
	}
	
	/**
	 * Creates a new instance of the SelectList control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param position A Vector2f containing the x/y position of the Element
	 */
	public SelectList(ElementManager screen, Vector2f position) {
		this(screen, UIDUtil.getUID(), position,
			screen.getStyle("Menu").getVector2f("defaultSize"),
			screen.getStyle("Menu").getVector4f("resizeBorders"),
			screen.getStyle("Menu").getString("defaultImg")
		);
	}
	
	/**
	 * Creates a new instance of the SelectList control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param position A Vector2f containing the x/y position of the Element
	 * @param dimensions A Vector2f containing the width/height dimensions of the Element
	 */
	public SelectList(ElementManager screen, Vector2f position, Vector2f dimensions) {
		this(screen, UIDUtil.getUID(), position, dimensions,
			screen.getStyle("Menu").getVector4f("resizeBorders"),
			screen.getStyle("Menu").getString("defaultImg")
		);
	}
	
	/**
	 * Creates a new instance of the SelectList control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param position A Vector2f containing the x/y position of the Element
	 * @param dimensions A Vector2f containing the width/height dimensions of the Element
	 * @param resizeBorders A Vector4f containg the border information used when resizing the default image (x = N, y = W, z = E, w = S)
	 * @param defaultImg The default image to use for the Menu
	 */
	public SelectList(ElementManager screen, Vector2f position, Vector2f dimensions, Vector4f resizeBorders, String defaultImg) {
		this(screen, UIDUtil.getUID(), position, dimensions, resizeBorders, defaultImg);
	}
	
	/**
	 * Creates a new instance of the SelectList control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param UID A unique String identifier for the Element
	 * @param position A Vector2f containing the x/y position of the Element
	 */
	public SelectList(ElementManager screen, String UID, Vector2f position) {
		this(screen, UID, position,
			screen.getStyle("Menu").getVector2f("defaultSize"),
			screen.getStyle("Menu").getVector4f("resizeBorders"),
			screen.getStyle("Menu").getString("defaultImg")
		);
	}
	
	/**
	 * Creates a new instance of the SelectList control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param UID A unique String identifier for the Element
	 * @param position A Vector2f containing the x/y position of the Element
	 * @param dimensions A Vector2f containing the width/height dimensions of the Element
	 */
	public SelectList(ElementManager screen, String UID, Vector2f position, Vector2f dimensions) {
		this(screen, UID, position, dimensions,
			screen.getStyle("Menu").getVector4f("resizeBorders"),
			screen.getStyle("Menu").getString("defaultImg")
		);
	}
	
	/**
	 * Creates a new instance of the SelectList control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param UID A unique String identifier for the Element
	 * @param position A Vector2f containing the x/y position of the Element
	 * @param dimensions A Vector2f containing the width/height dimensions of the Element
	 * @param resizeBorders A Vector4f containg the border information used when resizing the default image (x = N, y = W, z = E, w = S)
	 * @param defaultImg The default image to use for the Slider's track
	 */
	public SelectList(ElementManager screen, String UID, Vector2f position, Vector2f dimensions, Vector4f resizeBorders, String defaultImg) {
		super(screen, UID, position, dimensions, resizeBorders, defaultImg, false);
		
		listPadding = screen.getStyle("Menu").getVector4f("menuPadding").x;
		highlightColor = screen.getStyle("Menu").getColorRGBA("highlightColor");
		// Load default font info
		setFontColor(screen.getStyle("Menu").getColorRGBA("fontColor"));
		setFontSize(screen.getStyle("Menu").getFloat("fontSize"));
		setTextAlign(BitmapFont.Align.valueOf(screen.getStyle("Menu").getString("textAlign")));
		setTextVAlign(BitmapFont.VAlign.valueOf(screen.getStyle("Menu").getString("textVAlign")));
		setTextWrap(LineWrapMode.valueOf(screen.getStyle("Menu").getString("textWrap")));
		setTextPadding(screen.getStyle("Menu").getFloat("textPadding"));
		setTextClipPadding(screen.getStyle("Menu").getFloat("textPadding"));
		scrollableArea.setFontColor(screen.getStyle("Menu").getColorRGBA("fontColor"));
		scrollableArea.setFontSize(screen.getStyle("Menu").getFloat("fontSize"));
		scrollableArea.setTextAlign(BitmapFont.Align.valueOf(screen.getStyle("Menu").getString("textAlign")));
		scrollableArea.setTextVAlign(BitmapFont.VAlign.valueOf(screen.getStyle("Menu").getString("textVAlign")));
		scrollableArea.setTextWrap(LineWrapMode.valueOf(screen.getStyle("Menu").getString("textWrap")));
		scrollableArea.setTextPadding(screen.getStyle("Menu").getFloat("textPadding"));
		scrollableArea.setTextClipPadding(listPadding+screen.getStyle("Menu").getFloat("textPadding"));
		scrollableArea.setScaleEW(false);
		
		listItemHeight = BitmapTextUtil.getTextLineHeight(this, "Xg");
		
		scrollableArea.setText(" ");
		scrollableArea.setIgnoreMouse(true);
		scrollableArea.setHeight(listItemHeight);
		
		initWidth = listItemHeight*3;
	}
	
	public void setHighlightColor(ColorRGBA color) {
		this.highlightColor.set(color);
	}
	
	public void setIsMultiselect(boolean isMultiselect) {
		this.isMultiselect = isMultiselect;
	}
	
	public boolean getIsMultiselect() {
		return this.isMultiselect;
	}
	/**
	 * Adds a ListItem to the Menu
	 * @param caption The display caption of the MenuItem
	 * @param value The value to associate with the MenuItem
	 */
	public int addListItem(String caption, Object value) {
		this.getVScrollBar().hide();
		ListItem listItem = new ListItem(
			this,
			caption,
			value
		);
		
		this.listItems.add(listItem);
		pack();
		
		return listItems.size()-1;
	}
	
	/**
	 * Inserts a new ListItem at the provided index
	 * @param index The index to insert into
	 * @param caption The display caption of the MenuItem
	 * @param value The value to associate with the MenuItem
	 */
	public void insertListItem(int index, String caption, Object value) {
		if (!listItems.isEmpty()) {
			if (index >= 0 && index < listItems.size()) {
				this.getVScrollBar().hide();
				ListItem listItem = new ListItem(
					this,
					caption,
					value
				);
				this.listItems.add(index, listItem);
				pack();
			}
		}
	}
	
	public void updateListItem(int index, String caption, Object value) {
		if (!listItems.isEmpty()) {
			if (index >= 0 && index < listItems.size()) {
				this.getVScrollBar().hide();
				ListItem listItem = new ListItem(
					this,
					caption,
					value
				);
				this.listItems.remove(index);
				listItems.add(index, listItem);
				pack();
			}
		}
	}
	
	/**
	 * Remove the ListItem at the provided index
	 * @param index int
	 */
	public void removeListItem(int index) {
		this.getVScrollBar().hide();
		if (!listItems.isEmpty()) {
			if (index >= 0 && index < listItems.size()) {
				listItems.remove(index);
				pack();
			}
		}
	}
	
	/**
	 * Remove the first ListItem that contains the provided value
	 * @param value Object
	 */
	public int removeListItem(Object value) {
		if (!listItems.isEmpty()) {
			int index = -1;
			int count = 0;
			for (ListItem mi : listItems) {
				if (mi.getValue() == value) {
					index = count;
					break;
				}
				count++;
			}
			if (index != -1)
				removeListItem(index);
			return index;
		} else {
			return -1;
		}
	}
	
	/**
	 * Remove the first ListItem that contains the provided caption
	 * @param value Object
	 */
	public int removeListItem(String caption) {
		if (!listItems.isEmpty()) {
			int index = -1;
			int count = 0;
			for (ListItem mi : listItems) {
				if (mi.getCaption().equals(caption)) {
					index = count;
					break;
				}
				count++;
			}
			if (index != -1) {
				removeListItem(index);
				return index;
			} else {
				return -1;
			}
		} else {
			return -1;
		}
	}
	
	/**
	 * Removes the first ListItem in the SelectList
	 */
	public int removeFirstListItem() {
		if (!listItems.isEmpty()) {
			removeListItem(0);
			return 0;
		} else {
			return -1;
		}
	}
	
	/**
	 * Removes the last ListItem in the SelectList
	 */
	public int removeLastListItem() {
		if (!listItems.isEmpty()) {
			removeListItem(listItems.size()-1);
			return listItems.size();
		} else {
			return -1;
		}
	}
	
	public void removeAllListItems() {
		this.listItems = new ArrayList();
		this.selectedIndexes = new ArrayList();
		pack();
	}
	
	/**
	 * Sets the current selected index for single select SelectLists
	 * @param index int
	 */
	public void setSelectedIndex(Integer index) {
		if (index < 0) index = 0;
		else if (index >= listItems.size()) index = listItems.size()-1;
		selectedIndexes = new ArrayList();
		selectedIndexes.add(index);
		displayHighlights();
		onChange();
	}
	
	/**
	 * Sets the current list of selected indexes to the specified indexes
	 * @param indexes 
	 */
	public void setSelectedIndexes(Integer... indexes) {
		for (int i = 0; i < indexes.length; i++) {
			if (!selectedIndexes.contains(indexes[i]))
				selectedIndexes.add(indexes[i]);
		}
		displayHighlights();
		onChange();
	}
	
	/**
	 * Adds the specified index to the list of selected indexes
	 * @param index int
	 */
	public void addSelectedIndex(Integer index) {
		if (!selectedIndexes.contains(index))
			selectedIndexes.add(index);
		displayHighlights();
		onChange();
	}
	
	/**
	 * Removes the specified index from the list of selected indexes
	 * @param index int
	 */
	public void removeSelectedIndex(Integer index) {
		selectedIndexes.remove(index);
		displayHighlights();
		onChange();
	}
	
	/**
	 * Returns the first (or only) index in the list of selected indexes
	 * @return int
	 */
	public int getSelectedIndex() {
		if (selectedIndexes.isEmpty())
			return -1;
		else
			return selectedIndexes.get(0);
	}
	
	/**
	 * Returns the entire list of selected indexes
	 * @return List<Integer>
	 */
	public List<Integer> getSelectedIndexes() {
		return this.selectedIndexes;
	}
	
	/**
	 * Returns the ListItem at the specified index
	 * @param index int
	 * @return ListItem
	 */
	public ListItem getListItem(int index) {
		if (!listItems.isEmpty()) {
			if (index >= 0 && index < listItems.size())
				return listItems.get(index);
			else return null;
		} else
			return null;
	}
	
	public ListItem getListItemByCaption(String caption) {
		ListItem mItem = null;
		for (ListItem mi : getListItems()) {
			if (mi.getCaption().equals(caption)) {
				mItem = mi;
				break;
			}
		}
		
		return mItem;
	}
	
	
	
	public int getListItemIndexByCaption(String caption) {
		int index = -1;
		int count = 0;
		for (ListItem mi : getListItems()) {
			if (mi.getCaption().equals(caption)) {
				index = count;
				break;
			}
			count++;
		}
		
		return index;
	}
	
	/**
	 * Returns a List containing all ListItems corresponding to the list of selectedIndexes
	 * @return List<ListItem>
	 */
	public List<ListItem> getSelectedListItems() {
		List<ListItem> ret = new ArrayList();
		for (Integer i : selectedIndexes) {
			ret.add(getListItem(i));
		}
		return ret;
	}
	
	public List<ListItem> getListItems() {
		return this.listItems;
	}
	
	public float getListItemHeight() {
		return this.listItemHeight;
	}
	/**
	 * Forces the SelectList to rebuild all ListItems.  This does not need to be called, however it will not effect anything negatively if it is.
	 */
	public void pack() {
		String finalString = "";
		
		listItemHeight = BitmapTextUtil.getTextLineHeight(this, "Xg");
		
		scrollableArea.removeAllChildren();
		scrollableArea.setHeight(listItemHeight);
		
		int index = 0;
		float currentHeight = 0;
		float width = listItemHeight*3;
		boolean init = true;
		
		String leftSpacer = "  ";
		String rightSpacer = "  ";
		
		for (ListItem mi : listItems) {
			float tWidth = (listItemHeight*2)+BitmapTextUtil.getTextWidth(this, leftSpacer + mi.getCaption() + rightSpacer);
			width = (tWidth > width) ? tWidth : width;
			if (init) {
				finalString = leftSpacer + mi.getCaption() + rightSpacer;
				init = false;
			} else {
				finalString += "\n" + leftSpacer + mi.getCaption() + rightSpacer;
			}
			if (selectedIndexes.contains(index)) {
				Element highlight = createHighlight(index);
				highlight.setX(0);
				highlight.setWidth( getWidth()-(listPadding*2) );
				highlight.setHeight(listItemHeight);
				highlight.getElementMaterial().setColor("Color", highlightColor);
			//	highlight.setClippingLayer(this);
				highlight.addClippingLayer(this);
				highlight.setClipPadding(listPadding);
				highlight.setY(scrollableArea.getHeight()-((listItems.size()-index)*listItemHeight) + listPadding);
				scrollableArea.addChild(highlight);
			}
			currentHeight += listItemHeight;
			index++;
		}
		scrollableArea.setFontSize(fontSize);
		scrollableArea.setText(finalString);
		scrollableArea.setTextPosition(0, listPadding);
		
		scrollableArea.setX(listPadding);
		scrollableArea.setWidth( ((getWidth() > width) ? getWidth() : width)-(listPadding*2) );
		scrollableArea.setY(listPadding);
		scrollableArea.setHeight(currentHeight);
		
		if(getScrollableHeight() > getHeight()-(listPadding*2)) {
			scrollToTop();
			setWidth(getWidth());
			getVScrollBar().setX(getWidth());
			getVScrollBar().show();
		}
		scrollToTop();
	}
	
	private void displayHighlights() {
		scrollableArea.removeAllChildren();
		int index = 0;
		float currentHeight = 0;
		for (ListItem mi : listItems) {
			if (selectedIndexes.contains(index)) {
				Element highlight = createHighlight(index);
				highlight.setX(0);
				highlight.setWidth( getWidth()-(listPadding*2) );
				highlight.setHeight(listItemHeight);
				highlight.getElementMaterial().setColor("Color", highlightColor);
			//	highlight.setClippingLayer(this);
				highlight.addClippingLayer(this);
				highlight.setClipPadding(listPadding);
				highlight.setY(scrollableArea.getHeight()-((listItems.size()-index)*listItemHeight) + listPadding);
				scrollableArea.addChild(highlight);
			}
			currentHeight += listItemHeight;
			index++;
		}
	}
	
	private Element createHighlight(int index) {
		Element highlight = new Element(
			screen,
			getUID() + ":Highlight" + index,
			new Vector2f(0,0),
			new Vector2f(listPadding,listPadding),
			new Vector4f(1,1,1,1),
			null
		);
		highlight.setScaleEW(true);
		highlight.setScaleNS(false);
		highlight.setDocking(Docking.SW);
		highlight.setIgnoreMouse(true);
		
		return highlight;
	}
	@Override
	public void onMouseMove(MouseMotionEvent evt) {
		float x = evt.getX()-getX();
		float y = scrollableArea.getAbsoluteHeight()-listPadding-evt.getY();
		
		if (currentListItemIndex != (int)Math.floor(y/listItemHeight)) {
			currentListItemIndex = (int)Math.floor(y/listItemHeight);
		}
	}

	@Override
	public void onMouseLeftPressed(MouseButtonEvent evt) {
		evt.setConsumed();
	}

	@Override
	public void onMouseLeftReleased(MouseButtonEvent evt) {
		if (isMultiselect) {
			if (shift || ctrl) {
				if (!selectedIndexes.contains(currentListItemIndex)) {
					addSelectedIndex(currentListItemIndex);
				} else {
					removeSelectedIndex(currentListItemIndex);
				}
			} else {
				setSelectedIndex(currentListItemIndex);
			}
		} else {
			if (currentListItemIndex >= 0 && currentListItemIndex < listItems.size())
				setSelectedIndex(currentListItemIndex);
			else
				selectedIndexes = new ArrayList();
		}
		evt.setConsumed();
	}

	@Override
	public void onMouseRightPressed(MouseButtonEvent evt) {
		evt.setConsumed();
	}

	@Override
	public void onMouseRightReleased(MouseButtonEvent evt) {
		evt.setConsumed();
	}

	@Override
	public void onKeyPress(KeyInputEvent evt) {
		if (evt.getKeyCode() == KeyInput.KEY_LCONTROL || evt.getKeyCode() == KeyInput.KEY_RCONTROL) {
			ctrl = true;
		} else if (evt.getKeyCode() == KeyInput.KEY_LSHIFT || evt.getKeyCode() == KeyInput.KEY_RSHIFT) {
			shift = true;
		}
	}

	@Override
	public void onKeyRelease(KeyInputEvent evt) {
		if (evt.getKeyCode() == KeyInput.KEY_LCONTROL || evt.getKeyCode() == KeyInput.KEY_RCONTROL) {
			ctrl = false;
		} else if (evt.getKeyCode() == KeyInput.KEY_LSHIFT || evt.getKeyCode() == KeyInput.KEY_RSHIFT) {
			shift = false;
		}
	}

	@Override
	public void setTabFocus() {
		screen.setKeyboardElement(this);
	}

	@Override
	public void resetTabFocus() {
		screen.setKeyboardElement(null);
	}
	
	public abstract void onChange();
	
	public class ListItem {
		SelectList owner;
		String caption;
		Object value;
		
		public ListItem(SelectList owner, String caption, Object value) {
			this.owner = owner;
			this.caption = caption;
			this.value = value;
		}
		
		public String getCaption() {
			return this.caption;
		}
		
		public Object getValue() {
			return this.value;
		}
	}
	
	public void scrollToSelected() {
		int rIndex = getSelectedIndex();
		float diff = (rIndex+1) * getListItemHeight();
		
		float y = -(getScrollableHeight()-diff);
		
		if (FastMath.abs(y) > getScrollableHeight()) {
			y = getScrollableHeight();
		}
		
		scrollThumbYTo(y);
	}
}
 public abstract class Spinner extends TextField {
	/*
	public static enum Orientation {
		VERTICAL,
		HORIZONTAL
	}
	*/
	protected List<String> stepValues = new ArrayList();
	private boolean cycle = false;
	private int selectedIndex = -1;
	private Orientation orientation;
	
	private float btnWidth;
	private float btnIncX, btnIncY, btnIncH, btnIncIconSize;
	private float btnDecX, btnDecY, btnDecH, btnDecIconSize;
	private String btnIncIcon, btnDecIcon;
	
	private ButtonAdapter btnInc, btnDec;
	
	/**
	 * Creates a new instance of the Spinner control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param orientation Spinner.Orientation used to establish Horizontal/Vertical layout during control configuration
	 * @param cycle Boolean used to determine if the spinner should cycle back through values
	 */
	public Spinner(ElementManager screen, Spinner.Orientation orientation, boolean cycle) {
		this(screen, UIDUtil.getUID(), Vector2f.ZERO,
			screen.getStyle("Spinner").getVector2f("defaultSize"),
			screen.getStyle("Spinner").getVector4f("resizeBorders"),
			screen.getStyle("Spinner").getString("defaultImg"),
			orientation,
			cycle
		);
	}
	
	/**
	 * Creates a new instance of the Spinner control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param position A Vector2f containing the x/y position of the Element
	 * @param orientation Spinner.Orientation used to establish Horizontal/Vertical layout during control configuration
	 * @param cycle Boolean used to determine if the spinner should cycle back through values
	 */
	public Spinner(ElementManager screen, Vector2f position, Spinner.Orientation orientation, boolean cycle) {
		this(screen, UIDUtil.getUID(), position,
			screen.getStyle("Spinner").getVector2f("defaultSize"),
			screen.getStyle("Spinner").getVector4f("resizeBorders"),
			screen.getStyle("Spinner").getString("defaultImg"),
			orientation,
			cycle
		);
	}
	
	/**
	 * Creates a new instance of the Spinner control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param position A Vector2f containing the x/y position of the Element
	 * @param dimensions A Vector2f containing the width/height dimensions of the Element
	 * @param orientation Spinner.Orientation used to establish Horizontal/Vertical layout during control configuration
	 * @param cycle Boolean used to determine if the spinner should cycle back through values
	 */
	public Spinner(ElementManager screen, Vector2f position, Vector2f dimensions, Spinner.Orientation orientation, boolean cycle) {
		this(screen, UIDUtil.getUID(), position, dimensions,
			screen.getStyle("Spinner").getVector4f("resizeBorders"),
			screen.getStyle("Spinner").getString("defaultImg"),
			orientation,
			cycle
		);
	}
	
	/**
	 * Creates a new instance of the Spinner control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param position A Vector2f containing the x/y position of the Element
	 * @param dimensions A Vector2f containing the width/height dimensions of the Element
	 * @param resizeBorders A Vector4f containing the border information used when resizing the default image (x = N, y = W, z = E, w = S)
	 * @param defaultImg The default image to use for the Spinner
	 * @param orientation Spinner.Orientation used to establish Horizontal/Vertical layout during control configuration
	 * @param cycle Boolean used to determine if the spinner should cycle back through values
	 */
	public Spinner(ElementManager screen, Vector2f position, Vector2f dimensions, Vector4f resizeBorders, String defaultImg, Spinner.Orientation orientation, boolean cycle) {
		this(screen, UIDUtil.getUID(), position, dimensions, resizeBorders, defaultImg, orientation, cycle);
	}
	
	/**
	 * Creates a new instance of the Spinner control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param UID A unique String identifier for the Element
	 * @param position A Vector2f containing the x/y position of the Element
	 * @param orientation Spinner.Orientation used to establish Horizontal/Vertical layout during control configuration
	 * @param cycle Boolean used to determine if the spinner should cycle back through values
	 */
	public Spinner(ElementManager screen, String UID, Vector2f position, Spinner.Orientation orientation, boolean cycle) {
		this(screen, UID, position,
			screen.getStyle("Spinner").getVector2f("defaultSize"),
			screen.getStyle("Spinner").getVector4f("resizeBorders"),
			screen.getStyle("Spinner").getString("defaultImg"),
			orientation,
			cycle
		);
	}
	
	/**
	 * Creates a new instance of the Spinner control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param UID A unique String identifier for the Element
	 * @param position A Vector2f containing the x/y position of the Element
	 * @param dimensions A Vector2f containing the width/height dimensions of the Element
	 * @param orientation Spinner.Orientation used to establish Horizontal/Vertical layout during control configuration
	 * @param cycle Boolean used to determine if the spinner should cycle back through values
	 */
	public Spinner(ElementManager screen, String UID, Vector2f position, Vector2f dimensions, Spinner.Orientation orientation, boolean cycle) {
		this(screen, UID, position, dimensions,
			screen.getStyle("Spinner").getVector4f("resizeBorders"),
			screen.getStyle("Spinner").getString("defaultImg"),
			orientation,
			cycle
		);
	}
	
	/**
	 * Creates a new instance of the Spinner control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param UID A unique String identifier for the Element
	 * @param position A Vector2f containing the x/y position of the Element
	 * @param dimensions A Vector2f containing the width/height dimensions of the Element
	 * @param resizeBorders A Vector4f containing the border information used when resizing the default image (x = N, y = W, z = E, w = S)
	 * @param defaultImg The default image to use for the Spinner
	 * @param orientation Spinner.Orientation used to establish Horizontal/Vertical layout during control configuration
	 * @param cycle Boolean used to determine if the spinner should cycle back through values
	 */
	public Spinner(ElementManager screen, String UID, Vector2f position, Vector2f dimensions, Vector4f resizeBorders, String defaultImg, Spinner.Orientation orientation, boolean cycle) {
		super(screen, UID, position, dimensions, resizeBorders, defaultImg
		);
		
		this.orientation = orientation;
		this.cycle = cycle;
		this.setScaleEW(false);
		this.setScaleNS(false);
		this.setDocking(Docking.NW);
		
		btnWidth = getHeight();
		
		this.setFontSize(screen.getStyle("Spinner").getFloat("fontSize"));
		this.setTextPaddingByKey("Spinner","textPadding");
		this.setTextWrap(LineWrapMode.valueOf(screen.getStyle("Spinner").getString("textWrap")));
		this.setTextAlign(BitmapFont.Align.valueOf(screen.getStyle("Spinner").getString("textAlign")));
		this.setTextVAlign(BitmapFont.VAlign.valueOf(screen.getStyle("Spinner").getString("textVAlign")));
		
		if (orientation == Orientation.HORIZONTAL) {
			setWidth(getWidth()-(btnWidth*2));
			setX(getX()+btnWidth);
			btnIncX = getWidth();
			btnIncY = 0;
			btnIncH = getHeight();
			btnIncIcon = screen.getStyle("Common").getString("arrowRight");
			btnDecX = -getHeight();
			btnDecY = 0;
			btnDecH = getHeight();
			btnDecIcon = screen.getStyle("Common").getString("arrowLeft");
		//	layoutHints.setElementPadX(btnWidth*2);
		} else {
			setWidth(getWidth()-btnWidth);
			btnIncX = getWidth();
			btnIncY = 0;
			btnIncH = getHeight()/2;
			btnIncIcon = screen.getStyle("Common").getString("arrowUp");
			btnDecX = getWidth();
			btnDecY = getHeight()/2;
			btnDecH = getHeight()/2;
			btnDecIcon = screen.getStyle("Common").getString("arrowDown");
		//	layoutHints.setElementPadX(btnWidth);
		}
		btnIncIconSize = getHeight()/2;
		btnDecIconSize = getHeight()/2;
		
		btnInc = new ButtonAdapter(
			screen,
			UID + ":btnInc",
			new Vector2f(btnIncX, btnIncY),
			new Vector2f(getHeight(), btnIncH)
		) {
			@Override
			public void onButtonMouseLeftDown(MouseButtonEvent evt, boolean toggled) {
				screen.setTabFocusElement((Spinner)getElementParent());
				((Spinner)getElementParent()).incStep();
			}
			@Override
			public void onButtonStillPressedInterval() {
				((Spinner)getElementParent()).incStep();
			}
		};
		btnInc.setButtonIcon(btnIncIconSize, btnIncIconSize, btnIncIcon);
		btnInc.setDocking(Docking.SW);
		addChild(btnInc);
		
		btnDec = new ButtonAdapter(
			screen,
			UID + ":btnDec",
			new Vector2f(btnDecX, btnDecY),
			new Vector2f(getHeight(), btnIncH)
		) {
			@Override
			public void onButtonMouseLeftDown(MouseButtonEvent evt, boolean toggled) {
				screen.setTabFocusElement((Spinner)getElementParent());
				((Spinner)getElementParent()).decStep();
			}
			@Override
			public void onButtonStillPressedInterval() {
				((Spinner)getElementParent()).decStep();
			}
		};
		btnDec.setButtonIcon(btnDecIconSize, btnDecIconSize, btnDecIcon);
		btnDec.setDocking(Docking.SW);
		addChild(btnDec);
		
		setIsEnabled(false);
	}
	
	/**
	 * Returns the index of the selected stepValue
	 * @return int
	 */
	public int getSelectedIndex() {
		return this.selectedIndex;
	}
	
	/**
	 * Sets the interval speed for the spinner
	 * @param callsPerSecond float
	 */
	public void setInterval(float callsPerSecond) {
		btnInc.setInterval(callsPerSecond);
		btnDec.setInterval(callsPerSecond);
	}
	
	/**
	 * Adds a stepValue to the Spinner
	 * @param value String
	 */
	public void addStepValue(String value) {
		stepValues.add(value);
		if (selectedIndex == -1)
			selectedIndex = 0;
		displaySelectedStep();
	}
	
	/**
	 * Removes the provided value for the stepValues of the Spinner
	 * @param value String
	 */
	public void removeStepValue(String value) {
		stepValues.remove(value);
	}
	
	/**
	 * Populates the stepValues with an integer range using the provided information
	 * @param min int The minimum value of the range
	 * @param max int The maximum values of the range
	 * @param inc int the ammount to increment from step to step
	 */
	public void setStepIntegerRange(int min, int max, int inc) {
		stepValues.clear();
		selectedIndex = -1;
		for (int i = min; i <= max; i += inc) {
			stepValues.add(String.valueOf(i));
		}
		if (selectedIndex == -1)
			selectedIndex = 0;
		displaySelectedStep();
	}
	
	/**
	 * Populates the stepValues with an float range using the provided information
	 * @param min float The minimum value of the range
	 * @param max float The maximum values of the range
	 * @param inc float the ammount to increment from step to step
	 */
	public void setStepFloatRange(float min, float max, float inc) {
		stepValues.clear();
		selectedIndex = -1;
		for (float i = min; i <= max; i += inc) {
			stepValues.add(String.valueOf(i));
		}
		if (selectedIndex == -1)
			setSelectedIndexWithCallback(0);
		displaySelectedStep();
	}
	
	/**
	 * Sets the selected index of the Spinner to the provided index and calls the onChange event
	 * @param selectedIndex int
	 */
	public void setSelectedIndexWithCallback(int selectedIndex) {
		if (selectedIndex < 0)
			selectedIndex = 0;
		else if (selectedIndex > stepValues.size()-1)
			selectedIndex = stepValues.size()-1;
		ChangeType change = selectedIndex > this.selectedIndex ? ChangeType.INC : ChangeType.DEC;
		this.selectedIndex = selectedIndex;
		displaySelectedStep();
		onChange(selectedIndex, stepValues.get(selectedIndex), change);
	}
	
	/**
	 * Sets the selected index of the Spinner to the provided index
	 * @param selectedIndex int
	 */
	public void setSelectedIndex(int selectedIndex) {
		if (selectedIndex < 0)
			selectedIndex = 0;
		else if (selectedIndex > stepValues.size()-1)
			selectedIndex = stepValues.size()-1;
		
		this.selectedIndex = selectedIndex;
		displaySelectedStep();
	}
	
	private void incStep() {
		selectedIndex++;
		if (selectedIndex == stepValues.size()) {
			if (cycle) {
				selectedIndex = 0;
			} else {
				selectedIndex--;
			}
		}
		displaySelectedStep();
		onChange(selectedIndex, stepValues.get(selectedIndex), ChangeType.INC);
	}
	
	private void decStep() {
		selectedIndex--;
		if (selectedIndex == -1) {
			if (cycle) {
				selectedIndex = stepValues.size()-1;
			} else {
				selectedIndex = 0;
			}
		}
		displaySelectedStep();
		onChange(selectedIndex, stepValues.get(selectedIndex), ChangeType.DEC);
	}
	
	private void displaySelectedStep() {
		this.setText(stepValues.get(selectedIndex));
	}
	
	@Override
	public void controlKeyPressHook(KeyInputEvent evt, String text) {
		if (evt.getKeyCode() == KeyInput.KEY_LEFT || evt.getKeyCode() == KeyInput.KEY_DOWN) {
			decStep();
		} else if (evt.getKeyCode() == KeyInput.KEY_RIGHT || evt.getKeyCode() == KeyInput.KEY_UP) {
			incStep();
		} else {
			displaySelectedStep();
		}
	}
	
	/**
	 * The abstract event method that is called when the selectedIndex changes
	 * @param selectedIndex int The new selectedIndex
	 * @param value The value associated with the selected index
	 */
	public abstract void onChange(int selectedIndex, String value, ChangeType changeType);
        
        public enum ChangeType{
            INC,
            DEC;
        }
}
 public class ScrollArea extends Element implements MouseWheelListener, TouchListener, FlingListener {
	protected Element scrollableArea;
	private boolean isTextOnly = true;
	private boolean isScrollable = true;
	private VScrollBar vScrollBar;
	protected float scrollSize;
	private boolean scrollHidden = false;
    protected float scrollBarGap = 0;
	protected GameTimer flingTimer;
	private boolean flingEnabled = true;
	float flingSpeed = 0;
	boolean flingDir = true;
	float touchOffsetY = 0;
	float touchStartY = 0;
	float touchEndY = 0;
	
	/**
	 * Creates a new instance of the ScrollArea control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param isTextOnly Boolean defining if the scroll area will contain other Elements or use formatted text
	 */
	public ScrollArea(ElementManager screen, boolean isTextOnly) {
		this(screen, UIDUtil.getUID(), Vector2f.ZERO,
			screen.getStyle("ScrollArea").getVector2f("defaultSize"),
			screen.getStyle("ScrollArea").getVector4f("resizeBorders"),
			screen.getStyle("ScrollArea").getString("defaultImg"),
			isTextOnly
		);
	}
	
	/**
	 * Creates a new instance of the ScrollArea control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param position A Vector2f containing the x/y position of the Element
	 * @param isTextOnly Boolean defining if the scroll area will contain other Elements or use formatted text
	 */
	public ScrollArea(ElementManager screen, Vector2f position, boolean isTextOnly) {
		this(screen, UIDUtil.getUID(), position,
			screen.getStyle("ScrollArea").getVector2f("defaultSize"),
			screen.getStyle("ScrollArea").getVector4f("resizeBorders"),
			screen.getStyle("ScrollArea").getString("defaultImg"),
			isTextOnly
		);
	}
	
	/**
	 * Creates a new instance of the ScrollArea control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param position A Vector2f containing the x/y position of the Element
	 * @param dimensions A Vector2f containing the width/height dimensions of the Element
	 * @param isTextOnly Boolean defining if the scroll area will contain other Elements or use formatted text
	 */
	public ScrollArea(ElementManager screen, Vector2f position, Vector2f dimensions, boolean isTextOnly) {
		this(screen, UIDUtil.getUID(), position, dimensions,
			screen.getStyle("ScrollArea").getVector4f("resizeBorders"),
			screen.getStyle("ScrollArea").getString("defaultImg"),
			isTextOnly
		);
	}
	
	/**
	 * Creates a new instance of the ScrollArea control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param position A Vector2f containing the x/y position of the Element
	 * @param dimensions A Vector2f containing the width/height dimensions of the Element
	 * @param resizeBorders A Vector4f containg the border information used when resizing the default image (x = N, y = W, z = E, w = S)
	 * @param defaultImg The default image to use for the Slider's track
	 * @param isTextOnly Boolean defining if the scroll area will contain other Elements or use formatted text
	 */
	public ScrollArea(ElementManager screen, Vector2f position, Vector2f dimensions, Vector4f resizeBorders, String defaultImg, boolean isTextOnly) {
		this(screen, UIDUtil.getUID(), position, dimensions, resizeBorders, defaultImg, isTextOnly);
	}
	
	/**
	 * Creates a new instance of the ScrollArea control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param UID A unique String identifier for the Element
	 * @param position A Vector2f containing the x/y position of the Element
	 * @param isTextOnly Boolean defining if the scroll area will contain other Elements or use formatted text
	 */
	public ScrollArea(ElementManager screen, String UID, Vector2f position, boolean isTextOnly) {
		this(screen, UID, position,
			screen.getStyle("ScrollArea").getVector2f("defaultSize"),
			screen.getStyle("ScrollArea").getVector4f("resizeBorders"),
			screen.getStyle("ScrollArea").getString("defaultImg"),
			isTextOnly
		);
	}
	
	/**
	 * Creates a new instance of the ScrollArea control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param UID A unique String identifier for the Element
	 * @param position A Vector2f containing the x/y position of the Element
	 * @param dimensions A Vector2f containing the width/height dimensions of the Element
	 * @param isTextOnly Boolean defining if the scroll area will contain other Elements or use formatted text
	 */
	public ScrollArea(ElementManager screen, String UID, Vector2f position, Vector2f dimensions, boolean isTextOnly) {
		this(screen, UID, position, dimensions,
			screen.getStyle("ScrollArea").getVector4f("resizeBorders"),
			screen.getStyle("ScrollArea").getString("defaultImg"),
			isTextOnly
		);
	}
	
	/**
	 * Creates a new instance of the ScrollArea control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param UID A unique String identifier for the Element
	 * @param position A Vector2f containing the x/y position of the Element
	 * @param dimensions A Vector2f containing the width/height dimensions of the Element
	 * @param resizeBorders A Vector4f containg the border information used when resizing the default image (x = N, y = W, z = E, w = S)
	 * @param defaultImg The default image to use for the Slider's track
	 * @param isTextOnly Boolean defining if the scroll area will contain other Elements or use formatted text
	 */
	public ScrollArea (ElementManager screen, String UID, Vector2f position, Vector2f dimensions, Vector4f resizeBorders, String defaultImg, boolean isTextOnly) {
		super (screen, UID, position, dimensions, resizeBorders, defaultImg);
		
		this.isTextOnly = isTextOnly;
		
		setIgnoreMouseWheelMove(false);
		
		// Load default font info
		setFontColor(screen.getStyle("ScrollArea").getColorRGBA("fontColor"));
		setFontSize(screen.getStyle("ScrollArea").getFloat("fontSize"));
		setTextAlign(BitmapFont.Align.valueOf(screen.getStyle("ScrollArea").getString("textAlign")));
		setTextVAlign(BitmapFont.VAlign.valueOf(screen.getStyle("ScrollArea").getString("textVAlign")));
		setTextWrap(LineWrapMode.valueOf(screen.getStyle("ScrollArea").getString("textWrap")));
		
		scrollBarGap = screen.getStyle("ScrollArea#VScrollBar").getFloat("gap");
		scrollSize = screen.getStyle("ScrollArea#VScrollBar").getFloat("defaultControlSize");
		
	//	orgWidth = getWidth();
		
		if (!(this instanceof Menu)) setWidth(getWidth()-scrollSize);
		
		if (!isTextOnly) {
			createScrollableArea();
		} else {
			setTextPaddingByKey("ScrollArea","textPadding");
			setTextClipPaddingByKey("ScrollArea","textPadding");
			setText(" ");
		}
		
		vScrollBar = new VScrollBar(screen, UID + ":vScroll",
			new Vector2f(getWidth() + scrollBarGap, 0),
			new Vector2f(scrollSize, getHeight())
		);
		
		addChild(vScrollBar);
		
		setVScrollBar(vScrollBar);
		
		flingTimer = new GameTimer() {
			@Override
			public void timerUpdateHook(float tpf) {
				float currentY = getScrollablePosition();
				float nextInc = 15*flingSpeed*(1f-this.getPercentComplete());
				
				if (flingDir) {
					float nextY = currentY+nextInc;
					if (nextY <= getScrollableHeight() && nextY >= getHeight()-getPadding()) {
						scrollYTo(nextY);
						vScrollBar.setThumbByPosition();
					}
				} else {
					float nextY = currentY-nextInc;
					if (nextY <= getScrollableHeight() && nextY >= getHeight()-getPadding()) {
						scrollYTo(nextY);
						vScrollBar.setThumbByPosition();
					}
				}
			}
			@Override
			public void onComplete(float time) {
				
			}
		};
		flingTimer.setInterpolation(Interpolation.exp5Out);
	}
	
	public void setScrollBarWidth(float width) {
		this.scrollSize = width;
		this.getVScrollBar().setWidth(width);
		this.getVScrollBar().getScrollTrack().setWidth(width);
		this.getVScrollBar().getScrollTrack().setHeight(getHeight()-(width*2));
		this.getVScrollBar().getScrollTrack().setY(width);
		this.getVScrollBar().getScrollButtonUp().setDimensions(width, width);
		this.getVScrollBar().getScrollButtonUp().setY(getHeight()-width);
		this.getVScrollBar().getScrollThumb().setWidth(width);
		this.getVScrollBar().getScrollButtonDown().setDimensions(width, width);
		
		this.getVScrollBar().getScrollButtonUp().getButtonIcon().centerToParent();
		this.getVScrollBar().getScrollButtonDown().getButtonIcon().centerToParent();
		
		if (getVScrollBar().getIsVisible())
			this.setWidth(this.orgDimensions.x-width);
		else
			this.setWidth(this.orgDimensions.x);
	//	this.adjustWidthForScroll();
	}
	
	private void createScrollableArea() {
		scrollableArea = new Element(screen, getUID() + ":scrollable", new Vector2f(0, 0), new Vector2f(getWidth(), 25), new Vector4f(14,14,14,14), null);
		scrollableArea.setIsResizable(false);
		scrollableArea.setIsMovable(false);
		scrollableArea.setScaleEW(true);
		scrollableArea.setScaleNS(false);
		scrollableArea.setDocking(Docking.NW);
		
		// Load default font info
		scrollableArea.setFontColor(screen.getStyle("ScrollArea").getColorRGBA("fontColor"));
		scrollableArea.setFontSize(screen.getStyle("ScrollArea").getFloat("fontSize"));
		scrollableArea.setTextAlign(BitmapFont.Align.valueOf(screen.getStyle("ScrollArea").getString("textAlign")));
		scrollableArea.setTextVAlign(BitmapFont.VAlign.valueOf(screen.getStyle("ScrollArea").getString("textVAlign")));
		scrollableArea.setTextWrap(LineWrapMode.valueOf(screen.getStyle("ScrollArea").getString("textWrap")));
		scrollableArea.setTextPaddingByKey("ScrollArea","textPadding");
		scrollableArea.setTextClipPaddingByKey("ScrollArea","textPadding");
		
		scrollableArea.addClippingLayer(this);
	//	scrollableArea.setTextClipPadding(screen.getStyle("ScrollArea").getFloat("scrollAreaPadding"));
		
		this.addChild(scrollableArea);
	}
	
	/**
	 * Returns the Element that was created as a scrollable area for ScrollArea NOT flagged as isTextOnly
	 * @return Element
	 */
	public Element getScrollableArea() {
		return this.scrollableArea;
	}
	
	/**
	 * Returns if the ScrollArea is text only
	 * @return boolean
	 */
	public boolean getIsTextOnly() {
		return isTextOnly;
	}
	
	public void setFlingEnabled(boolean enabled) {
		this.flingEnabled = enabled;
	}
	
	public boolean getFlingEnabled() { return this.flingEnabled; }
	
	private void setVScrollBar(VScrollBar vScrollBar) {
		this.vScrollBar = vScrollBar;
		vScrollBar.setScrollableArea(this);
	}
	
	/**
	 * Returns the Vertical Scroll Bar
	 * @return VScrollBar
	 */
	public VScrollBar getVScrollBar() {
		return this.vScrollBar;
	}
	
	/**
	 * Adds an Element as a child to the ScrollArea.  This is usable by ScrollAreas NOT flagged for isTextOnly
	 * @param child Element
	 */
	public void addScrollableChild(Element child) {
		scrollableArea.addChild(child);
		child.setIgnoreMouseWheel(true);
	}
	
	/**
	 * Sets the padding for the ScrollArea
	 * @param padding float 
	 */
	public void setPadding(float padding) {
		if (isTextOnly) {
			setTextPadding(padding);
			setTextClipPadding(padding);
		} else {
			scrollableArea.setTextPadding(padding);
			scrollableArea.setTextClipPadding(padding);
		}
	}
	
	/**
	 * Returns the padding used for the ScollArea
	 * @return float
	 */
	public float getPadding() {
		if (isTextOnly) {
			return getTextPadding();
		} else {
			return scrollableArea.getTextPadding();
		}
	}
	
	/**
	 * Returns the current height of the scrollable area
	 * @return float
	 */
	public float getScrollableHeight() {
		if (isTextOnly) {
			return textElement.getHeight()+getTextPaddingVec().z+getTextPaddingVec().w;
		} else {
			return scrollableArea.getHeight()+(scrollableArea.getTextPadding()*2);
		}
	}
	
	/**
	 * Returns the current y position of the scrollable area
	 * @return float
	 */
	public float getScrollablePosition() {
		if (isTextOnly) {
			return textElement.getLocalTranslation().y;
		} else {
			return scrollableArea.getY();
		}
	}
	
	@Override
	public void controlResizeHook() {
		if (vScrollBar != null) {
			vScrollBar.setThumbScale();
		}
		adjustWidthForScroll();
		if (scrollableArea != null)
			if (scrollableArea.getY() > getResizeBorderNorthSize() && getScrollableHeight()+getResizeBorderNorthSize() > getHeight())
				scrollToBottom();
	}
	
	@Override
	public void setControlClippingLayer(Element clippingLayer) {
		for (Element el : elementChildren.values()) {
			el.setControlClippingLayer(clippingLayer);
		}
	}
	
	/**
	 * Internal use - Used to readjust the width of the scrollarea when hiding/showing scroll bars
	 */
	public final void adjustWidthForScroll() {
		if (vScrollBar.getParent() == null && !scrollHidden) {
			setWidth(getWidth()+vScrollBar.getWidth() + scrollBarGap);
			scrollHidden = true;
		} else if (vScrollBar.getParent() != null && scrollHidden) {
			setWidth(getWidth()-vScrollBar.getWidth() - scrollBarGap);
			vScrollBar.setX(getWidth() + scrollBarGap);
			scrollHidden = false;
		}
        onAdjustWidthForScroll();
	}
	
	/**
	 * Scrolls the scrollbar thumb to the specified Y coord
	 * @param y 
	 */
	public void scrollThumbYTo(float y) {
		adjustWidthForScroll();
		vScrollBar.scrollYTo(y);
	}
	
	/**
	 * Scrolls the Scrollable Area to the specified Y coord
	 * @param y float
	 */
	public void scrollYTo(float y) {
		adjustWidthForScroll();
		if (scrollableArea == null) {
			textElement.setLocalTranslation(textElement.getLocalTranslation().setY(y));
		} else {
			scrollableArea.setY(0);
			scrollableArea.setY(y);
		}
		controlScrollHook();
	}
	
	/**
	 * Overridable method for hooking the scroll event
	 */
	public void controlScrollHook() {  }
	
	/**
	 * To be used with interval calls.  Scrolls the Scrollable Area by the provided value
	 * @param yInc float
	 */
	public void scrollYBy(float yInc) {
		adjustWidthForScroll();
		if (scrollableArea == null) {
			float nextY = textElement.getLocalTranslation().getY() + yInc;
			textElement.setLocalTranslation(textElement.getLocalTranslation().setY(yInc));
		} else {
			scrollableArea.setY(scrollableArea.getY()+yInc);
		}
	}
	
	/**
	 * Scrolls to the bottom of the Scrollable Area
	 */
	public void scrollToBottom() {
		adjustWidthForScroll();
		vScrollBar.scrollToBottom();
	}
	
	/**
	 * Scrolls to the top of the Scrollable Area
	 */
	public void scrollToTop() {
		adjustWidthForScroll();
		vScrollBar.scrollToTop();
	}

	@Override
	public void onMouseWheelPressed(MouseButtonEvent evt) {
		evt.setConsumed();}
	@Override
	public void onMouseWheelReleased(MouseButtonEvent evt) {
		evt.setConsumed();}
	@Override
	public void onMouseWheelUp(MouseMotionEvent evt) {
		if (vScrollBar != null) {
			vScrollBar.scrollByYInc(-vScrollBar.getTrackInc());
		}
		evt.setConsumed();
	}
	@Override
	public void onMouseWheelDown(MouseMotionEvent evt) {
		if (vScrollBar != null) {
			vScrollBar.scrollByYInc(vScrollBar.getTrackInc());
		}
		evt.setConsumed();
	}
        
	protected void onAdjustWidthForScroll() {
		// Hook called when width adjusts because of scrollbar visibility
	}

	@Override
	public void onFling(TouchEvent evt) {
		if (flingEnabled && (evt.getDeltaY() > 0.2f || evt.getDeltaY() < -0.2f)) {
			if (!screen.getAnimManager().hasGameTimer(flingTimer)) {
				flingTimer.reset(false);
				flingDir  = (evt.getDeltaY() < 0) ? true : false;
				flingSpeed = FastMath.abs(evt.getDeltaY());
				screen.getAnimManager().addGameTimer(flingTimer);
			}
		}
	}

	@Override
	public void onTouchDown(TouchEvent evt) {
		if (screen.getAnimManager().hasGameTimer(flingTimer)) {
			flingTimer.endGameTimer();
			screen.getAnimManager().removeGameTimer(flingTimer);
		}
		if (flingEnabled) {
			touchStartY = getScrollablePosition();
			touchOffsetY = evt.getY()-touchStartY;
		}
	}

	@Override
	public void onTouchMove(TouchEvent evt) {
		if (flingEnabled) {
			float nextY = evt.getY()-touchOffsetY;
			if (nextY <= getScrollableHeight() && nextY >= getHeight()-(this.getPadding())) {
				scrollYTo(nextY);
				vScrollBar.setThumbByPosition();
				touchEndY = getScrollablePosition();
			}
		}
	}

	@Override
	public void onTouchUp(TouchEvent evt) {
		
	}
}
 public abstract class DialogBox extends AlertBox {
	private ButtonAdapter btnCancel;
	
	/**
	 * Creates a new instance of the DialogBox control
	 * 
	 * @param screen The screen control the Element is to be added to
	 */
	public DialogBox(ElementManager screen) {
		this(screen, UIDUtil.getUID(), Vector2f.ZERO,
			screen.getStyle("Window").getVector2f("defaultSize"),
			screen.getStyle("Window").getVector4f("resizeBorders"),
			screen.getStyle("Window").getString("defaultImg")
		);
	}
	
	/**
	 * Creates a new instance of the DialogBox control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param position A Vector2f containing the x/y position of the Element
	 */
	public DialogBox(ElementManager screen, Vector2f position) {
		this(screen, UIDUtil.getUID(), position,
			screen.getStyle("Window").getVector2f("defaultSize"),
			screen.getStyle("Window").getVector4f("resizeBorders"),
			screen.getStyle("Window").getString("defaultImg")
		);
	}
	
	/**
	 * Creates a new instance of the AlertBox control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param position A Vector2f containing the x/y position of the Element
	 * @param dimensions A Vector2f containing the width/height dimensions of the Element
	 */
	public DialogBox(ElementManager screen, Vector2f position, Vector2f dimensions) {
		this(screen, UIDUtil.getUID(), position, dimensions,
			screen.getStyle("Window").getVector4f("resizeBorders"),
			screen.getStyle("Window").getString("defaultImg")
		);
	}
	
	/**
	 * Creates a new instance of the Dialog control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param position A Vector2f containing the x/y position of the Element
	 * @param dimensions A Vector2f containing the width/height dimensions of the Element
	 * @param resizeBorders A Vector4f containg the border information used when resizing the default image (x = N, y = W, z = E, w = S)
	 * @param defaultImg The default image to use for the DialogBox window
	 */
	public DialogBox(ElementManager screen, Vector2f position, Vector2f dimensions, Vector4f resizeBorders, String defaultImg) {
		this(screen, UIDUtil.getUID(), position, dimensions, resizeBorders, defaultImg);
	}
	
	/**
	 * Creates a new instance of the DialogBox control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param UID A unique String identifier for the Element
	 * @param position A Vector2f containing the x/y position of the Element
	 */
	public DialogBox(ElementManager screen, String UID, Vector2f position) {
		this(screen, UID, position,
			screen.getStyle("Window").getVector2f("defaultSize"),
			screen.getStyle("Window").getVector4f("resizeBorders"),
			screen.getStyle("Window").getString("defaultImg")
		);
	}
	
	/**
	 * Creates a new instance of the AlertBox control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param UID A unique String identifier for the Element
	 * @param position A Vector2f containing the x/y position of the Element
	 * @param dimensions A Vector2f containing the width/height dimensions of the Element
	 */
	public DialogBox(ElementManager screen, String UID, Vector2f position, Vector2f dimensions) {
		this(screen, UID, position, dimensions,
			screen.getStyle("Window").getVector4f("resizeBorders"),
			screen.getStyle("Window").getString("defaultImg")
		);
	}
	
	/**
	 * Creates a new instance of the Dialog control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param UID A unique String identifier for the Element
	 * @param position A Vector2f containing the x/y position of the Element
	 * @param dimensions A Vector2f containing the width/height dimensions of the Element
	 * @param resizeBorders A Vector4f containg the border information used when resizing the default image (x = N, y = W, z = E, w = S)
	 * @param defaultImg The default image to use for the DialogBox window
	 */
	public DialogBox(ElementManager screen, String UID, Vector2f position, Vector2f dimensions, Vector4f resizeBorders, String defaultImg) {
		super(screen, UID, position, dimensions, resizeBorders, defaultImg);
		
		Vector4f indents = screen.getStyle("Window").getVector4f("contentIndents");
		
		btnCancel = new ButtonAdapter(screen, UID + ":btnCancel",
			new Vector2f(
				indents.y,
				contentArea.getHeight()-screen.getStyle("Button").getVector2f("defaultSize").y-indents.w
			)
		) {
			@Override
			public void onButtonMouseLeftUp(MouseButtonEvent evt, boolean toggled) {
				onButtonCancelPressed(evt, toggled);
			}
		};
		btnCancel.setText("Cancel");
		btnCancel.setDocking(Docking.SW);
		addWindowContent(btnCancel);
		form.addFormElement(btnCancel);
	}
	
	public ButtonAdapter getButtonCancel() {
		return this.btnCancel;
	}
	
	/**
	 * Sets the text of the Cancel button
	 * @param text String
	 */
	public void setButtonCancelText(String text) {
		btnCancel.setText(text);
	}
	
	/**
	 * Abstract method for handling Cancel button click event
	 * @param evt MouseButtonEvent
	 * @param toggled boolean
	 */
	public abstract void onButtonCancelPressed(MouseButtonEvent evt, boolean toggled);
	
	/**
	 * Sets the tooltip text to display when mouse hovers over the Cancel button
	 * @param tip String
	 */
	public void setToolTipCancelButton(String tip) {
		this.btnCancel.setToolTipText(tip);
	}
}
 public class Panel extends Element {
	/**
	 * Creates a new instance of the Panel control
	 * 
	 * @param screen The screen control the Element is to be added to
	 */
	public Panel(ElementManager screen) {
		this(screen, UIDUtil.getUID(), Vector2f.ZERO,
			screen.getStyle("Window").getVector2f("defaultSize"),
			screen.getStyle("Window").getVector4f("resizeBorders"),
			screen.getStyle("Window").getString("defaultImg")
		);
	}
	
	/**
	 * Creates a new instance of the Panel control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param position A Vector2f containing the x/y position of the Element
	 */
	public Panel(ElementManager screen, Vector2f position) {
		this(screen, UIDUtil.getUID(), position,
			screen.getStyle("Window").getVector2f("defaultSize"),
			screen.getStyle("Window").getVector4f("resizeBorders"),
			screen.getStyle("Window").getString("defaultImg")
		);
	}
	
	/**
	 * Creates a new instance of the Panel control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param position A Vector2f containing the x/y position of the Element
	 * @param dimensions A Vector2f containing the width/height dimensions of the Element
	 */
	public Panel(ElementManager screen, Vector2f position, Vector2f dimensions) {
		this(screen, UIDUtil.getUID(), position, dimensions,
			screen.getStyle("Window").getVector4f("resizeBorders"),
			screen.getStyle("Window").getString("defaultImg")
		);
	}
	
	/**
	 * Creates a new instance of the Panel control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param position A Vector2f containing the x/y position of the Element
	 * @param dimensions A Vector2f containing the width/height dimensions of the Element
	 * @param resizeBorders A Vector4f containg the border information used when resizing the default image (x = N, y = W, z = E, w = S)
	 * @param defaultImg The default image to use for the Panel
	 */
	public Panel(ElementManager screen, Vector2f position, Vector2f dimensions, Vector4f resizeBorders, String defaultImg) {
		this(screen, UIDUtil.getUID(), position, dimensions, resizeBorders, defaultImg);
	}
	
	/**
	 * Creates a new instance of the Panel control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param UID A unique String identifier for the Element
	 * @param position A Vector2f containing the x/y position of the Element
	 */
	public Panel(ElementManager screen, String UID, Vector2f position) {
		this(screen, UID, position,
			screen.getStyle("Window").getVector2f("defaultSize"),
			screen.getStyle("Window").getVector4f("resizeBorders"),
			screen.getStyle("Window").getString("defaultImg")
		);
	}
	
	/**
	 * Creates a new instance of the Panel control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param UID A unique String identifier for the Element
	 * @param position A Vector2f containing the x/y position of the Element
	 * @param dimensions A Vector2f containing the width/height dimensions of the Element
	 */
	public Panel(ElementManager screen, String UID, Vector2f position, Vector2f dimensions) {
		this(screen, UID, position, dimensions,
			screen.getStyle("Window").getVector4f("resizeBorders"),
			screen.getStyle("Window").getString("defaultImg")
		);
	}
	
	/**
	 * Creates a new instance of the Panel control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param UID A unique String identifier for the Element
	 * @param position A Vector2f containing the x/y position of the Element
	 * @param dimensions A Vector2f containing the width/height dimensions of the Element
	 * @param resizeBorders A Vector4f containg the border information used when resizing the default image (x = N, y = W, z = E, w = S)
	 * @param defaultImg The default image to use for the Panel
	 */
	public Panel(ElementManager screen, String UID, Vector2f position, Vector2f dimensions, Vector4f resizeBorders, String defaultImg) {
		super(screen, UID, position, dimensions, resizeBorders, defaultImg);
		
		this.setIsMovable(true);
		this.setIsResizable(true);
		this.setScaleNS(false);
		this.setScaleEW(false);
		this.setClipPadding(screen.getStyle("Window").getFloat("clipPadding"));
		
		populateEffects("Window");
	}
}
 public abstract class TabControl extends Element {
	private Orientation orientation = Orientation.HORIZONTAL;
	protected List<Button> tabs = new ArrayList();
	protected Map<Integer,ITabPanel> tabPanels = new HashMap();
	protected int tabButtonIndex = 0;
	protected float tabWidth, tabHeight, tabInc;
	protected RadioButtonGroup tabButtonGroup;
	protected Vector4f tabResizeBorders;
	protected SlideTray tabSlider;
	protected boolean isFixedTabSize = false;
	protected float fixedTabSize = 0;
	protected float tabTraySize, tabTrayOverlap = 3;
	protected float tabOverhang = 6;
	protected float labelPadding = 24;
	
	private Vector2f pos = new Vector2f(),
					dim = new Vector2f();
	
	/**
	 * Creates a new instance of the TabControl control
	 * 
	 * @param screen The screen control the Element is to be added to
	 */
	public TabControl(ElementManager screen) {
		this(screen, UIDUtil.getUID(), Vector2f.ZERO,
			screen.getStyle("Window").getVector2f("defaultSize"),
			screen.getStyle("Window").getVector4f("resizeBorders"),
			null,
			Orientation.HORIZONTAL
		);
	}
	
	/**
	 * Creates a new instance of the TabControl control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param position A Vector2f containing the x/y position of the Element
	 */
	public TabControl(ElementManager screen, Vector2f position) {
		this(screen, UIDUtil.getUID(), position,
			screen.getStyle("Window").getVector2f("defaultSize"),
			screen.getStyle("Window").getVector4f("resizeBorders"),
			null,
			Orientation.HORIZONTAL
		);
	}
	
	/**
	 * Creates a new instance of the TabControl control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param position A Vector2f containing the x/y position of the Element
	 * @param dimensions A Vector2f containing the width/height dimensions of the Element
	 */
	public TabControl(ElementManager screen, Vector2f position, Vector2f dimensions) {
		this(screen, UIDUtil.getUID(), position, dimensions,
			screen.getStyle("Window").getVector4f("resizeBorders"),
			null,
			Orientation.HORIZONTAL
		);
	}
	
	/**
	 * Creates a new instance of the TabControl control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param position A Vector2f containing the x/y position of the Element
	 * @param dimensions A Vector2f containing the width/height dimensions of the Element
	 * @param resizeBorders A Vector4f containg the border information used when resizing the default image (x = N, y = W, z = E, w = S)
	 * @param defaultImg The default image to use for the TabControl background
	 */
	public TabControl(ElementManager screen, Vector2f position, Vector2f dimensions, Vector4f resizeBorders, String defaultImg) {
		this(screen, UIDUtil.getUID(), position, dimensions, resizeBorders, defaultImg,
			Orientation.HORIZONTAL);
	}
	
	/**
	 * Creates a new instance of the TabControl control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param UID A unique String identifier for the Element
	 * @param position A Vector2f containing the x/y position of the Element
	 */
	public TabControl(ElementManager screen, String UID, Vector2f position) {
		this(screen, UID, position,
			screen.getStyle("Window").getVector2f("defaultSize"),
			screen.getStyle("Window").getVector4f("resizeBorders"),
			null,
			Orientation.HORIZONTAL
		);
	}
	
	/**
	 * Creates a new instance of the TabControl control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param UID A unique String identifier for the Element
	 * @param position A Vector2f containing the x/y position of the Element
	 * @param dimensions A Vector2f containing the width/height dimensions of the Element
	 */
	public TabControl(ElementManager screen, String UID, Vector2f position, Vector2f dimensions) {
		this(screen, UID, position, dimensions,
			screen.getStyle("Window").getVector4f("resizeBorders"),
			null,
			Orientation.HORIZONTAL
		);
	}
	
	/**
	 * Creates a new instance of the TabControl control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param UID A unique String identifier for the Element
	 * @param position A Vector2f containing the x/y position of the Element
	 * @param dimensions A Vector2f containing the width/height dimensions of the Element
	 * @param resizeBorders A Vector4f containg the border information used when resizing the default image (x = N, y = W, z = E, w = S)
	 * @param defaultImg The default image to use for the TabControl background
	 */
	public TabControl(ElementManager screen, String UID, Vector2f position, Vector2f dimensions, Vector4f resizeBorders, String defaultImg, Orientation orientation) {
		super(screen, UID, position, dimensions, resizeBorders, defaultImg);
		
		this.orientation = orientation;
		
		this.setIsMovable(false);
		this.setIsResizable(false);
		this.setDocking(Docking.NW);
		this.setScaleNS(true);
		this.setScaleEW(true);
		this.setClipPadding(screen.getStyle("Window").getFloat("clipPadding"));
		
		configTabControl();
	}
	
	private void configTabControl() {
		if (orientation == Orientation.HORIZONTAL) {
			tabWidth = screen.getStyle("Tab").getVector2f("defaultSize").x;
			tabHeight = screen.getStyle("Tab").getVector2f("defaultSize").y;
			tabResizeBorders = screen.getStyle("Tab").getVector4f("resizeBorders");
			tabInc = screen.getStyle("Tab").getVector2f("defaultSize").x-screen.getStyle("Tab").getFloat("tabOverhang");
		} else {
			tabWidth = screen.getStyle("Tab").getVector2f("defaultSize").y;
			tabHeight = screen.getStyle("Tab").getVector2f("defaultSize").x;
			tabResizeBorders = screen.getStyle("Tab").getVector4f("resizeBorders");
			tabInc = screen.getStyle("Tab").getVector2f("defaultSize").y-screen.getStyle("Tab").getFloat("tabOverhang");
		}
		
		tabOverhang = screen.getStyle("Tab").getFloat("tabOverhang");
		labelPadding = screen.getStyle("Tab").getFloat("labelPadding");
		tabTrayOverlap = screen.getStyle("Tab").getFloat("tabTrayOverlap");
		
		tabButtonGroup = new RadioButtonGroup(screen, getUID() + ":TabButtonGroup") {
			@Override
			public void onSelect(int index, Button value) {
				Set<Integer> keys = tabPanels.keySet();
				Element selectedPanel = (Element)tabPanels.get(index);
				Button selectedTab = tabs.get(index);
				tabSlider.resort(selectedTab);
				if (tabTrayOverlap != 0) {
					for (Button b : tabs) {
						if (b != selectedTab) {
							if (orientation == Orientation.HORIZONTAL) {
								b.setHeight(tabHeight-tabTrayOverlap);
								b.setY(tabSlider.getHeight()-b.getHeight());
							} else {
								b.setWidth(tabWidth-tabTrayOverlap);
							}
						} else {
							if (orientation == Orientation.HORIZONTAL) {
								b.setHeight(tabHeight);
								b.setY(tabSlider.getHeight()-b.getHeight());
							} else {
								b.setWidth(tabWidth);
							}
						}
					}
				}
				for (Integer key : keys) {
					if (key != index) {
						((Element)tabPanels.get(key)).hide();
					}
				}
				selectedPanel.show();
				selectedTab.removeFromParent();
				selectedTab.getElementParent().attachChild(selectedTab);
				selectedPanel.hide();
				selectedPanel.show();
				onTabSelect(index);
			}
		};
		
		float btnInset = 20;
		if (orientation == Orientation.HORIZONTAL) {
			tabSlider = new SlideTray(screen, getUID() + ":tabSlider",
				new Vector2f(btnInset,0),
				new Vector2f(getWidth()-(btnInset*2),(btnInset*2)),
				Orientation.HORIZONTAL
			) {
				@Override
				public void controlResizeHook() {
					this.updateClippingLayers();
				}
			};
//			tabSlider.setTrayPadding(-(tabResizeBorders.z/5*2));
			tabSlider.setTrayPadding(-tabOverhang);
			tabSlider.setZOrderSorting(ZOrderSort.LAST_TO_FIRST);
			tabSlider.setButtonSize(tabHeight);
			tabSlider.alignButtonsV(VAlign.Top);
		} else {
			tabSlider = new SlideTray(screen, getUID() + ":tabSlider",
				new Vector2f(0,btnInset),
				new Vector2f((btnInset*2),getHeight()-(btnInset*2)),
				Orientation.VERTICAL
			) {
				@Override
				public void controlResizeHook() {
					this.updateClippingLayers();
				}
			};
			tabSlider.setTrayPadding(-tabOverhang);
//			tabSlider.setTrayPadding(-(tabResizeBorders.z/5*2));
			tabSlider.setZOrderSorting(ZOrderSort.FIRST_TO_LAST);
			tabSlider.setButtonSize(tabWidth);
			tabSlider.alignButtonsH(Align.Left);
		}
		addChild(tabSlider);
	}
	
	public abstract void onTabSelect(int index);
	
	public Orientation getOrientation() {
		return this.orientation;
	}
	
	/**
	 * Sets the resize borders for use with ElementQuadGrid per tab
	 * @param tabResizeBorders 
	 */
	public void setTabResizeBorders(Vector4f tabResizeBorders) {
		this.tabResizeBorders.set(tabResizeBorders);
	}
	
	
	/**
	 * Sets the width to always use for Tabs
	 * @param fixedTabWidth float Forced width of all Tabs
	 */
	public void setFixedTabSize(float fixedTabWidth) {
		if (fixedTabWidth > 0) {
			isFixedTabSize = true;
			this.fixedTabSize = fixedTabWidth;
		} else {
			isFixedTabSize = false;
			this.fixedTabSize = 0;
		}
	}
	
	/**
	 * Sets the default tab height
	 * @param tabHeight 
	 */
	public void setTabSize(float size) {
		if (orientation == Orientation.HORIZONTAL)
			this.tabHeight = size;
		else
			this.tabWidth = size;
	}
	
	/**
	 * Enables the SliderToEffect of the SlideTray containing the tabs
	 * @param useSlideEffect boolean
	 */
	public void setUseSlideEffect(boolean useSlideEffect) {
		tabSlider.setUseSlideEffect(useSlideEffect);
	}
	
	/**
	 * Adds a new Tab and TabPanel to the TabControl
	 * @param title String The Title to set for the Tab
	 */
	private ButtonAdapter getTabButton() {
		if (orientation == Orientation.HORIZONTAL) {
			pos.set(tabInc*tabButtonIndex,0);
			dim.set(screen.getStyle("Tab").getVector2f("defaultSize"));
		} else {
			pos.set(0,tabInc*tabButtonIndex);
			dim.set(screen.getStyle("Tab").getVector2f("defaultSize").y,screen.getStyle("Tab").getVector2f("defaultSize").x);
		}
		ButtonAdapter tab = new ButtonAdapter(
			screen,
			getUID() + ":Tab" + tabButtonIndex,
			pos,
			dim,
			(orientation == Orientation.HORIZONTAL) ? screen.getStyle("Tab").getVector4f("resizeBorders") : screen.getStyle("Tab").getVector4f("resizeBordersV"),
			(orientation == Orientation.HORIZONTAL) ? screen.getStyle("Tab").getString("defaultImg") : screen.getStyle("Tab").getString("defaultImgV")
		);
		tab.setDocking(Docking.NW);
		tab.setScaleEW(false);
		tab.setScaleNS(false);
		
		return tab;
	}
	
	private LabelElement getButtonLabel(String title) {
		LabelElement label = new LabelElement(screen);
		label.setText(title);
		label.setSizeToText(true);
		label.setUseTextClipping(true);
		label.setDocking(Docking.SW);
		return label;
	}
	
	public void addTab(String title) {
		ButtonAdapter tab = getTabButton();
		addTab(title, tab, false, false);
	}
	
	public void addTab(String title, ButtonAdapter tab) {
		addTab(title, tab, false, true);
	}
	
	public void addTab(String title, boolean useScrollPanel) {
		ButtonAdapter tab = getTabButton();
		addTab(title, tab, useScrollPanel, false);
	}
	
	public void addTab(String title, ButtonAdapter tab, boolean useScrollPanel, boolean isCustomButton) {
		LabelElement label = getButtonLabel(title);
		AnimText txt = label.getAnimText();
		
		if (orientation == Orientation.VERTICAL) {
			txt.setPosition(-((txt.getLineWidth()/2)),(txt.getLineHeight())+(label.getHeight()/2));
			txt.setOrigin(txt.getLineWidth()/2,txt.getLineHeight()/2);
			txt.setRotation(90);
			label.setDimensions(label.getHeight(),label.getWidth());
			label.updateClippingLayers();
			txt.update(0);
		}
		
		if (isFixedTabSize) {
			tab.setWidth(fixedTabSize);
		} else {
			float width = BitmapTextUtil.getTextWidth(tab, title);
			if (orientation == Orientation.HORIZONTAL)
				tab.setWidth(Math.round(width+(labelPadding*2)+(tabResizeBorders.x+tabResizeBorders.z)));
			else
				tab.setHeight(Math.round(width+(labelPadding*2)+(tabResizeBorders.x+tabResizeBorders.z)));
		}
		
		if (!isCustomButton) {
			tab.clearAltImages();
			String hImg = (orientation == Orientation.HORIZONTAL) ? screen.getStyle("Tab").getString("hoverImg") : screen.getStyle("Tab").getString("hoverImgV");
			String pImg = (orientation == Orientation.HORIZONTAL) ? screen.getStyle("Tab").getString("pressedImg") : screen.getStyle("Tab").getString("pressedImgV");
			tab.setButtonHoverInfo(hImg, screen.getStyle("Tab").getColorRGBA("hoverColor"));
			tab.setButtonPressedInfo(pImg, screen.getStyle("Tab").getColorRGBA("pressedColor"));
		}
		
		tab.setElementUserData(tabButtonIndex);
		
		if (orientation == Orientation.VERTICAL) {
			tab.addChild(label);
                        
                        final Align hal = Align.valueOf(screen.getStyle("Tab").getString("textAlign"));
                        final VAlign val = VAlign.valueOf(screen.getStyle("Tab").getString("textVAlign"));
                        
                        // Flip the alignments
//                        switch(hal) {
//                            case Center:
//                                label.setTextVAlign(VAlign.Center);
//                                break;
//                            case Left:
//                                label.setTextVAlign(VAlign.Top);
//                                break;
//                            case Right:
//                                label.setTextVAlign(VAlign.Bottom);
//                                break;
//
//                        }
//                        switch(val) {
//                            case Center:
//                                label.setTextAlign(Align.Center);
//                                break;
//                            case Top:
//                                label.setTextAlign(Align.Left);
//                                break;
//                            case Bottom:
//                                label.setTextAlign(Align.Right);
//                                break;
//
//                        }
			label.centerToParent();
		} else {
                        
			tab.setText(title);
		}
		
		tabButtonGroup.addButton(tab);
		tabs.add(tab);
		
		if (orientation == Orientation.HORIZONTAL) {
			pos.set(0, tabHeight-tabTrayOverlap);
			dim.set(
				getDimensions().subtract(
					new Vector2f(0, tabHeight-tabTrayOverlap)
				)
			);
		} else {
			pos.set(tabWidth-tabTrayOverlap, 0);
			dim.set(
				getDimensions().subtract(
					new Vector2f(tabWidth-tabTrayOverlap, 0)
				)
			);
		}
		
		if (useScrollPanel) {
			addTabScrollPanel();
		} else {
			addTabPanel();
		}
		
		tabSlider.addTrayElement(tab);
		if (tabButtonIndex == 0)
			tab.setIsToggled(true);
		
		tab.addClippingLayer(tab);
		label.addClippingLayer(tab);
		label.removeClippingLayer(label);
		
		if (orientation == Orientation.VERTICAL)
			tab.resetTextElement();
		
		tabButtonIndex++;
	}
	
	private void addTabPanel() {
		TabPanel panel = new TabPanel(
			screen,
			getUID() + ":TabPanel" + tabButtonIndex,
			pos,
			dim
		);
		addChild(panel);
		tabPanels.put(tabButtonIndex,panel);
		
		if (tabButtonIndex != 0)
			panel.hide();
	}
	
	private void addTabScrollPanel() { 
		TabScrollPanel panel = new TabScrollPanel(
			screen,
			getUID() + ":TabPanel" + tabButtonIndex,
			pos,
			dim
		);
		addChild(panel);
		tabPanels.put(tabButtonIndex,panel);
		
		if (tabButtonIndex != 0)
			panel.hide();
	}
	
	public void setSelectedTab(int index) {
	//	if (tabButtonGroup.)
		tabButtonGroup.onSelect(index, tabs.get(index));
		tabSlider.resort(tabs.get(index));
	}
	
	/**
	 * Adds the provided Element to the panel associated with the tab index
	 * @param index int Tab index
	 * @param element Element
	 */
	public void addTabChild(int index, Element element) {
		if (index > -1 && index < tabs.size()) {
			Element tabPanel = (Element)tabPanels.get(index);
			if (tabPanel instanceof ScrollPanel)
				((ScrollPanel)tabPanel).addScrollableContent(element);
			else {
				tabPanel.addChild(element);
				element.addClippingLayer(tabPanel);
			}
		}
	}
	
	public void setTabPanelLayout(int index, Layout layout) {
		Element tabPanel = (Element)tabPanels.get(index);
		if (tabPanel instanceof ScrollPanel)
			((ScrollPanel)tabPanel).getScrollableArea().setLayout(layout);
		else
			tabPanel.setLayout(layout);
	}
	
	public void layoutTabPanelChildren(int index) {
		Element tabPanel = (Element)tabPanels.get(index);
		if (tabPanel instanceof ScrollPanel)
			((ScrollPanel)tabPanel).getScrollableArea().layoutChildren();
		else
			tabPanel.layoutChildren();
	}
	
	public interface ITabPanel {  }
	
	public class TabPanel extends Panel implements ITabPanel {
		/**
		 * Creates a new instance of the Panel control
		 * 
		 * @param screen The screen control the Element is to be added to
		 * @param UID A unique String identifier for the Element
		 * @param position A Vector2f containing the x/y position of the Element
		 */
		public TabPanel(ElementManager screen, String UID, Vector2f position) {
			this(screen, UID, position,
				screen.getStyle("Window").getVector2f("defaultSize"),
				screen.getStyle("Window").getVector4f("resizeBorders"),
				screen.getStyle("Tab").getString("panelImg")
			);
		}
		
		/**
		 * Creates a new instance of the Panel control
		 * 
		 * @param screen The screen control the Element is to be added to
		 * @param UID A unique String identifier for the Element
		 * @param position A Vector2f containing the x/y position of the Element
		 * @param dimensions A Vector2f containing the width/height dimensions of the Element
		 */
		public TabPanel(ElementManager screen, String UID, Vector2f position, Vector2f dimensions) {
			this(screen, UID, position, dimensions,
				screen.getStyle("Window").getVector4f("resizeBorders"),
				screen.getStyle("Tab").getString("panelImg")
			);
		}
		
		/**
		 * Creates a new instance of the Panel control
		 * 
		 * @param screen The screen control the Element is to be added to
		 * @param UID A unique String identifier for the Element
		 * @param position A Vector2f containing the x/y position of the Element
		 * @param dimensions A Vector2f containing the width/height dimensions of the Element
		 * @param resizeBorders A Vector4f containg the border information used when resizing the default image (x = N, y = W, z = E, w = S)
		 * @param defaultImg The default image to use for the Slider's track
		 */
		public TabPanel(ElementManager screen, String UID, Vector2f position, Vector2f dimensions, Vector4f resizeBorders, String defaultImg) {
			super(screen, UID, position, dimensions, resizeBorders, defaultImg);
			this.setIsMovable(false);
			this.setIsResizable(false);
			this.setScaleNS(true);
			this.setScaleEW(true);
		}
	}
	
	public class TabScrollPanel extends ScrollPanel implements ITabPanel {
		/**
		 * Creates a new instance of the Panel control
		 * 
		 * @param screen The screen control the Element is to be added to
		 * @param UID A unique String identifier for the Element
		 * @param position A Vector2f containing the x/y position of the Element
		 */
		public TabScrollPanel(ElementManager screen, String UID, Vector2f position) {
			this(screen, UID, position,
				screen.getStyle("Window").getVector2f("defaultSize"),
				screen.getStyle("Window").getVector4f("resizeBorders"),
				screen.getStyle("Tab").getString("panelImg")
			);
		}
		
		/**
		 * Creates a new instance of the Panel control
		 * 
		 * @param screen The screen control the Element is to be added to
		 * @param UID A unique String identifier for the Element
		 * @param position A Vector2f containing the x/y position of the Element
		 * @param dimensions A Vector2f containing the width/height dimensions of the Element
		 */
		public TabScrollPanel(ElementManager screen, String UID, Vector2f position, Vector2f dimensions) {
			this(screen, UID, position, dimensions,
				screen.getStyle("Window").getVector4f("resizeBorders"),
				screen.getStyle("Tab").getString("panelImg")
			);
		}
		
		/**
		 * Creates a new instance of the Panel control
		 * 
		 * @param screen The screen control the Element is to be added to
		 * @param UID A unique String identifier for the Element
		 * @param position A Vector2f containing the x/y position of the Element
		 * @param dimensions A Vector2f containing the width/height dimensions of the Element
		 * @param resizeBorders A Vector4f containg the border information used when resizing the default image (x = N, y = W, z = E, w = S)
		 * @param defaultImg The default image to use for the Slider's track
		 */
		public TabScrollPanel(ElementManager screen, String UID, Vector2f position, Vector2f dimensions, Vector4f resizeBorders, String defaultImg) {
			super(screen, UID, position, dimensions, resizeBorders, defaultImg);
			this.setIsMovable(false);
			this.setIsResizable(false);
			this.setScaleNS(true);
			this.setScaleEW(true);
		}
	}
}
 public class Window extends Element {
	protected Element dragBar;
	protected Element contentArea;
	protected ButtonAdapter close, collapse;
	private boolean useShowSound, useHideSound;
	private String showSound, hideSound;
	private float showSoundVolume, hideSoundVolume;
	protected Vector4f dbIndents = new Vector4f();
	private Window self;
	private boolean useClose = false, useCollapse = false, isCollapsed = false;
	private float winDif = 0;
	
	/**
	 * Creates a new instance of the Window control
	 * 
	 * @param screen The screen control the Element is to be added to
	 */
	public Window(ElementManager screen) {
		this(screen, UIDUtil.getUID(), Vector2f.ZERO,
			screen.getStyle("Window").getVector2f("defaultSize"),
			screen.getStyle("Window").getVector4f("resizeBorders"),
			screen.getStyle("Window").getString("defaultImg")
		);
	}
	
	/**
	 * Creates a new instance of the Window control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param position A Vector2f containing the x/y position of the Element
	 */
	public Window(ElementManager screen, Vector2f position) {
		this(screen, UIDUtil.getUID(), position,
			screen.getStyle("Window").getVector2f("defaultSize"),
			screen.getStyle("Window").getVector4f("resizeBorders"),
			screen.getStyle("Window").getString("defaultImg")
		);
	}
	
	/**
	 * Creates a new instance of the Window control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param position A Vector2f containing the x/y position of the Element
	 * @param dimensions A Vector2f containing the width/height dimensions of the Element
	 */
	public Window(ElementManager screen, Vector2f position, Vector2f dimensions) {
		this(screen, UIDUtil.getUID(), position, dimensions,
			screen.getStyle("Window").getVector4f("resizeBorders"),
			screen.getStyle("Window").getString("defaultImg")
		);
	}
	
	/**
	 * Creates a new instance of the Window control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param position A Vector2f containing the x/y position of the Element
	 * @param dimensions A Vector2f containing the width/height dimensions of the Element
	 * @param resizeBorders A Vector4f containg the border information used when resizing the default image (x = N, y = W, z = E, w = S)
	 * @param defaultImg The default image to use for the Window
	 */
	public Window(ElementManager screen, Vector2f position, Vector2f dimensions, Vector4f resizeBorders, String defaultImg) {
		this(screen, UIDUtil.getUID(), position, dimensions, resizeBorders, defaultImg);
	}
	
	/**
	 * Creates a new instance of the Window control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param UID A unique String identifier for the Element
	 * @param position A Vector2f containing the x/y position of the Element
	 */
	public Window(ElementManager screen, String UID, Vector2f position) {
		this(screen, UID, position,
			screen.getStyle("Window").getVector2f("defaultSize"),
			screen.getStyle("Window").getVector4f("resizeBorders"),
			screen.getStyle("Window").getString("defaultImg")
		);
	}
	
	/**
	 * Creates a new instance of the Window control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param UID A unique String identifier for the Element
	 * @param position A Vector2f containing the x/y position of the Element
	 * @param dimensions A Vector2f containing the width/height dimensions of the Element
	 */
	public Window(ElementManager screen, String UID, Vector2f position, Vector2f dimensions) {
		this(screen, UID, position, dimensions,
			screen.getStyle("Window").getVector4f("resizeBorders"),
			screen.getStyle("Window").getString("defaultImg")
		);
	}
	
	/**
	 * Creates a new instance of the Window control
	 * 
	 * @param screen The screen control the Element is to be added to
	 * @param UID A unique String identifier for the Element
	 * @param position A Vector2f containing the x/y position of the Element
	 * @param dimensions A Vector2f containing the width/height dimensions of the Element
	 * @param resizeBorders A Vector4f containg the border information used when resizing the default image (x = N, y = W, z = E, w = S)
	 * @param defaultImg The default image to use for the Slider's track
	 */
	public Window(ElementManager screen, String UID, Vector2f position, Vector2f dimensions, Vector4f resizeBorders, String defaultImg) {
		super(screen, UID, position, dimensions, resizeBorders, defaultImg);
		self = this;
		
		this.setIsResizable(true);
		this.setScaleNS(false);
		this.setScaleEW(false);
		this.setClipPadding(screen.getStyle("Window").getFloat("clipPadding"));
		this.setMinDimensions(screen.getStyle("Window").getVector2f("minSize"));
	//	this.setClippingLayer(this);
		
		dbIndents.set(screen.getStyle("Window#Dragbar").getVector4f("indents"));
		
		dragBar = new Element(screen, UID + ":DragBar",
			new Vector2f(dbIndents.y, dbIndents.x),
			new Vector2f(getWidth()-dbIndents.y-dbIndents.z, screen.getStyle("Window#Dragbar").getFloat("defaultControlSize")),
			screen.getStyle("Window#Dragbar").getVector4f("resizeBorders"),
			screen.getStyle("Window#Dragbar").getString("defaultImg")
		);
		dragBar.setFontSize(screen.getStyle("Window#Dragbar").getFloat("fontSize"));
		dragBar.setFontColor(screen.getStyle("Window#Dragbar").getColorRGBA("fontColor"));
		dragBar.setTextAlign(BitmapFont.Align.valueOf(screen.getStyle("Window#Dragbar").getString("textAlign")));
		dragBar.setTextVAlign(BitmapFont.VAlign.valueOf(screen.getStyle("Window#Dragbar").getString("textVAlign")));
		dragBar.setTextPosition(0,0);
		dragBar.setTextPaddingByKey("Window#Dragbar","textPadding");
		dragBar.setTextWrap(LineWrapMode.valueOf(screen.getStyle("Window#Dragbar").getString("textWrap")));
		dragBar.setIsResizable(false);
		dragBar.setScaleEW(true);
		dragBar.setScaleNS(false);
		dragBar.setIsMovable(true);
		dragBar.setEffectParent(true);
		dragBar.addClippingLayer(this);
		
		addChild(dragBar);
		
		float buttonHeight = (dragBar.getHeight() <= 25) ? 18 : dragBar.getHeight()-6;
	//	buttonHeight -= 2;
		
		close = new ButtonAdapter(screen, Vector2f.ZERO, new Vector2f(buttonHeight,buttonHeight)) {
			@Override
			public void onButtonMouseLeftUp(MouseButtonEvent evt, boolean toggled) {
				self.hideWindow();
			}
		};
		close.setText("X");
		close.setDocking(Docking.SE);
		
		collapse = new ButtonAdapter(screen, Vector2f.ZERO, new Vector2f(buttonHeight,buttonHeight)) {
			@Override
			public void onButtonMouseLeftUp(MouseButtonEvent evt, boolean toggled) {
				if (!isCollapsed) {
					isCollapsed = true;
					contentArea.hide();
					winDif = self.getHeight();
					self.setHeight(getDragBarHeight()+(dbIndents.y*2));
					winDif -= self.getHeight();
					dragBar.setY(self.getHeight()-dragBar.getHeight()-dbIndents.y);
					self.setY(self.getY()+winDif);
					setButtonIcon(getWidth(), getHeight(), screen.getStyle("Common").getString("arrowDown"));
					self.setResizeN(false);
					self.setResizeS(false);
				} else {
					isCollapsed = false;
					contentArea.show();
					self.setHeight(getDragBarHeight()+contentArea.getHeight()+dbIndents.y+2);
					dragBar.setY(self.getHeight()-dragBar.getHeight()-dbIndents.y);
					self.setY(self.getY()-winDif);setButtonIcon(getWidth(), getHeight(), screen.getStyle("Common").getString("arrowUp"));
					self.setResizeN(self.getIsResizable());
					self.setResizeS(self.getIsResizable());
				}
			}
		};
		collapse.setButtonIcon(collapse.getWidth(), collapse.getHeight(), screen.getStyle("Common").getString("arrowUp"));
		collapse.setDocking(Docking.SE);
		
		contentArea = ControlUtil.getContainer(screen);
		contentArea.setDimensions(dimensions.subtract(0, dragBar.getHeight()+dbIndents.y+2));
		contentArea.setPosition(0,dragBar.getHeight()+dbIndents.y);
		contentArea.setScaleEW(true);
		contentArea.setScaleNS(true);
		
		addChild(contentArea);
		
		showSound = screen.getStyle("Window").getString("showSound");
		useShowSound = screen.getStyle("Window").getBoolean("useShowSound");
		showSoundVolume = screen.getStyle("Window").getFloat("showSoundVolume");
		hideSound = screen.getStyle("Window").getString("hideSound");
		useHideSound = screen.getStyle("Window").getBoolean("useHideSound");
		hideSoundVolume = screen.getStyle("Window").getFloat("hideSoundVolume");
		
		populateEffects("Window");
	}
	
	/**
	 * Returns a pointer to the Element used as a window dragbar
	 * @return Element
	 */
	public Element getDragBar() {
		return this.dragBar;
	}
	
	/**
	 * Returns the drag bar height
	 * @return float
	 */
	public float getDragBarHeight() {
		return dragBar.getHeight();
	}
	
	/**
	 * Sets the Window title text
	 * @param title String
	 */
	public void setWindowTitle(String title) {
		dragBar.setText(title);
	}
	
	/**
	 * Shows the window using the default Show Effect
	 */
	public void showWindow() {
		Effect effect = getEffect(Effect.EffectEvent.Show);
		if (effect != null) {
			if (useShowSound && screen.getUseUIAudio()) {
				effect.setAudioFile(showSound);
				effect.setAudioVolume(showSoundVolume);
			}
			if (effect.getEffectType() == Effect.EffectType.FadeIn) {
				Effect clone = effect.clone();
				clone.setAudioFile(null);
				this.propagateEffect(clone, false);
			} else
				screen.getEffectManager().applyEffect(effect);
		} else
			this.show();
	}
	
	/**
	 * Hides the Window using the default Hide Effect
	 */
	public void hideWindow() {
		Effect effect = getEffect(Effect.EffectEvent.Hide);
		if (effect != null) {
			if (useHideSound && screen.getUseUIAudio()) {
				effect.setAudioFile(hideSound);
				effect.setAudioVolume(hideSoundVolume);
			}
			if (effect.getEffectType() == Effect.EffectType.FadeOut) {
				Effect clone = effect.clone();
				clone.setAudioFile(null);
				this.propagateEffect(clone, true);
			} else
				screen.getEffectManager().applyEffect(effect);
		} else
			this.hide();
	}
	
	/**
	 * Enables/disables the Window dragbar
	 * @param isMovable boolean
	 */
	public void setWindowIsMovable(boolean isMovable) {
		this.dragBar.setIsMovable(isMovable);
	}
	
	/**
	 * Returns if the Window dragbar is currently enabled/disabled
	 * @return boolean
	 */
	public boolean getWindowIsMovable() {
		return this.dragBar.getIsMovable();
	}
	
	public void addWindowContent(Element el) {
		contentArea.addChild(el);
//		contentArea.addClippingLayer(contentArea); // Cause issue when used with Button
	}
	
	public void removeWindowContent(Element el) {
		contentArea.removeChild(el);
	}
	
	public void setContentLayout(Layout layout) {
		contentArea.setLayout(layout);
	}
	
	public Element getContentArea() { return contentArea; }
	
	public void setUseCloseButton(boolean use) {
		if (use) {
			this.useClose = true;
			dragBar.addChild(close);
			close.centerToParentV();
			close.setX(dragBar.getWidth()-close.getWidth()-close.getY());
			if (useCollapse) {
				close.centerToParentV();
				close.setX(dragBar.getWidth()-close.getWidth()-collapse.getWidth()-collapse.getY()-5);
			}
		} else {
			this.useClose = false;
			dragBar.removeChild(close);
			if (useCollapse) {
				close.centerToParentV();
				close.setX(dragBar.getWidth()-collapse.getWidth()-collapse.getY());
			}
		}
	}
	
	public void setUseCollapseButton(boolean use) {
		if (use) {
			this.useCollapse = true;
			dragBar.addChild(collapse);
			collapse.centerToParentV();
			if (useClose)
				collapse.setX(dragBar.getWidth()-collapse.getWidth()-close.getWidth()-collapse.getY()-5);
			else
				collapse.setX(dragBar.getWidth()-collapse.getWidth()-collapse.getY());
		} else {
			this.useCollapse = false;
			dragBar.removeChild(collapse);
		}
	}
	
	public void sizeWindowToContent() {
		contentArea.sizeToContent();
		setDimensions(
			contentArea.getWidth()+(dbIndents.z),
			contentArea.getHeight()+getDragBarHeight()+(dbIndents.y+dbIndents.w)
		);
		dragBar.setY(dbIndents.y+contentArea.getHeight());
		dragBar.setWidth(getWidth()-(dbIndents.x+dbIndents.z));
	}
}
 public class Element extends Node {
	public static enum Borders {
		NW,
		N,
		NE,
		W,
		E,
		SW,
		S,
		SE;
	};
	/**
	 * Some controls provide different layout's based on the orientation of the control
	 */
	public static enum Orientation {
		/**
		 * Vertical layout
		 */
		VERTICAL,
		/**
		 * Horizontal layout
		 */
		HORIZONTAL
	}
	/**
	 * Defines how the element will dock to it's parent element during resize events
	 */
	public static enum Docking {
		/**
		 * Docks to the top left of parent
		 */
		NW,
		/**
		 * Docks to the top right of parent
		 */
		NE,
		/**
		 * Docks to the bottom left of parent
		 */
		SW,
		/**
		 * Docks to the bottom right of parent
		 */
		SE
	}
	
	//<editor-fold desc="Fields">
	protected Application app;
	protected ElementManager screen;
	private String UID;
	private Vector2f position = new Vector2f();
	public Vector2f orgPosition;
	private Vector2f dimensions = new Vector2f();
	public Vector2f orgDimensions, orgRelDimensions;
	public Vector4f borders = new Vector4f(1,1,1,1);
	public Vector4f borderHandles = new Vector4f(12,12,12,12);
	private Vector2f minDimensions = new Vector2f(10, 10);
	
	private boolean ignoreMouse = false;
	private boolean ignoreMouseLeftButton = false;
	private boolean ignoreMouseRightButton = false;
	private boolean ignoreMouseWheelClick = true;
	private boolean ignoreMouseWheelMove = true;
	private boolean ignoreMouseFocus = false;
	private boolean ignoreTouch = false;
	private boolean ignoreTouchMove = false;
	private boolean ignoreFling = false;
	protected boolean isMovable = false;
	private boolean lockToParentBounds = false;
	private boolean isResizable = false;
	private boolean resizeN = true;
	private boolean resizeS = true;
	private boolean resizeW = true;
	private boolean resizeE = true;
	private boolean dockN = false;
	private boolean dockW = true;
	private boolean dockE = false;
	private boolean dockS = true;
	private boolean scaleNS = true;
	private boolean scaleEW = true;
	
	private boolean effectParent = false;
	private boolean effectAbsoluteParent = false;
	
	private Geometry geom;
	private ElementQuadGrid model;
	private boolean tileImage = false;
	private Material mat;
	private Texture defaultTex;
	private boolean useLocalAtlas = false;
	private boolean useLocalTexture = false;
	private String atlasCoords = "";
	private Texture alphaMap = null;
	
	protected BitmapText textElement;
//	protected TextElement textElement;
	protected Vector2f textPosition = new Vector2f(0,0);
	protected LineWrapMode textWrap = LineWrapMode.Word;
	protected BitmapFont.Align textAlign = BitmapFont.Align.Left;
	protected BitmapFont.VAlign textVAlign = BitmapFont.VAlign.Top;
	protected String text = "";
	private String toolTipText = null;
	
	protected BitmapFont font;
	protected float fontSize = 20;
	protected Vector4f textPadding = new Vector4f(0,0,0,0);
	protected ColorRGBA fontColor = ColorRGBA.White;
	private ColorRGBA defaultColor = new ColorRGBA(1,1,1,0);
	
	private Element elementParent = null;
	protected Map<String, Element> elementChildren = new LinkedHashMap();
	
	// Clipping
	protected boolean isClipped = false;
	protected boolean wasClipped = false;
	protected Element clippingLayer, secondaryClippingLayer;
	protected Vector4f clippingBounds = new Vector4f();
	private Vector4f clipPadding = new Vector4f(0,0,0,0);
	private Vector4f textClipPadding = new Vector4f(0,0,0,0);
	
	// New Clipping
	protected List<ClippingDefine> clippingLayers = new ArrayList();
	private List<ClippingDefine> remClippingLayers = new ArrayList();
	private Vector4f clipTest = new Vector4f();
	
	protected boolean isVisible = true;
	protected boolean wasVisible = true;
	protected boolean isVisibleAsModal = false;
	private boolean hasFocus = false;
	private boolean resetKeyboardFocus = true;
	
	private Form form;
	private int tabIndex = 0;
	
	private float zOrder;
	private boolean effectZOrder = true;
	private Map<Effect.EffectEvent, Effect> effects = new HashMap();
	
	private OSRBridge bridge;
	
	private boolean ignoreGlobalAlpha = false;
	private boolean isModal = false;
	private boolean isGlobalModal = false;
	
	private Object elementUserData;
	
	private boolean initialized = false;
	protected boolean isEnabled = true;
	
	private boolean isDragElement = false, isDropElement = false;
	
	private Docking docking = Docking.NW;
	private Orientation orientation = Orientation.HORIZONTAL;
	
	// Layouts
	protected Layout layout = null;
	protected LayoutHints layoutHints = new LayoutHints();
	//</editor-fold>
	
	/**
	 * The Element class is the single primitive for all controls in the gui library.
	 * Each element consists of an ElementQuadMesh for rendering resizable textures,
	 * as well as a BitmapText element if setText(String text) is called.
	 * 
	 * Behaviors, such as movement and resizing, are common to all elements and can
	 * be enabled/disabled to ensure the element reacts to user input as needed.
	 * 
	 * @param screen The Screen control the element or it's absolute parent element is being added to
	 * @param UID A unique String identifier used when looking up elements by screen.getElementByID()
	 * @param position A Vector2f containing the x/y coordinates (relative to it's parent elements x/y) for positioning
	 * @param dimensions A Vector2f containing the dimensions of the element, x being width, y being height
	 * @param resizeBorders A Vector4f containing the size of each border used for scaling images without distorting them (x = N, y = W, x = E, w = S)
	 * @param texturePath A String path to the default image to be rendered on the element's mesh
	 */
	public Element(ElementManager screen, String UID, Vector2f position, Vector2f dimensions, Vector4f resizeBorders, String texturePath) {
		this.app = screen.getApplication();
		this.screen = screen;
		if (UID == null) {
			this.UID = UIDUtil.getUID();
		} else {
			this.UID = UID;
		}
		this.position.set(position);
		this.dimensions.set(dimensions);
		this.orgDimensions = dimensions.clone();
		this.orgRelDimensions = new Vector2f(1,1);
		this.borders.set(resizeBorders);
		
		BitmapFont tempFont = app.getAssetManager().loadFont(screen.getStyle("Font").getString("defaultFont"));
		
		font = new BitmapFont();
		font.setCharSet(app.getAssetManager().loadFont(screen.getStyle("Font").getString("defaultFont")).getCharSet());
		Material[] pages = new Material[tempFont.getPageSize()];
		for (int i = 0; i < pages.length; i++) {
			pages[i] = tempFont.getPage(i).clone();
		}
		font.setPages(pages);
		
		float imgWidth = 100;
		float imgHeight = 100;
		float pixelWidth = 1f/imgWidth;
		float pixelHeight = 1f/imgHeight;
		float textureAtlasX = 0, textureAtlasY = 0, textureAtlasW = imgWidth, textureAtlasH = imgHeight;
		
		boolean useAtlas = screen.getUseTextureAtlas();
		
		if (texturePath != null) {
			if (useAtlas && texturePath.indexOf("|") != -1 && texturePath.indexOf("=") != -1) {
				float[] coords = screen.parseAtlasCoords(texturePath);
				textureAtlasX = coords[0];
				textureAtlasY = coords[1];
				textureAtlasW = coords[2];
				textureAtlasH = coords[3];
				
				this.atlasCoords = "x=" + coords[0] + "|y=" + coords[1] + "|w=" + coords[2] + "|h=" + coords[3];
				
				defaultTex = screen.getAtlasTexture();

				imgWidth = defaultTex.getImage().getWidth();
				imgHeight = defaultTex.getImage().getHeight();
				pixelWidth = 1f/imgWidth;
				pixelHeight = 1f/imgHeight;

				textureAtlasY = imgHeight-textureAtlasY-textureAtlasH;
			} else {
				if (useAtlas) useLocalTexture = true;
				
				defaultTex = app.getAssetManager().loadTexture(texturePath);
				defaultTex.setMinFilter(Texture.MinFilter.BilinearNoMipMaps);
				defaultTex.setMagFilter(Texture.MagFilter.Nearest);
				defaultTex.setWrap(Texture.WrapMode.Clamp);

				imgWidth = defaultTex.getImage().getWidth();
				imgHeight = defaultTex.getImage().getHeight();
				pixelWidth = 1f/imgWidth;
				pixelHeight = 1f/imgHeight;

				textureAtlasW = imgWidth;
				textureAtlasH = imgHeight;
			}
		}
		mat = new Material(app.getAssetManager(), "tonegod/gui/shaders/Unshaded.j3md");
		if (texturePath != null) {
			mat.setTexture("ColorMap", defaultTex);
			mat.setColor("Color", new ColorRGBA(1,1,1,1));
		} else {
			mat.setColor("Color", defaultColor);
		}
		if (useAtlas) mat.setBoolean("UseEffectTexCoords", true);
		mat.setVector2("OffsetAlphaTexCoord", new Vector2f(0,0));
		mat.setFloat("GlobalAlpha", screen.getGlobalAlpha());
		
		mat.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);
		mat.getAdditionalRenderState().setFaceCullMode(RenderState.FaceCullMode.Back);
		
		this.model = new ElementQuadGrid(this.dimensions, borders, imgWidth, imgHeight, pixelWidth, pixelHeight, textureAtlasX, textureAtlasY, textureAtlasW, textureAtlasH);
		
        this.setName(UID + ":Node");
                
		geom = new Geometry(UID + ":Geometry");
		geom.setMesh(model);
		geom.setCullHint(CullHint.Never);
        geom.setQueueBucket(Bucket.Gui);
		geom.setMaterial(mat);
                
		this.attachChild(geom);
		
		this.setQueueBucket(Bucket.Gui);
		
		this.setLocalTranslation(position.x, position.y, 0);
	}
	
	/**
	 * Converts the the inputed percentage (0.0f-1.0f) into pixels of the elements image
	 * @param in Vector2f containing the x and y percentage
	 * @return Vector2f containing the actual width/height in pixels
	 */
	public final Vector2f getV2fPercentToPixels(Vector2f in) {
		if (getElementParent() == null) {
			if (in.x < 1) in.setX(screen.getWidth()*in.x);
			if (in.y < 1) in.setY(screen.getHeight()*in.y);
		} else {
			if (in.x < 1) in.setX(getElementParent().getWidth()*in.x);
			if (in.y < 1) in.setY(getElementParent().getHeight()*in.y);
		}
		return in;
	}
	
	//<editor-fold desc="Parent/Child">
	/**
	 * Adds the specified Element as a child to this Element.
	 * @param child The Element to add as a child
	 */
	public void addChild(Element child) {
		addChild(child, false);
	}
	
	/**
	 * Adds the specified Element as a child to this Element.
	 * @param child The Element to add as a child
	 */
	public void addChild(Element child, boolean hide) {
		child.elementParent = this;
		
		for (ClippingDefine def : clippingLayers) {
			if (def.getClipping() == null) {
			//	if (child.getX() >= 0 && child.getX()+child.getWidth() <= def.getElement().getWidth() &&
			//		child.getY() >= 0 && child.getY()+child.getHeight() <= def.getElement().getHeight())
						child.addClippingLayer(def.getElement());
			}// else
			//	child.addClippingLayer(def.getElement(),def.getClipping());
		}
		
		if (!child.getInitialized()) {
			child.setY(this.getHeight()-child.getHeight()-child.getY());
			child.orgPosition = position.clone();
			child.orgPosition.setY(child.getY());
			child.setInitialized();
		}
		child.orgRelDimensions.set(child.getWidth()/getWidth(),child.getHeight()/getHeight());
		child.setQueueBucket(RenderQueue.Bucket.Gui);
		
		if (screen.getElementById(child.getUID()) != null) {
			try {
				throw new ConflictingIDException();
			} catch (ConflictingIDException ex) {
				Logger.getLogger(Element.class.getName()).log(Level.SEVERE, "The child element '" + child.getUID() + "' (" + child.getClass() + ") conflicts with a previously added child element in parent element '" + getUID() + "'.", ex);
				System.exit(0);
			}
		} else {
			elementChildren.put(child.getUID(), child);
			this.attachChild(child);
			
			if (hide)
				child.hide();
		}
		resetChildZOrder();
	}
	
	/**
	 * Removes the specified Element
	 * @param child Element to remove
	 */
	public void removeChild(Element child) {
		Element e = elementChildren.remove(child.getUID());
		if (e != null) {
			e.elementParent = null;
			e.removeFromParent();
			e.removeClippingLayer(this);
			for (ClippingDefine def : clippingLayers)
				e.removeClippingLayer(def.getElement());
			e.cleanup();
			
			if (screen.getUseToolTips()) {
				if (screen.getToolTipFocus() == this)
					screen.hideToolTip();
				else if (screen.getToolTipFocus() != null) {
					if (getChildElementById(screen.getToolTipFocus().getUID()) != null)
						screen.hideToolTip();
				}
			}
		}
		resetChildZOrder();
	}
	
	/**
	 * Remove all child Elements from this Element
	 */
	public void removeAllChildren() {
		for (Element e : elementChildren.values()) {
			e.removeFromParent();
			e.removeClippingLayer(this);
			for (ClippingDefine def : clippingLayers)
				e.removeClippingLayer(def.getElement());
		}
		elementChildren.clear();
	}
	
	/**
	 * Returns the child elements as a Map
	 * @return 
	 */
	public Map<String, Element> getElementsAsMap() {
		return this.elementChildren;
	}
	
	/**
	 * Returns the child elements as a Collection
	 * @return 
	 */
	public Collection<Element> getElements() {
		return this.elementChildren.values();
	}
	
	/**
	 * Returns the one and only Element's screen
	 * @return 
	 */
	public ElementManager getScreen() {
		return this.screen;
	}
	
	/**
	 * Returns a list of all children that are an instance of DragElement
	 * @return List<Element>
	 */
	public List<Element> getDraggableChildren() {
		List<Element> ret = new ArrayList();
		for (Element el : elementChildren.values()) {
			if (el instanceof DragElement) {
				ret.add(el);
			}
		}
		return ret;
	}
	
	/**
	 * Recursively searches children elements for specified element containing the specified UID
	 * @param UID - Unique Indentifier of element to search for
	 * @return Element containing UID or null if not found
	 */
	public Element getChildElementById(String UID) {
		Element ret = null;
		if (this.UID.equals(UID)) {
			ret = this;
		} else {
			if (elementChildren.containsKey(UID)) {
				ret = elementChildren.get(UID);
			} else {
				for (Element el : elementChildren.values()) {
					ret = el.getChildElementById(UID);
					if (ret != null) {
						break;
					}
				}
			}
		}
		return ret;
	}
	
	/**
	 * Returns the top-most parent in the tree of Elements.  The topmost element will always have a parent of null
	 * @return Element elementParent
	 */
	public Element getAbsoluteParent() {
		if (elementParent == null) {
			return this;
		} else {
			return elementParent.getAbsoluteParent();
		}
	}
	
	/**
	 * Returns the parent element of this node
	 * @return Element elementParent
	 */
	public Element getElementParent() {
		return elementParent;
	}
	
	/**
	 * Sets the element's parent element
	 * @param elementParent Element
	 */
	public void setElementParent(Element elementParent) {
		this.elementParent = elementParent;
	}
	//</editor-fold>
	
	/**
	 * Allows for setting the Element UID if (and ONLY if) the Element Parent is null
	 * @param UID The new UID
	 * @return boolean If setting the UID was successful
	 */
	public boolean setUID(String UID) {
		if (this.elementParent == null) {
			this.UID = UID;
			return true;
		} else {
			return false;
		}
	}
	
	/**
	 * Returns the element's unique string identifier
	 * @return String UID
	 */
	public String getUID() {
		return UID;
	}
	
	//<editor-fold desc="Z-Order">
	private int getParentCount() {
		int count = 0;
		Element n = getElementParent();
		while (n != null) {
			count++;
			n = n.getElementParent();
		}
		return count;
	}
	
	public void resetChildZOrder() {
		int pCount = getParentCount();
		float nStep = screen.getZOrderStepMinor();
		if (pCount > 0)
			nStep /= pCount;
		float step = nStep;
		for (Element el : elementChildren.values()) {
			el.setLocalTranslation(el.getLocalTranslation().setZ(step));
			step += nStep;
			if (getTextElement() != null) {
				getTextElement().setLocalTranslation(getTextElement().getLocalTranslation().setZ(
					step
				));
				step += nStep;
			}
		}
	}
	
	public void bringToFront() {
		if (getElementParent() != null) {
			Element p = getElementParent();
			p.removeChild(this);
			p.addChild(this);
			p.resetChildZOrder();
		}
	}
	
	/**
	 * Recursive call made by the screen control to properly initialize z-order (depth) placement
	 * @param zOrder The depth to place the Element at. (Relative to the parent's z-order)
	 */
	protected void initZOrder(float zOrder) {
		setLocalTranslation(getLocalTranslation().setZ(
			zOrder
		));
		int pCount = getParentCount();
		float nStep = screen.getZOrderStepMinor();
		if (pCount > 0)
			nStep /= pCount;
		float step = nStep;
		if (getTextElement() != null) {
			getTextElement().setLocalTranslation(getTextElement().getLocalTranslation().setZ(
				step
			));
			step += nStep;
		}
		
		for (Element el : elementChildren.values()) {
			el.initZOrder(step);
			step += nStep;
		}
	}
	
	/**
	 * Returns the Element's zOrder
	 * @return float zOrder
	 */
	public float getZOrder() {
		return this.zOrder;
	}
	
	/**
	 * Sets the Elements zOrder (I would suggest NOT using this method)
	 * @param zOrder 
	 */
	public void setZOrder(float zOrder) {
		this.zOrder = zOrder;
		initZOrder(zOrder);
	}
	
	public void setEffectZOrder(boolean effectZOrder) {
		this.effectZOrder = effectZOrder;
	}
	
	public boolean getEffectZOrder() { return this.effectZOrder; }
	//</editor-fold>
	
	//<editor-fold desc="Scaling, Docking & Other Behaviors">
	/**
	 * The setAsContainer only method removes the Mesh component (rendered Mesh) from the
	 * Element, leaving only Element functionality.  Call this method to set the Element 
	 * for use as a parent container.
	 */
	public void setAsContainerOnly() {
		detachChildAt(0);
	}
	
	/**
	 * Informs the screen control that this Element should be ignored by mouse events.
	 * 
	 * @param ignoreMouse boolean
	 */
	public void setIgnoreMouse(boolean ignoreMouse) {
		this.ignoreMouse = ignoreMouse;
		setIgnoreMouseButtons(ignoreMouse);
		setIgnoreMouseWheel(ignoreMouse);
		setIgnoreMouseFocus(ignoreMouse);
		setIgnoreTouchEvents(ignoreMouse);
	}
	
	/**
	 * Returns if the element is set to ingnore mouse events
	 * 
	 * @return boolean ignoreMouse
	 */
	public boolean getIgnoreMouse() {
		return this.ignoreMouse;
	}
	
	/**
	 * Element will ignore mouse left & right button events
	 * @param ignoreMouseButtons 
	 */
	public void setIgnoreMouseButtons(boolean ignoreMouseButtons) {
		setIgnoreMouseLeftButton(ignoreMouseButtons);
		setIgnoreMouseRightButton(ignoreMouseButtons);
	}
	
	/**
	 * Returns true if both left and right mouse buttons are being ignored
	 * @return 
	 */
	public boolean getIgnoreMouseButtons() { return (getIgnoreMouseLeftButton() && getIgnoreMouseRightButton()); }
	
	/**
	 * Element will ignore mouse left button events
	 * @param ignoreMouseLeftButton 
	 */
	public void setIgnoreMouseLeftButton(boolean ignoreMouseLeftButton) {
		this.ignoreMouseLeftButton = ignoreMouseLeftButton;
	}
	
	/**
	 * Returns if the left mouse button is being ignored
	 * @return 
	 */
	public boolean getIgnoreMouseLeftButton() { return this.ignoreMouseLeftButton; }
	
	/**
	 * Element will ignore mouse right button events
	 * @param ignoreMouseRightButton 
	 */
	public void setIgnoreMouseRightButton(boolean ignoreMouseRightButton) {
		this.ignoreMouseRightButton = ignoreMouseRightButton;
	}
	
	/**
	 * Returns if the right mouse button is being ignored
	 * @return 
	 */
	public boolean getIgnoreMouseRightButton() { return this.ignoreMouseRightButton; }
	
	/**
	 * Element will ignore mouse focus
	 * @param ignoreMouseFocus 
	 */
	public void setIgnoreMouseFocus(boolean ignoreMouseFocus) {
		this.ignoreMouseFocus = ignoreMouseFocus;
	}
	
	/**
	 * Returns if the element ignores mouse focus
	 * @return 
	 */
	public boolean getIgnoreMouseFocus() { return this.ignoreMouseFocus; }
	
	/**
	 * Element will ignore mouse wheel click and move events
	 * @param ignoreMouseWheel 
	 */
	public void setIgnoreMouseWheel(boolean ignoreMouseWheel) {
		setIgnoreMouseWheelClick(ignoreMouseWheel);
		setIgnoreMouseWheelMove(ignoreMouseWheel);
	}
	
	/**
	 * Returns if the element is ignoring both mouse wheel click and move events
	 * @return 
	 */
	public boolean getIgnoreMouseWheel() { return (getIgnoreMouseWheelClick() && getIgnoreMouseWheelMove()); }
	
	/**
	 * Element will ignore mouse wheel click events
	 * @param ignoreMouseWheelClick 
	 */
	public void setIgnoreMouseWheelClick(boolean ignoreMouseWheelClick) {
		this.ignoreMouseWheelClick = ignoreMouseWheelClick;
	}
	
	/**
	 * Returns if the element ignores mouse wheel clicks
	 * @return 
	 */
	public boolean getIgnoreMouseWheelClick() { return this.ignoreMouseWheelClick; }
	
	/**
	 * Element will ignore mouse wheel mouse events;
	 * @param ignoreMouseWheelMove 
	 */
	public void setIgnoreMouseWheelMove(boolean ignoreMouseWheelMove) {
		this.ignoreMouseWheelMove = ignoreMouseWheelMove;
	}
	
	/**
	 * Returns if the element is ignoring mouse wheel moves
	 * @return 
	 */
	public boolean getIgnoreMouseWheelMove() { return this.ignoreMouseWheelMove; }
	
	/**
	 * Element will ignore touch down up move and fling events
	 * @param ignoreTouchEvents 
	 */
	public void setIgnoreTouchEvents(boolean ignoreTouchEvents) {
		setIgnoreTouch(ignoreTouchEvents);
		setIgnoreTouchMove(ignoreTouchEvents);
		setIgnoreFling(ignoreTouchEvents);
	}
	
	/**
	 * Returns if the element ignores touch down, up, move & fling events
	 * @return 
	 */
	public boolean getIgnoreTouchEvents() { return (getIgnoreTouch() && getIgnoreTouchMove() && getIgnoreFling()); }
	
	/**
	 * Element will ignore touch down and up events
	 * @param ignoreTouch 
	 */
	public void setIgnoreTouch(boolean ignoreTouch) {
		this.ignoreTouch = ignoreTouch;
	}
	
	/**
	 * Returns if the element ignores touch down and up events
	 * @return 
	 */
	public boolean getIgnoreTouch() { return ignoreTouch; }
	/**
	 * element will ignore touch move events
	 * @param ignoreTouchMove 
	 */
	public void setIgnoreTouchMove(boolean ignoreTouchMove) {
		this.ignoreTouchMove = ignoreTouchMove;
	}
	
	/**
	 * Returns if the element ignores touch move events
	 * @return 
	 */
	public boolean getIgnoreTouchMove() { return this.ignoreTouchMove; }
	
	/**
	 * Element will ignore touch fling events
	 * @param ignoreFling 
	 */
	public void setIgnoreFling(boolean ignoreFling) {
		this.ignoreFling = ignoreFling;
	}
	
	/**
	 * Returns if the element ignores fling events
	 * @return 
	 */
	public boolean getIgnoreFling() { return this.ignoreFling; }
	
	/**
	 * Enables draggable behavior for this element
	 * @param isMovable boolean
	 */
	public void setIsMovable(boolean isMovable) {
		this.isMovable = isMovable;
	}
	
	/**
	 * Returns if the element has draggable behavior set
	 * @return boolean isMovable
	 */
	public boolean getIsMovable() {
		return this.isMovable;
	}
	
	/**
	 * Enables resize behavior for this element
	 * @param isResizable boolean
	 */
	public void setIsResizable(boolean isResizable) {
		this.isResizable = isResizable;
	}
	
	/**
	 * Returns if the element has resize behavior set
	 * @return boolean isResizable
	 */
	public boolean getIsResizable() {
		return this.isResizable;
	}
	
	/**
	 * Enables/disables north border for resizing
	 * @param resizeN boolean
	 */
	public void setResizeN(boolean resizeN) {
		this.resizeS = resizeN;
	}
	
	/**
	 * Returns whether the elements north border has enabled/disabled resizing
	 * @return boolean resizeN
	 */
	public boolean getResizeN() {
		return this.resizeS;
	}
	
	/**
	 * Enables/disables south border for resizing
	 * @param resizeS boolean
	 */
	public void setResizeS(boolean resizeS) {
		this.resizeN = resizeS;
	}
	
	/**
	 * Returns whether the elements south border has enabled/disabled resizing
	 * @return boolean resizeS
	 */
	public boolean getResizeS() {
		return this.resizeN;
	}
	
	/**
	 * Enables/disables west border for resizing
	 * @param resizeW boolean
	 */
	public void setResizeW(boolean resizeW) {
		this.resizeW = resizeW;
	}
	
	/**
	 * Returns whether the elements west border has enabled/disabled resizing
	 * @return boolean resizeW
	 */
	public boolean getResizeW() {
		return this.resizeW;
	}
	
	/**
	 * Enables/disables east border for resizing
	 * @param resizeE boolean
	 */
	public void setResizeE(boolean resizeE) {
		this.resizeE = resizeE;
	}
	
	/**
	 * Returns whether the elements east border has enabled/disabled resizing
	 * @return boolean resizeE
	 */
	public boolean getResizeE() {
		return this.resizeE;
	}
	
	/**
	 * Sets how the element will docking to it's parent element during resize events.
	 * NW = Top Left of parent element
	 * NE = Top Right of parent element
	 * SW = Bottom Left of parent element
	 * SE = Bottom Right of parent element
	 * @param docking 
	 */
	public void setDocking(Docking docking) {
		this.docking = docking;
	}
	
	public Docking getDocking() {
		return this.docking;
	}
	/**
	 * Enables north docking of element (disables south docking of Element).  This
	 * determines how the Element should retain positioning on parent resize events.
	 * 
	 * @param dockN boolean
	 */
	@Deprecated
	public void setDockN(boolean dockN) {
		this.dockS = dockN;
		this.dockN = !dockN;
		Docking d = null;
		if (dockS) {
			if (dockE)	d = Docking.NE;
			else		d = Docking.NW;
		} else {
			if (dockE)	d = Docking.SE;
			else		d = Docking.SW;
		}
		setDocking(d);
	}
	
	/**
	 * Returns if the Element is docked to the north quadrant of it's parent element.
	 * @return boolean dockN
	 */
	@Deprecated
	public boolean getDockN() {
		return this.dockS;
	}
	
	/**
	 * Enables west docking of Element (disables east docking of Element).  This
	 * determines how the Element should retain positioning on parent resize events.
	 * 
	 * @param dockW boolean
	 */
	@Deprecated
	public void setDockW(boolean dockW) {
		this.dockW = dockW;
		this.dockE = !dockW;
		Docking d = null;
		if (dockE) {
			if (dockS)	d = Docking.NE;
			else		d = Docking.SE;
		} else {
			if (dockS)	d = Docking.NW;
			else		d = Docking.SW;
		}
		setDocking(d);
	}
	
	/**
	 * Returns if the Element is docked to the west quadrant of it's parent element.
	 * @return boolean dockW
	 */
	@Deprecated
	public boolean getDockW() {
		return this.dockW;
	}
	
	/**
	 * Enables east docking of Element (disables west docking of Element).  This
	 * determines how the Element should retain positioning on parent resize events.
	 * 
	 * @param dockE boolean
	 */
	@Deprecated
	public void setDockE(boolean dockE) {
		this.dockE = dockE;
		this.dockW = !dockE;
		Docking d = null;
		if (dockE) {
			if (dockS)	d = Docking.NE;
			else		d = Docking.SE;
		} else {
			if (dockS)	d = Docking.NW;
			else		d = Docking.SW;
		}
		setDocking(d);
	}
	
	/**
	 * Returns if the Element is docked to the east quadrant of it's parent element.
	 * @return boolean dockE
	 */
	@Deprecated
	public boolean getDockE() {
		return this.dockE;
	}
	
	/**
	 * Enables south docking of Element (disables north docking of Element).  This
	 * determines how the Element should retain positioning on parent resize events.
	 * 
	 * @param dockS boolean
	 */
	@Deprecated
	public void setDockS(boolean dockS) {
		this.dockN = dockS;
		this.dockS = !dockS;
		Docking d = null;
		if (dockS) {
			if (dockE)	d = Docking.NE;
			else		d = Docking.NW;
		} else {
			if (dockE)	d = Docking.SE;
			else		d = Docking.SW;
		}
		setDocking(d);
	}
	
	/**
	 * Returns if the Element is docked to the south quadrant of it's parent element.
	 * @return boolean dockS
	 */
	@Deprecated
	public boolean getDockS() {
		return this.dockN;
	}
	
	/**
	 * Determines if the element should scale with parent when resized vertically.
	 * @param scaleNS boolean
	 */
	public void setScaleNS(boolean scaleNS) {
		this.scaleNS = scaleNS;
	}
	
	/**
	 * Returns if the Element is set to scale vertically when it's parent Element is
	 * resized.
	 * 
	 * @return boolean scaleNS
	 */
	public boolean getScaleNS() {
		return this.scaleNS;
	}
	
	/**
	 * Determines if the element should scale with parent when resized horizontally.
	 * @param scaleEW boolean
	 */
	public void setScaleEW(boolean scaleEW) {
		this.scaleEW = scaleEW;
	}
	
	/**
	 * Returns if the Element is set to scale horizontally when it's parent Element is
	 * resized.
	 * 
	 * @return boolean scaleEW
	 */
	public boolean getScaleEW() {
		return this.scaleEW;
	}
	
	/**
	 * Sets the element to pass certain events (movement, resizing) to it direct parent instead
	 * of effecting itself.
	 * 
	 * @param effectParent boolean
	 */
	public void setEffectParent(boolean effectParent) {
		this.effectParent = effectParent;
	}
	
	/**
	 * Returns if the Element is set to pass events to it's direct parent
	 * @return boolean effectParent
	 */
	public boolean getEffectParent() {
		return this.effectParent;
	}
	
	/**
	 * Sets the element to pass certain events (movement, resizing) to it absolute
	 * parent instead of effecting itself.
	 * 
	 * The Elements absolute parent is the element farthest up in it's nesting order, 
	 * or simply put, was added to the screen.
	 * 
	 * @param effectAbsoluteParent boolean
	 */
	public void setEffectAbsoluteParent(boolean effectAbsoluteParent) {
		this.effectAbsoluteParent = effectAbsoluteParent;
	}
	
	/**
	 * Returns if the Element is set to pass events to it's absolute parent
	 * @return boolean effectParent
	 */
	public boolean getEffectAbsoluteParent() {
		return this.effectAbsoluteParent;
	}
	
	/**
	 * Forces the object to stay within the constraints of it's parent Elements
	 * dimensions.
	 * NOTE: use setLockToParentBounds instead.
	 * 
	 * @param lockToParentBounds boolean
	 */
	@Deprecated
	public void setlockToParentBounds(boolean lockToParentBounds) {
		this.lockToParentBounds = lockToParentBounds;
	}/**
	 * Forces the object to stay within the constraints of it's parent Elements
	 * dimensions.
	 * 
	 * @param lockToParentBounds boolean
	 */
	public void setLockToParentBounds(boolean lockToParentBounds) {
		this.lockToParentBounds = lockToParentBounds;
	}
	
	/**
	 * Returns if the Element has been constrained to it's parent Element's dimensions.
	 * 
	 * @return boolean lockToParentBounds
	 */
	public boolean getLockToParentBounds() {
		return this.lockToParentBounds;
	}
	
	public void setGlobalUIScale(float widthPercent, float heightPercent) {
		for (Element el : elementChildren.values()) {
			el.setPosition(el.getPosition().x*widthPercent, el.getPosition().y*heightPercent);
			el.setDimensions(el.getDimensions().x*widthPercent, el.getDimensions().y*heightPercent);
			el.setFontSize(el.getFontSize()*heightPercent);
			el.setGlobalUIScale(widthPercent, heightPercent);
		}
	}
	
	/**
	 * Allows for dynamically enabling/disabling the element
	 * @param isEnabled boolean
	 */
	public void setIsEnabled(boolean isEnabled) {
		this.isEnabled = isEnabled;
		controlIsEnabledHook(isEnabled);
		for (Element el : elementChildren.values()) {
			el.setIsEnabled(isEnabled);
		}
	}
	
	public void controlIsEnabledHook(boolean isEnabled) {  }
	
	/**
	 * Returns if the element is currently enabled
	 * @return boolean
	 */
	public boolean getIsEnabled() {
		return this.isEnabled;
	}
	//</editor-fold>
	
	//<editor-fold desc="Drag & Drop Support">
	/**
	 * Flags Element as Drag Element for Drag & Drop interaction
	 * @param isDragElement boolean
	 */
	public void setIsDragDropDragElement(boolean isDragElement) {
		this.isDragElement = isDragElement;
		if (isDragElement)
			this.isDropElement = false;
	}
	
	/**
	 * Returns if the Element is currently flagged as a Drag Element for Drag & Drop interaction
	 * @return boolean
	 */
	public boolean getIsDragDropDragElement() {
		return this.isDragElement;
	}
	
	/**
	 * Flags Element as Drop Element for Drag & Drop interaction
	 * @param isDropElement boolean
	 */
	public void setIsDragDropDropElement(boolean isDropElement) {
		this.isDropElement = isDropElement;
		if (isDropElement)
			this.isDragElement = false;
	}
	
	/**
	 * Returns if the Element is currently flagged as a Drop Element for Drag & Drop interaction
	 * @return boolean
	 */
	public boolean getIsDragDropDropElement() {
		return this.isDropElement;
	}
	//</editor-fold>
	
	//<editor-fold desc="Sizing & Positioning">
	/**
	 * Set the x,y coordinates of the Element.  X and y are relative to the parent
	 * Element.
	 * 
	 * @param position Vector2f screen poisition of Element
	 */
	public void setPosition(Vector2f position) {
		this.position.set(position);
		updateNodeLocation();
	}
	
	/**
	 * Set the x,y coordinates of the Element.  X and y are relative to the parent
	 * Element.
	 * 
	 * @param x The x coordinate screen poisition of Element
	 * @param y The y coordinate screen poisition of Element
	 */
	public void setPosition(float x, float y) {
		this.position.setX(x);
		this.position.setY(y);
		updateNodeLocation();
	}
	
	/**
	 * Set the x coordinates of the Element.  X is relative to the parent Element.
	 * 
	 * @param x The x coordinate screen poisition of Element
	 */
	public void setX(float x) {
		this.position.setX(x);
		updateNodeLocation();
	}
	
	/**
	 * Set the y coordinates of the Element.  Y is relative to the parent Element.
	 * 
	 * @param y The y coordinate screen poisition of Element
	 */
	public void setY(float y) {
		this.position.setY(y);
		updateNodeLocation();
	}
	
	private void updateNodeLocation() {
		this.setLocalTranslation(position.x, position.y, this.getLocalTranslation().getZ());
	//	updateClipping();
		updateClippingLayers();
	}
	
	/**
	 * Returns the current screen location of the Element
	 * @return Vector2f position
	 */
	public Vector2f getPosition() {
		return position;
	}
	
	/**
	 * Gets the relative x coordinate of the Element from it's parent Element's x
	 * @return  float
	 */
	public float getX() {
		return position.x;
	}
	
	/**
	 * Gets the relative y coordinate of the Element from it's parent Element's y
	 * @return  float
	 */
	public float getY() {
		return position.y;
	}
	
	/**
	 * Returns the x coord of an element from screen x 0, ignoring the nesting order.
	 * @return  float x
	 */
	public float getAbsoluteX() {
		float x = getX();
		Element el = this;
		while (el.getElementParent() != null) {
			el = el.getElementParent();
			x += el.getX();
		}
		return x;
	}
	
	/**
	 * Returns the y coord of an element from screen y 0, ignoring the nesting order.
	 * @return float
	 */
	public float getAbsoluteY() {
		float y = getY();
		Element el = this;
		while (el.getElementParent() != null) {
			el = el.getElementParent();
			y += el.getY();
		}
		return y;
	}
	
	/**
	 * Sets the width and height of the element
	 * @param w float
	 * @param h float
	 */
	public void setDimensions(float w, float h) {
		this.dimensions.setX(w);
		this.dimensions.setY(h);
		getModel().updateDimensions(dimensions.x, dimensions.y);
		if (tileImage) {
			float tcW = dimensions.x/getModel().getImageWidth();
			float tcH = dimensions.y/getModel().getImageHeight();
			getModel().updateTiledTexCoords(0, -tcH, tcW, 0);
		}
		geom.updateModelBound();
		if (textElement != null) {
			updateTextElement();
		}
	//	updateClipping();
		updateClippingLayers();
	}
	
	/**
	 * Sets the width and height of the element
	 * @param dimensions Vector2f
	 */
	public void setDimensions(Vector2f dimensions) {
		this.dimensions.set(dimensions);
		getModel().updateDimensions(dimensions.x, dimensions.y);
		if (tileImage) {
			float tcW = dimensions.x/getModel().getImageWidth();
			float tcH = dimensions.y/getModel().getImageHeight();
			getModel().updateTiledTexCoords(0, -tcH, tcW, 0);
		}
		geom.updateModelBound();
		if (textElement != null) {
			updateTextElement();
		}
	//	updateClipping();
		updateClippingLayers();
	}
	
	/**
	 * Stubbed for future use.  This should limit resizing to the minimum dimensions defined
	 * @param minDimensions The absolute minimum dimensions for this Element.
	 */
	public void setMinDimensions(Vector2f minDimensions) {
		if (this.minDimensions == null) this.minDimensions = new Vector2f();
		this.minDimensions.set(minDimensions);
	}
	
	public Vector2f getMinDimensions() { return this.minDimensions; }
	
	/**
	 * Sets the width of the element
	 * @param width float
	 */
	public void setWidth(float width) {
		this.dimensions.setX(width);
		getModel().updateWidth(dimensions.x);
		if (tileImage) {
			float tcW = dimensions.x/getModel().getImageWidth();
			float tcH = dimensions.y/getModel().getImageHeight();
			getModel().updateTiledTexCoords(0, -tcH, tcW, 0);
		}
		geom.updateModelBound();
		if (textElement != null) {
			updateTextElement();
		}
	//	updateClipping();
		updateClippingLayers();
	}
	
	/**
	 * Sets the height of the element
	 * @param height float
	 */
	public void setHeight(float height) {
		this.dimensions.setY(height);
		getModel().updateHeight(dimensions.y);
		if (tileImage) {
			float tcW = dimensions.x/getModel().getImageWidth();
			float tcH = dimensions.y/getModel().getImageHeight();
			getModel().updateTiledTexCoords(0, -tcH, tcW, 0);
		}
		geom.updateModelBound();
		if (textElement != null) {
			updateTextElement();
		}
	//	updateClipping();
		updateClippingLayers();
	}
	
	/**
	 * Returns a Vector2f containing the actual width and height of an Element
	 * @return float
	 */
	public Vector2f getDimensions() {
		return dimensions;
	}
	
	/**
	 * Returns the dimensions defined at the time of the Element's creation.
	 * @return 
	 */
	public Vector2f getOrgDimensions() { return this.orgDimensions; }
	
	/**
	 * Returns the actual width of an Element
	 * @return float
	 */
	public float getWidth() {
		return dimensions.x;
	}
	
	/**
	 * Returns the actual height of an Element
	 * @return float
	 */
	public float getHeight() {
		return dimensions.y;
	}
	
	/**
	 * Returns the width of an Element from screen x 0
	 * @return float
	 */
	public float getAbsoluteWidth() {
		return getAbsoluteX() + getWidth();
	}
	
	/**
	 * Returns the height of an Element from screen y 0
	 * @return float
	 */
	public float getAbsoluteHeight() {
		return getAbsoluteY() + getHeight();
	}
	//</editor-fold>
	
	/**
	 * Stubbed for future use.
	 */
	public void validateLayout() {
		if (getDimensions().x < 1 || getDimensions().y < 1) {
			Vector2f dim = getV2fPercentToPixels(getDimensions());
			resize(getAbsoluteX()+dim.x, getAbsoluteY()+dim.y, Element.Borders.SE);
		}
		if (getPosition().x < 1 || getPosition().y < 1) {
			Vector2f pos = getV2fPercentToPixels(getPosition());
			setPosition(pos.x,pos.y);
		}
		if (getElementParent() != null)
			setY(getElementParent().getHeight()-getHeight()-getY());
		else
			setY(screen.getHeight()-getHeight()-getY());
		
		for (Element el : elementChildren.values()) {
			el.validateLayout();
		}
	}
	
	/**
	 * Stubbed for future use.
	 */
	public void setInitialized() {
		this.initialized = true;
	}
	
	/**
	 * Stubbed for future use.
	 */
	public boolean getInitialized() { return this.initialized; }
	
	//<editor-fold desc="Resize & Move">
	/**
	 * The preferred method for resizing Elements if the resize must effect nested
	 * Elements as well.
	 * 
	 * @param x the absolute x coordinate from screen x 0
	 * @param y the absolute y coordinate from screen y 0
	 * @param dir The Element.Borders used to determine the direction of the resize event
	 */
	public void resize(float x, float y, Borders dir) {
		float prevWidth = getWidth();
		float prevHeight = getHeight();
		float oX = x, oY = y;
		if (getElementParent() != null) { x -= getAbsoluteX()-getX(); }
		if (getElementParent() != null) { y -= getAbsoluteY()-getY(); }
		float nextX, nextY;
		if (dir == Borders.NW) {
			if (getLockToParentBounds()) { if (x <= 0) { x = 0; } }
			if (minDimensions != null) {
				if (getX()+getWidth()-x <= minDimensions.x) { x = getX()+getWidth()-minDimensions.x; }
			}
			if (resizeW) {
				setWidth(getX()+getWidth()-x);
				setX(x);
			}
			if (getLockToParentBounds()) { if (y <= 0) { y = 0; } }
			if (minDimensions != null) {
				if (getY()+getHeight()-y <= minDimensions.y) { y = getY()+getHeight()-minDimensions.y; }
			}
			if (resizeN) {
				setHeight(getY()+getHeight()-y);
				setY(y);
			}
		} else if (dir == Borders.N) {
			if (getLockToParentBounds()) { if (y <= 0) { y = 0; } }
			if (minDimensions != null) {
				if (getY()+getHeight()-y <= minDimensions.y) { y = getY()+getHeight()-minDimensions.y; }
			}
			if (resizeN) {
				setHeight(getY()+getHeight()-y);
				setY(y);
			}
		} else if (dir == Borders.NE) {
			nextX = oX-getAbsoluteX();
			if (getLockToParentBounds()) {
				float checkWidth = (getElementParent() == null) ? screen.getWidth() : getElementParent().getWidth();
				if (nextX >= checkWidth-getX()) {
					nextX = checkWidth-getX();
				}
			}
			if (minDimensions != null) {
				if (nextX <= minDimensions.x) { nextX = minDimensions.x; }
			}
			if (resizeE) {
				setWidth(nextX);
			}
			if (getLockToParentBounds()) { if (y <= 0) { y = 0; } }
			if (minDimensions != null) {
				if (getY()+getHeight()-y <= minDimensions.y) { y = getY()+getHeight()-minDimensions.y; }
			}
			if (resizeN) {
				setHeight(getY()+getHeight()-y);
				setY(y);
			}
		} else if (dir == Borders.W) {
			if (getLockToParentBounds()) { if (x <= 0) { x = 0; } }
			if (minDimensions != null) {
				if (getX()+getWidth()-x <= minDimensions.x) { x = getX()+getWidth()-minDimensions.x; }
			}
			if (resizeW) {
				setWidth(getX()+getWidth()-x);
				setX(x);
			}
		} else if (dir == Borders.E) {
			nextX = oX-getAbsoluteX();
			if (getLockToParentBounds()) {
				float checkWidth = (getElementParent() == null) ? screen.getWidth() : getElementParent().getWidth();
				if (nextX >= checkWidth-getX()) {
					nextX = checkWidth-getX();
				}
			}
			if (minDimensions != null) {
				if (nextX <= minDimensions.x) { nextX = minDimensions.x; }
			}
			if (resizeE) {
				setWidth(nextX);
			}
		} else if (dir == Borders.SW) {
			if (getLockToParentBounds()) { if (x <= 0) { x = 0; } }
			if (minDimensions != null) {
				if (getX()+getWidth()-x <= minDimensions.x) { x = getX()+getWidth()-minDimensions.x; }
			}
			if (resizeW) {
				setWidth(getX()+getWidth()-x);
				setX(x);
			}
			nextY = oY-getAbsoluteY();
			if (getLockToParentBounds()) {
				float checkHeight = (getElementParent() == null) ? screen.getHeight() : getElementParent().getHeight();
				if (nextY >= checkHeight-getY()) {
					nextY = checkHeight-getY();
				}
			}
			if (minDimensions != null) {
				if (nextY <= minDimensions.y) { nextY = minDimensions.y; }
			}
			if (resizeS) {
				setHeight(nextY);
			}
		} else if (dir == Borders.S) {
			nextY = oY-getAbsoluteY();
			if (getLockToParentBounds()) {
				float checkHeight = (getElementParent() == null) ? screen.getHeight() : getElementParent().getHeight();
				if (nextY >= checkHeight-getY()) {
					nextY = checkHeight-getY();
				}
			}
			if (minDimensions != null) {
				if (nextY <= minDimensions.y) { nextY = minDimensions.y; }
			}
			if (resizeS) {
				setHeight(nextY);
			}
		} else if (dir == Borders.SE) {
			nextX = oX-getAbsoluteX();
			if (getLockToParentBounds()) {
				float checkWidth = (getElementParent() == null) ? screen.getWidth() : getElementParent().getWidth();
				if (nextX >= checkWidth-getX()) {
					nextX = checkWidth-getX();
				}
			}
			if (minDimensions != null) {
				if (nextX <= minDimensions.x) { nextX = minDimensions.x; }
			}
			if (resizeE) {
				setWidth(nextX);
			}
			nextY = oY-getAbsoluteY();
			if (getLockToParentBounds()) {
				float checkHeight = (getElementParent() == null) ? screen.getHeight() : getElementParent().getHeight();
				if (nextY >= checkHeight-getY()) {
					nextY = checkHeight-getY();
				}
			}
			if (minDimensions != null) {
				if (nextY <= minDimensions.y) { nextY = minDimensions.y; }
			}
			if (resizeS) {
				setHeight(nextY);
			}
		}
		float diffX = prevWidth-getWidth();
		float diffY = prevHeight-getHeight();
		controlResizeHook();
		boolean childResize = true;
		if (layout != null) {
			if (layout.getHandlesResize()) {
				childResize = false;
				layout.resize();
			}
		}
		if (childResize) {
			for (Element el : elementChildren.values()) {
				el.childResize(diffX,diffY,dir);
				el.controlResizeHook();
			}
		}
	}
	
	// TODO: enforce minimum size
	private void childResize(float diffX, float diffY, Borders dir) {
		boolean minSize = !(minDimensions == null);
		if (dir == Borders.NW || dir == Borders.N || dir == Borders.NE) {
			if (getScaleNS()) setHeight(getHeight()-diffY);
			if ((getDocking() == Docking.NW || getDocking() == Docking.NE) && !getScaleNS()) setY(getY()-diffY);
		} else if (dir == Borders.SW || dir == Borders.S || dir == Borders.SE) {
			if (getScaleNS()) setHeight(getHeight()-diffY);
			if ((getDocking() == Docking.NW || getDocking() == Docking.NE) && !getScaleNS()) setY(getY()-diffY);
		}
		if (dir == Borders.NW || dir == Borders.W || dir == Borders.SW) {
			if (getScaleEW()) setWidth(getWidth()-diffX);
			if ((getDocking() == Docking.NE || getDocking() == Docking.SE) && !getScaleEW()) setX(getX()-diffX);
		} else if (dir == Borders.NE || dir == Borders.E || dir == Borders.SE) {
			if (getScaleEW()) setWidth(getWidth()-diffX);
			if ((getDocking() == Docking.NE || getDocking() == Docking.SE) && !getScaleEW()) setX(getX()-diffX);
		}
		for (Element el : elementChildren.values()) {
			el.childResize(diffX,diffY,dir);
			el.controlResizeHook();
		}
	}
	
	/**
	 * Overridable method for extending the resize event
	 */
	public void controlResizeHook() {
		
	}
	
	public void sizeToContent() {
		float innerX = 10000, innerY = 10000, innerW = -10000, innerH = -10000;
		float currentHeight = getHeight();
		Map<Element,Float> newY = new HashMap();
		for (Element child : elementChildren.values()) {
			float x = child.getX();
			float y = currentHeight-(child.getY()+child.getHeight());
			float w = child.getX()+child.getWidth();
			float h = currentHeight-child.getY();
			if (x < innerX) innerX = x;
			if (y < innerY) innerY = y;
			if (w > innerW) innerW = w;
			if (h > innerH) innerH = h;
			newY.put(child, h);
		}
		this.setDimensions(innerW+innerX, innerH+innerY);
		for (Element child : elementChildren.values()) {
			float diff = newY.get(child);
			child.setY(innerH-(diff-innerY));
		}
	}
	
	/**
	 * Moves the Element to the specified coordinates
	 * @param x The new x screen coordinate of the Element
	 * @param y The new y screen coordinate of the Element
	 */
	public void moveTo(float x, float y) {
		if (getLockToParentBounds()) {
			if (x < 0) { x = 0; }
			if (y < 0) { y = 0; }
			if (getElementParent() != null) {
				if (x > getElementParent().getWidth()-getWidth()) {
					x = getElementParent().getWidth()-getWidth();
				}
				if (y > getElementParent().getHeight()-getHeight()) {
					y = getElementParent().getHeight()-getHeight();
				}
			} else {
				if (x > screen.getWidth()-getWidth()) {
					x = screen.getWidth()-getWidth();
				}
				if (y > screen.getHeight()-getHeight()) {
					y = screen.getHeight()-getHeight();
				}
			}
		}
		setX(x);
		setY(y);
		controlMoveHook();
	}
	
	/**
	 * Overridable method for extending the move event
	 */
	public void controlMoveHook() {
		
	}
	//</editor-fold>
	
	//<editor-fold desc="Auto Centering">
	/**
	 * Centers the Element to it's parent Element.  If the parent element is null, it will use the screen's width/height.
	 */
	public void centerToParent() {
		if (elementParent == null) {
			setPosition(screen.getWidth()/2-(getWidth()/2),screen.getHeight()/2-(getHeight()/2));
		} else {
			setPosition(elementParent.getWidth()/2-(getWidth()/2),elementParent.getHeight()/2-(getHeight()/2));
		}
	}
	
	public void centerToParentV() {
		if (elementParent == null) {
			setPosition(getX(),screen.getHeight()/2-(getHeight()/2));
		} else {
			setPosition(getX(),elementParent.getHeight()/2-(getHeight()/2));
		}
	}
	
	public void centerToParentH() {
		if (elementParent == null) {
			setPosition(screen.getWidth()/2-(getWidth()/2),getY());
		} else {
			setPosition(elementParent.getWidth()/2-(getWidth()/2),getY());
		}
	}
	//</editor-fold>
	
	//<editor-fold desc="Resze Borders">
	/**
	 * Set the north, west, east and south borders in number of pixels
	 * @param borderSize 
	 */
	public void setResizeBorders(float borderSize) {
		borders.set(borderSize,borderSize,borderSize,borderSize);
	}
	
	/**
	 * Set the north, west, east and south borders in number of pixels
	 * @param nBorder float
	 * @param wBorder float
	 * @param eBorder float
	 * @param sBorder float
	 */
	public void setResizeBorders(float nBorder, float wBorder, float eBorder, float sBorder) {
		borders.setX(nBorder);
		borders.setY(wBorder);
		borders.setZ(eBorder);
		borders.setW(sBorder);
	}
	
	/**
	 * Sets the width of north border in number of pixels
	 * @param nBorder float
	 */
	public void setNorthResizeBorder(float nBorder) {
		borders.setX(nBorder);
	}
	
	/**
	 * Sets the width of west border in number of pixels
	 * @param wBorder float
	 */
	public void setWestResizeBorder(float wBorder) {
		borders.setY(wBorder);
	}
	
	/**
	 * Sets the width of east border in number of pixels
	 * @param eBorder float
	 */
	public void setEastResizeBorder(float eBorder) {
		borders.setZ(eBorder);
	}
	
	/**
	 * Sets the width of south border in number of pixels
	 * @param sBorder float
	 */
	public void setSouthResizeBorder(float sBorder) {
		borders.setW(sBorder);
	}
	
	/**
	 * Returns the height of the north resize border
	 * 
	 * @return float
	 */
	public float getResizeBorderNorthSize() {
		return this.borderHandles.x;
	}
	
	/**
	 * Returns the width of the west resize border
	 * 
	 * @return float
	 */
	public float getResizeBorderWestSize() {
		return this.borderHandles.y;
	}
	
	/**
	 * Returns the width of the east resize border
	 * 
	 * @return float
	 */
	public float getResizeBorderEastSize() {
		return this.borderHandles.z;
	}
	
	/**
	 * Returns the height of the south resize border
	 * 
	 * @return float
	 */
	public float getResizeBorderSouthSize() {
		return this.borderHandles.w;
	}
	//</editor-fold>
	
	//<editor-fold desc="Mesh & Geometry">
	/**
	 * Returns a pointer to the custom mesh used to render the Element.
	 * 
	 * @return ElementGridQuad model
	 */
	public ElementQuadGrid getModel() {
		return this.model;
	}
	
	/**
	 * Returns the Element's Geometry.
	 * @return 
	 */
	public Geometry getGeometry() {
		return this.geom;
	}
	//</editor-fold>
	
	//<editor-fold desc="Fonts & Text">
	/**
	 * Sets the element's text layer font
	 * @param fontPath String The font asset path
	 */
	public void setFont(String fontPath) {
		font = app.getAssetManager().loadFont(fontPath);
		if (textElement != null) {
			String text = this.getText();
			textElement.removeFromParent();
			textElement = null;
			setText(text);
		}
	}
	
	/**
	 * Returns the Bitmapfont used by the element's text layer
	 * @return BitmapFont font
	 */
	public BitmapFont getFont() {
		return this.font;
	}
	
	/**
	 * Sets the element's text layer font size
	 * @param fontSize float The size to set the font to
	 */
	public void setFontSize(float fontSize) {
		this.fontSize = fontSize;
		if (textElement != null) {
			textElement.setSize(fontSize);
		}
	}
	
	/**
	 * Returns the element's text layer font size
	 * @return float fontSize
	 */
	public float getFontSize() {
		return this.fontSize;
	}
	
	/**
	 * Sets the element's text layer font color
	 * @param fontColor ColorRGBA The color to set the font to
	 */
	public void setFontColor(ColorRGBA fontColor) {
		this.fontColor = fontColor;
		if (textElement != null) {
			textElement.setColor(fontColor);
		}
	}
	
	/**
	 * Return the element's text layer font color
	 * @return ColorRGBA fontColor
	 */
	public ColorRGBA getFontColor() {
		return this.fontColor;
	}
	
	/**
	 * Sets the element's text layer horizontal alignment
	 * 
	 * @param textAlign 
	 */
	public void setTextAlign(BitmapFont.Align textAlign) {
		this.textAlign = textAlign;
		if (textElement != null) {
			textElement.setAlignment(textAlign);
		}
	}
	
	/**
	 * Returns the element's text layer horizontal alignment
	 * @return Align text Align
	 */
	public BitmapFont.Align getTextAlign() {
		return this.textAlign;
	}
	
	/**
	 * Sets the element's text layer vertical alignment
	 * 
	 * @param textVAlign 
	 */
	public void setTextVAlign(BitmapFont.VAlign textVAlign) {
		this.textVAlign = textVAlign;
		if (textElement != null) {
			textElement.setVerticalAlignment(textVAlign);
		}
	}
	
	/**
	 * Returns the element's text layer vertical alignment
	 * @return VAlign textVAlign
	 */
	public BitmapFont.VAlign getTextVAlign() {
		return this.textVAlign;
	}
	
	/**
	 * Sets the element's text later wrap mode
	 * @param textWrap LineWrapMode textWrap
	 */
	public void setTextWrap(LineWrapMode textWrap) {
		this.textWrap = textWrap;
		if (textElement != null) {
			textElement.setLineWrapMode(textWrap);
		}
	}
	
	/**
	 * Returns the element's text layer wrap mode
	 * @return LineWrapMode textWrap
	 */
	public LineWrapMode getTextWrap() {
		return this.textWrap;
	}
	
	/**
	 * Sets the elements text layer position
	 * @param x Position's x coord
	 * @param y Position's y coord
	 */
	public void setTextPosition(float x, float y) {
		this.textPosition = new Vector2f(x,y);
	}
	
	/**
	 * Returns the current x, y coords of the element's text layer
	 * @return Vector2f textPosition
	 */
	public Vector2f getTextPosition() {
		return this.textPosition;
	}
	
	/**
	 * Sets the padding set for the element's text layer
	 * @param textPadding 
	 */
	public void setTextPadding(float textPadding) {
		setTextPadding(textPadding,textPadding,textPadding,textPadding);
	}
	
	public void setTextPadding(float left, float right, float top, float bottom) {
		this.textPadding.set(left,right,top,bottom);
	}
	
	public void setTextPadding(Vector4f textPadding) {
		this.textPadding.set(textPadding);
	}
	
	/**
	 * Returns the ammount of padding set for the elements text layer
	 * @return float textPadding
	 */
	public float getTextPadding() {
		return this.textPadding.x;
	}
	
	public Vector4f getTextPaddingVec() {
		return this.textPadding;
	}
	
	/**
	 * Updates the element's textlayer position and boundary
	 */
	protected void updateTextElement() {
		if (textElement != null) {
			textElement.setLocalTranslation(textPosition.x+textPadding.x, getHeight()-(textPosition.y+textPadding.z), textElement.getLocalTranslation().z);
			textElement.setBox(new Rectangle(0,0,dimensions.x-(textPadding.x+textPadding.y),dimensions.y-(textPadding.z+textPadding.w)));
		}
	}
	
	public void resetTextElement() {
		if (textElement != null) {
			textElement.setLocalTranslation(textPosition.x+textPadding.x, getHeight()-(textPosition.y+textPadding.z), textElement.getLocalTranslation().z);
			textElement.setBox(new Rectangle(0,0,dimensions.x-(textPadding.x+textPadding.y),25));
		}
	}
	
	public void removeTextElement() {
		if (textElement != null) {
			textElement.removeFromParent();
			textElement = null;
		}
	}
	
	/**
	 * Sets the text of the element.
	 * @param text String The text to display.
	 */
	public void setText(String text) {
		this.text = text;
		if (textElement == null) {
			textElement = new BitmapText(font, false);
			textElement.setBox(new Rectangle(0,0,dimensions.x,dimensions.y));
		//	textElement = new TextElement(screen, Vector2f.ZERO, getDimensions());
		}
		textElement.setLineWrapMode(textWrap);
		textElement.setAlignment(textAlign);
		textElement.setVerticalAlignment(textVAlign);
		textElement.setSize(fontSize);
		textElement.setColor(fontColor);
		textElement.setText(text);
		updateTextElement();
		if (textElement.getParent() == null) {
			this.attachChild(textElement);
		}
	}
	
	/**
	 * Retuns the current visible text of the element.
	 * @return String text
	 */
	public String getText() {
		return this.text;
	}
	
	/**
	 * Returns a pointer to the BitmapText element of this Element.  Returns null
	 * if setText() has not been called.
	 * 
	 * @return BitmapText textElement
	 */
	public BitmapText getTextElement() {
//	public TextElement getTextElement() {
		return this.textElement;
	}
	
	/**
	 * Updates font materials with any changes to clipping layers
	 */
	private void setFontPages() {
		if (textElement != null) {
			if (!isVisible) {
				for (int i = 0; i < font.getPageSize(); i++) {
					this.font.getPage(i).setVector4("Clipping", clippingBounds);
					this.font.getPage(i).setBoolean("UseClipping", true);
				}
			} else {
				if (isClipped) {
					for (int i = 0; i < font.getPageSize(); i++) {
						this.font.getPage(i).setVector4("Clipping", clippingBounds.add(textClipPadding.x, textClipPadding.y, -textClipPadding.z, -textClipPadding.w));
						this.font.getPage(i).setBoolean("UseClipping", true);
					}
				} else {
					for (int i = 0; i < font.getPageSize(); i++) {
						this.font.getPage(i).setBoolean("UseClipping", false);
					}
				}
			} 
		}
	}
	//</editor-fold>
	
	//<editor-fold desc="Materials, Textures & Atlas">
	private void throwParserException() {
		try {
			throw new java.text.ParseException("The provided texture information does not conform to the expected standard of x=(int)|y=(int)|w=(int)|h=(int)", 0);
		} catch (ParseException ex) {
			Logger.getLogger(Element.class.getName()).log(Level.SEVERE, "The provided texture information does not conform to the expected standard of x=(int)|y=(int)|w=(int)|h=(int)", ex);
		}
	}
	
	/**
	 * Sets the texture to use as an atlas image as well as the atlas image coords.
	 * @param tex The texture to use as a local atlas image
	 * @param queryString The position of the desire atlas image (e.g. "x=0|y=0|w=50|h=50")
	 */
	public void setTextureAtlasImage(Texture tex, String queryString) {
		this.useLocalTexture = false;
		
		this.defaultTex = tex;
		mat.setTexture("ColorMap", tex);
		mat.setColor("Color", new ColorRGBA(1,1,1,1));
		mat.setBoolean("UseEffectTexCoords", true);
		
		this.useLocalAtlas = true;
		this.atlasCoords = queryString;
		
		float[] coords = screen.parseAtlasCoords(queryString);
		float textureAtlasX = coords[0];
		float textureAtlasY = coords[1];
		float textureAtlasW = coords[2];
		float textureAtlasH = coords[3];

		float imgWidth = defaultTex.getImage().getWidth();
		float imgHeight = defaultTex.getImage().getHeight();
		float pixelWidth = 1f/imgWidth;
		float pixelHeight = 1f/imgHeight;

		textureAtlasY = imgHeight-textureAtlasY-textureAtlasH;
		
		this.model = new ElementQuadGrid(this.dimensions, borders, imgWidth, imgHeight, pixelWidth, pixelHeight, textureAtlasX, textureAtlasY, textureAtlasW, textureAtlasH);
		geom.setMesh(model);
	}
	
	/**
	 * Returns the current unparsed string representing the Element's atlas image
	 * @return 
	 */
	public String getAtlasCoords() { return this.atlasCoords; }
	
	/**
	 * Sets the element image to the specified x/y/width/height
	 * @param queryString (e.g. "x=0|y=0|w=50|h=50")
	 */
	public void updateTextureAtlasImage(String queryString) {
		float[] coords = screen.parseAtlasCoords(queryString);
		float textureAtlasX = coords[0];
		float textureAtlasY = coords[1];
		float textureAtlasW = coords[2];
		float textureAtlasH = coords[3];
		
		float imgWidth = defaultTex.getImage().getWidth();
		float imgHeight = defaultTex.getImage().getHeight();
		float pixelWidth = 1f/imgWidth;
		float pixelHeight = 1f/imgHeight;

		textureAtlasY = imgHeight-textureAtlasY-textureAtlasH;
		
		getModel().updateTexCoords(textureAtlasX, textureAtlasY, textureAtlasW, textureAtlasH);
	}
	
	/**
	 * Returns if the element is using a local texture atlas of the screen defined texture atlas
	 * @return 
	 */
	public boolean getUseLocalAtlas() { return this.useLocalAtlas; }
	
	/**
	 * Returns the difference between the placement of the elements current image and the given texture coords.
	 * @param coords The x/y coords of the new image
	 * @return Vector2f containing The difference between the given coords and the original image
	 */
	public Vector2f getAtlasTextureOffset(float[] coords) {
		Texture tex;
		if (defaultTex != null) tex = defaultTex;
		else					tex = screen.getAtlasTexture();
		float imgWidth = tex.getImage().getWidth();
		float imgHeight = tex.getImage().getHeight();
		float pixelWidth = 1f/imgWidth;
		float pixelHeight = 1f/imgHeight;

		return new Vector2f( getModel().getEffectOffset( pixelWidth*coords[0], pixelHeight*(imgHeight-coords[1]-coords[3]) ));
	}
	
	public void setUseLocalTexture(boolean useLocalTexture) {
		this.useLocalTexture = useLocalTexture;
	}
	
	public boolean getUseLocalTexture() { return this.useLocalTexture; }
	
	/**
	 * Will set the textures WrapMode to repeat if enabled.<br/><br/>
	 * NOTE: This only works when texture atlasing has not been enabled.
	 * For info on texture atlas usage, see both:<br/>
	 * @see Screen#setUseTextureAtlas(boolean enable, String path) 
	 * @see #setTextureAtlasImage(com.jme3.texture.Texture tex, java.lang.String path) 
	 * @param tileImage 
	 */
	public void setTileImage(boolean tileImage) {
		this.useLocalTexture = true;
		
		this.tileImage = tileImage;
		if (tileImage)
			((Texture)mat.getParam("ColorMap").getValue()).setWrap(Texture.WrapMode.Repeat);
		else
			((Texture)mat.getParam("ColorMap").getValue()).setWrap(Texture.WrapMode.Clamp);
		setDimensions(dimensions);
	}
	
	public boolean getTileImage() {
		return this.tileImage;
	}
	
	public void setTileImageByKey(String style, String key) {
		boolean tile = false;
		try {
			tile = screen.getStyle(style).getBoolean(key);
		} catch (Exception ex) {  }
		setTileImage(tile);
	}
	
	public void setClipPaddingByKey(String style, String key) {
		try {
			setClipPadding(screen.getStyle(style).getFloat(key));
		} catch (Exception ex) {  }
		try {
			setClipPadding(screen.getStyle(style).getVector4f(key));
		} catch (Exception ex) {  }
	}
	
	public void setTextPaddingByKey(String style, String key) {
		try {
			setTextPadding(screen.getStyle(style).getFloat(key));
		} catch (Exception ex) {  }
		try {
			setTextPadding(screen.getStyle(style).getVector4f(key));
		} catch (Exception ex) {  }
	}
	
	public void setTextClipPaddingByKey(String style, String key) {
		try {
			setTextClipPadding(screen.getStyle(style).getFloat(key));
		} catch (Exception ex) {  }
		try {
			setTextClipPadding(screen.getStyle(style).getVector4f(key));
		} catch (Exception ex) {  }
	}
	
	/**
	 * Returns the default material for the element
	 * @return Material mat
	 */
	public Material getMaterial() {
		return this.mat;
	}
	
	/**
	 * A way to override the default material of the element.
	 * 
	 * NOTE: It is important that the shader used with the new material is either:
	 * A: The provided Unshaded material contained with this library, or
	 * B: The custom shader contains the caret, text range, clipping and effect 
	 *    handling provided in the default shader.
	 * 
	 * @param mat The Material to use for rendering this Element.
	 */
	public void setLocalMaterial(Material mat) {
		this.mat = mat;
		this.setMaterial(mat);
	}
	
	/**
	 * Returns the default material for the element
	 * @param mat 
	 */
	public void setElementMaterial(Material mat) {
		this.mat = mat;
	}
	
	/**
	 * Returns a pointer to the Material used for rendering this Element.
	 * 
	 * @return Material mat
	 */
	public Material getElementMaterial() {
		return this.mat;
	}
	
	/**
	 * Returns the default Texture for the Element
	 * 
	 * @return Texture defaultTexture
	 */
	public Texture getElementTexture() {
		return this.defaultTex;
	}
	
	/**
	 * Adds an alpha map to the Elements material
	 * @param alphaMap A String path to the alpha map
	 */
	public void setAlphaMap(String alphaMap) {
		Texture alpha = null;
		if (screen.getUseTextureAtlas() && !useLocalTexture) {
			if (this.getElementTexture() != null)	alpha = getElementTexture();
			else									alpha = screen.getAtlasTexture();
			Vector2f alphaOffset = getAtlasTextureOffset(screen.parseAtlasCoords(alphaMap));
			mat.setVector2("OffsetAlphaTexCoord", alphaOffset);
		} else {
			alpha = app.getAssetManager().loadTexture(alphaMap);
			alpha.setMinFilter(Texture.MinFilter.BilinearNoMipMaps);
			alpha.setMagFilter(Texture.MagFilter.Nearest);
			alpha.setWrap(Texture.WrapMode.Clamp);
		}
		
		this.alphaMap = alpha;
		
		if (defaultTex == null) {
			if (!screen.getUseTextureAtlas() || useLocalTexture) {
				float imgWidth = alpha.getImage().getWidth();
				float imgHeight = alpha.getImage().getHeight();
				float pixelWidth = 1f/imgWidth;
				float pixelHeight = 1f/imgHeight;

				this.model = new ElementQuadGrid(this.dimensions, borders, imgWidth, imgHeight, pixelWidth, pixelHeight, 0, 0, imgWidth, imgHeight);

				geom.setMesh(model);
			} else {
				float[] coords = screen.parseAtlasCoords(alphaMap);
				float textureAtlasX = coords[0];
				float textureAtlasY = coords[1];
				float textureAtlasW = coords[2];
				float textureAtlasH = coords[3];
				
				float imgWidth = alpha.getImage().getWidth();
				float imgHeight = alpha.getImage().getHeight();
				float pixelWidth = 1f/imgWidth;
				float pixelHeight = 1f/imgHeight;
				
				textureAtlasY = imgHeight-textureAtlasY-textureAtlasH;
				
				model = new ElementQuadGrid(this.getDimensions(), borders, imgWidth, imgHeight, pixelWidth, pixelHeight, textureAtlasX, textureAtlasY, textureAtlasW, textureAtlasH);
				
				geom.setMesh(model);
				mat.setVector2("OffsetAlphaTexCoord", new Vector2f(0,0));
			}
		}
		mat.setTexture("AlphaMap", alpha);
	}
	
	public Texture getAlphaMap() {
		return this.alphaMap;
	}
	
	public void setColorMap(String colorMap) {
		Texture color = null;
		if (screen.getUseTextureAtlas() && !useLocalTexture) {
			if (this.getElementTexture() != null)	color = getElementTexture();
			else									color = screen.getAtlasTexture();
		} else {
			color = app.getAssetManager().loadTexture(colorMap);
			color.setMinFilter(Texture.MinFilter.BilinearNoMipMaps);
			color.setMagFilter(Texture.MagFilter.Nearest);
			color.setWrap(Texture.WrapMode.Clamp);
		}
		
		this.defaultTex = color;
		
		if (!screen.getUseTextureAtlas() || useLocalTexture) {
			float imgWidth = color.getImage().getWidth();
			float imgHeight = color.getImage().getHeight();
			float pixelWidth = 1f/imgWidth;
			float pixelHeight = 1f/imgHeight;

			this.model = new ElementQuadGrid(this.dimensions, borders, imgWidth, imgHeight, pixelWidth, pixelHeight, 0, 0, imgWidth, imgHeight);

			geom.setMesh(model);
		} else {
			float[] coords = screen.parseAtlasCoords(colorMap);
			float textureAtlasX = coords[0];
			float textureAtlasY = coords[1];
			float textureAtlasW = coords[2];
			float textureAtlasH = coords[3];

			float imgWidth = color.getImage().getWidth();
			float imgHeight = color.getImage().getHeight();
			float pixelWidth = 1f/imgWidth;
			float pixelHeight = 1f/imgHeight;

			textureAtlasY = imgHeight-textureAtlasY-textureAtlasH;

			model = new ElementQuadGrid(this.getDimensions(), borders, imgWidth, imgHeight, pixelWidth, pixelHeight, textureAtlasX, textureAtlasY, textureAtlasW, textureAtlasH);

			geom.setMesh(model);
		}
		
		mat.setTexture("ColorMap", color);
		mat.setColor("Color", ColorRGBA.White);
	}
	
	public void rebuildModel() {
		if (!screen.getUseTextureAtlas() || useLocalTexture) {
			float imgWidth = defaultTex.getImage().getWidth();
			float imgHeight = defaultTex.getImage().getHeight();
			float pixelWidth = 1f/imgWidth;
			float pixelHeight = 1f/imgHeight;

			this.model = new ElementQuadGrid(this.dimensions, borders, imgWidth, imgHeight, pixelWidth, pixelHeight, 0, 0, imgWidth, imgHeight);
			geom.setMesh(model);
		} else {
			float[] coords = screen.parseAtlasCoords(this.atlasCoords);
			float textureAtlasX = coords[0];
			float textureAtlasY = coords[1];
			float textureAtlasW = coords[2];
			float textureAtlasH = coords[3];

			float imgWidth = defaultTex.getImage().getWidth();
			float imgHeight = defaultTex.getImage().getHeight();
			float pixelWidth = 1f/imgWidth;
			float pixelHeight = 1f/imgHeight;

			textureAtlasY = imgHeight-textureAtlasY-textureAtlasH;

			model = new ElementQuadGrid(this.getDimensions(), borders, imgWidth, imgHeight, pixelWidth, pixelHeight, textureAtlasX, textureAtlasY, textureAtlasW, textureAtlasH);
			geom.setMesh(model);
		}
	}
	//</editor-fold>
	
	//<editor-fold desc="Visibility">
	/**
	 * This may be remove soon and probably should not be used as the method of handling
	 * hide show was updated making this unnecissary.
	 * 
	 * @param wasVisible boolean
	 */
	public void setDefaultWasVisible(boolean wasVisible) {
		this.wasVisible = wasVisible;
	}
	
	/**
	 * Shows the current Element with the defined Show effect.  If no Show effect is defined, the Element will show as normal.
	 */
	public void showWithEffect() {
		Effect effect = getEffect(Effect.EffectEvent.Show);
		if (effect != null) {
			if (effect.getEffectType() == Effect.EffectType.FadeIn) {
				Effect clone = effect.clone();
				clone.setAudioFile(null);
				this.propagateEffect(clone, false);
			} else {
				if (getTextElement() != null)
					getTextElement().setAlpha(1f);
				screen.getEffectManager().applyEffect(effect);
			}
		} else
			this.show();
	}
	
	/**
	 * Sets this Element and any Element contained within it's nesting order to visible.
	 * 
	 * NOTE: Hide and Show relies on shader-based clipping
	 */
	public void show() {
		if (!isVisible) {
			screen.updateZOrder(getAbsoluteParent());
			this.isVisible = true;
			this.isClipped = wasClipped;
		//	updateClipping();
			updateClippingLayers();
			controlShowHook();
			
			if (getTextElement() != null)
				getTextElement().setAlpha(1f);
			
			if (getParent() == null) {
				if (getElementParent() != null) {
					getElementParent().attachChild(this);
				} else {
					screen.getGUINode().attachChild(this);
				}
			}
			
			for (Element el : elementChildren.values()) {
				el.childShow();
			}
		}
	}
	
	public void showAsModal(boolean showWithEffect) {
		isVisibleAsModal = true;
		screen.showAsModal(this,showWithEffect);
	}
	
	/**
	 * Recursive call for properly showing children of the Element.  I'm thinking this
	 * this needs to be a private method, however I need to verify this before I
	 * update it.
	 */
	public void childShow() {
		if (getTextElement() != null)
			getTextElement().setAlpha(1f);
		
		this.isVisible = wasVisible;
		this.isClipped = wasClipped;
	//	updateClipping();
		updateClippingLayers();
		controlShowHook();
		for (Element el : elementChildren.values()) {
			el.childShow();
		}
	}
	
	/**
	 * An overridable method for extending the show event.
	 */
	public void controlShowHook() {  }
	
	/**
	 * Hides the element using the current defined Hide effect.  If no Hide effect is defined, the Element will hide as usual.
	 */
	public void hideWithEffect() {
		Effect effect = getEffect(Effect.EffectEvent.Hide);
		if (effect != null) {
			if (effect.getEffectType() == Effect.EffectType.FadeOut) {
				Effect clone = effect.clone();
				clone.setAudioFile(null);
				this.propagateEffect(clone, true);
			} else
				screen.getEffectManager().applyEffect(effect);
			if (isVisibleAsModal) {
				isVisibleAsModal = false;
				screen.hideModalBackground();
			}
		} else
			this.hide();
	}
	
	/**
	 * Recursive call that sets this Element and any Element contained within it's 
	 * nesting order to hidden.
	 * 
	 * NOTE: Hide and Show relies on shader-based clipping
	 */
	public void hide() {
		if (isVisible) {
			if (isVisibleAsModal) {
				isVisibleAsModal = false;
				screen.hideModalBackground();
			}
			this.wasVisible = isVisible;
			this.isVisible = false;
			this.isClipped = true;
			
			if (screen.getUseToolTips())
				if (screen.getToolTipFocus() == this)
					screen.hideToolTip();
		}
	//	updateClipping();
		updateClippingLayers();
		controlHideHook();
		removeFromParent();
		for (Element el : elementChildren.values()) {
			el.childHide();
		}
	}
	
	/**
	 * For internal use.  This method should never be called directly.
	 */
	public void childHide() {
		if (isVisible) {
			this.wasVisible = isVisible;
			this.isVisible = false;
			this.isClipped = true;
			
			if (screen.getUseToolTips())
				if (screen.getToolTipFocus() == this)
					screen.hideToolTip();
		}
	//	updateClipping();
		updateClippingLayers();
		controlHideHook();
		for (Element el : elementChildren.values()) {
			el.childHide();
		}
	}
	
	/**
	 * Hides or shows the element (true = show, false = hide)
	 * @param visibleState 
	 */
	public void setIsVisible(boolean visibleState) {
		if (visibleState) {
			show();
		} else {
			hide();
		}
	}
	
	/**
	 * Toggles the Element's visibility based on the current state.
	 */
	public void setIsVisible() {
		if (getIsVisible())	hide();
		else				show();
	}
	
	/**
	 * An overridable method for extending the hide event.
	 */
	public void controlHideHook() {  }
	
	/**
	 * Return if the Element is visible
	 * 
	 * @return boolean isVisible
	 */
	public boolean getIsVisible() {
		return this.isVisible;
	}
	//</editor-fold>
	
	//<editor-fold desc="Cleanup">
	public void cleanup() {
		controlCleanupHook();
		for (Element el : elementChildren.values()) {
			el.cleanup();
		}
	}
	/**
	 * An overridable method for handling control specific cleanup.
	 */
	public void controlCleanupHook() {  }
	//</editor-fold>
	
	//<editor-fold desc="Clipping">
	/**
	 * Sets the elements clipping layer to the provided element.
	 * @param clippingLayer The element that provides the clipping boundaries.
	 */
	@Deprecated
	public void setClippingLayer(Element clippingLayer) {
		if (clippingLayer != null)
			addClippingLayer(clippingLayer);
		else
			removeClippingLayer(clippingLayer);
		/*
		if (clippingLayer != null) {
			this.isClipped = true;
			this.wasClipped = true;
			this.clippingLayer = clippingLayer;
			this.mat.setBoolean("UseClipping", true);
		} else {
			this.isClipped = false;
			this.wasClipped = false;
			this.clippingLayer = null;
			this.mat.setBoolean("UseClipping", false);
		}
		*/
	}
	
	/**
	 * Sets the elements clipping layer to the provided element.
	 * @param clippingLayer The element that provides the clipping boundaries.
	 */
	@Deprecated
	public void setSecondaryClippingLayer(Element secondaryClippingLayer) {
		if (secondaryClippingLayer != null)
			addClippingLayer(secondaryClippingLayer);
		else
			removeClippingLayer(secondaryClippingLayer);
		/*
		if (secondaryClippingLayer != null) {
			this.secondaryClippingLayer = secondaryClippingLayer;
		} else {
			this.secondaryClippingLayer = null;
		}
		*/
	}
	
	/**
	 * Recursive update of all child Elements clipping layer
	 * @param clippingLayer The clipping layer to apply
	 */
	@Deprecated
	public void setControlClippingLayer(Element clippingLayer) {
		setClippingLayer(clippingLayer);
	//	for (Element el : elementChildren.values()) {
	//		el.setControlClippingLayer(clippingLayer);
	//	}
	}
	
	/**
	 * Recursive update of all child Elements clipping & secondary clipping layers
	 * @param clippingLayer The clipping layer to apply
	 * @param secondaryClippingLayer The clipping layer's parent clipping layer to apply
	 */
	@Deprecated
	public void setControlClippingLayer(Element clippingLayer, Element secondaryClippingLayer) {
		setClippingLayer(clippingLayer);
		setSecondaryClippingLayer(secondaryClippingLayer);
		for (Element el : elementChildren.values()) {
			el.setControlClippingLayer(clippingLayer, secondaryClippingLayer);
		}
	}
	
	/**
	 * Returns if the Element's clipping layer has been set
	 * 
	 * @return boolean isClipped
	 */
	public boolean getIsClipped() {
		return isClipped;
	}
	
	/**
	 * Returns the elements clipping layer or null is element doesn't use clipping
	 * @return Element clippingLayer
	 */
	public Element getClippingLayer() {
		return this.clippingLayer;
	}
	
	/**
	 * Returns a Vector4f containing the current boundaries of the element's clipping layer
	 * @return Vector4f clippingBounds
	 */
	public Vector4f getClippingBounds() {
		return this.clippingBounds;
	}
	
	/**
	 * Adds a padding to the clippinglayer, in effect this contracts the size of the clipping
	 * bounds by the specified number of pixels
	 * @param clipPadding The number of pixels to pad the clipping area
	 */
	public void setClipPadding(float clipPadding) {
		this.clipPadding.set(
			clipPadding,clipPadding,clipPadding,clipPadding
		);
	}
	
	public void setClipPadding(float clipLeft, float clipRight, float clipTop, float clipBottom) {
		this.clipPadding.set(
			clipLeft, clipTop, clipRight, clipBottom
		);
	}
	
	public void setClipPadding(Vector4f clipPadding) {
		this.clipPadding.set(
			clipPadding
		);
	}
	
	/**
	 * Returns the current clipPadding
	 * @return float clipPadding
	 */
	public float getClipPadding() {
		return clipPadding.x;
	}
	
	public Vector4f getClipPaddingVec() {
		return clipPadding;
	}
	
	/**
	 * Shrinks the clipping area by set number of pixels
	 * 
	 * @param textClipPadding The number of pixels to pad the clipping area with on each side
	 */
	public void setTextClipPadding(float textClipPadding) {
		this.textClipPadding.set(
			textClipPadding,textClipPadding,textClipPadding,textClipPadding
		);
	}
	
	public void setTextClipPadding(float clipLeft, float clipRight, float clipTop, float clipBottom) {
		this.textClipPadding.set(
			clipLeft, clipTop, clipRight, clipBottom
		);
	}
	
	public void setTextClipPadding(Vector4f textClipPadding) {
		this.textClipPadding.set(
			textClipPadding
		);
	}
	
	public float getTextClipPadding() {
		return textClipPadding.x;
	}
	
	public Vector4f getTextClipPaddingVec() {
		return textClipPadding;
	}
	
	/**
	 * Updates the clipping bounds for any element that has a clipping layer
	 * 
	 * See updateLocalClipping
	 */
	@Deprecated
	public void updateClipping() {
	//	updateLocalClipping();
		updateLocalClippingLayer();
		for (Element el : elementChildren.values()) {
			el.updateClipping();
		}
	}
	
	/**
	 * Updates the clipping bounds for any element that has a clipping layer
	 */
	@Deprecated
	public void updateLocalClipping() {
		if (isVisible) {
			if (clippingLayer != null) {
				if (secondaryClippingLayer == null) {
					clippingBounds.set(
						clippingLayer.getAbsoluteX()+clipPadding.x,
						clippingLayer.getAbsoluteY()+clipPadding.y,
						clippingLayer.getAbsoluteWidth()-clipPadding.z,
						clippingLayer.getAbsoluteHeight()-clipPadding.w
					);
				} else {
					float clX = clippingLayer.getAbsoluteX();
					float sclX = secondaryClippingLayer.getAbsoluteX();
					float clY = clippingLayer.getAbsoluteY();
					float sclY = secondaryClippingLayer.getAbsoluteY();
					float clW = clippingLayer.getAbsoluteWidth();
					float sclW = secondaryClippingLayer.getAbsoluteWidth();
					float clH = clippingLayer.getAbsoluteHeight();
					float sclH = secondaryClippingLayer.getAbsoluteHeight();
					
					clippingBounds.set(
						(clX > sclX) ? clX+clipPadding.x : sclX+clipPadding.x,
						(clY > sclY) ? clY+clipPadding.y : sclY+clipPadding.y,
						(clW < sclW) ? clW-clipPadding.z : sclW-clipPadding.z,
						(clH < sclH) ? clH-clipPadding.w : sclH-clipPadding.w
					);
				}
				mat.setVector4("Clipping", clippingBounds);
				if (!(Boolean)mat.getParam("UseClipping").getValue())
					mat.setBoolean("UseClipping", true);
			} else {
				if ((Boolean)mat.getParam("UseClipping").getValue())
					mat.setBoolean("UseClipping", false);
			}
		} else {
			clippingBounds.set(0,0,0,0);
			mat.setVector4("Clipping", clippingBounds);
			if (!(Boolean)mat.getParam("UseClipping").getValue())
				mat.setBoolean("UseClipping", true);
		}
		setFontPages();
	}
	
	// New Clipping
	public void addClippingLayer(Element el) {
		ClippingDefine def = new ClippingDefine(el);
		propigateClippingLayerAdd(def);
	}
	
	public void addClippingLayer(Element el, Vector4f relativeClippingBounds) {
		ClippingDefine def = new ClippingDefine(el, relativeClippingBounds);
		propigateClippingLayerAdd(def);
	}
	
	public ClippingDefine getClippingDefine(Element el) {
		ClippingDefine def = null;
		
		for (ClippingDefine d : clippingLayers) {
			if (d.getElement() == el) {
				def = d;
				break;
			}
		}
		return def;
	}
	
	public void updateClippingLayer(Element el, Vector4f clip) {
		ClippingDefine def = getClippingDefine(el);
		if (def != null) {
			if (def.clip == null) def.clip = new Vector4f();
			def.clip.set(clip);
		}
		validateClipSettings();
		for (Element c : elementChildren.values()) {
			c.updateClippingLayer(el, clip);
		}
	}
	
	public void propigateClippingLayerAdd(ClippingDefine def) {
		if (!clippingLayers.contains(def))
			clippingLayers.add(def);
		validateClipSettings();
		for (Element c : elementChildren.values()) {
			c.propigateClippingLayerAdd(def);
		}
	}
	
	public void removeClippingLayer(Element el) {
		for (ClippingDefine def : clippingLayers) {
			if (def.getElement() == el)
				remClippingLayers.add(def);
		}
		if (!remClippingLayers.isEmpty()) {
			clippingLayers.removeAll(remClippingLayers);
			remClippingLayers.clear();
		}
		for (Element c : elementChildren.values()) {
			c.removeClippingLayer(el);
		}
	}
	
	public boolean getHasClippingLayers() {
		return !clippingLayers.isEmpty();
	}
	
	public void updateClippingLayers() {
		updateLocalClippingLayer();
		validateClipSettings();
		for (Element c : elementChildren.values()) {
			c.updateClippingLayers();
		}
	}
	
	public void updateLocalClippingLayer() {
		if (isVisible) {
			if (!clippingLayers.isEmpty()) {
				calcClipping();
				setFontPages();
			}
		}
	}
	
	private void calcClipping() {
		float cX = 0;
		float cY = 0;
		float cW = screen.getWidth();
		float cH = screen.getHeight();
		
		for (ClippingDefine def : clippingLayers) {
			clipTest.set(def.getClipping());
			if (def.getElement() != this) {
				clipTest.addLocal(
					def.getElement().getClipPaddingVec().x,
					def.getElement().getClipPaddingVec().y,
					-def.getElement().getClipPaddingVec().z,
					-def.getElement().getClipPaddingVec().w
				);
			}
			if (clipTest.x > cX)	cX = clipTest.x;
			if (clipTest.y > cY)	cY = clipTest.y;
			if (clipTest.z < cW)	cW = clipTest.z;
			if (clipTest.w < cH)	cH = clipTest.w;
		}
		clippingBounds.set(cX,cY,cW,cH);
	}
	
	protected void validateClipSettings() {
		if (!clippingLayers.isEmpty()) {
			this.isClipped = true;
			this.wasClipped = true;
			if (!(Boolean)mat.getParam("UseClipping").getValue())
				mat.setBoolean("UseClipping", true);
		} else {
			this.isClipped = false;
			this.wasClipped = false;
			if ((Boolean)mat.getParam("UseClipping").getValue())
				mat.setBoolean("UseClipping", false);
		}
		mat.setVector4("Clipping", clippingBounds);
	}
	
	public class ClippingDefine {
		public Element owner;
		public Vector4f clip = null;
		private Vector4f tempV4 = new Vector4f();
		
		public ClippingDefine(Element owner) {
			this.owner = owner;
		}
		public ClippingDefine(Element owner, Vector4f clip) {
			this.owner = owner;
			this.clip = new Vector4f(clip);
		}
		public Element getElement() { return owner; }
		public Vector4f getClipping() {
			if (clip == null) {
				tempV4.setX(owner.getAbsoluteX());
				tempV4.setY(owner.getAbsoluteY());
				tempV4.setZ(tempV4.getX()+owner.getWidth());
				tempV4.setW(tempV4.getY()+owner.getHeight());
			} else {
				float x = owner.getAbsoluteX();
				float y = owner.getAbsoluteY();
				tempV4.set(
					x+clip.x,
					y+clip.y,
					x+clip.z,
					y+clip.w
				);
			}
			return tempV4;
		}
	}
	//</editor-fold>
	
	//<editor-fold desc="Effects">
	/**
	 * Associates an Effect with this Element.  Effects are not automatically associated
	 * with the specified event, but instead, the event type is used to retrieve the Effect
	 * at a later point
	 * 
	 * @param effectEvent The Effect.EffectEvent the Effect is to be registered with
	 * @param effect The Effect to store
	 */
	public void addEffect(Effect.EffectEvent effectEvent, Effect effect) {
		addEffect(effect);
	}
	
	/**
	 * Associates an Effect with this Element.  Effects are not automatically associated
	 * with the specified event, but instead, the event type is used to retrieve the Effect
	 * at a later point
	 * 
	 * @param effect The Effect to store
	 */
	public void addEffect(Effect effect) {
		effects.remove(effect.getEffectEvent());
		if (!effects.containsKey(effect.getEffectEvent())) {
			effect.setElement(this);
			effects.put(effect.getEffectEvent(), effect);
		}
	}
	
	/**
	 * Removes the Effect associated with the Effect.EffectEvent specified
	 * @param effectEvent 
	 */
	public void removeEffect(Effect.EffectEvent effectEvent) {
		effects.remove(effectEvent);
	}
	
	/**
	 * Retrieves the Effect associated with the specified Effect.EffectEvent
	 * 
	 * @param effectEvent
	 * @return effect
	 */
	public Effect getEffect(Effect.EffectEvent effectEvent) {
		Effect effect = null;
		if (effects.get(effectEvent) != null)
			effect = effects.get(effectEvent).clone();
		return effect;
	}
	
	/**
	 * Called by controls during construction to prepopulate effects based on Styles.
	 * 
	 * @param styleName The String identifier of the Style
	 */
	protected void populateEffects(String styleName) {
		int index = 0;
		Effect effect;
		while ((effect = screen.getStyle(styleName).getEffect("event" + index)) != null) {
			effect = effect.clone();
			effect.setElement(this);
			this.addEffect(effect);
			index++;
		}
	}
	
	/**
	 * For internal use only - DO NOT CALL THIS METHOD
	 * @param effect Effect
	 * @param callHide boolean
	 */
	public void propagateEffect(Effect effect, boolean callHide) {
		Effect nEffect = effect.clone();
		nEffect.setCallHide(callHide);
		nEffect.setElement(this);
		screen.getEffectManager().applyEffect(nEffect);
		for (Element el : elementChildren.values()) {
			el.propagateEffect(effect, false);
		}
	}
	//</editor-fold>
	
	//<editor-fold desc="Alpha">
	/**
	 * Overrides the screen global alpha with the specified value. setIngoreGlobalAlpha must be enabled prior to calling this method.
	 * @param globalAlpha 
	 */
	public void setGlobalAlpha(float globalAlpha) {
		if (!ignoreGlobalAlpha) {
			getElementMaterial().setFloat("GlobalAlpha", globalAlpha);
			for (Element el : elementChildren.values()) {
				el.setGlobalAlpha(globalAlpha);
			}
		} else {
			getElementMaterial().setFloat("GlobalAlpha", 1);
		}
	}
	
	/**
	 * Will enable or disable the use of the screen defined global alpha setting.
	 * @param ignoreGlobalAlpha 
	 */
	public void setIgnoreGlobalAlpha(boolean ignoreGlobalAlpha) {
		this.ignoreGlobalAlpha = ignoreGlobalAlpha;
	}
	//</editor-fold>
	
	//<editor-fold desc="Focus">
	/**
	 * For use by the Form control (Do not call this method directly)
	 * @param form The form the Element has been added to
	 */
	public void setForm(Form form) {
		this.form = form;
	}
	
	/**
	 * Returns the form the Element is controlled by
	 * @return Form form
	 */
	public Form getForm() {
		return this.form;
	}
	
	/**
	 * Sets the tab index (This is assigned by the Form control. Do not call this method directly)
	 * @param tabIndex The tab index assigned to the Element
	 */
	public void setTabIndex(int tabIndex) {
		this.tabIndex = tabIndex;
	}
	
	/**
	 * Returns the tab index assigned by the Form control
	 * @return tabIndex
	 */
	public int getTabIndex() {
		return tabIndex;
	}
	
	
	/**
	 * For internal use - DO NOT CALL THIS METHOD
	 * @param hasFocus boolean
	 */
	public void setHasFocus(boolean hasFocus) {
		this.hasFocus = hasFocus;
	}
	
	/**
	 * Returns if the Element currently has input focus
	 * @return 
	 */
	public boolean getHasFocus() {
		return this.hasFocus;
	}
	
	public void setResetKeyboardFocus(boolean resetKeyboardFocus) {
		this.resetKeyboardFocus = resetKeyboardFocus;
	}
	
	public boolean getResetKeyboardFocus() { return this.resetKeyboardFocus; }
	
	//</editor-fold>
	
	// Off Screen Rendering Bridge
	public void addOSRBridge(OSRBridge bridge) {
		this.bridge = bridge;
		addControl(bridge);
		getElementMaterial().setTexture("ColorMap", bridge.getTexture());
		getElementMaterial().setColor("Color", ColorRGBA.White);
	}
	
	//<editor-fold desc="Tool Tips">
	/**
	 * Sets the Element's ToolTip text
	 * @param toolTip String
	 */
	public void setToolTipText(String toolTip) {
		this.toolTipText = toolTip;
	}
	
	/**
	 * Returns the Element's current ToolTip text
	 * @return String
	 */
	public String getToolTipText() {
		return toolTipText;
	}
	//</editor-fold>
	
	//<editor-fold desc="Modal">
	/**
	 * Enables standard modal mode for the Element.
	 * @param isModal 
	 */
	public void setIsModal(boolean isModal) {
		this.isModal = isModal;
	}
	
	/**
	 * Returns if the Element is currently modal
	 * @return Ret
	 */
	public boolean getIsModal() {
		return this.isModal;
	}
	
	/**
	 * For internal use - DO NOT CALL THIS METHOD
	 * @param hasFocus boolean
	 */
	public void setIsGlobalModal(boolean isGlobalModal) {
		this.isGlobalModal = isGlobalModal;
	}
	
	/**
	 * For internal use - DO NOT CALL THIS METHOD
	 * @param hasFocus boolean
	 */
	public boolean getIsGlobalModal() {
		return this.isGlobalModal;
	}
	//</editor-fold>
	
	//<editor-fold desc="User Data">
	/**
	 * Stores provided data with the Element
	 * @param elementUserData Object Data to store
	 */
	public void setElementUserData(Object elementUserData) {
		this.elementUserData = elementUserData;
	}
	
	/**
	 * Returns the data stored with this Element
	 * @return Object
	 */
	public Object getElementUserData() {
		return this.elementUserData;
	}
	//</editor-fold>
	
	Vector2f origin = new Vector2f(0,0);
	/**
	 * Stubbed for future use
	 * @param originX
	 * @param originY 
	 */
	public void setOrigin(float originX, float originY) {
		origin.set(originX, originY);
	}
	
	/**
	 * Stubbed for future use.
	 * @return 
	 */
	public Vector2f getOrigin() {
		return this.origin;
	}
	
	//<editor-fold desc="Layouts">
	public void setLayout(Layout layout) {
		this.layout = layout;
		this.layout.setOwner(this);
	}
	
	public Layout getLayout() { return this.layout; }
	
	public void setLayoutHints(LayoutHints layoutHints) {
		this.layoutHints = layoutHints;
	}
	
	public LayoutHints getLayoutHints() { return this.layoutHints; }
	
	public void layoutChildren() {
		if (layout != null) {
			layout.layoutChildren();
		}
		for (Element el : elementChildren.values()) {
			el.layoutChildren();
		}
	}
	//</editor-fold>
}
 public static enum Orientation {
	/**
	 * Vertical layout
	 */
	VERTICAL,
	/**
	 * Horizontal layout
	 */
	HORIZONTAL
}
 public class Effect implements Cloneable {
	public static enum EffectType {
		FadeIn,
		FadeOut,
		ZoomIn,
		ZoomOut,
		SlideIn,
		SlideOut,
		SlideTo,
		SpinIn,
		SpinOut,
		Pulse,
		ColorSwap,
		PulseColor,
		ImageSwap,
		ImageFadeIn,
		ImageFadeOut,
		Desaturate,
		Saturate
	}
	
	public enum EffectEvent {
		GetFocus,
		LoseFocus,
		Show,
		Hide,
		Hover,
		Press,
		Release,
		TabFocus,
		LoseTabFocus
	}
	
	public enum EffectDirection {
		Top,
		Bottom,
		Left,
		Right
	}
	
	private Element element;
	private EffectType type;
	private EffectEvent event;
	private float pass = 0.0f;
	private float time = 0.0f;
	private boolean direction = true;
	private float duration;
	private Vector2f destination;
	private boolean isActive = true;
	private boolean localActive = true;
	private Texture blendImage;
	private Vector2f blendImageOffset;
	private ColorRGBA blendColor;
	private ColorRGBA tempColor = new ColorRGBA();
	private boolean init = false;
	private boolean destroyOnHide = false;
	private EffectDirection effectDir = EffectDirection.Top;
	private Vector2f def = new Vector2f();
	private Vector2f diff = new Vector2f();
	private Vector2f fract = new Vector2f();
	private String audioFile = null;
	private float audioVolume = 1;
	private boolean callHide = true;
	private Interpolation interpolation;
	
	public Effect(EffectType type, EffectEvent event, float duration) {
		this(type, event, duration, null, 1);
	//	this.type = type;
	//	this.event = event;
	//	this.duration = duration;
	}
	
	public Effect (EffectType type, EffectEvent event, float duration, String audioFile, float audioVolume) {
		this.type = type;
		this.event = event;
		this.duration = duration;
		this.audioFile = audioFile;
		this.audioVolume = audioVolume;
		this.interpolation = Interpolation.linear;
	}
	
	public void setElement(Element element) {
		this.element = element;
	}
	
	public void setBlendImage(Texture blendImage) {
		this.blendImage = blendImage;
	}
	
	public void setBlendImageOffset(Vector2f blendImageOffset) {
		this.blendImageOffset = blendImageOffset;
	}
	
	public void setColor(ColorRGBA blendColor) {
		this.blendColor = blendColor;
	}
	
	public Element getElement() {
		return this.element;
	}
	
	public float getDuration() {
		return this.duration;
	}
	
	public boolean getIsActive() {
		return this.isActive;
	}
	
	public void setIsActive(boolean isActive) {
		this.isActive = isActive;
	}
	
	public void setCallHide(boolean callHide) {
		this.callHide = callHide;
	}
	
	public EffectEvent getEffectEvent() {
		return this.event;
	}
	
	public EffectType getEffectType() {
		return this.type;
	}
	
	public void setEffectDirection(EffectDirection effectDir) {
		this.effectDir = effectDir;
	}
	
	public EffectDirection getEffectDirection() {
		return this.effectDir;
	}
	
	public void setEffectDestination(Vector2f destination) {
		this.destination = destination;
	}
	
	public Vector2f getEffectDestination() {
		return this.destination;
	}
	
	public void setDestroyOnHide(boolean destroyOnHide) {
		this.destroyOnHide = destroyOnHide;
	}
	
	public void setAudioFile(String audioFile) {
		this.audioFile = audioFile;
	}
	
	public String getAudioFile() {
		return this.audioFile;
	}
	
	public void setAudioVolume(float audioVolume) {
		this.audioVolume = audioVolume;
	}
	
	public float getAudioVolume() {
		return this.audioVolume;
	}
	
	public void setInterpolation(Interpolation interpolation) {
		this.interpolation = null;
		this.interpolation = interpolation;
	}
	
	public Interpolation getInterpolation() {
		return this.interpolation;
	}
	
	public void update(float tpf) {
		switch(type) {
			case ZoomIn:
				updateZoomIn();
				break;
			case ZoomOut:
				updateZoomOut();
				break;
			case SlideIn:
				updateSlideIn();
				break;
			case SlideOut:
				updateSlideOut();
				break;
			case SlideTo:
				updateSlideTo();
				break;
			case SpinIn:
				updateSpinIn();
				break;
			case SpinOut:
				updateSpinOut();
				break;
			case FadeIn:
				updateFadeIn();
				break;
			case FadeOut:
				updateFadeOut();
				break;
			case ImageSwap:
				updateImageSwap();
				break;
			case ImageFadeIn:
				updateImageFadeIn();
				break;
			case ImageFadeOut:
				updateImageFadeOut();
				break;
			case ColorSwap:
				updateColorSwap();
				break;
			case Saturate:
			    updateSaturate();
			    break;
			case Desaturate:
			    updateDesaturate();
			    break;
		}

		if (isActive) {
			if (type != EffectType.Pulse && type != EffectType.PulseColor) {
				time += tpf;
				pass = interpolation.apply(time/duration);
				if (pass >= 1.0) {
					pass = 1.0f;
					localActive = false;
				}
			} else {
				if (type == EffectType.Pulse) {
					if (pass >= 1.0f) {
						pass = 1.0f;
						direction = false;
					} else if (pass <= 0.0f) {
						pass = 0.0f;
						direction = true;
					}
					if (direction)	time += tpf;
					else			time -= tpf;
					pass = interpolation.apply(time/duration);
					if (!init) {
						element.getElementMaterial().setBoolean("UseEffect", true);
						element.getElementMaterial().setBoolean("EffectFade", false);
						element.getElementMaterial().setBoolean("EffectPulse", true);
						element.getElementMaterial().setBoolean("EffectPulseColor", false);
						element.getElementMaterial().setBoolean("EffectImageSwap", false);
						element.getElementMaterial().setTexture("EffectMap", blendImage);
						if ((element.getScreen().getUseTextureAtlas() || element.getUseLocalAtlas()) && !element.getUseLocalTexture())
							element.getElementMaterial().setVector2("OffsetTexCoord", blendImageOffset);
						init = true;
					}
					element.getElementMaterial().setFloat("EffectStep", pass);
				} else if (type == EffectType.PulseColor) {
					if (pass >= 1.0f) {
						pass = 1.0f;
						direction = false;
					} else if (pass <= 0.0f) {
						pass = 0.0f;
						direction = true;
					}
					if (direction)	time += tpf;
					else			time -= tpf;
					pass = interpolation.apply(time/duration);
					if (!init) {
						element.getElementMaterial().setBoolean("UseEffect", true);
						element.getElementMaterial().setBoolean("EffectFade", false);
						element.getElementMaterial().setBoolean("EffectPulse", false);
						element.getElementMaterial().setBoolean("EffectPulseColor", true);
						element.getElementMaterial().setBoolean("EffectImageSwap", false);
						element.getElementMaterial().setColor("EffectColor", blendColor);
						init = true;
					}
					element.getElementMaterial().setFloat("EffectStep", pass);
				}
			}
		}
	}
	
    private void destoryElement() {
		if (element.getElementParent() == null) {
			element.getScreen().removeElement(element);
		} else {
			element.getElementParent().removeChild(element);
		}
	}
	
	public void resetShader() {
		element.getElementMaterial().setBoolean("UseEffect", false);
		element.getElementMaterial().setBoolean("EffectFade", false);
		element.getElementMaterial().setBoolean("EffectPulse", false);
		element.getElementMaterial().setBoolean("EffectSaturate", false);
		element.getElementMaterial().setTexture("EffectMap", null);
		element.getElementMaterial().setFloat("EffectStep", 0.0f);
	}
	
	@Override
	public Effect clone() {
		Effect effect = new Effect(
			this.type,
			this.event,
			this.duration
		);
		effect.setElement(this.element);
		effect.setEffectDirection(this.effectDir);
		effect.setDestroyOnHide(this.destroyOnHide);
		effect.setInterpolation(interpolation);
		return effect;
	}
	
	// Effect methods
	private void initSlides() {
		def.set(element.getPosition().clone());
		if (effectDir == EffectDirection.Bottom) {
			diff.set(0,element.getAbsoluteHeight());
		} else if (effectDir == EffectDirection.Top) {
			diff.set(0,element.getScreen().getHeight()-element.getAbsoluteY());
		} else if (effectDir == EffectDirection.Left) {
			diff.set(element.getAbsoluteWidth(),0);
		} else if (effectDir == EffectDirection.Right) {
			diff.set(element.getScreen().getWidth()-element.getAbsoluteX(),0);
		}
	}
	
	private void updateSlideIn() {
		if (!init) {
			initSlides();
			element.show();
			init = true;
		}
		Vector2f inc = new Vector2f(diff.x*pass,diff.y*pass);
		if (effectDir == EffectDirection.Bottom || effectDir == EffectDirection.Left) {
			element.setPosition(def.subtract(diff.subtract(inc)));
		} else if (effectDir == EffectDirection.Top || effectDir == EffectDirection.Right) {
			element.setPosition(def.add(diff.subtract(inc)));
		}
		if (pass >= 1.0) {
			element.setPosition(def);
			element.setLocalScale(pass);
			isActive = false;
		}
	}
	
	private void updateSlideOut() {
		if (!init) {
			initSlides();
			init = true;
		}
		Vector2f inc = new Vector2f(diff.x*(1-pass),diff.y*(1-pass));
		if (effectDir == EffectDirection.Bottom || effectDir == EffectDirection.Left) {
			element.setPosition(def.subtract(diff.subtract(inc)));
		} else if (effectDir == EffectDirection.Top || effectDir == EffectDirection.Right) {
			element.setPosition(def.add(diff.subtract(inc)));
		}
		if (pass >= 1.0) {
			if (!destroyOnHide) {
				if (callHide) element.hide();
				element.setPosition(def);
			} else {
				destoryElement();
			}
			isActive = false;
		}
	}
	
	private void updateSlideTo() {
		if (!init) {
			def.set(element.getPosition().clone());
			diff.set(element.getX()-destination.getX(),element.getY()-destination.getY());
			init = true;
		}
		
		Vector2f inc = new Vector2f(diff.x*pass,diff.y*pass);
		float nextX = def.x, nextY = def.y;
		if (diff.x < 0)			nextX = def.x-inc.x;
		else if (diff.x > 0)	nextX = def.x-inc.x;
		if (diff.y < 0)			nextY = def.y-inc.y;
		else if (diff.y > 0)	nextY = def.y-inc.y;
		element.setPosition(nextX,nextY);
		
		if (pass >= 1.0) {
			element.setPosition(destination);
			isActive = false;
		}
	}
	
	private void initPositions() {
		def.set(element.getPosition().clone());
		diff.set(element.getWidth()/2,element.getHeight()/2);
	}
	
	private void updateZoomIn() {
		if (!init) {
			initPositions();
			Vector2f inc = new Vector2f(diff.x*pass,diff.y*pass);
			element.setPosition(def.add(diff.subtract(inc)));
			element.setLocalScale(pass);
			element.show();
			init = true;
		} else if (localActive) {
			Vector2f inc = new Vector2f(diff.x*pass,diff.y*pass);
			element.setPosition(def.add(diff.subtract(inc)));
			element.setLocalScale(pass);
		}
		if (pass >= 1.0) {
			element.setPosition(def);
			element.setLocalScale(pass);
			isActive = false;
		}
	}
	
	private void updateZoomOut() {
		if (!init) {
			initPositions();
			Vector2f inc = new Vector2f(diff.x*pass,diff.y*pass);
			element.setPosition(def.add(inc));
			element.setLocalScale(1-pass);
			init = true;
		} else if (localActive) {
			Vector2f inc = new Vector2f(diff.x*pass,diff.y*pass);
			element.setPosition(def.add(inc));
			element.setLocalScale(1-pass);
		}
		if (pass >= 1.0) {
			if (!destroyOnHide) {
				if (callHide) element.hide();
				element.setPosition(def);
				element.setLocalScale(0);
			} else {
				destoryElement();
			}
			isActive = false;
		}
	}
	
	private void disableShaderEffect() {
		element.getElementMaterial().setBoolean("UseEffect", false);
		element.getElementMaterial().setBoolean("EffectFade", false);
		element.getElementMaterial().setBoolean("EffectPulse", false);
		element.getElementMaterial().setBoolean("EffectPulseColor", false);
		element.getElementMaterial().setBoolean("EffectSaturate", false);
		element.getElementMaterial().setBoolean("EffectImageSwap", false);
	//	element.getElementMaterial().setBoolean("UseEffectTexCoords", false);
	}
	
	private void initFades() {
		element.getElementMaterial().setBoolean("UseEffect", true);
		element.getElementMaterial().setBoolean("EffectFade", true);
		element.getElementMaterial().setBoolean("EffectPulse", false);
		element.getElementMaterial().setBoolean("EffectSaturate", false);
	}
	
	private void updateFadeIn() {
		if (!init) {
			initFades();
			element.show();
			init = true;
		}
		if (pass >= 1.0) {
			disableShaderEffect();
			if (element.getTextElement() != null) {
				element.getTextElement().setAlpha(1f);
			}
			if (element instanceof TextElement)
				((TextElement)element).setAlpha(1f);
			isActive = false;
		} else {
			float val = pass;
			if (val <= 0.0f)
				val = 0.01f;
			element.getElementMaterial().setFloat("EffectStep", val);
			if (element.getTextElement() != null) {
				element.getTextElement().setAlpha(val);
			}
			if (element instanceof TextElement)
				((TextElement)element).setAlpha(val);
		}
	}
	
	private void updateFadeOut() {
		if (!init) {
			initFades();
			init = true;
		}
		if (pass >= 1.0) {
			if (!destroyOnHide) {
				if (callHide) element.hide();
				disableShaderEffect();
				element.getElementMaterial().setFloat("EffectStep", 0.01f);
				if (element.getTextElement() != null) {
					element.getTextElement().setAlpha(0.01f);
				}
				if (element instanceof TextElement)
					((TextElement)element).setAlpha(0.01f);
				isActive = false;
			} else {
				destoryElement();
				isActive = false;
			}
		} else {
			element.getElementMaterial().setFloat("EffectStep", 1.0f-pass);
			if (element.getTextElement() != null) {
				element.getTextElement().setAlpha(1.0f-pass);
			}
			if (element instanceof TextElement)
				((TextElement)element).setAlpha(1.0f-pass);
		}
	}
	
	private void updateSpinIn() {
		if (!init) {
			initPositions();
			Vector2f inc = new Vector2f(diff.x*pass,diff.y*pass);
			element.setPosition(def.add(diff.subtract(inc)));
			element.setLocalScale(pass);
			element.show();
			init = true;
		} else if (localActive) {
			Vector2f inc = new Vector2f(diff.x*pass,diff.y*pass);
			element.setPosition(def.add(diff.subtract(inc)));
			element.setLocalScale(pass);
			element.setLocalRotation(element.getLocalRotation().fromAngles(0, 0, 360*FastMath.DEG_TO_RAD*pass));
		}
		if (pass >= 1.0) {
			element.setPosition(def);
			element.setLocalScale(pass);
			element.setLocalRotation(element.getLocalRotation().fromAngles(0, 0, 0));
			isActive = false;
		}
	}
	
	private void updateSpinOut() {
		if (!init) {
			initPositions();
			Vector2f inc = new Vector2f(diff.x*pass,diff.y*pass);
			element.setPosition(def.subtract(inc));
			element.setLocalScale(1-pass);
			init = true;
		} else if (localActive) {
			Vector2f inc = new Vector2f(diff.x*pass,diff.y*pass);
			element.setPosition(def.subtract(inc));
			element.setLocalScale(1-pass);
			element.setLocalRotation(element.getLocalRotation().fromAngles(0, 0, 360*FastMath.DEG_TO_RAD*(1.0f-pass)));
		}
		if (pass >= 1.0) {
			if (!destroyOnHide) {
				if (callHide) element.hide();
				element.setPosition(def);
				element.setLocalScale(1);
				element.setLocalRotation(element.getLocalRotation().fromAngles(0, 0, 0));
			} else {
				destoryElement();
			}
			isActive = false;
		}
	}
	
	private void updateImageSwap() {
		if (!init) {
			disableShaderEffect();
			element.getElementMaterial().setTexture("ColorMap", blendImage);
			element.getElementMaterial().setFloat("EffectStep", 1.0f);
		//	element.getElementMaterial().setBoolean("UseEffectTexCoords", true);
			element.getElementMaterial().setBoolean("EffectImageSwap", true);
			if ((element.getScreen().getUseTextureAtlas() || element.getUseLocalAtlas()) && !element.getUseLocalTexture())
				element.getElementMaterial().setVector2("OffsetTexCoord", blendImageOffset);
			init = true;
			isActive = false;
		}
	}
	
	private void updateImageFadeIn() {
		if (!init) {
			element.getElementMaterial().setBoolean("UseEffect", true);
			element.getElementMaterial().setBoolean("EffectFade", false);
			element.getElementMaterial().setBoolean("EffectPulse", true);
			element.getElementMaterial().setBoolean("EffectPulseColor", false);
			element.getElementMaterial().setBoolean("EffectImageSwap", false);
			element.getElementMaterial().setTexture("EffectMap", blendImage);
			if ((element.getScreen().getUseTextureAtlas() || element.getUseLocalAtlas()) && !element.getUseLocalTexture())
				element.getElementMaterial().setVector2("OffsetTexCoord", blendImageOffset);
			init = true;
		}
		element.getElementMaterial().setFloat("EffectStep", pass);
	}
	
	private void updateImageFadeOut() {
		if (!init) {
			element.getElementMaterial().setBoolean("UseEffect", true);
			element.getElementMaterial().setBoolean("EffectFade", false);
			element.getElementMaterial().setBoolean("EffectPulse", true);
			element.getElementMaterial().setBoolean("EffectPulseColor", false);
			element.getElementMaterial().setBoolean("EffectImageSwap", false);
			element.getElementMaterial().setTexture("EffectMap", blendImage);
			if ((element.getScreen().getUseTextureAtlas() || element.getUseLocalAtlas()) && !element.getUseLocalTexture())
				element.getElementMaterial().setVector2("OffsetTexCoord", blendImageOffset);
			init = true;
		}
		element.getElementMaterial().setFloat("EffectStep", 1.0f-pass);
	}
	
	private void updateColorSwap() {
		if (!init) {
			disableShaderEffect();
			element.getElementMaterial().setColor("Color", blendColor);
			element.getElementMaterial().setFloat("EffectStep", 1.0f);
			init = true;
			isActive = false;
		}
	}
	
    private void updateDesaturate() {
        if (!init) {
            element.getElementMaterial().setBoolean("UseEffect", true);
            element.getElementMaterial().setBoolean("EffectFade", false);
            element.getElementMaterial().setBoolean("EffectPulse", false);
            element.getElementMaterial().setBoolean("EffectSaturate", true);
            init = true;
        }
        element.getElementMaterial().setFloat("EffectStep", pass);
        if (pass >= 1.0) {
            isActive = false;
        }
        
    }

    private void updateSaturate() {
        if (!init) {
            element.getElementMaterial().setBoolean("UseEffect", true);
            element.getElementMaterial().setBoolean("EffectFade", false);
            element.getElementMaterial().setBoolean("EffectPulse", false);
            element.getElementMaterial().setBoolean("EffectSaturate", true);
            init = true;
        }
        if (pass >= 1.0) {
            disableShaderEffect();
            isActive = false;
        } else {
            element.getElementMaterial().setFloat("EffectStep", Math.max(0,1-pass));
        }
    }
	
	@Override
	public String toString() {
	    return "Event @"+element.getName() + " " + event + " " + type; 
	}
}

import com.jme3.app.state.AbstractAppState;
import com.jme3.asset.AssetKey;
import com.jme3.asset.AssetNotFoundException;
import com.jme3.font.BitmapFont;
import com.jme3.font.LineWrapMode;
import com.jme3.input.event.MouseButtonEvent;
import com.jme3.input.event.MouseMotionEvent;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.math.Vector4f;
import com.jme3.texture.Texture;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.StringTokenizer;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import tonegod.gui.controls.buttons.Button;
import tonegod.gui.controls.buttons.ButtonAdapter;
import tonegod.gui.controls.buttons.CheckBox;
import tonegod.gui.controls.buttons.RadioButton;
import tonegod.gui.controls.extras.ChatBox;
import tonegod.gui.controls.extras.ChatBoxExt;
import tonegod.gui.controls.extras.ColorWheel;
import tonegod.gui.controls.extras.Indicator;
import tonegod.gui.controls.extras.OSRViewPort;
import tonegod.gui.controls.lists.ComboBox;
import tonegod.gui.controls.lists.Dial;
import tonegod.gui.controls.lists.SelectBox;
import tonegod.gui.controls.lists.SelectList;
import tonegod.gui.controls.lists.SlideTray;
import tonegod.gui.controls.lists.Slider;
import tonegod.gui.controls.lists.Spinner;
import tonegod.gui.controls.menuing.Menu;
import tonegod.gui.controls.scrolling.ScrollArea;
import tonegod.gui.controls.scrolling.ScrollAreaAdapter;
import tonegod.gui.controls.text.Label;
import tonegod.gui.controls.text.Password;
import tonegod.gui.controls.text.TextField;
import tonegod.gui.controls.windows.AlertBox;
import tonegod.gui.controls.windows.DialogBox;
import tonegod.gui.controls.windows.LoginBox;
import tonegod.gui.controls.windows.Panel;
import tonegod.gui.controls.windows.TabControl;
import tonegod.gui.controls.windows.Window;
import tonegod.gui.core.Element;
import tonegod.gui.core.Element.Orientation;
import tonegod.gui.core.Screen;
import tonegod.gui.core.utils.UIDUtil;
import tonegod.gui.core.utils.XMLHelper;
import tonegod.gui.effects.Effect;

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package tonegod.gui.style;


/**
 *
 * @author t0neg0d
 */
public class LayoutParser {
        Screen screen;
        AbstractAppState state;
        
        List<Class> controls = new ArrayList();
        
        public LayoutParser(Screen screen) {
                this.screen = screen;
                
                
                controls.add(OSRViewPort.class);
                controls.add(Indicator.class);
                controls.add(Slider.class);
                controls.add(Dial.class);
                controls.add(CheckBox.class);
                controls.add(RadioButton.class);
                controls.add(Button.class);
                controls.add(ChatBox.class);
                controls.add(ChatBoxExt.class);
                controls.add(Panel.class);
                controls.add(ColorWheel.class);
                controls.add(LoginBox.class);
                controls.add(AlertBox.class);
                controls.add(DialogBox.class);
                controls.add(Window.class);
                controls.add(TabControl.class);
                controls.add(SelectBox.class);
                controls.add(ComboBox.class);
                controls.add(Menu.class);
                controls.add(SelectList.class);
                controls.add(ScrollArea.class);
                controls.add(SlideTray.class);

                controls.add(Spinner.class);
