Răsfoiți Sursa

Merge pull request #999 from ablake/next

Keyboard event handling for UI controls and containers.
Sean Paul Taylor 12 ani în urmă
părinte
comite
d9b733cdfa

+ 21 - 2
gameplay/src/Button.cpp

@@ -50,8 +50,8 @@ bool Button::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contac
                 y > _clipBounds.y && y <= _clipBounds.y + _clipBounds.height)
             {
                 _contactIndex = (int) contactIndex;
-                setState(Control::ACTIVE);
                 notifyListeners(Control::Listener::PRESS);
+                setState(Control::ACTIVE);
                 return _consumeInputEvents;
             }
             else
@@ -70,8 +70,8 @@ bool Button::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contac
                 x > _clipBounds.x && x <= _clipBounds.x + _clipBounds.width &&
                 y > _clipBounds.y && y <= _clipBounds.y + _clipBounds.height)
             {
-                setState(Control::FOCUS);
                 notifyListeners(Control::Listener::CLICK);
+                setState(Control::FOCUS);
             }
             else
             {
@@ -121,6 +121,25 @@ bool Button::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned
     return false;
 }
 
+bool Button::keyEvent(Keyboard::KeyEvent evt, int key)
+{
+    if (evt == Keyboard::KEY_PRESS && key == Keyboard::KEY_RETURN)
+    {
+        notifyListeners(Control::Listener::PRESS);
+        setState(Control::ACTIVE);
+        return _consumeInputEvents;
+    }
+    else if (_state == ACTIVE && evt == Keyboard::KEY_RELEASE && key == Keyboard::KEY_RETURN)
+    {
+        notifyListeners(Control::Listener::RELEASE);
+        notifyListeners(Control::Listener::CLICK);
+        setState(Control::FOCUS);
+        return _consumeInputEvents;
+    }
+
+    return false;
+}
+
 const char* Button::getType() const
 {
     return "button";

+ 8 - 0
gameplay/src/Button.h

@@ -92,6 +92,14 @@ protected:
      */
     virtual bool gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex);
 
+    /**
+     * Keyboard callback on key events.
+     *
+     * @see Keyboard::KeyEvent
+     * @see Keyboard::Key
+     */
+    virtual bool keyEvent(Keyboard::KeyEvent evt, int key);
+
     /**
      * @see Control::getType
      */

+ 11 - 0
gameplay/src/CheckBox.cpp

@@ -114,6 +114,17 @@ bool CheckBox::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigne
     return Button::gamepadEvent(evt, gamepad, analogIndex);
 }
 
+bool CheckBox::keyEvent(Keyboard::KeyEvent evt, int key)
+{
+    if (_state == ACTIVE && evt == Keyboard::KEY_RELEASE && key == Keyboard::KEY_RETURN)
+    {
+        _checked = !_checked;
+        notifyListeners(Control::Listener::VALUE_CHANGED);
+    }
+
+    return Button::keyEvent(evt, key);
+}
+
 void CheckBox::update(const Control* container, const Vector2& offset)
 {
     Label::update(container, offset);

+ 8 - 0
gameplay/src/CheckBox.h

@@ -138,6 +138,14 @@ protected:
      */
     bool gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex);
 
