contains a single window. Otherwise it makes the current window a split
container with opposite orientation compared to the parent container.
Use +layout toggle split+ to change the layout of any split container from
-splitv to splith or vice-versa.
+splitv to splith or vice-versa. You can also define a custom sequence of layouts
+to cycle through with +layout toggle+, see <<manipulating_layout>>.
*Syntax*:
--------------------------------
or +layout splith+ to change the current container layout to splith/splitv,
stacking, tabbed layout, splitv or splith, respectively.
+Specify up to four layouts after +layout toggle+ to cycle through them. Every
+time the command is executed, the layout specified after the currently active
+one will be applied. If the currently active layout is not in the list, the
+first layout in the list will be activated.
+
To make the current window (!) fullscreen, use +fullscreen enable+ (or
+fullscreen enable global+ for the global mode), to leave either fullscreen
mode use +fullscreen disable+, and to toggle between these two states use
--------------------------------------------
layout default|tabbed|stacking|splitv|splith
layout toggle [split|all]
+layout toggle [split|tabbed|stacking|splitv|splith] [split|tabbed|stacking|splitv|splith]…
--------------------------------------------
*Examples*:
# Toggle between stacking/tabbed/splith/splitv:
bindsym $mod+x layout toggle all
+# Toggle between stacking/tabbed/splith:
+bindsym $mod+x layout toggle stacking tabbed splith
+
+# Toggle between splitv/tabbed
+bindsym $mod+x layout toggle splitv tabbed
+
+# Toggle between last split layout/tabbed/stacking
+bindsym $mod+x layout toggle split tabbed stacking
+
# Toggle fullscreen
bindsym $mod+f fullscreen toggle
state LAYOUT_TOGGLE:
end
-> call cmd_layout_toggle($toggle_mode)
- toggle_mode = 'split', 'all'
+ toggle_mode = string
-> call cmd_layout_toggle($toggle_mode)
# append_layout <path>
parent = con->parent;
DLOG("con_toggle_layout(%p, %s), parent = %p\n", con, toggle_mode, parent);
- if (strcmp(toggle_mode, "split") == 0) {
- /* Toggle between splits. When the current layout is not a split
- * layout, we just switch back to last_split_layout. Otherwise, we
- * change to the opposite split layout. */
- if (parent->layout != L_SPLITH && parent->layout != L_SPLITV)
- con_set_layout(con, parent->last_split_layout);
- else {
- if (parent->layout == L_SPLITH)
- con_set_layout(con, L_SPLITV);
- else
- con_set_layout(con, L_SPLITH);
+ const char delim[] = " ";
+
+ if (strcasecmp(toggle_mode, "split") == 0 || strstr(toggle_mode, delim)) {
+ /* L_DEFAULT is used as a placeholder value to distinguish if
+ * the first layout has already been saved. (it can never be L_DEFAULT) */
+ layout_t new_layout = L_DEFAULT;
+ bool current_layout_found = false;
+ char *tm_dup = sstrdup(toggle_mode);
+ char *cur_tok = strtok(tm_dup, delim);
+
+ for (layout_t layout; cur_tok != NULL; cur_tok = strtok(NULL, delim)) {
+ if (strcasecmp(cur_tok, "split") == 0) {
+ /* Toggle between splits. When the current layout is not a split
+ * layout, we just switch back to last_split_layout. Otherwise, we
+ * change to the opposite split layout. */
+ if (parent->layout != L_SPLITH && parent->layout != L_SPLITV) {
+ layout = parent->last_split_layout;
+ } else {
+ layout = (parent->layout == L_SPLITH) ? L_SPLITV : L_SPLITH;
+ }
+ } else {
+ bool success = layout_from_name(cur_tok, &layout);
+ if (!success || layout == L_DEFAULT) {
+ ELOG("The token '%s' was not recognized and has been skipped.\n", cur_tok);
+ continue;
+ }
+ }
+
+ /* If none of the specified layouts match the current,
+ * fall back to the first layout in the list */
+ if (new_layout == L_DEFAULT) {
+ new_layout = layout;
+ }
+
+ /* We found the active layout in the last iteration, so
+ * now let's activate the current layout (next in list) */
+ if (current_layout_found) {
+ new_layout = layout;
+ free(tm_dup);
+ break;
+ }
+
+ if (parent->layout == layout) {
+ current_layout_found = true;
+ }
}
- } else {
+
+ con_set_layout(con, new_layout);
+ } else if (strcasecmp(toggle_mode, "all") == 0 || strcasecmp(toggle_mode, "default") == 0) {
if (parent->layout == L_STACKED)
con_set_layout(con, L_TABBED);
else if (parent->layout == L_TABBED) {
- if (strcmp(toggle_mode, "all") == 0)
+ if (strcasecmp(toggle_mode, "all") == 0)
con_set_layout(con, L_SPLITH);
else
con_set_layout(con, parent->last_split_layout);
} else if (parent->layout == L_SPLITH || parent->layout == L_SPLITV) {
- if (strcmp(toggle_mode, "all") == 0) {
+ if (strcasecmp(toggle_mode, "all") == 0) {
/* When toggling through all modes, we toggle between
* splith/splitv, whereas normally we just directly jump to
* stacked. */
return true;
}
-/**
+/*
* Set 'out' to the layout_t value for the given layout. The function
* returns true on success or false if the passed string is not a valid
* layout name.
bool layout_from_name(const char *layout_str, layout_t *out) {
if (strcmp(layout_str, "default") == 0) {
*out = L_DEFAULT;
+ return true;
} else if (strcasecmp(layout_str, "stacked") == 0 ||
strcasecmp(layout_str, "stacking") == 0) {
*out = L_STACKED;
+ return true;
} else if (strcasecmp(layout_str, "tabbed") == 0) {
*out = L_TABBED;
+ return true;
} else if (strcasecmp(layout_str, "splitv") == 0) {
*out = L_SPLITV;
+ return true;
} else if (strcasecmp(layout_str, "splith") == 0) {
*out = L_SPLITH;
- } else {
- return false;
+ return true;
}
- return true;
+ return false;
}
/*
($nodes, $focus) = get_ws_content($tmp);
is($nodes->[1]->{layout}, 'splitv', 'layout now splitv');
+cmd 'layout toggle splith splitv';
+($nodes, $focus) = get_ws_content($tmp);
+is($nodes->[1]->{layout}, 'splith', 'layout now splith');
+
+cmd 'layout toggle splith splitv';
+($nodes, $focus) = get_ws_content($tmp);
+is($nodes->[1]->{layout}, 'splitv', 'layout now splitv');
+
+cmd 'layout toggle stacked splitv tabbed';
+($nodes, $focus) = get_ws_content($tmp);
+is($nodes->[1]->{layout}, 'tabbed', 'layout now tabbed');
+
+cmd 'layout toggle stacking splitv tabbed';
+($nodes, $focus) = get_ws_content($tmp);
+is($nodes->[1]->{layout}, 'stacked', 'layout now stacked');
+
+cmd 'layout toggle stacking splitv tabbed';
+($nodes, $focus) = get_ws_content($tmp);
+is($nodes->[1]->{layout}, 'splitv', 'layout now splitv');
+
+cmd 'layout toggle splitv i stacking tabbed';
+($nodes, $focus) = get_ws_content($tmp);
+is($nodes->[1]->{layout}, 'stacked', 'layout now stacked');
+
+cmd 'layout toggle stacked';
+($nodes, $focus) = get_ws_content($tmp);
+# this is correct if it does nothing
+is($nodes->[1]->{layout}, 'stacked', 'layout now tabbed');
+
+cmd 'layout toggle tabbed stacked';
+($nodes, $focus) = get_ws_content($tmp);
+is($nodes->[1]->{layout}, 'tabbed', 'layout now stacked');
+
+# obsoletes 'split' ;)
+cmd 'layout toggle splith splitv';
+($nodes, $focus) = get_ws_content($tmp);
+is($nodes->[1]->{layout}, 'splith', 'layout now splith');
+
+# nonsense but works expectedly
+cmd 'layout toggle split split';
+($nodes, $focus) = get_ws_content($tmp);
+is($nodes->[1]->{layout}, 'splitv', 'layout now splitv');
+
+cmd 'layout toggle split split';
+($nodes, $focus) = get_ws_content($tmp);
+is($nodes->[1]->{layout}, 'splith', 'layout now splith');
+
+# testing with arbitrary length and garbage
+cmd 'layout toggle stacking splith tabbed splitv stacking';
+($nodes, $focus) = get_ws_content($tmp);
+is($nodes->[1]->{layout}, 'tabbed', 'layout now tabbed');
+
+cmd 'layout toggle stacking splith garbage tabbed splitv stacking';
+($nodes, $focus) = get_ws_content($tmp);
+is($nodes->[1]->{layout}, 'splitv', 'layout now splitv');
+
+cmd 'layout toggle stacking splith garbage tabbed splitv stacking';
+($nodes, $focus) = get_ws_content($tmp);
+is($nodes->[1]->{layout}, 'stacked', 'layout now stacked');
+
+cmd 'layout toggle splitv splith garbage splitv tabbed stacking splitv';
+($nodes, $focus) = get_ws_content($tmp);
+is($nodes->[1]->{layout}, 'splitv', 'layout now splitv');
+
+cmd 'layout toggle splitv garbage tabbed';
+($nodes, $focus) = get_ws_content($tmp);
+is($nodes->[1]->{layout}, 'tabbed', 'layout now tabbed');
+
done_testing;