Maelstrom: Created a more flexible callback structure, allowing member function callbacks.

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

From cf8bd825a815065f1493b407fc3883343b0611e6 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Sat, 5 Nov 2011 16:19:10 -0400
Subject: [PATCH] Created a more flexible callback structure, allowing member
 function callbacks.

---
 main.cpp                     | 48 +++++++++++++++----------------
 screenlib/UIDialog.cpp       |  2 +-
 screenlib/UIDialog.h         |  4 +--
 screenlib/UIElement.cpp      | 42 +++++++--------------------
 screenlib/UIElement.h        | 56 +++++++++++++++++++++++++++++++-----
 screenlib/UIElementRadio.cpp | 41 ++++++--------------------
 screenlib/UIElementRadio.h   | 56 +++++++++++++++++++++++++++++++-----
 7 files changed, 144 insertions(+), 105 deletions(-)

diff --git a/main.cpp b/main.cpp
index 71e3028d..b97f4c25 100644
--- a/main.cpp
+++ b/main.cpp
@@ -59,24 +59,24 @@ int	gNoDelay;
 
 
 // Main Menu actions:
-static void RunDoAbout(void)
+static void RunDoAbout(void*)
 {
 	ui->ShowPanel(PANEL_ABOUT);
 }
-static void RunPlayGame(void)
+static void RunPlayGame(void*)
 {
 	gStartLevel = 1;
 	gStartLives = 3;
 	gNoDelay = 0;
 	NewGame();
 }
-static void RunQuitGame(void)
+static void RunQuitGame(void*)
 {
 	while ( sound->Playing() )
 		Delay(SOUND_DELAY);
 	gRunning = false;
 }
-static void IncrementSound(void)
+static void IncrementSound(void*)
 {
 	if ( gSoundLevel < 8 ) {
 		sound->Volume(++gSoundLevel);
@@ -86,7 +86,7 @@ static void IncrementSound(void)
 		gUpdateBuffer = true;
 	}
 }
-static void DecrementSound(void)
+static void DecrementSound(void*)
 {
 	if ( gSoundLevel > 0 ) {
 		sound->Volume(--gSoundLevel);
@@ -108,7 +108,7 @@ static void SetSoundLevel(int volume)
 	/* -- Draw the new sound level */
 	gUpdateBuffer = true;
 }
-static void RunToggleFullscreen(void)
+static void RunToggleFullscreen(void*)
 {
 	screen->ToggleFullScreen();
 }
@@ -152,17 +152,17 @@ static void CheatDialogDone(UIDialog *dialog, int status)
 		NewGame();
 	}
 }
-static void RunScreenshot(void)
+static void RunScreenshot(void*)
 {
 	screen->ScreenDump("ScoreDump", 64, 48, 298, 384);
 }
 
