Creating a GUI (Part 2)

  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 1 of the GUI tutorial series, we created the foundation classes of our GUI. In Part 2, we will create_ 6 types of Controls: text labels, panels, buttons, checkboxes, radio buttons, and sliders. First up is the Label class. As stated in Part 1, all controls inherit from the Control abstract class.

class Label : Control
{
private BitmapFont.Align m_alignment;
public Label( int id, RectangleF screenRect, string text, float fontSize, ColorValue textColor, BitmapFont font, BitmapFont.Align alignment ) Toggle
{
m_id = id;
m_text = 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_alignment = alignment;
m_hotspot = new Rectangle( (int)screenRect.X, (int)screenRect.Y, (int)screenRect.Width, (int)screenRect.Height );
BuildText();

}

///

Builds the text
protected override void BuildText() Toggle
{
int index = m_bFont.AddString( m_text, new RectangleF( m_position.X, m_position.Y,
m_size.Width, m_size.Height ), m_alignment, 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 ) );

}

}

///

Gets and sets the Control’s position
public override PointF Position Toggle
{
get { return base.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_fontQuads.Count; i++ )
{
m_fontQuads[i].X += xOffset;
m_fontQuads[i].Y += yOffset;

}

}

}

public override string Text Toggle

{
get
{
return base.Text;

}
set
{

m_text = value;
BuildText();

}

}

}

The Label class is the simplest Control. In fact, it doesn’t really do anything besides sit there and look pretty. All it has is a set of Quads generated by the BitmapFont class we created in the Bitmap Font tutorial. What’s the difference between a Label and using the BitmapFont class to render the text? Labels are useful if you have text that doesn’t change much. BitmapFont rebuilds its buffer everytime any text changes. Of course, GuiManager will rebuild its buffer if any of the Controls are updated, but you could always make a GuiManager that only has Labels.

Next up is the Panel class:

///

A Panel. Panels are moveable containers that can hold other Controls
public class Panel : Control
{
private int m_numControls;
private bool m_locked;
private float m_xOffset;
private float m_yOffset;
private enum Section { Left, Right, Top, Bottom, Background, TopLeft, TopRight, BottomLeft, BottomRight };
/// Creates a Panel
/// Control ID
/// Screen screenRect
/// Button size in pixels
/// Button text
public Panel( int id, RectangleF screenRect, ControlNode node, ImageInformation info ) Toggle
{
m_id = id;
m_locked = false;
m_numControls = 0;
m_quads = new List( 9 );
m_position = new PointF( screenRect.X, screenRect.Y );
m_size = new SizeF( screenRect.Width, screenRect.Height );

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

m_quads.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 == “LeftBorder” )
{

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

}

}

PositionQuads();

}

///

Mouse down event.
/// Mouse position.
/// Mouse buttons.
protected override void OnMouseDown( Point cursor, bool[] buttons ) Toggle
{
if ( m_locked )
{
return;

}
m_xOffset = cursor.X – m_touchDownPoint.X;
m_yOffset = cursor.Y – m_touchDownPoint.Y;
foreach ( Quad q in m_quads )
{

q.X += m_xOffset;
q.Y += m_yOffset;

}
m_hotspot.X += (int)m_xOffset;
m_hotspot.Y += (int)m_yOffset;
m_touchDownPoint.X += (int)m_xOffset;
m_touchDownPoint.Y += (int)m_yOffset;

}

///

Gets and sets the Panel’s position
public override PointF Position Toggle
{
get { return m_position; }
set
{
m_position = value;

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

m_quads[i].X = value.X;
m_quads[i].Y = value.Y;

}
PositionQuads();

}

}

///

