Creating a GUI (Part 3)

  CU_MDX_GUI.zip (63.8 KiB, 7,603 hits)


  CUnitFramework.zip (101.1 KiB, 20,443 hits)

Extract the C-Unit Framework
inside the project (not solution) directory.

In Part 3 of the GUI series, we will create_ the following more complex controls: ComboBoxes, ListBoxes, and EditBoxes. ListBoxes and Comboxes both hold lists of items that the user can select from. This item will be defined as follows:

/// ListableItem for Controls that list items.
public class ListableItem
{
private string m_text;
private object m_data;
private bool m_selected;
private ColorValue m_textColor;

///

Creates a new ListableItem.
/// Text to display
/// Data for item to hold
public ListableItem( string text, object data ) Toggle
{
m_text = text;
m_data = data;

}

///

Gets the text of the ListableItem
public string Text Toggle
{
get { return m_text; }

}

///

Gets and sets whether the ListableItem is selected.
public bool Selected Toggle
{
get { return m_selected; }
set { m_selected = value; }

}

///

Gets and sets the text color of the ListableItem
public ColorValue TextColor Toggle
{
get { return m_textColor; }
set { m_textColor = value; }

}

///

Gets the data of the ListableItem
public object Data Toggle
{
get { return m_data; }

}

}

The ListableItem class represents a single item stored in a ComboBox or ListBox. Both of these Controls will have a List of ListableItems that the user will be able to choose from. With ListableItem now out of the way, we can move on to create_ a ListBox.

///
/// A ListBox control
///

