diff options
-rw-r--r-- | sway/commands/move.c | 209 |
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 | 266 | static 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 = |
269 | static 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 |
293 | static bool container_move_in_direction(struct sway_container *container, | 290 | static 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 | ||
388 | static struct cmd_results *cmd_move_to_scratchpad(void); | 403 | static struct cmd_results *cmd_move_to_scratchpad(void); |