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.style = uiStyle;
     newCursor.position = IntVector2(graphics.width / 2, graphics.height / 2);
     newCursor.position = IntVector2(graphics.width / 2, graphics.height / 2);
     ui.cursor = newCursor;
     ui.cursor = newCursor;
-    if (GetPlatform() == "Android")
+    if (GetPlatform() == "Android" || GetPlatform() == "iOS")
         ui.cursor.visible = false;
         ui.cursor.visible = false;
 
 
     downloadsText = Text();
     downloadsText = Text();

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

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

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

@@ -293,7 +293,7 @@ void InitUI()
     cursor.style = uiStyle;
     cursor.style = uiStyle;
     cursor.position = IntVector2(graphics.width / 2, graphics.height / 2);
     cursor.position = IntVector2(graphics.width / 2, graphics.height / 2);
     ui.cursor = cursor;
     ui.cursor = cursor;
-    if (GetPlatform() == "Android")
+    if (GetPlatform() == "Android" || GetPlatform() == "iOS")
         ui.cursor.visible = false;
         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
 - ScrollBar: a slider with back and forward buttons
 - ScrollView: a scrollable view of child elements
 - ScrollView: a scrollable view of child elements
 - Slider: a horizontal or vertical slider bar
 - 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
 - Text: static text that can be multiline
 - UIElement: container for other elements, renders nothing by itself
 - UIElement: container for other elements, renders nothing by itself
 - Window: a movable and resizable window
 - 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.
 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
 \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.
 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.
 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
 \page Serialization Serialization
 
 

+ 97 - 42
Docs/ScriptAPI.dox

@@ -3159,8 +3159,6 @@ Properties:<br>
 - int indent
 - int indent
 - int indentSpacing
 - int indentSpacing
 - int indentWidth (readonly)
 - int indentWidth (readonly)
-- IntVector2 rotationPivot
-- float rotation
 - IntVector2 childOffset (readonly)
 - IntVector2 childOffset (readonly)
 - uint[] numChildren (readonly)
 - uint[] numChildren (readonly)
 - uint numAllChildren (readonly)
 - uint numAllChildren (readonly)
@@ -3168,8 +3166,8 @@ Properties:<br>
 - UIElement@ parent (readonly)
 - UIElement@ parent (readonly)
 - UIElement@ root (readonly)
 - UIElement@ root (readonly)
 - IntVector2 screenPosition (readonly)
 - IntVector2 screenPosition (readonly)
-- float derivedOpacity (readonly)
 - IntRect combinedScreenRect (readonly)
 - IntRect combinedScreenRect (readonly)
+- float derivedOpacity (readonly)
 - VariantMap vars (readonly)
 - VariantMap vars (readonly)
 
 
 
 
@@ -3267,8 +3265,6 @@ Properties:<br>
 - int indent
 - int indent
 - int indentSpacing
 - int indentSpacing
 - int indentWidth (readonly)
 - int indentWidth (readonly)
-- IntVector2 rotationPivot
-- float rotation
 - IntVector2 childOffset (readonly)
 - IntVector2 childOffset (readonly)
 - uint[] numChildren (readonly)
 - uint[] numChildren (readonly)
 - uint numAllChildren (readonly)
 - uint numAllChildren (readonly)
@@ -3276,8 +3272,8 @@ Properties:<br>
 - UIElement@ parent (readonly)
 - UIElement@ parent (readonly)
 - UIElement@ root (readonly)
 - UIElement@ root (readonly)
 - IntVector2 screenPosition (readonly)
 - IntVector2 screenPosition (readonly)
-- float derivedOpacity (readonly)
 - IntRect combinedScreenRect (readonly)
 - IntRect combinedScreenRect (readonly)
+- float derivedOpacity (readonly)
 - VariantMap vars (readonly)
 - VariantMap vars (readonly)
 - Texture@ texture
 - Texture@ texture
 - IntRect imageRect
 - IntRect imageRect
@@ -3287,6 +3283,89 @@ Properties:<br>
 - bool tiled
 - 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
 Button
 
 
 Methods:<br>
 Methods:<br>
@@ -3384,8 +3463,6 @@ Properties:<br>
 - int indent
 - int indent
 - int indentSpacing
 - int indentSpacing
 - int indentWidth (readonly)
 - int indentWidth (readonly)
-- IntVector2 rotationPivot
-- float rotation
 - IntVector2 childOffset (readonly)
 - IntVector2 childOffset (readonly)
 - uint[] numChildren (readonly)
 - uint[] numChildren (readonly)
 - uint numAllChildren (readonly)
 - uint numAllChildren (readonly)
@@ -3393,8 +3470,8 @@ Properties:<br>
 - UIElement@ parent (readonly)
 - UIElement@ parent (readonly)
 - UIElement@ root (readonly)
 - UIElement@ root (readonly)
 - IntVector2 screenPosition (readonly)
 - IntVector2 screenPosition (readonly)
-- float derivedOpacity (readonly)
 - IntRect combinedScreenRect (readonly)
 - IntRect combinedScreenRect (readonly)
+- float derivedOpacity (readonly)
 - VariantMap vars (readonly)
 - VariantMap vars (readonly)
 - Texture@ texture
 - Texture@ texture
 - IntRect imageRect
 - IntRect imageRect
@@ -3503,8 +3580,6 @@ Properties:<br>
 - int indent
 - int indent
 - int indentSpacing
 - int indentSpacing
 - int indentWidth (readonly)
 - int indentWidth (readonly)
-- IntVector2 rotationPivot
-- float rotation
 - IntVector2 childOffset (readonly)
 - IntVector2 childOffset (readonly)
 - uint[] numChildren (readonly)
 - uint[] numChildren (readonly)
 - uint numAllChildren (readonly)
 - uint numAllChildren (readonly)
@@ -3512,8 +3587,8 @@ Properties:<br>
 - UIElement@ parent (readonly)
 - UIElement@ parent (readonly)
 - UIElement@ root (readonly)
 - UIElement@ root (readonly)
 - IntVector2 screenPosition (readonly)
 - IntVector2 screenPosition (readonly)
-- float derivedOpacity (readonly)
 - IntRect combinedScreenRect (readonly)
 - IntRect combinedScreenRect (readonly)
