From a7049ffc3158f469d7db903f375a730421736831 Mon Sep 17 00:00:00 2001
From: Dominic Masters <dominic@domsplace.com>
Date: Thu, 27 Oct 2022 08:16:55 -0700
Subject: [PATCH] Lots of UI Component Updates

---
 src/dawn/CMakeLists.txt                       |   3 +-
 src/dawn/input/_InputManager.hpp              |   2 -
 src/dawn/scene/SceneItem.cpp                  |  37 ++++-
 src/dawn/scene/SceneItem.hpp                  |   1 +
 src/dawn/scene/SceneItemComponent.cpp         |  13 ++
 src/dawn/scene/SceneItemComponent.hpp         |  18 ++-
 src/dawn/scene/components/display/Camera.cpp  |   2 +-
 src/dawn/scene/components/display/Camera.hpp  |   2 +-
 .../scene/components/display/Material.cpp     |   3 -
 .../scene/components/display/Material.hpp     |   5 -
 .../scene/components/display/MeshRenderer.cpp |   4 -
 .../scene/components/display/MeshRenderer.hpp |   5 -
 src/dawn/scene/components/ui/UICanvas.cpp     |  24 +++-
 src/dawn/scene/components/ui/UICanvas.hpp     |  12 +-
 src/dawn/ui/CMakeLists.txt                    |   1 +
 src/dawn/ui/UIBorder.cpp                      | 130 ++++++++++++++++++
 src/dawn/ui/UIBorder.hpp                      |  30 ++++
 src/dawn/ui/UIComponent.cpp                   |  44 +++++-
 src/dawn/ui/UIComponent.hpp                   |  15 +-
 src/dawn/visualnovel/CMakeLists.txt           |   8 ++
 src/dawn/visualnovel/ui/CMakeLists.txt        |  10 ++
 .../visualnovel/ui/VisualNovelTextbox.cpp     |  21 +++
 .../visualnovel/ui/VisualNovelTextbox.hpp     |  22 +++
 src/dawnglfw/host/DawnGLFWHost.cpp            |   8 +-
 src/dawnpokergame/game/DawnPokerGame.cpp      |  23 ++--
 25 files changed, 387 insertions(+), 56 deletions(-)
 create mode 100644 src/dawn/ui/UIBorder.cpp
 create mode 100644 src/dawn/ui/UIBorder.hpp
 create mode 100644 src/dawn/visualnovel/CMakeLists.txt
 create mode 100644 src/dawn/visualnovel/ui/CMakeLists.txt
 create mode 100644 src/dawn/visualnovel/ui/VisualNovelTextbox.cpp
 create mode 100644 src/dawn/visualnovel/ui/VisualNovelTextbox.hpp

diff --git a/src/dawn/CMakeLists.txt b/src/dawn/CMakeLists.txt
index 5d1b4b2f..088a0383 100644
--- a/src/dawn/CMakeLists.txt
+++ b/src/dawn/CMakeLists.txt
@@ -21,4 +21,5 @@ add_subdirectory(asset)
 add_subdirectory(display)
 add_subdirectory(input)
 add_subdirectory(scene)
-add_subdirectory(ui)
\ No newline at end of file
+add_subdirectory(ui)
+add_subdirectory(visualnovel)
\ No newline at end of file
diff --git a/src/dawn/input/_InputManager.hpp b/src/dawn/input/_InputManager.hpp
index ae1d3ddc..885b52c2 100644
--- a/src/dawn/input/_InputManager.hpp
+++ b/src/dawn/input/_InputManager.hpp
@@ -146,8 +146,6 @@ namespace Dawn {
             ++bindIt;
           }
 
