diff --git a/include/menu/menu.h b/include/menu/menu.h index a5ec7dd8..777bc6af 100644 --- a/include/menu/menu.h +++ b/include/menu/menu.h @@ -23,6 +23,7 @@ struct menu_scene { struct menuitem { struct wl_list actions; + struct menu *parent; struct menu *submenu; struct menu_scene normal; struct menu_scene selected; @@ -75,15 +76,19 @@ void menu_open(struct menu *menu, int x, int y); * - handles hover effects * - may open/close submenus */ -void menu_process_cursor_motion(struct menu *menu, struct wlr_scene_node *node); +void menu_process_cursor_motion(struct wlr_scene_node *node); /** - * menu_call_actions - call actions associated with a menu entry + * menu_call_actions - call actions associated with a menu node * - * If actions are found, server->menu_current will be closed and set to NULL - * Returns true if handled + * If menuitem connected to @node does not just open a submenu: + * - associated actions will be called + * - server->menu_current will be closed + * - server->menu_current will be set to NULL + * + * Returns true if actions have actually been executed */ -bool menu_call_actions(struct menu *menu, struct wlr_scene_node *node); +bool menu_call_actions(struct wlr_scene_node *node); /* menu_close - close menu */ void menu_close(struct menu *menu); diff --git a/include/node.h b/include/node.h index b1fffb9a..5cc7220e 100644 --- a/include/node.h +++ b/include/node.h @@ -6,6 +6,7 @@ struct view; struct lab_layer_surface; struct lab_layer_popup; +struct menuitem; enum node_descriptor_type { LAB_NODE_DESC_NODE = 0, @@ -13,6 +14,7 @@ enum node_descriptor_type { LAB_NODE_DESC_XDG_POPUP, LAB_NODE_DESC_LAYER_SURFACE, LAB_NODE_DESC_LAYER_POPUP, + LAB_NODE_DESC_MENUITEM, }; struct node_descriptor { @@ -54,4 +56,11 @@ struct lab_layer_surface *node_layer_surface_from_node( struct lab_layer_popup *node_layer_popup_from_node( struct wlr_scene_node *wlr_scene_node); +/** + * node_menuitem_from_node - return menuitem struct from node + * @wlr_scene_node: wlr_scene_node from which to return data + */ +struct menuitem *node_menuitem_from_node( + struct wlr_scene_node *wlr_scene_node); + #endif /* __LABWC_NODE_DESCRIPTOR_H */ diff --git a/src/cursor.c b/src/cursor.c index f6716cb7..7aa32d35 100644 --- a/src/cursor.c +++ b/src/cursor.c @@ -233,7 +233,7 @@ process_cursor_motion(struct server *server, uint32_t time) } if (view_area == LAB_SSD_MENU) { - menu_process_cursor_motion(server->menu_current, node); + menu_process_cursor_motion(node); return; } @@ -677,8 +677,8 @@ cursor_button(struct wl_listener *listener, void *data) if (view_area != LAB_SSD_MENU) { /* We close the menu on release so we don't leak a stray release */ close_menu = true; - } else if (menu_call_actions(server->menu_current, node)) { - /* Action was successfull, may fail if item contains a submenu */ + } else if (menu_call_actions(node)) { + /* Action was successfull, may fail if item just opens a submenu */ close_menu = true; } return; diff --git a/src/desktop.c b/src/desktop.c index 8c1c7401..d8768de5 100644 --- a/src/desktop.c +++ b/src/desktop.c @@ -284,9 +284,9 @@ desktop_node_and_view_at(struct server *server, double lx, double ly, #endif } struct wlr_scene_node *osd = &server->osd_tree->node; - struct wlr_scene_node *menu = &server->menu_tree->node; while (node) { struct node_descriptor *desc = node->data; + /* TODO: convert to switch() */ if (desc) { if (desc->type == LAB_NODE_DESC_VIEW) { goto has_view_data; @@ -299,13 +299,16 @@ desktop_node_and_view_at(struct server *server, double lx, double ly, *view_area = LAB_SSD_CLIENT; return NULL; } + if (desc->type == LAB_NODE_DESC_MENUITEM) { + /* Always return the top scene node for menu items */ + *scene_node = node; + *view_area = LAB_SSD_MENU; + return NULL; + } } if (node == osd) { *view_area = LAB_SSD_OSD; return NULL; - } else if (node == menu) { - *view_area = LAB_SSD_MENU; - return NULL; } node = node->parent; } diff --git a/src/menu/menu.c b/src/menu/menu.c index ff9a8b6a..2f34d4e3 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -20,6 +20,7 @@ #include "theme.h" #include "action.h" #include "buffer.h" +#include "node.h" #define MENUWIDTH (110) #define MENU_ITEM_PADDING_Y (4) @@ -82,6 +83,7 @@ item_create(struct menu *menu, const char *text) if (!menuitem) { return NULL; } + menuitem->parent = menu; struct server *server = menu->server; struct theme *theme = server->theme; struct font font = { @@ -117,6 +119,12 @@ item_create(struct menu *menu, const char *text) menuitem->selected.text = &wlr_scene_buffer_create( menuitem->selected.background, &menuitem->selected.buffer->base)->node; + /* Node descriptors for top scene nodes of menuitem */ + node_descriptor_create(menuitem->normal.background, + LAB_NODE_DESC_MENUITEM, menuitem); + node_descriptor_create(menuitem->selected.background, + LAB_NODE_DESC_MENUITEM, menuitem); + /* Center font nodes */ y = (menu->item_height - menuitem->normal.buffer->base.height) / 2; x = MENU_ITEM_PADDING_X; @@ -132,7 +140,7 @@ item_create(struct menu *menu, const char *text) /* Hide selected state */ wlr_scene_node_set_enabled(menuitem->selected.background, false); - /* Update menu extends */ + /* Update menu extents */ menu->size.height = (item_count + 1) * menu->item_height; wl_list_insert(&menu->menuitems, &menuitem->link); @@ -516,74 +524,47 @@ menu_open(struct menu *menu, int x, int y) } void -menu_process_cursor_motion(struct menu *menu, struct wlr_scene_node *node) +menu_process_cursor_motion(struct wlr_scene_node *node) { - if (!node) { - wlr_log(WLR_ERROR, "%s() node == NULL", __func__); + assert(node && node->data); + struct menuitem *item = node_menuitem_from_node(node); + + if (node == item->selected.background) { + /* We are on an already selected item */ return; } - assert(menu); - /* TODO: this would be much easier if we could use node->data */ - - struct menuitem *item; - wl_list_for_each (item, &menu->menuitems, link) { - if (node == item->selected.background - || node->parent == item->selected.background) { - /* We are on an already selected item */ - return; - } - if (node == item->normal.background - || node->parent == item->normal.background) { - /* We are on an item that has new mouse-focus */ - menu_set_selection(menu, item); - if (menu->selection.menu) { - /* Close old submenu tree */ - menu_close(menu->selection.menu); - } - if (item->submenu) { - /* And open the new one */ - wlr_scene_node_set_enabled( - &item->submenu->scene_tree->node, true); - } - menu->selection.menu = item->submenu; - return; - } - if (item->submenu && item->submenu == menu->selection.menu) { - menu_process_cursor_motion(item->submenu, node); - } + /* We are on an item that has new mouse-focus */ + menu_set_selection(item->parent, item); + if (item->parent->selection.menu) { + /* Close old submenu tree */ + menu_close(item->parent->selection.menu); } + + if (item->submenu) { + /* And open the new one */ + wlr_scene_node_set_enabled( + &item->submenu->scene_tree->node, true); + } + item->parent->selection.menu = item->submenu; } bool -menu_call_actions(struct menu *menu, struct wlr_scene_node *node) +menu_call_actions(struct wlr_scene_node *node) { - /* TODO: this would be much easier if we could use node->data */ + assert(node && node->data); + struct menuitem *item = node_menuitem_from_node(node); - if (!menu->selection.item) { - /* No item selected in current menu */ - wlr_log(WLR_ERROR, "No item on menu_action_selected"); + if (item->submenu) { + /* We received a click on an item that just opens a submenu */ return false; } - struct wlr_scene_node *menu_node = - menu->selection.item->selected.background; - if (node == menu_node || node->parent == menu_node) { - /* We found the correct menu item */ - if (menu->selection.item->submenu) { - /* ..but it just opens a submenu */ - return false; - } - actions_run(NULL, menu->server, &menu->selection.item->actions, 0); - menu_close(menu->server->menu_current); - menu->server->menu_current = NULL; - return true; - } - if (menu->selection.menu) { - return menu_call_actions(menu->selection.menu, node); - } - wlr_log(WLR_ERROR, "No match on menu_action_selected"); - return false; + + actions_run(NULL, item->parent->server, &item->actions, 0); + menu_close(item->parent->server->menu_current); + item->parent->server->menu_current = NULL; + return true; } void diff --git a/src/node.c b/src/node.c index c189358a..f667d82a 100644 --- a/src/node.c +++ b/src/node.c @@ -64,3 +64,12 @@ node_layer_popup_from_node(struct wlr_scene_node *wlr_scene_node) assert(node_descriptor->type == LAB_NODE_DESC_LAYER_POPUP); return (struct lab_layer_popup *)node_descriptor->data; } + +struct menuitem * +node_menuitem_from_node(struct wlr_scene_node *wlr_scene_node) +{ + assert(wlr_scene_node->data); + struct node_descriptor *node_descriptor = wlr_scene_node->data; + assert(node_descriptor->type == LAB_NODE_DESC_MENUITEM); + return (struct menuitem *)node_descriptor->data; +}