Fix 3298 - Add package gruops to selection (#3322)
This commit is contained in:
parent
7513ef1cc8
commit
294eea0a1c
|
|
@ -11,7 +11,7 @@ from archinstall.tui.types import Alignment, FrameProperties, Orientation, Previ
|
||||||
|
|
||||||
from ..locale.utils import list_timezones
|
from ..locale.utils import list_timezones
|
||||||
from ..models.audio_configuration import Audio, AudioConfiguration
|
from ..models.audio_configuration import Audio, AudioConfiguration
|
||||||
from ..models.packages import AvailablePackage
|
from ..models.packages import AvailablePackage, PackageGroup
|
||||||
from ..output import warn
|
from ..output import warn
|
||||||
from ..translationhandler import Language
|
from ..translationhandler import Language
|
||||||
|
|
||||||
|
|
@ -174,30 +174,41 @@ def ask_additional_packages_to_install(
|
||||||
|
|
||||||
repositories |= {Repository.Core, Repository.Extra}
|
repositories |= {Repository.Core, Repository.Extra}
|
||||||
packages = list_available_packages(tuple(repositories))
|
packages = list_available_packages(tuple(repositories))
|
||||||
|
package_groups = PackageGroup.from_available_packages(packages)
|
||||||
|
|
||||||
# Additional packages (with some light weight error handling for invalid package names)
|
# Additional packages (with some light weight error handling for invalid package names)
|
||||||
header = str(_('Only packages such as base, base-devel, linux, linux-firmware, efibootmgr and optional profile packages are installed.')) + '\n'
|
header = str(_('Only packages such as base, base-devel, linux, linux-firmware, efibootmgr and optional profile packages are installed.')) + '\n'
|
||||||
header += str(_('Select any packages from the below list that should be installed additionally')) + '\n'
|
header += str(_('Select any packages from the below list that should be installed additionally')) + '\n'
|
||||||
|
|
||||||
# there are over 15k packages so this needs to be quick
|
# there are over 15k packages so this needs to be quick
|
||||||
preset_packages = []
|
preset_packages: list[AvailablePackage | PackageGroup] = []
|
||||||
for p in preset:
|
for p in preset:
|
||||||
if p in packages:
|
if p in packages:
|
||||||
preset_packages.append(packages[p])
|
preset_packages.append(packages[p])
|
||||||
|
elif p in package_groups:
|
||||||
|
preset_packages.append(package_groups[p])
|
||||||
|
|
||||||
items = [
|
items = [
|
||||||
MenuItem(
|
MenuItem(
|
||||||
name,
|
name,
|
||||||
value=pkg,
|
value=pkg,
|
||||||
preview_action=lambda x: x.value.info()
|
preview_action=lambda x: x.value.info()
|
||||||
) for name,
|
) for name, pkg in packages.items()
|
||||||
pkg in packages.items()
|
|
||||||
]
|
]
|
||||||
group = MenuItemGroup(items, sort_items=True)
|
|
||||||
group.set_selected_by_value(preset_packages)
|
items += [
|
||||||
|
MenuItem(
|
||||||
|
name,
|
||||||
|
value=group,
|
||||||
|
preview_action=lambda x: x.value.info()
|
||||||
|
) for name, group in package_groups.items()
|
||||||
|
]
|
||||||
|
|
||||||
|
menu_group = MenuItemGroup(items, sort_items=True)
|
||||||
|
menu_group.set_selected_by_value(preset_packages)
|
||||||
|
|
||||||
result = SelectMenu(
|
result = SelectMenu(
|
||||||
group,
|
menu_group,
|
||||||
header=header,
|
header=header,
|
||||||
alignment=Alignment.LEFT,
|
alignment=Alignment.LEFT,
|
||||||
allow_reset=True,
|
allow_reset=True,
|
||||||
|
|
@ -214,7 +225,7 @@ def ask_additional_packages_to_install(
|
||||||
case ResultType.Reset:
|
case ResultType.Reset:
|
||||||
return []
|
return []
|
||||||
case ResultType.Selection:
|
case ResultType.Selection:
|
||||||
selected_pacakges: list[AvailablePackage] = result.get_values()
|
selected_pacakges: list[AvailablePackage | PackageGroup] = result.get_values()
|
||||||
return [pkg.name for pkg in selected_pacakges]
|
return [pkg.name for pkg in selected_pacakges]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,17 @@
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass, field
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from functools import cached_property
|
from functools import cached_property
|
||||||
from typing import Any, override
|
from typing import TYPE_CHECKING, Any, override
|
||||||
|
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from collections.abc import Callable
|
||||||
|
|
||||||
|
from archinstall.lib.translationhandler import DeferredTranslation
|
||||||
|
|
||||||
|
_: Callable[[str], DeferredTranslation]
|
||||||
|
|
||||||
|
|
||||||
class Repository(Enum):
|
class Repository(Enum):
|
||||||
Core = 'core'
|
Core = 'core'
|
||||||
|
|
@ -166,3 +173,37 @@ class AvailablePackage(BaseModel):
|
||||||
output += f'{key} : {value}\n'
|
output += f'{key} : {value}\n'
|
||||||
|
|
||||||
return output
|
return output
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class PackageGroup:
|
||||||
|
name: str
|
||||||
|
packages: list[str] = field(default_factory=list)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_available_packages(
|
||||||
|
cls,
|
||||||
|
packages: dict[str, AvailablePackage]
|
||||||
|
) -> dict[str, 'PackageGroup']:
|
||||||
|
pkg_groups: dict[str, 'PackageGroup'] = {}
|
||||||
|
|
||||||
|
for pkg in packages.values():
|
||||||
|
if 'None' in pkg.groups:
|
||||||
|
continue
|
||||||
|
|
||||||
|
groups = pkg.groups.split(' ')
|
||||||
|
|
||||||
|
for group in groups:
|
||||||
|
# same group names have multiple spaces in between
|
||||||
|
if len(group) == 0:
|
||||||
|
continue
|
||||||
|
|
||||||
|
pkg_groups.setdefault(group, PackageGroup(group))
|
||||||
|
pkg_groups[group].packages.append(pkg.name)
|
||||||
|
|
||||||
|
return pkg_groups
|
||||||
|
|
||||||
|
def info(self) -> str:
|
||||||
|
output = str(_('Package group:')) + '\n - '
|
||||||
|
output += '\n - '.join(self.packages)
|
||||||
|
return output
|
||||||
|
|
|
||||||
|
|
@ -89,7 +89,7 @@ class MenuItemGroup:
|
||||||
raise ValueError('Selected item not in menu')
|
raise ValueError('Selected item not in menu')
|
||||||
|
|
||||||
self.menu_items: list[MenuItem] = menu_items
|
self.menu_items: list[MenuItem] = menu_items
|
||||||
self.focus_item: MenuItem = focus_item
|
self.focus_item: MenuItem | None = focus_item
|
||||||
self.selected_items: list[MenuItem] = []
|
self.selected_items: list[MenuItem] = []
|
||||||
self.default_item: MenuItem | None = default_item
|
self.default_item: MenuItem | None = default_item
|
||||||
|
|
||||||
|
|
@ -146,7 +146,14 @@ class MenuItemGroup:
|
||||||
|
|
||||||
def index_focus(self) -> int | None:
|
def index_focus(self) -> int | None:
|
||||||
if self.focus_item and self.items:
|
if self.focus_item and self.items:
|
||||||
return self.items.index(self.focus_item)
|
try:
|
||||||
|
return self.items.index(self.focus_item)
|
||||||
|
except ValueError:
|
||||||
|
# on large menus (15k+) when filtering very quickly
|
||||||
|
# the index search is too slow while the items are reduced
|
||||||
|
# by the filter and it will blow up as it cannot find the
|
||||||
|
# focus item
|
||||||
|
pass
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
@ -227,6 +234,8 @@ class MenuItemGroup:
|
||||||
if len(self.items) > 0:
|
if len(self.items) > 0:
|
||||||
if self.focus_item not in self.items:
|
if self.focus_item not in self.items:
|
||||||
self.focus_first()
|
self.focus_first()
|
||||||
|
else:
|
||||||
|
self.focus_item = None
|
||||||
|
|
||||||
def is_item_selected(self, item: MenuItem) -> bool:
|
def is_item_selected(self, item: MenuItem) -> bool:
|
||||||
return item in self.selected_items
|
return item in self.selected_items
|
||||||
|
|
@ -261,22 +270,25 @@ class MenuItemGroup:
|
||||||
self.focus_item = last_item
|
self.focus_item = last_item
|
||||||
|
|
||||||
def focus_prev(self, skip_empty: bool = True) -> None:
|
def focus_prev(self, skip_empty: bool = True) -> None:
|
||||||
assert self.focus_item is not None
|
# e.g. when filter shows no items
|
||||||
|
if self.focus_item is None:
|
||||||
|
return
|
||||||
|
|
||||||
item = self._find_next_selectable_item(self.items, self.focus_item, -1)
|
item = self._find_next_selectable_item(self.items, self.focus_item, -1)
|
||||||
|
|
||||||
if item is not None:
|
if item is not None:
|
||||||
self.focus_item = item
|
self.focus_item = item
|
||||||
|
|
||||||
def focus_next(self, skip_not_enabled: bool = True) -> None:
|
def focus_next(self, skip_not_enabled: bool = True) -> None:
|
||||||
assert self.focus_item is not None
|
# e.g. when filter shows no items
|
||||||
|
if self.focus_item is None:
|
||||||
|
return
|
||||||
|
|
||||||
item = self._find_next_selectable_item(self.items, self.focus_item, 1)
|
item = self._find_next_selectable_item(self.items, self.focus_item, 1)
|
||||||
|
|
||||||
if item is not None:
|
if item is not None:
|
||||||
self.focus_item = item
|
self.focus_item = item
|
||||||
|
|
||||||
def get_focus_index(self) -> int:
|
|
||||||
return self.items.index(self.focus_item)
|
|
||||||
|
|
||||||
def _find_next_selectable_item(
|
def _find_next_selectable_item(
|
||||||
self,
|
self,
|
||||||
items: list[MenuItem],
|
items: list[MenuItem],
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue