X-Git-Url: http://git.localhorst.tv/?a=blobdiff_plain;f=src%2Fui%2Fwidgets.cpp;h=08147f6f22ac173e7c7ac51b28ef0b87d3429b6b;hb=HEAD;hp=533703fd84da47dea5e8d12f5c3df12d5c0e450f;hpb=b49cc8c88caf7d69b35b50e23a40528e71306ade;p=blank.git diff --git a/src/ui/widgets.cpp b/src/ui/widgets.cpp index 533703f..08147f6 100644 --- a/src/ui/widgets.cpp +++ b/src/ui/widgets.cpp @@ -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 +#include +#include + +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::epsilon()) { + if (bg.a > numeric_limits::epsilon()) { if (dirty) { Recalc(); } @@ -81,7 +86,7 @@ void MessageBox::Render(Viewport &viewport) noexcept { } BlendedSprite &prog = viewport.SpriteProgram(); prog.SetBG(glm::vec4(0.0f)); - prog.SetFG(fg); + prog.SetFG(glm::vec4(fg) * (1.0f / 255.0f)); for (Text &txt : lines) { prog.SetM(viewport.Cursor()); txt.Render(viewport); @@ -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,182 @@ 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 &) { + +} + +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::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(glm::vec4(fg) * (1.0f / 255.0f)); + 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(); + } +} + }