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.
This commit is contained in:
Ronan Pigott 2020-11-01 16:23:03 -07:00 committed by Tudor Brindus
parent ece6a1d408
commit e95c299f0a

View file

@ -263,126 +263,141 @@ static void container_move_to_container(struct sway_container *container,
} }
} }
/* Takes one child, sets it aside, wraps the rest of the children in a new static bool container_move_to_next_output(struct sway_container *container,
* container, switches the layout of the workspace, and drops the child back in. struct sway_output *output, enum wlr_direction move_dir) {
* In other words, rejigger it. */ struct sway_output *next_output =
static void workspace_rejigger(struct sway_workspace *ws, output_get_in_direction(output, move_dir);
struct sway_container *child, enum wlr_direction move_dir) { if (next_output) {
if (!child->parent && ws->tiling->length == 1) { struct sway_workspace *ws = output_get_active_workspace(next_output);
ws->layout = if (!sway_assert(ws, "Expected output to have a workspace")) {
move_dir == WLR_DIRECTION_LEFT || move_dir == WLR_DIRECTION_RIGHT ? return false;
L_HORIZ : L_VERT;
workspace_update_representation(ws);
return;
} }
container_detach(child); switch (container->fullscreen_mode) {
struct sway_container *new_parent = workspace_wrap_children(ws); case FULLSCREEN_NONE:
container_move_to_workspace_from_direction(container, ws, move_dir);
int index = return true;
move_dir == WLR_DIRECTION_LEFT || move_dir == WLR_DIRECTION_UP ? 0 : 1; case FULLSCREEN_WORKSPACE:
workspace_insert_tiling(ws, child, index); container_move_to_workspace(container, ws);
container_flatten(new_parent); return true;
ws->layout = case FULLSCREEN_GLOBAL:
move_dir == WLR_DIRECTION_LEFT || move_dir == WLR_DIRECTION_RIGHT ? return false;
L_HORIZ : L_VERT; }
workspace_update_representation(ws); }
child->width = child->height = 0; return false;
} }
// Returns true if moved // Returns true if moved
static bool container_move_in_direction(struct sway_container *container, static bool container_move_in_direction(struct sway_container *container,
enum wlr_direction move_dir) { enum wlr_direction move_dir) {
// If moving a fullscreen view, only consider outputs // If moving a fullscreen view, only consider outputs
if (container->fullscreen_mode == FULLSCREEN_WORKSPACE) { switch (container->fullscreen_mode) {
struct sway_output *new_output = case FULLSCREEN_NONE:
output_get_in_direction(container->workspace->output, move_dir); break;
if (!new_output) { case FULLSCREEN_WORKSPACE:
return false; return container_move_to_next_output(container,
} container->workspace->output, move_dir);
struct sway_workspace *ws = output_get_active_workspace(new_output); case FULLSCREEN_GLOBAL:
if (!sway_assert(ws, "Expected output to have a workspace")) {
return false;
}
container_move_to_workspace(container, ws);
return true;
}
if (container->fullscreen_mode == FULLSCREEN_GLOBAL) {
return false; return false;
} }
// Look for a suitable *container* sibling or parent.
// The below loop stops once we hit the workspace because current->parent
// is NULL for the topmost containers in a workspace.
struct sway_container *current = container;
int offs = int offs =
move_dir == WLR_DIRECTION_LEFT || move_dir == WLR_DIRECTION_UP ? -1 : 1; move_dir == WLR_DIRECTION_LEFT || move_dir == WLR_DIRECTION_UP ? -1 : 1;
int index = -1;
int desired = -1;
list_t *siblings = NULL;
struct sway_container *target = NULL;
while (current) { // Look for a suitable ancestor of the container to move within
list_t *siblings = container_get_siblings(current); struct sway_container *ancestor = NULL;
if (siblings) { struct sway_container *current = container;
enum sway_container_layout layout = container_parent_layout(current); bool wrapped = false;
int index = list_find(siblings, current); while (!ancestor) {
int desired = index + offs;
// Don't allow containers to move out of their // Don't allow containers to move out of their
// fullscreen or floating parent // fullscreen or floating parent
if (current->fullscreen_mode || container_is_floating(current)) { if (current->fullscreen_mode || container_is_floating(current)) {
return false; return false;
} }
if (is_parallel(layout, move_dir)) { enum sway_container_layout parent_layout = container_parent_layout(current);
if (desired == -1 || desired == siblings->length) { if (!is_parallel(parent_layout, move_dir)) {
if (current->parent == container->parent) { if (!current->parent) {
// No parallel parent, so we reorient the workspace
current = workspace_wrap_children(current->workspace);
current->workspace->layout =
move_dir == WLR_DIRECTION_LEFT ||
move_dir == WLR_DIRECTION_RIGHT ?
L_HORIZ : L_VERT;
container->height = container->width = 0;
container->height_fraction = container->width_fraction = 0;
workspace_update_representation(current->workspace);
wrapped = true;
} else {
// Keep looking for a parallel parent
current = current->parent;
}
continue;
}
// Only scratchpad hidden containers don't have siblings
// so siblings != NULL here
siblings = container_get_siblings(current);
index = list_find(siblings, current);
desired = index + offs;
target = desired == -1 || desired == siblings->length ?
NULL : siblings->items[desired];
// If the move is simple we can complete it here early
if (current == container) {
if (target) {
// Container will swap with or descend into its neighbor
container_move_to_container_from_direction(container,
target, move_dir);
return true;
} else if (!container->parent) {
// Container is at workspace level so we move it to the
// next workspace if possible
return container_move_to_next_output(container,
current->workspace->output, move_dir);
} else {
// Container has escaped its immediate parallel parent
current = current->parent; current = current->parent;
continue; continue;
} else {
// Reparenting
if (current->parent) {
container_insert_child(current->parent, container,
index + (offs < 0 ? 0 : 1));
} else {
workspace_insert_tiling(current->workspace, container,
index + (offs < 0 ? 0 : 1));
} }
return true;
} }
} else {
// Container can move within its siblings // We found a suitable ancestor, the loop will end
ancestor = current;
}
if (target) {
// Container will move in with its cousin
container_move_to_container_from_direction(container, container_move_to_container_from_direction(container,
siblings->items[desired], move_dir); target, move_dir);
return true;
} else if (!wrapped && !container->parent->parent &&
container->parent->children->length == 1) {
// Treat singleton children as if they are at workspace level like i3
// https://github.com/i3/i3/blob/1d9160f2d247dbaa83fb62f02fd7041dec767fc2/src/move.c#L367
return container_move_to_next_output(container,
ancestor->workspace->output, move_dir);
} else {
// Container will be promoted
struct sway_container *old_parent = container->parent;
if (ancestor->parent) {
// Container will move in with its parent
container_insert_child(ancestor->parent, container,
index + (offs < 0 ? 0 : 1));
} else {
// Container will move to workspace level,
// may be re-split by workspace_layout
workspace_insert_tiling(ancestor->workspace, container,
index + (offs < 0 ? 0 : 1));
}
if (old_parent) {
container_reap_empty(old_parent);
}
return true; return true;
} }
}
}
current = current->parent;
}
// Maybe rejigger the workspace
struct sway_workspace *ws = container->workspace;
if (ws) {
if (!is_parallel(ws->layout, move_dir)) {
workspace_rejigger(ws, container, move_dir);
return true;
} else if (ws->layout == L_TABBED || ws->layout == L_STACKED) {
workspace_rejigger(ws, container, move_dir);
return true;
}
// Try adjacent output
struct sway_output *output =
output_get_in_direction(container->workspace->output, move_dir);
if (output) {
struct sway_workspace *ws = output_get_active_workspace(output);
if (!sway_assert(ws, "Expected output to have a workspace")) {
return false;
}
container_move_to_workspace_from_direction(container, ws, move_dir);
return true;
}
sway_log(SWAY_DEBUG, "Hit edge of output, nowhere else to go");
}
return false;
} }
static struct cmd_results *cmd_move_to_scratchpad(void); static struct cmd_results *cmd_move_to_scratchpad(void);