Sfoglia il codice sorgente

Merge pull request #479 from blackberry-gaming/next-ablake

Next ablake: Mouse events and more!
Sean Paul Taylor 13 anni fa
parent
commit
8283260c97

+ 1 - 1
gameplay/src/Button.h

@@ -71,7 +71,7 @@ protected:
 
 private:
 
-    /*
+    /**
      * Constructor.
      */
     Button(const Button& copy);

+ 245 - 114
gameplay/src/Container.cpp

@@ -26,11 +26,12 @@ Container::Container()
       _scrollBarLeftCap(NULL), _scrollBarHorizontal(NULL), _scrollBarRightCap(NULL),
       _scroll(SCROLL_NONE), _scrollBarBounds(Rectangle::empty()), _scrollPosition(Vector2::zero()),
       _scrollBarsAutoHide(false), _scrollBarOpacity(1.0f), _scrolling(false),
-       _scrollingFirstX(0), _scrollingFirstY(0),
-      _scrollingLastX(0), _scrollingLastY(0),
+       _scrollingFirstX(0), _scrollingFirstY(0), _scrollingLastX(0), _scrollingLastY(0),
       _scrollingStartTimeX(0), _scrollingStartTimeY(0), _scrollingLastTime(0),
       _scrollingVelocity(Vector2::zero()), _scrollingFriction(1.0f),
-      _scrollingRight(false), _scrollingDown(false), _scrollBarOpacityClip(NULL), _zIndexDefault(0)
+      _scrollingRight(false), _scrollingDown(false),
+      _scrollingMouseVertically(false), _scrollingMouseHorizontally(false),
+      _scrollBarOpacityClip(NULL), _zIndexDefault(0), _totalWidth(0), _totalHeight(0)
 {
 }
 
@@ -108,9 +109,7 @@ void Container::addControls(Theme* theme, Properties* properties)
         }
         else
         {
-            Theme::Style::Overlay* overlay = Theme::Style::Overlay::create();
-            controlStyle = new Theme::Style(theme, "", 1.0f / theme->_texture->getWidth(), 1.0f / theme->_texture->getHeight(),
-                Theme::Margin::empty(), Theme::Border::empty(), overlay, overlay, overlay, overlay);
+            controlStyle = theme->getEmptyStyle();
         }
 
         std::string controlName(controlSpace->getNamespace());
@@ -499,81 +498,15 @@ bool Container::isDirty()
 
 bool Container::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
 {
-    if (!isEnabled())
-    {
-        return false;
-    }
-
-    bool eventConsumed = false;
-    const Theme::Border& border = getBorder(_state);
-    const Theme::Padding& padding = getPadding();
-    float xPos = border.left + padding.left;
-    float yPos = border.top + padding.top;
-
-    Vector2* offset = NULL;
-    if (_scroll != SCROLL_NONE)
-    {
-        offset = &_scrollPosition;
-    }
-
-    std::vector<Control*>::const_iterator it;
-    for (it = _controls.begin(); it < _controls.end(); it++)
-    {
-        Control* control = *it;
-        GP_ASSERT(control);
-        if (!control->isEnabled())
-        {
-            continue;
-        }
-
-        const Rectangle& bounds = control->getBounds();
-        float boundsX = bounds.x;
-        float boundsY = bounds.y;
-        if (offset)
-        {
-            boundsX += offset->x;
-            boundsY += offset->y;
-        }
-
-        if (control->getState() != Control::NORMAL ||
-            (evt == Touch::TOUCH_PRESS &&
-                x >= xPos + boundsX &&
-                x <= xPos + boundsX + bounds.width &&
-                y >= yPos + boundsY &&
-                y <= yPos + boundsY + bounds.height))
-        {
-            // Pass on the event's clip relative to the control.
-            eventConsumed |= control->touchEvent(evt, x - xPos - boundsX, y - yPos - boundsY, contactIndex);
-        }
-    }
-
-    if (!isEnabled())
-    {
-        return (_consumeTouchEvents | eventConsumed);
-    }
-
-    switch (evt)
-    {
-    case Touch::TOUCH_PRESS:
-        setState(Control::FOCUS);
-        break;
-    case Touch::TOUCH_RELEASE:
-        setState(Control::NORMAL);
-        break;
-    }
-
-    if (!eventConsumed && _scroll != SCROLL_NONE)
-    {
-        if (touchEventScroll(evt, x - xPos, y - yPos, contactIndex))
-        {
-            _dirty = true;
-        }
-    }
+    return pointerEvent(false, evt, x, y, (int)contactIndex);
+}
 
-    return (_consumeTouchEvents | eventConsumed);
+bool Container::mouseEvent(Mouse::MouseEvent evt, int x, int y, int wheelDelta)
+{
+    return pointerEvent(true, evt, x, y, wheelDelta);
 }
 
-void Container::keyEvent(Keyboard::KeyEvent evt, int key)
+bool Container::keyEvent(Keyboard::KeyEvent evt, int key)
 {
     std::vector<Control*>::const_iterator it;
     for (it = _controls.begin(); it < _controls.end(); it++)
@@ -587,9 +520,12 @@ void Container::keyEvent(Keyboard::KeyEvent evt, int key)
 
         if (control->isContainer() || control->getState() == Control::FOCUS)
         {
-            control->keyEvent(evt, key);
+            if (control->keyEvent(evt, key))
+                return _consumeInputEvents;
         }
     }
+
+    return false;
 }
 
 bool Container::isContainer()
@@ -632,7 +568,7 @@ Layout::Type Container::getLayoutType(const char* layoutString)
 void Container::updateScroll()
 {
     // Update Time.
-    double lastFrameTime = Game::getGameTime();
+    static double lastFrameTime = Game::getGameTime();
     double frameTime = Game::getGameTime();
     float elapsedTime = (float)(frameTime - lastFrameTime);
     lastFrameTime = frameTime;
@@ -641,8 +577,6 @@ void Container::updateScroll()
     const Theme::Padding& containerPadding = getPadding();
 
     // Calculate total width and height.
-    float totalWidth = 0;
-    float totalHeight = 0;
     std::vector<Control*> controls = getControls();
     unsigned int controlsCount = controls.size();
     for (unsigned int i = 0; i < controlsCount; i++)
@@ -653,15 +587,15 @@ void Container::updateScroll()
         const Theme::Margin& margin = control->getMargin();
 
         float newWidth = bounds.x + bounds.width;
-        if (newWidth > totalWidth)
+        if (newWidth > _totalWidth)
         {
-            totalWidth = newWidth;
+            _totalWidth = newWidth;
         }
 
         float newHeight = bounds.y + bounds.height;
-        if (newHeight > totalHeight)
+        if (newHeight > _totalHeight)
         {
-            totalHeight = newHeight;
+            _totalHeight = newHeight;
         }
     }
 
@@ -690,15 +624,15 @@ void Container::updateScroll()
     }
 
     // Stop scrolling when the far edge is reached.
-    if (-_scrollPosition.x > totalWidth - clipWidth)
+    if (-_scrollPosition.x > _totalWidth - clipWidth)
     {
-        _scrollPosition.x = -(totalWidth - clipWidth);
+        _scrollPosition.x = -(_totalWidth - clipWidth);
         _scrollingVelocity.x = 0;
     }
     
-    if (-_scrollPosition.y > totalHeight - clipHeight)
+    if (-_scrollPosition.y > _totalHeight - clipHeight)
     {
-        _scrollPosition.y = -(totalHeight - clipHeight);
+        _scrollPosition.y = -(_totalHeight - clipHeight);
         _scrollingVelocity.y = 0;
     }
 
@@ -715,15 +649,15 @@ void Container::updateScroll()
     }
 
     float scrollWidth = 0;
-    if (clipWidth < totalWidth)
-        scrollWidth = (clipWidth / totalWidth) * clipWidth;
+    if (clipWidth < _totalWidth)
+        scrollWidth = (clipWidth / _totalWidth) * clipWidth;
 
     float scrollHeight = 0;
-    if (clipHeight < totalHeight)
-        scrollHeight = (clipHeight / totalHeight) * clipHeight;
+    if (clipHeight < _totalHeight)
+        scrollHeight = (clipHeight / _totalHeight) * clipHeight;
 
