aboutsummaryrefslogtreecommitdiffstats
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
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.
-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);