source: mainline/uspace/lib/ui/src/menu.c@ 65ec18d

serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 65ec18d was d7f82635, checked in by jxsvoboda <5887334+jxsvoboda@…>, 4 years ago

Deliver close event to popup window when appropriate

That is, when focus changes or when user clicks outside of the
popup window.

  • Property mode set to 100644
File size: 9.0 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};
62
63static void ui_menu_popup_close(ui_popup_t *, void *);
64static void ui_menu_popup_pos(ui_popup_t *, void *, pos_event_t *);
65
66static ui_popup_cb_t ui_menu_popup_cb = {
67 .close = ui_menu_popup_close,
68 .pos = ui_menu_popup_pos
69};
70
71/** Create new menu.
72 *
73 * @param mbar Menu bar
74 * @param caption Caption
75 * @param rmenu Place to store pointer to new menu
76 * @return EOK on success, ENOMEM if out of memory
77 */
78errno_t ui_menu_create(ui_menu_bar_t *mbar, const char *caption,
79 ui_menu_t **rmenu)
80{
81 ui_menu_t *menu;
82
83 menu = calloc(1, sizeof(ui_menu_t));
84 if (menu == NULL)
85 return ENOMEM;
86
87 menu->caption = str_dup(caption);
88 if (menu->caption == NULL) {
89 free(menu);
90 return ENOMEM;
91 }
92
93 menu->mbar = mbar;
94 list_append(&menu->lmenus, &mbar->menus);
95 list_initialize(&menu->entries);
96
97 *rmenu = menu;
98 return EOK;
99}
100
101/** Destroy menu.
102 *
103 * @param menu Menu or @c NULL
104 */
105void ui_menu_destroy(ui_menu_t *menu)
106{
107 ui_menu_entry_t *mentry;
108
109 if (menu == NULL)
110 return;
111
112 /* Destroy entries */
113 mentry = ui_menu_entry_first(menu);
114 while (mentry != NULL) {
115 ui_menu_entry_destroy(mentry);
116 mentry = ui_menu_entry_first(menu);
117 }
118
119 list_remove(&menu->lmenus);
120 free(menu->caption);
121 free(menu);
122}
123
124/** Get first menu in menu bar.
125 *
126 * @param mbar Menu bar
127 * @return First menu or @c NULL if there is none
128 */
129ui_menu_t *ui_menu_first(ui_menu_bar_t *mbar)
130{
131 link_t *link;
132
133 link = list_first(&mbar->menus);
134 if (link == NULL)
135 return NULL;
136
137 return list_get_instance(link, ui_menu_t, lmenus);
138}
139
140/** Get next menu in menu bar.
141 *
142 * @param cur Current menu
143 * @return Next menu or @c NULL if @a cur is the last one
144 */
145ui_menu_t *ui_menu_next(ui_menu_t *cur)
146{
147 link_t *link;
148
149 link = list_next(&cur->lmenus, &cur->mbar->menus);
150 if (link == NULL)
151 return NULL;
152
153 return list_get_instance(link, ui_menu_t, lmenus);
154}
155
156/** Get menu caption.
157 *
158 * @param menu Menu
159 * @return Caption (owned by @a menu)
160 */
161const char *ui_menu_caption(ui_menu_t *menu)
162{
163 return menu->caption;
164}
165
166/** Get menu geometry.
167 *
168 * @param menu Menu
169 * @param spos Starting position
170 * @param geom Structure to fill in with computed geometry
171 */
172void ui_menu_get_geom(ui_menu_t *menu, gfx_coord2_t *spos,
173 ui_menu_geom_t *geom)
174{
175 ui_resource_t *res;
176 gfx_coord2_t edim;
177 gfx_coord_t frame_w;
178 gfx_coord_t frame_h;
179
180 res = ui_window_get_res(menu->mbar->window);
181
182 if (res->textmode) {
183 frame_w = menu_frame_w_text;
184 frame_h = menu_frame_h_text;
185 } else {
186 frame_w = menu_frame_w;
187 frame_h = menu_frame_h;
188 }
189
190 edim.x = ui_menu_entry_calc_width(menu, menu->max_caption_w,
191 menu->max_shortcut_w);
192 edim.y = menu->total_h;
193
194 geom->outer_rect.p0 = *spos;
195 geom->outer_rect.p1.x = spos->x + edim.x + 2 * frame_w;
196 geom->outer_rect.p1.y = spos->y + edim.y + 2 * frame_h;
197
198 geom->entries_rect.p0.x = spos->x + frame_w;
199 geom->entries_rect.p0.y = spos->y + frame_h;
200 geom->entries_rect.p1.x = geom->entries_rect.p0.x + edim.x;
201 geom->entries_rect.p1.y = geom->entries_rect.p0.x + edim.y;
202}
203
204/** Get menu rectangle.
205 *
206 * @param menu Menu
207 * @param spos Starting position (top-left corner)
208 * @param rect Place to store menu rectangle
209 */
210void ui_menu_get_rect(ui_menu_t *menu, gfx_coord2_t *spos, gfx_rect_t *rect)
211{
212 ui_menu_geom_t geom;
213
214 ui_menu_get_geom(menu, spos, &geom);
215 *rect = geom.outer_rect;
216}
217
218/** Get UI resource from menu.
219 *
220 * @param menu Menu
221 * @return UI resource
222 */
223ui_resource_t *ui_menu_get_res(ui_menu_t *menu)
224{
225 return ui_popup_get_res(menu->popup);
226}
227
228/** Open menu.
229 *
230 * @param menu Menu
231 * @param prect Parent rectangle around which the menu should be placed
232 */
233errno_t ui_menu_open(ui_menu_t *menu, gfx_rect_t *prect)
234{
235 ui_popup_t *popup = NULL;
236 ui_popup_params_t params;
237 ui_menu_geom_t geom;
238 gfx_coord2_t mpos;
239 errno_t rc;
240
241 /* Determine menu dimensions */
242
243 mpos.x = 0;
244 mpos.y = 0;
245 ui_menu_get_geom(menu, &mpos, &geom);
246
247 ui_popup_params_init(&params);
248 params.rect = geom.outer_rect;
249 params.place = *prect;
250
251 rc = ui_popup_create(menu->mbar->ui, menu->mbar->window, &params,
252 &popup);
253 if (rc != EOK)
254 return rc;
255
256 menu->popup = popup;
257 ui_popup_set_cb(popup, &ui_menu_popup_cb, menu);
258
259 return ui_menu_paint(menu, &mpos);
260}
261
262/** Close menu.
263 *
264 * @param menu Menu
265 */
266void ui_menu_close(ui_menu_t *menu)
267{
268 ui_popup_destroy(menu->popup);
269 menu->popup = NULL;
270}
271
272/** Paint menu.
273 *
274 * @param menu Menu
275 * @param spos Starting position (top-left corner)
276 * @return EOK on success or an error code
277 */
278errno_t ui_menu_paint(ui_menu_t *menu, gfx_coord2_t *spos)
279{
280 ui_resource_t *res;
281 gfx_coord2_t pos;
282 ui_menu_entry_t *mentry;
283 ui_menu_geom_t geom;
284 gfx_rect_t bg_rect;
285 errno_t rc;
286
287 res = ui_menu_get_res(menu);
288 ui_menu_get_geom(menu, spos, &geom);
289
290 /* Paint menu frame */
291
292 rc = gfx_set_color(res->gc, res->wnd_face_color);
293 if (rc != EOK)
294 goto error;
295
296 rc = ui_paint_outset_frame(res, &geom.outer_rect, &bg_rect);
297 if (rc != EOK)
298 goto error;
299
300 /* Paint menu background */
301
302 rc = gfx_set_color(res->gc, res->wnd_face_color);
303 if (rc != EOK)
304 goto error;
305
306 rc = gfx_fill_rect(res->gc, &bg_rect);
307 if (rc != EOK)
308 goto error;
309
310 /* Paint entries */
311 pos = geom.entries_rect.p0;
312
313 mentry = ui_menu_entry_first(menu);
314 while (mentry != NULL) {
315 rc = ui_menu_entry_paint(mentry, &pos);
316 if (rc != EOK)
317 goto error;
318
319 pos.y += ui_menu_entry_height(mentry);
320 mentry = ui_menu_entry_next(mentry);
321 }
322
323 rc = gfx_update(res->gc);
324 if (rc != EOK)
325 goto error;
326
327 return EOK;
328error:
329 return rc;
330}
331
332/** Handle position event in menu.
333 *
334 * @param menu Menu
335 * @param spos Starting position (top-left corner)
336 * @param event Position event
337 * @return ui_claimed iff the event was claimed
338 */
339ui_evclaim_t ui_menu_pos_event(ui_menu_t *menu, gfx_coord2_t *spos,
340 pos_event_t *event)
341{
342 ui_menu_geom_t geom;
343 ui_menu_entry_t *mentry;
344 gfx_coord2_t pos;
345 gfx_coord2_t epos;
346 ui_evclaim_t claimed;
347
348 ui_menu_get_geom(menu, spos, &geom);
349 epos.x = event->hpos;
350 epos.y = event->vpos;
351
352 pos = geom.entries_rect.p0;
353
354 mentry = ui_menu_entry_first(menu);
355 while (mentry != NULL) {
356 claimed = ui_menu_entry_pos_event(mentry, &pos, event);
357 if (claimed == ui_claimed)
358 return ui_claimed;
359
360 pos.y += ui_menu_entry_height(mentry);
361 mentry = ui_menu_entry_next(mentry);
362 }
363
364 /* Event inside menu? */
365 if (gfx_pix_inside_rect(&epos, &geom.outer_rect)) {
366 /* Claim event */
367 return ui_claimed;
368 } else {
369 /* Press outside menu - close it */
370 if (event->type == POS_PRESS)
371 ui_menu_bar_select(menu->mbar, NULL, NULL);
372 }
373
374 return ui_unclaimed;
375}
376
377/** Handle close event in menu popup window.
378 *
379 * @param popup Menu popup window
380 * @param arg Argument (ui_menu_t *)
381 */
382static void ui_menu_popup_close(ui_popup_t *popup, void *arg)
383{
384 ui_menu_t *menu = (ui_menu_t *)arg;
385
386 /* Close the menu */
387 ui_menu_bar_select(menu->mbar, NULL, NULL);
388}
389
390/** Handle position event in menu popup window.
391 *
392 * @param popup Menu popup window
393 * @param arg Argument (ui_menu_t *)
394 * @param event Position event
395 */
396static void ui_menu_popup_pos(ui_popup_t *popup, void *arg, pos_event_t *event)
397{
398 ui_menu_t *menu = (ui_menu_t *)arg;
399 gfx_coord2_t spos;
400
401 spos.x = 0;
402 spos.y = 0;
403 ui_menu_pos_event(menu, &spos, event);
404}
405
406/** @}
407 */
Note: See TracBrowser for help on using the repository browser.