-    _scrollBarBounds.set(((-_scrollPosition.x) / totalWidth) * clipWidth,
-                         ((-_scrollPosition.y) / totalHeight) * clipHeight,
+    _scrollBarBounds.set(((-_scrollPosition.x) / _totalWidth) * clipWidth,
+                         ((-_scrollPosition.y) / _totalHeight) * clipHeight,
                          scrollWidth, scrollHeight);
 
     // If scroll velocity is 0 and scrollbars are not always visible, trigger fade-out animation.
@@ -731,8 +665,11 @@ void Container::updateScroll()
     {
         float to = 0;
         _scrollBarOpacity = 0.99f;
-        Animation* animation = createAnimationFromTo("scrollbar-fade-out", ANIMATE_OPACITY, &_scrollBarOpacity, &to, Curve::QUADRATIC_IN_OUT, 500L);
-        _scrollBarOpacityClip = animation->getClip();
+        if (!_scrollBarOpacityClip)
+        {
+            Animation* animation = createAnimationFromTo("scrollbar-fade-out", ANIMATE_OPACITY, &_scrollBarOpacity, &to, Curve::QUADRATIC_IN_OUT, 500L);
+            _scrollBarOpacityClip = animation->getClip();
+        }
         _scrollBarOpacityClip->play();
     }
 
@@ -757,17 +694,39 @@ bool Container::touchEventScroll(Touch::TouchEvent evt, int x, int y, unsigned i
             _scrollBarOpacityClip = NULL;
         }
         _scrollBarOpacity = 1.0f;
-        return true;
+        return _consumeInputEvents;
 
     case Touch::TOUCH_MOVE:
         if (_scrolling)
         {
+            double gameTime = Game::getAbsoluteTime();
+
             // Calculate the latest movement delta for the next update to use.
             int vx = x - _scrollingLastX;
             int vy = y - _scrollingLastY;
-            _scrollingVelocity.set(vx, vy);
-            _scrollPosition.x += vx;
-            _scrollPosition.y += vy;
+            if (_scrollingMouseVertically)
+            {
+                float yRatio = _totalHeight / _absoluteBounds.height;
+                vy *= yRatio;
+
+                _scrollingVelocity.set(0, -vy);
+                _scrollPosition.y -= vy;
+            }
+            else if (_scrollingMouseHorizontally)
+            {
+                float xRatio = _totalWidth / _absoluteBounds.width;
+                vx *= xRatio;
+
+                _scrollingVelocity.set(-vx, 0);
+                _scrollPosition.x -= vx;
+            }
+            else
+            {
+                _scrollingVelocity.set(vx, vy);
+                _scrollPosition.x += vx;
+                _scrollPosition.y += vy;
+            }
+
             _scrollingLastX = x;
             _scrollingLastY = y;
 
@@ -777,7 +736,7 @@ bool Container::touchEventScroll(Touch::TouchEvent evt, int x, int y, unsigned i
             {
                 _scrollingFirstX = x;
                 _scrollingRight = goingRight;
-                _scrollingStartTimeX = Game::getAbsoluteTime();
+                _scrollingStartTimeX = gameTime;
             }
 
             bool goingDown = (vy > 0);
@@ -785,36 +744,38 @@ bool Container::touchEventScroll(Touch::TouchEvent evt, int x, int y, unsigned i
             {
                 _scrollingFirstY = y;
                 _scrollingDown = goingDown;
-                _scrollingStartTimeY = Game::getAbsoluteTime();
+                _scrollingStartTimeY = gameTime;
             }
 
             if (!_scrollingStartTimeX)
-                _scrollingStartTimeX = Game::getAbsoluteTime();
+                _scrollingStartTimeX = gameTime;
 
             if (!_scrollingStartTimeY)
-                _scrollingStartTimeY = Game::getAbsoluteTime();
+                _scrollingStartTimeY = gameTime;
 
-            _scrollingLastTime = Game::getAbsoluteTime();
+            _scrollingLastTime = gameTime;
 
-            return true;
+            return _consumeInputEvents;
         }
         break;
 
     case Touch::TOUCH_RELEASE:
         _scrolling = false;
-        float timeSinceLastMove = (float)(Game::getAbsoluteTime() - _scrollingLastTime);
+        double gameTime = Game::getAbsoluteTime();
+        float timeSinceLastMove = (float)(gameTime - _scrollingLastTime);
         if (timeSinceLastMove > SCROLL_INERTIA_DELAY)
         {
             _scrollingVelocity.set(0, 0);
-            return true;
+            _scrollingMouseVertically = _scrollingMouseHorizontally = false;
+            return _consumeInputEvents;
         }
 
         int dx = _scrollingLastX - _scrollingFirstX;
         int dy = _scrollingLastY - _scrollingFirstY;
 
-        float timeTakenX = (float)(Game::getAbsoluteTime() - _scrollingStartTimeX);
+        float timeTakenX = (float)(gameTime - _scrollingStartTimeX);
         float elapsedSecsX = timeTakenX * 0.001f;
-        float timeTakenY = (float)(Game::getAbsoluteTime() - _scrollingStartTimeY);
+        float timeTakenY = (float)(gameTime - _scrollingStartTimeY);
         float elapsedSecsY = timeTakenY * 0.001f;
 
         float vx = dx;
@@ -824,14 +785,184 @@ bool Container::touchEventScroll(Touch::TouchEvent evt, int x, int y, unsigned i
         if (elapsedSecsY > 0)
             vy = (float)dy / elapsedSecsY;
 
-        _scrollingVelocity.set(vx, vy);
+        if (_scrollingMouseVertically)
+        {
+            float yRatio = _totalHeight / _absoluteBounds.height;
+            vy *= yRatio;
+            _scrollingVelocity.set(0, -vy);
+        }
+        else if (_scrollingMouseHorizontally)
+        {
+            float xRatio = _totalWidth / _absoluteBounds.width;
+            vx *= xRatio;
+            _scrollingVelocity.set(-vx, 0);
+        }
+        else
+        {
+            _scrollingVelocity.set(vx, vy);
+        }
 
-        return true;
+        _scrollingMouseVertically = _scrollingMouseHorizontally = false;
+        return _consumeInputEvents;
     }
 
     return false;
 }
 
+bool Container::mouseEventScroll(Mouse::MouseEvent evt, int x, int y, int wheelDelta)
+{
+    switch(evt)
+    {
+        case Mouse::MOUSE_PRESS_LEFT_BUTTON:
+        {
+            if (_scrollBarVertical)
+            {
+                float vWidth = _scrollBarVertical->getRegion().width;
+                Rectangle vBounds(_viewportBounds.x + _viewportBounds.width - vWidth,
+                                 _scrollBarBounds.y,
+                                 vWidth, _scrollBarBounds.height);
+
+                if (x + _viewportBounds.x >= vBounds.x &&
+                    x + _viewportBounds.x <= vBounds.x + vBounds.width)
+                {
+                    // Then we're within the horizontal bounds of the verticle scrollbar.
+                    // We want to either jump up or down, or drag the scrollbar itself.
+                    if (y < vBounds.y)
+                    {
+                        _scrollPosition.y += _totalHeight / 5.0f;
+                    }
+                    else if (y > vBounds.y + vBounds.height)
+                    {
+                        _scrollPosition.y -= _totalHeight / 5.0f;
+                    }
+                    else
+                    {
+                        _scrollingMouseVertically = true;
+                    }
+                }
+            }
+            
+            if (_scrollBarHorizontal)
+            {
+                float hHeight = _scrollBarHorizontal->getRegion().height;
+                Rectangle hBounds(_scrollBarBounds.x,
+                                  _viewportBounds.y + _viewportBounds.height - hHeight,
+                                  _scrollBarBounds.width, hHeight);
+            
+                if (y + _viewportBounds.y >= hBounds.y &&
+                         y + _viewportBounds.y <= hBounds.y + hBounds.height)
+                {
+                    // We're within the vertical bounds of the horizontal scrollbar.
+                    if (x < hBounds.x)
+                        _scrollPosition.x += _totalWidth / 5.0f;
+                    else if (x > hBounds.x + hBounds.width)
+                        _scrollPosition.x -= _totalWidth / 5.0f;
+                    else
+                        _scrollingMouseHorizontally = true;
+                }
+            }
+
+            return touchEventScroll(Touch::TOUCH_PRESS, x, y, 0);
+        }
+
+        case Mouse::MOUSE_MOVE:
+            return touchEventScroll(Touch::TOUCH_MOVE, x, y, 0);
+
+        case Mouse::MOUSE_RELEASE_LEFT_BUTTON:
+            return touchEventScroll(Touch::TOUCH_RELEASE, x, y, 0);
+
+        case Mouse::MOUSE_WHEEL:
+            _scrollPosition.y += (_totalHeight / 10.0f) * wheelDelta;
+            return _consumeInputEvents;
+    }
+
+    return false;
+}
+
+bool Container::pointerEvent(bool mouse, char evt, int x, int y, int data)
+{
+    if (!isEnabled())
+    {
+        return false;
+    }
+
+    bool eventConsumed = false;
+    const Theme::Border& border = getBorder(_state);
+    const Theme::Padding& padding = getPadding();
+    float xPos = border.left + padding.left;
+    float yPos = border.top + padding.top;
+
+    Vector2* offset = NULL;
+    if (_scroll != SCROLL_NONE)
+    {
+        offset = &_scrollPosition;
+    }
+
+    std::vector<Control*>::const_iterator it;
+    for (it = _controls.begin(); it < _controls.end(); it++)
+    {
+        Control* control = *it;
+        GP_ASSERT(control);
+        if (!control->isEnabled())
+        {
+            continue;
+        }
+
+        const Rectangle& bounds = control->getBounds();
+        float boundsX = bounds.x;
+        float boundsY = bounds.y;
+        if (offset)
+        {
+            boundsX += offset->x;
+            boundsY += offset->y;
+        }
+
+        if (control->getState() != 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_WHEEL) &&
+                x >= xPos + boundsX &&
+                x <= xPos + boundsX + bounds.width &&
+                y >= yPos + boundsY &&
+                y <= yPos + boundsY + bounds.height))
+        {
+            // Pass on the event's clip relative to the control.
+            if (mouse)
+                eventConsumed |= control->mouseEvent((Mouse::MouseEvent)evt, x - xPos - boundsX, y - yPos - boundsY, data);
+            else
+                eventConsumed |= control->touchEvent((Touch::TouchEvent)evt, x - xPos - boundsX, y - yPos - boundsY, (unsigned int)data);
+        }
+    }
+
+    if (!isEnabled())
+    {
+        return (_consumeInputEvents | eventConsumed);
+    }
+
+    switch (evt)
+    {
+    case Touch::TOUCH_PRESS:
+        setState(Control::FOCUS);
+        break;
+    case Touch::TOUCH_RELEASE:
+        setState(Control::NORMAL);
+        break;
+    }
+
+    if (!eventConsumed && _scroll != SCROLL_NONE)
+    {
+        if (mouse && mouseEventScroll((Mouse::MouseEvent)evt, x - xPos, y - yPos, data) ||
+            (!mouse && touchEventScroll((Touch::TouchEvent)evt, x - xPos, y - yPos, (unsigned int)data)))
+        {
+            _dirty = true;
+        }
+    }
+
+    return (_consumeInputEvents | eventConsumed);
+}
+
 Container::Scroll Container::getScroll(const char* scroll)
 {
     if (!scroll)

+ 37 - 2
gameplay/src/Container.h

@@ -45,6 +45,8 @@ namespace gameplay
  */
 class Container : public Control
 {
+    friend class DropDownList;
+
 public:
 
     /**
@@ -241,11 +243,27 @@ protected:
      * @param evt The key event that occured.
      * @param key If evt is KEY_PRESS or KEY_RELEASE then key is the key code from Keyboard::Key.
      *            If evt is KEY_CHAR then key is the unicode value of the character.
+     *
+     * @return Whether the key event was consumed by this control.
      * 
      * @see Keyboard::KeyEvent
      * @see Keyboard::Key
      */
-    virtual void keyEvent(Keyboard::KeyEvent evt, int key);
+    virtual bool keyEvent(Keyboard::KeyEvent evt, int key);
+
+    /**
+     * Mouse callback on mouse events.
+     *
+     * @param evt The mouse event that occurred.
+     * @param x The x position of the mouse in pixels. Left edge is zero.
+     * @param y The y position of the mouse in pixels. Top edge is zero.
+     * @param wheelDelta The number of mouse wheel ticks. Positive is up (forward), negative is down (backward).
+     *
+     * @return True if the mouse event is consumed or false if it is not consumed.
+     *
+     * @see Mouse::MouseEvent
+     */
+    virtual bool mouseEvent(Mouse::MouseEvent evt, int x, int y, int wheelDelta);
 
     /**
      * Gets a Layout::Type enum from a matching string.
@@ -307,6 +325,10 @@ protected:
      */
     bool touchEventScroll(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex);
 
+    bool mouseEventScroll(Mouse::MouseEvent evt, int x, int y, int wheelDelta);
+
+    bool pointerEvent(bool mouse, char evt, int x, int y, int data);
+
     /**
      * Get a Scroll enum from a matching string.
      *
@@ -329,7 +351,7 @@ protected:
      */
     Theme::ThemeImage* _scrollBarTopCap;
     /**
-     * Scrollbar verticle image.
+     * Scrollbar vertical image.
      */
     Theme::ThemeImage* _scrollBarVertical;
     /**
@@ -417,6 +439,16 @@ protected:
      */ 
     bool _scrollingDown;
 
+    /**
+     * Locked to scrolling vertically by grabbing the scrollbar with the mouse.
+     */
+    bool _scrollingMouseVertically;
+
+    /**
+     * Locked to scrolling horizontally by grabbing the scrollbar with the mouse.
+     */
+    bool _scrollingMouseHorizontally;
+
 private:
 
     /**
@@ -426,6 +458,9 @@ private:
 
     AnimationClip* _scrollBarOpacityClip;
     int _zIndexDefault;
+
+    float _totalWidth;
+    float _totalHeight;
 };
 
 

+ 36 - 9
gameplay/src/Control.cpp

@@ -7,7 +7,7 @@ namespace gameplay
 
 Control::Control()
     : _id(""), _state(Control::NORMAL), _bounds(Rectangle::empty()), _clipBounds(Rectangle::empty()), _viewportClipBounds(Rectangle::empty()),
-    _dirty(true), _consumeTouchEvents(true), _listeners(NULL), _styleOverridden(false), _skin(NULL), _clearBounds(Rectangle::empty())
+    _dirty(true), _consumeInputEvents(true), _listeners(NULL), _styleOverridden(false), _skin(NULL), _clearBounds(Rectangle::empty())
 {
 }
 
@@ -607,14 +607,14 @@ Theme::Style::OverlayType Control::getOverlayType() const
     }
 }
 
-void Control::setConsumeTouchEvents(bool consume)
+void Control::setConsumeInputEvents(bool consume)
 {
-    _consumeTouchEvents = consume;
+    _consumeInputEvents = consume;
 }
     
-bool Control::getConsumeTouchEvents()
+bool Control::getConsumeInputEvents()
 {
-    return _consumeTouchEvents;
+    return _consumeInputEvents;
 }
 
 int Control::getZIndex() const
@@ -692,7 +692,7 @@ bool Control::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int conta
     {
     case Touch::TOUCH_PRESS:
         notifyListeners(Listener::PRESS);
-        break;
+        return _consumeInputEvents;
             
     case Touch::TOUCH_RELEASE:
         // Always trigger Listener::RELEASE
@@ -704,14 +704,41 @@ bool Control::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int conta
         {
             notifyListeners(Listener::CLICK);
         }
-        break;
+        return _consumeInputEvents;
     }
 
-    return _consumeTouchEvents;
+    return false;
 }
 
-void Control::keyEvent(Keyboard::KeyEvent evt, int key)
+bool Control::keyEvent(Keyboard::KeyEvent evt, int key)
 {
+    return false;
+}
+
+bool Control::mouseEvent(Mouse::MouseEvent evt, int x, int y, int wheelDelta)
+{
+    if (!isEnabled())
+    {
+        return false;
+    }
+
+    // By default, mouse events are either interpreted as touch events or ignored.
+    switch (evt)
+    {
+    case Mouse::MOUSE_PRESS_LEFT_BUTTON:
+        return touchEvent(Touch::TOUCH_PRESS, x, y, 0);
+
+    case Mouse::MOUSE_RELEASE_LEFT_BUTTON:
+        return touchEvent(Touch::TOUCH_RELEASE, x, y, 0);
+
+    case Mouse::MOUSE_MOVE:
+        return touchEvent(Touch::TOUCH_MOVE, x, y, 0);
+
+    default:
+        break;
+    }
+
+    return false;
 }
 
 void Control::notifyListeners(Listener::EventType eventType)

+ 35 - 8
gameplay/src/Control.h

@@ -9,6 +9,7 @@
 #include "ThemeStyle.h"
 #include "Touch.h"
 #include "Keyboard.h"
+#include "Mouse.h"
 
 namespace gameplay
 {
@@ -124,7 +125,17 @@ public:
             /**
              * Event triggered when the contents of a text box are modified.
              */
-            TEXT_CHANGED    = 0x10
+            TEXT_CHANGED    = 0x10,
+
+            /**
+             * Event triggered when a control is clicked with the middle mouse button.
+             */
+            MIDDLE_CLICK    = 0x20,
+
+            /**
+             * Event triggered when a control is clicked with the right mouse button.
+             */
+            RIGHT_CLICK     = 0x40,
         };
 
         /**
@@ -630,19 +641,19 @@ public:
     bool isEnabled();
 
     /**
-     * Set whether this control consumes touch events,
+     * Set whether this control consumes input events,
      * preventing them from being passed to the game.
      *
-     * @param consume Whether this control consumes touch events.
+     * @param consume Whether this control consumes input events.
      */
-    void setConsumeTouchEvents(bool consume);
+    void setConsumeInputEvents(bool consume);
 
     /**
      * Get whether this control consumes touch events.
      *
      * @return Whether this control consumes touch events.
      */
-    bool getConsumeTouchEvents();
+    bool getConsumeInputEvents();
 
     /**
      * Set the style this control will use when rendering.
@@ -738,11 +749,27 @@ protected:
      * @param evt The key event that occured.
      * @param key If evt is KEY_PRESS or KEY_RELEASE then key is the key code from Keyboard::Key.
      *            If evt is KEY_CHAR then key is the unicode value of the character.
+     *
+     * @return Whether the key event was consumed by this control.
      * 
      * @see Keyboard::KeyEvent
      * @see Keyboard::Key
      */
-    virtual void keyEvent(Keyboard::KeyEvent evt, int key);
+    virtual bool keyEvent(Keyboard::KeyEvent evt, int key);
+
+    /**
+     * Mouse callback on mouse events.
+     *
+     * @param evt The mouse event that occurred.
+     * @param x The x position of the mouse in pixels. Left edge is zero.
+     * @param y The y position of the mouse in pixels. Top edge is zero.
+     * @param wheelDelta The number of mouse wheel ticks. Positive is up (forward), negative is down (backward).
+     *
+     * @return True if the mouse event is consumed or false if it is not consumed.
+     *
+     * @see Mouse::MouseEvent
+     */
+    virtual bool mouseEvent(Mouse::MouseEvent evt, int x, int y, int wheelDelta);
 
     /**
      * Called when a control's properties change.  Updates this control's internal rendering
@@ -883,9 +910,9 @@ protected:
     bool _dirty;
     
     /**
-     * Flag for whether the Control consume's touch events.
+     * Flag for whether the Control consumes input events.
      */
-    bool _consumeTouchEvents;
+    bool _consumeInputEvents;
     
     /**
      * The Control's Alignmnet

+ 1 - 0
gameplay/src/FlowLayout.cpp

@@ -18,6 +18,7 @@ FlowLayout::FlowLayout(const FlowLayout& copy)
 
 FlowLayout::~FlowLayout()
 {
+    __instance = NULL;
 }
 
 FlowLayout* FlowLayout::create()

+ 120 - 60
gameplay/src/Form.cpp

@@ -543,63 +543,20 @@ bool Form::touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned int
 
         if (form->isEnabled())
         {
-            Node* node = form->_node;
-            if (node)
+            if (form->_node)
             {
-                Scene* scene = node->getScene();
-                GP_ASSERT(scene);
-                Camera* camera = scene->getActiveCamera();
-
-                if (camera)
+                Vector3 point;
+                if (form->projectPoint(x, y, &point))
                 {
-                    // Get info about the form's position.
-                    Matrix m = node->getMatrix();
-                    Vector3 min(0, 0, 0);
-                    m.transformPoint(&min);
-
-                    // Unproject point into world space.
-                    Ray ray;
-                    camera->pickRay(Game::getInstance()->getViewport(), x, y, &ray);
-
-                    // Find the quad's plane.
-                    // We know its normal is the quad's forward vector.
-                    Vector3 normal = node->getForwardVectorWorld();
-
-                    // To get the plane's distance from the origin,
-                    // we'll find the distance from the plane defined
-                    // by the quad's forward vector and one of its points
-                    // to the plane defined by the same vector and the origin.
-                    const float& a = normal.x; const float& b = normal.y; const float& c = normal.z;
-                    const float d = -(a*min.x) - (b*min.y) - (c*min.z);
-                    const float distance = fabs(d) /  sqrt(a*a + b*b + c*c);
-                    Plane plane(normal, -distance);
-
-                    // Check for collision with plane.
-                    float collisionDistance = ray.intersects(plane);
-                    if (collisionDistance != Ray::INTERSECTS_NONE)
+                    const Rectangle& bounds = form->getBounds();
+                    if (form->getState() == Control::FOCUS ||
+                        (evt == Touch::TOUCH_PRESS &&
+                         point.x >= bounds.x &&
+                         point.x <= bounds.x + bounds.width &&
+                         point.y >= bounds.y &&
+                         point.y <= bounds.y + bounds.height))
                     {
-                        // Multiply the ray's direction vector by collision distance
-                        // and add that to the ray's origin.
-                        Vector3 point = ray.getOrigin() + collisionDistance*ray.getDirection();
-
-                        // Project this point into the plane.
-                        m.invert();
-                        m.transformPoint(&point);
-
-                        // Pass the touch event on.
-                        const Rectangle& bounds = form->getBounds();
-                        if (form->getState() == Control::FOCUS ||
-                            (evt == Touch::TOUCH_PRESS &&
-                                point.x >= bounds.x &&
-                                point.x <= bounds.x + bounds.width &&
-                                point.y >= bounds.y &&
-                                point.y <= bounds.y + bounds.height))
-                        {
-                            if (form->touchEvent(evt, point.x - bounds.x, bounds.height - point.y - bounds.y, contactIndex))
-                            {
-                                return true;
-                            }
-                        }
+                        return form->touchEvent(evt, point.x - bounds.x, bounds.height - point.y - bounds.y, contactIndex);
                     }
                 }
             }
@@ -615,10 +572,7 @@ bool Form::touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned int
                         y <= bounds.y + bounds.height))
                 {
                     // Pass on the event's position relative to the form.
-                    if (form->touchEvent(evt, x - bounds.x, y - bounds.y, contactIndex))
-                    {
-                        return true;
-                    }
+                    return form->touchEvent(evt, x - bounds.x, y - bounds.y, contactIndex);
                 }
             }
         }
@@ -627,7 +581,7 @@ bool Form::touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned int
     return false;
 }
 
-void Form::keyEventInternal(Keyboard::KeyEvent evt, int key)
+bool Form::keyEventInternal(Keyboard::KeyEvent evt, int key)
 {
     std::vector<Form*>::const_iterator it;
     for (it = __forms.begin(); it < __forms.end(); it++)
@@ -636,9 +590,115 @@ void Form::keyEventInternal(Keyboard::KeyEvent evt, int key)
         GP_ASSERT(form);
         if (form->isEnabled())
         {
-            form->keyEvent(evt, key);
+            if (form->keyEvent(evt, key))
+                return true;
         }
     }
+
+    return false;
+}
+
+bool Form::mouseEventInternal(Mouse::MouseEvent evt, int x, int y, int wheelDelta)
+{
+    std::vector<Form*>::const_iterator it;
+    for (it = __forms.begin(); it < __forms.end(); it++)
+    {
+        Form* form = *it;
+        GP_ASSERT(form);
+
+        if (form->isEnabled())
+        {
+            if (form->_node)
+            {
+                Vector3 point;
+                if (form->projectPoint(x, y, &point))
+                {
+                    const Rectangle& bounds = form->getBounds();
+                    if (form->getState() == Control::FOCUS ||
+                        ((evt == Mouse::MOUSE_PRESS_LEFT_BUTTON ||
+                          evt == Mouse::MOUSE_PRESS_MIDDLE_BUTTON ||
+                          evt == Mouse::MOUSE_PRESS_RIGHT_BUTTON ||
+                          evt == Mouse::MOUSE_WHEEL) &&
+                         point.x >= bounds.x &&
+                         point.x <= bounds.x + bounds.width &&
+                         point.y >= bounds.y &&
+                         point.y <= bounds.y + bounds.height))
+                    {
+                        return form->mouseEvent(evt, point.x - bounds.x, bounds.height - point.y - bounds.y, wheelDelta);
+                    }
+                }
+            }
+            else
+            {
+                // Simply compare with the form's bounds.
+                const Rectangle& bounds = form->getBounds();
+                if (form->getState() == Control::FOCUS ||
+                    ((evt == Mouse::MOUSE_PRESS_LEFT_BUTTON ||
+                      evt == Mouse::MOUSE_PRESS_MIDDLE_BUTTON ||
+                      evt == Mouse::MOUSE_PRESS_RIGHT_BUTTON ||
+                      evt == Mouse::MOUSE_WHEEL) &&
+                        x >= bounds.x &&
+                        x <= bounds.x + bounds.width &&
+                        y >= bounds.y &&
+                        y <= bounds.y + bounds.height))
+                {
+                    // Pass on the event's position relative to the form.
+                    return form->mouseEvent(evt, x - bounds.x, y - bounds.y, wheelDelta);
+                }
+            }
+        }
+    }
+
+    return false;
+}
+
+bool Form::projectPoint(int x, int y, Vector3* point)
+{
+    Scene* scene = _node->getScene();
+    GP_ASSERT(scene);
+    Camera* camera = scene->getActiveCamera();
+
+    if (camera)
+    {
+        // Get info about the form's position.
+        Matrix m = _node->getMatrix();
+        Vector3 min(0, 0, 0);
+        m.transformPoint(&min);
+
+        // Unproject point into world space.
+        Ray ray;
+        camera->pickRay(Game::getInstance()->getViewport(), x, y, &ray);
+
+        // Find the quad's plane.
+        // We know its normal is the quad's forward vector.
+        Vector3 normal = _node->getForwardVectorWorld();
+
+        // To get the plane's distance from the origin,
+        // we'll find the distance from the plane defined
+        // by the quad's forward vector and one of its points
+        // to the plane defined by the same vector and the origin.
+        const float& a = normal.x; const float& b = normal.y; const float& c = normal.z;
+        const float d = -(a*min.x) - (b*min.y) - (c*min.z);
+        const float distance = abs(d) /  sqrt(a*a + b*b + c*c);
+        Plane plane(normal, -distance);
+
+        // Check for collision with plane.
+        float collisionDistance = ray.intersects(plane);
+        if (collisionDistance != Ray::INTERSECTS_NONE)
+        {
+            // Multiply the ray's direction vector by collision distance
+            // and add that to the ray's origin.
+            point->set(ray.getOrigin() + collisionDistance*ray.getDirection());
+
+            // Project this point into the plane.
+            m.invert();
+            m.transformPoint(point);
+
+            return true;
+        }
+    }
+
+    return false;
 }
 
 int Form::nextHighestPowerOfTwo(int x)

+ 31 - 1
gameplay/src/Form.h

@@ -8,6 +8,7 @@
 #include "FrameBuffer.h"
 #include "Touch.h"
 #include "Keyboard.h"
+#include "Mouse.h"
 
 namespace gameplay
 {
@@ -175,9 +176,38 @@ private:
 
     /**
      * Propagate key events to enabled forms.
+     *
+     * @return Whether the key event was consumed by a form.
+     */
+    static bool keyEventInternal(Keyboard::KeyEvent evt, int key);
+
+    /**
+     * Propagate mouse events to enabled forms.
+     *
+     * @return True if the mouse event is consumed or false if it is not consumed.
+     *
+     * @see Mouse::MouseEvent
      */
-    static void keyEventInternal(Keyboard::KeyEvent evt, int key);
+    static bool mouseEventInternal(Mouse::MouseEvent evt, int x, int y, int wheelDelta);
 
+    /**
+     * Unproject a point (from a mouse or touch event) into the scene and then project it onto the form.
+     *
+     * @param x The x coordinate of the mouse/touch point.
+     * @param y The y coordinate of the mouse/touch point.
+     * @param point A destination vector to populate with the projected point, in the form's plane.
+     *
+     * @return True if the projected point lies within the form's plane, false otherwise.
+     */
+    bool projectPoint(int x, int y, Vector3* point);
+
+    /**
+     * Get the next highest power of two of an integer.  Used when creating framebuffers.
+     *
+     * @param x The number to start with.
+     *
+     * @return The next highest power of two after x, or x if it is already a power of two.
+     */
     static int nextHighestPowerOfTwo(int x);
 
     Theme* _theme;              // The Theme applied to this Form.

+ 2 - 2
gameplay/src/Joystick.cpp

@@ -22,7 +22,7 @@ Joystick* Joystick::create(Theme::Style* style, Properties* properties)
 {
     Joystick* joystick = new Joystick();
     joystick->initialize(style, properties);
-    joystick->_consumeTouchEvents = false;
+    joystick->_consumeInputEvents = false;
 
     return joystick;
 }
@@ -59,7 +59,7 @@ void Joystick::addListener(Control::Listener* listener, int eventFlags)
         GP_ERROR("TEXT_CHANGED event is not applicable to this control.");
     }
 
