1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
|
"""This module implements the listbox widget"""
from asciimatics.strings import ColouredText
from asciimatics.widgets.utilities import _enforce_width
from asciimatics.widgets.baselistbox import _BaseListBox
class ListBox(_BaseListBox):
"""
A ListBox is a widget that displays a list from which the user can select one option.
"""
def __init__(self, height, options, centre=False, label=None, name=None, add_scroll_bar=False,
parser=None, on_change=None, on_select=None, validator=None):
"""
:param height: The required number of input lines for this ListBox.
:param options: The options for each row in the widget.
:param centre: Whether to centre the selected line in the list.
:param label: An optional label for the widget.
:param name: The name for the ListBox.
:param parser: Optional parser to colour text.
:param on_change: Optional function to call when selection changes.
:param on_select: Optional function to call when the user actually selects an entry from
:param validator: Optional function to validate selection for this widget.
The `options` are a list of tuples, where the first value is the string to be displayed
to the user and the second is an interval value to identify the entry to the program.
For example:
options=[("First option", 1), ("Second option", 2)]
"""
super().__init__(
height, options, label=label, name=name, parser=parser, on_change=on_change,
on_select=on_select, validator=validator)
self._centre = centre
self._add_scroll_bar = add_scroll_bar
def update(self, frame_no):
self._draw_label()
# Prepare to calculate new visible limits if needed.
height = self._h
width = self._w - self._offset
# Clear out the existing box content
(colour, attr, background) = self._frame.palette["field"]
for i in range(height):
self._frame.canvas.print_at(
" " * self.width,
self._x + self._offset,
self._y + i,
colour, attr, background)
# Don't bother with anything else if there are no options to render.
if len(self._options) <= 0:
return
# Decide whether we need to show or hide the scroll bar and adjust width accordingly.
if self._add_scroll_bar:
self._add_or_remove_scrollbar(width, height, 0)
if self._scroll_bar:
width -= 1
# Render visible portion of the text.
y_offset = 0
if self._centre:
# Always make selected text the centre - not very compatible with scroll bars, but
# there's not much else I can do here.
self._start_line = self._line - (height // 2)
start_line = self._start_line
if self._start_line < 0:
y_offset = -self._start_line
start_line = 0
for i, (text, _) in enumerate(self._options):
if start_line <= i < start_line + height - y_offset:
colour, attr, background = self._pick_colours("field", i == self._line)
if len(text) > width:
text = text[:width - 3] + "..."
paint_text = _enforce_width(text, width, self._frame.canvas.unicode_aware)
paint_text += " " * (width - self.string_len(str(paint_text)))
self._frame.canvas.paint(
str(paint_text),
self._x + self._offset,
self._y + y_offset + i - start_line,
colour, attr, background,
colour_map=paint_text.colour_map if hasattr(paint_text, "colour_map") else None)
# And finally draw any scroll bar.
if self._scroll_bar:
self._scroll_bar.update()
def _find_option(self, search_value):
for text, value in self._options:
if text.startswith(search_value):
return value
return None
def _parse_option(self, option):
"""
Parse a single option for ColouredText.
:param option: the option to parse
:returns: the option parsed and converted to ColouredText.
"""
try:
return ColouredText(option.raw_text, self._parser)
except AttributeError:
return ColouredText(option, self._parser)
@property
def options(self):
"""
The list of options available for user selection
This is a list of tuples (<human readable string>, <internal value>).
"""
return self._options
@options.setter
def options(self, new_value):
# Set net list of options and then force an update to the current value to align with the new options.
self._options = self._parse_options(new_value)
self.value = self._value
|