def python_sidebar_navigation(python_input): """ Create the `Layout` showing the navigation information for the sidebar. """ def get_text_fragments(): tokens = [] # Show navigation info. tokens.extend([ ('class:sidebar', ' '), ('class:sidebar.key', '[Arrows]'), ('class:sidebar', ' '), ('class:sidebar.description', 'Navigate'), ('class:sidebar', ' '), ('class:sidebar.key', '[Enter]'), ('class:sidebar', ' '), ('class:sidebar.description', 'Hide menu'), ]) return tokens return Window(FormattedTextControl(get_text_fragments), style='class:sidebar', width=Dimension.exact(43), height=Dimension.exact(1))
def setup_logging_containers(repl): j = KosmosShellConfig.j panel_line_count = j.core.myenv.config.get("LOGGER_PANEL_NRLINES", 12) parent_container = get_ptpython_parent_container(repl) parent_container.children.extend( [ ConditionalContainer( content=Window(height=Dimension.exact(1), char="\u2500", style="class:separator"), filter=HasLogs(repl) & ~is_done, ), ConditionalContainer( content=Window( BufferControl( buffer=LogPane.Buffer, input_processors=[FormatANSIText(), HighlightIncrementalSearchProcessor()], focusable=False, preview_search=True, ), wrap_lines=True, height=Dimension.exact(panel_line_count), ), filter=HasLogs(repl) & ~is_done, ), ] )
def show_sidebar_button_info(my_app: "sqlApp") -> Container: """ Create `Layout` for the information in the right-bottom corner. (The right part of the status bar.) """ @if_mousedown def toggle_sidebar(mouse_event: MouseEvent) -> None: " Click handler for the menu. " my_app.show_sidebar = not my_app.show_sidebar # TO DO: app version rather than python version = sys.version_info tokens: StyleAndTextTuples = [ ("class:status-toolbar.key", "[C-t]", toggle_sidebar), ("class:status-toolbar", " Object Browser", toggle_sidebar), ("class:status-toolbar", " - "), ("class:status-toolbar.cli-version", "odbcli %s" % __version__), ("class:status-toolbar", " "), ] width = fragment_list_width(tokens) def get_text_fragments() -> StyleAndTextTuples: # Python version return tokens return ConditionalContainer( content=Window( FormattedTextControl(get_text_fragments), style="class:status-toolbar", height=Dimension.exact(1), width=Dimension.exact(width), ), filter=~is_done & Condition(lambda: not my_app.show_exit_confirmation) & renderer_height_is_known)
def preferred_dimensions(self, width): dimensions = [[]] * self.columns for row in self.children: assert isinstance(row, _Row) j = 0 for cell in row.children: assert isinstance(cell, _Cell) if cell.merge != 1: dimensions[j].append(cell.preferred_width(width)) j += cell.merge for i, c in enumerate(dimensions): yield D.exact(1) try: w = self.column_widths[i] except IndexError: w = self.column_width if w is None: # fitted yield max_layout_dimensions(c) else: # fixed or weighted yield to_dimension(w) yield D.exact(1)
def show_sidebar_button_info(python_input): """ Create `Layout` for the information in the right-bottom corner. (The right part of the status bar.) """ @if_mousedown def toggle_sidebar(mouse_event): " Click handler for the menu. " python_input.show_sidebar = not python_input.show_sidebar version = sys.version_info tokens = [ ('class:status-toolbar.key', '[F2]', toggle_sidebar), ('class:status-toolbar', ' Menu', toggle_sidebar), ('class:status-toolbar', ' - '), ('class:status-toolbar.python-version', '%s %i.%i.%i' % (platform.python_implementation(), version[0], version[1], version[2])), ('class:status-toolbar', ' '), ] width = fragment_list_width(tokens) def get_text_fragments(): # Python version return tokens return ConditionalContainer( content=Window( FormattedTextControl(get_text_fragments), style='class:status-toolbar', height=Dimension.exact(1), width=Dimension.exact(width)), filter=~is_done & renderer_height_is_known & Condition(lambda: python_input.show_status_bar and not python_input.show_exit_confirmation))
def python_sidebar_navigation(python_input): """ Create the `Layout` showing the navigation information for the sidebar. """ def get_text_fragments(): tokens = [] # Show navigation info. tokens.extend([ ('class:sidebar', ' '), ('class:sidebar.key', '[Arrows]'), ('class:sidebar', ' '), ('class:sidebar.description', 'Navigate'), ('class:sidebar', ' '), ('class:sidebar.key', '[Enter]'), ('class:sidebar', ' '), ('class:sidebar.description', 'Hide menu'), ]) return tokens return Window( FormattedTextControl(get_text_fragments), style='class:sidebar', width=Dimension.exact(43), height=Dimension.exact(1))
def show_sidebar_button_info(python_input): """ Create `Layout` for the information in the right-bottom corner. (The right part of the status bar.) """ @if_mousedown def toggle_sidebar(mouse_event): " Click handler for the menu. " python_input.show_sidebar = not python_input.show_sidebar version = sys.version_info tokens = [ ('class:status-toolbar.key', '[F2]', toggle_sidebar), ('class:status-toolbar', ' Menu', toggle_sidebar), ('class:status-toolbar', ' - '), ('class:status-toolbar.python-version', '%s %i.%i.%i' % (platform.python_implementation(), version[0], version[1], version[2])), ('class:status-toolbar', ' '), ] width = fragment_list_width(tokens) def get_text_fragments(): # Python version return tokens return ConditionalContainer( content=Window(FormattedTextControl(get_text_fragments), style='class:status-toolbar', height=Dimension.exact(1), width=Dimension.exact(width)), filter=~is_done & renderer_height_is_known & Condition(lambda: python_input.show_status_bar and not python_input. show_exit_confirmation))
def get_width(self, progress_bar: "ProgressBar") -> AnyDimension: width = 5 for counter in progress_bar.counters: if counter.items_completed >= 1000: width = 6 break return Dimension.exact(width)
def sql_sidebar_navigation(): """ Create the `Layout` showing the navigation information for the sidebar. """ def get_text_fragments(): # Show navigation info. return [ ("class:sidebar.navigation", " "), ("class:sidebar.navigation.key", "[Up/Dn]"), ("class:sidebar.navigation", " "), ("class:sidebar.navigation.description", "Navigate"), ("class:sidebar.navigation", " "), ("class:sidebar.navigation.key", "[L/R]"), ("class:sidebar.navigation", " "), ("class:sidebar.navigation.description", "Expand/Collapse"), ("class:sidebar.navigation", "\n "), ("class:sidebar.navigation.key", "[Enter]"), ("class:sidebar.navigation", " "), ("class:sidebar.navigation.description", "Connect/Preview"), ] return Window( FormattedTextControl(get_text_fragments), style = "class:sidebar.navigation", width=Dimension.exact( 45 ), height=Dimension(max = 2), )
def __init__(self, prev, next, table, borders, window_too_small=None, align=HorizontalAlign.JUSTIFY, padding=D.exact(0), padding_char=None, padding_style='', width=None, height=None, z_index=None, modal=False, key_bindings=None, style=''): assert prev or next self.prev = prev self.next = next self.table = table self.borders = borders children = [_HorizontalBorder(borders=borders)] * self.columns super().__init__( children=children, window_too_small=window_too_small, align=align, padding=padding, padding_char=padding_char, padding_style=padding_style, width=width, height=height or 1, z_index=z_index, modal=modal, key_bindings=key_bindings, style=style)
def __init__(self, row, table, borders, window_too_small=None, align=HorizontalAlign.JUSTIFY, padding=D.exact(0), padding_char=None, padding_style='', width=None, height=None, z_index=None, modal=False, key_bindings=None, style=''): self.table = table self.borders = borders # ensure the row is iterable (has cells) if not isinstance(row, list): row = [row] children = [] for c in row: m = 1 if isinstance(c, Merge): c, m = c elif isinstance(c, dict): c, m = Merge(**c) children.append(_Cell(cell=c, table=table, row=self, merge=m)) super().__init__( children=children, window_too_small=window_too_small, align=align, padding=padding, padding_char=padding_char, padding_style=padding_style, width=width, height=height, z_index=z_index, modal=modal, key_bindings=key_bindings, style=style)
def progress_dialog( title: AnyFormattedText = "", text: AnyFormattedText = "", run_callback: Callable[[Callable[[int], None], Callable[[str], None]], None] = ( lambda *a: None ), style: Optional[BaseStyle] = None, ) -> Application[None]: """ :param run_callback: A function that receives as input a `set_percentage` function and it does the work. """ loop = get_event_loop() progressbar = ProgressBar() text_area = TextArea( focusable=False, # Prefer this text area as big as possible, to avoid having a window # that keeps resizing when we add text to it. height=D(preferred=10 ** 10), ) dialog = Dialog( body=HSplit( [ Box(Label(text=text)), Box(text_area, padding=D.exact(1)), progressbar, ] ), title=title, with_background=True, ) app = _create_app(dialog, style) def set_percentage(value: int) -> None: progressbar.percentage = int(value) app.invalidate() def log_text(text: str) -> None: loop.call_soon_threadsafe(text_area.buffer.insert_text, text) app.invalidate() # Run the callback in the executor. When done, set a return value for the # UI, so that it quits. def start() -> None: try: run_callback(set_percentage, log_text) finally: app.exit() def pre_run() -> None: run_in_executor_with_context(start) app.pre_run_callables.append(pre_run) return app
def show_sidebar_button_info(python_input: "PythonInput") -> Container: """ Create `Layout` for the information in the right-bottom corner. (The right part of the status bar.) """ @if_mousedown def toggle_sidebar(mouse_event: MouseEvent) -> None: " Click handler for the menu. " python_input.show_sidebar = not python_input.show_sidebar version = sys.version_info tokens: StyleAndTextTuples = [ ("class:status-toolbar.key", "[F2]", toggle_sidebar), ("class:status-toolbar", " Menu", toggle_sidebar), ("class:status-toolbar", " - "), ( "class:status-toolbar.python-version", "%s %i.%i.%i" % (platform.python_implementation(), version[0], version[1], version[2]), ), ("class:status-toolbar", " "), ] width = fragment_list_width(tokens) def get_text_fragments() -> StyleAndTextTuples: # Python version return tokens return ConditionalContainer( content=Window( FormattedTextControl(get_text_fragments), style="class:status-toolbar", height=Dimension.exact(1), width=Dimension.exact(width), ), filter=~is_done & renderer_height_is_known & Condition( lambda: python_input.show_status_bar and not python_input.show_exit_confirmation ), )
def select_prompt(message, options, mark='>'): control = SelectControl(options) def get_formatted_text(): return control.select_option_text(mark) layout = Layout( HSplit([ Window( height=Dimension.exact(1), content=FormattedTextControl(lambda: message + '\n', show_cursor=False), ), Window(height=Dimension.exact(len(control.options)), content=FormattedTextControl(get_formatted_text)), ConditionalContainer(Window(control), filter=~IsDone()) ])) app = Application(layout=layout, key_bindings=control.key_bindings, full_screen=False) return app.run()
def python_sidebar_navigation(python_input): """ Create the `Layout` showing the navigation information for the sidebar. """ def get_text_fragments(): # Show navigation info. return [ ("class:sidebar", " "), ("class:sidebar.key", "[Arrows]"), ("class:sidebar", " "), ("class:sidebar.description", "Navigate"), ("class:sidebar", " "), ("class:sidebar.key", "[Enter]"), ("class:sidebar", " "), ("class:sidebar.description", "Hide menu"), ] return Window( FormattedTextControl(get_text_fragments), style="class:sidebar", width=Dimension.exact(43), height=Dimension.exact(1), )
def progress_dialog(title='', text='', run_callback=None, style=None, async_=False): """ :param run_callback: A function that receives as input a `set_percentage` function and it does the work. """ assert callable(run_callback) progressbar = ProgressBar() text_area = TextArea( focusable=False, # Prefer this text area as big as possible, to avoid having a window # that keeps resizing when we add text to it. height=D(preferred=10**10)) dialog = Dialog(body=HSplit([ Box(Label(text=text)), Box(text_area, padding=D.exact(1)), progressbar, ]), title=title, with_background=True) app = _create_app(dialog, style) def set_percentage(value): progressbar.percentage = int(value) app.invalidate() def log_text(text): text_area.buffer.insert_text(text) app.invalidate() # Run the callback in the executor. When done, set a return value for the # UI, so that it quits. def start(): try: run_callback(set_percentage, log_text) finally: app.exit() run_in_executor(start) if async_: return app.run_async() else: return app.run()
def progress_dialog( title: AnyFormattedText = '', text: AnyFormattedText = '', run_callback: Callable[[Callable[[int], None], Callable[[str], None]], None] = (lambda *a: None), style: Optional[BaseStyle] = None) -> Application[None]: """ :param run_callback: A function that receives as input a `set_percentage` function and it does the work. """ loop = get_event_loop() progressbar = ProgressBar() text_area = TextArea( focusable=False, # Prefer this text area as big as possible, to avoid having a window # that keeps resizing when we add text to it. height=D(preferred=10**10)) dialog = Dialog( body=HSplit([ Box(Label(text=text)), Box(text_area, padding=D.exact(1)), progressbar, ]), title=title, with_background=True) app = _create_app(dialog, style) def set_percentage(value: int) -> None: progressbar.percentage = int(value) app.invalidate() def log_text(text: str) -> None: loop.call_soon_threadsafe(text_area.buffer.insert_text, text) app.invalidate() # Run the callback in the executor. When done, set a return value for the # UI, so that it quits. def start() -> None: try: run_callback(set_percentage, log_text) finally: app.exit() def pre_run() -> None: run_in_executor_with_context(start) app.pre_run_callables.append(pre_run) return app
def progress_dialog(title='', text='', run_callback=None, style=None, async_=False): """ :param run_callback: A function that receives as input a `set_percentage` function and it does the work. """ assert callable(run_callback) progressbar = ProgressBar() text_area = TextArea( focusable=False, # Prefer this text area as big as possible, to avoid having a window # that keeps resizing when we add text to it. height=D(preferred=10**10)) dialog = Dialog( body=HSplit([ Box(Label(text=text)), Box(text_area, padding=D.exact(1)), progressbar, ]), title=title, with_background=True) app = _create_app(dialog, style) def set_percentage(value): progressbar.percentage = int(value) app.invalidate() def log_text(text): text_area.buffer.insert_text(text) app.invalidate() # Run the callback in the executor. When done, set a return value for the # UI, so that it quits. def start(): try: run_callback(set_percentage, log_text) finally: app.exit() run_in_executor(start) if async_: return app.run_async() else: return app.run()
def setup_docstring_containers(repl): parent_container = get_ptpython_parent_container(repl) # the same as ptpython containers, but without signature checking parent_container.children.extend( [ ConditionalContainer( content=Window(height=Dimension.exact(1), char="\u2500", style="class:separator"), filter=HasDocString(repl) & ShowDocstring(repl) & ~is_done, ), ConditionalContainer( content=Window( BufferControl(buffer=repl.docstring_buffer, lexer=PygmentsLexer(PythonLexer)), height=Dimension(max=12), ), filter=HasDocString(repl) & ShowDocstring(repl) & ~is_done, ), ] )
def expanding_object_notification(my_app: "sqlApp"): """ Create the `Layout` for the 'Expanding object' notification. """ def get_text_fragments(): # Show navigation info. return [("fg:red", "Expanding object ...")] return ConditionalContainer( content=Window( FormattedTextControl(get_text_fragments), style="class:sidebar", width=Dimension.exact(45), height=Dimension(max=1), ), filter=~is_done & ShowSidebar(my_app) & Condition(lambda: my_app.show_expanding_object))
def setup_logging_containers(repl): parent_container = get_ptpython_parent_container(repl) parent_container.children.extend( [ ConditionalContainer( content=Window(height=Dimension.exact(1), char="\u2500", style="class:separator"), filter=HasLogs(repl) & ~is_done, ), ConditionalContainer( content=Window( BufferControl( buffer=LogPane.Buffer, input_processors=[FormatANSIText(), HighlightIncrementalSearchProcessor()], focusable=False, preview_search=True, ), height=Dimension(max=12), ), filter=HasLogs(repl) & ~is_done, ), ] )
def __init__( self, text='', width=None, height=None, max_length=None, allowed_chars=None, widget=None, style=None, ): self.max_length = max_length self.buffer = FixedLengthBuffer( document=Document(text, 0), multiline=False, max_length=max_length, widget=widget, allowed_chars=allowed_chars, accept_handler=None, ) self.control = BufferControl( buffer=self.buffer, focusable=True, focus_on_click=True, ) height = D.exact(1) self.window = Window( height=height, width=width, content=self.control, style=style, wrap_lines=False, )
def __init__(self, text: str = '', multiline: FilterOrBool = True, password: FilterOrBool = False, lexer: Optional[Lexer] = None, auto_suggest: Optional[AutoSuggest] = None, completer: Optional[Completer] = None, complete_while_typing: FilterOrBool = True, accept_handler: Optional[BufferAcceptHandler] = None, history: Optional[History] = None, focusable: FilterOrBool = True, focus_on_click: FilterOrBool = False, wrap_lines: FilterOrBool = True, read_only: FilterOrBool = False, width: AnyDimension = None, height: AnyDimension = None, dont_extend_height: FilterOrBool = False, dont_extend_width: FilterOrBool = False, line_numbers: bool = False, get_line_prefix: Optional[GetLinePrefixCallable] = None, scrollbar: bool = False, style: str = '', search_field: Optional[SearchToolbar] = None, preview_search: FilterOrBool = True, prompt: AnyFormattedText = '', input_processors: Optional[List[Processor]] = None) -> None: if search_field is None: search_control = None elif isinstance(search_field, SearchToolbar): search_control = search_field.control if input_processors is None: input_processors = [] # Writeable attributes. self.completer = completer self.complete_while_typing = complete_while_typing self.lexer = lexer self.auto_suggest = auto_suggest self.read_only = read_only self.wrap_lines = wrap_lines self.buffer = Buffer( document=Document(text, 0), multiline=multiline, read_only=Condition(lambda: is_true(self.read_only)), completer=DynamicCompleter(lambda: self.completer), complete_while_typing=Condition( lambda: is_true(self.complete_while_typing)), auto_suggest=DynamicAutoSuggest(lambda: self.auto_suggest), accept_handler=accept_handler, history=history) self.control = BufferControl( buffer=self.buffer, lexer=DynamicLexer(lambda: self.lexer), input_processors=[ ConditionalProcessor( AppendAutoSuggestion(), has_focus(self.buffer) & ~is_done), ConditionalProcessor( processor=PasswordProcessor(), filter=to_filter(password) ), BeforeInput(prompt, style='class:text-area.prompt'), ] + input_processors, search_buffer_control=search_control, preview_search=preview_search, focusable=focusable, focus_on_click=focus_on_click) if multiline: if scrollbar: right_margins = [ScrollbarMargin(display_arrows=True)] else: right_margins = [] if line_numbers: left_margins = [NumberedMargin()] else: left_margins = [] else: height = D.exact(1) left_margins = [] right_margins = [] style = 'class:text-area ' + style self.window = Window( height=height, width=width, dont_extend_height=dont_extend_height, dont_extend_width=dont_extend_width, content=self.control, style=style, wrap_lines=Condition(lambda: is_true(self.wrap_lines)), left_margins=left_margins, right_margins=right_margins, get_line_prefix=get_line_prefix)
def signature_toolbar(python_input): """ Return the `Layout` for the signature. """ def get_text_fragments(): result = [] append = result.append Signature = 'class:signature-toolbar' if python_input.signatures: sig = python_input.signatures[0] # Always take the first one. append((Signature, ' ')) try: append((Signature, sig.full_name)) except IndexError: # Workaround for #37: https://github.com/jonathanslenders/python-prompt-toolkit/issues/37 # See also: https://github.com/davidhalter/jedi/issues/490 return [] append((Signature + ',operator', '(')) try: enumerated_params = enumerate(sig.params) except AttributeError: # Workaround for #136: https://github.com/jonathanslenders/ptpython/issues/136 # AttributeError: 'Lambda' object has no attribute 'get_subscope_by_name' return [] for i, p in enumerated_params: # Workaround for #47: 'p' is None when we hit the '*' in the signature. # and sig has no 'index' attribute. # See: https://github.com/jonathanslenders/ptpython/issues/47 # https://github.com/davidhalter/jedi/issues/598 description = (p.description if p else '*') #or '*' sig_index = getattr(sig, 'index', 0) if i == sig_index: # Note: we use `_Param.description` instead of # `_Param.name`, that way we also get the '*' before args. append((Signature + ',current-name', str(description))) else: append((Signature, str(description))) append((Signature + ',operator', ', ')) if sig.params: # Pop last comma result.pop() append((Signature + ',operator', ')')) append((Signature, ' ')) return result return ConditionalContainer( content=Window( FormattedTextControl(get_text_fragments), height=Dimension.exact(1)), filter= # Show only when there is a signature HasSignature(python_input) & # And there are no completions to be shown. (would cover signature pop-up.) ~(has_completions & (show_completions_menu(python_input) | show_multi_column_completions_menu(python_input))) # Signature needs to be shown. & ShowSignature(python_input) & # Not done yet. ~is_done)
def __init__(self, text: str = '', multiline: FilterOrBool = True, password: FilterOrBool = False, lexer: Optional[Lexer] = None, auto_suggest: Optional[AutoSuggest] = None, completer: Optional[Completer] = None, complete_while_typing: FilterOrBool = True, accept_handler: Optional[BufferAcceptHandler] = None, history: Optional[History] = None, focusable: FilterOrBool = True, focus_on_click: FilterOrBool = False, wrap_lines: FilterOrBool = True, read_only: FilterOrBool = False, width: AnyDimension = None, height: AnyDimension = None, dont_extend_height: FilterOrBool = False, dont_extend_width: FilterOrBool = False, line_numbers: bool = False, get_line_prefix: Optional[GetLinePrefixCallable] = None, scrollbar: bool = False, style: str = '', search_field: Optional[SearchToolbar] = None, preview_search: FilterOrBool = True, prompt: AnyFormattedText = '', input_processors: Optional[List[Processor]] = None) -> None: if search_field is None: search_control = None elif isinstance(search_field, SearchToolbar): search_control = search_field.control if input_processors is None: input_processors = [] # Writeable attributes. self.completer = completer self.complete_while_typing = complete_while_typing self.lexer = lexer self.auto_suggest = auto_suggest self.read_only = read_only self.wrap_lines = wrap_lines self.buffer = Buffer( document=Document(text, 0), multiline=multiline, read_only=Condition(lambda: is_true(self.read_only)), completer=DynamicCompleter(lambda: self.completer), complete_while_typing=Condition( lambda: is_true(self.complete_while_typing)), auto_suggest=DynamicAutoSuggest(lambda: self.auto_suggest), accept_handler=accept_handler, history=history) self.control = BufferControl( buffer=self.buffer, lexer=DynamicLexer(lambda: self.lexer), input_processors=[ ConditionalProcessor(AppendAutoSuggestion(), has_focus(self.buffer) & ~is_done), ConditionalProcessor(processor=PasswordProcessor(), filter=to_filter(password)), BeforeInput(prompt, style='class:text-area.prompt'), ] + input_processors, search_buffer_control=search_control, preview_search=preview_search, focusable=focusable, focus_on_click=focus_on_click) if multiline: if scrollbar: right_margins = [ScrollbarMargin(display_arrows=True)] else: right_margins = [] if line_numbers: left_margins = [NumberedMargin()] else: left_margins = [] else: height = D.exact(1) left_margins = [] right_margins = [] style = 'class:text-area ' + style self.window = Window( height=height, width=width, dont_extend_height=dont_extend_height, dont_extend_width=dont_extend_width, content=self.control, style=style, wrap_lines=Condition(lambda: is_true(self.wrap_lines)), left_margins=left_margins, right_margins=right_margins, get_line_prefix=get_line_prefix)
def progress_log_dialog( title: AnyFormattedText = "", text: AnyFormattedText = "", wait_text: str = "Wait", quit_text: str = "Quit", status_text: AnyFormattedText = "", run_callback: Callable[[ Callable[[int], None], Callable[[str], None], Callable[ [str], None], Callable[[dict], None], Callable[[], bool] ], None] = (lambda *a: None), style: Optional[BaseStyle] = None, ) -> Application[None]: """ :param run_callback: A function that receives as input a `set_percentage` function and it does the work. """ loop = get_event_loop() def wait_handler() -> None: pass def quit_handler() -> None: app = get_app() if not app.exited: app.exited = True app.exit(result=app.result) wait_button = Button(text=wait_text, handler=wait_handler) quit_button = Button(text=quit_text, handler=quit_handler) progressbar = ProgressBar() text_area = TextArea( focusable=False, # Prefer this text area as big as possible, to avoid having a window # that keeps resizing when we add text to it. height=D(preferred=10**10), width=D(preferred=10**10)) status = Label(text=status_text) dialog = Dialog( title=title, body=HSplit([ Box(Label(text=text)), Box(text_area, padding=D.exact(1)), Box(status, padding=D.exact(1)), progressbar, ]), buttons=[wait_button, quit_button], with_background=True, ) app = _create_app(dialog, style) app.result = None app.exited = False def set_percentage(value: int) -> None: progressbar.percentage = int(value) app.invalidate() def log_text(text: str) -> None: loop.call_soon_threadsafe(text_area.buffer.insert_text, text) app.invalidate() def change_status(text: str) -> None: status.formatted_text_control.text = text app.invalidate() def set_result(new_result: dict) -> None: app.result = new_result def get_exited() -> bool: return app.exited # Run the callback in the executor. When done, set a return value for the # UI, so that it quits. def start() -> None: result = None try: result = run_callback(set_percentage, log_text, change_status, set_result, get_exited) finally: if not app.exited: app.exited = True app.exit(result=result) def pre_run() -> None: run_in_executor_with_context(start) app.pre_run_callables.append(pre_run) return app
def preferred_height(self, width, max_available_height): return D.exact(BigClock.HEIGHT)
def preferred_width(self, max_available_width): # Enough to display all the digits. return Dimension.exact(6 * len('%s' % self._get_index()) - 1)
def __init__(self, history): search_toolbar = SearchToolbar() self.help_buffer_control = BufferControl( buffer=history.help_buffer, lexer=PygmentsLexer(RstLexer)) help_window = _create_popup_window( title='History Help', body=Window( content=self.help_buffer_control, right_margins=[ScrollbarMargin(display_arrows=True)], scroll_offsets=ScrollOffsets(top=2, bottom=2))) self.default_buffer_control = BufferControl( buffer=history.default_buffer, input_processors=[GrayExistingText(history.history_mapping)], lexer=PygmentsLexer(PythonLexer)) self.history_buffer_control = BufferControl( buffer=history.history_buffer, lexer=PygmentsLexer(PythonLexer), search_buffer_control=search_toolbar.control, preview_search=True) history_window = Window( content=self.history_buffer_control, wrap_lines=False, left_margins=[HistoryMargin(history)], scroll_offsets=ScrollOffsets(top=2, bottom=2)) self.root_container = HSplit([ # Top title bar. Window( content=FormattedTextControl(_get_top_toolbar_fragments), align=WindowAlign.CENTER, style='class:status-toolbar'), FloatContainer( content=VSplit([ # Left side: history. history_window, # Separator. Window(width=D.exact(1), char=BORDER.LIGHT_VERTICAL, style='class:separator'), # Right side: result. Window( content=self.default_buffer_control, wrap_lines=False, left_margins=[ResultMargin(history)], scroll_offsets=ScrollOffsets(top=2, bottom=2)), ]), floats=[ # Help text as a float. Float(width=60, top=3, bottom=2, content=ConditionalContainer( content=help_window, filter=has_focus(history.help_buffer))), ] ), # Bottom toolbars. ArgToolbar(), search_toolbar, Window( content=FormattedTextControl( partial(_get_bottom_toolbar_fragments, history=history)), style='class:status-toolbar'), ]) self.layout = Layout(self.root_container, history_window)
def signature_toolbar(python_input): """ Return the `Layout` for the signature. """ def get_text_fragments() -> StyleAndTextTuples: result: StyleAndTextTuples = [] append = result.append Signature = "class:signature-toolbar" if python_input.signatures: sig = python_input.signatures[0] # Always take the first one. append((Signature, " ")) try: append((Signature, sig.name)) except IndexError: # Workaround for #37: https://github.com/jonathanslenders/python-prompt-toolkit/issues/37 # See also: https://github.com/davidhalter/jedi/issues/490 return [] append((Signature + ",operator", "(")) got_positional_only = False got_keyword_only = False for i, p in enumerate(sig.parameters): # Detect transition between positional-only and not positional-only. if p.kind == ParameterKind.POSITIONAL_ONLY: got_positional_only = True if got_positional_only and p.kind != ParameterKind.POSITIONAL_ONLY: got_positional_only = False append((Signature, "/")) append((Signature + ",operator", ", ")) if not got_keyword_only and p.kind == ParameterKind.KEYWORD_ONLY: got_keyword_only = True append((Signature, "*")) append((Signature + ",operator", ", ")) sig_index = getattr(sig, "index", 0) if i == sig_index: # Note: we use `_Param.description` instead of # `_Param.name`, that way we also get the '*' before args. append((Signature + ",current-name", p.description)) else: append((Signature, p.description)) if p.default: # NOTE: For the jedi-based completion, the default is # currently still part of the name. append((Signature, f"={p.default}")) append((Signature + ",operator", ", ")) if sig.parameters: # Pop last comma result.pop() append((Signature + ",operator", ")")) append((Signature, " ")) return result return ConditionalContainer( content=Window(FormattedTextControl(get_text_fragments), height=Dimension.exact(1)), filter= # Show only when there is a signature HasSignature(python_input) & # Signature needs to be shown. ShowSignature(python_input) & # And no sidebar is visible. ~ShowSidebar(python_input) & # Not done yet. ~is_done, )
def __init__(self, text='', multiline=True, password=False, lexer=None, auto_suggest=None, completer=None, complete_while_typing=True, accept_handler=None, history=None, focusable=True, focus_on_click=False, wrap_lines=True, read_only=False, width=None, height=None, dont_extend_height=False, dont_extend_width=False, line_numbers=False, get_line_prefix=None, scrollbar=False, style='', search_field=None, preview_search=True, prompt='', input_processors=None): # assert isinstance(text, six.text_type) if search_field is None: search_control = None if input_processors is None: input_processors = [] # Writeable attributes. self.completer = completer self.complete_while_typing = complete_while_typing self.lexer = lexer self.auto_suggest = auto_suggest self.read_only = read_only self.wrap_lines = wrap_lines self.buffer = Buffer( document=Document(text, 0), multiline=multiline, read_only=Condition(lambda: is_true(self.read_only)), completer=DynamicCompleter(lambda: self.completer), complete_while_typing=Condition( lambda: is_true(self.complete_while_typing)), auto_suggest=DynamicAutoSuggest(lambda: self.auto_suggest), accept_handler=accept_handler, history=history) self.control = BufferControl( buffer=self.buffer, lexer=DynamicLexer(lambda: self.lexer), input_processors=[ ConditionalProcessor( AppendAutoSuggestion(), has_focus(self.buffer) & is_done), # ConditionalProcessor( processor=PasswordProcessor(), filter=to_filter(password) ), BeforeInput(prompt, style='class:text-area.prompt'), ] + input_processors, search_buffer_control=search_control, preview_search=preview_search, focusable=focusable, focus_on_click=focus_on_click) if multiline: if scrollbar: right_margins = [ScrollbarMargin(display_arrows=True)] else: right_margins = [] if line_numbers: left_margins = [NumberedMargin()] else: left_margins = [] else: height = D.exact(1) left_margins = [] right_margins = [] style = 'class:text-area ' + style self.window = Window( height=height, width=width, dont_extend_height=dont_extend_height, dont_extend_width=dont_extend_width, content=self.control, style=style, wrap_lines=Condition(lambda: is_true(self.wrap_lines)), left_margins=left_margins, right_margins=right_margins, get_line_prefix=get_line_prefix)
def sql_sidebar(my_app: "sqlApp") -> Window: """ Create the `Layout` for the sidebar with the configurable objects. """ @if_mousedown def expand_item(obj: "myDBObject") -> None: obj.expand() def tokenize_obj(obj: "myDBObject") -> StyleAndTextTuples: " Recursively build the token list " tokens: StyleAndTextTuples = [] selected = obj is my_app.selected_object expanded = obj.children is not None connected = obj.otype == "Connection" and obj.conn.connected() active = my_app.active_conn is not None and my_app.active_conn is obj.conn and obj.level == 0 act = ",active" if active else "" sel = ",selected" if selected else "" if len(obj.name) > 24 - 2 * obj.level: name_trim = obj.name[:24 - 2 * obj.level - 3] + "..." else: name_trim = ("%-" + str(24 - 2 * obj.level) + "s") % obj.name tokens.append( ("class:sidebar.label" + sel + act, " >" if connected else " ")) tokens.append( ("class:sidebar.label" + sel, " " * 2 * obj.level, expand_item)) tokens.append( ("class:sidebar.label" + sel + act, name_trim, expand_item)) tokens.append(("class:sidebar.status" + sel + act, " ", expand_item)) tokens.append(("class:sidebar.status" + sel + act, "%+12s" % obj.otype, expand_item)) if selected: tokens.append(("[SetCursorPosition]", "")) if expanded: tokens.append(("class:sidebar.status" + sel + act, "\/")) else: tokens.append(("class:sidebar.status" + sel + act, " <" if selected else " ")) # Expand past the edge of the visible buffer to get an even panel tokens.append(("class:sidebar.status" + sel + act, " " * 10)) return tokens def _buffer_pos_changed(buff): """ When the cursor changes in the sidebar buffer, make sure the appropriate database object is market as selected """ # Only when this buffer has the focus. try: line_no = buff.document.cursor_position_row if line_no < 0: # When the cursor is above the inserted region. raise IndexError idx = 0 obj = my_app.obj_list[0] while idx < line_no: if not obj.next_object: raise IndexError idx += 1 obj = obj.next_object my_app.selected_object = obj except IndexError: pass search_buffer = Buffer(name="sidebarsearchbuffer") search_field = SearchToolbar(search_buffer=search_buffer, ignore_case=True) sidebar_buffer = Buffer(name="sidebarbuffer", read_only=True, on_cursor_position_changed=_buffer_pos_changed) class myLexer(Lexer): def __init__(self, token_list=None, *args, **kwargs): super().__init__(*args, **kwargs) self.token_list = token_list def lex_document( self, document: Document) -> Callable[[int], StyleAndTextTuples]: def get_line(lineno: int) -> StyleAndTextTuples: # TODO: raise out-of-range exception return self.token_list[lineno] return get_line def reset_tokens(self, tokens: [StyleAndTextTuples]): self.token_list = tokens sidebar_lexer = myLexer() class myControl(BufferControl): def move_cursor_down(self): my_app.select_next() # Need to figure out what do do here # AFAICT thse are only called for the mouse handler # when events are otherwise not handled def move_cursor_up(self): my_app.select_previous() def mouse_handler(self, mouse_event: MouseEvent) -> "NotImplementedOrNone": """ There is an intricate relationship between the cursor position in the sidebar document and which object is market as 'selected' in the linked list. Let's not muck that up by allowing the user to change the cursor position in the sidebar document with the mouse. """ return NotImplemented def create_content(self, width: int, height: Optional[int]) -> UIContent: res = [] res_tokens = [] count = 0 obj = my_app.obj_list[0] res.append(obj.name) res_tokens.append(tokenize_obj(obj)) found_selected = obj is my_app.selected_object idx = 0 while obj.next_object is not my_app.obj_list[0]: res.append(obj.next_object.name) res_tokens.append(tokenize_obj(obj.next_object)) if obj is not my_app.selected_object and not found_selected: count += 1 idx += len(obj.name) + 1 # Newline character else: found_selected = True obj = obj.next_object self.buffer.set_document( Document(text="\n".join(res), cursor_position=idx), True) self.lexer.reset_tokens(res_tokens) return super().create_content(width, height) sidebar_control = myControl( buffer=sidebar_buffer, lexer=sidebar_lexer, search_buffer_control=search_field.control, focusable=True, ) return HSplit([ search_field, Window(sidebar_control, right_margins=[ScrollbarMargin(display_arrows=True)], style="class:sidebar", width=Dimension.exact(45), height=Dimension(min=7, preferred=33), scroll_offsets=ScrollOffsets(top=1, bottom=1)), Window( height=Dimension.exact(1), char="\u2500", style="class:sidebar,separator", ), expanding_object_notification(my_app), sql_sidebar_navigation() ])
def python_sidebar(python_input: "PythonInput") -> Window: """ Create the `Layout` for the sidebar with the configurable options. """ def get_text_fragments() -> StyleAndTextTuples: tokens: StyleAndTextTuples = [] def append_category(category: "OptionCategory") -> None: tokens.extend([ ("class:sidebar", " "), ("class:sidebar.title", " %-36s" % category.title), ("class:sidebar", "\n"), ]) def append(index: int, label: str, status: str) -> None: selected = index == python_input.selected_option_index @if_mousedown def select_item(mouse_event: MouseEvent) -> None: python_input.selected_option_index = index @if_mousedown def goto_next(mouse_event: MouseEvent) -> None: " Select item and go to next value. " python_input.selected_option_index = index option = python_input.selected_option option.activate_next() sel = ",selected" if selected else "" tokens.append(("class:sidebar" + sel, " >" if selected else " ")) tokens.append( ("class:sidebar.label" + sel, "%-24s" % label, select_item)) tokens.append(("class:sidebar.status" + sel, " ", select_item)) tokens.append( ("class:sidebar.status" + sel, "%s" % status, goto_next)) if selected: tokens.append(("[SetCursorPosition]", "")) tokens.append(("class:sidebar.status" + sel, " " * (13 - len(status)), goto_next)) tokens.append(("class:sidebar", "<" if selected else "")) tokens.append(("class:sidebar", "\n")) i = 0 for category in python_input.options: append_category(category) for option in category.options: append(i, option.title, "%s" % option.get_current_value()) i += 1 tokens.pop() # Remove last newline. return tokens class Control(FormattedTextControl): def move_cursor_down(self): python_input.selected_option_index += 1 def move_cursor_up(self): python_input.selected_option_index -= 1 return Window( Control(get_text_fragments), style="class:sidebar", width=Dimension.exact(43), height=Dimension(min=3), scroll_offsets=ScrollOffsets(top=1, bottom=1), )
def python_sidebar(python_input): """ Create the `Layout` for the sidebar with the configurable options. """ def get_text_fragments(): tokens = [] def append_category(category): tokens.extend([ ('class:sidebar', ' '), ('class:sidebar.title', ' %-36s' % category.title), ('class:sidebar', '\n'), ]) def append(index, label, status): selected = index == python_input.selected_option_index @if_mousedown def select_item(mouse_event): python_input.selected_option_index = index @if_mousedown def goto_next(mouse_event): " Select item and go to next value. " python_input.selected_option_index = index option = python_input.selected_option option.activate_next() sel = ',selected' if selected else '' tokens.append(('class:sidebar' + sel, ' >' if selected else ' ')) tokens.append(('class:sidebar.label' + sel, '%-24s' % label, select_item)) tokens.append(('class:sidebar.status' + sel, ' ', select_item)) tokens.append(('class:sidebar.status' + sel, '%s' % status, goto_next)) if selected: tokens.append(('[SetCursorPosition]', '')) tokens.append(('class:sidebar.status' + sel, ' ' * (13 - len(status)), goto_next)) tokens.append(('class:sidebar', '<' if selected else '')) tokens.append(('class:sidebar', '\n')) i = 0 for category in python_input.options: append_category(category) for option in category.options: append(i, option.title, '%s' % option.get_current_value()) i += 1 tokens.pop() # Remove last newline. return tokens class Control(FormattedTextControl): def move_cursor_down(self): python_input.selected_option_index += 1 def move_cursor_up(self): python_input.selected_option_index -= 1 return Window( Control(get_text_fragments), style='class:sidebar', width=Dimension.exact(43), height=Dimension(min=3), scroll_offsets=ScrollOffsets(top=1, bottom=1))
def sql_sidebar(my_app: "sqlApp") -> Window: """ Create the `Layout` for the sidebar with the configurable objects. """ @if_mousedown def expand_item(obj: "myDBObject") -> None: obj.expand() def tokenize_obj(obj: "myDBObject") -> StyleAndTextTuples: " Recursively build the token list " tokens: StyleAndTextTuples = [] selected = obj is my_app.selected_object expanded = obj.children is not None connected = obj.otype == "Connection" and obj.conn.connected() active = my_app.active_conn is not None and my_app.active_conn is obj.conn and obj.level == 0 act = ",active" if active else "" sel = ",selected" if selected else "" if len(obj.name) > 24 - 2 * obj.level: name_trim = obj.name[:24 - 2 * obj.level - 3] + "..." else: name_trim = ("%-" + str(24 - 2 * obj.level) + "s") % obj.name tokens.append(("class:sidebar.label" + sel + act, " >" if connected else " ")) tokens.append( ("class:sidebar.label" + sel, " " * 2 * obj.level, expand_item) ) tokens.append( ("class:sidebar.label" + sel + act, name_trim, expand_item) ) tokens.append(("class:sidebar.status" + sel + act, " ", expand_item)) tokens.append(("class:sidebar.status" + sel + act, "%+12s" % obj.otype, expand_item)) if selected: tokens.append(("[SetCursorPosition]", "")) if expanded: tokens.append(("class:sidebar.status" + sel + act, "\/")) else: tokens.append(("class:sidebar.status" + sel + act, " <" if selected else " ")) # Expand past the edge of the visible buffer to get an even panel tokens.append(("class:sidebar.status" + sel + act, " " * 10)) return tokens search_buffer = Buffer(name = "sidebarsearchbuffer") search_field = SearchToolbar( search_buffer = search_buffer, ignore_case = True ) def _buffer_pos_changed(buff): """ This callback gets executed after cursor position changes. Most of the time we register a key-press (up / down), we change the selected object and as a result of that the cursor changes. By that time we don't need to updat the selected object (cursor changed as a result of the selected object being updated). The one exception is when searching the sidebar buffer. When this happens the cursor moves ahead of the selected object. When that happens, here we update the selected object to follow suit. """ if buff.document.cursor_position_row != my_app.selected_object_idx[0]: my_app.select(buff.document.cursor_position_row) sidebar_buffer = Buffer( name = "sidebarbuffer", read_only = True, on_cursor_position_changed = _buffer_pos_changed ) class myLexer(Lexer): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._obj_list = [] def add_objects(self, objects: List): self._obj_list = objects def lex_document(self, document: Document) -> Callable[[int], StyleAndTextTuples]: def get_line(lineno: int) -> StyleAndTextTuples: # TODO: raise out-of-range exception return tokenize_obj(self._obj_list[lineno]) return get_line sidebar_lexer = myLexer() class myControl(BufferControl): def move_cursor_down(self): my_app.select_next() # Need to figure out what do do here # AFAICT these are only called for the mouse handler # when events are otherwise not handled def move_cursor_up(self): my_app.select_previous() def mouse_handler(self, mouse_event: MouseEvent) -> "NotImplementedOrNone": """ There is an intricate relationship between the cursor position in the sidebar document and which object is market as 'selected' in the linked list. Let's not muck that up by allowing the user to change the cursor position in the sidebar document with the mouse. """ return NotImplemented def create_content(self, width: int, height: Optional[int]) -> UIContent: # Only traverse the obj_list if it has been expanded / collapsed if not my_app.obj_list_changed: self.buffer.cursor_position = my_app.selected_object_idx[1] return super().create_content(width, height) res = [] obj = my_app.obj_list[0] res.append(obj) while obj.next_object is not my_app.obj_list[0]: res.append(obj.next_object) obj = obj.next_object self.lexer.add_objects(res) self.buffer.set_document(Document( text = "\n".join([a.name for a in res]), cursor_position = my_app.selected_object_idx[1]), True) # Reset obj_list_changed flag, now that we have had a chance to # regenerate the sidebar document content my_app.obj_list_changed = False return super().create_content(width, height) sidebar_control = myControl( buffer = sidebar_buffer, lexer = sidebar_lexer, search_buffer_control = search_field.control, focusable = True, ) return HSplit([ search_field, Window( sidebar_control, right_margins = [ScrollbarMargin(display_arrows = True)], style = "class:sidebar", width = Dimension.exact( 45 ), height = Dimension(min = 7, preferred = 33), scroll_offsets = ScrollOffsets(top = 1, bottom = 1)), Window( height = Dimension.exact(1), char = "\u2500", style = "class:sidebar,separator", ), expanding_object_notification(my_app), sql_sidebar_navigation()])
def preferred_height(self, width, max_available_height): return Dimension.exact(self.HEIGHT)
def __init__(self, text='', multiline=True, password=False, lexer=None, completer=None, accept_handler=None, focusable=True, wrap_lines=True, read_only=False, width=None, height=None, dont_extend_height=False, dont_extend_width=False, line_numbers=False, scrollbar=False, style='', input_processor=None, search_field=None, preview_search=True, prompt=''): assert isinstance(text, six.text_type) assert search_field is None or isinstance(search_field, SearchToolbar) if search_field is None: search_control = None elif isinstance(search_field, SearchToolbar): search_control = search_field.control self.buffer = Buffer( document=Document(text, 0), multiline=multiline, read_only=read_only, completer=completer, complete_while_typing=True, accept_handler=lambda buff: accept_handler and accept_handler()) self.control = BufferControl( buffer=self.buffer, lexer=lexer, input_processor=merge_processors([ ConditionalProcessor(processor=PasswordProcessor(), filter=to_filter(password)), HighlightSearchProcessor(preview_search=preview_search), HighlightSelectionProcessor(), DisplayMultipleCursors(), BeforeInput(prompt, style='class:text-area.prompt'), ]), search_buffer_control=search_control, preview_search=preview_search, focusable=focusable) if multiline: if scrollbar: right_margins = [ScrollbarMargin(display_arrows=True)] else: right_margins = [] if line_numbers: left_margins = [NumberedMargin()] else: left_margins = [] else: wrap_lines = False # Never wrap for single line input. height = D.exact(1) left_margins = [] right_margins = [] style = 'class:text-area ' + style self.window = Window(height=height, width=width, dont_extend_height=dont_extend_height, dont_extend_width=dont_extend_width, content=self.control, style=style, wrap_lines=wrap_lines, left_margins=left_margins, right_margins=right_margins)
def signature_toolbar(python_input): """ Return the `Layout` for the signature. """ def get_text_fragments(): result = [] append = result.append Signature = 'class:signature-toolbar' if python_input.signatures: sig = python_input.signatures[0] # Always take the first one. append((Signature, ' ')) try: append((Signature, sig.full_name)) except IndexError: # Workaround for #37: https://github.com/jonathanslenders/python-prompt-toolkit/issues/37 # See also: https://github.com/davidhalter/jedi/issues/490 return [] append((Signature + ',operator', '(')) try: enumerated_params = enumerate(sig.params) except AttributeError: # Workaround for #136: https://github.com/jonathanslenders/ptpython/issues/136 # AttributeError: 'Lambda' object has no attribute 'get_subscope_by_name' return [] for i, p in enumerated_params: # Workaround for #47: 'p' is None when we hit the '*' in the signature. # and sig has no 'index' attribute. # See: https://github.com/jonathanslenders/ptpython/issues/47 # https://github.com/davidhalter/jedi/issues/598 description = (p.description if p else '*') #or '*' sig_index = getattr(sig, 'index', 0) if i == sig_index: # Note: we use `_Param.description` instead of # `_Param.name`, that way we also get the '*' before args. append((Signature + ',current-name', str(description))) else: append((Signature, str(description))) append((Signature + ',operator', ', ')) if sig.params: # Pop last comma result.pop() append((Signature + ',operator', ')')) append((Signature, ' ')) return result return ConditionalContainer( content=Window(FormattedTextControl(get_text_fragments), height=Dimension.exact(1)), filter= # Show only when there is a signature HasSignature(python_input) & # And there are no completions to be shown. (would cover signature pop-up.) ~(has_completions & (show_completions_menu(python_input) | show_multi_column_completions_menu(python_input))) # Signature needs to be shown. & ShowSignature(python_input) & # Not done yet. ~is_done)
def python_sidebar(python_input): """ Create the `Layout` for the sidebar with the configurable options. """ def get_text_fragments(): tokens = [] def append_category(category): tokens.extend([ ('class:sidebar', ' '), ('class:sidebar.title', ' %-36s' % category.title), ('class:sidebar', '\n'), ]) def append(index, label, status): selected = index == python_input.selected_option_index @if_mousedown def select_item(mouse_event): python_input.selected_option_index = index @if_mousedown def goto_next(mouse_event): " Select item and go to next value. " python_input.selected_option_index = index option = python_input.selected_option option.activate_next() sel = ',selected' if selected else '' tokens.append(('class:sidebar' + sel, ' >' if selected else ' ')) tokens.append( ('class:sidebar.label' + sel, '%-24s' % label, select_item)) tokens.append(('class:sidebar.status' + sel, ' ', select_item)) tokens.append( ('class:sidebar.status' + sel, '%s' % status, goto_next)) if selected: tokens.append(('[SetCursorPosition]', '')) tokens.append(('class:sidebar.status' + sel, ' ' * (13 - len(status)), goto_next)) tokens.append(('class:sidebar', '<' if selected else '')) tokens.append(('class:sidebar', '\n')) i = 0 for category in python_input.options: append_category(category) for option in category.options: append(i, option.title, '%s' % option.get_current_value()) i += 1 tokens.pop() # Remove last newline. return tokens class Control(FormattedTextControl): def move_cursor_down(self): python_input.selected_option_index += 1 def move_cursor_up(self): python_input.selected_option_index -= 1 return Window(Control(get_text_fragments), style='class:sidebar', width=Dimension.exact(43), height=Dimension(min=3), scroll_offsets=ScrollOffsets(top=1, bottom=1))
def preferred_width(self, max_available_width): return D.exact(BigClock.WIDTH)