Browse Source

3D UI Feature

Josh Engebretson 8 years ago
parent
commit
2cb4ac0243

+ 65 - 138
Source/Atomic/UI/UI.cpp

@@ -62,6 +62,7 @@ using namespace tb;
 
 
 #include "UIRenderer.h"
 #include "UIRenderer.h"
 #include "UI.h"
 #include "UI.h"
+#include "UIView.h"
 #include "UIButton.h"
 #include "UIButton.h"
 #include "UITextField.h"
 #include "UITextField.h"
 #include "UIEditField.h"
 #include "UIEditField.h"
@@ -91,6 +92,7 @@ using namespace tb;
 #include "UIPromptWindow.h"
 #include "UIPromptWindow.h"
 #include "UIFinderWindow.h"
 #include "UIFinderWindow.h"
 #include "UIPulldownMenu.h"
 #include "UIPulldownMenu.h"
+#include "UIComponent.h"
 
 
 #include "SystemUI/SystemUI.h"
 #include "SystemUI/SystemUI.h"
 #include "SystemUI/SystemUIEvents.h"
 #include "SystemUI/SystemUIEvents.h"
@@ -108,12 +110,15 @@ void TBSystem::RescheduleTimer(double fire_time)
 
 
 }
 }
 
 
-
 namespace Atomic
 namespace Atomic
 {
 {
 
 
-WeakPtr<Context> UI::uiContext_;
+void RegisterUILibrary(Context* context)
+{
+    UIComponent::RegisterObject(context);
+}
 
 
+WeakPtr<Context> UI::uiContext_;
 
 
 UI::UI(Context* context) :
 UI::UI(Context* context) :
     Object(context),
     Object(context),
@@ -128,6 +133,8 @@ UI::UI(Context* context) :
     tooltipHoverTime_ (0.0f)
     tooltipHoverTime_ (0.0f)
 {
 {
 
 
+    RegisterUILibrary(context);
+
     SubscribeToEvent(E_EXITREQUESTED, ATOMIC_HANDLER(UI, HandleExitRequested));
     SubscribeToEvent(E_EXITREQUESTED, ATOMIC_HANDLER(UI, HandleExitRequested));
 
 
 }
 }
@@ -201,8 +208,6 @@ void UI::Initialize(const String& languageFile)
     assert(graphics->IsInitialized());
     assert(graphics->IsInitialized());
     graphics_ = graphics;
     graphics_ = graphics;
 
 
-    vertexBuffer_ = new VertexBuffer(context_);
-
     uiContext_ = context_;
     uiContext_ = context_;
 
 
     TBFile::SetReaderFunction(TBFileReader);
     TBFile::SetReaderFunction(TBFileReader);
@@ -224,21 +229,10 @@ void UI::Initialize(const String& languageFile)
     rootWidget_->SetSize(width, height);
     rootWidget_->SetSize(width, height);
     rootWidget_->SetVisibilility(tb::WIDGET_VISIBILITY_VISIBLE);
     rootWidget_->SetVisibilility(tb::WIDGET_VISIBILITY_VISIBLE);
 
 
-    SubscribeToEvent(E_MOUSEBUTTONDOWN, ATOMIC_HANDLER(UI, HandleMouseButtonDown));
-    SubscribeToEvent(E_MOUSEBUTTONUP, ATOMIC_HANDLER(UI, HandleMouseButtonUp));
-    SubscribeToEvent(E_MOUSEMOVE, ATOMIC_HANDLER(UI, HandleMouseMove));
-    SubscribeToEvent(E_MOUSEWHEEL, ATOMIC_HANDLER(UI, HandleMouseWheel));
-    SubscribeToEvent(E_KEYDOWN, ATOMIC_HANDLER(UI, HandleKeyDown));
-    SubscribeToEvent(E_KEYUP, ATOMIC_HANDLER(UI, HandleKeyUp));
-    SubscribeToEvent(E_TEXTINPUT, ATOMIC_HANDLER(UI, HandleTextInput));
     SubscribeToEvent(E_UPDATE, ATOMIC_HANDLER(UI, HandleUpdate));
     SubscribeToEvent(E_UPDATE, ATOMIC_HANDLER(UI, HandleUpdate));
     SubscribeToEvent(E_SCREENMODE, ATOMIC_HANDLER(UI, HandleScreenMode));
     SubscribeToEvent(E_SCREENMODE, ATOMIC_HANDLER(UI, HandleScreenMode));
     SubscribeToEvent(E_CONSOLECLOSED, ATOMIC_HANDLER(UI, HandleConsoleClosed));
     SubscribeToEvent(E_CONSOLECLOSED, ATOMIC_HANDLER(UI, HandleConsoleClosed));
-
-    SubscribeToEvent(E_TOUCHBEGIN, ATOMIC_HANDLER(UI, HandleTouchBegin));
-    SubscribeToEvent(E_TOUCHEND, ATOMIC_HANDLER(UI, HandleTouchEnd));
-    SubscribeToEvent(E_TOUCHMOVE, ATOMIC_HANDLER(UI, HandleTouchMove));
-
+    SubscribeToEvent(E_POSTUPDATE, ATOMIC_HANDLER(UI, HandlePostUpdate));
     SubscribeToEvent(E_RENDERUPDATE, ATOMIC_HANDLER(UI, HandleRenderUpdate));
     SubscribeToEvent(E_RENDERUPDATE, ATOMIC_HANDLER(UI, HandleRenderUpdate));
 
 
     // register the UIDragDrop subsystem (after we have subscribed to events, so it is processed after)
     // register the UIDragDrop subsystem (after we have subscribed to events, so it is processed after)
@@ -316,159 +310,94 @@ void UI::AddFont(const String& fontFile, const String& name)
     tb::g_font_manager->AddFontInfo(fontFile.CString(), name.CString());
     tb::g_font_manager->AddFontInfo(fontFile.CString(), name.CString());
 }
 }
 
 
-void UI::Render(VertexBuffer* buffer, const PODVector<UIBatch>& batches, unsigned batchStart, unsigned batchEnd)
+void UI::AddUIView(UIView* uiView)
 {
 {
+    rootWidget_->AddChild(uiView->GetInternalWidget());
+    uiViews_.Push(SharedPtr<UIView>(uiView));
 
 
-    if (batches.Empty())
-        return;
-
-    Vector2 invScreenSize(1.0f / (float)graphics_->GetWidth(), 1.0f / (float)graphics_->GetHeight());
-    Vector2 scale(2.0f * invScreenSize.x_, -2.0f * invScreenSize.y_);
-    Vector2 offset(-1.0f, 1.0f);
-
-    Matrix4 projection(Matrix4::IDENTITY);
-    projection.m00_ = scale.x_;
-    projection.m03_ = offset.x_;
-    projection.m11_ = scale.y_;
-    projection.m13_ = offset.y_;
-    projection.m22_ = 1.0f;
-    projection.m23_ = 0.0f;
-    projection.m33_ = 1.0f;
-
-    graphics_->ClearParameterSources();
-    graphics_->SetColorWrite(true);
-    graphics_->SetCullMode(CULL_NONE);
-    graphics_->SetDepthTest(CMP_ALWAYS);
-    graphics_->SetDepthWrite(false);
-    graphics_->SetFillMode(FILL_SOLID);
-    graphics_->SetStencilTest(false);
-
-    graphics_->ResetRenderTargets();
-
-    graphics_->SetVertexBuffer(buffer);
-
-    ShaderVariation* noTextureVS = graphics_->GetShader(VS, "Basic", "VERTEXCOLOR");
-    ShaderVariation* diffTextureVS = graphics_->GetShader(VS, "Basic", "DIFFMAP VERTEXCOLOR");
-    ShaderVariation* noTexturePS = graphics_->GetShader(PS, "Basic", "VERTEXCOLOR");
-    ShaderVariation* diffTexturePS = graphics_->GetShader(PS, "Basic", "DIFFMAP VERTEXCOLOR");
-    ShaderVariation* diffMaskTexturePS = graphics_->GetShader(PS, "Basic", "DIFFMAP ALPHAMASK VERTEXCOLOR");
-    ShaderVariation* alphaTexturePS = graphics_->GetShader(PS, "Basic", "ALPHAMAP VERTEXCOLOR");
-
-    unsigned alphaFormat = Graphics::GetAlphaFormat();
-
-    for (unsigned i = batchStart; i < batchEnd; ++i)
+    if (!focusedView_ && uiView)
     {
     {
-        const UIBatch& batch = batches[i];
-        if (batch.vertexStart_ == batch.vertexEnd_)
-            continue;
-
-        ShaderVariation* ps;
-        ShaderVariation* vs;
-
-        if (!batch.texture_)
-        {
-            ps = noTexturePS;
-            vs = noTextureVS;
-        }
-        else
-        {
-            // If texture contains only an alpha channel, use alpha shader (for fonts)
-            vs = diffTextureVS;
-
-            if (batch.texture_->GetFormat() == alphaFormat)
-                ps = alphaTexturePS;
-            else if (batch.blendMode_ != BLEND_ALPHA && batch.blendMode_ != BLEND_ADDALPHA && batch.blendMode_ != BLEND_PREMULALPHA)
-                ps = diffMaskTexturePS;
-            else
-                ps = diffTexturePS;
-        }
+        uiView->SetFocus();
+    }
+}
 
 
-        graphics_->SetShaders(vs, ps);
-        if (graphics_->NeedParameterUpdate(SP_OBJECT, this))
-            graphics_->SetShaderParameter(VSP_MODEL, Matrix3x4::IDENTITY);
-        if (graphics_->NeedParameterUpdate(SP_CAMERA, this))
-            graphics_->SetShaderParameter(VSP_VIEWPROJ, projection);
-        if (graphics_->NeedParameterUpdate(SP_MATERIAL, this))
-            graphics_->SetShaderParameter(PSP_MATDIFFCOLOR, Color(1.0f, 1.0f, 1.0f, 1.0f));
-
-        graphics_->SetBlendMode(batch.blendMode_);
-        graphics_->SetScissorTest(true, batch.scissor_);
-        graphics_->SetTexture(0, batch.texture_);
-        graphics_->Draw(TRIANGLE_LIST, batch.vertexStart_ / UI_VERTEX_SIZE, (batch.vertexEnd_ - batch.vertexStart_) /
-            UI_VERTEX_SIZE);
+void UI::RemoveUIView(UIView* uiView)
+{
+    if (focusedView_ == uiView)
+    {
+        SetFocusedView(0);
     }
     }
+
+    rootWidget_->RemoveChild(uiView->GetInternalWidget());
+    uiViews_.Remove(SharedPtr<UIView>(uiView));
 }
 }
 
 
