версия 2.0.8: выбор шрифта интерфейса в Настройках

- Настройки: выпадающий список «Шрифт» со всеми системными шрифтами
  (+ «По умолчанию»), с превью каждого шрифта в себе самом
- шрифт применяется ко всему приложению (ресурс AppFontFamily + стиль Window),
  сохраняется и восстанавливается при старте

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Exest 2026-08-13 16:21:00 +03:00
parent 4e341f76c3
commit c808d62188
8 changed files with 64 additions and 5 deletions

View File

@ -3,7 +3,7 @@
#define MyAppName "Электронная Фармация"
#define MyAppBrand "ЭльФиСА"
#define MyAppVersion "2.0.7"
#define MyAppVersion "2.0.8"
#define MyAppPublisher "PharmData"
#define MyAppURL "https://cdn.24pharmdata.ru"
#define MyAppSupportURL "https://24pharmdata.ru"

View File

@ -41,11 +41,18 @@
<SolidColorBrush x:Key="SidebarActive" Color="#0A4A44"/>
<SolidColorBrush x:Key="SidebarText" Color="#D5F5F0"/>
<SolidColorBrush x:Key="OnAccent" Color="#FFFFFF"/>
<!-- Шрифт интерфейса. Меняется из Настроек: Resources["AppFontFamily"] = FontFamily. -->
<FontFamily x:Key="AppFontFamily">Segoe UI</FontFamily>
</ResourceDictionary>
</Application.Resources>
<Application.Styles>
<FluentTheme />
<!-- Единый шрифт для всех окон (дочерние контролы наследуют). -->
<Style Selector="Window">
<Setter Property="FontFamily" Value="{DynamicResource AppFontFamily}"/>
</Style>
<StyleInclude Source="avares://Avalonia.Controls.DataGrid/Themes/Fluent.xaml"/>
<StyleInclude Source="/Styles/Elfisa.axaml"/>
</Application.Styles>

View File

