From 3410cb256d3b033c2ca61c78766eb58652dce49f Mon Sep 17 00:00:00 2001 From: s3rb31 Date: Tue, 21 Feb 2017 02:12:39 +0100 Subject: [PATCH 1/2] Implement mapping from string to layout as extra function --- include/util.h | 8 ++++++++ src/commands.c | 15 +-------------- src/util.c | 25 +++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 14 deletions(-) diff --git a/include/util.h b/include/util.h index e5ba3341..cbe9778c 100644 --- a/include/util.h +++ b/include/util.h @@ -69,6 +69,14 @@ Rect rect_sub(Rect a, Rect b); */ __attribute__((pure)) bool name_is_digits(const char *name); +/** + * 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); + /** * Parses the workspace name as a number. Returns -1 if the workspace should be * interpreted as a "named workspace". diff --git a/src/commands.c b/src/commands.c index 2387ddd7..44a5d8a4 100644 --- a/src/commands.c +++ b/src/commands.c @@ -1489,21 +1489,8 @@ void cmd_move_direction(I3_CMD, const char *direction, long move_px) { void cmd_layout(I3_CMD, const char *layout_str) { HANDLE_EMPTY_MATCH; - if (strcmp(layout_str, "stacking") == 0) - layout_str = "stacked"; layout_t layout; - /* default is a special case which will be handled in con_set_layout(). */ - if (strcmp(layout_str, "default") == 0) - layout = L_DEFAULT; - else if (strcmp(layout_str, "stacked") == 0) - layout = L_STACKED; - else if (strcmp(layout_str, "tabbed") == 0) - layout = L_TABBED; - else if (strcmp(layout_str, "splitv") == 0) - layout = L_SPLITV; - else if (strcmp(layout_str, "splith") == 0) - layout = L_SPLITH; - else { + if (!layout_from_name(layout_str, &layout)) { ELOG("Unknown layout \"%s\", this is a mismatch between code and parser spec.\n", layout_str); return; } diff --git a/src/util.c b/src/util.c index cfe4c953..b6f45fde 100644 --- a/src/util.c +++ b/src/util.c @@ -66,6 +66,31 @@ __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. + * + */ +bool layout_from_name(const char *layout_str, layout_t *out) { + if (strcmp(layout_str, "default") == 0) { + *out = L_DEFAULT; + } else if (strcasecmp(layout_str, "stacked") == 0 || + strcasecmp(layout_str, "stacking") == 0) { + *out = L_STACKED; + } else if (strcasecmp(layout_str, "tabbed") == 0) { + *out = L_TABBED; + } else if (strcasecmp(layout_str, "splitv") == 0) { + *out = L_SPLITV; + } else if (strcasecmp(layout_str, "splith") == 0) { + *out = L_SPLITH; + } else { + return false; + } + + return true; +} + /* * Parses the workspace name as a number. Returns -1 if the workspace should be * interpreted as a "named workspace". From 37658bd6d7066489cef38dfbb8797975d2024b2a Mon Sep 17 00:00:00 2001 From: s3rb31 Date: Thu, 19 Jan 2017 20:49:56 +0100 Subject: [PATCH 2/2] 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. --- docs/userguide | 18 +++++++++- parser-specs/commands.spec | 2 +- src/con.c | 64 +++++++++++++++++++++++++++-------- src/util.c | 11 +++--- testcases/t/192-layout.t | 68 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 143 insertions(+), 20 deletions(-) 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;