Browse Source

Refactored freely rotating / scaling UIElements into a separate Sprite class; removed rotation from the base class as it would not work correctly with element hierarchies in most cases.

Lasse Öörni 12 years ago
parent
commit
5160610e25

+ 1 - 1
Bin/Data/Scripts/Terrain.as

@@ -80,7 +80,7 @@ void InitUI()
     newCursor.style = uiStyle;
     newCursor.position = IntVector2(graphics.width / 2, graphics.height / 2);
     ui.cursor = newCursor;
-    if (GetPlatform() == "Android")
+    if (GetPlatform() == "Android" || GetPlatform() == "iOS")
         ui.cursor.visible = false;
 
     downloadsText = Text();

+ 1 - 1
Bin/Data/Scripts/TestScene.as

@@ -84,7 +84,7 @@ void InitUI()
     newCursor.style = uiStyle;
     newCursor.position = IntVector2(graphics.width / 2, graphics.height / 2);
     ui.cursor = newCursor;
-    if (GetPlatform() == "Android")
+    if (GetPlatform() == "Android" || GetPlatform() == "iOS")
         ui.cursor.visible = false;
 
     downloadsText = Text();

+ 1 - 1
Bin/Data/Scripts/TestSceneOld.as

@@ -293,7 +293,7 @@ void InitUI()
     cursor.style = uiStyle;
     cursor.position = IntVector2(graphics.width / 2, graphics.height / 2);
     ui.cursor = cursor;
-    if (GetPlatform() == "Android")
+    if (GetPlatform() == "Android" || GetPlatform() == "iOS")
         ui.cursor.visible = false;
 }
 

+ 7 - 1
Docs/Reference.dox

@@ -1183,6 +1183,7 @@ Urho3D implements a simple, hierarchical user interface system based on rectangu
 - ScrollBar: a slider with back and forward buttons
 - ScrollView: a scrollable view of child elements
 - Slider: a horizontal or vertical slider bar
+- Sprite: a texture image which supports subpixel positioning, scaling and rotating.
 - Text: static text that can be multiline
 - UIElement: container for other elements, renders nothing by itself
 - Window: a movable and resizable window
@@ -1224,7 +1225,6 @@ tells to instantiate a Button element, and that it should use the style "CloseBu
 
 Note that when %UI elements are serialized back to XML using \ref UI::SaveLayout "SaveLayout()" it is no longer possible to separate what was defined in the style XML file, and what in the actual layout file. Instead all attributes will be serialized.
 
-
 \section UI_Layouts UI element layout
 
 By default %UI elements operate in a "free" layout mode, where child elements' positions can be specified relative to any of the parent element corners, but they are not automatically positioned or resized.
@@ -1233,6 +1233,12 @@ To create automatically adjusting layouts, the layout mode can be switched to ei
 
 Left, top, right & bottom border widths and spacing between elements can also be specified for the layout. A grid layout is not directly supported, but it can be manually created with a horizontal layout inside a vertical layout, or vice versa.
 
+\section UI_Sprites Sprites
+
+Sprites are a special kind of %UI element that allow subpixel (float) positioning and scaling, as well as rotation, while the other elements use integer positioning for pixel-perfect display. Sprites can be used to implement rotating HUD elements such as minimaps or speedometer needles.
+
+Due to the free transformability, sprites can not be reliably queried with \ref UI::GetElementAt "GetElementAt()". Also, only other sprites should be parented to sprites, as the other elements do not support scaling and rotation.
+
 
 \page Serialization Serialization
 

+ 97 - 42
Docs/ScriptAPI.dox

@@ -3159,8 +3159,6 @@ Properties:<br>
 - int indent
 - int indentSpacing
 - int indentWidth (readonly)
-- IntVector2 rotationPivot
-- float rotation
 - IntVector2 childOffset (readonly)
 - uint[] numChildren (readonly)
 - uint numAllChildren (readonly)
@@ -3168,8 +3166,8 @@ Properties:<br>
 - UIElement@ parent (readonly)
 - UIElement@ root (readonly)
 - IntVector2 screenPosition (readonly)
-- float derivedOpacity (readonly)
 - IntRect combinedScreenRect (readonly)
+- float derivedOpacity (readonly)
 - VariantMap vars (readonly)
 
 
@@ -3267,8 +3265,6 @@ Properties:<br>
 - int indent
 - int indentSpacing
 - int indentWidth (readonly)
-- IntVector2 rotationPivot
-- float rotation
 - IntVector2 childOffset (readonly)
 - uint[] numChildren (readonly)
 - uint numAllChildren (readonly)
@@ -3276,8 +3272,8 @@ Properties:<br>
 - UIElement@ parent (readonly)
 - UIElement@ root (readonly)
 - IntVector2 screenPosition (readonly)
-- float derivedOpacity (readonly)
 - IntRect combinedScreenRect (readonly)
+- float derivedOpacity (readonly)
 - VariantMap vars (readonly)
 - Texture@ texture
 - IntRect imageRect
@@ -3287,6 +3283,89 @@ Properties:<br>
 - bool tiled
 
 
+Sprite
+
+Methods:<br>
+- void SendEvent(const String&, VariantMap& arg1 = VariantMap ( ))
+- bool Load(File@)
+- bool Save(File@)
+- bool LoadXML(const XMLElement&)
+- bool SaveXML(XMLElement&)
+- void ApplyAttributes()
+- bool SetAttribute(const String&, const Variant&)
+- Variant GetAttribute(const String&)
+- bool LoadXML(const XMLElement&, XMLFile@)
+- bool LoadXML(File@)
+- bool LoadXML(XMLFile@, XMLFile@)
+- bool SaveXML(File@)
+- void SetStyle(const XMLElement&)
+- void SetStyle(XMLFile@, const String&)
+- void SetStyleAuto(XMLFile@)
+- void SetSize(int, int)
+- void SetMinSize(int, int)
+- void SetMaxSize(int, int)
+- void SetFixedSize(int, int)
+- void SetFixedWidth(int)
+- void SetFixedHeight(int)
+- void SetAlignment(HorizontalAlignment, VerticalAlignment)
+- void BringToFront()
+- UIElement@ CreateChild(const String&, const String& arg1 = String ( ))
+- void AddChild(UIElement@)
+- void InsertChild(uint, UIElement@)
+- void RemoveChild(UIElement@, uint arg1 = 0)
+- void RemoveChild(uint)
+- void RemoveAllChildren()
+- void Remove()
+- UIElement@ GetChild(const String&, bool arg1 = false) const
+- UIElement@[]@ GetChildren(bool arg0 = false) const
+- void SetPosition(float, float)
+- void SetHotSpot(int, int)
+- void SetScale(float, float)
+- void SetScale(float)
+- void SetFullImageRect()
+
+Properties:<br>
+- ShortStringHash type (readonly)
+- String typeName (readonly)
+- int refs (readonly)
+- int weakRefs (readonly)
+- uint numAttributes (readonly)
+- Variant[] attributes
+- AttributeInfo[] attributeInfos (readonly)
+- XMLFile@ style (writeonly)
+- String name
+- IntVector2 size
+- int width
+- int height
+- HorizontalAlignment horizontalAlignment
+- VerticalAlignment verticalAlignment
+- Color color (writeonly)
+- Color[] colors
+- int priority
+- float opacity
+- bool bringToFront
+- bool bringToBack
+- bool sortChildren
+- bool useDerivedOpacity
+- bool visible
+- bool colorGradient (readonly)
+- XMLFile@ defaultStyle
+- uint[] numChildren (readonly)
+- uint numAllChildren (readonly)
+- UIElement@[] children (readonly)
+- UIElement@ parent (readonly)
+- UIElement@ root (readonly)
+- float derivedOpacity (readonly)
+- VariantMap vars (readonly)
+- Vector2 position
+- IntVector2 hotSpot
+- Vector2 scale
+- float rotation
+- Texture@ texture
+- IntRect imageRect
+- BlendMode blendMode
+
+
 Button
 
 Methods:<br>
@@ -3384,8 +3463,6 @@ Properties:<br>
 - int indent
 - int indentSpacing
 - int indentWidth (readonly)
-- IntVector2 rotationPivot
-- float rotation
 - IntVector2 childOffset (readonly)
 - uint[] numChildren (readonly)
 - uint numAllChildren (readonly)
@@ -3393,8 +3470,8 @@ Properties:<br>
 - UIElement@ parent (readonly)
 - UIElement@ root (readonly)
 - IntVector2 screenPosition (readonly)
-- float derivedOpacity (readonly)
 - IntRect combinedScreenRect (readonly)
+- float derivedOpacity (readonly)
 - VariantMap vars (readonly)
 - Texture@ texture
 - IntRect imageRect
@@ -3503,8 +3580,6 @@ Properties:<br>
 - int indent
 - int indentSpacing
 - int indentWidth (readonly)
-- IntVector2 rotationPivot
-- float rotation
 - IntVector2 childOffset (readonly)
 - uint[] numChildren (readonly)
 - uint numAllChildren (readonly)
@@ -3512,8 +3587,8 @@ Properties:<br>
 - UIElement@ parent (readonly)
 - UIElement@ root (readonly)
 - IntVector2 screenPosition (readonly)
-- float derivedOpacity (readonly)
 - IntRect combinedScreenRect (readonly)
+- float derivedOpacity (readonly)
 - VariantMap vars (readonly)
 - Texture@ texture
 - IntRect imageRect
@@ -3620,8 +3695,6 @@ Properties:<br>
 - int indent
 - int indentSpacing
 - int indentWidth (readonly)
-- IntVector2 rotationPivot
-- float rotation
 - IntVector2 childOffset (readonly)
 - uint[] numChildren (readonly)
 - uint numAllChildren (readonly)
@@ -3629,8 +3702,8 @@ Properties:<br>
 - UIElement@ parent (readonly)
 - UIElement@ root (readonly)
 - IntVector2 screenPosition (readonly)
-- float derivedOpacity (readonly)
 - IntRect combinedScreenRect (readonly)
+- float derivedOpacity (readonly)
 - VariantMap vars (readonly)
 - Texture@ texture
 - IntRect imageRect
@@ -3736,8 +3809,6 @@ Properties:<br>
 - int indent
 - int indentSpacing
 - int indentWidth (readonly)
-- IntVector2 rotationPivot
-- float rotation
 - IntVector2 childOffset (readonly)
 - uint[] numChildren (readonly)
 - uint numAllChildren (readonly)
@@ -3745,8 +3816,8 @@ Properties:<br>
 - UIElement@ parent (readonly)
 - UIElement@ root (readonly)
 - IntVector2 screenPosition (readonly)
-- float derivedOpacity (readonly)
 - IntRect combinedScreenRect (readonly)
+- float derivedOpacity (readonly)
 - VariantMap vars (readonly)
 - Texture@ texture
 - IntRect imageRect
@@ -3856,8 +3927,6 @@ Properties:<br>
 - int indent
 - int indentSpacing
 - int indentWidth (readonly)
-- IntVector2 rotationPivot
-- float rotation
 - IntVector2 childOffset (readonly)
 - uint[] numChildren (readonly)
 - uint numAllChildren (readonly)
@@ -3865,8 +3934,8 @@ Properties:<br>
 - UIElement@ parent (readonly)
 - UIElement@ root (readonly)
 - IntVector2 screenPosition (readonly)
-- float derivedOpacity (readonly)
 - IntRect combinedScreenRect (readonly)
+- float derivedOpacity (readonly)
 - VariantMap vars (readonly)
 - Orientation orientation
 - float range
@@ -3973,8 +4042,6 @@ Properties:<br>
 - int indent
 - int indentSpacing
 - int indentWidth (readonly)
-- IntVector2 rotationPivot
-- float rotation
 - IntVector2 childOffset (readonly)
 - uint[] numChildren (readonly)
 - uint numAllChildren (readonly)
@@ -3982,8 +4049,8 @@ Properties:<br>
 - UIElement@ parent (readonly)
 - UIElement@ root (readonly)
 - IntVector2 screenPosition (readonly)
-- float derivedOpacity (readonly)
 - IntRect combinedScreenRect (readonly)
+- float derivedOpacity (readonly)
 - VariantMap vars (readonly)
 - UIElement@ contentElement
 - IntVector2 viewPosition
@@ -4106,8 +4173,6 @@ Properties:<br>
 - int indent
 - int indentSpacing
 - int indentWidth (readonly)
-- IntVector2 rotationPivot
-- float rotation
 - IntVector2 childOffset (readonly)
 - uint[] numChildren (readonly)
 - uint numAllChildren (readonly)
@@ -4115,8 +4180,8 @@ Properties:<br>
 - UIElement@ parent (readonly)
 - UIElement@ root (readonly)
 - IntVector2 screenPosition (readonly)