-    _consumeTouchEvents = true;
+    _consumeInputEvents = true;
 
     Control::addListener(listener, eventFlags);
 }

+ 2 - 2
gameplay/src/Label.cpp

@@ -20,7 +20,7 @@ Label* Label::create(Theme::Style* style, Properties* properties)
 {
     Label* label = new Label();
     label->initialize(style, properties);
-    label->_consumeTouchEvents = false;
+    label->_consumeInputEvents = false;
 
     return label;
 }
@@ -49,7 +49,7 @@ void Label::addListener(Control::Listener* listener, int eventFlags)
         GP_ERROR("VALUE_CHANGED event is not applicable to this control.");
     }
 
-    _consumeTouchEvents = true;
+    _consumeInputEvents = true;
 
     Control::addListener(listener, eventFlags);
 }

+ 16 - 0
gameplay/src/Platform.h

@@ -3,6 +3,7 @@
 
 #include "Touch.h"
 #include "Keyboard.h"
+#include "Mouse.h"
 
 namespace gameplay
 {
@@ -143,6 +144,21 @@ public:
      */
     static void keyEventInternal(Keyboard::KeyEvent evt, int key);
 
+    /**
+     * Mouse callback on mouse events. If the game does not consume the mouse move event or left mouse click event
+     * then it is interpreted as a touch event instead.
+     *
+     * @param evt The mouse event that occurred.
+     * @param x The x position of the mouse in pixels. Left edge is zero.
+     * @param y The y position of the mouse in pixels. Top edge is zero.
+     * @param wheelDelta The number of mouse wheel ticks. Positive is up (forward), negative is down (backward).
+     *
+     * @return True if the mouse event is consumed or false if it is not consumed.
+     *
+     * @see Mouse::MouseEvent
+     */
+    static bool mouseEventInternal(Mouse::MouseEvent evt, int x, int y, int wheelDelta);
+
     /**
      * Sleeps synchronously for the given amount of time (in milliseconds).
      *

+ 19 - 3
gameplay/src/PlatformAndroid.cpp

@@ -628,11 +628,11 @@ static int32_t engine_handle_input(struct android_app* app, AInputEvent* event)
             case AKEY_EVENT_ACTION_DOWN:
                 Game::getInstance()->keyEvent(Keyboard::KEY_PRESS, getKey(keycode, metastate));
                 if (int character = getUnicode(keycode, metastate))
-                    Game::getInstance()->keyEvent(Keyboard::KEY_CHAR, character);
+                    gameplay::Platform::keyEventInternal(Keyboard::KEY_CHAR, character);
                 break;
                     
             case AKEY_EVENT_ACTION_UP:
-                Game::getInstance()->keyEvent(Keyboard::KEY_RELEASE, getKey(keycode, metastate));
+                gameplay::Platform::keyEventInternal(Keyboard::KEY_RELEASE, getKey(keycode, metastate));
                 break;
         }
     }
@@ -941,8 +941,24 @@ void Platform::displayKeyboard(bool display)
 void Platform::touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
 {
     if (!Form::touchEventInternal(evt, x, y, contactIndex))
-    {
         Game::getInstance()->touchEvent(evt, x, y, contactIndex);
+}
+
+void Platform::keyEventInternal(Keyboard::KeyEvent evt, int key)
+{
+    if (!Form::keyEventInternal(evt, key))
+        Game::getInstance()->keyEvent(evt, key);
+}
+
+bool Platform::mouseEventInternal(Mouse::MouseEvent evt, int x, int y, int wheelDelta)
+{
+    if (Form::mouseEventInternal(evt, x, y, wheelDelta))
+    {
+        return true;
+    }
+    else
+    {
+        return Game::getInstance()->mouseEvent(evt, x, y, wheelDelta);
     }
 }
 

+ 25 - 16
gameplay/src/PlatformMacOSX.mm

@@ -213,7 +213,7 @@ static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTime
 
 - (void) mouse: (Mouse::MouseEvent) mouseEvent orTouchEvent: (Touch::TouchEvent) touchEvent x: (float) x y: (float) y s: (int) s 
 {
-    if (!Game::getInstance()->mouseEvent(mouseEvent, x, y, s))
+    if (!gameplay::Platform::mouseEventInternal(mouseEvent, x, y, s))
     {
         gameplay::Platform::touchEventInternal(touchEvent, x, y, 0);
     }
@@ -236,7 +236,7 @@ static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTime
 - (void)mouseMoved:(NSEvent *) event 
 {
     NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
-    Game::getInstance()->mouseEvent(Mouse::MOUSE_MOVE, point.x, __height - point.y, 0);
+    gameplay::Platform::mouseEventInternal(Mouse::MOUSE_MOVE, point.x, __height - point.y, 0);
 }
 
 - (void) mouseDragged: (NSEvent*) event
@@ -254,14 +254,14 @@ static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTime
      NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
     __lx = point.x;
     __ly = __height - point.y;    
-    _game->mouseEvent(Mouse::MOUSE_PRESS_RIGHT_BUTTON, point.x, __height - point.y, 0);
+    gameplay::Platform::mouseEventInternal(Mouse::MOUSE_PRESS_RIGHT_BUTTON, point.x, __height - point.y, 0);
 }
 
 - (void) rightMouseUp: (NSEvent*) event
 {
    __rightMouseDown = false;
     NSPoint point = [event locationInWindow];
-    _game->mouseEvent(Mouse::MOUSE_RELEASE_RIGHT_BUTTON, point.x, __height - point.y, 0);
+    gameplay::Platform::mouseEventInternal(Mouse::MOUSE_RELEASE_RIGHT_BUTTON, point.x, __height - point.y, 0);
 }
 
 - (void) rightMouseDragged: (NSEvent*) event
@@ -284,27 +284,27 @@ static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTime
     
     // In right-mouse case, whether __rightMouseDown is true or false
     // this should not matter, mouse move is still occuring
-    _game->mouseEvent(Mouse::MOUSE_MOVE, point.x, __height - point.y, 0);
+    gameplay::Platform::mouseEventInternal(Mouse::MOUSE_MOVE, point.x, __height - point.y, 0);
 }
 
 - (void)otherMouseDown: (NSEvent *) event 
 {
     __otherMouseDown = true;
     NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
-    _game->mouseEvent(Mouse::MOUSE_PRESS_MIDDLE_BUTTON, point.x, __height - point.y, 0);
+    gameplay::Platform::mouseEventInternal(Mouse::MOUSE_PRESS_MIDDLE_BUTTON, point.x, __height - point.y, 0);
 }
 
 - (void)otherMouseUp: (NSEvent *) event 
 {
     __otherMouseDown = false;
     NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
-    _game->mouseEvent(Mouse::MOUSE_RELEASE_MIDDLE_BUTTON, point.x, __height - point.y, 0);
+    gameplay::Platform::mouseEventInternal(Mouse::MOUSE_RELEASE_MIDDLE_BUTTON, point.x, __height - point.y, 0);
 }
 
 - (void)otherMouseDragged: (NSEvent *) event 
 {
     NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
-    _game->mouseEvent(Mouse::MOUSE_MOVE, point.x, __height - point.y, 0);
+    gameplay::Platform::mouseEventInternal(Mouse::MOUSE_MOVE, point.x, __height - point.y, 0);
 }
 
 - (void) mouseEntered: (NSEvent*)event
@@ -315,7 +315,7 @@ static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTime
 - (void)scrollWheel: (NSEvent *) event 
 {
     NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
-    Game::getInstance()->mouseEvent(Mouse::MOUSE_WHEEL, point.x, __height - point.y, (int)([event deltaY] * 10.0f));
+    gameplay::Platform::mouseEventInternal(Mouse::MOUSE_WHEEL, point.x, __height - point.y, (int)([event deltaY] * 10.0f));
 }
 
 - (void) mouseExited: (NSEvent*)event
@@ -770,17 +770,26 @@ void Platform::displayKeyboard(bool display)
 void Platform::touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
 {
     if (!Form::touchEventInternal(evt, x, y, contactIndex))
-    {
         Game::getInstance()->touchEvent(evt, x, y, contactIndex);
-    }
 }
     
-void Platform::keyEventInternal(Keyboard::KeyEvent evt, int key)
-{
-    Game::getInstance()->keyEvent(evt, key);
-    Form::keyEventInternal(evt, key);
+void Platform::keyEventInternal(Keyboard::KeyEvent evt, int key)
+{
+    if (!Form::keyEventInternal(evt, key))
+        Game::getInstance()->keyEvent(evt, key);
+}
+
+bool Platform::mouseEventInternal(Mouse::MouseEvent evt, int x, int y, int wheelDelta)
+{
+    if (Form::mouseEventInternal(evt, x, y, wheelDelta))
+    {
+        return true;
+    }
+    else
+    {
+        return Game::getInstance()->mouseEvent(evt, x, y, wheelDelta);
+    }
 }
-    
 
 void Platform::sleep(long ms)
 {

+ 21 - 11
gameplay/src/PlatformQNX.cpp

@@ -768,7 +768,7 @@ double timespec2millis(struct timespec *a)
  */
 void mouseOrTouchEvent(Mouse::MouseEvent mouseEvent, Touch::TouchEvent touchEvent, int x, int y)
 {
-    if (!Game::getInstance()->mouseEvent(mouseEvent, x, y, 0))
+    if (!gameplay::Platform::mouseEventInternal(mouseEvent, x, y, 0))
     {
         Platform::touchEventInternal(touchEvent, x, y, 0);
     }
@@ -887,14 +887,14 @@ int Platform::enterMessagePump()
                             {
                                 move = false;
                                 mouse_pressed |= SCREEN_RIGHT_MOUSE_BUTTON;
-                                Game::getInstance()->mouseEvent(Mouse::MOUSE_PRESS_RIGHT_BUTTON, position[0], position[1], 0);
+                                gameplay::Platform::mouseEventInternal(Mouse::MOUSE_PRESS_RIGHT_BUTTON, position[0], position[1], 0);
                             }
                         }
                         else if (mouse_pressed & SCREEN_RIGHT_MOUSE_BUTTON)
                         {
                             move = false;
                             mouse_pressed &= ~SCREEN_RIGHT_MOUSE_BUTTON;
-                            Game::getInstance()->mouseEvent(Mouse::MOUSE_RELEASE_RIGHT_BUTTON, position[0], position[1], 0);
+                            gameplay::Platform::mouseEventInternal(Mouse::MOUSE_RELEASE_RIGHT_BUTTON, position[0], position[1], 0);
                         }
 
                         // Handle middle mouse.
@@ -904,14 +904,14 @@ int Platform::enterMessagePump()
                             {
                                 move = false;
                                 mouse_pressed |= SCREEN_MIDDLE_MOUSE_BUTTON;
-                                Game::getInstance()->mouseEvent(Mouse::MOUSE_PRESS_MIDDLE_BUTTON, position[0], position[1], 0);
+                                gameplay::Platform::mouseEventInternal(Mouse::MOUSE_PRESS_MIDDLE_BUTTON, position[0], position[1], 0);
                             }
                         }
                         else if (mouse_pressed & SCREEN_MIDDLE_MOUSE_BUTTON)
                         {
                             move = false;
                             mouse_pressed &= ~SCREEN_MIDDLE_MOUSE_BUTTON;
-                            Game::getInstance()->mouseEvent(Mouse::MOUSE_RELEASE_MIDDLE_BUTTON, position[0], position[1], 0);
+                            gameplay::Platform::mouseEventInternal(Mouse::MOUSE_RELEASE_MIDDLE_BUTTON, position[0], position[1], 0);
                         }
 
                         // Fire a move event if none of the buttons changed.
