diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index 9f2a348025..7e5f7565b1 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -167,7 +167,7 @@ API they were so specified in `nvim_create_user_command()`. • |nvim_open_win()| floating windows can show a 'statusline'. Plugins can use `style='minimal'` or `:setlocal statusline=` to hide the statusline. -• |nvim_win_set_config()| can move floating windows to other tab pages. +• |nvim_win_set_config()| can move windows to other tab pages as floats. • Added experimental |nvim__exec_lua_fast()| to allow remote API clients to execute code while nvim is blocking for input. • |vim.secure.trust()| accepts `path` for the `allow` action. diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c index cf10883d81..9328c8d064 100644 --- a/src/nvim/api/win_config.c +++ b/src/nvim/api/win_config.c @@ -384,9 +384,13 @@ static int win_split_flags(WinSplit split, bool toplevel) } /// Checks if window `wp` can be moved to tabpage `tp`. -static bool win_can_move_tp(win_T *wp, Error *err) +static bool win_can_move_tp(win_T *wp, tabpage_T *tp, Error *err) FUNC_ATTR_NONNULL_ALL { + if (one_window(wp, tp == curtab ? NULL : tp)) { + api_set_error(err, kErrorTypeException, "Cannot move last non-floating window"); + return false; + } if (is_aucmd_win(wp)) { api_set_error(err, kErrorTypeException, "Cannot move autocmd window to another tabpage"); return false; @@ -403,6 +407,17 @@ static bool win_can_move_tp(win_T *wp, Error *err) return true; } +static win_T *win_find_altwin(win_T *win, tabpage_T *tp) + FUNC_ATTR_NONNULL_ALL +{ + if (win->w_floating) { + return win_float_find_altwin(win, tp == curtab ? NULL : tp); + } else { + int dir; + return winframe_find_altwin(win, &dir, tp == curtab ? NULL : tp, NULL); + } +} + /// Configures `win` into a split, also moving it to another tabpage if requested. static bool win_config_split(win_T *win, const Dict(win_config) *config, WinConfig *fconfig, Error *err) @@ -446,7 +461,7 @@ static bool win_config_split(win_T *win, const Dict(win_config) *config, WinConf api_set_error(err, kErrorTypeException, "Cannot split a floating window"); return false; } - if (win_tp != parent_tp && !win_can_move_tp(win, err)) { + if (win_tp != parent_tp && !win_can_move_tp(win, win_tp, err)) { return false; // error already set } } @@ -460,19 +475,9 @@ static bool win_config_split(win_T *win, const Dict(win_config) *config, WinConf // window list or remove its frame (if non-floating), so it's valid for autocommands. const bool curwin_moving_tp = win == curwin && parent && win_tp != parent_tp; if (curwin_moving_tp) { - if (was_split) { - int dir; - win_T *altwin = winframe_find_altwin(win, &dir, NULL, NULL); - // Autocommands may still make this the last non-float after this check. - // That case will be caught later when trying to move the window. - if (!altwin) { - api_set_error(err, kErrorTypeException, "Cannot move last non-floating window"); - return false; - } - win_goto(altwin); - } else { - win_goto(win_float_find_altwin(win, NULL)); - } + win_T *altwin = win_find_altwin(win, win_tp); + assert(altwin); // win_can_move_tp ensures `win` is not the only window + win_goto(altwin); // Autocommands may have been a real nuisance and messed things up... if (curwin == win) { @@ -642,12 +647,21 @@ static bool win_config_float_tp(win_T *win, const Dict(win_config) *config, parent_tp = win_find_tabpage(parent); } + bool curwin_moving_tp = false; + win_T *altwin = NULL; + if (win_tp != parent_tp) { - if (!win_can_move_tp(win, err)) { + if (!win_can_move_tp(win, win_tp, err)) { return false; // error already set } + altwin = win_find_altwin(win, win_tp); + assert(altwin); // win_can_move_tp ensures `win` is not the only window + + // If we are moving curwin to another tabpage, switch windows *before* we remove it from the + // window list or remove its frame (if non-floating), so it's valid for autocommands. if (curwin == win) { - win_goto(win_float_find_altwin(win, NULL)); + curwin_moving_tp = true; + win_goto(altwin); // Autocommands may have been a real nuisance and messed things up... if (curwin == win) { @@ -658,47 +672,51 @@ static bool win_config_float_tp(win_T *win, const Dict(win_config) *config, win_tp = win_find_tabpage(win); parent_tp = win_find_tabpage(parent); - bool restore_curwin = false; if (!win_tp || !parent_tp) { api_set_error(err, kErrorTypeException, "Target windows were closed"); - restore_curwin = true; - } else if (!win->w_floating) { - api_set_error(err, kErrorTypeException, "Window %d was made non-floating", win->handle); - restore_curwin = true; - } else if (win_tp != parent_tp && !win_can_move_tp(win, err)) { - restore_curwin = true; // error already set + goto restore_curwin; } - if (restore_curwin) { - // As `win` was the original curwin, and autocommands didn't move it outside of curtab, be - // a good citizen and try to return to it. - if (win_valid(win)) { - win_goto(win); - } - return false; + if (win_tp != parent_tp && !win_can_move_tp(win, win_tp, err)) { + goto restore_curwin; // error already set } + altwin = win_find_altwin(win, win_tp); + assert(altwin); // win_can_move_tp ensures `win` is not the only window + } + } + + // Convert the window to a float if needed. + if (!win->w_floating) { + if (!win_new_float(win, false, *fconfig, err)) { +restore_curwin: + // If `win` was the original curwin, and autocommands didn't move it outside of curtab, be a + // good citizen and try to return to it. + if (curwin_moving_tp && win_valid(win)) { + win_goto(win); + } + return false; + } + redraw_later(win, UPD_NOT_VALID); + } + + if (win_tp != parent_tp) { + win_remove(win, win_tp == curtab ? NULL : win_tp); + tabpage_T *append_tp = parent_tp == curtab ? NULL : parent_tp; + win_append(lastwin_nofloating(append_tp), win, append_tp); + + // If `win` was the curwin of its old tabpage, select a new curwin for it. + if (win_tp != curtab && win_tp->tp_curwin == win) { + win_tp->tp_curwin = altwin; } - // Check again, in case autocommands above moved windows to the same tabpage. - if (win_tp != parent_tp) { - win_remove(win, win_tp == curtab ? NULL : win_tp); - tabpage_T *append_tp = parent_tp == curtab ? NULL : parent_tp; - win_append(lastwin_nofloating(append_tp), win, append_tp); + // Remove grid if present. More reliable than checking curtab, as tabpage_check_windows may not + // run when temporarily switching tabpages, meaning grids may be stale from another tabpage! + // (e.g: switch_win_noblock with no_display=true) + ui_comp_remove_grid(&win->w_grid_alloc); - // If `win` was the curwin of its old tabpage, select a new curwin for it. - if (win_tp != curtab && win_tp->tp_curwin == win) { - win_tp->tp_curwin = win_float_find_altwin(win, win_tp); - } - - // Remove grid if present. More reliable than checking curtab, as tabpage_check_windows may - // not run when temporarily switching tabpages, meaning grids may be stale from another - // tabpage! (e.g: switch_win_noblock with no_display=true) - ui_comp_remove_grid(&win->w_grid_alloc); - - // Redraw tabline, update window's hl attribs, etc. Set must_redraw here, as redraw_later - // might not if w_redr_type >= UPD_NOT_VALID was set in the old tabpage. - redraw_later(win, UPD_NOT_VALID); - set_must_redraw(UPD_NOT_VALID); - } + // Redraw tabline, update window's hl attribs, etc. Set must_redraw here, as redraw_later might + // not if w_redr_type >= UPD_NOT_VALID was set in the old tabpage. + redraw_later(win, UPD_NOT_VALID); + set_must_redraw(UPD_NOT_VALID); } win_config_float(win, *fconfig); @@ -745,27 +763,11 @@ void nvim_win_set_config(Window window, Dict(win_config) *config, Error *err) return; } - if (was_split && !to_split) { - win_T *parent = find_window_by_handle(fconfig.window, err); - if (!parent) { - return; - } - // TODO(seandewar): support this, preferably via win_config_float_tp. - if (!win_valid(parent)) { - api_set_error(err, kErrorTypeValidation, - "Cannot configure split into float in another tabpage"); - return; - } - if (!win_new_float(win, false, fconfig, err)) { - return; - } - redraw_later(win, UPD_NOT_VALID); - } else if (to_split) { + if (to_split) { if (!win_config_split(win, config, &fconfig, err)) { return; } } else { - assert(!was_split); if (!win_config_float_tp(win, config, &fconfig, err)) { return; } diff --git a/src/nvim/winfloat.c b/src/nvim/winfloat.c index de9fe13417..f222f6b17e 100644 --- a/src/nvim/winfloat.c +++ b/src/nvim/winfloat.c @@ -35,10 +35,9 @@ #include "winfloat.c.generated.h" -/// Create a new float. +/// Creates a new float, or transforms an existing window to a float. /// /// @param wp if NULL, allocate a new window, otherwise turn existing window into a float. -/// It must then already belong to the current tabpage! /// @param last make the window the last one in the window list. /// Only used when allocating the autocommand window. /// @param config must already have been validated! @@ -74,19 +73,19 @@ win_T *win_new_float(win_T *wp, bool last, WinConfig fconfig, Error *err) } else { assert(!last); assert(!wp->w_floating); - if (firstwin == wp && lastwin_nofloating(NULL) == 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"); + tabpage_T *win_tp = win_find_tabpage(wp); + assert(win_tp); + if ((win_tp == curtab && firstwin == wp && lastwin_nofloating(NULL) == wp) + || (win_tp != curtab && win_tp->tp_firstwin == wp && lastwin_nofloating(win_tp) == wp)) { + api_set_error(err, kErrorTypeException, "Cannot change last window into float"); return NULL; } else if (cmdwin_win != NULL && !cmdwin_win->w_floating) { // cmdwin can't become the only non-float. Check for others. bool other_nonfloat = false; - for (win_T *wp2 = firstwin; wp2 != NULL && !wp2->w_floating; wp2 = wp2->w_next) { + FOR_ALL_WINDOWS_IN_TAB(wp2, win_tp) { + if (wp2->w_floating) { + break; + } if (wp2 != wp && wp2 != cmdwin_win) { other_nonfloat = true; break; @@ -97,13 +96,16 @@ win_T *win_new_float(win_T *wp, bool last, WinConfig fconfig, Error *err) return NULL; } } + tabpage_T *tp = win_tp == curtab ? NULL : win_tp; int dir; - winframe_remove(wp, &dir, NULL, NULL); + winframe_remove(wp, &dir, tp, NULL); XFREE_CLEAR(wp->w_frame); - win_remove(wp, NULL); - last_status(false); // may need to remove last status line - win_comp_pos(); // recompute window positions - win_append(lastwin_nofloating(NULL), wp, NULL); + win_remove(wp, tp); + if (win_tp == curtab) { + last_status(false); // may need to remove last status line + win_comp_pos(); // recompute window positions + } + win_append(lastwin_nofloating(tp), wp, tp); } wp->w_floating = true; wp->w_status_height = wp->w_p_stl && *wp->w_p_stl != NUL diff --git a/test/functional/api/window_spec.lua b/test/functional/api/window_spec.lua index d75dc78142..9937c5270b 100644 --- a/test/functional/api/window_spec.lua +++ b/test/functional/api/window_spec.lua @@ -2585,15 +2585,17 @@ describe('API/win', function() }, }, layout) - -- converting split into a float for a different tabpage is not yet supported - eq( - 'Cannot configure split into float in another tabpage', - pcall_err( - api.nvim_win_set_config, - win, - { relative = 'editor', row = 0, col = 0, width = 1, height = 1, win = first_win } - ) + -- directly convert split into a float for a different tabpage + local win2 = api.nvim_open_win(0, true, { split = 'below' }) + eq('', api.nvim_win_get_config(win2).relative) + api.nvim_win_set_config( + win2, + { relative = 'editor', row = 0, col = 0, width = 1, height = 1, win = first_win } ) + eq(first_tab, api.nvim_win_get_tabpage(win2)) + eq('editor', api.nvim_win_get_config(win2).relative) + eq({ first_win, win2 }, api.nvim_tabpage_list_wins(first_tab)) + eq({ tab2_win, win }, api.nvim_tabpage_list_wins(new_tab)) -- convert new win to float in new tabpage api.nvim_win_set_config(win, { relative = 'editor', row = 2, col = 2, height = 2, width = 2 }) @@ -2601,10 +2603,23 @@ describe('API/win', function() -- move to other tabpage api.nvim_win_set_config(win, { win = first_win }) eq(first_tab, api.nvim_win_get_tabpage(win)) - eq({ first_win, win }, api.nvim_tabpage_list_wins(first_tab)) + eq({ first_win, win, win2 }, api.nvim_tabpage_list_wins(first_tab)) eq({ tab2_win }, api.nvim_tabpage_list_wins(new_tab)) -- unlike splits, negative win is invalid eq('Invalid window id: -1', pcall_err(api.nvim_win_set_config, win, { win = -1 })) + + -- can't convert only window in other tabpage to float + command('tabnew') + local only_win = api.nvim_get_current_win() + command('tabprevious') + eq( + 'Cannot change last window into float', + pcall_err( + api.nvim_win_set_config, + only_win, + { relative = 'editor', width = 5, height = 5, row = 0, col = 0 } + ) + ) end) it('correctly moves curwin when moving curwin to a different tabpage', function() @@ -2773,7 +2788,6 @@ describe('API/win', function() it('messing with "win" or "parent" when moving "win" to other tabpage', function() command('split | tabnew') - local t2 = api.nvim_get_current_tabpage() local t2_win1 = api.nvim_get_current_win() command('split') local t2_win2 = api.nvim_get_current_win() @@ -2818,23 +2832,19 @@ describe('API/win', function() eq('', api.nvim_win_get_config(0).relative) eq(cur_win, api.nvim_get_current_win()) - -- Try to make "parent" floating. This should give the same error as before, but because - -- changing a split from another tabpage into a float isn't supported yet, check for that - -- error instead for now. - -- Use ":silent!" to avoid the one second delay from printing the error message. + -- Try to make "parent" floating. This should give the same error as before. exec(([[ - autocmd WinLeave * ++once silent! + autocmd WinLeave * ++once \ call nvim_win_set_config(%d, #{relative:'editor', row:0, col:0, width:5, height:5}) ]]):format(t2_win3)) cur_win = api.nvim_get_current_win() - api.nvim_win_set_config(0, { win = t2_win3, split = 'left' }) - matches( - 'Cannot change window from different tabpage into float$', - api.nvim_get_vvar('errmsg') + eq( + 'Floating state of windows to split changed', + pcall_err(api.nvim_win_set_config, 0, { win = t2_win3, split = 'left' }) ) - -- The error doesn't abort moving the window (or maybe it should, if that's wanted?) - neq(cur_win, api.nvim_get_current_win()) - eq(t2, api.nvim_win_get_tabpage(cur_win)) + eq('editor', api.nvim_win_get_config(t2_win3).relative) + eq('', api.nvim_win_get_config(0).relative) + eq(cur_win, api.nvim_get_current_win()) end) it('expected autocmds when moving window to other tabpage', function() @@ -3228,6 +3238,35 @@ describe('API/win', function() api.nvim_win_set_config(t2_cur_win, { split = 'left', win = 0 }) eq(t2_alt_win, api.nvim_tabpage_get_win(t2)) eq(t1, api.nvim_win_get_tabpage(t2_cur_win)) + + -- Very fun: move curwin between tabpages, converting from split to float, but with an autocmd + -- that deletes altwin after we're bumped to it, re-enters curwin, then switches to a 3rd + -- tabpage. tp_curwin of the window's old tabpage shouldn't be set to the freed altwin! + command('tablast | tab split | tabprevious | split') + command('autocmd WinEnter * ++once quit | let expect_alt = win_getid() | wincmd p | tabnext') + api.nvim_win_set_config(0, { + relative = 'editor', + win = api.nvim_tabpage_get_win(t1), + row = 0, + col = 0, + width = 5, + height = 5, + }) + eq(eval('g:expect_alt'), api.nvim_tabpage_get_win(t2)) + + -- Same, but for float -> float. + command('tabprevious | split') + api.nvim_open_win(0, true, { relative = 'editor', row = 0, col = 0, width = 1, height = 1 }) + command('autocmd WinEnter * ++once quit | let expect_alt = win_getid() | wincmd p | tabnext') + api.nvim_win_set_config(0, { + relative = 'editor', + win = api.nvim_tabpage_get_win(t1), + row = 0, + col = 0, + width = 5, + height = 5, + }) + eq(eval('g:expect_alt'), api.nvim_tabpage_get_win(t2)) end) it('set_config cannot change "noautocmd" #36409', function() @@ -3742,7 +3781,6 @@ describe('API/win', function() it('preserve current floating window when moving fails', function() local buf = api.nvim_create_buf(false, true) - local tab1_win = api.nvim_get_current_win() local float_win = api.nvim_open_win(buf, true, { relative = 'editor', row = 1, @@ -3770,18 +3808,6 @@ describe('API/win', function() ) eq(float_win, api.nvim_get_current_win()) - command( - ('autocmd WinLeave * ++once call nvim_win_set_config(%d, #{split: "left", win: %d})'):format( - float_win, - tab1_win - ) - ) - eq( - ('Window %d was made non-floating'):format(float_win), - pcall_err(api.nvim_win_set_config, float_win, { win = tab3_win }) - ) - eq(float_win, api.nvim_get_current_win()) - -- Need multigrid for external windows. Screen.new(20, 9, { ext_multigrid = true }) api.nvim_win_set_config(float_win, { external = true, width = 5, height = 5 }) diff --git a/test/functional/ui/float_spec.lua b/test/functional/ui/float_spec.lua index 9d3e83352c..e9f608099c 100644 --- a/test/functional/ui/float_spec.lua +++ b/test/functional/ui/float_spec.lua @@ -3782,10 +3782,7 @@ describe('float window', function() it('API has proper error messages', function() local buf = api.nvim_create_buf(false, false) eq("Invalid key: 'bork'", pcall_err(api.nvim_open_win, buf, false, { width = 20, height = 2, bork = true })) - eq( - "Must specify 'relative' or 'external' when creating a float", - pcall_err(api.nvim_open_win, buf, false, { win = 0 }) - ) + eq("Must specify 'relative' or 'external' when creating a float", pcall_err(api.nvim_open_win, buf, false, { win = 0 })) eq( "floating windows cannot have 'vertical'", pcall_err(api.nvim_open_win, buf, false, { width = 20, height = 2, relative = 'editor', row = 0, col = 0, vertical = true }) @@ -12030,6 +12027,7 @@ describe('float window', function() -- Check tablines are redrawn even when moving floats between two non-current tabpages. command('tabnew') + local tab3_win = api.nvim_get_current_win() if multigrid then screen:expect({ grid = [[ @@ -12094,6 +12092,270 @@ describe('float window', function() | ]]) end + + -- Try converting a split to a float, then moving it to another tabpage in one call. + command('set norightleft | new') + fn.setline(1, 'floaty mcfloatface') + api.nvim_win_set_config(0, { relative = 'editor', win = tab1_win, row = 3, col = 3, width = 15, height = 5 }) + if multigrid then + screen:expect({ + grid = [[ + ## grid 1 + {9: }{10:2}{9:+ No Name] }{3: [No Name] }{9: }{10:2}{9:+ No Name] }{5: }{9:X}| + [6:----------------------------------------]|*5 + [3:----------------------------------------]| + ## grid 2 (hidden) + olleh| + {0: ~}|*4 + ## grid 3 + | + ## grid 4 (hidden) + hello | + {0:~ }|*4 + ## grid 5 (hidden) + {1:hello }| + {2:~ }|*4 + ## grid 6 + ^ | + {0:~ }|*4 + ## grid 7 (hidden) + floaty mcfloatface | + {0:~ }| + ]], + }) + else + screen:expect([[ + {9: }{10:2}{9:+ No Name] }{3: [No Name] }{9: }{10:2}{9:+ No Name] }{5: }{9:X}| + ^ | + {0:~ }|*4 + | + ]]) + end + + command('tabfirst') + if multigrid then + screen:expect({ + grid = [[ + ## grid 1 + {3: }{11:2}{3:+ No Name] }{9: [No Name] }{10:2}{9:+ No Name] }{5: }{9:X}| + [2:----------------------------------------]|*5 + [3:----------------------------------------]| + ## grid 2 + olle^h| + {0: ~}|*4 + ## grid 3 + | + ## grid 4 (hidden) + hello | + {0:~ }|*4 + ## grid 5 (hidden) + {1:hello }| + {2:~ }|*4 + ## grid 6 (hidden) + | + {0:~ }|*4 + ## grid 7 + {1:floaty mcfloatf}| + {1:ace }| + {2:~ }|*3 + ]], + float_pos = { + [7] = { 1004, 'NW', 1, 3, 3, true, 50, 1, 1, 3 }, + }, + }) + else + screen:expect([[ + {3: }{11:2}{3:+ No Name] }{9: [No Name] }{10:2}{9:+ No Name] }{5: }{9:X}| + {1:floaty mcfloatf} olle^h| + {0: }{1:ace }{0: ~}| + {0: }{2:~ }{0: ~}|*3 + | + ]]) + end + + -- Works when doing the same between two non-current tabpages. + local float2 = api.nvim_open_win(0, false, { split = 'below', win = tab3_win }) + api.nvim_win_set_config(float2, { relative = 'win', win = tab2_win, row = 2, col = 7, width = 4, height = 4, border = 'single' }) + if multigrid then + screen:expect({ + grid = [[ + ## grid 1 + {3: }{11:2}{3:+ No Name] }{9: [No Name] }{10:3}{9:+ No Name] }{5: }{9:X}| + [2:----------------------------------------]|*5 + [3:----------------------------------------]| + ## grid 2 + olle^h| + {0: ~}|*4 + ## grid 3 + | + ## grid 4 (hidden) + hello | + {0:~ }|*4 + ## grid 5 (hidden) + {1:hello }| + {2:~ }|*4 + ## grid 6 (hidden) + | + {0:~ }|*4 + ## grid 7 + {1:floaty mcfloatf}| + {1:ace }| + {2:~ }|*3 + ]], + float_pos = { + [7] = { 1004, 'NW', 1, 3, 3, true, 50, 1, 1, 3 }, + }, + }) + else + screen:expect([[ + {3: }{11:2}{3:+ No Name] }{9: [No Name] }{10:3}{9:+ No Name] }{5: }{9:X}| + {1:floaty mcfloatf} olle^h| + {0: }{1:ace }{0: ~}| + {0: }{2:~ }{0: ~}|*3 + | + ]]) + end + + command('tabnext') + if multigrid then + screen:expect({ + grid = [[ + ## grid 1 + {9: }{10:2}{9:+ No Name] }{3: [No Name] }{9: }{10:3}{9:+ No Name] }{5: }{9:X}| + [6:----------------------------------------]|*5 + [3:----------------------------------------]| + ## grid 2 (hidden) + olleh| + {0: ~}|*4 + ## grid 3 + | + ## grid 4 (hidden) + hello | + {0:~ }|*4 + ## grid 5 (hidden) + {1:hello }| + {2:~ }|*4 + ## grid 6 + ^ | + {0:~ }|*4 + ## grid 7 (hidden) + {1:floaty mcfloatf}| + {1:ace }| + {2:~ }|*3 + ]], + }) + else + screen:expect([[ + {9: }{10:2}{9:+ No Name] }{3: [No Name] }{9: }{10:3}{9:+ No Name] }{5: }{9:X}| + ^ | + {0:~ }|*4 + | + ]]) + end + + command('tabnext') + if multigrid then + screen:expect({ + grid = [[ + ## grid 1 + {9: }{10:2}{9:+ No Name] [No Name] }{3: }{11:3}{3:+ No Name] }{5: }{9:X}| + [4:----------------------------------------]|*5 + [3:----------------------------------------]| + ## grid 2 (hidden) + olleh| + {0: ~}|*4 + ## grid 3 + | + ## grid 4 + ^hello | + {0:~ }|*4 + ## grid 5 + {1:hello }| + {2:~ }|*4 + ## grid 6 (hidden) + | + {0:~ }|*4 + ## grid 7 (hidden) + {1:floaty mcfloatf}| + {1:ace }| + {2:~ }|*3 + ## grid 8 + {5:┌────┐}| + {5:│}{1:lleh}{5:│}| + {5:│}{1: o}{5:│}| + {5:│}{2: ~}{5:│}|*2 + {5:└────┘}| + ]], + float_pos = { + [5] = { 1002, 'NW', 4, 0, 0, true, 50, 1, 1, 0 }, + [8] = { 1005, 'NW', 4, 2, 7, true, 50, 2, 0, 7 }, + }, + }) + else + screen:expect([[ + {9: }{10:2}{9:+ No }{5:┌────┐}{9: [No Name] }{3: }{11:3}{3:+ No Name] }{5: }{9:X}| + {1:^hello }{5:│}{1:lleh}{5:│} | + {2:~ }{5:│}{1: o}{5:│}{0: }| + {2:~ }{5:│}{2: ~}{5:│}{0: }|*2 + {2:~ }{5:└────┘}{0: }| + | + ]]) + end + + -- Used relative=win on two floats relative to this window. + -- Split it to the right to ensure both follow. + command('topleft vsplit') + if multigrid then + screen:expect({ + grid = [[ + ## grid 1 + {9: }{10:2}{9:+ No Name] [No Name] }{3: }{11:4}{3:+ No Name] }{5: }{9:X}| + [9:--------------------]{5:│}[4:-------------------]|*4 + {4:[No Name] [+] }{5:[No Name] [+] }| + [3:----------------------------------------]| + ## grid 2 (hidden) + olleh| + {0: ~}|*4 + ## grid 3 + | + ## grid 4 + hello | + {0:~ }|*3 + ## grid 5 + {1:hello }| + {2:~ }|*4 + ## grid 6 (hidden) + | + {0:~ }|*4 + ## grid 7 (hidden) + {1:floaty mcfloatf}| + {1:ace }| + {2:~ }|*3 + ## grid 8 + {5:┌────┐}| + {5:│}{1:lleh}{5:│}| + {5:│}{1: o}{5:│}| + {5:│}{2: ~}{5:│}|*2 + {5:└────┘}| + ## grid 9 + ^hello | + {0:~ }|*3 + ]], + float_pos = { + [5] = { 1002, 'NW', 4, 0, 0, true, 50, 1, 1, 21 }, + [8] = { 1005, 'NW', 4, 2, 7, true, 50, 2, 0, 28 }, + }, + }) + else + screen:expect([[ + {9: }{10:2}{9:+ No Name] [No Name] }{3: }{11:4}{3:+ }{5:┌────┐}{3:e] }{5: }{9:X}| + ^hello {5:│}{1:hello }{5:│}{1:lleh}{5:│} | + {0:~ }{5:│}{2:~ }{5:│}{1: o}{5:│}{0: }| + {0:~ }{5:│}{2:~ }{5:│}{2: ~}{5:│}{0: }|*2 + {4:[No Name] [+] }{2:~ }{5:└────┘ }| + | + ]]) + end end) end