refactor: rewrite the font configuration (#366)

* input font name directly instead of a font picker because localized font family name is not supported by Avalonia
* fallback monospace font to default font
* remove unused code
This commit is contained in:
leo 2024-08-19 17:14:41 +08:00
parent 24dde77548
commit 9057b71f2d
No known key found for this signature in database
20 changed files with 96 additions and 169 deletions

View file

@ -20,20 +20,6 @@ namespace SourceGit
}
}
public class FontFamilyConverter : JsonConverter<FontFamily>
{
public override FontFamily Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var name = reader.GetString();
return new FontFamily(name);
}
public override void Write(Utf8JsonWriter writer, FontFamily value, JsonSerializerOptions options)
{
writer.WriteStringValue(value.ToString());
}
}
public class GridLengthConverter : JsonConverter<GridLength>
{
public override GridLength Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
@ -54,7 +40,6 @@ namespace SourceGit
IgnoreReadOnlyProperties = true,
Converters = [
typeof(ColorConverter),
typeof(FontFamilyConverter),
typeof(GridLengthConverter),
]
)]

View file

@ -104,6 +104,7 @@ namespace SourceGit
var pref = ViewModels.Preference.Instance;
SetLocale(pref.Locale);
SetTheme(pref.Theme, pref.ThemeOverrides);
SetFonts(pref.DefaultFontFamily, pref.MonospaceFontFamily, pref.OnlyUseMonoFontInEditor);
}
public override void OnFrameworkInitializationCompleted()
@ -143,7 +144,10 @@ namespace SourceGit
public static void SetLocale(string localeKey)
{
var app = Current as App;
var targetLocale = app?.Resources[localeKey] as ResourceDictionary;
if (app == null)
return;
var targetLocale = app.Resources[localeKey] as ResourceDictionary;
if (targetLocale == null || targetLocale == app._activeLocale)
return;
@ -208,6 +212,46 @@ namespace SourceGit
}
}
public static void SetFonts(string defaultFont, string monospaceFont, bool onlyUseMonospaceFontInEditor)
{
var app = Current as App;
if (app == null)
return;
if (app._fontsOverrides != null)
{
app.Resources.MergedDictionaries.Remove(app._fontsOverrides);
app._fontsOverrides = null;
}
var resDic = new ResourceDictionary();
if (!string.IsNullOrEmpty(defaultFont))
resDic.Add("Fonts.Default", new FontFamily(defaultFont));
if (string.IsNullOrEmpty(monospaceFont))
{
if (!string.IsNullOrEmpty(defaultFont))
monospaceFont = $"fonts:SourceGit#JetBrains Mono,{defaultFont}";
}
else
{
if (!string.IsNullOrEmpty(defaultFont) && !monospaceFont.Contains(defaultFont, StringComparison.Ordinal))
monospaceFont = $"{monospaceFont},{defaultFont}";
resDic.Add("Fonts.Monospace", new FontFamily(monospaceFont));
}
var primary = onlyUseMonospaceFontInEditor ? defaultFont : monospaceFont;
if (!string.IsNullOrEmpty(primary))
resDic.Add("Fonts.Primary", new FontFamily(primary));
if (resDic.Count > 0)
{
app.Resources.MergedDictionaries.Add(resDic);
app._fontsOverrides = resDic;
}
}
public static async void CopyText(string data)
{
if (Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
@ -510,5 +554,6 @@ namespace SourceGit
private ViewModels.Launcher _launcher = null;
private ResourceDictionary _activeLocale = null;
private ResourceDictionary _themeOverrides = null;
private ResourceDictionary _fontsOverrides = null;
}
}

View file

