Jay Sistar 9 лет назад
Родитель
Сommit
4ffad2f2a2

+ 2 - 1
Script/Packages/Atomic/UI.json

@@ -9,7 +9,8 @@
 								"UISelectList", "UIListView", "UIMessageWindow", "UILayoutParams", "UIFontDescription",
 								"UISkinImage", "UITabContainer", "UISceneView", "UIPreferredSize", "UIDragObject",
 								"UIContainer", "UISection", "UIInlineSelect", "UITextureWidget", "UIColorWidget", "UIColorWheel",
-								"UIScrollContainer", "UISeparator", "UIDimmer", "UISelectDropdown", "UISlider"],
+								"UIScrollContainer", "UISeparator", "UIDimmer", "UISelectDropdown", "UISlider",
+								"UIOffscreenView"],
 	"overloads" : {
 	},
 	"typescript_decl" : {

+ 6 - 2
Source/Atomic/Graphics/View.cpp

@@ -48,7 +48,9 @@
 #include "../IO/Log.h"
 #include "../Resource/ResourceCache.h"
 #include "../Scene/Scene.h"
-#include "../UI/UI.h"
+// ATOMIC BEGIN
+#include "../UI/SystemUI/SystemUI.h"
+// ATOMIC END
 
 #include "../DebugNew.h"
 
@@ -1659,7 +1661,9 @@ void View::ExecuteRenderPathCommands()
             case CMD_RENDERUI:
                 {
                     SetRenderTargets(command);
-                    GetSubsystem<UI>()->Render(false);
+                    // ATOMIC BEGIN
+                    GetSubsystem<SystemUI::SystemUI>()->Render(false);
+                    // ATOMIC END
                 }
                 break;
 

+ 71 - 33
Source/Atomic/UI/UI.cpp

@@ -87,6 +87,7 @@ using namespace tb;
 #include "UISlider.h"
 #include "UIColorWidget.h"
 #include "UIColorWheel.h"
+#include "UIOffscreenView.h"
 
 #include "SystemUI/SystemUI.h"
 #include "SystemUI/SystemUIEvents.h"
@@ -312,13 +313,23 @@ void UI::AddFont(const String& fontFile, const String& name)
     tb::g_font_manager->AddFontInfo(fontFile.CString(), name.CString());
 }
 
