From fb5b2418c7ead54eed971da49d094c7bec4c82b4 Mon Sep 17 00:00:00 2001 From: Stefan Grosshauser Date: Thu, 10 Oct 2019 06:27:55 +0200 Subject: [PATCH 1/3] add option 'with_border' and document it to avoid drawing the border around the curses screen. The space of the border is still occupied, but empty. --- cursesmenu/curses_menu.py | 7 +++++-- cursesmenu/selection_menu.py | 6 ++++-- docs/usage.rst | 7 +++++++ 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/cursesmenu/curses_menu.py b/cursesmenu/curses_menu.py index 07e730b..ee7b677 100644 --- a/cursesmenu/curses_menu.py +++ b/cursesmenu/curses_menu.py @@ -14,12 +14,13 @@ class CursesMenu(object): currently_active_menu = None stdscr = None - def __init__(self, title=None, subtitle=None, show_exit_option=True): + def __init__(self, title=None, subtitle=None, show_exit_option=True, with_border=True): """ :ivar str title: The title of the menu :ivar str subtitle: The subtitle of the menu :ivar bool show_exit_option: Whether this menu should show an exit item by default. Can be overridden \ when the menu is started + :ivar bool with_border: Whether a border should be drawn around the menu. :ivar items: The list of MenuItems that the menu will display :vartype items: list[:class:`MenuItem`] :ivar CursesMenu parent: The parent of this menu @@ -42,6 +43,7 @@ def __init__(self, title=None, subtitle=None, show_exit_option=True): self.title = title self.subtitle = subtitle self.show_exit_option = show_exit_option + self.with_border = with_border self.items = list() @@ -196,8 +198,9 @@ def draw(self): """ Redraws the menu and refreshes the screen. Should be called whenever something changes that needs to be redrawn. """ + if(self.with_border): + self.screen.border(0) - self.screen.border(0) if self.title is not None: self.screen.addstr(2, 2, self.title, curses.A_STANDOUT) if self.subtitle is not None: diff --git a/cursesmenu/selection_menu.py b/cursesmenu/selection_menu.py index 10c9b69..1d9ecef 100644 --- a/cursesmenu/selection_menu.py +++ b/cursesmenu/selection_menu.py @@ -7,11 +7,13 @@ class SelectionMenu(CursesMenu): A menu that simplifies item creation, just give it a list of strings and it builds the menu for you """ - def __init__(self, strings, title=None, subtitle=None, show_exit_option=True): + def __init__(self, strings, title=None, subtitle=None, show_exit_option=True, + with_border=True): """ :ivar list[str] strings: The list of strings this menu should be built from """ - super(SelectionMenu, self).__init__(title, subtitle, show_exit_option) + super(SelectionMenu, self).__init__(title, subtitle, show_exit_option, + with_border) for index, item in enumerate(strings): self.append_item(SelectionItem(item, index, self)) diff --git a/docs/usage.rst b/docs/usage.rst index a0b50ce..213bd04 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -73,3 +73,10 @@ Which is equivalent to:: menu.join() selection = menu.selected_option + +If you want to avoid wasting space, maybe because your screen is really small, you can disable the border +drawn around the menu using the argument `with_border=False`. + + menu = CursesMenu("This is a menu!", "It has a subtitle too!", with_border=False) + +This option is also available for :py:class:`~cursesmenu.SelectionMenu`. From 5a67ff5677c89701fb2b4ce21821884fb108d6f7 Mon Sep 17 00:00:00 2001 From: Stefan Grosshauser Date: Thu, 10 Oct 2019 08:14:38 +0200 Subject: [PATCH 2/3] add option 'tight' and document it to make the menu fit better to tiny screens. This also computes some magic numbers like positions of menu items. Note that this commit will change the behaviour of the with_border option. In case of with_border=False, the border will disappear instead of be filled by blanks. --- cursesmenu/curses_menu.py | 41 +++++++++++++++++++++++++++++------- cursesmenu/selection_menu.py | 4 ++-- docs/usage.rst | 7 +++++- 3 files changed, 41 insertions(+), 11 deletions(-) diff --git a/cursesmenu/curses_menu.py b/cursesmenu/curses_menu.py index ee7b677..efc4981 100644 --- a/cursesmenu/curses_menu.py +++ b/cursesmenu/curses_menu.py @@ -14,13 +14,14 @@ class CursesMenu(object): currently_active_menu = None stdscr = None - def __init__(self, title=None, subtitle=None, show_exit_option=True, with_border=True): + def __init__(self, title=None, subtitle=None, show_exit_option=True, with_border=True, tight=False): """ :ivar str title: The title of the menu :ivar str subtitle: The subtitle of the menu :ivar bool show_exit_option: Whether this menu should show an exit item by default. Can be overridden \ when the menu is started :ivar bool with_border: Whether a border should be drawn around the menu. + :ivar bool tight: Whether to save screen space by not inserting some blanks and newlines as spacing. :ivar items: The list of MenuItems that the menu will display :vartype items: list[:class:`MenuItem`] :ivar CursesMenu parent: The parent of this menu @@ -44,6 +45,7 @@ def __init__(self, title=None, subtitle=None, show_exit_option=True, with_border self.subtitle = subtitle self.show_exit_option = show_exit_option self.with_border = with_border + self.tight = tight self.items = list() @@ -198,28 +200,51 @@ def draw(self): """ Redraws the menu and refreshes the screen. Should be called whenever something changes that needs to be redrawn. """ + origin_line = 0 + origin_col = 0 if(self.with_border): self.screen.border(0) + origin_line += 1 + origin_col += 1 + if(not self.tight): + origin_col += 1 + title_line = origin_line + title_col = origin_col if self.title is not None: - self.screen.addstr(2, 2, self.title, curses.A_STANDOUT) + if(not self.tight): + title_line += 1 + self.screen.addstr(title_line, title_col, self.title, curses.A_STANDOUT) + subtitle_line = title_line + subtitle_col = origin_col if self.subtitle is not None: - self.screen.addstr(4, 2, self.subtitle, curses.A_BOLD) - + # if there is no subtitle, the value is the same as for the title + subtitle_line += 1 + if(not self.tight): + subtitle_line += 1 + self.screen.addstr(subtitle_line, subtitle_col, self.subtitle, curses.A_BOLD) + + items_start_line = subtitle_line + 1 + items_start_col = origin_col + if(not self.tight): + items_start_col += 2 for index, item in enumerate(self.items): if self.current_option == index: text_style = self.highlight else: text_style = self.normal - self.screen.addstr(5 + index, 4, item.show(index), text_style) + self.screen.addstr(items_start_line + index, items_start_col, item.show(index), text_style) screen_rows, screen_cols = CursesMenu.stdscr.getmaxyx() + # in case the menu does not fit on the screen, scroll the 'viewport' top_row = 0 - if 6 + len(self.items) > screen_rows: - if screen_rows + self.current_option < 6 + len(self.items): + # if the last item is outside the screen... + if items_start_line + 1 + len(self.items) > screen_rows: + # and if + if items_start_line + 1 + len(self.items) - self.current_option > screen_rows: top_row = self.current_option else: - top_row = 6 + len(self.items) - screen_rows + top_row = items_start_line + 1 + len(self.items) - screen_rows self.screen.refresh(top_row, 0, 0, 0, screen_rows - 1, screen_cols - 1) diff --git a/cursesmenu/selection_menu.py b/cursesmenu/selection_menu.py index 1d9ecef..3451611 100644 --- a/cursesmenu/selection_menu.py +++ b/cursesmenu/selection_menu.py @@ -8,12 +8,12 @@ class SelectionMenu(CursesMenu): """ def __init__(self, strings, title=None, subtitle=None, show_exit_option=True, - with_border=True): + with_border=True, tight=False): """ :ivar list[str] strings: The list of strings this menu should be built from """ super(SelectionMenu, self).__init__(title, subtitle, show_exit_option, - with_border) + with_border, tight) for index, item in enumerate(strings): self.append_item(SelectionItem(item, index, self)) diff --git a/docs/usage.rst b/docs/usage.rst index 213bd04..b0badeb 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -79,4 +79,9 @@ drawn around the menu using the argument `with_border=False`. menu = CursesMenu("This is a menu!", "It has a subtitle too!", with_border=False) -This option is also available for :py:class:`~cursesmenu.SelectionMenu`. +Furthermore some spacings, like between title and subtitle and in front of menu items, will not appear +when the `tight=True` is passed to the menu constructors. + + menu = CursesMenu("This is a menu!", "It has a subtitle too!", tight=True) + +These two options are also available for :py:class:`~cursesmenu.SelectionMenu`. From ff2ff36bfb05b89be2cf1b6c632c1d6490613ad4 Mon Sep 17 00:00:00 2001 From: Stefan Grosshauser Date: Thu, 10 Oct 2019 08:16:33 +0200 Subject: [PATCH 3/3] introduce the with_border and tight options in the example --- examples/example.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/examples/example.py b/examples/example.py index 9026c93..1acd349 100644 --- a/examples/example.py +++ b/examples/example.py @@ -3,14 +3,18 @@ def main(): - menu = CursesMenu("Root Menu", "Root Menu Subtitle") + with_border = True + tight = False + menu = CursesMenu("Root Menu", "Root Menu Subtitle", with_border=with_border, tight=tight) item1 = MenuItem("Item 1", menu) function_item = FunctionItem("Fun item", input, ["Enter an input: "]) command_item = CommandItem("Command", "python examples/example.py") - submenu = SelectionMenu(["item1", "item2", "item3"]) + + submenu = SelectionMenu(["item1", "item2", "item3"], with_border=with_border, tight=tight) submenu_item = SubmenuItem("Submenu item", submenu=submenu) submenu_item.set_menu(menu) - submenu_2 = CursesMenu("Submenu Title", "Submenu subtitle") + + submenu_2 = CursesMenu("Submenu Title", "Submenu subtitle", with_border=with_border, tight=tight) function_item_2 = FunctionItem("Fun item", input, ["Enter an input"]) item2 = MenuItem("Another Item") submenu_2.append_item(function_item_2)