Repositions all the quads
private void PositionQuads() Toggle
{
// Adjust middle column x
m_quads[(int)Section.Top].X += m_quads[(int)Section.TopLeft].Width;
m_quads[(int)Section.Background].X += m_quads[(int)Section.Left].Width;
m_quads[(int)Section.Bottom].X += m_quads[(int)Section.BottomLeft].Width;

// 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.Background].Width =
m_size.Width – m_quads[(int)Section.Left].Width –
m_quads[(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;

// Adjust right column X
m_quads[(int)Section.TopRight].X +=
m_quads[(int)Section.TopLeft].Width +
m_quads[(int)Section.Top].Width;
m_quads[(int)Section.Right].X +=
m_quads[(int)Section.Left].Width +
m_quads[(int)Section.Background].Width;
m_quads[(int)Section.BottomRight].X +=
m_quads[(int)Section.BottomLeft].Width +
m_quads[(int)Section.Bottom].Width;

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

// 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_quads[(int)Section.Background].Height =
m_size.Height – m_quads[(int)Section.Top].Height –
m_quads[(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;

// Adjust bottom row Y
m_quads[(int)Section.BottomLeft].Y +=
m_quads[(int)Section.TopLeft].Height +
m_quads[(int)Section.Left].Height;
m_quads[(int)Section.Bottom].Y +=
m_quads[(int)Section.Top].Height +
m_quads[(int)Section.Background].Height;
m_quads[(int)Section.BottomRight].Y +=
m_quads[(int)Section.TopRight].Height +
m_quads[(int)Section.Right].Height;

// Reposition hotspot
int hotspotWidth = (int)m_quads[(int)Section.Left].Width +
(int)m_quads[(int)Section.Background].Width +
(int)m_quads[(int)Section.Right].Width;
int hotspotHeight = (int)m_quads[(int)Section.Top].Height +
(int)m_quads[(int)Section.Background].Height +
(int)m_quads[(int)Section.Bottom].Height;
m_hotspot = new Rectangle( (int)m_quads[(int)Section.TopLeft].X,
(int)m_quads[(int)Section.TopLeft].Y, hotspotWidth, hotspotHeight );

}

///

Gets and sets the number of controls in the Panel.
public int NumControls Toggle
{
get { return m_numControls; }
set { m_numControls = value; }

}

///

Gets the x offset used to drag the Panel.
public float XOffset Toggle
{
get { return m_xOffset; }

}

///

Gets the y offset used to drag the Panel.
public float YOffset Toggle
{
get { return m_yOffset; }

}

///

Gets the y offset used to drag the Panel.
public bool Locked Toggle
{
get { return m_locked; }
set { m_locked = value; }

}

}

The code in the constructor of all the Controls will appear similar to each other. In all the contructors, we sort through the List of ControlNodes, which was read from the XML file, and put them in they’re corresponding Lists. Once the ControlNodes are in their Lists, they are all positioned properly in PositionQuads. PositionQuads is where we put together the jigsaw puzzle of Quads. This happens in all of the Controls. Once the puzzle is put together, we create_ the hotspots and text (if the Control has any).

The Panel class is used as a container for other Controls. Use Panels if you want to have draggable windows. If you just want the Panel, but don’t want it draggable, you can Lock the Panel. The only action a Panel performs is move itself around based on user input. However, Panels are useful when performing certain actions on Controls. For example, if you disable or move a Panel, all of it’s attached Controls will be disabled or moved.

Next are Buttons:

/// A button control
public class Button : Control
{
private List m_normalQuads;
private List m_overQuads;
private List m_downQuads;
private List m_disabledQuads;

private enum Section { Left, Right, Top, Bottom, Background, TopLeft, TopRight, BottomLeft, BottomRight };

///

Creates a Button
/// Control ID
/// Screen rectangle
/// Button text
/// Font size
/// Text color
/// Bitmap font
/// ControlNode from XML file
/// Texture ImageInformation
public Button( int id, RectangleF screenRect, string text, float fontSize, ColorValue textColor, BitmapFont font, ControlNode node, ImageInformation info ) Toggle
{
m_id = id;
m_text = 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_normalQuads = new List();
m_overQuads = new List();
m_downQuads = new List();
m_disabledQuads = new List();

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

m_normalQuads.Add( new Quad() );
m_overQuads.Add( new Quad() );
m_downQuads.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_normalQuads[(int)Section.TopLeft] = q;

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

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

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

m_downQuads[(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_normalQuads[(int)Section.Top] = q;

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

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

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

m_downQuads[(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_normalQuads[(int)Section.TopRight] = q;

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

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

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

m_downQuads[(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_normalQuads[(int)Section.Left] = q;

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

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

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

m_downQuads[(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_normalQuads[(int)Section.Background] = q;

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

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

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

m_downQuads[(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_normalQuads[(int)Section.Right] = q;

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

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

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

m_downQuads[(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_normalQuads[(int)Section.BottomLeft] = q;

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

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

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

m_downQuads[(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_normalQuads[(int)Section.Bottom] = q;

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

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

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

m_downQuads[(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_normalQuads[(int)Section.BottomRight] = q;

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

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

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

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

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

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

}

}

}

PositionQuads();

}

///

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

{

get
{

switch ( m_state )
{

case ControlState.Normal:

return m_normalQuads;

case ControlState.Over:

return m_overQuads;

case ControlState.Down:

return m_downQuads;

default:

return m_disabledQuads;

}

}

}

///

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_normalQuads[i].X += xOffset;
m_normalQuads[i].Y += yOffset;
m_overQuads[i].X += xOffset;
m_overQuads[i].Y += yOffset;
m_downQuads[i].X += xOffset;
m_downQuads[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;

}
BuildHotspot();

}

}

///

Repositions the hot spot and text
private void PositionQuads() Toggle
{
// Adjust middle column x
m_normalQuads[(int)Section.Top].X = m_normalQuads[(int)Section.TopLeft].Right;
m_overQuads[(int)Section.Top].X = m_overQuads[(int)Section.TopLeft].Right;
m_downQuads[(int)Section.Top].X = m_downQuads[(int)Section.TopLeft].Right;
m_disabledQuads[(int)Section.Top].X = m_disabledQuads[(int)Section.TopLeft].Right;

m_normalQuads[(int)Section.Background].X = m_normalQuads[(int)Section.Left].Right;
m_overQuads[(int)Section.Background].X = m_overQuads[(int)Section.Left].Right;
m_downQuads[(int)Section.Background].X = m_downQuads[(int)Section.Left].Right;
m_disabledQuads[(int)Section.Background].X = m_disabledQuads[(int)Section.Left].Right;

m_normalQuads[(int)Section.Bottom].X = m_normalQuads[(int)Section.BottomLeft].Right;
m_overQuads[(int)Section.Bottom].X = m_overQuads[(int)Section.BottomLeft].Right;
m_downQuads[(int)Section.Bottom].X = m_downQuads[(int)Section.BottomLeft].Right;
m_disabledQuads[(int)Section.Bottom].X = m_disabledQuads[(int)Section.BottomLeft].Right;

// Adjust middle column width
m_normalQuads[(int)Section.Top].Width =
m_size.Width – m_normalQuads[(int)Section.TopLeft].Width –
m_normalQuads[(int)Section.TopRight].Width;
m_overQuads[(int)Section.Top].Width =
m_size.Width – m_overQuads[(int)Section.TopLeft].Width –
m_overQuads[(int)Section.TopRight].Width;
m_downQuads[(int)Section.Top].Width =
m_size.Width – m_downQuads[(int)Section.TopLeft].Width –
m_downQuads[(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_normalQuads[(int)Section.Background].Width =
m_size.Width – m_normalQuads[(int)Section.Left].Width –
m_normalQuads[(int)Section.Right].Width;
m_overQuads[(int)Section.Background].Width =
m_size.Width – m_overQuads[(int)Section.Left].Width –
m_overQuads[(int)Section.Right].Width;
m_downQuads[(int)Section.Background].Width =
m_size.Width – m_downQuads[(int)Section.Left].Width –
m_downQuads[(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_normalQuads[(int)Section.Bottom].Width =
m_size.Width – m_normalQuads[(int)Section.BottomLeft].Width –
m_normalQuads[(int)Section.BottomLeft].Width;
m_overQuads[(int)Section.Bottom].Width =
m_size.Width – m_overQuads[(int)Section.BottomLeft].Width –
m_overQuads[(int)Section.BottomLeft].Width;
m_downQuads[(int)Section.Bottom].Width = m_size.Width –
m_downQuads[(int)Section.BottomLeft].Width –
m_downQuads[(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_normalQuads[(int)Section.TopRight].X = m_normalQuads[(int)Section.Top].Right;
m_overQuads[(int)Section.TopRight].X = m_overQuads[(int)Section.Top].Right;
m_downQuads[(int)Section.TopRight].X = m_downQuads[(int)Section.Top].Right;
m_disabledQuads[(int)Section.TopRight].X = m_disabledQuads[(int)Section.Top].Right;

m_normalQuads[(int)Section.Right].X = m_normalQuads[(int)Section.Background].Right;
m_overQuads[(int)Section.Right].X = m_overQuads[(int)Section.Background].Right;
m_downQuads[(int)Section.Right].X = m_downQuads[(int)Section.Background].Right;
m_disabledQuads[(int)Section.Right].X = m_disabledQuads[(int)Section.Background].Right;

m_normalQuads[(int)Section.BottomRight].X = m_normalQuads[(int)Section.Bottom].Right;
m_overQuads[(int)Section.BottomRight].X = m_overQuads[(int)Section.Bottom].Right;
m_downQuads[(int)Section.BottomRight].X = m_downQuads[(int)Section.Bottom].Right;
m_disabledQuads[(int)Section.BottomRight].X = m_disabledQuads[(int)Section.Bottom].Right;

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

m_normalQuads[(int)Section.Background].Y = m_normalQuads[(int)Section.Top].Bottom;
m_overQuads[(int)Section.Background].Y = m_overQuads[(int)Section.Top].Bottom;
m_downQuads[(int)Section.Background].Y = m_downQuads[(int)Section.Top].Bottom;
m_disabledQuads[(int)Section.Background].Y = m_disabledQuads[(int)Section.Top].Bottom;

m_normalQuads[(int)Section.Right].Y = m_normalQuads[(int)Section.TopRight].Bottom;
m_overQuads[(int)Section.Right].Y = m_overQuads[(int)Section.TopRight].Bottom;
m_downQuads[(int)Section.Right].Y = m_downQuads[(int)Section.TopRight].Bottom;
m_disabledQuads[(int)Section.Right].Y = m_disabledQuads[(int)Section.TopRight].Bottom;

// Adjust middle row height
m_normalQuads[(int)Section.Left].Height =
m_size.Height – m_normalQuads[(int)Section.TopLeft].Height –
m_normalQuads[(int)Section.BottomLeft].Height;
m_overQuads[(int)Section.Left].Height =
m_size.Height – m_overQuads[(int)Section.TopLeft].Height –
m_overQuads[(int)Section.BottomLeft].Height;
m_downQuads[(int)Section.Left].Height =
m_size.Height – m_downQuads[(int)Section.TopLeft].Height –
m_downQuads[(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_normalQuads[(int)Section.Background].Height =
m_size.Height – m_normalQuads[(int)Section.Top].Height –
m_normalQuads[(int)Section.Bottom].Height;
m_overQuads[(int)Section.Background].Height =
m_size.Height – m_overQuads[(int)Section.Top].Height –
m_overQuads[(int)Section.Bottom].Height;
m_downQuads[(int)Section.Background].Height =
m_size.Height – m_downQuads[(int)Section.Top].Height –
m_downQuads[(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_normalQuads[(int)Section.Right].Height =
m_size.Height – m_normalQuads[(int)Section.TopRight].Height –
m_normalQuads[(int)Section.BottomRight].Height;
m_overQuads[(int)Section.Right].Height =
m_size.Height – m_overQuads[(int)Section.TopRight].Height –
m_overQuads[(int)Section.BottomRight].Height;
m_downQuads[(int)Section.Right].Height = m_size.Height –
m_downQuads[(int)Section.TopRight].Height –
m_downQuads[(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_normalQuads[(int)Section.BottomLeft].Y = m_normalQuads[(int)Section.Left].Bottom;
m_overQuads[(int)Section.BottomLeft].Y = m_overQuads[(int)Section.Left].Bottom;
m_downQuads[(int)Section.BottomLeft].Y = m_downQuads[(int)Section.Left].Bottom;
m_disabledQuads[(int)Section.BottomLeft].Y = m_disabledQuads[(int)Section.Left].Bottom;

m_normalQuads[(int)Section.Bottom].Y = m_normalQuads[(int)Section.Background].Bottom;
m_overQuads[(int)Section.Bottom].Y = m_overQuads[(int)Section.Background].Bottom;
m_downQuads[(int)Section.Bottom].Y = m_downQuads[(int)Section.Background].Bottom;
m_disabledQuads[(int)Section.Bottom].Y = m_disabledQuads[(int)Section.Background].Bottom;

m_normalQuads[(int)Section.BottomRight].Y = m_normalQuads[(int)Section.Right].Bottom;
m_overQuads[(int)Section.BottomRight].Y = m_overQuads[(int)Section.Right].Bottom;
m_downQuads[(int)Section.BottomRight].Y = m_downQuads[(int)Section.Right].Bottom;
m_disabledQuads[(int)Section.BottomRight].Y = m_disabledQuads[(int)Section.Right].Bottom;

BuildHotspot();

BuildText();

}

///

Builds the text
protected override void BuildText() Toggle
{
int index = m_bFont.AddString( m_text, new RectangleF( (float)m_hotspot.X,
(float)m_hotspot.Y + ( (float)m_hotspot.Height / 2f ) – ( m_fontSize / 2f ),
m_hotspot.Width, m_hotspot.Height ), BitmapFont.Align.Center, 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 ) );

}

}

///

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

}

}

Buttons, like all other Controls, have the same initialization code of reading in the Quad pieces, putting the pieces together in PositionQuads and building its hotspot(s) and text. The hotspot for a Button is the entire Button. When we build the text, we use the BitmapFont class we created in a previous tutorial to generate a bunch of Quads, which forms the text centered over the button. The Control initialization code thus far is consistent with all other Controls. In all the Controls we sort through the ControlNodes to get our Quad pieces, we put together our Quad puzzle in PositionQuads, we build the hotspot(s) with BuildHotSpot, and we build the Control’s text with BuildText. I’ve said that twice in the same paragraph now, so now you shouldn’t be confused when you see it in all the other Controls 🙂

What makes the Button a Button is that it returns a different set of Quads depending on the state is in (normal, over, down, or disabled). Remember, the base Control class is the one responsible for sending the Control’s data. Buttons don’t really have data, they just send an event when they are clicked. So all the Button class really does is change the Button graphic.

Next up is a CheckBox.

/// A CheckBox control
public class CheckBox : Control
{
protected List m_normalQuads;
protected List m_overQuads;
protected List m_downQuads;
protected List m_disabledQuads;
protected Quad m_marker;

protected enum Section { Left, Right, Top, Bottom, Background, TopLeft, TopRight, BottomLeft, BottomRight };

///

Default Constructor
public CheckBox() Toggle
{
// Empty

}

///

Creates a CheckBox
/// Control ID
/// Screen rectangle
/// CheckBox text
/// Font size
/// Text color
/// Text alignment
/// Whether the checkbox is initially checked
/// Initialized CUnit.BitmapFont instance
/// ControlNode from XML file
/// Texture ImageInformation
public CheckBox( int id, RectangleF screenRect, string text, float fontSize, ColorValue textColor, TextAlign textAlign, bool isChecked, BitmapFont font, ControlNode node, ImageInformation info ) Toggle
{
m_id = id;
m_text = text;
m_textAlign = textAlign;
m_fontSize = fontSize;
m_textColor = textColor;
m_bFont = font;
m_data = isChecked;
m_position = new PointF( screenRect.X, screenRect.Y );
m_hotspot = new Rectangle( (int)screenRect.X, (int)screenRect.Y, (int)screenRect.Width, (int)screenRect.Height );

m_normalQuads = new List();
m_overQuads = new List();
m_downQuads = new List();
m_disabledQuads = new List();

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.Right, screenRect.Y, z, rhw,
i.Color, rect.Right / (float)info.Width, rect.Y / (float)info.Height );
TransformedColoredTextured bottomRight = new TransformedColoredTextured(
screenRect.Right, screenRect.Bottom, z, rhw,
i.Color, rect.Right / (float)info.Width, rect.Bottom / (float)info.Height );
TransformedColoredTextured bottomLeft = new TransformedColoredTextured(
screenRect.X, screenRect.Bottom, z, rhw,
i.Color, rect.X / (float)info.Width, rect.Bottom / (float)info.Height );

if ( i.Name == “Normal” )
{

m_normalQuads.Add( new Quad( topLeft, topRight, bottomLeft, bottomRight ) );

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

m_overQuads.Add( new Quad( topLeft, topRight, bottomLeft, bottomRight ) );

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

m_downQuads.Add( new Quad( topLeft, topRight, bottomLeft, bottomRight ) );

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

m_disabledQuads.Add( new Quad( topLeft, topRight, bottomLeft, bottomRight ) );

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

m_marker = new Quad( topLeft, topRight, bottomLeft, bottomRight );

}

}
if ( isChecked )
{

// Add checkmark
m_normalQuads.Add( (Quad)m_marker.Clone() );
m_overQuads.Add( (Quad)m_marker.Clone() );
m_downQuads.Add( (Quad)m_marker.Clone() );
m_disabledQuads.Add( (Quad)m_marker.Clone() );

}

BuildText();

}

///

Mouse Release event
/// Mouse position
protected override void OnMouseRelease( Point cursor ) Toggle
{
m_data = !(bool)m_data;
if ( (bool)m_data == true )
{
// Add checkmark
m_normalQuads.Add( (Quad)m_marker.Clone() );
m_overQuads.Add( (Quad)m_marker.Clone() );
m_downQuads.Add( (Quad)m_marker.Clone() );
m_disabledQuads.Add( (Quad)m_marker.Clone() );

}
else
{

// Remove checkmark
m_normalQuads.RemoveAt( 1 );
m_overQuads.RemoveAt( 1 );
m_downQuads.RemoveAt( 1 );
m_disabledQuads.RemoveAt( 1 );

}

}

///

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

{

get
{

switch ( m_state )
{

case ControlState.Normal:

return m_normalQuads;

case ControlState.Over:

return m_overQuads;

case ControlState.Down:

return m_downQuads;

default:

return m_disabledQuads;

}

}

}

///

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;

// Reposition Quads
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_downQuads[i].X += xOffset;
m_downQuads[i].Y += yOffset;
m_disabledQuads[i].X += xOffset;
m_disabledQuads[i].Y += yOffset;

}
m_marker.X += xOffset;
m_marker.Y += yOffset;

// Reposition hotspot
m_hotspot = new Rectangle( (int)m_normalQuads[0].X, (int)m_normalQuads[0].Y, (int)m_normalQuads[0].Width, (int)m_normalQuads[0].Height );

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

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

}

}

}

///

Builds the Control text
protected override void BuildText() Toggle
{
// Create text
BitmapFont.Align alignment = BitmapFont.Align.Left;
switch ( m_textAlign )
{
case TextAlign.Left:

{

float x = m_normalQuads[0].X – 1000f;
float y = m_normalQuads[0].Y + ( m_normalQuads[0].Height / 2f ) – ( m_fontSize / 2f );
float width = 1000f – m_fontPadding;
float height = m_fontSize * 2f;
m_textRect = new RectangleF( x, y, width, height );
alignment = BitmapFont.Align.Right;
break;

}
case TextAlign.Right:

{
float x = m_normalQuads[0].Right + m_fontPadding;
float y = m_normalQuads[0].Y + ( m_normalQuads[0].Height / 2f ) – ( m_fontSize / 2f );
float width = 1000f;
float height = m_fontSize * 2f;
m_textRect = new RectangleF( x, y, width, height );
alignment = BitmapFont.Align.Left;
break;

}
case TextAlign.Top:

{
float x = m_normalQuads[0].X – 500f;
float y = m_normalQuads[0].Y – m_fontPadding – m_fontSize;
float width = 1000f + m_normalQuads[0].Width;
float height = m_fontSize * 2f;
m_textRect = new RectangleF( x, y, width, height );
alignment = BitmapFont.Align.Center;
break;

}
case TextAlign.Bottom:

{
float x = m_normalQuads[0].X – 500f;
float y = m_normalQuads[0].Bottom + m_fontPadding;
float width = 1000f + m_normalQuads[0].Width;
float height = m_fontSize * 2f;
m_textRect = new RectangleF( x, y, width, height );
alignment = BitmapFont.Align.Center;
break;

}
case TextAlign.Center:

{
float x = m_normalQuads[0].X – 500f;
float y = m_normalQuads[0].Y + ( m_normalQuads[0].Height / 2f ) – ( m_fontSize / 2f );
float width = 1000f + m_normalQuads[0].Width;
float height = m_fontSize * 2f;
m_textRect = new RectangleF( x, y, width, height );
alignment = BitmapFont.Align.Center;
break;

}

}
int index = m_bFont.AddString( m_text, m_textRect, alignment, 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 ) );

}

}

///

Gets and sets whether the Control is checked or not.
public virtual bool Checked Toggle
{
get { return (bool)m_data; }
set
{
if ( (bool)m_data == false && value == true )
{
// Add checkmark
m_normalQuads.Add( (Quad)m_marker.Clone() );
m_overQuads.Add( (Quad)m_marker.Clone() );
m_downQuads.Add( (Quad)m_marker.Clone() );
m_disabledQuads.Add( (Quad)m_marker.Clone() );

}
else if ( (bool)m_data == true && value == false )
{

// Remove checkmark
m_normalQuads.RemoveAt( 1 );
m_overQuads.RemoveAt( 1 );
m_downQuads.RemoveAt( 1 );
m_disabledQuads.RemoveAt( 1 );

}
m_data = value;

}

}

}

The CheckBox, contains the same initialization code as the Button, except the CheckBox only has 2 Quad pieces: the background and the check mark. When we click on a CheckBox we add the Quad that has the check mark onto the list of Quads (or remove it from the List). We also toggle the CheckBox’s data between true and false depending on whether the CheckBox is toggled.

/// A RadioButton control
public class RadioButton : CheckBox
{
private int m_groupID;
private bool m_needToDeselectOthers;

///

Creates a RadioButton
/// Control ID
/// GroupID to associate with RadioButton
/// Screen rectangle
/// CheckBox text
/// Font size
/// Text color
/// Text alignment
/// Whether the RadioButton is initially selected.
/// Initialized CUnit.BitmapFont instance
/// ControlNode from XML file
/// Texture ImageInformation
public RadioButton( int id, int groupID, RectangleF screenRect, string text, float fontSize, ColorValue textColor, TextAlign textAlign, bool isSelected, BitmapFont font, ControlNode node, ImageInformation info ) Toggle
{
m_id = id;
m_groupID = groupID;
m_needToDeselectOthers = isSelected;
m_text = text;
m_textAlign = textAlign;
m_fontSize = fontSize;
m_textColor = textColor;
m_bFont = font;
m_data = isSelected;
m_position = new PointF( screenRect.X, screenRect.Y );
m_hotspot = new Rectangle( (int)screenRect.X, (int)screenRect.Y, (int)screenRect.Width, (int)screenRect.Height );

m_normalQuads = new List();
m_overQuads = new List();
m_downQuads = new List();
m_disabledQuads = new List();

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.Right, screenRect.Y, z, rhw,
i.Color, rect.Right / (float)info.Width, rect.Y / (float)info.Height );
TransformedColoredTextured bottomRight = new TransformedColoredTextured(
screenRect.Right, screenRect.Bottom, z, rhw,
i.Color, rect.Right / (float)info.Width, rect.Bottom / (float)info.Height );
TransformedColoredTextured bottomLeft = new TransformedColoredTextured(
screenRect.X, screenRect.Bottom, z, rhw,
i.Color, rect.X / (float)info.Width, rect.Bottom / (float)info.Height );
if ( i.Name == “Normal” )
{
m_normalQuads.Add( new Quad( topLeft, topRight, bottomLeft, bottomRight ) );

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

m_overQuads.Add( new Quad( topLeft, topRight, bottomLeft, bottomRight ) );

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

m_downQuads.Add( new Quad( topLeft, topRight, bottomLeft, bottomRight ) );

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

m_disabledQuads.Add( new Quad( topLeft, topRight, bottomLeft, bottomRight ) );

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

m_marker = new Quad( topLeft, topRight, bottomLeft, bottomRight );

}

}
if ( isSelected )
{

// Add checkmark
m_normalQuads.Add( (Quad)m_marker.Clone() );
m_overQuads.Add( (Quad)m_marker.Clone() );
m_downQuads.Add( (Quad)m_marker.Clone() );
m_disabledQuads.Add( (Quad)m_marker.Clone() );

}

BuildText();

}

///

Mouse Release event
/// Mouse position
protected override void OnMouseRelease( Point cursor ) Toggle
{
m_data = true;
if ( (bool)m_data == true && ( m_normalQuads.Count < 2 ) )
{
m_needToDeselectOthers = true;

// Add radio mark
m_normalQuads.Add( (Quad)m_marker.Clone() );
m_overQuads.Add( (Quad)m_marker.Clone() );
m_downQuads.Add( (Quad)m_marker.Clone() );
m_disabledQuads.Add( (Quad)m_marker.Clone() );

}

}

///

Deselects the RadioButton.
public void Deselect() Toggle
{
m_data = false;
if ( m_normalQuads.Count > 1 )
{
m_needToDeselectOthers = false;

// Remove checkmark
m_normalQuads.RemoveAt( 1 );
m_overQuads.RemoveAt( 1 );
m_downQuads.RemoveAt( 1 );
m_disabledQuads.RemoveAt( 1 );

}

}

///

Gets the group id.
public int GroupID Toggle
{
get { return m_groupID; }

}

///

Gets and sets whether the GuiManager needs to
/// deselect the other RadioButtons in this groupID.

public bool NeedToDelectOthers Toggle
{
get { return m_needToDeselectOthers; }
set { m_needToDeselectOthers = value; }

}

}

The RadioButton is basically a CheckBox with a groupID. As a result, RadioButton inherits from CheckBox. The only differences are that only one RadioButton per groupID may be checked at a time and we cannot deselect a radio button.

/// A Slider control
public class Slider : Control
{
private List m_normalQuads;
private List m_overQuads;
private List m_downQuads;
private List m_disabledQuads;
private List m_hotspots;
private float m_min;
private float m_max;

private enum Section { Left, Right, Middle, Marker };

///

Creates a Button
/// Control ID
/// Screen rectangle
/// Button text
/// ControlNode from XML file
/// Texture ImageInformation
public Slider( int id, PointF position, float width, float min, float max, float current, string text, float fontSize, ColorValue textColor, Control.TextAlign textAlignment, BitmapFont font, ControlNode node, ImageInformation info ) Toggle
{
if ( max <= min )
{
throw new Exception( “Slider max must be greater than Slider min.” );

}

m_id = id;
m_text = text;
m_fontSize = fontSize;
m_textColor = textColor;
m_bFont = font;
m_textAlign = textAlignment;
m_position = new PointF( position.X, position.Y );

m_min = min;
m_max = max;
m_data = Math.Max( current, min );
m_data = Math.Min( (float)m_data, max );

m_normalQuads = new List();
m_overQuads = new List();
m_downQuads = new List();
m_disabledQuads = new List();
m_hotspots = new List();

// Initialize Lists so we can access them with indices
m_hotspots.Add( new Rectangle() );
m_hotspots.Add( new Rectangle() );

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

m_normalQuads.Add( new Quad() );
m_overQuads.Add( new Quad() );
m_downQuads.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(
position.X, position.Y, z, rhw,
i.Color, rect.X / (float)info.Width, rect.Y / (float)info.Height );
TransformedColoredTextured topRight = new TransformedColoredTextured(
position.X + rect.Width, position.Y, z, rhw,
i.Color, rect.Right / (float)info.Width, rect.Y / (float)info.Height );
TransformedColoredTextured bottomRight = new TransformedColoredTextured(
position.X + rect.Width, position.Y + rect.Height, z, rhw,
i.Color, rect.Right / (float)info.Width, rect.Bottom / (float)info.Height );
TransformedColoredTextured bottomLeft = new TransformedColoredTextured(
position.X, position.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( “LeftCap” ) )
{

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

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

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

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

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

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

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

}

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

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

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

m_overQuads[(int)Section.Middle] = q;

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

m_downQuads[(int)Section.Middle] = q;

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

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

}

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

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

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

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

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

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

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

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

}

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

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

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

m_overQuads[(int)Section.Marker] = q;

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

m_downQuads[(int)Section.Marker] = q;

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

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

}

}

}
m_size = new SizeF( width, m_normalQuads[(int)Section.Middle].Height );
PositionQuads();

}

///

Updates the current value based on the slider’s position.
private void ScreenXToCurrentValue() Toggle
{
float screenRange = m_normalQuads[(int)Section.Middle].Width – m_normalQuads[(int)Section.Marker].Width;
m_data = ( ( m_max – m_min ) * ( m_normalQuads[(int)Section.Marker].X – m_normalQuads[(int)Section.Middle].X ) ) / screenRange + m_min;

}

///

Updates the slider’s position based on the current value.
private void CurrentValueToScreenX() Toggle
{
float screenRange = m_normalQuads[(int)Section.Middle].Width – m_normalQuads[(int)Section.Marker].Width;
m_normalQuads[(int)Section.Marker].X =
( ( ( (float)m_data – m_min ) / ( m_max – m_min ) ) * screenRange ) + m_normalQuads[(int)Section.Middle].X;
m_overQuads[(int)Section.Marker].X = m_normalQuads[(int)Section.Marker].X;
m_downQuads[(int)Section.Marker].X = m_normalQuads[(int)Section.Marker].X;
m_disabledQuads[(int)Section.Marker].X = m_normalQuads[(int)Section.Marker].X;

}

///

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;

}

}
return false;

}

///

Mouse Down event
/// Mouse position
/// Mouse buttons
protected override void OnMouseDown( Point cursor, bool[] buttons ) Toggle
{
// Move slider
m_normalQuads[(int)Section.Marker].X = (float)cursor.X – ( m_normalQuads[(int)Section.Marker].Width / 2f );
m_overQuads[(int)Section.Marker].X = m_normalQuads[(int)Section.Marker].X;
m_downQuads[(int)Section.Marker].X = m_normalQuads[(int)Section.Marker].X;
m_disabledQuads[(int)Section.Marker].X = m_normalQuads[(int)Section.Marker].X;

// Constrain slider
float screenRange = m_normalQuads[(int)Section.Middle].Width – m_normalQuads[(int)Section.Marker].Width;
float max = screenRange + m_normalQuads[(int)Section.Middle].X;
float min = m_normalQuads[(int)Section.Middle].X;

if ( m_normalQuads[(int)Section.Marker].X > max )
{

m_normalQuads[(int)Section.Marker].X = max;
m_overQuads[(int)Section.Marker].X = max;
m_downQuads[(int)Section.Marker].X = max;
m_disabledQuads[(int)Section.Marker].X = max;

}
if ( m_normalQuads[(int)Section.Marker].X < min )
{

m_normalQuads[(int)Section.Marker].X = min;
m_overQuads[(int)Section.Marker].X = min;
m_downQuads[(int)Section.Marker].X = min;
m_disabledQuads[(int)Section.Marker].X = min;

}
ScreenXToCurrentValue();
BuildHotspots();

}

///

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

{

get
{

switch ( m_state )
{

case ControlState.Normal:

return m_normalQuads;

case ControlState.Over:

return m_overQuads;

case ControlState.Down:

return m_downQuads;

default:

return m_disabledQuads;

}

}

}

///

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 < 4; i++ )
{

m_normalQuads[i].X += xOffset;
m_normalQuads[i].Y += yOffset;
m_overQuads[i].X += xOffset;
m_overQuads[i].Y += yOffset;
m_downQuads[i].X += xOffset;
m_downQuads[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;

}
BuildHotspots();

}

}

///

Repositions the hot spot and text
private void PositionQuads() Toggle
{
// Adjust middle x
m_normalQuads[(int)Section.Middle].X = m_normalQuads[(int)Section.Left].Right;
m_overQuads[(int)Section.Middle].X = m_overQuads[(int)Section.Left].Right;
m_downQuads[(int)Section.Middle].X = m_downQuads[(int)Section.Left].Right;
m_disabledQuads[(int)Section.Middle].X = m_disabledQuads[(int)Section.Left].Right;

// Adjust middle width
m_normalQuads[(int)Section.Middle].Width =
m_size.Width – m_normalQuads[(int)Section.Left].Width –
m_normalQuads[(int)Section.Right].Width;
m_overQuads[(int)Section.Middle].Width =
m_size.Width – m_overQuads[(int)Section.Left].Width –
m_overQuads[(int)Section.Right].Width;
m_downQuads[(int)Section.Middle].Width =
m_size.Width – m_downQuads[(int)Section.Left].Width –
m_downQuads[(int)Section.Right].Width;
m_disabledQuads[(int)Section.Middle].Width =
m_size.Width – m_disabledQuads[(int)Section.Left].Width –
m_disabledQuads[(int)Section.Right].Width;

// Adjust right X
m_normalQuads[(int)Section.Right].X = m_normalQuads[(int)Section.Middle].Right;
m_overQuads[(int)Section.Right].X = m_overQuads[(int)Section.Middle].Right;
m_downQuads[(int)Section.Right].X = m_downQuads[(int)Section.Middle].Right;
m_disabledQuads[(int)Section.Right].X = m_disabledQuads[(int)Section.Middle].Right;

// Adjust marker Y
m_normalQuads[(int)Section.Marker].Y -=
( m_normalQuads[(int)Section.Marker].Height – m_normalQuads[(int)Section.Middle].Height ) / 2;
m_overQuads[(int)Section.Marker].Y -=
( m_overQuads[(int)Section.Marker].Height – m_overQuads[(int)Section.Middle].Height ) / 2;
m_downQuads[(int)Section.Marker].Y -=
( m_downQuads[(int)Section.Marker].Height – m_downQuads[(int)Section.Middle].Height ) / 2;
m_disabledQuads[(int)Section.Marker].Y -=
( m_disabledQuads[(int)Section.Marker].Height – m_disabledQuads[(int)Section.Middle].Height ) / 2;

// Adjust marker
CurrentValueToScreenX();

BuildHotspots();

BuildText();

}

///

Builds the hotspots
private void BuildHotspots() Toggle
{
// Reposition hotspots
// Marker is a hotspot
m_hotspots[0] = new Rectangle(
(int)m_normalQuads[(int)Section.Marker].X,
(int)m_normalQuads[(int)Section.Marker].Y,
(int)m_normalQuads[(int)Section.Marker].Width,
(int)m_normalQuads[(int)Section.Marker].Height );

// Slider is a hotspot with a pixel buffer around top and bottom
m_hotspots[1] = new Rectangle(
(int)m_normalQuads[(int)Section.Left].X,
(int)m_normalQuads[(int)Section.Middle].Y – 2,
(int)m_size.Width,
(int)m_normalQuads[(int)Section.Middle].Height + 2 );

}

///

Builds the text
protected override void BuildText() Toggle
{
// Create text
BitmapFont.Align alignment = BitmapFont.Align.Left;
switch ( m_textAlign )
{
case TextAlign.Left:
{
float x = m_normalQuads[(int)Section.Left].X – 1000f;
float y = m_normalQuads[(int)Section.Left].Y +
( m_normalQuads[(int)Section.Middle].Height / 2f ) – ( m_fontSize / 2f );
float width = 1000f – m_fontPadding;
float height = m_fontSize * 2f;
m_textRect = new RectangleF( x, y, width, height );
alignment = BitmapFont.Align.Right;
break;
}
case TextAlign.Right:
{
float x = m_normalQuads[(int)Section.Right].Right + m_fontPadding;
float y = m_normalQuads[(int)Section.Left].Y +
( m_normalQuads[(int)Section.Middle].Height / 2f ) – ( m_fontSize / 2f );
float width = 1000f;
float height = m_fontSize * 2f;
m_textRect = new RectangleF( x, y, width, height );
alignment = BitmapFont.Align.Left;
break;
}
case TextAlign.Top:
{
float x = m_normalQuads[(int)Section.Middle].X – 500f;
float y = m_normalQuads[(int)Section.Marker].Y – m_fontPadding – m_fontSize;
float width = 1000f + m_normalQuads[(int)Section.Middle].Width;
float height = m_fontSize * 2f;
m_textRect = new RectangleF( x, y, width, height );
alignment = BitmapFont.Align.Center;
break;
}
case TextAlign.Bottom:
{
float x = m_normalQuads[(int)Section.Middle].X – 500f;
float y = m_normalQuads[(int)Section.Marker].Bottom + m_fontPadding;
float width = 1000f + m_normalQuads[(int)Section.Middle].Width;
float height = m_fontSize * 2f;
m_textRect = new RectangleF( x, y, width, height );
alignment = BitmapFont.Align.Center;
break;
}
case TextAlign.Center:
{
float x = m_normalQuads[(int)Section.Middle].X – 500f;
float y = m_normalQuads[(int)Section.Middle].Y +
( m_normalQuads[(int)Section.Middle].Height / 2f ) – ( m_fontSize / 2f );
float width = 1000f + m_normalQuads[(int)Section.Middle].Width;
float height = m_fontSize * 2f;
m_textRect = new RectangleF( x, y, width, height );
alignment = BitmapFont.Align.Center;
break;
}
}
int index = m_bFont.AddString( m_text, m_textRect, alignment, 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 ) );
}
}

}

The Slider has Quad pieces for the left and right ends of the slider, the body of the slider, and the slider handle. When we create_ a Slider, we specify the minimum Slider value, the maximum Slider value, and the current Slider value. When we’re moving the slider in the OnMouseDown method, we have to constrain the slider to stay within the bounds of its track.

The ScreenXToCurrentValue method calculates the current value of the Slider based on the position of the Slider handle. So whenever we drag the Slider handle, this method is called to calculate a new Slider value. The opposite of ScreenXToCurrentValue is CurrentValueToScreenX which will place the Slider handle depending on the current value of the Slider. This is useful if we programmatically change current value of the Slider.

The controls covered in Part 2 are the simpler controls of our GUI system. In Part 3, we will create_ the more difficult controls: ComboBox, ListBox, and EditBox.