-void UI::Render(VertexBuffer* buffer, const PODVector<UIBatch>& batches, unsigned batchStart, unsigned batchEnd)
+void UI::Render(VertexBuffer* buffer, const PODVector<UIBatch>& batches, unsigned batchStart, unsigned batchEnd, RenderSurface* renderSurface, bool clearRenderSurface)
 {
-
     if (batches.Empty())
         return;
 
-    Vector2 invScreenSize(1.0f / (float)graphics_->GetWidth(), 1.0f / (float)graphics_->GetHeight());
+    Vector2 invScreenSize;
+    if (renderSurface)
+    {
+        invScreenSize.x_ = 1.0f / (float)renderSurface->GetWidth();
+        invScreenSize.y_ = 1.0f / (float)renderSurface->GetHeight();
+    }
+    else
+    {
+        invScreenSize.x_ = 1.0f / (float)graphics_->GetWidth();
+        invScreenSize.y_ = 1.0f / (float)graphics_->GetHeight();
+    }
+
     Vector2 scale(2.0f * invScreenSize.x_, -2.0f * invScreenSize.y_);
     Vector2 offset(-1.0f, 1.0f);
 
@@ -341,6 +352,12 @@ void UI::Render(VertexBuffer* buffer, const PODVector<UIBatch>& batches, unsigne
 
     graphics_->ResetRenderTargets();
 
+    if (renderSurface)
+        graphics_->SetRenderTarget(0, renderSurface);
+
+    if (clearRenderSurface)
+        graphics_->Clear(Atomic::CLEAR_COLOR);
+
     graphics_->SetVertexBuffer(buffer);
 
     ShaderVariation* noTextureVS = graphics_->GetShader(VS, "Basic", "VERTEXCOLOR");
@@ -410,11 +427,26 @@ void UI::SetVertexData(VertexBuffer* dest, const PODVector<float>& vertexData)
 }
 
 
-void UI::Render(bool resetRenderTargets)
+void UI::Render()
 {
+    // Render the off-screen root widgets.
+    for (HashSet<UIOffscreenView*>::Iterator i = offscreenViews_.Begin(); i != offscreenViews_.End(); ++i)
+    {
+        UIOffscreenView* view = *i;
+
+        // Create a vertex buffer for the view, if it doesn't already exist.
+        if (view->vertexBuffer_.Null())
+            view->vertexBuffer_ = vertexBuffer_ = new VertexBuffer(context_);
+
+        SetVertexData(view->vertexBuffer_, view->vertexData_);
+        Render(view->vertexBuffer_, view->batches_, 0, view->batches_.Size(), view->GetRenderSurface(), view->GetClearRenderTargetEachFrame());
+    }
+
+    // Render the on-screen root widget.
     SetVertexData(vertexBuffer_, vertexData_);
-    Render(vertexBuffer_, batches_, 0, batches_.Size());
+    Render(vertexBuffer_, batches_, 0, batches_.Size(), 0, false);
 
+    // Render the SystemUI (probably going away at some point).
     SystemUI::SystemUI* systemUI = GetSubsystem<SystemUI::SystemUI>();
     if (systemUI)
         systemUI->Render();
@@ -422,35 +454,38 @@ void UI::Render(bool resetRenderTargets)
 
 void UI::HandleRenderUpdate(StringHash eventType, VariantMap& eventData)
 {
-    // Get rendering batches from the non-modal UI elements
-    batches_.Clear();
-    vertexData_.Clear();
-
-    tb::TBRect rect = rootWidget_->GetRect();
+    TBAnimationManager::Update();
 
-    IntRect currentScissor = IntRect(0, 0, rect.w, rect.h);
-    GetBatches(batches_, vertexData_, currentScissor);
+    // Render the off-screen root widgets.
+    for (HashSet<UIOffscreenView*>::Iterator i = offscreenViews_.Begin(); i != offscreenViews_.End(); ++i)
+    {
+        UIOffscreenView* view = *i;
+        GetBatches(&view->batches_, &view->vertexData_, view->widget_, renderer_);
+    }
 
+    // Render the on-screen root widget.
+    GetBatches(&batches_, &vertexData_, rootWidget_, renderer_);
 }
 
-void UI::GetBatches(PODVector<UIBatch>& batches, PODVector<float>& vertexData, const IntRect& currentScissor)
+void UI::GetBatches(PODVector<UIBatch>* batches, PODVector<float>* vertexData, tb::TBWidget* rootWidget, UIRenderer* renderer, bool clearBatches, bool clearVertexData)
 {
-    //if (!initialized_)
-    //    return;
+    if (clearBatches)
+        batches->Clear();
+    if (clearVertexData)
+        vertexData->Clear();
 
-    TBAnimationManager::Update();
-
-    rootWidget_->InvokeProcessStates();
-    rootWidget_->InvokeProcess();
+    rootWidget->InvokeProcessStates();
+    rootWidget->InvokeProcess();
 
-    tb::g_renderer->BeginPaint(rootWidget_->GetRect().w, rootWidget_->GetRect().h);
+    tb::TBRect rect = rootWidget->GetRect();
+    renderer->BeginPaint(rect.w, rect.h);
 
-    renderer_->currentScissor_ = currentScissor;
-    renderer_->batches_ = &batches;
-    renderer_->vertexData_ = &vertexData;
-    rootWidget_->InvokePaint(tb::TBWidget::PaintProps());
+    renderer->currentScissor_ = IntRect(0, 0, rect.w, rect.h);
+    renderer->batches_ = batches;
+    renderer->vertexData_ = vertexData;
+    rootWidget->InvokePaint(tb::TBWidget::PaintProps());
 
-    tb::g_renderer->EndPaint();
+    renderer->EndPaint();
 }
 
 void UI::SubmitBatchVertexData(Texture* texture, const PODVector<float>& vertexData)
@@ -621,19 +656,22 @@ void UI::PruneUnreachableWidgets()
 
         itr.GotoNext();
 
+        if (itr->second_.Null())
+            continue;
+
         VariantMap eventData;
-        eventData[WidgetDeleted::P_WIDGET] = (UIWidget*) (*itr).second_;
-        (*itr).second_->SendEvent(E_WIDGETDELETED, eventData);
+        eventData[WidgetDeleted::P_WIDGET] = (UIWidget*) itr->second_;
+        itr->second_->SendEvent(E_WIDGETDELETED, eventData);
 
-        tb::TBWidget* toDelete = (*itr).first_;
+        tb::TBWidget* toDelete = itr->first_;
         UnwrapWidget(toDelete);
         delete toDelete;
-
     }
 }
 
 void UI::WrapWidget(UIWidget* widget, tb::TBWidget* tbwidget)
 {
+    assert (widget);
     assert (!widgetWrap_.Contains(tbwidget));
     widgetWrap_[tbwidget] = widget;
 }
@@ -946,20 +984,20 @@ void UI::HandleConsoleClosed(StringHash eventType, VariantMap& eventData)
 
 SystemUI::MessageBox* UI::ShowSystemMessageBox(const String& title, const String& message)
 {
-
     ResourceCache* cache = GetSubsystem<ResourceCache>();
     XMLFile* xmlFile = cache->GetResource<XMLFile>("UI/DefaultStyle.xml");
 
     SystemUI::MessageBox* messageBox = new SystemUI::MessageBox(context_, message, title, 0, xmlFile);
 
     return messageBox;
-
-
 }
 
 UIWidget* UI::GetWidgetAt(int x, int y, bool include_children)
 {
-    return WrapWidget(rootWidget_->GetWidgetAt(x, y, include_children));
+    IntVector2 viewPos;
+    tb::TBWidget* widget = GetInternalWidgetAndProjectedPositionFor(IntVector2(x, y), viewPos);
+
+    return WrapWidget(widget->GetWidgetAt(viewPos.x_, viewPos.y_, include_children));
 }
 
 bool UI::OnWidgetInvokeEvent(tb::TBWidget *widget, const tb::TBWidgetEvent &ev)

