Browse Source

UI layout optimizations.
Updated UI controls to track dirty state in such a way that layouts should no longer occur every frame. Layout should now only happen when a control is resized or some other property affecting layout changes.
For optimization purposes, UI now determines layout and auto-sizing based on the NORMAL UI/input state. This means that you cannot have a hover or active state for a UI control that changes the size of the control.

sgrenier 12 years ago
parent
commit
164e20ce81

+ 11 - 15
gameplay/src/CheckBox.cpp

@@ -94,31 +94,26 @@ void CheckBox::updateState(State state)
     _image = getImage(_checked ? "checked" : "unchecked", state);
     _image = getImage(_checked ? "checked" : "unchecked", state);
 }
 }
 
 
-bool CheckBox::updateBounds(const Vector2& offset)
+void CheckBox::updateBounds()
 {
 {
-    bool changed = Label::updateBounds(offset);
-
-    // Update bounds based on normal state only
-    Control::State state = NORMAL;
+    Label::updateBounds();
 
 
     Vector2 size;
     Vector2 size;
     if (_checked)
     if (_checked)
     {
     {
-        const Rectangle& selectedRegion = getImageRegion("checked", state);
+        const Rectangle& selectedRegion = getImageRegion("checked", NORMAL);
         size.set(selectedRegion.width, selectedRegion.height);
         size.set(selectedRegion.width, selectedRegion.height);
     }
     }
     else
     else
     {
     {
-        const Rectangle& unselectedRegion = getImageRegion("unchecked", state);
+        const Rectangle& unselectedRegion = getImageRegion("unchecked", NORMAL);
         size.set(unselectedRegion.width, unselectedRegion.height);
         size.set(unselectedRegion.width, unselectedRegion.height);
     }
     }
 
 
-    Rectangle oldBounds(_bounds);
-
     if (_autoSize & AUTO_SIZE_HEIGHT)
     if (_autoSize & AUTO_SIZE_HEIGHT)
     {
     {
         // Text-only width was already measured in Label::update - append image
         // Text-only width was already measured in Label::update - append image
-        const Theme::Border& border = getBorder(state);
+        const Theme::Border& border = getBorder(NORMAL);
         const Theme::Border& padding = getPadding();
         const Theme::Border& padding = getPadding();
         setHeightInternal(std::max(_bounds.height, size.y + border.top + border.bottom + padding.top + padding.bottom));
         setHeightInternal(std::max(_bounds.height, size.y + border.top + border.bottom + padding.top + padding.bottom));
     }
     }
@@ -126,14 +121,15 @@ bool CheckBox::updateBounds(const Vector2& offset)
     if (_autoSize & AUTO_SIZE_WIDTH)
     if (_autoSize & AUTO_SIZE_WIDTH)
     {
     {
         // Text-only width was already measured in Label::update - append image
         // Text-only width was already measured in Label::update - append image
-        setWidthInternal(_viewportBounds.height + 5 + _bounds.width);
+        setWidthInternal(_bounds.height + 5 + _bounds.width);
     }
     }
+}
 
 
-    changed = changed || (_bounds != oldBounds);
-
-    _textBounds.x += _viewportBounds.height + 5;
+void CheckBox::updateAbsoluteBounds(const Vector2& offset)
+{
+    Label::updateAbsoluteBounds(offset);
 
 
-    return changed;
+    _textBounds.x += _bounds.height + 5;
 }
 }
 
 
 unsigned int CheckBox::drawImages(Form* form, const Rectangle& clip)
 unsigned int CheckBox::drawImages(Form* form, const Rectangle& clip)

+ 6 - 1
gameplay/src/CheckBox.h

@@ -113,7 +113,12 @@ protected:
     /**
     /**
      * @see Control::updateBounds
      * @see Control::updateBounds
      */
      */
