Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,16 @@ choices = {
question = questions.Checkbox("foo", "Choose one:", choices=choices.keys(), hints=choices)
```

### max_options_displayed_at_once

**Optional** for `Checkbox` and `List` questions. When `None` (default), at most 13 choices are visible at once and the list scrolls around the cursor. Set it to a positive integer to display more (or fewer) choices in the visible window.

```python
from inquirer import questions
choices = [f"item-{i}" for i in range(200)]
question = questions.List("pick", "Pick one:", choices=choices, max_options_displayed_at_once=25)
```

### validate

Optional attribute that allows the program to check if the answer is valid or not. It requires a `boolean` value or a `function` with the signature:
Expand Down
4 changes: 4 additions & 0 deletions src/inquirer/questions.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,10 +157,12 @@ def __init__(
carousel=False,
other=False,
autocomplete=None,
max_options_displayed_at_once=None,
):
super().__init__(name, message, choices, default, ignore, validate, hints=hints, other=other)
self.carousel = carousel
self.autocomplete = autocomplete
self.max_options_displayed_at_once = max_options_displayed_at_once


class Checkbox(Question):
Expand All @@ -179,11 +181,13 @@ def __init__(
carousel=False,
other=False,
autocomplete=None,
max_options_displayed_at_once=None,
):
super().__init__(name, message, choices, default, ignore, validate, hints=hints, other=other)
self.locked = locked
self.carousel = carousel
self.autocomplete = autocomplete
self.max_options_displayed_at_once = max_options_displayed_at_once


class Path(Text):
Expand Down
20 changes: 15 additions & 5 deletions src/inquirer/render/console/_checkbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
from inquirer.render.console._other import GLOBAL_OTHER_CHOICE
from inquirer.render.console.base import MAX_OPTIONS_DISPLAYED_AT_ONCE
from inquirer.render.console.base import BaseConsoleRender
from inquirer.render.console.base import half_options


class Checkbox(BaseConsoleRender):
Expand All @@ -25,22 +24,33 @@ def default_choices(self):
default = self.question.default or []
return default + self.locked

@property
def max_options_displayed(self):
value = getattr(self.question, "max_options_displayed_at_once", None)
return value if value else MAX_OPTIONS_DISPLAYED_AT_ONCE

@property
def half_options(self):
return int((self.max_options_displayed - 1) / 2)

@property
def is_long(self):
choices = self.question.choices or []
return len(choices) >= MAX_OPTIONS_DISPLAYED_AT_ONCE
return len(choices) >= self.max_options_displayed

def get_options(self):
choices = self.question.choices or []
max_options = self.max_options_displayed
half_options = self.half_options
if self.is_long:
cmin = 0
cmax = MAX_OPTIONS_DISPLAYED_AT_ONCE
cmax = max_options

if half_options < self.current < len(choices) - half_options:
cmin += self.current - half_options
cmax += self.current - half_options
elif self.current >= len(choices) - half_options:
cmin += len(choices) - MAX_OPTIONS_DISPLAYED_AT_ONCE
cmin += len(choices) - max_options
cmax += len(choices)

cchoices = choices[cmin:cmax]
Expand All @@ -55,7 +65,7 @@ def get_options(self):
if (
(is_in_middle and self.current - half_options + index in self.selection)
or (is_in_beginning and index in self.selection)
or (is_in_end and index + max(len(choices) - MAX_OPTIONS_DISPLAYED_AT_ONCE, 0) in self.selection)
or (is_in_end and index + max(len(choices) - max_options, 0) in self.selection)
): # noqa
symbol = self.theme.Checkbox.selected_icon
color = self.theme.Checkbox.selected_color
Expand Down
18 changes: 14 additions & 4 deletions src/inquirer/render/console/_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,26 @@
from inquirer.render.console._other import GLOBAL_OTHER_CHOICE
from inquirer.render.console.base import MAX_OPTIONS_DISPLAYED_AT_ONCE
from inquirer.render.console.base import BaseConsoleRender
from inquirer.render.console.base import half_options


class List(BaseConsoleRender):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.current = self._current_index()

@property
def max_options_displayed(self):
value = getattr(self.question, "max_options_displayed_at_once", None)
return value if value else MAX_OPTIONS_DISPLAYED_AT_ONCE

@property
def half_options(self):
return int((self.max_options_displayed - 1) / 2)

@property
def is_long(self):
choices = self.question.choices or []
return len(choices) >= MAX_OPTIONS_DISPLAYED_AT_ONCE
return len(choices) >= self.max_options_displayed

def get_hint(self):
try:
Expand All @@ -30,15 +38,17 @@ def get_hint(self):

def get_options(self):
choices = self.question.choices or []
max_options = self.max_options_displayed
half_options = self.half_options
if self.is_long:
cmin = 0
cmax = MAX_OPTIONS_DISPLAYED_AT_ONCE
cmax = max_options

if half_options < self.current < len(choices) - half_options:
cmin += self.current - half_options
cmax += self.current - half_options
elif self.current >= len(choices) - half_options:
cmin += len(choices) - MAX_OPTIONS_DISPLAYED_AT_ONCE
cmin += len(choices) - max_options
cmax += len(choices)

cchoices = choices[cmin:cmax]
Expand Down
30 changes: 30 additions & 0 deletions tests/integration/console_render/test_checkbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -428,3 +428,33 @@ def test_second_hint_is_shown(self):
sut.render(question)

self.assertInStdout("Bar")

def test_max_options_displayed_at_once_shows_more_choices(self):
stdin = helper.event_factory(key.ENTER)
message = "Pick some"
variable = "many"
choices = [f"opt-{i:03d}" for i in range(40)]

question = questions.Checkbox(
variable, message, choices=choices, max_options_displayed_at_once=25
)
sut = ConsoleRender(event_generator=stdin)
sut.render(question)

self.assertInStdout("opt-000")
self.assertInStdout("opt-024")
self.assertNotInStdout("opt-025")

def test_max_options_displayed_at_once_default_unchanged(self):
stdin = helper.event_factory(key.ENTER)
message = "Pick some"
variable = "many"
choices = [f"opt-{i:03d}" for i in range(40)]

question = questions.Checkbox(variable, message, choices=choices)
sut = ConsoleRender(event_generator=stdin)
sut.render(question)

self.assertInStdout("opt-000")
self.assertInStdout("opt-012")
self.assertNotInStdout("opt-013")
30 changes: 30 additions & 0 deletions tests/integration/console_render/test_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,3 +181,33 @@ def test_taggedValue_with_dict(self):
sut.render(question)

self.assertInStdout("bb")

def test_max_options_displayed_at_once_shows_more_choices(self):
stdin = helper.event_factory(key.ENTER)
message = "Pick one"
variable = "many"
choices = [f"opt-{i:03d}" for i in range(40)]

question = questions.List(
variable, message, choices=choices, max_options_displayed_at_once=25
)
sut = ConsoleRender(event_generator=stdin)
sut.render(question)

self.assertInStdout("opt-000")
self.assertInStdout("opt-024")
self.assertNotInStdout("opt-025")

def test_max_options_displayed_at_once_default_unchanged(self):
stdin = helper.event_factory(key.ENTER)
message = "Pick one"
variable = "many"
choices = [f"opt-{i:03d}" for i in range(40)]

question = questions.List(variable, message, choices=choices)
sut = ConsoleRender(event_generator=stdin)
sut.render(question)

self.assertInStdout("opt-000")
self.assertInStdout("opt-012")
self.assertNotInStdout("opt-013")