Browse Source

Finished implementing gamepad and touch input support back for new UI focus/input management routines.

Steve Grenier 12 years ago
parent
commit
bc589616b0

+ 2 - 3
gameplay/.cproject

@@ -24,7 +24,6 @@
 								<option id="com.qnx.qcc.option.compiler.defines.1481323494" name="Defines (-D)" superClass="com.qnx.qcc.option.compiler.defines" valueType="definedSymbols">
 									<listOptionValue builtIn="false" value="_FORTIFY_SOURCE=2"/>
 									<listOptionValue builtIn="false" value="BT_USE_NEON"/>
-									<listOptionValue builtIn="false" value="GP_USE_SOCIAL"/>
 									<listOptionValue builtIn="false" value="GP_USE_GAMEPAD"/>
 								</option>
 								<option id="com.qnx.qcc.option.compiler.includePath.2133604142" name="Include Directories (-I)" superClass="com.qnx.qcc.option.compiler.includePath" valueType="includePath">
@@ -89,7 +88,6 @@
 								<option id="com.qnx.qcc.option.compiler.defines.398688299" name="Defines (-D)" superClass="com.qnx.qcc.option.compiler.defines" valueType="definedSymbols">
 									<listOptionValue builtIn="false" value="_FORTIFY_SOURCE=2"/>
 									<listOptionValue builtIn="false" value="BT_USE_NEON"/>
-									<listOptionValue builtIn="false" value="GP_USE_SOCIAL"/>
 									<listOptionValue builtIn="false" value="GP_USE_GAMEPAD"/>
 								</option>
 								<option id="com.qnx.qcc.option.compiler.includePath.1670164593" name="Include Directories (-I)" superClass="com.qnx.qcc.option.compiler.includePath" valueType="includePath">
@@ -149,7 +147,7 @@
 								<option id="com.qnx.qcc.option.compiler.security.1671403331" name="Enhanced Security (-fstack-protector-strong)" superClass="com.qnx.qcc.option.compiler.security" value="false" valueType="boolean"/>
 								<option id="com.qnx.qcc.option.compiler.defines.1863269886" name="Defines (-D)" superClass="com.qnx.qcc.option.compiler.defines" valueType="definedSymbols">
 									<listOptionValue builtIn="false" value="_FORTIFY_SOURCE=2"/>
-									<listOptionValue builtIn="false" value="GP_USE_SOCIAL"/>
+									<listOptionValue builtIn="false" value="BT_USE_NEON"/>
 									<listOptionValue builtIn="false" value="GP_USE_GAMEPAD"/>
 								</option>
 								<option id="com.qnx.qcc.option.compiler.includePath.847642559" name="Include Directories (-I)" superClass="com.qnx.qcc.option.compiler.includePath" valueType="includePath">
@@ -222,4 +220,5 @@
 	<storageModule moduleId="com.qnx.tools.ide.qde.core.QNXProjectProperties"/>
 	<storageModule moduleId="org.eclipse.cdt.make.core.buildtargets"/>
 	<storageModule moduleId="org.eclipse.cdt.core.LanguageSettingsProviders"/>
+	<storageModule moduleId="org.eclipse.cdt.internal.ui.text.commentOwnerProjectMappings"/>
 </cproject>

+ 1 - 5
gameplay/src/Button.cpp

@@ -7,6 +7,7 @@ namespace gameplay
 
 Button::Button() : _dataBinding(0)
 {
+    _canFocus = true;
 }
 
 Button::~Button()
@@ -54,9 +55,4 @@ void Button::setDataBinding(unsigned int dataBinding)
     _dataBinding = dataBinding;
 }
 
-bool Button::canFocus() const
-{
-    return true;
-}
-
 }

+ 0 - 5
gameplay/src/Button.h

@@ -49,11 +49,6 @@ public:
      */
     static Button* create(const char* id, Theme::Style* style);
 
-    /**
-     * @see Control#canFocus()
-     */
-    bool canFocus() const;
-
 protected:
 
     /**

+ 48 - 53
gameplay/src/Container.cpp

@@ -24,10 +24,6 @@ static const long SCROLL_INERTIA_DELAY = 100L;
 static const float SCROLL_FRICTION_FACTOR = 5.0f;
 // Distance that must be scrolled before isScrolling() will return true, used e.g. to cancel button-click events.
 static const float SCROLL_THRESHOLD = 10.0f;
-// Distance a joystick must be pushed in order to trigger focus-change and/or scrolling.
-static const float JOYSTICK_THRESHOLD = 0.75f;
-// Scroll speed when using a DPad -- max scroll speed when using a joystick.
-static const float GAMEPAD_SCROLL_SPEED = 500.0f;
 // If the DPad or joystick is held down, this is the initial delay in milliseconds between focus change events.
 static const float FOCUS_CHANGE_REPEAT_DELAY = 300.0f;
 
@@ -77,95 +73,75 @@ Container* Container::create(const char* id, Theme::Style* style, Layout::Type l
 {
     GP_ASSERT(style);
 
-    Container* container = Container::create(layoutType);
+    Container* container = new Container();
+    container->_layout = createLayout(layoutType);
     if (id)
         container->_id = id;
     container->_style = style;
     return container;
 }
 
-Container* Container::create(Layout::Type type)
+Container* Container::create(Theme::Style* style, Properties* properties, Theme* theme)
 {
-    Layout* layout = NULL;
-    switch (type)
-    {
-    case Layout::LAYOUT_ABSOLUTE:
-        layout = AbsoluteLayout::create();
-        break;
-    case Layout::LAYOUT_FLOW:
-        layout = FlowLayout::create();
-        break;
-    case Layout::LAYOUT_VERTICAL:
-        layout = VerticalLayout::create();
-        break;
-    default:
-        layout = AbsoluteLayout::create();
-        break;
-    }
+    GP_ASSERT(properties);
 
     Container* container = new Container();
-    container->_layout = layout;
-
+    container->initialize(style, properties, theme);
     return container;
 }
 
-Container* Container::create(Theme::Style* style, Properties* properties, Theme* theme)
+void Container::initialize(Theme::Style* style, Properties* properties, Theme* theme)
 {
-    GP_ASSERT(properties);
+    Control::initialize(style, properties);
 
     // Parse layout
-    Container* container;
     Properties* layoutNS = properties->getNamespace("layout", true, false);
     if (layoutNS)
     {
-        Layout::Type layoutType = getLayoutType(layoutNS->getString("type"));
-        container = Container::create(layoutType);
-        switch (layoutType)
+        _layout = createLayout(getLayoutType(layoutNS->getString("type")));
+        switch (_layout->getType())
         {
         case Layout::LAYOUT_FLOW:
-            static_cast<FlowLayout*>(container->getLayout())->setSpacing(layoutNS->getInt("horizontalSpacing"), layoutNS->getInt("verticalSpacing"));
+            static_cast<FlowLayout*>(_layout)->setSpacing(layoutNS->getInt("horizontalSpacing"), layoutNS->getInt("verticalSpacing"));
             break;
         case Layout::LAYOUT_VERTICAL:
-            static_cast<VerticalLayout*>(container->getLayout())->setSpacing(layoutNS->getInt("spacing"));
+            static_cast<VerticalLayout*>(_layout)->setSpacing(layoutNS->getInt("spacing"));
             break;
         }
     }
     else
     {
-        container = Container::create(getLayoutType(properties->getString("layout")));
+        _layout = createLayout(getLayoutType(properties->getString("layout")));
     }
 
-    container->initialize(style, properties);
-    container->_scroll = getScroll(properties->getString("scroll"));
-    container->_scrollBarsAutoHide = properties->getBool("scrollBarsAutoHide");
-    if (container->_scrollBarsAutoHide)
+    setScroll(getScroll(properties->getString("scroll")));
+    _scrollBarsAutoHide = properties->getBool("scrollBarsAutoHide");
+    if (_scrollBarsAutoHide)
     {
-        container->_scrollBarOpacity = 0.0f;
+        _scrollBarOpacity = 0.0f;
     }
     
-    container->_scrollWheelRequiresFocus = properties->getBool("scrollWheelRequiresFocus");
+    _scrollWheelRequiresFocus = properties->getBool("scrollWheelRequiresFocus");
     if (properties->exists("scrollingFriction"))
-        container->_scrollingFriction = properties->getFloat("scrollingFriction");
+        _scrollingFriction = properties->getFloat("scrollingFriction");
     if (properties->exists("scrollWheelSpeed"))
-        container->_scrollWheelSpeed = properties->getFloat("scrollWheelSpeed");
+        _scrollWheelSpeed = properties->getFloat("scrollWheelSpeed");
 
-    container->addControls(theme, properties);
-    container->_layout->update(container, container->_scrollPosition);
+    addControls(theme, properties);
+    _layout->update(this, _scrollPosition);
 
     const char* activeControl = properties->getString("activeControl");
     if (activeControl)
     {
-        for (size_t i = 0, count = container->_controls.size(); i < count; ++i)
+        for (size_t i = 0, count = _controls.size(); i < count; ++i)
         {
-            if (container->_controls[i]->_id == activeControl)
+            if (_controls[i]->_id == activeControl)
             {
-                container->_activeControl = container->_controls[i];
+                _activeControl = _controls[i];
                 break;
             }
         }
     }
-
-    return container;
 }
 
 void Container::addControls(Theme* theme, Properties* properties)
@@ -423,6 +399,10 @@ void Container::setScroll(Scroll scroll)
     {
         _scroll = scroll;
         _dirty = true;
+
+        // Scrollable containers can be focused (to allow scrolling)
+        if (_scroll != SCROLL_NONE)
+            _canFocus = true;
     }
 }
 
@@ -506,7 +486,7 @@ void Container::setScrollWheelRequiresFocus(bool required)
 bool Container::setFocus()
 {
     // If this container (or one of its children) already has focus, do nothing
-    if (Form::_focusControl && Form::_focusControl->isChild(this))
+    if (Form::_focusControl && (Form::_focusControl == this || Form::_focusControl->isChild(this)))
         return true;
 
     // First try to set focus to our active control
@@ -1015,7 +995,7 @@ void Container::startScrolling(float x, float y, bool resetTime)
 
     if (resetTime)
     {
-        _lastFrameTime = Game::getGameTime();
+        _lastFrameTime = Game::getAbsoluteTime();
     }
 }
 
@@ -1059,6 +1039,21 @@ Layout::Type Container::getLayoutType(const char* layoutString)
     }
 }
 
+Layout* Container::createLayout(Layout::Type type)
+{
+    switch (type)
+    {
+    case Layout::LAYOUT_ABSOLUTE:
+        return AbsoluteLayout::create();
+    case Layout::LAYOUT_FLOW:
+        return FlowLayout::create();
+    case Layout::LAYOUT_VERTICAL:
+        return VerticalLayout::create();
+    default:
+        return AbsoluteLayout::create();
+    }
+}
+
 void Container::updateScroll()
 {
     if (!_initializedWithScroll)
@@ -1072,9 +1067,9 @@ void Container::updateScroll()
     // Update time.
     if (!_lastFrameTime)
     {
-        _lastFrameTime = Game::getGameTime();
+        _lastFrameTime = Game::getAbsoluteTime();
     }
-    double frameTime = Game::getGameTime();
+    double frameTime = Game::getAbsoluteTime();
     float elapsedTime = (float)(frameTime - _lastFrameTime);
     _lastFrameTime = frameTime;
 
@@ -1404,7 +1399,7 @@ bool Container::mouseEventScroll(Mouse::MouseEvent evt, int x, int y, int wheelD
         {
             if (_scrollingVelocity.isZero())
             {
-                _lastFrameTime = Game::getGameTime();
+                _lastFrameTime = Game::getAbsoluteTime();
             }
             _scrolling = _scrollingMouseVertically = _scrollingMouseHorizontally = false;
 

+ 18 - 9
gameplay/src/Container.h

@@ -312,15 +312,6 @@ protected:
      */
     virtual ~Container();
 