@@ -921,13 +921,13 @@ int Platform::enterMessagePump()
                         }
                         else if (move)
                         {
-                            Game::getInstance()->mouseEvent(Mouse::MOUSE_MOVE, position[0], position[1], 0);
+                            gameplay::Platform::mouseEventInternal(Mouse::MOUSE_MOVE, position[0], position[1], 0);
                         }
 
                         // Handle mouse wheel events.
                         if (wheel)
                         {
-                            Game::getInstance()->mouseEvent(Mouse::MOUSE_WHEEL, position[0], position[1], -wheel);
+                            gameplay::Platform::mouseEventInternal(Mouse::MOUSE_WHEEL, position[0], position[1], -wheel);
                         }
                         break;
                     }
@@ -1137,15 +1137,25 @@ void Platform::displayKeyboard(bool display)
 void Platform::touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
 {
     if (!Form::touchEventInternal(evt, x, y, contactIndex))
-    {
         Game::getInstance()->touchEvent(evt, x, y, contactIndex);
-    }
 }
 
 void Platform::keyEventInternal(Keyboard::KeyEvent evt, int key)
 {
-    Game::getInstance()->keyEvent(evt, key);
-    Form::keyEventInternal(evt, key);
+    if (!Form::keyEventInternal(evt, key))
+        Game::getInstance()->keyEvent(evt, key);
+}
+
+bool Platform::mouseEventInternal(Mouse::MouseEvent evt, int x, int y, int wheelDelta)
+{
+    if (Form::mouseEventInternal(evt, x, y, wheelDelta))
+    {
+        return true;
+    }
+    else
+    {
+        return Game::getInstance()->mouseEvent(evt, x, y, wheelDelta);
+    }
 }
 
 void Platform::sleep(long ms)

