0
0
mirror of https://github.com/vim/vim.git synced 2025-10-29 09:37:35 -04:00

patch 9.1.1856: cannot style popup window (border, shadow, etc)

Problem:  cannot style popup window (border, shadow, etc)
Solution: Extend the 'completepopup' option with additional properties
          (Girish Palya)

This patch extends the 'completepopup' option with additional settings
to allow more configuration of info popup window.

New values:
```
- close           "on" (default) or "off"
- resize          "on" (default) or "off"
- borderchars     specify eight characters (separated by semicolons) to
                  draw the popup border: top, right, bottom, left,
                  topleft, topright, botright, botleft.
- borderhighlight highlight group for the popup border characters
- shadow          pum shadow
```

closes: #18487

Signed-off-by: Girish Palya <girishji@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
Girish Palya
2025-10-14 18:48:36 +00:00
committed by Christian Brabandt
parent e15cd0f065
commit 1a09f11f5d
12 changed files with 324 additions and 59 deletions

View File

@@ -1219,8 +1219,10 @@ popup_adjust_position(win_T *wp)
int center_hor = FALSE;
int allow_adjust_left = !wp->w_popup_fixed;
int top_extra = popup_top_extra(wp);
int right_extra = wp->w_popup_border[1] + wp->w_popup_padding[1];
int bot_extra = wp->w_popup_border[2] + wp->w_popup_padding[2];
int right_extra = wp->w_popup_border[1] + wp->w_popup_padding[1]
+ (wp->w_popup_shadow ? 2 : 0);
int bot_extra = wp->w_popup_border[2] + wp->w_popup_padding[2]
+ wp->w_popup_shadow;
int left_extra = wp->w_popup_border[3] + wp->w_popup_padding[3];
int extra_height = top_extra + bot_extra;
int extra_width = left_extra + right_extra;
@@ -1782,6 +1784,21 @@ popup_set_buffer_text(buf_T *buf, typval_T text)
curbuf = curwin->w_buffer;
}
#define SET_BORDER_CHARS(a0, a1, a2, a3, a4, a5, a6, a7) \
do { \
if (wp != NULL) \
{ \
wp->w_border_char[0] = (a0); \
wp->w_border_char[1] = (a1); \
wp->w_border_char[2] = (a2); \
wp->w_border_char[3] = (a3); \
wp->w_border_char[4] = (a4); \
wp->w_border_char[5] = (a5); \
wp->w_border_char[6] = (a6); \
wp->w_border_char[7] = (a7); \
} \
} while (0)
/*
* Parse the 'previewpopup' or 'completepopup' option and apply the values to
* window "wp" if it is not NULL.
@@ -1795,6 +1812,7 @@ parse_popup_option(win_T *wp, int is_preview)
!is_preview ? p_cpp :
#endif
p_pvp;
int border_enabled = FALSE;
if (wp != NULL)
wp->w_popup_flags &= ~POPF_INFO_MENU;
@@ -1847,28 +1865,159 @@ parse_popup_option(win_T *wp, int is_preview)
*p = NUL;
set_string_option_direct_in_win(wp, (char_u *)"wincolor", -1,
s + 10, OPT_FREE|OPT_LOCAL, 0);
s + 10, OPT_FREE|OPT_LOCAL, 0);
*p = c;
}
}
else if (STRNCMP(s, "borderhighlight:", 16) == 0)
{
char_u *arg = s + 16;
if (*arg == NUL || *arg == ',')
return FAIL;
if (wp != NULL)
{
for (int i = 0; i < 4; ++i)
{
VIM_CLEAR(wp->w_border_highlight[i]);
wp->w_border_highlight[i] = vim_strnsave(arg, p - arg);
}
}
}
else if (STRNCMP(s, "border:", 7) == 0)
{
// Note: Keep this in sync with p_popup_option_border_values.
char_u *arg = s + 7;
int on = STRNCMP(arg, "on", 2) == 0 && arg + 2 == p;
int off = STRNCMP(arg, "off", 3) == 0 && arg + 3 == p;
int i;
int token_len = p - arg;
char_u *token;
// Use box-drawing characters only when 'encoding' is "utf-8" and
// 'ambiwidth' is "single".
int can_use_box_chars = (enc_utf8 && *p_ambw == 's');
if (!on && !off)
if (token_len == 0
|| (STRNCMP(arg, "off", 3) == 0 && arg + 3 == p))
{
if (wp != NULL)
{
for (i = 0; i < 4; ++i)
wp->w_popup_border[i] = 0;
SET_BORDER_CHARS(0, 0, 0, 0, 0, 0, 0, 0);
// only show the X for close when there is a border
wp->w_popup_close = POPCLOSE_NONE;
}
continue;
}
token = vim_strnsave(arg, token_len);
if (token == NULL)
return FAIL;
if ((can_use_box_chars && (STRCMP(token, "single") == 0
|| STRCMP(token, "double") == 0
|| STRCMP(token, "on") == 0
|| STRCMP(token, "round") == 0))
|| STRCMP(token, "ascii") == 0
|| (STRNCMP(token, "custom:", 7) == 0))
{
if (STRCMP(token, "single") == 0)
SET_BORDER_CHARS(0x2500, 0x2502, 0x2500, 0x2502, // ─ │ ─ │
0x250c, 0x2510, 0x2518, 0x2514); // ┌ ┐ ┘ └
else if (STRCMP(token, "double") == 0)
SET_BORDER_CHARS(0x2550, 0x2551, 0x2550, 0x2551, // ═ ║ ═ ║
0x2554, 0x2557, 0x255D, 0x255A); // ╔ ╗ ╝ ╚
else if (STRCMP(token, "round") == 0)
SET_BORDER_CHARS(0x2500, 0x2502, 0x2500, 0x2502, // ─ │ ─ │
0x256d, 0x256e, 0x256f, 0x2570); // ╭ ╮ ╯ ╰
else if (STRCMP(token, "on") == 0)
SET_BORDER_CHARS(0, 0, 0, 0, 0, 0, 0, 0);
else if (STRCMP(token, "ascii") == 0)
SET_BORDER_CHARS('-', '|', '-', '|', '+', '+', '+', '+');
else if (STRNCMP(token, "custom:", 7) == 0)
{
char_u *q = token + 7;
int out[8];
int failed = FALSE;
SET_BORDER_CHARS(0, 0, 0, 0, 0, 0, 0, 0);
for (i = 0; i < 8 && !failed; i++)
{
if (*q == NUL)
failed = TRUE;
else
{
out[i] = mb_ptr2char(q);
mb_ptr2char_adv(&q);
if (i < 7)
{
if (*q != ';')
failed = TRUE; // must be semicolon
q++;
}
}
}
if (failed || *q != NUL) // must end exactly after the 8th char
{
vim_free(token);
return FAIL;
}
SET_BORDER_CHARS(out[0], out[1], out[2], out[3], out[4],
out[5], out[6], out[7]);
}
}
else
{
vim_free(token);
return FAIL;
}
if (wp != NULL)
{
for (i = 0; i < 4; ++i)
wp->w_popup_border[i] = on ? 1 : 0;
if (off)
// only show the X for close when there is a border
wp->w_popup_close = POPCLOSE_NONE;
wp->w_popup_border[i] = 1;
}
border_enabled = TRUE;
vim_free(token);
}
else if (STRNCMP(s, "close:", 6) == 0)
{
char_u *arg = s + 6;
int on = STRNCMP(arg, "on", 2) == 0 && arg + 2 == p;
int off = STRNCMP(arg, "off", 3) == 0 && arg + 3 == p;
if ((!on && !off) || is_preview)
return FAIL;
on = on && mouse_has(MOUSE_INSERT) && border_enabled;
if (wp != NULL)
wp->w_popup_close = on ? POPCLOSE_BUTTON : POPCLOSE_NONE;
}
else if (STRNCMP(s, "resize:", 7) == 0)
{
char_u *arg = s + 7;
int on = STRNCMP(arg, "on", 2) == 0 && arg + 2 == p;
int off = STRNCMP(arg, "off", 3) == 0 && arg + 3 == p;
if ((!on && !off) || is_preview)
return FAIL;
if (wp != NULL)
{
if (on && mouse_has(MOUSE_INSERT))
wp->w_popup_flags |= POPF_RESIZE;
else
wp->w_popup_flags &= ~POPF_RESIZE;
}
}
else if (STRNCMP(s, "shadow:", 7) == 0)
{
char_u *arg = s + 7;
int on = STRNCMP(arg, "on", 2) == 0 && arg + 2 == p;
int off = STRNCMP(arg, "off", 3) == 0 && arg + 3 == p;
if ((!on && !off) || is_preview)
return FAIL;
if (wp != NULL)
wp->w_popup_shadow = on ? 1 : 0;
}
else if (STRNCMP(s, "align:", 6) == 0)
{
@@ -2297,6 +2446,12 @@ popup_create(typval_T *argvars, typval_T *rettv, create_type_T type)
parse_previewpopup(wp);
popup_set_wantpos_cursor(wp, wp->w_minwidth, d);
}
for (i = 0; i < 4; ++i)
VIM_CLEAR(wp->w_border_highlight[i]);
for (i = 0; i < 8; ++i)
wp->w_border_char[i] = 0;
# ifdef FEAT_QUICKFIX
if (type == TYPE_INFO)
{
@@ -2311,10 +2466,6 @@ popup_create(typval_T *argvars, typval_T *rettv, create_type_T type)
}
# endif
for (i = 0; i < 4; ++i)
VIM_CLEAR(wp->w_border_highlight[i]);
for (i = 0; i < 8; ++i)
wp->w_border_char[i] = 0;
wp->w_want_scrollbar = 1;
wp->w_popup_fixed = 0;
wp->w_filter_mode = MODE_ALL;
@@ -4151,7 +4302,6 @@ update_popups(void (*win_update)(win_T *wp))
else
wp->w_popup_flags &= ~POPF_ON_CMDLINE;
// We can only use these line drawing characters when 'encoding' is
// "utf-8" and 'ambiwidth' is "single".
if (enc_utf8 && *p_ambw == 's')
@@ -4374,6 +4524,18 @@ update_popups(void (*win_update)(win_T *wp))
popup_attr);
}
// right shadow
if (wp->w_popup_shadow)
{
int col = wincol + total_width;
for (i = 0; i < total_height; ++i)
{
row = wp->w_winrow + i + 1;
put_shadow_char(row, col);
put_shadow_char(row, col + 1);
}
}
if (wp->w_popup_padding[2] > 0)
{
// bottom padding
@@ -4387,7 +4549,7 @@ update_popups(void (*win_update)(win_T *wp))
{
// bottom border
row = wp->w_winrow + total_height - 1;
screen_fill(row , row + 1,
screen_fill(row, row + 1,
wincol < 0 ? 0 : wincol,
wincol + total_width,
wp->w_popup_border[3] != 0 && wp->w_popup_leftoff == 0
@@ -4400,6 +4562,15 @@ update_popups(void (*win_update)(win_T *wp))
}
}
if (wp->w_popup_shadow)
{
// bottom shadow
row = wp->w_winrow + total_height;
for (int col = 2 + (wincol < 0 ? 0 : wincol);
col < wincol + total_width; col++)
put_shadow_char(row, col);
}
if (wp->w_popup_close == POPCLOSE_BUTTON)
{
// close button goes on top of anything at the top-right corner