Pull the UI module one level up

This commit is contained in:
Daniel Girtler 2026-05-03 10:38:59 +10:00
parent dd34954011
commit ed1d885351
40 changed files with 68 additions and 1820 deletions

View File

@ -4,8 +4,8 @@ from archinstall.default_profiles.profile import DisplayServerType, GreeterType,
from archinstall.lib.menu.helpers import Selection
from archinstall.lib.output import info
from archinstall.lib.profile.profiles_handler import profile_handler
from archinstall.tui.ui.menu_item import MenuItem, MenuItemGroup
from archinstall.tui.ui.result import ResultType
from archinstall.tui.menu_item import MenuItem, MenuItemGroup
from archinstall.tui.result import ResultType
if TYPE_CHECKING:
from archinstall.lib.installer import Installer

View File

@ -4,8 +4,8 @@ from archinstall.default_profiles.desktops import SeatAccess
from archinstall.default_profiles.profile import CustomSetting, DisplayServerType, GreeterType, Profile, ProfileType
from archinstall.lib.menu.helpers import Selection
from archinstall.lib.translationhandler import tr
from archinstall.tui.ui.menu_item import MenuItem, MenuItemGroup
from archinstall.tui.ui.result import ResultType
from archinstall.tui.menu_item import MenuItem, MenuItemGroup
from archinstall.tui.result import ResultType
class HyprlandProfile(Profile):

View File

@ -4,8 +4,8 @@ from archinstall.default_profiles.desktops import SeatAccess
from archinstall.default_profiles.profile import CustomSetting, DisplayServerType, GreeterType, Profile, ProfileType
from archinstall.lib.menu.helpers import Selection
from archinstall.lib.translationhandler import tr
from archinstall.tui.ui.menu_item import MenuItem, MenuItemGroup
from archinstall.tui.ui.result import ResultType
from archinstall.tui.menu_item import MenuItem, MenuItemGroup
from archinstall.tui.result import ResultType
class LabwcProfile(Profile):

View File

@ -4,8 +4,8 @@ from archinstall.default_profiles.desktops import SeatAccess
from archinstall.default_profiles.profile import CustomSetting, DisplayServerType, GreeterType, Profile, ProfileType
from archinstall.lib.menu.helpers import Selection
from archinstall.lib.translationhandler import tr
from archinstall.tui.ui.menu_item import MenuItem, MenuItemGroup
from archinstall.tui.ui.result import ResultType
from archinstall.tui.menu_item import MenuItem, MenuItemGroup
from archinstall.tui.result import ResultType
class NiriProfile(Profile):

View File

@ -5,8 +5,8 @@ from archinstall.default_profiles.profile import CustomSetting, DisplayServerTyp
from archinstall.lib.menu.helpers import Selection
from archinstall.lib.packages.packages import available_package, package_group_info
from archinstall.lib.translationhandler import tr
from archinstall.tui.ui.menu_item import MenuItem, MenuItemGroup
from archinstall.tui.ui.result import ResultType
from archinstall.tui.menu_item import MenuItem, MenuItemGroup
from archinstall.tui.result import ResultType
class PlasmaFlavor(StrEnum):

View File

@ -4,8 +4,8 @@ from archinstall.default_profiles.desktops import SeatAccess
from archinstall.default_profiles.profile import CustomSetting, DisplayServerType, GreeterType, Profile, ProfileType
from archinstall.lib.menu.helpers import Selection
from archinstall.lib.translationhandler import tr
from archinstall.tui.ui.menu_item import MenuItem, MenuItemGroup
from archinstall.tui.ui.result import ResultType
from archinstall.tui.menu_item import MenuItem, MenuItemGroup
from archinstall.tui.result import ResultType
class SwayProfile(Profile):

View File

@ -4,8 +4,8 @@ from archinstall.default_profiles.profile import Profile, ProfileType, SelectRes
from archinstall.lib.menu.helpers import Selection
from archinstall.lib.output import info
from archinstall.lib.profile.profiles_handler import profile_handler
from archinstall.tui.ui.menu_item import MenuItem, MenuItemGroup
from archinstall.tui.ui.result import ResultType
from archinstall.tui.menu_item import MenuItem, MenuItemGroup
from archinstall.tui.result import ResultType
if TYPE_CHECKING:
from archinstall.lib.installer import Installer

