Maelstrom: Added UI sounds for panels and buttons

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

From ca76f0e28e3e00dfc864073b2d87f24c9dd443a1 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Tue, 25 Oct 2011 00:40:31 -0400
Subject: [PATCH] Added UI sounds for panels and buttons Added catch-all key
 handling buttons

---
 UI/loading.xml                |  2 +-
 UI/main.xml                   | 17 +++++-----
 init.cpp                      |  8 ++++-
 main.cpp                      | 47 +++-------------------------
 screenlib/SDL_FrameBuf.cpp    |  2 +-
 screenlib/UIArea.h            |  4 +--
 screenlib/UIElementButton.cpp | 58 +++++++++++++++++++++++++++++------
 screenlib/UIElementButton.h   |  4 +++
 screenlib/UIManager.cpp       |  2 ++
 screenlib/UIManager.h         | 13 ++++++++
 screenlib/UIPanel.cpp         | 28 +++++++++++++++++
 screenlib/UIPanel.h           |  5 +++
 12 files changed, 126 insertions(+), 64 deletions(-)

diff --git a/UI/loading.xml b/UI/loading.xml
index 54ce8163..902f5c77 100644
--- a/UI/loading.xml
+++ b/UI/loading.xml
@@ -1,4 +1,4 @@
-<UIPanel>
+<UIPanel enterSound="111" leaveSound="123">
 	<Elements>
 		<Title name="image" id="130">
 			<Anchor anchorFrom="CENTER" anchorTo="CENTER"/>
diff --git a/UI/main.xml b/UI/main.xml
index 9a66d696..5d9fb0d4 100644
--- a/UI/main.xml
+++ b/UI/main.xml
@@ -4,6 +4,9 @@
 			<Anchor anchorFrom="TOPLEFT" anchorTo="CENTER" x="-251" y="-187"/>
 		</Title>
 
+		<!-- Catch-all button to bonk when a key is pressed -->
+		<Button hotkey="any" clickSound="108"/>
+
 		<!-- screen frame -->
 		<Rectangle>
 			<Color r="0x75" g="0x75" b="0xFF"/>
@@ -203,35 +206,35 @@
 		</Label>
 
 		<!-- Instructions -->
-		<KeyButton name="PlayButton" hotkey="P">
+		<KeyButton name="PlayButton" hotkey="P" clickSound="114">
 			<Anchor anchorFrom="TOPLEFT" anchorTo="TOPRIGHT" anchor="vertical_divider" x="9" y="10"/>
 		</KeyButton>
 		<Label fontName="Geneva" fontSize="9" fontStyle="BOLD" text=" Start playing Maelstrom">
 			<Color r="0xFF" g="0xFF" b="0x00"/>
 			<Anchor anchorFrom="BOTTOMLEFT" anchorTo="TOPRIGHT" anchor="PlayButton" x="3" y="21"/>
 		</Label>
-		<KeyButton name="ControlsButton" hotkey="C">
+		<KeyButton name="ControlsButton" hotkey="C" clickSound="119">
 			<Anchor anchorFrom="TOPLEFT" anchorTo="TOPLEFT" anchor="PlayButton" y="34"/>
 		</KeyButton>
 		<Label fontName="Geneva" fontSize="9" fontStyle="BOLD" text=" Configure the game controls">
 			<Color r="0xFF" g="0xFF" b="0x00"/>
 			<Anchor anchorFrom="BOTTOMLEFT" anchorTo="TOPRIGHT" anchor="ControlsButton" x="3" y="21"/>
 		</Label>
-		<KeyButton name="ZapButton" hotkey="Z">
+		<KeyButton name="ZapButton" hotkey="Z" clickSound="107">
 			<Anchor anchorFrom="TOPLEFT" anchorTo="TOPLEFT" anchor="ControlsButton" y="34"/>
 		</KeyButton>
 		<Label fontName="Geneva" fontSize="9" fontStyle="BOLD" text=" Zap the high scores">
 			<Color r="0xFF" g="0xFF" b="0x00"/>
 			<Anchor anchorFrom="BOTTOMLEFT" anchorTo="TOPRIGHT" anchor="ZapButton" x="3" y="21"/>
 		</Label>
