xdg_shell: float and position dragged toplevels

Make toplevels attached to an xdg-toplevel-drag automatically float, and
position them at the cursor when first mapped.

This enables the typical tab tear-off workflow:
1. User starts dragging a tab in a browser
2. Browser creates a new toplevel attached to the drag
3. Sway floats the toplevel and positions it at the cursor
4. The motion handler (from previous commit) keeps it moving with cursor
This commit is contained in:
Ryan Walklin 2025-12-12 21:04:15 +13:00
parent c65b7477b5
commit 6ac37faaa4
2 changed files with 25 additions and 0 deletions

View file

@ -2,10 +2,13 @@
#include <stdbool.h>
#include <stdlib.h>
#include <wayland-server-core.h>
#include <wlr/types/wlr_data_device.h>
#include <wlr/types/wlr_xdg_shell.h>
#include <wlr/types/wlr_xdg_toplevel_drag_v1.h>
#include <wlr/types/wlr_xdg_toplevel_tag_v1.h>
#include <wlr/util/edges.h>
#include "log.h"
#include "sway/server.h"
#include "sway/decoration.h"
#include "sway/scene_descriptor.h"
#include "sway/desktop/transaction.h"
@ -228,6 +231,13 @@ static void set_resizing(struct sway_view *view, bool resizing) {
static bool wants_floating(struct sway_view *view) {
struct wlr_xdg_toplevel *toplevel = view->wlr_xdg_toplevel;
struct wlr_xdg_toplevel_state *state = &toplevel->current;
// Check if this toplevel is attached to a toplevel drag
if (wlr_xdg_toplevel_drag_v1_from_wlr_xdg_toplevel(
server.xdg_toplevel_drag_manager, toplevel) != NULL) {
return true;
}
return (state->min_width != 0 && state->min_height != 0
&& (state->min_width == state->max_width
|| state->min_height == state->max_height))

View file

@ -8,10 +8,13 @@
#include <wlr/types/wlr_foreign_toplevel_management_v1.h>
#include <wlr/types/wlr_fractional_scale_v1.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_cursor.h>
#include <wlr/types/wlr_security_context_v1.h>
#include <wlr/types/wlr_server_decoration.h>
#include <wlr/types/wlr_subcompositor.h>
#include <wlr/types/wlr_xdg_decoration_v1.h>
#include <wlr/types/wlr_xdg_shell.h>
#include <wlr/types/wlr_xdg_toplevel_drag_v1.h>
#include <wlr/types/wlr_session_lock_v1.h>
#if WLR_HAS_XWAYLAND
#include <wlr/xwayland.h>
@ -861,6 +864,18 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
view->container->pending.border = config->floating_border;
view->container->pending.border_thickness = config->floating_border_thickness;
container_set_floating(view->container, true);
// If this is a toplevel attached to a drag, position it at the cursor
if (view->wlr_xdg_toplevel != NULL) {
struct wlr_xdg_toplevel_drag_v1 *toplevel_drag =
wlr_xdg_toplevel_drag_v1_from_wlr_xdg_toplevel(
server.xdg_toplevel_drag_manager, view->wlr_xdg_toplevel);
if (toplevel_drag != NULL) {
double x = seat->cursor->cursor->x - toplevel_drag->x_offset;
double y = seat->cursor->cursor->y - toplevel_drag->y_offset;
container_floating_move_to(view->container, x, y);
}
}
} else {
view->container->pending.border = config->border;
view->container->pending.border_thickness = config->border_thickness;