/*
* 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
}
}