diff --git a/CHANGES.rst b/CHANGES.rst index 0ca1283..bd80a99 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,7 @@ CHANGE HISTORY LATEST ------ +- Added border option to PopupMenu 1.15.0 ------ diff --git a/asciimatics/widgets/popupmenu.py b/asciimatics/widgets/popupmenu.py index 2af4780..c5fe5e2 100644 --- a/asciimatics/widgets/popupmenu.py +++ b/asciimatics/widgets/popupmenu.py @@ -16,21 +16,27 @@ class PopupMenu(Frame): palette = defaultdict(lambda: (Screen.COLOUR_WHITE, Screen.A_NORMAL, Screen.COLOUR_CYAN)) palette["focus_button"] = (Screen.COLOUR_CYAN, Screen.A_NORMAL, Screen.COLOUR_WHITE) - def __init__(self, screen, menu_items, x, y): + def __init__(self, screen, menu_items, x, y, has_border=False): """ :param screen: The Screen being used for this pop-up. :param menu_items: a list of items to be displayed in the menu. :param x: The X coordinate for the desired pop-up. :param y: The Y coordinate for the desired pop-up. + :param has_border: Whether the menu has a border box. Defaults to False. The menu_items parameter is a list of 2-tuples, which define the text to be displayed in the menu and the function to call when that menu item is clicked. For example: menu_items = [("Open", file_open), ("Save", file_save), ("Close", file_close)] """ + + border_adjustment = 0 + if has_border: + border_adjustment = 2 # We add one character to each side for the border + # Sort out location based on width of menu text. - w = max(len(i[0]) for i in menu_items) - h = len(menu_items) + w = max(len(i[0]) for i in menu_items) + border_adjustment + h = len(menu_items) + border_adjustment if x + w >= screen.width: x -= w - 1 if y + h >= screen.height: @@ -38,7 +44,7 @@ def __init__(self, screen, menu_items, x, y): # Construct the Frame super().__init__( - screen, h, w, x=x, y=y, has_border=False, can_scroll=False, is_modal=True, hover_focus=True) + screen, h, w, x=x, y=y, has_border=has_border, can_scroll=False, is_modal=True, hover_focus=True) # Build the widget to display the time selection. layout = Layout([1], fill_frame=True) diff --git a/tests/test_widgets.py b/tests/test_widgets.py index 81e2190..87f2bab 100644 --- a/tests/test_widgets.py +++ b/tests/test_widgets.py @@ -2905,6 +2905,73 @@ def click(x): self.assertEqual(len(scene.effects), 0) self.assertEqual(self.clicked, 2) + # Reset menu for border test + self.clicked = 0 + popup = PopupMenu(canvas, + [ + ("First", lambda: click(1)), + ("Second", lambda: click(2)), + ("Third", lambda: click(4)) + ], + 0, 0, has_border=True) + scene.add_effect(popup) + scene.reset() + + # Check that the menu is rendered correctly. + for effect in scene.effects: + effect.update(0) + self.assert_canvas_equals( + canvas, + "+------+ \n" + + "|First | \n" + + "|Second| \n" + + "|Third | \n" + + "+------+ \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n") + + # Check it handles a selection as expected + self.process_mouse(scene, [(1, 2, MouseEvent.LEFT_CLICK)]) + self.assertEqual(len(scene.effects), 0) + self.assertEqual(self.clicked, 2) + + # Reset menu for border and title test + self.clicked = 0 + popup = PopupMenu(canvas, + [ + ("First", lambda: click(1)), + ("Second Wider", lambda: click(2)), + ("Third", lambda: click(4)) + ], + 0, 0, has_border=True) + popup.title = 'Test' + scene.add_effect(popup) + scene.reset() + + # Check that the menu is rendered correctly. + for effect in scene.effects: + effect.update(0) + self.assert_canvas_equals( + canvas, + "+--- Test ---+ \n" + + "|First | \n" + + "|Second Wider| \n" + + "|Third | \n" + + "+------------+ \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n") + + # Check it handles a selection as expected + self.process_mouse(scene, [(1, 2, MouseEvent.LEFT_CLICK)]) + self.assertEqual(len(scene.effects), 0) + self.assertEqual(self.clicked, 2) + # Check choice of location at bottom right self.clicked = 0 canvas.reset()