Browse Source

Adds Hover state to UI controls. Tweaks the handling of scroll wheels to scroll containers and manipulate sliders.

ablake 12 years ago
parent
commit
ebd5d0a92a
48 changed files with 894 additions and 94 deletions
  1. 1 3
      gameplay/src/Button.cpp
  2. 104 39
      gameplay/src/Container.cpp
  3. 20 1
      gameplay/src/Container.h
  4. 58 2
      gameplay/src/Control.cpp
  5. 25 1
      gameplay/src/Control.h
  6. 3 2
      gameplay/src/Form.cpp
  7. 26 11
      gameplay/src/Slider.cpp
  8. 2 2
      gameplay/src/TextBox.cpp
  9. 27 8
      gameplay/src/Theme.cpp
  10. 8 3
      gameplay/src/ThemeStyle.cpp
  11. 2 1
      gameplay/src/ThemeStyle.h
  12. 36 0
      gameplay/src/lua/lua_Button.cpp
  13. 1 0
      gameplay/src/lua/lua_Button.h
  14. 36 0
      gameplay/src/lua/lua_CheckBox.cpp
  15. 1 0
      gameplay/src/lua/lua_CheckBox.h
  16. 109 0
      gameplay/src/lua/lua_Container.cpp
  17. 3 0
      gameplay/src/lua/lua_Container.h
  18. 36 0
      gameplay/src/lua/lua_Control.cpp
  19. 1 0
      gameplay/src/lua/lua_Control.h
  20. 10 0
      gameplay/src/lua/lua_ControlListenerEventType.cpp
  21. 5 0
      gameplay/src/lua/lua_ControlState.cpp
  22. 109 0
      gameplay/src/lua/lua_Form.cpp
  23. 3 0
      gameplay/src/lua/lua_Form.h
  24. 3 0
      gameplay/src/lua/lua_Global.cpp
  25. 36 0
      gameplay/src/lua/lua_ImageControl.cpp
  26. 1 0
      gameplay/src/lua/lua_ImageControl.h
  27. 36 0
      gameplay/src/lua/lua_Joystick.cpp
  28. 1 0
      gameplay/src/lua/lua_Joystick.h
  29. 36 0
      gameplay/src/lua/lua_Label.cpp
  30. 1 0
      gameplay/src/lua/lua_Label.h
  31. 36 0
      gameplay/src/lua/lua_RadioButton.cpp
  32. 1 0
      gameplay/src/lua/lua_RadioButton.h
  33. 36 0
      gameplay/src/lua/lua_Slider.cpp
  34. 1 0
      gameplay/src/lua/lua_Slider.h
  35. 36 0
      gameplay/src/lua/lua_TextBox.cpp
  36. 1 0
      gameplay/src/lua/lua_TextBox.h
  37. 21 9
      samples/browser/res/common/default.theme
  38. 3 3
      samples/browser/res/common/forms/formBasicControls.form
  39. 1 1
      samples/browser/res/common/forms/formFlowLayout.form
  40. 1 1
      samples/browser/res/common/forms/formScrolling.form
  41. 1 1
      samples/browser/res/common/forms/formSelect.form
  42. 1 1
      samples/browser/res/common/forms/formVerticalLayout.form
  43. 1 1
      samples/browser/res/common/forms/formZOrder.form
  44. 1 0
      samples/browser/res/common/text.form
  45. 1 1
      samples/browser/src/FormsSample.cpp
  46. 1 1
      samples/browser/src/Sample.cpp
  47. 1 1
      samples/browser/src/Sample.h
  48. 10 1
      samples/browser/src/SamplesGame.cpp

+ 1 - 3
gameplay/src/Button.cpp

@@ -81,9 +81,7 @@ bool Button::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contac
         }
         break;
     case Touch::TOUCH_MOVE:
-        if (_contactIndex == (int) contactIndex)
-            return _consumeInputEvents;
-        break;
+        return Control::touchEvent(evt, x, y, contactIndex);
     }
 
     return false;

+ 104 - 39
gameplay/src/Container.cpp

@@ -54,7 +54,7 @@ Container::Container()
       _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), _scrollWheelRequiresFocus(false)
 {
 }
 
@@ -114,6 +114,13 @@ Container* Container::create(Theme::Style* style, Properties* properties, Theme*
     {
         container->_scrollBarOpacity = 0.0f;
     }
+    
+    container->_scrollWheelRequiresFocus = properties->getBool("scrollWheelRequiresFocus");
+    if (properties->exists("scrollingFriction"))
+        container->_scrollingFriction = properties->getFloat("scrollingFriction");
+    if (properties->exists("scrollWheelSpeed"))
+        container->_scrollWheelSpeed = properties->getFloat("scrollWheelSpeed");
+
     container->addControls(theme, properties);
     container->_layout->update(container, container->_scrollPosition);
 
@@ -421,6 +428,16 @@ const char* Container::getType() const
     return "container";
 }
 
+bool Container::getScrollWheelRequiresFocus() const
+{
+    return _scrollWheelRequiresFocus;
+}
+
+void Container::setScrollWheelRequiresFocus(bool required)
+{
+    _scrollWheelRequiresFocus = required;
+}
+
 void Container::update(const Control* container, const Vector2& offset)
 {
     // Update this container's viewport.
@@ -628,7 +645,7 @@ bool Container::keyEvent(Keyboard::KeyEvent evt, int key)
             continue;
         }
 
