Send patches - preferably formatted by git format-patch - to patches at archlinux32 dot org.
summaryrefslogtreecommitdiff
path: root/archinstall/lib/menu/table_selection_menu.py
blob: 09cd6ee2bb8c412d1ffe398efe25bd4a6c471dc3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
from typing import Any, Tuple, List, Dict, Optional

from .menu import MenuSelectionType, MenuSelection
from ..output import FormattedOutput
from ..menu import Menu


class TableMenu(Menu):
	def __init__(
		self,
		title: str,
		data: List[Any] = [],
		table_data: Optional[Tuple[List[Any], str]] = None,
		custom_menu_options: List[str] = [],
		default: Any = None,
		multi: bool = False
	):
		"""
		param title: Text that will be displayed above the menu
		:type title: str

		param data: List of objects that will be displayed as rows
		:type data: List

		param table_data: Tuple containing a list of objects and the corresponding
		Table representation of the data as string; this can be used in case the table
		has to be crafted in a more sophisticated manner
		:type table_data: Optional[Tuple[List[Any], str]]

		param custom_options: List of custom options that will be displayed under the table
		:type custom_menu_options: List
		"""
		if not data and not table_data:
			raise ValueError('Either "data" or "table_data" must be provided')

		self._custom_options = custom_menu_options
		self._multi = multi

		if multi:
			header_padding = 7
		else:
			header_padding = 2

		if len(data):
			table_text = FormattedOutput.as_table(data)
			rows = table_text.split('\n')
			table = self._create_table(data, rows, header_padding=header_padding)
		elif table_data is not None:
			# we assume the table to be
			# h1  |   h2
			# -----------
			# r1  |   r2
			data = table_data[0]
			rows = table_data[1].split('\n')
			table = self._create_table(data, rows, header_padding=header_padding)

		self._options, header = self._prepare_selection(table)

		super().__init__(
			title,
			self._options,
			header=header,
			skip_empty_entries=True,
			show_search_hint=False,
			allow_reset=True,
			multi=multi,
			default_option=default
		)

	def run(self) -> MenuSelection:
		choice = super().run()

		match choice.type_:
			case MenuSelectionType.Selection:
				if self._multi:
					choice.value = [self._options[val] for val in choice.value]  # type: ignore
				else:
					choice.value = self._options[choice.value]  # type: ignore

		return choice

	def _create_table(self, data: List[Any], rows: List[str], header_padding: int = 2) -> Dict[str, Any]:
		# these are the header rows of the table and do not map to any data 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
		padding = ' ' * header_padding
		display_data = {f'{padding}{rows[0]}': None, f'{padding}{rows[1]}': None}

		for row, entry in zip(rows[2:], data):
			row = row.replace('|', '\\|')
			display_data[row] = entry

		return display_data

	def _prepare_selection(self, table: Dict[str, Any]) -> Tuple[Dict[str, Any], str]:
		# header rows are mapped to None so make sure to exclude those from the selectable data
		options = {key: val for key, val in table.items() if val is not None}
		header = ''

		if len(options) > 0:
			table_header = [key for key, val in table.items() if val is None]
			header = '\n'.join(table_header)

		custom = {key: None for key in self._custom_options}
		options.update(custom)

		return options, header