+    /**
+     * Keyboard callback on key events.
+     *
+     * @see Keyboard::KeyEvent
+     * @see Keyboard::Key
+     */
+    bool keyEvent(Keyboard::KeyEvent evt, int key);
+
     /**
      * Called when a control's properties change.  Updates this control's internal rendering
      * properties, such as its text viewport.

+ 83 - 10
gameplay/src/Container.cpp

@@ -635,6 +635,8 @@ bool Container::keyEvent(Keyboard::KeyEvent evt, int key)
     // need to keep it alive until the method returns.
     addRef();
 
+    bool eventConsumed = false;
+
     std::vector<Control*>::const_iterator it;
     for (it = _controls.begin(); it < _controls.end(); it++)
     {
@@ -645,22 +647,81 @@ bool Container::keyEvent(Keyboard::KeyEvent evt, int key)
             continue;
         }
 
-        if (control->hasFocus() && control->keyEvent(evt, key))
+        if ((control->hasFocus() || control->getState() == ACTIVE) && control->keyEvent(evt, key))
         {
-            release();
-            return true;
+            eventConsumed |= true;
+            break;
         }
     }
 
-    // If we made it this far, no control handled the event.
-    if (evt == Keyboard::KEY_CHAR && key == Keyboard::KEY_TAB)
+    switch (evt)
     {
-        moveFocus(NEXT);
-        return _consumeInputEvents;
+        case Keyboard::KEY_PRESS:
+        {
+            if (!eventConsumed)
+            {
+                switch (key)
+                {
+                case Keyboard::KEY_TAB:
+                    _focusPressed |= NEXT;
+                    if (moveFocus(NEXT))
+                        eventConsumed |= true;
+                    break;
+                case Keyboard::KEY_UP_ARROW:
+                    _focusPressed |= UP;
+                    if (moveFocus(UP))
+                        eventConsumed |= true;
+                    break;
+                case Keyboard::KEY_DOWN_ARROW:
+                    _focusPressed |= DOWN;
+                    if (moveFocus(DOWN))
+                        eventConsumed |= true;
+                    break;
+                case Keyboard::KEY_LEFT_ARROW:
+                    _focusPressed |= LEFT;
+                    if (moveFocus(LEFT))
+                        eventConsumed |= true;
+                    break;
+                case Keyboard::KEY_RIGHT_ARROW:
+                    _focusPressed |= RIGHT;
+                    if (moveFocus(RIGHT))
+                        eventConsumed |= true;
+                    break;
+                }
+            }
+            break;
+        }
+        case Keyboard::KEY_RELEASE:
+        {
+            switch (key)
+            {
+            case Keyboard::KEY_TAB:
+                _focusPressed &= ~NEXT;
+                eventConsumed |= true;
+                break;
+            case Keyboard::KEY_UP_ARROW:
+                _focusPressed &= ~UP;
+                eventConsumed |= true;
+                break;
+            case Keyboard::KEY_DOWN_ARROW:
+                _focusPressed &= ~DOWN;
+                eventConsumed |= true;
+                break;
+            case Keyboard::KEY_LEFT_ARROW:
+                _focusPressed &= ~LEFT;
+                eventConsumed |= true;
+                break;
+            case Keyboard::KEY_RIGHT_ARROW:
+                _focusPressed &= ~RIGHT;
+                eventConsumed |= true;
+                break;
+            }
+            break;
+        }
     }
 
     release();
-    return false;
+    return eventConsumed;
 }
 
 void Container::guaranteeFocus(Control* inFocus)
@@ -787,10 +848,11 @@ bool Container::moveFocus(Direction direction, Control* outsideControl)
         if (!next)
         {
             // Check for controls in the given direction in our parent container.
-            if (!outsideControl && _parent && _parent->moveFocus(direction, start))
+            if (direction != NEXT && !outsideControl && _parent && _parent->moveFocus(direction, start))
             {
                 setState(NORMAL);
                 _focusChangeRepeat = false;
+                _focusPressed = 0;
                 return true;
             }
             
@@ -821,6 +883,14 @@ bool Container::moveFocus(Direction direction, Control* outsideControl)
 
             if (focusIndex > _focusIndexMax)
             {
+                if (direction == NEXT && !outsideControl && _parent && _parent->moveFocus(direction, start))
+                {
+                    setState(NORMAL);
+                    _focusChangeRepeat = false;
+                    _focusPressed = 0;
+                    return true;
+                }
+
                 focusIndex = 0;
             }
             else if (focusIndex < 0)
@@ -839,6 +909,7 @@ bool Container::moveFocus(Direction direction, Control* outsideControl)
             if (nextControl->getFocusIndex() == focusIndex)
             {
                 next = nextControl;
+                break;
             }
         }
     }
@@ -851,9 +922,11 @@ bool Container::moveFocus(Direction direction, Control* outsideControl)
 
         if (next->isContainer())
         {
-            if (((Container*)next)->moveFocus(direction, start))
+            if ((direction == NEXT && ((Container*)next)->moveFocus(direction)) ||
+                ((Container*)next)->moveFocus(direction, start))
             {
                 _focusChangeRepeat = false;
+                _focusPressed = 0;
                 return true;
             }
         }

+ 3 - 2
gameplay/src/Form.cpp

@@ -20,7 +20,8 @@ namespace gameplay
 static Effect* __formEffect = NULL;
 static std::vector<Form*> __forms;
 
-Form::Form() : _theme(NULL), _frameBuffer(NULL), _spriteBatch(NULL), _node(NULL), _nodeQuad(NULL), _nodeMaterial(NULL) , _u2(0), _v1(0)
+Form::Form() : _theme(NULL), _frameBuffer(NULL), _spriteBatch(NULL), _node(NULL),
+    _nodeQuad(NULL), _nodeMaterial(NULL) , _u2(0), _v1(0), _isGamepad(false)
 {
 }
 
@@ -659,7 +660,7 @@ bool Form::keyEventInternal(Keyboard::KeyEvent evt, int key)
     {
         Form* form = __forms[i];
         GP_ASSERT(form);
-        if (form->isEnabled() && form->isVisible() && form->hasFocus())
+        if (form->isEnabled() && form->isVisible() && form->hasFocus() && !form->_isGamepad)
         {
             if (form->keyEvent(evt, key))
                 return true;

+ 1 - 0
gameplay/src/Form.h

@@ -256,6 +256,7 @@ private:
     float _v1;
     Matrix _projectionMatrix;           // Orthographic projection matrix to be set on SpriteBatch objects when rendering into the FBO.
     Matrix _defaultProjectionMatrix;
+    bool _isGamepad;
 };
 
 }

+ 1 - 0
gameplay/src/Gamepad.cpp

@@ -19,6 +19,7 @@ Gamepad::Gamepad(const char* formPath)
     _form = Form::create(formPath);
     GP_ASSERT(_form);
     _form->setConsumeInputEvents(false);
+    _form->_isGamepad = true;
     _vendorString = "None";
     _productString = "Virtual";
 

+ 12 - 0
gameplay/src/RadioButton.cpp

@@ -153,6 +153,18 @@ void RadioButton::clearSelected(const std::string& groupId)
     }
 }
 
+bool RadioButton::keyEvent(Keyboard::KeyEvent evt, int key)
+{
+    if (_state == ACTIVE && evt == Keyboard::KEY_RELEASE && key == Keyboard::KEY_RETURN && !_selected)
+    {
+        RadioButton::clearSelected(_groupId);
+        _selected = true;
+        notifyListeners(Control::Listener::VALUE_CHANGED);
+    }
+
+    return Button::keyEvent(evt, key);
+}
+
 void RadioButton::update(const Control* container, const Vector2& offset)
 {
     Label::update(container, offset);

+ 8 - 0
gameplay/src/RadioButton.h

@@ -151,6 +151,14 @@ protected:
      */
     bool gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex);
 
