Browse Source

Merge branch 'master' into xaml-group-transfomers

pull/9537/head
Jumar Macato 3 years ago
committed by GitHub
parent
commit
f886a13de7
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      src/Avalonia.Base/Rendering/Composition/Animations/KeyFrameAnimationInstance.cs
  2. 20
      src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs
  3. 3
      src/Avalonia.Base/Rendering/Composition/Compositor.Factories.cs
  4. 21
      src/Avalonia.Base/Rendering/Composition/ElementCompositionPreview.cs
  5. 11
      src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionSolidColorVisual.cs
  6. 6
      src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs
  7. 29
      src/Avalonia.Base/Threading/DispatcherPriority.cs
  8. 8
      src/Avalonia.Base/Visual.cs
  9. 3
      src/Avalonia.Base/composition-schema.xml
  10. 14
      src/Avalonia.Controls/Border.cs
  11. 76
      src/Avalonia.Controls/BorderVisual.cs

5
src/Avalonia.Base/Rendering/Composition/Animations/KeyFrameAnimationInstance.cs

@ -128,8 +128,11 @@ namespace Avalonia.Rendering.Composition.Animations
left = kf; left = kf;
right = _keyFrames[c + 1]; right = _keyFrames[c + 1];
break;
} }
else if (c == 0)
return ExpressionVariant.Create(GetKeyFrame(ref ctx, kf));
else
break;
} }
var keyProgress = Math.Max(0, Math.Min(1, (iterationProgress - left.Key) / (right.Key - left.Key))); var keyProgress = Math.Max(0, Math.Min(1, (iterationProgress - left.Key) / (right.Key - left.Key)));

20
src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs

@ -146,8 +146,18 @@ public class CompositingRenderer : IRendererWithCompositor
return result == 0 ? lhs.index.CompareTo(rhs.index) : result; return result == 0 ? lhs.index.CompareTo(rhs.index) : result;
}); });
} }
if (compositionChildren.Count == visualChildren.Count) var childVisual = v.ChildCompositionVisual;
// Check if the current visual somehow got migrated to another compositor
if (childVisual != null && childVisual.Compositor != v.CompositionVisual.Compositor)
childVisual = null;
var expectedCount = visualChildren.Count;
if (childVisual != null)
expectedCount++;
if (compositionChildren.Count == expectedCount)
{ {
bool mismatch = false; bool mismatch = false;
if (sortedChildren != null) if (sortedChildren != null)
@ -167,6 +177,9 @@ public class CompositingRenderer : IRendererWithCompositor
break; break;
} }
if (childVisual != null &&
!ReferenceEquals(compositionChildren[compositionChildren.Count - 1], childVisual))
mismatch = true;
if (!mismatch) if (!mismatch)
{ {
@ -193,6 +206,9 @@ public class CompositingRenderer : IRendererWithCompositor
if (compositionChild != null) if (compositionChild != null)
compositionChildren.Add(compositionChild); compositionChildren.Add(compositionChild);
} }
if (childVisual != null)
compositionChildren.Add(childVisual);
} }
private void UpdateCore() private void UpdateCore()

3
src/Avalonia.Base/Rendering/Composition/Compositor.Factories.cs

@ -29,4 +29,7 @@ public partial class Compositor
public ImplicitAnimationCollection CreateImplicitAnimationCollection() => new ImplicitAnimationCollection(this); public ImplicitAnimationCollection CreateImplicitAnimationCollection() => new ImplicitAnimationCollection(this);
public CompositionAnimationGroup CreateAnimationGroup() => new CompositionAnimationGroup(this); public CompositionAnimationGroup CreateAnimationGroup() => new CompositionAnimationGroup(this);
public CompositionSolidColorVisual CreateSolidColorVisual() =>
new(this, new ServerCompositionSolidColorVisual(Server));
} }

21
src/Avalonia.Base/Rendering/Composition/ElementCompositionPreview.cs

