]> git.localhorst.tv Git - gong.git/blob - src/ui/widgets.cpp
code, assets, and other stuff stolen from blank
[gong.git] / src / ui / widgets.cpp
1 #include "FixedText.hpp"
2 #include "MessageBox.hpp"
3 #include "Text.hpp"
4 #include "TextInput.hpp"
5
6 #include "../graphics/Font.hpp"
7 #include "../graphics/Viewport.hpp"
8
9 #include <cstdio>
10 #include <cstring>
11 #include <limits>
12
13 using namespace std;
14
15
16 namespace gong {
17 namespace ui {
18
19 MessageBox::MessageBox(const graphics::Font &f)
20 : font(f)
21 , lines()
22 , max_lines(10)
23 , pos(0.0f)
24 , adv(0.0f, font.LineSkip(), 0.0f)
25 , bg(1.0f, 1.0f, 1.0f, 0.0f)
26 , fg(1.0f, 1.0f, 1.0f, 1.0f)
27 , grav(graphics::Gravity::NORTH_WEST)
28 , dirty(true) {
29
30 }
31
32 void MessageBox::Position(const glm::vec3 &p, graphics::Gravity g) noexcept {
33         pos = p;
34         grav = g;
35         if (get_y(g) == graphics::Align::END) {
36                 adv.y = -font.LineSkip();
37         } else {
38                 adv.y = font.LineSkip();
39         }
40         for (Text &txt : lines) {
41                 txt.Pivot(g);
42         }
43         dirty = true;
44 }
45
46 void MessageBox::PushLine(const char *text) {
47         lines.emplace_front();
48         Text &txt = lines.front();
49         txt.Set(font, text);
50         txt.Pivot(grav);
51
52         while (lines.size() > max_lines) {
53                 lines.pop_back();
54         }
55         dirty = true;
56 }
57
58 namespace {
59
60 graphics::PrimitiveMesh::Buffer bg_buf;
61
62 }
63
64 void MessageBox::Recalc() {
65         size = glm::vec2(0.0f, 0.0f);
66         for (const Text &line : lines) {
67                 size.x = max(size.x, line.Size().x);
68                 size.y += line.Size().y;
69         }
70         bg_buf.FillRect(size.x, size.y, bg, align(grav, size));
71         bg_mesh.Update(bg_buf);
72         bg_buf.Clear();
73         dirty = false;
74 }
75
76 void MessageBox::Render(graphics::Viewport &viewport) noexcept {
77         viewport.SetCursor(pos, grav);
78         if (bg.a > numeric_limits<float>::epsilon()) {
79                 if (dirty) {
80                         Recalc();
81                 }
82                 graphics::PlainColor &prog = viewport.HUDColorProgram();
83                 prog.SetM(viewport.Cursor());
84                 bg_mesh.DrawTriangles();
85                 viewport.MoveCursor(glm::vec3(0.0f, 0.0f, -1.0f));
86         }
87         graphics::BlendedSprite &prog = viewport.SpriteProgram();
88         prog.SetBG(glm::vec4(0.0f));
89         prog.SetFG(glm::vec4(fg) * (1.0f / 255.0f));
90         for (Text &txt : lines) {
91                 prog.SetM(viewport.Cursor());
92                 txt.Render(viewport);
93                 viewport.MoveCursor(adv);
94         }
95 }
96
97
98 Text::Text() noexcept
99 : tex()
100 , sprite()
101 , size(0.0f)
102 , pivot(graphics::Gravity::NORTH_WEST)
103 , dirty(false) {
104
105 }
106
107 FixedText::FixedText() noexcept
108 : Text()
109 , bg(1.0f, 1.0f, 1.0f, 0.0f)
110 , fg(1.0f, 1.0f, 1.0f, 1.0f)
111 , pos(0.0f)
112 , grav(graphics::Gravity::NORTH_WEST)
113 , visible(false) {
114
115 }
116
117 void Text::Set(const graphics::Font &font, const char *text) {
118         font.Render(text, tex);
119         size = font.TextSize(text);
120         dirty = true;
121 }
122
123 namespace {
124
125 graphics::SpriteMesh::Buffer sprite_buf;
126
127 }
128
129 void Text::Update() {
130         sprite_buf.LoadRect(size.x, size.y, align(pivot, size));
131         sprite.Update(sprite_buf);
132         dirty = false;
133 }
134
135 void FixedText::Render(graphics::Viewport &viewport) noexcept {
136         graphics::BlendedSprite &prog = viewport.SpriteProgram();
137         viewport.SetCursor(pos, grav);
138         prog.SetM(viewport.Cursor());
139         prog.SetBG(bg);
140         prog.SetFG(fg);
141         Text::Render(viewport);
142 }
143
144 void Text::Render(graphics::Viewport &viewport) noexcept {
145         if (dirty) {
146                 Update();
147         }
148         graphics::BlendedSprite &prog = viewport.SpriteProgram();
149         prog.SetTexture(tex);
150         sprite.Draw();
151 }
152
153
154 TextInput::TextInput(const graphics::Font &font)
155 : font(font)
156 , input()
157 , cursor(0)
158 , text()
159 , bg_mesh()
160 , cursor_mesh()
161 , bg(1.0f, 1.0f, 1.0f, 0.0f)
162 , fg(1.0f, 1.0f, 1.0f, 1.0f)
163 , position(0.0f)
164 , size(font.LineSkip())
165 , gravity(graphics::Gravity::NORTH_WEST)
166 , active(false)
167 , dirty_box(true)
168 , dirty_cursor(true)
169 , dirty_text(true) {
170
171 }
172
173 void TextInput::Focus(graphics::Viewport &viewport) noexcept {
174         SDL_StartTextInput();
175         active = true;
176
177         glm::vec2 p(viewport.GetPosition(glm::vec2(position), gravity));
178         SDL_Rect rect;
179         rect.x = p.x;
180         rect.y = p.y;
181         rect.w = size.x;
182         rect.h = size.y;
183         SDL_SetTextInputRect(&rect);
184 }
185
186 void TextInput::Blur() noexcept {
187         SDL_StopTextInput();
188         active = false;
189 }
190
191 void TextInput::Clear() noexcept {
192         input.clear();
193         cursor = 0;
194         dirty_text = true;
195 }
196
197 void TextInput::Backspace() noexcept {
198         string::size_type previous(cursor);
199         MoveBackward();
200         input.erase(cursor, previous - cursor);
201         dirty_text = true;
202 }
203
204 void TextInput::Delete() noexcept {
205         string::size_type previous(cursor);
206         MoveForward();
207         input.erase(previous, cursor - previous);
208         cursor = previous;
209         dirty_text = true;
210 }
211
212 void TextInput::MoveBegin() noexcept {
213         cursor = 0;
214 }
215
216 void TextInput::MoveBackward() noexcept {
217         if (AtBegin()) return;
218         --cursor;
219         while (cursor > 0 && (input[cursor] & 0xC0) == 0x80) {
220                 --cursor;
221         }
222 }
223
224 void TextInput::MoveForward() noexcept {
225         if (AtEnd()) return;
226         ++cursor;
227         while (cursor <= input.size() && (input[cursor] & 0xC0) == 0x80) {
228                 ++cursor;
229         }
230 }
231
232 void TextInput::MoveEnd() noexcept {
233         cursor = input.size();
234 }
235
236 void TextInput::Insert(const char *str) {
237         size_t len = strlen(str);
238         input.insert(cursor, str, len);
239         cursor += len;
240         dirty_text = true;
241 }
242
243 bool TextInput::AtBegin() const noexcept {
244         return cursor == 0;
245 }
246
247 bool TextInput::AtEnd() const noexcept {
248         return cursor == input.size();
249 }
250
251 void TextInput::Position(const glm::vec3 &p, graphics::Gravity g, graphics::Gravity pv) noexcept {
252         position = p;
253         gravity = g;
254         text.Pivot(pv);
255         dirty_box = true;
256 }
257
258 void TextInput::Width(float w) noexcept {
259         size.x = w;
260         dirty_box = true;
261 }
262
263 void TextInput::Handle(const SDL_TextInputEvent &e) {
264         Insert(e.text);
265 }
266
267 void TextInput::Handle(const SDL_TextEditingEvent &) {
268
269 }
270
271 void TextInput::Refresh() {
272         if (dirty_box) {
273                 bg_buf.FillRect(size.x, size.y, bg, align(gravity, size));
274                 bg_mesh.Update(bg_buf);
275                 bg_buf.Clear();
276                 dirty_box = false;
277         }
278         if (dirty_cursor) {
279                 bg_buf.Reserve(2, 2);
280                 bg_buf.vertices.emplace_back(0.0f, 0.0f, 0.0f);
281                 bg_buf.vertices.emplace_back(0.0f, float(font.LineSkip()), 0.0f);
282                 bg_buf.colors.resize(2, fg);
283                 bg_buf.indices.push_back(0);
284                 bg_buf.indices.push_back(1);
285                 cursor_mesh.Update(bg_buf);
286                 bg_buf.Clear();
287                 dirty_cursor = false;
288         }
289         if (dirty_text) {
290                 if (!input.empty()) {
291                         text.Set(font, input.c_str());
292                 }
293                 dirty_text = false;
294         }
295 }
296
297 void TextInput::Render(graphics::Viewport &viewport) {
298         Refresh();
299         viewport.SetCursor(position, gravity);
300         if (bg.a > numeric_limits<float>::epsilon()) {
301                 viewport.EnableAlphaBlending();
302                 graphics::PlainColor &prog = viewport.HUDColorProgram();
303                 prog.SetM(viewport.Cursor());
304                 bg_mesh.DrawTriangles();
305                 viewport.MoveCursor(glm::vec3(0.0f, 0.0f, -1.0f));
306         }
307         if (!input.empty()) {
308                 graphics::BlendedSprite &prog = viewport.SpriteProgram();
309                 prog.SetBG(glm::vec4(0.0f));
310                 prog.SetFG(glm::vec4(fg) * (1.0f / 255.0f));
311                 prog.SetM(viewport.Cursor());
312                 text.Render(viewport);
313         }
314         if (active) {
315                 glm::vec2 offset(0.0f);
316                 if (input.empty() || AtBegin()) {
317                         // a okay
318                         offset = -align(text.Pivot(), glm::vec2(0.0f, font.LineSkip()));
319                 } else if (AtEnd()) {
320                         offset = -align(text.Pivot(), text.Size(), glm::vec2(-text.Size().x, 0.0f));
321                 } else {
322                         offset = -align(text.Pivot(), text.Size(), glm::vec2(-font.TextSize(input.substr(0, cursor).c_str()).x, 0.0f));
323                 }
324                 viewport.MoveCursor(glm::vec3(offset, -1.0f));
325                 graphics::PlainColor &prog = viewport.HUDColorProgram();
326                 prog.SetM(viewport.Cursor());
327                 cursor_mesh.DrawLines();
328         }
329 }
330
331 }
332 }