From 738083c4a474e960573a29b62adccdf8d4a11a15 Mon Sep 17 00:00:00 2001 From: Michael Grant Date: Sat, 20 Jun 2026 08:29:47 +0200 Subject: [PATCH] Add scrollbar auto-hide feature. --- cmd-resize-pane.c | 4 +- layout.c | 8 ++-- options-table.c | 17 +++++++ options.c | 7 +++ screen-redraw.c | 34 ++++++++++---- server-client.c | 105 ++++++++++++++++++++++++++++++++++++++++--- tmux.h | 9 ++++ window-copy.c | 17 +++++-- window-visible.c | 2 +- window.c | 110 +++++++++++++++++++++++++++++++++++++++++++++- 10 files changed, 288 insertions(+), 25 deletions(-) diff --git a/cmd-resize-pane.c b/cmd-resize-pane.c index bc91ab2b..9c17f1b8 100644 --- a/cmd-resize-pane.c +++ b/cmd-resize-pane.c @@ -250,10 +250,10 @@ cmd_resize_pane_mouse_resize_move_floating(struct client *c, sb_pos = options_get_number(w->options, "pane-scrollbars-position"); left = wp->xoff - 1; right = wp->xoff + sx; - if (window_pane_show_scrollbar(wp, scrollbars) && + if (window_pane_scrollbar_reserve(wp, scrollbars) && sb_pos == PANE_SCROLLBARS_LEFT) { left -= wp->scrollbar_style.width + wp->scrollbar_style.pad; - } else if (window_pane_show_scrollbar(wp, scrollbars) && + } else if (window_pane_scrollbar_reserve(wp, scrollbars) && sb_pos == PANE_SCROLLBARS_RIGHT) { right += wp->scrollbar_style.width + wp->scrollbar_style.pad; } diff --git a/layout.c b/layout.c index 286de2ba..e9d566de 100644 --- a/layout.c +++ b/layout.c @@ -433,7 +433,7 @@ layout_fix_panes(struct window *w, struct window_pane *skip) sy--; } - if (window_pane_show_scrollbar(wp, scrollbars)) { + if (window_pane_scrollbar_reserve(wp, scrollbars)) { sb_w = wp->scrollbar_style.width; sb_pad = wp->scrollbar_style.pad; if (sb_w < 1) @@ -506,7 +506,8 @@ layout_resize_check(struct window *w, struct layout_cell *lc, /* Space available in this cell only. */ if (type == LAYOUT_LEFTRIGHT) { available = lc->sx; - if (scrollbars) + if (scrollbars && !options_get_number(w->options, + "pane-scrollbars-auto-hide")) minimum = PANE_MINIMUM + sb_style->width + sb_style->pad; else @@ -1210,7 +1211,8 @@ 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) { + if (scrollbars && !options_get_number(wp->window->options, + "pane-scrollbars-auto-hide")) { minimum = PANE_MINIMUM * 2 + sb_style->width + sb_style->pad; } else diff --git a/options-table.c b/options-table.c index ca9c071f..84c3bee9 100644 --- a/options-table.c +++ b/options-table.c @@ -1404,6 +1404,23 @@ const struct options_table_entry options_table[] = { .text = "Pane scrollbar state." }, + { .name = "pane-scrollbars-auto-hide", + .type = OPTIONS_TABLE_FLAG, + .scope = OPTIONS_TABLE_WINDOW, + .default_num = 0, + .text = "Whether pane scrollbars are hidden when not in use." + }, + + { .name = "pane-scrollbars-auto-hide-timeout", + .type = OPTIONS_TABLE_NUMBER, + .scope = OPTIONS_TABLE_WINDOW, + .minimum = 0, + .maximum = INT_MAX, + .default_num = 1000, + .unit = "milliseconds", + .text = "Time before auto-hidden pane scrollbars disappear." + }, + { .name = "pane-scrollbars-style", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, diff --git a/options.c b/options.c index a9ef2f94..fef474c9 100644 --- a/options.c +++ b/options.c @@ -1225,6 +1225,8 @@ options_push_changes(const char *name) strcmp(name, "pane-border-lines") == 0 || strcmp(name, "pane-border-status") == 0 || strcmp(name, "pane-scrollbars") == 0 || + strcmp(name, "pane-scrollbars-auto-hide") == 0 || + strcmp(name, "pane-scrollbars-auto-hide-timeout") == 0 || strcmp(name, "pane-scrollbars-position") == 0 || strcmp(name, "pane-scrollbars-style") == 0) redraw_invalidate_all_scenes(); @@ -1245,10 +1247,15 @@ options_push_changes(const char *name) } if (strcmp(name, "pane-border-status") == 0 || strcmp(name, "pane-scrollbars") == 0 || + strcmp(name, "pane-scrollbars-auto-hide") == 0 || strcmp(name, "pane-scrollbars-position") == 0) { RB_FOREACH(w, windows, &windows) layout_fix_panes(w, NULL); } + if (strcmp(name, "pane-scrollbars-auto-hide") == 0) { + RB_FOREACH(wp, window_pane_tree, &all_window_panes) + window_pane_scrollbar_hide(wp); + } if (strcmp(name, "pane-scrollbars-style") == 0) { RB_FOREACH(wp, window_pane_tree, &all_window_panes) { style_set_scrollbar_style_from_option( diff --git a/screen-redraw.c b/screen-redraw.c index 3dddc659..559a5f41 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -81,6 +81,7 @@ enum redraw_span_type { #define REDRAW_BORDER_IS_ARROW 0x1 #define REDRAW_SCROLLBAR_LEFT 0x2 #define REDRAW_SCROLLBAR_RIGHT 0x4 +#define REDRAW_SCROLLBAR_OVERLAY 0x8 /* Draw operations. */ #define REDRAW_PANE 0x1 @@ -447,7 +448,7 @@ redraw_mark_pane_inside(struct redraw_build_ctx *bctx, struct window_pane *wp) /* Mark scrollbar data. */ static void redraw_mark_pane_scrollbar(struct redraw_build_ctx *bctx, - struct window_pane *wp, int sb_w, int sb_left) + struct window_pane *wp, int sb_w, int sb_left, int overlay) { struct redraw_build_cell *bc; u_int x, y; @@ -457,7 +458,13 @@ redraw_mark_pane_scrollbar(struct redraw_build_ctx *bctx, if (sb_w == 0) return; - if (sb_left) { + if (overlay && sb_left) { + sx = wp->xoff; + ex = sx + sb_w - 1; + } else if (overlay) { + ex = wp->xoff + (int)wp->sx - 1; + sx = ex - sb_w + 1; + } else if (sb_left) { sx = wp->xoff - sb_w; ex = wp->xoff - 1; } else { @@ -481,6 +488,8 @@ redraw_mark_pane_scrollbar(struct redraw_build_ctx *bctx, bc->data.sb.flags |= REDRAW_SCROLLBAR_LEFT; else bc->data.sb.flags |= REDRAW_SCROLLBAR_RIGHT; + if (overlay) + bc->data.sb.flags |= REDRAW_SCROLLBAR_OVERLAY; } } } @@ -755,19 +764,26 @@ redraw_mark_pane_borders(struct redraw_build_ctx *bctx, struct window_pane *wp, static void redraw_mark_pane(struct redraw_build_ctx *bctx, struct window_pane *wp) { - int sb_w = 0, sb_left = 0; + int sb_w = 0, sb_left = 0, overlay = 0; if (!window_pane_is_visible(wp)) return; - if (window_pane_show_scrollbar(wp, bctx->sb)) - sb_w = wp->scrollbar_style.width + wp->scrollbar_style.pad; + if (window_pane_scrollbar_visible(wp, bctx->sb)) { + overlay = window_pane_scrollbar_overlay(wp, bctx->sb); + if (overlay) { + sb_w = wp->scrollbar_style.width; + if (sb_w > (int)wp->sx) + sb_w = wp->sx; + } else + sb_w = wp->scrollbar_style.width + wp->scrollbar_style.pad; + } if (sb_w != 0 && bctx->sbp == PANE_SCROLLBARS_LEFT) sb_left = 1; redraw_mark_pane_inside(bctx, wp); - redraw_mark_pane_borders(bctx, wp, sb_w, sb_left); - redraw_mark_pane_scrollbar(bctx, wp, sb_w, sb_left); + redraw_mark_pane_borders(bctx, wp, overlay ? 0 : sb_w, sb_left); + redraw_mark_pane_scrollbar(bctx, wp, sb_w, sb_left, overlay); } /* Choose the pane that will provide the border style for two-pane layouts. */ @@ -1267,7 +1283,9 @@ redraw_draw_scrollbar_span(struct redraw_draw_ctx *dctx, tty_cursor(tty, x, y); for (i = 0; i < n; i++) { - if (span->data.sb.flags & REDRAW_SCROLLBAR_LEFT) { + if (span->data.sb.flags & REDRAW_SCROLLBAR_OVERLAY) { + /* Padding is transparent and is not part of overlay spans. */ + } else if (span->data.sb.flags & REDRAW_SCROLLBAR_LEFT) { if (off + i >= sb_w && off + i < sb_w + sb_pad) { tty_cell(tty, &grid_default_cell, NULL); continue; diff --git a/server-client.c b/server-client.c index e7d13ec7..43535261 100644 --- a/server-client.c +++ b/server-client.c @@ -48,6 +48,8 @@ static void server_client_dispatch(struct imsg *, void *); static int server_client_dispatch_command(struct client *, struct imsg *); static int server_client_dispatch_identify(struct client *, struct imsg *); static int server_client_dispatch_shell(struct client *); +static void server_client_update_scrollbar_hover(struct client *, int, int, + int); static void server_client_report_theme(struct client *, enum client_theme); /* Compare client windows. */ @@ -599,6 +601,60 @@ server_client_exec(struct client *c, const char *cmd) free(msg); } +/* Is this point inside the auto-hide scrollbar interaction area? */ +static int +server_client_in_scrollbar_area(struct window_pane *wp, int px, int py) +{ + struct window *w = wp->window; + u_int width, pad, total; + int sb, sb_pos, start, end; + + sb = options_get_number(w->options, "pane-scrollbars"); + if (!window_pane_scrollbar_overlay(wp, sb)) + return (0); + if (py < wp->yoff || py >= wp->yoff + (int)wp->sy) + return (0); + + width = wp->scrollbar_style.width; + pad = wp->scrollbar_style.pad; + total = width + pad; + if (total == 0 || total > wp->sx) + total = wp->sx; + + sb_pos = options_get_number(w->options, "pane-scrollbars-position"); + if (sb_pos == PANE_SCROLLBARS_LEFT) { + start = wp->xoff; + end = wp->xoff + (int)total - 1; + } else { + end = wp->xoff + (int)wp->sx - 1; + start = end - (int)total + 1; + } + return (px >= start && px <= end); +} + +/* Update auto-hide scrollbars for a mouse movement. */ +static void +server_client_update_scrollbar_hover(struct client *c, int type, int px, int py) +{ + struct window *w = c->session->curw->window; + struct window_pane *wp; + + if (type != KEYC_TYPE_MOUSEMOVE) + return; + + TAILQ_FOREACH(wp, &w->panes, entry) { + if (!window_pane_is_visible(wp)) + continue; + if (server_client_in_scrollbar_area(wp, px, py)) { + wp->sb_auto_hover = 1; + window_pane_scrollbar_show(wp, 0); + } else { + wp->sb_auto_hover = 0; + window_pane_scrollbar_start_timer(wp); + } + } +} + /* Is the mouse inside a pane? */ static enum key_code_mouse_location server_client_check_mouse_in_pane(struct window_pane *wp, int px, int py, @@ -606,17 +662,21 @@ server_client_check_mouse_in_pane(struct window_pane *wp, int px, int py, { struct window *w = wp->window; struct window_pane *fwp; - int pane_status, sb, sb_pos, sb_w, sb_pad; + int pane_status, sb, sb_pos, sb_w, sb_pad, sb_overlay; int pane_status_line, sl_top, sl_bottom; int bdr_bottom, bdr_top, bdr_left, bdr_right; + int sb_start, sb_end; sb = options_get_number(w->options, "pane-scrollbars"); sb_pos = options_get_number(w->options, "pane-scrollbars-position"); pane_status = window_pane_get_pane_status(wp); + sb_overlay = window_pane_scrollbar_overlay(wp, sb); - if (window_pane_show_scrollbar(wp, sb)) { + if (window_pane_scrollbar_visible(wp, sb)) { sb_w = wp->scrollbar_style.width; sb_pad = wp->scrollbar_style.pad; + if (sb_overlay && sb_w > (int)wp->sx) + sb_w = wp->sx; } else { sb_w = 0; sb_pad = 0; @@ -629,9 +689,34 @@ server_client_check_mouse_in_pane(struct window_pane *wp, int px, int py, else pane_status_line = -1; /* not used */ bdr_left = wp->xoff - 1; - if (sb_pos == PANE_SCROLLBARS_LEFT) + if (!sb_overlay && sb_pos == PANE_SCROLLBARS_LEFT) bdr_left -= sb_pad + sb_w; + if (sb_overlay && sb_w != 0 && + py >= wp->yoff && py < wp->yoff + (int)wp->sy && + px >= wp->xoff && px < wp->xoff + (int)wp->sx) { + if (sb_pos == PANE_SCROLLBARS_LEFT) { + sb_start = wp->xoff; + sb_end = sb_start + sb_w - 1; + } else { + sb_end = wp->xoff + (int)wp->sx - 1; + sb_start = sb_end - sb_w + 1; + } + if (px >= sb_start && px <= sb_end) { + sl_top = wp->yoff + wp->sb_slider_y; + sl_bottom = (wp->yoff + wp->sb_slider_y + + wp->sb_slider_h - 1); + if (py < sl_top) + return (KEYC_MOUSE_LOCATION_SCROLLBAR_UP); + else if (py >= sl_top && py <= sl_bottom) { + *sl_mpos = (py - wp->sb_slider_y - wp->yoff); + return (KEYC_MOUSE_LOCATION_SCROLLBAR_SLIDER); + } else + return (KEYC_MOUSE_LOCATION_SCROLLBAR_DOWN); + } + return (KEYC_MOUSE_LOCATION_PANE); + } + /* Check if point is within the pane or scrollbar. */ if (((pane_status != PANE_STATUS_OFF && py != pane_status_line && py != wp->yoff + (int)wp->sy) || @@ -679,7 +764,7 @@ server_client_check_mouse_in_pane(struct window_pane *wp, int px, int py, if (window_pane_is_floating(fwp) && window_pane_get_pane_lines(fwp) == PANE_LINES_NONE) continue; - if (window_pane_show_scrollbar(fwp, sb)) { + if (window_pane_scrollbar_reserve(fwp, sb)) { sb_w = fwp->scrollbar_style.width; sb_pad = fwp->scrollbar_style.pad; } else { @@ -909,10 +994,14 @@ have_event: tty_window_offset(&c->tty, &m->ox, &m->oy, &sx, &sy); log_debug("mouse window @%u at %u,%u (%ux%u)", w->id, m->ox, m->oy, sx, sy); - if (px > sx || py > sy) + if (px > sx || py > sy) { + server_client_update_scrollbar_hover(c, type, + -1, -1); return (KEYC_UNKNOWN); + } px = px + m->ox; py = py + m->oy; + server_client_update_scrollbar_hover(c, type, px, py); if (type == KEYC_TYPE_MOUSEDRAG && lwp != NULL) { /* Use pane from last mouse event. */ @@ -945,7 +1034,8 @@ have_event: m->wp = wp->id; m->w = wp->window->id; } - } + } else + server_client_update_scrollbar_hover(c, type, -1, -1); /* Reset click type or add a click timer if needed. */ if (type == KEYC_TYPE_MOUSEDOWN || @@ -1878,7 +1968,8 @@ server_client_reset_state(struct client *c) mode |= MODE_MOUSE_ALL; } } - if (options_get_number(oo, "focus-follows-mouse")) + if (options_get_number(oo, "focus-follows-mouse") || + options_get_number(w->options, "pane-scrollbars-auto-hide")) mode |= MODE_MOUSE_ALL; else if (~mode & MODE_MOUSE_ALL) mode |= MODE_MOUSE_BUTTON; diff --git a/tmux.h b/tmux.h index cb429f06..007ec437 100644 --- a/tmux.h +++ b/tmux.h @@ -1282,6 +1282,9 @@ struct window_pane { u_int sb_slider_y; u_int sb_slider_h; + int sb_auto_visible; + int sb_auto_hover; + struct event sb_auto_timer; int argc; char **argv; @@ -3512,6 +3515,12 @@ void window_set_fill_character(struct window *); void window_pane_default_cursor(struct window_pane *); int window_pane_mode(struct window_pane *); int window_pane_show_scrollbar(struct window_pane *, int); +int window_pane_scrollbar_reserve(struct window_pane *, int); +int window_pane_scrollbar_visible(struct window_pane *, int); +int window_pane_scrollbar_overlay(struct window_pane *, int); +void window_pane_scrollbar_show(struct window_pane *, int); +void window_pane_scrollbar_hide(struct window_pane *); +void window_pane_scrollbar_start_timer(struct window_pane *); int window_pane_get_bg(struct window_pane *); int window_pane_get_fg(struct window_pane *); int window_pane_get_fg_control_client(struct window_pane *); diff --git a/window-copy.c b/window-copy.c index 4ba791e7..fd225bc9 100644 --- a/window-copy.c +++ b/window-copy.c @@ -860,6 +860,7 @@ window_copy_scroll1(struct window_mode_entry *wme, struct window_pane *wp, if (data->searchmark != NULL && !data->timeout) window_copy_search_marks(wme, NULL, data->searchregex, 1); window_copy_update_selection(wme, 1, 0); + window_pane_scrollbar_show(wp, 1); window_copy_redraw_screen(wme); } @@ -913,6 +914,7 @@ window_copy_pageup1(struct window_mode_entry *wme, int half_page) if (data->searchmark != NULL && !data->timeout) window_copy_search_marks(wme, NULL, data->searchregex, 1); window_copy_update_selection(wme, 1, 0); + window_pane_scrollbar_show(wme->wp, 1); window_copy_redraw_screen(wme); } @@ -973,6 +975,7 @@ window_copy_pagedown1(struct window_mode_entry *wme, int half_page, if (data->searchmark != NULL && !data->timeout) window_copy_search_marks(wme, NULL, data->searchregex, 1); window_copy_update_selection(wme, 1, 0); + window_pane_scrollbar_show(wme->wp, 1); window_copy_redraw_screen(wme); return (0); } @@ -1789,7 +1792,7 @@ window_copy_cmd_history_bottom(struct window_copy_cmd_state *cs) struct window_mode_entry *wme = cs->wme; struct window_copy_mode_data *data = wme->data; struct screen *s = data->backing; - u_int oy; + u_int oy, old_oy = data->oy; oy = screen_hsize(s) + data->cy - data->oy; if (data->lineflag == LINE_SEL_RIGHT_LEFT && oy == data->endsely) @@ -1802,6 +1805,8 @@ window_copy_cmd_history_bottom(struct window_copy_cmd_state *cs) if (data->searchmark != NULL && !data->timeout) window_copy_search_marks(wme, NULL, data->searchregex, 1); window_copy_update_selection(wme, 1, 0); + if (data->oy != old_oy) + window_pane_scrollbar_show(wme->wp, 1); return (WINDOW_COPY_CMD_REDRAW); } @@ -1810,7 +1815,7 @@ window_copy_cmd_history_top(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; struct window_copy_mode_data *data = wme->data; - u_int oy; + u_int oy, old_oy = data->oy; oy = screen_hsize(data->backing) + data->cy - data->oy; if (data->lineflag == LINE_SEL_LEFT_RIGHT && oy == data->sely) @@ -1823,6 +1828,8 @@ window_copy_cmd_history_top(struct window_copy_cmd_state *cs) if (data->searchmark != NULL && !data->timeout) window_copy_search_marks(wme, NULL, data->searchregex, 1); window_copy_update_selection(wme, 1, 0); + if (data->oy != old_oy) + window_pane_scrollbar_show(wme->wp, 1); return (WINDOW_COPY_CMD_REDRAW); } @@ -3772,7 +3779,7 @@ window_copy_scroll_to(struct window_mode_entry *wme, u_int px, u_int py, { struct window_copy_mode_data *data = wme->data; struct grid *gd = data->backing->grid; - u_int offset, gap; + u_int offset, gap, old_oy = data->oy; data->cx = px; @@ -3796,6 +3803,8 @@ window_copy_scroll_to(struct window_mode_entry *wme, u_int px, u_int py, if (!no_redraw && data->searchmark != NULL && !data->timeout) window_copy_search_marks(wme, NULL, data->searchregex, 1); window_copy_update_selection(wme, 1, 0); + if (data->oy != old_oy) + window_pane_scrollbar_show(wme->wp, 1); if (!no_redraw) window_copy_redraw_screen(wme); } @@ -6539,6 +6548,7 @@ window_copy_scroll_up(struct window_mode_entry *wme, u_int ny) if (ny == 0) return; data->oy -= ny; + window_pane_scrollbar_show(wp, 1); if (data->searchmark != NULL && !data->timeout) window_copy_search_marks(wme, NULL, data->searchregex, 1); @@ -6604,6 +6614,7 @@ window_copy_scroll_down(struct window_mode_entry *wme, u_int ny) if (ny == 0) return; data->oy += ny; + window_pane_scrollbar_show(wp, 1); if (data->searchmark != NULL && !data->timeout) window_copy_search_marks(wme, NULL, data->searchregex, 1); diff --git a/window-visible.c b/window-visible.c index 7feb496d..f0f7acdf 100644 --- a/window-visible.c +++ b/window-visible.c @@ -126,7 +126,7 @@ window_visible_ranges(struct window_pane *base_wp, int px, int py, u_int width, continue; sb_w = wp->scrollbar_style.width + wp->scrollbar_style.pad; - if (!window_pane_show_scrollbar(wp, sb)) + if (!window_pane_scrollbar_reserve(wp, sb)) sb_w = sb_pos = 0; for (i = 0; i < r->used; i++) { diff --git a/window.c b/window.c index 29dafb14..0547fb71 100644 --- a/window.c +++ b/window.c @@ -71,6 +71,7 @@ struct window_pane_input_data { static struct window_pane *window_pane_create(struct window *, u_int, u_int, u_int); static void window_pane_destroy(struct window_pane *); +static void window_pane_scrollbar_timer(int, short, void *); static void window_pane_full_size_offset(struct window_pane *wp, int *xoff, int *yoff, u_int *sx, u_int *sy); @@ -1101,6 +1102,7 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) screen_init(&wp->status_screen, 1, 1, 0); style_ranges_init(&wp->border_status_line.ranges); + evtimer_set(&wp->sb_auto_timer, window_pane_scrollbar_timer, wp); if (gethostname(host, sizeof host) == 0) screen_set_title(&wp->base, host, 0); @@ -1132,6 +1134,31 @@ window_pane_wait_finish(struct window_pane *wp) cmdq_continue(item); } +static void +window_pane_scrollbar_timer(__unused int fd, __unused short events, void *arg) +{ + struct window_pane *wp = arg; + + if (wp->sb_auto_hover) + return; + window_pane_scrollbar_hide(wp); +} + +static int +window_pane_scrollbar_auto_hide(struct window_pane *wp) +{ + return (options_get_number(wp->window->options, + "pane-scrollbars-auto-hide")); +} + +static void +window_pane_scrollbar_redraw(struct window_pane *wp) +{ + redraw_invalidate_scene(wp->window); + wp->flags |= PANE_REDRAW; + server_redraw_window(wp->window); +} + static void window_pane_destroy(struct window_pane *wp) { @@ -1164,6 +1191,8 @@ window_pane_destroy(struct window_pane *wp) event_del(&wp->resize_timer); if (event_initialized(&wp->sync_timer)) event_del(&wp->sync_timer); + if (event_initialized(&wp->sb_auto_timer)) + event_del(&wp->sb_auto_timer); window_pane_clear_resizes(wp, NULL); RB_REMOVE(window_pane_tree, &all_window_panes, wp); @@ -1540,7 +1569,7 @@ window_pane_full_size_offset(struct window_pane *wp, int *xoff, int *yoff, pane_scrollbars = options_get_number(w->options, "pane-scrollbars"); sb_pos = options_get_number(w->options, "pane-scrollbars-position"); - if (window_pane_show_scrollbar(wp, pane_scrollbars)) + if (window_pane_scrollbar_reserve(wp, pane_scrollbars)) sb_w = wp->scrollbar_style.width + wp->scrollbar_style.pad; else sb_w = 0; @@ -1973,6 +2002,85 @@ window_pane_show_scrollbar(struct window_pane *wp, int sb_option) return (0); } +int +window_pane_scrollbar_reserve(struct window_pane *wp, int sb_option) +{ + if (!window_pane_show_scrollbar(wp, sb_option)) + return (0); + return (!window_pane_scrollbar_auto_hide(wp)); +} + +int +window_pane_scrollbar_overlay(struct window_pane *wp, int sb_option) +{ + if (!window_pane_show_scrollbar(wp, sb_option)) + return (0); + return (window_pane_scrollbar_auto_hide(wp)); +} + +int +window_pane_scrollbar_visible(struct window_pane *wp, int sb_option) +{ + if (!window_pane_show_scrollbar(wp, sb_option)) + return (0); + if (!window_pane_scrollbar_auto_hide(wp)) + return (1); + return (wp->sb_auto_visible); +} + +void +window_pane_scrollbar_start_timer(struct window_pane *wp) +{ + struct timeval tv; + u_int delay; + + if (!window_pane_scrollbar_auto_hide(wp) || !wp->sb_auto_visible) + return; + if (wp->sb_auto_hover) + return; + + delay = options_get_number(wp->window->options, + "pane-scrollbars-auto-hide-timeout"); + tv.tv_sec = delay / 1000; + tv.tv_usec = (delay % 1000) * 1000L; + evtimer_del(&wp->sb_auto_timer); + evtimer_add(&wp->sb_auto_timer, &tv); +} + +void +window_pane_scrollbar_show(struct window_pane *wp, int start_timer) +{ + int changed = 0; + int sb; + + if (!window_pane_scrollbar_auto_hide(wp)) + return; + sb = options_get_number(wp->window->options, "pane-scrollbars"); + if (!window_pane_show_scrollbar(wp, sb)) + return; + if (!wp->sb_auto_visible) { + wp->sb_auto_visible = 1; + changed = 1; + } + evtimer_del(&wp->sb_auto_timer); + if (start_timer) + window_pane_scrollbar_start_timer(wp); + if (changed) + window_pane_scrollbar_redraw(wp); +} + +void +window_pane_scrollbar_hide(struct window_pane *wp) +{ + if (event_initialized(&wp->sb_auto_timer)) + evtimer_del(&wp->sb_auto_timer); + wp->sb_auto_hover = 0; + if (!wp->sb_auto_visible) + return; + wp->sb_auto_visible = 0; + window_pane_scrollbar_redraw(wp); +} + int window_pane_get_bg(struct window_pane *wp) {