Ignore:
File:
1 edited

Legend:

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

    rec50d65e 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>
     
    4645#include <ui/control.h>
    4746#include <ui/paint.h>
     47#include <ui/menu.h>
    4848#include <ui/menubar.h>
    49 #include <ui/menudd.h>
    50 #include <ui/wdecor.h>
    5149#include <ui/window.h>
    5250#include "../private/menubar.h"
    5351#include "../private/resource.h"
    54 #include "../private/wdecor.h"
    55 #include "../private/window.h"
    5652
    5753enum {
     
    6460static void ui_menu_bar_ctl_destroy(void *);
    6561static errno_t ui_menu_bar_ctl_paint(void *);
    66 static ui_evclaim_t ui_menu_bar_ctl_kbd_event(void *, kbd_event_t *);
    6762static ui_evclaim_t ui_menu_bar_ctl_pos_event(void *, pos_event_t *);
    68 static void ui_menu_bar_activate_ev(ui_menu_bar_t *);
    69 static void ui_menu_bar_deactivate_ev(ui_menu_bar_t *);
    7063
    7164/** Menu bar control ops */
     
    7366        .destroy = ui_menu_bar_ctl_destroy,
    7467        .paint = ui_menu_bar_ctl_paint,
    75         .kbd_event = ui_menu_bar_ctl_kbd_event,
    76         .pos_event = ui_menu_bar_ctl_pos_event
     68        .pos_event = ui_menu_bar_ctl_pos_event,
    7769};
    7870
     
    10193        mbar->ui = ui;
    10294        mbar->window = window;
    103         list_initialize(&mbar->menudds);
    104 
    105         if (window->mbar == NULL)
    106                 window->mbar = mbar;
    107 
     95        list_initialize(&mbar->menus);
    10896        *rmbar = mbar;
    10997        return EOK;
     
    116104void ui_menu_bar_destroy(ui_menu_bar_t *mbar)
    117105{
    118         ui_menu_dd_t *mdd;
     106        ui_menu_t *menu;
    119107
    120108        if (mbar == NULL)
    121109                return;
    122110
    123         if (mbar->window->mbar == mbar)
    124                 mbar->window->mbar = NULL;
    125 
    126         /* Destroy menu drop-downs */
    127         mdd = ui_menu_dd_first(mbar);
    128         while (mdd != NULL) {
    129                 ui_menu_dd_destroy(mdd);
    130                 mdd = ui_menu_dd_first(mbar);
     111        /* Destroy menus */
     112        menu = ui_menu_first(mbar);
     113        while (menu != NULL) {
     114                ui_menu_destroy(menu);
     115                menu = ui_menu_first(mbar);
    131116        }
    132117
     
    135120}
    136121
    137 /** Set menu bar callbacks.
    138  *
    139  * @param mbar Menu bar
    140  * @param cb Callbacks
    141  * @param arg Callback argument
    142  */
    143 void ui_menu_bar_set_cb(ui_menu_bar_t *mbar, ui_menu_bar_cb_t *cb, void *arg)
    144 {
    145         mbar->cb = cb;
    146         mbar->arg = arg;
    147 }
    148 
    149122/** Get base control from menu bar.
    150123 *
     
    175148{
    176149        ui_resource_t *res;
    177         ui_text_fmt_t fmt;
     150        gfx_text_fmt_t fmt;
    178151        gfx_coord2_t pos;
    179152        gfx_coord2_t tpos;
    180153        gfx_rect_t rect;
    181154        gfx_color_t *bg_color;
    182         ui_menu_dd_t *mdd;
     155        ui_menu_t *menu;
    183156        const char *caption;
    184157        gfx_coord_t width;
     
    209182        pos = mbar->rect.p0;
    210183
    211         ui_text_fmt_init(&fmt);
    212         fmt.font = res->font;
     184        gfx_text_fmt_init(&fmt);
    213185        fmt.halign = gfx_halign_left;
    214186        fmt.valign = gfx_valign_top;
    215187
    216         mdd = ui_menu_dd_first(mbar);
    217         while (mdd != NULL) {
    218                 caption = ui_menu_dd_caption(mdd);
    219                 width = ui_text_width(res->font, caption) + 2 * hpad;
     188        menu = ui_menu_first(mbar);
     189        while (menu != NULL) {
     190                caption = ui_menu_caption(menu);
     191                width = gfx_text_width(res->font, caption) + 2 * hpad;
    220192                tpos.x = pos.x + hpad;
    221193                tpos.y = pos.y + vpad;
     
    225197                rect.p1.y = mbar->rect.p1.y;
    226198
    227                 if (mdd == mbar->selected) {
     199                if (menu == mbar->selected) {
    228200                        fmt.color = res->wnd_sel_text_color;
    229                         fmt.hgl_color = res->wnd_sel_text_hgl_color;
    230201                        bg_color = res->wnd_sel_text_bg_color;
    231202                } else {
    232203                        fmt.color = res->wnd_text_color;
    233                         fmt.hgl_color = res->wnd_text_hgl_color;
    234204                        bg_color = res->wnd_face_color;
    235205                }
     
    243213                        goto error;
    244214
    245                 rc = ui_paint_text(&tpos, &fmt, caption);
     215                rc = gfx_puttext(res->font, &tpos, &fmt, caption);
    246216                if (rc != EOK)
    247217                        goto error;
    248218
    249219                pos.x += width;
    250                 mdd = ui_menu_dd_next(mdd);
     220                menu = ui_menu_next(menu);
    251221        }
    252222
     
    266236 *
    267237 * @param mbar Menu bar
    268  * @param mdd Menu drop-down to select (or deselect if selected) or @c NULL
    269  * @param openup Open menu even if not currently open
    270  * @param idev_id Input device ID associated with the selecting seat
    271  */
    272 void ui_menu_bar_select(ui_menu_bar_t *mbar, ui_menu_dd_t *mdd, bool openup,
    273     sysarg_t idev_id)
    274 {
    275         ui_menu_dd_t *old_mdd;
    276         gfx_rect_t rect;
    277         bool was_open;
    278 
    279         old_mdd = mbar->selected;
    280 
    281         mbar->selected = mdd;
    282 
    283         /* Close previously open menu drop-down */
    284         if (old_mdd != NULL && ui_menu_dd_is_open(old_mdd)) {
    285                 was_open = true;
    286                 (void) ui_menu_dd_close(old_mdd);
    287         } else {
    288                 was_open = false;
    289         }
     238 * @param rect Menu bar entry rectangle
     239 * @param menu Menu to select (or deselect if selected) or @c NULL
     240 */
     241void ui_menu_bar_select(ui_menu_bar_t *mbar, gfx_rect_t *rect,
     242    ui_menu_t *menu)
     243{
     244        ui_menu_t *old_menu;
     245
     246        old_menu = mbar->selected;
     247
     248        if (mbar->selected != menu)
     249                mbar->selected = menu;
     250        else
     251                mbar->selected = NULL;
     252
     253        /* Close previously open menu */
     254        if (old_menu != NULL)
     255                (void) ui_menu_close(old_menu);
    290256
    291257        (void) ui_menu_bar_paint(mbar);
    292258
    293259        if (mbar->selected != NULL) {
    294                 ui_menu_bar_entry_rect(mbar, mbar->selected, &rect);
    295                 if (openup || was_open) {
    296                         /*
    297                          * Open the newly selected menu drop-down if either
    298                          * the old menu drop-down was open or @a openup was
    299                          * specified.
    300                          */
    301                         (void) ui_menu_dd_open(mbar->selected, &rect, idev_id);
    302                 }
    303 
    304                 if (!mbar->active)
    305                         ui_menu_bar_activate_ev(mbar);
    306                 mbar->active = true;
    307         } else {
    308                 if (mbar->active)
    309                         ui_menu_bar_deactivate_ev(mbar);
    310                 mbar->active = false;
    311         }
    312 }
    313 
    314 /** Select first drop-down.
    315  *
    316  * @param mbar Menu bar
    317  * @param openup @c true to open drop-down if it was not open
    318  * @param idev_id Input device ID
    319  */
    320 void ui_menu_bar_select_first(ui_menu_bar_t *mbar, bool openup,
    321     sysarg_t idev_id)
    322 {
    323         ui_menu_dd_t *mdd;
    324 
    325         mdd = ui_menu_dd_first(mbar);
    326         ui_menu_bar_select(mbar, mdd, openup, idev_id);
    327 }
    328 
    329 /** Select last drop-down.
    330  *
    331  * @param mbar Menu bar
    332  * @param openup @c true to open drop-down if it was not open
    333  * @param idev_id Input device ID
    334  */
    335 void ui_menu_bar_select_last(ui_menu_bar_t *mbar, bool openup,
    336     sysarg_t idev_id)
    337 {
    338         ui_menu_dd_t *mdd;
    339 
    340         mdd = ui_menu_dd_last(mbar);
    341         ui_menu_bar_select(mbar, mdd, openup, idev_id);
    342 }
    343 
    344 /** Select system menu.
    345  *
    346  * @param mbar Menu bar
    347  * @param openup @c true to open drop-down if it was not open
    348  * @param idev_id Input device ID
    349  */
    350 void ui_menu_bar_select_sysmenu(ui_menu_bar_t *mbar, bool openup,
    351     sysarg_t idev_id)
    352 {
    353         ui_wdecor_sysmenu_hdl_set_active(mbar->window->wdecor, true);
    354 
    355         if (openup)
    356                 ui_window_send_sysmenu(mbar->window, idev_id);
    357 }
    358 
    359 /** Move one entry left.
    360  *
    361  * If the selected menu is open, the newly selected menu will be open
    362  * as well. If we are already at the first entry, we wrap around.
    363  *
    364  * @param mbar Menu bar
    365  * @param idev_id Input device ID
    366  */
    367 void ui_menu_bar_left(ui_menu_bar_t *mbar, sysarg_t idev_id)
    368 {
    369         ui_menu_dd_t *nmdd;
    370         bool sel_sysmenu = false;
    371         bool was_open;
    372 
    373         if (mbar->selected == NULL)
    374                 return;
    375 
    376         nmdd = ui_menu_dd_prev(mbar->selected);
    377         if (nmdd == NULL) {
    378                 if ((mbar->window->wdecor->style & ui_wds_sysmenu_hdl) != 0) {
    379                         sel_sysmenu = true;
    380                 } else {
    381                         nmdd = ui_menu_dd_last(mbar);
    382                 }
    383         }
    384 
    385         was_open = mbar->selected != NULL &&
    386             ui_menu_dd_is_open(mbar->selected);
    387 
    388         if (nmdd != mbar->selected)
    389                 ui_menu_bar_select(mbar, nmdd, false, idev_id);
    390 
    391         /*
    392          * Only open sysmenu *after* closing the previous menu, avoid
    393          * having multiple popup windows at the same time.
    394          */
    395         if (sel_sysmenu)
    396                 ui_menu_bar_select_sysmenu(mbar, was_open, idev_id);
    397 }
    398 
    399 /** Move one entry right.
    400  *
    401  * If the selected menu is open, the newly selected menu will be open
    402  * as well. If we are already at the last entry, we wrap around.
    403  *
    404  * @param mbar Menu bar
    405  * @param idev_id Input device ID
    406  */
    407 void ui_menu_bar_right(ui_menu_bar_t *mbar, sysarg_t idev_id)
    408 {
    409         ui_menu_dd_t *nmdd;
    410         bool sel_sysmenu = false;
    411         bool was_open;
    412 
    413         if (mbar->selected == NULL)
    414                 return;
    415 
    416         nmdd = ui_menu_dd_next(mbar->selected);
    417         if (nmdd == NULL) {
    418                 if ((mbar->window->wdecor->style & ui_wds_sysmenu_hdl) != 0) {
    419                         sel_sysmenu = true;
    420                 } else {
    421                         nmdd = ui_menu_dd_first(mbar);
    422                 }
    423         }
    424 
    425         was_open = mbar->selected != NULL &&
    426             ui_menu_dd_is_open(mbar->selected);
    427 
    428         if (nmdd != mbar->selected)
    429                 ui_menu_bar_select(mbar, nmdd, false, idev_id);
    430 
    431         /*
    432          * Only open sysmenu *after* closing the previous menu, avoid
    433          * having multiple popup windows at the same time.
    434          */
    435         if (sel_sysmenu)
    436                 ui_menu_bar_select_sysmenu(mbar, was_open, idev_id);
    437 }
    438 
    439 /** Handle menu bar key press without modifiers.
    440  *
    441  * @param mbar Menu bar
    442  * @param kbd_event Keyboard event
    443  * @return @c ui_claimed iff the event is claimed
    444  */
    445 ui_evclaim_t ui_menu_bar_key_press_unmod(ui_menu_bar_t *mbar, kbd_event_t *event)
    446 {
    447         gfx_rect_t rect;
    448 
    449         if (event->key == KC_F10) {
    450                 ui_menu_bar_activate(mbar);
    451                 return ui_claimed;
    452         }
    453 
    454         if (!mbar->active)
    455                 return ui_unclaimed;
    456 
    457         if (event->key == KC_ESCAPE) {
    458                 ui_menu_bar_deactivate(mbar);
    459                 return ui_claimed;
    460         }
    461 
    462         if (event->key == KC_LEFT)
    463                 ui_menu_bar_left(mbar, event->kbd_id);
    464 
    465         if (event->key == KC_RIGHT)
    466                 ui_menu_bar_right(mbar, event->kbd_id);
    467 
    468         if (event->key == KC_ENTER || event->key == KC_DOWN) {
    469                 if (mbar->selected != NULL &&
    470                     !ui_menu_dd_is_open(mbar->selected)) {
    471                         ui_menu_bar_entry_rect(mbar, mbar->selected,
    472                             &rect);
    473                         ui_menu_dd_open(mbar->selected, &rect, event->kbd_id);
    474                 }
    475 
    476                 return ui_claimed;
    477         }
    478 
    479         if (event->c != '\0' && !ui_menu_dd_is_open(mbar->selected)) {
    480                 /* Check if it is an accelerator. */
    481                 ui_menu_bar_press_accel(mbar, event->c, event->kbd_id);
    482         }
    483 
    484         return ui_claimed;
    485 }
    486 
    487 /** Handle menu bar keyboard event.
    488  *
    489  * @param mbar Menu bar
    490  * @param kbd_event Keyboard event
    491  * @return @c ui_claimed iff the event is claimed
    492  */
    493 ui_evclaim_t ui_menu_bar_kbd_event(ui_menu_bar_t *mbar, kbd_event_t *event)
    494 {
    495         if ((event->mods & KM_ALT) != 0 &&
    496             (event->mods & (KM_CTRL | KM_SHIFT)) == 0 && event->c != '\0') {
    497                 /* Check if it is an accelerator. */
    498                 ui_menu_bar_press_accel(mbar, event->c, event->kbd_id);
    499         }
    500 
    501         if (event->type == KEY_PRESS && (event->mods &
    502             (KM_CTRL | KM_ALT | KM_SHIFT)) == 0) {
    503                 return ui_menu_bar_key_press_unmod(mbar, event);
    504         }
    505 
    506         if (mbar->active)
    507                 return ui_claimed;
    508 
    509         return ui_unclaimed;
    510 }
    511 
    512 /** Accelerator key press.
    513  *
    514  * If @a c matches an accelerator key, open the respective menu.
    515  *
    516  * @param mbar Menu bar
    517  * @param c Character
    518  * @param kbd_id Keyboard ID
    519  */
    520 void ui_menu_bar_press_accel(ui_menu_bar_t *mbar, char32_t c, sysarg_t kbd_id)
    521 {
    522         ui_menu_dd_t *mdd;
    523         char32_t maccel;
    524 
    525         mdd = ui_menu_dd_first(mbar);
    526         while (mdd != NULL) {
    527                 maccel = ui_menu_dd_get_accel(mdd);
    528                 if ((char32_t)tolower(c) == maccel) {
    529                         ui_menu_bar_select(mbar, mdd, true, kbd_id);
    530                         return;
    531                 }
    532 
    533                 mdd = ui_menu_dd_next(mdd);
     260                (void) ui_menu_open(mbar->selected, rect);
    534261        }
    535262}
     
    546273        gfx_coord2_t pos;
    547274        gfx_rect_t rect;
    548         ui_menu_dd_t *mdd;
     275        ui_menu_t *menu;
    549276        const char *caption;
    550277        gfx_coord_t width;
    551278        gfx_coord_t hpad;
    552279        gfx_coord2_t ppos;
    553         sysarg_t pos_id;
    554280
    555281        res = ui_window_get_res(mbar->window);
     
    565291
    566292        pos = mbar->rect.p0;
    567         pos_id = event->pos_id;
    568 
    569         mdd = ui_menu_dd_first(mbar);
    570         while (mdd != NULL) {
    571                 caption = ui_menu_dd_caption(mdd);
    572                 width = ui_text_width(res->font, caption) + 2 * hpad;
     293
     294        menu = ui_menu_first(mbar);
     295        while (menu != NULL) {
     296                caption = ui_menu_caption(menu);
     297                width = gfx_text_width(res->font, caption) + 2 * hpad;
    573298
    574299                rect.p0 = pos;
     
    579304                if (event->type == POS_PRESS &&
    580305                    gfx_pix_inside_rect(&ppos, &rect)) {
    581                         mbar->active = true;
    582 
    583                         /* Open the menu, if not already open. */
    584                         if (mdd != mbar->selected)
    585                                 ui_menu_bar_select(mbar, mdd, true, pos_id);
    586 
     306                        ui_menu_bar_select(mbar, &rect, menu);
    587307                        return ui_claimed;
    588308                }
    589309
    590310                pos.x += width;
    591                 mdd = ui_menu_dd_next(mdd);
     311                menu = ui_menu_next(menu);
    592312        }
    593313
    594314        return ui_unclaimed;
    595 }
    596 
    597 /** Handle menu bar position event.
    598  *
    599  * @param mbar Menu bar
    600  * @param mdd Menu drop-down whose entry's rectangle is to be returned
    601  * @param rrect Place to store entry rectangle
    602  */
    603 void ui_menu_bar_entry_rect(ui_menu_bar_t *mbar, ui_menu_dd_t *mdd,
    604     gfx_rect_t *rrect)
    605 {
    606         ui_resource_t *res;
    607         gfx_coord2_t pos;
    608         gfx_rect_t rect;
    609         ui_menu_dd_t *cur;
    610         const char *caption;
    611         gfx_coord_t width;
    612         gfx_coord_t hpad;
    613 
    614         res = ui_window_get_res(mbar->window);
    615 
    616         if (res->textmode) {
    617                 hpad = menubar_hpad_text;
    618         } else {
    619                 hpad = menubar_hpad;
    620         }
    621 
    622         pos = mbar->rect.p0;
    623 
    624         cur = ui_menu_dd_first(mbar);
    625         while (cur != NULL) {
    626                 caption = ui_menu_dd_caption(cur);
    627                 width = ui_text_width(res->font, caption) + 2 * hpad;
    628 
    629                 rect.p0 = pos;
    630                 rect.p1.x = rect.p0.x + width;
    631                 rect.p1.y = mbar->rect.p1.y;
    632 
    633                 if (cur == mdd) {
    634                         *rrect = rect;
    635                         return;
    636                 }
    637 
    638                 pos.x += width;
    639                 cur = ui_menu_dd_next(cur);
    640         }
    641 
    642         /* We should never get here */
    643         assert(false);
    644 }
    645 
    646 /** Activate menu bar.
    647  *
    648  * @param mbar Menu bar
    649  */
    650 void ui_menu_bar_activate(ui_menu_bar_t *mbar)
    651 {
    652         if (mbar->active)
    653                 return;
    654 
    655         mbar->active = true;
    656         if (mbar->selected == NULL)
    657                 mbar->selected = ui_menu_dd_first(mbar);
    658 
    659         (void) ui_menu_bar_paint(mbar);
    660         ui_menu_bar_activate_ev(mbar);
    661 }
    662 
    663 /** Deactivate menu bar.
    664  *
    665  * @param mbar Menu bar
    666  */
    667 void ui_menu_bar_deactivate(ui_menu_bar_t *mbar)
    668 {
    669         ui_menu_bar_select(mbar, NULL, false, 0);
    670         ui_menu_bar_deactivate_ev(mbar);
    671315}
    672316
     
    694338}
    695339
    696 /** Handle menu bar control keyboard event.
     340/** Handle menu bar control position event.
    697341 *
    698342 * @param arg Argument (ui_menu_bar_t *)
     
    700344 * @return @c ui_claimed iff the event is claimed
    701345 */
    702 ui_evclaim_t ui_menu_bar_ctl_kbd_event(void *arg, kbd_event_t *event)
     346ui_evclaim_t ui_menu_bar_ctl_pos_event(void *arg, pos_event_t *event)
    703347{
    704348        ui_menu_bar_t *mbar = (ui_menu_bar_t *) arg;
    705349
    706         return ui_menu_bar_kbd_event(mbar, event);
    707 }
    708 
    709 /** Handle menu bar control position event.
    710  *
    711  * @param arg Argument (ui_menu_bar_t *)
    712  * @param pos_event Position event
    713  * @return @c ui_claimed iff the event is claimed
    714  */
    715 ui_evclaim_t ui_menu_bar_ctl_pos_event(void *arg, pos_event_t *event)
    716 {
    717         ui_menu_bar_t *mbar = (ui_menu_bar_t *) arg;
    718 
    719350        return ui_menu_bar_pos_event(mbar, event);
    720351}
    721352
    722 /** Send menu bar activate event.
    723  *
    724  * @param mbar Menu bar
    725  */
    726 static void ui_menu_bar_activate_ev(ui_menu_bar_t *mbar)
    727 {
    728         if (mbar->cb != NULL && mbar->cb->activate != NULL)
    729                 mbar->cb->activate(mbar, mbar->arg);
    730 }
    731 
    732 /** Send menu bar deactivate event.
    733  *
    734  * @param mbar Menu bar
    735  */
    736 static void ui_menu_bar_deactivate_ev(ui_menu_bar_t *mbar)
    737 {
    738         if (mbar->cb != NULL && mbar->cb->deactivate != NULL)
    739                 mbar->cb->deactivate(mbar, mbar->arg);
    740 }
    741 
    742353/** @}
    743354 */
Note: See TracChangeset for help on using the changeset viewer.