+ 15 - 4
Source/Atomic/UI/UI.h

@@ -26,14 +26,17 @@
 
 #include "../Core/Object.h"
 #include "../UI/UIBatch.h"
+#include "../Container/HashSet.h"
 
 namespace Atomic
 {
 
+class RenderSurface;
 class VertexBuffer;
 class UIRenderer;
 class UIWidget;
 class UIPopupWindow;
+class UIOffscreenView;
 
 namespace SystemUI
 {
@@ -52,13 +55,14 @@ public:
     virtual ~UI();
 
     tb::TBWidget* GetRootWidget() { return rootWidget_; }
+    HashSet<UIOffscreenView*>* GetOffscreenViews() { return &offscreenViews_; }
     bool LoadResourceFile(tb::TBWidget* widget, const String& filename);
 
     void SetKeyboardDisabled(bool disabled) {keyboardDisabled_ = disabled; }
     void SetInputDisabled(bool disabled) { inputDisabled_ = disabled; }
 
-    void Render(bool resetRenderTargets = true);
-    void GetBatches(PODVector<UIBatch>& batches, PODVector<float>& vertexData, const IntRect& currentScissor);
+    void Render();
+    static void GetBatches(PODVector<UIBatch>* batches, PODVector<float>* vertexData, tb::TBWidget* rootWidget, UIRenderer* renderer, bool clearBatches = true, bool clearVertexData = true);
     void SubmitBatchVertexData(Texture* texture, const PODVector<float>& vertexData);
 
     void Initialize(const String& languageFile);
@@ -116,6 +120,10 @@ public:
 
     UIWidget* GetHoveredWidget();
 
+    UIOffscreenView* FindOffscreenViewAtScreenPosition(const IntVector2& screenPos, IntVector2& viewPos);
+
+    tb::TBWidget* GetInternalWidgetAndProjectedPositionFor(const IntVector2& screenPos, IntVector2& viewPos);
+
     // Debugging
     static void DebugShowSettingsWindow(UIWidget* parent);
 
@@ -128,8 +136,8 @@ private:
     void HandleRenderUpdate(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);
+    void Render(VertexBuffer* buffer, const PODVector<UIBatch>& batches, unsigned batchStart, unsigned batchEnd, RenderSurface* renderSurface, bool clearRenderSurface);
+    static void SetVertexData(VertexBuffer* dest, const PODVector<float>& vertexData);
 
     // TBWidgetListener
     void OnWidgetDelete(tb::TBWidget *widget);
@@ -140,6 +148,7 @@ private:
 
 
     tb::TBWidget* rootWidget_;
+    HashSet<UIOffscreenView*> offscreenViews_;
     UIRenderer* renderer_;
 
     /// UI rendering batches.
@@ -166,6 +175,8 @@ private:
     
     float tooltipHoverTime_;
 
+    bool InvokeKey(unsigned key, unsigned special_key, unsigned modifierkeys, bool keydown);
+
     // Events
     void HandleScreenMode(StringHash eventType, VariantMap& eventData);
     void HandleMouseButtonDown(StringHash eventType, VariantMap& eventData);

+ 129 - 42
Source/Atomic/UI/UIInput.cpp

@@ -25,22 +25,25 @@
 using namespace tb;
 
 #include "../Core/Timer.h"
+#include "../Graphics/Graphics.h"
+#include "../IO/Log.h"
 #include "../Input/Input.h"
 #include "../Input/InputEvents.h"
 
 #include "UI.h"
 #include "UIEvents.h"
+#include "UIOffscreenView.h"
 
 namespace Atomic
 {
 
-static MODIFIER_KEYS GetModifierKeys(int qualifiers, bool superKey)
+static inline MODIFIER_KEYS GetModifierKeys(int qualifiers, bool superKey)
 {
     MODIFIER_KEYS code = TB_MODIFIER_NONE;
-    if (qualifiers & QUAL_ALT)    code |= TB_ALT;
-    if (qualifiers & QUAL_CTRL)    code |= TB_CTRL;
-    if (qualifiers & QUAL_SHIFT)    code |= TB_SHIFT;
-    if (superKey)    code |= TB_SUPER;
+    if (qualifiers & QUAL_ALT)   code |= TB_ALT;
+    if (qualifiers & QUAL_CTRL)  code |= TB_CTRL;
+    if (qualifiers & QUAL_SHIFT) code |= TB_SHIFT;
+    if (superKey)                code |= TB_SUPER;
     return code;
 }
 
@@ -53,6 +56,56 @@ static int toupr_ascii(int ascii)
     return ascii;
 }
 
+UIOffscreenView* UI::FindOffscreenViewAtScreenPosition(const IntVector2& screenPos, IntVector2& viewPos)
+{
+    for (HashSet<UIOffscreenView*>::Iterator it = offscreenViews_.Begin(); it != offscreenViews_.End(); ++it)
+    {
+        UIOffscreenView* osView = *it;
+        IntRect rect = osView->inputRect_;
+        Camera* camera = osView->inputCamera_;
+        Octree* octree = osView->inputOctree_;
+        Drawable* drawable = osView->inputDrawable_;
+        bool rectIsDefault = rect == IntRect::ZERO;
+
+        if (!camera || !octree || !drawable || (!rectIsDefault && !rect.IsInside(screenPos)))
+            continue;
+
+        Vector2 normPos(screenPos.x_ - rect.left_, screenPos.y_ - rect.top_);
+        normPos /= rectIsDefault ? Vector2(graphics_->GetWidth(), graphics_->GetHeight()) : Vector2(rect.Width(), rect.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->RaycastSingle(query);
+
+        if (queryResultVector.Empty())
+            continue;
+
+        RayQueryResult& queryResult(queryResultVector.Front());
+
+        if (queryResult.drawable_ != drawable)
+            continue;
+
+        Vector2& uv = queryResult.textureUV_;
+        viewPos = IntVector2(uv.x_ * osView->GetWidth(), uv.y_ * osView->GetHeight());
+
+        return osView;
+    }
+
+    return nullptr;
+}
+
+tb::TBWidget* UI::GetInternalWidgetAndProjectedPositionFor(const IntVector2& screenPos, IntVector2& viewPos)
+{
+    UIOffscreenView* osView = FindOffscreenViewAtScreenPosition(screenPos, viewPos);
+    if (osView)
+        return osView->GetInternalWidget();
+
+    viewPos = screenPos;
+    return rootWidget_;
+}
+
 void UI::HandleMouseButtonDown(StringHash eventType, VariantMap& eventData)
 {
     if (inputDisabled_ || consoleVisible_)
@@ -88,12 +141,15 @@ void UI::HandleMouseButtonDown(StringHash eventType, VariantMap& eventData)
         counter = 1;
 
     last_time = time;
-    if (button == MOUSEB_RIGHT)
-        rootWidget_->InvokeRightPointerDown(pos.x_, pos.y_, counter, mod);
-    else
-        rootWidget_->InvokePointerDown(pos.x_, pos.y_, counter, mod, false);
 
 
+    IntVector2 viewPos;
+    tb::TBWidget* widget = UI::GetInternalWidgetAndProjectedPositionFor(pos, viewPos);
+
+    if (button == MOUSEB_RIGHT)
+        widget->InvokeRightPointerDown(viewPos.x_, viewPos.y_, counter, mod);
+    else
+        widget->InvokePointerDown(viewPos.x_, viewPos.y_, counter, mod, false);
 }
 
 
@@ -120,10 +176,15 @@ void UI::HandleMouseButtonUp(StringHash eventType, VariantMap& eventData)
     MODIFIER_KEYS mod = GetModifierKeys(qualifiers, superdown);
 
 
+    IntVector2 viewPos;
+    tb::TBWidget* widget = UI::GetInternalWidgetAndProjectedPositionFor(pos, viewPos);
+
     if (button == MOUSEB_RIGHT)
-        rootWidget_->InvokeRightPointerUp(pos.x_, pos.y_, mod);
+        widget->InvokeRightPointerUp(viewPos.x_, viewPos.y_, mod);
     else
-        rootWidget_->InvokePointerUp(pos.x_, pos.y_, mod, false);
+        widget->InvokePointerUp(viewPos.x_, viewPos.y_, mod, false);
+
+    // InvokePointerUp() seems to do the right thing no mater which root widget gets the call.
 }
 
 
@@ -134,10 +195,13 @@ void UI::HandleMouseMove(StringHash eventType, VariantMap& eventData)
     if (inputDisabled_ || consoleVisible_)
         return;
 
-    int px = eventData[P_X].GetInt();
-    int py = eventData[P_Y].GetInt();
+    IntVector2 pos(eventData[P_X].GetInt(), eventData[P_Y].GetInt());
 
-    rootWidget_->InvokePointerMove(px, py, tb::TB_MODIFIER_NONE, false);
+
+    IntVector2 viewPos;
+    tb::TBWidget* widget = UI::GetInternalWidgetAndProjectedPositionFor(pos, viewPos);
+
+    widget->InvokePointerMove(viewPos.x_, viewPos.y_, tb::TB_MODIFIER_NONE, false);
 
     tooltipHoverTime_ = 0;
 }
@@ -151,11 +215,14 @@ void UI::HandleMouseWheel(StringHash eventType, VariantMap& eventData)
     using namespace MouseWheel;
 
     int delta = eventData[P_WHEEL].GetInt();
-
     Input* input = GetSubsystem<Input>();
+    IntVector2 pos(input->GetMousePosition().x_, input->GetMousePosition().y_);
+
 
-    rootWidget_->InvokeWheel(input->GetMousePosition().x_, input->GetMousePosition().y_, 0, -delta, tb::TB_MODIFIER_NONE);
+    IntVector2 viewPos;
+    tb::TBWidget* widget = UI::GetInternalWidgetAndProjectedPositionFor(pos, viewPos);
 
+    widget->InvokeWheel(viewPos.x_, viewPos.y_, 0, -delta, tb::TB_MODIFIER_NONE);
 }
 
 //Touch Input
