Ignore:
File:
1 edited

Legend:

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

    rd92b8e8f rff6e91b  
    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"
     
    6663
    6764static void ui_menu_popup_close(ui_popup_t *, void *);
    68 static void ui_menu_popup_kbd(ui_popup_t *, void *, kbd_event_t *);
    6965static 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 *);
    7166
    7267static ui_popup_cb_t ui_menu_popup_cb = {
    7368        .close = ui_menu_popup_close,
    74         .kbd = ui_menu_popup_kbd,
    7569        .pos = ui_menu_popup_pos
    7670};
     
    7872/** Create new menu.
    7973 *
    80  * @param parent Parent window
    8174 * @param mbar Menu bar
     75 * @param caption Caption
    8276 * @param rmenu Place to store pointer to new menu
    8377 * @return EOK on success, ENOMEM if out of memory
    8478 */
    85 errno_t ui_menu_create(ui_window_t *parent, ui_menu_t **rmenu)
     79errno_t ui_menu_create(ui_menu_bar_t *mbar, const char *caption,
     80    ui_menu_t **rmenu)
    8681{
    8782        ui_menu_t *menu;
     
    9186                return ENOMEM;
    9287
    93         menu->parent = parent;
     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);
    9496        list_initialize(&menu->entries);
    9597
     
    116118        }
    117119
     120        list_remove(&menu->lmenus);
    118121        free(menu->caption);
    119122        free(menu);
    120123}
    121124
    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;
     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;
    132165}
    133166
     
    141174    ui_menu_geom_t *geom)
    142175{
     176        ui_resource_t *res;
    143177        gfx_coord2_t edim;
    144178        gfx_coord_t frame_w;
    145179        gfx_coord_t frame_h;
    146         ui_resource_t *res;
    147 
    148         res = ui_window_get_res(menu->parent);
     180
     181        res = ui_window_get_res(menu->mbar->window);
    149182
    150183        if (res->textmode) {
     
    170203}
    171204
     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
    172219/** Get UI resource from menu.
    173220 *
     
    184231 * @param menu Menu
    185232 * @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)
     233 */
     234errno_t ui_menu_open(ui_menu_t *menu, gfx_rect_t *prect)
    189235{
    190236        ui_popup_t *popup = NULL;
     
    193239        gfx_coord2_t mpos;
    194240        errno_t rc;
    195 
    196         /* Select first entry */
    197         menu->selected = ui_menu_entry_first(menu);
    198241
    199242        /* Determine menu dimensions */
     
    206249        params.rect = geom.outer_rect;
    207250        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);
     251
     252        rc = ui_popup_create(menu->mbar->ui, menu->mbar->window, &params,
     253            &popup);
    212254        if (rc != EOK)
    213255                return rc;
     
    227269        ui_popup_destroy(menu->popup);
    228270        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;
    239271}
    240272
     
    407439                /* Press outside menu - close it */
    408440                if (event->type == POS_PRESS)
    409                         ui_menu_close_req(menu);
     441                        ui_menu_bar_select(menu->mbar, NULL, NULL);
    410442        }
    411443
    412444        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         }
    555445}
    556446
     
    564454        ui_menu_t *menu = (ui_menu_t *)arg;
    565455
    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);
     456        /* Close the menu */
     457        ui_menu_bar_select(menu->mbar, NULL, NULL);
    582458}
    583459
     
    593469        gfx_coord2_t spos;
    594470
    595         menu->idev_id = ui_popup_get_idev_id(menu->popup);
    596 
    597471        spos.x = 0;
    598472        spos.y = 0;
     
    600474}
    601475
    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 
    656476/** @}
    657477 */
Note: See TracChangeset for help on using the changeset viewer.