From 4f899e3866d81d0e7fec5d496578a0fe81617e72 Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 12 Apr 2022 19:46:55 +1000 Subject: Downstream new simple menu feature (#1055) * Downstream new simple menu feature * Fix flake8 * Update * Fix flake8 Co-authored-by: Daniel Girtler --- archinstall/lib/menu/global_menu.py | 4 ++ archinstall/lib/menu/list_manager.py | 5 ++- archinstall/lib/menu/selection_menu.py | 18 +++++---- archinstall/lib/menu/simple_menu.py | 74 +++++++++++++++++++++++++--------- 4 files changed, 72 insertions(+), 29 deletions(-) (limited to 'archinstall/lib/menu') diff --git a/archinstall/lib/menu/global_menu.py b/archinstall/lib/menu/global_menu.py index 1cd39553..50d3180b 100644 --- a/archinstall/lib/menu/global_menu.py +++ b/archinstall/lib/menu/global_menu.py @@ -151,6 +151,10 @@ class GlobalMenu(GeneralMenu): _('Set automatic time sync (NTP)'), lambda preset: self._select_ntp(preset), default=True) + self._menu_options['__separator__'] = \ + Selector( + '', + enabled=True) self._menu_options['save_config'] = \ Selector( _('Save configuration'), diff --git a/archinstall/lib/menu/list_manager.py b/archinstall/lib/menu/list_manager.py index 4ca33db2..5b4f720c 100644 --- a/archinstall/lib/menu/list_manager.py +++ b/archinstall/lib/menu/list_manager.py @@ -129,7 +129,7 @@ class ListManager: self.header = header if header else None self.cancel_action = str(_('Cancel')) self.confirm_action = str(_('Confirm and exit')) - self.separator = '==>' + self.separator = '' self.bottom_list = [self.confirm_action,self.cancel_action] self.bottom_item = [self.cancel_action] self.base_actions = base_actions if base_actions else [str(_('Add')),str(_('Copy')),str(_('Edit')),str(_('Delete'))] @@ -155,7 +155,8 @@ class ListManager: sort=False, clear_screen=False, clear_menu_on_exit=False, - header=self.header).run() + header=self.header, + skip_empty_entries=True).run() if not target or target in self.bottom_list: break diff --git a/archinstall/lib/menu/selection_menu.py b/archinstall/lib/menu/selection_menu.py index 18313360..cbcb2583 100644 --- a/archinstall/lib/menu/selection_menu.py +++ b/archinstall/lib/menu/selection_menu.py @@ -116,6 +116,9 @@ class Selector: self._description = description def menu_text(self) -> str: + if self._description == '': # special menu option for __separator__ + return '' + current = '' if self._display_func: @@ -273,21 +276,22 @@ class GeneralMenu: # This will just help the user with the next following questions. self._set_kb_language() enabled_menus = self._menus_to_enable() - menu_text = [m.text for m in enabled_menus.values()] + menu_options = [m.text for m in enabled_menus.values()] selection = Menu( _('Set/Modify the below options'), - menu_text, + menu_options, sort=False, cursor_index=cursor_pos, preview_command=self._preview_display, - preview_size=self.preview_size + preview_size=self.preview_size, + skip_empty_entries=True ).run() if selection and self.auto_cursor: - cursor_pos = menu_text.index(selection) + 1 # before the strip otherwise fails - if cursor_pos >= len(menu_text): - cursor_pos = len(menu_text) - 1 + cursor_pos = menu_options.index(selection) + 1 # before the strip otherwise fails + if cursor_pos >= len(menu_options): + cursor_pos = len(menu_options) - 1 selection = selection.strip() if selection: # if this calls returns false, we exit the menu. We allow for an callback for special processing on realeasing control @@ -416,4 +420,4 @@ class GeneralMenu: def _select_archinstall_language(self, default_lang): language = select_archinstall_language(default_lang) self._translation.activate(language) - return language \ No newline at end of file + return language diff --git a/archinstall/lib/menu/simple_menu.py b/archinstall/lib/menu/simple_menu.py index a9d6d7ec..a0a241bd 100644 --- a/archinstall/lib/menu/simple_menu.py +++ b/archinstall/lib/menu/simple_menu.py @@ -298,6 +298,7 @@ class TerminalMenu: selection: "TerminalMenu.Selection", viewport: "TerminalMenu.Viewport", cycle_cursor: bool = True, + skip_indices: List[int] = [], ): self._menu_entries = list(menu_entries) self._search = search @@ -305,6 +306,7 @@ class TerminalMenu: self._viewport = viewport self._cycle_cursor = cycle_cursor self._active_displayed_index = None # type: Optional[int] + self._skip_indices = skip_indices self.update_view() def update_view(self) -> None: @@ -328,6 +330,9 @@ class TerminalMenu: self._active_displayed_index = 0 self._viewport.keep_visible(self._active_displayed_index) + if self._active_displayed_index in self._skip_indices: + self.increment_active_index() + def decrement_active_index(self) -> None: if self._active_displayed_index is not None: if self._active_displayed_index > 0: @@ -336,6 +341,9 @@ class TerminalMenu: self._active_displayed_index = len(self._displayed_index_to_menu_index) - 1 self._viewport.keep_visible(self._active_displayed_index) + if self._active_displayed_index in self._skip_indices: + self.decrement_active_index() + def is_visible(self, menu_index: int) -> bool: return menu_index in self._menu_index_to_displayed_index and ( self._viewport.lower_index @@ -584,6 +592,7 @@ class TerminalMenu: show_search_hint_text: Optional[str] = None, show_shortcut_hints: bool = DEFAULT_SHOW_SHORTCUT_HINTS, show_shortcut_hints_in_status_bar: bool = DEFAULT_SHOW_SHORTCUT_HINTS_IN_STATUS_BAR, + skip_empty_entries: bool = False, status_bar: Optional[Union[str, Iterable[str], Callable[[str], str]]] = None, status_bar_below_preview: bool = DEFAULT_STATUS_BAR_BELOW_PREVIEW, status_bar_style: Optional[Iterable[str]] = DEFAULT_STATUS_BAR_STYLE, @@ -591,24 +600,35 @@ class TerminalMenu: ): def extract_shortcuts_menu_entries_and_preview_arguments( entries: Iterable[str], - ) -> Tuple[List[str], List[str], List[str]]: + ) -> Tuple[List[str], List[Optional[str]], List[Optional[str]], List[int]]: separator_pattern = re.compile(r"([^\\])\|") escaped_separator_pattern = re.compile(r"\\\|") menu_entry_pattern = re.compile(r"^(?:\[(\S)\]\s*)?([^\x1F]+)(?:\x1F([^\x1F]*))?") - shortcut_keys = [] - menu_entries = [] - preview_arguments = [] - for entry in entries: - unit_separated_entry = escaped_separator_pattern.sub("|", separator_pattern.sub("\\1\x1F", entry)) - match_obj = menu_entry_pattern.match(unit_separated_entry) - assert match_obj is not None - shortcut_key = match_obj.group(1) - display_text = match_obj.group(2) - preview_argument = match_obj.group(3) - shortcut_keys.append(shortcut_key) - menu_entries.append(display_text) - preview_arguments.append(preview_argument) - return menu_entries, shortcut_keys, preview_arguments + shortcut_keys = [] # type: List[Optional[str]] + menu_entries = [] # type: List[str] + preview_arguments = [] # type: List[Optional[str]] + skip_indices = [] # type: List[int] + + for idx, entry in enumerate(entries): + if entry is None or (entry == "" and skip_empty_entries): + shortcut_keys.append(None) + menu_entries.append("") + preview_arguments.append(None) + skip_indices.append(idx) + else: + unit_separated_entry = escaped_separator_pattern.sub("|", separator_pattern.sub("\\1\x1F", entry)) + match_obj = menu_entry_pattern.match(unit_separated_entry) + # this is none in case the entry was an emtpy string which + # will be interpreted as a separator + assert match_obj is not None + shortcut_key = match_obj.group(1) + display_text = match_obj.group(2) + preview_argument = match_obj.group(3) + shortcut_keys.append(shortcut_key) + menu_entries.append(display_text) + preview_arguments.append(preview_argument) + + return menu_entries, shortcut_keys, preview_arguments, skip_indices def convert_preselected_entries_to_indices( preselected_indices_or_entries: Iterable[Union[str, int]] @@ -641,7 +661,7 @@ class TerminalMenu: title_or_status_bar: Optional[Union[str, Iterable[str]]], show_shortcut_hints: bool, menu_entries: Iterable[str], - shortcut_keys: Iterable[str], + shortcut_keys: Iterable[Optional[str]], shortcut_hints_in_parentheses: bool, ) -> Tuple[str, ...]: if title_or_status_bar is None: @@ -662,6 +682,7 @@ class TerminalMenu: self._menu_entries, self._shortcut_keys, self._preview_arguments, + self._skip_indices, ) = extract_shortcuts_menu_entries_and_preview_arguments(menu_entries) self._shortcuts_defined = any(key is not None for key in self._shortcut_keys) self._accept_keys = tuple(accept_keys) @@ -749,7 +770,9 @@ class TerminalMenu: 0, 0, ) - self._view = self.View(self._menu_entries, self._search, self._selection, self._viewport, self._cycle_cursor) + self._view = self.View( + self._menu_entries, self._search, self._selection, self._viewport, self._cycle_cursor, self._skip_indices + ) if cursor_index and 0 < cursor_index < len(self._menu_entries): self._view.active_menu_index = cursor_index self._search.change_callback = self._view.update_view @@ -767,7 +790,7 @@ class TerminalMenu: @staticmethod def _get_shortcut_hints_line( menu_entries: Iterable[str], - shortcut_keys: Iterable[str], + shortcut_keys: Iterable[Optional[str]], shortcut_hints_in_parentheses: bool, ) -> Optional[str]: shortcut_hints_line = ", ".join( @@ -1139,7 +1162,7 @@ class TerminalMenu: e.stderr.decode(encoding=self._user_locale, errors="replace").strip() ) from e else: - preview_string = self._preview_command(preview_argument) + preview_string = self._preview_command(preview_argument) if preview_argument is not None else "" return preview_string @static_variables( @@ -1336,7 +1359,9 @@ class TerminalMenu: displayed_index = 0 for displayed_index, _, _ in self._view: self._tty_out.write("\r" + cursor_width * self._codename_to_terminal_code["cursor_right"]) - if displayed_index in displayed_selected_indices: + if displayed_index in self._skip_indices: + self._tty_out.write("") + elif displayed_index in displayed_selected_indices: self._tty_out.write(checked_multi_select_cursor) else: self._tty_out.write(unchecked_multi_select_cursor) @@ -1779,6 +1804,12 @@ def get_argumentparser() -> argparse.ArgumentParser: default=True, help="show shortcut hints in the menu title", ) + parser.add_argument( + "--skip-empty-entries", + action="store_true", + dest="skip_empty_entries", + help="Interpret an empty string in menu entries as an empty menu entry", + ) parser.add_argument( "-b", "--status-bar", @@ -1837,6 +1868,8 @@ def parse_arguments() -> AttributeDict: args = AttributeDict({key: value for key, value in vars(parser.parse_args()).items()}) if not args.print_version and not args.entries: raise NoMenuEntriesError("No menu entries given!") + if args.skip_empty_entries: + args.entries = [entry if entry != "None" else None for entry in args.entries] if args.cursor_style != "": args.cursor_style = tuple(args.cursor_style.split(",")) else: @@ -1933,6 +1966,7 @@ def main() -> None: show_search_hint_text=args.show_search_hint_text, show_shortcut_hints=args.show_shortcut_hints, show_shortcut_hints_in_status_bar=args.show_shortcut_hints_in_status_bar, + skip_empty_entries=args.skip_empty_entries, status_bar=args.status_bar, status_bar_below_preview=args.status_bar_below_preview, status_bar_style=args.status_bar_style, -- cgit v1.2.3-70-g09d2