Преглед изворни кода

Fixed next and previous focus movement (i.e. TAB key) for UI and made it work with any arbitrary focus indexes, instead of only when controls have perfectly sequential focus indexes.

sgrenier пре 12 година
родитељ
комит
b0798a6210
2 измењених фајлова са 136 додато и 15 уклоњено
  1. 106 14
      gameplay/src/Container.cpp
  2. 30 1
      gameplay/src/Form.cpp

+ 106 - 14
gameplay/src/Container.cpp

@@ -789,47 +789,139 @@ bool Container::isDirty()
     return false;
 }
 
+static bool canReceiveFocus(Control* control)
+{
+    if (!(control->isEnabled() && control->isVisible()))
+        return false;
+
+    if (control->canFocus())
+        return true;
+
+    if (control->isContainer())
+    {
+        Container* container = static_cast<Container*>(control);
+        for (unsigned int i = 0, count = (unsigned int)container->getControlCount(); i < count; ++i)
+        {
+            if (canReceiveFocus(container->getControl(i)))
+                return true;
+        }
+    }
+
+    return false;
+}
+
 bool Container::moveFocus(Direction direction)
 {
     switch (direction)
     {
     case UP:
         return moveFocus(Vector2(0, -1));
-
     case DOWN:
         return moveFocus(Vector2(0, 1));
-
     case LEFT:
         return moveFocus(Vector2(-1, 0));
-
     case RIGHT:
         return moveFocus(Vector2(1, 0));
     }
 
-    // Get the current control that has focus within this container
+    // Get the current control that has focus (either directly or indirectly) within this container
     Control* current = NULL;
-    if (Form::_focusControl && Form::_focusControl->_parent == this)
-        current = Form::_focusControl;
+    if (Form::_focusControl && Form::_focusControl->isChild(this))
+    {
+        if (Form::_focusControl->_parent == this)
+        {
+            // Currently focused control is a direct child of us
+            current = Form::_focusControl;
+        }
+        else
+        {
+            // Currently focused control is a child of one of our child containers
+            for (size_t i = 0, count = _controls.size(); i < count; ++i)
+            {
+                if (Form::_focusControl->isChild(_controls[i]))
+                {
+                    current = _controls[i];
+                    break;
+                }
+            }
+        }
+    }
+
+    Control* nextCtrl = NULL;
+    int nextIndex = direction == NEXT ? INT_MAX : INT_MIN;
+    bool moveFirst = false;
 
     if (current)
     {
-        // Find the next control in this container in the specified direction
-        int focusDir = direction == PREVIOUS ? -1 : 1;
+        // There is a control inside us that currently has focus, so find the next control that
+        // should receive focus.
+        int focusableControlCount = 0; // track the number of valid focusable controls in this container
+
+        for (size_t i = 0, count = _controls.size(); i < count; ++i)
+        {
+            Control* ctrl = _controls[i];
+            if (ctrl->_focusIndex < 0 || !canReceiveFocus(ctrl))
+                continue;
+
+            if ((direction == NEXT && ctrl->_focusIndex > current->_focusIndex && ctrl->_focusIndex < nextIndex) ||
+                (direction == PREVIOUS && ctrl->_focusIndex < current->_focusIndex && ctrl->_focusIndex > nextIndex))
+            {
+                nextCtrl = ctrl;
+                nextIndex = ctrl->_focusIndex;
+            }
+
+            ++focusableControlCount;
+        }
+
+        if (nextCtrl)
+        {
+            if (nextCtrl->isContainer() && static_cast<Container*>(nextCtrl)->moveFocus(direction))
+                return true;
+            if (nextCtrl->setFocus())
+                return true;
+        }
+
+        // Search up into our parent container for a focus move
+        if (_parent && _parent->isContainer() && static_cast<Container*>(_parent)->moveFocus(direction))
+            return true;
+
+        // We didn't find a control to move to, so we must be the first or last focusable control in our parent.
+        // Wrap focus to the other side of the container.
+        if (focusableControlCount > 1)
+        {
+            moveFirst = true;
+        }
     }
     else
     {
-        // No controls within this container have focus, so set focus to the first or last focus control
-        Control* next = NULL;
+        moveFirst = true;
+    }
+
+    if (moveFirst)
+    {
+
+        nextIndex = direction == NEXT ? INT_MAX : INT_MIN;
+        nextCtrl = NULL;
         for (size_t i = 0, count = _controls.size(); i < count; ++i)
         {
             Control* ctrl = _controls[i];
-            if (ctrl->canFocus())
+            if (ctrl->_focusIndex < 0 || !canReceiveFocus(ctrl))
+                continue;
+            if ((direction == NEXT && ctrl->_focusIndex < nextIndex) ||
+                (direction == PREVIOUS && ctrl->_focusIndex > nextIndex))
             {
-                if (!next || (direction == NEXT && ctrl->_focusIndex < next->_focusIndex) || (direction == PREVIOUS && ctrl->_focusIndex > next->_focusIndex))
-                    next = ctrl;
+                nextCtrl = ctrl;
+                nextIndex = ctrl->_focusIndex;
             }
         }
-        //if (next && next->setFocus())
+
+        if (nextCtrl)
+        {
+            if (nextCtrl->isContainer() && static_cast<Container*>(nextCtrl)->moveFocus(direction))
+                return true;
+            if (nextCtrl->setFocus())
+                return true;
+        }
     }
 
     return false;

+ 30 - 1
gameplay/src/Form.cpp

@@ -1163,11 +1163,40 @@ void Form::setFocusControl(Control* control)
         _focusControl->notifyListeners(Control::Listener::FOCUS_GAINED);
 
         // Set the activeControl property of the control's parent container
+        Container* parent = NULL;
         if (_focusControl->_parent && _focusControl->_parent->isContainer())
         {
-            Container* parent = static_cast<Container*>(_focusControl->_parent);
+            parent = static_cast<Container*>(_focusControl->_parent);
             parent->_activeControl = _focusControl;
         }
+
+        // If this control is inside a scrollable container and is not fully visible,
+        // scroll the container so that it is.
+        if (parent && parent->_scroll != SCROLL_NONE && !parent->_viewportBounds.isEmpty())
+        {
+            const Rectangle& bounds = _focusControl->getBounds();
+            if (bounds.x < parent->_scrollPosition.x)
+            {
+                // Control is to the left of the scrolled viewport.
+                parent->_scrollPosition.x = -bounds.x;
+            }
+            else if (bounds.x + bounds.width > parent->_scrollPosition.x + parent->_viewportBounds.width)
+            {
+                // Control is off to the right.
+                parent->_scrollPosition.x = -(bounds.x + bounds.width - parent->_viewportBounds.width);
+            }
+
+            if (bounds.y < parent->_viewportBounds.y - parent->_scrollPosition.y)
+            {
+                // Control is above the viewport.
+                parent->_scrollPosition.y = -bounds.y;
+            }
+            else if (bounds.y + bounds.height > parent->_viewportBounds.height - parent->_scrollPosition.y)
+            {
+                // Control is below the viewport.
+                parent->_scrollPosition.y = -(bounds.y + bounds.height - parent->_viewportBounds.height);
+            }
+        }
     }
 }