Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • uspace/lib/ui/src/menu.c

    rd92b8e8f rd7f82635  
    11/*
    2  * Copyright (c) 2024 Jiri Svoboda
     2 * Copyright (c) 2021 Jiri Svoboda
    33 * All rights reserved.
    44 *
     
    3535
    3636#include <adt/list.h>
    37 #include <ctype.h>
    3837#include <errno.h>
    3938#include <gfx/color.h>
     
    4443#include <stdlib.h>
    4544#include <str.h>
    46 #include <uchar.h>
    47 #include <ui/ui.h>
    48 #include <ui/accel.h>
    4945#include <ui/control.h>
    5046#include <ui/paint.h>
     
    5450#include <ui/resource.h>
    5551#include <ui/window.h>
     52#include "../private/menubar.h"
    5653#include "../private/menu.h"
    5754#include "../private/resource.h"
     
    6158        menu_frame_h = 4,
    6259        menu_frame_w_text = 2,
    63         menu_frame_h_text = 1,
    64         menu_frame_h_margin_text = 1
     60        menu_frame_h_text = 1
    6561};
    6662
    6763static void ui_menu_popup_close(ui_popup_t *, void *);
    68 static void ui_menu_popup_kbd(ui_popup_t *, void *, kbd_event_t *);
    6964static void ui_menu_popup_pos(ui_popup_t *, void *, pos_event_t *);
    70 static void ui_menu_key_press_unmod(ui_menu_t *, kbd_event_t *);
    7165
    7266static ui_popup_cb_t ui_menu_popup_cb = {
    7367        .close = ui_menu_popup_close,
    74         .kbd = ui_menu_popup_kbd,
    7568        .pos = ui_menu_popup_pos
    7669};
     
    7871/** Create new menu.
    7972 *
    80  * @param parent Parent window
    8173 * @param mbar Menu bar
     74 * @param caption Caption
    8275 * @param rmenu Place to store pointer to new menu
    8376 * @return EOK on success, ENOMEM if out of memory
    8477 */
    85 errno_t ui_menu_create(ui_window_t *parent, ui_menu_t **rmenu)
     78errno_t ui_menu_create(ui_menu_bar_t *mbar, const char *caption,
     79    ui_menu_t **rmenu)
    8680{
    8781        ui_menu_t *menu;
     
    9185                return ENOMEM;
    9286
    93         menu->parent = parent;
     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);
    9495        list_initialize(&menu->entries);
    9596
     
    116117        }
    117118
     119        list_remove(&menu->lmenus);
    118120        free(menu->caption);
    119121        free(menu);
    120122}
    121123
    122 /** Set menu callbacks.
    123  *
    124  * @param menu Menu
    125  * @param cb Callbacks
    126  * @param arg Callback argument
    127  */
    128 void ui_menu_set_cb(ui_menu_t *menu, ui_menu_cb_t *cb, void *arg)
    129 {
    130         menu->cb = cb;
    131         menu->arg = arg;
     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;
    132164}
    133165
     
    141173    ui_menu_geom_t *geom)
    142174{
     175        ui_resource_t *res;
    143176        gfx_coord2_t edim;
    144177        gfx_coord_t frame_w;
    145178        gfx_coord_t frame_h;
    146         ui_resource_t *res;
    147 
    148         res = ui_window_get_res(menu->parent);
     179
     180        res = ui_window_get_res(menu->mbar->window);
    149181
    150182        if (res->textmode) {
     
    170202}
    171203
     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
    172218/** Get UI resource from menu.
    173219 *
     
    184230 * @param menu Menu
    185231 * @param prect Parent rectangle around which the menu should be placed
    186  * @param idev_id Input device associated with the menu's seat
    187  */
    188 errno_t ui_menu_open(ui_menu_t *menu, gfx_rect_t *prect, sysarg_t idev_id)
     232 */
     233errno_t ui_menu_open(ui_menu_t *menu, gfx_rect_t *prect)
    189234{
    190235        ui_popup_t *popup = NULL;
     
    194239        errno_t rc;
    195240
    196         /* Select first entry */
    197         menu->selected = ui_menu_entry_first(menu);
    198 
    199241        /* Determine menu dimensions */
    200242
     
    206248        params.rect = geom.outer_rect;
    207249        params.place = *prect;
    208         params.idev_id = idev_id;
    209 
    210         rc = ui_popup_create(ui_window_get_ui(menu->parent), menu->parent,
    211             &params, &popup);
     250
     251        rc = ui_popup_create(menu->mbar->ui, menu->mbar->window, &params,
     252            &popup);
    212253        if (rc != EOK)
    213254                return rc;
     
    227268        ui_popup_destroy(menu->popup);
    228269        menu->popup = NULL;
    229 }
    230 
    231 /** Determine if menu is open.
    232  *
    233  * @param menu Menu
    234  * @return @c true iff menu is open
    235  */
    236 bool ui_menu_is_open(ui_menu_t *menu)
    237 {
    238         return menu->popup != NULL;
    239 }
    240 
    241 /** Paint menu.
    242  *
    243  * @param menu Menu
    244  * @param spos Starting position (top-left corner)
    245  * @return EOK on success or an error code
    246  */
    247 errno_t ui_menu_paint_bg_gfx(ui_menu_t *menu, gfx_coord2_t *spos)
    248 {
    249         ui_resource_t *res;
    250         ui_menu_geom_t geom;
    251         gfx_rect_t bg_rect;
    252         errno_t rc;
    253 
    254         res = ui_menu_get_res(menu);
    255         ui_menu_get_geom(menu, spos, &geom);
    256 
    257         /* Paint menu frame */
    258 
    259         rc = gfx_set_color(res->gc, res->wnd_face_color);
    260         if (rc != EOK)
    261                 goto error;
    262 
    263         rc = ui_paint_outset_frame(res, &geom.outer_rect, &bg_rect);
    264         if (rc != EOK)
    265                 goto error;
    266 
    267         /* Paint menu background */
    268 
    269         rc = gfx_set_color(res->gc, res->wnd_face_color);
    270         if (rc != EOK)
    271                 goto error;
    272 
    273         rc = gfx_fill_rect(res->gc, &bg_rect);
    274         if (rc != EOK)
    275                 goto error;
    276 
    277         return EOK;
    278 error:
    279         return rc;
    280 }
    281 
    282 /** Paint menu.
    283  *
    284  * @param menu Menu
    285  * @param spos Starting position (top-left corner)
    286  * @return EOK on success or an error code
    287  */
    288 errno_t ui_menu_paint_bg_text(ui_menu_t *menu, gfx_coord2_t *spos)
    289 {
    290         ui_resource_t *res;
    291         ui_menu_geom_t geom;
    292         gfx_rect_t rect;
    293         errno_t rc;
    294 
    295         res = ui_menu_get_res(menu);
    296         ui_menu_get_geom(menu, spos, &geom);
    297 
    298         /* Paint menu background */
    299 
    300         rc = gfx_set_color(res->gc, res->wnd_face_color);
    301         if (rc != EOK)
    302                 goto error;
    303 
    304         rc = gfx_fill_rect(res->gc, &geom.outer_rect);
    305         if (rc != EOK)
    306                 goto error;
    307 
    308         /* Paint menu box */
    309 
    310         rect = geom.outer_rect;
    311         rect.p0.x += menu_frame_h_margin_text;
    312         rect.p1.x -= menu_frame_h_margin_text;
    313 
    314         rc = ui_paint_text_box(res, &rect, ui_box_single, res->wnd_face_color);
    315         if (rc != EOK)
    316                 goto error;
    317 
    318         return EOK;
    319 error:
    320         return rc;
    321270}
    322271
     
    333282        ui_menu_entry_t *mentry;
    334283        ui_menu_geom_t geom;
     284        gfx_rect_t bg_rect;
    335285        errno_t rc;
    336286
     
    338288        ui_menu_get_geom(menu, spos, &geom);
    339289
    340         /* Paint menu frame and background */
    341         if (res->textmode)
    342                 rc = ui_menu_paint_bg_text(menu, spos);
    343         else
    344                 rc = ui_menu_paint_bg_gfx(menu, spos);
     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);
    345307        if (rc != EOK)
    346308                goto error;
     
    407369                /* Press outside menu - close it */
    408370                if (event->type == POS_PRESS)
    409                         ui_menu_close_req(menu);
     371                        ui_menu_bar_select(menu->mbar, NULL, NULL);
    410372        }
    411373
    412374        return ui_unclaimed;
    413 }
    414 
    415 /** Handle keyboard event in menu.
    416  *
    417  * @param menu Menu
    418  * @param event Keyboard event
    419  * @return ui_claimed iff the event was claimed
    420  */
    421 ui_evclaim_t ui_menu_kbd_event(ui_menu_t *menu, kbd_event_t *event)
    422 {
    423         if (event->type == KEY_PRESS && (event->mods &
    424             (KM_CTRL | KM_ALT | KM_SHIFT)) == 0) {
    425                 ui_menu_key_press_unmod(menu, event);
    426         }
    427 
    428         if (event->type == KEY_PRESS && (event->mods & KM_ALT) != 0 &&
    429             (event->mods & (KM_CTRL | KM_SHIFT)) == 0 && event->c != '\0')
    430                 ui_menu_press_accel(menu, event->c, event->kbd_id);
    431 
    432         return ui_claimed;
    433 }
    434 
    435 /** Move one entry up.
    436  *
    437  * Non-selectable entries are skipped. If we are already at the top,
    438  * we wrap around.
    439  *
    440  * @param menu Menu
    441  */
    442 void ui_menu_up(ui_menu_t *menu)
    443 {
    444         gfx_coord2_t mpos;
    445         ui_menu_entry_t *nentry;
    446 
    447         if (menu->selected == NULL)
    448                 return;
    449 
    450         nentry = ui_menu_entry_prev(menu->selected);
    451         if (nentry == NULL)
    452                 nentry = ui_menu_entry_last(menu);
    453 
    454         /* Need to find a selectable entry */
    455         while (!ui_menu_entry_selectable(nentry)) {
    456                 nentry = ui_menu_entry_prev(nentry);
    457                 if (nentry == NULL)
    458                         nentry = ui_menu_entry_last(menu);
    459 
    460                 /* Went completely around and found nothing? */
    461                 if (nentry == menu->selected)
    462                         return;
    463         }
    464 
    465         menu->selected = nentry;
    466 
    467         mpos.x = 0;
    468         mpos.y = 0;
    469         (void) ui_menu_paint(menu, &mpos);
    470 }
    471 
    472 /** Move one entry down.
    473  *
    474  * Non-selectable entries are skipped. If we are already at the bottom,
    475  * we wrap around.
    476  *
    477  * @param menu Menu
    478  */
    479 void ui_menu_down(ui_menu_t *menu)
    480 {
    481         gfx_coord2_t mpos;
    482         ui_menu_entry_t *nentry;
    483 
    484         if (menu->selected == NULL)
    485                 return;
    486 
    487         nentry = ui_menu_entry_next(menu->selected);
    488         if (nentry == NULL)
    489                 nentry = ui_menu_entry_first(menu);
    490 
    491         /* Need to find a selectable entry */
    492         while (!ui_menu_entry_selectable(nentry)) {
    493                 nentry = ui_menu_entry_next(nentry);
    494                 if (nentry == NULL)
    495                         nentry = ui_menu_entry_first(menu);
    496 
    497                 /* Went completely around and found nothing? */
    498                 if (nentry == menu->selected)
    499                         return;
    500         }
    501 
    502         menu->selected = nentry;
    503 
    504         mpos.x = 0;
    505         mpos.y = 0;
    506         (void) ui_menu_paint(menu, &mpos);
    507 }
    508 
    509 /** Handle key press without modifiers in menu popup window.
    510  *
    511  * @param menu Menu
    512  * @param event Keyboard event
    513  */
    514 static void ui_menu_key_press_unmod(ui_menu_t *menu, kbd_event_t *event)
    515 {
    516         ui_menu_entry_t *mentry;
    517         char32_t c;
    518 
    519         switch (event->key) {
    520         case KC_ESCAPE:
    521                 ui_menu_close_req(menu);
    522                 break;
    523         case KC_LEFT:
    524                 ui_menu_left(menu, event->kbd_id);
    525                 break;
    526         case KC_RIGHT:
    527                 ui_menu_right(menu, event->kbd_id);
    528                 break;
    529         case KC_UP:
    530                 ui_menu_up(menu);
    531                 break;
    532         case KC_DOWN:
    533                 ui_menu_down(menu);
    534                 break;
    535         case KC_ENTER:
    536                 if (menu->selected != NULL &&
    537                     !ui_menu_entry_is_disabled(menu->selected))
    538                         ui_menu_entry_activate(menu->selected);
    539                 break;
    540         default:
    541                 if (event->c != '\0') {
    542                         mentry = ui_menu_entry_first(menu);
    543                         while (mentry != NULL) {
    544                                 c = ui_menu_entry_get_accel(mentry);
    545                                 if (c == (char32_t)tolower(event->c) &&
    546                                     !ui_menu_entry_is_disabled(mentry)) {
    547                                         ui_menu_entry_activate(mentry);
    548                                         break;
    549                                 }
    550                                 mentry = ui_menu_entry_next(mentry);
    551                         }
    552                 }
    553                 break;
    554         }
    555375}
    556376
     
    564384        ui_menu_t *menu = (ui_menu_t *)arg;
    565385
    566         /* Forward close request to caller */
    567         ui_menu_close_req(menu);
    568 }
    569 
    570 /** Handle keyboard event in menu popup window.
    571  *
    572  * @param popup Menu popup window
    573  * @param arg Argument (ui_menu_t *)
    574  * @param event Keyboard event
    575  */
    576 static void ui_menu_popup_kbd(ui_popup_t *popup, void *arg, kbd_event_t *event)
    577 {
    578         ui_menu_t *menu = (ui_menu_t *)arg;
    579 
    580         menu->idev_id = ui_popup_get_idev_id(menu->popup);
    581         ui_menu_kbd_event(menu, event);
     386        /* Close the menu */
     387        ui_menu_bar_select(menu->mbar, NULL, NULL);
    582388}
    583389
     
    593399        gfx_coord2_t spos;
    594400
    595         menu->idev_id = ui_popup_get_idev_id(menu->popup);
    596 
    597401        spos.x = 0;
    598402        spos.y = 0;
     
    600404}
    601405
    602 /** Send menu left event.
    603  *
    604  * @param menu Menu
    605  * @param idev_id Input device ID
    606  */
    607 void ui_menu_left(ui_menu_t *menu, sysarg_t idev_id)
    608 {
    609         if (menu->cb != NULL && menu->cb->left != NULL)
    610                 menu->cb->left(menu, menu->arg, idev_id);
    611 }
    612 
    613 /** Send menu right event.
    614  *
    615  * @param menu Menu
    616  * @param idev_id Input device ID
    617  */
    618 void ui_menu_right(ui_menu_t *menu, sysarg_t idev_id)
    619 {
    620         if (menu->cb != NULL && menu->cb->right != NULL)
    621                 menu->cb->right(menu, menu->arg, idev_id);
    622 }
    623 
    624 /** Send menu close request event.
    625  *
    626  * @param menu Menu
    627  */
    628 void ui_menu_close_req(ui_menu_t *menu)
    629 {
    630         if (menu->cb != NULL && menu->cb->close_req != NULL)
    631                 menu->cb->close_req(menu, menu->arg);
    632 }
    633 
    634 /** Send menu accelerator key press event.
    635  *
    636  * @param menu Menu
    637  * @param c Character
    638  * @param kbd_id Keyboard ID
    639  */
    640 void ui_menu_press_accel(ui_menu_t *menu, char32_t c, sysarg_t kbd_id)
    641 {
    642         if (menu->cb != NULL && menu->cb->press_accel != NULL)
    643                 menu->cb->press_accel(menu, menu->arg, c, kbd_id);
    644 }
    645 
    646 /** Get ID of last device that input event.
    647  *
    648  * @param menu Menu
    649  * @return Input device ID
    650  */
    651 sysarg_t ui_menu_get_idev_id(ui_menu_t *menu)
    652 {
    653         return menu->idev_id;
    654 }
    655 
    656406/** @}
    657407 */
Note: See TracChangeset for help on using the changeset viewer.