Add basic editing support for the broadcast kitten
This commit is contained in:
parent
34db18ea0a
commit
00aba7c646
@ -12,11 +12,12 @@ from kitty.cli_stub import BroadcastCLIOptions
|
|||||||
from kitty.key_encoding import RELEASE, encode_key_event, key_defs as K
|
from kitty.key_encoding import RELEASE, encode_key_event, key_defs as K
|
||||||
from kitty.rc.base import MATCH_TAB_OPTION, MATCH_WINDOW_OPTION
|
from kitty.rc.base import MATCH_TAB_OPTION, MATCH_WINDOW_OPTION
|
||||||
from kitty.remote_control import create_basic_command, encode_send
|
from kitty.remote_control import create_basic_command, encode_send
|
||||||
from kitty.typing import KeyEventType
|
from kitty.typing import KeyEventType, ScreenSize
|
||||||
|
|
||||||
from ..tui.handler import Handler
|
from ..tui.handler import Handler
|
||||||
|
from ..tui.line_edit import LineEdit
|
||||||
from ..tui.loop import Loop
|
from ..tui.loop import Loop
|
||||||
from ..tui.operations import styled
|
from ..tui.operations import RESTORE_CURSOR, SAVE_CURSOR, styled
|
||||||
|
|
||||||
|
|
||||||
class Broadcast(Handler):
|
class Broadcast(Handler):
|
||||||
@ -25,6 +26,7 @@ class Broadcast(Handler):
|
|||||||
self.opts = opts
|
self.opts = opts
|
||||||
self.initial_strings = initial_strings
|
self.initial_strings = initial_strings
|
||||||
self.payload = {'exclude_active': True, 'data': '', 'match': opts.match_tab, 'match_tab': opts.match_tab}
|
self.payload = {'exclude_active': True, 'data': '', 'match': opts.match_tab, 'match_tab': opts.match_tab}
|
||||||
|
self.line_edit = LineEdit()
|
||||||
if not opts.match and not opts.match_tab:
|
if not opts.match and not opts.match_tab:
|
||||||
self.payload['all'] = True
|
self.payload['all'] = True
|
||||||
|
|
||||||
@ -32,10 +34,21 @@ class Broadcast(Handler):
|
|||||||
self.print('Type the text to broadcast below, press', styled('Ctrl+c', fg='yellow'), 'to quit:')
|
self.print('Type the text to broadcast below, press', styled('Ctrl+c', fg='yellow'), 'to quit:')
|
||||||
for x in self.initial_strings:
|
for x in self.initial_strings:
|
||||||
self.write_broadcast_text(x)
|
self.write_broadcast_text(x)
|
||||||
|
self.write(SAVE_CURSOR)
|
||||||
|
|
||||||
|
def commit_line(self) -> None:
|
||||||
|
self.write(RESTORE_CURSOR + SAVE_CURSOR)
|
||||||
|
self.cmd.clear_to_end_of_screen()
|
||||||
|
self.line_edit.write(self.write, screen_cols=self.screen_size.cols)
|
||||||
|
|
||||||
|
def on_resize(self, screen_size: ScreenSize) -> None:
|
||||||
|
super().on_resize(screen_size)
|
||||||
|
self.commit_line()
|
||||||
|
|
||||||
def on_text(self, text: str, in_bracketed_paste: bool = False) -> None:
|
def on_text(self, text: str, in_bracketed_paste: bool = False) -> None:
|
||||||
self.write_broadcast_text(text)
|
self.write_broadcast_text(text)
|
||||||
self.write(text)
|
self.line_edit.on_text(text, in_bracketed_paste)
|
||||||
|
self.commit_line()
|
||||||
|
|
||||||
def on_interrupt(self) -> None:
|
def on_interrupt(self) -> None:
|
||||||
self.quit_loop(0)
|
self.quit_loop(0)
|
||||||
@ -44,19 +57,16 @@ class Broadcast(Handler):
|
|||||||
self.write_broadcast_text('\x04')
|
self.write_broadcast_text('\x04')
|
||||||
|
|
||||||
def on_key(self, key_event: KeyEventType) -> None:
|
def on_key(self, key_event: KeyEventType) -> None:
|
||||||
|
if self.line_edit.on_key(key_event):
|
||||||
|
self.commit_line()
|
||||||
if key_event.type is not RELEASE and not key_event.mods:
|
if key_event.type is not RELEASE and not key_event.mods:
|
||||||
if key_event.key is K['TAB']:
|
if key_event.key is K['ENTER']:
|
||||||
self.write_broadcast_text('\t')
|
|
||||||
self.write('\t')
|
|
||||||
return
|
|
||||||
elif key_event.key is K['BACKSPACE']:
|
|
||||||
self.write_broadcast_text('\177')
|
|
||||||
self.write('\x08\x1b[X')
|
|
||||||
return
|
|
||||||
elif key_event.key is K['ENTER']:
|
|
||||||
self.write_broadcast_text('\r')
|
self.write_broadcast_text('\r')
|
||||||
self.print('')
|
self.print('')
|
||||||
|
self.line_edit.clear()
|
||||||
|
self.write(SAVE_CURSOR)
|
||||||
return
|
return
|
||||||
|
|
||||||
ek = encode_key_event(key_event)
|
ek = encode_key_event(key_event)
|
||||||
self.write_broadcast_data('kitty-key:' + ek)
|
self.write_broadcast_data('kitty-key:' + ek)
|
||||||
|
|
||||||
|
|||||||
@ -7,6 +7,8 @@ from typing import Callable, Tuple
|
|||||||
from kitty.fast_data_types import truncate_point_for_length, wcswidth
|
from kitty.fast_data_types import truncate_point_for_length, wcswidth
|
||||||
from kitty.key_encoding import RELEASE, KeyEvent, key_defs as K
|
from kitty.key_encoding import RELEASE, KeyEvent, key_defs as K
|
||||||
|
|
||||||
|
from .operations import RESTORE_CURSOR, SAVE_CURSOR, move_cursor_by
|
||||||
|
|
||||||
HOME = K['HOME']
|
HOME = K['HOME']
|
||||||
END = K['END']
|
END = K['END']
|
||||||
BACKSPACE = K['BACKSPACE']
|
BACKSPACE = K['BACKSPACE']
|
||||||
@ -31,13 +33,28 @@ class LineEdit:
|
|||||||
before, after = self.current_input[:x], self.current_input[x:]
|
before, after = self.current_input[:x], self.current_input[x:]
|
||||||
return before, after
|
return before, after
|
||||||
|
|
||||||
def write(self, write: Callable[[str], None], prompt: str = '') -> None:
|
def write(self, write: Callable[[str], None], prompt: str = '', screen_cols: int = 0) -> None:
|
||||||
if self.pending_bell:
|
if self.pending_bell:
|
||||||
write('\a')
|
write('\a')
|
||||||
self.pending_bell = False
|
self.pending_bell = False
|
||||||
write(prompt)
|
text = prompt + self.current_input
|
||||||
write(self.current_input)
|
cursor_pos = self.cursor_pos + wcswidth(prompt)
|
||||||
write('\r\x1b[{}C'.format(self.cursor_pos + wcswidth(prompt)))
|
if screen_cols:
|
||||||
|
write(SAVE_CURSOR + text + RESTORE_CURSOR)
|
||||||
|
used_lines, last_line_cursor_pos = divmod(cursor_pos, screen_cols)
|
||||||
|
if used_lines == 0:
|
||||||
|
if last_line_cursor_pos:
|
||||||
|
write(move_cursor_by(last_line_cursor_pos, 'right'))
|
||||||
|
else:
|
||||||
|
if used_lines:
|
||||||
|
write(move_cursor_by(used_lines, 'down'))
|
||||||
|
if last_line_cursor_pos:
|
||||||
|
write(move_cursor_by(last_line_cursor_pos, 'right'))
|
||||||
|
else:
|
||||||
|
write(text)
|
||||||
|
write('\r')
|
||||||
|
if cursor_pos:
|
||||||
|
write(move_cursor_by(cursor_pos, 'right'))
|
||||||
|
|
||||||
def add_text(self, text: str) -> None:
|
def add_text(self, text: str) -> None:
|
||||||
if self.current_input:
|
if self.current_input:
|
||||||
|
|||||||
@ -68,6 +68,11 @@ def clear_screen() -> str:
|
|||||||
return '\033[H\033[2J'
|
return '\033[H\033[2J'
|
||||||
|
|
||||||
|
|
||||||
|
@cmd
|
||||||
|
def clear_to_end_of_screen() -> str:
|
||||||
|
return '\033[J'
|
||||||
|
|
||||||
|
|
||||||
@cmd
|
@cmd
|
||||||
def clear_to_eol() -> str:
|
def clear_to_eol() -> str:
|
||||||
return '\033[K'
|
return '\033[K'
|
||||||
@ -108,6 +113,12 @@ def set_cursor_position(x: int, y: int) -> str: # (0, 0) is top left
|
|||||||
return '\033[{};{}H'.format(y + 1, x + 1)
|
return '\033[{};{}H'.format(y + 1, x + 1)
|
||||||
|
|
||||||
|
|
||||||
|
@cmd
|
||||||
|
def move_cursor_by(amt: int, direction: str) -> str:
|
||||||
|
suffix = {'up': 'A', 'down': 'B', 'right': 'C', 'left': 'D'}[direction]
|
||||||
|
return f'\033[{amt}{suffix}'
|
||||||
|
|
||||||
|
|
||||||
@cmd
|
@cmd
|
||||||
def set_cursor_shape(shape: str = 'block', blink: bool = True) -> str:
|
def set_cursor_shape(shape: str = 'block', blink: bool = True) -> str:
|
||||||
val = {'block': 1, 'underline': 3, 'bar': 5}.get(shape, 1)
|
val = {'block': 1, 'underline': 3, 'bar': 5}.get(shape, 1)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user