Maelstrom: Made the UI manager an interface which games can implement to extend the base functionality.

https://github.com/libsdl-org/Maelstrom/commit/9306d2609cefd0373620cc1306d531dfefb06aaa

From 9306d2609cefd0373620cc1306d531dfefb06aaa Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Fri, 28 Oct 2011 18:33:23 -0400
Subject: [PATCH] Made the UI manager an interface which games can implement to
 extend the base functionality.

---
 MaelstromUI.cpp              | 84 ++++++++++++++++++++++++++++++++++++
 MaelstromUI.h                | 32 ++++++++++++++
 Makefile.am                  |  8 ++--
 UIElements.cpp               | 37 ----------------
 UIElements.h                 |  9 ----
 UIPanels.cpp                 | 43 ------------------
 UIPanels.h                   |  9 ----
 init.cpp                     | 18 ++------
 screenlib/Makefile.am        |  2 +
 screenlib/UIFontInterface.h  | 42 ++++++++++++++++++
 screenlib/UIManager.cpp      | 77 ++++++++++++++++++++++++---------
 screenlib/UIManager.h        | 39 +++++------------
 screenlib/UIPanel.cpp        |  2 +-
 screenlib/UISoundInterface.h | 32 ++++++++++++++
 14 files changed, 268 insertions(+), 166 deletions(-)
 create mode 100644 MaelstromUI.cpp
 create mode 100644 MaelstromUI.h
 delete mode 100644 UIElements.cpp
 delete mode 100644 UIElements.h
 delete mode 100644 UIPanels.cpp
 delete mode 100644 UIPanels.h
 create mode 100644 screenlib/UIFontInterface.h
 create mode 100644 screenlib/UISoundInterface.h

diff --git a/MaelstromUI.cpp b/MaelstromUI.cpp
new file mode 100644
index 00000000..c160fafb
--- /dev/null
+++ b/MaelstromUI.cpp
@@ -0,0 +1,84 @@
+
+#include "MaelstromUI.h"
+#include "Maelstrom_Globals.h"
+#include "UIDialog.h"
+#include "main.h"
+#include "netlogic/about.h"
+#include "netlogic/game.h"
+#include "utils/hashtable.h"
+#include "UIDialogLabel.h"
+#include "UIElementIcon.h"
+#include "UIElementKeyButton.h"
+#include "UIElementLabel.h"
+#include "UIElementSprite.h"
+#include "UIElementTitle.h"
+
+
+MaelstromUI::MaelstromUI(FrameBuf *screen) : UIManager(screen)
+{
+	/* Load up our UI templates */
+	SetLoadPath("UI");
+	LoadTemplates("UITemplates.xml");
+}
+
+MaelstromUI::~MaelstromUI()
+{
+}
+
+SDL_Texture *
+MaelstromUI::CreateText(const char *text, const char *fontName, int fontSize, UIFontStyle fontStyle)
+{
+}
+
+void 
+MaelstromUI::FreeText(SDL_Texture *texture)
+{
+	fontserv->FreeText(texture);
+}
+
+void
+MaelstromUI::PlaySound(int soundID)
+{
+	sound->PlaySound(soundID, 5);
+}
+
+UIPanel *
+MaelstromUI::CreatePanel(const char *type, const char *name)
+{
+	if (strcasecmp(type, "Dialog") == 0) {
+		return new UIDialog(ui, name);
+	}
+	return UIManager::CreatePanel(type, name);
+}
+
+UIPanelDelegate *
+MaelstromUI::CreatePanelDelegate(UIPanel *panel, const char *delegate)
+{
+	if (strcasecmp(delegate, "MainPanel") == 0) {
+		return new MainPanelDelegate(panel);
+	} else if (strcasecmp(delegate, "AboutPanel") == 0) {
+		return new AboutPanelDelegate(panel);
+	} else if (strcasecmp(delegate, "GamePanel") == 0) {
+		return new GamePanelDelegate(panel);
+	}
+	return UIManager::CreatePanelDelegate(panel, delegate);
+}
+
+UIElement *
+MaelstromUI::CreateElement(UIPanel *panel, const char *type)
+{
+	if (strcasecmp(type, "Label") == 0) {
+		return new UIElementLabel(panel);
+	} else if (strcasecmp(type, "DialogLabel") == 0) {
+		return new UIDialogLabel(panel);
+	} else if (strcasecmp(type, "KeyButton") == 0) {
+		return new UIElementKeyButton(panel);
+	} else if (strcasecmp(type, "Icon") == 0) {
+		return new UIElementIcon(panel);
+	} else if (strcasecmp(type, "Sprite") == 0) {
+		return new UIElementSprite(panel);
+	} else if (strcasecmp(type, "Title") == 0) {
+		return new UIElementTitle(panel);
+	}
+	return UIManager::CreateElement(panel, type);;
+}
diff --git a/MaelstromUI.h b/MaelstromUI.h
new file mode 100644
index 00000000..b1590fa6
--- /dev/null
+++ b/MaelstromUI.h
@@ -0,0 +1,32 @@
+
+#include "screenlib/UIManager.h"
+
+class HashTable;
+
+class MaelstromUI : public UIManager
+{
+public:
+	MaelstromUI(FrameBuf *screen);
+	virtual ~MaelstromUI();
+
+	//
+	// UIFontInterface
+	//
+	virtual SDL_Texture *CreateText(const char *text, const char *fontName, int fontSize, UIFontStyle fontStyle);
+	virtual void FreeText(SDL_Texture *texture);
+
+	//
+	// UISoundInterface
+	//
+	virtual void PlaySound(int soundID);
+
+	//
+	// UIManager functions
+	//
+	virtual UIPanel *CreatePanel(const char *type, const char *name);
+	virtual UIPanelDelegate *CreatePanelDelegate(UIPanel *panel, const char *delegate);
+	virtual UIElement *CreateElement(UIPanel *panel, const char *type);
+
+protected:
+	HashTable *m_fonts;
+};
diff --git a/Makefile.am b/Makefile.am
index 1e975918..e685d417 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -5,6 +5,8 @@ bin_PROGRAMS = Maelstrom Maelstrom-netd
 Maelstrom_SOURCES =		\
 	Maelstrom.h		\
 	Maelstrom_Globals.h	\
