/* * GUIList.cs * Authors: Mike DeMauro, Bradley Blankenship * 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 System; using System.Collections.Generic; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using Util; namespace tAC_Engine.GUI { /// /// Displays a list of strings using GUILabels /// TODO: add support for objects /// public class GUIList : GUIContainer { #region Fields /// /// Pixel texture used for list GUI /// protected Texture2D mPixel; /// /// The sprite font /// protected SpriteFont mFont; /// /// The color for the text /// protected Color mTextColor = Color.Black; /// /// The color for the background /// protected Color mBackgroundColor = Color.White; /// /// The color to use when a label is selected /// protected Color mSelectedColor = Color.Yellow; /// /// Draw a border around the list /// protected bool mIsBorderVisible = true; /// /// Uses a scroll bar /// protected bool mIsScrollBarEnabled = true; /// /// The color for the border /// protected Color mBorderColor = Color.Black; /// /// The labels used to display the list /// protected List mLabels = new List(); /// /// The index of the selected label in the list, -1 if no label is selected /// protected int mSelectedIndex = -1; /// /// The selected label in the list, null if no label is selected /// protected GUILabel mSelectedLabel; /// /// Height of each row /// TODO: make variable /// protected const int mRowHeight = 24; /// /// GUI scroll bar component /// protected GUIScrollBar mScrollBar; /// /// The current viewport to draw to /// protected Viewport mViewport; /// /// Reference to the game's viewport /// protected Viewport mGameViewport; #endregion #region Properties /// /// Allows access to the basic font values. /// public SpriteFont Font { get { return mFont; } set { mFont = value; foreach (GUILabel label in mLabels) { label.Font = value; } } } /// /// The color of the background. /// public Color BackgroundColor { get { return mBackgroundColor; } set { mBackgroundColor = value; foreach (GUILabel l in mLabels) l.BackgroundColor = value; } } /// /// Allows access to the color of the text. /// public Color TextColor { get { return mTextColor; } set { mTextColor = value; foreach (GUILabel l in mLabels) l.TextColor = value; } } /// /// Allows access to the selection color. /// public Color SelectedColor { get { return mSelectedColor; } set { mSelectedColor = value; if (mSelectedLabel != null) mSelectedLabel.SelectedColor = value; } } /// /// The index of the selected label in the list, -1 if no label is selected /// public int SelectedIndex { get { return mSelectedIndex; } set { if (value >= 0 && value < mLabels.Count) { if (mSelectedLabel != null) mSelectedLabel.Selected = false; mSelectedIndex = value; mSelectedLabel = mLabels[mSelectedIndex]; mSelectedLabel.Selected = true; if (SelectionChanged != null) SelectionChanged(this, EventArgs.Empty); } else { if (mSelectedLabel != null) mSelectedLabel.Selected = false; mSelectedIndex = -1; mSelectedLabel = null; if (SelectionChanged != null) SelectionChanged(this, EventArgs.Empty); } } } /// /// The selected label in the list, null if no label is selected /// public GUILabel SelectedLabel { get { return mSelectedLabel; } } /// /// The items in the list /// public IEnumerable Items { get { foreach (GUILabel l in mLabels) yield return l.Text; } } /// /// Sets/Gets the visibility of this control (scrollbar visibility is preserved) /// public override bool Visible { get { return base.Visible; } set { base.Visible = value; mScrollBar.Visible = mIsScrollBarEnabled; } } /// /// Use a scrollbar /// public bool IsScrollbarEnabled { get { return mIsScrollBarEnabled; } set { mIsScrollBarEnabled = value; mScrollBar.Visible = value; updateScrollBar(); } } public override Vector2 Position { get { return base.Position; } set { base.Position = value; SetScrollBarPosition(); updateLabels(); } } public override Vector2 ScreenOffset { get { return base.ScreenOffset; } set { base.ScreenOffset = value; SetScrollBarPosition(); updateLabels(); } } #endregion #region Creation /// /// Constructor /// public GUIList() { mScrollBar = new GUIScrollBar(); //mScrollBar.LayerDepth = this.LayerDepth + 0.001f; Add(mScrollBar); } #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() { SetScrollBarPosition(); mScrollBar.ScrollChanged += mScrollBar_ScrollChanged; base.Initialize(); } /// /// Set the position of the scrollbar /// protected virtual void SetScrollBarPosition() { if (mScrollBar != null) { //NOTE: hardcoded width mScrollBar.Size = new Vector2(24, mSize.Y); mScrollBar.Position = new Vector2(mPosition.X + mSize.X - 24, mPosition.Y); mScrollBar.ScreenOffset = mScreenOffset; //mScrollBar.LayerDepth = this.LayerDepth + 0.001f; } } #endregion #region Management /// /// Loads the necessary information for all content dependent objects /// public override void LoadContent() { // If we have a parent container if (mParentScreen != null) { // Get the basic font if we do not have one if (Font == null) { // Load the standard font mFont = mParentScreen.Content.Load(@"Fonts\Courier New"); } // create pixel mPixel = new Texture2D(mParentScreen.GraphicsDevice, 1, 1, 1, TextureUsage.None, SurfaceFormat.Color); Color[] pixels = new Color[1]; pixels[0] = Color.White; mPixel.SetData(pixels); mGameViewport = mParentScreen.GraphicsDevice.Viewport; mViewport = new Viewport(); mViewport.Width = mBounds.Width; mViewport.Height = mBounds.Height; mViewport.X = mBounds.X; mViewport.Y = mBounds.Y; mViewport.MaxDepth = mGameViewport.MaxDepth; mViewport.MinDepth = mGameViewport.MinDepth; } base.LoadContent(); } #endregion #region Event Handling /// /// The event that is thrown when an item in the list is selected. /// public event EventHandler SelectionChanged; /// /// Processes when the scroll bar has changed /// /// The sender of the event /// The associated event arguments with the event public void mScrollBar_ScrollChanged(object sender, EventArgs e) { updateLabels(); } /// /// Handles mouse events /// /// The key event that has just been received /// Whether or not the event had to be processed public override bool HandleMouseEvent(MouseInputEvent e) { if(mScrollBar.HandleMouseEvent(e)) return true; bool retVal = base.HandleMouseEvent(e); // If we're visible if (retVal && Enabled) { if (e.EventCode == (int)GUIEventCodes.LEFT_MOUSE_RELEASED) { mSelectedIndex = -1; mSelectedLabel = null; for (int i = 0; i < mLabels.Count; i++) { if (mLabels[i].Bounds.Contains(e.X, e.Y)) { mSelectedIndex = i; mSelectedLabel = mLabels[i]; mSelectedLabel.Selected = true; mSelectedLabel.SelectedColor = mSelectedColor; if (SelectionChanged != null) SelectionChanged(this, EventArgs.Empty); } else { mLabels[i].Selected = false; } } } } return retVal; } /// /// Handles key events /// /// The key event that has just been received /// Whether or not the event had to be processed public override bool HandleKeyEvent(KeyInputEvent e) { if (mSelectedIndex != -1) { if (e.Trigger == Trigger.Activated) { // Move selection up or down if necessary if (e.Key == Keys.Up && mSelectedIndex > 0) { mSelectedLabel.Selected = false; --mSelectedIndex; mSelectedLabel = mLabels[mSelectedIndex]; mSelectedLabel.Selected = true; if (SelectionChanged != null) SelectionChanged(this, EventArgs.Empty); } else if (e.Key == Keys.Down && mSelectedIndex < mLabels.Count - 1) { mSelectedLabel.Selected = false; ++mSelectedIndex; mSelectedLabel = mLabels[mSelectedIndex]; mSelectedLabel.Selected = true; if (SelectionChanged != null) SelectionChanged(this, EventArgs.Empty); } // Move viewable rows if necessary int numVisibleRows = (int)Size.Y / mRowHeight; if ((mSelectedIndex - mScrollBar.ScrollPosition) < 0) { --mScrollBar.ScrollPosition; updateLabels(); } else if ((mSelectedIndex - mScrollBar.ScrollPosition) >= numVisibleRows) { ++mScrollBar.ScrollPosition; updateLabels(); } } } return base.HandleKeyEvent(e); } #endregion #region Mutators /// /// Updates visibility and positions of labels /// protected void updateLabels() { int numVisibleRows = (int)Size.Y / mRowHeight; if ((int)Size.Y % mRowHeight > 0) { ++numVisibleRows; } for (int i = 0; i < mLabels.Count; i++) { if ((i - mScrollBar.ScrollPosition) < 0 || (i - mScrollBar.ScrollPosition) >= numVisibleRows) { mLabels[i].Visible = false; } else { mLabels[i].Visible = true; } mLabels[i].Position = new Vector2(Position.X + 1, Position.Y + (i - mScrollBar.ScrollPosition) * mRowHeight); mLabels[i].ScreenOffset = mScreenOffset; //mLabels[i].LayerDepth = mLayerDepth + 0.001f; } } /// /// Updates visibility and scroll positions of the scroll bar /// protected void updateScrollBar() { if (!mIsScrollBarEnabled) { mScrollBar.Visible = false; mScrollBar.ScrollPosition = 0; } else { int numVisibleRows = (int)Size.Y / mRowHeight; if (mLabels.Count > numVisibleRows) { mScrollBar.Visible = true; } if (mLabels.Count > numVisibleRows) { mScrollBar.MaxScrollPosition = mLabels.Count - numVisibleRows; } else { mScrollBar.MaxScrollPosition = 0; } //mScrollBar.LayerDepth = mLayerDepth + 0.001f; } } /// /// Adds a label to the list /// /// The string to add public new void Add(GUILabel label) { mLabels.Add(label); label.Position = new Vector2(1, mLabels.Count * mRowHeight); label.Size = new Vector2(Size.X - 2, mRowHeight); base.Add(label); updateLabels(); updateScrollBar(); } /// /// Adds a string to the list /// /// The string to add public void Add(string s) { GUILabel label = new GUILabel(); label.Text = s; label.Font = mFont; label.Position = new Vector2(1, mLabels.Count * mRowHeight); label.Size = new Vector2(Size.X - 2, mRowHeight); label.BackgroundColor = mBackgroundColor; label.TextColor = mTextColor; Add(label); } /// /// Adds a list of strings to the list /// /// The list of strings to add public void Add(IEnumerable strings) { foreach (string s in strings) { Add(s); } } /// /// Override to remove the game components. /// /// Remove the given game component. /// Whether the component was successfully removed or not. public override bool Remove(GUIComponent component) { // The return value bool retVal = base.Remove(component); // If it's a label if (component is GUILabel) { // Remove from our label list mLabels.Remove(component as GUILabel); // Unselected the label if it's the one if (mSelectedLabel == component) mSelectedLabel = null; // Now redo the layout updateLabels(); } // Return return retVal; } /// /// Remove the game component corresponding the given string /// /// /// public bool Remove(string value) { GUILabel labelToRemove = null; foreach(GUILabel label in mLabels) { if (label.Text == value) labelToRemove = label; } if (labelToRemove != null) return Remove(labelToRemove); return false; } #endregion #region Draw /// /// Renders the GUI list /// /// Time that has passed in game public override void Draw(GameTime gameTime) { // If we're visible if (Visible) { // Draw Background Batch.Draw(mPixel, Bounds, null, mBackgroundColor, 0f, Vector2.Zero, SpriteEffects.None, 0f); // Draw Labels // NOTE: this is not ideal //Batch.End(); //ParentScreen.GraphicsDevice.Viewport = mViewport; //Batch.Begin(); //foreach (GUILabel label in mLabels) //{ // label.Draw(gameTime); //} ////Batch.End(); ////ParentScreen.GraphicsDevice.Viewport = mGameViewport; ////Batch.Begin(); //// Draw scrollbar //mScrollBar.Draw(gameTime); base.Draw(gameTime); if (mIsBorderVisible) { // Draw (inset) borders // -top Batch.Draw(mPixel, Position, null, mBorderColor, 0f, Vector2.Zero, new Vector2(Size.X, 1f), SpriteEffects.None, 0f); // -right Batch.Draw(mPixel, new Vector2(Position.X + Size.X - 1, Position.Y), null, mBorderColor, 0f, Vector2.Zero, new Vector2(1f, Size.Y), SpriteEffects.None, 0f); // -bottom Batch.Draw(mPixel, new Vector2(Position.X, Position.Y + Size.Y - 1), null, mBorderColor, 0f, Vector2.Zero, new Vector2(Size.X, 1f), SpriteEffects.None, 0f); // -left Batch.Draw(mPixel, Position, null, mBorderColor, 0f, Vector2.Zero, new Vector2(1f, Size.Y), SpriteEffects.None, 0f); } //NOTE: don't call base.Draw() since this method draws each component from mComponents } } #endregion } }