diff --git a/docs/ipc b/docs/ipc index ea7a5892a..df93a7d1f 100644 --- a/docs/ipc +++ b/docs/ipc @@ -357,6 +357,14 @@ focus (array of integer):: order. Traversing the tree by following the first entry in this array will result in eventually reaching the one node with +focused+ set to true. +fullscreen_mode (integer):: + Whether this container is in fullscreen state or not. + Possible values are + +0+ (no fullscreen), + +1+ (fullscreened on output) or + +2+ (fullscreened globally). + Note that all workspaces are considered fullscreened on their respective output. + nodes (array of node):: The tiling (i.e. non-floating) child containers of this node. floating_nodes (array of node):: diff --git a/docs/userguide b/docs/userguide index 5851d02c1..5637129eb 100644 --- a/docs/userguide +++ b/docs/userguide @@ -1625,6 +1625,35 @@ bar { } ------------------------ +=== Minimal width for workspace buttons + +By default, the width a workspace button is determined by the width of the text +showing the workspace name. If the name is too short (say, one letter), then the +workspace button may look too small. + +This option specifies the minimum width for workspace buttons. If the name of +a workspace is too short to cover the button, an additional padding is added on +both sides of the button so that the text is centered. + +The default value of zero means that no additional padding is added. + +The setting also applies to the current binding mode indicator. + +Note that the specified pixels refer to logical pixels, which may translate +into more pixels on HiDPI displays. + +*Syntax*: +------------------------ +workspace_min_width [px] +------------------------ + +*Example*: +------------------------ +bar { + workspace_min_width 40 +} +------------------------ + === Strip workspace numbers/name Specifies whether workspace numbers should be displayed within the workspace diff --git a/i3bar/include/configuration.h b/i3bar/include/configuration.h index a843474a8..6fd304137 100644 --- a/i3bar/include/configuration.h +++ b/i3bar/include/configuration.h @@ -53,6 +53,7 @@ typedef struct config_t { struct xcb_color_strings_t colors; bool disable_binding_mode_indicator; bool disable_ws; + int ws_min_width; bool strip_ws_numbers; bool strip_ws_name; char *bar_id; diff --git a/i3bar/include/mode.h b/i3bar/include/mode.h index 5c87d9048..e8e4296d2 100644 --- a/i3bar/include/mode.h +++ b/i3bar/include/mode.h @@ -18,7 +18,7 @@ /* Name of current binding mode and its render width */ struct mode { i3String *name; - int width; + int name_width; }; typedef struct mode mode; diff --git a/i3bar/src/config.c b/i3bar/src/config.c index a11382e0c..18096c39a 100644 --- a/i3bar/src/config.c +++ b/i3bar/src/config.c @@ -351,6 +351,12 @@ static int config_integer_cb(void *params_, long long val) { return 1; } + if (!strcmp(cur_key, "workspace_min_width")) { + DLOG("workspace_min_width = %lld\n", val); + config.ws_min_width = val; + return 1; + } + return 0; } diff --git a/i3bar/src/mode.c b/i3bar/src/mode.c index 17e7b97ea..97087ce09 100644 --- a/i3bar/src/mode.c +++ b/i3bar/src/mode.c @@ -81,7 +81,7 @@ static int mode_end_map_cb(void *params_) { params->mode->name = i3string_from_utf8(params->name); i3string_set_markup(params->mode->name, params->pango_markup); /* Save its rendered width */ - params->mode->width = predict_text_width(params->mode->name); + params->mode->name_width = predict_text_width(params->mode->name); DLOG("Got mode change: %s\n", i3string_as_utf8(params->mode->name)); FREE(params->cur_key); diff --git a/i3bar/src/xcb.c b/i3bar/src/xcb.c index b56a13baf..77a91edd7 100644 --- a/i3bar/src/xcb.c +++ b/i3bar/src/xcb.c @@ -499,6 +499,15 @@ static void child_handle_button(xcb_button_press_event_t *event, i3_output *outp } } +/* + * Predict the width of a workspace button or the current binding mode indicator. + * + */ +static int predict_button_width(int name_width) { + return MAX(name_width + 2 * logical_px(ws_hoff_px) + 2 * logical_px(1), + logical_px(config.ws_min_width)); +} + /* * Handle a button press event (i.e. a mouse click on one of our bars). * We determine, whether the click occurred on a workspace button or if the scroll- @@ -530,7 +539,7 @@ static void handle_button(xcb_button_press_event_t *event) { i3_ws *cur_ws = NULL, *clicked_ws = NULL, *ws_walk; TAILQ_FOREACH(ws_walk, walk->workspaces, tailq) { - int w = 2 * logical_px(ws_hoff_px) + 2 * logical_px(1) + ws_walk->name_width; + int w = predict_button_width(ws_walk->name_width); if (x >= workspace_width && x <= workspace_width + w) clicked_ws = ws_walk; if (ws_walk->visible) @@ -1917,6 +1926,25 @@ void reconfig_windows(bool redraw_bars) { } } +/* + * Draw the button for a workspace or the current binding mode indicator. + * + */ +static void draw_button(surface_t *surface, color_t fg_color, color_t bg_color, color_t border_color, + int x, int width, int text_width, i3String *text) { + int height = bar_height - 2 * logical_px(1); + + /* Draw the border of the button. */ + draw_util_rectangle(surface, border_color, x, logical_px(1), width, height); + + /* Draw the inside of the button. */ + draw_util_rectangle(surface, bg_color, x + logical_px(1), 2 * logical_px(1), + width - 2 * logical_px(1), height - 2 * logical_px(1)); + + draw_util_text(text, surface, fg_color, bg_color, x + (width - text_width) / 2, + bar_height / 2 - font.height / 2, text_width); +} + /* * Render the bars, with buttons and statusline * @@ -1972,26 +2000,11 @@ void draw_bars(bool unhide) { unhide = true; } - /* Draw the border of the button. */ - draw_util_rectangle(&(outputs_walk->buffer), border_color, - workspace_width, - logical_px(1), - ws_walk->name_width + 2 * logical_px(ws_hoff_px) + 2 * logical_px(1), - bar_height - 2 * logical_px(1)); - - /* Draw the inside of the button. */ - draw_util_rectangle(&(outputs_walk->buffer), bg_color, - workspace_width + logical_px(1), - 2 * logical_px(1), - ws_walk->name_width + 2 * logical_px(ws_hoff_px), - bar_height - 4 * logical_px(1)); - - draw_util_text(ws_walk->name, &(outputs_walk->buffer), fg_color, bg_color, - workspace_width + logical_px(ws_hoff_px) + logical_px(1), - bar_height / 2 - font.height / 2, - ws_walk->name_width); - - workspace_width += 2 * logical_px(ws_hoff_px) + 2 * logical_px(1) + ws_walk->name_width; + int w = predict_button_width(ws_walk->name_width); + draw_button(&(outputs_walk->buffer), fg_color, bg_color, border_color, + workspace_width, w, ws_walk->name_width, ws_walk->name); + + workspace_width += w; if (TAILQ_NEXT(ws_walk, tailq) != NULL) workspace_width += logical_px(ws_spacing_px); } @@ -2000,28 +2013,12 @@ void draw_bars(bool unhide) { if (binding.name && !config.disable_binding_mode_indicator) { workspace_width += logical_px(ws_spacing_px); - color_t fg_color = colors.binding_mode_fg; - color_t bg_color = colors.binding_mode_bg; - - draw_util_rectangle(&(outputs_walk->buffer), colors.binding_mode_border, - workspace_width, - logical_px(1), - binding.width + 2 * logical_px(ws_hoff_px) + 2 * logical_px(1), - bar_height - 2 * logical_px(1)); - - draw_util_rectangle(&(outputs_walk->buffer), bg_color, - workspace_width + logical_px(1), - 2 * logical_px(1), - binding.width + 2 * logical_px(ws_hoff_px), - bar_height - 4 * logical_px(1)); - - draw_util_text(binding.name, &(outputs_walk->buffer), fg_color, bg_color, - workspace_width + logical_px(ws_hoff_px) + logical_px(1), - bar_height / 2 - font.height / 2, - binding.width); + int w = predict_button_width(binding.name_width); + draw_button(&(outputs_walk->buffer), colors.binding_mode_fg, colors.binding_mode_bg, + colors.binding_mode_border, workspace_width, w, binding.name_width, binding.name); unhide = true; - workspace_width += 2 * logical_px(ws_hoff_px) + 2 * logical_px(1) + binding.width; + workspace_width += w; } if (!TAILQ_EMPTY(&statusline_head)) { diff --git a/include/config_directives.h b/include/config_directives.h index 3662bbc14..e8d4bd2d5 100644 --- a/include/config_directives.h +++ b/include/config_directives.h @@ -101,6 +101,7 @@ CFGFUN(bar_color_single, const char *colorclass, const char *color); CFGFUN(bar_status_command, const char *command); CFGFUN(bar_binding_mode_indicator, const char *value); CFGFUN(bar_workspace_buttons, const char *value); +CFGFUN(bar_workspace_min_width, const long width); CFGFUN(bar_strip_workspace_numbers, const char *value); CFGFUN(bar_strip_workspace_name, const char *value); CFGFUN(bar_start); diff --git a/include/configuration.h b/include/configuration.h index 9e5b61407..cd5daa8e1 100644 --- a/include/configuration.h +++ b/include/configuration.h @@ -334,6 +334,9 @@ struct Barconfig { * zero. */ bool hide_workspace_buttons; + /** The minimal width for workspace buttons. */ + int workspace_min_width; + /** Strip workspace numbers? Configuration option is * 'strip_workspace_numbers yes'. */ bool strip_workspace_numbers; diff --git a/parser-specs/config.spec b/parser-specs/config.spec index 9ce65b870..e8f505508 100644 --- a/parser-specs/config.spec +++ b/parser-specs/config.spec @@ -498,6 +498,7 @@ state BAR: 'separator_symbol' -> BAR_SEPARATOR_SYMBOL 'binding_mode_indicator' -> BAR_BINDING_MODE_INDICATOR 'workspace_buttons' -> BAR_WORKSPACE_BUTTONS + 'workspace_min_width' -> BAR_WORKSPACE_MIN_WIDTH 'strip_workspace_numbers' -> BAR_STRIP_WORKSPACE_NUMBERS 'strip_workspace_name' -> BAR_STRIP_WORKSPACE_NAME 'verbose' -> BAR_VERBOSE @@ -603,6 +604,16 @@ state BAR_WORKSPACE_BUTTONS: value = word -> call cfg_bar_workspace_buttons($value); BAR +state BAR_WORKSPACE_MIN_WIDTH: + width = number + -> BAR_WORKSPACE_MIN_WIDTH_PX + +state BAR_WORKSPACE_MIN_WIDTH_PX: + 'px' + -> + end + -> call cfg_bar_workspace_min_width(&width); BAR + state BAR_STRIP_WORKSPACE_NUMBERS: value = word -> call cfg_bar_strip_workspace_numbers($value); BAR diff --git a/src/config_directives.c b/src/config_directives.c index c78e7ba62..4b954d09a 100644 --- a/src/config_directives.c +++ b/src/config_directives.c @@ -808,6 +808,10 @@ CFGFUN(bar_workspace_buttons, const char *value) { current_bar->hide_workspace_buttons = !eval_boolstr(value); } +CFGFUN(bar_workspace_min_width, const long width) { + current_bar->workspace_min_width = width; +} + CFGFUN(bar_strip_workspace_numbers, const char *value) { current_bar->strip_workspace_numbers = eval_boolstr(value); } diff --git a/src/floating.c b/src/floating.c index b6ab567bd..462510225 100644 --- a/src/floating.c +++ b/src/floating.c @@ -371,6 +371,7 @@ void floating_enable(Con *con, bool automatic) { if (nc->rect.x == 0 && nc->rect.y == 0) { Con *leader; if (con->window && con->window->leader != XCB_NONE && + con->window->id != con->window->leader && (leader = con_by_window_id(con->window->leader)) != NULL) { DLOG("Centering above leader\n"); floating_center(nc, leader->rect); diff --git a/src/ipc.c b/src/ipc.c index 531e1910d..5afe421d3 100644 --- a/src/ipc.c +++ b/src/ipc.c @@ -818,6 +818,9 @@ static void dump_bar_config(yajl_gen gen, Barconfig *config) { ystr("workspace_buttons"); y(bool, !config->hide_workspace_buttons); + ystr("workspace_min_width"); + y(integer, config->workspace_min_width); + ystr("strip_workspace_numbers"); y(bool, config->strip_workspace_numbers); diff --git a/src/workspace.c b/src/workspace.c index 953a062aa..d4dcaed3b 100644 --- a/src/workspace.c +++ b/src/workspace.c @@ -213,18 +213,27 @@ void extract_workspace_names_from_bindings(void) { while (*target == ' ' || *target == '\t') target++; /* We check if this is the workspace - * next/prev/next_on_output/prev_on_output/back_and_forth/number command. + * next/prev/next_on_output/prev_on_output/back_and_forth command. * Beware: The workspace names "next", "prev", "next_on_output", - * "prev_on_output", "number", "back_and_forth" and "current" are OK, + * "prev_on_output", "back_and_forth" and "current" are OK, * so we check before stripping the double quotes */ if (strncasecmp(target, "next", strlen("next")) == 0 || strncasecmp(target, "prev", strlen("prev")) == 0 || strncasecmp(target, "next_on_output", strlen("next_on_output")) == 0 || strncasecmp(target, "prev_on_output", strlen("prev_on_output")) == 0 || - strncasecmp(target, "number", strlen("number")) == 0 || strncasecmp(target, "back_and_forth", strlen("back_and_forth")) == 0 || strncasecmp(target, "current", strlen("current")) == 0) continue; + if (strncasecmp(target, "--no-auto-back-and-forth", strlen("--no-auto-back-and-forth")) == 0) { + target += strlen("--no-auto-back-and-forth"); + while (*target == ' ' || *target == '\t') + target++; + } + if (strncasecmp(target, "number", strlen("number")) == 0) { + target += strlen("number"); + while (*target == ' ' || *target == '\t') + target++; + } char *target_name = parse_string(&target, false); if (target_name == NULL) continue; diff --git a/testcases/t/172-start-on-named-ws.t b/testcases/t/172-start-on-named-ws.t index 778eb23bf..d1af6c07b 100644 --- a/testcases/t/172-start-on-named-ws.t +++ b/testcases/t/172-start-on-named-ws.t @@ -125,4 +125,22 @@ is_deeply(\@names, [ '3' ], 'i3 starts on workspace 3'); exit_gracefully($pid); +############################################################## +# 7: verify optional flags do not affect startup workspace +############################################################## + +$config = <get_bar_config($bar_id)->recv; is($bar_config->{status_command}, 'i3status --foo', 'status_command correct'); ok(!$bar_config->{verbose}, 'verbose off by default'); ok($bar_config->{workspace_buttons}, 'workspace buttons enabled per default'); +is($bar_config->{workspace_min_width}, 0, 'workspace_min_width ok'); ok($bar_config->{binding_mode_indicator}, 'mode indicator enabled per default'); is($bar_config->{mode}, 'dock', 'dock mode by default'); is($bar_config->{position}, 'bottom', 'position bottom by default'); @@ -102,6 +103,7 @@ bar { mode dock font Terminus workspace_buttons no + workspace_min_width 30 binding_mode_indicator no verbose yes socket_path /tmp/foobar @@ -134,6 +136,7 @@ $bar_config = $i3->get_bar_config($bar_id)->recv; is($bar_config->{status_command}, 'i3status --bar', 'status_command correct'); ok($bar_config->{verbose}, 'verbose on'); ok(!$bar_config->{workspace_buttons}, 'workspace buttons disabled'); +is($bar_config->{workspace_min_width}, 30, 'workspace_min_width ok'); ok(!$bar_config->{binding_mode_indicator}, 'mode indicator disabled'); is($bar_config->{mode}, 'dock', 'dock mode'); is($bar_config->{position}, 'top', 'position top'); diff --git a/testcases/t/201-config-parser.t b/testcases/t/201-config-parser.t index 70e96a176..3b9db08b5 100644 --- a/testcases/t/201-config-parser.t +++ b/testcases/t/201-config-parser.t @@ -736,7 +736,7 @@ EOT $expected = <<'EOT'; cfg_bar_start() cfg_bar_output(LVDS-1) -ERROR: CONFIG: Expected one of these tokens: , '#', 'set', 'i3bar_command', 'status_command', 'socket_path', 'mode', 'hidden_state', 'id', 'modifier', 'wheel_up_cmd', 'wheel_down_cmd', 'bindsym', 'position', 'output', 'tray_output', 'tray_padding', 'font', 'separator_symbol', 'binding_mode_indicator', 'workspace_buttons', 'strip_workspace_numbers', 'strip_workspace_name', 'verbose', 'height', 'colors', '}' +ERROR: CONFIG: Expected one of these tokens: , '#', 'set', 'i3bar_command', 'status_command', 'socket_path', 'mode', 'hidden_state', 'id', 'modifier', 'wheel_up_cmd', 'wheel_down_cmd', 'bindsym', 'position', 'output', 'tray_output', 'tray_padding', 'font', 'separator_symbol', 'binding_mode_indicator', 'workspace_buttons', 'workspace_min_width', 'strip_workspace_numbers', 'strip_workspace_name', 'verbose', 'height', 'colors', '}' ERROR: CONFIG: (in file ) ERROR: CONFIG: Line 1: bar { ERROR: CONFIG: Line 2: output LVDS-1 diff --git a/testcases/t/270-config-no-newline-end.t b/testcases/t/270-config-no-newline-end.t index 1faa60512..24c017f26 100644 --- a/testcases/t/270-config-no-newline-end.t +++ b/testcases/t/270-config-no-newline-end.t @@ -19,10 +19,9 @@ use i3test i3_autostart => 0; my $first_lines = <<'EOT'; -set $workspace1 workspace number 1 set $workspace0 workspace eggs -bindsym Mod4+1 $workspace1 +font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1 EOT # Intentionally don't add a trailing newline for the last line since this is