-class SetVolumeDelegate : public UIClickDelegate
+class SetVolumeCallback : public UIClickCallback
 {
 public:
-	SetVolumeDelegate(int volume) : m_volume(volume) { }
+	SetVolumeCallback(int volume) : m_volume(volume) { }
 
-	virtual void OnClick() {
+	virtual void operator()() {
 		SetSoundLevel(m_volume);
 	}
 private:
@@ -309,7 +309,7 @@ int main(int argc, char *argv[])
 
 			/* -- Handle window close requests */
 			if ( event.type == SDL_QUIT ) {
-				RunQuitGame();
+				RunQuitGame(0);
 			}
 		}
 		Delay(FRAME_DELAY);
@@ -343,11 +343,11 @@ MainPanelDelegate::OnLoad()
 	}
 	button = m_panel->GetElement<UIElementButton>("ControlsButton");
 	if (button) {
-		button->SetClickDelegate(new UIDialogLauncher(ui, DIALOG_CONTROLS));
+		button->SetClickCallback(new UIDialogLauncher(ui, DIALOG_CONTROLS));
 	}
 	button = m_panel->GetElement<UIElementButton>("ZapButton");
 	if (button) {
-		button->SetClickDelegate(new UIDialogLauncher(ui, DIALOG_ZAP, NULL, ZapHighScores));
+		button->SetClickCallback(new UIDialogLauncher(ui, DIALOG_ZAP, NULL, ZapHighScores));
 	}
 	button = m_panel->GetElement<UIElementButton>("AboutButton");
 	if (button) {
@@ -371,11 +371,11 @@ MainPanelDelegate::OnLoad()
 	}
 	button = m_panel->GetElement<UIElementButton>("Cheat");
 	if (button) {
-		button->SetClickDelegate(new UIDialogLauncher(ui, DIALOG_CHEAT, CheatDialogInit, CheatDialogDone));
+		button->SetClickCallback(new UIDialogLauncher(ui, DIALOG_CHEAT, CheatDialogInit, CheatDialogDone));
 	}
 	button = m_panel->GetElement<UIElementButton>("Special");
 	if (button) {
-		button->SetClickDelegate(new UIDialogLauncher(ui, DIALOG_DAWN));
+		button->SetClickCallback(new UIDialogLauncher(ui, DIALOG_DAWN));
 	}
 	button = m_panel->GetElement<UIElementButton>("Screenshot");
 	if (button) {
@@ -384,39 +384,39 @@ MainPanelDelegate::OnLoad()
 
 	button = m_panel->GetElement<UIElementButton>("SetVolume0");
 	if (button) {
-		button->SetClickDelegate(new SetVolumeDelegate(0));
+		button->SetClickCallback(new SetVolumeCallback(0));
 	}
 	button = m_panel->GetElement<UIElementButton>("SetVolume1");
 	if (button) {
-		button->SetClickDelegate(new SetVolumeDelegate(1));
+		button->SetClickCallback(new SetVolumeCallback(1));
 	}
 	button = m_panel->GetElement<UIElementButton>("SetVolume2");
 	if (button) {
-		button->SetClickDelegate(new SetVolumeDelegate(2));
+		button->SetClickCallback(new SetVolumeCallback(2));
 	}
 	button = m_panel->GetElement<UIElementButton>("SetVolume3");
 	if (button) {
-		button->SetClickDelegate(new SetVolumeDelegate(3));
+		button->SetClickCallback(new SetVolumeCallback(3));
 	}
 	button = m_panel->GetElement<UIElementButton>("SetVolume4");
 	if (button) {
-		button->SetClickDelegate(new SetVolumeDelegate(4));
+		button->SetClickCallback(new SetVolumeCallback(4));
 	}
 	button = m_panel->GetElement<UIElementButton>("SetVolume5");
 	if (button) {
-		button->SetClickDelegate(new SetVolumeDelegate(5));
+		button->SetClickCallback(new SetVolumeCallback(5));
 	}
 	button = m_panel->GetElement<UIElementButton>("SetVolume6");
 	if (button) {
-		button->SetClickDelegate(new SetVolumeDelegate(6));
+		button->SetClickCallback(new SetVolumeCallback(6));
 	}
 	button = m_panel->GetElement<UIElementButton>("SetVolume7");
 	if (button) {
-		button->SetClickDelegate(new SetVolumeDelegate(7));
+		button->SetClickCallback(new SetVolumeCallback(7));
 	}
 	button = m_panel->GetElement<UIElementButton>("SetVolume8");
 	if (button) {
-		button->SetClickDelegate(new SetVolumeDelegate(8));
+		button->SetClickCallback(new SetVolumeCallback(8));
 	}
 
 	return true;
diff --git a/screenlib/UIDialog.cpp b/screenlib/UIDialog.cpp
index cc66f8b4..2c2ed851 100644
--- a/screenlib/UIDialog.cpp
+++ b/screenlib/UIDialog.cpp
@@ -102,7 +102,7 @@ UIDialogLauncher::UIDialogLauncher(UIManager *ui, const char *name, UIDialogInit
 }
 
 void
-UIDialogLauncher::OnClick()
+UIDialogLauncher::operator()()
 {
 	UIDialog *dialog;
 
diff --git a/screenlib/UIDialog.h b/screenlib/UIDialog.h
index 2d4c2b7f..c8994f0c 100644
--- a/screenlib/UIDialog.h
+++ b/screenlib/UIDialog.h
@@ -78,12 +78,12 @@ DECLARE_TYPESAFE_CLASS(UIPanel)
 //
 // A class to make it easy to launch a dialog from a button
 //
-class UIDialogLauncher : public UIClickDelegate
+class UIDialogLauncher : public UIClickCallback
 {
 public:
 	UIDialogLauncher(UIManager *ui, const char *name, UIDialogInitHandler = NULL, UIDialogDoneHandler handleDone = NULL);
 
-	override void OnClick();
+	override void operator()();
 
 protected:
 	UIManager *m_ui;
diff --git a/screenlib/UIElement.cpp b/screenlib/UIElement.cpp
index 0a248c86..fb9e3d3a 100644
--- a/screenlib/UIElement.cpp
+++ b/screenlib/UIElement.cpp
@@ -24,22 +24,6 @@
 #include "SDL_FrameBuf.h"
 #include "UIElement.h"
 
-
-class SimpleClickDelegate : public UIClickDelegate
-{
-public:
-	SimpleClickDelegate(void (*callback)(void)) {
-		m_callback = callback;
-	}
-
-	virtual void OnClick() {
-		m_callback();
-	}
-
-protected:
-	void (*m_callback)(void);
-};
-
 UIElementType UIElement::s_elementType;
 
 
@@ -66,7 +50,7 @@ UIElement::UIElement(UIBaseElement *parent, const char *name, UIDrawEngine *draw
 	m_mouseEnabled = false;
 	m_mouseInside = false;
 	m_mousePressed = false;
-	m_clickDelegate = NULL;
+	m_clickCallback = NULL;
 
 	SetDrawEngine(drawEngine);
 }
@@ -88,8 +72,8 @@ UIElement::~UIElement()
 	if (m_image) {
 		m_screen->FreeImage(m_image);
 	}
-	if (m_clickDelegate) {
-		delete m_clickDelegate;
+	if (m_clickCallback) {
+		delete m_clickCallback;
 	}
 }
 
@@ -579,21 +563,15 @@ UIElement::HandleEvent(const SDL_Event &event)
 }
 
 void
-UIElement::SetClickCallback(void (*callback)(void))
-{
-	SetClickDelegate(new SimpleClickDelegate(callback));
-}
-
-void
-UIElement::SetClickDelegate(UIClickDelegate *delegate)
+UIElement::SetClickCallback(UIClickCallback *callback)
 {
-	if (m_clickDelegate) {
-		delete m_clickDelegate;
+	if (m_clickCallback) {
+		delete m_clickCallback;
 	}
 
-	m_clickDelegate = delegate;
+	m_clickCallback = callback;
 
-	if (m_clickDelegate) {
+	if (m_clickCallback) {
 		m_mouseEnabled = true;
 	}
 }
@@ -634,7 +612,7 @@ UIElement::OnMouseUp()
 void
 UIElement::OnClick()
 {
-	if (m_clickDelegate) {
-		m_clickDelegate->OnClick();
+	if (m_clickCallback) {
+		(*m_clickCallback)();
 	}
 }
diff --git a/screenlib/UIElement.h b/screenlib/UIElement.h
index f70032c5..bc9e8cfb 100644
--- a/screenlib/UIElement.h
+++ b/screenlib/UIElement.h
@@ -26,10 +26,47 @@
 #include "UIDrawEngine.h"
 #include "UIFontInterface.h"
 
-class UIClickDelegate
+class UIClickCallback
 {
 public:
-	virtual void OnClick() { }
+	virtual void operator()() = 0;
+};
+
+template <class C>
+class UIObjectClickCallback : public UIClickCallback
+{
+public:
+	UIObjectClickCallback(C *obj, void (C::*callback)(void*), void *param) : UIClickCallback() {
+		m_obj = obj;
+		m_callback = callback;
+		m_param = param;
+	}
+
+	virtual void operator()() {
+		(m_obj->*m_callback)(m_param);
+	}
+
+protected:
+	C *m_obj;
+	void (C::*m_callback)(void*);
+	void *m_param;
+};
+
+class UIFunctionClickCallback : public UIClickCallback
+{
+public:
+	UIFunctionClickCallback(void (*callback)(void*), void *param) : UIClickCallback() {
+		m_callback = callback;
+		m_param = param;
+	}
+
+	virtual void operator()() {
+		(*m_callback)(m_param);
+	}
+
+protected:
+	void (*m_callback)(void*);
+	void *m_param;
 };
 
 // This is the basic thing you see on the screen.
@@ -131,10 +168,15 @@ DECLARE_TYPESAFE_CLASS(UIBaseElement)
 	// Events
 	override bool HandleEvent(const SDL_Event &event);
 
-	// Setting a click callback sets a simplified delegate
-	// Once set, the element owns the click delegate and will free it.
-	void SetClickCallback(void (*callback)(void));
-	void SetClickDelegate(UIClickDelegate *delegate);
+	// Once set, the element owns the click callback and will free it.
+	template <class C>
+	void SetClickCallback(C *obj, void (C::*callback)(void*), void *param = 0) {
+		SetClickCallback(new UIObjectClickCallback<C>(obj, callback, param));
+	}
+	void SetClickCallback(void (*callback)(void*), void *param = 0) {
+		SetClickCallback(new UIFunctionClickCallback(callback, param));
+	}
+	void SetClickCallback(UIClickCallback *callback);
 
 	// These can be overridden by inheriting classes
 	virtual void OnMouseEnter();
@@ -164,7 +206,7 @@ DECLARE_TYPESAFE_CLASS(UIBaseElement)
 	bool m_mouseEnabled;
 	bool m_mouseInside;
 	bool m_mousePressed;
-	UIClickDelegate *m_clickDelegate;
+	UIClickCallback *m_clickCallback;
 
 protected:
 	bool LoadColor(rapidxml::xml_node<> *node, const char *name, Uint32 &value);
diff --git a/screenlib/UIElementRadio.cpp b/screenlib/UIElementRadio.cpp
index 95c8469f..a94d4029 100644
--- a/screenlib/UIElementRadio.cpp
+++ b/screenlib/UIElementRadio.cpp
@@ -21,23 +21,6 @@
 
 #include "UIElementRadio.h"
 
-
-class SimpleRadioDelegate : public UIRadioDelegate
-{
-public:
-	SimpleRadioDelegate(void (*callback)(int value)) {
-		m_callback = callback;
-	}
-
-	virtual void OnValueChanged(int oldValue, int newValue) {
-		m_callback(newValue);
-	}
-
-protected:
-	void (*m_callback)(int value);
-};
-
-
 UIElementType UIElementRadioGroup::s_elementType;
 
 
@@ -46,7 +29,7 @@ UIElementRadioGroup::UIElementRadioGroup(UIBaseElement *parent, const char *name
 {
 	m_value = -1;
 	m_valueBinding = NULL;
-	m_delegate = NULL;
+	m_callback = NULL;
 }
 
 UIElementRadioGroup::~UIElementRadioGroup()
@@ -54,8 +37,8 @@ UIElementRadioGroup::~UIElementRadioGroup()
 	if (m_valueBinding) {
 		SDL_free(m_valueBinding);
 	}
-	if (m_delegate) {
-		delete m_delegate;
+	if (m_callback) {
+		delete m_callback;
 	}
 }
 
@@ -129,24 +112,18 @@ UIElementRadioGroup::SetValue(int value)
 		}
 	}
 
-	if (m_delegate) {
-		m_delegate->OnValueChanged(oldValue, m_value);
+	if (m_callback) {
+		(*m_callback)(m_value);
 	}
 }
 
 void
-UIElementRadioGroup::SetCallback(void (*callback)(int value))
-{
-	SetDelegate(new SimpleRadioDelegate(callback));
-}
-
-void
-UIElementRadioGroup::SetDelegate(UIRadioDelegate *delegate)
+UIElementRadioGroup::SetValueCallback(UIRadioCallback *callback)
 {
-	if (m_delegate) {
-		delete m_delegate;
+	if (m_callback) {
+		delete m_callback;
 	}
-	m_delegate = delegate;
+	m_callback = callback;
 }
 
 
diff --git a/screenlib/UIElementRadio.h b/screenlib/UIElementRadio.h
index 364ce9a7..911531b0 100644
--- a/screenlib/UIElementRadio.h
+++ b/screenlib/UIElementRadio.h
@@ -25,10 +25,47 @@
 #include "UIElement.h"
 #include "UIElementCheckbox.h"
 
-class UIRadioDelegate
+class UIRadioCallback
 {
 public:
-	virtual void OnValueChanged(int oldValue, int newValue) { }
+	virtual void operator ()(int value) = 0;
+};
+
+template <class C>
+class UIObjectRadioCallback : public UIRadioCallback
+{
+public:
+	UIObjectRadioCallback(C *obj, void (C::*callback)(void*, int), void *param) : UIRadioCallback() {
+		m_obj = obj;
+		m_callback = callback;
+		m_param = param;
+	}
+
+	virtual void operator()(int value) {
+		(m_obj->*m_callback)(m_param, value);
+	}
+
+protected:
+	C *m_obj;
+	void (C::*m_callback)(void*, int);
+	void *m_param;
+};
+
+class UIFunctionRadioCallback : public UIRadioCallback
+{
+public:
+	UIFunctionRadioCallback(void (*callback)(void*, int), void *param) : UIRadioCallback() {
+		m_callback = callback;
+		m_param = param;
+	}
+
+	virtual void operator()(int value) {
+		(*m_callback)(m_param, value);
+	}
+
+protected:
+	void (*m_callback)(void*, int);
+	void *m_param;
 };
 
 //
@@ -61,15 +98,20 @@ DECLARE_TYPESAFE_CLASS(UIElement)
 		return m_value;
 	}
 
-	// Setting a callback sets a simplified delegate
-	// Once set, the element owns the delegate and will free it.
-	void SetCallback(void (*callback)(int value));
-	void SetDelegate(UIRadioDelegate *delegate);
+	// Once set, the element owns the callback and will free it.
+	template <class C>
+	void SetValueCallback(C *obj, void (C::*callback)(void*, int), void *param = 0) {
+		SetValueCallback(new UIObjectRadioCallback<C>(obj, callback, param));
+	}
+	void SetValueCallback(void (*callback)(void*, int), void *param = 0) {
+		SetValueCallback(new UIFunctionRadioCallback(callback, param));
+	}
+	void SetValueCallback(UIRadioCallback *callback);
 
 protected:
 	int m_value;
 	char *m_valueBinding;
-	UIRadioDelegate *m_delegate;
+	UIRadioCallback *m_callback;
 };
 
 class UIElementRadioButton : public UIElementCheckbox