+    /**
+     * Keyboard callback on key events.
+     *
+     * @see Keyboard::KeyEvent
+     * @see Keyboard::Key
+     */
+    bool keyEvent(Keyboard::KeyEvent evt, int key);
+
     /**
      * Called when a control's properties change.  Updates this control's internal rendering
      * properties, such as its text viewport.

+ 64 - 0
gameplay/src/Slider.cpp

@@ -378,6 +378,70 @@ bool Slider::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned
     return eventConsumed;
 }
 
+bool Slider::keyEvent(Keyboard::KeyEvent evt, int key)
+{
+    if (_state == ACTIVE)
+    {
+        switch (evt)
+        {
+        case Keyboard::KEY_PRESS:
+            switch (key)
+            {
+            case Keyboard::KEY_LEFT_ARROW:
+                _delta = -1.0f;
+                _directionButtonDown = true;
+                _dirty = true;
+                _gamepadValue = _value;
+                return true;
+
+            case Keyboard::KEY_RIGHT_ARROW:
+                _delta = 1.0f;
+                _directionButtonDown = true;
+                _dirty = true;
+                _gamepadValue = _value;
+                return true;
+            }
+            break;
+
+        case Keyboard::KEY_RELEASE:
+            switch (key)
+            {
+            case Keyboard::KEY_LEFT_ARROW:
+                if (_delta == -1.0f)
+                {
+                    _directionButtonDown = false;
+                    _dirty = true;
+                    _delta = 0.0f;
+                    return true;
+                }
+                break;
+
+            case Keyboard::KEY_RIGHT_ARROW:
+                if (_delta == 1.0f)
+                {
+                    _directionButtonDown = false;
+                    _dirty = true;
+                    _delta = 0.0f;
+                    return true;
+                }
+                break;
+            }
+        }
+    }
+
+    if (evt == Keyboard::KEY_PRESS && key == Keyboard::KEY_RETURN)
+    {
+        if (hasFocus())
+            setState(ACTIVE);
+        else if (_state == ACTIVE)
+            setState(FOCUS);
+
+        return _consumeInputEvents;
+    }
+
+    return Control::keyEvent(evt, key);
+}
+
 void Slider::update(const Control* container, const Vector2& offset)
 {
     Label::update(container, offset);

+ 8 - 0
gameplay/src/Slider.h

@@ -221,6 +221,14 @@ protected:
      */
     bool gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex);
 
