mirror of
https://github.com/neovim/neovim.git
synced 2025-09-11 13:58:18 +00:00
screen: use dedicated message grid
add proper msg_set_pos event, delet win_scroll_over_* make compositor click through unfocusable grids add MsgArea attribute for the message/cmdline area, and add docs and tests
This commit is contained in:
@@ -19,6 +19,7 @@
|
||||
#include "nvim/ui.h"
|
||||
#include "nvim/highlight.h"
|
||||
#include "nvim/memory.h"
|
||||
#include "nvim/message.h"
|
||||
#include "nvim/popupmnu.h"
|
||||
#include "nvim/ui_compositor.h"
|
||||
#include "nvim/ugrid.h"
|
||||
@@ -46,8 +47,11 @@ static int chk_width = 0, chk_height = 0;
|
||||
static ScreenGrid *curgrid;
|
||||
|
||||
static bool valid_screen = true;
|
||||
static bool msg_scroll_mode = false;
|
||||
static int msg_first_invalid = 0;
|
||||
static int msg_current_row = INT_MAX;
|
||||
static bool msg_was_scrolled = false;
|
||||
|
||||
static int msg_sep_row = -1;
|
||||
static schar_T msg_sep_char = { ' ', NUL };
|
||||
|
||||
static int dbghl_normal, dbghl_clear, dbghl_composed, dbghl_recompose;
|
||||
|
||||
@@ -63,8 +67,7 @@ void ui_comp_init(void)
|
||||
compositor->grid_scroll = ui_comp_grid_scroll;
|
||||
compositor->grid_cursor_goto = ui_comp_grid_cursor_goto;
|
||||
compositor->raw_line = ui_comp_raw_line;
|
||||
compositor->win_scroll_over_start = ui_comp_win_scroll_over_start;
|
||||
compositor->win_scroll_over_reset = ui_comp_win_scroll_over_reset;
|
||||
compositor->msg_set_pos = ui_comp_msg_set_pos;
|
||||
|
||||
// Be unopinionated: will be attached together with a "real" ui anyway
|
||||
compositor->width = INT_MAX;
|
||||
@@ -158,8 +161,19 @@ bool ui_comp_put_grid(ScreenGrid *grid, int row, int col, int height, int width,
|
||||
}
|
||||
#endif
|
||||
|
||||
// TODO(bfredl): this is pretty ad-hoc, add a proper z-order/priority
|
||||
// scheme. For now:
|
||||
// - msg_grid is always on top.
|
||||
// - pum_grid is on top of all windows but not msg_grid. Except for when
|
||||
// wildoptions=pum, and completing the cmdline with scrolled messages,
|
||||
// then the pum has to be drawn over the scrolled messages.
|
||||
size_t insert_at = kv_size(layers);
|
||||
if (kv_A(layers, insert_at-1) == &pum_grid) {
|
||||
bool cmd_completion = (grid == &pum_grid && (State & CMDLINE)
|
||||
&& (wop_flags & WOP_PUM));
|
||||
if (kv_A(layers, insert_at-1) == &msg_grid && !cmd_completion) {
|
||||
insert_at--;
|
||||
}
|
||||
if (kv_A(layers, insert_at-1) == &pum_grid && (grid != &msg_grid)) {
|
||||
insert_at--;
|
||||
}
|
||||
if (insert_at > 1 && !on_top) {
|
||||
@@ -280,10 +294,10 @@ static void ui_comp_grid_cursor_goto(UI *ui, Integer grid_handle,
|
||||
|
||||
ScreenGrid *ui_comp_mouse_focus(int row, int col)
|
||||
{
|
||||
// TODO(bfredl): click "through" unfocusable grids?
|
||||
for (ssize_t i = (ssize_t)kv_size(layers)-1; i > 0; i--) {
|
||||
ScreenGrid *grid = kv_A(layers, i);
|
||||
if (row >= grid->comp_row && row < grid->comp_row+grid->Rows
|
||||
if (grid->focusable
|
||||
&& row >= grid->comp_row && row < grid->comp_row+grid->Rows
|
||||
&& col >= grid->comp_col && col < grid->comp_col+grid->Columns) {
|
||||
return grid;
|
||||
}
|
||||
@@ -337,10 +351,28 @@ static void compose_line(Integer row, Integer startcol, Integer endcol,
|
||||
assert(until > col);
|
||||
assert(until <= default_grid.Columns);
|
||||
size_t n = (size_t)(until-col);
|
||||
size_t off = grid->line_offset[row-grid->comp_row]
|
||||
+ (size_t)(col-grid->comp_col);
|
||||
memcpy(linebuf+(col-startcol), grid->chars+off, n * sizeof(*linebuf));
|
||||
memcpy(attrbuf+(col-startcol), grid->attrs+off, n * sizeof(*attrbuf));
|
||||
|
||||
if (row == msg_sep_row && grid->comp_index <= msg_grid.comp_index) {
|
||||
grid = &msg_grid;
|
||||
sattr_T msg_sep_attr = (sattr_T)HL_ATTR(HLF_MSGSEP);
|
||||
for (int i = col; i < until; i++) {
|
||||
memcpy(linebuf[i-startcol], msg_sep_char, sizeof(*linebuf));
|
||||
attrbuf[i-startcol] = msg_sep_attr;
|
||||
}
|
||||
} else {
|
||||
size_t off = grid->line_offset[row-grid->comp_row]
|
||||
+ (size_t)(col-grid->comp_col);
|
||||
memcpy(linebuf+(col-startcol), grid->chars+off, n * sizeof(*linebuf));
|
||||
memcpy(attrbuf+(col-startcol), grid->attrs+off, n * sizeof(*attrbuf));
|
||||
if (grid->comp_col+grid->Columns > until
|
||||
&& grid->chars[off+n][0] == NUL) {
|
||||
linebuf[until-1-startcol][0] = ' ';
|
||||
linebuf[until-1-startcol][1] = '\0';
|
||||
if (col == startcol && n == 1) {
|
||||
skipstart = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 'pumblend' and 'winblend'
|
||||
if (grid->blending) {
|
||||
@@ -375,14 +407,6 @@ static void compose_line(Integer row, Integer startcol, Integer endcol,
|
||||
} else if (n > 1 && linebuf[col-startcol+1][0] == NUL) {
|
||||
skipstart = 0;
|
||||
}
|
||||
if (grid->comp_col+grid->Columns > until
|
||||
&& grid->chars[off+n][0] == NUL) {
|
||||
linebuf[until-1-startcol][0] = ' ';
|
||||
linebuf[until-1-startcol][1] = '\0';
|
||||
if (col == startcol && n == 1) {
|
||||
skipstart = 0;
|
||||
}
|
||||
}
|
||||
|
||||
col = until;
|
||||
}
|
||||
@@ -500,9 +524,12 @@ static void ui_comp_raw_line(UI *ui, Integer grid, Integer row,
|
||||
endcol = MIN(endcol, clearcol);
|
||||
}
|
||||
|
||||
if (flags & kLineFlagInvalid
|
||||
|| kv_size(layers) > curgrid->comp_index+1
|
||||
|| curgrid->blending) {
|
||||
bool above_msg = (kv_A(layers, kv_size(layers)-1) == &msg_grid
|
||||
&& row < msg_current_row-(msg_was_scrolled?1:0));
|
||||
bool covered = kv_size(layers)-(above_msg?1:0) > curgrid->comp_index+1;
|
||||
// TODO(bfredl): eventually should just fix compose_line to respect clearing
|
||||
// and optimize it for uncovered lines.
|
||||
if (flags & kLineFlagInvalid || covered || curgrid->blending) {
|
||||
compose_debug(row, row+1, startcol, clearcol, dbghl_composed, true);
|
||||
compose_line(row, startcol, clearcol, flags);
|
||||
} else {
|
||||
@@ -519,27 +546,44 @@ static void ui_comp_raw_line(UI *ui, Integer grid, Integer row,
|
||||
void ui_comp_set_screen_valid(bool valid)
|
||||
{
|
||||
valid_screen = valid;
|
||||
if (!valid) {
|
||||
msg_sep_row = -1;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(bfredl): These events are somewhat of a hack. multiline messages
|
||||
// should later on be a separate grid, then this would just be ordinary
|
||||
// ui_comp_put_grid and ui_comp_remove_grid calls.
|
||||
static void ui_comp_win_scroll_over_start(UI *ui)
|
||||
static void ui_comp_msg_set_pos(UI *ui, Integer grid, Integer row,
|
||||
Boolean scrolled, String sep_char)
|
||||
{
|
||||
msg_scroll_mode = true;
|
||||
msg_first_invalid = ui->height;
|
||||
}
|
||||
msg_grid.comp_row = (int)row;
|
||||
if (scrolled && row > 0) {
|
||||
msg_sep_row = (int)row-1;
|
||||
if (sep_char.data) {
|
||||
STRLCPY(msg_sep_char, sep_char.data, sizeof(msg_sep_char));
|
||||
}
|
||||
} else {
|
||||
msg_sep_row = -1;
|
||||
}
|
||||
|
||||
static void ui_comp_win_scroll_over_reset(UI *ui)
|
||||
{
|
||||
msg_scroll_mode = false;
|
||||
for (size_t i = 1; i < kv_size(layers); i++) {
|
||||
ScreenGrid *grid = kv_A(layers, i);
|
||||
if (grid->comp_row+grid->Rows > msg_first_invalid) {
|
||||
compose_area(msg_first_invalid, grid->comp_row+grid->Rows,
|
||||
grid->comp_col, grid->comp_col+grid->Columns);
|
||||
if (row > msg_current_row && ui_comp_should_draw()) {
|
||||
compose_area(MAX(msg_current_row-1, 0), row, 0, default_grid.Columns);
|
||||
} else if (row < msg_current_row && ui_comp_should_draw()
|
||||
&& msg_current_row < Rows) {
|
||||
int delta = msg_current_row - (int)row;
|
||||
if (msg_grid.blending) {
|
||||
int first_row = MAX((int)row-(scrolled?1:0), 0);
|
||||
compose_area(first_row, Rows-delta, 0, Columns);
|
||||
} else {
|
||||
// scroll separator togheter with message text
|
||||
int first_row = MAX((int)row-(msg_was_scrolled?1:0), 0);
|
||||
ui_composed_call_grid_scroll(1, first_row, Rows, 0, Columns, delta, 0);
|
||||
if (scrolled && !msg_was_scrolled && row > 0) {
|
||||
compose_area(row-1, row, 0, Columns);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
msg_current_row = (int)row;
|
||||
msg_was_scrolled = scrolled;
|
||||
}
|
||||
|
||||
static void ui_comp_grid_scroll(UI *ui, Integer grid, Integer top,
|
||||
@@ -554,7 +598,8 @@ static void ui_comp_grid_scroll(UI *ui, Integer grid, Integer top,
|
||||
left += curgrid->comp_col;
|
||||
right += curgrid->comp_col;
|
||||
bool covered = kv_size(layers) > curgrid->comp_index+1 || curgrid->blending;
|
||||
if (!msg_scroll_mode && covered) {
|
||||
|
||||
if (covered) {
|
||||
// TODO(bfredl):
|
||||
// 1. check if rectangles actually overlap
|
||||
// 2. calulate subareas that can scroll.
|
||||
@@ -565,7 +610,6 @@ static void ui_comp_grid_scroll(UI *ui, Integer grid, Integer top,
|
||||
}
|
||||
compose_area(top, bot, left, right);
|
||||
} else {
|
||||
msg_first_invalid = MIN(msg_first_invalid, (int)top);
|
||||
ui_composed_call_grid_scroll(1, top, bot, left, right, rows, cols);
|
||||
if (rdb_flags & RDB_COMPOSITOR) {
|
||||
debug_delay(2);
|
||||
|
Reference in New Issue
Block a user