@ -1,5 +1,8 @@
// Special license applies <see href="https://raw.githubusercontent.com/AvaloniaUI/Avalonia/master/src/Avalonia.Base/Rendering/Composition/License.md">License.md</see> // Special license applies <see href="https://raw.githubusercontent.com/AvaloniaUI/Avalonia/master/src/Avalonia.Base/Rendering/Composition/License.md">License.md</see>
using System;
using Avalonia.VisualTree;
namespace Avalonia.Rendering.Composition; namespace Avalonia.Rendering.Composition;
/// <summary> /// <summary>
@ -13,4 +16,22 @@ public static class ElementComposition
/// <param name="visual"></param> /// <param name="visual"></param>
/// <returns></returns> /// <returns></returns>
public static CompositionVisual? GetElementVisual(Visual visual) => visual.CompositionVisual; public static CompositionVisual? GetElementVisual(Visual visual) => visual.CompositionVisual;
/// <summary>
/// Sets a custom <see cref="CompositionVisual"/> as the last child of the element’s visual tree.
/// </summary>
public static void SetElementChildVisual(Visual visual, CompositionVisual? compositionVisual)
{
if (compositionVisual != null && visual.CompositionVisual != null &&
compositionVisual.Compositor != visual.CompositionVisual.Compositor)
throw new InvalidOperationException("Composition visuals belong to different compositor instances");
visual.ChildCompositionVisual = compositionVisual;
visual.GetVisualRoot()?.Renderer.RecalculateChildren(visual);
}
/// <summary>
/// Retrieves a <see cref="CompositionVisual"/> object previously set by a call to <see cref="SetElementChildVisual" />.
/// </summary>
public static CompositionVisual? GetElementChildVisual(Visual visual) => visual.ChildCompositionVisual;
} }

11
src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionSolidColorVisual.cs

@ -0,0 +1,11 @@
using Avalonia.Media.Immutable;
namespace Avalonia.Rendering.Composition.Server;
internal partial class ServerCompositionSolidColorVisual
{
protected override void RenderCore(CompositorDrawingContextProxy canvas, Rect currentTransformedClip)
{
canvas.DrawRectangle(new ImmutableSolidColorBrush(Color), null, new Rect(0, 0, Size.X, Size.Y));
}
}

6
src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs

@ -48,7 +48,7 @@ namespace Avalonia.Rendering.Composition.Server
if (Opacity != 1) if (Opacity != 1)
canvas.PushOpacity(Opacity); canvas.PushOpacity(Opacity);
var boundsRect = new Rect(new Size(Size.X, Size.Y)); var boundsRect = new Rect(new Size(Size.X, Size.Y));
if(ClipToBounds) if (ClipToBounds && !HandlesClipToBounds)
canvas.PushClip(Root!.SnapToDevicePixels(boundsRect)); canvas.PushClip(Root!.SnapToDevicePixels(boundsRect));
if (Clip != null) if (Clip != null)
canvas.PushGeometryClip(Clip); canvas.PushGeometryClip(Clip);
@ -65,11 +65,13 @@ namespace Avalonia.Rendering.Composition.Server
canvas.PopOpacityMask(); canvas.PopOpacityMask();
if (Clip != null) if (Clip != null)
canvas.PopGeometryClip(); canvas.PopGeometryClip();
if (ClipToBounds) if (ClipToBounds && !HandlesClipToBounds)
canvas.PopClip(); canvas.PopClip();
if(Opacity != 1) if(Opacity != 1)
canvas.PopOpacity(); canvas.PopOpacity();
} }
protected virtual bool HandlesClipToBounds => false;
private ReadbackData _readback0, _readback1, _readback2; private ReadbackData _readback0, _readback1, _readback2;

29
src/Avalonia.Base/Threading/DispatcherPriority.cs