-void UI::SetVertexData(VertexBuffer* dest, const PODVector<float>& vertexData)
+void UI::SetFocusedView(UIView* uiView)
 {
 {
-    if (vertexData.Empty())
+    if (focusedView_ == uiView)
+    {
         return;
         return;
+    }
 
 
-    // Update quad geometry into the vertex buffer
-    // Resize the vertex buffer first if too small or much too large
-    unsigned numVertices = vertexData.Size() / UI_VERTEX_SIZE;
-    if (dest->GetVertexCount() < numVertices || dest->GetVertexCount() > numVertices * 2)
-        dest->SetSize(numVertices, MASK_POSITION | MASK_COLOR | MASK_TEXCOORD1, true);
+    focusedView_ = uiView;
 
 
-    dest->SetData(&vertexData[0]);
-}
+    if (focusedView_)
+    {
+        focusedView_->BecomeFocused();
+    }
+    else
+    {
+        focusedView_ = 0;
 
 
+        // look for first auto activated UIView, and recurse
+        Vector<SharedPtr<UIView>>::Iterator itr = uiViews_.Begin();
 
 
-void UI::Render(bool resetRenderTargets)
-{
-    SetVertexData(vertexBuffer_, vertexData_);
-    Render(vertexBuffer_, batches_, 0, batches_.Size());
+        while (itr != uiViews_.End())
+        {
+            if ((*itr)->GetAutoFocus())
+            {                
+                SetFocusedView(*itr);
+                return;
+            }
+
+            itr++;
+        }
+
+    }
 }
 }
 
 
-void UI::HandleRenderUpdate(StringHash eventType, VariantMap& eventData)
+void UI::Render(bool resetRenderTargets)
 {
 {
-    // Get rendering batches from the non-modal UI elements
-    batches_.Clear();
-    vertexData_.Clear();
+    Vector<SharedPtr<UIView>>::Iterator itr = uiViews_.Begin();
 
 
-    tb::TBRect rect = rootWidget_->GetRect();
+    while (itr != uiViews_.End())
+    {
+        (*itr)->Render(resetRenderTargets);
 
 
-    IntRect currentScissor = IntRect(0, 0, rect.w, rect.h);
-    GetBatches(batches_, vertexData_, currentScissor);
+        itr++;
+    }
 
 
 }
 }
 
 
-void UI::GetBatches(PODVector<UIBatch>& batches, PODVector<float>& vertexData, const IntRect& currentScissor)
-{
-    //if (!initialized_)
-    //    return;
 
 
+void UI::HandlePostUpdate(StringHash eventType, VariantMap& eventData)
+{
     TBAnimationManager::Update();
     TBAnimationManager::Update();
 
 
     rootWidget_->InvokeProcessStates();
     rootWidget_->InvokeProcessStates();
     rootWidget_->InvokeProcess();
     rootWidget_->InvokeProcess();
-
-    tb::g_renderer->BeginPaint(rootWidget_->GetRect().w, rootWidget_->GetRect().h);
-
-    renderer_->currentScissor_ = currentScissor;
-    renderer_->batches_ = &batches;
-    renderer_->vertexData_ = &vertexData;
-    rootWidget_->InvokePaint(tb::TBWidget::PaintProps());
-
-    tb::g_renderer->EndPaint();
 }
 }
 
 
-void UI::SubmitBatchVertexData(Texture* texture, const PODVector<float>& vertexData)
+void UI::HandleRenderUpdate(StringHash eventType, VariantMap& eventData)
 {
 {
-    UIBatch b(BLEND_ALPHA , renderer_->currentScissor_, texture, &vertexData_);
-
-    unsigned begin = b.vertexData_->Size();
-    b.vertexData_->Resize(begin + vertexData.Size());
-    float* dest = &(b.vertexData_->At(begin));
-    b.vertexEnd_ = b.vertexData_->Size();
+    Vector<SharedPtr<UIView>>::Iterator itr = uiViews_.Begin();
 
 
-    for (unsigned i = 0; i < vertexData.Size(); i++, dest++)
+    while (itr != uiViews_.End())
     {
     {
-        *dest = vertexData[i];
+        (*itr)->UpdateUIBatches();
+        itr++;
     }
     }
 
 
-    UIBatch::AddOrMerge(b, batches_);
-
 }
 }
 
 
 void UI::TBFileReader(const char* filename, void** data, unsigned* length)
 void UI::TBFileReader(const char* filename, void** data, unsigned* length)
@@ -1034,6 +963,4 @@ void UI::DebugShowSettingsWindow(UIWidget* parent)
 
 
 }
 }
 
 
-
-
 }
 }

+ 17 - 23
Source/Atomic/UI/UI.h

@@ -34,11 +34,14 @@ namespace Atomic
 class VertexBuffer;
 class VertexBuffer;
 class UIRenderer;
 class UIRenderer;
 class UIWidget;
 class UIWidget;
+class UIView;
 class UIPopupWindow;
 class UIPopupWindow;
 class MessageBox;
 class MessageBox;
 
 
 class ATOMIC_API UI : public Object, private tb::TBWidgetListener
 class ATOMIC_API UI : public Object, private tb::TBWidgetListener
 {
 {
+    friend class UIView;
+
     ATOMIC_OBJECT(UI, Object)
     ATOMIC_OBJECT(UI, Object)
 
 
 public:
 public:
@@ -55,8 +58,6 @@ public:
     void SetInputDisabled(bool disabled) { inputDisabled_ = disabled; }
     void SetInputDisabled(bool disabled) { inputDisabled_ = disabled; }
 
 
     void Render(bool resetRenderTargets = true);
     void Render(bool resetRenderTargets = true);
-    void GetBatches(PODVector<UIBatch>& batches, PODVector<float>& vertexData, const IntRect& currentScissor);
-    void SubmitBatchVertexData(Texture* texture, const PODVector<float>& vertexData);
 
 
     void Initialize(const String& languageFile);
     void Initialize(const String& languageFile);
 
 
@@ -130,18 +131,19 @@ public:
     // Debugging
     // Debugging
     static void DebugShowSettingsWindow(UIWidget* parent);
     static void DebugShowSettingsWindow(UIWidget* parent);
 
 
+    /// Get the currently focused view
+    UIView* GetFocusedView() const { return focusedView_; }
+
 private:
 private:
 
 
     static WeakPtr<Context> uiContext_;
     static WeakPtr<Context> uiContext_;
     static void TBFileReader(const char* filename, void** data, unsigned* length);
     static void TBFileReader(const char* filename, void** data, unsigned* length);
     static void TBIDRegisterStringCallback(unsigned id, const char* value);
     static void TBIDRegisterStringCallback(unsigned id, const char* value);
 
 
+    void HandlePostUpdate(StringHash eventType, VariantMap& eventData);
     void HandleRenderUpdate(StringHash eventType, VariantMap& eventData);
     void HandleRenderUpdate(StringHash eventType, VariantMap& eventData);
     void HandleExitRequested(StringHash eventType, VariantMap& eventData);
     void HandleExitRequested(StringHash eventType, VariantMap& eventData);
 
 
-    void Render(VertexBuffer* buffer, const PODVector<UIBatch>& batches, unsigned batchStart, unsigned batchEnd);
-    void SetVertexData(VertexBuffer* dest, const PODVector<float>& vertexData);
-
     // TBWidgetListener
     // TBWidgetListener
     void OnWidgetDelete(tb::TBWidget *widget);
     void OnWidgetDelete(tb::TBWidget *widget);
     bool OnWidgetDying(tb::TBWidget *widget);    
     bool OnWidgetDying(tb::TBWidget *widget);    
@@ -149,16 +151,16 @@ private:
     bool OnWidgetInvokeEvent(tb::TBWidget *widget, const tb::TBWidgetEvent &ev);
     bool OnWidgetInvokeEvent(tb::TBWidget *widget, const tb::TBWidgetEvent &ev);
     void OnWindowClose(tb::TBWindow *window);
     void OnWindowClose(tb::TBWindow *window);
 
 
+    /// Add a UIView to UI subsystem, happens immediately at UIView creation
+    void AddUIView(UIView* uiView);
+    /// Set the currently focused view
+    void SetFocusedView(UIView* uiView);
+    /// Removes a UIView from the UI subsystem, readding a view is not encouraged
+    void RemoveUIView(UIView* uiView);
 
 
     tb::TBWidget* rootWidget_;
     tb::TBWidget* rootWidget_;
     UIRenderer* renderer_;
     UIRenderer* renderer_;
 
 
-    /// UI rendering batches.
-    PODVector<UIBatch> batches_;
-    /// UI rendering vertex data.
-    PODVector<float> vertexData_;
-    SharedPtr<VertexBuffer> vertexBuffer_;
-
     WeakPtr<Graphics> graphics_;
     WeakPtr<Graphics> graphics_;
 
 
     HashMap<tb::TBWidget*, SharedPtr<UIWidget> > widgetWrap_;
     HashMap<tb::TBWidget*, SharedPtr<UIWidget> > widgetWrap_;
@@ -177,22 +179,14 @@ private:
     
     
     float tooltipHoverTime_;
     float tooltipHoverTime_;
 
 
+    Vector<SharedPtr<UIView>> uiViews_;
+
+    WeakPtr<UIView> focusedView_;
+
     // Events
     // Events
     void HandleScreenMode(StringHash eventType, VariantMap& eventData);
     void HandleScreenMode(StringHash eventType, VariantMap& eventData);
-    void HandleMouseButtonDown(StringHash eventType, VariantMap& eventData);
-    void HandleMouseButtonUp(StringHash eventType, VariantMap& eventData);
-    void HandleMouseMove(StringHash eventType, VariantMap& eventData);
-    void HandleMouseWheel(StringHash eventType, VariantMap& eventData);
-    void HandleKeyDown(StringHash eventType, VariantMap& eventData);
-    void HandleKeyUp(StringHash eventType, VariantMap& eventData);
-    void HandleKey(bool keydown, int keycode, int scancode);
-    void HandleTextInput(StringHash eventType, VariantMap& eventData);
     void HandleUpdate(StringHash eventType, VariantMap& eventData);
     void HandleUpdate(StringHash eventType, VariantMap& eventData);
     void HandleConsoleClosed(StringHash eventType, VariantMap& eventData);
     void HandleConsoleClosed(StringHash eventType, VariantMap& eventData);
-    //Touch Input
-    void HandleTouchBegin(StringHash eventType, VariantMap& eventData);
-    void HandleTouchMove(StringHash eventType, VariantMap& eventData);
-    void HandleTouchEnd(StringHash eventType, VariantMap& eventData);
 };
 };
 
 
 }
 }