-    bool updateBounds(const Vector2& offset);
+    void updateBounds();
+
+    /**
+     * @see Control::updateAbsoluteBounds
+     */
+    void updateAbsoluteBounds(const Vector2& offset);
 
 
     /**
     /**
      * @see Control::drawImages
      * @see Control::drawImages

+ 56 - 74
gameplay/src/Container.cpp

@@ -57,7 +57,7 @@ Container::Container()
       _scrollingMouseVertically(false), _scrollingMouseHorizontally(false),
       _scrollingMouseVertically(false), _scrollingMouseHorizontally(false),
       _scrollBarOpacityClip(NULL), _zIndexDefault(0),
       _scrollBarOpacityClip(NULL), _zIndexDefault(0),
       _selectButtonDown(false), _lastFrameTime(0), _totalWidth(0), _totalHeight(0),
       _selectButtonDown(false), _lastFrameTime(0), _totalWidth(0), _totalHeight(0),
-      _initializedWithScroll(false), _scrollWheelRequiresFocus(false), _inRelayout(false)
+      _initializedWithScroll(false), _scrollWheelRequiresFocus(false)
 {
 {
 	clearContacts();
 	clearContacts();
 }
 }
@@ -540,54 +540,12 @@ void Container::updateState(State state)
     }
     }
 }
 }
 
 
-bool Container::updateBounds(const Vector2& offset)
+void Container::updateBounds()
 {
 {
-    // Update our child bounds first
-    updateChildBounds();
-
-    /*if ((_dirtyBits & DIRTY_BOUNDS) == 0)
-    {
-        // We're not dirty, but our children might be
-        updateChildBounds();
-
-        // If we're still not dirty after updating our child bounds, we have nothing to do
-        if ((_dirtyBits & DIRTY_BOUNDS) == 0)
-            return false;
-    }*/
-
-    bool changed = Control::updateBounds(offset);
-
-    Control::State state = getState();
-
-    // Get scrollbar images and diminish clipping bounds to make room for scrollbars.
-    if ((_scroll & SCROLL_HORIZONTAL) == SCROLL_HORIZONTAL)
-    {
-        GP_ASSERT(_scrollBarLeftCap && _scrollBarHorizontal && _scrollBarRightCap);
-        _viewportBounds.height -= _scrollBarHorizontal->getRegion().height;
-        _viewportClipBounds.height -= _scrollBarHorizontal->getRegion().height;
-    }
-
-    if ((_scroll & SCROLL_VERTICAL) == SCROLL_VERTICAL)
-    {
-        GP_ASSERT(_scrollBarTopCap && _scrollBarVertical && _scrollBarBottomCap);
-        _viewportBounds.width -= _scrollBarVertical->getRegion().width;
-        _viewportClipBounds.width -= _scrollBarVertical->getRegion().width;
-    }
-
-    // Update scroll position and scrollbars
-    updateScroll();
-
-    // Update layout
-    GP_ASSERT(_layout);
-    _layout->update(this);
-
-    // Update bounds of our children
-    //updateChildBounds();
-
-    changed = changed || (_dirtyBits & DIRTY_BOUNDS);
+    // Compute total bounds of container
+    Control::updateBounds();
 
 
     // Handle automatically sizing based on our children
     // Handle automatically sizing based on our children
-    Rectangle oldBounds(_bounds);
     if (_autoSize != AUTO_SIZE_NONE)
     if (_autoSize != AUTO_SIZE_NONE)
     {
     {
         if (_autoSize & AUTO_SIZE_WIDTH)
         if (_autoSize & AUTO_SIZE_WIDTH)
@@ -604,7 +562,7 @@ bool Container::updateBounds(const Vector2& offset)
                         width = w;
                         width = w;
                 }
                 }
             }
             }
-            width += getBorder(state).left + getBorder(state).right + getPadding().left + getPadding().right;
+            width += getBorder(NORMAL).left + getBorder(NORMAL).right + getPadding().left + getPadding().right;
             setWidthInternal(width);
             setWidthInternal(width);
         }
         }
 
 
@@ -622,51 +580,55 @@ bool Container::updateBounds(const Vector2& offset)
                         height = h;
                         height = h;
                 }
                 }
             }
             }
-            height += getBorder(state).top + getBorder(state).bottom + getPadding().top + getPadding().bottom;
+            height += getBorder(NORMAL).top + getBorder(NORMAL).bottom + getPadding().top + getPadding().bottom;
             setHeightInternal(height);
             setHeightInternal(height);
         }
         }
     }
     }
-    changed = changed || (_bounds != oldBounds);
 
 
-    // If our bounds have changed, dirty our entire child hierarchy since at a minimum their absolute bounds
-    // will need to be recomputed to be properly offset from our new bounds.
-    if (changed)
+    // Update layout to position children correctly within us
+    GP_ASSERT(_layout);
+    _layout->update(this);
+}
+
+void Container::updateAbsoluteBounds(const Vector2& offset)
+{
+    Control::updateAbsoluteBounds(offset);
+
+    // Get scrollbar images and diminish clipping bounds to make room for scrollbars.
+    if ((_scroll & SCROLL_HORIZONTAL) == SCROLL_HORIZONTAL)
     {
     {
-        setChildrenDirty(DIRTY_BOUNDS, true);
+        GP_ASSERT(_scrollBarLeftCap && _scrollBarHorizontal && _scrollBarRightCap);
+        _viewportBounds.height -= _scrollBarHorizontal->getRegion().height;
+        _viewportClipBounds.height -= _scrollBarHorizontal->getRegion().height;
     }
     }
-    /*if ((_bounds != oldBounds) || (_dirtyBits & DIRTY_BOUNDS))
-    {
-        //setDirty(DIRTY_BOUNDS);
-        setChildrenDirty(DIRTY_BOUNDS, true);
 
 
-        if (!_inRelayout)
-        {
-            // Perform a second pass layout to update ourself based on our children and our children based on ourself
-            _inRelayout = true;
-            //updateBounds(offset);
-            _inRelayout = false;
-        }
-    }*/
+    if ((_scroll & SCROLL_VERTICAL) == SCROLL_VERTICAL)
+    {
+        GP_ASSERT(_scrollBarTopCap && _scrollBarVertical && _scrollBarBottomCap);
+        _viewportBounds.width -= _scrollBarVertical->getRegion().width;
+        _viewportClipBounds.width -= _scrollBarVertical->getRegion().width;
+    }
 
 
-    return changed;
+    // Update scroll position and scrollbars after updating absolute bounds since
+    // computation relies on up-to-date absolute bounds information.
+    updateScroll();
 }
 }
 
 
-void Container::updateChildBounds()
+bool Container::updateChildBounds()
 {
 {
+    bool result = false;
+
     for (size_t i = 0, count = _controls.size(); i < count; ++i)
     for (size_t i = 0, count = _controls.size(); i < count; ++i)
     {
     {
         Control* ctrl = _controls[i];
         Control* ctrl = _controls[i];
         GP_ASSERT(ctrl);
         GP_ASSERT(ctrl);
 
 
-        if (ctrl->isVisible() && (ctrl->isContainer() || (ctrl->_dirtyBits & DIRTY_BOUNDS)))
+        if (ctrl->isVisible())
         {
         {
-            //Rectangle oldBounds(ctrl->_absoluteBounds);
-
-            bool changed = ctrl->updateBounds(_scrollPosition);
+            bool changed = ctrl->updateBoundsInternal(_scrollPosition);
 
 
             // If the child bounds have changed, dirty our bounds and all of our
             // If the child bounds have changed, dirty our bounds and all of our
             // parent bounds so that our layout and/or bounds are recomputed.
             // parent bounds so that our layout and/or bounds are recomputed.
-            //if (ctrl->_absoluteBounds != oldBounds)
             if (changed)
             if (changed)
             {
             {
                 Control* parent = this;
                 Control* parent = this;
@@ -676,8 +638,12 @@ void Container::updateChildBounds()
                     parent = parent->_parent;
                     parent = parent->_parent;
                 }
                 }
             }
             }
+
+            result = result || changed;
         }
         }
     }
     }
+
+    return result;
 }
 }
 
 
 unsigned int Container::draw(Form* form, const Rectangle& clip)
 unsigned int Container::draw(Form* form, const Rectangle& clip)
@@ -1136,6 +1102,8 @@ void Container::updateScroll()
     float clipWidth = _absoluteBounds.width - containerBorder.left - containerBorder.right - containerPadding.left - containerPadding.right - vWidth;
     float clipWidth = _absoluteBounds.width - containerBorder.left - containerBorder.right - containerPadding.left - containerPadding.right - vWidth;
     float clipHeight = _absoluteBounds.height - containerBorder.top - containerBorder.bottom - containerPadding.top - containerPadding.bottom - hHeight;
     float clipHeight = _absoluteBounds.height - containerBorder.top - containerBorder.bottom - containerPadding.top - containerPadding.bottom - hHeight;
 
 
+    bool dirty = false;
+
     // Apply and dampen inertia.
     // Apply and dampen inertia.
     if (!_scrollingVelocity.isZero())
     if (!_scrollingVelocity.isZero())
     {
     {
@@ -1157,8 +1125,7 @@ void Container::updateScroll()
                 _scrollingVelocity.y = 0.0f;
                 _scrollingVelocity.y = 0.0f;
         }
         }
 
 
-        setDirty(DIRTY_BOUNDS);
-        setChildrenDirty(DIRTY_BOUNDS, true);
+        dirty = true;
     }
     }
 
 
     // Stop scrolling when the far edge is reached.
     // Stop scrolling when the far edge is reached.
@@ -1166,24 +1133,28 @@ void Container::updateScroll()
     {
     {
         _scrollPosition.x = -(_totalWidth - clipWidth);
         _scrollPosition.x = -(_totalWidth - clipWidth);
         _scrollingVelocity.x = 0;
         _scrollingVelocity.x = 0;
+        dirty = true;
     }
     }
     
     
     if (-_scrollPosition.y > _totalHeight - clipHeight)
     if (-_scrollPosition.y > _totalHeight - clipHeight)
     {
     {
         _scrollPosition.y = -(_totalHeight - clipHeight);
         _scrollPosition.y = -(_totalHeight - clipHeight);
         _scrollingVelocity.y = 0;
         _scrollingVelocity.y = 0;
+        dirty = true;
     }
     }
 
 
     if (_scrollPosition.x > 0)
     if (_scrollPosition.x > 0)
     {
     {
         _scrollPosition.x = 0;
         _scrollPosition.x = 0;
         _scrollingVelocity.x = 0;
         _scrollingVelocity.x = 0;
+        dirty = true;
     }
     }
 
 
     if (_scrollPosition.y > 0)
     if (_scrollPosition.y > 0)
     {
     {
         _scrollPosition.y = 0;
         _scrollPosition.y = 0;
         _scrollingVelocity.y = 0;
         _scrollingVelocity.y = 0;
+        dirty = true;
     }
     }
 
 
     float scrollWidth = 0;
     float scrollWidth = 0;
