mirror of
https://github.com/kovidgoyal/kitty.git
synced 2026-06-27 19:31:46 +00:00
Implement rendering of selections that intersect multicell cells
This commit is contained in:
parent
2a996418bf
commit
ac32f91a2e
2 changed files with 78 additions and 5 deletions
|
|
@ -7,11 +7,13 @@
|
|||
|
||||
#define EXTRA_INIT { \
|
||||
PyModule_AddIntMacro(module, SCROLL_LINE); PyModule_AddIntMacro(module, SCROLL_PAGE); PyModule_AddIntMacro(module, SCROLL_FULL); \
|
||||
PyModule_AddIntMacro(module, EXTEND_CELL); PyModule_AddIntMacro(module, EXTEND_WORD); PyModule_AddIntMacro(module, EXTEND_LINE); \
|
||||
if (PyModule_AddFunctions(module, module_methods) != 0) return false; \
|
||||
}
|
||||
|
||||
#include "data-types.h"
|
||||
#include "control-codes.h"
|
||||
#include "screen.h"
|
||||
#include "state.h"
|
||||
#include "iqsort.h"
|
||||
#include "fonts.h"
|
||||
|
|
@ -3359,6 +3361,19 @@ xrange_for_iteration(const IterationData *idata, const int y, const Line *line)
|
|||
return ans;
|
||||
}
|
||||
|
||||
static XRange
|
||||
xrange_for_iteration_with_multicells(const IterationData *idata, const int y, const Line *line) {
|
||||
XRange ans = xrange_for_iteration(idata, y, line);
|
||||
if (ans.x_limit > ans.x) {
|
||||
CPUCell *c; index_type ml;
|
||||
if (ans.x && (c = &line->cpu_cells[ans.x])->is_multicell && c->x) ans.x = ans.x > c->x ? ans.x - c->x : 0;
|
||||
if (ans.x_limit < line->xnum && (c = &line->cpu_cells[ans.x_limit-1])->is_multicell && c->x + 1u < (ml = mcd_x_limit(c))) {
|
||||
ans.x_limit += ml - 1 - c->x; if (ans.x_limit > line->xnum) ans.x_limit = line->xnum;
|
||||
}
|
||||
}
|
||||
return ans;
|
||||
}
|
||||
|
||||
static bool
|
||||
iteration_data_is_empty(const Screen *self, const IterationData *idata) {
|
||||
if (idata->y >= idata->y_limit) return true;
|
||||
|
|
@ -3375,15 +3390,22 @@ static void
|
|||
apply_selection(Screen *self, uint8_t *data, Selection *s, uint8_t set_mask) {
|
||||
iteration_data(s, &s->last_rendered, self->columns, -self->historybuf->count, self->scrolled_by);
|
||||
Line *line;
|
||||
|
||||
for (int y = MAX(0, s->last_rendered.y); y < s->last_rendered.y_limit && y < (int)self->lines; y++) {
|
||||
const int y_min = MAX(0, s->last_rendered.y), y_limit = MIN(s->last_rendered.y_limit, (int)self->lines);
|
||||
for (int y = y_min; y < y_limit; y++) {
|
||||
if (self->paused_rendering.expires_at) {
|
||||
linebuf_init_line(self->paused_rendering.linebuf, y);
|
||||
line = self->paused_rendering.linebuf->line;
|
||||
} else line = visual_line_(self, y);
|
||||
uint8_t *line_start = data + self->columns * y;
|
||||
XRange xr = xrange_for_iteration(&s->last_rendered, y, line);
|
||||
for (index_type x = xr.x; x < xr.x_limit; x++) line_start[x] |= set_mask;
|
||||
XRange xr = xrange_for_iteration_with_multicells(&s->last_rendered, y, line);
|
||||
for (index_type x = xr.x; x < xr.x_limit; x++) {
|
||||
line_start[x] |= set_mask;
|
||||
CPUCell *c = &line->cpu_cells[x];
|
||||
if (c->is_multicell && c->scale > 1) {
|
||||
for (int ym = MAX(0, y - c->y); ym < y; ym++) data[self->columns * ym + x] |= set_mask;
|
||||
for (int ym = y + 1; ym < MIN((int)self->lines, y + c->scale - c->y); ym++) data[self->columns * ym + x] |= set_mask;
|
||||
}
|
||||
}
|
||||
}
|
||||
s->last_rendered.y = MAX(0, s->last_rendered.y);
|
||||
}
|
||||
|
|
@ -5178,6 +5200,14 @@ line_edge_colors(Screen *self, PyObject *a UNUSED) {
|
|||
return Py_BuildValue("kk", (unsigned long)left, (unsigned long)right);
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
current_selections(Screen *self, PyObject *a UNUSED) {
|
||||
PyObject *ans = PyBytes_FromStringAndSize(NULL, self->lines * self->columns);
|
||||
if (!ans) return NULL;
|
||||
screen_apply_selection(self, PyBytes_AS_STRING(ans), PyBytes_GET_SIZE(ans));
|
||||
return ans;
|
||||
}
|
||||
|
||||
WRAP0(update_only_line_graphics_data)
|
||||
WRAP0(bell)
|
||||
|
||||
|
|
@ -5364,6 +5394,7 @@ static PyMethodDef methods[] = {
|
|||
MND(scroll_to_next_mark, METH_VARARGS)
|
||||
MND(update_only_line_graphics_data, METH_NOARGS)
|
||||
MND(bell, METH_NOARGS)
|
||||
MND(current_selections, METH_NOARGS)
|
||||
{"select_graphic_rendition", (PyCFunction)_select_graphic_rendition, METH_VARARGS, ""},
|
||||
|
||||
{NULL} /* Sentinel */
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
# License: GPLv3 Copyright: 2024, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
|
||||
from kitty.fast_data_types import TEXT_SIZE_CODE, test_ch_and_idx, wcswidth
|
||||
from kitty.fast_data_types import EXTEND_CELL, TEXT_SIZE_CODE, test_ch_and_idx, wcswidth
|
||||
|
||||
from . import BaseTest, parse_bytes
|
||||
from . import draw_multicell as multicell
|
||||
|
|
@ -591,3 +591,45 @@ def test_multicell(self: TestMulticell) -> None:
|
|||
for y in range(2):
|
||||
for x in range(4):
|
||||
ac(x, y, is_multicell=True)
|
||||
|
||||
# selections
|
||||
s = self.create_screen(lines=5, cols=8)
|
||||
|
||||
def p(x=0, y=0, in_left_half_of_cell=True):
|
||||
return (x, y, in_left_half_of_cell)
|
||||
|
||||
def ss(start, end, rectangle_select=False, extend_mode=EXTEND_CELL):
|
||||
s.start_selection(start[0], start[1], rectangle_select, extend_mode, start[2])
|
||||
s.update_selection(end[0], end[1], end[2])
|
||||
|
||||
def asl(*ranges, bp=1):
|
||||
actual = s.current_selections()
|
||||
def as_lists(x):
|
||||
a = []
|
||||
for y in range(s.lines):
|
||||
a.append(x[y*s.columns: (y+1)*s.columns ])
|
||||
return a
|
||||
|
||||
expected = bytearray(s.lines * s.columns)
|
||||
for (y, x1, x2) in ranges:
|
||||
pos = y * s.columns
|
||||
for x in range(x1, x2 + 1):
|
||||
expected[pos + x] = bp
|
||||
for i, (e, a) in enumerate(zip(as_lists(bytes(expected)), as_lists(actual))):
|
||||
self.ae(e, a, f'Row: {i}')
|
||||
|
||||
s.reset()
|
||||
s.draw('a'), multicell(s, 'b', width=2), s.draw('c')
|
||||
ss(p(), p(x=1, in_left_half_of_cell=False))
|
||||
asl((0, 0, 2))
|
||||
ss(p(x=2), p(x=3, in_left_half_of_cell=False))
|
||||
asl((0, 1, 3))
|
||||
|
||||
s.reset()
|
||||
s.draw('a'), multicell(s, 'b', scale=2), s.draw('c'), multicell(s, 'd', scale=2)
|
||||
ss(p(), p(x=4, in_left_half_of_cell=False))
|
||||
asl((0, 0, 5), (1, 1, 2), (1, 4, 5))
|
||||
ss(p(y=1, x=1), p(y=1, x=1, in_left_half_of_cell=False))
|
||||
asl((0, 1, 2), (1, 1, 2))
|
||||
ss(p(y=1, x=0), p(y=1, x=1, in_left_half_of_cell=False)) # empty leading cell before multiline on y=1
|
||||
asl((0, 1, 2), (1, 0, 2))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue