https://github.com/libsdl-org/Maelstrom/commit/984eb09383c00eac76d6cab3033954559aa097aa
From 984eb09383c00eac76d6cab3033954559aa097aa Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Wed, 23 Nov 2011 14:22:40 -0500
Subject: [PATCH] Added stackable containers for easy UI layout
---
game/MaelstromUI.cpp | 3 +
screenlib/Makefile.am | 2 +
screenlib/Makefile.in | 15 ++--
screenlib/UIBaseElement.h | 17 +++++
screenlib/UIContainer.cpp | 141 ++++++++++++++++++++++++++++++++++++++
screenlib/UIContainer.h | 66 ++++++++++++++++++
6 files changed, 238 insertions(+), 6 deletions(-)
create mode 100644 screenlib/UIContainer.cpp
create mode 100644 screenlib/UIContainer.h
diff --git a/game/MaelstromUI.cpp b/game/MaelstromUI.cpp
index daae29f8..050a34ad 100644
--- a/game/MaelstromUI.cpp
+++ b/game/MaelstromUI.cpp
@@ -30,6 +30,7 @@
#include "player.h"
#include "lobby.h"
#include "MacDialog.h"
+#include "../screenlib/UIContainer.h"
#include "../screenlib/UIElementButton.h"
#include "../screenlib/UIElementCheckbox.h"
#include "../screenlib/UIElementEditbox.h"
@@ -199,6 +200,8 @@ MaelstromUI::CreateElement(UIBaseElement *parent, const char *type, const char *
element = new UIElement(parent, name, new UIDrawEngine());
} else if (strcasecmp(type, "Image") == 0) {
element = new UIElement(parent, name, new UIDrawEngine());
+ } else if (strcasecmp(type, "Container") == 0) {
+ element = new UIContainer(parent, name, new UIDrawEngine());
} else if (strcasecmp(type, "Button") == 0) {
element = new UIElementButton(parent, name, new UIDrawEngine());
} else if (strcasecmp(type, "Icon") == 0) {
diff --git a/screenlib/Makefile.am b/screenlib/Makefile.am
index 813791ce..2e3bee5c 100644
--- a/screenlib/Makefile.am
+++ b/screenlib/Makefile.am
@@ -9,6 +9,8 @@ libSDLscreen_a_SOURCES = \
UIArea.h \
UIBaseElement.cpp \
UIBaseElement.h \
+ UIContainer.cpp \
+ UIContainer.h \
UIDialog.cpp \
UIDialog.h \
UIDialogButton.cpp \
diff --git a/screenlib/Makefile.in b/screenlib/Makefile.in
index b5d00d72..d791de47 100644
--- a/screenlib/Makefile.in
+++ b/screenlib/Makefile.in
@@ -50,12 +50,12 @@ ARFLAGS = cru
libSDLscreen_a_AR = $(AR) $(ARFLAGS)
libSDLscreen_a_LIBADD =
am_libSDLscreen_a_OBJECTS = SDL_FrameBuf.$(OBJEXT) UIArea.$(OBJEXT) \
- UIBaseElement.$(OBJEXT) UIDialog.$(OBJEXT) \
- UIDialogButton.$(OBJEXT) UIDrawEngine.$(OBJEXT) \
- UIElement.$(OBJEXT) UIElementButton.$(OBJEXT) \
- UIElementCheckbox.$(OBJEXT) UIElementEditbox.$(OBJEXT) \
- UIElementRadio.$(OBJEXT) UIManager.$(OBJEXT) UIPanel.$(OBJEXT) \
- UITemplates.$(OBJEXT)
+ UIBaseElement.$(OBJEXT) UIContainer.$(OBJEXT) \
+ UIDialog.$(OBJEXT) UIDialogButton.$(OBJEXT) \
+ UIDrawEngine.$(OBJEXT) UIElement.$(OBJEXT) \
+ UIElementButton.$(OBJEXT) UIElementCheckbox.$(OBJEXT) \
+ UIElementEditbox.$(OBJEXT) UIElementRadio.$(OBJEXT) \
+ UIManager.$(OBJEXT) UIPanel.$(OBJEXT) UITemplates.$(OBJEXT)
libSDLscreen_a_OBJECTS = $(am_libSDLscreen_a_OBJECTS)
DEFAULT_INCLUDES = -I.@am__isrc@
depcomp = $(SHELL) $(top_srcdir)/build-scripts/depcomp
@@ -192,6 +192,8 @@ libSDLscreen_a_SOURCES = \
UIArea.h \
UIBaseElement.cpp \
UIBaseElement.h \
+ UIContainer.cpp \
+ UIContainer.h \
UIDialog.cpp \
UIDialog.h \
UIDialogButton.cpp \
@@ -267,6 +269,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/SDL_FrameBuf.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/UIArea.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/UIBaseElement.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/UIContainer.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/UIDialog.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/UIDialogButton.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/UIDrawEngine.Po@am__quote@
diff --git a/screenlib/UIBaseElement.h b/screenlib/UIBaseElement.h
index 9f7ccf5c..2be78f7b 100644
--- a/screenlib/UIBaseElement.h
+++ b/screenlib/UIBaseElement.h
@@ -155,13 +155,26 @@ class UIBaseElement : public UIArea
virtual void Show() {
m_shown = true;
+ if (m_parent) {
+ m_parent->OnChildShown(this);
+ }
}
virtual void Hide() {
m_shown = false;
+ if (m_parent) {
+ m_parent->OnChildHidden(this);
+ }
}
bool IsShown() const {
return m_shown;
}
+ virtual void OnRectChanged() {
+ UIArea::OnRectChanged();
+
+ if (m_parent) {
+ m_parent->OnChildRectChanged(this);
+ }
+ }
void SetDisabled(bool disabled);
void SetParentDisabled(bool disabled);
@@ -172,6 +185,10 @@ class UIBaseElement : public UIArea
virtual void Draw();
virtual bool HandleEvent(const SDL_Event &event);
+ virtual void OnChildShown(UIBaseElement *child) { }
+ virtual void OnChildHidden(UIBaseElement *child) { }
+ virtual void OnChildRectChanged(UIBaseElement *child) { }
+
protected:
FrameBuf *m_screen;
UIManager *m_ui;
diff --git a/screenlib/UIContainer.cpp b/screenlib/UIContainer.cpp
new file mode 100644
index 00000000..04ff16fa
--- /dev/null
+++ b/screenlib/UIContainer.cpp
@@ -0,0 +1,141 @@
+/*
+ screenlib: A simple window and UI library based on the SDL library
+ Copyright (C) 1997-2011 Sam Lantinga <slouken@libsdl.org>
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+*/
+
+#include "UIContainer.h"
+
+UIElementType UIContainer::s_elementType;
+
+
+UIContainer::UIContainer(UIBaseElement *parent, const char *name, UIDrawEngine *drawEngine) :
+ UIElement(parent, name, drawEngine)
+{
+ m_layoutType = LAYOUT_VERTICAL;
+ m_spacing = 0;
+ m_layoutInProgress = false;
+}
+
+
+bool
+UIContainer::Load(rapidxml::xml_node<> *node, const UITemplates *templates)
+{
+ rapidxml::xml_attribute<> *attr;
+
+ if (!UIElement::Load(node, templates)) {
+ return false;
+ }
+
+ attr = node->first_attribute("layout", 0, false);
+ if (attr) {
+ if (!ParseLayoutType(attr->value())) {
+ fprintf(stderr, "Warning: Unknown layout type '%s'\n", attr->value());
+ return false;
+ }
+ }
+
+ LoadNumber(node, "spacing", m_spacing);
+
+ return true;
+}
+
+void
+UIContainer::LayoutChildren()
+{
+ UIBaseElement *anchor;
+ AnchorLocation anchorLocation;
+ int offsetX = 0;
+ int offsetY = 0;
+ int i;
+
+ if (m_layoutInProgress) {
+ return;
+ }
+ m_layoutInProgress = true;
+
+ // Anchor the first visible element
+ anchor = NULL;
+ for (i = 0; i < m_elements.length(); ++i) {
+ if (m_elements[i]->IsShown()) {
+ m_elements[i]->SetAnchor(TOPLEFT, TOPLEFT, this);
+ anchor = m_elements[i];
+ break;
+ }
+ }
+
+ // Anchor the rest of the elements
+ if (m_layoutType == LAYOUT_HORIZONTAL) {
+ anchorLocation = TOPRIGHT;
+ offsetX = m_spacing;
+ } else {
+ anchorLocation = BOTTOMLEFT;
+ offsetY = m_spacing;
+ }
+ for (++i; i < m_elements.length(); ++i) {
+ if (m_elements[i]->IsShown()) {
+ m_elements[i]->SetAnchor(TOPLEFT, anchorLocation, anchor, offsetX, offsetY);
+ anchor = m_elements[i];
+ }
+ }
+
+ // Adjust our width and height to match
+ int w = 0, h = 0;
+ if (m_layoutType == LAYOUT_HORIZONTAL) {
+ // Width is the sum of children, height is the max of children
+ for (i = 0; i < m_elements.length(); ++i) {
+ if (m_elements[i]->IsShown()) {
+ if (m_elements[i]->Height() > h) {
+ h = m_elements[i]->Height();
+ }
+ }
+ }
+ if (anchor) {
+ w = (anchor->X() - X()) + anchor->Width();
+ }
+ } else {
+ // Width is the max of children, height is the sum of children
+ for (i = 0; i < m_elements.length(); ++i) {
+ if (m_elements[i]->IsShown()) {
+ if (m_elements[i]->Width() > w) {
+ w = m_elements[i]->Width();
+ }
+ }
+ }
+ if (anchor) {
+ h = (anchor->Y() - Y()) + anchor->Height();
+ }
+ }
+ SetSize(w, h, true);
+
+ m_layoutInProgress = false;
+}
+
+bool
+UIContainer::ParseLayoutType(const char *text)
+{
+ if (SDL_strcasecmp(text, "HORIZONTAL") == 0) {
+ m_layoutType = LAYOUT_HORIZONTAL;
+ return true;
+ }
+ if (SDL_strcasecmp(text, "VERTICAL") == 0) {
+ m_layoutType = LAYOUT_VERTICAL;
+ return true;
+ }
+ return false;
+}
diff --git a/screenlib/UIContainer.h b/screenlib/UIContainer.h
new file mode 100644
index 00000000..cbaf0907
--- /dev/null
+++ b/screenlib/UIContainer.h
@@ -0,0 +1,66 @@
+/*
+ screenlib: A simple window and UI library based on the SDL library
+ Copyright (C) 1997-2011 Sam Lantinga <slouken@libsdl.org>
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+*/
+
+#ifndef _UIContainer_h
+#define _UIContainer_h
+
+#include "UIElement.h"
+
+
+enum LAYOUT_TYPE {
+ LAYOUT_HORIZONTAL,
+ LAYOUT_VERTICAL,
+};
+
+class UIContainer : public UIElement
+{
+DECLARE_TYPESAFE_CLASS(UIElement)
+public:
+ UIContainer(UIBaseElement *parent, const char *name, UIDrawEngine *drawEngine);
+
+ override bool Load(rapidxml::xml_node<> *node, const UITemplates *templates);
+ override bool FinishLoading() {
+ LayoutChildren();
+ return true;
+ }
+
+ override void OnChildShown(UIBaseElement *child) {
+ LayoutChildren();
+ }
+ override void OnChildHidden(UIBaseElement *child) {
+ LayoutChildren();
+ }
+ override void OnChildRectChanged(UIBaseElement *child) {
+ LayoutChildren();
+ }
+
+protected:
+ LAYOUT_TYPE m_layoutType;
+ int m_spacing;
+ bool m_layoutInProgress;
+
+protected:
+ bool ParseLayoutType(const char *text);
+
+ virtual void LayoutChildren();
+};
+
+#endif // _UIContainer_h