@@ -1210,6 +1181,14 @@ void Container::updateScroll()
         }
         }
         _scrollBarOpacityClip->play();
         _scrollBarOpacityClip->play();
     }
     }
+
+    // When scroll position is updated, we need to recompute bounds since children
+    // absolute bounds offset will need to be updated.
+    if (dirty)
+    {
+        setDirty(DIRTY_BOUNDS);
+        setChildrenDirty(DIRTY_BOUNDS, true);
+    }
 }
 }
 
 
 void Container::sortControls()
 void Container::sortControls()
@@ -1438,7 +1417,10 @@ bool Container::mouseEventScroll(Mouse::MouseEvent evt, int x, int y, int wheelD
             }
             }
 
 
             if (dirty)
             if (dirty)
+            {
+                setDirty(DIRTY_BOUNDS);
                 setChildrenDirty(DIRTY_BOUNDS, true);
                 setChildrenDirty(DIRTY_BOUNDS, true);
+            }
 
 
             return touchEventScroll(Touch::TOUCH_PRESS, x, y, 0);
             return touchEventScroll(Touch::TOUCH_PRESS, x, y, 0);
         }
         }

+ 7 - 3
gameplay/src/Container.h

@@ -317,12 +317,17 @@ protected:
     /**
     /**
      * @see Control::updateBounds
      * @see Control::updateBounds
      */
      */
-    bool updateBounds(const Vector2& offset);
+    void updateBounds();
+
+    /**
+     * @see Control::updateAbsoluteBounds
+     */
+    void updateAbsoluteBounds(const Vector2& offset);
 
 
     /**
     /**
      * Updates the bounds for this container's child controls.
      * Updates the bounds for this container's child controls.
      */
      */