-        if (control->getState() == Control::FOCUS && control->keyEvent(evt, key))
+        if (control->isInFocus() && control->keyEvent(evt, key))
         {
             release();
             return true;
@@ -660,7 +677,7 @@ void Container::guaranteeFocus(Control* inFocus)
         {
             ((Container*)control)->guaranteeFocus(inFocus);
         }
-        else if (control->getState() == Control::FOCUS)
+        else if (control->isInFocus())
         {
             control->setState(NORMAL);
             return;
@@ -680,7 +697,7 @@ bool Container::moveFocus(Direction direction, Control* outsideControl)
         {
             Control* control = *it;
             GP_ASSERT(control);
-            if (control->getState() == Control::FOCUS)
+            if (control->isInFocus())
             {
                 start = control;
                 break;
@@ -887,7 +904,7 @@ bool Container::moveFocus(Direction direction, Control* outsideControl)
 void Container::timeEvent(long timeDiff, void* cookie)
 {
     double time = Game::getAbsoluteTime();
-    if (_focusChangeRepeat && _state == FOCUS && _focusPressed &&
+    if (_focusChangeRepeat && isInFocus() && _focusPressed &&
         abs(time - timeDiff - _focusChangeRepeatDelay - _focusChangeStartTime) < 50)
     {
         ++_focusChangeCount;
@@ -944,7 +961,7 @@ bool Container::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsign
     {
         Control* control = *it;
         GP_ASSERT(control);
-        if (control->getState() == Control::FOCUS || control->getState() == Control::ACTIVE)
+        if (control->isInFocus() || control->getState() == Control::ACTIVE)
         {
             eventConsumed |= control->gamepadEvent(evt, gamepad, analogIndex);
             break;
@@ -986,7 +1003,7 @@ bool Container::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsign
                     gamepad->isButtonDown(Gamepad::BUTTON_DOWN))
                 {
                     _focusPressed |= DOWN;
-                    eventConsumed |= _consumeInputEvents;
+                    eventConsumed = true;
                     if (moveFocus(DOWN))
                         break;
                     else
@@ -997,7 +1014,7 @@ bool Container::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsign
                     gamepad->isButtonDown(Gamepad::BUTTON_RIGHT))
                 {
                     _focusPressed |= RIGHT;
-                    eventConsumed |= _consumeInputEvents;
+                    eventConsumed = true;
                     if (moveFocus(RIGHT))
                         break;
                     else
@@ -1008,7 +1025,7 @@ bool Container::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsign
                     gamepad->isButtonDown(Gamepad::BUTTON_UP))
                 {
                     _focusPressed |= UP;
-                    eventConsumed |= _consumeInputEvents;
+                    eventConsumed = true;
                     if (moveFocus(UP))
                         break;
                     else
@@ -1019,7 +1036,7 @@ bool Container::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsign
                     gamepad->isButtonDown(Gamepad::BUTTON_LEFT))
                 {
                     _focusPressed |= LEFT;
-                    eventConsumed |= _consumeInputEvents;
+                    eventConsumed = true;
                     if (moveFocus(LEFT))
                         break;
                     else
@@ -1033,7 +1050,7 @@ bool Container::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsign
                 {
                 case 0:
                     // The left analog stick can be used in the same way as the DPad.
-                    eventConsumed |= _consumeInputEvents;
+                    eventConsumed = true;
                     if (!(_focusPressed & RIGHT) &&
                         joystick.x > JOYSTICK_THRESHOLD)
                     {
@@ -1111,7 +1128,7 @@ bool Container::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsign
             joystick.y > -JOYSTICK_THRESHOLD)
         {
             _focusPressed &= ~DOWN;
-            eventConsumed |= _consumeInputEvents;
+            eventConsumed = true;
         }
 
         if ((_focusPressed & RIGHT) &&
@@ -1119,7 +1136,7 @@ bool Container::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsign
             joystick.x < JOYSTICK_THRESHOLD)
         {
             _focusPressed &= ~RIGHT;
-            eventConsumed |= _consumeInputEvents;
+            eventConsumed = true;
         }
     
         if ((_focusPressed & UP) &&
@@ -1127,7 +1144,7 @@ bool Container::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsign
             joystick.y < JOYSTICK_THRESHOLD)
         {
             _focusPressed &= ~UP;
-            eventConsumed |= _consumeInputEvents;
+            eventConsumed = true;
         }
 
         if ((_focusPressed & LEFT) &&
@@ -1135,7 +1152,7 @@ bool Container::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsign
             joystick.x > -JOYSTICK_THRESHOLD)
         {
             _focusPressed &= ~LEFT;
-            eventConsumed |= _consumeInputEvents;
+            eventConsumed = true;
         }
     }
 
@@ -1521,22 +1538,27 @@ bool Container::mouseEventScroll(Mouse::MouseEvent evt, int x, int y, int wheelD
             return touchEventScroll(Touch::TOUCH_RELEASE, x, y, 0);
 
         case Mouse::MOUSE_WHEEL:
-            if (_scrollingVelocity.isZero())
+            if ((_state == HOVER && (!_scrollWheelRequiresFocus || _previousState == FOCUS)) ||
+                _state == FOCUS && _scrollWheelRequiresFocus)
             {
-                _lastFrameTime = Game::getGameTime();
-            }
-            _scrolling = _scrollingMouseVertically = _scrollingMouseHorizontally = false;
+                if (_scrollingVelocity.isZero())
+                {
+                    _lastFrameTime = Game::getGameTime();
+                }
+                _scrolling = _scrollingMouseVertically = _scrollingMouseHorizontally = false;
 
-            _scrollingVelocity.y += _scrollWheelSpeed * wheelDelta;
+                _scrollingVelocity.y += _scrollWheelSpeed * wheelDelta;
 
-            if (_scrollBarOpacityClip && _scrollBarOpacityClip->isPlaying())
-            {
-                _scrollBarOpacityClip->stop();
-                _scrollBarOpacityClip = NULL;
+                if (_scrollBarOpacityClip && _scrollBarOpacityClip->isPlaying())
+                {
+                    _scrollBarOpacityClip->stop();
+                    _scrollBarOpacityClip = NULL;
+                }
+                _scrollBarOpacity = 1.0f;
+                _dirty = true;
+                return _consumeInputEvents;
             }
-            _scrollBarOpacity = 1.0f;
-            _dirty = true;
-            return _consumeInputEvents;
+            break;
     }
 
     return false;
@@ -1586,12 +1608,13 @@ bool Container::pointerEvent(bool mouse, char evt, int x, int y, int data)
         }
 
         Control::State currentState = control->getState();
-        if ((control->isContainer() && currentState == Control::FOCUS) ||
-            (currentState != Control::NORMAL) ||
+        if ((currentState != Control::NORMAL) ||
             ((evt == Touch::TOUCH_PRESS ||
               evt == Mouse::MOUSE_PRESS_LEFT_BUTTON ||
               evt == Mouse::MOUSE_PRESS_MIDDLE_BUTTON ||
-              evt == Mouse::MOUSE_PRESS_RIGHT_BUTTON) &&
+              evt == Mouse::MOUSE_PRESS_RIGHT_BUTTON ||
+              evt == Mouse::MOUSE_MOVE ||
+              evt == Mouse::MOUSE_WHEEL) &&
                 x >= xPos + boundsX &&
                 x <= xPos + boundsX + bounds.width &&
                 y >= yPos + boundsY &&
@@ -1607,18 +1630,23 @@ bool Container::pointerEvent(bool mouse, char evt, int x, int y, int data)
 
     if (!isEnabled() || !isVisible())
     {
+        _contactIndex = INVALID_CONTACT_INDEX;
+        _contactIndices = 0;
+        _scrolling = false;
+        _scrollingMouseVertically = _scrollingMouseHorizontally = false;
+
         release();
         return (_consumeInputEvents | eventConsumed);
     }
-    
-    bool withinClipBounds = false;
+
+    bool withinClipBounds = (x > _clipBounds.x && x <= _clipBounds.x + _clipBounds.width &&
+                             y > _clipBounds.y && y <= _clipBounds.y + _clipBounds.height);
     switch (evt)
     {
     case Touch::TOUCH_PRESS:
-        if (x > _clipBounds.x && x <= _clipBounds.x + _clipBounds.width &&
-            y > _clipBounds.y && y <= _clipBounds.y + _clipBounds.height)
+        if (withinClipBounds)
         {
-            setState(Control::FOCUS);
+            setState(Control::ACTIVE);
             withinClipBounds = true;
             if (eventConsumed)
                 _contactIndices++;
@@ -1631,16 +1659,53 @@ bool Container::pointerEvent(bool mouse, char evt, int x, int y, int data)
             return false;
         }
         break;
+    case Mouse::MOUSE_MOVE:
+        if (_state != ACTIVE)
+        {
+            if (_state != HOVER && withinClipBounds)
+            {
+                _previousState = _state;
+                setState(HOVER);
+                notifyListeners(Control::Listener::ENTER);
+            }
+            else if (_state == HOVER && !withinClipBounds)
+            {
+                setState(_previousState);
+                notifyListeners(Control::Listener::LEAVE);
+            }
+            else if (_state != HOVER)
+            {
+                release();
+                return false;
+            }
+        }
+        break;
+    case Mouse::MOUSE_WHEEL:
+        if (!withinClipBounds && !_scrollWheelRequiresFocus)
+        {
+            release();
+            return false;
+        }
+        break;
     case Touch::TOUCH_RELEASE:
-        if (eventConsumed)
+        if (eventConsumed && _contactIndices > 0)
+        {
+            _contactIndices--;
+        }
+
+        if (_state == ACTIVE && withinClipBounds)
+        {
+            setState(FOCUS);
+        }
+        else
         {
-            if (_contactIndices > 0)
-                _contactIndices--;
+            setState(NORMAL);
         }
         break;
     }
 
-    if (!eventConsumed && _scroll != SCROLL_NONE && getState() == Control::FOCUS && (evt != Touch::TOUCH_PRESS || withinClipBounds))
+    if (!eventConsumed && _scroll != SCROLL_NONE &&
+        (evt != Touch::TOUCH_PRESS || withinClipBounds))
     {
         if ((mouse && mouseEventScroll((Mouse::MouseEvent)evt, x - xPos, y - yPos, data)) ||
             (!mouse && touchEventScroll((Touch::TouchEvent)evt, x - xPos, y - yPos, (unsigned int)data)))

+ 20 - 1
gameplay/src/Container.h

@@ -27,7 +27,10 @@ namespace gameplay
          width       = <width>   // Can be used in place of 'size', e.g. with 'autoHeight = true'
          height      = <height>  // Can be used in place of 'size', e.g. with 'autoWidth = true'
          scroll      = <Container::Scroll constant> // Whether scrolling is allowed and in which directions.
-         scrollBarsAutoHide = <bool>    // Whether scrollbars fade out when not in use.
+         scrollBarsAutoHide = <bool>        // Whether scrollbars fade out when not in use.
+         scrollingFriction = <float>        // Friction applied to inertial scrolling.
+         scrollWheelRequiresFocus = <bool>  // Whether focus or hover state handles scroll-wheel events.
+         scrollWheelSpeed = <float>         // Speed to scroll at on a scroll-wheel event.
          consumeEvents = <bool>             // Whether the container propagates input events to the Game's input event handler. Default is true.
 
          // All the nested controls within this container.
@@ -219,6 +222,21 @@ public:
      */
     const char* getType() const;
 
+    /**
+     * Get whether this container requires focus in order to handle scroll-wheel events.
+     */
+    bool getScrollWheelRequiresFocus() const;
+
+    /**
+     * Set whether this container requires focus in order to handle scroll-wheel events.
+     * If this property is set to true, scroll-wheel events will only be handled when the container has focus.
+     * If this property is set tofalse, scroll-wheel events will only be handled
+     * when the container is in the HOVER state.
+     *
+     * @param required Whether focus is required in order to handle scroll-wheel events.
+     */
+    void setScrollWheelRequiresFocus(bool required);
+
     /**
      * @see AnimationTarget::getAnimationPropertyComponentCount
      */
@@ -597,6 +615,7 @@ private:
     float _totalHeight;
     int _contactIndices;
     bool _initializedWithScroll;
+    bool _scrollWheelRequiresFocus;
 };
 
 }

+ 58 - 2
gameplay/src/Control.cpp

@@ -8,7 +8,7 @@ namespace gameplay
 Control::Control()
     : _id(""), _state(Control::NORMAL), _bounds(Rectangle::empty()), _clipBounds(Rectangle::empty()), _viewportClipBounds(Rectangle::empty()),
     _clearBounds(Rectangle::empty()), _dirty(true), _consumeInputEvents(false), _alignment(ALIGN_TOP_LEFT), _isAlignmentSet(false), _autoWidth(false), _autoHeight(false), _listeners(NULL), _visible(true),
-    _zIndex(-1), _contactIndex(INVALID_CONTACT_INDEX), _focusIndex(-1), _parent(NULL), _styleOverridden(false), _skin(NULL)
+    _zIndex(-1), _contactIndex(INVALID_CONTACT_INDEX), _focusIndex(-1), _parent(NULL), _styleOverridden(false), _skin(NULL), _previousState(NORMAL)
 {
     addScriptEvent("controlEvent", "<Control>[Control::Listener::EventType]");
 }
@@ -115,6 +115,10 @@ void Control::initialize(Theme::Style* style, Properties* properties)
         {
             overrideThemedProperties(innerSpace, DISABLED);
         }
+        else if (spaceName == "STATEHOVER")
+        {
+            overrideThemedProperties(innerSpace, HOVER);
+        }
         else if (spaceName == "MARGIN")
         {
             setMargin(innerSpace->getFloat("top"), innerSpace->getFloat("bottom"),
@@ -271,6 +275,11 @@ bool Control::isVisible() const
     return _visible;
 }
 
+bool Control::isInFocus() const
+{
+    return (_state == FOCUS || (_state == HOVER && _previousState == FOCUS));
+}
+
 void Control::setOpacity(float opacity, unsigned char states)
 {
     overrideStyle();
@@ -653,6 +662,8 @@ Theme::Style::OverlayType Control::getOverlayType() const
         return Theme::Style::OVERLAY_ACTIVE;
     case Control::DISABLED:
         return Theme::Style::OVERLAY_DISABLED;
+    case Control::HOVER:
+        return Theme::Style::OVERLAY_HOVER;
     default:
         return Theme::Style::OVERLAY_NORMAL;
     }
@@ -789,6 +800,9 @@ bool Control::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int conta
         }
         break;
             
+    case Touch::TOUCH_MOVE:
+        break;
+
     case Touch::TOUCH_RELEASE:
         if (_contactIndex == (int)contactIndex)
         {
@@ -830,6 +844,25 @@ bool Control::mouseEvent(Mouse::MouseEvent evt, int x, int y, int wheelDelta)
         return touchEvent(Touch::TOUCH_RELEASE, x, y, 0);
 
     case Mouse::MOUSE_MOVE:
+        if (_state != ACTIVE)
+        {
+            if (_state != HOVER &&
+                x > _clipBounds.x && x <= _clipBounds.x + _clipBounds.width &&
+                y > _clipBounds.y && y <= _clipBounds.y + _clipBounds.height)
+            {
+                _previousState = _state;
+                setState(HOVER);
+                notifyListeners(Control::Listener::ENTER);
+                return _consumeInputEvents;
+            }
+            else if (_state == HOVER && !(x > _clipBounds.x && x <= _clipBounds.x + _clipBounds.width &&
+                        y > _clipBounds.y && y <= _clipBounds.y + _clipBounds.height))
+            {
+                setState(_previousState);
+                notifyListeners(Control::Listener::LEAVE);
+                return _consumeInputEvents;
+            }
+        }
         return touchEvent(Touch::TOUCH_MOVE, x, y, 0);
 
     default:
@@ -1130,6 +1163,10 @@ Control::State Control::getState(const char* state)
     {
         return DISABLED;
     }
+    else if (strcmp(state, "HOVER") == 0)
+    {
+        return HOVER;
+    }
 
     return NORMAL;
 }
@@ -1272,6 +1309,11 @@ Theme::Style::Overlay** Control::getOverlays(unsigned char overlayTypes, Theme::
         overlays[index++] = _style->getOverlay(Theme::Style::OVERLAY_DISABLED);
     }
 
+    if ((overlayTypes & HOVER) == HOVER)
+    {
+        overlays[index++] = _style->getOverlay(Theme::Style::OVERLAY_HOVER);
+    }
+
     return overlays;
 }
 
@@ -1286,9 +1328,23 @@ Theme::Style::Overlay* Control::getOverlay(State state) const
     case Control::FOCUS:
         return _style->getOverlay(Theme::Style::OVERLAY_FOCUS);
     case Control::ACTIVE:
-        return _style->getOverlay(Theme::Style::OVERLAY_ACTIVE);
+    {
+        Theme::Style::Overlay* activeOverlay = _style->getOverlay(Theme::Style::OVERLAY_ACTIVE);
+        if (activeOverlay)
+            return activeOverlay;
+        else
+            return getOverlay(_previousState);
+    }
     case Control::DISABLED:
         return _style->getOverlay(Theme::Style::OVERLAY_DISABLED);
+    case Control::HOVER:
+    {
+        Theme::Style::Overlay* hoverOverlay = _style->getOverlay(Theme::Style::OVERLAY_HOVER);
+        if (hoverOverlay)
+            return hoverOverlay;
+        else
+            return getOverlay(_previousState);
+    }
     default:
         return NULL;
     }

+ 25 - 1
gameplay/src/Control.h

@@ -57,6 +57,11 @@ public:
          * State of a control that has been disabled.
          */
         DISABLED = 0x08,
+
+        /**
+         * When a mouse is in use, the state of a control the cursor is over.
+         */
+        HOVER = 0x10,
     };
 
     /**
@@ -135,6 +140,16 @@ public:
              * Event triggered when a control is clicked with the right mouse button.
              */
             RIGHT_CLICK     = 0x40,
+
+            /**
+             * Event triggered when a mouse cursor enters a control.
+             */
+            ENTER           = 0x80,
+
+            /**
+             * Event triggered when a mouse cursor leaves a control.
+             */
+            LEAVE           = 0x100,
         };
     
         /*
@@ -155,7 +170,7 @@ public:
      * @script{ignore}
      * A constant used for setting themed attributes on all control states simultaneously.
      */
-    static const unsigned char STATE_ALL = NORMAL | FOCUS | ACTIVE | DISABLED;
+    static const unsigned char STATE_ALL = NORMAL | FOCUS | ACTIVE | DISABLED | HOVER;
 
     /**
      * Position animation property. Data = x, y
@@ -615,6 +630,14 @@ public:
      */
     bool isVisible() const;
 
+    /**
+     * Gets whether this control is in focus.
+     * Note that a control's state can be HOVER while the control is in focus.
+     * When the cursor leaves the control, it will return to the FOCUS state.
+     * This method will still return true in this case.
+     */
+    bool isInFocus() const;
+
     /**
      * Set the opacity of this control.
      *
@@ -1095,6 +1118,7 @@ private:
     
     bool _styleOverridden;
     Theme::Skin* _skin;
+    State _previousState;
 };
 
 }

+ 3 - 2
gameplay/src/Form.cpp

@@ -670,10 +670,11 @@ bool Form::keyEventInternal(Keyboard::KeyEvent evt, int key)
 
 static bool shouldPropagateMouseEvent(Control::State state, Mouse::MouseEvent evt, const Rectangle& bounds, int x, int y)
 {
-    return (state == Control::FOCUS ||
+    return (state != Control::NORMAL ||
             ((evt == Mouse::MOUSE_PRESS_LEFT_BUTTON ||
               evt == Mouse::MOUSE_PRESS_MIDDLE_BUTTON ||
               evt == Mouse::MOUSE_PRESS_RIGHT_BUTTON ||
+              evt == Mouse::MOUSE_MOVE ||
               evt == Mouse::MOUSE_WHEEL) &&
                 x >= bounds.x &&
                 x <= bounds.x + bounds.width &&
@@ -726,7 +727,7 @@ void Form::gamepadEventInternal(Gamepad::GamepadEvent evt, Gamepad* gamepad, uns
         Form* form = __forms[i];
         GP_ASSERT(form);
 
-        if (form->isEnabled() && form->isVisible() && form->getState() == FOCUS)
+        if (form->isEnabled() && form->isVisible() && form->isInFocus())
         {
             if (form->gamepadEvent(evt, gamepad, analogIndex))
                 return;

+ 26 - 11
gameplay/src/Slider.cpp

@@ -144,13 +144,22 @@ bool Slider::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contac
     case Touch::TOUCH_PRESS:
         if (_contactIndex != INVALID_CONTACT_INDEX)
             return false;
-        _state = Control::ACTIVE;
-        _originalX = x;
-        _originalValue = _value;
-        _originalConsumeInputEvents = _consumeInputEvents;
-        _moveCancelled = false;
-        
-        // Fall through to calculate new value.
+        else if (x > _clipBounds.x && x <= _clipBounds.x + _clipBounds.width &&
+            y > _clipBounds.y && y <= _clipBounds.y + _clipBounds.height)
+        {
+            _state = Control::ACTIVE;
+            _originalX = x;
+            _originalValue = _value;
+            _originalConsumeInputEvents = _consumeInputEvents;
+            _moveCancelled = false;
+            // Fall through to calculate new value.
+        }
+        else
+        {
+            _state = NORMAL;
+            _dirty = true;
+            break;
+        }
     case Touch::TOUCH_MOVE:
     
         if (evt != Touch::TOUCH_PRESS && _contactIndex != (int)contactIndex)
@@ -246,14 +255,14 @@ bool Slider::mouseEvent(Mouse::MouseEvent evt, int x, int y, int wheelDelta)
             return touchEvent(Touch::TOUCH_PRESS, x, y, 0);
 
         case Mouse::MOUSE_MOVE:
-            return touchEvent(Touch::TOUCH_MOVE, x, y, 0);
+            return Control::mouseEvent(evt, x, y, 0);
 
         case Mouse::MOUSE_RELEASE_LEFT_BUTTON:
             return touchEvent(Touch::TOUCH_RELEASE, x, y, 0);
 
         case Mouse::MOUSE_WHEEL:
         {
-            if (_state == FOCUS || _state == ACTIVE)
+            if ((isInFocus() && _state == HOVER) || _state == ACTIVE)
             {
                 float total = _max - _min;
                 float oldValue = _value;
@@ -264,13 +273,19 @@ bool Slider::mouseEvent(Mouse::MouseEvent evt, int x, int y, int wheelDelta)
                 else if (_value < _min)
                     _value = _min;
 
+                if (_step > 0.0f)
+                {            
+                    int numSteps = round(_value / _step);
+                    _value = _step * numSteps;
+                }
+
                 if (_value != oldValue)
                 {
                     notifyListeners(Control::Listener::VALUE_CHANGED);
                 }
 
                 _dirty = true;
-                return _consumeInputEvents;
+                return true;
             }
             break;
         }
@@ -351,7 +366,7 @@ bool Slider::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned
         {
             _selectButtonDown = false;
 
-            if (_state == FOCUS)
+            if (isInFocus())
                 setState(ACTIVE);
             else if (_state == ACTIVE)
                 setState(FOCUS);

+ 2 - 2
gameplay/src/TextBox.cpp

@@ -105,7 +105,7 @@ bool TextBox::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int conta
 
 bool TextBox::keyEvent(Keyboard::KeyEvent evt, int key)
 {
-    if (_state == FOCUS)
+    if (isInFocus())
     {
         switch (evt)
         {
@@ -326,7 +326,7 @@ void TextBox::update(const Control* container, const Vector2& offset)
 
 void TextBox::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)
 {
-    if (_caretImage && (_state == ACTIVE || _state == FOCUS))
+    if (_caretImage && (_state == ACTIVE || isInFocus()))
     {
         // Draw the cursor at its current location.
         const Rectangle& region = _caretImage->getRegion();

+ 27 - 8
gameplay/src/Theme.cpp

@@ -161,6 +161,7 @@ Theme* Theme::create(const char* url)
             Theme::Style::Overlay* focus = NULL;
             Theme::Style::Overlay* active = NULL;
             Theme::Style::Overlay* disabled = NULL;
+            Theme::Style::Overlay* hover = NULL;
 
             // Need to load OVERLAY_NORMAL first so that the other overlays can inherit from it.
             Properties* innerSpace = space->getNextNamespace();
@@ -387,6 +388,26 @@ Theme* Theme::create(const char* url)
                         disabled->setTextRightToLeft(rightToLeft);
                         disabled->setOpacity(opacity);
 
+                        if (font)
+                        {
+                            theme->_fonts.insert(font);
+                            font->release();
+                        }
+                    }
+                    else if (strcmp(innerSpacename, "stateHover") == 0)
+                    {
+                        hover = Theme::Style::Overlay::create();
+                        GP_ASSERT(hover);
+                        hover->setSkin(skin);
+                        hover->setCursor(cursor);
+                        hover->setImageList(imageList);
+                        hover->setTextColor(textColor);
+                        hover->setFont(font);
+                        hover->setFontSize(fontSize);
+                        hover->setTextAlignment(textAlignment);
+                        hover->setTextRightToLeft(rightToLeft);
+                        hover->setOpacity(opacity);
+
                         if (font)
                         {
                             theme->_fonts.insert(font);
@@ -404,19 +425,16 @@ Theme* Theme::create(const char* url)
                 focus->addRef();
             }
 
-            if (!active)
-            {
-                active = normal;
-                active->addRef();
-            }
-
             if (!disabled)
             {
                 disabled = normal;
                 disabled->addRef();
             }
 
-            Theme::Style* s = new Theme::Style(theme, space->getId(), tw, th, margin, padding, normal, focus, active, disabled);
+            // Note: The hover and active states have their overlay left NULL if unspecified.
+            // Events will still be triggered, but a control's overlay will not be changed.
+
+            Theme::Style* s = new Theme::Style(theme, space->getId(), tw, th, margin, padding, normal, focus, active, disabled, hover);
             GP_ASSERT(s);
             theme->_styles.push_back(s);
         }
@@ -458,8 +476,9 @@ Theme::Style* Theme::getEmptyStyle()
         overlay->addRef();
         overlay->addRef();
         overlay->addRef();
+        overlay->addRef();
         emptyStyle = new Theme::Style(const_cast<Theme*>(this), "EMPTY_STYLE", 1.0f / _texture->getWidth(), 1.0f / _texture->getHeight(),
-            Theme::Margin::empty(), Theme::Border::empty(), overlay, overlay, overlay, overlay);
+            Theme::Margin::empty(), Theme::Border::empty(), overlay, overlay, NULL, overlay, NULL);
 
         _styles.push_back(emptyStyle);
     }

+ 8 - 3
gameplay/src/ThemeStyle.cpp

@@ -8,13 +8,16 @@ namespace gameplay
  ****************/
 Theme::Style::Style(Theme* theme, const char* id, float tw, float th,
         const Theme::Margin& margin, const Theme::Padding& padding,
-        Theme::Style::Overlay* normal, Theme::Style::Overlay* focus, Theme::Style::Overlay* active, Theme::Style::Overlay* disabled)
+        Theme::Style::Overlay* normal, Theme::Style::Overlay* focus,
+        Theme::Style::Overlay* active, Theme::Style::Overlay* disabled,
+        Theme::Style::Overlay* hover)
     : _theme(theme), _id(id), _tw(tw), _th(th), _margin(margin), _padding(padding)
 {
     _overlays[OVERLAY_NORMAL] = normal;
     _overlays[OVERLAY_FOCUS] = focus;
     _overlays[OVERLAY_ACTIVE] = active;
     _overlays[OVERLAY_DISABLED] = disabled;
+    _overlays[OVERLAY_HOVER] = hover;
 }
 
 Theme::Style::Style(const Style& copy)
@@ -27,8 +30,10 @@ Theme::Style::Style(const Style& copy)
 
     for (int i = 0; i < OVERLAY_MAX; i++)
     {
-        GP_ASSERT(copy._overlays[i]);
-        _overlays[i] = new Theme::Style::Overlay(*copy._overlays[i]);
+        if (copy._overlays[i])
+            _overlays[i] = new Theme::Style::Overlay(*copy._overlays[i]);
+        else
+            _overlays[i] = NULL;
     }
 }
 

+ 2 - 1
gameplay/src/ThemeStyle.h

@@ -45,6 +45,7 @@ private:
         OVERLAY_FOCUS,
         OVERLAY_ACTIVE,
         OVERLAY_DISABLED,
+        OVERLAY_HOVER,
         OVERLAY_MAX
     };
 
@@ -179,7 +180,7 @@ private:
      */
     Style(Theme* theme, const char* id, float tw, float th,
           const Theme::Margin& margin, const Theme::Padding& padding,
-          Overlay* normal, Overlay* focus, Overlay* active, Overlay* disabled);
+          Overlay* normal, Overlay* focus, Overlay* active, Overlay* disabled, Overlay* hover);
 
     /**
      * Constructor.

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

@@ -74,6 +74,7 @@ void luaRegister_Button()
         {"getZIndex", lua_Button_getZIndex},
         {"isContainer", lua_Button_isContainer},
         {"isEnabled", lua_Button_isEnabled},
+        {"isInFocus", lua_Button_isInFocus},
         {"isVisible", lua_Button_isVisible},
         {"release", lua_Button_release},
         {"removeListener", lua_Button_removeListener},
@@ -2602,6 +2603,41 @@ int lua_Button_isEnabled(lua_State* state)
     return 0;
 }
 
+int lua_Button_isInFocus(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);
+                bool result = instance->isInFocus();
+
+                // Push the return value onto the stack.
+                lua_pushboolean(state, result);
+
+                return 1;
+            }
+
+            lua_pushstring(state, "lua_Button_isInFocus - 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_isVisible(lua_State* state)
 {
     // Get the number of parameters.

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

@@ -54,6 +54,7 @@ int lua_Button_getY(lua_State* state);
 int lua_Button_getZIndex(lua_State* state);
 int lua_Button_isContainer(lua_State* state);
 int lua_Button_isEnabled(lua_State* state);
+int lua_Button_isInFocus(lua_State* state);
 int lua_Button_isVisible(lua_State* state);
 int lua_Button_release(lua_State* state);
 int lua_Button_removeListener(lua_State* state);

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

@@ -78,6 +78,7 @@ void luaRegister_CheckBox()
         {"isChecked", lua_CheckBox_isChecked},
         {"isContainer", lua_CheckBox_isContainer},
         {"isEnabled", lua_CheckBox_isEnabled},
+        {"isInFocus", lua_CheckBox_isInFocus},
         {"isVisible", lua_CheckBox_isVisible},
         {"release", lua_CheckBox_release},
         {"removeListener", lua_CheckBox_removeListener},
@@ -2722,6 +2723,41 @@ int lua_CheckBox_isEnabled(lua_State* state)
     return 0;
 }
 
+int lua_CheckBox_isInFocus(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);
+                bool result = instance->isInFocus();
+
+                // Push the return value onto the stack.
+                lua_pushboolean(state, result);
+
+                return 1;
+            }
+
+            lua_pushstring(state, "lua_CheckBox_isInFocus - 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_isVisible(lua_State* state)
 {
     // Get the number of parameters.

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

@@ -57,6 +57,7 @@ int lua_CheckBox_getZIndex(lua_State* state);
 int lua_CheckBox_isChecked(lua_State* state);
 int lua_CheckBox_isContainer(lua_State* state);
 int lua_CheckBox_isEnabled(lua_State* state);
+int lua_CheckBox_isInFocus(lua_State* state);
 int lua_CheckBox_isVisible(lua_State* state);
 int lua_CheckBox_release(lua_State* state);
 int lua_CheckBox_removeListener(lua_State* state);

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

@@ -76,6 +76,7 @@ void luaRegister_Container()
         {"getPadding", lua_Container_getPadding},
         {"getRefCount", lua_Container_getRefCount},
         {"getScroll", lua_Container_getScroll},
+        {"getScrollWheelRequiresFocus", lua_Container_getScrollWheelRequiresFocus},
         {"getScrollWheelSpeed", lua_Container_getScrollWheelSpeed},
         {"getScrollingFriction", lua_Container_getScrollingFriction},
         {"getSkinColor", lua_Container_getSkinColor},
@@ -93,6 +94,7 @@ void luaRegister_Container()
         {"insertControl", lua_Container_insertControl},
         {"isContainer", lua_Container_isContainer},
         {"isEnabled", lua_Container_isEnabled},
+        {"isInFocus", lua_Container_isInFocus},
         {"isScrollBarsAutoHide", lua_Container_isScrollBarsAutoHide},
         {"isScrolling", lua_Container_isScrolling},
         {"isVisible", lua_Container_isVisible},
@@ -122,6 +124,7 @@ void luaRegister_Container()
         {"setPosition", lua_Container_setPosition},
         {"setScroll", lua_Container_setScroll},
         {"setScrollBarsAutoHide", lua_Container_setScrollBarsAutoHide},
+        {"setScrollWheelRequiresFocus", lua_Container_setScrollWheelRequiresFocus},
         {"setScrollWheelSpeed", lua_Container_setScrollWheelSpeed},
         {"setScrollingFriction", lua_Container_setScrollingFriction},
         {"setSize", lua_Container_setSize},
@@ -2172,6 +2175,41 @@ int lua_Container_getScroll(lua_State* state)
     return 0;
 }
 
+int lua_Container_getScrollWheelRequiresFocus(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);
+                bool result = instance->getScrollWheelRequiresFocus();
+
+                // Push the return value onto the stack.
+                lua_pushboolean(state, result);
+
+                return 1;
+            }
+
+            lua_pushstring(state, "lua_Container_getScrollWheelRequiresFocus - 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_getScrollWheelSpeed(lua_State* state)
 {
     // Get the number of parameters.
@@ -2946,6 +2984,41 @@ int lua_Container_isEnabled(lua_State* state)
     return 0;
 }
 
+int lua_Container_isInFocus(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);
+                bool result = instance->isInFocus();
+
+                // Push the return value onto the stack.
+                lua_pushboolean(state, result);
+
+                return 1;
+            }
+
+            lua_pushstring(state, "lua_Container_isInFocus - 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_isScrollBarsAutoHide(lua_State* state)
 {
     // Get the number of parameters.
@@ -4333,6 +4406,42 @@ int lua_Container_setScrollBarsAutoHide(lua_State* state)
     return 0;
 }
 
+int lua_Container_setScrollWheelRequiresFocus(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 2:
+        {
+            if ((lua_type(state, 1) == LUA_TUSERDATA) &&
+                lua_type(state, 2) == LUA_TBOOLEAN)
+            {
+                // Get parameter 1 off the stack.
+                bool param1 = gameplay::ScriptUtil::luaCheckBool(state, 2);
+
+                Container* instance = getInstance(state);
+                instance->setScrollWheelRequiresFocus(param1);
+                
+                return 0;
+            }
+
+            lua_pushstring(state, "lua_Container_setScrollWheelRequiresFocus - Failed to match the given parameters to a valid function signature.");
+            lua_error(state);
+            break;
+        }
+        default:
+        {
+            lua_pushstring(state, "Invalid number of parameters (expected 2).");
+            lua_error(state);
+            break;
+        }
+    }
+    return 0;
+}
+
 int lua_Container_setScrollWheelSpeed(lua_State* state)
 {
     // Get the number of parameters.

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

@@ -44,6 +44,7 @@ int lua_Container_getOpacity(lua_State* state);
 int lua_Container_getPadding(lua_State* state);
 int lua_Container_getRefCount(lua_State* state);
 int lua_Container_getScroll(lua_State* state);
+int lua_Container_getScrollWheelRequiresFocus(lua_State* state);
 int lua_Container_getScrollWheelSpeed(lua_State* state);
 int lua_Container_getScrollingFriction(lua_State* state);
 int lua_Container_getSkinColor(lua_State* state);
@@ -61,6 +62,7 @@ int lua_Container_getZIndex(lua_State* state);
 int lua_Container_insertControl(lua_State* state);
 int lua_Container_isContainer(lua_State* state);
 int lua_Container_isEnabled(lua_State* state);
+int lua_Container_isInFocus(lua_State* state);
 int lua_Container_isScrollBarsAutoHide(lua_State* state);
 int lua_Container_isScrolling(lua_State* state);
 int lua_Container_isVisible(lua_State* state);
@@ -90,6 +92,7 @@ int lua_Container_setPadding(lua_State* state);
 int lua_Container_setPosition(lua_State* state);
 int lua_Container_setScroll(lua_State* state);
 int lua_Container_setScrollBarsAutoHide(lua_State* state);
+int lua_Container_setScrollWheelRequiresFocus(lua_State* state);
 int lua_Container_setScrollWheelSpeed(lua_State* state);
 int lua_Container_setScrollingFriction(lua_State* state);
 int lua_Container_setSize(lua_State* state);

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

@@ -71,6 +71,7 @@ void luaRegister_Control()
         {"getZIndex", lua_Control_getZIndex},
         {"isContainer", lua_Control_isContainer},
         {"isEnabled", lua_Control_isEnabled},
+        {"isInFocus", lua_Control_isInFocus},
         {"isVisible", lua_Control_isVisible},
         {"release", lua_Control_release},
         {"removeListener", lua_Control_removeListener},
@@ -2597,6 +2598,41 @@ int lua_Control_isEnabled(lua_State* state)
     return 0;
 }
 
+int lua_Control_isInFocus(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);
+                bool result = instance->isInFocus();
+
+                // Push the return value onto the stack.
+                lua_pushboolean(state, result);
+
+                return 1;
+            }
+
+            lua_pushstring(state, "lua_Control_isInFocus - 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_isVisible(lua_State* state)
 {
     // Get the number of parameters.

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

@@ -54,6 +54,7 @@ int lua_Control_getY(lua_State* state);
 int lua_Control_getZIndex(lua_State* state);
 int lua_Control_isContainer(lua_State* state);
 int lua_Control_isEnabled(lua_State* state);
+int lua_Control_isInFocus(lua_State* state);
 int lua_Control_isVisible(lua_State* state);
 int lua_Control_release(lua_State* state);
 int lua_Control_removeListener(lua_State* state);

+ 10 - 0
gameplay/src/lua/lua_ControlListenerEventType.cpp

@@ -13,6 +13,8 @@ static const char* luaEnumString_ControlListenerEventType_VALUE_CHANGED = "VALUE
 static const char* luaEnumString_ControlListenerEventType_TEXT_CHANGED = "TEXT_CHANGED";
 static const char* luaEnumString_ControlListenerEventType_MIDDLE_CLICK = "MIDDLE_CLICK";
 static const char* luaEnumString_ControlListenerEventType_RIGHT_CLICK = "RIGHT_CLICK";
+static const char* luaEnumString_ControlListenerEventType_ENTER = "ENTER";
+static const char* luaEnumString_ControlListenerEventType_LEAVE = "LEAVE";
 
 Control::Listener::EventType lua_enumFromString_ControlListenerEventType(const char* s)
 {
@@ -30,6 +32,10 @@ Control::Listener::EventType lua_enumFromString_ControlListenerEventType(const c
         return Control::Listener::MIDDLE_CLICK;
     if (strcmp(s, luaEnumString_ControlListenerEventType_RIGHT_CLICK) == 0)
         return Control::Listener::RIGHT_CLICK;
+    if (strcmp(s, luaEnumString_ControlListenerEventType_ENTER) == 0)
+        return Control::Listener::ENTER;
+    if (strcmp(s, luaEnumString_ControlListenerEventType_LEAVE) == 0)
+        return Control::Listener::LEAVE;
     GP_ERROR("Invalid enumeration value '%s' for enumeration Control::Listener::EventType.", s);
     return Control::Listener::PRESS;
 }
@@ -50,6 +56,10 @@ const char* lua_stringFromEnum_ControlListenerEventType(Control::Listener::Event
         return luaEnumString_ControlListenerEventType_MIDDLE_CLICK;
     if (e == Control::Listener::RIGHT_CLICK)
         return luaEnumString_ControlListenerEventType_RIGHT_CLICK;
+    if (e == Control::Listener::ENTER)
+        return luaEnumString_ControlListenerEventType_ENTER;
+    if (e == Control::Listener::LEAVE)
+        return luaEnumString_ControlListenerEventType_LEAVE;
     GP_ERROR("Invalid enumeration value '%d' for enumeration Control::Listener::EventType.", e);
     return enumStringEmpty;
 }

+ 5 - 0
gameplay/src/lua/lua_ControlState.cpp

@@ -10,6 +10,7 @@ static const char* luaEnumString_ControlState_NORMAL = "NORMAL";
 static const char* luaEnumString_ControlState_FOCUS = "FOCUS";
 static const char* luaEnumString_ControlState_ACTIVE = "ACTIVE";
 static const char* luaEnumString_ControlState_DISABLED = "DISABLED";
+static const char* luaEnumString_ControlState_HOVER = "HOVER";
 
 Control::State lua_enumFromString_ControlState(const char* s)
 {
@@ -21,6 +22,8 @@ Control::State lua_enumFromString_ControlState(const char* s)
         return Control::ACTIVE;
     if (strcmp(s, luaEnumString_ControlState_DISABLED) == 0)
         return Control::DISABLED;
+    if (strcmp(s, luaEnumString_ControlState_HOVER) == 0)
+        return Control::HOVER;
     GP_ERROR("Invalid enumeration value '%s' for enumeration Control::State.", s);
     return Control::NORMAL;
 }
@@ -35,6 +38,8 @@ const char* lua_stringFromEnum_ControlState(Control::State e)
         return luaEnumString_ControlState_ACTIVE;
     if (e == Control::DISABLED)
         return luaEnumString_ControlState_DISABLED;
+    if (e == Control::HOVER)
+        return luaEnumString_ControlState_HOVER;
     GP_ERROR("Invalid enumeration value '%d' for enumeration Control::State.", e);
     return enumStringEmpty;
 }

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

@@ -80,6 +80,7 @@ void luaRegister_Form()
         {"getPadding", lua_Form_getPadding},
         {"getRefCount", lua_Form_getRefCount},
         {"getScroll", lua_Form_getScroll},
+        {"getScrollWheelRequiresFocus", lua_Form_getScrollWheelRequiresFocus},
         {"getScrollWheelSpeed", lua_Form_getScrollWheelSpeed},
         {"getScrollingFriction", lua_Form_getScrollingFriction},
         {"getSkinColor", lua_Form_getSkinColor},
@@ -98,6 +99,7 @@ void luaRegister_Form()
         {"insertControl", lua_Form_insertControl},
         {"isContainer", lua_Form_isContainer},
         {"isEnabled", lua_Form_isEnabled},
+        {"isInFocus", lua_Form_isInFocus},
         {"isScrollBarsAutoHide", lua_Form_isScrollBarsAutoHide},
         {"isScrolling", lua_Form_isScrolling},
         {"isVisible", lua_Form_isVisible},
@@ -128,6 +130,7 @@ void luaRegister_Form()
         {"setPosition", lua_Form_setPosition},
         {"setScroll", lua_Form_setScroll},
         {"setScrollBarsAutoHide", lua_Form_setScrollBarsAutoHide},
+        {"setScrollWheelRequiresFocus", lua_Form_setScrollWheelRequiresFocus},
         {"setScrollWheelSpeed", lua_Form_setScrollWheelSpeed},
         {"setScrollingFriction", lua_Form_setScrollingFriction},
         {"setSize", lua_Form_setSize},
@@ -2212,6 +2215,41 @@ int lua_Form_getScroll(lua_State* state)
     return 0;
 }
 
+int lua_Form_getScrollWheelRequiresFocus(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);
+                bool result = instance->getScrollWheelRequiresFocus();
+
+                // Push the return value onto the stack.
+                lua_pushboolean(state, result);
+
+                return 1;
+            }
+
+            lua_pushstring(state, "lua_Form_getScrollWheelRequiresFocus - 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_getScrollWheelSpeed(lua_State* state)
 {
     // Get the number of parameters.
@@ -3030,6 +3068,41 @@ int lua_Form_isEnabled(lua_State* state)
     return 0;
 }
 
+int lua_Form_isInFocus(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);
+                bool result = instance->isInFocus();
+
+                // Push the return value onto the stack.
+                lua_pushboolean(state, result);
+
+                return 1;
+            }
+
+            lua_pushstring(state, "lua_Form_isInFocus - 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_isScrollBarsAutoHide(lua_State* state)
 {
     // Get the number of parameters.
@@ -4459,6 +4532,42 @@ int lua_Form_setScrollBarsAutoHide(lua_State* state)
     return 0;
 }
 
+int lua_Form_setScrollWheelRequiresFocus(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 2:
+        {
+            if ((lua_type(state, 1) == LUA_TUSERDATA) &&
+                lua_type(state, 2) == LUA_TBOOLEAN)
+            {
+                // Get parameter 1 off the stack.
+                bool param1 = gameplay::ScriptUtil::luaCheckBool(state, 2);
+
+                Form* instance = getInstance(state);
+                instance->setScrollWheelRequiresFocus(param1);
+                
+                return 0;
+            }
+
+            lua_pushstring(state, "lua_Form_setScrollWheelRequiresFocus - Failed to match the given parameters to a valid function signature.");
+            lua_error(state);
+            break;
+        }
+        default:
+        {
+            lua_pushstring(state, "Invalid number of parameters (expected 2).");
+            lua_error(state);
+            break;
+        }
+    }
+    return 0;
+}
+
 int lua_Form_setScrollWheelSpeed(lua_State* state)
 {
     // Get the number of parameters.

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

@@ -45,6 +45,7 @@ int lua_Form_getOpacity(lua_State* state);
 int lua_Form_getPadding(lua_State* state);
 int lua_Form_getRefCount(lua_State* state);
 int lua_Form_getScroll(lua_State* state);
+int lua_Form_getScrollWheelRequiresFocus(lua_State* state);
 int lua_Form_getScrollWheelSpeed(lua_State* state);
 int lua_Form_getScrollingFriction(lua_State* state);
 int lua_Form_getSkinColor(lua_State* state);
@@ -63,6 +64,7 @@ int lua_Form_getZIndex(lua_State* state);
 int lua_Form_insertControl(lua_State* state);
 int lua_Form_isContainer(lua_State* state);
 int lua_Form_isEnabled(lua_State* state);
+int lua_Form_isInFocus(lua_State* state);
 int lua_Form_isScrollBarsAutoHide(lua_State* state);
 int lua_Form_isScrolling(lua_State* state);
 int lua_Form_isVisible(lua_State* state);
@@ -93,6 +95,7 @@ int lua_Form_setPadding(lua_State* state);
 int lua_Form_setPosition(lua_State* state);
 int lua_Form_setScroll(lua_State* state);
 int lua_Form_setScrollBarsAutoHide(lua_State* state);
+int lua_Form_setScrollWheelRequiresFocus(lua_State* state);
 int lua_Form_setScrollWheelSpeed(lua_State* state);
 int lua_Form_setScrollingFriction(lua_State* state);
 int lua_Form_setSize(lua_State* state);

+ 3 - 0
gameplay/src/lua/lua_Global.cpp

@@ -225,6 +225,8 @@ void luaRegister_lua_Global()
         gameplay::ScriptUtil::registerConstantString("TEXT_CHANGED", "TEXT_CHANGED", scopePath);
         gameplay::ScriptUtil::registerConstantString("MIDDLE_CLICK", "MIDDLE_CLICK", scopePath);
         gameplay::ScriptUtil::registerConstantString("RIGHT_CLICK", "RIGHT_CLICK", scopePath);
+        gameplay::ScriptUtil::registerConstantString("ENTER", "ENTER", scopePath);
+        gameplay::ScriptUtil::registerConstantString("LEAVE", "LEAVE", scopePath);
     }
 
     // Register enumeration Control::State.
@@ -235,6 +237,7 @@ void luaRegister_lua_Global()
         gameplay::ScriptUtil::registerConstantString("FOCUS", "FOCUS", scopePath);
         gameplay::ScriptUtil::registerConstantString("ACTIVE", "ACTIVE", scopePath);
         gameplay::ScriptUtil::registerConstantString("DISABLED", "DISABLED", scopePath);
+        gameplay::ScriptUtil::registerConstantString("HOVER", "HOVER", scopePath);
     }
 
     // Register enumeration Curve::InterpolationType.

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

@@ -78,6 +78,7 @@ void luaRegister_ImageControl()
         {"getZIndex", lua_ImageControl_getZIndex},
         {"isContainer", lua_ImageControl_isContainer},
         {"isEnabled", lua_ImageControl_isEnabled},
+        {"isInFocus", lua_ImageControl_isInFocus},
         {"isVisible", lua_ImageControl_isVisible},
         {"release", lua_ImageControl_release},
         {"removeListener", lua_ImageControl_removeListener},
@@ -2732,6 +2733,41 @@ int lua_ImageControl_isEnabled(lua_State* state)
     return 0;
 }
 
+int lua_ImageControl_isInFocus(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);
+                bool result = instance->isInFocus();
+
+                // Push the return value onto the stack.
+                lua_pushboolean(state, result);
+
+                return 1;
+            }
+
+            lua_pushstring(state, "lua_ImageControl_isInFocus - 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_isVisible(lua_State* state)
 {
     // Get the number of parameters.

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

@@ -57,6 +57,7 @@ int lua_ImageControl_getY(lua_State* state);
 int lua_ImageControl_getZIndex(lua_State* state);
 int lua_ImageControl_isContainer(lua_State* state);
 int lua_ImageControl_isEnabled(lua_State* state);
+int lua_ImageControl_isInFocus(lua_State* state);
 int lua_ImageControl_isVisible(lua_State* state);
 int lua_ImageControl_release(lua_State* state);
 int lua_ImageControl_removeListener(lua_State* state);

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

@@ -76,6 +76,7 @@ void luaRegister_Joystick()
         {"getZIndex", lua_Joystick_getZIndex},
         {"isContainer", lua_Joystick_isContainer},
         {"isEnabled", lua_Joystick_isEnabled},
+        {"isInFocus", lua_Joystick_isInFocus},
         {"isRelative", lua_Joystick_isRelative},
         {"isVisible", lua_Joystick_isVisible},
         {"release", lua_Joystick_release},
@@ -2774,6 +2775,41 @@ int lua_Joystick_isEnabled(lua_State* state)
     return 0;
 }
 
+int lua_Joystick_isInFocus(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);
+                bool result = instance->isInFocus();
+
+                // Push the return value onto the stack.
+                lua_pushboolean(state, result);
+
+                return 1;
+            }
+
+            lua_pushstring(state, "lua_Joystick_isInFocus - 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_isRelative(lua_State* state)
 {
     // Get the number of parameters.

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

@@ -58,6 +58,7 @@ int lua_Joystick_getY(lua_State* state);
 int lua_Joystick_getZIndex(lua_State* state);
 int lua_Joystick_isContainer(lua_State* state);
 int lua_Joystick_isEnabled(lua_State* state);
+int lua_Joystick_isInFocus(lua_State* state);
 int lua_Joystick_isRelative(lua_State* state);
 int lua_Joystick_isVisible(lua_State* state);
 int lua_Joystick_release(lua_State* state);

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

@@ -73,6 +73,7 @@ void luaRegister_Label()
         {"getZIndex", lua_Label_getZIndex},
         {"isContainer", lua_Label_isContainer},
         {"isEnabled", lua_Label_isEnabled},
+        {"isInFocus", lua_Label_isInFocus},
         {"isVisible", lua_Label_isVisible},
         {"release", lua_Label_release},
         {"removeListener", lua_Label_removeListener},
@@ -2636,6 +2637,41 @@ int lua_Label_isEnabled(lua_State* state)
     return 0;
 }
 
+int lua_Label_isInFocus(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);
+                bool result = instance->isInFocus();
+
+                // Push the return value onto the stack.
+                lua_pushboolean(state, result);
+
+                return 1;
+            }
+
+            lua_pushstring(state, "lua_Label_isInFocus - 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_isVisible(lua_State* state)
 {
     // Get the number of parameters.

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

@@ -55,6 +55,7 @@ int lua_Label_getY(lua_State* state);
 int lua_Label_getZIndex(lua_State* state);
 int lua_Label_isContainer(lua_State* state);
 int lua_Label_isEnabled(lua_State* state);
+int lua_Label_isInFocus(lua_State* state);
 int lua_Label_isVisible(lua_State* state);
 int lua_Label_release(lua_State* state);
 int lua_Label_removeListener(lua_State* state);

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

@@ -78,6 +78,7 @@ void luaRegister_RadioButton()
         {"getZIndex", lua_RadioButton_getZIndex},
         {"isContainer", lua_RadioButton_isContainer},
         {"isEnabled", lua_RadioButton_isEnabled},
+        {"isInFocus", lua_RadioButton_isInFocus},
         {"isSelected", lua_RadioButton_isSelected},
         {"isVisible", lua_RadioButton_isVisible},
         {"release", lua_RadioButton_release},
@@ -2724,6 +2725,41 @@ int lua_RadioButton_isEnabled(lua_State* state)
     return 0;
 }
 
+int lua_RadioButton_isInFocus(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);
+                bool result = instance->isInFocus();
+
+                // Push the return value onto the stack.
+                lua_pushboolean(state, result);
+
+                return 1;
+            }
+
+            lua_pushstring(state, "lua_RadioButton_isInFocus - 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_isSelected(lua_State* state)
 {
     // Get the number of parameters.

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

@@ -57,6 +57,7 @@ int lua_RadioButton_getY(lua_State* state);
 int lua_RadioButton_getZIndex(lua_State* state);
 int lua_RadioButton_isContainer(lua_State* state);
 int lua_RadioButton_isEnabled(lua_State* state);
+int lua_RadioButton_isInFocus(lua_State* state);
 int lua_RadioButton_isSelected(lua_State* state);
 int lua_RadioButton_isVisible(lua_State* state);
 int lua_RadioButton_release(lua_State* state);

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

@@ -80,6 +80,7 @@ void luaRegister_Slider()
         {"getZIndex", lua_Slider_getZIndex},
         {"isContainer", lua_Slider_isContainer},
         {"isEnabled", lua_Slider_isEnabled},
+        {"isInFocus", lua_Slider_isInFocus},
         {"isValueTextVisible", lua_Slider_isValueTextVisible},
         {"isVisible", lua_Slider_isVisible},
         {"release", lua_Slider_release},
@@ -2861,6 +2862,41 @@ int lua_Slider_isEnabled(lua_State* state)
     return 0;
 }
 
+int lua_Slider_isInFocus(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);
+                bool result = instance->isInFocus();
+
+                // Push the return value onto the stack.
+                lua_pushboolean(state, result);
+
+                return 1;
+            }
+
+            lua_pushstring(state, "lua_Slider_isInFocus - 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_isValueTextVisible(lua_State* state)
 {
     // Get the number of parameters.

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

@@ -61,6 +61,7 @@ int lua_Slider_getY(lua_State* state);
 int lua_Slider_getZIndex(lua_State* state);
 int lua_Slider_isContainer(lua_State* state);
 int lua_Slider_isEnabled(lua_State* state);
+int lua_Slider_isInFocus(lua_State* state);
 int lua_Slider_isValueTextVisible(lua_State* state);
 int lua_Slider_isVisible(lua_State* state);
 int lua_Slider_release(lua_State* state);

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

@@ -75,6 +75,7 @@ void luaRegister_TextBox()
         {"getZIndex", lua_TextBox_getZIndex},
         {"isContainer", lua_TextBox_isContainer},
         {"isEnabled", lua_TextBox_isEnabled},
+        {"isInFocus", lua_TextBox_isInFocus},
         {"isVisible", lua_TextBox_isVisible},
         {"release", lua_TextBox_release},
         {"removeListener", lua_TextBox_removeListener},
@@ -2673,6 +2674,41 @@ int lua_TextBox_isEnabled(lua_State* state)
     return 0;
 }
 
+int lua_TextBox_isInFocus(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);
+                bool result = instance->isInFocus();
+
+                // Push the return value onto the stack.
+                lua_pushboolean(state, result);
+
+                return 1;
+            }
+
+            lua_pushstring(state, "lua_TextBox_isInFocus - 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_isVisible(lua_State* state)
 {
     // Get the number of parameters.

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

@@ -56,6 +56,7 @@ int lua_TextBox_getY(lua_State* state);
 int lua_TextBox_getZIndex(lua_State* state);
 int lua_TextBox_isContainer(lua_State* state);
 int lua_TextBox_isEnabled(lua_State* state);
+int lua_TextBox_isInFocus(lua_State* state);
 int lua_TextBox_isVisible(lua_State* state);
 int lua_TextBox_release(lua_State* state);
 int lua_TextBox_removeListener(lua_State* state);

+ 21 - 9
samples/browser/res/common/default.theme

@@ -231,10 +231,10 @@ theme mainMenu
             textAlignment = ALIGN_TOP_LEFT
         }
     }
-
-    style basic
-    {
-        stateNormal
+	
+	style basicContainer
+	{
+		stateNormal
         {
             skin = mainNormal
             imageList = normalImages
@@ -244,22 +244,26 @@ theme mainMenu
             fontSize = 18
             textAlignment = ALIGN_VCENTER_HCENTER
         }
-		
-        stateActive
-        {
-            imageList = activeImages
-        }
 
 		stateFocus
 		{
 			skin = mainFocus
 			imageList = focusImages
+			textColor = #aaa000ff
 		}
 		
 		stateDisabled
 		{
 			opacity = 0.6
 		}
+	}
+
+    style basic : basicContainer
+    {
+        stateActive
+        {
+            imageList = activeImages
+        }
     }
 
     style topLeftAlignedEntry : basic
@@ -295,6 +299,14 @@ theme mainMenu
             skin = mainActive
         }
     }
+	
+	style buttonHover : buttonStyle
+	{
+		stateHover
+		{
+			textColor = #ff0000ff
+		}
+	}
 
     style noBorder
     {

+ 3 - 3
samples/browser/res/common/forms/formBasicControls.form

@@ -2,7 +2,7 @@ form basicControls
 {
     theme = res/common/default.theme
     layout = LAYOUT_ABSOLUTE
-    style = basic
+    style = basicContainer
     size = 600, 600
 	consumeInputEvents = true
     	
@@ -16,10 +16,10 @@ form basicControls
 		
 	button testButton
 	{
-		style = buttonStyle
+		style = buttonHover
 		position = 20, 80
 		size = 200, 100
-		text = This is a button.
+		text = This is a button with a hover state.
 	}
 
 	checkbox testCheckbox

+ 1 - 1
samples/browser/res/common/forms/formFlowLayout.form

@@ -2,7 +2,7 @@ form flowLayout
 {
     theme = res/common/default.theme
     layout = LAYOUT_FLOW
-    style = basic
+    style = basicContainer
     size = 600, 600
     scroll = SCROLL_BOTH
 	consumeInputEvents = true

+ 1 - 1
samples/browser/res/common/forms/formScrolling.form

@@ -2,7 +2,7 @@ form scrolling
 {
     theme = res/common/default.theme
     layout = LAYOUT_VERTICAL
-    style = basic
+    style = basicContainer
     size = 600, 600
     scroll = SCROLL_BOTH
     scrollBarsAutoHide = true

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

@@ -2,7 +2,7 @@ form formSelect
 {
     theme = res/common/default.theme
     layout = LAYOUT_VERTICAL
-    style = basic
+    style = basicContainer
     alignment = ALIGN_TOP_LEFT
     width = 200
     autoHeight = true

+ 1 - 1
samples/browser/res/common/forms/formVerticalLayout.form

@@ -2,7 +2,7 @@ form verticalLayout
 {
     theme = res/common/default.theme
     layout = LAYOUT_VERTICAL
-    style = basic
+    style = basicContainer
     size = 600, 600
     scroll = SCROLL_BOTH
 	consumeInputEvents = true

+ 1 - 1
samples/browser/res/common/forms/formZOrder.form

@@ -1,7 +1,7 @@
 form zOrder
 {
     theme = res/common/default.theme
-    style = basic
+    style = basicContainer
     size = 600, 600
     scroll = SCROLL_BOTH
 	consumeInputEvents = true

+ 1 - 0
samples/browser/res/common/text.form

@@ -6,6 +6,7 @@ form textTest
     width = 240
     layout = LAYOUT_VERTICAL
     scroll = SCROLL_VERTICAL
+	consumeInputEvents = true
 
     label topLabel
     {

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

@@ -158,7 +158,7 @@ void FormsSample::formChanged()
 
 void FormsSample::createSampleForm(Theme* theme)
 {
-    Form* form = Form::create("testForm", theme->getStyle("buttonStyle"));
+    Form* form = Form::create("testForm", theme->getStyle("basicContainer"));
     form->setSize(600, 600);
 
     Label* label = Label::create("testLabel", theme->getStyle("iconNoBorder"));

+ 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, unsigned int analogIndex)
+void Sample::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad)
 {
 }
 

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

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

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

@@ -28,7 +28,7 @@ void SamplesGame::initialize()
 
     // Construct a form for selecting which sample to run.
     Theme* theme = Theme::create("res/common/default.theme");
-    Theme::Style* formStyle = theme->getStyle("basic");
+    Theme::Style* formStyle = theme->getStyle("basicContainer");
     Theme::Style* buttonStyle = theme->getStyle("buttonStyle");
     Theme::Style* titleStyle = theme->getStyle("title");
 
@@ -39,6 +39,7 @@ void SamplesGame::initialize()
     _sampleSelectForm->setAutoHeight(true);
     _sampleSelectForm->setWidth(200.0f);
     _sampleSelectForm->setScroll(Container::SCROLL_VERTICAL);
+    _sampleSelectForm->setConsumeInputEvents(true);
 
     const size_t size = _samples->size();
     for (size_t i = 0; i < size; ++i)
@@ -97,6 +98,13 @@ void SamplesGame::update(float elapsedTime)
 {
     if (_activeSample)
     {
+        Gamepad* gamepad = getGamepad(0);
+        if (gamepad && gamepad->isButtonDown(Gamepad::BUTTON_MENU2))
+        {
+            exitActiveSample();
+            return;
+        }
+
         getScriptController()->executeFunction<void>("camera_update", "f", elapsedTime);
         _activeSample->update(elapsedTime);
         return;
@@ -249,6 +257,7 @@ void SamplesGame::exitActiveSample()
         SAFE_DELETE(_activeSample);
 
         _sampleSelectForm->setEnabled(true);
+        _sampleSelectForm->setState(Control::FOCUS);
     }
 
     // Reset some game options