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