+- float derivedOpacity (readonly)
 - VariantMap vars (readonly)
 - VariantMap vars (readonly)
 - Texture@ texture
 - Texture@ texture
 - IntRect imageRect
 - IntRect imageRect
@@ -3620,8 +3695,6 @@ Properties:<br>
 - int indent
 - int indent
 - int indentSpacing
 - int indentSpacing
 - int indentWidth (readonly)
 - int indentWidth (readonly)
-- IntVector2 rotationPivot
-- float rotation
 - IntVector2 childOffset (readonly)
 - IntVector2 childOffset (readonly)
 - uint[] numChildren (readonly)
 - uint[] numChildren (readonly)
 - uint numAllChildren (readonly)
 - uint numAllChildren (readonly)
@@ -3629,8 +3702,8 @@ Properties:<br>
 - UIElement@ parent (readonly)
 - UIElement@ parent (readonly)
 - UIElement@ root (readonly)
 - UIElement@ root (readonly)
 - IntVector2 screenPosition (readonly)
 - IntVector2 screenPosition (readonly)
-- float derivedOpacity (readonly)
 - IntRect combinedScreenRect (readonly)
 - IntRect combinedScreenRect (readonly)
+- float derivedOpacity (readonly)
 - VariantMap vars (readonly)
 - VariantMap vars (readonly)
 - Texture@ texture
 - Texture@ texture
 - IntRect imageRect
 - IntRect imageRect
@@ -3736,8 +3809,6 @@ Properties:<br>
 - int indent
 - int indent
 - int indentSpacing
 - int indentSpacing
 - int indentWidth (readonly)
 - int indentWidth (readonly)
-- IntVector2 rotationPivot
-- float rotation
 - IntVector2 childOffset (readonly)
 - IntVector2 childOffset (readonly)
 - uint[] numChildren (readonly)
 - uint[] numChildren (readonly)
 - uint numAllChildren (readonly)
 - uint numAllChildren (readonly)
@@ -3745,8 +3816,8 @@ Properties:<br>
 - UIElement@ parent (readonly)
 - UIElement@ parent (readonly)
 - UIElement@ root (readonly)
 - UIElement@ root (readonly)
 - IntVector2 screenPosition (readonly)
 - IntVector2 screenPosition (readonly)
-- float derivedOpacity (readonly)
 - IntRect combinedScreenRect (readonly)
 - IntRect combinedScreenRect (readonly)
+- float derivedOpacity (readonly)
 - VariantMap vars (readonly)
 - VariantMap vars (readonly)
 - Texture@ texture
 - Texture@ texture
 - IntRect imageRect
 - IntRect imageRect
@@ -3856,8 +3927,6 @@ Properties:<br>
 - int indent
 - int indent
 - int indentSpacing
 - int indentSpacing
 - int indentWidth (readonly)
 - int indentWidth (readonly)
-- IntVector2 rotationPivot
-- float rotation
 - IntVector2 childOffset (readonly)
 - IntVector2 childOffset (readonly)
 - uint[] numChildren (readonly)
 - uint[] numChildren (readonly)
 - uint numAllChildren (readonly)
 - uint numAllChildren (readonly)
@@ -3865,8 +3934,8 @@ Properties:<br>
 - UIElement@ parent (readonly)
 - UIElement@ parent (readonly)
 - UIElement@ root (readonly)
 - UIElement@ root (readonly)
 - IntVector2 screenPosition (readonly)
 - IntVector2 screenPosition (readonly)
-- float derivedOpacity (readonly)
 - IntRect combinedScreenRect (readonly)
 - IntRect combinedScreenRect (readonly)
+- float derivedOpacity (readonly)
 - VariantMap vars (readonly)
 - VariantMap vars (readonly)
 - Orientation orientation
 - Orientation orientation
 - float range
 - float range
@@ -3973,8 +4042,6 @@ Properties:<br>
 - int indent
 - int indent
 - int indentSpacing
 - int indentSpacing
 - int indentWidth (readonly)
 - int indentWidth (readonly)
-- IntVector2 rotationPivot
-- float rotation
 - IntVector2 childOffset (readonly)
 - IntVector2 childOffset (readonly)
 - uint[] numChildren (readonly)
 - uint[] numChildren (readonly)
 - uint numAllChildren (readonly)
 - uint numAllChildren (readonly)
@@ -3982,8 +4049,8 @@ Properties:<br>
 - UIElement@ parent (readonly)
 - UIElement@ parent (readonly)
 - UIElement@ root (readonly)
 - UIElement@ root (readonly)
 - IntVector2 screenPosition (readonly)
 - IntVector2 screenPosition (readonly)
-- float derivedOpacity (readonly)
 - IntRect combinedScreenRect (readonly)
 - IntRect combinedScreenRect (readonly)
+- float derivedOpacity (readonly)
 - VariantMap vars (readonly)
 - VariantMap vars (readonly)
 - UIElement@ contentElement
 - UIElement@ contentElement
 - IntVector2 viewPosition
 - IntVector2 viewPosition
@@ -4106,8 +4173,6 @@ Properties:<br>
 - int indent
 - int indent
 - int indentSpacing
 - int indentSpacing
 - int indentWidth (readonly)
 - int indentWidth (readonly)
-- IntVector2 rotationPivot
-- float rotation
 - IntVector2 childOffset (readonly)
 - IntVector2 childOffset (readonly)
 - uint[] numChildren (readonly)
 - uint[] numChildren (readonly)
 - uint numAllChildren (readonly)
 - uint numAllChildren (readonly)
@@ -4115,8 +4180,8 @@ Properties:<br>
 - UIElement@ parent (readonly)
 - UIElement@ parent (readonly)
 - UIElement@ root (readonly)
 - UIElement@ root (readonly)
 - IntVector2 screenPosition (readonly)
 - IntVector2 screenPosition (readonly)
-- float derivedOpacity (readonly)
 - IntRect combinedScreenRect (readonly)
 - IntRect combinedScreenRect (readonly)
+- float derivedOpacity (readonly)
 - VariantMap vars (readonly)
 - VariantMap vars (readonly)
 - IntVector2 viewPosition
 - IntVector2 viewPosition
 - UIElement@ contentElement (readonly)
 - UIElement@ contentElement (readonly)