+ 26 - 12
gameplay/src/PlatformWin32.cpp

@@ -290,7 +290,7 @@ LRESULT CALLBACK __WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
         int y = GET_Y_LPARAM(lParam);
 
         UpdateCapture(wParam);
-        if (!gameplay::Game::getInstance()->mouseEvent(gameplay::Mouse::MOUSE_PRESS_LEFT_BUTTON, x, y, 0))
+        if (!gameplay::Platform::mouseEventInternal(gameplay::Mouse::MOUSE_PRESS_LEFT_BUTTON, x, y, 0))
         {
             gameplay::Platform::touchEventInternal(gameplay::Touch::TOUCH_PRESS, x, y, 0);
         }
@@ -301,7 +301,7 @@ LRESULT CALLBACK __WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
         int x = GET_X_LPARAM(lParam);
         int y = GET_Y_LPARAM(lParam);
 
-        if (!gameplay::Game::getInstance()->mouseEvent(gameplay::Mouse::MOUSE_RELEASE_LEFT_BUTTON, x, y, 0))
+        if (!gameplay::Platform::mouseEventInternal(gameplay::Mouse::MOUSE_RELEASE_LEFT_BUTTON, x, y, 0))
         {
             gameplay::Platform::touchEventInternal(gameplay::Touch::TOUCH_RELEASE, x, y, 0);
         }