-		<KeyButton name="AboutButton" hotkey="A">
+		<KeyButton name="AboutButton" hotkey="A" clickSound="122">
 			<Anchor anchorFrom="TOPLEFT" anchorTo="TOPLEFT" anchor="ZapButton" y="34"/>
 		</KeyButton>
 		<Label fontName="Geneva" fontSize="9" fontStyle="BOLD" text=" About Maelstrom...">
 			<Color r="0xFF" g="0xFF" b="0x00"/>
 			<Anchor anchorFrom="BOTTOMLEFT" anchorTo="TOPRIGHT" anchor="AboutButton" x="3" y="21"/>
 		</Label>
-		<KeyButton name="QuitButton" hotkey="Q">
+		<KeyButton name="QuitButton" hotkey="Q" clickSound="106">
 			<Anchor anchorFrom="TOPLEFT" anchorTo="TOPLEFT" anchor="AboutButton" y="68"/>
 		</KeyButton>
 		<Label fontName="Geneva" fontSize="9" fontStyle="BOLD" text=" Quit Maelstrom">
@@ -255,8 +258,8 @@
 
 		<!-- Hidden action buttons -->
 		<Button name="ToggleFullscreen" hotkey="ALT-Return"/>
-		<Button name="Cheat" hotkey="L"/>
-		<Button name="Special" hotkey="X"/>
+		<Button name="Cheat" hotkey="L" clickSound="124"/>
+		<Button name="Special" hotkey="X" clickSound="128"/>
 		<Button name="Screenshot" hotkey="F3"/>
 		<Button name="SetVolume0" hotkey="0"/>
 		<Button name="SetVolume1" hotkey="1"/>
diff --git a/init.cpp b/init.cpp
index d093d017..20772835 100644
--- a/init.cpp
+++ b/init.cpp
@@ -641,6 +641,12 @@ 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)
@@ -716,6 +722,7 @@ int DoInitializations(Uint32 window_flags, Uint32 render_flags)
 
 	/* Create the UI manager */
 	ui = new UIManager(screen, CreateMaelstromUIElement);
+	ui->SetSoundCallback(PlayUISound, NULL);
 	ui->SetLoadPath("UI");
 
 	/* Load the Sound Server and initialize sound */
@@ -771,7 +778,6 @@ int DoInitializations(Uint32 window_flags, Uint32 render_flags)
 
 	/* -- Throw up our intro screen */
 	ui->ShowPanel(PANEL_LOADING);
-	sound->PlaySound(gPrizeAppears, 1);
 	ui->Draw();
 
 	/* -- Load in our sprites and other needed resources */
diff --git a/main.cpp b/main.cpp
index 7c6c9baf..6a9a4ed7 100644
--- a/main.cpp
+++ b/main.cpp
@@ -40,14 +40,10 @@ static void DrawMainScreen(void);
 static void RunDoAbout(void)
 {
 	gNoDelay = 0;
-	Delay(SOUND_DELAY);
-	sound->PlaySound(gNovaAppears, 5);
 	DoAbout();
 }
 static void RunConfigureControls(void)
 {
-	Delay(SOUND_DELAY);
-	sound->PlaySound(gHomingAppears, 5);
 	ConfigureControls();
 }
 static void RunPlayGame(void)
@@ -55,14 +51,10 @@ static void RunPlayGame(void)
 	gStartLives = 3;
 	gStartLevel = 1;
 	gNoDelay = 0;