-    void updateChildBounds();
+    bool updateChildBounds();
 
 
     /**
     /**
      * Sets the specified dirty bits for all children within this container.
      * Sets the specified dirty bits for all children within this container.
@@ -576,7 +581,6 @@ private:
     bool _contactIndices[MAX_CONTACT_INDICES];
     bool _contactIndices[MAX_CONTACT_INDICES];
     bool _initializedWithScroll;
     bool _initializedWithScroll;
     bool _scrollWheelRequiresFocus;
     bool _scrollWheelRequiresFocus;
-    bool _inRelayout;
 };
 };
 
 
 }
 }

+ 48 - 21
gameplay/src/Control.cpp

@@ -1145,7 +1145,7 @@ void Control::updateState(State state)
     _skin = getSkin(state);
     _skin = getSkin(state);
 }
 }
 
 
-bool Control::updateBounds(const Vector2& offset)
+bool Control::updateBoundsInternal(const Vector2& offset)
 {
 {
     // If our state is currently dirty, update it here so that any rendering state objects needed
     // If our state is currently dirty, update it here so that any rendering state objects needed
     // for bounds computation are accessible.
     // for bounds computation are accessible.
@@ -1156,13 +1156,45 @@ bool Control::updateBounds(const Vector2& offset)
         _dirtyBits &= ~DIRTY_STATE;
         _dirtyBits &= ~DIRTY_STATE;
     }
     }
 
 
-    // Clear dirty bounds bit
+    // Clear our dirty bounds bit
+    bool dirtyBounds = (_dirtyBits & DIRTY_BOUNDS) != 0;
     _dirtyBits &= ~DIRTY_BOUNDS;
     _dirtyBits &= ~DIRTY_BOUNDS;
 
 
+    // If we are a container, always update child bounds first
+    bool changed = false;
+    if (isContainer())
+        changed = static_cast<Container*>(this)->updateChildBounds();
+
+    if (dirtyBounds)
+    {
+        // Store old bounds so we can determine if they change
+        Rectangle oldAbsoluteBounds(_absoluteBounds);
+        Rectangle oldAbsoluteClipBounds(_absoluteClipBounds);
+        Rectangle oldViewportBounds(_viewportBounds);
+        Rectangle oldViewportClipBounds(_viewportClipBounds);
+
+        updateBounds();
+        updateAbsoluteBounds(offset);
+
+        if (_absoluteBounds != oldAbsoluteBounds ||
+            _absoluteClipBounds != oldAbsoluteClipBounds ||
+            _viewportBounds != oldViewportBounds ||
+            _viewportClipBounds != oldViewportClipBounds)
+        {
+            if (isContainer())
+                static_cast<Container*>(this)->setChildrenDirty(DIRTY_BOUNDS, true);
+            changed = true;
+        }
+    }
+
+    return changed;
+}
+
+void Control::updateBounds()
+{
     Game* game = Game::getInstance();
     Game* game = Game::getInstance();
 
 
     const Rectangle parentAbsoluteBounds = _parent ? _parent->_viewportBounds : Rectangle(0, 0, game->getViewport().width, game->getViewport().height);
     const Rectangle parentAbsoluteBounds = _parent ? _parent->_viewportBounds : Rectangle(0, 0, game->getViewport().width, game->getViewport().height);
-    const Rectangle parentAbsoluteClip = _parent ? _parent->_viewportClipBounds : parentAbsoluteBounds;
 
 
     // Calculate local unclipped bounds.
     // Calculate local unclipped bounds.
     _bounds.set(_relativeBounds);
     _bounds.set(_relativeBounds);
@@ -1225,9 +1257,17 @@ bool Control::updateBounds(const Vector2& offset)
             _bounds.x = margin.left;
             _bounds.x = margin.left;
         }
         }
     }
     }
+}
+
+void Control::updateAbsoluteBounds(const Vector2& offset)
+{
+    Game* game = Game::getInstance();
+
+    const Rectangle parentAbsoluteBounds = _parent ? _parent->_viewportBounds : Rectangle(0, 0, game->getViewport().width, game->getViewport().height);
+    const Rectangle parentAbsoluteClip = _parent ? _parent->_viewportClipBounds : parentAbsoluteBounds;
 
 
     // Compute content area padding values
     // Compute content area padding values
-    const Theme::Border& border = getBorder(state);
+    const Theme::Border& border = getBorder(NORMAL);
     const Theme::Padding& padding = getPadding();
     const Theme::Padding& padding = getPadding();
     float lpadding = border.left + padding.left;
     float lpadding = border.left + padding.left;
     float rpadding = border.right + padding.right;
     float rpadding = border.right + padding.right;
@@ -1236,22 +1276,15 @@ bool Control::updateBounds(const Vector2& offset)
     float hpadding = lpadding + rpadding;
     float hpadding = lpadding + rpadding;
     float vpadding = tpadding + bpadding;
     float vpadding = tpadding + bpadding;
 
 
-    Rectangle temp;
-    bool changed = false;
-
     // Calculate absolute unclipped bounds
     // Calculate absolute unclipped bounds
-    temp.set(
+    _absoluteBounds.set(
         parentAbsoluteBounds.x + offset.x + _bounds.x,
         parentAbsoluteBounds.x + offset.x + _bounds.x,
         parentAbsoluteBounds.y + offset.y + _bounds.y,
         parentAbsoluteBounds.y + offset.y + _bounds.y,
         _bounds.width,
         _bounds.width,
         _bounds.height);
         _bounds.height);
-    changed = temp != _absoluteBounds;
-    _absoluteBounds = temp;
 
 
     // Calculate absolute clipped bounds
     // Calculate absolute clipped bounds
-    Rectangle::intersect(_absoluteBounds, parentAbsoluteClip, &temp);
-    changed = changed || (temp != _absoluteClipBounds);
-    _absoluteClipBounds = temp;
+    Rectangle::intersect(_absoluteBounds, parentAbsoluteClip, &_absoluteClipBounds);
 
 
     // Calculate the local clipped bounds
     // Calculate the local clipped bounds
     _clipBounds.set(
     _clipBounds.set(
@@ -1262,20 +1295,14 @@ bool Control::updateBounds(const Vector2& offset)
         );
         );
 
 
     // Calculate the absolute unclipped viewport bounds (content area, which does not include border and padding)
     // Calculate the absolute unclipped viewport bounds (content area, which does not include border and padding)
-    temp.set(
+    _viewportBounds.set(
         _absoluteBounds.x + lpadding,
         _absoluteBounds.x + lpadding,
         _absoluteBounds.y + tpadding,
         _absoluteBounds.y + tpadding,
         _absoluteBounds.width - hpadding,
         _absoluteBounds.width - hpadding,
         _absoluteBounds.height - vpadding);
         _absoluteBounds.height - vpadding);
-    changed = changed || (temp != _viewportBounds);
-    _viewportBounds = temp;
 
 
     // Calculate the absolute clipped viewport bounds
     // Calculate the absolute clipped viewport bounds
-    Rectangle::intersect(_viewportBounds, parentAbsoluteClip, &temp);
-    changed = changed || (temp != _viewportClipBounds);
-    _viewportClipBounds = temp;
-
-    return true;
+    Rectangle::intersect(_viewportBounds, parentAbsoluteClip, &_viewportClipBounds);
 }
 }
 
 
 void Control::startBatch(Form* form, SpriteBatch* batch)
 void Control::startBatch(Form* form, SpriteBatch* batch)

+ 10 - 5
gameplay/src/Control.h

@@ -1127,15 +1127,18 @@ protected:
     virtual void updateState(State state);
     virtual void updateState(State state);
 
 
     /**
     /**
-     * Updates the bounds for this control and its children.
+     * Updates the local bounds for this control and its children.
      *
      *
      * Child controls that need to customize their bounds calculation should override this method.
      * Child controls that need to customize their bounds calculation should override this method.
+     */
+    virtual void updateBounds();
+
+    /**
+     * Updates the absolute bounds for this control and its children.
      *
      *
-     * @param offset Positioning offset to add to the control's position (most often used for scrolling).
-     *
-     * @return True if the bounds were updated, or false if there was no change.
+     * @param offset Offset to add to the control's position (most often used for scrolling).
      */
      */