public class ListBox : Control
{
protected List m_scrollUp;
protected List m_scrollDown;
protected List m_scrollMarker;
protected List m_disabledQuads;
protected List m_highlightQuads;
protected List m_normalHighlights;
protected List m_disabledHighlights;
protected List m_items;
protected List m_hotspots;
protected int m_topItem;
protected float m_itemHeight;
protected float m_minMarkerHeight;
protected int m_numDisplayedItems;
protected int m_highlightColor;
protected int m_disabledHighlightColor;
protected bool m_scrolling;
protected bool m_overState;
protected bool m_singleItemListBox;
protected bool m_newData;

protected enum Section { Left, Right, Top, Bottom, Background, TopLeft, TopRight, BottomLeft, BottomRight, ScrollUp, ScrollDown, ScrollBody, ScrollMarker };
protected enum HotSpot { Background, ScrollUp, ScrollDown, ScrollMarker, ScrollBody };
protected enum Highlight { Normal, Disabled };

///

Default Constructor
public ListBox() Toggle
{
// Empty

}

///

Creates a ListBox
/// Control ID
/// Whether single or multiple items can be selected
/// Screen rectangle
/// Font size
/// Text color
/// Bitmap Font
/// ControlNode from XML file
/// Texture ImageInformation
public ListBox( int id, bool singleItemSelect, RectangleF screenRect, float fontSize, ColorValue textColor, BitmapFont font, ControlNode node, ImageInformation info ) Toggle
{
m_topItem = 0;
m_id = id;
m_minMarkerHeight = 5f;
m_fontSize = fontSize;
m_itemHeight = 0f;
m_textColor = textColor;
m_bFont = font;
m_scrolling = false;
m_overState = false;
m_newData = false;
m_singleItemListBox = singleItemSelect;
m_position = new PointF( screenRect.X, screenRect.Y );
m_size = new SizeF( screenRect.Width, screenRect.Height );
m_data = new ArrayList();

int index = m_bFont.AddString( “x”, new RectangleF( 0f, 0f, 0f, 0f ), BitmapFont.Align.Left, m_fontSize, m_textColor, true );
m_itemHeight = m_bFont.GetLineHeight( index );
m_bFont.ClearString( index );

m_scrollUp = new List();
m_scrollDown = new List();
m_scrollMarker = new List();
m_quads = new List();
m_disabledQuads = new List();
m_normalHighlights = new List();
m_disabledHighlights = new List();
m_highlightQuads = new List();
m_items = new List();
m_hotspots = new List();

// Initialize Lists so we can access them with indices
m_highlightQuads.Add( new Quad() );
m_highlightQuads.Add( new Quad() );
for ( int i = 0; i < 5; i++ )
{

m_hotspots.Add( new Rectangle() );

}
for ( int i = 0; i < 4; i++ )
{

m_scrollUp.Add( new Quad() );
m_scrollDown.Add( new Quad() );
m_scrollMarker.Add( new Quad() );

}
for ( int i = 0; i < 13; i++ )
{

m_quads.Add( new Quad() );
m_disabledQuads.Add( new Quad() );

}

float z = 0f;
float rhw = 1f;
foreach ( ImageNode i in node.Images )
{

RectangleF rect = i.Rectangle;

TransformedColoredTextured topLeft = new TransformedColoredTextured(
screenRect.X, screenRect.Y, z, rhw,
i.Color, rect.X / (float)info.Width, rect.Y / (float)info.Height );
TransformedColoredTextured topRight = new TransformedColoredTextured(
screenRect.X + rect.Width, screenRect.Y, z, rhw,
i.Color, rect.Right / (float)info.Width, rect.Y / (float)info.Height );
TransformedColoredTextured bottomRight = new TransformedColoredTextured(
screenRect.X + rect.Width, screenRect.Y + rect.Height, z, rhw,
i.Color, rect.Right / (float)info.Width, rect.Bottom / (float)info.Height );
TransformedColoredTextured bottomLeft = new TransformedColoredTextured(
screenRect.X, screenRect.Y + rect.Height, z, rhw,
i.Color, rect.X / (float)info.Width, rect.Bottom / (float)info.Height );
Quad q = new Quad( topLeft, topRight, bottomLeft, bottomRight );
if ( i.Name.EndsWith( “TopLeftCorner” ) )
{

if ( i.Name.StartsWith( “Normal” ) )
{
m_quads[(int)Section.TopLeft] = q;

}
else if ( i.Name.StartsWith( “Disabled” ) )
{

m_disabledQuads[(int)Section.TopLeft] = q;

}

}
else if ( i.Name.EndsWith( “TopBorder” ) )
{

if ( i.Name.StartsWith( “Normal” ) )
{
m_quads[(int)Section.Top] = q;

}
else if ( i.Name.StartsWith( “Disabled” ) )
{

m_disabledQuads[(int)Section.Top] = q;

}

}
else if ( i.Name.EndsWith( “TopRightCorner” ) )
{

if ( i.Name.StartsWith( “Normal” ) )
{
m_quads[(int)Section.TopRight] = q;

}
else if ( i.Name.StartsWith( “Disabled” ) )
{

m_disabledQuads[(int)Section.TopRight] = q;

}

}
else if ( i.Name.EndsWith( “LeftBorder” ) )
{

if ( i.Name.StartsWith( “Normal” ) )
{
m_quads[(int)Section.Left] = q;

}
else if ( i.Name.StartsWith( “Disabled” ) )
{

m_disabledQuads[(int)Section.Left] = q;

}

}
else if ( i.Name.EndsWith( “Background” ) )
{

if ( i.Name.StartsWith( “Normal” ) )
{
m_quads[(int)Section.Background] = q;

}
else if ( i.Name.StartsWith( “Disabled” ) )
{

m_disabledQuads[(int)Section.Background] = q;

}

}
else if ( i.Name.EndsWith( “RightBorder” ) )
{

if ( i.Name.StartsWith( “Normal” ) )
{
m_quads[(int)Section.Right] = q;

}
else if ( i.Name.StartsWith( “Disabled” ) )
{

m_disabledQuads[(int)Section.Right] = q;

}

}
else if ( i.Name.EndsWith( “BottomLeftCorner” ) )
{

if ( i.Name.StartsWith( “Normal” ) )
{
m_quads[(int)Section.BottomLeft] = q;

}
else if ( i.Name.StartsWith( “Disabled” ) )
{

m_disabledQuads[(int)Section.BottomLeft] = q;

}

}
else if ( i.Name.EndsWith( “BottomBorder” ) )
{

if ( i.Name.StartsWith( “Normal” ) )
{
m_quads[(int)Section.Bottom] = q;

}
else if ( i.Name.StartsWith( “Disabled” ) )
{

m_disabledQuads[(int)Section.Bottom] = q;

}

}
else if ( i.Name.EndsWith( “BottomRightCorner” ) )
{

if ( i.Name.StartsWith( “Normal” ) )
{
m_quads[(int)Section.BottomRight] = q;

}
else if ( i.Name.StartsWith( “Disabled” ) )
{

m_disabledQuads[(int)Section.BottomRight] = q;

}

}
else if ( i.Name.EndsWith( “ScrollBody” ) )
{

if ( i.Name.StartsWith( “Normal” ) )
{
m_quads[(int)Section.ScrollBody] = q;

}
else if ( i.Name.StartsWith( “Disabled” ) )
{

m_disabledQuads[(int)Section.ScrollBody] = q;

}

}
else if ( i.Name.EndsWith( “Highlight” ) )
{

if ( i.Name.StartsWith( “Normal” ) )
{
q.Height = m_fontSize + 6f;
m_highlightQuads[(int)Highlight.Normal] = q;
m_highlightColor = i.Color.ToArgb();

}
else if ( i.Name.StartsWith( “Disabled” ) )
{

m_disabledHighlightColor = i.Color.ToArgb();
m_highlightQuads[(int)Highlight.Disabled] = q;

}

}
else if ( i.Name.EndsWith( “ScrollUp” ) )
{

if ( i.Name.StartsWith( “Normal” ) )
{
m_quads[(int)Section.ScrollUp] = q;
m_scrollUp[(int)ControlState.Normal] = q;

}
else if ( i.Name.StartsWith( “Over” ) )
{

m_scrollUp[(int)ControlState.Over] = q;

}
else if ( i.Name.StartsWith( “Down” ) )
{

m_scrollUp[(int)ControlState.Down] = q;

}
else if ( i.Name.StartsWith( “Disabled” ) )
{

m_scrollUp[(int)ControlState.Disabled] = q;
m_disabledQuads[(int)Section.ScrollUp] = q;

}

}
else if ( i.Name.EndsWith( “ScrollDown” ) )
{

if ( i.Name.StartsWith( “Normal” ) )
{
m_quads[(int)Section.ScrollDown] = q;
m_scrollDown[(int)ControlState.Normal] = q;

}
else if ( i.Name.StartsWith( “Over” ) )
{

m_scrollDown[(int)ControlState.Over] = q;

}
else if ( i.Name.StartsWith( “Down” ) )
{

m_scrollDown[(int)ControlState.Down] = q;

}
else if ( i.Name.StartsWith( “Disabled” ) )
{

m_scrollDown[(int)ControlState.Disabled] = q;
m_disabledQuads[(int)Section.ScrollDown] = q;

}

}
else if ( i.Name.EndsWith( “ScrollMarker” ) )
{

if ( i.Name.StartsWith( “Normal” ) )
{
m_quads[(int)Section.ScrollMarker] = q;
m_scrollMarker[(int)ControlState.Normal] = q;

}
else if ( i.Name.StartsWith( “Over” ) )
{

m_scrollMarker[(int)ControlState.Over] = q;

}
else if ( i.Name.StartsWith( “Down” ) )
{

m_scrollMarker[(int)ControlState.Down] = q;

}
else if ( i.Name.StartsWith( “Disabled” ) )
{

m_disabledQuads[(int)Section.ScrollMarker] = q;
m_scrollMarker[(int)ControlState.Disabled] = q;

}

}

}

PositionQuads();

}

///

Adds a new item to the ListBox.
/// Item text.
/// Item data.
public virtual void AddItem( string text, object data ) Toggle
{
m_items.Add( new ListableItem( text, data ) );
BuildText();

// Highlights are designated by item index
Quad normalHighlight = (Quad)m_highlightQuads[(int)Highlight.Normal].Clone();
normalHighlight.X = m_quads[(int)Section.Background].X;
normalHighlight.Width = m_quads[(int)Section.Background].Width;
normalHighlight.Color = 0;
m_normalHighlights.Add( normalHighlight );
m_quads.Add( normalHighlight );

Quad disabledHighlight = (Quad)m_highlightQuads[(int)Highlight.Disabled].Clone();
disabledHighlight.X = m_quads[(int)Section.Background].X;
disabledHighlight.Width = m_quads[(int)Section.Background].Width;
disabledHighlight.Color = 0;
m_disabledHighlights.Add( disabledHighlight );
m_disabledQuads.Add( disabledHighlight );

for ( int i = 0; i < 4; i++ )
{

if ( m_numDisplayedItems == m_items.Count )
{
m_scrollMarker[i].Height = 0f;

}
else
{

m_scrollMarker[i].Height = m_quads[(int)Section.ScrollBody].Height / ( (float)m_items.Count – (float)m_numDisplayedItems + 1 );
m_scrollMarker[i].Height = Math.Max( m_scrollMarker[i].Height, m_minMarkerHeight );

}

}
m_numDisplayedItems = (int)( m_quads[(int)Section.Background].Height / m_itemHeight );
m_numDisplayedItems = Math.Min( m_numDisplayedItems, m_items.Count );
BuildText();
BuildHotspots();

}

///

Checks is the mouse is over the Control’s hotspot
/// Mouse position
/// true if the cursor is over the Control’s hotspot, false otherwise.
public override bool Contains( Point cursor ) Toggle
{
foreach ( Rectangle r in m_hotspots )
{
if ( r.Contains( cursor ) )
{
return true;

}

}
if ( m_overState )
{

m_overState = false;
m_quads[(int)Section.ScrollUp] = m_scrollUp[(int)ControlState.Normal];
m_quads[(int)Section.ScrollDown] = m_scrollDown[(int)ControlState.Normal];
m_quads[(int)Section.ScrollMarker] = m_scrollMarker[(int)ControlState.Normal];

}
return false;

}

///

Mouse Over event
/// Mouse position
/// Mouse buttons
protected override void OnMouseOver( Point cursor, bool[] buttons ) Toggle
{
if ( m_hotspots[(int)HotSpot.ScrollUp].Contains( cursor ) )
{
m_overState = true;
m_quads[(int)Section.ScrollUp] = m_scrollUp[(int)ControlState.Over];

}
else
{

m_quads[(int)Section.ScrollUp] = m_scrollUp[(int)ControlState.Normal];

}
if ( m_hotspots[(int)HotSpot.ScrollDown].Contains( cursor ) )
{

m_overState = true;
m_quads[(int)Section.ScrollDown] = m_scrollDown[(int)ControlState.Over];

}
else
{

m_quads[(int)Section.ScrollDown] = m_scrollDown[(int)ControlState.Normal];

}
if ( m_hotspots[(int)HotSpot.ScrollMarker].Contains( cursor ) )
{

m_overState = true;
m_quads[(int)Section.ScrollMarker] = m_scrollMarker[(int)ControlState.Over];

}
else
{

m_quads[(int)Section.ScrollMarker] = m_scrollMarker[(int)ControlState.Normal];

}

}

///

Mouse Down event
/// Mouse position
/// Mouse buttons
protected override void OnMouseDown( Point cursor, bool[] buttons ) Toggle
{
if ( m_hotspots[(int)HotSpot.ScrollUp].Contains( cursor ) && !m_hasFocus )
{
m_quads[(int)Section.ScrollUp] = m_scrollUp[(int)ControlState.Down];

}
else if ( !m_hasFocus )
{

m_quads[(int)Section.ScrollUp] = m_scrollUp[(int)ControlState.Normal];

}
if ( m_hotspots[(int)HotSpot.ScrollDown].Contains( cursor ) && !m_hasFocus )
{

m_quads[(int)Section.ScrollDown] = m_scrollDown[(int)ControlState.Down];

}
else if ( !m_hasFocus )
{

m_quads[(int)Section.ScrollDown] = m_scrollDown[(int)ControlState.Normal];

}
if ( m_hotspots[(int)HotSpot.ScrollMarker].Contains( cursor ) && !m_hasFocus )
{

m_quads[(int)Section.ScrollMarker] = m_scrollMarker[(int)ControlState.Down];

}
else if ( !m_hasFocus )
{

m_quads[(int)Section.ScrollMarker] = m_scrollMarker[(int)ControlState.Normal];

}
if ( ( m_hotspots[(int)HotSpot.ScrollBody].Contains( cursor ) || m_scrolling ) &&
cursor.Y < m_quads[(int)Section.ScrollBody].Bottom &&
cursor.Y > m_quads[(int)Section.ScrollBody].Y )
{

// Scroll with draggable scroll box
m_scrolling = true;
m_quads[(int)Section.ScrollMarker] = m_scrollMarker[(int)ControlState.Down];

// Move scroll marker
for ( int i = 0; i < 4; i++ )
{

m_scrollMarker[i].Y =
m_scrollMarker[i].Y = cursor.Y – ( m_scrollMarker[i].Height / 2f );
m_scrollMarker[i].Y = Math.Max( m_scrollMarker[i].Y,
m_quads[(int)Section.ScrollBody].Y );
m_scrollMarker[i].Y = Math.Min( m_scrollMarker[i].Y,
m_quads[(int)Section.ScrollDown].Y – m_scrollMarker[i].Height );

}
m_disabledQuads[(int)Section.ScrollMarker].Y = m_quads[(int)Section.ScrollMarker].Y;

// Change item range

int oldTopItem = m_topItem;
int divisionHeight = (int)m_quads[(int)Section.ScrollBody].Height / ( m_items.Count – m_numDisplayedItems + 1 );
if ( divisionHeight > 0 )
{

m_topItem = ( cursor.Y – (int)m_quads[(int)Section.ScrollBody].Y ) / divisionHeight;
m_topItem = Math.Min( m_topItem, m_items.Count – m_numDisplayedItems );

}
else
{

m_topItem = 0;

}

// Scroll highlights also
float highlightOffset = (float)( oldTopItem – m_topItem ) * m_itemHeight;
for ( int i = 0; i < m_normalHighlights.Count; i++ )
{

m_normalHighlights[i].Y += highlightOffset;
m_disabledHighlights[i].Y += highlightOffset;
if ( m_normalHighlights[i].Y < m_quads[(int)Section.Background].Y ||
m_normalHighlights[i].Bottom > m_quads[(int)Section.Background].Bottom )
{
m_normalHighlights[i].Color = 0;
m_disabledHighlights[i].Color = 0;

}
else if ( m_items[i].Selected )
{

m_normalHighlights[i].Color = m_highlightColor;
m_disabledHighlights[i].Color = m_disabledHighlightColor;

}

}

// Update the text and hotspots
BuildText();
BuildHotspots();

}

}

///

Mouse Release event
/// Mouse position
protected override void OnMouseRelease( Point cursor ) Toggle
{
m_scrolling = false;
m_newData = false;
if ( m_hotspots[(int)HotSpot.ScrollDown].Contains( cursor ) )
{
ScrollDown();
BuildHotspots();

}
else if ( m_hotspots[(int)HotSpot.ScrollUp].Contains( cursor ) )
{

ScrollUp();

}
else if ( m_hotspots[(int)HotSpot.Background].Contains( cursor ) )
{

int highlightedItem = ( cursor.Y – (int)m_quads[(int)Section.Background].Y ) / (int)m_itemHeight;
if ( ( highlightedItem < m_numDisplayedItems ) && ( highlightedItem + m_topItem < m_items.Count ) )
{
highlightedItem += m_topItem;
m_items[highlightedItem].Selected = !m_items[highlightedItem].Selected;
if ( m_items[highlightedItem].Selected )
{
// Check for new data
m_newData = true;
foreach ( object o in (ArrayList)m_data )
{
if ( m_items[highlightedItem].Data == o )
{
m_newData = false;
break;

}

}

// Turn alpha on
m_normalHighlights[highlightedItem].Color = m_highlightColor;
m_disabledHighlights[highlightedItem].Color = m_disabledHighlightColor;

// Deselect other items if a single item list box
if ( m_singleItemListBox )
{

( (ArrayList)m_data ).Clear();
for ( int i = 0; i < m_items.Count; i++ )
{
if ( i != highlightedItem )
{
m_items[i].Selected = false;
m_normalHighlights[i].Color = 0;
m_disabledHighlights[i].Color = 0;

}

}

}
( (ArrayList)m_data ).Add( m_items[highlightedItem].Data );

float newY = ( highlightedItem – m_topItem ) * m_itemHeight + (int)m_quads[(int)Section.Background].Y;
m_normalHighlights[highlightedItem].Y = newY;
m_disabledHighlights[highlightedItem].Y = newY;
m_normalHighlights[highlightedItem].Color = m_highlightColor;
m_disabledHighlights[highlightedItem].Color = m_disabledHighlightColor;

}
else
{

// Item was deselected
m_newData = true;
( (ArrayList)m_data ).Remove( m_items[highlightedItem].Data );
m_normalHighlights[highlightedItem].Color = 0;
m_disabledHighlights[highlightedItem].Color = 0;

}

}

}
BuildHotspots();

}

///

Scrolls down one line
protected virtual void ScrollDown() Toggle
{
if ( m_topItem + m_numDisplayedItems < m_items.Count )
{
m_topItem++;

// Move marker down
int divisionHeight = (int)m_quads[(int)Section.ScrollBody].Height / ( m_items.Count – m_numDisplayedItems + 1 );
for ( int i = 0; i < 4; i++ )
{

m_scrollMarker[i].Y = m_quads[(int)Section.ScrollBody].Y + ( (float)m_topItem * (float)divisionHeight );

}

BuildText();

// Adjust the highlights
for ( int i = 0; i < m_normalHighlights.Count; i++ )
{

m_normalHighlights[i].Y -= m_itemHeight;
m_disabledHighlights[i].Y -= m_itemHeight;

if ( m_normalHighlights[i].Y < m_quads[(int)Section.Background].Y ||
m_normalHighlights[i].Bottom > m_quads[(int)Section.Background].Bottom )
{

m_normalHighlights[i].Color = 0;
m_disabledHighlights[i].Color = 0;

}
else if ( m_items[i].Selected )
{

m_normalHighlights[i].Color = m_highlightColor;
m_disabledHighlights[i].Color = m_disabledHighlightColor;

}

}

}
else
{

for ( int i = 0; i < 4; i++ )
{
m_scrollMarker[i].Y = m_quads[(int)Section.ScrollBody].Bottom – m_quads[(int)Section.ScrollMarker].Height;

}

}

}

///

Scrolls up one line
protected virtual void ScrollUp() Toggle
{
if ( m_topItem > 0 )
{
m_topItem–;

// Move marker up
int divisionHeight = (int)m_quads[(int)Section.ScrollBody].Height / ( m_items.Count – m_numDisplayedItems + 1 );
for ( int i = 0; i < 4; i++ )
{

m_scrollMarker[i].Y = m_quads[(int)Section.ScrollBody].Y + ( (float)m_topItem * (float)divisionHeight );

}

BuildText();

// Adjust the highlights
for ( int i = 0; i < m_normalHighlights.Count; i++ )
{

m_normalHighlights[i].Y += m_itemHeight;
m_disabledHighlights[i].Y += m_itemHeight;

if ( m_normalHighlights[i].Y < m_quads[(int)Section.Background].Y ||
m_normalHighlights[i].Bottom > m_quads[(int)Section.Background].Bottom )
{

m_normalHighlights[i].Color = 0;
m_disabledHighlights[i].Color = 0;

}
else if ( m_items[i].Selected )
{

m_normalHighlights[i].Color = m_highlightColor;
m_disabledHighlights[i].Color = m_disabledHighlightColor;

}

}

}
else
{

for ( int i = 0; i < 4; i++ )
{
m_scrollMarker[i].Y = m_quads[(int)Section.ScrollBody].Y;

}

}

}

///

Mouse wheel event
/// Mouse wheel delta
protected override void OnZDelta( float zDelta ) Toggle
{
if ( zDelta > 0f )
{
ScrollUp();

}
else
{

ScrollDown();

}

}

///

Gets the Button’s current Quads
public override List Quads Toggle

{

get
{

switch ( m_state )
{

case ControlState.Disabled:

return m_disabledQuads;

default:

return m_quads;

}

}

}

///

Gets and sets the Panel’s position
public override PointF Position Toggle
{
get { return m_position; }
set
{
float xOffset = value.X – m_position.X;
float yOffset = value.Y – m_position.Y;
m_position = value;
for ( int i = 0; i < m_quads.Count; i++ )
{
if ( i == (int)Section.ScrollUp || i == (int)Section.ScrollDown || i == (int)Section.ScrollMarker )
{
continue;

}
m_quads[i].X += xOffset;
m_quads[i].Y += yOffset;
m_disabledQuads[i].X += xOffset;
m_disabledQuads[i].Y += yOffset;

}
for ( int i = 0; i < 4; i++ )
{

m_scrollUp[i].X += xOffset;
m_scrollUp[i].Y += yOffset;
m_scrollDown[i].X += xOffset;
m_scrollDown[i].Y += yOffset;
m_scrollMarker[i].X += xOffset;
m_scrollMarker[i].Y += yOffset;

}

BuildHotspots();

for ( int i = 0; i < m_fontQuads.Count; i++ )
{

m_fontQuads[i].X += xOffset;
m_fontQuads[i].Y += yOffset;

}

}

}

///

Positions the Quads
protected virtual void PositionQuads() Toggle
{
// Adjust middle column x
m_quads[(int)Section.Top].X = m_quads[(int)Section.TopLeft].Right;
m_disabledQuads[(int)Section.Top].X = m_disabledQuads[(int)Section.TopLeft].Right;

m_quads[(int)Section.Background].X = m_quads[(int)Section.Left].Right;
m_disabledQuads[(int)Section.Background].X = m_disabledQuads[(int)Section.Left].Right;

m_quads[(int)Section.Bottom].X = m_quads[(int)Section.BottomLeft].Right;
m_disabledQuads[(int)Section.Bottom].X = m_disabledQuads[(int)Section.BottomLeft].Right;

// Adjust middle column width
m_quads[(int)Section.Top].Width =
m_size.Width – m_quads[(int)Section.TopLeft].Width –
m_quads[(int)Section.TopRight].Width – m_quads[(int)Section.ScrollUp].Width;
m_disabledQuads[(int)Section.Top].Width =
m_size.Width – m_disabledQuads[(int)Section.TopLeft].Width –
m_disabledQuads[(int)Section.TopRight].Width – m_disabledQuads[(int)Section.ScrollUp].Width;

m_quads[(int)Section.Background].Width =
m_size.Width – m_quads[(int)Section.Left].Width –
m_quads[(int)Section.Right].Width – m_quads[(int)Section.ScrollBody].Width;
m_disabledQuads[(int)Section.Background].Width =
m_size.Width – m_disabledQuads[(int)Section.Left].Width –
m_disabledQuads[(int)Section.Right].Width – m_disabledQuads[(int)Section.ScrollBody].Width;

m_quads[(int)Section.Bottom].Width =
m_size.Width – m_quads[(int)Section.BottomLeft].Width –
m_quads[(int)Section.BottomRight].Width – m_quads[(int)Section.ScrollDown].Width;
m_disabledQuads[(int)Section.Bottom].Width =
m_size.Width – m_disabledQuads[(int)Section.BottomLeft].Width –
m_disabledQuads[(int)Section.BottomRight].Width – m_disabledQuads[(int)Section.ScrollDown].Width;

// Adjust right column X
m_quads[(int)Section.TopRight].X =
m_quads[(int)Section.Top].Right;
m_disabledQuads[(int)Section.TopRight].X =
m_disabledQuads[(int)Section.Top].Right;

m_quads[(int)Section.Right].X =
m_quads[(int)Section.Background].Right;
m_disabledQuads[(int)Section.Right].X =
m_disabledQuads[(int)Section.Background].Right;

m_quads[(int)Section.BottomRight].X =
m_quads[(int)Section.Bottom].Right;
m_disabledQuads[(int)Section.BottomRight].X =
m_disabledQuads[(int)Section.Bottom].Right;

// Adjust middle row Y
m_quads[(int)Section.Left].Y = m_quads[(int)Section.TopLeft].Bottom;
m_disabledQuads[(int)Section.Left].Y = m_disabledQuads[(int)Section.TopLeft].Bottom;

m_quads[(int)Section.Background].Y = m_quads[(int)Section.Top].Bottom;
m_disabledQuads[(int)Section.Background].Y = m_disabledQuads[(int)Section.Top].Bottom;

m_quads[(int)Section.Right].Y = m_quads[(int)Section.TopRight].Bottom;
m_disabledQuads[(int)Section.Right].Y = m_disabledQuads[(int)Section.TopRight].Bottom;

// Adjust middle row height
m_quads[(int)Section.Left].Height =
m_size.Height – m_quads[(int)Section.TopLeft].Height –
m_quads[(int)Section.BottomLeft].Height;
m_disabledQuads[(int)Section.Left].Height =
m_size.Height – m_disabledQuads[(int)Section.TopLeft].Height –
m_disabledQuads[(int)Section.BottomLeft].Height;

m_quads[(int)Section.Background].Height =
m_size.Height – m_quads[(int)Section.Top].Height –
m_quads[(int)Section.Bottom].Height;
m_disabledQuads[(int)Section.Background].Height =
m_size.Height – m_disabledQuads[(int)Section.Top].Height –
m_disabledQuads[(int)Section.Bottom].Height;

m_quads[(int)Section.Right].Height =
m_size.Height – m_quads[(int)Section.TopRight].Height –
m_quads[(int)Section.BottomRight].Height;
m_disabledQuads[(int)Section.Right].Height =
m_size.Height – m_disabledQuads[(int)Section.TopRight].Height –
m_disabledQuads[(int)Section.BottomRight].Height;

// Adjust bottom row Y
m_quads[(int)Section.BottomLeft].Y =
m_quads[(int)Section.Left].Bottom;
m_disabledQuads[(int)Section.BottomLeft].Y =
m_disabledQuads[(int)Section.Left].Bottom;

m_quads[(int)Section.Bottom].Y =
m_quads[(int)Section.Background].Bottom;
m_disabledQuads[(int)Section.Bottom].Y =
m_disabledQuads[(int)Section.Background].Bottom;

m_quads[(int)Section.BottomRight].Y =
m_quads[(int)Section.Right].Bottom;
m_disabledQuads[(int)Section.BottomRight].Y =
m_disabledQuads[(int)Section.Right].Bottom;

// Adjust scroll bar X
for ( int i = 0; i < 4; i++ )
{

m_scrollUp[i].X = m_quads[(int)Section.TopRight].Right;
m_scrollDown[i].X = m_quads[(int)Section.BottomRight].Right;
m_scrollMarker[i].X = m_quads[(int)Section.Right].Right;

}

m_quads[(int)Section.ScrollBody].X =
m_quads[(int)Section.Right].Right;
m_disabledQuads[(int)Section.ScrollBody].X =
m_disabledQuads[(int)Section.Right].Right;

// Adjust scroll bar Y
m_quads[(int)Section.ScrollBody].Y =
m_quads[(int)Section.ScrollUp].Bottom;
m_disabledQuads[(int)Section.ScrollBody].Y =
m_disabledQuads[(int)Section.ScrollUp].Bottom;

m_quads[(int)Section.ScrollBody].Height =
m_size.Height – m_quads[(int)Section.ScrollUp].Height –
m_quads[(int)Section.ScrollDown].Height;
m_disabledQuads[(int)Section.ScrollBody].Height =
m_size.Height – m_disabledQuads[(int)Section.ScrollUp].Height –
m_disabledQuads[(int)Section.ScrollDown].Height;

for ( int i = 0; i < 4; i++ )
{

m_scrollDown[i].Y = m_quads[(int)Section.ScrollBody].Bottom;
m_scrollMarker[i].Y = m_quads[(int)Section.ScrollUp].Bottom;

}

BuildHotspots();

BuildText();

}

///

Builds the hotspots
protected virtual void BuildHotspots() Toggle
{
m_hotspots[(int)HotSpot.Background] = new Rectangle(
(int)m_quads[(int)Section.Background].X,
(int)m_quads[(int)Section.Background].Y,
(int)m_quads[(int)Section.Background].Width,
(int)m_quads[(int)Section.Background].Height );

m_hotspots[(int)HotSpot.ScrollBody] = new Rectangle(
(int)m_quads[(int)Section.ScrollBody].X,
(int)m_quads[(int)Section.ScrollBody].Y,
(int)m_quads[(int)Section.ScrollBody].Width,
(int)m_quads[(int)Section.ScrollBody].Height );

m_hotspots[(int)HotSpot.ScrollUp] = new Rectangle(
(int)m_quads[(int)Section.ScrollUp].X,
(int)m_quads[(int)Section.ScrollUp].Y,
(int)m_quads[(int)Section.ScrollUp].Width,
(int)m_quads[(int)Section.ScrollUp].Height );

m_hotspots[(int)HotSpot.ScrollDown] = new Rectangle(
(int)m_quads[(int)Section.ScrollDown].X,
(int)m_quads[(int)Section.ScrollDown].Y,
(int)m_quads[(int)Section.ScrollDown].Width,
(int)m_quads[(int)Section.ScrollDown].Height );

m_hotspots[(int)HotSpot.ScrollMarker] = new Rectangle(
(int)m_quads[(int)Section.ScrollMarker].X,
(int)m_quads[(int)Section.ScrollMarker].Y,
(int)m_quads[(int)Section.ScrollMarker].Width,
(int)m_quads[(int)Section.ScrollMarker].Height );

}

///

Builds the text
protected virtual new void BuildText() Toggle
{
m_text = “”;
for ( int i = m_topItem; i < m_items.Count; i++ )
{
m_text += m_items[i].Text + “n”;

}
int index = m_bFont.AddString( m_text, new RectangleF(
m_quads[(int)Section.Background].X + 2f, m_quads[(int)Section.Background].Y,
m_quads[(int)Section.Background].Width, m_quads[(int)Section.Background].Height ),
BitmapFont.Align.Left, m_fontSize, m_textColor, true );
List fontQuads = m_bFont.GetProcessedQuads( index );

m_numDisplayedItems = (int)(m_quads[(int)Section.Background].Height / m_itemHeight);
m_numDisplayedItems = Math.Min( m_numDisplayedItems, m_items.Count );

// Highlight height is dependent on line height
float highlightHeight = m_itemHeight;// +( m_itemHeight / 10f );
for ( int i = 0; i < m_normalHighlights.Count; i++ )
{

m_normalHighlights[i].Height = highlightHeight;
m_disabledHighlights[i].Height = highlightHeight;

}

// Convert FontQuads to Quads
m_fontQuads = new List();
for ( int j = 0; j < fontQuads.Count; j++ )
{

m_fontQuads.Add( new Quad( fontQuads[j].TopLeft, fontQuads[j].TopRight, fontQuads[j].BottomLeft, fontQuads[j].BottomRight ) );

}
m_bFont.ClearString( index );

}

///

Checks if the cursor is in the scrollable background
/// Mouse position
/// true if the cursor is in the scrollable background, false otherwise
public bool BackgroundContainsCursor( Point cursor ) Toggle
{
return m_hotspots[(int)HotSpot.Background].Contains( cursor );

}

///

Checks if an item is in the list of items.
/// Item text
/// true if the item is already in the ListBox, false otherwise.
public virtual bool ContainsItem( string text ) Toggle
{
for ( int i = 0; i < m_items.Count; i++ )
{
if ( m_items[i].Text == text )
{
return true;

}

}
return false;

}

///

Clears the ListBox’s current selection
public virtual void Clear() Toggle
{
if ( m_quads.Count > 13 )
{
m_quads.RemoveRange( 13, m_items.Count );
m_normalHighlights.Clear();
m_disabledHighlights.Clear();

}
( (ArrayList)m_data ).Clear();
m_items.Clear();

}

///

Selects an item
/// Text of item to select
/// ID of the selected item.
public virtual int SelectItem( string itemText ) Toggle
{
int id = –1;
if ( m_singleItemListBox )
{
( (ArrayList)m_data ).Clear();

}

for ( int i = 0; i < m_items.Count; i++ )
{

if ( m_items[i].Text != itemText )
{
// Deselect other items if a single item list box
if ( m_singleItemListBox )
{
m_items[i].Selected = false;
m_normalHighlights[i].Color = 0;
m_disabledHighlights[i].Color = 0;

}
continue;

}
id = i;
m_items[i].Selected = true;
( (ArrayList)m_data ).Add( m_items[i].Data );

// Turn alpha on
m_normalHighlights[i].Color = m_highlightColor;
m_disabledHighlights[i].Color = m_disabledHighlightColor;

int oldTopItem = m_topItem;

m_topItem = Math.Min( i, m_items.Count – m_numDisplayedItems );

// Adjust highlights for multiple selection lists
if ( !m_singleItemListBox )
{

float highlightOffset = ( m_topItem – oldTopItem ) * m_itemHeight;
for ( int j = 0; j < m_normalHighlights.Count; j++ )
{
m_normalHighlights[j].Y -= highlightOffset;
m_disabledHighlights[j].Y -= highlightOffset;
if ( m_normalHighlights[j].Y < m_quads[(int)Section.Background].Y ||
m_normalHighlights[j].Bottom > m_quads[(int)Section.Background].Bottom )
{
m_normalHighlights[j].Color = 0;
m_disabledHighlights[j].Color = 0;

}
else if ( m_items[j].Selected )
{

m_normalHighlights[j].Color = m_highlightColor;
m_disabledHighlights[j].Color = m_disabledHighlightColor;

}

}

}

// Move Scroll Marker
int divisionHeight = (int)m_quads[(int)Section.ScrollBody].Height / ( m_items.Count – m_numDisplayedItems + 1 );
for ( int j = 0; j < 4; j++ )
{

m_scrollMarker[j].Y = m_quads[(int)Section.ScrollBody].Y + (float)m_topItem * (float)divisionHeight;

}

float newY = ( i – m_topItem ) * m_itemHeight + (int)m_quads[(int)Section.Background].Y;
m_normalHighlights[i].Y = newY;
m_disabledHighlights[i].Y = newY;
m_normalHighlights[i].Color = m_highlightColor;
m_disabledHighlights[i].Color = m_disabledHighlightColor;

if ( !( this is ComboBox ) )
{

BuildText();

}

}
return id;

}

///

Gets whether a new item has been selected.
public bool HasNewData Toggle
{
get { return m_newData; }

}

}

