]> git.sur5r.net Git - i3/i3/commitdiff
refactor tree_move() into src/move.c, change config (!), change testcase
authorMichael Stapelberg <michael@stapelberg.de>
Mon, 14 Feb 2011 22:05:20 +0000 (23:05 +0100)
committerMichael Stapelberg <michael@stapelberg.de>
Mon, 14 Feb 2011 22:05:20 +0000 (23:05 +0100)
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]'.

14 files changed:
Makefile
i3.config
include/all.h
include/con.h
include/move.h [new file with mode: 0644]
include/tree.h
include/workspace.h
src/cmdparse.l
src/cmdparse.y
src/con.c
src/move.c [new file with mode: 0644]
src/tree.c
src/workspace.c
testcases/t/24-move.t

index 89647f6c3a8519a5b52f69d77abc9a82a40302c3..25da180e0f2fc7bd72a42ef5f96294863b373664 100644 (file)
--- 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))
 
index 185c2462296e54474f70fa72e6516d4b5afed38e..786ee6a6cea1d94ae146d2489f8f3a306f1f6afd 100644 (file)
--- 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
index 3cc28940393cd4cf7886313567b82f7e9b427b23..dd2b9365f7a09e80308fcce6715ae462441397fd 100644 (file)
@@ -54,5 +54,6 @@
 #include "xcursor.h"
 #include "resize.h"
 #include "sighandler.h"
+#include "move.h"
 
 #endif
index de53721850e9ccbf800a4400445868cf9be1dab6..d5dc74e79d7cc6d2d6f1241a61c98940c809525d 100644 (file)
@@ -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 (file)
index 0000000..d0c9701
--- /dev/null
@@ -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
index c93d4c22f87df2d0fbb0b910801d8a3c64910ae9..40d9a5417714a9c76ec1268b641dce8ca52d8919 100644 (file)
@@ -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
  *
index f65f1494b1062a8f412d0dfbeddb1b03c8296090..b902f4903f988cbd5392e7bde257c05f213ee8d6 100644 (file)
@@ -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
index d832057803a005ea0e233c564f7fffc8a7d814e7..ebd466afa795061a7fe80ddcb93f0d6bb476e3e4 100644 (file)
@@ -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; }
index 50738f70eccfd690e926603d6c32084673ca5ff2..d71773fe64f25f1d4366265d5ce75f8361484b59 100644 (file)
@@ -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", ($<number>3 == TOK_BEFORE ? "before" : "after"), $<chr>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(($<number>3 == TOK_BEFORE ? 'p' : 'n'), ($<chr>5 == 'v' ? VERT : HORIZ));
+        printf("moving in direction %d\n", $<number>3);
+        tree_move($<number>3);
     }
     | TOK_MOVE WHITESPACE TOK_WORKSPACE WHITESPACE STR
     {
@@ -555,11 +551,6 @@ move:
     }
     ;
 
-before_after:
-    TOK_BEFORE { $<number>$ = TOK_BEFORE; }
-    | TOK_AFTER { $<number>$ = TOK_AFTER; }
-    ;
-
 restore:
     TOK_RESTORE WHITESPACE STR
     {
index 65b974438dd61bb9ac6ddf4754ca69aeab9b7bba..d36a0da4063c19a94e87b5105e685114caac2869 100644 (file)
--- 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 (file)
index 0000000..2700f20
--- /dev/null
@@ -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);
+}
index 77da3ac3d90041e92fecc8c6d4d8aae01ee06b59..f2b1d90e2a5b2f9c7c46d464581dc487dce9cc39 100644 (file)
@@ -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]
index 55f7dd17246cb2aace774a5bb5154626230b8190..c17eb7ed65583f11e58318093d654d73f16c67c3 100644 (file)
@@ -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);
+}
index 5d6a96efdce99ce85a842f9a57ec5f6a2a0ae3ff..e887b9f3c66cae7a2ebea817bd506c97ea8e64b7 100644 (file)
@@ -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');