Mercurial > molko
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 } |