소스 검색

Started work on UI element attributes.

Lasse Öörni 13 년 전
부모
커밋
83e90ae61c

+ 1 - 1
Docs/ScriptAPI.dox

@@ -3755,7 +3755,7 @@ Properties:<br>
 - uint cursorPosition
 - float cursorBlinkRate
 - uint maxLength
-- uint8 echoCharacter
+- uint echoCharacter
 - bool cursorMovable
 - bool textSelectable
 - bool textCopyable

+ 14 - 0
Engine/Core/StringUtils.cpp

@@ -376,4 +376,18 @@ unsigned GetStringListIndex(const char* value, const String* strings, unsigned d
     return defaultIndex;
 }
 
+unsigned GetStringListIndex(const char* value, const char** strings, unsigned defaultIndex, bool caseSensitive)
+{
+    unsigned i = 0;
+    
+    while (strings[i])
+    {
+        if (!String::Compare(value, strings[i], caseSensitive))
+            return i;
+        ++i;
+    }
+    
+    return defaultIndex;
+}
+
 }

+ 2 - 0
Engine/Core/StringUtils.h

@@ -88,5 +88,7 @@ String ToStringHex(unsigned value);
 unsigned GetStringListIndex(const String& value, const String* strings, unsigned defaultIndex, bool caseSensitive = false);
 /// Return an index to a string list corresponding to the given C string, or a default value if not found. The string list must be empty-terminated.
 unsigned GetStringListIndex(const char* value, const String* strings, unsigned defaultIndex, bool caseSensitive = false);
+/// Return an index to a string list corresponding to the given string, or a default value if not found. The string list must be empty-terminated.
+unsigned GetStringListIndex(const char* value, const char** strings, unsigned defaultIndex, bool caseSensitive = false);
 
 }

+ 2 - 2
Engine/Engine/UIAPI.cpp

@@ -300,8 +300,8 @@ static void RegisterLineEdit(asIScriptEngine* engine)
     engine->RegisterObjectMethod("LineEdit", "float get_cursorBlinkRate() const", asMETHOD(LineEdit, GetCursorBlinkRate), asCALL_THISCALL);
     engine->RegisterObjectMethod("LineEdit", "void set_maxLength(uint)", asMETHOD(LineEdit, SetMaxLength), asCALL_THISCALL);
     engine->RegisterObjectMethod("LineEdit", "uint get_maxLength() const", asMETHOD(LineEdit, GetMaxLength), asCALL_THISCALL);
-    engine->RegisterObjectMethod("LineEdit", "void set_echoCharacter(uint8)", asMETHOD(LineEdit, SetEchoCharacter), asCALL_THISCALL);
-    engine->RegisterObjectMethod("LineEdit", "uint8 get_echoCharacter() const", asMETHOD(LineEdit, GetEchoCharacter), asCALL_THISCALL);
+    engine->RegisterObjectMethod("LineEdit", "void set_echoCharacter(uint)", asMETHOD(LineEdit, SetEchoCharacter), asCALL_THISCALL);
+    engine->RegisterObjectMethod("LineEdit", "uint get_echoCharacter() const", asMETHOD(LineEdit, GetEchoCharacter), asCALL_THISCALL);
     engine->RegisterObjectMethod("LineEdit", "void set_cursorMovable(bool)", asMETHOD(LineEdit, SetCursorMovable), asCALL_THISCALL);
     engine->RegisterObjectMethod("LineEdit", "bool get_cursorMovable() const", asMETHOD(LineEdit, IsCursorMovable), asCALL_THISCALL);
     engine->RegisterObjectMethod("LineEdit", "void set_textSelectable(bool)", asMETHOD(LineEdit, SetTextSelectable), asCALL_THISCALL);

+ 6 - 0
Engine/IO/Deserializer.cpp

@@ -322,6 +322,12 @@ Variant Deserializer::ReadVariant(VariantType type)
     case VAR_VARIANTMAP:
         return Variant(ReadVariantMap());
         
+    case VAR_INTRECT:
+        return Variant(ReadIntRect());
+        
+    case VAR_INTVECTOR2:
+        return Variant(ReadIntVector2());
+        
     default:
         return Variant();
     }