+	MaelstromUI.cpp		\
+	MaelstromUI.h		\
 	buttonlist.h		\
 	checksum.cpp		\
 	checksum.h		\
@@ -43,11 +45,7 @@ Maelstrom_SOURCES =		\
 	UIElementSprite.cpp	\
 	UIElementSprite.h	\
 	UIElementTitle.cpp	\
-	UIElementTitle.h	\
-	UIElements.cpp		\
-	UIElements.h		\
-	UIPanels.cpp		\
-	UIPanels.h
+	UIElementTitle.h
 
 LOGIC = netlogic
 
diff --git a/UIElements.cpp b/UIElements.cpp
deleted file mode 100644
index 95bd0b6e..00000000
--- a/UIElements.cpp
+++ /dev/null
@@ -1,37 +0,0 @@
-
-#include "UIElements.h"
-#include "screenlib/UIElementButton.h"
-#include "screenlib/UIElementLine.h"
-#include "screenlib/UIElementRect.h"
-#include "UIDialogLabel.h"
-#include "UIElementIcon.h"
-#include "UIElementKeyButton.h"
-#include "UIElementLabel.h"
-#include "UIElementSprite.h"
-#include "UIElementTitle.h"
-
-
-UIElement *
-CreateMaelstromUIElement(UIPanel *panel, const char *type)
-{
-	if (strcasecmp(type, "Line") == 0) {
-		return new UIElementLine(panel);
-	} else if (strcasecmp(type, "Rectangle") == 0) {
-		return new UIElementRect(panel);
-	} else if (strcasecmp(type, "Label") == 0) {
-		return new UIElementLabel(panel);
-	} else if (strcasecmp(type, "DialogLabel") == 0) {
-		return new UIDialogLabel(panel);
-	} else if (strcasecmp(type, "Button") == 0) {
-		return new UIElementButton(panel);
-	} else if (strcasecmp(type, "KeyButton") == 0) {
-		return new UIElementKeyButton(panel);
-	} else if (strcasecmp(type, "Icon") == 0) {
-		return new UIElementIcon(panel);
-	} else if (strcasecmp(type, "Sprite") == 0) {
-		return new UIElementSprite(panel);
-	} else if (strcasecmp(type, "Title") == 0) {
-		return new UIElementTitle(panel);
-	}
-	return NULL;
-}
diff --git a/UIElements.h b/UIElements.h
deleted file mode 100644
index 82d56435..00000000
--- a/UIElements.h
+++ /dev/null
@@ -1,9 +0,0 @@
-#ifndef _UIElements_h
-#define _UIElements_h
-
-class UIPanel;
-class UIElement;
-
-UIElement *CreateMaelstromUIElement(UIPanel *panel, const char *type);
-
-#endif // _UIElements_h
diff --git a/UIPanels.cpp b/UIPanels.cpp
deleted file mode 100644
index ff0a1c1d..00000000
--- a/UIPanels.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-
-#include "screenlib/UIPanel.h"
-#include "UIPanels.h"
-#include "UIDialog.h"
-#include "main.h"
-#include "netlogic/about.h"
-#include "netlogic/game.h"
-
-
-static UIPanelDelegate *
-CreateMaelstromUIDelegate(UIPanel *panel, const char *delegate)
-{
-	if (strcasecmp(delegate, "MainPanel") == 0) {
-		return new MainPanelDelegate(panel);
-	} else if (strcasecmp(delegate, "AboutPanel") == 0) {
-		return new AboutPanelDelegate(panel);
-	} else if (strcasecmp(delegate, "GamePanel") == 0) {
-		return new GamePanelDelegate(panel);
-	} 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 if (strcasecmp(type, "Dialog") == 0) {
-		panel = new UIDialog(ui, name);
-	} else {
-		panel = NULL;
-	}
-
-	if (panel && delegate && *delegate) {
-		panel->SetPanelDelegate(CreateMaelstromUIDelegate(panel, delegate));
-	}
-
-	return panel;
-}
diff --git a/UIPanels.h b/UIPanels.h
deleted file mode 100644
index d8674d80..00000000
--- a/UIPanels.h
+++ /dev/null
@@ -1,9 +0,0 @@
-#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 cc70ce2a..1e5b5189 100644
--- a/init.cpp
+++ b/init.cpp
@@ -8,8 +8,7 @@
 #include "load.h"
 #include "colortable.h"
 #include "fastrand.h"