+ 455 - 0
Source/Atomic/UI/UIComponent.cpp

@@ -0,0 +1,455 @@
+//
+// Copyright (c) 2014-2017, THUNDERBEAST GAMES LLC All rights reserved
+//
+// 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 "../Core/Context.h"
+#include "../IO/Log.h"
+#include "../Resource/ResourceCache.h"
+#include "../Graphics/Graphics.h"
+#include "../Graphics/Texture2D.h"
+#include "../Graphics/Technique.h"
+#include "../Graphics/Material.h"
+#include "../Graphics/Octree.h"
+#include "../Graphics/OctreeQuery.h"
+#include "../Graphics/Camera.h"
+#include "../Graphics/BillboardSet.h"
+#include "../Graphics/StaticModel.h"
+#include "../Graphics/Renderer.h"
+#include "../Scene/Node.h"
+#include "../Scene/Scene.h"
+#include "../Scene/SceneEvents.h"
+
+#include "UIView.h"
+#include "UIComponent.h"
+
+#include <Atomic/Input/Input.h>
+#include <Atomic/Input/InputEvents.h>
+#include <Atomic/UI/UI.h>
+#include <Atomic/UI/UIEvents.h>
+
+namespace Atomic
+{
+
+static const IntVector2 DEFAULT_UICOMPONENT_SIZE(UIVIEW_DEFAULT_TEXTURE_SIZE, UIVIEW_DEFAULT_TEXTURE_SIZE);
+
+UIComponent::UIComponent(Context* context) :
+    Component(context),
+    viewSize_(IntVector2(UIVIEW_DEFAULT_TEXTURE_SIZE, UIVIEW_DEFAULT_TEXTURE_SIZE)),
+    mouseScreenRect_(IntRect::ZERO)
+{
+
+}
+
+UIComponent::~UIComponent()
+{
+
+}
+
+void UIComponent::RegisterObject(Context* context)
+{
+    context->RegisterFactory<UIComponent>();
+
+    ATOMIC_ACCESSOR_ATTRIBUTE("Size", GetSizeAttr, SetSizeAttr, IntVector2, DEFAULT_UICOMPONENT_SIZE, AM_DEFAULT);
+}
+
+const IntVector2& UIComponent::GetSizeAttr() const
+{
+    return viewSize_;
+}
+
+void UIComponent::SetSizeAttr(const IntVector2& value)
+{
+    viewSize_ = value;
+
+    if (uiView_.NotNull())
+    {
+        uiView_->SetSize(viewSize_.x_, viewSize_.y_);
+    }
+}
+
+void UIComponent::SetStaticModel(StaticModel* staticModel)
+{
+    staticModel_ = staticModel_;
+}
+
+StaticModel* UIComponent::GetStaticModel() const
+{
+    return staticModel_;
+}
+
+void UIComponent::SetUIView(UIView* view)
+{
+    uiView_ = view;
+    viewSize_.x_ = uiView_->GetWidth();
+    viewSize_.y_ = uiView_->GetHeight();
+    view->SetRenderToTexture(true, viewSize_.x_, viewSize_.y_);
+}
+
+void UIComponent::OnSetEnabled()
+{
+    Component::OnSetEnabled();
+    UpdateEventSubscriptions(IsEnabledEffective());
+}
+
+void UIComponent::UpdateMouseEventSubscriptions(bool unsubscribe)
+{
+    if (!unsubscribe)
+    {
+        SubscribeToEvent(E_MOUSEBUTTONDOWN, ATOMIC_HANDLER(UIComponent, HandleMouseButtonDown));
+        SubscribeToEvent(E_MOUSEBUTTONUP, ATOMIC_HANDLER(UIComponent, HandleMouseButtonUp));
+        SubscribeToEvent(E_MOUSEMOVE, ATOMIC_HANDLER(UIComponent, HandleMouseMove));
+        SubscribeToEvent(E_MOUSEWHEEL, ATOMIC_HANDLER(UIComponent, HandleMouseWheel));
+    }
+    else
+    {
+        UnsubscribeFromEvent(E_MOUSEBUTTONDOWN);
+        UnsubscribeFromEvent(E_MOUSEBUTTONUP);
+        UnsubscribeFromEvent(E_MOUSEMOVE);
+        UnsubscribeFromEvent(E_MOUSEWHEEL);
+    }
+
+}
+void UIComponent::UpdateEventSubscriptions(bool subscribe)
+{
+    Scene* scene = GetScene();
+
+    if (subscribe && scene)
+    {
+        SubscribeToEvent(scene, E_SCENEPOSTUPDATE, ATOMIC_HANDLER(UIComponent, HandleScenePostUpdate));
+        UpdateMouseEventSubscriptions();
+    }
+    else
+    {
+        UpdateMouseEventSubscriptions(true);
+        UnsubscribeFromEvent(E_SCENEPOSTUPDATE);
+    }
+}
+
+void UIComponent::HandleScenePostUpdate(StringHash eventType, VariantMap& eventData)
+{
+    using namespace ScenePostUpdate;
+    float timeStep = eventData[P_TIMESTEP].GetFloat();
+
+    if (!IsEnabledEffective())
+    {
+        return;
+    }
+
+    // TODO: check visibility
+
+    // early out if nothing to render
+    if (staticModel_.Null())
+    {
+        staticModel_ = node_->GetComponent<StaticModel>();
+
+        if (staticModel_.Null())
+        {
+            return;
+        }
+    }
+
+    // if we don't have a view, let's make it
+    if (uiView_.Null())
+    {
+        UIView* view = new UIView(context_);
+        view->SetSize(viewSize_.x_, viewSize_.y_);
+        SetUIView(view);
+    }
+
+    if (!staticModel_->GetMaterial())
+    {
+        if (defaultMaterial_.Null())
+        {
+            ResourceCache* cache = GetSubsystem<ResourceCache>();
+            defaultMaterial_ = new Material(context_);
+            defaultMaterial_->SetTechnique(0, cache->GetResource<Technique>("Techniques/Diff.xml"));
+            defaultMaterial_->SetTexture(Atomic::TU_DIFFUSE, uiView_->GetRenderTexture());
+        }
+
+        staticModel_->SetMaterial(defaultMaterial_);
+    }
+
+}
+
+Viewport* UIComponent::GetViewport() const
+{
+    if (!GetScene())
+        return 0;
+
+    Renderer* renderer = GetSubsystem<Renderer>();
+    for (unsigned i = 0; i < renderer->GetNumViewports(); ++i)
+    {
+        Viewport* viewport = renderer->GetViewport(i);
+
+        // Find viewport with same scene
+        // TODO: support multiple viewports
+        if (viewport && viewport->GetScene() == GetScene())
+        {
+            return viewport;
+        }
+    }
+
+    return 0;
+
+}
+
+bool UIComponent::CalcUIViewPos(const IntVector2& screenPos, IntVector2& viewPos)
+{
+
+    if (staticModel_.Null() || uiView_.Null())
+    {
+        return false;
+    }
+
+    Viewport* viewport = GetViewport();
+
+    if (!viewport)
+    {
+        return false;
+    }
+
+    Scene* scene = GetScene();
+    Camera* camera = viewport->GetCamera();
+    Octree* octree = scene->GetComponent<Octree>();
+
+    bool rectIsDefault = mouseScreenRect_ == IntRect::ZERO;
+
+    if (!camera || !octree || (!rectIsDefault && !mouseScreenRect_.IsInside(screenPos)))
+    {
+        return false;
+    }
+
+    IntRect vpRect = viewport->GetRect();
+
+    if (vpRect == IntRect::ZERO)
+    {
+        Graphics* graphics = GetSubsystem<Graphics>();
+
+        vpRect.right_ = graphics->GetWidth();
+        vpRect.bottom_ = graphics->GetHeight();
+    }
+
+    Vector2 normPos(screenPos.x_ - mouseScreenRect_.left_, screenPos.y_ - mouseScreenRect_.top_);
+    normPos /= rectIsDefault ? Vector2(vpRect.Width(), vpRect.Height()) : Vector2(mouseScreenRect_.Width(), mouseScreenRect_.Height());
+
+    Ray ray(camera->GetScreenRay(normPos.x_, normPos.y_));
+    PODVector<RayQueryResult> queryResultVector;
+    RayOctreeQuery query(queryResultVector, ray, RAY_TRIANGLE_UV, M_INFINITY, DRAWABLE_GEOMETRY, DEFAULT_VIEWMASK);
+
+    octree->Raycast(query);
+
+    if (queryResultVector.Empty())
+        return false;
+
+    for (unsigned i = 0; i < queryResultVector.Size(); i++)
+    {
+        RayQueryResult& queryResult = queryResultVector[i];
+
+        if (queryResult.drawable_ != staticModel_)
+        {
+            // ignore billboard sets by default
+            if (queryResult.drawable_->GetTypeInfo()->IsTypeOf(BillboardSet::GetTypeStatic()))
+            {
+                continue;
+            }
+
+            return false;
+        }
+
+        Vector2& uv = queryResult.textureUV_;
+        viewPos = IntVector2(uv.x_ * uiView_->GetWidth(), uv.y_ * uiView_->GetHeight());
+        return true;
+
+    }
+
+    return false;
+
+}
+
+bool UIComponent::FilterInput() const
+{
+    if (uiView_.Null() || staticModel_.Null() || !uiView_->GetMouseEnabled() || !uiView_->GetFocus())
+    {
+        return true;
+    }
+
+    return false;
+}
+
+void UIComponent::HandleMouseMove(StringHash eventType, VariantMap& eventData)
+{    
+    if (FilterInput())
+    {
+        return;
+    }
+
+    using namespace MouseMove;
+
+    IntVector2 screenPos(eventData[P_X].GetInt(), eventData[P_Y].GetInt());
+    IntVector2 viewPos;
+
+    if (!CalcUIViewPos(screenPos, viewPos))
+    {
+        return;
+    }
+
+    tb::TBWidget *widget = uiView_->GetInternalWidget();
+    widget->InvokePointerMove(viewPos.x_, viewPos.y_, tb::TB_MODIFIER_NONE, false);
+}
+
+// this is duplicated in UIView.cpp, don't want to expose in header
+// possible need a static TB <-> Atomic UI helper class to encapsulate
+static tb::MODIFIER_KEYS GetModifierKeys(int qualifiers, bool superKey)
+{
+    tb::MODIFIER_KEYS code = tb::TB_MODIFIER_NONE;
+    if (qualifiers & QUAL_ALT)    code |= tb::TB_ALT;
+    if (qualifiers & QUAL_CTRL)    code |= tb::TB_CTRL;
+    if (qualifiers & QUAL_SHIFT)    code |= tb::TB_SHIFT;
+    if (superKey)    code |= tb::TB_SUPER;
+    return code;
+}
+
+void UIComponent::HandleMouseButtonDown(StringHash eventType, VariantMap& eventData)
+{
+    if (FilterInput())
+    {
+        return;
+    }
+
+    using namespace MouseButtonDown;
+
+    Input* input = GetSubsystem<Input>();
+
+    unsigned button = eventData[P_BUTTON].GetUInt();
+
+    IntVector2 screenPos = GetSubsystem<Input>()->GetMousePosition();
+    IntVector2 viewPos;
+
+    if (!CalcUIViewPos(screenPos, viewPos))
+    {
+        return;
+    }
+
+    int qualifiers = input->GetQualifiers();
+
+#ifdef ATOMIC_PLATFORM_WINDOWS
+    bool superdown = input->GetKeyDown(KEY_LCTRL) || input->GetKeyDown(KEY_RCTRL);
+#else
+    bool superdown = input->GetKeyDown(KEY_LGUI) || input->GetKeyDown(KEY_RGUI);
+#endif
+
+    tb::TBWidget *widget = uiView_->GetInternalWidget();
+
+    const int clickCount = 1;
+
+    tb::MODIFIER_KEYS mod = GetModifierKeys(qualifiers, superdown);
+
+    if (button == MOUSEB_RIGHT)
+        widget->InvokeRightPointerDown(viewPos.x_, viewPos.y_, clickCount, mod);
+    else
+        widget->InvokePointerDown(viewPos.x_, viewPos.y_, clickCount, mod, false);
+
+}
+
+
+void UIComponent::HandleMouseButtonUp(StringHash eventType, VariantMap& eventData)
+{
+    if (FilterInput())
+    {
+        return;
+    }
+
+    using namespace MouseButtonUp;
+
+    Input* input = GetSubsystem<Input>();
+
+    unsigned button = eventData[P_BUTTON].GetUInt();
+
+    IntVector2 screenPos = GetSubsystem<Input>()->GetMousePosition();
+    IntVector2 viewPos;
+
+    if (!CalcUIViewPos(screenPos, viewPos))
+    {
+        return;
+    }
+
+    int qualifiers = input->GetQualifiers();
+
+#ifdef ATOMIC_PLATFORM_WINDOWS
+    bool superdown = input->GetKeyDown(KEY_LCTRL) || input->GetKeyDown(KEY_RCTRL);
+#else
+    bool superdown = input->GetKeyDown(KEY_LGUI) || input->GetKeyDown(KEY_RGUI);
+#endif
+
+    tb::MODIFIER_KEYS mod = GetModifierKeys(qualifiers, superdown);
+
+    tb::TBWidget *widget = uiView_->GetInternalWidget();
+
+    if (button == MOUSEB_RIGHT)
+        widget->InvokeRightPointerUp(viewPos.x_, viewPos.y_, mod);
+    else
+        widget->InvokePointerUp(viewPos.x_, viewPos.y_, mod, false);
+
+}
+
+void UIComponent::HandleMouseWheel(StringHash eventType, VariantMap& eventData)
+{
+    if (FilterInput())
+    {
+        return;
+    }
+
+    using namespace MouseWheel;
+
+    if (uiView_.Null() || staticModel_.Null())
+    {
+        return;
+    }
+
+    int delta = eventData[P_WHEEL].GetInt();
+
+    Input* input = GetSubsystem<Input>();
+
+    tb::TBWidget *widget = uiView_->GetInternalWidget();
+
+    widget->InvokeWheel(input->GetMousePosition().x_, input->GetMousePosition().y_, 0, -delta, tb::TB_MODIFIER_NONE);
+}
+
+
+void UIComponent::OnSceneSet(Scene* scene)
+{
+    Component::OnSceneSet(scene);
+    if (!scene)
+    {
+        UpdateEventSubscriptions(false);
+    }
+    else
+    {
+        UpdateEventSubscriptions(IsEnabledEffective());
+    }
+}
+
+void UIComponent::OnNodeSet(Node* node)
+{
+    Component::OnNodeSet(node);
+}
+
+}

