Downstream new simple menu feature (#1055)
* Downstream new simple menu feature * Fix flake8 * Update * Fix flake8 Co-authored-by: Daniel Girtler <girtler.daniel@gmail.com>
This commit is contained in:
parent
f644022e0c
commit
4f899e3866
|
|
@ -151,6 +151,10 @@ class GlobalMenu(GeneralMenu):
|
||||||
_('Set automatic time sync (NTP)'),
|
_('Set automatic time sync (NTP)'),
|
||||||
lambda preset: self._select_ntp(preset),
|
lambda preset: self._select_ntp(preset),
|
||||||
default=True)
|
default=True)
|
||||||
|
self._menu_options['__separator__'] = \
|
||||||
|
Selector(
|
||||||
|
'',
|
||||||
|
enabled=True)
|
||||||
self._menu_options['save_config'] = \
|
self._menu_options['save_config'] = \
|
||||||
Selector(
|
Selector(
|
||||||
_('Save configuration'),
|
_('Save configuration'),
|
||||||
|
|
|
||||||
|
|
@ -129,7 +129,7 @@ class ListManager:
|
||||||
self.header = header if header else None
|
self.header = header if header else None
|
||||||
self.cancel_action = str(_('Cancel'))
|
self.cancel_action = str(_('Cancel'))
|
||||||
self.confirm_action = str(_('Confirm and exit'))
|
self.confirm_action = str(_('Confirm and exit'))
|
||||||
self.separator = '==>'
|
self.separator = ''
|
||||||
self.bottom_list = [self.confirm_action,self.cancel_action]
|
self.bottom_list = [self.confirm_action,self.cancel_action]
|
||||||
self.bottom_item = [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'))]
|
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,
|
sort=False,
|
||||||
clear_screen=False,
|
clear_screen=False,
|
||||||
clear_menu_on_exit=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:
|
if not target or target in self.bottom_list:
|
||||||
break
|
break
|
||||||
|
|
|
||||||
|
|
@ -116,6 +116,9 @@ class Selector:
|
||||||
self._description = description
|
self._description = description
|
||||||
|
|
||||||
def menu_text(self) -> str:
|
def menu_text(self) -> str:
|
||||||
|
if self._description == '': # special menu option for __separator__
|
||||||
|
return ''
|
||||||
|
|
||||||
current = ''
|
current = ''
|
||||||
|
|
||||||
if self._display_func:
|
if self._display_func:
|
||||||
|
|
@ -273,21 +276,22 @@ class GeneralMenu:
|
||||||
# This will just help the user with the next following questions.
|
# This will just help the user with the next following questions.
|
||||||
self._set_kb_language()
|
self._set_kb_language()
|
||||||
enabled_menus = self._menus_to_enable()
|
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(
|
selection = Menu(
|
||||||
_('Set/Modify the below options'),
|
_('Set/Modify the below options'),
|
||||||
menu_text,
|
menu_options,
|
||||||
sort=False,
|
sort=False,
|
||||||
cursor_index=cursor_pos,
|
cursor_index=cursor_pos,
|
||||||
preview_command=self._preview_display,
|
preview_command=self._preview_display,
|
||||||
preview_size=self.preview_size
|
preview_size=self.preview_size,
|
||||||
|
skip_empty_entries=True
|
||||||
).run()
|
).run()
|
||||||
|
|
||||||
if selection and self.auto_cursor:
|
if selection and self.auto_cursor:
|
||||||
cursor_pos = menu_text.index(selection) + 1 # before the strip otherwise fails
|
cursor_pos = menu_options.index(selection) + 1 # before the strip otherwise fails
|
||||||
if cursor_pos >= len(menu_text):
|
if cursor_pos >= len(menu_options):
|
||||||
cursor_pos = len(menu_text) - 1
|
cursor_pos = len(menu_options) - 1
|
||||||
selection = selection.strip()
|
selection = selection.strip()
|
||||||
if selection:
|
if selection:
|
||||||
# if this calls returns false, we exit the menu. We allow for an callback for special processing on realeasing control
|
# 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):
|
def _select_archinstall_language(self, default_lang):
|
||||||
language = select_archinstall_language(default_lang)
|
language = select_archinstall_language(default_lang)
|
||||||
self._translation.activate(language)
|
self._translation.activate(language)
|
||||||
return language
|
return language
|
||||||
|
|
|
||||||
|
|
@ -298,6 +298,7 @@ class TerminalMenu:
|
||||||
selection: "TerminalMenu.Selection",
|
selection: "TerminalMenu.Selection",
|
||||||
viewport: "TerminalMenu.Viewport",
|
viewport: "TerminalMenu.Viewport",
|
||||||
cycle_cursor: bool = True,
|
cycle_cursor: bool = True,
|
||||||
|
skip_indices: List[int] = [],
|
||||||
):
|
):
|
||||||
self._menu_entries = list(menu_entries)
|
self._menu_entries = list(menu_entries)
|
||||||
self._search = search
|
self._search = search
|
||||||
|
|
@ -305,6 +306,7 @@ class TerminalMenu:
|
||||||
self._viewport = viewport
|
self._viewport = viewport
|
||||||
self._cycle_cursor = cycle_cursor
|
self._cycle_cursor = cycle_cursor
|
||||||
self._active_displayed_index = None # type: Optional[int]
|
self._active_displayed_index = None # type: Optional[int]
|
||||||
|
self._skip_indices = skip_indices
|
||||||
self.update_view()
|
self.update_view()
|
||||||
|
|
||||||
def update_view(self) -> None:
|
def update_view(self) -> None:
|
||||||
|
|
@ -328,6 +330,9 @@ class TerminalMenu:
|
||||||
self._active_displayed_index = 0
|
self._active_displayed_index = 0
|
||||||
self._viewport.keep_visible(self._active_displayed_index)
|
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:
|
def decrement_active_index(self) -> None:
|
||||||
if self._active_displayed_index is not None:
|
if self._active_displayed_index is not None:
|
||||||
if self._active_displayed_index > 0:
|
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._active_displayed_index = len(self._displayed_index_to_menu_index) - 1
|
||||||
self._viewport.keep_visible(self._active_displayed_index)
|
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:
|
def is_visible(self, menu_index: int) -> bool:
|
||||||
return menu_index in self._menu_index_to_displayed_index and (
|
return menu_index in self._menu_index_to_displayed_index and (
|
||||||
self._viewport.lower_index
|
self._viewport.lower_index
|
||||||
|
|
@ -584,6 +592,7 @@ class TerminalMenu:
|
||||||
show_search_hint_text: Optional[str] = None,
|
show_search_hint_text: Optional[str] = None,
|
||||||
show_shortcut_hints: bool = DEFAULT_SHOW_SHORTCUT_HINTS,
|
show_shortcut_hints: bool = DEFAULT_SHOW_SHORTCUT_HINTS,
|
||||||
show_shortcut_hints_in_status_bar: bool = DEFAULT_SHOW_SHORTCUT_HINTS_IN_STATUS_BAR,
|
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: Optional[Union[str, Iterable[str], Callable[[str], str]]] = None,
|
||||||
status_bar_below_preview: bool = DEFAULT_STATUS_BAR_BELOW_PREVIEW,
|
status_bar_below_preview: bool = DEFAULT_STATUS_BAR_BELOW_PREVIEW,
|
||||||
status_bar_style: Optional[Iterable[str]] = DEFAULT_STATUS_BAR_STYLE,
|
status_bar_style: Optional[Iterable[str]] = DEFAULT_STATUS_BAR_STYLE,
|
||||||
|
|
@ -591,24 +600,35 @@ class TerminalMenu:
|
||||||
):
|
):
|
||||||
def extract_shortcuts_menu_entries_and_preview_arguments(
|
def extract_shortcuts_menu_entries_and_preview_arguments(
|
||||||
entries: Iterable[str],
|
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"([^\\])\|")
|
separator_pattern = re.compile(r"([^\\])\|")
|
||||||
escaped_separator_pattern = re.compile(r"\\\|")
|
escaped_separator_pattern = re.compile(r"\\\|")
|
||||||
menu_entry_pattern = re.compile(r"^(?:\[(\S)\]\s*)?([^\x1F]+)(?:\x1F([^\x1F]*))?")
|
menu_entry_pattern = re.compile(r"^(?:\[(\S)\]\s*)?([^\x1F]+)(?:\x1F([^\x1F]*))?")
|
||||||
shortcut_keys = []
|
shortcut_keys = [] # type: List[Optional[str]]
|
||||||
menu_entries = []
|
menu_entries = [] # type: List[str]
|
||||||
preview_arguments = []
|
preview_arguments = [] # type: List[Optional[str]]
|
||||||
for entry in entries:
|
skip_indices = [] # type: List[int]
|
||||||
unit_separated_entry = escaped_separator_pattern.sub("|", separator_pattern.sub("\\1\x1F", entry))
|
|
||||||
match_obj = menu_entry_pattern.match(unit_separated_entry)
|
for idx, entry in enumerate(entries):
|
||||||
assert match_obj is not None
|
if entry is None or (entry == "" and skip_empty_entries):
|
||||||
shortcut_key = match_obj.group(1)
|
shortcut_keys.append(None)
|
||||||
display_text = match_obj.group(2)
|
menu_entries.append("")
|
||||||
preview_argument = match_obj.group(3)
|
preview_arguments.append(None)
|
||||||
shortcut_keys.append(shortcut_key)
|
skip_indices.append(idx)
|
||||||
menu_entries.append(display_text)
|
else:
|
||||||
preview_arguments.append(preview_argument)
|
unit_separated_entry = escaped_separator_pattern.sub("|", separator_pattern.sub("\\1\x1F", entry))
|
||||||
return menu_entries, shortcut_keys, preview_arguments
|
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(
|
def convert_preselected_entries_to_indices(
|
||||||
preselected_indices_or_entries: Iterable[Union[str, int]]
|
preselected_indices_or_entries: Iterable[Union[str, int]]
|
||||||
|
|
@ -641,7 +661,7 @@ class TerminalMenu:
|
||||||
title_or_status_bar: Optional[Union[str, Iterable[str]]],
|
title_or_status_bar: Optional[Union[str, Iterable[str]]],
|
||||||
show_shortcut_hints: bool,
|
show_shortcut_hints: bool,
|
||||||
menu_entries: Iterable[str],
|
menu_entries: Iterable[str],
|
||||||
shortcut_keys: Iterable[str],
|
shortcut_keys: Iterable[Optional[str]],
|
||||||
shortcut_hints_in_parentheses: bool,
|
shortcut_hints_in_parentheses: bool,
|
||||||
) -> Tuple[str, ...]:
|
) -> Tuple[str, ...]:
|
||||||
if title_or_status_bar is None:
|
if title_or_status_bar is None:
|
||||||
|
|
@ -662,6 +682,7 @@ class TerminalMenu:
|
||||||
self._menu_entries,
|
self._menu_entries,
|
||||||
self._shortcut_keys,
|
self._shortcut_keys,
|
||||||
self._preview_arguments,
|
self._preview_arguments,
|
||||||
|
self._skip_indices,
|
||||||
) = extract_shortcuts_menu_entries_and_preview_arguments(menu_entries)
|
) = extract_shortcuts_menu_entries_and_preview_arguments(menu_entries)
|
||||||
self._shortcuts_defined = any(key is not None for key in self._shortcut_keys)
|
self._shortcuts_defined = any(key is not None for key in self._shortcut_keys)
|
||||||
self._accept_keys = tuple(accept_keys)
|
self._accept_keys = tuple(accept_keys)
|
||||||
|
|
@ -749,7 +770,9 @@ class TerminalMenu:
|
||||||
0,
|
0,
|
||||||
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):
|
if cursor_index and 0 < cursor_index < len(self._menu_entries):
|
||||||
self._view.active_menu_index = cursor_index
|
self._view.active_menu_index = cursor_index
|
||||||
self._search.change_callback = self._view.update_view
|
self._search.change_callback = self._view.update_view
|
||||||
|
|
@ -767,7 +790,7 @@ class TerminalMenu:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _get_shortcut_hints_line(
|
def _get_shortcut_hints_line(
|
||||||
menu_entries: Iterable[str],
|
menu_entries: Iterable[str],
|
||||||
shortcut_keys: Iterable[str],
|
shortcut_keys: Iterable[Optional[str]],
|
||||||
shortcut_hints_in_parentheses: bool,
|
shortcut_hints_in_parentheses: bool,
|
||||||
) -> Optional[str]:
|
) -> Optional[str]:
|
||||||
shortcut_hints_line = ", ".join(
|
shortcut_hints_line = ", ".join(
|
||||||
|
|
@ -1139,7 +1162,7 @@ class TerminalMenu:
|
||||||
e.stderr.decode(encoding=self._user_locale, errors="replace").strip()
|
e.stderr.decode(encoding=self._user_locale, errors="replace").strip()
|
||||||
) from e
|
) from e
|
||||||
else:
|
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
|
return preview_string
|
||||||
|
|
||||||
@static_variables(
|
@static_variables(
|
||||||
|
|
@ -1336,7 +1359,9 @@ class TerminalMenu:
|
||||||
displayed_index = 0
|
displayed_index = 0
|
||||||
for displayed_index, _, _ in self._view:
|
for displayed_index, _, _ in self._view:
|
||||||
self._tty_out.write("\r" + cursor_width * self._codename_to_terminal_code["cursor_right"])
|
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)
|
self._tty_out.write(checked_multi_select_cursor)
|
||||||
else:
|
else:
|
||||||
self._tty_out.write(unchecked_multi_select_cursor)
|
self._tty_out.write(unchecked_multi_select_cursor)
|
||||||
|
|
@ -1779,6 +1804,12 @@ def get_argumentparser() -> argparse.ArgumentParser:
|
||||||
default=True,
|
default=True,
|
||||||
help="show shortcut hints in the menu title",
|
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(
|
parser.add_argument(
|
||||||
"-b",
|
"-b",
|
||||||
"--status-bar",
|
"--status-bar",
|
||||||
|
|
@ -1837,6 +1868,8 @@ def parse_arguments() -> AttributeDict:
|
||||||
args = AttributeDict({key: value for key, value in vars(parser.parse_args()).items()})
|
args = AttributeDict({key: value for key, value in vars(parser.parse_args()).items()})
|
||||||
if not args.print_version and not args.entries:
|
if not args.print_version and not args.entries:
|
||||||
raise NoMenuEntriesError("No menu entries given!")
|
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 != "":
|
if args.cursor_style != "":
|
||||||
args.cursor_style = tuple(args.cursor_style.split(","))
|
args.cursor_style = tuple(args.cursor_style.split(","))
|
||||||
else:
|
else:
|
||||||
|
|
@ -1933,6 +1966,7 @@ def main() -> None:
|
||||||
show_search_hint_text=args.show_search_hint_text,
|
show_search_hint_text=args.show_search_hint_text,
|
||||||
show_shortcut_hints=args.show_shortcut_hints,
|
show_shortcut_hints=args.show_shortcut_hints,
|
||||||
show_shortcut_hints_in_status_bar=args.show_shortcut_hints_in_status_bar,
|
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=args.status_bar,
|
||||||
status_bar_below_preview=args.status_bar_below_preview,
|
status_bar_below_preview=args.status_bar_below_preview,
|
||||||
status_bar_style=args.status_bar_style,
|
status_bar_style=args.status_bar_style,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue