Changeset 59768c7 in mainline for uspace/lib/ui/src


Ignore:
Timestamp:
2022-01-10T19:29:00Z (4 years ago)
Author:
Jiri Svoboda <jiri@…>
Branches:
master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
Children:
5de852c
Parents:
9754ed2
git-author:
Jiri Svoboda <jiri@…> (2022-01-10 19:27:55)
git-committer:
Jiri Svoboda <jiri@…> (2022-01-10 19:29:00)
Message:

Menu control using F10, cursor keys, Enter, Escape

Location:
uspace/lib/ui/src
Files:
4 edited

Legend:

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

    r9754ed2 r59768c7  
    11/*
    2  * Copyright (c) 2021 Jiri Svoboda
     2 * Copyright (c) 2022 Jiri Svoboda
    33 * All rights reserved.
    44 *
     
    4747#include <ui/popup.h>
    4848#include <ui/menu.h>
     49#include <ui/menubar.h>
    4950#include <ui/menuentry.h>
    5051#include <ui/resource.h>
     
    6364
    6465static void ui_menu_popup_close(ui_popup_t *, void *);
     66static void ui_menu_popup_kbd(ui_popup_t *, void *, kbd_event_t *);
    6567static void ui_menu_popup_pos(ui_popup_t *, void *, pos_event_t *);
    6668
    6769static ui_popup_cb_t ui_menu_popup_cb = {
    6870        .close = ui_menu_popup_close,
     71        .kbd = ui_menu_popup_kbd,
    6972        .pos = ui_menu_popup_pos
    7073};
     
    149152
    150153        link = list_next(&cur->lmenus, &cur->mbar->menus);
     154        if (link == NULL)
     155                return NULL;
     156
     157        return list_get_instance(link, ui_menu_t, lmenus);
     158}
     159
     160/** Get last menu in menu bar.
     161 *
     162 * @param mbar Menu bar
     163 * @return Last menu or @c NULL if there is none
     164 */
     165ui_menu_t *ui_menu_last(ui_menu_bar_t *mbar)
     166{
     167        link_t *link;
     168
     169        link = list_last(&mbar->menus);
     170        if (link == NULL)
     171                return NULL;
     172
     173        return list_get_instance(link, ui_menu_t, lmenus);
     174}
     175
     176/** Get previous menu in menu bar.
     177 *
     178 * @param cur Current menu
     179 * @return Previous menu or @c NULL if @a cur is the fist one
     180 */
     181ui_menu_t *ui_menu_prev(ui_menu_t *cur)
     182{
     183        link_t *link;
     184
     185        link = list_prev(&cur->lmenus, &cur->mbar->menus);
    151186        if (link == NULL)
    152187                return NULL;
     
    240275        errno_t rc;
    241276
     277        /* Select first entry */
     278        menu->selected = ui_menu_entry_first(menu);
     279
    242280        /* Determine menu dimensions */
    243281
     
    269307        ui_popup_destroy(menu->popup);
    270308        menu->popup = NULL;
     309}
     310
     311/** Determine if menu is open.
     312 *
     313 * @param menu Menu
     314 * @return @c true iff menu is open
     315 */
     316bool ui_menu_is_open(ui_menu_t *menu)
     317{
     318        return menu->popup != NULL;
    271319}
    272320
     
    439487                /* Press outside menu - close it */
    440488                if (event->type == POS_PRESS)
    441                         ui_menu_bar_select(menu->mbar, NULL, NULL);
     489                        ui_menu_bar_deactivate(menu->mbar);
    442490        }
    443491
     
    454502        ui_menu_t *menu = (ui_menu_t *)arg;
    455503
    456         /* Close the menu */
    457         ui_menu_bar_select(menu->mbar, NULL, NULL);
     504        /* Deactivate menu bar, close menu */
     505        ui_menu_bar_deactivate(menu->mbar);
     506}
     507
     508/** Move one entry up.
     509 *
     510 * Non-selectable entries are skipped. If we are already at the top,
     511 * we wrap around.
     512 *
     513 * @param menu Menu
     514 */
     515void ui_menu_up(ui_menu_t *menu)
     516{
     517        gfx_coord2_t mpos;
     518        ui_menu_entry_t *nentry;
     519
     520        if (menu->selected == NULL)
     521                return;
     522
     523        nentry = ui_menu_entry_prev(menu->selected);
     524        if (nentry == NULL)
     525                nentry = ui_menu_entry_last(menu);
     526
     527        /* Need to find a selectable entry */
     528        while (!ui_menu_entry_selectable(nentry)) {
     529                nentry = ui_menu_entry_prev(nentry);
     530                if (nentry == NULL)
     531                        nentry = ui_menu_entry_last(menu);
     532
     533                /* Went completely around and found nothing? */
     534                if (nentry == menu->selected)
     535                        return;
     536        }
     537
     538        menu->selected = nentry;
     539
     540        mpos.x = 0;
     541        mpos.y = 0;
     542        (void) ui_menu_paint(menu, &mpos);
     543}
     544
     545/** Move one entry down.
     546 *
     547 * Non-selectable entries are skipped. If we are already at the bottom,
     548 * we wrap around.
     549 *
     550 * @param menu Menu
     551 */
     552void ui_menu_down(ui_menu_t *menu)
     553{
     554        gfx_coord2_t mpos;
     555        ui_menu_entry_t *nentry;
     556
     557        if (menu->selected == NULL)
     558                return;
     559
     560        nentry = ui_menu_entry_next(menu->selected);
     561        if (nentry == NULL)
     562                nentry = ui_menu_entry_first(menu);
     563
     564        /* Need to find a selectable entry */
     565        while (!ui_menu_entry_selectable(nentry)) {
     566                nentry = ui_menu_entry_next(nentry);
     567                if (nentry == NULL)
     568                        nentry = ui_menu_entry_first(menu);
     569
     570                /* Went completely around and found nothing? */
     571                if (nentry == menu->selected)
     572                        return;
     573        }
     574
     575        menu->selected = nentry;
     576
     577        mpos.x = 0;
     578        mpos.y = 0;
     579        (void) ui_menu_paint(menu, &mpos);
     580}
     581
     582/** Handle key press without modifiers in menu popup window.
     583 *
     584 * @param menu Menu
     585 * @param event Keyboard event
     586 */
     587static void ui_menu_key_press_unmod(ui_menu_t *menu, kbd_event_t *event)
     588{
     589        switch (event->key) {
     590        case KC_ESCAPE:
     591                ui_menu_bar_deactivate(menu->mbar);
     592                break;
     593        case KC_LEFT:
     594                ui_menu_bar_left(menu->mbar);
     595                break;
     596        case KC_RIGHT:
     597                ui_menu_bar_right(menu->mbar);
     598                break;
     599        case KC_UP:
     600                ui_menu_up(menu);
     601                break;
     602        case KC_DOWN:
     603                ui_menu_down(menu);
     604                break;
     605        case KC_ENTER:
     606                if (menu->selected != NULL)
     607                        ui_menu_entry_activate(menu->selected);
     608                break;
     609        default:
     610                break;
     611        }
     612}
     613
     614/** Handle keyboard event in menu popup window.
     615 *
     616 * @param popup Menu popup window
     617 * @param arg Argument (ui_menu_t *)
     618 * @param event Keyboard event
     619 */
     620static void ui_menu_popup_kbd(ui_popup_t *popup, void *arg, kbd_event_t *event)
     621{
     622        ui_menu_t *menu = (ui_menu_t *)arg;
     623
     624        if (event->type == KEY_PRESS && (event->mods &
     625            (KM_CTRL | KM_ALT | KM_SHIFT)) == 0) {
     626                ui_menu_key_press_unmod(menu, event);
     627        }
    458628}
    459629
  • uspace/lib/ui/src/menubar.c

    r9754ed2 r59768c7  
    11/*
    2  * Copyright (c) 2021 Jiri Svoboda
     2 * Copyright (c) 2022 Jiri Svoboda
    33 * All rights reserved.
    44 *
     
    6060static void ui_menu_bar_ctl_destroy(void *);
    6161static errno_t ui_menu_bar_ctl_paint(void *);
     62static ui_evclaim_t ui_menu_bar_ctl_kbd_event(void *, kbd_event_t *);
    6263static ui_evclaim_t ui_menu_bar_ctl_pos_event(void *, pos_event_t *);
    6364
     
    6667        .destroy = ui_menu_bar_ctl_destroy,
    6768        .paint = ui_menu_bar_ctl_paint,
    68         .pos_event = ui_menu_bar_ctl_pos_event,
     69        .kbd_event = ui_menu_bar_ctl_kbd_event,
     70        .pos_event = ui_menu_bar_ctl_pos_event
    6971};
    7072
     
    236238 *
    237239 * @param mbar Menu bar
    238  * @param rect Menu bar entry rectangle
    239240 * @param menu Menu to select (or deselect if selected) or @c NULL
    240  */
    241 void ui_menu_bar_select(ui_menu_bar_t *mbar, gfx_rect_t *rect,
    242     ui_menu_t *menu)
     241 * @param openup Open menu even if not currently open
     242 */
     243void ui_menu_bar_select(ui_menu_bar_t *mbar, ui_menu_t *menu, bool openup)
    243244{
    244245        ui_menu_t *old_menu;
     246        gfx_rect_t rect;
     247        bool was_open;
    245248
    246249        old_menu = mbar->selected;
     
    252255
    253256        /* Close previously open menu */
    254         if (old_menu != NULL)
     257        if (old_menu != NULL && ui_menu_is_open(old_menu)) {
     258                was_open = true;
    255259                (void) ui_menu_close(old_menu);
     260        } else {
     261                was_open = false;
     262        }
    256263
    257264        (void) ui_menu_bar_paint(mbar);
    258265
    259266        if (mbar->selected != NULL) {
    260                 (void) ui_menu_open(mbar->selected, rect);
    261         }
     267                ui_menu_bar_entry_rect(mbar, mbar->selected, &rect);
     268                if (openup || was_open) {
     269                        /*
     270                         * Open the newly selected menu if either
     271                         * the old menu was open or @a openup was
     272                         * specified.
     273                         */
     274                        (void) ui_menu_open(mbar->selected, &rect);
     275                }
     276        }
     277}
     278
     279/** Move one entry left.
     280 *
     281 * If the selected menu is open, the newly selected menu will be open
     282 * as well. If we are already at the first entry, we wrap around.
     283 *
     284 * @param mbar Menu bar
     285 */
     286void ui_menu_bar_left(ui_menu_bar_t *mbar)
     287{
     288        ui_menu_t *nmenu;
     289
     290        if (mbar->selected == NULL)
     291                return;
     292
     293        nmenu = ui_menu_prev(mbar->selected);
     294        if (nmenu == NULL)
     295                nmenu = ui_menu_last(mbar);
     296
     297        ui_menu_bar_select(mbar, nmenu, false);
     298}
     299
     300/** Move one entry right.
     301 *
     302 * If the selected menu is open, the newly selected menu will be open
     303 * as well. If we are already at the last entry, we wrap around.
     304 *
     305 * @param mbar Menu bar
     306 */
     307void ui_menu_bar_right(ui_menu_bar_t *mbar)
     308{
     309        ui_menu_t *nmenu;
     310
     311        if (mbar->selected == NULL)
     312                return;
     313
     314        nmenu = ui_menu_next(mbar->selected);
     315        if (nmenu == NULL)
     316                nmenu = ui_menu_first(mbar);
     317
     318        ui_menu_bar_select(mbar, nmenu, false);
     319}
     320
     321/** Handle menu bar key press without modifiers.
     322 *
     323 * @param mbar Menu bar
     324 * @param kbd_event Keyboard event
     325 * @return @c ui_claimed iff the event is claimed
     326 */
     327ui_evclaim_t ui_menu_bar_key_press_unmod(ui_menu_bar_t *mbar, kbd_event_t *event)
     328{
     329        gfx_rect_t rect;
     330
     331        if (event->key == KC_F10) {
     332                ui_menu_bar_activate(mbar);
     333                return ui_claimed;
     334        }
     335
     336        if (!mbar->active)
     337                return ui_unclaimed;
     338
     339        if (event->key == KC_ESCAPE) {
     340                ui_menu_bar_deactivate(mbar);
     341                return ui_claimed;
     342        }
     343
     344        if (event->key == KC_LEFT)
     345                ui_menu_bar_left(mbar);
     346
     347        if (event->key == KC_RIGHT)
     348                ui_menu_bar_right(mbar);
     349
     350        if (event->key == KC_ENTER || event->key == KC_DOWN) {
     351                if (mbar->selected != NULL && !ui_menu_is_open(mbar->selected)) {
     352                        ui_menu_bar_entry_rect(mbar, mbar->selected,
     353                            &rect);
     354                        ui_menu_open(mbar->selected, &rect);
     355                }
     356
     357                return ui_claimed;
     358        }
     359
     360        return ui_claimed;
     361}
     362
     363/** Handle menu bar keyboard event.
     364 *
     365 * @param mbar Menu bar
     366 * @param kbd_event Keyboard event
     367 * @return @c ui_claimed iff the event is claimed
     368 */
     369ui_evclaim_t ui_menu_bar_kbd_event(ui_menu_bar_t *mbar, kbd_event_t *event)
     370{
     371        if (event->type == KEY_PRESS && (event->mods &
     372            (KM_CTRL | KM_ALT | KM_SHIFT)) == 0) {
     373                ui_menu_bar_key_press_unmod(mbar, event);
     374                return ui_claimed;
     375        }
     376
     377        return ui_claimed;
    262378}
    263379
     
    304420                if (event->type == POS_PRESS &&
    305421                    gfx_pix_inside_rect(&ppos, &rect)) {
    306                         ui_menu_bar_select(mbar, &rect, menu);
     422                        mbar->active = true;
     423                        ui_menu_bar_select(mbar, menu, true);
    307424                        return ui_claimed;
    308425                }
     
    315432}
    316433
     434/** Handle menu bar position event.
     435 *
     436 * @param mbar Menu bar
     437 * @param menu Menu whose entry's rectangle is to be returned
     438 * @param rrect Place to store entry rectangle
     439 */
     440void ui_menu_bar_entry_rect(ui_menu_bar_t *mbar, ui_menu_t *menu,
     441    gfx_rect_t *rrect)
     442{
     443        ui_resource_t *res;
     444        gfx_coord2_t pos;
     445        gfx_rect_t rect;
     446        ui_menu_t *cur;
     447        const char *caption;
     448        gfx_coord_t width;
     449        gfx_coord_t hpad;
     450
     451        res = ui_window_get_res(mbar->window);
     452
     453        if (res->textmode) {
     454                hpad = menubar_hpad_text;
     455        } else {
     456                hpad = menubar_hpad;
     457        }
     458
     459        pos = mbar->rect.p0;
     460
     461        cur = ui_menu_first(mbar);
     462        while (cur != NULL) {
     463                caption = ui_menu_caption(cur);
     464                width = gfx_text_width(res->font, caption) + 2 * hpad;
     465
     466                rect.p0 = pos;
     467                rect.p1.x = rect.p0.x + width;
     468                rect.p1.y = mbar->rect.p1.y;
     469
     470                if (cur == menu) {
     471                        *rrect = rect;
     472                        return;
     473                }
     474
     475                pos.x += width;
     476                cur = ui_menu_next(cur);
     477        }
     478
     479        /* We should never get here */
     480        assert(false);
     481}
     482
     483/** Activate menu bar.
     484 *
     485 * @param mbar Menu bar
     486 */
     487void ui_menu_bar_activate(ui_menu_bar_t *mbar)
     488{
     489        if (mbar->active)
     490                return;
     491
     492        mbar->active = true;
     493        if (mbar->selected == NULL)
     494                mbar->selected = ui_menu_first(mbar);
     495
     496        (void) ui_menu_bar_paint(mbar);
     497}
     498
     499void ui_menu_bar_deactivate(ui_menu_bar_t *mbar)
     500{
     501        ui_menu_bar_select(mbar, NULL, false);
     502        mbar->active = false;
     503}
     504
    317505/** Destroy menu bar control.
    318506 *
     
    338526}
    339527
    340 /** Handle menu bar control position event.
     528/** Handle menu bar control keyboard event.
    341529 *
    342530 * @param arg Argument (ui_menu_bar_t *)
     
    344532 * @return @c ui_claimed iff the event is claimed
    345533 */
     534ui_evclaim_t ui_menu_bar_ctl_kbd_event(void *arg, kbd_event_t *event)
     535{
     536        ui_menu_bar_t *mbar = (ui_menu_bar_t *) arg;
     537
     538        return ui_menu_bar_kbd_event(mbar, event);
     539}
     540
     541/** Handle menu bar control position event.
     542 *
     543 * @param arg Argument (ui_menu_bar_t *)
     544 * @param pos_event Position event
     545 * @return @c ui_claimed iff the event is claimed
     546 */
    346547ui_evclaim_t ui_menu_bar_ctl_pos_event(void *arg, pos_event_t *event)
    347548{
  • uspace/lib/ui/src/menuentry.c

    r9754ed2 r59768c7  
    11/*
    2  * Copyright (c) 2021 Jiri Svoboda
     2 * Copyright (c) 2022 Jiri Svoboda
    33 * All rights reserved.
    44 *
     
    4545#include <ui/control.h>
    4646#include <ui/paint.h>
     47#include <ui/menubar.h>
    4748#include <ui/menuentry.h>
    4849#include <ui/window.h>
     
    177178}
    178179
     180/** Get last menu entry in menu.
     181 *
     182 * @param menu Menu
     183 * @return Last menu entry or @c NULL if there is none
     184 */
     185ui_menu_entry_t *ui_menu_entry_last(ui_menu_t *menu)
     186{
     187        link_t *link;
     188
     189        link = list_last(&menu->entries);
     190        if (link == NULL)
     191                return NULL;
     192
     193        return list_get_instance(link, ui_menu_entry_t, lentries);
     194}
     195
    179196/** Get next menu entry in menu.
    180197 *
     
    187204
    188205        link = list_next(&cur->lentries, &cur->menu->entries);
     206        if (link == NULL)
     207                return NULL;
     208
     209        return list_get_instance(link, ui_menu_entry_t, lentries);
     210}
     211
     212/** Get previous menu entry in menu.
     213 *
     214 * @param cur Current menu entry
     215 * @return Next menu entry or @c NULL if @a cur is the last one
     216 */
     217ui_menu_entry_t *ui_menu_entry_prev(ui_menu_entry_t *cur)
     218{
     219        link_t *link;
     220
     221        link = list_prev(&cur->lentries, &cur->menu->entries);
    189222        if (link == NULL)
    190223                return NULL;
     
    378411}
    379412
     413/** Determine if entry is selectable.
     414 *
     415 * @return @c true iff entry is selectable
     416 */
     417bool ui_menu_entry_selectable(ui_menu_entry_t *mentry)
     418{
     419        return !mentry->separator;
     420}
     421
    380422/** Handle button press in menu entry.
    381423 *
     
    407449        mentry->held = false;
    408450
    409         if (mentry->inside) {
    410                 /* Close menu */
    411                 ui_menu_bar_select(mentry->menu->mbar, NULL, NULL);
    412 
    413                 /* Call back */
    414                 ui_menu_entry_cb(mentry);
    415         }
     451        ui_menu_entry_activate(mentry);
     452}
     453
     454/** Activate menu entry.
     455 *
     456 * @param mentry Menu entry
     457 */
     458void ui_menu_entry_activate(ui_menu_entry_t *mentry)
     459{
     460        /* Deactivate menu bar, close menu */
     461        ui_menu_bar_deactivate(mentry->menu->mbar);
     462
     463        /* Call back */
     464        ui_menu_entry_cb(mentry);
    416465}
    417466
  • uspace/lib/ui/src/popup.c

    r9754ed2 r59768c7  
    11/*
    2  * Copyright (c) 2021 Jiri Svoboda
     2 * Copyright (c) 2022 Jiri Svoboda
    33 * All rights reserved.
    44 *
     
    3636#include <errno.h>
    3737#include <gfx/context.h>
    38 //#include <io/kbd_event.h>
     38#include <io/kbd_event.h>
    3939#include <io/pos_event.h>
    4040#include <mem.h>
     
    4747
    4848static void ui_popup_window_close(ui_window_t *, void *);
     49static void ui_popup_window_kbd(ui_window_t *, void *, kbd_event_t *);
    4950static void ui_popup_window_pos(ui_window_t *, void *, pos_event_t *);
    5051
    5152static ui_window_cb_t ui_popup_window_cb = {
    5253        .close = ui_popup_window_close,
     54        .kbd = ui_popup_window_kbd,
    5355        .pos = ui_popup_window_pos
    5456};
     
    200202}
    201203
     204/** Handle keyboard event in popup window.
     205 *
     206 * @param window Window
     207 * @param arg Argument (ui_popup_t *)
     208 * @param event Keyboard event
     209 */
     210static void ui_popup_window_kbd(ui_window_t *window, void *arg,
     211    kbd_event_t *event)
     212{
     213        ui_popup_t *popup = (ui_popup_t *)arg;
     214
     215        if (popup->cb != NULL && popup->cb->kbd != NULL)
     216                popup->cb->kbd(popup, popup->arg, event);
     217}
     218
    202219/** Handle position event in popup window.
    203220 *
Note: See TracChangeset for help on using the changeset viewer.