-          std::cout << "Bind " << it->first << ": " << value << std::endl;
-
           // Set into current values
           if(this->currentIsLeft) {
             this->valuesLeft[it->first] = value;
diff --git a/src/dawn/scene/SceneItem.cpp b/src/dawn/scene/SceneItem.cpp
index 73bf4f5d..5d761b07 100644
--- a/src/dawn/scene/SceneItem.cpp
+++ b/src/dawn/scene/SceneItem.cpp
@@ -16,7 +16,42 @@ SceneItem::SceneItem(Scene &scene, sceneitemid_t id) :
 }
 
 void SceneItem::init() {
-  
+  // Keep checking all components until they have all inited
+  int32_t waitingOn;
+  do {
+    waitingOn = 0;
+
+    // For each component
+    auto it = this->components.begin();
+    while(it != this->components.end()) {
+      // Has this component already inited?
+      if((*it)->hasInitialized) {
+        ++it;
+        continue;
+      }
+
+      // For each dependency.
+      auto deps = (*it)->getDependencies();
+      bool_t waiting = false;
+      auto it2 = deps.begin();
+      while(it2 != deps.end()) {
+        // Has the dep not yet inited?
+        if(!(*it2)->hasInitialized) {
+          waiting = true;
+          break;
+        }
+        ++it2;
+      }
+
+      // Are we waiting for a dep?
+      if(waiting) {
+        waitingOn++;
+      } else {
+        (*it)->init();
+      }
+      ++it;
+    }
+  } while(waitingOn != 0);
 }
 
 SceneItem::~SceneItem() {
diff --git a/src/dawn/scene/SceneItem.hpp b/src/dawn/scene/SceneItem.hpp
index 78ad5b14..c70bc707 100644
--- a/src/dawn/scene/SceneItem.hpp
+++ b/src/dawn/scene/SceneItem.hpp
@@ -6,6 +6,7 @@
 #pragma once
 #include "SceneItemComponent.hpp"
 #include "display/Transform.hpp"
+#include "event/Event.hpp"
 
 namespace Dawn {
   typedef int32_t sceneitemid_t;
diff --git a/src/dawn/scene/SceneItemComponent.cpp b/src/dawn/scene/SceneItemComponent.cpp
index e063ecb6..18e61e41 100644
--- a/src/dawn/scene/SceneItemComponent.cpp
+++ b/src/dawn/scene/SceneItemComponent.cpp
@@ -16,6 +16,15 @@ SceneItemComponent::SceneItemComponent(SceneItem &item) :
 {
 }
 
+void SceneItemComponent::init() {
+  this->onStart();
+  this->hasInitialized = true;
+}
+
+std::vector<SceneItemComponent*> SceneItemComponent::getDependencies() {
+  return std::vector<SceneItemComponent*>();
+}
+
 Scene & SceneItemComponent::getScene() {
   return this->item.scene;
 }
@@ -24,6 +33,10 @@ DawnGame & SceneItemComponent::getGame() {
   return this->item.scene.game;
 }
 
+void SceneItemComponent::onStart() {
+  
+}
+
 SceneItemComponent::~SceneItemComponent() {
 
 }
\ No newline at end of file
diff --git a/src/dawn/scene/SceneItemComponent.hpp b/src/dawn/scene/SceneItemComponent.hpp
index aa9dc8d0..34de32f4 100644
--- a/src/dawn/scene/SceneItemComponent.hpp
+++ b/src/dawn/scene/SceneItemComponent.hpp
@@ -16,6 +16,7 @@ namespace Dawn {
     public:
       SceneItem &item;
       Transform &transform;
+      bool_t hasInitialized = false;
 
       /**
        * Constructs a new SceneItemComponent. Components are attached to
@@ -28,9 +29,17 @@ namespace Dawn {
       
       /**
        * Requested on the first frame that the parent scene item has become
-       * active.
+       * active, after all of my dependencies are ready.
        */
-      virtual void start() = 0;
+      void init();
+      
+      /**
+       * Optionally return all dependencies for this component to wait for init
+       * for before the component will be initialized.
+       * 
+       * @return Array of dependencies.
+       */
+      virtual std::vector<SceneItemComponent*> getDependencies();
 
       /**
        * Shorthand to return the scene that this component's item belongs to.
@@ -44,6 +53,11 @@ namespace Dawn {
        */
       DawnGame & getGame();
 
+      /**
+       * Same as init, but intended for your subclass to override.
+       */
+      virtual void onStart();
+
       /**
        * Cleanup the SceneItemComponent.
        */
diff --git a/src/dawn/scene/components/display/Camera.cpp b/src/dawn/scene/components/display/Camera.cpp
index 737c8f56..74aab987 100644
--- a/src/dawn/scene/components/display/Camera.cpp
+++ b/src/dawn/scene/components/display/Camera.cpp
@@ -65,7 +65,7 @@ float_t Camera::getAspect() {
   return target.getWidth() / target.getHeight();
 }
 
-void Camera::start() {
+void Camera::onStart() {
   this->updateProjection();
 }
 
diff --git a/src/dawn/scene/components/display/Camera.hpp b/src/dawn/scene/components/display/Camera.hpp
index dbc83d4b..1f95bd1e 100644
--- a/src/dawn/scene/components/display/Camera.hpp
+++ b/src/dawn/scene/components/display/Camera.hpp
@@ -73,7 +73,7 @@ namespace Dawn {
       /**
        * Event triggered by the scene item when the item is added to the scene.
        */
-      void start() override;
+      void onStart() override;
 
       /**
        * Disposes a previously initialized camera.
diff --git a/src/dawn/scene/components/display/Material.cpp b/src/dawn/scene/components/display/Material.cpp
index ea8e5a7c..d02fbabb 100644
--- a/src/dawn/scene/components/display/Material.cpp
+++ b/src/dawn/scene/components/display/Material.cpp
@@ -65,7 +65,4 @@ std::shared_ptr<Shader> Material::getShader() {
 void Material::setShader(std::shared_ptr<Shader> shader) {
   this->shader = shader;
   this->updateShaderParameters();
-}
-
-void Material::start() {
 }
\ No newline at end of file
diff --git a/src/dawn/scene/components/display/Material.hpp b/src/dawn/scene/components/display/Material.hpp
index 172fd9e1..30aa54df 100644
--- a/src/dawn/scene/components/display/Material.hpp
+++ b/src/dawn/scene/components/display/Material.hpp
@@ -58,10 +58,5 @@ namespace Dawn {
        * This method assumes that the shader has already been bound.
        */
       void setShaderParameters();
-
-      /**
-       * Overrides the default SceneItemComponent start method.
-       */
-      void start() override;
   };
 }
\ No newline at end of file
diff --git a/src/dawn/scene/components/display/MeshRenderer.cpp b/src/dawn/scene/components/display/MeshRenderer.cpp
index af72d9ff..0cee6c0e 100644
--- a/src/dawn/scene/components/display/MeshRenderer.cpp
+++ b/src/dawn/scene/components/display/MeshRenderer.cpp
@@ -11,8 +11,4 @@ MeshRenderer::MeshRenderer(SceneItem &item) :
   SceneItemComponent(item)
 {
   
-}
-
-void MeshRenderer::start() {
-
 }
\ No newline at end of file
diff --git a/src/dawn/scene/components/display/MeshRenderer.hpp b/src/dawn/scene/components/display/MeshRenderer.hpp
index 37c10279..18adb22f 100644
--- a/src/dawn/scene/components/display/MeshRenderer.hpp
+++ b/src/dawn/scene/components/display/MeshRenderer.hpp
@@ -18,10 +18,5 @@ namespace Dawn {
        * @param item Scene Item this mesh renderer belongs to.
        */
       MeshRenderer(SceneItem &item);
-
-      /**
-       * Overrides the default SceneItemComponent start method.
-       */
-      void start() override;
   };
 }
\ No newline at end of file
diff --git a/src/dawn/scene/components/ui/UICanvas.cpp b/src/dawn/scene/components/ui/UICanvas.cpp
index 0ec9d80f..53724f70 100644
--- a/src/dawn/scene/components/ui/UICanvas.cpp
+++ b/src/dawn/scene/components/ui/UICanvas.cpp
@@ -16,7 +16,6 @@ std::shared_ptr<UICanvas> UICanvas::createCanvas(std::shared_ptr<Scene> scene) {
 }
 
 UICanvas::UICanvas(SceneItem &item) : SceneItemComponent(item) {
-
 }
 
 float_t UICanvas::getWidth() {
@@ -27,6 +26,27 @@ float_t UICanvas::getHeight() {
   return this->getGame().renderManager.getBackBuffer().getHeight();
 }
 
-void UICanvas::start() {
+void UICanvas::onStart() {
+  std::cout << "Canvas event" << std::endl;
+  this->getGame().renderManager.getBackBuffer()
+    .eventRenderTargetResized.addListener(this, &UICanvas::onBackBufferResize)
+  ;
+}
 
+void UICanvas::onBackBufferResize(
+  RenderTarget &target,
+  float_t width,
+  float_t height
+) {
+  auto it = this->children.begin();
+  while(it != this->children.end()) {
+    (*it)->updatePositions();
+    ++it;
+  }
+}
+
+UICanvas::~UICanvas() {
+  this->getGame().renderManager.getBackBuffer()
+    .eventRenderTargetResized.removeListener(this, &UICanvas::onBackBufferResize)
+  ;
 }
\ No newline at end of file
diff --git a/src/dawn/scene/components/ui/UICanvas.hpp b/src/dawn/scene/components/ui/UICanvas.hpp
index 7b027210..0465132c 100644
--- a/src/dawn/scene/components/ui/UICanvas.hpp
+++ b/src/dawn/scene/components/ui/UICanvas.hpp
@@ -5,6 +5,7 @@
 
 #pragma once
 #include "scene/SceneItemComponent.hpp"
+#include "display/RenderTarget.hpp"
 
 namespace Dawn {
   enum UIDrawType {
@@ -16,6 +17,13 @@ namespace Dawn {
   class UIComponent;
 
   class UICanvas : public SceneItemComponent {
+    protected:
+      void onBackBufferResize(
+        RenderTarget &target,
+        float_t width,
+        float_t height
+      );
+
     public:
       static std::shared_ptr<UICanvas> createCanvas(
         std::shared_ptr<Scene> scene
@@ -37,6 +45,8 @@ namespace Dawn {
       float_t getWidth();
       float_t getHeight();
 
-      void start() override;
+      void onStart() override;
+
+      ~UICanvas();
   };
 }
\ No newline at end of file
diff --git a/src/dawn/ui/CMakeLists.txt b/src/dawn/ui/CMakeLists.txt
index 87d0bafc..4dde864d 100644
--- a/src/dawn/ui/CMakeLists.txt
+++ b/src/dawn/ui/CMakeLists.txt
@@ -6,6 +6,7 @@
 # Sources
 target_sources(${DAWN_TARGET_NAME}
   PRIVATE
+    UIBorder.cpp
     UIComponent.cpp
     UILabel.cpp
     UISprite.cpp
diff --git a/src/dawn/ui/UIBorder.cpp b/src/dawn/ui/UIBorder.cpp
new file mode 100644
index 00000000..1d0dceec
--- /dev/null
+++ b/src/dawn/ui/UIBorder.cpp
@@ -0,0 +1,130 @@
+// Copyright (c) 2022 Dominic Masters
+// 
+// This software is released under the MIT License.
+// https://opensource.org/licenses/MIT
+
+#include "UIBorder.hpp"
+
+using namespace Dawn;
+
+UIBorder::UIBorder(UICanvas &canvas) : UIComponent(canvas) {
+  this->mesh.createBuffers(QUAD_VERTICE_COUNT * 9, QUAD_INDICE_COUNT * 9);
+
+  this->texture = new Texture();
+  this->texture->setSize(3, 3);
+  struct Color pixels[9] = {
+    COLOR_WHITE, COLOR_RED, COLOR_BLUE,
+    COLOR_GREEN, COLOR_MAGENTA, COLOR_BLACK,
+    COLOR_CORNFLOWER_BLUE, COLOR_WHITE, COLOR_MAGENTA
+  };
+  this->texture->buffer(pixels);
+
+  this->updatePositions();
+}
+
+void UIBorder::updatePositions() {
+  UIComponent::updatePositions();
+
+  glm::vec2 oneThird = this->uv1 / 3.0f;
+  glm::vec2 overallDimensions = glm::vec2(this->getWidth(), this->getHeight());
+  glm::vec2 innerDimensions = overallDimensions - (this->edgeDimensions * 2.0f);
+  
+  // Top Left.
+  QuadMesh::bufferQuadMesh(this->mesh,
+    glm::vec2(0, 0),
+    this->uv0,
+    edgeDimensions,
+    this->uv0 + oneThird,
+    0, 0
+  );
+
+  // Top Center
+  QuadMesh::bufferQuadMesh(this->mesh,
+    glm::vec2(edgeDimensions.x, 0),
+    this->uv0 + glm::vec2(oneThird.x, 0),
+    glm::vec2(edgeDimensions.x + innerDimensions.x, edgeDimensions.y),
+    this->uv0 + glm::vec2(oneThird.x * 2.0f, oneThird.y),
+    QUAD_VERTICE_COUNT, QUAD_INDICE_COUNT
+  );
+
+  // Top Right
+  QuadMesh::bufferQuadMesh(this->mesh,
+    glm::vec2(edgeDimensions.x + innerDimensions.x, 0),
+    this->uv0 + glm::vec2(oneThird.x * 2.0f, 0),
+    glm::vec2(overallDimensions.x, edgeDimensions.y),
+    glm::vec2(this->uv1.x, this->uv0.y + oneThird.y),
+    QUAD_VERTICE_COUNT * 2, QUAD_INDICE_COUNT * 2
+  );
+
+  // Middle Left
+  QuadMesh::bufferQuadMesh(this->mesh,
+    glm::vec2(0, edgeDimensions.y),
+    this->uv0 + glm::vec2(0, oneThird.y),
+    glm::vec2(edgeDimensions.x, edgeDimensions.y + innerDimensions.y),
+    this->uv0 + glm::vec2(oneThird.x, oneThird.y * 2.0f),
+    QUAD_VERTICE_COUNT * 3, QUAD_INDICE_COUNT * 3
+  );
+
+  // Center
+  QuadMesh::bufferQuadMesh(this->mesh,
+    edgeDimensions,
+    this->uv0 + oneThird,
+    edgeDimensions + innerDimensions,
+    this->uv0 + (oneThird * 2.0f),
+    QUAD_VERTICE_COUNT * 4, QUAD_INDICE_COUNT * 4
+  );
+
+  // Middle Right
+  QuadMesh::bufferQuadMesh(this->mesh,
+    edgeDimensions + glm::vec2(innerDimensions.x, 0),
+    this->uv0 + glm::vec2(oneThird.x * 2.0f, oneThird.y),
+    edgeDimensions + innerDimensions + glm::vec2(edgeDimensions.x, 0),
+    this->uv1 - glm::vec2(0.0f, oneThird.y),
+    QUAD_VERTICE_COUNT * 5, QUAD_INDICE_COUNT * 5
+  );
+
+  // Bottom Left
+  QuadMesh::bufferQuadMesh(this->mesh,
+    glm::vec2(0.0f, edgeDimensions.y + innerDimensions.y),
+    this->uv0 + glm::vec2(0.0f, uv1.y - oneThird.y),
+    glm::vec2(edgeDimensions.x, overallDimensions.y),
+    this->uv1 - glm::vec2(oneThird.x * 2.0f, 0.0f),
+    QUAD_VERTICE_COUNT * 6, QUAD_INDICE_COUNT * 6
+  );
+
+  // Bottom Center
+  QuadMesh::bufferQuadMesh(this->mesh,
+    edgeDimensions + glm::vec2(0.0f, innerDimensions.y),
+    this->uv1 - oneThird,
+    overallDimensions - glm::vec2(edgeDimensions.x, 0.0f),
+    this->uv1 - glm::vec2(oneThird.x, 0.0f),
+    QUAD_VERTICE_COUNT * 7, QUAD_INDICE_COUNT * 7
+  );
+
+  // Bottom Right
+  QuadMesh::bufferQuadMesh(this->mesh,
+    overallDimensions - edgeDimensions,
+    this->uv1 - oneThird,
+    overallDimensions,
+    this->uv1,
+    QUAD_VERTICE_COUNT * 8, QUAD_INDICE_COUNT * 8
+  );
+}
+
+void UIBorder::drawSelf(UIShader &shader, glm::mat4 transform) {
+  if(this->texture == nullptr) return;
+
+  shader.setUIColor(COLOR_WHITE);
+  shader.setUIModel(transform);
+  shader.setUITexture(this->texture);
+  this->mesh.draw(MESH_DRAW_MODE_TRIANGLES, 0, -1);
+}
+
+void UIBorder::setBorderSize(glm::vec2 borderSize) {
+  this->edgeDimensions = borderSize;
+  this->updatePositions();
+}
+
+glm::vec2 UIBorder::getBorderSize() {
+  return this->edgeDimensions;
+}
\ No newline at end of file
diff --git a/src/dawn/ui/UIBorder.hpp b/src/dawn/ui/UIBorder.hpp
new file mode 100644
index 00000000..0c32739f
--- /dev/null
+++ b/src/dawn/ui/UIBorder.hpp
@@ -0,0 +1,30 @@
+// Copyright (c) 2022 Dominic Masters
+// 
+// This software is released under the MIT License.
+// https://opensource.org/licenses/MIT
+
+#pragma once
+#include "UIComponent.hpp"
+#include "display/mesh/QuadMesh.hpp"
+#include "display/Texture.hpp"
+
+namespace Dawn {
+  class UIBorder : public UIComponent {
+    private:
+      Mesh mesh;
+      glm::vec2 edgeDimensions = glm::vec2(8.0f, 8.0f);
+      glm::vec2 uv0 = glm::vec2(0.0f, 0.0f);
+      glm::vec2 uv1 = glm::vec2(1.0f, 1.0f);
+
+      void updatePositions() override;
+
+    public:
+      Texture *texture;
+      
+      UIBorder(UICanvas &canvas);
+      void drawSelf(UIShader &shader, glm::mat4 selfTransform) override;
+
+      void setBorderSize(glm::vec2 borderSize);
+      glm::vec2 getBorderSize();
+  };
+}
\ No newline at end of file
diff --git a/src/dawn/ui/UIComponent.cpp b/src/dawn/ui/UIComponent.cpp
index d4b7d9d9..49258662 100644
--- a/src/dawn/ui/UIComponent.cpp
+++ b/src/dawn/ui/UIComponent.cpp
@@ -22,9 +22,26 @@ void UIComponent::updatePositions() {
     }
     this->relativeX = this->alignment[0];
     this->width -= (this->alignment[0] + this->alignment[2]);
-  } else {
+  } else if(this->alignX == UI_COMPONENT_ALIGN_START) {
     this->relativeX = this->alignment[0];
     this->width = this->alignment[2];
+  } else if(this->alignX == UI_COMPONENT_ALIGN_END) {
+    this->width = this->alignment[0];
+    if(this->parent == nullptr) {
+      this->relativeX = this->canvas.getWidth();
+    } else {
+      this->relativeX = this->parent->getWidth();
+    }
+    this->relativeX -= this->width;
+    this->relativeX -= this->alignment[2];
+  } else if(this->alignX == UI_COMPONENT_ALIGN_MIDDLE) {
+    this->width = this->alignment[2];
+    if(this->parent == nullptr) {
+      this->relativeX = this->canvas.getWidth();
+    } else {
+      this->relativeX = this->parent->getWidth();
+    }
+    this->relativeX = (this->relativeX / 2.0f) - (this->width / 2.0f) + this->alignment[0];
   }
 
   // Y Alignment
@@ -36,9 +53,26 @@ void UIComponent::updatePositions() {
     }
     this->relativeY = this->alignment[1];
     this->height -= (this->alignment[1] + this->alignment[3]);
-  } else {
+  } else if(this->alignY == UI_COMPONENT_ALIGN_START) {
     this->relativeY = this->alignment[1];
     this->height = this->alignment[3];
+  } else if(this->alignY == UI_COMPONENT_ALIGN_END) {
+    this->height = this->alignment[1];
+    if(this->parent == nullptr) {
+      this->relativeY = this->canvas.getHeight();
+    } else {
+      this->relativeY = this->parent->getHeight();
+    }
+    this->relativeY -= this->height;
+    this->relativeY -= this->alignment[3];
+  } else if(this->alignY == UI_COMPONENT_ALIGN_MIDDLE) {
+    this->height = this->alignment[3];
+    if(this->parent == nullptr) {
+      this->relativeY = this->canvas.getHeight();
+    } else {
+      this->relativeY = this->parent->getHeight();
+    }
+    this->relativeY = (this->relativeY / 2.0f) - (this->height / 2.0f) + this->alignment[1];
   }
 
   // Update children
@@ -95,18 +129,18 @@ void UIComponent::draw(UIShader &uiShader, glm::mat4 parentTransform) {
   }
 }
 
-void UIComponent::addChild(std::shared_ptr<UIComponent> child) {
+void UIComponent::addChild(UIComponent *child) {
   if(child->parent == this) return;
   if(child->parent != nullptr) child->parent->removeChild(child);
   this->children.push_back(child);
   child->parent = this;
 }
 
-void UIComponent::removeChild(std::shared_ptr<UIComponent> child) {
+void UIComponent::removeChild(UIComponent *child) {
   if(child->parent != this) throw "Invalid child";
   auto it = this->children.begin();
   while(it != this->children.end()) {
-    if(it->get() == child.get()) {
+    if(*it == child) {
       this->children.erase(it);
       break;
     }
diff --git a/src/dawn/ui/UIComponent.hpp b/src/dawn/ui/UIComponent.hpp
index 9b690280..ebf24761 100644
--- a/src/dawn/ui/UIComponent.hpp
+++ b/src/dawn/ui/UIComponent.hpp
@@ -28,9 +28,9 @@ namespace Dawn {
       // Setting values
       UIComponentAlign alignX = UI_COMPONENT_ALIGN_START;
       UIComponentAlign alignY = UI_COMPONENT_ALIGN_START;
-      glm::vec4 alignment = glm::vec4(0, 0, 1, 1);
+      glm::vec4 alignment = glm::vec4(0, 0, 32, 32);
       float_t z = 0;
-      std::vector<std::shared_ptr<UIComponent>> children;
+      std::vector<UIComponent*> children;
       UIComponent *parent = nullptr;
       
       // I currently don't support rotation or scale. Not because I can't but
@@ -51,6 +51,11 @@ namespace Dawn {
 
       UIComponent(UICanvas &canvas);
 
+      /**
+       * Returns the calculated width, based on the internal alignment values.
+       * 
+       * @return Width of the component.
+       */
       float_t getWidth();
       float_t getHeight();
       float_t getRelativeX();
@@ -67,9 +72,11 @@ namespace Dawn {
       void draw(UIShader &uiShader, glm::mat4 parentTransform);
       virtual void drawSelf(UIShader &uiShader, glm::mat4 selfTransform) = 0;
 
-      void addChild(std::shared_ptr<UIComponent> chidl);
-      void removeChild(std::shared_ptr<UIComponent> child);
+      void addChild(UIComponent *child);
+      void removeChild(UIComponent *child);
       
       virtual ~UIComponent();
+
+      friend class UICanvas;
   };
 }
\ No newline at end of file
diff --git a/src/dawn/visualnovel/CMakeLists.txt b/src/dawn/visualnovel/CMakeLists.txt
new file mode 100644
index 00000000..bdfddbb9
--- /dev/null
+++ b/src/dawn/visualnovel/CMakeLists.txt
@@ -0,0 +1,8 @@
+# Copyright (c) 2022 Dominic Masters
+# 
+# This software is released under the MIT License.
+# https://opensource.org/licenses/MIT
+
+
+# Subdirs
+add_subdirectory(ui)
\ No newline at end of file
diff --git a/src/dawn/visualnovel/ui/CMakeLists.txt b/src/dawn/visualnovel/ui/CMakeLists.txt
new file mode 100644
index 00000000..4df3e017
--- /dev/null
+++ b/src/dawn/visualnovel/ui/CMakeLists.txt
@@ -0,0 +1,10 @@
+# Copyright (c) 2022 Dominic Masters
+# 
+# This software is released under the MIT License.
+# https://opensource.org/licenses/MIT
+
+# Sources
+target_sources(${DAWN_TARGET_NAME}
+  PRIVATE
+    VisualNovelTextbox.cpp
+)
\ No newline at end of file
diff --git a/src/dawn/visualnovel/ui/VisualNovelTextbox.cpp b/src/dawn/visualnovel/ui/VisualNovelTextbox.cpp
new file mode 100644
index 00000000..441b3f9b
--- /dev/null
+++ b/src/dawn/visualnovel/ui/VisualNovelTextbox.cpp
@@ -0,0 +1,21 @@
+// Copyright (c) 2022 Dominic Masters
+// 
+// This software is released under the MIT License.
+// https://opensource.org/licenses/MIT
+
+#include "VisualNovelTextbox.hpp"
+
+using namespace Dawn;
+
+VisualNovelTextbox::VisualNovelTextbox(UICanvas &canvas) :
+  UIComponent(canvas),
+  border(canvas),
+  label(canvas)
+{
+  this->addChild(&this->border);
+  this->addChild(&this->label);
+}
+
+void VisualNovelTextbox::drawSelf(UIShader &shader, glm::mat4 self) {
+  
+}
\ No newline at end of file
diff --git a/src/dawn/visualnovel/ui/VisualNovelTextbox.hpp b/src/dawn/visualnovel/ui/VisualNovelTextbox.hpp
new file mode 100644
index 00000000..fa79ea3e
--- /dev/null
+++ b/src/dawn/visualnovel/ui/VisualNovelTextbox.hpp
@@ -0,0 +1,22 @@
+// Copyright (c) 2022 Dominic Masters
+// 
+// This software is released under the MIT License.
+// https://opensource.org/licenses/MIT
+
+#pragma once
+#include "ui/UISprite.hpp"
+#include "ui/UIBorder.hpp"
+#include "ui/UILabel.hpp"
+
+namespace Dawn {
+  class VisualNovelTextbox : public UIComponent {
+    private:
+      UIBorder border;
+      UILabel label;
+      glm::vec2 labelPadding = glm::vec2(0, 0);
+
+    public:
+      VisualNovelTextbox(UICanvas &canvas);
+      void drawSelf(UIShader &shader, glm::mat4 selfTransform) override;
+  };
+}
\ No newline at end of file
diff --git a/src/dawnglfw/host/DawnGLFWHost.cpp b/src/dawnglfw/host/DawnGLFWHost.cpp
index 34907798..6a31d811 100644
--- a/src/dawnglfw/host/DawnGLFWHost.cpp
+++ b/src/dawnglfw/host/DawnGLFWHost.cpp
@@ -50,16 +50,16 @@ int32_t DawnHost::init(DawnGame &game) {
   glfwSwapInterval(0);
   gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);
 
-  // Initialize the game
-  auto result = game.init();
-  if(result != DAWN_GAME_INIT_RESULT_SUCCESS) return result;
-
   // Override the defaults
   game.renderManager.backBuffer.setSize(
     DAWN_GLFW_WINDOW_WIDTH_DEFAULT,
     DAWN_GLFW_WINDOW_HEIGHT_DEFAULT
   );
 
+  // Initialize the game
+  auto result = game.init();
+  if(result != DAWN_GAME_INIT_RESULT_SUCCESS) return result;
+
   // Set up event listeners
   glfwSetWindowSizeCallback(this->data->window, &glfwOnResize);
   glfwSetKeyCallback(this->data->window, &glfwOnKey);
diff --git a/src/dawnpokergame/game/DawnPokerGame.cpp b/src/dawnpokergame/game/DawnPokerGame.cpp
index 7ec5da27..40e98874 100644
--- a/src/dawnpokergame/game/DawnPokerGame.cpp
+++ b/src/dawnpokergame/game/DawnPokerGame.cpp
@@ -5,13 +5,12 @@
 
 #include "DawnPokerGame.hpp"
 #include "asset/assets/TextureAsset.hpp"
+#include "ui/UIBorder.hpp"
 
 using namespace Dawn;
 
 std::shared_ptr<TrueTypeAsset> asset;
 std::shared_ptr<TextureAsset> assetTexture;
-std::shared_ptr<UILabel> label;
-float_t fs = 1.0f;
 
 DawnGame::DawnGame(DawnHost &host) :
   host(host),
@@ -38,6 +37,13 @@ int32_t DawnGame::init() {
     this->assetManager.update();
   }
 
+  auto border = canvas->addElement<UIBorder>();
+  border->texture = assetTexture->texture.get();
+  border->setTransform(
+    UI_COMPONENT_ALIGN_STRETCH, UI_COMPONENT_ALIGN_STRETCH,
+    glm::vec4(32, 32, 32, 32), 0.0f
+  );
+
   // auto sprite = canvas->addElement<UISprite>();
   // sprite->setTransform(
   //   UI_COMPONENT_ALIGN_START,
@@ -47,17 +53,6 @@ int32_t DawnGame::init() {
   // );
   // sprite->texture = &asset->font.getTexture();
 
-
-  label = canvas->addElement<UILabel>();
-  label->setTransform(
-    UI_COMPONENT_ALIGN_START,
-    UI_COMPONENT_ALIGN_START,
-    glm::vec4(0, 0, 200, 200),
-    0
-  );
-  label->setText("Hello World\nHow are you today?");
-  label->setFont(&asset->font);
-
   return DAWN_GAME_INIT_RESULT_SUCCESS;
 }
 
@@ -66,8 +61,6 @@ int32_t DawnGame::update(float_t delta) {
   this->inputManager.update();
 
   if(this->scene != nullptr) this->scene->update();
-
-  label->setFontSize(fs += delta);
   
   this->renderManager.update();