Browse Source

Merge branch 'next' of https://github.com/blackberry/GamePlay into next

Conflicts:
	gameplay-tests/src/Test.cpp
	gameplay-tests/src/TestsGame.cpp
	samples/browser/src/FormsSample.cpp
	samples/browser/src/FormsSample.h
	samples/browser/src/InputSample.cpp
	samples/browser/src/Sample.h
seanpaultaylor 12 years ago
parent
commit
7730aa26eb
71 changed files with 1973 additions and 431 deletions
  1. 1 1
      gameplay/src/AbsoluteLayout.cpp
  2. 8 0
      gameplay/src/AnimationClip.cpp
  3. 3 1
      gameplay/src/AnimationController.cpp
  4. 37 7
      gameplay/src/Button.cpp
  5. 7 0
      gameplay/src/Button.h
  6. 20 0
      gameplay/src/CheckBox.cpp
  7. 7 0
      gameplay/src/CheckBox.h
  8. 554 51
      gameplay/src/Container.cpp
  9. 54 4
      gameplay/src/Container.h
  10. 70 30
      gameplay/src/Control.cpp
  11. 37 17
      gameplay/src/Control.h
  12. 119 119
      gameplay/src/Form.cpp
  13. 7 0
      gameplay/src/Form.h
  14. 7 7
      gameplay/src/Game.cpp
  15. 6 4
      gameplay/src/Game.h
  16. 3 3
      gameplay/src/Gamepad.cpp
  17. 1 1
      gameplay/src/Image.h
  18. 2 0
      gameplay/src/ImageControl.h
  19. 6 8
      gameplay/src/Joystick.cpp
  20. 9 7
      gameplay/src/Label.cpp
  21. 9 0
      gameplay/src/Platform.cpp
  22. 10 2
      gameplay/src/Platform.h
  23. 5 3
      gameplay/src/PlatformBlackBerry.cpp
  24. 15 14
      gameplay/src/PlatformMacOSX.mm
  25. 8 12
      gameplay/src/PlatformWindows.cpp
  26. 24 3
      gameplay/src/RadioButton.cpp
  27. 7 0
      gameplay/src/RadioButton.h
  28. 1 1
      gameplay/src/ScriptController.cpp
  29. 3 2
      gameplay/src/ScriptController.h
  30. 146 11
      gameplay/src/Slider.cpp
  31. 25 0
      gameplay/src/Slider.h
  32. 5 3
      gameplay/src/TextBox.cpp
  33. 1 1
      gameplay/src/VerticalLayout.cpp
  34. 45 0
      gameplay/src/lua/lua_Button.cpp
  35. 1 0
      gameplay/src/lua/lua_Button.h
  36. 45 0
      gameplay/src/lua/lua_CheckBox.cpp
  37. 1 0
      gameplay/src/lua/lua_CheckBox.h
  38. 45 0
      gameplay/src/lua/lua_Container.cpp
  39. 1 0
      gameplay/src/lua/lua_Container.h
  40. 45 0
      gameplay/src/lua/lua_Control.cpp
  41. 1 0
      gameplay/src/lua/lua_Control.h
  42. 45 0
      gameplay/src/lua/lua_Form.cpp
  43. 1 0
      gameplay/src/lua/lua_Form.h
  44. 33 1
      gameplay/src/lua/lua_Game.cpp
  45. 45 0
      gameplay/src/lua/lua_ImageControl.cpp
  46. 1 0
      gameplay/src/lua/lua_ImageControl.h
  47. 45 0
      gameplay/src/lua/lua_Joystick.cpp
  48. 1 0
      gameplay/src/lua/lua_Joystick.h
  49. 45 0
      gameplay/src/lua/lua_Label.cpp
  50. 1 0
      gameplay/src/lua/lua_Label.h
  51. 45 0
      gameplay/src/lua/lua_RadioButton.cpp
  52. 1 0
      gameplay/src/lua/lua_RadioButton.h
  53. 45 0
      gameplay/src/lua/lua_Slider.cpp
  54. 1 0
      gameplay/src/lua/lua_Slider.h
  55. 45 0
      gameplay/src/lua/lua_TextBox.cpp
  56. 1 0
      gameplay/src/lua/lua_TextBox.h
  57. 25 0
      samples/browser/res/common/default.theme
  58. 67 59
      samples/browser/res/common/forms/formBasicControls.form
  59. 32 32
      samples/browser/res/common/forms/formSelect.form
  60. 57 17
      samples/browser/src/FormsSample.cpp
  61. 4 2
      samples/browser/src/FormsSample.h
  62. 2 1
      samples/browser/src/InputSample.cpp
  63. 1 1
      samples/browser/src/LightSample.cpp
  64. 1 1
      samples/browser/src/Sample.cpp
  65. 1 1
      samples/browser/src/Sample.h
  66. 1 1
      samples/browser/src/SamplesGame.cpp
  67. 1 1
      samples/browser/src/SamplesGame.h
  68. 10 0
      samples/particles/res/editor.theme
  69. 1 0
      samples/particles/src/ParticlesGame.cpp
  70. 10 0
      samples/racer/res/common/menu.theme
  71. 4 2
      samples/racer/src/RacerGame.cpp

+ 1 - 1
gameplay/src/AbsoluteLayout.cpp

@@ -41,7 +41,7 @@ void AbsoluteLayout::update(const Container* container, const Vector2& offset)
     GP_ASSERT(container);
     GP_ASSERT(container);
 
 
     // An AbsoluteLayout does nothing to modify the layout of Controls.
     // An AbsoluteLayout does nothing to modify the layout of Controls.
-    std::vector<Control*> controls = container->getControls();
+    const std::vector<Control*>& controls = container->getControls();
     for (size_t i = 0, count = controls.size(); i < count; i++)
     for (size_t i = 0, count = controls.size(); i < count; i++)
     {
     {
         Control* control = controls[i];
         Control* control = controls[i];

+ 8 - 0
gameplay/src/AnimationClip.cpp

@@ -556,6 +556,8 @@ bool AnimationClip::update(float elapsedTime)
 
 
 void AnimationClip::onBegin()
 void AnimationClip::onBegin()
 {
 {
+    addRef();
+
     // Initialize animation to play.
     // Initialize animation to play.
     setClipStateBit(CLIP_IS_STARTED_BIT);
     setClipStateBit(CLIP_IS_STARTED_BIT);
     if (_speed >= 0)
     if (_speed >= 0)
@@ -584,10 +586,14 @@ void AnimationClip::onBegin()
             listener++;
             listener++;
         }
         }
     }
     }
+
+    release();
 }
 }
 
 
 void AnimationClip::onEnd()
 void AnimationClip::onEnd()
 {
 {
+    addRef();
+
     _blendWeight = 1.0f;
     _blendWeight = 1.0f;
     resetClipStateBit(CLIP_ALL_BITS);
     resetClipStateBit(CLIP_ALL_BITS);
 
 
@@ -602,6 +608,8 @@ void AnimationClip::onEnd()
             listener++;
             listener++;
         }
         }
     }
     }
+
+    release();
 }
 }
 
 
 bool AnimationClip::isClipStateBitSet(unsigned char bit) const
 bool AnimationClip::isClipStateBitSet(unsigned char bit) const

+ 3 - 1
gameplay/src/AnimationController.cpp

@@ -106,6 +106,7 @@ void AnimationController::update(float elapsedTime)
     {
     {
         AnimationClip* clip = (*clipIter);
         AnimationClip* clip = (*clipIter);
         GP_ASSERT(clip);
         GP_ASSERT(clip);
+        clip->addRef();
         if (clip->isClipStateBitSet(AnimationClip::CLIP_IS_RESTARTED_BIT))
         if (clip->isClipStateBitSet(AnimationClip::CLIP_IS_RESTARTED_BIT))
         {   // If the CLIP_IS_RESTARTED_BIT is set, we should end the clip and 
         {   // If the CLIP_IS_RESTARTED_BIT is set, we should end the clip and 
             // move it from where it is in the running clips list to the back.
             // move it from where it is in the running clips list to the back.
@@ -116,13 +117,14 @@ void AnimationController::update(float elapsedTime)
         }
         }
         else if (clip->update(elapsedTime))
         else if (clip->update(elapsedTime))
         {
         {
-            SAFE_RELEASE(clip);
+            clip->release();
             clipIter = _runningClips.erase(clipIter);
             clipIter = _runningClips.erase(clipIter);
         }
         }
         else
         else
         {
         {
             clipIter++;
             clipIter++;
         }
         }
+        clip->release();
     }
     }
 
 
     Transform::resumeTransformChanged();
     Transform::resumeTransformChanged();

+ 37 - 7
gameplay/src/Button.cpp

@@ -50,11 +50,8 @@ bool Button::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contac
                 y > _clipBounds.y && y <= _clipBounds.y + _clipBounds.height)
                 y > _clipBounds.y && y <= _clipBounds.y + _clipBounds.height)
             {
             {
                 _contactIndex = (int) contactIndex;
                 _contactIndex = (int) contactIndex;
-
                 setState(Control::ACTIVE);
                 setState(Control::ACTIVE);
-
-                notifyListeners(Listener::PRESS);
-
+                notifyListeners(Control::Listener::PRESS);
                 return _consumeInputEvents;
                 return _consumeInputEvents;
             }
             }
             else
             else
@@ -68,14 +65,13 @@ bool Button::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contac
         if (_contactIndex == (int) contactIndex)
         if (_contactIndex == (int) contactIndex)
         {
         {
             _contactIndex = INVALID_CONTACT_INDEX;
             _contactIndex = INVALID_CONTACT_INDEX;
-            notifyListeners(Listener::RELEASE);
+            notifyListeners(Control::Listener::RELEASE);
             if (!_parent->isScrolling() &&
             if (!_parent->isScrolling() &&
                 x > _clipBounds.x && x <= _clipBounds.x + _clipBounds.width &&
                 x > _clipBounds.x && x <= _clipBounds.x + _clipBounds.width &&
                 y > _clipBounds.y && y <= _clipBounds.y + _clipBounds.height)
                 y > _clipBounds.y && y <= _clipBounds.y + _clipBounds.height)
             {
             {
                 setState(Control::FOCUS);
                 setState(Control::FOCUS);
-
-                notifyListeners(Listener::CLICK);
+                notifyListeners(Control::Listener::CLICK);
             }
             }
             else
             else
             {
             {
@@ -93,6 +89,40 @@ bool Button::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contac
     return false;
     return false;
 }
 }
 
 
+bool Button::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex)
+{
+    switch (evt)
+    {
+    case Gamepad::BUTTON_EVENT:
+        if (_state == Control::FOCUS)
+        {
+            if (gamepad->isButtonDown(Gamepad::BUTTON_A) ||
+                gamepad->isButtonDown(Gamepad::BUTTON_X))
+            {
+                notifyListeners(Control::Listener::PRESS);
+                setState(Control::ACTIVE);
+                return _consumeInputEvents;
+            }
+        }
+        else if (_state == Control::ACTIVE)
+        {
+            if (!gamepad->isButtonDown(Gamepad::BUTTON_A) &&
+                !gamepad->isButtonDown(Gamepad::BUTTON_X))
+            {
+                notifyListeners(Control::Listener::RELEASE);
+                notifyListeners(Control::Listener::CLICK);
+                setState(Control::FOCUS);
+                return _consumeInputEvents;
+            }
+        }
+        break;
+    default:
+        break;
+    }
+
+    return false;
+}
+
 const char* Button::getType() const
 const char* Button::getType() const
 {
 {
     return "button";
     return "button";

+ 7 - 0
gameplay/src/Button.h

@@ -85,6 +85,13 @@ protected:
      */
      */
     bool touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex);
     bool touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex);
 
 
+    /**
+     * Gamepad callback on gamepad events.
+     *
+     * @see Control::gamepadEvent
+     */
+    virtual bool gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex);
+
     /**
     /**
      * @see Control::getType
      * @see Control::getType
      */
      */

+ 20 - 0
gameplay/src/CheckBox.cpp

@@ -94,6 +94,26 @@ bool CheckBox::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int cont
     return Button::touchEvent(evt, x, y, contactIndex);
     return Button::touchEvent(evt, x, y, contactIndex);
 }
 }
 
 
+bool CheckBox::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex)
+{
+    switch (evt)
+    {
+    case Gamepad::BUTTON_EVENT:
+        if (_state == Control::ACTIVE)
+        {
+            if (!gamepad->isButtonDown(Gamepad::BUTTON_A) &&
+                !gamepad->isButtonDown(Gamepad::BUTTON_X))
+            {
+                _checked = !_checked;
+                notifyListeners(Control::Listener::VALUE_CHANGED);   
+            }
+        }
+        break;
+    }
+
+    return Button::gamepadEvent(evt, gamepad, analogIndex);
+}
+
 void CheckBox::update(const Control* container, const Vector2& offset)
 void CheckBox::update(const Control* container, const Vector2& offset)
 {
 {
     Label::update(container, offset);
     Label::update(container, offset);

+ 7 - 0
gameplay/src/CheckBox.h

@@ -131,6 +131,13 @@ protected:
      */
      */
     bool touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex);
     bool touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex);
 
 
