0
0
mirror of https://github.com/vim/vim.git synced 2025-09-29 04:34:16 -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:
Bram Moolenaar
2022-11-22 12:40:50 +00:00
parent ce30ccc06a
commit 35fc61cb5b
14 changed files with 461 additions and 60 deletions

View File

@@ -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)
{
int size_changed = wp->w_last_width != wp->w_width
|| wp->w_last_height != wp->w_height;
if (size_changed)
{
// 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.
snapshot_windows_scroll_size();
// "curwin" may be different from the actual current window, make
// sure it can be restored.
window_layout_lock();
recursive = TRUE;
char_u winid[NUMBUFLEN];
vim_snprintf((char *)winid, sizeof(winid), "%d", wp->w_id);
apply_autocmds(EVENT_WINSCROLLED, winid, winid, FALSE,
wp->w_buffer);
recursive = FALSE;
window_layout_unlock();
break;
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",
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,
first_scroll_win->w_buffer);
restore_v_event(v_event, &save_v_event);
}
recursive = FALSE;
window_layout_unlock();
}
/*