Maelstrom: It's now possible to create different types of panels, and the panel delegates are more robust with OnLoad and OnTick callbacks.

https://github.com/libsdl-org/Maelstrom/commit/318ebafe8239267685bd7f4244acbff9fa284230

From 318ebafe8239267685bd7f4244acbff9fa284230 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Tue, 25 Oct 2011 08:58:14 -0400
Subject: [PATCH] It's now possible to create different types of panels, and
 the panel delegates are more robust with OnLoad and OnTick callbacks.

---
 Maelstrom_Globals.h     |  5 +++
 Makefile.am             |  4 ++-
 UIElements.cpp          | 16 +++++-----
 UIElements.h            |  2 +-
 UIPanels.cpp            | 38 +++++++++++++++++++++++
 UIPanels.h              |  9 ++++++
 init.cpp                |  3 +-
 screenlib/UIManager.cpp | 66 +++++++++++++++++++++++++++++++++++-----
 screenlib/UIManager.h   |  8 ++++-
 screenlib/UIPanel.cpp   | 67 ++++++++---------------------------------
 screenlib/UIPanel.h     |  4 ++-
 11 files changed, 148 insertions(+), 74 deletions(-)
 create mode 100644 UIPanels.cpp
 create mode 100644 UIPanels.h

diff --git a/Maelstrom_Globals.h b/Maelstrom_Globals.h
index 22b05327..c974e295 100644
--- a/Maelstrom_Globals.h
+++ b/Maelstrom_Globals.h
@@ -1,4 +1,7 @@
 
+#ifndef _Maelstrom_Globals_h
+#define _Maelstrom_Globals_h
+
 #include <stdlib.h>
 
 #include "SDL_FrameBuf.h"
@@ -153,3 +156,5 @@ extern BlitPtr	gThrust1, gThrust2, gShrapnel1, gShrapnel2;
 extern SDL_Texture *gAutoFireIcon, *gAirBrakesIcon, *gMult2Icon, *gMult3Icon;
 extern SDL_Texture *gMult4Icon, *gMult5Icon, *gLuckOfTheIrishIcon;
 extern SDL_Texture *gLongFireIcon, *gTripleFireIcon, *gKeyIcon, *gShieldIcon;
+
+#endif // _Maelstrom_Globals_h
diff --git a/Makefile.am b/Makefile.am
index 43ffce36..74b9d738 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -38,7 +38,9 @@ Maelstrom_SOURCES =		\
 	UIElementTitle.cpp	\
 	UIElementTitle.h	\
 	UIElements.cpp		\
-	UIElements.h
+	UIElements.h		\
+	UIPanels.cpp		\
+	UIPanels.h
 
 LOGIC = netlogic
 
diff --git a/UIElements.cpp b/UIElements.cpp
index 4f0cebef..2dd91d88 100644
--- a/UIElements.cpp
+++ b/UIElements.cpp
@@ -10,21 +10,21 @@
 
 
 UIElement *
