From 517a733352a40c5d1b34e613fe080f52eaf21786 Mon Sep 17 00:00:00 2001 From: Michael Hudson-Doyle Date: Wed, 23 May 2018 12:31:04 +1200 Subject: [PATCH] make ScrollBarListBox decoration --- subiquity/ui/views/installprogress.py | 27 +++++++++++--------- subiquity/ui/views/snaplist.py | 21 +++++----------- subiquitycore/ui/container.py | 36 +++++++++++++++++---------- 3 files changed, 44 insertions(+), 40 deletions(-) diff --git a/subiquity/ui/views/installprogress.py b/subiquity/ui/views/installprogress.py index 7e2fe263..7205fc21 100644 --- a/subiquity/ui/views/installprogress.py +++ b/subiquity/ui/views/installprogress.py @@ -66,21 +66,24 @@ class ProgressView(BaseView): super().__init__(self.event_pile) - def add_event(self, text): - at_end = len(self.event_listwalker) == 0 or self.event_listbox.focus_position == len(self.event_listwalker) - 1 - if len(self.event_listwalker) > 0: - self.event_listwalker[-1] = self.event_listwalker[-1][0] - self.event_listwalker.append(Columns([('pack', Text(text)), ('pack', self.spinner)], dividechars=1)) + def _add_line(self, lb, line): + lb = lb.base_widget + walker = lb.body + at_end = len(walker) == 0 or lb.focus_position == len(walker) - 1 + walker.append(line) if at_end: - self.event_listbox.set_focus(len(self.event_listwalker) - 1) - self.event_listbox.set_focus_valign('bottom') + lb.set_focus(len(walker) - 1) + lb.set_focus_valign('bottom') + + def add_event(self, text): + walker = self.event_listwalker + if len(walker) > 0: + walker[-1] = walker[-1][0] + new = Columns([('pack', Text(text)), ('pack', self.spinner)], dividechars=1) + self._add_line(self.event_listbox, new) def add_log_line(self, text): - at_end = len(self.log_listwalker) == 0 or self.log_listbox.focus_position == len(self.log_listwalker) - 1 - self.log_listwalker.append(Text(text)) - if at_end: - self.log_listbox.set_focus(len(self.log_listwalker) - 1) - self.log_listbox.set_focus_valign('bottom') + self._add_line(self.log_listbox, Text(text)) def set_status(self, text): self.event_linebox.set_title(text) diff --git a/subiquity/ui/views/snaplist.py b/subiquity/ui/views/snaplist.py index 843970f8..b8744e8a 100644 --- a/subiquity/ui/views/snaplist.py +++ b/subiquity/ui/views/snaplist.py @@ -19,15 +19,17 @@ from urwid import ( BOX, CheckBox, LineBox, + ListBox as UrwidListBox, RadioButton, SelectableIcon, + SimpleFocusListWalker, Text, Widget, WidgetWrap, ) from subiquitycore.ui.buttons import ok_btn, cancel_btn, other_btn -from subiquitycore.ui.container import Columns, ListBox, Pile +from subiquitycore.ui.container import Columns, ListBox, Pile, ScrollBarListBox from subiquitycore.ui.utils import button_pile, Color, Padding, screen from subiquitycore.view import BaseView @@ -54,20 +56,9 @@ class StarRadioButton(RadioButton): reserve_columns = 3 -class NoTabCyclingListBox(ListBox): - # Carefully disable the TabCycling parts of our ListBox (but keep the scrollbar!) - - def keypress(self, size, key): - if not key.startswith("enter") and self._command_map[key] in ('next selectable', 'prev selectable'): - return key - else: - return super().keypress(size, key) - - def _select_first_selectable(self): - return - - def _select_last_selectable(self): - return +def NoTabCyclingListBox(body): + body = SimpleFocusListWalker(body) + return ScrollBarListBox(UrwidListBox(body)) class SnapInfoView(Widget): diff --git a/subiquitycore/ui/container.py b/subiquitycore/ui/container.py index 9797bdf9..9b8923d1 100644 --- a/subiquitycore/ui/container.py +++ b/subiquitycore/ui/container.py @@ -386,41 +386,46 @@ class FocusTrackingListBox(TabCyclingListBox): Columns = FocusTrackingColumns Pile = FocusTrackingPile +class ScrollBarListBox(urwid.WidgetDecoration): -class ScrollBarListBox(FocusTrackingListBox): - - def __init__(self, walker=None): + def __init__(self, lb): def f(char, attr): return urwid.AttrMap(urwid.SolidFill(char), attr) self.boxes = [ - urwid.AttrMap(urwid.SolidFill("\N{FULL BLOCK}"), 'scrollbar_bg'), - urwid.AttrMap(urwid.SolidFill("\N{FULL BLOCK}"), 'scrollbar_fg'), + f("\N{FULL BLOCK}", 'scrollbar_bg'), + f("\N{FULL BLOCK}", 'scrollbar_fg'), ] self.bar = Pile([ ('weight', 1, f("\N{BOX DRAWINGS LIGHT VERTICAL}", 'scrollbar_bg')), ('weight', 1, self.boxes[0]), ('weight', 1, f("\N{BOX DRAWINGS LIGHT VERTICAL}", 'scrollbar_bg')), ]) - super().__init__(walker) + super().__init__(lb) + + def keypress(self, size, key): + visible = self.original_widget.ends_visible(size, True) + if len(visible) != 2: + size = (size[0]-1, size[1]) + return self.original_widget.keypress(size, key) def render(self, size, focus=False): - visible = self.ends_visible(size, focus) + visible = self.original_widget.ends_visible(size, focus) if len(visible) == 2: - return super().render(size, focus) + return self.original_widget.render(size, focus) else: # This implementation assumes that the number of rows is # not too large (and in particular is finite). That's the # case for all the listboxes we have in subiquity today. maxcol, maxrow = size - offset, inset = self.get_focus_offset_inset((maxcol - 1, maxrow)) + offset, inset = self.original_widget.get_focus_offset_inset((maxcol - 1, maxrow)) seen_focus = False height = height_before_focus = 0 - focus_widget, focus_pos = self.body.get_focus() + focus_widget, focus_pos = self.original_widget.body.get_focus() # Scan through the rows calculating total height and the # height of the rows before the focus widget. - for widget in self.body: + for widget in self.original_widget.body: rows = widget.rows((maxcol - 1,)) if widget is focus_widget: seen_focus = True @@ -459,10 +464,15 @@ class ScrollBarListBox(FocusTrackingListBox): (self.bar.contents[2][0], self.bar.options('weight', bottom)), ] canvases = [ - (super().render((maxcol - 1, maxrow), focus), self.focus_position, True, maxcol - 1), + (self.original_widget.render((maxcol - 1, maxrow), focus), self.original_widget.focus_position, True, maxcol - 1), (self.bar.render((1, maxrow)), None, False, 1) ] return urwid.CanvasJoin(canvases) -ListBox = ScrollBarListBox +def ListBox(body=None): + # urwid.ListBox converts an arbitrary sequence argument to a + # PollingListWalker, which doesn't work with our code. + if body is not None and getattr(body, 'get_focus', None) is None: + body = urwid.SimpleFocusListWalker(body) + return ScrollBarListBox(FocusTrackingListBox(body))