Просмотр исходного кода

Adding support for default UI themes.

Steve Grenier 12 лет назад
Родитель
Сommit
61c495c1a8

+ 3 - 0
gameplay/src/Base.h

@@ -59,6 +59,9 @@ namespace gameplay
  * @script{ignore}
  */
 extern void print(const char* format, ...);
+
+// Define a platform-independent case-insensitive ASCII string comparison function.
+extern int strcmpnocase(const char* s1, const char* s2);
 }
 
 // Current function macro.

+ 13 - 20
gameplay/src/Button.cpp

@@ -14,28 +14,21 @@ Button::~Button()
 {
 }
 
-Button* Button::create(const char* id, Theme::Style* style)
+Control* Button::create(Theme::Style* style, Properties* properties)
 {
     Button* button = new Button();
-
-    button->_id = id;
-    button->_style = style;
-
-    return button;
-}
-
-Control* Button::create(Theme::Style* style, Properties* properties, Theme* theme)
-{
-    Button* button = new Button();
-    button->initialize(style, properties);
-
-    // Different types of data bindings can be named differently in a button namespace.
-    // Gamepad button mappings have the name "mapping" and correspond to Gamepad::ButtonMapping enums.
-    const char* mapping = properties->getString("mapping");
-    if (mapping)
-    {
-        button->_dataBinding = Gamepad::getButtonMappingFromString(mapping);
-    }
+	button->initialize(style, properties);
+
+	if (properties)
+	{
+		// Different types of data bindings can be named differently in a button namespace.
+		// Gamepad button mappings have the name "mapping" and correspond to Gamepad::ButtonMapping enums.
+		const char* mapping = properties->getString("mapping");
+		if (mapping)
+		{
+			button->_dataBinding = Gamepad::getButtonMappingFromString(mapping);
+		}
+	}
 
     return button;
 }

+ 2 - 16
gameplay/src/Button.h

@@ -37,19 +37,6 @@ class Button : public Label
     friend class Gamepad;
     friend class ControlFactory;
 
-public:
-
-    /**
-     * Create a new button control.
-     *
-     * @param id The control's ID.
-     * @param style The control's style.
-     *
-     * @return The new button.
-     * @script{create}
-     */
-    static Button* create(const char* id, Theme::Style* style);
-
 protected:
 
     /**
@@ -66,12 +53,11 @@ protected:
      * Create a button with a given style and properties.
      *
      * @param style The style to apply to this button.
-     * @param properties The properties to set on this button.
-     * @param theme The theme to set on this control if needed.
+     * @param properties A properties object containing a definition of the button (optional).
      *
      * @return The new button.
      */
-    static Control* create(Theme::Style* style, Properties* properties, Theme* theme = NULL);
+    static Control* create(Theme::Style* style, Properties* properties = NULL);
 
     /**
      * @see Control::getType

+ 7 - 17
gameplay/src/CheckBox.cpp

@@ -14,26 +14,16 @@ CheckBox::~CheckBox()
 
 }
 
-CheckBox* CheckBox::create(const char* id, Theme::Style* style)
+Control* CheckBox::create(Theme::Style* style, Properties* properties)
 {
-    GP_ASSERT(style);
-
     CheckBox* checkBox = new CheckBox();
-    if (id)
-        checkBox->_id = id;
-    checkBox->setStyle(style);
-
-    return checkBox;
-}
+	checkBox->initialize(style, properties);
 
-Control* CheckBox::create(Theme::Style* style, Properties* properties, Theme* theme)
-{
-    GP_ASSERT(properties);
-
-    CheckBox* checkBox = new CheckBox();
-    checkBox->initialize(style, properties);
-    properties->getVector2("imageSize", &checkBox->_imageSize);
-    checkBox->_checked = properties->getBool("checked");
+	if (properties)
+	{
+		properties->getVector2("imageSize", &checkBox->_imageSize);
+		checkBox->_checked = properties->getBool("checked");
+	}
 
     return checkBox;
 }

+ 2 - 14
gameplay/src/CheckBox.h

@@ -39,17 +39,6 @@ class CheckBox : public Button
 
 public:
 
-    /**
-     * Create a new check box control.
-     *
-     * @param id The control's ID.
-     * @param style The control's style.
-     *
-     * @return The new check box.
-     * @script{create}
-     */
-    static CheckBox* create(const char* id, Theme::Style* style);
-
     /**
      * Gets whether this checkbox is checked.
      *
@@ -112,12 +101,11 @@ protected:
      * Create a checkbox with a given style and properties.
      *
      * @param style The style to apply to this checkbox.
-     * @param properties The properties to set on this checkbox.
-     * @param theme The theme to set on this control if needed.
+     * @param properties A properties object containing a definition of the checkbox (optional).
      *
      * @return The new checkbox.
      */