-	sound->PlaySound(gNewLife, 5);
-	Delay(SOUND_DELAY);
 	NewGame();
 }
 static void RunQuitGame(void)
 {
-	Delay(SOUND_DELAY);
-	sound->PlaySound(gMultiplierGone, 5);
 	while ( sound->Playing() )
 		Delay(SOUND_DELAY);
 	gRunning = false;
@@ -101,8 +93,6 @@ static void SetSoundLevel(int volume)
 }
 static void RunZapScores(void)
 {
-	Delay(SOUND_DELAY);
-	sound->PlaySound(gMultShotSound, 5);
 	if ( ZapHighScores() ) {
 		/* Fade the screen and redisplay scores */
 		screen->Fade();
@@ -117,8 +107,6 @@ static void RunToggleFullscreen(void)
 }
 static void RunCheat(void)
 {
-	Delay(SOUND_DELAY);
-	sound->PlaySound(gLuckySound, 5);
 	gStartLevel = GetStartLevel();
 	if ( gStartLevel > 0 ) {
 		Delay(SOUND_DELAY);
@@ -127,12 +115,6 @@ static void RunCheat(void)
 		NewGame();
 	}
 }
-static void RunSpecial(void)
-{
-	Delay(SOUND_DELAY);
-	sound->PlaySound(gEnemyAppears, 5);
-	ShowDawn();
-}
 static void RunScreenshot(void)
 {
 	screen->ScreenDump("ScoreDump", 64, 48, 298, 384);
@@ -336,11 +318,10 @@ int main(int argc, char *argv[])
 
 	SetupMainScreen();
 
+	DropEvents();
 	gRunning = true;
-	sound->PlaySound(gNovaBoom, 5);
-	Delay(SOUND_DELAY);
-	while ( sound->Playing() )
-		Delay(SOUND_DELAY);
+//	while ( sound->Playing() )
+//		Delay(SOUND_DELAY);
 	ui->ShowPanel(PANEL_MAIN);
 
 	while ( gRunning ) {
@@ -355,26 +336,6 @@ int main(int argc, char *argv[])
 		if ( ui->HandleEvent(event) )
 			continue;
 
-		/* -- Handle it! */
-		if ( event.type == SDL_KEYDOWN ) {
-			switch (event.key.keysym.sym) {
-
-				// Ignore Shift, Ctrl, Alt keys
-				case SDLK_LSHIFT:
-				case SDLK_RSHIFT:
-				case SDLK_LCTRL:
-				case SDLK_RCTRL:
-				case SDLK_LALT:
-				case SDLK_RALT:
-					break;
-
-				// Dink! :-)
-				default:
-					Delay(SOUND_DELAY);
-					sound->PlaySound(gSteelHit, 5);
-					break;
-			}
-		} else
 		/* -- Handle window close requests */
 		if ( event.type == SDL_QUIT ) {
 			RunQuitGame();
@@ -466,7 +427,7 @@ void SetupMainScreen()
 	}
 	button = panel->GetElement<UIElementButton>("Special");
 	if (button) {
-		button->SetClickCallback(RunSpecial);
+		button->SetClickCallback(ShowDawn);
 	}
 	button = panel->GetElement<UIElementButton>("Screenshot");
 	if (button) {
diff --git a/screenlib/SDL_FrameBuf.cpp b/screenlib/SDL_FrameBuf.cpp
index a190e6c9..9e6f1b05 100644
--- a/screenlib/SDL_FrameBuf.cpp
+++ b/screenlib/SDL_FrameBuf.cpp
@@ -131,7 +131,7 @@ void
 FrameBuf:: Fade(void)
 {
 // Temporary for development
-//return;
+return;
 	const int max = 32;
 	Uint16 ramp[256];   
 
diff --git a/screenlib/UIArea.h b/screenlib/UIArea.h
index 44771e54..45ca0033 100644
--- a/screenlib/UIArea.h
+++ b/screenlib/UIArea.h
@@ -105,10 +105,10 @@ class UIArea : public ErrorBase
 	}
 	void GetAnchorLocation(AnchorLocation spot, int *x, int *y) const;
 
-	void Show() {
+	virtual void Show() {
 		m_shown = true;
 	}
-	void Hide() {
+	virtual void Hide() {
 		m_shown = false;
 	}
 	bool IsShown() const {
diff --git a/screenlib/UIElementButton.cpp b/screenlib/UIElementButton.cpp
index ea2eec43..0bbe3ed4 100644
--- a/screenlib/UIElementButton.cpp
+++ b/screenlib/UIElementButton.cpp
@@ -50,6 +50,7 @@ UIElementButton::UIElementButton(UIPanel *panel, const char *name) :
 	m_hotkeyMod = KMOD_NONE;
 	m_mouseInside = false;
 	m_mousePressed = false;
+	m_clickSound = 0;
 	m_clickPanel = NULL;
 	m_callback = NULL;
 }
@@ -90,14 +91,23 @@ UIElementButton::Load(rapidxml::xml_node<> *node)
 			value = hyphen+1;
 		}
 
-		m_hotkey = SDL_GetKeyFromName(value);
-		if (m_hotkey == SDLK_UNKNOWN) {
-			SetError("Couldn't interpret hotkey value '%s'", value);
-			return false;
+		if (strcmp(value, "any") == 0) {
+			/* This will be a catch-all button */
+			m_hotkey = ~0;
+		} else {
+			m_hotkey = SDL_GetKeyFromName(value);
+			if (m_hotkey == SDLK_UNKNOWN) {
+				SetError("Couldn't interpret hotkey value '%s'", value);
+				return false;
+			}
 		}
 	}
 
-	attr = node->first_attribute("clickActivatePanel", 0, false);
+	attr = node->first_attribute("clickSound", 0, false);
+	if (attr) {
+		m_clickSound = atoi(attr->value());
+	}
+	attr = node->first_attribute("clickPanel", 0, false);
 	if (attr) {
 		const char *value = attr->value();
 		m_clickPanel = new char[strlen(value)+1];
@@ -106,6 +116,31 @@ UIElementButton::Load(rapidxml::xml_node<> *node)
 	return UIElement::Load(node);
 }
 
+bool
+UIElementButton::ShouldHandleKey(SDL_Keycode key)
+{
+	if (key == m_hotkey) {
+		return true;
+	}
+	if (m_hotkey == ~0) {
+		switch (key) {
+			// Ignore modifier keys
+			case SDLK_LSHIFT:
+			case SDLK_RSHIFT:
+			case SDLK_LCTRL:
+			case SDLK_RCTRL:
+			case SDLK_LALT:
+			case SDLK_RALT:
+			case SDLK_LGUI:
+			case SDLK_RGUI:
+				return false;
+			default:
+				return true;
+		}
+	}
+	return false;
+}
+
 bool
 UIElementButton::HandleEvent(const SDL_Event &event)
 {
@@ -144,7 +179,8 @@ UIElementButton::HandleEvent(const SDL_Event &event)
 		return true;
 	}
 
-	if (event.type == SDL_KEYDOWN && event.key.keysym.sym == m_hotkey) {
+	if (event.type == SDL_KEYDOWN &&
+	    ShouldHandleKey(event.key.keysym.sym)) {
 		if (!m_mousePressed) {
 			m_mousePressed = true;
 			OnMouseDown();
@@ -152,7 +188,8 @@ UIElementButton::HandleEvent(const SDL_Event &event)
 		return true;
 	}
 
-	if (event.type == SDL_KEYUP && event.key.keysym.sym == m_hotkey) {
+	if (event.type == SDL_KEYUP &&
+	    ShouldHandleKey(event.key.keysym.sym)) {
 		if (!m_hotkeyMod || (event.key.keysym.mod & m_hotkeyMod)) {
 			if (m_mousePressed) {
 				m_mousePressed = false;
@@ -169,12 +206,15 @@ UIElementButton::HandleEvent(const SDL_Event &event)
 void
 UIElementButton::OnClick()
 {
-	if (m_callback) {
-		m_callback->OnClick();
+	if (m_clickSound) {
+		m_panel->GetUI()->PlaySound(m_clickSound);
 	}
 	if (m_clickPanel) {
 		m_panel->GetUI()->ShowPanel(m_clickPanel);
 	}
+	if (m_callback) {
+		m_callback->OnClick();
+	}
 }
 
 void
diff --git a/screenlib/UIElementButton.h b/screenlib/UIElementButton.h
index 2ffc170a..eeb63b81 100644
--- a/screenlib/UIElementButton.h
+++ b/screenlib/UIElementButton.h
@@ -61,9 +61,13 @@ class UIElementButton : public UIElement
 	int m_hotkeyMod;
 	bool m_mouseInside;
 	bool m_mousePressed;
+	int m_clickSound;
 	char *m_clickPanel;
 	UIButtonCallback *m_callback;
 
+protected:
+	bool ShouldHandleKey(SDL_Keycode key);
+
 protected:
 	static UIElementType s_elementType;
 
diff --git a/screenlib/UIManager.cpp b/screenlib/UIManager.cpp
index ceee020f..d2a4b2ff 100644
--- a/screenlib/UIManager.cpp
+++ b/screenlib/UIManager.cpp
@@ -28,6 +28,8 @@
 UIManager::UIManager(FrameBuf *screen, UIElementFactory factory) : UIArea(screen)
 {
 	m_elementFactory = factory;
+	m_soundCallback = NULL;
+	m_soundCallbackParam = NULL;
 	m_loadPath = new char[2];
 	strcpy(m_loadPath, ".");
 }
diff --git a/screenlib/UIManager.h b/screenlib/UIManager.h
index 8a7cc2dc..52c1d453 100644
--- a/screenlib/UIManager.h
+++ b/screenlib/UIManager.h
@@ -32,6 +32,7 @@ class UIPanel;
 class UIElement;
 
 typedef UIElement *(*UIElementFactory)(UIPanel *panel, const char *name);
+typedef void (*UISoundCallback)(void *, int soundID);
 
 class UIManager : public UIArea
 {
@@ -75,11 +76,23 @@ 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);
 
 protected:
 	UIElementFactory m_elementFactory;
+	UISoundCallback m_soundCallback;
+	void *m_soundCallbackParam;
 	char *m_loadPath;
 	array<UIPanel *> m_panels;
 	array<UIPanel *> m_visible;
diff --git a/screenlib/UIPanel.cpp b/screenlib/UIPanel.cpp
index 9cb59c72..36d24c82 100644
--- a/screenlib/UIPanel.cpp
+++ b/screenlib/UIPanel.cpp
@@ -38,6 +38,8 @@ UIPanel::UIPanel(UIManager *ui, const char *name) : UIArea(ui->GetScreen())
 	m_rect.h = m_screen->Height();
 	m_shown = false;
 	m_fullscreen = true;
+	m_enterSound = 0;
+	m_leaveSound = 0;
 
 	m_ui->AddPanel(this);
 }
@@ -103,6 +105,14 @@ UIPanel::Load(const char *file)
 			m_fullscreen = false;
 		}
 	}
+	attr = node->first_attribute("enterSound", 0, false);
+	if (attr) {
+		m_enterSound = atoi(attr->value());
+	}
+	attr = node->first_attribute("leaveSound", 0, false);
+	if (attr) {
+		m_leaveSound = atoi(attr->value());
+	}
 	if (strcmp(node->name(), "UIPanel") != 0) {
 		SetError("Parse error: UIPanel root element expected");
 		delete[] buffer;
@@ -162,6 +172,24 @@ UIPanel::GetElement(const char *name)
 	return NULL;
 }
 
+void
+UIPanel::Show()
+{
+	if (m_enterSound) {
+		m_ui->PlaySound(m_enterSound);
+	}
+	UIArea::Show();
+}
+
+void
+UIPanel::Hide()
+{
+	if (m_leaveSound) {
+		m_ui->PlaySound(m_leaveSound);
+	}
+	UIArea::Hide();
+}
+
 void
 UIPanel::Draw()
 {
diff --git a/screenlib/UIPanel.h b/screenlib/UIPanel.h
index c09b4e1a..26ff81a2 100644
--- a/screenlib/UIPanel.h
+++ b/screenlib/UIPanel.h
@@ -70,6 +70,9 @@ class UIPanel : public UIArea
 		m_elements.remove(element);
 	}
 
+	virtual void Show();
+	virtual void Hide();
+
 	void Draw();
 	bool HandleEvent(const SDL_Event &event);
 
@@ -77,6 +80,8 @@ class UIPanel : public UIArea
 	UIManager *m_ui;
 	char *m_name;
 	bool m_fullscreen;
+	int m_enterSound;
+	int m_leaveSound;
 	array<UIElement *> m_elements;
 
 	UIElement *GetElement(const char *name);