Maelstrom: Added an easy way to bind buttons to launch dialogs.

https://github.com/libsdl-org/Maelstrom/commit/f8513d4c35b5711845a6fb4c132ffbd67e9e1e28

From f8513d4c35b5711845a6fb4c132ffbd67e9e1e28 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Sat, 29 Oct 2011 03:51:18 -0400
Subject: [PATCH] Added an easy way to bind buttons to launch dialogs. Added a
 way to set dialog completion callbacks, which are passed the ID of the button
 that closed the dialog.

---
 UI/dawn.xml           |  2 +-
 UI/zap.xml            |  4 ++--
 UIDialog.cpp          | 44 +++++++++++++++++++++++++++++++++++---
 UIDialog.h            | 49 +++++++++++++++++++++++++++++++++++++++++++
 UIDialogButton.cpp    | 12 ++++++++++-
 UIDialogButton.h      |  1 +
 main.cpp              | 29 +++----------------------
 scores.cpp            | 22 ++++++++++---------
 scores.h              |  4 +++-
 screenlib/UIManager.h | 11 ++++++++--
 10 files changed, 132 insertions(+), 46 deletions(-)

diff --git a/UI/dawn.xml b/UI/dawn.xml
index 1aeddd61..cd4f32e1 100644
--- a/UI/dawn.xml
+++ b/UI/dawn.xml
@@ -24,7 +24,7 @@
 			<Anchor anchorFrom="TOPLEFT" anchorTo="BOTTOMRIGHT" anchor="line5" x="2" y="2"/>
 		</DialogLabel>
 
-		<DialogButton text="OK" default="true" closeDialog="true">
+		<DialogButton text="OK" default="true">
 			<Size w="90"/>
 			<Anchor anchorFrom="TOPLEFT" anchorTo="TOPLEFT" x="210" y="160"/>
 		</DialogButton>
diff --git a/UI/zap.xml b/UI/zap.xml
index 41e5353a..6079c6ba 100644
--- a/UI/zap.xml
+++ b/UI/zap.xml
@@ -5,10 +5,10 @@
 		<Title id="102">
 			<Anchor anchorFrom="TOPLEFT" anchorTo="TOPLEFT" x="4" y="4"/>
 		</Icon>
-		<DialogButton name="clearButton" text="Clear" closeDialog="true">
+		<DialogButton name="clearButton" text="Clear" id="1">
 			<Anchor anchorFrom="TOPLEFT" anchorTo="TOPLEFT" x="99" y="74"/>
 		</DialogButton>
-		<DialogButton text="Cancel" default="true" closeDialog="true">
+		<DialogButton text="Cancel" default="true">
 			<Anchor anchorFrom="TOPLEFT" anchorTo="TOPRIGHT" anchor="clearButton"x="13"/>
 		</DialogButton>
 	</Elements>
diff --git a/UIDialog.cpp b/UIDialog.cpp
index afafe96a..f6a539b9 100644
--- a/UIDialog.cpp
+++ b/UIDialog.cpp
@@ -7,6 +7,9 @@
 #define EXPAND_STEPS 30
 
 
+UIElementType UIDialog::s_elementType;
+
+
 UIDialog::UIDialog(UIManager *ui, const char *name) :
 	UIPanel(ui, name)
 {
@@ -19,6 +22,8 @@ UIDialog::UIDialog(UIManager *ui, const char *name) :
 	m_colors[COLOR_WHITE] = m_screen->MapRGB(0xFF, 0xFF, 0xFF);
 	m_expand = true;
 	m_step = 0;
+	m_status = 0;
+	m_handler = NULL;
 }
 
 UIDialog::~UIDialog()
@@ -28,8 +33,6 @@ UIDialog::~UIDialog()
 bool
 UIDialog::Load(rapidxml::xml_node<> *node, const UITemplates *templates)
 {
-	rapidxml::xml_attribute<> *attr;
-
 	if (!UIPanel::Load(node, templates)) {
 		return false;
 	}
@@ -47,6 +50,19 @@ UIDialog::Show()
 	} else {
 		m_step = EXPAND_STEPS;
 	}
+	m_status = 0;
+
+	UIPanel::Show();
+}
+
+void
+UIDialog::Hide()
+{
+	UIPanel::Hide();
+
+	if (m_handler) {
+		m_handler(this, m_status);
+	}
 }
 
 void
@@ -100,7 +116,9 @@ UIDialog::Draw()
 bool
 UIDialog::HandleEvent(const SDL_Event &event)
 {
-	UIPanel::HandleEvent(event);
+	if (UIPanel::HandleEvent(event)) {
+		return true;
+	}
 
 	if (event.type != SDL_QUIT) {
 		/* Press escape to cancel out of dialogs */
@@ -112,3 +130,23 @@ UIDialog::HandleEvent(const SDL_Event &event)
 	}
 	return false;
 }
