Merge pull request #2649 from s3rb31/next

layout toggle: take any combination of layouts as arguments (continuation of #2476)
This commit is contained in:
Ingo Bürk 2017-02-24 00:58:19 +01:00 committed by GitHub
commit c474ddd782
7 changed files with 173 additions and 30 deletions

View File

@ -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 contains a single window. Otherwise it makes the current window a split
container with opposite orientation compared to the parent container. container with opposite orientation compared to the parent container.
Use +layout toggle split+ to change the layout of any split container from 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*: *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, or +layout splith+ to change the current container layout to splith/splitv,
stacking, tabbed layout, splitv or splith, respectively. 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 To make the current window (!) fullscreen, use +fullscreen enable+ (or
+fullscreen enable global+ for the global mode), to leave either fullscreen +fullscreen enable global+ for the global mode), to leave either fullscreen
mode use +fullscreen disable+, and to toggle between these two states use 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 default|tabbed|stacking|splitv|splith
layout toggle [split|all] layout toggle [split|all]
layout toggle [split|tabbed|stacking|splitv|splith] [split|tabbed|stacking|splitv|splith]…
-------------------------------------------- --------------------------------------------
*Examples*: *Examples*:
@ -1848,6 +1855,15 @@ bindsym $mod+x layout toggle
# Toggle between stacking/tabbed/splith/splitv: # Toggle between stacking/tabbed/splith/splitv:
bindsym $mod+x layout toggle all 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 # Toggle fullscreen
bindsym $mod+f fullscreen toggle bindsym $mod+f fullscreen toggle

View File

@ -69,6 +69,14 @@ Rect rect_sub(Rect a, Rect b);
*/ */
__attribute__((pure)) bool name_is_digits(const char *name); __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 * Parses the workspace name as a number. Returns -1 if the workspace should be
* interpreted as a "named workspace". * interpreted as a "named workspace".

View File

@ -110,7 +110,7 @@ state LAYOUT:
state LAYOUT_TOGGLE: state LAYOUT_TOGGLE:
end end
-> call cmd_layout_toggle($toggle_mode) -> call cmd_layout_toggle($toggle_mode)
toggle_mode = 'split', 'all' toggle_mode = string
-> call cmd_layout_toggle($toggle_mode) -> call cmd_layout_toggle($toggle_mode)
# append_layout <path> # append_layout <path>

View File

@ -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) { void cmd_layout(I3_CMD, const char *layout_str) {
HANDLE_EMPTY_MATCH; HANDLE_EMPTY_MATCH;
if (strcmp(layout_str, "stacking") == 0)
layout_str = "stacked";
layout_t layout; layout_t layout;
/* default is a special case which will be handled in con_set_layout(). */ if (!layout_from_name(layout_str, &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 {
ELOG("Unknown layout \"%s\", this is a mismatch between code and parser spec.\n", layout_str); ELOG("Unknown layout \"%s\", this is a mismatch between code and parser spec.\n", layout_str);
return; return;
} }

View File

@ -1719,28 +1719,64 @@ void con_toggle_layout(Con *con, const char *toggle_mode) {
parent = con->parent; parent = con->parent;
DLOG("con_toggle_layout(%p, %s), parent = %p\n", con, toggle_mode, parent); DLOG("con_toggle_layout(%p, %s), parent = %p\n", con, toggle_mode, parent);
if (strcmp(toggle_mode, "split") == 0) { 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 /* Toggle between splits. When the current layout is not a split
* layout, we just switch back to last_split_layout. Otherwise, we * layout, we just switch back to last_split_layout. Otherwise, we
* change to the opposite split layout. */ * change to the opposite split layout. */
if (parent->layout != L_SPLITH && parent->layout != L_SPLITV) if (parent->layout != L_SPLITH && parent->layout != L_SPLITV) {
con_set_layout(con, parent->last_split_layout); layout = parent->last_split_layout;
else { } else {
if (parent->layout == L_SPLITH) layout = (parent->layout == L_SPLITH) ? L_SPLITV : L_SPLITH;
con_set_layout(con, L_SPLITV);
else
con_set_layout(con, L_SPLITH);
} }
} else { } 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;
}
}
con_set_layout(con, new_layout);
} else if (strcasecmp(toggle_mode, "all") == 0 || strcasecmp(toggle_mode, "default") == 0) {
if (parent->layout == L_STACKED) if (parent->layout == L_STACKED)
con_set_layout(con, L_TABBED); con_set_layout(con, L_TABBED);
else if (parent->layout == 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); con_set_layout(con, L_SPLITH);
else else
con_set_layout(con, parent->last_split_layout); con_set_layout(con, parent->last_split_layout);
} else if (parent->layout == L_SPLITH || parent->layout == L_SPLITV) { } 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 /* When toggling through all modes, we toggle between
* splith/splitv, whereas normally we just directly jump to * splith/splitv, whereas normally we just directly jump to
* stacked. */ * stacked. */

View File

@ -66,6 +66,34 @@ __attribute__((pure)) bool name_is_digits(const char *name) {
return true; 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;
return true;
}
return false;
}
/* /*
* Parses the workspace name as a number. Returns -1 if the workspace should be * Parses the workspace name as a number. Returns -1 if the workspace should be
* interpreted as a "named workspace". * interpreted as a "named workspace".

View File

@ -95,4 +95,72 @@ cmd 'layout toggle all';
($nodes, $focus) = get_ws_content($tmp); ($nodes, $focus) = get_ws_content($tmp);
is($nodes->[1]->{layout}, 'splitv', 'layout now splitv'); 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; done_testing;