@@ -312,21 +312,21 @@ LRESULT CALLBACK __WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
         UpdateCapture(wParam);
         lx = GET_X_LPARAM(lParam);
         ly = GET_Y_LPARAM(lParam);
-        gameplay::Game::getInstance()->mouseEvent(gameplay::Mouse::MOUSE_PRESS_RIGHT_BUTTON, lx, ly, 0);
+        gameplay::Platform::mouseEventInternal(gameplay::Mouse::MOUSE_RELEASE_RIGHT_BUTTON, lx, ly, 0);
         break;
 
     case WM_RBUTTONUP:
-        gameplay::Game::getInstance()->mouseEvent(gameplay::Mouse::MOUSE_RELEASE_RIGHT_BUTTON,  GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), 0);
+        gameplay::Platform::mouseEventInternal(gameplay::Mouse::MOUSE_RELEASE_RIGHT_BUTTON, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), 0);
         UpdateCapture(wParam);
         break;
 
     case WM_MBUTTONDOWN:
         UpdateCapture(wParam);
-        gameplay::Game::getInstance()->mouseEvent(gameplay::Mouse::MOUSE_PRESS_MIDDLE_BUTTON,  GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), 0);
+        gameplay::Platform::mouseEventInternal(gameplay::Mouse::MOUSE_PRESS_MIDDLE_BUTTON, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), 0);
         break;
 
     case WM_MBUTTONUP:
