Maelstrom: Better organization of the relationship between the UI manager and individual panels.

From e41ce95e74b3c37311d5f3c842a1ed86ff16c7fc Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Sun, 23 Oct 2011 01:14:12 -0400
Subject: [PATCH] Better organization of the relationship between the UI
 manager and individual panels.

---
 init.cpp                | 31 +++++++++++++++-------
 screenlib/UIManager.cpp | 57 ++++++++++++++++++++++++++++++++++++-----
 screenlib/UIManager.h   | 41 +++++++++++++++++++++++++----
 screenlib/UIPanel.cpp   | 27 ++++++++++---------
 screenlib/UIPanel.h     | 16 +++---------
 utils/array.h           |  8 ++++++
 6 files changed, 134 insertions(+), 46 deletions(-)

diff --git a/init.cpp b/init.cpp
index 23b3d4f2..cd9ca23f 100644
--- a/init.cpp
+++ b/init.cpp
@@ -16,7 +16,7 @@ Sound    *sound = NULL;
 FontServ *fontserv = NULL;
 MFont    *fonts[NUM_FONTS];
 FrameBuf *screen = NULL;
-UIManager ui;
+UIManager *ui = NULL;
 
 Sint32	gLastHigh;
 Uint32	gLastDrawn;
@@ -64,13 +64,20 @@ static int LoadSmallSprite(Mac_Resource *spriteres,
 /* Put up an Ambrosia Software splash screen */
 void DoSplash(void)
 {
-	UIPanel panel(screen);
+	UIPanel *panel;
 
 	screen->Clear();
-	if (panel.Load("splash.xml")) {
-		panel.Draw();
+
+	panel = ui->LoadPanel("splash");
+	if (panel) {
+		ui->ShowPanel(panel);
+		ui->Draw();
 	}
 	screen->Update();
+
+	if (panel) {
+		delete panel;
+	}
 }
 
 /* ----------------------------------------------------------------- */
@@ -686,6 +693,10 @@ void CleanUp(void)
 	int i;
 
 	HaltLogic();
+	if ( ui ) {
+		delete ui;
+		ui = NULL;
+	}
 	if ( fontserv ) {
 		for ( i = 0; i < NUM_FONTS; ++i ) {
 			if ( fonts[i] ) {
@@ -695,14 +706,14 @@ void CleanUp(void)
 		delete fontserv;
 		fontserv = NULL;
 	}
-	if ( sound ) {
-		delete sound;
-		sound = NULL;
-	}
 	if ( screen ) {
 		delete screen;
 		screen = NULL;
 	}
+	if ( sound ) {
+		delete sound;
+		sound = NULL;
+	}
 	SaveControls();
 	PHYSFS_deinit();
 	SDL_Quit();
@@ -779,7 +790,9 @@ int DoInitializations(Uint32 window_flags, Uint32 render_flags)
 			return(-1);
 		}
 	}
-	UIPanel::SetElementFactory(CreateMaelstromUIElement);
+
+	/* Create the UI manager */
+	ui = new UIManager(screen, CreateMaelstromUIElement);
 
 	/* Load the Sound Server and initialize sound */
 	sound = new Sound("Maelstrom Sounds", gSoundLevel);
diff --git a/screenlib/UIManager.cpp b/screenlib/UIManager.cpp
index 57a38256..41dabe55 100644
--- a/screenlib/UIManager.cpp
+++ b/screenlib/UIManager.cpp
@@ -24,6 +24,36 @@
 #include "UIPanel.h"
 
 
+UIManager::UIManager(FrameBuf *screen, UIElementFactory factory) : ErrorBase()
+{
+	m_screen = screen;
+	m_elementFactory = factory;
+}
+
+UIManager::~UIManager()
+{
+	/* Deleting the panels will remove them from the manager */
+	while (m_panels.length() > 0) {
+		delete m_panels[m_panels.length()-1];
+	}
+}
+
+UIPanel *
+UIManager::LoadPanel(const char *name)
+{
+	UIPanel *panel;
+	char file[1024];
+
+	sprintf(file, "%s.xml", name);
+	panel = new UIPanel(this, name);
+	if (!panel->Load(file)) {
+		SetError("%s", panel->Error());
+		delete panel;
+		return false;
+	}
+	return panel;
+}
+
 UIPanel *
 UIManager::GetPanel(const char *name)
 {
@@ -35,21 +65,36 @@ UIManager::GetPanel(const char *name)
 	return NULL;
 }
 
+void
+UIManager::ShowPanel(UIPanel *panel)
+{
+	if (panel && !m_visible.find(panel)) {
+		m_visible.add(panel);
+		panel->Show();
+	}
+}
+
+void
+UIManager::HidePanel(UIPanel *panel)
+{
+	if (panel && m_visible.remove(panel)) {
+		panel->Hide();
+	}
+}
+
 void
 UIManager::Draw()
 {
-	for (unsigned i = 0; i < m_panels.length(); ++i) {
-		if (m_panels[i]->IsShown()) {
-			m_panels[i]->Draw();
-		}
+	for (unsigned i = 0; i < m_visible.length(); ++i) {
+		m_visible[i]->Draw();
 	}
 }
 
 bool
 UIManager::HandleEvent(const SDL_Event &event)
 {
-	for (unsigned i = m_panels.length(); i--; ) {
-		if (m_panels[i]->HandleEvent(event)) {
+	for (unsigned i = m_visible.length(); i--; ) {
+		if (m_visible[i]->HandleEvent(event)) {
 			return true;
 		}
 	}
diff --git a/screenlib/UIManager.h b/screenlib/UIManager.h
index 40567352..661e6e9d 100644
--- a/screenlib/UIManager.h
+++ b/screenlib/UIManager.h
@@ -24,28 +24,59 @@
 #define _UIManager_h
 
 #include "SDL.h"
-#include "array.h"
+#include "../utils/array.h"
+#include "ErrorBase.h"
 
+class FrameBuf;
 class UIPanel;
+class UIElement;
 
-class UIManager
+typedef UIElement *(*UIElementFactory)(UIPanel *panel, const char *name);
+
+class UIManager : public ErrorBase
 {
 public:
-	UIManager() { }
+	UIManager(FrameBuf *screen, UIElementFactory factory);
+	~UIManager();
 
-	void AddPanel(UIPanel *panel) {
-		m_panels.add(panel);
+	FrameBuf *GetScreen() const {
+		return m_screen;
+	}
+	UIElementFactory GetElementFactory() const {
+		return m_elementFactory;
 	}
+
+	UIPanel *LoadPanel(const char *name);
 	UIPanel *GetPanel(const char *name);
+
+	/* These are called by the UIPanel class */
+	void AddPanel(UIPanel *panel) {
+		if (!m_panels.find(panel)) {
+			m_panels.add(panel);
+		}
+	}
 	void RemovePanel(UIPanel *panel) {
+		m_visible.remove(panel);
 		m_panels.remove(panel);
 	}
 
+	void ShowPanel(UIPanel *panel);
+	void ShowPanel(const char *name) {
+		ShowPanel(GetPanel(name));
+	}
+	void HidePanel(UIPanel *panel);
+	void HidePanel(const char *name) {
+		HidePanel(GetPanel(name));
+	}
+
 	void Draw();
 	bool HandleEvent(const SDL_Event &event);
 
 protected:
+	FrameBuf *m_screen;
+	UIElementFactory m_elementFactory;
 	array<UIPanel *> m_panels;
+	array<UIPanel *> m_visible;
 };
 
 #endif // _UIManager_h
diff --git a/screenlib/UIPanel.cpp b/screenlib/UIPanel.cpp
index 317843ec..ea69a07e 100644
--- a/screenlib/UIPanel.cpp
+++ b/screenlib/UIPanel.cpp
@@ -24,22 +24,28 @@
 
 #include "SDL_FrameBuf.h"
 #include "UIPanel.h"
+#include "UIManager.h"
 #include "UIElement.h"
 
-UIElementFactory UIPanel::s_elementFactory;
 
-UIPanel::UIPanel(FrameBuf *screen, const char *name) : UIArea()
+UIPanel::UIPanel(UIManager *ui, const char *name) : UIArea()
 {
-	m_screen = screen;
+	m_ui = ui;
+	m_screen = ui->GetScreen();
 	m_name = new char[strlen(name)+1];
 	strcpy(m_name, name);
 
-	m_rect.w = screen->Width();
-	m_rect.h = screen->Height();
+	m_rect.w = m_screen->Width();
+	m_rect.h = m_screen->Height();
+	m_shown = false;
+
+	m_ui->AddPanel(this);
 }
 
 UIPanel::~UIPanel()
 {
+	m_ui->RemovePanel(this);
+
 	delete[] m_name;
 
 	for (unsigned i = 0; i < m_elements.length(); ++i) {
@@ -56,7 +62,7 @@ UIPanel::Load(const char *file)
 
 	ClearError();
 
-	if (!s_elementFactory) {
+	if (!m_ui->GetElementFactory()) {
 		SetError("No panel element factory set");
 		return false;
 	}
@@ -95,13 +101,6 @@ UIPanel::Load(const char *file)
 		delete[] buffer;
 		return false;
 	}
-	attr = node->first_attribute("name", 0, false);;
-	if (attr) {
-		const char *name = node->value();
-		delete[] m_name;
-		m_name = new char[strlen(name)+1];
-		strcpy(m_name, name);
-	}
 	if (!UIArea::Load(node)) {
 		delete[] buffer;
 		return false;
@@ -121,7 +120,7 @@ bool
 UIPanel::LoadElements(rapidxml::xml_node<> *node)
 {
 	for (node = node->first_node(); node; node = node->next_sibling()) {
-		UIElement *element = s_elementFactory(this, node->name());
+		UIElement *element = (m_ui->GetElementFactory())(this, node->name());
 		if (!element) {
 			SetError("Couldn't find handler for element %s", node->name());
 			return false;
diff --git a/screenlib/UIPanel.h b/screenlib/UIPanel.h
index 681f7893..54056b85 100644
--- a/screenlib/UIPanel.h
+++ b/screenlib/UIPanel.h
@@ -31,20 +31,14 @@
 #include "UIArea.h"
 
 class FrameBuf;
-class UIElement;
+class UIManager;
 class UIPanel;
-
-typedef UIElement *(*UIElementFactory)(UIPanel *panel, const char *name);
+class UIElement;
 
 class UIPanel : public UIArea
 {
 public:
-	static void SetElementFactory(UIElementFactory function) {
-		s_elementFactory = function;
-	}
-
-public:
-	UIPanel(FrameBuf *screen, const char *name = "");
+	UIPanel(UIManager *ui, const char *name);
 	virtual ~UIPanel();
 
 	FrameBuf *GetScreen() const {
@@ -70,14 +64,12 @@ class UIPanel : public UIArea
 	bool HandleEvent(const SDL_Event &event);
 
 protected:
+	UIManager *m_ui;
 	FrameBuf *m_screen;
 	char *m_name;
 	array<UIElement *> m_elements;
 
 	bool LoadElements(rapidxml::xml_node<> *node);
-
-protected:
-	static UIElementFactory s_elementFactory;
 };
 
 #endif // _UIPanel_h
diff --git a/utils/array.h b/utils/array.h
index 0d273307..20a9de4c 100644
--- a/utils/array.h
+++ b/utils/array.h
@@ -16,6 +16,14 @@ class array
 	array() : m_len(0), m_max(1), m_data((T*)malloc(sizeof(T))) { }
 	~array() { free(m_data); }
 
+	bool find(const T& item) {
+		for (unsigned i = 0; i < m_len; ++i) {
+			if (m_data[i] == item) {
+				return true;
+			}
+		}
+		return false;
+	}
 	void add(const T& item) {
 		resize(m_len+1);
 		m_data[m_len++] = item;