Maelstrom: Added separate drawing layers so popups can draw over the rest of the UI.

https://github.com/libsdl-org/Maelstrom/commit/2326838e34b5e09be00a9ca4fcf0f4c92fe4114a

From 2326838e34b5e09be00a9ca4fcf0f4c92fe4114a Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Sun, 21 Oct 2012 13:46:37 -0700
Subject: [PATCH] Added separate drawing layers so popups can draw over the
 rest of the UI.

---
 game/MacDialog.cpp          | 76 +++++++++++++++++++------------------
 game/MacDialog.h            |  2 +-
 game/about.cpp              |  6 ++-
 game/about.h                |  2 +-
 game/game.cpp               |  6 ++-
 game/game.h                 |  2 +-
 screenlib/UIBaseElement.cpp | 47 ++++++++++++++++++++++-
 screenlib/UIBaseElement.h   | 15 +++++++-
 screenlib/UIElement.cpp     |  6 +--
 screenlib/UIElement.h       |  2 +-
 screenlib/UIManager.cpp     |  4 +-
 screenlib/UIPanel.cpp       | 10 ++---
 screenlib/UIPanel.h         |  7 +---
 13 files changed, 124 insertions(+), 61 deletions(-)

diff --git a/game/MacDialog.cpp b/game/MacDialog.cpp
index ca9a5242..0f88b7d8 100644
--- a/game/MacDialog.cpp
+++ b/game/MacDialog.cpp
@@ -84,51 +84,55 @@ MacDialog::Show()
 }
 
 void