+ 6 - 0
Engine/IO/Serializer.cpp

@@ -269,6 +269,12 @@ bool Serializer::WriteVariantData(const Variant& value)
         
     case VAR_VARIANTMAP:
         return WriteVariantMap(value.GetVariantMap());
+        
+    case VAR_INTRECT:
+        return WriteIntRect(value.GetIntRect());
+        
+    case VAR_INTVECTOR2:
+        return WriteIntVector2(value.GetIntVector2());
     }
     
     return false;

+ 1 - 1
Engine/Scene/Serializable.cpp

@@ -285,7 +285,7 @@ bool Serializable::LoadXML(const XMLElement& source)
                     bool enumFound = false;
                     while (*enumPtr)
                     {
-                        if (!String::Compare(*enumPtr, value, true))
+                        if (!String::Compare(*enumPtr, value, false))
                         {
                             enumFound = true;
                             break;

+ 17 - 0
Engine/UI/BorderImage.cpp

@@ -49,6 +49,12 @@ BorderImage::~BorderImage()
 void BorderImage::RegisterObject(Context* context)
 {
     context->RegisterFactory<BorderImage>();
+    
+    COPY_BASE_ATTRIBUTES(BorderImage, UIElement);
+    ACCESSOR_ATTRIBUTE(BorderImage, VAR_RESOURCEREF, "Texture", GetTextureAttr, SetTextureAttr, ResourceRef, ResourceRef(Texture2D::GetTypeStatic()), AM_FILE);
+    REF_ACCESSOR_ATTRIBUTE(BorderImage, VAR_INTRECT, "Image Rect", GetImageRect, SetImageRect, IntRect, IntRect::ZERO, AM_FILE);
+    REF_ACCESSOR_ATTRIBUTE(BorderImage, VAR_INTRECT, "Border", GetBorder, SetBorder, IntRect, IntRect::ZERO, AM_FILE);
+    REF_ACCESSOR_ATTRIBUTE(BorderImage, VAR_INTVECTOR2, "Hover Offset", GetHoverOffset, SetHoverOffset, IntVector2, IntVector2::ZERO, AM_FILE);
 }
 
 void BorderImage::SetStyle(const XMLElement& element)
@@ -69,6 +75,17 @@ void BorderImage::SetStyle(const XMLElement& element)
         SetHoverOffset(element.GetChild("hoveroffset").GetIntVector2("value"));
 }
 
+void BorderImage::SetTextureAttr(ResourceRef value)
+{
+    ResourceCache* cache = GetSubsystem<ResourceCache>();
+    SetTexture(cache->GetResource<Texture2D>(value.id_));
+}
+
+ResourceRef BorderImage::GetTextureAttr() const
+{
+    return GetResourceRef(texture_, Texture2D::GetTypeStatic());
+}
+
 void BorderImage::GetBatches(PODVector<UIBatch>& batches, PODVector<UIQuad>& quads, const IntRect& currentScissor)
 {
     if (hovering_ || selected_)

+ 5 - 0
Engine/UI/BorderImage.h

@@ -73,6 +73,11 @@ public:
     /// Return offset to image rectangle used on hover.
     const IntVector2& GetHoverOffset() const { return hoverOffset_; }
     
+    /// Set texture attribute.
+    void SetTextureAttr(ResourceRef value);
+    /// Return texture attribute.
+    ResourceRef GetTextureAttr() const;
+    
 protected:
     /// Return UI rendering batches with offset to image rectangle.
     void GetBatches(PODVector<UIBatch>& batches, PODVector<UIQuad>& quads, const IntRect& currentScissor, const IntVector2& offset);

+ 6 - 0
Engine/UI/Button.cpp

@@ -53,6 +53,12 @@ Button::~Button()
 void Button::RegisterObject(Context* context)
 {
     context->RegisterFactory<Button>();
+    
+    COPY_BASE_ATTRIBUTES(Button, BorderImage);
+    REF_ACCESSOR_ATTRIBUTE(Button, VAR_INTVECTOR2, "Pressed Offset", GetPressedOffset, SetPressedOffset, IntVector2, IntVector2::ZERO, AM_FILE);
+    REF_ACCESSOR_ATTRIBUTE(Button, VAR_INTVECTOR2, "Label Offset", GetLabelOffset, SetLabelOffset, IntVector2, IntVector2::ZERO, AM_FILE);
+    ACCESSOR_ATTRIBUTE(Button, VAR_FLOAT, "Repeat Delay", GetRepeatDelay, SetRepeatDelay, float, 1.0f, AM_FILE);
+    ACCESSOR_ATTRIBUTE(Button, VAR_FLOAT, "Repeat Rate", GetRepeatRate, SetRepeatRate, float, 0.0f, AM_FILE);
 }
 
 void Button::SetStyle(const XMLElement& element)

+ 2 - 2
Engine/UI/Button.h

@@ -54,9 +54,9 @@ public:
     /// React to mouse click.
     virtual void OnClick(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor);
     
-    /// Set pressed image offset.
+    /// Set offset to image rectangle used when pressed.
     void SetPressedOffset(const IntVector2& offset);
-    /// Set pressed image offset.
+    /// Set offset to image rectangle used when pressed.
     void SetPressedOffset(int x, int y);
     /// Set pressed label offset.
     void SetLabelOffset(const IntVector2& offset);

+ 3 - 0
Engine/UI/CheckBox.cpp

@@ -49,6 +49,9 @@ CheckBox::~CheckBox()
 void CheckBox::RegisterObject(Context* context)
 {
     context->RegisterFactory<CheckBox>();
+    
+    COPY_BASE_ATTRIBUTES(CheckBox, BorderImage);
+    REF_ACCESSOR_ATTRIBUTE(CheckBox, VAR_INTVECTOR2,"Checked Offset", GetCheckedOffset, SetCheckedOffset, IntVector2, IntVector2::ZERO, AM_FILE);
 }
 
 void CheckBox::SetStyle(const XMLElement& element)

+ 11 - 3
Engine/UI/LineEdit.cpp

@@ -71,6 +71,15 @@ LineEdit::~LineEdit()
 void LineEdit::RegisterObject(Context* context)
 {
     context->RegisterFactory<LineEdit>();
+    
+    COPY_BASE_ATTRIBUTES(LineEdit, BorderImage);
+    ACCESSOR_ATTRIBUTE(LineEdit, VAR_INT, "Max Length", GetMaxLength, SetMaxLength, unsigned, 0, AM_FILE);
+    ACCESSOR_ATTRIBUTE(LineEdit, VAR_BOOL, "Is Cursor Movable", IsCursorMovable, SetCursorMovable, bool, true, AM_FILE);
+    ACCESSOR_ATTRIBUTE(LineEdit, VAR_BOOL, "Is Text Selectable", IsTextSelectable, SetTextSelectable, bool, true, AM_FILE);
+    ACCESSOR_ATTRIBUTE(LineEdit, VAR_BOOL, "Is Text Copyable", IsTextCopyable, SetTextCopyable, bool, true, AM_FILE);
+    ACCESSOR_ATTRIBUTE(LineEdit, VAR_INT, "Cursor Position", GetCursorPosition, SetCursorPosition, unsigned, 0, AM_FILE);
+    ACCESSOR_ATTRIBUTE(LineEdit, VAR_FLOAT, "Cursor Blink Rate", GetCursorBlinkRate, SetCursorBlinkRate, float, 1.0f, AM_FILE);
+    ATTRIBUTE(LineEdit, VAR_INT, "Echo Character", echoCharacter_, 0, AM_FILE);
 }
 
 void LineEdit::SetStyle(const XMLElement& element)
@@ -466,7 +475,7 @@ void LineEdit::SetMaxLength(unsigned length)
     maxLength_ = length;
 }
 
-void LineEdit::SetEchoCharacter(char c)
+void LineEdit::SetEchoCharacter(unsigned c)
 {
     echoCharacter_ = c;
     UpdateText();
@@ -496,9 +505,8 @@ void LineEdit::UpdateText()
     else
     {
         String echoText;
-        echoText.Resize(utf8Length);
         for (unsigned i = 0; i < utf8Length; ++i)
-            echoText[i] = echoCharacter_;
+            echoText.AppendUTF8(echoCharacter_);
         text_->SetText(echoText);
     }
     if (cursorPosition_ > utf8Length)

+ 3 - 3
Engine/UI/LineEdit.h

@@ -75,7 +75,7 @@ public:
     /// Set maximum text length. 0 for unlimited.
     void SetMaxLength(unsigned length);
     /// Set echo character for password entry and such. 0 (default) shows the actual text.
-    void SetEchoCharacter(char c);
+    void SetEchoCharacter(unsigned c);
     /// Set whether can move cursor with arrows or mouse, default true.
     void SetCursorMovable(bool enable);
     /// Set whether selections are allowed, default true.
@@ -92,7 +92,7 @@ public:
     /// Return maximum text length.
     unsigned GetMaxLength() const { return maxLength_; }
     /// Return echo character.
-    char GetEchoCharacter() const { return echoCharacter_; }
+    unsigned GetEchoCharacter() const { return echoCharacter_; }
     /// Return whether can move cursor with arrows or mouse.
     bool IsCursorMovable() const { return cursorMovable_; }
     /// Return whether selections are allowed.
@@ -133,7 +133,7 @@ protected:
     /// Maximum text length.
     unsigned maxLength_;
     /// Echo character.
-    char echoCharacter_;
+    unsigned echoCharacter_;
     /// Cursor movable flag.
     bool cursorMovable_;
     /// Text selectable flag.

+ 35 - 5
Engine/UI/Text.cpp

@@ -38,11 +38,11 @@ namespace Urho3D
 
 static const float MIN_ROW_SPACING = 0.5f;
 
-static const String horizontalAlignments[] =
+static const char* horizontalAlignments[] =
 {
-    "left",
-    "center",
-    "right",
+    "Left",
+    "Center",
+    "Right",
     ""
 };
 
@@ -69,6 +69,25 @@ Text::~Text()
 void Text::RegisterObject(Context* context)
 {
     context->RegisterFactory<Text>();
+    
+    COPY_BASE_ATTRIBUTES(Text, UIElement);
+    ACCESSOR_ATTRIBUTE(Text, VAR_RESOURCEREF, "Font", GetFontAttr, SetFontAttr, ResourceRef, ResourceRef(Font::GetTypeStatic()), AM_FILE);
+    ATTRIBUTE(Text, VAR_INT, "Font Size", fontSize_, DEFAULT_FONT_SIZE, AM_FILE);
+    ATTRIBUTE(Text, VAR_STRING, "Text", text_, String(), AM_FILE);
+    ENUM_ATTRIBUTE(Text, "Text Alignment", textAlignment_, horizontalAlignments, HA_LEFT, AM_FILE);
+    ATTRIBUTE(Text, VAR_FLOAT, "Row Spacing", rowSpacing_, 1.0f, AM_FILE);
+    ATTRIBUTE(Text, VAR_BOOL, "Word Wrap", wordWrap_, false, AM_FILE);
+    ATTRIBUTE(Text, VAR_INT, "Selection Start", selectionStart_, 0, AM_FILE);
+    ATTRIBUTE(Text, VAR_INT, "Selection Length", selectionLength_, 0, AM_FILE);
+    REF_ACCESSOR_ATTRIBUTE(Text, VAR_COLOR, "Selection Color", GetSelectionColor, SetSelectionColor, Color, Color::BLACK, AM_FILE);
+    REF_ACCESSOR_ATTRIBUTE(Text, VAR_COLOR, "Hover Color", GetHoverColor, SetHoverColor, Color, Color::BLACK, AM_FILE);
+}
+
+void Text::ApplyAttributes()
+{
+    fontSize_ = Max(fontSize_, 1);
+    ValidateSelection();
+    UpdateText();
 }
 
 void Text::SetStyle(const XMLElement& element)
@@ -123,7 +142,7 @@ void Text::SetStyle(const XMLElement& element)
         String horiz = element.GetChild("textalignment").GetAttributeLower("value");
         if (!horiz.Empty())
         {
-            textAlignment_ = (HorizontalAlignment)GetStringListIndex(horiz, horizontalAlignments, HA_LEFT);
+            textAlignment_ = (HorizontalAlignment)GetStringListIndex(horiz.CString(), horizontalAlignments, HA_LEFT);
             changed = true;
         }
     }
@@ -349,6 +368,17 @@ void Text::SetHoverColor(const Color& color)
     hoverColor_ = color;
 }
 
