wayland: fix dialog/menu hit-testing and parenting
Several related fixes for WINGs dialogs and menus that depend on correct scene graph parenting and hit-testing: 1. fake_leader_create: now creates a wlr_scene_tree (not just a scene_buf). When the fake_leader is reparented into a WMaker frame, any content inside it (e.g. WINGs panel) is reparented as part of the tree, so it follows the WMaker frame's position when moved. 2. wl_client_reparent (frame_buf path): if the source frame_buf has a tree, reparent the tree (with all its children) instead of the bare scene_buf. 3. frame_buf_update_children_abs: new recursive helper to walk the entire descendant tree updating fb->x/y absolute tracking. Used by wl_window_move, wl_frame_configure, and wl_client_reparent. Without this, grandchildren (e.g. WINGs panel inside a fake_leader inside a WMaker frame) had stale fb->x/y after the WMaker frame was moved, causing click coordinate translation to be wrong and buttons to stop responding after dragging the dialog. 4. frame_buf_button_at: now walks up the scene tree checking ancestor enabled state. A frame_buf's scene_buf may be flagged enabled even when an ancestor tree is disabled (e.g. menu unmapped). Without this check, clicks could be incorrectly routed to ghost frame_bufs from recently-unmapped menus, causing root-menu reopen to require multiple clicks.
This commit is contained in:
parent
bc25aa4b8a
commit
c7e2b7ba88
|
|
@ -3981,6 +3981,34 @@ frame_buf_find(WNativeWindow id)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/* Recursively update fb->x, fb->y for all descendants of `parent`, used
|
||||
* to keep absolute-position tracking in sync after the parent moves.
|
||||
* Hit-testing (frame_buf_at, frame_buf_button_at) and click coordinate
|
||||
* translation depend on these fields being current. */
|
||||
static void
|
||||
frame_buf_update_children_abs(WNativeWindow parent_id)
|
||||
{
|
||||
struct wl_frame_buf *parent = frame_buf_find(parent_id);
|
||||
if (!parent) return;
|
||||
struct wl_frame_buf *child;
|
||||
wl_list_for_each(child, &wl_state.frame_list, link) {
|
||||
if (child->parent_id == parent_id) {
|
||||
child->x = parent->x + child->rel_x;
|
||||
child->y = parent->y + child->rel_y;
|
||||
/* Recurse into grandchildren */
|
||||
frame_buf_update_children_abs(child->id);
|
||||
}
|
||||
}
|
||||
/* Also update toplevel views parented to this frame */
|
||||
struct wl_toplevel_view *cv;
|
||||
wl_list_for_each(cv, &wl_state.toplevel_list, link) {
|
||||
if (cv->frame_id == parent_id) {
|
||||
cv->x = parent->x + cv->frame_rel_x;
|
||||
cv->y = parent->y + cv->frame_rel_y;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Hit-test: find the frame_buf under (px, py) that should receive the event.
|
||||
*
|
||||
* Returns the most-specific (innermost) frame_buf whose rendered pixels
|
||||
|
|
@ -4077,6 +4105,17 @@ frame_buf_button_at(int px, int py)
|
|||
if (!fb->parent_id) continue; /* top-level root */
|
||||
if (!fb->scene_buf) continue; /* no scene node */
|
||||
if (!fb->scene_buf->node.enabled) continue; /* hidden */
|
||||
/* Walk up the scene tree — if any ancestor is disabled, this
|
||||
* frame_buf is not actually visible (just its node enabled flag
|
||||
* is set, but a parent tree was disabled e.g. by window_unmap). */
|
||||
struct wlr_scene_node *n = fb->scene_buf->node.parent
|
||||
? &fb->scene_buf->node.parent->node : NULL;
|
||||
int ancestor_disabled = 0;
|
||||
while (n) {
|
||||
if (!n->enabled) { ancestor_disabled = 1; break; }
|
||||
n = n->parent ? &n->parent->node : NULL;
|
||||
}
|
||||
if (ancestor_disabled) continue;
|
||||
/* Buttons are small children of the titlebar with enabled scene_bufs.
|
||||
* Skip large nodes (titlebars, resizebars) by size threshold. */
|
||||
if (px < fb->x || px >= fb->x + fb->width) continue;
|
||||
|
|
@ -4278,22 +4317,8 @@ wl_frame_configure(WNativeWindow win, int x, int y, int width, int height)
|
|||
wlr_scene_buffer_set_dest_size(fb->scene_buf, width, height);
|
||||
}
|
||||
|
||||
/* Update absolute position tracking for children (used by hit-testing) */
|
||||
struct wl_toplevel_view *cv;
|
||||
wl_list_for_each(cv, &wl_state.toplevel_list, link) {
|
||||
if (cv->frame_id == win) {
|
||||
cv->x = fb->x + cv->frame_rel_x;
|
||||
cv->y = fb->y + cv->frame_rel_y;
|
||||
break;
|
||||
}
|
||||
}
|
||||
struct wl_frame_buf *child;
|
||||
wl_list_for_each(child, &wl_state.frame_list, link) {
|
||||
if (child->parent_id == win) {
|
||||
child->x = fb->x + child->rel_x;
|
||||
child->y = fb->y + child->rel_y;
|
||||
}
|
||||
}
|
||||
/* Update absolute position tracking — recursively for all descendants. */
|
||||
frame_buf_update_children_abs(win);
|
||||
}
|
||||
|
||||
static void wl_frame_paint_button(WCoreWindow *button, WTexture *texture,
|
||||
|
|
@ -6551,22 +6576,10 @@ static void wl_window_move(WNativeWindow win, int x, int y)
|
|||
if (fb->tree)
|
||||
wlr_scene_node_set_position(&fb->tree->node, x, y);
|
||||
}
|
||||
/* Update absolute position tracking for hit-testing */
|
||||
struct wl_toplevel_view *cv;
|
||||
wl_list_for_each(cv, &wl_state.toplevel_list, link) {
|
||||
if (cv->frame_id == win) {
|
||||
cv->x = fb->x + cv->frame_rel_x;
|
||||
cv->y = fb->y + cv->frame_rel_y;
|
||||
break;
|
||||
}
|
||||
}
|
||||
struct wl_frame_buf *child;
|
||||
wl_list_for_each(child, &wl_state.frame_list, link) {
|
||||
if (child->parent_id == win) {
|
||||
child->x = fb->x + child->rel_x;
|
||||
child->y = fb->y + child->rel_y;
|
||||
}
|
||||
}
|
||||
/* Update absolute position tracking for hit-testing — recursively
|
||||
* for all descendants so grandchildren (e.g. WINGs panel inside
|
||||
* a fake_leader inside a WMaker frame) stay in sync. */
|
||||
frame_buf_update_children_abs(win);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -6948,20 +6961,20 @@ wl_client_reparent(WNativeWindow client, WNativeWindow new_parent, int x, int y)
|
|||
fb->rel_y = y;
|
||||
fb->x = parent_fb->x + x;
|
||||
fb->y = parent_fb->y + y;
|
||||
if (fb->scene_buf) {
|
||||
/* Reparent under parent's tree and use relative position */
|
||||
if (fb->tree) {
|
||||
/* This frame_buf has a tree (e.g. fake_leader): reparent
|
||||
* the whole tree so all children move with it. */
|
||||
if (parent_fb->tree)
|
||||
wlr_scene_node_reparent(&fb->tree->node, parent_fb->tree);
|
||||
wlr_scene_node_set_position(&fb->tree->node, x, y);
|
||||
} else if (fb->scene_buf) {
|
||||
/* Plain frame_buf (no tree): reparent scene_buf directly. */
|
||||
if (parent_fb->tree)
|
||||
wlr_scene_node_reparent(&fb->scene_buf->node, parent_fb->tree);
|
||||
wlr_scene_node_set_position(&fb->scene_buf->node, x, y);
|
||||
}
|
||||
/* Update absolute tracking for children */
|
||||
struct wl_frame_buf *child;
|
||||
wl_list_for_each(child, &wl_state.frame_list, link) {
|
||||
if (child->parent_id == client) {
|
||||
child->x = fb->x + child->rel_x;
|
||||
child->y = fb->y + child->rel_y;
|
||||
}
|
||||
}
|
||||
/* Update absolute tracking recursively for all descendants */
|
||||
frame_buf_update_children_abs(client);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -7140,7 +7153,11 @@ wl_icccm_free_command(char **argv)
|
|||
static WNativeWindow wl_fake_leader_create(WScreen *scr)
|
||||
{
|
||||
(void)scr;
|
||||
/* Create a frame_buf to act as the dialog's content container */
|
||||
/* Create a frame_buf with a tree to act as the dialog's content
|
||||
* container. The tree is essential — content reparented into the
|
||||
* fake_leader (e.g. WINGs panel views) becomes a child of the tree
|
||||
* and follows it when the fake_leader itself is reparented into
|
||||
* a WMaker frame. */
|
||||
struct wl_frame_buf *fb = wmalloc(sizeof(*fb));
|
||||
memset(fb, 0, sizeof(*fb));
|
||||
|
||||
|
|
@ -7154,9 +7171,16 @@ static WNativeWindow wl_fake_leader_create(WScreen *scr)
|
|||
if (wl_state.scene) {
|
||||
struct wlr_scene_tree *_pp = wl_state.level_trees[WL_LAYER_NORMAL]
|
||||
? wl_state.level_trees[WL_LAYER_NORMAL] : &wl_state.scene->tree;
|
||||
fb->scene_buf = wlr_scene_buffer_create(_pp, &fb->base);
|
||||
if (fb->scene_buf)
|
||||
wlr_scene_node_set_enabled(&fb->scene_buf->node, false);
|
||||
fb->tree = wlr_scene_tree_create(_pp);
|
||||
if (fb->tree) {
|
||||
wlr_scene_node_set_position(&fb->tree->node, 0, 0);
|
||||
fb->scene_buf = wlr_scene_buffer_create(fb->tree, &fb->base);
|
||||
if (fb->scene_buf) {
|
||||
wlr_scene_node_set_position(&fb->scene_buf->node, 0, 0);
|
||||
wlr_scene_node_set_enabled(&fb->scene_buf->node, false);
|
||||
}
|
||||
wlr_scene_node_set_enabled(&fb->tree->node, true);
|
||||
}
|
||||
}
|
||||
|
||||
wl_list_insert(&wl_state.frame_list, &fb->link);
|
||||
|
|
|
|||
Loading…
Reference in New Issue