CU_MDX_GUI.zip (63.8 KiB, 5,736 hits)
CUnitFramework.zip (101.1 KiB, 14,846 hits)
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.
{
public Label( int id, RectangleF screenRect, string text, float fontSize, ColorValue textColor, BitmapFont font, BitmapFont.Align alignment )
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();
}
/// <summary>Builds the text</summary>
protected override void BuildText() 
m_size.Width, m_size.Height ), m_alignment, m_fontSize, m_textColor, true );
List<FontQuad> fontQuads = m_bFont.GetProcessedQuads( index );
m_bFont.ClearString( index );
// Convert FontQuads to Quads
m_fontQuads = new List<Quad>( fontQuads.Count );
for ( int i = 0; i < fontQuads.Count; i++ )
{
}
}
/// <summary>Gets and sets the Control’s position</summary>
public override PointF Position 
set
{
float yOffset = value.Y – m_position.Y;
m_position = value;
for ( int i = 0; i < m_fontQuads.Count; i++ )
{
m_fontQuads[i].Y += yOffset;
}
}
}
public override string Text 
{
}
set
{
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:
/// <summary>A Panel. Panels are moveable containers that can hold other Controls</summary>
public class Panel : Control
{
private bool m_locked;
private float m_xOffset;
private float m_yOffset;
private enum Section { Left, Right, Top, Bottom, Background, TopLeft, TopRight, BottomLeft, BottomRight };
/// <summary>Creates a Panel</summary>
/// <param name=”id”>Control ID</param>
/// <param name=”screenRect”>Screen screenRect</param>
/// <param name=”size”>Button size in pixels</param>
/// <param name=”text”>Button text</param>
public Panel( int id, RectangleF screenRect, ControlNode node, ImageInformation info )
m_locked = false;
m_numControls = 0;
m_quads = new List<Quad>( 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++ )
{
}
float z = 0f;
float rhw = 1f;
foreach ( ImageNode i in node.Images )
{
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” )
{
}
else if ( i.Name == “TopBorder” )
{
}
else if ( i.Name == “RightBorder” )
{
}
else if ( i.Name == “BottomBorder” )
{
}
else if ( i.Name == “Background” )
{
}
else if ( i.Name == “TopLeftCorner” )
{
}
else if ( i.Name == “TopRightCorner” )
{
}
else if ( i.Name == “BottomLeftCorner” )
{
}
else if ( i.Name == “BottomRightCorner” )
{
}
}
PositionQuads();
}
/// <summary>Mouse down event.</summary>
/// <param name=”cursor”>Mouse position.</param>
/// <param name=”buttons”>Mouse buttons.</param>
protected override void OnMouseDown( Point cursor, bool[] buttons ) 
{
}
m_xOffset = cursor.X – m_touchDownPoint.X;
m_yOffset = cursor.Y – m_touchDownPoint.Y;
foreach ( Quad q in m_quads )
{
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;
}
/// <summary>Gets and sets the Panel’s position</summary>
public override PointF Position 
set
{
for ( int i = 0; i < 9; i++ )
{
m_quads[i].Y = value.Y;
}
PositionQuads();
}
}
/// <summary>Repositions all the quads</summary>
private void PositionQuads() 
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 );
}
/// <summary>Gets and sets the number of controls in the Panel.</summary>
public int NumControls 
set { m_numControls = value; }
}
/// <summary>Gets the x offset used to drag the Panel.</summary>
public float XOffset 
}
/// <summary>Gets the y offset used to drag the Panel.</summary>
public float YOffset 
}
/// <summary>Gets the y offset used to drag the Panel.</summary>
public bool 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:
public class Button : Control
{
private List<Quad> m_overQuads;
private List<Quad> m_downQuads;
private List<Quad> m_disabledQuads;
private enum Section { Left, Right, Top, Bottom, Background, TopLeft, TopRight, BottomLeft, BottomRight };
/// <summary>Creates a Button</summary>
/// <param name=”id”>Control ID</param>
/// <param name=”screenRect”>Screen rectangle</param>
/// <param name=”text”>Button text</param>
/// <param name=”fontSize”>Font size</param>
/// <param name=”textColor”>Text color</param>
/// <param name=”font”>Bitmap font</param>
/// <param name=”node”>ControlNode from XML file</param>
/// <param name=”info”>Texture ImageInformation</param>
public Button( int id, RectangleF screenRect, string text, float fontSize, ColorValue textColor, BitmapFont font, ControlNode node, ImageInformation info ) 
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<Quad>();
m_overQuads = new List<Quad>();
m_downQuads = new List<Quad>();
m_disabledQuads = new List<Quad>();
// Initialize Lists so we can access them with indices
for ( int i = 0; i < 9; i++ )
{
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 )
{
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” ) )
{
{
}
else if ( i.Name.StartsWith( “Over” ) )
{
}
else if ( i.Name.StartsWith( “Down” ) )
{
}
else if ( i.Name.StartsWith( “Disabled” ) )
{
}
}
else if ( i.Name.EndsWith( “TopBorder” ) )
{
{
}
else if ( i.Name.StartsWith( “Over” ) )
{
}
else if ( i.Name.StartsWith( “Down” ) )
{
}
else if ( i.Name.StartsWith( “Disabled” ) )
{
}
}
else if ( i.Name.EndsWith( “TopRightCorner” ) )
{
{
}
else if ( i.Name.StartsWith( “Over” ) )
{
}
else if ( i.Name.StartsWith( “Down” ) )
{
}
else if ( i.Name.StartsWith( “Disabled” ) )
{
}
}
else if ( i.Name.EndsWith( “LeftBorder” ) )
{
{
}
else if ( i.Name.StartsWith( “Over” ) )
{
}
else if ( i.Name.StartsWith( “Down” ) )
{
}
else if ( i.Name.StartsWith( “Disabled” ) )
{
}
}
else if ( i.Name.EndsWith( “Background” ) )
{
{
}
else if ( i.Name.StartsWith( “Over” ) )
{
}
else if ( i.Name.StartsWith( “Down” ) )
{
}
else if ( i.Name.StartsWith( “Disabled” ) )
{
}
}
else if ( i.Name.EndsWith( “RightBorder” ) )
{
{
}
else if ( i.Name.StartsWith( “Over” ) )
{
}
else if ( i.Name.StartsWith( “Down” ) )
{
}
else if ( i.Name.StartsWith( “Disabled” ) )
{
}
}
else if ( i.Name.EndsWith( “BottomLeftCorner” ) )
{
{
}
else if ( i.Name.StartsWith( “Over” ) )
{
}
else if ( i.Name.StartsWith( “Down” ) )
{
}
else if ( i.Name.StartsWith( “Disabled” ) )
{
}
}
else if ( i.Name.EndsWith( “BottomBorder” ) )
{
{
}
else if ( i.Name.StartsWith( “Over” ) )
{
}
else if ( i.Name.StartsWith( “Down” ) )
{
}
else if ( i.Name.StartsWith( “Disabled” ) )
{
}
}
else if ( i.Name.EndsWith( “BottomRightCorner” ) )
{
{
}
else if ( i.Name.StartsWith( “Over” ) )
{
}
else if ( i.Name.StartsWith( “Down” ) )
{
}
else if ( i.Name.StartsWith( “Disabled” ) )
{
}
}
}
PositionQuads();
}
/// <summary>Gets the Button’s current Quads</summary>
public override List<Quad> Quads 
{
{
case ControlState.Over:
return m_overQuads;
case ControlState.Down:
default:
}
}
}
/// <summary>Gets and sets the Panel’s position</summary>
public override PointF Position 
set
{
float yOffset = value.Y – m_position.Y;
m_position = value;
for ( int i = 0; i < 9; i++ )
{
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].Y += yOffset;
}
BuildHotspot();
}
}
/// <summary>Repositions the hot spot and text</summary>
private void PositionQuads() 
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();
}
/// <summary>Builds the text</summary>
protected override void BuildText() 
(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<FontQuad> fontQuads = m_bFont.GetProcessedQuads( index );
m_bFont.ClearString( index );
// Convert FontQuads to Quads
m_fontQuads = new List<Quad>( fontQuads.Count );
for ( int i = 0; i < fontQuads.Count; i++ )
{
fontQuads[i].BottomLeft, fontQuads[i].BottomRight ) );
}
}
/// <summary>Builds the hotspot</summary>
private void BuildHotspot() 
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.
public class CheckBox : Control
{
protected List<Quad> m_overQuads;
protected List<Quad> m_downQuads;
protected List<Quad> m_disabledQuads;
protected Quad m_marker;
protected enum Section { Left, Right, Top, Bottom, Background, TopLeft, TopRight, BottomLeft, BottomRight };
/// <summary>Default Constructor</summary>
public CheckBox() 
}
/// <summary>Creates a CheckBox</summary>
/// <param name=”id”>Control ID</param>
/// <param name=”screenRect”>Screen rectangle</param>
/// <param name=”text”>CheckBox text</param>
/// <param name=”fontSize”>Font size</param>
/// <param name=”textColor”>Text color</param>
/// <param name=”textAlign”>Text alignment</param>
/// <param name=”isChecked”>Whether the checkbox is initially checked</param>
/// <param name=”font”>Initialized CUnit.BitmapFont instance</param>
/// <param name=”node”>ControlNode from XML file</param>
/// <param name=”info”>Texture ImageInformation</param>
public CheckBox( int id, RectangleF screenRect, string text, float fontSize, ColorValue textColor, TextAlign textAlign, bool isChecked, BitmapFont font, ControlNode node, ImageInformation info ) 
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<Quad>();
m_overQuads = new List<Quad>();
m_downQuads = new List<Quad>();
m_disabledQuads = new List<Quad>();
float z = 0f;
float rhw = 1f;
foreach ( ImageNode i in node.Images )
{
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” )
{
}
else if ( i.Name == “Over” )
{
}
else if ( i.Name == “Down” )
{
}
else if ( i.Name == “Disabled” )
{
}
else if ( i.Name == “CheckMark” )
{
}
}
if ( isChecked )
{
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();
}
/// <summary>Mouse Release event</summary>
/// <param name=”cursor”>Mouse position</param>
protected override void OnMouseRelease( Point cursor ) 
if ( (bool)m_data == true )
{
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
{
m_normalQuads.RemoveAt( 1 );
m_overQuads.RemoveAt( 1 );
m_downQuads.RemoveAt( 1 );
m_disabledQuads.RemoveAt( 1 );
}
}
/// <summary>Gets the Control’s current Quads</summary>
public override List<Quad> Quads 
get
{
{
case ControlState.Over:
case ControlState.Down:
return m_downQuads;
default:
}
}
}
/// <summary>Gets and sets the Panel’s position</summary>
public override PointF Position 
set
{
float yOffset = value.Y – m_position.Y;
m_position = value;
// Reposition Quads
for ( int i = 0; i < m_normalQuads.Count; i++ )
{
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].Y += yOffset;
}
}
}
/// <summary>Builds the Control text</summary>
protected override void BuildText() 
BitmapFont.Align alignment = BitmapFont.Align.Left;
switch ( m_textAlign )
{
{
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 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 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 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 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<FontQuad> fontQuads = m_bFont.GetProcessedQuads( index );
m_bFont.ClearString( index );
// Convert FontQuads to Quads
m_fontQuads = new List<Quad>( fontQuads.Count );
for ( int i = 0; i < fontQuads.Count; i++ )
{
}
}
/// <summary>Gets and sets whether the Control is checked or not.</summary>
public virtual bool Checked 
set
{
{
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 )
{
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.
public class RadioButton : CheckBox
{
private bool m_needToDeselectOthers;
/// <summary>Creates a RadioButton</summary>
/// <param name=”id”>Control ID</param>
/// <param name=”groupID”>GroupID to associate with RadioButton</param>
/// <param name=”screenRect”>Screen rectangle</param>
/// <param name=”text”>CheckBox text</param>
/// <param name=”fontSize”>Font size</param>
/// <param name=”textColor”>Text color</param>
/// <param name=”textAlign”>Text alignment</param>
/// <param name=”isSelected”>Whether the RadioButton is initially selected.</param>
/// <param name=”font”>Initialized CUnit.BitmapFont instance</param>
/// <param name=”node”>ControlNode from XML file</param>
/// <param name=”info”>Texture ImageInformation</param>
public RadioButton( int id, int groupID, RectangleF screenRect, string text, float fontSize, ColorValue textColor, TextAlign textAlign, bool isSelected, BitmapFont font, ControlNode node, ImageInformation info ) 
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<Quad>();
m_overQuads = new List<Quad>();
m_downQuads = new List<Quad>();
m_disabledQuads = new List<Quad>();
float z = 0f;
float rhw = 1f;
foreach ( ImageNode i in node.Images )
{
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” )
{
}
else if ( i.Name == “Over” )
{
}
else if ( i.Name == “Down” )
{
}
else if ( i.Name == “Disabled” )
{
}
else if ( i.Name == “RadioMark” )
{
}
}
if ( isSelected )
{
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();
}
/// <summary>Mouse Release event</summary>
/// <param name=”cursor”>Mouse position</param>
protected override void OnMouseRelease( Point cursor ) 
if ( (bool)m_data == true && ( m_normalQuads.Count < 2 ) )
{
// 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() );
}
}
/// <summary>Deselects the RadioButton.</summary>
public void Deselect() 
if ( m_normalQuads.Count > 1 )
{
// Remove checkmark
m_normalQuads.RemoveAt( 1 );
m_overQuads.RemoveAt( 1 );
m_downQuads.RemoveAt( 1 );
m_disabledQuads.RemoveAt( 1 );
}
}
/// <summary>Gets the group id.</summary>
public int GroupID 
}
/// <summary>Gets and sets whether the GuiManager needs to
/// deselect the other RadioButtons in this groupID.</summary>
public bool NeedToDelectOthers 
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.
public class Slider : Control
{
private List<Quad> m_overQuads;
private List<Quad> m_downQuads;
private List<Quad> m_disabledQuads;
private List<Rectangle> m_hotspots;
private float m_min;
private float m_max;
private enum Section { Left, Right, Middle, Marker };
/// <summary>Creates a Button</summary>
/// <param name=”id”>Control ID</param>
/// <param name=”rectangle”>Screen rectangle</param>
/// <param name=”text”>Button text</param>
/// <param name=”node”>ControlNode from XML file</param>
/// <param name=”info”>Texture ImageInformation</param>
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 ) 
{
}
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<Quad>();
m_overQuads = new List<Quad>();
m_downQuads = new List<Quad>();
m_disabledQuads = new List<Quad>();
m_hotspots = new List<Rectangle>();
// 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_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 )
{
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” ) )
{
{
}
else if ( i.Name.StartsWith( “Over” ) )
{
}
else if ( i.Name.StartsWith( “Down” ) )
{
}
else if ( i.Name.StartsWith( “Disabled” ) )
{
}
}
else if ( i.Name.EndsWith( “Middle” ) )
{
{
}
else if ( i.Name.StartsWith( “Over” ) )
{
}
else if ( i.Name.StartsWith( “Down” ) )
{
}
else if ( i.Name.StartsWith( “Disabled” ) )
{
}
}
else if ( i.Name.EndsWith( “RightCap” ) )
{
{
}
else if ( i.Name.StartsWith( “Over” ) )
{
}
else if ( i.Name.StartsWith( “Down” ) )
{
}
else if ( i.Name.StartsWith( “Disabled” ) )
{
}
}
else if ( i.Name.EndsWith( “Marker” ) )
{
{
}
else if ( i.Name.StartsWith( “Over” ) )
{
}
else if ( i.Name.StartsWith( “Down” ) )
{
}
else if ( i.Name.StartsWith( “Disabled” ) )
{
}
}
}
m_size = new SizeF( width, m_normalQuads[(int)Section.Middle].Height );
PositionQuads();
}
/// <summary>Updates the current value based on the slider’s position.</summary>
private void ScreenXToCurrentValue() 
m_data = ( ( m_max – m_min ) * ( m_normalQuads[(int)Section.Marker].X – m_normalQuads[(int)Section.Middle].X ) ) / screenRange + m_min;
}
/// <summary>Updates the slider’s position based on the current value.</summary>
private void CurrentValueToScreenX() 
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;
}
/// <summary>Checks is the mouse is over the Control’s hotspot</summary>
/// <param name=”cursor”>Mouse position</param>
/// <returns>true if the cursor is over the Control’s hotspot, false otherwise.</returns>
public override bool Contains( Point cursor ) 
{
{
}
}
return false;
}
/// <summary>Mouse Down event</summary>
/// <param name=”cursor”>Mouse position</param>
/// <param name=”buttons”>Mouse buttons</param>
protected override void OnMouseDown( Point cursor, bool[] buttons ) 
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_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_overQuads[(int)Section.Marker].X = min;
m_downQuads[(int)Section.Marker].X = min;
m_disabledQuads[(int)Section.Marker].X = min;
}
ScreenXToCurrentValue();
BuildHotspots();
}
/// <summary>Gets the Slider’s current Quads</summary>
public override List<Quad> Quads 
{
{
case ControlState.Normal:
case ControlState.Over:
case ControlState.Down:
default:
}
}
}
/// <summary>Gets and sets the Panel’s position</summary>
public override PointF Position 
set
{
float yOffset = value.Y – m_position.Y;
m_position = value;
for ( int i = 0; i < 4; i++ )
{
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].Y += yOffset;
}
BuildHotspots();
}
}
/// <summary>Repositions the hot spot and text</summary>
private void PositionQuads() 
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();
}
/// <summary>Builds the hotspots</summary>
private void BuildHotspots() 
// 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 );
}
/// <summary>Builds the text</summary>
protected override void BuildText() 
// 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<FontQuad> fontQuads = m_bFont.GetProcessedQuads( index );
m_bFont.ClearString( index );
// Convert FontQuads to Quads
m_fontQuads = new List<Quad>( 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.