-#include "UIPanels.h"
-#include "UIElements.h"
+#include "MaelstromUI.h"
 #include "screenlib/UIElement.h"
 
 
@@ -647,12 +646,6 @@ void CleanUp(void)
 	SDL_Quit();
 }
 
-/* This function is called by the UI to play menu sounds */
-static void PlayUISound(void*, int soundID)
-{
-	sound->PlaySound(soundID, 5);
-}
-
 /* ----------------------------------------------------------------- */
 /* -- Perform some initializations and report failure if we choke */
 int DoInitializations(Uint32 window_flags, Uint32 render_flags)
@@ -726,12 +719,6 @@ int DoInitializations(Uint32 window_flags, Uint32 render_flags)
 		}
 	}
 
-	/* Create the UI manager */
-	ui = new UIManager(screen, CreateMaelstromUIPanel, CreateMaelstromUIElement);
-	ui->SetSoundCallback(PlayUISound, NULL);
-	ui->SetLoadPath("UI");
-	ui->LoadTemplates("UITemplates.xml");
-
 	/* Load the Sound Server and initialize sound */
 	sound = new Sound("Maelstrom Sounds", gSoundLevel);
 	if ( sound->Error() ) {
@@ -739,6 +726,9 @@ int DoInitializations(Uint32 window_flags, Uint32 render_flags)
 		return(-1);
 	}
 
