]> git.ipfire.org Git - people/ms/bricklayer.git/commitdiff
tui: Add own implementation of button and list choice windows
authorMichael Tremer <michael.tremer@ipfire.org>
Sat, 8 May 2021 16:30:25 +0000 (16:30 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Sat, 8 May 2021 16:30:25 +0000 (16:30 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
src/python/errors.py
src/python/tui.py

index cfd1cee96f71ee365f3153d3250d91f68515e5d5..1b94b0065600453c04432101963a4e5f2da9a14f 100644 (file)
@@ -23,3 +23,10 @@ class InstallError(Exception):
 
 class InstallAbortedError(InstallError):
        pass
+
+
+class UserCanceledError(InstallError):
+       """
+               Raised when the user wants to cancel the process
+       """
+       pass
index 80d5c3bab45366f5cb22174c7dce145b2cda1a59..0ac99ac59d9903ab20a5b8f04920605ca4a3ee22 100644 (file)
@@ -26,6 +26,12 @@ from .i18n import _
 # Setup logging
 log = logging.getLogger("bricklayer.tui")
 
+def abort():
+       raise InstallAbortedError
+
+def cancel():
+       raise UserCanceledError
+
 class Tui(object):
        def __init__(self, bricklayer):
                self.bricklayer = bricklayer
@@ -125,40 +131,28 @@ class Tui(object):
                if buttons is None:
                        buttons = (_("OK"), _("Cancel"))
 
-               return snack.ButtonChoiceWindow(self.screen, title=title, text=text,
+               window = ButtonsWindow(self, title=title, text=text,
                        buttons=buttons, width=width, help=help)
 
+               return window.run()
+
        def error(self, title, text, buttons=None, width=40):
                if not buttons:
-                       buttons = [_("Abort Installation")]
+                       buttons = [
+                               (_("Abort Installation"), abort),
+                       ]
 
                return self.message(title, text, buttons=buttons, width=width)
 
        def progress(self, *args, **kwargs):
                return ProgressWindow(self, *args, **kwargs)
 
-       def select(self, title, text, items, buttons=None, default=None, help=None, width=40):
-               # Translate default
-               if default:
-                       default = items.get(default, None)
-
-               # Convert items into a list which is sorted by its values
-               items = sorted(items.items(), key=lambda item: item[1])
+       def select(self, title, text, items, default=None, buttons=None, height=None,
+                       width=40, help=None):
+               window = ListboxChoiceWindow(self, title, text, items, default=default,
+                       buttons=buttons, height=height, width=width, help=help)
 
-               # Set some default buttons
-               if buttons is None:
-                       buttons = (_("Select"), _("Cancel"))
-
-               # Show the window
-               button, item = snack.ListboxChoiceWindow(self.screen, title, text,
-                       [value for key, value in items], buttons=buttons, default=default, help=help,
-                       width=width)
-
-               # Find the selected item
-               key, value = items[item]
-
-               # Return the key
-               return key
+               return window.run()
 
        def multi_select(self, title, text, items, selection=[], buttons=None,
                        width=40, height=None, help=None):
@@ -192,6 +186,128 @@ class Tui(object):
                return checkboxes.getSelection()
 
 
+class Window(object):
+       def __init__(self, tui, title, text, height=None, width=None, help=None):
+               self.tui = tui
+               self.title = title
+               self.text = text
+               self.height = height
+               self.width = width
+               self.help = help
+
+       @property
+       def max_height(self):
+               """
+                       The maximum height of any window
+               """
+               return self.tui.screen.height - 12
+
+       def run(self):
+               raise NotImplementedError
+
+
+class ButtonsWindow(Window):
+       def __init__(self, tui, title, text, buttons=None, height=None, width=None, help=None):
+               Window.__init__(self, tui, title, text, height=height, width=width, help=help)
+
+               # Configure some default buttons
+               if buttons is None:
+                       buttons = [
+                               (_("Ok"), None),
+                               (_("Cancel"), cancel),
+                       ]
+
+               self.buttons = snack.ButtonBar(self.tui.screen, buttons)
+
+       def _make_window(self):
+               # Create a grid
+               grid = snack.GridFormHelp(self.tui.screen, self.title, self.help, 1, 2)
+
+               # Create the box that shows the text
+               textbox = snack.TextboxReflowed(self.width, self.text, maxHeight=self.max_height)
+               grid.add(textbox, 0, 0, padding=(0, 0, 0, 1))
+
+               # Create the button bar
+               grid.add(self.buttons, 0, 1, growx=True)
+
+               return grid
+
+       def run(self):
+               window = self._make_window()
+
+               # Run the window
+               button = window.runOnce()
+
+               # Which button was pressed?
+               callback = self.buttons.buttonPressed(button)
+
+               # If the button had a callback, we will call the callback
+               if callable(callback):
+                       return callback()
+
+               # Otherwise call the default action
+               return self.default_action()
+
+       def default_action(self):
+               """
+                       Called when a button did not have a callback
+               """
+               pass
+
+
+class ListboxChoiceWindow(ButtonsWindow):
+       def __init__(self, tui, title, text, items, default=None, buttons=None, height=None,
+                       width=None, help=None):
+               # Set height to number of items by default
+               if height is None:
+                       height = len(items)
+
+               # Set some default buttons
+               if buttons is None:
+                       buttons = [
+                               (_("Select"), None),
+                               (_("Cancel"), cancel),
+                       ]
+
+               super().__init__(tui, title, text, buttons=buttons,
+                       height=height, width=width, help=help)
+
+               self.items = items
+               self.default = default
+
+               # Should we enable scrolling?
+               scroll = height < len(items)
+
+               # Create the list box
+               self.listbox = snack.Listbox(self.height, scroll=scroll, returnExit=True)
+               for key in self.items:
+                       self.listbox.append(self.items[key], key)
+
+               # Set the default value
+               if self.default:
+                       self.listbox.setCurrent(self.default)
+
+       def _make_window(self):
+               # Create a grid
+               grid = snack.GridFormHelp(self.tui.screen, self.title, self.help, 1, 3)
+
+               # Create the text box
+               textbox = snack.TextboxReflowed(self.width, self.text)
+               grid.add(textbox, 0, 0)
+
+               # Add the listbox
+               grid.add(self.listbox, 0, 1, padding=(0, 1, 0, 1))
+
+               # Add the buttons
+               grid.add(self.buttons, 0, 2, growx=True)
+
+               return grid
+
+       def default_action(self):
+               # Return the selected item
+               return self.listbox.current()
+
+
 class ProgressWindow(object):
        def __init__(self, tui, title, text, max_value=0, width=60, help=None):
                self.tui = tui