-MacDialog::Draw()
+MacDialog::Draw(DRAWLEVEL drawLevel)
 {
-	int x, y, w, h;
-	int maxx, maxy;
+	if (drawLevel == DRAWLEVEL_BACKGROUND) {
+		int x, y, w, h;
+		int maxx, maxy;
+
+		if (m_step < EXPAND_STEPS) {
+			w = (Width()*m_step)/EXPAND_STEPS;
+			h = (Height()*m_step)/EXPAND_STEPS;
+			x = X() + Width()/2 - (w/2);
+			y = Y() + Height()/2 - (h/2);
+		} else {
+			w = Width();
+			h = Height();
+			x = X();
+			y = Y();
+		}
 
-	if (m_step < EXPAND_STEPS) {
-		w = (Width()*m_step)/EXPAND_STEPS;
-		h = (Height()*m_step)/EXPAND_STEPS;
-		x = X() + Width()/2 - (w/2);
-		y = Y() + Height()/2 - (h/2);
-	} else {
-		w = Width();
-		h = Height();
-		x = X();
-		y = Y();
+		/* The border is 4 pixels around the area of the dialog */
+		w += 8;
+		h += 8;
+		x -= 4;
+		y -= 4;
+		maxx = x+w-1;
+		maxy = y+h-1;
+
+		/* Draw the dialog border and background color */
+		m_screen->DrawLine(x, y, maxx, y, m_colors[COLOR_LIGHT]);
+		m_screen->DrawLine(x, y, x, maxy, m_colors[COLOR_LIGHT]);
+		m_screen->DrawLine(x, maxy, maxx, maxy, m_colors[COLOR_DARK]);
+		m_screen->DrawLine(maxx, y, maxx, maxy, m_colors[COLOR_DARK]);
+		m_screen->DrawRect(x+1, y+1, w-2, h-2, m_colors[COLOR_MEDIUM]);
+		m_screen->DrawLine(x+2, y+2, maxx-2, y+2, m_colors[COLOR_DARK]);
+		m_screen->DrawLine(x+2, y+2, x+2, maxy-2, m_colors[COLOR_DARK]);
+		m_screen->DrawLine(x+3, maxy-2, maxx-2, maxy-2, m_colors[COLOR_LIGHT]);
+		m_screen->DrawLine(maxx-2, y+3, maxx-2, maxy-2, m_colors[COLOR_LIGHT]);
+		m_screen->DrawRect(x+3, y+3, w-6, h-6, m_colors[COLOR_BLACK]);
+		m_screen->FillRect(x+4, y+4, w-8, h-8, m_colors[COLOR_WHITE]);
 	}
 
-	/* The border is 4 pixels around the area of the dialog */
-	w += 8;
-	h += 8;
-	x -= 4;
-	y -= 4;
-	maxx = x+w-1;
-	maxy = y+h-1;
-
-	/* Draw the dialog border and background color */
-	m_screen->DrawLine(x, y, maxx, y, m_colors[COLOR_LIGHT]);
-	m_screen->DrawLine(x, y, x, maxy, m_colors[COLOR_LIGHT]);
-	m_screen->DrawLine(x, maxy, maxx, maxy, m_colors[COLOR_DARK]);
-	m_screen->DrawLine(maxx, y, maxx, maxy, m_colors[COLOR_DARK]);
-	m_screen->DrawRect(x+1, y+1, w-2, h-2, m_colors[COLOR_MEDIUM]);
-	m_screen->DrawLine(x+2, y+2, maxx-2, y+2, m_colors[COLOR_DARK]);
-	m_screen->DrawLine(x+2, y+2, x+2, maxy-2, m_colors[COLOR_DARK]);
-	m_screen->DrawLine(x+3, maxy-2, maxx-2, maxy-2, m_colors[COLOR_LIGHT]);
-	m_screen->DrawLine(maxx-2, y+3, maxx-2, maxy-2, m_colors[COLOR_LIGHT]);
-	m_screen->DrawRect(x+3, y+3, w-6, h-6, m_colors[COLOR_BLACK]);
-	m_screen->FillRect(x+4, y+4, w-8, h-8, m_colors[COLOR_WHITE]);
-
 	/* Don't draw the controls until we've finished expanding */
 	if (m_step < EXPAND_STEPS) {
-		++m_step;
+		if (drawLevel == NUM_DRAWLEVELS-1) {
+			++m_step;
+		}
 		return;
 	}
 
-	UIDialog::Draw();
+	UIDialog::Draw(drawLevel);
 }
 
 //////////////////////////////////////////////////////////////////////////////
diff --git a/game/MacDialog.h b/game/MacDialog.h
index 03dc4617..b2492b86 100644
--- a/game/MacDialog.h
+++ b/game/MacDialog.h
@@ -35,7 +35,7 @@ DECLARE_TYPESAFE_CLASS(UIDialog)
 	override bool Load(rapidxml::xml_node<> *node, const UITemplates *templates);
 
 	override void Show();
-	override void Draw();
+	override void Draw(DRAWLEVEL drawLevel);
 
 protected:
 	enum {
diff --git a/game/about.cpp b/game/about.cpp
index ff949a42..b8ea844c 100644
--- a/game/about.cpp
+++ b/game/about.cpp
@@ -92,10 +92,14 @@ AboutPanelDelegate::OnHide()
 }
 
 void
-AboutPanelDelegate::OnDraw()
+AboutPanelDelegate::OnDraw(DRAWLEVEL drawLevel)
 {
 	int i;
 
+	if ( drawLevel != DRAWLEVEL_NORMAL ) {
+		return;
+	}
+
 	for ( i=0; i<numsprites; ++i ) {
 		objects[i]->Move(0);
 		objects[i]->BlitSprite();
diff --git a/game/about.h b/game/about.h
index 8d610a9a..9e8c8194 100644
--- a/game/about.h
+++ b/game/about.h
@@ -39,7 +39,7 @@ class AboutPanelDelegate : public UIPanelDelegate
 
 	virtual void OnShow();
 	virtual void OnHide();
-	virtual void OnDraw();
+	virtual void OnDraw(DRAWLEVEL drawLevel);
 
 protected:
 	int numsprites;
diff --git a/game/game.cpp b/game/game.cpp
index f3f0e28e..d56cab8b 100644
--- a/game/game.cpp
+++ b/game/game.cpp
@@ -404,10 +404,14 @@ GamePanelDelegate::OnTick()
 }
 
 void
-GamePanelDelegate::OnDrawBackground()
+GamePanelDelegate::OnDraw(DRAWLEVEL drawLevel)
 {
 	int i;
 
+	if (drawLevel != DRAWLEVEL_BACKGROUND) {
+		return;
+	}
+
 	/* Draw the status frame */
 	DrawStatus(false);
 
diff --git a/game/game.h b/game/game.h
index a3d17984..d5d7ba45 100644
--- a/game/game.h
+++ b/game/game.h
@@ -37,7 +37,7 @@ class GamePanelDelegate : public UIPanelDelegate
 	virtual void OnShow();
 	virtual void OnHide();
 	virtual void OnTick();
-	virtual void OnDrawBackground();
+	virtual void OnDraw(DRAWLEVEL drawLevel);
 	virtual bool OnAction(UIBaseElement *sender, const char *action);
 
 protected:
diff --git a/screenlib/UIBaseElement.cpp b/screenlib/UIBaseElement.cpp
index f6cc2eea..8c7abfb7 100644
--- a/screenlib/UIBaseElement.cpp
+++ b/screenlib/UIBaseElement.cpp
@@ -42,6 +42,7 @@ UIBaseElement::UIBaseElement(UIManager *ui, const char *name) :
 	m_shown = true;
 	m_disabled = false;
 	m_parentDisabled = false;
+	m_drawLevel = DRAWLEVEL_NORMAL;
 }
 
 UIBaseElement::UIBaseElement(UIBaseElement *parent, const char *name) :
@@ -54,6 +55,7 @@ UIBaseElement::UIBaseElement(UIBaseElement *parent, const char *name) :
 	m_shown = true;
 	m_disabled = false;
 	m_parentDisabled = parent->IsDisabled();
+	m_drawLevel = DRAWLEVEL_NORMAL;
 }
 
 UIBaseElement::~UIBaseElement()
@@ -95,6 +97,11 @@ UIBaseElement::Load(rapidxml::xml_node<> *node, const UITemplates *templates)
 		}
 	}
 
