def bottom_toolbar(): media_player = player.get_media_player() media = media_player.get_media() media.parse() media_time = seconds_to_human(round(media_player.get_time() / 1000)) media_length = seconds_to_human(round(media_player.get_length() / 1000)) artist = media.get_meta(vlc.Meta.Artist) album = media.get_meta(vlc.Meta.Album) title = media.get_meta(vlc.Meta.Title) current = '({} / {}) {} - {} - {}'.format(media_time, media_length, artist, album, title) get_app().invalidate() return HTML('Current song: {}'.format(current))
def get_text_fragments(): python_buffer = python_input.default_buffer result = [] append = result.append append((TB, ' ')) result.extend(get_inputmode_fragments(python_input)) append((TB, ' ')) # Position in history. append((TB, '%i/%i ' % (python_buffer.working_index + 1, len(python_buffer._working_lines)))) # Shortcuts. app = get_app() if not python_input.vi_mode and app.current_buffer == python_input.search_buffer: append((TB, '[Ctrl-G] Cancel search [Enter] Go to this position.')) elif bool(app.current_buffer.selection_state) and not python_input.vi_mode: # Emacs cut/copy keys. append((TB, '[Ctrl-W] Cut [Meta-W] Copy [Ctrl-Y] Paste [Ctrl-G] Cancel')) else: result.extend([ (TB + ' class:key', '[F3]', enter_history), (TB, ' History ', enter_history), (TB + ' class:key', '[F6]', toggle_paste_mode), (TB, ' ', toggle_paste_mode), ]) if python_input.paste_mode: append((TB + ' class:paste-mode-on', 'Paste mode (on)', toggle_paste_mode)) else: append((TB, 'Paste mode', toggle_paste_mode)) return result
def _get_vi_mode(): return { InputMode.INSERT: 'I', InputMode.NAVIGATION: 'N', InputMode.REPLACE: 'R', InputMode.INSERT_MULTIPLE: 'M', }[get_app().vi_state.input_mode]
def _get_vi_mode(): """Get the current vi mode for display.""" return { InputMode.INSERT: 'I', InputMode.NAVIGATION: 'N', InputMode.REPLACE: 'R', InputMode.INSERT_MULTIPLE: 'M', }[get_app().vi_state.input_mode]
def whitespace_before_cursor_on_line(): """ Filter which evaluates to True when the characters before the cursor are whitespace, or we are at the start of te line. """ b = get_app().current_buffer before_cursor = b.document.current_line_before_cursor return bool(not before_cursor or before_cursor[-1].isspace())
def get_inputmode_fragments(python_input): """ Return current input mode as a list of (token, text) tuples for use in a toolbar. """ app = get_app() @if_mousedown def toggle_vi_mode(mouse_event): python_input.vi_mode = not python_input.vi_mode token = 'class:status-toolbar' input_mode_t = 'class:status-toolbar.input-mode' mode = app.vi_state.input_mode result = [] append = result.append append((input_mode_t, '[F4] ', toggle_vi_mode)) # InputMode if python_input.vi_mode: recording_register = app.vi_state.recording_register if recording_register: append((token, ' ')) append((token + ' class:record', 'RECORD({})'.format(recording_register))) append((token, ' - ')) if bool(app.current_buffer.selection_state): if app.current_buffer.selection_state.type == SelectionType.LINES: append((input_mode_t, 'Vi (VISUAL LINE)', toggle_vi_mode)) elif app.current_buffer.selection_state.type == SelectionType.CHARACTERS: append((input_mode_t, 'Vi (VISUAL)', toggle_vi_mode)) append((token, ' ')) elif app.current_buffer.selection_state.type == 'BLOCK': append((input_mode_t, 'Vi (VISUAL BLOCK)', toggle_vi_mode)) append((token, ' ')) elif mode in (InputMode.INSERT, 'vi-insert-multiple'): append((input_mode_t, 'Vi (INSERT)', toggle_vi_mode)) append((token, ' ')) elif mode == InputMode.NAVIGATION: append((input_mode_t, 'Vi (NAV)', toggle_vi_mode)) append((token, ' ')) elif mode == InputMode.REPLACE: append((input_mode_t, 'Vi (REPLACE)', toggle_vi_mode)) append((token, ' ')) else: if app.emacs_state.is_recording: append((token, ' ')) append((token + ' class:record', 'RECORD')) append((token, ' - ')) append((input_mode_t, 'Emacs', toggle_vi_mode)) append((token, ' ')) return result
def tab_should_insert_whitespace(): """ When the 'tab' key is pressed with only whitespace character before the cursor, do autocompletion. Otherwise, insert indentation. Except for the first character at the first line. Then always do a completion. It doesn't make sense to start the first line with indentation. """ b = get_app().current_buffer before_cursor = b.document.current_line_before_cursor return bool(b.text and (not before_cursor or before_cursor.isspace()))
def create_python_input_window(): def menu_position(): """ When there is no autocompletion menu to be shown, and we have a signature, set the pop-up position at `bracket_start`. """ b = python_input.default_buffer if b.complete_state is None and python_input.signatures: row, col = python_input.signatures[0].bracket_start index = b.document.translate_row_col_to_index(row - 1, col) return index return Window( BufferControl( buffer=python_input.default_buffer, search_buffer_control=search_toolbar.control, lexer=lexer, include_default_input_processors=False, input_processors=[ ConditionalProcessor( processor=HighlightIncrementalSearchProcessor(), filter=has_focus(SEARCH_BUFFER) | has_focus(search_toolbar.control), ), HighlightSelectionProcessor(), DisplayMultipleCursors(), # Show matching parentheses, but only while editing. ConditionalProcessor( processor=HighlightMatchingBracketProcessor(chars='[](){}'), filter=has_focus(DEFAULT_BUFFER) & ~is_done & Condition(lambda: python_input.highlight_matching_parenthesis)), ConditionalProcessor( processor=AppendAutoSuggestion(), filter=~is_done) ] + extra_buffer_processors, menu_position=menu_position, # Make sure that we always see the result of an reverse-i-search: preview_search=True, ), left_margins=[PythonPromptMargin(python_input)], # Scroll offsets. The 1 at the bottom is important to make sure # the cursor is never below the "Press [Meta+Enter]" message # which is a float. scroll_offsets=ScrollOffsets(bottom=1, left=4, right=4), # As long as we're editing, prefer a minimal height of 6. height=(lambda: ( None if get_app().is_done or python_input.show_exit_confirmation else input_buffer_height)), wrap_lines=Condition(lambda: python_input.wrap_lines), )
def enter_history(self): """ Display the history. """ app = get_app() app.vi_state.input_mode = InputMode.NAVIGATION def done(f): result = f.result() if result is not None: self.default_buffer.text = result app.vi_state.input_mode = InputMode.INSERT history = History(self, self.default_buffer.document) future = run_coroutine_in_terminal(history.app.run_async) future.add_done_callback(done)
def enter_history(self) -> None: """ Display the history. """ app = get_app() app.vi_state.input_mode = InputMode.NAVIGATION history = PythonHistory(self, self.default_buffer.document) import asyncio from prompt_toolkit.application import in_terminal async def do_in_terminal() -> None: async with in_terminal(): result = await history.app.run_async() if result is not None: self.default_buffer.text = result app.vi_state.input_mode = InputMode.INSERT asyncio.ensure_future(do_in_terminal())
def cond(): if not pgcli.multi_line: _logger.debug("Not in multi-line mode. Handle the buffer.") return True if pgcli.multiline_mode == "safe": _logger.debug( "Multi-line mode is set to 'safe'. Do NOT handle the buffer.") return False doc = get_app().layout.get_buffer_by_name(DEFAULT_BUFFER).document text = doc.text.strip() return (text.startswith("\\") # Special Command or text.endswith(r"\e") # Special Command or text.endswith( r"\G") # Ended with \e which should launch the editor or _is_complete(text) # A complete SQL command or (text == "exit") # Exit doesn't need semi-colon or (text == "quit") # Quit doesn't need semi-colon or (text == ":q") # To all the vim fans out there or (text == "") # Just a plain enter without any text )
def get_info_app(app: Flask): left_part = HTML("<left-part>" " <import_name>%s</import_name> " "<path>%s</path>" "</left-part>") % (app.import_name, app.root_path) right_part = HTML("<right-part> " " <env>%s</env> " " <time>%s</time> " "</right-part>") % ( os.environ["ZEMFROG_ENV"], datetime.datetime.now().isoformat(), ) used_width = sum([ fragment_list_width(to_formatted_text(left_part)), fragment_list_width(to_formatted_text(right_part)), ]) total_width = get_app().output.get_size().columns padding_size = total_width - used_width padding = HTML("<padding>%s</padding>") % (" " * padding_size, ) return left_part, padding, right_part, "\n"
def _quit_interactive(event) -> None: # Quit Interactive CLI when user presses [CTRL + D] get_app().exit()
def exit_app(): """Exit the app and save any state.""" get_app().exit()
def set_current_sidebar(menu): global _current_menu _current_menu = menu if menu.has_focusable_items: get_app().layout.focus(menu)
def handle(self, words=[]): get_app().exit(0) return True
def copy_to_clipboard(text): get_app().clipboard.set_text(text)
def vi_buffer_focussed(): app = get_app() if app.layout.has_focus(editor.search_buffer) or app.layout.has_focus(editor.command_buffer): return False return True
def load_python_bindings(python_input): """ Custom key bindings. """ bindings = KeyBindings() sidebar_visible = Condition(lambda: python_input.show_sidebar) handle = bindings.add @handle('c-l') def _(event): """ Clear whole screen and render again -- also when the sidebar is visible. """ event.app.renderer.clear() @handle('c-z') def _(event): """ Suspend. """ if python_input.enable_system_bindings: event.app.suspend_to_background() @handle('f2') def _(event): """ Show/hide sidebar. """ python_input.show_sidebar = not python_input.show_sidebar if python_input.show_sidebar: event.app.layout.focus(python_input.ptpython_layout.sidebar) else: event.app.layout.focus_last() @handle('f3') def _(event): """ Select from the history. """ python_input.enter_history() @handle('f4') def _(event): """ Toggle between Vi and Emacs mode. """ python_input.vi_mode = not python_input.vi_mode @handle('f6') def _(event): """ Enable/Disable paste mode. """ python_input.paste_mode = not python_input.paste_mode @handle('tab', filter= ~sidebar_visible & ~has_selection & tab_should_insert_whitespace) def _(event): """ When tab should insert whitespace, do that instead of completion. """ event.app.current_buffer.insert_text(' ') @Condition def is_multiline(): return document_is_multiline_python(python_input.default_buffer.document) @handle('enter', filter= ~sidebar_visible & ~has_selection & (vi_insert_mode | emacs_insert_mode) & has_focus(DEFAULT_BUFFER) & ~is_multiline) @handle(Keys.Escape, Keys.Enter, filter= ~sidebar_visible & emacs_mode) def _(event): """ Accept input (for single line input). """ b = event.current_buffer if b.validate(): # When the cursor is at the end, and we have an empty line: # drop the empty lines, but return the value. b.document = Document( text=b.text.rstrip(), cursor_position=len(b.text.rstrip())) b.validate_and_handle() @handle('enter', filter= ~sidebar_visible & ~has_selection & (vi_insert_mode | emacs_insert_mode) & has_focus(DEFAULT_BUFFER) & is_multiline) def _(event): """ Behaviour of the Enter key. Auto indent after newline/Enter. (When not in Vi navigaton mode, and when multiline is enabled.) """ b = event.current_buffer empty_lines_required = python_input.accept_input_on_enter or 10000 def at_the_end(b): """ we consider the cursor at the end when there is no text after the cursor, or only whitespace. """ text = b.document.text_after_cursor return text == '' or (text.isspace() and not '\n' in text) if python_input.paste_mode: # In paste mode, always insert text. b.insert_text('\n') elif at_the_end(b) and b.document.text.replace(' ', '').endswith( '\n' * (empty_lines_required - 1)): # When the cursor is at the end, and we have an empty line: # drop the empty lines, but return the value. if b.validate(): b.document = Document( text=b.text.rstrip(), cursor_position=len(b.text.rstrip())) b.validate_and_handle() else: auto_newline(b) @handle('c-d', filter=~sidebar_visible & has_focus(python_input.default_buffer) & Condition(lambda: # The current buffer is empty. not get_app().current_buffer.text)) def _(event): """ Override Control-D exit, to ask for confirmation. """ if python_input.confirm_exit: python_input.show_exit_confirmation = True else: event.app.exit(exception=EOFError) @handle('c-c', filter=has_focus(python_input.default_buffer)) def _(event): " Abort when Control-C has been pressed. " event.app.exit(exception=KeyboardInterrupt, style='class:aborting') return bindings
def cancel(self): get_app().exit(result=False)
def vi_buffer_focussed(): app = get_app() if app.layout.has_focus(editor.search_buffer) or app.layout.has_focus(editor.command_buffer): return False return True
def no_handler(): get_app().exit(result=False)
def ok_handler(): get_app().exit(result=textfield.text)
def load_python_bindings(python_input): """ Custom key bindings. """ bindings = KeyBindings() sidebar_visible = Condition(lambda: python_input.show_sidebar) handle = bindings.add @handle("c-l") def _(event): """ Clear whole screen and render again -- also when the sidebar is visible. """ event.app.renderer.clear() @handle("c-z") def _(event): """ Suspend. """ if python_input.enable_system_bindings: event.app.suspend_to_background() # Delete word before cursor, but use all Python symbols as separators # (WORD=False). handle("c-w")(get_by_name("backward-kill-word")) @handle("f2") def _(event): """ Show/hide sidebar. """ python_input.show_sidebar = not python_input.show_sidebar if python_input.show_sidebar: event.app.layout.focus(python_input.ptpython_layout.sidebar) else: event.app.layout.focus_last() @handle("f3") def _(event): """ Select from the history. """ python_input.enter_history() @handle("f4") def _(event): """ Toggle between Vi and Emacs mode. """ python_input.vi_mode = not python_input.vi_mode @handle("f6") def _(event): """ Enable/Disable paste mode. """ python_input.paste_mode = not python_input.paste_mode @handle("tab", filter=~sidebar_visible & ~has_selection & tab_should_insert_whitespace) def _(event): """ When tab should insert whitespace, do that instead of completion. """ event.app.current_buffer.insert_text(" ") @Condition def is_multiline(): return document_is_multiline_python( python_input.default_buffer.document) @handle( "enter", filter=~sidebar_visible & ~has_selection & (vi_insert_mode | emacs_insert_mode) & has_focus(DEFAULT_BUFFER) & ~is_multiline, ) @handle(Keys.Escape, Keys.Enter, filter=~sidebar_visible & emacs_mode) def _(event): """ Accept input (for single line input). """ b = event.current_buffer if b.validate(): # When the cursor is at the end, and we have an empty line: # drop the empty lines, but return the value. b.document = Document(text=b.text.rstrip(), cursor_position=len(b.text.rstrip())) b.validate_and_handle() @handle( "enter", filter=~sidebar_visible & ~has_selection & (vi_insert_mode | emacs_insert_mode) & has_focus(DEFAULT_BUFFER) & is_multiline, ) def _(event): """ Behaviour of the Enter key. Auto indent after newline/Enter. (When not in Vi navigaton mode, and when multiline is enabled.) """ b = event.current_buffer empty_lines_required = python_input.accept_input_on_enter or 10000 def at_the_end(b): """we consider the cursor at the end when there is no text after the cursor, or only whitespace.""" text = b.document.text_after_cursor return text == "" or (text.isspace() and not "\n" in text) if python_input.paste_mode: # In paste mode, always insert text. b.insert_text("\n") elif at_the_end(b) and b.document.text.replace(" ", "").endswith( "\n" * (empty_lines_required - 1)): # When the cursor is at the end, and we have an empty line: # drop the empty lines, but return the value. if b.validate(): b.document = Document(text=b.text.rstrip(), cursor_position=len(b.text.rstrip())) b.validate_and_handle() else: auto_newline(b) @handle( "c-d", filter=~sidebar_visible & has_focus(python_input.default_buffer) & Condition(lambda: # The current buffer is empty. not get_app().current_buffer.text), ) def _(event): """ Override Control-D exit, to ask for confirmation. """ if python_input.confirm_exit: # Show exit confirmation and focus it (focusing is important for # making sure the default buffer key bindings are not active). python_input.show_exit_confirmation = True python_input.app.layout.focus( python_input.ptpython_layout.exit_confirmation) else: event.app.exit(exception=EOFError) @handle("c-c", filter=has_focus(python_input.default_buffer)) def _(event): "Abort when Control-C has been pressed." event.app.exit(exception=KeyboardInterrupt, style="class:aborting") return bindings
def exit_modal(self): self.input = self.cmder_input app = get_app() app.invalidate() app.layout.focus(self.cmder_input) focus_prev()
def scroll_amount(self): # magic number: top frame + bottom frame + prompt line + status line console_size = get_app().output.get_size().rows - 4 return console_size * 2//3
def default_focus(): app = get_app() return app.layout.current_window == pager.current_source_info.window
def _accept_handler(self, buff): app = get_app() app.exit(result=buff.text) app.pre_run_callables.append(buff.reset) return True # Keep text, we call 'reset' later on.
def yes_handler(): get_app().exit(result=True)
def is_focused(self): return get_app().layout.current_window == self.window
def accept(buf): get_app().layout.focus(ok_button) return True
def _accept_handler(self, buff): app = get_app() app.exit(result=buff.text) app.pre_run_callables.append(buff.reset) return True # Keep text, we call 'reset' later on.
def select_item(mouse_event): # bind option with this index to mouse event self.selected_option_index = index self.answered = True get_app().exit(result=self.get_selection()[0])
def _use_color_depth(self, depth): get_app()._color_depth = depth
def has_selected_completion(): """Enable when the current buffer has a selected completion.""" complete_state = get_app().current_buffer.complete_state return (complete_state is not None and complete_state.current_completion is not None)
def get_text(): app = get_app() if app.key_processor.arg is not None: return ' %s ' % app.key_processor.arg else: return ''
def commit_changes(buffer): controller.input_mode = False controller.view.update_cell(controller.view.current_cell_coord(), buffer.text) get_app().invalidate()
def exit_(): get_app().exit() exit(0)
def _accept_handler(self, buff): app = get_app() app.exit(result=buff.text) app.pre_run_callables.append(buff.reset)
def get_app_container_checklist_window(): app = get_app() container = app.layout.container checklist_window = container.content.children[0] return (app, container, checklist_window)
def _create_options(self): """ Create a list of `Option` instances for the options sidebar. """ def enable(attribute, value=True): setattr(self, attribute, value) # Return `True`, to be able to chain this in the lambdas below. return True def disable(attribute): setattr(self, attribute, False) return True def simple_option(title, description, field_name, values=None): " Create Simple on/of option. " values = values or ['off', 'on'] def get_current_value(): return values[bool(getattr(self, field_name))] def get_values(): return { values[1]: lambda: enable(field_name), values[0]: lambda: disable(field_name), } return Option(title=title, description=description, get_values=get_values, get_current_value=get_current_value) return [ OptionCategory('Input', [ simple_option(title='Editing mode', description='Vi or emacs key bindings.', field_name='vi_mode', values=[EditingMode.EMACS, EditingMode.VI]), simple_option( title='Paste mode', description="When enabled, don't indent automatically.", field_name='paste_mode'), Option( title='Complete while typing', description= "Generate autocompletions automatically while typing. " 'Don\'t require pressing TAB. (Not compatible with "History search".)', get_current_value=lambda: ['off', 'on'][ self.complete_while_typing], get_values=lambda: { 'on': lambda: enable('complete_while_typing') and disable( 'enable_history_search'), 'off': lambda: disable('complete_while_typing'), }), Option( title='History search', description= 'When pressing the up-arrow, filter the history on input starting ' 'with the current text. (Not compatible with "Complete while typing".)', get_current_value=lambda: ['off', 'on'][ self.enable_history_search], get_values=lambda: { 'on': lambda: enable('enable_history_search') and disable( 'complete_while_typing'), 'off': lambda: disable('enable_history_search'), }), simple_option( title='Mouse support', description= 'Respond to mouse clicks and scrolling for positioning the cursor, ' 'selecting text and scrolling through windows.', field_name='enable_mouse_support'), simple_option(title='Confirm on exit', description='Require confirmation when exiting.', field_name='confirm_exit'), simple_option( title='Input validation', description= 'In case of syntax errors, move the cursor to the error ' 'instead of showing a traceback of a SyntaxError.', field_name='enable_input_validation'), simple_option( title='Auto suggestion', description='Auto suggest inputs by looking at the history. ' 'Pressing right arrow or Ctrl-E will complete the entry.', field_name='enable_auto_suggest'), Option( title='Accept input on enter', description= 'Amount of ENTER presses required to execute input when the cursor ' 'is at the end of the input. (Note that META+ENTER will always execute.)', get_current_value=lambda: str(self.accept_input_on_enter or 'meta-enter'), get_values=lambda: { '2': lambda: enable('accept_input_on_enter', 2), '3': lambda: enable('accept_input_on_enter', 3), '4': lambda: enable('accept_input_on_enter', 4), 'meta-enter': lambda: enable('accept_input_on_enter', None), }), ]), OptionCategory('Display', [ Option( title='Completions', description= 'Visualisation to use for displaying the completions. (Multiple columns, one column, a toolbar or nothing.)', get_current_value=lambda: self.completion_visualisation, get_values=lambda: { CompletionVisualisation.NONE: lambda: enable('completion_visualisation', CompletionVisualisation.NONE), CompletionVisualisation.POP_UP: lambda: enable('completion_visualisation', CompletionVisualisation.POP_UP), CompletionVisualisation.MULTI_COLUMN: lambda: enable('completion_visualisation', CompletionVisualisation.MULTI_COLUMN), CompletionVisualisation.TOOLBAR: lambda: enable('completion_visualisation', CompletionVisualisation.TOOLBAR), }), Option(title='Prompt', description= "Visualisation of the prompt. ('>>>' or 'In [1]:')", get_current_value=lambda: self.prompt_style, get_values=lambda: dict( (s, partial(enable, 'prompt_style', s)) for s in self.all_prompt_styles)), simple_option( title='Blank line after output', description='Insert a blank line after the output.', field_name='insert_blank_line_after_output'), simple_option(title='Show signature', description='Display function signatures.', field_name='show_signature'), simple_option(title='Show docstring', description='Display function docstrings.', field_name='show_docstring'), simple_option( title='Show line numbers', description= 'Show line numbers when the input consists of multiple lines.', field_name='show_line_numbers'), simple_option( title='Show Meta+Enter message', description= 'Show the [Meta+Enter] message when this key combination is required to execute commands. ' + '(This is the case when a simple [Enter] key press will insert a newline.', field_name='show_meta_enter_message'), simple_option( title='Wrap lines', description='Wrap lines instead of scrolling horizontally.', field_name='wrap_lines'), simple_option( title='Show status bar', description= 'Show the status bar at the bottom of the terminal.', field_name='show_status_bar'), simple_option( title='Show sidebar help', description= 'When the sidebar is visible, also show this help text.', field_name='show_sidebar_help'), simple_option( title='Highlight parenthesis', description= 'Highlight matching parenthesis, when the cursor is on or right after one.', field_name='highlight_matching_parenthesis'), ]), OptionCategory('Colors', [ simple_option(title='Syntax highlighting', description='Use colors for syntax highligthing', field_name='enable_syntax_highlighting'), Option(title='Code', description='Color scheme to use for the Python code.', get_current_value=lambda: self._current_code_style_name, get_values=lambda: dict( (name, partial(self.use_code_colorscheme, name)) for name in self.code_styles)), Option( title='User interface', description='Color scheme to use for the user interface.', get_current_value=lambda: self._current_ui_style_name, get_values=lambda: dict( (name, partial(self.use_ui_colorscheme, name)) for name in self.ui_styles)), Option( title='Color depth', description= 'Monochrome (1 bit), 16 ANSI colors (4 bit),\n256 colors (8 bit), or 24 bit.', get_current_value=lambda: COLOR_DEPTHS[get_app(). color_depth], get_values=lambda: dict( (name, partial(self._use_color_depth, depth)) for depth, name in COLOR_DEPTHS.items())), ]), ]
def default_focus(): app = get_app() return app.layout.current_window == pager.current_source_info.window
def _help(event: Any) -> None: # pylint: disable=unused-argument def _print_help() -> None: print_help() run_in_terminal(_print_help) get_app().invalidate()
def write_to_screen( self, screen: Screen, mouse_handlers: MouseHandlers, write_position: WritePosition, parent_style: str, erase_bg: bool, z_index: Optional[int], ) -> None: """ Render scrollable pane content. This works by rendering on an off-screen canvas, and copying over the visible region. """ show_scrollbar = self.show_scrollbar() if show_scrollbar: virtual_width = write_position.width - 1 else: virtual_width = write_position.width # Compute preferred height again. virtual_height = self.content.preferred_height( virtual_width, self.max_available_height).preferred # Ensure virtual height is at least the available height. virtual_height = max(virtual_height, write_position.height) virtual_height = min(virtual_height, self.max_available_height) # First, write the content to a virtual screen, then copy over the # visible part to the real screen. temp_screen = Screen(default_char=Char(char=" ", style=parent_style)) temp_write_position = WritePosition(xpos=0, ypos=0, width=virtual_width, height=virtual_height) temp_mouse_handlers = MouseHandlers() self.content.write_to_screen( temp_screen, temp_mouse_handlers, temp_write_position, parent_style, erase_bg, z_index, ) temp_screen.draw_all_floats() # If anything in the virtual screen is focused, move vertical scroll to from prompt_toolkit.application import get_app focused_window = get_app().layout.current_window try: visible_win_write_pos = temp_screen.visible_windows_to_write_positions[ focused_window] except KeyError: pass # No window focused here. Don't scroll. else: # Make sure this window is visible. self._make_window_visible( write_position.height, virtual_height, visible_win_write_pos, temp_screen.cursor_positions.get(focused_window), ) # Copy over virtual screen and zero width escapes to real screen. self._copy_over_screen(screen, temp_screen, write_position, virtual_width) # Copy over mouse handlers. self._copy_over_mouse_handlers(mouse_handlers, temp_mouse_handlers, write_position, virtual_width) # Set screen.width/height. ypos = write_position.ypos xpos = write_position.xpos screen.width = max(screen.width, xpos + virtual_width) screen.height = max(screen.height, ypos + write_position.height) # Copy over window write positions. self._copy_over_write_positions(screen, temp_screen, write_position) if temp_screen.show_cursor: screen.show_cursor = True # Copy over cursor positions, if they are visible. for window, point in temp_screen.cursor_positions.items(): if (0 <= point.x < write_position.width and self.vertical_scroll <= point.y < write_position.height + self.vertical_scroll): screen.cursor_positions[window] = Point(x=point.x + xpos, y=point.y + ypos - self.vertical_scroll) # Copy over menu positions, but clip them to the visible area. for window, point in temp_screen.menu_positions.items(): screen.menu_positions[window] = self._clip_point_to_visible_area( Point(x=point.x + xpos, y=point.y + ypos - self.vertical_scroll), write_position, ) # Draw scrollbar. if show_scrollbar: self._draw_scrollbar( write_position, virtual_height, screen, )