mirror of
https://github.com/vim/vim.git
synced 2025-09-25 03:54:15 -04:00
patch 9.0.0917: the WinScrolled autocommand event is not enough
Problem: The WinScrolled autocommand event is not enough. Solution: Add WinResized and provide information about what changed. (closes #11576)
This commit is contained in:
@@ -1371,21 +1371,24 @@ WinNew When a new window was created. Not done for
|
||||
Before a WinEnter event.
|
||||
|
||||
*WinScrolled*
|
||||
WinScrolled After scrolling the content of a window or
|
||||
resizing a window in the current tab page.
|
||||
|
||||
When more than one window scrolled or resized
|
||||
only one WinScrolled event is triggered. You
|
||||
can use the `winlayout()` and `getwininfo()`
|
||||
functions to see what changed.
|
||||
WinScrolled After any window in the current tab page
|
||||
scrolled the text (horizontally or vertically)
|
||||
or changed width or height. See
|
||||
|win-scrolled-resized|.
|
||||
|
||||
The pattern is matched against the |window-ID|
|
||||
of the first window that scrolled or resized.
|
||||
Both <amatch> and <afile> are set to the
|
||||
|window-ID|.
|
||||
|
||||
|v:event| is set with information about size
|
||||
and scroll changes. |WinScrolled-event|
|
||||
|
||||
Only starts triggering after startup finished
|
||||
and the first screen redraw was done.
|
||||
Does not trigger when defining the first
|
||||
WinScrolled or WinResized event, but may
|
||||
trigger when adding more.
|
||||
|
||||
Non-recursive: the event will not trigger
|
||||
while executing commands for the WinScrolled
|
||||
@@ -1393,11 +1396,17 @@ WinScrolled After scrolling the content of a window or
|
||||
window to scroll or change size, then another
|
||||
WinScrolled event will be triggered later.
|
||||
|
||||
Does not trigger when the command is added,
|
||||
only after the first scroll or resize.
|
||||
*E1312*
|
||||
It is not allowed to change the window layout
|
||||
here (split, close or move windows).
|
||||
|
||||
*WinResized*
|
||||
WinResized After a window in the current tab page changed
|
||||
width or height.
|
||||
See |win-scrolled-resized|.
|
||||
|
||||
|v:event| is set with information about size
|
||||
changes. |WinResized-event|
|
||||
|
||||
Same behavior as |WinScrolled| for the
|
||||
pattern, triggering and recursiveness.
|
||||
|
||||
==============================================================================
|
||||
6. Patterns *autocmd-patterns* *{aupat}*
|
||||
|
@@ -631,6 +631,54 @@ it).
|
||||
The minimal height and width of a window is set with 'winminheight' and
|
||||
'winminwidth'. These are hard values, a window will never become smaller.
|
||||
|
||||
|
||||
WinScrolled and WinResized autocommands ~
|
||||
*win-scrolled-resized*
|
||||
If you want to get notified of changes in window sizes, the |WinResized|
|
||||
autocommand event can be used.
|
||||
If you want to get notified of text in windows scrolling vertically or
|
||||
horizontally, the |WinScrolled| autocommand event can be used. This will also
|
||||
trigger in window size changes.
|
||||
*WinResized-event*
|
||||
The |WinResized| event is triggered after updating the display, several
|
||||
windows may have changed size then. A list of the IDs of windows that changed
|
||||
since last time is provided in the v:event.windows variable, for example:
|
||||
[1003, 1006]
|
||||
*WinScrolled-event*
|
||||
The |WinScrolled| event is triggered after |WinResized|, and also if a window
|
||||
was scrolled. That can be vertically (the text at the top of the window
|
||||
changed) or horizontally (when 'wrap' is off or when the first displayed part
|
||||
of the first line changes). Note that |WinScrolled| will trigger many more
|
||||
times than |WinResized|, it may slow down editing a bit.
|
||||
|
||||
The information provided by |WinScrolled| is a dictionary for each window that
|
||||
has changes, using the window ID as the key, and a total count of the changes
|
||||
with the key "all". Example value for |v:event| (|Vim9| syntax):
|
||||
{
|
||||
all: {width: 0, height: 2, leftcol: 0, topline: 1, skipcol: 0},
|
||||
1003: {width: 0, height: -1, leftcol: 0, topline: 0, skipcol: 0},
|
||||
1006: {width: 0, height: 1, leftcol: 0, topline: 1, skipcol: 0},
|
||||
}
|
||||
|
||||
Note that the "all" entry has the absolute values of the individual windows
|
||||
accumulated.
|
||||
|
||||
If you need more information about what changed, or you want to "debounce" the
|
||||
events (not handle every event to avoid doing too much work), you may want to
|
||||
use the `winlayout()` and `getwininfo()` functions.
|
||||
|
||||
|WinScrolled| and |WinResized| do not trigger when the first autocommand is
|
||||
added, only after the first scroll or resize. They may trigger when switching
|
||||
to another tab page.
|
||||
|
||||
The commands executed are expected to not cause window size or scroll changes.
|
||||
If this happens anyway, the event will trigger again very soon. In other
|
||||
words: Just before triggering the event, the current sizes and scroll
|
||||
positions are stored and used to decide whether there was a change.
|
||||
*E1312*
|
||||
It is not allowed to change the window layout here (split, close or move
|
||||
windows).
|
||||
|
||||
==============================================================================
|
||||
7. Argument and buffer list commands *buffer-list*
|
||||
|
||||
|
@@ -191,6 +191,7 @@ static struct event_name
|
||||
{"WinClosed", EVENT_WINCLOSED},
|
||||
{"WinEnter", EVENT_WINENTER},
|
||||
{"WinLeave", EVENT_WINLEAVE},
|
||||
{"WinResized", EVENT_WINRESIZED},
|
||||
{"WinScrolled", EVENT_WINSCROLLED},
|
||||
{"VimResized", EVENT_VIMRESIZED},
|
||||
{"TextYankPost", EVENT_TEXTYANKPOST},
|
||||
@@ -1263,10 +1264,11 @@ do_autocmd_event(
|
||||
if (event == EVENT_MODECHANGED && !has_modechanged())
|
||||
get_mode(last_mode);
|
||||
#endif
|
||||
// Initialize the fields checked by the WinScrolled trigger to
|
||||
// prevent it from firing right after the first autocmd is
|
||||
// defined.
|
||||
if (event == EVENT_WINSCROLLED && !has_winscrolled())
|
||||
// Initialize the fields checked by the WinScrolled and
|
||||
// WinResized trigger to prevent them from firing right after
|
||||
// the first autocmd is defined.
|
||||
if ((event == EVENT_WINSCROLLED || event == EVENT_WINRESIZED)
|
||||
&& !(has_winscrolled() || has_winresized()))
|
||||
{
|
||||
tabpage_T *save_curtab = curtab;
|
||||
tabpage_T *tp;
|
||||
@@ -1810,6 +1812,15 @@ trigger_cursorhold(void)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return TRUE when there is a WinResized autocommand defined.
|
||||
*/
|
||||
int
|
||||
has_winresized(void)
|
||||
{
|
||||
return (first_autopat[(int)EVENT_WINRESIZED] != NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return TRUE when there is a WinScrolled autocommand defined.
|
||||
*/
|
||||
@@ -2117,6 +2128,7 @@ apply_autocmds_group(
|
||||
|| event == EVENT_MENUPOPUP
|
||||
|| event == EVENT_USER
|
||||
|| event == EVENT_WINCLOSED
|
||||
|| event == EVENT_WINRESIZED
|
||||
|| event == EVENT_WINSCROLLED)
|
||||
{
|
||||
fname = vim_strsave(fname);
|
||||
|
21
src/dict.c
21
src/dict.c
@@ -1082,13 +1082,14 @@ failret:
|
||||
* Go over all entries in "d2" and add them to "d1".
|
||||
* When "action" is "error" then a duplicate key is an error.
|
||||
* When "action" is "force" then a duplicate key is overwritten.
|
||||
* When "action" is "move" then move items instead of copying.
|
||||
* Otherwise duplicate keys are ignored ("action" is "keep").
|
||||
* "func_name" is used for reporting where an error occurred.
|
||||
*/
|
||||
void
|
||||
dict_extend(dict_T *d1, dict_T *d2, char_u *action, char *func_name)
|
||||
{
|
||||
dictitem_T *di1;
|
||||
hashitem_T *hi2;
|
||||
int todo;
|
||||
char_u *arg_errmsg = (char_u *)N_("extend() argument");
|
||||
type_T *type;
|
||||
@@ -1098,8 +1099,11 @@ dict_extend(dict_T *d1, dict_T *d2, char_u *action, char *func_name)
|
||||
else
|
||||
type = NULL;
|
||||
|
||||
if (*action == 'm')
|
||||
hash_lock(&d2->dv_hashtab); // don't rehash on hash_remove()
|
||||
|
||||
todo = (int)d2->dv_hashtab.ht_used;
|
||||
for (hi2 = d2->dv_hashtab.ht_array; todo > 0; ++hi2)
|
||||
for (hashitem_T *hi2 = d2->dv_hashtab.ht_array; todo > 0; ++hi2)
|
||||
{
|
||||
if (!HASHITEM_EMPTY(hi2))
|
||||
{
|
||||
@@ -1115,11 +1119,21 @@ dict_extend(dict_T *d1, dict_T *d2, char_u *action, char *func_name)
|
||||
break;
|
||||
|
||||
if (di1 == NULL)
|
||||
{
|
||||
if (*action == 'm')
|
||||
{
|
||||
// cheap way to move a dict item from "d2" to "d1"
|
||||
di1 = HI2DI(hi2);
|
||||
dict_add(d1, di1);
|
||||
hash_remove(&d2->dv_hashtab, hi2);
|
||||
}
|
||||
else
|
||||
{
|
||||
di1 = dictitem_copy(HI2DI(hi2));
|
||||
if (di1 != NULL && dict_add(d1, di1) == FAIL)
|
||||
dictitem_free(di1);
|
||||
}
|
||||
}
|
||||
else if (*action == 'e')
|
||||
{
|
||||
semsg(_(e_key_already_exists_str), hi2->hi_key);
|
||||
@@ -1138,6 +1152,9 @@ dict_extend(dict_T *d1, dict_T *d2, char_u *action, char *func_name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (*action == 'm')
|
||||
hash_unlock(&d2->dv_hashtab);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@@ -1510,7 +1510,7 @@ ins_redraw(int ready) // not busy with something
|
||||
}
|
||||
|
||||
if (ready)
|
||||
may_trigger_winscrolled();
|
||||
may_trigger_win_scrolled_resized();
|
||||
|
||||
// Trigger SafeState if nothing is pending.
|
||||
may_trigger_safestate(ready
|
||||
|
@@ -5097,7 +5097,7 @@ gui_update_screen(void)
|
||||
}
|
||||
|
||||
if (!finish_op)
|
||||
may_trigger_winscrolled();
|
||||
may_trigger_win_scrolled_resized();
|
||||
|
||||
# ifdef FEAT_CONCEAL
|
||||
if (conceal_update_lines
|
||||
|
@@ -1358,7 +1358,7 @@ main_loop(
|
||||
validate_cursor();
|
||||
|
||||
if (!finish_op)
|
||||
may_trigger_winscrolled();
|
||||
may_trigger_win_scrolled_resized();
|
||||
|
||||
// If nothing is pending and we are going to wait for the user to
|
||||
// type a character, trigger SafeState.
|
||||
|
@@ -1171,7 +1171,7 @@ do_mousescroll(cmdarg_T *cap)
|
||||
leftcol = 0;
|
||||
do_mousescroll_horiz((long_u)leftcol);
|
||||
}
|
||||
may_trigger_winscrolled();
|
||||
may_trigger_win_scrolled_resized();
|
||||
}
|
||||
|
||||
/*
|
||||
|
@@ -16,6 +16,7 @@ int apply_autocmds(event_T event, char_u *fname, char_u *fname_io, int force, bu
|
||||
int apply_autocmds_exarg(event_T event, char_u *fname, char_u *fname_io, int force, buf_T *buf, exarg_T *eap);
|
||||
int apply_autocmds_retval(event_T event, char_u *fname, char_u *fname_io, int force, buf_T *buf, int *retval);
|
||||
int trigger_cursorhold(void);
|
||||
int has_winresized(void);
|
||||
int has_winscrolled(void);
|
||||
int has_cursormoved(void);
|
||||
int has_cursormovedI(void);
|
||||
|
@@ -20,7 +20,7 @@ int one_window(void);
|
||||
int win_close(win_T *win, int free_buf);
|
||||
void snapshot_windows_scroll_size(void);
|
||||
void may_make_initial_scroll_size_snapshot(void);
|
||||
void may_trigger_winscrolled(void);
|
||||
void may_trigger_win_scrolled_resized(void);
|
||||
void win_close_othertab(win_T *win, int free_buf, tabpage_T *tp);
|
||||
void win_free_all(void);
|
||||
win_T *winframe_remove(win_T *win, int *dirp, tabpage_T *tp);
|
||||
|
@@ -306,6 +306,61 @@ func Test_win_tab_autocmd()
|
||||
unlet g:record
|
||||
endfunc
|
||||
|
||||
func Test_WinResized()
|
||||
CheckRunVimInTerminal
|
||||
|
||||
let lines =<< trim END
|
||||
set scrolloff=0
|
||||
call setline(1, ['111', '222'])
|
||||
vnew
|
||||
call setline(1, ['aaa', 'bbb'])
|
||||
new
|
||||
call setline(1, ['foo', 'bar'])
|
||||
|
||||
let g:resized = 0
|
||||
au WinResized * let g:resized += 1
|
||||
|
||||
func WriteResizedEvent()
|
||||
call writefile([json_encode(v:event)], 'XresizeEvent')
|
||||
endfunc
|
||||
au WinResized * call WriteResizedEvent()
|
||||
END
|
||||
call writefile(lines, 'Xtest_winresized', 'D')
|
||||
let buf = RunVimInTerminal('-S Xtest_winresized', {'rows': 10})
|
||||
|
||||
" redraw now to avoid a redraw after the :echo command
|
||||
call term_sendkeys(buf, ":redraw!\<CR>")
|
||||
call TermWait(buf)
|
||||
|
||||
call term_sendkeys(buf, ":echo g:resized\<CR>")
|
||||
call WaitForAssert({-> assert_match('^0$', term_getline(buf, 10))}, 1000)
|
||||
|
||||
" increase window height, two windows will be reported
|
||||
call term_sendkeys(buf, "\<C-W>+")
|
||||
call TermWait(buf)
|
||||
call term_sendkeys(buf, ":echo g:resized\<CR>")
|
||||
call WaitForAssert({-> assert_match('^1$', term_getline(buf, 10))}, 1000)
|
||||
|
||||
let event = readfile('XresizeEvent')[0]->json_decode()
|
||||
call assert_equal({
|
||||
\ 'windows': [1002, 1001],
|
||||
\ }, event)
|
||||
|
||||
" increase window width, three windows will be reported
|
||||
call term_sendkeys(buf, "\<C-W>>")
|
||||
call TermWait(buf)
|
||||
call term_sendkeys(buf, ":echo g:resized\<CR>")
|
||||
call WaitForAssert({-> assert_match('^2$', term_getline(buf, 10))}, 1000)
|
||||
|
||||
let event = readfile('XresizeEvent')[0]->json_decode()
|
||||
call assert_equal({
|
||||
\ 'windows': [1002, 1001, 1000],
|
||||
\ }, event)
|
||||
|
||||
call delete('XresizeEvent')
|
||||
call StopVimInTerminal(buf)
|
||||
endfunc
|
||||
|
||||
func Test_WinScrolled()
|
||||
CheckRunVimInTerminal
|
||||
|
||||
@@ -316,11 +371,15 @@ func Test_WinScrolled()
|
||||
endfor
|
||||
let win_id = win_getid()
|
||||
let g:matched = v:false
|
||||
func WriteScrollEvent()
|
||||
call writefile([json_encode(v:event)], 'XscrollEvent')
|
||||
endfunc
|
||||
execute 'au WinScrolled' win_id 'let g:matched = v:true'
|
||||
let g:scrolled = 0
|
||||
au WinScrolled * let g:scrolled += 1
|
||||
au WinScrolled * let g:amatch = str2nr(expand('<amatch>'))
|
||||
au WinScrolled * let g:afile = str2nr(expand('<afile>'))
|
||||
au WinScrolled * call WriteScrollEvent()
|
||||
END
|
||||
call writefile(lines, 'Xtest_winscrolled', 'D')
|
||||
let buf = RunVimInTerminal('-S Xtest_winscrolled', {'rows': 6})
|
||||
@@ -332,15 +391,33 @@ func Test_WinScrolled()
|
||||
call term_sendkeys(buf, "zlzh:echo g:scrolled\<CR>")
|
||||
call WaitForAssert({-> assert_match('^2 ', term_getline(buf, 6))}, 1000)
|
||||
|
||||
let event = readfile('XscrollEvent')[0]->json_decode()
|
||||
call assert_equal({
|
||||
\ 'all': {'leftcol': 1, 'topline': 0, 'width': 0, 'height': 0, 'skipcol': 0},
|
||||
\ '1000': {'leftcol': -1, 'topline': 0, 'width': 0, 'height': 0, 'skipcol': 0}
|
||||
\ }, event)
|
||||
|
||||
" Scroll up/down in Normal mode.
|
||||
call term_sendkeys(buf, "\<c-e>\<c-y>:echo g:scrolled\<CR>")
|
||||
call WaitForAssert({-> assert_match('^4 ', term_getline(buf, 6))}, 1000)
|
||||
|
||||
let event = readfile('XscrollEvent')[0]->json_decode()
|
||||
call assert_equal({
|
||||
\ 'all': {'leftcol': 0, 'topline': 1, 'width': 0, 'height': 0, 'skipcol': 0},
|
||||
\ '1000': {'leftcol': 0, 'topline': -1, 'width': 0, 'height': 0, 'skipcol': 0}
|
||||
\ }, event)
|
||||
|
||||
" Scroll up/down in Insert mode.
|
||||
call term_sendkeys(buf, "Mi\<c-x>\<c-e>\<Esc>i\<c-x>\<c-y>\<Esc>")
|
||||
call term_sendkeys(buf, ":echo g:scrolled\<CR>")
|
||||
call WaitForAssert({-> assert_match('^6 ', term_getline(buf, 6))}, 1000)
|
||||
|
||||
let event = readfile('XscrollEvent')[0]->json_decode()
|
||||
call assert_equal({
|
||||
\ 'all': {'leftcol': 0, 'topline': 1, 'width': 0, 'height': 0, 'skipcol': 0},
|
||||
\ '1000': {'leftcol': 0, 'topline': -1, 'width': 0, 'height': 0, 'skipcol': 0}
|
||||
\ }, event)
|
||||
|
||||
" Scroll the window horizontally to focus the last letter of the third line
|
||||
" containing only six characters. Moving to the previous and shorter lines
|
||||
" should trigger another autocommand as Vim has to make them visible.
|
||||
@@ -348,6 +425,12 @@ func Test_WinScrolled()
|
||||
call term_sendkeys(buf, ":echo g:scrolled\<CR>")
|
||||
call WaitForAssert({-> assert_match('^8 ', term_getline(buf, 6))}, 1000)
|
||||
|
||||
let event = readfile('XscrollEvent')[0]->json_decode()
|
||||
call assert_equal({
|
||||
\ 'all': {'leftcol': 5, 'topline': 0, 'width': 0, 'height': 0, 'skipcol': 0},
|
||||
\ '1000': {'leftcol': -5, 'topline': 0, 'width': 0, 'height': 0, 'skipcol': 0}
|
||||
\ }, event)
|
||||
|
||||
" Ensure the command was triggered for the specified window ID.
|
||||
call term_sendkeys(buf, ":echo g:matched\<CR>")
|
||||
call WaitForAssert({-> assert_match('^v:true ', term_getline(buf, 6))}, 1000)
|
||||
@@ -356,6 +439,7 @@ func Test_WinScrolled()
|
||||
call term_sendkeys(buf, ":echo g:amatch == win_id && g:afile == win_id\<CR>")
|
||||
call WaitForAssert({-> assert_match('^v:true ', term_getline(buf, 6))}, 1000)
|
||||
|
||||
call delete('XscrollEvent')
|
||||
call StopVimInTerminal(buf)
|
||||
endfunc
|
||||
|
||||
|
@@ -695,6 +695,8 @@ static char *(features[]) =
|
||||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
917,
|
||||
/**/
|
||||
916,
|
||||
/**/
|
||||
|
@@ -1407,7 +1407,8 @@ enum auto_event
|
||||
EVENT_WINCLOSED, // after closing a window
|
||||
EVENT_VIMSUSPEND, // before Vim is suspended
|
||||
EVENT_VIMRESUME, // after Vim is resumed
|
||||
EVENT_WINSCROLLED, // after Vim window was scrolled
|
||||
EVENT_WINRESIZED, // after a window was resized
|
||||
EVENT_WINSCROLLED, // after a window was scrolled or resized
|
||||
|
||||
NUM_EVENTS // MUST be the last one
|
||||
};
|
||||
|
273
src/window.c
273
src/window.c
@@ -2873,46 +2873,273 @@ may_make_initial_scroll_size_snapshot(void)
|
||||
}
|
||||
|
||||
/*
|
||||
* Trigger WinScrolled if any window scrolled or changed size.
|
||||
* Create a dictionary with information about size and scroll changes in a
|
||||
* window.
|
||||
* Returns the dictionary with refcount set to one.
|
||||
* Returns NULL when out of memory.
|
||||
*/
|
||||
void
|
||||
may_trigger_winscrolled(void)
|
||||
static dict_T *
|
||||
make_win_info_dict(
|
||||
int width,
|
||||
int height,
|
||||
int topline,
|
||||
int leftcol,
|
||||
int skipcol)
|
||||
{
|
||||
static int recursive = FALSE;
|
||||
dict_T *d = dict_alloc();
|
||||
if (d == NULL)
|
||||
return NULL;
|
||||
d->dv_refcount = 1;
|
||||
|
||||
if (recursive
|
||||
|| !has_winscrolled()
|
||||
|| !did_initial_scroll_size_snapshot)
|
||||
return;
|
||||
// not actually looping, for breaking out on error
|
||||
while (1)
|
||||
{
|
||||
typval_T tv;
|
||||
tv.v_lock = 0;
|
||||
tv.v_type = VAR_NUMBER;
|
||||
|
||||
tv.vval.v_number = width;
|
||||
if (dict_add_tv(d, "width", &tv) == FAIL)
|
||||
break;
|
||||
tv.vval.v_number = height;
|
||||
if (dict_add_tv(d, "height", &tv) == FAIL)
|
||||
break;
|
||||
tv.vval.v_number = topline;
|
||||
if (dict_add_tv(d, "topline", &tv) == FAIL)
|
||||
break;
|
||||
tv.vval.v_number = leftcol;
|
||||
if (dict_add_tv(d, "leftcol", &tv) == FAIL)
|
||||
break;
|
||||
tv.vval.v_number = skipcol;
|
||||
if (dict_add_tv(d, "skipcol", &tv) == FAIL)
|
||||
break;
|
||||
return d;
|
||||
}
|
||||
dict_unref(d);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Return values of check_window_scroll_resize():
|
||||
#define CWSR_SCROLLED 1 // at least one window scrolled
|
||||
#define CWSR_RESIZED 2 // at least one window size changed
|
||||
|
||||
/*
|
||||
* This function is used for three purposes:
|
||||
* 1. Goes over all windows in the current tab page and returns:
|
||||
* 0 no scrolling and no size changes found
|
||||
* CWSR_SCROLLED at least one window scrolled
|
||||
* CWSR_RESIZED at least one window changed size
|
||||
* CWSR_SCROLLED + CWSR_RESIZED both
|
||||
* "size_count" is set to the nr of windows with size changes.
|
||||
* "first_scroll_win" is set to the first window with any relevant changes.
|
||||
* "first_size_win" is set to the first window with size changes.
|
||||
*
|
||||
* 2. When the first three arguments are NULL and "winlist" is not NULL,
|
||||
* "winlist" is set to the list of window IDs with size changes.
|
||||
*
|
||||
* 3. When the first three arguments are NULL and "v_event" is not NULL,
|
||||
* information about changed windows is added to "v_event".
|
||||
*/
|
||||
static int
|
||||
check_window_scroll_resize(
|
||||
int *size_count,
|
||||
win_T **first_scroll_win,
|
||||
win_T **first_size_win,
|
||||
list_T *winlist,
|
||||
dict_T *v_event)
|
||||
{
|
||||
int result = 0;
|
||||
int listidx = 0;
|
||||
int tot_width = 0;
|
||||
int tot_height = 0;
|
||||
int tot_topline = 0;
|
||||
int tot_leftcol = 0;
|
||||
int tot_skipcol = 0;
|
||||
|
||||
win_T *wp;
|
||||
FOR_ALL_WINDOWS(wp)
|
||||
if (wp->w_last_topline != wp->w_topline
|
||||
|| wp->w_last_leftcol != wp->w_leftcol
|
||||
|| wp->w_last_skipcol != wp->w_skipcol
|
||||
|| wp->w_last_width != wp->w_width
|
||||
|| wp->w_last_height != wp->w_height)
|
||||
{
|
||||
// WinScrolled is triggered only once, even when multiple windows
|
||||
// scrolled or changed size. Store the current values before
|
||||
// triggering the event, if a scroll or resize happens as a side
|
||||
// effect then WinScrolled is triggered again later.
|
||||
int size_changed = wp->w_last_width != wp->w_width
|
||||
|| wp->w_last_height != wp->w_height;
|
||||
if (size_changed)
|
||||
{
|
||||
result |= CWSR_RESIZED;
|
||||
if (winlist != NULL)
|
||||
{
|
||||
// Add this window to the list of changed windows.
|
||||
typval_T tv;
|
||||
tv.v_lock = 0;
|
||||
tv.v_type = VAR_NUMBER;
|
||||
tv.vval.v_number = wp->w_id;
|
||||
list_set_item(winlist, listidx++, &tv);
|
||||
}
|
||||
else if (size_count != NULL)
|
||||
{
|
||||
++*size_count;
|
||||
if (*first_size_win == NULL)
|
||||
*first_size_win = wp;
|
||||
// For WinScrolled the first window with a size change is used
|
||||
// even when it didn't scroll.
|
||||
if (*first_scroll_win == NULL)
|
||||
*first_scroll_win = wp;
|
||||
}
|
||||
}
|
||||
|
||||
int scroll_changed = wp->w_last_topline != wp->w_topline
|
||||
|| wp->w_last_leftcol != wp->w_leftcol
|
||||
|| wp->w_last_skipcol != wp->w_skipcol;
|
||||
if (scroll_changed)
|
||||
{
|
||||
result |= CWSR_SCROLLED;
|
||||
if (first_scroll_win != NULL && *first_scroll_win == NULL)
|
||||
*first_scroll_win = wp;
|
||||
}
|
||||
|
||||
if ((size_changed || scroll_changed) && v_event != NULL)
|
||||
{
|
||||
// Add info about this window to the v:event dictionary.
|
||||
int width = wp->w_width - wp->w_last_width;
|
||||
int height = wp->w_height - wp->w_last_height;
|
||||
int topline = wp->w_topline - wp->w_last_topline;
|
||||
int leftcol = wp->w_leftcol - wp->w_last_leftcol;
|
||||
int skipcol = wp->w_skipcol - wp->w_last_skipcol;
|
||||
dict_T *d = make_win_info_dict(width, height,
|
||||
topline, leftcol, skipcol);
|
||||
if (d == NULL)
|
||||
break;
|
||||
char winid[NUMBUFLEN];
|
||||
vim_snprintf(winid, sizeof(winid), "%d", wp->w_id);
|
||||
if (dict_add_dict(v_event, winid, d) == FAIL)
|
||||
{
|
||||
dict_unref(d);
|
||||
break;
|
||||
}
|
||||
--d->dv_refcount;
|
||||
|
||||
tot_width += abs(width);
|
||||
tot_height += abs(height);
|
||||
tot_topline += abs(topline);
|
||||
tot_leftcol += abs(leftcol);
|
||||
tot_skipcol += abs(skipcol);
|
||||
}
|
||||
}
|
||||
|
||||
if (v_event != NULL)
|
||||
{
|
||||
dict_T *alldict = make_win_info_dict(tot_width, tot_height,
|
||||
tot_topline, tot_leftcol, tot_skipcol);
|
||||
if (alldict != NULL)
|
||||
{
|
||||
if (dict_add_dict(v_event, "all", alldict) == FAIL)
|
||||
dict_unref(alldict);
|
||||
else
|
||||
--alldict->dv_refcount;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Trigger WinScrolled and/or WinResized if any window in the current tab page
|
||||
* scrolled or changed size.
|
||||
*/
|
||||
void
|
||||
may_trigger_win_scrolled_resized(void)
|
||||
{
|
||||
static int recursive = FALSE;
|
||||
int do_resize = has_winresized();
|
||||
int do_scroll = has_winscrolled();
|
||||
|
||||
// Do not trigger WinScrolled or WinResized recursively. Do not trigger
|
||||
// before the initial snapshot of the w_last_ values was made.
|
||||
if (recursive
|
||||
|| !(do_scroll || do_resize)
|
||||
|| !did_initial_scroll_size_snapshot)
|
||||
return;
|
||||
|
||||
int size_count = 0;
|
||||
win_T *first_scroll_win = NULL, *first_size_win = NULL;
|
||||
int cwsr = check_window_scroll_resize(&size_count,
|
||||
&first_scroll_win, &first_size_win,
|
||||
NULL, NULL);
|
||||
int trigger_resize = do_resize && size_count > 0;
|
||||
int trigger_scroll = do_scroll && cwsr != 0;
|
||||
if (!trigger_resize && !trigger_scroll)
|
||||
return; // no relevant changes
|
||||
|
||||
list_T *windows_list = NULL;
|
||||
if (trigger_resize)
|
||||
{
|
||||
// Create the list for v:event.windows before making the snapshot.
|
||||
windows_list = list_alloc_with_items(size_count);
|
||||
(void)check_window_scroll_resize(NULL, NULL, NULL, windows_list, NULL);
|
||||
}
|
||||
|
||||
dict_T *scroll_dict = NULL;
|
||||
if (trigger_scroll)
|
||||
{
|
||||
// Create the dict with entries for v:event before making the snapshot.
|
||||
scroll_dict = dict_alloc();
|
||||
if (scroll_dict != NULL)
|
||||
{
|
||||
scroll_dict->dv_refcount = 1;
|
||||
(void)check_window_scroll_resize(NULL, NULL, NULL, NULL,
|
||||
scroll_dict);
|
||||
}
|
||||
}
|
||||
|
||||
// WinScrolled/WinResized are triggered only once, even when multiple
|
||||
// windows scrolled or changed size. Store the current values before
|
||||
// triggering the event, if a scroll or resize happens as a side effect
|
||||
// then WinScrolled/WinResized is triggered for that later.
|
||||
snapshot_windows_scroll_size();
|
||||
|
||||
// "curwin" may be different from the actual current window, make
|
||||
// sure it can be restored.
|
||||
window_layout_lock();
|
||||
|
||||
recursive = TRUE;
|
||||
|
||||
// If both are to be triggered do WinResized first.
|
||||
if (trigger_resize)
|
||||
{
|
||||
save_v_event_T save_v_event;
|
||||
dict_T *v_event = get_v_event(&save_v_event);
|
||||
|
||||
if (dict_add_list(v_event, "windows", windows_list) == OK)
|
||||
{
|
||||
dict_set_items_ro(v_event);
|
||||
|
||||
char_u winid[NUMBUFLEN];
|
||||
vim_snprintf((char *)winid, sizeof(winid), "%d", wp->w_id);
|
||||
vim_snprintf((char *)winid, sizeof(winid), "%d",
|
||||
first_size_win->w_id);
|
||||
apply_autocmds(EVENT_WINRESIZED, winid, winid, FALSE,
|
||||
first_size_win->w_buffer);
|
||||
}
|
||||
restore_v_event(v_event, &save_v_event);
|
||||
}
|
||||
|
||||
if (trigger_scroll)
|
||||
{
|
||||
save_v_event_T save_v_event;
|
||||
dict_T *v_event = get_v_event(&save_v_event);
|
||||
|
||||
// Move the entries from scroll_dict to v_event.
|
||||
dict_extend(v_event, scroll_dict, (char_u *)"move", NULL);
|
||||
dict_set_items_ro(v_event);
|
||||
dict_unref(scroll_dict);
|
||||
|
||||
char_u winid[NUMBUFLEN];
|
||||
vim_snprintf((char *)winid, sizeof(winid), "%d",
|
||||
first_scroll_win->w_id);
|
||||
apply_autocmds(EVENT_WINSCROLLED, winid, winid, FALSE,
|
||||
wp->w_buffer);
|
||||
first_scroll_win->w_buffer);
|
||||
|
||||
restore_v_event(v_event, &save_v_event);
|
||||
}
|
||||
|
||||
recursive = FALSE;
|
||||
window_layout_unlock();
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
Reference in New Issue
Block a user