Browse Source

New branch for component based UI system.

aster2013 11 years ago
parent
commit
529a58a610

+ 77 - 0
Bin/Data/Scenes/UIX-Test.xml

@@ -0,0 +1,77 @@
+<?xml version="1.0"?>
+<scene id="1">
+	<attribute name="Name" value="" />
+	<attribute name="Time Scale" value="1" />
+	<attribute name="Smoothing Constant" value="50" />
+	<attribute name="Snap Threshold" value="5" />
+	<attribute name="Elapsed Time" value="261.324" />
+	<attribute name="Next Replicated Node ID" value="1" />
+	<attribute name="Next Replicated Component ID" value="5" />
+	<attribute name="Next Local Node ID" value="16777219" />
+	<attribute name="Next Local Component ID" value="16777221" />
+	<attribute name="Variables" />
+	<attribute name="Variable Names" value="" />
+	<component type="Octree" id="1" />
+	<component type="DebugRenderer" id="2" />
+	<component type="UIRoot" id="3" />
+	<component type="Renderer2D" id="4" />
+	<node id="16777216">
+		<attribute name="Is Enabled" value="true" />
+		<attribute name="Name" value="" />
+		<attribute name="Position" value="0 0 0" />
+		<attribute name="Rotation" value="1 0 0 0" />
+		<attribute name="Scale" value="1 1 1" />
+		<attribute name="Variables" />
+		<component type="UIRect" id="16777216">
+			<attribute name="Layout Mode" value="Anchor" />
+			<attribute name="Left Anchor Pivot" value="Left" />
+			<attribute name="Right Anchor Pivot" value="Right" />
+			<attribute name="Top Anchor Pivot" value="Top" />
+			<attribute name="Bottom Anchor Pivot" value="Bottom" />
+			<attribute name="Left Offset" value="0" />
+			<attribute name="Right Offset" value="0" />
+			<attribute name="Right Offset" value="0" />
+			<attribute name="Bottom Offset" value="0" />
+		</component>
+		<component type="UIImage" id="16777217">
+			<attribute name="Sprite" value="Sprite2D;Urho2D/GoldIcon/1.png" />
+			<attribute name="Draw Mode" value="Tiled" />
+		</component>
+		<node id="16777217">
+			<attribute name="Is Enabled" value="true" />
+			<attribute name="Name" value="" />
+			<attribute name="Position" value="0 0 0" />
+			<attribute name="Rotation" value="1 0 0 0" />
+			<attribute name="Scale" value="1 1 1" />
+			<attribute name="Variables" />
+			<component type="UIRect" id="16777218">
+				<attribute name="Size" value="4 2" />
+			</component>
+			<component type="UIImage" id="16777219">
+				<attribute name="Sprite" value="Sprite2D;Urho2D/Box.png" />
+				<attribute name="Draw Mode" value="Sliced" />
+				<attribute name="X Slice Size" value="9" />
+				<attribute name="Y Slice Size" value="9" />
+				<attribute name="Layer" value="1" />
+			</component>
+			<component type="UIText" id="16777220">
+				<attribute name="Font" value="Font;Fonts/BlueHighway.ttf" />
+				<attribute name="Font Size" value="32" />
+				<attribute name="Text" value="Hello UI X" />
+				<attribute name="Text Alignment" value="Center" />
+				<attribute name="Text Effect" value="Stroke" />
+				<attribute name="Effect Color" value="1 0 0 1" />
+				<attribute name="Layer" value="1" />
+				<attribute name="Order in Layer" value="1" />
+			</component>
+			<node id="16777218">
+				<attribute name="Is Enabled" value="true" />
+				<attribute name="Name" value="" />
+				<attribute name="Position" value="0 -3.94427 7.88854" />
+				<attribute name="Rotation" value="1 0 0 0" />
+				<attribute name="Scale" value="1 1 1" />
+				<attribute name="Variables" />
+			</node>
+		</node>
+	</node>
+</scene>

+ 1 - 1
Source/CMake/Modules/FindUrho3D.cmake

@@ -63,7 +63,7 @@ if (URHO3D_HOME)
     if (URHO3D_SOURCE_TREE)
         get_filename_component (URHO3D_SOURCE_TREE ${URHO3D_SOURCE_TREE} PATH)
         set (URHO3D_INCLUDE_DIRS ${URHO3D_SOURCE_TREE})
-        foreach (DIR Audio Container Core Engine Graphics Input IO LuaScript Math Navigation Network Physics Resource Scene Script UI Urho2D)
+        foreach (DIR Audio Container Core Engine Graphics Input IO LuaScript Math Navigation Network Physics Resource Scene Script UI UIX Urho2D)
             list (APPEND URHO3D_INCLUDE_DIRS ${URHO3D_SOURCE_TREE}/${DIR})     # Note: variable change to list context after this
         endforeach ()
         set (DIRS SDL/include)

+ 1 - 0
Source/Engine/CMakeLists.txt

@@ -81,6 +81,7 @@ endif ()
 
 if (URHO3D_URHO2D)
     list (APPEND SOURCES Urho2D)
+    list (APPEND SOURCES UIX)
 endif ()
 
 foreach (SOURCE ${SOURCES})

+ 3 - 0
Source/Engine/Engine/Engine.cpp

@@ -51,6 +51,7 @@
 #include "UI.h"
 #ifdef URHO3D_URHO2D
 #include "Urho2D.h"
+#include "UIX.h"
 #endif
 #include "WorkQueue.h"
 #include "XMLFile.h"
@@ -166,6 +167,8 @@ bool Engine::Initialize(const VariantMap& parameters)
 #ifdef URHO3D_URHO2D
     // 2D graphics library is dependent on 3D graphics library
     RegisterUrho2DLibrary(context_);
+    // UIX library is dependent on Urho2D library
+    RegisterUIXLibrary(context_);
 #endif
 
     // Start logging

+ 27 - 0
Source/Engine/UIX/CMakeLists.txt

@@ -0,0 +1,27 @@
+#
+# Copyright (c) 2008-2014 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.
+#
+
+# Define source files
+define_source_files (PARENT_SCOPE)
+
+# Define dependency libs
+set (ENGINE_INCLUDE_DIRS_ONLY ${ENGINE_INCLUDE_DIRS_ONLY} ../ThirdParty/FreeType/include PARENT_SCOPE)

+ 371 - 0
Source/Engine/UIX/UIImage.cpp