+ 105 - 0
Source/Atomic/UI/UIComponent.h

@@ -0,0 +1,105 @@
+//
+// Copyright (c) 2014-2017, THUNDERBEAST GAMES LLC All rights reserved
+//
+// 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 "../Scene/Component.h"
+
+namespace Atomic
+{
+
+class Viewport;
+class UIView;
+class StaticModel;
+class Material;
+
+// UI Component which can be attached to a scene node with a StaticModel
+class ATOMIC_API UIComponent : public Component
+{
+    ATOMIC_OBJECT(UIComponent, Component)
+
+public:
+
+    /// Construct.
+    UIComponent(Context* context);
+    /// Destruct.
+    virtual ~UIComponent();
+
+    /// Register object factory.
+    static void RegisterObject(Context* context);
+
+    /// Handle enabled/disabled state change.
+    virtual void OnSetEnabled();
+
+    /// Set the UIView for this UIComponent, note this is optional as one will be autocreated
+    void SetUIView(UIView* view);
+    /// Get the UIView for this UIComponent
+    UIView* GetUIView() const { return uiView_; }
+
+    /// Get the viewport associated with this UIComponent
+    Viewport* GetViewport() const;
+
+    /// Set the static model used for rendering and ray casting
+    void SetStaticModel(StaticModel* staticModel);
+    /// Get the static model used for rendering and ray casting
+    StaticModel* GetStaticModel() const;
+
+    /// Calculate position in UI space given a screen position
+    bool CalcUIViewPos(const IntVector2& screenPos, IntVector2& viewPos);
+
+protected:
+
+    /// Handle scene node being assigned at creation.
+    virtual void OnNodeSet(Node* node);
+
+    /// Handle scene being assigned. This may happen several times during the component's lifetime. Scene-wide subsystems and events are subscribed to here.
+    virtual void OnSceneSet(Scene* scene);
+
+private:
+
+    const IntVector2& GetSizeAttr() const;
+    void SetSizeAttr(const IntVector2& value);
+
+    void UpdateEventSubscriptions(bool subscribe);
+    void HandleScenePostUpdate(StringHash eventType, VariantMap& eventData);
+
+    bool FilterInput() const;
+
+    void UpdateMouseEventSubscriptions(bool unsubscribe = false);
+    void HandleMouseButtonDown(StringHash eventType, VariantMap& eventData);
+    void HandleMouseButtonUp(StringHash eventType, VariantMap& eventData);
+    void HandleMouseMove(StringHash eventType, VariantMap& eventData);
+    void HandleMouseWheel(StringHash eventType, VariantMap& eventData);
+
+    /// WeakPtr to UIView as we don't want to hold after UI subsystem has released
+    WeakPtr<UIView> uiView_;
+    WeakPtr<StaticModel> staticModel_;
+
+    IntVector2 viewSize_;
+
+    IntRect mouseScreenRect_;
+
+    SharedPtr<Material> defaultMaterial_;
+
+};
+
+}

+ 2 - 1
Source/Atomic/UI/UIRenderer.h