The ListBox is a menu of ListableItems with a scroll bar on the right side. I’ll start at the top and go through the methods that need some explanations. The data of a ListBox is an ArrayList of objects. The ListBox class supports both single selection and multi selection capabilities. When only a single selection is allowed, the first element of the ArrayList will hold the ListBox’s data. When multiple selections are allowed, the ArrayList will grow or shrink depending on what is selected. To support both capabilities in one class, I created another List of Quads, which will hold a highlight for each item. When we add an item with AddItem, we add the item to the List of ListableItems as well as add a new highlight onto the List of highlights.

The scroll bar logic is a bit tedious to explain so I’ll just go over it briefly. You should read through the code to understand it better. The scroll bar is divided into a number of sections that is equal to the number of times you can scroll down from the top of the list. The height of the scroll marker is equal to the height of one of these sections. However, the scroll marker does have a minimum height to prevent it from getting too small. When we drag the scroll marker, we have to keep track of a few things. First, we have to constrain its movement to stay within the bounds of the scroll body. Second, whenever the marker passes the border of one of the divided sections, we have to scroll the menu to that corresponding section. To scroll the menu, we have a top item variable. This variable is used to…duh…keep track of the top item of the displayed List. When we build the menu text in BuildText, we start at the index of m_topItem to iterate through the list. A bonus of the BitmapFont class is that it will automatically crop text within a rectangle. So if we use a rectangle the same size as the menu, BitmapFont will automatically create_ text to fit within the menu. The ScrollUp and ScrollDown, as well as the OnZDelta, methods simply move the menu one section at a time.

