|
|
@@ -46,8 +46,8 @@ 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), _style(NULL), _listeners(NULL), _visible(true), _zIndex(-1),
|
|
|
+ : _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)
|
|
|
{
|
|
|
addScriptEvent("controlEvent", "<Control>[Control::Listener::EventType]");
|
|
|
@@ -79,15 +79,19 @@ Control::~Control()
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-static Control::AutoSize parseAutoSize(const char* str)
|
|
|
+Control::AutoSize Control::parseAutoSize(const char* str)
|
|
|
{
|
|
|
if (str == NULL)
|
|
|
- return Control::AUTO_SIZE_NONE;
|
|
|
- if (strcmp(str, "AUTO_SIZE_STRETCH") == 0 || strcmp(str, "true") == 0) // left for backwards compatibility
|
|
|
- return Control::AUTO_SIZE_STRETCH;
|
|
|
- if (strcmp(str, "AUTO_SIZE_FIT") == 0)
|
|
|
- return Control::AUTO_SIZE_FIT;
|
|
|
- return Control::AUTO_SIZE_NONE;
|
|
|
+ return _autoSize;
|
|
|
+
|
|
|
+ if (strcmpnocase(str, "AUTO_SIZE_WIDTH") == 0 )
|
|
|
+ return AUTO_SIZE_WIDTH;
|
|
|
+ if (strcmpnocase(str, "AUTO_SIZE_HEIGHT") == 0)
|
|
|
+ return AUTO_SIZE_HEIGHT;
|
|
|
+ if (strcmpnocase(str, "AUTO_SIZE_BOTH") == 0)
|
|
|
+ return AUTO_SIZE_BOTH;
|
|
|
+
|
|
|
+ return _autoSize;
|
|
|
}
|
|
|
|
|
|
void Control::initialize(const char* typeName, Theme::Style* style, Properties* properties)
|
|
|
@@ -106,8 +110,8 @@ void Control::initialize(const char* typeName, Theme::Style* style, Properties*
|
|
|
|
|
|
if (!_style)
|
|
|
{
|
|
|
- // Try loading a style from the default theme
|
|
|
- _style = Theme::getDefault()->getStyle(styleName);
|
|
|
+ // Use an empty style from our parent's theme
|
|
|
+ _style = style->getTheme()->getEmptyStyle();
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
@@ -133,15 +137,15 @@ void Control::initialize(const char* typeName, Theme::Style* style, Properties*
|
|
|
|
|
|
if (properties)
|
|
|
{
|
|
|
+ const char* id = properties->getId();
|
|
|
+ if (id)
|
|
|
+ _id = id;
|
|
|
+
|
|
|
// Properties not defined by the style.
|
|
|
- const char * alignmentString = properties->getString("alignment");
|
|
|
+ const char* alignmentString = properties->getString("alignment");
|
|
|
|
|
|
- _isAlignmentSet = alignmentString != NULL;
|
|
|
_alignment = getAlignment(alignmentString);
|
|
|
|
|
|
- _autoWidth = parseAutoSize(properties->getString("autoWidth"));
|
|
|
- _autoHeight = parseAutoSize(properties->getString("autoHeight"));
|
|
|
-
|
|
|
_consumeInputEvents = properties->getBool("consumeInputEvents", true);
|
|
|
|
|
|
_visible = properties->getBool("visible", true);
|
|
|
@@ -167,35 +171,60 @@ void Control::initialize(const char* typeName, Theme::Style* style, Properties*
|
|
|
_focusIndex = -1;
|
|
|
}
|
|
|
|
|
|
- float bounds[4];
|
|
|
- bool boundsBits[4];
|
|
|
- if (properties->exists("position"))
|
|
|
- {
|
|
|
- parseCoordPair(properties->getString("position", "0,0"), &bounds[0], &bounds[1], &boundsBits[0], &boundsBits[1]);
|
|
|
+ float bounds[2];
|
|
|
+ bool boundsBits[2];
|
|
|
+ const char* position = properties->getString("position");
|
|
|
+ if (position && parseCoordPair(position, &bounds[0], &bounds[1], &boundsBits[0], &boundsBits[1]))
|
|
|
+ {
|
|
|
+ setX(bounds[0], boundsBits[0]);
|
|
|
+ setY(bounds[1], boundsBits[1]);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- bounds[0] = parseCoord(properties->getString("x", "0"), &boundsBits[0]);
|
|
|
- bounds[1] = parseCoord(properties->getString("y", "0"), &boundsBits[1]);
|
|
|
+ if (properties->exists("x"))
|
|
|
+ {
|
|
|
+ bounds[0] = parseCoord(properties->getString("x", "0"), &boundsBits[0]);
|
|
|
+ setX(bounds[0], boundsBits[0]);
|
|
|
+ }
|
|
|
+ if (properties->exists("y"))
|
|
|
+ {
|
|
|
+ bounds[1] = parseCoord(properties->getString("y", "0"), &boundsBits[1]);
|
|
|
+ setY(bounds[1], boundsBits[1]);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- if (properties->exists("size"))
|
|
|
- {
|
|
|
- parseCoordPair(properties->getString("size", "0,0"), &bounds[2], &bounds[3], &boundsBits[2], &boundsBits[3]);
|
|
|
- }
|
|
|
+ // If there is an explicitly specified size, width or height, unset the corresponding autoSize bit
|
|
|
+ const char* size = properties->getString("size");
|
|
|
+ if (size && parseCoordPair(size, &bounds[0], &bounds[1], &boundsBits[0], &boundsBits[1]))
|
|
|
+ {
|
|
|
+ setWidth(bounds[0], boundsBits[0]);
|
|
|
+ setHeight(bounds[1], boundsBits[1]);
|
|
|
+ }
|
|
|
else
|
|
|
{
|
|
|
- bounds[2] = parseCoord(properties->getString("width", "0"), &boundsBits[2]);
|
|
|
- bounds[3] = parseCoord(properties->getString("height", "0"), &boundsBits[3]);
|
|
|
+ const char* width = properties->getString("width");
|
|
|
+ if (width)
|
|
|
+ {
|
|
|
+ bounds[0] = parseCoord(width, &boundsBits[0]);
|
|
|
+ setWidth(bounds[0], boundsBits[0]);
|
|
|
+ }
|
|
|
+ const char* height = properties->getString("height");
|
|
|
+ if (height)
|
|
|
+ {
|
|
|
+ bounds[1] = parseCoord(height, &boundsBits[1]);
|
|
|
+ setHeight(bounds[1], boundsBits[1]);
|
|
|
+ }
|
|
|
}
|
|
|
- setX(bounds[0], boundsBits[0]);
|
|
|
- setY(bounds[1], boundsBits[1]);
|
|
|
- setWidth(bounds[2], boundsBits[2]);
|
|
|
- setHeight(bounds[3], boundsBits[3]);
|
|
|
|
|
|
- const char* id = properties->getId();
|
|
|
- if (id)
|
|
|
- _id = id;
|
|
|
+ // Backwards Compatibility: Support deprecated autoWidth and autoHeight properties,
|
|
|
+ // which resolve to width=100% and height=100%.
|
|
|
+ if (properties->getBool("autoWidth"))
|
|
|
+ setWidth(1.0f, true);
|
|
|
+ if (properties->getBool("autoHeight"))
|
|
|
+ setHeight(1.0f, true);
|
|
|
+
|
|
|
+ // Parse the auto-size mode for the control (this overrides explicit sizes and legacy autoWidth/autoHeight)
|
|
|
+ _autoSize = parseAutoSize(properties->getString("autoSize"));
|
|
|
|
|
|
if (properties->exists("enabled"))
|
|
|
{
|
|
|
@@ -270,17 +299,22 @@ void Control::setX(float x, bool percentage)
|
|
|
{
|
|
|
if (_relativeBounds.x != x || percentage != ((_boundsBits & BOUNDS_X_PERCENTAGE_BIT) != 0))
|
|
|
{
|
|
|
- _relativeBounds.x = x;
|
|
|
- if (percentage)
|
|
|
- {
|
|
|
- _boundsBits |= BOUNDS_X_PERCENTAGE_BIT;
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- _boundsBits &= ~BOUNDS_X_PERCENTAGE_BIT;
|
|
|
- _bounds.x = x;
|
|
|
- }
|
|
|
- _dirty = true;
|
|
|
+ setXInternal(x, percentage);
|
|
|
+ setDirty(DIRTY_BOUNDS);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void Control::setXInternal(float x, bool percentage)
|
|
|
+{
|
|
|
+ _relativeBounds.x = x;
|
|
|
+ if (percentage)
|
|
|
+ {
|
|
|
+ _boundsBits |= BOUNDS_X_PERCENTAGE_BIT;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ _boundsBits &= ~BOUNDS_X_PERCENTAGE_BIT;
|
|
|
+ _bounds.x = x;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -298,17 +332,22 @@ void Control::setY(float y, bool percentage)
|
|
|
{
|
|
|
if (_relativeBounds.y != y || percentage != ((_boundsBits & BOUNDS_Y_PERCENTAGE_BIT) != 0))
|
|
|
{
|
|
|
- _relativeBounds.y = y;
|
|
|
- if (percentage)
|
|
|
- {
|
|
|
- _boundsBits |= BOUNDS_Y_PERCENTAGE_BIT;
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- _boundsBits &= ~BOUNDS_Y_PERCENTAGE_BIT;
|
|
|
- _bounds.y = y;
|
|
|
- }
|
|
|
- _dirty = true;
|
|
|
+ setYInternal(y, percentage);
|
|
|
+ setDirty(DIRTY_BOUNDS);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void Control::setYInternal(float y, bool percentage)
|
|
|
+{
|
|
|
+ _relativeBounds.y = y;
|
|
|
+ if (percentage)
|
|
|
+ {
|
|
|
+ _boundsBits |= BOUNDS_Y_PERCENTAGE_BIT;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ _boundsBits &= ~BOUNDS_Y_PERCENTAGE_BIT;
|
|
|
+ _bounds.y = y;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -324,19 +363,26 @@ float Control::getWidth() const
|
|
|
|
|
|
void Control::setWidth(float width, bool percentage)
|
|
|
{
|
|
|
+ _autoSize = (AutoSize)(_autoSize & ~AUTO_SIZE_WIDTH);
|
|
|
+
|
|
|
if (_relativeBounds.width != width || percentage != ((_boundsBits & BOUNDS_WIDTH_PERCENTAGE_BIT) != 0))
|
|
|
{
|
|
|
- _relativeBounds.width = width;
|
|
|
- if (percentage)
|
|
|
- {
|
|
|
- _boundsBits |= BOUNDS_WIDTH_PERCENTAGE_BIT;
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- _boundsBits &= ~BOUNDS_WIDTH_PERCENTAGE_BIT;
|
|
|
- _bounds.width = width;
|
|
|
- }
|
|
|
- _dirty = true;
|
|
|
+ setWidthInternal(width, percentage);
|
|
|
+ setDirty(DIRTY_BOUNDS);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void Control::setWidthInternal(float width, bool percentage)
|
|
|
+{
|
|
|
+ _relativeBounds.width = width;
|
|
|
+ if (percentage)
|
|
|
+ {
|
|
|
+ _boundsBits |= BOUNDS_WIDTH_PERCENTAGE_BIT;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ _boundsBits &= ~BOUNDS_WIDTH_PERCENTAGE_BIT;
|
|
|
+ _bounds.width = width;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -352,19 +398,26 @@ float Control::getHeight() const
|
|
|
|
|
|
void Control::setHeight(float height, bool percentage)
|
|
|
{
|
|
|
+ _autoSize = (AutoSize)(_autoSize & ~AUTO_SIZE_HEIGHT);
|
|
|
+
|
|
|
if (_relativeBounds.height != height || percentage != ((_boundsBits & BOUNDS_HEIGHT_PERCENTAGE_BIT) != 0))
|
|
|
{
|
|
|
- _relativeBounds.height = height;
|
|
|
- if (percentage)
|
|
|
- {
|
|
|
- _boundsBits |= BOUNDS_HEIGHT_PERCENTAGE_BIT;
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- _boundsBits &= ~BOUNDS_HEIGHT_PERCENTAGE_BIT;
|
|
|
- _bounds.height = height;
|
|
|
- }
|
|
|
- _dirty = true;
|
|
|
+ setHeightInternal(height, percentage);
|
|
|
+ setDirty(DIRTY_BOUNDS);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void Control::setHeightInternal(float height, bool percentage)
|
|
|
+{
|
|
|
+ _relativeBounds.height = height;
|
|
|
+ if (percentage)
|
|
|
+ {
|
|
|
+ _boundsBits |= BOUNDS_HEIGHT_PERCENTAGE_BIT;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ _boundsBits &= ~BOUNDS_HEIGHT_PERCENTAGE_BIT;
|
|
|
+ _bounds.height = height;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -415,51 +468,29 @@ const Rectangle& Control::getClip() const
|
|
|
|
|
|
void Control::setAlignment(Alignment alignment)
|
|
|
{
|
|
|
- _alignment = alignment;
|
|
|
- _isAlignmentSet = true;
|
|
|
- _dirty = true;
|
|
|
-}
|
|
|
-
|
|
|
-Control::Alignment Control::getAlignment() const
|
|
|
-{
|
|
|
- return _alignment;
|
|
|
-}
|
|
|
-
|
|
|
-Control::AutoSize Control::getAutoWidth() const
|
|
|
-{
|
|
|
- return _autoWidth;
|
|
|
-}
|
|
|
-
|
|
|
-void Control::setAutoWidth(bool autoWidth)
|
|
|
-{
|
|
|
- setAutoWidth(autoWidth ? AUTO_SIZE_STRETCH : AUTO_SIZE_NONE);
|
|
|
-}
|
|
|
-
|
|
|
-void Control::setAutoWidth(AutoSize mode)
|
|
|
-{
|
|
|
- if (_autoWidth != mode)
|
|
|
+ if (_alignment != alignment)
|
|
|
{
|
|
|
- _autoWidth = mode;
|
|
|
- _dirty = true;
|
|
|
+ _alignment = alignment;
|
|
|
+ setDirty(DIRTY_BOUNDS);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-Control::AutoSize Control::getAutoHeight() const
|
|
|
+Control::Alignment Control::getAlignment() const
|
|
|
{
|
|
|
- return _autoHeight;
|
|
|
+ return _alignment;
|
|
|
}
|
|
|
|
|
|
-void Control::setAutoHeight(bool autoHeight)
|
|
|
+Control::AutoSize Control::getAutoSize() const
|
|
|
{
|
|
|
- setAutoHeight(autoHeight ? AUTO_SIZE_STRETCH : AUTO_SIZE_NONE);
|
|
|
+ return _autoSize;
|
|
|
}
|
|
|
|
|
|
-void Control::setAutoHeight(AutoSize mode)
|
|
|
+void Control::setAutoSize(AutoSize mode)
|
|
|
{
|
|
|
- if (_autoHeight != mode)
|
|
|
+ if (_autoSize != mode)
|
|
|
{
|
|
|
- _autoHeight = mode;
|
|
|
- _dirty = true;
|
|
|
+ _autoSize = mode;
|
|
|
+ setDirty(DIRTY_BOUNDS);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -468,10 +499,11 @@ void Control::setVisible(bool visible)
|
|
|
if (_visible != visible)
|
|
|
{
|
|
|
_visible = visible;
|
|
|
- _dirty = true;
|
|
|
|
|
|
if (!_visible)
|
|
|
Form::controlDisabled(this);
|
|
|
+
|
|
|
+ setDirty(DIRTY_BOUNDS);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -525,10 +557,9 @@ void Control::setOpacity(float opacity, unsigned char states)
|
|
|
|
|
|
for (int i = 0; i < Theme::Style::OVERLAY_MAX; ++i)
|
|
|
{
|
|
|
- if( overlays[i] )
|
|
|
+ if (overlays[i])
|
|
|
overlays[i]->setOpacity(opacity);
|
|
|
}
|
|
|
- _dirty = true;
|
|
|
}
|
|
|
|
|
|
float Control::getOpacity(State state) const
|
|
|
@@ -543,10 +574,11 @@ void Control::setEnabled(bool enabled)
|
|
|
if (_enabled != enabled)
|
|
|
{
|
|
|
_enabled = enabled;
|
|
|
- _dirty = true;
|
|
|
|
|
|
if (!_enabled)
|
|
|
Form::controlDisabled(this);
|
|
|
+
|
|
|
+ setDirty(DIRTY_STATE);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -574,11 +606,11 @@ void Control::setBorder(float top, float bottom, float left, float right, unsign
|
|
|
|
|
|
for (int i = 0; i < Theme::Style::OVERLAY_MAX; ++i)
|
|
|
{
|
|
|
- if( overlays[i] )
|
|
|
+ if (overlays[i])
|
|
|
overlays[i]->setBorder(top, bottom, left, right);
|
|
|
}
|
|
|
|
|
|
- _dirty = true;
|
|
|
+ setDirty(DIRTY_BOUNDS);
|
|
|
}
|
|
|
|
|
|
const Theme::Border& Control::getBorder(State state) const
|
|
|
@@ -599,7 +631,6 @@ void Control::setSkinRegion(const Rectangle& region, unsigned char states)
|
|
|
if( overlays[i] )
|
|
|
overlays[i]->setSkinRegion(region, _style->_tw, _style->_th);
|
|
|
}
|
|
|
- _dirty = true;
|
|
|
}
|
|
|
|
|
|
const Rectangle& Control::getSkinRegion(State state) const
|
|
|
@@ -620,8 +651,6 @@ void Control::setSkinColor(const Vector4& color, unsigned char states)
|
|
|
if( overlays[i] )
|
|
|
overlays[i]->setSkinColor(color);
|
|
|
}
|
|
|
-
|
|
|
- _dirty = true;
|
|
|
}
|
|
|
|
|
|
const Vector4& Control::getSkinColor(State state) const
|
|
|
@@ -636,7 +665,7 @@ void Control::setMargin(float top, float bottom, float left, float right)
|
|
|
GP_ASSERT(_style);
|
|
|
overrideStyle();
|
|
|
_style->setMargin(top, bottom, left, right);
|
|
|
- _dirty = true;
|
|
|
+ setDirty(DIRTY_BOUNDS);
|
|
|
}
|
|
|
|
|
|
const Theme::Margin& Control::getMargin() const
|
|
|
@@ -650,7 +679,7 @@ void Control::setPadding(float top, float bottom, float left, float right)
|
|
|
GP_ASSERT(_style);
|
|
|
overrideStyle();
|
|
|
_style->setPadding(top, bottom, left, right);
|
|
|
- _dirty = true;
|
|
|
+ setDirty(DIRTY_BOUNDS);
|
|
|
}
|
|
|
|
|
|
const Theme::Padding& Control::getPadding() const
|
|
|
@@ -670,8 +699,6 @@ void Control::setImageRegion(const char* id, const Rectangle& region, unsigned c
|
|
|
if( overlays[i] )
|
|
|
overlays[i]->setImageRegion(id, region, _style->_tw, _style->_th);
|
|
|
}
|
|
|
-
|
|
|
- _dirty = true;
|
|
|
}
|
|
|
|
|
|
const Rectangle& Control::getImageRegion(const char* id, State state) const
|
|
|
@@ -692,8 +719,6 @@ void Control::setImageColor(const char* id, const Vector4& color, unsigned char
|
|
|
if( overlays[i] )
|
|
|
overlays[i]->setImageColor(id, color);
|
|
|
}
|
|
|
-
|
|
|
- _dirty = true;
|
|
|
}
|
|
|
|
|
|
const Vector4& Control::getImageColor(const char* id, State state) const
|
|
|
@@ -721,8 +746,6 @@ void Control::setCursorRegion(const Rectangle& region, unsigned char states)
|
|
|
if( overlays[i] )
|
|
|
overlays[i]->setCursorRegion(region, _style->_tw, _style->_th);
|
|
|
}
|
|
|
-
|
|
|
- _dirty = true;
|
|
|
}
|
|
|
|
|
|
const Rectangle& Control::getCursorRegion(State state) const
|
|
|
@@ -743,8 +766,6 @@ void Control::setCursorColor(const Vector4& color, unsigned char states)
|
|
|
if( overlays[i] )
|
|
|
overlays[i]->setCursorColor(color);
|
|
|
}
|
|
|
-
|
|
|
- _dirty = true;
|
|
|
}
|
|
|
|
|
|
const Vector4& Control::getCursorColor(State state)
|
|
|
@@ -773,7 +794,8 @@ void Control::setFont(Font* font, unsigned char states)
|
|
|
overlays[i]->setFont(font);
|
|
|
}
|
|
|
|
|
|
- _dirty = true;
|
|
|
+ if (_autoSize != AUTO_SIZE_NONE)
|
|
|
+ setDirty(DIRTY_BOUNDS);
|
|
|
}
|
|
|
|
|
|
Font* Control::getFont(State state) const
|
|
|
@@ -795,7 +817,8 @@ void Control::setFontSize(unsigned int fontSize, unsigned char states)
|
|
|
overlays[i]->setFontSize(fontSize);
|
|
|
}
|
|
|
|
|
|
- _dirty = true;
|
|
|
+ if (_autoSize != AUTO_SIZE_NONE)
|
|
|
+ setDirty(DIRTY_BOUNDS);
|
|
|
}
|
|
|
|
|
|
unsigned int Control::getFontSize(State state) const
|
|
|
@@ -816,8 +839,6 @@ void Control::setTextColor(const Vector4& color, unsigned char states)
|
|
|
if( overlays[i] )
|
|
|
overlays[i]->setTextColor(color);
|
|
|
}
|
|
|
-
|
|
|
- _dirty = true;
|
|
|
}
|
|
|
|
|
|
const Vector4& Control::getTextColor(State state) const
|
|
|
@@ -838,8 +859,6 @@ void Control::setTextAlignment(Font::Justify alignment, unsigned char states)
|
|
|
if( overlays[i] )
|
|
|
overlays[i]->setTextAlignment(alignment);
|
|
|
}
|
|
|
-
|
|
|
- _dirty = true;
|
|
|
}
|
|
|
|
|
|
Font::Justify Control::getTextAlignment(State state) const
|
|
|
@@ -860,8 +879,6 @@ void Control::setTextRightToLeft(bool rightToLeft, unsigned char states)
|
|
|
if( overlays[i] )
|
|
|
overlays[i]->setTextRightToLeft(rightToLeft);
|
|
|
}
|
|
|
-
|
|
|
- _dirty = true;
|
|
|
}
|
|
|
|
|
|
bool Control::getTextRightToLeft(State state) const
|
|
|
@@ -880,10 +897,9 @@ void Control::setStyle(Theme::Style* style)
|
|
|
{
|
|
|
if (style != _style)
|
|
|
{
|
|
|
- _dirty = true;
|
|
|
+ _style = style;
|
|
|
+ setDirty(DIRTY_BOUNDS);
|
|
|
}
|
|
|
-
|
|
|
- _style = style;
|
|
|
}
|
|
|
|
|
|
Theme* Control::getTheme() const
|
|
|
@@ -904,7 +920,7 @@ Control::State Control::getState() const
|
|
|
return FOCUS;
|
|
|
return Form::_activeControlState;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
if (Form::_focusControl == this)
|
|
|
return FOCUS;
|
|
|
|
|
|
@@ -950,7 +966,6 @@ void Control::setZIndex(int zIndex)
|
|
|
if (zIndex != _zIndex)
|
|
|
{
|
|
|
_zIndex = zIndex;
|
|
|
- _dirty = true;
|
|
|
|
|
|
if (_parent)
|
|
|
{
|
|
|
@@ -1044,12 +1059,12 @@ void Control::addSpecificListener(Control::Listener* listener, Control::Listener
|
|
|
|
|
|
bool Control::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
|
|
|
{
|
|
|
- return false;// _consumeInputEvents;
|
|
|
+ return false;
|
|
|
}
|
|
|
|
|
|
bool Control::keyEvent(Keyboard::KeyEvent evt, int key)
|
|
|
{
|
|
|
- return false;// _consumeInputEvents;
|
|
|
+ return false;
|
|
|
}
|
|
|
|
|
|
bool Control::mouseEvent(Mouse::MouseEvent evt, int x, int y, int wheelDelta)
|
|
|
@@ -1061,7 +1076,7 @@ bool Control::mouseEvent(Mouse::MouseEvent evt, int x, int y, int wheelDelta)
|
|
|
|
|
|
bool Control::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex)
|
|
|
{
|
|
|
- return false;// _consumeInputEvents;
|
|
|
+ return false;
|
|
|
}
|
|
|
|
|
|
void Control::notifyListeners(Control::Listener::EventType eventType)
|
|
|
@@ -1096,23 +1111,90 @@ void Control::controlEvent(Control::Listener::EventType evt)
|
|
|
{
|
|
|
}
|
|
|
|
|
|
-void Control::update(const Control* container, const Vector2& offset)
|
|
|
+void Control::setDirty(int bits)
|
|
|
{
|
|
|
- Game* game = Game::getInstance();
|
|
|
+ _dirtyBits |= bits;
|
|
|
+}
|
|
|
|
|
|
- const Rectangle parentAbsoluteBounds = container ? container->_viewportBounds : Rectangle(0, 0, game->getViewport().width, game->getViewport().height);
|
|
|
- const Rectangle parentAbsoluteClip = container ? container->_viewportClipBounds : parentAbsoluteBounds;
|
|
|
+bool Control::isDirty(int bit) const
|
|
|
+{
|
|
|
+ return (_dirtyBits & bit) == bit;
|
|
|
+}
|
|
|
|
|
|
- const Theme::Border& border = getBorder(getState());
|
|
|
- const Theme::Padding& padding = getPadding();
|
|
|
+void Control::update(float elapsedTime)
|
|
|
+{
|
|
|
+ State state = getState();
|
|
|
|
|
|
- // Compute content are padding values
|
|
|
- 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;
|
|
|
+ // 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::updateState(State state)
|
|
|
+{
|
|
|
+ // Clear dirty state bit
|
|
|
+ _dirtyBits &= ~DIRTY_STATE;
|
|
|
+
|
|
|
+ // Cache themed attributes for performance.
|
|
|
+ _skin = getSkin(state);
|
|
|
+}
|
|
|
+
|
|
|
+bool Control::updateBoundsInternal(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 our dirty bounds bit
|
|
|
+ bool dirtyBounds = (_dirtyBits & DIRTY_BOUNDS) != 0;
|
|
|
+ _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();
|
|
|
+
|
|
|
+ const Rectangle parentAbsoluteBounds = _parent ? _parent->_viewportBounds : Rectangle(0, 0, game->getViewport().width, game->getViewport().height);
|
|
|
|
|
|
// Calculate local unclipped bounds.
|
|
|
_bounds.set(_relativeBounds);
|
|
|
@@ -1120,15 +1202,80 @@ void Control::update(const Control* container, const Vector2& offset)
|
|
|
_bounds.x *= parentAbsoluteBounds.width;
|
|
|
if (isYPercentage())
|
|
|
_bounds.y *= parentAbsoluteBounds.height;
|
|
|
- if (_autoWidth == AUTO_SIZE_STRETCH)
|
|
|
- _bounds.width = parentAbsoluteBounds.width;
|
|
|
- else if (isWidthPercentage())
|
|
|
+ if (isWidthPercentage())
|
|
|
_bounds.width *= parentAbsoluteBounds.width;
|
|
|
- if (_autoHeight == AUTO_SIZE_STRETCH)
|
|
|
- _bounds.height = parentAbsoluteBounds.height;
|
|
|
- else if (isHeightPercentage())
|
|
|
+ if (isHeightPercentage())
|
|
|
_bounds.height *= parentAbsoluteBounds.height;
|
|
|
|
|
|
+ // Apply control alignment
|
|
|
+ if (_alignment != Control::ALIGN_TOP_LEFT)
|
|
|
+ {
|
|
|
+ const Theme::Margin& margin = _style->getMargin();
|
|
|
+ const Rectangle& parentBounds = _parent ? _parent->getBounds() : Rectangle(0, 0, game->getViewport().width, game->getViewport().height);
|
|
|
+ const Theme::Border& parentBorder = _parent ? _parent->getBorder(_parent->getState()) : Theme::Border::empty();
|
|
|
+ const Theme::Padding& parentPadding = _parent ? _parent->getPadding() : Theme::Padding::empty();
|
|
|
+
|
|
|
+ float clipWidth, clipHeight;
|
|
|
+ if (_parent && (_parent->getScroll() != Container::SCROLL_NONE))
|
|
|
+ {
|
|
|
+ const Rectangle& verticalScrollBarBounds = _parent->getImageRegion("verticalScrollBar", _parent->getState());
|
|
|
+ const Rectangle& horizontalScrollBarBounds = _parent->getImageRegion("horizontalScrollBar", _parent->getState());
|
|
|
+ clipWidth = parentBounds.width - parentBorder.left - parentBorder.right - parentPadding.left - parentPadding.right - verticalScrollBarBounds.width;
|
|
|
+ clipHeight = parentBounds.height - parentBorder.top - parentBorder.bottom - parentPadding.top - parentPadding.bottom - horizontalScrollBarBounds.height;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ clipWidth = parentBounds.width - parentBorder.left - parentBorder.right - parentPadding.left - parentPadding.right;
|
|
|
+ clipHeight = parentBounds.height - parentBorder.top - parentBorder.bottom - parentPadding.top - parentPadding.bottom;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Vertical alignment
|
|
|
+ if ((_alignment & Control::ALIGN_BOTTOM) == Control::ALIGN_BOTTOM)
|
|
|
+ {
|
|
|
+ _bounds.y = clipHeight - _bounds.height - margin.bottom;
|
|
|
+ }
|
|
|
+ else if ((_alignment & Control::ALIGN_VCENTER) == Control::ALIGN_VCENTER)
|
|
|
+ {
|
|
|
+ _bounds.y = clipHeight * 0.5f - _bounds.height * 0.5f;
|
|
|
+ }
|
|
|
+ else if ((_alignment & Control::ALIGN_TOP) == Control::ALIGN_TOP)
|
|
|
+ {
|
|
|
+ _bounds.y = margin.top;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Horizontal alignment
|
|
|
+ if ((_alignment & Control::ALIGN_RIGHT) == Control::ALIGN_RIGHT)
|
|
|
+ {
|
|
|
+ _bounds.x = clipWidth - _bounds.width - margin.right;
|
|
|
+ }
|
|
|
+ else if ((_alignment & Control::ALIGN_HCENTER) == Control::ALIGN_HCENTER)
|
|
|
+ {
|
|
|
+ _bounds.x = clipWidth * 0.5f - _bounds.width * 0.5f;
|
|
|
+ }
|
|
|
+ else if ((_alignment & Control::ALIGN_LEFT) == Control::ALIGN_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
|
|
|
+ const Theme::Border& border = getBorder(NORMAL);
|
|
|
+ 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 absolute unclipped bounds
|
|
|
_absoluteBounds.set(
|
|
|
parentAbsoluteBounds.x + offset.x + _bounds.x,
|
|
|
@@ -1156,14 +1303,6 @@ void Control::update(const Control* container, const Vector2& offset)
|
|
|
|
|
|
// Calculate the absolute clipped viewport bounds
|
|
|
Rectangle::intersect(_viewportBounds, parentAbsoluteClip, &_viewportClipBounds);
|
|
|
-
|
|
|
- // Cache themed attributes for performance.
|
|
|
- _skin = getSkin(getState());
|
|
|
-
|
|
|
- // Current opacity should be multiplied by that of the parent container.
|
|
|
- _opacity = getOpacity(getState());
|
|
|
- if (container)
|
|
|
- _opacity *= container->_opacity;
|
|
|
}
|
|
|
|
|
|
void Control::startBatch(Form* form, SpriteBatch* batch)
|
|
|
@@ -1293,11 +1432,6 @@ unsigned int Control::drawText(Form* form, const Rectangle& position)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-bool Control::isDirty()
|
|
|
-{
|
|
|
- return _dirty;
|
|
|
-}
|
|
|
-
|
|
|
bool Control::isContainer() const
|
|
|
{
|
|
|
return false;
|
|
|
@@ -1636,7 +1770,8 @@ void Control::setImageList(Theme::ImageList* imageList, unsigned char states)
|
|
|
overlays[i]->setImageList(imageList);
|
|
|
}
|
|
|
|
|
|
- _dirty = true;
|
|
|
+ if (_autoSize != AUTO_SIZE_NONE)
|
|
|
+ setDirty(DIRTY_BOUNDS);
|
|
|
}
|
|
|
|
|
|
void Control::setCursor(Theme::ThemeImage* cursor, unsigned char states)
|
|
|
@@ -1650,8 +1785,6 @@ void Control::setCursor(Theme::ThemeImage* cursor, unsigned char states)
|
|
|
if( overlays[i] )
|
|
|
overlays[i]->setCursor(cursor);
|
|
|
}
|
|
|
-
|
|
|
- _dirty = true;
|
|
|
}
|
|
|
|
|
|
void Control::setSkin(Theme::Skin* skin, unsigned char states)
|
|
|
@@ -1666,7 +1799,8 @@ void Control::setSkin(Theme::Skin* skin, unsigned char states)
|
|
|
overlays[i]->setSkin(skin);
|
|
|
}
|
|
|
|
|
|
- _dirty = true;
|
|
|
+ if (_autoSize != AUTO_SIZE_NONE)
|
|
|
+ setDirty(DIRTY_BOUNDS);
|
|
|
}
|
|
|
|
|
|
Theme::Skin* Control::getSkin(State state)
|