@@ -4235,8 +4300,6 @@ Properties:<br>
 - int indent
 - int indent
 - int indentSpacing
 - int indentSpacing
 - int indentWidth (readonly)
 - int indentWidth (readonly)
-- IntVector2 rotationPivot
-- float rotation
 - IntVector2 childOffset (readonly)
 - IntVector2 childOffset (readonly)
 - uint[] numChildren (readonly)
 - uint[] numChildren (readonly)
 - uint numAllChildren (readonly)
 - uint numAllChildren (readonly)
@@ -4244,8 +4307,8 @@ Properties:<br>
 - UIElement@ parent (readonly)
 - UIElement@ parent (readonly)
 - UIElement@ root (readonly)
 - UIElement@ root (readonly)
 - IntVector2 screenPosition (readonly)
 - IntVector2 screenPosition (readonly)
-- float derivedOpacity (readonly)
 - IntRect combinedScreenRect (readonly)
 - IntRect combinedScreenRect (readonly)
+- float derivedOpacity (readonly)
 - VariantMap vars (readonly)
 - VariantMap vars (readonly)
 - Font@ font (readonly)
 - Font@ font (readonly)
 - int fontSize (readonly)
 - int fontSize (readonly)
@@ -4355,8 +4418,6 @@ Properties:<br>
 - int indent
 - int indent
 - int indentSpacing
 - int indentSpacing
 - int indentWidth (readonly)
 - int indentWidth (readonly)
-- IntVector2 rotationPivot
-- float rotation
 - IntVector2 childOffset (readonly)
 - IntVector2 childOffset (readonly)
 - uint[] numChildren (readonly)
 - uint[] numChildren (readonly)
 - uint numAllChildren (readonly)
 - uint numAllChildren (readonly)
@@ -4364,8 +4425,8 @@ Properties:<br>
 - UIElement@ parent (readonly)
 - UIElement@ parent (readonly)
 - UIElement@ root (readonly)
 - UIElement@ root (readonly)
 - IntVector2 screenPosition (readonly)
 - IntVector2 screenPosition (readonly)
-- float derivedOpacity (readonly)
 - IntRect combinedScreenRect (readonly)
 - IntRect combinedScreenRect (readonly)
+- float derivedOpacity (readonly)
 - VariantMap vars (readonly)
 - VariantMap vars (readonly)
 - Texture@ texture
 - Texture@ texture
 - IntRect imageRect
 - IntRect imageRect
@@ -4485,8 +4546,6 @@ Properties:<br>
 - int indent
 - int indent
 - int indentSpacing
 - int indentSpacing
 - int indentWidth (readonly)
 - int indentWidth (readonly)
-- IntVector2 rotationPivot
-- float rotation
 - IntVector2 childOffset (readonly)
 - IntVector2 childOffset (readonly)
 - uint[] numChildren (readonly)
 - uint[] numChildren (readonly)
 - uint numAllChildren (readonly)
 - uint numAllChildren (readonly)
@@ -4494,8 +4553,8 @@ Properties:<br>
 - UIElement@ parent (readonly)
 - UIElement@ parent (readonly)
 - UIElement@ root (readonly)
 - UIElement@ root (readonly)
 - IntVector2 screenPosition (readonly)
 - IntVector2 screenPosition (readonly)
-- float derivedOpacity (readonly)
 - IntRect combinedScreenRect (readonly)
 - IntRect combinedScreenRect (readonly)
+- float derivedOpacity (readonly)
 - VariantMap vars (readonly)
 - VariantMap vars (readonly)
 - Texture@ texture
 - Texture@ texture
 - IntRect imageRect
 - IntRect imageRect
@@ -4619,8 +4678,6 @@ Properties:<br>
 - int indent
 - int indent
 - int indentSpacing
 - int indentSpacing
 - int indentWidth (readonly)
 - int indentWidth (readonly)
-- IntVector2 rotationPivot
-- float rotation
 - IntVector2 childOffset (readonly)
 - IntVector2 childOffset (readonly)
 - uint[] numChildren (readonly)
 - uint[] numChildren (readonly)
 - uint numAllChildren (readonly)
 - uint numAllChildren (readonly)
@@ -4628,8 +4685,8 @@ Properties:<br>
 - UIElement@ parent (readonly)
 - UIElement@ parent (readonly)
 - UIElement@ root (readonly)
 - UIElement@ root (readonly)
 - IntVector2 screenPosition (readonly)
 - IntVector2 screenPosition (readonly)
-- float derivedOpacity (readonly)
 - IntRect combinedScreenRect (readonly)
 - IntRect combinedScreenRect (readonly)
+- float derivedOpacity (readonly)
 - VariantMap vars (readonly)
 - VariantMap vars (readonly)
 - Texture@ texture
 - Texture@ texture
 - IntRect imageRect
 - IntRect imageRect
@@ -4747,8 +4804,6 @@ Properties:<br>
 - int indent
 - int indent
 - int indentSpacing
 - int indentSpacing
 - int indentWidth (readonly)
 - int indentWidth (readonly)
-- IntVector2 rotationPivot
-- float rotation
 - IntVector2 childOffset (readonly)
 - IntVector2 childOffset (readonly)
 - uint[] numChildren (readonly)
 - uint[] numChildren (readonly)
 - uint numAllChildren (readonly)
 - uint numAllChildren (readonly)
@@ -4756,8 +4811,8 @@ Properties:<br>
 - UIElement@ parent (readonly)
 - UIElement@ parent (readonly)
 - UIElement@ root (readonly)
 - UIElement@ root (readonly)
 - IntVector2 screenPosition (readonly)
 - IntVector2 screenPosition (readonly)
-- float derivedOpacity (readonly)
 - IntRect combinedScreenRect (readonly)
 - IntRect combinedScreenRect (readonly)
+- float derivedOpacity (readonly)
 - VariantMap vars (readonly)
 - VariantMap vars (readonly)
 - Texture@ texture
 - Texture@ texture
 - IntRect imageRect
 - IntRect imageRect

+ 85 - 57
Engine/Engine/APITemplates.h