+	/* Create the UI manager */
+	ui = new MaelstromUI(screen);
+
 	/* -- We want to access the FULL screen! */
 	SetRect(&gScrnRect, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
 	gStatusLine = (gScrnRect.bottom - gScrnRect.top - STATUS_HEIGHT);
diff --git a/screenlib/Makefile.am b/screenlib/Makefile.am
index 3eb44741..b2c4b0e2 100644
--- a/screenlib/Makefile.am
+++ b/screenlib/Makefile.am
@@ -17,9 +17,11 @@ libSDLscreen_a_SOURCES =	\
 	UIElementRect.h		\
 	UIElementTexture.cpp	\
 	UIElementTexture.h	\
+	UIFontInterface.h	\
 	UIManager.cpp		\
 	UIManager.h		\
 	UIPanel.cpp		\
 	UIPanel.h		\
+	UISoundInterface.h	\
 	UITemplates.cpp		\
 	UITemplates.h
diff --git a/screenlib/UIFontInterface.h b/screenlib/UIFontInterface.h
new file mode 100644
index 00000000..06e0e91e
--- /dev/null
+++ b/screenlib/UIFontInterface.h
@@ -0,0 +1,42 @@
+/*
+    SCREENLIB:  A framebuffer library based on the SDL library
+    Copyright (C) 1997  Sam Lantinga
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Sam Lantinga
+    slouken@libsdl.org
+*/
+
+#ifndef _UIFontInterface_h
+#define _UIFontInterface_h
+
+enum UIFontStyle {
+	UIFONT_STYLE_NORMAL	= 0x00,
+	UIFONT_STYLE_BOLD	= 0x01,
+	UIFONT_STYLE_UNDERLINE	= 0x02,
+	UIFONT_STYLE_ITALIC	= 0x04,
+};
+
+struct SDL_Texture;
+
+class UIFontInterface
+{
+public:
+	virtual SDL_Texture *CreateText(const char *text, const char *fontName, int fontSize, UIFontStyle fontStyle) = 0;
+	virtual void FreeText(SDL_Texture *texture) = 0;
+};
+
+#endif // _UIFontInterface_h
diff --git a/screenlib/UIManager.cpp b/screenlib/UIManager.cpp
index 15ed1254..ae573f0f 100644
--- a/screenlib/UIManager.cpp
+++ b/screenlib/UIManager.cpp
@@ -25,14 +25,13 @@
 #include "SDL_FrameBuf.h"
 #include "UIManager.h"
 #include "UIPanel.h"
+#include "UIElementButton.h"
+#include "UIElementLine.h"
+#include "UIElementRect.h"
 
 
-UIManager::UIManager(FrameBuf *screen, UIPanelFactory panelFactory, UIElementFactory elementFactory) : UIArea(screen)
+UIManager::UIManager(FrameBuf *screen) : UIArea(screen)
 {
-	m_panelFactory = panelFactory;
-	m_elementFactory = elementFactory;
-	m_soundCallback = NULL;
-	m_soundCallbackParam = NULL;
 	m_loadPath = new char[2];
 	strcpy(m_loadPath, ".");
 }
@@ -94,15 +93,6 @@ UIManager::LoadPanel(const char *name)
 		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);
 		fp = PHYSFS_openRead(file);
 		if (!fp) {
@@ -135,17 +125,36 @@ UIManager::LoadPanel(const char *name)
 
 		rapidxml::xml_node<> *node = doc.first_node();
 		rapidxml::xml_attribute<> *attr;
+
+		panel = CreatePanel(node->name(), name);
+		if (!panel) {
+			fprintf(stderr, "Warning: Couldn't create panel %s in %s\n",
+						node->name(), file);
+			delete[] buffer;
+			return NULL;
+		}
+
 		attr = node->first_attribute("delegate", 0, false);
-		panel = (GetPanelFactory())(this, node->name(), name, attr ? attr->value() : NULL);
-		if (panel) {
-			if (!panel->Load(node, GetTemplates()) ||
-			    !panel->FinishLoading()) {
-				fprintf(stderr, "Warning: Couldn't load %s: %s\n",
-							file, panel->Error());
+		if (attr) {
+			UIPanelDelegate *delegate;
+
+			delegate = CreatePanelDelegate(panel, attr->value());
+			if (!delegate) {
+				fprintf(stderr, "Warning: Couldn't find delegate '%s'\n", attr->value());
 				delete[] buffer;
 				delete panel;
 				return NULL;
 			}
+			panel->SetPanelDelegate(delegate);
+		}
+		
+		if (!panel->Load(node, GetTemplates()) ||
+		    !panel->FinishLoading()) {
+			fprintf(stderr, "Warning: Couldn't load %s: %s\n",
+						file, panel->Error());
+			delete[] buffer;
+			delete panel;
+			return NULL;
 		}
 		delete[] buffer;
 	}
@@ -256,3 +265,31 @@ UIManager::HandleEvent(const SDL_Event &event)
 	}
 	return false;
 }
