def __enter__(self) -> "ProgressBar": # Create UI Application. title_toolbar = ConditionalContainer( Window( FormattedTextControl(lambda: self.title), height=1, style="class:progressbar,title", ), filter=Condition(lambda: self.title is not None), ) bottom_toolbar = ConditionalContainer( Window( FormattedTextControl( lambda: self.bottom_toolbar, style="class:bottom-toolbar.text" ), style="class:bottom-toolbar", height=1, ), filter=~is_done & renderer_height_is_known & Condition(lambda: self.bottom_toolbar is not None), ) def width_for_formatter(formatter: Formatter) -> AnyDimension: # Needs to be passed as callable (partial) to the 'width' # parameter, because we want to call it on every resize. return formatter.get_width(progress_bar=self) progress_controls = [ Window( content=_ProgressControl(self, f), width=functools.partial(width_for_formatter, f), ) for f in self.formatters ] self.app: Application[None] = Application( min_redraw_interval=0.05, layout=Layout( HSplit( [ title_toolbar, VSplit( progress_controls, height=lambda: D( preferred=len(self.counters), max=len(self.counters) ), ), Window(), bottom_toolbar, ] ) ), style=self.style, key_bindings=self.key_bindings, refresh_interval=0.3, color_depth=self.color_depth, output=self.output, input=self.input, ) # Run application in different thread. def run() -> None: set_event_loop(self._app_loop) try: self.app.run() except BaseException as e: traceback.print_exc() print(e) ctx: contextvars.Context = contextvars.copy_context() self._thread = threading.Thread(target=ctx.run, args=(run,)) self._thread.start() # Attach WINCH signal handler in main thread. # (Interrupt that we receive during resize events.) self._has_sigwinch = hasattr(signal, "SIGWINCH") and in_main_thread() if self._has_sigwinch: self._previous_winch_handler = signal.getsignal(signal.SIGWINCH) self._loop.add_signal_handler(signal.SIGWINCH, self.invalidate) return self
def create_default_layout(app, message='', lexer=None, is_password=False, reserve_space_for_menu=False, get_prompt_tokens=None, get_bottom_toolbar_tokens=None, display_completions_in_columns=False, extra_input_processors=None, multiline=False): """ Generate default layout. Returns a ``Layout`` instance. :param message: Text to be used as prompt. :param lexer: Lexer to be used for the highlighting. :param is_password: `bool` or `CLIFilter`. When True, display input as '*'. :param reserve_space_for_menu: When True, make sure that a minimal height is allocated in the terminal, in order to display the completion menu. :param get_prompt_tokens: An optional callable that returns the tokens to be shown in the menu. (To be used instead of a `message`.) :param get_bottom_toolbar_tokens: An optional callable that returns the tokens for a toolbar at the bottom. :param display_completions_in_columns: `bool` or `CLIFilter`. Display the completions in multiple columns. :param multiline: `bool` or `CLIFilter`. When True, prefer a layout that is more adapted for multiline input. Text after newlines is automatically indented, and search/arg input is shown below the input, instead of replacing the prompt. """ assert isinstance(message, text_type) assert (get_bottom_toolbar_tokens is None or callable(get_bottom_toolbar_tokens)) assert get_prompt_tokens is None or callable(get_prompt_tokens) assert not (message and get_prompt_tokens) display_completions_in_columns = to_cli_filter( display_completions_in_columns) multiline = to_cli_filter(multiline) if get_prompt_tokens is None: get_prompt_tokens = lambda _: [(Token.Prompt, message)] get_prompt_tokens_1, get_prompt_tokens_2 = _split_multiline_prompt( get_prompt_tokens) # `lexer` is supposed to be a `Lexer` instance. But if a Pygments lexer # class is given, turn it into a PygmentsLexer. (Important for # backwards-compatibility.) try: if issubclass(lexer, Lexer): lexer = PygmentsLexer(lexer) except TypeError: # Happens when lexer is `None` or an instance of something else. pass # Create processors list. # (DefaultPrompt should always be at the end.) input_processors = [ ConditionalProcessor( # By default, only highlight search when the search # input has the focus. (Note that this doesn't mean # there is no search: the Vi 'n' binding for instance # still allows to jump to the next match in # navigation mode.) HighlightSearchProcessor(preview_search=Always()), HasFocus(SEARCH_BUFFER)), HighlightSelectionProcessor(), ConditionalProcessor(AppendAutoSuggestion(), HasFocus(DEFAULT_BUFFER) & ~IsDone()), ConditionalProcessor(PasswordProcessor(), is_password) ] if extra_input_processors: input_processors.extend(extra_input_processors) # Show the prompt before the input (using the DefaultPrompt processor. # This also replaces it with reverse-i-search and 'arg' when required. # (Only for single line mode.) input_processors.append( ConditionalProcessor(DefaultPrompt(get_prompt_tokens), ~multiline)) # Create bottom toolbar. if get_bottom_toolbar_tokens: toolbars = [ ConditionalContainer(Window(TokenListControl( get_bottom_toolbar_tokens, default_char=Char(' ', Token.Toolbar)), height=LayoutDimension.exact(1)), filter=~IsDone() & RendererHeightIsKnown()) ] else: toolbars = [] def get_height(cli): # 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 cli.is_done: return LayoutDimension(min=8) else: return LayoutDimension() def separator(): return ConditionalContainer(content=Window( height=LayoutDimension.exact(1), content=FillControl(u'\u2500', token=Token.Separator)), filter=HasDocumentation(app) & ~IsDone()) # Create and return Layout instance. return HSplit([ ConditionalContainer( Window(TokenListControl(get_prompt_tokens_1), dont_extend_height=True), filter=multiline, ), VSplit([ # In multiline mode, the prompt is displayed in a left pane. ConditionalContainer( Window( TokenListControl(get_prompt_tokens_2), dont_extend_width=True, ), filter=multiline, ), # The main input, with completion menus floating on top of it. FloatContainer( Window( BufferControl( input_processors=input_processors, lexer=lexer, # Enable preview_search, we want to have immediate # feedback in reverse-i-search mode. preview_search=Always()), get_height=get_height, ), [ Float(xcursor=True, ycursor=True, content=CompletionsMenu( max_height=16, scroll_offset=1, extra_filter=( HasFocus(DEFAULT_BUFFER) & ~display_completions_in_columns))), Float(xcursor=True, ycursor=True, content=MultiColumnCompletionsMenu( extra_filter=(HasFocus(DEFAULT_BUFFER) & display_completions_in_columns), show_meta=Always())) ]), ]), separator(), ConditionalContainer( content=Window(BufferControl(buffer_name='clidocs', ), height=LayoutDimension(max=15)), filter=HasDocumentation(app) & ~IsDone(), ), separator(), ValidationToolbar(), SystemToolbar(), # In multiline mode, we use two toolbars for 'arg' and 'search'. ConditionalContainer(ArgToolbar(), multiline), ConditionalContainer(SearchToolbar(), multiline), ] + toolbars)
def __init__(self, editor, manager, window_arrangement): self.editor = editor # Back reference to editor. self.manager = manager self.window_arrangement = window_arrangement # Mapping from (`window_arrangement.Window`, `EditorBuffer`) to a frame # (Layout instance). # We keep this as a cache in order to easily reuse the same frames when # the layout is updated. (We don't want to create new frames on every # update call, because that way, we would loose some state, like the # vertical scroll offset.) self._frames = {} self._fc = FloatContainer( content=VSplit([ Window(BufferControl()) # Dummy window ]), floats=[ Float(xcursor=True, ycursor=True, content=CompletionsMenu( max_height=12, scroll_offset=2, extra_filter=~HasFocus(COMMAND_BUFFER))), Float(content=BufferListOverlay(editor), bottom=1, left=0), Float(bottom=1, left=0, right=0, height=1, content=CompletionsToolbar( extra_filter=HasFocus(COMMAND_BUFFER) & ~bufferlist_overlay_visible_filter & Condition(lambda cli: editor.show_wildmenu))), Float(bottom=1, left=0, right=0, height=1, content=ValidationToolbar()), Float(bottom=1, left=0, right=0, height=1, content=MessageToolbarBar(editor)), Float(content=WelcomeMessageWindow(editor), height=WELCOME_MESSAGE_HEIGHT, width=WELCOME_MESSAGE_WIDTH), ]) self.layout = FloatContainer(content=HSplit([ TabsToolbar(editor), self._fc, CommandLine(), ReportMessageToolbar(editor), SystemToolbar(), SearchToolbar(vi_mode=True), ]), floats=[ Float(right=0, height=1, bottom=0, width=5, content=SimpleArgToolbar()), ])
def create_layout(buffer, lexer=None, reserve_space_for_menu=8, get_prompt_tokens=None, get_bottom_toolbar_tokens=None, extra_input_processors=None, multiline=False, wrap_lines=True): """ Creates a custom `Layout` for the Crash input REPL This layout includes: * a bottom left-aligned session toolbar container * a bottom right-aligned side-bar container +-------------------------------------------+ | cr> select 1; | | | | | +-------------------------------------------+ | bottom_toolbar_tokens sidebar_tokens | +-------------------------------------------+ """ input_processors = [ ConditionalProcessor( # Highlight the reverse-i-search buffer HighlightSearchProcessor(), has_focus(SEARCH_BUFFER)), ] + (extra_input_processors or []) lexer = PygmentsLexer(lexer, sync_from_start=True) sidebar_token = [ ('class:status-toolbar', "[ctrl+d]"), ('class:status-toolbar.text', " Exit") ] 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() search_toolbar = SearchToolbar() buf_ctrl_window = Window( BufferControl( buffer=buffer, search_buffer_control=search_toolbar.control, input_processors=input_processors, lexer=lexer, preview_search=True ), height=lambda: _get_buffer_control_height(buffer), wrap_lines=wrap_lines, left_margins=[ ConditionalMargin( PromptMargin(get_prompt_tokens), filter=multiline ) ] ) in_out_area = VSplit([ HSplit([ FloatContainer( HSplit([buf_ctrl_window]), [ Float( xcursor=True, ycursor=True, content=CompletionsMenu( max_height=16, scroll_offset=1, extra_filter=has_focus(DEFAULT_BUFFER) ) ), ] ), # reverse-i-search toolbar (ctrl+r) search_toolbar, ]) ]) bottom_toolbar = VSplit([ ConditionalContainer( Window( FormattedTextControl(get_bottom_toolbar_tokens), height=Dimension.exact(1) ), filter=~is_done & renderer_height_is_known ), ConditionalContainer( Window( FormattedTextControl(lambda: sidebar_token), height=Dimension.exact(1), align=WindowAlign.RIGHT ), filter=~is_done & renderer_height_is_known ) ]) return Layout(HSplit([in_out_area, bottom_toolbar]))
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 [])), ] # 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(), ], 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 is_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, get_line_prefix=partial( self._get_line_prefix, get_prompt_text_2=get_prompt_text_2), 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 __init__(self, exchange, get_paths_in_directory): super().__init__() self.use_default_bindings = False self._exchange = exchange self._input = TextArea(text="/project/", multiline=False) self._buffer = self._input.buffer self._buffer.cursor_position = len(self._buffer.text) self._completions_component = Completions() self._completer = AsyncCompleter(exchange, get_paths_in_directory) self._completer_status_component = AsyncCompleterStatus() self._bottom_toolbar = Window( FormattedTextControl( "[tab] Enter selected directory " "[return] Choose selected directory " "[arrows] Navigation " "[C-c] Quit" ), height=1, style="reverse", ) self._container = HSplit( [ Window(height=1), Window( FormattedTextControl( "Choose directory to synchronize to on Faculty Platform: " ), height=1, ), self._input, Window(height=1), self._completions_component.container, self._completer_status_component.container, ] ) self.main_container = HSplit( [VSplit([Window(width=2), self._container]), self._bottom_toolbar] ) self._buffer.on_text_changed += self._handle_text_changed self.bindings = KeyBindings() @self.bindings.add("down") def _(event): self._completions_component.move_selection_down() @self.bindings.add("up") def _(event): self._completions_component.move_selection_up() @self.bindings.add("tab") def _(event): current_selection = self._completions_component.current_selection() if current_selection is not None: self._buffer.cursor_position = 0 self._buffer.text = current_selection + "/" self._buffer.cursor_position = len(self._buffer.text) @self.bindings.add("enter") def _(event): current_selection = self._completions_component.current_selection() self._exchange.publish( Messages.VERIFY_REMOTE_DIRECTORY, current_selection ) @self.bindings.add("c-c") def _(event): self._exchange.publish(Messages.STOP_CALLED) self._exchange.subscribe( RemoteDirMessages.NEW_SUBDIRECTORIES_WALKED, lambda _: self._handle_text_changed(), ) self._exchange.subscribe( RemoteDirMessages.SUBDIRECTORY_WALKER_STATUS_CHANGE, lambda path: self._handle_walker_status_change(path), )
message1 = HTML("<ansibrightred>Hello</ansibrightred>") message2 = HTML("<ansibrightyellow>world!</ansibrightyellow>") message3 = HTML("<ansibrightyellow>(Esc to quit)</ansibrightyellow>") # widgety widget1 = Label(message1) widget2 = Label(message2) widget3 = Label(message3) # styl rámce style = "bg:#ansiblue #ansiwhite" # správce rozvržení vsplit = HSplit([ VSplit([Frame(widget1, style=style), Frame(widget2, style=style)]), Frame(TextArea(), title="Editor"), Frame(widget3, style=style) ]) layout = Layout(vsplit) # napojení na klávesové zkratky key_bindings = KeyBindings() @key_bindings.add('escape') def on_escape_press(event): """Callback funkce volaná při stisku klávesy Esc.""" print("\n\n[escape]\n\n") event.app.exit()
def button3_clicked(): text_area.text += "Button 3 clicked\n" def exit_clicked(): get_app().exit() button1 = Button("Button 1", handler=button1_clicked) button2 = Button("Button 2", handler=button2_clicked) button3 = Button("Button 3", handler=button3_clicked) button4 = Button("Exit", handler=exit_clicked) buttons = HSplit([button1, button2, button3, button4]) text_area = TextArea(focusable=False) # správce rozvržení root = VSplit([ Box(Frame(buttons, style="class:button_panel"), padding=2), Box(Frame(text_area, title="Events", style="class:edit_panel"), padding=2), ]) layout = Layout(root) # napojení na klávesové zkratky key_bindings = KeyBindings() key_bindings.add("up")(focus_previous) key_bindings.add("down")(focus_next)
btn_pause = Button("Pause", handler=tomato.pause) btn_reset = Button("Reset", handler=tomato.reset) btn_reset_all = Button("Reset All", handler=tomato.reset_all) btn_exit = Button("Exit", handler=exit_clicked) text_area = FormattedTextControl(focusable=False, show_cursor=False) text_window = Window( content=text_area, dont_extend_height=True, height=11, style="bg:#000000 #ffffff" ) root_container = Box( HSplit( [ VSplit( [btn_start, btn_pause, btn_reset, btn_reset_all, btn_exit], padding=1, style="bg:#cccccc", ), text_window, ] ), ) layout = Layout(container=root_container, focused_element=btn_start) # Key bindings. These values are set in pydoro_core/config.py. kb = KeyBindings() # WHY: string to action map to allow for easy configuration actions = { "focus_next": focus_next, "focus_previous": focus_previous,
radiolist = RadioList([ ("python_val", "Python"), ("java_val", "Java"), ("clojure_val", "Clojure"), ("perl_val", "Perl"), ]) checkbox1 = Checkbox("Checkbox 1") checkbox2 = Checkbox("Checkbox 2") checkbox3 = Checkbox("Checkbox 3") hl = HorizontalLine() button1 = Button("Show status", handler=show_status) button2 = Button("Exit", handler=exit_clicked) buttons = HSplit( [radiolist, hl, checkbox1, checkbox2, checkbox3, hl, button1, hl, button2]) text_area = TextArea(focusable=False) # správce rozvržení root = VSplit([ Box(Frame(buttons, style="bg:#ansiblue #ansiwhite"), padding=2), Box(Frame(text_area, title="Events"), padding=2), ]) layout = Layout(root) # napojení na klávesové zkratky key_bindings = KeyBindings() key_bindings.add("s-tab")(focus_previous) key_bindings.add("tab")(focus_next)
btn_pause = Button("Pause", handler=tomato.pause) btn_reset = Button("Reset", handler=tomato.reset) btn_reset_all = Button("Reset All", handler=tomato.reset_all) btn_exit = Button("Exit", handler=exit_clicked) text_area = TextArea(read_only=True, height=11, focusable=False) # Combine all the widgets in a UI. # The `Box` object ensures that padding will be inserted around the containing # widget. It adapts automatically, unless an explicit `padding` amount is given. root_container = Box( HSplit([ Label(text="Press `Tab` to move the focus."), HSplit([ VSplit( [btn_start, btn_pause, btn_reset, btn_reset_all, btn_exit], padding=1, style="bg:#cccccc", ), text_area, ]), ])) layout = Layout(container=root_container, focused_element=btn_start) # Key bindings. kb = KeyBindings() kb.add("tab")(focus_next) kb.add("s-tab")(focus_previous) kb.add("right")(focus_next) kb.add("left")(focus_previous) kb.add("q")(exit_clicked)
def edit_file(filename): manager = KeyBindingManager(enable_vi_mode=True, enable_system_prompt=True) manager.vi_state.input_mode = InputMode.NAVIGATION layout = HSplit([ Header(), Window(content=BufferControl(show_line_numbers=AlwaysOn(), lexer=get_lexer_for_filename( filename).__class__)), VimToolbar(filename, manager), VimInputBar(), SystemToolbar(), SearchToolbar(), ]) with codecs.open(filename, 'r', 'utf-8') as f: text = f.read() @manager.registry.add_binding(':', filter=ViStateFilter(manager.vi_state, InputMode.NAVIGATION)) def enter_command_mode(event): """ Entering command mode. """ event.cli.focus_stack.push('vim-input') manager.vi_state.input_mode = InputMode.INSERT @manager.registry.add_binding(Keys.Escape, filter=HasFocus('vim-input')) @manager.registry.add_binding(Keys.ControlC, filter=HasFocus('vim-input')) @manager.registry.add_binding( Keys.Backspace, filter=HasFocus('vim-input') & Condition(lambda cli: cli.buffers['vim-input'].text == '')) def leave_command_mode(event): """ Leaving command mode. """ event.cli.focus_stack.pop() manager.vi_state.input_mode = InputMode.NAVIGATION event.cli.buffers['vim-input'].document = Document() @manager.registry.add_binding(Keys.ControlJ, filter=HasFocus('vim-input')) def process_command(event): """ Handling of commands. """ text = event.current_buffer.text def save(): with codecs.open(filename, 'w', 'utf-8') as f: f.write(event.cli.buffers['default'].text) def leave_command_mode(): event.cli.focus_stack.pop() manager.vi_state.input_mode = InputMode.NAVIGATION event.cli.buffers['vim-input'].document = Document() if text == 'w': save() leave_command_mode() elif text in ('wq', 'wqa'): save() event.cli.set_return_value('') elif text in ('q', 'qa', 'q!', 'qa!'): event.cli.set_return_value('') else: leave_command_mode() # TODO: validation of other commands. cli = CommandLineInterface( layout=layout, renderer=Renderer(use_alternate_screen=True), key_bindings_registry=manager.registry, buffers={ 'default': Buffer(returnable=AlwaysOff(), is_multiline=True, initial_document=Document(text, 0)), 'vim-input': Buffer(returnable=AlwaysOff()), }, style=VimStyle, ) # Run interface. cli.read_input()
def main(): manager = KeyBindingManager(enable_system_prompt=True) D = LayoutDimension layout = HSplit([ VSplit([ Window(width=D(min=15, max=30, preferred=30), content=FillControl('a', token=Token.A)), Window(width=D.exact(1), content=FillControl('|', token=Token.Line)), Window(content=TokenListControl.static([(Token.HelloWorld, lipsum)])), Window(width=D.exact(1), content=FillControl('|', token=Token.Line)), Window(content=BufferControl(lexer=PythonLexer, show_line_numbers=Always(), input_processors=[ DefaultPrompt('python> '), AfterInput.static( ' <python', token=Token.AfterInput), ]), ), Window(width=D.exact(1), content=FillControl('|', token=Token.Line)), HSplit([ Window(width=D(max=40), height=D.exact(4), content=FillControl('b', token=Token.B)), Window(width=D(max=40), content=FillControl('f', token=Token.F)), Window(width=D.exact(30), height=D.exact(2), content=FillControl('c', token=Token.C)), ]), #CompletionsMenu(), ]), Window(height=D.exact(1), content=FillControl('-', token=Token.Line)), Window(height=D.exact(3), content=FillControl('d', token=Token.D)), SystemToolbar(), ArgToolbar(), CompletionsToolbar(), SearchToolbar(), ]) layout = FloatContainer(content=layout, floats=[ Float(xcursor=True, ycursor=True, content=VSplit([ Window(width=D.exact(5), content=FillControl( 'f', token=Token.F)), CompletionsMenu(), ])), ]) eventloop = create_eventloop() cli = CommandLineInterface(eventloop=eventloop, layout=layout, style=TestStyle, key_bindings_registry=manager.registry, buffer=Buffer(is_multiline=Always(), completer=TestCompleter())) cli.read_input() eventloop.close()
def create_layout(message='', lexer=None, reserve_space_for_menu=8, get_prompt_tokens=None, get_bottom_toolbar_tokens=None, extra_input_processors=None, multiline=False, wrap_lines=True): # Create processors list. input_processors = [ ConditionalProcessor( # Highlight the reverse-i-search buffer HighlightSearchProcessor(preview_search=True), HasFocus(SEARCH_BUFFER)), ] if extra_input_processors: input_processors.extend(extra_input_processors) lexer = PygmentsLexer(lexer, sync_from_start=True) multiline = to_cli_filter(multiline) sidebar_token = [(Token.Toolbar.Status.Key, "[ctrl+d]"), (Token.Toolbar.Status, " Exit")] sidebar_width = token_list_width(sidebar_token) get_prompt_tokens = lambda _: [(Token.Prompt, message)] get_sidebar_tokens = lambda _: sidebar_token def get_height(cli): # 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 cli.is_done: buff = cli.current_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 LayoutDimension(min=reserve_space_for_menu) return LayoutDimension() # Create and return Container instance. return HSplit([ VSplit([ HSplit([ # The main input, with completion menus floating on top of it. FloatContainer( HSplit([ Window( BufferControl( input_processors=input_processors, lexer=lexer, # enable preview search for reverse-i-search preview_search=True), get_height=get_height, wrap_lines=wrap_lines, left_margins=[ # In multiline mode, use the window margin to display # the prompt and continuation tokens. ConditionalMargin(PromptMargin( get_prompt_tokens), filter=multiline) ], ), ]), [ # Completion menu Float(xcursor=True, ycursor=True, content=CompletionsMenu(max_height=16, scroll_offset=1, extra_filter=HasFocus( DEFAULT_BUFFER))), ]), # reverse-i-search toolbar (ctrl+r) ConditionalContainer(SearchToolbar(), multiline), ]) ]), ] + [ VSplit([ # Left-Aligned Session Toolbar ConditionalContainer(Window(TokenListControl( get_bottom_toolbar_tokens), height=LayoutDimension.exact(1)), filter=~IsDone() & RendererHeightIsKnown()), # Right-Aligned Container ConditionalContainer(Window(TokenListControl(get_sidebar_tokens), height=LayoutDimension.exact(1), width=LayoutDimension.exact( sidebar_width)), filter=~IsDone() & RendererHeightIsKnown()) ]) ])
def start_app(store: RetroStore, connection_string: str = ''): kb = KeyBindings() @kb.add('c-q') def exit_(event): event.app.exit() @kb.add('c-r') def refresh_(event): refresh() def refresh(): items = store.list() texts = { Category.GOOD: StringIO(), Category.NEUTRAL: StringIO(), Category.BAD: StringIO(), } for item in items: texts[item.category].write(f'{item.key}. {item.text}\n') good_buffer.text = texts[Category.GOOD].getvalue() neutral_buffer.text = texts[Category.NEUTRAL].getvalue() bad_buffer.text = texts[Category.BAD].getvalue() @kb.add('c-m') def enter_(event): text = input_buffer.text if text.startswith('+'): input_buffer.reset() store.add_item(text[1:].strip(), Category.GOOD) elif text.startswith('.'): input_buffer.reset() store.add_item(text[1:].strip(), Category.NEUTRAL) elif text.startswith('-'): input_buffer.reset() store.add_item(text[1:].strip(), Category.BAD) elif text.startswith('mv '): cmd, key, column = text.split() categories = { '+': Category.GOOD, '.': Category.NEUTRAL, '-': Category.BAD, } input_buffer.reset() store.move_item(int(key), categories[column]) elif text.startswith('rm '): cmd, key = text.split() input_buffer.reset() store.remove(int(key)) refresh() @kb.add('c-p') def ping_(event): start = time() store.list(Category.GOOD) app.print_text(f'latency: {time() - start:.3f}') good_buffer = Buffer() neutral_buffer = Buffer() bad_buffer = Buffer() input_buffer = Buffer() input = Window(content=BufferControl(buffer=input_buffer), height=1) root_container = HSplit([ VSplit([ HSplit([ Window(content=FormattedTextControl(text=':)'), height=1, align=WindowAlign.CENTER), Window(height=1, char='-'), Window(content=BufferControl(buffer=good_buffer)), ], style="fg:white bold bg:ansigreen"), Window(width=2, char='|'), HSplit([ Window(content=FormattedTextControl(text=':|'), height=1, align=WindowAlign.CENTER), Window(height=1, char='-'), Window(content=BufferControl(buffer=neutral_buffer)), ], style="fg:white bold bg:ansiyellow"), Window(width=2, char='|'), HSplit([ Window(content=FormattedTextControl(text=':('), height=1, align=WindowAlign.CENTER), Window(height=1, char='-'), Window(content=BufferControl(buffer=bad_buffer)), ], style="fg:white bold bg:ansired"), ]), Window( content=FormattedTextControl(text=f'Invite: {connection_string}'), height=1, align=WindowAlign.CENTER), input ], style='bg:grey') layout = Layout(root_container) layout.focus(input) app = Application(layout=layout, key_bindings=kb, full_screen=True) async def active_refresh(): while True: refresh() await asyncio.sleep(2) app.create_background_task(active_refresh()) app.run()
def __init__( self, title=None, elements=None, width=None, height=None, align=WindowAlign.LEFT, get_bullet=None, allow_select=True, scrollbar=True, ): self.index = 0 self.get_bullet = get_bullet self.selected = -1 self.elements = elements or [] self.title = title self.allow_select = allow_select self.cursor = Point(0, 0) self.scrollbar = scrollbar self.control = FormattedTextControl( text=self._get_text, focusable=True, get_cursor_position=lambda: self.cursor, key_bindings=self.get_key_bindings(), ) # TODO: figure out how to the make it look nicer right_margins = [ ConditionalMargin( ScrollbarMargin(display_arrows=True), filter=Condition(lambda: self.scrollbar), ), ] self.title_window = FormattedTextArea( text=self.title, height=Dimension(min=1), width=Dimension(min=1), ) self.list_window = Window( content=self.control, width=width, height=height, always_hide_cursor=False, style="class:list", wrap_lines=True, dont_extend_height=True, dont_extend_width=False, cursorline=False, right_margins=right_margins, allow_scroll_beyond_bottom=True, get_line_prefix=self._get_line_prefix, ) self.window = HSplit( children=[ Box( self.title_window, padding=Dimension.exact(1), ), Box( self.list_window, padding=Dimension.exact(1), padding_top=Dimension.exact(0), ), ], height=Dimension(min=1), width=Dimension(min=1), )
# All the widgets for the UI. button1 = Button('Button 1', handler=button1_clicked) button2 = Button('Button 2', handler=button2_clicked) button3 = Button('Button 3', handler=button3_clicked) button4 = Button('Exit', handler=exit_clicked) text_area = TextArea(focusable=True) # Combine all the widgets in a UI. # The `Box` object ensures that padding will be inserted around the containing # widget. It adapts automatically, unless an explicit `padding` amount is given. root_container = Box( HSplit([ Label(text='Press `Tab` to move the focus.'), VSplit([ Box(body=HSplit([button1, button2, button3, button4], padding=1), padding=1, style='class:left-pane'), Box(body=Frame(text_area), padding=1, style='class:right-pane'), ]), ]), ) layout = Layout(container=root_container, focused_element=button1) # Key bindings. kb = KeyBindings() kb.add('tab')(focus_next) kb.add('s-tab')(focus_previous) # Styling. style = Style([ ('left-pane', 'bg:#888800 #000000'),
def __enter__(self): # Create UI Application. title_toolbar = ConditionalContainer( Window(FormattedTextControl(lambda: self.title), height=1, style='class:progressbar,title'), filter=Condition(lambda: self.title is not None)) bottom_toolbar = ConditionalContainer( Window(FormattedTextControl(lambda: self.bottom_toolbar, style='class:bottom-toolbar.text'), style='class:bottom-toolbar', height=1), filter=~is_done & renderer_height_is_known & Condition(lambda: self.bottom_toolbar is not None)) def width_for_formatter(formatter): # Needs to be passed as callable (partial) to the 'width' # parameter, because we want to call it on every resize. return formatter.get_width(progress_bar=self) progress_controls = [ Window(content=_ProgressControl(self, f), width=functools.partial(width_for_formatter, f)) for f in self.formatters ] self.app = Application( min_redraw_interval=.05, layout=Layout( HSplit([ title_toolbar, VSplit(progress_controls, height=lambda: D(preferred=len(self.counters), max=len(self.counters))), Window(), bottom_toolbar, ])), style=self.style, key_bindings=self.key_bindings, output=self.output, input=self.input) # Run application in different thread. def run(): with _auto_refresh_context(self.app, .3): try: self.app.run() except Exception as e: traceback.print_exc() print(e) self._thread = threading.Thread(target=run) self._thread.start() # Attach WINCH signal handler in main thread. # (Interrupt that we receive during resize events.) self._has_sigwinch = hasattr(signal, 'SIGWINCH') and in_main_thread() if self._has_sigwinch: self._previous_winch_handler = self._loop.add_signal_handler( signal.SIGWINCH, self.app.invalidate) return self
def _create_application(self, editing_mode, erase_when_done): def dyncond(attr_name): """ Dynamically take this setting from this 'Prompt' class. `attr_name` represents an attribute name of this class. Its value can either be a boolean or a `Filter`. This returns something that can be used as either a `Filter` or `Filter`. """ @Condition def dynamic(): value = getattr(self, attr_name) return to_filter(value)() return dynamic # 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) # Create buffers list. def accept(buff): """ Accept the content of the default buffer. This is called when the validation succeeds. """ self.app.set_result(buff.document.text) # Reset content before running again. self.app.pre_run_callables.append(buff.reset) default_buffer = Buffer( name=DEFAULT_BUFFER, # Make sure that complete_while_typing is disabled when # enable_history_search is enabled. (First convert to Filter, # to avoid doing bitwise operations on bool objects.) complete_while_typing=Condition( lambda: _true(self.complete_while_typing) and not _true( self.enable_history_search) and not self.complete_style == CompleteStyle.READLINE_LIKE), validate_while_typing=dyncond('validate_while_typing'), enable_history_search=dyncond('enable_history_search'), validator=DynamicValidator(lambda: self.validator), completer=ThreadedCompleter( completer=DynamicCompleter(lambda: self.completer), in_thread=dyncond('complete_in_thread'), ), history=DynamicHistory(lambda: self.history), auto_suggest=DynamicAutoSuggest(lambda: self.auto_suggest), accept_handler=accept, get_tempfile_suffix=lambda: self.tempfile_suffix) search_buffer = Buffer(name=SEARCH_BUFFER) # Create processors list. input_processor = merge_processors([ ConditionalProcessor( # By default, only highlight search when the search # input has the focus. (Note that this doesn't mean # there is no search: the Vi 'n' binding for instance # still allows to jump to the next match in # navigation mode.) HighlightSearchProcessor(preview_search=True), has_focus(search_buffer)), HighlightSelectionProcessor(), ConditionalProcessor(AppendAutoSuggestion(), has_focus(default_buffer) & ~is_done), ConditionalProcessor(PasswordProcessor(), dyncond('is_password')), DisplayMultipleCursors(), # Users can insert processors here. DynamicProcessor(lambda: self.extra_input_processor), # 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, get_search_state=lambda: default_buffer_control.get_search_state()) search_buffer_control = BufferControl(buffer=search_buffer, input_processor=merge_processors( [ ReverseSearchProcessor(), ShowArg(), ])) system_toolbar = SystemToolbar() 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, get_search_buffer_control=get_search_buffer_control, input_processor=input_processor, 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, ]) # Default key bindings. auto_suggest_bindings = load_auto_suggest_bindings() open_in_editor_bindings = load_open_in_editor_bindings() prompt_bindings = self._create_prompt_bindings() # Create application application = Application( layout=Layout(layout, default_buffer_window), style=DynamicStyle(lambda: self.style), include_default_pygments_style=dyncond( 'include_default_pygments_style'), clipboard=DynamicClipboard(lambda: self.clipboard), key_bindings=merge_key_bindings([ merge_key_bindings([ auto_suggest_bindings, ConditionalKeyBindings( open_in_editor_bindings, dyncond('enable_open_in_editor') & has_focus(DEFAULT_BUFFER)), prompt_bindings ]), ConditionalKeyBindings( system_toolbar.get_global_key_bindings(), dyncond('enable_system_prompt')), DynamicKeyBindings(lambda: self.extra_key_bindings), ]), mouse_support=dyncond('mouse_support'), editing_mode=editing_mode, erase_when_done=erase_when_done, reverse_vi_search_direction=True, # I/O. input=self.input, output=self.output) # During render time, make sure that we focus the right search control # (if we are searching). - This could be useful if people make the # 'multiline' property dynamic. ''' def on_render(app): multiline = _true(self.multiline) current_control = app.layout.current_control if multiline: if current_control == search_buffer_control: app.layout.current_control = search_toolbar.control app.invalidate() else: if current_control == search_toolbar.control: app.layout.current_control = search_buffer_control app.invalidate() app.on_render += on_render ''' return application, default_buffer, default_buffer_control
def create_default_layout(message='', lexer=None, is_password=False, reserve_space_for_menu=False, get_prompt_tokens=None, get_bottom_toolbar_tokens=None, extra_input_processors=None): """ Generate default layout. Returns a ``Layout`` instance. :param message: Text to be used as prompt. :param lexer: Pygments lexer to be used for the highlighting. :param is_password: When True, display input as '*'. :param reserve_space_for_menu: When True, make sure that a minimal height is allocated in the terminal, in order to display the completion menu. :param get_prompt_tokens: An optional callable that returns the tokens to be shown in the menu. (To be used instead of a `message`.) :param get_bottom_toolbar_tokens: An optional callable that returns the tokens for a toolbar at the bottom. """ assert isinstance(message, text_type) assert get_bottom_toolbar_tokens is None or callable( get_bottom_toolbar_tokens) assert get_prompt_tokens is None or callable(get_prompt_tokens) assert not (message and get_prompt_tokens) # Create processors list. # (DefaultPrompt should always be at the end.) input_processors = [ HighlightSearchProcessor(preview_search=Always()), HighlightSelectionProcessor() ] if is_password: input_processors.append(PasswordProcessor()) if extra_input_processors: input_processors.extend(extra_input_processors) if message: input_processors.append(DefaultPrompt.from_message(message)) else: input_processors.append(DefaultPrompt(get_prompt_tokens)) # Create bottom toolbar. if get_bottom_toolbar_tokens: toolbars = [ Window(TokenListControl(get_bottom_toolbar_tokens, default_char=Char(' ', Token.Toolbar)), height=LayoutDimension.exact(1), filter=~IsDone() & RendererHeightIsKnown()) ] else: toolbars = [] def get_height(cli): # 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 cli.is_done: return LayoutDimension(min=8) else: return LayoutDimension() # Create and return Layout instance. return HSplit([ FloatContainer( Window( BufferControl( input_processors=input_processors, lexer=lexer, # Enable preview_search, we want to have immediate feedback # in reverse-i-search mode. preview_search=Always()), get_height=get_height, ), [ Float(xcursor=True, ycursor=True, content=CompletionsMenu(max_height=16, extra_filter=HasFocus( DEFAULT_BUFFER))) ]), ValidationToolbar(), SystemToolbar(), ] + toolbars)
from xradios.tui.widget.display import Display from xradios.tui.widget.popup import PopupWindow from xradios.tui.widget.listview import ListView from xradios.tui.widget.prompt import Prompt from xradios.tui.widget.topbar import TopBar from xradios.tui.buffers.display import buffer as display_buffer from xradios.tui.buffers.popup import buffer as popup_buffer from xradios.tui.buffers.prompt import buffer as prompt_buffer from xradios.tui.buffers.listview import buffer as listview_buffer layout = Layout( FloatContainer( content=HSplit([ TopBar(message="Need help! Press `F1`."), Display(display_buffer), ListView(listview_buffer), Prompt(prompt_buffer), ]), modal=True, floats=[ # Help text as a float. Float( top=3, bottom=2, left=2, right=2, content=ConditionalContainer( content=PopupWindow(popup_buffer, title="Help"), filter=has_focus(popup_buffer), ), )