# dropdown.py Extension to ugui providing the Dropdown class # Released under the MIT License (MIT). See LICENSE. # Copyright (c) 2021 Peter Hinch # 12 Sep 21 Support for scrolling. from gui.core.ugui import Widget, display, Window, Screen from gui.core.colors import * from gui.widgets.listbox import Listbox dolittle = lambda *_ : None # Next and Prev close the listbox without updating the Dropdown. This is # handled by Screen .move bound method class _ListDialog(Window): def __init__(self, writer, row, col, dd): # dd is parent dropdown # Need to determine Window dimensions from size of Listbox, which # depends on number and length of elements. _, lb_height, dlines, tw = Listbox.dimensions(writer, dd.elements, dd.dlines) lb_width = tw + 2 # Text width + 2 # Calculate Window dimensions ap_height = lb_height + 6 # Allow for listbox border ap_width = lb_width + 6 super().__init__(row, col, ap_height, ap_width, draw_border=False) self.listbox = Listbox(writer, row + 3, col + 3, elements = dd.elements, dlines = dlines, width = lb_width, fgcolor = dd.fgcolor, bgcolor = dd.bgcolor, bdcolor=False, fontcolor = dd.fontcolor, select_color = dd.select_color, value = dd.value(), callback = self.callback) self.dd = dd def callback(self, obj_listbox): display.ipdev.adj_mode(False) # If in 3-button mode, leave adjust mode Screen.back() self.dd.value(obj_listbox.value()) # Update it class Dropdown(Widget): def __init__(self, writer, row, col, *, elements, dlines=None, width=None, value=0, fgcolor=None, bgcolor=None, bdcolor=False, fontcolor=None, select_color=DARKBLUE, callback=dolittle, args=[]): self.entry_height = writer.height + 2 # Allow a pixel above and below text height = self.entry_height e0 = elements[0] # Check whether elements specified as (str, str,...) or ([str, callback, args], [...) if isinstance(e0, tuple) or isinstance(e0, list): te = [x[0] for x in elements] # Copy text component self.els = elements # Retain original elements = te if callback is not dolittle: raise ValueError('Cannot specify callback.') callback = self._despatch if width is None: # Allow for square at end for arrow self.textwidth = max(writer.stringlen(s) for s in elements) width = self.textwidth + 2 + height else: self.textwidth = width super().__init__(writer, row, col, height, width, fgcolor, bgcolor, bdcolor, value, True) super()._set_callbacks(callback, args) self.select_color = select_color self.fontcolor = self.fgcolor if fontcolor is None else fontcolor self.elements = elements self.dlines = dlines def show(self): if super().show(): x, y = self.col, self.row self._draw(x, y) if self._value is not None: display.print_left(self.writer, x, y + 1, self.elements[self._value], self.fontcolor) def textvalue(self, text=None): # if no arg return current text if text is None: return self.elements[self._value] else: # set value by text try: v = self.elements.index(text) except ValueError: v = None else: if v != self._value: self.value(v) return v def _draw(self, x, y): self.draw_border() display.vline(x + self.width - self.height, y, self.height, self.fgcolor) xcentre = x + self.width - self.height // 2 # Centre of triangle ycentre = y + self.height // 2 halflength = (self.height - 8) // 2 length = halflength * 2 if length > 0: display.hline(xcentre - halflength, ycentre - halflength, length, self.fgcolor) display.line(xcentre - halflength, ycentre - halflength, xcentre, ycentre + halflength, self.fgcolor) display.line(xcentre + halflength, ycentre - halflength, xcentre, ycentre + halflength, self.fgcolor) def do_sel(self): # Select was pushed if len(self.elements) > 1: args = (self.writer, self.row - 2, self.col - 2, self) Screen.change(_ListDialog, args = args) display.ipdev.adj_mode(True) # If in 3-button mode, go into adjust mode def _despatch(self, _): # Run the callback specified in elements x = self.els[self()] x[1](self, *x[2])