Smart option added to hide_edge_borders config param (#2191) (#2191)

Use case:

* When managing multiple terminals in a workspace, the borders makes it easier
to know where the focus is, but when there is only one it's obvious where the
focus is.

* When there's only a web browser for example, the borders are actually counter-
productive since it makes clicking a side scrollbar or a tab a bit harder (if I
smash my cursor to the side or the top of the workspace, I have to move it in
the other direction by just a few pixels to be able to grab it)

Behaviour:

* No borders when there's a single window in a workspace
* Borders when there are multiple windows in a workspace

fixes #2188
This commit is contained in:
Julien Lequertier 2016-05-10 20:27:20 +02:00 committed by Michael Stapelberg
parent 47562b4143
commit 4bec3b9d24
9 changed files with 220 additions and 13 deletions

View File

@ -610,11 +610,13 @@ new_window pixel 3
You can hide container borders adjacent to the screen edges using You can hide container borders adjacent to the screen edges using
+hide_edge_borders+. This is useful if you are using scrollbars, or do not want +hide_edge_borders+. This is useful if you are using scrollbars, or do not want
to waste even two pixels in displayspace. Default is none. to waste even two pixels in displayspace. The "smart" setting hides borders on
workspaces with only one window visible, but keeps them on workspaces with
multiple windows visible. Default is none.
*Syntax*: *Syntax*:
----------------------------------------------- -----------------------------------------------
hide_edge_borders none|vertical|horizontal|both hide_edge_borders none|vertical|horizontal|both|smart
----------------------------------------------- -----------------------------------------------
*Example*: *Example*:

View File

@ -200,6 +200,13 @@ Con *con_for_window(Con *con, i3Window *window, Match **store_match);
*/ */
int con_num_children(Con *con); int con_num_children(Con *con);
/**
* Returns the number of visible non-floating children of this container.
* For example, if the container contains a hsplit which has two children,
* this will return 2 instead of 1.
*/
int con_num_visible_children(Con *con);
/** /**
* Count the number of windows (i.e., leaf containers). * Count the number of windows (i.e., leaf containers).
* *

View File

@ -125,7 +125,7 @@ struct Config {
* This is useful if you are reaching scrollbar on the edge of the * This is useful if you are reaching scrollbar on the edge of the
* screen or do not want to waste a single pixel of displayspace. * screen or do not want to waste a single pixel of displayspace.
* By default, this is disabled. */ * By default, this is disabled. */
adjacent_t hide_edge_borders; hide_edge_borders_mode_t hide_edge_borders;
/** By default, a workspace bar is drawn at the bottom of the screen. /** By default, a workspace bar is drawn at the bottom of the screen.
* If you want to have a more fancy bar, it is recommended to replace * If you want to have a more fancy bar, it is recommended to replace

View File

@ -75,6 +75,12 @@ typedef enum { ADJ_NONE = 0,
ADJ_UPPER_SCREEN_EDGE = (1 << 2), ADJ_UPPER_SCREEN_EDGE = (1 << 2),
ADJ_LOWER_SCREEN_EDGE = (1 << 4) } adjacent_t; ADJ_LOWER_SCREEN_EDGE = (1 << 4) } adjacent_t;
typedef enum { HEBM_NONE = ADJ_NONE,
HEBM_VERTICAL = ADJ_LEFT_SCREEN_EDGE | ADJ_RIGHT_SCREEN_EDGE,
HEBM_HORIZONTAL = ADJ_UPPER_SCREEN_EDGE | ADJ_LOWER_SCREEN_EDGE,
HEBM_BOTH = HEBM_VERTICAL | HEBM_HORIZONTAL,
HEBM_SMART = (1 << 5) } hide_edge_borders_mode_t;
typedef enum { MM_REPLACE, typedef enum { MM_REPLACE,
MM_ADD } mark_mode_t; MM_ADD } mark_mode_t;

View File

@ -122,10 +122,10 @@ state NEW_WINDOW_PIXELS_PX:
end end
-> call cfg_new_window($windowtype, $border, &width) -> call cfg_new_window($windowtype, $border, &width)
# hide_edge_borders <none|vertical|horizontal|both> # hide_edge_borders <none|vertical|horizontal|both|smart>
# also hide_edge_borders <bool> for compatibility # also hide_edge_borders <bool> for compatibility
state HIDE_EDGE_BORDERS: state HIDE_EDGE_BORDERS:
hide_borders = 'none', 'vertical', 'horizontal', 'both' hide_borders = 'none', 'vertical', 'horizontal', 'both', 'smart'
-> call cfg_hide_edge_borders($hide_borders) -> call cfg_hide_edge_borders($hide_borders)
hide_borders = '1', 'yes', 'true', 'on', 'enable', 'active' hide_borders = '1', 'yes', 'true', 'on', 'enable', 'active'
-> call cfg_hide_edge_borders($hide_borders) -> call cfg_hide_edge_borders($hide_borders)

View File

@ -727,6 +727,29 @@ int con_num_children(Con *con) {
return children; return children;
} }
/**
* Returns the number of visible non-floating children of this container.
* For example, if the container contains a hsplit which has two children,
* this will return 2 instead of 1.
*/
int con_num_visible_children(Con *con) {
if (con == NULL)
return 0;
int children = 0;
Con *current = NULL;
TAILQ_FOREACH(current, &(con->nodes_head), nodes) {
/* Visible leaf nodes are a child. */
if (!con_is_hidden(current) && con_is_leaf(current))
children++;
/* All other containers need to be recursed. */
else
children += con_num_visible_children(current);
}
return children;
}
/* /*
* Count the number of windows (i.e., leaf containers). * Count the number of windows (i.e., leaf containers).
* *
@ -1444,6 +1467,12 @@ Con *con_descend_direction(Con *con, direction_t direction) {
* *
*/ */
Rect con_border_style_rect(Con *con) { Rect con_border_style_rect(Con *con) {
if (config.hide_edge_borders == HEBM_SMART && con_num_visible_children(con_get_workspace(con)) <= 1) {
if (!con_is_floating(con)) {
return (Rect){0, 0, 0, 0};
}
}
adjacent_t borders_to_hide = ADJ_NONE; adjacent_t borders_to_hide = ADJ_NONE;
int border_width = con->current_border_width; int border_width = con->current_border_width;
DLOG("The border width for con is set to: %d\n", con->current_border_width); DLOG("The border width for con is set to: %d\n", con->current_border_width);

View File

@ -223,18 +223,20 @@ CFGFUN(new_window, const char *windowtype, const char *border, const long width)
} }
CFGFUN(hide_edge_borders, const char *borders) { CFGFUN(hide_edge_borders, const char *borders) {
if (strcmp(borders, "vertical") == 0) if (strcmp(borders, "smart") == 0)
config.hide_edge_borders = ADJ_LEFT_SCREEN_EDGE | ADJ_RIGHT_SCREEN_EDGE; config.hide_edge_borders = HEBM_SMART;
else if (strcmp(borders, "vertical") == 0)
config.hide_edge_borders = HEBM_VERTICAL;
else if (strcmp(borders, "horizontal") == 0) else if (strcmp(borders, "horizontal") == 0)
config.hide_edge_borders = ADJ_UPPER_SCREEN_EDGE | ADJ_LOWER_SCREEN_EDGE; config.hide_edge_borders = HEBM_HORIZONTAL;
else if (strcmp(borders, "both") == 0) else if (strcmp(borders, "both") == 0)
config.hide_edge_borders = ADJ_LEFT_SCREEN_EDGE | ADJ_RIGHT_SCREEN_EDGE | ADJ_UPPER_SCREEN_EDGE | ADJ_LOWER_SCREEN_EDGE; config.hide_edge_borders = HEBM_BOTH;
else if (strcmp(borders, "none") == 0) else if (strcmp(borders, "none") == 0)
config.hide_edge_borders = ADJ_NONE; config.hide_edge_borders = HEBM_NONE;
else if (eval_boolstr(borders)) else if (eval_boolstr(borders))
config.hide_edge_borders = ADJ_LEFT_SCREEN_EDGE | ADJ_RIGHT_SCREEN_EDGE; config.hide_edge_borders = HEBM_VERTICAL;
else else
config.hide_edge_borders = ADJ_NONE; config.hide_edge_borders = HEBM_NONE;
} }
CFGFUN(focus_follows_mouse, const char *value) { CFGFUN(focus_follows_mouse, const char *value) {

View File

@ -286,6 +286,7 @@ hide_edge_borders none
hide_edge_borders vertical hide_edge_borders vertical
hide_edge_borders horizontal hide_edge_borders horizontal
hide_edge_borders both hide_edge_borders both
hide_edge_borders smart
EOT EOT
$expected = <<'EOT'; $expected = <<'EOT';
@ -293,6 +294,7 @@ cfg_hide_edge_borders(none)
cfg_hide_edge_borders(vertical) cfg_hide_edge_borders(vertical)
cfg_hide_edge_borders(horizontal) cfg_hide_edge_borders(horizontal)
cfg_hide_edge_borders(both) cfg_hide_edge_borders(both)
cfg_hide_edge_borders(smart)
EOT EOT
is(parser_calls($config), is(parser_calls($config),
@ -464,7 +466,7 @@ client.focused #4c7899 #285577 #ffffff #2e9ef4
EOT EOT
$expected = <<'EOT'; $expected = <<'EOT';
ERROR: CONFIG: Expected one of these tokens: 'none', 'vertical', 'horizontal', 'both', '1', 'yes', 'true', 'on', 'enable', 'active' ERROR: CONFIG: Expected one of these tokens: 'none', 'vertical', 'horizontal', 'both', 'smart', '1', 'yes', 'true', 'on', 'enable', 'active'
ERROR: CONFIG: (in file <stdin>) ERROR: CONFIG: (in file <stdin>)
ERROR: CONFIG: Line 1: hide_edge_borders FOOBAR ERROR: CONFIG: Line 1: hide_edge_borders FOOBAR
ERROR: CONFIG: ^^^^^^ ERROR: CONFIG: ^^^^^^

View File

@ -0,0 +1,159 @@
#!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 the hide_edge_borders smart option works
# Ticket: #2188
use i3test i3_autostart => 0;
####################################################################
# 1: check that the borders are present on a floating windows
#####################################################################
my $config = <<EOT;
# i3 config file (v4)
font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
new_window pixel 2
new_float pixel 2
hide_edge_borders smart
EOT
my $pid = launch_with_config($config);
my $tmp = fresh_workspace;
ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
my $floatwindow = open_floating_window;
my $wscontent = get_ws($tmp);
my @floating = @{$wscontent->{floating_nodes}};
ok(@floating == 1, 'one floating container opened');
is($floating[0]->{nodes}[0]->{current_border_width}, 2, 'floating current border width set to 2');
is($floatwindow->rect->width, $floating[0]->{rect}->{width} - 2*2, 'floating border width 2');
exit_gracefully($pid);
#####################################################################
# 2: check that the borders are present on a workspace with two tiled
# windows visible
#####################################################################
$config = <<EOT;
# i3 config file (v4)
font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
new_window pixel 2
new_float pixel 2
hide_edge_borders smart
EOT
$pid = launch_with_config($config);
$tmp = fresh_workspace;
ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
my $tilewindow = open_window;
my $tilewindow2 = open_window;
$wscontent = get_ws($tmp);
my @tiled = @{$wscontent->{nodes}};
ok(@tiled == 2, 'two tiled container opened');
is($tiled[0]->{current_border_width}, 2, 'first tiled current border width set to 2');
is($tilewindow->rect->width, $tiled[0]->{rect}->{width} - 2*2, 'first tiled border width 2');
is($tiled[1]->{current_border_width}, 2, 'second tiled current border width set to 2');
is($tilewindow2->rect->width, $tiled[1]->{rect}->{width} - 2*2, 'second tiled border width 2');
exit_gracefully($pid);
#####################################################################
# 3: check that the borders are hidden on a workspace with one tiled
# window visible
#####################################################################
$config = <<EOT;
# i3 config file (v4)
font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
new_window pixel 2
new_float pixel 2
hide_edge_borders smart
EOT
$pid = launch_with_config($config);
$tmp = fresh_workspace;
ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
$tilewindow = open_window;
$wscontent = get_ws($tmp);
@tiled = @{$wscontent->{nodes}};
ok(@tiled == 1, 'one tiled container opened');
is($tiled[0]->{current_border_width}, 2, 'tiled current border width set to 2');
is($tilewindow->rect->width, $tiled[0]->{rect}->{width} - 2*0, 'single tiled border width 0');
exit_gracefully($pid);
#####################################################################
# 4: check that the borders are present on a workspace with two tiled
# windows visible, recursively
#####################################################################
$config = <<EOT;
# i3 config file (v4)
font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
new_window pixel 2
new_float pixel 2
hide_edge_borders smart
EOT
$pid = launch_with_config($config);
$tmp = fresh_workspace;
ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
$tilewindow = open_window;
$tilewindow2 = open_window;
ok(@{get_ws_content($tmp)} == 2, 'two containers opened');
cmd 'layout tabbed';
ok(@{get_ws_content($tmp)} == 1, 'layout tabbed -> back to one container');
cmd 'focus parent';
my $tilewindow3 = open_window;
ok(@{get_ws_content($tmp)} == 2, 'after split & new window, two containers');
$wscontent = get_ws($tmp);
@tiled = @{$wscontent->{nodes}};
ok(@tiled == 2, 'two tiled container opened in another container');
is($tiled[0]->{current_border_width}, -1, 'first tiled current border width set to -1');
is($tilewindow->rect->width, $tiled[0]->{rect}->{width} - 2*2, 'first tiled border width 2');
is($tiled[1]->{current_border_width}, 2, 'second tiled current border width set to 2');
is($tilewindow2->rect->width, $tiled[1]->{rect}->{width} - 2*2, 'second tiled border width 2');
exit_gracefully($pid);
done_testing;