Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 3fcecb2

Browse files
committed
floats: implement floating windows
Co-Author: Dongdong Zhou <[email protected]>
1 parent 018e0d5 commit 3fcecb2

30 files changed

+3917
-217
lines changed

runtime/doc/ui.txt

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -520,19 +520,33 @@ tabs.
520520
size). If the window was previously hidden, it should now be shown
521521
again.
522522

523+
["win_float_pos", grid, win, anchor, anchor_grid, anchor_row, anchor_col, focusable]
524+
Display or reconfigure floating window `win`. The window should be
525+
displayed above another grid `anchor_grid` at the specified position
526+
`anchor_row` and `anchor_col`. For the meaning of `anchor` and more
527+
details of positioning, see |nvim_open_win()|.
528+
529+
["win_external_pos", grid, win]
530+
Display or reconfigure external window `win`. The window should be
531+
displayed as a separate top-level window in the desktop envirionment,
532+
or something similar.
533+
523534
["win_hide", grid]
524-
Stop displaying the window.
535+
Stop displaying the window. The window can be shown again later.
525536

526537
["win_scroll_over_start"]
527538
Hint that following `grid_scroll` on the default grid should
528539
scroll over windows. This is a temporary workaround to allow
529540
UIs to use the builtin message drawing. Later on, messages will be
530-
drawn on a dedicated grid.
541+
drawn on a dedicated grid. Using |ui-messages| also avoids this issue.
531542

532543
["win_scroll_over_reset"]
533544
Hint that scrolled over windows should be redrawn again, and not be
534545
overdrawn by default grid scrolling anymore.
535546

547+
["win_close", grid]
548+
Close the window.
549+
536550
See |ui-linegrid| for grid events.
537551
See |nvim_ui_try_resize_grid| in |api-ui| to request changing the grid size.
538552
See |nvim_input_mouse| for sending mouse events to Nvim.

runtime/doc/windows.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,10 @@ CTRL-W ^ Does ":split #", split window in two and edit alternate file.
197197
When a count is given, it becomes ":split #N", split window
198198
and edit buffer N.
199199

200+
CTRL-W ge *CTRL-W_ge*
201+
Detach the current window as an external window.
202+
Only available when using an UI with |ui-multigrid| support.
203+
200204
Note that the 'splitbelow' and 'splitright' options influence where a new
201205
window will appear.
202206

src/nvim/api/private/helpers.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1004,7 +1004,9 @@ static void init_ui_event_metadata(Dictionary *metadata)
10041004
Array ui_options = ARRAY_DICT_INIT;
10051005
ADD(ui_options, STRING_OBJ(cstr_to_string("rgb")));
10061006
for (UIExtension i = 0; i < kUIExtCount; i++) {
1007-
ADD(ui_options, STRING_OBJ(cstr_to_string(ui_ext_names[i])));
1007+
if (ui_ext_names[i][0] != '_') {
1008+
ADD(ui_options, STRING_OBJ(cstr_to_string(ui_ext_names[i])));
1009+
}
10081010
}
10091011
PUT(*metadata, "ui_options", ARRAY_OBJ(ui_options));
10101012
}

src/nvim/api/ui.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
#include "nvim/popupmnu.h"
1818
#include "nvim/cursor_shape.h"
1919
#include "nvim/highlight.h"
20+
#include "nvim/screen.h"
21+
#include "nvim/window.h"
2022

2123
#ifdef INCLUDE_GENERATED_DECLARATIONS
2224
# include "api/ui.c.generated.h"

src/nvim/api/ui_events.in.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ void hl_attr_define(Integer id, HlAttrs rgb_attrs, HlAttrs cterm_attrs,
7676
void grid_resize(Integer grid, Integer width, Integer height)
7777
FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL FUNC_API_COMPOSITOR_IMPL;
7878
void grid_clear(Integer grid)
79-
FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL FUNC_API_COMPOSITOR_IMPL;
79+
FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL;
8080
void grid_cursor_goto(Integer grid, Integer row, Integer col)
8181
FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL FUNC_API_COMPOSITOR_IMPL;
8282
void grid_line(Integer grid, Integer row, Integer col_start, Array data)
@@ -101,8 +101,15 @@ void event(char *name, Array args, bool *args_consumed)
101101
void win_pos(Integer grid, Integer win, Integer startrow,
102102
Integer startcol, Integer width, Integer height)
103103
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
104+
void win_float_pos(Integer grid, Window win, String anchor, Integer anchor_grid,
105+
Float anchor_row, Float anchor_col, Boolean focusable)
106+
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
107+
void win_external_pos(Integer grid, Window win)
108+
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
104109
void win_hide(Integer grid)
105110
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
111+
void win_close(Integer grid)
112+
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
106113
void win_scroll_over_start(void)
107114
FUNC_API_SINCE(6) FUNC_API_BRIDGE_IMPL FUNC_API_COMPOSITOR_IMPL;
108115
void win_scroll_over_reset(void)

src/nvim/api/vim.c

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -987,6 +987,85 @@ Buffer nvim_create_buf(Boolean listed, Boolean scratch, Error *err)
987987
return buf->b_fnum;
988988
}
989989