-- float derivedOpacity (readonly)
 - IntRect combinedScreenRect (readonly)
+- float derivedOpacity (readonly)
 - VariantMap vars (readonly)
 - IntVector2 viewPosition
 - UIElement@ contentElement (readonly)
@@ -4235,8 +4300,6 @@ Properties:<br>
 - int indent
 - int indentSpacing
 - int indentWidth (readonly)
-- IntVector2 rotationPivot
-- float rotation
 - IntVector2 childOffset (readonly)
 - uint[] numChildren (readonly)
 - uint numAllChildren (readonly)
@@ -4244,8 +4307,8 @@ Properties:<br>
 - UIElement@ parent (readonly)
 - UIElement@ root (readonly)
 - IntVector2 screenPosition (readonly)
-- float derivedOpacity (readonly)
 - IntRect combinedScreenRect (readonly)
+- float derivedOpacity (readonly)
 - VariantMap vars (readonly)
 - Font@ font (readonly)
 - int fontSize (readonly)
@@ -4355,8 +4418,6 @@ Properties:<br>
 - int indent
 - int indentSpacing
 - int indentWidth (readonly)
-- IntVector2 rotationPivot
-- float rotation
 - IntVector2 childOffset (readonly)
 - uint[] numChildren (readonly)
 - uint numAllChildren (readonly)
@@ -4364,8 +4425,8 @@ Properties:<br>
 - UIElement@ parent (readonly)
 - UIElement@ root (readonly)
 - IntVector2 screenPosition (readonly)
-- float derivedOpacity (readonly)
 - IntRect combinedScreenRect (readonly)
+- float derivedOpacity (readonly)
 - VariantMap vars (readonly)
 - Texture@ texture
 - IntRect imageRect
@@ -4485,8 +4546,6 @@ Properties:<br>
 - int indent
 - int indentSpacing
 - int indentWidth (readonly)
-- IntVector2 rotationPivot
-- float rotation
 - IntVector2 childOffset (readonly)
 - uint[] numChildren (readonly)
 - uint numAllChildren (readonly)
@@ -4494,8 +4553,8 @@ Properties:<br>
 - UIElement@ parent (readonly)
 - UIElement@ root (readonly)
 - IntVector2 screenPosition (readonly)
-- float derivedOpacity (readonly)
 - IntRect combinedScreenRect (readonly)
+- float derivedOpacity (readonly)
 - VariantMap vars (readonly)
 - Texture@ texture
 - IntRect imageRect
@@ -4619,8 +4678,6 @@ Properties:<br>
 - int indent
 - int indentSpacing
 - int indentWidth (readonly)
-- IntVector2 rotationPivot
-- float rotation
 - IntVector2 childOffset (readonly)
 - uint[] numChildren (readonly)
 - uint numAllChildren (readonly)
@@ -4628,8 +4685,8 @@ Properties:<br>
 - UIElement@ parent (readonly)
 - UIElement@ root (readonly)
 - IntVector2 screenPosition (readonly)
-- float derivedOpacity (readonly)
 - IntRect combinedScreenRect (readonly)
+- float derivedOpacity (readonly)
 - VariantMap vars (readonly)
 - Texture@ texture
 - IntRect imageRect
@@ -4747,8 +4804,6 @@ Properties:<br>
 - int indent
 - int indentSpacing
 - int indentWidth (readonly)
-- IntVector2 rotationPivot
-- float rotation
 - IntVector2 childOffset (readonly)
 - uint[] numChildren (readonly)
 - uint numAllChildren (readonly)
@@ -4756,8 +4811,8 @@ Properties:<br>
 - UIElement@ parent (readonly)
 - UIElement@ root (readonly)
 - IntVector2 screenPosition (readonly)
-- float derivedOpacity (readonly)
 - IntRect combinedScreenRect (readonly)
+- float derivedOpacity (readonly)
 - VariantMap vars (readonly)
 - Texture@ texture
 - IntRect imageRect

+ 85 - 57
Engine/Engine/APITemplates.h

@@ -813,7 +813,7 @@ static VariantMap& UIElementGetVars(UIElement* ptr)
 #endif
 
 /// Template function for registering a class derived from UIElement.
