From 26a416e016314bdeb299cebccae3e8617b697d98 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Mon, 14 Feb 2011 23:05:20 +0100 Subject: [PATCH] refactor tree_move() into src/move.c, change config (!), change testcase Due to lots of cases which were added and added to tree_move(), the function was not really easy to understand. For this refactoring, I wrote tree_move() from scratch, thinking about (hopefully) all cases. The testsuite still passes. The move command also has different parameters now. Instead of the hard to understand 'before v' stuff, we use 'move [left|right|up|down]'. --- Makefile | 2 +- i3.config | 8 +- include/all.h | 1 + include/con.h | 3 + include/move.h | 15 +++ include/tree.h | 7 -- include/workspace.h | 2 + src/cmdparse.l | 2 - src/cmdparse.y | 15 +-- src/con.c | 53 ++++++++++- src/move.c | 174 +++++++++++++++++++++++++++++++++++ src/tree.c | 207 ------------------------------------------ src/workspace.c | 33 +++++++ testcases/t/24-move.t | 30 +++--- 14 files changed, 302 insertions(+), 250 deletions(-) create mode 100644 include/move.h create mode 100644 src/move.c diff --git a/Makefile b/Makefile index 89647f6c..25da180e 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ include $(TOPDIR)/common.mk # Depend on the object files of all source-files in src/*.c and on all header files AUTOGENERATED:=src/cfgparse.tab.c src/cfgparse.yy.c src/cmdparse.tab.c src/cmdparse.yy.c -FILES:=src/ipc.c src/main.c src/log.c src/util.c src/tree.c src/xcb.c src/manage.c src/workspace.c src/x.c src/floating.c src/click.c src/config.c src/handlers.c src/randr.c src/xinerama.c src/con.c src/load_layout.c src/render.c src/window.c src/match.c src/xcursor.c src/resize.c src/sighandler.c +FILES:=src/ipc.c src/main.c src/log.c src/util.c src/tree.c src/xcb.c src/manage.c src/workspace.c src/x.c src/floating.c src/click.c src/config.c src/handlers.c src/randr.c src/xinerama.c src/con.c src/load_layout.c src/render.c src/window.c src/match.c src/xcursor.c src/resize.c src/sighandler.c src/move.c FILES:=$(FILES:.c=.o) HEADERS:=$(filter-out include/loglevels.h,$(wildcard include/*.h)) diff --git a/i3.config b/i3.config index 185c2462..786ee6a6 100644 --- a/i3.config +++ b/i3.config @@ -66,10 +66,10 @@ bindsym Mod1+Down next v bindsym Mod1+Up prev v # Move -bindsym Mod1+Shift+n move before h -bindsym Mod1+Shift+r move before v -bindsym Mod1+Shift+t move after v -bindsym Mod1+Shift+d move after h +bindsym Mod1+Shift+n move left +bindsym Mod1+Shift+r move down +bindsym Mod1+Shift+t move up +bindsym Mod1+Shift+d move right # alternatively, you can use the cursor keys: bindsym Mod1+Shift+Left move before h diff --git a/include/all.h b/include/all.h index 3cc28940..dd2b9365 100644 --- a/include/all.h +++ b/include/all.h @@ -54,5 +54,6 @@ #include "xcursor.h" #include "resize.h" #include "sighandler.h" +#include "move.h" #endif diff --git a/include/con.h b/include/con.h index de537218..d5dc74e7 100644 --- a/include/con.h +++ b/include/con.h @@ -42,6 +42,9 @@ Con *con_get_output(Con *con); */ Con *con_get_workspace(Con *con); + +Con *con_parent_with_orientation(Con *con, orientation_t orientation); + /** * Returns the first fullscreen node below this node. * diff --git a/include/move.h b/include/move.h new file mode 100644 index 00000000..d0c97014 --- /dev/null +++ b/include/move.h @@ -0,0 +1,15 @@ +/* + * vim:ts=4:sw=4:expandtab + */ + +#ifndef _MOVE_H +#define _MOVE_H + +/** + * Moves the current container in the given direction (TOK_LEFT, TOK_RIGHT, + * TOK_UP, TOK_DOWN from cmdparse.l) + * + */ +void tree_move(int direction); + +#endif diff --git a/include/tree.h b/include/tree.h index c93d4c22..40d9a541 100644 --- a/include/tree.h +++ b/include/tree.h @@ -65,13 +65,6 @@ void tree_close_con(); */ void tree_next(char way, orientation_t orientation); -/** - * Moves the current container in the given way (next/previous) and given - * orientation (horizontal/vertical). - * - */ -void tree_move(char way, orientation_t orientation); - /** * Closes the given container including all children * diff --git a/include/workspace.h b/include/workspace.h index f65f1494..b902f490 100644 --- a/include/workspace.h +++ b/include/workspace.h @@ -99,4 +99,6 @@ void workspace_map_clients(xcb_connection_t *conn, Workspace *ws); */ void workspace_update_urgent_flag(Con *ws); +void ws_force_orientation(Con *ws, orientation_t orientation); + #endif diff --git a/src/cmdparse.l b/src/cmdparse.l index d8320578..ebd466af 100644 --- a/src/cmdparse.l +++ b/src/cmdparse.l @@ -112,8 +112,6 @@ up { return TOK_UP; } down { return TOK_DOWN; } left { return TOK_LEFT; } right { return TOK_RIGHT; } -before { return TOK_BEFORE; } -after { return TOK_AFTER; } resize { return TOK_RESIZE; } shrink { return TOK_SHRINK; } grow { return TOK_GROW; } diff --git a/src/cmdparse.y b/src/cmdparse.y index 50738f70..d71773fe 100644 --- a/src/cmdparse.y +++ b/src/cmdparse.y @@ -134,8 +134,6 @@ char *parse_cmd(const char *new) { %token TOK_DOWN "down" %token TOK_LEFT "left" %token TOK_RIGHT "right" -%token TOK_AFTER "after" -%token TOK_BEFORE "before" %token TOK_RESTORE "restore" %token TOK_MARK "mark" %token TOK_RESIZE "resize" @@ -527,12 +525,10 @@ level_direction: ; move: - TOK_MOVE WHITESPACE before_after WHITESPACE direction + TOK_MOVE WHITESPACE direction { - printf("moving: %s and %c\n", ($3 == TOK_BEFORE ? "before" : "after"), $5); - /* TODO: change API for the next call, we need to convert in both directions while ideally - * we should not need any of both */ - tree_move(($3 == TOK_BEFORE ? 'p' : 'n'), ($5 == 'v' ? VERT : HORIZ)); + printf("moving in direction %d\n", $3); + tree_move($3); } | TOK_MOVE WHITESPACE TOK_WORKSPACE WHITESPACE STR { @@ -555,11 +551,6 @@ move: } ; -before_after: - TOK_BEFORE { $$ = TOK_BEFORE; } - | TOK_AFTER { $$ = TOK_AFTER; } - ; - restore: TOK_RESTORE WHITESPACE STR { diff --git a/src/con.c b/src/con.c index 65b97443..d36a0da4 100644 --- a/src/con.c +++ b/src/con.c @@ -230,6 +230,24 @@ Con *con_get_workspace(Con *con) { return result; } +Con *con_parent_with_orientation(Con *con, orientation_t orientation) { + DLOG("Searching for parent of Con %p with orientation %d\n", con, orientation); + Con *parent = con->parent; + if (parent->type == CT_FLOATING_CON) + return NULL; + while (con_orientation(parent) != orientation) { + DLOG("Need to go one level further up\n"); + parent = parent->parent; + /* Abort when we reach a floating con */ + if (parent && parent->type == CT_FLOATING_CON) + parent = NULL; + if (parent == NULL) + break; + } + DLOG("Result: %p\n", parent); + return parent; +} + /* * helper data structure for the breadth-first-search in * con_get_fullscreen_con() @@ -742,15 +760,44 @@ void con_set_layout(Con *con, int layout) { } static void con_on_remove_child(Con *con) { + DLOG("on_remove_child\n"); + /* Nothing to do for workspaces */ - if (con->type == CT_WORKSPACE) + if (con->type == CT_WORKSPACE || con->type == CT_OUTPUT || con->type == CT_ROOT) { + DLOG("not handling, type = %d\n", con->type); return; + } /* TODO: check if this container would swallow any other client and * don’t close it automatically. */ - DLOG("on_remove_child\n"); - if (con_num_children(con) == 0) { + int children = con_num_children(con); + if (children == 0) { DLOG("Container empty, closing\n"); tree_close(con, false, false); + return; + } + + /* If we did not close the container, check if we have only a single child left */ + if (children == 1) { + Con *child = TAILQ_FIRST(&(con->nodes_head)); + Con *parent = con->parent; + DLOG("Container has only one child, replacing con %p with child %p\n", con, child); + + /* TODO: refactor it into con_swap */ + TAILQ_REPLACE(&(parent->nodes_head), con, child, nodes); + TAILQ_REPLACE(&(parent->focus_head), con, child, focused); + if (focused == con) + focused = child; + child->parent = parent; + child->percent = 0.0; + con_fix_percent(parent); + + con->parent = NULL; + x_con_kill(con); + free(con->name); + TAILQ_REMOVE(&all_cons, con, all_cons); + free(con); + + return; } } diff --git a/src/move.c b/src/move.c new file mode 100644 index 00000000..2700f205 --- /dev/null +++ b/src/move.c @@ -0,0 +1,174 @@ +/* + * vim:ts=4:sw=4:expandtab + */ + +#include "all.h" +#include "cmdparse.tab.h" + +typedef enum { BEFORE, AFTER } position_t; + +/* + * This function detaches 'con' from its parent and inserts it either before or + * after 'target'. + * + */ +static void insert_con_into(Con *con, Con *target, position_t position) { + Con *parent = target->parent; + /* We need to preserve the old con->parent. While it might still be used to + * insert the entry before/after it, we call the on_remove_child callback + * afterwards which might then close the con if it is empty. */ + Con *old_parent = con->parent; + + con_detach(con); + con_fix_percent(con->parent); + + con->parent = parent; + + if (position == BEFORE) { + TAILQ_INSERT_BEFORE(target, con, nodes); + TAILQ_INSERT_HEAD(&(parent->focus_head), con, focused); + } else if (position == AFTER) { + TAILQ_INSERT_AFTER(&(parent->nodes_head), target, con, nodes); + TAILQ_INSERT_HEAD(&(parent->focus_head), con, focused); + } + + /* Pretend the con was just opened with regards to size percent values. + * Since the con is moved to a completely different con, the old value + * does not make sense anyways. */ + con->percent = 0.0; + con_fix_percent(parent); + + CALL(old_parent, on_remove_child); +} + +/* + * This function detaches 'con' from its parent and inserts it at the given + * workspace. + * + */ +static void attach_to_workspace(Con *con, Con *ws) { + con_detach(con); + con_fix_percent(con->parent); + + CALL(con->parent, on_remove_child); + + con->parent = ws; + + TAILQ_INSERT_TAIL(&(ws->nodes_head), con, nodes); + TAILQ_INSERT_TAIL(&(ws->focus_head), con, focused); + + /* Pretend the con was just opened with regards to size percent values. + * Since the con is moved to a completely different con, the old value + * does not make sense anyways. */ + con->percent = 0.0; + con_fix_percent(ws); +} + +/* + * Moves the current container in the given direction (TOK_LEFT, TOK_RIGHT, + * TOK_UP, TOK_DOWN from cmdparse.l) + * + */ +void tree_move(int direction) { + DLOG("Moving in direction %d\n", direction); + /* 1: get the first parent with the same orientation */ + Con *con = focused; + + if (con->type == CT_WORKSPACE) { + DLOG("Not moving workspace\n"); + return; + } + + if (con->parent->type == CT_WORKSPACE && con_num_children(con->parent) == 1) { + DLOG("This is the only con on this workspace, not doing anything\n"); + return; + } + + orientation_t o = (direction == TOK_LEFT || direction == TOK_RIGHT ? HORIZ : VERT); + + Con *same_orientation = con_parent_with_orientation(con, o); + /* There is no parent container with the same orientation */ + if (!same_orientation) { + if (con_is_floating(con)) { + /* this is a floating con, we just disable floating */ + floating_disable(con, true); + return; + } + if (con_inside_floating(con)) { + /* 'con' should be moved out of a floating container */ + DLOG("Inside floating, moving to workspace\n"); + attach_to_workspace(con, con_get_workspace(con)); + goto end; + } + DLOG("Force-changing orientation\n"); + ws_force_orientation(con_get_workspace(con), o); + same_orientation = con_parent_with_orientation(con, o); + } + + /* easy case: the move is within this container */ + if (same_orientation == con->parent) { + DLOG("We are in the same container\n"); + Con *swap; + /* TODO: TAILQ_SWAP? */ + if (direction == TOK_LEFT || direction == TOK_UP) { + if (!(swap = TAILQ_PREV(con, nodes_head, nodes))) + return; + + if (!con_is_leaf(swap)) { + insert_con_into(con, con_descend_focused(swap), AFTER); + goto end; + } + + /* the container right of the current one is a normal one. */ + con_detach(con); + TAILQ_INSERT_BEFORE(swap, con, nodes); + TAILQ_INSERT_HEAD(&(swap->parent->focus_head), con, focused); + } else { + if (!(swap = TAILQ_NEXT(con, nodes))) + return; + + if (!con_is_leaf(swap)) { + insert_con_into(con, con_descend_focused(swap), AFTER); + goto end; + } + + con_detach(con); + TAILQ_INSERT_AFTER(&(swap->parent->nodes_head), swap, con, nodes); + TAILQ_INSERT_HEAD(&(swap->parent->focus_head), con, focused); + } + DLOG("Swapped.\n"); + return; + } + + /* this time, we have to move to another container */ + /* This is the container *above* 'con' which is inside 'same_orientation' */ + Con *above = con; + while (above->parent != same_orientation) + above = above->parent; + + DLOG("above = %p\n", above); + Con *next; + position_t position; + if (direction == TOK_UP || direction == TOK_LEFT) { + position = BEFORE; + next = TAILQ_PREV(above, nodes_head, nodes); + } else if (direction == TOK_DOWN || direction == TOK_RIGHT) { + position = AFTER; + next = TAILQ_NEXT(above, nodes); + } + + /* special case: there is a split container in the direction we are moving + * to, so descend and append */ + if (next && !con_is_leaf(next)) + insert_con_into(con, con_descend_focused(next), AFTER); + else + insert_con_into(con, above, position); + +end: + /* We need to call con_focus() to fix the focus stack "above" the container + * we just inserted the focused container into (otherwise, the parent + * container(s) would still point to the old container(s)). */ + con_focus(con); + + tree_flatten(croot); +} diff --git a/src/tree.c b/src/tree.c index 77da3ac3..f2b1d90e 100644 --- a/src/tree.c +++ b/src/tree.c @@ -365,213 +365,6 @@ void tree_next(char way, orientation_t orientation) { con_focus(con_descend_focused(next)); } -/* - * Moves the current container in the given way (next/previous) and given - * orientation (horizontal/vertical). - * - */ -void tree_move(char way, orientation_t orientation) { - /* 1: get the first parent with the same orientation */ - Con *con = focused; - Con *parent = con->parent; - Con *old_parent = parent; - if (con->type == CT_WORKSPACE) - return; - DLOG("con = %p / %s\n", con, con->name); - bool level_changed = false; - while (con_orientation(parent) != orientation) { - DLOG("need to go one level further up\n"); - /* If the current parent is an output, we are at a workspace - * and the orientation still does not match. In this case, we split the - * workspace to have the same look & feel as in older i3 releases. */ - if (parent->type == CT_WORKSPACE) { - DLOG("Arrived at workspace (%p / %s)\n", parent, parent->name); - /* In case of moving a window out of a floating con, there might be - * not a single tiling container. Makes no sense to split then, so - * just use the workspace as target */ - if (TAILQ_EMPTY(&(parent->nodes_head))) - break; - - /* Check if there are any other cons at all. If not, there is no - * point in creating a new split con and changing workspace - * orientation. Instead, the operation is a no-op. */ - Con *child; - bool other_container = false; - TAILQ_FOREACH(child, &(parent->nodes_head), nodes) - if (child != con) - other_container = true; - - if (!other_container) { - DLOG("No other container found, we are not creating this split container.\n"); - return; - } - - /* 1: create a new split container */ - Con *new = con_new(NULL); - new->parent = parent; - - /* 2: copy layout and orientation from workspace */ - new->layout = parent->layout; - new->orientation = parent->orientation; - - Con *old_focused = TAILQ_FIRST(&(parent->focus_head)); - if (old_focused == TAILQ_END(&(parent->focus_head))) - old_focused = NULL; - - /* 3: move the existing cons of this workspace below the new con */ - DLOG("Moving cons\n"); - while (!TAILQ_EMPTY(&(parent->nodes_head))) { - child = TAILQ_FIRST(&(parent->nodes_head)); - con_detach(child); - con_attach(child, new, true); - } - - /* 4: switch workspace orientation */ - parent->orientation = orientation; - - /* 5: attach the new split container to the workspace */ - DLOG("Attaching new split to ws\n"); - con_attach(new, parent, false); - - /* 6: fix the percentages */ - con_fix_percent(parent); - - if (old_focused) - con_focus(old_focused); - - level_changed = true; - - break; - } - parent = parent->parent; - level_changed = true; - } - /* If we have no tiling cons (when moving a window out of a floating con to - * an otherwise empty workspace for example), we just attach the window to - * the workspace. */ - bool fix_percent = false; - if (TAILQ_EMPTY(&(parent->nodes_head))) { - con_detach(con); - con_fix_percent(con->parent); - con->parent = parent; - fix_percent = true; - - TAILQ_INSERT_HEAD(&(parent->nodes_head), con, nodes); - TAILQ_INSERT_HEAD(&(parent->focus_head), con, focused); - } else { - Con *current = NULL, *loop; - /* Get the first tiling container in focus stack */ - TAILQ_FOREACH(loop, &(parent->focus_head), focused) { - if (loop->type == CT_FLOATING_CON) - continue; - current = loop; - break; - } - assert(current != TAILQ_END(&(parent->focus_head))); - - /* 2: chose next (or previous) */ - Con *next = current; - if (way == 'n') { - LOG("i would insert it after %p / %s\n", next, next->name); - - /* Have a look at the next container: If there is no next container or - * if it is a leaf node, we move the con one left to it. However, - * for split containers, we descend into it. */ - next = TAILQ_NEXT(next, nodes); - if (next == TAILQ_END(&(next->parent->nodes_head))) { - if (con == current) - return; - next = current; - } else { - if (level_changed && con_is_leaf(next)) { - next = current; - } else { - /* if this is a split container, we need to go down */ - next = con_descend_focused(next); - } - } - - con_detach(con); - if (con->parent != next->parent) { - con_fix_percent(con->parent); - con->parent = next->parent; - fix_percent = true; - } - - CALL(con->parent, on_remove_child); - - TAILQ_INSERT_AFTER(&(next->parent->nodes_head), next, con, nodes); - TAILQ_INSERT_HEAD(&(next->parent->focus_head), con, focused); - /* TODO: don’t influence focus handling? */ - } else { - LOG("i would insert it before %p / %s\n", current, current->name); - bool gone_down = false; - next = TAILQ_PREV(next, nodes_head, nodes); - if (next == TAILQ_END(&(next->parent->nodes_head))) { - if (con == current) { - DLOG("Cannot move, no other container in that direction\n"); - return; - } - next = current; - } else { - if (level_changed && con_is_leaf(next)) { - next = current; - } else { - /* if this is a split container, we need to go down */ - while (!TAILQ_EMPTY(&(next->focus_head))) { - gone_down = true; - next = TAILQ_FIRST(&(next->focus_head)); - } - } - } - - DLOG("detaching con = %p / %s, next = %p / %s\n", - con, con->name, next, next->name); - con_detach(con); - if (con->parent != next->parent) { - DLOG("different parents. new parent = %p / %s\n", next->parent, next->parent->name); - con_fix_percent(con->parent); - con->parent = next->parent; - fix_percent = true; - } - - /* After going down in the tree, we insert the container *after* - * the currently focused one even though the command used "before". - * This is to keep the user experience clear, since the before/after - * only signifies the direction of the movement on top-level */ - if (gone_down) - TAILQ_INSERT_AFTER(&(next->parent->nodes_head), next, con, nodes); - else TAILQ_INSERT_BEFORE(next, con, nodes); - TAILQ_INSERT_HEAD(&(next->parent->focus_head), con, focused); - /* TODO: don’t influence focus handling? */ - } - } - - /* fix the percentages in the container we moved to */ - if (fix_percent) { - int children = con_num_children(con->parent); - if (children == 1) { - con->percent = 1.0; - } else { - con->percent = 1.0 / (children - 1); - con_fix_percent(con->parent); - } - } - - /* We need to call con_focus() to fix the focus stack "above" the container - * we just inserted the focused container into (otherwise, the parent - * container(s) would still point to the old container(s)). */ - con_focus(con); - - /* fix the percentages in the container we moved from */ - if (level_changed) - con_fix_percent(old_parent); - - CALL(old_parent, on_remove_child); - - tree_flatten(croot); -} - /* * tree_flatten() removes pairs of redundant split containers, e.g.: * [workspace, horizontal] diff --git a/src/workspace.c b/src/workspace.c index 55f7dd17..c17eb7ed 100644 --- a/src/workspace.c +++ b/src/workspace.c @@ -487,3 +487,36 @@ void workspace_update_urgent_flag(Con *ws) { if (old_flag != ws->urgent) ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"urgent\"}"); } + +void ws_force_orientation(Con *ws, orientation_t orientation) { + /* 1: create a new split container */ + Con *split = con_new(NULL); + split->parent = ws; + + /* 2: copy layout and orientation from workspace */ + split->layout = ws->layout; + split->orientation = ws->orientation; + + Con *old_focused = TAILQ_FIRST(&(ws->focus_head)); + + /* 3: move the existing cons of this workspace below the new con */ + DLOG("Moving cons\n"); + while (!TAILQ_EMPTY(&(ws->nodes_head))) { + Con *child = TAILQ_FIRST(&(ws->nodes_head)); + con_detach(child); + con_attach(child, split, true); + } + + /* 4: switch workspace orientation */ + ws->orientation = orientation; + + /* 5: attach the new split container to the workspace */ + DLOG("Attaching new split to ws\n"); + con_attach(split, ws, false); + + /* 6: fix the percentages */ + con_fix_percent(ws); + + if (old_focused) + con_focus(old_focused); +} diff --git a/testcases/t/24-move.t b/testcases/t/24-move.t index 5d6a96ef..e887b9f3 100644 --- a/testcases/t/24-move.t +++ b/testcases/t/24-move.t @@ -13,13 +13,13 @@ use X11::XCB qw(:all); my $i3 = i3("/tmp/nestedcons"); my $tmp = get_unused_workspace(); -$i3->command("workspace $tmp")->recv; +cmd "workspace $tmp"; ###################################################################### # 1) move a container which cannot be moved ###################################################################### -$i3->command('open')->recv; +cmd 'open'; my $old_content = get_ws_content($tmp); is(@{$old_content}, 1, 'one container on this workspace'); @@ -46,22 +46,22 @@ my $second = $content->[1]->{id}; is($content->[0]->{id}, $first, 'first container unmodified'); # Move the second container before the first one (→ swap them) -$i3->command('move before h')->recv; +$i3->command('move left')->recv; $content = get_ws_content($tmp); is($content->[0]->{id}, $second, 'first container modified'); # We should not be able to move any further -$i3->command('move before h')->recv; +$i3->command('move left')->recv; $content = get_ws_content($tmp); is($content->[0]->{id}, $second, 'first container unmodified'); # Now move in the other direction -$i3->command('move after h')->recv; +$i3->command('move right')->recv; $content = get_ws_content($tmp); is($content->[0]->{id}, $first, 'first container modified'); # We should not be able to move any further -$i3->command('move after h')->recv; +$i3->command('move right')->recv; $content = get_ws_content($tmp); is($content->[0]->{id}, $first, 'first container unmodified'); @@ -84,7 +84,7 @@ $content = get_ws_content($tmp); is(@{$content}, 3, 'three containers on this workspace'); my $third = $content->[2]->{id}; -$i3->command('move before h')->recv; +$i3->command('move left')->recv; $content = get_ws_content($tmp); is(@{$content}, 2, 'only two containers on this workspace'); my $nodes = $content->[1]->{nodes}; @@ -95,19 +95,21 @@ is($nodes->[1]->{id}, $third, 'third container on bottom'); # move it inside the split container ###################################################################### -$i3->command('move before v')->recv; +$i3->command('move up')->recv; $nodes = get_ws_content($tmp)->[1]->{nodes}; is($nodes->[0]->{id}, $third, 'third container on top'); is($nodes->[1]->{id}, $second, 'second container on bottom'); # move it outside again -$i3->command('move before h')->recv; +$i3->command('move left')->recv; $content = get_ws_content($tmp); is(@{$content}, 3, 'three nodes on this workspace'); -$i3->command('move after h')->recv; +# due to automatic flattening/cleanup, the remaining split container +# will be replaced by the con itself, so we will still have 3 nodes +$i3->command('move right')->recv; $content = get_ws_content($tmp); -is(@{$content}, 2, 'two nodes on this workspace'); +is(@{$content}, 3, 'two nodes on this workspace'); ###################################################################### # 4) We create two v-split containers on the workspace, then we move @@ -116,7 +118,7 @@ is(@{$content}, 2, 'two nodes on this workspace'); ###################################################################### my $otmp = get_unused_workspace(); -$i3->command("workspace $otmp")->recv; +cmd "workspace $otmp"; $i3->command("open")->recv; $i3->command("open")->recv; @@ -125,9 +127,9 @@ $i3->command("open")->recv; $i3->command("prev h")->recv; $i3->command("split v")->recv; $i3->command("open")->recv; -$i3->command("move after h")->recv; +$i3->command("move right")->recv; $i3->command("prev h")->recv; -$i3->command("move after h")->recv; +$i3->command("move right")->recv; $content = get_ws_content($otmp); is(@{$content}, 1, 'only one nodes on this workspace'); -- 2.39.5