From 9eaac896501bcd6abdd430a90293eae8101df24a Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 1 Jun 2019 22:49:29 +0200 Subject: patch 8.1.1446: popup window callback not implemented yet Problem: Popup window callback not implemented yet. Solution: Implement the callback. --- runtime/doc/popup.txt | 20 ++++++++++++----- src/evalfunc.c | 2 +- src/popupwin.c | 51 ++++++++++++++++++++++++++++++++++++++++++- src/structs.h | 1 + src/testdir/test_popupwin.vim | 10 +++++++++ src/version.c | 2 ++ src/window.c | 1 + 7 files changed, 79 insertions(+), 8 deletions(-) diff --git a/runtime/doc/popup.txt b/runtime/doc/popup.txt index ac16b221c4..c8cca5b905 100644 --- a/runtime/doc/popup.txt +++ b/runtime/doc/popup.txt @@ -90,6 +90,7 @@ Probably 2. is the best choice. IMPLEMENTATION: - Code is in popupwin.c +- Fix positioning with border and padding. - Why does 'nrformats' leak from the popup window buffer??? - Make redrawing more efficient and avoid flicker. Store popup info in a mask, use the mask in screen_line() @@ -133,10 +134,15 @@ popup_create({text}, {options}) *popup_create()* < In case of failure zero is returned. -popup_close({id}) *popup_close()* +popup_close({id} [, {result}]) *popup_close()* Close popup {id}. The window and the associated buffer will be deleted. + If the popup has a callback it will be called just before the + popup window is deleted. If the optional {result} is present + it will be passed as the second argument of the callback. + Otherwise zero is passed to the callback. + popup_dialog({text}, {options}) *popup_dialog()* {not implemented yet} @@ -145,6 +151,7 @@ popup_dialog({text}, {options}) *popup_dialog()* \ 'pos': 'center', \ 'zindex': 200, \ 'border': [], + \ 'padding': [], \}) < Use {options} to change the properties. @@ -166,6 +173,7 @@ popup_notification({text}, {options}) *popup_notification()* popup_atcursor({text}, {options}) *popup_atcursor()* + {not implemented yet: close when cursor moves} Show the {text} above the cursor, and close it when the cursor moves. This works like: > call popup_create({text}, { @@ -394,7 +402,6 @@ The second argument of |popup_create()| is a dictionary with options: |popup-filter| callback a callback to be used when the popup closes, e.g. when using |popup_filter_menu()|, see |popup-callback|. - {not implemented yet} Depending on the "zindex" the popup goes under or above other popups. The completion menu (|popup-menu|) has zindex 100. For messages that occur for a @@ -477,11 +484,12 @@ Vim recognizes the Esc key. If you do use Esc, it is reecommended to set the POPUP CALLBACK *popup-callback* -{not implemented yet} A callback that is invoked when the popup closes. Used by -|popup_filter_menu()|. Invoked with two arguments: the ID of the popup and -the result, which would usually be an index in the popup lines, or whatever -the filter wants to pass. +|popup_filter_menu()|. + +The callback is invoked with two arguments: the ID of the popup window and the +result, which could be an index in the popup lines, or whatever was passed as +the second argument of `popup_close()`. ============================================================================== 3. Examples *popup-examples* diff --git a/src/evalfunc.c b/src/evalfunc.c index ccb8b3e890..bc60567859 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -810,7 +810,7 @@ static struct fst #endif #ifdef FEAT_TEXT_PROP {"popup_atcursor", 2, 2, f_popup_atcursor}, - {"popup_close", 1, 1, f_popup_close}, + {"popup_close", 1, 2, f_popup_close}, {"popup_create", 2, 2, f_popup_create}, {"popup_getoptions", 1, 1, f_popup_getoptions}, {"popup_getpos", 1, 1, f_popup_getpos}, diff --git a/src/popupwin.c b/src/popupwin.c index 810987aace..dc3b56468b 100644 --- a/src/popupwin.c +++ b/src/popupwin.c @@ -201,6 +201,15 @@ apply_options(win_T *wp, buf_T *buf UNUSED, dict_T *dict, int atcursor) wp->w_p_wrap = nr != 0; } + di = dict_find(dict, (char_u *)"callback", -1); + if (di != NULL) + { + callback_T callback = get_callback(&di->di_tv); + + if (callback.cb_name != NULL) + set_callback(&wp->w_close_cb, &callback); + } + di = dict_find(dict, (char_u *)"filter", -1); if (di != NULL) { @@ -631,6 +640,37 @@ popup_any_visible(void) return FALSE; } +/* + * Invoke the close callback for window "wp" with value "result". + * Careful: The callback may make "wp" invalid! + */ + static void +invoke_popup_callback(win_T *wp, typval_T *result) +{ + typval_T rettv; + int dummy; + typval_T argv[3]; + + argv[0].v_type = VAR_NUMBER; + argv[0].vval.v_number = (varnumber_T)wp->w_id; + + if (result != NULL && result->v_type != VAR_UNKNOWN) + copy_tv(result, &argv[1]); + else + { + argv[1].v_type = VAR_NUMBER; + argv[1].vval.v_number = 0; + } + + argv[2].v_type = VAR_UNKNOWN; + + call_callback(&wp->w_close_cb, -1, + &rettv, 2, argv, NULL, 0L, 0L, &dummy, TRUE, NULL); + if (result != NULL) + clear_tv(&argv[1]); + clear_tv(&rettv); +} + /* * popup_close({id}) */ @@ -638,8 +678,16 @@ popup_any_visible(void) f_popup_close(typval_T *argvars, typval_T *rettv UNUSED) { int id = (int)tv_get_number(argvars); + win_T *wp = find_popup_win(id); + + if (wp != NULL) + { + if (wp->w_close_cb.cb_name != NULL) + // Careful: This may make "wp" invalid. + invoke_popup_callback(wp, &argvars[1]); - popup_close(id); + popup_close(id); + } } /* @@ -688,6 +736,7 @@ popup_free(win_T *wp) /* * Close a popup window by Window-id. + * Does not invoke the callback. */ void popup_close(int id) diff --git a/src/structs.h b/src/structs.h index f6db26465b..2100135ac6 100644 --- a/src/structs.h +++ b/src/structs.h @@ -2894,6 +2894,7 @@ struct window_S int w_border_char[8]; // popup border characters varnumber_T w_popup_last_changedtick; // b:changedtick when position was // computed + callback_T w_close_cb; // popup close callback callback_T w_filter_cb; // popup filter callback # if defined(FEAT_TIMERS) timer_T *w_popup_timer; // timer for closing popup window diff --git a/src/testdir/test_popupwin.vim b/src/testdir/test_popupwin.vim index 6a2ef24a47..6e059193bf 100644 --- a/src/testdir/test_popupwin.vim +++ b/src/testdir/test_popupwin.vim @@ -586,3 +586,13 @@ func Test_popup_filter() delfunc MyPopupFilter popupclear endfunc + +func Test_popup_close_callback() + func PopupDone(id, result) + let g:result = a:result + endfunc + let winid = popup_create('something', {'callback': 'PopupDone'}) + redraw + call popup_close(winid, 'done') + call assert_equal('done', g:result) +endfunc diff --git a/src/version.c b/src/version.c index 3be4e657ab..597fb90fe7 100644 --- a/src/version.c +++ b/src/version.c @@ -767,6 +767,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1446, /**/ 1445, /**/ diff --git a/src/window.c b/src/window.c index e15b31191f..f4ef983c6d 100644 --- a/src/window.c +++ b/src/window.c @@ -4845,6 +4845,7 @@ win_free( remove_winbar(wp); #endif #ifdef FEAT_TEXT_PROP + free_callback(&wp->w_close_cb); free_callback(&wp->w_filter_cb); for (i = 0; i < 4; ++i) VIM_CLEAR(wp->w_border_highlight[i]); -- cgit v1.2.3