Send patches - preferably formatted by git format-patch - to patches at archlinux32 dot org.
summaryrefslogtreecommitdiff
path: root/archinstall/lib/user_interaction
diff options
context:
space:
mode:
authorDaniel Girtler <blackrabbit256@gmail.com>2022-06-09 22:54:12 +1000
committerGitHub <noreply@github.com>2022-06-09 14:54:12 +0200
commit0bdb46f3081aaed095f87e35b18b3714d8782ed1 (patch)
tree894ef3050fdfae1b455fea5ad06aac980c9e26a5 /archinstall/lib/user_interaction
parentaec86eb04e96f4c178cf89f7e72a257d12019fdc (diff)
Fancy user interface (#1320)
* Display submenus as tables * Update * Update * Update * Update Co-authored-by: Daniel Girtler <girtler.daniel@gmail.com>
Diffstat (limited to 'archinstall/lib/user_interaction')
-rw-r--r--archinstall/lib/user_interaction/manage_users_conf.py42
-rw-r--r--archinstall/lib/user_interaction/network_conf.py96
-rw-r--r--archinstall/lib/user_interaction/subvolume_config.py24
3 files changed, 87 insertions, 75 deletions
diff --git a/archinstall/lib/user_interaction/manage_users_conf.py b/archinstall/lib/user_interaction/manage_users_conf.py
index 567a2964..33c31342 100644
--- a/archinstall/lib/user_interaction/manage_users_conf.py
+++ b/archinstall/lib/user_interaction/manage_users_conf.py
@@ -7,6 +7,7 @@ from .utils import get_password
from ..menu import Menu
from ..menu.list_manager import ListManager
from ..models.users import User
+from ..output import FormattedOutput
if TYPE_CHECKING:
_: Any
@@ -18,13 +19,6 @@ class UserList(ListManager):
"""
def __init__(self, prompt: str, lusers: List[User]):
- """
- param: prompt
- type: str
- param: lusers dict with the users already defined for the system
- type: Dict
- param: sudo. boolean to determine if we handle superusers or users. If None handles both types
- """
self._actions = [
str(_('Add a user')),
str(_('Change password')),
@@ -34,21 +28,25 @@ class UserList(ListManager):
super().__init__(prompt, lusers, self._actions, self._actions[0])
def reformat(self, data: List[User]) -> Dict[str, User]:
- return {e.display(): e for e in data}
+ table = FormattedOutput.as_table(data)
+ rows = table.split('\n')
- def action_list(self):
- active_user = self.target if self.target else None
+ # 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 = {f' {rows[0]}': None, f' {rows[1]}': None}
+
+ for row, user in zip(rows[2:], data):
+ row = row.replace('|', '\\|')
+ display_data[row] = user
- if active_user is None:
- return [self._actions[0]]
- else:
- return self._actions[1:]
+ return display_data
+
+ def selected_action_display(self, user: User) -> str:
+ return user.username
def exec_action(self, data: List[User]) -> List[User]:
- if self.target:
- active_user = self.target
- else:
- active_user = None
+ active_user = self.target if self.target else None
if self.action == self._actions[0]: # add
new_user = self._add_user()
@@ -77,8 +75,7 @@ class UserList(ListManager):
return False
def _add_user(self) -> Optional[User]:
- print(_('\nDefine a new user\n'))
- prompt = str(_('Enter username (leave blank to skip): '))
+ prompt = '\n\n' + str(_('Enter username (leave blank to skip): '))
while True:
username = input(prompt).strip(' ')
@@ -94,7 +91,9 @@ class UserList(ListManager):
choice = Menu(
str(_('Should "{}" be a superuser (sudo)?')).format(username), Menu.yes_no(),
skip=False,
- default_option=Menu.no()
+ default_option=Menu.no(),
+ clear_screen=False,
+ show_search_hint=False
).run()
sudo = True if choice.value == Menu.yes() else False
@@ -102,6 +101,5 @@ class UserList(ListManager):
def ask_for_additional_users(prompt: str = '', defined_users: List[User] = []) -> List[User]:
- prompt = prompt if prompt else _('Enter username (leave blank to skip): ')
users = UserList(prompt, defined_users).run()
return users
diff --git a/archinstall/lib/user_interaction/network_conf.py b/archinstall/lib/user_interaction/network_conf.py
index 5154d8b1..4f22b790 100644
--- a/archinstall/lib/user_interaction/network_conf.py
+++ b/archinstall/lib/user_interaction/network_conf.py
@@ -2,7 +2,7 @@ from __future__ import annotations
import ipaddress
import logging
-from typing import Any, Optional, TYPE_CHECKING, List, Union
+from typing import Any, Optional, TYPE_CHECKING, List, Union, Dict
from ..menu.menu import MenuSelectionType
from ..menu.text_input import TextInput
@@ -10,7 +10,7 @@ from ..models.network_configuration import NetworkConfiguration, NicType
from ..networking import list_interfaces
from ..menu import Menu
-from ..output import log
+from ..output import log, FormattedOutput
from ..menu.list_manager import ListManager
if TYPE_CHECKING:
@@ -19,55 +19,57 @@ if TYPE_CHECKING:
class ManualNetworkConfig(ListManager):
"""
- subclass of ListManager for the managing of network configuration accounts
+ subclass of ListManager for the managing of network configurations
"""
- def __init__(self, prompt: str, ifaces: Union[None, NetworkConfiguration, List[NetworkConfiguration]]):
- """
- param: prompt
- type: str
- param: ifaces already defined previously
- type: Dict
- """
+ def __init__(self, prompt: str, ifaces: List[NetworkConfiguration]):
+ self._actions = [
+ str(_('Add interface')),
+ str(_('Edit interface')),
+ str(_('Delete interface'))
+ ]
- if ifaces is not None and isinstance(ifaces, list):
- display_values = {iface.iface: iface for iface in ifaces}
- else:
- display_values = {}
+ super().__init__(prompt, ifaces, self._actions, self._actions[0])
+
+ def reformat(self, data: List[NetworkConfiguration]) -> Dict[str, Optional[NetworkConfiguration]]:
+ table = FormattedOutput.as_table(data)
+ rows = table.split('\n')
- self._action_add = str(_('Add interface'))
- self._action_edit = str(_('Edit interface'))
- self._action_delete = str(_('Delete interface'))
+ # 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[NetworkConfiguration]] = {f' {rows[0]}': None, f' {rows[1]}': None}
- self._iface_actions = [self._action_edit, self._action_delete]
+ for row, iface in zip(rows[2:], data):
+ row = row.replace('|', '\\|')
+ display_data[row] = iface
- super().__init__(prompt, display_values, self._iface_actions, self._action_add)
+ return display_data
- def run_manual(self) -> List[NetworkConfiguration]:
- ifaces = super().run()
- if ifaces is not None:
- return list(ifaces.values())
- return []
+ def selected_action_display(self, iface: NetworkConfiguration) -> str:
+ return iface.iface if iface.iface else ''
- def exec_action(self, data: Any):
- if self.action == self._action_add:
- iface_name = self._select_iface(data.keys())
+ def exec_action(self, data: List[NetworkConfiguration]):
+ active_iface: Optional[NetworkConfiguration] = self.target if self.target else None
+
+ if self.action == self._actions[0]: # add
+ iface_name = self._select_iface(data)
if iface_name:
iface = NetworkConfiguration(NicType.MANUAL, iface=iface_name)
- data[iface_name] = self._edit_iface(iface)
- elif self.target:
- iface_name = list(self.target.keys())[0]
- iface = data[iface_name]
-
- if self.action == self._action_edit:
- data[iface_name] = self._edit_iface(iface)
- elif self.action == self._action_delete:
- del data[iface_name]
+ iface = self._edit_iface(iface)
+ data += [iface]
+ elif active_iface:
+ if self.action == self._actions[1]: # edit interface
+ data = [d for d in data if d.iface != active_iface.iface]
+ data.append(self._edit_iface(active_iface))
+ elif self.action == self._actions[2]: # delete
+ data = [d for d in data if d != active_iface]
return data
- def _select_iface(self, existing_ifaces: List[str]) -> Optional[Any]:
+ def _select_iface(self, data: List[NetworkConfiguration]) -> Optional[Any]:
all_ifaces = list_interfaces().values()
+ existing_ifaces = [d.iface for d in data]
available = set(all_ifaces) - set(existing_ifaces)
choice = Menu(str(_('Select interface to add')), list(available), skip=True).run()
@@ -76,7 +78,7 @@ class ManualNetworkConfig(ListManager):
return choice.value
- def _edit_iface(self, edit_iface :NetworkConfiguration):
+ def _edit_iface(self, edit_iface: NetworkConfiguration):
iface_name = edit_iface.iface
modes = ['DHCP (auto detect)', 'IP (static)']
default_mode = 'DHCP (auto detect)'
@@ -99,11 +101,13 @@ class ManualNetworkConfig(ListManager):
gateway = None
while 1:
- gateway_input = TextInput(_('Enter your gateway (router) IP address or leave blank for none: '),
- edit_iface.gateway).run().strip()
+ gateway = TextInput(
+ _('Enter your gateway (router) IP address or leave blank for none: '),
+ edit_iface.gateway
+ ).run().strip()
try:
- if len(gateway_input) > 0:
- ipaddress.ip_address(gateway_input)
+ if len(gateway) > 0:
+ ipaddress.ip_address(gateway)
break
except ValueError:
log("You need to enter a valid gateway (router) IP address.", level=logging.WARNING, fg='red')
@@ -124,7 +128,9 @@ class ManualNetworkConfig(ListManager):
return NetworkConfiguration(NicType.MANUAL, iface=iface_name)
-def ask_to_configure_network(preset: Union[None, NetworkConfiguration, List[NetworkConfiguration]]) -> Optional[Union[List[NetworkConfiguration], NetworkConfiguration]]:
+def ask_to_configure_network(
+ preset: Union[NetworkConfiguration, List[NetworkConfiguration]]
+) -> Optional[NetworkConfiguration | List[NetworkConfiguration]]:
"""
Configure the network on the newly installed system
"""
@@ -165,7 +171,7 @@ def ask_to_configure_network(preset: Union[None, NetworkConfiguration, List[Netw
elif choice.value == network_options['network_manager']:
return NetworkConfiguration(NicType.NM)
elif choice.value == network_options['manual']:
- manual = ManualNetworkConfig('Configure interfaces', preset)
- return manual.run_manual()
+ preset_ifaces = preset if isinstance(preset, list) else []
+ return ManualNetworkConfig('Configure interfaces', preset_ifaces).run()
return preset
diff --git a/archinstall/lib/user_interaction/subvolume_config.py b/archinstall/lib/user_interaction/subvolume_config.py
index a54ec891..e2797bba 100644
--- a/archinstall/lib/user_interaction/subvolume_config.py
+++ b/archinstall/lib/user_interaction/subvolume_config.py
@@ -5,6 +5,7 @@ from ..menu.menu import MenuSelectionType
from ..menu.text_input import TextInput
from ..menu import Menu
from ..models.subvolume import Subvolume
+from ... import FormattedOutput
if TYPE_CHECKING:
_: Any
@@ -19,16 +20,23 @@ class SubvolumeList(ListManager):
]
super().__init__(prompt, current_volumes, self._actions, self._actions[0])
- def reformat(self, data: List[Subvolume]) -> Dict[str, Subvolume]:
- return {e.display(): e for e in data}
+ def reformat(self, data: List[Subvolume]) -> Dict[str, Optional[Subvolume]]:
+ table = FormattedOutput.as_table(data)
+ rows = table.split('\n')
- def action_list(self):
- active_user = self.target if self.target else None
+ # 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[Subvolume]] = {f' {rows[0]}': None, f' {rows[1]}': None}
- if active_user is None:
- return [self._actions[0]]
- else:
- return self._actions[1:]
+ for row, subvol in zip(rows[2:], data):
+ row = row.replace('|', '\\|')
+ display_data[row] = subvol
+
+ return display_data
+
+ def selected_action_display(self, subvolume: Subvolume) -> str:
+ return subvolume.name
def _prompt_options(self, editing: Optional[Subvolume] = None) -> List[str]:
preset_options = []