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