View File

@ -17,8 +17,8 @@ from archinstall.lib.models.application import (
PrintServiceConfiguration,
)
from archinstall.lib.translationhandler import tr
from archinstall.tui.ui.menu_item import MenuItem, MenuItemGroup
from archinstall.tui.ui.result import ResultType
from archinstall.tui.menu_item import MenuItem, MenuItemGroup
from archinstall.tui.result import ResultType
class ApplicationMenu(AbstractSubMenu[ApplicationConfiguration]):

View File

@ -30,7 +30,7 @@ from archinstall.lib.output import debug, error, logger, warn
from archinstall.lib.plugins import load_plugin
from archinstall.lib.translationhandler import Language, tr, translation_handler
from archinstall.lib.version import get_version
from archinstall.tui.ui.components import tui
from archinstall.tui.components import tui
@p_dataclass

View File

@ -9,8 +9,8 @@ from archinstall.lib.models.users import Password, User
from archinstall.lib.output import FormattedOutput
from archinstall.lib.translationhandler import tr
from archinstall.lib.user.user_menu import select_users
from archinstall.tui.ui.menu_item import MenuItem, MenuItemGroup
from archinstall.tui.ui.result import ResultType
from archinstall.tui.menu_item import MenuItem, MenuItemGroup
from archinstall.tui.result import ResultType
class AuthenticationMenu(AbstractSubMenu[AuthenticationConfiguration]):

View File

@ -5,8 +5,8 @@ from archinstall.lib.menu.abstract_menu import AbstractSubMenu
from archinstall.lib.menu.helpers import Confirmation, Selection
from archinstall.lib.models.bootloader import Bootloader, BootloaderConfiguration
from archinstall.lib.translationhandler import tr
from archinstall.tui.ui.menu_item import MenuItem, MenuItemGroup
from archinstall.tui.ui.result import ResultType
from archinstall.tui.menu_item import MenuItem, MenuItemGroup
from archinstall.tui.result import ResultType
class BootloaderMenu(AbstractSubMenu[BootloaderConfiguration]):

View File

@ -14,8 +14,8 @@ from archinstall.lib.models.bootloader import Bootloader
from archinstall.lib.models.network import NetworkConfiguration
from archinstall.lib.output import debug, logger, warn
from archinstall.lib.translationhandler import tr
from archinstall.tui.ui.menu_item import MenuItem, MenuItemGroup
from archinstall.tui.ui.result import ResultType
from archinstall.tui.menu_item import MenuItem, MenuItemGroup
from archinstall.tui.result import ResultType
class ConfigurationOutput:

View File

@ -37,8 +37,8 @@ from archinstall.lib.models.device import (
)
from archinstall.lib.output import FormattedOutput, debug
from archinstall.lib.translationhandler import tr
from archinstall.tui.ui.menu_item import MenuItem, MenuItemGroup
from archinstall.tui.ui.result import ResultType
from archinstall.tui.menu_item import MenuItem, MenuItemGroup
from archinstall.tui.result import ResultType
@dataclass

View File

