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