Ignore:
File:
1 edited

Legend:

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

    rff6e91b rd92b8e8f  
    11/*
    2  * Copyright (c) 2021 Jiri Svoboda
     2 * Copyright (c) 2024 Jiri Svoboda
    33 * All rights reserved.
    44 *
     
    3535
    3636#include <adt/list.h>
     37#include <ctype.h>
    3738#include <errno.h>
    3839#include <gfx/color.h>
     
    4344#include <stdlib.h>
    4445#include <str.h>
     46#include <uchar.h>
     47#include <ui/ui.h>
     48#include <ui/accel.h>
    4549#include <ui/control.h>
    4650#include <ui/paint.h>
     
    5054#include <ui/resource.h>
    5155#include <ui/window.h>
    52 #include "../private/menubar.h"
    5356#include "../private/menu.h"
    5457#include "../private/resource.h"
     
    6366
    6467static void ui_menu_popup_close(ui_popup_t *, void *);
     68static void ui_menu_popup_kbd(ui_popup_t *, void *, kbd_event_t *);
    6569static void ui_menu_popup_pos(ui_popup_t *, void *, pos_event_t *);
     70static void ui_menu_key_press_unmod(ui_menu_t *, kbd_event_t *);
    6671
    6772static ui_popup_cb_t ui_menu_popup_cb = {
    6873        .close = ui_menu_popup_close,
     74        .kbd = ui_menu_popup_kbd,
    6975        .pos = ui_menu_popup_pos
    7076};
     
    7278/** Create new menu.
    7379 *
     80 * @param parent Parent window
    7481 * @param mbar Menu bar
    75  * @param caption Caption
    7682 * @param rmenu Place to store pointer to new menu
    7783 * @return EOK on success, ENOMEM if out of memory
    7884 */
    79 errno_t ui_menu_create(ui_menu_bar_t *mbar, const char *caption,
    80     ui_menu_t **rmenu)
     85errno_t ui_menu_create(ui_window_t *parent, ui_menu_t **rmenu)
    8186{
    8287        ui_menu_t *menu;
     
    8691                return ENOMEM;
    8792
    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);
     93        menu->parent = parent;
    9694        list_initialize(&menu->entries);
    9795
     
    118116        }
    119117
    120         list_remove(&menu->lmenus);
    121118        free(menu->caption);
    122119        free(menu);
    123120}
    124121
    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  */
    130 ui_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  */
    146 ui_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  */
    162 const char *ui_menu_caption(ui_menu_t *menu)
    163 {
    164         return menu->caption;
     122/** Set menu callbacks.
     123 *
     124 * @param menu Menu
     125 * @param cb Callbacks
     126 * @param arg Callback argument
     127 */
     128void ui_menu_set_cb(ui_menu_t *menu, ui_menu_cb_t *cb, void *arg)
     129{
     130        menu->cb = cb;
     131        menu->arg = arg;
    165132}
    166133
     
    174141    ui_menu_geom_t *geom)
    175142{
    176         ui_resource_t *res;
    177143        gfx_coord2_t edim;
    178144        gfx_coord_t frame_w;
    179145        gfx_coord_t frame_h;
    180 
    181         res = ui_window_get_res(menu->mbar->window);
     146        ui_resource_t *res;
     147
     148        res = ui_window_get_res(menu->parent);
    182149
    183150        if (res->textmode) {
     
    203170}
    204171
    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  */
    211 void 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 
    219172/** Get UI resource from menu.
    220173 *
     
    231184 * @param menu Menu
    232185 * @param prect Parent rectangle around which the menu should be placed
    233  */
    234 errno_t ui_menu_open(ui_menu_t *menu, gfx_rect_t *prect)
     186 * @param idev_id Input device associated with the menu's seat
     187 */
     188errno_t ui_menu_open(ui_menu_t *menu, gfx_rect_t *prect, sysarg_t idev_id)
    235189{
    236190        ui_popup_t *popup = NULL;
     
    240194        errno_t rc;
    241195
     196        /* Select first entry */
     197        menu->selected = ui_menu_entry_first(menu);
     198
    242199        /* Determine menu dimensions */
    243200
     
    249206        params.rect = geom.outer_rect;
    250207        params.place = *prect;
    251 
    252         rc = ui_popup_create(menu->mbar->ui, menu->mbar->window, &params,
    253             &popup);
     208        params.idev_id = idev_id;
     209
     210        rc = ui_popup_create(ui_window_get_ui(menu->parent), menu->parent,
     211            &params, &popup);
    254212        if (rc != EOK)
    255213                return rc;
     
    269227        ui_popup_destroy(menu->popup);
    270228        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 */
     236bool ui_menu_is_open(ui_menu_t *menu)
     237{
     238        return menu->popup != NULL;
    271239}
    272240
     
    439407                /* Press outside menu - close it */
    440408                if (event->type == POS_PRESS)
    441                         ui_menu_bar_select(menu->mbar, NULL, NULL);
     409                        ui_menu_close_req(menu);
    442410        }
    443411
    444412        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 */
     421ui_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 */
     442void 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 */
     479void 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 */
     514static 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        }
    445555}
    446556
     
    454564        ui_menu_t *menu = (ui_menu_t *)arg;
    455565
    456         /* Close the menu */
    457         ui_menu_bar_select(menu->mbar, NULL, NULL);
     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 */
     576static 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);
    458582}
    459583
     
    469593        gfx_coord2_t spos;
    470594
     595        menu->idev_id = ui_popup_get_idev_id(menu->popup);
     596
    471597        spos.x = 0;
    472598        spos.y = 0;
     
    474600}
    475601
     602/** Send menu left event.
     603 *
     604 * @param menu Menu
     605 * @param idev_id Input device ID
     606 */
     607void 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 */
     618void 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 */
     628void 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 */
     640void 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 */
     651sysarg_t ui_menu_get_idev_id(ui_menu_t *menu)
     652{
     653        return menu->idev_id;
     654}
     655
    476656/** @}
    477657 */
Note: See TracChangeset for help on using the changeset viewer.