+    /**
+     * Gamepad callback on gamepad events.
+     *
+     * @see Control::gamepadEvent
+     */
+    bool gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex);
+
     /**
     /**
      * Called when a control's properties change.  Updates this control's internal rendering
      * Called when a control's properties change.  Updates this control's internal rendering
      * properties, such as its text viewport.
      * properties, such as its text viewport.

+ 554 - 51
gameplay/src/Container.cpp

@@ -10,8 +10,8 @@
 #include "RadioButton.h"
 #include "RadioButton.h"
 #include "Slider.h"
 #include "Slider.h"
 #include "TextBox.h"
 #include "TextBox.h"
-#include "ImageControl.h"
 #include "Joystick.h"
 #include "Joystick.h"
+#include "ImageControl.h"
 #include "Game.h"
 #include "Game.h"
 
 
 namespace gameplay
 namespace gameplay
@@ -23,6 +23,12 @@ static const long SCROLL_INERTIA_DELAY = 100L;
 static const float SCROLL_FRICTION_FACTOR = 5.0f;
 static const float SCROLL_FRICTION_FACTOR = 5.0f;
 // Distance that must be scrolled before isScrolling() will return true, used e.g. to cancel button-click events.
 // Distance that must be scrolled before isScrolling() will return true, used e.g. to cancel button-click events.
 static const float SCROLL_THRESHOLD = 10.0f;
 static const float SCROLL_THRESHOLD = 10.0f;
+// Distance a joystick must be pushed in order to trigger focus-change and/or scrolling.
+static const float JOYSTICK_THRESHOLD = 0.75f;
+// Scroll speed when using a DPad -- max scroll speed when using a joystick.
+static const float GAMEPAD_SCROLL_SPEED = 500.0f;
+// If the DPad or joystick is held down, this is the initial delay in milliseconds between focus change events.
+static const float FOCUS_CHANGE_REPEAT_DELAY = 300.0f;
 
 
 /**
 /**
  * Sort function for use with _controls.sort(), based on Z-Order.
  * Sort function for use with _controls.sort(), based on Z-Order.
@@ -43,7 +49,11 @@ Container::Container()
       _scrollingVelocity(Vector2::zero()), _scrollingFriction(1.0f),
       _scrollingVelocity(Vector2::zero()), _scrollingFriction(1.0f),
       _scrollingRight(false), _scrollingDown(false),
       _scrollingRight(false), _scrollingDown(false),
       _scrollingMouseVertically(false), _scrollingMouseHorizontally(false),
       _scrollingMouseVertically(false), _scrollingMouseHorizontally(false),
-      _scrollBarOpacityClip(NULL), _zIndexDefault(0), _focusIndexDefault(0), _focusIndexMax(0), _totalWidth(0), _totalHeight(0),
+      _scrollBarOpacityClip(NULL), _zIndexDefault(0), _focusIndexDefault(0), _focusIndexMax(0),
+      _focusPressed(0), _selectButtonDown(false),
+      _lastFrameTime(0), _focusChangeRepeat(false),
+      _focusChangeStartTime(0), _focusChangeRepeatDelay(FOCUS_CHANGE_REPEAT_DELAY), _focusChangeCount(0),
+      _totalWidth(0), _totalHeight(0),
       _contactIndices(0), _initializedWithScroll(false)
       _contactIndices(0), _initializedWithScroll(false)
 {
 {
 }
 }
@@ -182,20 +192,6 @@ void Container::addControls(Theme* theme, Properties* properties)
         {
         {
             addControl(control);
             addControl(control);
             control->release();
             control->release();
-
-            if (control->getZIndex() == -1)
-            {
-                control->setZIndex(_zIndexDefault++);
-            }
-
-            if (control->getFocusIndex() == -1)
-            {
-                control->setFocusIndex(_focusIndexDefault++);
-            }
-
-            int focusIndex = control->getFocusIndex();
-            if (focusIndex > _focusIndexMax)
-                _focusIndexMax = focusIndex;
         }
         }
 
 
         // Get the next control.
         // Get the next control.
@@ -203,7 +199,7 @@ void Container::addControls(Theme* theme, Properties* properties)
     }
     }
 
 
     // Sort controls by Z-Order.
     // Sort controls by Z-Order.
-    std::sort(_controls.begin(), _controls.end(), &sortControlsByZOrder);
+    sortControls();
 }
 }
 
 
 Layout* Container::getLayout()
 Layout* Container::getLayout()
@@ -220,11 +216,26 @@ unsigned int Container::addControl(Control* control)
         control->_parent->removeControl(control);
         control->_parent->removeControl(control);
     }
     }
 
 
+    if (control->getZIndex() == -1)
+    {
+        control->setZIndex(_zIndexDefault++);
+    }
+
+    if (control->getFocusIndex() == -1)
+    {
+        control->setFocusIndex(_focusIndexDefault++);
+    }
+
+    int focusIndex = control->getFocusIndex();
+    if (focusIndex > _focusIndexMax)
+        _focusIndexMax = focusIndex;
+
     if (control->_parent != this)
     if (control->_parent != this)
     {
     {
         _controls.push_back(control);
         _controls.push_back(control);
         control->addRef();
         control->addRef();
         control->_parent = this;
         control->_parent = this;
+        sortControls();
         return (unsigned int)(_controls.size() - 1);
         return (unsigned int)(_controls.size() - 1);
     }
     }
     else
     else
@@ -438,12 +449,6 @@ void Container::update(const Control* container, const Vector2& offset)
         _viewportClipBounds.width -= _scrollBarVertical->getRegion().width;
         _viewportClipBounds.width -= _scrollBarVertical->getRegion().width;
     }
     }
 
 
-    // Sort controls by Z-Order.
-    if (_layout->getType() == Layout::LAYOUT_ABSOLUTE)
-    {
-        std::sort(_controls.begin(), _controls.end(), &sortControlsByZOrder);
-    }
-
     GP_ASSERT(_layout);
     GP_ASSERT(_layout);
     if (_scroll != SCROLL_NONE)
     if (_scroll != SCROLL_NONE)
     {
     {
@@ -623,40 +628,524 @@ bool Container::keyEvent(Keyboard::KeyEvent evt, int key)
             continue;
             continue;
         }
         }
 
 
-        if (control->getState() == Control::FOCUS)
+        if (control->getState() == Control::FOCUS && control->keyEvent(evt, key))
         {
         {
-            if (control->keyEvent(evt, key))
+            release();
+            return true;
+        }
+    }
+
+    // If we made it this far, no control handled the event.
+    if (evt == Keyboard::KEY_CHAR && key == Keyboard::KEY_TAB)
+    {
+        moveFocus(NEXT);
+        return _consumeInputEvents;
+    }
+
+    release();
+    return false;
+}
+
+void Container::guaranteeFocus(Control* inFocus)
+{
+    std::vector<Control*>::const_iterator it;
+    for (it = _controls.begin(); it < _controls.end(); it++)
+    {
+        Control* control = *it;
+        GP_ASSERT(control);
+        if (control == inFocus)
+            continue;
+
+        if (control->isContainer())
+        {
+            ((Container*)control)->guaranteeFocus(inFocus);
+        }
+        else if (control->getState() == Control::FOCUS)
+        {
+            control->setState(NORMAL);
+            return;
+        }
+    }
+}
+
+bool Container::moveFocus(Direction direction, Control* outsideControl)
+{
+    _direction = direction;
+
+    Control* start = outsideControl;
+    if (!start)
+    {
+        std::vector<Control*>::const_iterator it;
+        for (it = _controls.begin(); it < _controls.end(); it++)
+        {
+            Control* control = *it;
+            GP_ASSERT(control);
+            if (control->getState() == Control::FOCUS)
             {
             {
-                release();
+                start = control;
+                break;
+            }
+        }
+    }
+
+    int focusIndex = 0;
+    Control* next = NULL;
+    if (start)
+    {
+        const Rectangle& startBounds = start->getAbsoluteBounds();
+        Vector2 vStart, vNext;
+        float distance = FLT_MAX;
+        start->setState(Control::NORMAL);
+
+        switch(direction)
+        {
+        case UP:
+            vStart.set(startBounds.x + startBounds.width * 0.5f,
+                        startBounds.y);
+            break;
+        case DOWN:
+            vStart.set(startBounds.x + startBounds.width * 0.5f,
+                        startBounds.bottom());
+            break;
+        case LEFT:
+            vStart.set(startBounds.x,
+                        startBounds.y + startBounds.height * 0.5f);
+            break;
+        case RIGHT:
+            vStart.set(startBounds.right(),
+                        startBounds.y + startBounds.height * 0.5f);
+            break;
+        case NEXT:
+            break;
+        }
+
+        if (direction != NEXT)
+        {
+            std::vector<Control*>::const_iterator itt;
+            for (itt = _controls.begin(); itt < _controls.end(); itt++)
+            {
+                Control* nextControl = *itt;
+
+                if (nextControl == start || nextControl->getFocusIndex() < 0 ||
+                    !nextControl->isEnabled() || !nextControl->isVisible())
+                {
+                    // Control is not focusable.
+                    continue;
+                }
+
+                const Rectangle& nextBounds = nextControl->getAbsoluteBounds();
+                switch(direction)
+                {
+                case UP:
+                    vNext.set(nextBounds.x + nextBounds.width * 0.5f,
+                              nextBounds.bottom());
+                    if (vNext.y > vStart.y) continue;
+                    break;
+                case DOWN:
+                    vNext.set(nextBounds.x + nextBounds.width * 0.5f,
+                              nextBounds.y);
+                    if (vNext.y < vStart.y) continue;
+                    break;
+                case LEFT:
+                    vNext.set(nextBounds.right(),
+                              nextBounds.y + nextBounds.height * 0.5f);
+                    if (vNext.x > vStart.x) continue;
+                    break;
+                case RIGHT:
+                    vNext.set(nextBounds.x,
+                              nextBounds.y + nextBounds.height * 0.5f);
+                    if (vNext.x < vStart.x) continue;
+                    break;
+                }
+
+                float nextDistance = vStart.distance(vNext);
+                if (abs(nextDistance) < distance)
+                {
+                    distance = nextDistance;
+                    next = nextControl;
+                }
+            }
+        }
+
+        if (!next)
+        {
+            // Check for controls in the given direction in our parent container.
+            if (!outsideControl && _parent && _parent->moveFocus(direction, start))
+            {
+                setState(NORMAL);
+                _focusChangeRepeat = false;
                 return true;
                 return true;
             }
             }
-            else if (evt == Keyboard::KEY_CHAR && key == Keyboard::KEY_TAB)
+            
+            // No control is in the given direction.  Move to the next control in the focus order.
+            int focusDelta;
+            switch(direction)
+            {
+            case UP:
+            case LEFT:
+                focusDelta = -1;
+                break;
+            case DOWN:
+            case RIGHT:
+            case NEXT:
+                focusDelta = 1;
+                break;
+            }
+
+            // Find the index to search for.
+            if (outsideControl)
+            {
+                focusIndex = outsideControl->_parent->getFocusIndex() + focusDelta;
+            }
+            else
+            {
+                focusIndex = start->getFocusIndex() + focusDelta;
+            }
+
+            if (focusIndex > _focusIndexMax)
+            {
+                focusIndex = 0;
+            }
+            else if (focusIndex < 0)
+            {
+                focusIndex = _focusIndexMax;
+            }
+        }
+    }
+
+    if (!next)
+    {
+        std::vector<Control*>::const_iterator itt;
+        for (itt = _controls.begin(); itt < _controls.end(); itt++)
+        {
+            Control* nextControl = *itt;
+            if (nextControl->getFocusIndex() == focusIndex)
+            {
+                next = nextControl;
+            }
+        }
+    }
+
+    // If we haven't found next by now, then there are no focusable controls in this container.
+    if (next)
+    {
+        next->setState(Control::FOCUS);
+        _dirty = true;
+
+        if (next->isContainer())
+        {
+            if (((Container*)next)->moveFocus(direction, start))
             {
             {
-                // Shift focus to next control.
-                int focusIndex = control->getFocusIndex() + 1; // Index to search for.
-                if (focusIndex > _focusIndexMax)
+                _focusChangeRepeat = false;
+                return true;
+            }
+        }
+
+        // If the next control is not fully visible, scroll the container so that it is.
+        const Rectangle& bounds = next->getBounds();
+        if (bounds.x < _scrollPosition.x)
+        {
+            // Control is to the left of the scrolled viewport.
+            _scrollPosition.x = -bounds.x;
+        }
+        else if (bounds.x + bounds.width > _scrollPosition.x + _viewportBounds.width)
+        {
+            // Control is off to the right.
+            _scrollPosition.x = -(bounds.x + bounds.width - _viewportBounds.width);
+        }
+
+        if (bounds.y < _viewportBounds.y - _scrollPosition.y)
+        {
+            // Control is above the viewport.
+            _scrollPosition.y = -bounds.y;
+        }
+        else if (bounds.y + bounds.height > _viewportBounds.height - _scrollPosition.y)
+        {
+            // Control is below the viewport.
+            _scrollPosition.y = -(bounds.y + bounds.height - _viewportBounds.height);
+        }
+
+        if (outsideControl && outsideControl->_parent)
+        {
+            _focusPressed = outsideControl->_parent->_focusPressed;
+            _focusChangeCount = outsideControl->_parent->_focusChangeCount;
+            _focusChangeRepeatDelay = outsideControl->_parent->_focusChangeRepeatDelay;
+            outsideControl->_parent->guaranteeFocus(next);
+        }
+
+        _focusChangeStartTime = Game::getAbsoluteTime();
+        _focusChangeRepeat = true;
+        addRef();
+        Game::getInstance()->schedule(_focusChangeRepeatDelay, this);
+
+        return true;
+    }
+
+    return false;
+}
+
+void Container::timeEvent(long timeDiff, void* cookie)
+{
+    double time = Game::getAbsoluteTime();
+    if (_focusChangeRepeat && _state == FOCUS && _focusPressed &&
+        abs(time - timeDiff - _focusChangeRepeatDelay - _focusChangeStartTime) < 50)
+    {
+        ++_focusChangeCount;
+        if (_focusChangeCount == 5)
+        {
+            _focusChangeRepeatDelay *= 0.5;
+        }
+        moveFocus(_direction);
+    }
+    else
+    {
+        _focusChangeCount = 0;
+        _focusChangeRepeatDelay = FOCUS_CHANGE_REPEAT_DELAY;
+    }
+
+    release();
+}
+
+void Container::startScrolling(float x, float y, bool resetTime)
+{
+    _scrollingVelocity.set(-x, y);
+    _scrolling = true;
+    _scrollBarOpacity = 1.0f;
+    _dirty = true;
+
+    if (_scrollBarOpacityClip && _scrollBarOpacityClip->isPlaying())
+    {
+        _scrollBarOpacityClip->stop();
+        _scrollBarOpacityClip = NULL;
+    }
+
+    if (resetTime)
+    {
+        _lastFrameTime = Game::getGameTime();
+    }
+}
+
+void Container::stopScrolling()
+{
+    _scrollingVelocity.set(0, 0);
+    _scrolling = false;
+    _dirty = true;
+}
+
+bool Container::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex)
+{
+    addRef();
+
+    bool eventConsumed = false;
+
+    // Pass the event to any control that is active or in focus.
+    std::vector<Control*>::const_iterator it;
+    for (it = _controls.begin(); it < _controls.end(); it++)
+    {
+        Control* control = *it;
+        GP_ASSERT(control);
+        if (control->getState() == Control::FOCUS || control->getState() == Control::ACTIVE)
+        {
+            eventConsumed |= control->gamepadEvent(evt, gamepad, analogIndex);
+            break;
+        }
+    }
+
+    // First check if a selection button is down.
+    if (!_selectButtonDown)
+    {
+        if (gamepad->isButtonDown(Gamepad::BUTTON_A) ||
+            gamepad->isButtonDown(Gamepad::BUTTON_X))
+        {
+            _selectButtonDown = true;
+            _focusChangeRepeat = false;
+            eventConsumed |= _consumeInputEvents;
+        }
+    }
+    else
+    {
+        if (!gamepad->isButtonDown(Gamepad::BUTTON_A) &&
+            !gamepad->isButtonDown(Gamepad::BUTTON_X))
+        {
+            _selectButtonDown = false;
+        }
+    }
+
+    Vector2 joystick;
+    gamepad->getJoystickValues(analogIndex, &joystick);
+
+    // Don't allow focus changes or scrolling while a selection button is down.
+    if (!_selectButtonDown && !eventConsumed)
+    {
+        switch (evt)
+        {
+            case Gamepad::BUTTON_EVENT:
+            {
+                // Shift focus forward or backward when the DPad is used.
+                if (!(_focusPressed & DOWN) &&
+                    gamepad->isButtonDown(Gamepad::BUTTON_DOWN))
                 {
                 {
-                    focusIndex = 0;
+                    _focusPressed |= DOWN;
+                    eventConsumed |= _consumeInputEvents;
+                    if (moveFocus(DOWN))
+                        break;
+                    else
+                        startScrolling(0, -GAMEPAD_SCROLL_SPEED);
+                }
+                    
+                if (!(_focusPressed & RIGHT) &&
+                    gamepad->isButtonDown(Gamepad::BUTTON_RIGHT))
+                {
+                    _focusPressed |= RIGHT;
+                    eventConsumed |= _consumeInputEvents;
+                    if (moveFocus(RIGHT))
+                        break;
+                    else
+                        startScrolling(GAMEPAD_SCROLL_SPEED, 0);
                 }
                 }
-                control->setState(Control::NORMAL);
 
 
-                std::vector<Control*>::const_iterator itt;
-                for (itt = _controls.begin(); itt < _controls.end(); itt++)
+                if (!(_focusPressed & UP) &&
+                    gamepad->isButtonDown(Gamepad::BUTTON_UP))
+                {
+                    _focusPressed |= UP;
+                    eventConsumed |= _consumeInputEvents;
+                    if (moveFocus(UP))
+                        break;
+                    else
+                        startScrolling(0, GAMEPAD_SCROLL_SPEED);
+                }
+
+                if (!(_focusPressed & LEFT) &&
+                    gamepad->isButtonDown(Gamepad::BUTTON_LEFT))
+                {
+                    _focusPressed |= LEFT;
+                    eventConsumed |= _consumeInputEvents;
+                    if (moveFocus(LEFT))
+                        break;
+                    else
+                        startScrolling(-GAMEPAD_SCROLL_SPEED, 0);
+                }
+                break;
+            }
+            case Gamepad::JOYSTICK_EVENT:
+            {
+                switch (analogIndex)
                 {
                 {
-                    Control* nextControl = *itt;
-                    if (nextControl->getFocusIndex() == focusIndex)
+                case 0:
+                    // The left analog stick can be used in the same way as the DPad.
+                    eventConsumed |= _consumeInputEvents;
+                    if (!(_focusPressed & RIGHT) &&
+                        joystick.x > JOYSTICK_THRESHOLD)
+                    {
+                        _focusPressed |= RIGHT;
+                        if (moveFocus(RIGHT))
+                            break;
+                        else
+                            startScrolling(GAMEPAD_SCROLL_SPEED * joystick.x, 0);
+                    }
+
+                    if (!(_focusPressed & DOWN) &&
+                        joystick.y < -JOYSTICK_THRESHOLD)
+                    {
+                        _focusPressed |= DOWN;
+                        if (moveFocus(DOWN))
+                            break;
+                        else
+                            startScrolling(0, GAMEPAD_SCROLL_SPEED * joystick.y);
+                    }
+
+                    if (!(_focusPressed & LEFT) &&
+                        joystick.x < -JOYSTICK_THRESHOLD)
+                    {
+                        _focusPressed |= LEFT;
+                        if (moveFocus(LEFT))
+                            break;
+                        else
+                            startScrolling(GAMEPAD_SCROLL_SPEED * joystick.x, 0);
+                    }
+                        
+                    if (!(_focusPressed & UP) &&
+                        joystick.y > JOYSTICK_THRESHOLD)
+                    {
+                        _focusPressed |= UP;
+                        if (moveFocus(UP))
+                            break;
+                        else
+                            startScrolling(0, GAMEPAD_SCROLL_SPEED * joystick.y);
+                    }
+                    break;
+
+                case 1:
+                    // The right analog stick can be used to scroll.
+                    if (_scroll != SCROLL_NONE)
                     {
                     {
-                        nextControl->setState(Control::FOCUS);
+                        if (_scrolling)
+                        {
+                            if (joystick.isZero())
+                            {
+                                stopScrolling();
+                            }
+                            else
+                            {
+                                startScrolling(GAMEPAD_SCROLL_SPEED * joystick.x, GAMEPAD_SCROLL_SPEED * joystick.y, false);
+                            }
+                        }
+                        else
+                        {
+                            startScrolling(GAMEPAD_SCROLL_SPEED * joystick.x, GAMEPAD_SCROLL_SPEED * joystick.y);
+                        }
                         release();
                         release();
                         return _consumeInputEvents;
                         return _consumeInputEvents;
                     }
                     }
+                    break;
                 }
                 }
             }
             }
         }
         }
     }
     }
 
 
+    if ((evt == Gamepad::BUTTON_EVENT || evt == Gamepad::JOYSTICK_EVENT) &&
+        analogIndex == 0)
+    {
+        if ((_focusPressed & DOWN) &&
+            !gamepad->isButtonDown(Gamepad::BUTTON_DOWN) &&
+            joystick.y > -JOYSTICK_THRESHOLD)
+        {
+            _focusPressed &= ~DOWN;
+            eventConsumed |= _consumeInputEvents;
+        }
+
+        if ((_focusPressed & RIGHT) &&
+            !gamepad->isButtonDown(Gamepad::BUTTON_RIGHT) &&
+            joystick.x < JOYSTICK_THRESHOLD)
+        {
+            _focusPressed &= ~RIGHT;
+            eventConsumed |= _consumeInputEvents;
+        }
+    
+        if ((_focusPressed & UP) &&
+            !gamepad->isButtonDown(Gamepad::BUTTON_UP) &&
+            joystick.y < JOYSTICK_THRESHOLD)
+        {
+            _focusPressed &= ~UP;
+            eventConsumed |= _consumeInputEvents;
+        }
+
+        if ((_focusPressed & LEFT) &&
+            !gamepad->isButtonDown(Gamepad::BUTTON_LEFT) &&
+            joystick.x > -JOYSTICK_THRESHOLD)
+        {
+            _focusPressed &= ~LEFT;
+            eventConsumed |= _consumeInputEvents;
+        }
+    }
+
+    if (!_focusPressed && _scrolling)
+    {
+        stopScrolling();
+    }
+
     release();
     release();
-    return false;
+    return eventConsumed;
 }
 }
 
 
 bool Container::isContainer() const
 bool Container::isContainer() const
@@ -700,11 +1189,14 @@ void Container::updateScroll()
         _layout->update(this, _scrollPosition);
         _layout->update(this, _scrollPosition);
     }
     }
 
 
-    // Update Time.
-    static double lastFrameTime = Game::getGameTime();
+    // Update time.
+    if (!_lastFrameTime)
+    {
+        _lastFrameTime = Game::getGameTime();
+    }
     double frameTime = Game::getGameTime();
     double frameTime = Game::getGameTime();
-    float elapsedTime = (float)(frameTime - lastFrameTime);
-    lastFrameTime = frameTime;
+    float elapsedTime = (float)(frameTime - _lastFrameTime);
+    _lastFrameTime = frameTime;
 
 
     const Theme::Border& containerBorder = getBorder(_state);
     const Theme::Border& containerBorder = getBorder(_state);
     const Theme::Padding& containerPadding = getPadding();
     const Theme::Padding& containerPadding = getPadding();
@@ -737,7 +1229,7 @@ void Container::updateScroll()
     float clipHeight = _bounds.height - containerBorder.top - containerBorder.bottom - containerPadding.top - containerPadding.bottom - hHeight;
     float clipHeight = _bounds.height - containerBorder.top - containerBorder.bottom - containerPadding.top - containerPadding.bottom - hHeight;
 
 
     // Apply and dampen inertia.
     // Apply and dampen inertia.
-    if (!_scrolling && !_scrollingVelocity.isZero())
+    if (!_scrollingVelocity.isZero())
     {
     {
         // Calculate the time passed since last update.
         // Calculate the time passed since last update.
         float elapsedSecs = (float)elapsedTime * 0.001f;
         float elapsedSecs = (float)elapsedTime * 0.001f;
@@ -745,14 +1237,17 @@ void Container::updateScroll()
         _scrollPosition.x += _scrollingVelocity.x * elapsedSecs;
         _scrollPosition.x += _scrollingVelocity.x * elapsedSecs;
         _scrollPosition.y += _scrollingVelocity.y * elapsedSecs;
         _scrollPosition.y += _scrollingVelocity.y * elapsedSecs;
 
 
-        float dampening = 1.0f - _scrollingFriction * SCROLL_FRICTION_FACTOR * elapsedSecs;
-        _scrollingVelocity.x *= dampening;
-        _scrollingVelocity.y *= dampening;
-
-        if (fabs(_scrollingVelocity.x) < 100.0f)
-            _scrollingVelocity.x = 0.0f;
-        if (fabs(_scrollingVelocity.y) < 100.0f)
-            _scrollingVelocity.y = 0.0f;
+        if (!_scrolling)
+        {
+            float dampening = 1.0f - _scrollingFriction * SCROLL_FRICTION_FACTOR * elapsedSecs;
+            _scrollingVelocity.x *= dampening;
+            _scrollingVelocity.y *= dampening;
+
+            if (fabs(_scrollingVelocity.x) < 100.0f)
+                _scrollingVelocity.x = 0.0f;
+            if (fabs(_scrollingVelocity.y) < 100.0f)
+                _scrollingVelocity.y = 0.0f;
+        }
     }
     }
 
 
     // Stop scrolling when the far edge is reached.
     // Stop scrolling when the far edge is reached.
@@ -809,6 +1304,14 @@ void Container::updateScroll()
     _layout->update(this, _scrollPosition);
     _layout->update(this, _scrollPosition);
 }
 }
 
 
+void Container::sortControls()
+{
+    if (_layout->getType() == Layout::LAYOUT_ABSOLUTE)
+    {
+        std::sort(_controls.begin(), _controls.end(), &sortControlsByZOrder);
+    }
+}
+
 bool Container::touchEventScroll(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
 bool Container::touchEventScroll(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
 {
 {
     switch(evt)
     switch(evt)

+ 54 - 4
gameplay/src/Container.h

@@ -3,6 +3,7 @@
 
 
 #include "Control.h"
 #include "Control.h"
 #include "Layout.h"
 #include "Layout.h"
+#include "TimeListener.h"
 
 
 namespace gameplay
 namespace gameplay
 {
 {
@@ -44,7 +45,7 @@ namespace gameplay
     }
     }
  @endverbatim
  @endverbatim
  */
  */
-class Container : public Control
+class Container : public Control, TimeListener
 {
 {
 
 
 public:
 public:
@@ -212,6 +213,13 @@ public:
      */
      */
     virtual void setAnimationPropertyValue(int propertyId, AnimationValue* value, float blendWeight = 1.0f);
     virtual void setAnimationPropertyValue(int propertyId, AnimationValue* value, float blendWeight = 1.0f);
 
 
+    /**
+     * @see TimeListener::timeEvent
+     *
+     * @script{ignore}
+     */
+    void timeEvent(long timeDiff, void* cookie);
+
 protected:
 protected:
 
 
     /**
     /**
@@ -291,10 +299,17 @@ protected:
      *
      *
      * @return True if the mouse event is consumed or false if it is not consumed.
      * @return True if the mouse event is consumed or false if it is not consumed.
      *
      *
-     * @see Mouse::MouseEvent
+     * @see Mouse::mouseEvent
      */
      */
     virtual bool mouseEvent(Mouse::MouseEvent evt, int x, int y, int wheelDelta);
     virtual bool mouseEvent(Mouse::MouseEvent evt, int x, int y, int wheelDelta);
 
 
+    /**
+     * Gamepad callback on gamepad events.
+     *
+     * @see Control::gamepadEvent
+     */
+    virtual bool gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex);
+
     /**
     /**
      * Gets a Layout::Type enum from a matching string.
      * Gets a Layout::Type enum from a matching string.
      *
      *
@@ -334,6 +349,13 @@ protected:
      */
      */
     void updateScroll();
     void updateScroll();
 
 
+    /**
+     * Sorts controls by Z-Order (for absolute layouts only).
+     * This method is used by controls to notify their parent container when
+     * their Z-Index changes.
+     */
+    void sortControls();
+
     /**
     /**
      * Applies touch events to scroll state.
      * Applies touch events to scroll state.
      *
      *
@@ -513,16 +535,44 @@ private:
      */
      */
     Container(const Container& copy);
     Container(const Container& copy);
 
 
+    enum Direction
+    {
+        UP = 0x01,
+        DOWN = 0x02,
+        LEFT = 0x04,
+        RIGHT = 0x08,
+        NEXT = 0x10
+    };
+
+    // Returns true on success; false if there are no controls to focus on,
+    // in which case scrolling can be initiated.
+    bool moveFocus(Direction direction, Control* outsideControl = NULL);
+
+    void guaranteeFocus(Control* inFocus);
+
+    // Starts scrolling at the given horizontal and vertical speeds.
+    void startScrolling(float x, float y, bool resetTime = true);
+
+    void stopScrolling();
+
     AnimationClip* _scrollBarOpacityClip;
     AnimationClip* _scrollBarOpacityClip;
     int _zIndexDefault;
     int _zIndexDefault;
     int _focusIndexDefault;
     int _focusIndexDefault;
     int _focusIndexMax;
     int _focusIndexMax;
+    unsigned int _focusPressed;
+    bool _selectButtonDown;
+    double _lastFrameTime;
+
+    // Timing information for repeating focus changes.
+    bool _focusChangeRepeat;
+    double _focusChangeStartTime;
+    double _focusChangeRepeatDelay;
+    unsigned int _focusChangeCount;
+    Direction _direction;
 
 
     float _totalWidth;
     float _totalWidth;
     float _totalHeight;
     float _totalHeight;
-
     int _contactIndices;
     int _contactIndices;
-
     bool _initializedWithScroll;
     bool _initializedWithScroll;
 };
 };
 
 

+ 70 - 30
gameplay/src/Control.cpp

@@ -8,7 +8,7 @@ namespace gameplay
 Control::Control()
 Control::Control()
     : _id(""), _state(Control::NORMAL), _bounds(Rectangle::empty()), _clipBounds(Rectangle::empty()), _viewportClipBounds(Rectangle::empty()),
     : _id(""), _state(Control::NORMAL), _bounds(Rectangle::empty()), _clipBounds(Rectangle::empty()), _viewportClipBounds(Rectangle::empty()),
     _clearBounds(Rectangle::empty()), _dirty(true), _consumeInputEvents(true), _alignment(ALIGN_TOP_LEFT), _isAlignmentSet(false), _autoWidth(false), _autoHeight(false), _listeners(NULL), _visible(true),
     _clearBounds(Rectangle::empty()), _dirty(true), _consumeInputEvents(true), _alignment(ALIGN_TOP_LEFT), _isAlignmentSet(false), _autoWidth(false), _autoHeight(false), _listeners(NULL), _visible(true),
-    _contactIndex(INVALID_CONTACT_INDEX), _focusIndex(0), _parent(NULL), _styleOverridden(false), _skin(NULL)
+    _zIndex(-1), _contactIndex(INVALID_CONTACT_INDEX), _focusIndex(-1), _parent(NULL), _styleOverridden(false), _skin(NULL)
 {
 {
     addScriptEvent("controlEvent", "<Control>[Control::Listener::EventType]");
     addScriptEvent("controlEvent", "<Control>[Control::Listener::EventType]");
 }
 }