-    /**
-     * Create an empty container.  A container's layout type must be specified at creation time.
-     *
-     * @param type The container's layout type.
-     *
-     * @return The new container.
-     */
-    static Container* create(Layout::Type type);
-
     /**
      * Create a container with a given style and properties, including a list of controls.
      *
@@ -332,6 +323,15 @@ protected:
      */
     static Container* create(Theme::Style* style, Properties* properties, Theme* theme);
 
+    /**
+     * Initialize properties common to all Containers from a Properties object.
+     *
+     * @param style The style to apply to this control.
+     * @param properties The properties to set on this control.
+     * @param theme The theme for this control.
+     */
+    void initialize(Theme::Style* style, Properties* properties, Theme* theme);
+
     /**
      * Updates each control within this container,
      * and positions them according to the container's layout.
@@ -345,9 +345,18 @@ protected:
      * Gets a Layout::Type enum from a matching string.
      *
      * @param layoutString The layout string to parse
+     * @return The parsed layout type.
      */
     static Layout::Type getLayoutType(const char* layoutString);
 
+    /**
+     * Creates a layout for the specified layout type.
+     *
+     * @param type The type of layout to create.
+     * @return The new Layout.
+     */
+    static Layout* createLayout(Layout::Type type);
+
     /**
      * Returns whether this container or any of its controls have been modified and require an update.
      * 

+ 11 - 3
gameplay/src/Control.cpp

@@ -47,7 +47,7 @@ static bool parseCoordPair(const char* s, float* v1, float* v2, bool* v1Percenta
 Control::Control()
     : _id(""), _enabled(true), _boundsBits(0), _dirty(true), _consumeInputEvents(true), _alignment(ALIGN_TOP_LEFT), _isAlignmentSet(false),
     _autoWidth(AUTO_SIZE_NONE), _autoHeight(AUTO_SIZE_NONE), _listeners(NULL), _visible(true), _zIndex(-1),
-    _contactIndex(INVALID_CONTACT_INDEX), _focusIndex(-1), _parent(NULL), _styleOverridden(false), _skin(NULL)
+    _contactIndex(INVALID_CONTACT_INDEX), _focusIndex(-1), _canFocus(false), _parent(NULL), _styleOverridden(false), _skin(NULL)
 {
     addScriptEvent("controlEvent", "<Control>[Control::Listener::EventType]");
 }
@@ -110,6 +110,9 @@ void Control::initialize(Theme::Style* style, Properties* properties)
         _zIndex = -1;
     }
 
+    if (properties->exists("canFocus"))
+        _canFocus = properties->getBool("canFocus", false);
+
     if (properties->exists("focusIndex"))
     {
         _focusIndex = properties->getInt("focusIndex");
@@ -439,7 +442,12 @@ bool Control::isVisibleInHierarchy() const
 
 bool Control::canFocus() const
 {
-    return false;
+    return _canFocus;
+}
+
+void Control::setCanFocus(bool acceptsFocus)
+{
+    _canFocus = acceptsFocus;
 }
 
 bool Control::hasFocus() const
@@ -844,7 +852,7 @@ Control::State Control::getState() const
     if (Form::_focusControl == this)
         return FOCUS;
 
-    return State::NORMAL;
+    return NORMAL;
 }
 
 Theme::Style::OverlayType Control::getOverlayType() const

+ 38 - 26
gameplay/src/Control.h

@@ -868,6 +868,39 @@ public:
      */
     void setZIndex(int zIndex);
 
+    /**
+     * Determines if this control accepts focus.
+     *
+     * @return True if this control accepts focus, false if it does not.
+     */
+    bool canFocus() const;
+
+    /**
+     * Sets whether or not the control accepts input focus.
+     *
+     * @param acceptsFocus True if the control should accept input focus, false otherwise.
+     */
+    void setCanFocus(bool acceptsFocus);
+
+    /**
+     * Determines if this control is currently in focus.
+     *
+     * @return True if the control is currently in focus.
+     */
+    bool hasFocus() const;
+
+    /**
+     * Sets input focus to this control.
+     *
+     * If this control accepts focus (the hasFocus method returns true), input focus
+     * is set to this control. If this control is a container, the first focusable
+     * control within it gains focus.
+     *
+     * @return True if this control or one of its children successfully gained focus,
+     *      false otherwise.
+     */
+    virtual bool setFocus();
+
     /**
      * Get this control's focus index.
      *
@@ -1133,32 +1166,6 @@ protected:
      */
     static Alignment getAlignment(const char* alignment);
 
-    /**
-     * Determines if this control accepts focus.
-     *
-     * @return True if this control accepts focus, false if it does not.
-     */
-    virtual bool canFocus() const;
-
-    /**
-     * Determines if this control is currently in focus.
-     *
-     * @return True if the control is currently in focus.
-     */
-    bool hasFocus() const;
-
-    /**
-     * Sets input focus to this control.
-     *
-     * If this control accepts focus (the hasFocus method returns true), input focus
-     * is set to this control. If this control is a container, the first focusable
-     * control within it gains focus.
-     *
-     * @return True if this control or one of its children successfully gained focus,
-     *      false otherwise.
-     */
-    virtual bool setFocus();
-
     /** 
      * The Control's ID.
      */ 
@@ -1280,6 +1287,11 @@ protected:
      */
     int _focusIndex;
 
+    /**
+     * Whether or not the control accepts input focus.
+     */
+    bool _canFocus;
+
     /**
      * The control's parent container.
      */

+ 401 - 87
gameplay/src/Form.cpp

@@ -14,6 +14,13 @@
 #define FORM_VSH "res/shaders/form.vert"
 #define FORM_FSH "res/shaders/form.frag"
 