@@ -813,7 +813,7 @@ static VariantMap& UIElementGetVars(UIElement* ptr)
 #endif
 #endif
 
 
 /// Template function for registering a class derived from UIElement.
 /// 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);
     RegisterSerializable<T>(engine, className);
     RegisterObjectConstructor<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(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 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 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 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 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);
     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 SetFixedWidth(int)", asMETHOD(T, SetFixedWidth), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void SetFixedHeight(int)", asMETHOD(T, SetFixedHeight), 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 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, "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, "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);
     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, "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, "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, "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_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, "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, "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, "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, "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, "void set_width(int)", asMETHOD(T, SetWidth), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "int get_width() const", asMETHOD(T, GetWidth), 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, "void set_height(int)", asMETHOD(T, SetHeight), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "int get_height() const", asMETHOD(T, GetHeight), 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, "void set_horizontalAlignment(HorizontalAlignment)", asMETHOD(T, SetHorizontalAlignment), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "HorizontalAlignment get_horizontalAlignment() const", asMETHOD(T, GetHorizontalAlignment), 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, "void set_verticalAlignment(VerticalAlignment)", asMETHOD(T, SetVerticalAlignment), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "VerticalAlignment get_verticalAlignment() const", asMETHOD(T, GetVerticalAlignment), 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_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, "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);
     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, "bool get_bringToFront() const", asMETHOD(T, SetBringToFront), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void set_bringToBack(bool)", asMETHOD(T, SetBringToBack), 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, "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, "void set_sortChildren(bool)", asMETHOD(T, SetSortChildren), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "bool get_sortChildren() const", asMETHOD(T, GetSortChildren), 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, "void set_useDerivedOpacity(bool)", asMETHOD(T, SetUseDerivedOpacity), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "bool get_useDerivedOpacity() const", asMETHOD(T, GetUseDerivedOpacity), 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, "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_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, "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, "void set_defaultStyle(XMLFile@+)", asMETHOD(T, SetDefaultStyle), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "XMLFile@+ get_defaultStyle()", asMETHOD(T, GetDefaultStyle), 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_numChildren() const", asFUNCTION(UIElementGetNumChildrenNonRecursive), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod(className, "uint get_numAllChildren() const", asFUNCTION(UIElementGetNumChildrenRecursive), 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, "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_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_parent() const", asMETHOD(T, GetParent), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "UIElement@+ get_root() const", asMETHOD(T, GetRoot), 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, "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);
     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 "ListView.h"
 #include "ScrollBar.h"
 #include "ScrollBar.h"
 #include "Slider.h"
 #include "Slider.h"
+#include "Sprite.h"
 #include "Text.h"
 #include "Text.h"
 #include "UI.h"
 #include "UI.h"
 #include "Window.h"
 #include "Window.h"
@@ -103,6 +104,30 @@ static void RegisterBorderImage(asIScriptEngine* engine)
     RegisterBorderImage<BorderImage>(engine, "BorderImage");
     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)
 static void RegisterCursor(asIScriptEngine* engine)
 {
 {
     engine->RegisterEnum("CursorShape");
     engine->RegisterEnum("CursorShape");
@@ -516,6 +541,7 @@ void RegisterUIAPI(asIScriptEngine* engine)
     RegisterFont(engine);
     RegisterFont(engine);
     RegisterUIElement(engine);
     RegisterUIElement(engine);
     RegisterBorderImage(engine);
     RegisterBorderImage(engine);
+    RegisterSprite(engine);
     RegisterButton(engine);
     RegisterButton(engine);
     RegisterCheckBox(engine);
     RegisterCheckBox(engine);
     RegisterCursor(engine);
     RegisterCursor(engine);

+ 3 - 3
Engine/Graphics/Technique.cpp

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

+ 15 - 25
Engine/UI/BorderImage.cpp

@@ -31,17 +31,7 @@
 namespace Urho3D
 namespace Urho3D
 {
 {
 
 
-static const char* blendModes[] =
-{
-    "Replace",
-    "Add",
-    "Multiply",
-    "Alpha",
-    "AddAlpha",
-    "PreMultiply",
-    "InvDestAlpha",
-    0
-};
+extern const char* blendModeNames[];
 
 
 template<> BlendMode Variant::Get<BlendMode>() const
 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_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);
     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);
     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);
     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)
 void BorderImage::GetBatches(PODVector<UIBatch>& batches, PODVector<UIQuad>& quads, const IntRect& currentScissor)
 {
 {
     GetBatches(batches, quads, currentScissor, hovering_ || selected_ ? hoverOffset_ : IntVector2::ZERO);
     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 ||
     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)
         color_[C_BOTTOMLEFT].a_ < 1.0f || color_[C_BOTTOMRIGHT].a_ < 1.0f)
         allOpaque = false;
         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
     // Calculate size of the inner rect, and texture dimensions of the inner rect
     int x = GetIndentWidth();
     int x = GetIndentWidth();
@@ -211,4 +190,15 @@ void BorderImage::GetBatches(PODVector<UIBatch>& batches, PODVector<UIQuad>& qua
     hovering_ = false;
     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.
     /// Return UI rendering batches.
     virtual void GetBatches(PODVector<UIBatch>& batches, PODVector<UIQuad>& quads, const IntRect& currentScissor);
     virtual void GetBatches(PODVector<UIBatch>& batches, PODVector<UIQuad>& quads, const IntRect& currentScissor);
     
     
-    /// Set textures.
+    /// Set texture.
     void SetTexture(Texture* texture);
     void SetTexture(Texture* texture);
     /// Set part of texture to use as the image.
     /// Set part of texture to use as the image.
     void SetImageRect(const IntRect& rect);
     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();
     unsigned initialSize = quads.Size();
     const IntVector2& offset = shapeInfos_[shape_].hotSpot_;
     const IntVector2& offset = shapeInfos_[shape_].hotSpot_;
+    Vector2 floatOffset((float)offset.x_, (float)offset.y_);
     
     
     BorderImage::GetBatches(batches, quads, currentScissor);
     BorderImage::GetBatches(batches, quads, currentScissor);
     for (unsigned i = initialSize; i < quads.Size(); ++i)
     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];
         bool visible = enable && expanded[indent - 1];
         item->SetVisible(visible);
         item->SetVisible(visible);
 
 
