Ignore:
File:
1 edited

Legend:

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

    rd7f82635 rec50d65e  
    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>
     
    4546#include <ui/control.h>
    4647#include <ui/paint.h>
    47 #include <ui/menu.h>
    4848#include <ui/menubar.h>
     49#include <ui/menudd.h>
     50#include <ui/wdecor.h>
    4951#include <ui/window.h>
    5052#include "../private/menubar.h"
    5153#include "../private/resource.h"
     54#include "../private/wdecor.h"
     55#include "../private/window.h"
    5256
    5357enum {
     
    6064static void ui_menu_bar_ctl_destroy(void *);
    6165static errno_t ui_menu_bar_ctl_paint(void *);
     66static ui_evclaim_t ui_menu_bar_ctl_kbd_event(void *, kbd_event_t *);
    6267static ui_evclaim_t ui_menu_bar_ctl_pos_event(void *, pos_event_t *);
     68static void ui_menu_bar_activate_ev(ui_menu_bar_t *);
     69static void ui_menu_bar_deactivate_ev(ui_menu_bar_t *);
    6370
    6471/** Menu bar control ops */
     
    6673        .destroy = ui_menu_bar_ctl_destroy,
    6774        .paint = ui_menu_bar_ctl_paint,
    68         .pos_event = ui_menu_bar_ctl_pos_event,
     75        .kbd_event = ui_menu_bar_ctl_kbd_event,
     76        .pos_event = ui_menu_bar_ctl_pos_event
    6977};
    7078
     
    93101        mbar->ui = ui;
    94102        mbar->window = window;
    95         list_initialize(&mbar->menus);
     103        list_initialize(&mbar->menudds);
     104
     105        if (window->mbar == NULL)
     106                window->mbar = mbar;
     107
    96108        *rmbar = mbar;
    97109        return EOK;
     
    104116void ui_menu_bar_destroy(ui_menu_bar_t *mbar)
    105117{
    106         ui_menu_t *menu;
     118        ui_menu_dd_t *mdd;
    107119
    108120        if (mbar == NULL)
    109121                return;
    110122
    111         /* Destroy menus */
    112         menu = ui_menu_first(mbar);
    113         while (menu != NULL) {
    114                 ui_menu_destroy(menu);
    115                 menu = ui_menu_first(mbar);
     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);
    116131        }
    117132
     
    120135}
    121136
     137/** Set menu bar callbacks.
     138 *
     139 * @param mbar Menu bar
     140 * @param cb Callbacks
     141 * @param arg Callback argument
     142 */
     143void 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
    122149/** Get base control from menu bar.
    123150 *
     
    148175{
    149176        ui_resource_t *res;
    150         gfx_text_fmt_t fmt;
     177        ui_text_fmt_t fmt;
    151178        gfx_coord2_t pos;
    152179        gfx_coord2_t tpos;
    153180        gfx_rect_t rect;
    154181        gfx_color_t *bg_color;
    155         ui_menu_t *menu;
     182        ui_menu_dd_t *mdd;
    156183        const char *caption;
    157184        gfx_coord_t width;
     
    182209        pos = mbar->rect.p0;
    183210
    184         gfx_text_fmt_init(&fmt);
     211        ui_text_fmt_init(&fmt);
     212        fmt.font = res->font;
    185213        fmt.halign = gfx_halign_left;
    186214        fmt.valign = gfx_valign_top;
    187215
    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;
     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;
    192220                tpos.x = pos.x + hpad;
    193221                tpos.y = pos.y + vpad;
     
    197225                rect.p1.y = mbar->rect.p1.y;
    198226
    199                 if (menu == mbar->selected) {
     227                if (mdd == mbar->selected) {
    200228                        fmt.color = res->wnd_sel_text_color;
     229                        fmt.hgl_color = res->wnd_sel_text_hgl_color;
    201230                        bg_color = res->wnd_sel_text_bg_color;
    202231                } else {
    203232                        fmt.color = res->wnd_text_color;
     233                        fmt.hgl_color = res->wnd_text_hgl_color;
    204234                        bg_color = res->wnd_face_color;
    205235                }
     
    213243                        goto error;
    214244
    215                 rc = gfx_puttext(res->font, &tpos, &fmt, caption);
     245                rc = ui_paint_text(&tpos, &fmt, caption);
    216246                if (rc != EOK)
    217247                        goto error;
    218248
    219249                pos.x += width;
    220                 menu = ui_menu_next(menu);
     250                mdd = ui_menu_dd_next(mdd);
    221251        }
    222252
     
    236266 *
    237267 * @param mbar Menu bar
    238  * @param rect Menu bar entry rectangle
    239  * @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)
    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);
     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 */
     272void 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        }
    256290
    257291        (void) ui_menu_bar_paint(mbar);
    258292
    259293        if (mbar->selected != NULL) {
    260                 (void) ui_menu_open(mbar->selected, rect);
     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 */
     320void 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 */
     335void 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 */
     350void 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 */
     367void 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 */
     407void 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 */
     445ui_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 */
     493ui_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 */
     520void 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);
    261534        }
    262535}
     
    273546        gfx_coord2_t pos;
    274547        gfx_rect_t rect;
    275         ui_menu_t *menu;
     548        ui_menu_dd_t *mdd;
    276549        const char *caption;
    277550        gfx_coord_t width;
    278551        gfx_coord_t hpad;
    279552        gfx_coord2_t ppos;
     553        sysarg_t pos_id;
    280554
    281555        res = ui_window_get_res(mbar->window);
     
    291565
    292566        pos = mbar->rect.p0;
    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;
     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;
    298573
    299574                rect.p0 = pos;
     
    304579                if (event->type == POS_PRESS &&
    305580                    gfx_pix_inside_rect(&ppos, &rect)) {
    306                         ui_menu_bar_select(mbar, &rect, menu);
     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
    307587                        return ui_claimed;
    308588                }
    309589
    310590                pos.x += width;
    311                 menu = ui_menu_next(menu);
     591                mdd = ui_menu_dd_next(mdd);
    312592        }
    313593
    314594        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 */
     603void 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 */
     650void 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 */
     667void 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);
    315671}
    316672
     
    338694}
    339695
    340 /** Handle menu bar control position event.
     696/** Handle menu bar control keyboard event.
    341697 *
    342698 * @param arg Argument (ui_menu_bar_t *)
     
    344700 * @return @c ui_claimed iff the event is claimed
    345701 */
     702ui_evclaim_t ui_menu_bar_ctl_kbd_event(void *arg, kbd_event_t *event)
     703{
     704        ui_menu_bar_t *mbar = (ui_menu_bar_t *) arg;
     705
     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 */
    346715ui_evclaim_t ui_menu_bar_ctl_pos_event(void *arg, pos_event_t *event)
    347716{
     
    351720}
    352721
     722/** Send menu bar activate event.
     723 *
     724 * @param mbar Menu bar
     725 */
     726static 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 */
     736static 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
    353742/** @}
    354743 */
Note: See TracChangeset for help on using the changeset viewer.