|
|
@@ -46,7 +46,7 @@ static bool parseCoordPair(const char* s, float* v1, float* v2, bool* v1Percenta
|
|
|
}
|
|
|
|
|
|
Control::Control()
|
|
|
- : _id(""), _enabled(true), _boundsBits(0), _dirtyBits(DIRTY_BOUNDS), _consumeInputEvents(true), _alignment(ALIGN_TOP_LEFT),
|
|
|
+ : _id(""), _enabled(true), _boundsBits(0), _dirtyBits(DIRTY_BOUNDS | DIRTY_STATE), _consumeInputEvents(true), _alignment(ALIGN_TOP_LEFT),
|
|
|
_autoSize(AUTO_SIZE_BOTH), _style(NULL), _listeners(NULL), _visible(true), _zIndex(-1),
|
|
|
_contactIndex(INVALID_CONTACT_INDEX), _focusIndex(-1), _canFocus(false), _parent(NULL), _styleOverridden(false), _skin(NULL)
|
|
|
{
|
|
|
@@ -500,10 +500,10 @@ void Control::setVisible(bool visible)
|
|
|
{
|
|
|
_visible = visible;
|
|
|
|
|
|
- if (_visible)
|
|
|
- setDirty(DIRTY_BOUNDS);
|
|
|
- else
|
|
|
+ if (!_visible)
|
|
|
Form::controlDisabled(this);
|
|
|
+
|
|
|
+ setDirty(DIRTY_BOUNDS);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -560,10 +560,6 @@ void Control::setOpacity(float opacity, unsigned char states)
|
|
|
if (overlays[i])
|
|
|
overlays[i]->setOpacity(opacity);
|
|
|
}
|
|
|
-
|
|
|
- // Opacity is currently pre-multiplied in updateBounds, so we need to
|
|
|
- // dirty our bounds when it changes.
|
|
|
- setDirty(DIRTY_BOUNDS);
|
|
|
}
|
|
|
|
|
|
float Control::getOpacity(State state) const
|
|
|
@@ -582,9 +578,7 @@ void Control::setEnabled(bool enabled)
|
|
|
if (!_enabled)
|
|
|
Form::controlDisabled(this);
|
|
|
|
|
|
- // The enabled flag modifies control state which can be themed, so we need to assume
|
|
|
- // that the bounds may change as a result of this.
|
|
|
- setDirty(DIRTY_BOUNDS);
|
|
|
+ setDirty(DIRTY_STATE);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -1120,18 +1114,6 @@ void Control::controlEvent(Control::Listener::EventType evt)
|
|
|
void Control::setDirty(int bits)
|
|
|
{
|
|
|
_dirtyBits |= bits;
|
|
|
-
|
|
|
- if (bits & DIRTY_BOUNDS)
|
|
|
- {
|
|
|
- // When a container's bounds are dirty, all children controls also need
|
|
|
- // to be dirtied so that their absolute bounds are updated correctly.
|
|
|
- if (isContainer())
|
|
|
- {
|
|
|
- Container* container = static_cast<Container*>(this);
|
|
|
- for (unsigned int i = 0, count = container->getControlCount(); i < count; ++i)
|
|
|
- container->getControl(i)->setDirty(DIRTY_BOUNDS);
|
|
|
- }
|
|
|
- }
|
|
|
}
|
|
|
|
|
|
bool Control::isDirty(int bit) const
|
|
|
@@ -1141,11 +1123,40 @@ bool Control::isDirty(int bit) const
|
|
|
|
|
|
void Control::update(float elapsedTime)
|
|
|
{
|
|
|
+ State state = getState();
|
|
|
+
|
|
|
+ // Update visual state if it's dirty
|
|
|
+ if (_dirtyBits & DIRTY_STATE)
|
|
|
+ updateState(getState());
|
|
|
+
|
|
|
+ // Since opacity is pre-multiplied, we compute it every frame so that we don't need to
|
|
|
+ // dirty the entire hierarchy any time a state changes (which could affect opacity).
|
|
|
+ _opacity = getOpacity(state);
|
|
|
+ if (_parent)
|
|
|
+ _opacity *= _parent->_opacity;
|
|
|
}
|
|
|
|
|
|
-void Control::updateBounds(const Vector2& offset)
|
|
|
+void Control::updateState(State state)
|
|
|
{
|
|
|
- // Clear our dirty bounds bit
|
|
|
+ // Clear dirty state bit
|
|
|
+ _dirtyBits &= ~DIRTY_STATE;
|
|
|
+
|
|
|
+ // Cache themed attributes for performance.
|
|
|
+ _skin = getSkin(state);
|
|
|
+}
|
|
|
+
|
|
|
+bool Control::updateBounds(const Vector2& offset)
|
|
|
+{
|
|
|
+ // If our state is currently dirty, update it here so that any rendering state objects needed
|
|
|
+ // for bounds computation are accessible.
|
|
|
+ State state = getState();
|
|
|
+ if (_dirtyBits & DIRTY_STATE)
|
|
|
+ {
|
|
|
+ updateState(state);
|
|
|
+ _dirtyBits &= ~DIRTY_STATE;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Clear dirty bounds bit
|
|
|
_dirtyBits &= ~DIRTY_BOUNDS;
|
|
|
|
|
|
Game* game = Game::getInstance();
|
|
|
@@ -1153,17 +1164,18 @@ void Control::updateBounds(const Vector2& offset)
|
|
|
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
|
|
|
- const Theme::Border& border = getBorder(getState());
|
|
|
- const Theme::Padding& padding = getPadding();
|
|
|
- float lpadding = border.left + padding.left;
|
|
|
- float rpadding = border.right + padding.right;
|
|
|
- float tpadding = border.top + padding.top;
|
|
|
- float bpadding = border.bottom + padding.bottom;
|
|
|
- float hpadding = lpadding + rpadding;
|
|
|
- float vpadding = tpadding + bpadding;
|
|
|
+ // Calculate local unclipped bounds.
|
|
|
+ _bounds.set(_relativeBounds);
|
|
|
+ if (isXPercentage())
|
|
|
+ _bounds.x *= parentAbsoluteBounds.width;
|
|
|
+ if (isYPercentage())
|
|
|
+ _bounds.y *= parentAbsoluteBounds.height;
|
|
|
+ if (isWidthPercentage())
|
|
|
+ _bounds.width *= parentAbsoluteBounds.width;
|
|
|
+ if (isHeightPercentage())
|
|
|
+ _bounds.height *= parentAbsoluteBounds.height;
|
|
|
|
|
|
- // Apply control alignment first
|
|
|
+ // Apply control alignment
|
|
|
if (_alignment != Control::ALIGN_TOP_LEFT)
|
|
|
{
|
|
|
const Theme::Margin& margin = _style->getMargin();
|
|
|
@@ -1188,52 +1200,58 @@ void Control::updateBounds(const Vector2& offset)
|
|
|
// Vertical alignment
|
|
|
if ((_alignment & Control::ALIGN_BOTTOM) == Control::ALIGN_BOTTOM)
|
|
|
{
|
|
|
- setYInternal(clipHeight - _bounds.height - margin.bottom);
|
|
|
+ _bounds.y = clipHeight - _bounds.height - margin.bottom;
|
|
|
}
|
|
|
else if ((_alignment & Control::ALIGN_VCENTER) == Control::ALIGN_VCENTER)
|
|
|
{
|
|
|
- setYInternal(clipHeight * 0.5f - _bounds.height * 0.5f);
|
|
|
+ _bounds.y = clipHeight * 0.5f - _bounds.height * 0.5f;
|
|
|
}
|
|
|
else if ((_alignment & Control::ALIGN_TOP) == Control::ALIGN_TOP)
|
|
|
{
|
|
|
- setYInternal(margin.top);
|
|
|
+ _bounds.y = margin.top;
|
|
|
}
|
|
|
|
|
|
// Horizontal alignment
|
|
|
if ((_alignment & Control::ALIGN_RIGHT) == Control::ALIGN_RIGHT)
|
|
|
{
|
|
|
- setXInternal(clipWidth - _bounds.width - margin.right);
|
|
|
+ _bounds.x = clipWidth - _bounds.width - margin.right;
|
|
|
}
|
|
|
else if ((_alignment & Control::ALIGN_HCENTER) == Control::ALIGN_HCENTER)
|
|
|
{
|
|
|
- setXInternal(clipWidth * 0.5f - _bounds.width * 0.5f);
|
|
|
+ _bounds.x = clipWidth * 0.5f - _bounds.width * 0.5f;
|
|
|
}
|
|
|
else if ((_alignment & Control::ALIGN_LEFT) == Control::ALIGN_LEFT)
|
|
|
{
|
|
|
- setXInternal(margin.left);
|
|
|
+ _bounds.x = margin.left;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // Calculate local unclipped bounds.
|
|
|
- _bounds.set(_relativeBounds);
|
|
|
- if (isXPercentage())
|
|
|
- _bounds.x *= parentAbsoluteBounds.width;
|
|
|
- if (isYPercentage())
|
|
|
- _bounds.y *= parentAbsoluteBounds.height;
|
|
|
- if (isWidthPercentage())
|
|
|
- _bounds.width *= parentAbsoluteBounds.width;
|
|
|
- if (isHeightPercentage())
|
|
|
- _bounds.height *= parentAbsoluteBounds.height;
|
|
|
+ // Compute content area padding values
|
|
|
+ const Theme::Border& border = getBorder(state);
|
|
|
+ const Theme::Padding& padding = getPadding();
|
|
|
+ float lpadding = border.left + padding.left;
|
|
|
+ float rpadding = border.right + padding.right;
|
|
|
+ float tpadding = border.top + padding.top;
|
|
|
+ float bpadding = border.bottom + padding.bottom;
|
|
|
+ float hpadding = lpadding + rpadding;
|
|
|
+ float vpadding = tpadding + bpadding;
|
|
|
+
|
|
|
+ Rectangle temp;
|
|
|
+ bool changed = false;
|
|
|
|
|
|
// Calculate absolute unclipped bounds
|
|
|
- _absoluteBounds.set(
|
|
|
+ temp.set(
|
|
|
parentAbsoluteBounds.x + offset.x + _bounds.x,
|
|
|
parentAbsoluteBounds.y + offset.y + _bounds.y,
|
|
|
_bounds.width,
|
|
|
_bounds.height);
|
|
|
+ changed = temp != _absoluteBounds;
|
|
|
+ _absoluteBounds = temp;
|
|
|
|
|
|
// Calculate absolute clipped bounds
|
|
|
- Rectangle::intersect(_absoluteBounds, parentAbsoluteClip, &_absoluteClipBounds);
|
|
|
+ Rectangle::intersect(_absoluteBounds, parentAbsoluteClip, &temp);
|
|
|
+ changed = changed || (temp != _absoluteClipBounds);
|
|
|
+ _absoluteClipBounds = temp;
|
|
|
|
|
|
// Calculate the local clipped bounds
|
|
|
_clipBounds.set(
|
|
|
@@ -1244,22 +1262,20 @@ void Control::updateBounds(const Vector2& offset)
|
|
|
);
|
|
|
|
|
|
// Calculate the absolute unclipped viewport bounds (content area, which does not include border and padding)
|
|
|
- _viewportBounds.set(
|
|
|
+ temp.set(
|
|
|
_absoluteBounds.x + lpadding,
|
|
|
_absoluteBounds.y + tpadding,
|
|
|
_absoluteBounds.width - hpadding,
|
|
|
_absoluteBounds.height - vpadding);
|
|
|
+ changed = changed || (temp != _viewportBounds);
|
|
|
+ _viewportBounds = temp;
|
|
|
|
|
|
// Calculate the absolute clipped viewport bounds
|
|
|
- Rectangle::intersect(_viewportBounds, parentAbsoluteClip, &_viewportClipBounds);
|
|
|
-
|
|
|
- // Cache themed attributes for performance.
|
|
|
- _skin = getSkin(getState());
|
|
|
+ Rectangle::intersect(_viewportBounds, parentAbsoluteClip, &temp);
|
|
|
+ changed = changed || (temp != _viewportClipBounds);
|
|
|
+ _viewportClipBounds = temp;
|
|
|
|
|
|
- // Current opacity should be multiplied by that of the parent container.
|
|
|
- _opacity = getOpacity(getState());
|
|
|
- if (_parent)
|
|
|
- _opacity *= _parent->_opacity;
|
|
|
+ return true;
|
|
|
}
|
|
|
|
|
|
void Control::startBatch(Form* form, SpriteBatch* batch)
|