990+
/// Open a new window.
991+
///
992+
/// Currently this is used to open floating and external windows.
993+
/// Floats are windows that are drawn above the split layout, at some anchor
994+
/// position in some other window. Floats can be draw internally or by external
995+
/// GUI with the |ui-multigrid| extension. External windows are only supported
996+
/// with multigrid GUIs, and are displayed as separate top-level windows.
997+
///
998+
/// Exactly one of `external` and `relative` must be specified.
999+
///
1000+
/// @param buffer handle of buffer to be displayed in the window
1001+
/// @param enter whether the window should be entered (made the current window)
1002+
/// @param width width of window (in character cells)
1003+
/// @param height height of window (in character cells)
1004+
/// @param options dict of options for configuring window positioning
1005+
/// accepts the following keys:
1006+
/// `relative`: If set, the window becomes a floating window. The window
1007+
/// will be placed with row,col coordinates relative one of the
1008+
/// following:
1009+
/// "editor" the global editor grid
1010+
/// "win" a window. Use 'win' option below to specify window id,
1011+
/// or current window will be used by default.
1012+
/// "cursor" the cursor position in current window.
1013+
/// `anchor`: the corner of the float that the row,col position defines
1014+
/// "NW" north-west (default)
1015+
/// "NE" north-east
1016+
/// "SW" south-west
1017+
/// "SE" south-east
1018+
/// `focusable`: Whether window can be focused by wincmds and
1019+
/// mouse events. Defaults to true. Even if set to false, the window
1020+
/// can still be entered using |nvim_set_current_win()| API call.
1021+
/// `row`: row position. Screen cell height are used as unit. Can be
1022+
/// floating point.
1023+
/// `col`: column position. Screen cell width is used as unit. Can be
1024+
/// floating point.
1025+
/// `win`: when using relative='win', window id of the window where the
1026+
/// position is defined.
1027+
/// `external` GUI should display the window as an external
1028+
/// top-level window. Currently accepts no other positioning options
1029+
/// together with this.
1030+
///
1031+
/// With editor positioning row=0, col=0 refers to the top-left corner of the
1032+
/// screen-grid and row=Lines-1, Columns-1 refers to the bottom-right corner.
1033+
/// Floating point values are allowed, but the builtin implementation (used by
1034+
/// TUI and GUIs without multigrid support) will always round down to nearest
1035+
/// integer.
1036+
///
1037+
/// The behavior of out-of-bounds values, and other configurations that make
1038+
/// the float not fit inside the main editor, is not specified. The builtin
1039+
/// implementation will truncate values so floats are completely
1040+
/// within the main screen grid. External GUIs could let floats hover slightly
1041+
/// outside of the main window, like a tooltip, but this should not be used to
1042+
/// specify arbitrary WM screen positions.
1043+
///
1044+
/// @param[out] err Error details, if any
1045+
/// @return the buffer handle or 0 when error
1046+
Window nvim_open_win(Buffer buffer, Boolean enter,
1047+
Integer width, Integer height,
1048+
Dictionary options, Error *err)
1049+
FUNC_API_SINCE(6)
1050+
{
1051+
win_T *old = curwin;
1052+
FloatConfig config = FLOAT_CONFIG_INIT;
1053+
if (!parse_float_config(options, &config, false, err)) {
1054+
return 0;
1055+
}
1056+
win_T *wp = win_new_float(NULL, (int)width, (int)height, config, err);
1057+
if (!wp) {
1058+
return 0;
1059+
}
1060+
if (buffer > 0) {
1061+
nvim_set_current_buf(buffer, err);
1062+
}
1063+
if (!enter) {
1064+
win_enter(old, false);
1065+
}
1066+
return wp->handle;
1067+
}
1068+
9901069
/// Gets the current list of tabpage handles.
9911070
///
9921071
/// @return List of tabpage handles

src/nvim/api/window.c

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,3 +432,40 @@ Boolean nvim_win_is_valid(Window window)
432432
return ret;
433433
}
434434

435+
436+
/// Configure window position. Currently this is only used to configure
437+
/// floating and external windows (including changing a split window to these
438+
/// types).
439+
///
440+
/// See documentation at |nvim_open_win()|, for the meaning of parameters.
441+
///
442+
/// When reconfiguring a floating window, absent option keys will not be
443+
/// changed. The following restriction apply though: `row`, `col` and `relative`
444+
/// must be reconfigured together. Only changing a subset of these is an error.
445+
void nvim_win_config(Window window, Integer width, Integer height,
446+
Dictionary options, Error *err)
447+
FUNC_API_SINCE(6)
448+
{
449+
win_T *win = find_window_by_handle(window, err);
450+
if (!win) {
451+
return;
452+
}
453+
bool new_float = !win->w_floating;
454+
width = width > 0 ? width: win->w_width;
455+
height = height > 0 ? height : win->w_height;
456+
// reuse old values, if not overriden
457+
FloatConfig config = new_float ? FLOAT_CONFIG_INIT : win->w_float_config;
458+
459+
if (!parse_float_config(options, &config, !new_float, err)) {
460+
return;
461+
}
462+
if (new_float) {
463+
if (!win_new_float(win, (int)width, (int)height, config, err)) {
464+
return;
465+
}
466+
redraw_later(NOT_VALID);
467+
} else {
468+
win_config_float(win, (int)width, (int)height, config);
469+
win->w_pos_changed = true;
470+
}
471+
}

