aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBjörn Linse <bjorn.linse@gmail.com>2021-03-22 23:27:59 +0100
committerGitHub <noreply@github.com>2021-03-22 23:27:59 +0100
commit1df1098b0c54258a41c4f97c9d3131d061a5b206 (patch)
tree315d116677afd7dfbfbc6858949e66e574c81aee /src
parent6fef28da58a217a45d2d6e5b68ecfddd1eb84c29 (diff)
parent243820ebd0d9df7664311c8bf79d879bf23eb742 (diff)
downloadrneovim-1df1098b0c54258a41c4f97c9d3131d061a5b206.tar.gz
rneovim-1df1098b0c54258a41c4f97c9d3131d061a5b206.tar.bz2
rneovim-1df1098b0c54258a41c4f97c9d3131d061a5b206.zip
Merge pull request #13998 from bfredl/border
floats: add MS-DOS mode (borders)
Diffstat (limited to 'src')
-rw-r--r--src/nvim/api/private/helpers.c352
-rw-r--r--src/nvim/api/vim.c23
-rw-r--r--src/nvim/autocmd.c8
-rw-r--r--src/nvim/buffer_defs.h13
-rw-r--r--src/nvim/edit.c2
-rw-r--r--src/nvim/eval.c2
-rw-r--r--src/nvim/grid_defs.h17
-rw-r--r--src/nvim/highlight.c15
-rw-r--r--src/nvim/highlight_defs.h2
-rw-r--r--src/nvim/mbyte.c3
-rw-r--r--src/nvim/message.c2
-rw-r--r--src/nvim/mouse.c14
-rw-r--r--src/nvim/option.c4
-rw-r--r--src/nvim/popupmnu.c2
-rw-r--r--src/nvim/screen.c138
-rw-r--r--src/nvim/syntax.c1
-rw-r--r--src/nvim/window.c311
17 files changed, 577 insertions, 332 deletions
diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c
index 7cee569989..d2b787a6f5 100644
--- a/src/nvim/api/private/helpers.c
+++ b/src/nvim/api/private/helpers.c
@@ -1645,6 +1645,20 @@ bool api_object_to_bool(Object obj, const char *what,
}
}
+int object_to_hl_id(Object obj, const char *what, Error *err)
+{
+ if (obj.type == kObjectTypeString) {
+ String str = obj.data.string;
+ return str.size ? syn_check_group((char_u *)str.data, (int)str.size) : 0;
+ } else if (obj.type == kObjectTypeInteger) {
+ return (int)obj.data.integer;
+ } else {
+ api_set_error(err, kErrorTypeValidation,
+ "%s is not a valid highlight", what);
+ return 0;
+ }
+}
+
HlMessage parse_hl_msg(Array chunks, Error *err)
{
HlMessage hl_msg = KV_INITIAL_VALUE;
@@ -1720,3 +1734,341 @@ DecorProvider *get_provider(NS ns_id, bool force)
return item;
}
+
+static bool parse_float_anchor(String anchor, FloatAnchor *out)
+{
+ if (anchor.size == 0) {
+ *out = (FloatAnchor)0;
+ }
+ char *str = anchor.data;
+ if (striequal(str, "NW")) {
+ *out = 0; // NW is the default
+ } else if (striequal(str, "NE")) {
+ *out = kFloatAnchorEast;
+ } else if (striequal(str, "SW")) {
+ *out = kFloatAnchorSouth;
+ } else if (striequal(str, "SE")) {
+ *out = kFloatAnchorSouth | kFloatAnchorEast;
+ } else {
+ return false;
+ }
+ return true;
+}
+
+static bool parse_float_relative(String relative, FloatRelative *out)
+{
+ char *str = relative.data;
+ if (striequal(str, "editor")) {
+ *out = kFloatRelativeEditor;
+ } else if (striequal(str, "win")) {
+ *out = kFloatRelativeWindow;
+ } else if (striequal(str, "cursor")) {
+ *out = kFloatRelativeCursor;
+ } else {
+ return false;
+ }
+ return true;
+}
+
+static bool parse_float_bufpos(Array bufpos, lpos_T *out)
+{
+ if (bufpos.size != 2
+ || bufpos.items[0].type != kObjectTypeInteger
+ || bufpos.items[1].type != kObjectTypeInteger) {
+ return false;
+ }
+ out->lnum = bufpos.items[0].data.integer;
+ out->col = (colnr_T)bufpos.items[1].data.integer;
+ return true;
+}
+
+static void parse_border_style(Object style, FloatConfig *fconfig, Error *err)
+{
+ struct {
+ const char *name;
+ schar_T chars[8];
+ } defaults[] = {
+ { "double", { "╔", "═", "╗", "║", "╝", "═", "╚", "║" } },
+ { "single", { "┌", "─", "┐", "│", "┘", "─", "└", "│" } },
+ { NULL, { { NUL } } },
+ };
+
+ schar_T *chars = fconfig->border_chars;
+ int *hl_ids = fconfig->border_hl_ids;
+
+ fconfig->border = true;
+
+ if (style.type == kObjectTypeArray) {
+ Array arr = style.data.array;
+ size_t size = arr.size;
+ if (!size || size > 8 || (size & (size-1))) {
+ api_set_error(err, kErrorTypeValidation,
+ "invalid number of border chars");
+ return;
+ }
+ for (size_t i = 0; i < size; i++) {
+ Object iytem = arr.items[i];
+ String string = NULL_STRING;
+ int hl_id = 0;
+ if (iytem.type == kObjectTypeArray) {
+ Array iarr = iytem.data.array;
+ if (!iarr.size || iarr.size > 2) {
+ api_set_error(err, kErrorTypeValidation, "invalid border char");
+ return;
+ }
+ if (iarr.items[0].type != kObjectTypeString) {
+ api_set_error(err, kErrorTypeValidation, "invalid border char");
+ return;
+ }
+ string = iarr.items[0].data.string;
+ if (iarr.size == 2) {
+ hl_id = object_to_hl_id(iarr.items[1], "border char highlight", err);
+ if (ERROR_SET(err)) {
+ return;
+ }
+ }
+
+ } else if (iytem.type == kObjectTypeString) {
+ string = iytem.data.string;
+ } else {
+ api_set_error(err, kErrorTypeValidation, "invalid border char");
+ return;
+ }
+ if (!string.size
+ || mb_string2cells_len((char_u *)string.data, string.size) != 1) {
+ api_set_error(err, kErrorTypeValidation,
+ "border chars must be one cell");
+ }
+ size_t len = MIN(string.size, sizeof(*chars)-1);
+ memcpy(chars[i], string.data, len);
+ chars[i][len] = NUL;
+ hl_ids[i] = hl_id;
+ }
+ while (size < 8) {
+ memcpy(chars+size, chars, sizeof(*chars) * size);
+ memcpy(hl_ids+size, hl_ids, sizeof(*hl_ids) * size);
+ size <<= 1;
+ }
+ } else if (style.type == kObjectTypeString) {
+ String str = style.data.string;
+ if (str.size == 0 || strequal(str.data, "none")) {
+ fconfig->border = false;
+ return;
+ }
+ for (size_t i = 0; defaults[i].name; i++) {
+ if (strequal(str.data, defaults[i].name)) {
+ memcpy(chars, defaults[i].chars, sizeof(defaults[i].chars));
+ memset(hl_ids, 0, 8 * sizeof(*hl_ids));
+ return;
+ }
+ }
+ api_set_error(err, kErrorTypeValidation,
+ "invalid border style \"%s\"", str.data);
+ }
+}
+
+bool parse_float_config(Dictionary config, FloatConfig *fconfig, bool reconf,
+ Error *err)
+{
+ // TODO(bfredl): use a get/has_key interface instead and get rid of extra
+ // flags
+ bool has_row = false, has_col = false, has_relative = false;
+ bool has_external = false, has_window = false;
+ bool has_width = false, has_height = false;
+ bool has_bufpos = 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) {
+ fconfig->row = (double)val.data.integer;
+ } else if (val.type == kObjectTypeFloat) {
+ fconfig->row = val.data.floating;
+ } else {
+ api_set_error(err, kErrorTypeValidation,
+ "'row' key must be Integer or Float");
+ return false;
+ }
+ } else if (!strcmp(key, "col")) {
+ has_col = true;
+ if (val.type == kObjectTypeInteger) {
+ fconfig->col = (double)val.data.integer;
+ } else if (val.type == kObjectTypeFloat) {
+ fconfig->col = val.data.floating;
+ } else {
+ api_set_error(err, kErrorTypeValidation,
+ "'col' key must be Integer or Float");
+ return false;
+ }
+ } else if (strequal(key, "width")) {
+ has_width = true;
+ if (val.type == kObjectTypeInteger && val.data.integer > 0) {
+ fconfig->width = (int)val.data.integer;
+ } else {
+ api_set_error(err, kErrorTypeValidation,
+ "'width' key must be a positive Integer");
+ return false;
+ }
+ } else if (strequal(key, "height")) {
+ has_height = true;
+ if (val.type == kObjectTypeInteger && val.data.integer > 0) {
+ fconfig->height= (int)val.data.integer;
+ } else {
+ api_set_error(err, kErrorTypeValidation,
+ "'height' key must be a positive Integer");
+ return false;
+ }
+ } else if (!strcmp(key, "anchor")) {
+ if (val.type != kObjectTypeString) {
+ api_set_error(err, kErrorTypeValidation,
+ "'anchor' key must be String");
+ return false;
+ }
+ if (!parse_float_anchor(val.data.string, &fconfig->anchor)) {
+ api_set_error(err, kErrorTypeValidation,
+ "Invalid value of 'anchor' key");
+ return false;
+ }
+ } else if (!strcmp(key, "relative")) {
+ if (val.type != kObjectTypeString) {
+ api_set_error(err, kErrorTypeValidation,
+ "'relative' key must be String");
+ return false;
+ }
+ // ignore empty string, to match nvim_win_get_config
+ if (val.data.string.size > 0) {
+ has_relative = true;
+ if (!parse_float_relative(val.data.string, &fconfig->relative)) {
+ api_set_error(err, kErrorTypeValidation,
+ "Invalid value of 'relative' key");
+ return false;
+ }
+ }
+ } else if (!strcmp(key, "win")) {
+ has_window = true;
+ if (val.type != kObjectTypeInteger
+ && val.type != kObjectTypeWindow) {
+ api_set_error(err, kErrorTypeValidation,
+ "'win' key must be Integer or Window");
+ return false;
+ }
+ fconfig->window = (Window)val.data.integer;
+ } else if (!strcmp(key, "bufpos")) {
+ if (val.type != kObjectTypeArray) {
+ api_set_error(err, kErrorTypeValidation,
+ "'bufpos' key must be Array");
+ return false;
+ }
+ if (!parse_float_bufpos(val.data.array, &fconfig->bufpos)) {
+ api_set_error(err, kErrorTypeValidation,
+ "Invalid value of 'bufpos' key");
+ return false;
+ }
+ has_bufpos = true;
+ } else if (!strcmp(key, "external")) {
+ if (val.type == kObjectTypeInteger) {
+ fconfig->external = val.data.integer;
+ } else if (val.type == kObjectTypeBoolean) {
+ fconfig->external = val.data.boolean;
+ } else {
+ api_set_error(err, kErrorTypeValidation,
+ "'external' key must be Boolean");
+ return false;
+ }
+ has_external = fconfig->external;
+ } else if (!strcmp(key, "focusable")) {
+ if (val.type == kObjectTypeInteger) {
+ fconfig->focusable = val.data.integer;
+ } else if (val.type == kObjectTypeBoolean) {
+ fconfig->focusable = val.data.boolean;
+ } else {
+ api_set_error(err, kErrorTypeValidation,
+ "'focusable' key must be Boolean");
+ return false;
+ }
+ } else if (!strcmp(key, "border")) {
+ parse_border_style(val, fconfig, err);
+ if (ERROR_SET(err)) {
+ return false;
+ }
+ } else if (!strcmp(key, "style")) {
+ if (val.type != kObjectTypeString) {
+ api_set_error(err, kErrorTypeValidation,
+ "'style' key must be String");
+ return false;
+ }
+ if (val.data.string.data[0] == NUL) {
+ fconfig->style = kWinStyleUnused;
+ } else if (striequal(val.data.string.data, "minimal")) {
+ fconfig->style = kWinStyleMinimal;
+ } else {
+ api_set_error(err, kErrorTypeValidation,
+ "Invalid value of 'style' key");
+ }
+ } else {
+ api_set_error(err, kErrorTypeValidation,
+ "Invalid key '%s'", key);
+ return false;
+ }
+ }
+
+ if (has_window && !(has_relative
+ && fconfig->relative == kFloatRelativeWindow)) {
+ api_set_error(err, kErrorTypeValidation,
+ "'win' key is only valid with relative='win'");
+ return false;
+ }
+
+ if ((has_relative && fconfig->relative == kFloatRelativeWindow)
+ && (!has_window || fconfig->window == 0)) {
+ fconfig->window = curwin->handle;
+ }
+
+ if (has_window && !has_bufpos) {
+ fconfig->bufpos.lnum = -1;
+ }
+
+ if (has_bufpos) {
+ if (!has_row) {
+ fconfig->row = (fconfig->anchor & kFloatAnchorSouth) ? 0 : 1;
+ has_row = true;
+ }
+ if (!has_col) {
+ fconfig->col = 0;
+ has_col = true;
+ }
+ }
+
+ if (has_relative && has_external) {
+ api_set_error(err, kErrorTypeValidation,
+ "Only one of 'relative' and 'external' must be used");
+ return false;
+ } else if (!reconf && !has_relative && !has_external) {
+ api_set_error(err, kErrorTypeValidation,
+ "One of 'relative' and 'external' must be used");
+ return false;
+ } else if (has_relative) {
+ fconfig->external = false;
+ }
+
+ if (!reconf && !(has_height && has_width)) {
+ api_set_error(err, kErrorTypeValidation,
+ "Must specify 'width' and 'height'");
+ return false;
+ }
+
+ if (fconfig->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,
+ "'relative' requires 'row'/'col' or 'bufpos'");
+ return false;
+ }
+ return true;
+}
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index 6358f35d0a..9dde62f0ee 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -1416,6 +1416,25 @@ void nvim_chan_send(Integer chan, String data, Error *err)
/// end-of-buffer region is hidden by setting `eob` flag of
/// 'fillchars' to a space char, and clearing the
/// |EndOfBuffer| region in 'winhighlight'.
+/// - `border`: style of (optional) window border. This can either be a string
+/// or an array. the string values are:
+/// - "none" No border. This is the default
+/// - "single" a single line box
+/// - "double" a double line box
+/// If it is an array it should be an array of eight items or any divisor of
+/// eight. The array will specifify the eight chars building up the border
+/// in a clockwise fashion starting with the top-left corner. As, an
+/// example, the double box style could be specified as:
+/// [ "╔", "═" ,"╗", "║", "╝", "═", "╚", "║" ]
+/// if the number of chars are less than eight, they will be repeated. Thus
+/// an ASCII border could be specified as:
+/// [ "/", "-", "\\", "|" ]
+/// or all chars the same as:
+/// [ "x" ]
+/// By default `FloatBorder` highlight is used which links to `VertSplit`
+/// when not defined. It could also be specified by character:
+/// [ {"+", "MyCorner"}, {"x", "MyBorder"} ]
+///
/// @param[out] err Error details, if any
///
/// @return Window handle, or 0 on error
@@ -2831,8 +2850,8 @@ Array nvim__inspect_cell(Integer grid, Integer row, Integer col, Error *err)
g = &pum_grid;
} else if (grid > 1) {
win_T *wp = get_win_by_grid_handle((handle_T)grid);
- if (wp != NULL && wp->w_grid.chars != NULL) {
- g = &wp->w_grid;
+ if (wp != NULL && wp->w_grid_alloc.chars != NULL) {
+ g = &wp->w_grid_alloc;
} else {
api_set_error(err, kErrorTypeValidation,
"No grid with the given handle");
diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c
index 2ddb925d40..f71075ae74 100644
--- a/src/nvim/autocmd.c
+++ b/src/nvim/autocmd.c
@@ -1192,10 +1192,10 @@ void aucmd_restbuf(aco_save_T *aco)
win_remove(curwin, NULL);
handle_unregister_window(curwin);
- if (curwin->w_grid.chars != NULL) {
- ui_comp_remove_grid(&curwin->w_grid);
- ui_call_win_hide(curwin->w_grid.handle);
- grid_free(&curwin->w_grid);
+ if (curwin->w_grid_alloc.chars != NULL) {
+ ui_comp_remove_grid(&curwin->w_grid_alloc);
+ ui_call_win_hide(curwin->w_grid_alloc.handle);
+ grid_free(&curwin->w_grid_alloc);
}
aucmd_win_used = false;
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index a05bd6fcc7..e8038e7281 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -1079,6 +1079,10 @@ typedef struct {
bool external;
bool focusable;
WinStyle style;
+ bool border;
+ schar_T border_chars[8];
+ int border_hl_ids[8];
+ int border_attr[8];
} FloatConfig;
#define FLOAT_CONFIG_INIT ((FloatConfig){ .height = 0, .width = 0, \
@@ -1256,6 +1260,11 @@ struct window_S {
int w_height_request;
int w_width_request;
+ int w_border_adj;
+ // outer size of window grid, including border
+ int w_height_outer;
+ int w_width_outer;
+
/*
* === start of cached values ====
*/
@@ -1331,7 +1340,8 @@ struct window_S {
// w_redr_type is REDRAW_TOP
linenr_T w_redraw_top; // when != 0: first line needing redraw
linenr_T w_redraw_bot; // when != 0: last line needing redraw
- int w_redr_status; // if TRUE status line must be redrawn
+ bool w_redr_status; // if true status line must be redrawn
+ bool w_redr_border; // if true border must be redrawn
// remember what is shown in the ruler for this window (if 'ruler' set)
pos_T w_ru_cursor; // cursor position shown in ruler
@@ -1409,6 +1419,7 @@ struct window_S {
int w_tagstacklen; // number of tags on stack
ScreenGrid w_grid; // the grid specific to the window
+ ScreenGrid w_grid_alloc; // 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;
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index 68c7438ea3..cffa46fa77 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -1565,7 +1565,7 @@ void edit_putchar(int c, bool highlight)
{
int attr;
- if (curwin->w_grid.chars != NULL || default_grid.chars != NULL) {
+ if (curwin->w_grid_alloc.chars != NULL || default_grid.chars != NULL) {
update_topline(curwin); // just in case w_topline isn't valid
validate_cursor();
if (highlight) {
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index f190ef14c4..e1fcbdce25 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -8501,7 +8501,7 @@ static bool tv_is_luafunc(typval_T *tv)
int check_luafunc_name(const char *str, bool paren)
{
const char *p = str;
- while (ASCII_ISALNUM(*p) || *p == '_' || *p == '.') {
+ while (ASCII_ISALNUM(*p) || *p == '_' || *p == '.' || *p == '\'') {
p++;
}
if (*p != (paren ? '(' : NUL)) {
diff --git a/src/nvim/grid_defs.h b/src/nvim/grid_defs.h
index 752be4f5a8..3b34af46e4 100644
--- a/src/nvim/grid_defs.h
+++ b/src/nvim/grid_defs.h
@@ -7,7 +7,7 @@
#include "nvim/types.h"
-#define MAX_MCO 6 // maximum value for 'maxcombine'
+#define MAX_MCO 6 // fixed value for 'maxcombine'
// The characters and attributes drawn on grids.
typedef char_u schar_T[(MAX_MCO+1) * 4 + 1];
@@ -35,7 +35,8 @@ typedef int sattr_T;
/// line_wraps[] is an array of boolean flags indicating if the screen line
/// wraps to the next line. It can only be true if a window occupies the entire
/// screen width.
-typedef struct {
+typedef struct ScreenGrid ScreenGrid;
+struct ScreenGrid {
handle_T handle;
schar_T *chars;
@@ -58,10 +59,13 @@ typedef struct {
// external UI.
bool throttled;
- // offsets for the grid relative to the global screen. Used by screen.c
- // for windows that don't have w_grid->chars etc allocated
+ // TODO(bfredl): maybe physical grids and "views" (i e drawing
+ // specifications) should be two separate types?
+ // offsets for the grid relative to another grid. Used for grids
+ // that are views into another, actually allocated grid 'target'
int row_offset;
int col_offset;
+ ScreenGrid *target;
// whether the compositor should blend the grid with the background grid
bool blending;
@@ -89,9 +93,10 @@ typedef struct {
// compositor should momentarily ignore the grid. Used internally when
// moving around grids etc.
bool comp_disabled;
-} ScreenGrid;
+};
#define SCREEN_GRID_INIT { 0, NULL, NULL, NULL, NULL, NULL, 0, 0, false, \
- false, 0, 0, false, true, 0, 0, 0, 0, 0, false }
+ false, 0, 0, NULL, false, true, \
+ 0, 0, 0, 0, 0, false }
#endif // NVIM_GRID_DEFS_H
diff --git a/src/nvim/highlight.c b/src/nvim/highlight.c
index b01cdde236..f03382bea7 100644
--- a/src/nvim/highlight.c
+++ b/src/nvim/highlight.c
@@ -341,6 +341,17 @@ void update_window_hl(win_T *wp, bool invalid)
}
wp->w_hl_attrs[hlf] = attr;
}
+
+ if (wp->w_floating && wp->w_float_config.border) {
+ for (int i = 0; i < 8; i++) {
+ int attr = wp->w_hl_attrs[HLF_BORDER];
+ if (wp->w_float_config.border_hl_ids[i]) {
+ attr = hl_get_ui_attr(HLF_BORDER, wp->w_float_config.border_hl_ids[i],
+ false);
+ }
+ wp->w_float_config.border_attr[i] = attr;
+ }
+ }
}
/// Gets HL_UNDERLINE highlight.
@@ -517,6 +528,10 @@ static HlAttrs get_colors_force(int attr)
/// @return the resulting attributes.
int hl_blend_attrs(int back_attr, int front_attr, bool *through)
{
+ if (front_attr < 0 || back_attr < 0) {
+ return -1;
+ }
+
HlAttrs fattrs = get_colors_force(front_attr);
int ratio = fattrs.hl_blend;
if (ratio <= 0) {
diff --git a/src/nvim/highlight_defs.h b/src/nvim/highlight_defs.h
index 2bda094d8e..ed4aefb577 100644
--- a/src/nvim/highlight_defs.h
+++ b/src/nvim/highlight_defs.h
@@ -101,6 +101,7 @@ typedef enum {
, HLF_MSGSEP // message separator line
, HLF_NFLOAT // Floating window
, HLF_MSG // Message area
+ , HLF_BORDER // Floating window border
, HLF_COUNT // MUST be the last one
} hlf_T;
@@ -155,6 +156,7 @@ EXTERN const char *hlf_names[] INIT(= {
[HLF_MSGSEP] = "MsgSeparator",
[HLF_NFLOAT] = "NormalFloat",
[HLF_MSG] = "MsgArea",
+ [HLF_BORDER] = "FloatBorder",
});
diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c
index ec4f4cbc21..73e3ba53a5 100644
--- a/src/nvim/mbyte.c
+++ b/src/nvim/mbyte.c
@@ -571,11 +571,12 @@ size_t mb_string2cells(const char_u *str)
/// @param size maximum length of string. It will terminate on earlier NUL.
/// @return The number of cells occupied by string `str`
size_t mb_string2cells_len(const char_u *str, size_t size)
+ FUNC_ATTR_NONNULL_ARG(1)
{
size_t clen = 0;
for (const char_u *p = str; *p != NUL && p < str+size;
- p += utf_ptr2len_len(p, size+(p-str))) {
+ p += utfc_ptr2len_len(p, size+(p-str))) {
clen += utf_ptr2cells(p);
}
diff --git a/src/nvim/message.c b/src/nvim/message.c
index ba7a667a60..71cb345878 100644
--- a/src/nvim/message.c
+++ b/src/nvim/message.c
@@ -178,6 +178,7 @@ void msg_grid_validate(void)
msg_grid.throttled = false; // don't throttle in 'cmdheight' area
msg_scrolled_at_flush = msg_scrolled;
msg_grid.focusable = false;
+ msg_grid_adj.target = &msg_grid;
if (!msg_scrolled) {
msg_grid_set_pos(Rows - p_ch, false);
}
@@ -188,6 +189,7 @@ void msg_grid_validate(void)
ui_call_grid_destroy(msg_grid.handle);
msg_grid.throttled = false;
msg_grid_adj.row_offset = 0;
+ msg_grid_adj.target = &default_grid;
redraw_cmdline = true;
} else if (msg_grid.chars && !msg_scrolled && msg_grid_pos != Rows - p_ch) {
msg_grid_set_pos(Rows - p_ch, false);
diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c
index 9dc85797b4..fa9787a3ac 100644
--- a/src/nvim/mouse.c
+++ b/src/nvim/mouse.c
@@ -470,21 +470,21 @@ static win_T *mouse_find_grid_win(int *gridp, int *rowp, int *colp)
*gridp = DEFAULT_GRID_HANDLE;
} else if (*gridp > 1) {
win_T *wp = get_win_by_grid_handle(*gridp);
- if (wp && wp->w_grid.chars
+ if (wp && wp->w_grid_alloc.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);
+ *rowp = MIN(*rowp-wp->w_grid.row_offset, wp->w_grid.Rows-1);
+ *colp = MIN(*colp-wp->w_grid.col_offset, 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) {
+ if (&wp->w_grid_alloc != grid) {
continue;
}
*gridp = grid->handle;
- *rowp -= grid->comp_row;
- *colp -= grid->comp_col;
+ *rowp -= grid->comp_row+wp->w_grid.row_offset;
+ *colp -= grid->comp_col+wp->w_grid.col_offset;
return wp;
}
@@ -729,7 +729,7 @@ int mouse_check_fold(void)
if (wp && mouse_row >= 0 && mouse_row < Rows
&& mouse_col >= 0 && mouse_col <= Columns) {
int multigrid = ui_has(kUIMultigrid);
- ScreenGrid *gp = multigrid ? &wp->w_grid : &default_grid;
+ ScreenGrid *gp = multigrid ? &wp->w_grid_alloc : &default_grid;
int fdc = win_fdccol_count(wp);
int row = multigrid && mouse_grid == 0 ? click_row : mouse_row;
int col = multigrid && mouse_grid == 0 ? click_col : mouse_col;
diff --git a/src/nvim/option.c b/src/nvim/option.c
index dbd8ceb55c..d04329e104 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -4287,7 +4287,7 @@ static char *set_num_option(int opt_idx, char_u *varp, long value,
// 'floatblend'
curwin->w_p_winbl = MAX(MIN(curwin->w_p_winbl, 100), 0);
curwin->w_hl_needs_update = true;
- curwin->w_grid.blending = curwin->w_p_winbl > 0;
+ curwin->w_grid_alloc.blending = curwin->w_p_winbl > 0;
}
@@ -5800,7 +5800,7 @@ void didset_window_options(win_T *wp)
set_chars_option(wp, &wp->w_p_fcs, true);
set_chars_option(wp, &wp->w_p_lcs, true);
parse_winhl_opt(wp); // sets w_hl_needs_update also for w_p_winbl
- wp->w_grid.blending = wp->w_p_winbl > 0;
+ wp->w_grid_alloc.blending = wp->w_p_winbl > 0;
}
diff --git a/src/nvim/popupmnu.c b/src/nvim/popupmnu.c
index aef7ffa397..69c614fff9 100644
--- a/src/nvim/popupmnu.c
+++ b/src/nvim/popupmnu.c
@@ -139,7 +139,7 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed,
cursor_col = curwin->w_wcol;
}
- pum_anchor_grid = (int)curwin->w_grid.handle;
+ pum_anchor_grid = (int)curwin->w_grid.target->handle;
if (!ui_has(kUIMultigrid)) {
pum_anchor_grid = (int)default_grid.handle;
pum_win_row += curwin->w_winrow;
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index 467cac4f27..d50520f49e 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -232,7 +232,7 @@ void screen_invalidate_highlights(void)
{
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
redraw_later(wp, NOT_VALID);
- wp->w_grid.valid = false;
+ wp->w_grid_alloc.valid = false;
}
}
@@ -582,11 +582,18 @@ int update_screen(int type)
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);
+ if (wp->w_redr_type == CLEAR && wp->w_floating && wp->w_grid_alloc.chars) {
+ grid_invalidate(&wp->w_grid_alloc);
wp->w_redr_type = NOT_VALID;
}
+ // reallocate grid if needed.
+ win_grid_alloc(wp);
+
+ if (wp->w_redr_border || wp->w_redr_type >= NOT_VALID) {
+ win_redr_border(wp);
+ }
+
if (wp->w_redr_type != 0) {
if (!did_one) {
did_one = TRUE;
@@ -774,8 +781,6 @@ static void win_update(win_T *wp, Providers *providers)
type = wp->w_redr_type;
- win_grid_alloc(wp);
-
if (type >= NOT_VALID) {
wp->w_redr_status = true;
wp->w_lines_valid = 0;
@@ -4404,14 +4409,10 @@ void draw_virt_text(buf_T *buf, int *end_col, int max_col)
/// screen positions.
void screen_adjust_grid(ScreenGrid **grid, int *row_off, int *col_off)
{
- if (!(*grid)->chars && *grid != &default_grid) {
- *row_off += (*grid)->row_offset;
- *col_off += (*grid)->col_offset;
- if (*grid == &msg_grid_adj && msg_grid.chars) {
- *grid = &msg_grid;
- } else {
- *grid = &default_grid;
- }
+ if ((*grid)->target) {
+ *row_off += (*grid)->row_offset;
+ *col_off += (*grid)->col_offset;
+ *grid = (*grid)->target;
}
}
@@ -5415,6 +5416,46 @@ theend:
entered = FALSE;
}
+static void win_redr_border(win_T *wp)
+{
+ wp->w_redr_border = false;
+ if (!(wp->w_floating && wp->w_float_config.border)) {
+ return;
+ }
+
+ ScreenGrid *grid = &wp->w_grid_alloc;
+
+ schar_T *chars = wp->w_float_config.border_chars;
+ int *attrs = wp->w_float_config.border_attr;
+
+ int endrow = grid->Rows-1, endcol = grid->Columns-1;
+
+ grid_puts_line_start(grid, 0);
+ grid_put_schar(grid, 0, 0, chars[0], attrs[0]);
+ for (int i = 1; i < endcol; i++) {
+ grid_put_schar(grid, 0, i, chars[1], attrs[1]);
+ }
+ grid_put_schar(grid, 0, endcol, chars[2], attrs[2]);
+ grid_puts_line_flush(false);
+
+ for (int i = 1; i < endrow; i++) {
+ grid_puts_line_start(grid, i);
+ grid_put_schar(grid, i, 0, chars[7], attrs[7]);
+ grid_puts_line_flush(false);
+ grid_puts_line_start(grid, i);
+ grid_put_schar(grid, i, endcol, chars[3], attrs[3]);
+ grid_puts_line_flush(false);
+ }
+
+ grid_puts_line_start(grid, endrow);
+ grid_put_schar(grid, endrow, 0, chars[6], attrs[6]);
+ for (int i = 1; i < endcol; i++) {
+ grid_put_schar(grid, endrow, i, chars[5], attrs[5]);
+ }
+ grid_put_schar(grid, endrow, endcol, chars[4], attrs[4]);
+ grid_puts_line_flush(false);
+}
+
// Low-level functions to manipulate invidual character cells on the
// screen grid.
@@ -5552,6 +5593,20 @@ void grid_puts_line_start(ScreenGrid *grid, int row)
put_dirty_grid = grid;
}
+void grid_put_schar(ScreenGrid *grid, int row, int col, char_u *schar, int attr)
+{
+ assert(put_dirty_row == row);
+ unsigned int off = grid->line_offset[row] + col;
+ if (grid->attrs[off] != attr || schar_cmp(grid->chars[off], schar)) {
+ schar_copy(grid->chars[off], schar);
+ grid->attrs[off] = attr;
+
+ put_dirty_first = MIN(put_dirty_first, col);
+ // TODO(bfredl): Y U NO DOUBLEWIDTH?
+ put_dirty_last = MAX(put_dirty_last, col+1);
+ }
+}
+
/// like grid_puts(), but output "text[len]". When "len" is -1 output up to
/// a NUL.
void grid_puts_len(ScreenGrid *grid, char_u *text, int textlen, int row,
@@ -6143,12 +6198,15 @@ void check_for_delay(int check_msg_scroll)
void win_grid_alloc(win_T *wp)
{
ScreenGrid *grid = &wp->w_grid;
+ ScreenGrid *grid_allocated = &wp->w_grid_alloc;
int rows = wp->w_height_inner;
int cols = wp->w_width_inner;
+ int total_rows = wp->w_height_outer;
+ int total_cols = wp->w_width_outer;
bool want_allocation = ui_has(kUIMultigrid) || wp->w_floating;
- bool has_allocation = (grid->chars != NULL);
+ bool has_allocation = (grid_allocated->chars != NULL);
if (grid->Rows != rows) {
wp->w_lines_valid = 0;
@@ -6157,35 +6215,47 @@ void win_grid_alloc(win_T *wp)
}
int was_resized = false;
- if ((has_allocation != want_allocation)
- || grid->Rows != rows
- || grid->Columns != cols) {
- if (want_allocation) {
- grid_alloc(grid, rows, cols, wp->w_grid.valid, false);
- 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;
+ if (want_allocation && (!has_allocation
+ || grid_allocated->Rows != total_rows
+ || grid_allocated->Columns != total_cols)) {
+ grid_alloc(grid_allocated, total_rows, total_cols,
+ wp->w_grid_alloc.valid, false);
+ grid_allocated->valid = true;
+ if (wp->w_border_adj) {
+ wp->w_redr_border = true;
}
was_resized = true;
- } else if (want_allocation && has_allocation && !wp->w_grid.valid) {
- grid_invalidate(grid);
- grid->valid = true;
+ } else if (!want_allocation && has_allocation) {
+ // 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_allocated);
+ grid_allocated->valid = false;
+ was_resized = true;
+ } else if (want_allocation && has_allocation && !wp->w_grid_alloc.valid) {
+ grid_invalidate(grid_allocated);
+ grid_allocated->valid = true;
}
- grid->row_offset = wp->w_winrow;
- grid->col_offset = wp->w_wincol;
+ grid->Rows = rows;
+ grid->Columns = cols;
+
+ if (want_allocation) {
+ grid->target = grid_allocated;
+ grid->row_offset = wp->w_border_adj;
+ grid->col_offset = wp->w_border_adj;
+ } else {
+ grid->target = &default_grid;
+ grid->row_offset = wp->w_winrow;
+ grid->col_offset = wp->w_wincol;
+ }
// send grid resize event if:
// - 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) && want_allocation) {
- ui_call_grid_resize(grid->handle, grid->Columns, grid->Rows);
+ ui_call_grid_resize(grid_allocated->handle,
+ grid_allocated->Columns, grid_allocated->Rows);
}
}
@@ -7531,7 +7601,7 @@ void win_new_shellsize(void)
win_T *get_win_by_grid_handle(handle_T handle)
{
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_grid.handle == handle) {
+ if (wp->w_grid_alloc.handle == handle) {
return wp;
}
}
diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c
index d204968c0f..f1eb7879b0 100644
--- a/src/nvim/syntax.c
+++ b/src/nvim/syntax.c
@@ -6046,6 +6046,7 @@ static const char *highlight_init_both[] = {
"default link Whitespace NonText",
"default link MsgSeparator StatusLine",
"default link NormalFloat Pmenu",
+ "default link FloatBorder VertSplit",
"RedrawDebugNormal cterm=reverse gui=reverse",
"RedrawDebugClear ctermbg=Yellow guibg=Yellow",
"RedrawDebugComposed ctermbg=Green guibg=Green",
diff --git a/src/nvim/window.c b/src/nvim/window.c
index eef75b10bd..fd7af108b7 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -605,6 +605,7 @@ win_T *win_new_float(win_T *wp, FloatConfig fconfig, Error *err)
wp->w_vsep_width = 0;
win_config_float(wp, fconfig);
+ win_set_inner_size(wp);
wp->w_pos_changed = true;
redraw_later(wp, VALID);
return wp;
@@ -667,6 +668,8 @@ void win_config_float(win_T *wp, FloatConfig fconfig)
}
bool change_external = fconfig.external != wp->w_float_config.external;
+ bool change_border = fconfig.border != wp->w_float_config.border;
+
wp->w_float_config = fconfig;
if (!ui_has(kUIMultigrid)) {
@@ -676,11 +679,18 @@ void win_config_float(win_T *wp, FloatConfig fconfig)
win_set_inner_size(wp);
must_redraw = MAX(must_redraw, VALID);
+
wp->w_pos_changed = true;
- if (change_external) {
+ if (change_external || change_border) {
wp->w_hl_needs_update = true;
redraw_later(wp, NOT_VALID);
}
+
+ // changing border style while keeping border only requires redrawing border
+ if (fconfig.border) {
+ wp->w_redr_border = true;
+ redraw_later(wp, VALID);
+ }
}
void win_check_anchored_floats(win_T *win)
@@ -713,7 +723,7 @@ int win_fdccol_count(win_T *wp)
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,
+ ui_call_win_pos(wp->w_grid_alloc.handle, wp->handle, wp->w_winrow,
wp->w_wincol, wp->w_width, wp->w_height);
return;
}
@@ -743,8 +753,8 @@ void ui_ext_win_position(win_T *wp)
}
if (ui_has(kUIMultigrid)) {
String anchor = cstr_to_string(float_anchor_str[c.anchor]);
- ui_call_win_float_pos(wp->w_grid.handle, wp->handle, anchor, grid->handle,
- row, col, c.focusable);
+ ui_call_win_float_pos(wp->w_grid_alloc.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.
@@ -759,17 +769,17 @@ void ui_ext_win_position(win_T *wp)
wp->w_wincol = comp_col;
bool valid = (wp->w_redr_type == 0);
bool on_top = (curwin == wp) || !curwin->w_floating;
- ui_comp_put_grid(&wp->w_grid, comp_row, comp_col, wp->w_height,
- wp->w_width, valid, on_top);
- ui_check_cursor_grid(wp->w_grid.handle);
- wp->w_grid.focusable = wp->w_float_config.focusable;
+ ui_comp_put_grid(&wp->w_grid_alloc, comp_row, comp_col,
+ wp->w_height_outer, wp->w_width_outer, valid, on_top);
+ ui_check_cursor_grid(wp->w_grid_alloc.handle);
+ wp->w_grid_alloc.focusable = wp->w_float_config.focusable;
if (!valid) {
- wp->w_grid.valid = false;
+ wp->w_grid_alloc.valid = false;
redraw_later(wp, NOT_VALID);
}
}
} else {
- ui_call_win_external_pos(wp->w_grid.handle, wp->handle);
+ ui_call_win_external_pos(wp->w_grid_alloc.handle, wp->handle);
}
}
@@ -784,260 +794,12 @@ void ui_ext_win_viewport(win_T *wp)
// interact with incomplete final line? Diff filler lines?
botline = wp->w_buffer->b_ml.ml_line_count;
}
- ui_call_win_viewport(wp->w_grid.handle, wp->handle, wp->w_topline-1,
+ ui_call_win_viewport(wp->w_grid_alloc.handle, wp->handle, wp->w_topline-1,
botline, wp->w_cursor.lnum-1, wp->w_cursor.col);
wp->w_viewport_invalid = false;
}
}
-static bool parse_float_anchor(String anchor, FloatAnchor *out)
-{
- if (anchor.size == 0) {
- *out = (FloatAnchor)0;
- }
- char *str = anchor.data;
- if (striequal(str, "NW")) {
- *out = 0; // NW is the default
- } else if (striequal(str, "NE")) {
- *out = kFloatAnchorEast;
- } else if (striequal(str, "SW")) {
- *out = kFloatAnchorSouth;
- } else if (striequal(str, "SE")) {
- *out = kFloatAnchorSouth | kFloatAnchorEast;
- } else {
- return false;
- }
- return true;
-}
-
-static bool parse_float_relative(String relative, FloatRelative *out)
-{
- char *str = relative.data;
- if (striequal(str, "editor")) {
- *out = kFloatRelativeEditor;
- } else if (striequal(str, "win")) {
- *out = kFloatRelativeWindow;
- } else if (striequal(str, "cursor")) {
- *out = kFloatRelativeCursor;
- } else {
- return false;
- }
- return true;
-}
-
-static bool parse_float_bufpos(Array bufpos, lpos_T *out)
-{
- if (bufpos.size != 2
- || bufpos.items[0].type != kObjectTypeInteger
- || bufpos.items[1].type != kObjectTypeInteger) {
- return false;
- }
- out->lnum = bufpos.items[0].data.integer;
- out->col = bufpos.items[1].data.integer;
- return true;
-}
-
-bool parse_float_config(Dictionary config, FloatConfig *fconfig, bool reconf,
- Error *err)
-{
- // TODO(bfredl): use a get/has_key interface instead and get rid of extra
- // flags
- bool has_row = false, has_col = false, has_relative = false;
- bool has_external = false, has_window = false;
- bool has_width = false, has_height = false;
- bool has_bufpos = 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) {
- fconfig->row = val.data.integer;
- } else if (val.type == kObjectTypeFloat) {
- fconfig->row = val.data.floating;
- } else {
- api_set_error(err, kErrorTypeValidation,
- "'row' key must be Integer or Float");
- return false;
- }
- } else if (!strcmp(key, "col")) {
- has_col = true;
- if (val.type == kObjectTypeInteger) {
- fconfig->col = val.data.integer;
- } else if (val.type == kObjectTypeFloat) {
- fconfig->col = val.data.floating;
- } else {
- api_set_error(err, kErrorTypeValidation,
- "'col' key must be Integer or Float");
- return false;
- }
- } else if (strequal(key, "width")) {
- has_width = true;
- if (val.type == kObjectTypeInteger && val.data.integer > 0) {
- fconfig->width = val.data.integer;
- } else {
- api_set_error(err, kErrorTypeValidation,
- "'width' key must be a positive Integer");
- return false;
- }
- } else if (strequal(key, "height")) {
- has_height = true;
- if (val.type == kObjectTypeInteger && val.data.integer > 0) {
- fconfig->height= val.data.integer;
- } else {
- api_set_error(err, kErrorTypeValidation,
- "'height' key must be a positive Integer");
- return false;
- }
- } else if (!strcmp(key, "anchor")) {
- if (val.type != kObjectTypeString) {
- api_set_error(err, kErrorTypeValidation,
- "'anchor' key must be String");
- return false;
- }
- if (!parse_float_anchor(val.data.string, &fconfig->anchor)) {
- api_set_error(err, kErrorTypeValidation,
- "Invalid value of 'anchor' key");
- return false;
- }
- } else if (!strcmp(key, "relative")) {
- if (val.type != kObjectTypeString) {
- api_set_error(err, kErrorTypeValidation,
- "'relative' key must be String");
- return false;
- }
- // ignore empty string, to match nvim_win_get_config
- if (val.data.string.size > 0) {
- has_relative = true;
- if (!parse_float_relative(val.data.string, &fconfig->relative)) {
- api_set_error(err, kErrorTypeValidation,
- "Invalid value of 'relative' key");
- return false;
- }
- }
- } else if (!strcmp(key, "win")) {
- has_window = true;
- if (val.type != kObjectTypeInteger
- && val.type != kObjectTypeWindow) {
- api_set_error(err, kErrorTypeValidation,
- "'win' key must be Integer or Window");
- return false;
- }
- fconfig->window = val.data.integer;
- } else if (!strcmp(key, "bufpos")) {
- if (val.type != kObjectTypeArray) {
- api_set_error(err, kErrorTypeValidation,
- "'bufpos' key must be Array");
- return false;
- }
- if (!parse_float_bufpos(val.data.array, &fconfig->bufpos)) {
- api_set_error(err, kErrorTypeValidation,
- "Invalid value of 'bufpos' key");
- return false;
- }
- has_bufpos = true;
- } else if (!strcmp(key, "external")) {
- if (val.type == kObjectTypeInteger) {
- fconfig->external = val.data.integer;
- } else if (val.type == kObjectTypeBoolean) {
- fconfig->external = val.data.boolean;
- } else {
- api_set_error(err, kErrorTypeValidation,
- "'external' key must be Boolean");
- return false;
- }
- has_external = fconfig->external;
- } else if (!strcmp(key, "focusable")) {
- if (val.type == kObjectTypeInteger) {
- fconfig->focusable = val.data.integer;
- } else if (val.type == kObjectTypeBoolean) {
- fconfig->focusable = val.data.boolean;
- } else {
- api_set_error(err, kErrorTypeValidation,
- "'focusable' key must be Boolean");
- return false;
- }
- } else if (!strcmp(key, "style")) {
- if (val.type != kObjectTypeString) {
- api_set_error(err, kErrorTypeValidation,
- "'style' key must be String");
- return false;
- }
- if (val.data.string.data[0] == NUL) {
- fconfig->style = kWinStyleUnused;
- } else if (striequal(val.data.string.data, "minimal")) {
- fconfig->style = kWinStyleMinimal;
- } else {
- api_set_error(err, kErrorTypeValidation,
- "Invalid value of 'style' key");
- }
- } else {
- api_set_error(err, kErrorTypeValidation,
- "Invalid key '%s'", key);
- return false;
- }
- }
-
- if (has_window && !(has_relative
- && fconfig->relative == kFloatRelativeWindow)) {
- api_set_error(err, kErrorTypeValidation,
- "'win' key is only valid with relative='win'");
- return false;
- }
-
- if ((has_relative && fconfig->relative == kFloatRelativeWindow)
- && (!has_window || fconfig->window == 0)) {
- fconfig->window = curwin->handle;
- }
-
- if (has_window && !has_bufpos) {
- fconfig->bufpos.lnum = -1;
- }
-
- if (has_bufpos) {
- if (!has_row) {
- fconfig->row = (fconfig->anchor & kFloatAnchorSouth) ? 0 : 1;
- has_row = true;
- }
- if (!has_col) {
- fconfig->col = 0;
- has_col = true;
- }
- }
-
- if (has_relative && has_external) {
- api_set_error(err, kErrorTypeValidation,
- "Only one of 'relative' and 'external' must be used");
- return false;
- } else if (!reconf && !has_relative && !has_external) {
- api_set_error(err, kErrorTypeValidation,
- "One of 'relative' and 'external' must be used");
- return false;
- } else if (has_relative) {
- fconfig->external = false;
- }
-
- if (!reconf && !(has_height && has_width)) {
- api_set_error(err, kErrorTypeValidation,
- "Must specify 'width' and 'height'");
- return false;
- }
-
- if (fconfig->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,
- "'relative' requires 'row'/'col' or 'bufpos'");
- return false;
- }
- return true;
-}
-
/*
* split the current window, implements CTRL-W s and :split
*
@@ -1953,12 +1715,12 @@ static void win_totop(int size, int flags)
}
if (curwin->w_floating) {
- ui_comp_remove_grid(&curwin->w_grid);
+ ui_comp_remove_grid(&curwin->w_grid_alloc);
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);
+ ui_call_win_hide(curwin->w_grid_alloc.handle);
win_free_grid(curwin, false);
}
} else {
@@ -2581,11 +2343,11 @@ int win_close(win_T *win, bool free_buf)
bool was_floating = win->w_floating;
if (ui_has(kUIMultigrid)) {
- ui_call_win_close(win->w_grid.handle);
+ ui_call_win_close(win->w_grid_alloc.handle);
}
if (win->w_floating) {
- ui_comp_remove_grid(&win->w_grid);
+ ui_comp_remove_grid(&win->w_grid_alloc);
if (win->w_float_config.external) {
for (tabpage_T *tp = first_tabpage; tp != NULL; tp = tp->tp_next) {
if (tp == curtab) {
@@ -3763,9 +3525,11 @@ void win_init_size(void)
{
firstwin->w_height = ROWS_AVAIL;
firstwin->w_height_inner = firstwin->w_height;
+ firstwin->w_height_outer = firstwin->w_height;
topframe->fr_height = ROWS_AVAIL;
firstwin->w_width = Columns;
firstwin->w_width_inner = firstwin->w_width;
+ firstwin->w_width_outer = firstwin->w_width;
topframe->fr_width = Columns;
}
@@ -4131,7 +3895,7 @@ static void tabpage_check_windows(tabpage_T *old_curtab)
win_remove(wp, old_curtab);
win_append(lastwin_nofloating(), wp);
} else {
- ui_comp_remove_grid(&wp->w_grid);
+ ui_comp_remove_grid(&wp->w_grid_alloc);
}
}
wp->w_pos_changed = true;
@@ -4726,7 +4490,7 @@ static win_T *win_alloc(win_T *after, int hidden)
new_wp->handle = ++last_win_id;
handle_register_window(new_wp);
- grid_assign_handle(&new_wp->w_grid);
+ grid_assign_handle(&new_wp->w_grid_alloc);
// Init w: variables.
new_wp->w_vars = tv_dict_alloc();
@@ -4850,15 +4614,14 @@ win_free (
void win_free_grid(win_T *wp, bool reinit)
{
- if (wp->w_grid.handle != 0 && ui_has(kUIMultigrid)) {
- ui_call_grid_destroy(wp->w_grid.handle);
- wp->w_grid.handle = 0;
+ if (wp->w_grid_alloc.handle != 0 && ui_has(kUIMultigrid)) {
+ ui_call_grid_destroy(wp->w_grid_alloc.handle);
}
- grid_free(&wp->w_grid);
+ grid_free(&wp->w_grid_alloc);
if (reinit) {
// if a float is turned into a split and back into a float, the grid
// data structure will be reused
- memset(&wp->w_grid, 0, sizeof(wp->w_grid));
+ memset(&wp->w_grid_alloc, 0, sizeof(wp->w_grid_alloc));
}
}
@@ -5963,6 +5726,10 @@ void win_set_inner_size(win_T *wp)
if (wp->w_buffer->terminal) {
terminal_check_size(wp->w_buffer->terminal);
}
+
+ wp->w_border_adj = wp->w_floating && wp->w_float_config.border ? 1 : 0;
+ wp->w_height_outer = wp->w_height_inner + 2 * wp->w_border_adj;
+ wp->w_width_outer = wp->w_width_inner + 2 * wp->w_border_adj;
}
/// Set the width of a window.
@@ -7099,11 +6866,11 @@ void get_framelayout(const frame_T *fr, list_T *l, bool outer)
void win_ui_flush(void)
{
FOR_ALL_TAB_WINDOWS(tp, wp) {
- if (wp->w_pos_changed && wp->w_grid.chars != NULL) {
+ if (wp->w_pos_changed && wp->w_grid_alloc.chars != NULL) {
if (tp == curtab) {
ui_ext_win_position(wp);
} else {
- ui_call_win_hide(wp->w_grid.handle);
+ ui_call_win_hide(wp->w_grid_alloc.handle);
}
wp->w_pos_changed = false;
}