Mercurial > urwid-satext
comparison urwid_satext/sat_widgets.py @ 70:24d49f1d735f
added TableContainer + some bug fixes (bad default parameters)
author | Goffi <goffi@goffi.org> |
---|---|
date | Thu, 30 Jan 2014 15:00:42 +0100 |
parents | b39c81cdd863 |
children | eddb8369ba06 |
comparison
equal
deleted
inserted
replaced
69:b39c81cdd863 | 70:24d49f1d735f |
---|---|
83 except AttributeError: | 83 except AttributeError: |
84 #No completion method defined | 84 #No completion method defined |
85 pass | 85 pass |
86 return super(AdvancedEdit, self).keypress(size, key) | 86 return super(AdvancedEdit, self).keypress(size, key) |
87 | 87 |
88 | |
88 class Password(AdvancedEdit): | 89 class Password(AdvancedEdit): |
89 """Edit box which doesn't show what is entered (show '*' or other char instead)""" | 90 """Edit box which doesn't show what is entered (show '*' or other char instead)""" |
90 | 91 |
91 def __init__(self, *args, **kwargs): | 92 def __init__(self, *args, **kwargs): |
92 """Same args than Edit.__init__ with an additional keyword arg 'hidden_char' | 93 """Same args than Edit.__init__ with an additional keyword arg 'hidden_char' |
108 self._edit_text = self.__real_text | 109 self._edit_text = self.__real_text |
109 super(Password,self).insert_text(text) | 110 super(Password,self).insert_text(text) |
110 | 111 |
111 def render(self, size, focus=False): | 112 def render(self, size, focus=False): |
112 return super(Password, self).render(size, focus) | 113 return super(Password, self).render(size, focus) |
114 | |
113 | 115 |
114 class ModalEdit(AdvancedEdit): | 116 class ModalEdit(AdvancedEdit): |
115 """AdvancedEdit with vi-like mode management | 117 """AdvancedEdit with vi-like mode management |
116 - there is a new 'mode' property which can be changed with properties | 118 - there is a new 'mode' property which can be changed with properties |
117 specified during init | 119 specified during init |
158 if self._mode == 'NORMAL' and key in self._modes: | 160 if self._mode == 'NORMAL' and key in self._modes: |
159 self.mode = self._modes[key][0] | 161 self.mode = self._modes[key][0] |
160 return | 162 return |
161 return super(ModalEdit, self).keypress(size, key) | 163 return super(ModalEdit, self).keypress(size, key) |
162 | 164 |
165 | |
163 class SurroundedText(urwid.Widget): | 166 class SurroundedText(urwid.Widget): |
164 """Text centered on a repeated character (like a Divider, but with a text in the center)""" | 167 """Text centered on a repeated character (like a Divider, but with a text in the center)""" |
165 _sizing = frozenset(['flow']) | 168 _sizing = frozenset(['flow']) |
166 | 169 |
167 def __init__(self,text,car=utf8decode('─')): | 170 def __init__(self,text,car=utf8decode('─')): |
177 def display_widget(self, size, focus): | 180 def display_widget(self, size, focus): |
178 (maxcol,) = size | 181 (maxcol,) = size |
179 middle = (maxcol-len(self.text))/2 | 182 middle = (maxcol-len(self.text))/2 |
180 render_text = middle * self.car + self.text + (maxcol - len(self.text) - middle) * self.car | 183 render_text = middle * self.car + self.text + (maxcol - len(self.text) - middle) * self.car |
181 return urwid.Text(render_text) | 184 return urwid.Text(render_text) |
185 | |
182 | 186 |
183 class SelectableText(urwid.WidgetWrap): | 187 class SelectableText(urwid.WidgetWrap): |
184 """Text which can be selected with space""" | 188 """Text which can be selected with space""" |
185 signals = ['change'] | 189 signals = ['change'] |
186 | 190 |
292 attr_list[idx] = (attr,attr_len) | 296 attr_list[idx] = (attr,attr_len) |
293 self._w.base_widget._invalidate() | 297 self._w.base_widget._invalidate() |
294 self.__was_focused = True #bloody ugly hack :) | 298 self.__was_focused = True #bloody ugly hack :) |
295 return self._w.render(size, focus) | 299 return self._w.render(size, focus) |
296 | 300 |
301 | |
297 class ClickableText(SelectableText): | 302 class ClickableText(SelectableText): |
298 signals = SelectableText.signals + ['click'] | 303 signals = SelectableText.signals + ['click'] |
299 | 304 |
300 def setState(self, selected, invisible=False): | 305 def setState(self, selected, invisible=False): |
301 super(ClickableText,self).setState(False,True) | 306 super(ClickableText,self).setState(False,True) |
302 if not invisible: | 307 if not invisible: |
303 self._emit('click') | 308 self._emit('click') |
309 | |
304 | 310 |
305 class CustomButton(ClickableText): | 311 class CustomButton(ClickableText): |
306 | 312 |
307 def __init__(self, label, on_press=None, user_data=None, left_border = "[ ", right_border = " ]"): | 313 def __init__(self, label, on_press=None, user_data=None, left_border = "[ ", right_border = " ]"): |
308 self.label = label | 314 self.label = label |
322 | 328 |
323 def set_label(self, label): | 329 def set_label(self, label): |
324 self.label = label | 330 self.label = label |
325 self.set_text([self.left_border, label, self.right_border]) | 331 self.set_text([self.left_border, label, self.right_border]) |
326 | 332 |
333 | |
327 class ListOption(unicode): | 334 class ListOption(unicode): |
328 """ Class similar to unicode, but which make the difference between value and label | 335 """ Class similar to unicode, but which make the difference between value and label |
329 label is show when use as unicode, the .value attribute contain the actual value | 336 label is show when use as unicode, the .value attribute contain the actual value |
330 Can be initialised with: | 337 Can be initialised with: |
331 - basestring (label = value = given string) | 338 - basestring (label = value = given string) |
332 - a tuple with (value, label) | 339 - a tuple with (value, label) |
333 XXX: comparaison is made again value, not the label which is the one displayed | 340 XXX: comparaison is made against value, not the label which is the one displayed |
334 | 341 |
335 """ | 342 """ |
336 | 343 |
337 def __new__(cls, option): | 344 def __new__(cls, option): |
338 if (isinstance(option, cls)): | 345 if (isinstance(option, cls)): |
341 value = label = option | 348 value = label = option |
342 elif (isinstance(option, tuple) and len(option) == 2): | 349 elif (isinstance(option, tuple) and len(option) == 2): |
343 value, label = option | 350 value, label = option |
344 else: | 351 else: |
345 raise NotImplementedError | 352 raise NotImplementedError |
346 if not value: | |
347 raise ValueError("value can't be empty") | |
348 if not label: | 353 if not label: |
349 label = value | 354 label = value |
350 instance = super(ListOption, cls).__new__(cls, label) | 355 instance = super(ListOption, cls).__new__(cls, label) |
351 instance._value = value | 356 instance._value = value |
352 return instance | 357 return instance |
385 | 390 |
386 | 391 |
387 class GenericList(urwid.WidgetWrap): | 392 class GenericList(urwid.WidgetWrap): |
388 signals = ['click','change'] | 393 signals = ['click','change'] |
389 | 394 |
390 def __init__(self, options, style=[], align='left', option_type = SelectableText, on_click=None, on_change=None, user_data=None): | 395 def __init__(self, options, style=None, align='left', option_type = SelectableText, on_click=None, on_change=None, user_data=None): |
391 """ | 396 """ |
392 Widget managing list of string and their selection | 397 Widget managing list of string and their selection |
393 @param options: list of strings used for options | 398 @param options: list of strings used for options |
394 @param style: list of string: | 399 @param style: list of string: |
395 - 'single' if only one must be selected | 400 - 'single' if only one must be selected |
397 - 'can_select_none' if we can select nothing | 402 - 'can_select_none' if we can select nothing |
398 @param align: alignement of text inside the list | 403 @param align: alignement of text inside the list |
399 @param on_click: method called when click signal is emited | 404 @param on_click: method called when click signal is emited |
400 @param user_data: data sent to the callback for click signal | 405 @param user_data: data sent to the callback for click signal |
401 """ | 406 """ |
407 if style is None: | |
408 style = [] | |
402 self.single = 'single' in style | 409 self.single = 'single' in style |
403 self.no_first_select = 'no_first_select' in style | 410 self.no_first_select = 'no_first_select' in style |
404 self.can_select_none = 'can_select_none' in style | 411 self.can_select_none = 'can_select_none' in style |
405 self.align = align | 412 self.align = align |
406 self.option_type = option_type | 413 self.option_type = option_type |
499 widget.setState(True) | 506 widget.setState(True) |
500 self.list_box.set_focus(idx) | 507 self.list_box.set_focus(idx) |
501 return | 508 return |
502 idx+=1 | 509 idx+=1 |
503 | 510 |
511 | |
504 class List(urwid.Widget): | 512 class List(urwid.Widget): |
505 """FlowWidget list, same arguments as GenericList, with an additional one 'max_height'""" | 513 """FlowWidget list, same arguments as GenericList, with an additional one 'max_height'""" |
506 signals = ['click','change'] | 514 signals = ['click','change'] |
507 _sizing = frozenset(['flow']) | 515 _sizing = frozenset(['flow']) |
508 | 516 |
509 def __init__(self, options, style=[], max_height=5, align='left', option_type = SelectableText, on_click=None, on_change=None, user_data=None): | 517 def __init__(self, options, style=None, max_height=5, align='left', option_type = SelectableText, on_click=None, on_change=None, user_data=None): |
518 if style is None: | |
519 style = [] | |
510 self.genericList = GenericList(options, style, align, option_type, on_click, on_change, user_data) | 520 self.genericList = GenericList(options, style, align, option_type, on_click, on_change, user_data) |
521 urwid.connect_signal(self.genericList, 'change', self._onChange) | |
522 urwid.connect_signal(self.genericList, 'click', self._onClick) | |
511 self.max_height = max_height | 523 self.max_height = max_height |
524 | |
525 def _onChange(self, widget): | |
526 self._emit('change') | |
527 | |
528 def _onClick(self, widget): | |
529 self._emit('click') | |
512 | 530 |
513 def selectable(self): | 531 def selectable(self): |
514 return True | 532 return True |
515 | 533 |
516 def keypress(self, size, key): | 534 def keypress(self, size, key): |
545 | 563 |
546 def displayWidget(self, size, focus): | 564 def displayWidget(self, size, focus): |
547 list_size = sum([wid.rows(size, focus) for wid in self.genericList.content]) | 565 list_size = sum([wid.rows(size, focus) for wid in self.genericList.content]) |
548 height = min(list_size,self.max_height) or 1 | 566 height = min(list_size,self.max_height) or 1 |
549 return urwid.BoxAdapter(self.genericList, height) | 567 return urwid.BoxAdapter(self.genericList, height) |
568 | |
550 | 569 |
551 ## MISC ## | 570 ## MISC ## |
552 | 571 |
553 class NotificationBar(urwid.WidgetWrap): | 572 class NotificationBar(urwid.WidgetWrap): |
554 """Bar used to show misc information to user""" | 573 """Bar used to show misc information to user""" |
662 | 681 |
663 def onClick(self, wid): | 682 def onClick(self, wid): |
664 self.selected = wid.getValue() | 683 self.selected = wid.getValue() |
665 self._emit('click') | 684 self._emit('click') |
666 | 685 |
686 | |
667 class Menu(urwid.WidgetWrap): | 687 class Menu(urwid.WidgetWrap): |
668 | 688 |
669 def __init__(self,loop, x_orig=0): | 689 def __init__(self,loop, x_orig=0): |
670 """Menu widget | 690 """Menu widget |
671 @param loop: main loop of urwid | 691 @param loop: main loop of urwid |
688 return len(self.menu_keys) | 708 return len(self.menu_keys) |
689 | 709 |
690 def setOrigX(self, orig_x): | 710 def setOrigX(self, orig_x): |
691 self.x_orig = orig_x | 711 self.x_orig = orig_x |
692 | 712 |
693 def __buildOverlay(self,menu_key,columns): | 713 def __buildOverlay(self, menu_key, columns): |
694 """Build the overlay menu which show menuitems | 714 """Build the overlay menu which show menuitems |
695 @param menu_key: name of the category | 715 @param menu_key: name of the category |
696 @colums: column number where the menubox must be displayed""" | 716 @param columns: column number where the menubox must be displayed""" |
697 max_len = 0 | 717 max_len = 0 |
698 for item in self.menu[menu_key]: | 718 for item in self.menu[menu_key]: |
699 if len(item[0]) > max_len: | 719 if len(item[0]) > max_len: |
700 max_len = len(item[0]) | 720 max_len = len(item[0]) |
701 | 721 |
825 | 845 |
826 ## DIALOGS ## | 846 ## DIALOGS ## |
827 | 847 |
828 class GenericDialog(urwid.WidgetWrap): | 848 class GenericDialog(urwid.WidgetWrap): |
829 | 849 |
830 def __init__(self, widgets_lst, title, style=[], **kwargs): | 850 def __init__(self, widgets_lst, title, style=None, **kwargs): |
851 if style is None: | |
852 style = [] | |
831 frame_header = urwid.AttrMap(urwid.Text(title,'center'),'title') | 853 frame_header = urwid.AttrMap(urwid.Text(title,'center'),'title') |
832 | 854 |
833 buttons = None | 855 buttons = None |
834 | 856 |
835 if "OK/CANCEL" in style: | 857 if "OK/CANCEL" in style: |
852 frame = FocusFrame(frame_body, frame_header, buttons_flow if buttons else None, 'footer' if buttons else 'body') | 874 frame = FocusFrame(frame_body, frame_header, buttons_flow if buttons else None, 'footer' if buttons else 'body') |
853 decorated_frame = urwid.LineBox(frame) | 875 decorated_frame = urwid.LineBox(frame) |
854 urwid.WidgetWrap.__init__(self, decorated_frame) | 876 urwid.WidgetWrap.__init__(self, decorated_frame) |
855 | 877 |
856 | 878 |
857 | |
858 class InputDialog(GenericDialog): | 879 class InputDialog(GenericDialog): |
859 """Dialog with an edit box""" | 880 """Dialog with an edit box""" |
860 | 881 |
861 def __init__(self, title, instrucions, style=['OK/CANCEL'], default_txt = '', **kwargs): | 882 def __init__(self, title, instrucions, style=None, default_txt = '', **kwargs): |
883 if style is None: | |
884 style = ['OK/CANCEL'] | |
862 instr_wid = urwid.Text(instrucions+':') | 885 instr_wid = urwid.Text(instrucions+':') |
863 edit_box = AdvancedEdit(edit_text=default_txt) | 886 edit_box = AdvancedEdit(edit_text=default_txt) |
864 GenericDialog.__init__(self, [instr_wid,edit_box], title, style, ok_value=edit_box, **kwargs) | 887 GenericDialog.__init__(self, [instr_wid,edit_box], title, style, ok_value=edit_box, **kwargs) |
865 self._w.base_widget.set_focus('body') | 888 self._w.base_widget.set_focus('body') |
866 | 889 |
890 | |
867 class ConfirmDialog(GenericDialog): | 891 class ConfirmDialog(GenericDialog): |
868 """Dialog with buttons for confirm or cancel an action""" | 892 """Dialog with buttons for confirm or cancel an action""" |
869 | 893 |
870 def __init__(self, title, style=['YES/NO'], **kwargs): | 894 def __init__(self, title, style=None, **kwargs): |
895 if style is None: | |
896 style = ['YES/NO'] | |
871 GenericDialog.__init__(self, [], title, style, **kwargs) | 897 GenericDialog.__init__(self, [], title, style, **kwargs) |
898 | |
872 | 899 |
873 class Alert(GenericDialog): | 900 class Alert(GenericDialog): |
874 """Dialog with just a message and a OK button""" | 901 """Dialog with just a message and a OK button""" |
875 | 902 |
876 def __init__(self, title, message, style=['OK'], **kwargs): | 903 def __init__(self, title, message, style=['OK'], **kwargs): |
877 GenericDialog.__init__(self, [urwid.Text(message, 'center')], title, style, ok_value=None, **kwargs) | 904 GenericDialog.__init__(self, [urwid.Text(message, 'center')], title, style, ok_value=None, **kwargs) |
905 | |
878 | 906 |
879 ## CONTAINERS ## | 907 ## CONTAINERS ## |
880 | 908 |
881 class ColumnsRoller(urwid.Widget): | 909 class ColumnsRoller(urwid.Widget): |
882 _sizing = frozenset(['flow']) | 910 _sizing = frozenset(['flow']) |
1029 widget = getattr(self,'_'+focus_name) | 1057 widget = getattr(self,'_'+focus_name) |
1030 if widget!=None and widget.selectable(): | 1058 if widget!=None and widget.selectable(): |
1031 self.set_focus(focus_name) | 1059 self.set_focus(focus_name) |
1032 | 1060 |
1033 return ret | 1061 return ret |
1062 | |
1034 | 1063 |
1035 class TabsContainer(urwid.WidgetWrap): | 1064 class TabsContainer(urwid.WidgetWrap): |
1036 signals = ['click'] | 1065 signals = ['click'] |
1037 | 1066 |
1038 def __init__(self): | 1067 def __init__(self): |
1076 if len(self._buttons_cont.widget_list) == 1: | 1105 if len(self._buttons_cont.widget_list) == 1: |
1077 #first button: we set the focus and the body | 1106 #first button: we set the focus and the body |
1078 self._buttons_cont.set_focus(0) | 1107 self._buttons_cont.set_focus(0) |
1079 self.__buttonClicked(button,True) | 1108 self.__buttonClicked(button,True) |
1080 | 1109 |
1081 def addTab(self,name,content=[]): | 1110 def addTab(self,name,content=None): |
1082 """Add a page to the container | 1111 """Add a page to the container |
1083 @param name: name of the page (what appear on the tab) | 1112 @param name: name of the page (what appear on the tab) |
1084 @param content: content of the page | 1113 @param content: content of the page |
1085 @return: ListBox (content of the page)""" | 1114 @return: ListBox (content of the page)""" |
1115 if content is None: | |
1116 content = [] | |
1086 listbox = urwid.ListBox(urwid.SimpleListWalker(content)) | 1117 listbox = urwid.ListBox(urwid.SimpleListWalker(content)) |
1087 self.tabs.append([name,listbox]) | 1118 self.tabs.append([name,listbox]) |
1088 self.__appendButton(name) | 1119 self.__appendButton(name) |
1089 return listbox | 1120 return listbox |
1090 | 1121 |
1092 """Add a widget on the bottom of the tab (will be displayed on all pages) | 1123 """Add a widget on the bottom of the tab (will be displayed on all pages) |
1093 @param widget: FlowWidget""" | 1124 @param widget: FlowWidget""" |
1094 self._w.footer = widget | 1125 self._w.footer = widget |
1095 | 1126 |
1096 | 1127 |
1128 class HighlightColumns(urwid.AttrMap): | |
1129 """ Decorated columns which highlight all or some columns """ | |
1130 | |
1131 def __init__(self, highlight_cols, highlight_attr, *args, **kwargs): | |
1132 """ Create the HighlightColumns | |
1133 @param highlight_cols: tuple of columns to highlight, () to highlight to whole row | |
1134 @param highlight_attr: name of the attribute to use when focused | |
1135 other parameter are passed to urwid Columns | |
1136 | |
1137 """ | |
1138 columns = urwid.Columns(*args, **kwargs) | |
1139 self.highlight_cols = highlight_cols | |
1140 self.highlight_attr = highlight_attr | |
1141 self.has_focus = False | |
1142 if highlight_cols == (): | |
1143 super(HighlightColumns, self).__init__(columns, None, highlight_attr) | |
1144 self.highlight_cols = None | |
1145 else: | |
1146 super(HighlightColumns, self).__init__(columns, None) | |
1147 | |
1148 @property | |
1149 def options(self): | |
1150 return self.base_widget.options | |
1151 | |
1152 @property | |
1153 def contents(self): | |
1154 return self.base_widget.contents | |
1155 | |
1156 @property | |
1157 def focus_position(self): | |
1158 return self.base_widget.focus_position | |
1159 | |
1160 @focus_position.setter | |
1161 def focus_position(self, value): | |
1162 self.base_widget.focus_position = value | |
1163 | |
1164 def addWidget(self, wid, options): | |
1165 """ Add a widget to the columns | |
1166 Widget is wrapped with AttrMap, that's why Columns.contents should not be used directly for appending new widgets | |
1167 @param wid: widget to add | |
1168 @param options: result of Columns.options(...) | |
1169 | |
1170 """ | |
1171 wrapper = urwid.AttrMap(wid, None) | |
1172 self.base_widget.contents.append((wrapper, options)) | |
1173 | |
1174 | |
1175 def render(self, size, focus=False): | |
1176 if self.highlight_cols and focus != self.has_focus: | |
1177 self.has_focus = focus | |
1178 for idx in self.highlight_cols: | |
1179 wid = self.base_widget.contents[idx][0] | |
1180 wid.set_attr_map({None: self.highlight_attr if focus else None}) | |
1181 | |
1182 return super(HighlightColumns, self).render(size, focus) | |
1183 | |
1184 | |
1185 class TableContainer(urwid.WidgetWrap): | |
1186 """ Widgets are disposed in row and columns """ | |
1187 signals = ['click'] | |
1188 | |
1189 def __init__(self, items=None, columns=None, dividechars=1, row_selectable=False, select_key='enter', options=None): | |
1190 """ Create a TableContainer | |
1191 @param items: iterable of widgets to add to this container | |
1192 @param columns: nb of columns of this table | |
1193 @param dividechars: same as dividechars param for urwid.Columns | |
1194 @param row_selectable: if True, row are always selectable, even if they don't contain any selectable widget | |
1195 @param options: dictionnary with the following keys: | |
1196 - ADAPT: tuple of columns for which the size must be adapted to its contents, | |
1197 empty tuple for all columns | |
1198 - HIGHLIGHT: tuple of columns which must be higlighted on focus, | |
1199 empty tuple for the whole row | |
1200 - FOCUS_ATTR: Attribute name to use when focused (see HIGHLIGHT). Default is "table_selected" | |
1201 | |
1202 """ | |
1203 pile = urwid.Pile([]) | |
1204 super(TableContainer, self).__init__(pile) | |
1205 if items is None: | |
1206 items = [] | |
1207 if columns is None: # if columns is None, we suppose only one row is given in items | |
1208 columns = len(items) | |
1209 assert columns | |
1210 self._columns = columns | |
1211 self._row_selectable = row_selectable | |
1212 self.select_key = select_key | |
1213 if options is None: | |
1214 options = {} | |
1215 for opt in ['ADAPT', 'HIGHLIGHT']: | |
1216 if opt in options: | |
1217 try: | |
1218 options[opt] = tuple(options[opt]) | |
1219 except TypeError: | |
1220 warning('[%s] option is not a tuple' % opt) | |
1221 options[opt] = () | |
1222 self._options = options | |
1223 self._dividechars = dividechars | |
1224 self._idx = 0 | |
1225 self._longuest = self._columns * [0] | |
1226 self._next_row_idx = None | |
1227 for item in items: | |
1228 self.addWidget(item) | |
1229 | |
1230 def _getIdealSize(self, widget): | |
1231 """ return preferred size for widget, or 0 if we can't find it """ | |
1232 try: | |
1233 return len(widget.text) | |
1234 except AttributeError: | |
1235 return 0 | |
1236 | |
1237 def keypress(self, size, key): | |
1238 if key == self.select_key and self._row_selectable: | |
1239 self._emit('click') | |
1240 else: | |
1241 return super(TableContainer, self).keypress(size, key) | |
1242 | |
1243 | |
1244 def addWidget(self, widget): | |
1245 # TODO: use a contents property ? | |
1246 pile = self._w | |
1247 col_idx = self._idx % self._columns | |
1248 | |
1249 options = None | |
1250 | |
1251 if col_idx == 0: | |
1252 # we have a new row | |
1253 columns = HighlightColumns(self._options.get('HIGHLIGHT'), self._options.get('FOCUS_ATTR', 'table_selected'), [], dividechars=self._dividechars) | |
1254 columns.row_idx = self._next_row_idx | |
1255 pile.contents.append((columns, pile.options())) | |
1256 else: | |
1257 columns = pile.contents[-1][0] | |
1258 | |
1259 if 'ADAPT' in self._options and (col_idx in self._options['ADAPT'] | |
1260 or self._options['ADAPT'] == ()): | |
1261 current_len = self._getIdealSize(widget) | |
1262 longuest = self._longuest[col_idx] | |
1263 max_len = max(longuest, current_len) | |
1264 if max_len > longuest: | |
1265 self._longuest[col_idx] = max_len | |
1266 for wid,_ in pile.contents[:-1]: | |
1267 col = wid.base_widget | |
1268 col.contents[col_idx] = (col.contents[col_idx][0], col.options('given', max_len)) | |
1269 options = columns.options('given', max_len) if max_len else columns.options() | |
1270 | |
1271 columns.addWidget(widget, options or columns.options()) | |
1272 | |
1273 if self._row_selectable and col_idx == self._columns - 1: | |
1274 columns.addWidget(urwid.SelectableIcon(''), columns.options('given', 0)) | |
1275 | |
1276 if not columns.selectable() and columns.contents[-1][0].base_widget.selectable(): | |
1277 columns.focus_position = len(columns.contents)-1 | |
1278 self._idx += 1 | |
1279 | |
1280 def setRowIndex(self, idx): | |
1281 self._next_row_idx = idx | |
1282 | |
1283 def getSelectedWidgets(self): | |
1284 columns = self._w.focus | |
1285 return (wid for wid, _ in columns.contents) | |
1286 | |
1287 def getSelectedIndex(self): | |
1288 columns = self._w.focus | |
1289 return columns.row_idx | |
1290 | |
1097 ## DECORATORS ## | 1291 ## DECORATORS ## |
1098 class LabelLine(urwid.LineBox): | 1292 class LabelLine(urwid.LineBox): |
1099 """Like LineBox, but with a Label centered in the top line""" | 1293 """Like LineBox, but with a Label centered in the top line""" |
1100 | 1294 |
1101 def __init__(self, original_widget, label_widget): | 1295 def __init__(self, original_widget, label_widget): |
1102 urwid.LineBox.__init__(self, original_widget) | 1296 urwid.LineBox.__init__(self, original_widget) |
1103 top_columns = self._w.widget_list[0] | 1297 top_columns = self._w.widget_list[0] |
1104 top_columns.widget_list[1] = label_widget | 1298 top_columns.widget_list[1] = label_widget |
1299 | |
1105 | 1300 |
1106 class VerticalSeparator(urwid.WidgetDecoration, urwid.WidgetWrap): | 1301 class VerticalSeparator(urwid.WidgetDecoration, urwid.WidgetWrap): |
1107 def __init__(self, original_widget, left_char = u"│", right_char = ''): | 1302 def __init__(self, original_widget, left_char = u"│", right_char = ''): |
1108 """Draw a separator on left and/or right of original_widget.""" | 1303 """Draw a separator on left and/or right of original_widget.""" |
1109 | 1304 |