]> git.localhorst.tv Git - blank.git/commitdiff
chat state
authorDaniel Karbach <daniel.karbach@localhorst.tv>
Tue, 20 Oct 2015 14:45:26 +0000 (16:45 +0200)
committerDaniel Karbach <daniel.karbach@localhorst.tv>
Tue, 20 Oct 2015 14:45:26 +0000 (16:45 +0200)
standalone only atm

13 files changed:
src/app/ChatState.cpp [new file with mode: 0644]
src/app/ChatState.hpp [new file with mode: 0644]
src/app/State.hpp
src/app/app.cpp
src/graphics/Viewport.hpp
src/graphics/viewport.cpp
src/standalone/MasterState.cpp
src/standalone/MasterState.hpp
src/ui/HUD.hpp
src/ui/Text.hpp
src/ui/TextInput.hpp [new file with mode: 0644]
src/ui/ui.cpp
src/ui/widgets.cpp

diff --git a/src/app/ChatState.cpp b/src/app/ChatState.cpp
new file mode 100644 (file)
index 0000000..1eb35be
--- /dev/null
@@ -0,0 +1,107 @@
+#include "ChatState.hpp"
+
+#include "Environment.hpp"
+#include "../io/event.hpp"
+
+#include <iostream>
+
+
+namespace blank {
+
+ChatState::ChatState(Environment &env, State &parent, Responder &responder)
+: env(env)
+, parent(parent)
+, responder(responder)
+, input(env.assets.small_ui_font) {
+       input.Position(glm::vec3(25.0f, -25.0f, -1.0f), Gravity::SOUTH_WEST, Gravity::SOUTH_WEST);
+       input.Width(env.viewport.Width() - 50.0f);
+       input.Foreground(glm::vec4(1.0f));
+       input.Background(glm::vec4(0.5f));
+}
+
+void ChatState::OnResume() {
+       OnResize(env.viewport);
+       input.Clear();
+       input.Focus(env.viewport);
+}
+
+void ChatState::OnPause() {
+       input.Blur();
+}
+
+void ChatState::OnResize(Viewport &viewport) {
+       input.Width(viewport.Width() - 50.0f);
+}
+
+
+void ChatState::Handle(const SDL_Event &e) {
+       switch (e.type) {
+               case SDL_KEYDOWN:
+                       switch (e.key.keysym.sym) {
+                               case SDLK_ESCAPE:
+                                       Quit();
+                                       break;
+                               case SDLK_KP_ENTER:
+                               case SDLK_RETURN:
+                                       responder.OnLineSubmit(input.GetInput());
+                                       Quit();
+                                       break;
+
+                               case SDLK_BACKSPACE:
+                                       input.Backspace();
+                                       break;
+                               case SDLK_DELETE:
+                                       input.Delete();
+                                       break;
+
+                               case SDLK_LEFT:
+                                       input.MoveBackward();
+                                       break;
+                               case SDLK_RIGHT:
+                                       input.MoveForward();
+                                       break;
+
+                               case SDLK_HOME:
+                                       input.MoveBegin();
+                                       break;
+                               case SDLK_END:
+                                       input.MoveEnd();
+                                       break;
+
+                               default:
+                                       break;
+                       }
+                       break;
+
+               case SDL_QUIT:
+                       env.state.PopAll();
+                       break;
+
+               case SDL_TEXTINPUT:
+                       input.Handle(e.text);
+                       break;
+
+               case SDL_TEXTEDITING:
+                       std::cout << e << std::endl;
+                       input.Handle(e.edit);
+                       break;
+
+               default:
+                       break;
+       }
+}
+
+void ChatState::Quit() {
+       env.state.PopUntil(this);
+}
+
+void ChatState::Update(int dt) {
+       parent.Update(dt);
+}
+
+void ChatState::Render(Viewport &viewport) {
+       parent.Render(viewport);
+       input.Render(viewport);
+}
+
+}
diff --git a/src/app/ChatState.hpp b/src/app/ChatState.hpp
new file mode 100644 (file)
index 0000000..56f8538
--- /dev/null
@@ -0,0 +1,48 @@
+#ifndef BLANK_APP_CHATSTATE_HPP_
+#define BLANK_APP_CHATSTATE_HPP_
+
+#include "State.hpp"
+
+#include "../ui/TextInput.hpp"
+
+#include <string>
+
+
+namespace blank {
+
+class Environment;
+
+class ChatState
+: public State {
+
+public:
+       struct Responder {
+               virtual void OnLineSubmit(const std::string &) = 0;
+       };
+
+public:
+       ChatState(Environment &env, State &parent, Responder &);
+
+       void OnResume() override;
+       void OnPause() override;
+
+       void OnResize(Viewport &) override;
+
+       void Handle(const SDL_Event &) override;
+       void Update(int dt) override;
+       void Render(Viewport &) override;
+
+       void Quit();
+
+private:
+       Environment &env;
+       State &parent;
+       Responder &responder;
+
+       TextInput input;
+
+};
+
+}
+
+#endif
index f79bc60f03d7c96e6c0668b73ad62688fc0c44c2..b6c20f662dc12804ab5b6dd410292ca296ae2691 100644 (file)
@@ -11,6 +11,7 @@ class Viewport;
 
 struct State {
 
+       friend class Application;
        friend class HeadlessApplication;
 
        virtual void Handle(const SDL_Event &) = 0;
@@ -28,6 +29,10 @@ private:
        virtual void OnPause() { }
        virtual void OnExit() { }
 
+       virtual void OnFocus() { }
+       virtual void OnBlur() { }
+       virtual void OnResize(Viewport &) { }
+
 };
 
 };