The OnMouseOver method simply switches out different graphics depending on where the mouse is located. This lets us see the mouse over effects on the scroll buttons. The OnMouseDown method mostly just scrolls the menu, which was explained above. It also swaps in some down state graphics much like OnMouseOver. The OnMouseRelease method is used to select or deselect items. In order to do this, we have to map the mouse cursor relative to the menu and then calculate which item the mouse clicked on. This involves some basic math that includes the position of the ListBox, the top item, and the mouse position. The difference between single selection and multi selection ListBoxes occurs in OnMouseRelease. In single selection mode, when a new item is selected, any previously selected item is deselected. In multi selection mode, the new item is just added to the list of selected items.

Along with all the previously explained code is the highlight manipulation code. Whenever we scroll the menu, we have to scroll all the highlights also. And when we select an item, we have to toggle that highlight’s visibility and move it into place.

The ListBox class contains some other useful methods such as ContainsItem, which checks if an item is already in the ListBox. SelectItem allows you to select a ListBox’s item by code. And Clear will clear all the contents of the ListBox.

Next, we’ll go over ComboBoxes.

public class ComboBox : ListBox
{
protected bool m_isOpen;
protected int m_selectedItem;
protected float m_openHeight;
// Open quads will be m_quads since it’s already implemented in ListBox
protected List m_normalQuads;
protected List m_overQuads;
protected List m_disabledClosedQuads;

protected enum Box
{

Left, Right, Top, Bottom, Background, TopLeft, TopRight,
BottomLeft, BottomRight, OpenArrow

};

protected new enum Section
{

Left, Right, Top, Bottom, Background, TopLeft, TopRight,
BottomLeft, BottomRight, ScrollUp, ScrollDown, ScrollBody,
ScrollMarker, OpenArrow

};
protected new enum HotSpot { Background, ScrollUp, ScrollDown, ScrollMarker, ScrollBody, Box };

///

Creates a ListBox
/// Control ID
/// Screen rectangle
/// Font size
/// Text color
/// Bitmap Font
/// ControlNode from XML file
/// Texture ImageInformation
public ComboBox( int id, RectangleF screenRect, float openHeight, float fontSize, ColorValue textColor, BitmapFont font, ControlNode node, ImageInformation info ) Toggle
{
m_singleItemListBox = true;
m_isOpen = false;
m_topItem = 0;
m_id = id;
m_fontSize = fontSize;
m_itemHeight = 0f;
m_textColor = textColor;
m_bFont = font;
m_scrolling = false;
m_overState = false;
m_singleItemListBox = true;
// Fill m_text up with dummy text so the FontQuads are picked up by GuiManager
m_text = “x”;
m_selectedItem = –1;
m_position = new PointF( screenRect.X, screenRect.Y );
m_size = new SizeF( screenRect.Width, screenRect.Height );
m_openHeight = openHeight;
if ( m_openHeight <= m_size.Height )
{
throw new Exception( “ComboBox open height must be greater than closed height.” );

}
m_data = new ArrayList();

int index = m_bFont.AddString( “x”, new RectangleF( 0f, 0f, 0f, 0f ), BitmapFont.Align.Left, m_fontSize, m_textColor, true );
m_itemHeight = m_bFont.GetLineHeight( index );
m_bFont.ClearString( index );

m_scrollUp = new List();
m_scrollDown = new List();
m_scrollMarker = new List();
m_quads = new List();
m_disabledQuads = new List();
m_normalHighlights = new List();
m_disabledHighlights = new List();
m_highlightQuads = new List();
m_items = new List();
m_hotspots = new List();
m_normalQuads = new List();
m_overQuads = new List();
m_disabledClosedQuads = new List();

// Initialize Lists so we can access them with indices
m_highlightQuads.Add( new Quad() );
m_highlightQuads.Add( new Quad() );
for ( int i = 0; i < 6; i++ )
{

m_hotspots.Add( new Rectangle() );

}
for ( int i = 0; i < 4; i++ )
{

m_scrollUp.Add( new Quad() );
m_scrollDown.Add( new Quad() );
m_scrollMarker.Add( new Quad() );

}
for ( int i = 0; i < 13; i++ )
{

m_quads.Add( new Quad() );

}
// m_disabledQuads aren’t used in ComboBox but are used in ListBox, so
// initialize the List so ListBox will still work
TransformedColoredTextured t = new TransformedColoredTextured( 0f, 0f, 0f, 0f, 0, 0f, 0f );
for ( int i = 0; i < 13; i++ )
{

m_disabledQuads.Add( new Quad( t, t, t, t ) );

}
for ( int i = 0; i < 10; i++ )
{

m_normalQuads.Add( new Quad() );
m_overQuads.Add( new Quad() );
m_disabledClosedQuads.Add( new Quad() );

}

float z = 0f;
float rhw = 1f;
foreach ( ImageNode i in node.Images )
{

RectangleF rect = i.Rectangle;

TransformedColoredTextured topLeft = new TransformedColoredTextured(
screenRect.X, screenRect.Y, z, rhw,
i.Color, rect.X / (float)info.Width, rect.Y / (float)info.Height );
TransformedColoredTextured topRight = new TransformedColoredTextured(
screenRect.X + rect.Width, screenRect.Y, z, rhw,
i.Color, rect.Right / (float)info.Width, rect.Y / (float)info.Height );
TransformedColoredTextured bottomRight = new TransformedColoredTextured(
screenRect.X + rect.Width, screenRect.Y + rect.Height, z, rhw,
i.Color, rect.Right / (float)info.Width, rect.Bottom / (float)info.Height );
TransformedColoredTextured bottomLeft = new TransformedColoredTextured(
screenRect.X, screenRect.Y + rect.Height, z, rhw,
i.Color, rect.X / (float)info.Width, rect.Bottom / (float)info.Height );
Quad q = new Quad( topLeft, topRight, bottomLeft, bottomRight );
if ( i.Name.EndsWith( “TopLeftCorner” ) )
{

if ( i.Name.StartsWith( “Normal” ) )
{
m_normalQuads[(int)Box.TopLeft] = q;

}
else if ( i.Name.StartsWith( “Over” ) )
{

m_overQuads[(int)Box.TopLeft] = q;

}
else if ( i.Name.StartsWith( “Disabled” ) )
{

m_disabledClosedQuads[(int)Box.TopLeft] = q;

}
else if ( i.Name.StartsWith( “List” ) )
{

m_quads[(int)Section.TopLeft] = q;

}

}
else if ( i.Name.EndsWith( “TopBorder” ) )
{

if ( i.Name.StartsWith( “Normal” ) )
{
m_normalQuads[(int)Box.Top] = q;

}
else if ( i.Name.StartsWith( “Over” ) )
{

m_overQuads[(int)Box.Top] = q;

}
else if ( i.Name.StartsWith( “Disabled” ) )
{

m_disabledClosedQuads[(int)Box.Top] = q;

}
else if ( i.Name.StartsWith( “List” ) )
{

m_quads[(int)Section.Top] = q;

}

}
else if ( i.Name.EndsWith( “TopRightCorner” ) )
{

if ( i.Name.StartsWith( “Normal” ) )
{
m_normalQuads[(int)Box.TopRight] = q;

}
else if ( i.Name.StartsWith( “Over” ) )
{

m_overQuads[(int)Box.TopRight] = q;

}
else if ( i.Name.StartsWith( “Disabled” ) )
{

m_disabledClosedQuads[(int)Box.TopRight] = q;

}
else if ( i.Name.StartsWith( “List” ) )
{

m_quads[(int)Section.TopRight] = q;

}

}
else if ( i.Name.EndsWith( “LeftBorder” ) )
{

if ( i.Name.StartsWith( “Normal” ) )
{
m_normalQuads[(int)Box.Left] = q;

}
else if ( i.Name.StartsWith( “Over” ) )
{

m_overQuads[(int)Box.Left] = q;

}
else if ( i.Name.StartsWith( “Disabled” ) )
{

m_disabledClosedQuads[(int)Box.Left] = q;

}
else if ( i.Name.StartsWith( “List” ) )
{

m_quads[(int)Section.Left] = q;

}

}
else if ( i.Name.EndsWith( “Background” ) )
{

if ( i.Name.StartsWith( “Normal” ) )
{
m_normalQuads[(int)Box.Background] = q;

}
else if ( i.Name.StartsWith( “Over” ) )
{

m_overQuads[(int)Box.Background] = q;

}
else if ( i.Name.StartsWith( “Disabled” ) )
{

m_disabledClosedQuads[(int)Box.Background] = q;

}
else if ( i.Name.StartsWith( “List” ) )
{

m_quads[(int)Section.Background] = q;

}

}
else if ( i.Name.EndsWith( “RightBorder” ) )
{

if ( i.Name.StartsWith( “Normal” ) )
{
m_normalQuads[(int)Box.Right] = q;

}
else if ( i.Name.StartsWith( “Over” ) )
{

m_overQuads[(int)Box.Right] = q;

}
else if ( i.Name.StartsWith( “Disabled” ) )
{

m_disabledClosedQuads[(int)Box.Right] = q;

}
else if ( i.Name.StartsWith( “List” ) )
{

m_quads[(int)Section.Right] = q;

}

}
else if ( i.Name.EndsWith( “BottomLeftCorner” ) )
{

if ( i.Name.StartsWith( “Normal” ) )
{
m_normalQuads[(int)Box.BottomLeft] = q;

}
else if ( i.Name.StartsWith( “Over” ) )
{

m_overQuads[(int)Box.BottomLeft] = q;

}
else if ( i.Name.StartsWith( “Disabled” ) )
{

m_disabledClosedQuads[(int)Box.BottomLeft] = q;

}
else if ( i.Name.StartsWith( “List” ) )
{

m_quads[(int)Section.BottomLeft] = q;

}

}
else if ( i.Name.EndsWith( “BottomBorder” ) )
{

if ( i.Name.StartsWith( “Normal” ) )
{
m_normalQuads[(int)Box.Bottom] = q;

}
else if ( i.Name.StartsWith( “Over” ) )
{

m_overQuads[(int)Box.Bottom] = q;

}
else if ( i.Name.StartsWith( “Disabled” ) )
{

m_disabledClosedQuads[(int)Box.Bottom] = q;

}
else if ( i.Name.StartsWith( “List” ) )
{

m_quads[(int)Section.Bottom] = q;

}

}
else if ( i.Name.EndsWith( “BottomRightCorner” ) )
{

if ( i.Name.StartsWith( “Normal” ) )
{
m_normalQuads[(int)Box.BottomRight] = q;

}
else if ( i.Name.StartsWith( “Over” ) )
{

m_overQuads[(int)Box.BottomRight] = q;

}
else if ( i.Name.StartsWith( “Disabled” ) )
{

m_disabledClosedQuads[(int)Box.BottomRight] = q;

}
else if ( i.Name.StartsWith( “List” ) )
{

m_quads[(int)Section.BottomRight] = q;

}

}
else if ( i.Name.EndsWith( “OpenArrow” ) )
{

if ( i.Name.StartsWith( “Normal” ) )
{
m_normalQuads[(int)Box.OpenArrow] = q;

}
else if ( i.Name.StartsWith( “Over” ) )
{

m_overQuads[(int)Box.OpenArrow] = q;

}
else if ( i.Name.StartsWith( “Disabled” ) )
{

m_disabledClosedQuads[(int)Box.OpenArrow] = q;

}

}
else if ( i.Name == “ListScrollBody” )
{

m_quads[(int)Section.ScrollBody] = q;

}
else if ( i.Name == “ListHighlight” )
{

m_highlightQuads[(int)Highlight.Normal] = q;
m_highlightColor = i.Color.ToArgb();
// Dummy Disabled highlight for ListBox implementation
m_highlightQuads[(int)Highlight.Disabled] = (Quad)q.Clone();

}
else if ( i.Name.EndsWith( “ScrollUp” ) )
{

if ( i.Name.StartsWith( “Normal” ) )
{
m_quads[(int)Section.ScrollUp] = q;
m_scrollUp[(int)ControlState.Normal] = q;

}
else if ( i.Name.StartsWith( “Over” ) )
{

m_scrollUp[(int)ControlState.Over] = q;

}
else if ( i.Name.StartsWith( “Down” ) )
{

m_scrollUp[(int)ControlState.Down] = q;

}
// Junk for inherited ListBox
m_scrollUp[(int)ControlState.Disabled] = (Quad)q.Clone();

}
else if ( i.Name.EndsWith( “ScrollDown” ) )
{

if ( i.Name.StartsWith( “Normal” ) )
{
m_quads[(int)Section.ScrollDown] = q;
m_scrollDown[(int)ControlState.Normal] = q;

}
else if ( i.Name.StartsWith( “Over” ) )
{

m_scrollDown[(int)ControlState.Over] = q;

}
else if ( i.Name.StartsWith( “Down” ) )
{

m_scrollDown[(int)ControlState.Down] = q;

}
// Junk for inherited ListBox
m_scrollDown[(int)ControlState.Disabled] = (Quad)q.Clone();

}
else if ( i.Name.EndsWith( “ScrollMarker” ) )
{

if ( i.Name.StartsWith( “Normal” ) )
{
m_quads[(int)Section.ScrollMarker] = q;
m_scrollMarker[(int)ControlState.Normal] = q;

}
else if ( i.Name.StartsWith( “Over” ) )
{

m_scrollMarker[(int)ControlState.Over] = q;

}
else if ( i.Name.StartsWith( “Down” ) )
{

m_scrollMarker[(int)ControlState.Down] = q;

}
// Junk for inherited ListBox
m_scrollMarker[(int)ControlState.Disabled] = (Quad)q.Clone();

}

}

PositionQuads();

}

///

Checks is the mouse is over the Control’s hotspot
/// Mouse position
/// true if the cursor is over the Control’s hotspot, false otherwise.
public override bool Contains( Point cursor ) Toggle
{
if ( m_isOpen )
{
foreach ( Rectangle r in m_hotspots )
{
if ( r.Contains( cursor ) )
{
return true;

}

}
if ( m_overState )
{

m_overState = false;
m_quads[(int)Section.ScrollUp] = m_scrollUp[(int)ControlState.Normal];
m_quads[(int)Section.ScrollDown] = m_scrollDown[(int)ControlState.Normal];
m_quads[(int)Section.ScrollMarker] = m_scrollMarker[(int)ControlState.Normal];

}

}
else if ( m_hotspots[(int)HotSpot.Box].Contains( cursor ) )
{

return true;

}
return false;

}

///

Mouse Over event
/// Mouse position
/// Mouse buttons
protected override void OnMouseOver( Point cursor, bool[] buttons ) Toggle
{
if ( m_isOpen )
{
// Create rollover effect
if ( m_hotspots[(int)HotSpot.ScrollUp].Contains( cursor ) )
{
m_overState = true;
m_overQuads[10 + (int)Section.ScrollUp] = m_scrollUp[(int)ControlState.Over];

}
else
{

m_overQuads[10 + (int)Section.ScrollUp] = m_scrollUp[(int)ControlState.Normal];

}
if ( m_hotspots[(int)HotSpot.ScrollDown].Contains( cursor ) )
{

m_overState = true;
m_overQuads[10 + (int)Section.ScrollDown] = m_scrollDown[(int)ControlState.Over];

}
else
{

m_overQuads[10 + (int)Section.ScrollDown] = m_scrollDown[(int)ControlState.Normal];

}
if ( m_hotspots[(int)HotSpot.ScrollMarker].Contains( cursor ) )
{

m_overState = true;
m_overQuads[10 + (int)Section.ScrollMarker] = m_scrollMarker[(int)ControlState.Over];

}
else
{

m_overQuads[10 + (int)Section.ScrollMarker] = m_scrollMarker[(int)ControlState.Normal];

}

// Move highlight along with mouse
int highlightedItem = ( cursor.Y – (int)m_quads[(int)Section.Background].Y ) / (int)m_itemHeight;
if ( m_hotspots[(int)HotSpot.Background].Contains( cursor ) )
{

if ( ( highlightedItem < m_numDisplayedItems ) && ( highlightedItem + m_topItem < m_items.Count ) )
{
int highlight = 0;
if ( m_selectedItem >= 0 )
{
highlight = m_selectedItem;

}
highlightedItem += m_topItem;
float newY = ( highlightedItem – m_topItem ) * m_itemHeight + (int)m_quads[(int)Section.Background].Y;
m_normalHighlights[highlight].Y = newY;
m_normalHighlights[highlight].Color = m_highlightColor;

}

}

}

}

///

Mouse Down event
/// Mouse position
/// Mouse buttons
protected override void OnMouseDown( Point cursor, bool[] buttons ) Toggle
{
if ( m_isOpen )
{
base.OnMouseDown( cursor, buttons );

// Create pushdown effect
if ( m_hotspots[(int)HotSpot.ScrollUp].Contains( cursor ) && !m_hasFocus )
{

m_overQuads[10 + (int)Section.ScrollUp] = m_scrollUp[(int)ControlState.Down];

}
else if ( !m_hasFocus )
{

m_overQuads[10 + (int)Section.ScrollUp] = m_scrollUp[(int)ControlState.Normal];

}
if ( m_hotspots[(int)HotSpot.ScrollDown].Contains( cursor ) && !m_hasFocus )
{

m_overQuads[10 + (int)Section.ScrollDown] = m_scrollDown[(int)ControlState.Down];

}
else if ( !m_hasFocus )
{

m_overQuads[10 + (int)Section.ScrollDown] = m_scrollDown[(int)ControlState.Normal];

}
if ( m_hotspots[(int)HotSpot.ScrollMarker].Contains( cursor ) && !m_hasFocus )
{

m_overQuads[10 + (int)Section.ScrollMarker] = m_scrollMarker[(int)ControlState.Down];

}
else if ( !m_hasFocus )
{

m_overQuads[10 + (int)Section.ScrollMarker] = m_scrollMarker[(int)ControlState.Normal];

}

}

}

///

Mouse Release event
/// Mouse position
protected override void OnMouseRelease( System.Drawing.Point cursor ) Toggle
{
m_newData = false;
if ( m_isOpen )
{
if ( m_hotspots[(int)HotSpot.ScrollDown].Contains( cursor ) )
{
ScrollDown();

}
else if ( m_hotspots[(int)HotSpot.ScrollUp].Contains( cursor ) )
{

ScrollUp();

}
else if ( m_hotspots[(int)HotSpot.Background].Contains( cursor ) )
{

int highlightedItem = ( cursor.Y – (int)m_quads[(int)Section.Background].Y ) / (int)m_itemHeight;
if ( ( highlightedItem < m_numDisplayedItems ) && ( highlightedItem + m_topItem < m_items.Count ) )
{
highlightedItem += m_topItem;

// Check for new data
m_newData = true;
foreach ( object o in (ArrayList)m_data )
{

if ( m_items[highlightedItem].Data == o )
{
m_newData = false;
break;

}

}

m_items[highlightedItem].Selected = true;
m_selectedItem = highlightedItem;
// Turn alpha on
m_normalHighlights[highlightedItem].Color = m_highlightColor;
m_disabledHighlights[highlightedItem].Color = m_disabledHighlightColor;

// Deselect other items if a single item list box
( (ArrayList)m_data ).Clear();
for ( int i = 0; i < m_items.Count; i++ )
{

if ( i != highlightedItem )
{
m_items[i].Selected = false;
m_normalHighlights[i].Color = 0;
m_disabledHighlights[i].Color = 0;

}

}
( (ArrayList)m_data ).Add( m_items[highlightedItem].Data );
m_text = m_items[highlightedItem].Text;
float newY = ( highlightedItem – m_topItem ) * m_itemHeight + (int)m_quads[(int)Section.Background].Y;
m_normalHighlights[highlightedItem].Y = newY;
m_disabledHighlights[highlightedItem].Y = newY;
m_normalHighlights[highlightedItem].Color = m_highlightColor;
m_disabledHighlights[highlightedItem].Color = m_disabledHighlightColor;

if ( !m_scrolling )
{

m_isOpen = false;
m_overQuads.RemoveRange( 10, m_quads.Count );
BuildText();

}

}

}
else if ( m_hotspots[(int)HotSpot.Box].Contains( cursor ) && !m_scrolling || !Contains( cursor ) )
{

m_isOpen = false;
m_overQuads.RemoveRange( 10, m_quads.Count );
BuildText();

}

}
else if ( m_hotspots[(int)HotSpot.Box].Contains( cursor ) )
{

m_isOpen = true;
if ( m_selectedItem >= 0 )
{
// Highlight may be active from mouseover
m_normalHighlights[0].Color = 0;

// Turn on selected highlight and move it to position
m_normalHighlights[m_selectedItem].Color = m_highlightColor;
m_topItem = Math.Min( m_selectedItem, m_items.Count – m_numDisplayedItems );
float newY = ( m_selectedItem – m_topItem ) * m_itemHeight + (int)m_quads[(int)Section.Background].Y;
m_normalHighlights[m_selectedItem].Y = newY;

// Move Scroll Marker
int divisionHeight = (int)m_quads[(int)Section.ScrollBody].Height / ( m_items.Count – m_numDisplayedItems + 1 );
for ( int i = 0; i < 4; i++ )
{

m_scrollMarker[i].Y = m_quads[(int)Section.ScrollBody].Y + (float)m_topItem * (float)divisionHeight;

}

}

m_overQuads.AddRange( m_quads );
BuildText();

for ( int i = 0; i < 4; i++ )
{

if ( m_numDisplayedItems == m_items.Count )
{
m_scrollMarker[i].Height = 0f;

}
else
{

m_scrollMarker[i].Height = m_quads[(int)Section.ScrollBody].Height / ( (float)m_items.Count – (float)m_numDisplayedItems + 1 );
m_scrollMarker[i].Height = Math.Max( m_scrollMarker[i].Height, m_minMarkerHeight );

}

}

}
BuildHotspots();

m_scrolling = false;

}

///

Gets the Contol’s current Quads
public override List Quads Toggle

{

get
{

switch ( m_state )
{

case ControlState.Disabled:

return m_disabledClosedQuads;

case ControlState.Over:

return m_overQuads;

case ControlState.Down:

return m_overQuads;

default:

if ( m_isOpen )
{

return m_overQuads;

}
return m_normalQuads;

}

}

}

///

Gets and sets the Panel’s position
public override PointF Position Toggle
{
get { return m_position; }
set
{
float xOffset = value.X – m_position.X;
float yOffset = value.Y – m_position.Y;
m_position = value;
for ( int i = 0; i < m_normalQuads.Count; i++ )
{
m_normalQuads[i].X += xOffset;
m_normalQuads[i].Y += yOffset;
m_overQuads[i].X += xOffset;
m_overQuads[i].Y += yOffset;
m_disabledClosedQuads[i].X += xOffset;
m_disabledClosedQuads[i].Y += yOffset;

}
for ( int i = 0; i < m_quads.Count; i++ )
{

if ( i == (int)Section.ScrollUp || i == (int)Section.ScrollDown || i == (int)Section.ScrollMarker )
{
continue;

}
m_quads[i].X += xOffset;
m_quads[i].Y += yOffset;

}
for ( int i = 0; i < 3; i++ )
{

m_scrollUp[i].X += xOffset;
m_scrollUp[i].Y += yOffset;
m_scrollDown[i].X += xOffset;
m_scrollDown[i].Y += yOffset;
m_scrollMarker[i].X += xOffset;
m_scrollMarker[i].Y += yOffset;

}

BuildHotspots();

for ( int i = 0; i < m_fontQuads.Count; i++ )
{

m_fontQuads[i].X += xOffset;
m_fontQuads[i].Y += yOffset;

}

}

}

///

Positions the Quads
protected override void PositionQuads() Toggle
{
// Adjust box middle column X
m_normalQuads[(int)Box.Top].X = m_normalQuads[(int)Box.TopLeft].Right;
m_normalQuads[(int)Box.Background].X = m_normalQuads[(int)Box.Left].Right;
m_normalQuads[(int)Box.Bottom].X = m_normalQuads[(int)Box.BottomLeft].Right;
m_overQuads[(int)Box.Top].X = m_overQuads[(int)Box.TopLeft].Right;
m_overQuads[(int)Box.Background].X = m_overQuads[(int)Box.Left].Right;
m_overQuads[(int)Box.Bottom].X = m_overQuads[(int)Box.BottomLeft].Right;
m_disabledClosedQuads[(int)Box.Top].X = m_disabledClosedQuads[(int)Box.TopLeft].Right;
m_disabledClosedQuads[(int)Box.Background].X = m_disabledClosedQuads[(int)Box.Left].Right;
m_disabledClosedQuads[(int)Box.Bottom].X = m_disabledClosedQuads[(int)Box.BottomLeft].Right;

// Adjust box middle row Y
m_normalQuads[(int)Box.Left].Y = m_normalQuads[(int)Box.TopLeft].Bottom;
m_normalQuads[(int)Box.Background].Y = m_normalQuads[(int)Box.Top].Bottom;
m_normalQuads[(int)Box.Right].Y = m_normalQuads[(int)Box.TopRight].Bottom;
m_overQuads[(int)Box.Left].Y = m_overQuads[(int)Box.TopLeft].Bottom;
m_overQuads[(int)Box.Background].Y = m_overQuads[(int)Box.Top].Bottom;
m_overQuads[(int)Box.Right].Y = m_overQuads[(int)Box.TopRight].Bottom;
m_disabledClosedQuads[(int)Box.Left].Y = m_disabledClosedQuads[(int)Box.TopLeft].Bottom;
m_disabledClosedQuads[(int)Box.Background].Y = m_disabledClosedQuads[(int)Box.Top].Bottom;
m_disabledClosedQuads[(int)Box.Right].Y = m_disabledClosedQuads[(int)Box.TopRight].Bottom;

// Adjust box middle row height
m_normalQuads[(int)Box.Left].Height =
m_size.Height – m_normalQuads[(int)Box.TopLeft].Height –
m_normalQuads[(int)Box.BottomLeft].Height;
m_normalQuads[(int)Box.Background].Height =
m_size.Height – m_normalQuads[(int)Box.Top].Height –
m_normalQuads[(int)Box.Bottom].Height;
m_normalQuads[(int)Box.Right].Height =
m_size.Height – m_quads[(int)Section.TopRight].Height –
m_normalQuads[(int)Box.BottomRight].Height;

m_overQuads[(int)Box.Left].Height =
m_size.Height – m_overQuads[(int)Box.TopLeft].Height –
m_overQuads[(int)Box.BottomLeft].Height;
m_overQuads[(int)Box.Background].Height =
m_size.Height – m_overQuads[(int)Box.Top].Height –
m_overQuads[(int)Box.Bottom].Height;
m_overQuads[(int)Box.Right].Height =
m_size.Height – m_quads[(int)Section.TopRight].Height –
m_overQuads[(int)Box.BottomRight].Height;

m_disabledClosedQuads[(int)Box.Left].Height =
m_size.Height – m_disabledClosedQuads[(int)Box.TopLeft].Height –
m_disabledClosedQuads[(int)Box.BottomLeft].Height;
m_disabledClosedQuads[(int)Box.Background].Height =
m_size.Height – m_disabledClosedQuads[(int)Box.Top].Height –
m_disabledClosedQuads[(int)Box.Bottom].Height;
m_disabledClosedQuads[(int)Box.Right].Height =
m_size.Height – m_quads[(int)Section.TopRight].Height –
m_disabledClosedQuads[(int)Box.BottomRight].Height;

// Adjust box bottom row Y
m_normalQuads[(int)Box.BottomLeft].Y = m_normalQuads[(int)Box.Left].Bottom;
m_normalQuads[(int)Box.Bottom].Y = m_normalQuads[(int)Box.Background].Bottom;
m_normalQuads[(int)Box.BottomRight].Y = m_normalQuads[(int)Box.Right].Bottom;
m_overQuads[(int)Box.BottomLeft].Y = m_overQuads[(int)Box.Left].Bottom;
m_overQuads[(int)Box.Bottom].Y = m_overQuads[(int)Box.Background].Bottom;
m_overQuads[(int)Box.BottomRight].Y = m_overQuads[(int)Box.Right].Bottom;
m_disabledClosedQuads[(int)Box.BottomLeft].Y = m_disabledClosedQuads[(int)Box.Left].Bottom;
m_disabledClosedQuads[(int)Box.Bottom].Y = m_disabledClosedQuads[(int)Box.Background].Bottom;
m_disabledClosedQuads[(int)Box.BottomRight].Y = m_disabledClosedQuads[(int)Box.Right].Bottom;

// Adjust open arrow dimensions
m_normalQuads[(int)Box.OpenArrow].Height = m_normalQuads[(int)Box.Bottom].Bottom – m_normalQuads[(int)Box.Top].Y;
m_overQuads[(int)Box.OpenArrow].Height = m_overQuads[(int)Box.Bottom].Bottom – m_overQuads[(int)Box.Top].Y;
m_disabledClosedQuads[(int)Box.OpenArrow].Height = m_disabledClosedQuads[(int)Box.Bottom].Bottom – m_disabledClosedQuads[(int)Box.Top].Y;

m_normalQuads[(int)Box.OpenArrow].Width = m_normalQuads[(int)Box.OpenArrow].Height;
m_overQuads[(int)Box.OpenArrow].Width = m_overQuads[(int)Box.OpenArrow].Height;
m_disabledClosedQuads[(int)Box.OpenArrow].Width = m_disabledClosedQuads[(int)Box.OpenArrow].Height;

// Adjust box middle column width
m_normalQuads[(int)Box.Top].Width =
m_size.Width – m_normalQuads[(int)Box.TopLeft].Width –
m_normalQuads[(int)Box.TopRight].Width – m_normalQuads[(int)Box.OpenArrow].Width;
m_normalQuads[(int)Box.Background].Width =
m_size.Width – m_normalQuads[(int)Box.Left].Width –
m_normalQuads[(int)Box.Right].Width – m_normalQuads[(int)Box.OpenArrow].Width;
m_normalQuads[(int)Box.Bottom].Width =
m_size.Width – m_quads[(int)Section.BottomLeft].Width –
m_normalQuads[(int)Box.BottomRight].Width – m_normalQuads[(int)Box.OpenArrow].Width;

m_overQuads[(int)Box.Top].Width =
m_size.Width – m_overQuads[(int)Box.TopLeft].Width –
m_overQuads[(int)Box.TopRight].Width – m_overQuads[(int)Box.OpenArrow].Width;
m_overQuads[(int)Box.Background].Width =
m_size.Width – m_overQuads[(int)Box.Left].Width –
m_overQuads[(int)Box.Right].Width – m_overQuads[(int)Box.OpenArrow].Width;
m_overQuads[(int)Box.Bottom].Width =
m_size.Width – m_quads[(int)Section.BottomLeft].Width –
m_overQuads[(int)Box.BottomRight].Width – m_overQuads[(int)Box.OpenArrow].Width;

m_disabledClosedQuads[(int)Box.Top].Width =
m_size.Width – m_disabledClosedQuads[(int)Box.TopLeft].Width –
m_disabledClosedQuads[(int)Box.TopRight].Width – m_disabledClosedQuads[(int)Box.OpenArrow].Width;
m_disabledClosedQuads[(int)Box.Background].Width =
m_size.Width – m_disabledClosedQuads[(int)Box.Left].Width –
m_disabledClosedQuads[(int)Box.Right].Width – m_disabledClosedQuads[(int)Box.OpenArrow].Width;
m_disabledClosedQuads[(int)Box.Bottom].Width =
m_size.Width – m_quads[(int)Section.BottomLeft].Width –
m_disabledClosedQuads[(int)Box.BottomRight].Width – m_disabledClosedQuads[(int)Box.OpenArrow].Width;

// Adjust box right column x
m_normalQuads[(int)Box.TopRight].X = m_normalQuads[(int)Box.Top].Right;
m_normalQuads[(int)Box.Right].X = m_normalQuads[(int)Box.Background].Right;
m_normalQuads[(int)Box.BottomRight].X = m_normalQuads[(int)Box.Bottom].Right;
m_overQuads[(int)Box.TopRight].X = m_overQuads[(int)Box.Top].Right;
m_overQuads[(int)Box.Right].X = m_overQuads[(int)Box.Background].Right;
m_overQuads[(int)Box.BottomRight].X = m_overQuads[(int)Box.Bottom].Right;
m_disabledClosedQuads[(int)Box.TopRight].X = m_disabledClosedQuads[(int)Box.Top].Right;
m_disabledClosedQuads[(int)Box.Right].X = m_disabledClosedQuads[(int)Box.Background].Right;
m_disabledClosedQuads[(int)Box.BottomRight].X = m_disabledClosedQuads[(int)Box.Bottom].Right;

// Adjust open arrow
m_normalQuads[(int)Box.OpenArrow].X = m_normalQuads[(int)Box.TopRight].Right;
m_overQuads[(int)Box.OpenArrow].X = m_overQuads[(int)Box.TopRight].Right;
m_disabledClosedQuads[(int)Box.OpenArrow].X = m_disabledClosedQuads[(int)Box.TopRight].Right;

m_quads[(int)Section.TopLeft].Y = m_overQuads[(int)Box.BottomLeft].Bottom;
m_quads[(int)Section.Top].Y = m_overQuads[(int)Box.Bottom].Bottom;
m_quads[(int)Section.TopRight].Y = m_overQuads[(int)Box.BottomRight].Bottom;
for ( int i = 0; i < 4; i++ )
{

m_scrollUp[i].Y = m_overQuads[(int)Box.OpenArrow].Bottom;

}

float oldHeight = m_size.Height;
m_size.Height = m_openHeight – m_size.Height;
base.PositionQuads();
m_size.Height = oldHeight;

}

///

Builds the hotspots
protected override void BuildHotspots() Toggle
{
m_hotspots[(int)HotSpot.Background] = new Rectangle(
(int)m_quads[(int)Section.Background].X,
(int)m_quads[(int)Section.Background].Y,
(int)m_quads[(int)Section.Background].Width,
(int)m_quads[(int)Section.Background].Height );

m_hotspots[(int)HotSpot.ScrollBody] = new Rectangle(
(int)m_quads[(int)Section.ScrollBody].X,
(int)m_quads[(int)Section.ScrollBody].Y,
(int)m_quads[(int)Section.ScrollBody].Width,
(int)m_quads[(int)Section.ScrollBody].Height );

m_hotspots[(int)HotSpot.ScrollUp] = new Rectangle(
(int)m_quads[(int)Section.ScrollUp].X,
(int)m_quads[(int)Section.ScrollUp].Y,
(int)m_quads[(int)Section.ScrollUp].Width,
(int)m_quads[(int)Section.ScrollUp].Height );

m_hotspots[(int)HotSpot.ScrollDown] = new Rectangle(
(int)m_quads[(int)Section.ScrollDown].X,
(int)m_quads[(int)Section.ScrollDown].Y,
(int)m_quads[(int)Section.ScrollDown].Width,
(int)m_quads[(int)Section.ScrollDown].Height );

m_hotspots[(int)HotSpot.ScrollMarker] = new Rectangle(
(int)m_quads[(int)Section.ScrollMarker].X,
(int)m_quads[(int)Section.ScrollMarker].Y,
(int)m_quads[(int)Section.ScrollMarker].Width,
(int)m_quads[(int)Section.ScrollMarker].Height );

m_hotspots[(int)HotSpot.Box] = new Rectangle(
(int)m_normalQuads[(int)Box.TopLeft].X,
(int)m_normalQuads[(int)Box.TopLeft].Y,
(int)m_normalQuads[(int)Box.OpenArrow].Right – (int)m_normalQuads[(int)Box.Left].X,
(int)m_normalQuads[(int)Box.Bottom].Bottom – (int)m_normalQuads[(int)Box.Top].Y );

}

///

Builds the text
protected override void BuildText() Toggle
{
m_fontQuads = new List();

// Add selected item text
if ( m_selectedItem >= 0 )
{

float y = m_normalQuads[(int)Box.Background].Y +
( m_normalQuads[(int)Box.Background].Height / 2f ) – ( m_itemHeight / 2f );
int index = m_bFont.AddString( m_items[m_selectedItem].Text, new RectangleF(
m_normalQuads[(int)Box.Background].X + 2f, y,
m_quads[(int)Section.Background].Width, m_quads[(int)Section.Background].Height ),
BitmapFont.Align.Left, m_fontSize, m_textColor, true );
List selectedFontQuads = m_bFont.GetProcessedQuads( index );
m_bFont.ClearString( index );

// Convert FontQuads to Quads
for ( int j = 0; j < selectedFontQuads.Count; j++ )
{

m_fontQuads.Add( new Quad( selectedFontQuads[j].TopLeft, selectedFontQuads[j].TopRight, selectedFontQuads[j].BottomLeft, selectedFontQuads[j].BottomRight ) );

}

}

if ( m_isOpen )
{

string text = “”;
for ( int i = m_topItem; i < m_items.Count; i++ )
{
text += m_items[i].Text + “n”;

}
int index = m_bFont.AddString( text, new RectangleF(
m_quads[(int)Section.Background].X + 2f, m_quads[(int)Section.Background].Y,
m_quads[(int)Section.Background].Width, m_quads[(int)Section.Background].Height ),
BitmapFont.Align.Left, m_fontSize, m_textColor, true );
List fontQuads = m_bFont.GetProcessedQuads( index );
m_bFont.ClearString( index );

// Highlight height is dependent on line height
float highlightHeight = m_itemHeight;
for ( int i = 0; i < m_normalHighlights.Count; i++ )
{

m_normalHighlights[i].Height = highlightHeight;
m_disabledHighlights[i].Height = highlightHeight;

}

// Convert FontQuads to Quads
for ( int j = 0; j < fontQuads.Count; j++ )
{

m_fontQuads.Add( new Quad( fontQuads[j].TopLeft, fontQuads[j].TopRight, fontQuads[j].BottomLeft, fontQuads[j].BottomRight ) );

}

}

}

///

Selects an item.
/// Item text
/// ID of the selected item.
public override int SelectItem( string itemText ) Toggle
{
m_selectedItem = base.SelectItem( itemText );

BuildText();

m_topItem = Math.Min( m_selectedItem, m_items.Count – m_numDisplayedItems );

return m_selectedItem;

}

///

Clears the ComboBox’s selected item.
public override void Clear() Toggle
{
base.Clear();
m_selectedItem = –1;
BuildText();

}

///

Gets and sets whether the ComboBox is open.
public bool IsOpen Toggle
{
get { return m_isOpen; }
set
{
if ( value && !m_isOpen )
{
m_overQuads.AddRange( m_quads );

}
else if ( !value && m_isOpen )
{

m_overQuads.RemoveRange( 10, m_quads.Count );

}
m_isOpen = value;
BuildText();

}

}

}