@@ -17,9 +17,9 @@ Control::~Control()
 {
 {
     if (_listeners)
     if (_listeners)
     {
     {
-        for (std::map<Listener::EventType, std::list<Listener*>*>::const_iterator itr = _listeners->begin(); itr != _listeners->end(); ++itr)
+        for (std::map<Control::Listener::EventType, std::list<Control::Listener*>*>::const_iterator itr = _listeners->begin(); itr != _listeners->end(); ++itr)
         {
         {
-            std::list<Listener*>* list = itr->second;
+            std::list<Control::Listener*>* list = itr->second;
             SAFE_DELETE(list);
             SAFE_DELETE(list);
         }
         }
         SAFE_DELETE(_listeners);
         SAFE_DELETE(_listeners);
@@ -187,6 +187,11 @@ const Rectangle& Control::getBounds() const
     return _bounds;
     return _bounds;
 }
 }
 
 
+const Rectangle& Control::getAbsoluteBounds() const
+{
+    return _absoluteBounds;
+}
+
 float Control::getX() const
 float Control::getX() const
 {
 {
     return _bounds.x;
     return _bounds.x;
@@ -691,29 +696,29 @@ void Control::addListener(Control::Listener* listener, int eventFlags)
 {
 {
     GP_ASSERT(listener);
     GP_ASSERT(listener);
 
 
-    if ((eventFlags & Listener::PRESS) == Listener::PRESS)
+    if ((eventFlags & Control::Listener::PRESS) == Control::Listener::PRESS)
     {
     {
-        addSpecificListener(listener, Listener::PRESS);
+        addSpecificListener(listener, Control::Listener::PRESS);
     }
     }
 
 
-    if ((eventFlags & Listener::RELEASE) == Listener::RELEASE)
+    if ((eventFlags & Control::Listener::RELEASE) == Control::Listener::RELEASE)
     {
     {
-        addSpecificListener(listener, Listener::RELEASE);
+        addSpecificListener(listener, Control::Listener::RELEASE);
     }
     }
 
 
-    if ((eventFlags & Listener::CLICK) == Listener::CLICK)
+    if ((eventFlags & Control::Listener::CLICK) == Control::Listener::CLICK)
     {
     {
-        addSpecificListener(listener, Listener::CLICK);
+        addSpecificListener(listener, Control::Listener::CLICK);
     }
     }
 
 
-    if ((eventFlags & Listener::VALUE_CHANGED) == Listener::VALUE_CHANGED)
+    if ((eventFlags & Control::Listener::VALUE_CHANGED) == Control::Listener::VALUE_CHANGED)
     {
     {
-        addSpecificListener(listener, Listener::VALUE_CHANGED);
+        addSpecificListener(listener, Control::Listener::VALUE_CHANGED);
     }
     }
 
 
-    if ((eventFlags & Listener::TEXT_CHANGED) == Listener::TEXT_CHANGED)
+    if ((eventFlags & Control::Listener::TEXT_CHANGED) == Control::Listener::TEXT_CHANGED)
     {
     {
-        addSpecificListener(listener, Listener::TEXT_CHANGED);
+        addSpecificListener(listener, Control::Listener::TEXT_CHANGED);
     }
     }
 }
 }
 
 
@@ -722,13 +727,13 @@ void Control::removeListener(Control::Listener* listener)
     if (_listeners == NULL || listener == NULL)
     if (_listeners == NULL || listener == NULL)
         return;
         return;
 
 
-    for (std::map<Listener::EventType, std::list<Listener*>*>::iterator itr = _listeners->begin(); itr != _listeners->end();)
+    for (std::map<Control::Listener::EventType, std::list<Control::Listener*>*>::iterator itr = _listeners->begin(); itr != _listeners->end();)
     {
     {
         itr->second->remove(listener);
         itr->second->remove(listener);
 
 
         if(itr->second->empty())
         if(itr->second->empty())
         {
         {
-            std::list<Listener*>* list = itr->second;
+            std::list<Control::Listener*>* list = itr->second;
             _listeners->erase(itr++);
             _listeners->erase(itr++);
             SAFE_DELETE(list);
             SAFE_DELETE(list);
         }
         }
@@ -740,23 +745,23 @@ void Control::removeListener(Control::Listener* listener)
         SAFE_DELETE(_listeners);
         SAFE_DELETE(_listeners);
 }
 }
 
 
-void Control::addSpecificListener(Control::Listener* listener, Listener::EventType eventType)
+void Control::addSpecificListener(Control::Listener* listener, Control::Listener::EventType eventType)
 {
 {
     GP_ASSERT(listener);
     GP_ASSERT(listener);
 
 
     if (!_listeners)
     if (!_listeners)
     {
     {
-        _listeners = new std::map<Listener::EventType, std::list<Listener*>*>();
+        _listeners = new std::map<Control::Listener::EventType, std::list<Control::Listener*>*>();
     }
     }
 
 
-    std::map<Listener::EventType, std::list<Listener*>*>::const_iterator itr = _listeners->find(eventType);
+    std::map<Control::Listener::EventType, std::list<Control::Listener*>*>::const_iterator itr = _listeners->find(eventType);
     if (itr == _listeners->end())
     if (itr == _listeners->end())
     {
     {
-        _listeners->insert(std::make_pair(eventType, new std::list<Listener*>()));
+        _listeners->insert(std::make_pair(eventType, new std::list<Control::Listener*>()));
         itr = _listeners->find(eventType);
         itr = _listeners->find(eventType);
     }
     }
 
 
-    std::list<Listener*>* listenerList = itr->second;
+    std::list<Control::Listener*>* listenerList = itr->second;
     listenerList->push_back(listener);
     listenerList->push_back(listener);
 }
 }
 
 
@@ -773,7 +778,7 @@ bool Control::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int conta
         {
         {
             _contactIndex = (int) contactIndex;
             _contactIndex = (int) contactIndex;
 
 
-            notifyListeners(Listener::PRESS);
+            notifyListeners(Control::Listener::PRESS);
 
 
             return _consumeInputEvents;
             return _consumeInputEvents;
         }
         }
@@ -789,15 +794,15 @@ bool Control::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int conta
         {
         {
             _contactIndex = INVALID_CONTACT_INDEX;
             _contactIndex = INVALID_CONTACT_INDEX;
 
 
-            // Always trigger Listener::RELEASE
-            notifyListeners(Listener::RELEASE);
+            // Always trigger Control::Listener::RELEASE
+            notifyListeners(Control::Listener::RELEASE);
 
 
-            // Only trigger Listener::CLICK if both PRESS and RELEASE took place within the control's bounds.
+            // Only trigger Control::Listener::CLICK if both PRESS and RELEASE took place within the control's bounds.
             if (x > _clipBounds.x && x <= _clipBounds.x + _clipBounds.width &&
             if (x > _clipBounds.x && x <= _clipBounds.x + _clipBounds.width &&
                 y > _clipBounds.y && y <= _clipBounds.y + _clipBounds.height)
                 y > _clipBounds.y && y <= _clipBounds.y + _clipBounds.height)
             {
             {
                 // Leave this control in the FOCUS state.
                 // Leave this control in the FOCUS state.
-                notifyListeners(Listener::CLICK);
+                notifyListeners(Control::Listener::CLICK);
             }
             }
 
 
             return _consumeInputEvents;
             return _consumeInputEvents;
@@ -834,7 +839,42 @@ bool Control::mouseEvent(Mouse::MouseEvent evt, int x, int y, int wheelDelta)
     return false;
     return false;
 }
 }
 
 
-void Control::notifyListeners(Listener::EventType eventType)
+bool Control::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex)
+{
+    // Default behavior for gamepad events.
+    switch (evt)
+    {
+    case Gamepad::BUTTON_EVENT:
+        if (_state == Control::FOCUS)
+        {
+            if (gamepad->isButtonDown(Gamepad::BUTTON_A) ||
+                gamepad->isButtonDown(Gamepad::BUTTON_X))
+            {
+                notifyListeners(Control::Listener::PRESS);
+                return _consumeInputEvents;
+            }
+        }
+        else if (_state == Control::ACTIVE)
+        {
+            if (!gamepad->isButtonDown(Gamepad::BUTTON_A) &&
+                !gamepad->isButtonDown(Gamepad::BUTTON_X))
+            {
+                notifyListeners(Control::Listener::RELEASE);
+                notifyListeners(Control::Listener::CLICK);
+                return _consumeInputEvents;
+            }
+        }
+        break;
+    case Gamepad::JOYSTICK_EVENT:
+        break;
+    case Gamepad::TRIGGER_EVENT:
+        break;
+    }
+
+    return false;
+}
+
+void Control::notifyListeners(Control::Listener::EventType eventType)
 {
 {
     // This method runs untrusted code by notifying listeners of events.
     // This method runs untrusted code by notifying listeners of events.
     // If the user calls exit() or otherwise releases this control, we
     // If the user calls exit() or otherwise releases this control, we
@@ -843,11 +883,11 @@ void Control::notifyListeners(Listener::EventType eventType)
 
 
     if (_listeners)
     if (_listeners)
     {
     {
-        std::map<Listener::EventType, std::list<Listener*>*>::const_iterator itr = _listeners->find(eventType);
+        std::map<Control::Listener::EventType, std::list<Control::Listener*>*>::const_iterator itr = _listeners->find(eventType);
         if (itr != _listeners->end())
         if (itr != _listeners->end())
         {
         {
-            std::list<Listener*>* listenerList = itr->second;
-            for (std::list<Listener*>::iterator listenerItr = listenerList->begin(); listenerItr != listenerList->end(); ++listenerItr)
+            std::list<Control::Listener*>* listenerList = itr->second;
+            for (std::list<Control::Listener*>::iterator listenerItr = listenerList->begin(); listenerItr != listenerList->end(); ++listenerItr)
             {
             {
                 GP_ASSERT(*listenerItr);
                 GP_ASSERT(*listenerItr);
                 (*listenerItr)->controlEvent(this, eventType);
                 (*listenerItr)->controlEvent(this, eventType);
@@ -1011,7 +1051,7 @@ void Control::drawBorder(SpriteBatch* spriteBatch, const Rectangle& clip)
             spriteBatch->draw(rightX, _absoluteBounds.y, border.right, border.top, topRight.u1, topRight.v1, topRight.u2, topRight.v2, skinColor, clip);
             spriteBatch->draw(rightX, _absoluteBounds.y, border.right, border.top, topRight.u1, topRight.v1, topRight.u2, topRight.v2, skinColor, clip);
         if (border.left)
         if (border.left)
             spriteBatch->draw(_absoluteBounds.x, midY, border.left, midHeight, left.u1, left.v1, left.u2, left.v2, skinColor, clip);
             spriteBatch->draw(_absoluteBounds.x, midY, border.left, midHeight, left.u1, left.v1, left.u2, left.v2, skinColor, clip);
-        
+
         // Always draw the background.
         // Always draw the background.
         spriteBatch->draw(_absoluteBounds.x + border.left, _absoluteBounds.y + border.top, _bounds.width - border.left - border.right, _bounds.height - border.top - border.bottom,
         spriteBatch->draw(_absoluteBounds.x + border.left, _absoluteBounds.y + border.top, _bounds.width - border.left - border.right, _bounds.height - border.top - border.bottom,
             center.u1, center.v1, center.u2, center.v2, skinColor, clip);
             center.u1, center.v1, center.u2, center.v2, skinColor, clip);

+ 37 - 17
gameplay/src/Control.h

@@ -11,6 +11,7 @@
 #include "Keyboard.h"
 #include "Keyboard.h"
 #include "Mouse.h"
 #include "Mouse.h"
 #include "ScriptTarget.h"
 #include "ScriptTarget.h"
+#include "Gamepad.h"
 
 
 namespace gameplay
 namespace gameplay
 {
 {
@@ -85,12 +86,6 @@ public:
         ALIGN_BOTTOM_RIGHT = ALIGN_BOTTOM | ALIGN_RIGHT
         ALIGN_BOTTOM_RIGHT = ALIGN_BOTTOM | ALIGN_RIGHT
     };
     };
 
 
-    /**
-     * @script{ignore}
-     * A constant used for setting themed attributes on all control states simultaneously.
-     */
-    static const unsigned char STATE_ALL = NORMAL | FOCUS | ACTIVE | DISABLED;
-
     /**
     /**
      * Implement Control::Listener and call Control::addListener()
      * Implement Control::Listener and call Control::addListener()
      * in order to listen for events on controls.
      * in order to listen for events on controls.
@@ -156,6 +151,12 @@ public:
         virtual void controlEvent(Control* control, EventType evt) = 0;
         virtual void controlEvent(Control* control, EventType evt) = 0;
     };
     };
 
 
+    /**
+     * @script{ignore}
+     * A constant used for setting themed attributes on all control states simultaneously.
+     */
+    static const unsigned char STATE_ALL = NORMAL | FOCUS | ACTIVE | DISABLED;
+
     /**
     /**
      * Position animation property. Data = x, y
      * Position animation property. Data = x, y
      */
      */
@@ -244,6 +245,14 @@ public:
      */
      */
     const Rectangle& getBounds() const;
     const Rectangle& getBounds() const;
 
 
+    /**
+     * Get the absolute bounds of this control, in pixels, including border and padding,
+     * before clipping.
+     *
+     * @return The absolute bounds of this control.
+     */
+    const Rectangle& getAbsoluteBounds() const;
+
     /**
     /**
      * Get the x coordinate of this control's bounds.
      * Get the x coordinate of this control's bounds.
      *
      *
@@ -841,6 +850,15 @@ protected:
      */
      */
     virtual bool mouseEvent(Mouse::MouseEvent evt, int x, int y, int wheelDelta);
     virtual bool mouseEvent(Mouse::MouseEvent evt, int x, int y, int wheelDelta);
 
 
+    /**
+     * Gamepad callback on gamepad events.
+     *
+     * @param gamepad The gamepad whose state changed.
+     * @param evt The gamepad event that occurred.
+     * @param analogIndex If evt is JOYSTICK_EVENT or TRIGGER_EVENT, this will be the index of the corresponding control.
+     */
+    virtual bool gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex);
+
     /**
     /**
      * Called when a control's properties change.  Updates this control's internal rendering
      * Called when a control's properties change.  Updates this control's internal rendering
      * properties, such as its text viewport.
      * properties, such as its text viewport.
@@ -850,6 +868,14 @@ protected:
      */
      */
     virtual void update(const Control* container, const Vector2& offset);
     virtual void update(const Control* container, const Vector2& offset);
 
 
+    /**
+     * Draws the themed border and background of a control.
+     *
+     * @param spriteBatch The sprite batch containing this control's border images.
+     * @param clip The clipping rectangle of this control's parent container.
+     */
+    virtual void drawBorder(SpriteBatch* spriteBatch, const Rectangle& clip);
+
     /**
     /**
      * Draw the images associated with this control.
      * Draw the images associated with this control.
      *
      *
@@ -915,7 +941,8 @@ protected:
      *
      *
      * @param eventType The event to trigger.
      * @param eventType The event to trigger.
      */
      */
-    void notifyListeners(Listener::EventType eventType);
+    //void notifyListeners(Listener::EventType eventType);
+    void notifyListeners(Control::Listener::EventType eventType);
 
 
     /**
     /**
      * Gets the Alignment by string.
      * Gets the Alignment by string.
@@ -1003,7 +1030,8 @@ protected:
     /**
     /**
      * Listeners map of EventType's to a list of Listeners.
      * Listeners map of EventType's to a list of Listeners.
      */
      */
-    std::map<Listener::EventType, std::list<Listener*>*>* _listeners;
+    //std::map<Listener::EventType, std::list<Listener*>*>* _listeners;
+    std::map<Control::Listener::EventType, std::list<Control::Listener*>*>* _listeners;
     
     
     /**
     /**
      * The Control's Theme::Style.
      * The Control's Theme::Style.
@@ -1063,15 +1091,7 @@ private:
 
 
     Theme::Skin* getSkin(State state);
     Theme::Skin* getSkin(State state);
 
 
-    void addSpecificListener(Control::Listener* listener, Listener::EventType eventType);
-    
-    /**
-     * Draws the themed border and background of a control.
-     *
-     * @param spriteBatch The sprite batch containing this control's border images.
-     * @param clip The clipping rectangle of this control's parent container.
-     */
-    virtual void drawBorder(SpriteBatch* spriteBatch, const Rectangle& clip);
+    void addSpecificListener(Control::Listener* listener, Control::Listener::EventType eventType);
     
     
     bool _styleOverridden;
     bool _styleOverridden;
     Theme::Skin* _skin;
     Theme::Skin* _skin;

+ 119 - 119
gameplay/src/Form.cpp

@@ -389,138 +389,138 @@ void Form::setNode(Node* node)
 }
 }
 
 
 void Form::update(float elapsedTime)
 void Form::update(float elapsedTime)
-{
-    updateBounds();
-}
-
-void Form::updateBounds()
 {
 {
     if (isDirty())
     if (isDirty())
     {
     {
-        _clearBounds.set(_absoluteClipBounds);
+        updateBounds();
 
 
-        // Calculate the clipped bounds.
-        float x = 0;
-        float y = 0;
-        float width = _bounds.width;
-        float height = _bounds.height;
-
-        Rectangle clip(0, 0, _bounds.width, _bounds.height);
-
-        float clipX2 = clip.x + clip.width;
-        float x2 = clip.x + x + width;
-        if (x2 > clipX2)
-            width -= x2 - clipX2;
-
-        float clipY2 = clip.y + clip.height;
-        float y2 = clip.y + y + height;
-        if (y2 > clipY2)
-            height -= y2 - clipY2;
+        // Cache themed attributes for performance.
+        _skin = getSkin(_state);
+        _opacity = getOpacity(_state);
 
 
-        if (x < 0)
+        GP_ASSERT(_layout);
+        if (_scroll != SCROLL_NONE)
         {
         {
-            width += x;
-            x = -x;
+            updateScroll();
         }
         }
         else
         else
         {
         {
-            x = 0;
+            _layout->update(this, Vector2::zero());
         }
         }
+    }
+}
 
 
-        if (y < 0)
-        {
-            height += y;
-            y = -y;
-        }
-        else
-        {
-            y = 0;
-        }
+void Form::updateBounds()
+{   
+    _clearBounds.set(_absoluteClipBounds);
+
+    // Calculate the clipped bounds.
+    float x = 0;
+    float y = 0;
+    float width = _bounds.width;
+    float height = _bounds.height;
+
+    Rectangle clip(0, 0, _bounds.width, _bounds.height);
 
 
-        _clipBounds.set(x, y, width, height);
+    float clipX2 = clip.x + clip.width;
+    float x2 = clip.x + x + width;
+    if (x2 > clipX2)
+        width -= x2 - clipX2;
 
 
-        // Calculate the absolute bounds.
+    float clipY2 = clip.y + clip.height;
+    float y2 = clip.y + y + height;
+    if (y2 > clipY2)
+        height -= y2 - clipY2;
+
+    if (x < 0)
+    {
+        width += x;
+        x = -x;
+    }
+    else
+    {
         x = 0;
         x = 0;
+    }
+
+    if (y < 0)
+    {
+        height += y;
+        y = -y;
+    }
+    else
+    {
         y = 0;
         y = 0;
-        _absoluteBounds.set(x, y, _bounds.width, _bounds.height);
+    }
 
 
-        // Calculate the absolute viewport bounds. Absolute bounds minus border and padding.
-        const Theme::Border& border = getBorder(_state);
-        const Theme::Padding& padding = getPadding();
+    _clipBounds.set(x, y, width, height);
 
 
-        x += border.left + padding.left;
-        y += border.top + padding.top;
-        width = _bounds.width - border.left - padding.left - border.right - padding.right;
-        height = _bounds.height - border.top - padding.top - border.bottom - padding.bottom;
+    // Calculate the absolute bounds.
+    x = 0;
+    y = 0;
+    _absoluteBounds.set(x, y, _bounds.width, _bounds.height);
 
 
-        _viewportBounds.set(x, y, width, height);
+    // Calculate the absolute viewport bounds. Absolute bounds minus border and padding.
+    const Theme::Border& border = getBorder(_state);
+    const Theme::Padding& padding = getPadding();
 
 
-        // Calculate the clip area. Absolute bounds, minus border and padding, clipped to the parent container's clip area.
-        clipX2 = clip.x + clip.width;
-        x2 = x + width;
-        if (x2 > clipX2)
-            width = clipX2 - x;
+    x += border.left + padding.left;
+    y += border.top + padding.top;
+    width = _bounds.width - border.left - padding.left - border.right - padding.right;
+    height = _bounds.height - border.top - padding.top - border.bottom - padding.bottom;
 
 
-        clipY2 = clip.y + clip.height;
-        y2 = y + height;
-        if (y2 > clipY2)
-            height = clipY2 - y;
+    _viewportBounds.set(x, y, width, height);
 
 
-        if (x < clip.x)
-        {
-            float dx = clip.x - x;
-            width -= dx;
-            x = clip.x;
-        }
+    // Calculate the clip area. Absolute bounds, minus border and padding, clipped to the parent container's clip area.
+    clipX2 = clip.x + clip.width;
+    x2 = x + width;
+    if (x2 > clipX2)
+        width = clipX2 - x;
 
 
-        if (y < clip.y)
-        {
-            float dy = clip.y - y;
-            height -= dy;
-            y = clip.y;
-        }
- 
-        _viewportClipBounds.set(x, y, width, height);
-        _absoluteClipBounds.set(x - border.left - padding.left, y - border.top - padding.top,
-                                width + border.left + padding.left + border.right + padding.right,
-                                height + border.top + padding.top + border.bottom + padding.bottom);
-        if (_clearBounds.isEmpty())
-        {
-            _clearBounds.set(_absoluteClipBounds);
-        }
+    clipY2 = clip.y + clip.height;
+    y2 = y + height;
+    if (y2 > clipY2)
+        height = clipY2 - y;
 
 
-        // Cache themed attributes for performance.
-        _skin = getSkin(_state);
-        _opacity = getOpacity(_state);
+    if (x < clip.x)
+    {
+        float dx = clip.x - x;
+        width -= dx;
+        x = clip.x;
+    }
 
 
-        // Get scrollbar images and diminish clipping bounds to make room for scrollbars.
-        if ((_scroll & SCROLL_HORIZONTAL) == SCROLL_HORIZONTAL)
-        {
-            _scrollBarLeftCap = getImage("scrollBarLeftCap", _state);
-            _scrollBarHorizontal = getImage("horizontalScrollBar", _state);
-            _scrollBarRightCap = getImage("scrollBarRightCap", _state);
+    if (y < clip.y)
+    {
+        float dy = clip.y - y;
+        height -= dy;
+        y = clip.y;
+    }
+ 
+    _viewportClipBounds.set(x, y, width, height);
+    _absoluteClipBounds.set(x - border.left - padding.left, y - border.top - padding.top,
+                            width + border.left + padding.left + border.right + padding.right,
+                            height + border.top + padding.top + border.bottom + padding.bottom);
+    if (_clearBounds.isEmpty())
+    {
+        _clearBounds.set(_absoluteClipBounds);
+    }
 
 
-            _viewportClipBounds.height -= _scrollBarHorizontal->getRegion().height;
-        }
+    // Get scrollbar images and diminish clipping bounds to make room for scrollbars.
+    if ((_scroll & SCROLL_HORIZONTAL) == SCROLL_HORIZONTAL)
+    {
+        _scrollBarLeftCap = getImage("scrollBarLeftCap", _state);
+        _scrollBarHorizontal = getImage("horizontalScrollBar", _state);
+        _scrollBarRightCap = getImage("scrollBarRightCap", _state);
 
 
-        if ((_scroll & SCROLL_VERTICAL) == SCROLL_VERTICAL)
-        {
-            _scrollBarTopCap = getImage("scrollBarTopCap", _state);
-            _scrollBarVertical = getImage("verticalScrollBar", _state);
-            _scrollBarBottomCap = getImage("scrollBarBottomCap", _state);
-        
-            _viewportClipBounds.width -= _scrollBarVertical->getRegion().width;
-        }
+        _viewportClipBounds.height -= _scrollBarHorizontal->getRegion().height;
+    }
 
 
-        GP_ASSERT(_layout);
-        if (_scroll != SCROLL_NONE)
-        {
-            updateScroll();
-        }
-        else
-        {
-            _layout->update(this, Vector2::zero());
-        }
+    if ((_scroll & SCROLL_VERTICAL) == SCROLL_VERTICAL)
+    {
+        _scrollBarTopCap = getImage("scrollBarTopCap", _state);
+        _scrollBarVertical = getImage("verticalScrollBar", _state);
+        _scrollBarBottomCap = getImage("scrollBarBottomCap", _state);
+        
+        _viewportClipBounds.width -= _scrollBarVertical->getRegion().width;
     }
     }
 }
 }
 
 
@@ -605,7 +605,6 @@ bool Form::touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned int
 {
 {
     // Check for a collision with each Form in __forms.
     // Check for a collision with each Form in __forms.
     // Pass the event on.
     // Pass the event on.
-    bool eventConsumed = false;
     size_t size = __forms.size();
     size_t size = __forms.size();
     for (size_t i = 0; i < size; ++i)
     for (size_t i = 0; i < size; ++i)
     {
     {
@@ -627,7 +626,8 @@ bool Form::touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned int
                          point.y >= bounds.y &&
                          point.y >= bounds.y &&
                          point.y <= bounds.y + bounds.height))
                          point.y <= bounds.y + bounds.height))
                     {
                     {
-                        eventConsumed |= form->touchEvent(evt, point.x - bounds.x, bounds.height - point.y - bounds.y, contactIndex);
+                        if (form->touchEvent(evt, point.x - bounds.x, bounds.height - point.y - bounds.y, contactIndex))
+                            return true;
                     }
                     }
                 }
                 }
             }
             }
@@ -643,12 +643,13 @@ bool Form::touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned int
                         y <= bounds.y + bounds.height))
                         y <= bounds.y + bounds.height))
                 {
                 {
                     // Pass on the event's position relative to the form.
                     // Pass on the event's position relative to the form.
-                    eventConsumed |= form->touchEvent(evt, x - bounds.x, y - bounds.y, contactIndex);
+                    if (form->touchEvent(evt, x - bounds.x, y - bounds.y, contactIndex))
+                        return true;
                 }
                 }
             }
             }
         }
         }
     }
     }
