menu: recursive findMenu so sub-sub-menus respond to mouse

The previous implementation only checked one level of cascades —
scr->root_menu and scr->root_menu->cascades[i].  Sub-sub-menus
(cascade->cascade->...) were never checked, so hovering over a
sub-sub-menu's entries did not return the menu, and selectEntry
therefore could not open the next-level cascade or react to clicks.

Replace with a recursive walk over each top-level menu's full
cascade tree.  Pick the deepest mapped menu that contains the
pointer (deeper cascades render on top in the scene graph and
should win the hit-test).

X11 sidesteps this with XQueryPointer + XFindContext, which finds
any menu window via the X server's hit-test regardless of nesting.
This commit is contained in:
Window Maker 2026-05-24 08:23:42 +02:00
parent 31576e4b25
commit 3fb9f3925f
1 changed files with 35 additions and 59 deletions

View File

@ -1109,73 +1109,49 @@ static void selectEntry(WMenu * menu, int entry_no)
}
}
/* Recursively walk a menu and all of its descendants checking whether
* the pointer is over the entries area. Updates *deepest_p to the
* deepest mapped menu under (x,y) so that sub-sub-menus (cascade-of-
* cascade-...) are reachable.
*/
static void findMenuWalk(WMenu *m, int x, int y,
WMenu **deepest_p, int *dx_p, int *dy_p)
{
if (!m) return;
if (m->flags.mapped &&
x >= m->frame_x && x < m->frame_x + MENUW(m) &&
y >= m->frame_y && y < m->frame_y + MENUH(m)) {
*deepest_p = m;
*dx_p = x - m->frame_x;
*dy_p = y - m->frame_y - m->frame->top_width;
}
if (m->brother)
findMenuWalk(m->brother, x, y, deepest_p, dx_p, dy_p);
for (int i = 0; i < m->cascade_no; i++)
findMenuWalk(m->cascades[i], x, y, deepest_p, dx_p, dy_p);
}
static WMenu *findMenu(WScreen * scr, int *x_ret, int *y_ret)
{
WMenu *menu;
int x, y;
WMenu *deepest = NULL;
int dx = 0, dy = 0;
wm_backend->pointer_get_position(&x, &y);
/* Helper: check if (x,y) is inside a mapped menu */
#define CHECK_MENU(m) do { \
if ((m) && (m)->flags.mapped && \
x >= (m)->frame_x && x < (m)->frame_x + MENUW(m) && \
y >= (m)->frame_y && y < (m)->frame_y + MENUH(m)) { \
*x_ret = x - (m)->frame_x; \
*y_ret = y - (m)->frame_y - (m)->frame->top_width; \
return (m); \
} \
} while(0)
findMenuWalk(scr->root_menu, x, y, &deepest, &dx, &dy);
findMenuWalk(scr->switch_menu, x, y, &deepest, &dx, &dy);
findMenuWalk(scr->window_menu, x, y, &deepest, &dx, &dy);
findMenuWalk(scr->dock ? scr->dock->menu : NULL,
x, y, &deepest, &dx, &dy);
findMenuWalk(scr->clip_menu, x, y, &deepest, &dx, &dy);
findMenuWalk(scr->dock_pos_menu, x, y, &deepest, &dx, &dy);
/* Check root menu, its brother, and cascades */
menu = scr->root_menu;
if (menu) {
CHECK_MENU(menu);
CHECK_MENU(menu->brother);
for (int i = 0; i < menu->cascade_no; i++) {
CHECK_MENU(menu->cascades[i]);
if (menu->cascades[i])
CHECK_MENU(menu->cascades[i]->brother);
}
if (deepest) {
*x_ret = dx;
*y_ret = dy;
return deepest;
}
/* Check switch menu */
menu = scr->switch_menu;
CHECK_MENU(menu);
if (menu) CHECK_MENU(menu->brother);
/* Check window menu */
menu = scr->window_menu;
if (menu) {
CHECK_MENU(menu);
CHECK_MENU(menu->brother);
for (int i = 0; i < menu->cascade_no; i++) {
CHECK_MENU(menu->cascades[i]);
if (menu->cascades[i])
CHECK_MENU(menu->cascades[i]->brother);
}
}
/* Check dock menu */
menu = scr->dock ? scr->dock->menu : NULL;
if (menu) {
CHECK_MENU(menu);
for (int i = 0; i < menu->cascade_no; i++)
CHECK_MENU(menu->cascades[i]);
}
/* Check clip menu */
menu = scr->clip_menu;
if (menu) {
CHECK_MENU(menu);
for (int i = 0; i < menu->cascade_no; i++)
CHECK_MENU(menu->cascades[i]);
}
/* Check dock position menu */
CHECK_MENU(scr->dock_pos_menu);
#undef CHECK_MENU
return NULL;
}