@ -45,32 +45,37 @@ namespace Avalonia.Threading
/// <summary> /// <summary>
/// The job will be processed after other non-idle operations have completed. /// The job will be processed after other non-idle operations have completed.
/// </summary> /// </summary>
public static readonly DispatcherPriority Background = new(1); public static readonly DispatcherPriority Background = new(MinValue + 1);
/// <summary> /// <summary>
/// The job will be processed with the same priority as input. /// The job will be processed with the same priority as input.
/// </summary> /// </summary>
public static readonly DispatcherPriority Input = new(2); public static readonly DispatcherPriority Input = new(Background + 1);
/// <summary> /// <summary>
/// The job will be processed after layout and render but before input. /// The job will be processed after layout and render but before input.
/// </summary> /// </summary>
public static readonly DispatcherPriority Loaded = new(3); public static readonly DispatcherPriority Loaded = new(Input + 1);
/// <summary> /// <summary>
/// The job will be processed with the same priority as render. /// The job will be processed with the same priority as render.
/// </summary> /// </summary>
public static readonly DispatcherPriority Render = new(5); public static readonly DispatcherPriority Render = new(Loaded + 1);
/// <summary> /// <summary>
/// The job will be processed with the same priority as composition updates. /// The job will be processed with the same priority as composition updates.
/// </summary> /// </summary>
public static readonly DispatcherPriority Composition = new(6); public static readonly DispatcherPriority Composition = new(Render + 1);
/// <summary> /// <summary>
/// The job will be processed with the same priority as render. /// The job will be processed with before composition updates.
/// </summary>
public static readonly DispatcherPriority PreComposition = new(Composition + 1);
/// <summary>
/// The job will be processed with the same priority as layout.
/// </summary> /// </summary>
public static readonly DispatcherPriority Layout = new(7); public static readonly DispatcherPriority Layout = new(PreComposition + 1);
/// <summary> /// <summary>
/// The job will be processed with the same priority as data binding. /// The job will be processed with the same priority as data binding.
@ -80,7 +85,7 @@ namespace Avalonia.Threading
/// <summary> /// <summary>
/// The job will be processed before other asynchronous operations. /// The job will be processed before other asynchronous operations.
/// </summary> /// </summary>
public static readonly DispatcherPriority Send = new(8); public static readonly DispatcherPriority Send = new(Layout + 1);
/// <summary> /// <summary>
/// Maximum possible priority /// Maximum possible priority
@ -123,4 +128,4 @@ namespace Avalonia.Threading
/// <inheritdoc /> /// <inheritdoc />
public int CompareTo(DispatcherPriority other) => Value.CompareTo(other.Value); public int CompareTo(DispatcherPriority other) => Value.CompareTo(other.Value);
} }
} }

8
src/Avalonia.Base/Visual.cs

@ -292,6 +292,7 @@ namespace Avalonia
protected IRenderRoot? VisualRoot => _visualRoot ?? (this as IRenderRoot); protected IRenderRoot? VisualRoot => _visualRoot ?? (this as IRenderRoot);
internal CompositionDrawListVisual? CompositionVisual { get; private set; } internal CompositionDrawListVisual? CompositionVisual { get; private set; }
internal CompositionVisual? ChildCompositionVisual { get; set; }
public bool HasNonUniformZIndexChildren { get; private set; } public bool HasNonUniformZIndexChildren { get; private set; }
@ -452,12 +453,15 @@ namespace Avalonia
} }
} }
private protected virtual CompositionDrawListVisual CreateCompositionVisual(Compositor compositor)
=> new CompositionDrawListVisual(compositor,
new ServerCompositionDrawListVisual(compositor.Server, this), this);
internal CompositionVisual AttachToCompositor(Compositor compositor) internal CompositionVisual AttachToCompositor(Compositor compositor)
{ {
if (CompositionVisual == null || CompositionVisual.Compositor != compositor) if (CompositionVisual == null || CompositionVisual.Compositor != compositor)
{ {
CompositionVisual = new CompositionDrawListVisual(compositor, CompositionVisual = CreateCompositionVisual(compositor);
new ServerCompositionDrawListVisual(compositor.Server, this), this);
} }
return CompositionVisual; return CompositionVisual;

3
src/Avalonia.Base/composition-schema.xml

@ -27,6 +27,9 @@
<Property Name="OpacityMaskBrush" Type="Avalonia.Media.IBrush?" Internal="true" /> <Property Name="OpacityMaskBrush" Type="Avalonia.Media.IBrush?" Internal="true" />
</Object> </Object>
<Object Name="CompositionContainerVisual" Inherits="CompositionVisual"/> <Object Name="CompositionContainerVisual" Inherits="CompositionVisual"/>
<Object Name="CompositionSolidColorVisual" Inherits="CompositionContainerVisual">
<Property Name="Color" Type="Avalonia.Media.Color" Animated="true" />
</Object>
<List Name="CompositionVisualCollection" ItemType="CompositionVisual" CustomCtor="true"/> <List Name="CompositionVisualCollection" ItemType="CompositionVisual" CustomCtor="true"/>
<Object Name="CompositionTarget" CustomServerCtor="true"> <Object Name="CompositionTarget" CustomServerCtor="true">
<Property Name="Root" Type="CompositionVisual?"/> <Property Name="Root" Type="CompositionVisual?"/>

14
src/Avalonia.Controls/Border.cs

@ -4,6 +4,7 @@ using Avalonia.Controls.Shapes;
using Avalonia.Controls.Utils; using Avalonia.Controls.Utils;
using Avalonia.Layout; using Avalonia.Layout;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Rendering.Composition;
using Avalonia.Utilities; using Avalonia.Utilities;
using Avalonia.VisualTree; using Avalonia.VisualTree;
@ -73,6 +74,7 @@ namespace Avalonia.Controls
private readonly BorderRenderHelper _borderRenderHelper = new BorderRenderHelper(); private readonly BorderRenderHelper _borderRenderHelper = new BorderRenderHelper();
private Thickness? _layoutThickness; private Thickness? _layoutThickness;
private double _scale; private double _scale;
private CompositionBorderVisual? _borderVisual;
/// <summary> /// <summary>
/// Initializes static members of the <see cref="Border"/> class. /// Initializes static members of the <see cref="Border"/> class.
@ -101,6 +103,10 @@ namespace Avalonia.Controls
case nameof(BorderThickness): case nameof(BorderThickness):
_layoutThickness = null; _layoutThickness = null;
break; break;
case nameof(CornerRadius):
if (_borderVisual != null)
_borderVisual.CornerRadius = CornerRadius;
break;
} }
} }
@ -245,6 +251,14 @@ namespace Avalonia.Controls
return LayoutHelper.ArrangeChild(Child, finalSize, Padding, BorderThickness); return LayoutHelper.ArrangeChild(Child, finalSize, Padding, BorderThickness);
} }
private protected override CompositionDrawListVisual CreateCompositionVisual(Compositor compositor)
{
return _borderVisual = new CompositionBorderVisual(compositor, this)
{
CornerRadius = CornerRadius
};
}
public CornerRadius ClipToBoundsRadius => CornerRadius; public CornerRadius ClipToBoundsRadius => CornerRadius;
} }
} }

