-
Notifications
You must be signed in to change notification settings - Fork 747
Description
One downside of turning my script into a prompt_toolkit Application
is that I can no longer just write to stdout. How would I implement something like a scrolling log with line-wrapping using widgets?
I would like an area that I can print to, with simple line-wrapping, and the ability to scroll while text is being printed.
What I've tried:
Label
I first tried to use a label since I don't need the text to be user-editable. I thought I could simply append the text I wanted to print at the end of the current text, but I couldn't figure out how to update the label text and have it update visually.
TextArea or Buffer + BufferControl + Window
From what I can tell a buffer wants to be used to insert text at a cursor position which must be visible on the screen. TextArea works the same way. That means that whenever I print text, I need to store the current scroll position, move to the bottom and insert text, then restore the previous position. I could not get scrolling to work with wrap_lines = True
for the life of me. #686 may be related. I had to manually wrap lines by checking the terminal width and inserting \n
.
async def write(self, text: str):
ri = self.output.render_info
at_bottom = True
if ri:
at_bottom = (ri.vertical_scroll + ri.window_height >= ri.ui_content.line_count)
old_cursor = self.output_buffer.cursor_position
try:
self.output_buffer.cursor_position = len(self.output_buffer.text)
free_space = ri.window_width - ri.cursor_position.x - 1
to_print = ''
while text != '':
to_print += text[0]
free_space = ri.window_width if text[0] == '\n' else free_space - 1
text = text[1:]
if free_space < 1:
text = '\n' + text
self.output_buffer.insert_text(to_print)
finally:
pass
if not at_bottom:
self.output_buffer.cursor_position = old_cursor
self.app.invalidate()
This works, but it's fragile, ugly, and breaks when the user resizes their terminal.