Merge pull request #2953 from CyberShadow/focus_wrapping

Add "focus_wrapping" option
This commit is contained in:
Michael Stapelberg 2017-09-27 09:31:39 -07:00 committed by GitHub
commit 54d61b510d
10 changed files with 137 additions and 25 deletions

View File

@ -1055,26 +1055,39 @@ popup_during_fullscreen smart
=== Focus wrapping
When being in a tabbed or stacked container, the first container will be
focused when you use +focus down+ on the last container -- the focus wraps. If
however there is another stacked/tabbed container in that direction, focus will
be set on that container. This is the default behavior so you can navigate to
all your windows without having to use +focus parent+.
By default, when in a container with several windows or child containers, the
opposite window will be focused when trying to move the focus over the edge of
a container (and there are no other containers in that direction) -- the focus
wraps.
If desired, you can disable this behavior by setting the +focus_wrapping+
configuration directive to the value +no+.
When enabled, focus wrapping does not occur by default if there is another
window or container in the specified direction, and focus will instead be set
on that window or container. This is the default behavior so you can navigate
to all your windows without having to use +focus parent+.
If you want the focus to *always* wrap and you are aware of using +focus
parent+ to switch to different containers, you can use the
+force_focus_wrapping+ configuration directive. After enabling it, the focus
will always wrap.
parent+ to switch to different containers, you can instead set +focus_wrapping+
to the value +force+.
*Syntax*:
---------------------------
force_focus_wrapping yes|no
focus_wrapping yes|no|force
# Legacy syntax, equivalent to "focus_wrapping force"
force_focus_wrapping yes
---------------------------
*Example*:
------------------------
force_focus_wrapping yes
------------------------
*Examples*:
-----------------
# Disable focus wrapping
focus_wrapping no
# Force focus wrapping
focus_wrapping force
-----------------
=== Forcing Xinerama

View File

@ -49,6 +49,7 @@ CFGFUN(workspace_layout, const char *layout);
CFGFUN(workspace_back_and_forth, const char *value);
CFGFUN(focus_follows_mouse, const char *value);
CFGFUN(mouse_warping, const char *value);
CFGFUN(focus_wrapping, const char *value);
CFGFUN(force_focus_wrapping, const char *value);
CFGFUN(force_xinerama, const char *value);
CFGFUN(disable_randr15, const char *value);

View File