src/nvim/buffer.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -474,8 +474,8 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last)
474474
return;
475475
}
476476
buf->b_locked--;
477-
if (abort_if_last && one_window()) {
478-
/* Autocommands made this the only window. */
477+
if (abort_if_last && last_nonfloat(win)) {
478+
// Autocommands made this the only window.
479479
EMSG(_(e_auabort));
480480
return;
481481
}
@@ -491,8 +491,8 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last)
491491
return;
492492
}
493493
buf->b_locked--;
494-
if (abort_if_last && one_window()) {
495-
/* Autocommands made this the only window. */
494+
if (abort_if_last && last_nonfloat(win)) {
495+
// Autocommands made this the only window.
496496
EMSG(_(e_auabort));
497497
return;
498498
}

src/nvim/buffer_defs.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -958,6 +958,35 @@ struct matchitem {
958958
int conceal_char; ///< cchar for Conceal highlighting
959959
};
960960

961+
typedef enum {
962+
kFloatAnchorEast = 1,
963+
kFloatAnchorSouth = 2,
964+
965+
kFloatAnchorNW = 0,
966+
kFloatAnchorNE = 1,
967+
kFloatAnchorSW = 2,
968+
kFloatAnchorSE = 3,
969+
} FloatAnchor;
970+
971+
typedef enum {
972+
kFloatRelativeEditor = 0,
973+
kFloatRelativeWindow = 1,
974+
kFloatRelativeCursor = 2,
975+
} FloatRelative;
976+
977+
typedef struct {
978+
Window window;
979+
double row, col;
980+
FloatAnchor anchor;
981+
FloatRelative relative;
982+
bool external;
983+
bool focusable;
984+
} FloatConfig;
985+
986+
#define FLOAT_CONFIG_INIT ((FloatConfig){ .row = 0, .col = 0, .anchor = 0, \
987+
.relative = 0, .external = false, \
988+
.focusable = true })
989+
961990
/*
962991
* Structure which contains all information that belongs to a window
963992
*
@@ -1221,6 +1250,8 @@ struct window_S {
12211250

12221251
ScreenGrid w_grid; // the grid specific to the window
12231252
bool w_pos_changed; // true if window position changed
1253+
bool w_floating; ///< whether the window is floating
1254+
FloatConfig w_float_config;
12241255

12251256
/*
12261257
* w_fraction is the fractional row of the cursor within the window, from

src/nvim/ex_docmd.c

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6285,6 +6285,9 @@ void tabpage_close(int forceit)
62856285
{
62866286
// First close all the windows but the current one. If that worked then
62876287
// close the last window in this tab, that will close it.
6288+
while (curwin->w_floating) {
6289+
ex_win_close(forceit, curwin, NULL);
6290+
}
62886291
if (!ONE_WINDOW) {
62896292
close_others(true, forceit);
62906293
}
@@ -6309,8 +6312,8 @@ void tabpage_close_other(tabpage_T *tp, int forceit)
63096312
/* Limit to 1000 windows, autocommands may add a window while we close
63106313
* one. OK, so I'm paranoid... */
63116314
while (++done < 1000) {
6312-
sprintf((char *)prev_idx, "%i", tabpage_index(tp));
6313-
wp = tp->tp_firstwin;
6315+
snprintf((char *)prev_idx, sizeof(prev_idx), "%i", tabpage_index(tp));
6316+
wp = tp->tp_lastwin;
63146317
ex_win_close(forceit, wp, tp);
63156318

63166319
/* Autocommands may delete the tab page under our fingers and we may
@@ -6331,6 +6334,7 @@ static void ex_only(exarg_T *eap)
63316334
{
63326335
win_T *wp;
63336336
int wnr;
6337+
63346338
if (eap->addr_count > 0) {
63356339
wnr = eap->line2;
63366340
for (wp = firstwin; --wnr > 0;) {
@@ -6339,6 +6343,10 @@ static void ex_only(exarg_T *eap)
63396343
else
63406344
wp = wp->w_next;
63416345
}
6346+
} else {
6347+
wp = curwin;
6348+
}
6349+
if (wp != curwin) {
63426350
win_goto(wp);
63436351
}
63446352
close_others(TRUE, eap->forceit);

0 commit comments

Comments
 (0)