A ComboBox is basically a ListBox that opens and closes. Therefore, ComboBox inherits from ListBox. Most of the logic behind a ComboBox was explained in the ListBoxes section so there isn’t as much to say. The selected item’s text is displayed in the little text section at the top of the ComboBox.

Some features of the ComboBox include the highlight moving with the mouse when the mouse moves over the open menu. Also, when an item is currently selected and the menu opens, the menu will automatically be scrolled to the selected item.

Since ComboBox’s and ListBoxes share most of the same explanations, we’ll move onto the last Control, EditBoxes:

public class EditBox : Control
{
private const int Backspace = (int)System.Windows.Forms.Keys.Back;
private const int Delete = (int)System.Windows.Forms.Keys.Delete;
private const int Left = (int)System.Windows.Forms.Keys.Left;
private const int Right = (int)System.Windows.Forms.Keys.Right;
private const int End = (int)System.Windows.Forms.Keys.End;
private const int Home = (int)System.Windows.Forms.Keys.Home;
private const int Return = (int)System.Windows.Forms.Keys.Return;

private List m_disabledQuads;
private Quad m_cursor;
private int m_cursorPosition;
private int m_maxLength;
private enum Section { Left, Right, Top, Bottom, Background, TopLeft, TopRight, BottomLeft, BottomRight };

///

Creates an EditBox
/// Control ID
/// Screen rectangle
/// EditBox text
/// Font size
/// Max number of characters allowed in the edit box.
/// Text color
/// Bitmap font
/// ControlNode from XML file
/// Texture ImageInformation
public EditBox( int id, RectangleF screenRect, string text, float fontSize, int maxLength, ColorValue textColor, BitmapFont font, ControlNode node, ImageInformation info ) Toggle
{
m_id = id;
if ( text.Length > maxLength )
{
text.Remove( maxLength – 1 );

}
m_text = text;
m_data = text;
m_fontSize = fontSize;
m_textColor = textColor;
m_bFont = font;
m_position = new PointF( screenRect.X, screenRect.Y );
m_size = new SizeF( screenRect.Width, screenRect.Height );
m_cursorPosition = 0;
m_maxLength = maxLength;

m_quads = new List();
m_disabledQuads = new List();

// Initialize Lists so we can access them with indices
for ( int i = 0; i < 9; i++ )
{

m_quads.Add( new Quad() );
m_disabledQuads.Add( new Quad() );

}

float z = 0f;
float rhw = 1f;
foreach ( ImageNode i in node.Images )
{

RectangleF rect = i.Rectangle;

TransformedColoredTextured topLeft = new TransformedColoredTextured(
screenRect.X, screenRect.Y, z, rhw,
i.Color, rect.X / (float)info.Width, rect.Y / (float)info.Height );
TransformedColoredTextured topRight = new TransformedColoredTextured(
screenRect.X + rect.Width, screenRect.Y, z, rhw,
i.Color, rect.Right / (float)info.Width, rect.Y / (float)info.Height );
TransformedColoredTextured bottomRight = new TransformedColoredTextured(
screenRect.X + rect.Width, screenRect.Y + rect.Height, z, rhw,
i.Color, rect.Right / (float)info.Width, rect.Bottom / (float)info.Height );
TransformedColoredTextured bottomLeft = new TransformedColoredTextured(
screenRect.X, screenRect.Y + rect.Height, z, rhw,
i.Color, rect.X / (float)info.Width, rect.Bottom / (float)info.Height );
Quad q = new Quad( topLeft, topRight, bottomLeft, bottomRight );
if ( i.Name.EndsWith( “TopLeftCorner” ) )
{

if ( i.Name.StartsWith( “Normal” ) )
{
m_quads[(int)Section.TopLeft] = q;

}
else if ( i.Name.StartsWith( “Disabled” ) )
{

m_disabledQuads[(int)Section.TopLeft] = q;

}

}
else if ( i.Name.EndsWith( “TopBorder” ) )
{

if ( i.Name.StartsWith( “Normal” ) )
{
m_quads[(int)Section.Top] = q;

}
else if ( i.Name.StartsWith( “Disabled” ) )
{

m_disabledQuads[(int)Section.Top] = q;

}

}
else if ( i.Name.EndsWith( “TopRightCorner” ) )
{

if ( i.Name.StartsWith( “Normal” ) )
{
m_quads[(int)Section.TopRight] = q;

}
else if ( i.Name.StartsWith( “Disabled” ) )
{

m_disabledQuads[(int)Section.TopRight] = q;

}

}
else if ( i.Name.EndsWith( “LeftBorder” ) )
{

if ( i.Name.StartsWith( “Normal” ) )
{
m_quads[(int)Section.Left] = q;

}
else if ( i.Name.StartsWith( “Disabled” ) )
{

m_disabledQuads[(int)Section.Left] = q;

}

}
else if ( i.Name.EndsWith( “Background” ) )
{

if ( i.Name.StartsWith( “Normal” ) )
{
m_quads[(int)Section.Background] = q;

}
else if ( i.Name.StartsWith( “Disabled” ) )
{

m_disabledQuads[(int)Section.Background] = q;

}

}
else if ( i.Name.EndsWith( “RightBorder” ) )
{

if ( i.Name.StartsWith( “Normal” ) )
{
m_quads[(int)Section.Right] = q;

}
else if ( i.Name.StartsWith( “Disabled” ) )
{

m_disabledQuads[(int)Section.Right] = q;

}

}
else if ( i.Name.EndsWith( “BottomLeftCorner” ) )
{

if ( i.Name.StartsWith( “Normal” ) )
{
m_quads[(int)Section.BottomLeft] = q;

}
else if ( i.Name.StartsWith( “Disabled” ) )
{

m_disabledQuads[(int)Section.BottomLeft] = q;

}

}
else if ( i.Name.EndsWith( “BottomBorder” ) )
{

if ( i.Name.StartsWith( “Normal” ) )
{
m_quads[(int)Section.Bottom] = q;

}
else if ( i.Name.StartsWith( “Disabled” ) )
{

m_disabledQuads[(int)Section.Bottom] = q;

}

}
else if ( i.Name.EndsWith( “BottomRightCorner” ) )
{

if ( i.Name.StartsWith( “Normal” ) )
{
m_quads[(int)Section.BottomRight] = q;

}
else if ( i.Name.StartsWith( “Disabled” ) )
{

m_disabledQuads[(int)Section.BottomRight] = q;

}

}
else if ( i.Name == “NormalCursor” )
{

m_cursor = q;

}

}

PositionQuads();

}

///

Mouse Release event
/// Mouse position
protected override void OnMouseRelease( Point cursor ) Toggle
{
if ( m_hasFocus )
{
// Add cursor
if ( m_quads.Count == 9 )
{
m_quads.Add( m_cursor );

}

}
else
{

// Remove cursor
m_quads.RemoveAt( 9 );

}

}

///

Releases EditBox focus
public void ReleaseFocus() Toggle
{
if ( m_hasFocus )
{
m_hasFocus = false;
// Remove cursor
m_quads.RemoveAt( 9 );

}

}

///

Key down handler.
/// List of pressed keys
/// Pressed character
/// Pressed key from Form used for repeatable keys
/// true if a Control processed the keyboard, false otherwise
public override bool OnKeyDown( List pressedKeys, char pressedChar, int pressedKey ) Toggle
{
if ( !m_hasFocus )
{
return false;

}
// Non-displayable character
if ( pressedKey == Backspace )
{

if ( m_cursorPosition > 0 )
{
m_cursorPosition–;
m_text = m_text.Remove( m_cursorPosition, 1 );

}

}
else if ( pressedKey == Delete )
{

if ( m_cursorPosition < m_text.Length )
{
m_text = m_text.Remove( m_cursorPosition, 1 );

}

}
else if ( pressedKey == Left )
{

if ( m_cursorPosition <= 0 )
{
return false;

}
if ( pressedKeys.Contains( Keys.ControlKey ) )
{

if ( m_text[m_cursorPosition – 1] == ‘ ‘ )
{
while ( m_cursorPosition != 0 && m_text[m_cursorPosition – 1] == ‘ ‘ )
{
m_cursorPosition–;

}

}
else
{

while ( m_cursorPosition != 0 && m_text[m_cursorPosition – 1] != ‘ ‘ )
{
m_cursorPosition–;

}

}

}
else
{

m_cursorPosition–;

}

}
else if ( pressedKey == Right )
{

if ( m_cursorPosition >= m_text.Length )
{
return false;

}
if ( pressedKeys.Contains( Keys.ControlKey ) )
{

if ( m_text[m_cursorPosition] == ‘ ‘ )
{
while ( m_cursorPosition < m_text.Length && m_text[m_cursorPosition] == ‘ ‘ )
{
m_cursorPosition++;

}

}
else
{

while ( m_cursorPosition < m_text.Length && m_text[m_cursorPosition] != ‘ ‘ )
{
m_cursorPosition++;

}

}

}
else
{

m_cursorPosition++;

}

}
else if ( pressedKey == End )
{

m_cursorPosition = m_text.Length;

}
else if ( pressedKey == Home )
{

m_cursorPosition = 0;

}
else if ( pressedKey == Return )
{

ReleaseFocus();
return true;

}

if ( char.IsLetterOrDigit( pressedChar ) || char.IsPunctuation( pressedChar ) || char.IsSymbol( pressedChar ) || char.IsWhiteSpace( pressedChar ) )
{

if ( m_text.Length < m_maxLength && pressedChar != char.MinValue )
{
if ( m_cursorPosition == m_text.Length )
{
m_text += pressedChar.ToString();

}
else
{

m_text = m_text.Insert( m_cursorPosition, pressedChar.ToString() );

}
m_cursorPosition++;

}

}

m_data = m_text;
BuildText();
return true;

}

///

Gets and sets the Panel’s position
public override PointF Position Toggle
{
get { return m_position; }
set
{
float xOffset = value.X – m_position.X;
float yOffset = value.Y – m_position.Y;
m_position = value;

for ( int i = 0; i < 9; i++ )
{

m_quads[i].X += xOffset;
m_quads[i].Y += yOffset;
m_disabledQuads[i].X += xOffset;
m_disabledQuads[i].Y += yOffset;

}
for ( int i = 0; i < m_fontQuads.Count; i++ )
{

m_fontQuads[i].X += xOffset;
m_fontQuads[i].Y += yOffset;

}
m_cursor.X += xOffset;
m_cursor.Y += yOffset;
BuildHotspot();

}

}

///

Repositions the hot spot and text
private void PositionQuads() Toggle
{
// Adjust middle column x
m_quads[(int)Section.Top].X = m_quads[(int)Section.TopLeft].Right;
m_quads[(int)Section.Background].X = m_quads[(int)Section.Left].Right;
m_quads[(int)Section.Bottom].X = m_quads[(int)Section.BottomLeft].Right;
m_disabledQuads[(int)Section.Top].X = m_disabledQuads[(int)Section.TopLeft].Right;
m_disabledQuads[(int)Section.Background].X = m_disabledQuads[(int)Section.Left].Right;
m_disabledQuads[(int)Section.Bottom].X = m_disabledQuads[(int)Section.BottomLeft].Right;

// Adjust middle column width
m_quads[(int)Section.Top].Width =
m_size.Width – m_quads[(int)Section.TopLeft].Width –
m_quads[(int)Section.TopRight].Width;
m_disabledQuads[(int)Section.Top].Width =
m_size.Width – m_disabledQuads[(int)Section.TopLeft].Width –
m_disabledQuads[(int)Section.TopRight].Width;

m_quads[(int)Section.Background].Width =
m_size.Width – m_quads[(int)Section.Left].Width –
m_quads[(int)Section.Right].Width;
m_disabledQuads[(int)Section.Background].Width =
m_size.Width – m_disabledQuads[(int)Section.Left].Width –
m_disabledQuads[(int)Section.Right].Width;

m_quads[(int)Section.Bottom].Width =
m_size.Width – m_quads[(int)Section.BottomLeft].Width –
m_quads[(int)Section.BottomLeft].Width;
m_disabledQuads[(int)Section.Bottom].Width =
m_size.Width – m_disabledQuads[(int)Section.BottomLeft].Width –
m_disabledQuads[(int)Section.BottomLeft].Width;

// Adjust right column X
m_quads[(int)Section.TopRight].X = m_quads[(int)Section.Top].Right;
m_quads[(int)Section.Right].X = m_quads[(int)Section.Background].Right;
m_quads[(int)Section.BottomRight].X = m_quads[(int)Section.Bottom].Right;
m_disabledQuads[(int)Section.TopRight].X = m_disabledQuads[(int)Section.Top].Right;
m_disabledQuads[(int)Section.Right].X = m_disabledQuads[(int)Section.Background].Right;
m_disabledQuads[(int)Section.BottomRight].X = m_disabledQuads[(int)Section.Bottom].Right;

// Adjust middle row Y
m_quads[(int)Section.Left].Y = m_quads[(int)Section.TopLeft].Bottom;
m_quads[(int)Section.Background].Y = m_quads[(int)Section.Top].Bottom;
m_quads[(int)Section.Right].Y = m_quads[(int)Section.TopRight].Bottom;
m_disabledQuads[(int)Section.Left].Y = m_disabledQuads[(int)Section.TopLeft].Bottom;
m_disabledQuads[(int)Section.Background].Y = m_disabledQuads[(int)Section.Top].Bottom;
m_disabledQuads[(int)Section.Right].Y = m_disabledQuads[(int)Section.TopRight].Bottom;

// Adjust middle row height
m_quads[(int)Section.Left].Height =
m_size.Height – m_quads[(int)Section.TopLeft].Height –
m_quads[(int)Section.BottomLeft].Height;
m_disabledQuads[(int)Section.Left].Height =
m_size.Height – m_disabledQuads[(int)Section.TopLeft].Height –
m_disabledQuads[(int)Section.BottomLeft].Height;

m_quads[(int)Section.Background].Height =
m_size.Height – m_quads[(int)Section.Top].Height –
m_quads[(int)Section.Bottom].Height;
m_disabledQuads[(int)Section.Background].Height =
m_size.Height – m_disabledQuads[(int)Section.Top].Height –
m_disabledQuads[(int)Section.Bottom].Height;

m_quads[(int)Section.Right].Height =
m_size.Height – m_quads[(int)Section.TopRight].Height –
m_quads[(int)Section.BottomRight].Height;
m_disabledQuads[(int)Section.Right].Height =
m_size.Height – m_disabledQuads[(int)Section.TopRight].Height –
m_disabledQuads[(int)Section.BottomRight].Height;

// Adjust bottom row Y
m_quads[(int)Section.BottomLeft].Y = m_quads[(int)Section.Left].Bottom;
m_quads[(int)Section.Bottom].Y = m_quads[(int)Section.Background].Bottom;
m_quads[(int)Section.BottomRight].Y = m_quads[(int)Section.Right].Bottom;
m_disabledQuads[(int)Section.BottomLeft].Y = m_disabledQuads[(int)Section.Left].Bottom;
m_disabledQuads[(int)Section.Bottom].Y = m_disabledQuads[(int)Section.Background].Bottom;
m_disabledQuads[(int)Section.BottomRight].Y = m_disabledQuads[(int)Section.Right].Bottom;

// Adjust cursor
m_cursor.Height = m_fontSize;
m_cursor.Width = 2f;

BuildHotspot();

// Place cursor at the end of the string
m_cursorPosition = m_text.Length;

BuildText();

}

///

Builds the text
protected override void BuildText() Toggle
{
int index = m_bFont.AddString( m_text, new RectangleF( m_quads[(int)Section.Background].X + 2f,
m_quads[(int)Section.Background].Y + 2f, m_quads[(int)Section.Background].Width,
m_quads[(int)Section.Background].Height ), BitmapFont.Align.Left, m_fontSize, m_textColor, true );
List fontQuads = m_bFont.GetProcessedQuads( index );
m_bFont.ClearString( index );

// Convert FontQuads to Quads
m_fontQuads = new List( fontQuads.Count );
for ( int i = 0; i < fontQuads.Count; i++ )
{

m_fontQuads.Add( new Quad( fontQuads[i].TopLeft, fontQuads[i].TopRight, fontQuads[i].BottomLeft, fontQuads[i].BottomRight ) );

}
if ( m_cursorPosition != m_text.Length )
{

m_cursor.X = fontQuads[m_cursorPosition].X – ( fontQuads[m_cursorPosition].BitmapCharacter.XOffset * fontQuads[m_cursorPosition].SizeScale );
m_cursor.Y = fontQuads[m_cursorPosition].Y – ( fontQuads[m_cursorPosition].BitmapCharacter.YOffset * fontQuads[m_cursorPosition].SizeScale );

}
else if ( m_text.Length > 0 )
{

m_cursor.X = fontQuads[m_cursorPosition – 1].X + ( fontQuads[m_cursorPosition – 1].BitmapCharacter.XAdvance * fontQuads[m_cursorPosition – 1].SizeScale );
m_cursor.Y = fontQuads[m_cursorPosition – 1].Y – ( fontQuads[m_cursorPosition – 1].BitmapCharacter.YOffset * fontQuads[m_cursorPosition – 1].SizeScale );

}
else if ( m_text.Length == 0 )
{

m_cursor.X = m_quads[(int)Section.Background].X + 2f;
m_cursor.Y = m_quads[(int)Section.Background].Y + 2f;

}

}

///

Builds the hotspot
private void BuildHotspot() Toggle
{
int hotspotWidth = (int)m_quads[(int)Section.Right].Right – (int)m_quads[(int)Section.Left].X;
int hotspotHeight = (int)m_quads[(int)Section.Bottom].Bottom – (int)m_quads[(int)Section.Top].Y;
m_hotspot = new Rectangle( (int)m_quads[(int)Section.TopLeft].X,
(int)m_quads[(int)Section.TopLeft].Y, hotspotWidth, hotspotHeight );

}

///

Gets and sets whether the Control is disabled.
public override bool Disabled Toggle
{
get
{
return base.Disabled;

}
set
{

base.Disabled = value;
if ( Disabled )
{
ReleaseFocus();

}

}

}

///

Gets the EditBox’s current Quads
public override List Quads Toggle

{

get
{

switch ( m_state )
{

case ControlState.Disabled:

return m_disabledQuads;

default:

return m_quads;

}

}

}

///

Gets and sets the Control’s state.
public override ControlState State Toggle
{
get
{
return base.State;

}
set
{

base.State = value;
if ( Disabled )
{
ReleaseFocus();

}

}

}

}

EditBoxes allow users to enter text into your program. To implement this functionality, EditBox has a cursor position, which is an index into the string. All new characters are entered into the string at the cursor position via the string.Insert method. When the user clicks on the EditBox, the cursor Quad is added to the Control’s List of Quads, and the EditBox gains the keyboards focus.

Most of the action occurs in OnKeyDown. The OnKeyDown method adds or subtracts characters from its string. It also allows for cursor movement and string manipulation using the left and right arrow keys, ctrl + the left or right arrow keys, home, end, delete, and backspace. Just about all of the logic is pretty straight forward. Once again, the BitmapFont class provides the added bonus of cropping and wrapping the text inside the EditBox rectangle.

The EditBox class has a max length field, which limits the amount of text you want displayed in the text box. While the BitmapFont class automatically crops and wraps text within a rectangle, the max length field can further restrict the amount of text that is displayed in the EditBox.

All the Controls are now implemented. In the next tutorial, you’ll see how to use the GUI.