@ -137,15 +137,24 @@ struct Config {
* comes with i3. Thus, you can turn it off entirely. */
bool disable_workspace_bar;
/** Think of the following layout: Horizontal workspace with a tabbed
* con on the left of the screen and a terminal on the right of the
* screen. You are in the second container in the tabbed container and
* focus to the right. By default, i3 will set focus to the terminal on
* the right. If you are in the first container in the tabbed container
* however, focusing to the left will wrap. This option forces i3 to
* always wrap, which will result in you having to use "focus parent"
* more often. */
bool force_focus_wrapping;
/** When focus wrapping is enabled (the default), attempting to
* move focus past the edge of the screen (in other words, in a
* direction in which there are no more containers to focus) will
* cause the focus to wrap to the opposite edge of the current
* container. When it is disabled, nothing happens; the current
* focus is preserved.
*
* Additionally, focus wrapping may be forced. Think of the
* following layout: Horizontal workspace with a tabbed con on the
* left of the screen and a terminal on the right of the
* screen. You are in the second container in the tabbed container
* and focus to the right. By default, i3 will set focus to the
* terminal on the right. If you are in the first container in the
* tabbed container however, focusing to the left will
* wrap. Setting focus_wrapping to FOCUS_WRAPPING_FORCE forces i3
* to always wrap, which will result in you having to use "focus
* parent" more often. */
focus_wrapping_t focus_wrapping;
/** By default, use the RandR API for multi-monitor setups.
* Unfortunately, the nVidia binary graphics driver doesn't support

View File

@ -133,6 +133,15 @@ typedef enum {
POINTER_WARPING_NONE = 1
} warping_t;
/**
* Focus wrapping modes.
*/
typedef enum {
FOCUS_WRAPPING_OFF = 0,
FOCUS_WRAPPING_ON = 1,
FOCUS_WRAPPING_FORCE = 2
} focus_wrapping_t;
/**
* Stores a rectangle, for example the size of a window, the child window etc.
* It needs to be packed so that the compiler will not add any padding bytes.

View File

@ -36,6 +36,7 @@ state INITIAL:
'no_focus' -> NO_FOCUS
'focus_follows_mouse' -> FOCUS_FOLLOWS_MOUSE
'mouse_warping' -> MOUSE_WARPING
'focus_wrapping' -> FOCUS_WRAPPING
'force_focus_wrapping' -> FORCE_FOCUS_WRAPPING
'force_xinerama', 'force-xinerama' -> FORCE_XINERAMA
'disable_randr15', 'disable-randr15' -> DISABLE_RANDR15
@ -209,6 +210,11 @@ state MOUSE_WARPING:
value = 'none', 'output'
-> call cfg_mouse_warping($value)
# focus_wrapping
state FOCUS_WRAPPING:
value = '1', 'yes', 'true', 'on', 'enable', 'active', '0', 'no', 'false', 'off', 'disable', 'inactive', 'force'
-> call cfg_focus_wrapping($value)
# force_focus_wrapping
state FORCE_FOCUS_WRAPPING:
value = word

View File

@ -227,6 +227,8 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
if (config.workspace_urgency_timer == 0)
config.workspace_urgency_timer = 0.5;
config.focus_wrapping = FOCUS_WRAPPING_ON;
parse_configuration(override_configpath, true);
if (reload) {

View File

@ -264,8 +264,27 @@ CFGFUN(disable_randr15, const char *value) {
config.disable_randr15 = eval_boolstr(value);
}
CFGFUN(focus_wrapping, const char *value) {
if (strcmp(value, "force") == 0) {
config.focus_wrapping = FOCUS_WRAPPING_FORCE;
} else if (eval_boolstr(value)) {
config.focus_wrapping = FOCUS_WRAPPING_ON;
} else {
config.focus_wrapping = FOCUS_WRAPPING_OFF;
}
}
CFGFUN(force_focus_wrapping, const char *value) {
config.force_focus_wrapping = eval_boolstr(value);
/* Legacy syntax. */
if (eval_boolstr(value)) {
config.focus_wrapping = FOCUS_WRAPPING_FORCE;
} else {
/* For "force_focus_wrapping off", don't enable or disable
* focus wrapping, just ensure it's not forced. */
if (config.focus_wrapping == FOCUS_WRAPPING_FORCE) {
config.focus_wrapping = FOCUS_WRAPPING_ON;
}
}
}
CFGFUN(workspace_back_and_forth, const char *value) {

View File

@ -641,7 +641,7 @@ static bool _tree_next(Con *con, char way, orientation_t orientation, bool wrap)
next = TAILQ_PREV(current, nodes_head, nodes);
if (!next) {
if (!config.force_focus_wrapping) {
if (config.focus_wrapping != FOCUS_WRAPPING_FORCE) {
/* If there is no next/previous container, we check if we can focus one
* when going higher (without wrapping, though). If so, we are done, if
* not, we wrap */
@ -675,7 +675,8 @@ static bool _tree_next(Con *con, char way, orientation_t orientation, bool wrap)
*
*/
void tree_next(char way, orientation_t orientation) {
_tree_next(focused, way, orientation, true);
_tree_next(focused, way, orientation,
config.focus_wrapping != FOCUS_WRAPPING_OFF);
}
/*

View File

@ -470,6 +470,7 @@ my $expected_all_tokens = "ERROR: CONFIG: Expected one of these tokens: <end>, '
no_focus
focus_follows_mouse
mouse_warping
focus_wrapping
force_focus_wrapping
force_xinerama
force-xinerama

View File

@ -0,0 +1,51 @@
#!perl
# vim:ts=4:sw=4:expandtab
#
# Please read the following documents before working on tests:
# • http://build.i3wm.org/docs/testsuite.html
# (or docs/testsuite)
#
# • http://build.i3wm.org/docs/lib-i3test.html
# (alternatively: perldoc ./testcases/lib/i3test.pm)
#
# • http://build.i3wm.org/docs/ipc.html
# (or docs/ipc)
#
# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
# (unless you are already familiar with Perl)
#
# Tests that focus does not wrap when focus_wrapping is disabled in
# the configuration.
# Ticket: #2352
# Bug still in: 4.14-72-g6411130c
use i3test i3_config => <<EOT;
# i3 config file (v4)
font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
focus_wrapping no
EOT
sub test_orientation {
my ($orientation, $prev, $next) = @_;
my $tmp = fresh_workspace;
cmd "split $orientation";
my $win1 = open_window;
my $win2 = open_window;
is($x->input_focus, $win2->id, "Second window focused initially");
cmd "focus $prev";
is($x->input_focus, $win1->id, "First window focused");
cmd "focus $prev";
is($x->input_focus, $win1->id, "First window still focused");
cmd "focus $next";
is($x->input_focus, $win2->id, "Second window focused");
cmd "focus $next";
is($x->input_focus, $win2->id, "Second window still focused");
}
test_orientation('v', 'up', 'down');
test_orientation('h', 'left', 'right');
done_testing;