source: mainline/uspace/lib/ui/src/menubar.c@ f536a16

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

Close menu when window is unfocused

This of course means we need to do all the plumbing for delivering
unfocus event to UI controls.

  • Property mode set to 100644
File size: 8.7 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 bar
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/menu.h>
48#include <ui/menubar.h>
49#include "../private/menubar.h"
50#include "../private/resource.h"
51
52enum {
53 menubar_hpad = 4,
54 menubar_vpad = 4,
55 menubar_hpad_text = 1,
56 menubar_vpad_text = 0
57};
58
59static void ui_menu_bar_ctl_destroy(void *);
60static errno_t ui_menu_bar_ctl_paint(void *);
61static ui_evclaim_t ui_menu_bar_ctl_pos_event(void *, pos_event_t *);
62static void ui_menu_bar_ctl_unfocus(void *);
63
64/** Menu bar control ops */
65ui_control_ops_t ui_menu_bar_ops = {
66 .destroy = ui_menu_bar_ctl_destroy,
67 .paint = ui_menu_bar_ctl_paint,
68 .pos_event = ui_menu_bar_ctl_pos_event,
69 .unfocus = ui_menu_bar_ctl_unfocus
70};
71
72/** Create new menu bar.
73 *
74 * @param res UI resource
75 * @param rmbar Place to store pointer to new menu bar
76 * @return EOK on success, ENOMEM if out of memory
77 */
78errno_t ui_menu_bar_create(ui_resource_t *res, ui_menu_bar_t **rmbar)
79{
80 ui_menu_bar_t *mbar;
81 errno_t rc;
82
83 mbar = calloc(1, sizeof(ui_menu_bar_t));
84 if (mbar == NULL)
85 return ENOMEM;
86
87 rc = ui_control_new(&ui_menu_bar_ops, (void *) mbar, &mbar->control);
88 if (rc != EOK) {
89 free(mbar);
90 return rc;
91 }
92
93 mbar->res = res;
94 list_initialize(&mbar->menus);
95 *rmbar = mbar;
96 return EOK;
97}
98
99/** Destroy menu bar
100 *
101 * @param mbar Menu bar or @c NULL
102 */
103void ui_menu_bar_destroy(ui_menu_bar_t *mbar)
104{
105 ui_menu_t *menu;
106
107 if (mbar == NULL)
108 return;
109
110 /* Destroy menus */
111 menu = ui_menu_first(mbar);
112 while (menu != NULL) {
113 ui_menu_destroy(menu);
114 menu = ui_menu_first(mbar);
115 }
116
117 ui_control_delete(mbar->control);
118 free(mbar);
119}
120
121/** Get base control from menu bar.
122 *
123 * @param mbar Menu bar
124 * @return Control
125 */
126ui_control_t *ui_menu_bar_ctl(ui_menu_bar_t *mbar)
127{
128 return mbar->control;
129}
130
131/** Set menu bar rectangle.
132 *
133 * @param mbar Menu bar
134 * @param rect New menu bar rectangle
135 */
136void ui_menu_bar_set_rect(ui_menu_bar_t *mbar, gfx_rect_t *rect)
137{
138 mbar->rect = *rect;
139}
140
141/** Paint menu bar.
142 *
143 * @param mbar Menu bar
144 * @return EOK on success or an error code
145 */
146errno_t ui_menu_bar_paint(ui_menu_bar_t *mbar)
147{
148 gfx_text_fmt_t fmt;
149 gfx_coord2_t pos;
150 gfx_coord2_t tpos;
151 gfx_rect_t rect;
152 gfx_color_t *bg_color;
153 ui_menu_t *menu;
154 const char *caption;
155 gfx_coord_t width;
156 gfx_coord_t hpad;
157 gfx_coord_t vpad;
158 errno_t rc;
159
160 /* Paint menu bar background */
161
162 rc = gfx_set_color(mbar->res->gc, mbar->res->wnd_face_color);
163 if (rc != EOK)
164 goto error;
165
166 rc = gfx_fill_rect(mbar->res->gc, &mbar->rect);
167 if (rc != EOK)
168 goto error;
169
170 if (mbar->res->textmode) {
171 hpad = menubar_hpad_text;
172 vpad = menubar_vpad_text;
173 } else {
174 hpad = menubar_hpad;
175 vpad = menubar_vpad;
176 }
177
178 pos = mbar->rect.p0;
179
180 gfx_text_fmt_init(&fmt);
181 fmt.halign = gfx_halign_left;
182 fmt.valign = gfx_valign_top;
183
184 menu = ui_menu_first(mbar);
185 while (menu != NULL) {
186 caption = ui_menu_caption(menu);
187 width = gfx_text_width(mbar->res->font, caption) + 2 * hpad;
188 tpos.x = pos.x + hpad;
189 tpos.y = pos.y + vpad;
190
191 rect.p0 = pos;
192 rect.p1.x = rect.p0.x + width;
193 rect.p1.y = mbar->rect.p1.y;
194
195 if (menu == mbar->selected) {
196 fmt.color = mbar->res->wnd_sel_text_color;
197 bg_color = mbar->res->wnd_sel_text_bg_color;
198 } else {
199 fmt.color = mbar->res->wnd_text_color;
200 bg_color = mbar->res->wnd_face_color;
201 }
202
203 rc = gfx_set_color(mbar->res->gc, bg_color);
204 if (rc != EOK)
205 goto error;
206
207 rc = gfx_fill_rect(mbar->res->gc, &rect);
208 if (rc != EOK)
209 goto error;
210
211 rc = gfx_puttext(mbar->res->font, &tpos, &fmt, caption);
212 if (rc != EOK)
213 goto error;
214
215 pos.x += width;
216 menu = ui_menu_next(menu);
217 }
218
219 rc = gfx_update(mbar->res->gc);
220 if (rc != EOK)
221 goto error;
222
223 return EOK;
224error:
225 return rc;
226}
227
228/** Select or deselect menu from menu bar.
229 *
230 * Select @a menu. If @a menu is @c NULL or it is already selected,
231 * then select none.
232 *
233 * @param mbar Menu bar
234 * @param pos Position (top-left corner) of menu bar entry
235 * @param menu Menu to select (or deselect if selected) or @c NULL
236 */
237void ui_menu_bar_select(ui_menu_bar_t *mbar, gfx_coord2_t *pos,
238 ui_menu_t *menu)
239{
240 gfx_coord2_t spos;
241 ui_menu_t *old_menu;
242
243 old_menu = mbar->selected;
244
245 if (mbar->selected != menu)
246 mbar->selected = menu;
247 else
248 mbar->selected = NULL;
249
250 /* Need to clear the menu has just been closed */
251 if (old_menu != NULL)
252 (void) ui_menu_unpaint(old_menu);
253
254 (void) ui_menu_bar_paint(mbar);
255
256 if (mbar->selected != NULL) {
257 /* Cache position of selected entry */
258 mbar->sel_pos = *pos;
259
260 /* Position menu under selected menu bar entry */
261 spos.x = pos->x;
262 spos.y = mbar->rect.p1.y;
263
264 (void) ui_menu_paint(mbar->selected, &spos);
265 }
266}
267
268/** Handle menu bar position event.
269 *
270 * @param mbar Menu bar
271 * @param pos_event Position event
272 * @return @c ui_claimed iff the event is claimed
273 */
274ui_evclaim_t ui_menu_bar_pos_event(ui_menu_bar_t *mbar, pos_event_t *event)
275{
276 gfx_coord2_t pos;
277 gfx_coord2_t spos;
278 gfx_rect_t rect;
279 ui_menu_t *menu;
280 const char *caption;
281 gfx_coord_t width;
282 gfx_coord_t hpad;
283 gfx_coord2_t ppos;
284 ui_evclaim_t claimed;
285
286 ppos.x = event->hpos;
287 ppos.y = event->vpos;
288
289 if (mbar->res->textmode) {
290 hpad = menubar_hpad_text;
291 } else {
292 hpad = menubar_hpad;
293 }
294
295 pos = mbar->rect.p0;
296
297 menu = ui_menu_first(mbar);
298 while (menu != NULL) {
299 caption = ui_menu_caption(menu);
300 width = gfx_text_width(mbar->res->font, caption) + 2 * hpad;
301
302 rect.p0 = pos;
303 rect.p1.x = rect.p0.x + width;
304 rect.p1.y = mbar->rect.p1.y;
305
306 /* Check if press is inside menu bar entry */
307 if (event->type == POS_PRESS &&
308 gfx_pix_inside_rect(&ppos, &rect)) {
309 ui_menu_bar_select(mbar, &pos, menu);
310 return ui_claimed;
311 }
312
313 if (menu == mbar->selected) {
314 /* Open menu is positioned below menu bar entry */
315 spos.x = pos.x;
316 spos.y = mbar->rect.p1.y;
317
318 ui_menu_get_rect(menu, &spos, &rect);
319
320 claimed = ui_menu_pos_event(menu, &spos, event);
321 if (claimed == ui_claimed)
322 return ui_claimed;
323 }
324
325 pos.x += width;
326 menu = ui_menu_next(menu);
327 }
328
329 return ui_unclaimed;
330}
331
332/** Handle menu bar window unfocus notification.
333 *
334 * @param mbar Menu bar
335 */
336void ui_menu_bar_unfocus(ui_menu_bar_t *mbar)
337{
338 ui_menu_bar_select(mbar, NULL, NULL);
339}
340
341/** Destroy menu bar control.
342 *
343 * @param arg Argument (ui_menu_bar_t *)
344 */
345void ui_menu_bar_ctl_destroy(void *arg)
346{
347 ui_menu_bar_t *mbar = (ui_menu_bar_t *) arg;
348
349 ui_menu_bar_destroy(mbar);
350}
351
352/** Paint menu bar control.
353 *
354 * @param arg Argument (ui_menu_bar_t *)
355 * @return EOK on success or an error code
356 */
357errno_t ui_menu_bar_ctl_paint(void *arg)
358{
359 ui_menu_bar_t *mbar = (ui_menu_bar_t *) arg;
360
361 return ui_menu_bar_paint(mbar);
362}
363
364/** Handle menu bar control position event.
365 *
366 * @param arg Argument (ui_menu_bar_t *)
367 * @param pos_event Position event
368 * @return @c ui_claimed iff the event is claimed
369 */
370ui_evclaim_t ui_menu_bar_ctl_pos_event(void *arg, pos_event_t *event)
371{
372 ui_menu_bar_t *mbar = (ui_menu_bar_t *) arg;
373
374 return ui_menu_bar_pos_event(mbar, event);
375}
376
377/** Handle menu bar control window unfocus notification.
378 *
379 * @param arg Argument (ui_menu_bar_t *)
380 */
381void ui_menu_bar_ctl_unfocus(void *arg)
382{
383 ui_menu_bar_t *mbar = (ui_menu_bar_t *) arg;
384
385 ui_menu_bar_unfocus(mbar);
386}
387
388/** @}
389 */
Note: See TracBrowser for help on using the repository browser.