+
+UIPanel *
+UIManager::CreatePanel(const char *type, const char *name)
+{
+	if (strcasecmp(type, "UIPanel") == 0) {
+		return new UIPanel(this, name);
+	}
+	return NULL;
+}
+
+UIPanelDelegate *
+UIManager::CreatePanelDelegate(UIPanel *panel, const char *delegate)
+{
+	return NULL;
+}
+
+UIElement *
+UIManager::CreateElement(UIPanel *panel, const char *type)
+{
+	if (strcasecmp(type, "Line") == 0) {
+		return new UIElementLine(panel);
+	} else if (strcasecmp(type, "Rectangle") == 0) {
+		return new UIElementRect(panel);
+	} else if (strcasecmp(type, "Button") == 0) {
+		return new UIElementButton(panel);
+	}
+	return NULL;
+}
diff --git a/screenlib/UIManager.h b/screenlib/UIManager.h
index 6f97016e..fe38d54a 100644
--- a/screenlib/UIManager.h
+++ b/screenlib/UIManager.h
@@ -26,32 +26,24 @@
 #include "SDL.h"
 #include "../utils/array.h"
 #include "UIArea.h"
+#include "UIFontInterface.h"
+#include "UISoundInterface.h"
 #include "UITemplates.h"
 
 class FrameBuf;
-class UIManager;
 class UIPanel;
+class UIPanelDelegate;
 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
+class UIManager : public UIArea, public UIFontInterface, public UISoundInterface
 {
 public:
-	UIManager(FrameBuf *screen, UIPanelFactory panelFactory, UIElementFactory elementFactory);
-	~UIManager();
+	UIManager(FrameBuf *screen);
+	virtual ~UIManager();
 
 	FrameBuf *GetScreen() const {
 		return m_screen;
 	}
-	UIPanelFactory GetPanelFactory() const {
-		return m_panelFactory;
-	}
-	UIElementFactory GetElementFactory() const {
-		return m_elementFactory;
-	}
 	const UITemplates *GetTemplates() const {
 		return &m_templates;
 	}
@@ -86,24 +78,15 @@ class UIManager : public UIArea
 		DeletePanel(GetPanel(name));
 	}
 
-	void SetSoundCallback(UISoundCallback callback, void *param) {
-		m_soundCallback = callback;
-		m_soundCallbackParam = param;
-	}
-	void PlaySound(int soundID) {
-		if (m_soundCallback) {
-			m_soundCallback(m_soundCallbackParam, soundID);
-		}
-	}
-			
 	void Draw(bool fullUpdate = true);
 	bool HandleEvent(const SDL_Event &event);
 
+public:
+	virtual UIPanel *CreatePanel(const char *type, const char *name);
+	virtual UIPanelDelegate *CreatePanelDelegate(UIPanel *panel, const char *delegate);
+	virtual UIElement *CreateElement(UIPanel *panel, const char *type);
+
 protected:
-	UIPanelFactory m_panelFactory;
-	UIElementFactory m_elementFactory;
-	UISoundCallback m_soundCallback;
-	void *m_soundCallbackParam;
 	char *m_loadPath;
 	UITemplates m_templates;
 	array<UIPanel *> m_panels;
diff --git a/screenlib/UIPanel.cpp b/screenlib/UIPanel.cpp
index 791ea7b0..074f3127 100644
--- a/screenlib/UIPanel.cpp
+++ b/screenlib/UIPanel.cpp
@@ -125,7 +125,7 @@ bool
 UIPanel::LoadElements(rapidxml::xml_node<> *node, const UITemplates *templates)
 {
 	for (node = node->first_node(); node; node = node->next_sibling()) {
-		UIElement *element = (m_ui->GetElementFactory())(this, node->name());
+		UIElement *element = m_ui->CreateElement(this, node->name());
 		if (!element) {
 			fprintf(stderr, "Warning: Couldn't find handler for element %s\n", node->name());
 			continue;
diff --git a/screenlib/UISoundInterface.h b/screenlib/UISoundInterface.h
new file mode 100644
index 00000000..bd125db6
--- /dev/null
+++ b/screenlib/UISoundInterface.h
@@ -0,0 +1,32 @@
+/*
+    SCREENLIB:  A framebuffer library based on the SDL library
+    Copyright (C) 1997  Sam Lantinga
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Sam Lantinga
+    slouken@libsdl.org
+*/
+
+#ifndef _UISoundInterface_h
+#define _UISoundInterface_h
+
+class UISoundInterface
+{
+public:
+	virtual void PlaySound(int soundID) = 0;
+};
+
+#endif // _UISoundInterface_h