-    static Control* create(Theme::Style* style, Properties* properties, Theme* theme = NULL);
+    static Control* create(Theme::Style* style, Properties* properties = NULL);
 
     /**
      * Keyboard callback on key events.

+ 90 - 104
gameplay/src/Container.cpp

@@ -14,6 +14,7 @@
 #include "ImageControl.h"
 #include "Form.h"
 #include "Game.h"
+#include "ControlFactory.h"
 
 namespace gameplay
 {
@@ -69,79 +70,74 @@ Container::~Container()
     SAFE_RELEASE(_layout);
 }
 
-Container* Container::create(const char* id, Theme::Style* style, Layout::Type layoutType)
+Control* Container::create(Theme::Style* style, Properties* properties)
 {
-    GP_ASSERT(style);
-
     Container* container = new Container();
-    container->_layout = createLayout(layoutType);
-    if (id)
-        container->_id = id;
-    container->_style = style;
-    return container;
-}
+    container->initialize(style, properties);
 
-Control* Container::create(Theme::Style* style, Properties* properties, Theme* theme)
-{
-    GP_ASSERT(properties);
+	if (container->_layout == NULL)
+		container->_layout = createLayout(Layout::LAYOUT_ABSOLUTE);
 
-    Container* container = new Container();
-    container->initialize(style, properties, theme);
     return container;
 }
 
-void Container::initialize(Theme::Style* style, Properties* properties, Theme* theme)
+void Container::initialize(Theme::Style* style, Properties* properties)
 {
+	GP_ASSERT(style);
+
     Control::initialize(style, properties);
 
-    // Parse layout
-    Properties* layoutNS = properties->getNamespace("layout", true, false);
-    if (layoutNS)
-    {
-        _layout = createLayout(getLayoutType(layoutNS->getString("type")));
-        switch (_layout->getType())
-        {
-        case Layout::LAYOUT_FLOW:
-            static_cast<FlowLayout*>(_layout)->setSpacing(layoutNS->getInt("horizontalSpacing"), layoutNS->getInt("verticalSpacing"));
-            break;
-        case Layout::LAYOUT_VERTICAL:
-            static_cast<VerticalLayout*>(_layout)->setSpacing(layoutNS->getInt("spacing"));
-            break;
-        }
-    }
-    else
-    {
-        _layout = createLayout(getLayoutType(properties->getString("layout")));
-    }
+	if (properties)
+	{
+		// Parse layout
+		Properties* layoutNS = properties->getNamespace("layout", true, false);
+		if (layoutNS)
+		{
+			_layout = createLayout(getLayoutType(layoutNS->getString("type")));
+			switch (_layout->getType())
+			{
+			case Layout::LAYOUT_FLOW:
+				static_cast<FlowLayout*>(_layout)->setSpacing(layoutNS->getInt("horizontalSpacing"), layoutNS->getInt("verticalSpacing"));
+				break;
+			case Layout::LAYOUT_VERTICAL:
+				static_cast<VerticalLayout*>(_layout)->setSpacing(layoutNS->getInt("spacing"));
+				break;
+			}
+		}
+		else
+		{
+			_layout = createLayout(getLayoutType(properties->getString("layout")));
+		}
 
-    setScroll(getScroll(properties->getString("scroll")));
-    _scrollBarsAutoHide = properties->getBool("scrollBarsAutoHide");
-    if (_scrollBarsAutoHide)
-    {
-        _scrollBarOpacity = 0.0f;
-    }
-    
-    _scrollWheelRequiresFocus = properties->getBool("scrollWheelRequiresFocus");
-    if (properties->exists("scrollingFriction"))
-        _scrollingFriction = properties->getFloat("scrollingFriction");
-    if (properties->exists("scrollWheelSpeed"))
-        _scrollWheelSpeed = properties->getFloat("scrollWheelSpeed");
+		setScroll(getScroll(properties->getString("scroll")));
+		_scrollBarsAutoHide = properties->getBool("scrollBarsAutoHide");
+		if (_scrollBarsAutoHide)
+		{
+			_scrollBarOpacity = 0.0f;
+		}
 
-    addControls(theme, properties);
-    _layout->update(this, _scrollPosition);
+		_scrollWheelRequiresFocus = properties->getBool("scrollWheelRequiresFocus");
+		if (properties->exists("scrollingFriction"))
+			_scrollingFriction = properties->getFloat("scrollingFriction");
+		if (properties->exists("scrollWheelSpeed"))
+			_scrollWheelSpeed = properties->getFloat("scrollWheelSpeed");
 
-    const char* activeControl = properties->getString("activeControl");
-    if (activeControl)
-    {
-        for (size_t i = 0, count = _controls.size(); i < count; ++i)
-        {
-            if (_controls[i]->_id == activeControl)
-            {
-                _activeControl = _controls[i];
-                break;
-            }
-        }
-    }
+		addControls(style->getTheme(), properties);
+		_layout->update(this, _scrollPosition);
+
+		const char* activeControl = properties->getString("activeControl");
+		if (activeControl)
+		{
+			for (size_t i = 0, count = _controls.size(); i < count; ++i)
+			{
+				if (_controls[i]->_id == activeControl)
+				{
+					_activeControl = _controls[i];
+					break;
+				}
+			}
+		}
+	}
 }
 
 void Container::addControls(Theme* theme, Properties* properties)
@@ -168,48 +164,8 @@ void Container::addControls(Theme* theme, Properties* properties)
 
         std::string controlName(controlSpace->getNamespace());
         std::transform(controlName.begin(), controlName.end(), controlName.begin(), (int(*)(int))toupper);
-        if (controlName == "LABEL")
-        {
-            control = Label::create(controlStyle, controlSpace);
-        }
-        else if (controlName == "BUTTON")
-        {
-            control = Button::create(controlStyle, controlSpace);
-        }
-        else if (controlName == "CHECKBOX")
-        {
-            control = CheckBox::create(controlStyle, controlSpace);
-        }
-        else if (controlName == "RADIOBUTTON")
-        {
-            control = RadioButton::create(controlStyle, controlSpace);
-        }
-        else if (controlName == "CONTAINER")
-        {
-            control = Container::create(controlStyle, controlSpace, theme);
-        }
-        else if (controlName == "SLIDER")
-        {
-            control = Slider::create(controlStyle, controlSpace);
-        }
-        else if (controlName == "TEXTBOX")
-        {
-            control = TextBox::create(controlStyle, controlSpace);
-        }
-        else if (controlName == "JOYSTICK")
-        {
-            control = Joystick::create(controlStyle, controlSpace);
-        }
-        else if (controlName == "IMAGE")
-        {
-            control = ImageControl::create(controlStyle, controlSpace);
-        }
-        else
-        {
-            // Ignore - not a valid control name.
-            // This used to fail, but I see no reason to hard fail here (this also fixes not being able
-            // to set padding on containers).
-        }
+
+		control = ControlFactory::getInstance()->createControl(controlName.c_str(), controlStyle, controlSpace);
 
         // Add the new control to the form.
         if (control)
@@ -231,9 +187,39 @@ Layout* Container::getLayout()
     return _layout;
 }
 
-unsigned int Container::addControl(Control* control)
+void Container::setLayout(Layout::Type type)
 {
-    GP_ASSERT(control);
+	if (_layout == NULL || _layout->getType() != type)
+	{
+		SAFE_RELEASE(_layout);
+
+		_layout = createLayout(type);
+		_layout->update(this, _scrollPosition);
+		_dirty = true;
+	}
+}
+
+unsigned int Container::addControl(const char* typeName, const char* styleName)
+{
+	GP_ASSERT(typeName);
+
+	Form* form = getTopLevelForm();
+	if (!form)
+	{
+		GP_WARN("Attempted to add a new control to an orphaned container.");
+		return 0;
+	}
+
+	Theme* theme = form->getTheme();
+	GP_ASSERT(theme);
+
+	Theme::Style* style = NULL;
+	if (styleName)
+		style = theme->getStyle(styleName);
+	if (!style)
+		style = theme->getStyle(typeName);
+
+	Control* control = ControlFactory::getInstance()->createControl()
 
     // Remove the control from its current parent
     if (control->_parent && control->_parent != this)

+ 19 - 25
gameplay/src/Container.h

@@ -72,18 +72,6 @@ public:
         SCROLL_BOTH = SCROLL_HORIZONTAL | SCROLL_VERTICAL
     };
 
-    /**
-     * Create a new container.
-     *
-     * @param id The container's ID.
-     * @param style The container's style.
-     * @param layoutType The container's layout type.
-     *
-     * @return The new container.
-     * @script{create}
-     */
-    static Container* create(const char* id, Theme::Style* style, Layout::Type layoutType = Layout::LAYOUT_ABSOLUTE);
-
     /**
      * Get this container's layout.
      *
@@ -91,23 +79,31 @@ public:
      */
     Layout* getLayout();
 
+	/**
+	 * Sets the layout type for this container.
+	 *
+	 * @param type The new layout type for the container.
+	 */
+	void setLayout(Layout::Type type);
+
     /**
-     * Add a control to this layout.
-     * The control will be assigned the next available index.
+     * Creates and adds a new control to this container.
      *
-     * @param control The Control to add.
+	 * @param type The type of the control to add.
+	 * @param style Name of the theme style to use for the new control (optional).
      *
-     * @return The index assigned to the added Control.
+     * @return The index assigned to the new Control.
      */
-    unsigned int addControl(Control* control);
+    unsigned int addControl(const char* type, const char* style = NULL);
 
     /**
-     * Insert a control at a specific index.
+     * Creates and inserts a control at a specific index.
      *
-     * @param control The control to add.
+     * @param type The type of the control to add.
      * @param index The index at which to insert the control.
+	 * @param style Name of the style to use for the new control (optional).
      */
-    void insertControl(Control* control, unsigned int index);
+    void insertControl(const char* type, unsigned int index, const char* style = NULL);
 
     /**
      * Remove a control at a specific index.
@@ -317,21 +313,19 @@ protected:
      * Create a container with a given style and properties, including a list of controls.
      *
      * @param style The style to apply to this container.
-     * @param properties The properties to set on this container, including nested controls.
-     * @param theme The theme to search for control styles within.
+     * @param properties A properties object containing a definition of the container and its nested controls (optional).
      *
      * @return The new container.
      */
-    static Control* create(Theme::Style* style, Properties* properties, Theme* theme = NULL);
+    static Control* create(Theme::Style* style, Properties* properties = NULL);
 
     /**
      * Initialize properties common to all Containers from a Properties object.
      *
      * @param style The style to apply to this control.
      * @param properties The properties to set on this control.
-     * @param theme The theme for this control.
      */