-    return eventConsumed;
+    return false;
 }
 }
 
 
 bool Form::keyEventInternal(Keyboard::KeyEvent evt, int key)
 bool Form::keyEventInternal(Keyboard::KeyEvent evt, int key)
@@ -669,8 +670,6 @@ bool Form::keyEventInternal(Keyboard::KeyEvent evt, int key)
 
 
 bool Form::mouseEventInternal(Mouse::MouseEvent evt, int x, int y, int wheelDelta)
 bool Form::mouseEventInternal(Mouse::MouseEvent evt, int x, int y, int wheelDelta)
 {
 {
-    bool eventConsumed = false;
-
     for (size_t i = 0; i < __forms.size(); ++i)
     for (size_t i = 0; i < __forms.size(); ++i)
     {
     {
         Form* form = __forms[i];
         Form* form = __forms[i];
@@ -694,7 +693,8 @@ bool Form::mouseEventInternal(Mouse::MouseEvent evt, int x, int y, int wheelDelt
                          point.y >= bounds.y &&
                          point.y >= bounds.y &&
                          point.y <= bounds.y + bounds.height))
                          point.y <= bounds.y + bounds.height))
                     {
                     {
-                        eventConsumed |= form->mouseEvent(evt, point.x - bounds.x, bounds.height - point.y - bounds.y, wheelDelta);
+                        if (form->mouseEvent(evt, point.x - bounds.x, bounds.height - point.y - bounds.y, wheelDelta))
+                            return true;
                     }
                     }
                 }
                 }
             }
             }
@@ -713,30 +713,30 @@ bool Form::mouseEventInternal(Mouse::MouseEvent evt, int x, int y, int wheelDelt
                         y <= bounds.y + bounds.height))
                         y <= bounds.y + bounds.height))
                 {
                 {
                     // Pass on the event's position relative to the form.
                     // Pass on the event's position relative to the form.
-                    eventConsumed |= form->mouseEvent(evt, x - bounds.x, y - bounds.y, wheelDelta);
+                    if (form->mouseEvent(evt, x - bounds.x, y - bounds.y, wheelDelta))
+                        return true;
                 }
                 }
             }
             }
         }
         }
     }
     }
-    return eventConsumed;
+    return false;
 }
 }
 
 
 bool Form::gamepadEventInternal(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex)
 bool Form::gamepadEventInternal(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex)
 {
 {
-    bool eventConsumed = false;
-
     for (size_t i = 0; i < __forms.size(); ++i)
     for (size_t i = 0; i < __forms.size(); ++i)
     {
     {
         Form* form = __forms[i];
         Form* form = __forms[i];
         GP_ASSERT(form);
         GP_ASSERT(form);
 
 
-        if (form->isEnabled() && form->isVisible())
+        if (form->isEnabled() && form->isVisible() && form->getState() == FOCUS)
         {
         {
-            eventConsumed |= form->gamepadEventInternal(evt, gamepad, analogIndex);
+            if (form->gamepadEvent(evt, gamepad, analogIndex))
+                return true;
         }
         }
     }
     }
 
 
-    return eventConsumed;
+    return false;
 }
 }
 
 
 bool Form::projectPoint(int x, int y, Vector3* point)
 bool Form::projectPoint(int x, int y, Vector3* point)

+ 7 - 0
gameplay/src/Form.h

@@ -219,6 +219,13 @@ private:
      */
      */
     static bool mouseEventInternal(Mouse::MouseEvent evt, int x, int y, int wheelDelta);
     static bool mouseEventInternal(Mouse::MouseEvent evt, int x, int y, int wheelDelta);
 
 
+    /**
+     * Propagate gamepad events to enabled forms.
+     *
+     * @return True if the gamepad event is consumed, false otherwise.
+     *
+     * @see Control::gamepadEvent
+     */
     static bool gamepadEventInternal(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex);
     static bool gamepadEventInternal(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex);
 
 
     /**
     /**

+ 7 - 7
gameplay/src/Game.cpp

@@ -317,12 +317,12 @@ void Game::frame()
         // Update gamepads.
         // Update gamepads.
         Gamepad::updateInternal(elapsedTime);
         Gamepad::updateInternal(elapsedTime);
 
 
-        // Update forms.
-        Form::updateInternal(elapsedTime);
-
         // Application Update.
         // Application Update.
         update(elapsedTime);
         update(elapsedTime);
 
 
+        // Update forms.
+        Form::updateInternal(elapsedTime);
+
         // Run script update.
         // Run script update.
         _scriptController->update(elapsedTime);
         _scriptController->update(elapsedTime);
 
 
@@ -349,12 +349,12 @@ void Game::frame()
         // Update gamepads.
         // Update gamepads.
         Gamepad::updateInternal(0);
         Gamepad::updateInternal(0);
 
 
-        // Update forms.
-        Form::updateInternal(0);
-
         // Application Update.
         // Application Update.
         update(0);
         update(0);
 
 
+        // Update forms.
+        Form::updateInternal(0);
+
         // Script update.
         // Script update.
         _scriptController->update(0);
         _scriptController->update(0);
 
 
@@ -509,7 +509,7 @@ void Game::gestureTapEvent(int x, int y)
 {
 {
 }
 }
 
 
-void Game::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad)
+void Game::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex)
 {
 {
 }
 }
 
 

+ 6 - 4
gameplay/src/Game.h

@@ -443,13 +443,15 @@ public:
     virtual void gestureTapEvent(int x, int y);
     virtual void gestureTapEvent(int x, int y);
 
 
     /**
     /**
-     * Gamepad callback on gamepad events. Override to receive Gamepad::CONNECTED_EVENT 
-     * and Gamepad::DISCONNECTED_EVENT.
+     * Gamepad callback on gamepad events.  Override to receive Gamepad::CONNECTED_EVENT 
+     * and Gamepad::DISCONNECTED_EVENT, and store the Gamepad* in order to poll it from update().
+     * Or, handle all gamepad input through BUTTON, JOYSTICK and TRIGGER events.
      *
      *
      * @param evt The gamepad event that occurred.
      * @param evt The gamepad event that occurred.
-     * @param gamepad the gamepad the event occurred on
+     * @param gamepad The gamepad that generated the event.
+     * @param analogIndex If this is a JOYSTICK_EVENT or TRIGGER_EVENT, the index of the joystick or trigger whose value changed.
      */
      */
-    virtual void gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad);
+    virtual void gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex = 0);
 
 
     /**
     /**
      * Gets the current number of gamepads currently connected to the system.
      * Gets the current number of gamepads currently connected to the system.

+ 3 - 3
gameplay/src/Gamepad.cpp

@@ -384,7 +384,7 @@ void Gamepad::setButtons(unsigned int buttons)
     if (buttons != _buttons)
     if (buttons != _buttons)
     {
     {
         _buttons = buttons;
         _buttons = buttons;
-        Form::gamepadEventInternal(BUTTON_EVENT, this, 0);
+        Platform::gamepadEventInternal(BUTTON_EVENT, this);
     }
     }
 }
 }
 
 
@@ -393,7 +393,7 @@ void Gamepad::setJoystickValue(unsigned int index, float x, float y)
     if (_joysticks[index].x != x || _joysticks[index].y != y)
     if (_joysticks[index].x != x || _joysticks[index].y != y)
     {
     {
         _joysticks[index].set(x, y);
         _joysticks[index].set(x, y);
-        Form::gamepadEventInternal(JOYSTICK_EVENT, this, index);
+        Platform::gamepadEventInternal(JOYSTICK_EVENT, this, index);
     }
     }
 }
 }
 
 
@@ -402,7 +402,7 @@ void Gamepad::setTriggerValue(unsigned int index, float value)
     if (_triggers[index] != value)
     if (_triggers[index] != value)
     {
     {
         _triggers[index] = value;
         _triggers[index] = value;
-        Form::gamepadEventInternal(TRIGGER_EVENT, this, index);
+        Platform::gamepadEventInternal(TRIGGER_EVENT, this, index);
     }
     }
 }
 }
 
 

+ 1 - 1
gameplay/src/Image.h

@@ -66,7 +66,7 @@ private:
      * Constructor.
      * Constructor.
      */
      */
     Image();
     Image();
-        
+
     /**
     /**
      * Destructor.
      * Destructor.
      */
      */

+ 2 - 0
gameplay/src/ImageControl.h

@@ -52,6 +52,8 @@ public:
 
 
     /**
     /**
      * Set the path of the image for this ImageControl to display.
      * Set the path of the image for this ImageControl to display.
+     *
+     * @param path The path to the image.
      */
      */
     void setImage(const char* path);
     void setImage(const char* path);
 
 

+ 6 - 8
gameplay/src/Joystick.cpp

@@ -110,7 +110,7 @@ void Joystick::initialize(Theme::Style* style, Properties* properties)
 
 
 void Joystick::addListener(Control::Listener* listener, int eventFlags)
 void Joystick::addListener(Control::Listener* listener, int eventFlags)
 {
 {
-    if ((eventFlags & Listener::TEXT_CHANGED) == Listener::TEXT_CHANGED)
+    if ((eventFlags & Control::Listener::TEXT_CHANGED) == Control::Listener::TEXT_CHANGED)
     {
     {
         GP_ERROR("TEXT_CHANGED event is not applicable to this control.");
         GP_ERROR("TEXT_CHANGED event is not applicable to this control.");
     }
     }
@@ -130,7 +130,7 @@ bool Joystick::touchEvent(Touch::TouchEvent touchEvent, int x, int y, unsigned i
                 float dy = 0.0f;
                 float dy = 0.0f;
 
 
                 _contactIndex = (int) contactIndex;
                 _contactIndex = (int) contactIndex;
-                notifyListeners(Listener::PRESS);
+                notifyListeners(Control::Listener::PRESS);
 
 
                 // Get the displacement of the touch from the centre.
                 // Get the displacement of the touch from the centre.
                 if (!_relative)
                 if (!_relative)
@@ -168,7 +168,7 @@ bool Joystick::touchEvent(Touch::TouchEvent touchEvent, int x, int y, unsigned i
                 {
                 {
                     _value.set(value);
                     _value.set(value);
                     _dirty = true;
                     _dirty = true;
-                    notifyListeners(Listener::VALUE_CHANGED);
+                    notifyListeners(Control::Listener::VALUE_CHANGED);
                 }
                 }
 
 
                 _state = ACTIVE;
                 _state = ACTIVE;
@@ -203,7 +203,7 @@ bool Joystick::touchEvent(Touch::TouchEvent touchEvent, int x, int y, unsigned i
                 {
                 {
                     _value.set(value);
                     _value.set(value);
                     _dirty = true;
                     _dirty = true;
-                    notifyListeners(Listener::VALUE_CHANGED);
+                    notifyListeners(Control::Listener::VALUE_CHANGED);
                 }
                 }
 
 
                 return _consumeInputEvents;
                 return _consumeInputEvents;
@@ -216,7 +216,7 @@ bool Joystick::touchEvent(Touch::TouchEvent touchEvent, int x, int y, unsigned i
             {
             {
                 _contactIndex = INVALID_CONTACT_INDEX;
                 _contactIndex = INVALID_CONTACT_INDEX;
 
 
-                notifyListeners(Listener::RELEASE);
+                notifyListeners(Control::Listener::RELEASE);
 
 
                 // Reset displacement and direction vectors.
                 // Reset displacement and direction vectors.
                 _displacement.set(0.0f, 0.0f);
                 _displacement.set(0.0f, 0.0f);
@@ -225,7 +225,7 @@ bool Joystick::touchEvent(Touch::TouchEvent touchEvent, int x, int y, unsigned i
                 {
                 {
                     _value.set(value);
                     _value.set(value);
                     _dirty = true;
                     _dirty = true;
-                    notifyListeners(Listener::VALUE_CHANGED);
+                    notifyListeners(Control::Listener::VALUE_CHANGED);
                 }
                 }
 
 
                 _state = NORMAL;
                 _state = NORMAL;
@@ -242,7 +242,6 @@ bool Joystick::touchEvent(Touch::TouchEvent touchEvent, int x, int y, unsigned i
 void Joystick::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)
 void Joystick::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)
 {
 {
     GP_ASSERT(spriteBatch);
     GP_ASSERT(spriteBatch);
-    spriteBatch->start();
 
 
     // If the joystick is not absolute, then only draw if it is active.
     // If the joystick is not absolute, then only draw if it is active.
     if (!_relative || (_relative && _state == ACTIVE))
     if (!_relative || (_relative && _state == ACTIVE))
@@ -284,7 +283,6 @@ void Joystick::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)
                 spriteBatch->draw(position.x, position.y, _innerSize->x, _innerSize->y, uvs.u1, uvs.v1, uvs.u2, uvs.v2, color, _viewportClipBounds);
                 spriteBatch->draw(position.x, position.y, _innerSize->x, _innerSize->y, uvs.u1, uvs.v1, uvs.u2, uvs.v2, color, _viewportClipBounds);
         }
         }
     }
     }
-    spriteBatch->finish();
 }
 }
 
 
 const char* Joystick::getType() const
 const char* Joystick::getType() const

+ 9 - 7
gameplay/src/Label.cpp

@@ -21,6 +21,12 @@ Label* Label::create(const char* id, Theme::Style* style)
         label->_id = id;
         label->_id = id;
     label->setStyle(style);
     label->setStyle(style);
 
 
+    // Labels don't consume input events by default like other controls.
+    label->_consumeInputEvents = false;
+
+    // Ensure that labels cannot receive focus.
+    label->_focusIndex = -2;
+
     return label;
     return label;
 }
 }
 
 
@@ -28,9 +34,8 @@ Label* Label::create(Theme::Style* style, Properties* properties)
 {
 {
     Label* label = new Label();
     Label* label = new Label();
     label->initialize(style, properties);
     label->initialize(style, properties);
+
     label->_consumeInputEvents = false;
     label->_consumeInputEvents = false;
-    
-    // Ensure that labels cannot receive focus.
     label->_focusIndex = -2;
     label->_focusIndex = -2;
 
 
     return label;
     return label;
@@ -41,7 +46,6 @@ void Label::initialize(Theme::Style* style, Properties* properties)
     GP_ASSERT(properties);
     GP_ASSERT(properties);
 
 
     Control::initialize(style, properties);
     Control::initialize(style, properties);
-
     const char* text = properties->getString("text");
     const char* text = properties->getString("text");
     if (text)
     if (text)
     {
     {
@@ -51,11 +55,11 @@ void Label::initialize(Theme::Style* style, Properties* properties)
 
 
 void Label::addListener(Control::Listener* listener, int eventFlags)
 void Label::addListener(Control::Listener* listener, int eventFlags)
 {
 {
-    if ((eventFlags & Listener::TEXT_CHANGED) == Listener::TEXT_CHANGED)
+    if ((eventFlags & Control::Listener::TEXT_CHANGED) == Control::Listener::TEXT_CHANGED)
     {
     {
         GP_ERROR("TEXT_CHANGED event is not applicable to this control.");
         GP_ERROR("TEXT_CHANGED event is not applicable to this control.");
     }
     }
-    if ((eventFlags & Listener::VALUE_CHANGED) == Listener::VALUE_CHANGED)
+    if ((eventFlags & Control::Listener::VALUE_CHANGED) == Control::Listener::VALUE_CHANGED)
     {
     {
         GP_ERROR("VALUE_CHANGED event is not applicable to this control.");
         GP_ERROR("VALUE_CHANGED event is not applicable to this control.");
     }
     }
@@ -102,8 +106,6 @@ void Label::drawText(const Rectangle& clip)
         _font->drawText(_text.c_str(), _textBounds, _textColor, getFontSize(_state), getTextAlignment(_state), true, getTextRightToLeft(_state), &_viewportClipBounds);
         _font->drawText(_text.c_str(), _textBounds, _textColor, getFontSize(_state), getTextAlignment(_state), true, getTextRightToLeft(_state), &_viewportClipBounds);
         _font->finish();
         _font->finish();
     }
     }
-
-    _dirty = false;
 }
 }
 
 
 const char* Label::getType() const
 const char* Label::getType() const

+ 9 - 0
gameplay/src/Platform.cpp

@@ -55,6 +55,15 @@ void Platform::resizeEventInternal(unsigned int width, unsigned int height)
     }
     }
 }
 }
 
 