+	DRAWLEVEL drawLevel;
+	if (LoadDrawLevel(node, "drawLevel", drawLevel)) {
+		SetDrawLevel(drawLevel);
+	}
+
 	return true;
 }
 
@@ -173,11 +180,21 @@ UIBaseElement::UpdateDisabledState()
 }
 
 void
-UIBaseElement::Draw()
+UIBaseElement::SetDrawLevel(DRAWLEVEL drawLevel)
+{
+	m_drawLevel = drawLevel;
+
+	for (int i = 0; i < m_elements.length(); ++i) {
+		m_elements[i]->SetDrawLevel(drawLevel);
+	}
+}
+
+void
+UIBaseElement::Draw(DRAWLEVEL drawLevel)
 {
 	for (int i = 0; i < m_elements.length(); ++i) {
 		if (m_elements[i]->IsShown()) {
-			m_elements[i]->Draw();
+			m_elements[i]->Draw(drawLevel);
 		}
 	}
 }
@@ -242,3 +259,29 @@ UIBaseElement::LoadElements(rapidxml::xml_node<> *node, const UITemplates *templ
 	}
 	return true;
 }
+
+bool
+UIBaseElement::LoadDrawLevel(rapidxml::xml_node<> *node, const char *name, DRAWLEVEL &value)
+{
+	rapidxml::xml_attribute<> *attr;
+
+	attr = node->first_attribute(name, 0, false);
+	if (attr) {
+		static const char *s_drawLevels[] = {
+			"background",
+			"normal",
+			"popup"
+		};
+		SDL_COMPILE_TIME_ASSERT(drawLevels, SDL_arraysize(s_drawLevels) == NUM_DRAWLEVELS);
+
+		for (int i = 0; i < NUM_DRAWLEVELS; ++i) {
+			if (SDL_strcasecmp(attr->value(), s_drawLevels[i]) == 0) {
+				value = (DRAWLEVEL)i;
+				return true;
+			}
+		}
+		fprintf(stderr, "Warning: Unknown draw level: %s\n", attr->value());
+		return false;
+	}
+	return false;
+}
diff --git a/screenlib/UIBaseElement.h b/screenlib/UIBaseElement.h
index a0c85e5d..685f09b4 100644
--- a/screenlib/UIBaseElement.h
+++ b/screenlib/UIBaseElement.h
@@ -35,6 +35,14 @@ class UITemplates;
 
 typedef int UIElementType;
 
+enum DRAWLEVEL
+{
+	DRAWLEVEL_BACKGROUND,
+	DRAWLEVEL_NORMAL,
+	DRAWLEVEL_POPUP,
+	NUM_DRAWLEVELS
+};
+
 class UIBaseElement : public UIArea
 {
 public:
@@ -194,7 +202,10 @@ class UIBaseElement : public UIArea
 		return m_disabled || m_parentDisabled;
 	}
 
-	virtual void Draw();
+	void SetDrawLevel(DRAWLEVEL drawLevel);
+	DRAWLEVEL GetDrawLevel() const { return m_drawLevel; }
+
+	virtual void Draw(DRAWLEVEL drawLevel);
 	virtual bool HandleEvent(const SDL_Event &event);
 	virtual void Action(UIBaseElement *sender, const char *action);
 
