diff --git a/Avalonia.sln b/Avalonia.sln
index 410a7171c7e..124f1e535dd 100644
--- a/Avalonia.sln
+++ b/Avalonia.sln
@@ -223,8 +223,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.ReactiveUI.Events"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sandbox", "samples\Sandbox\Sandbox.csproj", "{11BE52AF-E2DD-4CF0-B19A-05285ACAF571}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MicroComGenerator", "src\tools\MicroComGenerator\MicroComGenerator.csproj", "{AEC9031E-06EA-4A9E-9E7F-7D7C719404DD}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.MicroCom", "src\Avalonia.MicroCom\Avalonia.MicroCom.csproj", "{FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MiniMvvm", "samples\MiniMvvm\MiniMvvm.csproj", "{BC594FD5-4AF2-409E-A1E6-04123F54D7C5}"
@@ -2029,30 +2027,6 @@ Global
{11BE52AF-E2DD-4CF0-B19A-05285ACAF571}.Release|iPhone.Build.0 = Release|Any CPU
{11BE52AF-E2DD-4CF0-B19A-05285ACAF571}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{11BE52AF-E2DD-4CF0-B19A-05285ACAF571}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
- {AEC9031E-06EA-4A9E-9E7F-7D7C719404DD}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
- {AEC9031E-06EA-4A9E-9E7F-7D7C719404DD}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
- {AEC9031E-06EA-4A9E-9E7F-7D7C719404DD}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
- {AEC9031E-06EA-4A9E-9E7F-7D7C719404DD}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
- {AEC9031E-06EA-4A9E-9E7F-7D7C719404DD}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
- {AEC9031E-06EA-4A9E-9E7F-7D7C719404DD}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
- {AEC9031E-06EA-4A9E-9E7F-7D7C719404DD}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
- {AEC9031E-06EA-4A9E-9E7F-7D7C719404DD}.AppStore|Any CPU.Build.0 = Debug|Any CPU
- {AEC9031E-06EA-4A9E-9E7F-7D7C719404DD}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
- {AEC9031E-06EA-4A9E-9E7F-7D7C719404DD}.AppStore|iPhone.Build.0 = Debug|Any CPU
- {AEC9031E-06EA-4A9E-9E7F-7D7C719404DD}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
- {AEC9031E-06EA-4A9E-9E7F-7D7C719404DD}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
- {AEC9031E-06EA-4A9E-9E7F-7D7C719404DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {AEC9031E-06EA-4A9E-9E7F-7D7C719404DD}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {AEC9031E-06EA-4A9E-9E7F-7D7C719404DD}.Debug|iPhone.ActiveCfg = Debug|Any CPU
- {AEC9031E-06EA-4A9E-9E7F-7D7C719404DD}.Debug|iPhone.Build.0 = Debug|Any CPU
- {AEC9031E-06EA-4A9E-9E7F-7D7C719404DD}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
- {AEC9031E-06EA-4A9E-9E7F-7D7C719404DD}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
- {AEC9031E-06EA-4A9E-9E7F-7D7C719404DD}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {AEC9031E-06EA-4A9E-9E7F-7D7C719404DD}.Release|Any CPU.Build.0 = Release|Any CPU
- {AEC9031E-06EA-4A9E-9E7F-7D7C719404DD}.Release|iPhone.ActiveCfg = Release|Any CPU
- {AEC9031E-06EA-4A9E-9E7F-7D7C719404DD}.Release|iPhone.Build.0 = Release|Any CPU
- {AEC9031E-06EA-4A9E-9E7F-7D7C719404DD}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
- {AEC9031E-06EA-4A9E-9E7F-7D7C719404DD}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
@@ -2255,7 +2229,6 @@ Global
{3C84E04B-36CF-4D0D-B965-C26DD649D1F3} = {A0CC0258-D18C-4AB3-854F-7101680FC3F9}
{909A8CBD-7D0E-42FD-B841-022AD8925820} = {8B6A8209-894F-4BA1-B880-965FD453982C}
{11BE52AF-E2DD-4CF0-B19A-05285ACAF571} = {9B9E3891-2366-4253-A952-D08BCEB71098}
- {AEC9031E-06EA-4A9E-9E7F-7D7C719404DD} = {4ED8B739-6F4E-4CD4-B993-545E6B5CE637}
{BC594FD5-4AF2-409E-A1E6-04123F54D7C5} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{25831348-EB2A-483E-9576-E8F6528674A5} = {86A3F706-DC3C-43C6-BE1B-B98F5BAAA268}
{C08E9894-AA92-426E-BF56-033E262CAD3E} = {9B9E3891-2366-4253-A952-D08BCEB71098}
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 903146cdd7d..497402fe4bb 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -58,8 +58,10 @@ jobs:
displayName: 'Generate avalonia-native'
inputs:
script: |
- export PATH="`pwd`/sdk:$PATH"
- cd src/tools/MicroComGenerator; dotnet run -f net6.0 -i ../../Avalonia.Native/avn.idl --cpp ../../../native/Avalonia.Native/inc/avalonia-native.h
+ export COREHOST_TRACE=0
+ export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1
+ export DOTNET_CLI_TELEMETRY_OPTOUT=1
+ ./build.sh --target GenerateCppHeaders --configuration Release
- task: Xcode@5
inputs:
diff --git a/build/MicroCom.targets b/build/MicroCom.targets
deleted file mode 100644
index 1ed388f689e..00000000000
--- a/build/MicroCom.targets
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
- false
- all
- true
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- <_AvaloniaPatchComInterop>true
-
-
-
diff --git a/build/SharedVersion.props b/build/SharedVersion.props
index 38e1ed42438..4316d91a54f 100644
--- a/build/SharedVersion.props
+++ b/build/SharedVersion.props
@@ -2,7 +2,7 @@
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
Avalonia
- 0.10.12
+ 0.10.13
Copyright 2022 © The AvaloniaUI Project
https://avaloniaui.net
https://github.com/AvaloniaUI/Avalonia/
@@ -11,7 +11,7 @@
latest
MIT
Icon.png
- Avalonia is a WPF/UWP-inspired cross-platform XAML-based UI framework providing a flexible styling system and supporting a wide range of Operating Systems such as Windows (.NET Framework, .NET Core), Linux (via Xorg), MacOS and with experimental support for Android and iOS.
+ Avalonia is a cross-platform UI framework for .NET providing a flexible styling system and supporting a wide range of Operating Systems such as Windows, Linux, MacOS and with experimental support for Android, iOS and WebAssembly.
avalonia;avaloniaui;mvvm;rx;reactive extensions;android;ios;mac;forms;wpf;net;netstandard;net461;uwp;xamarin
https://github.com/AvaloniaUI/Avalonia/releases
git
diff --git a/build/System.Drawing.Common.props b/build/System.Drawing.Common.props
index 2b3707d38a9..108a0f41e04 100644
--- a/build/System.Drawing.Common.props
+++ b/build/System.Drawing.Common.props
@@ -1,5 +1,6 @@
-
+
+
diff --git a/nukebuild/MicroComGen.cs b/nukebuild/MicroComGen.cs
index 06c8acbf238..b1e546cb97b 100644
--- a/nukebuild/MicroComGen.cs
+++ b/nukebuild/MicroComGen.cs
@@ -1,14 +1,14 @@
using System.IO;
-using MicroComGenerator;
+using MicroCom.CodeGenerator;
using Nuke.Common;
partial class Build : NukeBuild
{
Target GenerateCppHeaders => _ => _.Executes(() =>
{
- var text = File.ReadAllText(RootDirectory / "src" / "Avalonia.Native" / "avn.idl");
- var ast = AstParser.Parse(text);
+ var file = MicroComCodeGenerator.Parse(
+ File.ReadAllText(RootDirectory / "src" / "Avalonia.Native" / "avn.idl"));
File.WriteAllText(RootDirectory / "native" / "Avalonia.Native" / "inc" / "avalonia-native.h",
- CppGen.GenerateCpp(ast));
+ file.GenerateCppHeader());
});
}
\ No newline at end of file
diff --git a/nukebuild/_build.csproj b/nukebuild/_build.csproj
index a6cb86a9bdb..af087e3180e 100644
--- a/nukebuild/_build.csproj
+++ b/nukebuild/_build.csproj
@@ -15,7 +15,7 @@
-
+
@@ -38,10 +38,6 @@
-
- MicroComGenerator\%(Filename)%(Extension)
-
-
diff --git a/samples/ControlCatalog/Pages/PointersPage.cs b/samples/ControlCatalog/Pages/PointersPage.cs
index fddc503a905..2901013cea3 100644
--- a/samples/ControlCatalog/Pages/PointersPage.cs
+++ b/samples/ControlCatalog/Pages/PointersPage.cs
@@ -1,15 +1,37 @@
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.Input;
+using Avalonia.Layout;
using Avalonia.Media;
using Avalonia.Media.Immutable;
+using Avalonia.Threading;
+using Avalonia.VisualTree;
-namespace ControlCatalog.Pages
+namespace ControlCatalog.Pages;
+
+public class PointersPage : Decorator
{
- public class PointersPage : Control
+ 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
{
@@ -45,7 +67,7 @@ class PointerInfo
private Dictionary _pointers = new Dictionary();
- public PointersPage()
+ public PointerContactsTab()
{
ClipToBounds = true;
}
@@ -104,4 +126,196 @@ public override void Render(DrawingContext context)
}
}
}
+
+ public class PointerIntermediatePointsTab : Decorator
+ {
+ public PointerIntermediatePointsTab()
+ {
+ this[TextBlock.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/ViewboxPage.xaml b/samples/ControlCatalog/Pages/ViewboxPage.xaml
index 81e5046636b..e7e3007d355 100644
--- a/samples/ControlCatalog/Pages/ViewboxPage.xaml
+++ b/samples/ControlCatalog/Pages/ViewboxPage.xaml
@@ -1,5 +1,6 @@
@@ -12,8 +13,8 @@
+ Stretch="{Binding #StretchSelector.SelectedItem, FallbackValue={x:Static Stretch.Uniform}}"
+ StretchDirection="{Binding #StretchDirectionSelector.SelectedItem, FallbackValue={x:Static StretchDirection.Both}}">
@@ -25,9 +26,22 @@
-
+
+
+ Uniform
+ UniformToFill
+ Fill
+ None
+
+
-
+
+
+ Both
+ DownOnly
+ UpOnly
+
+
diff --git a/samples/ControlCatalog/Pages/ViewboxPage.xaml.cs b/samples/ControlCatalog/Pages/ViewboxPage.xaml.cs
index 94b3f3ea14c..12b5086e12b 100644
--- a/samples/ControlCatalog/Pages/ViewboxPage.xaml.cs
+++ b/samples/ControlCatalog/Pages/ViewboxPage.xaml.cs
@@ -1,6 +1,5 @@
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
-using Avalonia.Media;
namespace ControlCatalog.Pages
{
@@ -9,24 +8,6 @@ public class ViewboxPage : UserControl
public ViewboxPage()
{
InitializeComponent();
-
- var stretchSelector = this.FindControl("StretchSelector");
-
- stretchSelector.Items = new[]
- {
- Stretch.Uniform, Stretch.UniformToFill, Stretch.Fill, Stretch.None
- };
-
- stretchSelector.SelectedIndex = 0;
-
- var stretchDirectionSelector = this.FindControl("StretchDirectionSelector");
-
- stretchDirectionSelector.Items = new[]
- {
- StretchDirection.Both, StretchDirection.DownOnly, StretchDirection.UpOnly
- };
-
- stretchDirectionSelector.SelectedIndex = 0;
}
private void InitializeComponent()
diff --git a/samples/RenderDemo/Pages/ClippingPage.xaml b/samples/RenderDemo/Pages/ClippingPage.xaml
index 10225f7c491..698e18d496f 100644
--- a/samples/RenderDemo/Pages/ClippingPage.xaml
+++ b/samples/RenderDemo/Pages/ClippingPage.xaml
@@ -19,30 +19,36 @@
-
+
+
+
+
diff --git a/samples/RenderDemo/Pages/ClippingPage.xaml.cs b/samples/RenderDemo/Pages/ClippingPage.xaml.cs
index 5357181838c..c5b669343a5 100644
--- a/samples/RenderDemo/Pages/ClippingPage.xaml.cs
+++ b/samples/RenderDemo/Pages/ClippingPage.xaml.cs
@@ -1,35 +1,18 @@
-using System;
-using System.Reactive.Linq;
-using Avalonia;
-using Avalonia.Animation;
using Avalonia.Controls;
-using Avalonia.Data;
using Avalonia.Markup.Xaml;
-using Avalonia.Media;
namespace RenderDemo.Pages
{
public class ClippingPage : UserControl
{
- private Geometry _clip;
-
public ClippingPage()
{
InitializeComponent();
- WireUpCheckbox();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
-
- private void WireUpCheckbox()
- {
- var useMask = this.FindControl("useMask");
- var clipped = this.FindControl("clipped");
- _clip = clipped.Clip;
- useMask.Click += (s, e) => clipped.Clip = clipped.Clip == null ? _clip : null;
- }
}
}
diff --git a/src/Avalonia.Base/Threading/Dispatcher.cs b/src/Avalonia.Base/Threading/Dispatcher.cs
index 949c11fbe0b..65507e5310c 100644
--- a/src/Avalonia.Base/Threading/Dispatcher.cs
+++ b/src/Avalonia.Base/Threading/Dispatcher.cs
@@ -78,6 +78,13 @@ public void RunJobs()
///
///
public void RunJobs(DispatcherPriority minimumPriority) => _jobRunner.RunJobs(minimumPriority);
+
+ ///
+ /// Use this method to check if there are more prioritized tasks
+ ///
+ ///
+ public bool HasJobsWithPriority(DispatcherPriority minimumPriority) =>
+ _jobRunner.HasJobsWithPriority(minimumPriority);
///
public Task InvokeAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal)
diff --git a/src/Avalonia.Base/Threading/JobRunner.cs b/src/Avalonia.Base/Threading/JobRunner.cs
index eb7c5e1af31..99ef511a019 100644
--- a/src/Avalonia.Base/Threading/JobRunner.cs
+++ b/src/Avalonia.Base/Threading/JobRunner.cs
@@ -109,7 +109,22 @@ private void AddJob(IJob job)
}
return null;
}
-
+
+ public bool HasJobsWithPriority(DispatcherPriority minimumPriority)
+ {
+ for (int c = (int)minimumPriority; c < (int)DispatcherPriority.MaxValue; c++)
+ {
+ var q = _queues[c];
+ lock (q)
+ {
+ if (q.Count > 0)
+ return true;
+ }
+ }
+
+ return false;
+ }
+
private interface IJob
{
///
diff --git a/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs b/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs
index a856ee071ca..1d19393c774 100644
--- a/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs
+++ b/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs
@@ -185,7 +185,9 @@ public class CalendarDatePicker : TemplatedControl
AvaloniaProperty.RegisterDirect(
nameof(SelectedDate),
o => o.SelectedDate,
- (o, v) => o.SelectedDate = v);
+ (o, v) => o.SelectedDate = v,
+ enableDataValidation: true,
+ defaultBindingMode:BindingMode.TwoWay);
public static readonly StyledProperty SelectedDateFormatProperty =
AvaloniaProperty.Register(
diff --git a/src/Avalonia.Controls/DateTimePickers/DatePicker.cs b/src/Avalonia.Controls/DateTimePickers/DatePicker.cs
index 5893a02b04a..771f8a176b6 100644
--- a/src/Avalonia.Controls/DateTimePickers/DatePicker.cs
+++ b/src/Avalonia.Controls/DateTimePickers/DatePicker.cs
@@ -4,6 +4,7 @@
using Avalonia.Controls.Templates;
using Avalonia.Data;
using Avalonia.Interactivity;
+using Avalonia.Layout;
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -398,18 +399,27 @@ private void SetSelectedDateText()
private void OnFlyoutButtonClicked(object sender, RoutedEventArgs e)
{
if (_presenter == null)
- throw new InvalidOperationException("No DatePickerPresenter found");
+ throw new InvalidOperationException("No DatePickerPresenter found.");
+ if (_popup == null)
+ throw new InvalidOperationException("No Popup found.");
_presenter.Date = SelectedDate ?? DateTimeOffset.Now;
+ _popup.PlacementMode = PlacementMode.AnchorAndGravity;
+ _popup.PlacementAnchor = Primitives.PopupPositioning.PopupAnchor.Bottom;
+ _popup.PlacementGravity = Primitives.PopupPositioning.PopupGravity.Bottom;
+ _popup.PlacementConstraintAdjustment = Primitives.PopupPositioning.PopupPositionerConstraintAdjustment.SlideY;
_popup.IsOpen = true;
+ // Overlay popup hosts won't get measured until the next layout pass, but we need the
+ // template to be applied to `_presenter` now. Detect this case and force a layout pass.
+ if (!_presenter.IsMeasureValid)
+ (VisualRoot as ILayoutRoot)?.LayoutManager?.ExecuteInitialLayoutPass();
+
var deltaY = _presenter.GetOffsetForPopup();
// The extra 5 px I think is related to default popup placement behavior
- _popup.Host.ConfigurePosition(_popup.PlacementTarget, PlacementMode.AnchorAndGravity, new Point(0, deltaY + 5),
- Primitives.PopupPositioning.PopupAnchor.Bottom, Primitives.PopupPositioning.PopupGravity.Bottom,
- Primitives.PopupPositioning.PopupPositionerConstraintAdjustment.SlideY);
+ _popup.VerticalOffset = deltaY + 5;
}
protected virtual void OnSelectedDateChanged(object sender, DatePickerSelectedValueChangedEventArgs e)
diff --git a/src/Avalonia.Controls/DateTimePickers/DatePickerPresenter.cs b/src/Avalonia.Controls/DateTimePickers/DatePickerPresenter.cs
index eec4615736f..91375e4eafc 100644
--- a/src/Avalonia.Controls/DateTimePickers/DatePickerPresenter.cs
+++ b/src/Avalonia.Controls/DateTimePickers/DatePickerPresenter.cs
@@ -534,6 +534,9 @@ private void OnMonthChanged(object sender, EventArgs e)
internal double GetOffsetForPopup()
{
+ if (_monthSelector is null)
+ return 0;
+
var acceptDismissButtonHeight = _acceptButton != null ? _acceptButton.Bounds.Height : 41;
return -(MaxHeight - acceptDismissButtonHeight) / 2 - (_monthSelector.ItemHeight / 2);
}
diff --git a/src/Avalonia.Controls/DateTimePickers/TimePicker.cs b/src/Avalonia.Controls/DateTimePickers/TimePicker.cs
index 6b3f66912fd..2921984f082 100644
--- a/src/Avalonia.Controls/DateTimePickers/TimePicker.cs
+++ b/src/Avalonia.Controls/DateTimePickers/TimePicker.cs
@@ -3,6 +3,7 @@
using Avalonia.Controls.Shapes;
using Avalonia.Controls.Templates;
using Avalonia.Data;
+using Avalonia.Layout;
using System;
using System.Globalization;
@@ -254,16 +255,28 @@ protected virtual void OnSelectedTimeChanged(TimeSpan? oldTime, TimeSpan? newTim
private void OnFlyoutButtonClicked(object sender, Interactivity.RoutedEventArgs e)
{
+ if (_presenter == null)
+ throw new InvalidOperationException("No DatePickerPresenter found.");
+ if (_popup == null)
+ throw new InvalidOperationException("No Popup found.");
+
_presenter.Time = SelectedTime ?? DateTime.Now.TimeOfDay;
+ _popup.PlacementMode = PlacementMode.AnchorAndGravity;
+ _popup.PlacementAnchor = Primitives.PopupPositioning.PopupAnchor.Bottom;
+ _popup.PlacementGravity = Primitives.PopupPositioning.PopupGravity.Bottom;
+ _popup.PlacementConstraintAdjustment = Primitives.PopupPositioning.PopupPositionerConstraintAdjustment.SlideY;
_popup.IsOpen = true;
+ // Overlay popup hosts won't get measured until the next layout pass, but we need the
+ // template to be applied to `_presenter` now. Detect this case and force a layout pass.
+ if (!_presenter.IsMeasureValid)
+ (VisualRoot as ILayoutRoot)?.LayoutManager?.ExecuteInitialLayoutPass();
+
var deltaY = _presenter.GetOffsetForPopup();
// The extra 5 px I think is related to default popup placement behavior
- _popup.Host.ConfigurePosition(_popup.PlacementTarget, PlacementMode.AnchorAndGravity, new Point(0, deltaY + 5),
- Primitives.PopupPositioning.PopupAnchor.Bottom, Primitives.PopupPositioning.PopupGravity.Bottom,
- Primitives.PopupPositioning.PopupPositionerConstraintAdjustment.SlideY);
+ _popup.VerticalOffset = deltaY + 5;
}
private void OnDismissPicker(object sender, EventArgs e)
diff --git a/src/Avalonia.Controls/DateTimePickers/TimePickerPresenter.cs b/src/Avalonia.Controls/DateTimePickers/TimePickerPresenter.cs
index 920eeeb4063..ab9393ed4e7 100644
--- a/src/Avalonia.Controls/DateTimePickers/TimePickerPresenter.cs
+++ b/src/Avalonia.Controls/DateTimePickers/TimePickerPresenter.cs
@@ -253,6 +253,9 @@ private void OnSelectorButtonClick(object sender, RoutedEventArgs e)
internal double GetOffsetForPopup()
{
+ if (_hourSelector is null)
+ return 0;
+
var acceptDismissButtonHeight = _acceptButton != null ? _acceptButton.Bounds.Height : 41;
return -(MaxHeight - acceptDismissButtonHeight) / 2 - (_hourSelector.ItemHeight / 2);
}
diff --git a/src/Avalonia.Controls/TransitioningContentControl.cs b/src/Avalonia.Controls/TransitioningContentControl.cs
new file mode 100644
index 00000000000..cb0d2291108
--- /dev/null
+++ b/src/Avalonia.Controls/TransitioningContentControl.cs
@@ -0,0 +1,96 @@
+using System;
+using System.Threading;
+using Avalonia.Animation;
+using Avalonia.Controls.Templates;
+using Avalonia.Threading;
+
+namespace Avalonia.Controls;
+
+///
+/// Displays according to a .
+/// Uses to move between the old and new content values.
+///
+public class TransitioningContentControl : ContentControl
+{
+ private CancellationTokenSource? _lastTransitionCts;
+ private object? _currentContent;
+
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty PageTransitionProperty =
+ AvaloniaProperty.Register(nameof(PageTransition),
+ new CrossFade(TimeSpan.FromSeconds(0.125)));
+
+ ///
+ /// Defines the property.
+ ///
+ public static readonly DirectProperty CurrentContentProperty =
+ AvaloniaProperty.RegisterDirect(nameof(CurrentContent),
+ o => o.CurrentContent);
+
+ ///
+ /// Gets or sets the animation played when content appears and disappears.
+ ///
+ public IPageTransition? PageTransition
+ {
+ get => GetValue(PageTransitionProperty);
+ set => SetValue(PageTransitionProperty, value);
+ }
+
+ ///
+ /// Gets the content currently displayed on the screen.
+ ///
+ public object? CurrentContent
+ {
+ get => _currentContent;
+ private set => SetAndRaise(CurrentContentProperty, ref _currentContent, value);
+ }
+
+ protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
+ {
+ base.OnAttachedToVisualTree(e);
+
+ Dispatcher.UIThread.Post(() => UpdateContentWithTransition(Content));
+ }
+
+ protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
+ {
+ base.OnDetachedFromVisualTree(e);
+
+ _lastTransitionCts?.Cancel();
+ }
+
+ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
+ {
+ base.OnPropertyChanged(change);
+
+ if (change.Property == ContentProperty)
+ {
+ Dispatcher.UIThread.Post(() => UpdateContentWithTransition(Content));
+ }
+ }
+
+ ///
+ /// Updates the content with transitions.
+ ///
+ /// New content to set.
+ private async void UpdateContentWithTransition(object? content)
+ {
+ if (VisualRoot is null)
+ {
+ return;
+ }
+
+ _lastTransitionCts?.Cancel();
+ _lastTransitionCts = new CancellationTokenSource();
+
+ if (PageTransition != null)
+ await PageTransition.Start(this, null, true, _lastTransitionCts.Token);
+
+ CurrentContent = content;
+
+ if (PageTransition != null)
+ await PageTransition.Start(null, this, true, _lastTransitionCts.Token);
+ }
+}
diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs
index 4c94b725ea3..dec1f4bb23c 100644
--- a/src/Avalonia.Controls/Window.cs
+++ b/src/Avalonia.Controls/Window.cs
@@ -255,6 +255,11 @@ public Window(IWindowImpl impl)
[CanBeNull]
public new IWindowImpl PlatformImpl => (IWindowImpl)base.PlatformImpl;
+ ///
+ /// Gets a collection of child windows owned by this window.
+ ///
+ public IReadOnlyList OwnedWindows => _children.Select(x => x.child).ToList();
+
///
/// Gets or sets a value indicating how the window will size itself to fit its content.
///
@@ -854,6 +859,17 @@ private void OnGotInputWhenDisabled()
private void SetWindowStartupLocation(IWindowBaseImpl owner = null)
{
+ var startupLocation = WindowStartupLocation;
+
+ if (startupLocation == WindowStartupLocation.CenterOwner &&
+ Owner is Window ownerWindow &&
+ ownerWindow.WindowState == WindowState.Minimized)
+ {
+ // If startup location is CenterOwner, but owner is minimized then fall back
+ // to CenterScreen. This behavior is consistent with WPF.
+ startupLocation = WindowStartupLocation.CenterScreen;
+ }
+
var scaling = owner?.DesktopScaling ?? PlatformImpl?.DesktopScaling ?? 1;
// TODO: We really need non-client size here.
@@ -861,7 +877,7 @@ private void SetWindowStartupLocation(IWindowBaseImpl owner = null)
PixelPoint.Origin,
PixelSize.FromSize(ClientSize, scaling));
- if (WindowStartupLocation == WindowStartupLocation.CenterScreen)
+ if (startupLocation == WindowStartupLocation.CenterScreen)
{
var screen = Screens.ScreenFromPoint(owner?.Position ?? Position);
@@ -870,7 +886,7 @@ private void SetWindowStartupLocation(IWindowBaseImpl owner = null)
Position = screen.WorkingArea.CenterRect(rect).Position;
}
}
- else if (WindowStartupLocation == WindowStartupLocation.CenterOwner)
+ else if (startupLocation == WindowStartupLocation.CenterOwner)
{
if (owner != null)
{
diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/VisualTreeNode.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/VisualTreeNode.cs
index 6a430897bab..81a19d05500 100644
--- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/VisualTreeNode.cs
+++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/VisualTreeNode.cs
@@ -77,7 +77,8 @@ public override void Dispose()
c.GetObservable(Control.ContextMenuProperty),
c.GetObservable(FlyoutBase.AttachedFlyoutProperty),
c.GetObservable(ToolTipDiagnostics.ToolTipProperty),
- (ContextFlyout, ContextMenu, AttachedFlyout, ToolTip) =>
+ c.GetObservable(Button.FlyoutProperty),
+ (ContextFlyout, ContextMenu, AttachedFlyout, ToolTip, ButtonFlyout) =>
{
if (ContextMenu != null)
//Note: ContextMenus are special since all the items are added as visual children.
@@ -93,6 +94,9 @@ public override void Dispose()
if (ToolTip != null)
return GetPopupHostObservable(ToolTip, "ToolTip");
+ if (ButtonFlyout != null)
+ return GetPopupHostObservable(ButtonFlyout, "Flyout");
+
return Observable.Return(null);
})
.Switch(),
diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs b/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs
index 62bfdb5186c..4a661b9cbdc 100644
--- a/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs
+++ b/src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml.cs
@@ -147,6 +147,7 @@ void ProcessProperty(IControl control, AvaloniaProperty property)
ProcessProperty(control, ContextMenuProperty);
ProcessProperty(control, FlyoutBase.AttachedFlyoutProperty);
ProcessProperty(control, ToolTipDiagnostics.ToolTipProperty);
+ ProcessProperty(control, Button.FlyoutProperty);
}
return popupRoots;
@@ -162,7 +163,9 @@ private void RawKeyDown(RawKeyEventArgs e)
switch (e.Modifiers)
{
- case RawInputModifiers.Control | RawInputModifiers.Shift:
+ case RawInputModifiers.Control when (e.Key == Key.LeftShift || e.Key == Key.RightShift):
+ case RawInputModifiers.Shift when (e.Key == Key.LeftCtrl || e.Key == Key.RightCtrl):
+ case RawInputModifiers.Shift | RawInputModifiers.Control:
{
IControl? control = null;
diff --git a/src/Avalonia.Input/MouseDevice.cs b/src/Avalonia.Input/MouseDevice.cs
index b377df0dc47..268129c05a6 100644
--- a/src/Avalonia.Input/MouseDevice.cs
+++ b/src/Avalonia.Input/MouseDevice.cs
@@ -1,9 +1,11 @@
using System;
+using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
using Avalonia.Input.Raw;
using Avalonia.Interactivity;
using Avalonia.Platform;
+using Avalonia.Utilities;
using Avalonia.VisualTree;
namespace Avalonia.Input
@@ -159,7 +161,7 @@ private void ProcessRawEvent(RawPointerEventArgs e)
case RawPointerEventType.XButton1Down:
case RawPointerEventType.XButton2Down:
if (ButtonCount(props) > 1)
- e.Handled = MouseMove(mouse, e.Timestamp, e.Root, e.Position, props, keyModifiers);
+ e.Handled = MouseMove(mouse, e.Timestamp, e.Root, e.Position, props, keyModifiers, e.IntermediatePoints);
else
e.Handled = MouseDown(mouse, e.Timestamp, e.Root, e.Position,
props, keyModifiers);
@@ -170,12 +172,12 @@ private void ProcessRawEvent(RawPointerEventArgs e)
case RawPointerEventType.XButton1Up:
case RawPointerEventType.XButton2Up:
if (ButtonCount(props) != 0)
- e.Handled = MouseMove(mouse, e.Timestamp, e.Root, e.Position, props, keyModifiers);
+ e.Handled = MouseMove(mouse, e.Timestamp, e.Root, e.Position, props, keyModifiers, e.IntermediatePoints);
else
e.Handled = MouseUp(mouse, e.Timestamp, e.Root, e.Position, props, keyModifiers);
break;
case RawPointerEventType.Move:
- e.Handled = MouseMove(mouse, e.Timestamp, e.Root, e.Position, props, keyModifiers);
+ e.Handled = MouseMove(mouse, e.Timestamp, e.Root, e.Position, props, keyModifiers, e.IntermediatePoints);
break;
case RawPointerEventType.Wheel:
e.Handled = MouseWheel(mouse, e.Timestamp, e.Root, e.Position, props, ((RawMouseWheelEventArgs)e).Delta, keyModifiers);
@@ -263,7 +265,7 @@ private bool MouseDown(IMouseDevice device, ulong timestamp, IInputElement root,
}
private bool MouseMove(IMouseDevice device, ulong timestamp, IInputRoot root, Point p, PointerPointProperties properties,
- KeyModifiers inputModifiers)
+ KeyModifiers inputModifiers, Lazy?>? intermediatePoints)
{
device = device ?? throw new ArgumentNullException(nameof(device));
root = root ?? throw new ArgumentNullException(nameof(root));
@@ -283,7 +285,7 @@ private bool MouseMove(IMouseDevice device, ulong timestamp, IInputRoot root, Po
if (source is object)
{
var e = new PointerEventArgs(InputElement.PointerMovedEvent, source, _pointer, root,
- p, timestamp, properties, inputModifiers);
+ p, timestamp, properties, inputModifiers, intermediatePoints);
source.RaiseEvent(e);
return e.Handled;
@@ -324,6 +326,13 @@ private bool MouseWheel(IMouseDevice device, ulong timestamp, IInputRoot root, P
var hit = HitTest(root, p);
var source = GetSource(hit);
+ // KeyModifiers.Shift should scroll in horizontal direction. This does not work on every platform.
+ // If Shift-Key is pressed and X is close to 0 we swap the Vector.
+ if (inputModifiers == KeyModifiers.Shift && MathUtilities.IsZero(delta.X))
+ {
+ delta = new Vector(delta.Y, delta.X);
+ }
+
if (source is not null)
{
var e = new PointerWheelEventArgs(source, _pointer, root, p, timestamp, props, inputModifiers, delta);
diff --git a/src/Avalonia.Input/PointerEventArgs.cs b/src/Avalonia.Input/PointerEventArgs.cs
index 8c86cd46374..0604d09dc4e 100644
--- a/src/Avalonia.Input/PointerEventArgs.cs
+++ b/src/Avalonia.Input/PointerEventArgs.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using Avalonia.Input.Raw;
using Avalonia.Interactivity;
using Avalonia.VisualTree;
@@ -10,6 +11,7 @@ public class PointerEventArgs : RoutedEventArgs
private readonly IVisual? _rootVisual;
private readonly Point _rootVisualPosition;
private readonly PointerPointProperties _properties;
+ private Lazy?>? _previousPoints;
public PointerEventArgs(RoutedEvent routedEvent,
IInteractive? source,
@@ -28,6 +30,20 @@ public PointerEventArgs(RoutedEvent routedEvent,
Timestamp = timestamp;
KeyModifiers = modifiers;
}
+
+ public PointerEventArgs(RoutedEvent routedEvent,
+ IInteractive? source,
+ IPointer pointer,
+ IVisual? rootVisual, Point rootVisualPosition,
+ ulong timestamp,
+ PointerPointProperties properties,
+ KeyModifiers modifiers,
+ Lazy?>? previousPoints)
+ : this(routedEvent, source, pointer, rootVisual, rootVisualPosition, timestamp, properties, modifiers)
+ {
+ _previousPoints = previousPoints;
+ }
+
class EmulatedDevice : IPointerDevice
{
@@ -76,14 +92,16 @@ public InputModifiers InputModifiers
public KeyModifiers KeyModifiers { get; }
- public Point GetPosition(IVisual? relativeTo)
+ private Point GetPosition(Point pt, IVisual? relativeTo)
{
if (_rootVisual == null)
return default;
if (relativeTo == null)
- return _rootVisualPosition;
- return _rootVisualPosition * _rootVisual.TransformToVisual(relativeTo) ?? default;
+ return pt;
+ return pt * _rootVisual.TransformToVisual(relativeTo) ?? default;
}
+
+ public Point GetPosition(IVisual? relativeTo) => GetPosition(_rootVisualPosition, relativeTo);
[Obsolete("Use GetCurrentPoint")]
public PointerPoint GetPointerPoint(IVisual? relativeTo) => GetCurrentPoint(relativeTo);
@@ -96,6 +114,27 @@ public Point GetPosition(IVisual? relativeTo)
public PointerPoint GetCurrentPoint(IVisual? relativeTo)
=> new PointerPoint(Pointer, GetPosition(relativeTo), _properties);
+ ///
+ /// Returns the PointerPoint associated with the current event
+ ///
+ /// The visual which coordinate system to use. Pass null for toplevel coordinate system
+ ///
+ public IReadOnlyList GetIntermediatePoints(IVisual? relativeTo)
+ {
+ var previousPoints = _previousPoints?.Value;
+ if (previousPoints == null || previousPoints.Count == 0)
+ return new[] { GetCurrentPoint(relativeTo) };
+ var points = new PointerPoint[previousPoints.Count + 1];
+ for (var c = 0; c < previousPoints.Count; c++)
+ {
+ var pt = previousPoints[c];
+ points[c] = new PointerPoint(Pointer, GetPosition(pt.Position, relativeTo), _properties);
+ }
+
+ points[points.Length - 1] = GetCurrentPoint(relativeTo);
+ return points;
+ }
+
///
/// Returns the current pointer point properties
///
diff --git a/src/Avalonia.Input/Raw/RawInputEventArgs.cs b/src/Avalonia.Input/Raw/RawInputEventArgs.cs
index dcc5f27a79f..3a5ae1340f2 100644
--- a/src/Avalonia.Input/Raw/RawInputEventArgs.cs
+++ b/src/Avalonia.Input/Raw/RawInputEventArgs.cs
@@ -51,6 +51,6 @@ public RawInputEventArgs(IInputDevice device, ulong timestamp, IInputRoot root)
///
/// Gets the timestamp associated with the event.
///
- public ulong Timestamp { get; private set; }
+ public ulong Timestamp { get; set; }
}
}
diff --git a/src/Avalonia.Input/Raw/RawPointerEventArgs.cs b/src/Avalonia.Input/Raw/RawPointerEventArgs.cs
index 62a1dd5d846..e81f32c3aff 100644
--- a/src/Avalonia.Input/Raw/RawPointerEventArgs.cs
+++ b/src/Avalonia.Input/Raw/RawPointerEventArgs.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
namespace Avalonia.Input.Raw
{
@@ -29,6 +30,8 @@ public enum RawPointerEventType
///
public class RawPointerEventArgs : RawInputEventArgs
{
+ private RawPointerPoint _point;
+
///
/// Initializes a new instance of the class.
///
@@ -54,11 +57,50 @@ public RawPointerEventArgs(
Type = type;
InputModifiers = inputModifiers;
}
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The associated device.
+ /// The event timestamp.
+ /// The root from which the event originates.
+ /// The type of the event.
+ /// The point properties and position, in client DIPs.
+ /// The input modifiers.
+ public RawPointerEventArgs(
+ IInputDevice device,
+ ulong timestamp,
+ IInputRoot root,
+ RawPointerEventType type,
+ RawPointerPoint point,
+ RawInputModifiers inputModifiers)
+ : base(device, timestamp, root)
+ {
+ Contract.Requires(device != null);
+ Contract.Requires(root != null);
+
+ Point = point;
+ Type = type;
+ InputModifiers = inputModifiers;
+ }
+
+ ///
+ /// Gets the pointer properties and position, in client DIPs.
+ ///
+ public RawPointerPoint Point
+ {
+ get => _point;
+ set => _point = value;
+ }
///
/// Gets the mouse position, in client DIPs.
///
- public Point Position { get; set; }
+ public Point Position
+ {
+ get => _point.Position;
+ set => _point.Position = value;
+ }
///
/// Gets the type of the event.
@@ -68,6 +110,25 @@ public RawPointerEventArgs(
///
/// Gets the input modifiers.
///
- public RawInputModifiers InputModifiers { get; private set; }
+ public RawInputModifiers InputModifiers { get; set; }
+
+ ///
+ /// Points that were traversed by a pointer since the previous relevant event,
+ /// only valid for Move and TouchUpdate
+ ///
+ public Lazy?>? IntermediatePoints { get; set; }
+ }
+
+ public struct RawPointerPoint
+ {
+ ///
+ /// Pointer position, in client DIPs.
+ ///
+ public Point Position { get; set; }
+
+ public RawPointerPoint()
+ {
+ Position = default;
+ }
}
}
diff --git a/src/Avalonia.Input/TouchDevice.cs b/src/Avalonia.Input/TouchDevice.cs
index af8851a82b1..f4b7b871cba 100644
--- a/src/Avalonia.Input/TouchDevice.cs
+++ b/src/Avalonia.Input/TouchDevice.cs
@@ -107,7 +107,7 @@ public void ProcessRawEvent(RawInputEventArgs ev)
target.RaiseEvent(new PointerEventArgs(InputElement.PointerMovedEvent, target, pointer, args.Root,
args.Position, ev.Timestamp,
new PointerPointProperties(GetModifiers(args.InputModifiers, true), PointerUpdateKind.Other),
- GetKeyModifiers(args.InputModifiers)));
+ GetKeyModifiers(args.InputModifiers), args.IntermediatePoints));
}
diff --git a/src/Avalonia.MicroCom/MicroComRuntime.cs b/src/Avalonia.MicroCom/MicroComRuntime.cs
index 85507674d20..e0f524146ad 100644
--- a/src/Avalonia.MicroCom/MicroComRuntime.cs
+++ b/src/Avalonia.MicroCom/MicroComRuntime.cs
@@ -36,7 +36,13 @@ public static void Register(Type t, Guid guid, Func proxyF
public static T CreateProxyFor(void* pObject, bool ownsHandle) => (T)CreateProxyFor(typeof(T), new IntPtr(pObject), ownsHandle);
public static T CreateProxyFor(IntPtr pObject, bool ownsHandle) => (T)CreateProxyFor(typeof(T), pObject, ownsHandle);
+
+ public static T CreateProxyOrNullFor(void* pObject, bool ownsHandle) where T : class =>
+ pObject == null ? null : (T)CreateProxyFor(typeof(T), new IntPtr(pObject), ownsHandle);
+ public static T CreateProxyOrNullFor(IntPtr pObject, bool ownsHandle) where T : class =>
+ pObject == IntPtr.Zero ? null : (T)CreateProxyFor(typeof(T), pObject, ownsHandle);
+
public static object CreateProxyFor(Type type, IntPtr pObject, bool ownsHandle) => _factories[type](pObject, ownsHandle);
public static IntPtr GetNativeIntPtr(this T obj, bool owned = false) where T : IUnknown
diff --git a/src/Avalonia.MicroCom/MicroComVtblBase.cs b/src/Avalonia.MicroCom/MicroComVtblBase.cs
index 2f0607c0a88..7092f8131a4 100644
--- a/src/Avalonia.MicroCom/MicroComVtblBase.cs
+++ b/src/Avalonia.MicroCom/MicroComVtblBase.cs
@@ -21,6 +21,11 @@ public MicroComVtblBase()
AddMethod((AddRefDelegate)Release);
}
+ protected void AddMethod(void* f)
+ {
+ _methods.Add(new IntPtr(f));
+ }
+
protected void AddMethod(Delegate d)
{
GCHandle.Alloc(d);
diff --git a/src/Avalonia.Native/Avalonia.Native.csproj b/src/Avalonia.Native/Avalonia.Native.csproj
index 39134844315..393467a8b1c 100644
--- a/src/Avalonia.Native/Avalonia.Native.csproj
+++ b/src/Avalonia.Native/Avalonia.Native.csproj
@@ -6,6 +6,7 @@
true
netstandard2.0
true
+ Avalonia.MicroCom
@@ -20,7 +21,7 @@
-
+
+
-
diff --git a/src/Avalonia.Native/WindowImpl.cs b/src/Avalonia.Native/WindowImpl.cs
index f740be44a23..865aab3fd10 100644
--- a/src/Avalonia.Native/WindowImpl.cs
+++ b/src/Avalonia.Native/WindowImpl.cs
@@ -87,7 +87,10 @@ public void SetTitleBarColor(Avalonia.Media.Color color)
_native.SetTitleBarColor(new AvnColor { Alpha = color.A, Red = color.R, Green = color.G, Blue = color.B });
}
- public void SetTitle(string title) => _native.SetTitle(title);
+ public void SetTitle(string title)
+ {
+ _native.SetTitle(title ?? "");
+ }
public WindowState WindowState
{
diff --git a/src/Avalonia.OpenGL/Controls/OpenGlControlBase.cs b/src/Avalonia.OpenGL/Controls/OpenGlControlBase.cs
index 33773ed8e27..b3469c212b5 100644
--- a/src/Avalonia.OpenGL/Controls/OpenGlControlBase.cs
+++ b/src/Avalonia.OpenGL/Controls/OpenGlControlBase.cs
@@ -89,7 +89,9 @@ void DoCleanup()
gl.BindTexture(GL_TEXTURE_2D, 0);
gl.BindFramebuffer(GL_FRAMEBUFFER, 0);
gl.DeleteFramebuffers(1, new[] { _fb });
+ _fb = 0;
gl.DeleteRenderbuffers(1, new[] { _depthBuffer });
+ _depthBuffer = 0;
_attachment?.Dispose();
_attachment = null;
_bitmap?.Dispose();
diff --git a/src/Avalonia.OpenGL/Egl/EglContext.cs b/src/Avalonia.OpenGL/Egl/EglContext.cs
index 58137dfff06..bc517c3e273 100644
--- a/src/Avalonia.OpenGL/Egl/EglContext.cs
+++ b/src/Avalonia.OpenGL/Egl/EglContext.cs
@@ -82,7 +82,7 @@ public IDisposable MakeCurrent(EglSurface surface)
finally
{
if(!success)
- Monitor.Enter(_lock);
+ Monitor.Exit(_lock);
}
}
diff --git a/src/Avalonia.ReactiveUI/ReactiveUserControl.cs b/src/Avalonia.ReactiveUI/ReactiveUserControl.cs
index 49873491627..21cdef2634a 100644
--- a/src/Avalonia.ReactiveUI/ReactiveUserControl.cs
+++ b/src/Avalonia.ReactiveUI/ReactiveUserControl.cs
@@ -48,6 +48,7 @@ public TViewModel? ViewModel
protected override void OnDataContextChanged(EventArgs e)
{
+ base.OnDataContextChanged(e);
ViewModel = DataContext as TViewModel;
}
diff --git a/src/Avalonia.ReactiveUI/TransitioningContentControl.cs b/src/Avalonia.ReactiveUI/TransitioningContentControl.cs
index c4dd79f4688..d26e90b2daa 100644
--- a/src/Avalonia.ReactiveUI/TransitioningContentControl.cs
+++ b/src/Avalonia.ReactiveUI/TransitioningContentControl.cs
@@ -10,6 +10,7 @@ namespace Avalonia.ReactiveUI
///
/// A ContentControl that animates the transition when its content is changed.
///
+ [Obsolete("Use TransitioningContentControl in Avalonia.Controls namespace")]
public class TransitioningContentControl : ContentControl, IStyleable
{
///
diff --git a/src/Avalonia.Themes.Default/DefaultTheme.xaml b/src/Avalonia.Themes.Default/DefaultTheme.xaml
index 4ae9ea4812c..54cbd4faa1f 100644
--- a/src/Avalonia.Themes.Default/DefaultTheme.xaml
+++ b/src/Avalonia.Themes.Default/DefaultTheme.xaml
@@ -38,6 +38,7 @@
+
diff --git a/src/Avalonia.Themes.Default/Expander.xaml b/src/Avalonia.Themes.Default/Expander.xaml
index e72ddea1632..2f18faf84a8 100644
--- a/src/Avalonia.Themes.Default/Expander.xaml
+++ b/src/Avalonia.Themes.Default/Expander.xaml
@@ -1,5 +1,36 @@
-
+
+
+
+
+
+
+
+
+
+
+
+ Expanded content
+
+
+
+
+ Expanded content
+
+
+
+
+ Expanded content
+
+
+
+
+ Expanded content
+
+
+
+
+
+
diff --git a/src/Avalonia.Themes.Fluent/Controls/Expander.xaml b/src/Avalonia.Themes.Fluent/Controls/Expander.xaml
index a32e0b580ce..c5bc7b6a388 100644
--- a/src/Avalonia.Themes.Fluent/Controls/Expander.xaml
+++ b/src/Avalonia.Themes.Fluent/Controls/Expander.xaml
@@ -5,6 +5,12 @@
+
+
+
+
+
+
Expanded content
@@ -90,7 +96,7 @@
+
diff --git a/src/Avalonia.Themes.Fluent/FluentTheme.cs b/src/Avalonia.Themes.Fluent/FluentTheme.cs
index 53be41e4d13..d1136d44a49 100644
--- a/src/Avalonia.Themes.Fluent/FluentTheme.cs
+++ b/src/Avalonia.Themes.Fluent/FluentTheme.cs
@@ -15,6 +15,12 @@ public enum FluentThemeMode
Dark,
}
+ public enum DensityStyle
+ {
+ Normal,
+ Compact
+ }
+
///
/// Includes the fluent theme in an application.
///
@@ -24,6 +30,7 @@ public class FluentTheme : AvaloniaObject, IStyle, IResourceProvider
private Styles _fluentDark = new();
private Styles _fluentLight = new();
private Styles _sharedStyles = new();
+ private Styles _densityStyles = new();
private bool _isLoading;
private IStyle? _loaded;
@@ -47,9 +54,12 @@ public FluentTheme(IServiceProvider serviceProvider)
InitStyles(_baseUri);
}
-
public static readonly StyledProperty ModeProperty =
AvaloniaProperty.Register(nameof(Mode));
+
+ public static readonly StyledProperty DensityStyleProperty =
+ AvaloniaProperty.Register(nameof(DensityStyle));
+
///
/// Gets or sets the mode of the fluent theme (light, dark).
///
@@ -58,6 +68,16 @@ public FluentThemeMode Mode
get => GetValue(ModeProperty);
set => SetValue(ModeProperty, value);
}
+
+ ///
+ /// Gets or sets the density style of the fluent theme (normal, compact).
+ ///
+ public DensityStyle DensityStyle
+ {
+ get => GetValue(DensityStyleProperty);
+ set => SetValue(DensityStyleProperty, value);
+ }
+
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);
@@ -74,6 +94,18 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs
(Loaded as Styles)![2] = _fluentLight[1];
}
}
+
+ if (change.Property == DensityStyleProperty)
+ {
+ if (DensityStyle == DensityStyle.Compact)
+ {
+ (Loaded as Styles)!.Add(_densityStyles[0]);
+ }
+ else if (DensityStyle == DensityStyle.Normal)
+ {
+ (Loaded as Styles)!.Remove(_densityStyles[0]);
+ }
+ }
}
public IResourceHost? Owner => (Loaded as IResourceProvider)?.Owner;
@@ -97,6 +129,12 @@ public IStyle Loaded
{
_loaded = new Styles() { _sharedStyles, _fluentDark[0], _fluentDark[1] };
}
+
+ if (DensityStyle == DensityStyle.Compact)
+ {
+ (_loaded as Styles)!.Add(_densityStyles[0]);
+ }
+
_isLoading = false;
}
@@ -183,6 +221,14 @@ private void InitStyles(Uri baseUri)
Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/FluentControlResourcesDark.xaml")
}
};
+
+ _densityStyles = new Styles
+ {
+ new StyleInclude(baseUri)
+ {
+ Source = new Uri("avares://Avalonia.Themes.Fluent/DensityStyles/Compact.xaml")
+ }
+ };
}
}
}
diff --git a/src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs b/src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs
index 7c6372aae23..f0e72f0b376 100644
--- a/src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs
+++ b/src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs
@@ -12,6 +12,8 @@ namespace Avalonia.Animation.Animators
///
public class ISolidColorBrushAnimator : Animator
{
+ private static readonly DoubleAnimator s_doubleAnimator = new DoubleAnimator();
+
public override ISolidColorBrush? Interpolate(double progress, ISolidColorBrush? oldValue, ISolidColorBrush? newValue)
{
if (oldValue is null || newValue is null)
@@ -19,7 +21,9 @@ public class ISolidColorBrushAnimator : Animator
return progress >= 0.5 ? newValue : oldValue;
}
- return new ImmutableSolidColorBrush(ColorAnimator.InterpolateCore(progress, oldValue.Color, newValue.Color));
+ return new ImmutableSolidColorBrush(
+ ColorAnimator.InterpolateCore(progress, oldValue.Color, newValue.Color),
+ s_doubleAnimator.Interpolate(progress, oldValue.Opacity, newValue.Opacity));
}
public override IDisposable BindAnimation(Animatable control, IObservable instance)
diff --git a/src/Avalonia.Visuals/Animation/PageSlide.cs b/src/Avalonia.Visuals/Animation/PageSlide.cs
index 7d033ccf61c..8badc018e06 100644
--- a/src/Avalonia.Visuals/Animation/PageSlide.cs
+++ b/src/Avalonia.Visuals/Animation/PageSlide.cs
@@ -79,6 +79,7 @@ public async Task Start(Visual from, Visual to, bool forward, CancellationToken
var animation = new Animation
{
Easing = SlideOutEasing,
+ FillMode = FillMode.Forward,
Children =
{
new KeyFrame
@@ -109,6 +110,7 @@ public async Task Start(Visual from, Visual to, bool forward, CancellationToken
to.IsVisible = true;
var animation = new Animation
{
+ FillMode = FillMode.Forward,
Easing = SlideInEasing,
Children =
{
diff --git a/src/Avalonia.X11/Avalonia.X11.csproj b/src/Avalonia.X11/Avalonia.X11.csproj
index c160fd77261..3f5d4979691 100644
--- a/src/Avalonia.X11/Avalonia.X11.csproj
+++ b/src/Avalonia.X11/Avalonia.X11.csproj
@@ -9,6 +9,7 @@
+
diff --git a/src/Avalonia.X11/NativeDialogs/Gtk.cs b/src/Avalonia.X11/NativeDialogs/Gtk.cs
index 77e410162f0..872c824f743 100644
--- a/src/Avalonia.X11/NativeDialogs/Gtk.cs
+++ b/src/Avalonia.X11/NativeDialogs/Gtk.cs
@@ -63,7 +63,7 @@ public void Dispose()
public static IDisposable ConnectSignal(IntPtr obj, string name, T handler)
{
var handle = GCHandle.Alloc(handler);
- var ptr = Marshal.GetFunctionPointerForDelegate((Delegate)(object)handler);
+ var ptr = Marshal.GetFunctionPointerForDelegate(handler);
using (var utf = new Utf8Buffer(name))
{
var id = g_signal_connect_object(obj, utf, ptr, IntPtr.Zero, 0);
diff --git a/src/Avalonia.X11/SMLib.cs b/src/Avalonia.X11/SMLib.cs
index 6ffcad60bfc..e2b39cfcffb 100644
--- a/src/Avalonia.X11/SMLib.cs
+++ b/src/Avalonia.X11/SMLib.cs
@@ -1,3 +1,4 @@
+#nullable enable
using System;
using System.Runtime.InteropServices;
@@ -7,20 +8,19 @@ internal static unsafe class SMLib
{
private const string LibSm = "libSM.so.6";
- [DllImport(LibSm, CharSet = CharSet.Ansi)]
+ [DllImport(LibSm, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern IntPtr SmcOpenConnection(
- [MarshalAs(UnmanagedType.LPWStr)] string networkId,
+ [MarshalAs(UnmanagedType.LPStr)] string? networkId,
IntPtr content,
int xsmpMajorRev,
int xsmpMinorRev,
- ulong mask,
+ nuint mask,
ref SmcCallbacks callbacks,
- [MarshalAs(UnmanagedType.LPWStr)] [Out]
- out string previousId,
- [MarshalAs(UnmanagedType.LPWStr)] [Out]
- out string clientIdRet,
+ [MarshalAs(UnmanagedType.LPStr)] string? previousId,
+ ref IntPtr clientIdRet,
int errorLength,
- [Out] char[] errorStringRet);
+ [Out] byte[] errorStringRet
+ );
[DllImport(LibSm, CallingConvention = CallingConvention.StdCall)]
public static extern int SmcCloseConnection(
diff --git a/src/Avalonia.X11/X11PlatformLifetimeEvents.cs b/src/Avalonia.X11/X11PlatformLifetimeEvents.cs
index 06e1d8879f0..1a17a018e8b 100644
--- a/src/Avalonia.X11/X11PlatformLifetimeEvents.cs
+++ b/src/Avalonia.X11/X11PlatformLifetimeEvents.cs
@@ -1,5 +1,6 @@
#nullable enable
using System;
+using System.Text;
using System.Collections.Concurrent;
using System.Runtime.InteropServices;
using System.Threading;
@@ -14,10 +15,10 @@ namespace Avalonia.X11
internal unsafe class X11PlatformLifetimeEvents : IDisposable, IPlatformLifetimeEventsImpl
{
private readonly AvaloniaX11Platform _platform;
- private const ulong SmcSaveYourselfProcMask = 1L;
- private const ulong SmcDieProcMask = 2L;
- private const ulong SmcSaveCompleteProcMask = 4L;
- private const ulong SmcShutdownCancelledProcMask = 8L;
+ private const nuint SmcSaveYourselfProcMask = 1;
+ private const nuint SmcDieProcMask = 2;
+ private const nuint SmcSaveCompleteProcMask = 4;
+ private const nuint SmcShutdownCancelledProcMask = 8;
private static readonly ConcurrentDictionary s_nativeToManagedMapper =
new ConcurrentDictionary();
@@ -62,24 +63,24 @@ internal X11PlatformLifetimeEvents(AvaloniaX11Platform platform)
return;
}
- var errorBuf = new char[255];
-
- var smcConn = SMLib.SmcOpenConnection(null!,
+ byte[] errorBuf = new byte[255];
+ IntPtr clientIdRet = IntPtr.Zero;
+ var smcConn = SMLib.SmcOpenConnection(null,
IntPtr.Zero, 1, 0,
SmcSaveYourselfProcMask |
SmcSaveCompleteProcMask |
SmcShutdownCancelledProcMask |
SmcDieProcMask,
ref s_callbacks,
- out _,
- out _,
+ null,
+ ref clientIdRet,
errorBuf.Length,
errorBuf);
if (smcConn == IntPtr.Zero)
{
Logger.TryGet(LogEventLevel.Warning, LogArea.X11Platform)?.Log(this,
- $"SMLib/ICELib reported a new error: {new string(errorBuf)}");
+ $"SMLib/ICELib reported a new error: {Encoding.ASCII.GetString(errorBuf)}");
return;
}
diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs
index 07469b73627..520b35dd033 100644
--- a/src/Avalonia.X11/X11Window.cs
+++ b/src/Avalonia.X11/X11Window.cs
@@ -50,13 +50,7 @@ unsafe partial class X11Window : IWindowImpl, IPopupImpl, IXI2Client,
private double? _scalingOverride;
private bool _disabled;
private TransparencyHelper _transparencyHelper;
-
- class InputEventContainer
- {
- public RawInputEventArgs Event;
- }
- private readonly Queue _inputQueue = new Queue();
- private InputEventContainer _lastEvent;
+ private RawEventGrouper _rawEventGrouper;
private bool _useRenderWindow = false;
public X11Window(AvaloniaX11Platform platform, IWindowImpl popupParent)
{
@@ -180,6 +174,8 @@ public X11Window(AvaloniaX11Platform platform, IWindowImpl popupParent)
UpdateMotifHints();
UpdateSizeHints(null);
+ _rawEventGrouper = new RawEventGrouper(e => Input?.Invoke(e));
+
_transparencyHelper = new TransparencyHelper(_x11, _handle, platform.Globals);
_transparencyHelper.SetTransparencyRequest(WindowTransparencyLevel.None);
@@ -721,33 +717,14 @@ private void ScheduleInput(RawInputEventArgs args)
if (args is RawDragEvent drag)
drag.Location = drag.Location / RenderScaling;
- _lastEvent = new InputEventContainer() {Event = args};
- _inputQueue.Enqueue(_lastEvent);
- if (_inputQueue.Count == 1)
- {
- Dispatcher.UIThread.Post(() =>
- {
- while (_inputQueue.Count > 0)
- {
- Dispatcher.UIThread.RunJobs(DispatcherPriority.Input + 1);
- var ev = _inputQueue.Dequeue();
- Input?.Invoke(ev.Event);
- }
- }, DispatcherPriority.Input);
- }
+ _rawEventGrouper.HandleEvent(args);
}
void MouseEvent(RawPointerEventType type, ref XEvent ev, XModifierMask mods)
{
var mev = new RawPointerEventArgs(
_mouse, (ulong)ev.ButtonEvent.time.ToInt64(), _inputRoot,
- type, new Point(ev.ButtonEvent.x, ev.ButtonEvent.y), TranslateModifiers(mods));
- if(type == RawPointerEventType.Move && _inputQueue.Count>0 && _lastEvent.Event is RawPointerEventArgs ma)
- if (ma.Type == RawPointerEventType.Move)
- {
- _lastEvent.Event = mev;
- return;
- }
+ type, new Point(ev.ButtonEvent.x, ev.ButtonEvent.y), TranslateModifiers(mods));
ScheduleInput(mev, ref ev);
}
@@ -775,6 +752,12 @@ public void Dispose()
void Cleanup()
{
+ if (_rawEventGrouper != null)
+ {
+ _rawEventGrouper.Dispose();
+ _rawEventGrouper = null;
+ }
+
if (_transparencyHelper != null)
{
_transparencyHelper.Dispose();
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index a3f0c01a514..869602e4528 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -2,4 +2,10 @@
+
+
+ Shared\_ModuleInitializer.cs
+ false
+
+
diff --git a/src/Linux/Avalonia.LinuxFramebuffer/Avalonia.LinuxFramebuffer.csproj b/src/Linux/Avalonia.LinuxFramebuffer/Avalonia.LinuxFramebuffer.csproj
index 48095a4c25b..a03a90038fe 100644
--- a/src/Linux/Avalonia.LinuxFramebuffer/Avalonia.LinuxFramebuffer.csproj
+++ b/src/Linux/Avalonia.LinuxFramebuffer/Avalonia.LinuxFramebuffer.csproj
@@ -7,5 +7,6 @@
+
diff --git a/src/Linux/Avalonia.LinuxFramebuffer/Input/EvDev/EvDevBackend.cs b/src/Linux/Avalonia.LinuxFramebuffer/Input/EvDev/EvDevBackend.cs
index b3fc979fcac..2eb10ae6662 100644
--- a/src/Linux/Avalonia.LinuxFramebuffer/Input/EvDev/EvDevBackend.cs
+++ b/src/Linux/Avalonia.LinuxFramebuffer/Input/EvDev/EvDevBackend.cs
@@ -13,15 +13,16 @@ public class EvDevBackend : IInputBackend
private readonly EvDevDeviceDescription[] _deviceDescriptions;
private readonly List _handlers = new List();
private int _epoll;
- private Queue _inputQueue = new Queue();
private bool _isQueueHandlerTriggered;
private object _lock = new object();
private Action _onInput;
private IInputRoot _inputRoot;
+ private RawEventGroupingThreadingHelper _inputQueue;
public EvDevBackend(EvDevDeviceDescription[] devices)
{
_deviceDescriptions = devices;
+ _inputQueue = new RawEventGroupingThreadingHelper(e => _onInput?.Invoke(e));
}
unsafe void InputThread()
@@ -49,42 +50,9 @@ unsafe void InputThread()
private void OnRawEvent(RawInputEventArgs obj)
{
- lock (_lock)
- {
- _inputQueue.Enqueue(obj);
- TriggerQueueHandler();
- }
-
+ _inputQueue.OnEvent(obj);
}
- void TriggerQueueHandler()
- {
- if (_isQueueHandlerTriggered)
- return;
- _isQueueHandlerTriggered = true;
- Dispatcher.UIThread.Post(InputQueueHandler, DispatcherPriority.Input);
-
- }
-
- void InputQueueHandler()
- {
- RawInputEventArgs ev;
- lock (_lock)
- {
- _isQueueHandlerTriggered = false;
- if(_inputQueue.Count == 0)
- return;
- ev = _inputQueue.Dequeue();
- }
-
- _onInput?.Invoke(ev);
-
- lock (_lock)
- {
- if (_inputQueue.Count > 0)
- TriggerQueueHandler();
- }
- }
public void Initialize(IScreenInfoProvider info, Action onInput)
{
diff --git a/src/Linux/Avalonia.LinuxFramebuffer/Input/LibInput/LibInputBackend.cs b/src/Linux/Avalonia.LinuxFramebuffer/Input/LibInput/LibInputBackend.cs
index 432344955ac..15d42789d44 100644
--- a/src/Linux/Avalonia.LinuxFramebuffer/Input/LibInput/LibInputBackend.cs
+++ b/src/Linux/Avalonia.LinuxFramebuffer/Input/LibInput/LibInputBackend.cs
@@ -17,15 +17,15 @@ public class LibInputBackend : IInputBackend
private TouchDevice _touch = new TouchDevice();
private MouseDevice _mouse = new MouseDevice();
private Point _mousePosition;
-
- private readonly Queue _inputQueue = new Queue();
+
+ private readonly RawEventGroupingThreadingHelper _inputQueue;
private Action _onInput;
private Dictionary _pointers = new Dictionary();
public LibInputBackend()
{
var ctx = libinput_path_create_context();
-
+ _inputQueue = new(e => _onInput?.Invoke(e));
new Thread(()=>InputThread(ctx)).Start();
}
@@ -66,30 +66,7 @@ private unsafe void InputThread(IntPtr ctx)
}
}
- private void ScheduleInput(RawInputEventArgs ev)
- {
- lock (_inputQueue)
- {
- _inputQueue.Enqueue(ev);
- if (_inputQueue.Count == 1)
- {
- Dispatcher.UIThread.Post(() =>
- {
- while (true)
- {
- Dispatcher.UIThread.RunJobs(DispatcherPriority.Input + 1);
- RawInputEventArgs dequeuedEvent = null;
- lock(_inputQueue)
- if (_inputQueue.Count != 0)
- dequeuedEvent = _inputQueue.Dequeue();
- if (dequeuedEvent == null)
- return;
- _onInput?.Invoke(dequeuedEvent);
- }
- }, DispatcherPriority.Input);
- }
- }
- }
+ private void ScheduleInput(RawInputEventArgs ev) => _inputQueue.OnEvent(ev);
private void HandleTouch(IntPtr ev, LibInputEventType type)
{
diff --git a/src/Linux/Avalonia.LinuxFramebuffer/Input/RawEventGroupingThreadingHelper.cs b/src/Linux/Avalonia.LinuxFramebuffer/Input/RawEventGroupingThreadingHelper.cs
new file mode 100644
index 00000000000..f706f184612
--- /dev/null
+++ b/src/Linux/Avalonia.LinuxFramebuffer/Input/RawEventGroupingThreadingHelper.cs
@@ -0,0 +1,43 @@
+using System;
+using System.Collections.Generic;
+using Avalonia.Input.Raw;
+using Avalonia.Threading;
+
+namespace Avalonia.LinuxFramebuffer.Input;
+
+internal class RawEventGroupingThreadingHelper : IDisposable
+{
+ private readonly RawEventGrouper _grouper;
+ private readonly Queue _rawQueue = new();
+ private readonly Action _queueHandler;
+
+ public RawEventGroupingThreadingHelper(Action eventCallback)
+ {
+ _grouper = new RawEventGrouper(eventCallback);
+ _queueHandler = QueueHandler;
+ }
+
+ private void QueueHandler()
+ {
+ lock (_rawQueue)
+ {
+ while (_rawQueue.Count > 0)
+ _grouper.HandleEvent(_rawQueue.Dequeue());
+ }
+ }
+
+ public void OnEvent(RawInputEventArgs args)
+ {
+ lock (_rawQueue)
+ {
+ _rawQueue.Enqueue(args);
+ if (_rawQueue.Count == 1)
+ {
+ Dispatcher.UIThread.Post(_queueHandler, DispatcherPriority.Input);
+ }
+ }
+ }
+
+ public void Dispose() =>
+ Dispatcher.UIThread.Post(() => _grouper.Dispose(), DispatcherPriority.Input + 1);
+}
\ No newline at end of file
diff --git a/src/Shared/ModuleInitializer.cs b/src/Shared/ModuleInitializer.cs
new file mode 100644
index 00000000000..c14b150c1a3
--- /dev/null
+++ b/src/Shared/ModuleInitializer.cs
@@ -0,0 +1,10 @@
+namespace System.Runtime.CompilerServices
+{
+#if !NET5_0_OR_GREATER
+ internal class ModuleInitializerAttribute : Attribute
+ {
+
+ }
+#endif
+}
+
diff --git a/src/Shared/RawEventGrouping.cs b/src/Shared/RawEventGrouping.cs
new file mode 100644
index 00000000000..084593ffc6a
--- /dev/null
+++ b/src/Shared/RawEventGrouping.cs
@@ -0,0 +1,133 @@
+#nullable enable
+using System;
+using System.Collections.Generic;
+using Avalonia.Collections.Pooled;
+using Avalonia.Input;
+using Avalonia.Input.Raw;
+using Avalonia.Threading;
+using JetBrains.Annotations;
+
+namespace Avalonia;
+
+/*
+ This helper maintains an input queue for backends that handle input asynchronously.
+ While doing that it groups Move and TouchUpdate events so we could provide GetIntermediatePoints API
+ */
+
+internal class RawEventGrouper : IDisposable
+{
+ private readonly Action _eventCallback;
+ private readonly Queue _inputQueue = new();
+ private readonly Action _dispatchFromQueue;
+ readonly Dictionary _lastTouchPoints = new();
+ RawInputEventArgs? _lastEvent;
+
+ public RawEventGrouper(Action eventCallback)
+ {
+ _eventCallback = eventCallback;
+ _dispatchFromQueue = DispatchFromQueue;
+ }
+
+ private void AddToQueue(RawInputEventArgs args)
+ {
+ _lastEvent = args;
+ _inputQueue.Enqueue(args);
+ if (_inputQueue.Count == 1)
+ Dispatcher.UIThread.Post(_dispatchFromQueue, DispatcherPriority.Input);
+ }
+
+ private void DispatchFromQueue()
+ {
+ while (true)
+ {
+ if(_inputQueue.Count == 0)
+ return;
+
+ var ev = _inputQueue.Dequeue();
+
+ if (_lastEvent == ev)
+ _lastEvent = null;
+
+ if (ev is RawTouchEventArgs { Type: RawPointerEventType.TouchUpdate } touchUpdate)
+ _lastTouchPoints.Remove(touchUpdate.TouchPointId);
+
+ _eventCallback?.Invoke(ev);
+
+ if (ev is RawPointerEventArgs { IntermediatePoints.Value: PooledList list })
+ list.Dispose();
+
+ if (Dispatcher.UIThread.HasJobsWithPriority(DispatcherPriority.Input + 1))
+ {
+ Dispatcher.UIThread.Post(_dispatchFromQueue, DispatcherPriority.Input);
+ return;
+ }
+ }
+ }
+
+ public void HandleEvent(RawInputEventArgs args)
+ {
+ /*
+ Try to update already enqueued events if
+ 1) they are still not handled (_lastEvent and _lastTouchPoints shouldn't contain said event in that case)
+ 2) previous event belongs to the same "event block", events in the same block:
+ - belong from the same device
+ - are pointer move events (Move/TouchUpdate)
+ - have the same type
+ - have same modifiers
+
+ Even if nothing is updated and the event is actually enqueued, we need to update the relevant tracking info
+ */
+ if (
+ args is RawPointerEventArgs pointerEvent
+ && _lastEvent != null
+ && _lastEvent.Device == args.Device
+ && _lastEvent is RawPointerEventArgs lastPointerEvent
+ && lastPointerEvent.InputModifiers == pointerEvent.InputModifiers
+ && lastPointerEvent.Type == pointerEvent.Type
+ && lastPointerEvent.Type is RawPointerEventType.Move or RawPointerEventType.TouchUpdate)
+ {
+ if (args is RawTouchEventArgs touchEvent)
+ {
+ if (_lastTouchPoints.TryGetValue(touchEvent.TouchPointId, out var lastTouchEvent))
+ MergeEvents(lastTouchEvent, touchEvent);
+ else
+ {
+ _lastTouchPoints[touchEvent.TouchPointId] = touchEvent;
+ AddToQueue(touchEvent);
+ }
+ }
+ else
+ MergeEvents(lastPointerEvent, pointerEvent);
+
+ return;
+ }
+ else
+ {
+ _lastTouchPoints.Clear();
+ if (args is RawTouchEventArgs { Type: RawPointerEventType.TouchUpdate } touchEvent)
+ _lastTouchPoints[touchEvent.TouchPointId] = touchEvent;
+ }
+ AddToQueue(args);
+ }
+
+ private static IReadOnlyList GetPooledList() => new PooledList();
+ private static readonly Func> s_getPooledListDelegate = GetPooledList;
+
+ private static void MergeEvents(RawPointerEventArgs last, RawPointerEventArgs current)
+ {
+
+ last.IntermediatePoints ??= new Lazy?>(s_getPooledListDelegate);
+ ((PooledList)last.IntermediatePoints.Value!).Add(new RawPointerPoint { Position = last.Position });
+ last.Position = current.Position;
+ last.Timestamp = current.Timestamp;
+ last.InputModifiers = current.InputModifiers;
+ }
+
+ public void Dispose()
+ {
+ _inputQueue.Clear();
+ _lastEvent = null;
+ _lastTouchPoints.Clear();
+ }
+}
+
diff --git a/src/Skia/Avalonia.Skia/Helpers/DrawingContextHelper.cs b/src/Skia/Avalonia.Skia/Helpers/DrawingContextHelper.cs
index 72438609d51..d0b45b7c5d7 100644
--- a/src/Skia/Avalonia.Skia/Helpers/DrawingContextHelper.cs
+++ b/src/Skia/Avalonia.Skia/Helpers/DrawingContextHelper.cs
@@ -1,10 +1,11 @@
-using Avalonia.Platform;
+using System;
+using Avalonia.Platform;
using Avalonia.Rendering;
using SkiaSharp;
namespace Avalonia.Skia.Helpers
{
- public class DrawingContextHelper
+ public static class DrawingContextHelper
{
///
/// Wrap Skia canvas in drawing context so we can use Avalonia api to render to external skia canvas
@@ -27,5 +28,71 @@ public static IDrawingContextImpl WrapSkiaCanvas(SKCanvas canvas, Vector dpi, IV
return new DrawingContextImpl(createInfo);
}
+
+ ///
+ /// Unsupported - Wraps a GPU Backed SkiaSurface in an Avalonia DrawingContext.
+ ///
+ [Obsolete]
+ public static ISkiaDrawingContextImpl WrapSkiaSurface(this SKSurface surface, GRContext grContext, Vector dpi, params IDisposable[] disposables)
+ {
+ var createInfo = new DrawingContextImpl.CreateInfo
+ {
+ GrContext = grContext,
+ Surface = surface,
+ Dpi = dpi,
+ DisableTextLcdRendering = false,
+ };
+
+ return new DrawingContextImpl(createInfo, disposables);
+ }
+
+ ///
+ /// Unsupported - Wraps a non-GPU Backed SkiaSurface in an Avalonia DrawingContext.
+ ///
+ [Obsolete]
+ public static ISkiaDrawingContextImpl WrapSkiaSurface(this SKSurface surface, Vector dpi, params IDisposable[] disposables)
+ {
+ var createInfo = new DrawingContextImpl.CreateInfo
+ {
+ Surface = surface,
+ Dpi = dpi,
+ DisableTextLcdRendering = false,
+ };
+
+ return new DrawingContextImpl(createInfo, disposables);
+ }
+
+ [Obsolete]
+ public static ISkiaDrawingContextImpl CreateDrawingContext(Size size, Vector dpi, GRContext grContext = null)
+ {
+ if (grContext is null)
+ {
+ var surface = SKSurface.Create(
+ new SKImageInfo(
+ (int)Math.Ceiling(size.Width),
+ (int)Math.Ceiling(size.Height),
+ SKImageInfo.PlatformColorType,
+ SKAlphaType.Premul));
+
+ return WrapSkiaSurface(surface, dpi, surface);
+ }
+ else
+ {
+ var surface = SKSurface.Create(grContext, false,
+ new SKImageInfo(
+ (int)Math.Ceiling(size.Width),
+ (int)Math.Ceiling(size.Height),
+ SKImageInfo.PlatformColorType,
+ SKAlphaType.Premul));
+
+ return WrapSkiaSurface(surface, grContext, dpi, surface);
+ }
+ }
+
+ [Obsolete]
+ public static void DrawTo(this ISkiaDrawingContextImpl source, ISkiaDrawingContextImpl destination, SKPaint paint = null)
+ {
+ destination.SkCanvas.DrawSurface(source.SkSurface, new SKPoint(0, 0), paint);
+ }
}
}
diff --git a/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj b/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj
index 2a2aff322a2..e970fc87ea3 100644
--- a/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj
+++ b/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj
@@ -3,14 +3,15 @@
netstandard2.0
true
Avalonia.Win32
+ Avalonia.MicroCom
-
-
+
+
+
-
diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
index c74c5fbc01e..1809fcf98b6 100644
--- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
+++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
@@ -862,7 +862,7 @@ public struct BITMAPINFOHEADER
public void Init()
{
- biSize = (uint)Marshal.SizeOf(this);
+ biSize = (uint)sizeof(BITMAPINFOHEADER);
}
}
@@ -1521,7 +1521,7 @@ internal struct RTL_OSVERSIONINFOEX
internal static Version RtlGetVersion()
{
RTL_OSVERSIONINFOEX v = new RTL_OSVERSIONINFOEX();
- v.dwOSVersionInfoSize = (uint)Marshal.SizeOf(v);
+ v.dwOSVersionInfoSize = (uint)Marshal.SizeOf();
if (RtlGetVersion(ref v) == 0)
{
return new Version((int)v.dwMajorVersion, (int)v.dwMinorVersion, (int)v.dwBuildNumber);
@@ -1914,7 +1914,7 @@ public static WINDOWPLACEMENT Default
get
{
WINDOWPLACEMENT result = new WINDOWPLACEMENT();
- result.Length = Marshal.SizeOf(result);
+ result.Length = Marshal.SizeOf();
return result;
}
}
diff --git a/src/Windows/Avalonia.Win32/SystemDialogImpl.cs b/src/Windows/Avalonia.Win32/SystemDialogImpl.cs
index 29844368db6..fa53bf57fed 100644
--- a/src/Windows/Avalonia.Win32/SystemDialogImpl.cs
+++ b/src/Windows/Avalonia.Win32/SystemDialogImpl.cs
@@ -79,7 +79,16 @@ internal class SystemDialogImpl : ISystemDialogImpl
}
}
- frm.Show(hWnd);
+ var showResult = frm.Show(hWnd);
+
+ if ((uint)showResult == (uint)UnmanagedMethods.HRESULT.E_CANCELLED)
+ {
+ return result;
+ }
+ else if ((uint)showResult != (uint)UnmanagedMethods.HRESULT.S_OK)
+ {
+ throw new Win32Exception(showResult);
+ }
if (openDialog?.AllowMultiple == true)
{
@@ -108,10 +117,6 @@ internal class SystemDialogImpl : ISystemDialogImpl
}
catch (COMException ex)
{
- if ((uint)ex.HResult == (uint)UnmanagedMethods.HRESULT.E_CANCELLED)
- {
- return result;
- }
throw new Win32Exception(ex.HResult);
}
})!;
@@ -151,7 +156,17 @@ internal class SystemDialogImpl : ISystemDialogImpl
}
}
- frm.Show(hWnd);
+ var showResult = frm.Show(hWnd);
+
+ if ((uint)showResult == (uint)UnmanagedMethods.HRESULT.E_CANCELLED)
+ {
+ return result;
+ }
+ else if ((uint)showResult != (uint)UnmanagedMethods.HRESULT.S_OK)
+ {
+ throw new Win32Exception(showResult);
+ }
+
if (frm.Result is not null)
{
result = GetAbsoluteFilePath(frm.Result);
@@ -161,10 +176,6 @@ internal class SystemDialogImpl : ISystemDialogImpl
}
catch (COMException ex)
{
- if ((uint)ex.HResult == (uint)UnmanagedMethods.HRESULT.E_CANCELLED)
- {
- return result;
- }
throw new Win32Exception(ex.HResult);
}
});
diff --git a/src/Windows/Avalonia.Win32/Win32Com/win32.idl b/src/Windows/Avalonia.Win32/Win32Com/win32.idl
index 904ac41ff24..54e9aa583f2 100644
--- a/src/Windows/Avalonia.Win32/Win32Com/win32.idl
+++ b/src/Windows/Avalonia.Win32/Win32Com/win32.idl
@@ -112,7 +112,7 @@ interface IShellItemArray : IUnknown
interface IModalWindow : IUnknown
{
[local]
- HRESULT Show(
+ int Show(
[in, unique] HWND hwndOwner);
}
diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs
index e4f52682853..d1945e6c855 100644
--- a/src/Windows/Avalonia.Win32/WindowImpl.cs
+++ b/src/Windows/Avalonia.Win32/WindowImpl.cs
@@ -228,7 +228,7 @@ public Size? FrameSize
return new Size(rcWindow.Width, rcWindow.Height) / RenderScaling;
}
- DwmGetWindowAttribute(_hwnd, (int)DwmWindowAttribute.DWMWA_EXTENDED_FRAME_BOUNDS, out var rect, Marshal.SizeOf(typeof(RECT)));
+ DwmGetWindowAttribute(_hwnd, (int)DwmWindowAttribute.DWMWA_EXTENDED_FRAME_BOUNDS, out var rect, Marshal.SizeOf());
return new Size(rect.Width, rect.Height) / RenderScaling;
}
}
@@ -265,7 +265,7 @@ public WindowState WindowState
{
if (IsWindowVisible(_hwnd))
{
- ShowWindow(value, true);
+ ShowWindow(value, value != WindowState.Minimized); // If the window is minimized, it shouldn't be activated
}
_showWindowState = value;
@@ -337,7 +337,7 @@ private WindowTransparencyLevel Win7EnableBlur(WindowTransparencyLevel transpare
private WindowTransparencyLevel Win8xEnableBlur(WindowTransparencyLevel transparencyLevel)
{
var accent = new AccentPolicy();
- var accentStructSize = Marshal.SizeOf(accent);
+ var accentStructSize = Marshal.SizeOf();
if (transparencyLevel == WindowTransparencyLevel.AcrylicBlur)
{
@@ -392,7 +392,7 @@ private WindowTransparencyLevel Win10EnableBlur(WindowTransparencyLevel transpar
bool canUseAcrylic = Win32Platform.WindowsVersion.Major > 10 || Win32Platform.WindowsVersion.Build >= 19628;
var accent = new AccentPolicy();
- var accentStructSize = Marshal.SizeOf(accent);
+ var accentStructSize = Marshal.SizeOf();
if (transparencyLevel == WindowTransparencyLevel.AcrylicBlur && !canUseAcrylic)
{
@@ -973,7 +973,7 @@ private void ShowWindow(WindowState state, bool activate)
{
case WindowState.Minimized:
newWindowProperties.IsFullScreen = false;
- command = activate ? ShowWindowCommand.Minimize : ShowWindowCommand.ShowMinNoActive;
+ command = ShowWindowCommand.Minimize;
break;
case WindowState.Maximized:
newWindowProperties.IsFullScreen = false;
diff --git a/src/tools/MicroComGenerator/Ast.cs b/src/tools/MicroComGenerator/Ast.cs
deleted file mode 100644
index e9a55308be6..00000000000
--- a/src/tools/MicroComGenerator/Ast.cs
+++ /dev/null
@@ -1,241 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-
-namespace MicroComGenerator.Ast
-{
- public class AstAttributeNode
- {
- public string Name { get; set; }
- public string Value { get; set; }
-
- public AstAttributeNode(string name, string value)
- {
- Name = name;
- Value = value;
- }
-
- public override string ToString() => $"{Name} = {Value}";
- public AstAttributeNode Clone() => new AstAttributeNode(Name, Value);
- }
-
- public class AstAttributes : List
- {
- public bool HasAttribute(string a) => this.Any(x => x.Name == a);
-
- public AstAttributes Clone()
- {
- var rv= new AstAttributes();
- rv.AddRange(this.Select(x => x.Clone()));
- return rv;
- }
- }
-
- public interface IAstNodeWithAttributes
- {
- public AstAttributes Attributes { get; set; }
- }
-
- public class AstEnumNode : List, IAstNodeWithAttributes
- {
- public AstAttributes Attributes { get; set; } = new AstAttributes();
- public string Name { get; set; }
- public override string ToString() => "Enum " + Name;
-
- public AstEnumNode Clone()
- {
- var rv = new AstEnumNode { Name = Name, Attributes = Attributes.Clone() };
- rv.AddRange(this.Select(x => x.Clone()));
- return rv;
- }
- }
-
- public class AstEnumMemberNode
- {
- public string Name { get; set; }
- public string Value { get; set; }
-
- public AstEnumMemberNode(string name, string value)
- {
- Name = name;
- Value = value;
- }
-
- public override string ToString() => $"Enum member {Name} = {Value}";
- public AstEnumMemberNode Clone() => new AstEnumMemberNode(Name, Value);
- }
-
- public class AstStructNode : List, IAstNodeWithAttributes
- {
- public AstAttributes Attributes { get; set; } = new AstAttributes();
- public string Name { get; set; }
- public override string ToString() => "Struct " + Name;
-
- public AstStructNode Clone()
- {
- var rv = new AstStructNode { Name = Name, Attributes = Attributes.Clone() };
- rv.AddRange(this.Select(x => x.Clone()));
- return rv;
- }
- }
-
- public class AstTypeNode
- {
- public string Name { get; set; }
- public int PointerLevel { get; set; }
- public bool IsLink { get; set; }
-
- public string Format() => Name + new string('*', PointerLevel)
- + (IsLink ? "&" : "");
- public override string ToString() => Format();
- public AstTypeNode Clone() => new AstTypeNode() {
- Name = Name,
- PointerLevel = PointerLevel,
- IsLink = IsLink
- };
- }
-
- public class AstStructMemberNode : IAstNodeWithAttributes
- {
- public string Name { get; set; }
- public AstTypeNode Type { get; set; }
-
- public override string ToString() => $"Struct member {Type.Format()} {Name}";
- public AstStructMemberNode Clone() => new AstStructMemberNode() { Name = Name, Type = Type.Clone() };
- public AstAttributes Attributes { get; set; } = new AstAttributes();
- }
-
- public class AstInterfaceNode : List, IAstNodeWithAttributes
- {
- public AstAttributes Attributes { get; set; } = new AstAttributes();
- public string Name { get; set; }
- public string Inherits { get; set; }
-
- public override string ToString()
- {
- if (Inherits == null)
- return Name;
- return $"Interface {Name} : {Inherits}";
- }
- public AstInterfaceNode Clone()
- {
- var rv = new AstInterfaceNode { Name = Name, Inherits = Inherits, Attributes = Attributes.Clone() };
- rv.AddRange(this.Select(x => x.Clone()));
- return rv;
- }
- }
-
- public class AstInterfaceMemberNode : List, IAstNodeWithAttributes
- {
- public string Name { get; set; }
- public AstTypeNode ReturnType { get; set; }
- public AstAttributes Attributes { get; set; } = new AstAttributes();
-
- public AstInterfaceMemberNode Clone()
- {
- var rv = new AstInterfaceMemberNode()
- {
- Name = Name, Attributes = Attributes.Clone(), ReturnType = ReturnType
- };
- rv.AddRange(this.Select(x => x.Clone()));
- return rv;
- }
-
- public override string ToString() =>
- $"Interface member {ReturnType.Format()} {Name} ({string.Join(", ", this.Select(x => x.Format()))})";
- }
-
- public class AstInterfaceMemberArgumentNode : IAstNodeWithAttributes
- {
- public string Name { get; set; }
- public AstTypeNode Type { get; set; }
- public AstAttributes Attributes { get; set; } = new AstAttributes();
-
-
- public string Format() => $"{Type.Format()} {Name}";
- public override string ToString() => "Argument " + Format();
-
- public AstInterfaceMemberArgumentNode Clone() => new AstInterfaceMemberArgumentNode
- {
- Name = Name, Type = Type.Clone(), Attributes = Attributes.Clone()
- };
- }
-
- public static class AstExtensions
- {
- public static bool HasAttribute(this IAstNodeWithAttributes node, string s) => node.Attributes.HasAttribute(s);
-
- public static string GetAttribute(this IAstNodeWithAttributes node, string s)
- {
- var value = node.Attributes.FirstOrDefault(a => a.Name == s)?.Value;
- if (value == null)
- throw new CodeGenException("Expected attribute " + s + " for node " + node);
- return value;
- }
-
- public static string GetAttributeOrDefault(this IAstNodeWithAttributes node, string s)
- => node.Attributes.FirstOrDefault(a => a.Name == s)?.Value;
- }
-
- class AstVisitor
- {
- protected virtual void VisitType(AstTypeNode type)
- {
- }
-
- protected virtual void VisitArgument(AstInterfaceMemberArgumentNode argument)
- {
- VisitType(argument.Type);
- }
-
- protected virtual void VisitInterfaceMember(AstInterfaceMemberNode member)
- {
- foreach(var a in member)
- VisitArgument(a);
- VisitType(member.ReturnType);
- }
-
- protected virtual void VisitInterface(AstInterfaceNode iface)
- {
- foreach(var m in iface)
- VisitInterfaceMember(m);
- }
-
- protected virtual void VisitStructMember(AstStructMemberNode member)
- {
- VisitType(member.Type);
- }
-
- protected virtual void VisitStruct(AstStructNode node)
- {
- foreach(var m in node)
- VisitStructMember(m);
- }
-
- public virtual void VisitAst(AstIdlNode ast)
- {
- foreach(var iface in ast.Interfaces)
- VisitInterface(iface);
- foreach (var s in ast.Structs)
- VisitStruct(s);
- }
-
-
- }
-
- public class AstIdlNode : IAstNodeWithAttributes
- {
- public AstAttributes Attributes { get; set; } = new AstAttributes();
- public List Enums { get; set; } = new List();
- public List Structs { get; set; } = new List();
- public List Interfaces { get; set; } = new List();
-
- public AstIdlNode Clone() => new AstIdlNode()
- {
- Attributes = Attributes.Clone(),
- Enums = Enums.Select(x => x.Clone()).ToList(),
- Structs = Structs.Select(x => x.Clone()).ToList(),
- Interfaces = Interfaces.Select(x => x.Clone()).ToList()
- };
- }
-}
diff --git a/src/tools/MicroComGenerator/AstParser.cs b/src/tools/MicroComGenerator/AstParser.cs
deleted file mode 100644
index 388a8eb0186..00000000000
--- a/src/tools/MicroComGenerator/AstParser.cs
+++ /dev/null
@@ -1,232 +0,0 @@
-using System.Collections.Generic;
-using MicroComGenerator.Ast;
-
-namespace MicroComGenerator
-{
- public class AstParser
- {
- public static AstIdlNode Parse(string source)
- {
- var parser = new TokenParser(source);
- var idl = new AstIdlNode { Attributes = ParseGlobalAttributes(ref parser) };
-
- while (!parser.Eof)
- {
- var attrs = ParseLocalAttributes(ref parser);
- if (parser.TryConsume(";"))
- continue;
- if (parser.TryParseKeyword("enum"))
- idl.Enums.Add(ParseEnum(attrs, ref parser));
- else if (parser.TryParseKeyword("struct"))
- idl.Structs.Add(ParseStruct(attrs, ref parser));
- else if (parser.TryParseKeyword("interface"))
- idl.Interfaces.Add(ParseInterface(attrs, ref parser));
- else
- throw new ParseException("Unexpected character", ref parser);
- }
-
- return idl;
- }
-
- static AstAttributes ParseGlobalAttributes(ref TokenParser parser)
- {
- var rv = new AstAttributes();
- while (!parser.Eof)
- {
- parser.SkipWhitespace();
- if (parser.TryConsume('@'))
- {
- var ident = parser.ParseIdentifier("-");
- var value = parser.ReadToEol().Trim();
- if (value == "@@")
- {
- parser.Advance(1);
- value = "";
- while (true)
- {
- var l = parser.ReadToEol();
- if (l == "@@")
- break;
- else
- value = value.Length == 0 ? l : (value + "\n" + l);
- parser.Advance(1);
- }
-
- }
- rv.Add(new AstAttributeNode(ident, value));
- }
- else
- return rv;
- }
-
- return rv;
- }
-
- static AstAttributes ParseLocalAttributes(ref TokenParser parser)
- {
- var rv = new AstAttributes();
- while (parser.TryConsume("["))
- {
- while (!parser.TryConsume("]") && !parser.Eof)
- {
- if (parser.TryConsume(','))
- continue;
-
- // Get identifier
- var ident = parser.ParseIdentifier("-");
-
- // No value, end of attribute list
- if (parser.TryConsume(']'))
- {
- rv.Add(new AstAttributeNode(ident, null));
- break;
- }
- // No value, next attribute
- else if (parser.TryConsume(','))
- rv.Add(new AstAttributeNode(ident, null));
- // Has value
- else if (parser.TryConsume('('))
- {
- var value = parser.ReadTo(')');
- parser.Consume(')');
- rv.Add(new AstAttributeNode(ident, value));
- }
- else
- throw new ParseException("Unexpected character", ref parser);
- }
-
- if (parser.Eof)
- throw new ParseException("Unexpected EOF", ref parser);
- }
-
- return rv;
- }
-
- static void EnsureOpenBracket(ref TokenParser parser)
- {
- if (!parser.TryConsume('{'))
- throw new ParseException("{ expected", ref parser);
- }
-
- static AstEnumNode ParseEnum(AstAttributes attrs, ref TokenParser parser)
- {
- var name = parser.ParseIdentifier();
- EnsureOpenBracket(ref parser);
- var rv = new AstEnumNode { Name = name, Attributes = attrs };
- while (!parser.TryConsume('}') && !parser.Eof)
- {
- if (parser.TryConsume(','))
- continue;
-
- var ident = parser.ParseIdentifier();
-
- // Automatic value
- if (parser.TryConsume(',') || parser.Peek == '}')
- {
- rv.Add(new AstEnumMemberNode(ident, null));
- continue;
- }
-
- if (!parser.TryConsume('='))
- throw new ParseException("Unexpected character", ref parser);
-
- var value = parser.ReadToAny(",}").Trim();
- rv.Add(new AstEnumMemberNode(ident, value));
-
- if (parser.Eof)
- throw new ParseException("Unexpected EOF", ref parser);
- }
-
-
- return rv;
- }
-
- static AstTypeNode ParseType(ref TokenParser parser)
- {
- var ident = parser.ParseIdentifier();
- var t = new AstTypeNode { Name = ident };
- while (parser.TryConsume('*'))
- t.PointerLevel++;
- if (parser.TryConsume("&"))
- t.IsLink = true;
- return t;
- }
-
- static AstStructNode ParseStruct(AstAttributes attrs, ref TokenParser parser)
- {
- var name = parser.ParseIdentifier();
- EnsureOpenBracket(ref parser);
- var rv = new AstStructNode { Name = name, Attributes = attrs };
- while (!parser.TryConsume('}') && !parser.Eof)
- {
- var memberAttrs = ParseLocalAttributes(ref parser);
- var t = ParseType(ref parser);
- bool parsedAtLeastOneMember = false;
- while (!parser.TryConsume(';'))
- {
- // Skip any ,
- while (parser.TryConsume(',')) { }
-
- var ident = parser.ParseIdentifier();
- parsedAtLeastOneMember = true;
- rv.Add(new AstStructMemberNode { Name = ident, Type = t, Attributes = memberAttrs});
- }
-
- if (!parsedAtLeastOneMember)
- throw new ParseException("Expected at least one enum member with declared type " + t, ref parser);
- }
-
- return rv;
- }
-
- static AstInterfaceNode ParseInterface(AstAttributes interfaceAttrs, ref TokenParser parser)
- {
- var interfaceName = parser.ParseIdentifier();
- string inheritsFrom = null;
- if (parser.TryConsume(":"))
- inheritsFrom = parser.ParseIdentifier();
-
- EnsureOpenBracket(ref parser);
- var rv = new AstInterfaceNode
- {
- Name = interfaceName, Attributes = interfaceAttrs, Inherits = inheritsFrom
- };
- while (!parser.TryConsume('}') && !parser.Eof)
- {
- var memberAttrs = ParseLocalAttributes(ref parser);
- var returnType = ParseType(ref parser);
- var name = parser.ParseIdentifier();
- var member = new AstInterfaceMemberNode
- {
- Name = name, ReturnType = returnType, Attributes = memberAttrs
- };
- rv.Add(member);
-
- parser.Consume('(');
- while (true)
- {
- if (parser.TryConsume(')'))
- break;
-
- var argumentAttrs = ParseLocalAttributes(ref parser);
- var type = ParseType(ref parser);
- var argName = parser.ParseIdentifier();
- member.Add(new AstInterfaceMemberArgumentNode
- {
- Name = argName, Type = type, Attributes = argumentAttrs
- });
-
- if (parser.TryConsume(')'))
- break;
- if (parser.TryConsume(','))
- continue;
- throw new ParseException("Unexpected character", ref parser);
- }
-
- parser.Consume(';');
- }
-
- return rv;
- }
- }
-}
diff --git a/src/tools/MicroComGenerator/CSharpGen.InterfaceGen.cs b/src/tools/MicroComGenerator/CSharpGen.InterfaceGen.cs
deleted file mode 100644
index adb8faf9381..00000000000
--- a/src/tools/MicroComGenerator/CSharpGen.InterfaceGen.cs
+++ /dev/null
@@ -1,484 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-using MicroComGenerator.Ast;
-using Microsoft.CodeAnalysis;
-using Microsoft.CodeAnalysis.CSharp;
-using Microsoft.CodeAnalysis.CSharp.Syntax;
-using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
-
-// ReSharper disable CoVariantArrayConversion
-
-// HERE BE DRAGONS
-
-namespace MicroComGenerator
-{
- public partial class CSharpGen
- {
- abstract class Arg
- {
- public string Name;
- public string NativeType;
- public AstAttributes Attributes { get; set; }
- public virtual StatementSyntax CreateFixed(StatementSyntax inner) => inner;
-
- public virtual void PreMarshal(List body)
- {
- }
-
- public virtual void PreMarshalForReturn(List body) =>
- throw new InvalidOperationException("Don't know how to use " + NativeType + " as HRESULT-return");
-
- public virtual ExpressionSyntax Value(bool isHresultReturn) => ParseExpression(Name);
- public abstract string ManagedType { get; }
- public virtual string ReturnManagedType => ManagedType;
-
- public virtual StatementSyntax[] ReturnMarshalResult() => new[] { ParseStatement("return " + Name + ";") };
-
-
- public virtual void BackPreMarshal(List body)
- {
- }
-
- public virtual ExpressionSyntax BackMarshalValue() => ParseExpression(Name);
- public virtual ExpressionSyntax BackMarshalReturn(string resultVar) => ParseExpression(resultVar);
-
- }
-
- class InterfaceReturnArg : Arg
- {
- public string InterfaceType;
- public override ExpressionSyntax Value(bool isHresultReturn) => ParseExpression("&" + PName);
- public override string ManagedType => InterfaceType;
-
- private string PName => "__marshal_" + Name;
-
- public override void PreMarshalForReturn(List body)
- {
- body.Add(ParseStatement("void* " + PName + " = null;"));
- }
-
- public override StatementSyntax[] ReturnMarshalResult() => new[]
- {
- ParseStatement("return Avalonia.MicroCom.MicroComRuntime.CreateProxyFor<" + InterfaceType + ">(" +
- PName + ", true);")
- };
-
- public override ExpressionSyntax BackMarshalValue()
- {
- return ParseExpression("INVALID");
- }
-
- public override ExpressionSyntax BackMarshalReturn(string resultVar)
- {
- return ParseExpression($"Avalonia.MicroCom.MicroComRuntime.GetNativePointer({resultVar}, true)");
- }
- }
-
- class InterfaceArg : Arg
- {
- public string InterfaceType;
-
- public override ExpressionSyntax Value(bool isHresultReturn) =>
- ParseExpression("Avalonia.MicroCom.MicroComRuntime.GetNativePointer(" + Name + ")");
-
- public override string ManagedType => InterfaceType;
-
- public override StatementSyntax[] ReturnMarshalResult() => new[]
- {
- ParseStatement("return Avalonia.MicroCom.MicroComRuntime.CreateProxyFor<" + InterfaceType + ">(" +
- Name + ", true);")
- };
-
- public override ExpressionSyntax BackMarshalValue()
- {
- return ParseExpression("Avalonia.MicroCom.MicroComRuntime.CreateProxyFor<" + InterfaceType + ">(" +
- Name + ", false)");
- }
-
- public override ExpressionSyntax BackMarshalReturn(string resultVar)
- {
- return ParseExpression($"Avalonia.MicroCom.MicroComRuntime.GetNativePointer({resultVar}, true)");
- }
- }
-
- class BypassArg : Arg
- {
- public string Type { get; set; }
- public int PointerLevel;
- public override string ManagedType => Type + new string('*', PointerLevel);
- public override string ReturnManagedType => Type + new string('*', PointerLevel - 1);
-
- public override ExpressionSyntax Value(bool isHresultReturn)
- {
- if (isHresultReturn)
- return ParseExpression("&" + Name);
- return base.Value(false);
- }
-
- public override void PreMarshalForReturn(List body)
- {
- if (PointerLevel == 0)
- base.PreMarshalForReturn(body);
- else
- body.Add(ParseStatement(Type + new string('*', PointerLevel - 1) + " " + Name + "=default;"));
- }
- }
-
- class StringArg : Arg
- {
- private string BName => "__bytemarshal_" + Name;
- private string FName => "__fixedmarshal_" + Name;
-
- public override void PreMarshal(List body)
- {
- body.Add(ParseStatement($"var {BName} = new byte[System.Text.Encoding.UTF8.GetByteCount({Name})+1];"));
- body.Add(ParseStatement($"System.Text.Encoding.UTF8.GetBytes({Name}, 0, {Name}.Length, {BName}, 0);"));
- }
-
- public override StatementSyntax CreateFixed(StatementSyntax inner)
- {
- return FixedStatement(DeclareVar("byte*", FName, ParseExpression(BName)), inner);
- }
-
- public override ExpressionSyntax Value(bool isHresultReturn) => ParseExpression(FName);
- public override string ManagedType => "string";
- public override ExpressionSyntax BackMarshalValue()
- {
- return ParseExpression(
- $"({Name} == null ? null : System.Runtime.InteropServices.Marshal.PtrToStringAnsi(new IntPtr(" + Name + ")))");
- }
- }
-
- string ConvertNativeType(string type)
- {
- if (type == "size_t")
- return "System.IntPtr";
- if (type == "HRESULT")
- return "int";
- return type;
- }
-
- Arg ConvertArg(AstInterfaceMemberArgumentNode node)
- {
- var arg = ConvertArg(node.Name, node.Type);
- arg.Attributes = node.Attributes.Clone();
- return arg;
- }
-
- Arg ConvertArg(string name, AstTypeNode type)
- {
- type = new AstTypeNode { Name = ConvertNativeType(type.Name), PointerLevel = type.PointerLevel };
-
- if (type.PointerLevel == 2)
- {
- if (IsInterface(type))
- return new InterfaceReturnArg { Name = name, InterfaceType = type.Name, NativeType = "void**" };
- }
- else if (type.PointerLevel == 1)
- {
- if (IsInterface(type))
- return new InterfaceArg { Name = name, InterfaceType = type.Name, NativeType = "void*" };
- if (type.Name == "char")
- return new StringArg { Name = name, NativeType = "byte*" };
- }
-
- return new BypassArg
- {
- Name = name, Type = type.Name, PointerLevel = type.PointerLevel, NativeType = type.ToString()
- };
- }
-
-
- void GenerateInterfaceMember(AstInterfaceMemberNode member, ref InterfaceDeclarationSyntax iface,
- ref ClassDeclarationSyntax proxy, ref ClassDeclarationSyntax vtbl,
- List vtblCtor, int num)
- {
- // Prepare method information
- if (member.Name == "GetRenderingDevice")
- Console.WriteLine();
- var args = member.Select(ConvertArg).ToList();
- var returnArg = ConvertArg("__result", member.ReturnType);
- bool isHresult = member.ReturnType.Name == "HRESULT";
- bool isHresultLastArgumentReturn = isHresult
- && args.Count > 0
- && (args.Last().Name == "ppv"
- || args.Last().Name == "retOut"
- || args.Last().Name == "ret"
- || args.Last().Attributes.HasAttribute("out")
- || args.Last().Attributes.HasAttribute("retval")
- )
- && ((member.Last().Type.PointerLevel > 0
- && !IsInterface(member.Last().Type))
- || member.Last().Type.PointerLevel == 2);
-
- bool isVoidReturn = member.ReturnType.Name == "void" && member.ReturnType.PointerLevel == 0;
-
-
- // Generate method signature
- MethodDeclarationSyntax GenerateManagedSig(string returnType, string name,
- IEnumerable<(string n, string t)> args)
- => MethodDeclaration(ParseTypeName(returnType), name).WithParameterList(
- ParameterList(
- SeparatedList(args.Select(x => Parameter(Identifier(x.n)).WithType(ParseTypeName(x.t))))));
-
- var managedSig =
- isHresult ?
- GenerateManagedSig(isHresultLastArgumentReturn ? args.Last().ReturnManagedType : "void",
- member.Name,
- (isHresultLastArgumentReturn ? args.SkipLast(1) : args).Select(a => (a.Name, a.ManagedType))) :
- GenerateManagedSig(returnArg.ManagedType, member.Name, args.Select(a => (a.Name, a.ManagedType)));
-
- iface = iface.AddMembers(managedSig.WithSemicolonToken(Semicolon()));
-
- // Prepare args for marshaling
- var preMarshal = new List();
- if (!isVoidReturn)
- preMarshal.Add(ParseStatement(returnArg.NativeType + " __result;"));
-
- for (var idx = 0; idx < args.Count; idx++)
- {
- if (isHresultLastArgumentReturn && idx == args.Count - 1)
- args[idx].PreMarshalForReturn(preMarshal);
- else
- args[idx].PreMarshal(preMarshal);
- }
-
- // Generate call expression
- ExpressionSyntax callExpr = InvocationExpression(_localInterop.GetCaller(returnArg.NativeType,
- args.Select(x => x.NativeType).ToList()))
- .AddArgumentListArguments(Argument(ParseExpression("PPV")))
- .AddArgumentListArguments(args
- .Select((a, i) => Argument(a.Value(isHresultLastArgumentReturn && i == args.Count - 1))).ToArray())
- .AddArgumentListArguments(Argument(ParseExpression("(*PPV)[base.VTableSize + " + num + "]")));
-
- if (!isVoidReturn)
- callExpr = CastExpression(ParseTypeName(returnArg.NativeType), callExpr);
-
- // Save call result if needed
- if (!isVoidReturn)
- callExpr = AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, ParseExpression("__result"),
- callExpr);
-
-
- // Wrap call into fixed() blocks
- StatementSyntax callStatement = ExpressionStatement(callExpr);
- foreach (var arg in args)
- callStatement = arg.CreateFixed(callStatement);
-
- // Build proxy body
- var proxyBody = Block()
- .AddStatements(preMarshal.ToArray())
- .AddStatements(callStatement);
-
- // Process return value
- if (!isVoidReturn)
- {
- if (isHresult)
- {
- proxyBody = proxyBody.AddStatements(
- ParseStatement(
- $"if(__result != 0) throw new System.Runtime.InteropServices.COMException(\"{member.Name} failed\", __result);"));
-
- if (isHresultLastArgumentReturn)
- proxyBody = proxyBody.AddStatements(args.Last().ReturnMarshalResult());
- }
- else
- proxyBody = proxyBody.AddStatements(returnArg.ReturnMarshalResult());
- }
-
- // Add the proxy method
- proxy = proxy.AddMembers(managedSig.AddModifiers(SyntaxKind.PublicKeyword)
- .WithBody(proxyBody));
-
-
- // Generate VTable method
- var shadowDelegate = DelegateDeclaration(ParseTypeName(returnArg.NativeType), member.Name + "Delegate")
- .AddParameterListParameters(Parameter(Identifier("@this")).WithType(ParseTypeName("IntPtr")))
- .AddParameterListParameters(args.Select(x =>
- Parameter(Identifier(x.Name)).WithType(ParseTypeName(x.NativeType))).ToArray())
- .AddAttribute("System.Runtime.InteropServices.UnmanagedFunctionPointer",
- "System.Runtime.InteropServices.CallingConvention.StdCall");
-
- var shadowMethod = MethodDeclaration(shadowDelegate.ReturnType, member.Name)
- .WithParameterList(shadowDelegate.ParameterList)
- .AddModifiers(Token(SyntaxKind.StaticKeyword));
-
- var backPreMarshal = new List();
- foreach (var arg in args)
- arg.BackPreMarshal(backPreMarshal);
-
- backPreMarshal.Add(
- ParseStatement($"__target = ({iface.Identifier.Text})Avalonia.MicroCom.MicroComRuntime.GetObjectFromCcw(@this);"));
-
- var isBackVoidReturn = isVoidReturn || (isHresult && !isHresultLastArgumentReturn);
-
- StatementSyntax backCallStatement;
-
- var backCallExpr =
- IsPropertyRewriteCandidate(managedSig) ?
- ParseExpression("__target." + member.Name.Substring(3)) :
- InvocationExpression(ParseExpression("__target." + member.Name))
- .WithArgumentList(ArgumentList(SeparatedList(
- (isHresultLastArgumentReturn ? args.SkipLast(1) : args)
- .Select(a =>
- Argument(a.BackMarshalValue())))));
-
- if (isBackVoidReturn)
- backCallStatement = ExpressionStatement(backCallExpr);
- else
- {
- backCallStatement = LocalDeclarationStatement(DeclareVar("var", "__result", backCallExpr));
- if (isHresultLastArgumentReturn)
- {
- backCallStatement = Block(backCallStatement,
- ExpressionStatement(AssignmentExpression(SyntaxKind.SimpleAssignmentExpression,
- ParseExpression("*" + args.Last().Name),
- args.Last().BackMarshalReturn("__result")
- )));
-
- }
- else
- backCallStatement = Block(backCallStatement,
- ReturnStatement(returnArg.BackMarshalReturn("__result")));
- }
-
- BlockSyntax backBodyBlock = Block().AddStatements(backPreMarshal.ToArray()).AddStatements(backCallStatement);
-
-
- var exceptions = new List()
- {
- CatchClause(
- CatchDeclaration(ParseTypeName("System.Exception"), Identifier("__exception__")), null,
- Block(
- ParseStatement(
- "Avalonia.MicroCom.MicroComRuntime.UnhandledException(__target, __exception__);"),
- isHresult ? ParseStatement("return unchecked((int)0x80004005u);")
- : isVoidReturn ? EmptyStatement() : ParseStatement("return default;")
- ))
- };
-
- if (isHresult)
- exceptions.Insert(0, CatchClause(
- CatchDeclaration(ParseTypeName("System.Runtime.InteropServices.COMException"),
- Identifier("__com_exception__")),
- null, Block(ParseStatement("return __com_exception__.ErrorCode;"))));
-
- backBodyBlock = Block(
- TryStatement(
- List(exceptions))
- .WithBlock(Block(backBodyBlock))
- );
- if (isHresult)
- backBodyBlock = backBodyBlock.AddStatements(ParseStatement("return 0;"));
-
-
- backBodyBlock = Block()
- .AddStatements(ParseStatement($"{iface.Identifier.Text} __target = null;"))
- .AddStatements(backBodyBlock.Statements.ToArray());
-
- shadowMethod = shadowMethod.WithBody(backBodyBlock);
-
- vtbl = vtbl.AddMembers(shadowDelegate).AddMembers(shadowMethod);
- vtblCtor.Add(ParseStatement("base.AddMethod((" + shadowDelegate.Identifier.Text + ")" +
- shadowMethod.Identifier.Text + ");"));
-
-
-
-
- }
-
- class LocalInteropHelper
- {
- public ClassDeclarationSyntax Class { get; private set; } = ClassDeclaration("LocalInterop");
- private HashSet _existing = new HashSet();
-
- public ExpressionSyntax GetCaller(string returnType, List args)
- {
- string ConvertType(string t) => t.EndsWith("*") ? "void*" : t;
- returnType = ConvertType(returnType);
- args = args.Select(ConvertType).ToList();
-
- var name = "CalliStdCall" + returnType.Replace("*", "_ptr");
- var signature = returnType + "::" + name + "::" + string.Join("::", args);
- if (_existing.Add(signature))
- {
- Class = Class.AddMembers(MethodDeclaration(ParseTypeName(returnType), name)
- .AddModifiers(SyntaxKind.StaticKeyword, SyntaxKind.UnsafeKeyword, SyntaxKind.PublicKeyword)
- .AddParameterListParameters(Parameter(Identifier("thisObj")).WithType(ParseTypeName("void*")))
- .AddParameterListParameters(args.Select((x, i) =>
- Parameter(Identifier("arg" + i)).WithType(ParseTypeName(x))).ToArray())
- .AddParameterListParameters(Parameter(Identifier("methodPtr")).WithType(ParseTypeName("void*")))
- .WithBody(Block(ExpressionStatement(ThrowExpression(ParseExpression("null"))))));
- }
-
- return ParseExpression("LocalInterop." + name);
- }
- }
-
-
- void GenerateInterface(ref NamespaceDeclarationSyntax ns, ref NamespaceDeclarationSyntax implNs,
- AstInterfaceNode iface)
- {
- var guidString = iface.GetAttribute("uuid");
- var inheritsUnknown = iface.Inherits == null || iface.Inherits == "IUnknown";
-
- var ifaceDec = InterfaceDeclaration(iface.Name)
- .WithBaseType(inheritsUnknown ? "Avalonia.MicroCom.IUnknown" : iface.Inherits)
- .AddModifiers(Token(_visibility), Token(SyntaxKind.UnsafeKeyword), Token(SyntaxKind.PartialKeyword));
-
- var proxyClassName = "__MicroCom" + iface.Name + "Proxy";
- var proxy = ClassDeclaration(proxyClassName)
- .AddModifiers(Token(SyntaxKind.UnsafeKeyword), Token(_visibility), Token(SyntaxKind.PartialKeyword))
- .WithBaseType(inheritsUnknown ?
- "Avalonia.MicroCom.MicroComProxyBase" :
- ("__MicroCom" + iface.Inherits + "Proxy"))
- .AddBaseListTypes(SimpleBaseType(ParseTypeName(iface.Name)));
-
-
- // Generate vtable
- var vtbl = ClassDeclaration("__MicroCom" + iface.Name + "VTable")
- .AddModifiers(Token(SyntaxKind.UnsafeKeyword));
-
- vtbl = vtbl.WithBaseType(inheritsUnknown ?
- "Avalonia.MicroCom.MicroComVtblBase" :
- "__MicroCom" + iface.Inherits + "VTable");
-
- var vtblCtor = new List();
- for (var idx = 0; idx < iface.Count; idx++)
- GenerateInterfaceMember(iface[idx], ref ifaceDec, ref proxy, ref vtbl, vtblCtor, idx);
-
- vtbl = vtbl.AddMembers(
- ConstructorDeclaration(vtbl.Identifier.Text)
- .AddModifiers(Token(SyntaxKind.PublicKeyword))
- .WithBody(Block(vtblCtor))
- )
- .AddMembers(MethodDeclaration(ParseTypeName("void"), "__MicroComModuleInit")
- .AddModifiers(Token(SyntaxKind.StaticKeyword), Token(SyntaxKind.InternalKeyword))
- .WithExpressionBody(ArrowExpressionClause(
- ParseExpression("Avalonia.MicroCom.MicroComRuntime.RegisterVTable(typeof(" +
- iface.Name + "), new " + vtbl.Identifier.Text + "().CreateVTable())")))
- .WithSemicolonToken(Semicolon()));
-
-
- // Finalize proxy code
- proxy = proxy.AddMembers(
- MethodDeclaration(ParseTypeName("void"), "__MicroComModuleInit")
- .AddModifiers(Token(SyntaxKind.StaticKeyword), Token(SyntaxKind.InternalKeyword))
- .WithBody(Block(
- ParseStatement("Avalonia.MicroCom.MicroComRuntime.Register(typeof(" +
- iface.Name + "), new Guid(\"" + guidString + "\"), (p, owns) => new " +
- proxyClassName + "(p, owns));")
- )))
- .AddMembers(ParseMemberDeclaration("public " + proxyClassName +
- "(IntPtr nativePointer, bool ownsHandle) : base(nativePointer, ownsHandle) {}"))
- .AddMembers(ParseMemberDeclaration("protected override int VTableSize => base.VTableSize + " +
- iface.Count + ";"));
-
- ns = ns.AddMembers(RewriteMethodsToProperties(ifaceDec));
- implNs = implNs.AddMembers(RewriteMethodsToProperties(proxy), RewriteMethodsToProperties(vtbl));
- }
- }
-}
diff --git a/src/tools/MicroComGenerator/CSharpGen.Utils.cs b/src/tools/MicroComGenerator/CSharpGen.Utils.cs
deleted file mode 100644
index 28baaa65f83..00000000000
--- a/src/tools/MicroComGenerator/CSharpGen.Utils.cs
+++ /dev/null
@@ -1,111 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using MicroComGenerator.Ast;
-using Microsoft.CodeAnalysis;
-using Microsoft.CodeAnalysis.CSharp;
-using Microsoft.CodeAnalysis.CSharp.Formatting;
-using Microsoft.CodeAnalysis.CSharp.Syntax;
-
-using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
-namespace MicroComGenerator
-{
- public partial class CSharpGen
- {
-
- CompilationUnitSyntax Unit()
- => CompilationUnit().WithUsings(List(new[]
- {
- "System", "System.Text", "System.Collections", "System.Collections.Generic", "Avalonia.MicroCom"
- }
- .Concat(_extraUsings).Select(u => UsingDirective(IdentifierName(u)))));
-
- string Format(CompilationUnitSyntax unit)
- {
- var cw = new AdhocWorkspace();
- return
- "#pragma warning disable 108\n" +
- Microsoft.CodeAnalysis.Formatting.Formatter.Format(unit.NormalizeWhitespace(), cw, cw.Options
- .WithChangedOption(CSharpFormattingOptions.NewLineForMembersInObjectInit, true)
- .WithChangedOption(CSharpFormattingOptions.NewLinesForBracesInObjectCollectionArrayInitializers,
- true)
- .WithChangedOption(CSharpFormattingOptions.NewLineForMembersInAnonymousTypes, true)
- .WithChangedOption(CSharpFormattingOptions.NewLinesForBracesInMethods, true)
-
- ).ToFullString();
- }
-
-
- SyntaxToken Semicolon() => Token(SyntaxKind.SemicolonToken);
-
- static VariableDeclarationSyntax DeclareVar(string type, string name,
- ExpressionSyntax initializer = null)
- => VariableDeclaration(ParseTypeName(type),
- SingletonSeparatedList(VariableDeclarator(name)
- .WithInitializer(initializer == null ? null : EqualsValueClause(initializer))));
-
- FieldDeclarationSyntax DeclareConstant(string type, string name, LiteralExpressionSyntax value)
- => FieldDeclaration(
- VariableDeclaration(ParseTypeName(type),
- SingletonSeparatedList(
- VariableDeclarator(name).WithInitializer(EqualsValueClause(value))
- ))
- ).WithSemicolonToken(Semicolon())
- .WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.ConstKeyword)));
-
- FieldDeclarationSyntax DeclareField(string type, string name, params SyntaxKind[] modifiers) =>
- DeclareField(type, name, null, modifiers);
-
- FieldDeclarationSyntax DeclareField(string type, string name, EqualsValueClauseSyntax initializer,
- params SyntaxKind[] modifiers) =>
- FieldDeclaration(
- VariableDeclaration(ParseTypeName(type),
- SingletonSeparatedList(
- VariableDeclarator(name).WithInitializer(initializer))))
- .WithSemicolonToken(Semicolon())
- .WithModifiers(TokenList(modifiers.Select(x => Token(x))));
-
- bool IsPropertyRewriteCandidate(MethodDeclarationSyntax method)
- {
-
- return
- method.ReturnType.ToFullString() != "void"
- && method.Identifier.Text.StartsWith("Get")
- && method.ParameterList.Parameters.Count == 0;
- }
-
- TypeDeclarationSyntax RewriteMethodsToProperties(T decl) where T : TypeDeclarationSyntax
- {
- var replace = new Dictionary();
- foreach (var method in decl.Members.OfType().ToList())
- {
- if (IsPropertyRewriteCandidate(method))
- {
- var getter = AccessorDeclaration(SyntaxKind.GetAccessorDeclaration);
- if (method.Body != null)
- getter = getter.WithBody(method.Body);
- else
- getter = getter.WithSemicolonToken(Semicolon());
-
- replace[method] = PropertyDeclaration(method.ReturnType,
- method.Identifier.Text.Substring(3))
- .WithModifiers(method.Modifiers).AddAccessorListAccessors(getter);
-
- }
- }
-
- return decl.ReplaceNodes(replace.Keys, (m, m2) => replace[m]);
- }
-
- bool IsInterface(string name)
- {
- if (name == "IUnknown")
- return true;
- return _idl.Interfaces.Any(i => i.Name == name);
- }
-
- private bool IsInterface(AstTypeNode type) => IsInterface(type.Name);
-
- }
-}
diff --git a/src/tools/MicroComGenerator/CSharpGen.cs b/src/tools/MicroComGenerator/CSharpGen.cs
deleted file mode 100644
index ff4c351fd92..00000000000
--- a/src/tools/MicroComGenerator/CSharpGen.cs
+++ /dev/null
@@ -1,155 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Runtime.CompilerServices;
-using MicroComGenerator.Ast;
-using Microsoft.CodeAnalysis;
-using Microsoft.CodeAnalysis.CSharp;
-using Microsoft.CodeAnalysis.CSharp.Syntax;
-using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
-// ReSharper disable CoVariantArrayConversion
-
-namespace MicroComGenerator
-{
- public partial class CSharpGen
- {
- private readonly AstIdlNode _idl;
- private List _extraUsings;
- private string _namespace;
- private SyntaxKind _visibility;
- private LocalInteropHelper _localInterop = new LocalInteropHelper();
-
- public CSharpGen(AstIdlNode idl)
- {
- _idl = idl.Clone();
- new AstRewriter(_idl.Attributes.Where(a => a.Name == "clr-map")
- .Select(x => x.Value.Trim().Split(' '))
- .ToDictionary(x => x[0], x => x[1])
- ).VisitAst(_idl);
-
- _extraUsings = _idl.Attributes.Where(u => u.Name == "clr-using").Select(u => u.Value).ToList();
- _namespace = _idl.GetAttribute("clr-namespace");
- var visibilityString = _idl.GetAttribute("clr-access");
-
- if (visibilityString == "internal")
- _visibility = SyntaxKind.InternalKeyword;
- else if (visibilityString == "public")
- _visibility = SyntaxKind.PublicKeyword;
- else
- throw new CodeGenException("Invalid clr-access attribute");
- }
-
- class AstRewriter : AstVisitor
- {
- private readonly Dictionary _typeMap = new Dictionary();
-
- public AstRewriter(Dictionary typeMap)
- {
- _typeMap = typeMap;
- }
-
- void ConvertIntPtr(AstTypeNode type)
- {
- if (type.Name == "void" && type.PointerLevel > 0)
- {
- type.Name = "IntPtr";
- type.PointerLevel--;
- }
- }
-
- protected override void VisitStructMember(AstStructMemberNode member)
- {
- if (member.HasAttribute("intptr"))
- ConvertIntPtr(member.Type);
- base.VisitStructMember(member);
- }
-
- protected override void VisitType(AstTypeNode type)
- {
- if (type.IsLink)
- {
- type.PointerLevel++;
- type.IsLink = false;
- }
-
- if (_typeMap.TryGetValue(type.Name, out var mapped))
- type.Name = mapped;
-
- base.VisitType(type);
- }
-
- protected override void VisitArgument(AstInterfaceMemberArgumentNode argument)
- {
- if (argument.HasAttribute("intptr"))
- {
- if(argument.Name == "retOut")
- Console.WriteLine();
- ConvertIntPtr(argument.Type);
- }
-
- base.VisitArgument(argument);
- }
-
- protected override void VisitInterfaceMember(AstInterfaceMemberNode member)
- {
- if (member.HasAttribute("intptr"))
- ConvertIntPtr(member.ReturnType);
- if (member.HasAttribute("propget") && !member.Name.StartsWith("Get"))
- member.Name = "Get" + member.Name;
- if (member.HasAttribute("propput") && !member.Name.StartsWith("Set"))
- member.Name = "Set" + member.Name;
- base.VisitInterfaceMember(member);
- }
- }
-
-
- public string Generate()
- {
- var ns = NamespaceDeclaration(ParseName(_namespace));
- var implNs = NamespaceDeclaration(ParseName(_namespace + ".Impl"));
- ns = GenerateEnums(ns);
- ns = GenerateStructs(ns);
- foreach (var i in _idl.Interfaces)
- GenerateInterface(ref ns, ref implNs, i);
-
- implNs = implNs.AddMembers(_localInterop.Class);
- var unit = Unit().AddMembers(ns, implNs);
-
- return Format(unit);
- }
-
- NamespaceDeclarationSyntax GenerateEnums(NamespaceDeclarationSyntax ns)
- {
- return ns.AddMembers(_idl.Enums.Select(e =>
- {
- var dec = EnumDeclaration(e.Name)
- .WithModifiers(TokenList(Token(_visibility)))
- .WithMembers(SeparatedList(e.Select(m =>
- {
- var member = EnumMemberDeclaration(m.Name);
- if (m.Value != null)
- return member.WithEqualsValue(EqualsValueClause(ParseExpression(m.Value)));
- return member;
- })));
- if (e.HasAttribute("flags"))
- dec = dec.AddAttribute("System.Flags");
- return dec;
- }).ToArray());
- }
-
- NamespaceDeclarationSyntax GenerateStructs(NamespaceDeclarationSyntax ns)
- {
- return ns.AddMembers(_idl.Structs.Select(e =>
- StructDeclaration(e.Name)
- .WithModifiers(TokenList(Token(_visibility)))
- .AddAttribute("System.Runtime.InteropServices.StructLayout", "System.Runtime.InteropServices.LayoutKind.Sequential")
- .AddModifiers(Token(SyntaxKind.UnsafeKeyword))
- .WithMembers(new SyntaxList(SeparatedList(e.Select(m =>
- DeclareField(m.Type.ToString(), m.Name, SyntaxKind.PublicKeyword)))))
- ).ToArray());
- }
-
-
-
- }
-}
diff --git a/src/tools/MicroComGenerator/CppGen.cs b/src/tools/MicroComGenerator/CppGen.cs
deleted file mode 100644
index b053088ca9b..00000000000
--- a/src/tools/MicroComGenerator/CppGen.cs
+++ /dev/null
@@ -1,119 +0,0 @@
-using System;
-using System.Linq;
-using System.Text;
-using MicroComGenerator.Ast;
-
-namespace MicroComGenerator
-{
- public class CppGen
- {
- static string ConvertType(AstTypeNode type)
- {
- var name = type.Name;
- if (name == "byte")
- name = "unsigned char";
- else if(name == "uint")
- name = "unsigned int";
-
- type = type.Clone();
- type.Name = name;
- return type.Format();
- }
-
- public static string GenerateCpp(AstIdlNode idl)
- {
- var sb = new StringBuilder();
- var preamble = idl.GetAttributeOrDefault("cpp-preamble");
- if (preamble != null)
- sb.AppendLine(preamble);
-
- foreach (var s in idl.Structs)
- sb.AppendLine("struct " + s.Name + ";");
-
- foreach (var s in idl.Interfaces)
- sb.AppendLine("struct " + s.Name + ";");
-
- foreach (var en in idl.Enums)
- {
- sb.Append("enum ");
- if (en.Attributes.Any(a => a.Name == "class-enum"))
- sb.Append("class ");
- sb.AppendLine(en.Name).AppendLine("{");
-
- foreach (var m in en)
- {
- sb.Append(" ").Append(m.Name);
- if (m.Value != null)
- sb.Append(" = ").Append(m.Value);
- sb.AppendLine(",");
- }
-
- sb.AppendLine("};");
- }
-
- foreach (var s in idl.Structs)
- {
- sb.Append("struct ").AppendLine(s.Name).AppendLine("{");
- foreach (var m in s)
- sb.Append(" ").Append(ConvertType(m.Type)).Append(" ").Append(m.Name).AppendLine(";");
-
- sb.AppendLine("};");
- }
-
- foreach (var i in idl.Interfaces)
- {
- var guidString = i.GetAttribute("uuid");
- var guid = Guid.Parse(guidString).ToString().Replace("-", "");
-
-
- sb.Append("COMINTERFACE(").Append(i.Name).Append(", ")
- .Append(guid.Substring(0, 8)).Append(", ")
- .Append(guid.Substring(8, 4)).Append(", ")
- .Append(guid.Substring(12, 4));
- for (var c = 0; c < 8; c++)
- {
- sb.Append(", ").Append(guid.Substring(16 + c * 2, 2));
- }
-
- sb.Append(") : ");
- if (i.HasAttribute("cpp-virtual-inherits"))
- sb.Append("virtual ");
- sb.AppendLine(i.Inherits ?? "IUnknown")
- .AppendLine("{");
-
- foreach (var m in i)
- {
- sb.Append(" ")
- .Append("virtual ")
- .Append(ConvertType(m.ReturnType))
- .Append(" ").Append(m.Name).Append(" (");
- if (m.Count == 0)
- sb.AppendLine(") = 0;");
- else
- {
- sb.AppendLine();
- for (var c = 0; c < m.Count; c++)
- {
- var arg = m[c];
- sb.Append(" ");
- if (arg.Attributes.Any(a => a.Name == "const"))
- sb.Append("const ");
- sb.Append(ConvertType(arg.Type))
- .Append(" ")
- .Append(arg.Name);
- if (c != m.Count - 1)
- sb.Append(", ");
- sb.AppendLine();
- }
-
- sb.AppendLine(" ) = 0;");
- }
- }
-
- sb.AppendLine("};");
- }
-
- return sb.ToString();
- }
- }
-}
diff --git a/src/tools/MicroComGenerator/Extensions.cs b/src/tools/MicroComGenerator/Extensions.cs
deleted file mode 100644
index c8a4c8f45cd..00000000000
--- a/src/tools/MicroComGenerator/Extensions.cs
+++ /dev/null
@@ -1,97 +0,0 @@
-
-using System;
-using System.Linq;
-using Microsoft.CodeAnalysis;
-using Microsoft.CodeAnalysis.CSharp;
-using Microsoft.CodeAnalysis.CSharp.Syntax;
-using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
-namespace MicroComGenerator
-{
- public static class Extensions
- {
- public static ClassDeclarationSyntax AddModifiers(this ClassDeclarationSyntax cl, params SyntaxKind[] modifiers)
- {
- if (modifiers == null)
- return cl;
- return cl.AddModifiers(modifiers.Select(x => SyntaxFactory.Token(x)).ToArray());
- }
-
- public static MethodDeclarationSyntax AddModifiers(this MethodDeclarationSyntax cl, params SyntaxKind[] modifiers)
- {
- if (modifiers == null)
- return cl;
- return cl.AddModifiers(modifiers.Select(x => SyntaxFactory.Token(x)).ToArray());
- }
-
- public static PropertyDeclarationSyntax AddModifiers(this PropertyDeclarationSyntax cl, params SyntaxKind[] modifiers)
- {
- if (modifiers == null)
- return cl;
- return cl.AddModifiers(modifiers.Select(x => SyntaxFactory.Token(x)).ToArray());
- }
-
- public static ConstructorDeclarationSyntax AddModifiers(this ConstructorDeclarationSyntax cl, params SyntaxKind[] modifiers)
- {
- if (modifiers == null)
- return cl;
- return cl.AddModifiers(modifiers.Select(x => SyntaxFactory.Token(x)).ToArray());
- }
-
- public static AccessorDeclarationSyntax AddModifiers(this AccessorDeclarationSyntax cl, params SyntaxKind[] modifiers)
- {
- if (modifiers == null)
- return cl;
- return cl.AddModifiers(modifiers.Select(x => SyntaxFactory.Token(x)).ToArray());
- }
-
- public static string WithLowerFirst(this string s)
- {
- if (string.IsNullOrEmpty(s))
- return s;
- return char.ToLowerInvariant(s[0]) + s.Substring(1);
- }
-
- public static ExpressionSyntax MemberAccess(params string[] identifiers)
- {
- if (identifiers == null || identifiers.Length == 0)
- throw new ArgumentException();
- var expr = (ExpressionSyntax)IdentifierName(identifiers[0]);
- for (var c = 1; c < identifiers.Length; c++)
- expr = MemberAccess(expr, identifiers[c]);
- return expr;
- }
-
- public static ExpressionSyntax MemberAccess(ExpressionSyntax expr, params string[] identifiers)
- {
- foreach (var i in identifiers)
- expr = MemberAccess(expr, i);
- return expr;
- }
-
- public static MemberAccessExpressionSyntax MemberAccess(ExpressionSyntax expr, string identifier) =>
- MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, expr, IdentifierName(identifier));
-
- public static ClassDeclarationSyntax WithBaseType(this ClassDeclarationSyntax cl, string bt)
- {
- return cl.AddBaseListTypes(SimpleBaseType(SyntaxFactory.ParseTypeName(bt)));
- }
-
- public static InterfaceDeclarationSyntax WithBaseType(this InterfaceDeclarationSyntax cl, string bt)
- {
- return cl.AddBaseListTypes(SimpleBaseType(SyntaxFactory.ParseTypeName(bt)));
- }
-
- public static T AddAttribute(this T member, string attribute, params string[] args) where T : MemberDeclarationSyntax
- {
- return (T)member.AddAttributeLists(AttributeList(SingletonSeparatedList(
- Attribute(ParseName(attribute), AttributeArgumentList(
- SeparatedList(args.Select(a => AttributeArgument(ParseExpression(a)))))))));
- }
-
- public static string StripPrefix(this string s, string prefix) => string.IsNullOrEmpty(s)
- ? s
- : s.StartsWith(prefix)
- ? s.Substring(prefix.Length)
- : s;
- }
-}
diff --git a/src/tools/MicroComGenerator/MicroComGenerator.csproj b/src/tools/MicroComGenerator/MicroComGenerator.csproj
deleted file mode 100644
index 68895b96ca3..00000000000
--- a/src/tools/MicroComGenerator/MicroComGenerator.csproj
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
- Exe
- net6.0
-
-
-
-
-
-
diff --git a/src/tools/MicroComGenerator/ParseException.cs b/src/tools/MicroComGenerator/ParseException.cs
deleted file mode 100644
index cb549181009..00000000000
--- a/src/tools/MicroComGenerator/ParseException.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-using System;
-
-namespace MicroComGenerator
-{
- class ParseException : Exception
- {
- public int Line { get; }
- public int Position { get; }
-
- public ParseException(string message, int line, int position) : base($"({line}, {position}) {message}")
- {
- Line = line;
- Position = position;
- }
-
- public ParseException(string message, ref TokenParser parser) : this(message, parser.Line, parser.Position)
- {
- }
- }
-
- class CodeGenException : Exception
- {
- public CodeGenException(string message) : base(message)
- {
- }
- }
-}
diff --git a/src/tools/MicroComGenerator/Program.cs b/src/tools/MicroComGenerator/Program.cs
deleted file mode 100644
index 2468b1b5a48..00000000000
--- a/src/tools/MicroComGenerator/Program.cs
+++ /dev/null
@@ -1,52 +0,0 @@
-using System;
-using System.IO;
-using System.Linq;
-using CommandLine;
-
-namespace MicroComGenerator
-{
- class Program
- {
- public class Options
- {
- [Option('i', "input", Required = true, HelpText = "Input IDL file")]
- public string Input { get; set; }
-
- [Option("cpp", Required = false, HelpText = "C++ output file")]
- public string CppOutput { get; set; }
-
- [Option("cs", Required = false, HelpText = "C# output file")]
- public string CSharpOutput { get; set; }
-
- }
-
- static int Main(string[] args)
- {
- var p = Parser.Default.ParseArguments(args);
- if (p is NotParsed)
- {
- return 1;
- }
-
- var opts = ((Parsed)p).Value;
-
- var text = File.ReadAllText(opts.Input);
- var ast = AstParser.Parse(text);
-
- if (opts.CppOutput != null)
- File.WriteAllText(opts.CppOutput, CppGen.GenerateCpp(ast));
-
- if (opts.CSharpOutput != null)
- {
- File.WriteAllText(opts.CSharpOutput, new CSharpGen(ast).Generate());
-
- // HACK: Can't work out how to get the VS project system's fast up-to-date checks
- // to ignore the generated code, so as a workaround set the write time to that of
- // the input.
- File.SetLastWriteTime(opts.CSharpOutput, File.GetLastWriteTime(opts.Input));
- }
-
- return 0;
- }
- }
-}
diff --git a/src/tools/MicroComGenerator/TokenParser.cs b/src/tools/MicroComGenerator/TokenParser.cs
deleted file mode 100644
index ea8850b8e4e..00000000000
--- a/src/tools/MicroComGenerator/TokenParser.cs
+++ /dev/null
@@ -1,417 +0,0 @@
-using System;
-using System.Globalization;
-using System.IO;
-
-namespace MicroComGenerator
-{
- internal ref struct TokenParser
- {
- private ReadOnlySpan _s;
- public int Position { get; private set; }
- public int Line { get; private set; }
- public TokenParser(ReadOnlySpan s)
- {
- _s = s;
- Position = 0;
- Line = 0;
- }
-
- public void SkipWhitespace()
- {
- while (true)
- {
- if(_s.Length == 0)
- return;
- if (char.IsWhiteSpace(_s[0]))
- Advance(1);
- else if (_s[0] == '/' && _s.Length>1)
- {
- if (_s[1] == '/')
- SkipOneLineComment();
- else if (_s[1] == '*')
- SkipMultiLineComment();
- else
- return;
- }
- else
- return;
- }
- }
-
- void SkipOneLineComment()
- {
- while (true)
- {
- if (_s.Length > 0 && _s[0] != '\n' && _s[0] != '\r')
- Advance(1);
- else
- return;
- }
- }
-
- void SkipMultiLineComment()
- {
- var l = Line;
- var p = Position;
- while (true)
- {
- if (_s.Length == 0)
- throw new ParseException("No matched */ found for /*", l, p);
-
- if (_s[0] == '*' && _s.Length > 1 && _s[1] == '/')
- {
- Advance(2);
- return;
- }
-
- Advance(1);
- }
- }
-
- static bool IsAlphaNumeric(char ch) => (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z') ||
- (ch >= 'A' && ch <= 'Z');
-
- public void Consume(char c)
- {
- if (!TryConsume(c))
- throw new ParseException("Expected " + c, Line, Position);
- }
- public bool TryConsume(char c)
- {
- SkipWhitespace();
- if (_s.Length == 0 || _s[0] != c)
- return false;
-
- Advance(1);
- return true;
- }
-
- public bool TryConsume(string s)
- {
- SkipWhitespace();
- if (_s.Length < s.Length)
- return false;
- for (var c = 0; c < s.Length; c++)
- {
- if (_s[c] != s[c])
- return false;
- }
-
- Advance(s.Length);
- return true;
- }
-
- public bool TryConsumeAny(ReadOnlySpan chars, out char token)
- {
- SkipWhitespace();
- token = default;
- if (_s.Length == 0)
- return false;
-
- foreach (var c in chars)
- {
- if (c == _s[0])
- {
- token = c;
- Advance(1);
- return true;
- }
- }
-
- return false;
- }
-
-
- public bool TryParseKeyword(string keyword)
- {
- SkipWhitespace();
- if (keyword.Length > _s.Length)
- return false;
- for(var c=0; c keyword.Length && IsAlphaNumeric(_s[keyword.Length]))
- return false;
-
- Advance(keyword.Length);
- return true;
- }
-
- public bool TryParseKeywordLowerCase(string keywordInLowerCase)
- {
- SkipWhitespace();
- if (keywordInLowerCase.Length > _s.Length)
- return false;
- for(var c=0; c keywordInLowerCase.Length && IsAlphaNumeric(_s[keywordInLowerCase.Length]))
- return false;
-
- Advance(keywordInLowerCase.Length);
- return true;
- }
-
- public void Advance(int c)
- {
- while (c > 0)
- {
- if (_s[0] == '\n')
- {
- Line++;
- Position = 0;
- }
- else
- Position++;
-
- _s = _s.Slice(1);
- c--;
- }
- }
-
- public int Length => _s.Length;
- public bool Eof
- {
- get
- {
- SkipWhitespace();
- return Length == 0;
- }
- }
-
- public char Peek
- {
- get
- {
- if (_s.Length == 0)
- throw new ParseException("Unexpected EOF", Line, Position);
- return _s[0];
- }
- }
-
- public string ParseIdentifier(ReadOnlySpan extraValidChars)
- {
- if (!TryParseIdentifier(extraValidChars, out var ident))
- throw new ParseException("Identifier expected", Line, Position);
- return ident.ToString();
- }
-
- public string ParseIdentifier()
- {
- if (!TryParseIdentifier(out var ident))
- throw new ParseException("Identifier expected", Line, Position);
- return ident.ToString();
- }
-
- public bool TryParseIdentifier(ReadOnlySpan extraValidChars, out ReadOnlySpan res)
- {
- res = ReadOnlySpan.Empty;
- SkipWhitespace();
- if (_s.Length == 0)
- return false;
- var first = _s[0];
- if (!((first >= 'a' && first <= 'z') || (first >= 'A' && first <= 'Z') || first == '_'))
- return false;
- int len = 1;
- for (var c = 1; c < _s.Length; c++)
- {
- var ch = _s[c];
- if (IsAlphaNumeric(ch) || ch == '_')
- len++;
- else
- {
- var found = false;
- foreach(var vc in extraValidChars)
- if (vc == ch)
- {
- found = true;
- break;
- }
-
- if (found)
- len++;
- else
- break;
- }
- }
-
- res = _s.Slice(0, len);
- Advance(len);
- return true;
- }
-
- public bool TryParseIdentifier(out ReadOnlySpan res)
- {
- res = ReadOnlySpan.Empty;
- SkipWhitespace();
- if (_s.Length == 0)
- return false;
- var first = _s[0];
- if (!((first >= 'a' && first <= 'z') || (first >= 'A' && first <= 'Z') || first == '_'))
- return false;
- int len = 1;
- for (var c = 1; c < _s.Length; c++)
- {
- var ch = _s[c];
- if (IsAlphaNumeric(ch) || ch == '_')
- len++;
- else
- break;
- }
-
- res = _s.Slice(0, len);
- Advance(len);
- return true;
- }
-
- public string ReadToEol()
- {
- var initial = _s;
- var len = 0;
- while (true)
- {
- if (_s.Length > 0 && _s[0] != '\n' && _s[0] != '\r')
- {
- len++;
- Advance(1);
- }
- else
- return initial.Slice(0, len).ToString();
- }
- }
-
- public string ReadTo(char c)
- {
- var initial = _s;
- var len = 0;
- var l = Line;
- var p = Position;
- while (true)
- {
- if (_s.Length == 0)
- throw new ParseException("Expected " + c + " before EOF", l, p);
-
- if (_s[0] != c)
- {
- len++;
- Advance(1);
- }
- else
- return initial.Slice(0, len).ToString();
- }
- }
-
- public string ReadToAny(ReadOnlySpan chars)
- {
- var initial = _s;
- var len = 0;
- var l = Line;
- var p = Position;
- while (true)
- {
- if (_s.Length == 0)
- throw new ParseException("Expected any of '" + chars.ToString() + "' before EOF", l, p);
-
- var foundTerminator = false;
- foreach (var term in chars)
- {
- if (_s[0] == term)
- {
- foundTerminator = true;
- break;
- }
- }
-
- if (!foundTerminator)
- {
- len++;
- Advance(1);
- }
- else
- return initial.Slice(0, len).ToString();
- }
- }
-
- public bool TryParseCall(out ReadOnlySpan res)
- {
- res = ReadOnlySpan.Empty;
- SkipWhitespace();
- if (_s.Length == 0)
- return false;
- var first = _s[0];
- if (!((first >= 'a' && first <= 'z') || (first >= 'A' && first <= 'Z')))
- return false;
- int len = 1;
- for (var c = 1; c < _s.Length; c++)
- {
- var ch = _s[c];
- if ((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch<= 'Z') || ch == '.')
- len++;
- else
- break;
- }
-
- res = _s.Slice(0, len);
-
- // Find '('
- for (var c = len; c < _s.Length; c++)
- {
- if(char.IsWhiteSpace(_s[c]))
- continue;
- if(_s[c]=='(')
- {
- Advance(c + 1);
- return true;
- }
-
- return false;
-
- }
-
- return false;
-
- }
-
-
- public bool TryParseFloat(out float res)
- {
- res = 0;
- SkipWhitespace();
- if (_s.Length == 0)
- return false;
-
- var len = 0;
- var dotCount = 0;
- for (var c = 0; c < _s.Length; c++)
- {
- var ch = _s[c];
- if (ch >= '0' && ch <= '9')
- len = c + 1;
- else if (ch == '.' && dotCount == 0)
- {
- len = c + 1;
- dotCount++;
- }
- else
- break;
- }
-
- var span = _s.Slice(0, len);
-
-#if NETSTANDARD2_0
- if (!float.TryParse(span.ToString(), NumberStyles.Number, CultureInfo.InvariantCulture, out res))
- return false;
-#else
- if (!float.TryParse(span, NumberStyles.Number, CultureInfo.InvariantCulture, out res))
- return false;
-#endif
- Advance(len);
- return true;
- }
-
- public override string ToString() => _s.ToString();
-
- }
-}
diff --git a/tests/Avalonia.Animation.UnitTests/BrushTransitionTests.cs b/tests/Avalonia.Animation.UnitTests/BrushTransitionTests.cs
new file mode 100644
index 00000000000..1986bd2ee39
--- /dev/null
+++ b/tests/Avalonia.Animation.UnitTests/BrushTransitionTests.cs
@@ -0,0 +1,54 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Avalonia.Controls;
+using Avalonia.Media;
+using Xunit;
+
+namespace Avalonia.Animation.UnitTests
+{
+ public class BrushTransitionTests
+ {
+ [Fact]
+ public void SolidColorBrush_Opacity_IsInteroplated()
+ {
+ Test(0, new SolidColorBrush { Opacity = 0 }, new SolidColorBrush { Opacity = 0 });
+ Test(0, new SolidColorBrush { Opacity = 0 }, new SolidColorBrush { Opacity = 1 });
+ Test(0.5, new SolidColorBrush { Opacity = 0 }, new SolidColorBrush { Opacity = 1 });
+ Test(0.5, new SolidColorBrush { Opacity = 0.5 }, new SolidColorBrush { Opacity = 0.5 });
+ Test(1, new SolidColorBrush { Opacity = 1 }, new SolidColorBrush { Opacity = 1 });
+ // TODO: investigate why this case fails.
+ //Test2(1, new SolidColorBrush { Opacity = 0 }, new SolidColorBrush { Opacity = 1 });
+ }
+
+ [Fact]
+ public void LinearGradientBrush_Opacity_IsInteroplated()
+ {
+ Test(0, new LinearGradientBrush { Opacity = 0 }, new LinearGradientBrush { Opacity = 0 });
+ Test(0, new LinearGradientBrush { Opacity = 0 }, new LinearGradientBrush { Opacity = 1 });
+ Test(0.5, new LinearGradientBrush { Opacity = 0 }, new LinearGradientBrush { Opacity = 1 });
+ Test(0.5, new LinearGradientBrush { Opacity = 0.5 }, new LinearGradientBrush { Opacity = 0.5 });
+ Test(1, new LinearGradientBrush { Opacity = 1 }, new LinearGradientBrush { Opacity = 1 });
+ }
+
+ private static void Test(double progress, IBrush oldBrush, IBrush newBrush)
+ {
+ var clock = new TestClock();
+ var border = new Border() { Background = oldBrush };
+ BrushTransition sut = new BrushTransition
+ {
+ Duration = TimeSpan.FromSeconds(1),
+ Property = Border.BackgroundProperty
+ };
+
+ sut.Apply(border, clock, oldBrush, newBrush);
+ clock.Pulse(TimeSpan.Zero);
+ clock.Pulse(sut.Duration * progress);
+
+ Assert.NotNull(border.Background);
+ Assert.Equal(oldBrush.Opacity + (newBrush.Opacity - oldBrush.Opacity) * progress, border.Background.Opacity);
+ }
+ }
+}