Browse Source

Merge branch 'master' into master

pull/10142/head
Max Katz 3 years ago
committed by GitHub
parent
commit
84767a13cd
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 75
      src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs
  2. 14
      src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs
  3. 29
      src/Avalonia.Controls/TreeViewItem.cs
  4. 1
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs
  5. 3
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlControlThemeTransformer.cs
  6. 45
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDuplicateSettersChecker.cs
  7. 3
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSelectorTransformer.cs
  8. 4
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs
  9. 41
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlTests.cs
  10. 8
      tests/Avalonia.Skia.UnitTests/Media/TextFormatting/SingleBufferTextSource.cs
  11. 11
      tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs

75
src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs

@ -57,7 +57,7 @@ namespace Avalonia.Media.TextFormatting
textSourceLength,
paragraphWidth, paragraphProperties, resolvedFlowDirection, nextLineBreak);
textLine.FinalizeLine();
textLine.FinalizeLine();
return textLine;
}
@ -236,49 +236,49 @@ namespace Avalonia.Media.TextFormatting
switch (currentRun)
{
case UnshapedTextRun shapeableRun:
{
groupedRuns.Clear();
groupedRuns.Add(shapeableRun);
{
groupedRuns.Clear();
groupedRuns.Add(shapeableRun);
var text = shapeableRun.Text;
var properties = shapeableRun.Properties;
var text = shapeableRun.Text;
var properties = shapeableRun.Properties;
while (index + 1 < processedRuns.Count)
{
if (processedRuns[index + 1] is not UnshapedTextRun nextRun)
while (index + 1 < processedRuns.Count)
{
if (processedRuns[index + 1] is not UnshapedTextRun nextRun)
{
break;
}
if (shapeableRun.BidiLevel == nextRun.BidiLevel
&& TryJoinContiguousMemories(text, nextRun.Text, out var joinedText)
&& CanShapeTogether(properties, nextRun.Properties))
{
groupedRuns.Add(nextRun);
index++;
shapeableRun = nextRun;
text = joinedText;
continue;
}
break;
}
if (shapeableRun.BidiLevel == nextRun.BidiLevel
&& TryJoinContiguousMemories(text, nextRun.Text, out var joinedText)
&& CanShapeTogether(properties, nextRun.Properties))
{
groupedRuns.Add(nextRun);
index++;
shapeableRun = nextRun;
text = joinedText;
continue;
}
var shaperOptions = new TextShaperOptions(
properties.CachedGlyphTypeface,
properties.FontRenderingEmSize, shapeableRun.BidiLevel, properties.CultureInfo,
paragraphProperties.DefaultIncrementalTab, paragraphProperties.LetterSpacing);
ShapeTogether(groupedRuns, text, shaperOptions, textShaper, shapedRuns);
break;
}
var shaperOptions = new TextShaperOptions(
properties.CachedGlyphTypeface,
properties.FontRenderingEmSize, shapeableRun.BidiLevel, properties.CultureInfo,
paragraphProperties.DefaultIncrementalTab, paragraphProperties.LetterSpacing);
ShapeTogether(groupedRuns, text, shaperOptions, textShaper, shapedRuns);
break;
}
default:
{
shapedRuns.Add(currentRun);
{
shapedRuns.Add(currentRun);
break;
}
break;
}
}
}
}
@ -699,7 +699,7 @@ namespace Avalonia.Media.TextFormatting
switch (currentRun)
{
case ShapedTextRun:
{
{
var lineBreaker = new LineBreakEnumerator(currentRun.Text.Span);
while (lineBreaker.MoveNext(out var lineBreak))
@ -741,7 +741,7 @@ namespace Avalonia.Media.TextFormatting
break;
}
while (lineBreaker.MoveNext(out lineBreak) && index < textRuns.Count)
while (lineBreaker.MoveNext(out lineBreak))
{
currentPosition += lineBreak.PositionWrap;
@ -767,6 +767,11 @@ namespace Avalonia.Media.TextFormatting
currentPosition = currentLength + lineBreak.PositionWrap;
}
if (currentPosition == 0 && measuredLength > 0)
{
currentPosition = measuredLength;
}
breakFound = true;
break;

14
src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs

@ -172,9 +172,21 @@ namespace Avalonia.Media.TextFormatting
distance -= Start;
var firstRunIndex = 0;
if (_textRuns[firstRunIndex] is TextEndOfLine)
{
firstRunIndex++;
}
if(firstRunIndex >= _textRuns.Length)
{
return new CharacterHit(FirstTextSourceIndex);
}
if (distance <= 0)
{
var firstRun = _textRuns[0];
var firstRun = _textRuns[firstRunIndex];
return GetRunCharacterHit(firstRun, FirstTextSourceIndex, 0);
}

29
src/Avalonia.Controls/TreeViewItem.cs

@ -1,11 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Avalonia.Controls.Generators;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Mixins;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Data;
using Avalonia.Input;
using Avalonia.LogicalTree;
using Avalonia.Threading;
@ -22,11 +22,10 @@ namespace Avalonia.Controls
/// <summary>
/// Defines the <see cref="IsExpanded"/> property.
/// </summary>
public static readonly DirectProperty<TreeViewItem, bool> IsExpandedProperty =
AvaloniaProperty.RegisterDirect<TreeViewItem, bool>(
public static readonly StyledProperty<bool> IsExpandedProperty =
AvaloniaProperty.Register<TreeViewItem, bool>(
nameof(IsExpanded),
o => o.IsExpanded,
(o, v) => o.IsExpanded = v);
defaultBindingMode: BindingMode.TwoWay);
/// <summary>
/// Defines the <see cref="IsSelected"/> property.
@ -46,7 +45,6 @@ namespace Avalonia.Controls
private TreeView? _treeView;
private Control? _header;
private bool _isExpanded;
private int _level;
private bool _templateApplied;
private bool _deferredBringIntoViewFlag;
@ -68,8 +66,8 @@ namespace Avalonia.Controls
/// </summary>
public bool IsExpanded
{
get { return _isExpanded; }
set { SetAndRaise(IsExpandedProperty, ref _isExpanded, value); }
get => GetValue(IsExpandedProperty);
set => SetValue(IsExpandedProperty, value);
}
/// <summary>
@ -77,8 +75,8 @@ namespace Avalonia.Controls
/// </summary>
public bool IsSelected
{
get { return GetValue(IsSelectedProperty); }
set { SetValue(IsSelectedProperty, value); }
get => GetValue(IsSelectedProperty);
set => SetValue(IsSelectedProperty, value);
}
/// <summary>
@ -86,8 +84,8 @@ namespace Avalonia.Controls
/// </summary>
public int Level
{
get { return _level; }
private set { SetAndRaise(LevelProperty, ref _level, value); }
get => _level;
private set => SetAndRaise(LevelProperty, ref _level, value);
}
internal TreeView? TreeViewOwner => _treeView;
@ -115,11 +113,6 @@ namespace Avalonia.Controls
}
}
protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e)
{
base.OnDetachedFromLogicalTree(e);
}
protected virtual void OnRequestBringIntoView(RequestBringIntoViewEventArgs e)
{
if (e.TargetObject == this)
@ -266,7 +259,7 @@ namespace Avalonia.Controls
}
/// <summary>
/// Invoked when the <see cref="DoubleTapped"/> event occurs in the header.
/// Invoked when the <see cref="InputElement.DoubleTapped"/> event occurs in the header.
/// </summary>
protected virtual void OnHeaderDoubleTapped(TappedEventArgs e)
{

1
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs

@ -49,6 +49,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
InsertBefore<ContentConvertTransformer>(
new AvaloniaXamlIlControlThemeTransformer(),
new AvaloniaXamlIlSelectorTransformer(),
new AvaloniaXamlIlDuplicateSettersChecker(),
new AvaloniaXamlIlControlTemplateTargetTypeMetadataTransformer(),
new AvaloniaXamlIlBindingPathParser(),
new AvaloniaXamlIlPropertyPathTransformer(),

3
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlControlThemeTransformer.cs

@ -11,7 +11,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
{
public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node)
{
if (!(node is XamlAstObjectNode on && on.Type.GetClrType().FullName == "Avalonia.Styling.ControlTheme"))
if (node is not XamlAstObjectNode on ||
!context.GetAvaloniaTypes().ControlTheme.IsAssignableFrom(on.Type.GetClrType()))
return node;
// Check if we've already transformed this node.

45
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDuplicateSettersChecker.cs

@ -0,0 +1,45 @@
using System.Collections.Generic;
using System.Linq;
using XamlX;
using XamlX.Ast;
using XamlX.Transform;
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers;
class AvaloniaXamlIlDuplicateSettersChecker : IXamlAstTransformer
{
public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node)
{
if (node is not XamlAstObjectNode objectNode)
{
return node;
}
var nodeType = objectNode.Type.GetClrType();
if (!context.GetAvaloniaTypes().Style.IsAssignableFrom(nodeType) &&
!context.GetAvaloniaTypes().ControlTheme.IsAssignableFrom(nodeType))
{
return node;
}
var properties = objectNode.Children
.OfType<XamlAstObjectNode>()
.Where(n => n.Type.GetClrType().Name == "Setter")
.SelectMany(setter =>
setter.Children.OfType<XamlAstXamlPropertyValueNode>()
.Where(c => c.Property.GetClrProperty().Name == "Property"))
.Select(p => p.Values[0])
.OfType<XamlAstTextNode>()
.Select(x => x.Text);
var index = new HashSet<string>();
foreach (var property in properties)
{
if (!index.Add(property))
{
throw new XamlParseException($"Duplicate setter encountered for property '{property}'", node);
}
}
return node;
}
}

3
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSelectorTransformer.cs

@ -19,7 +19,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
{
public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node)
{
if (!(node is XamlAstObjectNode on && on.Type.GetClrType().FullName == "Avalonia.Styling.Style"))
if (node is not XamlAstObjectNode on ||
!context.GetAvaloniaTypes().Style.IsAssignableFrom(on.Type.GetClrType()))
return node;
var pn = on.Children.OfType<XamlAstXamlPropertyValueNode>()

4
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs

@ -112,6 +112,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
public IXamlMethod ResourceDictionaryDeferredAdd { get; }
public IXamlType UriKind { get; }
public IXamlConstructor UriConstructor { get; }
public IXamlType Style { get; }
public IXamlType ControlTheme { get; }
public AvaloniaXamlIlWellKnownTypes(TransformerConfiguration cfg)
{
@ -250,6 +252,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
XamlIlTypes.Object));
UriKind = cfg.TypeSystem.GetType("System.UriKind");
UriConstructor = Uri.GetConstructor(new List<IXamlType>() { cfg.WellKnownTypes.String, UriKind });
Style = cfg.TypeSystem.GetType("Avalonia.Styling.Style");
ControlTheme = cfg.TypeSystem.GetType("Avalonia.Styling.ControlTheme");
}
}