@ -19,8 +19,8 @@ from archinstall.lib.models.device import (
from archinstall.lib.models.users import Password
from archinstall.lib.output import FormattedOutput
from archinstall.lib.translationhandler import tr
from archinstall.tui.ui.menu_item import MenuItem, MenuItemGroup
from archinstall.tui.ui.result import ResultType
from archinstall.tui.menu_item import MenuItem, MenuItemGroup
from archinstall.tui.result import ResultType
class DiskEncryptionMenu(AbstractSubMenu[DiskEncryption]):

View File

@ -21,8 +21,8 @@ from archinstall.lib.models.device import (
)
from archinstall.lib.output import FormattedOutput
from archinstall.lib.translationhandler import tr
from archinstall.tui.ui.menu_item import MenuItem, MenuItemGroup
from archinstall.tui.ui.result import ResultType
from archinstall.tui.menu_item import MenuItem, MenuItemGroup
from archinstall.tui.result import ResultType
class FreeSpace:

View File

@ -6,7 +6,7 @@ from archinstall.lib.menu.list_manager import ListManager
from archinstall.lib.menu.util import prompt_dir
from archinstall.lib.models.device import SubvolumeModification
from archinstall.lib.translationhandler import tr
from archinstall.tui.ui.result import ResultType
from archinstall.tui.result import ResultType
class SubvolumeMenu(ListManager[SubvolumeModification]):

View File

@ -4,8 +4,8 @@ from archinstall.lib.locale.utils import list_timezones
from archinstall.lib.menu.helpers import Confirmation, Input, Selection
from archinstall.lib.output import warn
from archinstall.lib.translationhandler import Language, tr
from archinstall.tui.ui.menu_item import MenuItem, MenuItemGroup
from archinstall.tui.ui.result import ResultType
from archinstall.tui.menu_item import MenuItem, MenuItemGroup
from archinstall.tui.result import ResultType
class PostInstallationAction(Enum):

View File

@ -5,8 +5,8 @@ from archinstall.lib.menu.helpers import Confirmation, Selection
from archinstall.lib.models.application import ZramAlgorithm, ZramConfiguration
from archinstall.lib.models.package_types import DEFAULT_KERNEL, Kernel
from archinstall.lib.translationhandler import tr
from archinstall.tui.ui.menu_item import MenuItem, MenuItemGroup
from archinstall.tui.ui.result import ResultType
from archinstall.tui.menu_item import MenuItem, MenuItemGroup
from archinstall.tui.result import ResultType
async def select_kernel(preset: list[Kernel] = []) -> list[Kernel]:

View File

@ -32,8 +32,8 @@ from archinstall.lib.packages.packages import list_available_packages, select_ad
from archinstall.lib.pacman.config import PacmanConfig
from archinstall.lib.pacman.pacman_menu import PacmanMenu
from archinstall.lib.translationhandler import Language, tr, translation_handler
from archinstall.tui.ui.components import tui
from archinstall.tui.ui.menu_item import MenuItem, MenuItemGroup
from archinstall.tui.components import tui
from archinstall.tui.menu_item import MenuItem, MenuItemGroup
class GlobalMenu(AbstractMenu[None]):

View File

@ -5,8 +5,8 @@ from archinstall.lib.menu.abstract_menu import AbstractSubMenu
from archinstall.lib.menu.helpers import Selection
from archinstall.lib.models.locale import LocaleConfiguration
from archinstall.lib.translationhandler import tr
from archinstall.tui.ui.menu_item import MenuItem, MenuItemGroup
from archinstall.tui.ui.result import ResultType
from archinstall.tui.menu_item import MenuItem, MenuItemGroup
from archinstall.tui.result import ResultType
class LocaleMenu(AbstractSubMenu[LocaleConfiguration]):

View File

@ -5,9 +5,9 @@ from typing import Any, Self, override
from archinstall.lib.menu.helpers import Selection
from archinstall.lib.output import error
from archinstall.lib.translationhandler import tr
from archinstall.tui.ui.components import InstanceRunnable
from archinstall.tui.ui.menu_item import MenuItem, MenuItemGroup
from archinstall.tui.ui.result import ResultType
from archinstall.tui.components import InstanceRunnable
from archinstall.tui.menu_item import MenuItem, MenuItemGroup
from archinstall.tui.result import ResultType
CONFIG_KEY = '__config__'

View File

@ -4,9 +4,9 @@ from typing import Any, Literal, override
from textual.validation import ValidationResult, Validator
from archinstall.lib.translationhandler import tr
from archinstall.tui.ui.components import InputInfo, InputScreen, LoadingScreen, NotifyScreen, OptionListScreen, SelectListScreen, TableSelectionScreen
from archinstall.tui.ui.menu_item import MenuItemGroup
from archinstall.tui.ui.result import Result, ResultType
from archinstall.tui.components import InputInfo, InputScreen, LoadingScreen, NotifyScreen, OptionListScreen, SelectListScreen, TableSelectionScreen
from archinstall.tui.menu_item import MenuItemGroup
from archinstall.tui.result import Result, ResultType
class Selection[ValueT]:

View File

@ -4,8 +4,8 @@ from typing import cast
from archinstall.lib.menu.helpers import Selection
from archinstall.lib.menu.menu_helper import MenuHelper
from archinstall.lib.translationhandler import tr
from archinstall.tui.ui.menu_item import MenuItem, MenuItemGroup
from archinstall.tui.ui.result import ResultType
from archinstall.tui.menu_item import MenuItem, MenuItemGroup
from archinstall.tui.result import ResultType
class ListManager[ValueT]:

View File

@ -1,5 +1,5 @@
from archinstall.lib.output import FormattedOutput
from archinstall.tui.ui.menu_item import MenuItem, MenuItemGroup
from archinstall.tui.menu_item import MenuItem, MenuItemGroup
class MenuHelper[ValueT]:

View File

@ -5,8 +5,8 @@ from pathlib import Path
from archinstall.lib.menu.helpers import Confirmation, Input
from archinstall.lib.models.users import Password, PasswordStrength
from archinstall.lib.translationhandler import tr
from archinstall.tui.ui.components import InputInfo, InputInfoType, tui
from archinstall.tui.ui.result import ResultType
from archinstall.tui.components import InputInfo, InputInfoType, tui
from archinstall.tui.result import ResultType
async def get_password(

View File

@ -15,8 +15,8 @@ from archinstall.lib.models.mirrors import (
from archinstall.lib.models.packages import Repository
from archinstall.lib.output import FormattedOutput
from archinstall.lib.translationhandler import tr
from archinstall.tui.ui.menu_item import MenuItem, MenuItemGroup
from archinstall.tui.ui.result import ResultType
from archinstall.tui.menu_item import MenuItem, MenuItemGroup
from archinstall.tui.result import ResultType
class CustomMirrorRepositoriesList(ListManager[CustomRepository]):

View File

@ -6,8 +6,8 @@ from archinstall.lib.menu.list_manager import ListManager
from archinstall.lib.models.network import NetworkConfiguration, Nic, NicType
from archinstall.lib.networking import list_interfaces
from archinstall.lib.translationhandler import tr
from archinstall.tui.ui.menu_item import MenuItem, MenuItemGroup
from archinstall.tui.ui.result import ResultType
from archinstall.tui.menu_item import MenuItem, MenuItemGroup
from archinstall.tui.result import ResultType
class ManualNetworkConfig(ListManager[Nic]):

View File

@ -9,9 +9,9 @@ from archinstall.lib.models.network import WifiConfiguredNetwork, WifiNetwork
from archinstall.lib.network.wpa_supplicant import WpaSupplicantConfig
from archinstall.lib.output import debug
from archinstall.lib.translationhandler import tr
from archinstall.tui.ui.components import ConfirmationScreen, InputScreen, InstanceRunnable, LoadingScreen, NotifyScreen, TableSelectionScreen, tui
from archinstall.tui.ui.menu_item import MenuItem, MenuItemGroup
from archinstall.tui.ui.result import Result, ResultType
from archinstall.tui.components import ConfirmationScreen, InputScreen, InstanceRunnable, LoadingScreen, NotifyScreen, TableSelectionScreen, tui
from archinstall.tui.menu_item import MenuItem, MenuItemGroup
from archinstall.tui.result import Result, ResultType
@dataclass

View File

@ -6,8 +6,8 @@ from archinstall.lib.models.packages import AvailablePackage, LocalPackage, Pack
from archinstall.lib.output import debug
from archinstall.lib.pacman.pacman import Pacman
from archinstall.lib.translationhandler import tr
from archinstall.tui.ui.menu_item import MenuItem, MenuItemGroup
from archinstall.tui.ui.result import ResultType
from archinstall.tui.menu_item import MenuItem, MenuItemGroup
from archinstall.tui.result import ResultType
def installed_package(package: str) -> LocalPackage | None:

View File

@ -5,8 +5,8 @@ from archinstall.lib.menu.helpers import Confirmation, Input
from archinstall.lib.models.pacman import PacmanConfiguration
from archinstall.lib.pathnames import PACMAN_CONF
from archinstall.lib.translationhandler import tr
from archinstall.tui.ui.menu_item import MenuItem, MenuItemGroup
from archinstall.tui.ui.result import ResultType
from archinstall.tui.menu_item import MenuItem, MenuItemGroup
from archinstall.tui.result import ResultType
class PacmanMenu(AbstractSubMenu[PacmanConfiguration]):

View File

@ -7,8 +7,8 @@ from archinstall.lib.menu.abstract_menu import AbstractSubMenu
from archinstall.lib.menu.helpers import Confirmation, Selection
from archinstall.lib.models.profile import ProfileConfiguration
from archinstall.lib.translationhandler import tr
from archinstall.tui.ui.menu_item import MenuItem, MenuItemGroup
from archinstall.tui.ui.result import ResultType
from archinstall.tui.menu_item import MenuItem, MenuItemGroup
from archinstall.tui.result import ResultType
class ProfileMenu(AbstractSubMenu[ProfileConfiguration]):

View File

@ -6,8 +6,8 @@ from archinstall.lib.menu.list_manager import ListManager
from archinstall.lib.menu.util import get_password
from archinstall.lib.models.users import User
from archinstall.lib.translationhandler import tr
from archinstall.tui.ui.menu_item import MenuItem
from archinstall.tui.ui.result import ResultType
from archinstall.tui.menu_item import MenuItem
from archinstall.tui.result import ResultType
class UserList(ListManager[User]):

View File

@ -18,7 +18,7 @@ from archinstall.lib.packages.util import check_version_upgrade
from archinstall.lib.pacman.pacman import Pacman
from archinstall.lib.translationhandler import tr, translation_handler
from archinstall.lib.utils.util import running_from_iso
from archinstall.tui.ui.components import tui
from archinstall.tui.components import tui
def _log_sys_info() -> None:

View File

@ -22,7 +22,7 @@ from archinstall.lib.output import debug, error, info
from archinstall.lib.packages.util import check_version_upgrade
from archinstall.lib.profile.profiles_handler import profile_handler
from archinstall.lib.translationhandler import tr
from archinstall.tui.ui.components import tui
from archinstall.tui.components import tui
def show_menu(

View File

@ -12,7 +12,7 @@ from archinstall.lib.network.network_handler import install_network_config
from archinstall.lib.output import debug, error, info
from archinstall.lib.profile.profiles_handler import profile_handler
from archinstall.lib.translationhandler import tr
from archinstall.tui.ui.components import tui
from archinstall.tui.components import tui
def perform_installation(arch_config_handler: ArchConfigHandler) -> None:

View File

@ -10,7 +10,7 @@ from archinstall.lib.installer import Installer
from archinstall.lib.menu.util import delayed_warning
from archinstall.lib.output import debug, error
from archinstall.lib.translationhandler import tr
from archinstall.tui.ui.components import tui
from archinstall.tui.components import tui
def show_menu(arch_config_handler: ArchConfigHandler) -> None:

File diff suppressed because it is too large Load Diff

View File

@ -1,334 +0,0 @@
from collections.abc import Awaitable, Callable, Iterable
from dataclasses import dataclass, field
from enum import Enum
from functools import cached_property
from typing import Any, ClassVar, Self, override
from archinstall.lib.translationhandler import tr
@dataclass
class MenuItem:
text: str
value: Any | None = None
action: Callable[[Any], Awaitable[Any]] | None = None
enabled: bool = True
read_only: bool = False
mandatory: bool = False
dependencies: list[str | Callable[[], bool]] = field(default_factory=list)
dependencies_not: list[str] = field(default_factory=list)
display_action: Callable[[Any], str] | None = None
preview_action: Callable[[Self], str | None] | None = None
key: str | None = None
_id: str = ''
_yes: ClassVar[Self | None] = None
_no: ClassVar[Self | None] = None
def __post_init__(self) -> None:
if self.key is not None:
self._id = self.key
else:
self._id = str(id(self))
@override
def __hash__(self) -> int:
return hash(self._id)
def get_id(self) -> str:
return self._id
def get_value(self) -> Any:
assert self.value is not None
return self.value
@classmethod
def yes(cls, action: Callable[[Any], Any] | None = None) -> Self:
if cls._yes is None:
cls._yes = cls(tr('Yes'), value=True, key='yes', action=action)
return cls._yes
@classmethod
def no(cls, action: Callable[[Any], Any] | None = None) -> Self:
if cls._no is None:
cls._no = cls(tr('No'), value=False, key='no', action=action)
return cls._no
def is_empty(self) -> bool:
return self.text == '' or self.text is None
def has_value(self) -> bool:
if self.value is None:
return False
elif isinstance(self.value, list) and len(self.value) == 0:
return False
elif isinstance(self.value, dict) and len(self.value) == 0:
return False
else:
return True
def get_display_value(self) -> str | None:
if self.display_action is not None:
return self.display_action(self.value)
return None
class MenuItemGroup:
def __init__(
self,
menu_items: list[MenuItem],
focus_item: MenuItem | None = None,
default_item: MenuItem | None = None,
sort_items: bool = False,
sort_case_sensitive: bool = True,
checkmarks: bool = False,
) -> None:
if len(menu_items) < 1:
raise ValueError('Menu must have at least one item')
if sort_items:
if sort_case_sensitive:
menu_items = sorted(menu_items, key=lambda x: x.text)
else:
menu_items = sorted(menu_items, key=lambda x: x.text.lower())
self._filter_pattern: str = ''
self._checkmarks: bool = checkmarks
self._menu_items: list[MenuItem] = menu_items
self.focus_item: MenuItem | None = focus_item
self.selected_items: list[MenuItem] = []
self.default_item: MenuItem | None = default_item
if not focus_item:
self.focus_first()
if self.focus_item not in self.items:
raise ValueError(f'Selected item not in menu: {self.focus_item}')
@classmethod
def from_objects(cls, items: list[Any]) -> Self:
items = [MenuItem(str(id(item)), value=item) for item in items]
return cls(items)
def add_item(self, item: MenuItem) -> None:
self._menu_items.append(item)
del self.items # resetting the cache
def find_by_id(self, item_id: str) -> MenuItem:
for item in self._menu_items:
if item.get_id() == item_id:
return item
raise ValueError(f'No item found for id: {item_id}')
def find_by_key(self, key: str) -> MenuItem:
for item in self._menu_items:
if item.key == key:
return item
raise ValueError(f'No item found for key: {key}')
def get_enabled_items(self) -> list[MenuItem]:
return [it for it in self.items if self.is_enabled(it)]
@classmethod
def yes_no(cls) -> Self:
return cls(
[MenuItem.yes(), MenuItem.no()],
sort_items=True,
)
@classmethod
def from_enum(
cls,
enum_cls: type[Enum],
sort_items: bool = False,
preset: Iterable[Enum] | Enum | None = None,
) -> Self:
items = [MenuItem(elem.value, value=elem) for elem in enum_cls]
group = cls(items, sort_items=sort_items)
if preset is not None:
group.set_selected_by_value(preset)
return group
def set_preview_for_all(self, action: Callable[[Any], str | None]) -> None:
for item in self.items:
item.preview_action = action
def set_focus_by_value(self, value: Any) -> None:
for item in self._menu_items:
if item.value == value:
self.focus_item = item
break
def set_default_by_value(self, value: Any) -> None:
for item in self._menu_items:
if item.value == value:
self.default_item = item
break
def set_selected_by_value(self, values: Any | list[Any] | None) -> None:
if values is None:
return
if not isinstance(values, list):
values = [values]
for item in self._menu_items:
if item.value in values:
self.selected_items.append(item)
if values:
self.set_focus_by_value(values[0])
def get_focused_index(self) -> int | None:
items = self.get_enabled_items()
if self.focus_item and items:
try:
return 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
@cached_property
def _max_items_text_width(self) -> int:
return max([len(item.text) for item in self._menu_items])
def _default_suffix(self, item: MenuItem) -> str:
if self.default_item == item:
return tr(' (default)')
return ''
def set_action_for_all(self, action: Callable[[Any], Any]) -> None:
for item in self.items:
item.action = action
@cached_property
def items(self) -> list[MenuItem]:
pattern = self._filter_pattern.lower()
items = filter(lambda item: item.is_empty() or pattern in item.text.lower(), self._menu_items)
l_items = sorted(items, key=self._items_score)
return l_items
def _items_score(self, item: MenuItem) -> int:
pattern = self._filter_pattern.lower()
if item.text.lower().startswith(pattern):
return 0
return 1
def set_filter_pattern(self, pattern: str) -> None:
self._filter_pattern = pattern
del self.items # resetting the cache
self.focus_first()
def focus_index(self, index: int) -> None:
enabled = self.get_enabled_items()
self.focus_item = enabled[index]
def focus_first(self) -> None:
if len(self.items) == 0:
return
first_item: MenuItem | None = self.items[0]
if first_item and not self._is_selectable(first_item):
first_item = self._find_next_selectable_item(self.items, first_item, 1)
if first_item is not None:
self.focus_item = first_item
def focus_last(self) -> None:
if len(self.items) == 0:
return
last_item: MenuItem | None = self.items[-1]
if last_item and not self._is_selectable(last_item):
last_item = self._find_next_selectable_item(self.items, last_item, -1)
if last_item is not None:
self.focus_item = last_item
def focus_prev(self, skip_empty: bool = True) -> 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)
if item is not None:
self.focus_item = item
def focus_next(self, skip_not_enabled: bool = True) -> 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)
if item is not None:
self.focus_item = item
def _find_next_selectable_item(
self,
items: list[MenuItem],
start_item: MenuItem,
direction: int,
) -> MenuItem | None:
start_index = self.items.index(start_item)
n = len(items)
current_index = start_index
for _ in range(n):
current_index = (current_index + direction) % n
if self._is_selectable(items[current_index]):
return items[current_index]
return None
def max_item_width(self) -> int:
spaces = [len(str(it.text)) for it in self.items]
if spaces:
return max(spaces)
return 0
def _is_selectable(self, item: MenuItem) -> bool:
if item.is_empty():
return False
elif item.read_only:
return False
return self.is_enabled(item)
def is_enabled(self, item: MenuItem) -> bool:
if not item.enabled:
return False
for dep in item.dependencies:
if isinstance(dep, str):
item = self.find_by_key(dep)
if not item.value or not self.is_enabled(item):
return False
else:
return dep()
for dep_not in item.dependencies_not:
item = self.find_by_key(dep_not)
if item.value is not None:
return False
return True

View File

@ -1,71 +0,0 @@
from dataclasses import dataclass
from enum import Enum, auto
from typing import Self, cast
from archinstall.tui.ui.menu_item import MenuItem
class ResultType(Enum):
Selection = auto()
Skip = auto()
Reset = auto()
@dataclass
class Result[ValueT]:
type_: ResultType
_data: ValueT | list[ValueT] | None = None
_item: MenuItem | list[MenuItem] | None = None
@classmethod
def true(cls) -> Self:
return cls(ResultType.Selection, _data=True) # type: ignore[arg-type]
@classmethod
def false(cls) -> Self:
return cls(ResultType.Selection, _data=False) # type: ignore[arg-type]
@classmethod
def reset(cls) -> Self:
return cls(ResultType.Reset)
@classmethod
def selection(cls, value: ValueT | list[ValueT] | None) -> Self:
return cls(ResultType.Selection, _data=value)
@classmethod
def skip(cls) -> Self:
return cls(ResultType.Skip)
def has_data(self) -> bool:
return self._data is not None
def has_value(self) -> bool:
return self._item is not None
def item(self) -> MenuItem:
if isinstance(self._item, list) or self._item is None:
raise ValueError('Invalid item type')
return self._item
def items(self) -> list[MenuItem]:
if isinstance(self._item, list):
return self._item
raise ValueError('Invalid item type')
def get_value(self) -> ValueT:
if self._item is not None:
return self.item().get_value() # type: ignore[no-any-return]
if type(self._data) is not list and self._data is not None:
return cast(ValueT, self._data)
raise ValueError('No value found')
def get_values(self) -> list[ValueT]:
if self._item is not None:
return [i.get_value() for i in self.items()]
assert type(self._data) is list
return cast(list[ValueT], self._data)