-    void initialize(Theme::Style* style, Properties* properties, Theme* theme);
+    void initialize(Theme::Style* style, Properties* properties);
 
     /**
      * Updates each control within this container,

+ 125 - 118
gameplay/src/Control.cpp

@@ -85,124 +85,126 @@ static Control::AutoSize parseAutoSize(const char* str)
 
 void Control::initialize(Theme::Style* style, Properties* properties)
 {
-    GP_ASSERT(properties);
-    _style = style;
-
-    // Properties not defined by the style.
-    const char * alignmentString = properties->getString("alignment");
-
-    _isAlignmentSet = alignmentString != NULL;
-    _alignment = getAlignment(alignmentString);
-
-    _autoWidth = parseAutoSize(properties->getString("autoWidth"));
-    _autoHeight = parseAutoSize(properties->getString("autoHeight"));
-
-    _consumeInputEvents = properties->getBool("consumeInputEvents", true);
-
-    _visible = properties->getBool("visible", true);
-
-    if (properties->exists("zIndex"))
-    {
-        _zIndex = properties->getInt("zIndex");
-    }
-    else
-    {
-        _zIndex = -1;
-    }
-
-    if (properties->exists("canFocus"))
-        _canFocus = properties->getBool("canFocus", false);
-
-    if (properties->exists("focusIndex"))
-    {
-        _focusIndex = properties->getInt("focusIndex");
-    }
-    else
-    {
-        _focusIndex = -1;
-    }
-
-    float bounds[4];
-    bool boundsBits[4];
-    if (properties->exists("position"))
-    {
-        parseCoordPair(properties->getString("position", "0,0"), &bounds[0], &bounds[1], &boundsBits[0], &boundsBits[1]);
-    }
-    else
-    {
-        bounds[0] = parseCoord(properties->getString("x", "0"), &boundsBits[0]);
-        bounds[1] = parseCoord(properties->getString("y", "0"), &boundsBits[1]);
-    }
-
-    if (properties->exists("size"))
-    {
-        parseCoordPair(properties->getString("size", "0,0"), &bounds[2], &bounds[3], &boundsBits[2], &boundsBits[3]);
-    }
-    else
-    {
-        bounds[2] = parseCoord(properties->getString("width", "0"), &boundsBits[2]);
-        bounds[3] = parseCoord(properties->getString("height", "0"), &boundsBits[3]);
-    }
-    setX(bounds[0], boundsBits[0]);
-    setY(bounds[1], boundsBits[1]);
-    setWidth(bounds[2], boundsBits[2]);
-    setHeight(bounds[3], boundsBits[3]);
-
-    const char* id = properties->getId();
-    if (id)
-        _id = id;
-
-    if (properties->exists("enabled"))
-    {
-        setEnabled(properties->getBool("enabled"));
-    }
-
-    // Register script listeners for control events
-    if (properties->exists("listener"))
-        addScriptCallback("controlEvent", properties->getString("listener"));
-
-    // Potentially override themed properties for all states.
-    overrideThemedProperties(properties, STATE_ALL);
-
-    // Override themed properties on specific states.
-    Properties* innerSpace = properties->getNextNamespace();
-    while (innerSpace != NULL)
-    {
-        std::string spaceName(innerSpace->getNamespace());
-        std::transform(spaceName.begin(), spaceName.end(), spaceName.begin(), (int(*)(int))toupper);
-        if (spaceName == "STATENORMAL")
-        {
-            overrideThemedProperties(innerSpace, NORMAL);
-        }
-        else if (spaceName == "STATEFOCUS")
-        {
-            overrideThemedProperties(innerSpace, FOCUS);
-        }
-        else if (spaceName == "STATEACTIVE")
-        {
-            overrideThemedProperties(innerSpace, ACTIVE);
-        }
-        else if (spaceName == "STATEDISABLED")
-        {
-            overrideThemedProperties(innerSpace, DISABLED);
-        }
-        else if (spaceName == "STATEHOVER")
-        {
-            overrideThemedProperties(innerSpace, HOVER);
-        }
-        else if (spaceName == "MARGIN")
-        {
-            setMargin(innerSpace->getFloat("top"), innerSpace->getFloat("bottom"),
-                innerSpace->getFloat("left"), innerSpace->getFloat("right"));
-        }
-        else if (spaceName == "PADDING")
-        {
-            setPadding(innerSpace->getFloat("top"), innerSpace->getFloat("bottom"),
-                innerSpace->getFloat("left"), innerSpace->getFloat("right"));
-        }
-
-        innerSpace = properties->getNextNamespace();
-    }
+	_style = style;
+
+	if (properties)
+	{
+		// Properties not defined by the style.
+		const char * alignmentString = properties->getString("alignment");
+
+		_isAlignmentSet = alignmentString != NULL;
+		_alignment = getAlignment(alignmentString);
+
+		_autoWidth = parseAutoSize(properties->getString("autoWidth"));
+		_autoHeight = parseAutoSize(properties->getString("autoHeight"));
+
+		_consumeInputEvents = properties->getBool("consumeInputEvents", true);
+
+		_visible = properties->getBool("visible", true);
+
+		if (properties->exists("zIndex"))
+		{
+			_zIndex = properties->getInt("zIndex");
+		}
+		else
+		{
+			_zIndex = -1;
+		}
+
+		if (properties->exists("canFocus"))
+			_canFocus = properties->getBool("canFocus", false);
+
+		if (properties->exists("focusIndex"))
+		{
+			_focusIndex = properties->getInt("focusIndex");
+		}
+		else
+		{
+			_focusIndex = -1;
+		}
+
+		float bounds[4];
+		bool boundsBits[4];
+		if (properties->exists("position"))
+		{
+			parseCoordPair(properties->getString("position", "0,0"), &bounds[0], &bounds[1], &boundsBits[0], &boundsBits[1]);
+		}
+		else
+		{
+			bounds[0] = parseCoord(properties->getString("x", "0"), &boundsBits[0]);
+			bounds[1] = parseCoord(properties->getString("y", "0"), &boundsBits[1]);
+		}
+
+		if (properties->exists("size"))
+		{
+			parseCoordPair(properties->getString("size", "0,0"), &bounds[2], &bounds[3], &boundsBits[2], &boundsBits[3]);
+		}
+		else
+		{
+			bounds[2] = parseCoord(properties->getString("width", "0"), &boundsBits[2]);
+			bounds[3] = parseCoord(properties->getString("height", "0"), &boundsBits[3]);
+		}
+		setX(bounds[0], boundsBits[0]);
+		setY(bounds[1], boundsBits[1]);
+		setWidth(bounds[2], boundsBits[2]);
+		setHeight(bounds[3], boundsBits[3]);
+
+		const char* id = properties->getId();
+		if (id)
+			_id = id;
+
+		if (properties->exists("enabled"))
+		{
+			setEnabled(properties->getBool("enabled"));
+		}
+
+		// Register script listeners for control events
+		if (properties->exists("listener"))
+			addScriptCallback("controlEvent", properties->getString("listener"));
+
+		// Potentially override themed properties for all states.
+		overrideThemedProperties(properties, STATE_ALL);
+
+		// Override themed properties on specific states.
+		Properties* innerSpace = properties->getNextNamespace();
+		while (innerSpace != NULL)
+		{
+			std::string spaceName(innerSpace->getNamespace());
+			std::transform(spaceName.begin(), spaceName.end(), spaceName.begin(), (int(*)(int))toupper);
+			if (spaceName == "STATENORMAL")
+			{
+				overrideThemedProperties(innerSpace, NORMAL);
+			}
+			else if (spaceName == "STATEFOCUS")
+			{
+				overrideThemedProperties(innerSpace, FOCUS);
+			}
+			else if (spaceName == "STATEACTIVE")
+			{
+				overrideThemedProperties(innerSpace, ACTIVE);
+			}
+			else if (spaceName == "STATEDISABLED")
+			{
+				overrideThemedProperties(innerSpace, DISABLED);
+			}
+			else if (spaceName == "STATEHOVER")
+			{
+				overrideThemedProperties(innerSpace, HOVER);
+			}
+			else if (spaceName == "MARGIN")
+			{
+				setMargin(innerSpace->getFloat("top"), innerSpace->getFloat("bottom"),
+					innerSpace->getFloat("left"), innerSpace->getFloat("right"));
+			}
+			else if (spaceName == "PADDING")
+			{
+				setPadding(innerSpace->getFloat("top"), innerSpace->getFloat("bottom"),
+					innerSpace->getFloat("left"), innerSpace->getFloat("right"));
+			}
+
+			innerSpace = properties->getNextNamespace();
+		}
+	}
 }
 
 const char* Control::getId() const
@@ -210,6 +212,11 @@ const char* Control::getId() const
     return _id.c_str();
 }
 
+void Control::setId(const char* id)
+{
+	_id = id ? id : "";
+}
+
 float Control::getX() const
 {
     return _bounds.x;

+ 7 - 0
gameplay/src/Control.h

@@ -248,6 +248,13 @@ public:
      */
     const char* getId() const;
 
+	/**
+	 * Sets this control's ID string.
+	 *
+	 * @param id The new control ID.
+	 */
+	void setId(const char* id);
+
     /**
      * Get the x coordinate of this control.
      *

+ 2 - 4
gameplay/src/ControlFactory.cpp

@@ -10,13 +10,11 @@
 #include "Joystick.h"
 #include "ImageControl.h"
 
-
 namespace gameplay 
 {
 
 static ControlFactory* __controlFactory = NULL;
 
-
 ControlFactory::ControlFactory() 
 {
 	registerStandardControls();
@@ -55,12 +53,12 @@ void ControlFactory::unregisterCustomControl(const char* controlName)
 	}
 }
 
-Control *ControlFactory::createControl(const char* controlName, Theme::Style* style, Properties* properties, Theme* theme)
+Control *ControlFactory::createControl(const char* controlName, Theme::Style* style, Properties* properties)
 {
 	if (_registeredControls.find(controlName) == _registeredControls.end())
 		return NULL;
 
-	return (*_registeredControls[controlName])(style, properties, theme);
+	return (*_registeredControls[controlName])(style, properties);
 }
 
 void ControlFactory::registerStandardControls() 

+ 15 - 11
gameplay/src/ControlFactory.h

@@ -15,13 +15,15 @@ class Control;
  * @script{ignore}
  */
 class ControlFactory 
-{	
-public :
+{
+	friend class Container;
+
+public:
 
 	/**
 	 * The activator interface for controls that are created.
 	 */
-	typedef Control* (*ControlActivator)(Theme::Style*, Properties*, Theme*);
+	typedef Control* (*ControlActivator)(Theme::Style*, Properties*);
 
 	/**
 	 * Gets the single instance of the control factory used to create controls and register/unregister custom controls.
@@ -46,13 +48,6 @@ public :
 	 * @param controlName The name of the custom control to unregister.
 	 */
 	void unregisterCustomControl(const char* controlName);
-		
-	/**
-	 * Creates a controls from the set of core and custom controls registered.
-	 *
-	 * @param controlName The name of the control to create
-	 */
-	Control* createControl(const char* controlName, Theme::Style *style, Properties *properties, Theme *theme = NULL);
 
 private:
 
@@ -75,6 +70,16 @@ private:
 	 * Assignment operator
 	 */
 	ControlFactory &operator=(const ControlFactory&);
+
+	/**
+	* Creates a controls from the set of core and custom controls registered.
+	*
+	* @param controlName The name of the control to create.
+	* @param style The style to apply to the control.
+	* @param properties A Properties object describing the control (optional).
+	* @return The new control.
+	*/
+	Control* createControl(const char* controlName, Theme::Style *style, Properties *properties = NULL);
 	
 	/**
 	 * Registers the standard (built-in) controls
@@ -87,4 +92,3 @@ private:
 }
 
 #endif
-

+ 16 - 4
gameplay/src/Form.cpp

@@ -61,7 +61,18 @@ Form::~Form()
 
 Form* Form::create(const char* id, Theme::Style* style, Layout::Type layoutType)
 {
-    GP_ASSERT(style);
+	Theme* theme;
+	if (style)
+	{
+		theme = style->getTheme();
+	}
+	else
+	{
+		theme = Theme::getDefault();
+		style = theme->getStyle("form");
+	}
+
+	GP_ASSERT(style);
 
     Layout* layout;
     switch (layoutType)
@@ -76,7 +87,8 @@ Form* Form::create(const char* id, Theme::Style* style, Layout::Type layoutType)
         layout = VerticalLayout::create();
         break;
     default:
-        GP_ERROR("Unsupported layout type '%d'.", layoutType);
+		layout = AbsoluteLayout::create();
+        GP_WARN("Unsupported layout type '%d'.", layoutType);
         break;
     }
 
@@ -85,7 +97,7 @@ Form* Form::create(const char* id, Theme::Style* style, Layout::Type layoutType)
         form->_id = id;
     form->_style = style;
     form->_layout = layout;
-    form->_theme = style->getTheme();
+	form->_theme = theme;
     form->_theme->addRef();
 
     form->updateFrameBuffer();
@@ -130,7 +142,7 @@ Form* Form::create(const char* url)
     form->_theme = theme;
 
     // Initialize common container properties
-    form->initialize(style, formProperties, theme);
+    form->initialize(style, formProperties);
 
     form->updateFrameBuffer();
 

+ 11 - 2
gameplay/src/Form.h

@@ -17,7 +17,12 @@ namespace gameplay
 class Theme;
 
 /**
- * Top-level container of UI controls.  The following properties are available for forms:
+ * Top-level container of UI controls.
+ 
+   Child controls and containers can be created and added to a form using the
+   Container::addControl and Container::insertControl methods.
+
+   The following properties are available for forms:
 
  @verbatim
     form <formID>
@@ -68,9 +73,13 @@ public:
 
     /**
      * Create a new Form.
+	 *
+	 * The specified style defines the visual style for the form. If NULL is passed
+	 * for the style, the default UI theme is used. All controls attached to this
+	 * form will inherit the theme that contains the form's style.
      *
      * @param id The Form's ID.
-     * @param style The Form's style.
+     * @param style The Form's custom style (optional).
      * @param layoutType The form's layout type.
      *
      * @return The new Form.

+ 25 - 38
gameplay/src/ImageControl.cpp

@@ -15,21 +15,7 @@ ImageControl::~ImageControl()
     SAFE_DELETE(_batch);
 }
 
-ImageControl* ImageControl::create(const char* id, Theme::Style* style)
-{
-    GP_ASSERT(style);
-
-    ImageControl* imageControl = new ImageControl();
-    if (id)
-        imageControl->_id = id;
-    imageControl->setStyle(style);
-
-    imageControl->_focusIndex = -2;
-
-    return imageControl;
-}
-
-Control* ImageControl::create(Theme::Style* style, Properties* properties, Theme* theme)
+Control* ImageControl::create(Theme::Style* style, Properties* properties)
 {
     ImageControl* imageControl = new ImageControl();
     imageControl->initialize(style, properties);
@@ -41,29 +27,30 @@ Control* ImageControl::create(Theme::Style* style, Properties* properties, Theme
 
 void ImageControl::initialize(Theme::Style* style, Properties* properties)
 {
-    GP_ASSERT(properties);
-
-    Control::initialize(style, properties);
-
-    std::string path;
-    if (properties->getPath("path", &path))
-    {
-        setImage(path.c_str());
-    }
-
-    if (properties->exists("srcRegion"))
-    {
-        Vector4 region;
-        properties->getVector4("srcRegion", &region);
-        setRegionSrc(region.x, region.y, region.z, region.w);
-    }
-
-    if (properties->exists("dstRegion"))
-    {
-        Vector4 region;
-        properties->getVector4("dstRegion", &region);
-        setRegionDst(region.x, region.y, region.z, region.w);
-    }
+	Control::initialize(style, properties);
+
+	if (properties)
+	{
+		std::string path;
+		if (properties->getPath("path", &path))
+		{
+			setImage(path.c_str());
+		}
+
+		if (properties->exists("srcRegion"))
+		{
+			Vector4 region;
+			properties->getVector4("srcRegion", &region);
+			setRegionSrc(region.x, region.y, region.z, region.w);
+		}
+
+		if (properties->exists("dstRegion"))
+		{
+			Vector4 region;
+			properties->getVector4("dstRegion", &region);
+			setRegionDst(region.x, region.y, region.z, region.w);
+		}
+	}
 }
 
 void ImageControl::setImage(const char* path)

+ 3 - 14
gameplay/src/ImageControl.h

@@ -40,17 +40,6 @@ class ImageControl : public Control
 
 public:
 
-    /**
-     * Create a new ImageControl.
-     *
-     * @param id The control's ID.
-     * @param style The control's style.
-     *
-     * @return The new ImageControl.
-     * @script{create}
-     */
-    static ImageControl* create(const char* id, Theme::Style* style);
-
     /**
      * Set the path of the image for this ImageControl to display.
      *
@@ -94,7 +83,7 @@ public:
      * @param height The height of the destination region.
      */
     void setRegionDst(float x, float y, float width, float height);
-    
+
     /**
      * Sets the destination region of this ImageControl.  This is the region
      * within the control's viewport to draw the image.
@@ -115,10 +104,10 @@ public:
 protected:
 
     ImageControl();
-    
+
     virtual ~ImageControl();
 
-    static Control* create(Theme::Style* style, Properties* properties, Theme* theme = NULL);
+    static Control* create(Theme::Style* style, Properties* properties = NULL);
 
     virtual void initialize(Theme::Style* style, Properties* properties);
 

+ 79 - 80
gameplay/src/Joystick.cpp

@@ -16,23 +16,11 @@ Joystick::~Joystick()
         SAFE_DELETE(_outerSize);
 }
 
-Joystick* Joystick::create(const char* id, Theme::Style* style)
-{
-    GP_ASSERT(style);
-
-    Joystick* joystick = new Joystick();
-    if (id)
-        joystick->_id = id;
-    joystick->setStyle(style);
-
-    return joystick;
-}
-
-Control* Joystick::create(Theme::Style* style, Properties* properties, Theme* theme)
+Control* Joystick::create(Theme::Style* style, Properties* properties)
 {
     Joystick* joystick = new Joystick();
     joystick->initialize(style, properties);
-    joystick->_index = properties->getInt("index");
+
     return joystick;
 }
 
@@ -42,72 +30,83 @@ void Joystick::initialize(Theme::Style* style, Properties* properties)
 
     Control::initialize(style, properties);
 
-    Control::State state = getState();
-
-    if (!properties->exists("radius"))
-    {
-        GP_ERROR("Failed to load joystick; required attribute 'radius' is missing.");
-        return;
-    }
-    _radius = properties->getFloat("radius");
-    GP_ASSERT(_radius != 0.0f);
-
-    if (properties->exists("relative"))
-    {
-        setRelative(properties->getBool("relative"));
-    }
-    else
-    {
-        setRelative(false);
-    }
-
-    Theme::ThemeImage* inner = getImage("inner", state);
-    if (inner)
-    {
-        _innerSize = new Vector2();
-        Vector2 innerSize;
-        if (properties->getVector2("innerRegion", &innerSize))
-        {
-            _innerSize->set(innerSize.x, innerSize.y);
-        }
-        else
-        {
-            const Rectangle& rect = inner->getRegion();
-            _innerSize->set(rect.width, rect.height);
-        }
-    }
-
-    Theme::ThemeImage* outer = getImage("outer", state);
-    if (outer)
-    {
-        _outerSize = new Vector2();
-        Vector2 outerSize;
-        if (properties->getVector2("outerRegion", &outerSize))
-        {
-            _outerSize->set(outerSize.x, outerSize.y);
-        }
-        else
-        {
-            const Rectangle& rect = outer->getRegion();
-            _outerSize->set(rect.width, rect.height);
-        }
-        _screenRegion.width = _outerSize->x;
-        _screenRegion.height = _outerSize->y;
-    }
-    else
-    {
-        if (inner)
-        {
-            const Rectangle& rect = inner->getRegion();
-            _screenRegion.width = rect.width;
-            _screenRegion.height = rect.height;
-        }
-        else
-        {
-            _screenRegion.width = _radius * 2.0f;
-            _screenRegion.height = _screenRegion.width;
-        }
-    }
+	if (!properties)
+	{
+		GP_WARN("Joystick creation without properties object is unsupported.");
+		return;
+	}
+
+	Control::State state = getState();
+
+	if (!properties->exists("radius"))
+	{
+		GP_WARN("Joystick: required attribute 'radius' is missing.");
+	}
+	else
+	{
+		_radius = properties->getFloat("radius");
+		if (_radius < 1.0f)
+			_radius = 1.0f;
+	}
+
+	if (properties->exists("relative"))
+	{
+		setRelative(properties->getBool("relative"));
+	}
+	else
+	{
+		setRelative(false);
+	}
+
+	Theme::ThemeImage* inner = getImage("inner", state);
+	if (inner)
+	{
+		_innerSize = new Vector2();
+		Vector2 innerSize;
+		if (properties->getVector2("innerRegion", &innerSize))
+		{
+			_innerSize->set(innerSize.x, innerSize.y);
+		}
+		else
+		{
+			const Rectangle& rect = inner->getRegion();
+			_innerSize->set(rect.width, rect.height);
+		}
+	}
+
+	Theme::ThemeImage* outer = getImage("outer", state);
+	if (outer)
+	{
+		_outerSize = new Vector2();
+		Vector2 outerSize;
+		if (properties->getVector2("outerRegion", &outerSize))
+		{
+			_outerSize->set(outerSize.x, outerSize.y);
+		}
+		else
+		{
+			const Rectangle& rect = outer->getRegion();
+			_outerSize->set(rect.width, rect.height);
+		}
+		_screenRegion.width = _outerSize->x;
+		_screenRegion.height = _outerSize->y;
+	}
+	else
+	{
+		if (inner)
+		{
+			const Rectangle& rect = inner->getRegion();
+			_screenRegion.width = rect.width;
+			_screenRegion.height = rect.height;
+		}
+		else
+		{
+			_screenRegion.width = _radius * 2.0f;
+			_screenRegion.height = _screenRegion.width;
+		}
+	}
+
+	_index = properties->getInt("index");
 }
 
 void Joystick::addListener(Control::Listener* listener, int eventFlags)

+ 183 - 195
gameplay/src/Joystick.h

@@ -1,195 +1,183 @@
-#ifndef JOYSTICK_H_
-#define JOYSTICK_H_
-
-#include "Control.h"
-
-namespace gameplay
-{
-
-/**
- * Defines a control representing a joystick (axis).
- *
-  @verbatim
-    slider
-    {
-        style       = <styleID>                 // A Style from the Theme.
-        position    = <x, y>                    // Position of the Control on-screen, measured in pixels.
-        alignment   = <Control::Alignment constant> // Note: 'position' will be ignored.
-        size        = <width, height>           // Size of the Control, measured in pixels.
-        radius      = <float>                   // The value of the left- / bottom-most point on the slider.
-        consumeEvents = <bool>                  // Whether the slider propagates input events to the Game's input event handler. Default is true.
-    }
- @endverbatim
- */
-class Joystick : public Control
-{
-    friend class Container;
-    friend class Gamepad;
-	friend class ControlFactory;
-
-public:
-
-    /**
-     * Create a new joystick control.
-     *
-     * @param id The control's ID.
-     * @param style The control's style.
-     *
-     * @return The new joystick.
-     * @script{create}
-     */
-    static Joystick* create(const char* id, Theme::Style* style);
-    
-    /**
-     * Add a listener to be notified of specific events affecting
-     * this control.  Event types can be OR'ed together.
-     * E.g. To listen to touch-press and touch-release events,
-     * pass <code>Control::Listener::TOUCH | Control::Listener::RELEASE</code>
-     * as the second parameter.
-     *
-     * @param listener The listener to add.
-     * @param eventFlags The events to listen for.
-     */
-    void addListener(Control::Listener* listener, int eventFlags);
-
-    /**
-     * Retrieves the value (2-dimensional direction) of the joystick.
-     * 
-     * @return The value of the joystick.
-     */
-    inline const Vector2& getValue() const;
-
-    /**
-     * Sets the image size of the inner region of the joystick. Does not do anything if there is no
-     * inner image region defined.
-     * 
-     * @param size The size of the inner region of the joystick. (x, y) == (width, height)
-     */
-    inline void setInnerRegionSize(const Vector2& size);
-
-    /**
-     * Gets the image size of the inner region of the joystick. Returns (0,0) if there is no inner image
-     * region defined.
-     * 
-     * @return The image size of the inner region of the joystick. (x, y) == (width, height)
-     */
-    inline const Vector2& getInnerRegionSize() const;
-
-    /**
-     * Sets the image size of the outer region of the joystick. Does not do anything if there is no
-     * outer image region defined.
-     * 
-     * @param size The size of the outer region of the joystick. (x, y) == (width, height)
-     */
-    inline void setOuterRegionSize(const Vector2& size);
-
-    /**
-     * Gets the image size of the outer region of the joystick. Returns (0,0) if there is no outer image
-     * region defined.
-     * 
-     * @return The image size of the outer region of the joystick. (x, y) == (width, height)
-     */
-    inline const Vector2& getOuterRegionSize() const;
-
-    /**
-     * Sets whether relative positioning is enabled or not.
-     * 
-     * Note: The default behavior is absolute positioning, and not relative.
-     *
-     * @param relative Whether relative positioning should be enabled or not.
-     */
-    inline void setRelative(bool relative);
-
-    /**
-     * Retrieves whether absolute positioning is enabled or not.
-     * 
-     * Note: The default behavior is absolute positioning, and not relative.
-     *
-     * @return <code>true</code> if relative positioning is enabled; <code>false</code> otherwise.
-     */
-    inline bool isRelative() const;
-
-    /**
-     * @see Control::getType
-     */
-    const char* getType() const;
-
-    /**
-     * Gets the index of this joystick across all joysticks on a form.
-     *
-     * @return The index of this joystick on a form.
-     */
-    inline const unsigned int getIndex() const;
-
-protected:
-    
-    /**
-     * Constructor.
-     */
-    Joystick();
-
-    /**
-     * Destructor.
-     */
-    virtual ~Joystick();
-
-    /**
-     * Create a joystick with a given style and properties.
-     *
-     * @param style The style to apply to this joystick.
-     * @param properties The properties to set on this joystick.
-     * @param theme The theme to set on this control if needed.
-	 *
-     * @return The new joystick.
-     */
-    static Control* create(Theme::Style* style, Properties* properties, Theme *theme = NULL);
-
-    /**
-     * Initialize this joystick.
-     */
-    virtual void initialize(Theme::Style* style, Properties* properties);
-
-    /**
-     * Touch callback on touch events.  Controls return true if they consume the touch event.
-     *
-     * @param evt The touch event that occurred.
-     * @param x The x position of the touch in pixels. Left edge is zero.
-     * @param y The y position of the touch in pixels. Top edge is zero.
-     * @param contactIndex The order of occurrence for multiple touch contacts starting at zero.
-     *
-     * @return Whether the touch event was consumed by the control.
-     *
-     * @see Touch::TouchEvent
-     */
-    bool touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex);
-
-    /**
-     * Draw the images associated with this control.
-     *
-     * @param spriteBatch The sprite batch containing this control's icons.
-     * @param clip The clipping rectangle of this control's parent container.
-     */
-    void drawImages(SpriteBatch* spriteBatch, const Rectangle& clip);
-
-private:
-
-    /**
-     * Copy constructor.
-     */
-    Joystick(const Joystick& copy);
-
-    float _radius; 
-    bool _relative;
-    Rectangle _screenRegion;
-    Vector2 _value;
-    Vector2 _displacement;
-    Vector2* _innerSize;
-    Vector2* _outerSize;
-    unsigned int _index;
-};
-
-}
-
-#include "Joystick.inl"
-
-#endif
+#ifndef JOYSTICK_H_
+#define JOYSTICK_H_
+
+#include "Control.h"
+
+namespace gameplay
+{
+
+/**
+ * Defines a control representing a joystick (axis).
+ *
+  @verbatim
+    slider
+    {
+        style       = <styleID>                 // A Style from the Theme.
+        position    = <x, y>                    // Position of the Control on-screen, measured in pixels.
+        alignment   = <Control::Alignment constant> // Note: 'position' will be ignored.
+        size        = <width, height>           // Size of the Control, measured in pixels.
+        radius      = <float>                   // The value of the left- / bottom-most point on the slider.
+        consumeEvents = <bool>                  // Whether the slider propagates input events to the Game's input event handler. Default is true.
+    }
+ @endverbatim
+ */
+class Joystick : public Control
+{
+    friend class Container;
+    friend class Gamepad;
+	friend class ControlFactory;
+
+public:
+
+    /**
+     * Add a listener to be notified of specific events affecting
+     * this control.  Event types can be OR'ed together.
+     * E.g. To listen to touch-press and touch-release events,
+     * pass <code>Control::Listener::TOUCH | Control::Listener::RELEASE</code>
+     * as the second parameter.
+     *
+     * @param listener The listener to add.
+     * @param eventFlags The events to listen for.
+     */
+    void addListener(Control::Listener* listener, int eventFlags);
+
+    /**
+     * Retrieves the value (2-dimensional direction) of the joystick.
+     * 
+     * @return The value of the joystick.
+     */
+    inline const Vector2& getValue() const;
+
+    /**
+     * Sets the image size of the inner region of the joystick. Does not do anything if there is no
+     * inner image region defined.
+     * 
+     * @param size The size of the inner region of the joystick. (x, y) == (width, height)
+     */
+    inline void setInnerRegionSize(const Vector2& size);
+
+    /**
+     * Gets the image size of the inner region of the joystick. Returns (0,0) if there is no inner image
+     * region defined.
+     * 
+     * @return The image size of the inner region of the joystick. (x, y) == (width, height)
+     */
+    inline const Vector2& getInnerRegionSize() const;
+
+    /**
+     * Sets the image size of the outer region of the joystick. Does not do anything if there is no
+     * outer image region defined.
+     * 
+     * @param size The size of the outer region of the joystick. (x, y) == (width, height)
+     */
+    inline void setOuterRegionSize(const Vector2& size);
+
+    /**
+     * Gets the image size of the outer region of the joystick. Returns (0,0) if there is no outer image
+     * region defined.
+     * 
+     * @return The image size of the outer region of the joystick. (x, y) == (width, height)
+     */
+    inline const Vector2& getOuterRegionSize() const;
+
+    /**
+     * Sets whether relative positioning is enabled or not.
+     * 
+     * Note: The default behavior is absolute positioning, and not relative.
+     *
+     * @param relative Whether relative positioning should be enabled or not.
+     */
+    inline void setRelative(bool relative);
+
+    /**
+     * Retrieves whether absolute positioning is enabled or not.
+     * 
+     * Note: The default behavior is absolute positioning, and not relative.
+     *
+     * @return <code>true</code> if relative positioning is enabled; <code>false</code> otherwise.
+     */
+    inline bool isRelative() const;
+
+    /**
+     * @see Control::getType
+     */
+    const char* getType() const;
+
+    /**
+     * Gets the index of this joystick across all joysticks on a form.
+     *
+     * @return The index of this joystick on a form.
+     */
+    inline const unsigned int getIndex() const;
+
+protected:
+    
+    /**
+     * Constructor.
+     */
+    Joystick();
+
+    /**
+     * Destructor.
+     */
+    virtual ~Joystick();
+
+    /**
+     * Create a joystick with a given style and properties.
+     *
+     * @param style The style to apply to this joystick.
+     * @param properties A properties object containing a definition of the joystick (optional).
+	 *
+     * @return The new joystick.
+     */
+    static Control* create(Theme::Style* style, Properties* properties = NULL);
+
+    /**
+     * Initialize this joystick.
+     */
+    virtual void initialize(Theme::Style* style, Properties* properties);
+
+    /**
+     * Touch callback on touch events.  Controls return true if they consume the touch event.
+     *
+     * @param evt The touch event that occurred.
+     * @param x The x position of the touch in pixels. Left edge is zero.
+     * @param y The y position of the touch in pixels. Top edge is zero.
+     * @param contactIndex The order of occurrence for multiple touch contacts starting at zero.
+     *
+     * @return Whether the touch event was consumed by the control.
+     *
+     * @see Touch::TouchEvent
+     */
+    bool touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex);
+
+    /**
+     * Draw the images associated with this control.
+     *
+     * @param spriteBatch The sprite batch containing this control's icons.
+     * @param clip The clipping rectangle of this control's parent container.
+     */
+    void drawImages(SpriteBatch* spriteBatch, const Rectangle& clip);
+
+private:
+
+    /**
+     * Copy constructor.
+     */
+    Joystick(const Joystick& copy);
+
+    float _radius; 
+    bool _relative;
+    Rectangle _screenRegion;
+    Vector2 _value;
+    Vector2 _displacement;
+    Vector2* _innerSize;
+    Vector2* _outerSize;
+    unsigned int _index;
+};
+
+}
+
+#include "Joystick.inl"
+
+#endif

+ 12 - 25
gameplay/src/Label.cpp

@@ -12,41 +12,28 @@ Label::~Label()
 {
 }
 
-Label* Label::create(const char* id, Theme::Style* style)
+Control* Label::create(Theme::Style* style, Properties* properties)
 {
-    GP_ASSERT(style);
-
     Label* label = new Label();
-    if (id)
-        label->_id = id;
-    label->setStyle(style);
+	label->initialize(style, properties);
 
-    // Ensure that labels cannot receive focus.
-    label->_focusIndex = -2;
-
-    return label;
-}
-
-Control* Label::create(Theme::Style* style, Properties* properties, Theme* theme)
-{
-    Label* label = new Label();
-    label->initialize(style, properties);
-
-    label->_focusIndex = -2;
+	label->_focusIndex = -2;
 
     return label;
 }
 
 void Label::initialize(Theme::Style* style, Properties* properties)
 {
-    GP_ASSERT(properties);
-
     Control::initialize(style, properties);
-    const char* text = properties->getString("text");
-    if (text)
-    {
-        _text = text;
-    }
+
+	if (properties)
+	{
+		const char* text = properties->getString("text");
+		if (text)
+		{
+			_text = text;
+		}
+	}
 }
 
 void Label::addListener(Control::Listener* listener, int eventFlags)

+ 143 - 154
gameplay/src/Label.h

@@ -1,154 +1,143 @@
-#ifndef LABEL_H_
-#define LABEL_H_
-
-#include "Control.h"
-#include "Theme.h"
-
-namespace gameplay
-{
-
-/**
- * A label is the most basic type of control, capable only of rendering text within its border.
- *
- * The following properties are available for labels:
-
- @verbatim
-    label <labelID>
-    {
-         style       = <styleID>
-         alignment   = <Control::Alignment constant> // Note: 'position' will be ignored.
-         position    = <x, y>
-         autoWidth   = <bool>
-         autoHeight  = <bool>
-         size        = <width, height>
-         width       = <width>   // Can be used in place of 'size', e.g. with 'autoHeight = true'
-         height      = <height>  // Can be used in place of 'size', e.g. with 'autoWidth = true'
-         text        = <string>
-         consumeEvents = <bool>  // Whether the label propagates input events to the Game's input event handler. Default is true.
-    }
- @endverbatim
- */
-class Label : public Control
-{
-    friend class Container;
-	friend class ControlFactory;
-
-public:
-
-    /**
-     * Create a new label control.
-     *
-     * @param id The control's ID.
-     * @param style The control's style.
-     *
-     * @return The new label.
-     * @script{create}
-     */
-    static Label* create(const char*id, Theme::Style* style);
-
-    /**
-     * Set the text for this label to display.
-     *
-     * @param text The text to display.
-     */
-    void setText(const char* text);
-
-    /**
-     * Get the text displayed by this label.
-     *
-     * @return The text displayed by this label.
-     */
-    const char* getText();
-
-    /**
-     * @see Control::getType
-     */
-    const char* getType() const;
-
-    /**
-     * Add a listener to be notified of specific events affecting
-     * this control.  Event types can be OR'ed together.
-     * E.g. To listen to touch-press and touch-release events,
-     * pass <code>Control::Listener::TOUCH | Control::Listener::RELEASE</code>
-     * as the second parameter.
-     *
-     * @param listener The listener to add.
-     * @param eventFlags The events to listen for.
-     */
-    virtual void addListener(Control::Listener* listener, int eventFlags);
-
-protected:
-
-    /**
-     * Constructor.
-     */
-    Label();
-
-    /**
-     * Destructor.
-     */
-    virtual ~Label();
-
-    /**
-     * Create a label with a given style and properties.
-     *
-     * @param style The style to apply to this label.
-     * @param properties The properties to set on this label.
-     * @param theme The theme to set on this control if needed.
-	 * 
-     * @return The new label.
-     */
-    static Control* create(Theme::Style* style, Properties* properties, Theme *theme = NULL);
-
-    /**
-     * Initialize this label.
-     */
-    virtual void initialize(Theme::Style* style, Properties* properties);
-
-    /**
-     * Called when a label's properties change. Updates this label's internal rendering
-     * properties, such as its text viewport.
-     *
-     * @param container This label's parent container.
-     * @param offset The scroll offset of this label's parent container.
-     */
-    void update(const Control* container, const Vector2& offset);
-
-    /**
-     * Draw this label's text.
-     *
-     * @param clip The clipping rectangle of this label's parent container.
-     */
-    virtual void drawText(const Rectangle& clip);
-
-    /**
-     * The text displayed by this label.
-     */
-    std::string _text;
-
-    /**
-     * The font being used to display the label.
-     */
-    Font* _font;
-    
-    /**
-     * The text color being used to display the label.
-     */
-    Vector4 _textColor;
-
-    /**
-     * The position and size of this control's text area, before clipping.  Used for text alignment.
-     */
-    Rectangle _textBounds;
-
-private:
-
-    /**
-     * Constructor.
-     */
-    Label(const Label& copy);
-};
-
-}
-
-#endif
+#ifndef LABEL_H_
+#define LABEL_H_
+
+#include "Control.h"
+#include "Theme.h"
+
+namespace gameplay
+{
+
+/**
+ * A label is the most basic type of control, capable only of rendering text within its border.
+ *
+ * The following properties are available for labels:
+
+ @verbatim
+    label <labelID>
+    {
+         style       = <styleID>
+         alignment   = <Control::Alignment constant> // Note: 'position' will be ignored.
+         position    = <x, y>
+         autoWidth   = <bool>
+         autoHeight  = <bool>
+         size        = <width, height>
+         width       = <width>   // Can be used in place of 'size', e.g. with 'autoHeight = true'
+         height      = <height>  // Can be used in place of 'size', e.g. with 'autoWidth = true'
+         text        = <string>
+         consumeEvents = <bool>  // Whether the label propagates input events to the Game's input event handler. Default is true.
+    }
+ @endverbatim
+ */
+class Label : public Control
+{
+    friend class Container;
+	friend class ControlFactory;
+
+public:
+
+    /**
+     * Set the text for this label to display.
+     *
+     * @param text The text to display.
+     */
+    void setText(const char* text);
+
+    /**
+     * Get the text displayed by this label.
+     *
+     * @return The text displayed by this label.
+     */
+    const char* getText();
+
+    /**
+     * @see Control::getType
+     */
+    const char* getType() const;
+
+    /**
+     * Add a listener to be notified of specific events affecting
+     * this control.  Event types can be OR'ed together.
+     * E.g. To listen to touch-press and touch-release events,
+     * pass <code>Control::Listener::TOUCH | Control::Listener::RELEASE</code>
+     * as the second parameter.
+     *
+     * @param listener The listener to add.
+     * @param eventFlags The events to listen for.
+     */
+    virtual void addListener(Control::Listener* listener, int eventFlags);
+
+protected:
+
+    /**
+     * Constructor.
+     */
+    Label();
+
+    /**
+     * Destructor.
+     */
+    virtual ~Label();
+
+	/**
+	* Create a new label control.
+	*
+	* @param style The control's custom style.
+	* @param properties A properties object containing a definition of the label (optional).
+	*
+	* @return The new label.
+	* @script{create}
+	*/
+	static Control* create(Theme::Style* style, Properties* properties = NULL);
+
+    /**
+     * Initialize this label.
+     */
+    virtual void initialize(Theme::Style* style, Properties* properties);
+
+    /**
+     * Called when a label's properties change. Updates this label's internal rendering
+     * properties, such as its text viewport.
+     *
+     * @param container This label's parent container.
+     * @param offset The scroll offset of this label's parent container.
+     */
+    void update(const Control* container, const Vector2& offset);
+
+    /**
+     * Draw this label's text.
+     *
+     * @param clip The clipping rectangle of this label's parent container.
+     */
+    virtual void drawText(const Rectangle& clip);
+
+    /**
+     * The text displayed by this label.
+     */
+    std::string _text;
+
+    /**
+     * The font being used to display the label.
+     */
+    Font* _font;
+    
+    /**
+     * The text color being used to display the label.
+     */
+    Vector4 _textColor;
+
+    /**
+     * The position and size of this control's text area, before clipping.  Used for text alignment.
+     */
+    Rectangle _textBounds;
+
+private:
+
+    /**
+     * Constructor.
+     */
+    Label(const Label& copy);
+};
+
+}
+
+#endif