41
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlTests.cs

@ -334,6 +334,47 @@ namespace Avalonia.Markup.Xaml.UnitTests
var parsed = (Button)AvaloniaRuntimeXamlLoader.Load(document);
Assert.Equal(Colors.Blue, ((ISolidColorBrush)parsed.Background!).Color);
}
[Fact]
public void Style_Parser_Throws_For_Duplicate_Setter()
{
var xaml = @"
<Window xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
xmlns:local='clr-namespace:Avalonia.Markup.Xaml.UnitTests.Xaml;assembly=Avalonia.Markup.Xaml.UnitTests'>
<Window.Styles>
<Style Selector='TextBlock'>
<Setter Property='Width' Value='100'/>
<Setter Property='Height' Value='20'/>
<Setter Property='Height' Value='30'/>
</Style>
</Window.Styles>
<TextBlock/>
</Window>";
AssertThrows(() => AvaloniaRuntimeXamlLoader.Load(xaml, typeof(XamlIlTests).Assembly, designMode: true),
e => e.Message.StartsWith("Duplicate setter encountered for property 'Height'"));
}
[Fact]
public void Control_Theme_Parser_Throws_For_Duplicate_Setter()
{
var xaml = @"
<Window xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
xmlns:u='using:Avalonia.Markup.Xaml.UnitTests.Xaml'>
<Window.Resources>
<ControlTheme x:Key='MyTheme' TargetType='u:TestTemplatedControl'>
<Setter Property='Width' Value='100'/>
<Setter Property='Height' Value='20'/>
<Setter Property='Height' Value='30'/>
</ControlTheme>
</Window.Resources>
<u:TestTemplatedControl Theme='{StaticResource MyTheme}'/>
</Window>";
AssertThrows(() => AvaloniaRuntimeXamlLoader.Load(xaml, typeof(XamlIlTests).Assembly, designMode: true),
e => e.Message.StartsWith("Duplicate setter encountered for property 'Height'"));
}
}
public class XamlIlBugTestsEventHandlerCodeBehind : Window

