Implement rendering of selections
This commit is contained in:
parent
05662efdd9
commit
f4e3fbcb2e
@ -136,6 +136,10 @@ GLFW_KEY_RIGHT_ALT = 346
|
||||
GLFW_KEY_RIGHT_SUPER = 347
|
||||
GLFW_KEY_MENU = 348
|
||||
GLFW_KEY_LAST = GLFW_KEY_MENU
|
||||
MODIFIER_KEYS = (
|
||||
GLFW_KEY_LEFT_SHIFT, GLFW_KEY_RIGHT_SHIFT, GLFW_KEY_LEFT_ALT,
|
||||
GLFW_KEY_RIGHT_ALT, GLFW_KEY_LEFT_CONTROL, GLFW_KEY_RIGHT_CONTROL,
|
||||
GLFW_KEY_LEFT_SUPER, GLFW_KEY_RIGHT_SUPER)
|
||||
|
||||
|
||||
# --- Modifiers ---------------------------------------------------------------
|
||||
|
||||
@ -148,10 +148,30 @@ def color_as_int(val):
|
||||
return val[0] << 16 | val[1] << 8 | val[2]
|
||||
|
||||
|
||||
class Selection:
|
||||
|
||||
__slots__ = tuple('in_progress start_x start_y start_scrolled_by end_x end_y end_scrolled_by'.split())
|
||||
|
||||
def __init__(self):
|
||||
self.clear()
|
||||
|
||||
def clear(self):
|
||||
self.in_progress = False
|
||||
self.start_x = self.start_y = self.end_x = self.end_y = 0
|
||||
self.start_scrolled_by = self.end_scrolled_by = 0
|
||||
|
||||
def limits(self, scrolled_by):
|
||||
a = (self.start_x, self.start_y - self.start_scrolled_by + scrolled_by)
|
||||
b = (self.end_x, self.end_y - self.end_scrolled_by + scrolled_by)
|
||||
return (a, b) if a <= b else (b, a)
|
||||
|
||||
|
||||
class CharGrid:
|
||||
|
||||
def __init__(self, screen, opts):
|
||||
self.buffer_lock = Lock()
|
||||
self.current_selection = Selection()
|
||||
self.last_rendered_selection = self.current_selection.limits(0)
|
||||
self.render_buf_is_dirty = True
|
||||
self.render_data = None
|
||||
self.scrolled_by = 0
|
||||
@ -170,6 +190,7 @@ class CharGrid:
|
||||
self.opts = opts
|
||||
self.original_bg = opts.background
|
||||
self.original_fg = opts.foreground
|
||||
self.selection_foreground, self.selection_background = map(color_as_int, (opts.selection_foreground, opts.selection_background))
|
||||
self.sprite_map_type = self.main_sprite_map = self.scroll_sprite_map = self.render_buf = None
|
||||
|
||||
def update_position(self, window_geometry):
|
||||
@ -187,7 +208,9 @@ class CharGrid:
|
||||
self.scroll_sprite_map = self.sprite_map_type()
|
||||
with self.buffer_lock:
|
||||
self.render_buf = self.sprite_map_type()
|
||||
self.selection_buf = self.sprite_map_type()
|
||||
self.render_buf_is_dirty = True
|
||||
self.current_selection.clear()
|
||||
|
||||
def change_colors(self, changes):
|
||||
dirtied = False
|
||||
@ -215,6 +238,7 @@ class CharGrid:
|
||||
|
||||
def update_cell_data(self, force_full_refresh=False):
|
||||
sprites = tab_manager().sprites
|
||||
is_dirty = self.screen.is_dirty()
|
||||
with sprites.lock:
|
||||
cursor_changed, history_line_added_count = self.screen.update_cell_data(
|
||||
sprites.backend, self.color_profile, addressof(self.main_sprite_map), self.default_fg, self.default_bg, force_full_refresh)
|
||||
@ -226,6 +250,8 @@ class CharGrid:
|
||||
|
||||
data = self.scroll_sprite_map if self.scrolled_by else self.main_sprite_map
|
||||
with self.buffer_lock:
|
||||
if is_dirty:
|
||||
self.current_selection.clear()
|
||||
memmove(self.render_buf, data, sizeof(type(data)))
|
||||
self.render_data = self.screen_geometry
|
||||
self.render_buf_is_dirty = True
|
||||
@ -233,14 +259,40 @@ class CharGrid:
|
||||
c = self.screen.cursor
|
||||
self.current_cursor = Cursor(c.x, c.y, c.hidden, c.shape, c.color, c.blink)
|
||||
|
||||
def cell_for_pos(self, x, y):
|
||||
return int(x // cell_size.width), int(y // cell_size.height)
|
||||
|
||||
def update_drag(self, is_press, x, y):
|
||||
with self.buffer_lock:
|
||||
x, y = self.cell_for_pos(x, y)
|
||||
if is_press:
|
||||
self.current_selection.start_x = self.current_selection.end_x = x
|
||||
self.current_selection.start_y = self.current_selection.end_y = y
|
||||
self.current_selection.start_scrolled_by = self.current_selection.end_scrolled_by = self.scrolled_by
|
||||
self.current_selection.in_progress = True
|
||||
elif self.current_selection.in_progress:
|
||||
self.current_selection.end_x = x
|
||||
self.current_selection.end_y = y
|
||||
self.current_selection.end_scrolled_by = self.scrolled_by
|
||||
if is_press is False:
|
||||
self.current_selection.in_progress = False
|
||||
|
||||
def prepare_for_render(self, sprites):
|
||||
with self.buffer_lock:
|
||||
sg = self.render_data
|
||||
if sg is None:
|
||||
return
|
||||
if self.render_buf_is_dirty:
|
||||
sprites.set_sprite_map(self.render_buf)
|
||||
buf = self.render_buf
|
||||
start, end = sel = self.current_selection.limits(self.scrolled_by)
|
||||
if start != end:
|
||||
buf = self.selection_buf
|
||||
if self.render_buf_is_dirty or sel != self.last_rendered_selection:
|
||||
memmove(buf, self.render_buf, sizeof(type(buf)))
|
||||
self.screen.apply_selection(addressof(buf), start[0], start[1], end[0], end[1], self.selection_foreground, self.selection_background)
|
||||
if self.render_buf_is_dirty or self.last_rendered_selection != sel:
|
||||
sprites.set_sprite_map(buf)
|
||||
self.render_buf_is_dirty = False
|
||||
self.last_rendered_selection = sel
|
||||
return sg
|
||||
|
||||
def render_cells(self, sg, cell_program, sprites):
|
||||
|
||||
@ -47,7 +47,7 @@ type_map = {
|
||||
'window_border_width': float,
|
||||
}
|
||||
|
||||
for name in 'foreground background cursor active_border_color inactive_border_color'.split():
|
||||
for name in 'foreground background cursor active_border_color inactive_border_color selection_foreground selection_background'.split():
|
||||
type_map[name] = lambda x: to_color(x, validate=True)
|
||||
for i in range(16):
|
||||
type_map['color%d' % i] = lambda x: to_color(x, validate=True)
|
||||
|
||||
@ -40,6 +40,9 @@ class ViewportSize:
|
||||
def __init__(self):
|
||||
self.width = self.height = 1024
|
||||
|
||||
def __repr__(self):
|
||||
return '(width={}, height={})'.format(self.width, self.height)
|
||||
|
||||
|
||||
def tab_manager():
|
||||
return tab_manager.manager
|
||||
|
||||
@ -4,6 +4,10 @@ term xterm-kitty
|
||||
foreground #dddddd
|
||||
# The background color
|
||||
background #000000
|
||||
# The foreground for selections
|
||||
selection_foreground #000000
|
||||
# The background for selections
|
||||
selection_background #FFFACD
|
||||
|
||||
# The cursor color
|
||||
cursor #ffffff
|
||||
|
||||
@ -1000,6 +1000,21 @@ set_scroll_cell_data(Screen *self, PyObject *args) {
|
||||
}
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
apply_selection(Screen *self, PyObject *args) {
|
||||
unsigned int fg, bg, startx, endx, starty, endy;
|
||||
PyObject *l;
|
||||
if (!PyArg_ParseTuple(args, "O!IIIIII", &PyLong_Type, &l, &startx, &starty, &endx, &endy, &fg, &bg)) return NULL;
|
||||
if (startx >= self->columns || starty >= self->lines || endx >= self->columns || endy >= self->lines) { Py_RETURN_NONE; }
|
||||
unsigned int *data = PyLong_AsVoidPtr(l), offset;
|
||||
for(unsigned int i = starty * self->columns + startx; i <= endy * self->columns + endx; i++) {
|
||||
offset = DATA_CELL_SIZE * i;
|
||||
data[offset + 3] = fg;
|
||||
data[offset + 4] = bg;
|
||||
}
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject* is_dirty(Screen *self) {
|
||||
PyObject *ans = self->change_tracker->dirty ? Py_True : Py_False;
|
||||
@ -1068,6 +1083,7 @@ static PyMethodDef methods[] = {
|
||||
MND(mark_as_dirty, METH_NOARGS)
|
||||
MND(resize, METH_VARARGS)
|
||||
MND(set_scroll_cell_data, METH_VARARGS)
|
||||
MND(apply_selection, METH_VARARGS)
|
||||
MND(in_bracketed_paste_mode, METH_NOARGS)
|
||||
MND(focus_tracking_enabled, METH_NOARGS)
|
||||
MND(mouse_button_tracking_enabled, METH_NOARGS)
|
||||
|
||||
@ -302,7 +302,7 @@ class TabManager(Thread):
|
||||
if not passthrough:
|
||||
return
|
||||
if window:
|
||||
if window.char_grid.scrolled_by:
|
||||
if window.char_grid.scrolled_by and key not in glfw_constants.MODIFIER_KEYS:
|
||||
window.scroll_end()
|
||||
data = interpret_key_event(key, scancode, mods)
|
||||
if data:
|
||||
@ -323,7 +323,7 @@ class TabManager(Thread):
|
||||
if w is not None:
|
||||
if button == glfw_constants.GLFW_MOUSE_BUTTON_1 and w is not self.active_window:
|
||||
pass # TODO: Switch focus to this window
|
||||
w.on_mouse_button(button, action, mods)
|
||||
w.on_mouse_button(window, button, action, mods)
|
||||
|
||||
def on_mouse_move(self, window, xpos, ypos):
|
||||
w = self.window_for_pos(*glfw.glfwGetCursorPos(window))
|
||||
|
||||
@ -10,7 +10,7 @@ from functools import partial
|
||||
import glfw
|
||||
import glfw_constants
|
||||
from .char_grid import CharGrid
|
||||
from .constants import wakeup, tab_manager, appname, WindowGeometry
|
||||
from .constants import wakeup, tab_manager, appname, WindowGeometry, queue_action
|
||||
from .fast_data_types import (
|
||||
BRACKETED_PASTE_START, BRACKETED_PASTE_END, Screen, read_bytes_dump, read_bytes
|
||||
)
|
||||
@ -114,15 +114,19 @@ class Window:
|
||||
def request_capabilities(self, q):
|
||||
self.write_to_child(get_capabilities(q))
|
||||
|
||||
def on_mouse_button(self, button, action, mods):
|
||||
# ignore_mouse_mode = mods == glfw_constants.GLFW_MOD_SHIFT
|
||||
def on_mouse_button(self, window, button, action, mods):
|
||||
ignore_mouse_mode = mods == glfw_constants.GLFW_MOD_SHIFT or not self.screen.mouse_button_tracking_enabled()
|
||||
if button == glfw_constants.GLFW_MOUSE_BUTTON_1 and ignore_mouse_mode:
|
||||
x, y = glfw.glfwGetCursorPos(window)
|
||||
self.char_grid.update_drag(action == glfw_constants.GLFW_PRESS, max(0, x - self.geometry.left), max(0, y - self.geometry.top))
|
||||
if action == glfw_constants.GLFW_RELEASE:
|
||||
if button == glfw_constants.GLFW_MOUSE_BUTTON_MIDDLE:
|
||||
self.paste_from_selection()
|
||||
return
|
||||
|
||||
def on_mouse_move(self, xpos, ypos):
|
||||
pass
|
||||
def on_mouse_move(self, x, y):
|
||||
if self.char_grid.current_selection.in_progress:
|
||||
self.char_grid.update_drag(None, max(0, x - self.geometry.left), max(0, y - self.geometry.top))
|
||||
|
||||
def on_mouse_scroll(self, x, y):
|
||||
pass
|
||||
@ -146,22 +150,22 @@ class Window:
|
||||
self.paste(text)
|
||||
|
||||
def scroll_line_up(self):
|
||||
self.queue_action(self.char_grid.scroll, 'line', True)
|
||||
queue_action(self.char_grid.scroll, 'line', True)
|
||||
|
||||
def scroll_line_down(self):
|
||||
self.queue_action(self.char_grid.scroll, 'line', False)
|
||||
queue_action(self.char_grid.scroll, 'line', False)
|
||||
|
||||
def scroll_page_up(self):
|
||||
self.queue_action(self.char_grid.scroll, 'page', True)
|
||||
queue_action(self.char_grid.scroll, 'page', True)
|
||||
|
||||
def scroll_page_down(self):
|
||||
self.queue_action(self.char_grid.scroll, 'page', False)
|
||||
queue_action(self.char_grid.scroll, 'page', False)
|
||||
|
||||
def scroll_home(self):
|
||||
self.queue_action(self.char_grid.scroll, 'full', True)
|
||||
queue_action(self.char_grid.scroll, 'full', True)
|
||||
|
||||
def scroll_end(self):
|
||||
self.queue_action(self.char_grid.scroll, 'full', False)
|
||||
queue_action(self.char_grid.scroll, 'full', False)
|
||||
# }}}
|
||||
|
||||
def dump_commands(self, *a):
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user