Add support for a scrollbar at the side of each pane. New options

pane-scrollbars turn them on or off, pane-scrollbars-position sets the
position (left or right), and pane-scrollbars-style to set the colours.
Mouse support will come later. From Michael Grant in GitHub issue 4221.
This commit is contained in:
nicm 2024-11-05 09:41:17 +00:00
parent a0c79aa87b
commit 09f4e43189
10 changed files with 481 additions and 108 deletions

View File

@ -275,7 +275,8 @@ layout_cell_is_bottom(struct window *w, struct layout_cell *lc)
* the case for the most upper or lower panes only.
*/
static int
layout_add_border(struct window *w, struct layout_cell *lc, int status)
layout_add_horizontal_border(struct window *w, struct layout_cell *lc,
int status)
{
if (status == PANE_STATUS_TOP)
return (layout_cell_is_top(w, lc));
@ -290,22 +291,41 @@ layout_fix_panes(struct window *w, struct window_pane *skip)
{
struct window_pane *wp;
struct layout_cell *lc;
int status;
int status, scrollbars, sb_pos;
u_int sx, sy, mode;
status = options_get_number(w->options, "pane-border-status");
scrollbars = options_get_number(w->options, "pane-scrollbars");
sb_pos = options_get_number(w->options, "pane-scrollbars-position");
TAILQ_FOREACH(wp, &w->panes, entry) {
if ((lc = wp->layout_cell) == NULL || wp == skip)
continue;
wp->xoff = lc->xoff;
wp->yoff = lc->yoff;
sx = lc->sx;
sy = lc->sy;
if (layout_add_border(w, lc, status)) {
if (layout_add_horizontal_border(w, lc, status)) {
if (status == PANE_STATUS_TOP)
wp->yoff++;
window_pane_resize(wp, lc->sx, lc->sy - 1);
} else
window_pane_resize(wp, lc->sx, lc->sy);
sy--;
}
mode = window_pane_mode(wp);
if (scrollbars == PANE_SCROLLBARS_ALWAYS ||
(scrollbars == PANE_SCROLLBARS_MODAL &&
mode != WINDOW_PANE_NO_MODE)) {
if (sb_pos == PANE_SCROLLBARS_LEFT) {
sx = sx - PANE_SCROLLBARS_WIDTH;
wp->xoff = wp->xoff + PANE_SCROLLBARS_WIDTH;
} else /* sb_pos == PANE_SCROLLBARS_RIGHT */
sx = sx - PANE_SCROLLBARS_WIDTH;
wp->flags |= PANE_REDRAWSCROLLBAR;
}
window_pane_resize(wp, sx, sy);
}
}
@ -337,17 +357,22 @@ layout_resize_check(struct window *w, struct layout_cell *lc,
{
struct layout_cell *lcchild;
u_int available, minimum;
int status;
int status, scrollbars;
status = options_get_number(w->options, "pane-border-status");
scrollbars = options_get_number(w->options, "pane-scrollbars");
if (lc->type == LAYOUT_WINDOWPANE) {
/* Space available in this cell only. */
if (type == LAYOUT_LEFTRIGHT) {
available = lc->sx;
minimum = PANE_MINIMUM;
if (scrollbars)
minimum = PANE_MINIMUM + PANE_SCROLLBARS_WIDTH;
else
minimum = PANE_MINIMUM;
} else {
available = lc->sy;
if (layout_add_border(w, lc, status))
if (layout_add_horizontal_border(w, lc, status))
minimum = PANE_MINIMUM + 1;
else
minimum = PANE_MINIMUM;
@ -873,6 +898,7 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
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
@ -883,6 +909,7 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
else
lc = wp->layout_cell;
status = options_get_number(wp->window->options, "pane-border-status");
scrollbars = options_get_number(wp->window->options, "pane-scrollbars");
/* Copy the old cell size. */
sx = lc->sx;
@ -893,11 +920,15 @@ 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 (sx < PANE_MINIMUM * 2 + 1)
if (scrollbars)
minimum = PANE_MINIMUM * 2 + PANE_SCROLLBARS_WIDTH;
else
minimum = PANE_MINIMUM * 2 + 1;
if (sx < minimum)
return (NULL);
break;
case LAYOUT_TOPBOTTOM:
if (layout_add_border(wp->window, lc, status))
if (layout_add_horizontal_border(wp->window, lc, status))
minimum = PANE_MINIMUM * 2 + 2;
else
minimum = PANE_MINIMUM * 2 + 1;
@ -1054,7 +1085,7 @@ layout_spread_cell(struct window *w, struct layout_cell *parent)
{
struct layout_cell *lc;
u_int number, each, size, this;
int change, changed, status;
int change, changed, status, scrollbars;
number = 0;
TAILQ_FOREACH (lc, &parent->cells, entry)
@ -1062,11 +1093,16 @@ layout_spread_cell(struct window *w, struct layout_cell *parent)
if (number <= 1)
return (0);
status = options_get_number(w->options, "pane-border-status");
scrollbars = options_get_number(w->options, "pane-scrollbars");
if (parent->type == LAYOUT_LEFTRIGHT)
size = parent->sx;
if (parent->type == LAYOUT_LEFTRIGHT) {
if (scrollbars)
size = parent->sx - PANE_SCROLLBARS_WIDTH;
else
size = parent->sx;
}
else if (parent->type == LAYOUT_TOPBOTTOM) {
if (layout_add_border(w, parent, status))
if (layout_add_horizontal_border(w, parent, status))
size = parent->sy - 1;
else
size = parent->sy;
@ -1087,7 +1123,7 @@ layout_spread_cell(struct window *w, struct layout_cell *parent)
change = each - (int)lc->sx;
layout_resize_adjust(w, lc, LAYOUT_LEFTRIGHT, change);
} else if (parent->type == LAYOUT_TOPBOTTOM) {
if (layout_add_border(w, lc, status))
if (layout_add_horizontal_border(w, lc, status))
this = each + 1;
else
this = each;

View File

@ -64,6 +64,12 @@ static const char *options_table_cursor_style_list[] = {
"default", "blinking-block", "block", "blinking-underline", "underline",
"blinking-bar", "bar", NULL
};
static const char *options_table_pane_scrollbars_list[] = {
"off", "modal", "on", NULL
};
static const char *options_table_pane_scrollbars_position_list[] = {
"right", "left", NULL
};
static const char *options_table_pane_status_list[] = {
"off", "top", "bottom", NULL
};
@ -1161,7 +1167,32 @@ const struct options_table_entry options_table[] = {
.text = "The default colour palette for colours zero to 255."
},
{ .name = "popup-style",
{ .name = "pane-scrollbars",
.type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_WINDOW,
.choices = options_table_pane_scrollbars_list,
.default_num = PANE_SCROLLBARS_OFF,
.text = "Pane scrollbar state."
},
{ .name = "pane-scrollbars-style",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
.default_str = "bg=black,fg=white",
.flags = OPTIONS_TABLE_IS_STYLE,
.separator = ",",
.text = "Style of the pane scrollbar."
},
{ .name = "pane-scrollbars-position",
.type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_WINDOW,
.choices = options_table_pane_scrollbars_position_list,
.default_num = PANE_SCROLLBARS_RIGHT,
.text = "Pane scrollbar position."
},
{ .name = "popup-style",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW,
.default_str = "default",

View File

@ -1171,7 +1171,9 @@ options_push_changes(const char *name)
RB_FOREACH(wp, window_pane_tree, &all_window_panes)
colour_palette_from_option(&wp->palette, wp->options);
}
if (strcmp(name, "pane-border-status") == 0) {
if (strcmp(name, "pane-border-status") == 0 ||
strcmp(name, "pane-scrollbars") == 0 ||
strcmp(name, "pane-scrollbars-position") == 0) {
RB_FOREACH(w, windows, &windows)
layout_fix_panes(w, NULL);
}

View File

@ -30,6 +30,11 @@ static void screen_redraw_draw_pane(struct screen_redraw_ctx *,
struct window_pane *);
static void screen_redraw_set_context(struct client *,
struct screen_redraw_ctx *);
static void screen_redraw_draw_pane_scrollbars(struct screen_redraw_ctx *);
static void screen_redraw_draw_scrollbar(struct screen_redraw_ctx *,
struct window_pane *, int, int, int, u_int, u_int, u_int);
static void screen_redraw_draw_pane_scrollbar(struct screen_redraw_ctx *,
struct window_pane *);
#define START_ISOLATE "\342\201\246"
#define END_ISOLATE "\342\201\251"
@ -113,9 +118,10 @@ screen_redraw_pane_border(struct screen_redraw_ctx *ctx, struct window_pane *wp,
u_int px, u_int py)
{
struct options *oo = wp->window->options;
int split = 0;
u_int ex = wp->xoff + wp->sx, ey = wp->yoff + wp->sy;
int pane_status = ctx->pane_status;
int hsplit = 0, vsplit = 0, pane_status = ctx->pane_status;
int pane_scrollbars = ctx->pane_scrollbars, sb_w = 0;
int sb_pos = ctx->pane_scrollbars_pos;
/* Inside pane. */
if (px >= wp->xoff && px < ex && py >= wp->yoff && py < ey)
@ -125,62 +131,62 @@ screen_redraw_pane_border(struct screen_redraw_ctx *ctx, struct window_pane *wp,
switch (options_get_number(oo, "pane-border-indicators")) {
case PANE_BORDER_COLOUR:
case PANE_BORDER_BOTH:
split = 1;
hsplit = screen_redraw_two_panes(wp->window, 0);
vsplit = screen_redraw_two_panes(wp->window, 1);
break;
}
/* Left/right borders. */
if (pane_status == PANE_STATUS_OFF) {
if (screen_redraw_two_panes(wp->window, 0) && split) {
if (wp->xoff == 0 && px == wp->sx && py <= wp->sy / 2)
return (SCREEN_REDRAW_BORDER_RIGHT);
if (wp->xoff != 0 &&
px == wp->xoff - 1 &&
py > wp->sy / 2)
return (SCREEN_REDRAW_BORDER_LEFT);
} else {
if ((wp->yoff == 0 || py >= wp->yoff - 1) && py <= ey) {
if (wp->xoff != 0 && px == wp->xoff - 1)
return (SCREEN_REDRAW_BORDER_LEFT);
if (px == ex)
/* Are scrollbars enabled? */
if (pane_scrollbars == PANE_SCROLLBARS_ALWAYS ||
(pane_scrollbars == PANE_SCROLLBARS_MODAL &&
window_pane_mode(wp) != WINDOW_PANE_NO_MODE))
sb_w = PANE_SCROLLBARS_WIDTH;
/*
* Left/right borders. The wp->sy / 2 test is to colour only half the
* active window's border when there are two panes.
*/
if ((wp->yoff == 0 || py >= wp->yoff - 1) && py <= ey) {
if (sb_pos == PANE_SCROLLBARS_LEFT) {
if (wp->xoff - sb_w == 0 && px == wp->sx + sb_w)
if (!hsplit || (hsplit && py <= wp->sy / 2))
return (SCREEN_REDRAW_BORDER_RIGHT);
if (wp->xoff - sb_w != 0 && px == wp->xoff - sb_w - 1)
if (!hsplit || (hsplit && py > wp->sy / 2))
return (SCREEN_REDRAW_BORDER_LEFT);
} else { /* sb_pos == PANE_SCROLLBARS_RIGHT */
if (wp->xoff == 0 && px == wp->sx + sb_w)
if (!hsplit || (hsplit && py <= wp->sy / 2))
return (SCREEN_REDRAW_BORDER_RIGHT);
}
}
} else {
if ((wp->yoff == 0 || py >= wp->yoff - 1) && py <= ey) {
if (wp->xoff != 0 && px == wp->xoff - 1)
return (SCREEN_REDRAW_BORDER_LEFT);
if (px == ex)
return (SCREEN_REDRAW_BORDER_RIGHT);
if (!hsplit || (hsplit && py > wp->sy / 2))
return (SCREEN_REDRAW_BORDER_LEFT);
}
}
/* Top/bottom borders. */
if (pane_status == PANE_STATUS_OFF) {
if (screen_redraw_two_panes(wp->window, 1) && split) {
if (wp->yoff == 0 && py == wp->sy && px <= wp->sx / 2)
return (SCREEN_REDRAW_BORDER_BOTTOM);
if (wp->yoff != 0 &&
py == wp->yoff - 1 &&
px > wp->sx / 2)
return (SCREEN_REDRAW_BORDER_TOP);
} else {
if ((wp->xoff == 0 || px >= wp->xoff - 1) && px <= ex) {
if (vsplit && pane_status == PANE_STATUS_OFF && sb_w == 0) {
if (wp->yoff == 0 && py == wp->sy && px <= wp->sx / 2)
return (SCREEN_REDRAW_BORDER_BOTTOM);
if (wp->yoff != 0 && py == wp->yoff - 1 && px > wp->sx / 2)
return (SCREEN_REDRAW_BORDER_TOP);
} 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))) {
if (wp->yoff != 0 && py == wp->yoff - 1)
return (SCREEN_REDRAW_BORDER_TOP);
if (py == ey)
return (SCREEN_REDRAW_BORDER_BOTTOM);
}
} else { /* sb_pos == PANE_SCROLLBARS_RIGHT */
if ((wp->xoff == 0 || px >= wp->xoff) &&
(px <= ex || (sb_w != 0 && px - 1 == ex))) {
if (wp->yoff != 0 && py == wp->yoff - 1)
return (SCREEN_REDRAW_BORDER_TOP);
if (py == ey)
return (SCREEN_REDRAW_BORDER_BOTTOM);
}
}
} else if (pane_status == PANE_STATUS_TOP) {
if ((wp->xoff == 0 || px >= wp->xoff - 1) && px <= ex) {
if (wp->yoff != 0 && py == wp->yoff - 1)
return (SCREEN_REDRAW_BORDER_TOP);
}
} else {
if ((wp->xoff == 0 || px >= wp->xoff - 1) && px <= ex) {
if (py == ey)
return (SCREEN_REDRAW_BORDER_BOTTOM);
}
}
@ -243,8 +249,12 @@ screen_redraw_type_of_cell(struct screen_redraw_ctx *ctx, u_int px, u_int py)
return (CELL_OUTSIDE);
/*
* Construct a bitmask of whether the cells to the left (bit 4), right,
* Construct a bitmask of whether the cells to the left (bit 8), right,
* top, and bottom (bit 1) of this cell are borders.
*
* bits 8 4 2 1: 2
* 8 + 4
* 1
*/
if (px == 0 || screen_redraw_cell_border(ctx, px - 1, py))
borders |= 8;
@ -313,8 +323,10 @@ screen_redraw_check_cell(struct screen_redraw_ctx *ctx, u_int px, u_int py,
struct window_pane *wp, *active;
int pane_status = ctx->pane_status;
u_int sx = w->sx, sy = w->sy;
int border;
int border, pane_scrollbars = ctx->pane_scrollbars;
u_int right, line;
int sb_pos = ctx->pane_scrollbars_pos;
int sb_w = PANE_SCROLLBARS_WIDTH;
*wpp = NULL;
@ -351,6 +363,35 @@ screen_redraw_check_cell(struct screen_redraw_ctx *ctx, u_int px, u_int py,
goto next2;
*wpp = wp;
/* Check if CELL_SCROLLBAR */
if (pane_scrollbars == PANE_SCROLLBARS_ALWAYS ||
(pane_scrollbars == PANE_SCROLLBARS_MODAL &&
window_pane_mode(wp) != WINDOW_PANE_NO_MODE)) {
if (pane_status == PANE_STATUS_TOP)
line = wp->yoff - 1;
else
line = wp->yoff + wp->sy;
/*
* Check if py could lie within a scrollbar. If the
* pane is at the top then py == 0 to sy; if the pane
* is not at the top, then yoff to yoff + sy.
*/
if ((pane_status && py != line) ||
(wp->yoff == 0 && py < wp->sy) ||
(py >= wp->yoff && py < wp->yoff + wp->sy)) {
/* Check if px lies within a scrollbar. */
if ((sb_pos == PANE_SCROLLBARS_RIGHT &&
(px >= wp->xoff + wp->sx &&
px < wp->xoff + wp->sx + sb_w)) ||
(sb_pos == PANE_SCROLLBARS_LEFT &&
(px >= wp->xoff - sb_w &&
px < wp->xoff)))
return (CELL_SCROLLBAR);
}
}
/*
* If definitely inside, return. If not on border, skip.
* Otherwise work out the cell.
@ -510,14 +551,13 @@ screen_redraw_draw_pane_status(struct screen_redraw_ctx *ctx)
/* Update status line and change flags if unchanged. */
static uint64_t
screen_redraw_update(struct client *c, uint64_t flags)
screen_redraw_update(struct screen_redraw_ctx *ctx, uint64_t flags)
{
struct client *c = ctx->c;
struct window *w = c->session->curw->window;
struct window_pane *wp;
struct options *wo = w->options;
int redraw;
enum pane_lines lines;
struct screen_redraw_ctx ctx;
if (c->message_string != NULL)
redraw = status_message_redraw(c);
@ -531,17 +571,17 @@ screen_redraw_update(struct client *c, uint64_t flags)
if (c->overlay_draw != NULL)
flags |= CLIENT_REDRAWOVERLAY;
if (options_get_number(wo, "pane-border-status") != PANE_STATUS_OFF) {
screen_redraw_set_context(c, &ctx);
lines = options_get_number(wo, "pane-border-lines");
if (ctx->pane_status != PANE_STATUS_OFF) {
lines = ctx->pane_lines;
redraw = 0;
TAILQ_FOREACH(wp, &w->panes, entry) {
if (screen_redraw_make_pane_status(c, wp, &ctx, lines))
if (screen_redraw_make_pane_status(c, wp, ctx, lines))
redraw = 1;
}
if (redraw)
flags |= CLIENT_REDRAWBORDERS;
}
return (flags);
}
@ -568,6 +608,10 @@ screen_redraw_set_context(struct client *c, struct screen_redraw_ctx *ctx)
ctx->pane_status = options_get_number(wo, "pane-border-status");
ctx->pane_lines = options_get_number(wo, "pane-border-lines");
ctx->pane_scrollbars = options_get_number(wo, "pane-scrollbars");
ctx->pane_scrollbars_pos = options_get_number(wo,
"pane-scrollbars-position");
tty_window_offset(&c->tty, &ctx->ox, &ctx->oy, &ctx->sx, &ctx->sy);
log_debug("%s: %s @%u ox=%u oy=%u sx=%u sy=%u %u/%d", __func__, c->name,
@ -585,11 +629,12 @@ screen_redraw_screen(struct client *c)
if (c->flags & CLIENT_SUSPENDED)
return;
flags = screen_redraw_update(c, c->flags);
screen_redraw_set_context(c, &ctx);
flags = screen_redraw_update(&ctx, c->flags);
if ((flags & CLIENT_ALLREDRAWFLAGS) == 0)
return;
screen_redraw_set_context(c, &ctx);
tty_sync_start(&c->tty);
tty_update_mode(&c->tty, c->tty.mode, NULL);
@ -598,10 +643,12 @@ screen_redraw_screen(struct client *c)
screen_redraw_draw_borders(&ctx);
if (ctx.pane_status != PANE_STATUS_OFF)
screen_redraw_draw_pane_status(&ctx);
screen_redraw_draw_pane_scrollbars(&ctx);
}
if (flags & CLIENT_REDRAWWINDOW) {
log_debug("%s: redrawing panes", c->name);
screen_redraw_draw_panes(&ctx);
screen_redraw_draw_pane_scrollbars(&ctx);
}
if (ctx.statuslines != 0 &&
(flags & (CLIENT_REDRAWSTATUS|CLIENT_REDRAWSTATUSALWAYS))) {
@ -616,20 +663,35 @@ screen_redraw_screen(struct client *c)
tty_reset(&c->tty);
}
/* Redraw a single pane. */
/* Redraw a single pane and its scrollbar. */
void
screen_redraw_pane(struct client *c, struct window_pane *wp)
screen_redraw_pane(struct client *c, struct window_pane *wp,
int redraw_scrollbar_only)
{
struct screen_redraw_ctx ctx;
struct screen_redraw_ctx ctx;
int pane_scrollbars, mode;
if (!window_pane_visible(wp))
return;
mode = window_pane_mode(wp);
screen_redraw_set_context(c, &ctx);
tty_sync_start(&c->tty);
tty_update_mode(&c->tty, c->tty.mode, NULL);
screen_redraw_draw_pane(&ctx, wp);
if (!redraw_scrollbar_only)
screen_redraw_draw_pane(&ctx, wp);
/*
* Redraw scrollbar if needed. Always redraw scrollbar in a mode because
* if redrawing a pane, it's because pane has scrolled.
*/
pane_scrollbars = ctx.pane_scrollbars;
if (pane_scrollbars == PANE_SCROLLBARS_MODAL &&
mode == WINDOW_PANE_NO_MODE)
pane_scrollbars = PANE_SCROLLBARS_OFF;
if (pane_scrollbars != PANE_SCROLLBARS_OFF)
screen_redraw_draw_pane_scrollbar(&ctx, wp);
tty_reset(&c->tty);
}
@ -675,8 +737,7 @@ screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j)
const struct grid_cell *tmp;
struct overlay_ranges r;
u_int cell_type, x = ctx->ox + i, y = ctx->oy + j;
int arrows = 0, border;
int isolates;
int arrows = 0, border, isolates;
if (c->overlay_check != NULL) {
c->overlay_check(c, c->overlay_data, x, y, 1, &r);
@ -685,7 +746,7 @@ screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j)
}
cell_type = screen_redraw_check_cell(ctx, x, y, &wp);
if (cell_type == CELL_INSIDE)
if (cell_type == CELL_INSIDE || cell_type == CELL_SCROLLBAR)
return;
if (wp == NULL) {
@ -870,3 +931,127 @@ screen_redraw_draw_pane(struct screen_redraw_ctx *ctx, struct window_pane *wp)
tty_draw_line(tty, s, i, j, width, x, y, &defaults, palette);
}
}
/* Draw the panes scrollbars */
static void
screen_redraw_draw_pane_scrollbars(struct screen_redraw_ctx *ctx)
{
struct client *c = ctx->c;
struct window *w = c->session->curw->window;
struct window_pane *wp;
log_debug("%s: %s @%u", __func__, c->name, w->id);
TAILQ_FOREACH(wp, &w->panes, entry) {
switch (ctx->pane_scrollbars) {
case PANE_SCROLLBARS_OFF:
return;
case PANE_SCROLLBARS_MODAL:
if (window_pane_mode(wp) == WINDOW_PANE_NO_MODE)
return;
break;
case PANE_SCROLLBARS_ALWAYS:
break;
}
if (window_pane_visible(wp))
screen_redraw_draw_pane_scrollbar(ctx, wp);
}
}
/* Draw pane scrollbar. */
void
screen_redraw_draw_pane_scrollbar(struct screen_redraw_ctx *ctx,
struct window_pane *wp)
{
struct screen *s = wp->screen;
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_x, sb_y = (int)(wp->yoff - ctx->oy); /* sb top */
if (window_pane_mode(wp) == WINDOW_PANE_NO_MODE) {
if (sb == PANE_SCROLLBARS_MODAL)
return;
/* Show slider at the bottom of the scrollbar. */
total_height = screen_size_y(s) + screen_hsize(s);
percent_view = (double)sb_h / total_height;
slider_h = (double)sb_h * percent_view;
slider_y = sb_h - slider_h;
} else {
if (TAILQ_FIRST(&wp->modes) == NULL)
return;
if (window_copy_get_current_offset(wp, &cm_y, &cm_size) == 0)
return;
total_height = cm_size + sb_h;
percent_view = (double)sb_h / (cm_size + sb_h);
slider_h = (double)sb_h * percent_view;
slider_y = (sb_h + 1) * ((double)cm_y / total_height);
}
if (sb_pos == PANE_SCROLLBARS_LEFT)
sb_x = (int)wp->xoff - sb_w - ctx->ox;
else
sb_x = (int)wp->xoff + wp->sx - ctx->ox;
if (slider_h < 1)
slider_h = 1;
if (slider_y >= sb_h)
slider_y = sb_h - 1;
screen_redraw_draw_scrollbar(ctx, wp, sb_pos, sb_x, sb_y, sb_h,
slider_h, slider_y);
}
static void
screen_redraw_draw_scrollbar(struct screen_redraw_ctx *ctx,
struct window_pane *wp, int sb_pos, int sb_x, int sb_y, u_int sb_h,
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;
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, ' ');
/* Set up style for slider. */
memcpy(&slgc, &gc, sizeof slgc);
slgc.bg = gc.fg;
if (sb_pad != 0) {
if (sb_pos == PANE_SCROLLBARS_RIGHT)
pad_col = 0;
else
pad_col = sb_w - 1;
}
for (i = 0; i < sb_w; i++) {
for (j = 0; j < sb_h; j++) {
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)
continue;
tty_cursor(tty, px, py);
if (sb_pad && i == pad_col) {
tty_cell(tty, &grid_default_cell,
&grid_default_cell, NULL, NULL);
} else {
if (j >= slider_y && j < slider_y + slider_h)
gcp = &slgc;
else
gcp = &gc;
tty_cell(tty, gcp, &grid_default_cell, NULL,
NULL);
}
}
}
}

View File

@ -151,7 +151,7 @@ screen_write_set_client_cb(struct tty_ctx *ttyctx, struct client *c)
*/
log_debug("%s: adding %%%u to deferred redraw", __func__,
wp->id);
wp->flags |= PANE_REDRAW;
wp->flags |= (PANE_REDRAW|PANE_REDRAWSCROLLBAR);
return (-1);
}
@ -1178,13 +1178,14 @@ screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny, u_int bg)
struct screen *s = ctx->s;
struct grid *gd = s->grid;
struct tty_ctx ttyctx;
u_int sy = screen_size_y(s);
if (ny == 0)
ny = 1;
if (s->cy < s->rupper || s->cy > s->rlower) {
if (ny > screen_size_y(s) - s->cy)
ny = screen_size_y(s) - s->cy;
if (ny > sy - s->cy)
ny = sy - s->cy;
if (ny == 0)
return;
@ -1376,13 +1377,14 @@ screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped, u_int bg)
struct screen *s = ctx->s;
struct grid *gd = s->grid;
struct grid_line *gl;
u_int rupper = s->rupper, rlower = s->rlower;
gl = grid_get_line(gd, gd->hsize + s->cy);
if (wrapped)
gl->flags |= GRID_LINE_WRAPPED;
log_debug("%s: at %u,%u (region %u-%u)", __func__, s->cx, s->cy,
s->rupper, s->rlower);
rupper, rlower);
if (bg != ctx->bg) {
screen_write_collect_flush(ctx, 1, __func__);
@ -1700,6 +1702,9 @@ screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only,
ttyctx.num = ctx->scrolled;
ttyctx.bg = ctx->bg;
tty_write(tty_cmd_scrollup, &ttyctx);
if (ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAWSCROLLBAR;
}
ctx->scrolled = 0;
ctx->bg = 8;

View File

@ -2230,7 +2230,7 @@ server_client_loop(void)
server_client_check_pane_resize(wp);
server_client_check_pane_buffer(wp);
}
wp->flags &= ~PANE_REDRAW;
wp->flags &= ~(PANE_REDRAW|PANE_REDRAWSCROLLBAR);
}
check_window_name(w);
}
@ -2662,7 +2662,7 @@ server_client_check_redraw(struct client *c)
struct window_pane *wp;
int needed, tty_flags, mode = tty->mode;
uint64_t client_flags = 0;
int redraw;
int redraw_pane, redraw_scrollbar_only;
u_int bit = 0;
struct timeval tv = { .tv_usec = 1000 };
static struct event ev;
@ -2671,12 +2671,13 @@ server_client_check_redraw(struct client *c)
if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED))
return;
if (c->flags & CLIENT_ALLREDRAWFLAGS) {
log_debug("%s: redraw%s%s%s%s%s", c->name,
log_debug("%s: redraw%s%s%s%s%s%s", c->name,
(c->flags & CLIENT_REDRAWWINDOW) ? " window" : "",
(c->flags & CLIENT_REDRAWSTATUS) ? " status" : "",
(c->flags & CLIENT_REDRAWBORDERS) ? " borders" : "",
(c->flags & CLIENT_REDRAWOVERLAY) ? " overlay" : "",
(c->flags & CLIENT_REDRAWPANES) ? " panes" : "");
(c->flags & CLIENT_REDRAWPANES) ? " panes" : "",
(c->flags & CLIENT_REDRAWSCROLLBARS) ? " scrollbars" : "");
}
/*
@ -2691,11 +2692,15 @@ server_client_check_redraw(struct client *c)
TAILQ_FOREACH(wp, &w->panes, entry) {
if (wp->flags & PANE_REDRAW) {
needed = 1;
client_flags |= CLIENT_REDRAWPANES;
break;
}
if (wp->flags & PANE_REDRAWSCROLLBAR) {
needed = 1;
client_flags |= CLIENT_REDRAWSCROLLBARS;
/* no break - later panes may need redraw */
}
}
if (needed)
client_flags |= CLIENT_REDRAWPANES;
}
if (needed && (left = EVBUFFER_LENGTH(tty->out)) != 0) {
log_debug("%s: redraw deferred (%zu left)", c->name, left);
@ -2708,23 +2713,30 @@ server_client_check_redraw(struct client *c)
if (~c->flags & CLIENT_REDRAWWINDOW) {
TAILQ_FOREACH(wp, &w->panes, entry) {
if (wp->flags & PANE_REDRAW) {
if (wp->flags & (PANE_REDRAW)) {
log_debug("%s: pane %%%u needs redraw",
c->name, wp->id);
c->redraw_panes |= (1 << bit);
} else if (wp->flags & PANE_REDRAWSCROLLBAR) {
log_debug("%s: pane %%%u scrollbar "
"needs redraw", c->name, wp->id);
c->redraw_scrollbars |= (1 << bit);
}
if (++bit == 64) {
/*
* If more that 64 panes, give up and
* just redraw the window.
*/
client_flags &= CLIENT_REDRAWPANES;
client_flags &= ~(CLIENT_REDRAWPANES|
CLIENT_REDRAWSCROLLBARS);
client_flags |= CLIENT_REDRAWWINDOW;
break;
}
}
if (c->redraw_panes != 0)
c->flags |= CLIENT_REDRAWPANES;
if (c->redraw_scrollbars != 0)
c->flags |= CLIENT_REDRAWSCROLLBARS;
}
c->flags |= client_flags;
return;
@ -2740,19 +2752,32 @@ server_client_check_redraw(struct client *c)
* needs to be redrawn.
*/
TAILQ_FOREACH(wp, &w->panes, entry) {
redraw = 0;
redraw_pane = 0;
redraw_scrollbar_only = 0;
if (wp->flags & PANE_REDRAW)
redraw = 1;
else if (c->flags & CLIENT_REDRAWPANES)
redraw = !!(c->redraw_panes & (1 << bit));
redraw_pane = 1;
else if (c->flags & CLIENT_REDRAWPANES) {
if (c->redraw_panes & (1 << bit))
redraw_pane = 1;
} else if (c->flags & CLIENT_REDRAWSCROLLBARS) {
if (c->redraw_scrollbars & (1 << bit))
redraw_scrollbar_only = 1;
}
bit++;
if (!redraw)
if (!redraw_pane && !redraw_scrollbar_only)
continue;
log_debug("%s: redrawing pane %%%u", __func__, wp->id);
screen_redraw_pane(c, wp);
if (redraw_scrollbar_only) {
log_debug("%s: redrawing (scrollbar only) pane "
"%%%u", __func__, wp->id);
} else {
log_debug("%s: redrawing pane %%%u", __func__,
wp->id);
}
screen_redraw_pane(c, wp, redraw_scrollbar_only);
}
c->redraw_panes = 0;
c->flags &= ~CLIENT_REDRAWPANES;
c->redraw_scrollbars = 0;
c->flags &= ~(CLIENT_REDRAWPANES|CLIENT_REDRAWSCROLLBARS);
}
if (c->flags & CLIENT_ALLREDRAWFLAGS) {

40
tmux.1
View File

@ -5026,6 +5026,46 @@ and
.Ql heavy
will fall back to standard ACS line drawing when UTF-8 is not supported.
.Pp
.It Xo Ic pane-scrollbars
.Op Ic off | modal | on
.Xc
When enabled, a character based scrollbar appears on the left or right
of each pane.
A filled section of the scrollbar, known as the
.Ql slider ,
represents the position and size of the visible part of the pane content.
.Pp
If set to
.Ic on
the scrollbar is visible all the time.
If set to
.Ic modal
the scrollbar only appears when the pane is in copy mode or view mode.
When the scrollbar is visible, the pane is narrowed by the width of the
scrollbar and the text in the pane is reflowed.
If set to
.Ic modal ,
the pane is narrowed only when the scrollbar is visible.
.Pp
See also
.Xr pane-scrollbars-style .
.Pp
.It Ic pane-scrollbars-style Ar style
Set the scrollbars style.
For how to specify
.Ar style ,
see the
.Sx STYLES
section.
The foreground colour is used for the slider, the background for the rest of the
scrollbar.
Attributes are ignored.
.Pp
.It Xo Ic pane-scrollbars-position
.Op Ic left | right
.Xc
Sets which side of the pane to display pane scrollbars on.
.Pp
.It Ic window-status-activity-style Ar style
Set status line style for windows with an activity alert.
For how to specify

25
tmux.h
View File

@ -739,6 +739,7 @@ struct colour_palette {
#define CELL_RIGHTJOIN 10
#define CELL_JOIN 11
#define CELL_OUTSIDE 12
#define CELL_SCROLLBAR 13
/* Cell borders. */
#define CELL_BORDERS " xqlkmjwvtun~"
@ -985,6 +986,9 @@ struct screen_redraw_ctx {
int pane_status;
enum pane_lines pane_lines;
int pane_scrollbars;
int pane_scrollbars_pos;
struct grid_cell no_pane_gc;
int no_pane_gc_set;
@ -1103,6 +1107,7 @@ struct window_pane {
#define PANE_EMPTY 0x800
#define PANE_STYLECHANGED 0x1000
#define PANE_UNSEENCHANGES 0x2000
#define PANE_REDRAWSCROLLBAR 0x4000
int argc;
char **argv;
@ -1245,6 +1250,19 @@ TAILQ_HEAD(winlink_stack, winlink);
#define PANE_STATUS_TOP 1
#define PANE_STATUS_BOTTOM 2
/* Pane scrollbars option. */
#define PANE_SCROLLBARS_OFF 0
#define PANE_SCROLLBARS_MODAL 1
#define PANE_SCROLLBARS_ALWAYS 2
/* Pane scrollbars position option. */
#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
/* Layout direction. */
enum layout_type {
LAYOUT_LEFTRIGHT,
@ -1880,13 +1898,15 @@ struct client {
#define CLIENT_CLIPBOARDBUFFER 0x800000000ULL
#define CLIENT_BRACKETPASTING 0x1000000000ULL
#define CLIENT_ASSUMEPASTING 0x2000000000ULL
#define CLIENT_REDRAWSCROLLBARS 0x4000000000ULL
#define CLIENT_ALLREDRAWFLAGS \
(CLIENT_REDRAWWINDOW| \
CLIENT_REDRAWSTATUS| \
CLIENT_REDRAWSTATUSALWAYS| \
CLIENT_REDRAWBORDERS| \
CLIENT_REDRAWOVERLAY| \
CLIENT_REDRAWPANES)
CLIENT_REDRAWPANES| \
CLIENT_REDRAWSCROLLBARS)
#define CLIENT_UNATTACHEDFLAGS \
(CLIENT_DEAD| \
CLIENT_SUSPENDED| \
@ -1913,6 +1933,7 @@ struct client {
key_code last_key;
uint64_t redraw_panes;
uint64_t redraw_scrollbars;
int message_ignore_keys;
int message_ignore_styles;
@ -3014,7 +3035,7 @@ void screen_write_alternateoff(struct screen_write_ctx *,
/* screen-redraw.c */
void screen_redraw_screen(struct client *);
void screen_redraw_pane(struct client *, struct window_pane *);
void screen_redraw_pane(struct client *, struct window_pane *, int);
/* screen.c */
void screen_init(struct screen *, u_int, u_int, u_int);

View File

@ -4319,6 +4319,8 @@ window_copy_redraw_lines(struct window_mode_entry *wme, u_int py, u_int ny)
window_copy_write_line(wme, &ctx, i);
screen_write_cursormove(&ctx, data->cx, data->cy, 0);
screen_write_stop(&ctx);
wp->flags |= PANE_REDRAWSCROLLBAR;
}
static void
@ -5367,8 +5369,7 @@ window_copy_cursor_previous_word_pos(struct window_mode_entry *wme,
py = hsize + data->cy - data->oy;
grid_reader_start(&gr, back_s->grid, px, py);
grid_reader_cursor_previous_word(&gr, separators, /* already= */ 0,
/* stop_at_eol= */ 1);
grid_reader_cursor_previous_word(&gr, separators, 0, 1);
grid_reader_get_cursor(&gr, &px, &py);
*ppx = px;
*ppy = py;
@ -5481,6 +5482,7 @@ window_copy_scroll_up(struct window_mode_entry *wme, u_int ny)
window_copy_write_line(wme, &ctx, screen_size_y(s) - ny - 1);
screen_write_cursormove(&ctx, data->cx, data->cy, 0);
screen_write_stop(&ctx);
wp->flags |= PANE_REDRAWSCROLLBAR;
}
static void
@ -5514,6 +5516,7 @@ window_copy_scroll_down(struct window_mode_entry *wme, u_int ny)
window_copy_write_line(wme, &ctx, 1);
screen_write_cursormove(&ctx, data->cx, data->cy, 0);
screen_write_stop(&ctx);
wp->flags |= PANE_REDRAWSCROLLBAR;
}
static void

View File

@ -584,11 +584,31 @@ struct window_pane *
window_get_active_at(struct window *w, u_int x, u_int y)
{
struct window_pane *wp;
int pane_scrollbars;
u_int sb_pos, sb_w, xoff, sx;
pane_scrollbars = options_get_number(w->options, "pane-scrollbars");
sb_pos = options_get_number(w->options, "pane-scrollbars-position");
TAILQ_FOREACH(wp, &w->panes, entry) {
if (!window_pane_visible(wp))
continue;
if (x < wp->xoff || x > wp->xoff + wp->sx)
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
sb_w = 0;
if (sb_pos == PANE_SCROLLBARS_LEFT) {
xoff = wp->xoff - sb_w;
sx = wp->sx + sb_w;
} else { /* sb_pos == PANE_SCROLLBARS_RIGHT */
xoff = wp->xoff;
sx = wp->sx + sb_w;
}
if (x < xoff || x > xoff + sx)
continue;
if (y < wp->yoff || y > wp->yoff + wp->sy)
continue;
@ -1086,6 +1106,7 @@ window_pane_set_mode(struct window_pane *wp, struct window_pane *swp,
struct args *args)
{
struct window_mode_entry *wme;
struct window *w = wp->window;
if (!TAILQ_EMPTY(&wp->modes) && TAILQ_FIRST(&wp->modes)->mode == mode)
return (1);
@ -1106,9 +1127,10 @@ window_pane_set_mode(struct window_pane *wp, struct window_pane *swp,
TAILQ_INSERT_HEAD(&wp->modes, wme, entry);
wme->screen = wme->mode->init(wme, fs, args);
}
wp->screen = wme->screen;
wp->flags |= (PANE_REDRAW|PANE_CHANGED);
wp->flags |= (PANE_REDRAW|PANE_REDRAWSCROLLBAR|PANE_CHANGED);
layout_fix_panes(w, NULL);
server_redraw_window_borders(wp->window);
server_status_window(wp->window);
@ -1121,6 +1143,7 @@ void
window_pane_reset_mode(struct window_pane *wp)
{
struct window_mode_entry *wme, *next;
struct window *w = wp->window;
if (TAILQ_EMPTY(&wp->modes))
return;
@ -1141,7 +1164,9 @@ window_pane_reset_mode(struct window_pane *wp)
if (next->mode->resize != NULL)
next->mode->resize(next, wp->sx, wp->sy);
}
wp->flags |= (PANE_REDRAW|PANE_CHANGED);
wp->flags |= (PANE_REDRAW|PANE_REDRAWSCROLLBAR|PANE_CHANGED);
layout_fix_panes(w, NULL);
server_redraw_window_borders(wp->window);
server_status_window(wp->window);