mirror of
				https://github.com/neovim/neovim.git
				synced 2025-10-26 12:27:24 +00:00 
			
		
		
		
	floats: implement floating windows
Co-Author: Dongdong Zhou <dzhou121@gmail.com>
This commit is contained in:
		| @@ -520,19 +520,33 @@ tabs. | ||||
| 	size). If the window was previously hidden, it should now be shown | ||||
| 	again. | ||||
|  | ||||
| ["win_float_pos", grid, win, anchor, anchor_grid, anchor_row, anchor_col, focusable] | ||||
| 	Display or reconfigure floating window `win`. The window should be | ||||
| 	displayed above another grid `anchor_grid` at the specified position | ||||
| 	`anchor_row` and `anchor_col`. For the meaning of `anchor` and more | ||||
| 	details of positioning, see |nvim_open_win()|. | ||||
|  | ||||
| ["win_external_pos", grid, win] | ||||
| 	Display or reconfigure external window `win`. The window should be | ||||
| 	displayed as a separate top-level window in the desktop envirionment, | ||||
| 	or something similar. | ||||
|  | ||||
| ["win_hide", grid] | ||||
| 	Stop displaying the window. | ||||
| 	Stop displaying the window. The window can be shown again later. | ||||
|  | ||||
| ["win_scroll_over_start"] | ||||
| 	Hint that following `grid_scroll` on the default grid should | ||||
| 	scroll over windows. This is a temporary workaround to allow | ||||
| 	UIs to use the builtin message drawing. Later on, messages will be | ||||
| 	drawn on a dedicated grid. | ||||
| 	drawn on a dedicated grid. Using |ui-messages| also avoids this issue. | ||||
|  | ||||
| ["win_scroll_over_reset"] | ||||
| 	Hint that scrolled over windows should be redrawn again, and not be | ||||
| 	overdrawn by default grid scrolling anymore. | ||||
|  | ||||
| ["win_close", grid] | ||||
| 	Close the window. | ||||
|  | ||||
| See |ui-linegrid| for grid events. | ||||
| See |nvim_ui_try_resize_grid| in |api-ui| to request changing the grid size. | ||||
| See |nvim_input_mouse| for sending mouse events to Nvim. | ||||
|   | ||||
| @@ -197,6 +197,10 @@ CTRL-W ^	Does ":split #", split window in two and edit alternate file. | ||||
| 		When a count is given, it becomes ":split #N", split window | ||||
| 		and edit buffer N. | ||||
|  | ||||
| CTRL-W ge						*CTRL-W_ge* | ||||
| 		Detach the current window as an external window. | ||||
| 		Only available when using an UI with |ui-multigrid| support. | ||||
|  | ||||
| Note that the 'splitbelow' and 'splitright' options influence where a new | ||||
| window will appear. | ||||
|  | ||||
|   | ||||
| @@ -1004,8 +1004,10 @@ static void init_ui_event_metadata(Dictionary *metadata) | ||||
|   Array ui_options = ARRAY_DICT_INIT; | ||||
|   ADD(ui_options, STRING_OBJ(cstr_to_string("rgb"))); | ||||
|   for (UIExtension i = 0; i < kUIExtCount; i++) { | ||||
|     if (ui_ext_names[i][0] != '_') { | ||||
|       ADD(ui_options, STRING_OBJ(cstr_to_string(ui_ext_names[i]))); | ||||
|     } | ||||
|   } | ||||
|   PUT(*metadata, "ui_options", ARRAY_OBJ(ui_options)); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -17,6 +17,8 @@ | ||||
| #include "nvim/popupmnu.h" | ||||
| #include "nvim/cursor_shape.h" | ||||
| #include "nvim/highlight.h" | ||||
| #include "nvim/screen.h" | ||||
| #include "nvim/window.h" | ||||
|  | ||||
| #ifdef INCLUDE_GENERATED_DECLARATIONS | ||||
| # include "api/ui.c.generated.h" | ||||
|   | ||||
| @@ -76,7 +76,7 @@ void hl_attr_define(Integer id, HlAttrs rgb_attrs, HlAttrs cterm_attrs, | ||||
| void grid_resize(Integer grid, Integer width, Integer height) | ||||
|   FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL FUNC_API_COMPOSITOR_IMPL; | ||||
| void grid_clear(Integer grid) | ||||
|   FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL FUNC_API_COMPOSITOR_IMPL; | ||||
|   FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL; | ||||
| void grid_cursor_goto(Integer grid, Integer row, Integer col) | ||||
|   FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL FUNC_API_COMPOSITOR_IMPL; | ||||
| 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) | ||||
| void win_pos(Integer grid, Integer win, Integer startrow, | ||||
|              Integer startcol, Integer width, Integer height) | ||||
|   FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY; | ||||
| void win_float_pos(Integer grid, Window win, String anchor, Integer anchor_grid, | ||||
|                    Float anchor_row, Float anchor_col, Boolean focusable) | ||||
|   FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY; | ||||
| void win_external_pos(Integer grid, Window win) | ||||
|   FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY; | ||||
| void win_hide(Integer grid) | ||||
|   FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY; | ||||
| void win_close(Integer grid) | ||||
|   FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY; | ||||
| void win_scroll_over_start(void) | ||||
|   FUNC_API_SINCE(6) FUNC_API_BRIDGE_IMPL FUNC_API_COMPOSITOR_IMPL; | ||||
| void win_scroll_over_reset(void) | ||||
|   | ||||
| @@ -987,6 +987,84 @@ Buffer nvim_create_buf(Boolean listed, Boolean scratch, Error *err) | ||||
|   return buf->b_fnum; | ||||
| } | ||||
|  | ||||
| /// Open a new window. | ||||
| /// | ||||
| /// Currently this is used to open floating and external windows. | ||||
| /// Floats are windows that are drawn above the split layout, at some anchor | ||||
| /// position in some other window. Floats can be draw internally or by external | ||||
| /// GUI with the |ui-multigrid| extension. External windows are only supported | ||||
| /// with multigrid GUIs, and are displayed as separate top-level windows. | ||||
| /// | ||||
| /// Exactly one of `external` and `relative` must be specified. | ||||
| /// | ||||
| /// @param buffer handle of buffer to be displayed in the window | ||||
| /// @param enter whether the window should be entered (made the current window) | ||||
| /// @param width width of window (in character cells) | ||||
| /// @param height height of window (in character cells) | ||||
| /// @param options dict of options for configuring window positioning | ||||
| ///                accepts the following keys: | ||||
| ///     `relative`: If set, the window becomes a floating window. The window | ||||
| ///         will be placed with row,col coordinates relative one of the | ||||
| ///         following: | ||||
| ///        "editor" the global editor grid | ||||
| ///        "win"    a window. Use 'win' option below to specify window id, | ||||
| ///                 or current window will be used by default. | ||||
| ///        "cursor" the cursor position in current window. | ||||
| ///     `anchor`:   the corner of the float that the row,col position defines | ||||
| ///        "NW" north-west (default) | ||||
| ///        "NE" north-east | ||||
| ///        "SW" south-west | ||||
| ///        "SE" south-east | ||||
| ///     `focusable`: Whether window can be focused by wincmds and | ||||
| ///         mouse events. Defaults to true. Even if set to false, the window | ||||
| ///         can still be entered using |nvim_set_current_win()| API call. | ||||
| ///     `row`: row position. Screen cell height are used as unit.  Can be | ||||
| ///         floating point. | ||||
| ///     `col`: column position. Screen cell width is used as unit. Can be | ||||
| ///         floating point. | ||||
| ///     `win`: when using relative='win', window id of the window where the | ||||
| ///         position is defined. | ||||
| ///     `external` GUI should display the window as an external | ||||
| ///         top-level window. Currently accepts no other positioning options | ||||
| ///         together with this. | ||||
| /// | ||||
| /// With editor positioning row=0, col=0 refers to the top-left corner of the | ||||
| /// screen-grid and row=Lines-1, Columns-1 refers to the bottom-right corner. | ||||
| /// Floating point values are allowed, but the builtin implementation (used by | ||||
| /// TUI and GUIs without multigrid support) will always round down to nearest | ||||
| /// integer. | ||||
| /// | ||||
| /// Out-of-bounds values, and configurations that make the float not fit inside | ||||
| /// the main editor, are allowed. The builtin implementation will truncate | ||||
| /// values so floats are completely within the main screen grid. External GUIs | ||||
| /// could let floats hover outside of the main window like a tooltip, but | ||||
| /// this should not be used to specify arbitrary WM screen positions. | ||||
| /// | ||||
| /// @param[out] err Error details, if any | ||||
| /// @return the window handle or 0 when error | ||||
| Window nvim_open_win(Buffer buffer, Boolean enter, | ||||
|                      Integer width, Integer height, | ||||
|                      Dictionary options, Error *err) | ||||
|   FUNC_API_SINCE(6) | ||||
| { | ||||
|   win_T *old = curwin; | ||||
|   FloatConfig config = FLOAT_CONFIG_INIT; | ||||
|   if (!parse_float_config(options, &config, false, err)) { | ||||
|     return 0; | ||||
|   } | ||||
|   win_T *wp = win_new_float(NULL, (int)width, (int)height, config, err); | ||||
|   if (!wp) { | ||||
|     return 0; | ||||
|   } | ||||
|   if (buffer > 0) { | ||||
|     nvim_set_current_buf(buffer, err); | ||||
|   } | ||||
|   if (!enter) { | ||||
|     win_enter(old, false); | ||||
|   } | ||||
|   return wp->handle; | ||||
| } | ||||
|  | ||||
| /// Gets the current list of tabpage handles. | ||||
| /// | ||||
| /// @return List of tabpage handles | ||||
|   | ||||
| @@ -432,3 +432,41 @@ Boolean nvim_win_is_valid(Window window) | ||||
|   return ret; | ||||
| } | ||||
|  | ||||
|  | ||||
| /// Configure window position. Currently this is only used to configure | ||||
| /// floating and external windows (including changing a split window to these | ||||
| /// types). | ||||
| /// | ||||
| /// See documentation at |nvim_open_win()|, for the meaning of parameters. Pass | ||||
| /// in -1 for 'witdh' and 'height' to keep exiting size. | ||||
| /// | ||||
| /// When reconfiguring a floating window, absent option keys will not be | ||||
| /// changed. The following restriction apply: `row`, `col` and `relative` | ||||
| /// must be reconfigured together. Only changing a subset of these is an error. | ||||
| void nvim_win_config(Window window, Integer width, Integer height, | ||||
|                      Dictionary options, Error *err) | ||||
|   FUNC_API_SINCE(6) | ||||
| { | ||||
|   win_T *win = find_window_by_handle(window, err); | ||||
|   if (!win) { | ||||
|     return; | ||||
|   } | ||||
|   bool new_float = !win->w_floating; | ||||
|   width = width > 0 ? width: win->w_width; | ||||
|   height = height > 0 ? height : win->w_height; | ||||
|   // reuse old values, if not overriden | ||||
|   FloatConfig config = new_float ? FLOAT_CONFIG_INIT : win->w_float_config; | ||||
|  | ||||
|   if (!parse_float_config(options, &config, !new_float, err)) { | ||||
|     return; | ||||
|   } | ||||
|   if (new_float) { | ||||
|     if (!win_new_float(win, (int)width, (int)height, config, err)) { | ||||
|       return; | ||||
|     } | ||||
|     redraw_later(NOT_VALID); | ||||
|   } else { | ||||
|     win_config_float(win, (int)width, (int)height, config); | ||||
|     win->w_pos_changed = true; | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -474,8 +474,8 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last) | ||||
|       return; | ||||
|     } | ||||
|     buf->b_locked--; | ||||
|     if (abort_if_last && one_window()) { | ||||
|       /* Autocommands made this the only window. */ | ||||
|     if (abort_if_last && last_nonfloat(win)) { | ||||
|       // Autocommands made this the only window. | ||||
|       EMSG(_(e_auabort)); | ||||
|       return; | ||||
|     } | ||||
| @@ -491,8 +491,8 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last) | ||||
|         return; | ||||
|       } | ||||
|       buf->b_locked--; | ||||
|       if (abort_if_last && one_window()) { | ||||
|         /* Autocommands made this the only window. */ | ||||
|       if (abort_if_last && last_nonfloat(win)) { | ||||
|         // Autocommands made this the only window. | ||||
|         EMSG(_(e_auabort)); | ||||
|         return; | ||||
|       } | ||||
|   | ||||
| @@ -958,6 +958,35 @@ struct matchitem { | ||||
|   int conceal_char;         ///< cchar for Conceal highlighting | ||||
| }; | ||||
|  | ||||
| typedef enum { | ||||
|     kFloatAnchorEast = 1, | ||||
|     kFloatAnchorSouth = 2, | ||||
|  | ||||
|     kFloatAnchorNW = 0, | ||||
|     kFloatAnchorNE = 1, | ||||
|     kFloatAnchorSW = 2, | ||||
|     kFloatAnchorSE = 3, | ||||
| } FloatAnchor; | ||||
|  | ||||
| typedef enum { | ||||
|     kFloatRelativeEditor = 0, | ||||
|     kFloatRelativeWindow = 1, | ||||
|     kFloatRelativeCursor = 2, | ||||
| } FloatRelative; | ||||
|  | ||||
| typedef struct { | ||||
|   Window window; | ||||
|   double row, col; | ||||
|   FloatAnchor anchor; | ||||
|   FloatRelative relative; | ||||
|   bool external; | ||||
|   bool focusable; | ||||
| } FloatConfig; | ||||
|  | ||||
| #define FLOAT_CONFIG_INIT ((FloatConfig){ .row = 0, .col = 0, .anchor = 0, \ | ||||
|                                           .relative = 0, .external = false, \ | ||||
|                                           .focusable = true }) | ||||
|  | ||||
| /* | ||||
|  * Structure which contains all information that belongs to a window | ||||
|  * | ||||
| @@ -1221,6 +1250,8 @@ struct window_S { | ||||
|  | ||||
|   ScreenGrid w_grid;                    // the grid specific to the window | ||||
|   bool w_pos_changed;                   // true if window position changed | ||||
|   bool w_floating;                       ///< whether the window is floating | ||||
|   FloatConfig w_float_config; | ||||
|  | ||||
|   /* | ||||
|    * w_fraction is the fractional row of the cursor within the window, from | ||||
|   | ||||
| @@ -6285,6 +6285,9 @@ void tabpage_close(int forceit) | ||||
| { | ||||
|   // First close all the windows but the current one.  If that worked then | ||||
|   // close the last window in this tab, that will close it. | ||||
|   while (curwin->w_floating) { | ||||
|     ex_win_close(forceit, curwin, NULL); | ||||
|   } | ||||
|   if (!ONE_WINDOW) { | ||||
|     close_others(true, forceit); | ||||
|   } | ||||
| @@ -6309,8 +6312,8 @@ void tabpage_close_other(tabpage_T *tp, int forceit) | ||||
|   /* Limit to 1000 windows, autocommands may add a window while we close | ||||
|    * one.  OK, so I'm paranoid... */ | ||||
|   while (++done < 1000) { | ||||
|     sprintf((char *)prev_idx, "%i", tabpage_index(tp)); | ||||
|     wp = tp->tp_firstwin; | ||||
|     snprintf((char *)prev_idx, sizeof(prev_idx), "%i", tabpage_index(tp)); | ||||
|     wp = tp->tp_lastwin; | ||||
|     ex_win_close(forceit, wp, tp); | ||||
|  | ||||
|     /* Autocommands may delete the tab page under our fingers and we may | ||||
| @@ -6331,6 +6334,7 @@ static void ex_only(exarg_T *eap) | ||||
| { | ||||
|   win_T *wp; | ||||
|   int wnr; | ||||
|  | ||||
|   if (eap->addr_count > 0) { | ||||
|     wnr = eap->line2; | ||||
|     for (wp = firstwin; --wnr > 0;) { | ||||
| @@ -6339,6 +6343,10 @@ static void ex_only(exarg_T *eap) | ||||
|       else | ||||
|         wp = wp->w_next; | ||||
|     } | ||||
|   } else { | ||||
|     wp = curwin; | ||||
|   } | ||||
|   if (wp != curwin) { | ||||
|     win_goto(wp); | ||||
|   } | ||||
|   close_others(TRUE, eap->forceit); | ||||
|   | ||||
| @@ -3470,11 +3470,13 @@ void redrawcmd(void) | ||||
|  | ||||
| void compute_cmdrow(void) | ||||
| { | ||||
|   if (exmode_active || msg_scrolled != 0) | ||||
|   if (exmode_active || msg_scrolled != 0) { | ||||
|     cmdline_row = Rows - 1; | ||||
|   else | ||||
|     cmdline_row = lastwin->w_winrow + lastwin->w_height | ||||
|                   + lastwin->w_status_height; | ||||
|   } else { | ||||
|     win_T *wp = lastwin_nofloating(); | ||||
|     cmdline_row = wp->w_winrow + wp->w_height | ||||
|                   + wp->w_status_height; | ||||
|   } | ||||
| } | ||||
|  | ||||
| static void cursorcmd(void) | ||||
|   | ||||
| @@ -1049,6 +1049,10 @@ EXTERN char_u e_cmdmap_repeated[] INIT(=N_( | ||||
|     "E5521: <Cmd> mapping must end with <CR> before second <Cmd>")); | ||||
| EXTERN char_u e_cmdmap_key[] INIT(=N_( | ||||
|     "E5522: <Cmd> mapping must not include %s key")); | ||||
| EXTERN char_u e_floatonly[] INIT(=N_( | ||||
|     "E5601: Cannot close window, only floating window would remain")); | ||||
| EXTERN char_u e_floatexchange[] INIT(=N_( | ||||
|     "E5602: Cannot exchange or rotate float")); | ||||
|  | ||||
|  | ||||
| EXTERN char top_bot_msg[] INIT(= N_("search hit TOP, continuing at BOTTOM")); | ||||
|   | ||||
| @@ -47,6 +47,9 @@ typedef struct { | ||||
|   int Rows; | ||||
|   int Columns; | ||||
|  | ||||
|   // The state of the grid is valid. Otherwise it needs to be redrawn. | ||||
|   bool valid; | ||||
|  | ||||
|   // offsets for the grid relative to the global screen | ||||
|   int row_offset; | ||||
|   int col_offset; | ||||
| @@ -58,7 +61,7 @@ typedef struct { | ||||
|   bool comp_disabled; | ||||
| } ScreenGrid; | ||||
|  | ||||
| #define SCREEN_GRID_INIT { 0, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, \ | ||||
|                            false } | ||||
| #define SCREEN_GRID_INIT { 0, NULL, NULL, NULL, NULL, 0, 0, false, 0, 0, 0, \ | ||||
|                            0, 0,  false } | ||||
|  | ||||
| #endif  // NVIM_GRID_DEFS_H | ||||
|   | ||||
| @@ -46,6 +46,7 @@ | ||||
| #include "nvim/os/os_defs.h" | ||||
| #include "nvim/path.h" | ||||
| #include "nvim/profile.h" | ||||
| #include "nvim/popupmnu.h" | ||||
| #include "nvim/quickfix.h" | ||||
| #include "nvim/screen.h" | ||||
| #include "nvim/state.h" | ||||
|   | ||||
| @@ -572,8 +572,13 @@ void free_all_mem(void) | ||||
|   p_ea = false; | ||||
|   if (first_tabpage->tp_next != NULL) | ||||
|     do_cmdline_cmd("tabonly!"); | ||||
|   if (!ONE_WINDOW) | ||||
|  | ||||
|   if (!ONE_WINDOW) { | ||||
|     // to keep things simple, don't perform this | ||||
|     // ritual inside a float | ||||
|     curwin = firstwin; | ||||
|     do_cmdline_cmd("only!"); | ||||
|   } | ||||
|  | ||||
|   /* Free all spell info. */ | ||||
|   spell_free_all(); | ||||
|   | ||||
| @@ -2777,10 +2777,12 @@ void msg_ext_flush_showmode(void) | ||||
| { | ||||
|   // Showmode messages doesn't interrupt normal message flow, so we use | ||||
|   // separate event. Still reuse the same chunking logic, for simplicity. | ||||
|   if (ui_has(kUIMessages)) { | ||||
|     msg_ext_emit_chunk(); | ||||
|     ui_call_msg_showmode(msg_ext_chunks); | ||||
|     msg_ext_chunks = (Array)ARRAY_DICT_INIT; | ||||
|   } | ||||
| } | ||||
|  | ||||
| void msg_ext_clear(bool force) | ||||
| { | ||||
|   | ||||
| @@ -12,6 +12,7 @@ | ||||
| #include "nvim/screen.h" | ||||
| #include "nvim/syntax.h" | ||||
| #include "nvim/ui.h" | ||||
| #include "nvim/ui_compositor.h" | ||||
| #include "nvim/os_unix.h" | ||||
| #include "nvim/fold.h" | ||||
| #include "nvim/diff.h" | ||||
| @@ -441,12 +442,6 @@ win_T *mouse_find_win(int *gridp, int *rowp, int *colp) | ||||
|     return wp_grid; | ||||
|   } | ||||
|  | ||||
|   // TODO(bfredl): grid zero will have floats displayed on it, and will | ||||
|   // be adjusted to float grids. | ||||
|   if (*gridp == 0) { | ||||
|     *gridp = DEFAULT_GRID_HANDLE; | ||||
|   } | ||||
|  | ||||
|   frame_T     *fp; | ||||
|  | ||||
|   fp = topframe; | ||||
| @@ -478,15 +473,31 @@ win_T *mouse_find_win(int *gridp, int *rowp, int *colp) | ||||
|   return NULL; | ||||
| } | ||||
|  | ||||
| static win_T *mouse_find_grid_win(int *grid, int *rowp, int *colp) | ||||
| static win_T *mouse_find_grid_win(int *gridp, int *rowp, int *colp) | ||||
| { | ||||
|   if (*grid > 1) { | ||||
|     win_T *wp = get_win_by_grid_handle(*grid); | ||||
|     if (wp && wp->w_grid.chars) { | ||||
|   if (*gridp > 1) { | ||||
|     win_T *wp = get_win_by_grid_handle(*gridp); | ||||
|     if (wp && wp->w_grid.chars | ||||
|         && !(wp->w_floating && !wp->w_float_config.focusable)) { | ||||
|       *rowp = MIN(*rowp, wp->w_grid.Rows-1); | ||||
|       *colp = MIN(*colp, wp->w_grid.Columns-1); | ||||
|       return wp; | ||||
|     } | ||||
|   } else if (*gridp == 0) { | ||||
|     ScreenGrid *grid = ui_comp_mouse_focus(*rowp, *colp); | ||||
|     FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { | ||||
|       if (&wp->w_grid != grid || !wp->w_float_config.focusable) { | ||||
|         continue; | ||||
|       } | ||||
|       *gridp = grid->handle; | ||||
|       *rowp -= grid->comp_row; | ||||
|       *colp -= grid->comp_col; | ||||
|       return wp; | ||||
|     } | ||||
|  | ||||
|     // no float found, click on the default grid | ||||
|     // TODO(bfredl): grid can be &pum_grid, allow select pum items by mouse? | ||||
|     *gridp = DEFAULT_GRID_HANDLE; | ||||
|   } | ||||
|   return NULL; | ||||
| } | ||||
|   | ||||
| @@ -2200,6 +2200,7 @@ do_mouse ( | ||||
|        * one. Speeds up dragging the status line. */ | ||||
|       if (vpeekc() != NUL) { | ||||
|         int nc; | ||||
|         int save_mouse_grid = mouse_grid; | ||||
|         int save_mouse_row = mouse_row; | ||||
|         int save_mouse_col = mouse_col; | ||||
|  | ||||
| @@ -2209,6 +2210,7 @@ do_mouse ( | ||||
|         if (c == nc) | ||||
|           continue; | ||||
|         vungetc(nc); | ||||
|         mouse_grid = save_mouse_grid; | ||||
|         mouse_row = save_mouse_row; | ||||
|         mouse_col = save_mouse_col; | ||||
|       } | ||||
|   | ||||
| @@ -331,7 +331,6 @@ static unsigned int handle_mouse_event(char **ptr, uint8_t *buf, | ||||
|   uint8_t modifiers = check_multiclick(mouse_code, mouse_grid, | ||||
|                                        mouse_row, mouse_col); | ||||
|  | ||||
|  | ||||
|   if (modifiers) { | ||||
|     if (buf[1] != KS_MODIFIER) { | ||||
|       // no modifiers in the buffer yet, shift the bytes 3 positions | ||||
|   | ||||
| @@ -38,16 +38,16 @@ static int pum_base_width;          // width of pum items base | ||||
| static int pum_kind_width;          // width of pum items kind column | ||||
| static int pum_scrollbar;           // TRUE when scrollbar present | ||||
|  | ||||
| static int pum_anchor_grid;         // grid where position is defined | ||||
| static int pum_row;                 // top row of pum | ||||
| static int pum_col;                 // left column of pum | ||||
| static bool pum_above;              // pum is drawn above cursor line | ||||
|  | ||||
| static bool pum_is_visible = false; | ||||
| static bool pum_is_drawn = false; | ||||
| static bool pum_external = false; | ||||
| static bool pum_invalid = false;  // the screen was just cleared | ||||
|  | ||||
| static ScreenGrid pum_grid = SCREEN_GRID_INIT; | ||||
|  | ||||
| #ifdef INCLUDE_GENERATED_DECLARATIONS | ||||
| # include "popupmnu.c.generated.h" | ||||
| #endif | ||||
| @@ -103,9 +103,9 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed) | ||||
|       col = curwin->w_wcol; | ||||
|     } | ||||
|  | ||||
|     int grid = (int)curwin->w_grid.handle; | ||||
|     pum_anchor_grid = (int)curwin->w_grid.handle; | ||||
|     if (!ui_has(kUIMultigrid)) { | ||||
|       grid = (int)default_grid.handle; | ||||
|       pum_anchor_grid = (int)default_grid.handle; | ||||
|       row += curwin->w_winrow; | ||||
|       col += curwin->w_wincol; | ||||
|     } | ||||
| @@ -121,7 +121,7 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed) | ||||
|           ADD(item, STRING_OBJ(cstr_to_string((char *)array[i].pum_info))); | ||||
|           ADD(arr, ARRAY_OBJ(item)); | ||||
|         } | ||||
|         ui_call_popupmenu_show(arr, selected, row, col, grid); | ||||
|         ui_call_popupmenu_show(arr, selected, row, col, pum_anchor_grid); | ||||
|       } else { | ||||
|         ui_call_popupmenu_select(selected); | ||||
|       } | ||||
| @@ -165,6 +165,7 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed) | ||||
|     if (row + 2 >= below_row - pum_height | ||||
|         && row - above_row > (below_row - above_row) / 2) { | ||||
|       // pum above "row" | ||||
|       pum_above = true; | ||||
|  | ||||
|       // Leave two lines of context if possible | ||||
|       if (curwin->w_wrow - curwin->w_cline_row >= 2) { | ||||
| @@ -187,6 +188,7 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed) | ||||
|       } | ||||
|     } else { | ||||
|       // pum below "row" | ||||
|       pum_above = false; | ||||
|  | ||||
|       // Leave two lines of context if possible | ||||
|       if (curwin->w_cline_row + curwin->w_cline_height - curwin->w_wrow >= 3) { | ||||
| @@ -360,7 +362,7 @@ void pum_redraw(void) | ||||
|  | ||||
|   grid_assign_handle(&pum_grid); | ||||
|   bool moved = ui_comp_put_grid(&pum_grid, pum_row, pum_col-col_off, | ||||
|                                 pum_height, grid_width); | ||||
|                                 pum_height, grid_width, false, true); | ||||
|   bool invalid_grid = moved || pum_invalid; | ||||
|   pum_invalid = false; | ||||
|  | ||||
| @@ -371,6 +373,13 @@ void pum_redraw(void) | ||||
|   } else if (invalid_grid) { | ||||
|     grid_invalidate(&pum_grid); | ||||
|   } | ||||
|   if (ui_has(kUIMultigrid)) { | ||||
|     const char *anchor = pum_above ? "SW" : "NW"; | ||||
|     int row_off = pum_above ? pum_height : 0; | ||||
|     ui_call_win_float_pos(pum_grid.handle, -1, cstr_to_string(anchor), | ||||
|                           pum_anchor_grid, pum_row-row_off, pum_col-col_off, | ||||
|                           false); | ||||
|   } | ||||
|  | ||||
|  | ||||
|   // Never display more than we have | ||||
| @@ -783,6 +792,10 @@ void pum_check_clear(void) | ||||
|       ui_call_popupmenu_hide(); | ||||
|     } else { | ||||
|       ui_comp_remove_grid(&pum_grid); | ||||
|       if (ui_has(kUIMultigrid)) { | ||||
|         ui_call_win_close(pum_grid.handle); | ||||
|         ui_call_grid_destroy(pum_grid.handle); | ||||
|       } | ||||
|       // TODO(bfredl): consider keeping float grids allocated. | ||||
|       grid_free(&pum_grid); | ||||
|     } | ||||
|   | ||||
| @@ -1,6 +1,8 @@ | ||||
| #ifndef NVIM_POPUPMNU_H | ||||
| #define NVIM_POPUPMNU_H | ||||
|  | ||||
| #include "nvim/macros.h" | ||||
| #include "nvim/grid_defs.h" | ||||
| #include "nvim/types.h" | ||||
|  | ||||
| /// Used for popup menu items. | ||||
| @@ -11,6 +13,7 @@ typedef struct { | ||||
|   char_u *pum_info;        // extra info | ||||
| } pumitem_T; | ||||
|  | ||||
| EXTERN ScreenGrid pum_grid INIT(= SCREEN_GRID_INIT); | ||||
|  | ||||
| #ifdef INCLUDE_GENERATED_DECLARATIONS | ||||
| # include "popupmnu.h.generated.h" | ||||
|   | ||||
| @@ -148,9 +148,6 @@ typedef struct { | ||||
| /// Whether to call "ui_call_grid_resize" in win_grid_alloc | ||||
| static bool send_grid_resize = false; | ||||
|  | ||||
| /// Highlight ids are no longer valid. Force retransmission | ||||
| static bool highlights_invalid = false; | ||||
|  | ||||
| static bool conceal_cursor_used = false; | ||||
|  | ||||
| static bool redraw_popupmenu = false; | ||||
| @@ -197,8 +194,10 @@ void redraw_all_later(int type) | ||||
|  | ||||
| void screen_invalidate_highlights(void) | ||||
| { | ||||
|   redraw_all_later(NOT_VALID); | ||||
|   highlights_invalid = true; | ||||
|   FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { | ||||
|     redraw_win_later(wp, NOT_VALID); | ||||
|     wp->w_grid.valid = false; | ||||
|   } | ||||
| } | ||||
|  | ||||
| /* | ||||
| @@ -338,6 +337,9 @@ void update_screen(int type) | ||||
|       grid_ins_lines(&default_grid, 0, msg_scrolled, (int)Rows, | ||||
|                      0, (int)Columns); | ||||
|       FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { | ||||
|         if (wp->w_floating) { | ||||
|           continue; | ||||
|         } | ||||
|         if (wp->w_winrow < msg_scrolled) { | ||||
|           if (W_ENDROW(wp) > msg_scrolled | ||||
|               && wp->w_redr_type < REDRAW_TOP | ||||
| @@ -361,6 +363,10 @@ void update_screen(int type) | ||||
|     need_wait_return = FALSE; | ||||
|   } | ||||
|  | ||||
|   if (type >= NOT_VALID) { | ||||
|     ui_comp_set_screen_valid(false); | ||||
|   } | ||||
|   win_ui_flush_positions(); | ||||
|   msg_ext_check_prompt(); | ||||
|  | ||||
|   /* reset cmdline_row now (may have been changed temporarily) */ | ||||
| @@ -376,9 +382,11 @@ void update_screen(int type) | ||||
|     type = NOT_VALID; | ||||
|     // must_redraw may be set indirectly, avoid another redraw later | ||||
|     must_redraw = 0; | ||||
|   } else if (highlights_invalid) { | ||||
|   } else if (!default_grid.valid) { | ||||
|     grid_invalidate(&default_grid); | ||||
|     default_grid.valid = true; | ||||
|   } | ||||
|   ui_comp_set_screen_valid(true); | ||||
|  | ||||
|   if (clear_cmdline)            /* going to clear cmdline (done below) */ | ||||
|     check_for_delay(FALSE); | ||||
| @@ -449,7 +457,14 @@ void update_screen(int type) | ||||
|    */ | ||||
|   did_one = FALSE; | ||||
|   search_hl.rm.regprog = NULL; | ||||
|  | ||||
|  | ||||
|   FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { | ||||
|     if (wp->w_redr_type == CLEAR && wp->w_floating && wp->w_grid.chars) { | ||||
|       grid_invalidate(&wp->w_grid); | ||||
|       wp->w_redr_type = NOT_VALID; | ||||
|     } | ||||
|  | ||||
|     if (wp->w_redr_type != 0) { | ||||
|       if (!did_one) { | ||||
|         did_one = TRUE; | ||||
| @@ -471,7 +486,6 @@ void update_screen(int type) | ||||
|   } | ||||
|  | ||||
|   send_grid_resize = false; | ||||
|   highlights_invalid = false; | ||||
|   redraw_popupmenu = false; | ||||
|  | ||||
|   /* Reset b_mod_set flags.  Going through all windows is probably faster | ||||
| @@ -4287,9 +4301,12 @@ win_line ( | ||||
| /// If UI did not request multigrid support, draw all windows on the | ||||
| /// default_grid. | ||||
| /// | ||||
| /// NB: this function can only been used with window grids in a context where | ||||
| /// win_grid_alloc already has been called! | ||||
| /// | ||||
| /// If the default_grid is used, adjust window relative positions to global | ||||
| /// screen positions. | ||||
| static void screen_adjust_grid(ScreenGrid **grid, int *row_off, int *col_off) | ||||
| void screen_adjust_grid(ScreenGrid **grid, int *row_off, int *col_off) | ||||
| { | ||||
|   if (!(*grid)->chars && *grid != &default_grid) { | ||||
|     *row_off += (*grid)->row_offset; | ||||
| @@ -5292,7 +5309,6 @@ void grid_getbytes(ScreenGrid *grid, int row, int col, char_u *bytes, | ||||
|  | ||||
|   screen_adjust_grid(&grid, &row, &col); | ||||
|  | ||||
|  | ||||
|   // safety check | ||||
|   if (grid->chars != NULL && row < grid->Rows && col < grid->Columns) { | ||||
|     off = grid->line_offset[row] + col; | ||||
| @@ -5909,14 +5925,9 @@ void win_grid_alloc(win_T *wp) | ||||
|   int rows = wp->w_height_inner; | ||||
|   int cols = wp->w_width_inner; | ||||
|  | ||||
|   // TODO(bfredl): floating windows should force this to true | ||||
|   bool want_allocation = ui_has(kUIMultigrid); | ||||
|   bool want_allocation = ui_has(kUIMultigrid) || wp->w_floating; | ||||
|   bool has_allocation = (grid->chars != NULL); | ||||
|  | ||||
|   if (want_allocation && has_allocation && highlights_invalid) { | ||||
|     grid_invalidate(grid); | ||||
|   } | ||||
|  | ||||
|   if (grid->Rows != rows) { | ||||
|     wp->w_lines_valid = 0; | ||||
|     xfree(wp->w_lines); | ||||
| @@ -5928,15 +5939,20 @@ void win_grid_alloc(win_T *wp) | ||||
|       || grid->Rows != rows | ||||
|       || grid->Columns != cols) { | ||||
|     if (want_allocation) { | ||||
|       grid_alloc(grid, rows, cols, true, true); | ||||
|       grid_alloc(grid, rows, cols, wp->w_grid.valid, wp->w_grid.valid); | ||||
|       grid->valid = true; | ||||
|     } else { | ||||
|       // Single grid mode, all rendering will be redirected to default_grid. | ||||
|       // Only keep track of the size and offset of the window. | ||||
|       grid_free(grid); | ||||
|       grid->Rows = rows; | ||||
|       grid->Columns = cols; | ||||
|       grid->valid = false; | ||||
|     } | ||||
|     was_resized = true; | ||||
|   } else if (want_allocation && has_allocation && !wp->w_grid.valid) { | ||||
|     grid_invalidate(grid); | ||||
|     grid->valid = true; | ||||
|   } | ||||
|  | ||||
|   grid->row_offset = wp->w_winrow; | ||||
| @@ -5946,7 +5962,7 @@ void win_grid_alloc(win_T *wp) | ||||
|   // - a grid was just resized | ||||
|   // - screen_resize was called and all grid sizes must be sent | ||||
|   // - the UI wants multigrid event (necessary) | ||||
|   if ((send_grid_resize || was_resized) && ui_has(kUIMultigrid)) { | ||||
|   if ((send_grid_resize || was_resized) && want_allocation) { | ||||
|     ui_call_grid_resize(grid->handle, grid->Columns, grid->Rows); | ||||
|   } | ||||
| } | ||||
| @@ -6008,7 +6024,7 @@ retry: | ||||
|  | ||||
|   // win_new_shellsize will recompute floats position, but tell the | ||||
|   // compositor to not redraw them yet | ||||
|   ui_comp_invalidate_screen(); | ||||
|   ui_comp_set_screen_valid(false); | ||||
|  | ||||
|   win_new_shellsize();      /* fit the windows in the new sized shell */ | ||||
|  | ||||
| @@ -6162,6 +6178,8 @@ void screenclear(void) | ||||
|   } | ||||
|  | ||||
|   ui_call_grid_clear(1);  // clear the display | ||||
|   ui_comp_set_screen_valid(true); | ||||
|  | ||||
|   clear_cmdline = false; | ||||
|   mode_displayed = false; | ||||
|  | ||||
| @@ -6170,6 +6188,11 @@ void screenclear(void) | ||||
|   redraw_tabline = true; | ||||
|   redraw_popupmenu = true; | ||||
|   pum_invalidate(); | ||||
|   FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { | ||||
|     if (wp->w_floating) { | ||||
|       wp->w_redr_type = CLEAR; | ||||
|     } | ||||
|   } | ||||
|   if (must_redraw == CLEAR) { | ||||
|     must_redraw = NOT_VALID;  // no need to clear again | ||||
|   } | ||||
| @@ -6559,14 +6582,14 @@ int showmode(void) | ||||
|   if (VIsual_active) | ||||
|     clear_showcmd(); | ||||
|  | ||||
|   /* If the last window has no status line, the ruler is after the mode | ||||
|    * message and must be redrawn */ | ||||
|   if (redrawing() | ||||
|       && lastwin->w_status_height == 0 | ||||
|       ) | ||||
|     win_redr_ruler(lastwin, TRUE); | ||||
|   redraw_cmdline = FALSE; | ||||
|   clear_cmdline = FALSE; | ||||
|   // If the last window has no status line, the ruler is after the mode | ||||
|   // message and must be redrawn | ||||
|   win_T *last = lastwin_nofloating(); | ||||
|   if (redrawing() && last->w_status_height == 0) { | ||||
|     win_redr_ruler(last, true); | ||||
|   } | ||||
|   redraw_cmdline = false; | ||||
|   clear_cmdline = false; | ||||
|  | ||||
|   return length; | ||||
| } | ||||
| @@ -7150,7 +7173,7 @@ void screen_resize(int width, int height) | ||||
|   check_shellsize(); | ||||
|   height = Rows; | ||||
|   width = Columns; | ||||
|   ui_resize(width, height); | ||||
|   ui_call_grid_resize(1, width, height); | ||||
|  | ||||
|   send_grid_resize = true; | ||||
|  | ||||
|   | ||||
| @@ -165,6 +165,7 @@ UI *tui_start(void) | ||||
|  | ||||
|   memset(ui->ui_ext, 0, sizeof(ui->ui_ext)); | ||||
|   ui->ui_ext[kUILinegrid] = true; | ||||
|   ui->ui_ext[kUITermColors] = true; | ||||
|  | ||||
|   return ui_bridge_attach(ui, tui_main, tui_scheduler); | ||||
| } | ||||
|   | ||||
| @@ -219,11 +219,6 @@ void ui_schedule_refresh(void) | ||||
|   loop_schedule(&main_loop, event_create(ui_refresh_event, 0)); | ||||
| } | ||||
|  | ||||
| void ui_resize(int width, int height) | ||||
| { | ||||
|   ui_call_grid_resize(1, width, height); | ||||
| } | ||||
|  | ||||
| void ui_default_colors_set(void) | ||||
| { | ||||
|   ui_call_default_colors_set(normal_fg, normal_bg, normal_sp, | ||||
| @@ -249,7 +244,7 @@ void ui_attach_impl(UI *ui) | ||||
|   if (ui_count == MAX_UI_COUNT) { | ||||
|     abort(); | ||||
|   } | ||||
|   if (!ui->ui_ext[kUIMultigrid]) { | ||||
|   if (!ui->ui_ext[kUIMultigrid] && !ui->ui_ext[kUIFloatDebug]) { | ||||
|     ui_comp_attach(ui); | ||||
|   } | ||||
|  | ||||
| @@ -299,7 +294,7 @@ void ui_detach_impl(UI *ui) | ||||
|     ui_schedule_refresh(); | ||||
|   } | ||||
|  | ||||
|   if (!ui->ui_ext[kUIMultigrid]) { | ||||
|   if (!ui->ui_ext[kUIMultigrid] && !ui->ui_ext[kUIFloatDebug]) { | ||||
|     ui_comp_detach(ui); | ||||
|   } | ||||
| } | ||||
| @@ -310,7 +305,7 @@ void ui_set_ext_option(UI *ui, UIExtension ext, bool active) | ||||
|     ui_refresh(); | ||||
|     return; | ||||
|   } | ||||
|   if (ui->option_set) { | ||||
|   if (ui->option_set && (ui_ext_names[ext][0] != '_' || active)) { | ||||
|     ui->option_set(ui, cstr_as_string((char *)ui_ext_names[ext]), | ||||
|                    BOOLEAN_OBJ(active)); | ||||
|   } | ||||
| @@ -383,7 +378,7 @@ int ui_current_col(void) | ||||
| void ui_flush(void) | ||||
| { | ||||
|   cmdline_ui_flush(); | ||||
|   win_ui_flush(); | ||||
|   win_ui_flush_positions(); | ||||
|   msg_ext_ui_flush(); | ||||
|  | ||||
|   if (pending_cursor_update) { | ||||
| @@ -405,7 +400,6 @@ void ui_flush(void) | ||||
|   ui_call_flush(); | ||||
| } | ||||
|  | ||||
|  | ||||
| /// Check if current mode has changed. | ||||
| /// May update the shape of the cursor. | ||||
| void ui_cursor_shape(void) | ||||
| @@ -438,8 +432,10 @@ Array ui_array(void) | ||||
|     PUT(info, "height", INTEGER_OBJ(ui->height)); | ||||
|     PUT(info, "rgb", BOOLEAN_OBJ(ui->rgb)); | ||||
|     for (UIExtension j = 0; j < kUIExtCount; j++) { | ||||
|       if (ui_ext_names[j][0] != '_' || ui->ui_ext[j]) { | ||||
|         PUT(info, ui_ext_names[j], BOOLEAN_OBJ(ui->ui_ext[j])); | ||||
|       } | ||||
|     } | ||||
|     if (ui->inspect) { | ||||
|       ui->inspect(ui, &info); | ||||
|     } | ||||
| @@ -462,8 +458,14 @@ void ui_grid_resize(handle_T grid_handle, int width, int height, Error *error) | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   if (wp->w_floating) { | ||||
|     if (width != wp->w_width && height != wp->w_height) { | ||||
|       win_config_float(wp, (int)width, (int)height, wp->w_float_config); | ||||
|     } | ||||
|   } else { | ||||
|     // non-positive indicates no request | ||||
|     wp->w_height_request = (int)MAX(height, 0); | ||||
|     wp->w_width_request = (int)MAX(width, 0); | ||||
|     win_set_inner_size(wp); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -20,6 +20,7 @@ typedef enum { | ||||
|   kUIMultigrid, | ||||
|   kUIHlState, | ||||
|   kUITermColors, | ||||
|   kUIFloatDebug, | ||||
|   kUIExtCount, | ||||
| } UIExtension; | ||||
|  | ||||
| @@ -33,9 +34,9 @@ EXTERN const char *ui_ext_names[] INIT(= { | ||||
|   "ext_multigrid", | ||||
|   "ext_hlstate", | ||||
|   "ext_termcolors", | ||||
|   "_debug_float", | ||||
| }); | ||||
|  | ||||
|  | ||||
| typedef struct ui_t UI; | ||||
|  | ||||
| enum { | ||||
|   | ||||
| @@ -17,6 +17,7 @@ | ||||
| #include "nvim/ui.h" | ||||
| #include "nvim/highlight.h" | ||||
| #include "nvim/memory.h" | ||||
| #include "nvim/popupmnu.h" | ||||
| #include "nvim/ui_compositor.h" | ||||
| #include "nvim/ugrid.h" | ||||
| #include "nvim/screen.h" | ||||
| @@ -55,7 +56,6 @@ void ui_comp_init(void) | ||||
|  | ||||
|   compositor->rgb = true; | ||||
|   compositor->grid_resize = ui_comp_grid_resize; | ||||
|   compositor->grid_clear = ui_comp_grid_clear; | ||||
|   compositor->grid_scroll = ui_comp_grid_scroll; | ||||
|   compositor->grid_cursor_goto = ui_comp_grid_cursor_goto; | ||||
|   compositor->raw_line = ui_comp_raw_line; | ||||
| @@ -107,10 +107,12 @@ bool ui_comp_should_draw(void) | ||||
| /// TODO(bfredl): later on the compositor should just use win_float_pos events, | ||||
| /// though that will require slight event order adjustment: emit the win_pos | ||||
| /// events in the beginning of  update_screen(0), rather than in ui_flush() | ||||
| bool ui_comp_put_grid(ScreenGrid *grid, int row, int col, int height, int width) | ||||
| bool ui_comp_put_grid(ScreenGrid *grid, int row, int col, int height, int width, | ||||
|                       bool valid, bool on_top) | ||||
| { | ||||
|   bool moved; | ||||
|   if (grid->comp_index != 0) { | ||||
|     bool moved = (row != grid->comp_row) || (col != grid->comp_col); | ||||
|     moved = (row != grid->comp_row) || (col != grid->comp_col); | ||||
|     if (ui_comp_should_draw()) { | ||||
|       // Redraw the area covered by the old position, and is not covered | ||||
|       // by the new position. Disable the grid so that compose_area() will not | ||||
| @@ -134,8 +136,8 @@ bool ui_comp_put_grid(ScreenGrid *grid, int row, int col, int height, int width) | ||||
|     } | ||||
|     grid->comp_row = row; | ||||
|     grid->comp_col = col; | ||||
|     return moved; | ||||
|   } | ||||
|   } else { | ||||
|     moved = true; | ||||
| #ifndef NDEBUG | ||||
|     for (size_t i = 0; i < kv_size(layers); i++) { | ||||
|       if (kv_A(layers, i) == grid) { | ||||
| @@ -143,12 +145,32 @@ bool ui_comp_put_grid(ScreenGrid *grid, int row, int col, int height, int width) | ||||
|       } | ||||
|     } | ||||
| #endif | ||||
|  | ||||
|     size_t insert_at = kv_size(layers); | ||||
|     if (kv_A(layers, insert_at-1) == &pum_grid) { | ||||
|       insert_at--; | ||||
|     } | ||||
|     if (insert_at > 1 && !on_top) { | ||||
|       insert_at--; | ||||
|     } | ||||
|     // not found: new grid | ||||
|     kv_push(layers, grid); | ||||
|     if (insert_at < kv_size(layers)-1) { | ||||
|       for (size_t i = kv_size(layers)-1; i > insert_at; i--) { | ||||
|         kv_A(layers, i) = kv_A(layers, i-i); | ||||
|       } | ||||
|       kv_A(layers, insert_at) = grid; | ||||
|     } | ||||
|  | ||||
|     grid->comp_row = row; | ||||
|     grid->comp_col = col; | ||||
|   grid->comp_index = kv_size(layers)-1; | ||||
|   return true; | ||||
|     grid->comp_index = insert_at; | ||||
|   } | ||||
|   if (moved && valid && ui_comp_should_draw()) { | ||||
|     compose_area(grid->comp_row, grid->comp_row+grid->Rows, | ||||
|                  grid->comp_col, grid->comp_col+grid->Columns); | ||||
|   } | ||||
|   return moved; | ||||
| } | ||||
|  | ||||
| void ui_comp_remove_grid(ScreenGrid *grid) | ||||
| @@ -194,6 +216,26 @@ bool ui_comp_set_grid(handle_T handle) | ||||
|   return false; | ||||
| } | ||||
|  | ||||
| static void ui_comp_raise_grid(ScreenGrid *grid, size_t new_index) | ||||
| { | ||||
|   size_t old_index = grid->comp_index; | ||||
|   for (size_t i = old_index; i < new_index; i++) { | ||||
|     kv_A(layers, i) = kv_A(layers, i+1); | ||||
|     kv_A(layers, i)->comp_index = i; | ||||
|   } | ||||
|   kv_A(layers, new_index) = grid; | ||||
|   grid->comp_index = new_index; | ||||
|   for (size_t i = old_index; i < new_index; i++) { | ||||
|     ScreenGrid *grid2 = kv_A(layers, i); | ||||
|     int startcol = MAX(grid->comp_col, grid2->comp_col); | ||||
|     int endcol = MIN(grid->comp_col+grid->Columns, | ||||
|                      grid2->comp_col+grid2->Columns); | ||||
|     compose_area(MAX(grid->comp_row, grid2->comp_row), | ||||
|                  MIN(grid->comp_row+grid->Rows, grid2->comp_row+grid2->Rows), | ||||
|                  startcol, endcol); | ||||
|   } | ||||
| } | ||||
|  | ||||
| static void ui_comp_grid_cursor_goto(UI *ui, Integer grid_handle, | ||||
|                                      Integer r, Integer c) | ||||
| { | ||||
| @@ -203,6 +245,18 @@ static void ui_comp_grid_cursor_goto(UI *ui, Integer grid_handle, | ||||
|   int cursor_row = curgrid->comp_row+(int)r; | ||||
|   int cursor_col = curgrid->comp_col+(int)c; | ||||
|  | ||||
|   // TODO(bfredl): maybe not the best time to do this, for efficiency we | ||||
|   // should configure all grids before entering win_update() | ||||
|   if (curgrid != &default_grid) { | ||||
|     size_t new_index = kv_size(layers)-1; | ||||
|     if (kv_A(layers, new_index) == &pum_grid) { | ||||
|       new_index--; | ||||
|     } | ||||
|     if (curgrid->comp_index < new_index) { | ||||
|       ui_comp_raise_grid(curgrid, new_index); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   if (cursor_col >= default_grid.Columns || cursor_row >= default_grid.Rows) { | ||||
|     // TODO(bfredl): this happens with 'writedelay', refactor? | ||||
|     // abort(); | ||||
| @@ -211,6 +265,18 @@ static void ui_comp_grid_cursor_goto(UI *ui, Integer grid_handle, | ||||
|   ui_composed_call_grid_cursor_goto(1, cursor_row, cursor_col); | ||||
| } | ||||
|  | ||||
| ScreenGrid *ui_comp_mouse_focus(int row, int col) | ||||
| { | ||||
|   // TODO(bfredl): click "through" unfocusable grids? | ||||
|   for (ssize_t i = (ssize_t)kv_size(layers)-1; i > 0; i--) { | ||||
|     ScreenGrid *grid = kv_A(layers, i); | ||||
|     if (row >= grid->comp_row && row < grid->comp_row+grid->Rows | ||||
|         && col >= grid->comp_col && col < grid->comp_col+grid->Columns) { | ||||
|       return grid; | ||||
|     } | ||||
|   } | ||||
|   return NULL; | ||||
| } | ||||
|  | ||||
| /// Baseline implementation. This is always correct, but we can sometimes | ||||
| /// do something more efficient (where efficiency means smaller deltas to | ||||
| @@ -260,7 +326,7 @@ static void compose_line(Integer row, Integer startcol, Integer endcol, | ||||
|     memcpy(attrbuf+(col-startcol), grid->attrs+off, n * sizeof(*attrbuf)); | ||||
|  | ||||
|     // 'pumblend' | ||||
|     if (grid != &default_grid && p_pb) { | ||||
|     if (grid == &pum_grid && p_pb) { | ||||
|       for (int i = col-(int)startcol; i < until-startcol; i++) { | ||||
|         bool thru = strequal((char *)linebuf[i], " ");  // negative space | ||||
|         attrbuf[i] = (sattr_T)hl_blend_attrs(bg_attrs[i], attrbuf[i], thru); | ||||
| @@ -348,7 +414,8 @@ static void ui_comp_raw_line(UI *ui, Integer grid, Integer row, | ||||
|   assert(row < default_grid.Rows); | ||||
|   assert(clearcol <= default_grid.Columns); | ||||
|   if (flags & kLineFlagInvalid | ||||
|       || kv_size(layers) > (p_pb ? 1 : curgrid->comp_index+1)) { | ||||
|       || kv_size(layers) > curgrid->comp_index+1 | ||||
|       || (p_pb && curgrid == &pum_grid)) { | ||||
|     compose_line(row, startcol, clearcol, flags); | ||||
|   } else { | ||||
|     ui_composed_call_raw_line(1, row, startcol, endcol, clearcol, clearattr, | ||||
| @@ -359,17 +426,9 @@ static void ui_comp_raw_line(UI *ui, Integer grid, Integer row, | ||||
| /// The screen is invalid and will soon be cleared | ||||
| /// | ||||
| /// Don't redraw floats until screen is cleared | ||||
| void ui_comp_invalidate_screen(void) | ||||
| void ui_comp_set_screen_valid(bool valid) | ||||
| { | ||||
|   valid_screen = false; | ||||
| } | ||||
|  | ||||
| static void ui_comp_grid_clear(UI *ui, Integer grid) | ||||
| { | ||||
|   // By design, only first grid uses clearing. | ||||
|   assert(grid == 1); | ||||
|   ui_composed_call_grid_clear(1); | ||||
|   valid_screen = true; | ||||
|   valid_screen = valid; | ||||
| } | ||||
|  | ||||
| // TODO(bfredl): These events are somewhat of a hack. multiline messages | ||||
|   | ||||
| @@ -6,6 +6,7 @@ | ||||
| #include <stdbool.h> | ||||
|  | ||||
| #include "nvim/api/private/handle.h" | ||||
| #include "nvim/api/private/helpers.h" | ||||
| #include "nvim/vim.h" | ||||
| #include "nvim/ascii.h" | ||||
| #include "nvim/window.h" | ||||
| @@ -48,6 +49,7 @@ | ||||
| #include "nvim/terminal.h" | ||||
| #include "nvim/undo.h" | ||||
| #include "nvim/ui.h" | ||||
| #include "nvim/ui_compositor.h" | ||||
| #include "nvim/os/os.h" | ||||
|  | ||||
|  | ||||
| @@ -203,14 +205,24 @@ newwindow: | ||||
|             wp = wp->w_next; | ||||
|         } | ||||
|       } else { | ||||
|         if (nchar == 'W') {                         /* go to previous window */ | ||||
|         if (nchar == 'W') {  // go to previous window | ||||
|           wp = curwin->w_prev; | ||||
|           if (wp == NULL) | ||||
|             wp = lastwin;                           /* wrap around */ | ||||
|         } else {                                  /* go to next window */ | ||||
|           if (wp == NULL) { | ||||
|             wp = lastwin;  // wrap around | ||||
|           } | ||||
|           while (wp != NULL && wp->w_floating | ||||
|                  && !wp->w_float_config.focusable) { | ||||
|             wp = wp->w_prev; | ||||
|           } | ||||
|         } else {  // go to next window | ||||
|           wp = curwin->w_next; | ||||
|           if (wp == NULL) | ||||
|             wp = firstwin;                          /* wrap around */ | ||||
|           while (wp != NULL && wp->w_floating | ||||
|                  && !wp->w_float_config.focusable) { | ||||
|             wp = wp->w_next; | ||||
|           } | ||||
|           if (wp == NULL) { | ||||
|             wp = firstwin;  // wrap around | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|       win_goto(wp); | ||||
| @@ -281,7 +293,7 @@ newwindow: | ||||
|   /* cursor to bottom-right window */ | ||||
|   case 'b': | ||||
|   case Ctrl_B: | ||||
|     win_goto(lastwin); | ||||
|     win_goto(lastwin_nofloating()); | ||||
|     break; | ||||
|  | ||||
|   /* cursor to last accessed (previous) window */ | ||||
| @@ -483,6 +495,22 @@ wingotofile: | ||||
|       cmdmod.tab = tabpage_index(curtab) + 1; | ||||
|       nchar = xchar; | ||||
|       goto wingotofile; | ||||
|  | ||||
|     case 'e': | ||||
|       if (curwin->w_floating || !ui_has(kUIMultigrid)) { | ||||
|         beep_flush(); | ||||
|         break; | ||||
|       } | ||||
|       FloatConfig config = FLOAT_CONFIG_INIT; | ||||
|       config.external = true; | ||||
|       Error err = ERROR_INIT; | ||||
|       if (!win_new_float(curwin, curwin->w_width, curwin->w_height, config, | ||||
|                          &err)) { | ||||
|         EMSG(err.msg); | ||||
|         api_clear_error(&err); | ||||
|         beep_flush(); | ||||
|       } | ||||
|       break; | ||||
|     default: | ||||
|       beep_flush(); | ||||
|       break; | ||||
| @@ -504,6 +532,302 @@ static void cmd_with_count(char *cmd, char_u *bufp, size_t bufsize, | ||||
|   } | ||||
| } | ||||
|  | ||||
| /// Create a new float. | ||||
| /// | ||||
| /// if wp == NULL allocate a new window, otherwise turn existing window into a | ||||
| /// float. It must then already belong to the current tabpage! | ||||
| /// | ||||
| /// config must already have been validated! | ||||
| win_T *win_new_float(win_T *wp, int width, int height, FloatConfig config, | ||||
|                      Error *err) | ||||
| { | ||||
|   bool new = false; | ||||
|   if (wp == NULL) { | ||||
|     new = true; | ||||
|     wp = win_alloc(lastwin_nofloating(), false); | ||||
|     win_init(wp, curwin, 0); | ||||
|   } else { | ||||
|     assert(!wp->w_floating); | ||||
|     if (firstwin == wp && lastwin_nofloating() == wp) { | ||||
|       // last non-float | ||||
|       api_set_error(err, kErrorTypeException, | ||||
|                     "Cannot change last window into float"); | ||||
|       return NULL; | ||||
|     } else if (!win_valid(wp)) { | ||||
|       api_set_error(err, kErrorTypeException, | ||||
|                     "Cannot change window from different tabpage into float"); | ||||
|       return NULL; | ||||
|     } | ||||
|     int dir; | ||||
|     winframe_remove(wp, &dir, NULL); | ||||
|     xfree(wp->w_frame); | ||||
|     wp->w_frame = NULL; | ||||
|     (void)win_comp_pos();  // recompute window positions | ||||
|     win_remove(wp, NULL); | ||||
|     win_append(lastwin_nofloating(), wp); | ||||
|   } | ||||
|   wp->w_floating = 1; | ||||
|   wp->w_status_height = 0; | ||||
|   wp->w_vsep_width = 0; | ||||
|   win_config_float(wp, width, height, config); | ||||
|   wp->w_pos_changed = true; | ||||
|   redraw_win_later(wp, VALID); | ||||
|   if (new) { | ||||
|     win_enter(wp, false); | ||||
|   } | ||||
|   return wp; | ||||
| } | ||||
|  | ||||
| void win_config_float(win_T *wp, int width, int height, | ||||
|                       FloatConfig config) | ||||
| { | ||||
|   wp->w_height = MAX(height, 1); | ||||
|   wp->w_width = MAX(width, 2); | ||||
|  | ||||
|   if (config.relative == kFloatRelativeCursor) { | ||||
|     config.relative = kFloatRelativeWindow; | ||||
|     config.row += curwin->w_wrow; | ||||
|     config.col += curwin->w_wcol; | ||||
|     config.window = curwin->handle; | ||||
|   } | ||||
|  | ||||
|   wp->w_float_config = config; | ||||
|  | ||||
|   if (!ui_has(kUIMultigrid)) { | ||||
|     wp->w_height = MIN(wp->w_height, Rows-1); | ||||
|     wp->w_width = MIN(wp->w_width, Columns); | ||||
|   } | ||||
|  | ||||
|   win_set_inner_size(wp); | ||||
|   must_redraw = MAX(must_redraw, VALID); | ||||
|   wp->w_pos_changed = true; | ||||
| } | ||||
|  | ||||
| static void ui_ext_win_position(win_T *wp) | ||||
| { | ||||
|   if (!wp->w_floating) { | ||||
|     ui_call_win_pos(wp->w_grid.handle, wp->handle, wp->w_winrow, | ||||
|                     wp->w_wincol, wp->w_width, wp->w_height); | ||||
|     return; | ||||
|   } | ||||
|   const char *const anchor_str[] = { | ||||
|     "NW", | ||||
|     "NE", | ||||
|     "SW", | ||||
|     "SE" | ||||
|   }; | ||||
|  | ||||
|   FloatConfig c = wp->w_float_config; | ||||
|   if (!c.external) { | ||||
|     ScreenGrid *grid = &default_grid; | ||||
|     int row = c.row, col = c.col; | ||||
|     if (c.relative == kFloatRelativeWindow) { | ||||
|       Error dummy = ERROR_INIT; | ||||
|       win_T *win = find_window_by_handle(c.window, &dummy); | ||||
|       if (win) { | ||||
|         grid = &win->w_grid; | ||||
|         screen_adjust_grid(&grid, &row, &col); | ||||
|       } | ||||
|       api_clear_error(&dummy); | ||||
|     } | ||||
|     if (ui_has(kUIMultigrid)) { | ||||
|       String anchor = cstr_to_string(anchor_str[c.anchor]); | ||||
|       ui_call_win_float_pos(wp->w_grid.handle, wp->handle, anchor, grid->handle, | ||||
|                             row, col, c.focusable); | ||||
|     } else { | ||||
|       // TODO(bfredl): ideally, compositor should work like any multigrid UI | ||||
|       // and use standard win_pos events. | ||||
|       bool east = c.anchor & kFloatAnchorEast; | ||||
|       bool south = c.anchor & kFloatAnchorSouth; | ||||
|  | ||||
|       row -= (south ? wp->w_height : 0); | ||||
|       col -= (east ? wp->w_width : 0); | ||||
|       row = MAX(MIN(row, Rows-wp->w_height-1), 0); | ||||
|       col = MAX(MIN(col, Columns-wp->w_width), 0); | ||||
|       wp->w_winrow = row; | ||||
|       wp->w_wincol = col; | ||||
|       bool valid = (wp->w_redr_type == 0); | ||||
|       bool on_top = (curwin == wp) || !curwin->w_floating; | ||||
|       ui_comp_put_grid(&wp->w_grid, row, col, wp->w_height, wp->w_width, | ||||
|                        valid, on_top); | ||||
|       if (!valid) { | ||||
|         wp->w_grid.valid = false; | ||||
|         redraw_win_later(wp, NOT_VALID); | ||||
|       } | ||||
|     } | ||||
|   } else { | ||||
|     ui_call_win_external_pos(wp->w_grid.handle, wp->handle); | ||||
|   } | ||||
|  | ||||
| } | ||||
|  | ||||
|  | ||||
| static bool parse_float_anchor(String anchor, FloatAnchor *out) | ||||
| { | ||||
|   if (anchor.size == 0) { | ||||
|     *out = (FloatAnchor)0; | ||||
|   } | ||||
|   char *str = anchor.data; | ||||
|   if (!STRICMP(str, "NW")) { | ||||
|     *out = kFloatAnchorNW; | ||||
|   } else if (!STRICMP(str, "NE")) { | ||||
|     *out = kFloatAnchorNE; | ||||
|   } else if (!STRICMP(str, "SW")) { | ||||
|     *out = kFloatAnchorSW; | ||||
|   } else if (!STRICMP(str, "SE")) { | ||||
|     *out = kFloatAnchorSE; | ||||
|   } else { | ||||
|     return false; | ||||
|   } | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| static bool parse_float_relative(String relative, FloatRelative *out) | ||||
| { | ||||
|   if (relative.size == 0) { | ||||
|     *out = (FloatRelative)0; | ||||
|   } | ||||
|   char *str = relative.data; | ||||
|   if (!STRICMP(str, "editor")) { | ||||
|     *out = kFloatRelativeEditor; | ||||
|   }  else if (!STRICMP(str, "win")) { | ||||
|     *out = kFloatRelativeWindow; | ||||
|   } else if (!STRICMP(str, "cursor")) { | ||||
|     *out = kFloatRelativeCursor; | ||||
|   } else { | ||||
|     return false; | ||||
|   } | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| bool parse_float_config(Dictionary config, FloatConfig *out, bool reconf, | ||||
|                         Error *err) | ||||
| { | ||||
|   bool has_row = false, has_col = false, has_relative = false; | ||||
|   bool has_external = false, has_window = false; | ||||
|  | ||||
|   for (size_t i = 0; i < config.size; i++) { | ||||
|     char *key = config.items[i].key.data; | ||||
|     Object val = config.items[i].value; | ||||
|     if (!strcmp(key, "row")) { | ||||
|       has_row = true; | ||||
|       if (val.type == kObjectTypeInteger) { | ||||
|         out->row = val.data.integer; | ||||
|       } else if (val.type == kObjectTypeFloat) { | ||||
|         out->row = val.data.floating; | ||||
|       } else { | ||||
|         api_set_error(err, kErrorTypeValidation, | ||||
|                       "'row' option has to be Integer or Float"); | ||||
|         return false; | ||||
|       } | ||||
|     } else if (!strcmp(key, "col")) { | ||||
|       has_col = true; | ||||
|       if (val.type == kObjectTypeInteger) { | ||||
|         out->col = val.data.integer; | ||||
|       } else if (val.type == kObjectTypeFloat) { | ||||
|         out->col = val.data.floating; | ||||
|       } else { | ||||
|         api_set_error(err, kErrorTypeValidation, | ||||
|                       "'col' option has to be Integer or Float"); | ||||
|         return false; | ||||
|       } | ||||
|     } else if (!strcmp(key, "anchor")) { | ||||
|       if (val.type != kObjectTypeString) { | ||||
|         api_set_error(err, kErrorTypeValidation, | ||||
|                       "'anchor' option has to be String"); | ||||
|         return false; | ||||
|       } | ||||
|       if (!parse_float_anchor(val.data.string, &out->anchor)) { | ||||
|         api_set_error(err, kErrorTypeValidation, | ||||
|                       "Invalid value of 'anchor' option"); | ||||
|         return false; | ||||
|       } | ||||
|     } else if (!strcmp(key, "relative")) { | ||||
|       has_relative = true; | ||||
|       if (val.type != kObjectTypeString) { | ||||
|         api_set_error(err, kErrorTypeValidation, | ||||
|                       "'relative' option has to be String"); | ||||
|         return false; | ||||
|       } | ||||
|       if (!parse_float_relative(val.data.string, &out->relative)) { | ||||
|         api_set_error(err, kErrorTypeValidation, | ||||
|                       "Invalid value of 'relative' option"); | ||||
|         return false; | ||||
|       } | ||||
|     } else if (!strcmp(key, "win")) { | ||||
|       has_window = true; | ||||
|       if (val.type != kObjectTypeInteger | ||||
|           && val.type != kObjectTypeWindow) { | ||||
|         api_set_error(err, kErrorTypeValidation, | ||||
|                       "'win' option has to be Integer or Window"); | ||||
|         return false; | ||||
|       } | ||||
|       out->window = val.data.integer; | ||||
|     } else if (!strcmp(key, "external")) { | ||||
|       if (val.type == kObjectTypeInteger) { | ||||
|         out->external = val.data.integer; | ||||
|       } else if (val.type == kObjectTypeBoolean) { | ||||
|         out->external = val.data.boolean; | ||||
|       } else { | ||||
|         api_set_error(err, kErrorTypeValidation, | ||||
|                       "'external' option has to be Boolean"); | ||||
|         return false; | ||||
|       } | ||||
|       has_external = out->external; | ||||
|     } else if (!strcmp(key, "focusable")) { | ||||
|       if (val.type == kObjectTypeInteger) { | ||||
|         out->focusable = val.data.integer; | ||||
|       } else if (val.type == kObjectTypeBoolean) { | ||||
|         out->focusable = val.data.boolean; | ||||
|       } else { | ||||
|         api_set_error(err, kErrorTypeValidation, | ||||
|                       "'focusable' option has to be Boolean"); | ||||
|         return false; | ||||
|       } | ||||
|     } else { | ||||
|       api_set_error(err, kErrorTypeValidation, | ||||
|                     "Invalid options key '%s'", key); | ||||
|       return false; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   if (has_window && !(has_relative && out->relative == kFloatRelativeWindow)) { | ||||
|     api_set_error(err, kErrorTypeValidation, | ||||
|                   "'win' option is only valid with relative='win'"); | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   if ((has_relative && out->relative == kFloatRelativeWindow) | ||||
|       && (!has_window || out->window == 0)) { | ||||
|     out->window = curwin->handle; | ||||
|   } | ||||
|  | ||||
|   if (has_relative && has_external) { | ||||
|     api_set_error(err, kErrorTypeValidation, | ||||
|                   "Only one of 'relative' and 'external' should be used"); | ||||
|     return false; | ||||
|   } else if (has_relative) { | ||||
|     out->external = false; | ||||
|   } else if (!reconf && !has_relative && !has_external) { | ||||
|     api_set_error(err, kErrorTypeValidation, | ||||
|                   "One of 'relative' and 'external' must be used"); | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   if (out->external && !ui_has(kUIMultigrid)) { | ||||
|     api_set_error(err, kErrorTypeValidation, | ||||
|                   "UI doesn't support external windows"); | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   if (has_relative != has_row || has_row != has_col) { | ||||
|     api_set_error(err, kErrorTypeValidation, "All of 'relative', 'row', and " | ||||
|                   "'col' has to be specified at once"); | ||||
|     return false; | ||||
|   } | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * split the current window, implements CTRL-W s and :split | ||||
|  * | ||||
| @@ -566,16 +890,20 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) | ||||
|   int wmh1; | ||||
|   bool did_set_fraction = false; | ||||
|  | ||||
|   if (flags & WSP_TOP) | ||||
|   if (flags & WSP_TOP) { | ||||
|     oldwin = firstwin; | ||||
|   else if (flags & WSP_BOT) | ||||
|     oldwin = lastwin; | ||||
|   else | ||||
|   } else if (flags & WSP_BOT || curwin->w_floating) { | ||||
|     // can't split float, use last nonfloating window instead | ||||
|     oldwin = lastwin_nofloating(); | ||||
|   } else { | ||||
|     oldwin = curwin; | ||||
|   } | ||||
|  | ||||
|   /* add a status line when p_ls == 1 and splitting the first window */ | ||||
|   if (ONE_WINDOW && p_ls == 1 && oldwin->w_status_height == 0) { | ||||
|     if (oldwin->w_height <= p_wmh && new_wp == NULL) { | ||||
|   bool new_in_layout = (new_wp == NULL || new_wp->w_floating); | ||||
|  | ||||
|   // add a status line when p_ls == 1 and splitting the first window | ||||
|   if (one_nonfloat() && p_ls == 1 && oldwin->w_status_height == 0) { | ||||
|     if (oldwin->w_height <= p_wmh && new_in_layout) { | ||||
|       EMSG(_(e_noroom)); | ||||
|       return FAIL; | ||||
|     } | ||||
| @@ -624,7 +952,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) | ||||
|       available = oldwin->w_frame->fr_width; | ||||
|       needed += minwidth; | ||||
|     } | ||||
|     if (available < needed && new_wp == NULL) { | ||||
|     if (available < needed && new_in_layout) { | ||||
|       EMSG(_(e_noroom)); | ||||
|       return FAIL; | ||||
|     } | ||||
| @@ -702,7 +1030,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) | ||||
|       available = oldwin->w_frame->fr_height; | ||||
|       needed += minheight; | ||||
|     } | ||||
|     if (available < needed && new_wp == NULL) { | ||||
|     if (available < needed && new_in_layout) { | ||||
|       EMSG(_(e_noroom)); | ||||
|       return FAIL; | ||||
|     } | ||||
| @@ -790,6 +1118,9 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) | ||||
|  | ||||
|     /* make the contents of the new window the same as the current one */ | ||||
|     win_init(wp, curwin, flags); | ||||
|   } else if (wp->w_floating) { | ||||
|     new_frame(wp); | ||||
|     wp->w_floating = false; | ||||
|   } | ||||
|  | ||||
|   /* | ||||
| @@ -1192,7 +1523,13 @@ static void win_exchange(long Prenum) | ||||
|   win_T       *wp2; | ||||
|   int temp; | ||||
|  | ||||
|   if (ONE_WINDOW) {        /* just one window */ | ||||
|   if (curwin->w_floating) { | ||||
|     EMSG(e_floatexchange); | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   if (firstwin == curwin && lastwin_nofloating() == curwin) { | ||||
|     // just one window | ||||
|     beep_flush(); | ||||
|     return; | ||||
|   } | ||||
| @@ -1282,7 +1619,13 @@ static void win_rotate(int upwards, int count) | ||||
|   frame_T     *frp; | ||||
|   int n; | ||||
|  | ||||
|   if (ONE_WINDOW) {            /* nothing to do */ | ||||
|   if (curwin->w_floating) { | ||||
|     EMSG(e_floatexchange); | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   if (firstwin == curwin && lastwin_nofloating() == curwin) { | ||||
|     // nothing to do | ||||
|     beep_flush(); | ||||
|     return; | ||||
|   } | ||||
| @@ -1355,16 +1698,27 @@ static void win_rotate(int upwards, int count) | ||||
|  */ | ||||
| static void win_totop(int size, int flags) | ||||
| { | ||||
|   int dir; | ||||
|   int dir = 0; | ||||
|   int height = curwin->w_height; | ||||
|  | ||||
|   if (ONE_WINDOW) { | ||||
|   if (firstwin == curwin && lastwin_nofloating() == curwin) { | ||||
|     beep_flush(); | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   /* Remove the window and frame from the tree of frames. */ | ||||
|   if (curwin->w_floating) { | ||||
|     ui_comp_remove_grid(&curwin->w_grid); | ||||
|     if (ui_has(kUIMultigrid)) { | ||||
|       curwin->w_pos_changed = true; | ||||
|     } else { | ||||
|       // No longer a float, a non-multigrid UI shouldn't draw it as such | ||||
|       ui_call_win_hide(curwin->w_grid.handle); | ||||
|       win_free_grid(curwin, false); | ||||
|     } | ||||
|   } else { | ||||
|     // Remove the window and frame from the tree of frames. | ||||
|     (void)winframe_remove(curwin, &dir, NULL); | ||||
|   } | ||||
|   win_remove(curwin, NULL); | ||||
|   last_status(FALSE);       /* may need to remove last status line */ | ||||
|   (void)win_comp_pos();     /* recompute window positions */ | ||||
| @@ -1795,13 +2149,13 @@ static bool last_window(void) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT | ||||
| } | ||||
|  | ||||
| /// Check that current tab page contains no more then one window other than | ||||
| /// "aucmd_win". | ||||
| /// "aucmd_win". Only counts floating window if it is current. | ||||
| bool one_window(void) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT | ||||
| { | ||||
|   bool seen_one = false; | ||||
|  | ||||
|   FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { | ||||
|     if (wp != aucmd_win) { | ||||
|     if (wp != aucmd_win && (!wp->w_floating || wp == curwin)) { | ||||
|       if (seen_one) { | ||||
|         return false; | ||||
|       } | ||||
| @@ -1811,6 +2165,20 @@ bool one_window(void) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| /// Like ONE_WINDOW but only considers non-floating windows | ||||
| bool one_nonfloat(void) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT | ||||
| { | ||||
|   return firstwin->w_next == NULL || firstwin->w_next->w_floating; | ||||
| } | ||||
|  | ||||
| /// if wp is the last non-floating window | ||||
| /// | ||||
| /// always false for a floating window | ||||
| bool last_nonfloat(win_T *wp) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT | ||||
| { | ||||
|   return firstwin == wp && !(wp->w_next && !wp->w_floating); | ||||
| } | ||||
|  | ||||
| /// Close the possibly last window in a tab page. | ||||
| /// | ||||
| /// @param  win          window to close | ||||
| @@ -1882,9 +2250,9 @@ int win_close(win_T *win, bool free_buf) | ||||
|   int dir; | ||||
|   bool help_window = false; | ||||
|   tabpage_T   *prev_curtab = curtab; | ||||
|   frame_T *win_frame = win->w_frame->fr_parent; | ||||
|   frame_T *win_frame = win->w_floating ? NULL : win->w_frame->fr_parent; | ||||
|  | ||||
|   if (last_window()) { | ||||
|   if (last_window() && !win->w_floating) { | ||||
|     EMSG(_("E444: Cannot close last window")); | ||||
|     return FAIL; | ||||
|   } | ||||
| @@ -1897,10 +2265,17 @@ int win_close(win_T *win, bool free_buf) | ||||
|     EMSG(_("E813: Cannot close autocmd window")); | ||||
|     return FAIL; | ||||
|   } | ||||
|   if ((firstwin == aucmd_win || lastwin == aucmd_win) && one_window()) { | ||||
|   if ((firstwin == aucmd_win || lastwin_nofloating() == aucmd_win) | ||||
|       && one_window()) { | ||||
|     EMSG(_("E814: Cannot close window, only autocmd window would remain")); | ||||
|     return FAIL; | ||||
|   } | ||||
|   if ((firstwin == win && lastwin_nofloating() == win) | ||||
|       && lastwin->w_floating) { | ||||
|     // TODO(bfredl): we might close the float also instead | ||||
|     EMSG(e_floatonly); | ||||
|     return FAIL; | ||||
|   } | ||||
|  | ||||
|   /* When closing the last window in a tab page first go to another tab page | ||||
|    * and then close the window and the tab page to avoid that curwin and | ||||
| @@ -1921,7 +2296,15 @@ int win_close(win_T *win, bool free_buf) | ||||
|      * Guess which window is going to be the new current window. | ||||
|      * This may change because of the autocommands (sigh). | ||||
|      */ | ||||
|     if (!win->w_floating) { | ||||
|       wp = frame2win(win_altframe(win, NULL)); | ||||
|     } else { | ||||
|       if (win_valid(prevwin)) { | ||||
|         wp = prevwin; | ||||
|       } else { | ||||
|         wp = curtab->tp_firstwin; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     /* | ||||
|      * Be careful: If autocommands delete the window or cause this window | ||||
| @@ -1949,6 +2332,27 @@ int win_close(win_T *win, bool free_buf) | ||||
|       return FAIL; | ||||
|   } | ||||
|  | ||||
|   bool was_floating = win->w_floating; | ||||
|   if (ui_has(kUIMultigrid)) { | ||||
|     ui_call_win_close(win->w_grid.handle); | ||||
|   } | ||||
|  | ||||
|   if (win->w_floating) { | ||||
|     ui_comp_remove_grid(&win->w_grid); | ||||
|     if (win->w_float_config.external) { | ||||
|       for (tabpage_T *tp = first_tabpage; tp != NULL; tp = tp->tp_next) { | ||||
|         if (tp == curtab) { | ||||
|           continue; | ||||
|         } | ||||
|         if (tp->tp_curwin == win) { | ||||
|           // NB: an autocmd can still abort the closing of this window, | ||||
|           // bur carring out this change anyway shouldn't be a catastrophe. | ||||
|           tp->tp_curwin = tp->tp_firstwin; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|  | ||||
|   /* Free independent synblock before the buffer is freed. */ | ||||
|   if (win->w_buffer != NULL) | ||||
| @@ -1975,7 +2379,8 @@ int win_close(win_T *win, bool free_buf) | ||||
|  | ||||
|   if (only_one_window() && win_valid(win) && win->w_buffer == NULL | ||||
|       && (last_window() || curtab != prev_curtab | ||||
|           || close_last_window_tabpage(win, free_buf, prev_curtab))) { | ||||
|           || close_last_window_tabpage(win, free_buf, prev_curtab)) | ||||
|       && !win->w_floating) { | ||||
|     // Autocommands have closed all windows, quit now.  Restore | ||||
|     // curwin->w_buffer, otherwise writing ShaDa file may fail. | ||||
|     if (curwin->w_buffer == NULL) { | ||||
| @@ -1992,7 +2397,7 @@ int win_close(win_T *win, bool free_buf) | ||||
|   } | ||||
|   // Autocommands may have closed the window already, or closed the only | ||||
|   // other window or moved to another tab page. | ||||
|   if (!win_valid(win) || last_window() | ||||
|   if (!win_valid(win) || (!win->w_floating && last_window()) | ||||
|       || close_last_window_tabpage(win, free_buf, prev_curtab)) { | ||||
|     return FAIL; | ||||
|   } | ||||
| @@ -2041,6 +2446,8 @@ int win_close(win_T *win, bool free_buf) | ||||
|     // using the window. | ||||
|     check_cursor(); | ||||
|   } | ||||
|  | ||||
|   if (!was_floating) { | ||||
|     if (p_ea && (*p_ead == 'b' || *p_ead == dir)) { | ||||
|       // If the frame of the closed window contains the new current window, | ||||
|       // only resize that frame.  Otherwise resize all windows. | ||||
| @@ -2048,6 +2455,7 @@ int win_close(win_T *win, bool free_buf) | ||||
|     } else { | ||||
|       win_comp_pos(); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   if (close_curwin) { | ||||
|     win_enter_ext(wp, false, true, false, true, true); | ||||
| @@ -2167,10 +2575,18 @@ win_free_mem ( | ||||
|   frame_T     *frp; | ||||
|   win_T       *wp; | ||||
|  | ||||
|   /* Remove the window and its frame from the tree of frames. */ | ||||
|   if (!win->w_floating) { | ||||
|     // Remove the window and its frame from the tree of frames. | ||||
|     frp = win->w_frame; | ||||
|     wp = winframe_remove(win, dirp, tp); | ||||
|     xfree(frp); | ||||
|   } else { | ||||
|     if (win_valid(prevwin)) { | ||||
|       wp = prevwin; | ||||
|     } else { | ||||
|       wp = curtab->tp_firstwin; | ||||
|     } | ||||
|   } | ||||
|   win_free(win, tp); | ||||
|  | ||||
|   /* When deleting the current window of another tab page select a new | ||||
| @@ -2189,6 +2605,12 @@ void win_free_all(void) | ||||
|   while (first_tabpage->tp_next != NULL) | ||||
|     tabpage_close(TRUE); | ||||
|  | ||||
|   while (lastwin != NULL && lastwin->w_floating) { | ||||
|     win_T *wp = lastwin; | ||||
|     win_remove(lastwin, NULL); | ||||
|     (void)win_free_mem(wp, &dummy, NULL); | ||||
|   } | ||||
|  | ||||
|   if (aucmd_win != NULL) { | ||||
|     (void)win_free_mem(aucmd_win, &dummy, NULL); | ||||
|     aucmd_win = NULL; | ||||
| @@ -2870,11 +3292,19 @@ close_others ( | ||||
|   win_T       *nextwp; | ||||
|   int r; | ||||
|  | ||||
|   if (one_window()) { | ||||
|   if (curwin->w_floating) { | ||||
|     if (message && !autocmd_busy) { | ||||
|       EMSG(e_floatonly); | ||||
|     } | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   if (one_window() && !lastwin->w_floating) { | ||||
|     if (message | ||||
|         && !autocmd_busy | ||||
|         ) | ||||
|         ) { | ||||
|       MSG(_(m_onlyone)); | ||||
|     } | ||||
|     return; | ||||
|   } | ||||
|  | ||||
| @@ -3116,9 +3546,7 @@ int win_new_tabpage(int after, char_u *filename) | ||||
|  | ||||
|     redraw_all_later(NOT_VALID); | ||||
|  | ||||
|     if (ui_has(kUIMultigrid)) { | ||||
|     tabpage_check_windows(tp); | ||||
|     } | ||||
|  | ||||
|     apply_autocmds(EVENT_WINNEW, NULL, NULL, false, curbuf); | ||||
|     apply_autocmds(EVENT_WINENTER, NULL, NULL, false, curbuf); | ||||
| @@ -3317,7 +3745,7 @@ static void enter_tabpage(tabpage_T *tp, buf_T *old_curbuf, int trigger_enter_au | ||||
|   lastwin = tp->tp_lastwin; | ||||
|   topframe = tp->tp_topframe; | ||||
|  | ||||
|   if (old_curtab != curtab && ui_has(kUIMultigrid)) { | ||||
|   if (old_curtab != curtab) { | ||||
|      tabpage_check_windows(old_curtab); | ||||
|   } | ||||
|  | ||||
| @@ -3355,16 +3783,31 @@ static void enter_tabpage(tabpage_T *tp, buf_T *old_curbuf, int trigger_enter_au | ||||
|   redraw_all_later(NOT_VALID); | ||||
| } | ||||
|  | ||||
| /// called when changing current tabpage from old_curtab to curtab | ||||
| /// tells external UI that windows and inline floats in old_curtab are invisible | ||||
| /// and that floats in curtab is now visible. | ||||
| /// | ||||
| /// External floats are considered independent of tabpages. This is | ||||
| /// implemented by always moving them to curtab. | ||||
| static void tabpage_check_windows(tabpage_T *old_curtab) | ||||
| { | ||||
|   win_T *next_wp; | ||||
|   for (win_T *wp = old_curtab->tp_firstwin; wp; wp = next_wp) { | ||||
|     next_wp = wp->w_next; | ||||
|     if (wp->w_floating) { | ||||
|       if (wp->w_float_config.external) { | ||||
|         win_remove(wp, old_curtab); | ||||
|         win_append(lastwin_nofloating(), wp); | ||||
|       } else { | ||||
|         ui_comp_remove_grid(&wp->w_grid); | ||||
|       } | ||||
|     } | ||||
|     wp->w_pos_changed = true; | ||||
|   } | ||||
|  | ||||
|   for (win_T *wp = firstwin; wp; wp = wp->w_next) { | ||||
|     if (wp->w_floating && !wp->w_float_config.external) { | ||||
|       win_config_float(wp, wp->w_width, wp->w_height, wp->w_float_config); | ||||
|     } | ||||
|     wp->w_pos_changed = true; | ||||
|   } | ||||
| } | ||||
| @@ -3577,6 +4020,12 @@ win_goto_ver ( | ||||
|   frame_T     *foundfr; | ||||
|  | ||||
|   foundfr = curwin->w_frame; | ||||
|  | ||||
|   if (curwin->w_floating) { | ||||
|     win_goto(prevwin); | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   while (count--) { | ||||
|     /* | ||||
|      * First go upwards in the tree of frames until we find an upwards or | ||||
| @@ -3636,6 +4085,12 @@ win_goto_hor ( | ||||
|   frame_T     *foundfr; | ||||
|  | ||||
|   foundfr = curwin->w_frame; | ||||
|  | ||||
|   if (curwin->w_floating) { | ||||
|     win_goto(prevwin); | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   while (count--) { | ||||
|     /* | ||||
|      * First go upwards in the tree of frames until we find a left or | ||||
| @@ -3740,6 +4195,7 @@ static void win_enter_ext(win_T *wp, bool undo_sync, int curwin_invalid, | ||||
|   } | ||||
|   curwin = wp; | ||||
|   curbuf = wp->w_buffer; | ||||
|  | ||||
|   check_cursor(); | ||||
|   if (!virtual_active()) | ||||
|     curwin->w_cursor.coladd = 0; | ||||
| @@ -3809,9 +4265,10 @@ static void win_enter_ext(win_T *wp, bool undo_sync, int curwin_invalid, | ||||
|   else if (curwin->w_height == 0) | ||||
|     win_setheight(1); | ||||
|  | ||||
|   /* set window width to desired minimal value */ | ||||
|   if (curwin->w_width < p_wiw && !curwin->w_p_wfw) | ||||
|   // set window width to desired minimal value | ||||
|   if (curwin->w_width < p_wiw && !curwin->w_p_wfw && !wp->w_floating) { | ||||
|     win_setwidth((int)p_wiw); | ||||
|   } | ||||
|  | ||||
|   setmouse();                   /* in case jumped to/from help buffer */ | ||||
|  | ||||
| @@ -3916,6 +4373,7 @@ static win_T *win_alloc(win_T *after, int hidden) | ||||
|   new_wp->w_botline = 2; | ||||
|   new_wp->w_cursor.lnum = 1; | ||||
|   new_wp->w_scbind_pos = 1; | ||||
|   new_wp->w_floating = 0; | ||||
|  | ||||
|   /* We won't calculate w_fraction until resizing the window */ | ||||
|   new_wp->w_fraction = 0; | ||||
| @@ -4204,6 +4662,13 @@ int win_comp_pos(void) | ||||
|   int col = 0; | ||||
|  | ||||
|   frame_comp_pos(topframe, &row, &col); | ||||
|  | ||||
|   // Too often, but when we support anchoring floats to split windows, | ||||
|   // this will be needed | ||||
|   for (win_T *wp = lastwin; wp && wp->w_floating; wp = wp->w_prev) { | ||||
|     win_config_float(wp, wp->w_width, wp->w_height, wp->w_float_config); | ||||
|   } | ||||
|  | ||||
|   return row; | ||||
| } | ||||
|  | ||||
| @@ -4263,8 +4728,6 @@ void win_setheight(int height) | ||||
|  */ | ||||
| void win_setheight_win(int height, win_T *win) | ||||
| { | ||||
|   int row; | ||||
|  | ||||
|   if (win == curwin) { | ||||
|     /* Always keep current window at least one line high, even when | ||||
|      * 'winminheight' is zero. */ | ||||
| @@ -4274,21 +4737,28 @@ void win_setheight_win(int height, win_T *win) | ||||
|       height = 1; | ||||
|   } | ||||
|  | ||||
|   if (win->w_floating) { | ||||
|     if (win->w_float_config.external) { | ||||
|       win_config_float(win, win->w_width, height, win->w_float_config); | ||||
|     } else { | ||||
|       beep_flush(); | ||||
|       return; | ||||
|     } | ||||
|   } else { | ||||
|     frame_setheight(win->w_frame, height + win->w_status_height); | ||||
|  | ||||
|   /* recompute the window positions */ | ||||
|   row = win_comp_pos(); | ||||
|     // recompute the window positions | ||||
|     int row = win_comp_pos(); | ||||
|  | ||||
|   /* | ||||
|    * If there is extra space created between the last window and the command | ||||
|    * line, clear it. | ||||
|    */ | ||||
|     // If there is extra space created between the last window and the command | ||||
|     // line, clear it. | ||||
|     if (full_screen && msg_scrolled == 0 && row < cmdline_row) { | ||||
|       grid_fill(&default_grid, row, cmdline_row, 0, (int)Columns, ' ', ' ', 0); | ||||
|     } | ||||
|     cmdline_row = row; | ||||
|     msg_row = row; | ||||
|     msg_col = 0; | ||||
|   } | ||||
|  | ||||
|   redraw_all_later(NOT_VALID); | ||||
| } | ||||
| @@ -4358,15 +4828,17 @@ static void frame_setheight(frame_T *curfrp, int height) | ||||
|         if (frp != curfrp) | ||||
|           room -= frame_minheight(frp, NULL); | ||||
|       } | ||||
|       if (curfrp->fr_width != Columns) | ||||
|       if (curfrp->fr_width != Columns) { | ||||
|         room_cmdline = 0; | ||||
|       else { | ||||
|         room_cmdline = Rows - p_ch - (lastwin->w_winrow | ||||
|                                       + lastwin->w_height + | ||||
|                                       lastwin->w_status_height); | ||||
|         if (room_cmdline < 0) | ||||
|       } else { | ||||
|         win_T *wp = lastwin_nofloating(); | ||||
|         room_cmdline = Rows - p_ch - (wp->w_winrow | ||||
|                                       + wp->w_height + | ||||
|                                       wp->w_status_height); | ||||
|         if (room_cmdline < 0) { | ||||
|           room_cmdline = 0; | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       if (height <= room + room_cmdline) { | ||||
|         break; | ||||
| @@ -4470,11 +4942,19 @@ void win_setwidth_win(int width, win_T *wp) | ||||
|     if (width == 0) | ||||
|       width = 1; | ||||
|   } | ||||
|  | ||||
|   if (wp->w_floating) { | ||||
|     if (wp->w_float_config.external) { | ||||
|       win_config_float(wp, width, wp->w_height, wp->w_float_config); | ||||
|     } else { | ||||
|       beep_flush(); | ||||
|       return; | ||||
|     } | ||||
|   } else { | ||||
|     frame_setwidth(wp->w_frame, width + wp->w_vsep_width); | ||||
|  | ||||
|   /* recompute the window positions */ | ||||
|     // recompute the window positions | ||||
|     (void)win_comp_pos(); | ||||
|   } | ||||
|  | ||||
|   redraw_all_later(NOT_VALID); | ||||
| } | ||||
| @@ -5015,6 +5495,7 @@ void win_set_inner_size(win_T *wp) | ||||
|     if (!exiting) { | ||||
|       scroll_to_fraction(wp, prev_height); | ||||
|     } | ||||
|     redraw_win_later(wp, NOT_VALID);  // SOME_VALID?? | ||||
|   } | ||||
|  | ||||
|   if (width != wp->w_width_inner) { | ||||
| @@ -5026,6 +5507,7 @@ void win_set_inner_size(win_T *wp) | ||||
|       update_topline(); | ||||
|       curs_columns(true);  // validate w_wrow | ||||
|     } | ||||
|     redraw_win_later(wp, NOT_VALID); | ||||
|   } | ||||
|  | ||||
|   if (wp->w_buffer->terminal) { | ||||
| @@ -5039,9 +5521,7 @@ void win_new_width(win_T *wp, int width) | ||||
|   wp->w_width = width; | ||||
|   win_set_inner_size(wp); | ||||
|  | ||||
|   redraw_win_later(wp, NOT_VALID); | ||||
|   wp->w_redr_status = TRUE; | ||||
|  | ||||
|   wp->w_redr_status = true; | ||||
|   wp->w_pos_changed = true; | ||||
| } | ||||
|  | ||||
| @@ -5379,7 +5859,7 @@ int min_rows(void) | ||||
|  | ||||
| /// Check that there is only one window (and only one tab page), not counting a | ||||
| /// help or preview window, unless it is the current window. Does not count | ||||
| /// "aucmd_win". | ||||
| /// "aucmd_win". Does not count floats unless it is current. | ||||
| bool only_one_window(void) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT | ||||
| { | ||||
|   // If there is another tab page there always is another window. | ||||
| @@ -5390,7 +5870,7 @@ bool only_one_window(void) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT | ||||
|   int count = 0; | ||||
|   FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { | ||||
|     if (wp->w_buffer != NULL | ||||
|         && (!((bt_help(wp->w_buffer) && !bt_help(curbuf)) | ||||
|         && (!((bt_help(wp->w_buffer) && !bt_help(curbuf)) || wp->w_floating | ||||
|               || wp->w_p_pvw) || wp == curwin) && wp != aucmd_win) { | ||||
|       count++; | ||||
|     } | ||||
| @@ -6080,22 +6560,24 @@ void win_findbuf(typval_T *argvars, list_T *list) | ||||
|   } | ||||
| } | ||||
|  | ||||
| void win_ui_flush(void) | ||||
| void win_ui_flush_positions(void) | ||||
| { | ||||
|   if (!ui_has(kUIMultigrid)) { | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   FOR_ALL_TAB_WINDOWS(tp, wp) { | ||||
|     if (wp->w_pos_changed && wp->w_grid.chars != NULL) { | ||||
|       if (tp == curtab) { | ||||
|         ui_call_win_pos(wp->w_grid.handle, wp->handle, wp->w_winrow, | ||||
|                         wp->w_wincol, wp->w_width, wp->w_height); | ||||
|         ui_ext_win_position(wp); | ||||
|       } else { | ||||
|         ui_call_win_hide(wp->w_grid.handle); | ||||
|       } | ||||
|       wp->w_pos_changed = false; | ||||
|     } | ||||
|   } | ||||
|  | ||||
| } | ||||
|  | ||||
| win_T *lastwin_nofloating(void) { | ||||
|   win_T *res = lastwin; | ||||
|   while (res->w_floating) { | ||||
|     res = res->w_prev; | ||||
|   } | ||||
|   return res; | ||||
| } | ||||
|   | ||||
							
								
								
									
										3030
									
								
								test/functional/ui/float_spec.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3030
									
								
								test/functional/ui/float_spec.lua
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -158,6 +158,7 @@ function Screen.new(width, height) | ||||
|     wildmenu_items = nil, | ||||
|     wildmenu_selected = nil, | ||||
|     win_position = {}, | ||||
|     float_pos = {}, | ||||
|     _session = nil, | ||||
|     messages = {}, | ||||
|     msg_history = {}, | ||||
| @@ -227,10 +228,9 @@ function Screen:attach(options, session) | ||||
|     -- simplify test code by doing the same. | ||||
|     self._options.rgb = true | ||||
|   end | ||||
|   if self._options.ext_multigrid then | ||||
|   if self._options.ext_multigrid or self._options.ext_float then | ||||
|     self._options.ext_linegrid = true | ||||
|   end | ||||
|   self._session = session | ||||
| end | ||||
|  | ||||
| function Screen:detach() | ||||
| @@ -256,7 +256,7 @@ end | ||||
| -- canonical order of ext keys, used  to generate asserts | ||||
| local ext_keys = { | ||||
|   'popupmenu', 'cmdline', 'cmdline_block', 'wildmenu_items', 'wildmenu_pos', | ||||
|   'messages', 'showmode', 'showcmd', 'ruler', | ||||
|   'messages', 'showmode', 'showcmd', 'ruler', 'float_pos', | ||||
| } | ||||
|  | ||||
| -- Asserts that the screen state eventually matches an expected state | ||||
| @@ -642,7 +642,7 @@ function Screen:_handle_grid_resize(grid, width, height) | ||||
|   end | ||||
|  | ||||
|   if self._cursor.grid == grid then | ||||
|     self._cursor.row = 1 | ||||
|     self._cursor.row = 1 -- -1 ? | ||||
|     self._cursor.col = 1 | ||||
|   end | ||||
|   self._grids[grid] = { | ||||
| @@ -676,7 +676,6 @@ function Screen:_reset() | ||||
|   self.wildmenu_pos = nil | ||||
| end | ||||
|  | ||||
|  | ||||
| function Screen:_handle_mode_info_set(cursor_style_enabled, mode_info) | ||||
|   self._cursor_style_enabled = cursor_style_enabled | ||||
|   for _, item in pairs(mode_info) do | ||||
| @@ -713,7 +712,6 @@ end | ||||
| function Screen:_handle_grid_destroy(grid) | ||||
|   self._grids[grid] = nil | ||||
|   if self._options.ext_multigrid then | ||||
|     assert(self.win_position[grid]) | ||||
|     self.win_position[grid] = nil | ||||
|   end | ||||
| end | ||||
| @@ -734,6 +732,36 @@ function Screen:_handle_grid_cursor_goto(grid, row, col) | ||||
|   self._cursor.col = col + 1 | ||||
| end | ||||
|  | ||||
| function Screen:_handle_win_pos(grid, win, startrow, startcol, width, height) | ||||
|     self.win_position[grid] = { | ||||
|         win = win, | ||||
|         startrow = startrow, | ||||
|         startcol = startcol, | ||||
|         width = width, | ||||
|         height = height | ||||
|     } | ||||
|     self.float_pos[grid] = nil | ||||
| end | ||||
|  | ||||
| function Screen:_handle_win_float_pos(grid, ...) | ||||
|   self.win_position[grid] = nil | ||||
|   self.float_pos[grid] = {...} | ||||
| end | ||||
|  | ||||
| function Screen:_handle_win_external_pos(grid) | ||||
|   self.win_position[grid] = nil | ||||
|   self.float_pos[grid] = {external=true} | ||||
| end | ||||
|  | ||||
| function Screen:_handle_win_hide(grid) | ||||
|   self.win_position[grid] = nil | ||||
|   self.float_pos[grid] = nil | ||||
| end | ||||
|  | ||||
| function Screen:_handle_win_close(grid) | ||||
|   self.float_pos[grid] = nil | ||||
| end | ||||
|  | ||||
| function Screen:_handle_busy_start() | ||||
|   self._busy = true | ||||
| end | ||||
| @@ -815,20 +843,6 @@ function Screen:_handle_hl_attr_define(id, rgb_attrs, cterm_attrs, info) | ||||
|   self._new_attrs = true | ||||
| end | ||||
|  | ||||
| function Screen:_handle_win_pos(grid, win, startrow, startcol, width, height) | ||||
|     self.win_position[grid] = { | ||||
|         win = win, | ||||
|         startrow = startrow, | ||||
|         startcol = startcol, | ||||
|         width = width, | ||||
|         height = height | ||||
|     } | ||||
| end | ||||
|  | ||||
| function Screen:_handle_win_hide(grid) | ||||
|   self.win_position[grid] = nil | ||||
| end | ||||
|  | ||||
| function Screen:get_hl(val) | ||||
|   if self._options.ext_newgrid then | ||||
|     return self._attr_table[val][1] | ||||
| @@ -922,8 +936,11 @@ function Screen:_handle_option_set(name, value) | ||||
|   self.options[name] = value | ||||
| end | ||||
|  | ||||
| function Screen:_handle_popupmenu_show(items, selected, row, col) | ||||
|   self.popupmenu = {items=items, pos=selected, anchor={row, col}} | ||||
| function Screen:_handle_popupmenu_show(items, selected, row, col, grid) | ||||
|   if (not self._options.ext_multigrid) and grid == 1 then | ||||
|     grid = nil | ||||
|   end | ||||
|   self.popupmenu = {items=items, pos=selected, anchor={row, col, grid}} | ||||
| end | ||||
|  | ||||
| function Screen:_handle_popupmenu_select(selected) | ||||
| @@ -1112,6 +1129,7 @@ function Screen:_extstate_repr(attr_state) | ||||
|     showcmd=self:_chunks_repr(self.showcmd, attr_state), | ||||
|     ruler=self:_chunks_repr(self.ruler, attr_state), | ||||
|     msg_history=msg_history, | ||||
|     float_pos=self.float_pos | ||||
|   } | ||||
| end | ||||
|  | ||||
| @@ -1146,7 +1164,10 @@ function Screen:redraw_debug(attrs, ignore, timeout) | ||||
|   local function notification_cb(method, args) | ||||
|     assert(method == 'redraw') | ||||
|     for _, update in ipairs(args) do | ||||
|       print(require('inspect')(update)) | ||||
|       -- mode_info_set is quite verbose, comment out the condition to debug it. | ||||
|       if update[1] ~= "mode_info_set" then | ||||
|         print(inspect(update)) | ||||
|       end | ||||
|     end | ||||
|     self:_redraw(args) | ||||
|     self:print_snapshot(attrs, ignore) | ||||
| @@ -1159,7 +1180,7 @@ function Screen:redraw_debug(attrs, ignore, timeout) | ||||
| end | ||||
|  | ||||
| function Screen:render(headers, attr_state, preview) | ||||
|   headers = headers and self._options.ext_multigrid | ||||
|   headers = headers and (self._options.ext_multigrid or self._options._debug_float) | ||||
|   local rv = {} | ||||
|   for igrid,grid in pairs(self._grids) do | ||||
|     if headers then | ||||
| @@ -1227,6 +1248,7 @@ function Screen:print_snapshot(attrs, ignore) | ||||
|   io.stdout:write( "]]"..attrstr) | ||||
|   for _, k in ipairs(ext_keys) do | ||||
|     if ext_state[k] ~= nil then | ||||
|       -- TODO(bfredl): improve formating, remove ext metatables | ||||
|       io.stdout:write(", "..k.."="..inspect(ext_state[k])) | ||||
|     end | ||||
|   end | ||||
|   | ||||
| @@ -47,8 +47,8 @@ local check_logs_useless_lines = { | ||||
|   ['See README_MISSING_SYSCALL_OR_IOCTL for guidance']=3, | ||||
| } | ||||
|  | ||||
| local function eq(expected, actual, ctx) | ||||
|   return assert.are.same(expected, actual, ctx) | ||||
| local function eq(expected, actual, context) | ||||
|   return assert.are.same(expected, actual, context) | ||||
| end | ||||
| local function neq(expected, actual, context) | ||||
|   return assert.are_not.same(expected, actual, context) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Björn Linse
					Björn Linse