/* * GUIButton.cs * Authors: Bradley Blankenship, Mike DeMauro * Copyright (c) 2007-2008 Cornell University This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ using Microsoft.Xna.Framework.Graphics; using Util; using System.Collections.Generic; using Microsoft.Xna.Framework; using System; namespace tAC_Engine.GUI { /// /// Represents a button for use by the GUI. /// public class GUIButton : GUIComponent { #region Button State /// /// The enumeration of the button's state /// public enum ButtonState { /// /// State for when a button is up /// UP, /// /// State for when a button is down /// DOWN, /// /// State for when a button has the mouse over it /// OVER } #endregion #region Fields /// /// The state of the button /// protected ButtonState mState; /// /// The name of the texture for button up state /// protected string mButtonUpName = @"Textures\BasicButtonUp"; /// /// The name of the texture for button down state /// protected string mButtonDownName = @"Textures\BasicButtonDown"; /// /// The name of the texture for mouseover state /// protected string mButtonOverName = @"Textures\BasicButtonDown"; /// /// The textures representing being up and down /// protected Texture2D mButtonUp; /// /// The textures representing being up and down /// protected Texture2D mButtonDown; /// /// The texture representing being over /// protected Texture2D mButtonOver; /// /// The current texture of the button /// protected Texture2D mCurrentTexture; /// /// The font for the button /// protected SpriteFont mSpriteFont; /// /// The text for the button /// protected string mText; /// /// The position for the text (calculated to be cenetered based on the text of the button) /// protected Vector2 mTextPosition = Vector2.Zero; /// /// A custom offset for the text (exposed externally) /// protected Vector2 mTextCustomOffset = Vector2.Zero; /// /// Whether the button has changed state or not /// protected bool mStateChanged = false; /// /// The text color /// protected Color mTextColor = Color.Black; /// /// Size of the source border in texels /// protected int mBorderSizeSource = 0; /// /// Size of the destination border in pixels /// protected int mBorderSizeDest = 0; /// /// Icon texture /// protected Texture2D mIcon; /// /// The position for the icon /// protected Rectangle mIconDestination = Rectangle.Empty; #endregion #region Texture Rectangles /// /// Destination rectangle for the center of the button /// protected Rectangle centerDest = Rectangle.Empty; /// /// Source rectangle for the center of the button /// protected Rectangle centerSource = Rectangle.Empty; /// /// Destination rectangle for the top left of the button /// protected Rectangle topLeftDest = Rectangle.Empty; /// /// Source rectangle for the top left of the button /// protected Rectangle topLeftSource = Rectangle.Empty; /// /// Destination rectangle for the top of the button /// protected Rectangle topDest = Rectangle.Empty; /// /// Source rectangle for the top of the button /// protected Rectangle topSource = Rectangle.Empty; /// /// Destination rectangle for the top right of the button /// protected Rectangle topRightDest = Rectangle.Empty; /// /// Source rectangle for the top right of the button /// protected Rectangle topRightSource = Rectangle.Empty; /// /// Destination rectangle for the right of the button /// protected Rectangle rightDest = Rectangle.Empty; /// /// Source rectangle for the right of the button /// protected Rectangle rightSource = Rectangle.Empty; /// /// Destination rectangle for the bottom right of the button /// protected Rectangle bottomRightDest = Rectangle.Empty; /// /// Source rectangle for the bottom right of the button /// protected Rectangle bottomRightSource = Rectangle.Empty; /// /// Destination rectangle for the bottom of the button /// protected Rectangle bottomDest = Rectangle.Empty; /// /// Source rectangle for the bottom of the button /// protected Rectangle bottomSource = Rectangle.Empty; /// /// Destination rectangle for the bottom left of the button /// protected Rectangle bottomLeftDest = Rectangle.Empty; /// /// Source rectangle for the bottom left of the button /// protected Rectangle bottomLeftSource = Rectangle.Empty; /// /// Destination rectangle for the left of the button /// protected Rectangle leftDest = Rectangle.Empty; /// /// Source rectangle for the left of the button /// protected Rectangle leftSource = Rectangle.Empty; #endregion #region Properties /// /// Allows access to the size of the component. /// public override Vector2 Size { get { return base.Size; } set { base.Size = value; updatePositions(); } } /// /// Allows access to the position of the component. /// public override Vector2 Position { get { return base.Position; } set { base.Position = value; updatePositions(); } } /// /// Specifies an offset for the text label /// public virtual Vector2 TextOffset { get { return mTextCustomOffset; } set { mTextCustomOffset = value; } } /// /// Allows access to the button state internally. /// public ButtonState State { get { return mState; } set { // Only if the state is new if (value != mState) { HasChanged = true; mState = value; if (mState == ButtonState.UP) { mCurrentTexture = mButtonUp; } else if(mState == ButtonState.DOWN) { mCurrentTexture = mButtonDown; } else if (mState == ButtonState.OVER) { mCurrentTexture = mButtonOver; } } } } /// /// The name of the texture to load for the button up state /// public string ButtonUpName { get { return mButtonUpName; } set { mButtonUpName = value; } } /// /// The name of the texture to load for the button down state /// public string ButtonDownName { get { return mButtonDownName; } set { mButtonDownName = value; } } /// /// The name of the texture to load for the button over state /// public string ButtonOverName { get { return mButtonOverName; } set { mButtonOverName = value; } } /// /// Allows access to the button's up texture internally. /// public Texture2D ButtonUp { get { return mButtonUp; } set { mButtonUp = value; if (State == ButtonState.UP) { // Set the current texture mCurrentTexture = mButtonUp; } } } /// /// Allows access to the button's down texture internally. /// public Texture2D ButtonDown { get { return mButtonDown; } set { mButtonDown = value; if (State == ButtonState.DOWN) { // Set the current texture mCurrentTexture = mButtonDown; } } } /// /// Allows access to the button's over texture internally. /// public Texture2D ButtonOver { get { return mButtonOver; } set { mButtonOver = value; if (State == ButtonState.OVER) { // Set the current texture mCurrentTexture = mButtonOver; } } } /// /// Allow saccess to the button's current display texture. /// protected Texture2D CurrentTexture { get { return mCurrentTexture; } set { mCurrentTexture = value; updatePositions(); } } /// /// Allows access to the font of the button internally. /// public SpriteFont Font { get { return mSpriteFont; } set { mSpriteFont = value; UpdateTextPosition(); } } /// /// Allows internal access for manipulating the button text. /// public string Text { get { return mText; } set { mText = value; UpdateTextPosition(); } } /// /// Allows access to the value to determine if that state's changed locally. /// protected bool HasChanged { get { return mStateChanged; } set { mStateChanged = value; } } /// /// Allows access to the text color. /// public Color TextColor { get { return mTextColor; } set { mTextColor = value; } } /// /// Size of the source border in texels /// public int BorderSizeSource { get { return mBorderSizeSource; } set { mBorderSizeSource = value; } } /// /// Size of the destination border in pixels /// public int BorderSizeDest { get { return mBorderSizeDest; } set { mBorderSizeDest = value; } } /// /// Name of the Icon texture to be loaded /// public Texture2D Icon { get { return mIcon; } set { mIcon = value; } } #endregion #region Initialization /// /// Initializes the component. Override this method to load any non-graphics /// resources and query for any required services. /// public override void Initialize() { // Set the button's state up State = ButtonState.UP; // Now re-initialize to store the base data Resize(); } #endregion #region Management /// /// Loads all the necessary informatiion for content dependent objects /// public override void LoadContent() { // If the parent screen is not null if (mParentScreen != null) { // If we do not have a texture for our up state if (mButtonUp == null) { // Load the basic texture for an up button mButtonUp = mParentScreen.Content.Load(mButtonUpName); } mCurrentTexture = mButtonUp; // If we do not have a texture for our down state if (mButtonDown == null) { // Load the basic texture for a down button mButtonDown = mParentScreen.Content.Load(mButtonDownName); } // If we do not have a texture for over state if (mButtonOver == null) { mButtonOver = mParentScreen.Content.Load(mButtonOverName); } updatePositions(); } base.LoadContent(); } #endregion #region Rescale /// /// Adjusts the Size of this component based on the size of the texture /// /// public void Rescale(float uniformScale) { Rescale(uniformScale, uniformScale); } /// /// Adjusts the Size of this component based on the size of the texture /// /// /// public void Rescale(float xScale, float yScale) { System.Diagnostics.Debug.Assert(mButtonUp != null, "Must have a texture before calling Rescale"); Size = new Vector2(mButtonUp.Width * xScale, mButtonUp.Height * yScale); } #endregion #region Event Handling /// /// Event for when a button is pressed /// public event EventHandler ButtonPressed; /// /// Event for when a button is released /// public event EventHandler ButtonReleased; /// /// Removes all handlers from the ButtonReleased event. /// public void RemoveButtonReleasedHandlers() { ButtonReleased = null; } /// /// Handle the given event. /// /// The event to handle. public override bool HandleMouseEvent(MouseInputEvent e) { // The return value bool retVal = false; // If we're visible if (Visible && Enabled) { // Check to see if it's within our bounds if (IsInside(e.Location)) { // Switch on the given codes switch (e.EventCode) { // Evaluate for a left mouse button press case (int)GUIEventCodes.LEFT_MOUSE_CLICKED: State = ButtonState.DOWN; // Invoke Event if we're enabled if (ButtonPressed != null) ButtonPressed.Invoke(this, EventArgs.Empty); break; // Evaluate for a left mouse button release case (int)GUIEventCodes.LEFT_MOUSE_RELEASED: State = ButtonState.UP; // Invoke Event if we're enabled if (ButtonReleased != null) ButtonReleased.Invoke(this, EventArgs.Empty); break; case (int)GUIEventCodes.MOUSE_OVER: State = ButtonState.OVER; break; case (int)GUIEventCodes.MOUSE_OUT: State = ButtonState.UP; break; } // Set the ret val to true retVal = true; // Set the event args if they're null if (e.EventArgs == null) { // Set the event args to us e.EventArgs = this; } } } // Return return retVal; } /// /// Handle the given key input event. /// /// The key input event to handle. public override bool HandleKeyEvent(KeyInputEvent e) { return false; } #endregion #region Update /// /// Update the button. /// /// The current game time. public override void Update(GameTime gameTime) {} /// /// Update the position of the text for the button. /// private void UpdateTextPosition() { // Check to make sure they're not null if (Font != null && Text != null) { // The position for the text Vector2 size = Font.MeasureString(Text); Vector2 centerOffset = new Vector2((Size.X - size.X) / 2, (Size.Y - size.Y) / 2); Vector2 sumOffset = centerOffset + mTextCustomOffset; float offsetLength = sumOffset.Length(); double offsetRotation = Math.Atan2(sumOffset.Y, sumOffset.X); Vector2 rotatedOffset = new Vector2((float)(Math.Cos(offsetRotation + mRotation) * offsetLength), (float)(Math.Sin(offsetRotation + mRotation) * offsetLength)); mTextPosition = Position + rotatedOffset; } } /// /// Updates teh button GUI /// protected void updatePositions() { // Update background rectangles if (mCurrentTexture != null) { centerDest = new Rectangle(mBounds.X + mBorderSizeDest, mBounds.Y + mBorderSizeDest, Bounds.Width - mBorderSizeDest * 2, Bounds.Height - mBorderSizeDest * 2); centerSource = new Rectangle(mBorderSizeSource, mBorderSizeSource, CurrentTexture.Width - mBorderSizeSource * 2, CurrentTexture.Height - mBorderSizeSource * 2); topLeftDest = new Rectangle(mBounds.X, mBounds.Y, mBorderSizeDest, mBorderSizeDest); topLeftSource = new Rectangle(0, 0, mBorderSizeSource, mBorderSizeSource); topDest = new Rectangle(mBounds.X + mBorderSizeDest, mBounds.Y, mBounds.Width - mBorderSizeDest * 2, mBorderSizeDest); topSource = new Rectangle(mBorderSizeSource, 0, CurrentTexture.Width - mBorderSizeSource * 2, mBorderSizeSource); topRightDest = new Rectangle(mBounds.X + mBounds.Width - mBorderSizeDest, mBounds.Y, mBorderSizeDest, mBorderSizeDest); topRightSource = new Rectangle(CurrentTexture.Width - mBorderSizeSource, 0, mBorderSizeSource, mBorderSizeSource); rightDest = new Rectangle(mBounds.X + mBounds.Width - mBorderSizeDest, mBounds.Y + mBorderSizeDest, mBorderSizeDest, mBounds.Height - mBorderSizeDest * 2); rightSource = new Rectangle(CurrentTexture.Width - mBorderSizeSource, mBorderSizeSource, mBorderSizeSource, CurrentTexture.Height - mBorderSizeSource * 2); bottomRightDest = new Rectangle(mBounds.X + mBounds.Width - mBorderSizeDest, mBounds.Y + mBounds.Height - mBorderSizeDest, mBorderSizeDest, mBorderSizeDest); bottomRightSource = new Rectangle(CurrentTexture.Width - mBorderSizeSource, CurrentTexture.Height - mBorderSizeSource, mBorderSizeSource, mBorderSizeSource); bottomDest = new Rectangle(mBounds.X + mBorderSizeDest, mBounds.Y + mBounds.Height - mBorderSizeDest, mBounds.Width - mBorderSizeDest * 2, mBorderSizeDest); bottomSource = new Rectangle(mBorderSizeSource, CurrentTexture.Height - mBorderSizeSource, CurrentTexture.Width - mBorderSizeSource * 2, mBorderSizeSource); bottomLeftDest = new Rectangle(mBounds.X, mBounds.Y + mBounds.Height - mBorderSizeDest, mBorderSizeDest, mBorderSizeDest); bottomLeftSource = new Rectangle(0, CurrentTexture.Height - mBorderSizeSource, mBorderSizeSource, mBorderSizeSource); leftDest = new Rectangle(mBounds.X, mBounds.Y + mBorderSizeDest, mBorderSizeDest, mBounds.Height - mBorderSizeDest * 2); leftSource = new Rectangle(0, mBorderSizeSource, mBorderSizeSource, CurrentTexture.Height - mBorderSizeSource * 2); } // Update icon and text if (mIcon == null) { // Update the text position UpdateTextPosition(); mTextPosition.X += mBorderSizeDest; mTextPosition.Y += mBorderSizeDest; } else { mIconDestination.X = mBorderSizeDest + (int)(mPosition.X - mScreenOffset.X); mIconDestination.Y = mBorderSizeDest + (int)(mPosition.Y - mScreenOffset.Y); mIconDestination.Height = (int)mSize.Y - mBorderSizeDest * 2; mIconDestination.Width = (int)(mIcon.Width * ((float)mIconDestination.Height / mIcon.Height)); mTextPosition.X = mIconDestination.X + mIcon.Width + mPosition.X; mTextPosition.Y = mBorderSizeDest + mPosition.Y; } } /// /// Reinitialize the button to being up. /// public override void Resize() { // Set the button's state up State = ButtonState.UP; // If the parent screen is not null if (ParentScreen != null) { // Set the current texture to the up one CurrentTexture = ButtonUp; } } #endregion #region Draw /// /// Draw the button. /// /// The current game time. public override void Draw(Microsoft.Xna.Framework.GameTime gameTime) { // Do this if we're visible if (Visible) { // If we have a current texture, draw it if (CurrentTexture != null) { if (mBorderSizeSource <= 0 && mBorderSizeDest <= 0) { // Draw the background texture Batch.Draw(CurrentTexture, Bounds, null, mAddColor, mRotation, Vector2.Zero, mSpriteEffects, mLayerDepth); } else { // TODO: Support rotation in bordered buttons // Draw with scaled borders Batch.Draw(CurrentTexture, topRightDest, topRightSource, mAddColor); Batch.Draw(CurrentTexture, centerDest, centerSource, mAddColor); Batch.Draw(CurrentTexture, topLeftDest, topLeftSource, mAddColor); Batch.Draw(CurrentTexture, topDest, topSource, mAddColor); Batch.Draw(CurrentTexture, topRightDest, topRightSource, mAddColor); Batch.Draw(CurrentTexture, rightDest, rightSource, mAddColor); Batch.Draw(CurrentTexture, bottomRightDest, bottomRightSource, mAddColor); Batch.Draw(CurrentTexture, bottomDest, bottomSource, mAddColor); Batch.Draw(CurrentTexture, bottomLeftDest, bottomLeftSource, mAddColor); Batch.Draw(CurrentTexture, leftDest, leftSource, mAddColor); } } // If we have text and a font if (Font != null && Text != null) { // Draw the text to the screen Batch.DrawString(Font, mText, mTextPosition - mScreenOffset, mTextColor, mRotation, Vector2.Zero, 1f, SpriteEffects.None, mLayerDepth); } if (mIcon != null) { Batch.Draw(mIcon, mIconDestination, null, mAddColor, mRotation, Vector2.Zero, mSpriteEffects, 0); } } } #endregion } }