make ScrollBarListBox decoration

This commit is contained in:
Michael Hudson-Doyle 2018-05-23 12:31:04 +12:00
parent 3aac53cf1b
commit 517a733352
3 changed files with 44 additions and 40 deletions

View File

@ -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)

View File

@ -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):

View File

@ -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))