@ -5,7 +5,6 @@ using System.IO;
using System.Runtime.Versioning;
using Avalonia;
using Avalonia.Media;
namespace SourceGit.Native
{
@ -37,11 +36,6 @@ namespace SourceGit.Native
public void SetupApp(AppBuilder builder)
{
builder.With(new FontManagerOptions()
{
DefaultFamilyName = "fonts:SourceGit#JetBrains Mono",
});
builder.With(new X11PlatformOptions()
{
EnableIme = true,

View file

@ -6,7 +6,6 @@ using System.Runtime.Versioning;
using System.Text;
using Avalonia;
using Avalonia.Media;
namespace SourceGit.Native
{
@ -15,11 +14,6 @@ namespace SourceGit.Native
{
public void SetupApp(AppBuilder builder)
{
builder.With(new FontManagerOptions()
{
DefaultFamilyName = "PingFang SC",
});
builder.With(new MacOSPlatformOptions()
{
DisableDefaultApplicationMenuItems = true,

View file

@ -8,7 +8,6 @@ using System.Text;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Media;
namespace SourceGit.Native
{
@ -62,12 +61,6 @@ namespace SourceGit.Native
public void SetupApp(AppBuilder builder)
{
builder.With(new FontManagerOptions()
{
DefaultFamilyName = "Microsoft YaHei UI",
FontFallbacks = [new FontFallback { FontFamily = "Microsoft YaHei" }],
});
// Fix drop shadow issue on Windows 10
RTL_OSVERSIONINFOEX v = new RTL_OSVERSIONINFOEX();
v.dwOSVersionInfoSize = (uint)Marshal.SizeOf<RTL_OSVERSIONINFOEX>();

View file

@ -156,7 +156,7 @@
</Style>
<Style Selector="ContentPresenter">
<Setter Property="FontFamily" Value="{Binding Source={x:Static vm:Preference.Instance}, Path=DefaultFont}"/>
<Setter Property="FontFamily" Value="{DynamicResource Fonts.Default}"/>
<Setter Property="FontSize" Value="{Binding Source={x:Static vm:Preference.Instance}, Path=DefaultFontSize}"/>
</Style>
@ -251,7 +251,7 @@
<Setter Property="HorizontalAlignment" Value="Left"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="Foreground" Value="{DynamicResource Brush.FG1}"/>
<Setter Property="FontFamily" Value="{Binding Source={x:Static vm:Preference.Instance}, Path=DefaultFont}"/>
<Setter Property="FontFamily" Value="{DynamicResource Fonts.Default}"/>
<Setter Property="FontSize" Value="{Binding Source={x:Static vm:Preference.Instance}, Path=DefaultFontSize}"/>
</Style>
<Style Selector="TextBlock.small">
@ -264,7 +264,7 @@
<Setter Property="FontStyle" Value="Italic"/>
</Style>
<Style Selector="TextBlock.primary, SelectableTextBlock.primary">
<Setter Property="FontFamily" Value="{Binding Source={x:Static vm:Preference.Instance}, Path=PrimaryFont}"/>
<Setter Property="FontFamily" Value="{DynamicResource Fonts.Primary}"/>
</Style>
<Style Selector="TextBlock.group_header_label">
<Setter Property="Foreground" Value="{DynamicResource Brush.FG2}"/>

View file

@ -103,4 +103,8 @@
<SolidColorBrush x:Key="Brush.Diff.AddedHighlight" Color="{DynamicResource Color.Diff.AddedHighlight}"/>
<SolidColorBrush x:Key="Brush.Diff.DeletedHighlight" Color="{DynamicResource Color.Diff.DeletedHighlight}"/>
<SolidColorBrush x:Key="Brush.Link" Color="{DynamicResource Color.Link}"/>
<FontFamily x:Key="Fonts.Default">$Default</FontFamily>
<FontFamily x:Key="Fonts.Monospace">fonts:SourceGit#JetBrains Mono,$Default</FontFamily>
<FontFamily x:Key="Fonts.Primary">fonts:SourceGit#JetBrains Mono,$Default</FontFamily>
</ResourceDictionary>

View file

@ -5,7 +5,6 @@ using System.Text.Json;
using System.Text.Json.Serialization;
using Avalonia.Collections;
using Avalonia.Media;
using CommunityToolkit.Mvvm.ComponentModel;
@ -20,6 +19,7 @@ namespace SourceGit.ViewModels
{
if (_instance == null)
{
_isLoading = true;
if (!File.Exists(_savePath))
{
_instance = new Preference();
@ -35,14 +35,9 @@ namespace SourceGit.ViewModels
_instance = new Preference();
}
}
_isLoading = false;
}
if (_instance.DefaultFont == null)
_instance.DefaultFont = FontManager.Current.DefaultFontFamily;
if (_instance.MonospaceFont == null)
_instance.MonospaceFont = new FontFamily("fonts:SourceGit#JetBrains Mono");
if (!_instance.IsGitConfigured())
_instance.GitInstallPath = Native.OS.FindGitExecutable();
@ -55,7 +50,7 @@ namespace SourceGit.ViewModels
get => _locale;
set
{
if (SetProperty(ref _locale, value))
if (SetProperty(ref _locale, value) && !_isLoading)
App.SetLocale(value);
}
}
@ -65,7 +60,7 @@ namespace SourceGit.ViewModels
get => _theme;
set
{
if (SetProperty(ref _theme, value))
if (SetProperty(ref _theme, value) && !_isLoading)
App.SetTheme(_theme, _themeOverrides);
}
}
@ -75,44 +70,37 @@ namespace SourceGit.ViewModels
get => _themeOverrides;
set
{
if (SetProperty(ref _themeOverrides, value))
if (SetProperty(ref _themeOverrides, value) && !_isLoading)
App.SetTheme(_theme, value);
}
}
public FontFamily DefaultFont
public string DefaultFontFamily
{
get => _defaultFont;
set
{
if (SetProperty(ref _defaultFont, value) && _onlyUseMonoFontInEditor)
OnPropertyChanged(nameof(PrimaryFont));
get => _defaultFontFamily;
set {
if (SetProperty(ref _defaultFontFamily, value) && !_isLoading)
App.SetFonts(_defaultFontFamily, _monospaceFontFamily, _onlyUseMonoFontInEditor);
}
}
public FontFamily MonospaceFont
public string MonospaceFontFamily
{
get => _monospaceFont;
get => _monospaceFontFamily;
set
{
if (SetProperty(ref _monospaceFont, value) && !_onlyUseMonoFontInEditor)
OnPropertyChanged(nameof(PrimaryFont));
if (SetProperty(ref _monospaceFontFamily, value) && !_isLoading)
App.SetFonts(_defaultFontFamily, _monospaceFontFamily, _onlyUseMonoFontInEditor);
}
}
[JsonIgnore]
public FontFamily PrimaryFont
{
get => _onlyUseMonoFontInEditor ? _defaultFont : _monospaceFont;
}
public bool OnlyUseMonoFontInEditor
{
get => _onlyUseMonoFontInEditor;
set
{
if (SetProperty(ref _onlyUseMonoFontInEditor, value))
OnPropertyChanged(nameof(PrimaryFont));
if (SetProperty(ref _onlyUseMonoFontInEditor, value) && !_isLoading)
App.SetFonts(_defaultFontFamily, _monospaceFontFamily, _onlyUseMonoFontInEditor);
}
}
@ -494,13 +482,14 @@ namespace SourceGit.ViewModels
}
private static Preference _instance = null;
private static bool _isLoading = false;
private static readonly string _savePath = Path.Combine(Native.OS.DataDir, "preference.json");
private string _locale = "en_US";
private string _theme = "Default";
private string _themeOverrides = string.Empty;
private FontFamily _defaultFont = null;
private FontFamily _monospaceFont = null;
private string _defaultFontFamily = string.Empty;
private string _monospaceFontFamily = string.Empty;
private bool _onlyUseMonoFontInEditor = false;
private double _defaultFontSize = 13;
private LayoutInfo _layout = new LayoutInfo();

View file

@ -105,7 +105,8 @@ namespace SourceGit.Views
EndPoint = new RelativePoint(0, 1, RelativeUnit.Relative),
};
var typeface = new Typeface("fonts:SourceGit#JetBrains Mono");
var fontFamily = avatar.FindResource("Fonts.Monospace") as FontFamily;
var typeface = new Typeface(fontFamily);
avatar._fallbackLabel = new FormattedText(
placeholder,

View file

@ -58,7 +58,7 @@
BorderThickness="1"
Background="{DynamicResource Brush.Contents}"
Foreground="{DynamicResource Brush.FG1}"
FontFamily="{Binding Source={x:Static vm:Preference.Instance}, Path=MonospaceFont}"
FontFamily="{DynamicResource Fonts.Monospace}"
BlameData="{Binding Data}"/>
<!-- Not supported mask (for binary files) -->

View file

@ -86,7 +86,7 @@
<v:BranchTreeNodeTrackStatusPresenter Grid.Column="2"
Margin="8,0"
VerticalAlignment="Center"
FontFamily="{Binding Source={x:Static vm:Preference.Instance}, Path=MonospaceFont}"
FontFamily="{DynamicResource Fonts.Monospace}"
FontSize="10"
Foreground="{DynamicResource Brush.BadgeFG}"
Background="{DynamicResource Brush.Badge}"/>

View file

@ -91,7 +91,8 @@ namespace SourceGit.Views
if (Change == null || Bounds.Width <= 0)
return;
var typeface = new Typeface("fonts:SourceGit#JetBrains Mono");
var fontFamily = this.FindResource("Fonts.Monospace") as FontFamily;
var typeface = new Typeface(fontFamily);
IBrush background;
string indicator;

View file

@ -96,7 +96,7 @@
BranchNameBackground="{DynamicResource Brush.DecoratorBranch}"
TagNameBackground="{DynamicResource Brush.DecoratorTag}"
LabelForeground="{DynamicResource Brush.DecoratorFG}"
FontFamily="{Binding Source={x:Static vm:Preference.Instance}, Path=MonospaceFont}"
FontFamily="{DynamicResource Fonts.Monospace}"
FontSize="10"
VerticalAlignment="Center"/>
</Border>

View file

@ -73,7 +73,7 @@
BranchNameBackground="{DynamicResource Brush.DecoratorBranch}"
TagNameBackground="{DynamicResource Brush.DecoratorTag}"
LabelForeground="{DynamicResource Brush.DecoratorFG}"
FontFamily="{Binding Source={x:Static vm:Preference.Instance}, Path=MonospaceFont}"
FontFamily="{DynamicResource Fonts.Monospace}"
FontSize="10"
VerticalAlignment="Center"/>

View file

@ -157,39 +157,19 @@
Text="{DynamicResource Text.Preference.Appearance.DefaultFont}"
HorizontalAlignment="Right"
Margin="0,0,16,0"/>
<ComboBox Grid.Row="1" Grid.Column="1"
MinHeight="28"
Padding="8,0"
HorizontalAlignment="Stretch"
ItemsSource="{Binding #ThisControl.InstalledFonts}"
SelectedItem="{Binding DefaultFont, Mode=TwoWay}">
<ComboBox.ItemTemplate>
<DataTemplate DataType="FontFamily">
<Border Height="24">
<TextBlock VerticalAlignment="Center" Text="{Binding Name}" FontFamily="{Binding}"/>
</Border>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<TextBox Grid.Row="1" Grid.Column="1"
Height="28"
CornerRadius="3"
Text="{Binding DefaultFontFamily, Mode=TwoWay}"/>
<TextBlock Grid.Row="2" Grid.Column="0"
Text="{DynamicResource Text.Preference.Appearance.MonospaceFont}"
HorizontalAlignment="Right"
Margin="0,0,16,0"/>
<ComboBox Grid.Row="2" Grid.Column="1"
MinHeight="28"
Padding="8,0"
HorizontalAlignment="Stretch"
ItemsSource="{Binding #ThisControl.InstalledMonospaceFonts}"
SelectedItem="{Binding MonospaceFont, Mode=TwoWay}">
<ComboBox.ItemTemplate>
<DataTemplate DataType="FontFamily">
<Border Height="24">
<TextBlock VerticalAlignment="Center" Text="{Binding Name}" FontFamily="{Binding}"/>
</Border>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<TextBox Grid.Row="2" Grid.Column="1"
Height="28"
CornerRadius="3"
Text="{Binding MonospaceFontFamily, Mode=TwoWay}"/>
<TextBlock Grid.Row="3" Grid.Column="0"
Text="{DynamicResource Text.Preference.Appearance.DefaultFontSize}"

View file

@ -1,30 +1,15 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Threading.Tasks;
using Avalonia;
using Avalonia.Collections;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Media;
using Avalonia.Platform.Storage;
using Avalonia.Threading;
namespace SourceGit.Views
{
public partial class Preference : ChromelessWindow
{
public AvaloniaList<FontFamily> InstalledFonts
{
get;
}
public AvaloniaList<FontFamily> InstalledMonospaceFonts
{
get;
}
public string DefaultUser
{
get;
@ -93,51 +78,6 @@ namespace SourceGit.Views
var pref = ViewModels.Preference.Instance;
DataContext = pref;
var builtInMono = new FontFamily("fonts:SourceGit#JetBrains Mono");
InstalledFonts = new AvaloniaList<FontFamily>();
InstalledFonts.Add(builtInMono);
InstalledFonts.AddRange(FontManager.Current.SystemFonts);
InstalledMonospaceFonts = new AvaloniaList<FontFamily>();
InstalledMonospaceFonts.Add(builtInMono);
var curMonoFont = pref.MonospaceFont;
if (curMonoFont != builtInMono)
{
InstalledMonospaceFonts.Add(curMonoFont);
}
Task.Run(() =>
{
var sysMonoFonts = new List<FontFamily>();
foreach (var font in FontManager.Current.SystemFonts)
{
if (font == curMonoFont)
continue;
var typeface = new Typeface(font);
var testI = new FormattedText(
"i",
CultureInfo.CurrentCulture,
FlowDirection.LeftToRight,
typeface,
12,
Brushes.White);
var testW = new FormattedText(
"W",
CultureInfo.CurrentCulture,
FlowDirection.LeftToRight,
typeface,
12,
Brushes.White);
if (Math.Abs(testI.Width - testW.Width) < 0.0001)
sysMonoFonts.Add(font);
}
Dispatcher.UIThread.Post(() => InstalledMonospaceFonts.AddRange(sysMonoFonts));
});
var ver = string.Empty;
if (pref.IsGitConfigured())
{

View file

@ -95,7 +95,7 @@
Margin="6,0"
VerticalAlignment="Center"
Count="{Binding LocalChangesCount}"
FontFamily="{Binding Source={x:Static vm:Preference.Instance}, Path=MonospaceFont}"
FontFamily="{DynamicResource Fonts.Monospace}"
FontSize="10"
Foreground="{DynamicResource Brush.BadgeFG}"
Background="{DynamicResource Brush.Badge}"/>
@ -110,7 +110,7 @@
Margin="6,0"
VerticalAlignment="Center"
Count="{Binding StashesCount}"
FontFamily="{Binding Source={x:Static vm:Preference.Instance}, Path=MonospaceFont}"
FontFamily="{DynamicResource Fonts.Monospace}"
FontSize="10"
Foreground="{DynamicResource Brush.BadgeFG}"
Background="{DynamicResource Brush.Badge}"/>

View file

@ -42,7 +42,7 @@
</DataTemplate>
<DataTemplate DataType="m:RevisionTextFile">
<v:RevisionTextFileView FontFamily="{Binding Source={x:Static vm:Preference.Instance}, Path=MonospaceFont}" Background="{DynamicResource Brush.Contents}"/>
<v:RevisionTextFileView FontFamily="{DynamicResource Fonts.Monospace}" Background="{DynamicResource Brush.Contents}"/>
</DataTemplate>
<DataTemplate DataType="m:RevisionImageFile">

View file

@ -86,7 +86,8 @@ namespace SourceGit.Views
else
maxV = (int)Math.Ceiling(maxV / 500.0) * 500;
var typeface = new Typeface("fonts:SourceGit#JetBrains Mono");
var fontFamily = this.FindResource("Fonts.Monospace") as FontFamily;
var typeface = new Typeface(fontFamily);
var pen = new Pen(LineBrush);
var width = Bounds.Width;
var height = Bounds.Height;

View file

@ -22,7 +22,7 @@
AddedHighlightBrush="{DynamicResource Brush.Diff.AddedHighlight}"
DeletedHighlightBrush="{DynamicResource Brush.Diff.DeletedHighlight}"
IndicatorForeground="{DynamicResource Brush.FG2}"
FontFamily="{Binding Source={x:Static vm:Preference.Instance}, Path=MonospaceFont}"
FontFamily="{DynamicResource Fonts.Monospace}"
UseSyntaxHighlighting="{Binding Source={x:Static vm:Preference.Instance}, Path=UseSyntaxHighlighting}"
WordWrap="{Binding Source={x:Static vm:Preference.Instance}, Path=EnableDiffViewWordWrap}"
ShowHiddenSymbols="{Binding Source={x:Static vm:Preference.Instance}, Path=ShowHiddenSymbolsInDiffView}"
@ -43,7 +43,7 @@
AddedHighlightBrush="{DynamicResource Brush.Diff.AddedHighlight}"
DeletedHighlightBrush="{DynamicResource Brush.Diff.DeletedHighlight}"
IndicatorForeground="{DynamicResource Brush.FG2}"
FontFamily="{Binding Source={x:Static vm:Preference.Instance}, Path=MonospaceFont}"
FontFamily="{DynamicResource Fonts.Monospace}"
UseSyntaxHighlighting="{Binding Source={x:Static vm:Preference.Instance}, Path=UseSyntaxHighlighting}"
WordWrap="{Binding Source={x:Static vm:Preference.Instance}, Path=EnableDiffViewWordWrap}"
ShowHiddenSymbols="{Binding Source={x:Static vm:Preference.Instance}, Path=ShowHiddenSymbolsInDiffView}"
@ -63,7 +63,7 @@
AddedHighlightBrush="{DynamicResource Brush.Diff.AddedHighlight}"
DeletedHighlightBrush="{DynamicResource Brush.Diff.DeletedHighlight}"
IndicatorForeground="{DynamicResource Brush.FG2}"
FontFamily="{Binding Source={x:Static vm:Preference.Instance}, Path=MonospaceFont}"
FontFamily="{DynamicResource Fonts.Monospace}"
UseSyntaxHighlighting="{Binding Source={x:Static vm:Preference.Instance}, Path=UseSyntaxHighlighting}"
WordWrap="{Binding Source={x:Static vm:Preference.Instance}, Path=EnableDiffViewWordWrap}"
ShowHiddenSymbols="{Binding Source={x:Static vm:Preference.Instance}, Path=ShowHiddenSymbolsInDiffView}"