diff --git a/include/data.h b/include/data.h index e8df78c7..02f781c9 100644 --- a/include/data.h +++ b/include/data.h @@ -514,6 +514,20 @@ struct Con { TAILQ_HEAD(swallow_head, Match) swallow_head; enum { CF_NONE = 0, CF_OUTPUT = 1, CF_GLOBAL = 2 } fullscreen_mode; + /* layout is the layout of this container: one of split[v|h], stacked or + * tabbed. Special containers in the tree (above workspaces) have special + * layouts like dockarea or output. + * + * last_split_layout is one of splitv or splith to support the old "layout + * default" command which by now should be "layout splitv" or "layout + * splith" explicitly. + * + * workspace_layout is only for type == CT_WORKSPACE cons. When you change + * the layout of a workspace without any children, i3 cannot just set the + * layout (because workspaces need to be splitv/splith to allow focus + * parent and opening new containers). Instead, it stores the requested + * layout in workspace_layout and creates a new split container with that + * layout whenever a new container is attached to the workspace. */ enum { L_DEFAULT = 0, L_STACKED = 1, @@ -522,7 +536,7 @@ struct Con { L_OUTPUT = 4, L_SPLITV = 5, L_SPLITH = 6 - } layout, last_split_layout; + } layout, last_split_layout, workspace_layout; border_style_t border_style; /** floating? (= not in tiling layout) This cannot be simply a bool * because we want to keep track of whether the status was set by the diff --git a/src/con.c b/src/con.c index 1d86d73a..f5ccfcdd 100644 --- a/src/con.c +++ b/src/con.c @@ -135,7 +135,7 @@ void con_attach(Con *con, Con *parent, bool ignore_focus) { */ if (con->window != NULL && parent->type == CT_WORKSPACE && - config.default_layout != L_DEFAULT) { + parent->workspace_layout != L_DEFAULT) { DLOG("Parent is a workspace. Applying default layout...\n"); Con *target = workspace_attach_to(parent); @@ -1105,39 +1105,43 @@ void con_set_layout(Con *con, int layout) { * need to create a new split container. */ if (con->type == CT_WORKSPACE && (layout == L_STACKED || layout == L_TABBED)) { - DLOG("Creating new split container\n"); - /* 1: create a new split container */ - Con *new = con_new(NULL, NULL); - new->parent = con; + if (con_num_children(con) == 0) { + DLOG("Setting workspace_layout to %d\n", layout); + con->workspace_layout = layout; + } else { + DLOG("Creating new split container\n"); + /* 1: create a new split container */ + Con *new = con_new(NULL, NULL); + new->parent = con; - /* 2: Set the requested layout on the split container and mark it as - * split. */ - new->layout = layout; - new->last_split_layout = con->last_split_layout; - new->split = true; + /* 2: Set the requested layout on the split container and mark it as + * split. */ + new->layout = layout; + new->last_split_layout = con->last_split_layout; + new->split = true; - Con *old_focused = TAILQ_FIRST(&(con->focus_head)); - if (old_focused == TAILQ_END(&(con->focus_head))) - old_focused = NULL; + Con *old_focused = TAILQ_FIRST(&(con->focus_head)); + if (old_focused == TAILQ_END(&(con->focus_head))) + old_focused = NULL; - /* 3: move the existing cons of this workspace below the new con */ - DLOG("Moving cons\n"); - Con *child; - while (!TAILQ_EMPTY(&(con->nodes_head))) { - child = TAILQ_FIRST(&(con->nodes_head)); - con_detach(child); - con_attach(child, new, true); + /* 3: move the existing cons of this workspace below the new con */ + DLOG("Moving cons\n"); + Con *child; + while (!TAILQ_EMPTY(&(con->nodes_head))) { + child = TAILQ_FIRST(&(con->nodes_head)); + con_detach(child); + con_attach(child, new, true); + } + + /* 4: attach the new split container to the workspace */ + DLOG("Attaching new split to ws\n"); + con_attach(new, con, false); + + if (old_focused) + con_focus(old_focused); + + tree_flatten(croot); } - - /* 4: attach the new split container to the workspace */ - DLOG("Attaching new split to ws\n"); - con_attach(new, con, false); - - if (old_focused) - con_focus(old_focused); - - tree_flatten(croot); - return; } diff --git a/src/ipc.c b/src/ipc.c index 1c6de798..7dfbc871 100644 --- a/src/ipc.c +++ b/src/ipc.c @@ -231,6 +231,23 @@ void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) { break; } + ystr("workspace_layout"); + switch (con->workspace_layout) { + case L_DEFAULT: + ystr("default"); + break; + case L_STACKED: + ystr("stacked"); + break; + case L_TABBED: + ystr("tabbed"); + break; + default: + DLOG("About to dump workspace_layout=%d (none of default/stacked/tabbed), this is a bug.\n", con->workspace_layout); + assert(false); + break; + } + ystr("last_split_layout"); switch (con->layout) { case L_SPLITV: diff --git a/src/load_layout.c b/src/load_layout.c index 795fb6d8..cce1a712 100644 --- a/src/load_layout.c +++ b/src/load_layout.c @@ -211,6 +211,17 @@ static int json_string(void *ctx, const unsigned char *val, unsigned int len) { json_node->layout = L_SPLITV; else LOG("Unhandled \"layout\": %s\n", buf); free(buf); + } else if (strcasecmp(last_key, "workspace_layout") == 0) { + char *buf = NULL; + sasprintf(&buf, "%.*s", (int)len, val); + if (strcasecmp(buf, "default") == 0) + json_node->workspace_layout = L_DEFAULT; + else if (strcasecmp(buf, "stacked") == 0) + json_node->workspace_layout = L_STACKED; + else if (strcasecmp(buf, "tabbed") == 0) + json_node->workspace_layout = L_TABBED; + else LOG("Unhandled \"workspace_layout\": %s\n", buf); + free(buf); } else if (strcasecmp(last_key, "last_split_layout") == 0) { char *buf = NULL; sasprintf(&buf, "%.*s", (int)len, val); diff --git a/src/workspace.c b/src/workspace.c index 1749959a..94efd47b 100644 --- a/src/workspace.c +++ b/src/workspace.c @@ -73,6 +73,7 @@ Con *workspace_get(const char *num, bool *created) { workspace->type = CT_WORKSPACE; FREE(workspace->name); workspace->name = sstrdup(num); + workspace->workspace_layout = config.default_layout; /* We set ->num to the number if this workspace’s name begins with a * positive number. Otherwise it’s a named ws and num will be -1. */ char *endptr = NULL; @@ -742,7 +743,7 @@ void ws_force_orientation(Con *ws, orientation_t orientation) { Con *workspace_attach_to(Con *ws) { DLOG("Attaching a window to workspace %p / %s\n", ws, ws->name); - if (config.default_layout == L_DEFAULT) { + if (ws->workspace_layout == L_DEFAULT) { DLOG("Default layout, just attaching it to the workspace itself.\n"); return ws; } @@ -754,7 +755,7 @@ Con *workspace_attach_to(Con *ws) { new->split = true; /* 2: set the requested layout on the split con */ - new->layout = config.default_layout; + new->layout = ws->workspace_layout; /* 4: attach the new split container to the workspace */ DLOG("Attaching new split %p to workspace %p\n", new, ws); diff --git a/testcases/t/116-nestedcons.t b/testcases/t/116-nestedcons.t index 79447386..70008801 100644 --- a/testcases/t/116-nestedcons.t +++ b/testcases/t/116-nestedcons.t @@ -68,6 +68,7 @@ my $expected = { urgent => JSON::XS::false, border => 'normal', 'floating_nodes' => $ignore, + workspace_layout => 'default', }; # a shallow copy is sufficient, since we only ignore values at the root