diff options
Diffstat (limited to 'src/nvim/ui_compositor.c')
-rw-r--r-- | src/nvim/ui_compositor.c | 122 |
1 files changed, 83 insertions, 39 deletions
diff --git a/src/nvim/ui_compositor.c b/src/nvim/ui_compositor.c index 9517b362af..163eadbc95 100644 --- a/src/nvim/ui_compositor.c +++ b/src/nvim/ui_compositor.c @@ -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); |