+void Text::SetFontAttr(ResourceRef value)
+{
+    ResourceCache* cache = GetSubsystem<ResourceCache>();
+    font_ = cache->GetResource<Font>(value.id_);
+}
+
+ResourceRef Text::GetFontAttr() const
+{
+    return GetResourceRef(font_, Font::GetTypeStatic());
+}
+
 void Text::UpdateText(bool inResize)
 {
     int width = 0;

+ 7 - 0
Engine/UI/Text.h

@@ -47,6 +47,8 @@ public:
     /// Register object factory.
     static void RegisterObject(Context* context);
     
+    /// Apply attribute changes that can not be applied immediately.
+    virtual void ApplyAttributes();
     /// Set UI element style from XML data.
     virtual void SetStyle(const XMLElement& element);
     /// Return UI rendering batches.
@@ -106,6 +108,11 @@ public:
     /// Return size of each character.
     const PODVector<IntVector2>& GetCharSizes() const { return charSizes_; }
     
+    /// Set font attribute.
+    void SetFontAttr(ResourceRef value);
+    /// Return font attribute.
+    ResourceRef GetFontAttr() const;
+    
 protected:
     /// Update text when text, font or spacing changed.
     void UpdateText(bool inResize = false);

+ 94 - 27
Engine/UI/UIElement.cpp

@@ -36,38 +36,46 @@
 namespace Urho3D
 {
 
-static const String horizontalAlignments[] =
+static const char* horizontalAlignments[] =
 {
-    "left",
-    "center",
-    "right",
-    ""
+    "Left",
+    "Center",
+    "Right",
+    0
 };
 
-static const String verticalAlignments[] =
+static const char* verticalAlignments[] =
 {
-    "top",
-    "center",
-    "bottom",
-    ""
+    "Top",
+    "Center",
+    "Bottom",
+    0
 };
 
-static const String focusModes[] =
+static const char* focusModes[] =
 {
-    "notfocusable",
-    "resetfocus",
-    "focusable",
-    "focusabledefocusable",
-    ""
+    "NotFocusable",
+    "ResetFocus",
+    "Focusable",
+    "FocusableDefocusable",
+    0
 };
 
-static const String dragDropModes[] =
+static const char* dragDropModes[] =
 {
-    "disabled",
-    "source",
-    "target",
-    "sourceandtarget",
-    ""
+    "Disabled",
+    "Source",
+    "Target",
+    "SourceAndTarget",
+    0
+};
+
+static const char* layoutModes[] =
+{
+    "Free",
+    "Horizontal",
+    "Vertical",
+    0
 };
 
 static bool CompareUIElements(const UIElement* lhs, const UIElement* rhs)
@@ -75,10 +83,30 @@ static bool CompareUIElements(const UIElement* lhs, const UIElement* rhs)
     return lhs->GetPriority() < rhs->GetPriority();
 }
 
+template<> HorizontalAlignment Variant::Get<HorizontalAlignment>() const
+{
+    return (HorizontalAlignment)GetInt();
+}
+
+template<> VerticalAlignment Variant::Get<VerticalAlignment>() const
+{
+    return (VerticalAlignment)GetInt();
+}
+
+template<> FocusMode Variant::Get<FocusMode>() const
+{
+    return (FocusMode)GetInt();
+}
+
+template<> LayoutMode Variant::Get<LayoutMode>() const
+{
+    return (LayoutMode)GetInt();
+}
+
 OBJECTTYPESTATIC(UIElement);
 
 UIElement::UIElement(Context* context) :
-    Object(context),
+    Serializable(context),
     parent_(0),
     clipBorder_(IntRect::ZERO),
     priority_(0),
@@ -133,6 +161,45 @@ UIElement::~UIElement()
 void UIElement::RegisterObject(Context* context)
 {
     context->RegisterFactory<UIElement>();
+    
+    REF_ACCESSOR_ATTRIBUTE(UIElement, VAR_STRING, "Name", GetName, SetName, String, String(), AM_FILE);
+    REF_ACCESSOR_ATTRIBUTE(UIElement, VAR_INTVECTOR2, "Position", GetPosition, SetPosition, IntVector2, IntVector2::ZERO, AM_FILE);
+    REF_ACCESSOR_ATTRIBUTE(UIElement, VAR_INTVECTOR2, "Size", GetSize, SetSize, IntVector2, IntVector2::ZERO, AM_FILE);
+    REF_ACCESSOR_ATTRIBUTE(UIElement, VAR_INTVECTOR2, "Min Size", GetMinSize, SetMinSize, IntVector2, IntVector2::ZERO, AM_FILE);
+    REF_ACCESSOR_ATTRIBUTE(UIElement, VAR_INTVECTOR2, "Max Size", GetMaxSize, SetMaxSize, IntVector2, IntVector2::ZERO, AM_FILE);
+    ENUM_ACCESSOR_ATTRIBUTE(UIElement, "Horiz Alignment", GetHorizontalAlignment, SetHorizontalAlignment, HorizontalAlignment, horizontalAlignments, HA_LEFT, AM_FILE);
+    ENUM_ACCESSOR_ATTRIBUTE(UIElement, "Vert Alignment", GetVerticalAlignment, SetVerticalAlignment, VerticalAlignment, verticalAlignments, VA_TOP, AM_FILE);
+    REF_ACCESSOR_ATTRIBUTE(UIElement, VAR_INTRECT, "Clip Border", GetClipBorder, SetClipBorder, IntRect, IntRect::ZERO, AM_FILE);
+    ACCESSOR_ATTRIBUTE(UIElement, VAR_INT, "Priority", GetPriority, SetPriority, int, 0, AM_FILE);
+    ACCESSOR_ATTRIBUTE(UIElement, VAR_FLOAT, "Opacity", GetOpacity, SetOpacity, float, 1.0f, AM_FILE);
+    ATTRIBUTE(UIElement, VAR_COLOR, "Top Left Color", color_[0], Color::WHITE, AM_FILE);
+    ATTRIBUTE(UIElement, VAR_COLOR, "Top Right Color", color_[1], Color::WHITE, AM_FILE);
+    ATTRIBUTE(UIElement, VAR_COLOR, "Bottom Left Color", color_[2], Color::WHITE, AM_FILE);
+    ATTRIBUTE(UIElement, VAR_COLOR, "Bottom Right Color", color_[3], Color::WHITE, AM_FILE);
+    ACCESSOR_ATTRIBUTE(UIElement, VAR_BOOL, "Is Active", IsActive, SetActive, bool, false, AM_FILE);
+    ACCESSOR_ATTRIBUTE(UIElement, VAR_BOOL, "Is Selected", IsSelected, SetSelected, bool, false, AM_FILE);
+    ACCESSOR_ATTRIBUTE(UIElement, VAR_BOOL, "Is Visible", IsVisible, SetVisible, bool, true, AM_FILE);
+    ACCESSOR_ATTRIBUTE(UIElement, VAR_BOOL, "Bring To Front", GetBringToFront, SetBringToFront, bool, false, AM_FILE);
+    ACCESSOR_ATTRIBUTE(UIElement, VAR_BOOL, "Bring To Back", GetBringToBack, SetBringToBack, bool, true, AM_FILE);
+    ACCESSOR_ATTRIBUTE(UIElement, VAR_BOOL, "Clip Children", GetClipChildren, SetClipChildren, bool, false, AM_FILE);
+    ENUM_ACCESSOR_ATTRIBUTE(UIElement, "Focus Mode", GetFocusMode, SetFocusMode, FocusMode, focusModes, FM_NOTFOCUSABLE, AM_FILE);
+    ENUM_ACCESSOR_ATTRIBUTE(UIElement, "Drag And Drop Mode", GetDragDropMode, SetDragDropMode, unsigned, dragDropModes, DD_DISABLED, AM_FILE);
+    ENUM_ACCESSOR_ATTRIBUTE(UIElement, "Layout Mode", GetLayoutMode, SetLayoutMode, LayoutMode, layoutModes, LM_FREE, AM_FILE);
+    ACCESSOR_ATTRIBUTE(UIElement, VAR_INT, "Layout Spacing", GetLayoutSpacing, SetLayoutSpacing, int, 0, AM_FILE);
+    REF_ACCESSOR_ATTRIBUTE(UIElement, VAR_INTRECT, "Layout Border", GetLayoutBorder, SetLayoutBorder, IntRect, IntRect::ZERO, AM_FILE);
+    ATTRIBUTE(UIElement, VAR_VARIANTMAP, "Variables", vars_, VariantMap(), AM_FILE);
+}
+
+void UIElement::ApplyAttributes()
+{
+    colorGradient_ = false;
+    derivedColorDirty_ = true;
+    
+    for (unsigned i = 1; i < MAX_UIELEMENT_CORNERS; ++i)
+    {
+        if (color_[i] != color_[0])
+            colorGradient_ = true;
+    }
 }
 
 void UIElement::SetStyle(const XMLElement& element)
@@ -180,9 +247,9 @@ void UIElement::SetStyle(const XMLElement& element)
         if (alignElem.HasAttribute("v"))
             vert = alignElem.GetAttributeLower("v");
         if (!horiz.Empty())
-            SetHorizontalAlignment((HorizontalAlignment)GetStringListIndex(horiz, horizontalAlignments, HA_LEFT));
+            SetHorizontalAlignment((HorizontalAlignment)GetStringListIndex(horiz.CString(), horizontalAlignments, HA_LEFT));
         if (!vert.Empty())
-            SetVerticalAlignment((VerticalAlignment)GetStringListIndex(vert, verticalAlignments, VA_TOP));
+            SetVerticalAlignment((VerticalAlignment)GetStringListIndex(vert.CString(), verticalAlignments, VA_TOP));
     }
     if (element.HasChild("clipborder"))
         SetClipBorder(element.GetChild("clipborder").GetIntRect("value"));