@@ -165,11 +232,10 @@ void UI::HandleTouchBegin(StringHash eventType, VariantMap& eventData)
         return;
 
     using namespace TouchBegin;
-    
+
     int touchId = eventData[P_TOUCHID].GetInt();
-    int px = eventData[P_X].GetInt();
-    int py = eventData[P_Y].GetInt();
-    
+    IntVector2 pos(eventData[P_X].GetInt(), eventData[P_Y].GetInt());
+
     static double last_time = 0;
     static int counter = 1;
 
@@ -182,22 +248,29 @@ void UI::HandleTouchBegin(StringHash eventType, VariantMap& eventData)
         counter = 1;
 
     last_time = time;
-    
-    rootWidget_->InvokePointerDown(px, py, counter, TB_MODIFIER_NONE, true, touchId);
+
+
+    IntVector2 viewPos;
+    tb::TBWidget* widget = UI::GetInternalWidgetAndProjectedPositionFor(pos, viewPos);
+
+    widget->InvokePointerDown(viewPos.x_, viewPos.y_, counter, TB_MODIFIER_NONE, true, touchId);
 }
 
 void UI::HandleTouchMove(StringHash eventType, VariantMap& eventData)
 {
     if (inputDisabled_ || consoleVisible_)
         return;
-    
+
     using namespace TouchMove;
-    
+
     int touchId = eventData[P_TOUCHID].GetInt();
-    int px = eventData[P_X].GetInt();
-    int py = eventData[P_Y].GetInt();
+    IntVector2 pos(eventData[P_X].GetInt(), eventData[P_Y].GetInt());
+
+
+    IntVector2 viewPos;
+    tb::TBWidget* widget = UI::GetInternalWidgetAndProjectedPositionFor(pos, viewPos);
 
-    rootWidget_->InvokePointerMove(px, py, TB_MODIFIER_NONE, true, touchId);
+    widget->InvokePointerMove(viewPos.x_, viewPos.y_, TB_MODIFIER_NONE, true, touchId);
 }
 
 void UI::HandleTouchEnd(StringHash eventType, VariantMap& eventData)