+// Scroll speed when using a DPad -- max scroll speed when using a joystick.
+static const float GAMEPAD_SCROLL_SPEED = 500.0f;
+// Distance a joystick must be pushed in order to trigger focus-change and/or scrolling.
+static const float JOYSTICK_THRESHOLD = 0.75f;
+// If the DPad or joystick is held down, this is the initial delay in milliseconds between focus changes.
+static const float GAMEPAD_FOCUS_REPEAT_DELAY = 300.0f;
+
 namespace gameplay
 {
 
@@ -108,83 +115,22 @@ Form* Form::create(const char* url)
         return NULL;
     }
 
-    // Create new form with given ID, theme and layout.
+    // Parse theme
     std::string themeFile;
     formProperties->getPath("theme", &themeFile);
-
-    // Parse layout
-    Layout* layout = NULL;
-    Properties* layoutNS = formProperties->getNamespace("layout", true, false);
-    if (layoutNS)
-    {
-        Layout::Type layoutType = getLayoutType(layoutNS->getString("type"));
-        switch (layoutType)
-        {
-        case Layout::LAYOUT_ABSOLUTE:
-            layout = AbsoluteLayout::create();
-            break;
-        case Layout::LAYOUT_FLOW:
-            layout = FlowLayout::create();
-            static_cast<FlowLayout*>(layout)->setSpacing(layoutNS->getInt("horizontalSpacing"), layoutNS->getInt("verticalSpacing"));
-            break;
-        case Layout::LAYOUT_VERTICAL:
-            layout = VerticalLayout::create();
-            static_cast<VerticalLayout*>(layout)->setSpacing(layoutNS->getInt("spacing"));
-            break;
-        }
-    }
-    else
-    {
-        switch (getLayoutType(formProperties->getString("layout")))
-        {
-        case Layout::LAYOUT_ABSOLUTE:
-            layout = AbsoluteLayout::create();
-            break;
-        case Layout::LAYOUT_FLOW:
-            layout = FlowLayout::create();
-            break;
-        case Layout::LAYOUT_VERTICAL:
-            layout = VerticalLayout::create();
-            break;
-        }
-    }
-    if (layout == NULL)
-    {
-        GP_ERROR("Unsupported layout type for form: %s", url);
-    }
-
     Theme* theme = Theme::create(themeFile.c_str());
     GP_ASSERT(theme);
 
-    Form* form = new Form();
-    form->_layout = layout;
-    form->_theme = theme;
-
-    Theme::Style* style = NULL;
+    // Parse style
     const char* styleName = formProperties->getString("style");
-    if (styleName)
-    {
-        style = theme->getStyle(styleName);
-    }
-    else
-    {
-        style = theme->getEmptyStyle();
-    }
-    form->initialize(style, formProperties);
-
-    form->_consumeInputEvents = formProperties->getBool("consumeInputEvents", true);
-
-    form->_scroll = getScroll(formProperties->getString("scroll"));
-    form->_scrollBarsAutoHide = formProperties->getBool("scrollBarsAutoHide");
-    if (form->_scrollBarsAutoHide)
-    {
-        form->_scrollBarOpacity = 0.0f;
-    }
+    Theme::Style* style = styleName ? theme->getStyle(styleName) : theme->getEmptyStyle();
 
-    // Add all the controls to the form.
-    form->addControls(theme, formProperties);
+    // Create new form
+    Form* form = new Form();
+    form->_theme = theme;
 
-    SAFE_DELETE(properties);
+    // Initialize common container properties
+    form->initialize(style, formProperties, theme);
 
     form->updateFrameBuffer();
 
@@ -207,6 +153,16 @@ Form* Form::getForm(const char* id)
     return NULL;
 }
 