@@ -219,14 +286,14 @@ void UIElement::SetStyle(const XMLElement& element)
     if (element.HasChild("focusmode"))
     {
         String focusMode = element.GetChild("focusmode").GetAttributeLower("value");
-        SetFocusMode((FocusMode)GetStringListIndex(focusMode, focusModes, FM_NOTFOCUSABLE));
+        SetFocusMode((FocusMode)GetStringListIndex(focusMode.CString(), focusModes, FM_NOTFOCUSABLE));
         if (focusMode == "defocusable")
             SetFocusMode(FM_FOCUSABLE_DEFOCUSABLE);
     }
     if (element.HasChild("dragdropmode"))
     {
         String dragDropMode = element.GetChild("dragdropmode").GetAttributeLower("value");
-        SetDragDropMode(GetStringListIndex(dragDropMode, dragDropModes, DD_DISABLED));
+        SetDragDropMode(GetStringListIndex(dragDropMode.CString(), dragDropModes, DD_DISABLED));
     }
     if (element.HasChild("layout"))
     {

+ 4 - 2
Engine/UI/UIElement.h

@@ -23,7 +23,7 @@
 
 #pragma once
 
-#include "Object.h"
+#include "Serializable.h"
 #include "UIBatch.h"
 #include "Vector2.h"
 #include "XMLFile.h"
@@ -101,7 +101,7 @@ class Cursor;
 class ResourceCache;
 
 /// Base class for %UI elements.
-class UIElement : public Object
+class UIElement : public Serializable
 {
     OBJECT(UIElement);
     
@@ -113,6 +113,8 @@ public:
     /// Register object factory.
     static void RegisterObject(Context* context);
     
+    /// Apply attribute changes that can not be applied immediately.
+    virtual void ApplyAttributes();
     /// Set UI element style from XML data.
     virtual void SetStyle(const XMLElement& element);
     /// Perform UI element update.