-        if (indent >= expanded.Size())
+        if (indent >= (int)expanded.Size())
             expanded.Resize(indent + 1);
             expanded.Resize(indent + 1);
         expanded[indent] = visible && GetItemExpanded(item);
         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
     // Hovering or whole selection batch
     if ((hovering_ && hoverColor_.a_ > 0.0) || (selected_ && selectionColor_.a_ > 0.0f))
     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_ :
         batch.AddQuad(*this, 0, 0, GetWidth(), GetHeight(), 0, 0, 0, 0, selected_ && selectionColor_.a_ > 0.0f ? selectionColor_ :
             hoverColor_);
             hoverColor_);
         UIBatch::AddOrMerge(batch, batches);
         UIBatch::AddOrMerge(batch, batches);
@@ -113,7 +113,7 @@ void Text::GetBatches(PODVector<UIBatch>& batches, PODVector<UIQuad>& quads, con
     // Partial selection batch
     // Partial selection batch
     if (!selected_ && selectionLength_ && charSizes_.Size() >= selectionStart_ + selectionLength_ && selectionColor_.a_ > 0.0f)
     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 currentStart = charPositions_[selectionStart_];
         IntVector2 currentEnd = currentStart;
         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)
         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]);
             batch.AddQuad(pageQuads[page]);
             UIBatch::AddOrMerge(batch, batches);
             UIBatch::AddOrMerge(batch, batches);
         }
         }
