diff options
Diffstat (limited to 'sway/input/seatop_move_tiling.c')
-rw-r--r-- | sway/input/seatop_move_tiling.c | 170 |
1 files changed, 141 insertions, 29 deletions
diff --git a/sway/input/seatop_move_tiling.c b/sway/input/seatop_move_tiling.c index 704e7270..223c6c08 100644 --- a/sway/input/seatop_move_tiling.c +++ b/sway/input/seatop_move_tiling.c | |||
@@ -3,6 +3,7 @@ | |||
3 | #include <wlr/types/wlr_cursor.h> | 3 | #include <wlr/types/wlr_cursor.h> |
4 | #include <wlr/util/edges.h> | 4 | #include <wlr/util/edges.h> |
5 | #include "sway/desktop.h" | 5 | #include "sway/desktop.h" |
6 | #include "sway/desktop/transaction.h" | ||
6 | #include "sway/input/cursor.h" | 7 | #include "sway/input/cursor.h" |
7 | #include "sway/input/seat.h" | 8 | #include "sway/input/seat.h" |
8 | #include "sway/ipc-server.h" | 9 | #include "sway/ipc-server.h" |
@@ -15,6 +16,10 @@ | |||
15 | // Thickness of the dropzone when dragging to the edge of a layout container | 16 | // Thickness of the dropzone when dragging to the edge of a layout container |
16 | #define DROP_LAYOUT_BORDER 30 | 17 | #define DROP_LAYOUT_BORDER 30 |
17 | 18 | ||
19 | // Thickness of indicator when dropping onto a titlebar. This should be a | ||
20 | // multiple of 2. | ||
21 | #define DROP_SPLIT_INDICATOR 10 | ||
22 | |||
18 | struct seatop_move_tiling_event { | 23 | struct seatop_move_tiling_event { |
19 | struct sway_container *con; | 24 | struct sway_container *con; |
20 | struct sway_node *target_node; | 25 | struct sway_node *target_node; |
@@ -22,6 +27,8 @@ struct seatop_move_tiling_event { | |||
22 | struct wlr_box drop_box; | 27 | struct wlr_box drop_box; |
23 | double ref_lx, ref_ly; // cursor's x/y at start of op | 28 | double ref_lx, ref_ly; // cursor's x/y at start of op |
24 | bool threshold_reached; | 29 | bool threshold_reached; |
30 | bool split_target; | ||
31 | bool insert_after_target; | ||
25 | }; | 32 | }; |
26 | 33 | ||
27 | static void handle_render(struct sway_seat *seat, | 34 | static void handle_render(struct sway_seat *seat, |
@@ -91,8 +98,76 @@ static void resize_box(struct wlr_box *box, enum wlr_edges edge, | |||
91 | } | 98 | } |
92 | } | 99 | } |
93 | 100 | ||
101 | static void split_border(double pos, int offset, int len, int n_children, | ||
102 | int avoid, int *out_pos, bool *out_after) { | ||
103 | int region = 2 * n_children * (pos - offset) / len; | ||
104 | // If the cursor is over the right side of a left-adjacent titlebar, or the | ||
105 | // left side of a right-adjacent titlebar, it's position when dropped will | ||
106 | // be the same. To avoid this, shift the region for adjacent containers. | ||
107 | if (avoid >= 0) { | ||
108 | if (region == 2 * avoid - 1 || region == 2 * avoid) { | ||
109 | region--; | ||
110 | } else if (region == 2 * avoid + 1 || region == 2 * avoid + 2) { | ||
111 | region++; | ||
112 | } | ||
113 | } | ||
114 | |||
115 | int child_index = (region + 1) / 2; | ||
116 | *out_after = region % 2; | ||
117 | // When dropping at the beginning or end of a container, show the drop | ||
118 | // region within the container boundary, otherwise show it on top of the | ||
119 | // border between two titlebars. | ||
120 | if (child_index == 0) { | ||
121 | *out_pos = offset; | ||
122 | } else if (child_index == n_children) { | ||
123 | *out_pos = offset + len - DROP_SPLIT_INDICATOR; | ||
124 | } else { | ||
125 | *out_pos = offset + child_index * len / n_children - | ||
126 | DROP_SPLIT_INDICATOR / 2; | ||
127 | } | ||
128 | } | ||
129 | |||
130 | static bool split_titlebar(struct sway_node *node, struct sway_container *avoid, | ||
131 | struct wlr_cursor *cursor, struct wlr_box *title_box, bool *after) { | ||
132 | struct sway_container *con = node->sway_container; | ||
133 | struct sway_node *parent = &con->pending.parent->node; | ||
134 | int title_height = container_titlebar_height(); | ||
135 | struct wlr_box box; | ||
136 | int n_children, avoid_index; | ||
137 | enum sway_container_layout layout = | ||
138 | parent ? node_get_layout(parent) : L_NONE; | ||
139 | if (layout == L_TABBED || layout == L_STACKED) { | ||
140 | node_get_box(parent, &box); | ||
141 | n_children = node_get_children(parent)->length; | ||
142 | avoid_index = list_find(node_get_children(parent), avoid); | ||
143 | } else { | ||
144 | node_get_box(node, &box); | ||
145 | n_children = 1; | ||
146 | avoid_index = -1; | ||
147 | } | ||
148 | if (layout == L_STACKED && cursor->y < box.y + title_height * n_children) { | ||
149 | // Drop into stacked titlebars. | ||
150 | title_box->width = box.width; | ||
151 | title_box->height = DROP_SPLIT_INDICATOR; | ||
152 | title_box->x = box.x; | ||
153 | split_border(cursor->y, box.y, title_height * n_children, | ||
154 | n_children, avoid_index, &title_box->y, after); | ||
155 | return true; | ||
156 | } else if (layout != L_STACKED && cursor->y < box.y + title_height) { | ||
157 | // Drop into side-by-side titlebars. | ||
158 | title_box->width = DROP_SPLIT_INDICATOR; | ||
159 | title_box->height = title_height; | ||
160 | title_box->y = box.y; | ||
161 | split_border(cursor->x, box.x, box.width, n_children, | ||
162 | avoid_index, &title_box->x, after); | ||
163 | return true; | ||
164 | } | ||
165 | return false; | ||
166 | } | ||
167 | |||
94 | static void handle_motion_postthreshold(struct sway_seat *seat) { | 168 | static void handle_motion_postthreshold(struct sway_seat *seat) { |
95 | struct seatop_move_tiling_event *e = seat->seatop_data; | 169 | struct seatop_move_tiling_event *e = seat->seatop_data; |
170 | e->split_target = false; | ||
96 | struct wlr_surface *surface = NULL; | 171 | struct wlr_surface *surface = NULL; |
97 | double sx, sy; | 172 | double sx, sy; |
98 | struct sway_cursor *cursor = seat->cursor; | 173 | struct sway_cursor *cursor = seat->cursor; |
@@ -119,34 +194,60 @@ static void handle_motion_postthreshold(struct sway_seat *seat) { | |||
119 | 194 | ||
120 | // Deny moving within own workspace if this is the only child | 195 | // Deny moving within own workspace if this is the only child |
121 | struct sway_container *con = node->sway_container; | 196 | struct sway_container *con = node->sway_container; |
122 | if (workspace_num_tiling_views(e->con->workspace) == 1 && | 197 | if (workspace_num_tiling_views(e->con->pending.workspace) == 1 && |
123 | con->workspace == e->con->workspace) { | 198 | con->pending.workspace == e->con->pending.workspace) { |
124 | e->target_node = NULL; | 199 | e->target_node = NULL; |
125 | e->target_edge = WLR_EDGE_NONE; | 200 | e->target_edge = WLR_EDGE_NONE; |
126 | return; | 201 | return; |
127 | } | 202 | } |
128 | 203 | ||
204 | // Check if the cursor is over a tilebar only if the destination | ||
205 | // container is not a descendant of the source container. | ||
206 | if (!surface && !container_has_ancestor(con, e->con) && | ||
207 | split_titlebar(node, e->con, cursor->cursor, | ||
208 | &e->drop_box, &e->insert_after_target)) { | ||
209 | // Don't allow dropping over the source container's titlebar | ||
210 | // to give users a chance to cancel a drag operation. | ||
211 | if (con == e->con) { | ||
212 | e->target_node = NULL; | ||
213 | } else { | ||
214 | e->target_node = node; | ||
215 | e->split_target = true; | ||
216 | } | ||
217 | e->target_edge = WLR_EDGE_NONE; | ||
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,11 @@ 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 | e->drop_box = box; |
159 | resize_box(&e->drop_box, edge, DROP_LAYOUT_BORDER); | ||
160 | desktop_damage_box(&e->drop_box); | 260 | desktop_damage_box(&e->drop_box); |
161 | return; | 261 | return; |
162 | } | 262 | } |
163 | con = con->parent; | 263 | con = con->pending.parent; |
164 | } | 264 | } |
165 | 265 | ||
166 | // Use the hovered view - but we must be over the actual surface | 266 | // Use the hovered view - but we must be over the actual surface |
@@ -173,23 +273,23 @@ static void handle_motion_postthreshold(struct sway_seat *seat) { | |||
173 | } | 273 | } |
174 | 274 | ||
175 | // Find the closest edge | 275 | // Find the closest edge |
176 | size_t thickness = fmin(con->content_width, con->content_height) * 0.3; | 276 | size_t thickness = fmin(con->pending.content_width, con->pending.content_height) * 0.3; |
177 | size_t closest_dist = INT_MAX; | 277 | size_t closest_dist = INT_MAX; |
178 | size_t dist; | 278 | size_t dist; |
179 | e->target_edge = WLR_EDGE_NONE; | 279 | e->target_edge = WLR_EDGE_NONE; |
180 | if ((dist = cursor->cursor->y - con->y) < closest_dist) { | 280 | if ((dist = cursor->cursor->y - con->pending.y) < closest_dist) { |
181 | closest_dist = dist; | 281 | closest_dist = dist; |
182 | e->target_edge = WLR_EDGE_TOP; | 282 | e->target_edge = WLR_EDGE_TOP; |
183 | } | 283 | } |
184 | if ((dist = cursor->cursor->x - con->x) < closest_dist) { | 284 | if ((dist = cursor->cursor->x - con->pending.x) < closest_dist) { |
185 | closest_dist = dist; | 285 | closest_dist = dist; |
186 | e->target_edge = WLR_EDGE_LEFT; | 286 | e->target_edge = WLR_EDGE_LEFT; |
187 | } | 287 | } |
188 | if ((dist = con->x + con->width - cursor->cursor->x) < closest_dist) { | 288 | if ((dist = con->pending.x + con->pending.width - cursor->cursor->x) < closest_dist) { |
189 | closest_dist = dist; | 289 | closest_dist = dist; |
190 | e->target_edge = WLR_EDGE_RIGHT; | 290 | e->target_edge = WLR_EDGE_RIGHT; |
191 | } | 291 | } |
192 | if ((dist = con->y + con->height - cursor->cursor->y) < closest_dist) { | 292 | if ((dist = con->pending.y + con->pending.height - cursor->cursor->y) < closest_dist) { |
193 | closest_dist = dist; | 293 | closest_dist = dist; |
194 | e->target_edge = WLR_EDGE_BOTTOM; | 294 | e->target_edge = WLR_EDGE_BOTTOM; |
195 | } | 295 | } |
@@ -199,10 +299,10 @@ static void handle_motion_postthreshold(struct sway_seat *seat) { | |||
199 | } | 299 | } |
200 | 300 | ||
201 | e->target_node = node; | 301 | e->target_node = node; |
202 | e->drop_box.x = con->content_x; | 302 | e->drop_box.x = con->pending.content_x; |
203 | e->drop_box.y = con->content_y; | 303 | e->drop_box.y = con->pending.content_y; |
204 | e->drop_box.width = con->content_width; | 304 | e->drop_box.width = con->pending.content_width; |
205 | e->drop_box.height = con->content_height; | 305 | e->drop_box.height = con->pending.content_height; |
206 | resize_box(&e->drop_box, e->target_edge, thickness); | 306 | resize_box(&e->drop_box, e->target_edge, thickness); |
207 | desktop_damage_box(&e->drop_box); | 307 | desktop_damage_box(&e->drop_box); |
208 | } | 308 | } |
@@ -214,6 +314,7 @@ static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { | |||
214 | } else { | 314 | } else { |
215 | handle_motion_prethreshold(seat); | 315 | handle_motion_prethreshold(seat); |
216 | } | 316 | } |
317 | transaction_commit_dirty(); | ||
217 | } | 318 | } |
218 | 319 | ||
219 | static bool is_parallel(enum sway_container_layout layout, | 320 | static bool is_parallel(enum sway_container_layout layout, |
@@ -232,14 +333,15 @@ static void finalize_move(struct sway_seat *seat) { | |||
232 | } | 333 | } |
233 | 334 | ||
234 | struct sway_container *con = e->con; | 335 | struct sway_container *con = e->con; |
235 | struct sway_container *old_parent = con->parent; | 336 | struct sway_container *old_parent = con->pending.parent; |
236 | struct sway_workspace *old_ws = con->workspace; | 337 | struct sway_workspace *old_ws = con->pending.workspace; |
237 | struct sway_node *target_node = e->target_node; | 338 | struct sway_node *target_node = e->target_node; |
238 | struct sway_workspace *new_ws = target_node->type == N_WORKSPACE ? | 339 | struct sway_workspace *new_ws = target_node->type == N_WORKSPACE ? |
239 | target_node->sway_workspace : target_node->sway_container->workspace; | 340 | target_node->sway_workspace : target_node->sway_container->pending.workspace; |
240 | enum wlr_edges edge = e->target_edge; | 341 | enum wlr_edges edge = e->target_edge; |
241 | int after = edge != WLR_EDGE_TOP && edge != WLR_EDGE_LEFT; | 342 | int after = edge != WLR_EDGE_TOP && edge != WLR_EDGE_LEFT; |
242 | bool swap = edge == WLR_EDGE_NONE && target_node->type == N_CONTAINER; | 343 | bool swap = edge == WLR_EDGE_NONE && target_node->type == N_CONTAINER && |
344 | !e->split_target; | ||
243 | 345 | ||
244 | if (!swap) { | 346 | if (!swap) { |
245 | container_detach(con); | 347 | container_detach(con); |
@@ -248,6 +350,14 @@ static void finalize_move(struct sway_seat *seat) { | |||
248 | // Moving container into empty workspace | 350 | // Moving container into empty workspace |
249 | if (target_node->type == N_WORKSPACE && edge == WLR_EDGE_NONE) { | 351 | if (target_node->type == N_WORKSPACE && edge == WLR_EDGE_NONE) { |
250 | con = workspace_add_tiling(new_ws, con); | 352 | con = workspace_add_tiling(new_ws, con); |
353 | } else if (e->split_target) { | ||
354 | struct sway_container *target = target_node->sway_container; | ||
355 | enum sway_container_layout layout = container_parent_layout(target); | ||
356 | if (layout != L_TABBED && layout != L_STACKED) { | ||
357 | container_split(target, L_TABBED); | ||
358 | } | ||
359 | container_add_sibling(target, con, e->insert_after_target); | ||
360 | ipc_event_window(con, "move"); | ||
251 | } else if (target_node->type == N_CONTAINER) { | 361 | } else if (target_node->type == N_CONTAINER) { |
252 | // Moving container before/after another | 362 | // Moving container before/after another |
253 | struct sway_container *target = target_node->sway_container; | 363 | struct sway_container *target = target_node->sway_container; |
@@ -283,8 +393,8 @@ static void finalize_move(struct sway_seat *seat) { | |||
283 | int index = list_find(siblings, con); | 393 | int index = list_find(siblings, con); |
284 | struct sway_container *sibling = index == 0 ? | 394 | struct sway_container *sibling = index == 0 ? |
285 | siblings->items[1] : siblings->items[index - 1]; | 395 | siblings->items[1] : siblings->items[index - 1]; |
286 | con->width = sibling->width; | 396 | con->pending.width = sibling->pending.width; |
287 | con->height = sibling->height; | 397 | con->pending.height = sibling->pending.height; |
288 | con->width_fraction = sibling->width_fraction; | 398 | con->width_fraction = sibling->width_fraction; |
289 | con->height_fraction = sibling->height_fraction; | 399 | con->height_fraction = sibling->height_fraction; |
290 | } | 400 | } |
@@ -294,6 +404,7 @@ static void finalize_move(struct sway_seat *seat) { | |||
294 | arrange_workspace(new_ws); | 404 | arrange_workspace(new_ws); |
295 | } | 405 | } |
296 | 406 | ||
407 | transaction_commit_dirty(); | ||
297 | seatop_begin_default(seat); | 408 | seatop_begin_default(seat); |
298 | } | 409 | } |
299 | 410 | ||
@@ -348,6 +459,7 @@ void seatop_begin_move_tiling_threshold(struct sway_seat *seat, | |||
348 | seat->seatop_data = e; | 459 | seat->seatop_data = e; |
349 | 460 | ||
350 | container_raise_floating(con); | 461 | container_raise_floating(con); |
462 | transaction_commit_dirty(); | ||
351 | wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); | 463 | wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); |
352 | } | 464 | } |
353 | 465 | ||