From 7b0598559a1307ebf2eab4da53e11d05884582ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dariusz=20Komosi=C5=84ski?= Date: Tue, 6 Oct 2020 12:06:01 +0200 Subject: [PATCH 01/13] GeometryDrawing does not need to allocate new Pen for each GetBounds call. --- src/Avalonia.Visuals/ApiCompatBaseline.txt | 4 +++- src/Avalonia.Visuals/Media/Geometry.cs | 4 ++-- src/Avalonia.Visuals/Media/GeometryDrawing.cs | 10 ++++++---- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/Avalonia.Visuals/ApiCompatBaseline.txt b/src/Avalonia.Visuals/ApiCompatBaseline.txt index 7fb44152c9..2d00d82a46 100644 --- a/src/Avalonia.Visuals/ApiCompatBaseline.txt +++ b/src/Avalonia.Visuals/ApiCompatBaseline.txt @@ -2,6 +2,8 @@ Compat issues with assembly Avalonia.Visuals: MembersMustExist : Member 'public void Avalonia.Media.DrawingContext.DrawGlyphRun(Avalonia.Media.IBrush, Avalonia.Media.GlyphRun, Avalonia.Point)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public Avalonia.Media.Typeface Avalonia.Media.FontManager.GetOrAddTypeface(Avalonia.Media.FontFamily, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public Avalonia.Media.Typeface Avalonia.Media.FontManager.MatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontFamily, System.Globalization.CultureInfo)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public Avalonia.Rect Avalonia.Media.Geometry.GetRenderBounds(Avalonia.Media.Pen)' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public System.Boolean Avalonia.Media.Geometry.StrokeContains(Avalonia.Media.Pen, Avalonia.Point)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public Avalonia.Rect Avalonia.Media.GlyphRun.Bounds.get()' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public Avalonia.StyledProperty Avalonia.StyledProperty Avalonia.Media.GlyphRunDrawing.BaselineOriginProperty' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public Avalonia.Point Avalonia.Media.GlyphRunDrawing.BaselineOrigin.get()' does not exist in the implementation but it does exist in the contract. @@ -34,4 +36,4 @@ InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalon MembersMustExist : Member 'public System.Boolean Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontFamily, System.Globalization.CultureInfo, Avalonia.Media.Fonts.FontKey)' does not exist in the implementation but it does exist in the contract. InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontFamily, System.Globalization.CultureInfo, Avalonia.Media.Typeface)' is present in the implementation but not in the contract. MembersMustExist : Member 'public Avalonia.Utilities.IRef Avalonia.Rendering.RenderLayer.Bitmap.get()' does not exist in the implementation but it does exist in the contract. -Total Issues: 35 +Total Issues: 37 diff --git a/src/Avalonia.Visuals/Media/Geometry.cs b/src/Avalonia.Visuals/Media/Geometry.cs index 33aafb58fc..ccef6665a9 100644 --- a/src/Avalonia.Visuals/Media/Geometry.cs +++ b/src/Avalonia.Visuals/Media/Geometry.cs @@ -84,7 +84,7 @@ namespace Avalonia.Media /// /// The stroke thickness. /// The bounding rectangle. - public Rect GetRenderBounds(Pen pen) => PlatformImpl?.GetRenderBounds(pen) ?? Rect.Empty; + public Rect GetRenderBounds(IPen pen) => PlatformImpl?.GetRenderBounds(pen) ?? Rect.Empty; /// /// Indicates whether the geometry's fill contains the specified point. @@ -102,7 +102,7 @@ namespace Avalonia.Media /// The pen to use. /// The point. /// true if the geometry contains the point; otherwise, false. - public bool StrokeContains(Pen pen, Point point) + public bool StrokeContains(IPen pen, Point point) { return PlatformImpl?.StrokeContains(pen, point) == true; } diff --git a/src/Avalonia.Visuals/Media/GeometryDrawing.cs b/src/Avalonia.Visuals/Media/GeometryDrawing.cs index 4df3aa8ae2..b5052550e6 100644 --- a/src/Avalonia.Visuals/Media/GeometryDrawing.cs +++ b/src/Avalonia.Visuals/Media/GeometryDrawing.cs @@ -1,9 +1,13 @@ -using Avalonia.Metadata; +using Avalonia.Media.Immutable; +using Avalonia.Metadata; namespace Avalonia.Media { public class GeometryDrawing : Drawing { + // Adding the Pen's stroke thickness here could yield wrong results due to transforms. + private static readonly IPen s_boundsPen = new ImmutablePen(Colors.Black.ToUint32(), 0); + public static readonly StyledProperty GeometryProperty = AvaloniaProperty.Register(nameof(Geometry)); @@ -42,9 +46,7 @@ namespace Avalonia.Media public override Rect GetBounds() { - // adding the Pen's stroke thickness here could yield wrong results due to transforms - var pen = new Pen(Brushes.Black, 0); - return Geometry?.GetRenderBounds(pen) ?? new Rect(); + return Geometry?.GetRenderBounds(s_boundsPen) ?? new Rect(); } } } From 13d2c286fdd6aef41624a5fffa4f0dcbbb85f7a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dariusz=20Komosi=C5=84ski?= Date: Tue, 6 Oct 2020 12:22:41 +0200 Subject: [PATCH 02/13] Add doc comments and cleanup code a bit. --- src/Avalonia.Visuals/Media/Drawing.cs | 14 +++++-- src/Avalonia.Visuals/Media/GeometryDrawing.cs | 38 +++++++++++++++---- 2 files changed, 41 insertions(+), 11 deletions(-) diff --git a/src/Avalonia.Visuals/Media/Drawing.cs b/src/Avalonia.Visuals/Media/Drawing.cs index 6bc808e407..66d05e7e48 100644 --- a/src/Avalonia.Visuals/Media/Drawing.cs +++ b/src/Avalonia.Visuals/Media/Drawing.cs @@ -1,11 +1,19 @@ -using Avalonia.Platform; - -namespace Avalonia.Media +namespace Avalonia.Media { + /// + /// Abstract class that describes a 2-D drawing. + /// public abstract class Drawing : AvaloniaObject { + /// + /// Draws this drawing to the given . + /// + /// The drawing context. public abstract void Draw(DrawingContext context); + /// + /// Gets the drawing's bounding rectangle. + /// public abstract Rect GetBounds(); } } diff --git a/src/Avalonia.Visuals/Media/GeometryDrawing.cs b/src/Avalonia.Visuals/Media/GeometryDrawing.cs index b5052550e6..e46e3a7d5f 100644 --- a/src/Avalonia.Visuals/Media/GeometryDrawing.cs +++ b/src/Avalonia.Visuals/Media/GeometryDrawing.cs @@ -3,14 +3,36 @@ using Avalonia.Metadata; namespace Avalonia.Media { + /// + /// Represents a drawing operation that combines + /// a geometry with and brush and/or pen to produce rendered content. + /// public class GeometryDrawing : Drawing { // Adding the Pen's stroke thickness here could yield wrong results due to transforms. private static readonly IPen s_boundsPen = new ImmutablePen(Colors.Black.ToUint32(), 0); - + + /// + /// Defines the property. + /// public static readonly StyledProperty GeometryProperty = AvaloniaProperty.Register(nameof(Geometry)); + /// + /// Defines the property. + /// + public static readonly StyledProperty BrushProperty = + AvaloniaProperty.Register(nameof(Brush), Brushes.Transparent); + + /// + /// Defines the property. + /// + public static readonly StyledProperty PenProperty = + AvaloniaProperty.Register(nameof(Pen)); + + /// + /// Gets or sets the that describes the shape of this . + /// [Content] public Geometry Geometry { @@ -18,18 +40,18 @@ namespace Avalonia.Media set => SetValue(GeometryProperty, value); } - public static readonly StyledProperty BrushProperty = - AvaloniaProperty.Register(nameof(Brush), Brushes.Transparent); - + /// + /// Gets or sets the used to fill the interior of the shape described by this . + /// public IBrush Brush { get => GetValue(BrushProperty); set => SetValue(BrushProperty, value); } - public static readonly StyledProperty PenProperty = - AvaloniaProperty.Register(nameof(Pen)); - + /// + /// Gets or sets the used to stroke this . + /// public IPen Pen { get => GetValue(PenProperty); @@ -46,7 +68,7 @@ namespace Avalonia.Media public override Rect GetBounds() { - return Geometry?.GetRenderBounds(s_boundsPen) ?? new Rect(); + return Geometry?.GetRenderBounds(s_boundsPen) ?? Rect.Empty; } } } From c1bcdb95860739f4b5dd428d935ca7e57fbde8e3 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Wed, 14 Oct 2020 22:19:17 +0200 Subject: [PATCH 03/13] Allow for setting wm shadow hint on a context menu. --- src/Avalonia.Controls/ContextMenu.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/Avalonia.Controls/ContextMenu.cs b/src/Avalonia.Controls/ContextMenu.cs index c4df5c1815..ec9649bc64 100644 --- a/src/Avalonia.Controls/ContextMenu.cs +++ b/src/Avalonia.Controls/ContextMenu.cs @@ -62,6 +62,12 @@ namespace Avalonia.Controls public static readonly StyledProperty PlacementRectProperty = AvaloniaProperty.Register(nameof(PlacementRect)); + /// + /// Defines the property. + /// + public static readonly StyledProperty WindowManagerAddShadowHintProperty = + Popup.WindowManagerAddShadowHintProperty.AddOwner(); + /// /// Defines the property. /// @@ -158,6 +164,12 @@ namespace Avalonia.Controls set { SetValue(PlacementModeProperty, value); } } + public bool WindowManagerAddShadowHint + { + get { return GetValue(WindowManagerAddShadowHintProperty); } + set { SetValue(WindowManagerAddShadowHintProperty, value); } + } + /// /// Gets or sets the the anchor rectangle within the parent that the context menu will be placed /// relative to when is . @@ -267,6 +279,7 @@ namespace Avalonia.Controls PlacementTarget = PlacementTarget ?? control, IsLightDismissEnabled = true, OverlayDismissEventPassThrough = true, + WindowManagerAddShadowHint = WindowManagerAddShadowHint, }; _popup.Opened += PopupOpened; From 77cd5153329cde4763a55eae30f1718f47c3c535 Mon Sep 17 00:00:00 2001 From: mj Date: Sun, 18 Oct 2020 09:10:33 -0600 Subject: [PATCH 04/13] Fixed returns value; Issue 4883 --- src/Avalonia.Controls/ItemsControl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/ItemsControl.cs b/src/Avalonia.Controls/ItemsControl.cs index 3aec06e4eb..4dc8aec6f3 100644 --- a/src/Avalonia.Controls/ItemsControl.cs +++ b/src/Avalonia.Controls/ItemsControl.cs @@ -169,7 +169,7 @@ namespace Avalonia.Controls /// /// The collection. /// The index. - /// The index of the item or -1 if the item was not found. + /// The item at the given index or null if the index is out of bounds. protected static object ElementAt(IEnumerable items, int index) { if (index != -1 && index < items.Count()) From 1497882cf64941e1f1b21957b1e490290637bdb2 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Tue, 20 Oct 2020 16:54:48 +0300 Subject: [PATCH 05/13] Enable light dismiss overlay for popups --- src/Avalonia.Controls/Primitives/VisualLayerManager.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Avalonia.Controls/Primitives/VisualLayerManager.cs b/src/Avalonia.Controls/Primitives/VisualLayerManager.cs index d8d3450c6f..457a9ea282 100644 --- a/src/Avalonia.Controls/Primitives/VisualLayerManager.cs +++ b/src/Avalonia.Controls/Primitives/VisualLayerManager.cs @@ -67,8 +67,6 @@ namespace Avalonia.Controls.Primitives { get { - if (IsPopup) - return null; var rv = FindLayer(); if (rv == null) { From e04fe994fb9c032992c07380749933b4f8b6dca8 Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Wed, 21 Oct 2020 00:06:10 +0300 Subject: [PATCH 06/13] do not make any layout in ItemVirtualizer while not attached to visual tree --- src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs b/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs index bdc68bee7e..7d50ef7d33 100644 --- a/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs +++ b/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs @@ -512,6 +512,13 @@ namespace Avalonia.Controls.Presenters var generator = Owner.ItemContainerGenerator; var newOffset = -1.0; + //better not trigger any container generation/recycle while or layout stuff + //before panel is attached/visible + if (!panel.IsAttachedToVisualTree) + { + return null; + } + if (!panel.IsMeasureValid && panel.PreviousMeasure.HasValue) { //before any kind of scrolling we need to make sure panel measure is valid From a93ecd1c66d0126409fdfaea5cfc73124fed069d Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Wed, 21 Oct 2020 12:47:33 +0300 Subject: [PATCH 07/13] [X11] Handle PointerLeave through XI2 --- src/Avalonia.X11/XI2Manager.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.X11/XI2Manager.cs b/src/Avalonia.X11/XI2Manager.cs index 742973e0da..9349824525 100644 --- a/src/Avalonia.X11/XI2Manager.cs +++ b/src/Avalonia.X11/XI2Manager.cs @@ -162,7 +162,9 @@ namespace Avalonia.X11 | XEventMask.Button4MotionMask | XEventMask.Button5MotionMask | XEventMask.ButtonPressMask - | XEventMask.ButtonReleaseMask; + | XEventMask.ButtonReleaseMask + | XEventMask.LeaveWindowMask + | XEventMask.EnterWindowMask; } public void OnWindowDestroyed(IntPtr xid) => _clients.Remove(xid); @@ -201,6 +203,12 @@ namespace Avalonia.X11 return; } + if (ev.Type == XiEventType.XI_Leave) + { + client.ScheduleXI2Input(new RawPointerEventArgs(client.MouseDevice, ev.Timestamp, client.InputRoot, + RawPointerEventType.LeaveWindow, ev.Position, ev.Modifiers)); + } + if (_multitouch && ev.Emulated) return; From fb7b7a002ce8f46a924ea307a8b6710be1de0670 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Wed, 21 Oct 2020 13:04:05 +0300 Subject: [PATCH 08/13] Fixed initial pointerover at (0, 0) when window is just shown --- src/Avalonia.Input/MouseDevice.cs | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.Input/MouseDevice.cs b/src/Avalonia.Input/MouseDevice.cs index cec5029c18..5c63546f5d 100644 --- a/src/Avalonia.Input/MouseDevice.cs +++ b/src/Avalonia.Input/MouseDevice.cs @@ -19,6 +19,7 @@ namespace Avalonia.Input private readonly Pointer _pointer; private bool _disposed; + private PixelPoint? _position; public MouseDevice(Pointer? pointer = null) { @@ -39,10 +40,11 @@ namespace Avalonia.Input /// /// Gets the mouse position, in screen coordinates. /// + [Obsolete("Use events instead")] public PixelPoint Position { - get; - protected set; + get => _position ?? new PixelPoint(-1, -1); + protected set => _position = value; } /// @@ -91,7 +93,16 @@ namespace Avalonia.Input public void SceneInvalidated(IInputRoot root, Rect rect) { - var clientPoint = root.PointToClient(Position); + // Pointer is outside of the target area + if (_position == null ) + { + if (root.PointerOverElement != null) + ClearPointerOver(this, 0, root, PointerPointProperties.None, KeyModifiers.None); + return; + } + + + var clientPoint = root.PointToClient(_position.Value); if (rect.Contains(clientPoint)) { @@ -132,7 +143,7 @@ namespace Avalonia.Input if(mouse._disposed) return; - Position = e.Root.PointToScreen(e.Position); + _position = e.Root.PointToScreen(e.Position); var props = CreateProperties(e); var keyModifiers = KeyModifiersUtils.ConvertToKey(e.InputModifiers); switch (e.Type) @@ -176,6 +187,7 @@ namespace Avalonia.Input device = device ?? throw new ArgumentNullException(nameof(device)); root = root ?? throw new ArgumentNullException(nameof(root)); + _position = null; ClearPointerOver(this, timestamp, root, properties, inputModifiers); } From aa48b5733e14be820a01c452e15c947144cb81bc Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 21 Oct 2020 12:52:23 +0200 Subject: [PATCH 09/13] Replaced LogToDebug with LogToTrace. Calls to `System.Diagnostics.Debug` are removed in release builds meaning nothing was getting logged. Fixes #4901 --- samples/BindingDemo/App.xaml.cs | 2 +- samples/ControlCatalog.Desktop/Program.cs | 2 +- samples/ControlCatalog.NetCore/Program.cs | 2 +- samples/RenderDemo/App.xaml.cs | 2 +- samples/Sandbox/Program.cs | 2 +- samples/VirtualizationDemo/Program.cs | 2 +- src/Avalonia.Base/ApiCompatBaseline.txt | 3 ++- .../{DebugLogSink.cs => TraceLogSink.cs} | 14 +++++++------- src/Avalonia.Controls/LoggingExtensions.cs | 19 +++++++++++++++---- 9 files changed, 30 insertions(+), 18 deletions(-) rename src/Avalonia.Base/Logging/{DebugLogSink.cs => TraceLogSink.cs} (92%) diff --git a/samples/BindingDemo/App.xaml.cs b/samples/BindingDemo/App.xaml.cs index 5c38ab8305..eb2da03a7e 100644 --- a/samples/BindingDemo/App.xaml.cs +++ b/samples/BindingDemo/App.xaml.cs @@ -26,6 +26,6 @@ namespace BindingDemo => AppBuilder.Configure() .UsePlatformDetect() .UseReactiveUI() - .LogToDebug(); + .LogToTrace(); } } diff --git a/samples/ControlCatalog.Desktop/Program.cs b/samples/ControlCatalog.Desktop/Program.cs index e0cc8cc904..5af646b180 100644 --- a/samples/ControlCatalog.Desktop/Program.cs +++ b/samples/ControlCatalog.Desktop/Program.cs @@ -18,7 +18,7 @@ namespace ControlCatalog /// public static AppBuilder BuildAvaloniaApp() => AppBuilder.Configure() - .LogToDebug() + .LogToTrace() .UsePlatformDetect() .UseReactiveUI(); diff --git a/samples/ControlCatalog.NetCore/Program.cs b/samples/ControlCatalog.NetCore/Program.cs index 142736a0bb..c1fcdd6216 100644 --- a/samples/ControlCatalog.NetCore/Program.cs +++ b/samples/ControlCatalog.NetCore/Program.cs @@ -121,7 +121,7 @@ namespace ControlCatalog.NetCore .UseSkia() .UseReactiveUI() .UseManagedSystemDialogs() - .LogToDebug(); + .LogToTrace(); static void SilenceConsole() { diff --git a/samples/RenderDemo/App.xaml.cs b/samples/RenderDemo/App.xaml.cs index 2247fd7c5a..e6ec963d75 100644 --- a/samples/RenderDemo/App.xaml.cs +++ b/samples/RenderDemo/App.xaml.cs @@ -33,6 +33,6 @@ namespace RenderDemo }) .UsePlatformDetect() .UseReactiveUI() - .LogToDebug(); + .LogToTrace(); } } diff --git a/samples/Sandbox/Program.cs b/samples/Sandbox/Program.cs index 4d7eda8d9f..7d41a8b8c0 100644 --- a/samples/Sandbox/Program.cs +++ b/samples/Sandbox/Program.cs @@ -10,7 +10,7 @@ namespace Sandbox AppBuilder.Configure() .UsePlatformDetect() .UseReactiveUI() - .LogToDebug() + .LogToTrace() .StartWithClassicDesktopLifetime(args); } } diff --git a/samples/VirtualizationDemo/Program.cs b/samples/VirtualizationDemo/Program.cs index c23bfd7ad9..46c05f74b2 100644 --- a/samples/VirtualizationDemo/Program.cs +++ b/samples/VirtualizationDemo/Program.cs @@ -9,7 +9,7 @@ namespace VirtualizationDemo => AppBuilder.Configure() .UsePlatformDetect() .UseReactiveUI() - .LogToDebug(); + .LogToTrace(); public static int Main(string[] args) => BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); diff --git a/src/Avalonia.Base/ApiCompatBaseline.txt b/src/Avalonia.Base/ApiCompatBaseline.txt index 4668a572c5..5d19373a92 100644 --- a/src/Avalonia.Base/ApiCompatBaseline.txt +++ b/src/Avalonia.Base/ApiCompatBaseline.txt @@ -1,3 +1,4 @@ Compat issues with assembly Avalonia.Base: CannotAddAbstractMembers : Member 'protected System.IObservable Avalonia.AvaloniaProperty.GetChanged()' is abstract in the implementation but is missing in the contract. -Total Issues: 1 +TypesMustExist : Type 'Avalonia.Logging.DebugLogSink' does not exist in the implementation but it does exist in the contract. +Total Issues: 2 diff --git a/src/Avalonia.Base/Logging/DebugLogSink.cs b/src/Avalonia.Base/Logging/TraceLogSink.cs similarity index 92% rename from src/Avalonia.Base/Logging/DebugLogSink.cs rename to src/Avalonia.Base/Logging/TraceLogSink.cs index 3695afa860..f597844378 100644 --- a/src/Avalonia.Base/Logging/DebugLogSink.cs +++ b/src/Avalonia.Base/Logging/TraceLogSink.cs @@ -6,12 +6,12 @@ using Avalonia.Utilities; namespace Avalonia.Logging { - public class DebugLogSink : ILogSink + public class TraceLogSink : ILogSink { private readonly LogEventLevel _level; private readonly IList _areas; - public DebugLogSink( + public TraceLogSink( LogEventLevel minimumLevel, IList areas = null) { @@ -28,7 +28,7 @@ namespace Avalonia.Logging { if (IsEnabled(level, area)) { - Debug.WriteLine(Format(area, messageTemplate, source)); + Trace.WriteLine(Format(area, messageTemplate, source)); } } @@ -36,7 +36,7 @@ namespace Avalonia.Logging { if (IsEnabled(level, area)) { - Debug.WriteLine(Format(area, messageTemplate, source, propertyValue0)); + Trace.WriteLine(Format(area, messageTemplate, source, propertyValue0)); } } @@ -44,7 +44,7 @@ namespace Avalonia.Logging { if (IsEnabled(level, area)) { - Debug.WriteLine(Format(area, messageTemplate, source, propertyValue0, propertyValue1)); + Trace.WriteLine(Format(area, messageTemplate, source, propertyValue0, propertyValue1)); } } @@ -52,7 +52,7 @@ namespace Avalonia.Logging { if (IsEnabled(level, area)) { - Debug.WriteLine(Format(area, messageTemplate, source, propertyValue0, propertyValue1, propertyValue2)); + Trace.WriteLine(Format(area, messageTemplate, source, propertyValue0, propertyValue1, propertyValue2)); } } @@ -60,7 +60,7 @@ namespace Avalonia.Logging { if (IsEnabled(level, area)) { - Debug.WriteLine(Format(area, messageTemplate, source, propertyValues)); + Trace.WriteLine(Format(area, messageTemplate, source, propertyValues)); } } diff --git a/src/Avalonia.Controls/LoggingExtensions.cs b/src/Avalonia.Controls/LoggingExtensions.cs index 44e570bdfa..9eb3b140f6 100644 --- a/src/Avalonia.Controls/LoggingExtensions.cs +++ b/src/Avalonia.Controls/LoggingExtensions.cs @@ -1,25 +1,36 @@ -using Avalonia.Controls; +using System; +using Avalonia.Controls; using Avalonia.Logging; namespace Avalonia { public static class LoggingExtensions { + [Obsolete("Use LogToTrace")] + public static T LogToDebug( + this T builder, + LogEventLevel level = LogEventLevel.Warning, + params string[] areas) + where T : AppBuilderBase, new() + { + return LogToTrace(builder, level, areas); + } + /// - /// Logs Avalonia events to the sink. + /// Logs Avalonia events to the sink. /// /// The application class type. /// The app builder instance. /// The minimum level to log. /// The areas to log. Valid values are listed in . /// The app builder instance. - public static T LogToDebug( + public static T LogToTrace( this T builder, LogEventLevel level = LogEventLevel.Warning, params string[] areas) where T : AppBuilderBase, new() { - Logger.Sink = new DebugLogSink(level, areas); + Logger.Sink = new TraceLogSink(level, areas); return builder; } } From 4f6dd5a44a9da62446b91ed0f620b645bff00f60 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Wed, 21 Oct 2020 14:42:00 +0300 Subject: [PATCH 10/13] [X11] XI2 fix --- src/Avalonia.X11/XI2Manager.cs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/Avalonia.X11/XI2Manager.cs b/src/Avalonia.X11/XI2Manager.cs index 9349824525..d0655d05c1 100644 --- a/src/Avalonia.X11/XI2Manager.cs +++ b/src/Avalonia.X11/XI2Manager.cs @@ -13,7 +13,8 @@ namespace Avalonia.X11 { XiEventType.XI_Motion, XiEventType.XI_ButtonPress, - XiEventType.XI_ButtonRelease + XiEventType.XI_ButtonRelease, + XiEventType.XI_Leave }; private static readonly XiEventType[] MultiTouchEventTypes = new XiEventType[] @@ -177,9 +178,10 @@ namespace Avalonia.X11 _pointerDevice.Update(changed->Classes, changed->NumClasses); } - + if ((xev->evtype >= XiEventType.XI_ButtonPress && xev->evtype <= XiEventType.XI_Motion) - || (xev->evtype>=XiEventType.XI_TouchBegin&&xev->evtype<=XiEventType.XI_TouchEnd)) + || (xev->evtype >= XiEventType.XI_TouchBegin && xev->evtype <= XiEventType.XI_TouchEnd) + || xev->evtype == XiEventType.XI_Leave) { var dev = (XIDeviceEvent*)xev; if (_clients.TryGetValue(dev->EventWindow, out var client)) @@ -306,7 +308,7 @@ namespace Avalonia.X11 if (state.HasFlag(XModifierMask.Mod4Mask)) Modifiers |= RawInputModifiers.Meta; - if (ev->buttons.MaskLen > 0) + if (ev->buttons.MaskLen > 1 && Type != XiEventType.XI_Leave) { var buttons = ev->buttons.Mask; if (XIMaskIsSet(buttons, 1)) @@ -324,9 +326,11 @@ namespace Avalonia.X11 Valuators = new Dictionary(); Position = new Point(ev->event_x, ev->event_y); var values = ev->valuators.Values; - for (var c = 0; c < ev->valuators.MaskLen * 8; c++) - if (XIMaskIsSet(ev->valuators.Mask, c)) - Valuators[c] = *values++; + if(ev->valuators.Mask != null) + for (var c = 0; c < ev->valuators.MaskLen * 8; c++) + if (XIMaskIsSet(ev->valuators.Mask, c)) + Valuators[c] = *values++; + if (Type == XiEventType.XI_ButtonPress || Type == XiEventType.XI_ButtonRelease) Button = ev->detail; Detail = ev->detail; From 8f1e8590894d101dc57282c31a9759c5aad9d6ce Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Wed, 21 Oct 2020 15:53:32 +0300 Subject: [PATCH 11/13] Fixed XI_Leave handling --- src/Avalonia.X11/XI2Manager.cs | 76 ++++++++++++++++++++++++---------- src/Avalonia.X11/XIStructs.cs | 43 ++++++++++++++++++- 2 files changed, 95 insertions(+), 24 deletions(-) diff --git a/src/Avalonia.X11/XI2Manager.cs b/src/Avalonia.X11/XI2Manager.cs index d0655d05c1..b3a24e6c37 100644 --- a/src/Avalonia.X11/XI2Manager.cs +++ b/src/Avalonia.X11/XI2Manager.cs @@ -14,7 +14,9 @@ namespace Avalonia.X11 XiEventType.XI_Motion, XiEventType.XI_ButtonPress, XiEventType.XI_ButtonRelease, - XiEventType.XI_Leave + XiEventType.XI_Leave, + XiEventType.XI_Enter, + }; private static readonly XiEventType[] MultiTouchEventTypes = new XiEventType[] @@ -180,13 +182,37 @@ namespace Avalonia.X11 if ((xev->evtype >= XiEventType.XI_ButtonPress && xev->evtype <= XiEventType.XI_Motion) - || (xev->evtype >= XiEventType.XI_TouchBegin && xev->evtype <= XiEventType.XI_TouchEnd) - || xev->evtype == XiEventType.XI_Leave) + || (xev->evtype >= XiEventType.XI_TouchBegin && xev->evtype <= XiEventType.XI_TouchEnd)) { var dev = (XIDeviceEvent*)xev; if (_clients.TryGetValue(dev->EventWindow, out var client)) OnDeviceEvent(client, new ParsedDeviceEvent(dev)); } + + if (xev->evtype == XiEventType.XI_Leave || xev->evtype == XiEventType.XI_Enter) + { + var rev = (XIEnterLeaveEvent*)xev; + if (_clients.TryGetValue(rev->EventWindow, out var client)) + OnEnterLeaveEvent(client, ref *rev); + } + } + + void OnEnterLeaveEvent(IXI2Client client, ref XIEnterLeaveEvent ev) + { + if (ev.evtype == XiEventType.XI_Leave) + { + var buttons = ParsedDeviceEvent.ParseButtonState(ev.buttons.MaskLen, ev.buttons.Mask); + var detail = ev.detail; + if ((detail == XiEnterLeaveDetail.XINotifyNonlinearVirtual || + detail == XiEnterLeaveDetail.XINotifyNonlinear || + detail == XiEnterLeaveDetail.XINotifyVirtual) + && buttons == default) + { + client.ScheduleXI2Input(new RawPointerEventArgs(client.MouseDevice, (ulong)ev.time.ToInt64(), + client.InputRoot, + RawPointerEventType.LeaveWindow, new Point(ev.event_x, ev.event_y), buttons)); + } + } } void OnDeviceEvent(IXI2Client client, ParsedDeviceEvent ev) @@ -205,12 +231,6 @@ namespace Avalonia.X11 return; } - if (ev.Type == XiEventType.XI_Leave) - { - client.ScheduleXI2Input(new RawPointerEventArgs(client.MouseDevice, ev.Timestamp, client.InputRoot, - RawPointerEventType.LeaveWindow, ev.Position, ev.Modifiers)); - } - if (_multitouch && ev.Emulated) return; @@ -294,6 +314,29 @@ namespace Avalonia.X11 public int Detail { get; set; } public bool Emulated { get; set; } public Dictionary Valuators { get; } + + public static RawInputModifiers ParseButtonState(int len, byte* buttons) + { + RawInputModifiers rv = default; + if (len > 0) + { + if (XIMaskIsSet(buttons, 1)) + rv |= RawInputModifiers.LeftMouseButton; + if (XIMaskIsSet(buttons, 2)) + rv |= RawInputModifiers.MiddleMouseButton; + if (XIMaskIsSet(buttons, 3)) + rv |= RawInputModifiers.RightMouseButton; + if (len > 1) + { + if (XIMaskIsSet(buttons, 8)) + rv |= RawInputModifiers.XButton1MouseButton; + if (XIMaskIsSet(buttons, 9)) + rv |= RawInputModifiers.XButton2MouseButton; + } + } + return rv; + } + public ParsedDeviceEvent(XIDeviceEvent* ev) { Type = ev->evtype; @@ -308,20 +351,7 @@ namespace Avalonia.X11 if (state.HasFlag(XModifierMask.Mod4Mask)) Modifiers |= RawInputModifiers.Meta; - if (ev->buttons.MaskLen > 1 && Type != XiEventType.XI_Leave) - { - var buttons = ev->buttons.Mask; - if (XIMaskIsSet(buttons, 1)) - Modifiers |= RawInputModifiers.LeftMouseButton; - if (XIMaskIsSet(buttons, 2)) - Modifiers |= RawInputModifiers.MiddleMouseButton; - if (XIMaskIsSet(buttons, 3)) - Modifiers |= RawInputModifiers.RightMouseButton; - if (XIMaskIsSet(buttons, 8)) - Modifiers |= RawInputModifiers.XButton1MouseButton; - if (XIMaskIsSet(buttons, 9)) - Modifiers |= RawInputModifiers.XButton2MouseButton; - } + Modifiers = ParseButtonState(ev->buttons.MaskLen, ev->buttons.Mask); Valuators = new Dictionary(); Position = new Point(ev->event_x, ev->event_y); diff --git a/src/Avalonia.X11/XIStructs.cs b/src/Avalonia.X11/XIStructs.cs index 4675ef47f2..f4581a99ba 100644 --- a/src/Avalonia.X11/XIStructs.cs +++ b/src/Avalonia.X11/XIStructs.cs @@ -236,6 +236,34 @@ namespace Avalonia.X11 public XIModifierState mods; public XIModifierState group; } + + [StructLayout(LayoutKind.Sequential)] + unsafe struct XIEnterLeaveEvent + { + public XEventName type; /* GenericEvent */ + public UIntPtr serial; /* # of last request processed by server */ + public Bool send_event; /* true if this came from a SendEvent request */ + public IntPtr display; /* Display the event was read from */ + public int extension; /* XI extension offset */ + public XiEventType evtype; + public IntPtr time; + public int deviceid; + public int sourceid; + public XiEnterLeaveDetail detail; + public IntPtr RootWindow; + public IntPtr EventWindow; + public IntPtr ChildWindow; + public double root_x; + public double root_y; + public double event_x; + public double event_y; + public int mode; + public int focus; + public int same_screen; + public XIButtonState buttons; + public XIModifierState mods; + public XIModifierState group; + } [Flags] public enum XiDeviceEventFlags : int @@ -286,5 +314,18 @@ namespace Avalonia.X11 XI_BarrierLeave = 26, XI_LASTEVENT = XI_BarrierLeave, } - + + enum XiEnterLeaveDetail + { + XINotifyAncestor = 0, + XINotifyVirtual = 1, + XINotifyInferior = 2, + XINotifyNonlinear = 3, + XINotifyNonlinearVirtual = 4, + XINotifyPointer = 5, + XINotifyPointerRoot = 6, + XINotifyDetailNone = 7 + + } + } From 5d97e7fbf4f428f2578529de30d12a7152dd6c4d Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Wed, 21 Oct 2020 16:11:08 +0300 Subject: [PATCH 12/13] [DataGrid] Capture mouse pointer only when drag operation has actually started --- .../DataGridColumnHeader.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs b/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs index 856d1f6566..77f23392e9 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs @@ -340,8 +340,6 @@ namespace Avalonia.Controls if (OwningGrid != null && OwningGrid.ColumnHeaders != null) { - args.Pointer.Capture(this); - _dragMode = DragMode.MouseDown; _frozenColumnsWidth = OwningGrid.ColumnsInternal.GetVisibleFrozenEdgedColumnsWidth(); _lastMousePositionHeaders = this.Translate(OwningGrid.ColumnHeaders, mousePosition); @@ -413,8 +411,9 @@ namespace Avalonia.Controls } //TODO DragEvents - internal void OnMouseMove(ref bool handled, Point mousePosition, Point mousePositionHeaders) + internal void OnMouseMove(PointerEventArgs args, Point mousePosition, Point mousePositionHeaders) { + var handled = args.Handled; if (handled || OwningGrid == null || OwningGrid.ColumnHeaders == null) { return; @@ -438,7 +437,10 @@ namespace Avalonia.Controls } _lastMousePositionHeaders = mousePositionHeaders; - + + if (args.Pointer.Captured != this) + args.Pointer.Capture(this); + SetDragCursor(mousePosition); } @@ -506,8 +508,7 @@ namespace Avalonia.Controls Point mousePosition = e.GetPosition(this); Point mousePositionHeaders = e.GetPosition(OwningGrid.ColumnHeaders); - bool handled = false; - OnMouseMove(ref handled, mousePosition, mousePositionHeaders); + OnMouseMove(e, mousePosition, mousePositionHeaders); } /// From 26d3792b0942778c1461acf4c04a4df843e1004f Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Wed, 21 Oct 2020 19:40:54 +0300 Subject: [PATCH 13/13] Do not sign Avalonia.ReactiveUI --- src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj b/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj index d01a495108..3d7bd2548a 100644 --- a/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj +++ b/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj @@ -2,6 +2,7 @@ netstandard2.0 Avalonia.ReactiveUI + True