aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/window.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/window.c')
-rw-r--r--src/nvim/window.c652
1 files changed, 567 insertions, 85 deletions
diff --git a/src/nvim/window.c b/src/nvim/window.c
index bb71b12aed..18fad76a95 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -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. */
- (void)winframe_remove(curwin, &dir, NULL);
+ 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).
*/
- wp = frame2win(win_altframe(win, NULL));
+ 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,12 +2446,15 @@ int win_close(win_T *win, bool free_buf)
// using the window.
check_cursor();
}
- 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.
- win_equal(curwin, curwin->w_frame->fr_parent == win_frame, dir);
- } else {
- win_comp_pos();
+
+ 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.
+ win_equal(curwin, curwin->w_frame->fr_parent == win_frame, dir);
+ } else {
+ win_comp_pos();
+ }
}
if (close_curwin) {
@@ -2167,10 +2575,18 @@ win_free_mem (
frame_T *frp;
win_T *wp;
- /* Remove the window and its frame from the tree of frames. */
- frp = win->w_frame;
- wp = winframe_remove(win, dirp, tp);
- xfree(frp);
+ 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);
- }
+ 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;
}
- frame_setheight(win->w_frame, height + win->w_status_height);
+ 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 (full_screen && msg_scrolled == 0 && row < cmdline_row) {
- grid_fill(&default_grid, row, cmdline_row, 0, (int)Columns, ' ', ' ', 0);
+ // 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;
}
- cmdline_row = row;
- msg_row = row;
- msg_col = 0;
redraw_all_later(NOT_VALID);
}
@@ -4358,14 +4828,16 @@ 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) {
@@ -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);
- frame_setwidth(wp->w_frame, width + wp->w_vsep_width);
-
- /* recompute the window positions */
- (void)win_comp_pos();
+ // 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;
}