Send patches - preferably formatted by git format-patch - to patches at archlinux32 dot org.
summaryrefslogtreecommitdiff
path: root/archinstall/lib/menu
diff options
context:
space:
mode:
authorDaniel Girtler <girtler.daniel@gmail.com>2024-04-15 18:49:00 +1000
committerGitHub <noreply@github.com>2024-04-15 18:49:00 +1000
commitb470b16ec923260cfd9c5b9f2b88e0a39611b463 (patch)
tree25a32fd904f739e181a62a62451637bcf7cd6588 /archinstall/lib/menu
parent7d9e9d8ba0bcba888ec46554f87dfc414c73f9c4 (diff)
LVM support (#2104)
* Submenu for disk configuration * Update * Add LVM manual config * PV selection * LVM volume menu * Update * Fix mypy * Update * Update * Update * Update * Update * Update * Update * Update * Update LVM * Update * Update * Btrfs support * Refactor * LVM on Luks * Luks on LVM * Update * LVM on Luks * Update * Update * mypy * Update * Fix bug with LuksOnLvm and Btrfs * Update * Update * Info -> Debug output
Diffstat (limited to 'archinstall/lib/menu')
-rw-r--r--archinstall/lib/menu/abstract_menu.py93
-rw-r--r--archinstall/lib/menu/list_manager.py28
-rw-r--r--archinstall/lib/menu/menu.py8
-rw-r--r--archinstall/lib/menu/table_selection_menu.py4
4 files changed, 68 insertions, 65 deletions
diff --git a/archinstall/lib/menu/abstract_menu.py b/archinstall/lib/menu/abstract_menu.py
index 14db98ca..ee55f5c9 100644
--- a/archinstall/lib/menu/abstract_menu.py
+++ b/archinstall/lib/menu/abstract_menu.py
@@ -10,6 +10,7 @@ from ..translationhandler import TranslationHandler, Language
if TYPE_CHECKING:
_: Any
+
class Selector:
def __init__(
self,
@@ -68,42 +69,19 @@ class Selector:
:param no_store: A boolean which determines that the field should or shouldn't be stored in the data storage
:type no_store: bool
"""
- self._description = description
- self.func = func
self._display_func = display_func
- self._current_selection = default
+ self._no_store = no_store
+
+ self.description = description
+ self.func = func
+ self.current_selection = default
self.enabled = enabled
- self._dependencies = dependencies
- self._dependencies_not = dependencies_not
+ self.dependencies = dependencies
+ self.dependencies_not = dependencies_not
self.exec_func = exec_func
- self._preview_func = preview_func
+ self.preview_func = preview_func
self.mandatory = mandatory
- self._no_store = no_store
- self._default = default
-
- @property
- def default(self) -> Any:
- return self._default
-
- @property
- def description(self) -> str:
- return self._description
-
- @property
- def dependencies(self) -> List:
- return self._dependencies
-
- @property
- def dependencies_not(self) -> List:
- return self._dependencies_not
-
- @property
- def current_selection(self) -> Optional[Any]:
- return self._current_selection
-
- @property
- def preview_func(self):
- return self._preview_func
+ self.default = default
def do_store(self) -> bool:
return self._no_store is False
@@ -112,45 +90,45 @@ class Selector:
self.enabled = status
def update_description(self, description: str):
- self._description = description
+ self.description = description
def menu_text(self, padding: int = 0) -> str:
- if self._description == '': # special menu option for __separator__
+ if self.description == '': # special menu option for __separator__
return ''
current = ''
if self._display_func:
- current = self._display_func(self._current_selection)
+ current = self._display_func(self.current_selection)
else:
- if self._current_selection is not None:
- current = str(self._current_selection)
+ if self.current_selection is not None:
+ current = str(self.current_selection)
if current:
padding += 5
- description = unicode_ljust(str(self._description), padding, ' ')
+ description = unicode_ljust(str(self.description), padding, ' ')
current = current
else:
- description = self._description
+ description = self.description
current = ''
return f'{description} {current}'
def set_current_selection(self, current: Optional[Any]):
- self._current_selection = current
+ self.current_selection = current
def has_selection(self) -> bool:
- if not self._current_selection:
+ if not self.current_selection:
return False
return True
def get_selection(self) -> Any:
- return self._current_selection
+ return self.current_selection
def is_empty(self) -> bool:
- if self._current_selection is None:
+ if self.current_selection is None:
return True
- elif isinstance(self._current_selection, (str, list, dict)) and len(self._current_selection) == 0:
+ elif isinstance(self.current_selection, (str, list, dict)) and len(self.current_selection) == 0:
return True
return False
@@ -197,6 +175,8 @@ class AbstractMenu:
self._sync_all()
self._populate_default_values()
+ self.defined_text = str(_('Defined'))
+
@property
def last_choice(self):
return self._last_choice
@@ -382,9 +362,10 @@ class AbstractMenu:
result = None
if selector.func is not None:
- presel_val = self.option(config_name).get_selection()
- result = selector.func(presel_val)
+ cur_value = self.option(config_name).get_selection()
+ result = selector.func(cur_value)
self._menu_options[config_name].set_current_selection(result)
+
if selector.do_store():
self._data_store[config_name] = result
@@ -398,19 +379,23 @@ class AbstractMenu:
return True
def _verify_selection_enabled(self, selection_name: str) -> bool:
- """ general """
if selection := self._menu_options.get(selection_name, None):
if not selection.enabled:
return False
if len(selection.dependencies) > 0:
- for d in selection.dependencies:
- if not self._verify_selection_enabled(d) or self._menu_options[d].is_empty():
- return False
+ for dep in selection.dependencies:
+ if isinstance(dep, str):
+ if not self._verify_selection_enabled(dep) or self._menu_options[dep].is_empty():
+ return False
+ elif callable(dep): # callable dependency eval
+ return dep()
+ else:
+ raise ValueError(f'Unsupported dependency: {selection_name}')
if len(selection.dependencies_not) > 0:
- for d in selection.dependencies_not:
- if not self._menu_options[d].is_empty():
+ for dep in selection.dependencies_not:
+ if not self._menu_options[dep].is_empty():
return False
return True
@@ -454,8 +439,8 @@ class AbstractMenu:
class AbstractSubMenu(AbstractMenu):
- def __init__(self, data_store: Dict[str, Any] = {}):
- super().__init__(data_store=data_store)
+ def __init__(self, data_store: Dict[str, Any] = {}, preview_size: float = 0.2):
+ super().__init__(data_store=data_store, preview_size=preview_size)
self._menu_options['__separator__'] = Selector('')
self._menu_options['back'] = \
diff --git a/archinstall/lib/menu/list_manager.py b/archinstall/lib/menu/list_manager.py
index 54fb6a1b..de18791c 100644
--- a/archinstall/lib/menu/list_manager.py
+++ b/archinstall/lib/menu/list_manager.py
@@ -3,6 +3,7 @@ from os import system
from typing import Any, TYPE_CHECKING, Dict, Optional, Tuple, List
from .menu import Menu
+from ..output import FormattedOutput
if TYPE_CHECKING:
_: Any
@@ -127,18 +128,29 @@ class ListManager:
if choice.value and choice.value != self._cancel_action:
self._data = self.handle_action(choice.value, entry, self._data)
- def selected_action_display(self, selection: Any) -> str:
+ def reformat(self, data: List[Any]) -> Dict[str, Optional[Any]]:
"""
- this will return the value to be displayed in the
- "Select an action for '{}'" string
+ Default implementation of the table to be displayed.
+ Override if any custom formatting is needed
"""
- raise NotImplementedError('Please implement me in the child class')
+ table = FormattedOutput.as_table(data)
+ rows = table.split('\n')
- def reformat(self, data: List[Any]) -> Dict[str, Optional[Any]]:
+ # these are the header rows of the table and do not map to any User obviously
+ # we're adding 2 spaces as prefix because the menu selector '> ' will be put before
+ # the selectable rows so the header has to be aligned
+ display_data: Dict[str, Optional[Any]] = {f' {rows[0]}': None, f' {rows[1]}': None}
+
+ for row, entry in zip(rows[2:], data):
+ row = row.replace('|', '\\|')
+ display_data[row] = entry
+
+ return display_data
+
+ def selected_action_display(self, selection: Any) -> str:
"""
- this should return a dictionary of display string to actual data entry
- mapping; if the value for a given display string is None it will be used
- in the header value (useful when displaying tables)
+ this will return the value to be displayed in the
+ "Select an action for '{}'" string
"""
raise NotImplementedError('Please implement me in the child class')
diff --git a/archinstall/lib/menu/menu.py b/archinstall/lib/menu/menu.py
index f14b855d..38301d3a 100644
--- a/archinstall/lib/menu/menu.py
+++ b/archinstall/lib/menu/menu.py
@@ -66,7 +66,7 @@ class Menu(TerminalMenu):
sort: bool = True,
preset_values: Optional[Union[str, List[str]]] = None,
cursor_index: Optional[int] = None,
- preview_command: Optional[Callable] = None,
+ preview_command: Optional[Callable[[Any], str | None]] = None,
preview_size: float = 0.0,
preview_title: str = 'Info',
header: Union[List[str], str] = [],
@@ -228,7 +228,11 @@ class Menu(TerminalMenu):
default_str = str(_('(default)'))
return f'{self._default_option} {default_str}'
- def _show_preview(self, preview_command: Optional[Callable], selection: str) -> Optional[str]:
+ def _show_preview(
+ self,
+ preview_command: Optional[Callable[[Any], str | None]],
+ selection: str
+ ) -> Optional[str]:
if selection == self.back():
return None
diff --git a/archinstall/lib/menu/table_selection_menu.py b/archinstall/lib/menu/table_selection_menu.py
index 4cff7216..fec6ae59 100644
--- a/archinstall/lib/menu/table_selection_menu.py
+++ b/archinstall/lib/menu/table_selection_menu.py
@@ -19,6 +19,7 @@ class TableMenu(Menu):
preview_size: float = 0.0,
allow_reset: bool = True,
allow_reset_warning_msg: Optional[str] = None,
+ skip: bool = True
):
"""
param title: Text that will be displayed above the menu
@@ -81,7 +82,8 @@ class TableMenu(Menu):
preview_title=preview_title,
extra_bottom_space=extra_bottom_space,
allow_reset=allow_reset,
- allow_reset_warning_msg=allow_reset_warning_msg
+ allow_reset_warning_msg=allow_reset_warning_msg,
+ skip=skip
)
def _preset_values(self, preset: List[Any]) -> List[str]: