Browse Source

Only apply the font fallback to regions of missing glyphs

pull/8626/head
Benedikt Stebner 4 years ago
parent
commit
d524dc9cdb
  1. 26
      samples/Sandbox/MainWindow.axaml
  2. 115
      samples/Sandbox/MainWindow.axaml.cs
  3. 26
      src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs
  4. 4
      src/Avalonia.Controls/Presenters/TextPresenter.cs
  5. 2
      src/Avalonia.Controls/TextBox.cs

26
samples/Sandbox/MainWindow.axaml

@ -1,29 +1,5 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
x:Class="Sandbox.MainWindow">
<StackPanel>
<TextBox x:Name="txtBox" Text="أَبْجَدِيَّة عَرَبِيَّة" SelectionStart="19" SelectionEnd="23"/>
<NumericUpDown Value="{Binding #txtBox.SelectionStart, Mode=TwoWay}"/>
<NumericUpDown Value="{Binding #txtBox.SelectionEnd, Mode=TwoWay}"/>
<RichTextBlock x:Name="txtBlock" Text="أَبْجَدِيَّة عَرَبِيَّة" IsTextSelectionEnabled="True"/>
<NumericUpDown Value="{Binding Distance, Mode=TwoWay}"/>
<TextBlock Text="{Binding #txtBlock.SelectionStart}"/>
<TextBlock Text="{Binding #txtBlock.SelectionEnd}"/>
<DockPanel DockPanel.Dock="Top" LastChildFill="False">
<RichTextBlock Name="RichTextBlock"
TextAlignment="Left"
Margin="4,4,4,8"
ClipToBounds="True"
FontSize="30"
MaxLines="3"
TextWrapping="Wrap"
FontWeight="DemiBold"
Text="{Binding Text}"
Inlines="{Binding InlineCollection, Mode=OneWay}">
</RichTextBlock>
<Button Click="Button_OnClick">Update inlines</Button>
<Button Click="TextButton_OnClick">Update text</Button>
</DockPanel>
</StackPanel>
<TextBox Text="C:\привет мир\幸福\幸福"/>
</Window>

115
samples/Sandbox/MainWindow.axaml.cs

