]> git.sur5r.net Git - i3/i3/commitdiff
Implement moving workspaces as if they're regular containers
authorDeiz <silverwraithii@gmail.com>
Fri, 28 Sep 2012 17:54:24 +0000 (13:54 -0400)
committerMichael Stapelberg <michael@stapelberg.de>
Fri, 28 Sep 2012 22:17:36 +0000 (00:17 +0200)
include/workspace.h
src/commands.c
src/con.c
src/workspace.c
testcases/t/132-move-workspace.t

index a7f2d13bd1ba47932f466b1aba5d41926543134e..907e959f0347e8903eef96bee29498698570f718 100644 (file)
@@ -174,4 +174,11 @@ void ws_force_orientation(Con *ws, orientation_t orientation);
  */
 Con *workspace_attach_to(Con *ws);
 
+/**
+ * Creates a new container and re-parents all of children from the given
+ * workspace into it.
+ *
+ * The container inherits the layout from the workspace.
+ */
+Con *workspace_encapsulate(Con *ws);
 #endif
index 0263802fe4551c77c858f0397679e4aa112de4ca..535324359dddc5703c80a13767ff9b54b8de7ddc 100644 (file)
@@ -388,7 +388,8 @@ void cmd_move_con_to_workspace(I3_CMD, char *which) {
      *  when criteria was specified but didn't match any window or
      *  when criteria wasn't specified and we don't have any window focused. */
     if ((!match_is_empty(current_match) && TAILQ_EMPTY(&owindows)) ||
-        (match_is_empty(current_match) && focused->type == CT_WORKSPACE)) {
+        (match_is_empty(current_match) && focused->type == CT_WORKSPACE &&
+        con_is_leaf(focused))) {
         ysuccess(false);
         return;
     }
@@ -476,9 +477,8 @@ void cmd_move_con_to_workspace_name(I3_CMD, char *name) {
         ysuccess(false);
         return;
     }
-
-    if (match_is_empty(current_match) && focused->type == CT_WORKSPACE) {
-        ELOG("No window to move, you have focused a workspace.\n");
+    else if (match_is_empty(current_match) && focused->type == CT_WORKSPACE &&
+        con_is_leaf(focused)) {
         ysuccess(false);
         return;
     }
@@ -512,7 +512,8 @@ void cmd_move_con_to_workspace_number(I3_CMD, char *which) {
      *  when criteria was specified but didn't match any window or
      *  when criteria wasn't specified and we don't have any window focused. */
     if ((!match_is_empty(current_match) && TAILQ_EMPTY(&owindows)) ||
-        (match_is_empty(current_match) && focused->type == CT_WORKSPACE)) {
+        (match_is_empty(current_match) && focused->type == CT_WORKSPACE &&
+        con_is_leaf(focused))) {
         ysuccess(false);
         return;
     }
index 1140fe80704df202a8f6ef6d81aaa1c1d95d112a..5bba8c7c0a779f73ec9ca364a47ca7db1587e92c 100644 (file)
--- a/src/con.c
+++ b/src/con.c
@@ -607,11 +607,6 @@ void con_toggle_fullscreen(Con *con, int fullscreen_mode) {
  *
  */
 void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool dont_warp) {
-    if (con->type == CT_WORKSPACE) {
-        DLOG("Moving workspaces is not yet implemented.\n");
-        return;
-    }
-
     /* Prevent moving if this would violate the fullscreen focus restrictions. */
     if (!con_fullscreen_permits_focusing(workspace)) {
         LOG("Cannot move out of a fullscreen container");
@@ -629,6 +624,21 @@ void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool
         return;
     }
 
+    if (con->type == CT_WORKSPACE) {
+        con = workspace_encapsulate(con);
+        if (con == NULL) {
+            ELOG("Workspace failed to move its contents into a container!\n");
+            return;
+        }
+
+        /* Re-parent all of the old workspace's floating windows. */
+        Con *child;
+        while (!TAILQ_EMPTY(&(source_ws->floating_head))) {
+            child = TAILQ_FIRST(&(source_ws->floating_head));
+            con_move_to_workspace(child, workspace, true, true);
+        }
+    }
+
     /* Save the current workspace. So we can call workspace_show() by the end
      * of this function. */
     Con *current_ws = con_get_workspace(focused);
index 14840e4a985941e706ae4b559debceae86eee3ce..3fdb7db84ac5bfa4b23d62d71986ce405d309d49 100644 (file)
@@ -830,3 +830,34 @@ Con *workspace_attach_to(Con *ws) {
 
     return new;
 }
+
+/**
+ * Creates a new container and re-parents all of children from the given
+ * workspace into it.
+ *
+ * The container inherits the layout from the workspace.
+ */
+Con *workspace_encapsulate(Con *ws) {
+    if (TAILQ_EMPTY(&(ws->nodes_head))) {
+        ELOG("Workspace %p / %s has no children to encapsulate\n", ws, ws->name);
+        return NULL;
+    }
+
+    Con *new = con_new(NULL, NULL);
+    new->parent = ws;
+    new->layout = ws->layout;
+
+    DLOG("Moving children of workspace %p / %s into container %p\n",
+        ws, ws->name, new);
+
+    Con *child;
+    while (!TAILQ_EMPTY(&(ws->nodes_head))) {
+        child = TAILQ_FIRST(&(ws->nodes_head));
+        con_detach(child);
+        con_attach(child, new, true);
+    }
+
+    con_attach(new, ws, true);
+
+    return new;
+}
index a4f6b6082ad7da6f4a4ebef73241aad0ba402899..730041a642e5449b0f473d9738cc89a905f90ac9 100644 (file)
@@ -198,4 +198,124 @@ cmd 'move workspace number 17';
 ok(workspace_exists('17'), 'workspace 17 created by moving');
 is(@{get_ws('17')->{nodes}}, 1, 'one node on ws 16');
 
+################################################################################
+# The following four tests verify the various 'move workspace' commands when
+# the selection is itself a workspace.
+################################################################################
+
+# borrowed from 122-split.t
+# recursively sums up all nodes and their children
+sub sum_nodes {
+    my ($nodes) = @_;
+
+    return 0 if !@{$nodes};
+
+    my @children = (map { @{$_->{nodes}} } @{$nodes},
+                    map { @{$_->{'floating_nodes'}} } @{$nodes});
+
+    return @{$nodes} + sum_nodes(\@children);
+}
+
+############################################################
+# move workspace 'next|prev'
+############################################################
+$tmp = get_unused_workspace();
+$tmp2 = get_unused_workspace();
+
+cmd "workspace $tmp";
+cmd 'open';
+is_num_children($tmp, 1, 'one container on first ws');
+
+cmd "workspace $tmp2";
+cmd 'open';
+is_num_children($tmp2, 1, 'one container on second ws');
+cmd 'open';
+is_num_children($tmp2, 2, 'two containers on second ws');
+
+cmd 'focus parent';
+cmd 'move workspace prev';
+
+is_num_children($tmp, 2, 'two child containers on first ws');
+is(sum_nodes(get_ws_content($tmp)), 4, 'four total containers on first ws');
+is_num_children($tmp2, 0, 'no containers on second ws');
+
+############################################################
+# move workspace current
+# This is a special case that should be a no-op.
+############################################################
+$tmp = fresh_workspace();
+
+cmd 'open';
+is_num_children($tmp, 1, 'one container on first ws');
+my $tmpcount = sum_nodes(get_ws_content($tmp));
+
+cmd 'focus parent';
+cmd "move workspace $tmp";
+
+is(sum_nodes(get_ws_content($tmp)), $tmpcount, 'number of containers in first ws unchanged');
+
+############################################################
+# move workspace '<name>'
+############################################################
+$tmp2 = get_unused_workspace();
+$tmp = fresh_workspace();
+
+cmd 'open';
+is_num_children($tmp, 1, 'one container on first ws');
+
+cmd "workspace $tmp2";
+cmd 'open';
+is_num_children($tmp2, 1, 'one container on second ws');
+cmd 'open';
+is_num_children($tmp2, 2, 'two containers on second ws');
+
+cmd 'focus parent';
+cmd "move workspace $tmp";
+
+is_num_children($tmp, 2, 'two child containers on first ws');
+is(sum_nodes(get_ws_content($tmp)), 4, 'four total containers on first ws');
+is_num_children($tmp2, 0, 'no containers on second ws');
+
+############################################################
+# move workspace number '<number>'
+############################################################
+cmd 'workspace 18';
+cmd 'open';
+is_num_children('18', 1, 'one container on ws 18');
+
+cmd 'workspace 19';
+cmd 'open';
+is_num_children('19', 1, 'one container on ws 19');
+cmd 'open';
+is_num_children('19', 2, 'two containers on ws 19');
+
+cmd 'focus parent';
+cmd 'move workspace number 18';
+
+is_num_children('18', 2, 'two child containers on ws 18');
+is(sum_nodes(get_ws_content('18')), 4, 'four total containers on ws 18');
+is_num_children('19', 0, 'no containers on ws 19');
+
+###################################################################
+# move workspace '<name>' with a floating child
+###################################################################
+$tmp2 = get_unused_workspace();
+$tmp = fresh_workspace();
+cmd 'open';
+cmd 'floating toggle';
+cmd 'open';
+cmd 'floating toggle';
+cmd 'open';
+
+$ws = get_ws($tmp);
+is_num_children($tmp, 1, 'one container on first workspace');
+is(@{$ws->{floating_nodes}}, 2, 'two floating nodes on first workspace');
+
+cmd 'focus parent';
+cmd "move workspace $tmp2";
+
+$ws = get_ws($tmp2);
+is_num_children($tmp2, 1, 'one container on second workspace');
+is(@{$ws->{floating_nodes}}, 2, 'two floating nodes on second workspace');
+
 done_testing;