-    virtual bool updateBounds(const Vector2& offset);
+    virtual void updateAbsoluteBounds(const Vector2& offset);
 
 
     /**
     /**
      * Indicates that a control will begin drawing into the specified batch.
      * Indicates that a control will begin drawing into the specified batch.
@@ -1405,6 +1408,8 @@ private:
      */    
      */    
     Control(const Control& copy);
     Control(const Control& copy);
 
 
+    bool updateBoundsInternal(const Vector2& offset);
+
     AutoSize Control::parseAutoSize(const char* str);
     AutoSize Control::parseAutoSize(const char* str);
 
 
     Theme::Style::Overlay** getOverlays(unsigned char overlayTypes, Theme::Style::Overlay** overlays);
     Theme::Style::Overlay** getOverlays(unsigned char overlayTypes, Theme::Style::Overlay** overlays);

+ 4 - 16
gameplay/src/Form.cpp

@@ -122,7 +122,8 @@ void Form::initialize(const char* typeName, Theme::Style* style, Properties* pro
 
 
     // After creation, update our bounds once so code that runs immediately after form
     // After creation, update our bounds once so code that runs immediately after form
     // creation has access to up-to-date bounds.
     // creation has access to up-to-date bounds.
-    updateBounds(Vector2::zero());
+    if (updateBoundsInternal(Vector2::zero()))
+        updateBoundsInternal(Vector2::zero());
 }
 }
 
 
 Form* Form::getForm(const char* id)
 Form* Form::getForm(const char* id)
@@ -187,24 +188,11 @@ void Form::update(float elapsedTime)
 {
 {
     Container::update(elapsedTime);
     Container::update(elapsedTime);
 
 
-    // Update our bounds if needed
-    updateBounds(Vector2::zero());
-}
-
-bool Form::updateBounds(const Vector2& offset)
-{
     // Do a two-pass bounds update:
     // Do a two-pass bounds update:
     //  1. First pass updates leaf controls
     //  1. First pass updates leaf controls
     //  2. Second pass updates parent controls that depend on child sizes
     //  2. Second pass updates parent controls that depend on child sizes
-    // Note: We could have updateBounds return a boolean indicating whether anything
-    // was dirtied as a result of the update (so we can optionally skip the second pass).
-    if (Container::updateBounds(offset))
-    {
-        Container::updateBounds(offset);
-        return true;
-    }
-
-    return false;
+    if (updateBoundsInternal(Vector2::zero()))
+        updateBoundsInternal(Vector2::zero());
 }
 }
 
 
 void Form::startBatch(SpriteBatch* batch)
 void Form::startBatch(SpriteBatch* batch)

+ 0 - 5
gameplay/src/Form.h

@@ -106,11 +106,6 @@ public:
      */
      */
     void update(float elapsedTime);
     void update(float elapsedTime);
 
 
-    /**
-     * @see Control::updtaeBounds
-     */
-    bool updateBounds(const Vector2& offset);
-
     /**
     /**
      * Draws this form.
      * Draws this form.
      *
      *

+ 2 - 8
gameplay/src/ImageControl.cpp

@@ -138,12 +138,8 @@ unsigned int ImageControl::drawImages(Form* form, const Rectangle& clip)
     return 1;
     return 1;
 }
 }
 
 
-bool ImageControl::updateBounds(const Vector2& offset)
+void ImageControl::updateBounds()
 {
 {
-    bool changed = Control::updateBounds(offset);
-
-    Rectangle oldBounds(_bounds);
-
     if (_batch)
     if (_batch)
     {
     {
         if (_autoSize & AUTO_SIZE_WIDTH)
         if (_autoSize & AUTO_SIZE_WIDTH)
@@ -157,9 +153,7 @@ bool ImageControl::updateBounds(const Vector2& offset)
         }
         }
     }
     }
 
 
-    changed = changed || (_bounds != oldBounds);
-
-    return changed;
+    Control::updateBounds();
 }
 }
 
 
 }
 }

+ 1 - 1
gameplay/src/ImageControl.h

@@ -124,7 +124,7 @@ protected:
     /**
     /**
      * @see Control::updateBounds
      * @see Control::updateBounds
      */
      */