@ -13,126 +13,15 @@ namespace Sandbox
{
public class MainWindow : Window
{
private TestViewModel _dc;
public MainWindow()
{
this.InitializeComponent();
this.AttachDevTools();
var textBox = this.FindControl<TextBox>("txtBox");
textBox.TemplateApplied += TextBox_TemplateApplied;
}
private void TextBox_TemplateApplied(object sender, Avalonia.Controls.Primitives.TemplateAppliedEventArgs e)
{
var textBox = sender as TextBox;
var textPresenter = e.NameScope.Find("PART_TextPresenter") as TextPresenter;
_dc = new TestViewModel(textPresenter);
DataContext = _dc;
this.AttachDevTools();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
private void Button_OnClick(object? sender, RoutedEventArgs e)
{
_dc.InlineCollection = new InlineCollection
{
new Run(""),
new Run("test3") {FontWeight = Avalonia.Media.FontWeight.Bold},
};
// _dc.Text = "nununu";
}
private void TextButton_OnClick(object? sender, RoutedEventArgs e)
{
_dc.Text = "nununu";
}
}
public class TestViewModel : ViewModelBase
{
private readonly TextPresenter _textPresenter;
private double _distance = 45;
public TestViewModel(TextPresenter textPresenter)
{
_textPresenter = textPresenter;
}
private InlineCollection _inlineCollection;
private string _text;
public string Text
{
get => _text;
set
{
_text = value;
RaisePropertyChanged();
}
}
public InlineCollection InlineCollection
{
get => _inlineCollection;
set
{
_inlineCollection = value;
RaisePropertyChanged();
}
}
public double Distance
{
get => _distance;
set
{
OnDistanceChanged(value);
RaisePropertyChanged();
}
}
private void OnDistanceChanged(double distance)
{
if(distance < 0)
{
distance = 0;
}
if(distance > _textPresenter.TextLayout.Bounds.Width)
{
distance = _textPresenter.TextLayout.Bounds.Width;
}
var height = _textPresenter.TextLayout.Bounds.Height;
var distanceY = height / 2;
_textPresenter.MoveCaretToPoint(new Point(distance, distanceY));
var caretIndex = _textPresenter.CaretIndex;
Debug.WriteLine(caretIndex);
_distance = distance;
}
}
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged([CallerMemberName]string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}

26
src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs

@ -38,7 +38,7 @@ namespace Avalonia.Media.TextFormatting
/// Gets a list of <see cref="ShapeableTextCharacters"/>.
/// </summary>
/// <returns>The shapeable text characters.</returns>
internal IReadOnlyList<ShapeableTextCharacters> GetShapeableCharacters(ReadOnlySlice<char> runText, sbyte biDiLevel,
internal IReadOnlyList<ShapeableTextCharacters> GetShapeableCharacters(ReadOnlySlice<char> runText, sbyte biDiLevel,
ref TextRunProperties? previousProperties)
{
var shapeableCharacters = new List<ShapeableTextCharacters>(2);
@ -65,7 +65,7 @@ namespace Avalonia.Media.TextFormatting
/// <param name="biDiLevel">The bidi level of the run.</param>
/// <param name="previousProperties"></param>
/// <returns>A list of shapeable text runs.</returns>
private static ShapeableTextCharacters CreateShapeableRun(ReadOnlySlice<char> text,
private static ShapeableTextCharacters CreateShapeableRun(ReadOnlySlice<char> text,
TextRunProperties defaultProperties, sbyte biDiLevel, ref TextRunProperties? previousProperties)
{
var defaultTypeface = defaultProperties.Typeface;
@ -76,7 +76,7 @@ namespace Avalonia.Media.TextFormatting
{
if (script == Script.Common && previousTypeface is not null)
{
if(TryGetShapeableLength(text, previousTypeface.Value, defaultTypeface, out var fallbackCount, out _))
if (TryGetShapeableLength(text, previousTypeface.Value, defaultTypeface, out var fallbackCount, out _))
{
return new ShapeableTextCharacters(text.Take(fallbackCount),
defaultProperties.WithTypeface(previousTypeface.Value), biDiLevel);
@ -86,10 +86,10 @@ namespace Avalonia.Media.TextFormatting
return new ShapeableTextCharacters(text.Take(count), defaultProperties.WithTypeface(currentTypeface),
biDiLevel);
}
if (previousTypeface is not null)
{
if(TryGetShapeableLength(text, previousTypeface.Value, defaultTypeface, out count, out _))
if (TryGetShapeableLength(text, previousTypeface.Value, defaultTypeface, out count, out _))
{
return new ShapeableTextCharacters(text.Take(count),
defaultProperties.WithTypeface(previousTypeface.Value), biDiLevel);
@ -106,12 +106,12 @@ namespace Avalonia.Media.TextFormatting
{
continue;
}
codepoint = codepointEnumerator.Current;
break;
}
//ToDo: Fix FontFamily fallback
var matchFound =
FontManager.Current.TryMatchCharacter(codepoint, defaultTypeface.Style, defaultTypeface.Weight,
@ -157,14 +157,14 @@ namespace Avalonia.Media.TextFormatting
/// <param name="script"></param>
/// <returns></returns>
protected static bool TryGetShapeableLength(
ReadOnlySlice<char> text,
Typeface typeface,
ReadOnlySlice<char> text,
Typeface typeface,
Typeface? defaultTypeface,
out int length,
out Script script)
{
length = 0;
script = Script.Unknown;
script = Script.Unknown;
if (text.Length == 0)
{
@ -182,7 +182,7 @@ namespace Avalonia.Media.TextFormatting
var currentScript = currentGrapheme.FirstCodepoint.Script;
if (currentScript != Script.Common && defaultFont != null && defaultFont.TryGetGlyph(currentGrapheme.FirstCodepoint, out _))
if (defaultFont != null && defaultFont.TryGetGlyph(currentGrapheme.FirstCodepoint, out _))
{
break;
}
@ -192,7 +192,7 @@ namespace Avalonia.Media.TextFormatting
{
break;
}
if (currentScript != script)
{
if (script is Script.Unknown || currentScript != Script.Common &&

4
src/Avalonia.Controls/Presenters/TextPresenter.cs

@ -498,13 +498,13 @@ namespace Avalonia.Controls.Presenters
IReadOnlyList<ValueSpan<TextRunProperties>>? textStyleOverrides = null;
if (length > 0)
if (length > 0 && SelectionForegroundBrush != null)
{
textStyleOverrides = new[]
{
new ValueSpan<TextRunProperties>(start, length,
new GenericTextRunProperties(typeface, FontSize,
foregroundBrush: SelectionForegroundBrush ?? Brushes.Red))
foregroundBrush: SelectionForegroundBrush))
};
}

2
src/Avalonia.Controls/TextBox.cs

@ -1245,8 +1245,6 @@ namespace Avalonia.Controls
var caretIndex = hit.TextPosition;
Debug.WriteLine($"TextPos: {caretIndex}, X: {point.X}");
var text = Text;
if (text != null && _wordSelectionStart >= 0)

Loading…
Cancel
Save