+Control* Form::getFocusControl()
+{
+    return _focusControl;
+}
+
+void Form::clearFocus()
+{
+    setFocusControl(NULL);
+}
+
 bool Form::isForm() const
 {
     return true;
@@ -385,10 +341,10 @@ void Form::update(const Control* container, const Vector2& offset)
     // Store previous absolute bounds
     Rectangle oldAbsoluteClipBounds = _absoluteClipBounds;
 
-    _layout->align(this, NULL);
-
     Container::update(container, offset);
 
+    _layout->align(this, NULL);
+
     if (_absoluteClipBounds.width != oldAbsoluteClipBounds.width || _absoluteClipBounds.height != oldAbsoluteClipBounds.height)
     {
         updateFrameBuffer();
@@ -460,6 +416,8 @@ Control* Form::getActiveControl()
 
 void Form::updateInternal(float elapsedTime)
 {
+    pollGamepads();
+
     for (size_t i = 0, size = __forms.size(); i < size; ++i)
     {
         Form* form = __forms[i];
@@ -713,12 +671,151 @@ void Form::verifyRemovedControlState(Control* control)
     }
 }
 
-bool Form::touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
+// Generic pointer event handler that both touch and mouse events map to.
+// Mappings:
+//   mouse - true for mouse events, false for touch events
+//   evt - Mouse::MouseEvent or Touch::TouchEvent
+//   x, y - Point of event
+//   param - wheelData for mouse events, contactIndex for touch events
+bool Form::pointerEventInternal(bool mouse, int evt, int x, int y, int param)
 {
+    // Do not process mouse input when mouse is captured
+    if (mouse && Game::getInstance()->isMouseCaptured())
+        return false;
+
+    // Is this a press event (TOUCH_PRESS has the same value as MOUSE_PRESS_LEFT_BUTTON)
+    bool pressEvent = evt == Touch::TOUCH_PRESS || (mouse && (evt == Mouse::MOUSE_PRESS_MIDDLE_BUTTON || evt == Mouse::MOUSE_PRESS_RIGHT_BUTTON));
+
     Control* ctrl = NULL;
     int formX = x;
     int formY = y;
 
+    if (mouse || (param == 0))
+    {
+        // Note: TOUCH_PRESS and TOUCH_RELEASE have same values as MOUSE_PRESS_LEFT_BUTTON and MOUSE_RELEASE_LEFT_BUTTON
+        if (evt == Touch::TOUCH_PRESS)
+        {
+            ctrl = handlePointerPressRelease(&formX, &formY, true);
+        }
+        else if (evt == Touch::TOUCH_RELEASE)
+        {
+            ctrl = handlePointerPressRelease(&formX, &formY, false);
+        }
+        else if ((mouse && evt == Mouse::MOUSE_MOVE) || (!mouse && evt == Touch::TOUCH_MOVE))
+        {
+            ctrl = handlePointerMove(&formX, &formY);
+        }
+    }
+
+    // Dispatch input events to all controls that intersect this point
+    if (ctrl == NULL)
+    {
+        formX = x;
+        formY = y;
+        ctrl = findInputControl(&formX, &formY, false);
+    }
+
+    if (ctrl)
+    {
+        // Handle container scrolling
+        Control* tmp = ctrl;
+        while (tmp)
+        {
+            if (tmp->isContainer())
+            {
+                Container* container = static_cast<Container*>(tmp);
+                if (container->_scroll != SCROLL_NONE)
+                {
+                    if (mouse)
+                    {
+                        if (container->mouseEventScroll((Mouse::MouseEvent)evt, formX - tmp->_absoluteBounds.x, formY - tmp->_absoluteBounds.y, param))
+                            return true;
+                    }
+                    else
+                    {
+                        if (container->touchEventScroll((Touch::TouchEvent)evt, formX - tmp->_absoluteBounds.x, formY - tmp->_absoluteBounds.y, param))
+                            return true;
+                    }
+                    break; // scrollable parent container found
+                }
+            }
+            tmp = tmp->_parent;
+        }
+
+        // Handle setting focus for all press events
+        if (pressEvent)
+        {
+            Control* focusControl = ctrl;
+            while (focusControl && !focusControl->setFocus())
+                focusControl = focusControl->_parent;
+
+            if (focusControl == NULL)
+            {
+                // Nothing got focus on this press, so remove current focused control
+                setFocusControl(NULL);
+            }
+        }
+
+        // Dispatch the event from the bottom upwards, until a control intersecting the point consumes the event
+        while (ctrl)
+        {
+            int localX = formX - ctrl->_absoluteBounds.x;
+            int localY = formY - ctrl->_absoluteBounds.y;
+            if (mouse)
+            {
+                if (ctrl->mouseEvent((Mouse::MouseEvent)evt, localX, localY, param))
+                    return true;
+
+                // Forward to touch event hanlder if unhandled by mouse handler
+                switch (evt)
+                {
+                case Mouse::MOUSE_PRESS_LEFT_BUTTON:
+                    if (ctrl->touchEvent(Touch::TOUCH_PRESS, localX, localY, 0))
+                        return true;
+                    break;
+                case Mouse::MOUSE_RELEASE_LEFT_BUTTON:
+                    if (ctrl->touchEvent(Touch::TOUCH_RELEASE, localX, localY, 0))
+                        return true;
+                    break;
+                case Mouse::MOUSE_MOVE:
+                    if (ctrl->touchEvent(Touch::TOUCH_MOVE, localX, localY, 0))
+                        return true;
+                    break;
+                }
+            }
+            else
+            {
+                if (ctrl->touchEvent((Touch::TouchEvent)evt, localX, localY, param))
+                    return true;
+            }
+
+            // Consume all input events anyways?
+            if (ctrl->getConsumeInputEvents())
+                return true;
+
+            ctrl = ctrl->getParent();
+        }
+    }
+    else
+    {
+        // If this was a press event, remove all focus
+        if (pressEvent)
+        {
+            setFocusControl(NULL);
+        }
+    }
+
+    return false;
+}
+
+bool Form::touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
+{
+    return pointerEventInternal(false, evt, x, y, (int)contactIndex);
+
+    /*Control* ctrl = NULL;
+    int formX = x;
+    int formY = y;
+
     // Handle focus, active and hover state changes
     if (contactIndex == 0)
     {
@@ -745,12 +842,60 @@ bool Form::touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned int
 
     if (ctrl)
     {
+        // Handle container scrolling
+        Control* tmp = ctrl;
+        while (tmp)
+        {
+            if (tmp->isContainer())
+            {
+                Container* container = static_cast<Container*>(tmp);
+                if (container->_scroll != SCROLL_NONE)
+                {
+                    if (container->touchEventScroll(evt, formX - tmp->_absoluteBounds.x, formY - tmp->_absoluteBounds.y, contactIndex))
+                        return true;
+                    break; // scrollable parent container found
+                }
+            }
+            tmp = tmp->_parent;
+        }
+
+        // Handle setting focus for press events
+        switch (evt)
+        {
+        case Mouse::MOUSE_PRESS_LEFT_BUTTON:
+        case Mouse::MOUSE_PRESS_MIDDLE_BUTTON:
+        case Mouse::MOUSE_PRESS_RIGHT_BUTTON:
+            Control* focusControl = ctrl;
+            while (focusControl && !focusControl->setFocus())
+                focusControl = focusControl->_parent;
+            break;
+        }
+
         // Dispatch the event from the bottom upwards, until a control intersecting the point consumes the event
         while (ctrl)
         {
-            if (ctrl->touchEvent(evt, formX - ctrl->_absoluteBounds.x, formY - ctrl->_absoluteBounds.y, contactIndex))
+            int localX = formX - ctrl->_absoluteBounds.x;
+            int localY = formY - ctrl->_absoluteBounds.y;
+            if (ctrl->mouseEvent(evt, localX, localY, wheelDelta))
                 return true;
 
+            // Forward to touch event hanlder if unhandled by mouse handler
+            switch (evt)
+            {
+            case Mouse::MOUSE_PRESS_LEFT_BUTTON:
+                if (ctrl->touchEvent(Touch::TOUCH_PRESS, localX, localY, 0))
+                    return true;
+                break;
+            case Mouse::MOUSE_RELEASE_LEFT_BUTTON:
+                if (ctrl->touchEvent(Touch::TOUCH_RELEASE, localX, localY, 0))
+                    return true;
+                break;
+            case Mouse::MOUSE_MOVE:
+                if (ctrl->touchEvent(Touch::TOUCH_MOVE, localX, localY, 0))
+                    return true;
+                break;
+            }
+
             // Consume all input events anyways?
             if (ctrl->getConsumeInputEvents())
                 return true;
@@ -758,12 +903,27 @@ bool Form::touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned int
             ctrl = ctrl->getParent();
         }
     }
+    else
+    {
+        // If this was a press event, remove all focus
+        switch (evt)
+        {
+        case Mouse::MOUSE_PRESS_LEFT_BUTTON:
+        case Mouse::MOUSE_PRESS_MIDDLE_BUTTON:
+        case Mouse::MOUSE_PRESS_RIGHT_BUTTON:
+            setFocusControl(NULL);
+            break;
+        }
+    }
 
-    return false;
+    return false;*/
 }
 
 bool Form::mouseEventInternal(Mouse::MouseEvent evt, int x, int y, int wheelDelta)
 {
+    return pointerEventInternal(true, evt, x, y, wheelDelta);
+
+    /*
     // Do not process mouse input when mouse is captured
     if (Game::getInstance()->isMouseCaptured())
         return false;
@@ -780,11 +940,6 @@ bool Form::mouseEventInternal(Mouse::MouseEvent evt, int x, int y, int wheelDelt
         ctrl = handlePointerPressRelease(&formX, &formY, evt == Mouse::MOUSE_PRESS_LEFT_BUTTON);
         break;
 
-    case Mouse::MOUSE_PRESS_MIDDLE_BUTTON:
-    case Mouse::MOUSE_PRESS_RIGHT_BUTTON:
-
-        break;
-
     case Mouse::MOUSE_MOVE:
         ctrl = handlePointerMove(&formX, &formY);
         break;
@@ -875,6 +1030,7 @@ bool Form::mouseEventInternal(Mouse::MouseEvent evt, int x, int y, int wheelDelt
     }
 
     return false;
+    */
 }
 
 bool Form::keyEventInternal(Keyboard::KeyEvent evt, int key)
@@ -899,13 +1055,13 @@ bool Form::keyEventInternal(Keyboard::KeyEvent evt, int key)
     {
         switch (evt)
         {
-        case Keyboard::KeyEvent::KEY_PRESS:
+        case Keyboard::KEY_CHAR:
             switch (key)
             {
             case Keyboard::KEY_TAB:
                 if (_focusControl->_parent && _focusControl->_parent->isContainer())
                 {
-                    if (static_cast<Container*>(_focusControl->_parent)->moveFocus(Container::PREVIOUS))
+                    if (static_cast<Container*>(_focusControl->_parent)->moveFocus(_shiftKeyDown ? Container::PREVIOUS : Container::NEXT))
                         return true;
                 }
                 break;
@@ -930,18 +1086,176 @@ bool Form::keyEventInternal(Keyboard::KeyEvent evt, int key)
     return false;
 }
 
-void Form::gamepadEventInternal(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex)
+void Form::pollGamepads()
 {
-    for (int i = (int)__forms.size() - 1; i >= 0; --i)
+    Game* game = Game::getInstance();
+
+    // If no gamepads are connected, return immediately
+    unsigned int gamepadCount = game->getGamepadCount();
+    if (gamepadCount == 0)
+        return;
+
+    // For now, always use gamepad zero for controlling the UI.
+    // Possibly allow the developer to set the active gamepad for UI later.
+    Gamepad* gamepad = game->getGamepad(0, true);
+    if (!gamepad)
+        return;
+
+    pollGamepad(gamepad);
+}
+
+bool Form::pollGamepad(Gamepad* gamepad)
+{
+    // Get the currently focused control's container for focus management and scrolling
+    if (!_focusControl)
+        return false;
+
+    // Get parent container
+    Container* parentContainer = NULL;
+    if (_focusControl->_parent && _focusControl->_parent->isContainer())
+        parentContainer = static_cast<Container*>(_focusControl->_parent);
+
+    // Get scroll container
+    Container* scrollContainer = NULL;
+    if (_focusControl->isContainer())
     {
-        Form* form = __forms[i];
+        scrollContainer = static_cast<Container*>(_focusControl);
+        if (scrollContainer->_scroll == SCROLL_NONE)
+            scrollContainer = NULL;
+    }
+    if (!scrollContainer && parentContainer && parentContainer->_scroll != SCROLL_NONE)
+        scrollContainer = parentContainer;
+
+    // Static static maintained across function calls
+    static bool scrolling = false;
+    static double lastFocusChangeTime = 0;
+
+    bool focusPressed = false;
+    bool stillScrolling = false;
+    double currentTime = Game::getAbsoluteTime();
+    double focusChangeElapsedTime = currentTime - lastFocusChangeTime;
+
+    // Is a selection button down (i.e. buttons used for UI clicking/interactions)?
+    bool selectButtonDown = gamepad->isButtonDown(Gamepad::BUTTON_A) || gamepad->isButtonDown(Gamepad::BUTTON_X);
+
+    if (!selectButtonDown)
+    {
+        // Get values of analog joysticks 1 and 2 (assume left and right analog sticks)
+        Vector2 joystick;
+        unsigned int joystickCount = gamepad->getJoystickCount();
+        gamepad->getJoystickValues(0, &joystick);
+
+        if (parentContainer)
+        {
+            // The Dpad and left analog stick (i.e. first analog stick when there are two joysticks) controls focus
+            if (gamepad->isButtonDown(Gamepad::BUTTON_UP) || (joystickCount > 1 && joystick.y > JOYSTICK_THRESHOLD))
+            {
+                focusPressed = true;
+                if (focusChangeElapsedTime > GAMEPAD_FOCUS_REPEAT_DELAY && parentContainer->moveFocus(UP))
+                {
+                    lastFocusChangeTime = currentTime;
+                }
+            }
+
+            if (gamepad->isButtonDown(Gamepad::BUTTON_DOWN) || (joystickCount > 1 && joystick.y < -JOYSTICK_THRESHOLD))
+            {
+                focusPressed = true;
+                if (focusChangeElapsedTime > GAMEPAD_FOCUS_REPEAT_DELAY && parentContainer->moveFocus(DOWN))
+                {
+                    lastFocusChangeTime = currentTime;
+                }
+            }
+
+            if (gamepad->isButtonDown(Gamepad::BUTTON_LEFT) || (joystickCount > 1 && joystick.x < -JOYSTICK_THRESHOLD))
+            {
+                focusPressed = true;
+                if (focusChangeElapsedTime > GAMEPAD_FOCUS_REPEAT_DELAY && parentContainer->moveFocus(LEFT))
+                {
+                    lastFocusChangeTime = currentTime;
+                }
+            }
+
+            if (gamepad->isButtonDown(Gamepad::BUTTON_RIGHT) || (joystickCount > 1 && joystick.x > JOYSTICK_THRESHOLD))
+            {
+                focusPressed = true;
+                if (focusChangeElapsedTime > GAMEPAD_FOCUS_REPEAT_DELAY && parentContainer->moveFocus(RIGHT))
+                {
+                    lastFocusChangeTime = currentTime;
+                }
+            }
+        }
+
+        // The RIGHT analog stick (i.e. second), or ONLY analog stick (when only 1 joystick), is used to scroll.
+        if (scrollContainer && joystickCount > 0)
+        {
+            if (joystickCount > 1)
+                gamepad->getJoystickValues(1, &joystick);
+            if (std::fabs(joystick.x) > JOYSTICK_THRESHOLD || std::fabs(joystick.y) > JOYSTICK_THRESHOLD)
+            {
+                scrollContainer->startScrolling(GAMEPAD_SCROLL_SPEED * joystick.x, GAMEPAD_SCROLL_SPEED * joystick.y, !scrolling);
+                scrolling = stillScrolling = true;
+            }
+        }
+    }
+
+    if (!focusPressed)
+    {
+        // Reset focus repeat
+        lastFocusChangeTime = 0;
+    }
+
+    if (scrolling && !stillScrolling)
+    {
+        scrolling = false;
+        if (scrollContainer)
+            scrollContainer->stopScrolling();
+    }
 
-        if (form && form->isEnabled() && form->isVisible() && form->hasFocus())
+    return focusPressed || scrolling;
+}
+
+bool Form::gamepadEventInternal(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex)
+{
+    if (!_focusControl)
+        return false;
+
+    bool selectButtonPressed = gamepad->isButtonDown(Gamepad::BUTTON_A) || gamepad->isButtonDown(Gamepad::BUTTON_X);
+
+    // Fire press, release and click events to focused controls
+    switch (evt)
+    {
+    case Gamepad::BUTTON_EVENT:
+        if (selectButtonPressed && (_activeControl != _focusControl || _activeControlState != Control::ACTIVE))
         {
-            if (form->gamepadEvent(evt, gamepad, analogIndex))
-                return;
+            _activeControl = _focusControl;
+            _activeControlState = Control::ACTIVE;
+            _activeControl->notifyListeners(Control::Listener::PRESS);
+            return true;
         }
+        else if (!selectButtonPressed && _activeControl == _focusControl && _activeControlState == Control::ACTIVE)
+        {
+            _activeControlState = Control::NORMAL;
+            _activeControl->notifyListeners(Control::Listener::RELEASE);
+            _activeControl->notifyListeners(Control::Listener::CLICK);
+            return true;
+        }
+        break;
     }
+
+    // Dispatch gamepad events to focused controls (or their parents)
+    Control * ctrl = _focusControl;
+    while (ctrl)
+    {
+        if (ctrl->isEnabled() && ctrl->isVisible())
+        {
+            if (ctrl->gamepadEvent(evt, gamepad, analogIndex))
+                return true;
+        }
+
+        ctrl = ctrl->getParent();
+    }
+
+    return false;
 }
 
 void Form::resizeEventInternal(unsigned int width, unsigned int height)

+ 19 - 1
gameplay/src/Form.h

@@ -87,6 +87,18 @@ public:
      */
     static Form* getForm(const char* id);
     
+    /**
+     * Returns the current UI control that is in focus.
+     *
+     * @return The current control in focus, or NULL if no controls are in focus.
+     */
+    static Control* getFocusControl();
+
+    /**
+     * Removes focus from any currently focused UI control.
+     */
+    static void clearFocus();
+
     /**
      * @see Container#isForm()
      */
@@ -197,7 +209,7 @@ private:
      *
      * @see Control::gamepadEvent
      */
-    static void gamepadEventInternal(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex);
+    static bool gamepadEventInternal(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex);
 
     /**
      * Fired by the platform when the game window resizes.
@@ -232,6 +244,8 @@ private:
      */
     void updateFrameBuffer();
 
+    static bool pointerEventInternal(bool mouse, int evt, int x, int y, int param);
+
     static Control* findInputControl(int* x, int* y, bool focus);
 
     static Control* findInputControl(Control* control, int x, int y, bool focus);
@@ -248,6 +262,10 @@ private:
 
     static void setFocusControl(Control* control);
 
+    static void pollGamepads();
+
+    static bool pollGamepad(Gamepad* gamepad);
+
     Theme* _theme;                      // The Theme applied to this Form.
     FrameBuffer* _frameBuffer;          // FBO the Form is rendered into for texturing the quad. 
     SpriteBatch* _spriteBatch;

+ 1 - 0
gameplay/src/Gamepad.cpp

@@ -148,6 +148,7 @@ void Gamepad::bindGamepadControls(Container* container)
         {
             Button* button = (Button*)control;
             button->setConsumeInputEvents(true);
+            button->setCanFocus(false);
             _uiButtons[button->getDataBinding()] = button;
             _buttonCount++;
         }

+ 0 - 5
gameplay/src/Joystick.cpp

@@ -290,9 +290,4 @@ const char* Joystick::getType() const
     return "joystick";
 }
 
-bool Joystick::canFocus() const
-{
-    return true;
-}
-
 }

+ 0 - 5
gameplay/src/Joystick.h

@@ -120,11 +120,6 @@ public:
      */
     inline const unsigned int getIndex() const;
 
-    /**
-     * @see Control#canFocus()
-     */
-    bool canFocus() const;
-
 protected:
     
     /**

+ 4 - 12
gameplay/src/Platform.cpp

@@ -80,19 +80,11 @@ void Platform::resizeEventInternal(unsigned int width, unsigned int height)
 
 void Platform::gamepadEventInternal(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex)
 {
-	switch(evt)
-	{
-	case Gamepad::CONNECTED_EVENT:
-	case Gamepad::DISCONNECTED_EVENT:
-		Game::getInstance()->gamepadEvent(evt, gamepad);
+    if (!Form::gamepadEventInternal(evt, gamepad, analogIndex))
+    {
+        Game::getInstance()->gamepadEvent(evt, gamepad);
         Game::getInstance()->getScriptController()->gamepadEvent(evt, gamepad);
-		break;
-	case Gamepad::BUTTON_EVENT:
-	case Gamepad::JOYSTICK_EVENT:
-	case Gamepad::TRIGGER_EVENT:
-		Form::gamepadEventInternal(evt, gamepad, analogIndex);
-		break;
-	}
+    }
 }
 
 void Platform::gamepadEventConnectedInternal(GamepadHandle handle,  unsigned int buttonCount, unsigned int joystickCount, unsigned int triggerCount,

+ 13 - 75
gameplay/src/Slider.cpp

@@ -17,6 +17,7 @@ Slider::Slider() : _min(0.0f), _max(0.0f), _step(0.0f), _value(0.0f), _delta(0.0
     _valueTextAlignment(Font::ALIGN_BOTTOM_HCENTER), _valueTextPrecision(0), _valueText(""),
     _selectButtonDown(false), _directionButtonDown(false), _gamepadValue(0.0f)
 {
+    _canFocus = true;
 }
 
 Slider::~Slider()
@@ -162,11 +163,6 @@ void Slider::addListener(Control::Listener* listener, int eventFlags)
     Control::addListener(listener, eventFlags);
 }
 
-bool Slider::canFocus() const
-{
-    return true;
-}
-
 void Slider::updateValue(int x, int y)
 {
     State state = getState();
@@ -276,85 +272,27 @@ bool Slider::mouseEvent(Mouse::MouseEvent evt, int x, int y, int wheelDelta)
 
 bool Slider::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex)
 {
-    /*bool eventConsumed = false;
+    bool eventConsumed = false;
 
-    if (_state == ACTIVE)
+    switch (evt)
     {
-        switch (evt)
+        case Gamepad::JOYSTICK_EVENT:
         {
-            case Gamepad::BUTTON_EVENT:
+            // The right analog stick can be used to change a slider's value.
+            if (analogIndex == 1)
             {
-                if (gamepad->isButtonDown(Gamepad::BUTTON_LEFT))
-                {
-                    _delta = -1.0f;
-                    _directionButtonDown = true;
-                }
-                else if (gamepad->isButtonDown(Gamepad::BUTTON_RIGHT))
-                {
-                    _delta = 1.0f;
-                    _directionButtonDown = true;
-                }
-                else if (_delta != 0.0f && _directionButtonDown)
-                {
-                    _delta = 0.0f;
-                    _directionButtonDown = false;
-                }
-
-                if (_step > 0.0f && _delta != 0.0f)
-                {
-                    _value += _step * _delta;
-                    _gamepadValue = _value - (_step * _delta * 0.49f);
-                    _delta *= 0.2f;
-                }
-
-                // A slider consumes all button events until it is no longer active.
-                eventConsumed = true;
+                Vector2 joy;
+                gamepad->getJoystickValues(analogIndex, &joy);
+                _gamepadValue = _value;
+                _delta = joy.x;
                 _dirty = true;
-                break;
-            }
-            case Gamepad::JOYSTICK_EVENT:
-            {
-                // The left analog stick can be used to change a slider's value.
-                if (analogIndex == 0)
-                {
-                    Vector2 joy;
-                    gamepad->getJoystickValues(analogIndex, &joy);
-                    _gamepadValue = _value;
-                    _delta = joy.x;
-                    _dirty = true;
-                    eventConsumed = true;
-                }
-                break;
+                return true;
             }
+            break;
         }
     }
 
-    if (evt == Gamepad::BUTTON_EVENT && _delta == 0.0f)
-    {
-        if (gamepad->isButtonDown(Gamepad::BUTTON_A) ||
-            gamepad->isButtonDown(Gamepad::BUTTON_X))
-        {
-            _selectButtonDown = true;
-            eventConsumed |= _consumeInputEvents;
-        }
-        else if (_selectButtonDown && 
-                 !gamepad->isButtonDown(Gamepad::BUTTON_A) &&
-                 !gamepad->isButtonDown(Gamepad::BUTTON_X))
-        {
-            _selectButtonDown = false;
-
-            if (hasFocus())
-                setState(ACTIVE);
-            else if (_state == ACTIVE)
-                setState(FOCUS);
-
-            eventConsumed |= _consumeInputEvents;
-        }
-    }    
-
-    return eventConsumed;*/
-    
-    return Control::gamepadEvent(evt, gamepad, analogIndex);
+    return Label::gamepadEvent(evt, gamepad, analogIndex);
 }
 
 bool Slider::keyEvent(Keyboard::KeyEvent evt, int key)

+ 0 - 5
gameplay/src/Slider.h

@@ -164,11 +164,6 @@ public:
      */
     void addListener(Control::Listener* listener, int eventFlags);
 
-    /**
-     * @see Control#canFocus()
-     */
-    bool canFocus() const;
-
 protected:
 
     /**

+ 2 - 6
gameplay/src/TextBox.cpp

@@ -11,6 +11,7 @@ static bool space(char c)
 
 TextBox::TextBox() : _caretLocation(0), _lastKeypress(0), _fontSize(0), _caretImage(NULL), _passwordChar('*'), _inputMode(TEXT), _ctrlPressed(false)
 {
+    _canFocus = true;
 }
 
 TextBox::~TextBox()
@@ -102,7 +103,7 @@ static bool isWhitespace(char c)
     }
 }
 
-static unsigned int findNextWord(std::string& text, unsigned int from, bool backwards)
+static unsigned int findNextWord(const std::string& text, unsigned int from, bool backwards)
 {
     int pos = (int)from;
     if (backwards)
@@ -447,11 +448,6 @@ TextBox::InputMode TextBox::getInputMode() const
     return _inputMode;
 }
 
-bool TextBox::canFocus() const
-{
-    return true;
-}
-
 void TextBox::drawText(const Rectangle& clip)
 {
     if (_text.size() <= 0)

+ 0 - 5
gameplay/src/TextBox.h

@@ -126,11 +126,6 @@ public:
      */
     InputMode getInputMode() const;
 
-    /**
-     * @see Control#canFocus()
-     */
-    bool canFocus() const;
-
 protected:
 
     /**

+ 10 - 10
samples/browser/.cproject

@@ -25,7 +25,6 @@
 								<option id="com.qnx.qcc.option.compiler.security.1844270687" name="Enhanced Security (-fstack-protector-strong)" superClass="com.qnx.qcc.option.compiler.security" value="false" valueType="boolean"/>
 								<option id="com.qnx.qcc.option.compiler.defines.2033387229" name="Defines (-D)" superClass="com.qnx.qcc.option.compiler.defines" valueType="definedSymbols">
 									<listOptionValue builtIn="false" value="_FORTIFY_SOURCE=2"/>
-									<listOptionValue builtIn="false" value="GP_USE_SOCIAL"/>
 									<listOptionValue builtIn="false" value="GP_USE_GAMEPAD"/>
 								</option>
 								<option id="com.qnx.qcc.option.compiler.includePath.1368017357" name="Include Directories (-I)" superClass="com.qnx.qcc.option.compiler.includePath" valueType="includePath">
@@ -54,12 +53,13 @@
 								<option id="com.qnx.qcc.option.linker.libraryPaths.1429533021" name="Library Paths (-L)" superClass="com.qnx.qcc.option.linker.libraryPaths" valueType="libPaths">
 									<listOptionValue builtIn="false" value="${QNX_TARGET}/../target-override/armle-v7/lib"/>
 									<listOptionValue builtIn="false" value="${QNX_TARGET}/../target-override/armle-v7/usr/lib"/>
-									<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}}/../../gameplay/${ConfigName}&quot;"/>
 									<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}}/../../external-deps/lua/lib/blackberry/arm&quot;"/>
 									<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}}/../../external-deps/bullet/lib/blackberry/arm&quot;"/>
 									<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}}/../../external-deps/oggvorbis/lib/blackberry/arm&quot;"/>
+									<listOptionValue builtIn="false" value="&quot;${workspace_loc:/gameplay/${ConfigName}}&quot;"/>
 								</option>
 								<option id="com.qnx.qcc.option.linker.libraries.1174766388" name="Libraries (-l)" superClass="com.qnx.qcc.option.linker.libraries" valueType="libs">
+									<listOptionValue builtIn="false" value="gameplay"/>
 									<listOptionValue builtIn="false" value="GLESv2"/>
 									<listOptionValue builtIn="false" value="EGL"/>
 									<listOptionValue builtIn="false" value="screen"/>
@@ -70,7 +70,6 @@
 									<listOptionValue builtIn="false" value="OpenAL"/>
 									<listOptionValue builtIn="false" value="gestures"/>
 									<listOptionValue builtIn="false" value="asound"/>
-									<listOptionValue builtIn="false" value="gameplay"/>
 									<listOptionValue builtIn="false" value="lua"/>
 									<listOptionValue builtIn="false" value="bullet"/>
 									<listOptionValue builtIn="false" value="vorbis"/>
@@ -91,7 +90,9 @@
 					</sourceEntries>
 				</configuration>
 			</storageModule>
-			<storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
+			<storageModule moduleId="org.eclipse.cdt.core.externalSettings">
+				<externalSettings containerId="gameplay;" factoryId="org.eclipse.cdt.core.cfg.export.settings.sipplier"/>
+			</storageModule>
 		</cconfiguration>
 		<cconfiguration id="com.qnx.qcc.configuration.exe.release.693953760">
 			<storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="com.qnx.qcc.configuration.exe.release.693953760" moduleId="org.eclipse.cdt.core.settings" name="Device-Release">
@@ -115,7 +116,6 @@
 								<option id="com.qnx.qcc.option.compiler.security.1558473615" name="Enhanced Security (-fstack-protector-strong)" superClass="com.qnx.qcc.option.compiler.security" value="false" valueType="boolean"/>
 								<option id="com.qnx.qcc.option.compiler.defines.679430995" name="Defines (-D)" superClass="com.qnx.qcc.option.compiler.defines" valueType="definedSymbols">
 									<listOptionValue builtIn="false" value="_FORTIFY_SOURCE=2"/>
-									<listOptionValue builtIn="false" value="GP_USE_SOCIAL"/>
 									<listOptionValue builtIn="false" value="GP_USE_GAMEPAD"/>
 								</option>
 								<option id="com.qnx.qcc.option.compiler.includePath.1438345058" name="Include Directories (-I)" superClass="com.qnx.qcc.option.compiler.includePath" valueType="includePath">
@@ -142,12 +142,13 @@
 								<option id="com.qnx.qcc.option.linker.libraryPaths.1406850381" name="Library Paths (-L)" superClass="com.qnx.qcc.option.linker.libraryPaths" valueType="libPaths">
 									<listOptionValue builtIn="false" value="${QNX_TARGET}/../target-override/armle-v7/lib"/>
 									<listOptionValue builtIn="false" value="${QNX_TARGET}/../target-override/armle-v7/usr/lib"/>
-									<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}}/../../gameplay/${ConfigName}&quot;"/>
 									<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}}/../../external-deps/lua/lib/blackberry/arm&quot;"/>
 									<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}}/../../external-deps/bullet/lib/blackberry/arm&quot;"/>
 									<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}}/../../external-deps/oggvorbis/lib/blackberry/arm&quot;"/>
+									<listOptionValue builtIn="false" value="&quot;${workspace_loc:/gameplay/${ConfigName}}&quot;"/>
 								</option>
 								<option id="com.qnx.qcc.option.linker.libraries.380839761" name="Libraries (-l)" superClass="com.qnx.qcc.option.linker.libraries" valueType="libs">
+									<listOptionValue builtIn="false" value="gameplay"/>
 									<listOptionValue builtIn="false" value="GLESv2"/>
 									<listOptionValue builtIn="false" value="EGL"/>
 									<listOptionValue builtIn="false" value="screen"/>
@@ -158,7 +159,6 @@
 									<listOptionValue builtIn="false" value="OpenAL"/>
 									<listOptionValue builtIn="false" value="gestures"/>
 									<listOptionValue builtIn="false" value="asound"/>
-									<listOptionValue builtIn="false" value="gameplay"/>
 									<listOptionValue builtIn="false" value="lua"/>
 									<listOptionValue builtIn="false" value="bullet"/>
 									<listOptionValue builtIn="false" value="vorbis"/>
@@ -202,7 +202,6 @@
 								<option id="com.qnx.qcc.option.compiler.security.2051337094" name="Enhanced Security (-fstack-protector-strong)" superClass="com.qnx.qcc.option.compiler.security" value="false" valueType="boolean"/>
 								<option id="com.qnx.qcc.option.compiler.defines.1669819974" name="Defines (-D)" superClass="com.qnx.qcc.option.compiler.defines" valueType="definedSymbols">
 									<listOptionValue builtIn="false" value="_FORTIFY_SOURCE=2"/>
-									<listOptionValue builtIn="false" value="GP_USE_SOCIAL"/>
 									<listOptionValue builtIn="false" value="GP_USE_GAMEPAD"/>
 								</option>
 								<option id="com.qnx.qcc.option.compiler.includePath.456477750" name="Include Directories (-I)" superClass="com.qnx.qcc.option.compiler.includePath" valueType="includePath">
@@ -231,12 +230,13 @@
 								<option id="com.qnx.qcc.option.linker.libraryPaths.1127080358" name="Library Paths (-L)" superClass="com.qnx.qcc.option.linker.libraryPaths" valueType="libPaths">
 									<listOptionValue builtIn="false" value="${QNX_TARGET}/../target-override/x86/lib"/>
 									<listOptionValue builtIn="false" value="${QNX_TARGET}/../target-override/x86/usr/lib"/>
-									<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}}/../../gameplay/${ConfigName}&quot;"/>
 									<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}}/../../external-deps/lua/lib/blackberry/x86&quot;"/>
 									<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}}/../../external-deps/bullet/lib/blackberry/x86&quot;"/>
 									<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}}/../../external-deps/oggvorbis/lib/blackberry/x86&quot;"/>
+									<listOptionValue builtIn="false" value="&quot;${workspace_loc:/gameplay/${ConfigName}}&quot;"/>
 								</option>
 								<option id="com.qnx.qcc.option.linker.libraries.298922406" name="Libraries (-l)" superClass="com.qnx.qcc.option.linker.libraries" valueType="libs">
+									<listOptionValue builtIn="false" value="gameplay"/>
 									<listOptionValue builtIn="false" value="GLESv2"/>
 									<listOptionValue builtIn="false" value="EGL"/>
 									<listOptionValue builtIn="false" value="screen"/>
@@ -247,7 +247,6 @@
 									<listOptionValue builtIn="false" value="OpenAL"/>
 									<listOptionValue builtIn="false" value="gestures"/>
 									<listOptionValue builtIn="false" value="asound"/>
-									<listOptionValue builtIn="false" value="gameplay"/>
 									<listOptionValue builtIn="false" value="lua"/>
 									<listOptionValue builtIn="false" value="bullet"/>
 									<listOptionValue builtIn="false" value="vorbis"/>
@@ -303,4 +302,5 @@
 	</storageModule>
 	<storageModule moduleId="com.qnx.tools.ide.qde.core.QNXProjectProperties"/>
 	<storageModule moduleId="org.eclipse.cdt.core.LanguageSettingsProviders"/>
+	<storageModule moduleId="org.eclipse.cdt.internal.ui.text.commentOwnerProjectMappings"/>
 </cproject>

+ 2 - 1
samples/browser/bar-descriptor.xml

@@ -27,7 +27,8 @@
     <description>Samples</description>
 
     <!-- Name of author which is used for signing. Must match the developer name of your development certificate. -->
-    <author>RIM Canada</author>
+    <author>Steve Grenier</author>
+    <authorId>gYAAgKe4iG8txlFg4PTJpbImmxI</authorId>
 
     <!-- Unique author ID assigned by signing authority. Required if using debug tokens. -->
     <!-- <authorId>gYAAgPkLP1tZlyYP1wiMaRFFNMw</authorId> -->

+ 4 - 4
samples/browser/res/common/light.form

@@ -1,11 +1,11 @@
 form formLightSelect
 {
     theme = res/common/default.theme
-    layout = LAYOUT_HORIZONTAL
+    layout = LAYOUT_ABSOLUTE
     style = noBorder
-    alignment = ALIGN_TOP_HCENTER
-    autoWidth = true
-    autoHeight = true
+    alignment = ALIGN_TOP_LEFT
+    autoWidth = AUTO_SIZE_FIT
+    autoHeight = AUTO_SIZE_FIT
 
 	container lightingType
     {

+ 81 - 127
samples/browser/src/FormsSample.cpp

@@ -4,10 +4,8 @@
     ADD_SAMPLE("Graphics", "Forms", FormsSample, 10);
 #endif
 
-// Input bit-flags (powers of 2)
-#define KEY_A_MASK (1 << 0)
-#define KEY_B_MASK (1 << 1)
-#define KEY_SELECT_MASK (1 << 2)
+// Input bit-flags
+#define KEY_BACK 1
 
 const static unsigned int __formsCount = 5;
 
@@ -76,10 +74,10 @@ void FormsSample::initialize()
     setVsync(false);
 
     _formSelect = Form::create("res/common/forms/formSelect.form");
-    
+    _formSelect->setFocus();
+
     RadioButton* form0Button = static_cast<RadioButton*>(_formSelect->getControl("form0"));
     form0Button->addListener(this, Control::Listener::CLICK);
-    //form0Button->setState(Control::FOCUS);
 
     RadioButton* form1Button = static_cast<RadioButton*>(_formSelect->getControl("form1"));
     form1Button->addListener(this, Control::Listener::CLICK);
@@ -107,7 +105,7 @@ void FormsSample::initialize()
     createSampleForm(_forms[0]->getTheme());
 
     Button* button = static_cast<Button*>(_forms[0]->getControl("testButton"));
-    //button->setState(Control::FOCUS);
+    button->setFocus();
 
     // Create a scene with a camera node.
     Camera* camera = Camera::createPerspective(45.0f, (float)getWidth() / (float)getHeight(), 0.25f, 100.0f);
@@ -135,8 +133,7 @@ void FormsSample::formChanged()
         _activeForm->setEnabled(false);
     _activeForm = _forms[_formIndex];
     _activeForm->setEnabled(true);
-    //_activeForm->setState(Control::FOCUS);
-    //_formSelect->setState(Control::NORMAL);
+    _activeForm->setFocus();
 
     // Add the form to a node to allow it to be placed in 3D space.
     const Rectangle& bounds = _activeForm->getBounds();
@@ -183,90 +180,63 @@ void FormsSample::createSampleForm(Theme* theme)
 void FormsSample::update(float elapsedTime)
 {
     float speedFactor = 0.001f * elapsedTime;
-    bool aDown = (_keyFlags & KEY_A_MASK);
-    bool bDown = (_keyFlags & KEY_B_MASK);
 
-	// If no form is in focus, then we poll the gamepad for movement input.
-	if (_activeForm->getState() == Control::NORMAL &&
-        _formSelect->getState() == Control::NORMAL)
-    {
-        if (_gamepad->isButtonDown(Gamepad::BUTTON_A))
-            _keyFlags |= KEY_A_MASK;
-        else
-            _keyFlags &= ~KEY_A_MASK;
-
-        if (_gamepad->isButtonDown(Gamepad::BUTTON_B))
-            _keyFlags |= KEY_B_MASK;
-        else
-            _keyFlags &= ~KEY_B_MASK;
-
-        _gamepad->getJoystickValues(0, &_joysticks[0]);
-        _gamepad->getJoystickValues(1, &_joysticks[1]);
-    }
+    _gamepad->getJoystickValues(0, &_joysticks[0]);
+    _gamepad->getJoystickValues(1, &_joysticks[1]);
 
     // We'll use a physical gamepad's MENU1 button as the "back" button.
-    if (!(_keyFlags & KEY_SELECT_MASK) && _gamepad->isButtonDown(Gamepad::BUTTON_MENU1))
+    // Toggle focus between the active form, the selection form and no focus.
+    if (_gamepad->isButtonDown(Gamepad::BUTTON_MENU1))
     {
-        _keyFlags |= KEY_SELECT_MASK;
-        if (_formSelect->getState() == Control::FOCUS)
-        {
-            //_formSelect->setState(Control::NORMAL);
-        }
-        else if (_activeForm->getState() == Control::FOCUS)
-        {
-            //_activeForm->setState(Control::NORMAL);
-            //_formSelect->setState(Control::FOCUS);
-        }
-        else
+        if (!(_keyFlags & KEY_BACK))
         {
-            //_formSelect->setState(Control::FOCUS);
+            _keyFlags |= KEY_BACK;
+            if (Form::getFocusControl())
+            {
+                if (Form::getFocusControl() == _activeForm || Form::getFocusControl()->isChild(_activeForm))
+                    _formSelect->setFocus();
+                else
+                    Form::clearFocus();
+            }
+            else
+            {
+                if (!(_activeForm && _activeForm->setFocus()))
+                    _formSelect->setFocus();
+            }
         }
     }
-    else if ((_keyFlags & KEY_SELECT_MASK) && !_gamepad->isButtonDown(Gamepad::BUTTON_MENU1))
+    else
     {
-        _keyFlags &= ~KEY_SELECT_MASK;  
+        _keyFlags &= ~KEY_BACK;
     }
 
-    if (_gamepad->isVirtual())
+    // If no controls are in focus, then we poll the gamepad for movement input.
+    if (Form::getFocusControl() == NULL)
     {
-        aDown |= _gamepad->isButtonDown(Gamepad::BUTTON_A);
-        bDown |= _gamepad->isButtonDown(Gamepad::BUTTON_B);
-        _gamepad->getJoystickValues(0, &_joysticks[0]);
-    }
+        if (!_joysticks[0].isZero())
+        {
+            _formNodeParent->translate(0.5f * speedFactor * _joysticks[0].x, 0.5f * speedFactor * _joysticks[0].y, 0);
+        }
 
-    if (!_joysticks[0].isZero())
-    {
-        _gamepad->getJoystickValues(0, &_joysticks[0]);
+        if (!_joysticks[1].isZero())
+        {
+            Matrix m;
+            _formNodeParent->getWorldMatrix().transpose(&m);
+            Vector3 yaw;
+            m.getUpVector(&yaw);
+            _formNodeParent->rotate(yaw, speedFactor * _joysticks[1].x * 2.0f);
+            _formNodeParent->rotateX(-speedFactor * _joysticks[1].y * 2.0f);
+        }
     }
 
-    if (!_joysticks[1].isZero())
+    if (_gamepad->isButtonDown(Gamepad::BUTTON_A))
     {
-        Matrix m;
-        _formNodeParent->getWorldMatrix().transpose(&m);
-        Vector3 yaw;
-        m.getUpVector(&yaw);
-        _formNodeParent->rotate(yaw, speedFactor * _joysticks[1].x * 2.0f);
-        _formNodeParent->rotateX(-speedFactor * _joysticks[1].y * 2.0f);
+        _formNodeParent->setTranslation(0, 0, -1.5f);
     }
-
-    if (bDown)
+    if (_gamepad->isButtonDown(Gamepad::BUTTON_B))
     {
         _formNodeParent->setRotation(0, 0, 0, 1);
     }
-    else if (aDown)
-    {
-        // Yaw in world frame, pitch in body frame
-        Matrix m;
-        _formNodeParent->getWorldMatrix().transpose(&m);
-        Vector3 yaw;
-        m.getUpVector(&yaw);
-        _formNodeParent->rotate(yaw, speedFactor * _joysticks[0].x);
-        _formNodeParent->rotateX(-speedFactor * _joysticks[0].y);
-    }
-    else
-    {
-        _formNodeParent->translate(0.5f * speedFactor * _joysticks[0].x, 0.5f * speedFactor * _joysticks[0].y, 0);
-    }
 }
 
 void FormsSample::render(float elapsedTime)
@@ -351,28 +321,9 @@ void FormsSample::keyEvent(Keyboard::KeyEvent keyEvent, int key)
             case Keyboard::KEY_MINUS:
                 _formNodeParent->translateZ(-0.1f);
                 break;
-            case Keyboard::KEY_A:
-            case Keyboard::KEY_CAPITAL_A:
-                _keyFlags |= KEY_A_MASK;
-                break;
-            case Keyboard::KEY_B:
-            case Keyboard::KEY_CAPITAL_B:
-                _keyFlags |= KEY_B_MASK;
-                break;
             }
             break;
         case Keyboard::KEY_RELEASE:
-            switch (key)
-            {
-            case Keyboard::KEY_A:
-            case Keyboard::KEY_CAPITAL_A:
-                _keyFlags &= ~KEY_A_MASK;
-                break;
-            case Keyboard::KEY_B:
-            case Keyboard::KEY_CAPITAL_B:
-                _keyFlags &= ~KEY_B_MASK;
-                break;
-            }
             break;
         }
     }
@@ -380,41 +331,44 @@ void FormsSample::keyEvent(Keyboard::KeyEvent keyEvent, int key)
 
 void FormsSample::controlEvent(Control* control, EventType evt)
 {
-    if (strcmp("form0", control->getId()) == 0)
-    {
-        _formIndex = 0;
-        formChanged();
-    }
-    else if (strcmp("form1", control->getId()) == 0)
-    {
-        _formIndex = 1;
-        formChanged();
-    }
-    else if (strcmp("form2", control->getId()) == 0)
-    {
-        _formIndex = 2;
-        formChanged();
-    }
-    else if (strcmp("form3", control->getId()) == 0)
-    {
-        _formIndex = 3;
-        formChanged();
-    }
-    else if (strcmp("form4", control->getId()) == 0)
-    {
-        _formIndex = 4;
-        formChanged();
-    }
-    else if (strcmp("form5", control->getId()) == 0)
-    {
-        _formIndex = 5;
-        formChanged();
-    }
-    else if (strcmp("opacityButton", control->getId()) == 0)
+    if (evt == CLICK)
     {
-        float from[] = { 1.0f };
-        float to[] = { 0.5f };
-        control->createAnimationFromTo("opacityButton", Form::ANIMATE_OPACITY, from, to, Curve::LINEAR, 1000)->getClip()->play();
+        if (strcmp("form0", control->getId()) == 0)
+        {
+            _formIndex = 0;
+            formChanged();
+        }
+        else if (strcmp("form1", control->getId()) == 0)
+        {
+            _formIndex = 1;
+            formChanged();
+        }
+        else if (strcmp("form2", control->getId()) == 0)
+        {
+            _formIndex = 2;
+            formChanged();
+        }
+        else if (strcmp("form3", control->getId()) == 0)
+        {
+            _formIndex = 3;
+            formChanged();
+        }
+        else if (strcmp("form4", control->getId()) == 0)
+        {
+            _formIndex = 4;
+            formChanged();
+        }
+        else if (strcmp("form5", control->getId()) == 0)
+        {
+            _formIndex = 5;
+            formChanged();
+        }
+        else if (strcmp("opacityButton", control->getId()) == 0)
+        {
+            float from[] = { 1.0f };
+            float to[] = { 0.5f };
+            control->createAnimationFromTo("opacityButton", Form::ANIMATE_OPACITY, from, to, Curve::LINEAR, 1000)->getClip()->play();
+        }
     }
 }
 

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

@@ -135,6 +135,7 @@ void LightSample::initialize()
     _properties->setEnabled(false);
     _noLight->setSelected(true);
 	_form->setConsumeInputEvents(false);
+    _form->setFocus();
 
 	setSpecularValue(_specularSlider->getValue());
 }

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

@@ -67,7 +67,7 @@ void SamplesGame::initialize()
             sampleButton->release();
         }
     }
-    //_sampleSelectForm->setState(Control::FOCUS);
+    _sampleSelectForm->setFocus();
 
     // Disable virtual gamepads.
     unsigned int gamepadCount = getGamepadCount();
@@ -256,7 +256,7 @@ void SamplesGame::exitActiveSample()
         SAFE_DELETE(_activeSample);
 
         _sampleSelectForm->setEnabled(true);
-        //_sampleSelectForm->setState(Control::FOCUS);
+        _sampleSelectForm->setFocus();
     }
 
     // Reset some game options