+
+UIDialogLauncher::UIDialogLauncher(UIManager *ui, const char *name, UIDialogHandler handler)
+{
+	m_ui = ui;
+	m_name = name;
+	m_handler = handler;
+}
+
+void
+UIDialogLauncher::OnClick()
+{
+	UIDialog *dialog;
+
+	dialog = m_ui->GetPanel<UIDialog>(m_name);
+	if (dialog) {
+		dialog->SetDialogHandler(m_handler);
+
+		m_ui->ShowPanel(dialog);
+	}
+}
diff --git a/UIDialog.h b/UIDialog.h
index 3c2a6c8d..625e9062 100644
--- a/UIDialog.h
+++ b/UIDialog.h
@@ -3,7 +3,14 @@
 #define _UIDialog_h
 
 #include "screenlib/UIPanel.h"
+#include "screenlib/UIElementButton.h"
 
+class UIDialog;
+
+/* This function gets called when the dialog is hidden.
+   The status defaults to 0, but can be changed by dialog buttons.
+ */
+typedef void (*UIDialogHandler)(UIDialog *dialog, int status);
 
 class UIDialog : public UIPanel
 {
@@ -11,9 +18,22 @@ class UIDialog : public UIPanel
 	UIDialog(UIManager *ui, const char *name);
 	virtual ~UIDialog();
 
+	virtual bool IsA(UIElementType type) {
+		return UIPanel::IsA(type) || type == GetType();
+	}
+
+	/* Set a function that's called when the dialog is hidden */
+	void SetDialogHandler(UIDialogHandler handler) {
+		m_handler = handler;
+	}
+	void SetDialogStatus(int status) {
+		m_status = status;
+	}
+
 	virtual bool Load(rapidxml::xml_node<> *node, const UITemplates *templates);
 
 	virtual void Show();
+	virtual void Hide();
 	virtual void Draw();
 	virtual bool HandleEvent(const SDL_Event &event);
 
@@ -29,6 +49,35 @@ class UIDialog : public UIPanel
 	Uint32 m_colors[NUM_COLORS];
 	bool m_expand;
 	int m_step;
+	int m_status;
+	UIDialogHandler m_handler;
+
+protected:
+	static UIElementType s_elementType;
+
+public:
+	static UIElementType GetType() {
+		if (!s_elementType) {
+			s_elementType = GenerateType();
+		}
+		return s_elementType;
+	}
+};
+
+//
+// A class to make it easy to launch a dialog from a button
+//
+class UIDialogLauncher : public UIButtonDelegate
+{
+public:
+	UIDialogLauncher(UIManager *ui, const char *name, UIDialogHandler handler = NULL);
+
+	virtual void OnClick();
+
+protected:
+	UIManager *m_ui;
+	const char *m_name;
+	UIDialogHandler m_handler;
 };
 
 #endif // _UIDialog_h
diff --git a/UIDialogButton.cpp b/UIDialogButton.cpp
index abef44a5..1b655a3e 100644
--- a/UIDialogButton.cpp
+++ b/UIDialogButton.cpp
@@ -1,6 +1,7 @@
 
 #include "screenlib/SDL_FrameBuf.h"
 #include "screenlib/UIManager.h"
+#include "UIDialog.h"
 #include "UIDialogButton.h"
 #include "UIDialogLabel.h"
 
@@ -15,8 +16,9 @@ UIElementType UIDialogButton::s_elementType;
 UIDialogButton::UIDialogButton(UIBaseElement *parent, const char *name) :
 	UIElementButton(parent, name)
 {
+	m_statusID = 0;
 	m_default = false;
-	m_closeDialog = false;
+	m_closeDialog = true;
 
 	m_colors[0] = m_screen->MapRGB(0xFF, 0xFF, 0xFF);
 	m_colors[1] = m_screen->MapRGB(0x00, 0x00, 0x00);
@@ -37,6 +39,8 @@ UIDialogButton::Load(rapidxml::xml_node<> *node, const UITemplates *templates)
 		return false;
 	}
 
+	LoadNumber(node, "id", m_statusID);
+
 	LoadBool(node, "default", m_default);
 	if (m_default) {
 		m_hotkey = SDLK_RETURN;
@@ -156,6 +160,12 @@ UIDialogButton::OnClick()
 {
 	UIElementButton::OnClick();
 
+	if (m_statusID) {
+		UIPanel *panel = GetUI()->GetCurrentPanel();
+		if (panel->IsA(UIDialog::GetType())) {
+			static_cast<UIDialog*>(panel)->SetDialogStatus(m_statusID);
+		}
+	}
 	if (m_closeDialog) {
 		GetUI()->HidePanel(GetUI()->GetCurrentPanel());
 	}
diff --git a/UIDialogButton.h b/UIDialogButton.h
index 0e872e67..757617b4 100644
--- a/UIDialogButton.h
+++ b/UIDialogButton.h
@@ -24,6 +24,7 @@ class UIDialogButton : public UIElementButton
 
 protected:
 	Uint32 m_colors[2];
+	int m_statusID;
 	bool m_default;
 	bool m_closeDialog;
 
diff --git a/main.cpp b/main.cpp
index acd94fdf..37424e39 100644
--- a/main.cpp
+++ b/main.cpp
@@ -18,6 +18,7 @@
 #include "main.h"
 
 #include "screenlib/UIElementLabel.h"
+#include "UIDialog.h"
 #include "UIElementKeyButton.h"
 
 /* External functions used in this file */
@@ -89,26 +90,6 @@ static void SetSoundLevel(int volume)
 	/* -- Draw the new sound level */
 	gUpdateBuffer = true;
 }
-static void SetupZapScores(void)
-{
-	UIPanel *panel;
-	UIElementButton *button;
-
-	panel = ui->GetPanel(DIALOG_ZAP);
-	if (!panel) {
-		return;
-	}
-
-	button = panel->GetElement<UIElementButton>("clearButton");
-	if (button) {
-		button->SetClickCallback(ZapHighScores);
-	}
-}
-static void RunZapScores(void)
-{
-	SetupZapScores();
-	ui->ShowPanel(DIALOG_ZAP);
-}
 static void RunToggleFullscreen(void)
 {
 	screen->ToggleFullScreen();
@@ -123,10 +104,6 @@ static void RunCheat(void)
 		NewGame();
 	}
 }
