From: s3rb31 Date: Thu, 19 Jan 2017 19:49:56 +0000 (+0100) Subject: layout toggle: take any combination of layouts as arguments (#2649) X-Git-Tag: 4.14~49^2 X-Git-Url: https://git.sur5r.net/?a=commitdiff_plain;h=refs%2Fpull%2F2649%2Fhead;p=i3%2Fi3 layout toggle: take any combination of layouts as arguments (#2649) With this PR the 'layout toggle' command can be passed any combination of valid layout keywords as arguments. They will be activated one after another each time you issue the command, advancing from left to right always selecting the layout after the currently active layout or the leftmost layout if the active layout is not in the argument list. This PR also incorporates the feature request from #2476. --- diff --git a/docs/userguide b/docs/userguide index 6dc2241b..21d08625 100644 --- a/docs/userguide +++ b/docs/userguide @@ -1802,7 +1802,8 @@ The +toggle+ option will toggle the orientation of the split container if it 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 <>. *Syntax*: -------------------------------- @@ -1822,6 +1823,11 @@ Use +layout toggle split+, +layout stacking+, +layout tabbed+, +layout splitv+ 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 @@ -1834,6 +1840,7 @@ enable+ respectively +floating disable+ (or +floating toggle+): -------------------------------------------- layout default|tabbed|stacking|splitv|splith layout toggle [split|all] +layout toggle [split|tabbed|stacking|splitv|splith] [split|tabbed|stacking|splitv|splith]… -------------------------------------------- *Examples*: @@ -1848,6 +1855,15 @@ bindsym $mod+x layout toggle # 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 diff --git a/parser-specs/commands.spec b/parser-specs/commands.spec index d4b3dbc6..542ff798 100644 --- a/parser-specs/commands.spec +++ b/parser-specs/commands.spec @@ -110,7 +110,7 @@ state LAYOUT: 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 diff --git a/src/con.c b/src/con.c index 40924a73..b3f193e6 100644 --- a/src/con.c +++ b/src/con.c @@ -1719,28 +1719,64 @@ void con_toggle_layout(Con *con, const char *toggle_mode) { 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. */ diff --git a/src/util.c b/src/util.c index b6f45fde..0289ded9 100644 --- a/src/util.c +++ b/src/util.c @@ -66,7 +66,7 @@ __attribute__((pure)) bool name_is_digits(const char *name) { 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. @@ -75,20 +75,23 @@ __attribute__((pure)) bool name_is_digits(const char *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; } /* diff --git a/testcases/t/192-layout.t b/testcases/t/192-layout.t index 6fd6eae8..1d406fc6 100644 --- a/testcases/t/192-layout.t +++ b/testcases/t/192-layout.t @@ -95,4 +95,72 @@ cmd 'layout toggle all'; ($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;