Implement focus_wrapping workspace
I had a dilemma about the behaviour here: 1. Prohibit focus leaving the workspace in any case unless if get_tree_next's initial argument is a workspace. This is what this commit does (also i3-cycle). 2. Leave the workspace if no warp is possible (eg workspace with single container or `focus right` with `V[a b c*]`). Fixes #2180
This commit is contained in:
parent
bbc4c99c72
commit
24a58d2952
|
@ -1105,9 +1105,14 @@ If you want the focus to *always* wrap and you are aware of using +focus
|
|||
parent+ to switch to different containers, you can instead set +focus_wrapping+
|
||||
to the value +force+.
|
||||
|
||||
To restrict focus inside the current workspace set +focus_wrapping+ to the
|
||||
value +workspace+. You will need to use +focus parent+ until a workspace is
|
||||
selected to switch to a different workspace using the focus commands (the
|
||||
+workspace+ command will still work as expected).
|
||||
|
||||
*Syntax*:
|
||||
---------------------------
|
||||
focus_wrapping yes|no|force
|
||||
focus_wrapping yes|no|force|workspace
|
||||
|
||||
# Legacy syntax, equivalent to "focus_wrapping force"
|
||||
force_focus_wrapping yes
|
||||
|
|
|
@ -141,7 +141,8 @@ typedef enum {
|
|||
typedef enum {
|
||||
FOCUS_WRAPPING_OFF = 0,
|
||||
FOCUS_WRAPPING_ON = 1,
|
||||
FOCUS_WRAPPING_FORCE = 2
|
||||
FOCUS_WRAPPING_FORCE = 2,
|
||||
FOCUS_WRAPPING_WORKSPACE = 3
|
||||
} focus_wrapping_t;
|
||||
|
||||
/**
|
||||
|
|
|
@ -215,7 +215,7 @@ state MOUSE_WARPING:
|
|||
|
||||
# focus_wrapping
|
||||
state FOCUS_WRAPPING:
|
||||
value = '1', 'yes', 'true', 'on', 'enable', 'active', '0', 'no', 'false', 'off', 'disable', 'inactive', 'force'
|
||||
value = '1', 'yes', 'true', 'on', 'enable', 'active', '0', 'no', 'false', 'off', 'disable', 'inactive', 'force', 'workspace'
|
||||
-> call cfg_focus_wrapping($value)
|
||||
|
||||
# force_focus_wrapping
|
||||
|
|
|
@ -268,6 +268,8 @@ CFGFUN(disable_randr15, const char *value) {
|
|||
CFGFUN(focus_wrapping, const char *value) {
|
||||
if (strcmp(value, "force") == 0) {
|
||||
config.focus_wrapping = FOCUS_WRAPPING_FORCE;
|
||||
} else if (strcmp(value, "workspace") == 0) {
|
||||
config.focus_wrapping = FOCUS_WRAPPING_WORKSPACE;
|
||||
} else if (eval_boolstr(value)) {
|
||||
config.focus_wrapping = FOCUS_WRAPPING_ON;
|
||||
} else {
|
||||
|
|
13
src/tree.c
13
src/tree.c
|
@ -497,6 +497,13 @@ static Con *get_tree_next(Con *con, direction_t direction) {
|
|||
const orientation_t orientation = orientation_from_direction(direction);
|
||||
|
||||
Con *first_wrap = NULL;
|
||||
|
||||
if (con->type == CT_WORKSPACE) {
|
||||
/* Special case for FOCUS_WRAPPING_WORKSPACE: allow the focus to leave
|
||||
* the workspace only when a workspace is selected. */
|
||||
goto handle_workspace;
|
||||
}
|
||||
|
||||
while (con->type != CT_WORKSPACE) {
|
||||
if (con->fullscreen_mode == CF_OUTPUT) {
|
||||
/* We've reached a fullscreen container. Directional focus should
|
||||
|
@ -542,6 +549,7 @@ static Con *get_tree_next(Con *con, direction_t direction) {
|
|||
switch (config.focus_wrapping) {
|
||||
case FOCUS_WRAPPING_OFF:
|
||||
break;
|
||||
case FOCUS_WRAPPING_WORKSPACE:
|
||||
case FOCUS_WRAPPING_ON:
|
||||
if (!first_wrap && con_fullscreen_permits_focusing(wrap)) {
|
||||
first_wrap = wrap;
|
||||
|
@ -561,6 +569,11 @@ static Con *get_tree_next(Con *con, direction_t direction) {
|
|||
}
|
||||
|
||||
assert(con->type == CT_WORKSPACE);
|
||||
if (config.focus_wrapping == FOCUS_WRAPPING_WORKSPACE) {
|
||||
return first_wrap;
|
||||
}
|
||||
|
||||
handle_workspace:;
|
||||
Con *workspace = get_tree_next_workspace(con, direction);
|
||||
return workspace ? workspace : first_wrap;
|
||||
}
|
||||
|
|
|
@ -15,10 +15,11 @@
|
|||
# (unless you are already familiar with Perl)
|
||||
#
|
||||
# Tests focus_wrapping yes|no|force|workspace with cmp_tree
|
||||
# Tickets: #2352
|
||||
# Tickets: #2180 #2352
|
||||
use i3test i3_autostart => 0;
|
||||
|
||||
my $pid = 0;
|
||||
|
||||
sub focus_wrapping {
|
||||
my ($setting) = @_;
|
||||
|
||||
|
@ -249,7 +250,96 @@ cmp_tree( # 'focus_wrapping force|workspace' exclusive test
|
|||
is(focused_ws, 'right-top', 'Correct workspace focused');
|
||||
});
|
||||
|
||||
###############################################################################
|
||||
focus_wrapping('workspace');
|
||||
# See issue #2180
|
||||
###############################################################################
|
||||
|
||||
cmp_tree(
|
||||
msg => 'Normal focus up - should work for all options',
|
||||
layout_before => 'S[a b*] V[c d T[e f g]]',
|
||||
layout_after => 'S[a* b] V[c d T[e f g]]',
|
||||
ws => 'left-top',
|
||||
cb => sub {
|
||||
cmd 'focus up';
|
||||
});
|
||||
cmp_tree(
|
||||
msg => 'Normal focus right - should work for all options',
|
||||
layout_before => 'S[a b] V[c d T[e* f g]]',
|
||||
layout_after => 'S[a b] V[c d T[e f* g]]',
|
||||
ws => 'left-top',
|
||||
cb => sub {
|
||||
cmd 'focus right';
|
||||
});
|
||||
cmp_tree(
|
||||
msg => 'Focus does not leave workspace vertically',
|
||||
layout_before => 'S[a b*] V[c d T[e f g]]',
|
||||
layout_after => 'S[a* b] V[c d T[e f g]]',
|
||||
ws => 'left-top',
|
||||
cb => sub {
|
||||
cmd 'focus down';
|
||||
is(focused_ws, 'left-top', 'Correct workspace focused');
|
||||
});
|
||||
cmp_tree(
|
||||
msg => 'Focus wraps vertically',
|
||||
layout_before => 'S[a* b] V[c d T[e f g]]',
|
||||
layout_after => 'S[a b*] V[c d T[e f g]]',
|
||||
ws => 'left-top',
|
||||
cb => sub {
|
||||
cmd 'focus up';
|
||||
});
|
||||
cmp_tree(
|
||||
msg => 'Focus wraps horizontally',
|
||||
layout_before => 'S[a b*] V[c d T[e f g]]',
|
||||
layout_after => 'S[a b] V[c d T[e f g*]]',
|
||||
ws => 'left-top',
|
||||
cb => sub {
|
||||
cmd 'focus left';
|
||||
});
|
||||
cmp_tree(
|
||||
msg => 'Directional focus in the orientation of the parent does not wrap',
|
||||
layout_before => 'S[a b] V[c d T[e* f g]]',
|
||||
layout_after => 'S[a b*] V[c d T[e f g]]',
|
||||
ws => 'left-top',
|
||||
cb => sub {
|
||||
cmd 'focus left';
|
||||
});
|
||||
cmp_tree(
|
||||
msg => 'Focus does not leave workspace horizontally',
|
||||
layout_before => 'S[a b] V[c d* T[e f g]]',
|
||||
layout_after => 'S[a b*] V[c d T[e f g]]',
|
||||
ws => 'left-top',
|
||||
cb => sub {
|
||||
cmd 'focus right';
|
||||
is(focused_ws, 'left-top', 'Correct workspace focused');
|
||||
});
|
||||
cmp_tree( # 'focus_wrapping force|workspace' exclusive test
|
||||
msg => 'But leaves when selecting parent x2',
|
||||
layout_before => 'S[a b] V[c d* T[e f g]]',
|
||||
layout_after => 'S[a b] V[c d T[e f g]]',
|
||||
ws => 'left-top',
|
||||
cb => sub {
|
||||
cmd 'focus parent, focus parent, focus right';
|
||||
is(focused_ws, 'right-top', 'Correct workspace focused');
|
||||
});
|
||||
|
||||
cmp_tree( # 'focus_wrapping workspace' exclusive test
|
||||
msg => 'x',
|
||||
layout_before => 'S[a* b] V[c d T[e f g]]',
|
||||
layout_after => 'S[a b] V[c d T[e f g]]',
|
||||
ws => 'left-top',
|
||||
cb => sub {
|
||||
subtest 'random tests' => sub {
|
||||
my @directions = qw(left right top down);
|
||||
for my $i (1 .. 50) {
|
||||
my $direction = $directions[rand @directions];
|
||||
cmd "focus $direction";
|
||||
|
||||
return unless is(focused_ws, 'left-top', "'focus $direction' did not change workspace");
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
exit_gracefully($pid);
|
||||
|
||||
|
||||
done_testing;
|
||||
|
|
Loading…
Reference in New Issue