mirror of
https://github.com/vim/vim.git
synced 2026-05-28 00:21:37 +02:00
patch 9.2.0338: Cannot handle mouseclicks in the tabline
Problem: Cannot handle mouseclicks in the tabline
Solution: Support %[FuncName] click regions in 'tabline', add "area" key
to the click info dict (Yasuhiro Matsumoto).
The previous implementation resolved and stored click regions only for
per-window statuslines; the tabline path in win_redr_custom() (wp==NULL)
parsed %[FuncName] but discarded the regions, and tabline clicks were
dispatched via TabPageIdxs[] which didn't know about them.
Add a global tabline_stl_click array populated from the tabline path,
refactor stl_click_handler() to take the regions directly, and dispatch
matching clicks from do_mouse() before falling through to tab selection.
The winid entry in the callback dict is 0 for tabline clicks.
related: #19841
closes: #19950
Supported by AI.
Signed-off-by: Yasuhiro Matsumoto <mattn.jp@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
committed by
Christian Brabandt
parent
8fd37e42a6
commit
0802e00f2a
+11
-4
@@ -1,4 +1,4 @@
|
||||
*options.txt* For Vim version 9.2. Last change: 2026 Apr 09
|
||||
*options.txt* For Vim version 9.2. Last change: 2026 Apr 11
|
||||
|
||||
|
||||
VIM REFERENCE MANUAL by Bram Moolenaar
|
||||
@@ -8626,7 +8626,8 @@ A jump table for the options with a short description can be found at |Q_op|.
|
||||
|
||||
*stl-%[FuncName]*
|
||||
%[ defines clickable regions in the statusline. When the user clicks
|
||||
on a region with the mouse, the specified function is called.
|
||||
on a region with the mouse, the specified function is called. The
|
||||
same syntax can also be used in 'tabline'.
|
||||
|
||||
%[FuncName] Start of a clickable region. "FuncName" is the name
|
||||
of a Vim function to call when the region is clicked.
|
||||
@@ -8644,11 +8645,17 @@ A jump table for the options with a short description can be found at |Q_op|.
|
||||
"button" Mouse button: "l" (left), "m" (middle), "r" (right).
|
||||
"mods" Modifier keys: combination of "s" (shift), "c" (ctrl),
|
||||
"a" (alt). Empty string if no modifiers.
|
||||
"winid" |window-ID| of the window whose statusline was clicked.
|
||||
"winid" |window-ID| of the window whose statusline was clicked,
|
||||
or 0 when the click was in 'tabline'.
|
||||
"area" "statusline" or "tabline". Indicates which option the
|
||||
clicked region belongs to. Useful when a single
|
||||
callback is shared between 'statusline' and 'tabline'.
|
||||
|
||||
If the function returns non-zero, the statusline is redrawn.
|
||||
Dragging the statusline to resize the window still works even when
|
||||
click handlers are defined.
|
||||
click handlers are defined. When used in 'tabline', clicks in
|
||||
%[FuncName] regions are dispatched to the callback instead of the
|
||||
default tab-selection behavior.
|
||||
|
||||
Example: >
|
||||
func! ClickFile(info)
|
||||
|
||||
@@ -104,6 +104,10 @@ EXTERN int redrawing_for_callback INIT(= 0);
|
||||
*/
|
||||
EXTERN short *TabPageIdxs INIT(= NULL);
|
||||
|
||||
// Click regions for 'tabline' (%[FuncName]).
|
||||
EXTERN stl_click_region_T *tabline_stl_click INIT(= NULL);
|
||||
EXTERN int tabline_stl_click_count INIT(= 0);
|
||||
|
||||
#ifdef FEAT_PROP_POPUP
|
||||
// Array with size Rows x Columns containing zindex of popups.
|
||||
EXTERN short *popup_mask INIT(= NULL);
|
||||
|
||||
+59
-11
@@ -21,6 +21,9 @@ static long mouse_hor_step = 6;
|
||||
static long mouse_vert_step = 3;
|
||||
static win_T *dragwin = NULL; // window being dragged
|
||||
static int stl_click_handler(win_T *wp, int mcol, int which_button, int mods);
|
||||
static int stl_click_handler_regions(stl_click_region_T *regions,
|
||||
int region_count, int winid, int mcol,
|
||||
int which_button, int mods);
|
||||
|
||||
void
|
||||
mouse_set_vert_scroll_step(long step)
|
||||
@@ -502,6 +505,16 @@ do_mouse(
|
||||
// Check for clicking in the tab page line.
|
||||
if (TabPageIdxs != NULL && mouse_row == 0 && firstwin->w_winrow > 0)
|
||||
{
|
||||
// Dispatch 'tabline' %[FuncName] click regions before falling through
|
||||
// to tab-page selection. On drag events fall through to the normal
|
||||
// tab-drag handling.
|
||||
if (is_click && !is_drag
|
||||
&& stl_click_handler_regions(tabline_stl_click,
|
||||
tabline_stl_click_count,
|
||||
0, mouse_col, which_button,
|
||||
mod_mask))
|
||||
return FALSE;
|
||||
|
||||
tp_label.just_in = true;
|
||||
tp_label.nr = TabPageIdxs[mouse_col];
|
||||
|
||||
@@ -1640,11 +1653,19 @@ mouse_model_popup(void)
|
||||
}
|
||||
|
||||
/*
|
||||
* Call a statusline click handler function.
|
||||
* Call a click-region callback function.
|
||||
* "regions"/"region_count" describe the resolved click regions,
|
||||
* "winid" is stored as the "winid" key in the info dict (0 for tabline).
|
||||
* Returns TRUE if the function was called and handled the click.
|
||||
*/
|
||||
static int
|
||||
stl_click_handler(win_T *wp, int mcol, int which_button, int mods)
|
||||
stl_click_handler_regions(
|
||||
stl_click_region_T *regions,
|
||||
int region_count,
|
||||
int winid,
|
||||
int mcol,
|
||||
int which_button,
|
||||
int mods)
|
||||
{
|
||||
#ifdef FEAT_EVAL
|
||||
int n;
|
||||
@@ -1658,17 +1679,16 @@ stl_click_handler(win_T *wp, int mcol, int which_button, int mods)
|
||||
funcexe_T funcexe;
|
||||
int col = mcol;
|
||||
|
||||
if (wp == NULL || wp->w_stl_click == NULL || wp->w_stl_click_count == 0)
|
||||
if (regions == NULL || region_count == 0)
|
||||
return FALSE;
|
||||
|
||||
// Find the click region at the given column.
|
||||
for (n = 0; n < wp->w_stl_click_count; n++)
|
||||
for (n = 0; n < region_count; n++)
|
||||
{
|
||||
if (col >= wp->w_stl_click[n].col_start
|
||||
&& col < wp->w_stl_click[n].col_end)
|
||||
if (col >= regions[n].col_start && col < regions[n].col_end)
|
||||
break;
|
||||
}
|
||||
if (n >= wp->w_stl_click_count || wp->w_stl_click[n].funcname == NULL)
|
||||
if (n >= region_count || regions[n].funcname == NULL)
|
||||
return FALSE;
|
||||
|
||||
// Build the info dictionary.
|
||||
@@ -1676,7 +1696,7 @@ stl_click_handler(win_T *wp, int mcol, int which_button, int mods)
|
||||
if (info == NULL)
|
||||
return FALSE;
|
||||
|
||||
dict_add_number(info, "minwid", wp->w_stl_click[n].minwid);
|
||||
dict_add_number(info, "minwid", regions[n].minwid);
|
||||
|
||||
// Determine number of clicks.
|
||||
// MOD_MASK_2CLICK=0x20, MOD_MASK_3CLICK=0x40, MOD_MASK_4CLICK=0x60
|
||||
@@ -1705,7 +1725,13 @@ stl_click_handler(win_T *wp, int mcol, int which_button, int mods)
|
||||
mods_str[mi] = NUL;
|
||||
dict_add_string(info, "mods", mods_str);
|
||||
|
||||
dict_add_number(info, "winid", wp->w_id);
|
||||
dict_add_number(info, "winid", winid);
|
||||
|
||||
// "area": which option the clicked region belongs to. Lets a shared
|
||||
// dispatcher distinguish 'statusline' from 'tabline' (and future areas)
|
||||
// without having to overload winid == 0.
|
||||
dict_add_string(info, "area",
|
||||
winid == 0 ? (char_u *)"tabline" : (char_u *)"statusline");
|
||||
|
||||
// Call the function with the info dict as argument.
|
||||
argvars[0].v_type = VAR_DICT;
|
||||
@@ -1718,7 +1744,7 @@ stl_click_handler(win_T *wp, int mcol, int which_button, int mods)
|
||||
|
||||
CLEAR_FIELD(funcexe);
|
||||
funcexe.fe_evaluate = TRUE;
|
||||
(void)call_func(wp->w_stl_click[n].funcname, -1,
|
||||
(void)call_func(regions[n].funcname, -1,
|
||||
&rettv, 1, argvars, &funcexe);
|
||||
|
||||
n = (int)rettv.vval.v_number;
|
||||
@@ -1726,11 +1752,20 @@ stl_click_handler(win_T *wp, int mcol, int which_button, int mods)
|
||||
dict_unref(info);
|
||||
|
||||
if (n != 0)
|
||||
{
|
||||
// Make sure the tabline gets redrawn too when the callback asks for
|
||||
// a redraw (redraw_statuslines() only redraws the tabline when
|
||||
// redraw_tabline is set).
|
||||
if (winid == 0)
|
||||
redraw_tabline = TRUE;
|
||||
redraw_statuslines();
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
#else
|
||||
(void)wp;
|
||||
(void)regions;
|
||||
(void)region_count;
|
||||
(void)winid;
|
||||
(void)mcol;
|
||||
(void)which_button;
|
||||
(void)mods;
|
||||
@@ -1738,6 +1773,19 @@ stl_click_handler(win_T *wp, int mcol, int which_button, int mods)
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Call a statusline click handler function for window "wp".
|
||||
* Returns TRUE if the function was called and handled the click.
|
||||
*/
|
||||
static int
|
||||
stl_click_handler(win_T *wp, int mcol, int which_button, int mods)
|
||||
{
|
||||
if (wp == NULL)
|
||||
return FALSE;
|
||||
return stl_click_handler_regions(wp->w_stl_click, wp->w_stl_click_count,
|
||||
wp->w_id, mcol, which_button, mods);
|
||||
}
|
||||
|
||||
// dragwin is declared near the top of the file
|
||||
|
||||
/*
|
||||
|
||||
+32
-14
@@ -1486,23 +1486,41 @@ win_redr_custom(
|
||||
TabPageIdxs[col++] = fillchar;
|
||||
}
|
||||
|
||||
// Resolve click function regions for statusline.
|
||||
if (wp != NULL && !draw_ruler)
|
||||
// Resolve click function regions for statusline or tabline.
|
||||
if (!draw_ruler)
|
||||
{
|
||||
int click_count = 0;
|
||||
stl_click_region_T **out_regions;
|
||||
int *out_count;
|
||||
int base_col;
|
||||
int click_count = 0;
|
||||
|
||||
if (wp != NULL)
|
||||
{
|
||||
out_regions = &wp->w_stl_click;
|
||||
out_count = &wp->w_stl_click_count;
|
||||
base_col = wp->w_wincol;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 'tabline': store regions in global state since there is no
|
||||
// associated window.
|
||||
out_regions = &tabline_stl_click;
|
||||
out_count = &tabline_stl_click_count;
|
||||
base_col = firstwin->w_wincol;
|
||||
}
|
||||
|
||||
// Count the click regions.
|
||||
for (n = 0; clicktab[n].start != NULL; n++)
|
||||
click_count++;
|
||||
|
||||
// Free old click regions.
|
||||
if (wp->w_stl_click != NULL)
|
||||
if (*out_regions != NULL)
|
||||
{
|
||||
for (n = 0; n < wp->w_stl_click_count; n++)
|
||||
vim_free(wp->w_stl_click[n].funcname);
|
||||
VIM_CLEAR(wp->w_stl_click);
|
||||
for (n = 0; n < *out_count; n++)
|
||||
vim_free((*out_regions)[n].funcname);
|
||||
VIM_CLEAR(*out_regions);
|
||||
}
|
||||
wp->w_stl_click_count = 0;
|
||||
*out_count = 0;
|
||||
|
||||
if (click_count > 0)
|
||||
{
|
||||
@@ -1514,7 +1532,7 @@ win_redr_custom(
|
||||
{
|
||||
char_u *cur_funcname = NULL;
|
||||
int cur_minwid = 0;
|
||||
int region_start = wp->w_wincol;
|
||||
int region_start = base_col;
|
||||
|
||||
// Walk through click records converting buffer positions
|
||||
// to screen columns.
|
||||
@@ -1530,7 +1548,7 @@ win_redr_custom(
|
||||
if (cur_funcname != NULL)
|
||||
{
|
||||
regions[rcount].col_start = region_start;
|
||||
regions[rcount].col_end = wp->w_wincol + len;
|
||||
regions[rcount].col_end = base_col + len;
|
||||
regions[rcount].funcname =
|
||||
vim_strsave(cur_funcname);
|
||||
regions[rcount].minwid = cur_minwid;
|
||||
@@ -1539,22 +1557,22 @@ win_redr_custom(
|
||||
|
||||
cur_funcname = clicktab[n].funcname;
|
||||
cur_minwid = clicktab[n].minwid;
|
||||
region_start = wp->w_wincol + len;
|
||||
region_start = base_col + len;
|
||||
}
|
||||
|
||||
// Close final region if it extends to the end.
|
||||
if (cur_funcname != NULL)
|
||||
{
|
||||
regions[rcount].col_start = region_start;
|
||||
regions[rcount].col_end = wp->w_wincol + maxwidth;
|
||||
regions[rcount].col_end = base_col + maxwidth;
|
||||
regions[rcount].funcname =
|
||||
vim_strsave(cur_funcname);
|
||||
regions[rcount].minwid = cur_minwid;
|
||||
rcount++;
|
||||
}
|
||||
|
||||
wp->w_stl_click = regions;
|
||||
wp->w_stl_click_count = rcount;
|
||||
*out_regions = regions;
|
||||
*out_count = rcount;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -752,6 +752,7 @@ func Test_statusline_click_handler()
|
||||
call assert_equal(1, g:stl_click_info.nclicks)
|
||||
call assert_equal(0, g:stl_click_info.minwid)
|
||||
call assert_equal(win_getid(), g:stl_click_info.winid)
|
||||
call assert_equal('statusline', g:stl_click_info.area)
|
||||
unlet! g:stl_click_info
|
||||
|
||||
" Click outside click region (on the filename part)
|
||||
@@ -873,4 +874,60 @@ func Test_statusline_click_linebreak_still_works()
|
||||
let &laststatus = save_ls
|
||||
endfunc
|
||||
|
||||
func Test_tabline_click_handler()
|
||||
let save_mouse = &mouse
|
||||
let save_tal = &tabline
|
||||
let save_stal = &showtabline
|
||||
if has('gui')
|
||||
let save_go = &guioptions
|
||||
set guioptions-=e
|
||||
endif
|
||||
set mouse=a
|
||||
set showtabline=2
|
||||
|
||||
" Two adjacent click regions in 'tabline' with different minwid.
|
||||
set tabline=%1[StlClickTestFunc][AAA]%[]%2[StlClickTestFunc][BBB]%[]
|
||||
redraw!
|
||||
|
||||
" Click on [AAA] region (tabline is row 1).
|
||||
call test_setmouse(1, 2)
|
||||
call feedkeys("\<LeftMouse>\<LeftRelease>", 'xt')
|
||||
call assert_true(exists('g:stl_click_info'))
|
||||
call assert_equal('l', g:stl_click_info.button)
|
||||
call assert_equal(1, g:stl_click_info.nclicks)
|
||||
call assert_equal(1, g:stl_click_info.minwid)
|
||||
" winid is 0 for tabline clicks (no associated window).
|
||||
call assert_equal(0, g:stl_click_info.winid)
|
||||
call assert_equal('tabline', g:stl_click_info.area)
|
||||
unlet! g:stl_click_info
|
||||
|
||||
" Click on [BBB] region.
|
||||
call test_setmouse(1, 7)
|
||||
call feedkeys("\<LeftMouse>\<LeftRelease>", 'xt')
|
||||
call assert_true(exists('g:stl_click_info'))
|
||||
call assert_equal(2, g:stl_click_info.minwid)
|
||||
unlet! g:stl_click_info
|
||||
|
||||
" Middle click on [AAA].
|
||||
call test_setmouse(1, 2)
|
||||
call feedkeys("\<MiddleMouse>\<MiddleRelease>", 'xt')
|
||||
call assert_true(exists('g:stl_click_info'))
|
||||
call assert_equal('m', g:stl_click_info.button)
|
||||
unlet! g:stl_click_info
|
||||
|
||||
" Click outside any %[...] region: no callback, no error.
|
||||
set tabline=xxx%1[StlClickTestFunc][YYY]%[]
|
||||
redraw!
|
||||
call test_setmouse(1, 1)
|
||||
call feedkeys("\<LeftMouse>\<LeftRelease>", 'xt')
|
||||
call assert_false(exists('g:stl_click_info'))
|
||||
|
||||
let &mouse = save_mouse
|
||||
let &tabline = save_tal
|
||||
let &showtabline = save_stal
|
||||
if has('gui')
|
||||
let &guioptions = save_go
|
||||
endif
|
||||
endfunc
|
||||
|
||||
" vim: shiftwidth=2 sts=2 expandtab
|
||||
|
||||
@@ -734,6 +734,8 @@ static char *(features[]) =
|
||||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
338,
|
||||
/**/
|
||||
337,
|
||||
/**/
|
||||
|
||||
Reference in New Issue
Block a user