-    bool updateBounds(const Vector2& offset);
+    void updateBounds();
 
 
 private:
 private:
 
 

+ 13 - 16
gameplay/src/Label.cpp

@@ -86,36 +86,33 @@ void Label::updateState(State state)
     _font = getFont(state);
     _font = getFont(state);
 }
 }
 
 
-bool Label::updateBounds(const Vector2& offset)
+void Label::updateBounds()
 {
 {
-    bool changed = Control::updateBounds(offset);
-
-    // Measure bounds based only on normal state so that bounds updates are not always required on state changes.
-    // This is a trade-off for functionality vs performance, but changing the size of UI controls on hover/focus/etc
-    // is a pretty bad practice so we'll prioritize performance here.
-    Control::State state = NORMAL;
-
-    _textBounds.set((int)_viewportBounds.x, (int)_viewportBounds.y, _viewportBounds.width, _viewportBounds.height);
-
-    Rectangle oldBounds(_bounds);
+    Control::updateBounds();
 
 
     if (_autoSize != AUTO_SIZE_NONE && _font)
     if (_autoSize != AUTO_SIZE_NONE && _font)
     {
     {
+        // Measure bounds based only on normal state so that bounds updates are not always required on state changes.
+        // This is a trade-off for functionality vs performance, but changing the size of UI controls on hover/focus/etc
+        // is a pretty bad practice so we'll prioritize performance here.
         unsigned int w, h;
         unsigned int w, h;
-        _font->measureText(_text.c_str(), getFontSize(state), &w, &h);
+        _font->measureText(_text.c_str(), getFontSize(NORMAL), &w, &h);
         if (_autoSize & AUTO_SIZE_WIDTH)
         if (_autoSize & AUTO_SIZE_WIDTH)
         {
         {
-            setWidthInternal(w + getBorder(state).left + getBorder(state).right + getPadding().left + getPadding().right);
+            setWidthInternal(w + getBorder(NORMAL).left + getBorder(NORMAL).right + getPadding().left + getPadding().right);
         }
         }
         if (_autoSize & AUTO_SIZE_HEIGHT)
         if (_autoSize & AUTO_SIZE_HEIGHT)
         {
         {
-            setHeightInternal(h + getBorder(state).top + getBorder(state).bottom + getPadding().top + getPadding().bottom);
+            setHeightInternal(h + getBorder(NORMAL).top + getBorder(NORMAL).bottom + getPadding().top + getPadding().bottom);
         }
         }
     }
     }
+}
 
 
-    changed = changed || (_bounds != oldBounds);
+void Label::updateAbsoluteBounds(const Vector2& offset)
+{
+    Control::updateAbsoluteBounds(offset);
 
 
-    return changed;
+    _textBounds.set((int)_viewportBounds.x, (int)_viewportBounds.y, _viewportBounds.width, _viewportBounds.height);
 }
 }
 
 
 unsigned int Label::drawText(Form* form, const Rectangle& clip)
 unsigned int Label::drawText(Form* form, const Rectangle& clip)

+ 6 - 1
gameplay/src/Label.h

@@ -104,7 +104,12 @@ protected:
     /**
     /**
      * @see Control::updateBounds
      * @see Control::updateBounds
      */
      */