-template <class T> void RegisterUIElement(asIScriptEngine* engine, const char* className)
+template <class T> void RegisterUIElement(asIScriptEngine* engine, const char* className, bool isSprite = false)
 {
     RegisterSerializable<T>(engine, className);
     RegisterObjectConstructor<T>(engine, className);
@@ -826,7 +826,8 @@ template <class T> void RegisterUIElement(asIScriptEngine* engine, const char* c
     engine->RegisterObjectMethod(className, "void SetStyle(const XMLElement&in)", asMETHODPR(T, SetStyle, (const XMLElement&), void), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void SetStyle(XMLFile@+, const String&in)", asMETHODPR(T, SetStyle, (XMLFile*, const String&), void), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void SetStyleAuto(XMLFile@+)", asMETHODPR(T, SetStyleAuto, (XMLFile*), void), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "void SetPosition(int, int)", asMETHODPR(T, SetPosition, (int, int), void), asCALL_THISCALL);
+    if (!isSprite)
+        engine->RegisterObjectMethod(className, "void SetPosition(int, int)", asMETHODPR(UIElement, SetPosition, (int, int), void), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void SetSize(int, int)", asMETHODPR(T, SetSize, (int, int), void), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void SetMinSize(int, int)", asMETHODPR(T, SetMinSize, (int, int), void), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void SetMaxSize(int, int)", asMETHODPR(T, SetMaxSize, (int, int), void), asCALL_THISCALL);
@@ -834,10 +835,13 @@ template <class T> void RegisterUIElement(asIScriptEngine* engine, const char* c
     engine->RegisterObjectMethod(className, "void SetFixedWidth(int)", asMETHOD(T, SetFixedWidth), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void SetFixedHeight(int)", asMETHOD(T, SetFixedHeight), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void SetAlignment(HorizontalAlignment, VerticalAlignment)", asMETHOD(T, SetAlignment), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "void SetLayout(LayoutMode, int spacing = 0, const IntRect& border = IntRect(0, 0, 0, 0))", asMETHOD(T, SetLayout), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "void UpdateLayout()", asMETHOD(T, UpdateLayout), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "void DisableLayoutUpdate()", asMETHOD(T, DisableLayoutUpdate), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "void EnableLayoutUpdate()", asMETHOD(T, EnableLayoutUpdate), asCALL_THISCALL);
+    if (!isSprite)
+    {
+        engine->RegisterObjectMethod(className, "void SetLayout(LayoutMode, int spacing = 0, const IntRect& border = IntRect(0, 0, 0, 0))", asMETHOD(T, SetLayout), asCALL_THISCALL);
+        engine->RegisterObjectMethod(className, "void UpdateLayout()", asMETHOD(T, UpdateLayout), asCALL_THISCALL);
+        engine->RegisterObjectMethod(className, "void DisableLayoutUpdate()", asMETHOD(T, DisableLayoutUpdate), asCALL_THISCALL);
+        engine->RegisterObjectMethod(className, "void EnableLayoutUpdate()", asMETHOD(T, EnableLayoutUpdate), asCALL_THISCALL);
+    }
     engine->RegisterObjectMethod(className, "void BringToFront()", asMETHOD(T, BringToFront), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "UIElement@+ CreateChild(const String&in, const String&in name = String())", asFUNCTION(UIElementCreateChild), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod(className, "void AddChild(UIElement@+)", asMETHOD(T, AddChild), asCALL_THISCALL);
@@ -848,39 +852,51 @@ template <class T> void RegisterUIElement(asIScriptEngine* engine, const char* c
     engine->RegisterObjectMethod(className, "void Remove()", asMETHOD(T, Remove), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "UIElement@+ GetChild(const String&in, bool recursive = false) const", asMETHODPR(T, GetChild, (const String&, bool) const, UIElement*), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "Array<UIElement@>@ GetChildren(bool recursive = false) const", asFUNCTION(UIElementGetChildren), asCALL_CDECL_OBJLAST);
-    engine->RegisterObjectMethod(className, "IntVector2 ScreenToElement(const IntVector2&in)", asMETHOD(T, ScreenToElement), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "IntVector2 ElementToScreen(const IntVector2&in)", asMETHOD(T, ElementToScreen), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "bool IsInside(IntVector2, bool)", asMETHOD(T, IsInside), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "bool IsInsideCombined(IntVector2, bool)", asMETHOD(T, IsInsideCombined), asCALL_THISCALL);
+    if (!isSprite)
+    {
+        engine->RegisterObjectMethod(className, "IntVector2 ScreenToElement(const IntVector2&in)", asMETHOD(T, ScreenToElement), asCALL_THISCALL);
+        engine->RegisterObjectMethod(className, "IntVector2 ElementToScreen(const IntVector2&in)", asMETHOD(T, ElementToScreen), asCALL_THISCALL);
+        engine->RegisterObjectMethod(className, "bool IsInside(IntVector2, bool)", asMETHOD(T, IsInside), asCALL_THISCALL);
+        engine->RegisterObjectMethod(className, "bool IsInsideCombined(IntVector2, bool)", asMETHOD(T, IsInsideCombined), asCALL_THISCALL);
+    }
     engine->RegisterObjectMethod(className, "void set_style(XMLFile@+)", asMETHODPR(T, SetStyleAuto, (XMLFile*), void), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void set_name(const String&in)", asMETHOD(T, SetName), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "const String& get_name() const", asMETHOD(T, GetName), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "void set_position(const IntVector2&in)", asMETHODPR(T, SetPosition, (const IntVector2&), void), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "const IntVector2& get_position() const", asMETHOD(T, GetPosition), asCALL_THISCALL);
+    if (!isSprite)
+    {
+        engine->RegisterObjectMethod(className, "void set_position(const IntVector2&in)", asMETHODPR(UIElement, SetPosition, (const IntVector2&), void), asCALL_THISCALL);
+        engine->RegisterObjectMethod(className, "const IntVector2& get_position() const", asMETHOD(T, GetPosition), asCALL_THISCALL);
+    }
     engine->RegisterObjectMethod(className, "void set_size(const IntVector2&in)", asMETHODPR(T, SetSize, (const IntVector2&), void), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "const IntVector2& get_size() const", asMETHOD(T, GetSize), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void set_width(int)", asMETHOD(T, SetWidth), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "int get_width() const", asMETHOD(T, GetWidth), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void set_height(int)", asMETHOD(T, SetHeight), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "int get_height() const", asMETHOD(T, GetHeight), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "void set_minSize(const IntVector2&in)", asMETHODPR(T, SetMinSize, (const IntVector2&), void), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "const IntVector2& get_minSize() const", asMETHOD(T, GetMinSize), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "void set_minWidth(int)", asMETHOD(T, SetMinWidth), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "int get_minWidth() const", asMETHOD(T, GetMinWidth), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "void set_minHeight(int)", asMETHOD(T, SetMinHeight), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "int get_minHeight() const", asMETHOD(T, GetMinHeight), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "void set_maxSize(const IntVector2&in)", asMETHODPR(T, SetMaxSize, (const IntVector2&), void), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "const IntVector2& get_maxSize() const", asMETHOD(T, GetMaxSize), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "void set_maxWidth(int)", asMETHOD(T, SetMaxWidth), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "int get_maxWidth() const", asMETHOD(T, GetMaxWidth), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "void set_maxHeight(int)", asMETHOD(T, SetMaxHeight), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "int get_maxHeight() const", asMETHOD(T, GetMaxHeight), asCALL_THISCALL);
+    if (!isSprite)
+    {
+        engine->RegisterObjectMethod(className, "void set_minSize(const IntVector2&in)", asMETHODPR(T, SetMinSize, (const IntVector2&), void), asCALL_THISCALL);
+        engine->RegisterObjectMethod(className, "const IntVector2& get_minSize() const", asMETHOD(T, GetMinSize), asCALL_THISCALL);
+        engine->RegisterObjectMethod(className, "void set_minWidth(int)", asMETHOD(T, SetMinWidth), asCALL_THISCALL);
+        engine->RegisterObjectMethod(className, "int get_minWidth() const", asMETHOD(T, GetMinWidth), asCALL_THISCALL);
+        engine->RegisterObjectMethod(className, "void set_minHeight(int)", asMETHOD(T, SetMinHeight), asCALL_THISCALL);
+        engine->RegisterObjectMethod(className, "int get_minHeight() const", asMETHOD(T, GetMinHeight), asCALL_THISCALL);
+        engine->RegisterObjectMethod(className, "void set_maxSize(const IntVector2&in)", asMETHODPR(T, SetMaxSize, (const IntVector2&), void), asCALL_THISCALL);
+        engine->RegisterObjectMethod(className, "const IntVector2& get_maxSize() const", asMETHOD(T, GetMaxSize), asCALL_THISCALL);
+        engine->RegisterObjectMethod(className, "void set_maxWidth(int)", asMETHOD(T, SetMaxWidth), asCALL_THISCALL);
+        engine->RegisterObjectMethod(className, "int get_maxWidth() const", asMETHOD(T, GetMaxWidth), asCALL_THISCALL);
+        engine->RegisterObjectMethod(className, "void set_maxHeight(int)", asMETHOD(T, SetMaxHeight), asCALL_THISCALL);
+        engine->RegisterObjectMethod(className, "int get_maxHeight() const", asMETHOD(T, GetMaxHeight), asCALL_THISCALL);
+    }
     engine->RegisterObjectMethod(className, "void set_horizontalAlignment(HorizontalAlignment)", asMETHOD(T, SetHorizontalAlignment), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "HorizontalAlignment get_horizontalAlignment() const", asMETHOD(T, GetHorizontalAlignment), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void set_verticalAlignment(VerticalAlignment)", asMETHOD(T, SetVerticalAlignment), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "VerticalAlignment get_verticalAlignment() const", asMETHOD(T, GetVerticalAlignment), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "void set_clipBorder(const IntRect&in)", asMETHODPR(T, SetClipBorder, (const IntRect&), void), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "const IntRect& get_clipBorder() const", asMETHOD(T, GetClipBorder), asCALL_THISCALL);
+    if (!isSprite)
+    {
+        engine->RegisterObjectMethod(className, "void set_clipBorder(const IntRect&in)", asMETHODPR(T, SetClipBorder, (const IntRect&), void), asCALL_THISCALL);
+        engine->RegisterObjectMethod(className, "const IntRect& get_clipBorder() const", asMETHOD(T, GetClipBorder), asCALL_THISCALL);
+    }
     engine->RegisterObjectMethod(className, "void set_color(const Color&in)", asMETHODPR(T, SetColor, (const Color&), void), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void set_colors(Corner, const Color&in)", asMETHODPR(T, SetColor, (Corner, const Color&), void), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "const Color& get_colors(Corner) const", asMETHOD(T, GetColor), asCALL_THISCALL);
@@ -892,53 +908,65 @@ template <class T> void RegisterUIElement(asIScriptEngine* engine, const char* c
     engine->RegisterObjectMethod(className, "bool get_bringToFront() const", asMETHOD(T, SetBringToFront), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void set_bringToBack(bool)", asMETHOD(T, SetBringToBack), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "bool get_bringToBack() const", asMETHOD(T, GetBringToBack), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "void set_clipChildren(bool)", asMETHOD(T, SetClipChildren), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "bool get_clipChildren() const", asMETHOD(T, GetClipChildren), asCALL_THISCALL);
+    if (!isSprite)
+    {
+        engine->RegisterObjectMethod(className, "void set_clipChildren(bool)", asMETHOD(T, SetClipChildren), asCALL_THISCALL);
+        engine->RegisterObjectMethod(className, "bool get_clipChildren() const", asMETHOD(T, GetClipChildren), asCALL_THISCALL);
+    }
     engine->RegisterObjectMethod(className, "void set_sortChildren(bool)", asMETHOD(T, SetSortChildren), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "bool get_sortChildren() const", asMETHOD(T, GetSortChildren), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void set_useDerivedOpacity(bool)", asMETHOD(T, SetUseDerivedOpacity), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "bool get_useDerivedOpacity() const", asMETHOD(T, GetUseDerivedOpacity), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "void set_active(bool)", asMETHOD(T, SetActive), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "bool get_active() const", asMETHOD(T, IsActive), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "void set_focus(bool)", asMETHOD(T, SetFocus), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "bool get_focus() const", asMETHOD(T, HasFocus), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "void set_selected(bool)", asMETHOD(T, SetSelected), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "bool get_selected() const", asMETHOD(T, IsSelected), asCALL_THISCALL);
+    if (!isSprite)
+    {
+        engine->RegisterObjectMethod(className, "void set_active(bool)", asMETHOD(T, SetActive), asCALL_THISCALL);
+        engine->RegisterObjectMethod(className, "bool get_active() const", asMETHOD(T, IsActive), asCALL_THISCALL);
+        engine->RegisterObjectMethod(className, "void set_focus(bool)", asMETHOD(T, SetFocus), asCALL_THISCALL);
+        engine->RegisterObjectMethod(className, "bool get_focus() const", asMETHOD(T, HasFocus), asCALL_THISCALL);
+        engine->RegisterObjectMethod(className, "void set_selected(bool)", asMETHOD(T, SetSelected), asCALL_THISCALL);
+        engine->RegisterObjectMethod(className, "bool get_selected() const", asMETHOD(T, IsSelected), asCALL_THISCALL);
+    }
     engine->RegisterObjectMethod(className, "void set_visible(bool)", asMETHOD(T, SetVisible), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "bool get_visible() const", asMETHOD(T, IsVisible), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "bool get_hovering() const", asMETHOD(T, IsHovering), asCALL_THISCALL);
+    if (!isSprite)
+        engine->RegisterObjectMethod(className, "bool get_hovering() const", asMETHOD(T, IsHovering), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "bool get_colorGradient() const", asMETHOD(T, HasColorGradient), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "void set_focusMode(FocusMode)", asMETHOD(T, SetFocusMode), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "FocusMode get_focusMode() const", asMETHOD(T, GetFocusMode), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "void set_dragDropMode(uint)", asMETHOD(T, SetDragDropMode), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "uint get_dragDropMode() const", asMETHOD(T, GetDragDropMode), asCALL_THISCALL);
+    if (!isSprite)
+    {
+        engine->RegisterObjectMethod(className, "void set_focusMode(FocusMode)", asMETHOD(T, SetFocusMode), asCALL_THISCALL);
+        engine->RegisterObjectMethod(className, "FocusMode get_focusMode() const", asMETHOD(T, GetFocusMode), asCALL_THISCALL);
+        engine->RegisterObjectMethod(className, "void set_dragDropMode(uint)", asMETHOD(T, SetDragDropMode), asCALL_THISCALL);
+        engine->RegisterObjectMethod(className, "uint get_dragDropMode() const", asMETHOD(T, GetDragDropMode), asCALL_THISCALL);
+    }
     engine->RegisterObjectMethod(className, "void set_defaultStyle(XMLFile@+)", asMETHOD(T, SetDefaultStyle), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "XMLFile@+ get_defaultStyle()", asMETHOD(T, GetDefaultStyle), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "void set_layoutMode(LayoutMode)", asMETHOD(T, SetLayoutMode), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "LayoutMode get_layoutMode() const", asMETHOD(T, GetLayoutMode), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "void set_layoutSpacing(int)", asMETHOD(T, SetLayoutSpacing), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "int get_layoutSpacing() const", asMETHOD(T, GetLayoutSpacing), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "void set_layoutBorder(const IntRect&)", asMETHOD(T, SetLayoutBorder), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "const IntRect& get_layoutBorder() const", asMETHOD(T, GetLayoutBorder), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "void set_indent(int)", asMETHOD(T, SetIndent), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "int get_indent() const", asMETHOD(T, GetIndent), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "void set_indentSpacing(int)", asMETHOD(T, SetIndentSpacing), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "int get_indentSpacing() const", asMETHOD(T, GetIndentSpacing), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "int get_indentWidth() const", asMETHOD(T, GetIndentWidth), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "void set_rotationPivot(const IntVector2&)", asMETHOD(T, SetRotationPivot), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "const IntVector2& get_rotationPivot() const", asMETHOD(T, GetRotationPivot), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "void set_rotation(float)", asMETHOD(T, SetRotation), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "float get_rotation() const", asMETHOD(T, GetRotation), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "const IntVector2& get_childOffset() const", asMETHOD(T, GetChildOffset), asCALL_THISCALL);
+    if (!isSprite)
+    {
+        engine->RegisterObjectMethod(className, "void set_layoutMode(LayoutMode)", asMETHOD(T, SetLayoutMode), asCALL_THISCALL);
+        engine->RegisterObjectMethod(className, "LayoutMode get_layoutMode() const", asMETHOD(T, GetLayoutMode), asCALL_THISCALL);
+        engine->RegisterObjectMethod(className, "void set_layoutSpacing(int)", asMETHOD(T, SetLayoutSpacing), asCALL_THISCALL);
+        engine->RegisterObjectMethod(className, "int get_layoutSpacing() const", asMETHOD(T, GetLayoutSpacing), asCALL_THISCALL);
+        engine->RegisterObjectMethod(className, "void set_layoutBorder(const IntRect&)", asMETHOD(T, SetLayoutBorder), asCALL_THISCALL);
+        engine->RegisterObjectMethod(className, "const IntRect& get_layoutBorder() const", asMETHOD(T, GetLayoutBorder), asCALL_THISCALL);
+        engine->RegisterObjectMethod(className, "void set_indent(int)", asMETHOD(T, SetIndent), asCALL_THISCALL);
+        engine->RegisterObjectMethod(className, "int get_indent() const", asMETHOD(T, GetIndent), asCALL_THISCALL);
+        engine->RegisterObjectMethod(className, "void set_indentSpacing(int)", asMETHOD(T, SetIndentSpacing), asCALL_THISCALL);
+        engine->RegisterObjectMethod(className, "int get_indentSpacing() const", asMETHOD(T, GetIndentSpacing), asCALL_THISCALL);
+        engine->RegisterObjectMethod(className, "int get_indentWidth() const", asMETHOD(T, GetIndentWidth), asCALL_THISCALL);
+        engine->RegisterObjectMethod(className, "const IntVector2& get_childOffset() const", asMETHOD(T, GetChildOffset), asCALL_THISCALL);
+    }
     engine->RegisterObjectMethod(className, "uint get_numChildren() const", asFUNCTION(UIElementGetNumChildrenNonRecursive), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod(className, "uint get_numAllChildren() const", asFUNCTION(UIElementGetNumChildrenRecursive), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod(className, "uint get_numChildren(bool) const", asMETHOD(T, GetNumChildren), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "UIElement@+ get_children(uint) const", asMETHODPR(T, GetChild, (unsigned) const, UIElement*), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "UIElement@+ get_parent() const", asMETHOD(T, GetParent), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "UIElement@+ get_root() const", asMETHOD(T, GetRoot), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "IntVector2 get_screenPosition()", asMETHOD(T, GetScreenPosition), asCALL_THISCALL);
+    if (!isSprite)
+    {
+        engine->RegisterObjectMethod(className, "const IntVector2& get_screenPosition()", asMETHOD(T, GetScreenPosition), asCALL_THISCALL);
+        engine->RegisterObjectMethod(className, "IntRect get_combinedScreenRect()", asMETHOD(T, GetCombinedScreenRect), asCALL_THISCALL);
+    }
     engine->RegisterObjectMethod(className, "float get_derivedOpacity()", asMETHOD(T, GetDerivedOpacity), asCALL_THISCALL);
-    engine->RegisterObjectMethod(className, "IntRect get_combinedScreenRect()", asMETHOD(T, GetCombinedScreenRect), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "VariantMap& get_vars()", asFUNCTION(UIElementGetVars), asCALL_CDECL_OBJLAST);
 }
 

+ 26 - 0
Engine/Engine/UIAPI.cpp

@@ -31,6 +31,7 @@
 #include "ListView.h"
 #include "ScrollBar.h"
 #include "Slider.h"
+#include "Sprite.h"
 #include "Text.h"
 #include "UI.h"
 #include "Window.h"
@@ -103,6 +104,30 @@ static void RegisterBorderImage(asIScriptEngine* engine)
     RegisterBorderImage<BorderImage>(engine, "BorderImage");
 }
 
+static void RegisterSprite(asIScriptEngine* engine)
+{
+    RegisterUIElement<Sprite>(engine, "Sprite", true);
+    engine->RegisterObjectMethod("Sprite", "void SetPosition(float, float)", asMETHODPR(Sprite, SetPosition, (float, float), void), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Sprite", "void SetHotSpot(int, int)", asMETHODPR(Sprite, SetHotSpot, (int, int), void), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Sprite", "void SetScale(float, float)", asMETHODPR(Sprite, SetScale, (float, float), void), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Sprite", "void SetScale(float)", asMETHODPR(Sprite, SetScale, (float), void), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Sprite", "void SetFullImageRect()", asMETHOD(Sprite, SetFullImageRect), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Sprite", "void set_position(const Vector2&)", asMETHODPR(Sprite, SetPosition, (const Vector2&), void), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Sprite", "const Vector2& get_position() const", asMETHODPR(Sprite, GetPosition, () const, const Vector2&), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Sprite", "void set_hotSpot(const IntVector2&)", asMETHODPR(Sprite, SetHotSpot, (const IntVector2&), void), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Sprite", "const IntVector2& get_hotSpot() const", asMETHOD(Sprite, GetHotSpot), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Sprite", "void set_scale(const Vector2&)", asMETHODPR(Sprite, SetScale, (const Vector2&), void), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Sprite", "const Vector2& get_scale() const", asMETHOD(Sprite, GetScale), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Sprite", "void set_rotation(float)", asMETHOD(Sprite, SetRotation), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Sprite", "float get_rotation() const", asMETHOD(Sprite, GetRotation), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Sprite", "void set_texture(Texture@+)", asMETHOD(Sprite, SetTexture), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Sprite", "Texture@+ get_texture() const", asMETHOD(Sprite, GetTexture), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Sprite", "void set_imageRect(const IntRect&in)", asMETHODPR(Sprite, SetImageRect, (const IntRect&), void), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Sprite", "const IntRect& get_imageRect() const", asMETHOD(Sprite, GetImageRect), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Sprite", "void set_blendMode(BlendMode)", asMETHOD(Sprite, SetBlendMode), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Sprite", "BlendMode get_blendMode() const", asMETHOD(Sprite, GetBlendMode), asCALL_THISCALL);
+}
+
 static void RegisterCursor(asIScriptEngine* engine)
 {
     engine->RegisterEnum("CursorShape");
@@ -516,6 +541,7 @@ void RegisterUIAPI(asIScriptEngine* engine)
     RegisterFont(engine);
     RegisterUIElement(engine);
     RegisterBorderImage(engine);
+    RegisterSprite(engine);
     RegisterButton(engine);
     RegisterCheckBox(engine);
     RegisterCursor(engine);

+ 3 - 3
Engine/Graphics/Technique.cpp

@@ -35,7 +35,7 @@
 namespace Urho3D
 {
 
-static const String blendModeNames[] =
+const char* blendModeNames[] =
 {
     "replace",
     "add",
@@ -44,7 +44,7 @@ static const String blendModeNames[] =
     "addalpha",
     "premulalpha",
     "invdestalpha",
-    ""
+    0
 };
 
 static const String compareModeNames[] =
@@ -187,7 +187,7 @@ bool Technique::Load(Deserializer& source)
             if (passElem.HasAttribute("blend"))
             {
                 String blend = passElem.GetAttributeLower("blend");
-                newPass->SetBlendMode((BlendMode)GetStringListIndex(blend, blendModeNames, BLEND_REPLACE));
+                newPass->SetBlendMode((BlendMode)GetStringListIndex(blend.CString(), blendModeNames, BLEND_REPLACE));
             }
             
             if (passElem.HasAttribute("depthtest"))

+ 15 - 25
Engine/UI/BorderImage.cpp

@@ -31,17 +31,7 @@
 namespace Urho3D
 {
 
-static const char* blendModes[] =
-{
-    "Replace",
-    "Add",
-    "Multiply",
-    "Alpha",
-    "AddAlpha",
-    "PreMultiply",
-    "InvDestAlpha",
-    0
-};
+extern const char* blendModeNames[];
 
 template<> BlendMode Variant::Get<BlendMode>() const
 {
@@ -73,21 +63,10 @@ void BorderImage::RegisterObject(Context* context)
     REF_ACCESSOR_ATTRIBUTE(BorderImage, VAR_INTRECT, "Border", GetBorder, SetBorder, IntRect, IntRect::ZERO, AM_FILE);
     REF_ACCESSOR_ATTRIBUTE(BorderImage, VAR_INTVECTOR2, "Hover Image Offset", GetHoverOffset, SetHoverOffset, IntVector2, IntVector2::ZERO, AM_FILE);
     ACCESSOR_ATTRIBUTE(BorderImage, VAR_BOOL, "Tiled", IsTiled, SetTiled, bool, true, AM_FILE);
-    ENUM_ACCESSOR_ATTRIBUTE(BorderImage, "Blend Mode", GetBlendMode, SetBlendMode, BlendMode, blendModes, 0, AM_FILE);
+    ENUM_ACCESSOR_ATTRIBUTE(BorderImage, "Blend Mode", GetBlendMode, SetBlendMode, BlendMode, blendModeNames, 0, AM_FILE);
     COPY_BASE_ATTRIBUTES(BorderImage, UIElement);
 }
 
-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)
 {
     GetBatches(batches, quads, currentScissor, hovering_ || selected_ ? hoverOffset_ : IntVector2::ZERO);
@@ -146,8 +125,8 @@ void BorderImage::GetBatches(PODVector<UIBatch>& batches, PODVector<UIQuad>& qua
     if (GetDerivedOpacity() < 1.0f || color_[C_TOPLEFT].a_ < 1.0f || color_[C_TOPRIGHT].a_ < 1.0f ||
         color_[C_BOTTOMLEFT].a_ < 1.0f || color_[C_BOTTOMRIGHT].a_ < 1.0f)
         allOpaque = false;
-        
-    UIBatch batch(GetBatchTransform(), blendMode_ == BLEND_REPLACE && !allOpaque ? BLEND_ALPHA : blendMode_, currentScissor, texture_, &quads);
+    
+    UIBatch batch(blendMode_ == BLEND_REPLACE && !allOpaque ? BLEND_ALPHA : blendMode_, currentScissor, texture_, &quads);
     
     // Calculate size of the inner rect, and texture dimensions of the inner rect
     int x = GetIndentWidth();
@@ -211,4 +190,15 @@ void BorderImage::GetBatches(PODVector<UIBatch>& batches, PODVector<UIQuad>& qua
     hovering_ = false;
 }
 
+void BorderImage::SetTextureAttr(ResourceRef value)
+{
+    ResourceCache* cache = GetSubsystem<ResourceCache>();
+    SetTexture(cache->GetResource<Texture2D>(value.id_));
+}
+
+ResourceRef BorderImage::GetTextureAttr() const
+{
+    return GetResourceRef(texture_, Texture2D::GetTypeStatic());
+}
+
 }

+ 1 - 1
Engine/UI/BorderImage.h

@@ -47,7 +47,7 @@ public:
     /// Return UI rendering batches.
     virtual void GetBatches(PODVector<UIBatch>& batches, PODVector<UIQuad>& quads, const IntRect& currentScissor);
     
-    /// Set textures.
+    /// Set texture.
     void SetTexture(Texture* texture);
     /// Set part of texture to use as the image.
     void SetImageRect(const IntRect& rect);

+ 5 - 4
Engine/UI/Cursor.cpp

@@ -208,14 +208,15 @@ void Cursor::GetBatches(PODVector<UIBatch>& batches, PODVector<UIQuad>& quads, c
 {
     unsigned initialSize = quads.Size();
     const IntVector2& offset = shapeInfos_[shape_].hotSpot_;
+    Vector2 floatOffset((float)offset.x_, (float)offset.y_);
     
     BorderImage::GetBatches(batches, quads, currentScissor);
     for (unsigned i = initialSize; i < quads.Size(); ++i)
     {
-        quads[i].left_ -= offset.x_;
-        quads[i].top_ -= offset.y_;
-        quads[i].right_ -= offset.x_;
-        quads[i].bottom_ -= offset.y_;
+        quads[i].topLeft_ -= floatOffset;
+        quads[i].topRight_ -= floatOffset;
+        quads[i].bottomLeft_ -= floatOffset;
+        quads[i].bottomRight_ -= floatOffset;
     }
 }
 

+ 1 - 1
Engine/UI/ListView.cpp

@@ -728,7 +728,7 @@ void ListView::Expand(unsigned index, bool enable, bool recursive)
         bool visible = enable && expanded[indent - 1];
         item->SetVisible(visible);
 
-        if (indent >= expanded.Size())
+        if (indent >= (int)expanded.Size())
             expanded.Resize(indent + 1);
         expanded[indent] = visible && GetItemExpanded(item);
     }

+ 270 - 0
Engine/UI/Sprite.cpp

@@ -0,0 +1,270 @@
+//
+// Copyright (c) 2008-2013 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#include "Precompiled.h"
+#include "Context.h"
+#include "ResourceCache.h"
+#include "Sprite.h"
+#include "Texture2D.h"
+
+#include "DebugNew.h"
+
+namespace Urho3D
+{
+
+extern const char* blendModeNames[];
+extern const char* horizontalAlignments[];
+extern const char* verticalAlignments[];
+
+OBJECTTYPESTATIC(Sprite);
+
+Sprite::Sprite(Context* context) :
+    UIElement(context),
+    floatPosition_(Vector2::ZERO),
+    hotSpot_(IntVector2::ZERO),
+    scale_(Vector2::ONE),
+    rotation_(0.0f),
+    imageRect_(IntRect::ZERO),
+    blendMode_(BLEND_REPLACE)
+{
+}
+
+Sprite::~Sprite()
+{
+}
+
+void Sprite::RegisterObject(Context* context)
+{
+    context->RegisterFactory<Sprite>();
+    
+    ACCESSOR_ATTRIBUTE(Sprite, VAR_RESOURCEREF, "Texture", GetTextureAttr, SetTextureAttr, ResourceRef, ResourceRef(Texture2D::GetTypeStatic()), AM_FILE);
+    ENUM_ACCESSOR_ATTRIBUTE(Sprite, "Blend Mode", GetBlendMode, SetBlendMode, BlendMode, blendModeNames, 0, AM_FILE);
+    REF_ACCESSOR_ATTRIBUTE(Sprite, VAR_STRING, "Name", GetName, SetName, String, String::EMPTY, AM_FILE);
+    REF_ACCESSOR_ATTRIBUTE(Sprite, VAR_VECTOR2, "Position", GetPosition, SetPosition, Vector2, Vector2::ZERO, AM_FILE);
+    REF_ACCESSOR_ATTRIBUTE(Sprite, VAR_INTVECTOR2, "Size", GetSize, SetSize, IntVector2, IntVector2::ZERO, AM_FILE);
+    REF_ACCESSOR_ATTRIBUTE(Sprite, VAR_INTVECTOR2, "Hotspot", GetHotSpot, SetHotSpot, IntVector2, IntVector2::ZERO, AM_FILE);
+    REF_ACCESSOR_ATTRIBUTE(Sprite, VAR_VECTOR2, "Scale", GetScale, SetScale, Vector2, Vector2::ZERO, AM_FILE);
+    ACCESSOR_ATTRIBUTE(Sprite, VAR_FLOAT, "Rotation", GetRotation, SetRotation, float, 0.0f, AM_FILE);
+    ENUM_ACCESSOR_ATTRIBUTE(Sprite, "Horiz Alignment", GetHorizontalAlignment, SetHorizontalAlignment, HorizontalAlignment, horizontalAlignments, HA_LEFT, AM_FILE);
+    ENUM_ACCESSOR_ATTRIBUTE(Sprite, "Vert Alignment", GetVerticalAlignment, SetVerticalAlignment, VerticalAlignment, verticalAlignments, VA_TOP, AM_FILE);
+    ACCESSOR_ATTRIBUTE(Sprite, VAR_INT, "Priority", GetPriority, SetPriority, int, 0, AM_FILE);
+    ACCESSOR_ATTRIBUTE(Sprite, VAR_FLOAT, "Opacity", GetOpacity, SetOpacity, float, 1.0f, AM_FILE);
+    REF_ACCESSOR_ATTRIBUTE(Sprite, VAR_COLOR, "Color", GetColorAttr, SetColor, Color, Color::WHITE, AM_FILE);
+    ATTRIBUTE(Sprite, VAR_COLOR, "Top Left Color", color_[0], Color::WHITE, AM_FILE);
+    ATTRIBUTE(Sprite, VAR_COLOR, "Top Right Color", color_[1], Color::WHITE, AM_FILE);
+    ATTRIBUTE(Sprite, VAR_COLOR, "Bottom Left Color", color_[2], Color::WHITE, AM_FILE);
+    ATTRIBUTE(Sprite, VAR_COLOR, "Bottom Right Color", color_[3], Color::WHITE, AM_FILE);
+    ACCESSOR_ATTRIBUTE(Sprite, VAR_BOOL, "Is Visible", IsVisible, SetVisible, bool, true, AM_FILE);
+    ACCESSOR_ATTRIBUTE(Sprite, VAR_BOOL, "Use Derived Opacity", GetUseDerivedOpacity, SetUseDerivedOpacity, bool, false, AM_FILE);
+    ATTRIBUTE(Sprite, VAR_VARIANTMAP, "Variables", vars_, Variant::emptyVariantMap, AM_FILE);
+}
+
+bool Sprite::IsWithinScissor(const IntRect& currentScissor)
+{
+    /// \todo Implement properly, for now just checks visibility flag
+    return visible_;
+}
+
+const IntVector2& Sprite::GetScreenPosition() const
+{
+    // This updates screen position for a sprite
+    GetTransform();
+    return screenPosition_;
+}
+
+void Sprite::GetBatches(PODVector<UIBatch>& batches, PODVector<UIQuad>& quads, const IntRect& currentScissor)
+{
+    bool allOpaque = true;
+    if (GetDerivedOpacity() < 1.0f || color_[C_TOPLEFT].a_ < 1.0f || color_[C_TOPRIGHT].a_ < 1.0f ||
+        color_[C_BOTTOMLEFT].a_ < 1.0f || color_[C_BOTTOMRIGHT].a_ < 1.0f)
+        allOpaque = false;
+    
+    const IntVector2& size = GetSize();
+    UIBatch batch(blendMode_ == BLEND_REPLACE && !allOpaque ? BLEND_ALPHA : blendMode_, currentScissor, texture_, &quads);
+    
+    batch.AddQuad(UIQuad(*this, GetTransform(), 0, 0, size.x_, size.y_, imageRect_.left_, imageRect_.top_, imageRect_.right_ -
+        imageRect_.left_, imageRect_.bottom_ - imageRect_.top_));
+    
+    UIBatch::AddOrMerge(batch, batches);
+    
+    // Reset hovering for next frame
+    hovering_ = false;
+}
+
+void Sprite::SetPosition(const Vector2& position)
+{
+    if (position != floatPosition_)
+    {
+        floatPosition_ = position;
+        MarkDirty();
+    }
+}
+
+void Sprite::SetPosition(float x, float y)
+{
+    SetPosition(Vector2(x, y));
+}
+
+void Sprite::SetHotSpot(const IntVector2& hotSpot)
+{
+    if (hotSpot != hotSpot_)
+    {
+        hotSpot_ = hotSpot;
+        MarkDirty();
+    }
+}
+
+void Sprite::SetHotSpot(int x, int y)
+{
+    SetHotSpot(IntVector2(x, y));
+}
+
+void Sprite::SetScale(const Vector2& scale)
+{
+    if (scale != scale_)
+    {
+        scale_ = scale;
+        MarkDirty();
+    }
+}
+
+void Sprite::SetScale(float x, float y)
+{
+    SetScale(Vector2(x, y));
+}
+
+void Sprite::SetScale(float scale)
+{
+    SetScale(Vector2(scale, scale));
+}
+
+void Sprite::SetRotation(float angle)
+{
+    if (angle != rotation_)
+    {
+        rotation_ = angle;
+        MarkDirty();
+    }
+}
+
+void Sprite::SetTexture(Texture* texture)
+{
+    texture_ = texture;
+    if (imageRect_ == IntRect::ZERO)
+        SetFullImageRect();
+}
+
+void Sprite::SetImageRect(const IntRect& rect)
+{
+    if (rect != IntRect::ZERO)
+        imageRect_ = rect;
+}
+
+void Sprite::SetFullImageRect()
+{
+    if (texture_)
+        SetImageRect(IntRect(0, 0, texture_->GetWidth(), texture_->GetHeight()));
+}
+
+void Sprite::SetBlendMode(BlendMode mode)
+{
+    blendMode_ = mode;
+}
+
+const Matrix3x4& Sprite::GetTransform() const
+{
+    if (positionDirty_)
+    {
+        Vector2 pos = floatPosition_;
+        
+        Matrix3x4 parentTransform;
+        
+        if (parent_)
+        {
+            Sprite* parentSprite = dynamic_cast<Sprite*>(parent_);
+            if (parentSprite)
+                parentTransform = parentSprite->GetTransform();
+            else
+            {
+                const IntVector2& parentScreenPos = parent_->GetScreenPosition() + parent_->GetChildOffset();
+                parentTransform = Matrix3x4::IDENTITY;
+                parentTransform.SetTranslation(Vector3((float)parentScreenPos.x_, (float)parentScreenPos.y_, 0.0f));
+            }
+            
+            switch (GetHorizontalAlignment())
+            {
+            case HA_LEFT:
+                break;
+                
+            case HA_CENTER:
+                pos.x_ += (float)(parent_->GetSize().x_ / 2);
+                break;
+                
+            case HA_RIGHT:
+                pos.x_ += (float)parent_->GetSize().x_;
+                break;
+            }
+            switch (GetVerticalAlignment())
+            {
+            case VA_TOP:
+                break;
+                
+            case VA_CENTER:
+                pos.y_ += (float)(parent_->GetSize().y_ / 2);
+                break;
+                
+            case VA_BOTTOM:
+                pos.y_ += (float)(parent_->GetSize().y_);
+                break;
+            }
+        }
+        else
+            parentTransform = Matrix3x4::IDENTITY;
+        
+        Matrix3x4 hotspotAdjust(Matrix3x4::IDENTITY);
+        hotspotAdjust.SetTranslation(Vector3((float)-hotSpot_.x_, (float)-hotSpot_.y_, 0.0f));
+        
+        Matrix3x4 mainTransform(Vector3(pos, 0.0f), Quaternion(rotation_, Vector3::FORWARD), Vector3(scale_, 1.0f));
+        
+        transform_ = parentTransform * mainTransform * hotspotAdjust;
+        positionDirty_ = false;
+        
+        // Calculate an approximate screen position for GetElementAt(), or pixel-perfect child elements
+        Vector3 topLeftCorner = transform_ * Vector3::ZERO;
+        screenPosition_ = IntVector2((int)topLeftCorner.x_, (int)topLeftCorner.y_);
+    }
+    
+    return transform_;
+}
+
+void Sprite::SetTextureAttr(ResourceRef value)
+{
+    ResourceCache* cache = GetSubsystem<ResourceCache>();
+    SetTexture(cache->GetResource<Texture2D>(value.id_));
+}
+
+ResourceRef Sprite::GetTextureAttr() const
+{
+    return GetResourceRef(texture_, Texture2D::GetTypeStatic());
+}
+
+}

+ 117 - 0
Engine/UI/Sprite.h

@@ -0,0 +1,117 @@
+//
+// Copyright (c) 2008-2013 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#include "Matrix3x4.h"
+#include "UIElement.h"
+
+namespace Urho3D
+{
+
+/// %UI element which allows sub-pixel positioning and size, as well as rotation. Only other Sprites should be added as child elements.
+class Sprite : public UIElement
+{
+    OBJECT(Sprite)
+    
+public:
+    /// Construct.
+    Sprite(Context* context);
+    /// Destruct.
+    virtual ~Sprite();
+    /// Register object factory.
+    static void RegisterObject(Context* context);
+    
+    /// Return whether is visible and inside a scissor rectangle and should be rendered.
+    virtual bool IsWithinScissor(const IntRect& currentScissor);
+    /// Update and return screen position.
+    virtual const IntVector2& GetScreenPosition() const;
+    /// Return UI rendering batches.
+    virtual void GetBatches(PODVector<UIBatch>& batches, PODVector<UIQuad>& quads, const IntRect& currentScissor);
+    
+    /// Set floating point position.
+    void SetPosition(const Vector2& position);
+    /// Set floating point position.
+    void SetPosition(float x, float y);
+    /// Set hotspot for positioning and rotation.
+    void SetHotSpot(const IntVector2& hotSpot);
+    /// Set hotspot for positioning and rotation.
+    void SetHotSpot(int x, int y);
+    /// Set scale. Scale also affects child sprites.
+    void SetScale(const Vector2& scale);
+    /// Set scale. Scale also affects child sprites.
+    void SetScale(float x, float y);
+    /// Set uniform scale. Scale also affects child sprites.
+    void SetScale(float scale);
+    /// Set rotation angle.
+    void SetRotation(float angle);
+    /// Set texture.
+    void SetTexture(Texture* texture);
+    /// Set part of texture to use as the image.
+    void SetImageRect(const IntRect& rect);
+    /// Use whole texture as the image.
+    void SetFullImageRect();
+    /// Set blend mode.
+    void SetBlendMode(BlendMode mode);
+    
+    /// Return floating point position.
+    const Vector2& GetPosition() const { return floatPosition_; }
+    /// Return hotspot.
+    const IntVector2& GetHotSpot() const { return hotSpot_; }
+    /// Return scale.
+    const Vector2& GetScale() const { return scale_; }
+    /// Return rotation angle.
+    float GetRotation() const { return rotation_; }
+    /// Return texture.
+    Texture* GetTexture() const { return texture_; }
+    /// Return image rectangle.
+    const IntRect& GetImageRect() const { return imageRect_; }
+    /// Return blend mode.
+    BlendMode GetBlendMode() const { return blendMode_; }
+    
+    /// Set texture attribute.
+    void SetTextureAttr(ResourceRef value);
+    /// Return texture attribute.
+    ResourceRef GetTextureAttr() const;
+    /// Update and return rendering transform, also used to transform child sprites.
+    const Matrix3x4& GetTransform() const;
+    
+protected:
+    /// Floating point position.
+    Vector2 floatPosition_;
+    /// Hotspot for positioning and rotation.
+    IntVector2 hotSpot_;
+    /// Scale.
+    Vector2 scale_;
+    /// Rotation angle.
+    float rotation_;
+    /// Texture.
+    SharedPtr<Texture> texture_;
+    /// Image rectangle.
+    IntRect imageRect_;
+    /// Blend mode flag.
+    BlendMode blendMode_;
+    /// Transform matrix.
+    mutable Matrix3x4 transform_;
+};
+
+}

+ 10 - 6
Engine/UI/Text.cpp

@@ -104,7 +104,7 @@ void Text::GetBatches(PODVector<UIBatch>& batches, PODVector<UIQuad>& quads, con
     // Hovering or whole selection batch
     if ((hovering_ && hoverColor_.a_ > 0.0) || (selected_ && selectionColor_.a_ > 0.0f))
     {
-        UIBatch batch(GetBatchTransform(), BLEND_ALPHA, currentScissor, 0, &quads);
+        UIBatch batch(BLEND_ALPHA, currentScissor, 0, &quads);
         batch.AddQuad(*this, 0, 0, GetWidth(), GetHeight(), 0, 0, 0, 0, selected_ && selectionColor_.a_ > 0.0f ? selectionColor_ :
             hoverColor_);
         UIBatch::AddOrMerge(batch, batches);
@@ -113,7 +113,7 @@ void Text::GetBatches(PODVector<UIBatch>& batches, PODVector<UIQuad>& quads, con
     // Partial selection batch
     if (!selected_ && selectionLength_ && charSizes_.Size() >= selectionStart_ + selectionLength_ && selectionColor_.a_ > 0.0f)
     {
-        UIBatch batch(GetBatchTransform(), BLEND_ALPHA, currentScissor, 0, &quads);
+        UIBatch batch(BLEND_ALPHA, currentScissor, 0, &quads);
         
         IntVector2 currentStart = charPositions_[selectionStart_];
         IntVector2 currentEnd = currentStart;
@@ -184,7 +184,7 @@ void Text::GetBatches(PODVector<UIBatch>& batches, PODVector<UIQuad>& quads, con
             
         for (unsigned page = 0; page < face->textures_.Size(); ++page)
         {
-            UIBatch batch(GetBatchTransform(), BLEND_ALPHA, currentScissor, face->textures_[page], &quads);
+            UIBatch batch(BLEND_ALPHA, currentScissor, face->textures_[page], &quads);
             batch.AddQuad(pageQuads[page]);
             UIBatch::AddOrMerge(batch, batches);
         }
@@ -525,12 +525,16 @@ int Text::GetRowStartPosition(unsigned rowIndex) const
     switch (textAlignment_)
     {
     case HA_LEFT:
-        return ret;
+        break;
     case HA_CENTER:
-        return ret + (GetSize().x_ - rowWidth) / 2;
+        ret += (GetSize().x_ - rowWidth) / 2;
+        break;
     case HA_RIGHT:
-        return ret + GetSize().x_ - rowWidth;
+        ret += GetSize().x_ - rowWidth;
+        break;
     }
+    
+    return ret;
 }
 
 }

+ 8 - 34
Engine/UI/UI.cpp

@@ -42,6 +42,7 @@
 #include "Shader.h"
 #include "ShaderVariation.h"
 #include "Slider.h"
+#include "Sprite.h"
 #include "Text.h"
 #include "Texture2D.h"
 #include "UI.h"
@@ -94,9 +95,6 @@ UI::~UI()
 
 void UI::SetCursor(Cursor* cursor)
 {
-    if (!rootElement_)
-        return;
-    
     // Remove old cursor (if any) and set new
     if (cursor_)
     {
@@ -118,9 +116,6 @@ void UI::SetCursor(Cursor* cursor)
 
 void UI::SetFocusElement(UIElement* element)
 {
-    if (!rootElement_)
-        return;
-    
     using namespace FocusChanged;
     
     VariantMap eventData;
@@ -165,9 +160,6 @@ void UI::SetFocusElement(UIElement* element)
 
 void UI::Clear()
 {
-    if (!rootElement_)
-        return;
-    
     rootElement_->RemoveAllChildren();
     if (cursor_)
         rootElement_->AddChild(cursor_);
@@ -339,7 +331,8 @@ void UI::Render()
         }
         
         graphics_->SetShaders(vs, ps);
-        graphics_->SetShaderParameter(VSP_MODEL, batch.transform_);
+        if (graphics_->NeedParameterUpdate(SP_OBJECTTRANSFORM, this))
+            graphics_->SetShaderParameter(VSP_MODEL, Matrix3x4::IDENTITY);
         if (graphics_->NeedParameterUpdate(SP_CAMERA, this))
             graphics_->SetShaderParameter(VSP_VIEWPROJ, projection);
         if (graphics_->NeedParameterUpdate(SP_MATERIAL, this))
@@ -427,9 +420,6 @@ void UI::SetNonFocusedMouseWheel(bool nonFocusedMouseWheel)
 
 UIElement* UI::GetElementAt(const IntVector2& position, bool activeOnly)
 {
-    if (!rootElement_)
-        return 0;
-    
     UIElement* result = 0;
     GetElementAt(result, rootElement_, position, activeOnly);
     return result;
@@ -447,9 +437,6 @@ UIElement* UI::GetFocusElement() const
 
 UIElement* UI::GetFrontElement() const
 {
-    if (!rootElement_)
-        return 0;
-    
     const Vector<SharedPtr<UIElement> >& rootChildren = rootElement_->GetChildren();
     int maxPriority = M_MIN_INT;
     UIElement* front = 0;
@@ -542,14 +529,14 @@ void UI::GetBatches(UIElement* element, IntRect currentScissor)
             int currentPriority = (*i)->GetPriority();
             while (j != children.End() && (*j)->GetPriority() == currentPriority)
             {
-                if (IsVisible(*j, currentScissor))
+                if ((*j)->IsWithinScissor(currentScissor))
                     (*j)->GetBatches(batches_, quads_, currentScissor);
                 ++j;
             }
             // Now recurse into the children
             while (i != j)
             {
-                if (IsVisible(*i, currentScissor))
+                if ((*i)->IsVisible())
                     GetBatches(*i, currentScissor);
                 ++i;
             }
@@ -560,29 +547,15 @@ void UI::GetBatches(UIElement* element, IntRect currentScissor)
     {
         while (i != children.End())
         {
+            if ((*i)->IsWithinScissor(currentScissor))
+                (*i)->GetBatches(batches_, quads_, currentScissor);
             if ((*i)->IsVisible())
-            {
-                if (IsVisible(*i, currentScissor))
-                    (*i)->GetBatches(batches_, quads_, currentScissor);
                 GetBatches(*i, currentScissor);
-            }
             ++i;
         }
     }
 }
 
-bool UI::IsVisible(UIElement* element, const IntRect& currentScissor)
-{
-    // First check element's visibility
-    if (!element->IsVisible())
-        return false;
-
-    // Then check element dimensions against the scissor rectangle
-    const IntVector2& screenPos = element->GetScreenPosition();
-    return screenPos.x_ < currentScissor.right_ && screenPos.x_ + element->GetWidth() > currentScissor.left_ &&
-        screenPos.y_ < currentScissor.bottom_ && screenPos.y_ + element->GetHeight() > currentScissor.top_;
-}
-
 void UI::GetElementAt(UIElement*& result, UIElement* current, const IntVector2& position, bool activeOnly)
 {
     if (!current)
@@ -1041,6 +1014,7 @@ void RegisterUILibrary(Context* context)
     
     UIElement::RegisterObject(context);
     BorderImage::RegisterObject(context);
+    Sprite::RegisterObject(context);
     Button::RegisterObject(context);
     CheckBox::RegisterObject(context);
     Cursor::RegisterObject(context);

+ 0 - 2
Engine/UI/UI.h

@@ -97,8 +97,6 @@ private:
     void Update(float timeStep, UIElement* element);
     /// Generate batches from an UI element recursively.
     void GetBatches(UIElement* element, IntRect currentScissor);
-    /// Return whether element is visible and within the scissor rectangle
-    bool IsVisible(UIElement* element, const IntRect& currentScissor);
     /// Return UI element at screen position recursively.
     void GetElementAt(UIElement*& result, UIElement* current, const IntVector2& position, bool activeOnly);
     /// Return the first element in hierarchy that can alter focus.

+ 84 - 54
Engine/UI/UIBatch.cpp

@@ -22,6 +22,7 @@
 
 #include "Precompiled.h"
 #include "Graphics.h"
+#include "Matrix3x4.h"
 #include "ShaderVariation.h"
 #include "Texture.h"
 #include "UIElement.h"
@@ -31,6 +32,12 @@
 namespace Urho3D
 {
 
+#ifdef USE_OPENGL
+static const Vector2 posAdjust(Vector2::ZERO);
+#else
+static const Vector2 posAdjust(0.5f, 0.5f);
+#endif
+
 UIQuad::UIQuad() :
     defined_(false)
 {
@@ -61,10 +68,17 @@ UIQuad::UIQuad(const UIElement& element, int x, int y, int width, int height, in
         bottomRightColor_ = GetInterpolatedColor(element, x + width, y + height);
     }
     
-    left_ = x;
-    top_ = y;
-    right_ = left_ + width;
-    bottom_ = top_ + height;
+    const IntVector2& screenPos = element.GetScreenPosition();
+    
+    float left = (float)(x + screenPos.x_);
+    float right = left + (float)width;
+    float top = (float)(y + screenPos.y_);
+    float bottom = top + (float)height;
+    topLeft_ = Vector2(left, top) - posAdjust;
+    topRight_ = Vector2(right, top) - posAdjust;
+    bottomLeft_ = Vector2(left, bottom) - posAdjust;
+    bottomRight_ = Vector2(right, bottom) - posAdjust;
+    
     leftUV_ = texOffsetX;
     topUV_ = texOffsetY;
     rightUV_ = texOffsetX + (texWidth ? texWidth : width);
@@ -72,7 +86,50 @@ UIQuad::UIQuad(const UIElement& element, int x, int y, int width, int height, in
     
     defined_ = true;
 }
+
+UIQuad::UIQuad(const UIElement& element, const Matrix3x4& transform, int x, int y, int width, int height, int texOffsetX, int texOffsetY,
+    int texWidth, int texHeight, Color* color) :
+    defined_(false)
+{
+    if (color || !element.HasColorGradient())
+    {
+        unsigned uintColor = (color ? *color : element.GetDerivedColor()).ToUInt();
+        
+        // If alpha is 0, nothing will be rendered, so do not add the quad
+        if (!(uintColor & 0xff000000))
+            return;
+        
+        topLeftColor_ = uintColor;
+        topRightColor_ = uintColor;
+        bottomLeftColor_ = uintColor;
+        bottomRightColor_ = uintColor;
+    }
+    else
+    {
+        topLeftColor_ = GetInterpolatedColor(element, x, y);
+        topRightColor_ = GetInterpolatedColor(element, x + width, y);
+        bottomLeftColor_ = GetInterpolatedColor(element, x, y + height);
+        bottomRightColor_ = GetInterpolatedColor(element, x + width, y + height);
+    }
+    
+    Vector3 v1 = transform * Vector3((float)x, (float)y, 0.0f);
+    Vector3 v2 = transform * Vector3((float)x + (float)width, (float)y, 0.0f);
+    Vector3 v3 = transform * Vector3((float)x, (float)y + (float)height, 0.0f);
+    Vector3 v4 = transform * Vector3((float)x + (float)width, (float)y + (float)height, 0.0f);
+    
+    topLeft_.x_ = v1.x_; topLeft_.y_ = v1.y_;
+    topRight_.x_ = v2.x_; topRight_.y_ = v2.y_;
+    bottomLeft_.x_ = v3.x_; bottomLeft_.y_ = v3.y_;
+    bottomRight_.x_ = v4.x_; bottomRight_.y_ = v4.y_;
     
+    leftUV_ = texOffsetX;
+    topUV_ = texOffsetY;
+    rightUV_ = texOffsetX + (texWidth ? texWidth : (int)width);
+    bottomUV_ = texOffsetY + (texHeight ? texHeight : (int)height);
+    
+    defined_ = true;
+}
+
 unsigned UIQuad::GetInterpolatedColor(const UIElement& element, int x, int y)
 {
     const IntVector2& size = element.GetSize();
@@ -105,8 +162,7 @@ UIBatch::UIBatch() :
 {
 }
 
-UIBatch::UIBatch(const Matrix3x4& transform, BlendMode blendMode, const IntRect& scissor, Texture* texture, PODVector<UIQuad>* quads) :
-    transform_(transform),
+UIBatch::UIBatch(BlendMode blendMode, const IntRect& scissor, Texture* texture, PODVector<UIQuad>* quads) :
     blendMode_(blendMode),
     scissor_(scissor),
     texture_(texture),
@@ -119,9 +175,6 @@ UIBatch::UIBatch(const Matrix3x4& transform, BlendMode blendMode, const IntRect&
 
 void UIBatch::Begin(PODVector<UIQuad>* quads)
 {
-    if (!quads)
-        return;
-    
     quads_ = quads;
     quadStart_ = quads_->Size();
     quadCount_ = 0;
@@ -129,7 +182,7 @@ void UIBatch::Begin(PODVector<UIQuad>* quads)
 
 void UIBatch::AddQuad(const PODVector<UIQuad>& quads)
 {
-    if (!quads_ || quads.Empty())
+    if (quads.Empty())
         return;
     
     *quads_ += quads;
@@ -138,9 +191,6 @@ void UIBatch::AddQuad(const PODVector<UIQuad>& quads)
 
 void UIBatch::AddQuad(UIQuad quad)
 {
-    if (!quads_)
-        return;
-    
     if (quad.defined_)
     {
         quads_->Push(quad);
@@ -162,9 +212,6 @@ void UIBatch::AddQuad(const UIElement& element, int x, int y, int width, int hei
 void UIBatch::AddQuad(const UIElement& element, int x, int y, int width, int height, int texOffsetX, int texOffsetY, int texWidth,
     int texHeight, bool tiled)
 {
-    if (!quads_)
-        return;
-
     if (!(element.HasColorGradient() || element.GetDerivedColor().ToUInt() & 0xff000000))
         return; // No gradient and alpha is 0, so do not add the quads
     
@@ -173,26 +220,26 @@ void UIBatch::AddQuad(const UIElement& element, int x, int y, int width, int hei
         AddQuad(element, x, y, width, height, texOffsetX, texOffsetY, texWidth, texHeight);
         return;
     }
-
+    
     int tileX = 0;
     int tileY = 0;
     int tileW = 0;
     int tileH = 0;
-
+    
     while (tileY < height)
     {
         tileX = 0;
         tileH = Min(height - tileY, texHeight);
-
+        
         while (tileX < width)
         {
             tileW = Min(width - tileX, texWidth);
-
+            
             AddQuad(element, x + tileX, y + tileY, tileW, tileH, texOffsetX, texOffsetY, tileW, tileH);
-
+            
             tileX += tileW;
         }
-
+        
         tileY += tileH;
     }
 }
@@ -200,17 +247,13 @@ void UIBatch::AddQuad(const UIElement& element, int x, int y, int width, int hei
 void UIBatch::AddQuad(const UIElement& element, int x, int y, int width, int height, int texOffsetX, int texOffsetY, int texWidth,
     int texHeight, const Color& color)
 {
-    if (!quads_)
-        return;
-    
     Color derivedColor(color.r_, color.g_, color.b_, color.a_ * element.GetDerivedOpacity());
     AddQuad(UIQuad(element, x, y, width, height, texOffsetX, texOffsetY, texWidth, texHeight, &derivedColor));
 }
 
 bool UIBatch::Merge(const UIBatch& batch)
 {
-    if (memcmp(&batch.transform_, &transform_, sizeof transform_) ||
-        batch.blendMode_ != blendMode_ ||
+    if (batch.blendMode_ != blendMode_ ||
         batch.scissor_ != scissor_ ||
         batch.texture_ != texture_ ||
         batch.quads_ != quads_ ||
@@ -226,12 +269,6 @@ void UIBatch::UpdateGeometry(Graphics* graphics, void* lockedData)
     if (!quadCount_)
         return;
     
-    #ifdef USE_OPENGL
-    Vector2 posAdjust(Vector2::ZERO);
-    #else
-    Vector2 posAdjust(0.5f, 0.5f);
-    #endif
-    
     float* dest = (float*)lockedData;
     
     if (texture_)
@@ -241,34 +278,31 @@ void UIBatch::UpdateGeometry(Graphics* graphics, void* lockedData)
         for (unsigned i = 0; i < quadCount_; ++i)
         {
             const UIQuad& quad = quads_->At(quadStart_ + i);
-            Vector2 topLeft, bottomRight, topLeftUV, bottomRightUV;
             
-            topLeft = (Vector2((float)quad.left_, (float)quad.top_) - posAdjust);
-            bottomRight = (Vector2((float)quad.right_, (float)quad.bottom_) - posAdjust);
-            topLeftUV = Vector2((float)quad.leftUV_, (float)quad.topUV_) * invTextureSize;
-            bottomRightUV = Vector2((float)quad.rightUV_, (float)quad.bottomUV_) * invTextureSize;
+            Vector2 topLeftUV = Vector2((float)quad.leftUV_, (float)quad.topUV_) * invTextureSize;
+            Vector2 bottomRightUV = Vector2((float)quad.rightUV_, (float)quad.bottomUV_) * invTextureSize;
             
-            *dest++ = topLeft.x_; *dest++ = topLeft.y_; *dest++ = 0.0f;
+            *dest++ = quad.topLeft_.x_; *dest++ = quad.topLeft_.y_; *dest++ = 0.0f;
             *((unsigned*)dest) = quad.topLeftColor_; dest++;
             *dest++ = topLeftUV.x_; *dest++ = topLeftUV.y_;
             
-            *dest++ = bottomRight.x_; *dest++ = topLeft.y_; *dest++ = 0.0f;
+            *dest++ = quad.topRight_.x_; *dest++ = quad.topRight_.y_; *dest++ = 0.0f;
             *((unsigned*)dest) = quad.topRightColor_; dest++;
             *dest++ = bottomRightUV.x_; *dest++ = topLeftUV.y_;
             
-            *dest++ = topLeft.x_; *dest++ = bottomRight.y_; *dest++ = 0.0f;
+            *dest++ = quad.bottomLeft_.x_; *dest++ = quad.bottomLeft_.y_; *dest++ = 0.0f;
             *((unsigned*)dest) = quad.bottomLeftColor_; dest++;
             *dest++ = topLeftUV.x_; *dest++ = bottomRightUV.y_;
             
-            *dest++ = bottomRight.x_; *dest++ = topLeft.y_; *dest++ = 0.0f;
+            *dest++ = quad.topRight_.x_; *dest++ = quad.topRight_.y_; *dest++ = 0.0f;
             *((unsigned*)dest) = quad.topRightColor_; dest++;
             *dest++ = bottomRightUV.x_; *dest++ = topLeftUV.y_;
             
-            *dest++ = bottomRight.x_; *dest++ = bottomRight.y_; *dest++ = 0.0f;
+            *dest++ = quad.bottomRight_.x_; *dest++ = quad.bottomRight_.y_; *dest++ = 0.0f;
             *((unsigned*)dest) = quad.bottomRightColor_; dest++;
             *dest++ = bottomRightUV.x_; *dest++ = bottomRightUV.y_;
             
-            *dest++ = topLeft.x_; *dest++ = bottomRight.y_; *dest++ = 0.0f;
+            *dest++ = quad.bottomLeft_.x_; *dest++ = quad.bottomLeft_.y_; *dest++ = 0.0f;
             *((unsigned*)dest) = quad.bottomLeftColor_; dest++;
             *dest++ = topLeftUV.x_; *dest++ = bottomRightUV.y_;
         }
@@ -278,32 +312,28 @@ void UIBatch::UpdateGeometry(Graphics* graphics, void* lockedData)
         for (unsigned i = 0; i < quadCount_; ++i)
         {
             const UIQuad& quad = quads_->At(quadStart_ + i);
-            Vector2 topLeft, bottomRight, topLeftUV, bottomRightUV;
-            
-            topLeft = (Vector2((float)quad.left_, (float)quad.top_) - posAdjust);
-            bottomRight = (Vector2((float)quad.right_, (float)quad.bottom_) - posAdjust);
             
-            *dest++ = topLeft.x_; *dest++ = topLeft.y_; *dest++ = 0.0f;
+            *dest++ = quad.topLeft_.x_; *dest++ = quad.topLeft_.y_; *dest++ = 0.0f;
             *((unsigned*)dest) = quad.topLeftColor_; dest++;
             dest += 2; // Jump over unused UV coordinates
             
-            *dest++ = bottomRight.x_; *dest++ = topLeft.y_; *dest++ = 0.0f;
+            *dest++ = quad.topRight_.x_; *dest++ = quad.topRight_.y_; *dest++ = 0.0f;
             *((unsigned*)dest) = quad.topRightColor_; dest++;
             dest += 2;
             
-            *dest++ = topLeft.x_; *dest++ = bottomRight.y_; *dest++ = 0.0f;
+            *dest++ = quad.bottomLeft_.x_; *dest++ = quad.bottomLeft_.y_; *dest++ = 0.0f;
             *((unsigned*)dest) = quad.bottomLeftColor_; dest++;
             dest += 2;
             
-            *dest++ = bottomRight.x_; *dest++ = topLeft.y_; *dest++ = 0.0f;
+            *dest++ = quad.topRight_.x_; *dest++ = quad.topRight_.y_; *dest++ = 0.0f;
             *((unsigned*)dest) = quad.topRightColor_; dest++;
             dest += 2;
             
-            *dest++ = bottomRight.x_; *dest++ = bottomRight.y_; *dest++ = 0.0f;
+            *dest++ = quad.bottomRight_.x_; *dest++ = quad.bottomRight_.y_; *dest++ = 0.0f;
             *((unsigned*)dest) = quad.bottomRightColor_; dest++;
             dest += 2;
             
-            *dest++ = topLeft.x_; *dest++ = bottomRight.y_; *dest++ = 0.0f;
+            *dest++ = quad.bottomLeft_.x_; *dest++ = quad.bottomLeft_.y_; *dest++ = 0.0f;
             *((unsigned*)dest) = quad.bottomLeftColor_; dest++;
             dest += 2;
         }

+ 16 - 15
Engine/UI/UIBatch.h

@@ -24,7 +24,6 @@
 
 #include "Color.h"
 #include "GraphicsDefs.h"
-#include "Matrix3x4.h"
 #include "Rect.h"
 
 namespace Urho3D
@@ -32,6 +31,7 @@ namespace Urho3D
 
 class PixelShader;
 class Graphics;
+class Matrix3x4;
 class ShaderVariation;
 class Texture;
 class UIElement;
@@ -44,18 +44,21 @@ struct UIQuad
     /// Construct.
     UIQuad(const UIElement& element, int x, int y, int width, int height, int texOffsetX, int texOffsetY,
         int texWidth = 0, int texHeight = 0, Color* color = 0);
-
+    /// Construct using a transform matrix.
+    UIQuad(const UIElement& element, const Matrix3x4& transform, int x, int y, int width, int height, int texOffsetX, int texOffsetY,
+        int texWidth = 0, int texHeight = 0, Color* color = 0);
+    
     /// Return an interpolated color for an UI element.
     static unsigned GetInterpolatedColor(const UIElement& element, int x, int y);
-
-    /// Left coordinate.
-    int left_;
-    /// Top coordinate.
-    int top_;
-    /// Right coordinate.
-    int right_;
-    /// Bottom coordinate.
-    int bottom_;
+    
+    /// Top left position.
+    Vector2 topLeft_;
+    /// Top right position.
+    Vector2 topRight_;
+    /// Bottom left position.
+    Vector2 bottomLeft_;
+    /// Bottom right position.
+    Vector2 bottomRight_;
     /// Left texture coordinate.
     short leftUV_;
     /// Top texture coordinate.
@@ -82,8 +85,8 @@ class UIBatch
 public:
     /// Construct with defaults.
     UIBatch();
-    /// Construct
-    UIBatch(const Matrix3x4& transform, BlendMode blendMode, const IntRect& scissor, Texture* texture, PODVector<UIQuad>* quads);
+    /// Construct.
+    UIBatch(BlendMode blendMode, const IntRect& scissor, Texture* texture, PODVector<UIQuad>* quads);
     
     /// Begin adding quads.
     void Begin(PODVector<UIQuad>* quads);
@@ -107,8 +110,6 @@ public:
     /// Add or merge a batch.
     static void AddOrMerge(const UIBatch& batch, PODVector<UIBatch>& batches);
     
-    /// Transform matrix.
-    Matrix3x4 transform_;
     /// Blending mode.
     BlendMode blendMode_;
     /// Scissor rectangle.

+ 117 - 93
Engine/UI/UIElement.cpp

@@ -37,7 +37,7 @@
 namespace Urho3D
 {
 
-static const char* horizontalAlignments[] =
+const char* horizontalAlignments[] =
 {
     "Left",
     "Center",
@@ -45,7 +45,7 @@ static const char* horizontalAlignments[] =
     0
 };
 
-static const char* verticalAlignments[] =
+const char* verticalAlignments[] =
 {
     "Top",
     "Center",
@@ -126,12 +126,12 @@ UIElement::UIElement(Context* context) :
     layoutMode_(LM_FREE),
     layoutSpacing_(0),
     layoutBorder_(IntRect::ZERO),
-    rotationPivot_(IntVector2::ZERO),
-    rotation_(0.0f),
     resizeNestingLevel_(0),
     layoutNestingLevel_(0),
     layoutMinSize_(0),
     indent_(0),
+    indentSpacing_(16),
+    positionDirty_(true),
     position_(IntVector2::ZERO),
     size_(IntVector2::ZERO),
     minSize_(IntVector2::ZERO),
@@ -140,12 +140,10 @@ UIElement::UIElement(Context* context) :
     horizontalAlignment_(HA_LEFT),
     verticalAlignment_(VA_TOP),
     opacity_(1.0f),
-    positionDirty_(true),
     opacityDirty_(true),
     derivedColorDirty_(true),
     sortOrderDirty_(false),
-    colorGradient_(false),
-    indentSpacing_(16)
+    colorGradient_(false)
 {
 }
 
@@ -192,8 +190,6 @@ void UIElement::RegisterObject(Context* context)
     REF_ACCESSOR_ATTRIBUTE(UIElement, VAR_INTRECT, "Layout Border", GetLayoutBorder, SetLayoutBorder, IntRect, IntRect::ZERO, AM_FILE);
     ACCESSOR_ATTRIBUTE(UIElement, VAR_INT, "Indent", GetIndent, SetIndent, int, 0, AM_FILE);
     ACCESSOR_ATTRIBUTE(UIElement, VAR_INT, "Indent Spacing", GetIndentSpacing, SetIndentSpacing, int, 16, AM_FILE);
-    REF_ACCESSOR_ATTRIBUTE(UIElement, VAR_INTVECTOR2, "Rotation Pivot", GetRotationPivot, SetRotationPivot, IntVector2, IntVector2::ZERO, AM_FILE);
-    ACCESSOR_ATTRIBUTE(UIElement, VAR_FLOAT, "Rotation", GetRotation, SetRotation, float, 0.0f, AM_FILE);
     ATTRIBUTE(UIElement, VAR_VARIANTMAP, "Variables", vars_, Variant::emptyVariantMap, AM_FILE);
 }
 
@@ -312,6 +308,66 @@ void UIElement::GetBatches(PODVector<UIBatch>& batches, PODVector<UIQuad>& quads
     hovering_ = false;
 }
 
+bool UIElement::IsWithinScissor(const IntRect& currentScissor)
+{
+    if (!visible_)
+        return false;
+    
+    const IntVector2& screenPos = GetScreenPosition();
+    return screenPos.x_ < currentScissor.right_ && screenPos.x_ + GetWidth() > currentScissor.left_ &&
+        screenPos.y_ < currentScissor.bottom_ && screenPos.y_ + GetHeight() > currentScissor.top_;
+}
+
+const IntVector2& UIElement::GetScreenPosition() const
+{
+    if (positionDirty_)
+    {
+        IntVector2 pos = position_;
+        const UIElement* parent = parent_;
+        
+        if (parent)
+        {
+            const IntVector2& parentScreenPos = parent->GetScreenPosition();
+            
+            switch (horizontalAlignment_)
+            {
+            case HA_LEFT:
+                pos.x_ += parentScreenPos.x_;
+                break;
+                
+            case HA_CENTER:
+                pos.x_ += parentScreenPos.x_ + parent_->size_.x_ / 2 - size_.x_ / 2;
+                break;
+                
+            case HA_RIGHT:
+                pos.x_ += parentScreenPos.x_ + parent_->size_.x_ - size_.x_;
+                break;
+            }
+            switch (verticalAlignment_)
+            {
+            case VA_TOP:
+                pos.y_ += parentScreenPos.y_;
+                break;
+                
+            case VA_CENTER:
+                pos.y_ += parentScreenPos.y_ + parent_->size_.y_ / 2 - size_.y_ / 2;
+                break;
+                
+            case VA_BOTTOM:
+                pos.y_ += parentScreenPos.y_ + parent_->size_.y_ - size_.y_;
+                break;
+            }
+            
+            pos += parent_->childOffset_;
+        }
+        
+        screenPosition_ = pos;
+        positionDirty_ = false;
+    }
+    
+    return screenPosition_;
+}
+
 void UIElement::OnHover(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor)
 {
     if (cursor && cursor->IsVisible())
@@ -735,16 +791,6 @@ void UIElement::SetIndentSpacing(int indentSpacing)
     indentSpacing_ = Max(indentSpacing, 0);
 }
 
-void UIElement::SetRotationPivot(const IntVector2& pivot)
-{
-    rotationPivot_ = pivot;
-}
-
-void UIElement::SetRotation(float angle)
-{
-    rotation_ = angle;
-}
-
 void UIElement::UpdateLayout()
 {
     if (layoutMode_ == LM_FREE || layoutNestingLevel_)
@@ -963,14 +1009,43 @@ void UIElement::InsertChild(unsigned index, UIElement* element)
     element->parent_ = this;
     element->MarkDirty();
     UpdateLayout();
+    
+    // Send change event
+    UIElement* root = GetRoot();
+    if (root)
+    {
+        using namespace ElementAdded;
+        
+        VariantMap eventData;
+        eventData[P_ROOT] = (void*)root;
+        eventData[P_PARENT] = (void*)this;
+        eventData[P_ELEMENT] = (void*)element;
+        
+        root->SendEvent(E_ELEMENTADDED, eventData);
+    }
 }
 
 void UIElement::RemoveChild(UIElement* element, unsigned index)
 {
+    UIElement* root = GetRoot();
+    
     for (unsigned i = index; i < children_.Size(); ++i)
     {
         if (children_[i] == element)
         {
+            // Send change event if not already being destroyed
+            if (Refs() > 0 && root)
+            {
+                using namespace ElementRemoved;
+                
+                VariantMap eventData;
+                eventData[P_ROOT] = (void*)root;
+                eventData[P_PARENT] = (void*)this;
+                eventData[P_ELEMENT] = (void*)element;
+                
+                root->SendEvent(E_ELEMENTREMOVED, eventData);
+            }
+            
             element->Detach();
             children_.Erase(i);
             UpdateLayout();
@@ -991,8 +1066,25 @@ void UIElement::RemoveChildAtIndex(unsigned index)
 
 void UIElement::RemoveAllChildren()
 {
+    UIElement* root = GetRoot();
+    
     for (Vector<SharedPtr<UIElement> >::Iterator i = children_.Begin(); i < children_.End(); )
+    {
+        // Send change event if not already being destroyed
+        if (Refs() > 0 && root)
+        {
+            using namespace ElementRemoved;
+            
+            VariantMap eventData;
+            eventData[P_ROOT] = (void*)root;
+            eventData[P_PARENT] = (void*)this;
+            eventData[P_ELEMENT] = (void*)(*i).Get();
+            
+            root->SendEvent(E_ELEMENTREMOVED, eventData);
+        }
+        
         (*i++)->Detach();
+    }
     children_.Clear();
 }
 
@@ -1018,58 +1110,6 @@ void UIElement::SetInternal(bool enable)
     internal_ = enable;
 }
 
-IntVector2 UIElement::GetScreenPosition() const
-{
-    if (positionDirty_)
-    {
-        IntVector2 pos = position_;
-        const UIElement* parent = parent_;
-        const UIElement* current = this;
-        
-        while (parent)
-        {
-            switch (current->horizontalAlignment_)
-            {
-            case HA_LEFT:
-                pos.x_ += parent->position_.x_;
-                break;
-                
-            case HA_CENTER:
-                pos.x_ += parent->position_.x_ + parent->size_.x_ / 2 - current->size_.x_ / 2;
-                break;
-                
-            case HA_RIGHT:
-                pos.x_ += parent->position_.x_ + parent->size_.x_ - current->size_.x_;
-                break;
-            }
-            switch (current->verticalAlignment_)
-            {
-            case VA_TOP:
-                pos.y_ += parent->position_.y_;
-                break;
-                
-            case VA_CENTER:
-                pos.y_ += parent->position_.y_ + parent->size_.y_ / 2 - current->size_.y_ / 2;
-                break;
-                
-            case VA_BOTTOM:
-                pos.y_ += parent->position_.y_ + parent->size_.y_ - current->size_.y_;
-                break;
-            }
-            
-            pos += parent->childOffset_;
-            
-            current = parent;
-            parent = parent->parent_;
-        }
-        
-        screenPosition_ = pos;
-        positionDirty_ = false;
-    }
-    
-    return screenPosition_;
-}
-
 float UIElement::GetDerivedOpacity() const
 {
     if (!useDerivedOpacity_)
@@ -1302,35 +1342,19 @@ void UIElement::AdjustScissor(IntRect& currentScissor)
     }
 }
 
-Matrix3x4 UIElement::GetBatchTransform()
-{
-    const IntVector2& screenPos = GetScreenPosition();
-    const IntVector2& size = GetSize();
-    Vector3 pivot((float)rotationPivot_.x_, (float)rotationPivot_.y_, 0.0f);
-    
-    Matrix3x4 rotationAdjust(Matrix3x4::IDENTITY);
-    rotationAdjust.SetTranslation(-pivot);
-    
-    Matrix3x4 rotation(Vector3::ZERO, Quaternion(rotation_, Vector3::FORWARD), 1.0f);
-    rotation.SetTranslation(pivot);
-    
-    Matrix3x4 translation(Matrix3x4::IDENTITY);
-    translation.SetTranslation(Vector3((float)screenPos.x_, (float)screenPos.y_, 0.0f));
-    
-    return translation * rotation * rotationAdjust;
-}
-
 void UIElement::GetBatchesWithOffset(IntVector2& offset, PODVector<UIBatch>& batches, PODVector<UIQuad>& quads, IntRect
     currentScissor)
 {
+    Vector2 floatOffset((float)offset.x_, (float)offset.y_);
     unsigned initialSize = quads.Size();
+    
     GetBatches(batches, quads, currentScissor);
     for (unsigned i = initialSize; i < quads.Size(); ++i)
     {
-        quads[i].left_ += offset.x_;
-        quads[i].top_ += offset.y_;
-        quads[i].right_ += offset.x_;
-        quads[i].bottom_ += offset.y_;
+        quads[i].topLeft_ += floatOffset;
+        quads[i].topRight_ += floatOffset;
+        quads[i].bottomLeft_ += floatOffset;
+        quads[i].bottomRight_ += floatOffset;
     }
     
     AdjustScissor(currentScissor);

+ 11 - 23
Engine/UI/UIElement.h

@@ -123,6 +123,10 @@ public:
     
     /// Perform UI element update.
     virtual void Update(float timeStep);
+    /// Return whether is visible and inside a scissor rectangle and should be rendered.
+    virtual bool IsWithinScissor(const IntRect& currentScissor);
+    /// Update and return screen position.
+    virtual const IntVector2& GetScreenPosition() const;
     /// Return UI rendering batches.
     virtual void GetBatches(PODVector<UIBatch>& batches, PODVector<UIQuad>& quads, const IntRect& currentScissor);
     /// React to mouse hover.
@@ -249,10 +253,6 @@ public:
     void SetIndent(int indent);
     /// Set indent spacing (number of pixels per indentation level).
     void SetIndentSpacing(int indentSpacing);
-    /// Set content rotation pivot offset.
-    void SetRotationPivot(const IntVector2& pivot);
-    /// Set content rotation angle in degrees. Positive is clockwise. Note: child elements do not rotate.
-    void SetRotation(float angle);
     /// Manually update layout. Should not be necessary in most cases, but is provided for completeness.
     void UpdateLayout();
     /// Disable automatic layout update. Should only be used if there are performance problems.
@@ -288,8 +288,6 @@ public:
     const String& GetName() const { return name_; }
     /// Return position.
     const IntVector2& GetPosition() const { return position_; }
-    /// Return screen position.
-    IntVector2 GetScreenPosition() const;
     /// Return size.
     const IntVector2& GetSize() const { return size_; }
     /// Return width.
@@ -360,10 +358,6 @@ public:
     int GetLayoutSpacing() const { return layoutSpacing_; }
     /// Return layout border.
     const IntRect& GetLayoutBorder() const { return layoutBorder_; }
-    /// Return rotation pivot offset.
-    const IntVector2& GetRotationPivot() const { return rotationPivot_; }
-    /// Return rotation angle.
-    float GetRotation() const { return rotation_; }
     /// Return number of child elements.
     unsigned GetNumChildren(bool recursive = false) const;
     /// Return child element by index.
@@ -414,8 +408,6 @@ public:
     void SetTempVisible(bool enable);
     /// Adjust scissor for rendering.
     void AdjustScissor(IntRect& currentScissor);
-    /// Get transform for rendering batches.
-    Matrix3x4 GetBatchTransform();
     /// Get UI rendering batches with a specified offset. Also recurses to child elements.
     void GetBatchesWithOffset(IntVector2& offset, PODVector<UIBatch>& batches, PODVector<UIQuad>& quads, IntRect
         currentScissor);
@@ -470,10 +462,6 @@ protected:
     int layoutSpacing_;
     /// Layout borders.
     IntRect layoutBorder_;
-    /// Rotation pivot offset.
-    IntVector2 rotationPivot_;
-    /// Rotation angle.
-    float rotation_;
     /// Resize nesting level to prevent multiple events and endless loop.
     unsigned resizeNestingLevel_;
     /// Layout update nesting level to prevent endless loop.
@@ -482,7 +470,13 @@ protected:
     int layoutMinSize_;
     /// Horizontal indentation.
     int indent_;
-
+    /// Indent spacing (number of pixels per indentation level).
+    int indentSpacing_;
+    /// Screen position.
+    mutable IntVector2 screenPosition_;
+    /// Screen position dirty flag.
+    mutable bool positionDirty_;
+    
 private:
     /// Return child elements recursively.
     void GetChildrenRecursive(PODVector<UIElement*>& dest) const;
@@ -497,8 +491,6 @@ private:
     
     /// Position.
     IntVector2 position_;
-    /// Screen position.
-    mutable IntVector2 screenPosition_;
     /// Size.
     IntVector2 size_;
     /// Minimum size.
@@ -517,8 +509,6 @@ private:
     mutable float derivedOpacity_;
     /// Derived color. Only valid when no gradient.
     mutable Color derivedColor_;
-    /// Screen position dirty flag.
-    mutable bool positionDirty_;
     /// Derived opacity dirty flag.
     mutable bool opacityDirty_;
     /// Derived color dirty flag (only used when no gradient.)
@@ -527,8 +517,6 @@ private:
     bool sortOrderDirty_;
     /// Has color gradient flag.
     bool colorGradient_;
-    /// Indent spacing (number of pixels per indentation level).
-    int indentSpacing_;
     /// Default style file.
     SharedPtr<XMLFile> defaultStyle_;
 };

+ 16 - 0
Engine/UI/UIEvents.h

@@ -207,4 +207,20 @@ EVENT(E_FILESELECTED, FileSelected)
     PARAM(P_OK, Ok);                        // bool
 }
 
+/// A child element has been added to an element. Sent by the UI root element.
+EVENT(E_ELEMENTADDED, ElementAdded)
+{
+    PARAM(P_ROOT, Root);                    // UIElement pointer
+    PARAM(P_PARENT, Parent);                // UIElement pointer
+    PARAM(P_ELEMENT, Element);              // UIElement pointer
+}
+
+/// A child element is about to be removed from an element. Sent by the UI root element.
+EVENT(E_ELEMENTREMOVED, ElementRemoved)
+{
+    PARAM(P_ROOT, Root);                    // UIElement pointer
+    PARAM(P_PARENT, Parent);                // UIElement pointer
+    PARAM(P_ELEMENT, Element);              // UIElement pointer
+}
+
 }