@@ -210,12 +221,14 @@ class UIBaseElement : public UIArea
 	bool m_shown;
 	bool m_disabled;
 	bool m_parentDisabled;
+	DRAWLEVEL m_drawLevel;
 	array<UIBaseElement *> m_elements;
 
 protected:
 	UIBaseElement *CreateElement(const char *type);
 
 	bool LoadElements(rapidxml::xml_node<> *node, const UITemplates *templates);
+	bool LoadDrawLevel(rapidxml::xml_node<> *node, const char *name, DRAWLEVEL &value);
 
 	virtual void UpdateDisabledState();
 
diff --git a/screenlib/UIElement.cpp b/screenlib/UIElement.cpp
index 6c64c131..b4b73109 100644
--- a/screenlib/UIElement.cpp
+++ b/screenlib/UIElement.cpp
@@ -504,13 +504,13 @@ UIElement::SetImage(UITexture *image)
 }
 
 void
-UIElement::Draw()
+UIElement::Draw(DRAWLEVEL drawLevel)
 {
-	if (m_drawEngine) {
+	if (m_drawEngine && drawLevel == GetDrawLevel()) {
 		m_drawEngine->OnDraw();
 	}
 
-	UIBaseElement::Draw();
+	UIBaseElement::Draw(drawLevel);
 }
 
 bool
diff --git a/screenlib/UIElement.h b/screenlib/UIElement.h
index ae60c774..7e3f39cc 100644
--- a/screenlib/UIElement.h
+++ b/screenlib/UIElement.h
@@ -168,7 +168,7 @@ DECLARE_TYPESAFE_CLASS(UIBaseElement)
 	}
 
 	// Draw!
-	override void Draw();
+	override void Draw(DRAWLEVEL drawLevel);
 
 	// Events
 	override bool HandleEvent(const SDL_Event &event);
diff --git a/screenlib/UIManager.cpp b/screenlib/UIManager.cpp
index e14bf46f..48f32007 100644
--- a/screenlib/UIManager.cpp
+++ b/screenlib/UIManager.cpp
@@ -320,7 +320,9 @@ UIManager::Draw(bool fullUpdate)
 	for (i = 0; i < m_visible.length(); ++i) {
 		UIPanel *panel = m_visible[i];
 
-		panel->Draw();
+		for (int drawLevel = 0; drawLevel < NUM_DRAWLEVELS; ++drawLevel) {
+			panel->Draw((DRAWLEVEL)drawLevel);
+		}
 	}
 	if (fullUpdate) {
 		m_screen->Update();
diff --git a/screenlib/UIPanel.cpp b/screenlib/UIPanel.cpp
index 5919d0ef..d59f6dbd 100644
--- a/screenlib/UIPanel.cpp
+++ b/screenlib/UIPanel.cpp
@@ -152,16 +152,12 @@ UIPanel::Tick()
 }
 
 void
-UIPanel::Draw()
+UIPanel::Draw(DRAWLEVEL drawLevel)
 {
-	if (m_delegate) {
-		m_delegate->OnDrawBackground();
-	}
-
-	UIBaseElement::Draw();
+	UIBaseElement::Draw(drawLevel);
 
 	if (m_delegate) {
-		m_delegate->OnDraw();
+		m_delegate->OnDraw(drawLevel);
 	}
 }
 
diff --git a/screenlib/UIPanel.h b/screenlib/UIPanel.h
index ed294c14..5c364376 100644
--- a/screenlib/UIPanel.h
+++ b/screenlib/UIPanel.h
@@ -55,12 +55,9 @@ class UIPanelDelegate
 	// This is called once per frame before the screen is drawn
 	virtual void OnTick() { }
 
-	// This is called before the panel is drawn
-	virtual void OnDrawBackground() { }
-
 	// This is called after the panel is drawn, before the screen
 	// is updated, to allow any additional custom drawing.
-	virtual void OnDraw() { }
+	virtual void OnDraw(DRAWLEVEL drawLevel) { }
 
 	// This is called for events not handled by UI elements
 	virtual bool HandleEvent(const SDL_Event &event) { return false; }
@@ -99,7 +96,7 @@ DECLARE_TYPESAFE_CLASS(UIBaseElement)
 
 	virtual void Poll();
 	virtual void Tick();
-	override void Draw();
+	override void Draw(DRAWLEVEL drawLevel);
 	override bool HandleEvent(const SDL_Event &event);
 	override void Action(UIBaseElement *sender, const char *action);