From 3363a9459b6356540d7384082ab51066569ac3a9 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 10 Feb 2018 12:57:57 +0530 Subject: [PATCH] Implement drawing of titlebar, remembering of mode and fix various bugs in tui --- kittens/tui/loop.py | 4 ++- kittens/tui/operations.py | 38 ++++++++++++---------- kittens/unicode_input/main.py | 59 ++++++++++++++++++++++++++--------- 3 files changed, 70 insertions(+), 31 deletions(-) diff --git a/kittens/tui/loop.py b/kittens/tui/loop.py index e048bcdf7..434e476c3 100644 --- a/kittens/tui/loop.py +++ b/kittens/tui/loop.py @@ -56,6 +56,8 @@ def raw_terminal(fd): def write_all(fd, data): + if isinstance(data, str): + data = data.encode('utf-8') while data: n = os.write(fd, data) if not n: @@ -258,7 +260,7 @@ class Loop: del handler.write_buf[:consumed] 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: screen_size.changed = True handler.on_resize(screen_size()) diff --git a/kittens/tui/operations.py b/kittens/tui/operations.py index 6f246ea91..7e4215e6e 100644 --- a/kittens/tui/operations.py +++ b/kittens/tui/operations.py @@ -6,11 +6,11 @@ from contextlib import contextmanager from kitty.terminfo import string_capabilities -S7C1T = b'\033 F' -SAVE_CURSOR = b'\0337' -RESTORE_CURSOR = b'\0338' -SAVE_PRIVATE_MODE_VALUES = b'\033[?s' -RESTORE_PRIVATE_MODE_VALUES = b'\033[?r' +S7C1T = '\033 F' +SAVE_CURSOR = '\0337' +RESTORE_CURSOR = '\0338' +SAVE_PRIVATE_MODE_VALUES = '\033[?s' +RESTORE_PRIVATE_MODE_VALUES = '\033[?r' MODES = dict( LNM=(20, ''), @@ -36,20 +36,20 @@ MODES = dict( def set_mode(which, private=True): num, private = MODES[which] - return '\033[{}{}h'.format(private, num).encode('ascii') + return '\033[{}{}h'.format(private, num) def reset_mode(which): num, private = MODES[which] - return '\033[{}{}l'.format(private, num).encode('ascii') + return '\033[{}{}l'.format(private, num) 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): - 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): @@ -77,7 +77,7 @@ def colored(text, color, intense=False): 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 = [], [] if fg is not None: 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: start.append('4:{}'.format(UNDERLINE_STYLES[underline])) end.append('4:0') - if italic: - start.append('3'), end.append('23') - if bold: - start.append('1'), end.append('21') + if italic is not None: + s, e = (start, end) if italic else (end, start) + s.append('3'), e.append('23') + 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: return text 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('FOCUS_TRACKING') + 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: ans += set_mode('ALTERNATE_SCREEN') @@ -120,7 +126,7 @@ def init_state(alternate_screen=True): def reset_state(normal_screen=True): - ans = b'' + ans = '' if normal_screen: ans += reset_mode('ALTERNATE_SCREEN') ans += RESTORE_PRIVATE_MODE_VALUES diff --git a/kittens/unicode_input/main.py b/kittens/unicode_input/main.py index cad49afba..15f401f9a 100644 --- a/kittens/unicode_input/main.py +++ b/kittens/unicode_input/main.py @@ -6,8 +6,11 @@ import sys from functools import lru_cache from gettext import gettext as _ +from kitty.config import cached_values_for 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.loop import Loop @@ -15,7 +18,7 @@ from ..tui.operations import ( clear_screen, colored, cursor, set_line_wrapping, set_window_title, styled ) -HEX, NAME = 0, 1 +HEX, NAME = 'HEX', 'NAME' @lru_cache() @@ -37,19 +40,20 @@ FAINT = 242 class UnicodeInput(Handler): - def __init__(self): + def __init__(self, cached_values): + self.cached_values = cached_values self.current_input = '' self.current_char = None self.prompt_template = '{}> ' self.choice_line = '' - self.mode = HEX + self.mode = globals().get(cached_values.get('mode', 'HEX'), 'HEX') self.update_prompt() def update_current_char(self): if self.mode is HEX: try: 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 else: self.current_char = chr(code) @@ -88,10 +92,26 @@ class UnicodeInput(Handler): self.write(set_window_title(_('Unicode input'))) 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): self.write(clear_screen()) + self.draw_title_bar() 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')) else: self.print(_('Enter words from the name of the character')) @@ -108,10 +128,7 @@ class UnicodeInput(Handler): self.draw_screen() def on_text(self, text, in_bracketed_paste): - if self.mode is HEX and text == '/': - self.mode = NAME - else: - self.current_input += text + self.current_input += text self.refresh() def on_key(self, key_event): @@ -120,9 +137,22 @@ class UnicodeInput(Handler): self.refresh() elif key_event is enter_key: self.quit_loop(0) - else: + elif key_event.type is RELEASE: if key_event.key is ESCAPE: 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): self.quit_loop(1) @@ -132,13 +162,14 @@ class UnicodeInput(Handler): def on_resize(self, new_size): Handler.on_resize(self, new_size) - self.draw_screen() + self.refresh() def main(args=sys.argv): loop = Loop() - handler = UnicodeInput() - loop.loop(handler) + with cached_values_for('unicode-input') as cached_values: + handler = UnicodeInput(cached_values) + loop.loop(handler) if handler.current_char and loop.return_code == 0: print('OK:', hex(ord(handler.current_char))[2:]) raise SystemExit(loop.return_code)