+void Platform::gamepadEventInternal(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex)
+{
+    if (!Form::gamepadEventInternal(evt, gamepad, analogIndex))
+    {
+        Game::getInstance()->gamepadEvent(evt, gamepad, analogIndex);
+        Game::getInstance()->getScriptController()->gamepadEvent(evt, gamepad, analogIndex);
+    }
+}
+
 void Platform::gamepadEventConnectedInternal(GamepadHandle handle,  unsigned int buttonCount, unsigned int joystickCount, unsigned int triggerCount,
 void Platform::gamepadEventConnectedInternal(GamepadHandle handle,  unsigned int buttonCount, unsigned int joystickCount, unsigned int triggerCount,
                                              unsigned int vendorId, unsigned int productId, const char* vendorString, const char* productString)
                                              unsigned int vendorId, unsigned int productId, const char* vendorString, const char* productString)
 {
 {

+ 10 - 2
gameplay/src/Platform.h

@@ -300,7 +300,14 @@ public:
      */
      */
     static void resizeEventInternal(unsigned int width, unsigned int height);
     static void resizeEventInternal(unsigned int width, unsigned int height);
 
 
-   /**
+    /**
+     * Internal method used only from static code in various platform implementation.
+     *
+     * @script{ignore}
+     */
+    static void gamepadEventInternal(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex = 0);
+
+    /**
      * Internal method used only from static code in various platform implementation.
      * Internal method used only from static code in various platform implementation.
      *
      *
      * @script{ignore}
      * @script{ignore}
@@ -308,7 +315,8 @@ public:
     static void gamepadEventConnectedInternal(GamepadHandle handle, unsigned int buttonCount, unsigned int joystickCount, unsigned int triggerCount,
     static void gamepadEventConnectedInternal(GamepadHandle handle, unsigned int buttonCount, unsigned int joystickCount, unsigned int triggerCount,
                                               unsigned int vendorId, unsigned int productId, 
                                               unsigned int vendorId, unsigned int productId, 
                                               const char* vendorString, const char* productString);
                                               const char* vendorString, const char* productString);
-   /**
+
+    /**
      * Internal method used only from static code in various platform implementation.
      * Internal method used only from static code in various platform implementation.
      *
      *
      * @script{ignore}
      * @script{ignore}

+ 5 - 3
gameplay/src/PlatformBlackBerry.cpp

@@ -575,7 +575,9 @@ void queryGamepad(GamepadHandle handle, int* buttonCount, int* joystickCount, in
 
 
 void Platform::pollGamepadState(Gamepad* gamepad)
 void Platform::pollGamepadState(Gamepad* gamepad)
 {
 {
-    screen_get_device_property_iv(gamepad->_handle, SCREEN_PROPERTY_BUTTONS, (int*)&gamepad->_buttons);
+	unsigned int buttons;
+    screen_get_device_property_iv(gamepad->_handle, SCREEN_PROPERTY_BUTTONS, (int*)&buttons);
+	gamepad->setButtons(buttons);
 
 
     unsigned int i;
     unsigned int i;
     for (i = 0; i < gamepad->_joystickCount; ++i)
     for (i = 0; i < gamepad->_joystickCount; ++i)
@@ -604,7 +606,7 @@ void Platform::pollGamepadState(Gamepad* gamepad)
         x *= (x < 0) ? 0.0078125f : 0.0078740157480315f;
         x *= (x < 0) ? 0.0078125f : 0.0078740157480315f;
         y *= (y > 0) ? 0.0078125f : 0.0078740157480315f;
         y *= (y > 0) ? 0.0078125f : 0.0078740157480315f;
 
 
-        gamepad->_joysticks[i].set(x, y);        
+		gamepad->setJoystickValue(i, x, y);
     }
     }
 
 
     for (i = 0; i < gamepad->_triggerCount; ++i)
     for (i = 0; i < gamepad->_triggerCount; ++i)
@@ -623,7 +625,7 @@ void Platform::pollGamepadState(Gamepad* gamepad)
         }
         }
 
 
         float value = (float)analog[2] * 0.0078125f;
         float value = (float)analog[2] * 0.0078125f;
-        gamepad->_triggers[i] = value;
+		gamepad->setTriggerValue(i, value);
     }
     }
 }
 }
 #else
 #else

+ 15 - 14
gameplay/src/PlatformMacOSX.mm

@@ -2046,7 +2046,7 @@ void Platform::pollGamepadState(Gamepad* gamepad)
             }
             }
         }
         }
         
         
-        gamepad->_buttons = 0;
+        unsigned int buttons = 0;
         for (int i = 0; i < [gp numberOfButtons]; ++i)
         for (int i = 0; i < [gp numberOfButtons]; ++i)
         {
         {
             HIDGamepadButton* b = [gp buttonAtIndex: i];
             HIDGamepadButton* b = [gp buttonAtIndex: i];
@@ -2056,11 +2056,11 @@ void Platform::pollGamepadState(Gamepad* gamepad)
                 if (mapping)
                 if (mapping)
                 {
                 {
                     if (mapping[i] >= 0)
                     if (mapping[i] >= 0)
-                        gamepad->_buttons |= (1 << mapping[i]);
+                        buttons |= (1 << mapping[i]);
                 }
                 }
                 else
                 else
                 {
                 {
-                    gamepad->_buttons |= (1 << i);
+                    buttons |= (1 << i);
                 }
                 }
             }
             }
         }
         }
@@ -2074,32 +2074,34 @@ void Platform::pollGamepadState(Gamepad* gamepad)
                 case -1:
                 case -1:
                     break;
                     break;
                 case 0:
                 case 0:
-                    gamepad->_buttons |= (1 << Gamepad::BUTTON_UP);
+                    buttons |= (1 << Gamepad::BUTTON_UP);
                     break;
                     break;
                 case 1:
                 case 1:
-                    gamepad->_buttons |= (1 << Gamepad::BUTTON_UP) | (1 << Gamepad::BUTTON_RIGHT);
+                    buttons |= (1 << Gamepad::BUTTON_UP) | (1 << Gamepad::BUTTON_RIGHT);
                     break;
                     break;
                 case 2:
                 case 2:
-                    gamepad->_buttons |= (1 << Gamepad::BUTTON_RIGHT);
+                    buttons |= (1 << Gamepad::BUTTON_RIGHT);
                     break;
                     break;
                 case 3:
                 case 3:
-                    gamepad->_buttons |= (1 << Gamepad::BUTTON_RIGHT) | (1 << Gamepad::BUTTON_DOWN);
+                    buttons |= (1 << Gamepad::BUTTON_RIGHT) | (1 << Gamepad::BUTTON_DOWN);
                     break;
                     break;
                 case 4:
                 case 4:
-                    gamepad->_buttons |= (1 << Gamepad::BUTTON_DOWN);
+                    buttons |= (1 << Gamepad::BUTTON_DOWN);
                     break;
                     break;
                 case 5:
                 case 5:
-                    gamepad->_buttons |= (1 << Gamepad::BUTTON_DOWN) | (1 << Gamepad::BUTTON_LEFT);
+                    buttons |= (1 << Gamepad::BUTTON_DOWN) | (1 << Gamepad::BUTTON_LEFT);
                     break;
                     break;
                 case 6:
                 case 6:
-                    gamepad->_buttons |= (1 << Gamepad::BUTTON_LEFT);
+                    buttons |= (1 << Gamepad::BUTTON_LEFT);
                     break;
                     break;
                 case 7:
                 case 7:
-                    gamepad->_buttons |= (1 << Gamepad::BUTTON_LEFT) | (1 << Gamepad::BUTTON_UP);
+                    buttons |= (1 << Gamepad::BUTTON_LEFT) | (1 << Gamepad::BUTTON_UP);
                     break;
                     break;
             }
             }
         }
         }
         
         
+        gamepad->setButtons(buttons);
+        
         for (unsigned int i = 0; i < [gp numberOfSticks]; ++i)
         for (unsigned int i = 0; i < [gp numberOfSticks]; ++i)
         {
         {
             float rawX = [[gp axisAtIndex: i*2] calibratedValue];
             float rawX = [[gp axisAtIndex: i*2] calibratedValue];
@@ -2109,13 +2111,12 @@ void Platform::pollGamepadState(Gamepad* gamepad)
             if (std::fabs(rawY) <= axisDeadZone)
             if (std::fabs(rawY) <= axisDeadZone)
                 rawY = 0;
                 rawY = 0;
             
             
-            gamepad->_joysticks[i].x = rawX;
-            gamepad->_joysticks[i].y = rawY;
+            gamepad->setJoystickValue(i, rawX, rawY);
         }
         }
         
         
         for (unsigned int i = 0; i < [gp numberOfTriggerButtons]; ++i)
         for (unsigned int i = 0; i < [gp numberOfTriggerButtons]; ++i)
         {
         {
-            gamepad->_triggers[i] = [[gp triggerButtonAtIndex: i] calibratedStateValue];
+            gamepad->setTriggerValue(i, [[gp triggerButtonAtIndex: i] calibratedStateValue]);
         }
         }
     }
     }
 }
 }

+ 8 - 12
gameplay/src/PlatformWindows.cpp

@@ -44,9 +44,7 @@ static unsigned int __gamepadsConnected = 0;
 static const unsigned int XINPUT_BUTTON_COUNT = 14;
 static const unsigned int XINPUT_BUTTON_COUNT = 14;
 static const unsigned int XINPUT_JOYSTICK_COUNT = 2;
 static const unsigned int XINPUT_JOYSTICK_COUNT = 2;
 static const unsigned int XINPUT_TRIGGER_COUNT = 2;
 static const unsigned int XINPUT_TRIGGER_COUNT = 2;
-#endif
 
 
-#ifdef USE_XINPUT
 static XINPUT_STATE __xInputState;
 static XINPUT_STATE __xInputState;
 static bool __connectedXInput[4];
 static bool __connectedXInput[4];
 
 
@@ -947,7 +945,6 @@ Platform* Platform::create(Game* game, void* attachToWindow)
                 Platform::gamepadEventConnectedInternal(i, XINPUT_BUTTON_COUNT, XINPUT_JOYSTICK_COUNT, XINPUT_TRIGGER_COUNT, 0, 0, "Microsoft", "XBox360 Controller");
                 Platform::gamepadEventConnectedInternal(i, XINPUT_BUTTON_COUNT, XINPUT_JOYSTICK_COUNT, XINPUT_TRIGGER_COUNT, 0, 0, "Microsoft", "XBox360 Controller");
                 __connectedXInput[i] = true;
                 __connectedXInput[i] = true;
             }
             }
-
         }
         }
     }
     }
 #endif
 #endif
@@ -1245,13 +1242,15 @@ void Platform::pollGamepadState(Gamepad* gamepad)
         };
         };
 
 
         const unsigned int *mapping = xInputMapping;
         const unsigned int *mapping = xInputMapping;
-        for (gamepad->_buttons = 0; buttons; buttons >>= 1, mapping++)
+        unsigned int mappedButtons;
+        for (mappedButtons = 0; buttons; buttons >>= 1, mapping++)
         {
         {
             if (buttons & 1)
             if (buttons & 1)
             {
             {
-                gamepad->_buttons |= (1 << *mapping);
+                mappedButtons |= (1 << *mapping);
             }
             }
         }
         }
+        gamepad->setButtons(mappedButtons);
 
 
         unsigned int i;
         unsigned int i;
         for (i = 0; i < gamepad->_joystickCount; ++i)
         for (i = 0; i < gamepad->_joystickCount; ++i)
@@ -1272,7 +1271,7 @@ void Platform::pollGamepadState(Gamepad* gamepad)
                 break;
                 break;
             }
             }
 
 
-            gamepad->_joysticks[i].set(x, y);
+            gamepad->setJoystickValue(i, x, y);
         }
         }
 
 
         for (i = 0; i < gamepad->_triggerCount; ++i)
         for (i = 0; i < gamepad->_triggerCount; ++i)
@@ -1292,20 +1291,17 @@ void Platform::pollGamepadState(Gamepad* gamepad)
 
 
             if (trigger < XINPUT_GAMEPAD_TRIGGER_THRESHOLD)
             if (trigger < XINPUT_GAMEPAD_TRIGGER_THRESHOLD)
             {
             {
-                gamepad->_triggers[i] = 0.0f;
+                gamepad->setTriggerValue(i, 0.0f);
             }
             }
             else
             else
             {
             {
-                gamepad->_triggers[i] = (float)trigger / 255.0f;
+                gamepad->setTriggerValue(i, (float)trigger / 255.0f);
             }
             }
         }
         }
     }
     }
 }
 }
 #else
 #else
-void Platform::pollGamepadState(Gamepad* gamepad)
-{
-    // TODO: Support generic HID gamepads (including XBox controllers) without requiring XInput.
-}
+void Platform::pollGamepadState(Gamepad* gamepad) { }
 #endif
 #endif
 
 
 void Platform::shutdownInternal()
 void Platform::shutdownInternal()

+ 24 - 3
gameplay/src/RadioButton.cpp

@@ -81,7 +81,7 @@ const Vector2& RadioButton::getImageSize() const
 
 
 void RadioButton::addListener(Control::Listener* listener, int eventFlags)
 void RadioButton::addListener(Control::Listener* listener, int eventFlags)
 {
 {
-    if ((eventFlags & Listener::TEXT_CHANGED) == Listener::TEXT_CHANGED)
+    if ((eventFlags & Control::Listener::TEXT_CHANGED) == Control::Listener::TEXT_CHANGED)
     {
     {
         GP_ERROR("TEXT_CHANGED event is not applicable to RadioButton.");
         GP_ERROR("TEXT_CHANGED event is not applicable to RadioButton.");
     }
     }
@@ -105,7 +105,7 @@ bool RadioButton::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int c
                     {
                     {
                         RadioButton::clearSelected(_groupId);
                         RadioButton::clearSelected(_groupId);
                         _selected = true;
                         _selected = true;
-                        notifyListeners(Listener::VALUE_CHANGED);
+                        notifyListeners(Control::Listener::VALUE_CHANGED);
                     }
                     }
                 }
                 }
             }
             }
@@ -116,6 +116,27 @@ bool RadioButton::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int c
     return Button::touchEvent(evt, x, y, contactIndex);
     return Button::touchEvent(evt, x, y, contactIndex);
 }
 }
 
 
+bool RadioButton::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex)
+{
+    switch (evt)
+    {
+    case Gamepad::BUTTON_EVENT:
+        if (_state == Control::ACTIVE)
+        {
+            if (!gamepad->isButtonDown(Gamepad::BUTTON_A) &&
+                !gamepad->isButtonDown(Gamepad::BUTTON_X))
+            {
+                RadioButton::clearSelected(_groupId);
+                _selected = true;
+                notifyListeners(Control::Listener::VALUE_CHANGED);
+            }
+        }
+        break;
+    }
+
+    return Button::gamepadEvent(evt, gamepad, analogIndex);
+}
+
 void RadioButton::clearSelected(const std::string& groupId)
 void RadioButton::clearSelected(const std::string& groupId)
 {
 {
     std::vector<RadioButton*>::const_iterator it;
     std::vector<RadioButton*>::const_iterator it;
@@ -127,7 +148,7 @@ void RadioButton::clearSelected(const std::string& groupId)
         {
         {
             radioButton->_selected = false;
             radioButton->_selected = false;
             radioButton->_dirty = true;
             radioButton->_dirty = true;
-            radioButton->notifyListeners(Listener::VALUE_CHANGED);
+            radioButton->notifyListeners(Control::Listener::VALUE_CHANGED);
         }
         }
     }
     }
 }
 }

+ 7 - 0
gameplay/src/RadioButton.h

@@ -144,6 +144,13 @@ protected:
      */
      */
     bool touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex);
     bool touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex);
 
 
+    /**
+     * Gamepad callback on gamepad events.
+     *
+     * @see Control::gamepadEvent
+     */
+    bool gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex);
+
     /**
     /**
      * Called when a control's properties change.  Updates this control's internal rendering
      * Called when a control's properties change.  Updates this control's internal rendering
      * properties, such as its text viewport.
      * properties, such as its text viewport.

+ 1 - 1
gameplay/src/ScriptController.cpp

@@ -783,7 +783,7 @@ bool ScriptController::mouseEvent(Mouse::MouseEvent evt, int x, int y, int wheel
     return false;
     return false;
 }
 }
 
 
-void ScriptController::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad)
+void ScriptController::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex)
 {
 {
     std::vector<std::string>& list = _callbacks[GAMEPAD_EVENT];
     std::vector<std::string>& list = _callbacks[GAMEPAD_EVENT];
     for (size_t i = 0, count = list.size(); i < count; ++i)
     for (size_t i = 0, count = list.size(); i < count; ++i)

+ 3 - 2
gameplay/src/ScriptController.h

@@ -3,7 +3,8 @@
 
 
 #include "Base.h"
 #include "Base.h"
 #include "Game.h"
 #include "Game.h"
-#include "Gamepad.h"
+//#include "Gamepad.h"
+#include "Control.h"
 
 
 namespace gameplay
 namespace gameplay
 {
 {
@@ -872,7 +873,7 @@ private:
      * @param evt The gamepad event that occurred.
      * @param evt The gamepad event that occurred.
      * @param gamepad the gamepad the event occurred on
      * @param gamepad the gamepad the event occurred on
      */
      */
-    void gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad);
+    void gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex = 0);
 
 
     /**
     /**
      * Calls the specified Lua function using the given parameters.
      * Calls the specified Lua function using the given parameters.

+ 146 - 11
gameplay/src/Slider.cpp

@@ -1,17 +1,21 @@
 #include "Slider.h"
 #include "Slider.h"
+#include "Game.h"
 
 
 namespace gameplay
 namespace gameplay
 {
 {
 
 
 // Fraction of slider to scroll when mouse scrollwheel is used.
 // Fraction of slider to scroll when mouse scrollwheel is used.
-static const float SCROLL_FRACTION = 0.1f;
+static const float SCROLLWHEEL_FRACTION = 0.1f;
+// Fraction of slider to scroll for a delta of 1.0f when a gamepad is used.
+static const float GAMEPAD_FRACTION = 0.005f;
 // Distance that a slider must be moved before it starts consuming input events,
 // Distance that a slider must be moved before it starts consuming input events,
 // e.g. to prevent its parent container from scrolling at the same time.
 // e.g. to prevent its parent container from scrolling at the same time.
 static const float SLIDER_THRESHOLD = 5.0f;
 static const float SLIDER_THRESHOLD = 5.0f;
 
 
-Slider::Slider() : _min(0.0f), _max(0.0f), _step(0.0f), _value(0.0f), _minImage(NULL),
+Slider::Slider() : _min(0.0f), _max(0.0f), _step(0.0f), _value(0.0f), _delta(0.0f), _minImage(NULL),
     _maxImage(NULL), _trackImage(NULL), _markerImage(NULL), _valueTextVisible(false),
     _maxImage(NULL), _trackImage(NULL), _markerImage(NULL), _valueTextVisible(false),
-    _valueTextAlignment(Font::ALIGN_BOTTOM_HCENTER), _valueTextPrecision(0), _valueText("")
+    _valueTextAlignment(Font::ALIGN_BOTTOM_HCENTER), _valueTextPrecision(0), _valueText(""),
+    _selectButtonDown(false), _directionButtonDown(false), _gamepadValue(0.0f)
 {
 {
 }
 }
 
 
@@ -125,7 +129,7 @@ unsigned int Slider::getValueTextPrecision() const
 
 
 void Slider::addListener(Control::Listener* listener, int eventFlags)
 void Slider::addListener(Control::Listener* listener, int eventFlags)
 {
 {
-    if ((eventFlags & Listener::TEXT_CHANGED) == Listener::TEXT_CHANGED)
+    if ((eventFlags & Control::Listener::TEXT_CHANGED) == Control::Listener::TEXT_CHANGED)
     {
     {
         GP_ERROR("TEXT_CHANGED event is not applicable to Slider.");
         GP_ERROR("TEXT_CHANGED event is not applicable to Slider.");
     }
     }
@@ -168,7 +172,7 @@ bool Slider::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contac
             _value = _originalValue;
             _value = _originalValue;
             if (_value != oldValue)
             if (_value != oldValue)
             {
             {
-                notifyListeners(Listener::VALUE_CHANGED);
+                notifyListeners(Control::Listener::VALUE_CHANGED);
             }
             }
 
 
             _dirty = true;
             _dirty = true;
@@ -204,9 +208,7 @@ bool Slider::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contac
             float oldValue = _value;
             float oldValue = _value;
             _value = (markerPosition * (_max - _min)) + _min;
             _value = (markerPosition * (_max - _min)) + _min;
             if (_step > 0.0f)
             if (_step > 0.0f)
-            {
-                float stepDistance = _step / (_max - _min);
-            
+            {            
                 int numSteps = round(_value / _step);
                 int numSteps = round(_value / _step);
                 _value = _step * numSteps;
                 _value = _step * numSteps;
             }
             }
@@ -214,7 +216,7 @@ bool Slider::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contac
             // Call the callback if our value changed.
             // Call the callback if our value changed.
             if (_value != oldValue)
             if (_value != oldValue)
             {
             {
-                notifyListeners(Listener::VALUE_CHANGED);
+                notifyListeners(Control::Listener::VALUE_CHANGED);
             }
             }
             _dirty = true;
             _dirty = true;
         }
         }
@@ -255,7 +257,7 @@ bool Slider::mouseEvent(Mouse::MouseEvent evt, int x, int y, int wheelDelta)
             {
             {
                 float total = _max - _min;
                 float total = _max - _min;
                 float oldValue = _value;
                 float oldValue = _value;
-                _value += (total * SCROLL_FRACTION) * wheelDelta;
+                _value += (total * SCROLLWHEEL_FRACTION) * wheelDelta;
             
             
                 if (_value > _max)
                 if (_value > _max)
                     _value = _max;
                     _value = _max;
@@ -264,7 +266,7 @@ bool Slider::mouseEvent(Mouse::MouseEvent evt, int x, int y, int wheelDelta)
 
 
                 if (_value != oldValue)
                 if (_value != oldValue)
                 {
                 {
-                    notifyListeners(Listener::VALUE_CHANGED);
+                    notifyListeners(Control::Listener::VALUE_CHANGED);
                 }
                 }
 
 
                 _dirty = true;
                 _dirty = true;
@@ -280,6 +282,87 @@ bool Slider::mouseEvent(Mouse::MouseEvent evt, int x, int y, int wheelDelta)
     return false;
     return false;
 }
 }
 
 
+bool Slider::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex)
+{
+    bool eventConsumed = false;
+
+    if (_state == ACTIVE)
+    {
+        switch (evt)
+        {
+            case Gamepad::BUTTON_EVENT:
+            {
+                if (gamepad->isButtonDown(Gamepad::BUTTON_LEFT))
+                {
+                    _delta = -1.0f;
+                    _directionButtonDown = true;
+                }
+                else if (gamepad->isButtonDown(Gamepad::BUTTON_RIGHT))
+                {
+                    _delta = 1.0f;
+                    _directionButtonDown = true;
+                }
+                else if (_delta != 0.0f && _directionButtonDown)
+                {
+                    _delta = 0.0f;
+                    _directionButtonDown = false;
+                }
+
+                if (_step > 0.0f && _delta != 0.0f)
+                {
+                    _value += _step * _delta;
+                    _gamepadValue = _value - (_step * _delta * 0.49f);
+                    _delta *= 0.2f;
+                }
+
+                // A slider consumes all button events until it is no longer active.
+                eventConsumed = true;
+                _dirty = true;
+                break;
+            }
+            case Gamepad::JOYSTICK_EVENT:
+            {
+                // The left analog stick can be used to change a slider's value.
+                if (analogIndex == 0)
+                {
+                    Vector2 joy;
+                    gamepad->getJoystickValues(analogIndex, &joy);
+                    _gamepadValue = _value;
+                    _delta = joy.x;
+                    _dirty = true;
+                    eventConsumed = true;
+                }
+                break;
+            }
+        }
+    }
+
+    if (evt == Gamepad::BUTTON_EVENT && _delta == 0.0f)
+    {
+        if (gamepad->isButtonDown(Gamepad::BUTTON_A) ||
+            gamepad->isButtonDown(Gamepad::BUTTON_X))
+        {
+            _selectButtonDown = true;
+            eventConsumed |= _consumeInputEvents;
+        }
+        else if (_selectButtonDown && 
+                 !gamepad->isButtonDown(Gamepad::BUTTON_A) &&
+                 !gamepad->isButtonDown(Gamepad::BUTTON_X))
+        {
+            _selectButtonDown = false;
+
+            if (_state == FOCUS)
+                setState(ACTIVE);
+            else if (_state == ACTIVE)
+                setState(FOCUS);
+
+            eventConsumed |= _consumeInputEvents;
+        }
+    }    
+
+    return eventConsumed;
+}
+
 void Slider::update(const Control* container, const Vector2& offset)
 void Slider::update(const Control* container, const Vector2& offset)
 {
 {
     Label::update(container, offset);
     Label::update(container, offset);
@@ -292,6 +375,58 @@ void Slider::update(const Control* container, const Vector2& offset)
     char s[32];
     char s[32];
     sprintf(s, "%.*f", _valueTextPrecision, _value);
     sprintf(s, "%.*f", _valueTextPrecision, _value);
     _valueText = s;
     _valueText = s;
+
+    if (_delta != 0.0f)
+    {
+        float oldValue = _value;
+        float total = _max - _min;
+
+        if (_step > 0.0f)
+        {
+            _gamepadValue += (total * GAMEPAD_FRACTION) * _delta;
+            int numSteps = round(_gamepadValue / _step);
+            _value = _step * numSteps;
+        }
+        else
+        {
+            _value += (total * GAMEPAD_FRACTION) * _delta;
+        }
+            
+        if (_value > _max)
+            _value = _max;
+        else if (_value < _min)
+            _value = _min;
+
+        if (_value != oldValue)
+        {
+            notifyListeners(Control::Listener::VALUE_CHANGED);
+        }
+    }
+}
+
+void Slider::draw(SpriteBatch* spriteBatch, const Rectangle& clip, bool needsClear, bool cleared, float targetHeight)
+{
+    if (needsClear)
+    {
+        GL_ASSERT( glEnable(GL_SCISSOR_TEST) );
+        GL_ASSERT( glScissor(_clearBounds.x, targetHeight - _clearBounds.y - _clearBounds.height, _clearBounds.width, _clearBounds.height) );
+        Game::getInstance()->clear(Game::CLEAR_COLOR, Vector4::zero(), 1.0f, 0);
+        GL_ASSERT( glDisable(GL_SCISSOR_TEST) );
+    }
+
+    if (!_visible)
+        return;
+
+    spriteBatch->start();
+    drawBorder(spriteBatch, clip);
+    drawImages(spriteBatch, clip);
+    spriteBatch->finish();
+
+    drawText(clip);
+    if (_delta == 0.0f)
+    {
+        _dirty = false;
+    }
 }
 }
 
 
 void Slider::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)
 void Slider::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)

+ 25 - 0
gameplay/src/Slider.h

@@ -214,6 +214,19 @@ protected:
      */
      */
     bool mouseEvent(Mouse::MouseEvent evt, int x, int y, int wheelDelta);
     bool mouseEvent(Mouse::MouseEvent evt, int x, int y, int wheelDelta);
 
 
+    /**
+     * Gamepad callback on gamepad events.
+     *
+     * @see Control::gamepadEvent
+     */
+    bool gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex);
+
+    /**
+     * Slider overrides draw() so that it can avoid resetting the _dirty flag
+     * when a joystick is being used to change its value.
+     */
+    void draw(SpriteBatch* spriteBatch, const Rectangle& clip, bool needsClear, bool cleared, float targetHeight);
+
     /**
     /**
      * Draw the images associated with this control.
      * Draw the images associated with this control.
      *
      *
@@ -258,6 +271,11 @@ protected:
      */
      */
     float _value;
     float _value;
 
 
+    /**
+     * When a gamepad is in use, this stores how much to move the slider's value.
+     */
+    float _delta;
+
     /**
     /**
      * The X coordinate of the first touch event in a sequence.
      * The X coordinate of the first touch event in a sequence.
      */
      */
@@ -318,6 +336,13 @@ protected:
      */
      */
     std::string _valueText;
     std::string _valueText;
 
 
