aboutsummaryrefslogtreecommitdiffstats
path: root/sway/commands/move.c
diff options
context:
space:
mode:
authorLibravatar Ronan Pigott <rpigott@berkeley.edu>2020-11-01 16:23:03 -0700
committerLibravatar Tudor Brindus <me@tbrindus.ca>2020-12-20 00:58:42 -0500
commite95c299f0a724b7290a56f3ef81c9f9565bc9231 (patch)
tree61754d3d4578088a4a6526071fe9b013d9b47665 /sway/commands/move.c
parentChange workspace_layout to match i3 behavior (diff)
downloadsway-e95c299f0a724b7290a56f3ef81c9f9565bc9231.tar.gz
sway-e95c299f0a724b7290a56f3ef81c9f9565bc9231.tar.zst
sway-e95c299f0a724b7290a56f3ef81c9f9565bc9231.zip
commands/move: rework container_move_in_direction
This changes the move command to better match i3 behavior after the layout changes. workspace_rejigger handled the case where containers would escape their workspace in an orthogonal move by changing the layout to accomodate them, but this case is now handled within the loop.
Diffstat (limited to 'sway/commands/move.c')
-rw-r--r--sway/commands/move.c209
1 files changed, 112 insertions, 97 deletions
diff --git a/sway/commands/move.c b/sway/commands/move.c
index 32adf0bf..ecad9863 100644
--- a/sway/commands/move.c
+++ b/sway/commands/move.c
@@ -263,126 +263,141 @@ static void container_move_to_container(struct sway_container *container,
263 } 263 }
264} 264}
265 265
266/* Takes one child, sets it aside, wraps the rest of the children in a new 266static bool container_move_to_next_output(struct sway_container *container,
267 * container, switches the layout of the workspace, and drops the child back in. 267 struct sway_output *output, enum wlr_direction move_dir) {
268 * In other words, rejigger it. */ 268 struct sway_output *next_output =
269static void workspace_rejigger(struct sway_workspace *ws, 269 output_get_in_direction(output, move_dir);
270 struct sway_container *child, enum wlr_direction move_dir) { 270 if (next_output) {
271 if (!child->parent && ws->tiling->length == 1) { 271 struct sway_workspace *ws = output_get_active_workspace(next_output);
272 ws->layout = 272 if (!sway_assert(ws, "Expected output to have a workspace")) {
273 move_dir == WLR_DIRECTION_LEFT || move_dir == WLR_DIRECTION_RIGHT ? 273 return false;
274 L_HORIZ : L_VERT; 274 }
275 workspace_update_representation(ws); 275 switch (container->fullscreen_mode) {
276 return; 276 case FULLSCREEN_NONE:
277 container_move_to_workspace_from_direction(container, ws, move_dir);
278 return true;
279 case FULLSCREEN_WORKSPACE:
280 container_move_to_workspace(container, ws);
281 return true;
282 case FULLSCREEN_GLOBAL:
283 return false;
284 }
277 } 285 }
278 container_detach(child); 286 return false;
279 struct sway_container *new_parent = workspace_wrap_children(ws);
280
281 int index =
282 move_dir == WLR_DIRECTION_LEFT || move_dir == WLR_DIRECTION_UP ? 0 : 1;
283 workspace_insert_tiling(ws, child, index);
284 container_flatten(new_parent);
285 ws->layout =
286 move_dir == WLR_DIRECTION_LEFT || move_dir == WLR_DIRECTION_RIGHT ?
287 L_HORIZ : L_VERT;
288 workspace_update_representation(ws);
289 child->width = child->height = 0;
290} 287}
291 288
292// Returns true if moved 289// Returns true if moved
293static bool container_move_in_direction(struct sway_container *container, 290static bool container_move_in_direction(struct sway_container *container,
294 enum wlr_direction move_dir) { 291 enum wlr_direction move_dir) {
295 // If moving a fullscreen view, only consider outputs 292 // If moving a fullscreen view, only consider outputs
296 if (container->fullscreen_mode == FULLSCREEN_WORKSPACE) { 293 switch (container->fullscreen_mode) {
297 struct sway_output *new_output = 294 case FULLSCREEN_NONE:
298 output_get_in_direction(container->workspace->output, move_dir); 295 break;
299 if (!new_output) { 296 case FULLSCREEN_WORKSPACE:
300 return false; 297 return container_move_to_next_output(container,
301 } 298 container->workspace->output, move_dir);
302 struct sway_workspace *ws = output_get_active_workspace(new_output); 299 case FULLSCREEN_GLOBAL:
303 if (!sway_assert(ws, "Expected output to have a workspace")) {
304 return false;
305 }
306 container_move_to_workspace(container, ws);
307 return true;
308 }
309 if (container->fullscreen_mode == FULLSCREEN_GLOBAL) {
310 return false; 300 return false;
311 } 301 }
312 302
313 // Look for a suitable *container* sibling or parent.
314 // The below loop stops once we hit the workspace because current->parent
315 // is NULL for the topmost containers in a workspace.
316 struct sway_container *current = container;
317 int offs = 303 int offs =
318 move_dir == WLR_DIRECTION_LEFT || move_dir == WLR_DIRECTION_UP ? -1 : 1; 304 move_dir == WLR_DIRECTION_LEFT || move_dir == WLR_DIRECTION_UP ? -1 : 1;
305 int index = -1;
306 int desired = -1;
307 list_t *siblings = NULL;
308 struct sway_container *target = NULL;
309
310 // Look for a suitable ancestor of the container to move within
311 struct sway_container *ancestor = NULL;
312 struct sway_container *current = container;
313 bool wrapped = false;
314 while (!ancestor) {
315 // Don't allow containers to move out of their
316 // fullscreen or floating parent
317 if (current->fullscreen_mode || container_is_floating(current)) {
318 return false;
319 }
319 320
320 while (current) { 321 enum sway_container_layout parent_layout = container_parent_layout(current);
321 list_t *siblings = container_get_siblings(current); 322 if (!is_parallel(parent_layout, move_dir)) {
322 if (siblings) { 323 if (!current->parent) {
323 enum sway_container_layout layout = container_parent_layout(current); 324 // No parallel parent, so we reorient the workspace
324 int index = list_find(siblings, current); 325 current = workspace_wrap_children(current->workspace);
325 int desired = index + offs; 326 current->workspace->layout =
326 327 move_dir == WLR_DIRECTION_LEFT ||
327 // Don't allow containers to move out of their 328 move_dir == WLR_DIRECTION_RIGHT ?
328 // fullscreen or floating parent 329 L_HORIZ : L_VERT;
329 if (current->fullscreen_mode || container_is_floating(current)) { 330 container->height = container->width = 0;
330 return false; 331 container->height_fraction = container->width_fraction = 0;
332 workspace_update_representation(current->workspace);
333 wrapped = true;
334 } else {
335 // Keep looking for a parallel parent
336 current = current->parent;
331 } 337 }
338 continue;
339 }
332 340
333 if (is_parallel(layout, move_dir)) { 341 // Only scratchpad hidden containers don't have siblings
334 if (desired == -1 || desired == siblings->length) { 342 // so siblings != NULL here
335 if (current->parent == container->parent) { 343 siblings = container_get_siblings(current);
336 current = current->parent; 344 index = list_find(siblings, current);
337 continue; 345 desired = index + offs;
338 } else { 346 target = desired == -1 || desired == siblings->length ?
339 // Reparenting 347 NULL : siblings->items[desired];
340 if (current->parent) { 348
341 container_insert_child(current->parent, container, 349 // If the move is simple we can complete it here early
342 index + (offs < 0 ? 0 : 1)); 350 if (current == container) {
343 } else { 351 if (target) {
344 workspace_insert_tiling(current->workspace, container, 352 // Container will swap with or descend into its neighbor
345 index + (offs < 0 ? 0 : 1)); 353 container_move_to_container_from_direction(container,
346 } 354 target, move_dir);
347 return true; 355 return true;
348 } 356 } else if (!container->parent) {
349 } else { 357 // Container is at workspace level so we move it to the
350 // Container can move within its siblings 358 // next workspace if possible
351 container_move_to_container_from_direction(container, 359 return container_move_to_next_output(container,
352 siblings->items[desired], move_dir); 360 current->workspace->output, move_dir);
353 return true; 361 } else {
354 } 362 // Container has escaped its immediate parallel parent
363 current = current->parent;
364 continue;
355 } 365 }
356 } 366 }
357 367
358 current = current->parent; 368 // We found a suitable ancestor, the loop will end
369 ancestor = current;
359 } 370 }
360 371
361 // Maybe rejigger the workspace 372 if (target) {
362 struct sway_workspace *ws = container->workspace; 373 // Container will move in with its cousin
363 if (ws) { 374 container_move_to_container_from_direction(container,
364 if (!is_parallel(ws->layout, move_dir)) { 375 target, move_dir);
365 workspace_rejigger(ws, container, move_dir); 376 return true;
366 return true; 377 } else if (!wrapped && !container->parent->parent &&
367 } else if (ws->layout == L_TABBED || ws->layout == L_STACKED) { 378 container->parent->children->length == 1) {
368 workspace_rejigger(ws, container, move_dir); 379 // Treat singleton children as if they are at workspace level like i3
369 return true; 380 // https://github.com/i3/i3/blob/1d9160f2d247dbaa83fb62f02fd7041dec767fc2/src/move.c#L367
381 return container_move_to_next_output(container,
382 ancestor->workspace->output, move_dir);
383 } else {
384 // Container will be promoted
385 struct sway_container *old_parent = container->parent;
386 if (ancestor->parent) {
387 // Container will move in with its parent
388 container_insert_child(ancestor->parent, container,
389 index + (offs < 0 ? 0 : 1));
390 } else {
391 // Container will move to workspace level,
392 // may be re-split by workspace_layout
393 workspace_insert_tiling(ancestor->workspace, container,
394 index + (offs < 0 ? 0 : 1));
370 } 395 }
371 396 if (old_parent) {
372 // Try adjacent output 397 container_reap_empty(old_parent);
373 struct sway_output *output =
374 output_get_in_direction(container->workspace->output, move_dir);
375 if (output) {
376 struct sway_workspace *ws = output_get_active_workspace(output);
377 if (!sway_assert(ws, "Expected output to have a workspace")) {
378 return false;
379 }
380 container_move_to_workspace_from_direction(container, ws, move_dir);
381 return true;
382 } 398 }
383 sway_log(SWAY_DEBUG, "Hit edge of output, nowhere else to go"); 399 return true;
384 } 400 }
385 return false;
386} 401}
387 402
388static struct cmd_results *cmd_move_to_scratchpad(void); 403static struct cmd_results *cmd_move_to_scratchpad(void);