mirror of
https://github.com/neovim/neovim.git
synced 2024-12-20 03:05:11 -07:00
Merge pull request #9193 from bfredl/scrollstuff
UI/TUI: improvements and cleanups for scrolling and clearing
This commit is contained in:
commit
c936ae0f36
@ -325,14 +325,14 @@ numerical highlight `id`:s to the actual attributes.
|
|||||||
+-------------------------+ src_top |
|
+-------------------------+ src_top |
|
||||||
| src (moved up) and dst | |
|
| src (moved up) and dst | |
|
||||||
|-------------------------| dst_bot |
|
|-------------------------| dst_bot |
|
||||||
| src (cleared) | |
|
| src (invalid) | |
|
||||||
+=========================+ src_bot
|
+=========================+ src_bot
|
||||||
<
|
<
|
||||||
If `rows` is less than zero, move a rectangle in the SR down, this can
|
If `rows` is less than zero, move a rectangle in the SR down, this can
|
||||||
happen while scrolling up.
|
happen while scrolling up.
|
||||||
>
|
>
|
||||||
+=========================+ src_top
|
+=========================+ src_top
|
||||||
| src (cleared) | |
|
| src (invalid) | |
|
||||||
|------------------------ | dst_top |
|
|------------------------ | dst_top |
|
||||||
| src (moved down) and dst| |
|
| src (moved down) and dst| |
|
||||||
+-------------------------+ src_bot |
|
+-------------------------+ src_bot |
|
||||||
@ -348,6 +348,10 @@ numerical highlight `id`:s to the actual attributes.
|
|||||||
end-exclusive, which is consistent with API conventions, but different
|
end-exclusive, which is consistent with API conventions, but different
|
||||||
from `set_scroll_region` which was end-inclusive.
|
from `set_scroll_region` which was end-inclusive.
|
||||||
|
|
||||||
|
The scrolled-in area will be filled using |ui-event-grid_line| directly
|
||||||
|
after the scroll event. The UI thus doesn't need to clear this area as
|
||||||
|
part of handling the scroll event.
|
||||||
|
|
||||||
==============================================================================
|
==============================================================================
|
||||||
Legacy Grid Events (cell based) *ui-grid-old*
|
Legacy Grid Events (cell based) *ui-grid-old*
|
||||||
|
|
||||||
|
@ -37,17 +37,6 @@ typedef struct attr_entry {
|
|||||||
.cterm_bg_color = 0, \
|
.cterm_bg_color = 0, \
|
||||||
}
|
}
|
||||||
|
|
||||||
// sentinel value that compares unequal to any valid highlight
|
|
||||||
#define HLATTRS_INVALID (HlAttrs) { \
|
|
||||||
.rgb_ae_attr = -1, \
|
|
||||||
.cterm_ae_attr = -1, \
|
|
||||||
.rgb_fg_color = -1, \
|
|
||||||
.rgb_bg_color = -1, \
|
|
||||||
.rgb_sp_color = -1, \
|
|
||||||
.cterm_fg_color = 0, \
|
|
||||||
.cterm_bg_color = 0, \
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Values for index in highlight_attr[].
|
/// Values for index in highlight_attr[].
|
||||||
/// When making changes, also update hlf_names below!
|
/// When making changes, also update hlf_names below!
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
@ -1896,6 +1896,9 @@ static void msg_scroll_up(void)
|
|||||||
} else {
|
} else {
|
||||||
screen_del_lines(0, 1, (int)Rows, 0, Columns);
|
screen_del_lines(0, 1, (int)Rows, 0, Columns);
|
||||||
}
|
}
|
||||||
|
// TODO(bfredl): when msgsep display is properly batched, this fill should be
|
||||||
|
// eliminated.
|
||||||
|
screen_fill(Rows-1, Rows, 0, (int)Columns, ' ', ' ', 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -2311,6 +2314,7 @@ static int do_more_prompt(int typed_char)
|
|||||||
|
|
||||||
if (toscroll == -1
|
if (toscroll == -1
|
||||||
&& screen_ins_lines(0, 1, (int)Rows, 0, (int)Columns) == OK) {
|
&& screen_ins_lines(0, 1, (int)Rows, 0, (int)Columns) == OK) {
|
||||||
|
screen_fill(0, 1, 0, (int)Columns, ' ', ' ', 0);
|
||||||
// display line at top
|
// display line at top
|
||||||
(void)disp_sb_line(0, mp);
|
(void)disp_sb_line(0, mp);
|
||||||
} else {
|
} else {
|
||||||
|
@ -918,9 +918,9 @@ void curs_columns(
|
|||||||
|
|
||||||
extra = ((int)prev_skipcol - (int)curwin->w_skipcol) / width;
|
extra = ((int)prev_skipcol - (int)curwin->w_skipcol) / width;
|
||||||
if (extra > 0) {
|
if (extra > 0) {
|
||||||
win_ins_lines(curwin, 0, extra, false);
|
win_ins_lines(curwin, 0, extra);
|
||||||
} else if (extra < 0) {
|
} else if (extra < 0) {
|
||||||
win_del_lines(curwin, 0, -extra, false);
|
win_del_lines(curwin, 0, -extra);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
curwin->w_skipcol = 0;
|
curwin->w_skipcol = 0;
|
||||||
|
@ -888,10 +888,7 @@ static void win_update(win_T *wp)
|
|||||||
// Try to insert the correct number of lines.
|
// Try to insert the correct number of lines.
|
||||||
// If not the last window, delete the lines at the bottom.
|
// If not the last window, delete the lines at the bottom.
|
||||||
// win_ins_lines may fail when the terminal can't do it.
|
// win_ins_lines may fail when the terminal can't do it.
|
||||||
if (i > 0) {
|
if (win_ins_lines(wp, 0, i) == OK) {
|
||||||
check_for_delay(false);
|
|
||||||
}
|
|
||||||
if (win_ins_lines(wp, 0, i, false) == OK) {
|
|
||||||
if (wp->w_lines_valid != 0) {
|
if (wp->w_lines_valid != 0) {
|
||||||
/* Need to update rows that are new, stop at the
|
/* Need to update rows that are new, stop at the
|
||||||
* first one that scrolled down. */
|
* first one that scrolled down. */
|
||||||
@ -949,8 +946,7 @@ static void win_update(win_T *wp)
|
|||||||
/* ... but don't delete new filler lines. */
|
/* ... but don't delete new filler lines. */
|
||||||
row -= wp->w_topfill;
|
row -= wp->w_topfill;
|
||||||
if (row > 0) {
|
if (row > 0) {
|
||||||
check_for_delay(false);
|
if (win_del_lines(wp, 0, row) == OK) {
|
||||||
if (win_del_lines(wp, 0, row, false) == OK) {
|
|
||||||
bot_start = wp->w_height - row;
|
bot_start = wp->w_height - row;
|
||||||
} else {
|
} else {
|
||||||
mid_start = 0; // redraw all lines
|
mid_start = 0; // redraw all lines
|
||||||
@ -1305,8 +1301,7 @@ static void win_update(win_T *wp)
|
|||||||
if (row - xtra_rows >= wp->w_height - 2) {
|
if (row - xtra_rows >= wp->w_height - 2) {
|
||||||
mod_bot = MAXLNUM;
|
mod_bot = MAXLNUM;
|
||||||
} else {
|
} else {
|
||||||
check_for_delay(false);
|
if (win_del_lines(wp, row, -xtra_rows) == FAIL) {
|
||||||
if (win_del_lines(wp, row, -xtra_rows, false) == FAIL) {
|
|
||||||
mod_bot = MAXLNUM;
|
mod_bot = MAXLNUM;
|
||||||
} else {
|
} else {
|
||||||
bot_start = wp->w_height + xtra_rows;
|
bot_start = wp->w_height + xtra_rows;
|
||||||
@ -1319,8 +1314,7 @@ static void win_update(win_T *wp)
|
|||||||
if (row + xtra_rows >= wp->w_height - 2) {
|
if (row + xtra_rows >= wp->w_height - 2) {
|
||||||
mod_bot = MAXLNUM;
|
mod_bot = MAXLNUM;
|
||||||
} else {
|
} else {
|
||||||
check_for_delay(false);
|
if (win_ins_lines(wp, row + old_rows, xtra_rows) == FAIL) {
|
||||||
if (win_ins_lines(wp, row + old_rows, xtra_rows, false) == FAIL) {
|
|
||||||
mod_bot = MAXLNUM;
|
mod_bot = MAXLNUM;
|
||||||
} else if (top_end > row + old_rows) {
|
} else if (top_end > row + old_rows) {
|
||||||
// Scrolled the part at the top that requires
|
// Scrolled the part at the top that requires
|
||||||
@ -1513,8 +1507,7 @@ static void win_update(win_T *wp)
|
|||||||
wp->w_botline = lnum;
|
wp->w_botline = lnum;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
draw_vsep_win(wp, row);
|
if (eof) { // we hit the end of the file
|
||||||
if (eof) { /* we hit the end of the file */
|
|
||||||
wp->w_botline = buf->b_ml.ml_line_count + 1;
|
wp->w_botline = buf->b_ml.ml_line_count + 1;
|
||||||
j = diff_check_fill(wp, wp->w_botline);
|
j = diff_check_fill(wp, wp->w_botline);
|
||||||
if (j > 0 && !wp->w_botfill) {
|
if (j > 0 && !wp->w_botfill) {
|
||||||
@ -1538,6 +1531,10 @@ static void win_update(win_T *wp)
|
|||||||
win_draw_end(wp, fill_eob, ' ', row, wp->w_height, HLF_EOB);
|
win_draw_end(wp, fill_eob, ' ', row, wp->w_height, HLF_EOB);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (wp->w_redr_type >= REDRAW_TOP) {
|
||||||
|
draw_vsep_win(wp, 0);
|
||||||
|
}
|
||||||
|
|
||||||
/* Reset the type of redrawing required, the window has been updated. */
|
/* Reset the type of redrawing required, the window has been updated. */
|
||||||
wp->w_redr_type = 0;
|
wp->w_redr_type = 0;
|
||||||
wp->w_old_topfill = wp->w_topfill;
|
wp->w_old_topfill = wp->w_topfill;
|
||||||
@ -4262,7 +4259,6 @@ win_line (
|
|||||||
&& filler_todo <= 0
|
&& filler_todo <= 0
|
||||||
) {
|
) {
|
||||||
win_draw_end(wp, '@', ' ', row, wp->w_height, HLF_AT);
|
win_draw_end(wp, '@', ' ', row, wp->w_height, HLF_AT);
|
||||||
draw_vsep_win(wp, row);
|
|
||||||
row = endrow;
|
row = endrow;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4348,7 +4344,6 @@ static void screen_line(int row, int coloff, int endcol, int clear_width,
|
|||||||
unsigned max_off_from;
|
unsigned max_off_from;
|
||||||
unsigned max_off_to;
|
unsigned max_off_to;
|
||||||
int col = 0;
|
int col = 0;
|
||||||
int hl;
|
|
||||||
bool redraw_this; // Does character need redraw?
|
bool redraw_this; // Does character need redraw?
|
||||||
bool redraw_next; // redraw_this for next character
|
bool redraw_next; // redraw_this for next character
|
||||||
bool clear_next = false;
|
bool clear_next = false;
|
||||||
@ -4474,24 +4469,10 @@ static void screen_line(int row, int coloff, int endcol, int clear_width,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (clear_width > 0) {
|
if (clear_width > 0 || wp->w_width != Columns) {
|
||||||
// For a window that's left of another, draw the separator char.
|
// If we cleared after the end of the line, it did not wrap.
|
||||||
if (col + coloff < Columns && wp->w_vsep_width > 0) {
|
// For vsplit, line wrapping is not possible.
|
||||||
int c = fillchar_vsep(wp, &hl);
|
LineWraps[row] = false;
|
||||||
schar_T sc;
|
|
||||||
schar_from_char(sc, c);
|
|
||||||
|
|
||||||
if (schar_cmp(ScreenLines[off_to], sc)
|
|
||||||
|| ScreenAttrs[off_to] != hl) {
|
|
||||||
schar_copy(ScreenLines[off_to], sc);
|
|
||||||
ScreenAttrs[off_to] = hl;
|
|
||||||
if (start_dirty == -1) {
|
|
||||||
start_dirty = col;
|
|
||||||
}
|
|
||||||
end_dirty = col+1;
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
LineWraps[row] = FALSE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (clear_end < end_dirty) {
|
if (clear_end < end_dirty) {
|
||||||
@ -6071,10 +6052,10 @@ static void screenclear2(void)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* blank out ScreenLines */
|
// blank out ScreenLines
|
||||||
for (i = 0; i < Rows; ++i) {
|
for (i = 0; i < Rows; i++) {
|
||||||
lineclear(LineOffset[i], (int)Columns);
|
lineclear(LineOffset[i], (int)Columns, true);
|
||||||
LineWraps[i] = FALSE;
|
LineWraps[i] = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ui_call_grid_clear(1); // clear the display
|
ui_call_grid_clear(1); // clear the display
|
||||||
@ -6098,12 +6079,13 @@ static void screenclear2(void)
|
|||||||
/*
|
/*
|
||||||
* Clear one line in ScreenLines.
|
* Clear one line in ScreenLines.
|
||||||
*/
|
*/
|
||||||
static void lineclear(unsigned off, int width)
|
static void lineclear(unsigned off, int width, bool valid)
|
||||||
{
|
{
|
||||||
for (int col = 0; col < width; col++) {
|
for (int col = 0; col < width; col++) {
|
||||||
schar_from_ascii(ScreenLines[off + col], ' ');
|
schar_from_ascii(ScreenLines[off + col], ' ');
|
||||||
}
|
}
|
||||||
(void)memset(ScreenAttrs + off, 0, (size_t)width * sizeof(sattr_T));
|
int fill = valid ? 0 : -1;
|
||||||
|
(void)memset(ScreenAttrs + off, fill, (size_t)width * sizeof(sattr_T));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Copy part of a Screenline for vertically split window.
|
/// Copy part of a Screenline for vertically split window.
|
||||||
@ -6139,53 +6121,36 @@ void setcursor(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Insert 'line_count' lines at 'row' in window 'wp'.
|
/// Insert 'line_count' lines at 'row' in window 'wp'.
|
||||||
/// If 'invalid' is TRUE the wp->w_lines[].wl_lnum is invalidated.
|
|
||||||
/// If 'mayclear' is TRUE the screen will be cleared if it is faster than
|
|
||||||
/// scrolling.
|
|
||||||
/// Returns FAIL if the lines are not inserted, OK for success.
|
/// Returns FAIL if the lines are not inserted, OK for success.
|
||||||
int win_ins_lines(win_T *wp, int row, int line_count, int invalid)
|
int win_ins_lines(win_T *wp, int row, int line_count)
|
||||||
{
|
{
|
||||||
if (wp->w_height < 5) {
|
return win_do_lines(wp, row, line_count, false);
|
||||||
return FAIL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return win_do_lines(wp, row, line_count, invalid, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Delete "line_count" window lines at "row" in window "wp".
|
/// Delete "line_count" window lines at "row" in window "wp".
|
||||||
/// If "invalid" is TRUE curwin->w_lines[] is invalidated.
|
|
||||||
/// If "mayclear" is TRUE the screen will be cleared if it is faster than
|
|
||||||
/// scrolling
|
|
||||||
/// Return OK for success, FAIL if the lines are not deleted.
|
/// Return OK for success, FAIL if the lines are not deleted.
|
||||||
int win_del_lines(win_T *wp, int row, int line_count, int invalid)
|
int win_del_lines(win_T *wp, int row, int line_count)
|
||||||
{
|
{
|
||||||
return win_do_lines(wp, row, line_count, invalid, true);
|
return win_do_lines(wp, row, line_count, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Common code for win_ins_lines() and win_del_lines().
|
// Common code for win_ins_lines() and win_del_lines().
|
||||||
// Returns OK or FAIL when the work has been done.
|
// Returns OK or FAIL when the work has been done.
|
||||||
static int win_do_lines(win_T *wp, int row, int line_count,
|
static int win_do_lines(win_T *wp, int row, int line_count, int del)
|
||||||
int invalid, int del)
|
|
||||||
{
|
{
|
||||||
if (invalid) {
|
|
||||||
wp->w_lines_valid = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!redrawing() || line_count <= 0) {
|
if (!redrawing() || line_count <= 0) {
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete all remaining lines
|
// No lines are being moved, just draw over the entire area
|
||||||
if (row + line_count >= wp->w_height) {
|
if (row + line_count >= wp->w_height) {
|
||||||
screen_fill(wp->w_winrow + row, wp->w_winrow + wp->w_height,
|
|
||||||
wp->w_wincol, W_ENDCOL(wp),
|
|
||||||
' ', ' ', 0);
|
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
// when scrolling, the message on the command line should be cleared,
|
// when scrolling, the message on the command line should be cleared,
|
||||||
// otherwise it will stay there forever.
|
// otherwise it will stay there forever.
|
||||||
clear_cmdline = TRUE;
|
check_for_delay(false);
|
||||||
|
clear_cmdline = true;
|
||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
if (del) {
|
if (del) {
|
||||||
@ -6237,7 +6202,7 @@ int screen_ins_lines(int row, int line_count, int end, int col, int width)
|
|||||||
linecopy(j + line_count, j, col, width);
|
linecopy(j + line_count, j, col, width);
|
||||||
}
|
}
|
||||||
j += line_count;
|
j += line_count;
|
||||||
lineclear(LineOffset[j] + col, width);
|
lineclear(LineOffset[j] + col, width, false);
|
||||||
LineWraps[j] = false;
|
LineWraps[j] = false;
|
||||||
} else {
|
} else {
|
||||||
j = end - 1 - i;
|
j = end - 1 - i;
|
||||||
@ -6248,7 +6213,7 @@ int screen_ins_lines(int row, int line_count, int end, int col, int width)
|
|||||||
}
|
}
|
||||||
LineOffset[j + line_count] = temp;
|
LineOffset[j + line_count] = temp;
|
||||||
LineWraps[j + line_count] = false;
|
LineWraps[j + line_count] = false;
|
||||||
lineclear(temp, (int)Columns);
|
lineclear(temp, (int)Columns, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -6283,7 +6248,7 @@ int screen_del_lines(int row, int line_count, int end, int col, int width)
|
|||||||
linecopy(j - line_count, j, col, width);
|
linecopy(j - line_count, j, col, width);
|
||||||
}
|
}
|
||||||
j -= line_count;
|
j -= line_count;
|
||||||
lineclear(LineOffset[j] + col, width);
|
lineclear(LineOffset[j] + col, width, false);
|
||||||
LineWraps[j] = false;
|
LineWraps[j] = false;
|
||||||
} else {
|
} else {
|
||||||
// whole width, moving the line pointers is faster
|
// whole width, moving the line pointers is faster
|
||||||
@ -6295,7 +6260,7 @@ int screen_del_lines(int row, int line_count, int end, int col, int width)
|
|||||||
}
|
}
|
||||||
LineOffset[j - line_count] = temp;
|
LineOffset[j - line_count] = temp;
|
||||||
LineWraps[j - line_count] = false;
|
LineWraps[j - line_count] = false;
|
||||||
lineclear(temp, (int)Columns);
|
lineclear(temp, (int)Columns, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,6 +94,7 @@ typedef struct {
|
|||||||
bool can_change_scroll_region;
|
bool can_change_scroll_region;
|
||||||
bool can_set_lr_margin;
|
bool can_set_lr_margin;
|
||||||
bool can_set_left_right_margin;
|
bool can_set_left_right_margin;
|
||||||
|
bool can_erase_chars;
|
||||||
bool immediate_wrap_after_last_column;
|
bool immediate_wrap_after_last_column;
|
||||||
bool bce;
|
bool bce;
|
||||||
bool mouse_enabled;
|
bool mouse_enabled;
|
||||||
@ -103,7 +104,8 @@ typedef struct {
|
|||||||
cursorentry_T cursor_shapes[SHAPE_IDX_COUNT];
|
cursorentry_T cursor_shapes[SHAPE_IDX_COUNT];
|
||||||
HlAttrs clear_attrs;
|
HlAttrs clear_attrs;
|
||||||
kvec_t(HlAttrs) attrs;
|
kvec_t(HlAttrs) attrs;
|
||||||
HlAttrs print_attrs;
|
int print_attr_id;
|
||||||
|
bool has_bg;
|
||||||
bool default_attr;
|
bool default_attr;
|
||||||
ModeShape showing_mode;
|
ModeShape showing_mode;
|
||||||
struct {
|
struct {
|
||||||
@ -119,6 +121,7 @@ typedef struct {
|
|||||||
int set_cursor_style, reset_cursor_style;
|
int set_cursor_style, reset_cursor_style;
|
||||||
int enter_undercurl_mode, exit_undercurl_mode, set_underline_color;
|
int enter_undercurl_mode, exit_undercurl_mode, set_underline_color;
|
||||||
} unibi_ext;
|
} unibi_ext;
|
||||||
|
char *space_buf;
|
||||||
} TUIData;
|
} TUIData;
|
||||||
|
|
||||||
static bool volatile got_winch = false;
|
static bool volatile got_winch = false;
|
||||||
@ -184,6 +187,7 @@ static void terminfo_start(UI *ui)
|
|||||||
data->scroll_region_is_full_screen = true;
|
data->scroll_region_is_full_screen = true;
|
||||||
data->bufpos = 0;
|
data->bufpos = 0;
|
||||||
data->default_attr = false;
|
data->default_attr = false;
|
||||||
|
data->has_bg = false;
|
||||||
data->is_invisible = true;
|
data->is_invisible = true;
|
||||||
data->busy = false;
|
data->busy = false;
|
||||||
data->cork = false;
|
data->cork = false;
|
||||||
@ -239,6 +243,7 @@ static void terminfo_start(UI *ui)
|
|||||||
data->can_set_left_right_margin =
|
data->can_set_left_right_margin =
|
||||||
!!unibi_get_str(data->ut, unibi_set_left_margin_parm)
|
!!unibi_get_str(data->ut, unibi_set_left_margin_parm)
|
||||||
&& !!unibi_get_str(data->ut, unibi_set_right_margin_parm);
|
&& !!unibi_get_str(data->ut, unibi_set_right_margin_parm);
|
||||||
|
data->can_erase_chars = !!unibi_get_str(data->ut, unibi_erase_chars);
|
||||||
data->immediate_wrap_after_last_column =
|
data->immediate_wrap_after_last_column =
|
||||||
terminfo_is_term_family(term, "cygwin")
|
terminfo_is_term_family(term, "cygwin")
|
||||||
|| terminfo_is_term_family(term, "interix");
|
|| terminfo_is_term_family(term, "interix");
|
||||||
@ -302,7 +307,7 @@ static void terminfo_stop(UI *ui)
|
|||||||
static void tui_terminal_start(UI *ui)
|
static void tui_terminal_start(UI *ui)
|
||||||
{
|
{
|
||||||
TUIData *data = ui->data;
|
TUIData *data = ui->data;
|
||||||
data->print_attrs = HLATTRS_INVALID;
|
data->print_attr_id = -1;
|
||||||
ugrid_init(&data->grid);
|
ugrid_init(&data->grid);
|
||||||
terminfo_start(ui);
|
terminfo_start(ui);
|
||||||
update_size(ui);
|
update_size(ui);
|
||||||
@ -401,6 +406,7 @@ static void tui_main(UIBridgeData *bridge, UI *ui)
|
|||||||
loop_close(&tui_loop, false);
|
loop_close(&tui_loop, false);
|
||||||
kv_destroy(data->invalid_regions);
|
kv_destroy(data->invalid_regions);
|
||||||
kv_destroy(data->attrs);
|
kv_destroy(data->attrs);
|
||||||
|
xfree(data->space_buf);
|
||||||
xfree(data);
|
xfree(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -435,8 +441,17 @@ static void sigwinch_cb(SignalWatcher *watcher, int signum, void *data)
|
|||||||
ui_schedule_refresh();
|
ui_schedule_refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool attrs_differ(HlAttrs a1, HlAttrs a2, bool rgb)
|
static bool attrs_differ(UI *ui, int id1, int id2, bool rgb)
|
||||||
{
|
{
|
||||||
|
TUIData *data = ui->data;
|
||||||
|
if (id1 == id2) {
|
||||||
|
return false;
|
||||||
|
} else if (id1 < 0 || id2 < 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
HlAttrs a1 = kv_A(data->attrs, (size_t)id1);
|
||||||
|
HlAttrs a2 = kv_A(data->attrs, (size_t)id2);
|
||||||
|
|
||||||
if (rgb) {
|
if (rgb) {
|
||||||
return a1.rgb_fg_color != a2.rgb_fg_color
|
return a1.rgb_fg_color != a2.rgb_fg_color
|
||||||
|| a1.rgb_bg_color != a2.rgb_bg_color
|
|| a1.rgb_bg_color != a2.rgb_bg_color
|
||||||
@ -451,21 +466,16 @@ static bool attrs_differ(HlAttrs a1, HlAttrs a2, bool rgb)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool no_bg(UI *ui, HlAttrs attrs)
|
static void update_attrs(UI *ui, int attr_id)
|
||||||
{
|
|
||||||
return ui->rgb ? attrs.rgb_bg_color == -1
|
|
||||||
: attrs.cterm_bg_color == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void update_attrs(UI *ui, HlAttrs attrs)
|
|
||||||
{
|
{
|
||||||
TUIData *data = ui->data;
|
TUIData *data = ui->data;
|
||||||
|
|
||||||
if (!attrs_differ(attrs, data->print_attrs, ui->rgb)) {
|
if (!attrs_differ(ui, attr_id, data->print_attr_id, ui->rgb)) {
|
||||||
|
data->print_attr_id = attr_id;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
data->print_attr_id = attr_id;
|
||||||
data->print_attrs = attrs;
|
HlAttrs attrs = kv_A(data->attrs, (size_t)attr_id);
|
||||||
|
|
||||||
int fg = ui->rgb ? attrs.rgb_fg_color : (attrs.cterm_fg_color - 1);
|
int fg = ui->rgb ? attrs.rgb_fg_color : (attrs.cterm_fg_color - 1);
|
||||||
if (fg == -1) {
|
if (fg == -1) {
|
||||||
@ -479,6 +489,8 @@ static void update_attrs(UI *ui, HlAttrs attrs)
|
|||||||
: (data->clear_attrs.cterm_bg_color - 1);
|
: (data->clear_attrs.cterm_bg_color - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data->has_bg = bg != -1;
|
||||||
|
|
||||||
int attr = ui->rgb ? attrs.rgb_ae_attr : attrs.cterm_ae_attr;
|
int attr = ui->rgb ? attrs.rgb_ae_attr : attrs.cterm_ae_attr;
|
||||||
bool bold = attr & HL_BOLD;
|
bool bold = attr & HL_BOLD;
|
||||||
bool italic = attr & HL_ITALIC;
|
bool italic = attr & HL_ITALIC;
|
||||||
@ -595,7 +607,7 @@ static void print_cell(UI *ui, UCell *ptr)
|
|||||||
// Printing the next character finally advances the cursor.
|
// Printing the next character finally advances the cursor.
|
||||||
final_column_wrap(ui);
|
final_column_wrap(ui);
|
||||||
}
|
}
|
||||||
update_attrs(ui, kv_A(data->attrs, ptr->attr));
|
update_attrs(ui, ptr->attr);
|
||||||
out(ui, ptr->data, strlen(ptr->data));
|
out(ui, ptr->data, strlen(ptr->data));
|
||||||
grid->col++;
|
grid->col++;
|
||||||
if (data->immediate_wrap_after_last_column) {
|
if (data->immediate_wrap_after_last_column) {
|
||||||
@ -611,8 +623,8 @@ static bool cheap_to_print(UI *ui, int row, int col, int next)
|
|||||||
UCell *cell = grid->cells[row] + col;
|
UCell *cell = grid->cells[row] + col;
|
||||||
while (next) {
|
while (next) {
|
||||||
next--;
|
next--;
|
||||||
if (attrs_differ(kv_A(data->attrs, cell->attr),
|
if (attrs_differ(ui, cell->attr,
|
||||||
data->print_attrs, ui->rgb)) {
|
data->print_attr_id, ui->rgb)) {
|
||||||
if (data->default_attr) {
|
if (data->default_attr) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -662,7 +674,7 @@ static void cursor_goto(UI *ui, int row, int col)
|
|||||||
int n = col - grid->col;
|
int n = col - grid->col;
|
||||||
if (n <= (row == grid->row ? 4 : 2)
|
if (n <= (row == grid->row ? 4 : 2)
|
||||||
&& cheap_to_print(ui, grid->row, grid->col, n)) {
|
&& cheap_to_print(ui, grid->row, grid->col, n)) {
|
||||||
UGRID_FOREACH_CELL(grid, grid->row, grid->row, grid->col, col - 1, {
|
UGRID_FOREACH_CELL(grid, grid->row, grid->col, col, {
|
||||||
print_cell(ui, cell);
|
print_cell(ui, cell);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -734,50 +746,47 @@ safe_move:
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void clear_region(UI *ui, int top, int bot, int left, int right,
|
static void clear_region(UI *ui, int top, int bot, int left, int right,
|
||||||
HlAttrs attrs)
|
int attr_id)
|
||||||
{
|
{
|
||||||
TUIData *data = ui->data;
|
TUIData *data = ui->data;
|
||||||
UGrid *grid = &data->grid;
|
UGrid *grid = &data->grid;
|
||||||
|
|
||||||
bool cleared = false;
|
update_attrs(ui, attr_id);
|
||||||
|
|
||||||
// non-BCE terminals can't clear with non-default background color
|
// non-BCE terminals can't clear with non-default background color
|
||||||
bool can_clear = data->bce || no_bg(ui, attrs);
|
bool can_clear = data->bce || !data->has_bg;
|
||||||
|
|
||||||
if (can_clear && right == ui->width -1) {
|
// Background is set to the default color and the right edge matches the
|
||||||
// Background is set to the default color and the right edge matches the
|
// screen end, try to use terminal codes for clearing the requested area.
|
||||||
// screen end, try to use terminal codes for clearing the requested area.
|
if (can_clear && left == 0 && right == ui->width && bot == ui->height) {
|
||||||
update_attrs(ui, attrs);
|
if (top == 0) {
|
||||||
if (left == 0) {
|
unibi_out(ui, unibi_clear_screen);
|
||||||
if (bot == ui->height - 1) {
|
ugrid_goto(&data->grid, top, left);
|
||||||
if (top == 0) {
|
} else {
|
||||||
unibi_out(ui, unibi_clear_screen);
|
cursor_goto(ui, top, 0);
|
||||||
ugrid_goto(&data->grid, top, left);
|
unibi_out(ui, unibi_clr_eos);
|
||||||
} else {
|
|
||||||
cursor_goto(ui, top, 0);
|
|
||||||
unibi_out(ui, unibi_clr_eos);
|
|
||||||
}
|
|
||||||
cleared = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
int width = right-left;
|
||||||
|
|
||||||
if (!cleared) {
|
// iterate through each line and clear
|
||||||
// iterate through each line and clear with clr_eol
|
for (int row = top; row < bot; row++) {
|
||||||
for (int row = top; row <= bot; row++) {
|
cursor_goto(ui, row, left);
|
||||||
cursor_goto(ui, row, left);
|
if (can_clear && right == ui->width) {
|
||||||
unibi_out(ui, unibi_clr_eol);
|
unibi_out(ui, unibi_clr_eol);
|
||||||
|
} else if (data->can_erase_chars && can_clear && width >= 5) {
|
||||||
|
UNIBI_SET_NUM_VAR(data->params[0], width);
|
||||||
|
unibi_out(ui, unibi_erase_chars);
|
||||||
|
} else {
|
||||||
|
out(ui, data->space_buf, (size_t)width);
|
||||||
|
grid->col += width;
|
||||||
|
if (data->immediate_wrap_after_last_column) {
|
||||||
|
// Printing at the right margin immediately advances the cursor.
|
||||||
|
final_column_wrap(ui);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
cleared = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!cleared) {
|
|
||||||
// could not clear using faster terminal codes, refresh the whole region
|
|
||||||
UGRID_FOREACH_CELL(grid, top, bot, left, right, {
|
|
||||||
cursor_goto(ui, row, col);
|
|
||||||
print_cell(ui, cell);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void set_scroll_region(UI *ui, int top, int bot, int left, int right)
|
static void set_scroll_region(UI *ui, int top, int bot, int left, int right)
|
||||||
@ -838,12 +847,16 @@ static void tui_grid_resize(UI *ui, Integer g, Integer width, Integer height)
|
|||||||
UGrid *grid = &data->grid;
|
UGrid *grid = &data->grid;
|
||||||
ugrid_resize(grid, (int)width, (int)height);
|
ugrid_resize(grid, (int)width, (int)height);
|
||||||
|
|
||||||
|
xfree(data->space_buf);
|
||||||
|
data->space_buf = xmalloc((size_t)width * sizeof(*data->space_buf));
|
||||||
|
memset(data->space_buf, ' ', (size_t)width);
|
||||||
|
|
||||||
// resize might not always be followed by a clear before flush
|
// resize might not always be followed by a clear before flush
|
||||||
// so clip the invalid region
|
// so clip the invalid region
|
||||||
for (size_t i = 0; i < kv_size(data->invalid_regions); i++) {
|
for (size_t i = 0; i < kv_size(data->invalid_regions); i++) {
|
||||||
Rect *r = &kv_A(data->invalid_regions, i);
|
Rect *r = &kv_A(data->invalid_regions, i);
|
||||||
r->bot = MIN(r->bot, grid->height-1);
|
r->bot = MIN(r->bot, grid->height);
|
||||||
r->right = MIN(r->right, grid->width-1);
|
r->right = MIN(r->right, grid->width);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!got_winch) { // Try to resize the terminal window.
|
if (!got_winch) { // Try to resize the terminal window.
|
||||||
@ -866,8 +879,7 @@ static void tui_grid_clear(UI *ui, Integer g)
|
|||||||
UGrid *grid = &data->grid;
|
UGrid *grid = &data->grid;
|
||||||
ugrid_clear(grid);
|
ugrid_clear(grid);
|
||||||
kv_size(data->invalid_regions) = 0;
|
kv_size(data->invalid_regions) = 0;
|
||||||
clear_region(ui, 0, grid->height-1, 0, grid->width-1,
|
clear_region(ui, 0, grid->height, 0, grid->width, 0);
|
||||||
data->clear_attrs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tui_grid_cursor_goto(UI *ui, Integer grid, Integer row, Integer col)
|
static void tui_grid_cursor_goto(UI *ui, Integer grid, Integer row, Integer col)
|
||||||
@ -1025,9 +1037,7 @@ static void tui_grid_scroll(UI *ui, Integer g, Integer startrow, Integer endrow,
|
|||||||
data->scroll_region_is_full_screen = fullwidth
|
data->scroll_region_is_full_screen = fullwidth
|
||||||
&& top == 0 && bot == ui->height-1;
|
&& top == 0 && bot == ui->height-1;
|
||||||
|
|
||||||
int clear_top, clear_bot;
|
ugrid_scroll(grid, top, bot, left, right, (int)rows);
|
||||||
ugrid_scroll(grid, top, bot, left, right, (int)rows,
|
|
||||||
&clear_top, &clear_bot);
|
|
||||||
|
|
||||||
bool can_scroll = data->scroll_region_is_full_screen
|
bool can_scroll = data->scroll_region_is_full_screen
|
||||||
|| (data->can_change_scroll_region
|
|| (data->can_change_scroll_region
|
||||||
@ -1041,8 +1051,6 @@ static void tui_grid_scroll(UI *ui, Integer g, Integer startrow, Integer endrow,
|
|||||||
set_scroll_region(ui, top, bot, left, right);
|
set_scroll_region(ui, top, bot, left, right);
|
||||||
}
|
}
|
||||||
cursor_goto(ui, top, left);
|
cursor_goto(ui, top, left);
|
||||||
// also set default color attributes or some terminals can become funny
|
|
||||||
update_attrs(ui, data->clear_attrs);
|
|
||||||
|
|
||||||
if (rows > 0) {
|
if (rows > 0) {
|
||||||
if (rows == 1) {
|
if (rows == 1) {
|
||||||
@ -1064,16 +1072,14 @@ static void tui_grid_scroll(UI *ui, Integer g, Integer startrow, Integer endrow,
|
|||||||
if (!data->scroll_region_is_full_screen) {
|
if (!data->scroll_region_is_full_screen) {
|
||||||
reset_scroll_region(ui, fullwidth);
|
reset_scroll_region(ui, fullwidth);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(data->bce || no_bg(ui, data->clear_attrs))) {
|
|
||||||
// Scrolling will leave wrong background in the cleared area on non-BCE
|
|
||||||
// terminals. Update the cleared area.
|
|
||||||
clear_region(ui, clear_top, clear_bot, left, right,
|
|
||||||
data->clear_attrs);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// Mark the entire scroll region as invalid for redrawing later
|
// Mark the moved region as invalid for redrawing later
|
||||||
invalidate(ui, top, bot, left, right);
|
if (rows > 0) {
|
||||||
|
endrow = endrow - rows;
|
||||||
|
} else {
|
||||||
|
startrow = startrow - rows;
|
||||||
|
}
|
||||||
|
invalidate(ui, (int)startrow, (int)endrow, (int)startcol, (int)endcol);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1106,8 +1112,8 @@ static void tui_default_colors_set(UI *ui, Integer rgb_fg, Integer rgb_bg,
|
|||||||
data->clear_attrs.cterm_fg_color = (int)cterm_fg;
|
data->clear_attrs.cterm_fg_color = (int)cterm_fg;
|
||||||
data->clear_attrs.cterm_bg_color = (int)cterm_bg;
|
data->clear_attrs.cterm_bg_color = (int)cterm_bg;
|
||||||
|
|
||||||
data->print_attrs = HLATTRS_INVALID;
|
data->print_attr_id = -1;
|
||||||
invalidate(ui, 0, data->grid.height-1, 0, data->grid.width-1);
|
invalidate(ui, 0, data->grid.height, 0, data->grid.width);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tui_flush(UI *ui)
|
static void tui_flush(UI *ui)
|
||||||
@ -1129,11 +1135,27 @@ static void tui_flush(UI *ui)
|
|||||||
|
|
||||||
while (kv_size(data->invalid_regions)) {
|
while (kv_size(data->invalid_regions)) {
|
||||||
Rect r = kv_pop(data->invalid_regions);
|
Rect r = kv_pop(data->invalid_regions);
|
||||||
assert(r.bot < grid->height && r.right < grid->width);
|
assert(r.bot <= grid->height && r.right <= grid->width);
|
||||||
UGRID_FOREACH_CELL(grid, r.top, r.bot, r.left, r.right, {
|
|
||||||
cursor_goto(ui, row, col);
|
for (int row = r.top; row < r.bot; row++) {
|
||||||
print_cell(ui, cell);
|
int clear_attr = grid->cells[row][r.right-1].attr;
|
||||||
});
|
int clear_col;
|
||||||
|
for (clear_col = r.right; clear_col > 0; clear_col--) {
|
||||||
|
UCell *cell = &grid->cells[row][clear_col-1];
|
||||||
|
if (!(cell->data[0] == ' ' && cell->data[1] == NUL
|
||||||
|
&& cell->attr == clear_attr)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UGRID_FOREACH_CELL(grid, row, r.left, clear_col, {
|
||||||
|
cursor_goto(ui, row, col);
|
||||||
|
print_cell(ui, cell);
|
||||||
|
});
|
||||||
|
if (clear_col < r.right) {
|
||||||
|
clear_region(ui, row, row+1, clear_col, r.right, clear_attr);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cursor_goto(ui, data->row, data->col);
|
cursor_goto(ui, data->row, data->col);
|
||||||
@ -1218,8 +1240,8 @@ static void tui_option_set(UI *ui, String name, Object value)
|
|||||||
if (strequal(name.data, "termguicolors")) {
|
if (strequal(name.data, "termguicolors")) {
|
||||||
ui->rgb = value.data.boolean;
|
ui->rgb = value.data.boolean;
|
||||||
|
|
||||||
data->print_attrs = HLATTRS_INVALID;
|
data->print_attr_id = -1;
|
||||||
invalidate(ui, 0, data->grid.height-1, 0, data->grid.width-1);
|
invalidate(ui, 0, data->grid.height, 0, data->grid.width);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1235,18 +1257,16 @@ static void tui_raw_line(UI *ui, Integer g, Integer linerow, Integer startcol,
|
|||||||
assert((size_t)attrs[c-startcol] < kv_size(data->attrs));
|
assert((size_t)attrs[c-startcol] < kv_size(data->attrs));
|
||||||
grid->cells[linerow][c].attr = attrs[c-startcol];
|
grid->cells[linerow][c].attr = attrs[c-startcol];
|
||||||
}
|
}
|
||||||
UGRID_FOREACH_CELL(grid, (int)linerow, (int)linerow, (int)startcol,
|
UGRID_FOREACH_CELL(grid, (int)linerow, (int)startcol, (int)endcol, {
|
||||||
(int)endcol-1, {
|
cursor_goto(ui, (int)linerow, col);
|
||||||
cursor_goto(ui, row, col);
|
|
||||||
print_cell(ui, cell);
|
print_cell(ui, cell);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (clearcol > endcol) {
|
if (clearcol > endcol) {
|
||||||
HlAttrs cl_attrs = kv_A(data->attrs, (size_t)clearattr);
|
|
||||||
ugrid_clear_chunk(grid, (int)linerow, (int)endcol, (int)clearcol,
|
ugrid_clear_chunk(grid, (int)linerow, (int)endcol, (int)clearcol,
|
||||||
(sattr_T)clearattr);
|
(sattr_T)clearattr);
|
||||||
clear_region(ui, (int)linerow, (int)linerow, (int)endcol, (int)clearcol-1,
|
clear_region(ui, (int)linerow, (int)linerow+1, (int)endcol, (int)clearcol,
|
||||||
cl_attrs);
|
(int)clearattr);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wrap && ui->width == grid->width && linerow + 1 < grid->height) {
|
if (wrap && ui->width == grid->width && linerow + 1 < grid->height) {
|
||||||
@ -1269,27 +1289,17 @@ static void invalidate(UI *ui, int top, int bot, int left, int right)
|
|||||||
{
|
{
|
||||||
TUIData *data = ui->data;
|
TUIData *data = ui->data;
|
||||||
Rect *intersects = NULL;
|
Rect *intersects = NULL;
|
||||||
// Increase dimensions before comparing to ensure adjacent regions are
|
|
||||||
// treated as intersecting
|
|
||||||
--top;
|
|
||||||
++bot;
|
|
||||||
--left;
|
|
||||||
++right;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < kv_size(data->invalid_regions); i++) {
|
for (size_t i = 0; i < kv_size(data->invalid_regions); i++) {
|
||||||
Rect *r = &kv_A(data->invalid_regions, i);
|
Rect *r = &kv_A(data->invalid_regions, i);
|
||||||
if (!(top > r->bot || bot < r->top
|
// adjacent regions are treated as overlapping
|
||||||
|| left > r->right || right < r->left)) {
|
if (!(top > r->bot || bot < r->top)
|
||||||
|
&& !(left > r->right || right < r->left)) {
|
||||||
intersects = r;
|
intersects = r;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
++top;
|
|
||||||
--bot;
|
|
||||||
++left;
|
|
||||||
--right;
|
|
||||||
|
|
||||||
if (intersects) {
|
if (intersects) {
|
||||||
// If top/bot/left/right intersects with a invalid rect, we replace it
|
// If top/bot/left/right intersects with a invalid rect, we replace it
|
||||||
// by the union
|
// by the union
|
||||||
|
@ -52,8 +52,7 @@ void ugrid_goto(UGrid *grid, int row, int col)
|
|||||||
grid->col = col;
|
grid->col = col;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ugrid_scroll(UGrid *grid, int top, int bot, int left, int right,
|
void ugrid_scroll(UGrid *grid, int top, int bot, int left, int right, int count)
|
||||||
int count, int *clear_top, int *clear_bot)
|
|
||||||
{
|
{
|
||||||
// Compute start/stop/step for the loop below
|
// Compute start/stop/step for the loop below
|
||||||
int start, stop, step;
|
int start, stop, step;
|
||||||
@ -76,26 +75,18 @@ void ugrid_scroll(UGrid *grid, int top, int bot, int left, int right,
|
|||||||
memcpy(target_row, source_row,
|
memcpy(target_row, source_row,
|
||||||
sizeof(UCell) * (size_t)(right - left + 1));
|
sizeof(UCell) * (size_t)(right - left + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
// clear cells in the emptied region,
|
|
||||||
if (count > 0) {
|
|
||||||
*clear_top = stop;
|
|
||||||
*clear_bot = stop + count - 1;
|
|
||||||
} else {
|
|
||||||
*clear_bot = stop;
|
|
||||||
*clear_top = stop + count + 1;
|
|
||||||
}
|
|
||||||
clear_region(grid, *clear_top, *clear_bot, left, right, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void clear_region(UGrid *grid, int top, int bot, int left, int right,
|
static void clear_region(UGrid *grid, int top, int bot, int left, int right,
|
||||||
sattr_T attr)
|
sattr_T attr)
|
||||||
{
|
{
|
||||||
UGRID_FOREACH_CELL(grid, top, bot, left, right, {
|
for (int row = top; row <= bot; row++) {
|
||||||
cell->data[0] = ' ';
|
UGRID_FOREACH_CELL(grid, row, left, right+1, {
|
||||||
cell->data[1] = 0;
|
cell->data[0] = ' ';
|
||||||
cell->attr = attr;
|
cell->data[1] = 0;
|
||||||
});
|
cell->attr = attr;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void destroy_cells(UGrid *grid)
|
static void destroy_cells(UGrid *grid)
|
||||||
|
@ -22,15 +22,13 @@ struct ugrid {
|
|||||||
|
|
||||||
// -V:UGRID_FOREACH_CELL:625
|
// -V:UGRID_FOREACH_CELL:625
|
||||||
|
|
||||||
#define UGRID_FOREACH_CELL(grid, top, bot, left, right, code) \
|
#define UGRID_FOREACH_CELL(grid, row, startcol, endcol, code) \
|
||||||
do { \
|
do { \
|
||||||
for (int row = top; row <= bot; row++) { \
|
UCell *row_cells = (grid)->cells[row]; \
|
||||||
UCell *row_cells = (grid)->cells[row]; \
|
for (int col = startcol; col < endcol; col++) { \
|
||||||
for (int col = left; col <= right; col++) { \
|
UCell *cell = row_cells + col; \
|
||||||
UCell *cell = row_cells + col; \
|
(void)(cell); \
|
||||||
(void)(cell); \
|
code; \
|
||||||
code; \
|
|
||||||
} \
|
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user