• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4#
5# Copyright (c) 2020 Huawei Device Co., Ltd.
6# Licensed under the Apache License, Version 2.0 (the "License");
7# you may not use this file except in compliance with the License.
8# You may obtain a copy of the License at
9#
10#     http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS,
14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15# See the License for the specific language governing permissions and
16# limitations under the License.
17#
18
19from __future__ import print_function
20from __future__ import unicode_literals
21import sys
22from prompt_toolkit.application import Application
23from prompt_toolkit.key_binding.manager import KeyBindingManager
24from prompt_toolkit.keys import Keys
25from prompt_toolkit.layout.containers import Window
26from prompt_toolkit.filters import IsDone
27from prompt_toolkit.layout.controls import TokenListControl
28from prompt_toolkit.layout.containers import ConditionalContainer
29from prompt_toolkit.layout.containers import HSplit
30from prompt_toolkit.layout.dimension import LayoutDimension as D
31from prompt_toolkit.token import Token
32from hb_internal.cts.common import Separator
33from hb_internal.cts.common import if_mousedown
34from hb_internal.cts.common import get_style
35from hb_internal.common.utils import OHOSException
36
37
38class InquirerControl(TokenListControl):
39    def __init__(self, choices, **kwargs):
40        self.selected_option_index = 0
41        self.answered = False
42        self.choices = choices
43        self._init_choices(choices)
44        super(InquirerControl, self).__init__(self._get_choice_tokens,
45                                              **kwargs)
46
47    def _init_choices(self, choices, default=None):
48        # helper to convert from question format to internal format
49        self.choices = []  # list (name, value, disabled)
50        searching_first_choice = True
51        for index, choice in enumerate(choices):
52            if isinstance(choice, Separator):
53                self.choices.append((choice, None, None))
54            else:
55                base_string = str if sys.version_info[0] >= 3 else None
56                if isinstance(choice, base_string):
57                    self.choices.append((choice, choice, None))
58                else:
59                    name = choice.get('name')
60                    value = choice.get('value', name)
61                    disabled = choice.get('disabled', None)
62                    self.choices.append((name, value, disabled))
63                if searching_first_choice:
64                    self.selected_option_index = index
65                    searching_first_choice = False
66
67    @property
68    def choice_count(self):
69        return len(self.choices)
70
71    def _get_choice_tokens(self, cli):
72        tokens = []
73        token = Token
74
75        def append(index, choice):
76            selected = (index == self.selected_option_index)
77
78            @if_mousedown
79            def select_item(cli, mouse_event):
80                # bind option with this index to mouse event
81                self.selected_option_index = index
82                self.answered = True
83
84            tokens.append((token.Pointer if selected else token, ' \u276f '
85                          if selected else '   '))
86            if selected:
87                tokens.append((Token.SetCursorPosition, ''))
88            if choice[2]:  # disabled
89                tokens.append((token.Selected if selected else token,
90                               '- %s (%s)' % (choice[0], choice[2])))
91            else:
92                if isinstance(choice[0], Separator):
93                    tokens.append((token.Separator,
94                                  str(choice[0]),
95                                  select_item))
96                else:
97                    try:
98                        tokens.append((token.Selected if selected else token,
99                                      str(choice[0]), select_item))
100                    except Exception:
101                        tokens.append((token.Selected if selected else
102                                      token, choice[0], select_item))
103            tokens.append((token, '\n'))
104
105        # prepare the select choices
106        for i, choice in enumerate(self.choices):
107            append(i, choice)
108        tokens.pop()  # Remove last newline.
109        return tokens
110
111    def get_selection(self):
112        return self.choices[self.selected_option_index]
113
114
115def question(message, **kwargs):
116    if 'choices' not in kwargs:
117        raise OHOSException("You must choose one platform.")
118
119    choices = kwargs.pop('choices', None)
120    qmark = kwargs.pop('qmark', '?')
121    style = kwargs.pop('style', get_style('terminal'))
122
123    inquirer_control = InquirerControl(choices)
124
125    def get_prompt_tokens(cli):
126        tokens = []
127
128        tokens.append((Token.QuestionMark, qmark))
129        tokens.append((Token.Question, ' %s ' % message))
130        if inquirer_control.answered:
131            tokens.append((Token.Answer, ' ' +
132                          inquirer_control.get_selection()[0]))
133        else:
134            tokens.append((Token.Instruction, ' (Use arrow keys)'))
135        return tokens
136
137    # assemble layout
138    layout = HSplit([
139        Window(height=D.exact(1),
140               content=TokenListControl(get_prompt_tokens)),
141        ConditionalContainer(
142            Window(inquirer_control),
143            filter=~IsDone()
144        )
145    ])
146
147    # key bindings
148    manager = KeyBindingManager.for_prompt()
149
150    @manager.registry.add_binding(Keys.ControlQ, eager=True)
151    @manager.registry.add_binding(Keys.ControlC, eager=True)
152    def _(event):
153        raise KeyboardInterrupt()
154
155    @manager.registry.add_binding(Keys.Down, eager=True)
156    def move_cursor_down(event):
157        def _next():
158            inquirer_control.selected_option_index = (
159                (inquirer_control.selected_option_index + 1) %
160                inquirer_control.choice_count)
161        _next()
162        while isinstance(inquirer_control.choices[
163            inquirer_control.selected_option_index][0], Separator) \
164                or inquirer_control.choices[
165                    inquirer_control.selected_option_index][2]:
166            _next()
167
168    @manager.registry.add_binding(Keys.Up, eager=True)
169    def move_cursor_up(event):
170        def _prev():
171            inquirer_control.selected_option_index = (
172                (inquirer_control.selected_option_index - 1) %
173                inquirer_control.choice_count)
174        _prev()
175        while isinstance(inquirer_control.choices[
176            inquirer_control.selected_option_index][0], Separator) \
177                or inquirer_control.choices[
178                    inquirer_control.selected_option_index][2]:
179            _prev()
180
181    @manager.registry.add_binding(Keys.Enter, eager=True)
182    def set_answer(event):
183        inquirer_control.answered = True
184        event.cli.set_return_value(inquirer_control.get_selection())
185
186    return Application(
187        layout=layout,
188        key_bindings_registry=manager.registry,
189        mouse_support=True,
190        style=style
191    )
192