8
tests/Avalonia.Skia.UnitTests/Media/TextFormatting/SingleBufferTextSource.cs

@ -7,25 +7,27 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
{
private readonly string _text;
private readonly GenericTextRunProperties _defaultGenericPropertiesRunProperties;
private readonly bool _addEndOfParagraph;
public SingleBufferTextSource(string text, GenericTextRunProperties defaultProperties)
public SingleBufferTextSource(string text, GenericTextRunProperties defaultProperties, bool addEndOfParagraph = false)
{
_text = text;
_defaultGenericPropertiesRunProperties = defaultProperties;
_addEndOfParagraph = addEndOfParagraph;
}
public TextRun GetTextRun(int textSourceIndex)
{
if (textSourceIndex >= _text.Length)
{
return null;
return _addEndOfParagraph ? new TextEndOfParagraph() : null;
}
var runText = _text.AsMemory(textSourceIndex);
if (runText.IsEmpty)
{
return null;
return _addEndOfParagraph ? new TextEndOfParagraph() : null;
}
return new TextCharacters(runText, _defaultGenericPropertiesRunProperties);

11
tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs

@ -242,10 +242,16 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
{
var defaultProperties = new GenericTextRunProperties(Typeface.Default);
var textSource = new SingleBufferTextSource(text, defaultProperties);
var paragraphProperties = new GenericTextParagraphProperties(defaultProperties, textWrap: TextWrapping.WrapWithOverflow);
var textSource = new SingleBufferTextSource("ABCDEFHFFHFJHKHFK", defaultProperties, true);
var formatter = new TextFormatterImpl();
var line = formatter.FormatLine(textSource, 0, 33, paragraphProperties);
textSource = new SingleBufferTextSource(text, defaultProperties);
var numberOfLines = 0;
var currentPosition = 0;
@ -253,8 +259,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
while (currentPosition < text.Length)
{
var textLine =
formatter.FormatLine(textSource, currentPosition, 1,
new GenericTextParagraphProperties(defaultProperties, textWrap: TextWrapping.WrapWithOverflow));
formatter.FormatLine(textSource, currentPosition, 1, paragraphProperties);
if (text.Length - currentPosition > expectedCharactersPerLine)
{

Loading…
Cancel
Save