Add optional "Additional fonts" selection to Applications menu (#4420)

* Add Fonts application with multi-select for emoji and CJK packages

* Rename to Additional fonts and add package descriptions

* Add noto-fonts to font package selection

* Move font descriptions into FontPackage enum method

* ci: trigger tests

* Add ttf-liberation and ttf-dejavu to font selection
This commit is contained in:
Softer 2026-04-18 05:02:47 +03:00 committed by GitHub
parent 9fdd7eb12e
commit 4fcef35af0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 142 additions and 0 deletions

View File

@ -0,0 +1,14 @@
from typing import TYPE_CHECKING
from archinstall.lib.models.application import FontsConfiguration
from archinstall.lib.output import debug
if TYPE_CHECKING:
from archinstall.lib.installer import Installer
class FontsApp:
def install(self, install_session: Installer, fonts_config: FontsConfiguration) -> None:
packages = [f.value for f in fonts_config.fonts]
debug(f'Installing fonts: {packages}')
install_session.add_additional_packages(packages)

View File

@ -3,6 +3,7 @@ from typing import TYPE_CHECKING
from archinstall.applications.audio import AudioApp
from archinstall.applications.bluetooth import BluetoothApp
from archinstall.applications.firewall import FirewallApp
from archinstall.applications.fonts import FontsApp
from archinstall.applications.power_management import PowerManagementApp
from archinstall.applications.print_service import PrintServiceApp
from archinstall.lib.models import Audio
@ -42,3 +43,9 @@ class ApplicationHandler:
install_session,
app_config.firewall_config,
)
if app_config.fonts_config:
FontsApp().install(
install_session,
app_config.fonts_config,
)

View File

@ -10,6 +10,8 @@ from archinstall.lib.models.application import (
BluetoothConfiguration,
Firewall,
FirewallConfiguration,
FontPackage,
FontsConfiguration,
PowerManagement,
PowerManagementConfiguration,
PrintServiceConfiguration,
@ -77,6 +79,13 @@ class ApplicationMenu(AbstractSubMenu[ApplicationConfiguration]):
preview_action=self._prev_firewall,
key='firewall_config',
),
MenuItem(
text=tr('Additional fonts'),
action=select_fonts,
value=self._app_config.fonts_config,
preview_action=self._prev_fonts,
key='fonts_config',
),
]
def _prev_power_management(self, item: MenuItem) -> str | None:
@ -115,6 +124,13 @@ class ApplicationMenu(AbstractSubMenu[ApplicationConfiguration]):
return f'{tr("Firewall")}: {config.firewall.value}'
return None
def _prev_fonts(self, item: MenuItem) -> str | None:
if item.value is not None:
config: FontsConfiguration = item.value
packages = ', '.join(f.value for f in config.fonts)
return f'{tr("Additional fonts")}: {packages}'
return None
async def select_power_management(preset: PowerManagementConfiguration | None = None) -> PowerManagementConfiguration | None:
group = MenuItemGroup.from_enum(PowerManagement)
@ -217,3 +233,31 @@ async def select_firewall(preset: FirewallConfiguration | None = None) -> Firewa
return FirewallConfiguration(firewall=result.get_value())
case ResultType.Reset:
return None
async def select_fonts(preset: FontsConfiguration | None = None) -> FontsConfiguration | None:
items = [MenuItem(f'{f.value} ({f.description()})', value=f) for f in FontPackage]
group = MenuItemGroup(items)
if preset:
for f in preset.fonts:
group.set_selected_by_value(f)
result = await Selection[FontPackage](
group,
header=tr('Select font packages to install'),
allow_skip=True,
allow_reset=True,
multi=True,
).show()
match result.type_:
case ResultType.Skip:
return preset
case ResultType.Selection:
selected = result.get_values()
if selected:
return FontsConfiguration(fonts=selected)
return None
case ResultType.Reset:
return None

View File

