diff --git a/src/Avalonia.Controls/Templates/DataTemplateExtensions.cs b/src/Avalonia.Controls/Templates/DataTemplateExtensions.cs
index f6e28483fd..2b115aec7e 100644
--- a/src/Avalonia.Controls/Templates/DataTemplateExtensions.cs
+++ b/src/Avalonia.Controls/Templates/DataTemplateExtensions.cs
@@ -1,6 +1,7 @@
-using System.Linq;
using Avalonia.LogicalTree;
+#nullable enable
+
namespace Avalonia.Controls.Templates
{
///
@@ -18,21 +19,23 @@ namespace Avalonia.Controls.Templates
/// tree are searched.
///
/// The data template or null if no matching data template was found.
- public static IDataTemplate FindDataTemplate(
+ public static IDataTemplate? FindDataTemplate(
this IControl control,
object data,
- IDataTemplate primary = null)
+ IDataTemplate? primary = null)
{
if (primary?.Match(data) == true)
{
return primary;
}
- foreach (var i in control.GetSelfAndLogicalAncestors().OfType())
+ var currentTemplateHost = control as ILogical;
+
+ while (currentTemplateHost != null)
{
- if (i.IsDataTemplatesInitialized)
+ if (currentTemplateHost is IDataTemplateHost hostCandidate && hostCandidate.IsDataTemplatesInitialized)
{
- foreach (IDataTemplate dt in i.DataTemplates)
+ foreach (IDataTemplate dt in hostCandidate.DataTemplates)
{
if (dt.Match(data))
{
@@ -40,20 +43,19 @@ namespace Avalonia.Controls.Templates
}
}
}
+
+ currentTemplateHost = currentTemplateHost.LogicalParent;
}
IGlobalDataTemplates global = AvaloniaLocator.Current.GetService();
- if (global != null)
+ if (global != null && global.IsDataTemplatesInitialized)
{
- if (global.IsDataTemplatesInitialized)
+ foreach (IDataTemplate dt in global.DataTemplates)
{
- foreach (IDataTemplate dt in global.DataTemplates)
+ if (dt.Match(data))
{
- if (dt.Match(data))
- {
- return dt;
- }
+ return dt;
}
}
}
diff --git a/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs b/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs
index 15e14935ca..7a1a2cb4a3 100644
--- a/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs
+++ b/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs
@@ -99,6 +99,11 @@ namespace Avalonia.Rendering
///
public string DebugFramesPath { get; set; }
+ ///
+ /// Forces the renderer to only draw frames on the render thread. Makes Paint to wait until frame is rendered
+ ///
+ public bool RenderOnlyOnRenderThread { get; set; } = true;
+
///
public event EventHandler SceneInvalidated;
@@ -180,11 +185,38 @@ namespace Avalonia.Rendering
///
public void Paint(Rect rect)
{
- var t = (IRenderLoopTask)this;
- if(t.NeedsUpdate)
- UpdateScene();
- if(_scene?.Item != null)
- Render(true);
+ if (RenderOnlyOnRenderThread)
+ {
+ while (true)
+ {
+ Scene scene;
+ bool? updated;
+ lock (_sceneLock)
+ {
+ updated = UpdateScene();
+ scene = _scene?.Item;
+ }
+
+ // Renderer is in invalid state, skip drawing
+ if(updated == null)
+ return;
+
+ // Wait for the scene to be rendered or disposed
+ scene?.Rendered.Wait();
+
+ // That was an up-to-date scene, we can return immediately
+ if (updated == true)
+ return;
+ }
+ }
+ else
+ {
+ var t = (IRenderLoopTask)this;
+ if (t.NeedsUpdate)
+ UpdateScene();
+ if (_scene?.Item != null)
+ Render(true);
+ }
}
///
@@ -270,13 +302,20 @@ namespace Avalonia.Rendering
{
if (scene?.Item != null)
{
- var overlay = DrawDirtyRects || DrawFps;
- if (DrawDirtyRects)
- _dirtyRectsDisplay.Tick();
- if (overlay)
- RenderOverlay(scene.Item, ref context);
- if (updated || forceComposite || overlay)
- RenderComposite(scene.Item, ref context);
+ try
+ {
+ var overlay = DrawDirtyRects || DrawFps;
+ if (DrawDirtyRects)
+ _dirtyRectsDisplay.Tick();
+ if (overlay)
+ RenderOverlay(scene.Item, ref context);
+ if (updated || forceComposite || overlay)
+ RenderComposite(scene.Item, ref context);
+ }
+ finally
+ {
+ scene.Item.MarkAsRendered();
+ }
}
}
}
@@ -559,15 +598,15 @@ namespace Avalonia.Rendering
UpdateScene();
}
- private void UpdateScene()
+ private bool? UpdateScene()
{
Dispatcher.UIThread.VerifyAccess();
lock (_sceneLock)
{
if (_disposed)
- return;
+ return null;
if (_scene?.Item.Generation > _lastSceneId)
- return;
+ return false;
}
if (_root.IsVisible)
{
@@ -619,6 +658,8 @@ namespace Avalonia.Rendering
SceneInvalidated(this, new SceneInvalidatedEventArgs((IRenderRoot)_root, rect));
}
+
+ return true;
}
else
{
@@ -628,6 +669,8 @@ namespace Avalonia.Rendering
_scene = null;
oldScene?.Dispose();
}
+
+ return null;
}
}
diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/Scene.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/Scene.cs
index 4f5c97cdff..6a4c532d4a 100644
--- a/src/Avalonia.Visuals/Rendering/SceneGraph/Scene.cs
+++ b/src/Avalonia.Visuals/Rendering/SceneGraph/Scene.cs
@@ -2,6 +2,7 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
+using System.Threading.Tasks;
using Avalonia.Collections.Pooled;
using Avalonia.VisualTree;
@@ -13,6 +14,7 @@ namespace Avalonia.Rendering.SceneGraph
public class Scene : IDisposable
{
private readonly Dictionary _index;
+ private readonly TaskCompletionSource _rendered = new TaskCompletionSource();
///
/// Initializes a new instance of the class.
@@ -41,6 +43,8 @@ namespace Avalonia.Rendering.SceneGraph
root.LayerRoot = root.Visual;
}
+ public Task Rendered => _rendered.Task;
+
///
/// Gets a value identifying the scene's generation. This is incremented each time the scene is cloned.
///
@@ -97,6 +101,7 @@ namespace Avalonia.Rendering.SceneGraph
public void Dispose()
{
+ _rendered.TrySetResult(false);
foreach (var node in _index.Values)
{
node.Dispose();
@@ -340,5 +345,7 @@ namespace Avalonia.Rendering.SceneGraph
}
}
}
+
+ public void MarkAsRendered() => _rendered.TrySetResult(true);
}
}
diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs
index 2cd3b973d8..41c061613d 100644
--- a/src/Avalonia.X11/X11Window.cs
+++ b/src/Avalonia.X11/X11Window.cs
@@ -189,6 +189,11 @@ namespace Avalonia.X11
if (platform.Options.UseDBusMenu)
NativeMenuExporter = DBusMenuExporter.TryCreate(_handle);
NativeControlHost = new X11NativeControlHost(_platform, this);
+ DispatcherTimer.Run(() =>
+ {
+ Paint?.Invoke(default);
+ return _handle != IntPtr.Zero;
+ }, TimeSpan.FromMilliseconds(100));
}
class SurfaceInfo : EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo
@@ -338,7 +343,10 @@ namespace Avalonia.X11
return customRendererFactory.Create(root, loop);
return _platform.Options.UseDeferredRendering ?
- new DeferredRenderer(root, loop) :
+ new DeferredRenderer(root, loop)
+ {
+ RenderOnlyOnRenderThread = true
+ } :
(IRenderer)new X11ImmediateRendererProxy(root, loop);
}
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AstNodes/AvaloniaXamlIlFontFamilyAstNode.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AstNodes/AvaloniaXamlIlFontFamilyAstNode.cs
new file mode 100644
index 0000000000..1f50e661d8
--- /dev/null
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AstNodes/AvaloniaXamlIlFontFamilyAstNode.cs
@@ -0,0 +1,39 @@
+using System;
+using System.Collections.Generic;
+using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers;
+using XamlX.Ast;
+using XamlX.Emit;
+using XamlX.IL;
+using XamlX.TypeSystem;
+
+namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.AstNodes
+{
+ class AvaloniaXamlIlFontFamilyAstNode: XamlAstNode, IXamlAstValueNode, IXamlAstILEmitableNode
+ {
+ private readonly AvaloniaXamlIlWellKnownTypes _types;
+ private readonly string _text;
+
+ public IXamlAstTypeReference Type { get; }
+
+ public AvaloniaXamlIlFontFamilyAstNode(AvaloniaXamlIlWellKnownTypes types,
+ string text,
+ IXamlLineInfo lineInfo) : base(lineInfo)
+ {
+ _types = types;
+ _text = text;
+ Type = new XamlAstClrTypeReference(lineInfo, types.FontFamily, false);
+ }
+
+ public XamlILNodeEmitResult Emit(XamlEmitContext context, IXamlILEmitter codeGen)
+ {
+ codeGen
+ .Ldloc(context.ContextLocal)
+ .Castclass(context.Configuration.TypeMappings.UriContextProvider)
+ .EmitCall(context.Configuration.TypeMappings.UriContextProvider.FindMethod(
+ "get_BaseUri", _types.Uri, false))
+ .Ldstr(_text)
+ .Newobj(_types.FontFamilyConstructorUriName);
+ return XamlILNodeEmitResult.Type(0, _types.FontFamily);
+ }
+ }
+}
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguage.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguage.cs
index 99ec3744bf..15413689f8 100644
--- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguage.cs
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguage.cs
@@ -1,6 +1,8 @@
+using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
+using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.AstNodes;
using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers;
using XamlX;
using XamlX.Ast;
@@ -166,17 +168,41 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
public static bool CustomValueConverter(AstTransformationContext context,
IXamlAstValueNode node, IXamlType type, out IXamlAstValueNode result)
{
- if (type.FullName == "System.TimeSpan"
- && node is XamlAstTextNode tn
- && !tn.Text.Contains(":"))
+ if (!(node is XamlAstTextNode textNode))
{
- var seconds = double.Parse(tn.Text, CultureInfo.InvariantCulture);
- result = new XamlStaticOrTargetedReturnMethodCallNode(tn,
- type.FindMethod("FromSeconds", type, false, context.Configuration.WellKnownTypes.Double),
- new[]
- {
- new XamlConstantNode(tn, context.Configuration.WellKnownTypes.Double, seconds)
- });
+ result = null;
+ return false;
+ }
+
+ var text = textNode.Text;
+
+ var types = context.GetAvaloniaTypes();
+
+ if (type.FullName == "System.TimeSpan")
+ {
+ var tsText = text.Trim();
+
+ if (!TimeSpan.TryParse(tsText, CultureInfo.InvariantCulture, out var timeSpan))
+ {
+ // // shorthand seconds format (ie. "0.25")
+ if (!tsText.Contains(":") && double.TryParse(tsText,
+ NumberStyles.Float | NumberStyles.AllowThousands,
+ CultureInfo.InvariantCulture, out var seconds))
+ timeSpan = TimeSpan.FromSeconds(seconds);
+ else
+ throw new XamlX.XamlLoadException($"Unable to parse {text} as a time span", node);
+ }
+
+
+ result = new XamlStaticOrTargetedReturnMethodCallNode(node,
+ type.FindMethod("FromTicks", type, false, types.Long),
+ new[] { new XamlConstantNode(node, types.Long, timeSpan.Ticks) });
+ return true;
+ }
+
+ if (type.Equals(types.FontFamily))
+ {
+ result = new AvaloniaXamlIlFontFamilyAstNode(types, text, node);
return true;
}
@@ -185,9 +211,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
var scope = context.ParentNodes().OfType().FirstOrDefault();
if (scope == null)
throw new XamlX.XamlLoadException("Unable to find the parent scope for AvaloniaProperty lookup", node);
- if (!(node is XamlAstTextNode text))
- throw new XamlX.XamlLoadException("Property should be a text node", node);
- result = XamlIlAvaloniaPropertyHelper.CreateNode(context, text.Text, scope.TargetType, text);
+
+ result = XamlIlAvaloniaPropertyHelper.CreateNode(context, text, scope.TargetType, node );
return true;
}
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs
index 3dec96dc43..58f4ddfe31 100644
--- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs
@@ -1,3 +1,4 @@
+using System.Collections.Generic;
using XamlX.Emit;
using XamlX.IL;
using XamlX.Transform;
@@ -47,6 +48,11 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
public IXamlType ReflectionBindingExtension { get; }
public IXamlType RelativeSource { get; }
+ public IXamlType Long { get; }
+ public IXamlType Uri { get; }
+ public IXamlType FontFamily { get; }
+ public IXamlConstructor FontFamilyConstructorUriName { get; }
+
public AvaloniaXamlIlWellKnownTypes(TransformerConfiguration cfg)
{
@@ -104,6 +110,10 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
ItemsRepeater = cfg.TypeSystem.GetType("Avalonia.Controls.ItemsRepeater");
ReflectionBindingExtension = cfg.TypeSystem.GetType("Avalonia.Markup.Xaml.MarkupExtensions.ReflectionBindingExtension");
RelativeSource = cfg.TypeSystem.GetType("Avalonia.Data.RelativeSource");
+ Long = cfg.TypeSystem.GetType("System.Int64");
+ Uri = cfg.TypeSystem.GetType("System.Uri");
+ FontFamily = cfg.TypeSystem.GetType("Avalonia.Media.FontFamily");
+ FontFamilyConstructorUriName = FontFamily.FindConstructor(new List { Uri, XamlIlTypes.String });
}
}
diff --git a/src/Markup/Avalonia.Markup/Markup/Parsers/BindingExpressionGrammar.cs b/src/Markup/Avalonia.Markup/Markup/Parsers/BindingExpressionGrammar.cs
index 8de64e56ff..8e5631e198 100644
--- a/src/Markup/Avalonia.Markup/Markup/Parsers/BindingExpressionGrammar.cs
+++ b/src/Markup/Avalonia.Markup/Markup/Parsers/BindingExpressionGrammar.cs
@@ -16,7 +16,7 @@ namespace Avalonia.Markup.Parsers
internal static class BindingExpressionGrammar
{
- public static (IList Nodes, SourceMode Mode) Parse(ref CharacterReader r)
+ public static (List Nodes, SourceMode Mode) Parse(ref CharacterReader r)
{
var nodes = new List();
var state = State.Start;
diff --git a/src/Windows/Avalonia.Win32/PopupImpl.cs b/src/Windows/Avalonia.Win32/PopupImpl.cs
index 57da1c4d66..7fb146899b 100644
--- a/src/Windows/Avalonia.Win32/PopupImpl.cs
+++ b/src/Windows/Avalonia.Win32/PopupImpl.cs
@@ -69,7 +69,8 @@ namespace Avalonia.Win32
{
UnmanagedMethods.WindowStyles style =
UnmanagedMethods.WindowStyles.WS_POPUP |
- UnmanagedMethods.WindowStyles.WS_CLIPSIBLINGS;
+ UnmanagedMethods.WindowStyles.WS_CLIPSIBLINGS |
+ UnmanagedMethods.WindowStyles.WS_CLIPCHILDREN;
UnmanagedMethods.WindowStyles exStyle =
UnmanagedMethods.WindowStyles.WS_EX_TOOLWINDOW |