def __init__(self, inpipe): self.inpipe = inpipe self.tempfile = tempfile.NamedTemporaryFile(mode="w", delete=True) self.initialize_nodes() root = VSplit( [ # Input buffer and status line HSplit( [ self.first_line, Window( content=BufferControl( buffer=self.input_buffer, # This lexer is disabled for now because # I don't want to mess with colourschemes # lexer=PygmentsLexer(PythonLexer), ) ), self.error_output, ], width=Dimension(), ), Window(width=1, char="|"), # Output display area Window(ignore_content_width=True, content=self.output, wrap_lines=True), ], width=Dimension(), ) layout = Layout(root) self.app = Application(layout=layout, key_bindings=kb, full_screen=True)
def get_root_container(self) -> FloatContainer: buttons = [ *self._game_buttons.keys(), HorizontalLine(), Button("Quit", width=MENU_BUTTON_WIDTH, handler=self._exit), ] menu_keybindings = _create_menu_keybindings(buttons) game_buttons_container = Frame( HSplit( buttons, width=Dimension(min=MENU_BUTTON_WIDTH, max=40), height=Dimension(), ), title="Games", key_bindings=menu_keybindings, ) game_description_container = Frame( Box( Label( text=self._get_game_description, dont_extend_height=False, width=Dimension(min=40), ), padding=0, padding_left=1, ), ) return FloatContainer(VSplit([ game_buttons_container, game_description_container, ]), floats=[])
def __init__(self, title="", label_text="", completer=None): self.future = Future() def accept_text(buf): app.layout.focus(ok_button) buf.complete_state = None return True def accept(): self.future.set_result(self.text_area.text) def cancel(): self.future.set_result(None) self.text_area = TextArea( completer=completer, multiline=False, width=Dimension(preferred=40), accept_handler=accept_text, ) ok_button = Button(text="OK", handler=accept) cancel_button = Button(text="Cancel", handler=cancel) self.dialog = Dialog( title=title, body=HSplit([Label(text=label_text), self.text_area]), buttons=[ok_button, cancel_button], width=Dimension(preferred=80), modal=True, )
def get_default_buffer_control_height(): # If there is an autocompletion menu to be shown, make sure that our # layout has at least a minimal height in order to display it. space = self.reserve_space_for_menu if space and not get_app().is_done: buff = default_buffer if buff.complete_while_typing() or buff.complete_state is not None: return Dimension(min=space) return Dimension()
def _get_buffer_control_height(buf): # If there is an autocompletion menu to be shown, make sure that our # layout has at least a minimal height in order to display it. if reserve_space_for_menu and not get_app().is_done: buff = buffer # Reserve the space, either when there are completions, or when # `complete_while_typing` is true and we expect completions very # soon. if buff.complete_while_typing() or buff.complete_state is not None: return Dimension(min=reserve_space_for_menu) return Dimension()
def __init__(self, text: str, handler: Optional[Callable[[], None]] = None, width: int = None) -> None: self.text = text self.handler = handler self.width = width if width is None: self.width = max(12, len(text) + 2) self.control = FormattedTextControl( self._get_text_fragments, key_bindings=self._get_key_bindings(), focusable=True, ) def get_style() -> str: if get_app().layout.has_focus(self): return "class:button.focused" else: return "class:button" self.window = Window( self.control, align=WindowAlign.CENTER, height=1, width=Dimension(preferred=self.width, max=self.width), style=get_style, dont_extend_width=True, dont_extend_height=True, always_hide_cursor=True # Stops curser from showing when selected )
def InputDialog(on_ok, on_cancel, title="", prompt="", ok_text="OK", cancel_text="Cancel", is_text_valid=lambda text: True) -> Dialog: """Returns a Dialogue component displaying a text input box""" def on_accept(buf): if is_text_valid(textfield.text): get_app().layout.focus(ok_button) return True # Keep text. def on_ok_clicked(): if is_text_valid(textfield.text): on_ok(textfield.text) ok_button = Button(text=ok_text, handler=on_ok_clicked) exit_button = Button(text=cancel_text, handler=on_cancel) textfield = TextArea(multiline=False, accept_handler=on_accept) dialog = Dialog( title=title, body=HSplit( [Label(text=prompt, dont_extend_height=True), textfield], padding=Dimension(preferred=1, max=1), ), buttons=[ok_button, exit_button], with_background=True) return dialog
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 MenuScreen(controller): def on_start_click(): new_state = PlayingScreenState(username=controller.state.username, choice_history=[root_branch], choice_index=0, start_time=time()) controller.set_state(new_state) def on_help_click(): new_state = HelpScreenState(username=controller.state.username, previous_state=controller.state) controller.set_state(new_state) buttons = [ Button('start', handler=on_start_click), Button('help', handler=on_help_click), Button('quit', handler=exit_current_app) ] kb = create_vertical_button_list_kbs(buttons) body = Box( VSplit([ HSplit(children=buttons, padding=Dimension(preferred=1, max=1), key_bindings=kb) ])) toolbar_content = Window(content=FormattedTextControl( 'Hello %s. I wish you the best of luck...' % controller.state.username), align=WindowAlign.CENTER, height=1) return ToolbarFrame(body, toolbar_content)
def _create_layout(self): has_before_fragments, get_prompt_text_1, get_prompt_text_2 = \ _split_multiline_prompt(self._get_prompt) default_buffer = self.default_buffer default_buffer_control = BufferControl( buffer=default_buffer, search_buffer_control=None, input_processors=[], include_default_input_processors=False) prompt_window = Window(FormattedTextControl(get_prompt_text_1), dont_extend_height=True) default_buffer_window = Window( default_buffer_control, dont_extend_height=True, get_line_prefix=partial(self._get_line_prefix, get_prompt_text_2=get_prompt_text_2)) divider = Window(char='_', height=1, style='fg:gray bg:black') search_window = Window(content=SearchControl(), style='') bottom_toolbar = ConditionalContainer( Window(FormattedTextControl(lambda: self.bottom_toolbar), dont_extend_height=True, height=Dimension(min=1)), filter=~is_done & renderer_height_is_known & Condition(lambda: self.bottom_toolbar is not None)) layout = HSplit([ prompt_window, default_buffer_window, divider, search_window, bottom_toolbar ]) return Layout(layout, default_buffer_window)
def python_sidebar_help(python_input): """ Create the `Layout` for the help text for the current item in the sidebar. """ token = 'class:sidebar.helptext' def get_current_description(): """ Return the description of the selected option. """ i = 0 for category in python_input.options: for option in category.options: if i == python_input.selected_option_index: return option.description i += 1 return '' def get_help_text(): return [(token, get_current_description())] return ConditionalContainer( content=Window(FormattedTextControl(get_help_text), style=token, height=Dimension(min=3)), filter=ShowSidebar(python_input) & Condition(lambda: python_input.show_sidebar_help) & ~is_done)
def __init__(self, text: AnyFormattedText, style: str = '', **kw) -> None: # Note: The style needs to be applied to the toolbar as a whole, not # just the `FormattedTextControl`. super().__init__(FormattedTextControl(text, **kw), style=style, dont_extend_height=True, height=Dimension(min=1))
def sql_sidebar_help(my_app: "sqlApp"): """ Create the `Layout` for the help text for the current item in the sidebar. """ token = "class:sidebar.helptext" def get_current_description(): """ Return the description of the selected option. """ obj = my_app.selected_object if obj is not None: return obj.name return "" def get_help_text(): return [(token, get_current_description())] return ConditionalContainer( content=Window( FormattedTextControl(get_help_text), style=token, height=Dimension(min=3) ), filter = ~is_done & ShowSidebar(my_app) & Condition( lambda: not my_app.show_exit_confirmation ))
def select_menu(items, display_format=None, max_height=10): """ Presents a list of options and allows the user to select one. This presents a static list of options and prompts the user to select one. This is similar to a completion menu but is different in that it does not allow a user to type and the returned value is always a member of the list. :type items: list :param list: The list of items to be selected from. If this list contains elements that are not strings the display_format option must be specified. :type display_format: Callable[[Any], str] :param display_format: A callable that takes a single element from the items list as input and returns a string used to represent the item in the menu. :type max_height: int :param max_height: The max number of items to show in the list at a time. :returns: The selected element from the items list. """ app_bindings = KeyBindings() @app_bindings.add('c-c') def exit_app(event): event.app.exit(exception=KeyboardInterrupt, style='class:aborting') min_height = min(max_height, len(items)) menu_window = Window( SelectionMenuControl(items, display_format=display_format), always_hide_cursor=False, height=Dimension(min=min_height, max=min_height), scroll_offsets=ScrollOffsets(), right_margins=[ScrollbarMargin()], ) # Using a FloatContainer was the only way I was able to succesfully # limit the height and width of the window. content = FloatContainer( Window(height=Dimension(min=min_height, max=min_height)), [Float(menu_window, top=0, left=0)]) app = Application( layout=Layout(content), key_bindings=app_bindings, erase_when_done=True, ) return app.run()
def __init__(self, text='', style=''): self.percent = 0 self.container = VSplit( children=[ Window( style='class:info.progressbar.progress', width=lambda: Dimension(weight=int(self.percent)), height=1, ), Window( style='class:info.progressbar', width=lambda: Dimension(weight=int(100 - self.percent)), height=1, ), ], style=style, )
def render(self): choices_len = len(self._choices) buttons = [ Button('[%s] %s' % (i, label) if choices_len > 1 else label, handler=self._create_handler(sub_component)) for ((label, sub_component), i) in zip(self._choices, range( 1, choices_len + 1)) # this is still a loop even if it is inline.. ] self._first_button = buttons[0] global global__default_target_focus global__default_target_focus = self._first_button kb = create_vertical_button_list_kbs(buttons) is_first_selected = has_focus(buttons[0]) kb.add('up', filter=is_first_selected)(lambda e: focus_first_element()) return Box( HSplit( [ TextArea( text=self._label.format( name=self._controller.state.username) + '\n', read_only=True, focusable=False, scrollbar=True, # push buttons to bottom of screen height=Dimension(preferred=100000, max=100000)), HSplit([ VSplit(children=[button], align=HorizontalAlign.CENTER) for button in buttons ], padding=1, key_bindings=kb) ], padding=Dimension(preferred=2, max=2), width=Dimension(), align=VerticalAlign.TOP), padding=1)
def _get_default_buffer_control_height(self): # If there is an autocompletion menu to be shown, make sure that our # layout has at least a minimal height in order to display it. if (self.completer is not None and self.complete_style != CompleteStyle.READLINE_LIKE): space = self.reserve_space_for_menu else: space = 0 if space and not get_app().is_done: buff = self.default_buffer # Reserve the space, either when there are completions, or when # `complete_while_typing` is true and we expect completions very # soon. if buff.complete_while_typing() or buff.complete_state is not None: return Dimension(min=space) return Dimension()
def __init__(self, text, **kw): # The style needs to be applied to the toolbar as a whole, not just the # `FormattedTextControl`. style = kw.pop('style', '') super(FormattedTextToolbar, self).__init__(FormattedTextControl(text, **kw), style=style, dont_extend_height=True, height=Dimension(min=1))
def _build_layout(self) -> Layout: self._reset() layout = Layout( HSplit([ Window( FormattedTextControl(self._get_prompt), height=Dimension(1), dont_extend_height=True, always_hide_cursor=True, ), ConditionalContainer( Window( FormattedTextControl(self._get_choices_prompt), height=Dimension(1), dont_extend_height=True, ), ~is_done, ), ])) return layout
async def prompt_multiline_text_input(self, title, prompt: Union[str, Iterable[str]], initial_text=''): result = ImbTuiResult() input_done = asyncio.Event() dialog_body = [] if isinstance(prompt, str): dialog_body.append( Window(FormattedTextControl(prompt), height=1, align=WindowAlign.CENTER)) else: for line in prompt: dialog_body.append( Window(FormattedTextControl(line), height=1, align=WindowAlign.CENTER)) textfield = TextArea(text=initial_text, multiline=True, scrollbar=True) dialog_body.append(textfield) def ok_handler() -> None: result.value = textfield.text input_done.set() def back_handler() -> None: result.back_selected = True input_done.set() ok_button = Button(text='Ok', handler=ok_handler) back_button = Button(text='Back', handler=back_handler) dialog = Dialog( title=title, body=HSplit(dialog_body, padding=Dimension(preferred=1, max=1)), buttons=[ok_button, back_button], modal=False, ) dialog.container.container.content.style = "" self.app_frame.body = HSplit([ dialog, ]) self.app.invalidate() self.app.layout.focus(self.app_frame) await input_done.wait() return result
def __init__(self, title, text): self.future = Future() def set_done(): self.future.set_result(None) ok_button = Button(text="OK", handler=(lambda: set_done())) self.dialog = Dialog( title=title, body=HSplit([Label(text=text)]), buttons=[ok_button], width=Dimension(preferred=80), modal=True, )
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 _create_window_frame(self, editor_buffer): """ Create a Window for the buffer, with underneath a status bar. """ @Condition def wrap_lines(): return self.editor.wrap_lines window = Window( self._create_buffer_control(editor_buffer), allow_scroll_beyond_bottom=True, scroll_offsets=ScrollOffsets( left=0, right=0, top=(lambda: self.editor.scroll_offset), bottom=(lambda: self.editor.scroll_offset)), wrap_lines=wrap_lines, left_margins=[ ConditionalMargin(margin=NumberedMargin( display_tildes=True, relative=Condition(lambda: self.editor.relative_number)), filter=Condition( lambda: self.editor.show_line_numbers)) ], cursorline=Condition(lambda: self.editor.cursorline), cursorcolumn=Condition(lambda: self.editor.cursorcolumn), colorcolumns=( lambda: [ColorColumn(pos) for pos in self.editor.colorcolumn]), ignore_content_width=True, ignore_content_height=True, get_line_prefix=partial(self._get_line_prefix, editor_buffer.buffer)) return HSplit([ window, VSplit([ WindowStatusBar(self.editor, editor_buffer), WindowStatusBarRuler(self.editor, window, editor_buffer.buffer), ], width=Dimension()), # Ignore actual status bar width. ]), window
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 HelpScreen(controller): body = Box(TextArea(help_text, focusable=False, scrollbar=True), padding=0, padding_left=1, padding_right=1) def on_back_click(): new_state = controller.state.previous_state controller.set_state(new_state) buttons = [ Button('back', handler=on_back_click), Button('quit', handler=exit_current_app) ] kb = create_horizontal_button_list_kbs(buttons) toolbar_content = Box(VSplit(children=buttons, align=HorizontalAlign.CENTER, padding=Dimension(preferred=10, max=10), key_bindings=kb), height=1) return ToolbarFrame(body, toolbar_content)
def _create_layout(self): """ Create `Layout` for this prompt. """ dyncond = self._dyncond # Create functions that will dynamically split the prompt. (If we have # a multiline prompt.) has_before_fragments, get_prompt_text_1, get_prompt_text_2 = \ _split_multiline_prompt(self._get_prompt) default_buffer = self.default_buffer search_buffer = self.search_buffer # Create processors list. all_input_processors = [ HighlightIncrementalSearchProcessor(), HighlightSelectionProcessor(), ConditionalProcessor(AppendAutoSuggestion(), has_focus(default_buffer) & ~is_done), ConditionalProcessor(PasswordProcessor(), dyncond('is_password')), DisplayMultipleCursors(), # Users can insert processors here. DynamicProcessor( lambda: merge_processors(self.input_processors or [])), # For single line mode, show the prompt before the input. ConditionalProcessor( merge_processors([ BeforeInput(get_prompt_text_2), ShowArg(), ]), ~dyncond('multiline')) ] # Create bottom toolbars. bottom_toolbar = ConditionalContainer( Window(FormattedTextControl(lambda: self.bottom_toolbar, style='class:bottom-toolbar.text'), style='class:bottom-toolbar', dont_extend_height=True, height=Dimension(min=1)), filter=~is_done & renderer_height_is_known & Condition(lambda: self.bottom_toolbar is not None)) search_toolbar = SearchToolbar( search_buffer, ignore_case=dyncond('search_ignore_case')) search_buffer_control = SearchBufferControl( buffer=search_buffer, input_processors=[ ReverseSearchProcessor(), ShowArg(), ], ignore_case=dyncond('search_ignore_case')) system_toolbar = SystemToolbar( enable_global_bindings=dyncond('enable_system_prompt')) def get_search_buffer_control(): " Return the UIControl to be focused when searching start. " if _true(self.multiline): return search_toolbar.control else: return search_buffer_control default_buffer_control = BufferControl( buffer=default_buffer, search_buffer_control=get_search_buffer_control, input_processors=all_input_processors, include_default_input_processors=False, lexer=DynamicLexer(lambda: self.lexer), preview_search=True) default_buffer_window = Window( default_buffer_control, height=self._get_default_buffer_control_height, left_margins=[ # In multiline mode, use the window margin to display # the prompt and continuation fragments. ConditionalMargin( PromptMargin(get_prompt_text_2, self._get_continuation), filter=dyncond('multiline'), ) ], wrap_lines=dyncond('wrap_lines')) @Condition def multi_column_complete_style(): return self.complete_style == CompleteStyle.MULTI_COLUMN # Build the layout. layout = HSplit([ # The main input, with completion menus floating on top of it. FloatContainer( HSplit([ ConditionalContainer( Window(FormattedTextControl(get_prompt_text_1), dont_extend_height=True), Condition(has_before_fragments)), ConditionalContainer( default_buffer_window, Condition(lambda: get_app().layout.current_control != search_buffer_control), ), ConditionalContainer( Window(search_buffer_control), Condition(lambda: get_app().layout.current_control == search_buffer_control), ), ]), [ # Completion menus. Float(xcursor=True, ycursor=True, content=CompletionsMenu( max_height=16, scroll_offset=1, extra_filter=has_focus(default_buffer) & ~multi_column_complete_style)), Float(xcursor=True, ycursor=True, content=MultiColumnCompletionsMenu( show_meta=True, extra_filter=has_focus(default_buffer) & multi_column_complete_style)), # The right prompt. Float(right=0, top=0, hide_when_covering_content=True, content=_RPrompt(lambda: self.rprompt)), ]), ConditionalContainer(ValidationToolbar(), filter=~is_done), ConditionalContainer(system_toolbar, dyncond('enable_system_prompt') & ~is_done), # In multiline mode, we use two toolbars for 'arg' and 'search'. ConditionalContainer( Window(FormattedTextControl(self._get_arg_text), height=1), dyncond('multiline') & has_arg), ConditionalContainer(search_toolbar, dyncond('multiline') & ~is_done), bottom_toolbar, ]) return Layout(layout, default_buffer_window)
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 __init__( self, message: InquirerPyMessage, choices: InquirerPyListChoices, default: InquirerPyDefault = None, style: InquirerPyStyle = None, vi_mode: bool = False, qmark: str = "?", amark: str = "?", pointer: str = INQUIRERPY_POINTER_SEQUENCE, instruction: str = "", long_instruction: str = "", transformer: Callable[[Any], Any] = None, filter: Callable[[Any], Any] = None, height: Union[int, str] = None, max_height: Union[int, str] = None, multiselect: bool = False, marker: str = INQUIRERPY_POINTER_SEQUENCE, marker_pl: str = " ", border: bool = False, validate: InquirerPyValidate = None, invalid_message: str = "Invalid input", keybindings: Dict[str, List[Dict[str, Any]]] = None, show_cursor: bool = True, cycle: bool = True, wrap_lines: bool = True, raise_keyboard_interrupt: bool = True, mandatory: bool = True, mandatory_message: str = "Mandatory prompt", session_result: InquirerPySessionResult = None, ) -> None: if not hasattr(self, "_content_control"): self.content_control = InquirerPyListControl( choices=choices, default=default, pointer=pointer, marker=marker, session_result=session_result, multiselect=multiselect, marker_pl=marker_pl, ) super().__init__( message=message, style=style, border=border, vi_mode=vi_mode, qmark=qmark, amark=amark, instruction=instruction, long_instruction=long_instruction, transformer=transformer, filter=filter, validate=validate, invalid_message=invalid_message, multiselect=multiselect, keybindings=keybindings, cycle=cycle, wrap_lines=wrap_lines, raise_keyboard_interrupt=raise_keyboard_interrupt, mandatory=mandatory, mandatory_message=mandatory_message, session_result=session_result, ) self._show_cursor = show_cursor self._dimmension_height, self._dimmension_max_height = calculate_height( height, max_height, height_offset=self.height_offset) main_content_window = Window( content=self.content_control, height=Dimension( max=self._dimmension_max_height, preferred=self._dimmension_height, ), dont_extend_height=True, ) if self._border: main_content_window = Frame(main_content_window) self._layout = FloatContainer( content=HSplit([ MessageWindow( message=self._get_prompt_message_with_cursor if self._show_cursor else self._get_prompt_message, filter=True, wrap_lines=self._wrap_lines, show_cursor=self._show_cursor, ), ConditionalContainer(main_content_window, filter=~IsDone()), ConditionalContainer( Window(content=DummyControl()), filter=~IsDone() & self._is_displaying_long_instruction, ), InstructionWindow( message=self._long_instruction, filter=~IsDone() & self._is_displaying_long_instruction, wrap_lines=self._wrap_lines, ), ]), floats=[ ValidationFloat( invalid_message=self._get_error_message, filter=self._is_invalid & ~IsDone(), wrap_lines=self._wrap_lines, left=0, bottom=self._validation_window_bottom_offset, ), ], ) self.application = Application( layout=Layout(self._layout), style=self._style, key_bindings=self._kb, after_render=self._after_render, )
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), )