-static void RunShowDawn(void)
-{
-	ui->ShowPanel(DIALOG_DAWN);
-}
 static void RunScreenshot(void)
 {
 	screen->ScreenDump("ScoreDump", 64, 48, 298, 384);
@@ -385,7 +362,7 @@ MainPanelDelegate::OnLoad()
 	}
 	button = m_panel->GetElement<UIElementButton>("ZapButton");
 	if (button) {
-		button->SetClickCallback(RunZapScores);
+		button->SetButtonDelegate(new UIDialogLauncher(ui, DIALOG_ZAP, ZapHighScores));
 	}
 	button = m_panel->GetElement<UIElementButton>("AboutButton");
 	if (button) {
@@ -413,7 +390,7 @@ MainPanelDelegate::OnLoad()
 	}
 	button = m_panel->GetElement<UIElementButton>("Special");
 	if (button) {
-		button->SetClickCallback(RunShowDawn);
+		button->SetButtonDelegate(new UIDialogLauncher(ui, DIALOG_DAWN));
 	}
 	button = m_panel->GetElement<UIElementButton>("Screenshot");
 	if (button) {
diff --git a/scores.cpp b/scores.cpp
index 89ca8460..5c4c6a0b 100644
--- a/scores.cpp
+++ b/scores.cpp
@@ -99,17 +99,19 @@ void PrintHighScores(void)
 	}
 }
 
-void ZapHighScores(void)
+void ZapHighScores(UIDialog *dialog, int status)
 {
-	memset(hScores, 0, sizeof(hScores));
-	SaveScores();
-	gLastHigh = -1;
-
-	/* Fade the screen and redisplay scores */
-	screen->FadeOut();
-	Delay(SOUND_DELAY);
-	sound->PlaySound(gExplosionSound, 5);
-	gUpdateBuffer = true;
+	if (status) {
+		memset(hScores, 0, sizeof(hScores));
+		SaveScores();
+		gLastHigh = -1;
+
+		/* Fade the screen and redisplay scores */
+		screen->FadeOut();
+		Delay(SOUND_DELAY);
+		sound->PlaySound(gExplosionSound, 5);
+		gUpdateBuffer = true;
+	}
 }
 
 
diff --git a/scores.h b/scores.h
index 49d27df3..ef4a9e91 100644
--- a/scores.h
+++ b/scores.h
@@ -1,8 +1,10 @@
 
+class UIDialog;
+
 // Functions from scores.cc
 extern void	LoadScores(void);
 extern void	SaveScores(void);
-extern void	ZapHighScores(void);
+extern void	ZapHighScores(UIDialog *dialog, int status);
 extern int	GetStartLevel(void);
 extern void	PrintHighScores(void);
 
diff --git a/screenlib/UIManager.h b/screenlib/UIManager.h
index 806fd42c..99782f92 100644
--- a/screenlib/UIManager.h
+++ b/screenlib/UIManager.h
@@ -26,14 +26,13 @@
 #include "SDL.h"
 #include "../utils/array.h"
 #include "UIArea.h"
+#include "UIPanel.h"
 #include "UIFontInterface.h"
 #include "UISoundInterface.h"
 #include "UITemplates.h"
 
 class FrameBuf;
 class UIBaseElement;
-class UIPanel;
-class UIPanelDelegate;
 class UIElement;
 
 class UIManager : public UIArea, public UIFontInterface, public UISoundInterface
@@ -53,6 +52,14 @@ class UIManager : public UIArea, public UIFontInterface, public UISoundInterface
 	bool LoadTemplates(const char *file);
 	UIPanel *LoadPanel(const char *name);
 	UIPanel *GetPanel(const char *name, bool allowLoad = true);
+	template <typename T>
+	T *GetPanel(const char *name) {
+		UIPanel *panel = GetPanel(name);
+		if (panel && panel->IsA(T::GetType())) {
+			return (T*)panel;
+		}
+		return NULL;
+	}
 	UIPanel *GetCurrentPanel();
 
 	/* These are called by the UIPanel class */