+ 17 - 30
gameplay/src/RadioButton.cpp

@@ -19,40 +19,27 @@ RadioButton::~RadioButton()
     }
 }
 
-RadioButton* RadioButton::create(const char* id, Theme::Style* style)
+Control* RadioButton::create(Theme::Style* style, Properties* properties)
 {
-    GP_ASSERT(style);
-
-    RadioButton* radioButton = new RadioButton();
-    if (id)
-        radioButton->_id = id;
-    radioButton->setStyle(style);
-
-    __radioButtons.push_back(radioButton);
-
-    return radioButton;
-}
-
-Control* RadioButton::create(Theme::Style* style, Properties* properties, Theme* theme)
-{
-    GP_ASSERT(properties);
-
     RadioButton* radioButton = new RadioButton();
     radioButton->initialize(style, properties);
 
-    properties->getVector2("imageSize", &radioButton->_imageSize);
-
-    if (properties->getBool("selected"))
-    {
-        RadioButton::clearSelected(radioButton->_groupId);
-        radioButton->_selected = true;
-    }
-
-    const char* groupId = properties->getString("group");
-    if (groupId)
-    {
-        radioButton->_groupId = groupId;
-    }
+	if (properties)
+	{
+		properties->getVector2("imageSize", &radioButton->_imageSize);
+
+		if (properties->getBool("selected"))
+		{
+			RadioButton::clearSelected(radioButton->_groupId);
+			radioButton->_selected = true;
+		}
+
+		const char* groupId = properties->getString("group");
+		if (groupId)
+		{
+			radioButton->_groupId = groupId;
+		}
+	}
 
     __radioButtons.push_back(radioButton);
 

+ 2 - 14
gameplay/src/RadioButton.h

@@ -41,17 +41,6 @@ class RadioButton : public Button
 
 public:
 
-    /**
-     * Create a new radio button control.
-     *
-     * @param id The control's ID.
-     * @param style The control's style.
-     *
-     * @return The new radio button.
-     * @script{create}
-     */
-    static RadioButton* create(const char* id, Theme::Style* style);
-
     /**
      * Get whether this radio button is currently selected.
      *
@@ -125,12 +114,11 @@ protected:
      * Create a radio button with a given style and properties.
      *
      * @param style The style to apply to this radio button.
-     * @param properties The properties to set on this radio button.
-     * @param theme The theme to set on this control if needed.
+     * @param properties A properties object containing a definition of the radio button (optional).
      *
      * @return The new radio button.
      */
-    static Control* create(Theme::Style* style, Properties* properties, Theme* theme = NULL);
+    static Control* create(Theme::Style* style, Properties* properties = NULL);
 
     /**
      * Keyboard callback on key events.

+ 15 - 26
gameplay/src/Slider.cpp

@@ -24,36 +24,25 @@ Slider::~Slider()
 {
 }
 
-Slider* Slider::create(const char* id, Theme::Style* style)
+Control* Slider::create(Theme::Style* style, Properties* properties)
 {
-    GP_ASSERT(style);
-
-    Slider* slider = new Slider();
-    if (id)
-        slider->_id = id;
-    slider->setStyle(style);
-
-    return slider;
-}
-
-Control* Slider::create(Theme::Style* style, Properties* properties, Theme* theme)
-{
-    GP_ASSERT(properties);
-
     Slider* slider = new Slider();
     slider->initialize(style, properties);
 
-    slider->_min = properties->getFloat("min");
-    slider->_max = properties->getFloat("max");
-    slider->_value = properties->getFloat("value");
-    slider->_step = properties->getFloat("step");
-    slider->_valueTextVisible = properties->getBool("valueTextVisible");
-    slider->_valueTextPrecision = properties->getInt("valueTextPrecision");
-
-    if (properties->exists("valueTextAlignment"))
-    {
-        slider->_valueTextAlignment = Font::getJustify(properties->getString("valueTextAlignment"));
-    }
+	if (properties)
+	{
+		slider->_min = properties->getFloat("min");
+		slider->_max = properties->getFloat("max");
+		slider->_value = properties->getFloat("value");
+		slider->_step = properties->getFloat("step");
+		slider->_valueTextVisible = properties->getBool("valueTextVisible");
+		slider->_valueTextPrecision = properties->getInt("valueTextPrecision");
+
+		if (properties->exists("valueTextAlignment"))
+		{
+			slider->_valueTextAlignment = Font::getJustify(properties->getString("valueTextAlignment"));
+		}
+	}
 
     return slider;
 }

+ 2 - 14
gameplay/src/Slider.h

@@ -37,17 +37,6 @@ class Slider : public Label
 
 public:
 
-    /**
-     * Create a new slider control.
-     *
-     * @param id The control's ID.
-     * @param style The control's style.
-     *
-     * @return The new slider.
-     * @script{create}
-     */
-    static Slider* create(const char* id, Theme::Style* style);
-
     /**
      * Set the minimum value that can be set on this slider.
      *
@@ -181,12 +170,11 @@ protected:
      * Create a slider with a given style and properties.
      *
      * @param style The style to apply to this slider.
-     * @param properties The properties to set on this slider.
-     * @param theme The theme to set on this control if needed.
+     * @param properties A properties object containing a definition of the slider (optional).
      *
      * @return The new slider.
      */
-    static Control* create(Theme::Style* style, Properties* properties, Theme* theme = NULL);
+    static Control* create(Theme::Style* style, Properties* properties = NULL);
 
     /**
      * Touch callback on touch events.  Controls return true if they consume the touch event.

+ 6 - 16
gameplay/src/TextBox.cpp

@@ -18,19 +18,7 @@ TextBox::~TextBox()
 {
 }
 
-TextBox* TextBox::create(const char* id, Theme::Style* style)
-{
-    GP_ASSERT(style);
-
-    TextBox* textBox = new TextBox();
-    if (id)
-        textBox->_id = id;
-    textBox->setStyle(style);
-
-    return textBox;
-}
-
-Control* TextBox::create(Theme::Style* style, Properties* properties, Theme* theme)
+Control* TextBox::create(Theme::Style* style, Properties* properties)
 {
     TextBox* textBox = new TextBox();
     textBox->initialize(style, properties);
@@ -40,10 +28,12 @@ Control* TextBox::create(Theme::Style* style, Properties* properties, Theme* the
 
 void TextBox::initialize(Theme::Style* style, Properties* properties)
 {
-    GP_ASSERT(properties);
-
     Label::initialize(style, properties);
-    _inputMode = getInputMode(properties->getString("inputMode"));
+
+	if (properties)
+	{
+		_inputMode = getInputMode(properties->getString("inputMode"));
+	}
 }
 
 int TextBox::getLastKeypress()

+ 2 - 14
gameplay/src/TextBox.h

@@ -57,17 +57,6 @@ public:
         PASSWORD = 0x02
     };
 
-    /**
-     * Create a new text box control.
-     *
-     * @param id The control's ID.
-     * @param style The control's style.
-     *
-     * @return The new text box.
-     * @script{create}
-     */
-    static TextBox* create(const char* id, Theme::Style* style);
-
     /**
      * Initialize this textbox.
      */
@@ -143,12 +132,11 @@ protected:
      * Create a text box with a given style and properties.
      *
      * @param style The style to apply to this text box.
-     * @param properties The properties to set on this text box.
-     * @param theme The theme to set on this control if needed.
+     * @param properties A properties object containing a definition of the text box (optional).
      *
      * @return The new text box.
      */
-    static Control* create(Theme::Style* style, Properties* properties, Theme* theme = NULL);
+    static Control* create(Theme::Style* style, Properties* properties = NULL);
 
     /**
      * Touch callback on touch events.  Controls return true if they consume the touch event.

+ 38 - 0
gameplay/src/Theme.cpp

@@ -1,11 +1,14 @@
 #include "Base.h"
 #include "Theme.h"
 #include "ThemeStyle.h"
+#include "Game.h"
+#include "FileSystem.h"
 
 namespace gameplay
 {
 
 static std::vector<Theme*> __themeCache;
+static Theme* __defaultTheme = NULL;
 
 Theme::Theme()
 {
@@ -47,6 +50,39 @@ Theme::~Theme()
     {
         __themeCache.erase(itr);
     }
+
+	if (__defaultTheme == this)
+		__defaultTheme = NULL;
+}
+
+Theme* Theme::getDefault()
+{
+	if (!__defaultTheme)
+	{
+		// Check game.config for a default theme setting
+		Properties* config = Game::getInstance()->getConfig()->getNamespace("ui", true);
+		if (config)
+		{
+			const char* defaultTheme = config->getString("defaultTheme");
+			if (defaultTheme && FileSystem::fileExists(defaultTheme))
+				__defaultTheme = Theme::create(defaultTheme);
+		}
+
+		// Look for a default.theme file at the root of the resource directory
+		if (!__defaultTheme)
+		{
+			__defaultTheme = Theme::create("res/default.theme");
+		}
+
+		// TODO: Use a built-in (compiled-in) default theme resource as the final fallback so that
+		// UI still works even when no theme files are present.
+	}
+	else
+	{
+		__defaultTheme->addRef();
+	}
+
+	return __defaultTheme;
 }
 
 Theme* Theme::create(const char* url)
@@ -452,6 +488,8 @@ Theme* Theme::create(const char* url)
     return theme;
 }
 
+strcmpi
+
 Theme::Style* Theme::getStyle(const char* name) const
 {
     GP_ASSERT(name);

+ 13 - 3
gameplay/src/Theme.h

@@ -236,17 +236,17 @@ public:
          */
         float right;
     };