+    // Used by gamepads to toggle Slider state between FOCUS and ACTIVE.
+    bool _selectButtonDown;
+
+    bool _directionButtonDown;
+
+    float _gamepadValue;
+
 private:
 private:
 
 
     /**
     /**

+ 5 - 3
gameplay/src/TextBox.cpp

@@ -39,7 +39,7 @@ int TextBox::getLastKeypress()
 
 
 void TextBox::addListener(Control::Listener* listener, int eventFlags)
 void TextBox::addListener(Control::Listener* listener, int eventFlags)
 {
 {
-    if ((eventFlags & Listener::VALUE_CHANGED) == Listener::VALUE_CHANGED)
+    if ((eventFlags & Control::Listener::VALUE_CHANGED) == Control::Listener::VALUE_CHANGED)
     {
     {
         GP_ERROR("VALUE_CHANGED event is not applicable to TextBox.");
         GP_ERROR("VALUE_CHANGED event is not applicable to TextBox.");
     }
     }
@@ -142,7 +142,7 @@ bool TextBox::keyEvent(Keyboard::KeyEvent evt, int key)
                         font->getLocationAtIndex(_text.c_str(), _textBounds, fontSize, &_caretLocation, textIndex,
                         font->getLocationAtIndex(_text.c_str(), _textBounds, fontSize, &_caretLocation, textIndex,
                             textAlignment, true, rightToLeft);
                             textAlignment, true, rightToLeft);
                         _dirty = true;
                         _dirty = true;
-                        notifyListeners(Listener::TEXT_CHANGED);
+                        notifyListeners(Control::Listener::TEXT_CHANGED);
                         break;
                         break;
                     }
                     }
                     case Keyboard::KEY_LEFT_ARROW:
                     case Keyboard::KEY_LEFT_ARROW:
@@ -255,6 +255,8 @@ bool TextBox::keyEvent(Keyboard::KeyEvent evt, int key)
                     case Keyboard::KEY_ESCAPE:
                     case Keyboard::KEY_ESCAPE:
                         break;
                         break;
                     case Keyboard::KEY_TAB:
                     case Keyboard::KEY_TAB:
+                        // Allow tab to move the focus forward.
+                        return false;
                         break;
                         break;
                     default:
                     default:
                     {
                     {
@@ -303,7 +305,7 @@ bool TextBox::keyEvent(Keyboard::KeyEvent evt, int key)
                     break;
                     break;
                 }
                 }
 
 
-                notifyListeners(Listener::TEXT_CHANGED);
+                notifyListeners(Control::Listener::TEXT_CHANGED);
                 break;
                 break;
             }
             }
         }
         }

+ 1 - 1
gameplay/src/VerticalLayout.cpp

@@ -42,7 +42,7 @@ void VerticalLayout::update(const Container* container, const Vector2& offset)
 
 
     float yPosition = 0;
     float yPosition = 0;
 
 
-    std::vector<Control*> controls = container->getControls();
+    const std::vector<Control*>& controls = container->getControls();
 
 
     int i, end, iter;
     int i, end, iter;
     if (_bottomToTop)
     if (_bottomToTop)

+ 45 - 0
gameplay/src/lua/lua_Button.cpp

@@ -33,6 +33,7 @@ void luaRegister_Button()
         {"createAnimationFromBy", lua_Button_createAnimationFromBy},
         {"createAnimationFromBy", lua_Button_createAnimationFromBy},
         {"createAnimationFromTo", lua_Button_createAnimationFromTo},
         {"createAnimationFromTo", lua_Button_createAnimationFromTo},
         {"destroyAnimation", lua_Button_destroyAnimation},
         {"destroyAnimation", lua_Button_destroyAnimation},
+        {"getAbsoluteBounds", lua_Button_getAbsoluteBounds},
         {"getAlignment", lua_Button_getAlignment},
         {"getAlignment", lua_Button_getAlignment},
         {"getAnimation", lua_Button_getAnimation},
         {"getAnimation", lua_Button_getAnimation},
         {"getAnimationPropertyComponentCount", lua_Button_getAnimationPropertyComponentCount},
         {"getAnimationPropertyComponentCount", lua_Button_getAnimationPropertyComponentCount},
@@ -680,6 +681,50 @@ int lua_Button_destroyAnimation(lua_State* state)
     return 0;
     return 0;
 }
 }
 
 
+int lua_Button_getAbsoluteBounds(lua_State* state)
+{
+    // Get the number of parameters.
+    int paramCount = lua_gettop(state);
+
+    // Attempt to match the parameters to a valid binding.
+    switch (paramCount)
+    {
+        case 1:
+        {
+            if ((lua_type(state, 1) == LUA_TUSERDATA))
+            {
+                Button* instance = getInstance(state);
+                void* returnPtr = (void*)&(instance->getAbsoluteBounds());
+                if (returnPtr)
+                {
+                    gameplay::ScriptUtil::LuaObject* object = (gameplay::ScriptUtil::LuaObject*)lua_newuserdata(state, sizeof(gameplay::ScriptUtil::LuaObject));
+                    object->instance = returnPtr;
+                    object->owns = false;
+                    luaL_getmetatable(state, "Rectangle");
+                    lua_setmetatable(state, -2);
+                }
+                else
+                {
+                    lua_pushnil(state);
+                }
+
+                return 1;
+            }
+
+            lua_pushstring(state, "lua_Button_getAbsoluteBounds - Failed to match the given parameters to a valid function signature.");
+            lua_error(state);
+            break;
+        }
+        default:
+        {
+            lua_pushstring(state, "Invalid number of parameters (expected 1).");
+            lua_error(state);
+            break;
+        }
+    }
+    return 0;
+}
+
 int lua_Button_getAlignment(lua_State* state)
 int lua_Button_getAlignment(lua_State* state)
 {
 {
     // Get the number of parameters.
     // Get the number of parameters.

+ 1 - 0
gameplay/src/lua/lua_Button.h

@@ -13,6 +13,7 @@ int lua_Button_createAnimation(lua_State* state);
 int lua_Button_createAnimationFromBy(lua_State* state);
 int lua_Button_createAnimationFromBy(lua_State* state);
 int lua_Button_createAnimationFromTo(lua_State* state);
 int lua_Button_createAnimationFromTo(lua_State* state);
 int lua_Button_destroyAnimation(lua_State* state);
 int lua_Button_destroyAnimation(lua_State* state);
+int lua_Button_getAbsoluteBounds(lua_State* state);
 int lua_Button_getAlignment(lua_State* state);
 int lua_Button_getAlignment(lua_State* state);
 int lua_Button_getAnimation(lua_State* state);
 int lua_Button_getAnimation(lua_State* state);
 int lua_Button_getAnimationPropertyComponentCount(lua_State* state);
 int lua_Button_getAnimationPropertyComponentCount(lua_State* state);

+ 45 - 0
gameplay/src/lua/lua_CheckBox.cpp

@@ -34,6 +34,7 @@ void luaRegister_CheckBox()
         {"createAnimationFromBy", lua_CheckBox_createAnimationFromBy},
         {"createAnimationFromBy", lua_CheckBox_createAnimationFromBy},
         {"createAnimationFromTo", lua_CheckBox_createAnimationFromTo},
         {"createAnimationFromTo", lua_CheckBox_createAnimationFromTo},
         {"destroyAnimation", lua_CheckBox_destroyAnimation},
         {"destroyAnimation", lua_CheckBox_destroyAnimation},
+        {"getAbsoluteBounds", lua_CheckBox_getAbsoluteBounds},
         {"getAlignment", lua_CheckBox_getAlignment},
         {"getAlignment", lua_CheckBox_getAlignment},
         {"getAnimation", lua_CheckBox_getAnimation},
         {"getAnimation", lua_CheckBox_getAnimation},
         {"getAnimationPropertyComponentCount", lua_CheckBox_getAnimationPropertyComponentCount},
         {"getAnimationPropertyComponentCount", lua_CheckBox_getAnimationPropertyComponentCount},
@@ -686,6 +687,50 @@ int lua_CheckBox_destroyAnimation(lua_State* state)
     return 0;
     return 0;
 }
 }
 
 
+int lua_CheckBox_getAbsoluteBounds(lua_State* state)
+{
+    // Get the number of parameters.
+    int paramCount = lua_gettop(state);
+
+    // Attempt to match the parameters to a valid binding.
+    switch (paramCount)
+    {
+        case 1:
+        {
+            if ((lua_type(state, 1) == LUA_TUSERDATA))
+            {
+                CheckBox* instance = getInstance(state);
+                void* returnPtr = (void*)&(instance->getAbsoluteBounds());
+                if (returnPtr)
+                {
+                    gameplay::ScriptUtil::LuaObject* object = (gameplay::ScriptUtil::LuaObject*)lua_newuserdata(state, sizeof(gameplay::ScriptUtil::LuaObject));
+                    object->instance = returnPtr;
+                    object->owns = false;
+                    luaL_getmetatable(state, "Rectangle");
+                    lua_setmetatable(state, -2);
+                }
+                else
+                {
+                    lua_pushnil(state);
+                }
+
+                return 1;
+            }
+
+            lua_pushstring(state, "lua_CheckBox_getAbsoluteBounds - Failed to match the given parameters to a valid function signature.");
+            lua_error(state);
+            break;
+        }
+        default:
+        {
+            lua_pushstring(state, "Invalid number of parameters (expected 1).");
+            lua_error(state);
+            break;
+        }
+    }
+    return 0;
+}
+
 int lua_CheckBox_getAlignment(lua_State* state)
 int lua_CheckBox_getAlignment(lua_State* state)
 {
 {
     // Get the number of parameters.
     // Get the number of parameters.

+ 1 - 0
gameplay/src/lua/lua_CheckBox.h

@@ -13,6 +13,7 @@ int lua_CheckBox_createAnimation(lua_State* state);
 int lua_CheckBox_createAnimationFromBy(lua_State* state);
 int lua_CheckBox_createAnimationFromBy(lua_State* state);
 int lua_CheckBox_createAnimationFromTo(lua_State* state);
 int lua_CheckBox_createAnimationFromTo(lua_State* state);
 int lua_CheckBox_destroyAnimation(lua_State* state);
 int lua_CheckBox_destroyAnimation(lua_State* state);
+int lua_CheckBox_getAbsoluteBounds(lua_State* state);
 int lua_CheckBox_getAlignment(lua_State* state);
 int lua_CheckBox_getAlignment(lua_State* state);
 int lua_CheckBox_getAnimation(lua_State* state);
 int lua_CheckBox_getAnimation(lua_State* state);
 int lua_CheckBox_getAnimationPropertyComponentCount(lua_State* state);
 int lua_CheckBox_getAnimationPropertyComponentCount(lua_State* state);

+ 45 - 0
gameplay/src/lua/lua_Container.cpp

@@ -46,6 +46,7 @@ void luaRegister_Container()
         {"createAnimationFromBy", lua_Container_createAnimationFromBy},
         {"createAnimationFromBy", lua_Container_createAnimationFromBy},
         {"createAnimationFromTo", lua_Container_createAnimationFromTo},
         {"createAnimationFromTo", lua_Container_createAnimationFromTo},
         {"destroyAnimation", lua_Container_destroyAnimation},
         {"destroyAnimation", lua_Container_destroyAnimation},
+        {"getAbsoluteBounds", lua_Container_getAbsoluteBounds},
         {"getAlignment", lua_Container_getAlignment},
         {"getAlignment", lua_Container_getAlignment},
         {"getAnimation", lua_Container_getAnimation},
         {"getAnimation", lua_Container_getAnimation},
         {"getAnimationPropertyComponentCount", lua_Container_getAnimationPropertyComponentCount},
         {"getAnimationPropertyComponentCount", lua_Container_getAnimationPropertyComponentCount},
@@ -747,6 +748,50 @@ int lua_Container_destroyAnimation(lua_State* state)
     return 0;
     return 0;
 }
 }
 
 
+int lua_Container_getAbsoluteBounds(lua_State* state)
+{
+    // Get the number of parameters.
+    int paramCount = lua_gettop(state);
+
+    // Attempt to match the parameters to a valid binding.
+    switch (paramCount)
+    {
+        case 1:
+        {
+            if ((lua_type(state, 1) == LUA_TUSERDATA))
+            {
+                Container* instance = getInstance(state);
+                void* returnPtr = (void*)&(instance->getAbsoluteBounds());
+                if (returnPtr)
+                {
+                    gameplay::ScriptUtil::LuaObject* object = (gameplay::ScriptUtil::LuaObject*)lua_newuserdata(state, sizeof(gameplay::ScriptUtil::LuaObject));
+                    object->instance = returnPtr;
+                    object->owns = false;
+                    luaL_getmetatable(state, "Rectangle");
+                    lua_setmetatable(state, -2);
+                }
+                else
+                {
+                    lua_pushnil(state);
+                }
+
+                return 1;
+            }
+
+            lua_pushstring(state, "lua_Container_getAbsoluteBounds - Failed to match the given parameters to a valid function signature.");
+            lua_error(state);
+            break;
+        }
+        default:
+        {
+            lua_pushstring(state, "Invalid number of parameters (expected 1).");
+            lua_error(state);
+            break;
+        }
+    }
+    return 0;
+}
+
 int lua_Container_getAlignment(lua_State* state)
 int lua_Container_getAlignment(lua_State* state)
 {
 {
     // Get the number of parameters.
     // Get the number of parameters.

+ 1 - 0
gameplay/src/lua/lua_Container.h

@@ -14,6 +14,7 @@ int lua_Container_createAnimation(lua_State* state);
 int lua_Container_createAnimationFromBy(lua_State* state);
 int lua_Container_createAnimationFromBy(lua_State* state);
 int lua_Container_createAnimationFromTo(lua_State* state);
 int lua_Container_createAnimationFromTo(lua_State* state);
 int lua_Container_destroyAnimation(lua_State* state);
 int lua_Container_destroyAnimation(lua_State* state);
+int lua_Container_getAbsoluteBounds(lua_State* state);
 int lua_Container_getAlignment(lua_State* state);
 int lua_Container_getAlignment(lua_State* state);
 int lua_Container_getAnimation(lua_State* state);
 int lua_Container_getAnimation(lua_State* state);
 int lua_Container_getAnimationPropertyComponentCount(lua_State* state);
 int lua_Container_getAnimationPropertyComponentCount(lua_State* state);

+ 45 - 0
gameplay/src/lua/lua_Control.cpp

@@ -30,6 +30,7 @@ void luaRegister_Control()
         {"createAnimationFromBy", lua_Control_createAnimationFromBy},
         {"createAnimationFromBy", lua_Control_createAnimationFromBy},
         {"createAnimationFromTo", lua_Control_createAnimationFromTo},
         {"createAnimationFromTo", lua_Control_createAnimationFromTo},
         {"destroyAnimation", lua_Control_destroyAnimation},
         {"destroyAnimation", lua_Control_destroyAnimation},
+        {"getAbsoluteBounds", lua_Control_getAbsoluteBounds},
         {"getAlignment", lua_Control_getAlignment},
         {"getAlignment", lua_Control_getAlignment},
         {"getAnimation", lua_Control_getAnimation},
         {"getAnimation", lua_Control_getAnimation},
         {"getAnimationPropertyComponentCount", lua_Control_getAnimationPropertyComponentCount},
         {"getAnimationPropertyComponentCount", lua_Control_getAnimationPropertyComponentCount},
@@ -675,6 +676,50 @@ int lua_Control_destroyAnimation(lua_State* state)
     return 0;
     return 0;
 }
 }
 
 
+int lua_Control_getAbsoluteBounds(lua_State* state)
+{
+    // Get the number of parameters.
+    int paramCount = lua_gettop(state);
+
+    // Attempt to match the parameters to a valid binding.
+    switch (paramCount)
+    {
+        case 1:
+        {
+            if ((lua_type(state, 1) == LUA_TUSERDATA))
+            {
+                Control* instance = getInstance(state);
+                void* returnPtr = (void*)&(instance->getAbsoluteBounds());
+                if (returnPtr)
+                {
+                    gameplay::ScriptUtil::LuaObject* object = (gameplay::ScriptUtil::LuaObject*)lua_newuserdata(state, sizeof(gameplay::ScriptUtil::LuaObject));
+                    object->instance = returnPtr;
+                    object->owns = false;
+                    luaL_getmetatable(state, "Rectangle");
+                    lua_setmetatable(state, -2);
+                }
+                else
+                {
+                    lua_pushnil(state);
+                }
+
+                return 1;
+            }
+
+            lua_pushstring(state, "lua_Control_getAbsoluteBounds - Failed to match the given parameters to a valid function signature.");
+            lua_error(state);
+            break;
+        }
+        default:
+        {
+            lua_pushstring(state, "Invalid number of parameters (expected 1).");
+            lua_error(state);
+            break;
+        }
+    }
+    return 0;
+}
+
 int lua_Control_getAlignment(lua_State* state)
 int lua_Control_getAlignment(lua_State* state)
 {
 {
     // Get the number of parameters.
     // Get the number of parameters.

+ 1 - 0
gameplay/src/lua/lua_Control.h

@@ -13,6 +13,7 @@ int lua_Control_createAnimation(lua_State* state);
 int lua_Control_createAnimationFromBy(lua_State* state);
 int lua_Control_createAnimationFromBy(lua_State* state);
 int lua_Control_createAnimationFromTo(lua_State* state);
 int lua_Control_createAnimationFromTo(lua_State* state);
 int lua_Control_destroyAnimation(lua_State* state);
 int lua_Control_destroyAnimation(lua_State* state);
+int lua_Control_getAbsoluteBounds(lua_State* state);
 int lua_Control_getAlignment(lua_State* state);
 int lua_Control_getAlignment(lua_State* state);
 int lua_Control_getAnimation(lua_State* state);
 int lua_Control_getAnimation(lua_State* state);
 int lua_Control_getAnimationPropertyComponentCount(lua_State* state);
 int lua_Control_getAnimationPropertyComponentCount(lua_State* state);

+ 45 - 0
gameplay/src/lua/lua_Form.cpp

@@ -50,6 +50,7 @@ void luaRegister_Form()
         {"createAnimationFromTo", lua_Form_createAnimationFromTo},
         {"createAnimationFromTo", lua_Form_createAnimationFromTo},
         {"destroyAnimation", lua_Form_destroyAnimation},
         {"destroyAnimation", lua_Form_destroyAnimation},
         {"draw", lua_Form_draw},
         {"draw", lua_Form_draw},
+        {"getAbsoluteBounds", lua_Form_getAbsoluteBounds},
         {"getAlignment", lua_Form_getAlignment},
         {"getAlignment", lua_Form_getAlignment},
         {"getAnimation", lua_Form_getAnimation},
         {"getAnimation", lua_Form_getAnimation},
         {"getAnimationPropertyComponentCount", lua_Form_getAnimationPropertyComponentCount},
         {"getAnimationPropertyComponentCount", lua_Form_getAnimationPropertyComponentCount},
@@ -787,6 +788,50 @@ int lua_Form_draw(lua_State* state)
     return 0;
     return 0;
 }
 }
 
 
+int lua_Form_getAbsoluteBounds(lua_State* state)
+{
+    // Get the number of parameters.
+    int paramCount = lua_gettop(state);
+
+    // Attempt to match the parameters to a valid binding.
+    switch (paramCount)
+    {
+        case 1:
+        {
+            if ((lua_type(state, 1) == LUA_TUSERDATA))
+            {
+                Form* instance = getInstance(state);
+                void* returnPtr = (void*)&(instance->getAbsoluteBounds());
+                if (returnPtr)
+                {
+                    gameplay::ScriptUtil::LuaObject* object = (gameplay::ScriptUtil::LuaObject*)lua_newuserdata(state, sizeof(gameplay::ScriptUtil::LuaObject));
+                    object->instance = returnPtr;
+                    object->owns = false;
+                    luaL_getmetatable(state, "Rectangle");
+                    lua_setmetatable(state, -2);
+                }
+                else
+                {
+                    lua_pushnil(state);
+                }
+
+                return 1;
+            }
+
+            lua_pushstring(state, "lua_Form_getAbsoluteBounds - Failed to match the given parameters to a valid function signature.");
+            lua_error(state);
+            break;
+        }
+        default:
+        {
+            lua_pushstring(state, "Invalid number of parameters (expected 1).");
+            lua_error(state);
+            break;
+        }
+    }
+    return 0;
+}
+
 int lua_Form_getAlignment(lua_State* state)
 int lua_Form_getAlignment(lua_State* state)
 {
 {
     // Get the number of parameters.
     // Get the number of parameters.

+ 1 - 0
gameplay/src/lua/lua_Form.h

@@ -15,6 +15,7 @@ int lua_Form_createAnimationFromBy(lua_State* state);
 int lua_Form_createAnimationFromTo(lua_State* state);
 int lua_Form_createAnimationFromTo(lua_State* state);
 int lua_Form_destroyAnimation(lua_State* state);
 int lua_Form_destroyAnimation(lua_State* state);
 int lua_Form_draw(lua_State* state);
 int lua_Form_draw(lua_State* state);
+int lua_Form_getAbsoluteBounds(lua_State* state);
 int lua_Form_getAlignment(lua_State* state);
 int lua_Form_getAlignment(lua_State* state);
 int lua_Form_getAnimation(lua_State* state);
 int lua_Form_getAnimation(lua_State* state);
 int lua_Form_getAnimationPropertyComponentCount(lua_State* state);
 int lua_Form_getAnimationPropertyComponentCount(lua_State* state);

+ 33 - 1
gameplay/src/lua/lua_Game.cpp

@@ -404,9 +404,41 @@ int lua_Game_gamepadEvent(lua_State* state)
             lua_error(state);
             lua_error(state);
             break;
             break;
         }
         }
+        case 4:
+        {
+            if ((lua_type(state, 1) == LUA_TUSERDATA) &&
+                (lua_type(state, 2) == LUA_TSTRING || lua_type(state, 2) == LUA_TNIL) &&
+                (lua_type(state, 3) == LUA_TUSERDATA || lua_type(state, 3) == LUA_TTABLE || lua_type(state, 3) == LUA_TNIL) &&
+                lua_type(state, 4) == LUA_TNUMBER)
+            {
+                // Get parameter 1 off the stack.
+                Gamepad::GamepadEvent param1 = (Gamepad::GamepadEvent)lua_enumFromString_GamepadGamepadEvent(luaL_checkstring(state, 2));
+
+                // Get parameter 2 off the stack.
+                bool param2Valid;
+                gameplay::ScriptUtil::LuaArray<Gamepad> param2 = gameplay::ScriptUtil::getObjectPointer<Gamepad>(3, "Gamepad", false, &param2Valid);
+                if (!param2Valid)
+                {
+                    lua_pushstring(state, "Failed to convert parameter 2 to type 'Gamepad'.");
+                    lua_error(state);
+                }
+
+                // Get parameter 3 off the stack.
+                unsigned int param3 = (unsigned int)luaL_checkunsigned(state, 4);
+
+                Game* instance = getInstance(state);
+                instance->gamepadEvent(param1, param2, param3);
+                
+                return 0;
+            }
+
+            lua_pushstring(state, "lua_Game_gamepadEvent - Failed to match the given parameters to a valid function signature.");
+            lua_error(state);
+            break;
+        }
         default:
         default:
         {
         {
-            lua_pushstring(state, "Invalid number of parameters (expected 3).");
+            lua_pushstring(state, "Invalid number of parameters (expected 3 or 4).");
             lua_error(state);
             lua_error(state);
             break;
             break;
         }
         }

+ 45 - 0
gameplay/src/lua/lua_ImageControl.cpp

@@ -31,6 +31,7 @@ void luaRegister_ImageControl()
         {"createAnimationFromBy", lua_ImageControl_createAnimationFromBy},
         {"createAnimationFromBy", lua_ImageControl_createAnimationFromBy},
         {"createAnimationFromTo", lua_ImageControl_createAnimationFromTo},
         {"createAnimationFromTo", lua_ImageControl_createAnimationFromTo},
         {"destroyAnimation", lua_ImageControl_destroyAnimation},
         {"destroyAnimation", lua_ImageControl_destroyAnimation},
+        {"getAbsoluteBounds", lua_ImageControl_getAbsoluteBounds},
         {"getAlignment", lua_ImageControl_getAlignment},
         {"getAlignment", lua_ImageControl_getAlignment},
         {"getAnimation", lua_ImageControl_getAnimation},
         {"getAnimation", lua_ImageControl_getAnimation},
         {"getAnimationPropertyComponentCount", lua_ImageControl_getAnimationPropertyComponentCount},
         {"getAnimationPropertyComponentCount", lua_ImageControl_getAnimationPropertyComponentCount},
@@ -682,6 +683,50 @@ int lua_ImageControl_destroyAnimation(lua_State* state)
     return 0;
     return 0;
 }
 }
 
 
+int lua_ImageControl_getAbsoluteBounds(lua_State* state)
+{
+    // Get the number of parameters.
+    int paramCount = lua_gettop(state);
+
+    // Attempt to match the parameters to a valid binding.
+    switch (paramCount)
+    {
+        case 1:
+        {
+            if ((lua_type(state, 1) == LUA_TUSERDATA))
+            {
+                ImageControl* instance = getInstance(state);
+                void* returnPtr = (void*)&(instance->getAbsoluteBounds());
+                if (returnPtr)
+                {
+                    gameplay::ScriptUtil::LuaObject* object = (gameplay::ScriptUtil::LuaObject*)lua_newuserdata(state, sizeof(gameplay::ScriptUtil::LuaObject));
+                    object->instance = returnPtr;
+                    object->owns = false;
+                    luaL_getmetatable(state, "Rectangle");
+                    lua_setmetatable(state, -2);
+                }
+                else
+                {
+                    lua_pushnil(state);
+                }
+
+                return 1;
+            }
+
+            lua_pushstring(state, "lua_ImageControl_getAbsoluteBounds - Failed to match the given parameters to a valid function signature.");
+            lua_error(state);
+            break;
+        }
+        default:
+        {
+            lua_pushstring(state, "Invalid number of parameters (expected 1).");
+            lua_error(state);
+            break;
+        }
+    }
+    return 0;
+}
+
 int lua_ImageControl_getAlignment(lua_State* state)
 int lua_ImageControl_getAlignment(lua_State* state)
 {
 {
     // Get the number of parameters.
     // Get the number of parameters.

+ 1 - 0
gameplay/src/lua/lua_ImageControl.h

@@ -13,6 +13,7 @@ int lua_ImageControl_createAnimation(lua_State* state);
 int lua_ImageControl_createAnimationFromBy(lua_State* state);
 int lua_ImageControl_createAnimationFromBy(lua_State* state);
 int lua_ImageControl_createAnimationFromTo(lua_State* state);
 int lua_ImageControl_createAnimationFromTo(lua_State* state);
 int lua_ImageControl_destroyAnimation(lua_State* state);
 int lua_ImageControl_destroyAnimation(lua_State* state);
+int lua_ImageControl_getAbsoluteBounds(lua_State* state);
 int lua_ImageControl_getAlignment(lua_State* state);
 int lua_ImageControl_getAlignment(lua_State* state);
 int lua_ImageControl_getAnimation(lua_State* state);
 int lua_ImageControl_getAnimation(lua_State* state);
 int lua_ImageControl_getAnimationPropertyComponentCount(lua_State* state);
 int lua_ImageControl_getAnimationPropertyComponentCount(lua_State* state);

+ 45 - 0
gameplay/src/lua/lua_Joystick.cpp

@@ -31,6 +31,7 @@ void luaRegister_Joystick()
         {"createAnimationFromBy", lua_Joystick_createAnimationFromBy},
         {"createAnimationFromBy", lua_Joystick_createAnimationFromBy},
         {"createAnimationFromTo", lua_Joystick_createAnimationFromTo},
         {"createAnimationFromTo", lua_Joystick_createAnimationFromTo},
         {"destroyAnimation", lua_Joystick_destroyAnimation},
         {"destroyAnimation", lua_Joystick_destroyAnimation},
+        {"getAbsoluteBounds", lua_Joystick_getAbsoluteBounds},
         {"getAlignment", lua_Joystick_getAlignment},
         {"getAlignment", lua_Joystick_getAlignment},
         {"getAnimation", lua_Joystick_getAnimation},
         {"getAnimation", lua_Joystick_getAnimation},
         {"getAnimationPropertyComponentCount", lua_Joystick_getAnimationPropertyComponentCount},
         {"getAnimationPropertyComponentCount", lua_Joystick_getAnimationPropertyComponentCount},
@@ -685,6 +686,50 @@ int lua_Joystick_destroyAnimation(lua_State* state)
     return 0;
     return 0;
 }
 }
 
 
+int lua_Joystick_getAbsoluteBounds(lua_State* state)
+{
+    // Get the number of parameters.
+    int paramCount = lua_gettop(state);
+
+    // Attempt to match the parameters to a valid binding.
+    switch (paramCount)
+    {
+        case 1:
+        {
+            if ((lua_type(state, 1) == LUA_TUSERDATA))
+            {
+                Joystick* instance = getInstance(state);
+                void* returnPtr = (void*)&(instance->getAbsoluteBounds());
+                if (returnPtr)
+                {
+                    gameplay::ScriptUtil::LuaObject* object = (gameplay::ScriptUtil::LuaObject*)lua_newuserdata(state, sizeof(gameplay::ScriptUtil::LuaObject));
+                    object->instance = returnPtr;
+                    object->owns = false;
+                    luaL_getmetatable(state, "Rectangle");
+                    lua_setmetatable(state, -2);
+                }
+                else
+                {
+                    lua_pushnil(state);
+                }
+
+                return 1;
+            }
+
+            lua_pushstring(state, "lua_Joystick_getAbsoluteBounds - Failed to match the given parameters to a valid function signature.");
+            lua_error(state);
+            break;
+        }
+        default:
+        {
+            lua_pushstring(state, "Invalid number of parameters (expected 1).");
+            lua_error(state);
+            break;
+        }
+    }
+    return 0;
+}
+
 int lua_Joystick_getAlignment(lua_State* state)
 int lua_Joystick_getAlignment(lua_State* state)
 {
 {
     // Get the number of parameters.
     // Get the number of parameters.

+ 1 - 0
gameplay/src/lua/lua_Joystick.h

@@ -13,6 +13,7 @@ int lua_Joystick_createAnimation(lua_State* state);
 int lua_Joystick_createAnimationFromBy(lua_State* state);
 int lua_Joystick_createAnimationFromBy(lua_State* state);
 int lua_Joystick_createAnimationFromTo(lua_State* state);
 int lua_Joystick_createAnimationFromTo(lua_State* state);
 int lua_Joystick_destroyAnimation(lua_State* state);
 int lua_Joystick_destroyAnimation(lua_State* state);
+int lua_Joystick_getAbsoluteBounds(lua_State* state);
 int lua_Joystick_getAlignment(lua_State* state);
 int lua_Joystick_getAlignment(lua_State* state);
 int lua_Joystick_getAnimation(lua_State* state);
 int lua_Joystick_getAnimation(lua_State* state);
 int lua_Joystick_getAnimationPropertyComponentCount(lua_State* state);
 int lua_Joystick_getAnimationPropertyComponentCount(lua_State* state);

+ 45 - 0
gameplay/src/lua/lua_Label.cpp

@@ -31,6 +31,7 @@ void luaRegister_Label()
         {"createAnimationFromBy", lua_Label_createAnimationFromBy},
         {"createAnimationFromBy", lua_Label_createAnimationFromBy},
         {"createAnimationFromTo", lua_Label_createAnimationFromTo},
         {"createAnimationFromTo", lua_Label_createAnimationFromTo},
         {"destroyAnimation", lua_Label_destroyAnimation},
         {"destroyAnimation", lua_Label_destroyAnimation},
+        {"getAbsoluteBounds", lua_Label_getAbsoluteBounds},
         {"getAlignment", lua_Label_getAlignment},
         {"getAlignment", lua_Label_getAlignment},
         {"getAnimation", lua_Label_getAnimation},
         {"getAnimation", lua_Label_getAnimation},
         {"getAnimationPropertyComponentCount", lua_Label_getAnimationPropertyComponentCount},
         {"getAnimationPropertyComponentCount", lua_Label_getAnimationPropertyComponentCount},
@@ -679,6 +680,50 @@ int lua_Label_destroyAnimation(lua_State* state)
     return 0;
     return 0;
 }
 }
 
 
+int lua_Label_getAbsoluteBounds(lua_State* state)
+{
+    // Get the number of parameters.
+    int paramCount = lua_gettop(state);
+
+    // Attempt to match the parameters to a valid binding.
+    switch (paramCount)
+    {
+        case 1:
+        {
+            if ((lua_type(state, 1) == LUA_TUSERDATA))
+            {
+                Label* instance = getInstance(state);
+                void* returnPtr = (void*)&(instance->getAbsoluteBounds());
+                if (returnPtr)
+                {
+                    gameplay::ScriptUtil::LuaObject* object = (gameplay::ScriptUtil::LuaObject*)lua_newuserdata(state, sizeof(gameplay::ScriptUtil::LuaObject));
+                    object->instance = returnPtr;
+                    object->owns = false;
+                    luaL_getmetatable(state, "Rectangle");
+                    lua_setmetatable(state, -2);
+                }
+                else
+                {
+                    lua_pushnil(state);
+                }
+
+                return 1;
+            }
+
+            lua_pushstring(state, "lua_Label_getAbsoluteBounds - Failed to match the given parameters to a valid function signature.");
+            lua_error(state);
+            break;
+        }
+        default:
+        {
+            lua_pushstring(state, "Invalid number of parameters (expected 1).");
+            lua_error(state);
+            break;
+        }
+    }
+    return 0;
+}
+
 int lua_Label_getAlignment(lua_State* state)
 int lua_Label_getAlignment(lua_State* state)
 {
 {
     // Get the number of parameters.
     // Get the number of parameters.

+ 1 - 0
gameplay/src/lua/lua_Label.h

@@ -13,6 +13,7 @@ int lua_Label_createAnimation(lua_State* state);
 int lua_Label_createAnimationFromBy(lua_State* state);
 int lua_Label_createAnimationFromBy(lua_State* state);
 int lua_Label_createAnimationFromTo(lua_State* state);
 int lua_Label_createAnimationFromTo(lua_State* state);
 int lua_Label_destroyAnimation(lua_State* state);
 int lua_Label_destroyAnimation(lua_State* state);
+int lua_Label_getAbsoluteBounds(lua_State* state);
 int lua_Label_getAlignment(lua_State* state);
 int lua_Label_getAlignment(lua_State* state);
 int lua_Label_getAnimation(lua_State* state);
 int lua_Label_getAnimation(lua_State* state);
 int lua_Label_getAnimationPropertyComponentCount(lua_State* state);
 int lua_Label_getAnimationPropertyComponentCount(lua_State* state);

+ 45 - 0
gameplay/src/lua/lua_RadioButton.cpp

@@ -34,6 +34,7 @@ void luaRegister_RadioButton()
         {"createAnimationFromBy", lua_RadioButton_createAnimationFromBy},
         {"createAnimationFromBy", lua_RadioButton_createAnimationFromBy},
         {"createAnimationFromTo", lua_RadioButton_createAnimationFromTo},
         {"createAnimationFromTo", lua_RadioButton_createAnimationFromTo},
         {"destroyAnimation", lua_RadioButton_destroyAnimation},
         {"destroyAnimation", lua_RadioButton_destroyAnimation},
+        {"getAbsoluteBounds", lua_RadioButton_getAbsoluteBounds},
         {"getAlignment", lua_RadioButton_getAlignment},
         {"getAlignment", lua_RadioButton_getAlignment},
         {"getAnimation", lua_RadioButton_getAnimation},
         {"getAnimation", lua_RadioButton_getAnimation},
         {"getAnimationPropertyComponentCount", lua_RadioButton_getAnimationPropertyComponentCount},
         {"getAnimationPropertyComponentCount", lua_RadioButton_getAnimationPropertyComponentCount},
@@ -688,6 +689,50 @@ int lua_RadioButton_destroyAnimation(lua_State* state)
     return 0;
     return 0;
 }
 }
 
 
+int lua_RadioButton_getAbsoluteBounds(lua_State* state)
+{
+    // Get the number of parameters.
+    int paramCount = lua_gettop(state);
+
+    // Attempt to match the parameters to a valid binding.
+    switch (paramCount)
+    {
+        case 1:
+        {
+            if ((lua_type(state, 1) == LUA_TUSERDATA))
+            {
+                RadioButton* instance = getInstance(state);
+                void* returnPtr = (void*)&(instance->getAbsoluteBounds());
+                if (returnPtr)
+                {
+                    gameplay::ScriptUtil::LuaObject* object = (gameplay::ScriptUtil::LuaObject*)lua_newuserdata(state, sizeof(gameplay::ScriptUtil::LuaObject));
+                    object->instance = returnPtr;
+                    object->owns = false;
+                    luaL_getmetatable(state, "Rectangle");
+                    lua_setmetatable(state, -2);
+                }
+                else
+                {
+                    lua_pushnil(state);
+                }
+
+                return 1;
+            }
+
+            lua_pushstring(state, "lua_RadioButton_getAbsoluteBounds - Failed to match the given parameters to a valid function signature.");
+            lua_error(state);
+            break;
+        }
+        default:
+        {
+            lua_pushstring(state, "Invalid number of parameters (expected 1).");
+            lua_error(state);
+            break;
+        }
+    }
+    return 0;
+}
+
 int lua_RadioButton_getAlignment(lua_State* state)
 int lua_RadioButton_getAlignment(lua_State* state)
 {
 {
     // Get the number of parameters.
     // Get the number of parameters.

+ 1 - 0
gameplay/src/lua/lua_RadioButton.h

@@ -13,6 +13,7 @@ int lua_RadioButton_createAnimation(lua_State* state);
 int lua_RadioButton_createAnimationFromBy(lua_State* state);
 int lua_RadioButton_createAnimationFromBy(lua_State* state);
 int lua_RadioButton_createAnimationFromTo(lua_State* state);
 int lua_RadioButton_createAnimationFromTo(lua_State* state);
 int lua_RadioButton_destroyAnimation(lua_State* state);
 int lua_RadioButton_destroyAnimation(lua_State* state);
+int lua_RadioButton_getAbsoluteBounds(lua_State* state);
 int lua_RadioButton_getAlignment(lua_State* state);
 int lua_RadioButton_getAlignment(lua_State* state);
 int lua_RadioButton_getAnimation(lua_State* state);
 int lua_RadioButton_getAnimation(lua_State* state);
 int lua_RadioButton_getAnimationPropertyComponentCount(lua_State* state);
 int lua_RadioButton_getAnimationPropertyComponentCount(lua_State* state);

+ 45 - 0
gameplay/src/lua/lua_Slider.cpp

@@ -32,6 +32,7 @@ void luaRegister_Slider()
         {"createAnimationFromBy", lua_Slider_createAnimationFromBy},
         {"createAnimationFromBy", lua_Slider_createAnimationFromBy},
         {"createAnimationFromTo", lua_Slider_createAnimationFromTo},
         {"createAnimationFromTo", lua_Slider_createAnimationFromTo},
         {"destroyAnimation", lua_Slider_destroyAnimation},
         {"destroyAnimation", lua_Slider_destroyAnimation},
+        {"getAbsoluteBounds", lua_Slider_getAbsoluteBounds},
         {"getAlignment", lua_Slider_getAlignment},
         {"getAlignment", lua_Slider_getAlignment},
         {"getAnimation", lua_Slider_getAnimation},
         {"getAnimation", lua_Slider_getAnimation},
         {"getAnimationPropertyComponentCount", lua_Slider_getAnimationPropertyComponentCount},
         {"getAnimationPropertyComponentCount", lua_Slider_getAnimationPropertyComponentCount},
@@ -694,6 +695,50 @@ int lua_Slider_destroyAnimation(lua_State* state)
     return 0;
     return 0;
 }
 }
 
 
+int lua_Slider_getAbsoluteBounds(lua_State* state)
+{
+    // Get the number of parameters.
+    int paramCount = lua_gettop(state);
+
+    // Attempt to match the parameters to a valid binding.
+    switch (paramCount)
+    {
+        case 1:
+        {
+            if ((lua_type(state, 1) == LUA_TUSERDATA))
+            {
+                Slider* instance = getInstance(state);
+                void* returnPtr = (void*)&(instance->getAbsoluteBounds());
+                if (returnPtr)
+                {
+                    gameplay::ScriptUtil::LuaObject* object = (gameplay::ScriptUtil::LuaObject*)lua_newuserdata(state, sizeof(gameplay::ScriptUtil::LuaObject));
+                    object->instance = returnPtr;
+                    object->owns = false;
+                    luaL_getmetatable(state, "Rectangle");
+                    lua_setmetatable(state, -2);
+                }
+                else
+                {
+                    lua_pushnil(state);
+                }
+
+                return 1;
+            }
+
+            lua_pushstring(state, "lua_Slider_getAbsoluteBounds - Failed to match the given parameters to a valid function signature.");
+            lua_error(state);
+            break;
+        }
+        default:
+        {
+            lua_pushstring(state, "Invalid number of parameters (expected 1).");
+            lua_error(state);
+            break;
+        }
+    }
+    return 0;
+}
+
 int lua_Slider_getAlignment(lua_State* state)
 int lua_Slider_getAlignment(lua_State* state)
 {
 {
     // Get the number of parameters.
     // Get the number of parameters.

+ 1 - 0
gameplay/src/lua/lua_Slider.h

@@ -13,6 +13,7 @@ int lua_Slider_createAnimation(lua_State* state);
 int lua_Slider_createAnimationFromBy(lua_State* state);
 int lua_Slider_createAnimationFromBy(lua_State* state);
 int lua_Slider_createAnimationFromTo(lua_State* state);
 int lua_Slider_createAnimationFromTo(lua_State* state);
 int lua_Slider_destroyAnimation(lua_State* state);
 int lua_Slider_destroyAnimation(lua_State* state);
+int lua_Slider_getAbsoluteBounds(lua_State* state);
 int lua_Slider_getAlignment(lua_State* state);
 int lua_Slider_getAlignment(lua_State* state);
 int lua_Slider_getAnimation(lua_State* state);
 int lua_Slider_getAnimation(lua_State* state);
 int lua_Slider_getAnimationPropertyComponentCount(lua_State* state);
 int lua_Slider_getAnimationPropertyComponentCount(lua_State* state);

+ 45 - 0
gameplay/src/lua/lua_TextBox.cpp

@@ -32,6 +32,7 @@ void luaRegister_TextBox()
         {"createAnimationFromBy", lua_TextBox_createAnimationFromBy},
         {"createAnimationFromBy", lua_TextBox_createAnimationFromBy},
         {"createAnimationFromTo", lua_TextBox_createAnimationFromTo},
         {"createAnimationFromTo", lua_TextBox_createAnimationFromTo},
         {"destroyAnimation", lua_TextBox_destroyAnimation},
         {"destroyAnimation", lua_TextBox_destroyAnimation},
+        {"getAbsoluteBounds", lua_TextBox_getAbsoluteBounds},
         {"getAlignment", lua_TextBox_getAlignment},
         {"getAlignment", lua_TextBox_getAlignment},
         {"getAnimation", lua_TextBox_getAnimation},
         {"getAnimation", lua_TextBox_getAnimation},
         {"getAnimationPropertyComponentCount", lua_TextBox_getAnimationPropertyComponentCount},
         {"getAnimationPropertyComponentCount", lua_TextBox_getAnimationPropertyComponentCount},
@@ -681,6 +682,50 @@ int lua_TextBox_destroyAnimation(lua_State* state)
     return 0;
     return 0;
 }
 }
 
 
+int lua_TextBox_getAbsoluteBounds(lua_State* state)
+{
+    // Get the number of parameters.
+    int paramCount = lua_gettop(state);
+
+    // Attempt to match the parameters to a valid binding.
+    switch (paramCount)
+    {
+        case 1:
+        {
+            if ((lua_type(state, 1) == LUA_TUSERDATA))
+            {
+                TextBox* instance = getInstance(state);
+                void* returnPtr = (void*)&(instance->getAbsoluteBounds());
+                if (returnPtr)
+                {
+                    gameplay::ScriptUtil::LuaObject* object = (gameplay::ScriptUtil::LuaObject*)lua_newuserdata(state, sizeof(gameplay::ScriptUtil::LuaObject));
+                    object->instance = returnPtr;
+                    object->owns = false;
+                    luaL_getmetatable(state, "Rectangle");
+                    lua_setmetatable(state, -2);
+                }
+                else
+                {
+                    lua_pushnil(state);
+                }
+
+                return 1;
+            }
+
+            lua_pushstring(state, "lua_TextBox_getAbsoluteBounds - Failed to match the given parameters to a valid function signature.");
+            lua_error(state);
+            break;
+        }
+        default:
+        {
+            lua_pushstring(state, "Invalid number of parameters (expected 1).");
+            lua_error(state);
+            break;
+        }
+    }
+    return 0;
+}
+
 int lua_TextBox_getAlignment(lua_State* state)
 int lua_TextBox_getAlignment(lua_State* state)
 {
 {
     // Get the number of parameters.
     // Get the number of parameters.

+ 1 - 0
gameplay/src/lua/lua_TextBox.h

@@ -13,6 +13,7 @@ int lua_TextBox_createAnimation(lua_State* state);
 int lua_TextBox_createAnimationFromBy(lua_State* state);
 int lua_TextBox_createAnimationFromBy(lua_State* state);
 int lua_TextBox_createAnimationFromTo(lua_State* state);
 int lua_TextBox_createAnimationFromTo(lua_State* state);
 int lua_TextBox_destroyAnimation(lua_State* state);
 int lua_TextBox_destroyAnimation(lua_State* state);
+int lua_TextBox_getAbsoluteBounds(lua_State* state);
 int lua_TextBox_getAlignment(lua_State* state);
 int lua_TextBox_getAlignment(lua_State* state);
 int lua_TextBox_getAnimation(lua_State* state);
 int lua_TextBox_getAnimation(lua_State* state);
 int lua_TextBox_getAnimationPropertyComponentCount(lua_State* state);
 int lua_TextBox_getAnimationPropertyComponentCount(lua_State* state);

+ 25 - 0
samples/browser/res/common/default.theme

@@ -121,6 +121,11 @@ theme mainMenu
         region = 1, 1, 74, 74
         region = 1, 1, 74, 74
         color = #4A8799ff
         color = #4A8799ff
     }
     }
+	
+	skin mainFocus : mainNormal
+	{
+		color = #ff0000ff
+	}
 
 
     skin mainActive : mainNormal
     skin mainActive : mainNormal
     {
     {
@@ -235,6 +240,11 @@ theme mainMenu
             textAlignment = ALIGN_VCENTER_HCENTER
             textAlignment = ALIGN_VCENTER_HCENTER
         }
         }
 
 
+		stateFocus
+		{
+			skin = mainFocus
+		}
+		
         stateActive
         stateActive
         {
         {
             imageList = activeImages
             imageList = activeImages
@@ -268,6 +278,11 @@ theme mainMenu
             font = res/common/arial18.gpb
             font = res/common/arial18.gpb
             fontSize = 20
             fontSize = 20
         }
         }
+		
+		stateFocus
+		{
+			textColor = #00ff00ff
+		}
 
 
         stateActive
         stateActive
         {
         {
@@ -285,6 +300,11 @@ theme mainMenu
             fontSize = 14
             fontSize = 14
             textAlignment = ALIGN_VCENTER_HCENTER
             textAlignment = ALIGN_VCENTER_HCENTER
         }
         }
+		
+		stateFocus
+		{
+			textColor = #00ff00ff
+		}
 
 
         stateActive
         stateActive
         {
         {
@@ -301,6 +321,11 @@ theme mainMenu
             fontSize = 20
             fontSize = 20
             textAlignment = ALIGN_VCENTER_LEFT
             textAlignment = ALIGN_VCENTER_LEFT
         }
         }
+		
+		stateFocus
+		{
+			textColor = #00ff00ff
+		}
 
 
         stateActive
         stateActive
         {
         {

+ 67 - 59
samples/browser/res/common/forms/formBasicControls.form

@@ -4,44 +4,44 @@ form basicControls
     layout = LAYOUT_ABSOLUTE
     layout = LAYOUT_ABSOLUTE
     style = basic
     style = basic
     size = 600, 600
     size = 600, 600
-    scroll = SCROLL_BOTH
-    
-    label title
-    {
-        style = underlined
-        position = 185, 0
-        size = 200, 50
-        text = This is a label.
-    }
-
-    button testButton
-    {
-        style = buttonStyle
-        position = 20, 80
-        size = 200, 100
-        text = This is a button.
-    }
-
-    checkbox testCheckbox
-    {
-        style = iconNoBorder
-        position = 20, 220
-        size = 200, 40
-        text = This is a checkbox.
-    }
+    	
+	label title
+	{
+		style = underlined
+		position = 185, 0
+		size = 200, 50
+		text = This is a label.
+	}
+		
+	button testButton
+	{
+		style = buttonStyle
+		position = 20, 80
+		size = 200, 100
+		text = This is a button.
+	}
 
 
-    slider testSlider
-    {
-        style = topLeftNoBorder
-        position = 25, 315
-        size = 190, 60
-        orientation = HORIZONTAL
-        min = -100
-        max = 100
-        value = 0
-        text = This is a slider.
-        valueTextVisible = true
-    }
+	checkbox testCheckbox
+	{
+		style = iconNoBorder
+		position = 20, 220
+		size = 200, 40
+		text = This is a checkbox.
+	}
+	
+	slider testSlider
+	{
+		style = topLeftNoBorder
+		position = 25, 315
+		size = 190, 60
+		orientation = HORIZONTAL
+		min = -100
+		max = 100
+		value = 0
+		//step = 5
+		text = This is a slider.
+		valueTextVisible = true
+	}
 
 
     label textBoxLabel
     label textBoxLabel
     {
     {
@@ -59,28 +59,36 @@ form basicControls
         text = This is a text box.
         text = This is a text box.
     }
     }
 
 
-    radioButton choice1
-    {
-        style = iconNoBorder
-        text = Radio button 1.
-        group = testRadio
-        position = 350, 80
-        size = 180, 40
-        selected = true
-    }
+	container radioButtonContainer
+	{
+		layout = LAYOUT_VERTICAL
+		style = basic
+		position = 330, 100
+		size = 200, 140
+		
+		radioButton choice1
+		{
+			style = iconNoBorder
+			text = Radio button 1.
+			group = testRadio
+			//position = 350, 80
+			size = 180, 40
+			selected = true
+		}
 
 
-    radioButton choice2 : choice1
-    {
-        text = Radio button 2.
-        position = 350, 130
-        selected = false
-    }
+		radioButton choice2 : choice1
+		{
+			text = Radio button 2.
+			//position = 350, 130
+			selected = false
+		}
 
 
-    radioButton choice3 : choice2
-    {
-        text = Radio button 3.
-        position = 350, 180
-    }
+		radioButton choice3 : choice2
+		{
+			text = Radio button 3.
+			//position = 350, 180
+		}
+	}
 	
 	
 	image testImage
 	image testImage
 	{
 	{
@@ -88,8 +96,8 @@ form basicControls
 		style = basic
 		style = basic
 		size = 256, 256
 		size = 256, 256
 		path = res/png/box-diffuse.png
 		path = res/png/box-diffuse.png
-		srcRegion = 20, 20, 100, 100
-		dstRegion = 20, 20, 100, 100
+		//srcRegion = 20, 20, 100, 100
+		//dstRegion = 20, 20, 100, 100
 	}
 	}
 	
 	
 	button alignedButton
 	button alignedButton

+ 32 - 32
samples/browser/res/common/forms/formSelect.form

@@ -2,44 +2,44 @@ form formSelect
 {
 {
     theme = res/common/default.theme
     theme = res/common/default.theme
     layout = LAYOUT_VERTICAL
     layout = LAYOUT_VERTICAL
-    style = noBorder
+    style = basic
     alignment = ALIGN_TOP_LEFT
     alignment = ALIGN_TOP_LEFT
-    width = 240
+    width = 200
     autoHeight = true
     autoHeight = true
 
 
-    radioButton form0
-    {
-        style = basic
-        text = Basic Controls
-        group = formSelection
-        size = 180, 60
-        textAlignment = ALIGN_VCENTER_HCENTER
-        selected = true
-    }
+	radioButton form0
+	{
+		style = basic
+		text = Basic Controls
+		group = formSelection
+		size = 180, 60
+		textAlignment = ALIGN_VCENTER_HCENTER
+		selected = true
+	}
 
 
-    radioButton form1 : form0
-    {
-        text = Scrolling
-        selected = false
-    }
+	radioButton form1 : form0
+	{
+		text = Scrolling
+		selected = false
+	}
 
 
-    radioButton form2 : form1
-    {
-        text = Flow Layout
-    }
+	radioButton form2 : form1
+	{
+		text = Flow Layout
+	}
 
 
-    radioButton form3 : form1
-    {
-        text = Vertical Layout
-    }
+	radioButton form3 : form1
+	{
+		text = Vertical Layout
+	}
 
 
-    radioButton form4 : form1
-    {
-        text = Z-Order
-    }
+	radioButton form4 : form1
+	{
+		text = Z-Order
+	}
 
 
-    radioButton form5 : form1
-    {
-        text = Programmatic
-    }
+	radioButton form5 : form1
+	{
+		text = Programmatic
+	}
 }
 }

+ 57 - 17
samples/browser/src/FormsSample.cpp

@@ -78,6 +78,7 @@ void FormsSample::initialize()
     
     
     RadioButton* form0Button = static_cast<RadioButton*>(_formSelect->getControl("form0"));
     RadioButton* form0Button = static_cast<RadioButton*>(_formSelect->getControl("form0"));
     form0Button->addListener(this, Control::Listener::CLICK);
     form0Button->addListener(this, Control::Listener::CLICK);
+    form0Button->setState(Control::FOCUS);
 
 
     RadioButton* form1Button = static_cast<RadioButton*>(_formSelect->getControl("form1"));
     RadioButton* form1Button = static_cast<RadioButton*>(_formSelect->getControl("form1"));
     form1Button->addListener(this, Control::Listener::CLICK);
     form1Button->addListener(this, Control::Listener::CLICK);
@@ -105,7 +106,7 @@ void FormsSample::initialize()
     createSampleForm(_forms[0]->getTheme());
     createSampleForm(_forms[0]->getTheme());
 
 
     Button* button = static_cast<Button*>(_forms[0]->getControl("testButton"));
     Button* button = static_cast<Button*>(_forms[0]->getControl("testButton"));
-    button->addListener(this, Control::Listener::CLICK);
+    button->setState(Control::FOCUS);
 
 
     // Create a scene with a camera node.
     // Create a scene with a camera node.
     Camera* camera = Camera::createPerspective(45.0f, (float)getWidth() / (float)getHeight(), 0.25f, 100.0f);
     Camera* camera = Camera::createPerspective(45.0f, (float)getWidth() / (float)getHeight(), 0.25f, 100.0f);
@@ -133,6 +134,8 @@ void FormsSample::formChanged()
         _activeForm->setEnabled(false);
         _activeForm->setEnabled(false);
     _activeForm = _forms[_formIndex];
     _activeForm = _forms[_formIndex];
     _activeForm->setEnabled(true);
     _activeForm->setEnabled(true);
+    _activeForm->setState(Control::FOCUS);
+    _formSelect->setState(Control::NORMAL);
 
 
     // Add the form to a node to allow it to be placed in 3D space.
     // Add the form to a node to allow it to be placed in 3D space.
     const Rectangle& bounds = _activeForm->getBounds();
     const Rectangle& bounds = _activeForm->getBounds();
@@ -150,7 +153,6 @@ void FormsSample::formChanged()
     _formNodeParent->setTranslation(0, 0, -1.5f);
     _formNodeParent->setTranslation(0, 0, -1.5f);
     _formNode->setTranslation(-0.5f, -0.5f, 0);
     _formNode->setTranslation(-0.5f, -0.5f, 0);
     _formNode->setForm(_activeForm);
     _formNode->setForm(_activeForm);
-
 }
 }
 
 
 void FormsSample::createSampleForm(Theme* theme)
 void FormsSample::createSampleForm(Theme* theme)
@@ -180,23 +182,29 @@ void FormsSample::createSampleForm(Theme* theme)
 void FormsSample::update(float elapsedTime)
 void FormsSample::update(float elapsedTime)
 {
 {
     float speedFactor = 0.001f * elapsedTime;
     float speedFactor = 0.001f * elapsedTime;
-    bool aDown = (_keyFlags & KEY_A_MASK) || _gamepad->isButtonDown(Gamepad::BUTTON_A);
-    bool bDown = (_keyFlags & KEY_B_MASK) || _gamepad->isButtonDown(Gamepad::BUTTON_B);
-    Vector2 joyCommand;
-    if (_gamepad->getJoystickCount() > 0)
+    bool aDown = (_keyFlags & KEY_A_MASK);
+    bool bDown = (_keyFlags & KEY_B_MASK);
+
+    if (_gamepad->isVirtual())
     {
     {
-        _gamepad->getJoystickValues(0, &joyCommand);
+        aDown |= _gamepad->isButtonDown(Gamepad::BUTTON_A);
+        bDown |= _gamepad->isButtonDown(Gamepad::BUTTON_B);
+        _gamepad->getJoystickValues(0, &_joysticks[0]);
     }
     }
-    if (_gamepad->getJoystickCount() > 1)
+
+    if (!_joysticks[0].isZero())
+    {
+        _gamepad->getJoystickValues(0, &_joysticks[0]);
+    }
+
+    if (!_joysticks[1].isZero())
     {
     {
-        Vector2 joy2;
-        _gamepad->getJoystickValues(1, &joy2);
         Matrix m;
         Matrix m;
         _formNodeParent->getWorldMatrix().transpose(&m);
         _formNodeParent->getWorldMatrix().transpose(&m);
         Vector3 yaw;
         Vector3 yaw;
         m.getUpVector(&yaw);
         m.getUpVector(&yaw);
-        _formNodeParent->rotate(yaw, speedFactor * joy2.x * 2.0f);
-        _formNodeParent->rotateX(-speedFactor * joy2.y * 2.0f);
+        _formNodeParent->rotate(yaw, speedFactor * _joysticks[1].x * 2.0f);
+        _formNodeParent->rotateX(-speedFactor * _joysticks[1].y * 2.0f);
     }
     }
 
 
     if (bDown)
     if (bDown)
@@ -210,12 +218,12 @@ void FormsSample::update(float elapsedTime)
         _formNodeParent->getWorldMatrix().transpose(&m);
         _formNodeParent->getWorldMatrix().transpose(&m);
         Vector3 yaw;
         Vector3 yaw;
         m.getUpVector(&yaw);
         m.getUpVector(&yaw);
-        _formNodeParent->rotate(yaw, speedFactor * joyCommand.x);
-        _formNodeParent->rotateX(-speedFactor * joyCommand.y);
+        _formNodeParent->rotate(yaw, speedFactor * _joysticks[0].x);
+        _formNodeParent->rotateX(-speedFactor * _joysticks[0].y);
     }
     }
     else
     else
     {
     {
-        _formNodeParent->translate(0.5f * speedFactor * joyCommand.x, 0.5f * speedFactor * joyCommand.y, 0);
+        _formNodeParent->translate(0.5f * speedFactor * _joysticks[0].x, 0.5f * speedFactor * _joysticks[0].y, 0);
     }
     }
 }
 }
 
 
@@ -367,8 +375,8 @@ void FormsSample::controlEvent(Control* control, EventType evt)
         control->createAnimationFromTo("opacityButton", Form::ANIMATE_OPACITY, from, to, Curve::LINEAR, 1000)->getClip()->play();
         control->createAnimationFromTo("opacityButton", Form::ANIMATE_OPACITY, from, to, Curve::LINEAR, 1000)->getClip()->play();
     }
     }
 }
 }
-
-void FormsSample::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad)
+
+void FormsSample::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex)
 {
 {
     switch(evt)
     switch(evt)
     {
     {
@@ -380,5 +388,37 @@ void FormsSample::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad)
         if (_gamepad && _gamepad->isVirtual())
         if (_gamepad && _gamepad->isVirtual())
             _gamepad->getForm()->setEnabled(true);
             _gamepad->getForm()->setEnabled(true);
         break;
         break;
+    case Gamepad::BUTTON_EVENT:
+        if (_gamepad->isButtonDown(Gamepad::BUTTON_A))
+            _keyFlags |= KEY_A_MASK;
+        else
+            _keyFlags &= ~KEY_A_MASK;
+
+        if (_gamepad->isButtonDown(Gamepad::BUTTON_B))
+            _keyFlags |= KEY_B_MASK;
+        else
+            _keyFlags &= ~KEY_B_MASK;
+
+        // We'll use a physical gamepad's MENU1 button as the "back" button.
+        if (_gamepad->isButtonDown(Gamepad::BUTTON_MENU1))
+        {
+            if (_formSelect->getState() == Control::FOCUS)
+            {
+                _formSelect->setState(Control::NORMAL);
+            }
+            else if (_activeForm->getState() == Control::FOCUS)
+            {
+                _activeForm->setState(Control::NORMAL);
+                _formSelect->setState(Control::FOCUS);
+            }
+            else
+            {
+                _formSelect->setState(Control::FOCUS);
+            }
+        }
+        break;
+    case Gamepad::JOYSTICK_EVENT:
+        gamepad->getJoystickValues(analogIndex, &_joysticks[analogIndex]);
+        break;
     }
     }
 }
 }

+ 4 - 2
samples/browser/src/FormsSample.h

@@ -11,7 +11,7 @@ public:
     
     
     FormsSample();
     FormsSample();
 
 
-    void gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad);
+    void gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex = 0);
 
 
 protected:
 protected:
 
 
@@ -46,8 +46,10 @@ private:
     bool _touched;
     bool _touched;
     int _touchX;
     int _touchX;
     unsigned int _formIndex;
     unsigned int _formIndex;
-    std::vector<const char*> _formFiles;
+    std::vector<const char*> _formFiles;
     std::string _sampleString;
     std::string _sampleString;
+    Vector2 _joysticks[2];
+
 };
 };
 
 
 #endif
 #endif

+ 2 - 1
samples/browser/src/InputSample.cpp

@@ -32,10 +32,11 @@ void InputSample::initialize()
     _crosshairUpperLimit += _crosshairLowerLimit;
     _crosshairUpperLimit += _crosshairLowerLimit;
 
 
     // Create input sample controls
     // Create input sample controls
-    _keyboardState = false;
+    _keyboardState = false;
     _inputSampleControls = Form::create("res/common/inputs.form");
     _inputSampleControls = Form::create("res/common/inputs.form");
     static_cast<Button*>(_inputSampleControls->getControl("showKeyboardButton"))->addListener(this, Listener::CLICK);
     static_cast<Button*>(_inputSampleControls->getControl("showKeyboardButton"))->addListener(this, Listener::CLICK);
     static_cast<Button*>(_inputSampleControls->getControl("captureMouseButton"))->addListener(this, Listener::CLICK);
     static_cast<Button*>(_inputSampleControls->getControl("captureMouseButton"))->addListener(this, Listener::CLICK);
+
     if (!hasMouse())
     if (!hasMouse())
     {
     {
         static_cast<Button*>(_inputSampleControls->getControl("captureMouseButton"))->setVisible(false);
         static_cast<Button*>(_inputSampleControls->getControl("captureMouseButton"))->setVisible(false);

+ 1 - 1
samples/browser/src/LightSample.cpp

@@ -273,7 +273,7 @@ void LightSample::controlEvent(Control* control, EventType evt)
 	
 	
 	switch(evt)
 	switch(evt)
     {
     {
-	case Listener::VALUE_CHANGED:
+	case Control::Listener::VALUE_CHANGED:
 		if ((control == _redSlider) || (control == _greenSlider) || (control == _blueSlider))
 		if ((control == _redSlider) || (control == _greenSlider) || (control == _blueSlider))
         {
         {
 			Vector3 color(_redSlider->getValue(), _greenSlider->getValue(), _blueSlider->getValue());
 			Vector3 color(_redSlider->getValue(), _greenSlider->getValue(), _blueSlider->getValue());

+ 1 - 1
samples/browser/src/Sample.cpp

@@ -227,7 +227,7 @@ void Sample::gestureTapEvent(int x, int y)
 {
 {
 }
 }
 
 
-void Sample::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad)
+void Sample::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex)
 {
 {
 }
 }
 
 

+ 1 - 1
samples/browser/src/Sample.h

@@ -70,7 +70,7 @@ public:
     virtual void gestureSwipeEvent(int x, int y, int direction);
     virtual void gestureSwipeEvent(int x, int y, int direction);
     virtual void gesturePinchEvent(int x, int y, float scale);
     virtual void gesturePinchEvent(int x, int y, float scale);
     virtual void gestureTapEvent(int x, int y);
     virtual void gestureTapEvent(int x, int y);
-    virtual void gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad);
+    virtual void gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex = 0);
     unsigned int getGamepadCount() const;
     unsigned int getGamepadCount() const;
     Gamepad* getGamepad(unsigned int index, bool preferPhysical = true) const;
     Gamepad* getGamepad(unsigned int index, bool preferPhysical = true) const;
 
 

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

@@ -218,7 +218,7 @@ void SamplesGame::controlEvent(Control* control, EventType evt)
     }
     }
 }
 }
 
 
-void SamplesGame::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad)
+void SamplesGame::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex)
 {
 {
     if (_activeSample)
     if (_activeSample)
         _activeSample->gamepadEvent(evt, gamepad);
         _activeSample->gamepadEvent(evt, gamepad);

+ 1 - 1
samples/browser/src/SamplesGame.h

@@ -51,7 +51,7 @@ public:
 
 
     void controlEvent(Control* control, EventType evt);
     void controlEvent(Control* control, EventType evt);
 
 
-    void gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad);
+    void gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex = 0);
 
 
     /**
     /**
      * Adds a sample.
      * Adds a sample.

+ 10 - 0
samples/particles/res/editor.theme

@@ -125,6 +125,11 @@ theme particleEditor
             textAlignment = ALIGN_VCENTER_HCENTER
             textAlignment = ALIGN_VCENTER_HCENTER
         }
         }
 
 
+        stateFocus
+        {
+            textColor = #ff0000ff
+        }
+
         stateActive
         stateActive
         {
         {
             imageList = activeImages
             imageList = activeImages
@@ -170,6 +175,11 @@ theme particleEditor
             textAlignment = ALIGN_VCENTER_HCENTER
             textAlignment = ALIGN_VCENTER_HCENTER
         }
         }
 
 
+        stateFocus
+        {
+            textColor = #ff0000ff
+        }
+
         stateActive
         stateActive
         {
         {
             imageList = activeImages
             imageList = activeImages

+ 1 - 0
samples/particles/src/ParticlesGame.cpp

@@ -55,6 +55,7 @@ void ParticlesGame::initialize()
     // Load the form for editing ParticleEmitters.
     // Load the form for editing ParticleEmitters.
     _form = Form::create("res/editor.form");
     _form = Form::create("res/editor.form");
     _form->setConsumeInputEvents(false);
     _form->setConsumeInputEvents(false);
+    _form->setState(Control::FOCUS);
 
 
     // Store pointers to UI controls we care about.
     // Store pointers to UI controls we care about.
     _startRed = (Slider*)_form->getControl("startRed");
     _startRed = (Slider*)_form->getControl("startRed");

+ 10 - 0
samples/racer/res/common/menu.theme

@@ -145,6 +145,11 @@ theme menuTheme
             fontSize = 20
             fontSize = 20
         }
         }
 
 
+        stateFocus
+        {
+            textColor = #ff0000ff
+        }
+
         stateActive
         stateActive
         {
         {
             skin = mainActive
             skin = mainActive
@@ -162,6 +167,11 @@ theme menuTheme
             textAlignment = ALIGN_VCENTER_HCENTER
             textAlignment = ALIGN_VCENTER_HCENTER
         }
         }
 
 
+        stateFocus
+        {
+            textColor = #ff0000ff
+        }
+
         stateActive
         stateActive
         {
         {
             imageList = activeImages
             imageList = activeImages

+ 4 - 2
samples/racer/src/RacerGame.cpp

@@ -38,8 +38,6 @@ RacerGame::RacerGame()
 
 
 void RacerGame::initialize()
 void RacerGame::initialize()
 {
 {
-    //setVsync(false);
-
     setMultiTouch(true);
     setMultiTouch(true);
 
 
     _font = Font::create("res/common/arial40.gpb");
     _font = Font::create("res/common/arial40.gpb");
@@ -536,6 +534,7 @@ void RacerGame::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad)
                 _virtualGamepadClip->play();
                 _virtualGamepadClip->play();
             }
             }
             _gamepad = _physicalGamepad;
             _gamepad = _physicalGamepad;
+            _virtualGamepad->getForm()->setEnabled(false);
         }
         }
         else if (_virtualGamepad)
         else if (_virtualGamepad)
         {
         {
@@ -545,6 +544,7 @@ void RacerGame::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad)
                 _virtualGamepadClip->play();
                 _virtualGamepadClip->play();
             }
             }
             _gamepad = _virtualGamepad;
             _gamepad = _virtualGamepad;
+            _virtualGamepad->getForm()->setEnabled(true);
         }
         }
 
 
         break;
         break;
@@ -556,6 +556,7 @@ void RacerGame::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad)
 
 
             _virtualGamepadClip->setSpeed(-1.0f);
             _virtualGamepadClip->setSpeed(-1.0f);
             _virtualGamepadClip->play();
             _virtualGamepadClip->play();
+            _virtualGamepad->getForm()->setEnabled(true);
         }
         }
         break;
         break;
     }
     }
@@ -570,6 +571,7 @@ void RacerGame::menuEvent()
         static_cast<Button*>(_overlay->getControl("menuButton"))->setText("Resume");
         static_cast<Button*>(_overlay->getControl("menuButton"))->setText("Resume");
         pause();
         pause();
         _menu->setEnabled(true);
         _menu->setEnabled(true);
+        _menu->setState(Control::FOCUS);
     }
     }
     else
     else
     {
     {