index b3b330036b5e2f0c855266c0219dee023a2eebee..aba7093186853ba04a29fa64d1402506d1894cb3 100644 (file)
@@ -161,13 +161,14 @@ void Application::Handle(const SDL_Event &event) {
 void Application::Handle(const SDL_WindowEvent &event) {
        switch (event.event) {
                case SDL_WINDOWEVENT_FOCUS_GAINED:
-                       env.window.GrabMouse();
+                       GetState().OnFocus();
                        break;
                case SDL_WINDOWEVENT_FOCUS_LOST:
-                       env.window.ReleaseMouse();
+                       GetState().OnBlur();
                        break;
                case SDL_WINDOWEVENT_RESIZED:
                        env.viewport.Resize(event.data1, event.data2);
+                       GetState().OnResize(env.viewport);
                        break;
                default:
                        break;
index a8cb69f0947f70cee14b6fc20df6f544b5114b1b..96fbaa4deae54840efb53a1bb7fa2474a17d1b21 100644 (file)
@@ -44,9 +44,11 @@ public:
        void Clear() noexcept;
        void ClearDepth() noexcept;
 
-       void SetCursor(const glm::vec3 &);
-       void SetCursor(const glm::vec3 &, Gravity);
-       void MoveCursor(const glm::vec3 &);
+       glm::vec2 GetPosition(const glm::vec2 &off, Gravity grav) const noexcept;
+
+       void SetCursor(const glm::vec3 &) noexcept;
+       void SetCursor(const glm::vec3 &, Gravity) noexcept;
+       void MoveCursor(const glm::vec3 &) noexcept;
        const glm::mat4 &Cursor() const noexcept { return cursor; }
 
        BlockLighting &ChunkProgram() noexcept;
index f9558d036865de4e3b388e5e22a96b1d1a8536d8..9abe48f248261f81e8999a82eff70bc4a87a0010 100644 (file)
@@ -165,20 +165,24 @@ void Viewport::ClearDepth() noexcept {
 }
 
 
-void Viewport::SetCursor(const glm::vec3 &pos) {
+glm::vec2 Viewport::GetPosition(const glm::vec2 &off, Gravity grav) const noexcept {
+       return align(grav, canv.Size(), off + canv.Offset());
+}
+
+void Viewport::SetCursor(const glm::vec3 &pos) noexcept {
        cursor[3].x = pos.x;
        cursor[3].y = pos.y;
        cursor[3].z = pos.z;
 }
 
-void Viewport::SetCursor(const glm::vec3 &pos, Gravity grav) {
-       glm::vec2 p(align(grav, canv.Size(), glm::vec2(pos) + canv.Offset()));
+void Viewport::SetCursor(const glm::vec3 &pos, Gravity grav) noexcept {
+       glm::vec2 p(GetPosition(glm::vec2(pos), grav));
        cursor[3].x = p.x;
        cursor[3].y = p.y;
        cursor[3].z = pos.z;
 }
 
-void Viewport::MoveCursor(const glm::vec3 &d) {
+void Viewport::MoveCursor(const glm::vec3 &d) noexcept {
        cursor[3].x += d.x;
        cursor[3].y += d.y;
        cursor[3].z += d.z;
index 750e491141135ca52a14b534aab51ebbd4fdd46e..91ee4826682fbdaca890b76d3c1c8bb0c3ab4929 100644 (file)
@@ -37,7 +37,8 @@ MasterState::MasterState(
 , spawner(world, res.models, env.rng)
 , sky(env.loader.LoadCubeMap("skybox"))
 , preload(env, chunk_loader, chunk_renderer)
-, unload(env, world.Chunks(), save) {
+, unload(env, world.Chunks(), save)
+, chat(env, *this, *this) {
        res.Load(env.loader, "default");
        if (res.models.size() < 2) {
                throw std::runtime_error("need at least two models to run");
@@ -74,6 +75,7 @@ void MasterState::OnResume() {
                // TODO: spawn
                spawn_player = false;
        }
+       hud.KeepMessages(false);
 }
 
 void MasterState::OnPause() {
@@ -84,7 +86,13 @@ void MasterState::OnPause() {
 void MasterState::Handle(const SDL_Event &event) {
        switch (event.type) {
                case SDL_KEYDOWN:
-                       interface.HandlePress(event.key);
+                       // TODO: move to interface?
+                       if (event.key.keysym.sym == SDLK_RETURN) {
+                               env.state.Push(&chat);
+                               hud.KeepMessages(true);
+                       } else {
+                               interface.HandlePress(event.key);
+                       }
                        break;
                case SDL_KEYUP:
                        interface.HandleRelease(event.key);
@@ -187,5 +195,9 @@ void MasterState::Exit() {
        env.state.Switch(&unload);
 }
 
+void MasterState::OnLineSubmit(const std::string &line) {
+       hud.PostMessage(line);
+}
+
 }
 }
index 97834e6371db843f003fe5dbee5326a45452366c..173c07cdd1e8208dd5939efb9b5842311a449650 100644 (file)
@@ -7,6 +7,7 @@
 #include "PreloadState.hpp"
 #include "UnloadState.hpp"
 #include "../ai/Spawner.hpp"
+#include "../app/ChatState.hpp"
 #include "../audio/SoundBank.hpp"
 #include "../graphics/SkyBox.hpp"
 #include "../shared/WorldResources.hpp"
@@ -31,7 +32,8 @@ namespace standalone {
 
 class MasterState
 : public State
-, public ClientController {
+, public ClientController
+, public ChatState::Responder {
 
 public:
        MasterState(
@@ -59,6 +61,8 @@ public:
        void SetDebug(bool) override;
        void Exit() override;
 
+       void OnLineSubmit(const std::string &) override;
+
 private:
        Config &config;
        Environment &env;
@@ -82,6 +86,7 @@ private:
 
        PreloadState preload;
        UnloadState unload;
+       ChatState chat;
 
 };
 
index cab8f93e0bf13c26cccb69a6a3a4d48b6ac28fa9..7db025865c329c92cddde9916ee889fd85857832 100644 (file)
@@ -47,6 +47,8 @@ public:
        void PostMessage(const std::string &msg) {
                PostMessage(msg.c_str());
        }
+       // whether to always render message box regardless of last post
+       void KeepMessages(bool k) { msg_keep = k; }
 
        void Update(int dt);
        void Render(Viewport &) noexcept;
@@ -80,6 +82,7 @@ private:
        // message box
        MessageBox messages;
        IntervalTimer msg_timer;
+       bool msg_keep;
 
        // crosshair
        PrimitiveMesh crosshair;
index 84a6190999f89f4d2d340dd6ba4b64d1e1eb70b3..c4318737e1246de4036330ecf639f95fa5729621 100644 (file)
@@ -24,7 +24,8 @@ public:
                Set(f, s.c_str());
        }
 
-       void Pivot(Gravity p) {
+       Gravity Pivot() const noexcept { return pivot; }
+       void Pivot(Gravity p) noexcept {
                pivot = p;
                dirty = true;
        }
diff --git a/src/ui/TextInput.hpp b/src/ui/TextInput.hpp
new file mode 100644 (file)
index 0000000..44999fa
--- /dev/null
@@ -0,0 +1,78 @@
+#ifndef BLANK_UI_TEXTINPUT_HPP_
+#define BLANK_UI_TEXTINPUT_HPP_
+
+#include "Text.hpp"
+#include "../graphics/PrimitiveMesh.hpp"
+
+#include <string>
+#include <SDL.h>
+
+
+namespace blank {
+
+class Viewport;
+
+class TextInput {
+
+public:
+       explicit TextInput(const Font &);
+
+       const std::string &GetInput() const noexcept { return input; }
+
+       void Focus(Viewport &) noexcept;
+       void Blur() noexcept;
+
+       void Clear() noexcept;
+       void Backspace() noexcept;
+       void Delete() noexcept;
+
+       void MoveBegin() noexcept;
+       void MoveBackward() noexcept;
+       void MoveForward() noexcept;
+       void MoveEnd() noexcept;
+
+       void Insert(const char *);
+
+       bool AtBegin() const noexcept;
+       bool AtEnd() const noexcept;
+
+       void Position(const glm::vec3 &p, Gravity g, Gravity pv) noexcept;
+       void Width(float) noexcept;
+
+       void Foreground(const glm::vec4 &col) noexcept { fg = col; dirty_cursor = true; }
+       void Background(const glm::vec4 &col) noexcept { bg = col; dirty_box = true; }
+
+       void Handle(const SDL_TextInputEvent &);
+       void Handle(const SDL_TextEditingEvent &);
+
+       void Render(Viewport &);
+
+private:
+       void Refresh();
+
+private:
+       const Font &font;
+       std::string input;
+       std::string::size_type cursor;
+       Text text;
+
+       PrimitiveMesh bg_mesh;
+       PrimitiveMesh cursor_mesh;
+
+       glm::vec4 bg;
+       glm::vec4 fg;
+
+       glm::vec3 position;
+       glm::vec2 size;
+       Gravity gravity;
+
+       bool active;
+       bool dirty_box;
+       bool dirty_cursor;
+       bool dirty_text;
+
+};
+
+}
+
+#endif
index e7e6b0c7725a36264222a08633119d4f443ba6d2..0f82139b2a7bd5350db9965e51d89ea1ea3c0843 100644 (file)
@@ -212,8 +212,11 @@ HUD::HUD(Environment &env, Config &config, const Player &player)
 // message box
 , messages(env.assets.small_ui_font)
 , msg_timer(5000)
+, msg_keep(false)
 // crosshair
 , crosshair() {
+       const float ls = env.assets.small_ui_font.LineSkip();
+
        // "inventory"
        block_transform = glm::translate(block_transform, glm::vec3(50.0f, 50.0f, 0.0f));
        block_transform = glm::scale(block_transform, glm::vec3(50.0f));
@@ -231,23 +234,23 @@ HUD::HUD(Environment &env, Config &config, const Player &player)
        counter_text.Position(glm::vec3(-25.0f, 25.0f, 0.0f), Gravity::NORTH_EAST);
        counter_text.Foreground(glm::vec4(1.0f));
        counter_text.Background(glm::vec4(0.5f));
-       position_text.Position(glm::vec3(-25.0f, 25.0f + env.assets.small_ui_font.LineSkip(), 0.0f), Gravity::NORTH_EAST);
+       position_text.Position(glm::vec3(-25.0f, 25.0f + ls, 0.0f), Gravity::NORTH_EAST);
        position_text.Foreground(glm::vec4(1.0f));
        position_text.Background(glm::vec4(0.5f));
-       orientation_text.Position(glm::vec3(-25.0f, 25.0f + 2 * env.assets.small_ui_font.LineSkip(), 0.0f), Gravity::NORTH_EAST);
+       orientation_text.Position(glm::vec3(-25.0f, 25.0f + 2 * ls, 0.0f), Gravity::NORTH_EAST);
        orientation_text.Foreground(glm::vec4(1.0f));
        orientation_text.Background(glm::vec4(0.5f));
-       block_text.Position(glm::vec3(-25.0f, 25.0f + 4 * env.assets.small_ui_font.LineSkip(), 0.0f), Gravity::NORTH_EAST);
+       block_text.Position(glm::vec3(-25.0f, 25.0f + 4 * ls, 0.0f), Gravity::NORTH_EAST);
        block_text.Foreground(glm::vec4(1.0f));
        block_text.Background(glm::vec4(0.5f));
        block_text.Set(env.assets.small_ui_font, "Block: none");
-       entity_text.Position(glm::vec3(-25.0f, 25.0f + 4 * env.assets.small_ui_font.LineSkip(), 0.0f), Gravity::NORTH_EAST);
+       entity_text.Position(glm::vec3(-25.0f, 25.0f + 4 * ls, 0.0f), Gravity::NORTH_EAST);
        entity_text.Foreground(glm::vec4(1.0f));
        entity_text.Background(glm::vec4(0.5f));
        entity_text.Set(env.assets.small_ui_font, "Entity: none");
 
        // message box
-       messages.Position(glm::vec3(25.0f, -25.0f, 0.0f), Gravity::SOUTH_WEST);
+       messages.Position(glm::vec3(25.0f, -25.0f - 2 * ls, 0.0f), Gravity::SOUTH_WEST);
        messages.Foreground(glm::vec4(1.0f));
        messages.Background(glm::vec4(0.5f));
 
@@ -400,7 +403,7 @@ void HUD::Render(Viewport &viewport) noexcept {
                }
 
                // message box
-               if (msg_timer.Running()) {
+               if (msg_keep || msg_timer.Running()) {
                        messages.Render(viewport);
                }
 
index 533703fd84da47dea5e8d12f5c3df12d5c0e450f..d05ea2fc3584a762d0f70e5cdc9ea056ac56ea16 100644 (file)
@@ -2,11 +2,16 @@
 #include "MessageBox.hpp"
 #include "Progress.hpp"
 #include "Text.hpp"
+#include "TextInput.hpp"
 
 #include "../graphics/Font.hpp"
 #include "../graphics/Viewport.hpp"
 
 #include <cstdio>
+#include <cstring>
+#include <limits>
+
+using namespace std;
 
 
 namespace blank {
@@ -59,7 +64,7 @@ PrimitiveMesh::Buffer bg_buf;
 void MessageBox::Recalc() {
        size = glm::vec2(0.0f, 0.0f);
        for (const Text &line : lines) {
-               size.x = std::max(size.x, line.Size().x);
+               size.x = max(size.x, line.Size().x);
                size.y += line.Size().y;
        }
        bg_buf.FillRect(size.x, size.y, bg, align(grav, size));
@@ -70,7 +75,7 @@ void MessageBox::Recalc() {
 
 void MessageBox::Render(Viewport &viewport) noexcept {
        viewport.SetCursor(pos, grav);
-       if (bg.a > std::numeric_limits<float>::epsilon()) {
+       if (bg.a > numeric_limits<float>::epsilon()) {
                if (dirty) {
                        Recalc();
                }
@@ -104,7 +109,7 @@ char buf[128] = { '\0' };
 }
 
 void Progress::Update(int current, int total) {
-       std::snprintf(buf, sizeof(buf), tpl, current, total, current * 100 / total);
+       snprintf(buf, sizeof(buf), tpl, current, total, current * 100 / total);
        text.Set(font, buf);
 }
 
@@ -168,4 +173,181 @@ void Text::Render(Viewport &viewport) noexcept {
        sprite.Draw();
 }
 
+
+TextInput::TextInput(const Font &font)
+: font(font)
+, input()
+, cursor(0)
+, text()
+, bg_mesh()
+, cursor_mesh()
+, bg(1.0f, 1.0f, 1.0f, 0.0f)
+, fg(1.0f, 1.0f, 1.0f, 1.0f)
+, position(0.0f)
+, size(font.LineSkip())
+, gravity(Gravity::NORTH_WEST)
+, active(false)
+, dirty_box(true)
+, dirty_cursor(true)
+, dirty_text(true) {
+
+}
+
+void TextInput::Focus(Viewport &viewport) noexcept {
+       SDL_StartTextInput();
+       active = true;
+
+       glm::vec2 p(viewport.GetPosition(glm::vec2(position), gravity));
+       SDL_Rect rect;
+       rect.x = p.x;
+       rect.y = p.y;
+       rect.w = size.x;
+       rect.h = size.y;
+       SDL_SetTextInputRect(&rect);
+}
+
+void TextInput::Blur() noexcept {
+       SDL_StopTextInput();
+       active = false;
+}
+
+void TextInput::Clear() noexcept {
+       input.clear();
+       cursor = 0;
+       dirty_text = true;
+}
+
+void TextInput::Backspace() noexcept {
+       string::size_type previous(cursor);
+       MoveBackward();
+       input.erase(cursor, previous - cursor);
+       dirty_text = true;
+}
+
+void TextInput::Delete() noexcept {
+       string::size_type previous(cursor);
+       MoveForward();
+       input.erase(previous, cursor - previous);
+       cursor = previous;
+       dirty_text = true;
+}
+
+void TextInput::MoveBegin() noexcept {
+       cursor = 0;
+}
+
+void TextInput::MoveBackward() noexcept {
+       if (AtBegin()) return;
+       --cursor;
+       while (cursor > 0 && (input[cursor] & 0xC0) == 0x80) {
+               --cursor;
+       }
+}
+
+void TextInput::MoveForward() noexcept {
+       if (AtEnd()) return;
+       ++cursor;
+       while (cursor <= input.size() && (input[cursor] & 0xC0) == 0x80) {
+               ++cursor;
+       }
+}
+
+void TextInput::MoveEnd() noexcept {
+       cursor = input.size();
+}
+
+void TextInput::Insert(const char *str) {
+       size_t len = strlen(str);
+       input.insert(cursor, str, len);
+       cursor += len;
+       dirty_text = true;
+}
+
+bool TextInput::AtBegin() const noexcept {
+       return cursor == 0;
+}
+
+bool TextInput::AtEnd() const noexcept {
+       return cursor == input.size();
+}
+
+void TextInput::Position(const glm::vec3 &p, Gravity g, Gravity pv) noexcept {
+       position = p;
+       gravity = g;
+       text.Pivot(pv);
+       dirty_box = true;
+}
+
+void TextInput::Width(float w) noexcept {
+       size.x = w;
+       dirty_box = true;
+}
+
+void TextInput::Handle(const SDL_TextInputEvent &e) {
+       Insert(e.text);
+}
+
+void TextInput::Handle(const SDL_TextEditingEvent &e) {
+}
+
+void TextInput::Refresh() {
+       if (dirty_box) {
+               bg_buf.FillRect(size.x, size.y, bg, align(gravity, size));
+               bg_mesh.Update(bg_buf);
+               bg_buf.Clear();
+               dirty_box = false;
+       }
+       if (dirty_cursor) {
+               bg_buf.Reserve(2, 2);
+               bg_buf.vertices.emplace_back(0.0f, 0.0f, 0.0f);
+               bg_buf.vertices.emplace_back(0.0f, float(font.LineSkip()), 0.0f);
+               bg_buf.colors.resize(2, fg);
+               bg_buf.indices.push_back(0);
+               bg_buf.indices.push_back(1);
+               cursor_mesh.Update(bg_buf);
+               bg_buf.Clear();
+               dirty_cursor = false;
+       }
+       if (dirty_text) {
+               if (!input.empty()) {
+                       text.Set(font, input.c_str());
+               }
+               dirty_text = false;
+       }
+}
+
+void TextInput::Render(Viewport &viewport) {
+       Refresh();
+       viewport.SetCursor(position, gravity);
+       if (bg.a > numeric_limits<float>::epsilon()) {
+               viewport.EnableAlphaBlending();
+               PlainColor &prog = viewport.HUDColorProgram();
+               prog.SetM(viewport.Cursor());
+               bg_mesh.DrawTriangles();
+               viewport.MoveCursor(glm::vec3(0.0f, 0.0f, -1.0f));
+       }
+       if (!input.empty()) {
+               BlendedSprite &prog = viewport.SpriteProgram();
+               prog.SetBG(glm::vec4(0.0f));
+               prog.SetFG(fg);
+               prog.SetM(viewport.Cursor());
+               text.Render(viewport);
+       }
+       if (active) {
+               glm::vec2 offset(0.0f);
+               if (input.empty() || AtBegin()) {
+                       // a okay
+                       offset = -align(text.Pivot(), glm::vec2(0.0f, font.LineSkip()));
+               } else if (AtEnd()) {
+                       offset = -align(text.Pivot(), text.Size(), glm::vec2(-text.Size().x, 0.0f));
+               } else {
+                       offset = -align(text.Pivot(), text.Size(), glm::vec2(-font.TextSize(input.substr(0, cursor).c_str()).x, 0.0f));
+               }
+               viewport.MoveCursor(glm::vec3(offset, -1.0f));
+               PlainColor &prog = viewport.HUDColorProgram();
+               prog.SetM(viewport.Cursor());
+               cursor_mesh.DrawLines();
+       }
+}
+
 }