@@ -0,0 +1,371 @@
+//
+// Copyright (c) 2008-2014 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 "DebugRenderer.h"
+#include "Log.h"
+#include "Node.h"
+#include "Sprite2D.h"
+#include "Texture2D.h"
+#include "UIImage.h"
+#include "UIRect.h"
+#include "UIXEvents.h"
+
+#include "DebugNew.h"
+
+namespace Urho3D
+{
+
+extern const char* UIX_CATEGORY;
+
+static const char* uiImageDrawModes[] = 
+{
+    "Simple",
+    "Tiled",
+    "Sliced",
+    0
+};
+
+UIImage::UIImage(Context* context) :
+    Drawable2D(context),
+    color_(Color::WHITE),
+    drawMode_(UIIDM_SIMPLE),
+    xSliceSize_(4),
+    ySliceSize_(4)
+{
+}
+
+UIImage::~UIImage()
+{
+}
+
+void UIImage::RegisterObject(Context* context)
+{
+    context->RegisterFactory<UIImage>(UIX_CATEGORY);
+
+    ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
+    MIXED_ACCESSOR_ATTRIBUTE("Sprite", GetSpriteAttr, SetSpriteAttr, ResourceRef, ResourceRef(Sprite2D::GetTypeStatic()), AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE("Color", GetColor, SetColor, Color, Color::WHITE, AM_FILE);
+    ENUM_ACCESSOR_ATTRIBUTE("Draw Mode", GetDrawMode, SetDrawMode, UIImageDrawMode, uiImageDrawModes, UIIDM_SIMPLE, AM_FILE);
+    ACCESSOR_ATTRIBUTE("X Slice Size", GetXSliceSize, SetXSliceSize, int, 4, AM_FILE);
+    ACCESSOR_ATTRIBUTE("Y Slice Size", GetYSliceSize, SetYSliceSize, int, 4, AM_FILE);
+    COPY_BASE_ATTRIBUTES(Drawable2D);
+}
+
+void UIImage::SetSprite(Sprite2D* sprite)
+{
+    if (sprite == sprite_)
+        return;
+
+    sprite_ = sprite;
+
+    SetTexture(sprite_ ? sprite_->GetTexture() : 0);
+}
+
+void UIImage::SetColor(const Color& color)
+{
+    if (color == color_)
+        return;
+
+    color_ = color;
+
+    verticesDirty_ = true;
+}
+
+void UIImage::SetDrawMode(UIImageDrawMode mode)
+{
+    if (mode == drawMode_)
+        return;
+
+    drawMode_ = mode;
+    verticesDirty_ = true;
+}
+
+void UIImage::SetXSliceSize(int size)
+{
+    if (size < 0 || size == xSliceSize_)
+        return;
+
+    xSliceSize_ = size;
+
+    if (drawMode_ == UIIDM_SLICED)
+        verticesDirty_ = true;
+}
+
+void UIImage::SetYSliceSize(int size)
+{
+    if (size < 0 || size == ySliceSize_)
+        return;
+
+    ySliceSize_ = size;
+
+    if (drawMode_ == UIIDM_SLICED)
+        verticesDirty_ = true;
+}
+
+Sprite2D* UIImage::GetSprite() const
+{
+    return sprite_;
+}
+
+void UIImage::SetSpriteAttr(const ResourceRef& value)
+{
+    Sprite2D* sprite = Sprite2D::LoadFromResourceRef(this, value);
+    if (sprite)
+        SetSprite(sprite);
+}
+
+ResourceRef UIImage::GetSpriteAttr() const
+{
+    return Sprite2D::SaveToResourceRef(sprite_);
+}
+
+void UIImage::OnNodeSet(Node* node)
+{
+    Drawable2D::OnNodeSet(node);
+
+    if (node)
+    {
+        uiRect_ = node->GetComponent<UIRect>();
+        if (uiRect_)
+            SubscribeToEvent(uiRect_, E_UIRECTDIRTIED, HANDLER(UIImage, HandleRectDirtied));
+        else
+            LOGERROR("UIRect must by added first");
+    }
+}
+
+void UIImage::OnWorldBoundingBoxUpdate()
+{
+    boundingBox_.Clear();
+
+    if (uiRect_)
+    {
+        const Rect& rect = uiRect_->GetRect();
+        boundingBox_.Merge(rect.min_);
+        boundingBox_.Merge(rect.max_);
+    }
+
+    worldBoundingBox_ = boundingBox_;
+}
+
+void UIImage::UpdateVertices()
+{
+    if (!verticesDirty_)
+        return;
+
+    vertices_.Clear();
+
+    if (!uiRect_)
+        return;
+
+    if (!texture_ || !sprite_)
+        return;
+
+    const IntRect& rect = sprite_->GetRectangle();
+    if (rect.Width() == 0 || rect.Height() == 0)
+        return;
+
+    switch (drawMode_)
+    {
+    case UIIDM_SIMPLE:
+        UpdateVerticesSimpleMode();
+        break;
+    case UIIDM_TILED:
+        UpdateVerticesTiledMode();
+        break;
+    case UIIDM_SLICED:
+        UpdateVerticesSlicedMode();
+        break;
+    }
+
+    verticesDirty_ = false;
+}
+
+void UIImage::UpdateVerticesSimpleMode()
+{
+    float x = uiRect_->GetX();
+    float y = uiRect_->GetY();
+
+    const IntRect& intRect = sprite_->GetRectangle();
+    float halfWidth = (float)intRect.Width() * PIXEL_SIZE * 0.5f;
+    float halfHeight = (float)intRect.Height() * PIXEL_SIZE * 0.5f;
+
+    float left = x - halfWidth;
+    float right = x + halfWidth;
+    float top = y + halfHeight;
+    float bottom = y - halfHeight;
+    
+    float uLeft, uRight, vTop, vBottom;
+    GetSpriteTextureCoords(uLeft, uRight, vTop, vBottom);
+
+    AddQuad(left, right, top, bottom, uLeft, uRight, vTop, vBottom);
+}
+
+void UIImage::UpdateVerticesTiledMode()
+{
+    const Rect& rect = uiRect_->GetRect();
+    float xStart = rect.min_.x_;
+    float xEnd = rect.max_.x_;
+    float yStart = rect.min_.y_;
+    float yEnd = rect.max_.y_;
+
+    const IntRect& intRect = sprite_->GetRectangle();
+    float tileWidth = intRect.Width() * PIXEL_SIZE;
+    float tileHeight = intRect.Height() * PIXEL_SIZE;
+
+    float uLeft, uRight, vTop, vBottom;
+    GetSpriteTextureCoords(uLeft, uRight, vTop, vBottom);
+
+    for (float left = xStart; left < xEnd; left += tileWidth)
+    {
+        for (float top = yEnd; top > yStart; top -= tileHeight)
+        {
+            float right = left + tileWidth;
+            float bottom = top - tileHeight;
+
+            float uRight2 = uRight;
+            float vBottom2 = vBottom;
+
+            if (right > xEnd)
+            {
+                right = xEnd;
+                uRight2 = uLeft + (right - left) / tileWidth * (uRight - uLeft);
+            }
+
+            if (bottom < yStart)
+            {
+                bottom = yStart;
+                vBottom2 = vTop - (top - bottom) / tileHeight * (vTop - vBottom);
+            }
+
+            AddQuad(left, right, top, bottom, uLeft, uRight2, vTop, vBottom2);
+        }
+    }
+}
+
+void UIImage::UpdateVerticesSlicedMode()
+{
+    if (xSliceSize_ == 0 && ySliceSize_ == 0)
+    {
+        UpdateVerticesSimpleMode();
+        return;
+    }
+
+    const IntRect& intRect = sprite_->GetRectangle();
+    int xSliceSize = Min(xSliceSize_, intRect.Width() / 2);
+    int ySliceSize = Min(ySliceSize_, intRect.Height() / 2);
+
+    float left = uiRect_->GetLeft();
+    float right = uiRect_->GetRight();
+    float top = uiRect_->GetTop();
+    float bottom = uiRect_->GetBottom();
+
+    float xDelta = (float)xSliceSize * PIXEL_SIZE;
+    float yDelta = (float)ySliceSize * PIXEL_SIZE;
+
+    float uLeft, uRight, vTop, vBottom;
+    GetSpriteTextureCoords(uLeft, uRight, vTop, vBottom);
+
+    float uDelta = (float)xSliceSize / (float)texture_->GetWidth();
+    float vDelta = (float)ySliceSize / (float)texture_->GetHeight();
+
+    float x1 = left + xDelta;
+    float x2 = right - xDelta;
+    float y1 = top - yDelta;
+    float y2 = bottom + yDelta;
+
+    float u1 = uLeft + uDelta;
+    float u2 = uRight - uDelta;
+    float v1 = vTop + vDelta;
+    float v2 = vBottom - vDelta;
+
+    AddQuad(left, x1   , top, y1, uLeft, u1    , vTop, v1);
+    AddQuad(x1  , x2   , top, y1, u1   , u2    , vTop, v1);
+    AddQuad(x2  , right, top, y1, u2   , uRight, vTop, v1);
+
+    AddQuad(left, x1   , y1, y2, uLeft, u1    , v1, v2);
+    AddQuad(x1  , x2   , y1, y2, u1   , u2    , v1, v2);
+    AddQuad(x2  , right, y1, y2, u2   , uRight, v1, v2);
+
+    AddQuad(left, x1   , y2, bottom, uLeft, u1    , v2, vBottom);
+    AddQuad(x1  , x2   , y2, bottom, u1   , u2    , v2, vBottom);
+    AddQuad(x2  , right, y2, bottom, u2   , uRight, v2, vBottom);
+}
+
+void UIImage::GetSpriteTextureCoords(float& left, float& right, float& top, float& bottom) const
+{ 
+    Texture2D* texture = GetTexture();
+    float invTexW = 1.0f / (float)texture->GetWidth();
+    float invTexH = 1.0f / (float)texture->GetHeight();
+
+    const IntRect& intRect = sprite_->GetRectangle();
+    left = (float)intRect.left_ * invTexW;
+    right = (float)intRect.right_ * invTexW;
+    top = (float)intRect.top_ * invTexH;
+    bottom = (float)intRect.bottom_ * invTexH;
+}
+
+void UIImage::AddQuad(float left, float right, float top, float bottom, float uLeft, float uRight, float vTop, float vBottom)
+{
+    if (right - left <= 0.005f || top - bottom <= 0.005f)
+        return;
+
+    /*
+    V1---------V2
+    |         / |
+    |       /   |
+    |     /     |
+    |   /       |
+    | /         |
+    V0---------V3
+    */
+    Vertex2D vertex0;
+    Vertex2D vertex1;
+    Vertex2D vertex2;
+    Vertex2D vertex3;
+
+    vertex0.position_ = Vector3(left, bottom);
+    vertex1.position_ = Vector3(left, top);
+    vertex2.position_ = Vector3(right, top);
+    vertex3.position_ = Vector3(right, bottom);
+
+    vertex0.uv_ = Vector2(uLeft, vBottom);
+    vertex1.uv_ = Vector2(uLeft, vTop);
+    vertex2.uv_ = Vector2(uRight, vTop);
+    vertex3.uv_ = Vector2(uRight, vBottom);
+
+    vertex0.color_ = vertex1.color_ = vertex2.color_  = vertex3.color_ = color_.ToUInt();
+
+    vertices_.Push(vertex0);
+    vertices_.Push(vertex1);
+    vertices_.Push(vertex2);
+    vertices_.Push(vertex3);
+}
+
+void UIImage::HandleRectDirtied(StringHash eventType, VariantMap& eventData)
+{
+    worldBoundingBoxDirty_ = true;
+    verticesDirty_ = true;
+}
+
+}

+ 115 - 0
Source/Engine/UIX/UIImage.h

@@ -0,0 +1,115 @@
+//
+// Copyright (c) 2008-2014 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 "Drawable2D.h"
+
+namespace Urho3D
+{
+
+class Sprite2D;
+class UIRect;
+
+/// UI image draw mode.
+enum UIImageDrawMode
+{
+    UIIDM_SIMPLE = 0,
+    UIIDM_TILED,
+    UIIDM_SLICED,
+};
+
+/// UI drawable component.
+class URHO3D_API UIImage : public Drawable2D
+{
+    OBJECT(UIImage);
+
+public:
+    /// Construct.
+    UIImage(Context* scontext);
+    /// Destruct.
+    virtual ~UIImage();
+    /// Register object factory.
+    static void RegisterObject(Context* context);
+
+    /// Set sprite.
+    void SetSprite(Sprite2D* sprite);
+    /// Set color.
+    void SetColor(const Color& color);
+    /// Set draw mode.
+    void SetDrawMode(UIImageDrawMode mode);
+    /// Set X slice size.
+    void SetXSliceSize(int size);
+    /// Set Y slice size.
+    void SetYSliceSize(int size);
+
+    /// Return sprite.
+    Sprite2D* GetSprite() const;
+    /// Return color.
+    const Color& GetColor() const { return color_; }
+    /// Return draw mode.
+    UIImageDrawMode GetDrawMode() const { return drawMode_; }
+    /// Return X slice size.
+    int GetXSliceSize() const { return xSliceSize_;}
+    /// Return Y slice size.
+    int GetYSliceSize() const { return ySliceSize_;}
+
+    /// Set sprite attribute.
+    void SetSpriteAttr(const ResourceRef& value);
+    /// Return sprite attribute.
+    ResourceRef GetSpriteAttr() const;
+
+private:
+    /// Handle node being assigned.
+    virtual void OnNodeSet(Node* node);
+    /// Recalculate the world-space bounding box.
+    virtual void OnWorldBoundingBoxUpdate();
+    /// Update vertices.
+    virtual void UpdateVertices();
+    /// Update vertices simple mode.
+    void UpdateVerticesSimpleMode();
+    /// Update vertices tiled mode.
+    void UpdateVerticesTiledMode();
+    /// Update vertices sliced mode.
+    void UpdateVerticesSlicedMode();
+    /// Return sprite texture coords.
+    void GetSpriteTextureCoords(float& left, float& right, float& top, float& bottom) const;
+    /// Add quad.
+    void AddQuad(float left, float right, float top, float bottom, float uLeft, float uRight, float vTop, float vBottom);
+    /// handle rect changed.
+    void HandleRectDirtied(StringHash eventType, VariantMap& eventData);
+
+    /// UIRect
+    WeakPtr<UIRect> uiRect_;
+    /// Sprite.
+    SharedPtr<Sprite2D> sprite_;
+    /// Color.
+    Color color_;
+    /// Draw mode.
+    UIImageDrawMode drawMode_;
+    /// X slice size.
+    int xSliceSize_;
+    /// Y slice size.
+    int ySliceSize_;
+};
+
+}

+ 414 - 0
Source/Engine/UIX/UIRect.cpp

@@ -0,0 +1,414 @@
+//
+// Copyright (c) 2008-2014 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 "DebugRenderer.h"
+#include "Log.h"
+#include "Node.h"
+#include "Scene.h"
+#include "UIRect.h"
+#include "UIRoot.h"
+#include "UIXEvents.h"
+
+#include "DebugNew.h"
+
+namespace Urho3D
+{
+
+extern const char* UIX_CATEGORY;
+
+
+static const char* uiLayoutModes[] =
+{
+    "Free",
+    "Anchor",
+    0
+};
+
+static const char* uiXAnchorPivots[] = 
+{
+    "Center",
+    "Left",
+    "Right",
+    0
+};
+
+static const char* uiYAnchorPivots[] = 
+{
+    "Center",
+    "Top",
+    "Bottom",
+    0
+};
+
+UIRect::UIRect(Context* context) :
+    Component(context),
+    layoutMode_(UILM_FREE),
+    position_(0.0f, 0.0f),
+    size_(2.0f, 2.0f),
+    leftAnchorPivot_(AP_XCENTER),
+    rightAnchorPivot_(AP_XCENTER),
+    topAnchorPivot_(AP_YCENTER),
+    bottomAnchorPivot_(AP_YCENTER),
+    leftOffset_(1.0f),
+    rightOffset_(1.0f),
+    topOffset_(1.0f),
+    bottomOffset_(1.0f),
+    handleNodeDirty_(true),
+    rectangleDirty_(true)
+{
+}
+
+UIRect::~UIRect()
+{
+}
+
+void UIRect::RegisterObject(Context* context)
+{
+    context->RegisterFactory<UIRect>(UIX_CATEGORY);
+
+    ENUM_ACCESSOR_ATTRIBUTE("Layout Mode", GetLayoutMode, SetLayoutMode, UILayoutMode, uiLayoutModes, UILM_FREE, AM_FILE);
+    ACCESSOR_ATTRIBUTE("Center", GetPosition, SetPosition, Vector2, Vector2::ZERO, AM_FILE);
+    ACCESSOR_ATTRIBUTE("Size", GetSize, SetSize, Vector2, Vector2(2.0f, 2.0f), AM_FILE);
+    ENUM_ACCESSOR_ATTRIBUTE("Left Anchor Pivot", GetLeftAnchorPivot, SetLeftAnchorPivot, UIXAnchorPivot, uiXAnchorPivots, AP_XCENTER, AM_FILE);
+    ENUM_ACCESSOR_ATTRIBUTE("Right Anchor Pivot", GetRightAnchorPivot, SetRightAnchorPivot, UIXAnchorPivot, uiXAnchorPivots, AP_XCENTER, AM_FILE);
+    ENUM_ACCESSOR_ATTRIBUTE("Top Anchor Pivot", GetTopAnchorPivot, SetTopAnchorPivot, UIYAnchorPivot, uiYAnchorPivots, AP_YCENTER, AM_FILE);
+    ENUM_ACCESSOR_ATTRIBUTE("Bottom Anchor Pivot", GetBottomAnchorPivot, SetBottomAnchorPivot, UIYAnchorPivot, uiYAnchorPivots, AP_YCENTER, AM_FILE);    
+    ACCESSOR_ATTRIBUTE("Left Offset", GetLeftOffset, SetLeftOffset, float, 1.0f, AM_FILE);
+    ACCESSOR_ATTRIBUTE("Right Offset", GetRightOffset, SetRightOffset, float, 1.0f, AM_FILE);
+    ACCESSOR_ATTRIBUTE("Right Offset", GetTopOffset, SetTopOffset, float, 1.0f, AM_FILE);
+    ACCESSOR_ATTRIBUTE("Bottom Offset", GetBottomOffset, SetBottomOffset, float, 1.0f, AM_FILE);    
+}
+
+void UIRect::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
+{
+    // Draw parent rectangle
+    if (parent_)
+        parent_->DrawRect(debug, depthTest);
+
+    DrawRect(debug, depthTest);
+}
+
+void UIRect::SetPosition(const Vector2& position)
+{
+    if (layoutMode_ == UILM_ANCHOR)
+        return;
+
+    if (position == position_)
+        return;
+
+    position_ = position;
+    
+    // Update node's position for editor
+    if (node_)
+    {
+        handleNodeDirty_ = false;
+        node_->SetWorldPosition2D(GetRect().Center());
+        handleNodeDirty_ = true;
+    }
+
+    MarkRectDirty();
+}
+
+void UIRect::SetSize(const Vector2& size)
+{
+    if (layoutMode_ == UILM_ANCHOR)
+        return;
+    
+    if (size.x_ < 0.0f || size.y_ < 0.0f || size == size_)
+        return;
+
+    size_ = size;
+    
+    MarkRectDirty();
+}
+
+void UIRect::SetLayoutMode(UILayoutMode layoutMode)
+{
+    if (layoutMode == layoutMode_)
+        return;
+
+    Rect rect = GetRect();    
+    
+    layoutMode_ = layoutMode;
+
+    if (layoutMode_ == UILM_FREE)
+        CalculateFreeModeParameters(rect);
+    else
+        CalculateAnchorModeParameters(rect);
+}
+
+void UIRect::SetLeftAnchorPivot(UIXAnchorPivot anchorPivot)
+{
+    if (layoutMode_ == UILM_FREE)
+        return;
+
+    if (anchorPivot == leftAnchorPivot_)
+        return;
+
+    Rect rect = GetRect();    
+    leftAnchorPivot_ = anchorPivot;
+    CalculateAnchorModeParameters(rect);
+}
+
+void UIRect::SetRightAnchorPivot(UIXAnchorPivot anchorPivot)
+{
+    if (layoutMode_ == UILM_FREE)
+        return;
+    
+    if (anchorPivot == rightAnchorPivot_)
+        return;
+
+    Rect rect = GetRect();    
+    rightAnchorPivot_ = anchorPivot;
+    CalculateAnchorModeParameters(rect);
+}
+
+void UIRect::SetTopAnchorPivot(UIYAnchorPivot  anchorPivot)
+{
+    if (anchorPivot == topAnchorPivot_)
+        return;
+
+    Rect rect = GetRect();    
+    topAnchorPivot_ = anchorPivot;
+    CalculateAnchorModeParameters(rect);
+}
+
+void UIRect::SetBottomAnchorPivot(UIYAnchorPivot anchorPivot)
+{
+    if (layoutMode_ == UILM_FREE)
+        return;
+    
+    if (anchorPivot == bottomAnchorPivot_)
+        return;
+
+    Rect rect = GetRect();    
+    bottomAnchorPivot_ = anchorPivot;
+    CalculateAnchorModeParameters(rect);
+}
+
+void UIRect::SetLeftOffset(float offset)
+{
+    if (layoutMode_ == UILM_FREE)
+        return;
+
+    if (offset == leftOffset_)
+        return;
+
+    leftOffset_ = offset;
+    MarkRectDirty();
+}
+
+void UIRect::SetRightOffset(float offset)
+{
+    if (layoutMode_ == UILM_FREE)
+        return;
+
+    if (offset == rightOffset_)
+        return;
+
+    rightOffset_ = offset;
+    MarkRectDirty();
+}
+
+void UIRect::SetBottomOffset(float offset)
+{
+    if (layoutMode_ == UILM_FREE)
+        return;
+    
+    if (offset == bottomOffset_)
+        return;
+
+    bottomOffset_ = offset;
+    MarkRectDirty();
+}
+
+void UIRect::SetTopOffset(float offset)
+{
+    if (layoutMode_ == UILM_FREE)
+        return;
+    
+    if (offset == topOffset_)
+        return;
+
+    topOffset_ = offset;
+    MarkRectDirty();
+}
+
+const Rect& UIRect::GetRect() const
+{
+    if (!rectangleDirty_)
+        return rectangle_;
+
+    if (layoutMode_ == UILM_FREE)
+        rectangle_ = CalculateRectFreeMode();
+    else
+        rectangle_ = CalculateRectAnchorMode();
+
+    rectangleDirty_ = false;
+
+    return rectangle_;
+}
+
+void UIRect::OnNodeSet(Node* node)
+{
+    Component::OnNodeSet(node);
+
+    if (node)
+    {
+        root_ = GetScene()->GetOrCreateComponent<UIRoot>();
+
+        UIRect* rect = node->GetComponent<UIRect>();
+        if (rect && rect != this)
+        {
+            LOGERROR("Can not add more than one UIRect in node");
+            return;
+        }
+
+        node->AddListener(this);
+
+        Node* parent = node->GetParent();
+        if (parent)
+        {
+            parent_ = parent->GetComponent<UIRect>();
+            MarkRectDirty();
+        }
+    }
+    else
+    {
+        parent_ = 0;
+        MarkRectDirty();
+    }
+}
+
+void UIRect::OnMarkedDirty(Node* node)
+{
+    if (layoutMode_ == UILM_FREE && handleNodeDirty_)
+    {
+        position_ -= node_->GetPosition2D();
+        MarkRectDirty();
+    }
+}
+
+void UIRect::MarkRectDirty()
+{
+    rectangleDirty_ = true;
+
+    using namespace UIRectDirtied;
+    VariantMap& eventData = GetEventDataMap();
+    eventData[P_UIRECT] = this;
+    SendEvent(E_UIRECTDIRTIED, eventData);
+
+    MarkChildrenRectDirty();
+}
+
+void UIRect::MarkChildrenRectDirty()
+{
+    if (!node_)
+        return;
+
+    const Vector<SharedPtr<Node> >& children = node_->GetChildren();
+    for (unsigned i = 0; i < children.Size(); ++i)
+    {
+        UIRect* uiRect = children[i]->GetComponent<UIRect>();
+        if (uiRect)
+            uiRect->MarkRectDirty();
+    }
+}
+
+Rect UIRect::CalculateRectFreeMode() const
+{
+    if (!node_)
+        return Rect::ZERO;
+
+    Vector2 center = node_->GetPosition2D();
+    if (parent_)
+        center += parent_->GetRect().Center();
+    
+    Vector2 halfSize = size_ * 0.5f; 
+    return Rect(center - halfSize, center + halfSize);
+}
+
+void UIRect::CalculateFreeModeParameters(const Rect& rect)
+{
+    position_ = rect.Center();
+    if (parent_)
+        position_ -= parent_->GetRect().Center();
+    size_ = rect.Size();
+}
+
+Rect UIRect::CalculateRectAnchorMode() const
+{
+    UIRect* parent = parent_;
+    if (!parent)
+        parent = root_;
+
+    float parentXs[] = { parent->GetX(), parent->GetLeft(), parent->GetRight() };
+    float parentYs[] = { parent->GetY(), parent->GetTop(), parent->GetBottom() };
+
+    float left = parentXs[leftAnchorPivot_] + leftOffset_;
+    float right = parentXs[rightAnchorPivot_] + rightOffset_;
+    float top = parentYs[topAnchorPivot_] + topOffset_;
+    float bottom = parentYs[bottomAnchorPivot_] + bottomOffset_;
+
+    return Rect(left, bottom, right, top);
+}
+
+void UIRect::CalculateAnchorModeParameters(const Rect& rect)
+{
+    UIRect* parent = parent_;
+    if (!parent)
+        parent = root_;
+
+    float parentXs[] = { parent->GetX(), parent->GetLeft(), parent->GetRight() };
+    float parentYs[] = { parent->GetY(), parent->GetTop(), parent->GetBottom() };
+
+    leftOffset_ = rect.min_.x_ - parentXs[leftAnchorPivot_];
+    rightOffset_ = rect.max_.x_ - parentXs[rightAnchorPivot_];
+    topOffset_ = rect.max_.y_ - parentYs[topAnchorPivot_];
+    bottomOffset_ = rect.min_.y_ - parentYs[bottomAnchorPivot_];
+}
+
+void UIRect::DrawRect(DebugRenderer* debug, bool depthTest)
+{
+    if (!debug)
+        return;
+
+    const Rect& drawRect = GetRect();
+    if (drawRect.Size().x_ < M_EPSILON || drawRect.Size().y_ < M_EPSILON)
+        return;
+
+    const Vector3 leftTop(drawRect.min_.x_, drawRect.max_.y_);
+    const Vector3 leftBottom(drawRect.min_);
+    const Vector3 rightTop(drawRect.max_);
+    const Vector3 rightBottom(drawRect.max_.x_, drawRect.min_.y_);
+
+    const Color& color = Color::GREEN;
+
+    debug->AddLine(leftTop, rightTop, color, depthTest);
+    debug->AddLine(rightTop, rightBottom, color, depthTest);
+    debug->AddLine(rightBottom, leftBottom, color, depthTest);
+    debug->AddLine(leftBottom, leftTop, color, depthTest);
+}
+
+}

+ 190 - 0
Source/Engine/UIX/UIRect.h

@@ -0,0 +1,190 @@
+//
+// Copyright (c) 2008-2014 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 "Component.h"
+
+namespace Urho3D
+{
+
+class UIRoot;
+
+/// Layout mode.
+enum UILayoutMode
+{
+    UILM_FREE = 0,
+    UILM_ANCHOR,
+};
+
+/// X anchor pivot.
+enum UIXAnchorPivot
+{
+    AP_XCENTER = 0,
+    AP_LEFT,
+    AP_RIGHT,
+};
+
+/// Y anchor pivot.
+enum UIYAnchorPivot
+{
+    AP_YCENTER = 0,
+    AP_TOP,
+    AP_BOTTOM,
+};
+
+/// UI rectangle component.
+class URHO3D_API UIRect : public Component
+{
+    OBJECT(UIRect);
+
+public:
+    /// Construct.
+    UIRect(Context* scontext);
+    /// Destruct.
+    virtual ~UIRect();
+    /// Register object factory.
+    static void RegisterObject(Context* context);
+
+    /// Visualize the component as debug geometry.
+    virtual void DrawDebugGeometry(DebugRenderer* debug, bool depthTest);
+
+    /// Set layout mode.
+    void SetLayoutMode(UILayoutMode layoutMode);
+    /// Set position (center).
+    void SetPosition(const Vector2& position);
+    /// Set size.
+    void SetSize(const Vector2& size);    
+    /// Set left anchor pivot.
+    void SetLeftAnchorPivot(UIXAnchorPivot anchorPivot);
+    /// Set right anchor pivot.
+    void SetRightAnchorPivot(UIXAnchorPivot anchorPivot);
+    /// Set top anchor pivot.
+    void SetTopAnchorPivot(UIYAnchorPivot anchorPivot);
+    /// Set bottom anchor pivot.
+    void SetBottomAnchorPivot(UIYAnchorPivot anchorPivot);
+    /// Set left offset.
+    void SetLeftOffset(float offset);
+    /// Set right offset.
+    void SetRightOffset(float offset);
+    /// Set top offset.
+    void SetTopOffset(float offset);
+    /// Set bottom offset.
+    void SetBottomOffset(float offset);
+
+    /// Return layout mode.
+    UILayoutMode GetLayoutMode() const { return layoutMode_; }
+    /// Return position (center).
+    const Vector2& GetPosition() const { return position_; }
+    /// Return size.
+    const Vector2& GetSize() const { return size_; }
+    /// Return left anchor pivot.
+    UIXAnchorPivot GetLeftAnchorPivot() const { return leftAnchorPivot_; }
+    /// Return right anchor pivot
+    UIXAnchorPivot GetRightAnchorPivot() const { return rightAnchorPivot_; }
+    /// Return top anchor pivot
+    UIYAnchorPivot GetTopAnchorPivot() const { return topAnchorPivot_; }
+    /// Return bottom anchor pivot.
+    UIYAnchorPivot GetBottomAnchorPivot() const { return bottomAnchorPivot_; }
+    /// Return left offset.
+    float GetLeftOffset() const { return leftOffset_; }
+    /// Return right offset.
+    float GetRightOffset() const { return rightOffset_; }
+    /// Return top offset.
+    float GetTopOffset() const { return topOffset_; }
+    /// Return bottom offset.
+    float GetBottomOffset() const { return bottomOffset_; }
+
+    /// Return rectangle.
+    const Rect& GetRect() const;
+    /// Get x.
+    float GetX() const { return GetRect().Center().x_; }
+    /// Get y.
+    float GetY() const { return GetRect().Center().y_; }
+    /// Return left.
+    float GetLeft() const { return GetRect().min_.x_; }
+    /// Return right.
+    float GetRight() const { return GetRect().max_.x_; }
+    /// Return top.
+    float GetTop() const { return GetRect().max_.y_; }
+    /// Return bottom.
+    float GetBottom() const { return GetRect().min_.y_; }
+    /// Return width.
+    float GetWidth() const { return GetRect().max_.x_ - GetRect().min_.x_; }
+    /// Return height.
+    float GetHeight() const { return GetRect().max_.y_ - GetRect().min_.y_; }
+
+protected:
+    /// Handle node being assigned.
+    virtual void OnNodeSet(Node* node);
+    /// Handle node transform being dirtied.
+    virtual void OnMarkedDirty(Node* node);
+    /// Mark rectangle dirty.
+    void MarkRectDirty();
+    /// Mark children rectangle dirty.
+    void MarkChildrenRectDirty();
+    /// Calculate rectangle free mode.
+    Rect CalculateRectFreeMode() const;
+    /// Calculate free mode parameters.
+    void CalculateFreeModeParameters(const Rect& rect);
+    /// Calculate rectangle anchor mode.
+    Rect CalculateRectAnchorMode() const;
+    /// Calculate anchor offsets from rectangle.
+    void CalculateAnchorModeParameters(const Rect& rect);
+    /// Draw rectangle.
+    void DrawRect(DebugRenderer* debug, bool depthTest);
+
+    /// UIRoot.
+    WeakPtr<UIRoot> root_;
+    /// Parent.
+    WeakPtr<UIRect> parent_;
+    /// Layout mode.
+    UILayoutMode layoutMode_;
+    /// Position.
+    Vector2 position_;
+    /// Size.
+    Vector2 size_;
+    /// Left anchor pivot.
+    UIXAnchorPivot leftAnchorPivot_;
+    /// Right anchor pivot.
+    UIXAnchorPivot rightAnchorPivot_;
+    /// Top anchor pivot.
+    UIYAnchorPivot topAnchorPivot_;
+    /// Bottom anchor pivot.
+    UIYAnchorPivot bottomAnchorPivot_;    
+    /// Left offset.
+    float leftOffset_;
+    /// Right offset.
+    float rightOffset_;
+    /// Top offset.
+    float topOffset_;
+    /// Bottom offset.
+    float bottomOffset_;
+    /// Handle node dirty.
+    bool handleNodeDirty_;
+    /// Rectangle.
+    mutable Rect rectangle_;
+    /// Rectangle dirty flag.
+    mutable bool rectangleDirty_;
+};
+
+}

+ 102 - 0
Source/Engine/UIX/UIRoot.cpp

@@ -0,0 +1,102 @@
+//
+// Copyright (c) 2008-2014 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 "Graphics.h"
+#include "GraphicsEvents.h"
+#include "InputEvents.h"
+#include "Log.h"
+#include "Node.h"
+#include "UIRoot.h"
+
+#include "DebugNew.h"
+
+namespace Urho3D
+{
+
+extern URHO3D_API const float PIXEL_SIZE;
+
+UIRoot::UIRoot(Context* context) :
+    UIRect(context)
+{
+    SubscribeToEvent(E_SCREENMODE, HANDLER(UIRoot, HandleScreenMode));
+    SubscribeToEvent(E_MOUSEBUTTONDOWN, HANDLER(UIRoot, HandleMouseButtonDown));
+    SubscribeToEvent(E_MOUSEBUTTONUP, HANDLER(UIRoot, HandleMouseButtonUp));
+    SubscribeToEvent(E_MOUSEMOVE, HANDLER(UIRoot, HandleMouseMove));
+    // SubscribeToEvent(E_MOUSEWHEEL, HANDLER(UIRoot, HandleMouseWheel));
+    // SubscribeToEvent(E_TOUCHBEGIN, HANDLER(UIRoot, HandleTouchBegin));
+    // SubscribeToEvent(E_TOUCHEND, HANDLER(UIRoot, HandleTouchEnd));
+    // SubscribeToEvent(E_TOUCHMOVE, HANDLER(UIRoot, HandleTouchMove));
+    // SubscribeToEvent(E_KEYDOWN, HANDLER(UIRoot, HandleKeyDown));
+    // SubscribeToEvent(E_TEXTINPUT, HANDLER(UIRoot, HandleTextInput));
+    // SubscribeToEvent(E_DROPFILE, HANDLER(UIRoot, HandleDropFile));
+}
+
+UIRoot::~UIRoot()
+{
+}
+
+void UIRoot::RegisterObject(Context* context)
+{
+    context->RegisterFactory<UIRoot>();    
+}
+
+void UIRoot::OnNodeSet(Node* node)
+{
+    Graphics* graphics = GetSubsystem<Graphics>();
+    float width = (float)(graphics->GetWidth()) * PIXEL_SIZE;
+    float height = (float)(graphics->GetHeight()) * PIXEL_SIZE;
+    SetSize(Vector2(width, height));
+}
+
+void UIRoot::HandleScreenMode(StringHash eventType, VariantMap& eventData)
+{
+    using namespace ScreenMode;
+    
+    float width = ((float)eventData[P_WIDTH].GetInt()) * PIXEL_SIZE;
+    float height = ((float)eventData[P_HEIGHT].GetInt()) * PIXEL_SIZE;
+    SetSize(Vector2(width, height));
+}
+
+void UIRoot::HandleMouseButtonDown(StringHash eventType, VariantMap& eventData)
+{
+    using namespace MouseButtonDown;
+
+    // TODO:
+}
+
+void UIRoot::HandleMouseButtonUp(StringHash eventType, VariantMap& eventData)
+{
+    using namespace MouseButtonUp;
+
+    // TODO:
+}
+
+void UIRoot::HandleMouseMove(StringHash eventType, VariantMap& eventData)
+{
+    using namespace MouseMove;
+
+    // TODO:
+}
+
+}

+ 57 - 0
Source/Engine/UIX/UIRoot.h

@@ -0,0 +1,57 @@
+//
+// Copyright (c) 2008-2014 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 "UIRect.h"
+
+namespace Urho3D
+{
+
+/// UI root.
+class URHO3D_API UIRoot : public UIRect
+{
+    OBJECT(UIRoot);
+
+public:
+    /// Construct.
+    UIRoot(Context* scontext);
+    /// Destruct.
+    virtual ~UIRoot();
+    /// Register object factory.
+    static void RegisterObject(Context* context);
+
+private:
+    /// Handle node being assigned.
+    virtual void OnNodeSet(Node* node);
+    /// Handle screen mode.
+    void HandleScreenMode(StringHash eventType, VariantMap& eventData);
+    /// Handle mouse button down event.
+    void HandleMouseButtonDown(StringHash eventType, VariantMap& eventData);
+    /// Handle mouse button up event.
+    void HandleMouseButtonUp(StringHash eventType, VariantMap& eventData);
+    /// Handle mouse move event.
+    void HandleMouseMove(StringHash eventType, VariantMap& eventData);
+};
+
+}
+

+ 520 - 0
Source/Engine/UIX/UIText.cpp

@@ -0,0 +1,520 @@
+//
+// Copyright (c) 2008-2014 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 "Font.h"
+#include "FontFace.h"
+#include "Log.h"
+#include "Material.h"
+#include "Node.h"
+#include "Profiler.h"
+#include "ResourceCache.h"
+#include "Technique.h"
+#include "Texture2D.h"
+#include "UIRect.h"
+#include "UIText.h"
+#include "UIXEvents.h"
+
+#include "DebugNew.h"
+
+namespace Urho3D
+{
+
+static const float MIN_ROW_SPACING = 0.5f;
+
+extern const char* textEffects[];
+extern const char* horizontalAlignments[];
+extern const char* UIX_CATEGORY;
+
+void UITextLineInfo::Clear()
+{
+    lineWidth_ = 0.0f;
+    fontGlyphs_.Clear();
+    glyphAdvances_.Clear();    
+}
+
+void UITextLineInfo::Add(const FontGlyph* glyph, short kerning)
+{
+    fontGlyphs_.Push(glyph);
+    
+    float advance = (glyph->advanceX_ + kerning) * PIXEL_SIZE;
+    lineWidth_ += advance;
+    glyphAdvances_.Push(advance);
+}
+
+UIText::UIText(Context* context) :
+    Drawable2D(context),
+    usedInText3D_(false),
+    fontSize_(DEFAULT_FONT_SIZE),
+    textAlignment_(HA_LEFT),
+    rowSpacing_(1.0f),
+    color_(Color::WHITE),
+    selectionStart_(0),
+    selectionLength_(0),
+    selectionColor_(Color::TRANSPARENT),
+    textEffect_(TE_NONE),
+    effectColor_(Color::BLACK),
+    rowHeight_(0.0f),
+    numChars_(0)
+{
+}
+
+UIText::~UIText()
+{
+}
+
+void UIText::RegisterObject(Context* context)
+{
+    context->RegisterFactory<UIText>(UIX_CATEGORY);
+
+    MIXED_ACCESSOR_ATTRIBUTE("Font", GetFontAttr, SetFontAttr, ResourceRef, ResourceRef(Font::GetTypeStatic()), AM_FILE);
+    ACCESSOR_ATTRIBUTE("Font Size", GetFontSize, SetFontSize, int, DEFAULT_FONT_SIZE, AM_FILE);
+    ACCESSOR_ATTRIBUTE("Text", GetText, SetText, String, String::EMPTY, AM_FILE);
+    ENUM_ACCESSOR_ATTRIBUTE("Text Alignment", GetTextAlignment, SetTextAlignment, HorizontalAlignment, horizontalAlignments, HA_LEFT, AM_FILE);
+    ACCESSOR_ATTRIBUTE("Row Spacing", GetRowSpacing, SetRowSpacing, float, 1.0f, AM_FILE);
+    ACCESSOR_ATTRIBUTE("Color", GetColor, SetColor, Color, Color::WHITE, AM_FILE);
+    ACCESSOR_ATTRIBUTE("Selection Color", GetSelectionColor, SetSelectionColor, Color, Color::TRANSPARENT, AM_FILE);
+    ENUM_ACCESSOR_ATTRIBUTE("Text Effect", GetTextEffect, SetTextEffect, TextEffect, textEffects, TE_NONE, AM_FILE);
+    ACCESSOR_ATTRIBUTE("Effect Color", GetEffectColor, SetEffectColor, Color, Color::BLACK, AM_FILE);
+    COPY_BASE_ATTRIBUTES(Drawable2D);
+}
+
+bool UIText::SetFont(const String& fontName, int size)
+{
+    ResourceCache* cache = GetSubsystem<ResourceCache>();
+    return SetFont(cache->GetResource<Font>(fontName), size);
+}
+
+bool UIText::SetFont(Font* font, int size)
+{
+    if (!font)
+    {
+        LOGERROR("Null font for Text");
+        return false;
+    }
+
+    if (font != font_ || size != fontSize_)
+    {
+        font_ = font;
+        fontSize_ = Max(size, 1);
+
+        if (font_)
+        {
+            fontFace_ = font->GetFace(fontSize_);
+            SetTexture(fontFace_->GetTextures()[0]);
+            UpdateMaterial(true);
+        }
+        else
+        {
+            SetTexture(0);
+            SetCustomMaterial(0);
+        }
+        
+        UpdateText();
+        verticesDirty_ = true;
+    }
+
+    return true;
+}
+
+void UIText::SetFontSize(int fontSize)
+{
+    SetFont(font_, fontSize);
+}
+
+void UIText::SetText(const String& text)
+{
+    if (text == text_)
+        return;
+
+    text_ = text;
+    
+    UpdateText();
+    ValidateSelection();
+
+    verticesDirty_ = true;
+}
+
+void UIText::SetTextAlignment(HorizontalAlignment align)
+{
+    if (align == textAlignment_)
+        return;
+    
+    textAlignment_ = align;
+    verticesDirty_ = true;
+}
+
+void UIText::SetRowSpacing(float spacing)
+{
+    if (spacing == rowSpacing_)
+        return;
+
+    rowSpacing_ = Max(spacing, MIN_ROW_SPACING);
+    verticesDirty_ = true;
+}
+
+void UIText::SetColor(const Color& color)
+{
+    if (color == color_)
+        return;
+
+    color_ = color;
+
+    verticesDirty_ = true;
+}
+
+void UIText::SetSelection(unsigned start, unsigned length)
+{
+    if (start == selectionStart_ && length == selectionLength_)
+        return;
+
+    selectionStart_ = start;
+    selectionLength_ = length;
+
+    ValidateSelection();
+    verticesDirty_ = true;
+}
+
+void UIText::ClearSelection()
+{
+    selectionStart_ = 0;
+    
+    if (selectionLength_ != 0)
+    {
+        selectionLength_ = 0;
+        verticesDirty_ = true;
+    }
+}
+
+void UIText::SetSelectionColor(const Color& color)
+{
+    if (color == selectionColor_)
+        return;
+
+    selectionColor_ = color;
+
+    if (selectionLength_ != 0)
+        verticesDirty_ = true;
+}
+
+void UIText::SetTextEffect(TextEffect textEffect)
+{
+    if (textEffect == textEffect_)
+        return;
+
+    textEffect_ = textEffect;
+
+    UpdateMaterial();
+    verticesDirty_ = true;
+}
+
+void UIText::SetEffectColor(const Color& effectColor)
+{
+    if (effectColor == effectColor_)
+        return;
+
+    effectColor_ = effectColor;
+
+    if (textEffect_ != TE_NONE)
+        verticesDirty_ = true;
+}
+
+void UIText::SetFontAttr(const ResourceRef& value)
+{
+    ResourceCache* cache = GetSubsystem<ResourceCache>();
+    SetFont(cache->GetResource<Font>(value.name_), fontSize_);
+}
+
+ResourceRef UIText::GetFontAttr() const
+{
+    return GetResourceRef(font_, Font::GetTypeStatic());
+}
+
+void UIText::OnNodeSet(Node* node)
+{
+    Drawable2D::OnNodeSet(node);
+
+    if (node)
+    {
+        uiRect_ = node->GetComponent<UIRect>();
+        if (uiRect_)
+            SubscribeToEvent(uiRect_, E_UIRECTDIRTIED, HANDLER(UIText, HandleRectDirtied));
+        else
+            LOGERROR("UIRect must by added first");
+    }
+}
+
+void UIText::OnWorldBoundingBoxUpdate()
+{
+    boundingBox_.Clear();
+
+    if (uiRect_)
+    {
+        const Rect& rect = uiRect_->GetRect();
+        boundingBox_.Merge(rect.min_);
+        boundingBox_.Merge(rect.max_);
+    }
+
+    worldBoundingBox_ = boundingBox_;
+}
+
+void UIText::UpdateVertices()
+{
+    if (!verticesDirty_)
+        return;
+
+    vertices_.Clear();
+
+    if (textLineInfo_.Empty())
+        return;
+
+    TextEffect textEffect = font_->IsSDFFont() ? TE_NONE : textEffect_;
+    const Vector<SharedPtr<Texture2D> >& textures = fontFace_->GetTextures();
+
+    const Rect& rect = uiRect_->GetRect();
+    float totalHeight = textLineInfo_.Size() * rowHeight_;
+
+    unsigned color = color_.ToUInt();
+    unsigned effectColor = effectColor_.ToUInt();
+
+    for (unsigned i = 0; i < textLineInfo_.Size(); ++i)
+    {
+        const UITextLineInfo& lineInfo = textLineInfo_[i];
+        
+        float x;
+        switch (textAlignment_)
+        {
+        case HA_LEFT:
+            x = rect.min_.x_;
+            break;
+        case HA_CENTER:
+            x = rect.Center().x_ - lineInfo.lineWidth_ * 0.5f;
+            break;
+        case HA_RIGHT:
+            x = rect.max_.x_ - lineInfo.lineWidth_;
+            break;
+        }
+
+        float y = rect.Center().y_ + totalHeight * 0.5f - rowHeight_ * i;
+
+        switch (textEffect)
+        {
+        case TE_NONE:
+            UpdateTextLineVertices(lineInfo, x, y, color);
+            break;
+
+        case TE_SHADOW:
+            UpdateTextLineVertices(lineInfo, x + PIXEL_SIZE, y - PIXEL_SIZE, effectColor);
+            UpdateTextLineVertices(lineInfo, x, y, color);
+            break;
+
+        case TE_STROKE:
+            UpdateTextLineVertices(lineInfo, x - PIXEL_SIZE, y - PIXEL_SIZE, effectColor);
+            UpdateTextLineVertices(lineInfo, x             , y - PIXEL_SIZE, effectColor);
+            UpdateTextLineVertices(lineInfo, x + PIXEL_SIZE, y - PIXEL_SIZE, effectColor);
+
+            UpdateTextLineVertices(lineInfo, x - PIXEL_SIZE, y, effectColor);
+            UpdateTextLineVertices(lineInfo, x + PIXEL_SIZE, y, effectColor);
+            
+            UpdateTextLineVertices(lineInfo, x - PIXEL_SIZE, y + PIXEL_SIZE, effectColor);
+            UpdateTextLineVertices(lineInfo, x             , y + PIXEL_SIZE, effectColor);
+            UpdateTextLineVertices(lineInfo, x + PIXEL_SIZE, y + PIXEL_SIZE, effectColor);
+
+            UpdateTextLineVertices(lineInfo, x, y, color);
+            break;
+        }
+    }
+
+    verticesDirty_ = false;
+}
+
+void UIText::HandleRectDirtied(StringHash eventType, VariantMap& eventData)
+{
+    worldBoundingBoxDirty_ = true;
+    verticesDirty_ = true;
+}
+
+void UIText::UpdateMaterial(bool newFont)
+{
+    Material* material = GetCustomMaterial();
+    if (!material)
+    {
+        material = new Material(context_);
+        Technique* tech = new Technique(context_);
+        Pass* pass = tech->CreatePass(PASS_ALPHA);
+        pass->SetVertexShader("Text");
+        pass->SetPixelShader("Text");
+        pass->SetDepthWrite(false);
+
+        // Text alway use alpha blend mode
+        pass->SetBlendMode(BLEND_ALPHA);
+
+        material->SetTechnique(0, tech);
+        material->SetCullMode(CULL_NONE);
+
+        SetCustomMaterial(material);
+    }
+
+    material->SetTexture(TU_DIFFUSE, texture_);
+
+    if (newFont)
+    {
+        Technique* tech = material->GetTechnique(0);
+        Pass* pass = tech->GetPass(PASS_ALPHA);
+        if (font_ && font_->IsSDFFont())
+        {
+
+            switch (textEffect_)
+            {
+            case TE_NONE:
+                pass->SetPixelShaderDefines("SIGNED_DISTANCE_FIELD");
+                break;
+
+            case TE_SHADOW:
+                pass->SetPixelShaderDefines("SIGNED_DISTANCE_FIELD TEXT_EFFECT_SHADOW");
+                break;
+
+            case TE_STROKE:
+                pass->SetPixelShaderDefines("SIGNED_DISTANCE_FIELD TEXT_EFFECT_STROKE");
+                break;
+            }
+        }
+        else
+            pass->SetPixelShaderDefines("");
+    }    
+}
+
+void UIText::UpdateText()
+{
+    textLineInfo_.Clear();
+
+    // Convert to Unicode text
+    PODVector<unsigned> unicodeText;
+    for (unsigned i = 0; i < text_.Length();)
+        unicodeText.Push(text_.NextUTF8Char(i));
+
+    numChars_ = unicodeText.Size();
+
+    if (!fontFace_)
+        return;
+
+    rowHeight_ = fontFace_->GetRowHeight() * PIXEL_SIZE;
+
+    UITextLineInfo lineInfo;
+    for (unsigned i = 0; i < unicodeText.Size(); ++i)
+    {
+        unsigned c = unicodeText[i];
+        if (c != '\n')
+        {
+            const FontGlyph* glyph = fontFace_->GetGlyph(c);
+            if (glyph)
+                lineInfo.Add(glyph, i < unicodeText.Size() - 1 ? fontFace_->GetKerning(c, unicodeText[i + 1]) : 0);
+        }
+        else
+        {
+            textLineInfo_.Push(lineInfo);
+            lineInfo.Clear();
+        }
+    }
+
+    if (!lineInfo.fontGlyphs_.Empty())
+        textLineInfo_.Push(lineInfo);
+}
+
+void UIText::ValidateSelection()
+{
+    if (numChars_)
+    {
+        if (selectionStart_ >= numChars_)
+            selectionStart_ = numChars_ - 1;
+        if (selectionStart_ + selectionLength_ > numChars_)
+            selectionLength_ = numChars_ - selectionStart_;
+    }
+    else
+    {
+        selectionStart_ = 0;
+        selectionLength_ = 0;
+    }
+}
+
+
+void UIText::UpdateTextLineVertices(const UITextLineInfo& lineInfo, float x, float y, unsigned color)
+{
+    const PODVector<float>& advances = lineInfo.glyphAdvances_;
+    const PODVector<const FontGlyph*>& fontGlyphs = lineInfo.fontGlyphs_;
+
+    float invTexWidth = 1.0f / (float)texture_->GetWidth();
+    float invTexHeight = 1.0f / (float)texture_->GetHeight();
+
+    for (unsigned i = 0; i < fontGlyphs.Size(); ++i)
+    {
+        const FontGlyph& glyph = *fontGlyphs[i];
+        /*
+        V1---------V2
+        |         / |
+        |       /   |
+        |     /     |
+        |   /       |
+        | /         |
+        V0---------V3
+        */
+        Vertex2D vertex0;
+        Vertex2D vertex1;
+        Vertex2D vertex2;
+        Vertex2D vertex3;
+
+        float left = x + glyph.offsetX_ * PIXEL_SIZE;
+        float right = left + glyph.width_ * PIXEL_SIZE;
+        float top = y - glyph.offsetY_ * PIXEL_SIZE;
+        float bottom = top -  glyph.height_ * PIXEL_SIZE;
+
+        float uLeft = glyph.x_ * invTexWidth;
+        float uRight = (glyph.x_ + glyph.width_) * invTexWidth;
+        float vTop = glyph.y_ * invTexHeight;
+        float vBottom = (glyph.y_ + glyph.height_) * invTexHeight;
+
+        vertex0.position_ = Vector3(left, bottom);
+        vertex1.position_ = Vector3(left, top);
+        vertex2.position_ = Vector3(right, top);
+        vertex3.position_ = Vector3(right, bottom);
+
+        vertex0.uv_ = Vector2(uLeft, vBottom);
+        vertex1.uv_ = Vector2(uLeft, vTop);
+        vertex2.uv_ = Vector2(uRight, vTop);
+        vertex3.uv_ = Vector2(uRight, vBottom);
+
+        vertex0.color_ = color;
+        vertex1.color_ = color;
+        vertex2.color_ = color;
+        vertex3.color_ = color;
+
+        vertices_.Push(vertex0);
+        vertices_.Push(vertex1);
+        vertices_.Push(vertex2);
+        vertices_.Push(vertex3);
+
+        x += advances[i];
+    }
+}
+
+}

+ 183 - 0
Source/Engine/UIX/UIText.h

@@ -0,0 +1,183 @@
+//
+// Copyright (c) 2008-2014 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 "Drawable2D.h"
+#include "Text.h"
+
+namespace Urho3D
+{
+
+class UIRect;
+
+/// UI Line glyphs.
+struct UITextLineInfo
+{
+    /// Construct.
+    UITextLineInfo() : lineWidth_(0.0f)
+    {
+    }
+
+    /// Clear.
+    void Clear();
+    /// Add font flyph
+    void Add(const FontGlyph* glyph, short kerning);
+
+    /// Width.
+    float lineWidth_;
+    /// Font glyphs.
+    PODVector<const FontGlyph*> fontGlyphs_;
+    /// Advances;
+    PODVector<float> glyphAdvances_;
+};
+
+/// %Text %UI element.
+class URHO3D_API UIText : public Drawable2D
+{
+    OBJECT(UIText);
+
+public:
+    /// Construct.
+    UIText(Context* context);
+    /// Destruct.
+    virtual ~UIText();
+    /// Register object factory.
+    static void RegisterObject(Context* context);
+
+    /// Set font and font size and use signed distance field.
+    bool SetFont(const String& fontName, int size = DEFAULT_FONT_SIZE);
+    /// Set font and font size and use signed distance field.
+    bool SetFont(Font* font, int size = DEFAULT_FONT_SIZE);
+    /// Set font size.
+    void SetFontSize(int fontSize = DEFAULT_FONT_SIZE);
+    /// Set text. Text is assumed to be either ASCII or UTF8-encoded.
+    void SetText(const String& text);
+    /// Set row alignment.
+    void SetTextAlignment(HorizontalAlignment align);
+    /// Set row spacing, 1.0 for original font spacing.
+    void SetRowSpacing(float spacing);
+    /// Set color.
+    void SetColor(const Color& color);
+    /// Set selection. When length is not provided, select until the text ends.
+    void SetSelection(unsigned start, unsigned length = M_MAX_UNSIGNED);
+    /// Clear selection.
+    void ClearSelection();
+    /// Set selection background color. Color with 0 alpha (default) disables.
+    void SetSelectionColor(const Color& color);
+    /// Set text effect.
+    void SetTextEffect(TextEffect textEffect);
+    /// Set effect color.
+    void SetEffectColor(const Color& effectColor);
+
+    /// Return font.
+    Font* GetFont() const { return font_; }
+    /// Return font size.
+    int GetFontSize() const { return fontSize_; }
+    /// Return text.
+    const String& GetText() const { return text_; }
+    /// Return row alignment.
+    HorizontalAlignment GetTextAlignment() const { return textAlignment_; }
+    /// Return row spacing.
+    float GetRowSpacing() const { return rowSpacing_; }
+    /// Return color.
+    const Color& GetColor() const { return color_; }
+    /// Return selection start.
+    unsigned GetSelectionStart() const { return selectionStart_; }
+    /// Return selection length.
+    unsigned GetSelectionLength() const { return selectionLength_; }
+    /// Return selection background color.
+    const Color& GetSelectionColor() const { return selectionColor_; }
+    /// Return text effect.
+    TextEffect GetTextEffect() const { return textEffect_; }
+    /// Return effect color.
+    const Color& GetEffectColor() const { return effectColor_; }
+
+    /// Return number of characters.
+    unsigned GetNumChars() const { return numChars_; }
+    /// Return number of rows.
+    unsigned GetNumRows() const { return textLineInfo_.Size(); }
+    /// Return row width.
+    float GetRowWidth(unsigned row) const { return row < textLineInfo_.Size() ? textLineInfo_[row].lineWidth_ : 0.0f; }
+    /// Return row height.
+    float GetRowHeight() const { return rowHeight_; }
+
+    /// Set font attribute.
+    void SetFontAttr(const ResourceRef& value);
+    /// Return font attribute.
+    ResourceRef GetFontAttr() const;
+
+protected:
+    /// Handle node being assigned.
+    virtual void OnNodeSet(Node* node);
+    /// Recalculate the world-space bounding box.
+    virtual void OnWorldBoundingBoxUpdate();
+    /// Update vertices.
+    virtual void UpdateVertices();
+    /// handle rect changed.
+    void HandleRectDirtied(StringHash eventType, VariantMap& eventData);
+    /// Update material.
+    void UpdateMaterial(bool newFont = false);
+    /// Update text when text, font or spacing changed.
+    void UpdateText();
+    /// Validate text selection to be within the text.
+    void ValidateSelection();
+    /// Contruct batch.
+    void UpdateTextLineVertices(const UITextLineInfo& lineInfo, float x, float y, unsigned color);
+
+    /// UIRect
+    WeakPtr<UIRect> uiRect_;
+    /// Used in Text3D.
+    bool usedInText3D_;
+    /// Font.
+    SharedPtr<Font> font_;
+    /// Current face.
+    WeakPtr<FontFace> fontFace_;
+    /// Font size.
+    int fontSize_;
+    /// UTF-8 encoded text.
+    String text_;
+    /// Row alignment.
+    HorizontalAlignment textAlignment_;
+    /// Row spacing.
+    float rowSpacing_;
+    /// Color.
+    Color color_;
+    /// Selection background color.
+    Color selectionColor_;
+    /// Selection start.
+    unsigned selectionStart_;
+    /// Selection length.
+    unsigned selectionLength_;
+    /// Text effect.
+    TextEffect textEffect_;
+    /// Effect color.
+    Color effectColor_;
+    /// Number of characters.
+    unsigned numChars_;
+    /// Text line info.
+    Vector<UITextLineInfo> textLineInfo_;
+    /// Row height.
+    float rowHeight_;
+};
+
+}

+ 46 - 0
Source/Engine/UIX/UIX.cpp

@@ -0,0 +1,46 @@
+
+// Copyright (c) 2008-2014 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 "UIImage.h"
+#include "UIRect.h"
+#include "UIRoot.h"
+#include "UIText.h"
+#include "UIX.h"
+
+#include "DebugNew.h"
+
+namespace Urho3D
+{
+
+const char* UIX_CATEGORY = "UIX";
+
+void RegisterUIXLibrary(Context* context)
+{
+    UIRect::RegisterObject(context);
+    UIRoot::RegisterObject(context);
+    UIImage::RegisterObject(context);
+    UIText::RegisterObject(context);
+}
+
+}

+ 31 - 0
Source/Engine/UIX/UIX.h

@@ -0,0 +1,31 @@
+//
+// Copyright (c) 2008-2014 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
+
+namespace Urho3D
+{
+
+/// Register UIX library objects.
+void URHO3D_API RegisterUIXLibrary(Context* context);
+
+}

+ 34 - 0
Source/Engine/UIX/UIXEvents.h

@@ -0,0 +1,34 @@
+//
+// Copyright (c) 2008-2014 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
+
+namespace Urho3D
+{
+
+/// UIRect rect dirtied.
+EVENT(E_UIRECTDIRTIED, UIRectDirtied)
+{
+    PARAM(P_UIRECT, UIRect);                // UIRect
+}
+
+}