+    /**
+     * Keyboard callback on key events.
+     *
+     * @see Keyboard::KeyEvent
+     * @see Keyboard::Key
+     */
+    bool keyEvent(Keyboard::KeyEvent evt, int key);
+
     /**
      * Slider overrides draw() so that it can avoid resetting the _dirty flag
      * when a joystick is being used to change its value.

+ 8 - 0
gameplay/src/TextBox.cpp

@@ -143,6 +143,12 @@ bool TextBox::keyEvent(Keyboard::KeyEvent evt, int key)
                     notifyListeners(Control::Listener::TEXT_CHANGED);
                     break;
                 }
+                case Keyboard::KEY_TAB:
+                {
+                    // Allow tab to move the focus forward.
+                    return false;
+                    break;
+                }
                 case Keyboard::KEY_LEFT_ARROW:
                 {
                     Font* font = getFont(_state);
@@ -306,6 +312,8 @@ bool TextBox::keyEvent(Keyboard::KeyEvent evt, int key)
             notifyListeners(Control::Listener::TEXT_CHANGED);
             break;
         }
+        case Keyboard::KEY_RELEASE:
+            return false;
     }
 
     _lastKeypress = key;

+ 0 - 2
gameplay/src/Theme.cpp

@@ -475,8 +475,6 @@ Theme::Style* Theme::getEmptyStyle()
         Theme::Style::Overlay* overlay = Theme::Style::Overlay::create();
         overlay->addRef();
         overlay->addRef();
-        overlay->addRef();
-        overlay->addRef();
         emptyStyle = new Theme::Style(const_cast<Theme*>(this), "EMPTY_STYLE", 1.0f / _texture->getWidth(), 1.0f / _texture->getHeight(),
             Theme::Margin::empty(), Theme::Border::empty(), overlay, overlay, NULL, overlay, NULL);
 

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

@@ -36,7 +36,6 @@ Control::Listener::EventType lua_enumFromString_ControlListenerEventType(const c
         return Control::Listener::ENTER;
     if (strcmp(s, luaEnumString_ControlListenerEventType_LEAVE) == 0)
         return Control::Listener::LEAVE;
-    GP_ERROR("Invalid enumeration value '%s' for enumeration Control::Listener::EventType.", s);
     return Control::Listener::PRESS;
 }
 
@@ -60,7 +59,6 @@ const char* lua_stringFromEnum_ControlListenerEventType(Control::Listener::Event
         return luaEnumString_ControlListenerEventType_ENTER;
     if (e == Control::Listener::LEAVE)
         return luaEnumString_ControlListenerEventType_LEAVE;
-    GP_ERROR("Invalid enumeration value '%d' for enumeration Control::Listener::EventType.", e);
     return enumStringEmpty;
 }
 

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

@@ -24,7 +24,6 @@ Control::State lua_enumFromString_ControlState(const char* s)
         return Control::DISABLED;
     if (strcmp(s, luaEnumString_ControlState_HOVER) == 0)
         return Control::HOVER;
-    GP_ERROR("Invalid enumeration value '%s' for enumeration Control::State.", s);
     return Control::NORMAL;
 }
 
@@ -40,7 +39,6 @@ const char* lua_stringFromEnum_ControlState(Control::State e)
         return luaEnumString_ControlState_DISABLED;
     if (e == Control::HOVER)
         return luaEnumString_ControlState_HOVER;
-    GP_ERROR("Invalid enumeration value '%d' for enumeration Control::State.", e);
     return enumStringEmpty;
 }
 

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

@@ -48,6 +48,7 @@ void luaRegister_Game()
         {"getState", lua_Game_getState},
         {"getViewport", lua_Game_getViewport},
         {"getWidth", lua_Game_getWidth},
+        {"hasAccelerometer", lua_Game_hasAccelerometer},
         {"hasMouse", lua_Game_hasMouse},
         {"isCursorVisible", lua_Game_isCursorVisible},
         {"isGestureRegistered", lua_Game_isGestureRegistered},
@@ -1226,6 +1227,41 @@ int lua_Game_getWidth(lua_State* state)
     return 0;
 }
 
+int lua_Game_hasAccelerometer(lua_State* state)
+{
+    // Get the number of parameters.
+    int paramCount = lua_gettop(state);
+
+    // Attempt to match the parameters to a valid binding.
+    switch (paramCount)
+    {
+        case 1:
+        {
+            if ((lua_type(state, 1) == LUA_TUSERDATA))
+            {
+                Game* instance = getInstance(state);
+                bool result = instance->hasAccelerometer();
+
+                // Push the return value onto the stack.
+                lua_pushboolean(state, result);
+
+                return 1;
+            }
+
+            lua_pushstring(state, "lua_Game_hasAccelerometer - Failed to match the given parameters to a valid function signature.");
+            lua_error(state);
+            break;
+        }
+        default:
+        {
+            lua_pushstring(state, "Invalid number of parameters (expected 1).");
+            lua_error(state);
+            break;
+        }
+    }
+    return 0;
+}
+
 int lua_Game_hasMouse(lua_State* state)
 {
     // Get the number of parameters.

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

@@ -31,6 +31,7 @@ int lua_Game_getScriptController(lua_State* state);
 int lua_Game_getState(lua_State* state);
 int lua_Game_getViewport(lua_State* state);
 int lua_Game_getWidth(lua_State* state);
+int lua_Game_hasAccelerometer(lua_State* state);
 int lua_Game_hasMouse(lua_State* state);
 int lua_Game_isCursorVisible(lua_State* state);
 int lua_Game_isGestureRegistered(lua_State* state);

+ 1 - 0
samples/browser/res/common/forms/formBasicControls.form

@@ -42,6 +42,7 @@ form basicControls
 		//step = 5
 		text = This is a slider.
 		valueTextVisible = true
+		consumeInputEvents = true
 	}
 
     label textBoxLabel