@ -4,6 +4,7 @@ using Avalonia.Data.Core;
using Avalonia.Data.Core.Plugins;
using System.Linq;
using Avalonia.Markup.Xaml;
using Avalonia.Media;
using Elfisa.Avalonia.ViewModels;
using Elfisa.Avalonia.Views;
@ -11,17 +12,30 @@ namespace Elfisa.Avalonia;
public partial class App : Application
{
/// <summary>Дефолтный шрифт интерфейса (когда «По умолчанию»).</summary>
public const string DefaultFont = "Segoe UI";
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
}
/// <summary>Применяет шрифт ко всему приложению (ресурс наследуется всеми окнами).</summary>
public static void ApplyFont(string? name)
{
var family = string.IsNullOrWhiteSpace(name) ? DefaultFont : name!;
if (Current is { } app)
app.Resources["AppFontFamily"] = new FontFamily(family);
}
public override void OnFrameworkInitializationCompleted()
{
RequestedThemeVariant = Elfisa.Core.SettingsStore.Current.Theme == "Dark"
? global::Avalonia.Styling.ThemeVariant.Dark
: global::Avalonia.Styling.ThemeVariant.Light;
ApplyFont(Elfisa.Core.SettingsStore.Current.FontFamily);
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
desktop.MainWindow = new MainWindow

View File

@ -5,9 +5,9 @@
<Nullable>enable</Nullable>
<ApplicationManifest>app.manifest</ApplicationManifest>
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
<Version>2.0.7</Version>
<AssemblyVersion>2.0.7.0</AssemblyVersion>
<FileVersion>2.0.7.0</FileVersion>
<Version>2.0.8</Version>
<AssemblyVersion>2.0.8.0</AssemblyVersion>
<FileVersion>2.0.8.0</FileVersion>
<Product>ЭльФиСА — Электронная Фармация</Product>
<Company>PharmData</Company>
<!-- Кросс-платформенная публикация задаётся флагами CLI (RID + self-contained). -->

View File

@ -2,6 +2,7 @@ using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using Avalonia.Media;
using CommunityToolkit.Mvvm.ComponentModel;
using Elfisa.Core;
@ -11,10 +12,17 @@ public partial class SettingsViewModel : ViewModelBase
{
private readonly ApiClient _api = new();
/// <summary>Пункт «По умолчанию» в списке шрифтов.</summary>
public const string DefaultFontLabel = "По умолчанию";
[ObservableProperty] private string _apiBaseUrl = "";
public string[] Themes { get; } = { "Светлая", "Тёмная" };
[ObservableProperty] private int _themeIndex;
/// <summary>Все системные шрифты + «По умолчанию» первым.</summary>
public ObservableCollection<string> Fonts { get; } = new();
[ObservableProperty] private string _selectedFont = DefaultFontLabel;
public ObservableCollection<BuyerLocationDto> Locations { get; } = new();
[ObservableProperty] private BuyerLocationDto? _selectedLocation;
@ -23,6 +31,20 @@ public partial class SettingsViewModel : ViewModelBase
_api.SetToken(Session.Current.Token);
ApiBaseUrl = SettingsStore.Current.ApiBaseUrl ?? AppConfig.DefaultApiBaseUrl;
ThemeIndex = SettingsStore.Current.Theme == "Dark" ? 1 : 0;
Fonts.Add(DefaultFontLabel);
foreach (var f in FontManager.Current.SystemFonts
.Select(x => x.Name)
.Where(n => !string.IsNullOrWhiteSpace(n))
.Distinct()
.OrderBy(n => n, StringComparer.CurrentCultureIgnoreCase))
Fonts.Add(f);
var saved = SettingsStore.Current.FontFamily;
SelectedFont = !string.IsNullOrWhiteSpace(saved) && Fonts.Contains(saved!)
? saved!
: DefaultFontLabel;
if (Session.Current.IsAuthenticated) _ = LoadLocationsAsync();
}

View File

@ -3,7 +3,7 @@
xmlns:vm="using:Elfisa.Avalonia.ViewModels"
x:Class="Elfisa.Avalonia.Views.SettingsWindow"
x:DataType="vm:SettingsViewModel"
Width="460" Height="440" CanResize="False"
Width="460" Height="520" CanResize="False"
WindowStartupLocation="CenterOwner"
Background="{DynamicResource Bg}"
Title="Настройки">
@ -21,6 +21,18 @@
<ComboBox ItemsSource="{Binding Themes}" SelectedIndex="{Binding ThemeIndex}" HorizontalAlignment="Stretch"/>
</StackPanel>
<StackPanel Spacing="6">
<TextBlock Text="Шрифт" Classes="label"/>
<ComboBox ItemsSource="{Binding Fonts}" SelectedItem="{Binding SelectedFont}"
HorizontalAlignment="Stretch" MaxDropDownHeight="360">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" FontFamily="{Binding}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</StackPanel>
<StackPanel Spacing="6">
<TextBlock Text="Грузополучатель по умолчанию" Classes="label"/>
<ComboBox ItemsSource="{Binding Locations}" SelectedItem="{Binding SelectedLocation}" HorizontalAlignment="Stretch"/>

View File

@ -42,11 +42,14 @@ public partial class SettingsWindow : Window
{
SettingsStore.Current.ApiBaseUrl = string.IsNullOrWhiteSpace(vm.ApiBaseUrl) ? null : vm.ApiBaseUrl.Trim().TrimEnd('/');
SettingsStore.Current.Theme = vm.ThemeIndex == 1 ? "Dark" : "Light";
var font = vm.SelectedFont == SettingsViewModel.DefaultFontLabel ? null : vm.SelectedFont;
SettingsStore.Current.FontFamily = font;
SettingsStore.Current.DefaultLocationId = vm.SelectedLocation?.BuyerLocationId;
SettingsStore.Save();
if (Application.Current is { } app)
app.RequestedThemeVariant = vm.ThemeIndex == 1 ? ThemeVariant.Dark : ThemeVariant.Light;
App.ApplyFont(font); // применяем сразу ко всем окнам
}
Close();
}

View File

@ -6,6 +6,7 @@ public sealed class AppSettings
{
public string? ApiBaseUrl { get; set; }
public string Theme { get; set; } = "Light"; // Light | Dark
public string? FontFamily { get; set; } // шрифт интерфейса (null = по умолчанию)
public string? DefaultLocationId { get; set; }
public string? Token { get; set; } // сохранённая сессия
public string? Role { get; set; }