def edit_multiline(default_text=""): kb = KeyBindings() @kb.add('c-q') @kb.add('escape', 'enter') def exit_(event): """ Pressing Ctrl-Q, Alt+Enter or Esc + Enter will exit the editor. """ event.app.exit(textf.text) @kb.add('c-c') def do_copy(event): data = textf.buffer.copy_selection() get_app().clipboard.set_data(data) @kb.add('c-x', eager=True) def do_cut(event): data = textf.buffer.cut_selection() get_app().clipboard.set_data(data) @kb.add('c-z') def do_undo(event): textf.buffer.undo() @kb.add('c-y') def do_redo(event): textf.buffer.redo() @kb.add('c-a') def do_select_all(event): textf.buffer.cursor_position = 0 textf.buffer.start_selection() textf.buffer.cursor_position = len(textf.buffer.text) update_stored_pos(None) @kb.add('c-v') def do_paste(event): textf.buffer.paste_clipboard_data(get_app().clipboard.get_data()) @kb.add('left') def kb_left(event): textf.buffer.selection_state = None if textf.buffer.cursor_position != 0 and textf.text[ textf.buffer.cursor_position - 1] == '\n': textf.buffer.cursor_up() textf.buffer.cursor_right(len(textf.text)) else: textf.buffer.cursor_left() update_stored_pos(None) @kb.add('right') def kb_right(event): textf.buffer.selection_state = None if textf.buffer.cursor_position < len(textf.text) and textf.text[ textf.buffer.cursor_position] == '\n': textf.buffer.cursor_down() textf.buffer.cursor_left(len(textf.text)) else: textf.buffer.cursor_right() update_stored_pos(None) @kb.add('home') def kb_home(event): textf.buffer.selection_state = None width = getTermWidth() doc = textf.document if textf.buffer.cursor_position == doc._line_start_indexes[ cursor_row()] + int(cursor_col() / width) * width: textf.buffer.cursor_position = doc._line_start_indexes[ cursor_row()] else: textf.buffer.cursor_position = doc._line_start_indexes[ cursor_row()] + int(cursor_col() / width) * width update_stored_pos(None) @kb.add('end') def kb_end(event): textf.buffer.selection_state = None width = getTermWidth() doc = textf.document row = cursor_row() if textf.buffer.cursor_position == doc._line_start_indexes[row] + ( int(cursor_col() / width) + 1) * width - 1: textf.buffer.cursor_position = doc._line_start_indexes[row] + len( doc.current_line) else: textf.buffer.cursor_position = min( doc._line_start_indexes[row] + (int(cursor_col() / width) + 1) * width - 1, doc._line_start_indexes[row] + len(doc.current_line)) update_stored_pos(None) @kb.add('up') def kb_up(event): textf.freezestore = True width = getTermWidth() doc = textf.document textf.buffer.selection_state = None col = cursor_col() row = cursor_row() if width > 9000: # A failsafe in case the terminal size is incorrectly detected textf.buffer.cursor_up() return if col >= width: # Move one row up staying on the same line textf.buffer.cursor_position = doc._line_start_indexes[row] + int( col / width - 1) * width + textf.stored_cursor_pos elif row >= 1: # Moving up to a different line prevlinelen = len(doc.lines[row - 1]) textf.buffer.cursor_position = min( doc._line_start_indexes[row] - 1, doc._line_start_indexes[row - 1] + int(prevlinelen / width) * width + textf.stored_cursor_pos) else: # Cursor is on the first row of first line textf.buffer.cursor_position = 0 textf.freezestore = False update_stored_pos(None) @kb.add('down') def kb_down(event): textf.freezestore = True width = getTermWidth() doc = textf.document textf.buffer.selection_state = None col = cursor_col() row = cursor_row() nextlinelen = len(doc.lines[row + 1]) if row < len(doc.lines) - 1 else -1 if width > 9000: # A failsafe in case the terminal size is incorrectly detected textf.buffer.cursor_down() return if col <= len(doc.current_line ) - width: # Move one row down staying on the same line textf.buffer.cursor_position = doc._line_start_indexes[row] + int( col / width + 1) * width + textf.stored_cursor_pos elif nextlinelen < 0: # Move to the very end textf.buffer.cursor_position = len(textf.text) textf.freezestore = False update_stored_pos(None) # Move to the end of the same line the cursor is on elif col != len(doc.lines[row]) and textf.stored_cursor_pos >= len( doc.lines[row]) - int(len(doc.lines[row]) / width) * width: textf.buffer.cursor_position = doc._line_start_indexes[row + 1] - 1 else: # Move to a different line textf.buffer.cursor_position = min( doc._line_start_indexes[row + 1] + nextlinelen, doc._line_start_indexes[row + 1] + textf.stored_cursor_pos) textf = TextArea() bottom_bar_text = FormattedTextControl( text= '\nCurrently editing. Press Ctrl+Q, Alt+Enter or Esc + Enter to exit.') bottom_bar = Window(content=bottom_bar_text) root_container = HSplit([ textf, bottom_bar, ]) layout = Layout(root_container) app = Application(key_bindings=kb, layout=layout, enable_page_navigation_bindings=True, full_screen=False) textf.freezestore = False textf.text = default_text textf.buffer.cursor_position = len(textf.buffer.text) # Find the row the cursor is at # My own function, in fear of race conditions def cursor_row(): i = 0 while i < len( textf.document._line_start_indexes ) and textf.buffer.cursor_position >= textf.document._line_start_indexes[ i]: i += 1 return i - 1 # Find the column the cursor is at # There is a built-in function, but I think there's some kind of a race condition if it's used def cursor_col(): i = textf.buffer.cursor_position - 1 while i >= 0 and textf.text[i] != '\n': i -= 1 return textf.buffer.cursor_position - i - 1 def update_stored_pos(event): if not event: textf.freezestore = False if textf.freezestore: textf.freezestore = False return width = getTermWidth() col = cursor_col() textf.stored_cursor_pos = col - int(col / width) * width textf.buffer.on_cursor_position_changed += update_stored_pos update_stored_pos(None) text = app.run() clear_lines(1) return text
password=True) conf_prompt_base_url = Window(FormattedTextControl('SlashNext Base URL'), height=1, align=WindowAlign.LEFT, style="class:conf_prompt") conf_input_base_url = TextArea(height=1, prompt='', multiline=False, wrap_lines=False, style="class:conf_input", focusable=True, focus_on_click=True) conf_input_base_url.text = 'https://oti.slashnext.cloud/api' conf_status = TextArea(height=5, multiline=True, wrap_lines=True, style="class:conf_notice") separator = Window(height=1, width=1, align=WindowAlign.CENTER, style="class:conf_prompt") terminator = Window(align=WindowAlign.CENTER, style="class:conf_prompt") line = Window(height=1, char="─", style="class:line")