wmaker-popup-v1: add set_parent for parent-relative positioning
The previous version had set_position(x, y) in compositor-global coordinates, but Wayland clients don't know their toplevel's screen position — the editmenu and popup button code naturally compute positions relative to the parent toplevel via accumulated view->pos. Add set_parent(xdg_toplevel) request. When a parent is set, set_position(x, y) is interpreted as offsets from the parent toplevel's top-left corner. Compositor side resolves the parent's wl_toplevel_view via wlr_xdg_toplevel_from_resource → wl_find_view_by_xdg_surface, then adds parent->x and parent->y to the requested offsets. Client side (wclient_wl.c) tracks the first non-popup toplevel as ctx->main_toplevel and calls set_parent on every popup wrapper to anchor it to the application's main window.
This commit is contained in:
parent
1cf271932a
commit
27802e6d43
|
|
@ -66,6 +66,8 @@ typedef struct WLClientCtx {
|
|||
struct xdg_wm_base *wm_base;
|
||||
/* wmaker-popup-v1: client-driven popup positioning */
|
||||
struct wmaker_popup_manager_v1 *wmaker_popup_mgr;
|
||||
/* First non-popup toplevel — used as parent for popup positioning. */
|
||||
struct xdg_toplevel *main_toplevel;
|
||||
|
||||
struct wl_pointer *pointer;
|
||||
struct wl_keyboard *keyboard;
|
||||
|
|
@ -466,6 +468,11 @@ wl_client_toplevel_realize(struct W_View *view, int width, int height)
|
|||
wwarning("wclient_wl: wrapped popup toplevel id=0x%lx pos=(%d,%d) wrapper=%p",
|
||||
(unsigned long)view->window, view->pos.x, view->pos.y,
|
||||
(void*)top->wmaker_popup);
|
||||
/* Anchor positioning to the application's main toplevel. */
|
||||
if (top->wmaker_popup && ctx->main_toplevel) {
|
||||
wmaker_popup_v1_set_parent(top->wmaker_popup,
|
||||
ctx->main_toplevel);
|
||||
}
|
||||
/* Apply any position recorded in view->pos before realize. */
|
||||
if (top->wmaker_popup && (view->pos.x || view->pos.y)) {
|
||||
wmaker_popup_v1_set_position(top->wmaker_popup,
|
||||
|
|
@ -477,6 +484,10 @@ wl_client_toplevel_realize(struct W_View *view, int width, int height)
|
|||
} else {
|
||||
xdg_toplevel_set_app_id(top->xdg_toplevel, "WINGs");
|
||||
xdg_toplevel_set_title(top->xdg_toplevel, "WINGs Application");
|
||||
/* Remember the first regular toplevel as the "main" window so
|
||||
* subsequent popups can position relative to it. */
|
||||
if (!ctx->main_toplevel)
|
||||
ctx->main_toplevel = top->xdg_toplevel;
|
||||
}
|
||||
|
||||
/* Initial commit with no buffer so the compositor sends the first
|
||||
|
|
|
|||
|
|
@ -74,7 +74,10 @@
|
|||
<interface name="wmaker_popup_v1" version="1">
|
||||
<description summary="client-positioned popup toplevel">
|
||||
Wrapper around an xdg_toplevel that lets the client drive screen
|
||||
placement. Position is in compositor-global coordinates.
|
||||
placement. Position is in coordinates relative to the parent
|
||||
toplevel's top-left corner, or compositor-global if no parent is
|
||||
set. The parent is set via set_parent() and is typically the
|
||||
application's main toplevel (e.g., the WPrefs main window).
|
||||
</description>
|
||||
|
||||
<enum name="error">
|
||||
|
|
@ -90,14 +93,26 @@
|
|||
</description>
|
||||
</request>
|
||||
|
||||
<request name="set_position">
|
||||
<description summary="set the screen position of the popup">
|
||||
Place the popup's top-left corner at (x, y) in compositor-global
|
||||
coordinates. Takes effect on the next surface commit if the
|
||||
toplevel is not yet mapped, or immediately if already mapped.
|
||||
<request name="set_parent">
|
||||
<description summary="set the parent toplevel for relative positioning">
|
||||
Subsequent set_position calls are interpreted as offsets from
|
||||
the parent toplevel's top-left corner. Pass NULL to revert to
|
||||
compositor-global coordinates.
|
||||
</description>
|
||||
<arg name="x" type="int" summary="screen x in compositor coordinates"/>
|
||||
<arg name="y" type="int" summary="screen y in compositor coordinates"/>
|
||||
<arg name="parent" type="object" interface="xdg_toplevel"
|
||||
allow-null="true" summary="parent toplevel or null"/>
|
||||
</request>
|
||||
|
||||
<request name="set_position">
|
||||
<description summary="set the position of the popup">
|
||||
Place the popup at (x, y). If a parent has been set via
|
||||
set_parent, coordinates are relative to the parent's top-left
|
||||
corner. Otherwise, coordinates are compositor-global.
|
||||
Takes effect on the next surface commit if the toplevel is not
|
||||
yet mapped, or immediately if already mapped.
|
||||
</description>
|
||||
<arg name="x" type="int" summary="x offset"/>
|
||||
<arg name="y" type="int" summary="y offset"/>
|
||||
</request>
|
||||
</interface>
|
||||
</protocol>
|
||||
|
|
|
|||
|
|
@ -277,6 +277,7 @@ struct wl_toplevel_view {
|
|||
/* wmaker-popup-v1: client-driven popup position (only valid if popup != NULL) */
|
||||
struct wl_resource *popup; /* wmaker_popup_v1 resource or NULL */
|
||||
int popup_x, popup_y;
|
||||
struct wlr_xdg_toplevel *popup_parent; /* parent toplevel or NULL */
|
||||
long *wm_state; /* Phase 56: saved WM state or NULL */
|
||||
int wm_state_n; /* Phase 56: element count */
|
||||
char **wm_command; /* Phase 57: WM_COMMAND argv or NULL */
|
||||
|
|
@ -1390,6 +1391,23 @@ wmaker_popup_handle_destroy(struct wl_client *client,
|
|||
wl_resource_destroy(resource);
|
||||
}
|
||||
|
||||
static void
|
||||
wmaker_popup_handle_set_parent(struct wl_client *client,
|
||||
struct wl_resource *resource,
|
||||
struct wl_resource *parent_resource)
|
||||
{
|
||||
(void)client;
|
||||
struct wl_toplevel_view *view = wl_resource_get_user_data(resource);
|
||||
if (!view) return;
|
||||
if (parent_resource) {
|
||||
view->popup_parent = wlr_xdg_toplevel_from_resource(parent_resource);
|
||||
} else {
|
||||
view->popup_parent = NULL;
|
||||
}
|
||||
wwarning("wmaker_popup: set_parent view=%p parent=%p", (void*)view,
|
||||
(void*)view->popup_parent);
|
||||
}
|
||||
|
||||
static void
|
||||
wmaker_popup_handle_set_position(struct wl_client *client,
|
||||
struct wl_resource *resource,
|
||||
|
|
@ -1406,6 +1424,7 @@ wmaker_popup_handle_set_position(struct wl_client *client,
|
|||
|
||||
static const struct wmaker_popup_v1_interface wmaker_popup_impl = {
|
||||
.destroy = wmaker_popup_handle_destroy,
|
||||
.set_parent = wmaker_popup_handle_set_parent,
|
||||
.set_position = wmaker_popup_handle_set_position,
|
||||
};
|
||||
|
||||
|
|
@ -1422,14 +1441,27 @@ wmaker_popup_apply(struct wl_toplevel_view *view)
|
|||
{
|
||||
if (!view || !view->popup) return;
|
||||
|
||||
int dx = view->popup_x - view->x;
|
||||
int dy = view->popup_y - view->y;
|
||||
/* If a parent was set, translate the requested position into screen
|
||||
* coordinates by adding the parent toplevel's current position. */
|
||||
int target_x = view->popup_x;
|
||||
int target_y = view->popup_y;
|
||||
if (view->popup_parent) {
|
||||
struct wl_toplevel_view *parent_view =
|
||||
wl_find_view_by_xdg_surface(view->popup_parent->base);
|
||||
if (parent_view) {
|
||||
target_x += parent_view->x;
|
||||
target_y += parent_view->y;
|
||||
}
|
||||
}
|
||||
|
||||
int dx = target_x - view->x;
|
||||
int dy = target_y - view->y;
|
||||
wwarning("wmaker_popup_apply: view=%p (%d,%d)->(%d,%d) frame_id=0x%lx scene_node=%p",
|
||||
(void*)view, view->x, view->y, view->popup_x, view->popup_y,
|
||||
(void*)view, view->x, view->y, target_x, target_y,
|
||||
(unsigned long)view->frame_id, (void*)view->scene_node);
|
||||
|
||||
view->x = view->popup_x;
|
||||
view->y = view->popup_y;
|
||||
view->x = target_x;
|
||||
view->y = target_y;
|
||||
|
||||
/* If a frame_buf wraps this view, move it via the WM's window-move
|
||||
* path so the frame and its tree node update consistently. */
|
||||
|
|
|
|||
Loading…
Reference in New Issue