From 350a151ee4158ba56f03cb23ac95f2aa44a88216 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 15 Nov 2024 13:12:20 +0000 Subject: [PATCH] Add two new style parameters, width and pad, which apply to scrollbars. From Michael Grant, GitHub issue 4241. --- layout.c | 48 ++++++++++++++++++++++++++----------- options-table.c | 2 +- options.c | 9 +++++++ screen-redraw.c | 63 +++++++++++++++++++++++++++---------------------- server-client.c | 26 ++++++++++++-------- style.c | 44 +++++++++++++++++++++++++++++++++- tmux.1 | 7 +++++- tmux.h | 18 +++++++++++--- window.c | 10 +++++--- 9 files changed, 166 insertions(+), 61 deletions(-) diff --git a/layout.c b/layout.c index cbad5281..7a63d36f 100644 --- a/layout.c +++ b/layout.c @@ -291,7 +291,7 @@ layout_fix_panes(struct window *w, struct window_pane *skip) { struct window_pane *wp; struct layout_cell *lc; - int status, scrollbars, sb_pos; + int status, scrollbars, sb_pos, sb_w, sb_pad; u_int sx, sy; status = options_get_number(w->options, "pane-border-status"); @@ -314,11 +314,26 @@ layout_fix_panes(struct window *w, struct window_pane *skip) } if (window_pane_show_scrollbar(wp, scrollbars)) { + sb_w = wp->scrollbar_style.width; + sb_pad = wp->scrollbar_style.pad; + if (sb_w < 1) + sb_w = 1; + if (sb_pad < 0) + sb_pad = 0; if (sb_pos == PANE_SCROLLBARS_LEFT) { - sx = sx - PANE_SCROLLBARS_WIDTH; - wp->xoff = wp->xoff + PANE_SCROLLBARS_WIDTH; + if ((int)sx - sb_w < PANE_MINIMUM) { + wp->xoff = wp->xoff + + (int)sx - PANE_MINIMUM; + sx = PANE_MINIMUM; + } else { + sx = sx - sb_w - sb_pad; + wp->xoff = wp->xoff + sb_w + sb_pad; + } } else /* sb_pos == PANE_SCROLLBARS_RIGHT */ - sx = sx - PANE_SCROLLBARS_WIDTH; + if ((int)sx - sb_w - sb_pad < PANE_MINIMUM) + sx = PANE_MINIMUM; + else + sx = sx - sb_w - sb_pad; wp->flags |= PANE_REDRAWSCROLLBAR; } @@ -353,6 +368,7 @@ layout_resize_check(struct window *w, struct layout_cell *lc, enum layout_type type) { struct layout_cell *lcchild; + struct style *sb_style = &w->active->scrollbar_style; u_int available, minimum; int status, scrollbars; @@ -364,7 +380,8 @@ layout_resize_check(struct window *w, struct layout_cell *lc, if (type == LAYOUT_LEFTRIGHT) { available = lc->sx; if (scrollbars) - minimum = PANE_MINIMUM + PANE_SCROLLBARS_WIDTH; + minimum = PANE_MINIMUM + sb_style->width + + sb_style->pad; else minimum = PANE_MINIMUM; } else { @@ -891,11 +908,12 @@ struct layout_cell * layout_split_pane(struct window_pane *wp, enum layout_type type, int size, int flags) { - struct layout_cell *lc, *lcparent, *lcnew, *lc1, *lc2; - u_int sx, sy, xoff, yoff, size1, size2, minimum; - u_int new_size, saved_size, resize_first = 0; - int full_size = (flags & SPAWN_FULLSIZE), status; - int scrollbars; + struct layout_cell *lc, *lcparent, *lcnew, *lc1, *lc2; + struct style *sb_style = &wp->scrollbar_style; + u_int sx, sy, xoff, yoff, size1, size2, minimum; + u_int new_size, saved_size, resize_first = 0; + int full_size = (flags & SPAWN_FULLSIZE), status; + int scrollbars; /* * If full_size is specified, add a new cell at the top of the window @@ -917,9 +935,10 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size, /* Check there is enough space for the two new panes. */ switch (type) { case LAYOUT_LEFTRIGHT: - if (scrollbars) - minimum = PANE_MINIMUM * 2 + PANE_SCROLLBARS_WIDTH; - else + if (scrollbars) { + minimum = PANE_MINIMUM * 2 + sb_style->width + + sb_style->pad; + } else minimum = PANE_MINIMUM * 2 + 1; if (sx < minimum) return (NULL); @@ -1081,6 +1100,7 @@ int layout_spread_cell(struct window *w, struct layout_cell *parent) { struct layout_cell *lc; + struct style *sb_style = &w->active->scrollbar_style; u_int number, each, size, this; int change, changed, status, scrollbars; @@ -1094,7 +1114,7 @@ layout_spread_cell(struct window *w, struct layout_cell *parent) if (parent->type == LAYOUT_LEFTRIGHT) { if (scrollbars) - size = parent->sx - PANE_SCROLLBARS_WIDTH; + size = parent->sx - sb_style->width + sb_style->pad; else size = parent->sx; } diff --git a/options-table.c b/options-table.c index b327c1b4..0a75700d 100644 --- a/options-table.c +++ b/options-table.c @@ -1187,7 +1187,7 @@ const struct options_table_entry options_table[] = { { .name = "pane-scrollbars-style", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, - .default_str = "bg=black,fg=white", + .default_str = "bg=black,fg=white,width=1,pad=0", .flags = OPTIONS_TABLE_IS_STYLE, .separator = ",", .text = "Style of the pane scrollbar." diff --git a/options.c b/options.c index 74d34d88..528de504 100644 --- a/options.c +++ b/options.c @@ -1177,6 +1177,15 @@ options_push_changes(const char *name) RB_FOREACH(w, windows, &windows) layout_fix_panes(w, NULL); } + if (strcmp(name, "pane-scrollbars-style") == 0) { + RB_FOREACH(wp, window_pane_tree, &all_window_panes) { + style_set_scrollbar_style_from_option( + &wp->scrollbar_style, wp->options); + } + RB_FOREACH(w, windows, &windows) + layout_fix_panes(w, NULL); + } + if (strcmp(name, "input-buffer-size") == 0) input_set_buffer_size(options_get_number(global_options, name)); RB_FOREACH(s, sessions, &sessions) diff --git a/screen-redraw.c b/screen-redraw.c index 719eda6d..c1e1481c 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -138,7 +138,7 @@ screen_redraw_pane_border(struct screen_redraw_ctx *ctx, struct window_pane *wp, /* Are scrollbars enabled? */ if (window_pane_show_scrollbar(wp, pane_scrollbars)) - sb_w = PANE_SCROLLBARS_WIDTH; + sb_w = wp->scrollbar_style.width + wp->scrollbar_style.pad; /* * Left/right borders. The wp->sy / 2 test is to colour only half the @@ -171,7 +171,7 @@ screen_redraw_pane_border(struct screen_redraw_ctx *ctx, struct window_pane *wp, } else { if (sb_pos == PANE_SCROLLBARS_LEFT) { if ((wp->xoff - sb_w == 0 || px >= wp->xoff - sb_w) && - (px <= ex || (sb_w != 0 && px - 1 == ex))) { + (px <= ex || (sb_w != 0 && px < ex + sb_w))) { if (wp->yoff != 0 && py == wp->yoff - 1) return (SCREEN_REDRAW_BORDER_TOP); if (py == ey) @@ -179,7 +179,7 @@ screen_redraw_pane_border(struct screen_redraw_ctx *ctx, struct window_pane *wp, } } else { /* sb_pos == PANE_SCROLLBARS_RIGHT */ if ((wp->xoff == 0 || px >= wp->xoff) && - (px <= ex || (sb_w != 0 && px - 1 == ex))) { + (px <= ex || (sb_w != 0 && px < ex + sb_w))) { if (wp->yoff != 0 && py == wp->yoff - 1) return (SCREEN_REDRAW_BORDER_TOP); if (py == ey) @@ -324,7 +324,7 @@ screen_redraw_check_cell(struct screen_redraw_ctx *ctx, u_int px, u_int py, int border, pane_scrollbars = ctx->pane_scrollbars; u_int right, line; int sb_pos = ctx->pane_scrollbars_pos; - int sb_w = PANE_SCROLLBARS_WIDTH; + int sb_w; *wpp = NULL; @@ -374,6 +374,8 @@ screen_redraw_check_cell(struct screen_redraw_ctx *ctx, u_int px, u_int py, * pane is at the top then py == 0 to sy; if the pane * is not at the top, then yoff to yoff + sy. */ + sb_w = wp->scrollbar_style.width + + wp->scrollbar_style.pad; if ((pane_status && py != line) || (wp->yoff == 0 && py < wp->sy) || (py >= wp->yoff && py < wp->yoff + wp->sy)) { @@ -944,7 +946,9 @@ screen_redraw_draw_pane_scrollbar(struct screen_redraw_ctx *ctx, double percent_view; u_int sb = ctx->pane_scrollbars, total_height, sb_h = wp->sy; u_int sb_pos = ctx->pane_scrollbars_pos, slider_h, slider_y; - u_int sb_w = PANE_SCROLLBARS_WIDTH, cm_y, cm_size; + int sb_w = wp->scrollbar_style.width; + int sb_pad = wp->scrollbar_style.pad; + int cm_y, cm_size, xoff = wp->xoff, ox = ctx->ox; int sb_x, sb_y = (int)(wp->yoff - ctx->oy); /* sb top */ if (window_pane_mode(wp) == WINDOW_PANE_NO_MODE) { @@ -967,9 +971,9 @@ screen_redraw_draw_pane_scrollbar(struct screen_redraw_ctx *ctx, } if (sb_pos == PANE_SCROLLBARS_LEFT) - sb_x = (int)wp->xoff - sb_w - ctx->ox; + sb_x = xoff - sb_w - sb_pad - ox; else - sb_x = (int)wp->xoff + wp->sx - ctx->ox; + sb_x = xoff + wp->sx - ox; if (slider_h < 1) slider_h = 1; @@ -990,39 +994,42 @@ screen_redraw_draw_scrollbar(struct screen_redraw_ctx *ctx, u_int slider_h, u_int slider_y) { struct client *c = ctx->c; - struct window *w = wp->window; struct tty *tty = &c->tty; struct grid_cell gc, slgc, *gcp; - u_int i, j, sb_w = PANE_SCROLLBARS_WIDTH; - u_int pad_col = 0; + struct style *sb_style = &wp->scrollbar_style; + u_int i, j, imax, jmax; + u_int sb_w = sb_style->width, sb_pad = sb_style->pad; int px, py, ox = ctx->ox, oy = ctx->oy; - int sb_pad = PANE_SCROLLBARS_PADDING, sx = ctx->sx; - int sy = ctx->sy, xoff = wp->xoff, yoff = wp->yoff; - - /* Set up default style. */ - style_apply(&gc, w->options, "pane-scrollbars-style", NULL); - utf8_set(&gc.data, ' '); + int sx = ctx->sx, sy = ctx->sy, xoff = wp->xoff; + int yoff = wp->yoff; /* Set up style for slider. */ + gc = sb_style->gc; memcpy(&slgc, &gc, sizeof slgc); + slgc.fg = gc.bg; slgc.bg = gc.fg; - if (sb_pad != 0) { - if (sb_pos == PANE_SCROLLBARS_RIGHT) - pad_col = 0; - else - pad_col = sb_w - 1; - } + imax = sb_w + sb_pad; + if ((int)imax + sb_x > sx) + imax = sx - sb_x; + jmax = sb_h; + if ((int)jmax + sb_y > sy) + jmax = sy - sb_y; - for (i = 0; i < sb_w; i++) { - for (j = 0; j < sb_h; j++) { + for (j = 0; j < jmax; j++) { + py = sb_y + j; + for (i = 0; i < imax; i++) { px = sb_x + i; - py = sb_y + j; - if (px < xoff - ox - 1 || px >= sx || px < 0 || - py < yoff - oy - 1 || py >= sy || py < 0) + if (px < xoff - ox - (int)sb_w - (int)sb_pad || + px >= sx || px < 0 || + py < yoff - oy - 1 || + py >= sy || py < 0) continue; tty_cursor(tty, px, py); - if (sb_pad && i == pad_col) { + if ((sb_pos == PANE_SCROLLBARS_LEFT && + i >= sb_w && i < sb_w + sb_pad) || + (sb_pos == PANE_SCROLLBARS_RIGHT && + i < sb_pad)) { tty_cell(tty, &grid_default_cell, &grid_default_cell, NULL, NULL); } else { diff --git a/server-client.c b/server-client.c index d50af6c4..570c1ed9 100644 --- a/server-client.c +++ b/server-client.c @@ -581,7 +581,7 @@ server_client_check_mouse(struct client *c, struct key_event *event) struct window_pane *wp, *fwp; u_int x, y, b, sx, sy, px, py, line = 0, sb_pos; u_int sl_top, sl_bottom, sl_mpos = 0; - int ignore = 0, sb, sb_w, pane_status; + int ignore = 0, sb, sb_w, sb_pad, pane_status; key_code key; struct timeval tv; struct style_range *sr; @@ -783,10 +783,15 @@ have_event: /* Try the scrollbar next to a pane. */ sb = options_get_number(wo, "pane-scrollbars"); - if (window_pane_show_scrollbar(wp, sb)) - sb_w = PANE_SCROLLBARS_WIDTH; - else + sb_pos = options_get_number(wo, + "pane-scrollbars-position"); + if (window_pane_show_scrollbar(wp, sb)) { + sb_w = wp->scrollbar_style.width; + sb_pad = wp->scrollbar_style.pad; + } else { sb_w = 0; + sb_pad = 0; + } pane_status = options_get_number(wo, "pane-border-status"); if (pane_status == PANE_STATUS_TOP) @@ -795,8 +800,9 @@ have_event: line = wp->yoff + wp->sy; /* - * Check if py could lie within a scrollbar. If the - * pane is at the top, then py is 0; if not then the + * Check if py could lie within a scrollbar + * (but not within the padding). If the pane is + * at the top, then py is 0; if not then the * top, then yoff to yoff + sy. */ if ((pane_status != PANE_STATUS_OFF && py != line) || @@ -805,11 +811,11 @@ have_event: sb_pos = options_get_number(wo, "pane-scrollbars-position"); if ((sb_pos == PANE_SCROLLBARS_RIGHT && - (px >= wp->xoff + wp->sx && - px < wp->xoff + wp->sx + sb_w)) || + (px >= wp->xoff + wp->sx + sb_pad && + px < wp->xoff + wp->sx + sb_pad + sb_w)) || (sb_pos == PANE_SCROLLBARS_LEFT && - (px >= wp->xoff - sb_w && - px < wp->xoff))) { + (px >= wp->xoff - sb_pad - sb_w && + px < wp->xoff - sb_pad))) { sl_top = wp->yoff + wp->sb_slider_y; sl_bottom = (wp->yoff + wp->sb_slider_y + diff --git a/style.c b/style.c index 9d7c585d..c9e5b15e 100644 --- a/style.c +++ b/style.c @@ -39,6 +39,8 @@ static struct style style_default = { STYLE_RANGE_NONE, 0, "", + STYLE_WIDTH_DEFAULT, STYLE_PAD_DEFAULT, + STYLE_DEFAULT_BASE }; @@ -216,6 +218,16 @@ style_parse(struct style *sy, const struct grid_cell *base, const char *in) if ((value = attributes_fromstring(tmp + 2)) == -1) goto error; sy->gc.attr &= ~value; + } else if (end > 6 && strncasecmp(tmp, "width=", 6) == 0) { + n = strtonum(tmp + 6, 0, UINT_MAX, &errstr); + if (errstr != NULL) + goto error; + sy->width = (int)n; + } else if (end > 4 && strncasecmp(tmp, "pad=", 4) == 0) { + n = strtonum(tmp + 4, 0, UINT_MAX, &errstr); + if (errstr != NULL) + goto error; + sy->pad = (int)n; } else { if ((value = attributes_fromstring(tmp)) == -1) goto error; @@ -326,7 +338,16 @@ style_tostring(struct style *sy) attributes_tostring(gc->attr)); comma = ","; } - + if (sy->width >= 0) { + xsnprintf(s + off, sizeof s - off, "%swidth=%u", comma, + sy->width); + comma = ","; + } + if (sy->pad >= 0) { + xsnprintf(s + off, sizeof s - off, "%spad=%u", comma, + sy->pad); + comma = ","; + } if (*s == '\0') return ("default"); return (s); @@ -381,3 +402,24 @@ style_copy(struct style *dst, struct style *src) { memcpy(dst, src, sizeof *dst); } + +void +style_set_scrollbar_style_from_option(struct style *sb_style, struct options *oo) +{ + struct style *sy; + + sy = options_string_to_style(oo, "pane-scrollbars-style", NULL); + if (sy == NULL) { + style_set(sb_style, &grid_default_cell); + sb_style->width = PANE_SCROLLBARS_DEFAULT_WIDTH; + sb_style->pad = PANE_SCROLLBARS_DEFAULT_PADDING; + utf8_set(&sb_style->gc.data, PANE_SCROLLBARS_CHARACTER); + } else { + style_copy(sb_style, sy); + if (sb_style->width < 1) + sb_style->width = PANE_SCROLLBARS_DEFAULT_WIDTH; + if (sb_style->pad < 0) + sb_style->pad = PANE_SCROLLBARS_DEFAULT_PADDING; + utf8_set(&sb_style->gc.data, PANE_SCROLLBARS_CHARACTER); + } +} diff --git a/tmux.1 b/tmux.1 index fd544fc3..41c6e6d3 100644 --- a/tmux.1 +++ b/tmux.1 @@ -5071,7 +5071,12 @@ see the section. The foreground colour is used for the slider, the background for the rest of the scrollbar. -Attributes are ignored. +The +.Ar width +attribute sets the width of the scrollbar and the +.Ar pad +attribute the padding between the scrollbar and the pane. +Other attributes are ignored. .Pp .It Xo Ic pane-scrollbars-position .Op Ic left | right diff --git a/tmux.h b/tmux.h index fc4520af..9ef733c3 100644 --- a/tmux.h +++ b/tmux.h @@ -863,6 +863,10 @@ struct style_range { }; TAILQ_HEAD(style_ranges, style_range); +/* Default style width and pad. */ +#define STYLE_WIDTH_DEFAULT -1 +#define STYLE_PAD_DEFAULT -1 + /* Style default. */ enum style_default_type { STYLE_DEFAULT_BASE, @@ -883,6 +887,9 @@ struct style { u_int range_argument; char range_string[16]; + int width; + int pad; + enum style_default_type default_type; }; @@ -1164,6 +1171,8 @@ struct window_pane { int control_bg; int control_fg; + struct style scrollbar_style; + TAILQ_ENTRY(window_pane) entry; /* link in list of all panes */ TAILQ_ENTRY(window_pane) sentry; /* link in list of last visited */ RB_ENTRY(window_pane) tree_entry; @@ -1268,9 +1277,10 @@ TAILQ_HEAD(winlink_stack, winlink); #define PANE_SCROLLBARS_RIGHT 0 #define PANE_SCROLLBARS_LEFT 1 -/* Pane scrollbars width and padding. */ -#define PANE_SCROLLBARS_WIDTH 1 -#define PANE_SCROLLBARS_PADDING 0 +/* Pane scrollbars width, padding and fill character. */ +#define PANE_SCROLLBARS_DEFAULT_PADDING 0 +#define PANE_SCROLLBARS_DEFAULT_WIDTH 1 +#define PANE_SCROLLBARS_CHARACTER ' ' /* True if screen in alternate screen. */ #define SCREEN_IS_ALTERNATE(s) ((s)->saved_grid != NULL) @@ -3469,6 +3479,8 @@ void style_apply(struct grid_cell *, struct options *, const char *, struct format_tree *); void style_set(struct style *, const struct grid_cell *); void style_copy(struct style *, struct style *); +void style_set_scrollbar_style_from_option(struct style *, + struct options *); /* spawn.c */ struct winlink *spawn_window(struct spawn_context *, char **); diff --git a/window.c b/window.c index d5cb0518..67e96fcf 100644 --- a/window.c +++ b/window.c @@ -596,9 +596,10 @@ window_get_active_at(struct window *w, u_int x, u_int y) if (pane_scrollbars == PANE_SCROLLBARS_ALWAYS || (pane_scrollbars == PANE_SCROLLBARS_MODAL && - window_pane_mode(wp) != WINDOW_PANE_NO_MODE)) - sb_w = PANE_SCROLLBARS_WIDTH; - else + window_pane_mode(wp) != WINDOW_PANE_NO_MODE)) { + sb_w = wp->scrollbar_style.width + + wp->scrollbar_style.pad; + } else sb_w = 0; if (sb_pos == PANE_SCROLLBARS_LEFT) { @@ -961,6 +962,9 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) wp->control_bg = -1; wp->control_fg = -1; + style_set_scrollbar_style_from_option(&wp->scrollbar_style, + wp->options); + colour_palette_init(&wp->palette); colour_palette_from_option(&wp->palette, wp->options);