@@ -208,10 +281,13 @@ void UI::HandleTouchEnd(StringHash eventType, VariantMap& eventData)
     using namespace TouchEnd;
 
     int touchId = eventData[P_TOUCHID].GetInt();
-    int px = eventData[P_X].GetInt();
-    int py = eventData[P_Y].GetInt();
+    IntVector2 pos(eventData[P_X].GetInt(), eventData[P_Y].GetInt());
 
-    rootWidget_->InvokePointerUp(px, py, TB_MODIFIER_NONE, true, touchId);
+
+    IntVector2 viewPos;
+    tb::TBWidget* widget = UI::GetInternalWidgetAndProjectedPositionFor(pos, viewPos);
+
+    widget->InvokePointerUp(viewPos.x_, viewPos.y_, TB_MODIFIER_NONE, true, touchId);
 }
 
 static bool InvokeShortcut(UI* ui, int key, SPECIAL_KEY special_key, MODIFIER_KEYS modifierkeys, bool down)
@@ -298,12 +374,27 @@ static bool InvokeShortcut(UI* ui, int key, SPECIAL_KEY special_key, MODIFIER_KE
     return true;
 }
 
-static bool InvokeKey(UI* ui, TBWidget* root, unsigned int key, SPECIAL_KEY special_key, MODIFIER_KEYS modifierkeys, bool keydown)
+bool UI::InvokeKey(unsigned key, unsigned special_key, unsigned modifierkeys, bool keydown)
 {
-    if (InvokeShortcut(ui, key, special_key, modifierkeys, keydown))
+    if (InvokeShortcut(this, key, SPECIAL_KEY(special_key), MODIFIER_KEYS(modifierkeys), keydown))
         return true;
-    root->InvokeKey(key, special_key, modifierkeys, keydown);
-    return true;
+
+    for (HashSet<UIOffscreenView*>::Iterator it = offscreenViews_.Begin(); it != offscreenViews_.End(); ++it)
+    {
+        UIOffscreenView* osView = *it;
+        IntRect rect = osView->inputRect_;
+        Camera* camera = osView->inputCamera_;
+        Octree* octree = osView->inputOctree_;
+        Drawable* drawable = osView->inputDrawable_;
+
+        if (!camera || !octree || !drawable)
+            continue;
+
+        if (osView->GetInternalWidget()->InvokeKey(key, SPECIAL_KEY(special_key), MODIFIER_KEYS(modifierkeys), keydown))
+            return true;
+    }
+
+    return rootWidget_->InvokeKey(key, SPECIAL_KEY(special_key), MODIFIER_KEYS(modifierkeys), keydown);
 }
 
 
