]> git.lizzy.rs Git - nothing.git/blob - src/ui/edit_field.c
Merge pull request #972 from tsoding/965
[nothing.git] / src / ui / edit_field.c
1 #include <stdbool.h>
2
3 #include "edit_field.h"
4 #include "game/camera.h"
5 #include "game/sprite_font.h"
6 #include "sdl/renderer.h"
7 #include "system/lt.h"
8 #include "system/nth_alloc.h"
9 #include "system/stacktrace.h"
10
11 #define BUFFER_CAPACITY 256
12
13 struct Edit_field
14 {
15     Lt *lt;
16     char *buffer;
17     size_t buffer_size;
18     size_t cursor;
19     Vec font_size;
20     Color font_color;
21 };
22
23 static void edit_field_left(Edit_field *edit_field);
24 static void edit_field_right(Edit_field *edit_field);
25 static void edit_field_backspace(Edit_field *edit_field);
26 static void edit_field_delete(Edit_field *edit_field);
27 static void edit_field_insert_char(Edit_field *edit_field, char c);
28
29 Edit_field *create_edit_field(Vec font_size,
30                               Color font_color)
31 {
32     Lt *lt = create_lt();
33
34     Edit_field *const edit_field = PUSH_LT(lt, nth_calloc(1, sizeof(Edit_field)), free);
35     if (edit_field == NULL) {
36         RETURN_LT(lt, NULL);
37     }
38     edit_field->lt = lt;
39
40     edit_field->buffer = PUSH_LT(lt, nth_calloc(1, sizeof(char) * (BUFFER_CAPACITY + 10)), free);
41     if (edit_field->buffer == NULL) {
42         RETURN_LT(lt, NULL);
43     }
44
45     edit_field->buffer_size = 0;
46     edit_field->cursor = 0;
47     edit_field->font_size = font_size;
48     edit_field->font_color = font_color;
49
50     edit_field->buffer[edit_field->buffer_size] = 0;
51
52     return edit_field;
53 }
54
55 void destroy_edit_field(Edit_field *edit_field)
56 {
57     trace_assert(edit_field);
58     RETURN_LT0(edit_field->lt);
59 }
60
61 int edit_field_render_screen(const Edit_field *edit_field,
62                              Camera *camera,
63                              Point screen_position)
64 {
65     trace_assert(edit_field);
66     trace_assert(camera);
67
68     const float cursor_y_overflow = 10.0f;
69     const float cursor_width = 2.0f;
70
71     if (camera_render_text_screen(
72             camera,
73             edit_field->buffer,
74             edit_field->font_size,
75             edit_field->font_color,
76             screen_position) < 0) {
77         return -1;
78     }
79
80     /* TODO(#363): the size of the cursor does not correspond to font size */
81     if (camera_fill_rect_screen(
82             camera,
83             rect(screen_position.x + (float) edit_field->cursor * (float) FONT_CHAR_WIDTH * edit_field->font_size.x,
84                  screen_position.y - cursor_y_overflow,
85                  cursor_width,
86                  FONT_CHAR_HEIGHT * edit_field->font_size.y + cursor_y_overflow * 2.0f),
87             edit_field->font_color) < 0) {
88         return -1;
89     }
90
91     return 0;
92 }
93
94 int edit_field_render_world(const Edit_field *edit_field,
95                             Camera *camera,
96                             Point world_position)
97 {
98     trace_assert(edit_field);
99     trace_assert(camera);
100
101     const float cursor_y_overflow = 10.0f;
102     const float cursor_width = 2.0f;
103
104     if (camera_render_text(
105             camera,
106             edit_field->buffer,
107             edit_field->font_size,
108             edit_field->font_color,
109             world_position) < 0) {
110         return -1;
111     }
112
113     if (camera_fill_rect(
114             camera,
115             rect(world_position.x + (float) edit_field->cursor * (float) FONT_CHAR_WIDTH * edit_field->font_size.x,
116                  world_position.y - cursor_y_overflow,
117                  cursor_width,
118                  FONT_CHAR_HEIGHT * edit_field->font_size.y + cursor_y_overflow * 2.0f),
119             edit_field->font_color) < 0) {
120         return -1;
121     }
122
123     return 0;
124 }
125
126 const char *edit_field_as_text(const Edit_field *edit_field)
127 {
128     trace_assert(edit_field);
129     return edit_field->buffer;
130 }
131
132 static void edit_field_left(Edit_field *edit_field)
133 {
134     if (edit_field->cursor > 0) {
135         edit_field->cursor--;
136     }
137 }
138
139 static void edit_field_right(Edit_field *edit_field)
140 {
141     trace_assert(edit_field);
142     if (edit_field->cursor < edit_field->buffer_size) {
143         edit_field->cursor++;
144     }
145 }
146
147 static void edit_field_backspace(Edit_field *edit_field)
148 {
149     trace_assert(edit_field);
150
151     if (edit_field->cursor == 0) {
152         return;
153     }
154
155     for (size_t i = edit_field->cursor; i < edit_field->buffer_size; ++i) {
156         edit_field->buffer[i - 1] = edit_field->buffer[i];
157     }
158
159     edit_field->cursor--;
160     edit_field->buffer[--edit_field->buffer_size] = 0;
161 }
162
163 static void edit_field_delete(Edit_field *edit_field)
164 {
165     trace_assert(edit_field);
166
167     if (edit_field->cursor >= edit_field->buffer_size) {
168         return;
169     }
170
171     for (size_t i = edit_field->cursor; i < edit_field->buffer_size; ++i) {
172         edit_field->buffer[i] = edit_field->buffer[i + 1];
173     }
174
175     edit_field->buffer[--edit_field->buffer_size] = 0;
176 }
177
178 static void edit_field_insert_char(Edit_field *edit_field, char c)
179 {
180     trace_assert(edit_field);
181
182     if (edit_field->buffer_size >= BUFFER_CAPACITY) {
183         return;
184     }
185
186     for (int64_t i = (int64_t) edit_field->buffer_size - 1; i >= (int64_t) edit_field->cursor; --i) {
187         edit_field->buffer[i + 1] = edit_field->buffer[i];
188     }
189
190     edit_field->buffer[edit_field->cursor++] = c;
191     edit_field->buffer[++edit_field->buffer_size] = 0;
192 }
193
194 void edit_field_clean(Edit_field *edit_field)
195 {
196     trace_assert(edit_field);
197
198     edit_field->cursor = 0;
199     edit_field->buffer_size = 0;
200     edit_field->buffer[0] = 0;
201 }
202
203 void edit_field_replace(Edit_field *edit_field, const char *text)
204 {
205     trace_assert(edit_field);
206
207     edit_field_clean(edit_field);
208
209     if (text == NULL) {
210         return;
211     }
212
213     size_t n = strlen(text);
214     for (size_t i = 0; i < n; ++i) {
215         edit_field_insert_char(edit_field, text[i]);
216     }
217 }
218
219 int edit_field_event(Edit_field *edit_field, const SDL_Event *event)
220 {
221     trace_assert(edit_field);
222     trace_assert(event);
223
224     switch (event->type) {
225     case SDL_KEYDOWN: {
226         switch (event->key.keysym.sym) {
227         case SDLK_LEFT:
228             edit_field_left(edit_field);
229             break;
230
231         case SDLK_RIGHT:
232             edit_field_right(edit_field);
233             break;
234
235         case SDLK_BACKSPACE:
236             edit_field_backspace(edit_field);
237             break;
238
239         case SDLK_DELETE:
240             edit_field_delete(edit_field);
241             break;
242         }
243     } break;
244
245     case SDL_TEXTINPUT: {
246         size_t n = strlen(event->text.text);
247         for (size_t i = 0; i < n; ++i) {
248             edit_field_insert_char(edit_field, event->text.text[i]);
249         }
250     } break;
251     }
252
253     return 0;
254 }