aboutsummaryrefslogtreecommitdiffstats
path: root/sway/input/seatop_move_tiling.c
diff options
context:
space:
mode:
Diffstat (limited to 'sway/input/seatop_move_tiling.c')
-rw-r--r--sway/input/seatop_move_tiling.c233
1 files changed, 177 insertions, 56 deletions
diff --git a/sway/input/seatop_move_tiling.c b/sway/input/seatop_move_tiling.c
index 704e7270..c525b77a 100644
--- a/sway/input/seatop_move_tiling.c
+++ b/sway/input/seatop_move_tiling.c
@@ -1,8 +1,7 @@
1#define _POSIX_C_SOURCE 200809L
2#include <limits.h> 1#include <limits.h>
3#include <wlr/types/wlr_cursor.h> 2#include <wlr/types/wlr_cursor.h>
4#include <wlr/util/edges.h> 3#include <wlr/util/edges.h>
5#include "sway/desktop.h" 4#include "sway/desktop/transaction.h"
6#include "sway/input/cursor.h" 5#include "sway/input/cursor.h"
7#include "sway/input/seat.h" 6#include "sway/input/seat.h"
8#include "sway/ipc-server.h" 7#include "sway/ipc-server.h"
@@ -15,31 +14,25 @@
15// Thickness of the dropzone when dragging to the edge of a layout container 14// Thickness of the dropzone when dragging to the edge of a layout container
16#define DROP_LAYOUT_BORDER 30 15#define DROP_LAYOUT_BORDER 30
17 16
17// Thickness of indicator when dropping onto a titlebar. This should be a
18// multiple of 2.
19#define DROP_SPLIT_INDICATOR 10
20
18struct seatop_move_tiling_event { 21struct seatop_move_tiling_event {
19 struct sway_container *con; 22 struct sway_container *con;
20 struct sway_node *target_node; 23 struct sway_node *target_node;
21 enum wlr_edges target_edge; 24 enum wlr_edges target_edge;
22 struct wlr_box drop_box;
23 double ref_lx, ref_ly; // cursor's x/y at start of op 25 double ref_lx, ref_ly; // cursor's x/y at start of op
24 bool threshold_reached; 26 bool threshold_reached;
27 bool split_target;
28 bool insert_after_target;
29 struct wlr_scene_rect *indicator_rect;
25}; 30};
26 31
27static void handle_render(struct sway_seat *seat, 32static void handle_end(struct sway_seat *seat) {
28 struct sway_output *output, pixman_region32_t *damage) {
29 struct seatop_move_tiling_event *e = seat->seatop_data; 33 struct seatop_move_tiling_event *e = seat->seatop_data;
30 if (!e->threshold_reached) { 34 wlr_scene_node_destroy(&e->indicator_rect->node);
31 return; 35 e->indicator_rect = NULL;
32 }
33 if (e->target_node && node_get_output(e->target_node) == output) {
34 float color[4];
35 memcpy(&color, config->border_colors.focused.indicator,
36 sizeof(float) * 4);
37 premultiply_alpha(color, 0.5);
38 struct wlr_box box;
39 memcpy(&box, &e->drop_box, sizeof(struct wlr_box));
40 scale_box(&box, output->wlr_output->scale);
41 render_rect(output, damage, &box, color);
42 }
43} 36}
44 37
45static void handle_motion_prethreshold(struct sway_seat *seat) { 38static void handle_motion_prethreshold(struct sway_seat *seat) {
@@ -60,6 +53,7 @@ static void handle_motion_prethreshold(struct sway_seat *seat) {
60 53
61 // If the threshold has been exceeded, start the actual drag 54 // If the threshold has been exceeded, start the actual drag
62 if ((cx - sx) * (cx - sx) + (cy - sy) * (cy - sy) > threshold) { 55 if ((cx - sx) * (cx - sx) + (cy - sy) * (cy - sy) > threshold) {
56 wlr_scene_node_set_enabled(&e->indicator_rect->node, true);
63 e->threshold_reached = true; 57 e->threshold_reached = true;
64 cursor_set_image(seat->cursor, "grab", NULL); 58 cursor_set_image(seat->cursor, "grab", NULL);
65 } 59 }
@@ -91,15 +85,86 @@ static void resize_box(struct wlr_box *box, enum wlr_edges edge,
91 } 85 }
92} 86}
93 87
88static void split_border(double pos, int offset, int len, int n_children,
89 int avoid, int *out_pos, bool *out_after) {
90 int region = 2 * n_children * (pos - offset) / len;
91 // If the cursor is over the right side of a left-adjacent titlebar, or the
92 // left side of a right-adjacent titlebar, it's position when dropped will
93 // be the same. To avoid this, shift the region for adjacent containers.
94 if (avoid >= 0) {
95 if (region == 2 * avoid - 1 || region == 2 * avoid) {
96 region--;
97 } else if (region == 2 * avoid + 1 || region == 2 * avoid + 2) {
98 region++;
99 }
100 }
101
102 int child_index = (region + 1) / 2;
103 *out_after = region % 2;
104 // When dropping at the beginning or end of a container, show the drop
105 // region within the container boundary, otherwise show it on top of the
106 // border between two titlebars.
107 if (child_index == 0) {
108 *out_pos = offset;
109 } else if (child_index == n_children) {
110 *out_pos = offset + len - DROP_SPLIT_INDICATOR;
111 } else {
112 *out_pos = offset + child_index * len / n_children -
113 DROP_SPLIT_INDICATOR / 2;
114 }
115}
116
117static bool split_titlebar(struct sway_node *node, struct sway_container *avoid,
118 struct wlr_cursor *cursor, struct wlr_box *title_box, bool *after) {
119 struct sway_container *con = node->sway_container;
120 struct sway_node *parent = &con->pending.parent->node;
121 int title_height = container_titlebar_height();
122 struct wlr_box box;
123 int n_children, avoid_index;
124 enum sway_container_layout layout =
125 parent ? node_get_layout(parent) : L_NONE;
126 if (layout == L_TABBED || layout == L_STACKED) {
127 node_get_box(parent, &box);
128 n_children = node_get_children(parent)->length;
129 avoid_index = list_find(node_get_children(parent), avoid);
130 } else {
131 node_get_box(node, &box);
132 n_children = 1;
133 avoid_index = -1;
134 }
135 if (layout == L_STACKED && cursor->y < box.y + title_height * n_children) {
136 // Drop into stacked titlebars.
137 title_box->width = box.width;
138 title_box->height = DROP_SPLIT_INDICATOR;
139 title_box->x = box.x;
140 split_border(cursor->y, box.y, title_height * n_children,
141 n_children, avoid_index, &title_box->y, after);
142 return true;
143 } else if (layout != L_STACKED && cursor->y < box.y + title_height) {
144 // Drop into side-by-side titlebars.
145 title_box->width = DROP_SPLIT_INDICATOR;
146 title_box->height = title_height;
147 title_box->y = box.y;
148 split_border(cursor->x, box.x, box.width, n_children,
149 avoid_index, &title_box->x, after);
150 return true;
151 }
152 return false;
153}
154
155static void update_indicator(struct seatop_move_tiling_event *e, struct wlr_box *box) {
156 wlr_scene_node_set_position(&e->indicator_rect->node, box->x, box->y);
157 wlr_scene_rect_set_size(e->indicator_rect, box->width, box->height);
158}
159
94static void handle_motion_postthreshold(struct sway_seat *seat) { 160static void handle_motion_postthreshold(struct sway_seat *seat) {
95 struct seatop_move_tiling_event *e = seat->seatop_data; 161 struct seatop_move_tiling_event *e = seat->seatop_data;
162 e->split_target = false;
96 struct wlr_surface *surface = NULL; 163 struct wlr_surface *surface = NULL;
97 double sx, sy; 164 double sx, sy;
98 struct sway_cursor *cursor = seat->cursor; 165 struct sway_cursor *cursor = seat->cursor;
99 struct sway_node *node = node_at_coords(seat, 166 struct sway_node *node = node_at_coords(seat,
100 cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); 167 cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy);
101 // Damage the old location
102 desktop_damage_box(&e->drop_box);
103 168
104 if (!node) { 169 if (!node) {
105 // Eg. hovered over a layer surface such as swaybar 170 // Eg. hovered over a layer surface such as swaybar
@@ -112,41 +177,77 @@ static void handle_motion_postthreshold(struct sway_seat *seat) {
112 // Empty workspace 177 // Empty workspace
113 e->target_node = node; 178 e->target_node = node;
114 e->target_edge = WLR_EDGE_NONE; 179 e->target_edge = WLR_EDGE_NONE;
115 workspace_get_box(node->sway_workspace, &e->drop_box); 180
116 desktop_damage_box(&e->drop_box); 181 struct wlr_box drop_box;
182 workspace_get_box(node->sway_workspace, &drop_box);
183 update_indicator(e, &drop_box);
117 return; 184 return;
118 } 185 }
119 186
120 // Deny moving within own workspace if this is the only child 187 // Deny moving within own workspace if this is the only child
121 struct sway_container *con = node->sway_container; 188 struct sway_container *con = node->sway_container;
122 if (workspace_num_tiling_views(e->con->workspace) == 1 && 189 if (workspace_num_tiling_views(e->con->pending.workspace) == 1 &&
123 con->workspace == e->con->workspace) { 190 con->pending.workspace == e->con->pending.workspace) {
124 e->target_node = NULL; 191 e->target_node = NULL;
125 e->target_edge = WLR_EDGE_NONE; 192 e->target_edge = WLR_EDGE_NONE;
126 return; 193 return;
127 } 194 }
128 195
196 struct wlr_box drop_box = {
197 .x = con->pending.content_x,
198 .y = con->pending.content_y,
199 .width = con->pending.content_width,
200 .height = con->pending.content_height,
201 };
202
203 // Check if the cursor is over a tilebar only if the destination
204 // container is not a descendant of the source container.
205 if (!surface && !container_has_ancestor(con, e->con) &&
206 split_titlebar(node, e->con, cursor->cursor,
207 &drop_box, &e->insert_after_target)) {
208 // Don't allow dropping over the source container's titlebar
209 // to give users a chance to cancel a drag operation.
210 if (con == e->con) {
211 e->target_node = NULL;
212 } else {
213 e->target_node = node;
214 e->split_target = true;
215 }
216 e->target_edge = WLR_EDGE_NONE;
217 update_indicator(e, &drop_box);
218 return;
219 }
220
129 // Traverse the ancestors, trying to find a layout container perpendicular 221 // Traverse the ancestors, trying to find a layout container perpendicular
130 // to the edge. Eg. close to the top or bottom of a horiz layout. 222 // to the edge. Eg. close to the top or bottom of a horiz layout.
223 int thresh_top = con->pending.content_y + DROP_LAYOUT_BORDER;
224 int thresh_bottom = con->pending.content_y +
225 con->pending.content_height - DROP_LAYOUT_BORDER;
226 int thresh_left = con->pending.content_x + DROP_LAYOUT_BORDER;
227 int thresh_right = con->pending.content_x +
228 con->pending.content_width - DROP_LAYOUT_BORDER;
131 while (con) { 229 while (con) {
132 enum wlr_edges edge = WLR_EDGE_NONE; 230 enum wlr_edges edge = WLR_EDGE_NONE;
133 enum sway_container_layout layout = container_parent_layout(con); 231 enum sway_container_layout layout = container_parent_layout(con);
134 struct wlr_box parent; 232 struct wlr_box box;
135 con->parent ? container_get_box(con->parent, &parent) : 233 node_get_box(node_get_parent(&con->node), &box);
136 workspace_get_box(con->workspace, &parent);
137 if (layout == L_HORIZ || layout == L_TABBED) { 234 if (layout == L_HORIZ || layout == L_TABBED) {
138 if (cursor->cursor->y < parent.y + DROP_LAYOUT_BORDER) { 235 if (cursor->cursor->y < thresh_top) {
139 edge = WLR_EDGE_TOP; 236 edge = WLR_EDGE_TOP;
140 } else if (cursor->cursor->y > parent.y + parent.height 237 box.height = thresh_top - box.y;
141 - DROP_LAYOUT_BORDER) { 238 } else if (cursor->cursor->y > thresh_bottom) {
142 edge = WLR_EDGE_BOTTOM; 239 edge = WLR_EDGE_BOTTOM;
240 box.height = box.y + box.height - thresh_bottom;
241 box.y = thresh_bottom;
143 } 242 }
144 } else if (layout == L_VERT || layout == L_STACKED) { 243 } else if (layout == L_VERT || layout == L_STACKED) {
145 if (cursor->cursor->x < parent.x + DROP_LAYOUT_BORDER) { 244 if (cursor->cursor->x < thresh_left) {
146 edge = WLR_EDGE_LEFT; 245 edge = WLR_EDGE_LEFT;
147 } else if (cursor->cursor->x > parent.x + parent.width 246 box.width = thresh_left - box.x;
148 - DROP_LAYOUT_BORDER) { 247 } else if (cursor->cursor->x > thresh_right) {
149 edge = WLR_EDGE_RIGHT; 248 edge = WLR_EDGE_RIGHT;
249 box.width = box.x + box.width - thresh_right;
250 box.x = thresh_right;
150 } 251 }
151 } 252 }
152 if (edge) { 253 if (edge) {
@@ -155,12 +256,10 @@ static void handle_motion_postthreshold(struct sway_seat *seat) {
155 e->target_node = node_get_parent(e->target_node); 256 e->target_node = node_get_parent(e->target_node);
156 } 257 }
157 e->target_edge = edge; 258 e->target_edge = edge;
158 node_get_box(e->target_node, &e->drop_box); 259 update_indicator(e, &box);
159 resize_box(&e->drop_box, edge, DROP_LAYOUT_BORDER);
160 desktop_damage_box(&e->drop_box);
161 return; 260 return;
162 } 261 }
163 con = con->parent; 262 con = con->pending.parent;
164 } 263 }
165 264
166 // Use the hovered view - but we must be over the actual surface 265 // Use the hovered view - but we must be over the actual surface
@@ -173,23 +272,23 @@ static void handle_motion_postthreshold(struct sway_seat *seat) {
173 } 272 }
174 273
175 // Find the closest edge 274 // Find the closest edge
176 size_t thickness = fmin(con->content_width, con->content_height) * 0.3; 275 size_t thickness = fmin(con->pending.content_width, con->pending.content_height) * 0.3;
177 size_t closest_dist = INT_MAX; 276 size_t closest_dist = INT_MAX;
178 size_t dist; 277 size_t dist;
179 e->target_edge = WLR_EDGE_NONE; 278 e->target_edge = WLR_EDGE_NONE;
180 if ((dist = cursor->cursor->y - con->y) < closest_dist) { 279 if ((dist = cursor->cursor->y - con->pending.y) < closest_dist) {
181 closest_dist = dist; 280 closest_dist = dist;
182 e->target_edge = WLR_EDGE_TOP; 281 e->target_edge = WLR_EDGE_TOP;
183 } 282 }
184 if ((dist = cursor->cursor->x - con->x) < closest_dist) { 283 if ((dist = cursor->cursor->x - con->pending.x) < closest_dist) {
185 closest_dist = dist; 284 closest_dist = dist;
186 e->target_edge = WLR_EDGE_LEFT; 285 e->target_edge = WLR_EDGE_LEFT;
187 } 286 }
188 if ((dist = con->x + con->width - cursor->cursor->x) < closest_dist) { 287 if ((dist = con->pending.x + con->pending.width - cursor->cursor->x) < closest_dist) {
189 closest_dist = dist; 288 closest_dist = dist;
190 e->target_edge = WLR_EDGE_RIGHT; 289 e->target_edge = WLR_EDGE_RIGHT;
191 } 290 }
192 if ((dist = con->y + con->height - cursor->cursor->y) < closest_dist) { 291 if ((dist = con->pending.y + con->pending.height - cursor->cursor->y) < closest_dist) {
193 closest_dist = dist; 292 closest_dist = dist;
194 e->target_edge = WLR_EDGE_BOTTOM; 293 e->target_edge = WLR_EDGE_BOTTOM;
195 } 294 }
@@ -199,12 +298,8 @@ static void handle_motion_postthreshold(struct sway_seat *seat) {
199 } 298 }
200 299
201 e->target_node = node; 300 e->target_node = node;
202 e->drop_box.x = con->content_x; 301 resize_box(&drop_box, e->target_edge, thickness);
203 e->drop_box.y = con->content_y; 302 update_indicator(e, &drop_box);
204 e->drop_box.width = con->content_width;
205 e->drop_box.height = con->content_height;
206 resize_box(&e->drop_box, e->target_edge, thickness);
207 desktop_damage_box(&e->drop_box);
208} 303}
209 304
210static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { 305static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) {
@@ -214,6 +309,7 @@ static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) {
214 } else { 309 } else {
215 handle_motion_prethreshold(seat); 310 handle_motion_prethreshold(seat);
216 } 311 }
312 transaction_commit_dirty();
217} 313}
218 314
219static bool is_parallel(enum sway_container_layout layout, 315static bool is_parallel(enum sway_container_layout layout,
@@ -232,14 +328,15 @@ static void finalize_move(struct sway_seat *seat) {
232 } 328 }
233 329
234 struct sway_container *con = e->con; 330 struct sway_container *con = e->con;
235 struct sway_container *old_parent = con->parent; 331 struct sway_container *old_parent = con->pending.parent;
236 struct sway_workspace *old_ws = con->workspace; 332 struct sway_workspace *old_ws = con->pending.workspace;
237 struct sway_node *target_node = e->target_node; 333 struct sway_node *target_node = e->target_node;
238 struct sway_workspace *new_ws = target_node->type == N_WORKSPACE ? 334 struct sway_workspace *new_ws = target_node->type == N_WORKSPACE ?
239 target_node->sway_workspace : target_node->sway_container->workspace; 335 target_node->sway_workspace : target_node->sway_container->pending.workspace;
240 enum wlr_edges edge = e->target_edge; 336 enum wlr_edges edge = e->target_edge;
241 int after = edge != WLR_EDGE_TOP && edge != WLR_EDGE_LEFT; 337 int after = edge != WLR_EDGE_TOP && edge != WLR_EDGE_LEFT;
242 bool swap = edge == WLR_EDGE_NONE && target_node->type == N_CONTAINER; 338 bool swap = edge == WLR_EDGE_NONE && target_node->type == N_CONTAINER &&
339 !e->split_target;
243 340
244 if (!swap) { 341 if (!swap) {
245 container_detach(con); 342 container_detach(con);
@@ -248,6 +345,14 @@ static void finalize_move(struct sway_seat *seat) {
248 // Moving container into empty workspace 345 // Moving container into empty workspace
249 if (target_node->type == N_WORKSPACE && edge == WLR_EDGE_NONE) { 346 if (target_node->type == N_WORKSPACE && edge == WLR_EDGE_NONE) {
250 con = workspace_add_tiling(new_ws, con); 347 con = workspace_add_tiling(new_ws, con);
348 } else if (e->split_target) {
349 struct sway_container *target = target_node->sway_container;
350 enum sway_container_layout layout = container_parent_layout(target);
351 if (layout != L_TABBED && layout != L_STACKED) {
352 container_split(target, L_TABBED);
353 }
354 container_add_sibling(target, con, e->insert_after_target);
355 ipc_event_window(con, "move");
251 } else if (target_node->type == N_CONTAINER) { 356 } else if (target_node->type == N_CONTAINER) {
252 // Moving container before/after another 357 // Moving container before/after another
253 struct sway_container *target = target_node->sway_container; 358 struct sway_container *target = target_node->sway_container;
@@ -283,8 +388,8 @@ static void finalize_move(struct sway_seat *seat) {
283 int index = list_find(siblings, con); 388 int index = list_find(siblings, con);
284 struct sway_container *sibling = index == 0 ? 389 struct sway_container *sibling = index == 0 ?
285 siblings->items[1] : siblings->items[index - 1]; 390 siblings->items[1] : siblings->items[index - 1];
286 con->width = sibling->width; 391 con->pending.width = sibling->pending.width;
287 con->height = sibling->height; 392 con->pending.height = sibling->pending.height;
288 con->width_fraction = sibling->width_fraction; 393 con->width_fraction = sibling->width_fraction;
289 con->height_fraction = sibling->height_fraction; 394 con->height_fraction = sibling->height_fraction;
290 } 395 }
@@ -294,12 +399,13 @@ static void finalize_move(struct sway_seat *seat) {
294 arrange_workspace(new_ws); 399 arrange_workspace(new_ws);
295 } 400 }
296 401
402 transaction_commit_dirty();
297 seatop_begin_default(seat); 403 seatop_begin_default(seat);
298} 404}
299 405
300static void handle_button(struct sway_seat *seat, uint32_t time_msec, 406static void handle_button(struct sway_seat *seat, uint32_t time_msec,
301 struct wlr_input_device *device, uint32_t button, 407 struct wlr_input_device *device, uint32_t button,
302 enum wlr_button_state state) { 408 enum wl_pointer_button_state state) {
303 if (seat->cursor->pressed_button_count == 0) { 409 if (seat->cursor->pressed_button_count == 0) {
304 finalize_move(seat); 410 finalize_move(seat);
305 } 411 }
@@ -328,7 +434,7 @@ static const struct sway_seatop_impl seatop_impl = {
328 .pointer_motion = handle_pointer_motion, 434 .pointer_motion = handle_pointer_motion,
329 .tablet_tool_tip = handle_tablet_tool_tip, 435 .tablet_tool_tip = handle_tablet_tool_tip,
330 .unref = handle_unref, 436 .unref = handle_unref,
331 .render = handle_render, 437 .end = handle_end,
332}; 438};
333 439
334void seatop_begin_move_tiling_threshold(struct sway_seat *seat, 440void seatop_begin_move_tiling_threshold(struct sway_seat *seat,
@@ -340,6 +446,20 @@ void seatop_begin_move_tiling_threshold(struct sway_seat *seat,
340 if (!e) { 446 if (!e) {
341 return; 447 return;
342 } 448 }
449
450 const float *indicator = config->border_colors.focused.indicator;
451 float color[4] = {
452 indicator[0] * .5,
453 indicator[1] * .5,
454 indicator[2] * .5,
455 indicator[3] * .5,
456 };
457 e->indicator_rect = wlr_scene_rect_create(seat->scene_tree, 0, 0, color);
458 if (!e->indicator_rect) {
459 free(e);
460 return;
461 }
462
343 e->con = con; 463 e->con = con;
344 e->ref_lx = seat->cursor->cursor->x; 464 e->ref_lx = seat->cursor->cursor->x;
345 e->ref_ly = seat->cursor->cursor->y; 465 e->ref_ly = seat->cursor->cursor->y;
@@ -348,6 +468,7 @@ void seatop_begin_move_tiling_threshold(struct sway_seat *seat,
348 seat->seatop_data = e; 468 seat->seatop_data = e;
349 469
350 container_raise_floating(con); 470 container_raise_floating(con);
471 transaction_commit_dirty();
351 wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); 472 wlr_seat_pointer_notify_clear_focus(seat->wlr_seat);
352} 473}
353 474