@@ -423,14 +514,13 @@ void UI::HandleKey(bool keydown, int keycode, int scancode)
     {
         if (mod & TB_SUPER)
         {
-            InvokeKey(this, rootWidget_, keycode, TB_KEY_UNDEFINED, mod, keydown);
+            InvokeKey(keycode, TB_KEY_UNDEFINED, mod, keydown);
         }
     }
     else
     {
-        InvokeKey(this, rootWidget_, 0, specialKey, mod, keydown);
+        InvokeKey(0, specialKey, mod, keydown);
     }
-
 }
 
 void UI::HandleKeyDown(StringHash eventType, VariantMap& eventData)
@@ -467,7 +557,6 @@ void UI::HandleKeyDown(StringHash eventType, VariantMap& eventData)
     shortcutData[UIShortcut::P_QUALIFIERS] = eventData[P_QUALIFIERS].GetInt();
 
     SendEvent(E_UISHORTCUT, shortcutData);
-
 }
 
 void UI::HandleKeyUp(StringHash eventType, VariantMap& eventData)
@@ -481,7 +570,6 @@ void UI::HandleKeyUp(StringHash eventType, VariantMap& eventData)
     int scancode = eventData[P_SCANCODE].GetInt();
 
     HandleKey(false, keycode, scancode);
-
 }
 
 void UI::HandleTextInput(StringHash eventType, VariantMap& eventData)
@@ -495,9 +583,8 @@ void UI::HandleTextInput(StringHash eventType, VariantMap& eventData)
 
     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(text[i], TB_KEY_UNDEFINED, TB_MODIFIER_NONE, true);
+        InvokeKey(text[i], TB_KEY_UNDEFINED, TB_MODIFIER_NONE, false);
     }
-
 }
 }

+ 263 - 0
Source/Atomic/UI/UIOffscreenView.cpp

