comparison libmlk-ui/mlk/ui/gridmenu.c @ 506:e205625015ba

ui: gridmenu is stylable
author David Demelier <markand@malikania.fr>
date Thu, 02 Mar 2023 08:54:00 +0100
parents 6100c643dba0
children d49a05e7a5b5
comparison
equal deleted inserted replaced
505:6100c643dba0 506:e205625015ba
19 #include <assert.h> 19 #include <assert.h>
20 #include <math.h> 20 #include <math.h>
21 #include <string.h> 21 #include <string.h>
22 22
23 #include <mlk/core/event.h> 23 #include <mlk/core/event.h>
24 #include <mlk/core/font.h>
24 #include <mlk/core/maths.h> 25 #include <mlk/core/maths.h>
25 #include <mlk/core/painter.h> 26 #include <mlk/core/painter.h>
26 #include <mlk/core/panic.h> 27 #include <mlk/core/panic.h>
27 #include <mlk/core/texture.h> 28 #include <mlk/core/texture.h>
28 #include <mlk/core/trace.h> 29 #include <mlk/core/trace.h>
29 30
30 #include "frame.h"
31 #include "label.h"
32 #include "gridmenu.h" 31 #include "gridmenu.h"
32 #include "ui.h"
33 33
34 #define STYLE_INVOKE(s, f, ...) \ 34 #define STYLE_INVOKE(s, f, ...) \
35 do { \ 35 do { \
36 if (s && s->f) \ 36 if (s && s->f) \
37 s->f(s, __VA_ARGS__); \ 37 s->f(s, __VA_ARGS__); \
44 struct index { 44 struct index {
45 unsigned int row; 45 unsigned int row;
46 unsigned int col; 46 unsigned int col;
47 }; 47 };
48 48
49 static inline struct mlk_font *
50 style_font(struct mlk_gridmenu_style *style)
51 {
52 if (style && style->text_font)
53 return style->text_font;
54
55 return mlk_ui_fonts[MLK_UI_FONT_INTERFACE];
56 }
57
49 static struct index 58 static struct index
50 get_index(const struct mlk_gridmenu *menu) 59 get_index(const struct mlk_gridmenu *menu)
51 { 60 {
52 return (struct index) { 61 return (struct index) {
53 .row = menu->selected / menu->ncols, 62 .row = (unsigned int)(menu->selected / menu->ncols),
54 .col = menu->selected % menu->ncols 63 .col = (unsigned int)(menu->selected % menu->ncols)
55 }; 64 };
56 } 65 }
57 66
58 static void 67 static void
59 geometry(struct mlk_gridmenu *menu) 68 geometry(struct mlk_gridmenu *menu)
60 { 69 {
61 struct mlk_label label = {
62 .style = menu->text_style
63 };
64 unsigned int reqw = 0, reqh = 0, lw, lh; 70 unsigned int reqw = 0, reqh = 0, lw, lh;
65 71
66 /* Compute which item has the bigger width/height to create a spacing. */ 72 /* Compute which item has the bigger width/height to create a spacing. */
67 menu->eltw = menu->elth = 0; 73 menu->eltw = menu->elth = 0;
68 menu->spacew = menu->spaceh = 0; 74 menu->spacew = menu->spaceh = 0;
69 75
70 for (size_t i = 0; i < menu->itemsz; ++i) { 76 for (size_t i = 0; i < menu->itemsz; ++i) {
71 if (!(label.text = menu->items[i])) 77 if (!(menu->items[i]))
72 continue; 78 continue;
73 79
74 mlk_label_query(&label, &lw, &lh); 80 mlk_font_query(style_font(menu->style), menu->items[i], &lw, &lh);
75 81
76 menu->eltw = fmax(menu->eltw, lw); 82 menu->eltw = fmax(menu->eltw, lw);
77 menu->elth = fmax(menu->elth, lh); 83 menu->elth = fmax(menu->elth, lh);
78 } 84 }
79 85
80 /* Total texture size required to draw items. */ 86 /* Total texture size required to draw items. */
81 reqw = (STYLE_GET(menu->style, padding) * 2) + (menu->eltw * menu->ncols); 87 reqw = (STYLE_GET(menu->style, padding) * 3) + (menu->eltw * menu->ncols);
82 reqh = (STYLE_GET(menu->style, padding) * 2) + (menu->elth * menu->nrows); 88 reqh = (STYLE_GET(menu->style, padding) * 3) + (menu->elth * menu->nrows);
83 89
84 /* 90 /*
85 * Compute spacing between elements. We remove the padding because it 91 * Compute spacing between elements. We remove the padding because it
86 * is outside of the elements. 92 * is outside of the elements.
87 */ 93 */
100 reqh -= STYLE_GET(menu->style, padding) * 2; 106 reqh -= STYLE_GET(menu->style, padding) * 2;
101 menu->spaceh = (menu->h - reqh) / menu->nrows; 107 menu->spaceh = (menu->h - reqh) / menu->nrows;
102 } 108 }
103 } 109 }
104 110
111 static int
112 handle_keydown(struct mlk_gridmenu *menu, const struct mlk_event_key *key)
113 {
114 assert(key->type == MLK_EVENT_KEYDOWN);
115
116 const struct index idx = get_index(menu);
117 int validate = 0;
118
119 switch (key->key) {
120 case MLK_KEY_UP:
121 if (idx.row > 0)
122 menu->selected -= menu->ncols;
123 break;
124 case MLK_KEY_RIGHT:
125 if (menu->selected + 1U < menu->itemsz)
126 menu->selected += 1;
127 break;
128 case MLK_KEY_DOWN:
129 if (idx.row + 1U < menu->itemsz / menu->ncols)
130 menu->selected += menu->ncols;
131 else
132 menu->selected = menu->itemsz - 1;
133 break;
134 case MLK_KEY_LEFT:
135 if (idx.col > 0)
136 menu->selected -= 1;
137 break;
138 case MLK_KEY_ENTER:
139 validate = 1;
140 break;
141 default:
142 break;
143 }
144
145 return validate;
146 }
147
148 static int
149 handle_clickdown(struct mlk_gridmenu *menu, const struct mlk_event_click *click)
150 {
151 assert(click->type == MLK_EVENT_CLICKDOWN);
152
153 size_t pagesz, pagenr, selected, c = 0, r = 0;
154 int x, y;
155
156 pagesz = menu->nrows * menu->ncols;
157 pagenr = menu->selected / pagesz;
158
159 for (size_t i = 0; i < pagesz; ++i) {
160 x = (int)(menu->x + STYLE_GET(menu->style, padding) + (c * menu->eltw) + (c * menu->spacew));
161 y = (int)(menu->y + STYLE_GET(menu->style, padding) + (r * menu->elth) + (r * menu->spaceh));
162
163 if (mlk_maths_is_boxed(x, y, menu->eltw, menu->elth, click->x, click->y)) {
164 selected = c + r * menu->ncols;
165 selected += pagesz * pagenr;
166
167 if (selected < menu->itemsz) {
168 menu->selected = selected;
169 return 1;
170 }
171 }
172
173 if (++c >= menu->ncols) {
174 ++r;
175 c = 0;
176 }
177 }
178
179 return 0;
180 }
181
105 static void 182 static void
106 draw_frame(const struct mlk_gridmenu *menu) 183 draw(struct mlk_gridmenu_style *style, const struct mlk_gridmenu *menu)
107 {
108 const struct mlk_frame f = {
109 .x = menu->x,
110 .y = menu->y,
111 .w = menu->w,
112 .h = menu->h,
113 };
114
115 mlk_frame_draw(&f);
116 }
117
118 static void
119 draw_labels(const struct mlk_gridmenu *menu)
120 { 184 {
121 size_t pagesz, pagenr, item, c = 0, r = 0; 185 size_t pagesz, pagenr, item, c = 0, r = 0;
122 struct mlk_label label = {0}; 186 struct mlk_texture tex;
187 struct mlk_font *font;
188 unsigned long color;
189 int x, y, err;
123 190
124 /* 191 /*
125 * Select the first top-left column based on the current selection and 192 * Select the first top-left column based on the current selection and
126 * the number of rows/columns. 193 * the number of rows/columns.
127 */ 194 */
128 pagesz = menu->nrows * menu->ncols; 195 pagesz = menu->nrows * menu->ncols;
129 pagenr = menu->selected / pagesz; 196 pagenr = menu->selected / pagesz;
130 197
198 font = style_font(menu->style);
199
131 for (size_t i = 0; i < pagesz; ++i) { 200 for (size_t i = 0; i < pagesz; ++i) {
132 item = i + pagenr * pagesz; 201 item = i + pagenr * pagesz;
133 202
134 if (item >= menu->itemsz || !menu->items[item]) 203 if (item >= menu->itemsz || !menu->items[item])
135 continue; 204 continue;
136 205
137 label.text = menu->items[item]; 206 x = (int)(menu->x + STYLE_GET(menu->style, padding) + (c * menu->eltw) + (c * menu->spacew));
138 label.x = menu->x + STYLE_GET(menu->style, padding) + (c * menu->eltw) + (c * menu->spacew); 207 y = (int)(menu->y + STYLE_GET(menu->style, padding) + (r * menu->elth) + (r * menu->spaceh));
139 label.y = menu->y + STYLE_GET(menu->style, padding) + (r * menu->elth) + (r * menu->spaceh);
140 208
141 if (i == menu->selected % pagesz) 209 if (i == menu->selected % pagesz)
142 label.style = menu->text_selected_style 210 color = STYLE_GET(menu->style, text_selected_color);
143 ? menu->text_selected_style
144 : &mlk_label_style_selected;
145 else 211 else
146 label.style = menu->text_style; 212 color = STYLE_GET(menu->style, text_color);
147 213
148 mlk_label_draw(&label); 214 if ((err = mlk_font_render(font, &tex, menu->items[item], color)) < 0) {
215 mlk_tracef("unable to render grid menu item: %s", mlk_err_string(err));
216 continue;
217 }
218
219 mlk_texture_draw(&tex, x, y);
220 mlk_texture_finish(&tex);
149 221
150 if (++c >= menu->ncols) { 222 if (++c >= menu->ncols) {
151 ++r; 223 ++r;
152 c = 0; 224 c = 0;
153 } 225 }
154 } 226 }
155 } 227 }
156 228
157 static int 229 struct mlk_gridmenu_style mlk_gridmenu_style = {
158 handle_keydown(struct mlk_gridmenu *menu, const struct mlk_event_key *key) 230 .padding = 10,
159 { 231 .text_color = 0x000000ff,
160 assert(key->type == MLK_EVENT_KEYDOWN); 232 .text_selected_color = 0x328ca7ff,
161 233 .draw = draw
162 const struct index idx = get_index(menu); 234 };
163 int validate = 0; 235
164 236 void
165 switch (key->key) { 237 mlk_gridmenu_init(struct mlk_gridmenu *menu)
166 case MLK_KEY_UP: 238 {
167 if (idx.row > 0) 239 assert(menu);
168 menu->selected -= menu->ncols; 240
169 break; 241 STYLE_INVOKE(menu->style, init, menu);
170 case MLK_KEY_RIGHT: 242 }
171 if (menu->selected + 1U < menu->itemsz) 243
172 menu->selected += 1; 244 int
173 break; 245 mlk_gridmenu_ok(const struct mlk_gridmenu *menu)
174 case MLK_KEY_DOWN: 246 {
175 if (idx.row + 1U < menu->itemsz / menu->ncols) 247 return menu && menu->items && menu->itemsz;
176 menu->selected += menu->ncols;
177 else
178 menu->selected = menu->itemsz - 1;
179 break;
180 case MLK_KEY_LEFT:
181 if (idx.col > 0)
182 menu->selected -= 1;
183 break;
184 case MLK_KEY_ENTER:
185 validate = 1;
186 break;
187 default:
188 break;
189 }
190
191 return validate;
192 }
193
194 static int
195 handle_clickdown(struct mlk_gridmenu *menu, const struct mlk_event_click *click)
196 {
197 assert(click->type == MLK_EVENT_CLICKDOWN);
198
199 size_t pagesz, pagenr, selected, c = 0, r = 0;
200 int x, y;
201
202 pagesz = menu->nrows * menu->ncols;
203 pagenr = menu->selected / pagesz;
204
205 for (size_t i = 0; i < pagesz; ++i) {
206 x = menu->x + STYLE_GET(menu->style, padding) + (c * menu->eltw) + (c * menu->spacew);
207 y = menu->y + STYLE_GET(menu->style, padding) + (r * menu->elth) + (r * menu->spaceh);
208
209 if (mlk_maths_is_boxed(x, y, menu->eltw, menu->elth, click->x, click->y)) {
210 selected = c + r * menu->ncols;
211 selected += pagesz * pagenr;
212
213 if (selected < menu->itemsz) {
214 menu->selected = selected;
215 return 1;
216 }
217 }
218
219 if (++c >= menu->ncols) {
220 ++r;
221 c = 0;
222 }
223 }
224
225 return 0;
226 }
227
228 void
229 mlk_gridmenu_init(struct mlk_gridmenu *menu,
230 unsigned int nr,
231 unsigned int nc,
232 const char * const *items,
233 size_t itemsz)
234 {
235 assert(menu);
236 assert(nr);
237 assert(nc);
238
239 memset(menu, 0, sizeof (*menu));
240
241 menu->nrows = nr;
242 menu->ncols = nc;
243 menu->items = items;
244 menu->itemsz = itemsz;
245 } 248 }
246 249
247 void 250 void
248 mlk_gridmenu_resize(struct mlk_gridmenu *menu, int x, int y, unsigned int w, unsigned int h) 251 mlk_gridmenu_resize(struct mlk_gridmenu *menu, int x, int y, unsigned int w, unsigned int h)
249 { 252 {
276 279
277 return 0; 280 return 0;
278 } 281 }
279 282
280 void 283 void
284 mlk_gridmenu_update(struct mlk_gridmenu *menu, unsigned int ticks)
285 {
286 assert(mlk_gridmenu_ok(menu));
287
288 STYLE_INVOKE(menu->style, update, menu, ticks);
289 }
290
291 void
281 mlk_gridmenu_draw(const struct mlk_gridmenu *menu) 292 mlk_gridmenu_draw(const struct mlk_gridmenu *menu)
282 { 293 {
283 assert(menu); 294 assert(menu);
284 295
285 draw_frame(menu); 296 STYLE_INVOKE(menu->style, draw, menu);
286 draw_labels(menu);
287 } 297 }
288 298
289 void 299 void
290 mlk_gridmenu_finish(struct mlk_gridmenu *menu) 300 mlk_gridmenu_finish(struct mlk_gridmenu *menu)
291 { 301 {
292 assert(menu); 302 assert(menu);
293 303
294 304 STYLE_INVOKE(menu->style, finish, menu);
295 305 }
296 }