For those interested, this is my final (so far) Button class:
public class Button {
public event Action OnClick;
public event Action OnHover;
public Rectangle Rec { get; set; }
public string Text { get; set; }
public Button(Rectangle rec, string text) {
this.Rec = rec;
this.Text = text;
}
public void Update(MouseState prev, MouseState cur) { // called every tick
if (this.OnClick != null && Rec.Contains(cur.X, cur.Y) && cur.LeftButton == ButtonState.Pressed && prev.LeftButton == ButtonState.Released) {
// Someone's listening, and we have a click
this.OnClick.Invoke();
}
if (this.OnHover != null && Rec.Contains(cur.X, cur.Y)) {
// Someone's listening, and we have a hover
this.OnHover.Invoke();
}
}
public void Draw(SpriteBatch spriteBatch, Color btnCol, Color textCol) {
spriteBatch.Draw(Game1.TheGame.GuiTex, Rec, btnCol);
Vector2 fontCenter = Game1.TheGame.DebugFont.MeasureString(Text) / 2;
spriteBatch.DrawString(Game1.TheGame.DebugFont, Text, new Vector2(Rec.Center.X, Rec.Center.Y), textCol, 0, fontCenter, 1f, SpriteEffects.None, 0);
}
}
And here is an example of how it is used:
Button[] menuOptions;
int selectedOption = 0;
...
menuOptions = new Button[3] {
new Button(new Rectangle(x, y, width, height), "New Game"),
new Button(new Rectangle(x, y, width, height), "Load Game"),
new Button(new Rectangle(x, y, width, height), "Settings")
};
...
for (int i = 0; i < menuOptions.Length; i++) {
int j = i; // this is due to the way C# deals with closures and captured variables
menuOptions[i].OnHover += delegate() { selectedOption = j; };
menuOptions[i].OnClick += selectOption;
}
...
public void Draw(GameTime gameTime, SpriteBatch spriteBatch) {
spriteBatch.Begin();
foreach (Button btn in menuOptions) {
Color btnCol = (btn == menuOptions[selectedOption]) ? Color.DarkBlue : Color.CadetBlue;
Color textCol = (btn == menuOptions[selectedOption]) ? Color.White : Color.Black;
btn.Draw(spriteBatch, btnCol, textCol);
}
spriteBatch.End();
}
...
private void selectOption() {
if (selectedOption == 0) { // New Game
// do new game stuff
} else if (selectedOption == 1) { // Load Game
// do load game stuff
} else if (selectedOption == 2) { // Settings
// do settings stuff
}
}
This example is far from perfect and it would have to be adapted to any specific needs, but it should help.