@@ -40,7 +40,8 @@ class UI;
 
 
 class ATOMIC_API UIRenderer : public tb::TBRendererBatcher
 class ATOMIC_API UIRenderer : public tb::TBRendererBatcher
 {
 {
-    friend class UI;
+    friend class UIView;
+
 public:
 public:
 
 
     UIRenderer(Context* context);
     UIRenderer(Context* context);

+ 7 - 1
Source/Atomic/UI/UISceneView.cpp

@@ -23,6 +23,7 @@
 
 
 #include <Atomic/UI/UI.h>
 #include <Atomic/UI/UI.h>
 #include <Atomic/UI/UIBatch.h>
 #include <Atomic/UI/UIBatch.h>
+#include <Atomic/UI/UIView.h>
 #include <Atomic/IO/Log.h>
 #include <Atomic/IO/Log.h>
 #include <Atomic/Engine/Engine.h>
 #include <Atomic/Engine/Engine.h>
 #include <Atomic/Graphics/Graphics.h>
 #include <Atomic/Graphics/Graphics.h>
@@ -263,7 +264,12 @@ void SceneViewWidget::OnPaint(const PaintProps &paint_props)
     data[30] = x;
     data[30] = x;
     data[31] = y + h;
     data[31] = y + h;
 
 
-    sceneView_->GetSubsystem<UI>()->SubmitBatchVertexData(sceneView_->GetRenderTexture(), vertexData_);
+    UIView *view = sceneView_->GetView();
+
+    if (view)
+    {
+        view->SubmitBatchVertexData(sceneView_->GetRenderTexture(), vertexData_);
+    }
 
 
 }
 }
 
 

+ 7 - 1
Source/Atomic/UI/UITextureWidget.cpp

@@ -24,6 +24,7 @@
 #include <TurboBadger/tb_widgets_common.h>
 #include <TurboBadger/tb_widgets_common.h>
 
 
 #include <Atomic/IO/Log.h>
 #include <Atomic/IO/Log.h>
+#include <Atomic/UI/UIView.h>
 #include <Atomic/Graphics/Texture.h>
 #include <Atomic/Graphics/Texture.h>
 
 
 #include "UIEvents.h"
 #include "UIEvents.h"
@@ -151,7 +152,12 @@ void TBTextureWidget::OnPaint(const PaintProps &paint_props)
     data[30] = rect.x;
     data[30] = rect.x;
     data[31] = rect.y + rect.h;
     data[31] = rect.y + rect.h;
 
 
-    uiTextureWidget_->GetSubsystem<UI>()->SubmitBatchVertexData(uiTextureWidget_->GetTexture(), vertexData_);
+    UIView *view = uiTextureWidget_->GetView();
+
+    if (view)
+    {
+        view->SubmitBatchVertexData(uiTextureWidget_->GetTexture(), vertexData_);
+    }
 
 
 }
 }
 
 

+ 344 - 6
Source/Atomic/UI/UIView.cpp

@@ -22,31 +22,51 @@
 
 
 #include <TurboBadger/tb_widgets.h>
 #include <TurboBadger/tb_widgets.h>
 
 
+#include "../IO/Log.h"
+#include "../Graphics/Graphics.h"
+#include "../Graphics/VertexBuffer.h"
+#include "../Graphics/Texture2D.h"
+#include "../Input/InputEvents.h"
+
 #include "UI.h"
 #include "UI.h"
 #include "UIView.h"
 #include "UIView.h"
+#include "UIRenderer.h"
 
 
 using namespace tb;
 using namespace tb;
 
 
 namespace Atomic
 namespace Atomic
 {
 {
 
 
-UIView::UIView(Context* context) : UIWidget(context, false)
+UIView::UIView(Context* context) : UIWidget(context, false),
+    autoFocus_(true),
+    mouseEnabled_(true),
+    keyboardEnabled_(true)
 {
 {
+    graphics_ = GetSubsystem<Graphics>();
+    assert(graphics_.NotNull());
+    assert(graphics_->IsInitialized());
+
+    ui_ = GetSubsystem<UI>();
+    assert(ui_.NotNull());
+
+    renderer_ = ui_->GetRenderer();
+
     widget_ = new TBWidget();
     widget_ = new TBWidget();
     widget_->SetDelegate(this);
     widget_->SetDelegate(this);
 
 
     // Set gravity all so we resize correctly
     // Set gravity all so we resize correctly
     widget_->SetGravity(WIDGET_GRAVITY_ALL);
     widget_->SetGravity(WIDGET_GRAVITY_ALL);
 
 
-    UI* ui = GetSubsystem<UI>();
-    ui->WrapWidget(this, widget_);
+    ui_->WrapWidget(this, widget_);
 
 
     // Set initial size for view
     // Set initial size for view
-    TBRect rect = ui->GetRootWidget()->GetRect();
+    TBRect rect = ui_->GetRootWidget()->GetRect();
     widget_->SetSize(rect.w, rect.h);
     widget_->SetSize(rect.w, rect.h);
 
 
-    // add to the root widget
-    ui->GetRootWidget()->AddChild(widget_);
+    vertexBuffer_ = new VertexBuffer(context_);
+
+    ui_->AddUIView(this);
+
 
 
 }
 }
 
 
@@ -55,5 +75,323 @@ UIView::~UIView()
 
 
 }
 }
 
 