76
src/Avalonia.Controls/BorderVisual.cs

@ -0,0 +1,76 @@
using System;
using Avalonia.Rendering.Composition;
using Avalonia.Rendering.Composition.Server;
using Avalonia.Rendering.Composition.Transport;
using Avalonia.Rendering.SceneGraph;
namespace Avalonia.Controls;
class CompositionBorderVisual : CompositionDrawListVisual
{
private CornerRadius _cornerRadius;
private bool _cornerRadiusChanged;
public CompositionBorderVisual(Compositor compositor, Visual visual) : base(compositor,
new ServerBorderVisual(compositor.Server, visual), visual)
{
}
public CornerRadius CornerRadius
{
get => _cornerRadius;
set
{
if (_cornerRadius != value)
{
_cornerRadiusChanged = true;
_cornerRadius = value;
RegisterForSerialization();
}
}
}
private protected override void SerializeChangesCore(BatchStreamWriter writer)
{
base.SerializeChangesCore(writer);
writer.Write(_cornerRadiusChanged);
if (_cornerRadiusChanged)
writer.Write(_cornerRadius);
}
class ServerBorderVisual : ServerCompositionDrawListVisual
{
private CornerRadius _cornerRadius;
public ServerBorderVisual(ServerCompositor compositor, Visual v) : base(compositor, v)
{
}
protected override void RenderCore(CompositorDrawingContextProxy canvas, Rect currentTransformedClip)
{
if (ClipToBounds)
{
var clipRect = Root!.SnapToDevicePixels(new Rect(new Size(Size.X, Size.Y)));
if (_cornerRadius.IsEmpty)
canvas.PushClip(clipRect);
else
canvas.PushClip(new RoundedRect(clipRect, _cornerRadius));
}
base.RenderCore(canvas, currentTransformedClip);
if(ClipToBounds)
canvas.PopClip();
}
protected override void DeserializeChangesCore(BatchStreamReader reader, TimeSpan commitedAt)
{
base.DeserializeChangesCore(reader, commitedAt);
if (reader.Read<bool>())
_cornerRadius = reader.Read<CornerRadius>();
}
protected override bool HandlesClipToBounds => true;
}
}
Loading…
Cancel
Save