Add setting for minimal width of workspace buttons

This commit is contained in:
Konst Mayer 2019-06-25 01:07:36 +07:00
parent ca82f95812
commit 94228fd902
13 changed files with 101 additions and 43 deletions

View File

@ -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> [px]
------------------------
*Example*:
------------------------
bar {
workspace_min_width 40
}
------------------------
=== Strip workspace numbers/name === Strip workspace numbers/name
Specifies whether workspace numbers should be displayed within the workspace Specifies whether workspace numbers should be displayed within the workspace

View File

@ -52,6 +52,7 @@ typedef struct config_t {
struct xcb_color_strings_t colors; struct xcb_color_strings_t colors;
bool disable_binding_mode_indicator; bool disable_binding_mode_indicator;
bool disable_ws; bool disable_ws;
int ws_min_width;
bool strip_ws_numbers; bool strip_ws_numbers;
bool strip_ws_name; bool strip_ws_name;
char *bar_id; char *bar_id;

View File

@ -18,7 +18,7 @@
/* Name of current binding mode and its render width */ /* Name of current binding mode and its render width */
struct mode { struct mode {
i3String *name; i3String *name;
int width; int name_width;
}; };
typedef struct mode mode; typedef struct mode mode;

View File

@ -345,6 +345,12 @@ static int config_integer_cb(void *params_, long long val) {
return 1; 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; return 0;
} }

View File