-    
+
     /** 
      * Struct representing margin areas by the width or height of each side.
      */
     typedef SideRegions Margin;
-    
+
     /** 
      * Struct representing border areas by the width or height of each side.
      */
     typedef SideRegions Border;
-    
+
     /** 
      * Struct representing padding areas by the width or height of each side.
      */
@@ -308,6 +308,16 @@ public:
      */
     static Theme* create(const char* url);
 
+    /**
+     * Returns the default theme.
+	 *
+	 * Calling this method increases the reference count of the returned theme,
+	 * so you must ensure you call release() on it when you are finished with it.
+     *
+     * @return The default theme.
+     */
+    static Theme* getDefault();
+
     /**
      * Get a style by its ID.
      *

+ 5 - 0
samples/browser/game.config

@@ -16,3 +16,8 @@ gamepad
 {
     form = res/common/gamepad.form
 }
+
+ui
+{
+    defaultTheme = res/common/default.theme
+}

+ 6 - 3
samples/browser/res/common/default.theme

@@ -4,7 +4,7 @@ theme mainMenu
 
     imageList normalImages
     {
-        color = #4A8799ff
+        color = #ffffffff
 
         image unchecked
         {
@@ -49,7 +49,6 @@ theme mainMenu
         image textCaret
         {
             region = 5, 149, 11, 25
-            color = #C3D9BFff
         }
 
         image scrollBarTopCap
@@ -85,7 +84,7 @@ theme mainMenu
 
     imageList activeImages : normalImages
     {
-        color = #C3D9BFff
+        color = #ffffffff
 
         image unchecked
         {
@@ -390,4 +389,8 @@ theme mainMenu
             textColor = #C3D9BFff
         }
     }
+
+	style form : basic
+	{
+	}
 }

BIN
samples/browser/res/png/default-theme.png


+ 8 - 8
samples/browser/src/SamplesGame.cpp

@@ -27,14 +27,14 @@ void SamplesGame::initialize()
     getScriptController()->loadScript("res/common/camera.lua");
 
     // Construct a form for selecting which sample to run.
-    Theme* theme = Theme::create("res/common/default.theme");
-    Theme::Style* formStyle = theme->getStyle("basicContainer");
-    Theme::Style* buttonStyle = theme->getStyle("buttonStyle");
-    Theme::Style* titleStyle = theme->getStyle("title");
+    //Theme* theme = Theme::create("res/common/default.theme");
+    //Theme::Style* formStyle = theme->getStyle("basicContainer");
+    //Theme::Style* buttonStyle = theme->getStyle("buttonStyle");
+    //Theme::Style* titleStyle = theme->getStyle("title");
 
     // Note: this calls addRef() on formStyle's Theme, which we created above.
-    _sampleSelectForm = Form::create("sampleSelect", formStyle, Layout::LAYOUT_VERTICAL);
-    theme->release();   // So we can release it once we're done creating forms with it.
+    _sampleSelectForm = Form::create("sampleSelect", NULL, Layout::LAYOUT_VERTICAL);
+    //theme->release();   // So we can release it once we're done creating forms with it.
 
     _sampleSelectForm->setAutoHeight(true);
     _sampleSelectForm->setWidth(200.0f);
@@ -44,7 +44,7 @@ void SamplesGame::initialize()
     const size_t size = _samples->size();
     for (size_t i = 0; i < size; ++i)
     {
-        Label* categoryLabel = Label::create((*_categories)[i].c_str(), titleStyle);
+        Label* categoryLabel = Label::create((*_categories)[i].c_str());// titleStyle);
         categoryLabel->setAutoWidth(true);
         categoryLabel->setTextAlignment(Font::ALIGN_BOTTOM_LEFT);
         categoryLabel->setHeight(40);
@@ -58,7 +58,7 @@ void SamplesGame::initialize()
         for (size_t j = 0; j < listSize; ++j)
         {
             SampleRecord sampleRecord = list[j];
-            Button* sampleButton = Button::create(sampleRecord.title.c_str(), buttonStyle);
+            Button* sampleButton = Button::create(sampleRecord.title.c_str());// buttonStyle);
             sampleButton->setText(sampleRecord.title.c_str());
             sampleButton->setAutoWidth(true);
             sampleButton->setHeight(60);      // Tall enough to touch easily on a BB10 device.