comparison urwid_satext/sat_widgets.py @ 151:6689aa54b20c default tip

refactoring from camelCase -> snake_case: This libraries was using camelCase due for historical reasons (related to the use of Twisted in the initial project). This patch fixes it by using PEP8 compliant snake_case
author Goffi <goffi@goffi.org>
date Sat, 08 Apr 2023 15:38:18 +0200
parents aa8f46b43a71
children
comparison
equal deleted inserted replaced
150:aa8f46b43a71 151:6689aa54b20c
28 from .keys import action_key_map as a_key 28 from .keys import action_key_map as a_key
29 29
30 FOCUS_KEYS = (a_key['FOCUS_SWITCH'], a_key['FOCUS_UP'], a_key['FOCUS_DOWN']) 30 FOCUS_KEYS = (a_key['FOCUS_SWITCH'], a_key['FOCUS_UP'], a_key['FOCUS_DOWN'])
31 31
32 32
33 def getFocusDirection(key, inversed=False): 33 def get_focus_direction(key, inversed=False):
34 """Return direction and rotate boolean depending on key 34 """Return direction and rotate boolean depending on key
35 @param key: one of FOCUS_KEYS 35 @param key: one of FOCUS_KEYS
36 @param inversed: inverse directions if True 36 @param inversed: inverse directions if True
37 @return (tuple): (direction, rotate) where 37 @return (tuple): (direction, rotate) where
38 - direction is 1 or -1 38 - direction is 1 or -1
54 - C-k: remove everything on the right of the cursor 54 - C-k: remove everything on the right of the cursor
55 - C-w: remove the word on the back 55 - C-w: remove the word on the back
56 new behaviour: emit a 'click' signal when enter is pressed""" 56 new behaviour: emit a 'click' signal when enter is pressed"""
57 signals = urwid.Edit.signals + ['click'] 57 signals = urwid.Edit.signals + ['click']
58 58
59 def getValue(self): 59 def get_value(self):
60 return self.get_edit_text() 60 return self.get_edit_text()
61 61
62 def setCompletionMethod(self, callback): 62 def set_completion_method(self, callback):
63 """Define method called when completion is asked 63 """Define method called when completion is asked
64 64
65 @callback: method with 2 arguments: 65 @callback: method with 2 arguments:
66 - the text to complete (part after cursor position is ignored) 66 - the text to complete (part after cursor position is ignored)
67 - if there was already a completion, a dict with 67 - if there was already a completion, a dict with
173 self._mode = mode 173 self._mode = mode
174 self.set_caption(caption) 174 self.set_caption(caption)
175 if not mode_key: #we are in NORMAL mode 175 if not mode_key: #we are in NORMAL mode
176 self.set_edit_text('') 176 self.set_edit_text('')
177 177
178 def setCompletionMethod(self, callback): 178 def set_completion_method(self, callback):
179 """ Same as AdvancedEdit.setCompletionMethod, but with a third argument: current mode""" 179 """ Same as AdvancedEdit.set_completion_method, but with a third argument: current mode"""
180 super(ModalEdit, self).setCompletionMethod(lambda text,data: callback(text, data, self._mode)) 180 super(ModalEdit, self).set_completion_method(lambda text,data: callback(text, data, self._mode))
181 181
182 def keypress(self, size, key): 182 def keypress(self, size, key):
183 if key == a_key['MODAL_ESCAPE']: 183 if key == a_key['MODAL_ESCAPE']:
184 self.mode = "NORMAL" 184 self.mode = "NORMAL"
185 return 185 return
225 self._selected = False 225 self._selected = False
226 self._was_focused = False 226 self._was_focused = False
227 self.header = header 227 self.header = header
228 self.text = text 228 self.text = text
229 urwid.WidgetWrap.__init__(self, urwid.Text("",align=align)) 229 urwid.WidgetWrap.__init__(self, urwid.Text("",align=align))
230 self.setSelectedText(selected_text) 230 self.set_selected_text(selected_text)
231 self.setState(selected) 231 self.set_state(selected)
232 232
233 def getValue(self): 233 def get_value(self):
234 if isinstance(self.text,str): 234 if isinstance(self.text,str):
235 return self.text 235 return self.text
236 list_attr = self.text if isinstance(self.text, list) else [self.text] 236 list_attr = self.text if isinstance(self.text, list) else [self.text]
237 txt = "" 237 txt = ""
238 for attr in list_attr: 238 for attr in list_attr:
242 txt+=attr 242 txt+=attr
243 return txt 243 return txt
244 244
245 def get_text(self): 245 def get_text(self):
246 """for compatibility with urwid.Text""" 246 """for compatibility with urwid.Text"""
247 return self.getValue() 247 return self.get_value()
248 248
249 def set_text(self, text): 249 def set_text(self, text):
250 """/!\ set_text doesn't change self.selected_txt !""" 250 """/!\ set_text doesn't change self.selected_txt !"""
251 self.text = text 251 self.text = text
252 self.setState(self._selected,invisible=True) 252 self.set_state(self._selected,invisible=True)
253 253
254 def setSelectedText(self, text=None): 254 def set_selected_text(self, text=None):
255 """Text to display when selected 255 """Text to display when selected
256 256
257 @text: text as in urwid.Text or None for default value 257 @text: text as in urwid.Text or None for default value
258 """ 258 """
259 if text == None: 259 if text == None:
260 text = ('selected',self.getValue()) 260 text = ('selected',self.get_value())
261 self.selected_txt = text 261 self.selected_txt = text
262 if self._selected: 262 if self._selected:
263 self.setState(self._selected) 263 self.set_state(self._selected)
264 264
265 def _set_txt(self): 265 def _set_txt(self):
266 txt_list = [self.header] 266 txt_list = [self.header]
267 txt = self.selected_txt if self._selected else self.text 267 txt = self.selected_txt if self._selected else self.text
268 if isinstance(txt,list): 268 if isinstance(txt,list):
270 else: 270 else:
271 txt_list.append(txt) 271 txt_list.append(txt)
272 self._w.base_widget.set_text(txt_list) 272 self._w.base_widget.set_text(txt_list)
273 273
274 274
275 def setState(self, selected, invisible=False): 275 def set_state(self, selected, invisible=False):
276 """Change state 276 """Change state
277 277
278 @param selected: boolean state value 278 @param selected: boolean state value
279 @param invisible: don't emit change signal if True 279 @param invisible: don't emit change signal if True
280 """ 280 """
284 self._was_focused = False 284 self._was_focused = False
285 self._invalidate() 285 self._invalidate()
286 if not invisible: 286 if not invisible:
287 self._emit("change", self._selected) 287 self._emit("change", self._selected)
288 288
289 def getState(self): 289 def get_state(self):
290 return self._selected 290 return self._selected
291 291
292 def selectable(self): 292 def selectable(self):
293 return True 293 return True
294 294
295 def keypress(self, size, key): 295 def keypress(self, size, key):
296 if key in (a_key['TEXT_SELECT'], a_key['TEXT_SELECT2']): 296 if key in (a_key['TEXT_SELECT'], a_key['TEXT_SELECT2']):
297 self.setState(not self._selected) 297 self.set_state(not self._selected)
298 else: 298 else:
299 return key 299 return key
300 300
301 def mouse_event(self, size, event, button, x, y, focus): 301 def mouse_event(self, size, event, button, x, y, focus):
302 if is_mouse_press(event) and button == 1: 302 if is_mouse_press(event) and button == 1:
303 self.setState(not self._selected) 303 self.set_state(not self._selected)
304 return True 304 return True
305 305
306 return False 306 return False
307 307
308 def render(self, size, focus=False): 308 def render(self, size, focus=False):
338 338
339 339
340 class ClickableText(SelectableText): 340 class ClickableText(SelectableText):
341 signals = SelectableText.signals + ['click'] 341 signals = SelectableText.signals + ['click']
342 342
343 def setState(self, selected, invisible=False): 343 def set_state(self, selected, invisible=False):
344 super(ClickableText,self).setState(False,True) 344 super(ClickableText,self).set_state(False,True)
345 if not invisible: 345 if not invisible:
346 self._emit('click') 346 self._emit('click')
347 347
348 348
349 class CustomButton(ClickableText): 349 class CustomButton(ClickableText):
355 super(CustomButton, self).__init__([left_border, label, right_border], align=align) 355 super(CustomButton, self).__init__([left_border, label, right_border], align=align)
356 self.size = len(self.get_text()) 356 self.size = len(self.get_text())
357 if on_press: 357 if on_press:
358 urwid.connect_signal(self, 'click', on_press, user_data) 358 urwid.connect_signal(self, 'click', on_press, user_data)
359 359
360 def getSize(self): 360 def get_size(self):
361 """Return representation size of the button""" 361 """Return representation size of the button"""
362 return self.size 362 return self.size
363 363
364 def get_label(self): 364 def get_label(self):
365 return self.label[1] if isinstance(self.label,tuple) else self.label 365 return self.label[1] if isinstance(self.label,tuple) else self.label
421 @value.setter 421 @value.setter
422 def value(self, value): 422 def value(self, value):
423 self._value = value 423 self._value = value
424 424
425 @staticmethod 425 @staticmethod
426 def fromOptions(options): 426 def from_options(options):
427 """ convert a list of string/tuple options to a list of listOption 427 """ convert a list of string/tuple options to a list of listOption
428 @param options: list of managed option type (basestring, tuple) 428 @param options: list of managed option type (basestring, tuple)
429 return: list of ListOption 429 return: list of ListOption
430 """ 430 """
431 return [(ListOption(option)) for option in options] 431 return [(ListOption(option)) for option in options]
475 for content in contents: 475 for content in contents:
476 on_new(content) 476 on_new(content)
477 self._on_new = on_new 477 self._on_new = on_new
478 self._on_delete = on_delete 478 self._on_delete = on_delete
479 479
480 def __cbSingle(self, item, cb): 480 def __cb_single(self, item, cb):
481 try: 481 try:
482 cb(item) 482 cb(item)
483 except TypeError: 483 except TypeError:
484 pass 484 pass
485 485
486 def __cbMulti(self, items, cb): 486 def __cb_multi(self, items, cb):
487 if cb is not None: 487 if cb is not None:
488 for item in items: 488 for item in items:
489 cb(item) 489 cb(item)
490 490
491 def __add__(self, new_list): 491 def __add__(self, new_list):
492 self.__cbMulti(new_list, self._on_new) 492 self.__cb_multi(new_list, self._on_new)
493 return super(SimpleListWalkerWithCb, self).__add__(new_list) 493 return super(SimpleListWalkerWithCb, self).__add__(new_list)
494 494
495 def __delitem__(self, item): 495 def __delitem__(self, item):
496 self.__cbSingle(item, self._on_delete) 496 self.__cb_single(item, self._on_delete)
497 return super(SimpleListWalkerWithCb, self).__delitem__(item) 497 return super(SimpleListWalkerWithCb, self).__delitem__(item)
498 498
499 def __delslice__(self, i,j): 499 def __delslice__(self, i,j):
500 items = super(SimpleListWalkerWithCb, self).__getslice__(i,j) 500 items = super(SimpleListWalkerWithCb, self).__getslice__(i,j)
501 self.__cbMulti(items, self._on_delete) 501 self.__cb_multi(items, self._on_delete)
502 return super(SimpleListWalkerWithCb, self).__delslice(i,j) 502 return super(SimpleListWalkerWithCb, self).__delslice(i,j)
503 503
504 def __iadd__(self, y): 504 def __iadd__(self, y):
505 raise NotImplementedError 505 raise NotImplementedError
506 506
513 def __rmul__(self, n): 513 def __rmul__(self, n):
514 raise NotImplementedError 514 raise NotImplementedError
515 515
516 def __setitem__(self, i, y): 516 def __setitem__(self, i, y):
517 parent = super(SimpleListWalkerWithCb, self) 517 parent = super(SimpleListWalkerWithCb, self)
518 self.__cbSingle(y, self._on_new) 518 self.__cb_single(y, self._on_new)
519 to_delete = parent.__getitem__(i) 519 to_delete = parent.__getitem__(i)
520 self.__cbSingle(to_delete, self._on_delete) 520 self.__cb_single(to_delete, self._on_delete)
521 return parent.__setitem__(i, y) 521 return parent.__setitem__(i, y)
522 522
523 def __setslice__(self, i, j, y): 523 def __setslice__(self, i, j, y):
524 parent = super(SimpleListWalkerWithCb, self) 524 parent = super(SimpleListWalkerWithCb, self)
525 items_to_delete = parent.__getslice__(i,j) 525 items_to_delete = parent.__getslice__(i,j)
526 self.__cbMulti(items_to_delete, self._on_delete) 526 self.__cb_multi(items_to_delete, self._on_delete)
527 if hasattr(y, '__iter__'): 527 if hasattr(y, '__iter__'):
528 self.__cbMulti(y, self._on_new) 528 self.__cb_multi(y, self._on_new)
529 else: 529 else:
530 self.__cbSingle(y, self._on_new) 530 self.__cb_single(y, self._on_new)
531 return parent.__setslice__(i, j, y) 531 return parent.__setslice__(i, j, y)
532 532
533 def append(self, obj): 533 def append(self, obj):
534 self.__cbSingle(obj, self._on_new) 534 self.__cb_single(obj, self._on_new)
535 return super(SimpleListWalkerWithCb, self).append(obj) 535 return super(SimpleListWalkerWithCb, self).append(obj)
536 536
537 def extend(self, it): 537 def extend(self, it):
538 self.__cbMulti(it, self.__on_new) 538 self.__cb_multi(it, self.__on_new)
539 return super(SimpleListWalkerWithCb, self).extend(it) 539 return super(SimpleListWalkerWithCb, self).extend(it)
540 540
541 def insert(self, idx, obj): 541 def insert(self, idx, obj):
542 self.__cbSingle(obj, self.__on_new) 542 self.__cb_single(obj, self.__on_new)
543 return super(SimpleListWalkerWithCb, self).insert(idx, obj) 543 return super(SimpleListWalkerWithCb, self).insert(idx, obj)
544 544
545 def pop(self, idx=None): 545 def pop(self, idx=None):
546 if idx is None: 546 if idx is None:
547 idx=len(self)-1 547 idx=len(self)-1
548 548
549 parent = super(SimpleListWalkerWithCb, self) 549 parent = super(SimpleListWalkerWithCb, self)
550 to_remove = parent.__getitem__(idx) 550 to_remove = parent.__getitem__(idx)
551 self.__cbSingle(to_remove, self._on_delete) 551 self.__cb_single(to_remove, self._on_delete)
552 return parent.pop(idx) 552 return parent.pop(idx)
553 553
554 def remove(self, val): 554 def remove(self, val):
555 ret = super(SimpleListWalkerWithCb, self).remove(val) 555 ret = super(SimpleListWalkerWithCb, self).remove(val)
556 self.__cbSingle(val, self._on_delete) 556 self.__cb_single(val, self._on_delete)
557 return ret 557 return ret
558 558
559 559
560 class GenericList(urwid.ListBox): 560 class GenericList(urwid.ListBox):
561 signals = ['click','change'] 561 signals = ['click','change']
589 urwid.connect_signal(self, 'click', on_click, user_data) 589 urwid.connect_signal(self, 'click', on_click, user_data)
590 590
591 if on_change: 591 if on_change:
592 urwid.connect_signal(self, 'change', on_change, user_data) 592 urwid.connect_signal(self, 'change', on_change, user_data)
593 593
594 self.content = SimpleListWalkerWithCb([], self._addSignals, lambda widget: self._emit('change')) 594 self.content = SimpleListWalkerWithCb([], self._add_signals, lambda widget: self._emit('change'))
595 super(GenericList, self).__init__(self.content) 595 super(GenericList, self).__init__(self.content)
596 self.changeValues(options) 596 self.change_values(options)
597 597
598 def _addSignals(self, widget): 598 def _add_signals(self, widget):
599 for signal, callback in (('change', self._onStateChange), ('click', self._onClick)): 599 for signal, callback in (('change', self._on_state_change), ('click', self._on_click)):
600 try: 600 try:
601 urwid.connect_signal(widget, signal, callback) 601 urwid.connect_signal(widget, signal, callback)
602 except NameError: 602 except NameError:
603 pass #the widget given doesn't support the signal 603 pass #the widget given doesn't support the signal
604 604
605 @property 605 @property
606 def contents(self): 606 def contents(self):
607 return self.content 607 return self.content
608 608
609 def _onStateChange(self, widget, selected, *args): 609 def _on_state_change(self, widget, selected, *args):
610 if self.single: 610 if self.single:
611 if not selected and not self.can_select_none: 611 if not selected and not self.can_select_none:
612 #if in single mode, it's forbidden to unselect a value 612 #if in single mode, it's forbidden to unselect a value
613 widget.setState(True, invisible=True) 613 widget.set_state(True, invisible=True)
614 return 614 return
615 if selected: 615 if selected:
616 self.unselectAll(invisible=True) 616 self.unselect_all(invisible=True)
617 widget.setState(True, invisible=True) 617 widget.set_state(True, invisible=True)
618 self._emit("change", widget, selected, *args) 618 self._emit("change", widget, selected, *args)
619 619
620 def _onClick(self, widget, *args): 620 def _on_click(self, widget, *args):
621 if widget not in self.content: 621 if widget not in self.content:
622 urwid.disconnect_signal(widget, "click", self._onClick) 622 urwid.disconnect_signal(widget, "click", self._on_click)
623 return 623 return
624 self._emit("click", widget, *args) 624 self._emit("click", widget, *args)
625 625
626 def unselectAll(self, invisible=False): 626 def unselect_all(self, invisible=False):
627 for widget in self.content: 627 for widget in self.content:
628 if widget.getState(): 628 if widget.get_state():
629 widget.setState(False, invisible) 629 widget.set_state(False, invisible)
630 widget._invalidate() 630 widget._invalidate()
631 631
632 def deleteValue(self, value): 632 def delete_value(self, value):
633 """Delete the first value equal to the param given""" 633 """Delete the first value equal to the param given"""
634 for widget in self.content: 634 for widget in self.content:
635 if widget.getValue() == value: 635 if widget.get_value() == value:
636 self.content.remove(widget) 636 self.content.remove(widget)
637 self._emit('change') 637 self._emit('change')
638 return 638 return
639 raise ValueError("%s ==> %s" % (str(value),str(self.content))) 639 raise ValueError("%s ==> %s" % (str(value),str(self.content)))
640 640
641 def getSelectedValue(self): 641 def get_selected_value(self):
642 """Convenience method to get the value selected as a string in single mode, or None""" 642 """Convenience method to get the value selected as a string in single mode, or None"""
643 values = self.getSelectedValues() 643 values = self.get_selected_values()
644 return values[0] if values else None 644 return values[0] if values else None
645 645
646 def getAllValues(self): 646 def get_all_values(self):
647 """Return values of all items""" 647 """Return values of all items"""
648 return [widget.getValue() for widget in self.content] 648 return [widget.get_value() for widget in self.content]
649 649
650 def getSelectedValues(self): 650 def get_selected_values(self):
651 """Return values of selected items""" 651 """Return values of selected items"""
652 result = [] 652 result = []
653 for widget in self.content: 653 for widget in self.content:
654 if widget.getState(): 654 if widget.get_state():
655 result.append(widget.getValue()) 655 result.append(widget.get_value())
656 return result 656 return result
657 657
658 def on_option_change(self, wid, *args, **kwargs): 658 def on_option_change(self, wid, *args, **kwargs):
659 if self.single: 659 if self.single:
660 for w in self.content: 660 for w in self.content:
661 if w is not wid: 661 if w is not wid:
662 w.setState(False, invisible=True) 662 w.set_state(False, invisible=True)
663 663
664 def changeValues(self, new_values): 664 def change_values(self, new_values):
665 """Change all values in one shot""" 665 """Change all values in one shot"""
666 new_values = ListOption.fromOptions(new_values) 666 new_values = ListOption.from_options(new_values)
667 old_selected = self.getSelectedValues() if not self.first_display else [] 667 old_selected = self.get_selected_values() if not self.first_display else []
668 widgets = [] 668 widgets = []
669 for option in new_values: 669 for option in new_values:
670 widget = self.option_type(option, align=self.align) 670 widget = self.option_type(option, align=self.align)
671 urwid.connect_signal(widget, "change", self.on_option_change) 671 urwid.connect_signal(widget, "change", self.on_option_change)
672 if not self.first_display and option in old_selected: 672 if not self.first_display and option in old_selected:
673 widget.setState(True) 673 widget.set_state(True)
674 widgets.append(widget) 674 widgets.append(widget)
675 self.content[:] = widgets 675 self.content[:] = widgets
676 if self.first_display and self.single and new_values and not self.no_first_select: 676 if self.first_display and self.single and new_values and not self.no_first_select:
677 self.content[0].setState(True) 677 self.content[0].set_state(True)
678 self._emit('change') 678 self._emit('change')
679 self.first_display = False 679 self.first_display = False
680 680
681 def selectValue(self, value, move_focus=True): 681 def select_value(self, value, move_focus=True):
682 """Select the first item which has the given value. 682 """Select the first item which has the given value.
683 683
684 @param value 684 @param value
685 @param move_focus (bool): 685 @param move_focus (bool):
686 - True to move the focus on the selected value, 686 - True to move the focus on the selected value,
687 - False to leave the focus position unchanged. 687 - False to leave the focus position unchanged.
688 688
689 """ 689 """
690 self.unselectAll() 690 self.unselect_all()
691 idx = 0 691 idx = 0
692 for widget in self.content: 692 for widget in self.content:
693 if widget.getValue() == value: 693 if widget.get_value() == value:
694 widget.setState(True) 694 widget.set_state(True)
695 if move_focus: 695 if move_focus:
696 self.focus_position = idx 696 self.focus_position = idx
697 return 697 return
698 idx+=1 698 idx+=1
699 699
700 def selectValues(self, values, move_focus=True): 700 def select_values(self, values, move_focus=True):
701 """Select all the given values. 701 """Select all the given values.
702 702
703 @param values [set, list] 703 @param values [set, list]
704 @param move_focus (boolean): True to move the focus on the last selected value, 704 @param move_focus (boolean): True to move the focus on the last selected value,
705 False to leave the focus position unchanged. 705 False to leave the focus position unchanged.
706 """ 706 """
707 if self.single: 707 if self.single:
708 if values: 708 if values:
709 self.selectValue(values[-1], move_focus) 709 self.select_value(values[-1], move_focus)
710 return 710 return
711 self.unselectAll() 711 self.unselect_all()
712 for value in values: 712 for value in values:
713 idx = 0 713 idx = 0
714 for widget in self.content: 714 for widget in self.content:
715 if widget.getValue() == value: 715 if widget.get_value() == value:
716 widget.setState(True) 716 widget.set_state(True)
717 if move_focus: 717 if move_focus:
718 self.focus_position = idx 718 self.focus_position = idx
719 idx += 1 719 idx += 1
720 720
721 721
737 737
738 def selectable(self): 738 def selectable(self):
739 return True 739 return True
740 740
741 def get_cursor_coords(self, size): 741 def get_cursor_coords(self, size):
742 return self.genericList.get_cursor_coords((size[0], self._getHeight(size, True))) 742 return self.genericList.get_cursor_coords((size[0], self._get_height(size, True)))
743 743
744 def keypress(self, size, key): 744 def keypress(self, size, key):
745 return self.displayWidget(size,True).keypress(size, key) 745 return self.display_widget(size,True).keypress(size, key)
746 746
747 def unselectAll(self, invisible=False): 747 def unselect_all(self, invisible=False):
748 return self.genericList.unselectAll(invisible) 748 return self.genericList.unselect_all(invisible)
749 749
750 def deleteValue(self, value): 750 def delete_value(self, value):
751 return self.genericList.deleteValue(value) 751 return self.genericList.delete_value(value)
752 752
753 def getSelectedValue(self): 753 def get_selected_value(self):
754 return self.genericList.getSelectedValue() 754 return self.genericList.get_selected_value()
755 755
756 def getAllValues(self): 756 def get_all_values(self):
757 return self.genericList.getAllValues() 757 return self.genericList.get_all_values()
758 758
759 def getSelectedValues(self): 759 def get_selected_values(self):
760 return self.genericList.getSelectedValues() 760 return self.genericList.get_selected_values()
761 761
762 def changeValues(self, new_values): 762 def change_values(self, new_values):
763 return self.genericList.changeValues(new_values) 763 return self.genericList.change_values(new_values)
764 764
765 def selectValue(self, value, move_focus=True): 765 def select_value(self, value, move_focus=True):
766 return self.genericList.selectValue(value, move_focus) 766 return self.genericList.select_value(value, move_focus)
767 767
768 def selectValues(self, values, move_focus=True): 768 def select_values(self, values, move_focus=True):
769 return self.genericList.selectValues(values, move_focus) 769 return self.genericList.select_values(values, move_focus)
770 770
771 def render(self, size, focus=False): 771 def render(self, size, focus=False):
772 return self.displayWidget(size, focus).render(size, focus) 772 return self.display_widget(size, focus).render(size, focus)
773 773
774 def rows(self, size, focus=False): 774 def rows(self, size, focus=False):
775 return self.displayWidget(size, focus).rows(size, focus) 775 return self.display_widget(size, focus).rows(size, focus)
776 776
777 def _getHeight(self, size, focus): 777 def _get_height(self, size, focus):
778 list_size = sum([wid.rows(size, focus) for wid in self.genericList.content]) 778 list_size = sum([wid.rows(size, focus) for wid in self.genericList.content])
779 height = min(list_size,self.max_height) or 1 779 height = min(list_size,self.max_height) or 1
780 return height 780 return height
781 781
782 def displayWidget(self, size, focus): 782 def display_widget(self, size, focus):
783 return urwid.BoxAdapter(self.genericList, self._getHeight(size, focus)) 783 return urwid.BoxAdapter(self.genericList, self._get_height(size, focus))
784 784
785 785
786 ## MISC ## 786 ## MISC ##
787 787
788 class NotificationBar(urwid.WidgetWrap): 788 class NotificationBar(urwid.WidgetWrap):
790 signals = ['change'] 790 signals = ['change']
791 791
792 def __init__(self): 792 def __init__(self):
793 self.waitNotifs = urwid.Text('') 793 self.waitNotifs = urwid.Text('')
794 self.message = ClickableText('') 794 self.message = ClickableText('')
795 urwid.connect_signal(self.message, 'click', lambda wid: self.showNext()) 795 urwid.connect_signal(self.message, 'click', lambda wid: self.show_next())
796 self.progress = ClickableText('') 796 self.progress = ClickableText('')
797 self.columns = urwid.Columns([('fixed',6,self.waitNotifs),self.message,('fixed',4,self.progress)]) 797 self.columns = urwid.Columns([('fixed',6,self.waitNotifs),self.message,('fixed',4,self.progress)])
798 urwid.WidgetWrap.__init__(self, urwid.AttrMap(self.columns,'notifs')) 798 urwid.WidgetWrap.__init__(self, urwid.AttrMap(self.columns,'notifs'))
799 self.notifs = [] 799 self.notifs = []
800 800
801 def _modQueue(self): 801 def _mod_queue(self):
802 """must be called each time the notifications queue is changed""" 802 """must be called each time the notifications queue is changed"""
803 self.waitNotifs.set_text(('notifs',"(%i)" % len(self.notifs) if self.notifs else '')) 803 self.waitNotifs.set_text(('notifs',"(%i)" % len(self.notifs) if self.notifs else ''))
804 self._emit('change') 804 self._emit('change')
805 805
806 def setProgress(self,percentage): 806 def set_progress(self,percentage):
807 """Define the progression to show on the right side of the bar""" 807 """Define the progression to show on the right side of the bar"""
808 if percentage == None: 808 if percentage == None:
809 self.progress.set_text('') 809 self.progress.set_text('')
810 else: 810 else:
811 self.progress.set_text(('notifs','%02i%%' % percentage)) 811 self.progress.set_text(('notifs','%02i%%' % percentage))
812 if self.columns.focus != self.progress: 812 if self.columns.focus != self.progress:
813 self.columns.focus_position = len(self.columns.contents)-1 813 self.columns.focus_position = len(self.columns.contents)-1
814 self._emit('change') 814 self._emit('change')
815 815
816 def addPopUp(self, pop_up_widget): 816 def add_pop_up(self, pop_up_widget):
817 """Add a popup to the waiting queue""" 817 """Add a popup to the waiting queue"""
818 self.notifs.append(('popup',pop_up_widget)) 818 self.notifs.append(('popup',pop_up_widget))
819 self._modQueue() 819 self._mod_queue()
820 820
821 def removePopUp(self, pop_up_widget): 821 def remove_pop_up(self, pop_up_widget):
822 """Remove a popup from the waiting queue""" 822 """Remove a popup from the waiting queue"""
823 for idx, (wid_type, widget) in enumerate(self.notifs): 823 for idx, (wid_type, widget) in enumerate(self.notifs):
824 if widget == pop_up_widget: 824 if widget == pop_up_widget:
825 del self.notifs[idx] 825 del self.notifs[idx]
826 self._modQueue() 826 self._mod_queue()
827 return 827 return
828 828
829 raise ValueError("trying to remove an unknown pop_up_widget") 829 raise ValueError("trying to remove an unknown pop_up_widget")
830 830
831 def addMessage(self, message): 831 def add_message(self, message):
832 "Add a message to the notificatio bar" 832 "Add a message to the notificatio bar"
833 if not self.message.get_text(): 833 if not self.message.get_text():
834 self.message.set_text(('notifs',message)) 834 self.message.set_text(('notifs',message))
835 self._invalidate() 835 self._invalidate()
836 self._emit('change') 836 self._emit('change')
837 else: 837 else:
838 self.notifs.append(('message',message)) 838 self.notifs.append(('message',message))
839 self._modQueue() 839 self._mod_queue()
840 840
841 def showNext(self): 841 def show_next(self):
842 """Show next message if any, else delete current message""" 842 """Show next message if any, else delete current message"""
843 found = None 843 found = None
844 for notif in self.notifs: 844 for notif in self.notifs:
845 if notif[0] == "message": 845 if notif[0] == "message":
846 found = notif 846 found = notif
847 break 847 break
848 if found: 848 if found:
849 self.notifs.remove(found) 849 self.notifs.remove(found)
850 self.message.set_text(('notifs',found[1])) 850 self.message.set_text(('notifs',found[1]))
851 self._modQueue() 851 self._mod_queue()
852 self.focus_possition = 1 852 self.focus_possition = 1
853 else: 853 else:
854 self.message.set_text('') 854 self.message.set_text('')
855 self._emit('change') 855 self._emit('change')
856 856
857 def getNextPopup(self): 857 def get_next_popup(self):
858 """Return next pop-up and remove it from the queue 858 """Return next pop-up and remove it from the queue
859 @return: pop-up or None if there is no more in the queue""" 859 @return: pop-up or None if there is no more in the queue"""
860 ret = None 860 ret = None
861 for notif in self.notifs: 861 for notif in self.notifs:
862 if notif[0] == 'popup': 862 if notif[0] == 'popup':
863 ret = notif[1] 863 ret = notif[1]
864 break 864 break
865 if ret: 865 if ret:
866 self.notifs.remove(notif) 866 self.notifs.remove(notif)
867 self._modQueue() 867 self._mod_queue()
868 return ret 868 return ret
869 869
870 def isQueueEmpty(self): 870 def is_queue_empty(self):
871 return not bool(self.notifs) 871 return not bool(self.notifs)
872 872
873 def canHide(self): 873 def can_hide(self):
874 """Return True if there is no important information to show""" 874 """Return True if there is no important information to show"""
875 return self.isQueueEmpty() and not self.message.get_text() and not self.progress.get_text() 875 return self.is_queue_empty() and not self.message.get_text() and not self.progress.get_text()
876 876
877 877
878 class MenuBox(urwid.WidgetWrap): 878 class MenuBox(urwid.WidgetWrap):
879 """Show menu items of a category in a box""" 879 """Show menu items of a category in a box"""
880 signals = ['click'] 880 signals = ['click']
882 def __init__(self,parent,items): 882 def __init__(self,parent,items):
883 self.parent = parent 883 self.parent = parent
884 self.selected = None 884 self.selected = None
885 content = urwid.SimpleListWalker([ClickableText(('menuitem',text)) for text in items]) 885 content = urwid.SimpleListWalker([ClickableText(('menuitem',text)) for text in items])
886 for wid in content: 886 for wid in content:
887 urwid.connect_signal(wid, 'click', self.onClick) 887 urwid.connect_signal(wid, 'click', self.on_click)
888 888
889 self.listBox = urwid.ListBox(content) 889 self.listBox = urwid.ListBox(content)
890 menubox = urwid.LineBox(urwid.BoxAdapter(self.listBox,len(items))) 890 menubox = urwid.LineBox(urwid.BoxAdapter(self.listBox,len(items)))
891 urwid.WidgetWrap.__init__(self,menubox) 891 urwid.WidgetWrap.__init__(self,menubox)
892 892
893 def getValue(self): 893 def get_value(self):
894 return self.selected 894 return self.selected
895 895
896 def keypress(self, size, key): 896 def keypress(self, size, key):
897 if key==a_key['MENU_BOX_UP']: 897 if key==a_key['MENU_BOX_UP']:
898 if self.listBox.get_focus()[1] == 0: 898 if self.listBox.get_focus()[1] == 0:
906 if button == 3: 906 if button == 3:
907 self.parent.keypress(size,'up') 907 self.parent.keypress(size,'up')
908 return True 908 return True
909 return super(MenuBox,self).mouse_event(size, event, button, x, y, focus) 909 return super(MenuBox,self).mouse_event(size, event, button, x, y, focus)
910 910
911 def onClick(self, wid): 911 def on_click(self, wid):
912 self.selected = wid.getValue() 912 self.selected = wid.get_value()
913 self._emit('click') 913 self._emit('click')
914 914
915 915
916 class Menu(urwid.WidgetWrap): 916 class Menu(urwid.WidgetWrap):
917 917
930 urwid.WidgetWrap.__init__(self, urwid.AttrMap(col_rol,'menubar')) 930 urwid.WidgetWrap.__init__(self, urwid.AttrMap(col_rol,'menubar'))
931 931
932 def selectable(self): 932 def selectable(self):
933 return True 933 return True
934 934
935 def getMenuSize(self): 935 def get_menu_size(self):
936 """return the current number of categories in this menu""" 936 """return the current number of categories in this menu"""
937 return len(self.menu_keys) 937 return len(self.menu_keys)
938 938
939 def setOrigX(self, orig_x): 939 def set_orig_x(self, orig_x):
940 self.x_orig = orig_x 940 self.x_orig = orig_x
941 941
942 def __buildOverlay(self, menu_key, columns): 942 def __build_overlay(self, menu_key, columns):
943 """Build the overlay menu which show menuitems 943 """Build the overlay menu which show menuitems
944 @param menu_key: name of the category 944 @param menu_key: name of the category
945 @param columns: column number where the menubox must be displayed""" 945 @param columns: column number where the menubox must be displayed"""
946 max_len = 0 946 max_len = 0
947 for item in self.menu[menu_key]: 947 for item in self.menu[menu_key]:
948 if len(item[0]) > max_len: 948 if len(item[0]) > max_len:
949 max_len = len(item[0]) 949 max_len = len(item[0])
950 950
951 self.save_bottom = self.loop.widget 951 self.save_bottom = self.loop.widget
952 menu_box = MenuBox(self,[item[0] for item in self.menu[menu_key]]) 952 menu_box = MenuBox(self,[item[0] for item in self.menu[menu_key]])
953 urwid.connect_signal(menu_box, 'click', self.onItemClick) 953 urwid.connect_signal(menu_box, 'click', self.on_item_click)
954 954
955 self.loop.widget = urwid.Overlay(urwid.AttrMap(menu_box,'menubar'),self.save_bottom,('fixed left', columns),max_len+2,('fixed top',1),None) 955 self.loop.widget = urwid.Overlay(urwid.AttrMap(menu_box,'menubar'),self.save_bottom,('fixed left', columns),max_len+2,('fixed top',1),None)
956 956
957 def keypress(self, size, key): 957 def keypress(self, size, key):
958 if key == a_key['MENU_DOWN']: 958 if key == a_key['MENU_DOWN']:
962 self.loop.widget = self.save_bottom 962 self.loop.widget = self.save_bottom
963 self.save_bottom = None 963 self.save_bottom = None
964 964
965 return self._w.base_widget.keypress(size, key) 965 return self._w.base_widget.keypress(size, key)
966 966
967 def checkShortcuts(self, key): 967 def check_shortcuts(self, key):
968 for shortcut in list(self.shortcuts.keys()): 968 for shortcut in list(self.shortcuts.keys()):
969 if key == shortcut: 969 if key == shortcut:
970 category, item, callback = self.shortcuts[shortcut] 970 category, item, callback = self.shortcuts[shortcut]
971 callback((category, item)) 971 callback((category, item))
972 return key 972 return key
973 973
974 def addMenu(self, category, item=None, callback=None, shortcut=None): 974 def add_menu(self, category, item=None, callback=None, shortcut=None):
975 """Create the category if new and add a menu item (if item is not None). 975 """Create the category if new and add a menu item (if item is not None).
976 976
977 @param category: category of the menu (e.g. File/Edit) 977 @param category: category of the menu (e.g. File/Edit)
978 @param item: menu item (e.g. new/close/about) 978 @param item: menu item (e.g. new/close/about)
979 @callback: method to call when item is selected""" 979 @callback: method to call when item is selected"""
980 if not category in list(self.menu.keys()): 980 if not category in list(self.menu.keys()):
981 self.menu_keys.append(category) 981 self.menu_keys.append(category)
982 self.menu[category] = [] 982 self.menu[category] = []
983 button = CustomButton(('menubar',category), self.onCategoryClick, 983 button = CustomButton(('menubar',category), self.on_category_click,
984 left_border = ('menubar',"[ "), 984 left_border = ('menubar',"[ "),
985 right_border = ('menubar'," ]")) 985 right_border = ('menubar'," ]"))
986 self._w.base_widget.addWidget(button,button.getSize()) 986 self._w.base_widget.add_widget(button,button.get_size())
987 if not item: 987 if not item:
988 return 988 return
989 self.menu[category].append((item, callback)) 989 self.menu[category].append((item, callback))
990 if shortcut: 990 if shortcut:
991 assert(shortcut not in list(self.shortcuts.keys())) 991 assert(shortcut not in list(self.shortcuts.keys()))
992 self.shortcuts[shortcut] = (category, item, callback) 992 self.shortcuts[shortcut] = (category, item, callback)
993 993
994 def onItemClick(self, widget): 994 def on_item_click(self, widget):
995 category = self._w.base_widget.getSelected().get_label() 995 category = self._w.base_widget.get_selected().get_label()
996 item = widget.getValue() 996 item = widget.get_value()
997 callback = None 997 callback = None
998 for menu_item in self.menu[category]: 998 for menu_item in self.menu[category]:
999 if item == menu_item[0]: 999 if item == menu_item[0]:
1000 callback = menu_item[1] 1000 callback = menu_item[1]
1001 break 1001 break
1002 if callback: 1002 if callback:
1003 self.keypress(None, a_key['MENU_UP']) 1003 self.keypress(None, a_key['MENU_UP'])
1004 callback((category, item)) 1004 callback((category, item))
1005 1005
1006 def onCategoryClick(self, button): 1006 def on_category_click(self, button):
1007 self.__buildOverlay(button.get_label(), 1007 self.__build_overlay(button.get_label(),
1008 self.x_orig + self._w.base_widget.getStartCol(button)) 1008 self.x_orig + self._w.base_widget.get_start_col(button))
1009 1009
1010 MenuItem = collections.namedtuple('MenuItem', ('name', 'widget')) 1010 MenuItem = collections.namedtuple('MenuItem', ('name', 'widget'))
1011 1011
1012 class MenuRoller(urwid.WidgetWrap): 1012 class MenuRoller(urwid.WidgetWrap):
1013 1013
1029 try: 1029 try:
1030 name, menu, id_ = menu_tuple 1030 name, menu, id_ = menu_tuple
1031 except ValueError: 1031 except ValueError:
1032 name, menu = menu_tuple 1032 name, menu = menu_tuple
1033 id_ = None 1033 id_ = None
1034 self.addMenu(name, menu, id_) 1034 self.add_menu(name, menu, id_)
1035 1035
1036 def _showSelected(self): 1036 def _show_selected(self):
1037 """show menu selected""" 1037 """show menu selected"""
1038 if self.selected is None: 1038 if self.selected is None:
1039 self.columns.contents[0] = (urwid.Text(''), ('given', 0, False)) 1039 self.columns.contents[0] = (urwid.Text(''), ('given', 0, False))
1040 self.columns.contents[1] = (urwid.Text(''), ('weight', 1, False)) 1040 self.columns.contents[1] = (urwid.Text(''), ('weight', 1, False))
1041 else: 1041 else:
1042 menu_item = self.menu_items[self.selected] 1042 menu_item = self.menu_items[self.selected]
1043 name_txt = '\u21c9 ' + menu_item.name + ' \u21c7 ' 1043 name_txt = '\u21c9 ' + menu_item.name + ' \u21c7 '
1044 current_name = ClickableText(name_txt) 1044 current_name = ClickableText(name_txt)
1045 name_len = len(name_txt) 1045 name_len = len(name_txt)
1046 current_menu = menu_item.widget 1046 current_menu = menu_item.widget
1047 current_menu.setOrigX(name_len) 1047 current_menu.set_orig_x(name_len)
1048 self.columns.contents[0] = (current_name, ('given', name_len, False)) 1048 self.columns.contents[0] = (current_name, ('given', name_len, False))
1049 self.columns.contents[1] = (current_menu, ('weight', 1, False)) 1049 self.columns.contents[1] = (current_menu, ('weight', 1, False))
1050 1050
1051 def keypress(self, size, key): 1051 def keypress(self, size, key):
1052 menu_ids = list(self.menu_items.keys()) 1052 menu_ids = list(self.menu_items.keys())
1057 1057
1058 if key==a_key['MENU_ROLLER_UP']: 1058 if key==a_key['MENU_ROLLER_UP']:
1059 if self.columns.get_focus_column()==0: 1059 if self.columns.get_focus_column()==0:
1060 if idx > 0: 1060 if idx > 0:
1061 self.selected = menu_ids[idx-1] 1061 self.selected = menu_ids[idx-1]
1062 self._showSelected() 1062 self._show_selected()
1063 return 1063 return
1064 elif key==a_key['MENU_ROLLER_DOWN']: 1064 elif key==a_key['MENU_ROLLER_DOWN']:
1065 if self.columns.get_focus_column()==0: 1065 if self.columns.get_focus_column()==0:
1066 if idx < len(menu_ids)-1: 1066 if idx < len(menu_ids)-1:
1067 self.selected = menu_ids[idx+1] 1067 self.selected = menu_ids[idx+1]
1068 self._showSelected() 1068 self._show_selected()
1069 return 1069 return
1070 elif key==a_key['MENU_ROLLER_RIGHT']: 1070 elif key==a_key['MENU_ROLLER_RIGHT']:
1071 if self.columns.get_focus_column()==0 and \ 1071 if self.columns.get_focus_column()==0 and \
1072 (isinstance(self.columns.contents[1][0], urwid.Text) or \ 1072 (isinstance(self.columns.contents[1][0], urwid.Text) or \
1073 self.menu_items[self.selected].widget.getMenuSize()==0): 1073 self.menu_items[self.selected].widget.get_menu_size()==0):
1074 return #if we have no menu or the menu is empty, we don't go the right column 1074 return #if we have no menu or the menu is empty, we don't go the right column
1075 1075
1076 return super(MenuRoller, self).keypress(size, key) 1076 return super(MenuRoller, self).keypress(size, key)
1077 1077
1078 def addMenu(self, name, widget, menu_id=None): 1078 def add_menu(self, name, widget, menu_id=None):
1079 """Add a menu 1079 """Add a menu
1080 1080
1081 @param name: name of the menu to add, it name already exists, menu is not added 1081 @param name: name of the menu to add, it name already exists, menu is not added
1082 @param widget: instance of Menu 1082 @param widget: instance of Menu
1083 @param menu_id: id to use of this menu, or None to generate 1083 @param menu_id: id to use of this menu, or None to generate
1092 self.menu_items[id_] = MenuItem(name, widget) 1092 self.menu_items[id_] = MenuItem(name, widget)
1093 else: 1093 else:
1094 id_ = names[name] 1094 id_ = names[name]
1095 menu_item = self.menu_items[id_] 1095 menu_item = self.menu_items[id_]
1096 if menu_item.widget is not widget: 1096 if menu_item.widget is not widget:
1097 raise ValueError("The menu with id [{}] exists and doesn't contain the given instance. Use replaceMenu if you want to change the menu.".format(id_)) 1097 raise ValueError("The menu with id [{}] exists and doesn't contain the given instance. Use replace_menu if you want to change the menu.".format(id_))
1098 if self.selected is None: 1098 if self.selected is None:
1099 self.selected = id_ 1099 self.selected = id_
1100 self._showSelected() 1100 self._show_selected()
1101 return id_ 1101 return id_
1102 1102
1103 def replaceMenu(self, name, widget, menu_id): 1103 def replace_menu(self, name, widget, menu_id):
1104 """Add a menu or replace it if the id already exists 1104 """Add a menu or replace it if the id already exists
1105 1105
1106 @param name: name of the menu to add, it name already exists, menu is not added 1106 @param name: name of the menu to add, it name already exists, menu is not added
1107 @param widget: instance of Menu 1107 @param widget: instance of Menu
1108 @param menu_id: id or the menu 1108 @param menu_id: id or the menu
1109 """ 1109 """
1110 assert menu_id is not None 1110 assert menu_id is not None
1111 if menu_id in self.menu_items: 1111 if menu_id in self.menu_items:
1112 del self.menu_items[menu_id] 1112 del self.menu_items[menu_id]
1113 self.addMenu(name, widget, menu_id) 1113 self.add_menu(name, widget, menu_id)
1114 if self.selected == menu_id: 1114 if self.selected == menu_id:
1115 self._showSelected() #if we are on the menu, we update it 1115 self._show_selected() #if we are on the menu, we update it
1116 1116
1117 def removeMenu(self, menu_id): 1117 def remove_menu(self, menu_id):
1118 del self.menu_items[menu_id] 1118 del self.menu_items[menu_id]
1119 if self.selected == menu_id: 1119 if self.selected == menu_id:
1120 try: 1120 try:
1121 self.selected = next(iter(self.menu_items.keys())) 1121 self.selected = next(iter(self.menu_items.keys()))
1122 except StopIteration: 1122 except StopIteration:
1123 self.selected = None 1123 self.selected = None
1124 self._showSelected() 1124 self._show_selected()
1125 1125
1126 def checkShortcuts(self, key): 1126 def check_shortcuts(self, key):
1127 for menu_item in list(self.menu_items.values()): 1127 for menu_item in list(self.menu_items.values()):
1128 key = menu_item.widget.checkShortcuts(key) 1128 key = menu_item.widget.check_shortcuts(key)
1129 return key 1129 return key
1130 1130
1131 1131
1132 ## DIALOGS ## 1132 ## DIALOGS ##
1133 1133
1154 frame_body = UnselectableListBox(body_content) 1154 frame_body = UnselectableListBox(body_content)
1155 frame = FocusFrame(frame_body, frame_header, buttons_flow if self.buttons else None, 'footer' if self.buttons else 'body') 1155 frame = FocusFrame(frame_body, frame_header, buttons_flow if self.buttons else None, 'footer' if self.buttons else 'body')
1156 decorated_frame = urwid.LineBox(frame) 1156 decorated_frame = urwid.LineBox(frame)
1157 urwid.WidgetWrap.__init__(self, decorated_frame) 1157 urwid.WidgetWrap.__init__(self, decorated_frame)
1158 1158
1159 def setCallback(self, name, callback, data=None): 1159 def set_callback(self, name, callback, data=None):
1160 """Set the callback associated with a button press 1160 """Set the callback associated with a button press
1161 1161
1162 @param name: one of "ok", "cancel", "yes", "no" 1162 @param name: one of "ok", "cancel", "yes", "no"
1163 @aram callback(callable): method to call on requested action 1163 @aram callback(callable): method to call on requested action
1164 @param data: argument to send to the callback (first one will be the button widget) 1164 @param data: argument to send to the callback (first one will be the button widget)
1214 self.widget_list = widget_list or [] 1214 self.widget_list = widget_list or []
1215 self.focus_column = focus_column 1215 self.focus_column = focus_column
1216 self.__start = 0 1216 self.__start = 0
1217 self.__next = False 1217 self.__next = False
1218 1218
1219 def addWidget(self, widget, width): 1219 def add_widget(self, widget, width):
1220 self.widget_list.append((width,widget)) 1220 self.widget_list.append((width,widget))
1221 if len(self.widget_list) == 1: 1221 if len(self.widget_list) == 1:
1222 self.focus_position = 0 1222 self.focus_position = 0
1223 1223
1224 def getStartCol(self, widget): 1224 def get_start_col(self, widget):
1225 """Return the column of the left corner of the widget""" 1225 """Return the column of the left corner of the widget"""
1226 start_col = 0 1226 start_col = 0
1227 for wid in self.widget_list[self.__start:]: 1227 for wid in self.widget_list[self.__start:]:
1228 if wid[1] == widget: 1228 if wid[1] == widget:
1229 return start_col 1229 return start_col
1249 return 1249 return
1250 if self.focus_column<len(self.widget_list): 1250 if self.focus_column<len(self.widget_list):
1251 return self.widget_list[self.focus_column][1].keypress(size,key) 1251 return self.widget_list[self.focus_column][1].keypress(size,key)
1252 return key 1252 return key
1253 1253
1254 def getSelected(self): 1254 def get_selected(self):
1255 """Return selected widget""" 1255 """Return selected widget"""
1256 return self.widget_list[self.focus_column][1] 1256 return self.widget_list[self.focus_column][1]
1257 1257
1258 @property 1258 @property
1259 def focus_position(self): 1259 def focus_position(self):
1288 _next = True 1288 _next = True
1289 total_wid-=self.widget_list[end_wid][0] 1289 total_wid-=self.widget_list[end_wid][0]
1290 end_wid-=1 1290 end_wid-=1
1291 1291
1292 cols_left = maxcol - total_wid 1292 cols_left = maxcol - total_wid
1293 self.__start = start_wid #we need to keep it for getStartCol 1293 self.__start = start_wid #we need to keep it for get_start_col
1294 return _prev,_next,start_wid,end_wid,cols_left 1294 return _prev,_next,start_wid,end_wid,cols_left
1295 1295
1296 1296
1297 def mouse_event(self, size, event, button, x, y, focus): 1297 def mouse_event(self, size, event, button, x, y, focus):
1298 (maxcol,)=size 1298 (maxcol,)=size
1354 ret = super(FocusPile, self).keypress(size, key) 1354 ret = super(FocusPile, self).keypress(size, key)
1355 if not ret: 1355 if not ret:
1356 return 1356 return
1357 1357
1358 if key in FOCUS_KEYS: 1358 if key in FOCUS_KEYS:
1359 direction, rotate = getFocusDirection(key, inversed = self._focus_inversed) 1359 direction, rotate = get_focus_direction(key, inversed = self._focus_inversed)
1360 max_pos = len(self.contents) - 1 1360 max_pos = len(self.contents) - 1
1361 new_pos = self.focus_position + direction 1361 new_pos = self.focus_position + direction
1362 if rotate: 1362 if rotate:
1363 if new_pos > max_pos: 1363 if new_pos > max_pos:
1364 new_pos = 0 1364 new_pos = 0
1380 ret = super(FocusFrame, self).keypress(size, key) 1380 ret = super(FocusFrame, self).keypress(size, key)
1381 if not ret: 1381 if not ret:
1382 return 1382 return
1383 1383
1384 if key in FOCUS_KEYS: 1384 if key in FOCUS_KEYS:
1385 direction, rotate = getFocusDirection(key) 1385 direction, rotate = get_focus_direction(key)
1386 1386
1387 positions = [pos for pos in self.ordered_positions if pos in self] 1387 positions = [pos for pos in self.ordered_positions if pos in self]
1388 selectables = [pos for pos in positions if self.contents[pos][0].selectable()] # keep positions which exists and have a selectable widget 1388 selectables = [pos for pos in positions if self.contents[pos][0].selectable()] # keep positions which exists and have a selectable widget
1389 if not selectables: 1389 if not selectables:
1390 # no widget is selectable, we just return 1390 # no widget is selectable, we just return
1439 urwid.WidgetWrap.__init__(self, self._frame) 1439 urwid.WidgetWrap.__init__(self, self._frame)
1440 1440
1441 def keypress(self, size, key): 1441 def keypress(self, size, key):
1442 return self._w.keypress(size,key) 1442 return self._w.keypress(size,key)
1443 1443
1444 def _buttonClicked(self, button, invisible=False): 1444 def _button_clicked(self, button, invisible=False):
1445 """Called when a button on the tab is changed, 1445 """Called when a button on the tab is changed,
1446 change the page 1446 change the page
1447 @param button: button clicked 1447 @param button: button clicked
1448 @param invisible: emit signal only if False""" 1448 @param invisible: emit signal only if False"""
1449 tab_name = button.get_label() 1449 tab_name = button.get_label()
1459 self._current_tab.set_label(self._current_tab.get_label()) 1459 self._current_tab.set_label(self._current_tab.get_label())
1460 self._current_tab = button 1460 self._current_tab = button
1461 if not invisible: 1461 if not invisible:
1462 self._emit('click') 1462 self._emit('click')
1463 1463
1464 def _appendButton(self, name, selected=False): 1464 def _append_button(self, name, selected=False):
1465 """Append a button to the frame header, and link it to the page change method. 1465 """Append a button to the frame header, and link it to the page change method.
1466 1466
1467 @param name (unicode): button name 1467 @param name (unicode): button name
1468 @param selected (bool): set to True to select this tab 1468 @param selected (bool): set to True to select this tab
1469 """ 1469 """
1470 button = CustomButton(name, self._buttonClicked, left_border = '', right_border=' | ') 1470 button = CustomButton(name, self._button_clicked, left_border = '', right_border=' | ')
1471 self._buttons_cont.addWidget(button, button.getSize()) 1471 self._buttons_cont.add_widget(button, button.get_size())
1472 count = len(self._buttons_cont.widget_list) 1472 count = len(self._buttons_cont.widget_list)
1473 if selected or count == 1: 1473 if selected or count == 1:
1474 # first/selected button: we set the focus and the body 1474 # first/selected button: we set the focus and the body
1475 self.selectTab(count - 1) 1475 self.select_tab(count - 1)
1476 1476
1477 def addTab(self, name, content=None, selected=False): 1477 def add_tab(self, name, content=None, selected=False):
1478 """Add a page to the container 1478 """Add a page to the container
1479 1479
1480 @param name: name of the page (what appear on the tab) 1480 @param name: name of the page (what appear on the tab)
1481 @param content: content of the page: 1481 @param content: content of the page:
1482 - if None create and empty Listbox 1482 - if None create and empty Listbox
1488 tab = urwid.ListBox(urwid.SimpleListWalker(content or [])) 1488 tab = urwid.ListBox(urwid.SimpleListWalker(content or []))
1489 else: 1489 else:
1490 tab = content 1490 tab = content
1491 1491
1492 self.tabs.append([name, tab]) 1492 self.tabs.append([name, tab])
1493 self._appendButton(name, selected) 1493 self._append_button(name, selected)
1494 return tab 1494 return tab
1495 1495
1496 def addFooter(self, widget): 1496 def add_footer(self, widget):
1497 """Add a widget on the bottom of the tab (will be displayed on all pages) 1497 """Add a widget on the bottom of the tab (will be displayed on all pages)
1498 @param widget: FlowWidget""" 1498 @param widget: FlowWidget"""
1499 self._w.footer = widget 1499 self._w.footer = widget
1500 1500
1501 def selectTab(self, index): 1501 def select_tab(self, index):
1502 """Select a tab. 1502 """Select a tab.
1503 1503
1504 @param index (int): index of the tab to select 1504 @param index (int): index of the tab to select
1505 """ 1505 """
1506 self._buttons_cont.focus_position = index 1506 self._buttons_cont.focus_position = index
1507 self._buttonClicked(self._buttons_cont.widget_list[index][1], True) 1507 self._button_clicked(self._buttons_cont.widget_list[index][1], True)
1508 1508
1509 1509
1510 class HighlightColumns(urwid.AttrMap): 1510 class HighlightColumns(urwid.AttrMap):
1511 """ Decorated columns which highlight all or some columns """ 1511 """ Decorated columns which highlight all or some columns """
1512 1512
1541 1541
1542 @focus_position.setter 1542 @focus_position.setter
1543 def focus_position(self, value): 1543 def focus_position(self, value):
1544 self.base_widget.focus_position = value 1544 self.base_widget.focus_position = value
1545 1545
1546 def addWidget(self, wid, options): 1546 def add_widget(self, wid, options):
1547 """ Add a widget to the columns 1547 """ Add a widget to the columns
1548 Widget is wrapped with AttrMap, that's why Columns.contents should not be used directly for appending new widgets 1548 Widget is wrapped with AttrMap, that's why Columns.contents should not be used directly for appending new widgets
1549 @param wid: widget to add 1549 @param wid: widget to add
1550 @param options: result of Columns.options(...) 1550 @param options: result of Columns.options(...)
1551 1551
1605 self._dividechars = dividechars 1605 self._dividechars = dividechars
1606 self._idx = 0 1606 self._idx = 0
1607 self._longuest = self._columns * [0] 1607 self._longuest = self._columns * [0]
1608 self._next_row_idx = None 1608 self._next_row_idx = None
1609 for item in items: 1609 for item in items:
1610 self.addWidget(item) 1610 self.add_widget(item)
1611 1611
1612 def _getIdealSize(self, widget): 1612 def _get_ideal_size(self, widget):
1613 """ return preferred size for widget, or 0 if we can't find it """ 1613 """ return preferred size for widget, or 0 if we can't find it """
1614 try: 1614 try:
1615 return len(widget.text) 1615 return len(widget.text)
1616 except AttributeError: 1616 except AttributeError:
1617 return 0 1617 return 0
1621 self._emit('click') 1621 self._emit('click')
1622 else: 1622 else:
1623 return super(TableContainer, self).keypress(size, key) 1623 return super(TableContainer, self).keypress(size, key)
1624 1624
1625 1625
1626 def addWidget(self, widget): 1626 def add_widget(self, widget):
1627 # TODO: use a contents property ? 1627 # TODO: use a contents property ?
1628 pile = self._w 1628 pile = self._w
1629 col_idx = self._idx % self._columns 1629 col_idx = self._idx % self._columns
1630 1630
1631 options = None 1631 options = None
1638 else: 1638 else:
1639 columns = pile.contents[-1][0] 1639 columns = pile.contents[-1][0]
1640 1640
1641 if 'ADAPT' in self._options and (col_idx in self._options['ADAPT'] 1641 if 'ADAPT' in self._options and (col_idx in self._options['ADAPT']
1642 or self._options['ADAPT'] == ()): 1642 or self._options['ADAPT'] == ()):
1643 current_len = self._getIdealSize(widget) 1643 current_len = self._get_ideal_size(widget)
1644 longuest = self._longuest[col_idx] 1644 longuest = self._longuest[col_idx]
1645 max_len = max(longuest, current_len) 1645 max_len = max(longuest, current_len)
1646 if max_len > longuest: 1646 if max_len > longuest:
1647 self._longuest[col_idx] = max_len 1647 self._longuest[col_idx] = max_len
1648 for wid,_ in pile.contents[:-1]: 1648 for wid,_ in pile.contents[:-1]:
1649 col = wid.base_widget 1649 col = wid.base_widget
1650 col.contents[col_idx] = (col.contents[col_idx][0], col.options('given', max_len)) 1650 col.contents[col_idx] = (col.contents[col_idx][0], col.options('given', max_len))
1651 options = columns.options('given', max_len) if max_len else columns.options() 1651 options = columns.options('given', max_len) if max_len else columns.options()
1652 1652
1653 columns.addWidget(widget, options or columns.options()) 1653 columns.add_widget(widget, options or columns.options())
1654 1654
1655 if self._row_selectable and col_idx == self._columns - 1: 1655 if self._row_selectable and col_idx == self._columns - 1:
1656 columns.addWidget(urwid.SelectableIcon(''), columns.options('given', 0)) 1656 columns.add_widget(urwid.SelectableIcon(''), columns.options('given', 0))
1657 1657
1658 if not columns.selectable() and columns.contents[-1][0].base_widget.selectable(): 1658 if not columns.selectable() and columns.contents[-1][0].base_widget.selectable():
1659 columns.focus_position = len(columns.contents)-1 1659 columns.focus_position = len(columns.contents)-1
1660 if not self.selectable() and columns.selectable(): 1660 if not self.selectable() and columns.selectable():
1661 pile.focus_position = len(pile.contents) - 1 1661 pile.focus_position = len(pile.contents) - 1
1662 self._idx += 1 1662 self._idx += 1
1663 1663
1664 def setRowIndex(self, idx): 1664 def set_row_index(self, idx):
1665 self._next_row_idx = idx 1665 self._next_row_idx = idx
1666 1666
1667 def getSelectedWidgets(self): 1667 def get_selected_widgets(self):
1668 columns = self._w.focus 1668 columns = self._w.focus
1669 return (wid for wid, _ in columns.contents) 1669 return (wid for wid, _ in columns.contents)
1670 1670
1671 def getSelectedIndex(self): 1671 def get_selected_index(self):
1672 columns = self._w.focus 1672 columns = self._w.focus
1673 return columns.row_idx 1673 return columns.row_idx
1674 1674
1675 ## DECORATORS ## 1675 ## DECORATORS ##
1676 class LabelLine(urwid.LineBox): 1676 class LabelLine(urwid.LineBox):