+void UIView::SetFocus()
+{
+    if (ui_.Null() || ui_->GetFocusedView() == this)
+    {
+        return;
+    }
+
+    if (ui_->GetFocusedView())
+    {
+        ui_->GetFocusedView()->ResignFocus();
+    }
+
+    UIWidget::SetFocus();
+
+    ui_->SetFocusedView(this);
+}
+
+bool UIView::GetFocus() const
+{
+    if (ui_.Null())
+    {
+        return false;
+    }
+
+    return ui_->GetFocusedView() == this;
+}
+
+void UIView::BecomeFocused()
+{
+    widget_->SetZ(WIDGET_Z_TOP);
+
+    SubscribeToEvent(E_MOUSEBUTTONDOWN, ATOMIC_HANDLER(UIView, HandleMouseButtonDown));
+    SubscribeToEvent(E_MOUSEBUTTONUP, ATOMIC_HANDLER(UIView, HandleMouseButtonUp));
+    SubscribeToEvent(E_MOUSEMOVE, ATOMIC_HANDLER(UIView, HandleMouseMove));
+    SubscribeToEvent(E_MOUSEWHEEL, ATOMIC_HANDLER(UIView, HandleMouseWheel));
+    SubscribeToEvent(E_KEYDOWN, ATOMIC_HANDLER(UIView, HandleKeyDown));
+    SubscribeToEvent(E_KEYUP, ATOMIC_HANDLER(UIView, HandleKeyUp));
+    SubscribeToEvent(E_TEXTINPUT, ATOMIC_HANDLER(UIView, HandleTextInput));
+
+    SubscribeToEvent(E_TOUCHBEGIN, ATOMIC_HANDLER(UIView, HandleTouchBegin));
+    SubscribeToEvent(E_TOUCHEND, ATOMIC_HANDLER(UIView, HandleTouchEnd));
+    SubscribeToEvent(E_TOUCHMOVE, ATOMIC_HANDLER(UIView, HandleTouchMove));
+
+}
+
+void UIView::ResignFocus()
+{        
+    if (ui_.Null() || ui_->GetFocusedView() != this)
+    {
+        return;
+    }
+
+    widget_->SetZ(WIDGET_Z_BOTTOM);
+
+    UnsubscribeFromEvent(E_MOUSEBUTTONDOWN);
+    UnsubscribeFromEvent(E_MOUSEBUTTONUP);
+    UnsubscribeFromEvent(E_MOUSEMOVE);
+    UnsubscribeFromEvent(E_MOUSEWHEEL);
+    UnsubscribeFromEvent(E_KEYDOWN);
+    UnsubscribeFromEvent(E_KEYUP);
+    UnsubscribeFromEvent(E_TEXTINPUT);
+
+    UnsubscribeFromEvent(E_TOUCHBEGIN);
+    UnsubscribeFromEvent(E_TOUCHEND);
+    UnsubscribeFromEvent(E_TOUCHMOVE);
+
+    ui_->SetFocusedView(0);
+
+}
+
+void UIView::Remove()
+{
+    ResignFocus();
+
+    if (ui_.NotNull())
+    {
+        ui_->RemoveUIView(this);
+    }
+
+    UIWidget::Remove();
+}
+
+bool UIView::SetRenderToTexture(bool value, const int width, const int height)
+{
+    if (!value && renderTexture_.NotNull())
+    {
+        renderTexture_ = 0;
+    }
+    else if (value && renderTexture_.Null())
+    {
+        renderTexture_ = new Texture2D(context_);
+        SetAutoFocus(false);
+    }
+
+    return SetSize(width, height);
+}
+
+bool UIView::SetSize(int width, int height)
+{
+    if (!widget_)
+        return false;
+
+    if (width < UIVIEW_MIN_TEXTURE_SIZE || width > UIVIEW_MAX_TEXTURE_SIZE ||
+        height < UIVIEW_MIN_TEXTURE_SIZE || height > UIVIEW_MAX_TEXTURE_SIZE)
+    {
+        ATOMIC_LOGERROR("UIView::SetSize() - Attempting to set invalid size, failed");
+        return false;
+    }
+
+    if (renderTexture_.NotNull())
+    {
+        renderTexture_->SetSize(width, height, graphics_->GetRGBAFormat(), Atomic::TEXTURE_RENDERTARGET);
+        renderTexture_->SetFilterMode(FILTER_BILINEAR);
+        renderTexture_->SetAddressMode(COORD_U, ADDRESS_CLAMP);
+        renderTexture_->SetAddressMode(COORD_V, ADDRESS_CLAMP);
+        renderTexture_->SetNumLevels(1); // No mipmaps
+        renderTexture_->GetRenderSurface()->SetUpdateMode(SURFACE_MANUALUPDATE);
+
+    }
+
+    return UIWidget::SetSize(width, height);
+
+}
+
+void UIView::Render(VertexBuffer* buffer, const PODVector<UIBatch>& batches, unsigned batchStart, unsigned batchEnd)
+{
+
+    if (batches.Empty())
+    {
+        graphics_->ResetRenderTargets();
+        return;
+    }
+
+    IntVector2 size;
+
+    if (renderTexture_)
+    {
+        size.x_ = renderTexture_->GetWidth();
+        size.y_ = renderTexture_->GetHeight();
+    }
+    else
+    {
+        size.x_ = graphics_->GetWidth();
+        size.y_ = graphics_->GetHeight();
+    }
+
+    bool scissorEnabled = true;
+    IntRect rect(0, 0, size.x_, size.y_);
+    Vector2 invScreenSize(1.0f / (float)size.x_, 1.0f / (float)size.y_);
+    Vector2 scale(2.0f * invScreenSize.x_, -2.0f * invScreenSize.y_);
+    Vector2 offset(-1.0f, 1.0f);
+
+    // On OpenGL, flip the projection if rendering to a texture so that the texture can be addressed in the same way
+    // as a render texture produced on Direct3D
+#ifdef ATOMIC_OPENGL
+    if (renderTexture_)
+    {
+        // ATOMIC ISSUE: https://github.com/AtomicGameEngine/AtomicGameEngine/issues/1581
+        // this needs to be fixed, scissors can't be disabled
+        // and there is a flip to D3D in SetScissorTest        
+        scissorEnabled = false;
+        offset.y_ = -offset.y_;
+        scale.y_  = -scale.y_;
+    }
+#endif
+
+    Matrix4 projection(Matrix4::IDENTITY);
+    projection.m00_ = scale.x_;
+    projection.m03_ = offset.x_;
+    projection.m11_ = scale.y_;
+    projection.m13_ = offset.y_;
+    projection.m22_ = 1.0f;
+    projection.m23_ = 0.0f;
+    projection.m33_ = 1.0f;
+
+    graphics_->ClearParameterSources();
+    graphics_->SetColorWrite(true);
+    graphics_->SetCullMode(CULL_NONE);
+    graphics_->SetDepthTest(CMP_ALWAYS);
+    graphics_->SetDepthWrite(false);
+    graphics_->SetFillMode(FILL_SOLID);
+    graphics_->SetStencilTest(false);
+
+    graphics_->ResetRenderTargets();
+
+    if (renderTexture_)
+    {
+        graphics_->SetRenderTarget(0, renderTexture_->GetRenderSurface());
+    }
+
+    graphics_->SetViewport(rect);
+
+    graphics_->SetVertexBuffer(buffer);
+
+    ShaderVariation* noTextureVS = graphics_->GetShader(VS, "Basic", "VERTEXCOLOR");
+    ShaderVariation* diffTextureVS = graphics_->GetShader(VS, "Basic", "DIFFMAP VERTEXCOLOR");
+    ShaderVariation* noTexturePS = graphics_->GetShader(PS, "Basic", "VERTEXCOLOR");
+    ShaderVariation* diffTexturePS = graphics_->GetShader(PS, "Basic", "DIFFMAP VERTEXCOLOR");
+    ShaderVariation* diffMaskTexturePS = graphics_->GetShader(PS, "Basic", "DIFFMAP ALPHAMASK VERTEXCOLOR");
+    ShaderVariation* alphaTexturePS = graphics_->GetShader(PS, "Basic", "ALPHAMAP VERTEXCOLOR");
+
+    unsigned alphaFormat = Graphics::GetAlphaFormat();
+
+    if (renderTexture_)
+    {
+        graphics_->Clear(Atomic::CLEAR_COLOR);
+    }
+
+    for (unsigned i = batchStart; i < batchEnd; ++i)
+    {
+        const UIBatch& batch = batches[i];
+        if (batch.vertexStart_ == batch.vertexEnd_)
+            continue;
+
+        ShaderVariation* ps;
+        ShaderVariation* vs;
+
+        if (!batch.texture_)
+        {
+            ps = noTexturePS;
+            vs = noTextureVS;
+        }
+        else
+        {
+            // If texture contains only an alpha channel, use alpha shader (for fonts)
+            vs = diffTextureVS;
+
+            if (batch.texture_->GetFormat() == alphaFormat)
+                ps = alphaTexturePS;
+            else if (batch.blendMode_ != BLEND_ALPHA && batch.blendMode_ != BLEND_ADDALPHA && batch.blendMode_ != BLEND_PREMULALPHA)
+                ps = diffMaskTexturePS;
+            else
+                ps = diffTexturePS;
+        }
+
+        graphics_->SetShaders(vs, ps);
+        if (graphics_->NeedParameterUpdate(SP_OBJECT, this))
+            graphics_->SetShaderParameter(VSP_MODEL, Matrix3x4::IDENTITY);
+        if (graphics_->NeedParameterUpdate(SP_CAMERA, this))
+            graphics_->SetShaderParameter(VSP_VIEWPROJ, projection);
+        if (graphics_->NeedParameterUpdate(SP_MATERIAL, this))
+            graphics_->SetShaderParameter(PSP_MATDIFFCOLOR, Color(1.0f, 1.0f, 1.0f, 1.0f));
+
+        graphics_->SetBlendMode(batch.blendMode_);
+        graphics_->SetScissorTest(scissorEnabled, batch.scissor_);
+        graphics_->SetTexture(0, batch.texture_);
+        graphics_->Draw(TRIANGLE_LIST, batch.vertexStart_ / UI_VERTEX_SIZE, (batch.vertexEnd_ - batch.vertexStart_) /
+            UI_VERTEX_SIZE);
+    }
+
+    if (renderTexture_)
+    {
+        graphics_->ResetRenderTargets();
+    }
+}
+
+void UIView::SetVertexData(VertexBuffer* dest, const PODVector<float>& vertexData)
+{
+    if (vertexData.Empty())
+        return;
+
+    // Update quad geometry into the vertex buffer
+    // Resize the vertex buffer first if too small or much too large
+    unsigned numVertices = vertexData.Size() / UI_VERTEX_SIZE;
+    if (dest->GetVertexCount() < numVertices || dest->GetVertexCount() > numVertices * 2)
+        dest->SetSize(numVertices, MASK_POSITION | MASK_COLOR | MASK_TEXCOORD1, true);
+
+    dest->SetData(&vertexData[0]);
+}
+
+
+void UIView::Render(bool resetRenderTargets)
+{
+    SetVertexData(vertexBuffer_, vertexData_);
+    Render(vertexBuffer_, batches_, 0, batches_.Size());
+}
+
+void UIView::UpdateUIBatches()
+{
+    batches_.Clear();
+    vertexData_.Clear();
+
+    tb::TBRect rect = widget_->GetRect();
+    IntRect currentScissor = IntRect(0, 0, rect.w, rect.h);
+    GetBatches(batches_, vertexData_, currentScissor);
+}
+
+void UIView::GetBatches(PODVector<UIBatch>& batches, PODVector<float>& vertexData, const IntRect& currentScissor)
+{
+    tb::g_renderer->BeginPaint(currentScissor.Width(), currentScissor.Height());
+
+    renderer_->currentScissor_ = currentScissor;
+    renderer_->batches_ = &batches;
+    renderer_->vertexData_ = &vertexData;
+    widget_->InvokePaint(tb::TBWidget::PaintProps());
+
+    tb::g_renderer->EndPaint();
+}
+
+void UIView::SubmitBatchVertexData(Texture* texture, const PODVector<float>& vertexData)
+{
+    UIBatch b(BLEND_ALPHA , renderer_->currentScissor_, texture, &vertexData_);
+
+    unsigned begin = b.vertexData_->Size();
+    b.vertexData_->Resize(begin + vertexData.Size());
+    float* dest = &(b.vertexData_->At(begin));
+    b.vertexEnd_ = b.vertexData_->Size();
+
+    for (unsigned i = 0; i < vertexData.Size(); i++, dest++)
+    {
+        *dest = vertexData[i];
+    }
+
+    UIBatch::AddOrMerge(b, batches_);
+
+}
+
+
 
 
 }
 }

+ 101 - 1
Source/Atomic/UI/UIView.h

@@ -22,14 +22,28 @@
 
 
 #pragma once
 #pragma once
 
 
+#include "UIBatch.h"
 #include "UIWidget.h"
 #include "UIWidget.h"
 
 
 namespace Atomic
 namespace Atomic
 {
 {
 
 
-/// a view off the root widget
+class Texture;
+class Texture2D;
+class VertexBuffer;
+class UI;
+class UIRenderer;
+class UIComponent;
+
+static int const UIVIEW_DEFAULT_TEXTURE_SIZE = 512;
+static int const UIVIEW_MIN_TEXTURE_SIZE = 64;
+static int const UIVIEW_MAX_TEXTURE_SIZE = 4096;
+
+/// Top level UIView management
 class ATOMIC_API UIView : public UIWidget
 class ATOMIC_API UIView : public UIWidget
 {
 {
+    friend class UI;
+
     ATOMIC_OBJECT(UIView, UIWidget)
     ATOMIC_OBJECT(UIView, UIWidget)
 
 
 public:
 public:
@@ -37,10 +51,96 @@ public:
     UIView(Context* context);
     UIView(Context* context);
     virtual ~UIView();
     virtual ~UIView();
 
 
+    /// Set the view size
+    bool SetSize(int width, int height);
+
+    /// Remove the UIView from the UI subsystem, readding removed views is not advised
+    void Remove();
+
+    /// Focuses the UIView for input events
+    virtual void SetFocus();
+    /// Gets whether this UIView is focused
+    virtual bool GetFocus() const;
+    /// Resigned this views focus, the bottom UIView with autofocus on will become focused
+    void ResignFocus();
+
+    /// Sets whether the view will autofocus when at the bottom of the view stack
+    void SetAutoFocus(bool value) { autoFocus_ = value; }
+    /// Gets whether the view will autofocus when at the bottom of the view stack
+    bool GetAutoFocus() const { return autoFocus_; }
+
+    /// Gets whether both mouse and keyboard input are enabled
+    bool GetInputEnabled() const { return mouseEnabled_ || keyboardEnabled_; }
+    /// Sets whether both mouse and keyboard input are enabled
+    void SetInputEnabled(bool value) { mouseEnabled_ = keyboardEnabled_ = value; }
+
+    /// Gets whether mouse input is enabled
+    bool GetMouseEnabled() const { return mouseEnabled_; }
+    /// Sets whether mouse input is enabled
+    void SetMouseEnabled(bool value) { mouseEnabled_ = keyboardEnabled_ = value; }
+
+    /// Gets whether keyboard input is enabled
+    bool GetKeyboardEnabled() const { return keyboardEnabled_; }
+    /// Sets whether keyboard input is enabled
+    void SetKeyboardEnabled(bool value) { keyboardEnabled_ = value; }
+
+    /// Set whether the UIView renders to texture, useful for 3D UI's, etc
+    bool SetRenderToTexture(bool value, const int width = UIVIEW_DEFAULT_TEXTURE_SIZE, const int height = UIVIEW_DEFAULT_TEXTURE_SIZE);
+    /// Gets the UIViews render texture, if any
+    Texture2D* GetRenderTexture() { return renderTexture_; }
+    /// Returns whether this UIView has a valid render texture
+    bool HasRenderTexture() const { return renderTexture_.NotNull(); }
+
+    /// Render the UI
+    void Render(bool resetRenderTargets = true);
+
+    /// Low level vertex data submission
+    void SubmitBatchVertexData(Texture* texture, const PODVector<float>& vertexData);
+
 protected:
 protected:
 
 
 private:
 private:
 
 
+    bool FilterDefaultInput(bool keyEvent = false) const ;
+
+    void BecomeFocused();
+
+    void UpdateUIBatches();
+    void GetBatches(PODVector<UIBatch>& batches, PODVector<float>& vertexData, const IntRect& currentScissor);
+    void Render(VertexBuffer* buffer, const PODVector<UIBatch>& batches, unsigned batchStart, unsigned batchEnd);
+    void SetVertexData(VertexBuffer* dest, const PODVector<float>& vertexData);
+
+    void HandleMouseButtonDown(StringHash eventType, VariantMap& eventData);
+    void HandleMouseButtonUp(StringHash eventType, VariantMap& eventData);
+    void HandleMouseMove(StringHash eventType, VariantMap& eventData);
+    void HandleMouseWheel(StringHash eventType, VariantMap& eventData);
+    void HandleKeyDown(StringHash eventType, VariantMap& eventData);
+    void HandleKeyUp(StringHash eventType, VariantMap& eventData);
+    void HandleKey(bool keydown, int keycode, int scancode);
+    void HandleTextInput(StringHash eventType, VariantMap& eventData);
+    //Touch Input
+    void HandleTouchBegin(StringHash eventType, VariantMap& eventData);
+    void HandleTouchMove(StringHash eventType, VariantMap& eventData);
+    void HandleTouchEnd(StringHash eventType, VariantMap& eventData);
+
+    /// UI rendering batches.
+    PODVector<UIBatch> batches_;
+    /// UI rendering vertex data.
+    PODVector<float> vertexData_;
+    SharedPtr<VertexBuffer> vertexBuffer_;
+
+    WeakPtr<Graphics> graphics_;
+    UIRenderer* renderer_;
+    WeakPtr<UI> ui_;
+    WeakPtr<UIComponent> uiComponent_;
+
+    SharedPtr<Texture2D> renderTexture_;
+
+    bool autoFocus_;
+
+    bool mouseEnabled_;
+    bool keyboardEnabled_;
+
 };
 };
 
 
 }
 }

+ 60 - 35
Source/Atomic/UI/UIInput.cpp → Source/Atomic/UI/UIViewInput.cpp

@@ -30,6 +30,7 @@ using namespace tb;
 
 
 #include "UI.h"
 #include "UI.h"
 #include "UIEvents.h"
 #include "UIEvents.h"
+#include "UIView.h"
 
 
 namespace Atomic
 namespace Atomic
 {
 {
@@ -53,9 +54,33 @@ static int toupr_ascii(int ascii)
     return ascii;
     return ascii;
 }
 }
 
 
-void UI::HandleMouseButtonDown(StringHash eventType, VariantMap& eventData)
+bool UIView::FilterDefaultInput(bool keyEvent) const
 {
 {
-    if (inputDisabled_ || consoleVisible_)
+    if (!GetInputEnabled())
+        return true;
+
+    if (ui_.Null() || ui_->GetFocusedView() != this)
+        return true;
+
+    if (ui_->inputDisabled_ || ui_->consoleVisible_)
+        return true;
+
+    if (renderTexture_ && !keyEvent)
+        return true;
+
+    if (!keyEvent && !GetMouseEnabled())
+        return true;
+
+    if (keyEvent && !GetKeyboardEnabled() || ui_->keyboardDisabled_)
+        return true;
+
+    return false;
+
+}
+
+void UIView::HandleMouseButtonDown(StringHash eventType, VariantMap& eventData)
+{
+    if (FilterDefaultInput())
         return;
         return;
 
 
     using namespace MouseButtonDown;
     using namespace MouseButtonDown;
@@ -89,17 +114,17 @@ void UI::HandleMouseButtonDown(StringHash eventType, VariantMap& eventData)
 
 
     last_time = time;
     last_time = time;
     if (button == MOUSEB_RIGHT)
     if (button == MOUSEB_RIGHT)
-        rootWidget_->InvokeRightPointerDown(pos.x_, pos.y_, counter, mod);
+        widget_->InvokeRightPointerDown(pos.x_, pos.y_, counter, mod);
     else
     else
-        rootWidget_->InvokePointerDown(pos.x_, pos.y_, counter, mod, false);
+        widget_->InvokePointerDown(pos.x_, pos.y_, counter, mod, false);
 
 
 
 
 }
 }
 
 
 
 
-void UI::HandleMouseButtonUp(StringHash eventType, VariantMap& eventData)
+void UIView::HandleMouseButtonUp(StringHash eventType, VariantMap& eventData)
 {
 {
-    if (inputDisabled_ || consoleVisible_)
+    if (FilterDefaultInput())
         return;
         return;
 
 
     using namespace MouseButtonUp;
     using namespace MouseButtonUp;
@@ -121,31 +146,31 @@ void UI::HandleMouseButtonUp(StringHash eventType, VariantMap& eventData)
 
 
 
 
     if (button == MOUSEB_RIGHT)
     if (button == MOUSEB_RIGHT)
-        rootWidget_->InvokeRightPointerUp(pos.x_, pos.y_, mod);
+        widget_->InvokeRightPointerUp(pos.x_, pos.y_, mod);
     else
     else
-        rootWidget_->InvokePointerUp(pos.x_, pos.y_, mod, false);
+        widget_->InvokePointerUp(pos.x_, pos.y_, mod, false);
 }
 }
 
 
 
 
-void UI::HandleMouseMove(StringHash eventType, VariantMap& eventData)
+void UIView::HandleMouseMove(StringHash eventType, VariantMap& eventData)
 {
 {
     using namespace MouseMove;
     using namespace MouseMove;
 
 
-    if (inputDisabled_ || consoleVisible_)
+    if (FilterDefaultInput())
         return;
         return;
 
 
     int px = eventData[P_X].GetInt();
     int px = eventData[P_X].GetInt();
     int py = eventData[P_Y].GetInt();
     int py = eventData[P_Y].GetInt();
 
 
-    rootWidget_->InvokePointerMove(px, py, tb::TB_MODIFIER_NONE, false);
+    widget_->InvokePointerMove(px, py, tb::TB_MODIFIER_NONE, false);
 
 
-    tooltipHoverTime_ = 0;
+    ui_->tooltipHoverTime_ = 0;
 }
 }
 
 
 
 
-void UI::HandleMouseWheel(StringHash eventType, VariantMap& eventData)
+void UIView::HandleMouseWheel(StringHash eventType, VariantMap& eventData)
 {
 {
-    if (inputDisabled_ || consoleVisible_)
+    if (FilterDefaultInput())
         return;
         return;
 
 
     using namespace MouseWheel;
     using namespace MouseWheel;
@@ -154,14 +179,14 @@ void UI::HandleMouseWheel(StringHash eventType, VariantMap& eventData)
 
 
     Input* input = GetSubsystem<Input>();
     Input* input = GetSubsystem<Input>();
 
 
-    rootWidget_->InvokeWheel(input->GetMousePosition().x_, input->GetMousePosition().y_, 0, -delta, tb::TB_MODIFIER_NONE);
+    widget_->InvokeWheel(input->GetMousePosition().x_, input->GetMousePosition().y_, 0, -delta, tb::TB_MODIFIER_NONE);
 
 
 }
 }
 
 
 //Touch Input
 //Touch Input
-void UI::HandleTouchBegin(StringHash eventType, VariantMap& eventData)
+void UIView::HandleTouchBegin(StringHash eventType, VariantMap& eventData)
 {
 {
-    if (inputDisabled_ || consoleVisible_)
+    if (FilterDefaultInput())
         return;
         return;
 
 
     using namespace TouchBegin;
     using namespace TouchBegin;
@@ -183,12 +208,12 @@ void UI::HandleTouchBegin(StringHash eventType, VariantMap& eventData)
 
 
     last_time = time;
     last_time = time;
     
     
-    rootWidget_->InvokePointerDown(px, py, counter, TB_MODIFIER_NONE, true, touchId);
+    widget_->InvokePointerDown(px, py, counter, TB_MODIFIER_NONE, true, touchId);
 }
 }
 
 
-void UI::HandleTouchMove(StringHash eventType, VariantMap& eventData)
+void UIView::HandleTouchMove(StringHash eventType, VariantMap& eventData)
 {
 {
-    if (inputDisabled_ || consoleVisible_)
+    if (FilterDefaultInput())
         return;
         return;
     
     
     using namespace TouchMove;
     using namespace TouchMove;
@@ -197,12 +222,12 @@ void UI::HandleTouchMove(StringHash eventType, VariantMap& eventData)
     int px = eventData[P_X].GetInt();
     int px = eventData[P_X].GetInt();
     int py = eventData[P_Y].GetInt();
     int py = eventData[P_Y].GetInt();
 
 
-    rootWidget_->InvokePointerMove(px, py, TB_MODIFIER_NONE, true, touchId);
+    widget_->InvokePointerMove(px, py, TB_MODIFIER_NONE, true, touchId);
 }
 }
 
 
-void UI::HandleTouchEnd(StringHash eventType, VariantMap& eventData)
+void UIView::HandleTouchEnd(StringHash eventType, VariantMap& eventData)
 {
 {
-    if (inputDisabled_ || consoleVisible_)
+    if (FilterDefaultInput())
         return;
         return;
 
 
     using namespace TouchEnd;
     using namespace TouchEnd;
@@ -211,7 +236,7 @@ void UI::HandleTouchEnd(StringHash eventType, VariantMap& eventData)
     int px = eventData[P_X].GetInt();
     int px = eventData[P_X].GetInt();
     int py = eventData[P_Y].GetInt();
     int py = eventData[P_Y].GetInt();
 
 
-    rootWidget_->InvokePointerUp(px, py, TB_MODIFIER_NONE, true, touchId);
+    widget_->InvokePointerUp(px, py, TB_MODIFIER_NONE, true, touchId);
 }
 }
 
 
 static bool InvokeShortcut(UI* ui, int key, SPECIAL_KEY special_key, MODIFIER_KEYS modifierkeys, bool down)
 static bool InvokeShortcut(UI* ui, int key, SPECIAL_KEY special_key, MODIFIER_KEYS modifierkeys, bool down)
@@ -307,7 +332,7 @@ static bool InvokeKey(UI* ui, TBWidget* root, unsigned int key, SPECIAL_KEY spec
 }
 }
 
 
 
 
-void UI::HandleKey(bool keydown, int keycode, int scancode)
+void UIView::HandleKey(bool keydown, int keycode, int scancode)
 {
 {
     if (keydown && (keycode == KEY_ESCAPE || keycode == KEY_RETURN || keycode == KEY_RETURN2 || keycode == KEY_KP_ENTER)
     if (keydown && (keycode == KEY_ESCAPE || keycode == KEY_RETURN || keycode == KEY_RETURN2 || keycode == KEY_KP_ENTER)
             && TBWidget::focused_widget)
             && TBWidget::focused_widget)
@@ -423,19 +448,19 @@ void UI::HandleKey(bool keydown, int keycode, int scancode)
     {
     {
         if (mod & TB_SUPER)
         if (mod & TB_SUPER)
         {
         {
-            InvokeKey(this, rootWidget_, keycode, TB_KEY_UNDEFINED, mod, keydown);
+            InvokeKey(ui_, widget_, keycode, TB_KEY_UNDEFINED, mod, keydown);
         }
         }
     }
     }
     else
     else
     {
     {
-        InvokeKey(this, rootWidget_, 0, specialKey, mod, keydown);
+        InvokeKey(ui_, widget_, 0, specialKey, mod, keydown);
     }
     }
 
 
 }
 }
 
 
-void UI::HandleKeyDown(StringHash eventType, VariantMap& eventData)
+void UIView::HandleKeyDown(StringHash eventType, VariantMap& eventData)
 {
 {
-    if (inputDisabled_ || keyboardDisabled_ || consoleVisible_)
+    if (FilterDefaultInput(true))
         return;
         return;
 
 
     using namespace KeyDown;
     using namespace KeyDown;
@@ -470,9 +495,9 @@ void UI::HandleKeyDown(StringHash eventType, VariantMap& eventData)
 
 
 }
 }
 
 
-void UI::HandleKeyUp(StringHash eventType, VariantMap& eventData)
+void UIView::HandleKeyUp(StringHash eventType, VariantMap& eventData)
 {
 {
-    if (inputDisabled_ || keyboardDisabled_ || consoleVisible_)
+    if (FilterDefaultInput(true))
         return;
         return;
 
 
     using namespace KeyUp;
     using namespace KeyUp;
@@ -484,9 +509,9 @@ void UI::HandleKeyUp(StringHash eventType, VariantMap& eventData)
 
 
 }
 }
 
 
-void UI::HandleTextInput(StringHash eventType, VariantMap& eventData)
+void UIView::HandleTextInput(StringHash eventType, VariantMap& eventData)
 {
 {
-    if (inputDisabled_ || keyboardDisabled_ || consoleVisible_)
+    if (FilterDefaultInput(true))
         return;
         return;
 
 
     using namespace TextInput;
     using namespace TextInput;
@@ -495,8 +520,8 @@ void UI::HandleTextInput(StringHash eventType, VariantMap& eventData)
 
 
     for (unsigned i = 0; i < text.Length(); i++)
     for (unsigned i = 0; i < text.Length(); i++)
     {
     {
-        InvokeKey(this, rootWidget_, text[i], TB_KEY_UNDEFINED, TB_MODIFIER_NONE, true);
-        InvokeKey(this, rootWidget_, text[i], TB_KEY_UNDEFINED, TB_MODIFIER_NONE, false);
+        InvokeKey(ui_, widget_, text[i], TB_KEY_UNDEFINED, TB_MODIFIER_NONE, true);
+        InvokeKey(ui_, widget_, text[i], TB_KEY_UNDEFINED, TB_MODIFIER_NONE, false);
     }
     }
 
 
 }
 }

+ 5 - 3
Source/Atomic/UI/UIWidget.cpp

@@ -330,12 +330,14 @@ void UIWidget::SetRect(IntRect rect)
 }
 }
 
 
 
 
-void UIWidget::SetSize(int width, int height)
+bool UIWidget::SetSize(int width, int height)
 {
 {
     if (!widget_)
     if (!widget_)
-        return;
+        return false;
 
 
     widget_->SetSize(width, height);
     widget_->SetSize(width, height);
+
+    return true;
 }
 }
 
 
 void UIWidget::Invalidate()
 void UIWidget::Invalidate()
@@ -833,7 +835,7 @@ void UIWidget::SetFocus()
 
 
 }
 }
 
 
-bool UIWidget::GetFocus()
+bool UIWidget::GetFocus() const
 {
 {
     if (!widget_)
     if (!widget_)
         return false;
         return false;

+ 4 - 5
Source/Atomic/UI/UIWidget.h

@@ -191,14 +191,14 @@ class ATOMIC_API UIWidget : public Object, public tb::TBWidgetDelegate
     String GetText();
     String GetText();
 
 
     void SetRect(IntRect r);
     void SetRect(IntRect r);
-    void SetSize(int width, int height);
+    virtual bool SetSize(int width, int height);
     void SetPosition(int x, int y);
     void SetPosition(int x, int y);
     void SetText(const String& text);
     void SetText(const String& text);
     void SetSkinBg(const String& id);
     void SetSkinBg(const String& id);
     void SetLayoutParams(UILayoutParams* params);
     void SetLayoutParams(UILayoutParams* params);
     void SetFontDescription(UIFontDescription* fd);
     void SetFontDescription(UIFontDescription* fd);
 
 
-    void Remove();
+    virtual void Remove();
     void RemoveChild(UIWidget* child, bool cleanup = true);
     void RemoveChild(UIWidget* child, bool cleanup = true);
 
 
     void DeleteAllChildren();
     void DeleteAllChildren();
@@ -213,9 +213,8 @@ class ATOMIC_API UIWidget : public Object, public tb::TBWidgetDelegate
     void SetValue(double value);
     void SetValue(double value);
     virtual double GetValue();
     virtual double GetValue();
 
 
-    void SetFocus();
-    bool GetFocus();
-
+    virtual void SetFocus();
+    virtual bool GetFocus() const;
 
 
     /// Set focus to first widget which accepts it
     /// Set focus to first widget which accepts it
     void SetFocusRecursive();
     void SetFocusRecursive();

+ 10 - 1
Source/AtomicWebView/UIWebView.cpp

@@ -23,6 +23,8 @@
 #include <Atomic/IO/Log.h>
 #include <Atomic/IO/Log.h>
 #include <Atomic/Input/InputEvents.h>
 #include <Atomic/Input/InputEvents.h>
 #include <Atomic/Input/Input.h>
 #include <Atomic/Input/Input.h>
+
+#include <Atomic/UI/UIView.h>
 #include <Atomic/UI/UIRenderer.h>
 #include <Atomic/UI/UIRenderer.h>
 
 
 #include "WebClient.h"
 #include "WebClient.h"
@@ -147,7 +149,14 @@ public:
         data[30] = x;
         data[30] = x;
         data[31] = y + h;
         data[31] = y + h;
 
 
-        ui->SubmitBatchVertexData(webView_->GetWebTexture2D()->GetTexture2D(), vertexData_);
+        UIView *view = webView_->GetView();
+
+        if (view)
+        {
+            view->SubmitBatchVertexData(webView_->GetWebTexture2D()->GetTexture2D(), vertexData_);
+        }
+
+
 
 
     }
     }
 
 

+ 1 - 1
Submodules/AtomicExamples

@@ -1 +1 @@
-Subproject commit 776b1699bc28c7926fb78a483ef6ddc4ac7a72dc
+Subproject commit fb3543fc688fced6d6942ec96bb1a2f4843c999b