Source code for now.thirdparty.PyInquirer.prompts.expand

# -*- coding: utf-8 -*-
"""
`expand` type question
"""
from prompt_toolkit.application import Application
from prompt_toolkit.filters import IsDone
from prompt_toolkit.key_binding import KeyBindings
from prompt_toolkit.layout import Layout
from prompt_toolkit.layout.containers import ConditionalContainer, HSplit, Window
from prompt_toolkit.layout.controls import FormattedTextControl
from prompt_toolkit.layout.dimension import LayoutDimension as D

from ..separator import Separator
from . import PromptParameterException
from .common import default_style, if_mousedown

# custom control based on FormattedTextControl


[docs]class InquirerControl(FormattedTextControl): def __init__(self, choices, default=None, **kwargs): self.pointer_index = 0 self.answered = False self._init_choices(choices, default) self._help_active = False # help is activated via 'h' key super().__init__(self._get_choice_tokens, **kwargs) def _init_choices(self, choices, default=None): # helper to convert from question format to internal format self.choices = [] # list (key, name, value) for i, c in enumerate(choices): if isinstance(c, Separator): self.choices.append(c) else: if isinstance(c, str): self.choices.append((key, c, c)) else: key = c.get('key') name = c.get('name') value = c.get('value', name) self.choices.append([key, name, value]) # append the help choice self.choices.append(['h', 'Help, list all options', '__HELP__']) # set the default for i, choice in enumerate(self.choices): if isinstance(choice, list): key = choice[0] default = default or "h" if default == key: self.pointer_index = i choice[0] = key.upper() # default key is in uppercase @property def choice_count(self): return len(self.choices) def _get_choice_tokens(self): tokens = [] def _append(index, line): if isinstance(line, Separator): tokens.append(('class:separator', ' %s\n' % line)) else: key = line[0] line = line[1] pointed_at = index == self.pointer_index @if_mousedown def select_item(mouse_event): # bind option with this index to mouse event self.pointer_index = index if pointed_at: tokens.append( ('class:selected', ' %s) %s' % (key, line), select_item) ) else: tokens.append(('', ' %s) %s' % (key, line), select_item)) tokens.append(('', '\n')) if self._help_active: # prepare the select choices for i, choice in enumerate(self.choices): _append(i, choice) tokens.append(('', ' Answer: %s' % self.choices[self.pointer_index][0])) else: tokens.append(('class:pointer', '>> ')) tokens.append(('', self.choices[self.pointer_index][1])) return tokens
[docs] def get_selected_value(self): # get value not label return self.choices[self.pointer_index][2]
[docs]def question(message, **kwargs): # TODO extract common parts for list, checkbox, rawlist, expand # TODO up, down navigation if not 'choices' in kwargs: raise PromptParameterException('choices') choices = kwargs.pop('choices', None) default = kwargs.pop('default', None) qmark = kwargs.pop('qmark', '?') # TODO style defaults on detail level style = kwargs.pop('style', default_style) ic = InquirerControl(choices, default) def get_prompt_tokens(): tokens = [] tokens.append(('class:questionmark', qmark)) tokens.append(('class:question', ' %s ' % message)) if not ic.answered: tokens.append( ( 'class:instruction', ' (%s)' % ''.join( [k[0] for k in ic.choices if not isinstance(k, Separator)] ), ) ) else: tokens.append(('class:answer', ' %s' % ic.get_selected_value())) return tokens # @Condition # def is_help_active(): # return ic._help_active # assemble layout layout = HSplit( [ Window(height=D.exact(1), content=FormattedTextControl(get_prompt_tokens)), ConditionalContainer( Window(ic), # filter=is_help_active & ~IsDone() # ~ bitwise inverse filter=~IsDone(), # ~ bitwise inverse ), ] ) # key bindings kb = KeyBindings() @kb.add('c-q', eager=True) @kb.add('c-c', eager=True) def _(event): raise KeyboardInterrupt() # add key bindings for choices for i, c in enumerate(ic.choices): if not isinstance(c, Separator): def _reg_binding(i, keys): # trick out late evaluation with a "function factory": # http://stackoverflow.com/questions/3431676/creating-functions-in-a-loop @kb.add(keys, eager=True) def select_choice(event): ic.pointer_index = i if c[0] not in ['h', 'H']: _reg_binding(i, c[0]) if c[0].isupper(): _reg_binding(i, c[0].lower()) @kb.add('H', eager=True) @kb.add('h', eager=True) def help_choice(event): ic._help_active = not ic._help_active @kb.add('enter', eager=True) def set_answer(event): selected_value = ic.get_selected_value() if selected_value == '__HELP__': ic._help_active = True else: ic.answered = True event.app.exit(result=selected_value) return Application( layout=Layout(layout), key_bindings=kb, mouse_support=True, style=style )