]> git.lizzy.rs Git - nothing.git/blob - src/game/edit_field.c
Remove edit_field_ring
[nothing.git] / src / game / edit_field.c
1 #include <assert.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/error.h"
8 #include "system/lt.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     bool shift_key;
22     bool focused;
23 };
24
25 static void edit_field_left(Edit_field *edit_field);
26 static void edit_field_right(Edit_field *edit_field);
27 static void edit_field_backspace(Edit_field *edit_field);
28 static void edit_field_delete(Edit_field *edit_field);
29 static void edit_field_insert_char(Edit_field *edit_field, char c);
30
31 static char shift_char(char c)
32 {
33     if (c == '9') {
34         return '(';
35     } else if (c == '0') {
36         return ')';
37     } else if (c == '=') {
38         return '+';
39     } else if (c == '\'') {
40         return '"';
41     }else {
42         return c;
43     }
44 }
45
46 Edit_field *create_edit_field(const Sprite_font *font,
47                                 Vec font_size,
48                                 Color font_color)
49 {
50     assert(font);
51
52     Lt *lt = create_lt();
53
54     if (lt == NULL) {
55         return NULL;
56     }
57
58     Edit_field *const edit_field = PUSH_LT(lt, malloc(sizeof(Edit_field)), free);
59     if (edit_field == NULL) {
60         throw_error(ERROR_TYPE_LIBC);
61         RETURN_LT(lt, NULL);
62     }
63     edit_field->lt = lt;
64
65     edit_field->buffer = PUSH_LT(lt, malloc(sizeof(char) * (BUFFER_CAPACITY + 10)), free);
66     if (edit_field->buffer == NULL) {
67         throw_error(ERROR_TYPE_LIBC);
68         RETURN_LT(lt, NULL);
69     }
70
71     edit_field->buffer_size = 0;
72     edit_field->cursor = 0;
73     edit_field->font = font;
74     edit_field->font_size = font_size;
75     edit_field->font_color = font_color;
76     edit_field->shift_key = false;
77     edit_field->focused = false;
78
79     edit_field->buffer[edit_field->buffer_size] = 0;
80
81     return edit_field;
82 }
83
84 void destroy_edit_field(Edit_field *edit_field)
85 {
86     assert(edit_field);
87     RETURN_LT0(edit_field->lt);
88 }
89
90 int edit_field_render(const Edit_field *edit_field,
91                       SDL_Renderer *renderer,
92                       Point position)
93 {
94     assert(edit_field);
95     assert(renderer);
96
97     const float cursor_y_overflow = 10.0f;
98     const float cursor_width = 2.0f;
99
100     if (sprite_font_render_text(edit_field->font,
101                                 renderer,
102                                 position,
103                                 edit_field->font_size,
104                                 edit_field->font_color,
105                                 edit_field->buffer) < 0) {
106         return -1;
107     }
108
109     /* TODO(#363): the size of the cursor does not correspond to font size */
110     if (edit_field->focused) {
111         if (fill_rect(
112                 renderer,
113                 rect(position.x + (float) edit_field->cursor * (float) FONT_CHAR_WIDTH * edit_field->font_size.x,
114                      position.y - cursor_y_overflow,
115                      cursor_width,
116                      FONT_CHAR_HEIGHT * edit_field->font_size.y + cursor_y_overflow * 2.0f),
117                 edit_field->font_color) < 0) {
118             return -1;
119         }
120     }
121
122     return 0;
123 }
124
125 int edit_field_handle_event(Edit_field *edit_field,
126                             const SDL_Event *event)
127 {
128     assert(edit_field);
129     assert(event);
130
131     switch (event->type) {
132     case SDL_KEYDOWN:
133         switch (event->key.keysym.sym) {
134         case SDLK_LEFT:
135             edit_field_left(edit_field);
136             break;
137
138         case SDLK_RIGHT:
139             edit_field_right(edit_field);
140             break;
141
142         case SDLK_BACKSPACE:
143             edit_field_backspace(edit_field);
144             break;
145
146         case SDLK_RSHIFT:
147         case SDLK_LSHIFT:
148             edit_field->shift_key = true;
149             break;
150
151         case SDLK_DELETE:
152             edit_field_delete(edit_field);
153             break;
154
155         default: {
156             edit_field_insert_char(edit_field, (char) event->key.keysym.sym);
157         }
158         }
159         break;
160
161     case SDL_KEYUP:
162         switch (event->key.keysym.sym) {
163         case SDLK_RSHIFT:
164         case SDLK_LSHIFT:
165             edit_field->shift_key = false;
166             break;
167
168         default: {}
169         }
170         break;
171
172     default: {}
173     }
174
175     return 0;
176 }
177
178 const char *edit_field_as_text(const Edit_field *edit_field)
179 {
180     assert(edit_field);
181     return edit_field->buffer;
182 }
183
184 static void edit_field_left(Edit_field *edit_field)
185 {
186     if (edit_field->cursor > 0) {
187         edit_field->cursor--;
188     }
189 }
190
191 static void edit_field_right(Edit_field *edit_field)
192 {
193     assert(edit_field);
194     if (edit_field->cursor < edit_field->buffer_size) {
195         edit_field->cursor++;
196     }
197 }
198
199 static void edit_field_backspace(Edit_field *edit_field)
200 {
201     assert(edit_field);
202
203     if (edit_field->cursor == 0) {
204         return;
205     }
206
207     for (size_t i = edit_field->cursor; i < edit_field->buffer_size; ++i) {
208         edit_field->buffer[i - 1] = edit_field->buffer[i];
209     }
210
211     edit_field->cursor--;
212     edit_field->buffer[--edit_field->buffer_size] = 0;
213 }
214
215 static void edit_field_delete(Edit_field *edit_field)
216 {
217     assert(edit_field);
218
219     if (edit_field->cursor >= edit_field->buffer_size) {
220         return;
221     }
222
223     for (size_t i = edit_field->cursor; i < edit_field->buffer_size; ++i) {
224         edit_field->buffer[i] = edit_field->buffer[i + 1];
225     }
226
227     edit_field->buffer[--edit_field->buffer_size] = 0;
228 }
229
230 static void edit_field_insert_char(Edit_field *edit_field, char c)
231 {
232     assert(edit_field);
233
234     if (edit_field->shift_key) {
235         c = shift_char(c);
236     }
237
238     if (edit_field->buffer_size >= BUFFER_CAPACITY) {
239         return;
240     }
241
242     for (int64_t i = (int64_t) edit_field->buffer_size - 1; i >= (int64_t) edit_field->cursor; --i) {
243         edit_field->buffer[i + 1] = edit_field->buffer[i];
244     }
245
246     edit_field->buffer[edit_field->cursor++] = c;
247     edit_field->buffer[++edit_field->buffer_size] = 0;
248 }
249
250 void edit_field_clean(Edit_field *edit_field)
251 {
252     edit_field->cursor = 0;
253     edit_field->buffer_size = 0;
254     edit_field->buffer[0] = 0;
255 }
256
257 void edit_field_focus(Edit_field *edit_field)
258 {
259     edit_field->focused = true;
260 }
261
262 void edit_field_unfocus(Edit_field *edit_field)
263 {
264     edit_field->focused = false;
265 }