prompt-toolkit / prompt-toolkit/python-prompt-toolkit
HSplit: second window jumps to lower end of terminal
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 10.6k
- Forks
- 815
- PR merge metrics
- No merged PRs in 30d
Description
Hey there,
I'm using whaaaat, an inquirer.js clone based on prompt_toolkit to implement an internal CLI for our company. After building a minimal PoC, I found that when using prompt_toolkits HSplit component, the second window passed into the layout initially gets rendered correctly and then jumps to the bottom of the terminal window (see GIF). Also, this only happens when the Inquiry is started within a cleared window. I have already filed an issue at whaaaaat, but thought that you folks may be more suited to give me hint, since this seems to be caused by HSplit.
The library uses the following Layout:
layout = HSplit([
Window(content=TokenListControl(get_prompt_tokens, align_center=False)),
ConditionalContainer(
Window(
ic,
width=D.exact(43),
height=D(min=3),
scroll_offsets=ScrollOffsets(top=1, bottom=1)
),
filter=~IsDone()
)
])
where ic is an instance of InquirerControl implemented as follows:
class InquirerControl(TokenListControl):
def __init__(self, choices, **kwargs):
self.selected_option_index = 0
self.answered = False
self.choices = choices
self._init_choices(choices)
super(InquirerControl, self).__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 (name, value, disabled)
searching_first_choice = True
for i, c in enumerate(choices):
if isinstance(c, Separator):
self.choices.append((c, None, None))
else:
if isinstance(c, basestring):
self.choices.append((c, c, None))
else:
name = c.get('name')
value = c.get('value', name)
disabled = c.get('disabled', None)
self.choices.append((name, value, disabled))
if searching_first_choice:
self.selected_option_index = i # found the first choice
searching_first_choice = False
@property
def choice_count(self):
return len(self.choices)
def _get_choice_tokens(self, cli):
tokens = []
T = Token
def append(index, choice):
selected = (index == self.selected_option_index)
@if_mousedown
def select_item(cli, mouse_event):
# bind option with this index to mouse event
self.selected_option_index = index
self.answered = True
cli.set_return_value(None)
tokens.append((T.Pointer if selected else T, ' \u276f ' if selected
else ' '))
if selected:
tokens.append((Token.SetCursorPosition, ''))
if choice[2]: # disabled
tokens.append((T.Selected if selected else T,
'- %s (%s)' % (choice[0], choice[2])))
else:
tokens.append((T.Selected if selected else T, str(choice[0]),
select_item))
tokens.append((T, '\n'))
# prepare the select choices
for i, choice in enumerate(self.choices):
append(i, choice)
tokens.pop() # Remove last newline.
return tokens
def get_selection(self):
return self.choices[self.selected_option_index]
Gif with cleared window:
Gif without cleared window:

Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reproducing the minimal PoC with the shown prompt_toolkit HSplit layout in both cleared and uncleared terminal windows. Inspect HSplit rendering and terminal cursor or screen-position handling, then verify that the second window remains in its intended position in both cases.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- cli
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100