From 4bec3b9d24fe0bd7bab4792497bbd02bffaa6620 Mon Sep 17 00:00:00 2001 From: Julien Lequertier Date: Tue, 10 May 2016 20:27:20 +0200 Subject: [PATCH] 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 --- docs/userguide | 6 +- include/con.h | 7 ++ include/config.h | 2 +- include/data.h | 6 ++ parser-specs/config.spec | 4 +- src/con.c | 29 ++++++ src/config_directives.c | 16 ++-- testcases/t/201-config-parser.t | 4 +- testcases/t/263-edge-borders.t | 159 ++++++++++++++++++++++++++++++++ 9 files changed, 220 insertions(+), 13 deletions(-) create mode 100644 testcases/t/263-edge-borders.t diff --git a/docs/userguide b/docs/userguide index d9cfc642..9abdd5b0 100644 --- a/docs/userguide +++ b/docs/userguide @@ -610,11 +610,13 @@ new_window pixel 3 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 -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*: ----------------------------------------------- -hide_edge_borders none|vertical|horizontal|both +hide_edge_borders none|vertical|horizontal|both|smart ----------------------------------------------- *Example*: diff --git a/include/con.h b/include/con.h index 7fa7facf..73ae0f38 100644 --- a/include/con.h +++ b/include/con.h @@ -200,6 +200,13 @@ Con *con_for_window(Con *con, i3Window *window, Match **store_match); */ 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). * diff --git a/include/config.h b/include/config.h index acdd2c89..699cc882 100644 --- a/include/config.h +++ b/include/config.h @@ -125,7 +125,7 @@ struct Config { * 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. * 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. * If you want to have a more fancy bar, it is recommended to replace diff --git a/include/data.h b/include/data.h index 3a059e7b..88ed6879 100644 --- a/include/data.h +++ b/include/data.h @@ -75,6 +75,12 @@ typedef enum { ADJ_NONE = 0, ADJ_UPPER_SCREEN_EDGE = (1 << 2), 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, MM_ADD } mark_mode_t; diff --git a/parser-specs/config.spec b/parser-specs/config.spec index 24f40802..eeafaac8 100644 --- a/parser-specs/config.spec +++ b/parser-specs/config.spec @@ -122,10 +122,10 @@ state NEW_WINDOW_PIXELS_PX: end -> call cfg_new_window($windowtype, $border, &width) -# hide_edge_borders +# hide_edge_borders # also hide_edge_borders for compatibility state HIDE_EDGE_BORDERS: - hide_borders = 'none', 'vertical', 'horizontal', 'both' + hide_borders = 'none', 'vertical', 'horizontal', 'both', 'smart' -> call cfg_hide_edge_borders($hide_borders) hide_borders = '1', 'yes', 'true', 'on', 'enable', 'active' -> call cfg_hide_edge_borders($hide_borders) diff --git a/src/con.c b/src/con.c index 2c637a1f..dbb6d601 100644 --- a/src/con.c +++ b/src/con.c @@ -727,6 +727,29 @@ int con_num_children(Con *con) { 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). * @@ -1444,6 +1467,12 @@ Con *con_descend_direction(Con *con, direction_t direction) { * */ 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; int border_width = con->current_border_width; DLOG("The border width for con is set to: %d\n", con->current_border_width); diff --git a/src/config_directives.c b/src/config_directives.c index ec99321a..05c19c70 100644 --- a/src/config_directives.c +++ b/src/config_directives.c @@ -223,18 +223,20 @@ CFGFUN(new_window, const char *windowtype, const char *border, const long width) } CFGFUN(hide_edge_borders, const char *borders) { - if (strcmp(borders, "vertical") == 0) - config.hide_edge_borders = ADJ_LEFT_SCREEN_EDGE | ADJ_RIGHT_SCREEN_EDGE; + if (strcmp(borders, "smart") == 0) + config.hide_edge_borders = HEBM_SMART; + else if (strcmp(borders, "vertical") == 0) + config.hide_edge_borders = HEBM_VERTICAL; 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) - 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) - config.hide_edge_borders = ADJ_NONE; + config.hide_edge_borders = HEBM_NONE; else if (eval_boolstr(borders)) - config.hide_edge_borders = ADJ_LEFT_SCREEN_EDGE | ADJ_RIGHT_SCREEN_EDGE; + config.hide_edge_borders = HEBM_VERTICAL; else - config.hide_edge_borders = ADJ_NONE; + config.hide_edge_borders = HEBM_NONE; } CFGFUN(focus_follows_mouse, const char *value) { diff --git a/testcases/t/201-config-parser.t b/testcases/t/201-config-parser.t index bcccc5a2..d3da4235 100644 --- a/testcases/t/201-config-parser.t +++ b/testcases/t/201-config-parser.t @@ -286,6 +286,7 @@ hide_edge_borders none hide_edge_borders vertical hide_edge_borders horizontal hide_edge_borders both +hide_edge_borders smart EOT $expected = <<'EOT'; @@ -293,6 +294,7 @@ cfg_hide_edge_borders(none) cfg_hide_edge_borders(vertical) cfg_hide_edge_borders(horizontal) cfg_hide_edge_borders(both) +cfg_hide_edge_borders(smart) EOT is(parser_calls($config), @@ -464,7 +466,7 @@ client.focused #4c7899 #285577 #ffffff #2e9ef4 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 ) ERROR: CONFIG: Line 1: hide_edge_borders FOOBAR ERROR: CONFIG: ^^^^^^ diff --git a/testcases/t/263-edge-borders.t b/testcases/t/263-edge-borders.t new file mode 100644 index 00000000..0d14c65c --- /dev/null +++ b/testcases/t/263-edge-borders.t @@ -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 = <{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 = <{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 = <{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 = < 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; -- 2.39.5