-CreateMaelstromUIElement(UIPanel *panel, const char *name)
+CreateMaelstromUIElement(UIPanel *panel, const char *type)
 {
-	if (strcasecmp(name, "Line") == 0) {
+	if (strcasecmp(type, "Line") == 0) {
 		return new UIElementLine(panel);
-	} else if (strcasecmp(name, "Rectangle") == 0) {
+	} else if (strcasecmp(type, "Rectangle") == 0) {
 		return new UIElementRect(panel);
-	} else if (strcasecmp(name, "Label") == 0) {
+	} else if (strcasecmp(type, "Label") == 0) {
 		return new UIElementLabel(panel);
-	} else if (strcasecmp(name, "Button") == 0) {
+	} else if (strcasecmp(type, "Button") == 0) {
 		return new UIElementButton(panel);
-	} else if (strcasecmp(name, "KeyButton") == 0) {
+	} else if (strcasecmp(type, "KeyButton") == 0) {
 		return new UIElementKeyButton(panel);
-	} else if (strcasecmp(name, "Icon") == 0) {
+	} else if (strcasecmp(type, "Icon") == 0) {
 		return new UIElementIcon(panel);
-	} else if (strcasecmp(name, "Title") == 0) {
+	} else if (strcasecmp(type, "Title") == 0) {
 		return new UIElementTitle(panel);
 	}
 	return NULL;
diff --git a/UIElements.h b/UIElements.h
index d721dae9..82d56435 100644
--- a/UIElements.h
+++ b/UIElements.h
@@ -4,6 +4,6 @@
 class UIPanel;
 class UIElement;
 
-UIElement *CreateMaelstromUIElement(UIPanel *panel, const char *name);
+UIElement *CreateMaelstromUIElement(UIPanel *panel, const char *type);
 
 #endif // _UIElements_h
diff --git a/UIPanels.cpp b/UIPanels.cpp
new file mode 100644
index 00000000..db8fe9aa
--- /dev/null
+++ b/UIPanels.cpp
@@ -0,0 +1,38 @@
+
+#include "screenlib/UIPanel.h"
+#include "UIPanels.h"
+#include "netlogic/about.h"
+
+
+static UIPanelDelegate *
+CreateMaelstromUIDelegate(const char *delegate)
+{
+	if (!delegate || !*delegate) {
+		return NULL;
+	}
+
+	if (strcasecmp(delegate, "AboutPanel") == 0) {
+		return new AboutPanelDelegate();
+	} else {
+		fprintf(stderr, "Warning: Couldn't find delegate '%s'\n", delegate);
+		return NULL;
+	}
+}
+
+UIPanel *
+CreateMaelstromUIPanel(UIManager *ui, const char *type, const char *name, const char *delegate)
+{
+	UIPanel *panel;
+
+	if (strcasecmp(type, "UIPanel") == 0) {
+		panel = new UIPanel(ui, name);
+	} else {
+		panel = NULL;
+	}
+
+	if (panel && !delegate || !*delegate) {
+		panel->SetPanelDelegate(CreateMaelstromUIDelegate(delegate));
+	}
+
+	return panel;
+}
diff --git a/UIPanels.h b/UIPanels.h
new file mode 100644
index 00000000..d8674d80
--- /dev/null
+++ b/UIPanels.h
@@ -0,0 +1,9 @@
+#ifndef _UIPanels_h
+#define _UIPanels_h
+
+class UIManager;
+class UIPanel;
+
+UIPanel *CreateMaelstromUIPanel(UIManager *ui, const char *type, const char *name, const char *delegate);
+
+#endif // _UIPanels_h
diff --git a/init.cpp b/init.cpp
index 20772835..0feda5f9 100644
--- a/init.cpp
+++ b/init.cpp
@@ -8,6 +8,7 @@
 #include "load.h"
 #include "colortable.h"
 #include "fastrand.h"
+#include "UIPanels.h"
 #include "UIElements.h"
 #include "screenlib/UIElement.h"
 
@@ -721,7 +722,7 @@ int DoInitializations(Uint32 window_flags, Uint32 render_flags)
 	}
 
 	/* Create the UI manager */
-	ui = new UIManager(screen, CreateMaelstromUIElement);
+	ui = new UIManager(screen, CreateMaelstromUIPanel, CreateMaelstromUIElement);
 	ui->SetSoundCallback(PlayUISound, NULL);
 	ui->SetLoadPath("UI");
 
diff --git a/screenlib/UIManager.cpp b/screenlib/UIManager.cpp
index d57366c6..3f2768b8 100644
--- a/screenlib/UIManager.cpp
+++ b/screenlib/UIManager.cpp
@@ -20,14 +20,17 @@
     slouken@libsdl.org
 */
 
+#include <physfs.h>
+
 #include "SDL_FrameBuf.h"
 #include "UIManager.h"
 #include "UIPanel.h"
 
 
-UIManager::UIManager(FrameBuf *screen, UIElementFactory factory) : UIArea(screen)
+UIManager::UIManager(FrameBuf *screen, UIPanelFactory panelFactory, UIElementFactory elementFactory) : UIArea(screen)
 {
-	m_elementFactory = factory;
+	m_panelFactory = panelFactory;
+	m_elementFactory = elementFactory;
 	m_soundCallback = NULL;
 	m_soundCallbackParam = NULL;
 	m_loadPath = new char[2];
@@ -77,14 +80,63 @@ UIManager::LoadPanel(const char *name)
 	panel = GetPanel(name, false);
 	if (!panel) {
 		char file[1024];
+		PHYSFS_File *fp;
+		PHYSFS_sint64 size;
+		char *buffer;
+
+		if (!GetPanelFactory()) {
+			fprintf(stderr, "Error: No panel factory set\n");
+			return NULL;
+		}
+		if (!GetElementFactory()) {
+			fprintf(stderr, "Error: No element factory set\n");
+			return NULL;
+		}
 
 		sprintf(file, "%s/%s.xml", m_loadPath, name);
-		panel = new UIPanel(this, name);
-		if (!panel->Load(file)) {
-			SetError("%s", panel->Error());
-			delete panel;
-			return false;
+		fp = PHYSFS_openRead(file);
+		if (!fp) {
+			fprintf(stderr, "Warning: Couldn't open %s: %s\n",
+						file, PHYSFS_getLastError());
+			return NULL;
+		}
+
+		size = PHYSFS_fileLength(fp);
+		buffer = new char[size+1];
+		if (PHYSFS_readBytes(fp, buffer, size) != size) {
+			fprintf(stderr, "Warning: Couldn't read from %s: %s\n",
+						file, PHYSFS_getLastError());
+			PHYSFS_close(fp);
+			delete[] buffer;
+			return NULL;
+		}
+		buffer[size] = '\0';
+		PHYSFS_close(fp);
+
+		rapidxml::xml_document<> doc;
+		try {
+			doc.parse<0>(buffer);
+		} catch (rapidxml::parse_error e) {
+			fprintf(stderr, "Warning: Couldn't parse %s: error: %s\n",
+						file, e.what());
+			delete[] buffer;
+			return NULL;
+		}
+
+		rapidxml::xml_node<> *node = doc.first_node();
+		rapidxml::xml_attribute<> *attr;
+		attr = node->first_attribute("delegate", 0, false);
+		panel = (GetPanelFactory())(this, node->name(), name, attr ? attr->value() : NULL);
+		if (panel) {
+			if (!panel->Load(node)) {
+				fprintf(stderr, "Warning: Couldn't load %s: %s\n",
+							file, panel->Error());
+				delete[] buffer;
+				delete panel;
+				return NULL;
+			}
 		}
+		delete[] buffer;
 	}
 	return panel;
 }
diff --git a/screenlib/UIManager.h b/screenlib/UIManager.h
index 52c1d453..ff28c601 100644
--- a/screenlib/UIManager.h
+++ b/screenlib/UIManager.h
@@ -28,21 +28,26 @@
 #include "UIArea.h"
 
 class FrameBuf;
+class UIManager;
 class UIPanel;
 class UIElement;
 
+typedef UIPanel *(*UIPanelFactory)(UIManager *ui, const char *type, const char *name, const char *delegate);
 typedef UIElement *(*UIElementFactory)(UIPanel *panel, const char *name);
 typedef void (*UISoundCallback)(void *, int soundID);
 
 class UIManager : public UIArea
 {
 public:
-	UIManager(FrameBuf *screen, UIElementFactory factory);
+	UIManager(FrameBuf *screen, UIPanelFactory panelFactory, UIElementFactory elementFactory);
 	~UIManager();
 
 	FrameBuf *GetScreen() const {
 		return m_screen;
 	}
+	UIPanelFactory GetPanelFactory() const {
+		return m_panelFactory;
+	}
 	UIElementFactory GetElementFactory() const {
 		return m_elementFactory;
 	}
@@ -90,6 +95,7 @@ class UIManager : public UIArea
 	bool HandleEvent(const SDL_Event &event);
 
 protected:
+	UIPanelFactory m_panelFactory;
 	UIElementFactory m_elementFactory;
 	UISoundCallback m_soundCallback;
 	void *m_soundCallbackParam;
diff --git a/screenlib/UIPanel.cpp b/screenlib/UIPanel.cpp
index 5c584974..adbf6500 100644
--- a/screenlib/UIPanel.cpp
+++ b/screenlib/UIPanel.cpp
@@ -20,8 +20,6 @@
     slouken@libsdl.org
 */
 
-#include <physfs.h>
-
 #include "SDL_FrameBuf.h"
 #include "UIPanel.h"
 #include "UIManager.h"
@@ -60,48 +58,11 @@ UIPanel::~UIPanel()
 }
 
 bool
-UIPanel::Load(const char *file)
+UIPanel::Load(rapidxml::xml_node<> *node)
 {
-	PHYSFS_File *fp;
-	PHYSFS_sint64 size;
-	char *buffer;
-
-	ClearError();
-
-	if (!m_ui->GetElementFactory()) {
-		SetError("No panel element factory set");
-		return false;
-	}
-
-	fp = PHYSFS_openRead(file);
-	if (!fp) {
-		SetError("Couldn't open %s: %s", file, PHYSFS_getLastError());
-		return false;
-	}
-
-	size = PHYSFS_fileLength(fp);
-	buffer = new char[size+1];
-	if (PHYSFS_readBytes(fp, buffer, size) != size) {
-		SetError("Couldn't read from %s: %s", file, PHYSFS_getLastError());
-		PHYSFS_close(fp);
-		delete[] buffer;
-		return false;
-	}
-	buffer[size] = '\0';
-	PHYSFS_close(fp);
-
-	rapidxml::xml_document<> doc;
-	try {
-		doc.parse<0>(buffer);
-	} catch (rapidxml::parse_error e) {
-		SetError("Parse error: %s", e.what());
-		delete[] buffer;
-		return false;
-	}
-
-	rapidxml::xml_node<> *node = doc.first_node();
 	rapidxml::xml_node<> *child;
 	rapidxml::xml_attribute<> *attr;
+
 	attr = node->first_attribute("fullscreen", 0, false);
 	if (attr) {
 		const char *value = attr->value();
@@ -117,24 +78,21 @@ UIPanel::Load(const char *file)
 	if (attr) {
 		m_leaveSound = atoi(attr->value());
 	}
-	if (strcmp(node->name(), "UIPanel") != 0) {
-		SetError("Parse error: UIPanel root element expected");
-		delete[] buffer;
-		return false;
-	}
 	if (!UIArea::Load(node)) {
-		delete[] buffer;
 		return false;
 	}
 	child = node->first_node("elements", 0, false);
 	if (child) {
 		if (!LoadElements(child)) {
-			delete[] buffer;
 			return false;
 		}
 	}
-	delete[] buffer;
-	return true;
+
+	if (m_delegate) {
+		return m_delegate->OnLoad();
+	} else {
+		return true;
+	}
 }
 
 bool
@@ -143,13 +101,11 @@ UIPanel::LoadElements(rapidxml::xml_node<> *node)
 	for (node = node->first_node(); node; node = node->next_sibling()) {
 		UIElement *element = (m_ui->GetElementFactory())(this, node->name());
 		if (!element) {
-			SetError("Couldn't find handler for element %s", node->name());
-			return false;
+			fprintf(stderr, "Warning: Couldn't find handler for element %s\n", node->name());
 		}
 		if (!element->Load(node)) {
-			SetError("Couldn't load element %s: %s", node->name(), element->Error());
+			fprintf(stderr, "Warning: Couldn't load element %s: %s\n", node->name(), element->Error());
 			delete element;
-			return false;
 		}
 		AddElement(element);
 	}
@@ -215,6 +171,9 @@ UIPanel::Hide()
 void
 UIPanel::Draw()
 {
+	if (m_delegate) {
+		m_delegate->OnTick();
+	}
 	for (unsigned i = 0; i < m_elements.length(); ++i) {
 		if (m_elements[i]->IsShown()) {
 			m_elements[i]->Draw();
diff --git a/screenlib/UIPanel.h b/screenlib/UIPanel.h
index a7c4abe1..a96f59d1 100644
--- a/screenlib/UIPanel.h
+++ b/screenlib/UIPanel.h
@@ -38,8 +38,10 @@ class UIManager;
 class UIPanelDelegate
 {
 public:
+	virtual bool OnLoad() { return true; }
 	virtual void OnShow() { }
 	virtual void OnHide() { }
+	virtual void OnTick() { }
 	virtual void OnDraw() { }
 };
 
@@ -59,7 +61,7 @@ class UIPanel : public UIArea
 		return m_fullscreen;
 	}
 
-	bool Load(const char *file);
+	bool Load(rapidxml::xml_node<> *node);
 
 	virtual UIArea *GetAnchorElement(const char *name);