diff --git a/cursesmenu/curses_menu.py b/cursesmenu/curses_menu.py index 07e730b..efc4981 100644 --- a/cursesmenu/curses_menu.py +++ b/cursesmenu/curses_menu.py @@ -14,12 +14,14 @@ 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, 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 @@ -42,6 +44,8 @@ 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.tight = tight self.items = list() @@ -196,27 +200,51 @@ def draw(self): """ Redraws the menu and refreshes the screen. Should be called whenever something changes that needs to be redrawn. """ - - self.screen.border(0) + 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 10c9b69..3451611 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, 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) + super(SelectionMenu, self).__init__(title, subtitle, show_exit_option, + 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 a0b50ce..b0badeb 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -73,3 +73,15 @@ 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) + +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`. 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)