https://github.com/libsdl-org/Maelstrom/commit/2e2ff5fb69af2963203fa18fb30d98b9927fe045
From 2e2ff5fb69af2963203fa18fb30d98b9927fe045 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Sat, 5 Nov 2011 00:08:43 -0400
Subject: [PATCH] Added binding of UI elements to preferences so we can
automatically save/restore UI state to preferences. This was tested with the
basic UI element label, checkboxes and editboxes.
---
MaelstromUI.cpp | 2 +-
MaelstromUI.h | 2 +-
init.cpp | 2 +-
netlogic/game.cpp | 23 ++++++++++----------
screenlib/UIBaseElement.cpp | 16 ++++++++++++++
screenlib/UIBaseElement.h | 5 +++++
screenlib/UIDialog.cpp | 2 +-
screenlib/UIElement.cpp | 26 ++++++++++++++++++++++-
screenlib/UIElement.h | 8 +++++--
screenlib/UIElementCheckbox.cpp | 30 +++++++++++++++++++++++++-
screenlib/UIElementCheckbox.h | 6 ++++++
screenlib/UIManager.cpp | 3 ++-
screenlib/UIManager.h | 7 ++++++-
screenlib/UIPanel.cpp | 17 +++++++++++----
screenlib/UIPanel.h | 5 ++++-
utils/prefs.cpp | 37 ++++++++++++++++++++++++++++++++-
utils/prefs.h | 10 ++++++++-
17 files changed, 172 insertions(+), 29 deletions(-)
diff --git a/MaelstromUI.cpp b/MaelstromUI.cpp
index a9235f8e..00b7401c 100644
--- a/MaelstromUI.cpp
+++ b/MaelstromUI.cpp
@@ -50,7 +50,7 @@ hash_nuke_string_text(const void *key, const void *value, void *data)
fontserv->FreeText((SDL_Texture *)value);
}
-MaelstromUI::MaelstromUI(FrameBuf *screen) : UIManager(screen)
+MaelstromUI::MaelstromUI(FrameBuf *screen, Prefs *prefs) : UIManager(screen, prefs)
{
/* Create our font hashtables */
m_fonts = hash_create(screen, hash_hash_string, hash_keymatch_string, hash_nuke_string_font);
diff --git a/MaelstromUI.h b/MaelstromUI.h
index 960df50e..10277c4a 100644
--- a/MaelstromUI.h
+++ b/MaelstromUI.h
@@ -30,7 +30,7 @@ class HashTable;
class MaelstromUI : public UIManager
{
public:
- MaelstromUI(FrameBuf *screen);
+ MaelstromUI(FrameBuf *screen, Prefs *prefs);
virtual ~MaelstromUI();
//
diff --git a/init.cpp b/init.cpp
index 99193e42..d7e372ce 100644
--- a/init.cpp
+++ b/init.cpp
@@ -758,7 +758,7 @@ int DoInitializations(Uint32 window_flags, Uint32 render_flags)
}
/* Create the UI manager */
- ui = new MaelstromUI(screen);
+ ui = new MaelstromUI(screen, prefs);
/* -- We want to access the FULL screen! */
SetRect(&gScrnRect, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
diff --git a/netlogic/game.cpp b/netlogic/game.cpp
index 5ed528db..b52a82cf 100644
--- a/netlogic/game.cpp
+++ b/netlogic/game.cpp
@@ -32,7 +32,7 @@
#define PREFERENCES_HANDLE "Handle"
// Global variables set in this file...
-int gScore;
+Uint32 gScore;
int gGameOn;
int gPaused;
int gWave;
@@ -908,27 +908,27 @@ static void DoGameOver(void)
(gNumPlayers == 1) && !gDeathMatch ) {
sound->PlaySound(gBonusShot, 5);
- /* Get the previously used handle, if possible */
- const char *text = prefs->GetString(PREFERENCES_HANDLE);
- if (text) {
- SDL_strlcpy(handle, text, sizeof(handle));
- } else {
- *handle = '\0';
- }
- chars_in_handle = SDL_strlen(handle);
-
/* -- Let them enter their name */
+ const char *text = NULL;
label = panel->GetElement<UIElement>("name_label");
if (label) {
label->Show();
}
label = panel->GetElement<UIElement>("name");
if (label) {
- label->SetText(handle);
+ text = label->GetText();
label->Show();
}
ui->Draw();
+ /* Get the previously used handle, if possible */
+ if (text) {
+ SDL_strlcpy(handle, text, sizeof(handle));
+ } else {
+ *handle = '\0';
+ }
+ chars_in_handle = SDL_strlen(handle);
+
while ( screen->PollEvent(&event) ) /* Loop, flushing events */;
SDL_StartTextInput();
while ( label && !done ) {
@@ -978,7 +978,6 @@ static void DoGameOver(void)
hScores[which].wave = gWave;
hScores[which].score = OurShip->GetScore();
strcpy(hScores[which].name, handle);
- prefs->SetString(PREFERENCES_HANDLE, handle);
}
sound->HaltSound();
diff --git a/screenlib/UIBaseElement.cpp b/screenlib/UIBaseElement.cpp
index 028e9124..889cd435 100644
--- a/screenlib/UIBaseElement.cpp
+++ b/screenlib/UIBaseElement.cpp
@@ -96,6 +96,22 @@ UIBaseElement::Load(rapidxml::xml_node<> *node, const UITemplates *templates)
return true;
}
+void
+UIBaseElement::LoadData(Prefs *prefs)
+{
+ for (int i = 0; i < m_elements.length(); ++i) {
+ m_elements[i]->LoadData(prefs);
+ }
+}
+
+void
+UIBaseElement::SaveData(Prefs *prefs)
+{
+ for (int i = 0; i < m_elements.length(); ++i) {
+ m_elements[i]->SaveData(prefs);
+ }
+}
+
UIArea *
UIBaseElement::GetAnchorElement(const char *name)
{
diff --git a/screenlib/UIBaseElement.h b/screenlib/UIBaseElement.h
index 91559288..329d28b9 100644
--- a/screenlib/UIBaseElement.h
+++ b/screenlib/UIBaseElement.h
@@ -24,6 +24,7 @@
#include "../utils/array.h"
#include "../utils/rapidxml.h"
+#include "../utils/prefs.h"
#include "SDL.h"
#include "UIArea.h"
@@ -64,6 +65,10 @@ class UIBaseElement : public UIArea
return true;
}
+ // Bind any preferences variables to the preferences manager
+ virtual void LoadData(Prefs *prefs);
+ virtual void SaveData(Prefs *prefs);
+
virtual UIArea *GetAnchorElement(const char *name);
void AddElement(UIBaseElement *element) {
diff --git a/screenlib/UIDialog.cpp b/screenlib/UIDialog.cpp
index 93882e34..49028f4e 100644
--- a/screenlib/UIDialog.cpp
+++ b/screenlib/UIDialog.cpp
@@ -50,7 +50,7 @@ UIDialog::Show()
void
UIDialog::Hide()
{
- UIPanel::Hide();
+ UIPanel::Hide(m_status > 0);
if (m_handleDone) {
m_handleDone(this, m_status);
diff --git a/screenlib/UIElement.cpp b/screenlib/UIElement.cpp
index a084cc40..0a248c86 100644
--- a/screenlib/UIElement.cpp
+++ b/screenlib/UIElement.cpp
@@ -58,6 +58,7 @@ UIElement::UIElement(UIBaseElement *parent, const char *name, UIDrawEngine *draw
m_fontSize = 0;
m_fontStyle = UIFONT_STYLE_NORMAL;
m_text = NULL;
+ m_textBinding = NULL;
m_textShadowOffsetX = 0;
m_textShadowOffsetY = 0;
m_textShadowColor = m_screen->MapRGB(0x00, 0x00, 0x00);
@@ -81,6 +82,9 @@ UIElement::~UIElement()
if (m_text) {
SDL_free(m_text);
}
+ if (m_textBinding) {
+ SDL_free(m_textBinding);
+ }
if (m_image) {
m_screen->FreeImage(m_image);
}
@@ -133,6 +137,8 @@ UIElement::Load(rapidxml::xml_node<> *node, const UITemplates *templates)
SetText(attr->value());
}
+ LoadString(node, "bindText", m_textBinding);
+
child = node->first_node("TextArea", 0, false);
if (child) {
if (!m_textArea.Load(child)) {
@@ -193,7 +199,25 @@ UIElement::FinishLoading()
if (m_drawEngine) {
m_drawEngine->OnLoad();
}
- return true;
+ return UIBaseElement::FinishLoading();
+}
+
+void
+UIElement::LoadData(Prefs *prefs)
+{
+ if (m_textBinding) {
+ SetText(prefs->GetString(m_textBinding, GetText()));
+ }
+ UIBaseElement::LoadData(prefs);
+}
+
+void
+UIElement::SaveData(Prefs *prefs)
+{
+ if (m_textBinding) {
+ prefs->SetString(m_textBinding, GetText());
+ }
+ UIBaseElement::SaveData(prefs);
}
bool
diff --git a/screenlib/UIElement.h b/screenlib/UIElement.h
index 7acbd906..f70032c5 100644
--- a/screenlib/UIElement.h
+++ b/screenlib/UIElement.h
@@ -26,7 +26,6 @@
#include "UIDrawEngine.h"
#include "UIFontInterface.h"
-
class UIClickDelegate
{
public:
@@ -48,6 +47,10 @@ DECLARE_TYPESAFE_CLASS(UIBaseElement)
override bool Load(rapidxml::xml_node<> *node, const UITemplates *templates);
override bool FinishLoading();
+ // Bind any preferences variables to the preferences manager
+ override void LoadData(Prefs *prefs);
+ override void SaveData(Prefs *prefs);
+
// Set the draw engine for this element
// This should be called before Load() so the draw engine can load too.
// Once set, the element owns the draw engine and will free it.
@@ -98,7 +101,7 @@ DECLARE_TYPESAFE_CLASS(UIBaseElement)
return m_fontStyle;
}
- void SetText(const char *text);
+ virtual void SetText(const char *text);
const char *GetText() const {
return m_text;
}
@@ -151,6 +154,7 @@ DECLARE_TYPESAFE_CLASS(UIBaseElement)
int m_fontSize;
UIFontStyle m_fontStyle;
char *m_text;
+ char *m_textBinding;
UIArea m_textArea;
int m_textShadowOffsetX;
int m_textShadowOffsetY;
diff --git a/screenlib/UIElementCheckbox.cpp b/screenlib/UIElementCheckbox.cpp
index 8550cebd..a4b44189 100644
--- a/screenlib/UIElementCheckbox.cpp
+++ b/screenlib/UIElementCheckbox.cpp
@@ -28,6 +28,14 @@ UIElementCheckbox::UIElementCheckbox(UIBaseElement *parent, const char *name, UI
UIElementButton(parent, name, drawEngine)
{
m_checked = false;
+ m_valueBinding = NULL;
+}
+
+UIElementCheckbox::~UIElementCheckbox()
+{
+ if (m_valueBinding) {
+ SDL_free(m_valueBinding);
+ }
}
bool
@@ -43,6 +51,8 @@ UIElementCheckbox::Load(rapidxml::xml_node<> *node, const UITemplates *templates
SetChecked(checked);
}
+ LoadString(node, "bindValue", m_valueBinding);
+
return true;
}
@@ -55,7 +65,25 @@ UIElementCheckbox::FinishLoading()
} else {
assert(!"Need code for labels on the left");
}
- return true;
+ return UIElementButton::FinishLoading();
+}
+
+void
+UIElementCheckbox::LoadData(Prefs *prefs)
+{
+ if (m_valueBinding) {
+ SetChecked(prefs->GetBool(m_valueBinding, IsChecked()));
+ }
+ UIElementButton::LoadData(prefs);
+}
+
+void
+UIElementCheckbox::SaveData(Prefs *prefs)
+{
+ if (m_valueBinding) {
+ prefs->SetBool(m_valueBinding, IsChecked());
+ }
+ UIElementButton::SaveData(prefs);
}
void
diff --git a/screenlib/UIElementCheckbox.h b/screenlib/UIElementCheckbox.h
index 8ada094c..f1d2e23f 100644
--- a/screenlib/UIElementCheckbox.h
+++ b/screenlib/UIElementCheckbox.h
@@ -30,10 +30,15 @@ class UIElementCheckbox : public UIElementButton
DECLARE_TYPESAFE_CLASS(UIElementButton)
public:
UIElementCheckbox(UIBaseElement *parent, const char *name, UIDrawEngine *drawEngine);
+ virtual ~UIElementCheckbox();
override bool Load(rapidxml::xml_node<> *node, const UITemplates *templates);
override bool FinishLoading();
+ // Bind any preferences variables to the preferences manager
+ override void LoadData(Prefs *prefs);
+ override void SaveData(Prefs *prefs);
+
void SetChecked(bool checked) {
if (checked != m_checked) {
m_checked = checked;
@@ -52,6 +57,7 @@ DECLARE_TYPESAFE_CLASS(UIElementButton)
protected:
bool m_checked;
+ char *m_valueBinding;
};
#endif // _UIElementCheckbox_h
diff --git a/screenlib/UIManager.cpp b/screenlib/UIManager.cpp
index 5ea0a277..aa348b07 100644
--- a/screenlib/UIManager.cpp
+++ b/screenlib/UIManager.cpp
@@ -26,10 +26,11 @@
#include "UIPanel.h"
-UIManager::UIManager(FrameBuf *screen) :
+UIManager::UIManager(FrameBuf *screen, Prefs *prefs) :
UIArea(NULL, screen->Width(), screen->Height())
{
m_screen = screen;
+ m_prefs = prefs;
m_loadPath = new char[2];
strcpy(m_loadPath, ".");
}
diff --git a/screenlib/UIManager.h b/screenlib/UIManager.h
index 91cf480f..4df91be5 100644
--- a/screenlib/UIManager.h
+++ b/screenlib/UIManager.h
@@ -33,16 +33,20 @@
class FrameBuf;
class UIBaseElement;
class UIElement;
+class Prefs;
class UIManager : public UIArea, public UIFontInterface, public UISoundInterface
{
public:
- UIManager(FrameBuf *screen);
+ UIManager(FrameBuf *screen, Prefs *prefs);
virtual ~UIManager();
FrameBuf *GetScreen() const {
return m_screen;
}
+ Prefs *GetPrefs() const {
+ return m_prefs;
+ }
const UITemplates *GetTemplates() const {
return &m_templates;
}
@@ -102,6 +106,7 @@ class UIManager : public UIArea, public UIFontInterface, public UISoundInterface
protected:
FrameBuf *m_screen;
+ Prefs *m_prefs;
char *m_loadPath;
UITemplates m_templates;
array<UIPanel *> m_panels;
diff --git a/screenlib/UIPanel.cpp b/screenlib/UIPanel.cpp
index 39e50d68..0db30d0a 100644
--- a/screenlib/UIPanel.cpp
+++ b/screenlib/UIPanel.cpp
@@ -68,10 +68,11 @@ bool
UIPanel::FinishLoading()
{
if (m_delegate) {
- return m_delegate->OnLoad();
- } else {
- return true;
+ if (!m_delegate->OnLoad()) {
+ return false;
+ }
}
+ return UIBaseElement::FinishLoading();
}
void
@@ -91,6 +92,9 @@ UIPanel::Show()
m_ui->PlaySound(m_enterSound);
}
+ // Load data from preferences
+ LoadData(GetUI()->GetPrefs());
+
UIBaseElement::Show();
if (m_delegate) {
@@ -99,7 +103,7 @@ UIPanel::Show()
}
void
-UIPanel::Hide()
+UIPanel::Hide(bool saveData)
{
if (m_leaveSound) {
m_ui->PlaySound(m_leaveSound);
@@ -107,6 +111,11 @@ UIPanel::Hide()
UIBaseElement::Hide();
+ // Save data to preferences
+ if (saveData) {
+ SaveData(GetUI()->GetPrefs());
+ }
+
if (m_delegate) {
m_delegate->OnHide();
}
diff --git a/screenlib/UIPanel.h b/screenlib/UIPanel.h
index 0a1b7cf5..8509c212 100644
--- a/screenlib/UIPanel.h
+++ b/screenlib/UIPanel.h
@@ -69,7 +69,10 @@ DECLARE_TYPESAFE_CLASS(UIBaseElement)
void SetPanelDelegate(UIPanelDelegate *delegate, bool autodelete = true);
override void Show();
- override void Hide();
+ override void Hide() {
+ Hide(true);
+ }
+ void Hide(bool saveData);
void HideAll();
diff --git a/utils/prefs.cpp b/utils/prefs.cpp
index 112069bf..bf2c5e81 100644
--- a/utils/prefs.cpp
+++ b/utils/prefs.cpp
@@ -137,7 +137,17 @@ Prefs::Save()
void
Prefs::SetString(const char *key, const char *value)
{
- hash_remove(m_values, key);
+ const char *lastValue;
+
+ if (!value) {
+ value = "";
+ }
+ if (hash_find(m_values, key, (const void **)&lastValue)) {
+ if (SDL_strcmp(lastValue, value) == 0) {
+ return;
+ }
+ hash_remove(m_values, key);
+ }
hash_insert(m_values, SDL_strdup(key), SDL_strdup(value));
}
@@ -150,6 +160,16 @@ Prefs::SetNumber(const char *key, int value)
SetString(key, buf);
}
+void
+Prefs::SetBool(const char *key, bool value)
+{
+ if (value) {
+ SetString(key, "true");
+ } else {
+ SetString(key, "false");
+ }
+}
+
const char *
Prefs::GetString(const char *key, const char *defaultValue)
{
@@ -171,3 +191,18 @@ Prefs::GetNumber(const char *key, int defaultValue)
}
return defaultValue;
}
+
+bool
+Prefs::GetBool(const char *key, bool defaultValue)
+{
+ const char *value;
+
+ if (hash_find(m_values, key, (const void **)&value)) {
+ if (*value == '1' || *value == 't' || *value == 'T') {
+ return true;
+ } else if (*value == '0' || *value == 'f' || *value == 'F') {
+ return false;
+ }
+ }
+ return defaultValue;
+}
diff --git a/utils/prefs.h b/utils/prefs.h
index d11ec26a..8baf1d33 100644
--- a/utils/prefs.h
+++ b/utils/prefs.h
@@ -32,21 +32,29 @@ class Prefs
void SetString(const char *key, const char *value);
void SetNumber(const char *key, int value);
+ void SetBool(const char *key, bool value);
void Set(const char *key, const char *value) {
SetString(key, value);
}
void Set(const char *key, int value) {
SetNumber(key, value);
}
+ void Set(const char *key, bool value) {
+ SetBool(key, value);
+ }
const char *GetString(const char *key, const char *defaultValue = 0);
int GetNumber(const char *key, int defaultValue = 0);
+ bool GetBool(const char *key, bool defaultValue = false);
void Get(const char *key, const char *&value, const char *defaultValue) {
value = GetString(key, defaultValue);
}
void Get(const char *key, int &value, int defaultValue) {
value = GetNumber(key, defaultValue);
}
+ void Get(const char *key, bool &value, bool defaultValue) {
+ value = GetBool(key, defaultValue);
+ }
protected:
char *m_file;
@@ -75,7 +83,7 @@ class PrefsVariable
return *this = rhs.m_value;
}
- operator T() {
+ operator const T() const {
return m_value;
}