@@ -0,0 +1,263 @@
+//
+// Copyright (c) 2014-2016, 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 <TurboBadger/tb_widgets.h>
+
+#include "../Graphics/Graphics.h"
+#include "../Input/Input.h"
+#include "../Input/InputEvents.h"
+#include "UI.h"
+#include "UIOffscreenView.h"
+
+using namespace tb;
+
+namespace Atomic
+{
+
+UIOffscreenView::UIOffscreenView(Context* context) : UIWidget(context, false)
+{
+    widget_ = new TBWidget();
+    widget_->SetDelegate(this);
+    widget_->SetVisibilility(tb::WIDGET_VISIBILITY_VISIBLE);
+
+    UI* ui = GetSubsystem<UI>();
+    ui->WrapWidget(this, widget_);
+
+    texture_ = new Texture2D(context_);
+
+    clearRenderTargetEachFrame_ = true;
+
+    // Set initial size for view
+    SetSize(512, 512);
+
+    // Set initial filtering mode to bilinear.
+    GetTexture2D()->SetFilterMode(Atomic::FILTER_BILINEAR);
+
+    // Add our self to the ui subsystem.
+    ui->GetOffscreenViews()->Insert(this);
+}
+
+UIOffscreenView::~UIOffscreenView()
+{
+    UI* ui = GetSubsystem<UI>();
+
+    // Remove our self from the ui subsystem, if it's still around.
+    if (ui)
+        ui->GetOffscreenViews()->Erase(this);
+}
+
+void UIOffscreenView::SetSize(int width, int height)
+{
+    Graphics* graphics = GetSubsystem<Graphics>();
+    widget_->SetSize(width, height);
+    GetTexture2D()->SetSize(width, height, graphics->GetRGBAFormat(), Atomic::TEXTURE_RENDERTARGET);
+}
+
+Material* UIOffscreenView::GetMaterial()
+{
+    if (material_.Null())
+    {
+        // If the convenience material doesn't exist, then create it.
+        ResourceCache* cache = GetSubsystem<ResourceCache>();
+        material_ = new Material(context_);
+        material_->SetTechnique(0, cache->GetResource<Technique>("Techniques/Diff.xml"));
+        material_->SetTexture(Atomic::TU_DIFFUSE, texture_);
+    }
+    return material_;
+}
+
+void UIOffscreenView::RegisterInput(Camera* camera, Octree* octree, Drawable* drawable, const IntRect& rect)
+{
+    inputCamera_ = camera;
+    inputOctree_ = octree;
+    inputDrawable_ = drawable;
+    inputRect_ = rect;
+}
+
+void UIOffscreenView::UnregisterInput()
+{
+    inputCamera_.Reset();
+    inputOctree_.Reset();
+    inputDrawable_.Reset();
+    inputRect_ = IntRect::ZERO;
+}
+
+bool UIOffscreenView::SavePNGTo(Serializer& serializer) const
+{
+    Image image(context_);
+    if (!image.SetSize(GetWidth(), GetHeight(), 4))
+        return false;
+    if (!GetTexture2D()->GetData(0, image.GetData()))
+        return false;
+    if (!image.Save(serializer))
+        return false;
+    return true;
+}
+
+bool UIOffscreenView::SavePNG(const String& fileName) const
+{
+    File outFile(context_, fileName, FILE_WRITE);
+    if (!outFile.IsOpen())
+        return false;
+    return SavePNGTo(outFile);
+}
+
+static inline MODIFIER_KEYS GetModifierKeys(int qualifiers, bool superKey)
+{
+    MODIFIER_KEYS code = TB_MODIFIER_NONE;
+    if (qualifiers & QUAL_ALT)   code |= TB_ALT;
+    if (qualifiers & QUAL_CTRL)  code |= TB_CTRL;
+    if (qualifiers & QUAL_SHIFT) code |= TB_SHIFT;
+    if (superKey)                code |= TB_SUPER;
+    return code;
+}
+
+static inline SPECIAL_KEY GetSpecialKey(int keycode)
+{
+    SPECIAL_KEY specialKey;
+
+    switch (keycode)
+    {
+    case KEY_RETURN:
+    case KEY_RETURN2:
+    case KEY_KP_ENTER:
+        specialKey = TB_KEY_ENTER;
+        break;
+    case KEY_F1:
+        specialKey = TB_KEY_F1;
+        break;
+    case KEY_F2:
+        specialKey = TB_KEY_F2;
+        break;
+    case KEY_F3:
+        specialKey = TB_KEY_F3;
+        break;
+    case KEY_F4:
+        specialKey = TB_KEY_F4;
+        break;
+    case KEY_F5:
+        specialKey = TB_KEY_F5;
+        break;
+    case KEY_F6:
+        specialKey = TB_KEY_F6;
+        break;
+    case KEY_F7:
+        specialKey = TB_KEY_F7;
+        break;
+    case KEY_F8:
+        specialKey = TB_KEY_F8;
+        break;
+    case KEY_F9:
+        specialKey = TB_KEY_F9;
+        break;
+    case KEY_F10:
+        specialKey = TB_KEY_F10;
+        break;
+    case KEY_F11:
+        specialKey = TB_KEY_F11;
+        break;
+    case KEY_F12:
+        specialKey = TB_KEY_F12;
+        break;
+    case KEY_LEFT:
+        specialKey = TB_KEY_LEFT;
+        break;
+    case KEY_UP:
+        specialKey = TB_KEY_UP;
+        break;
+    case KEY_RIGHT:
+        specialKey = TB_KEY_RIGHT;
+        break;
+    case KEY_DOWN:
+        specialKey = TB_KEY_DOWN;
+        break;
+    case KEY_PAGEUP:
+        specialKey = TB_KEY_PAGE_UP;
+        break;
+    case KEY_PAGEDOWN:
+        specialKey = TB_KEY_PAGE_DOWN;
+        break;
+    case KEY_HOME:
+        specialKey = TB_KEY_HOME;
+        break;
+    case KEY_END:
+        specialKey = TB_KEY_END;
+        break;
+    case KEY_INSERT:
+        specialKey = TB_KEY_INSERT;
+        break;
+    case KEY_TAB:
+        specialKey = TB_KEY_TAB;
+        break;
+    case KEY_DELETE:
+        specialKey = TB_KEY_DELETE;
+        break;
+    case KEY_BACKSPACE:
+        specialKey = TB_KEY_BACKSPACE;
+        break;
+    case KEY_ESCAPE:
+        specialKey = TB_KEY_ESC;
+        break;
+    default:
+        specialKey = TB_KEY_UNDEFINED;
+        break;
+    }
+
+    return specialKey;
+}
+
+void UIOffscreenView::InvokeRightPointerDown(int x, int y, int click_count, int qualifiers, bool super)
+{
+    widget_->InvokeRightPointerDown(x, y, click_count, GetModifierKeys(qualifiers, super));
+}
+
+void UIOffscreenView::InvokeRightPointerUp(int x, int y, int qualifiers, bool super)
+{
+    widget_->InvokeRightPointerUp(x, y, GetModifierKeys(qualifiers, super));
+}
+
+void UIOffscreenView::InvokePointerDown(int x, int y, int click_count, int qualifiers, bool touch, int touchId, bool super)
+{
+    widget_->InvokePointerDown(x, y, click_count, GetModifierKeys(qualifiers, super), touch, touchId);
+}
+
+void UIOffscreenView::InvokePointerUp(int x, int y, int qualifiers, bool touch, int touchId, bool super)
+{
+    widget_->InvokePointerUp(x, y, GetModifierKeys(qualifiers, super), touch, touchId);
+}
+
+void UIOffscreenView::InvokePointerMove(int x, int y, int qualifiers, bool touch, int touchId, bool super)
+{
+    widget_->InvokePointerMove(x, y, GetModifierKeys(qualifiers, super), touch, touchId);
+}
+
+void UIOffscreenView::InvokeWheel(int x, int y, int delta_x, int delta_y, int qualifiers, bool super)
+{
+    widget_->InvokeWheel(x, y, delta_x, delta_y, GetModifierKeys(qualifiers, super));
+}
+
+bool UIOffscreenView::InvokeKey(int key, int keycode, bool down, int qualifiers, bool super)
+{
+    return widget_->InvokeKey(key, GetSpecialKey(keycode), GetModifierKeys(qualifiers, super), down);
+}
+
+}