@@ -525,12 +525,16 @@ int Text::GetRowStartPosition(unsigned rowIndex) const
     switch (textAlignment_)
     switch (textAlignment_)
     {
     {
     case HA_LEFT:
     case HA_LEFT:
-        return ret;
+        break;
     case HA_CENTER:
     case HA_CENTER:
-        return ret + (GetSize().x_ - rowWidth) / 2;
+        ret += (GetSize().x_ - rowWidth) / 2;
+        break;
     case HA_RIGHT:
     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 "Shader.h"
 #include "ShaderVariation.h"
 #include "ShaderVariation.h"
 #include "Slider.h"
 #include "Slider.h"
+#include "Sprite.h"
 #include "Text.h"
 #include "Text.h"
 #include "Texture2D.h"
 #include "Texture2D.h"
 #include "UI.h"
 #include "UI.h"
@@ -94,9 +95,6 @@ UI::~UI()
 
 
 void UI::SetCursor(Cursor* cursor)
 void UI::SetCursor(Cursor* cursor)
 {
 {
-    if (!rootElement_)
-        return;
-    
     // Remove old cursor (if any) and set new
     // Remove old cursor (if any) and set new
     if (cursor_)
     if (cursor_)
     {
     {
@@ -118,9 +116,6 @@ void UI::SetCursor(Cursor* cursor)
 
 
 void UI::SetFocusElement(UIElement* element)
 void UI::SetFocusElement(UIElement* element)
 {
 {
-    if (!rootElement_)
-        return;
-    
     using namespace FocusChanged;
     using namespace FocusChanged;
     
     
     VariantMap eventData;
     VariantMap eventData;
@@ -165,9 +160,6 @@ void UI::SetFocusElement(UIElement* element)
 
 
 void UI::Clear()
 void UI::Clear()
 {
 {
-    if (!rootElement_)
-        return;
-    
     rootElement_->RemoveAllChildren();
     rootElement_->RemoveAllChildren();
     if (cursor_)
     if (cursor_)
         rootElement_->AddChild(cursor_);
         rootElement_->AddChild(cursor_);
@@ -339,7 +331,8 @@ void UI::Render()
         }
         }
         
         
         graphics_->SetShaders(vs, ps);
         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))
         if (graphics_->NeedParameterUpdate(SP_CAMERA, this))
             graphics_->SetShaderParameter(VSP_VIEWPROJ, projection);
             graphics_->SetShaderParameter(VSP_VIEWPROJ, projection);
         if (graphics_->NeedParameterUpdate(SP_MATERIAL, this))
         if (graphics_->NeedParameterUpdate(SP_MATERIAL, this))
@@ -427,9 +420,6 @@ void UI::SetNonFocusedMouseWheel(bool nonFocusedMouseWheel)
 
 
 UIElement* UI::GetElementAt(const IntVector2& position, bool activeOnly)
 UIElement* UI::GetElementAt(const IntVector2& position, bool activeOnly)
 {
 {
-    if (!rootElement_)
-        return 0;
-    
     UIElement* result = 0;
     UIElement* result = 0;
     GetElementAt(result, rootElement_, position, activeOnly);
     GetElementAt(result, rootElement_, position, activeOnly);
     return result;
     return result;
@@ -447,9 +437,6 @@ UIElement* UI::GetFocusElement() const
 
 
 UIElement* UI::GetFrontElement() const
 UIElement* UI::GetFrontElement() const
 {
 {
-    if (!rootElement_)
-        return 0;
-    
     const Vector<SharedPtr<UIElement> >& rootChildren = rootElement_->GetChildren();
     const Vector<SharedPtr<UIElement> >& rootChildren = rootElement_->GetChildren();
     int maxPriority = M_MIN_INT;
     int maxPriority = M_MIN_INT;
     UIElement* front = 0;
     UIElement* front = 0;
@@ -542,14 +529,14 @@ void UI::GetBatches(UIElement* element, IntRect currentScissor)
             int currentPriority = (*i)->GetPriority();
             int currentPriority = (*i)->GetPriority();
             while (j != children.End() && (*j)->GetPriority() == currentPriority)
             while (j != children.End() && (*j)->GetPriority() == currentPriority)
             {
             {
-                if (IsVisible(*j, currentScissor))
+                if ((*j)->IsWithinScissor(currentScissor))
                     (*j)->GetBatches(batches_, quads_, currentScissor);
                     (*j)->GetBatches(batches_, quads_, currentScissor);
                 ++j;
                 ++j;
             }
             }
             // Now recurse into the children
             // Now recurse into the children
             while (i != j)
             while (i != j)
             {
             {
-                if (IsVisible(*i, currentScissor))
+                if ((*i)->IsVisible())
                     GetBatches(*i, currentScissor);
                     GetBatches(*i, currentScissor);
                 ++i;
                 ++i;
             }
             }
@@ -560,29 +547,15 @@ void UI::GetBatches(UIElement* element, IntRect currentScissor)
     {
     {
         while (i != children.End())
         while (i != children.End())
         {
         {
+            if ((*i)->IsWithinScissor(currentScissor))
+                (*i)->GetBatches(batches_, quads_, currentScissor);
             if ((*i)->IsVisible())
             if ((*i)->IsVisible())
-            {
-                if (IsVisible(*i, currentScissor))
-                    (*i)->GetBatches(batches_, quads_, currentScissor);
                 GetBatches(*i, currentScissor);
                 GetBatches(*i, currentScissor);
-            }
             ++i;
             ++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)
 void UI::GetElementAt(UIElement*& result, UIElement* current, const IntVector2& position, bool activeOnly)
 {
 {
     if (!current)
     if (!current)
@@ -1041,6 +1014,7 @@ void RegisterUILibrary(Context* context)
     
     
     UIElement::RegisterObject(context);
     UIElement::RegisterObject(context);
     BorderImage::RegisterObject(context);
     BorderImage::RegisterObject(context);
+    Sprite::RegisterObject(context);
     Button::RegisterObject(context);
     Button::RegisterObject(context);
     CheckBox::RegisterObject(context);
     CheckBox::RegisterObject(context);
     Cursor::RegisterObject(context);
     Cursor::RegisterObject(context);

+ 0 - 2
Engine/UI/UI.h

@@ -97,8 +97,6 @@ private:
     void Update(float timeStep, UIElement* element);
     void Update(float timeStep, UIElement* element);
     /// Generate batches from an UI element recursively.
     /// Generate batches from an UI element recursively.
     void GetBatches(UIElement* element, IntRect currentScissor);
     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.
     /// Return UI element at screen position recursively.
     void GetElementAt(UIElement*& result, UIElement* current, const IntVector2& position, bool activeOnly);
     void GetElementAt(UIElement*& result, UIElement* current, const IntVector2& position, bool activeOnly);
     /// Return the first element in hierarchy that can alter focus.
     /// Return the first element in hierarchy that can alter focus.

+ 84 - 54
Engine/UI/UIBatch.cpp

@@ -22,6 +22,7 @@
 
 
 #include "Precompiled.h"
 #include "Precompiled.h"
 #include "Graphics.h"
 #include "Graphics.h"
+#include "Matrix3x4.h"
 #include "ShaderVariation.h"
 #include "ShaderVariation.h"
 #include "Texture.h"
 #include "Texture.h"
 #include "UIElement.h"
 #include "UIElement.h"
@@ -31,6 +32,12 @@
 namespace Urho3D
 namespace Urho3D
 {
 {
 
 
+#ifdef USE_OPENGL
+static const Vector2 posAdjust(Vector2::ZERO);
+#else
+static const Vector2 posAdjust(0.5f, 0.5f);
+#endif
+
 UIQuad::UIQuad() :
 UIQuad::UIQuad() :
     defined_(false)
     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);
         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;
     leftUV_ = texOffsetX;
     topUV_ = texOffsetY;
     topUV_ = texOffsetY;
     rightUV_ = texOffsetX + (texWidth ? texWidth : width);
     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;
     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)
 unsigned UIQuad::GetInterpolatedColor(const UIElement& element, int x, int y)
 {
 {
     const IntVector2& size = element.GetSize();
     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),
     blendMode_(blendMode),
     scissor_(scissor),
     scissor_(scissor),
     texture_(texture),
     texture_(texture),
@@ -119,9 +175,6 @@ UIBatch::UIBatch(const Matrix3x4& transform, BlendMode blendMode, const IntRect&
 
 
 void UIBatch::Begin(PODVector<UIQuad>* quads)
 void UIBatch::Begin(PODVector<UIQuad>* quads)
 {
 {
-    if (!quads)
-        return;
-    
     quads_ = quads;
     quads_ = quads;
     quadStart_ = quads_->Size();
     quadStart_ = quads_->Size();
     quadCount_ = 0;
     quadCount_ = 0;
@@ -129,7 +182,7 @@ void UIBatch::Begin(PODVector<UIQuad>* quads)
 
 
 void UIBatch::AddQuad(const PODVector<UIQuad>& quads)
 void UIBatch::AddQuad(const PODVector<UIQuad>& quads)
 {
 {
-    if (!quads_ || quads.Empty())
+    if (quads.Empty())
         return;
         return;
     
     
     *quads_ += quads;
     *quads_ += quads;
@@ -138,9 +191,6 @@ void UIBatch::AddQuad(const PODVector<UIQuad>& quads)
 
 
 void UIBatch::AddQuad(UIQuad quad)
 void UIBatch::AddQuad(UIQuad quad)
 {
 {
-    if (!quads_)
-        return;
-    
     if (quad.defined_)
     if (quad.defined_)
     {
     {
         quads_->Push(quad);
         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,
 void UIBatch::AddQuad(const UIElement& element, int x, int y, int width, int height, int texOffsetX, int texOffsetY, int texWidth,
     int texHeight, bool tiled)
     int texHeight, bool tiled)
 {
 {
-    if (!quads_)
-        return;
-
     if (!(element.HasColorGradient() || element.GetDerivedColor().ToUInt() & 0xff000000))
     if (!(element.HasColorGradient() || element.GetDerivedColor().ToUInt() & 0xff000000))
         return; // No gradient and alpha is 0, so do not add the quads
         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);
         AddQuad(element, x, y, width, height, texOffsetX, texOffsetY, texWidth, texHeight);
         return;
         return;
     }
     }
-
+    
     int tileX = 0;
     int tileX = 0;
     int tileY = 0;
     int tileY = 0;
     int tileW = 0;
     int tileW = 0;
     int tileH = 0;
     int tileH = 0;
-
+    
     while (tileY < height)
     while (tileY < height)
     {
     {
         tileX = 0;
         tileX = 0;
         tileH = Min(height - tileY, texHeight);
         tileH = Min(height - tileY, texHeight);
-
+        
         while (tileX < width)
         while (tileX < width)
         {
         {
             tileW = Min(width - tileX, texWidth);
             tileW = Min(width - tileX, texWidth);
-
+            
             AddQuad(element, x + tileX, y + tileY, tileW, tileH, texOffsetX, texOffsetY, tileW, tileH);
             AddQuad(element, x + tileX, y + tileY, tileW, tileH, texOffsetX, texOffsetY, tileW, tileH);
-
+            
             tileX += tileW;
             tileX += tileW;
         }
         }
-
+        
         tileY += tileH;
         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,
 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)
     int texHeight, const Color& color)
 {
 {
-    if (!quads_)
-        return;
-    
     Color derivedColor(color.r_, color.g_, color.b_, color.a_ * element.GetDerivedOpacity());
     Color derivedColor(color.r_, color.g_, color.b_, color.a_ * element.GetDerivedOpacity());
     AddQuad(UIQuad(element, x, y, width, height, texOffsetX, texOffsetY, texWidth, texHeight, &derivedColor));
     AddQuad(UIQuad(element, x, y, width, height, texOffsetX, texOffsetY, texWidth, texHeight, &derivedColor));
 }
 }
 
 
 bool UIBatch::Merge(const UIBatch& batch)
 bool UIBatch::Merge(const UIBatch& batch)
 {
 {
-    if (memcmp(&batch.transform_, &transform_, sizeof transform_) ||
-        batch.blendMode_ != blendMode_ ||
+    if (batch.blendMode_ != blendMode_ ||
         batch.scissor_ != scissor_ ||
         batch.scissor_ != scissor_ ||
         batch.texture_ != texture_ ||
         batch.texture_ != texture_ ||
         batch.quads_ != quads_ ||
         batch.quads_ != quads_ ||
@@ -226,12 +269,6 @@ void UIBatch::UpdateGeometry(Graphics* graphics, void* lockedData)
     if (!quadCount_)
     if (!quadCount_)
         return;
         return;
     
     
-    #ifdef USE_OPENGL
-    Vector2 posAdjust(Vector2::ZERO);
-    #else
-    Vector2 posAdjust(0.5f, 0.5f);
-    #endif
-    
     float* dest = (float*)lockedData;
     float* dest = (float*)lockedData;
     
     
     if (texture_)
     if (texture_)
@@ -241,34 +278,31 @@ void UIBatch::UpdateGeometry(Graphics* graphics, void* lockedData)
         for (unsigned i = 0; i < quadCount_; ++i)
         for (unsigned i = 0; i < quadCount_; ++i)
         {
         {
             const UIQuad& quad = quads_->At(quadStart_ + 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++;
             *((unsigned*)dest) = quad.topLeftColor_; dest++;
             *dest++ = topLeftUV.x_; *dest++ = topLeftUV.y_;
             *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++;
             *((unsigned*)dest) = quad.topRightColor_; dest++;
             *dest++ = bottomRightUV.x_; *dest++ = topLeftUV.y_;
             *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++;
             *((unsigned*)dest) = quad.bottomLeftColor_; dest++;
             *dest++ = topLeftUV.x_; *dest++ = bottomRightUV.y_;
             *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++;
             *((unsigned*)dest) = quad.topRightColor_; dest++;
             *dest++ = bottomRightUV.x_; *dest++ = topLeftUV.y_;
             *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++;
             *((unsigned*)dest) = quad.bottomRightColor_; dest++;
             *dest++ = bottomRightUV.x_; *dest++ = bottomRightUV.y_;
             *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++;
             *((unsigned*)dest) = quad.bottomLeftColor_; dest++;
             *dest++ = topLeftUV.x_; *dest++ = bottomRightUV.y_;
             *dest++ = topLeftUV.x_; *dest++ = bottomRightUV.y_;
         }
         }
@@ -278,32 +312,28 @@ void UIBatch::UpdateGeometry(Graphics* graphics, void* lockedData)
         for (unsigned i = 0; i < quadCount_; ++i)
         for (unsigned i = 0; i < quadCount_; ++i)
         {
         {
             const UIQuad& quad = quads_->At(quadStart_ + 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++;
             *((unsigned*)dest) = quad.topLeftColor_; dest++;
             dest += 2; // Jump over unused UV coordinates
             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++;
             *((unsigned*)dest) = quad.topRightColor_; dest++;
             dest += 2;
             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++;
             *((unsigned*)dest) = quad.bottomLeftColor_; dest++;
             dest += 2;
             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++;
             *((unsigned*)dest) = quad.topRightColor_; dest++;
             dest += 2;
             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++;
             *((unsigned*)dest) = quad.bottomRightColor_; dest++;
             dest += 2;
             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++;
             *((unsigned*)dest) = quad.bottomLeftColor_; dest++;
             dest += 2;
             dest += 2;
         }
         }

+ 16 - 15
Engine/UI/UIBatch.h

@@ -24,7 +24,6 @@
 
 
 #include "Color.h"
 #include "Color.h"
 #include "GraphicsDefs.h"
 #include "GraphicsDefs.h"
-#include "Matrix3x4.h"
 #include "Rect.h"
 #include "Rect.h"
 
 
 namespace Urho3D
 namespace Urho3D
@@ -32,6 +31,7 @@ namespace Urho3D
 
 
 class PixelShader;
 class PixelShader;
 class Graphics;
 class Graphics;
+class Matrix3x4;
 class ShaderVariation;
 class ShaderVariation;
 class Texture;
 class Texture;
 class UIElement;
 class UIElement;
@@ -44,18 +44,21 @@ struct UIQuad
     /// Construct.
     /// Construct.
     UIQuad(const UIElement& element, int x, int y, int width, int height, int texOffsetX, int texOffsetY,
     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);
         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.
     /// Return an interpolated color for an UI element.
     static unsigned GetInterpolatedColor(const UIElement& element, int x, int y);
     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.
     /// Left texture coordinate.
     short leftUV_;
     short leftUV_;
     /// Top texture coordinate.
     /// Top texture coordinate.
@@ -82,8 +85,8 @@ class UIBatch
 public:
 public:
     /// Construct with defaults.
     /// Construct with defaults.
     UIBatch();
     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.
     /// Begin adding quads.
     void Begin(PODVector<UIQuad>* quads);
     void Begin(PODVector<UIQuad>* quads);
@@ -107,8 +110,6 @@ public:
     /// Add or merge a batch.
     /// Add or merge a batch.
     static void AddOrMerge(const UIBatch& batch, PODVector<UIBatch>& batches);
     static void AddOrMerge(const UIBatch& batch, PODVector<UIBatch>& batches);
     
     
-    /// Transform matrix.
-    Matrix3x4 transform_;
     /// Blending mode.
     /// Blending mode.
     BlendMode blendMode_;
     BlendMode blendMode_;
     /// Scissor rectangle.
     /// Scissor rectangle.

+ 117 - 93
Engine/UI/UIElement.cpp

@@ -37,7 +37,7 @@
 namespace Urho3D
 namespace Urho3D
 {
 {
 
 
-static const char* horizontalAlignments[] =
+const char* horizontalAlignments[] =
 {
 {
     "Left",
     "Left",
     "Center",
     "Center",
@@ -45,7 +45,7 @@ static const char* horizontalAlignments[] =
     0
     0
 };
 };
 
 
-static const char* verticalAlignments[] =
+const char* verticalAlignments[] =
 {
 {
     "Top",
     "Top",
     "Center",
     "Center",
@@ -126,12 +126,12 @@ UIElement::UIElement(Context* context) :
     layoutMode_(LM_FREE),
     layoutMode_(LM_FREE),
     layoutSpacing_(0),
     layoutSpacing_(0),
     layoutBorder_(IntRect::ZERO),
     layoutBorder_(IntRect::ZERO),
-    rotationPivot_(IntVector2::ZERO),
-    rotation_(0.0f),
     resizeNestingLevel_(0),
     resizeNestingLevel_(0),
     layoutNestingLevel_(0),
     layoutNestingLevel_(0),
     layoutMinSize_(0),
     layoutMinSize_(0),
     indent_(0),
     indent_(0),
+    indentSpacing_(16),
+    positionDirty_(true),
     position_(IntVector2::ZERO),
     position_(IntVector2::ZERO),
     size_(IntVector2::ZERO),
     size_(IntVector2::ZERO),
     minSize_(IntVector2::ZERO),
     minSize_(IntVector2::ZERO),
@@ -140,12 +140,10 @@ UIElement::UIElement(Context* context) :
     horizontalAlignment_(HA_LEFT),
     horizontalAlignment_(HA_LEFT),
     verticalAlignment_(VA_TOP),
     verticalAlignment_(VA_TOP),
     opacity_(1.0f),
     opacity_(1.0f),
-    positionDirty_(true),
     opacityDirty_(true),
     opacityDirty_(true),
     derivedColorDirty_(true),
     derivedColorDirty_(true),
     sortOrderDirty_(false),
     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);
     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", GetIndent, SetIndent, int, 0, AM_FILE);
     ACCESSOR_ATTRIBUTE(UIElement, VAR_INT, "Indent Spacing", GetIndentSpacing, SetIndentSpacing, int, 16, 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);
     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;
     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)
 void UIElement::OnHover(const IntVector2& position, const IntVector2& screenPosition, int buttons, int qualifiers, Cursor* cursor)
 {
 {
     if (cursor && cursor->IsVisible())
     if (cursor && cursor->IsVisible())
@@ -735,16 +791,6 @@ void UIElement::SetIndentSpacing(int indentSpacing)
     indentSpacing_ = Max(indentSpacing, 0);
     indentSpacing_ = Max(indentSpacing, 0);
 }
 }
 
 
-void UIElement::SetRotationPivot(const IntVector2& pivot)
-{
-    rotationPivot_ = pivot;
-}
-
-void UIElement::SetRotation(float angle)
-{
-    rotation_ = angle;
-}
-
 void UIElement::UpdateLayout()
 void UIElement::UpdateLayout()
 {
 {
     if (layoutMode_ == LM_FREE || layoutNestingLevel_)
     if (layoutMode_ == LM_FREE || layoutNestingLevel_)
@@ -963,14 +1009,43 @@ void UIElement::InsertChild(unsigned index, UIElement* element)
     element->parent_ = this;
     element->parent_ = this;
     element->MarkDirty();
     element->MarkDirty();
     UpdateLayout();
     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)
 void UIElement::RemoveChild(UIElement* element, unsigned index)
 {
 {
+    UIElement* root = GetRoot();
+    
     for (unsigned i = index; i < children_.Size(); ++i)
     for (unsigned i = index; i < children_.Size(); ++i)
     {
     {
         if (children_[i] == element)
         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();
             element->Detach();
             children_.Erase(i);
             children_.Erase(i);
             UpdateLayout();
             UpdateLayout();
@@ -991,8 +1066,25 @@ void UIElement::RemoveChildAtIndex(unsigned index)
 
 
 void UIElement::RemoveAllChildren()
 void UIElement::RemoveAllChildren()
 {
 {
+    UIElement* root = GetRoot();
+    
     for (Vector<SharedPtr<UIElement> >::Iterator i = children_.Begin(); i < children_.End(); )
     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();
         (*i++)->Detach();
+    }
     children_.Clear();
     children_.Clear();
 }
 }
 
 
@@ -1018,58 +1110,6 @@ void UIElement::SetInternal(bool enable)
     internal_ = 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
 float UIElement::GetDerivedOpacity() const
 {
 {
     if (!useDerivedOpacity_)
     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
 void UIElement::GetBatchesWithOffset(IntVector2& offset, PODVector<UIBatch>& batches, PODVector<UIQuad>& quads, IntRect
     currentScissor)
     currentScissor)
 {
 {
+    Vector2 floatOffset((float)offset.x_, (float)offset.y_);
     unsigned initialSize = quads.Size();
     unsigned initialSize = quads.Size();
+    
     GetBatches(batches, quads, currentScissor);
     GetBatches(batches, quads, currentScissor);
     for (unsigned i = initialSize; i < quads.Size(); ++i)
     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);
     AdjustScissor(currentScissor);

+ 11 - 23
Engine/UI/UIElement.h

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

+ 16 - 0
Engine/UI/UIEvents.h

@@ -207,4 +207,20 @@ EVENT(E_FILESELECTED, FileSelected)
     PARAM(P_OK, Ok);                        // bool
     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
+}
+
 }
 }