@ -2,6 +2,8 @@ from dataclasses import dataclass
from enum import StrEnum, auto
from typing import Any, NotRequired, Self, TypedDict
from archinstall.lib.translationhandler import tr
class PowerManagement(StrEnum):
POWER_PROFILES_DAEMON = 'power-profiles-daemon'
@ -39,6 +41,31 @@ class FirewallConfigSerialization(TypedDict):
firewall: str
class FontPackage(StrEnum):
NOTO = 'noto-fonts'
EMOJI = 'noto-fonts-emoji'
CJK = 'noto-fonts-cjk'
LIBERATION = 'ttf-liberation'
DEJAVU = 'ttf-dejavu'
def description(self) -> str:
match self:
case FontPackage.NOTO:
return tr('Unicode font coverage for most languages')
case FontPackage.EMOJI:
return tr('color emoji for browsers and apps')
case FontPackage.CJK:
return tr('Chinese, Japanese, Korean characters')
case FontPackage.LIBERATION:
return tr('Arial/Times/Courier replacement, Cyrillic support for Steam/games')
case FontPackage.DEJAVU:
return tr('wide Unicode coverage, good fallback font')
class FontsConfigSerialization(TypedDict):
fonts: list[str]
class ZramAlgorithm(StrEnum):
ZSTD = auto()
LZO_RLE = 'lzo-rle'
@ -53,6 +80,7 @@ class ApplicationSerialization(TypedDict):
power_management_config: NotRequired[PowerManagementConfigSerialization]
print_service_config: NotRequired[PrintServiceConfigSerialization]
firewall_config: NotRequired[FirewallConfigSerialization]
fonts_config: NotRequired[FontsConfigSerialization]
@dataclass
@ -127,6 +155,18 @@ class FirewallConfiguration:
)
@dataclass
class FontsConfiguration:
fonts: list[FontPackage]
def json(self) -> FontsConfigSerialization:
return {'fonts': [f.value for f in self.fonts]}
@classmethod
def parse_arg(cls, arg: FontsConfigSerialization) -> Self:
return cls(fonts=[FontPackage(f) for f in arg['fonts']])
@dataclass(frozen=True)
class ZramConfiguration:
enabled: bool
@ -149,6 +189,7 @@ class ApplicationConfiguration:
power_management_config: PowerManagementConfiguration | None = None
print_service_config: PrintServiceConfiguration | None = None
firewall_config: FirewallConfiguration | None = None
fonts_config: FontsConfiguration | None = None
@classmethod
def parse_arg(
@ -177,6 +218,9 @@ class ApplicationConfiguration:
if args and (firewall_config := args.get('firewall_config')) is not None:
app_config.firewall_config = FirewallConfiguration.parse_arg(firewall_config)
if args and (fonts_config := args.get('fonts_config')) is not None:
app_config.fonts_config = FontsConfiguration.parse_arg(fonts_config)
return app_config
def json(self) -> ApplicationSerialization:
@ -197,4 +241,7 @@ class ApplicationConfiguration:
if self.firewall_config:
config['firewall_config'] = self.firewall_config.json()
if self.fonts_config:
config['fonts_config'] = self.fonts_config.json()
return config

View File

@ -2070,6 +2070,21 @@ msgstr ""
msgid "Firewall"
msgstr ""
msgid "Additional fonts"
msgstr ""
msgid "Select font packages to install"
msgstr ""
msgid "Unicode font coverage for most languages"
msgstr ""
msgid "color emoji for browsers and apps"
msgstr ""
msgid "Chinese, Japanese, Korean characters"
msgstr ""
msgid "Select audio configuration"
msgstr ""

View File

@ -2011,6 +2011,21 @@ msgstr "Використовувати NetworkManager (з iwd)"
msgid "Firewall"
msgstr "Брандмауер"
msgid "Additional fonts"
msgstr "Додаткові шрифти"
msgid "Select font packages to install"
msgstr "Оберіть пакети шрифтів для встановлення"
msgid "Unicode font coverage for most languages"
msgstr "покриття шрифтами Unicode для більшості мов"
msgid "color emoji for browsers and apps"
msgstr "кольорові емодзі для браузерів та програм"
msgid "Chinese, Japanese, Korean characters"
msgstr "китайські, японські, корейські символи"
msgid "Select audio configuration"
msgstr "Оберіть конфігурацію аудіо"