Implement drawing of titlebar, remembering of mode and fix various bugs in tui
This commit is contained in:
parent
4d92f4871a
commit
3363a9459b
@ -56,6 +56,8 @@ def raw_terminal(fd):
|
|||||||
|
|
||||||
|
|
||||||
def write_all(fd, data):
|
def write_all(fd, data):
|
||||||
|
if isinstance(data, str):
|
||||||
|
data = data.encode('utf-8')
|
||||||
while data:
|
while data:
|
||||||
n = os.write(fd, data)
|
n = os.write(fd, data)
|
||||||
if not n:
|
if not n:
|
||||||
@ -258,7 +260,7 @@ class Loop:
|
|||||||
del handler.write_buf[:consumed]
|
del handler.write_buf[:consumed]
|
||||||
|
|
||||||
def _wakeup_ready(self, handler):
|
def _wakeup_ready(self, handler):
|
||||||
data = os.read(self.wakeup_read_fd)
|
data = os.read(self.wakeup_read_fd, 1024)
|
||||||
if b'r' in data:
|
if b'r' in data:
|
||||||
screen_size.changed = True
|
screen_size.changed = True
|
||||||
handler.on_resize(screen_size())
|
handler.on_resize(screen_size())
|
||||||
|
|||||||
@ -6,11 +6,11 @@ from contextlib import contextmanager
|
|||||||
|
|
||||||
from kitty.terminfo import string_capabilities
|
from kitty.terminfo import string_capabilities
|
||||||
|
|
||||||
S7C1T = b'\033 F'
|
S7C1T = '\033 F'
|
||||||
SAVE_CURSOR = b'\0337'
|
SAVE_CURSOR = '\0337'
|
||||||
RESTORE_CURSOR = b'\0338'
|
RESTORE_CURSOR = '\0338'
|
||||||
SAVE_PRIVATE_MODE_VALUES = b'\033[?s'
|
SAVE_PRIVATE_MODE_VALUES = '\033[?s'
|
||||||
RESTORE_PRIVATE_MODE_VALUES = b'\033[?r'
|
RESTORE_PRIVATE_MODE_VALUES = '\033[?r'
|
||||||
|
|
||||||
MODES = dict(
|
MODES = dict(
|
||||||
LNM=(20, ''),
|
LNM=(20, ''),
|
||||||
@ -36,20 +36,20 @@ MODES = dict(
|
|||||||
|
|
||||||
def set_mode(which, private=True):
|
def set_mode(which, private=True):
|
||||||
num, private = MODES[which]
|
num, private = MODES[which]
|
||||||
return '\033[{}{}h'.format(private, num).encode('ascii')
|
return '\033[{}{}h'.format(private, num)
|
||||||
|
|
||||||
|
|
||||||
def reset_mode(which):
|
def reset_mode(which):
|
||||||
num, private = MODES[which]
|
num, private = MODES[which]
|
||||||
return '\033[{}{}l'.format(private, num).encode('ascii')
|
return '\033[{}{}l'.format(private, num)
|
||||||
|
|
||||||
|
|
||||||
def clear_screen():
|
def clear_screen():
|
||||||
return string_capabilities['clear'].replace(r'\E', '\033').encode('ascii')
|
return string_capabilities['clear'].replace(r'\E', '\033')
|
||||||
|
|
||||||
|
|
||||||
def set_window_title(value):
|
def set_window_title(value):
|
||||||
return ('\033]2;' + value.replace('\033', '').replace('\x9c', '') + '\033\\').encode('utf-8')
|
return ('\033]2;' + value.replace('\033', '').replace('\x9c', '') + '\033\\')
|
||||||
|
|
||||||
|
|
||||||
def set_line_wrapping(yes_or_no):
|
def set_line_wrapping(yes_or_no):
|
||||||
@ -77,7 +77,7 @@ def colored(text, color, intense=False):
|
|||||||
return '\033[{}m{}\033[39m'.format(e, text)
|
return '\033[{}m{}\033[39m'.format(e, text)
|
||||||
|
|
||||||
|
|
||||||
def styled(text, fg=None, bg=None, fg_intense=False, bg_intense=False, italic=False, bold=False, underline=None, underline_color=None):
|
def styled(text, fg=None, bg=None, fg_intense=False, bg_intense=False, italic=None, bold=None, underline=None, underline_color=None, reverse=None):
|
||||||
start, end = [], []
|
start, end = [], []
|
||||||
if fg is not None:
|
if fg is not None:
|
||||||
start.append(_color(fg, fg_intense))
|
start.append(_color(fg, fg_intense))
|
||||||
@ -93,10 +93,15 @@ def styled(text, fg=None, bg=None, fg_intense=False, bg_intense=False, italic=Fa
|
|||||||
if underline is not None:
|
if underline is not None:
|
||||||
start.append('4:{}'.format(UNDERLINE_STYLES[underline]))
|
start.append('4:{}'.format(UNDERLINE_STYLES[underline]))
|
||||||
end.append('4:0')
|
end.append('4:0')
|
||||||
if italic:
|
if italic is not None:
|
||||||
start.append('3'), end.append('23')
|
s, e = (start, end) if italic else (end, start)
|
||||||
if bold:
|
s.append('3'), e.append('23')
|
||||||
start.append('1'), end.append('21')
|
if bold is not None:
|
||||||
|
s, e = (start, end) if bold else (end, start)
|
||||||
|
s.append('1'), e.append('22')
|
||||||
|
if reverse is not None:
|
||||||
|
s, e = (start, end) if reverse else (end, start)
|
||||||
|
s.append('7'), e.append('27')
|
||||||
if not start:
|
if not start:
|
||||||
return text
|
return text
|
||||||
return '\033[{}m{}\033[{}m'.format(';'.join(start), text, ';'.join(end))
|
return '\033[{}m{}\033[{}m'.format(';'.join(start), text, ';'.join(end))
|
||||||
@ -111,7 +116,8 @@ def init_state(alternate_screen=True):
|
|||||||
reset_mode('MOUSE_MOTION_TRACKING') + reset_mode('MOUSE_MOVE_TRACKING')
|
reset_mode('MOUSE_MOTION_TRACKING') + reset_mode('MOUSE_MOVE_TRACKING')
|
||||||
+ reset_mode('FOCUS_TRACKING') + reset_mode('MOUSE_UTF8_MODE') +
|
+ reset_mode('FOCUS_TRACKING') + reset_mode('MOUSE_UTF8_MODE') +
|
||||||
reset_mode('MOUSE_SGR_MODE') + reset_mode('MOUSE_UTF8_MODE') +
|
reset_mode('MOUSE_SGR_MODE') + reset_mode('MOUSE_UTF8_MODE') +
|
||||||
set_mode('BRACKETED_PASTE') + set_mode('EXTENDED_KEYBOARD')
|
set_mode('BRACKETED_PASTE') + set_mode('EXTENDED_KEYBOARD') +
|
||||||
|
'\033[*x' # reset DECSACE to default region select
|
||||||
)
|
)
|
||||||
if alternate_screen:
|
if alternate_screen:
|
||||||
ans += set_mode('ALTERNATE_SCREEN')
|
ans += set_mode('ALTERNATE_SCREEN')
|
||||||
@ -120,7 +126,7 @@ def init_state(alternate_screen=True):
|
|||||||
|
|
||||||
|
|
||||||
def reset_state(normal_screen=True):
|
def reset_state(normal_screen=True):
|
||||||
ans = b''
|
ans = ''
|
||||||
if normal_screen:
|
if normal_screen:
|
||||||
ans += reset_mode('ALTERNATE_SCREEN')
|
ans += reset_mode('ALTERNATE_SCREEN')
|
||||||
ans += RESTORE_PRIVATE_MODE_VALUES
|
ans += RESTORE_PRIVATE_MODE_VALUES
|
||||||
|
|||||||
@ -6,8 +6,11 @@ import sys
|
|||||||
from functools import lru_cache
|
from functools import lru_cache
|
||||||
from gettext import gettext as _
|
from gettext import gettext as _
|
||||||
|
|
||||||
|
from kitty.config import cached_values_for
|
||||||
from kitty.fast_data_types import wcswidth
|
from kitty.fast_data_types import wcswidth
|
||||||
from kitty.key_encoding import ESCAPE, backspace_key, enter_key
|
from kitty.key_encoding import (
|
||||||
|
ESCAPE, F1, F2, RELEASE, backspace_key, enter_key
|
||||||
|
)
|
||||||
|
|
||||||
from ..tui.handler import Handler
|
from ..tui.handler import Handler
|
||||||
from ..tui.loop import Loop
|
from ..tui.loop import Loop
|
||||||
@ -15,7 +18,7 @@ from ..tui.operations import (
|
|||||||
clear_screen, colored, cursor, set_line_wrapping, set_window_title, styled
|
clear_screen, colored, cursor, set_line_wrapping, set_window_title, styled
|
||||||
)
|
)
|
||||||
|
|
||||||
HEX, NAME = 0, 1
|
HEX, NAME = 'HEX', 'NAME'
|
||||||
|
|
||||||
|
|
||||||
@lru_cache()
|
@lru_cache()
|
||||||
@ -37,19 +40,20 @@ FAINT = 242
|
|||||||
|
|
||||||
class UnicodeInput(Handler):
|
class UnicodeInput(Handler):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, cached_values):
|
||||||
|
self.cached_values = cached_values
|
||||||
self.current_input = ''
|
self.current_input = ''
|
||||||
self.current_char = None
|
self.current_char = None
|
||||||
self.prompt_template = '{}> '
|
self.prompt_template = '{}> '
|
||||||
self.choice_line = ''
|
self.choice_line = ''
|
||||||
self.mode = HEX
|
self.mode = globals().get(cached_values.get('mode', 'HEX'), 'HEX')
|
||||||
self.update_prompt()
|
self.update_prompt()
|
||||||
|
|
||||||
def update_current_char(self):
|
def update_current_char(self):
|
||||||
if self.mode is HEX:
|
if self.mode is HEX:
|
||||||
try:
|
try:
|
||||||
code = int(self.current_input, 16)
|
code = int(self.current_input, 16)
|
||||||
if code <= 32 or code == 127 or 128 <= code <= 159:
|
if code <= 32 or code == 127 or 128 <= code <= 159 or 0xd800 <= code <= 0xdbff or 0xDC00 <= code <= 0xDFFF:
|
||||||
self.current_char = None
|
self.current_char = None
|
||||||
else:
|
else:
|
||||||
self.current_char = chr(code)
|
self.current_char = chr(code)
|
||||||
@ -88,10 +92,26 @@ class UnicodeInput(Handler):
|
|||||||
self.write(set_window_title(_('Unicode input')))
|
self.write(set_window_title(_('Unicode input')))
|
||||||
self.draw_screen()
|
self.draw_screen()
|
||||||
|
|
||||||
|
def draw_title_bar(self):
|
||||||
|
entries = []
|
||||||
|
for name, key, mode in [
|
||||||
|
(_('Code'), 'F1', HEX),
|
||||||
|
(_('Name'), 'F2', NAME),
|
||||||
|
]:
|
||||||
|
entry = ' {} ({}) '.format(name, key)
|
||||||
|
if mode is self.mode:
|
||||||
|
entry = styled(entry, reverse=False, bold=True)
|
||||||
|
entries.append(entry)
|
||||||
|
text = _('Search by:{}').format(' '.join(entries))
|
||||||
|
extra = self.screen_size.cols - wcswidth(text)
|
||||||
|
if extra > 0:
|
||||||
|
text += ' ' * extra
|
||||||
|
self.print(styled(text, reverse=True))
|
||||||
|
|
||||||
def draw_screen(self):
|
def draw_screen(self):
|
||||||
self.write(clear_screen())
|
self.write(clear_screen())
|
||||||
|
self.draw_title_bar()
|
||||||
if self.mode is HEX:
|
if self.mode is HEX:
|
||||||
self.print(styled(_('Press the / key to search by character name'), fg=FAINT, italic=True))
|
|
||||||
self.print(_('Enter the hex code for the character'))
|
self.print(_('Enter the hex code for the character'))
|
||||||
else:
|
else:
|
||||||
self.print(_('Enter words from the name of the character'))
|
self.print(_('Enter words from the name of the character'))
|
||||||
@ -108,10 +128,7 @@ class UnicodeInput(Handler):
|
|||||||
self.draw_screen()
|
self.draw_screen()
|
||||||
|
|
||||||
def on_text(self, text, in_bracketed_paste):
|
def on_text(self, text, in_bracketed_paste):
|
||||||
if self.mode is HEX and text == '/':
|
self.current_input += text
|
||||||
self.mode = NAME
|
|
||||||
else:
|
|
||||||
self.current_input += text
|
|
||||||
self.refresh()
|
self.refresh()
|
||||||
|
|
||||||
def on_key(self, key_event):
|
def on_key(self, key_event):
|
||||||
@ -120,9 +137,22 @@ class UnicodeInput(Handler):
|
|||||||
self.refresh()
|
self.refresh()
|
||||||
elif key_event is enter_key:
|
elif key_event is enter_key:
|
||||||
self.quit_loop(0)
|
self.quit_loop(0)
|
||||||
else:
|
elif key_event.type is RELEASE:
|
||||||
if key_event.key is ESCAPE:
|
if key_event.key is ESCAPE:
|
||||||
self.quit_loop(1)
|
self.quit_loop(1)
|
||||||
|
elif key_event.key is F1:
|
||||||
|
self.switch_mode(HEX)
|
||||||
|
elif key_event.key is F2:
|
||||||
|
self.switch_mode(NAME)
|
||||||
|
|
||||||
|
def switch_mode(self, mode):
|
||||||
|
if mode is not self.mode:
|
||||||
|
self.mode = mode
|
||||||
|
self.cached_values['mode'] = mode
|
||||||
|
self.current_input = ''
|
||||||
|
self.current_char = None
|
||||||
|
self.choice_line = ''
|
||||||
|
self.refresh()
|
||||||
|
|
||||||
def on_interrupt(self):
|
def on_interrupt(self):
|
||||||
self.quit_loop(1)
|
self.quit_loop(1)
|
||||||
@ -132,13 +162,14 @@ class UnicodeInput(Handler):
|
|||||||
|
|
||||||
def on_resize(self, new_size):
|
def on_resize(self, new_size):
|
||||||
Handler.on_resize(self, new_size)
|
Handler.on_resize(self, new_size)
|
||||||
self.draw_screen()
|
self.refresh()
|
||||||
|
|
||||||
|
|
||||||
def main(args=sys.argv):
|
def main(args=sys.argv):
|
||||||
loop = Loop()
|
loop = Loop()
|
||||||
handler = UnicodeInput()
|
with cached_values_for('unicode-input') as cached_values:
|
||||||
loop.loop(handler)
|
handler = UnicodeInput(cached_values)
|
||||||
|
loop.loop(handler)
|
||||||
if handler.current_char and loop.return_code == 0:
|
if handler.current_char and loop.return_code == 0:
|
||||||
print('OK:', hex(ord(handler.current_char))[2:])
|
print('OK:', hex(ord(handler.current_char))[2:])
|
||||||
raise SystemExit(loop.return_code)
|
raise SystemExit(loop.return_code)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user