source: mainline/uspace/lib/ui/src/menu.c@ 9754ed2

serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 9754ed2 was ff6e91b, checked in by Jiri Svoboda <jiri@…>, 4 years ago

Make use of code page 437 characters

To draw proper text boxes, for a nice round radio button light,
for a nice contiguous slider groove.

  • Property mode set to 100644
File size: 10.4 KB
Line 
1/*
2 * Copyright (c) 2021 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup libui
30 * @{
31 */
32/**
33 * @file Menu
34 */
35
36#include <adt/list.h>
37#include <errno.h>
38#include <gfx/color.h>
39#include <gfx/context.h>
40#include <gfx/render.h>
41#include <gfx/text.h>
42#include <io/pos_event.h>
43#include <stdlib.h>
44#include <str.h>
45#include <ui/control.h>
46#include <ui/paint.h>
47#include <ui/popup.h>
48#include <ui/menu.h>
49#include <ui/menuentry.h>
50#include <ui/resource.h>
51#include <ui/window.h>
52#include "../private/menubar.h"
53#include "../private/menu.h"
54#include "../private/resource.h"
55
56enum {
57 menu_frame_w = 4,
58 menu_frame_h = 4,
59 menu_frame_w_text = 2,
60 menu_frame_h_text = 1,
61 menu_frame_h_margin_text = 1
62};
63
64static void ui_menu_popup_close(ui_popup_t *, void *);
65static void ui_menu_popup_pos(ui_popup_t *, void *, pos_event_t *);
66
67static ui_popup_cb_t ui_menu_popup_cb = {
68 .close = ui_menu_popup_close,
69 .pos = ui_menu_popup_pos
70};
71
72/** Create new menu.
73 *
74 * @param mbar Menu bar
75 * @param caption Caption
76 * @param rmenu Place to store pointer to new menu
77 * @return EOK on success, ENOMEM if out of memory
78 */
79errno_t ui_menu_create(ui_menu_bar_t *mbar, const char *caption,
80 ui_menu_t **rmenu)
81{
82 ui_menu_t *menu;
83
84 menu = calloc(1, sizeof(ui_menu_t));
85 if (menu == NULL)
86 return ENOMEM;
87
88 menu->caption = str_dup(caption);
89 if (menu->caption == NULL) {
90 free(menu);
91 return ENOMEM;
92 }
93
94 menu->mbar = mbar;
95 list_append(&menu->lmenus, &mbar->menus);
96 list_initialize(&menu->entries);
97
98 *rmenu = menu;
99 return EOK;
100}
101
102/** Destroy menu.
103 *
104 * @param menu Menu or @c NULL
105 */
106void ui_menu_destroy(ui_menu_t *menu)
107{
108 ui_menu_entry_t *mentry;
109
110 if (menu == NULL)
111 return;
112
113 /* Destroy entries */
114 mentry = ui_menu_entry_first(menu);
115 while (mentry != NULL) {
116 ui_menu_entry_destroy(mentry);
117 mentry = ui_menu_entry_first(menu);
118 }
119
120 list_remove(&menu->lmenus);
121 free(menu->caption);
122 free(menu);
123}
124
125/** Get first menu in menu bar.
126 *
127 * @param mbar Menu bar
128 * @return First menu or @c NULL if there is none
129 */
130ui_menu_t *ui_menu_first(ui_menu_bar_t *mbar)
131{
132 link_t *link;
133
134 link = list_first(&mbar->menus);
135 if (link == NULL)
136 return NULL;
137
138 return list_get_instance(link, ui_menu_t, lmenus);
139}
140
141/** Get next menu in menu bar.
142 *
143 * @param cur Current menu
144 * @return Next menu or @c NULL if @a cur is the last one
145 */
146ui_menu_t *ui_menu_next(ui_menu_t *cur)
147{
148 link_t *link;
149
150 link = list_next(&cur->lmenus, &cur->mbar->menus);
151 if (link == NULL)
152 return NULL;
153
154 return list_get_instance(link, ui_menu_t, lmenus);
155}
156
157/** Get menu caption.
158 *
159 * @param menu Menu
160 * @return Caption (owned by @a menu)
161 */
162const char *ui_menu_caption(ui_menu_t *menu)
163{
164 return menu->caption;
165}
166
167/** Get menu geometry.
168 *
169 * @param menu Menu
170 * @param spos Starting position
171 * @param geom Structure to fill in with computed geometry
172 */
173void ui_menu_get_geom(ui_menu_t *menu, gfx_coord2_t *spos,
174 ui_menu_geom_t *geom)
175{
176 ui_resource_t *res;
177 gfx_coord2_t edim;
178 gfx_coord_t frame_w;
179 gfx_coord_t frame_h;
180
181 res = ui_window_get_res(menu->mbar->window);
182
183 if (res->textmode) {
184 frame_w = menu_frame_w_text;
185 frame_h = menu_frame_h_text;
186 } else {
187 frame_w = menu_frame_w;
188 frame_h = menu_frame_h;
189 }
190
191 edim.x = ui_menu_entry_calc_width(menu, menu->max_caption_w,
192 menu->max_shortcut_w);
193 edim.y = menu->total_h;
194
195 geom->outer_rect.p0 = *spos;
196 geom->outer_rect.p1.x = spos->x + edim.x + 2 * frame_w;
197 geom->outer_rect.p1.y = spos->y + edim.y + 2 * frame_h;
198
199 geom->entries_rect.p0.x = spos->x + frame_w;
200 geom->entries_rect.p0.y = spos->y + frame_h;
201 geom->entries_rect.p1.x = geom->entries_rect.p0.x + edim.x;
202 geom->entries_rect.p1.y = geom->entries_rect.p0.x + edim.y;
203}
204
205/** Get menu rectangle.
206 *
207 * @param menu Menu
208 * @param spos Starting position (top-left corner)
209 * @param rect Place to store menu rectangle
210 */
211void ui_menu_get_rect(ui_menu_t *menu, gfx_coord2_t *spos, gfx_rect_t *rect)
212{
213 ui_menu_geom_t geom;
214
215 ui_menu_get_geom(menu, spos, &geom);
216 *rect = geom.outer_rect;
217}
218
219/** Get UI resource from menu.
220 *
221 * @param menu Menu
222 * @return UI resource
223 */
224ui_resource_t *ui_menu_get_res(ui_menu_t *menu)
225{
226 return ui_popup_get_res(menu->popup);
227}
228
229/** Open menu.
230 *
231 * @param menu Menu
232 * @param prect Parent rectangle around which the menu should be placed
233 */
234errno_t ui_menu_open(ui_menu_t *menu, gfx_rect_t *prect)
235{
236 ui_popup_t *popup = NULL;
237 ui_popup_params_t params;
238 ui_menu_geom_t geom;
239 gfx_coord2_t mpos;
240 errno_t rc;
241
242 /* Determine menu dimensions */
243
244 mpos.x = 0;
245 mpos.y = 0;
246 ui_menu_get_geom(menu, &mpos, &geom);
247
248 ui_popup_params_init(&params);
249 params.rect = geom.outer_rect;
250 params.place = *prect;
251
252 rc = ui_popup_create(menu->mbar->ui, menu->mbar->window, &params,
253 &popup);
254 if (rc != EOK)
255 return rc;
256
257 menu->popup = popup;
258 ui_popup_set_cb(popup, &ui_menu_popup_cb, menu);
259
260 return ui_menu_paint(menu, &mpos);
261}
262
263/** Close menu.
264 *
265 * @param menu Menu
266 */
267void ui_menu_close(ui_menu_t *menu)
268{
269 ui_popup_destroy(menu->popup);
270 menu->popup = NULL;
271}
272
273/** Paint menu.
274 *
275 * @param menu Menu
276 * @param spos Starting position (top-left corner)
277 * @return EOK on success or an error code
278 */
279errno_t ui_menu_paint_bg_gfx(ui_menu_t *menu, gfx_coord2_t *spos)
280{
281 ui_resource_t *res;
282 ui_menu_geom_t geom;
283 gfx_rect_t bg_rect;
284 errno_t rc;
285
286 res = ui_menu_get_res(menu);
287 ui_menu_get_geom(menu, spos, &geom);
288
289 /* Paint menu frame */
290
291 rc = gfx_set_color(res->gc, res->wnd_face_color);
292 if (rc != EOK)
293 goto error;
294
295 rc = ui_paint_outset_frame(res, &geom.outer_rect, &bg_rect);
296 if (rc != EOK)
297 goto error;
298
299 /* Paint menu background */
300
301 rc = gfx_set_color(res->gc, res->wnd_face_color);
302 if (rc != EOK)
303 goto error;
304
305 rc = gfx_fill_rect(res->gc, &bg_rect);
306 if (rc != EOK)
307 goto error;
308
309 return EOK;
310error:
311 return rc;
312}
313
314/** Paint menu.
315 *
316 * @param menu Menu
317 * @param spos Starting position (top-left corner)
318 * @return EOK on success or an error code
319 */
320errno_t ui_menu_paint_bg_text(ui_menu_t *menu, gfx_coord2_t *spos)
321{
322 ui_resource_t *res;
323 ui_menu_geom_t geom;
324 gfx_rect_t rect;
325 errno_t rc;
326
327 res = ui_menu_get_res(menu);
328 ui_menu_get_geom(menu, spos, &geom);
329
330 /* Paint menu background */
331
332 rc = gfx_set_color(res->gc, res->wnd_face_color);
333 if (rc != EOK)
334 goto error;
335
336 rc = gfx_fill_rect(res->gc, &geom.outer_rect);
337 if (rc != EOK)
338 goto error;
339
340 /* Paint menu box */
341
342 rect = geom.outer_rect;
343 rect.p0.x += menu_frame_h_margin_text;
344 rect.p1.x -= menu_frame_h_margin_text;
345
346 rc = ui_paint_text_box(res, &rect, ui_box_single, res->wnd_face_color);
347 if (rc != EOK)
348 goto error;
349
350 return EOK;
351error:
352 return rc;
353}
354
355/** Paint menu.
356 *
357 * @param menu Menu
358 * @param spos Starting position (top-left corner)
359 * @return EOK on success or an error code
360 */
361errno_t ui_menu_paint(ui_menu_t *menu, gfx_coord2_t *spos)
362{
363 ui_resource_t *res;
364 gfx_coord2_t pos;
365 ui_menu_entry_t *mentry;
366 ui_menu_geom_t geom;
367 errno_t rc;
368
369 res = ui_menu_get_res(menu);
370 ui_menu_get_geom(menu, spos, &geom);
371
372 /* Paint menu frame and background */
373 if (res->textmode)
374 rc = ui_menu_paint_bg_text(menu, spos);
375 else
376 rc = ui_menu_paint_bg_gfx(menu, spos);
377 if (rc != EOK)
378 goto error;
379
380 /* Paint entries */
381 pos = geom.entries_rect.p0;
382
383 mentry = ui_menu_entry_first(menu);
384 while (mentry != NULL) {
385 rc = ui_menu_entry_paint(mentry, &pos);
386 if (rc != EOK)
387 goto error;
388
389 pos.y += ui_menu_entry_height(mentry);
390 mentry = ui_menu_entry_next(mentry);
391 }
392
393 rc = gfx_update(res->gc);
394 if (rc != EOK)
395 goto error;
396
397 return EOK;
398error:
399 return rc;
400}
401
402/** Handle position event in menu.
403 *
404 * @param menu Menu
405 * @param spos Starting position (top-left corner)
406 * @param event Position event
407 * @return ui_claimed iff the event was claimed
408 */
409ui_evclaim_t ui_menu_pos_event(ui_menu_t *menu, gfx_coord2_t *spos,
410 pos_event_t *event)
411{
412 ui_menu_geom_t geom;
413 ui_menu_entry_t *mentry;
414 gfx_coord2_t pos;
415 gfx_coord2_t epos;
416 ui_evclaim_t claimed;
417
418 ui_menu_get_geom(menu, spos, &geom);
419 epos.x = event->hpos;
420 epos.y = event->vpos;
421
422 pos = geom.entries_rect.p0;
423
424 mentry = ui_menu_entry_first(menu);
425 while (mentry != NULL) {
426 claimed = ui_menu_entry_pos_event(mentry, &pos, event);
427 if (claimed == ui_claimed)
428 return ui_claimed;
429
430 pos.y += ui_menu_entry_height(mentry);
431 mentry = ui_menu_entry_next(mentry);
432 }
433
434 /* Event inside menu? */
435 if (gfx_pix_inside_rect(&epos, &geom.outer_rect)) {
436 /* Claim event */
437 return ui_claimed;
438 } else {
439 /* Press outside menu - close it */
440 if (event->type == POS_PRESS)
441 ui_menu_bar_select(menu->mbar, NULL, NULL);
442 }
443
444 return ui_unclaimed;
445}
446
447/** Handle close event in menu popup window.
448 *
449 * @param popup Menu popup window
450 * @param arg Argument (ui_menu_t *)
451 */
452static void ui_menu_popup_close(ui_popup_t *popup, void *arg)
453{
454 ui_menu_t *menu = (ui_menu_t *)arg;
455
456 /* Close the menu */
457 ui_menu_bar_select(menu->mbar, NULL, NULL);
458}
459
460/** Handle position event in menu popup window.
461 *
462 * @param popup Menu popup window
463 * @param arg Argument (ui_menu_t *)
464 * @param event Position event
465 */
466static void ui_menu_popup_pos(ui_popup_t *popup, void *arg, pos_event_t *event)
467{
468 ui_menu_t *menu = (ui_menu_t *)arg;
469 gfx_coord2_t spos;
470
471 spos.x = 0;
472 spos.y = 0;
473 ui_menu_pos_event(menu, &spos, event);
474}
475
476/** @}
477 */
Note: See TracBrowser for help on using the repository browser.