@ -81,7 +81,7 @@ static int mode_end_map_cb(void *params_) {
params->mode->name = i3string_from_utf8(params->name); params->mode->name = i3string_from_utf8(params->name);
i3string_set_markup(params->mode->name, params->pango_markup); i3string_set_markup(params->mode->name, params->pango_markup);
/* Save its rendered width */ /* 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)); DLOG("Got mode change: %s\n", i3string_as_utf8(params->mode->name));
FREE(params->cur_key); FREE(params->cur_key);

View File

@ -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). * 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- * 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; i3_ws *cur_ws = NULL, *clicked_ws = NULL, *ws_walk;
TAILQ_FOREACH(ws_walk, walk->workspaces, tailq) { 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) if (x >= workspace_width && x <= workspace_width + w)
clicked_ws = ws_walk; clicked_ws = ws_walk;
if (ws_walk->visible) if (ws_walk->visible)
@ -1909,6 +1918,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 = font.height + 2 * logical_px(ws_voff_px) - 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,
logical_px(ws_voff_px), text_width);
}
/* /*
* Render the bars, with buttons and statusline * Render the bars, with buttons and statusline
* *
@ -1964,26 +1992,11 @@ void draw_bars(bool unhide) {
unhide = true; unhide = true;
} }
/* Draw the border of the button. */ int w = predict_button_width(ws_walk->name_width);
draw_util_rectangle(&(outputs_walk->buffer), border_color, draw_button(&(outputs_walk->buffer), fg_color, bg_color, border_color,
workspace_width, workspace_width, w, ws_walk->name_width, ws_walk->name);
logical_px(1),
ws_walk->name_width + 2 * logical_px(ws_hoff_px) + 2 * logical_px(1),
font.height + 2 * logical_px(ws_voff_px) - 2 * logical_px(1));
/* Draw the inside of the button. */ workspace_width += w;
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),
font.height + 2 * logical_px(ws_voff_px) - 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),
logical_px(ws_voff_px),
ws_walk->name_width);
workspace_width += 2 * logical_px(ws_hoff_px) + 2 * logical_px(1) + ws_walk->name_width;
if (TAILQ_NEXT(ws_walk, tailq) != NULL) if (TAILQ_NEXT(ws_walk, tailq) != NULL)
workspace_width += logical_px(ws_spacing_px); workspace_width += logical_px(ws_spacing_px);
} }
@ -1992,28 +2005,12 @@ void draw_bars(bool unhide) {
if (binding.name && !config.disable_binding_mode_indicator) { if (binding.name && !config.disable_binding_mode_indicator) {
workspace_width += logical_px(ws_spacing_px); workspace_width += logical_px(ws_spacing_px);
color_t fg_color = colors.binding_mode_fg; int w = predict_button_width(binding.name_width);
color_t bg_color = colors.binding_mode_bg; 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);
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),
font.height + 2 * logical_px(ws_voff_px) - 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),
font.height + 2 * logical_px(ws_voff_px) - 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),
logical_px(ws_voff_px),
binding.width);
unhide = true; unhide = true;
workspace_width += 2 * logical_px(ws_hoff_px) + 2 * logical_px(1) + binding.width; workspace_width += w;
} }
if (!TAILQ_EMPTY(&statusline_head)) { if (!TAILQ_EMPTY(&statusline_head)) {

View File

@ -97,6 +97,7 @@ CFGFUN(bar_color_single, const char *colorclass, const char *color);
CFGFUN(bar_status_command, const char *command); CFGFUN(bar_status_command, const char *command);
CFGFUN(bar_binding_mode_indicator, const char *value); CFGFUN(bar_binding_mode_indicator, const char *value);
CFGFUN(bar_workspace_buttons, 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_numbers, const char *value);
CFGFUN(bar_strip_workspace_name, const char *value); CFGFUN(bar_strip_workspace_name, const char *value);
CFGFUN(bar_start); CFGFUN(bar_start);

View File

@ -325,6 +325,9 @@ struct Barconfig {
* zero. */ * zero. */
bool hide_workspace_buttons; bool hide_workspace_buttons;
/** The minimal width for workspace buttons. */
int workspace_min_width;
/** Strip workspace numbers? Configuration option is /** Strip workspace numbers? Configuration option is
* 'strip_workspace_numbers yes'. */ * 'strip_workspace_numbers yes'. */
bool strip_workspace_numbers; bool strip_workspace_numbers;

View File

@ -468,6 +468,7 @@ state BAR:
'separator_symbol' -> BAR_SEPARATOR_SYMBOL 'separator_symbol' -> BAR_SEPARATOR_SYMBOL
'binding_mode_indicator' -> BAR_BINDING_MODE_INDICATOR 'binding_mode_indicator' -> BAR_BINDING_MODE_INDICATOR
'workspace_buttons' -> BAR_WORKSPACE_BUTTONS 'workspace_buttons' -> BAR_WORKSPACE_BUTTONS
'workspace_min_width' -> BAR_WORKSPACE_MIN_WIDTH
'strip_workspace_numbers' -> BAR_STRIP_WORKSPACE_NUMBERS 'strip_workspace_numbers' -> BAR_STRIP_WORKSPACE_NUMBERS
'strip_workspace_name' -> BAR_STRIP_WORKSPACE_NAME 'strip_workspace_name' -> BAR_STRIP_WORKSPACE_NAME
'verbose' -> BAR_VERBOSE 'verbose' -> BAR_VERBOSE
@ -572,6 +573,16 @@ state BAR_WORKSPACE_BUTTONS:
value = word value = word
-> call cfg_bar_workspace_buttons($value); BAR -> 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: state BAR_STRIP_WORKSPACE_NUMBERS:
value = word value = word
-> call cfg_bar_strip_workspace_numbers($value); BAR -> call cfg_bar_strip_workspace_numbers($value); BAR

View File

@ -646,6 +646,10 @@ CFGFUN(bar_workspace_buttons, const char *value) {
current_bar->hide_workspace_buttons = !eval_boolstr(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) { CFGFUN(bar_strip_workspace_numbers, const char *value) {
current_bar->strip_workspace_numbers = eval_boolstr(value); current_bar->strip_workspace_numbers = eval_boolstr(value);
} }

View File

@ -780,6 +780,9 @@ static void dump_bar_config(yajl_gen gen, Barconfig *config) {
ystr("workspace_buttons"); ystr("workspace_buttons");
y(bool, !config->hide_workspace_buttons); y(bool, !config->hide_workspace_buttons);
ystr("workspace_min_width");
y(integer, config->workspace_min_width);
ystr("strip_workspace_numbers"); ystr("strip_workspace_numbers");
y(bool, config->strip_workspace_numbers); y(bool, config->strip_workspace_numbers);

View File

@ -63,6 +63,7 @@ my $bar_config = $i3->get_bar_config($bar_id)->recv;
is($bar_config->{status_command}, 'i3status --foo', 'status_command correct'); is($bar_config->{status_command}, 'i3status --foo', 'status_command correct');
ok(!$bar_config->{verbose}, 'verbose off by default'); ok(!$bar_config->{verbose}, 'verbose off by default');
ok($bar_config->{workspace_buttons}, 'workspace buttons enabled per 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'); ok($bar_config->{binding_mode_indicator}, 'mode indicator enabled per default');
is($bar_config->{mode}, 'dock', 'dock mode by default'); is($bar_config->{mode}, 'dock', 'dock mode by default');
is($bar_config->{position}, 'bottom', 'position bottom by default'); is($bar_config->{position}, 'bottom', 'position bottom by default');
@ -102,6 +103,7 @@ bar {
mode dock mode dock
font Terminus font Terminus
workspace_buttons no workspace_buttons no
workspace_min_width 30
binding_mode_indicator no binding_mode_indicator no
verbose yes verbose yes
socket_path /tmp/foobar 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'); is($bar_config->{status_command}, 'i3status --bar', 'status_command correct');
ok($bar_config->{verbose}, 'verbose on'); ok($bar_config->{verbose}, 'verbose on');
ok(!$bar_config->{workspace_buttons}, 'workspace buttons disabled'); 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'); ok(!$bar_config->{binding_mode_indicator}, 'mode indicator disabled');
is($bar_config->{mode}, 'dock', 'dock mode'); is($bar_config->{mode}, 'dock', 'dock mode');
is($bar_config->{position}, 'top', 'position top'); is($bar_config->{position}, 'top', 'position top');

View File

@ -733,7 +733,7 @@ EOT
$expected = <<'EOT'; $expected = <<'EOT';
cfg_bar_start() cfg_bar_start()
cfg_bar_output(LVDS-1) cfg_bar_output(LVDS-1)
ERROR: CONFIG: Expected one of these tokens: <end>, '#', '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', 'colors', '}' ERROR: CONFIG: Expected one of these tokens: <end>, '#', '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', 'colors', '}'
ERROR: CONFIG: (in file <stdin>) ERROR: CONFIG: (in file <stdin>)
ERROR: CONFIG: Line 1: bar { ERROR: CONFIG: Line 1: bar {
ERROR: CONFIG: Line 2: output LVDS-1 ERROR: CONFIG: Line 2: output LVDS-1