-        gameplay::Game::getInstance()->mouseEvent(gameplay::Mouse::MOUSE_RELEASE_MIDDLE_BUTTON,  GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), 0);
+        gameplay::Platform::mouseEventInternal(gameplay::Mouse::MOUSE_RELEASE_MIDDLE_BUTTON, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), 0);
         UpdateCapture(wParam);
         break;
 
@@ -336,7 +336,7 @@ LRESULT CALLBACK __WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
         int y = GET_Y_LPARAM(lParam);
 
         // Allow Game::mouseEvent a chance to handle (and possibly consume) the event.
-        if (!gameplay::Game::getInstance()->mouseEvent(gameplay::Mouse::MOUSE_MOVE, x, y, 0))
+        if (!gameplay::Platform::mouseEventInternal(gameplay::Mouse::MOUSE_MOVE, x, y, 0))
         {
             if ((wParam & MK_LBUTTON) == MK_LBUTTON)
             {
@@ -363,7 +363,11 @@ LRESULT CALLBACK __WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
     }
 
     case WM_MOUSEWHEEL:
-        gameplay::Game::getInstance()->mouseEvent(gameplay::Mouse::MOUSE_WHEEL, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), GET_WHEEL_DELTA_WPARAM(wParam) / 120);
+        tagPOINT point;
+        point.x = GET_X_LPARAM(lParam);
+        point.y = GET_Y_LPARAM(lParam);
+        ScreenToClient(__hwnd, &point);
+        gameplay::Platform::mouseEventInternal(gameplay::Mouse::MOUSE_WHEEL, point.x, point.y, GET_WHEEL_DELTA_WPARAM(wParam) / 120);
         break;
 
     case WM_KEYDOWN:
@@ -745,15 +749,25 @@ void Platform::displayKeyboard(bool display)
 void Platform::touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
 {
     if (!Form::touchEventInternal(evt, x, y, contactIndex))
-    {
         Game::getInstance()->touchEvent(evt, x, y, contactIndex);
-    }
 }
 
 void Platform::keyEventInternal(Keyboard::KeyEvent evt, int key)
 {
-    Game::getInstance()->keyEvent(evt, key);
-    Form::keyEventInternal(evt, key);
+    if (!Form::keyEventInternal(evt, key))
+        Game::getInstance()->keyEvent(evt, key);
+}
+
+bool Platform::mouseEventInternal(Mouse::MouseEvent evt, int x, int y, int wheelDelta)
+{
+    if (Form::mouseEventInternal(evt, x, y, wheelDelta))
+    {
+        return true;
+    }
+    else
+    {
+        return Game::getInstance()->mouseEvent(evt, x, y, wheelDelta);
+    }
 }
 
 void Platform::sleep(long ms)

+ 17 - 8
gameplay/src/PlatformiOS.mm

@@ -928,17 +928,26 @@ void Platform::displayKeyboard(bool display)
 void Platform::touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
 {
     if (!Form::touchEventInternal(evt, x, y, contactIndex))
-    {
         Game::getInstance()->touchEvent(evt, x, y, contactIndex);
-    }
-}
-
-void Platform::keyEventInternal(Keyboard::KeyEvent evt, int key)
-{
-    Game::getInstance()->keyEvent(evt, key);
-    Form::keyEventInternal(evt, key);
 }
     
+void Platform::keyEventInternal(Keyboard::KeyEvent evt, int key)
+{
+    if (!Form::keyEventInternal(evt, key))
+        Game::getInstance()->keyEvent(evt, key);
+}
+
+bool Platform::mouseEventInternal(Mouse::MouseEvent evt, int x, int y, int wheelDelta)
+{
+    if (Form::mouseEventInternal(evt, x, y, wheelDelta))
+    {
+        return true;
+    }
+    else
+    {
+        return Game::getInstance()->mouseEvent(evt, x, y, wheelDelta);
+    }
+}    
 
 void Platform::sleep(long ms)
 {

+ 1 - 0
gameplay/src/Slider.cpp

@@ -134,6 +134,7 @@ bool Slider::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contac
         if (evt == Touch::TOUCH_RELEASE)
         {
             _state = NORMAL;
+            _dirty = true;
         }
         break;
     }

+ 5 - 8
gameplay/src/SpriteBatch.cpp