-    bool updateBounds(const Vector2& offset);
+    void updateBounds();
+
+    /**
+     * @see Control::updateAbsoluteBounds
+     */
+    void updateAbsoluteBounds(const Vector2& offset);
 
 
     /**
     /**
      * @see Control::drawText
      * @see Control::drawText

+ 11 - 15
gameplay/src/RadioButton.cpp

@@ -138,31 +138,26 @@ void RadioButton::updateState(State state)
     _image = getImage(_selected ? "selected" : "unselected", state);
     _image = getImage(_selected ? "selected" : "unselected", state);
 }
 }
 
 
-bool RadioButton::updateBounds(const Vector2& offset)
+void RadioButton::updateBounds()
 {
 {
-    bool changed = Label::updateBounds(offset);
-    
-    // Compute bounds based on normal state only
-    Control::State state = NORMAL;
+    Label::updateBounds();
 
 
     Vector2 size;
     Vector2 size;
     if (_selected)
     if (_selected)
     {
     {
-        const Rectangle& selectedRegion = getImageRegion("selected", state);
+        const Rectangle& selectedRegion = getImageRegion("selected", NORMAL);
         size.set(selectedRegion.width, selectedRegion.height);
         size.set(selectedRegion.width, selectedRegion.height);
     }
     }
     else
     else
     {
     {
-        const Rectangle& unselectedRegion = getImageRegion("unselected", state);
+        const Rectangle& unselectedRegion = getImageRegion("unselected", NORMAL);
         size.set(unselectedRegion.width, unselectedRegion.height);
         size.set(unselectedRegion.width, unselectedRegion.height);
     }
     }
 
 
-    Rectangle oldBounds(_bounds);
-
     if (_autoSize & AUTO_SIZE_HEIGHT)
     if (_autoSize & AUTO_SIZE_HEIGHT)
     {
     {
         // Text-only width was already measured in Label::update - append image
         // Text-only width was already measured in Label::update - append image
-        const Theme::Border& border = getBorder(state);
+        const Theme::Border& border = getBorder(NORMAL);
         const Theme::Border& padding = getPadding();
         const Theme::Border& padding = getPadding();
         setHeightInternal(std::max(_bounds.height, size.y + border.top + border.bottom + padding.top + padding.bottom));
         setHeightInternal(std::max(_bounds.height, size.y + border.top + border.bottom + padding.top + padding.bottom));
     }
     }
@@ -170,14 +165,15 @@ bool RadioButton::updateBounds(const Vector2& offset)
     if (_autoSize & AUTO_SIZE_WIDTH)
     if (_autoSize & AUTO_SIZE_WIDTH)
     {
     {
         // Text-only width was already measured in Label::update - append image
         // Text-only width was already measured in Label::update - append image
-        setWidthInternal(_viewportBounds.height + 5 + _bounds.width);
+        setWidthInternal(_bounds.height + 5 + _bounds.width);
     }
     }
+}
 
 
-    changed = changed || (_bounds != oldBounds);
-
-    _textBounds.x += _viewportBounds.height + 5;
+void RadioButton::updateAbsoluteBounds(const Vector2& offset)
+{
+    Label::updateAbsoluteBounds(offset);
 
 
-    return changed;
+    _textBounds.x += _bounds.height + 5;
 }
 }
 
 
 unsigned int RadioButton::drawImages(Form* form, const Rectangle& clip)
 unsigned int RadioButton::drawImages(Form* form, const Rectangle& clip)

+ 6 - 1
gameplay/src/RadioButton.h

@@ -125,7 +125,12 @@ protected:
     /**
     /**
      * @see Control::updateBounds
      * @see Control::updateBounds
      */
      */
-    bool updateBounds(const Vector2& offset);
+    void updateBounds();
+
+    /**
+     * @see Control::updateAbsoluteBounds
+     */
+    void updateAbsoluteBounds(const Vector2& offset);
 
 
     /**
     /**
      * @see Control::drawImages
      * @see Control::drawImages

+ 3 - 11
gameplay/src/Slider.cpp

@@ -340,11 +340,9 @@ void Slider::updateState(State state)
     _trackImage = getImage("track", state);
     _trackImage = getImage("track", state);
 }
 }
 
 
-bool Slider::updateBounds(const Vector2& offset)
+void Slider::updateBounds()
 {
 {
-    bool changed = Label::updateBounds(offset);
-
-    Control::State state = getState();
+    Label::updateBounds();
 
 
     // Compute height of track (max of track, min/max and marker
     // Compute height of track (max of track, min/max and marker
     _trackHeight = _minImage->getRegion().height;
     _trackHeight = _minImage->getRegion().height;
@@ -352,19 +350,13 @@ bool Slider::updateBounds(const Vector2& offset)
     _trackHeight = std::max(_trackHeight, _markerImage->getRegion().height);
     _trackHeight = std::max(_trackHeight, _markerImage->getRegion().height);
     _trackHeight = std::max(_trackHeight, _trackImage->getRegion().height);
     _trackHeight = std::max(_trackHeight, _trackImage->getRegion().height);
 
 
-    Rectangle oldBounds(_bounds);
-
     if (_autoSize & AUTO_SIZE_HEIGHT)
     if (_autoSize & AUTO_SIZE_HEIGHT)
     {
     {
         float height = _bounds.height + _trackHeight;
         float height = _bounds.height + _trackHeight;
         if (_valueTextVisible)
         if (_valueTextVisible)
-            height += getFontSize(state);
+            height += getFontSize(NORMAL);
         setHeightInternal(height);
         setHeightInternal(height);
     }
     }
-
-    changed = changed || (_bounds != oldBounds);
-
-    return changed;
 }
 }
 
 
 unsigned int Slider::drawImages(Form* form, const Rectangle& clip)
 unsigned int Slider::drawImages(Form* form, const Rectangle& clip)

+ 1 - 1
gameplay/src/Slider.h

@@ -245,7 +245,7 @@ protected:
     /**
     /**
      * @see Control::updateBounds
      * @see Control::updateBounds
      */
      */
-    bool updateBounds(const Vector2& offset);
+    void updateBounds();
 
 
     /**
     /**
      * The minimum value for the Slider.
      * The minimum value for the Slider.