diff --git a/.editorconfig b/.editorconfig
index cb589a5ce1..30edee1633 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -21,7 +21,7 @@ csharp_new_line_before_finally = true
csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_members_in_anonymous_types = true
csharp_new_line_between_query_expression_clauses = true
-trim_trailing_whitespace = true
+# trim_trailing_whitespace = true
# Indentation preferences
csharp_indent_block_contents = true
diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm
index 45d78b3926..77f53332cd 100644
--- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm
+++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm
@@ -48,7 +48,6 @@ WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl,
[Window setContentMaxSize:lastMaxSize];
[Window setOpaque:false];
- [Window setHasShadow:true];
}
HRESULT WindowBaseImpl::ObtainNSViewHandle(void **ret) {
diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm
index 85a89955f4..95f61422cb 100644
--- a/native/Avalonia.Native/src/OSX/WindowImpl.mm
+++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm
@@ -24,6 +24,8 @@ WindowImpl::WindowImpl(IAvnWindowEvents *events, IAvnGlContext *gl) : WindowBase
_lastTitle = @"";
_parent = nullptr;
WindowEvents = events;
+
+ [Window setHasShadow:true];
OnInitialiseNSWindow();
}
diff --git a/samples/ControlCatalog.NetCore/Program.cs b/samples/ControlCatalog.NetCore/Program.cs
index fd080cfc5b..13751b56b5 100644
--- a/samples/ControlCatalog.NetCore/Program.cs
+++ b/samples/ControlCatalog.NetCore/Program.cs
@@ -115,7 +115,6 @@ namespace ControlCatalog.NetCore
})
.With(new Win32PlatformOptions
{
- EnableMultitouch = true
})
.UseSkia()
.AfterSetup(builder =>
diff --git a/samples/ControlCatalog/MainView.xaml b/samples/ControlCatalog/MainView.xaml
index d8dc3bad2d..7676de54a6 100644
--- a/samples/ControlCatalog/MainView.xaml
+++ b/samples/ControlCatalog/MainView.xaml
@@ -109,7 +109,7 @@
-
+
diff --git a/samples/ControlCatalog/Pages/PointerCanvas.cs b/samples/ControlCatalog/Pages/PointerCanvas.cs
new file mode 100644
index 0000000000..5843b13a0c
--- /dev/null
+++ b/samples/ControlCatalog/Pages/PointerCanvas.cs
@@ -0,0 +1,235 @@
+#nullable enable
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Threading;
+
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Input;
+using Avalonia.Media;
+using Avalonia.Threading;
+
+namespace ControlCatalog.Pages;
+
+public class PointerCanvas : Control
+{
+ private readonly Stopwatch _stopwatch = Stopwatch.StartNew();
+ private int _events;
+ private IDisposable? _statusUpdated;
+ private Dictionary _pointers = new();
+ private PointerPointProperties? _lastProperties;
+ private PointerUpdateKind? _lastNonOtherUpdateKind;
+ class PointerPoints
+ {
+ struct CanvasPoint
+ {
+ public IBrush Brush;
+ public Point Point;
+ public double Radius;
+ public double? Pressure;
+ }
+
+ readonly CanvasPoint[] _points = new CanvasPoint[1000];
+ int _index;
+
+ public void Render(DrawingContext context, bool drawPoints)
+ {
+ CanvasPoint? prev = null;
+ for (var c = 0; c < _points.Length; c++)
+ {
+ var i = (c + _index) % _points.Length;
+ var pt = _points[i];
+ var pressure = (pt.Pressure ?? prev?.Pressure ?? 0.5);
+ var thickness = pressure * 10;
+ var radius = pressure * pt.Radius;
+
+ if (drawPoints)
+ {
+ if (pt.Brush != null)
+ {
+ context.DrawEllipse(pt.Brush, null, pt.Point, radius, radius);
+ }
+ }
+ else
+ {
+ if (prev.HasValue && prev.Value.Brush != null && pt.Brush != null
+ && prev.Value.Pressure != null && pt.Pressure != null)
+ {
+ var linePen = new Pen(Brushes.Black, thickness, null, PenLineCap.Round, PenLineJoin.Round);
+ context.DrawLine(linePen, prev.Value.Point, pt.Point);
+ }
+ }
+ prev = pt;
+ }
+
+ }
+
+ void AddPoint(Point pt, IBrush brush, double radius, float? pressure = null)
+ {
+ _points[_index] = new CanvasPoint { Point = pt, Brush = brush, Radius = radius, Pressure = pressure };
+ _index = (_index + 1) % _points.Length;
+ }
+
+ public void HandleEvent(PointerEventArgs e, Visual v)
+ {
+ e.Handled = true;
+ var currentPoint = e.GetCurrentPoint(v);
+ if (e.RoutedEvent == PointerPressedEvent)
+ AddPoint(currentPoint.Position, Brushes.Green, 10);
+ else if (e.RoutedEvent == PointerReleasedEvent)
+ AddPoint(currentPoint.Position, Brushes.Red, 10);
+ else
+ {
+ var pts = e.GetIntermediatePoints(v);
+ for (var c = 0; c < pts.Count; c++)
+ {
+ var pt = pts[c];
+ AddPoint(pt.Position, c == pts.Count - 1 ? Brushes.Blue : Brushes.Black,
+ c == pts.Count - 1 ? 5 : 2, pt.Properties.Pressure);
+ }
+ }
+ }
+ }
+
+ private int _threadSleep;
+ public static DirectProperty ThreadSleepProperty =
+ AvaloniaProperty.RegisterDirect(nameof(ThreadSleep), c => c.ThreadSleep, (c, v) => c.ThreadSleep = v);
+
+ public int ThreadSleep
+ {
+ get => _threadSleep;
+ set => SetAndRaise(ThreadSleepProperty, ref _threadSleep, value);
+ }
+
+ private bool _drawOnlyPoints;
+ public static DirectProperty DrawOnlyPointsProperty =
+ AvaloniaProperty.RegisterDirect(nameof(DrawOnlyPoints), c => c.DrawOnlyPoints, (c, v) => c.DrawOnlyPoints = v);
+
+ public bool DrawOnlyPoints
+ {
+ get => _drawOnlyPoints;
+ set => SetAndRaise(DrawOnlyPointsProperty, ref _drawOnlyPoints, value);
+ }
+
+ private string? _status;
+ public static DirectProperty StatusProperty =
+ AvaloniaProperty.RegisterDirect(nameof(DrawOnlyPoints), c => c.Status, (c, v) => c.Status = v,
+ defaultBindingMode: Avalonia.Data.BindingMode.TwoWay);
+
+ public string? Status
+ {
+ get => _status;
+ set => SetAndRaise(StatusProperty, ref _status, value);
+ }
+
+ protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
+ {
+ base.OnAttachedToVisualTree(e);
+
+ _statusUpdated = DispatcherTimer.Run(() =>
+ {
+ if (_stopwatch.Elapsed.TotalMilliseconds > 250)
+ {
+ Status = $@"Events per second: {(_events / _stopwatch.Elapsed.TotalSeconds)}
+PointerUpdateKind: {_lastProperties?.PointerUpdateKind}
+Last PointerUpdateKind != Other: {_lastNonOtherUpdateKind}
+IsLeftButtonPressed: {_lastProperties?.IsLeftButtonPressed}
+IsRightButtonPressed: {_lastProperties?.IsRightButtonPressed}
+IsMiddleButtonPressed: {_lastProperties?.IsMiddleButtonPressed}
+IsXButton1Pressed: {_lastProperties?.IsXButton1Pressed}
+IsXButton2Pressed: {_lastProperties?.IsXButton2Pressed}
+IsBarrelButtonPressed: {_lastProperties?.IsBarrelButtonPressed}
+IsEraser: {_lastProperties?.IsEraser}
+IsInverted: {_lastProperties?.IsInverted}
+Pressure: {_lastProperties?.Pressure}
+XTilt: {_lastProperties?.XTilt}
+YTilt: {_lastProperties?.YTilt}
+Twist: {_lastProperties?.Twist}";
+ _stopwatch.Restart();
+ _events = 0;
+ }
+
+ return true;
+ }, TimeSpan.FromMilliseconds(10));
+ }
+
+ protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
+ {
+ base.OnDetachedFromVisualTree(e);
+
+ _statusUpdated?.Dispose();
+ }
+
+ void HandleEvent(PointerEventArgs e)
+ {
+ _events++;
+
+ if (_threadSleep != 0)
+ {
+ Thread.Sleep(_threadSleep);
+ }
+ InvalidateVisual();
+
+ var lastPointer = e.GetCurrentPoint(this);
+ _lastProperties = lastPointer.Properties;
+
+ if (_lastProperties.PointerUpdateKind != PointerUpdateKind.Other)
+ {
+ _lastNonOtherUpdateKind = _lastProperties.PointerUpdateKind;
+ }
+
+ if (e.RoutedEvent == PointerReleasedEvent && e.Pointer.Type == PointerType.Touch)
+ {
+ _pointers.Remove(e.Pointer.Id);
+ return;
+ }
+
+ if (e.Pointer.Type != PointerType.Pen
+ || lastPointer.Properties.Pressure > 0)
+ {
+ if (!_pointers.TryGetValue(e.Pointer.Id, out var pt))
+ _pointers[e.Pointer.Id] = pt = new PointerPoints();
+ pt.HandleEvent(e, this);
+ }
+ }
+
+ public override void Render(DrawingContext context)
+ {
+ context.FillRectangle(Brushes.White, Bounds);
+ foreach (var pt in _pointers.Values)
+ pt.Render(context, _drawOnlyPoints);
+ base.Render(context);
+ }
+
+ protected override void OnPointerPressed(PointerPressedEventArgs e)
+ {
+ if (e.ClickCount == 2)
+ {
+ _pointers.Clear();
+ InvalidateVisual();
+ return;
+ }
+
+ HandleEvent(e);
+ base.OnPointerPressed(e);
+ }
+
+ protected override void OnPointerMoved(PointerEventArgs e)
+ {
+ HandleEvent(e);
+ base.OnPointerMoved(e);
+ }
+
+ protected override void OnPointerReleased(PointerReleasedEventArgs e)
+ {
+ HandleEvent(e);
+ base.OnPointerReleased(e);
+ }
+
+ protected override void OnPointerCaptureLost(PointerCaptureLostEventArgs e)
+ {
+ _lastProperties = null;
+ base.OnPointerCaptureLost(e);
+ }
+}
diff --git a/samples/ControlCatalog/Pages/PointerContactsTab.cs b/samples/ControlCatalog/Pages/PointerContactsTab.cs
new file mode 100644
index 0000000000..b6aabebf99
--- /dev/null
+++ b/samples/ControlCatalog/Pages/PointerContactsTab.cs
@@ -0,0 +1,109 @@
+#nullable enable
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reactive.Linq;
+
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Input;
+using Avalonia.Media;
+using Avalonia.Media.Immutable;
+
+namespace ControlCatalog.Pages;
+
+public class PointerContactsTab : Control
+{
+ class PointerInfo
+ {
+ public Point Point { get; set; }
+ public Color Color { get; set; }
+ }
+
+ private static Color[] AllColors = new[]
+ {
+ Colors.Aqua,
+ Colors.Beige,
+ Colors.Chartreuse,
+ Colors.Coral,
+ Colors.Fuchsia,
+ Colors.Crimson,
+ Colors.Lavender,
+ Colors.Orange,
+ Colors.Orchid,
+ Colors.ForestGreen,
+ Colors.SteelBlue,
+ Colors.PapayaWhip,
+ Colors.PaleVioletRed,
+ Colors.Goldenrod,
+ Colors.Maroon,
+ Colors.Moccasin,
+ Colors.Navy,
+ Colors.Wheat,
+ Colors.Violet,
+ Colors.Sienna,
+ Colors.Indigo,
+ Colors.Honeydew
+ };
+
+ private Dictionary _pointers = new Dictionary();
+
+ public PointerContactsTab()
+ {
+ ClipToBounds = true;
+ }
+
+ void UpdatePointer(PointerEventArgs e)
+ {
+ if (!_pointers.TryGetValue(e.Pointer, out var info))
+ {
+ if (e.RoutedEvent == PointerMovedEvent)
+ return;
+ var colors = AllColors.Except(_pointers.Values.Select(c => c.Color)).ToArray();
+ var color = colors[new Random().Next(0, colors.Length - 1)];
+ _pointers[e.Pointer] = info = new PointerInfo { Color = color };
+ }
+
+ info.Point = e.GetPosition(this);
+ InvalidateVisual();
+ }
+
+ protected override void OnPointerPressed(PointerPressedEventArgs e)
+ {
+ UpdatePointer(e);
+ e.Pointer.Capture(this);
+ e.Handled = true;
+ base.OnPointerPressed(e);
+ }
+
+ protected override void OnPointerMoved(PointerEventArgs e)
+ {
+ UpdatePointer(e);
+ e.Handled = true;
+ base.OnPointerMoved(e);
+ }
+
+ protected override void OnPointerReleased(PointerReleasedEventArgs e)
+ {
+ _pointers.Remove(e.Pointer);
+ e.Handled = true;
+ InvalidateVisual();
+ }
+
+ protected override void OnPointerCaptureLost(PointerCaptureLostEventArgs e)
+ {
+ _pointers.Remove(e.Pointer);
+ InvalidateVisual();
+ }
+
+ public override void Render(DrawingContext context)
+ {
+ context.FillRectangle(Brushes.Transparent, new Rect(default, Bounds.Size));
+ foreach (var pt in _pointers.Values)
+ {
+ var brush = new ImmutableSolidColorBrush(pt.Color);
+
+ context.DrawEllipse(brush, null, pt.Point, 75, 75);
+ }
+ }
+}
diff --git a/samples/ControlCatalog/Pages/PointersPage.cs b/samples/ControlCatalog/Pages/PointersPage.cs
deleted file mode 100644
index 0377993d2c..0000000000
--- a/samples/ControlCatalog/Pages/PointersPage.cs
+++ /dev/null
@@ -1,322 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Linq;
-using System.Reactive.Linq;
-using System.Runtime.InteropServices;
-using System.Threading;
-using Avalonia;
-using Avalonia.Controls;
-using Avalonia.Controls.Documents;
-using Avalonia.Input;
-using Avalonia.Layout;
-using Avalonia.Media;
-using Avalonia.Media.Immutable;
-using Avalonia.Threading;
-using Avalonia.VisualTree;
-
-namespace ControlCatalog.Pages;
-
-public class PointersPage : Decorator
-{
- public PointersPage()
- {
- Child = new TabControl
- {
- Items = new[]
- {
- new TabItem() { Header = "Contacts", Content = new PointerContactsTab() },
- new TabItem() { Header = "IntermediatePoints", Content = new PointerIntermediatePointsTab() }
- }
- };
- }
-
-
- class PointerContactsTab : Control
- {
- class PointerInfo
- {
- public Point Point { get; set; }
- public Color Color { get; set; }
- }
-
- private static Color[] AllColors = new[]
- {
- Colors.Aqua,
- Colors.Beige,
- Colors.Chartreuse,
- Colors.Coral,
- Colors.Fuchsia,
- Colors.Crimson,
- Colors.Lavender,
- Colors.Orange,
- Colors.Orchid,
- Colors.ForestGreen,
- Colors.SteelBlue,
- Colors.PapayaWhip,
- Colors.PaleVioletRed,
- Colors.Goldenrod,
- Colors.Maroon,
- Colors.Moccasin,
- Colors.Navy,
- Colors.Wheat,
- Colors.Violet,
- Colors.Sienna,
- Colors.Indigo,
- Colors.Honeydew
- };
-
- private Dictionary _pointers = new Dictionary();
-
- public PointerContactsTab()
- {
- ClipToBounds = true;
- }
-
- void UpdatePointer(PointerEventArgs e)
- {
- if (!_pointers.TryGetValue(e.Pointer, out var info))
- {
- if (e.RoutedEvent == PointerMovedEvent)
- return;
- var colors = AllColors.Except(_pointers.Values.Select(c => c.Color)).ToArray();
- var color = colors[new Random().Next(0, colors.Length - 1)];
- _pointers[e.Pointer] = info = new PointerInfo {Color = color};
- }
-
- info.Point = e.GetPosition(this);
- InvalidateVisual();
- }
-
- protected override void OnPointerPressed(PointerPressedEventArgs e)
- {
- UpdatePointer(e);
- e.Pointer.Capture(this);
- e.Handled = true;
- base.OnPointerPressed(e);
- }
-
- protected override void OnPointerMoved(PointerEventArgs e)
- {
- UpdatePointer(e);
- e.Handled = true;
- base.OnPointerMoved(e);
- }
-
- protected override void OnPointerReleased(PointerReleasedEventArgs e)
- {
- _pointers.Remove(e.Pointer);
- e.Handled = true;
- InvalidateVisual();
- }
-
- protected override void OnPointerCaptureLost(PointerCaptureLostEventArgs e)
- {
- _pointers.Remove(e.Pointer);
- InvalidateVisual();
- }
-
- public override void Render(DrawingContext context)
- {
- context.FillRectangle(Brushes.Transparent, new Rect(default, Bounds.Size));
- foreach (var pt in _pointers.Values)
- {
- var brush = new ImmutableSolidColorBrush(pt.Color);
-
- context.DrawEllipse(brush, null, pt.Point, 75, 75);
- }
- }
- }
-
- public class PointerIntermediatePointsTab : Decorator
- {
- public PointerIntermediatePointsTab()
- {
- this[TextElement.ForegroundProperty] = Brushes.Black;
- var slider = new Slider
- {
- Margin = new Thickness(5),
- Minimum = 0,
- Maximum = 500
- };
-
- var status = new TextBlock()
- {
- HorizontalAlignment = HorizontalAlignment.Left,
- VerticalAlignment = VerticalAlignment.Top,
- };
- Child = new Grid
- {
- Children =
- {
- new PointerCanvas(slider, status),
- new Border
- {
- Background = Brushes.LightYellow,
- Child = new StackPanel
- {
- Children =
- {
- new StackPanel
- {
- Orientation = Orientation.Horizontal,
- Children =
- {
- new TextBlock { Text = "Thread sleep:" },
- new TextBlock()
- {
- [!TextBlock.TextProperty] =slider.GetObservable(Slider.ValueProperty)
- .Select(x=>x.ToString()).ToBinding()
- }
- }
- },
- slider
- }
- },
-
- HorizontalAlignment = HorizontalAlignment.Right,
- VerticalAlignment = VerticalAlignment.Top,
- Width = 300,
- Height = 60
- },
- status
- }
- };
- }
-
- class PointerCanvas : Control
- {
- private readonly Slider _slider;
- private readonly TextBlock _status;
- private int _events;
- private Stopwatch _stopwatch = Stopwatch.StartNew();
- private Dictionary _pointers = new();
- class PointerPoints
- {
- struct CanvasPoint
- {
- public IBrush Brush;
- public Point Point;
- public double Radius;
- }
-
- readonly CanvasPoint[] _points = new CanvasPoint[1000];
- int _index;
-
- public void Render(DrawingContext context)
- {
-
- CanvasPoint? prev = null;
- for (var c = 0; c < _points.Length; c++)
- {
- var i = (c + _index) % _points.Length;
- var pt = _points[i];
- if (prev.HasValue && prev.Value.Brush != null && pt.Brush != null)
- context.DrawLine(new Pen(Brushes.Black), prev.Value.Point, pt.Point);
- prev = pt;
- if (pt.Brush != null)
- context.DrawEllipse(pt.Brush, null, pt.Point, pt.Radius, pt.Radius);
-
- }
-
- }
-
- void AddPoint(Point pt, IBrush brush, double radius)
- {
- _points[_index] = new CanvasPoint { Point = pt, Brush = brush, Radius = radius };
- _index = (_index + 1) % _points.Length;
- }
-
- public void HandleEvent(PointerEventArgs e, Visual v)
- {
- e.Handled = true;
- if (e.RoutedEvent == PointerPressedEvent)
- AddPoint(e.GetPosition(v), Brushes.Green, 10);
- else if (e.RoutedEvent == PointerReleasedEvent)
- AddPoint(e.GetPosition(v), Brushes.Red, 10);
- else
- {
- var pts = e.GetIntermediatePoints(v);
- for (var c = 0; c < pts.Count; c++)
- {
- var pt = pts[c];
- AddPoint(pt.Position, c == pts.Count - 1 ? Brushes.Blue : Brushes.Black,
- c == pts.Count - 1 ? 5 : 2);
- }
- }
- }
- }
-
- public PointerCanvas(Slider slider, TextBlock status)
- {
- _slider = slider;
- _status = status;
- DispatcherTimer.Run(() =>
- {
- if (_stopwatch.Elapsed.TotalSeconds > 1)
- {
- _status.Text = "Events per second: " + (_events / _stopwatch.Elapsed.TotalSeconds);
- _stopwatch.Restart();
- _events = 0;
- }
-
- return this.GetVisualRoot() != null;
- }, TimeSpan.FromMilliseconds(10));
- }
-
-
- void HandleEvent(PointerEventArgs e)
- {
- _events++;
- Thread.Sleep((int)_slider.Value);
- InvalidateVisual();
-
- if (e.RoutedEvent == PointerReleasedEvent && e.Pointer.Type == PointerType.Touch)
- {
- _pointers.Remove(e.Pointer.Id);
- return;
- }
-
- if (!_pointers.TryGetValue(e.Pointer.Id, out var pt))
- _pointers[e.Pointer.Id] = pt = new PointerPoints();
- pt.HandleEvent(e, this);
-
-
- }
-
- public override void Render(DrawingContext context)
- {
- context.FillRectangle(Brushes.White, Bounds);
- foreach(var pt in _pointers.Values)
- pt.Render(context);
- base.Render(context);
- }
-
- protected override void OnPointerPressed(PointerPressedEventArgs e)
- {
- if (e.ClickCount == 2)
- {
- _pointers.Clear();
- InvalidateVisual();
- return;
- }
-
- HandleEvent(e);
- base.OnPointerPressed(e);
- }
-
- protected override void OnPointerMoved(PointerEventArgs e)
- {
- HandleEvent(e);
- base.OnPointerMoved(e);
- }
-
- protected override void OnPointerReleased(PointerReleasedEventArgs e)
- {
- HandleEvent(e);
- base.OnPointerReleased(e);
- }
- }
-
- }
-}
diff --git a/samples/ControlCatalog/Pages/PointersPage.xaml b/samples/ControlCatalog/Pages/PointersPage.xaml
new file mode 100644
index 0000000000..c39106f29e
--- /dev/null
+++ b/samples/ControlCatalog/Pages/PointersPage.xaml
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Capture 1
+
+
+ Capture 2
+
+
+
+
+
diff --git a/samples/ControlCatalog/Pages/PointersPage.xaml.cs b/samples/ControlCatalog/Pages/PointersPage.xaml.cs
new file mode 100644
index 0000000000..6fc468e37f
--- /dev/null
+++ b/samples/ControlCatalog/Pages/PointersPage.xaml.cs
@@ -0,0 +1,78 @@
+using System;
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Input;
+using Avalonia.Interactivity;
+using Avalonia.Markup.Xaml;
+
+namespace ControlCatalog.Pages;
+
+public class PointersPage : UserControl
+{
+ public PointersPage()
+ {
+ this.InitializeComponent();
+
+ var border1 = this.Get("BorderCapture1");
+ var border2 = this.Get("BorderCapture2");
+
+ border1.PointerPressed += Border_PointerPressed;
+ border1.PointerReleased += Border_PointerReleased;
+ border1.PointerCaptureLost += Border_PointerCaptureLost;
+ border1.PointerMoved += Border_PointerUpdated;
+ border1.PointerEntered += Border_PointerUpdated;
+ border1.PointerExited += Border_PointerUpdated;
+
+ border2.PointerPressed += Border_PointerPressed;
+ border2.PointerReleased += Border_PointerReleased;
+ border2.PointerCaptureLost += Border_PointerCaptureLost;
+ border2.PointerMoved += Border_PointerUpdated;
+ border2.PointerEntered += Border_PointerUpdated;
+ border2.PointerExited += Border_PointerUpdated;
+ }
+
+ private void Border_PointerUpdated(object sender, PointerEventArgs e)
+ {
+ var textBlock = (TextBlock)((Border)sender).Child;
+ var position = e.GetPosition((Border)sender);
+ textBlock.Text = @$"Type: {e.Pointer.Type}
+Captured: {e.Pointer.Captured == sender}
+PointerId: {e.Pointer.Id}
+Position: {(int)position.X} {(int)position.Y}";
+ e.Handled = true;
+ }
+
+ private void Border_PointerCaptureLost(object sender, PointerCaptureLostEventArgs e)
+ {
+ var textBlock = (TextBlock)((Border)sender).Child;
+ textBlock.Text = @$"Type: {e.Pointer.Type}
+Captured: {e.Pointer.Captured == sender}
+PointerId: {e.Pointer.Id}
+Position: ??? ???";
+ e.Handled = true;
+ }
+
+ private void Border_PointerReleased(object sender, PointerReleasedEventArgs e)
+ {
+ if (e.Pointer.Captured == sender)
+ {
+ e.Pointer.Capture(null);
+ e.Handled = true;
+ }
+ else
+ {
+ throw new InvalidOperationException("How?");
+ }
+ }
+
+ private void Border_PointerPressed(object sender, PointerPressedEventArgs e)
+ {
+ e.Pointer.Capture((Border)sender);
+ e.Handled = true;
+ }
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+}
diff --git a/src/Avalonia.Base/Controls/ResourceDictionary.cs b/src/Avalonia.Base/Controls/ResourceDictionary.cs
index 3af14daa83..77863e5101 100644
--- a/src/Avalonia.Base/Controls/ResourceDictionary.cs
+++ b/src/Avalonia.Base/Controls/ResourceDictionary.cs
@@ -1,38 +1,45 @@
using System;
+using System.Collections;
using System.Collections.Generic;
-using System.Collections.Specialized;
+using System.Linq;
using Avalonia.Collections;
-using Avalonia.Metadata;
-
-#nullable enable
namespace Avalonia.Controls
{
///
/// An indexed dictionary of resources.
///
- public class ResourceDictionary : AvaloniaDictionary