@@ -159,8 +159,9 @@ SpriteBatch* SpriteBatch::create(Texture* texture, Effect* effect, unsigned int
     batch->_textureHeightRatio = 1.0f / (float)texture->getHeight();
 
     // Bind an ortho projection to the material by default (user can override with setProjectionMatrix)
-    material->getParameter("u_projectionMatrix")->bindValue(batch, &SpriteBatch::getOrthoMatrix);
-
+    Game* game = Game::getInstance();
+    Matrix::createOrthographicOffCenter(0, game->getWidth(), game->getHeight(), 0, 0, 1, &batch->_projectionMatrix);
+    material->getParameter("u_projectionMatrix")->bindValue(batch, &SpriteBatch::getProjectionMatrix);
     return batch;
 }
 
@@ -431,15 +432,11 @@ Material* SpriteBatch::getMaterial() const
 
 void SpriteBatch::setProjectionMatrix(const Matrix& matrix)
 {
-    // Bind the specified matrix to a parameter named 'u_projectionMatrix' (assumed to exist).
-    _batch->getMaterial()->getParameter("u_projectionMatrix")->setValue(matrix);
+    _projectionMatrix = matrix;
 }
 
-const Matrix& SpriteBatch::getOrthoMatrix() const
+const Matrix& SpriteBatch::getProjectionMatrix() const
 {
-    // Update matrix with ortho projection and return it.
-    Game* game = Game::getInstance();
-    Matrix::createOrthographicOffCenter(0, game->getWidth(), game->getHeight(), 0, 0, 1, &_projectionMatrix);
     return _projectionMatrix;
 }
 

+ 2 - 2
gameplay/src/SpriteBatch.h

@@ -273,6 +273,8 @@ public:
      */
     void setProjectionMatrix(const Matrix& matrix);
 
+    const Matrix& getProjectionMatrix() const;
+
 private:
 
     /**
@@ -303,8 +305,6 @@ private:
      */
     SpriteBatch(const SpriteBatch& copy);
 
-    const Matrix& getOrthoMatrix() const;
-
     /**
      * Adds a single sprite to a SpriteVertex array.
      * 

+ 19 - 8
gameplay/src/TextBox.cpp

@@ -54,21 +54,21 @@ bool TextBox::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int conta
             _state = ACTIVE;
             Game::getInstance()->displayKeyboard(true);
             _dirty = true;
-            return _consumeTouchEvents;
+            return _consumeInputEvents;
         }
         else if (x > _clipBounds.x && x <= _clipBounds.x + _clipBounds.width &&
                  y > _clipBounds.y && y <= _clipBounds.y + _clipBounds.height)
         {
             setCaretLocation(x, y);
             _dirty = true;
-            return _consumeTouchEvents;
+            return _consumeInputEvents;
         }
         else
         {
             _state = NORMAL;
             Game::getInstance()->displayKeyboard(false);
             _dirty = true;
-            return _consumeTouchEvents;
+            return _consumeInputEvents;
         }
         break;
     case Touch::TOUCH_MOVE:
@@ -78,7 +78,7 @@ bool TextBox::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int conta
         {
             setCaretLocation(x, y);
             _dirty = true;
-            return _consumeTouchEvents;
+            return _consumeInputEvents;
         }
         break;
     case Touch::TOUCH_RELEASE:
@@ -88,23 +88,25 @@ bool TextBox::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int conta
             setCaretLocation(x, y);
             _state = FOCUS;
             _dirty = true;
-            return _consumeTouchEvents;
+            return _consumeInputEvents;
         }
         else
         {
             _state = NORMAL;
             Game::getInstance()->displayKeyboard(false);
             _dirty = true;
-            return _consumeTouchEvents;
+            return _consumeInputEvents;
         }
         break;
     }
 
-    return _consumeTouchEvents;
+    return _consumeInputEvents;
 }
 
-void TextBox::keyEvent(Keyboard::KeyEvent evt, int key)
+bool TextBox::keyEvent(Keyboard::KeyEvent evt, int key)
 {
+    bool consume = false;
+
     if (_state == FOCUS)
     {
         switch (evt)
@@ -142,6 +144,7 @@ void TextBox::keyEvent(Keyboard::KeyEvent evt, int key)
                         font->getLocationAtIndex(_text.c_str(), _textBounds, fontSize, &_caretLocation, textIndex,
                             textAlignment, true, rightToLeft);
                         _dirty = true;
+                        consume = true;
                         notifyListeners(Listener::TEXT_CHANGED);
                         break;
                     }
@@ -158,6 +161,7 @@ void TextBox::keyEvent(Keyboard::KeyEvent evt, int key)
                         font->getLocationAtIndex(_text.c_str(), _textBounds, fontSize, &_caretLocation, textIndex - 1,
                             textAlignment, true, rightToLeft);
                         _dirty = true;
+                        consume = true;
                         break;
                     }
                     case Keyboard::KEY_RIGHT_ARROW:
@@ -173,6 +177,7 @@ void TextBox::keyEvent(Keyboard::KeyEvent evt, int key)
                         font->getLocationAtIndex(_text.c_str(), _textBounds, fontSize, &_caretLocation, textIndex + 1,
                             textAlignment, true, rightToLeft);
                         _dirty = true;
+                        consume = true;
                         break;
                     }
                     case Keyboard::KEY_UP_ARROW:
@@ -187,6 +192,7 @@ void TextBox::keyEvent(Keyboard::KeyEvent evt, int key)
                         font->getIndexAtLocation(_text.c_str(), _textBounds, fontSize, _caretLocation, &_caretLocation,
                             textAlignment, true, rightToLeft);
                         _dirty = true;
+                        consume = true;
                         break;
                     }
                     case Keyboard::KEY_DOWN_ARROW:
@@ -201,6 +207,7 @@ void TextBox::keyEvent(Keyboard::KeyEvent evt, int key)
                         font->getIndexAtLocation(_text.c_str(), _textBounds, fontSize, _caretLocation, &_caretLocation,
                             textAlignment, true, rightToLeft);
                         _dirty = true;
+                        consume = true;
                         break;
                     }
                 }
@@ -230,6 +237,7 @@ void TextBox::keyEvent(Keyboard::KeyEvent evt, int key)
                                 textAlignment, true, rightToLeft);
 
                             _dirty = true;
+                            consume = true;
                         }
                         break;
                     }
@@ -240,6 +248,7 @@ void TextBox::keyEvent(Keyboard::KeyEvent evt, int key)
                     {
                         // Insert character into string.
                         _text.insert(textIndex, 1, (char)key);
+                        consume = true;
 
                         // Get new location of caret.
                         font->getLocationAtIndex(_text.c_str(), _textBounds, fontSize, &_caretLocation, textIndex + 1,
@@ -289,6 +298,8 @@ void TextBox::keyEvent(Keyboard::KeyEvent evt, int key)
     }
 
     _lastKeypress = key;
+
+    return (consume & _consumeInputEvents);
 }
 
 void TextBox::update(const Control* container, const Vector2& offset)

+ 1 - 1
gameplay/src/TextBox.h

@@ -104,7 +104,7 @@ protected:
      * @see Keyboard::KeyEvent
      * @see Keyboard::Key
      */
-    void keyEvent(Keyboard::KeyEvent evt, int key);
+    bool keyEvent(Keyboard::KeyEvent evt, int key);
 
     /**
      * Called when a control's properties change.  Updates this control's internal rendering

+ 19 - 0
gameplay/src/Theme.cpp

@@ -451,6 +451,25 @@ Theme::Style* Theme::getStyle(const char* name) const
     return NULL;
 }
 
+Theme::Style* Theme::getEmptyStyle()
+{
+    Theme::Style* emptyStyle = getStyle("EMPTY_STYLE");
+
+    if (!emptyStyle)
+    {
+        Theme::Style::Overlay* overlay = Theme::Style::Overlay::create();
+        overlay->addRef();
+        overlay->addRef();
+        overlay->addRef();
+        emptyStyle = new Theme::Style((Theme*)this, "EMPTY_STYLE", 1.0f / _texture->getWidth(), 1.0f / _texture->getHeight(),
+            Theme::Margin::empty(), Theme::Border::empty(), overlay, overlay, overlay, overlay);
+
+        _styles.push_back(emptyStyle);
+    }
+
+    return emptyStyle;
+}
+
 void Theme::setProjectionMatrix(const Matrix& matrix)
 {
     GP_ASSERT(_spriteBatch);

+ 3 - 0
gameplay/src/Theme.h

@@ -419,6 +419,9 @@ private:
 
     Theme::Style* getStyle(const char* id) const;
 
+    // Used when a control does not specify a style.
+    Theme::Style* getEmptyStyle();
+
     void setProjectionMatrix(const Matrix& matrix);
 
     SpriteBatch* getSpriteBatch() const;