mirror of
				https://github.com/neovim/neovim.git
				synced 2025-10-26 12:27:24 +00:00 
			
		
		
		
	floats: z-index
This commit is contained in:
		| @@ -1983,6 +1983,14 @@ bool parse_float_config(Dictionary config, FloatConfig *fconfig, bool reconf, | ||||
|                       "'focusable' key must be Boolean"); | ||||
|         return false; | ||||
|       } | ||||
|     } else if (strequal(key, "zindex")) { | ||||
|       if (val.type == kObjectTypeInteger && val.data.integer > 0) { | ||||
|         fconfig->zindex = (int)val.data.integer; | ||||
|       } else { | ||||
|         api_set_error(err, kErrorTypeValidation, | ||||
|                       "'zindex' key must be a positive Integer"); | ||||
|         return false; | ||||
|       } | ||||
|     } else if (!strcmp(key, "border")) { | ||||
|       parse_border_style(val, fconfig, err); | ||||
|       if (ERROR_SET(err)) { | ||||
|   | ||||
| @@ -106,7 +106,8 @@ void win_pos(Integer grid, Window 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) | ||||
|                    Float anchor_row, Float anchor_col, Boolean focusable, | ||||
|                    Integer zindex) | ||||
|   FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY; | ||||
| void win_external_pos(Integer grid, Window win) | ||||
|   FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY; | ||||
|   | ||||
| @@ -1417,6 +1417,15 @@ void nvim_chan_send(Integer chan, String data, Error *err) | ||||
| ///   - `external`: GUI should display the window as an external | ||||
| ///       top-level window. Currently accepts no other positioning | ||||
| ///       configuration together with this. | ||||
| ///   - `zindex`: Stacking order. floats with higher `zindex` go on top on | ||||
| ///               floats with lower indices. Must be larger than zero. The | ||||
| ///               following screen elements have hard-coded z-indices: | ||||
| ///       - 100: insert completion popupmenu | ||||
| ///       - 200: message scrollback | ||||
| ///       - 250: cmdline completion popupmenu (when wildoptions+=pum) | ||||
| ///     The default value for floats are 50.  In general, values below 100 are | ||||
| ///     recommended, unless there is a good reason to overshadow builtin | ||||
| ///     elements. | ||||
| ///   - `style`: Configure the appearance of the window. Currently only takes | ||||
| ///       one non-empty value: | ||||
| ///       - "minimal"  Nvim will display the window with many UI options | ||||
|   | ||||
| @@ -1083,6 +1083,7 @@ typedef struct { | ||||
|   FloatRelative relative; | ||||
|   bool external; | ||||
|   bool focusable; | ||||
|   int zindex; | ||||
|   WinStyle style; | ||||
|   bool border; | ||||
|   bool shadow; | ||||
| @@ -1096,6 +1097,7 @@ typedef struct { | ||||
|                                           .row = 0, .col = 0, .anchor = 0, \ | ||||
|                                           .relative = 0, .external = false, \ | ||||
|                                           .focusable = true, \ | ||||
|                                           .zindex = kZIndexFloatDefault, \ | ||||
|                                           .style = kWinStyleUnused }) | ||||
|  | ||||
| // Structure to store last cursor position and topline.  Used by check_lnums() | ||||
|   | ||||
| @@ -13,6 +13,15 @@ | ||||
| typedef char_u schar_T[(MAX_MCO+1) * 4 + 1]; | ||||
| typedef int sattr_T; | ||||
|  | ||||
| enum { | ||||
|   kZIndexDefaultGrid = 0, | ||||
|   kZIndexFloatDefault = 50, | ||||
|   kZIndexPopupMenu = 100, | ||||
|   kZIndexMessages = 200, | ||||
|   kZIndexCmdlinePopupMenu = 250, | ||||
| }; | ||||
|  | ||||
|  | ||||
| /// ScreenGrid represents a resizable rectuangular grid displayed by UI clients. | ||||
| /// | ||||
| /// chars[] contains the UTF-8 text that is currently displayed on the grid. | ||||
| @@ -73,6 +82,9 @@ struct ScreenGrid { | ||||
|   // whether the grid can be focused with mouse clicks. | ||||
|   bool focusable; | ||||
|  | ||||
|   // z-index: the order in the stack of grids. | ||||
|   int zindex; | ||||
|  | ||||
|   // Below is state owned by the compositor. Should generally not be set/read | ||||
|   // outside this module, except for specific compatibilty hacks | ||||
|  | ||||
| @@ -96,7 +108,7 @@ struct ScreenGrid { | ||||
| }; | ||||
|  | ||||
| #define SCREEN_GRID_INIT { 0, NULL, NULL, NULL, NULL, NULL, 0, 0, false, \ | ||||
|                            false, 0, 0, NULL, false, true, \ | ||||
|                            false, 0, 0, NULL, false, true, 0, \ | ||||
|                            0, 0, 0, 0, 0,  false } | ||||
|  | ||||
| #endif  // NVIM_GRID_DEFS_H | ||||
|   | ||||
| @@ -165,6 +165,7 @@ void msg_grid_validate(void) | ||||
|     // TODO(bfredl): eventually should be set to "invalid". I e all callers | ||||
|     // will use the grid including clear to EOS if necessary. | ||||
|     grid_alloc(&msg_grid, Rows, Columns, false, true); | ||||
|     msg_grid.zindex = kZIndexMessages; | ||||
|  | ||||
|     xfree(msg_grid.dirty_col); | ||||
|     msg_grid.dirty_col = xcalloc(Rows, sizeof(*msg_grid.dirty_col)); | ||||
|   | ||||
| @@ -421,6 +421,10 @@ void pum_redraw(void) | ||||
|   } | ||||
|  | ||||
|   grid_assign_handle(&pum_grid); | ||||
|  | ||||
|   pum_grid.zindex = ((State == CMDLINE) | ||||
|                      ? kZIndexCmdlinePopupMenu : kZIndexPopupMenu); | ||||
|  | ||||
|   bool moved = ui_comp_put_grid(&pum_grid, pum_row, pum_col-col_off, | ||||
|                                 pum_height, grid_width, false, true); | ||||
|   bool invalid_grid = moved || pum_invalid; | ||||
| @@ -439,7 +443,7 @@ void pum_redraw(void) | ||||
|     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); | ||||
|                           false, pum_grid.zindex); | ||||
|   } | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -165,22 +165,13 @@ bool ui_comp_put_grid(ScreenGrid *grid, int row, int col, int height, int width, | ||||
|     } | ||||
| #endif | ||||
|  | ||||
|     // TODO(bfredl): this is pretty ad-hoc, add a proper z-order/priority | ||||
|     // scheme. For now: | ||||
|     // - msg_grid is always on top. | ||||
|     // - pum_grid is on top of all windows but not msg_grid. Except for when | ||||
|     //   wildoptions=pum, and completing the cmdline with scrolled messages, | ||||
|     //   then the pum has to be drawn over the scrolled messages. | ||||
|     size_t insert_at = kv_size(layers); | ||||
|     bool cmd_completion = (grid == &pum_grid && (State & CMDLINE) | ||||
|                            && (wop_flags & WOP_PUM)); | ||||
|     if (kv_A(layers, insert_at-1) == &msg_grid && !cmd_completion) { | ||||
|       insert_at--; | ||||
|     } | ||||
|     if (kv_A(layers, insert_at-1) == &pum_grid && (grid != &msg_grid)) { | ||||
|     while (insert_at > 0 && kv_A(layers, insert_at-1)->zindex > grid->zindex) { | ||||
|       insert_at--; | ||||
|     } | ||||
|  | ||||
|     if (curwin && kv_A(layers, insert_at-1) == &curwin->w_grid_alloc | ||||
|         && kv_A(layers, insert_at-1)->zindex == grid->zindex | ||||
|         && !on_top) { | ||||
|       insert_at--; | ||||
|     } | ||||
| @@ -279,12 +270,11 @@ static void ui_comp_grid_cursor_goto(UI *ui, Integer grid_handle, | ||||
|   // 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) == &msg_grid) { | ||||
|       new_index--; | ||||
|     } | ||||
|     if (kv_A(layers, new_index) == &pum_grid) { | ||||
|  | ||||
|     while (new_index > 1 && kv_A(layers, new_index)->zindex > curgrid->zindex) { | ||||
|       new_index--; | ||||
|     } | ||||
|  | ||||
|     if (curgrid->comp_index < new_index) { | ||||
|       ui_comp_raise_grid(curgrid, new_index); | ||||
|     } | ||||
|   | ||||
| @@ -763,10 +763,13 @@ void ui_ext_win_position(win_T *wp) | ||||
|       } | ||||
|       api_clear_error(&dummy); | ||||
|     } | ||||
|  | ||||
|     wp->w_grid_alloc.zindex = wp->w_float_config.zindex; | ||||
|     if (ui_has(kUIMultigrid)) { | ||||
|       String anchor = cstr_to_string(float_anchor_str[c.anchor]); | ||||
|       ui_call_win_float_pos(wp->w_grid_alloc.handle, wp->handle, anchor, | ||||
|                             grid->handle, row, col, c.focusable); | ||||
|                             grid->handle, row, col, c.focusable, | ||||
|                             wp->w_grid_alloc.zindex); | ||||
|     } else { | ||||
|       // TODO(bfredl): ideally, compositor should work like any multigrid UI | ||||
|       // and use standard win_pos events. | ||||
|   | ||||
| @@ -1077,8 +1077,8 @@ describe('float window', function() | ||||
|           {1: abb            }| | ||||
|           {13: acc            }| | ||||
|         ]], float_pos={ | ||||
|           [5] = { { id = 1002 }, "NW", 1, 0, 5, true }, | ||||
|           [6] = { { id = -1 }, "NW", 5, 4, 0, false } | ||||
|           [5] = { { id = 1002 }, "NW", 1, 0, 5, true, 50 }, | ||||
|           [6] = { { id = -1 }, "NW", 5, 4, 0, false, 100 } | ||||
|         }, win_viewport={ | ||||
|           [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0}; | ||||
|           [5] = {win = {id = 1002}, topline = 0, botline = 3, curline = 2, curcol = 3}; | ||||
| @@ -2755,8 +2755,8 @@ describe('float window', function() | ||||
|             {1: word           }| | ||||
|             {1: longtext       }| | ||||
|           ]], float_pos={ | ||||
|             [4] = {{ id = 1001 }, "NW", 1, 2, 5, true}, | ||||
|             [5] = {{ id = -1 }, "NW", 4, 1, 1, false} | ||||
|             [4] = {{ id = 1001 }, "NW", 1, 2, 5, true, 50}, | ||||
|             [5] = {{ id = -1 }, "NW", 4, 1, 1, false, 100} | ||||
|           }} | ||||
|         else | ||||
|           screen:expect([[ | ||||
| @@ -2842,8 +2842,8 @@ describe('float window', function() | ||||
|             {1:yy             }| | ||||
|             {1:zz             }| | ||||
|           ]], float_pos={ | ||||
|             [4] = {{ id = 1001 }, "NW", 1, 2, 5, true}, | ||||
|             [5] = {{ id = -1 }, "NW", 2, 1, 0, false} | ||||
|             [4] = {{ id = 1001 }, "NW", 1, 2, 5, true, 50}, | ||||
|             [5] = {{ id = -1 }, "NW", 2, 1, 0, false, 100} | ||||
|           }} | ||||
|         else | ||||
|           screen:expect([[ | ||||
| @@ -3104,7 +3104,7 @@ describe('float window', function() | ||||
|             {1:word           }| | ||||
|             {1:longtext       }| | ||||
|           ]], float_pos={ | ||||
|             [4] = {{id = -1}, "NW", 2, 1, 0, false}} | ||||
|             [4] = {{id = -1}, "NW", 2, 1, 0, false, 100}} | ||||
|           } | ||||
|         else | ||||
|           screen:expect([[ | ||||
| @@ -3148,8 +3148,8 @@ describe('float window', function() | ||||
|             {15:some info   }| | ||||
|             {15:about item  }| | ||||
|           ]], float_pos={ | ||||
|             [4] = {{id = -1}, "NW", 2, 1, 0, false}, | ||||
|             [6] = {{id = 1002}, "NW", 2, 1, 12, true}, | ||||
|             [4] = {{id = -1}, "NW", 2, 1, 0, false, 100}, | ||||
|             [6] = {{id = 1002}, "NW", 2, 1, 12, true, 50}, | ||||
|           }} | ||||
|         else | ||||
|           screen:expect([[ | ||||
| @@ -3263,7 +3263,7 @@ describe('float window', function() | ||||
|             {1:word           }| | ||||
|             {1:longtext       }| | ||||
|           ]], float_pos={ | ||||
|             [4] = {{id = -1}, "NW", 2, 1, 0, false}, | ||||
|             [4] = {{id = -1}, "NW", 2, 1, 0, false, 100}, | ||||
|           }} | ||||
|         else | ||||
|           screen:expect([[ | ||||
| @@ -6296,6 +6296,69 @@ describe('float window', function() | ||||
|         ]]} | ||||
|       end | ||||
|     end) | ||||
|  | ||||
|     it('can use z-index', function() | ||||
|       local buf = meths.create_buf(false,false) | ||||
|       local win1 = meths.open_win(buf, false, {relative='editor', width=20, height=3, row=1, col=5, zindex=30}) | ||||
|       meths.win_set_option(win1, "winhl", "Normal:ErrorMsg,EndOfBuffer:ErrorMsg") | ||||
|       local win2 = meths.open_win(buf, false, {relative='editor', width=20, height=3, row=2, col=6, zindex=50}) | ||||
|       meths.win_set_option(win2, "winhl", "Normal:Search,EndOfBuffer:Search") | ||||
|       local win3 = meths.open_win(buf, false, {relative='editor', width=20, height=3, row=3, col=7, zindex=40}) | ||||
|       meths.win_set_option(win3, "winhl", "Normal:Question,EndOfBuffer:Question") | ||||
|  | ||||
|       if multigrid then | ||||
|         screen:expect{grid=[[ | ||||
|         ## grid 1 | ||||
|           [2:----------------------------------------]| | ||||
|           [2:----------------------------------------]| | ||||
|           [2:----------------------------------------]| | ||||
|           [2:----------------------------------------]| | ||||
|           [2:----------------------------------------]| | ||||
|           [2:----------------------------------------]| | ||||
|           [3:----------------------------------------]| | ||||
|         ## grid 2 | ||||
|           ^                                        | | ||||
|           {0:~                                       }| | ||||
|           {0:~                                       }| | ||||
|           {0:~                                       }| | ||||
|           {0:~                                       }| | ||||
|           {0:~                                       }| | ||||
|         ## grid 3 | ||||
|                                                   | | ||||
|         ## grid 4 | ||||
|           {7:                    }| | ||||
|           {7:~                   }| | ||||
|           {7:~                   }| | ||||
|         ## grid 5 | ||||
|           {17:                    }| | ||||
|           {17:~                   }| | ||||
|           {17:~                   }| | ||||
|         ## grid 6 | ||||
|           {8:                    }| | ||||
|           {8:~                   }| | ||||
|           {8:~                   }| | ||||
|         ]], float_pos={ | ||||
|           [4] = {{id = 1001}, "NW", 1, 1, 5, true, 30}; | ||||
|           [5] = {{id = 1002}, "NW", 1, 2, 6, true, 50}; | ||||
|           [6] = {{id = 1003}, "NW", 1, 3, 7, true, 40}; | ||||
|         }, win_viewport={ | ||||
|           [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0}; | ||||
|           [4] = {win = {id = 1001}, topline = 0, botline = 2, curline = 0, curcol = 0}; | ||||
|           [5] = {win = {id = 1002}, topline = 0, botline = 2, curline = 0, curcol = 0}; | ||||
|           [6] = {win = {id = 1003}, topline = 0, botline = 2, curline = 0, curcol = 0}; | ||||
|         }} | ||||
|       else | ||||
|         screen:expect{grid=[[ | ||||
|           ^                                        | | ||||
|           {0:~    }{7:                    }{0:               }| | ||||
|           {0:~    }{7:~}{17:                    }{0:              }| | ||||
|           {0:~    }{7:~}{17:~                   }{8: }{0:             }| | ||||
|           {0:~     }{17:~                   }{8: }{0:             }| | ||||
|           {0:~      }{8:~                   }{0:             }| | ||||
|                                                   | | ||||
|         ]]} | ||||
|       end | ||||
|     end) | ||||
|   end | ||||
|  | ||||
|   describe('with ext_multigrid', function() | ||||
|   | ||||
| @@ -429,6 +429,15 @@ screen:redraw_debug() to show all intermediate screen states.  ]]) | ||||
|       extstate.win_viewport = nil | ||||
|     end | ||||
|  | ||||
|     if expected.float_pos then | ||||
|       expected.float_pos = deepcopy(expected.float_pos) | ||||
|       for _, v in pairs(expected.float_pos) do | ||||
|         if not v.external and v[7] == nil then | ||||
|           v[7] = 50 | ||||
|         end | ||||
|       end | ||||
|     end | ||||
|  | ||||
|     -- Convert assertion errors into invalid screen state descriptions. | ||||
|     for _, k in ipairs(concat_tables(ext_keys, {'mode', 'mouse_enabled'})) do | ||||
|       -- Empty states are considered the default and need not be mentioned. | ||||
| @@ -1287,6 +1296,11 @@ function Screen:get_snapshot(attrs, ignore) | ||||
| end | ||||
|  | ||||
| local function fmt_ext_state(name, state) | ||||
|   local function remove_all_metatables(item, path) | ||||
|     if path[#path] ~= inspect.METATABLE then | ||||
|       return item | ||||
|     end | ||||
|   end | ||||
|   if name == "win_viewport" then | ||||
|     local str = "{\n" | ||||
|     for k,v in pairs(state) do | ||||
| @@ -1295,13 +1309,18 @@ local function fmt_ext_state(name, state) | ||||
|              ..", curcol = "..v.curcol.."};\n") | ||||
|     end | ||||
|     return str .. "}" | ||||
|   elseif name == "float_pos" then | ||||
|     local str = "{\n" | ||||
|     for k,v in pairs(state) do | ||||
|       str = str.."  ["..k.."] = {{id = "..v[1].id.."}" | ||||
|       for i = 2, #v do | ||||
|         str = str..", "..inspect(v[i]) | ||||
|       end | ||||
|       str = str .. "};\n" | ||||
|     end | ||||
|     return str .. "}" | ||||
|   else | ||||
|     -- TODO(bfredl): improve formatting of more states | ||||
|     local function remove_all_metatables(item, path) | ||||
|       if path[#path] ~= inspect.METATABLE then | ||||
|         return item | ||||
|       end | ||||
|     end | ||||
|     return inspect(state,{process=remove_all_metatables}) | ||||
|   end | ||||
| end | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Björn Linse
					Björn Linse