Skip to content

Commit 294eea0

Browse files
authored
Fix 3298 - Add package gruops to selection (#3322)
1 parent 7513ef1 commit 294eea0

3 files changed

Lines changed: 81 additions & 17 deletions

File tree

archinstall/lib/interactions/general_conf.py

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
from ..locale.utils import list_timezones
1313
from ..models.audio_configuration import Audio, AudioConfiguration
14-
from ..models.packages import AvailablePackage
14+
from ..models.packages import AvailablePackage, PackageGroup
1515
from ..output import warn
1616
from ..translationhandler import Language
1717

@@ -174,30 +174,41 @@ def ask_additional_packages_to_install(
174174

175175
repositories |= {Repository.Core, Repository.Extra}
176176
packages = list_available_packages(tuple(repositories))
177+
package_groups = PackageGroup.from_available_packages(packages)
177178

178179
# Additional packages (with some light weight error handling for invalid package names)
179180
header = str(_('Only packages such as base, base-devel, linux, linux-firmware, efibootmgr and optional profile packages are installed.')) + '\n'
180181
header += str(_('Select any packages from the below list that should be installed additionally')) + '\n'
181182

182183
# there are over 15k packages so this needs to be quick
183-
preset_packages = []
184+
preset_packages: list[AvailablePackage | PackageGroup] = []
184185
for p in preset:
185186
if p in packages:
186187
preset_packages.append(packages[p])
188+
elif p in package_groups:
189+
preset_packages.append(package_groups[p])
187190

188191
items = [
189192
MenuItem(
190193
name,
191194
value=pkg,
192195
preview_action=lambda x: x.value.info()
193-
) for name,
194-
pkg in packages.items()
196+
) for name, pkg in packages.items()
197+
]
198+
199+
items += [
200+
MenuItem(
201+
name,
202+
value=group,
203+
preview_action=lambda x: x.value.info()
204+
) for name, group in package_groups.items()
195205
]
196-
group = MenuItemGroup(items, sort_items=True)
197-
group.set_selected_by_value(preset_packages)
206+
207+
menu_group = MenuItemGroup(items, sort_items=True)
208+
menu_group.set_selected_by_value(preset_packages)
198209

199210
result = SelectMenu(
200-
group,
211+
menu_group,
201212
header=header,
202213
alignment=Alignment.LEFT,
203214
allow_reset=True,
@@ -214,7 +225,7 @@ def ask_additional_packages_to_install(
214225
case ResultType.Reset:
215226
return []
216227
case ResultType.Selection:
217-
selected_pacakges: list[AvailablePackage] = result.get_values()
228+
selected_pacakges: list[AvailablePackage | PackageGroup] = result.get_values()
218229
return [pkg.name for pkg in selected_pacakges]
219230

220231

archinstall/lib/models/packages.py

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
1-
from dataclasses import dataclass
1+
from dataclasses import dataclass, field
22
from enum import Enum
33
from functools import cached_property
4-
from typing import Any, override
4+
from typing import TYPE_CHECKING, Any, override
55

66
from pydantic import BaseModel
77

8+
if TYPE_CHECKING:
9+
from collections.abc import Callable
10+
11+
from archinstall.lib.translationhandler import DeferredTranslation
12+
13+
_: Callable[[str], DeferredTranslation]
14+
815

916
class Repository(Enum):
1017
Core = 'core'
@@ -166,3 +173,37 @@ def info(self) -> str:
166173
output += f'{key} : {value}\n'
167174

168175
return output
176+
177+
178+
@dataclass
179+
class PackageGroup:
180+
name: str
181+
packages: list[str] = field(default_factory=list)
182+
183+
@classmethod
184+
def from_available_packages(
185+
cls,
186+
packages: dict[str, AvailablePackage]
187+
) -> dict[str, 'PackageGroup']:
188+
pkg_groups: dict[str, 'PackageGroup'] = {}
189+
190+
for pkg in packages.values():
191+
if 'None' in pkg.groups:
192+
continue
193+
194+
groups = pkg.groups.split(' ')
195+
196+
for group in groups:
197+
# same group names have multiple spaces in between
198+
if len(group) == 0:
199+
continue
200+
201+
pkg_groups.setdefault(group, PackageGroup(group))
202+
pkg_groups[group].packages.append(pkg.name)
203+
204+
return pkg_groups
205+
206+
def info(self) -> str:
207+
output = str(_('Package group:')) + '\n - '
208+
output += '\n - '.join(self.packages)
209+
return output

archinstall/tui/menu_item.py

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ def __init__(
8989
raise ValueError('Selected item not in menu')
9090

9191
self.menu_items: list[MenuItem] = menu_items
92-
self.focus_item: MenuItem = focus_item
92+
self.focus_item: MenuItem | None = focus_item
9393
self.selected_items: list[MenuItem] = []
9494
self.default_item: MenuItem | None = default_item
9595

@@ -146,7 +146,14 @@ def set_selected_by_value(self, values: Any | list[Any] | None) -> None:
146146

147147
def index_focus(self) -> int | None:
148148
if self.focus_item and self.items:
149-
return self.items.index(self.focus_item)
149+
try:
150+
return self.items.index(self.focus_item)
151+
except ValueError:
152+
# on large menus (15k+) when filtering very quickly
153+
# the index search is too slow while the items are reduced
154+
# by the filter and it will blow up as it cannot find the
155+
# focus item
156+
pass
150157

151158
return None
152159

@@ -227,6 +234,8 @@ def _reload_focus_item(self) -> None:
227234
if len(self.items) > 0:
228235
if self.focus_item not in self.items:
229236
self.focus_first()
237+
else:
238+
self.focus_item = None
230239

231240
def is_item_selected(self, item: MenuItem) -> bool:
232241
return item in self.selected_items
@@ -261,22 +270,25 @@ def focus_last(self) -> None:
261270
self.focus_item = last_item
262271

263272
def focus_prev(self, skip_empty: bool = True) -> None:
264-
assert self.focus_item is not None
273+
# e.g. when filter shows no items
274+
if self.focus_item is None:
275+
return
276+
265277
item = self._find_next_selectable_item(self.items, self.focus_item, -1)
266278

267279
if item is not None:
268280
self.focus_item = item
269281

270282
def focus_next(self, skip_not_enabled: bool = True) -> None:
271-
assert self.focus_item is not None
283+
# e.g. when filter shows no items
284+
if self.focus_item is None:
285+
return
286+
272287
item = self._find_next_selectable_item(self.items, self.focus_item, 1)
273288

274289
if item is not None:
275290
self.focus_item = item
276291

277-
def get_focus_index(self) -> int:
278-
return self.items.index(self.focus_item)
279-
280292
def _find_next_selectable_item(
281293
self,
282294
items: list[MenuItem],

0 commit comments

Comments
 (0)