+ 103 - 0
Source/Atomic/UI/UIOffscreenView.h

@@ -0,0 +1,103 @@
+//
+// Copyright (c) 2014-2016, 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 "../Graphics/Camera.h"
+#include "../Graphics/Material.h"
+#include "../Graphics/Technique.h"
+#include "../Graphics/Texture2D.h"
+#include "../Graphics/Octree.h"
+#include "../Graphics/VertexBuffer.h"
+#include "../Resource/ResourceCache.h"
+
+#include "UIWidget.h"
+
+namespace Atomic
+{
+
+/// a root widget that renders offscreen
+class UIOffscreenView : public UIWidget
+{
+    friend UI;
+
+    ATOMIC_OBJECT(UIOffscreenView, UIWidget)
+
+public:
+
+    /// Construct
+    UIOffscreenView(Context* context);
+
+    /// Destruct
+    virtual ~UIOffscreenView();
+
+    /// Get the current width of the texture.
+    int GetWidth() const { return GetTexture2D()->GetWidth(); }
+    /// Get the current height of the texture.
+    int GetHeight() const { return GetTexture2D()->GetHeight(); }
+    /// Set the dimensions of the texture.
+    void SetSize(int width, int height);
+    /// Get the Texture2D to which this view is rendering.
+    Texture2D* GetTexture2D() const { return texture_; }
+    /// Return render surface.
+    RenderSurface* GetRenderSurface() const { return GetTexture2D()->GetRenderSurface(); }
+    /// Get a material for this view (for convenience only/doesn't have to be used).
+    Material* GetMaterial();
+    /// Register instances nessisary to process input events automatically.
+    void RegisterInput(Camera* camera, Octree* octree, Drawable* drawable, const IntRect& rect = IntRect::ZERO);
+    /// Unregister instances to return to manual input.
+    void UnregisterInput();
+    /// Get wether to clear the render target each frame.
+    bool GetClearRenderTargetEachFrame() const { return clearRenderTargetEachFrame_; }
+    /// Set wether to clear the render target each frame.
+    void SetClearRenderTargetEachFrame(bool clear) { clearRenderTargetEachFrame_ = clear; }
+    /// Save PNG to a serializer.
+    bool SavePNGTo(Serializer& dest) const;
+    /// Save PNG to a file.
+    bool SavePNG(const String& fileName) const;
+
+    void InvokeRightPointerDown(int x, int y, int click_count, int qualifiers, bool super = false);
+    void InvokeRightPointerUp(int x, int y, int qualifiers, bool super = false);
+    void InvokePointerDown(int x, int y, int click_count, int qualifiers, bool touch, int touchId, bool super = false);
+    void InvokePointerUp(int x, int y, int qualifiers, bool touch, int touchId, bool super = false);
+    void InvokePointerMove(int x, int y, int qualifiers, bool touch, int touchId, bool super = false);
+    void InvokeWheel(int x, int y, int delta_x, int delta_y, int qualifiers, bool super = false);
+    bool InvokeKey(int key, int keycode, bool down, int qualifiers, bool super = false);
+
+protected:
+
+    SharedPtr<Texture2D> texture_;
+    SharedPtr<Material> material_;
+    PODVector<UIBatch> batches_;
+    PODVector<float> vertexData_;
+    SharedPtr<VertexBuffer> vertexBuffer_;
+    SharedPtr<Camera> inputCamera_;
+    SharedPtr<Octree> inputOctree_;
+    SharedPtr<Drawable> inputDrawable_;
+    IntRect inputRect_;
+    bool clearRenderTargetEachFrame_;
+
+private:
+
+};
+
+}