From 13cd835bc0b2b33e8a2884f21b91080d72c8126d Mon Sep 17 00:00:00 2001 From: adospace Date: Sat, 7 Nov 2020 00:00:18 +0100 Subject: [PATCH 001/100] ItemsControl+ItemVirtualizerSimple did not recreated item containers when Items or ItemTemplate were replaced --- global.json | 2 +- src/Avalonia.Controls/ItemsControl.cs | 6 +- .../Presenters/ItemVirtualizerSimple.cs | 4 + .../Presenters/ItemsPresenterBase.cs | 4 +- .../ItemsControlTests.cs | 92 ++++++++++++++ .../ListBoxTests.cs | 112 ++++++++++++++++++ 6 files changed, 217 insertions(+), 3 deletions(-) diff --git a/global.json b/global.json index b2b2da7c4f..684d9bed71 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "3.1.401" + "version": "3.1.301" }, "msbuild-sdks": { "Microsoft.Build.Traversal": "1.0.43", diff --git a/src/Avalonia.Controls/ItemsControl.cs b/src/Avalonia.Controls/ItemsControl.cs index 4dc8aec6f3..f955df5f21 100644 --- a/src/Avalonia.Controls/ItemsControl.cs +++ b/src/Avalonia.Controls/ItemsControl.cs @@ -449,7 +449,11 @@ namespace Avalonia.Controls if (_itemContainerGenerator != null) { _itemContainerGenerator.ItemTemplate = (IDataTemplate)e.NewValue; - // TODO: Rebuild the item containers. + + if (e.OldValue != null && Presenter != null) + { + Presenter.ItemsChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); + } } } diff --git a/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs b/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs index 7d50ef7d33..51dbc969a8 100644 --- a/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs +++ b/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs @@ -200,6 +200,10 @@ namespace Avalonia.Controls.Presenters break; case NotifyCollectionChangedAction.Reset: + Owner.ItemContainerGenerator.Clear(); + VirtualizingPanel.Children.Clear(); + FirstIndex = NextIndex = 0; + RecycleContainersOnRemove(); CreateAndRemoveContainers(); panel.ForceInvalidateMeasure(); diff --git a/src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs b/src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs index 52f173fc71..6c408bbed9 100644 --- a/src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs +++ b/src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs @@ -57,6 +57,8 @@ namespace Avalonia.Controls.Presenters set { + var itemsReplaced = (_items != value); + _itemsSubscription?.Dispose(); _itemsSubscription = null; @@ -67,7 +69,7 @@ namespace Avalonia.Controls.Presenters SetAndRaise(ItemsProperty, ref _items, value); - if (_createdPanel) + if (_createdPanel && itemsReplaced) { ItemsChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); } diff --git a/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs b/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs index 684486cbae..157eefb84a 100644 --- a/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs @@ -596,6 +596,98 @@ namespace Avalonia.Controls.UnitTests root.Child = target; } + [Fact] + public void Presenter_Items_Should_Be_In_Sync_When_Replacing_Items() + { + var target = new ItemsControl + { + Template = GetTemplate(), + Items = new[] + { + new Item("Item1") + } + }; + + var root = new TestRoot { Child = target }; + var otherPanel = new StackPanel(); + + target.ApplyTemplate(); + target.Presenter.ApplyTemplate(); + + int dematerializedEventCallCount = 0; + target.ItemContainerGenerator.Dematerialized += (s, e) => + { + Assert.IsType(e.Containers[0].Item); + Assert.Equal("Item1", ((Item)e.Containers[0].Item).Value); + dematerializedEventCallCount++; + }; + + int materializedEventCallCount = 0; + target.ItemContainerGenerator.Materialized += (s, e) => + { + Assert.IsType(e.Containers[0].Item); + Assert.Equal("Item2", ((Item)e.Containers[0].Item).Value); + materializedEventCallCount++; + }; + + target.Items = new[] + { + new Item("Item2") + }; + + //Ensure that events are called one time only + Assert.Equal(1, dematerializedEventCallCount); + Assert.Equal(1, materializedEventCallCount); + } + + [Fact] + public void Presenter_Items_Should_Be_In_Sync_When_Replacing_ItemTemplate() + { + var target = new ItemsControl + { + Template = GetTemplate(), + Items = new[] + { + new Item("Item1") + }, + ItemTemplate = new FuncDataTemplate((x, ns) => new TextBlock()) + }; + + var root = new TestRoot { Child = target }; + var otherPanel = new StackPanel(); + + target.ApplyTemplate(); + target.Presenter.ApplyTemplate(); + + int dematerializedEventCallCount = 0; + target.ItemContainerGenerator.Dematerialized += (s, e) => + { + Assert.IsType(e.Containers[0].Item); + Assert.Equal("Item1", ((Item)e.Containers[0].Item).Value); + var contentPresenter = ((ContentPresenter)e.Containers[0].ContainerControl); + contentPresenter.UpdateChild(); + Assert.IsType(contentPresenter.Child); + dematerializedEventCallCount++; + }; + + int materializedEventCallCount = 0; + target.ItemContainerGenerator.Materialized += (s, e) => + { + Assert.IsType(e.Containers[0].Item); + Assert.Equal("Item1", ((Item)e.Containers[0].Item).Value); + var contentPresenter = ((ContentPresenter)e.Containers[0].ContainerControl); + contentPresenter.UpdateChild(); + Assert.IsType(contentPresenter.Child); + materializedEventCallCount++; + }; + + target.ItemTemplate = + new FuncDataTemplate((x, ns) => new Canvas()); + + Assert.Equal(1, dematerializedEventCallCount); + Assert.Equal(1, materializedEventCallCount); + } + private class Item { public Item(string value) diff --git a/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs b/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs index 145fce4fed..364cb01c65 100644 --- a/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs @@ -454,6 +454,118 @@ namespace Avalonia.Controls.UnitTests } } + + [Fact] + public void ListBox_Presenter_Items_Should_Be_In_Sync_When_Replacing_Items() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var wnd = new Window() { Width = 100, Height = 100, IsVisible = true }; + + var target = new ListBox() + { + VerticalAlignment = Layout.VerticalAlignment.Top, + AutoScrollToSelectedItem = true, + Width = 50, + VirtualizationMode = ItemVirtualizationMode.Simple, + Items = new[] + { + new Item("Item1") + }, + }; + wnd.Content = target; + + var lm = wnd.LayoutManager; + + lm.ExecuteInitialLayoutPass(); + + int dematerializedEventCallCount = 0; + target.ItemContainerGenerator.Dematerialized += (s, e) => + { + Assert.IsType(e.Containers[0].Item); + Assert.Equal("Item1", ((Item)e.Containers[0].Item).Value); + dematerializedEventCallCount++; + }; + + int materializedEventCallCount = 0; + target.ItemContainerGenerator.Materialized += (s, e) => + { + Assert.IsType(e.Containers[0].Item); + Assert.Equal("Item2", ((Item)e.Containers[0].Item).Value); + materializedEventCallCount++; + }; + + target.Items = new[] + { + new Item("Item2") + }; + + //assert that materialize/dematerialize events are called exactly one time + Assert.Equal(1, dematerializedEventCallCount); + Assert.Equal(1, materializedEventCallCount); + } + } + + [Fact] + public void ListBox_Items_Should_Be_In_Sync_When_Replacing_ItemTemplate() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var wnd = new Window() { Width = 100, Height = 100, IsVisible = true }; + + var target = new ListBox() + { + VerticalAlignment = Layout.VerticalAlignment.Top, + AutoScrollToSelectedItem = true, + Width = 50, + VirtualizationMode = ItemVirtualizationMode.Simple, + Items = new[] + { + new Item("Item1") + }, + ItemTemplate = + new FuncDataTemplate((x, ns) => new Canvas()) + }; + + wnd.Content = target; + + var lm = wnd.LayoutManager; + + lm.ExecuteInitialLayoutPass(); + + int dematerializedEventCallCount = 0; + target.ItemContainerGenerator.Dematerialized += (s, e) => + { + Assert.IsType(e.Containers[0].Item); + Assert.Equal("Item1", ((Item)e.Containers[0].Item).Value); + Assert.IsType(((ListBoxItem)e.Containers[0].ContainerControl).Presenter.Child); + dematerializedEventCallCount++; + }; + + int materializedEventCallCount = 0; + ListBoxItem materializedListBoxItem = null; + target.ItemContainerGenerator.Materialized += (s, e) => + { + Assert.IsType(e.Containers[0].Item); + Assert.Equal("Item1", ((Item)e.Containers[0].Item).Value); + materializedListBoxItem = ((ListBoxItem)e.Containers[0].ContainerControl); + materializedEventCallCount++; + }; + + target.ItemTemplate = + new FuncDataTemplate((x, ns) => new TextBlock()); + + //ensure events are called only one time + Assert.Equal(1, dematerializedEventCallCount); + Assert.Equal(1, materializedEventCallCount); + + wnd.LayoutManager.ExecuteLayoutPass(); + + //ensure that new template has been applied + Assert.IsType(materializedListBoxItem.Presenter.Child); + } + } + private FuncControlTemplate ListBoxTemplate() { return new FuncControlTemplate((parent, scope) => From a1d9d5296fb328a3f18f3c3c98bf4cbefb9603bf Mon Sep 17 00:00:00 2001 From: adospace Date: Sat, 7 Nov 2020 00:05:25 +0100 Subject: [PATCH 002/100] Set sdk version back to 3.1.401 --- global.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/global.json b/global.json index 684d9bed71..b2b2da7c4f 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "3.1.301" + "version": "3.1.401" }, "msbuild-sdks": { "Microsoft.Build.Traversal": "1.0.43", From 59d531b9f310c27ce2c74b46ef96f45f5994dc40 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 10 Nov 2020 13:56:07 +0100 Subject: [PATCH 003/100] Don't use manual FBO for RenderTargetBitmap/VisualBrush. Fixes the first part of #4944. --- src/Skia/Avalonia.Skia/DrawingContextImpl.cs | 12 +++++++----- src/Skia/Avalonia.Skia/PlatformRenderInterface.cs | 3 ++- src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs | 5 ++++- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs index 44e0c82110..2a79a4bb50 100644 --- a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs +++ b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs @@ -434,7 +434,7 @@ namespace Avalonia.Skia /// public IDrawingContextLayerImpl CreateLayer(Size size) { - return CreateRenderTarget( size); + return CreateRenderTarget(size, true); } /// @@ -673,7 +673,7 @@ namespace Avalonia.Skia private void ConfigureTileBrush(ref PaintWrapper paintWrapper, Size targetSize, ITileBrush tileBrush, IDrawableBitmapImpl tileBrushImage) { var calc = new TileBrushCalculator(tileBrush, tileBrushImage.PixelSize.ToSizeWithDpi(_dpi), targetSize); - var intermediate = CreateRenderTarget(calc.IntermediateSize); + var intermediate = CreateRenderTarget(calc.IntermediateSize, false); paintWrapper.AddDisposable(intermediate); @@ -748,7 +748,7 @@ namespace Avalonia.Skia if (intermediateSize.Width >= 1 && intermediateSize.Height >= 1) { - var intermediate = CreateRenderTarget(intermediateSize); + var intermediate = CreateRenderTarget(intermediateSize, false); using (var ctx = intermediate.CreateDrawingContext(visualBrushRenderer)) { @@ -978,9 +978,10 @@ namespace Avalonia.Skia /// Create new render target compatible with this drawing context. /// /// The size of the render target in DIPs. + /// Whether the render target is being created for a layer. /// Pixel format. /// - private SurfaceRenderTarget CreateRenderTarget(Size size, PixelFormat? format = null) + private SurfaceRenderTarget CreateRenderTarget(Size size, bool isLayer, PixelFormat? format = null) { var pixelSize = PixelSize.FromSizeWithDpi(size, _dpi); var createInfo = new SurfaceRenderTarget.CreateInfo @@ -992,7 +993,8 @@ namespace Avalonia.Skia DisableTextLcdRendering = !_canTextUseLcdRendering, GrContext = _grContext, Gpu = _gpu, - Session = _session + Session = _session, + DisableManualFbo = !isLayer, }; return new SurfaceRenderTarget(createInfo); diff --git a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs index d6f76a2c20..72700fb8fd 100644 --- a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs +++ b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs @@ -124,7 +124,8 @@ namespace Avalonia.Skia Width = size.Width, Height = size.Height, Dpi = dpi, - DisableTextLcdRendering = false + DisableTextLcdRendering = false, + DisableManualFbo = true, }; return new SurfaceRenderTarget(createInfo); diff --git a/src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs b/src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs index 6347c64aed..01b7449b64 100644 --- a/src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs +++ b/src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs @@ -51,7 +51,8 @@ namespace Avalonia.Skia _grContext = createInfo.GrContext; _gpu = createInfo.Gpu; - _surface = _gpu?.TryCreateSurface(PixelSize, createInfo.Session); + if (!createInfo.DisableManualFbo) + _surface = _gpu?.TryCreateSurface(PixelSize, createInfo.Session); if (_surface == null) _surface = new SkiaSurfaceWrapper(CreateSurface(createInfo.GrContext, PixelSize.Width, PixelSize.Height, createInfo.Format)); @@ -220,6 +221,8 @@ namespace Avalonia.Skia public ISkiaGpu Gpu; public ISkiaGpuRenderSession Session; + + public bool DisableManualFbo; } } } From f9a6da26da55d5ebae013610b544258fb6f9ad89 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 10 Nov 2020 16:59:41 +0100 Subject: [PATCH 004/100] Don't respect root position when building scene. `VisualBrush` should not respect the root control's `Bounds.Position` and always display the root control at 0,0. Because other top level controls should always have a position of 0,0 anyway this should only affect visual brushes. Fixes the second part of #4944 --- .../Rendering/SceneGraph/SceneBuilder.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs index 872f69c884..7d5d62a091 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs @@ -24,7 +24,8 @@ namespace Avalonia.Rendering.SceneGraph using (var impl = new DeferredDrawingContextImpl(this, scene.Layers)) using (var context = new DrawingContext(impl)) { - Update(context, scene, (VisualNode)scene.Root, scene.Root.Visual.Bounds, true); + var clip = new Rect(scene.Root.Visual.Bounds.Size); + Update(context, scene, (VisualNode)scene.Root, clip, true); } } @@ -77,7 +78,7 @@ namespace Avalonia.Rendering.SceneGraph using (var impl = new DeferredDrawingContextImpl(this, scene.Layers)) using (var context = new DrawingContext(impl)) { - var clip = scene.Root.Visual.Bounds; + var clip = new Rect(scene.Root.Visual.Bounds.Size); if (node.Parent != null) { @@ -174,7 +175,9 @@ namespace Avalonia.Rendering.SceneGraph if (visual.IsVisible) { - var m = Matrix.CreateTranslation(visual.Bounds.Position); + var m = node != scene.Root ? + Matrix.CreateTranslation(visual.Bounds.Position) : + Matrix.Identity; var renderTransform = Matrix.Identity; From e17f18de45ed3b9c1c851dafefeb904f62346329 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 10 Nov 2020 17:10:38 +0100 Subject: [PATCH 005/100] Update SelectionBoxItem on visual tree attach. Fixes initial sizing of `SelectionBoxItem` when it's a control. --- src/Avalonia.Controls/ComboBox.cs | 32 ++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/Avalonia.Controls/ComboBox.cs b/src/Avalonia.Controls/ComboBox.cs index 02a9daee75..7f2acb58fe 100644 --- a/src/Avalonia.Controls/ComboBox.cs +++ b/src/Avalonia.Controls/ComboBox.cs @@ -173,11 +173,10 @@ namespace Avalonia.Controls ComboBoxItem.ContentTemplateProperty); } - /// - protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e) + protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) { - base.OnAttachedToLogicalTree(e); - this.UpdateSelectionBoxItem(this.SelectedItem); + base.OnAttachedToVisualTree(e); + this.UpdateSelectionBoxItem(SelectedItem); } /// @@ -373,19 +372,22 @@ namespace Avalonia.Controls if (control != null) { - control.Measure(Size.Infinity); - - SelectionBoxItem = new Rectangle + if (VisualRoot is object) { - Width = control.DesiredSize.Width, - Height = control.DesiredSize.Height, - Fill = new VisualBrush + control.Measure(Size.Infinity); + + SelectionBoxItem = new Rectangle { - Visual = control, - Stretch = Stretch.None, - AlignmentX = AlignmentX.Left, - } - }; + Width = control.DesiredSize.Width, + Height = control.DesiredSize.Height, + Fill = new VisualBrush + { + Visual = control, + Stretch = Stretch.None, + AlignmentX = AlignmentX.Left, + } + }; + } } else { From c6540c1bf266c619634551d7cfb2b3005bb23555 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 10 Nov 2020 18:09:49 +0100 Subject: [PATCH 006/100] Fix failing test. --- tests/Avalonia.Controls.UnitTests/ComboBoxTests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/Avalonia.Controls.UnitTests/ComboBoxTests.cs b/tests/Avalonia.Controls.UnitTests/ComboBoxTests.cs index 783215fb5d..c8a30a42e9 100644 --- a/tests/Avalonia.Controls.UnitTests/ComboBoxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ComboBoxTests.cs @@ -40,6 +40,7 @@ namespace Avalonia.Controls.UnitTests Items = items, SelectedIndex = 0, }; + var root = new TestRoot(target); var rectangle = target.GetValue(ComboBox.SelectionBoxItemProperty) as Rectangle; Assert.NotNull(rectangle); From 3b18572dd60cc0222ef7717d9455557ce01b1676 Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Sat, 14 Nov 2020 10:49:34 +0800 Subject: [PATCH 007/100] add property listening for path segments --- src/Avalonia.Visuals/Media/PathFigure.cs | 32 +++++++++++++++++++++- src/Avalonia.Visuals/Media/PathGeometry.cs | 20 ++++++++++++-- 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.Visuals/Media/PathFigure.cs b/src/Avalonia.Visuals/Media/PathFigure.cs index d0eb67ba39..ff63a6286c 100644 --- a/src/Avalonia.Visuals/Media/PathFigure.cs +++ b/src/Avalonia.Visuals/Media/PathFigure.cs @@ -1,3 +1,6 @@ +using System; +using System.ComponentModel; +using Avalonia.Collections; using Avalonia.Metadata; namespace Avalonia.Media @@ -25,14 +28,41 @@ namespace Avalonia.Media public static readonly StyledProperty StartPointProperty = AvaloniaProperty.Register(nameof(StartPoint)); + internal event EventHandler SegmentsInvalidated; + + private IDisposable? _segmentsObserver; + + private IDisposable? _segmentsPropertiesObserver; + /// /// Initializes a new instance of the class. /// public PathFigure() { + SegmentsProperty.Changed.AddClassHandler((s, e) => + s.OnSegmentsChanged(e.NewValue as PathSegments)); + Segments = new PathSegments(); } + private void OnSegmentsChanged(PathSegments? arg2NewValue) + { + _segmentsObserver?.Dispose(); + _segmentsPropertiesObserver?.Dispose(); + + _segmentsObserver = _segments?.ForEachItem( + _ => InvalidateSegments(), + _ => InvalidateSegments(), + InvalidateSegments); + + _segmentsPropertiesObserver = _segments?.TrackItemPropertyChanged(_ => InvalidateSegments()); + } + + private void InvalidateSegments() + { + SegmentsInvalidated?.Invoke(this, EventArgs.Empty); + } + /// /// Gets or sets a value indicating whether this instance is closed. /// @@ -99,4 +129,4 @@ namespace Avalonia.Media public override string ToString() => $"M {StartPoint} {string.Join(" ", _segments)}{(IsClosed ? "Z" : "")}"; } -} \ No newline at end of file +} diff --git a/src/Avalonia.Visuals/Media/PathGeometry.cs b/src/Avalonia.Visuals/Media/PathGeometry.cs index fbc29aedc8..819669d86e 100644 --- a/src/Avalonia.Visuals/Media/PathGeometry.cs +++ b/src/Avalonia.Visuals/Media/PathGeometry.cs @@ -104,12 +104,26 @@ namespace Avalonia.Media _figuresPropertiesObserver?.Dispose(); _figuresObserver = figures?.ForEachItem( - _ => InvalidateGeometry(), - _ => InvalidateGeometry(), - () => InvalidateGeometry()); + s => + { + s.SegmentsInvalidated += InvalidateGeometryFromSegments; + InvalidateGeometry(); + }, + s => + { + s.SegmentsInvalidated -= InvalidateGeometryFromSegments; + InvalidateGeometry(); + }, + InvalidateGeometry); + _figuresPropertiesObserver = figures?.TrackItemPropertyChanged(_ => InvalidateGeometry()); + } + public void InvalidateGeometryFromSegments(object _, EventArgs __) + { + InvalidateGeometry(); + } public override string ToString() => $"{(FillRule != FillRule.EvenOdd ? "F1 " : "")}{(string.Join(" ", Figures))}"; From d2339a041df94ca65878140435a351a3932f7ee4 Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Sat, 14 Nov 2020 11:02:30 +0800 Subject: [PATCH 008/100] try making a unit test --- .../Avalonia.RenderTests/Shapes/PathTests.cs | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/tests/Avalonia.RenderTests/Shapes/PathTests.cs b/tests/Avalonia.RenderTests/Shapes/PathTests.cs index bac16cca88..24e420d972 100644 --- a/tests/Avalonia.RenderTests/Shapes/PathTests.cs +++ b/tests/Avalonia.RenderTests/Shapes/PathTests.cs @@ -353,7 +353,47 @@ namespace Avalonia.Direct2D1.RenderTests.Shapes await RenderToFile(target); CompareImages(); } + + [Fact] + public async Task PathSegment_Triggers_Invalidation_On_Property_Change() + { + var targetSegment = new ArcSegment() + { + Size = new Size(10,10), + Point = new Point(5,5) + }; + + var targetPath = new Path + { + VerticalAlignment = VerticalAlignment.Center, + HorizontalAlignment = HorizontalAlignment.Center, + Fill = Brushes.Red, + Data = new PathGeometry + { + Figures = new PathFigures + { + new PathFigure { IsClosed = false, Segments = new PathSegments { targetSegment } } + } + } + }; + + var root = new Border + { + Width = 100, + Height = 100, + Background = Brushes.White, + Child =targetPath + }; + + Assert.Equal(10, targetPath.Bounds.Height); + Assert.Equal(10, targetPath.Bounds.Width); + + targetSegment.Size = new Size(20, 20); + Assert.Equal(20, targetPath.Bounds.Height); + Assert.Equal(20, targetPath.Bounds.Width); + } + [Fact] public async Task Path_With_Rotated_Geometry() { From 2fd70994adc8535c8380459a0505aaf43faac77c Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Sat, 14 Nov 2020 19:48:45 +0100 Subject: [PATCH 009/100] Remove enumerator allocations and LINQ from children change notifications. --- src/Avalonia.Styling/StyledElement.cs | 48 ++++++++++++++++----------- src/Avalonia.Visuals/Visual.cs | 37 ++++++++++----------- 2 files changed, 45 insertions(+), 40 deletions(-) diff --git a/src/Avalonia.Styling/StyledElement.cs b/src/Avalonia.Styling/StyledElement.cs index 65885ddebe..cc8d91462d 100644 --- a/src/Avalonia.Styling/StyledElement.cs +++ b/src/Avalonia.Styling/StyledElement.cs @@ -1,8 +1,8 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; -using System.Linq; using Avalonia.Animation; using Avalonia.Collections; using Avalonia.Controls; @@ -479,16 +479,16 @@ namespace Avalonia switch (e.Action) { case NotifyCollectionChangedAction.Add: - SetLogicalParent(e.NewItems.Cast()); + SetLogicalParent(e.NewItems); break; case NotifyCollectionChangedAction.Remove: - ClearLogicalParent(e.OldItems.Cast()); + ClearLogicalParent(e.OldItems); break; case NotifyCollectionChangedAction.Replace: - ClearLogicalParent(e.OldItems.Cast()); - SetLogicalParent(e.NewItems.Cast()); + ClearLogicalParent(e.OldItems); + SetLogicalParent(e.NewItems); break; case NotifyCollectionChangedAction.Reset: @@ -702,13 +702,32 @@ namespace Avalonia OnDataContextChanged(EventArgs.Empty); } - private void SetLogicalParent(IEnumerable children) + private void SetLogicalParent(IList children) { - foreach (var i in children) + var count = children.Count; + + for (var i = 0; i < count; i++) + { + var logical = (ILogical) children[i]; + + if (logical.LogicalParent is null) + { + ((ISetLogicalParent)logical).SetParent(this); + } + } + } + + private void ClearLogicalParent(IList children) + { + var count = children.Count; + + for (var i = 0; i < count; i++) { - if (i.LogicalParent == null) + var logical = (ILogical) children[i]; + + if (logical.LogicalParent == this) { - ((ISetLogicalParent)i).SetParent(this); + ((ISetLogicalParent)logical).SetParent(null); } } } @@ -784,17 +803,6 @@ namespace Avalonia } } - private void ClearLogicalParent(IEnumerable children) - { - foreach (var i in children) - { - if (i.LogicalParent == this) - { - ((ISetLogicalParent)i).SetParent(null); - } - } - } - private void NotifyResourcesChanged( ResourcesChangedEventArgs? e = null, bool propagate = true) diff --git a/src/Avalonia.Visuals/Visual.cs b/src/Avalonia.Visuals/Visual.cs index 283d9deb52..30073c9966 100644 --- a/src/Avalonia.Visuals/Visual.cs +++ b/src/Avalonia.Visuals/Visual.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Specialized; using Avalonia.Collections; using Avalonia.Data; @@ -619,34 +620,30 @@ namespace Avalonia switch (e.Action) { case NotifyCollectionChangedAction.Add: - foreach (Visual v in e.NewItems) - { - v.SetVisualParent(this); - } - + SetVisualParent(e.NewItems, this); break; case NotifyCollectionChangedAction.Remove: - foreach (Visual v in e.OldItems) - { - v.SetVisualParent(null); - } - + SetVisualParent(e.OldItems, null); break; case NotifyCollectionChangedAction.Replace: - foreach (Visual v in e.OldItems) - { - v.SetVisualParent(null); - } - - foreach (Visual v in e.NewItems) - { - v.SetVisualParent(this); - } - + SetVisualParent(e.OldItems, null); + SetVisualParent(e.NewItems, this); break; } } + + private static void SetVisualParent(IList children, Visual parent) + { + var count = children.Count; + + for (var i = 0; i < count; i++) + { + var visual = (Visual) children[i]; + + visual.SetVisualParent(parent); + } + } } } From a778800d2e706524895426dae254f07d046dd802 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Sun, 15 Nov 2020 14:19:07 +0100 Subject: [PATCH 010/100] Remove boxing from TemplatedControl as well. --- src/Avalonia.Controls/Primitives/TemplatedControl.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Controls/Primitives/TemplatedControl.cs b/src/Avalonia.Controls/Primitives/TemplatedControl.cs index d18cf7da71..9c73ff2411 100644 --- a/src/Avalonia.Controls/Primitives/TemplatedControl.cs +++ b/src/Avalonia.Controls/Primitives/TemplatedControl.cs @@ -354,11 +354,14 @@ namespace Avalonia.Controls.Primitives { control.SetValue(TemplatedParentProperty, this); - foreach (var child in control.LogicalChildren) + var children = control.LogicalChildren; + var count = children.Count; + + for (var i = 0; i < count; i++) { - if (child is IControl c) + if (children[i] is IControl child) { - ApplyTemplatedParent(c); + ApplyTemplatedParent(child); } } } From cde8f4cf1aa5ee1ad969a99b8363456c09aa53e7 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Sun, 15 Nov 2020 14:27:10 +0100 Subject: [PATCH 011/100] Fix control benchmarks. --- tests/Avalonia.Benchmarks/NullRenderingPlatform.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs b/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs index f632d85c26..1570205456 100644 --- a/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs +++ b/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs @@ -28,7 +28,7 @@ namespace Avalonia.Benchmarks public IGeometryImpl CreateRectangleGeometry(Rect rect) { - throw new NotImplementedException(); + return new MockStreamGeometryImpl(); } public IStreamGeometryImpl CreateStreamGeometry() From 7e4552ffedae697aa94b4766656915d1cfd7be7f Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Sun, 15 Nov 2020 18:32:15 +0300 Subject: [PATCH 012/100] Reduce the element of surprise in XAML compiler --- src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs | 4 +++- .../Avalonia.Markup.Xaml.Loader.csproj | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs index 0b9b50e771..6ef8a98fae 100644 --- a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs +++ b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs @@ -46,7 +46,9 @@ namespace Avalonia.Build.Tasks string output, bool verifyIl, MessageImportance logImportance, string strongNameKey, bool patchCom, bool skipXamlCompilation) { - var typeSystem = new CecilTypeSystem(references.Concat(new[] { input }), input); + var typeSystem = new CecilTypeSystem(references + .Where(r => !r.ToLowerInvariant().EndsWith("avalonia.build.tasks.dll")) + .Concat(new[] { input }), input); var asm = typeSystem.TargetAssemblyDefinition; diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/Avalonia.Markup.Xaml.Loader.csproj b/src/Markup/Avalonia.Markup.Xaml.Loader/Avalonia.Markup.Xaml.Loader.csproj index 514556d0b9..db9c414840 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/Avalonia.Markup.Xaml.Loader.csproj +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/Avalonia.Markup.Xaml.Loader.csproj @@ -4,6 +4,7 @@ netstandard2.0 true Avalonia.Markup.Xaml.Loader + $(DefineConstants);XAMLX_INTERNAL From b229acf8dca90955d44eb255f5d11c4991b42427 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Sun, 15 Nov 2020 20:19:32 +0300 Subject: [PATCH 013/100] Don't use the concrete type in tests --- .../CompiledBindingExtensionTests.cs | 29 +++++++++++++++---- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs index 50dfa2c7b0..1cf9e0877d 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs @@ -374,7 +374,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions "; - Assert.Throws(() => AvaloniaRuntimeXamlLoader.Load(xaml)); + ThrowsXamlTransformException(() => AvaloniaRuntimeXamlLoader.Load(xaml)); } } @@ -392,7 +392,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions "; - Assert.Throws(() => AvaloniaRuntimeXamlLoader.Load(xaml)); + ThrowsXamlTransformException(() => AvaloniaRuntimeXamlLoader.Load(xaml)); } } @@ -449,7 +449,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions "; - Assert.Throws(() => AvaloniaRuntimeXamlLoader.Load(xaml)); + ThrowsXamlTransformException(() => AvaloniaRuntimeXamlLoader.Load(xaml)); } } @@ -599,7 +599,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions x:CompileBindings='true'> "; - Assert.Throws(() => AvaloniaRuntimeXamlLoader.Load(xaml)); + ThrowsXamlParseException(() => AvaloniaRuntimeXamlLoader.Load(xaml)); } } @@ -657,7 +657,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions x:DataType='local:TestDataContext' x:CompileBindings='notabool'> "; - Assert.Throws(() => AvaloniaRuntimeXamlLoader.Load(xaml)); + ThrowsXamlParseException(() => AvaloniaRuntimeXamlLoader.Load(xaml)); } } @@ -818,8 +818,25 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions Assert.Equal(null, contentControl.Content); } } - } + void Throws(string type, Action cb) + { + try + { + cb(); + } + catch (Exception e) when (e.GetType().Name == type) + { + return; + } + + throw new Exception("Expected " + type); + } + + void ThrowsXamlParseException(Action cb) => Throws("XamlParseException", cb); + void ThrowsXamlTransformException(Action cb) => Throws("XamlTransformException", cb); + } + public interface INonIntegerIndexer { string this[string key] { get; set; } From da1f3968ef5e1180527c9f8fc71ea0545074a1a5 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Sun, 15 Nov 2020 18:48:40 +0100 Subject: [PATCH 014/100] Parse most of the vector-like types during XAML compilation. --- src/Avalonia.Base/Utilities/MathUtilities.cs | 5 +- .../Utilities/StringTokenizer.cs | 5 +- .../Avalonia.Build.Tasks.csproj | 24 ++++++++ src/Avalonia.Visuals/CornerRadius.cs | 9 ++- src/Avalonia.Visuals/Matrix.cs | 5 +- src/Avalonia.Visuals/Point.cs | 9 ++- src/Avalonia.Visuals/Size.cs | 9 ++- src/Avalonia.Visuals/Thickness.cs | 9 ++- src/Avalonia.Visuals/Vector.cs | 9 ++- ...AvaloniaXamlIlVectorLikeConstantAstNode.cs | 35 +++++++++++ .../AvaloniaXamlIlLanguage.cs | 60 +++++++++++++++++++ .../AvaloniaXamlIlWellKnownTypes.cs | 31 +++++++++- .../Avalonia.Markup.Xaml.Loader/xamlil.github | 2 +- 13 files changed, 201 insertions(+), 11 deletions(-) create mode 100644 src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AstNodes/AvaloniaXamlIlVectorLikeConstantAstNode.cs diff --git a/src/Avalonia.Base/Utilities/MathUtilities.cs b/src/Avalonia.Base/Utilities/MathUtilities.cs index 2a92e75f58..446b366dc8 100644 --- a/src/Avalonia.Base/Utilities/MathUtilities.cs +++ b/src/Avalonia.Base/Utilities/MathUtilities.cs @@ -5,7 +5,10 @@ namespace Avalonia.Utilities /// /// Provides math utilities not provided in System.Math. /// - public static class MathUtilities +#if !BUILDTASK + public +#endif + static class MathUtilities { // smallest such that 1.0+DoubleEpsilon != 1.0 internal static readonly double DoubleEpsilon = 2.2204460492503131e-016; diff --git a/src/Avalonia.Base/Utilities/StringTokenizer.cs b/src/Avalonia.Base/Utilities/StringTokenizer.cs index a159bfb72e..24e99febb9 100644 --- a/src/Avalonia.Base/Utilities/StringTokenizer.cs +++ b/src/Avalonia.Base/Utilities/StringTokenizer.cs @@ -4,7 +4,10 @@ using static System.Char; namespace Avalonia.Utilities { - public struct StringTokenizer : IDisposable +#if !BUILDTASK + public +#endif + struct StringTokenizer : IDisposable { private const char DefaultSeparatorChar = ','; diff --git a/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj b/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj index 94ad4adb7d..bfac1ac1e1 100644 --- a/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj +++ b/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj @@ -45,6 +45,12 @@ Markup/%(RecursiveDir)%(FileName)%(Extension) + + Markup/%(RecursiveDir)%(FileName)%(Extension) + + + Markup/%(RecursiveDir)%(FileName)%(Extension) + Markup/%(RecursiveDir)%(FileName)%(Extension) @@ -57,6 +63,24 @@ Markup/%(RecursiveDir)%(FileName)%(Extension) + + Markup/%(RecursiveDir)%(FileName)%(Extension) + + + Markup/%(RecursiveDir)%(FileName)%(Extension) + + + Markup/%(RecursiveDir)%(FileName)%(Extension) + + + Markup/%(RecursiveDir)%(FileName)%(Extension) + + + Markup/%(RecursiveDir)%(FileName)%(Extension) + + + Markup/%(RecursiveDir)%(FileName)%(Extension) + diff --git a/src/Avalonia.Visuals/CornerRadius.cs b/src/Avalonia.Visuals/CornerRadius.cs index c02aacfb4d..037bb16e31 100644 --- a/src/Avalonia.Visuals/CornerRadius.cs +++ b/src/Avalonia.Visuals/CornerRadius.cs @@ -1,6 +1,8 @@ using System; using System.Globalization; +#if !BUILDTASK using Avalonia.Animation.Animators; +#endif using Avalonia.Utilities; namespace Avalonia @@ -8,11 +10,16 @@ namespace Avalonia /// /// Represents the radii of a rectangle's corners. /// - public readonly struct CornerRadius : IEquatable +#if !BUILDTASK + public +#endif + readonly struct CornerRadius : IEquatable { static CornerRadius() { +#if !BUILDTASK Animation.Animation.RegisterAnimator(prop => typeof(CornerRadius).IsAssignableFrom(prop.PropertyType)); +#endif } public CornerRadius(double uniformRadius) diff --git a/src/Avalonia.Visuals/Matrix.cs b/src/Avalonia.Visuals/Matrix.cs index 206b842220..2ccfd43f03 100644 --- a/src/Avalonia.Visuals/Matrix.cs +++ b/src/Avalonia.Visuals/Matrix.cs @@ -7,7 +7,10 @@ namespace Avalonia /// /// A 2x3 matrix. /// - public readonly struct Matrix : IEquatable +#if !BUILDTASK + public +#endif + readonly struct Matrix : IEquatable { private readonly double _m11; private readonly double _m12; diff --git a/src/Avalonia.Visuals/Point.cs b/src/Avalonia.Visuals/Point.cs index 7324f5fbd0..6febe9c802 100644 --- a/src/Avalonia.Visuals/Point.cs +++ b/src/Avalonia.Visuals/Point.cs @@ -1,6 +1,8 @@ using System; using System.Globalization; +#if !BUILDTASK using Avalonia.Animation.Animators; +#endif using Avalonia.Utilities; namespace Avalonia @@ -8,11 +10,16 @@ namespace Avalonia /// /// Defines a point. /// - public readonly struct Point : IEquatable +#if !BUILDTASK + public +#endif + readonly struct Point : IEquatable { static Point() { +#if !BUILDTASK Animation.Animation.RegisterAnimator(prop => typeof(Point).IsAssignableFrom(prop.PropertyType)); +#endif } /// diff --git a/src/Avalonia.Visuals/Size.cs b/src/Avalonia.Visuals/Size.cs index d87d2c5fc2..8a805dc6c5 100644 --- a/src/Avalonia.Visuals/Size.cs +++ b/src/Avalonia.Visuals/Size.cs @@ -1,6 +1,8 @@ using System; using System.Globalization; +#if !BUILDTASK using Avalonia.Animation.Animators; +#endif using Avalonia.Utilities; namespace Avalonia @@ -8,11 +10,16 @@ namespace Avalonia /// /// Defines a size. /// - public readonly struct Size : IEquatable +#if !BUILDTASK + public +#endif + readonly struct Size : IEquatable { static Size() { +#if !BUILDTASK Animation.Animation.RegisterAnimator(prop => typeof(Size).IsAssignableFrom(prop.PropertyType)); +#endif } /// diff --git a/src/Avalonia.Visuals/Thickness.cs b/src/Avalonia.Visuals/Thickness.cs index 6d69c4d9a9..06ebc9bfe7 100644 --- a/src/Avalonia.Visuals/Thickness.cs +++ b/src/Avalonia.Visuals/Thickness.cs @@ -1,6 +1,8 @@ using System; using System.Globalization; +#if !BUILDTASK using Avalonia.Animation.Animators; +#endif using Avalonia.Utilities; namespace Avalonia @@ -8,11 +10,16 @@ namespace Avalonia /// /// Describes the thickness of a frame around a rectangle. /// - public readonly struct Thickness : IEquatable +#if !BUILDTASK + public +#endif + readonly struct Thickness : IEquatable { static Thickness() { +#if !BUILDTASK Animation.Animation.RegisterAnimator(prop => typeof(Thickness).IsAssignableFrom(prop.PropertyType)); +#endif } /// diff --git a/src/Avalonia.Visuals/Vector.cs b/src/Avalonia.Visuals/Vector.cs index 2fcf804f14..1b9f5c67d5 100644 --- a/src/Avalonia.Visuals/Vector.cs +++ b/src/Avalonia.Visuals/Vector.cs @@ -1,6 +1,8 @@ using System; using System.Globalization; +#if !BUILDTASK using Avalonia.Animation.Animators; +#endif using Avalonia.Utilities; #nullable enable @@ -10,11 +12,16 @@ namespace Avalonia /// /// Defines a vector. /// - public readonly struct Vector : IEquatable +#if !BUILDTASK + public +#endif + readonly struct Vector : IEquatable { static Vector() { +#if !BUILDTASK Animation.Animation.RegisterAnimator(prop => typeof(Vector).IsAssignableFrom(prop.PropertyType)); +#endif } /// diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AstNodes/AvaloniaXamlIlVectorLikeConstantAstNode.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AstNodes/AvaloniaXamlIlVectorLikeConstantAstNode.cs new file mode 100644 index 0000000000..db23856900 --- /dev/null +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AstNodes/AvaloniaXamlIlVectorLikeConstantAstNode.cs @@ -0,0 +1,35 @@ +using XamlX.Ast; +using XamlX.Emit; +using XamlX.IL; +using XamlX.TypeSystem; + +namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.AstNodes +{ + class AvaloniaXamlIlVectorLikeConstantAstNode : XamlAstNode, IXamlAstValueNode, IXamlAstILEmitableNode + { + private readonly IXamlConstructor _constructor; + private readonly double[] _values; + + public AvaloniaXamlIlVectorLikeConstantAstNode(IXamlLineInfo lineInfo, IXamlType type, IXamlConstructor constructor, double[] values) : base(lineInfo) + { + _constructor = constructor; + _values = values; + + Type = new XamlAstClrTypeReference(lineInfo, type, false); + } + + public IXamlAstTypeReference Type { get; } + + public XamlILNodeEmitResult Emit(XamlEmitContext context, IXamlILEmitter codeGen) + { + foreach (var value in _values) + { + codeGen.Ldc_R8(value); + } + + codeGen.Newobj(_constructor); + + return XamlILNodeEmitResult.Type(0, Type.GetClrType()); + } + } +} diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguage.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguage.cs index 15413689f8..5dc6682129 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguage.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguage.cs @@ -205,6 +205,66 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions result = new AvaloniaXamlIlFontFamilyAstNode(types, text, node); return true; } + + if (type.Equals(types.Thickness)) + { + var thickness = Thickness.Parse(text); + + result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types.Thickness, types.ThicknessFullConstructorName, + new[] { thickness.Left, thickness.Top, thickness.Right, thickness.Bottom }); + + return true; + } + + if (type.Equals(types.Point)) + { + var point = Point.Parse(text); + + result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types.Point, types.PointFullConstructorName, + new[] { point.X, point.Y }); + + return true; + } + + if (type.Equals(types.Vector)) + { + var vector = Vector.Parse(text); + + result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types.Vector, types.VectorFullConstructorName, + new[] { vector.X, vector.Y }); + + return true; + } + + if (type.Equals(types.Size)) + { + var size = Size.Parse(text); + + result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types.Size, types.SizeFullConstructorName, + new[] { size.Width, size.Height }); + + return true; + } + + if (type.Equals(types.Matrix)) + { + var matrix = Matrix.Parse(text); + + result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types.Matrix, types.MatrixFullConstructorName, + new[] { matrix.M11, matrix.M12, matrix.M21, matrix.M22, matrix.M31, matrix.M32 }); + + return true; + } + + if (type.Equals(types.CornerRadius)) + { + var cornerRadius = CornerRadius.Parse(text); + + result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types.CornerRadius, types.CornerRadiusFullConstructorName, + new[] { cornerRadius.TopLeft, cornerRadius.TopRight, cornerRadius.BottomRight, cornerRadius.BottomLeft }); + + return true; + } if (type.FullName == "Avalonia.AvaloniaProperty") { diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs index 58f4ddfe31..8ac2daa707 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using XamlX.Emit; using XamlX.IL; using XamlX.Transform; @@ -52,7 +53,18 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers public IXamlType Uri { get; } public IXamlType FontFamily { get; } public IXamlConstructor FontFamilyConstructorUriName { get; } - + public IXamlType Thickness { get; } + public IXamlConstructor ThicknessFullConstructorName { get; } + public IXamlType Point { get; } + public IXamlConstructor PointFullConstructorName { get; } + public IXamlType Vector { get; } + public IXamlConstructor VectorFullConstructorName { get; } + public IXamlType Size { get; } + public IXamlConstructor SizeFullConstructorName { get; } + public IXamlType Matrix { get; } + public IXamlConstructor MatrixFullConstructorName { get; } + public IXamlType CornerRadius { get; } + public IXamlConstructor CornerRadiusFullConstructorName { get; } public AvaloniaXamlIlWellKnownTypes(TransformerConfiguration cfg) { @@ -113,7 +125,22 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers Long = cfg.TypeSystem.GetType("System.Int64"); Uri = cfg.TypeSystem.GetType("System.Uri"); FontFamily = cfg.TypeSystem.GetType("Avalonia.Media.FontFamily"); - FontFamilyConstructorUriName = FontFamily.FindConstructor(new List { Uri, XamlIlTypes.String }); + FontFamilyConstructorUriName = FontFamily.GetConstructor(new List { Uri, XamlIlTypes.String }); + + (IXamlType, IXamlConstructor) GetNumericTypeInfo(string name, IXamlType componentType, int componentCount) + { + var type = cfg.TypeSystem.GetType(name); + var ctor = type.GetConstructor(Enumerable.Range(0, componentCount).Select(_ => componentType).ToList()); + + return (type, ctor); + } + + (Thickness, ThicknessFullConstructorName) = GetNumericTypeInfo("Avalonia.Thickness", XamlIlTypes.Double, 4); + (Point, PointFullConstructorName) = GetNumericTypeInfo("Avalonia.Point", XamlIlTypes.Double, 2); + (Vector, VectorFullConstructorName) = GetNumericTypeInfo("Avalonia.Vector", XamlIlTypes.Double, 2); + (Size, SizeFullConstructorName) = GetNumericTypeInfo("Avalonia.Size", XamlIlTypes.Double, 2); + (Matrix, MatrixFullConstructorName) = GetNumericTypeInfo("Avalonia.Matrix", XamlIlTypes.Double, 6); + (CornerRadius, CornerRadiusFullConstructorName) = GetNumericTypeInfo("Avalonia.CornerRadius", XamlIlTypes.Double, 4); } } diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github b/src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github index 5420df861c..8d2b6e6ee5 160000 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github @@ -1 +1 @@ -Subproject commit 5420df861ce6f2be5ead9efa078fe7242ce88f18 +Subproject commit 8d2b6e6ee5099d6a0271e743d2808f44e3241945 From 346687423c7e7be1dc56aad5d99971842789fe59 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Sun, 15 Nov 2020 19:12:45 +0100 Subject: [PATCH 015/100] Bump compiler version. --- src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github b/src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github index 8d2b6e6ee5..ea80a607c5 160000 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github @@ -1 +1 @@ -Subproject commit 8d2b6e6ee5099d6a0271e743d2808f44e3241945 +Subproject commit ea80a607c5e9d8f000160dbbb48c27ed4cfafbc9 From 78dde1f2dacfa1eb12d0811bbc818f4555da3863 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Sun, 15 Nov 2020 19:36:46 +0100 Subject: [PATCH 016/100] Review fixes. --- ...AvaloniaXamlIlVectorLikeConstantAstNode.cs | 23 ++++++++++++++++-- .../AvaloniaXamlIlLanguage.cs | 12 +++++----- .../AvaloniaXamlIlWellKnownTypes.cs | 24 +++++++++---------- 3 files changed, 39 insertions(+), 20 deletions(-) diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AstNodes/AvaloniaXamlIlVectorLikeConstantAstNode.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AstNodes/AvaloniaXamlIlVectorLikeConstantAstNode.cs index db23856900..35cc9b3cf2 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AstNodes/AvaloniaXamlIlVectorLikeConstantAstNode.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AstNodes/AvaloniaXamlIlVectorLikeConstantAstNode.cs @@ -1,4 +1,6 @@ -using XamlX.Ast; +using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers; +using XamlX; +using XamlX.Ast; using XamlX.Emit; using XamlX.IL; using XamlX.TypeSystem; @@ -10,8 +12,25 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.AstNodes private readonly IXamlConstructor _constructor; private readonly double[] _values; - public AvaloniaXamlIlVectorLikeConstantAstNode(IXamlLineInfo lineInfo, IXamlType type, IXamlConstructor constructor, double[] values) : base(lineInfo) + public AvaloniaXamlIlVectorLikeConstantAstNode(IXamlLineInfo lineInfo, AvaloniaXamlIlWellKnownTypes types, IXamlType type, IXamlConstructor constructor, double[] values) : base(lineInfo) { + var parameters = constructor.Parameters; + + if (parameters.Count != values.Length) + { + throw new XamlTypeSystemException($"Constructor that takes {values.Length} parameters is expected, got {parameters.Count} instead."); + } + + var elementType = types.XamlIlTypes.Double; + + foreach (var parameter in parameters) + { + if (parameter != elementType) + { + throw new XamlTypeSystemException($"Expected parameter of type {elementType}, got {parameter} instead."); + } + } + _constructor = constructor; _values = values; diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguage.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguage.cs index 5dc6682129..c3d9534828 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguage.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguage.cs @@ -210,7 +210,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions { var thickness = Thickness.Parse(text); - result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types.Thickness, types.ThicknessFullConstructorName, + result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Thickness, types.ThicknessFullConstructor, new[] { thickness.Left, thickness.Top, thickness.Right, thickness.Bottom }); return true; @@ -220,7 +220,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions { var point = Point.Parse(text); - result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types.Point, types.PointFullConstructorName, + result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Point, types.PointFullConstructor, new[] { point.X, point.Y }); return true; @@ -230,7 +230,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions { var vector = Vector.Parse(text); - result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types.Vector, types.VectorFullConstructorName, + result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Vector, types.VectorFullConstructor, new[] { vector.X, vector.Y }); return true; @@ -240,7 +240,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions { var size = Size.Parse(text); - result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types.Size, types.SizeFullConstructorName, + result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Size, types.SizeFullConstructor, new[] { size.Width, size.Height }); return true; @@ -250,7 +250,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions { var matrix = Matrix.Parse(text); - result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types.Matrix, types.MatrixFullConstructorName, + result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Matrix, types.MatrixFullConstructor, new[] { matrix.M11, matrix.M12, matrix.M21, matrix.M22, matrix.M31, matrix.M32 }); return true; @@ -260,7 +260,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions { var cornerRadius = CornerRadius.Parse(text); - result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types.CornerRadius, types.CornerRadiusFullConstructorName, + result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.CornerRadius, types.CornerRadiusFullConstructor, new[] { cornerRadius.TopLeft, cornerRadius.TopRight, cornerRadius.BottomRight, cornerRadius.BottomLeft }); return true; diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs index 8ac2daa707..05b13b61d3 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs @@ -54,17 +54,17 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers public IXamlType FontFamily { get; } public IXamlConstructor FontFamilyConstructorUriName { get; } public IXamlType Thickness { get; } - public IXamlConstructor ThicknessFullConstructorName { get; } + public IXamlConstructor ThicknessFullConstructor { get; } public IXamlType Point { get; } - public IXamlConstructor PointFullConstructorName { get; } + public IXamlConstructor PointFullConstructor { get; } public IXamlType Vector { get; } - public IXamlConstructor VectorFullConstructorName { get; } + public IXamlConstructor VectorFullConstructor { get; } public IXamlType Size { get; } - public IXamlConstructor SizeFullConstructorName { get; } + public IXamlConstructor SizeFullConstructor { get; } public IXamlType Matrix { get; } - public IXamlConstructor MatrixFullConstructorName { get; } + public IXamlConstructor MatrixFullConstructor { get; } public IXamlType CornerRadius { get; } - public IXamlConstructor CornerRadiusFullConstructorName { get; } + public IXamlConstructor CornerRadiusFullConstructor { get; } public AvaloniaXamlIlWellKnownTypes(TransformerConfiguration cfg) { @@ -135,12 +135,12 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers return (type, ctor); } - (Thickness, ThicknessFullConstructorName) = GetNumericTypeInfo("Avalonia.Thickness", XamlIlTypes.Double, 4); - (Point, PointFullConstructorName) = GetNumericTypeInfo("Avalonia.Point", XamlIlTypes.Double, 2); - (Vector, VectorFullConstructorName) = GetNumericTypeInfo("Avalonia.Vector", XamlIlTypes.Double, 2); - (Size, SizeFullConstructorName) = GetNumericTypeInfo("Avalonia.Size", XamlIlTypes.Double, 2); - (Matrix, MatrixFullConstructorName) = GetNumericTypeInfo("Avalonia.Matrix", XamlIlTypes.Double, 6); - (CornerRadius, CornerRadiusFullConstructorName) = GetNumericTypeInfo("Avalonia.CornerRadius", XamlIlTypes.Double, 4); + (Thickness, ThicknessFullConstructor) = GetNumericTypeInfo("Avalonia.Thickness", XamlIlTypes.Double, 4); + (Point, PointFullConstructor) = GetNumericTypeInfo("Avalonia.Point", XamlIlTypes.Double, 2); + (Vector, VectorFullConstructor) = GetNumericTypeInfo("Avalonia.Vector", XamlIlTypes.Double, 2); + (Size, SizeFullConstructor) = GetNumericTypeInfo("Avalonia.Size", XamlIlTypes.Double, 2); + (Matrix, MatrixFullConstructor) = GetNumericTypeInfo("Avalonia.Matrix", XamlIlTypes.Double, 6); + (CornerRadius, CornerRadiusFullConstructor) = GetNumericTypeInfo("Avalonia.CornerRadius", XamlIlTypes.Double, 4); } } From 32be146090772f6bcc4978d51074ba730769fd32 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Sun, 15 Nov 2020 12:42:36 +0000 Subject: [PATCH 017/100] lock api diff to preview6. --- build/ApiDiff.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/ApiDiff.props b/build/ApiDiff.props index 3d322f56d5..fb65ef6e87 100644 --- a/build/ApiDiff.props +++ b/build/ApiDiff.props @@ -1,6 +1,6 @@  - 0.10.0-preview3 + 0.10.0-preview6 $(PackageId) Avalonia From 8c7540ddf15137d96f77cf386dde5eebd49c44df Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Sun, 15 Nov 2020 12:42:50 +0000 Subject: [PATCH 018/100] update System.Reactive to .net5 --- build/Rx.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/Rx.props b/build/Rx.props index 8a15ccd6a9..fde1f80ea1 100644 --- a/build/Rx.props +++ b/build/Rx.props @@ -1,5 +1,5 @@  - + From ae982fcfa15041409645c52156041d51151fdaf3 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Sun, 15 Nov 2020 18:32:03 +0000 Subject: [PATCH 019/100] try updating nuke. --- nukebuild/_build.csproj | 2 +- samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/nukebuild/_build.csproj b/nukebuild/_build.csproj index 745c727be2..b0380cc92b 100644 --- a/nukebuild/_build.csproj +++ b/nukebuild/_build.csproj @@ -10,7 +10,7 @@ - + diff --git a/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj b/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj index d5aedf7783..11e340e647 100644 --- a/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj +++ b/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp3.1 + net5 true From 3d98acbed9624109baa60e66fb06c4c55d8b0a9c Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Sun, 15 Nov 2020 18:54:13 +0000 Subject: [PATCH 020/100] fix nuke --- nukebuild/Build.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/nukebuild/Build.cs b/nukebuild/Build.cs index 97647a1c59..8e331edab4 100644 --- a/nukebuild/Build.cs +++ b/nukebuild/Build.cs @@ -107,7 +107,7 @@ partial class Build : NukeBuild .AddProperty("JavaSdkDirectory", GetVariable("JAVA_HOME_8_X64"))) .AddProperty("PackageVersion", Parameters.Version) .AddProperty("iOSRoslynPathHackRequired", true) - .SetToolPath(MsBuildExe.Value) + .SetProcessToolPath(MsBuildExe.Value) .SetConfiguration(Parameters.Configuration) .SetVerbosity(MSBuildVerbosity.Minimal) .Apply(configurator)); @@ -132,10 +132,10 @@ partial class Build : NukeBuild var webappDir = RootDirectory / "src" / "Avalonia.DesignerSupport" / "Remote" / "HtmlTransport" / "webapp"; NpmTasks.NpmInstall(c => c - .SetWorkingDirectory(webappDir) - .SetArgumentConfigurator(a => a.Add("--silent"))); + .SetProcessWorkingDirectory(webappDir) + .SetProcessArgumentConfigurator(a => a.Add("--silent"))); NpmTasks.NpmRun(c => c - .SetWorkingDirectory(webappDir) + .SetProcessWorkingDirectory(webappDir) .SetCommand("dist")); }); @@ -157,7 +157,7 @@ partial class Build : NukeBuild { if (Parameters.IsRunningOnWindows) MsBuildCommon(Parameters.MSBuildSolution, c => c - .SetArgumentConfigurator(a => a.Add("/r")) + .SetProcessArgumentConfigurator(a => a.Add("/r")) .AddTargets("Build") ); @@ -194,7 +194,7 @@ partial class Build : NukeBuild var eventsProject = Path.Combine(eventsDirectory, "Avalonia.ReactiveUI.Events.csproj"); if (Parameters.IsRunningOnWindows) MsBuildCommon(eventsProject, c => c - .SetArgumentConfigurator(a => a.Add("/r")) + .SetProcessArgumentConfigurator(a => a.Add("/r")) .AddTargets("Build") ); else @@ -242,10 +242,10 @@ partial class Build : NukeBuild var webappTestDir = RootDirectory / "tests" / "Avalonia.DesignerSupport.Tests" / "Remote" / "HtmlTransport" / "webapp"; NpmTasks.NpmInstall(c => c - .SetWorkingDirectory(webappTestDir) - .SetArgumentConfigurator(a => a.Add("--silent"))); + .SetProcessWorkingDirectory(webappTestDir) + .SetProcessArgumentConfigurator(a => a.Add("--silent"))); NpmTasks.NpmRun(c => c - .SetWorkingDirectory(webappTestDir) + .SetProcessWorkingDirectory(webappTestDir) .SetCommand("test")); }); From d3e304048c955149285864da6a0032cd24a57b84 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Sun, 15 Nov 2020 19:06:13 +0000 Subject: [PATCH 021/100] revert controlcatalog target. --- samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj b/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj index 11e340e647..d5aedf7783 100644 --- a/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj +++ b/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj @@ -2,7 +2,7 @@ Exe - net5 + netcoreapp3.1 true From 3518f19b2aa5d688781a06217e7090bb2efb575c Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Mon, 16 Nov 2020 11:22:05 +0100 Subject: [PATCH 022/100] Revert "ItemsControl+ItemVirtualizerSimple don not recreate containers when Items or ItemTemplate are replaced" --- src/Avalonia.Controls/ItemsControl.cs | 6 +- .../Presenters/ItemVirtualizerSimple.cs | 4 - .../Presenters/ItemsPresenterBase.cs | 4 +- .../ItemsControlTests.cs | 92 -------------- .../ListBoxTests.cs | 112 ------------------ 5 files changed, 2 insertions(+), 216 deletions(-) diff --git a/src/Avalonia.Controls/ItemsControl.cs b/src/Avalonia.Controls/ItemsControl.cs index f955df5f21..4dc8aec6f3 100644 --- a/src/Avalonia.Controls/ItemsControl.cs +++ b/src/Avalonia.Controls/ItemsControl.cs @@ -449,11 +449,7 @@ namespace Avalonia.Controls if (_itemContainerGenerator != null) { _itemContainerGenerator.ItemTemplate = (IDataTemplate)e.NewValue; - - if (e.OldValue != null && Presenter != null) - { - Presenter.ItemsChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); - } + // TODO: Rebuild the item containers. } } diff --git a/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs b/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs index 51dbc969a8..7d50ef7d33 100644 --- a/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs +++ b/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs @@ -200,10 +200,6 @@ namespace Avalonia.Controls.Presenters break; case NotifyCollectionChangedAction.Reset: - Owner.ItemContainerGenerator.Clear(); - VirtualizingPanel.Children.Clear(); - FirstIndex = NextIndex = 0; - RecycleContainersOnRemove(); CreateAndRemoveContainers(); panel.ForceInvalidateMeasure(); diff --git a/src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs b/src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs index 6c408bbed9..52f173fc71 100644 --- a/src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs +++ b/src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs @@ -57,8 +57,6 @@ namespace Avalonia.Controls.Presenters set { - var itemsReplaced = (_items != value); - _itemsSubscription?.Dispose(); _itemsSubscription = null; @@ -69,7 +67,7 @@ namespace Avalonia.Controls.Presenters SetAndRaise(ItemsProperty, ref _items, value); - if (_createdPanel && itemsReplaced) + if (_createdPanel) { ItemsChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); } diff --git a/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs b/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs index 157eefb84a..684486cbae 100644 --- a/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs @@ -596,98 +596,6 @@ namespace Avalonia.Controls.UnitTests root.Child = target; } - [Fact] - public void Presenter_Items_Should_Be_In_Sync_When_Replacing_Items() - { - var target = new ItemsControl - { - Template = GetTemplate(), - Items = new[] - { - new Item("Item1") - } - }; - - var root = new TestRoot { Child = target }; - var otherPanel = new StackPanel(); - - target.ApplyTemplate(); - target.Presenter.ApplyTemplate(); - - int dematerializedEventCallCount = 0; - target.ItemContainerGenerator.Dematerialized += (s, e) => - { - Assert.IsType(e.Containers[0].Item); - Assert.Equal("Item1", ((Item)e.Containers[0].Item).Value); - dematerializedEventCallCount++; - }; - - int materializedEventCallCount = 0; - target.ItemContainerGenerator.Materialized += (s, e) => - { - Assert.IsType(e.Containers[0].Item); - Assert.Equal("Item2", ((Item)e.Containers[0].Item).Value); - materializedEventCallCount++; - }; - - target.Items = new[] - { - new Item("Item2") - }; - - //Ensure that events are called one time only - Assert.Equal(1, dematerializedEventCallCount); - Assert.Equal(1, materializedEventCallCount); - } - - [Fact] - public void Presenter_Items_Should_Be_In_Sync_When_Replacing_ItemTemplate() - { - var target = new ItemsControl - { - Template = GetTemplate(), - Items = new[] - { - new Item("Item1") - }, - ItemTemplate = new FuncDataTemplate((x, ns) => new TextBlock()) - }; - - var root = new TestRoot { Child = target }; - var otherPanel = new StackPanel(); - - target.ApplyTemplate(); - target.Presenter.ApplyTemplate(); - - int dematerializedEventCallCount = 0; - target.ItemContainerGenerator.Dematerialized += (s, e) => - { - Assert.IsType(e.Containers[0].Item); - Assert.Equal("Item1", ((Item)e.Containers[0].Item).Value); - var contentPresenter = ((ContentPresenter)e.Containers[0].ContainerControl); - contentPresenter.UpdateChild(); - Assert.IsType(contentPresenter.Child); - dematerializedEventCallCount++; - }; - - int materializedEventCallCount = 0; - target.ItemContainerGenerator.Materialized += (s, e) => - { - Assert.IsType(e.Containers[0].Item); - Assert.Equal("Item1", ((Item)e.Containers[0].Item).Value); - var contentPresenter = ((ContentPresenter)e.Containers[0].ContainerControl); - contentPresenter.UpdateChild(); - Assert.IsType(contentPresenter.Child); - materializedEventCallCount++; - }; - - target.ItemTemplate = - new FuncDataTemplate((x, ns) => new Canvas()); - - Assert.Equal(1, dematerializedEventCallCount); - Assert.Equal(1, materializedEventCallCount); - } - private class Item { public Item(string value) diff --git a/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs b/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs index 364cb01c65..145fce4fed 100644 --- a/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs @@ -454,118 +454,6 @@ namespace Avalonia.Controls.UnitTests } } - - [Fact] - public void ListBox_Presenter_Items_Should_Be_In_Sync_When_Replacing_Items() - { - using (UnitTestApplication.Start(TestServices.StyledWindow)) - { - var wnd = new Window() { Width = 100, Height = 100, IsVisible = true }; - - var target = new ListBox() - { - VerticalAlignment = Layout.VerticalAlignment.Top, - AutoScrollToSelectedItem = true, - Width = 50, - VirtualizationMode = ItemVirtualizationMode.Simple, - Items = new[] - { - new Item("Item1") - }, - }; - wnd.Content = target; - - var lm = wnd.LayoutManager; - - lm.ExecuteInitialLayoutPass(); - - int dematerializedEventCallCount = 0; - target.ItemContainerGenerator.Dematerialized += (s, e) => - { - Assert.IsType(e.Containers[0].Item); - Assert.Equal("Item1", ((Item)e.Containers[0].Item).Value); - dematerializedEventCallCount++; - }; - - int materializedEventCallCount = 0; - target.ItemContainerGenerator.Materialized += (s, e) => - { - Assert.IsType(e.Containers[0].Item); - Assert.Equal("Item2", ((Item)e.Containers[0].Item).Value); - materializedEventCallCount++; - }; - - target.Items = new[] - { - new Item("Item2") - }; - - //assert that materialize/dematerialize events are called exactly one time - Assert.Equal(1, dematerializedEventCallCount); - Assert.Equal(1, materializedEventCallCount); - } - } - - [Fact] - public void ListBox_Items_Should_Be_In_Sync_When_Replacing_ItemTemplate() - { - using (UnitTestApplication.Start(TestServices.StyledWindow)) - { - var wnd = new Window() { Width = 100, Height = 100, IsVisible = true }; - - var target = new ListBox() - { - VerticalAlignment = Layout.VerticalAlignment.Top, - AutoScrollToSelectedItem = true, - Width = 50, - VirtualizationMode = ItemVirtualizationMode.Simple, - Items = new[] - { - new Item("Item1") - }, - ItemTemplate = - new FuncDataTemplate((x, ns) => new Canvas()) - }; - - wnd.Content = target; - - var lm = wnd.LayoutManager; - - lm.ExecuteInitialLayoutPass(); - - int dematerializedEventCallCount = 0; - target.ItemContainerGenerator.Dematerialized += (s, e) => - { - Assert.IsType(e.Containers[0].Item); - Assert.Equal("Item1", ((Item)e.Containers[0].Item).Value); - Assert.IsType(((ListBoxItem)e.Containers[0].ContainerControl).Presenter.Child); - dematerializedEventCallCount++; - }; - - int materializedEventCallCount = 0; - ListBoxItem materializedListBoxItem = null; - target.ItemContainerGenerator.Materialized += (s, e) => - { - Assert.IsType(e.Containers[0].Item); - Assert.Equal("Item1", ((Item)e.Containers[0].Item).Value); - materializedListBoxItem = ((ListBoxItem)e.Containers[0].ContainerControl); - materializedEventCallCount++; - }; - - target.ItemTemplate = - new FuncDataTemplate((x, ns) => new TextBlock()); - - //ensure events are called only one time - Assert.Equal(1, dematerializedEventCallCount); - Assert.Equal(1, materializedEventCallCount); - - wnd.LayoutManager.ExecuteLayoutPass(); - - //ensure that new template has been applied - Assert.IsType(materializedListBoxItem.Presenter.Child); - } - } - private FuncControlTemplate ListBoxTemplate() { return new FuncControlTemplate((parent, scope) => From cc1051efa35342ba4ef095b987b3f583cfe550d8 Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Mon, 16 Nov 2020 14:03:13 +0200 Subject: [PATCH 023/100] add failing test for set of base class property --- .../Xaml/XamlIlTests.cs | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlTests.cs index 67e46d25c3..77a4932ccc 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlTests.cs @@ -295,8 +295,27 @@ namespace Avalonia.Markup.Xaml.UnitTests Assert.Equal("Test", templated.Text); } } + + [Fact] + public void Should_Work_With_Base_Property() + { + var parsed = (ListBox)AvaloniaRuntimeXamlLoader.Load(@" + + + + + + +"); + + Assert.NotNull(parsed.ItemTemplate); + } } - + public class XamlIlBugTestsEventHandlerCodeBehind : Window { public object SavedContext; From db96b4d39ee3733dcd55ead409c6df9a7314342f Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Mon, 16 Nov 2020 14:04:09 +0200 Subject: [PATCH 024/100] fix set of base class property in xaml fixes #1926 --- .../AvaloniaXamlIlTransformInstanceAttachedProperties.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlTransformInstanceAttachedProperties.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlTransformInstanceAttachedProperties.cs index d78ceeb918..f87e73a783 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlTransformInstanceAttachedProperties.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlTransformInstanceAttachedProperties.cs @@ -22,7 +22,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers var avaloniaObject = context.Configuration.TypeSystem.FindType("Avalonia.AvaloniaObject"); if (avaloniaObject.IsAssignableFrom(targetRef.Type) && avaloniaObject.IsAssignableFrom(declaringRef.Type) - && !targetRef.Type.IsAssignableFrom(declaringRef.Type)) + && !declaringRef.Type.IsAssignableFrom(targetRef.Type)) { // Instance property var clrProp = declaringRef.Type.GetAllProperties().FirstOrDefault(p => p.Name == prop.Name); From 1dfb76d993fed57ac3ac64da4bcc1af475639983 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Mon, 16 Nov 2020 19:43:24 +0300 Subject: [PATCH 025/100] Fixed COM calling convention for 32 bit windows --- src/Avalonia.MicroCom/MicroComVtblBase.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.MicroCom/MicroComVtblBase.cs b/src/Avalonia.MicroCom/MicroComVtblBase.cs index e06e2d4934..2f0607c0a8 100644 --- a/src/Avalonia.MicroCom/MicroComVtblBase.cs +++ b/src/Avalonia.MicroCom/MicroComVtblBase.cs @@ -7,10 +7,10 @@ namespace Avalonia.MicroCom public unsafe class MicroComVtblBase { private List _methods = new List(); - [UnmanagedFunctionPointerAttribute(CallingConvention.ThisCall)] + [UnmanagedFunctionPointerAttribute(CallingConvention.StdCall)] private delegate int AddRefDelegate(Ccw* ccw); - [UnmanagedFunctionPointerAttribute(CallingConvention.ThisCall)] + [UnmanagedFunctionPointerAttribute(CallingConvention.StdCall)] private delegate int QueryInterfaceDelegate(Ccw* ccw, Guid* guid, void** ppv); public static IntPtr Vtable { get; } = new MicroComVtblBase().CreateVTable(); From 303b6c15866fb61ae2e17093ddf5e85acc670ffb Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Mon, 16 Nov 2020 23:25:13 +0300 Subject: [PATCH 026/100] Remove ReactiveUI usage from sample apps --- Avalonia.sln | 27 +++++ samples/BindingDemo/App.xaml.cs | 2 - samples/BindingDemo/BindingDemo.csproj | 3 +- .../ViewModels/ExceptionErrorViewModel.cs | 4 +- .../ViewModels/IndeiErrorViewModel.cs | 4 +- .../ViewModels/MainWindowViewModel.cs | 12 +- .../ViewModels/NestedCommandViewModel.cs | 8 +- samples/BindingDemo/ViewModels/TestItem.cs | 4 +- samples/ControlCatalog.Desktop/Program.cs | 4 +- samples/ControlCatalog.NetCore/Program.cs | 2 - samples/ControlCatalog/ControlCatalog.csproj | 2 +- .../ControlCatalog/Pages/LabelsPage.axaml.cs | 1 - samples/ControlCatalog/Pages/ListBoxPage.xaml | 2 +- samples/ControlCatalog/Pages/MenuPage.xaml.cs | 1 - .../Pages/NumericUpDownPage.xaml.cs | 4 +- .../Pages/ScrollViewerPage.xaml.cs | 4 +- .../Pages/TabControlPage.xaml.cs | 4 +- .../ViewModels/ContextMenuPageViewModel.cs | 14 +-- .../ViewModels/ItemsRepeaterPageViewModel.cs | 6 +- .../ViewModels/ListBoxPageViewModel.cs | 27 +++-- .../ViewModels/MainWindowViewModel.cs | 28 ++--- .../ViewModels/MenuPageViewModel.cs | 14 +-- .../ViewModels/NotificationViewModel.cs | 10 +- .../ViewModels/SplitViewPageViewModel.cs | 4 +- .../ViewModels/TreeViewPageViewModel.cs | 16 +-- samples/MiniMvvm/MiniCommand.cs | 66 +++++++++++ samples/MiniMvvm/MiniMvvm.csproj | 6 + samples/MiniMvvm/PropertyChangedExtensions.cs | 108 ++++++++++++++++++ samples/MiniMvvm/ViewModelBase.cs | 26 +++++ samples/Previewer/Previewer.csproj | 1 - samples/RenderDemo/App.xaml.cs | 2 - samples/RenderDemo/MainWindow.xaml.cs | 2 +- samples/RenderDemo/RenderDemo.csproj | 3 +- .../ViewModels/AnimationsPageViewModel.cs | 4 +- .../ViewModels/MainWindowViewModel.cs | 17 ++- samples/Sandbox/Program.cs | 2 - samples/Sandbox/Sandbox.csproj | 1 - samples/VirtualizationDemo/Program.cs | 2 - .../ViewModels/ItemViewModel.cs | 4 +- .../ViewModels/MainWindowViewModel.cs | 24 ++-- .../VirtualizationDemo.csproj | 3 +- .../Direct3DInteropSample.csproj | 2 +- .../MainWindowViewModel.cs | 4 +- .../WindowsInteropTest.csproj | 6 +- .../Avalonia.AndroidTestApplication.csproj | 4 - 45 files changed, 349 insertions(+), 145 deletions(-) create mode 100644 samples/MiniMvvm/MiniCommand.cs create mode 100644 samples/MiniMvvm/MiniMvvm.csproj create mode 100644 samples/MiniMvvm/PropertyChangedExtensions.cs create mode 100644 samples/MiniMvvm/ViewModelBase.cs diff --git a/Avalonia.sln b/Avalonia.sln index 74a2dbb94b..75f1dd8407 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -230,6 +230,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MicroComGenerator", "src\to EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.MicroCom", "src\Avalonia.MicroCom\Avalonia.MicroCom.csproj", "{FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MiniMvvm", "samples\MiniMvvm\MiniMvvm.csproj", "{BC594FD5-4AF2-409E-A1E6-04123F54D7C5}" +EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution src\Shared\RenderHelpers\RenderHelpers.projitems*{3c4c0cb4-0c0f-4450-a37b-148c84ff905f}*SharedItemsImports = 13 @@ -2116,6 +2118,30 @@ Global {FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}.Release|iPhone.Build.0 = Release|Any CPU {FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {FE2F3E5E-1E34-4972-8DC1-5C2C588E5ECE}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.AppStore|iPhone.Build.0 = Debug|Any CPU + {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Debug|iPhone.Build.0 = Debug|Any CPU + {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Release|Any CPU.Build.0 = Release|Any CPU + {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Release|iPhone.ActiveCfg = Release|Any CPU + {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Release|iPhone.Build.0 = Release|Any CPU + {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {BC594FD5-4AF2-409E-A1E6-04123F54D7C5}.Release|iPhoneSimulator.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -2176,6 +2202,7 @@ Global {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} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {87366D66-1391-4D90-8999-95A620AD786A} diff --git a/samples/BindingDemo/App.xaml.cs b/samples/BindingDemo/App.xaml.cs index eb2da03a7e..8a5364c70b 100644 --- a/samples/BindingDemo/App.xaml.cs +++ b/samples/BindingDemo/App.xaml.cs @@ -1,7 +1,6 @@ using Avalonia; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Markup.Xaml; -using Avalonia.ReactiveUI; namespace BindingDemo { @@ -25,7 +24,6 @@ namespace BindingDemo public static AppBuilder BuildAvaloniaApp() => AppBuilder.Configure() .UsePlatformDetect() - .UseReactiveUI() .LogToTrace(); } } diff --git a/samples/BindingDemo/BindingDemo.csproj b/samples/BindingDemo/BindingDemo.csproj index 817023fd71..d898b737a9 100644 --- a/samples/BindingDemo/BindingDemo.csproj +++ b/samples/BindingDemo/BindingDemo.csproj @@ -6,12 +6,11 @@ - + - diff --git a/samples/BindingDemo/ViewModels/ExceptionErrorViewModel.cs b/samples/BindingDemo/ViewModels/ExceptionErrorViewModel.cs index 0fe12a8ef7..7de083351e 100644 --- a/samples/BindingDemo/ViewModels/ExceptionErrorViewModel.cs +++ b/samples/BindingDemo/ViewModels/ExceptionErrorViewModel.cs @@ -1,9 +1,9 @@ -using ReactiveUI; +using MiniMvvm; using System; namespace BindingDemo.ViewModels { - public class ExceptionErrorViewModel : ReactiveObject + public class ExceptionErrorViewModel : ViewModelBase { private int _lessThan10; diff --git a/samples/BindingDemo/ViewModels/IndeiErrorViewModel.cs b/samples/BindingDemo/ViewModels/IndeiErrorViewModel.cs index caf75c846c..9ae8d9558f 100644 --- a/samples/BindingDemo/ViewModels/IndeiErrorViewModel.cs +++ b/samples/BindingDemo/ViewModels/IndeiErrorViewModel.cs @@ -1,11 +1,11 @@ -using ReactiveUI; +using MiniMvvm; using System; using System.ComponentModel; using System.Collections; namespace BindingDemo.ViewModels { - public class IndeiErrorViewModel : ReactiveObject, INotifyDataErrorInfo + public class IndeiErrorViewModel : ViewModelBase, INotifyDataErrorInfo { private int _maximum = 10; private int _value; diff --git a/samples/BindingDemo/ViewModels/MainWindowViewModel.cs b/samples/BindingDemo/ViewModels/MainWindowViewModel.cs index f0241cad48..18a7a01a69 100644 --- a/samples/BindingDemo/ViewModels/MainWindowViewModel.cs +++ b/samples/BindingDemo/ViewModels/MainWindowViewModel.cs @@ -5,14 +5,14 @@ using System.Reactive; using System.Reactive.Linq; using System.Threading.Tasks; using System.Threading; -using ReactiveUI; +using MiniMvvm; using Avalonia.Controls; using Avalonia.Metadata; using Avalonia.Controls.Selection; namespace BindingDemo.ViewModels { - public class MainWindowViewModel : ReactiveObject + public class MainWindowViewModel : ViewModelBase { private string _booleanString = "True"; private double _doubleValue = 5.0; @@ -32,13 +32,13 @@ namespace BindingDemo.ViewModels Selection = new SelectionModel { SingleSelect = false }; - ShuffleItems = ReactiveCommand.Create(() => + ShuffleItems = MiniCommand.Create(() => { var r = new Random(); Items.Move(r.Next(Items.Count), 1); }); - StringValueCommand = ReactiveCommand.Create(param => + StringValueCommand = MiniCommand.Create(param => { BooleanFlag = !BooleanFlag; StringValue = param.ToString(); @@ -60,7 +60,7 @@ namespace BindingDemo.ViewModels public ObservableCollection Items { get; } public SelectionModel Selection { get; } - public ReactiveCommand ShuffleItems { get; } + public MiniCommand ShuffleItems { get; } public string BooleanString { @@ -93,7 +93,7 @@ namespace BindingDemo.ViewModels } public IObservable CurrentTimeObservable { get; } - public ReactiveCommand StringValueCommand { get; } + public MiniCommand StringValueCommand { get; } public DataAnnotationsErrorViewModel DataAnnotationsValidation { get; } = new DataAnnotationsErrorViewModel(); public ExceptionErrorViewModel ExceptionDataValidation { get; } = new ExceptionErrorViewModel(); diff --git a/samples/BindingDemo/ViewModels/NestedCommandViewModel.cs b/samples/BindingDemo/ViewModels/NestedCommandViewModel.cs index 0e9139ab98..1c2222b0b0 100644 --- a/samples/BindingDemo/ViewModels/NestedCommandViewModel.cs +++ b/samples/BindingDemo/ViewModels/NestedCommandViewModel.cs @@ -1,18 +1,18 @@ -using ReactiveUI; -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Input; +using MiniMvvm; namespace BindingDemo.ViewModels { - public class NestedCommandViewModel : ReactiveObject + public class NestedCommandViewModel : ViewModelBase { public NestedCommandViewModel() { - Command = ReactiveCommand.Create(() => { }); + Command = MiniCommand.Create(() => { }); } public ICommand Command { get; } diff --git a/samples/BindingDemo/ViewModels/TestItem.cs b/samples/BindingDemo/ViewModels/TestItem.cs index 5a9f192f58..2f49a3c99f 100644 --- a/samples/BindingDemo/ViewModels/TestItem.cs +++ b/samples/BindingDemo/ViewModels/TestItem.cs @@ -1,8 +1,8 @@ -using ReactiveUI; +using MiniMvvm; namespace BindingDemo.ViewModels { - public class TestItem : ReactiveObject + public class TestItem : ViewModelBase { private string _stringValue = "String Value"; private string _detail; diff --git a/samples/ControlCatalog.Desktop/Program.cs b/samples/ControlCatalog.Desktop/Program.cs index 5af646b180..7b8b27fff7 100644 --- a/samples/ControlCatalog.Desktop/Program.cs +++ b/samples/ControlCatalog.Desktop/Program.cs @@ -3,7 +3,6 @@ using System.Linq; using Avalonia; using Avalonia.Controls; using Avalonia.Platform; -using Avalonia.ReactiveUI; namespace ControlCatalog { @@ -19,8 +18,7 @@ namespace ControlCatalog public static AppBuilder BuildAvaloniaApp() => AppBuilder.Configure() .LogToTrace() - .UsePlatformDetect() - .UseReactiveUI(); + .UsePlatformDetect(); private static void ConfigureAssetAssembly(AppBuilder builder) { diff --git a/samples/ControlCatalog.NetCore/Program.cs b/samples/ControlCatalog.NetCore/Program.cs index 675ea2e10f..1dc8c09c0e 100644 --- a/samples/ControlCatalog.NetCore/Program.cs +++ b/samples/ControlCatalog.NetCore/Program.cs @@ -10,7 +10,6 @@ using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Dialogs; using Avalonia.Headless; using Avalonia.LogicalTree; -using Avalonia.ReactiveUI; using Avalonia.Threading; namespace ControlCatalog.NetCore @@ -118,7 +117,6 @@ namespace ControlCatalog.NetCore AllowEglInitialization = true }) .UseSkia() - .UseReactiveUI() .UseManagedSystemDialogs() .LogToTrace(); diff --git a/samples/ControlCatalog/ControlCatalog.csproj b/samples/ControlCatalog/ControlCatalog.csproj index 8a88b89b48..1aa926a2a6 100644 --- a/samples/ControlCatalog/ControlCatalog.csproj +++ b/samples/ControlCatalog/ControlCatalog.csproj @@ -24,8 +24,8 @@ - + diff --git a/samples/ControlCatalog/Pages/LabelsPage.axaml.cs b/samples/ControlCatalog/Pages/LabelsPage.axaml.cs index b8503d6ae6..a14978fd2c 100644 --- a/samples/ControlCatalog/Pages/LabelsPage.axaml.cs +++ b/samples/ControlCatalog/Pages/LabelsPage.axaml.cs @@ -2,7 +2,6 @@ using Avalonia.Controls; using Avalonia.Markup.Xaml; using ControlCatalog.Models; -using ReactiveUI; namespace ControlCatalog.Pages { diff --git a/samples/ControlCatalog/Pages/ListBoxPage.xaml b/samples/ControlCatalog/Pages/ListBoxPage.xaml index 3521ad71a9..f515db84d4 100644 --- a/samples/ControlCatalog/Pages/ListBoxPage.xaml +++ b/samples/ControlCatalog/Pages/ListBoxPage.xaml @@ -20,6 +20,6 @@ + SelectionMode="{Binding SelectionMode^}"/> diff --git a/samples/ControlCatalog/Pages/MenuPage.xaml.cs b/samples/ControlCatalog/Pages/MenuPage.xaml.cs index 46dbe3dcad..5999510b6c 100644 --- a/samples/ControlCatalog/Pages/MenuPage.xaml.cs +++ b/samples/ControlCatalog/Pages/MenuPage.xaml.cs @@ -6,7 +6,6 @@ using System.Windows.Input; using Avalonia.Controls; using Avalonia.Markup.Xaml; using ControlCatalog.ViewModels; -using ReactiveUI; namespace ControlCatalog.Pages { diff --git a/samples/ControlCatalog/Pages/NumericUpDownPage.xaml.cs b/samples/ControlCatalog/Pages/NumericUpDownPage.xaml.cs index 92da64d87e..31749edf08 100644 --- a/samples/ControlCatalog/Pages/NumericUpDownPage.xaml.cs +++ b/samples/ControlCatalog/Pages/NumericUpDownPage.xaml.cs @@ -6,7 +6,7 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Controls.Primitives; using Avalonia.Markup.Xaml; -using ReactiveUI; +using MiniMvvm; namespace ControlCatalog.Pages { @@ -26,7 +26,7 @@ namespace ControlCatalog.Pages } - public class NumbersPageViewModel : ReactiveObject + public class NumbersPageViewModel : ViewModelBase { private IList _formats; private FormatObject _selectedFormat; diff --git a/samples/ControlCatalog/Pages/ScrollViewerPage.xaml.cs b/samples/ControlCatalog/Pages/ScrollViewerPage.xaml.cs index 36d3768b13..dcd7a88a56 100644 --- a/samples/ControlCatalog/Pages/ScrollViewerPage.xaml.cs +++ b/samples/ControlCatalog/Pages/ScrollViewerPage.xaml.cs @@ -2,11 +2,11 @@ using System.Collections.Generic; using Avalonia.Controls; using Avalonia.Controls.Primitives; using Avalonia.Markup.Xaml; -using ReactiveUI; +using MiniMvvm; namespace ControlCatalog.Pages { - public class ScrollViewerPageViewModel : ReactiveObject + public class ScrollViewerPageViewModel : ViewModelBase { private bool _allowAutoHide; private ScrollBarVisibility _horizontalScrollVisibility; diff --git a/samples/ControlCatalog/Pages/TabControlPage.xaml.cs b/samples/ControlCatalog/Pages/TabControlPage.xaml.cs index a38a3ab4cb..f49b13091b 100644 --- a/samples/ControlCatalog/Pages/TabControlPage.xaml.cs +++ b/samples/ControlCatalog/Pages/TabControlPage.xaml.cs @@ -6,7 +6,7 @@ using Avalonia.Markup.Xaml; using Avalonia.Media.Imaging; using Avalonia.Platform; -using ReactiveUI; +using MiniMvvm; namespace ControlCatalog.Pages { @@ -56,7 +56,7 @@ namespace ControlCatalog.Pages return new Bitmap(assets.Open(new Uri(uri))); } - private class PageViewModel : ReactiveObject + private class PageViewModel : ViewModelBase { private Dock _tabPlacement; diff --git a/samples/ControlCatalog/ViewModels/ContextMenuPageViewModel.cs b/samples/ControlCatalog/ViewModels/ContextMenuPageViewModel.cs index 5c2f74d2d5..3f5d0cd93c 100644 --- a/samples/ControlCatalog/ViewModels/ContextMenuPageViewModel.cs +++ b/samples/ControlCatalog/ViewModels/ContextMenuPageViewModel.cs @@ -3,7 +3,7 @@ using System.Reactive; using System.Threading.Tasks; using Avalonia.Controls; using Avalonia.VisualTree; -using ReactiveUI; +using MiniMvvm; namespace ControlCatalog.ViewModels { @@ -12,9 +12,9 @@ namespace ControlCatalog.ViewModels public Control View { get; set; } public ContextMenuPageViewModel() { - OpenCommand = ReactiveCommand.CreateFromTask(Open); - SaveCommand = ReactiveCommand.Create(Save); - OpenRecentCommand = ReactiveCommand.Create(OpenRecent); + OpenCommand = MiniCommand.CreateFromTask(Open); + SaveCommand = MiniCommand.Create(Save); + OpenRecentCommand = MiniCommand.Create(OpenRecent); MenuItems = new[] { @@ -44,9 +44,9 @@ namespace ControlCatalog.ViewModels } public IReadOnlyList MenuItems { get; set; } - public ReactiveCommand OpenCommand { get; } - public ReactiveCommand SaveCommand { get; } - public ReactiveCommand OpenRecentCommand { get; } + public MiniCommand OpenCommand { get; } + public MiniCommand SaveCommand { get; } + public MiniCommand OpenRecentCommand { get; } public async Task Open() { diff --git a/samples/ControlCatalog/ViewModels/ItemsRepeaterPageViewModel.cs b/samples/ControlCatalog/ViewModels/ItemsRepeaterPageViewModel.cs index f893a6e28e..ee1fa6ae77 100644 --- a/samples/ControlCatalog/ViewModels/ItemsRepeaterPageViewModel.cs +++ b/samples/ControlCatalog/ViewModels/ItemsRepeaterPageViewModel.cs @@ -2,11 +2,11 @@ using System.Collections.ObjectModel; using System.Linq; using Avalonia.Media; -using ReactiveUI; +using MiniMvvm; namespace ControlCatalog.ViewModels { - public class ItemsRepeaterPageViewModel : ReactiveObject + public class ItemsRepeaterPageViewModel : ViewModelBase { private int _newItemIndex = 1; private int _newGenerationIndex = 0; @@ -59,7 +59,7 @@ namespace ControlCatalog.ViewModels })); } - public class Item : ReactiveObject + public class Item : ViewModelBase { private double _height = double.NaN; diff --git a/samples/ControlCatalog/ViewModels/ListBoxPageViewModel.cs b/samples/ControlCatalog/ViewModels/ListBoxPageViewModel.cs index f75bc32105..7f2d6e9572 100644 --- a/samples/ControlCatalog/ViewModels/ListBoxPageViewModel.cs +++ b/samples/ControlCatalog/ViewModels/ListBoxPageViewModel.cs @@ -4,18 +4,18 @@ using System.Linq; using System.Reactive; using Avalonia.Controls; using Avalonia.Controls.Selection; -using ReactiveUI; +using MiniMvvm; namespace ControlCatalog.ViewModels { - public class ListBoxPageViewModel : ReactiveObject + public class ListBoxPageViewModel : ViewModelBase { private bool _multiple; private bool _toggle; private bool _alwaysSelected; private bool _autoScrollToSelectedItem = true; private int _counter; - private ObservableAsPropertyHelper _selectionMode; + private IObservable _selectionMode; public ListBoxPageViewModel() { @@ -29,14 +29,13 @@ namespace ControlCatalog.ViewModels x => x.Toggle, x => x.AlwaysSelected, (m, t, a) => - (m ? SelectionMode.Multiple : 0) | - (t ? SelectionMode.Toggle : 0) | - (a ? SelectionMode.AlwaysSelected : 0)) - .ToProperty(this, x => x.SelectionMode); + (m ? Avalonia.Controls.SelectionMode.Multiple : 0) | + (t ? Avalonia.Controls.SelectionMode.Toggle : 0) | + (a ? Avalonia.Controls.SelectionMode.AlwaysSelected : 0)); - AddItemCommand = ReactiveCommand.Create(() => Items.Add(GenerateItem())); + AddItemCommand = MiniCommand.Create(() => Items.Add(GenerateItem())); - RemoveItemCommand = ReactiveCommand.Create(() => + RemoveItemCommand = MiniCommand.Create(() => { var items = Selection.SelectedItems.ToList(); @@ -46,7 +45,7 @@ namespace ControlCatalog.ViewModels } }); - SelectRandomItemCommand = ReactiveCommand.Create(() => + SelectRandomItemCommand = MiniCommand.Create(() => { var random = new Random(); @@ -60,7 +59,7 @@ namespace ControlCatalog.ViewModels public ObservableCollection Items { get; } public SelectionModel Selection { get; } - public SelectionMode SelectionMode => _selectionMode.Value; + public IObservable SelectionMode => _selectionMode; public bool Multiple { @@ -86,9 +85,9 @@ namespace ControlCatalog.ViewModels set => this.RaiseAndSetIfChanged(ref _autoScrollToSelectedItem, value); } - public ReactiveCommand AddItemCommand { get; } - public ReactiveCommand RemoveItemCommand { get; } - public ReactiveCommand SelectRandomItemCommand { get; } + public MiniCommand AddItemCommand { get; } + public MiniCommand RemoveItemCommand { get; } + public MiniCommand SelectRandomItemCommand { get; } private string GenerateItem() => $"Item {_counter++.ToString()}"; } diff --git a/samples/ControlCatalog/ViewModels/MainWindowViewModel.cs b/samples/ControlCatalog/ViewModels/MainWindowViewModel.cs index 4356a032fa..4b3cfa9c9d 100644 --- a/samples/ControlCatalog/ViewModels/MainWindowViewModel.cs +++ b/samples/ControlCatalog/ViewModels/MainWindowViewModel.cs @@ -5,11 +5,11 @@ using Avalonia.Controls.Notifications; using Avalonia.Dialogs; using Avalonia.Platform; using System; -using ReactiveUI; +using MiniMvvm; namespace ControlCatalog.ViewModels { - class MainWindowViewModel : ReactiveObject + class MainWindowViewModel : ViewModelBase { private IManagedNotificationManager _notificationManager; @@ -27,22 +27,22 @@ namespace ControlCatalog.ViewModels { _notificationManager = notificationManager; - ShowCustomManagedNotificationCommand = ReactiveCommand.Create(() => + ShowCustomManagedNotificationCommand = MiniCommand.Create(() => { NotificationManager.Show(new NotificationViewModel(NotificationManager) { Title = "Hey There!", Message = "Did you know that Avalonia now supports Custom In-Window Notifications?" }); }); - ShowManagedNotificationCommand = ReactiveCommand.Create(() => + ShowManagedNotificationCommand = MiniCommand.Create(() => { NotificationManager.Show(new Avalonia.Controls.Notifications.Notification("Welcome", "Avalonia now supports Notifications.", NotificationType.Information)); }); - ShowNativeNotificationCommand = ReactiveCommand.Create(() => + ShowNativeNotificationCommand = MiniCommand.Create(() => { NotificationManager.Show(new Avalonia.Controls.Notifications.Notification("Error", "Native Notifications are not quite ready. Coming soon.", NotificationType.Error)); }); - AboutCommand = ReactiveCommand.CreateFromTask(async () => + AboutCommand = MiniCommand.CreateFromTask(async () => { var dialog = new AboutAvaloniaDialog(); @@ -51,12 +51,12 @@ namespace ControlCatalog.ViewModels await dialog.ShowDialog(mainWindow); }); - ExitCommand = ReactiveCommand.Create(() => + ExitCommand = MiniCommand.Create(() => { (App.Current.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime).Shutdown(); }); - ToggleMenuItemCheckedCommand = ReactiveCommand.Create(() => + ToggleMenuItemCheckedCommand = MiniCommand.Create(() => { IsMenuItemChecked = !IsMenuItemChecked; }); @@ -153,16 +153,16 @@ namespace ControlCatalog.ViewModels set { this.RaiseAndSetIfChanged(ref _isMenuItemChecked, value); } } - public ReactiveCommand ShowCustomManagedNotificationCommand { get; } + public MiniCommand ShowCustomManagedNotificationCommand { get; } - public ReactiveCommand ShowManagedNotificationCommand { get; } + public MiniCommand ShowManagedNotificationCommand { get; } - public ReactiveCommand ShowNativeNotificationCommand { get; } + public MiniCommand ShowNativeNotificationCommand { get; } - public ReactiveCommand AboutCommand { get; } + public MiniCommand AboutCommand { get; } - public ReactiveCommand ExitCommand { get; } + public MiniCommand ExitCommand { get; } - public ReactiveCommand ToggleMenuItemCheckedCommand { get; } + public MiniCommand ToggleMenuItemCheckedCommand { get; } } } diff --git a/samples/ControlCatalog/ViewModels/MenuPageViewModel.cs b/samples/ControlCatalog/ViewModels/MenuPageViewModel.cs index 9e7ae8b716..ecbd59c5d7 100644 --- a/samples/ControlCatalog/ViewModels/MenuPageViewModel.cs +++ b/samples/ControlCatalog/ViewModels/MenuPageViewModel.cs @@ -4,7 +4,7 @@ using System.Reactive.Linq; using System.Threading.Tasks; using Avalonia.Controls; using Avalonia.VisualTree; -using ReactiveUI; +using MiniMvvm; namespace ControlCatalog.ViewModels { @@ -13,9 +13,9 @@ namespace ControlCatalog.ViewModels public Control View { get; set; } public MenuPageViewModel() { - OpenCommand = ReactiveCommand.CreateFromTask(Open); - SaveCommand = ReactiveCommand.Create(Save, Observable.Return(false)); - OpenRecentCommand = ReactiveCommand.Create(OpenRecent); + OpenCommand = MiniCommand.CreateFromTask(Open); + SaveCommand = MiniCommand.Create(Save); + OpenRecentCommand = MiniCommand.Create(OpenRecent); var recentItems = new[] { @@ -65,9 +65,9 @@ namespace ControlCatalog.ViewModels public IReadOnlyList MenuItems { get; set; } public IReadOnlyList RecentItems { get; set; } - public ReactiveCommand OpenCommand { get; } - public ReactiveCommand SaveCommand { get; } - public ReactiveCommand OpenRecentCommand { get; } + public MiniCommand OpenCommand { get; } + public MiniCommand SaveCommand { get; } + public MiniCommand OpenRecentCommand { get; } public async Task Open() { diff --git a/samples/ControlCatalog/ViewModels/NotificationViewModel.cs b/samples/ControlCatalog/ViewModels/NotificationViewModel.cs index 8724ba344b..2052481015 100644 --- a/samples/ControlCatalog/ViewModels/NotificationViewModel.cs +++ b/samples/ControlCatalog/ViewModels/NotificationViewModel.cs @@ -1,6 +1,6 @@ using System.Reactive; using Avalonia.Controls.Notifications; -using ReactiveUI; +using MiniMvvm; namespace ControlCatalog.ViewModels { @@ -8,12 +8,12 @@ namespace ControlCatalog.ViewModels { public NotificationViewModel(INotificationManager manager) { - YesCommand = ReactiveCommand.Create(() => + YesCommand = MiniCommand.Create(() => { manager.Show(new Avalonia.Controls.Notifications.Notification("Avalonia Notifications", "Start adding notifications to your app today.")); }); - NoCommand = ReactiveCommand.Create(() => + NoCommand = MiniCommand.Create(() => { manager.Show(new Avalonia.Controls.Notifications.Notification("Avalonia Notifications", "Start adding notifications to your app today. To find out more visit...")); }); @@ -22,9 +22,9 @@ namespace ControlCatalog.ViewModels public string Title { get; set; } public string Message { get; set; } - public ReactiveCommand YesCommand { get; } + public MiniCommand YesCommand { get; } - public ReactiveCommand NoCommand { get; } + public MiniCommand NoCommand { get; } } } diff --git a/samples/ControlCatalog/ViewModels/SplitViewPageViewModel.cs b/samples/ControlCatalog/ViewModels/SplitViewPageViewModel.cs index f27f605a8b..9e6932bb76 100644 --- a/samples/ControlCatalog/ViewModels/SplitViewPageViewModel.cs +++ b/samples/ControlCatalog/ViewModels/SplitViewPageViewModel.cs @@ -1,10 +1,10 @@ using System; using Avalonia.Controls; -using ReactiveUI; +using MiniMvvm; namespace ControlCatalog.ViewModels { - public class SplitViewPageViewModel : ReactiveObject + public class SplitViewPageViewModel : ViewModelBase { private bool _isLeft = true; private int _displayMode = 3; //CompactOverlay diff --git a/samples/ControlCatalog/ViewModels/TreeViewPageViewModel.cs b/samples/ControlCatalog/ViewModels/TreeViewPageViewModel.cs index 210e281ed6..c03379330f 100644 --- a/samples/ControlCatalog/ViewModels/TreeViewPageViewModel.cs +++ b/samples/ControlCatalog/ViewModels/TreeViewPageViewModel.cs @@ -3,11 +3,11 @@ using System.Collections.ObjectModel; using System.Linq; using System.Reactive; using Avalonia.Controls; -using ReactiveUI; +using MiniMvvm; namespace ControlCatalog.ViewModels { - public class TreeViewPageViewModel : ReactiveObject + public class TreeViewPageViewModel : ViewModelBase { private readonly Node _root; private SelectionMode _selectionMode; @@ -19,16 +19,16 @@ namespace ControlCatalog.ViewModels Items = _root.Children; SelectedItems = new ObservableCollection(); - AddItemCommand = ReactiveCommand.Create(AddItem); - RemoveItemCommand = ReactiveCommand.Create(RemoveItem); - SelectRandomItemCommand = ReactiveCommand.Create(SelectRandomItem); + AddItemCommand = MiniCommand.Create(AddItem); + RemoveItemCommand = MiniCommand.Create(RemoveItem); + SelectRandomItemCommand = MiniCommand.Create(SelectRandomItem); } public ObservableCollection Items { get; } public ObservableCollection SelectedItems { get; } - public ReactiveCommand AddItemCommand { get; } - public ReactiveCommand RemoveItemCommand { get; } - public ReactiveCommand SelectRandomItemCommand { get; } + public MiniCommand AddItemCommand { get; } + public MiniCommand RemoveItemCommand { get; } + public MiniCommand SelectRandomItemCommand { get; } public SelectionMode SelectionMode { diff --git a/samples/MiniMvvm/MiniCommand.cs b/samples/MiniMvvm/MiniCommand.cs new file mode 100644 index 0000000000..c6a9273c20 --- /dev/null +++ b/samples/MiniMvvm/MiniCommand.cs @@ -0,0 +1,66 @@ +using System; +using System.Threading.Tasks; +using System.Windows.Input; + +namespace MiniMvvm +{ + public sealed class MiniCommand : MiniCommand, ICommand + { + private readonly Action _cb; + private bool _busy; + private Func _acb; + + public MiniCommand(Action cb) + { + _cb = cb; + } + + public MiniCommand(Func cb) + { + _acb = cb; + } + + private bool Busy + { + get => _busy; + set + { + _busy = value; + CanExecuteChanged?.Invoke(this, EventArgs.Empty); + } + } + + + public override event EventHandler CanExecuteChanged; + public override bool CanExecute(object parameter) => !_busy; + + public override async void Execute(object parameter) + { + if(Busy) + return; + try + { + Busy = true; + if (_cb != null) + _cb((T)parameter); + else + await _acb((T)parameter); + } + finally + { + Busy = false; + } + } + } + + public abstract class MiniCommand : ICommand + { + public static MiniCommand Create(Action cb) => new MiniCommand(_ => cb()); + public static MiniCommand Create(Action cb) => new MiniCommand(cb); + public static MiniCommand CreateFromTask(Func cb) => new MiniCommand(_ => cb()); + + public abstract bool CanExecute(object parameter); + public abstract void Execute(object parameter); + public abstract event EventHandler CanExecuteChanged; + } +} diff --git a/samples/MiniMvvm/MiniMvvm.csproj b/samples/MiniMvvm/MiniMvvm.csproj new file mode 100644 index 0000000000..6535b2bdbd --- /dev/null +++ b/samples/MiniMvvm/MiniMvvm.csproj @@ -0,0 +1,6 @@ + + + netstandard2.0 + + + diff --git a/samples/MiniMvvm/PropertyChangedExtensions.cs b/samples/MiniMvvm/PropertyChangedExtensions.cs new file mode 100644 index 0000000000..f1065c7530 --- /dev/null +++ b/samples/MiniMvvm/PropertyChangedExtensions.cs @@ -0,0 +1,108 @@ +using System; +using System.ComponentModel; +using System.Linq.Expressions; +using System.Reactive.Linq; +using System.Reflection; + +namespace MiniMvvm +{ + public static class PropertyChangedExtensions + { + class PropertyObservable : IObservable + { + private readonly INotifyPropertyChanged _target; + private readonly PropertyInfo _info; + + public PropertyObservable(INotifyPropertyChanged target, PropertyInfo info) + { + _target = target; + _info = info; + } + + class Subscription : IDisposable + { + private readonly INotifyPropertyChanged _target; + private readonly PropertyInfo _info; + private readonly IObserver _observer; + + public Subscription(INotifyPropertyChanged target, PropertyInfo info, IObserver observer) + { + _target = target; + _info = info; + _observer = observer; + _target.PropertyChanged += OnPropertyChanged; + _observer.OnNext((T)_info.GetValue(_target)); + } + + private void OnPropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == _info.Name) + _observer.OnNext((T)_info.GetValue(_target)); + } + + public void Dispose() + { + _target.PropertyChanged -= OnPropertyChanged; + _observer.OnCompleted(); + } + } + + public IDisposable Subscribe(IObserver observer) + { + return new Subscription(_target, _info, observer); + } + } + + public static IObservable WhenAnyValue(this TModel model, + Expression> expr) where TModel : INotifyPropertyChanged + { + var l = (LambdaExpression)expr; + var ma = (MemberExpression)l.Body; + var prop = (PropertyInfo)ma.Member; + return new PropertyObservable(model, prop); + } + + public static IObservable WhenAnyValue(this TModel model, + Expression> v1, + Func cb + ) where TModel : INotifyPropertyChanged + { + return model.WhenAnyValue(v1).Select(cb); + } + + public static IObservable WhenAnyValue(this TModel model, + Expression> v1, + Expression> v2, + Func cb + ) where TModel : INotifyPropertyChanged => + Observable.CombineLatest( + model.WhenAnyValue(v1), + model.WhenAnyValue(v2), + cb); + + public static IObservable> WhenAnyValue(this TModel model, + Expression> v1, + Expression> v2 + ) where TModel : INotifyPropertyChanged => + model.WhenAnyValue(v1, v2, (a1, a2) => (a1, a2)); + + public static IObservable WhenAnyValue(this TModel model, + Expression> v1, + Expression> v2, + Expression> v3, + Func cb + ) where TModel : INotifyPropertyChanged => + Observable.CombineLatest( + model.WhenAnyValue(v1), + model.WhenAnyValue(v2), + model.WhenAnyValue(v3), + cb); + + public static IObservable> WhenAnyValue(this TModel model, + Expression> v1, + Expression> v2, + Expression> v3 + ) where TModel : INotifyPropertyChanged => + model.WhenAnyValue(v1, v2, v3, (a1, a2, a3) => (a1, a2, a3)); + } +} diff --git a/samples/MiniMvvm/ViewModelBase.cs b/samples/MiniMvvm/ViewModelBase.cs new file mode 100644 index 0000000000..7256b05cef --- /dev/null +++ b/samples/MiniMvvm/ViewModelBase.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using System.ComponentModel; +using System.Reactive.Joins; +using System.Runtime.CompilerServices; + +namespace MiniMvvm +{ + public class ViewModelBase : INotifyPropertyChanged + { + public event PropertyChangedEventHandler PropertyChanged; + protected bool RaiseAndSetIfChanged(ref T field, T value, [CallerMemberName] string propertyName = null) + { + if (!EqualityComparer.Default.Equals(field, value)) + { + field = value; + RaisePropertyChanged(propertyName); + return true; + } + return false; + } + + + protected void RaisePropertyChanged([CallerMemberName] string propertyName = null) + => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } +} diff --git a/samples/Previewer/Previewer.csproj b/samples/Previewer/Previewer.csproj index cd3daf61e1..cfedb7ad9e 100644 --- a/samples/Previewer/Previewer.csproj +++ b/samples/Previewer/Previewer.csproj @@ -8,7 +8,6 @@ %(Filename) - diff --git a/samples/RenderDemo/App.xaml.cs b/samples/RenderDemo/App.xaml.cs index e6ec963d75..8054b06964 100644 --- a/samples/RenderDemo/App.xaml.cs +++ b/samples/RenderDemo/App.xaml.cs @@ -1,7 +1,6 @@ using Avalonia; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Markup.Xaml; -using Avalonia.ReactiveUI; namespace RenderDemo { @@ -32,7 +31,6 @@ namespace RenderDemo OverlayPopups = true, }) .UsePlatformDetect() - .UseReactiveUI() .LogToTrace(); } } diff --git a/samples/RenderDemo/MainWindow.xaml.cs b/samples/RenderDemo/MainWindow.xaml.cs index b45a605e04..877eb8016a 100644 --- a/samples/RenderDemo/MainWindow.xaml.cs +++ b/samples/RenderDemo/MainWindow.xaml.cs @@ -3,7 +3,7 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Markup.Xaml; using RenderDemo.ViewModels; -using ReactiveUI; +using MiniMvvm; namespace RenderDemo { diff --git a/samples/RenderDemo/RenderDemo.csproj b/samples/RenderDemo/RenderDemo.csproj index d1654f4b54..0d33b4c111 100644 --- a/samples/RenderDemo/RenderDemo.csproj +++ b/samples/RenderDemo/RenderDemo.csproj @@ -9,12 +9,11 @@ - + - diff --git a/samples/RenderDemo/ViewModels/AnimationsPageViewModel.cs b/samples/RenderDemo/ViewModels/AnimationsPageViewModel.cs index 7b89b7944c..f8ba01f3d1 100644 --- a/samples/RenderDemo/ViewModels/AnimationsPageViewModel.cs +++ b/samples/RenderDemo/ViewModels/AnimationsPageViewModel.cs @@ -1,10 +1,10 @@ using System; -using ReactiveUI; +using MiniMvvm; using Avalonia.Animation; namespace RenderDemo.ViewModels { - public class AnimationsPageViewModel : ReactiveObject + public class AnimationsPageViewModel : ViewModelBase { private bool _isPlaying = true; diff --git a/samples/RenderDemo/ViewModels/MainWindowViewModel.cs b/samples/RenderDemo/ViewModels/MainWindowViewModel.cs index eda5e80530..19917c20df 100644 --- a/samples/RenderDemo/ViewModels/MainWindowViewModel.cs +++ b/samples/RenderDemo/ViewModels/MainWindowViewModel.cs @@ -1,11 +1,10 @@ using System.Reactive; using System.Threading.Tasks; - -using ReactiveUI; +using MiniMvvm; namespace RenderDemo.ViewModels { - public class MainWindowViewModel : ReactiveObject + public class MainWindowViewModel : ViewModelBase { private bool drawDirtyRects = false; private bool drawFps = true; @@ -14,9 +13,9 @@ namespace RenderDemo.ViewModels public MainWindowViewModel() { - ToggleDrawDirtyRects = ReactiveCommand.Create(() => DrawDirtyRects = !DrawDirtyRects); - ToggleDrawFps = ReactiveCommand.Create(() => DrawFps = !DrawFps); - ResizeWindow = ReactiveCommand.CreateFromTask(ResizeWindowAsync); + ToggleDrawDirtyRects = MiniCommand.Create(() => DrawDirtyRects = !DrawDirtyRects); + ToggleDrawFps = MiniCommand.Create(() => DrawFps = !DrawFps); + ResizeWindow = MiniCommand.CreateFromTask(ResizeWindowAsync); } public bool DrawDirtyRects @@ -43,9 +42,9 @@ namespace RenderDemo.ViewModels set => this.RaiseAndSetIfChanged(ref height, value); } - public ReactiveCommand ToggleDrawDirtyRects { get; } - public ReactiveCommand ToggleDrawFps { get; } - public ReactiveCommand ResizeWindow { get; } + public MiniCommand ToggleDrawDirtyRects { get; } + public MiniCommand ToggleDrawFps { get; } + public MiniCommand ResizeWindow { get; } private async Task ResizeWindowAsync() { diff --git a/samples/Sandbox/Program.cs b/samples/Sandbox/Program.cs index 7d41a8b8c0..1e74105196 100644 --- a/samples/Sandbox/Program.cs +++ b/samples/Sandbox/Program.cs @@ -1,5 +1,4 @@ using Avalonia; -using Avalonia.ReactiveUI; namespace Sandbox { @@ -9,7 +8,6 @@ namespace Sandbox { AppBuilder.Configure() .UsePlatformDetect() - .UseReactiveUI() .LogToTrace() .StartWithClassicDesktopLifetime(args); } diff --git a/samples/Sandbox/Sandbox.csproj b/samples/Sandbox/Sandbox.csproj index 1a0a8a7ce5..0c19440a1e 100644 --- a/samples/Sandbox/Sandbox.csproj +++ b/samples/Sandbox/Sandbox.csproj @@ -8,7 +8,6 @@ - diff --git a/samples/VirtualizationDemo/Program.cs b/samples/VirtualizationDemo/Program.cs index 46c05f74b2..febda46450 100644 --- a/samples/VirtualizationDemo/Program.cs +++ b/samples/VirtualizationDemo/Program.cs @@ -1,5 +1,4 @@ using Avalonia; -using Avalonia.ReactiveUI; namespace VirtualizationDemo { @@ -8,7 +7,6 @@ namespace VirtualizationDemo public static AppBuilder BuildAvaloniaApp() => AppBuilder.Configure() .UsePlatformDetect() - .UseReactiveUI() .LogToTrace(); public static int Main(string[] args) diff --git a/samples/VirtualizationDemo/ViewModels/ItemViewModel.cs b/samples/VirtualizationDemo/ViewModels/ItemViewModel.cs index cf34980b40..9ba505ffe5 100644 --- a/samples/VirtualizationDemo/ViewModels/ItemViewModel.cs +++ b/samples/VirtualizationDemo/ViewModels/ItemViewModel.cs @@ -1,9 +1,9 @@ using System; -using ReactiveUI; +using MiniMvvm; namespace VirtualizationDemo.ViewModels { - internal class ItemViewModel : ReactiveObject + internal class ItemViewModel : ViewModelBase { private string _prefix; private int _index; diff --git a/samples/VirtualizationDemo/ViewModels/MainWindowViewModel.cs b/samples/VirtualizationDemo/ViewModels/MainWindowViewModel.cs index 852c01399f..514df691ae 100644 --- a/samples/VirtualizationDemo/ViewModels/MainWindowViewModel.cs +++ b/samples/VirtualizationDemo/ViewModels/MainWindowViewModel.cs @@ -5,13 +5,13 @@ using System.Reactive; using Avalonia.Collections; using Avalonia.Controls; using Avalonia.Controls.Primitives; -using ReactiveUI; using Avalonia.Layout; using Avalonia.Controls.Selection; +using MiniMvvm; namespace VirtualizationDemo.ViewModels { - internal class MainWindowViewModel : ReactiveObject + internal class MainWindowViewModel : ViewModelBase { private int _itemCount = 200; private string _newItemString = "New Item"; @@ -26,15 +26,15 @@ namespace VirtualizationDemo.ViewModels public MainWindowViewModel() { this.WhenAnyValue(x => x.ItemCount).Subscribe(ResizeItems); - RecreateCommand = ReactiveCommand.Create(() => Recreate()); + RecreateCommand = MiniCommand.Create(() => Recreate()); - AddItemCommand = ReactiveCommand.Create(() => AddItem()); + AddItemCommand = MiniCommand.Create(() => AddItem()); - RemoveItemCommand = ReactiveCommand.Create(() => Remove()); + RemoveItemCommand = MiniCommand.Create(() => Remove()); - SelectFirstCommand = ReactiveCommand.Create(() => SelectItem(0)); + SelectFirstCommand = MiniCommand.Create(() => SelectItem(0)); - SelectLastCommand = ReactiveCommand.Create(() => SelectItem(Items.Count - 1)); + SelectLastCommand = MiniCommand.Create(() => SelectItem(Items.Count - 1)); } public string NewItemString @@ -90,11 +90,11 @@ namespace VirtualizationDemo.ViewModels public IEnumerable VirtualizationModes => Enum.GetValues(typeof(ItemVirtualizationMode)).Cast(); - public ReactiveCommand AddItemCommand { get; private set; } - public ReactiveCommand RecreateCommand { get; private set; } - public ReactiveCommand RemoveItemCommand { get; private set; } - public ReactiveCommand SelectFirstCommand { get; private set; } - public ReactiveCommand SelectLastCommand { get; private set; } + public MiniCommand AddItemCommand { get; private set; } + public MiniCommand RecreateCommand { get; private set; } + public MiniCommand RemoveItemCommand { get; private set; } + public MiniCommand SelectFirstCommand { get; private set; } + public MiniCommand SelectLastCommand { get; private set; } public void RandomizeSize() { diff --git a/samples/VirtualizationDemo/VirtualizationDemo.csproj b/samples/VirtualizationDemo/VirtualizationDemo.csproj index 817023fd71..d898b737a9 100644 --- a/samples/VirtualizationDemo/VirtualizationDemo.csproj +++ b/samples/VirtualizationDemo/VirtualizationDemo.csproj @@ -6,12 +6,11 @@ - + - diff --git a/samples/interop/Direct3DInteropSample/Direct3DInteropSample.csproj b/samples/interop/Direct3DInteropSample/Direct3DInteropSample.csproj index bd6b6f170f..cd9963a2e5 100644 --- a/samples/interop/Direct3DInteropSample/Direct3DInteropSample.csproj +++ b/samples/interop/Direct3DInteropSample/Direct3DInteropSample.csproj @@ -22,9 +22,9 @@ - + diff --git a/samples/interop/Direct3DInteropSample/MainWindowViewModel.cs b/samples/interop/Direct3DInteropSample/MainWindowViewModel.cs index d39a21cd07..21679a99c5 100644 --- a/samples/interop/Direct3DInteropSample/MainWindowViewModel.cs +++ b/samples/interop/Direct3DInteropSample/MainWindowViewModel.cs @@ -3,11 +3,11 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; -using ReactiveUI; +using MiniMvvm; namespace Direct3DInteropSample { - public class MainWindowViewModel : ReactiveObject + public class MainWindowViewModel : ViewModelBase { private double _rotationX; diff --git a/samples/interop/WindowsInteropTest/WindowsInteropTest.csproj b/samples/interop/WindowsInteropTest/WindowsInteropTest.csproj index c067d38595..8394d7cb13 100644 --- a/samples/interop/WindowsInteropTest/WindowsInteropTest.csproj +++ b/samples/interop/WindowsInteropTest/WindowsInteropTest.csproj @@ -136,10 +136,6 @@ {42472427-4774-4c81-8aff-9f27b8e31721} Avalonia.Layout - - {6417b24e-49c2-4985-8db2-3ab9d898ec91} - Avalonia.ReactiveUI - {eb582467-6abb-43a1-b052-e981ba910e3a} Avalonia.Visuals @@ -190,4 +186,4 @@ - \ No newline at end of file + diff --git a/src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj b/src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj index b8697e0ca2..f880e48282 100644 --- a/src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj +++ b/src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj @@ -127,10 +127,6 @@ {42472427-4774-4c81-8aff-9f27b8e31721} Avalonia.Layout - - {6417b24e-49c2-4985-8db2-3ab9d898ec91} - Avalonia.ReactiveUI - {eb582467-6abb-43a1-b052-e981ba910e3a} Avalonia.Visuals From be8a3e83fa9fd50c29e12c33af4951c05105f231 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 16 Nov 2020 21:12:46 +0000 Subject: [PATCH 027/100] fix OSX NRE. --- src/Avalonia.Native/WindowImplBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Native/WindowImplBase.cs b/src/Avalonia.Native/WindowImplBase.cs index 20b6b8ecc5..150ab2703e 100644 --- a/src/Avalonia.Native/WindowImplBase.cs +++ b/src/Avalonia.Native/WindowImplBase.cs @@ -174,7 +174,7 @@ namespace Avalonia.Native void IAvnWindowBaseEvents.Resized(AvnSize* size) { - if (_parent._native != null) + if (_parent?._native != null) { var s = new Size(size->Width, size->Height); _parent._savedLogicalSize = s; From c24f65a5f78cbe0dd11d926d28ea8d8a198d90a0 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Mon, 16 Nov 2020 22:51:18 +0100 Subject: [PATCH 028/100] Fix even more broken cases for closing child/dialog windows on win32. --- .../ControlCatalog/Pages/DialogsPage.xaml.cs | 29 +++++++++++++--- .../Avalonia.Win32/WindowImpl.AppWndProc.cs | 19 ++--------- src/Windows/Avalonia.Win32/WindowImpl.cs | 34 +++++++++++++++++++ 3 files changed, 61 insertions(+), 21 deletions(-) diff --git a/samples/ControlCatalog/Pages/DialogsPage.xaml.cs b/samples/ControlCatalog/Pages/DialogsPage.xaml.cs index cf6c771e34..49921fb7f6 100644 --- a/samples/ControlCatalog/Pages/DialogsPage.xaml.cs +++ b/samples/ControlCatalog/Pages/DialogsPage.xaml.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Reflection; using Avalonia.Controls; using Avalonia.Dialogs; +using Avalonia.Layout; using Avalonia.Markup.Xaml; #pragma warning disable 4014 @@ -112,11 +113,29 @@ namespace ControlCatalog.Pages private Window CreateSampleWindow() { - var window = new Window(); - window.Height = 200; - window.Width = 200; - window.Content = new TextBlock { Text = "Hello world!" }; - window.WindowStartupLocation = WindowStartupLocation.CenterOwner; + Button button; + + var window = new Window + { + Height = 200, + Width = 200, + Content = new StackPanel + { + Spacing = 4, + Children = + { + new TextBlock { Text = "Hello world!" }, + (button = new Button + { + HorizontalAlignment = HorizontalAlignment.Center, + Content = "Click to close" + }) + } + }, + WindowStartupLocation = WindowStartupLocation.CenterOwner + }; + + button.Click += (_, __) => window.Close(); return window; } diff --git a/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs b/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs index d770f4b211..78de681403 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs @@ -65,23 +65,10 @@ namespace Avalonia.Win32 return IntPtr.Zero; } - // Based on https://github.com/dotnet/wpf/blob/master/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Window.cs#L4270-L4337 - // We need to enable parent window before destroying child window to prevent OS from activating a random window behind us. - // This is described here: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enablewindow#remarks - // Our window closed callback will set enabled state to a correct value after child window gets destroyed. - // We need to verify if parent is still alive (perhaps it got destroyed somehow). - if (_parent != null && IsWindow(_parent._hwnd)) - { - var wasActive = GetActiveWindow() == _hwnd; - - _parent.SetEnabled(true); + BeforeCloseCleanup(false); - // We also need to activate our parent window since again OS might try to activate a window behind if it is not set. - if (wasActive) - { - SetActiveWindow(_parent._hwnd); - } - } + // Used to distinguish between programmatic and regular close requests. + _isCloseRequested = true; break; } diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index c603128a18..2483356e9a 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -83,6 +83,7 @@ namespace Avalonia.Win32 private POINT _maxTrackSize; private WindowImpl _parent; private ExtendClientAreaChromeHints _extendChromeHints = ExtendClientAreaChromeHints.Default; + private bool _isCloseRequested; public WindowImpl() { @@ -506,6 +507,13 @@ namespace Avalonia.Win32 if (_hwnd != IntPtr.Zero) { + // Detect if we are being closed programmatically - this would mean that WM_CLOSE was not called + // and we didn't prepare this window for destruction. + if (!_isCloseRequested) + { + BeforeCloseCleanup(true); + } + DestroyWindow(_hwnd); _hwnd = IntPtr.Zero; } @@ -948,6 +956,32 @@ namespace Avalonia.Win32 SetFocus(_hwnd); } } + + private void BeforeCloseCleanup(bool isDisposing) + { + // Based on https://github.com/dotnet/wpf/blob/master/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Window.cs#L4270-L4337 + // We need to enable parent window before destroying child window to prevent OS from activating a random window behind us (or last active window). + // This is described here: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enablewindow#remarks + // We need to verify if parent is still alive (perhaps it got destroyed somehow). + if (_parent != null && IsWindow(_parent._hwnd)) + { + var wasActive = GetActiveWindow() == _hwnd; + + // We can only set enabled state if we are not disposing - generally Dispose happens after enabled state has been set. + // Ignoring this would cause us to enable a window that might be disabled. + if (!isDisposing) + { + // Our window closed callback will set enabled state to a correct value after child window gets destroyed. + _parent.SetEnabled(true); + } + + // We also need to activate our parent window since again OS might try to activate a window behind if it is not set. + if (wasActive) + { + SetActiveWindow(_parent._hwnd); + } + } + } private void MaximizeWithoutCoveringTaskbar() { From 1428badc6b17c1922151d31af42d34d9d875ae97 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Tue, 17 Nov 2020 01:46:22 +0300 Subject: [PATCH 029/100] Fixed CPP header codegen --- src/tools/MicroComGenerator/CppGen.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/tools/MicroComGenerator/CppGen.cs b/src/tools/MicroComGenerator/CppGen.cs index 68192ebffe..b053088ca9 100644 --- a/src/tools/MicroComGenerator/CppGen.cs +++ b/src/tools/MicroComGenerator/CppGen.cs @@ -14,7 +14,10 @@ namespace MicroComGenerator name = "unsigned char"; else if(name == "uint") name = "unsigned int"; - return name + new string('*', type.PointerLevel); + + type = type.Clone(); + type.Name = name; + return type.Format(); } public static string GenerateCpp(AstIdlNode idl) From 91791c7d15db039be527936375cef79278d59732 Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Tue, 17 Nov 2020 07:54:16 +0800 Subject: [PATCH 030/100] Implement RadiusX/Y and center, just like in WPF. --- src/Avalonia.Visuals/Media/EllipseGeometry.cs | 56 ++++++++++++++++++- 1 file changed, 54 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Visuals/Media/EllipseGeometry.cs b/src/Avalonia.Visuals/Media/EllipseGeometry.cs index fd73eee69a..bae6dc3d8c 100644 --- a/src/Avalonia.Visuals/Media/EllipseGeometry.cs +++ b/src/Avalonia.Visuals/Media/EllipseGeometry.cs @@ -12,10 +12,28 @@ namespace Avalonia.Media /// public static readonly StyledProperty RectProperty = AvaloniaProperty.Register(nameof(Rect)); + + /// + /// Defines the property. + /// + public static readonly StyledProperty RadiusXProperty = + AvaloniaProperty.Register(nameof(RadiusX)); + + /// + /// Defines the property. + /// + public static readonly StyledProperty RadiusYProperty = + AvaloniaProperty.Register(nameof(RadiusY)); + + /// + /// Defines the property. + /// + public static readonly StyledProperty CenterProperty = + AvaloniaProperty.Register(nameof(Center)); static EllipseGeometry() { - AffectsGeometry(RectProperty); + AffectsGeometry(RectProperty, RadiusXProperty, RadiusYProperty, CenterProperty); } /// @@ -43,6 +61,33 @@ namespace Avalonia.Media set => SetValue(RectProperty, value); } + /// + /// Gets or sets a double that defines the radius in the X-axis of the ellipse. + /// + public double RadiusX + { + get => GetValue(RadiusXProperty); + set => SetValue(RadiusXProperty, value); + } + + /// + /// Gets or sets a double that defines the radius in the Y-axis of the ellipse. + /// + public double RadiusY + { + get => GetValue(RadiusYProperty); + set => SetValue(RadiusYProperty, value); + } + + /// + /// Gets or sets a point that defines the center of the ellipse. + /// + public Point Center + { + get => GetValue(CenterProperty); + set => SetValue(CenterProperty, value); + } + /// public override Geometry Clone() { @@ -54,7 +99,14 @@ namespace Avalonia.Media { var factory = AvaloniaLocator.Current.GetService(); - return factory.CreateEllipseGeometry(Rect); + if (Rect != default) return factory.CreateEllipseGeometry(Rect); + + var originX = Center.X - RadiusX; + var originY = Center.Y - RadiusY; + var width = RadiusX * 2; + var height = RadiusY * 2; + + return factory.CreateEllipseGeometry(new Rect(originX, originY, width, height)); } } } From e90a5d285c7bebf478a97b4c357681a049702250 Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Tue, 17 Nov 2020 16:54:08 +0800 Subject: [PATCH 031/100] add change listener on static ctor --- src/Avalonia.Visuals/Media/PathFigure.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Visuals/Media/PathFigure.cs b/src/Avalonia.Visuals/Media/PathFigure.cs index ff63a6286c..a9e3c6d628 100644 --- a/src/Avalonia.Visuals/Media/PathFigure.cs +++ b/src/Avalonia.Visuals/Media/PathFigure.cs @@ -38,11 +38,14 @@ namespace Avalonia.Media /// Initializes a new instance of the class. /// public PathFigure() + { + Segments = new PathSegments(); + } + + static PathFigure() { SegmentsProperty.Changed.AddClassHandler((s, e) => s.OnSegmentsChanged(e.NewValue as PathSegments)); - - Segments = new PathSegments(); } private void OnSegmentsChanged(PathSegments? arg2NewValue) From 7df1d2ad4d07f30fe41aaf8e477c1be8832f7de4 Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Tue, 17 Nov 2020 16:58:53 +0800 Subject: [PATCH 032/100] add test, thanks @donandren ! :D --- .../Media/PathSegmentTests.cs | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 tests/Avalonia.Visuals.UnitTests/Media/PathSegmentTests.cs diff --git a/tests/Avalonia.Visuals.UnitTests/Media/PathSegmentTests.cs b/tests/Avalonia.Visuals.UnitTests/Media/PathSegmentTests.cs new file mode 100644 index 0000000000..0737b4dc88 --- /dev/null +++ b/tests/Avalonia.Visuals.UnitTests/Media/PathSegmentTests.cs @@ -0,0 +1,34 @@ +using Avalonia.Media; +using Xunit; + +namespace Avalonia.Visuals.UnitTests.Media +{ + public class PathSegmentTests + { + [Fact] + public void PathSegment_Triggers_Invalidation_On_Property_Change() + { + var targetSegment = new ArcSegment() + { + Size = new Size(10, 10), + Point = new Point(5, 5) + }; + + var target = new PathGeometry + { + Figures = new PathFigures + { + new PathFigure { IsClosed = false, Segments = new PathSegments { targetSegment } } + } + }; + + var changed = false; + + target.Changed += (s, e) => changed = true; + + targetSegment.Size = new Size(20, 20); + + Assert.True(changed); + } + } +} From 4e180c88cdb5f4e22e177f41a83010165e7836fb Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Tue, 17 Nov 2020 16:59:43 +0800 Subject: [PATCH 033/100] remove old test --- .../Avalonia.RenderTests/Shapes/PathTests.cs | 40 ------------------- 1 file changed, 40 deletions(-) diff --git a/tests/Avalonia.RenderTests/Shapes/PathTests.cs b/tests/Avalonia.RenderTests/Shapes/PathTests.cs index 24e420d972..bac16cca88 100644 --- a/tests/Avalonia.RenderTests/Shapes/PathTests.cs +++ b/tests/Avalonia.RenderTests/Shapes/PathTests.cs @@ -353,47 +353,7 @@ namespace Avalonia.Direct2D1.RenderTests.Shapes await RenderToFile(target); CompareImages(); } - - [Fact] - public async Task PathSegment_Triggers_Invalidation_On_Property_Change() - { - var targetSegment = new ArcSegment() - { - Size = new Size(10,10), - Point = new Point(5,5) - }; - - var targetPath = new Path - { - VerticalAlignment = VerticalAlignment.Center, - HorizontalAlignment = HorizontalAlignment.Center, - Fill = Brushes.Red, - Data = new PathGeometry - { - Figures = new PathFigures - { - new PathFigure { IsClosed = false, Segments = new PathSegments { targetSegment } } - } - } - }; - - var root = new Border - { - Width = 100, - Height = 100, - Background = Brushes.White, - Child =targetPath - }; - - Assert.Equal(10, targetPath.Bounds.Height); - Assert.Equal(10, targetPath.Bounds.Width); - - targetSegment.Size = new Size(20, 20); - Assert.Equal(20, targetPath.Bounds.Height); - Assert.Equal(20, targetPath.Bounds.Width); - } - [Fact] public async Task Path_With_Rotated_Geometry() { From ea90f05f83bea42dd47892f76d5de5012ff6afba Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 17 Nov 2020 14:34:44 +0100 Subject: [PATCH 034/100] Added Window.ShowActive for win32. --- .../Platform/IWindowBaseImpl.cs | 4 ++-- src/Avalonia.Controls/Window.cs | 20 ++++++++++++++++--- src/Avalonia.Controls/WindowBase.cs | 2 +- .../Remote/PreviewerWindowImpl.cs | 2 +- src/Avalonia.DesignerSupport/Remote/Stubs.cs | 2 +- src/Avalonia.Headless/HeadlessWindowImpl.cs | 7 ++++--- src/Avalonia.X11/X11Window.cs | 2 +- src/Windows/Avalonia.Win32/PopupImpl.cs | 3 ++- src/Windows/Avalonia.Win32/Win32Platform.cs | 2 +- src/Windows/Avalonia.Win32/WindowImpl.cs | 14 +++++++------ .../ContextMenuTests.cs | 16 +++++++-------- .../WindowBaseTests.cs | 2 +- .../MockWindowingPlatform.cs | 2 +- 13 files changed, 48 insertions(+), 30 deletions(-) diff --git a/src/Avalonia.Controls/Platform/IWindowBaseImpl.cs b/src/Avalonia.Controls/Platform/IWindowBaseImpl.cs index ecaf87d1ed..0d303a6666 100644 --- a/src/Avalonia.Controls/Platform/IWindowBaseImpl.cs +++ b/src/Avalonia.Controls/Platform/IWindowBaseImpl.cs @@ -5,9 +5,9 @@ namespace Avalonia.Platform public interface IWindowBaseImpl : ITopLevelImpl { /// - /// Shows the top level. + /// Shows the window. /// - void Show(); + void Show(bool activate); /// /// Hides the window. diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs index 54574a7e1c..ab99cfad17 100644 --- a/src/Avalonia.Controls/Window.cs +++ b/src/Avalonia.Controls/Window.cs @@ -129,6 +129,12 @@ namespace Avalonia.Controls public static readonly StyledProperty SystemDecorationsProperty = AvaloniaProperty.Register(nameof(SystemDecorations), SystemDecorations.Full); + /// + /// Defines the property. + /// + public static readonly StyledProperty ShowActivatedProperty = + AvaloniaProperty.Register(nameof(ShowActivated), true); + /// /// Enables or disables the taskbar icon /// @@ -352,13 +358,21 @@ namespace Avalonia.Controls /// /// Sets the system decorations (title bar, border, etc) /// - /// public SystemDecorations SystemDecorations { get { return GetValue(SystemDecorationsProperty); } set { SetValue(SystemDecorationsProperty, value); } } + /// + /// Gets or sets a value that indicates whether a window is activated when first shown. + /// + public bool ShowActivated + { + get { return GetValue(ShowActivatedProperty); } + set { SetValue(ShowActivatedProperty, value); } + } + /// /// Enables or disables the taskbar icon /// @@ -650,7 +664,7 @@ namespace Avalonia.Controls Owner = parent; parent?.AddChild(this, false); - PlatformImpl?.Show(); + PlatformImpl?.Show(ShowActivated); Renderer?.Start(); SetWindowStartupLocation(Owner?.PlatformImpl); } @@ -720,7 +734,7 @@ namespace Avalonia.Controls PlatformImpl?.SetParent(owner.PlatformImpl); Owner = owner; owner.AddChild(this, true); - PlatformImpl?.Show(); + PlatformImpl?.Show(ShowActivated); Renderer?.Start(); diff --git a/src/Avalonia.Controls/WindowBase.cs b/src/Avalonia.Controls/WindowBase.cs index 1efd6c8c1d..cdcb499e98 100644 --- a/src/Avalonia.Controls/WindowBase.cs +++ b/src/Avalonia.Controls/WindowBase.cs @@ -162,7 +162,7 @@ namespace Avalonia.Controls LayoutManager.ExecuteInitialLayoutPass(); _hasExecutedInitialLayoutPass = true; } - PlatformImpl?.Show(); + PlatformImpl?.Show(true); Renderer?.Start(); OnOpened(EventArgs.Empty); } diff --git a/src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs b/src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs index 25c26be91e..787f44887f 100644 --- a/src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs +++ b/src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs @@ -20,7 +20,7 @@ namespace Avalonia.DesignerSupport.Remote ClientSize = new Size(1, 1); } - public void Show() + public void Show(bool activate) { } diff --git a/src/Avalonia.DesignerSupport/Remote/Stubs.cs b/src/Avalonia.DesignerSupport/Remote/Stubs.cs index f377b1bcd1..f6783dc0b7 100644 --- a/src/Avalonia.DesignerSupport/Remote/Stubs.cs +++ b/src/Avalonia.DesignerSupport/Remote/Stubs.cs @@ -77,7 +77,7 @@ namespace Avalonia.DesignerSupport.Remote { } - public void Show() + public void Show(bool activate) { } diff --git a/src/Avalonia.Headless/HeadlessWindowImpl.cs b/src/Avalonia.Headless/HeadlessWindowImpl.cs index 8f4fa5e304..3a1f3bdaf7 100644 --- a/src/Avalonia.Headless/HeadlessWindowImpl.cs +++ b/src/Avalonia.Headless/HeadlessWindowImpl.cs @@ -75,9 +75,10 @@ namespace Avalonia.Headless public Action Closed { get; set; } public IMouseDevice MouseDevice { get; } - public void Show() + public void Show(bool activate) { - Dispatcher.UIThread.Post(() => Activated?.Invoke(), DispatcherPriority.Input); + if (activate) + Dispatcher.UIThread.Post(() => Activated?.Invoke(), DispatcherPriority.Input); } public void Hide() @@ -148,7 +149,7 @@ namespace Avalonia.Headless public void ShowDialog(IWindowImpl parent) { - Show(); + Show(true); } public void SetSystemDecorations(bool enabled) diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs index 2cd3b973d8..d62e3bed23 100644 --- a/src/Avalonia.X11/X11Window.cs +++ b/src/Avalonia.X11/X11Window.cs @@ -815,7 +815,7 @@ namespace Avalonia.X11 XSetTransientForHint(_x11.Display, _handle, parent.Handle.Handle); } - public void Show() + public void Show(bool activate) { _wasMappedAtLeastOnce = true; XMapWindow(_x11.Display, _handle); diff --git a/src/Windows/Avalonia.Win32/PopupImpl.cs b/src/Windows/Avalonia.Win32/PopupImpl.cs index 7fb146899b..dd3fd1342c 100644 --- a/src/Windows/Avalonia.Win32/PopupImpl.cs +++ b/src/Windows/Avalonia.Win32/PopupImpl.cs @@ -17,8 +17,9 @@ namespace Avalonia.Win32 [ThreadStatic] private static IntPtr s_parentHandle; - public override void Show() + public override void Show(bool activate) { + // Popups are always shown non-activated. UnmanagedMethods.ShowWindow(Handle.Handle, UnmanagedMethods.ShowWindowCommand.ShowNoActivate); // We need to steal focus if it's held by a child window of our toplevel window diff --git a/src/Windows/Avalonia.Win32/Win32Platform.cs b/src/Windows/Avalonia.Win32/Win32Platform.cs index 5b16cae26e..af9531400e 100644 --- a/src/Windows/Avalonia.Win32/Win32Platform.cs +++ b/src/Windows/Avalonia.Win32/Win32Platform.cs @@ -229,7 +229,7 @@ namespace Avalonia.Win32 public IWindowImpl CreateEmbeddableWindow() { var embedded = new EmbeddedWindowImpl(); - embedded.Show(); + embedded.Show(true); return embedded; } diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 715c8fc01d..f594763413 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -240,7 +240,7 @@ namespace Avalonia.Win32 { if (IsWindowVisible(_hwnd)) { - ShowWindow(value); + ShowWindow(value, true); } else { @@ -550,10 +550,11 @@ namespace Avalonia.Win32 UnmanagedMethods.ShowWindow(_hwnd, ShowWindowCommand.Hide); } - public virtual void Show() + public virtual void Show(bool activate) { SetWindowLongPtr(_hwnd, (int)WindowLongParam.GWL_HWNDPARENT, _parent != null ? _parent._hwnd : IntPtr.Zero); - ShowWindow(_showWindowState); + + ShowWindow(_showWindowState, activate); } public Action GotInputWhenDisabled { get; set; } @@ -891,7 +892,7 @@ namespace Avalonia.Win32 ExtendClientAreaToDecorationsChanged?.Invoke(_isClientAreaExtended); } - private void ShowWindow(WindowState state) + private void ShowWindow(WindowState state, bool activate) { ShowWindowCommand? command; @@ -901,7 +902,7 @@ namespace Avalonia.Win32 { case WindowState.Minimized: newWindowProperties.IsFullScreen = false; - command = ShowWindowCommand.Minimize; + command = activate ? ShowWindowCommand.Minimize : ShowWindowCommand.ShowMinNoActive; break; case WindowState.Maximized: newWindowProperties.IsFullScreen = false; @@ -910,7 +911,8 @@ namespace Avalonia.Win32 case WindowState.Normal: newWindowProperties.IsFullScreen = false; - command = ShowWindowCommand.Restore; + command = IsWindowVisible(_hwnd) ? ShowWindowCommand.Restore : + activate ? ShowWindowCommand.Normal : ShowWindowCommand.ShowNoActivate; break; case WindowState.FullScreen: diff --git a/tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs b/tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs index c179aef9ac..39a3250686 100644 --- a/tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs @@ -79,7 +79,7 @@ namespace Avalonia.Controls.UnitTests { using (Application()) { - popupImpl.Setup(x => x.Show()).Verifiable(); + popupImpl.Setup(x => x.Show(true)).Verifiable(); popupImpl.Setup(x => x.Hide()).Verifiable(); var sut = new ContextMenu(); @@ -99,7 +99,7 @@ namespace Avalonia.Controls.UnitTests _mouse.Click(target); Assert.False(sut.IsOpen); - popupImpl.Verify(x => x.Show(), Times.Once); + popupImpl.Verify(x => x.Show(true), Times.Once); popupImpl.Verify(x => x.Hide(), Times.Once); } } @@ -109,7 +109,7 @@ namespace Avalonia.Controls.UnitTests { using (Application()) { - popupImpl.Setup(x => x.Show()).Verifiable(); + popupImpl.Setup(x => x.Show(true)).Verifiable(); popupImpl.Setup(x => x.Hide()).Verifiable(); var sut = new ContextMenu(); @@ -130,7 +130,7 @@ namespace Avalonia.Controls.UnitTests Assert.True(sut.IsOpen); popupImpl.Verify(x => x.Hide(), Times.Once); - popupImpl.Verify(x => x.Show(), Times.Exactly(2)); + popupImpl.Verify(x => x.Show(true), Times.Exactly(2)); } } @@ -177,7 +177,7 @@ namespace Avalonia.Controls.UnitTests { using (Application()) { - popupImpl.Setup(x => x.Show()).Verifiable(); + popupImpl.Setup(x => x.Show(true)).Verifiable(); bool eventCalled = false; var sut = new ContextMenu(); @@ -193,7 +193,7 @@ namespace Avalonia.Controls.UnitTests Assert.True(eventCalled); Assert.False(sut.IsOpen); - popupImpl.Verify(x => x.Show(), Times.Never); + popupImpl.Verify(x => x.Show(true), Times.Never); } } @@ -297,7 +297,7 @@ namespace Avalonia.Controls.UnitTests { using (Application()) { - popupImpl.Setup(x => x.Show()).Verifiable(); + popupImpl.Setup(x => x.Show(true)).Verifiable(); popupImpl.Setup(x => x.Hide()).Verifiable(); bool eventCalled = false; @@ -321,7 +321,7 @@ namespace Avalonia.Controls.UnitTests Assert.True(eventCalled); Assert.True(sut.IsOpen); - popupImpl.Verify(x => x.Show(), Times.Once()); + popupImpl.Verify(x => x.Show(true), Times.Once()); popupImpl.Verify(x => x.Hide(), Times.Never); } } diff --git a/tests/Avalonia.Controls.UnitTests/WindowBaseTests.cs b/tests/Avalonia.Controls.UnitTests/WindowBaseTests.cs index 84f212d1b3..8109b037c5 100644 --- a/tests/Avalonia.Controls.UnitTests/WindowBaseTests.cs +++ b/tests/Avalonia.Controls.UnitTests/WindowBaseTests.cs @@ -137,7 +137,7 @@ namespace Avalonia.Controls.UnitTests var target = new TestWindowBase(windowImpl.Object); target.IsVisible = true; - windowImpl.Verify(x => x.Show()); + windowImpl.Verify(x => x.Show(true)); } } diff --git a/tests/Avalonia.UnitTests/MockWindowingPlatform.cs b/tests/Avalonia.UnitTests/MockWindowingPlatform.cs index 9ec8ca2d36..8a24a8366f 100644 --- a/tests/Avalonia.UnitTests/MockWindowingPlatform.cs +++ b/tests/Avalonia.UnitTests/MockWindowingPlatform.cs @@ -58,7 +58,7 @@ namespace Avalonia.UnitTests windowImpl.Object.Resized?.Invoke(clientSize); }); - windowImpl.Setup(x => x.Show()).Callback(() => + windowImpl.Setup(x => x.Show(true)).Callback(() => { windowImpl.Object.Activated?.Invoke(); }); From 2b9e092b32757f32b028288aac2934027b5ce924 Mon Sep 17 00:00:00 2001 From: capdj <42602325+capdj@users.noreply.github.com> Date: Tue, 17 Nov 2020 15:33:45 +0000 Subject: [PATCH 035/100] Fix X11 dropping modifier keys Fixes #4988 --- src/Avalonia.X11/XI2Manager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.X11/XI2Manager.cs b/src/Avalonia.X11/XI2Manager.cs index b3a24e6c37..8cdf24cc7b 100644 --- a/src/Avalonia.X11/XI2Manager.cs +++ b/src/Avalonia.X11/XI2Manager.cs @@ -351,7 +351,7 @@ namespace Avalonia.X11 if (state.HasFlag(XModifierMask.Mod4Mask)) Modifiers |= RawInputModifiers.Meta; - Modifiers = ParseButtonState(ev->buttons.MaskLen, ev->buttons.Mask); + Modifiers |= ParseButtonState(ev->buttons.MaskLen, ev->buttons.Mask); Valuators = new Dictionary(); Position = new Point(ev->event_x, ev->event_y); From 053537721a65632346ccd55441fdabdcb2e30ef1 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Tue, 17 Nov 2020 16:49:55 +0100 Subject: [PATCH 036/100] Parse Color and GridLength during compilation. Improve error handling for failed parsing. --- .../Avalonia.Build.Tasks.csproj | 11 +- src/Avalonia.Build.Tasks/SpanCompat.cs | 4 + src/Avalonia.Controls/GridLength.cs | 10 +- src/Avalonia.Visuals/Media/Color.cs | 14 +- src/Avalonia.Visuals/Media/KnownColors.cs | 9 ++ .../AvaloniaXamlIlGridLengthAstNode.cs | 34 +++++ .../AvaloniaXamlIlLanguage.cs | 135 ++++++++++++++---- .../AvaloniaXamlIlWellKnownTypes.cs | 9 ++ 8 files changed, 192 insertions(+), 34 deletions(-) create mode 100644 src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AstNodes/AvaloniaXamlIlGridLengthAstNode.cs diff --git a/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj b/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj index bfac1ac1e1..90f6abc873 100644 --- a/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj +++ b/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj @@ -1,7 +1,7 @@  netstandard2.0 - netstandard2.0;netcoreapp2.0 + netstandard2.0;netcoreapp3.1 exe false tools @@ -81,6 +81,15 @@ Markup/%(RecursiveDir)%(FileName)%(Extension) + + Markup/%(RecursiveDir)%(FileName)%(Extension) + + + Markup/%(RecursiveDir)%(FileName)%(Extension) + + + Markup/%(RecursiveDir)%(FileName)%(Extension) + diff --git a/src/Avalonia.Build.Tasks/SpanCompat.cs b/src/Avalonia.Build.Tasks/SpanCompat.cs index d5c406293e..f8960f56ec 100644 --- a/src/Avalonia.Build.Tasks/SpanCompat.cs +++ b/src/Avalonia.Build.Tasks/SpanCompat.cs @@ -1,3 +1,4 @@ +#if !NETCOREAPP3_1 namespace System { // This is a hack to enable our span code to work inside MSBuild task without referencing System.Memory @@ -63,6 +64,8 @@ namespace System } public override string ToString() => _length == 0 ? string.Empty : _s.Substring(_start, _length); + + public static implicit operator ReadOnlySpan(char[] arr) => new ReadOnlySpan(new string(arr)); } static class SpanCompatExtensions @@ -71,3 +74,4 @@ namespace System } } +#endif diff --git a/src/Avalonia.Controls/GridLength.cs b/src/Avalonia.Controls/GridLength.cs index 57f308d59f..b8418949d9 100644 --- a/src/Avalonia.Controls/GridLength.cs +++ b/src/Avalonia.Controls/GridLength.cs @@ -8,7 +8,10 @@ namespace Avalonia.Controls /// /// Defines the valid units for a . /// - public enum GridUnitType +#if !BUILDTASK + public +#endif + enum GridUnitType { /// /// The row or column is auto-sized to fit its content. @@ -29,7 +32,10 @@ namespace Avalonia.Controls /// /// Holds the width or height of a 's column and row definitions. /// - public struct GridLength : IEquatable +#if !BUILDTASK + public +#endif + struct GridLength : IEquatable { private readonly GridUnitType _type; diff --git a/src/Avalonia.Visuals/Media/Color.cs b/src/Avalonia.Visuals/Media/Color.cs index 16b4f90d57..a57a962db4 100644 --- a/src/Avalonia.Visuals/Media/Color.cs +++ b/src/Avalonia.Visuals/Media/Color.cs @@ -1,17 +1,24 @@ using System; using System.Globalization; +#if !BUILDTASK using Avalonia.Animation.Animators; +#endif namespace Avalonia.Media { /// /// An ARGB color. /// - public readonly struct Color : IEquatable +#if !BUILDTASK + public +#endif + readonly struct Color : IEquatable { static Color() { +#if !BUILDTASK Animation.Animation.RegisterAnimator(prop => typeof(Color).IsAssignableFrom(prop.PropertyType)); +#endif } /// @@ -223,7 +230,12 @@ namespace Avalonia.Media if (input.Length == 3 || input.Length == 4) { var extendedLength = 2 * input.Length; + +#if !BUILDTASK Span extended = stackalloc char[extendedLength]; +#else + char[] extended = new char[extendedLength]; +#endif for (int i = 0; i < input.Length; i++) { diff --git a/src/Avalonia.Visuals/Media/KnownColors.cs b/src/Avalonia.Visuals/Media/KnownColors.cs index 0887d2c913..fe09f5f538 100644 --- a/src/Avalonia.Visuals/Media/KnownColors.cs +++ b/src/Avalonia.Visuals/Media/KnownColors.cs @@ -8,7 +8,9 @@ namespace Avalonia.Media { private static readonly IReadOnlyDictionary _knownColorNames; private static readonly IReadOnlyDictionary _knownColors; +#if !BUILDTASK private static readonly Dictionary _knownBrushes; +#endif static KnownColors() { @@ -32,14 +34,19 @@ namespace Avalonia.Media _knownColorNames = knownColorNames; _knownColors = knownColors; + +#if !BUILDTASK _knownBrushes = new Dictionary(); +#endif } +#if !BUILDTASK public static ISolidColorBrush GetKnownBrush(string s) { var color = GetKnownColor(s); return color != KnownColor.None ? color.ToBrush() : null; } +#endif public static KnownColor GetKnownColor(string s) { @@ -61,6 +68,7 @@ namespace Avalonia.Media return Color.FromUInt32((uint)color); } +#if !BUILDTASK public static ISolidColorBrush ToBrush(this KnownColor color) { lock (_knownBrushes) @@ -74,6 +82,7 @@ namespace Avalonia.Media return brush; } } +#endif } internal enum KnownColor : uint diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AstNodes/AvaloniaXamlIlGridLengthAstNode.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AstNodes/AvaloniaXamlIlGridLengthAstNode.cs new file mode 100644 index 0000000000..218c49512c --- /dev/null +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AstNodes/AvaloniaXamlIlGridLengthAstNode.cs @@ -0,0 +1,34 @@ +using Avalonia.Controls; +using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers; +using XamlX.Ast; +using XamlX.Emit; +using XamlX.IL; + +namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.AstNodes +{ + class AvaloniaXamlIlGridLengthAstNode : XamlAstNode, IXamlAstValueNode, IXamlAstILEmitableNode + { + private readonly AvaloniaXamlIlWellKnownTypes _types; + private readonly GridLength _gridLength; + + public AvaloniaXamlIlGridLengthAstNode(IXamlLineInfo lineInfo, AvaloniaXamlIlWellKnownTypes types, GridLength gridLength) : base(lineInfo) + { + _types = types; + _gridLength = gridLength; + + Type = new XamlAstClrTypeReference(lineInfo, types.GridLength, false); + } + + public IXamlAstTypeReference Type { get; } + + public XamlILNodeEmitResult Emit(XamlEmitContext context, IXamlILEmitter codeGen) + { + codeGen + .Ldc_R8(_gridLength.Value) + .Ldc_I4((int)_gridLength.GridUnitType) + .Newobj(_types.GridLengthConstructorValueType); + + return XamlILNodeEmitResult.Type(0, Type.GetClrType()); + } + } +} diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguage.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguage.cs index c3d9534828..87b82c112e 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguage.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguage.cs @@ -2,8 +2,10 @@ using System; using System.Collections.Generic; using System.Globalization; using System.Linq; +using Avalonia.Controls; using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.AstNodes; using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers; +using Avalonia.Media; using XamlX; using XamlX.Ast; using XamlX.Emit; @@ -205,67 +207,140 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions result = new AvaloniaXamlIlFontFamilyAstNode(types, text, node); return true; } - + if (type.Equals(types.Thickness)) { - var thickness = Thickness.Parse(text); - - result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Thickness, types.ThicknessFullConstructor, - new[] { thickness.Left, thickness.Top, thickness.Right, thickness.Bottom }); + try + { + var thickness = Thickness.Parse(text); + + result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Thickness, types.ThicknessFullConstructor, + new[] { thickness.Left, thickness.Top, thickness.Right, thickness.Bottom }); - return true; + return true; + } + catch + { + throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a thickness", node); + } } if (type.Equals(types.Point)) { - var point = Point.Parse(text); - - result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Point, types.PointFullConstructor, - new[] { point.X, point.Y }); + try + { + var point = Point.Parse(text); + + result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Point, types.PointFullConstructor, + new[] { point.X, point.Y }); - return true; + return true; + } + catch + { + throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a point", node); + } } if (type.Equals(types.Vector)) { - var vector = Vector.Parse(text); - - result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Vector, types.VectorFullConstructor, - new[] { vector.X, vector.Y }); + try + { + var vector = Vector.Parse(text); + + result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Vector, types.VectorFullConstructor, + new[] { vector.X, vector.Y }); - return true; + return true; + } + catch + { + throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a vector", node); + } } if (type.Equals(types.Size)) { - var size = Size.Parse(text); - - result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Size, types.SizeFullConstructor, - new[] { size.Width, size.Height }); + try + { + var size = Size.Parse(text); - return true; + result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Size, types.SizeFullConstructor, + new[] { size.Width, size.Height }); + + return true; + } + catch + { + throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a size", node); + } } if (type.Equals(types.Matrix)) { - var matrix = Matrix.Parse(text); - - result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Matrix, types.MatrixFullConstructor, - new[] { matrix.M11, matrix.M12, matrix.M21, matrix.M22, matrix.M31, matrix.M32 }); + try + { + var matrix = Matrix.Parse(text); + + result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Matrix, types.MatrixFullConstructor, + new[] { matrix.M11, matrix.M12, matrix.M21, matrix.M22, matrix.M31, matrix.M32 }); - return true; + return true; + } + catch + { + throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a matrix", node); + } } if (type.Equals(types.CornerRadius)) { - var cornerRadius = CornerRadius.Parse(text); - - result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.CornerRadius, types.CornerRadiusFullConstructor, - new[] { cornerRadius.TopLeft, cornerRadius.TopRight, cornerRadius.BottomRight, cornerRadius.BottomLeft }); + try + { + var cornerRadius = CornerRadius.Parse(text); + + result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.CornerRadius, types.CornerRadiusFullConstructor, + new[] { cornerRadius.TopLeft, cornerRadius.TopRight, cornerRadius.BottomRight, cornerRadius.BottomLeft }); + return true; + } + catch + { + throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a corner radius", node); + } + } + + if (type.Equals(types.Color)) + { + if (!Color.TryParse(text, out Color color)) + { + throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a color", node); + } + + result = new XamlStaticOrTargetedReturnMethodCallNode(node, + type.GetMethod( + new FindMethodMethodSignature("FromUInt32", type, types.UInt) { IsStatic = true }), + new[] { new XamlConstantNode(node, types.UInt, color.ToUint32()) }); + return true; } + if (type.Equals(types.GridLength)) + { + try + { + var gridLength = GridLength.Parse(text); + + result = new AvaloniaXamlIlGridLengthAstNode(node, types, gridLength); + + return true; + } + catch + { + throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a grid length", node); + } + } + if (type.FullName == "Avalonia.AvaloniaProperty") { var scope = context.ParentNodes().OfType().FirstOrDefault(); diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs index 05b13b61d3..2a7e10d42e 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs @@ -49,6 +49,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers public IXamlType ReflectionBindingExtension { get; } public IXamlType RelativeSource { get; } + public IXamlType UInt { get; } public IXamlType Long { get; } public IXamlType Uri { get; } public IXamlType FontFamily { get; } @@ -65,6 +66,9 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers public IXamlConstructor MatrixFullConstructor { get; } public IXamlType CornerRadius { get; } public IXamlConstructor CornerRadiusFullConstructor { get; } + public IXamlType GridLength { get; } + public IXamlConstructor GridLengthConstructorValueType { get; } + public IXamlType Color { get; } public AvaloniaXamlIlWellKnownTypes(TransformerConfiguration cfg) { @@ -122,6 +126,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers ItemsRepeater = cfg.TypeSystem.GetType("Avalonia.Controls.ItemsRepeater"); ReflectionBindingExtension = cfg.TypeSystem.GetType("Avalonia.Markup.Xaml.MarkupExtensions.ReflectionBindingExtension"); RelativeSource = cfg.TypeSystem.GetType("Avalonia.Data.RelativeSource"); + UInt = cfg.TypeSystem.GetType("System.UInt32"); Long = cfg.TypeSystem.GetType("System.Int64"); Uri = cfg.TypeSystem.GetType("System.Uri"); FontFamily = cfg.TypeSystem.GetType("Avalonia.Media.FontFamily"); @@ -141,6 +146,10 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers (Size, SizeFullConstructor) = GetNumericTypeInfo("Avalonia.Size", XamlIlTypes.Double, 2); (Matrix, MatrixFullConstructor) = GetNumericTypeInfo("Avalonia.Matrix", XamlIlTypes.Double, 6); (CornerRadius, CornerRadiusFullConstructor) = GetNumericTypeInfo("Avalonia.CornerRadius", XamlIlTypes.Double, 4); + + GridLength = cfg.TypeSystem.GetType("Avalonia.Controls.GridLength"); + GridLengthConstructorValueType = GridLength.GetConstructor(new List { XamlIlTypes.Double, cfg.TypeSystem.GetType("Avalonia.Controls.GridUnitType") }); + Color = cfg.TypeSystem.GetType("Avalonia.Media.Color"); } } From ff9981cfbbf058bcd839b1e8bbbc98381f07eb13 Mon Sep 17 00:00:00 2001 From: Hank Grabowski Date: Tue, 17 Nov 2020 13:05:56 -0500 Subject: [PATCH 037/100] Update macOS build instructions dylib copy instructions --- Documentation/build.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Documentation/build.md b/Documentation/build.md index 2f59146a48..45ee980e0b 100644 --- a/Documentation/build.md +++ b/Documentation/build.md @@ -68,7 +68,15 @@ On macOS it is necessary to build and manually install the respective native lib ``` cd ~/Library/Developer/Xcode/DerivedData/Avalonia.Native.OSX-[guid]/Build/Products/Debug -cp libAvalonia.Native.OSX.dylib /usr/local/lib/libAvaloniaNative.dylib +cp libAvalonia.Native.OSX.dylib /usr/local/lib/libAvalonia.Native.OSX.dylib +``` + +For compilation we also need this library on the build path. From the Avalonia root project directory: + +``` +mkdir -p build/Products/Release +cp /usr/local/lib/libAvalonia.Native.OSX.dylib build/Products/Release + ``` ### Build and Run Avalonia From 823ab3a40ddd8f37bb3384f1a386d4f67ee13fc2 Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Wed, 18 Nov 2020 18:21:07 +0800 Subject: [PATCH 038/100] address review --- src/Avalonia.Visuals/Media/PathFigure.cs | 8 ++++---- src/Avalonia.Visuals/Media/PathGeometry.cs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Avalonia.Visuals/Media/PathFigure.cs b/src/Avalonia.Visuals/Media/PathFigure.cs index a9e3c6d628..311a70a098 100644 --- a/src/Avalonia.Visuals/Media/PathFigure.cs +++ b/src/Avalonia.Visuals/Media/PathFigure.cs @@ -30,9 +30,9 @@ namespace Avalonia.Media internal event EventHandler SegmentsInvalidated; - private IDisposable? _segmentsObserver; + private IDisposable _segmentsDisposable; - private IDisposable? _segmentsPropertiesObserver; + private IDisposable _segmentsPropertiesObserver; /// /// Initializes a new instance of the class. @@ -50,10 +50,10 @@ namespace Avalonia.Media private void OnSegmentsChanged(PathSegments? arg2NewValue) { - _segmentsObserver?.Dispose(); + _segmentsDisposable?.Dispose(); _segmentsPropertiesObserver?.Dispose(); - _segmentsObserver = _segments?.ForEachItem( + _segmentsDisposable = _segments?.ForEachItem( _ => InvalidateSegments(), _ => InvalidateSegments(), InvalidateSegments); diff --git a/src/Avalonia.Visuals/Media/PathGeometry.cs b/src/Avalonia.Visuals/Media/PathGeometry.cs index 819669d86e..3d11c19b7d 100644 --- a/src/Avalonia.Visuals/Media/PathGeometry.cs +++ b/src/Avalonia.Visuals/Media/PathGeometry.cs @@ -120,7 +120,7 @@ namespace Avalonia.Media } - public void InvalidateGeometryFromSegments(object _, EventArgs __) + private void InvalidateGeometryFromSegments(object _, EventArgs __) { InvalidateGeometry(); } From 90e6e072212b1fb5924ba7f21027ab9a8dd993ac Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Wed, 18 Nov 2020 18:23:28 +0800 Subject: [PATCH 039/100] address review --- src/Avalonia.Visuals/Media/PathFigure.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Visuals/Media/PathFigure.cs b/src/Avalonia.Visuals/Media/PathFigure.cs index 311a70a098..0c4cc592af 100644 --- a/src/Avalonia.Visuals/Media/PathFigure.cs +++ b/src/Avalonia.Visuals/Media/PathFigure.cs @@ -32,7 +32,7 @@ namespace Avalonia.Media private IDisposable _segmentsDisposable; - private IDisposable _segmentsPropertiesObserver; + private IDisposable _segmentsPropertiesDisposable; /// /// Initializes a new instance of the class. @@ -51,14 +51,14 @@ namespace Avalonia.Media private void OnSegmentsChanged(PathSegments? arg2NewValue) { _segmentsDisposable?.Dispose(); - _segmentsPropertiesObserver?.Dispose(); + _segmentsPropertiesDisposable?.Dispose(); _segmentsDisposable = _segments?.ForEachItem( _ => InvalidateSegments(), _ => InvalidateSegments(), InvalidateSegments); - _segmentsPropertiesObserver = _segments?.TrackItemPropertyChanged(_ => InvalidateSegments()); + _segmentsPropertiesDisposable = _segments?.TrackItemPropertyChanged(_ => InvalidateSegments()); } private void InvalidateSegments() From 538b723c2d25916bd1e1d2f0898a7923862d2281 Mon Sep 17 00:00:00 2001 From: Jumar Macato Date: Wed, 18 Nov 2020 20:53:07 +0800 Subject: [PATCH 040/100] address review --- src/Avalonia.Visuals/Media/PathFigure.cs | 28 ++++++++++++++---------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/Avalonia.Visuals/Media/PathFigure.cs b/src/Avalonia.Visuals/Media/PathFigure.cs index 0c4cc592af..2f8d11b06e 100644 --- a/src/Avalonia.Visuals/Media/PathFigure.cs +++ b/src/Avalonia.Visuals/Media/PathFigure.cs @@ -1,5 +1,5 @@ +#nullable enable using System; -using System.ComponentModel; using Avalonia.Collections; using Avalonia.Metadata; @@ -11,29 +11,35 @@ namespace Avalonia.Media /// Defines the property. /// public static readonly StyledProperty IsClosedProperty - = AvaloniaProperty.Register(nameof(IsClosed), true); + = AvaloniaProperty.Register(nameof(IsClosed), true); + /// /// Defines the property. /// public static readonly StyledProperty IsFilledProperty - = AvaloniaProperty.Register(nameof(IsFilled), true); + = AvaloniaProperty.Register(nameof(IsFilled), true); + /// /// Defines the property. /// public static readonly DirectProperty SegmentsProperty - = AvaloniaProperty.RegisterDirect(nameof(Segments), f => f.Segments, (f, s) => f.Segments = s); + = AvaloniaProperty.RegisterDirect(nameof(Segments), f => f.Segments, + (f, s) => f.Segments = s); + /// /// Defines the property. /// public static readonly StyledProperty StartPointProperty - = AvaloniaProperty.Register(nameof(StartPoint)); + = AvaloniaProperty.Register(nameof(StartPoint)); + + internal event EventHandler? SegmentsInvalidated; - internal event EventHandler SegmentsInvalidated; + private PathSegments? _segments; - private IDisposable _segmentsDisposable; + private IDisposable? _segmentsDisposable; + + private IDisposable? _segmentsPropertiesDisposable; - private IDisposable _segmentsPropertiesDisposable; - /// /// Initializes a new instance of the class. /// @@ -57,7 +63,7 @@ namespace Avalonia.Media _ => InvalidateSegments(), _ => InvalidateSegments(), InvalidateSegments); - + _segmentsPropertiesDisposable = _segments?.TrackItemPropertyChanged(_ => InvalidateSegments()); } @@ -127,8 +133,6 @@ namespace Avalonia.Media ctx.EndFigure(IsClosed); } - private PathSegments _segments; - public override string ToString() => $"M {StartPoint} {string.Join(" ", _segments)}{(IsClosed ? "Z" : "")}"; } From 1800ad01257216c30ae5c40b8567ab437a0432f2 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 18 Nov 2020 13:57:16 +0000 Subject: [PATCH 041/100] fix nullable warnings. --- src/Avalonia.Visuals/Media/PathFigure.cs | 29 +++++++++++++++--------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/src/Avalonia.Visuals/Media/PathFigure.cs b/src/Avalonia.Visuals/Media/PathFigure.cs index 2f8d11b06e..caf86cb234 100644 --- a/src/Avalonia.Visuals/Media/PathFigure.cs +++ b/src/Avalonia.Visuals/Media/PathFigure.cs @@ -1,5 +1,6 @@ #nullable enable using System; +using System.Linq; using Avalonia.Collections; using Avalonia.Metadata; @@ -22,8 +23,10 @@ namespace Avalonia.Media /// /// Defines the property. /// - public static readonly DirectProperty SegmentsProperty - = AvaloniaProperty.RegisterDirect(nameof(Segments), f => f.Segments, + public static readonly DirectProperty SegmentsProperty + = AvaloniaProperty.RegisterDirect( + nameof(Segments), + f => f.Segments, (f, s) => f.Segments = s); /// @@ -50,11 +53,12 @@ namespace Avalonia.Media static PathFigure() { - SegmentsProperty.Changed.AddClassHandler((s, e) => - s.OnSegmentsChanged(e.NewValue as PathSegments)); + SegmentsProperty.Changed.AddClassHandler( + (s, e) => + s.OnSegmentsChanged()); } - private void OnSegmentsChanged(PathSegments? arg2NewValue) + private void OnSegmentsChanged() { _segmentsDisposable?.Dispose(); _segmentsPropertiesDisposable?.Dispose(); @@ -103,7 +107,7 @@ namespace Avalonia.Media /// The segments. /// [Content] - public PathSegments Segments + public PathSegments? Segments { get { return _segments; } set { SetAndRaise(SegmentsProperty, ref _segments, value); } @@ -120,20 +124,23 @@ namespace Avalonia.Media get { return GetValue(StartPointProperty); } set { SetValue(StartPointProperty, value); } } + + public override string ToString() + => $"M {StartPoint} {string.Join(" ", _segments ?? Enumerable.Empty())}{(IsClosed ? "Z" : "")}"; internal void ApplyTo(StreamGeometryContext ctx) { ctx.BeginFigure(StartPoint, IsFilled); - foreach (var segment in Segments) + if (Segments != null) { - segment.ApplyTo(ctx); + foreach (var segment in Segments) + { + segment.ApplyTo(ctx); + } } ctx.EndFigure(IsClosed); } - - public override string ToString() - => $"M {StartPoint} {string.Join(" ", _segments)}{(IsClosed ? "Z" : "")}"; } } From dc698b3b1d404a92aebdf7183c13fe9769fcc116 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Thu, 19 Nov 2020 12:26:11 +0100 Subject: [PATCH 042/100] Reuse empty value store arrays. --- .../Utilities/AvaloniaPropertyValueStore.cs | 24 +++++++++++++++---- .../Layout/ControlsBenchmark.cs | 3 ++- .../Avalonia.Benchmarks/NullCursorFactory.cs | 14 +++++++++++ 3 files changed, 36 insertions(+), 5 deletions(-) create mode 100644 tests/Avalonia.Benchmarks/NullCursorFactory.cs diff --git a/src/Avalonia.Base/Utilities/AvaloniaPropertyValueStore.cs b/src/Avalonia.Base/Utilities/AvaloniaPropertyValueStore.cs index 0238446892..6e52b6770a 100644 --- a/src/Avalonia.Base/Utilities/AvaloniaPropertyValueStore.cs +++ b/src/Avalonia.Base/Utilities/AvaloniaPropertyValueStore.cs @@ -1,5 +1,8 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +#nullable enable namespace Avalonia.Utilities { @@ -9,12 +12,14 @@ namespace Avalonia.Utilities /// Stored value type. internal sealed class AvaloniaPropertyValueStore { + // The last item in the list is always int.MaxValue. + private static readonly Entry[] s_emptyEntries = { new Entry { PropertyId = int.MaxValue, Value = default! } }; + private Entry[] _entries; public AvaloniaPropertyValueStore() { - // The last item in the list is always int.MaxValue - _entries = new[] { new Entry { PropertyId = int.MaxValue, Value = default } }; + _entries = s_emptyEntries; } private (int, bool) TryFindEntry(int propertyId) @@ -86,7 +91,7 @@ namespace Avalonia.Utilities return (0, false); } - public bool TryGetValue(AvaloniaProperty property, out TValue value) + public bool TryGetValue(AvaloniaProperty property, [MaybeNull] out TValue value) { (int index, bool found) = TryFindEntry(property.Id); if (!found) @@ -132,7 +137,18 @@ namespace Avalonia.Utilities if (found) { - Entry[] entries = new Entry[_entries.Length - 1]; + var newLength = _entries.Length - 1; + + // Special case - one element left means that value store is empty so we can just reuse our "empty" array. + if (newLength == 1) + { + _entries = s_emptyEntries; + + return; + } + + var entries = new Entry[newLength]; + int ix = 0; for (int i = 0; i < _entries.Length; ++i) diff --git a/tests/Avalonia.Benchmarks/Layout/ControlsBenchmark.cs b/tests/Avalonia.Benchmarks/Layout/ControlsBenchmark.cs index 7170f6d7d4..3493dd0f53 100644 --- a/tests/Avalonia.Benchmarks/Layout/ControlsBenchmark.cs +++ b/tests/Avalonia.Benchmarks/Layout/ControlsBenchmark.cs @@ -17,7 +17,8 @@ namespace Avalonia.Benchmarks.Layout _app = UnitTestApplication.Start( TestServices.StyledWindow.With( renderInterface: new NullRenderingPlatform(), - threadingInterface: new NullThreadingPlatform())); + threadingInterface: new NullThreadingPlatform(), + standardCursorFactory: new NullCursorFactory())); _root = new TestRoot(true, null) { diff --git a/tests/Avalonia.Benchmarks/NullCursorFactory.cs b/tests/Avalonia.Benchmarks/NullCursorFactory.cs new file mode 100644 index 0000000000..012adce0f2 --- /dev/null +++ b/tests/Avalonia.Benchmarks/NullCursorFactory.cs @@ -0,0 +1,14 @@ +using System; +using Avalonia.Input; +using Avalonia.Platform; + +namespace Avalonia.Benchmarks +{ + internal class NullCursorFactory : IStandardCursorFactory + { + public IPlatformHandle GetCursor(StandardCursorType cursorType) + { + return new PlatformHandle(IntPtr.Zero, "null"); + } + } +} From 492534fc9a2239009baf7029b954b215c8526f33 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Thu, 19 Nov 2020 12:59:05 +0100 Subject: [PATCH 043/100] Parse cursor type during xaml compilation. --- .../CompilerExtensions/AvaloniaXamlIlLanguage.cs | 12 ++++++++++++ .../Transformers/AvaloniaXamlIlWellKnownTypes.cs | 6 ++++++ 2 files changed, 18 insertions(+) diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguage.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguage.cs index 87b82c112e..40cfd21a76 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguage.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguage.cs @@ -341,6 +341,18 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions } } + if (type.Equals(types.Cursor)) + { + if (TypeSystemHelpers.TryGetEnumValueNode(types.StandardCursorType, text, node, out var enumConstantNode)) + { + var cursorTypeRef = new XamlAstClrTypeReference(node, types.Cursor, false); + + result = new XamlAstNewClrObjectNode(node, cursorTypeRef, types.CursorTypeConstructor, new List { enumConstantNode }); + + return true; + } + } + if (type.FullName == "Avalonia.AvaloniaProperty") { var scope = context.ParentNodes().OfType().FirstOrDefault(); diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs index 2a7e10d42e..f046778429 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs @@ -69,6 +69,9 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers public IXamlType GridLength { get; } public IXamlConstructor GridLengthConstructorValueType { get; } public IXamlType Color { get; } + public IXamlType StandardCursorType { get; } + public IXamlType Cursor { get; } + public IXamlConstructor CursorTypeConstructor { get; } public AvaloniaXamlIlWellKnownTypes(TransformerConfiguration cfg) { @@ -150,6 +153,9 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers GridLength = cfg.TypeSystem.GetType("Avalonia.Controls.GridLength"); GridLengthConstructorValueType = GridLength.GetConstructor(new List { XamlIlTypes.Double, cfg.TypeSystem.GetType("Avalonia.Controls.GridUnitType") }); Color = cfg.TypeSystem.GetType("Avalonia.Media.Color"); + StandardCursorType = cfg.TypeSystem.GetType("Avalonia.Input.StandardCursorType"); + Cursor = cfg.TypeSystem.GetType("Avalonia.Input.Cursor"); + CursorTypeConstructor = Cursor.GetConstructor(new List { StandardCursorType }); } } From eb0389e95635d405b104fe50dd8e53b15182dfed Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 19 Nov 2020 18:51:30 +0000 Subject: [PATCH 044/100] remove toggleswitch minwidth. --- src/Avalonia.Themes.Default/ToggleSwitch.xaml | 2 +- src/Avalonia.Themes.Fluent/ToggleSwitch.xaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Themes.Default/ToggleSwitch.xaml b/src/Avalonia.Themes.Default/ToggleSwitch.xaml index 9d1c024eb9..f11f77df5a 100644 --- a/src/Avalonia.Themes.Default/ToggleSwitch.xaml +++ b/src/Avalonia.Themes.Default/ToggleSwitch.xaml @@ -5,7 +5,7 @@ 0,0,0,6 6 6 - 154 + 0 0 1 diff --git a/src/Avalonia.Themes.Fluent/ToggleSwitch.xaml b/src/Avalonia.Themes.Fluent/ToggleSwitch.xaml index 2491225a44..45537e41ad 100644 --- a/src/Avalonia.Themes.Fluent/ToggleSwitch.xaml +++ b/src/Avalonia.Themes.Fluent/ToggleSwitch.xaml @@ -5,7 +5,7 @@ 0,0,0,6 6 6 - 154 + 0 From 165bc2840909cda8c0c6fe751974757b4f32e84c Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Sat, 21 Nov 2020 19:19:27 +0300 Subject: [PATCH 045/100] [AVNCOM] Manually pass `bool` values as `int` for now --- src/Avalonia.Native/Extensions.cs | 8 ++++++++ src/Avalonia.Native/IAvnMenuItem.cs | 2 +- src/Avalonia.Native/PlatformThreadingInterface.cs | 6 +++--- src/Avalonia.Native/PopupImpl.cs | 4 ++-- src/Avalonia.Native/PredicateCallback.cs | 4 ++-- src/Avalonia.Native/ScreenImpl.cs | 2 +- src/Avalonia.Native/SystemDialogs.cs | 2 +- src/Avalonia.Native/WindowImpl.cs | 12 ++++++------ src/Avalonia.Native/WindowImplBase.cs | 12 ++++++------ src/Avalonia.Native/avn.idl | 1 + 10 files changed, 31 insertions(+), 22 deletions(-) create mode 100644 src/Avalonia.Native/Extensions.cs diff --git a/src/Avalonia.Native/Extensions.cs b/src/Avalonia.Native/Extensions.cs new file mode 100644 index 0000000000..c0d90f7a85 --- /dev/null +++ b/src/Avalonia.Native/Extensions.cs @@ -0,0 +1,8 @@ +namespace Avalonia.Native +{ + internal static class Extensions + { + public static int AsComBool(this bool b) => b ? 1 : 0; + public static bool FromComBool(this int b) => b != 0; + } +} diff --git a/src/Avalonia.Native/IAvnMenuItem.cs b/src/Avalonia.Native/IAvnMenuItem.cs index e2feffaa33..4c0c81394f 100644 --- a/src/Avalonia.Native/IAvnMenuItem.cs +++ b/src/Avalonia.Native/IAvnMenuItem.cs @@ -24,7 +24,7 @@ namespace Avalonia.Native.Interop.Impl private void UpdateTitle(string title) => SetTitle(title ?? ""); - private void UpdateIsChecked(bool isChecked) => SetIsChecked(isChecked); + private void UpdateIsChecked(bool isChecked) => SetIsChecked(isChecked.AsComBool()); private void UpdateToggleType(NativeMenuItemToggleType toggleType) { diff --git a/src/Avalonia.Native/PlatformThreadingInterface.cs b/src/Avalonia.Native/PlatformThreadingInterface.cs index df69f2eafb..882d5a2ed3 100644 --- a/src/Avalonia.Native/PlatformThreadingInterface.cs +++ b/src/Avalonia.Native/PlatformThreadingInterface.cs @@ -33,9 +33,9 @@ namespace Avalonia.Native _parent = parent; } - public void Signaled(int priority, bool priorityContainsMeaningfulValue) + public void Signaled(int priority, int priorityContainsMeaningfulValue) { - _parent.Signaled?.Invoke(priorityContainsMeaningfulValue ? (DispatcherPriority?)priority : null); + _parent.Signaled?.Invoke(priorityContainsMeaningfulValue.FromComBool() ? (DispatcherPriority?)priority : null); } } @@ -50,7 +50,7 @@ namespace Avalonia.Native _native.SetSignaledCallback(cb); } - public bool CurrentThreadIsLoopThread => _native.CurrentThreadIsLoopThread; + public bool CurrentThreadIsLoopThread => _native.CurrentThreadIsLoopThread.FromComBool(); public event Action Signaled; diff --git a/src/Avalonia.Native/PopupImpl.cs b/src/Avalonia.Native/PopupImpl.cs index 2f98385038..60c552a937 100644 --- a/src/Avalonia.Native/PopupImpl.cs +++ b/src/Avalonia.Native/PopupImpl.cs @@ -50,9 +50,9 @@ namespace Avalonia.Native // NOP on Popup } - bool IAvnWindowEvents.Closing() + int IAvnWindowEvents.Closing() { - return true; + return true.AsComBool(); } void IAvnWindowEvents.WindowStateChanged(AvnWindowState state) diff --git a/src/Avalonia.Native/PredicateCallback.cs b/src/Avalonia.Native/PredicateCallback.cs index 1ed2ae36af..19c470bcb3 100644 --- a/src/Avalonia.Native/PredicateCallback.cs +++ b/src/Avalonia.Native/PredicateCallback.cs @@ -12,9 +12,9 @@ namespace Avalonia.Native _predicate = predicate; } - bool IAvnPredicateCallback.Evaluate() + int IAvnPredicateCallback.Evaluate() { - return _predicate(); + return _predicate().AsComBool(); } } } diff --git a/src/Avalonia.Native/ScreenImpl.cs b/src/Avalonia.Native/ScreenImpl.cs index ae6da01388..7b4a001486 100644 --- a/src/Avalonia.Native/ScreenImpl.cs +++ b/src/Avalonia.Native/ScreenImpl.cs @@ -33,7 +33,7 @@ namespace Avalonia.Native screen.PixelDensity, screen.Bounds.ToAvaloniaPixelRect(), screen.WorkingArea.ToAvaloniaPixelRect(), - screen.Primary); + screen.Primary.FromComBool()); } return result; diff --git a/src/Avalonia.Native/SystemDialogs.cs b/src/Avalonia.Native/SystemDialogs.cs index 0239fc680d..8012813644 100644 --- a/src/Avalonia.Native/SystemDialogs.cs +++ b/src/Avalonia.Native/SystemDialogs.cs @@ -26,7 +26,7 @@ namespace Avalonia.Native if (dialog is OpenFileDialog ofd) { _native.OpenFileDialog(nativeParent, - events, ofd.AllowMultiple, + events, ofd.AllowMultiple.AsComBool(), ofd.Title ?? "", ofd.Directory ?? "", ofd.InitialFileName ?? "", diff --git a/src/Avalonia.Native/WindowImpl.cs b/src/Avalonia.Native/WindowImpl.cs index b42831854d..f60e83efe3 100644 --- a/src/Avalonia.Native/WindowImpl.cs +++ b/src/Avalonia.Native/WindowImpl.cs @@ -42,14 +42,14 @@ namespace Avalonia.Native _parent = parent; } - bool IAvnWindowEvents.Closing() + int IAvnWindowEvents.Closing() { if (_parent.Closing != null) { - return _parent.Closing(); + return _parent.Closing().AsComBool(); } - return true; + return true.AsComBool(); } void IAvnWindowEvents.WindowStateChanged(AvnWindowState state) @@ -69,7 +69,7 @@ namespace Avalonia.Native public void CanResize(bool value) { - _native.SetCanResize(value); + _native.SetCanResize(value.AsComBool()); } public void SetSystemDecorations(Controls.SystemDecorations enabled) @@ -145,7 +145,7 @@ namespace Avalonia.Native { _isExtended = extendIntoClientAreaHint; - _native.SetExtendClientArea(extendIntoClientAreaHint); + _native.SetExtendClientArea(extendIntoClientAreaHint.AsComBool()); InvalidateExtendedMargins(); } @@ -198,7 +198,7 @@ namespace Avalonia.Native public void SetEnabled(bool enable) { - _native.SetEnabled(enable); + _native.SetEnabled(enable.AsComBool()); } } } diff --git a/src/Avalonia.Native/WindowImplBase.cs b/src/Avalonia.Native/WindowImplBase.cs index 150ab2703e..88c3144884 100644 --- a/src/Avalonia.Native/WindowImplBase.cs +++ b/src/Avalonia.Native/WindowImplBase.cs @@ -192,14 +192,14 @@ namespace Avalonia.Native _parent.RawMouseEvent(type, timeStamp, modifiers, point, delta); } - bool IAvnWindowBaseEvents.RawKeyEvent(AvnRawKeyEventType type, uint timeStamp, AvnInputModifiers modifiers, uint key) + int IAvnWindowBaseEvents.RawKeyEvent(AvnRawKeyEventType type, uint timeStamp, AvnInputModifiers modifiers, uint key) { - return _parent.RawKeyEvent(type, timeStamp, modifiers, key); + return _parent.RawKeyEvent(type, timeStamp, modifiers, key).AsComBool(); } - bool IAvnWindowBaseEvents.RawTextInputEvent(uint timeStamp, string text) + int IAvnWindowBaseEvents.RawTextInputEvent(uint timeStamp, string text) { - return _parent.RawTextInputEvent(timeStamp, text); + return _parent.RawTextInputEvent(timeStamp, text).AsComBool(); } @@ -388,7 +388,7 @@ namespace Avalonia.Native public void SetTopmost(bool value) { - _native.SetTopMost(value); + _native.SetTopMost(value.AsComBool()); } public double RenderScaling => _native?.Scaling ?? 1; @@ -456,7 +456,7 @@ namespace Avalonia.Native TransparencyLevel = transparencyLevel; - _native?.SetBlurEnabled(TransparencyLevel >= WindowTransparencyLevel.Blur); + _native?.SetBlurEnabled((TransparencyLevel >= WindowTransparencyLevel.Blur).AsComBool()); TransparencyLevelChanged?.Invoke(TransparencyLevel); } } diff --git a/src/Avalonia.Native/avn.idl b/src/Avalonia.Native/avn.idl index 1d36cce20d..3f33476c3d 100644 --- a/src/Avalonia.Native/avn.idl +++ b/src/Avalonia.Native/avn.idl @@ -1,5 +1,6 @@ @clr-namespace Avalonia.Native.Interop @clr-access internal +@clr-map bool int @cpp-preamble @@ #include "com.h" #include "key.h" From b9a9ac94e76b0e75916539a355f4609bfff6824b Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Sat, 21 Nov 2020 16:59:35 +0000 Subject: [PATCH 046/100] Fix mainthread detection osx. --- native/Avalonia.Native/src/OSX/platformthreading.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/native/Avalonia.Native/src/OSX/platformthreading.mm b/native/Avalonia.Native/src/OSX/platformthreading.mm index f93436d157..e83bf53331 100644 --- a/native/Avalonia.Native/src/OSX/platformthreading.mm +++ b/native/Avalonia.Native/src/OSX/platformthreading.mm @@ -101,7 +101,7 @@ public: virtual bool GetCurrentThreadIsLoopThread() override { - return [[NSThread currentThread] isMainThread]; + return [NSThread isMainThread]; } virtual void SetSignaledCallback(IAvnSignaledCallback* cb) override { From 89b5d13408cca05ec939474d78a8499b6ec4fa8c Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Sat, 21 Nov 2020 23:01:57 +0000 Subject: [PATCH 047/100] [OSX] remove most of the stretching and flickering by calling updateLayer at the end of resize. --- native/Avalonia.Native/src/OSX/rendertarget.mm | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/native/Avalonia.Native/src/OSX/rendertarget.mm b/native/Avalonia.Native/src/OSX/rendertarget.mm index 93a33bbbb0..6643c5c089 100644 --- a/native/Avalonia.Native/src/OSX/rendertarget.mm +++ b/native/Avalonia.Native/src/OSX/rendertarget.mm @@ -143,13 +143,17 @@ static IAvnGlSurfaceRenderTarget* CreateGlRenderTarget(IOSurfaceRenderTarget* ta return _layer; } -- (void)resize:(AvnPixelSize)size withScale: (float) scale;{ +- (void)resize:(AvnPixelSize)size withScale: (float) scale{ @synchronized (lock) { if(surface == nil || surface->size.Width != size.Width || surface->size.Height != size.Height || surface->scale != scale) + { surface = [[IOSurfaceHolder alloc] initWithSize:size withScale:scale withOpenGlContext:_glContext.getRaw()]; + + [self updateLayer]; + } } } From d96bfe6996d7c8a3f42186f907134c4952f20466 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Sat, 21 Nov 2020 23:02:32 +0000 Subject: [PATCH 048/100] Remove last bit of flickering by wrapping layer updates inside a CATransaction. --- native/Avalonia.Native/src/OSX/rendertarget.mm | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/native/Avalonia.Native/src/OSX/rendertarget.mm b/native/Avalonia.Native/src/OSX/rendertarget.mm index 6643c5c089..00b6dab219 100644 --- a/native/Avalonia.Native/src/OSX/rendertarget.mm +++ b/native/Avalonia.Native/src/OSX/rendertarget.mm @@ -2,6 +2,7 @@ #include "rendertarget.h" #import #import +#import #include #include @@ -163,12 +164,15 @@ static IAvnGlSurfaceRenderTarget* CreateGlRenderTarget(IOSurfaceRenderTarget* ta @synchronized (lock) { if(_layer == nil) return; + [CATransaction begin]; [_layer setContents: nil]; if(surface != nil) { [_layer setContentsScale: surface->scale]; [_layer setContents: (__bridge IOSurface*) surface->surface]; } + [CATransaction commit]; + [CATransaction flush]; } } else From 019a5096f1372c4dee801befbd654bff0f1c72fe Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Sun, 22 Nov 2020 12:27:28 +0000 Subject: [PATCH 049/100] incremement min win10 version for win.ui.comp. --- .../WinRT/Composition/WinUICompositorConnection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositorConnection.cs b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositorConnection.cs index 7e1f97ab84..2aa82436f6 100644 --- a/src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositorConnection.cs +++ b/src/Windows/Avalonia.Win32/WinRT/Composition/WinUICompositorConnection.cs @@ -134,7 +134,7 @@ namespace Avalonia.Win32.WinRT.Composition public static void TryCreateAndRegister(EglPlatformOpenGlInterface angle) { const int majorRequired = 10; - const int buildRequired = 16299; + const int buildRequired = 17134; var majorInstalled = Win32Platform.WindowsVersion.Major; var buildInstalled = Win32Platform.WindowsVersion.Build; From 31bfcb6b05262cad46fce35340932b742e1b88e6 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Mon, 23 Nov 2020 11:39:52 +0100 Subject: [PATCH 050/100] Fix BindingValue comparison. `NativeMenuBar` click forwarding was broken by https://github.com/AvaloniaUI/Avalonia/pull/4648 - this fixes it. --- src/Avalonia.Controls/NativeMenuBar.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/NativeMenuBar.cs b/src/Avalonia.Controls/NativeMenuBar.cs index 63bb39108f..5d2e1087a7 100644 --- a/src/Avalonia.Controls/NativeMenuBar.cs +++ b/src/Avalonia.Controls/NativeMenuBar.cs @@ -1,7 +1,6 @@ using System; using Avalonia.Controls.Primitives; using Avalonia.Interactivity; -using Avalonia.Styling; namespace Avalonia.Controls { @@ -16,7 +15,7 @@ namespace Avalonia.Controls EnableMenuItemClickForwardingProperty.Changed.Subscribe(args => { var item = (MenuItem)args.Sender; - if (args.NewValue.Equals(true)) + if (args.NewValue.GetValueOrDefault()) item.Click += OnMenuItemClick; else item.Click -= OnMenuItemClick; From 5d4b56d04207025a4e161606ceac6a34b11bfd8b Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Mon, 23 Nov 2020 12:18:16 +0100 Subject: [PATCH 051/100] NativeMenuItem.Clicked -> Click. And mark `Clicked` as obsolete. For consistency with `MenuItem.Click`. --- src/Avalonia.Controls/NativeMenuItem.cs | 16 +++++++++++++--- .../AvaloniaNativeMenuExporter.cs | 2 +- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.Controls/NativeMenuItem.cs b/src/Avalonia.Controls/NativeMenuItem.cs index a0fec9e677..e8cb59e76e 100644 --- a/src/Avalonia.Controls/NativeMenuItem.cs +++ b/src/Avalonia.Controls/NativeMenuItem.cs @@ -151,7 +151,7 @@ namespace Avalonia.Controls IsEnabled = _command?.CanExecute(null) ?? true; } - public bool HasClickHandlers => Clicked != null; + public bool HasClickHandlers => Click != null; public ICommand Command { @@ -182,11 +182,21 @@ namespace Avalonia.Controls set { SetValue(CommandParameterProperty, value); } } - public event EventHandler Clicked; + /// + /// Occurs when a is clicked. + /// + public event EventHandler Click; + + [Obsolete("Use Click event.")] + public event EventHandler Clicked + { + add => Click += value; + remove => Click -= value; + } void INativeMenuItemExporterEventsImplBridge.RaiseClicked() { - Clicked?.Invoke(this, new EventArgs()); + Click?.Invoke(this, new EventArgs()); if (Command?.CanExecute(CommandParameter) == true) { diff --git a/src/Avalonia.Native/AvaloniaNativeMenuExporter.cs b/src/Avalonia.Native/AvaloniaNativeMenuExporter.cs index b192de95de..2e3408eca5 100644 --- a/src/Avalonia.Native/AvaloniaNativeMenuExporter.cs +++ b/src/Avalonia.Native/AvaloniaNativeMenuExporter.cs @@ -60,7 +60,7 @@ namespace Avalonia.Native Header = "About Avalonia", }; - aboutItem.Clicked += async (sender, e) => + aboutItem.Click += async (sender, e) => { var dialog = new AboutAvaloniaDialog(); From e59573d6309bb98e75ccd75d9c3c8050a9de4d7a Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Mon, 23 Nov 2020 12:33:17 +0100 Subject: [PATCH 052/100] Mark NativeMenuItem.Menu as content property. Means that submenus don't need the extra `` element. --- samples/ControlCatalog/DecoratedWindow.xaml | 30 ++++------ samples/ControlCatalog/MainWindow.xaml | 66 +++++++++------------ src/Avalonia.Controls/NativeMenuItem.cs | 2 + 3 files changed, 44 insertions(+), 54 deletions(-) diff --git a/samples/ControlCatalog/DecoratedWindow.xaml b/samples/ControlCatalog/DecoratedWindow.xaml index 8e4c97b7f0..5251a2fa55 100644 --- a/samples/ControlCatalog/DecoratedWindow.xaml +++ b/samples/ControlCatalog/DecoratedWindow.xaml @@ -6,25 +6,21 @@ - - - - - - - - - - - + + + + + + + + + - - - - - - + + + + diff --git a/samples/ControlCatalog/MainWindow.xaml b/samples/ControlCatalog/MainWindow.xaml index 97bd88f5e4..6a70bb082f 100644 --- a/samples/ControlCatalog/MainWindow.xaml +++ b/samples/ControlCatalog/MainWindow.xaml @@ -16,47 +16,39 @@ - - - - - - - - - - - - - + + + + + + + + + - - - - - - + + + + - - - - - - - + + + + + diff --git a/src/Avalonia.Controls/NativeMenuItem.cs b/src/Avalonia.Controls/NativeMenuItem.cs index e8cb59e76e..76197b62ad 100644 --- a/src/Avalonia.Controls/NativeMenuItem.cs +++ b/src/Avalonia.Controls/NativeMenuItem.cs @@ -2,6 +2,7 @@ using System; using System.Windows.Input; using Avalonia.Input; using Avalonia.Media.Imaging; +using Avalonia.Metadata; using Avalonia.Utilities; namespace Avalonia.Controls @@ -62,6 +63,7 @@ namespace Avalonia.Controls public static readonly DirectProperty MenuProperty = AvaloniaProperty.RegisterDirect(nameof(Menu), o => o.Menu, (o, v) => o.Menu = v); + [Content] public NativeMenu Menu { get => _menu; From ad677f0ff43ca6bcd9f5f5524692e3d58a6956c6 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Mon, 23 Nov 2020 15:28:42 +0100 Subject: [PATCH 053/100] Added failing test for #4900. --- .../Xaml/ShapeTests.cs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ShapeTests.cs diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ShapeTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ShapeTests.cs new file mode 100644 index 0000000000..bd577e84f8 --- /dev/null +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ShapeTests.cs @@ -0,0 +1,24 @@ +using Avalonia.Controls; +using Avalonia.Media; +using Xunit; + +namespace Avalonia.Markup.Xaml.UnitTests.Xaml +{ + public class ShapeTests : XamlTestBase + { + [Fact] + public void Can_Specify_DashStyle_In_XAML() + { + var xaml = @" + + + + +"; + + var target = AvaloniaRuntimeXamlLoader.Parse(xaml); + + Assert.NotNull(target); + } + } +} From d99512a785e78c5976a2f70f013a21dd40743342 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Mon, 23 Nov 2020 15:29:37 +0100 Subject: [PATCH 054/100] Make DashStyle.Dashes an AvaloniaList. So that it can be parsed from XAML and changes can be made. --- src/Avalonia.Visuals/Media/DashStyle.cs | 75 +++++++++++++------ .../Media/PenTests.cs | 18 ++++- 2 files changed, 67 insertions(+), 26 deletions(-) diff --git a/src/Avalonia.Visuals/Media/DashStyle.cs b/src/Avalonia.Visuals/Media/DashStyle.cs index 1e813edc13..acba2d3615 100644 --- a/src/Avalonia.Visuals/Media/DashStyle.cs +++ b/src/Avalonia.Visuals/Media/DashStyle.cs @@ -1,11 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using Avalonia.Animation; +using Avalonia.Collections; +using Avalonia.Media.Immutable; + +#nullable enable + namespace Avalonia.Media { - using System; - using System.Collections.Generic; - using System.Linq; - using Avalonia.Animation; - using Avalonia.Media.Immutable; - /// /// Represents the sequence of dashes and gaps that will be applied by a . /// @@ -14,8 +17,8 @@ namespace Avalonia.Media /// /// Defines the property. /// - public static readonly StyledProperty> DashesProperty = - AvaloniaProperty.Register>(nameof(Dashes)); + public static readonly StyledProperty> DashesProperty = + AvaloniaProperty.Register>(nameof(Dashes)); /// /// Defines the property. @@ -23,10 +26,10 @@ namespace Avalonia.Media public static readonly StyledProperty OffsetProperty = AvaloniaProperty.Register(nameof(Offset)); - private static ImmutableDashStyle s_dash; - private static ImmutableDashStyle s_dot; - private static ImmutableDashStyle s_dashDot; - private static ImmutableDashStyle s_dashDotDot; + private static ImmutableDashStyle? s_dash; + private static ImmutableDashStyle? s_dot; + private static ImmutableDashStyle? s_dashDot; + private static ImmutableDashStyle? s_dashDotDot; /// /// Initializes a new instance of the class. @@ -41,9 +44,9 @@ namespace Avalonia.Media /// /// The dashes collection. /// The dash sequence offset. - public DashStyle(IEnumerable dashes, double offset) + public DashStyle(IEnumerable? dashes, double offset) { - Dashes = (IReadOnlyList)dashes?.ToList() ?? Array.Empty(); + Dashes = (dashes as AvaloniaList) ?? new AvaloniaList(dashes ?? Array.Empty()); Offset = offset; } @@ -61,31 +64,27 @@ namespace Avalonia.Media /// /// Represents a dashed . /// - public static IDashStyle Dash => - s_dash ?? (s_dash = new ImmutableDashStyle(new double[] { 2, 2 }, 1)); + public static IDashStyle Dash => s_dash ??= new ImmutableDashStyle(new double[] { 2, 2 }, 1); /// /// Represents a dotted . /// - public static IDashStyle Dot => - s_dot ?? (s_dot = new ImmutableDashStyle(new double[] { 0, 2 }, 0)); + public static IDashStyle Dot => s_dot ??= new ImmutableDashStyle(new double[] { 0, 2 }, 0); /// /// Represents a dashed dotted . /// - public static IDashStyle DashDot => - s_dashDot ?? (s_dashDot = new ImmutableDashStyle(new double[] { 2, 2, 0, 2 }, 1)); + public static IDashStyle DashDot => s_dashDot ??= new ImmutableDashStyle(new double[] { 2, 2, 0, 2 }, 1); /// /// Represents a dashed double dotted . /// - public static IDashStyle DashDotDot => - s_dashDotDot ?? (s_dashDotDot = new ImmutableDashStyle(new double[] { 2, 2, 0, 2, 0, 2 }, 1)); + public static IDashStyle DashDotDot => s_dashDotDot ??= new ImmutableDashStyle(new double[] { 2, 2, 0, 2, 0, 2 }, 1); /// /// Gets or sets the length of alternating dashes and gaps. /// - public IReadOnlyList Dashes + public AvaloniaList Dashes { get => GetValue(DashesProperty); set => SetValue(DashesProperty, value); @@ -100,15 +99,43 @@ namespace Avalonia.Media set => SetValue(OffsetProperty, value); } + IReadOnlyList IDashStyle.Dashes => Dashes; + /// /// Raised when the dash style changes. /// - public event EventHandler Invalidated; + public event EventHandler? Invalidated; /// /// Returns an immutable clone of the . /// /// public ImmutableDashStyle ToImmutable() => new ImmutableDashStyle(Dashes, Offset); + + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + + if (change.Property == DashesProperty) + { + var oldValue = change.OldValue.GetValueOrDefault>(); + var newValue = change.NewValue.GetValueOrDefault>(); + + if (oldValue is object) + { + oldValue.CollectionChanged -= DashesChanged; + } + + if (newValue is object) + { + newValue.CollectionChanged += DashesChanged; + } + } + } + + private void DashesChanged(object sender, NotifyCollectionChangedEventArgs e) + { + Invalidated?.Invoke(this, e); + } } } diff --git a/tests/Avalonia.Visuals.UnitTests/Media/PenTests.cs b/tests/Avalonia.Visuals.UnitTests/Media/PenTests.cs index 418ac7576b..c2a1a5f9e4 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/PenTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/Media/PenTests.cs @@ -1,4 +1,5 @@ -using Avalonia.Media; +using Avalonia.Collections; +using Avalonia.Media; using Avalonia.Media.Immutable; using Xunit; @@ -39,7 +40,20 @@ namespace Avalonia.Visuals.UnitTests.Media var raised = false; target.Invalidated += (s, e) => raised = true; - dashes.Dashes = new[] { 0.1, 0.2 }; + dashes.Dashes = new AvaloniaList { 0.1, 0.2 }; + + Assert.True(raised); + } + + [Fact] + public void Adding_DashStyle_Dashes_Raises_Invalidated() + { + var dashes = new DashStyle(); + var target = new Pen { DashStyle = dashes }; + var raised = false; + + target.Invalidated += (s, e) => raised = true; + dashes.Dashes.Add(0.3); Assert.True(raised); } From 6e41b197f0b9513ba264701f39d8ce1b7436a841 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Mon, 23 Nov 2020 15:35:04 +0100 Subject: [PATCH 055/100] Updated API compat. --- src/Avalonia.Visuals/ApiCompatBaseline.txt | 42 +++------------------- 1 file changed, 4 insertions(+), 38 deletions(-) diff --git a/src/Avalonia.Visuals/ApiCompatBaseline.txt b/src/Avalonia.Visuals/ApiCompatBaseline.txt index 2d00d82a46..62f4def701 100644 --- a/src/Avalonia.Visuals/ApiCompatBaseline.txt +++ b/src/Avalonia.Visuals/ApiCompatBaseline.txt @@ -1,39 +1,5 @@ Compat issues with assembly Avalonia.Visuals: -MembersMustExist : Member 'public void Avalonia.Media.DrawingContext.DrawGlyphRun(Avalonia.Media.IBrush, Avalonia.Media.GlyphRun, Avalonia.Point)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Media.Typeface Avalonia.Media.FontManager.GetOrAddTypeface(Avalonia.Media.FontFamily, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Media.Typeface Avalonia.Media.FontManager.MatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontFamily, System.Globalization.CultureInfo)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Rect Avalonia.Media.Geometry.GetRenderBounds(Avalonia.Media.Pen)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public System.Boolean Avalonia.Media.Geometry.StrokeContains(Avalonia.Media.Pen, Avalonia.Point)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Rect Avalonia.Media.GlyphRun.Bounds.get()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.StyledProperty Avalonia.StyledProperty Avalonia.Media.GlyphRunDrawing.BaselineOriginProperty' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Point Avalonia.Media.GlyphRunDrawing.BaselineOrigin.get()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.GlyphRunDrawing.BaselineOrigin.set(Avalonia.Point)' does not exist in the implementation but it does exist in the contract. -CannotSealType : Type 'Avalonia.Media.Typeface' is actually (has the sealed modifier) sealed in the implementation but not sealed in the contract. -TypeCannotChangeClassification : Type 'Avalonia.Media.Typeface' is a 'struct' in the implementation but is a 'class' in the contract. -CannotMakeMemberNonVirtual : Member 'public System.Boolean Avalonia.Media.Typeface.Equals(System.Object)' is non-virtual in the implementation but is virtual in the contract. -CannotMakeMemberNonVirtual : Member 'public System.Int32 Avalonia.Media.Typeface.GetHashCode()' is non-virtual in the implementation but is virtual in the contract. -TypesMustExist : Type 'Avalonia.Media.Fonts.FontKey' does not exist in the implementation but it does exist in the contract. -CannotAddAbstractMembers : Member 'public Avalonia.Size Avalonia.Media.TextFormatting.DrawableTextRun.Size' is abstract in the implementation but is missing in the contract. -MembersMustExist : Member 'public Avalonia.Rect Avalonia.Media.TextFormatting.DrawableTextRun.Bounds.get()' does not exist in the implementation but it does exist in the contract. -CannotAddAbstractMembers : Member 'public void Avalonia.Media.TextFormatting.DrawableTextRun.Draw(Avalonia.Media.DrawingContext)' is abstract in the implementation but is missing in the contract. -MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.DrawableTextRun.Draw(Avalonia.Media.DrawingContext, Avalonia.Point)' does not exist in the implementation but it does exist in the contract. -CannotAddAbstractMembers : Member 'public Avalonia.Size Avalonia.Media.TextFormatting.DrawableTextRun.Size.get()' is abstract in the implementation but is missing in the contract. -MembersMustExist : Member 'public Avalonia.Rect Avalonia.Media.TextFormatting.ShapedTextCharacters.Bounds.get()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.ShapedTextCharacters.Draw(Avalonia.Media.DrawingContext, Avalonia.Point)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.TextLayout.Draw(Avalonia.Media.DrawingContext, Avalonia.Point)' does not exist in the implementation but it does exist in the contract. -CannotAddAbstractMembers : Member 'public Avalonia.Media.TextFormatting.TextLineBreak Avalonia.Media.TextFormatting.TextLine.TextLineBreak' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public void Avalonia.Media.TextFormatting.TextLine.Draw(Avalonia.Media.DrawingContext)' is abstract in the implementation but is missing in the contract. -MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.TextLine.Draw(Avalonia.Media.DrawingContext, Avalonia.Point)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Media.TextFormatting.TextLineBreak Avalonia.Media.TextFormatting.TextLine.LineBreak.get()' does not exist in the implementation but it does exist in the contract. -CannotAddAbstractMembers : Member 'public Avalonia.Media.TextFormatting.TextLineBreak Avalonia.Media.TextFormatting.TextLine.TextLineBreak.get()' is abstract in the implementation but is missing in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IDrawingContextLayerImpl Avalonia.Platform.IDrawingContextImpl.CreateLayer(Avalonia.Size)' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IRenderTargetBitmapImpl Avalonia.Platform.IDrawingContextImpl.CreateLayer(Avalonia.Size)' is present in the contract but not in the implementation. -MembersMustExist : Member 'public Avalonia.Platform.IRenderTargetBitmapImpl Avalonia.Platform.IDrawingContextImpl.CreateLayer(Avalonia.Size)' does not exist in the implementation but it does exist in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.IDrawingContextImpl.DrawGlyphRun(Avalonia.Media.IBrush, Avalonia.Media.GlyphRun)' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.IDrawingContextImpl.DrawGlyphRun(Avalonia.Media.IBrush, Avalonia.Media.GlyphRun, Avalonia.Point)' is present in the contract but not in the implementation. -MembersMustExist : Member 'public void Avalonia.Platform.IDrawingContextImpl.DrawGlyphRun(Avalonia.Media.IBrush, Avalonia.Media.GlyphRun, Avalonia.Point)' does not exist in the implementation but it does exist in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontFamily, System.Globalization.CultureInfo, Avalonia.Media.Fonts.FontKey)' is present in the contract but not in the implementation. -MembersMustExist : Member 'public System.Boolean Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontFamily, System.Globalization.CultureInfo, Avalonia.Media.Fonts.FontKey)' does not exist in the implementation but it does exist in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontFamily, System.Globalization.CultureInfo, Avalonia.Media.Typeface)' is present in the implementation but not in the contract. -MembersMustExist : Member 'public Avalonia.Utilities.IRef Avalonia.Rendering.RenderLayer.Bitmap.get()' does not exist in the implementation but it does exist in the contract. -Total Issues: 37 +MembersMustExist : Member 'public Avalonia.StyledProperty> Avalonia.StyledProperty> Avalonia.Media.DashStyle.DashesProperty' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public System.Collections.Generic.IReadOnlyList Avalonia.Media.DashStyle.Dashes.get()' does not exist in the implementation but it does exist in the contract. +MembersMustExist : Member 'public void Avalonia.Media.DashStyle.Dashes.set(System.Collections.Generic.IReadOnlyList)' does not exist in the implementation but it does exist in the contract. +Total Issues: 3 From 90fb76f364605928ef124e9e875066a4bcc10a60 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Mon, 23 Nov 2020 15:50:11 +0100 Subject: [PATCH 056/100] Prevent exceptions when removing styles. When `_target.Owner` was null, the resource observable produced a `null` which may not be valid for the target property, resulting in an exception . Although the exception is caught, it slows things down. --- src/Avalonia.Styling/Controls/ResourceNodeExtensions.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Styling/Controls/ResourceNodeExtensions.cs b/src/Avalonia.Styling/Controls/ResourceNodeExtensions.cs index 250274c39b..fedd3469e6 100644 --- a/src/Avalonia.Styling/Controls/ResourceNodeExtensions.cs +++ b/src/Avalonia.Styling/Controls/ResourceNodeExtensions.cs @@ -147,13 +147,16 @@ namespace Avalonia.Controls { if (_target.Owner is object) { - observer.OnNext(Convert(_target.Owner?.FindResource(_key))); + observer.OnNext(Convert(_target.Owner.FindResource(_key))); } } private void PublishNext() { - PublishNext(Convert(_target.Owner?.FindResource(_key))); + if (_target.Owner is object) + { + PublishNext(Convert(_target.Owner.FindResource(_key))); + } } private void OwnerChanged(object sender, EventArgs e) From 4aa013a79e67e4152ec57b93ce4b1cbfc339212c Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Mon, 23 Nov 2020 16:25:07 +0100 Subject: [PATCH 057/100] Don't handle tab navigation in DefaultMenuInteractionHandler. Allow the default keyboard interaction handler to handle tab key presses in menus, because the logic in `DefaultMenuInteractionHandler` was causing the focus to get stuck in the menu. Also fix `NavigationDirection.IsDirectional` method. Fixes #5002. --- src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs | 2 +- src/Avalonia.Input/NavigationDirection.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs b/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs index a54d1ce308..5cbd3698b6 100644 --- a/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs +++ b/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs @@ -231,7 +231,7 @@ namespace Avalonia.Controls.Platform { var direction = e.Key.ToNavigationDirection(); - if (direction.HasValue) + if (direction?.IsDirectional() == true) { if (item == null && _isContextMenu) { diff --git a/src/Avalonia.Input/NavigationDirection.cs b/src/Avalonia.Input/NavigationDirection.cs index 9b9af0b0a6..9bd6a8bb42 100644 --- a/src/Avalonia.Input/NavigationDirection.cs +++ b/src/Avalonia.Input/NavigationDirection.cs @@ -83,7 +83,7 @@ namespace Avalonia.Input /// public static bool IsDirectional(this NavigationDirection direction) { - return direction > NavigationDirection.Previous || + return direction > NavigationDirection.Previous && direction <= NavigationDirection.PageDown; } From 4ab5cc4bbdf0dda18db79db1d389867fa6f36d41 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 23 Nov 2020 16:24:28 +0000 Subject: [PATCH 058/100] enable datavalidation on slider value property. --- src/Avalonia.Controls/Slider.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/Avalonia.Controls/Slider.cs b/src/Avalonia.Controls/Slider.cs index 6e08e78813..fe4b24099f 100644 --- a/src/Avalonia.Controls/Slider.cs +++ b/src/Avalonia.Controls/Slider.cs @@ -3,6 +3,7 @@ using Avalonia.Collections; using Avalonia.Controls.Metadata; using Avalonia.Controls.Mixins; using Avalonia.Controls.Primitives; +using Avalonia.Data; using Avalonia.Input; using Avalonia.Interactivity; using Avalonia.Layout; @@ -94,6 +95,8 @@ namespace Avalonia.Controls Thumb.DragStartedEvent.AddClassHandler((x, e) => x.OnThumbDragStarted(e), RoutingStrategies.Bubble); Thumb.DragCompletedEvent.AddClassHandler((x, e) => x.OnThumbDragCompleted(e), RoutingStrategies.Bubble); + + ValueProperty.OverrideMetadata(new DirectPropertyMetadata(enableDataValidation: true)); } /// @@ -225,6 +228,14 @@ namespace Avalonia.Controls Value = IsSnapToTickEnabled ? SnapToTick(finalValue) : finalValue; } + protected override void UpdateDataValidation(AvaloniaProperty property, BindingValue value) + { + if (property == ValueProperty) + { + DataValidationErrors.SetError(this, value.Error); + } + } + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) { base.OnPropertyChanged(change); From b16820a230e566ed7606cdc7ed3a302f1f88c53e Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Mon, 23 Nov 2020 23:12:09 +0100 Subject: [PATCH 059/100] Added failing test for application resources. `DynamicResource` in `Application.Resources` fails to resolve resource. --- .../DynamicResourceExtensionTests.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs index 47a73cb360..9152131ab3 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs @@ -345,6 +345,23 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions Assert.Equal(0xff506070, brush.Color.ToUint32()); } + [Fact] + public void DynamicResource_Can_Be_Assigned_To_Resource_Property_In_Application() + { + var xaml = @" + + + #ff506070 + + +"; + + var application = (Application)AvaloniaRuntimeXamlLoader.Load(xaml); + var brush = (SolidColorBrush)application.Resources["brush"]; + + Assert.Equal(0xff506070, brush.Color.ToUint32()); + } [Fact] public void DynamicResource_Can_Be_Assigned_To_ItemTemplate_Property() From 64677edb8a50896baa9b15d1dbb81343c041da68 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Mon, 23 Nov 2020 23:17:43 +0100 Subject: [PATCH 060/100] Fix resolving dynamic resource in Application. Instead of looking for an `IStyledElement` as an anchor, look for an `IResourceHost` because `Application` doesn't implement `IStyledElement` but it does implement `IResourceHost` and this interface has everything we need. --- .../Controls/ResourceNodeExtensions.cs | 6 +++--- .../DynamicResourceExtension.cs | 18 +++++++----------- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/src/Avalonia.Styling/Controls/ResourceNodeExtensions.cs b/src/Avalonia.Styling/Controls/ResourceNodeExtensions.cs index 250274c39b..9654d5d9e3 100644 --- a/src/Avalonia.Styling/Controls/ResourceNodeExtensions.cs +++ b/src/Avalonia.Styling/Controls/ResourceNodeExtensions.cs @@ -60,7 +60,7 @@ namespace Avalonia.Controls } public static IObservable GetResourceObservable( - this IStyledElement control, + this IResourceHost control, object key, Func? converter = null) { @@ -83,11 +83,11 @@ namespace Avalonia.Controls private class ResourceObservable : LightweightObservableBase { - private readonly IStyledElement _target; + private readonly IResourceHost _target; private readonly object _key; private readonly Func? _converter; - public ResourceObservable(IStyledElement target, object key, Func? converter) + public ResourceObservable(IResourceHost target, object key, Func? converter) { _target = target; _key = key; diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs index 03fd2e60dd..95380be65f 100644 --- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs +++ b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs @@ -10,8 +10,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions { public class DynamicResourceExtension : IBinding { - private IStyledElement? _anchor; - private IResourceProvider? _resourceProvider; + private object? _anchor; public DynamicResourceExtension() { @@ -30,12 +29,9 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions if (!(provideTarget.TargetObject is IStyledElement)) { - _anchor = serviceProvider.GetFirstParent(); - - if (_anchor is null) - { - _resourceProvider = serviceProvider.GetFirstParent(); - } + _anchor = serviceProvider.GetFirstParent() ?? + serviceProvider.GetFirstParent() ?? + (object?)serviceProvider.GetFirstParent(); } return this; @@ -52,16 +48,16 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions return null; } - var control = target as IStyledElement ?? _anchor as IStyledElement; + var control = target as IResourceHost ?? _anchor as IResourceHost; if (control != null) { var source = control.GetResourceObservable(ResourceKey, GetConverter(targetProperty)); return InstancedBinding.OneWay(source); } - else if (_resourceProvider is object) + else if (_anchor is IResourceProvider resourceProvider) { - var source = _resourceProvider.GetResourceObservable(ResourceKey, GetConverter(targetProperty)); + var source = resourceProvider.GetResourceObservable(ResourceKey, GetConverter(targetProperty)); return InstancedBinding.OneWay(source); } From b989086c5f242feb1f06eb9cdc36de7be31eaf64 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Mon, 23 Nov 2020 23:27:16 +0100 Subject: [PATCH 061/100] Add breaking change. --- src/Avalonia.Styling/ApiCompatBaseline.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 src/Avalonia.Styling/ApiCompatBaseline.txt diff --git a/src/Avalonia.Styling/ApiCompatBaseline.txt b/src/Avalonia.Styling/ApiCompatBaseline.txt new file mode 100644 index 0000000000..001e165830 --- /dev/null +++ b/src/Avalonia.Styling/ApiCompatBaseline.txt @@ -0,0 +1,3 @@ +Compat issues with assembly Avalonia.Styling: +MembersMustExist : Member 'public System.IObservable Avalonia.Controls.ResourceNodeExtensions.GetResourceObservable(Avalonia.IStyledElement, System.Object, System.Func)' does not exist in the implementation but it does exist in the contract. +Total Issues: 1 From b488e882d448e4d758a609633ba334d4ac539fe7 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Tue, 24 Nov 2020 14:14:39 +0000 Subject: [PATCH 062/100] Add a background to PathIcon. --- src/Avalonia.Themes.Default/PathIcon.xaml | 15 +++++++++------ src/Avalonia.Themes.Fluent/PathIcon.xaml | 15 +++++++++------ 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/Avalonia.Themes.Default/PathIcon.xaml b/src/Avalonia.Themes.Default/PathIcon.xaml index a2d01f7b5b..039f0ef10c 100644 --- a/src/Avalonia.Themes.Default/PathIcon.xaml +++ b/src/Avalonia.Themes.Default/PathIcon.xaml @@ -2,16 +2,19 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> diff --git a/src/Avalonia.Themes.Fluent/PathIcon.xaml b/src/Avalonia.Themes.Fluent/PathIcon.xaml index 0a2c747514..d4952b3571 100644 --- a/src/Avalonia.Themes.Fluent/PathIcon.xaml +++ b/src/Avalonia.Themes.Fluent/PathIcon.xaml @@ -10,16 +10,19 @@ From c9f7461204df2a530d3e09d5df728aec0242f309 Mon Sep 17 00:00:00 2001 From: Hank G Date: Tue, 24 Nov 2020 16:47:00 -0500 Subject: [PATCH 063/100] Update build.md Update Mac instructions to use the build command --- Documentation/build.md | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/Documentation/build.md b/Documentation/build.md index 45ee980e0b..5f75290424 100644 --- a/Documentation/build.md +++ b/Documentation/build.md @@ -60,23 +60,10 @@ git submodule update --init --recursive ### Build native libraries (macOS only) -On macOS it is necessary to build and manually install the respective native libraries using [Xcode](https://developer.apple.com/xcode/). The steps to get this working correctly are: -- (for revisions after 2 Nov 2020) Run `./build.sh GenerateCppHeaders` to generate `avalonia-native.h` from `avn.idl` -- Navigate to the Avalonia/native/Avalonia.Native/src/OSX folder and open the `Avalonia.Native.OSX.xcodeproj` project -- Build the library via the Product->Build menu. This will generate binaries in your local path under ~/Library/Developer/Xcode/DerivedData/Avalonia.Native.OSX-*guid* where "guid" is uniquely generated every time you build. -- Manually install the native library by copying it from the build artifacts folder into the shared dynamic library path: - -``` -cd ~/Library/Developer/Xcode/DerivedData/Avalonia.Native.OSX-[guid]/Build/Products/Debug -cp libAvalonia.Native.OSX.dylib /usr/local/lib/libAvalonia.Native.OSX.dylib -``` - -For compilation we also need this library on the build path. From the Avalonia root project directory: - -``` -mkdir -p build/Products/Release -cp /usr/local/lib/libAvalonia.Native.OSX.dylib build/Products/Release +On macOS it is necessary to build and manually install the respective native libraries using [Xcode](https://developer.apple.com/xcode/). Execute the build script in the root project with the `CompileNative` task. It will build the headers, build the libraries, and place them in the appropriate place to allow .NET to find them at compilation and run time. +```bash +./build.sh CompileNative ``` ### Build and Run Avalonia From f46a3b6138fbfbb19232e971b1d9b0ca0c39ff17 Mon Sep 17 00:00:00 2001 From: Luis von der Eltz Date: Wed, 25 Nov 2020 10:09:38 +0100 Subject: [PATCH 064/100] Set default visibility of scrollbar to "Visible" --- src/Avalonia.Controls/Primitives/ScrollBar.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Primitives/ScrollBar.cs b/src/Avalonia.Controls/Primitives/ScrollBar.cs index a7fb7ae08c..d264ed76cc 100644 --- a/src/Avalonia.Controls/Primitives/ScrollBar.cs +++ b/src/Avalonia.Controls/Primitives/ScrollBar.cs @@ -35,7 +35,7 @@ namespace Avalonia.Controls.Primitives /// Defines the property. /// public static readonly StyledProperty VisibilityProperty = - AvaloniaProperty.Register(nameof(Visibility)); + AvaloniaProperty.Register(nameof(Visibility), ScrollBarVisibility.Visible); /// /// Defines the property. From dd1cbd1d8a03809a9202b6373f9b872604f289ec Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Sun, 22 Nov 2020 22:29:14 +0100 Subject: [PATCH 065/100] Add general purpose AST node for creating new Avalonia lists. Implement xaml compile time parsing of row/column definitions. --- src/Avalonia.Base/Collections/AvaloniaList.cs | 18 +++++++ ...aloniaXamlIlAvaloniaListConstantAstNode.cs | 54 +++++++++++++++++++ .../AvaloniaXamlIlLanguage.cs | 48 +++++++++++++++++ .../AvaloniaXamlIlWellKnownTypes.cs | 10 ++++ 4 files changed, 130 insertions(+) create mode 100644 src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AstNodes/AvaloniaXamlIlAvaloniaListConstantAstNode.cs diff --git a/src/Avalonia.Base/Collections/AvaloniaList.cs b/src/Avalonia.Base/Collections/AvaloniaList.cs index d43b4e04bb..5681214222 100644 --- a/src/Avalonia.Base/Collections/AvaloniaList.cs +++ b/src/Avalonia.Base/Collections/AvaloniaList.cs @@ -63,6 +63,15 @@ namespace Avalonia.Collections _inner = new List(); } + /// + /// Initializes a new instance of the . + /// + /// Initial list capacity. + public AvaloniaList(int capacity) + { + _inner = new List(capacity); + } + /// /// Initializes a new instance of the class. /// @@ -175,6 +184,15 @@ namespace Avalonia.Collections set { this[index] = (T)value; } } + /// + /// Gets or sets the total number of elements the internal data structure can hold without resizing. + /// + public int Capacity + { + get => _inner.Capacity; + set => _inner.Capacity = value; + } + /// /// Adds an item to the collection. /// diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AstNodes/AvaloniaXamlIlAvaloniaListConstantAstNode.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AstNodes/AvaloniaXamlIlAvaloniaListConstantAstNode.cs new file mode 100644 index 0000000000..0f4efc9f65 --- /dev/null +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AstNodes/AvaloniaXamlIlAvaloniaListConstantAstNode.cs @@ -0,0 +1,54 @@ +using System.Collections.Generic; +using Avalonia.Controls; +using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers; +using XamlX.Ast; +using XamlX.Emit; +using XamlX.IL; +using XamlX.TypeSystem; + +namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.AstNodes +{ + class AvaloniaXamlIlAvaloniaListConstantAstNode : XamlAstNode, IXamlAstValueNode, IXamlAstILEmitableNode + { + private readonly IXamlType _elementType; + private readonly IReadOnlyList _values; + private readonly IXamlConstructor _constructor; + private readonly IXamlMethod _listAddMethod; + private readonly IXamlMethod _listSetCapacityMethod; + + public AvaloniaXamlIlAvaloniaListConstantAstNode(IXamlLineInfo lineInfo, AvaloniaXamlIlWellKnownTypes types, IXamlType listType, IXamlType elementType, IReadOnlyList values) : base(lineInfo) + { + _constructor = listType.GetConstructor(); + _listAddMethod = listType.GetMethod(new FindMethodMethodSignature("Add", types.XamlIlTypes.Void, elementType)); + _listSetCapacityMethod = listType.GetMethod(new FindMethodMethodSignature("set_Capacity", types.XamlIlTypes.Void, types.Int)); + + _elementType = elementType; + _values = values; + + Type = new XamlAstClrTypeReference(lineInfo, listType, false); + } + + public IXamlAstTypeReference Type { get; } + + public XamlILNodeEmitResult Emit(XamlEmitContext context, IXamlILEmitter codeGen) + { + codeGen.Newobj(_constructor); + + codeGen + .Dup() + .Ldc_I4(_values.Count) + .EmitCall(_listSetCapacityMethod); + + foreach (var value in _values) + { + codeGen.Dup(); + + context.Emit(value, codeGen, _elementType); + + codeGen.EmitCall(_listAddMethod); + } + + return XamlILNodeEmitResult.Type(0, Type.GetClrType()); + } + } +} diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguage.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguage.cs index 40cfd21a76..10c5e615bb 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguage.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguage.cs @@ -353,6 +353,16 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions } } + if (type.Equals(types.ColumnDefinitions)) + { + return ConvertDefinitionList(node, text, types, types.ColumnDefinitions, types.ColumnDefinition, "column definitions", out result); + } + + if (type.Equals(types.RowDefinitions)) + { + return ConvertDefinitionList(node, text, types, types.RowDefinitions, types.RowDefinition, "row definitions", out result); + } + if (type.FullName == "Avalonia.AvaloniaProperty") { var scope = context.ParentNodes().OfType().FirstOrDefault(); @@ -366,5 +376,43 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions result = null; return false; } + + private static bool ConvertDefinitionList( + IXamlAstValueNode node, + string text, + AvaloniaXamlIlWellKnownTypes types, + IXamlType listType, + IXamlType elementType, + string errorDisplayName, + out IXamlAstValueNode result) + { + try + { + var lengths = GridLength.ParseLengths(text); + + var definitionTypeRef = new XamlAstClrTypeReference(node, elementType, false); + + var definitionConstructorGridLength = elementType.GetConstructor(new List {types.GridLength}); + + IXamlAstValueNode CreateDefinitionNode(GridLength length) + { + var lengthNode = new AvaloniaXamlIlGridLengthAstNode(node, types, length); + + return new XamlAstNewClrObjectNode(node, definitionTypeRef, + definitionConstructorGridLength, new List {lengthNode}); + } + + var definitionNodes = + new List(lengths.Select(CreateDefinitionNode)); + + result = new AvaloniaXamlIlAvaloniaListConstantAstNode(node, types, listType, elementType, definitionNodes); + + return true; + } + catch + { + throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a {errorDisplayName}", node); + } + } } } diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs index f046778429..125701ca9e 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs @@ -50,6 +50,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers public IXamlType RelativeSource { get; } public IXamlType UInt { get; } + public IXamlType Int { get; } public IXamlType Long { get; } public IXamlType Uri { get; } public IXamlType FontFamily { get; } @@ -72,6 +73,10 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers public IXamlType StandardCursorType { get; } public IXamlType Cursor { get; } public IXamlConstructor CursorTypeConstructor { get; } + public IXamlType RowDefinition { get; } + public IXamlType RowDefinitions { get; } + public IXamlType ColumnDefinition { get; } + public IXamlType ColumnDefinitions { get; } public AvaloniaXamlIlWellKnownTypes(TransformerConfiguration cfg) { @@ -130,6 +135,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers ReflectionBindingExtension = cfg.TypeSystem.GetType("Avalonia.Markup.Xaml.MarkupExtensions.ReflectionBindingExtension"); RelativeSource = cfg.TypeSystem.GetType("Avalonia.Data.RelativeSource"); UInt = cfg.TypeSystem.GetType("System.UInt32"); + Int = cfg.TypeSystem.GetType("System.Int32"); Long = cfg.TypeSystem.GetType("System.Int64"); Uri = cfg.TypeSystem.GetType("System.Uri"); FontFamily = cfg.TypeSystem.GetType("Avalonia.Media.FontFamily"); @@ -156,6 +162,10 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers StandardCursorType = cfg.TypeSystem.GetType("Avalonia.Input.StandardCursorType"); Cursor = cfg.TypeSystem.GetType("Avalonia.Input.Cursor"); CursorTypeConstructor = Cursor.GetConstructor(new List { StandardCursorType }); + ColumnDefinition = cfg.TypeSystem.GetType("Avalonia.Controls.ColumnDefinition"); + ColumnDefinitions = cfg.TypeSystem.GetType("Avalonia.Controls.ColumnDefinitions"); + RowDefinition = cfg.TypeSystem.GetType("Avalonia.Controls.RowDefinition"); + RowDefinitions = cfg.TypeSystem.GetType("Avalonia.Controls.RowDefinitions"); } } From aefa58bdc4b1dabf5f8bb8832867b01ca5e39d26 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Sun, 22 Nov 2020 22:43:25 +0100 Subject: [PATCH 066/100] Optimize DefinitionList collection changed handler. --- src/Avalonia.Controls/DefinitionList.cs | 60 +++++++++++++++++-------- 1 file changed, 41 insertions(+), 19 deletions(-) diff --git a/src/Avalonia.Controls/DefinitionList.cs b/src/Avalonia.Controls/DefinitionList.cs index bb815171c0..ad0e2513c4 100644 --- a/src/Avalonia.Controls/DefinitionList.cs +++ b/src/Avalonia.Controls/DefinitionList.cs @@ -1,8 +1,9 @@ -using System; +using System.Collections; using System.Collections.Specialized; -using System.Linq; using Avalonia.Collections; +#nullable enable + namespace Avalonia.Controls { public abstract class DefinitionList : AvaloniaList where T : DefinitionBase @@ -14,44 +15,65 @@ namespace Avalonia.Controls } internal bool IsDirty = true; - private Grid _parent; + private Grid? _parent; - internal Grid Parent + internal Grid? Parent { get => _parent; set => SetParent(value); } - private void SetParent(Grid value) + private void SetParent(Grid? value) { _parent = value; - foreach (var pair in this.Select((definitions, index) => (definitions, index))) + var idx = 0; + + foreach (T definition in this) { - pair.definitions.Parent = value; - pair.definitions.Index = pair.index; + definition.Parent = value; + definition.Index = idx++; } } internal void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { - foreach (var nI in this.Select((d, i) => (d, i))) - nI.d._parentIndex = nI.i; + var idx = 0; - foreach (var nD in e.NewItems?.Cast() - ?? Enumerable.Empty()) + foreach (T definition in this) { - nD.Parent = this.Parent; - nD.OnEnterParentTree(); + definition.Index = idx++; } + + UpdateDefinitionParent(e.NewItems, false); + UpdateDefinitionParent(e.OldItems, true); + + IsDirty = true; + } - foreach (var oD in e.OldItems?.Cast() - ?? Enumerable.Empty()) + private void UpdateDefinitionParent(IList? items, bool wasRemoved) + { + if (items is null) { - oD.OnExitParentTree(); + return; } + + var count = items.Count; - IsDirty = true; + for (var i = 0; i < count; i++) + { + var definition = (DefinitionBase) items[i]; + + if (wasRemoved) + { + definition.OnExitParentTree(); + } + else + { + definition.Parent = Parent; + definition.OnEnterParentTree(); + } + } } } -} \ No newline at end of file +} From 8a2701c84b0e8f7daac2a3ff06be5f440d073b20 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Wed, 25 Nov 2020 12:48:45 +0100 Subject: [PATCH 067/100] Move parsing code to a separate file. --- .../AvaloniaXamlIlLanguage.cs | 219 +--------------- .../AvaloniaXamlIlLanguageParseIntrinsics.cs | 243 ++++++++++++++++++ 2 files changed, 244 insertions(+), 218 deletions(-) create mode 100644 src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguageParseIntrinsics.cs diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguage.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguage.cs index 10c5e615bb..a82f5b9e60 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguage.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguage.cs @@ -177,192 +177,13 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions } var text = textNode.Text; - var types = context.GetAvaloniaTypes(); - if (type.FullName == "System.TimeSpan") - { - var tsText = text.Trim(); - - if (!TimeSpan.TryParse(tsText, CultureInfo.InvariantCulture, out var timeSpan)) - { - // // shorthand seconds format (ie. "0.25") - if (!tsText.Contains(":") && double.TryParse(tsText, - NumberStyles.Float | NumberStyles.AllowThousands, - CultureInfo.InvariantCulture, out var seconds)) - timeSpan = TimeSpan.FromSeconds(seconds); - else - throw new XamlX.XamlLoadException($"Unable to parse {text} as a time span", node); - } - - - result = new XamlStaticOrTargetedReturnMethodCallNode(node, - type.FindMethod("FromTicks", type, false, types.Long), - new[] { new XamlConstantNode(node, types.Long, timeSpan.Ticks) }); - return true; - } - - if (type.Equals(types.FontFamily)) + if (AvaloniaXamlIlLanguageParseIntrinsics.TryConvert(context, node, text, type, types, out result)) { - result = new AvaloniaXamlIlFontFamilyAstNode(types, text, node); return true; } - - if (type.Equals(types.Thickness)) - { - try - { - var thickness = Thickness.Parse(text); - - result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Thickness, types.ThicknessFullConstructor, - new[] { thickness.Left, thickness.Top, thickness.Right, thickness.Bottom }); - - return true; - } - catch - { - throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a thickness", node); - } - } - - if (type.Equals(types.Point)) - { - try - { - var point = Point.Parse(text); - - result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Point, types.PointFullConstructor, - new[] { point.X, point.Y }); - - return true; - } - catch - { - throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a point", node); - } - } - - if (type.Equals(types.Vector)) - { - try - { - var vector = Vector.Parse(text); - - result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Vector, types.VectorFullConstructor, - new[] { vector.X, vector.Y }); - - return true; - } - catch - { - throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a vector", node); - } - } - - if (type.Equals(types.Size)) - { - try - { - var size = Size.Parse(text); - - result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Size, types.SizeFullConstructor, - new[] { size.Width, size.Height }); - - return true; - } - catch - { - throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a size", node); - } - } - - if (type.Equals(types.Matrix)) - { - try - { - var matrix = Matrix.Parse(text); - - result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Matrix, types.MatrixFullConstructor, - new[] { matrix.M11, matrix.M12, matrix.M21, matrix.M22, matrix.M31, matrix.M32 }); - - return true; - } - catch - { - throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a matrix", node); - } - } - if (type.Equals(types.CornerRadius)) - { - try - { - var cornerRadius = CornerRadius.Parse(text); - - result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.CornerRadius, types.CornerRadiusFullConstructor, - new[] { cornerRadius.TopLeft, cornerRadius.TopRight, cornerRadius.BottomRight, cornerRadius.BottomLeft }); - - return true; - } - catch - { - throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a corner radius", node); - } - } - - if (type.Equals(types.Color)) - { - if (!Color.TryParse(text, out Color color)) - { - throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a color", node); - } - - result = new XamlStaticOrTargetedReturnMethodCallNode(node, - type.GetMethod( - new FindMethodMethodSignature("FromUInt32", type, types.UInt) { IsStatic = true }), - new[] { new XamlConstantNode(node, types.UInt, color.ToUint32()) }); - - return true; - } - - if (type.Equals(types.GridLength)) - { - try - { - var gridLength = GridLength.Parse(text); - - result = new AvaloniaXamlIlGridLengthAstNode(node, types, gridLength); - - return true; - } - catch - { - throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a grid length", node); - } - } - - if (type.Equals(types.Cursor)) - { - if (TypeSystemHelpers.TryGetEnumValueNode(types.StandardCursorType, text, node, out var enumConstantNode)) - { - var cursorTypeRef = new XamlAstClrTypeReference(node, types.Cursor, false); - - result = new XamlAstNewClrObjectNode(node, cursorTypeRef, types.CursorTypeConstructor, new List { enumConstantNode }); - - return true; - } - } - - if (type.Equals(types.ColumnDefinitions)) - { - return ConvertDefinitionList(node, text, types, types.ColumnDefinitions, types.ColumnDefinition, "column definitions", out result); - } - - if (type.Equals(types.RowDefinitions)) - { - return ConvertDefinitionList(node, text, types, types.RowDefinitions, types.RowDefinition, "row definitions", out result); - } - if (type.FullName == "Avalonia.AvaloniaProperty") { var scope = context.ParentNodes().OfType().FirstOrDefault(); @@ -376,43 +197,5 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions result = null; return false; } - - private static bool ConvertDefinitionList( - IXamlAstValueNode node, - string text, - AvaloniaXamlIlWellKnownTypes types, - IXamlType listType, - IXamlType elementType, - string errorDisplayName, - out IXamlAstValueNode result) - { - try - { - var lengths = GridLength.ParseLengths(text); - - var definitionTypeRef = new XamlAstClrTypeReference(node, elementType, false); - - var definitionConstructorGridLength = elementType.GetConstructor(new List {types.GridLength}); - - IXamlAstValueNode CreateDefinitionNode(GridLength length) - { - var lengthNode = new AvaloniaXamlIlGridLengthAstNode(node, types, length); - - return new XamlAstNewClrObjectNode(node, definitionTypeRef, - definitionConstructorGridLength, new List {lengthNode}); - } - - var definitionNodes = - new List(lengths.Select(CreateDefinitionNode)); - - result = new AvaloniaXamlIlAvaloniaListConstantAstNode(node, types, listType, elementType, definitionNodes); - - return true; - } - catch - { - throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a {errorDisplayName}", node); - } - } } } diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguageParseIntrinsics.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguageParseIntrinsics.cs new file mode 100644 index 0000000000..7c4cc8d28b --- /dev/null +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguageParseIntrinsics.cs @@ -0,0 +1,243 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using Avalonia.Controls; +using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.AstNodes; +using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers; +using Avalonia.Media; +using XamlX.Ast; +using XamlX.Transform; +using XamlX.TypeSystem; + +namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions +{ + class AvaloniaXamlIlLanguageParseIntrinsics + { + public static bool TryConvert(AstTransformationContext context, IXamlAstValueNode node, string text, IXamlType type, AvaloniaXamlIlWellKnownTypes types, out IXamlAstValueNode result) + { + if (type.FullName == "System.TimeSpan") + { + var tsText = text.Trim(); + + if (!TimeSpan.TryParse(tsText, CultureInfo.InvariantCulture, out var timeSpan)) + { + // // shorthand seconds format (ie. "0.25") + if (!tsText.Contains(":") && double.TryParse(tsText, + NumberStyles.Float | NumberStyles.AllowThousands, + CultureInfo.InvariantCulture, out var seconds)) + timeSpan = TimeSpan.FromSeconds(seconds); + else + throw new XamlX.XamlLoadException($"Unable to parse {text} as a time span", node); + } + + result = new XamlStaticOrTargetedReturnMethodCallNode(node, + type.FindMethod("FromTicks", type, false, types.Long), + new[] { new XamlConstantNode(node, types.Long, timeSpan.Ticks) }); + return true; + } + + if (type.Equals(types.FontFamily)) + { + result = new AvaloniaXamlIlFontFamilyAstNode(types, text, node); + return true; + } + + if (type.Equals(types.Thickness)) + { + try + { + var thickness = Thickness.Parse(text); + + result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Thickness, types.ThicknessFullConstructor, + new[] { thickness.Left, thickness.Top, thickness.Right, thickness.Bottom }); + + return true; + } + catch + { + throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a thickness", node); + } + } + + if (type.Equals(types.Point)) + { + try + { + var point = Point.Parse(text); + + result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Point, types.PointFullConstructor, + new[] { point.X, point.Y }); + + return true; + } + catch + { + throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a point", node); + } + } + + if (type.Equals(types.Vector)) + { + try + { + var vector = Vector.Parse(text); + + result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Vector, types.VectorFullConstructor, + new[] { vector.X, vector.Y }); + + return true; + } + catch + { + throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a vector", node); + } + } + + if (type.Equals(types.Size)) + { + try + { + var size = Size.Parse(text); + + result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Size, types.SizeFullConstructor, + new[] { size.Width, size.Height }); + + return true; + } + catch + { + throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a size", node); + } + } + + if (type.Equals(types.Matrix)) + { + try + { + var matrix = Matrix.Parse(text); + + result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Matrix, types.MatrixFullConstructor, + new[] { matrix.M11, matrix.M12, matrix.M21, matrix.M22, matrix.M31, matrix.M32 }); + + return true; + } + catch + { + throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a matrix", node); + } + } + + if (type.Equals(types.CornerRadius)) + { + try + { + var cornerRadius = CornerRadius.Parse(text); + + result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.CornerRadius, types.CornerRadiusFullConstructor, + new[] { cornerRadius.TopLeft, cornerRadius.TopRight, cornerRadius.BottomRight, cornerRadius.BottomLeft }); + + return true; + } + catch + { + throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a corner radius", node); + } + } + + if (type.Equals(types.Color)) + { + if (!Color.TryParse(text, out Color color)) + { + throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a color", node); + } + + result = new XamlStaticOrTargetedReturnMethodCallNode(node, + type.GetMethod( + new FindMethodMethodSignature("FromUInt32", type, types.UInt) { IsStatic = true }), + new[] { new XamlConstantNode(node, types.UInt, color.ToUint32()) }); + + return true; + } + + if (type.Equals(types.GridLength)) + { + try + { + var gridLength = GridLength.Parse(text); + + result = new AvaloniaXamlIlGridLengthAstNode(node, types, gridLength); + + return true; + } + catch + { + throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a grid length", node); + } + } + + if (type.Equals(types.Cursor)) + { + if (TypeSystemHelpers.TryGetEnumValueNode(types.StandardCursorType, text, node, out var enumConstantNode)) + { + var cursorTypeRef = new XamlAstClrTypeReference(node, types.Cursor, false); + + result = new XamlAstNewClrObjectNode(node, cursorTypeRef, types.CursorTypeConstructor, new List { enumConstantNode }); + + return true; + } + } + + if (type.Equals(types.ColumnDefinitions)) + { + return ConvertDefinitionList(node, text, types, types.ColumnDefinitions, types.ColumnDefinition, "column definitions", out result); + } + + if (type.Equals(types.RowDefinitions)) + { + return ConvertDefinitionList(node, text, types, types.RowDefinitions, types.RowDefinition, "row definitions", out result); + } + + result = null; + return false; + } + + private static bool ConvertDefinitionList( + IXamlAstValueNode node, + string text, + AvaloniaXamlIlWellKnownTypes types, + IXamlType listType, + IXamlType elementType, + string errorDisplayName, + out IXamlAstValueNode result) + { + try + { + var lengths = GridLength.ParseLengths(text); + + var definitionTypeRef = new XamlAstClrTypeReference(node, elementType, false); + + var definitionConstructorGridLength = elementType.GetConstructor(new List {types.GridLength}); + + IXamlAstValueNode CreateDefinitionNode(GridLength length) + { + var lengthNode = new AvaloniaXamlIlGridLengthAstNode(node, types, length); + + return new XamlAstNewClrObjectNode(node, definitionTypeRef, + definitionConstructorGridLength, new List {lengthNode}); + } + + var definitionNodes = + new List(lengths.Select(CreateDefinitionNode)); + + result = new AvaloniaXamlIlAvaloniaListConstantAstNode(node, types, listType, elementType, definitionNodes); + + return true; + } + catch + { + throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a {errorDisplayName}", node); + } + } + } +} From 286ca73fdd417c363991b3d790e57d030835826b Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 25 Nov 2020 15:15:30 +0100 Subject: [PATCH 068/100] Prevent hangs on win32 in case of clipboard errors. --- src/Windows/Avalonia.Win32/ClipboardImpl.cs | 37 +++++++++++++++++---- 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/src/Windows/Avalonia.Win32/ClipboardImpl.cs b/src/Windows/Avalonia.Win32/ClipboardImpl.cs index 7d9e0a8bd2..0475a9d607 100644 --- a/src/Windows/Avalonia.Win32/ClipboardImpl.cs +++ b/src/Windows/Avalonia.Win32/ClipboardImpl.cs @@ -12,6 +12,9 @@ namespace Avalonia.Win32 { internal class ClipboardImpl : IClipboard { + private const int OleRetryCount = 10; + private const int OleRetryDelay = 100; + private async Task OpenClipboard() { while (!UnmanagedMethods.OpenClipboard(IntPtr.Zero)) @@ -72,20 +75,32 @@ namespace Avalonia.Win32 { Dispatcher.UIThread.VerifyAccess(); var wrapper = new DataObject(data); + var i = OleRetryCount; + while (true) { - if (UnmanagedMethods.OleSetClipboard(wrapper) == 0) + var hr = UnmanagedMethods.OleSetClipboard(wrapper); + + if (hr == 0) break; - await Task.Delay(100); + + if (--i == 0) + Marshal.ThrowExceptionForHR(hr); + + await Task.Delay(OleRetryDelay); } } public async Task GetFormatsAsync() { Dispatcher.UIThread.VerifyAccess(); + var i = OleRetryCount; + while (true) { - if (UnmanagedMethods.OleGetClipboard(out var dataObject) == 0) + var hr = UnmanagedMethods.OleGetClipboard(out var dataObject); + + if (hr == 0) { var wrapper = new OleDataObject(dataObject); var formats = wrapper.GetDataFormats().ToArray(); @@ -93,16 +108,23 @@ namespace Avalonia.Win32 return formats; } - await Task.Delay(100); + if (--i == 0) + Marshal.ThrowExceptionForHR(hr); + + await Task.Delay(OleRetryDelay); } } public async Task GetDataAsync(string format) { Dispatcher.UIThread.VerifyAccess(); + var i = OleRetryCount; + while (true) { - if (UnmanagedMethods.OleGetClipboard(out var dataObject) == 0) + var hr = UnmanagedMethods.OleGetClipboard(out var dataObject); + + if (hr == 0) { var wrapper = new OleDataObject(dataObject); var rv = wrapper.Get(format); @@ -110,7 +132,10 @@ namespace Avalonia.Win32 return rv; } - await Task.Delay(100); + if (--i == 0) + Marshal.ThrowExceptionForHR(hr); + + await Task.Delay(OleRetryDelay); } } } From a088910f313635144559d3eabc9b007d6a15ac98 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 25 Nov 2020 19:06:37 +0000 Subject: [PATCH 069/100] Add OSX implementation. --- native/Avalonia.Native/src/OSX/window.mm | 8 ++++---- src/Avalonia.Native/PopupImpl.cs | 4 ++-- src/Avalonia.Native/WindowImplBase.cs | 4 ++-- src/Avalonia.Native/avn.idl | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm index e3996a1fae..8419258fe9 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -106,13 +106,13 @@ public: return Window; } - virtual HRESULT Show() override + virtual HRESULT Show(bool activate) override { @autoreleasepool { SetPosition(lastPositionSet); UpdateStyle(); - if(ShouldTakeFocusOnShow()) + if(ShouldTakeFocusOnShow() && activate) { [Window makeKeyAndOrderFront:Window]; [NSApp activateIgnoringOtherApps:YES]; @@ -561,11 +561,11 @@ private: } } - virtual HRESULT Show () override + virtual HRESULT Show (bool activate) override { @autoreleasepool { - WindowBaseImpl::Show(); + WindowBaseImpl::Show(activate); HideOrShowTrafficLights(); diff --git a/src/Avalonia.Native/PopupImpl.cs b/src/Avalonia.Native/PopupImpl.cs index 60c552a937..c36675afcd 100644 --- a/src/Avalonia.Native/PopupImpl.cs +++ b/src/Avalonia.Native/PopupImpl.cs @@ -60,14 +60,14 @@ namespace Avalonia.Native } } - public override void Show() + public override void Show(bool activate) { var parent = _parent; while (parent is PopupImpl p) parent = p._parent; if (parent is WindowImpl w) w.Native.TakeFocusFromChildren(); - base.Show(); + base.Show(false); } public override IPopupImpl CreatePopup() => new PopupImpl(_factory, _opts, _glFeature, this); diff --git a/src/Avalonia.Native/WindowImplBase.cs b/src/Avalonia.Native/WindowImplBase.cs index 88c3144884..927b739b0c 100644 --- a/src/Avalonia.Native/WindowImplBase.cs +++ b/src/Avalonia.Native/WindowImplBase.cs @@ -351,9 +351,9 @@ namespace Avalonia.Native } - public virtual void Show() + public virtual void Show(bool activate) { - _native.Show(); + _native.Show(activate.AsComBool()); } diff --git a/src/Avalonia.Native/avn.idl b/src/Avalonia.Native/avn.idl index 3f33476c3d..4f7e5833a3 100644 --- a/src/Avalonia.Native/avn.idl +++ b/src/Avalonia.Native/avn.idl @@ -225,7 +225,7 @@ interface IAvnString : IUnknown [uuid(e5aca675-02b7-4129-aa79-d6e417210bda)] interface IAvnWindowBase : IUnknown { - HRESULT Show(); + HRESULT Show(bool activate); HRESULT Hide(); HRESULT Close(); HRESULT Activate(); From 43078d720a6ca398795cbb081e4b588c2ca94425 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 25 Nov 2020 19:07:17 +0000 Subject: [PATCH 070/100] fix apicompat baselines. --- .../ApiCompatBaseline.txt | 6 +--- src/Avalonia.Controls/ApiCompatBaseline.txt | 29 +++---------------- .../ApiCompatBaseline.txt | 5 +--- 3 files changed, 6 insertions(+), 34 deletions(-) diff --git a/src/Avalonia.Controls.DataGrid/ApiCompatBaseline.txt b/src/Avalonia.Controls.DataGrid/ApiCompatBaseline.txt index 82472c505a..fcc74cf864 100644 --- a/src/Avalonia.Controls.DataGrid/ApiCompatBaseline.txt +++ b/src/Avalonia.Controls.DataGrid/ApiCompatBaseline.txt @@ -1,5 +1 @@ -Compat issues with assembly Avalonia.Controls.DataGrid: -MembersMustExist : Member 'public Avalonia.StyledProperty Avalonia.StyledProperty Avalonia.Controls.DataGridTextColumn.FontFamilyProperty' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public System.String Avalonia.Controls.DataGridTextColumn.FontFamily.get()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Controls.DataGridTextColumn.FontFamily.set(System.String)' does not exist in the implementation but it does exist in the contract. -Total Issues: 3 +Total Issues: 0 diff --git a/src/Avalonia.Controls/ApiCompatBaseline.txt b/src/Avalonia.Controls/ApiCompatBaseline.txt index 16c801201c..7488ef8149 100644 --- a/src/Avalonia.Controls/ApiCompatBaseline.txt +++ b/src/Avalonia.Controls/ApiCompatBaseline.txt @@ -1,26 +1,5 @@ Compat issues with assembly Avalonia.Controls: -TypesMustExist : Type 'Avalonia.Controls.IndexPath' does not exist in the implementation but it does exist in the contract. -TypesMustExist : Type 'Avalonia.Controls.ISelectedItemInfo' does not exist in the implementation but it does exist in the contract. -TypesMustExist : Type 'Avalonia.Controls.ISelectionModel' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.DirectProperty Avalonia.DirectProperty Avalonia.Controls.ListBox.SelectionProperty' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Controls.ISelectionModel Avalonia.Controls.ListBox.Selection.get()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Controls.ListBox.Selection.set(Avalonia.Controls.ISelectionModel)' does not exist in the implementation but it does exist in the contract. -TypesMustExist : Type 'Avalonia.Controls.SelectionModel' does not exist in the implementation but it does exist in the contract. -TypesMustExist : Type 'Avalonia.Controls.SelectionModelChildrenRequestedEventArgs' does not exist in the implementation but it does exist in the contract. -TypesMustExist : Type 'Avalonia.Controls.SelectionModelSelectionChangedEventArgs' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.StyledProperty Avalonia.StyledProperty Avalonia.Controls.SplitView.ContentProperty' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.StyledProperty Avalonia.StyledProperty Avalonia.Controls.SplitView.PaneProperty' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Controls.IControl Avalonia.Controls.SplitView.Content.get()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Controls.SplitView.Content.set(Avalonia.Controls.IControl)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Controls.IControl Avalonia.Controls.SplitView.Pane.get()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Controls.SplitView.Pane.set(Avalonia.Controls.IControl)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.DirectProperty Avalonia.DirectProperty Avalonia.Controls.TreeView.SelectionProperty' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Interactivity.RoutedEvent Avalonia.Interactivity.RoutedEvent Avalonia.Controls.TreeView.SelectionChangedEvent' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Controls.ISelectionModel Avalonia.Controls.TreeView.Selection.get()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Controls.TreeView.Selection.set(Avalonia.Controls.ISelectionModel)' does not exist in the implementation but it does exist in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public System.String[] Avalonia.Controls.ApplicationLifetimes.IClassicDesktopStyleApplicationLifetime.Args' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public System.String[] Avalonia.Controls.ApplicationLifetimes.IClassicDesktopStyleApplicationLifetime.Args.get()' is present in the implementation but not in the contract. -MembersMustExist : Member 'public Avalonia.DirectProperty Avalonia.DirectProperty Avalonia.Controls.Primitives.SelectingItemsControl.SelectionProperty' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'protected Avalonia.Controls.ISelectionModel Avalonia.Controls.Primitives.SelectingItemsControl.Selection.get()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'protected void Avalonia.Controls.Primitives.SelectingItemsControl.Selection.set(Avalonia.Controls.ISelectionModel)' does not exist in the implementation but it does exist in the contract. -Total Issues: 24 +InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.IWindowBaseImpl.Show()' is present in the contract but not in the implementation. +MembersMustExist : Member 'public void Avalonia.Platform.IWindowBaseImpl.Show()' does not exist in the implementation but it does exist in the contract. +InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.IWindowBaseImpl.Show(System.Boolean)' is present in the implementation but not in the contract. +Total Issues: 3 diff --git a/src/Markup/Avalonia.Markup.Xaml/ApiCompatBaseline.txt b/src/Markup/Avalonia.Markup.Xaml/ApiCompatBaseline.txt index c87dcacda4..fcc74cf864 100644 --- a/src/Markup/Avalonia.Markup.Xaml/ApiCompatBaseline.txt +++ b/src/Markup/Avalonia.Markup.Xaml/ApiCompatBaseline.txt @@ -1,4 +1 @@ -Compat issues with assembly Avalonia.Markup.Xaml: -MembersMustExist : Member 'public Avalonia.Data.Binding Avalonia.Markup.Xaml.Templates.TreeDataTemplate.ItemsSource.get()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Markup.Xaml.Templates.TreeDataTemplate.ItemsSource.set(Avalonia.Data.Binding)' does not exist in the implementation but it does exist in the contract. -Total Issues: 2 +Total Issues: 0 From d438e0e954c8b3e8d6e29eb1a996f0f86b5d77c1 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 26 Nov 2020 09:41:19 +0100 Subject: [PATCH 071/100] Prevent infinite loop on OpenClipboard. --- src/Windows/Avalonia.Win32/ClipboardImpl.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Windows/Avalonia.Win32/ClipboardImpl.cs b/src/Windows/Avalonia.Win32/ClipboardImpl.cs index 0475a9d607..047b7c361f 100644 --- a/src/Windows/Avalonia.Win32/ClipboardImpl.cs +++ b/src/Windows/Avalonia.Win32/ClipboardImpl.cs @@ -17,8 +17,12 @@ namespace Avalonia.Win32 private async Task OpenClipboard() { + var i = OleRetryCount; + while (!UnmanagedMethods.OpenClipboard(IntPtr.Zero)) { + if (--i == 0) + throw new TimeoutException("Timeout opening clipboard."); await Task.Delay(100); } From 0cb25c78c95dd382a8368bd422c8ec3222ceff3e Mon Sep 17 00:00:00 2001 From: Loris Bognanni Date: Thu, 26 Nov 2020 09:01:08 +0000 Subject: [PATCH 072/100] Only handle the Enter key when the DropDown is open Fixes #5106 --- src/Avalonia.Controls/AutoCompleteBox.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/AutoCompleteBox.cs b/src/Avalonia.Controls/AutoCompleteBox.cs index bfd633c947..b59fd7abde 100644 --- a/src/Avalonia.Controls/AutoCompleteBox.cs +++ b/src/Avalonia.Controls/AutoCompleteBox.cs @@ -1405,8 +1405,11 @@ namespace Avalonia.Controls break; case Key.Enter: - OnAdapterSelectionComplete(this, new RoutedEventArgs()); - e.Handled = true; + if (IsDropDownOpen) + { + OnAdapterSelectionComplete(this, new RoutedEventArgs()); + e.Handled = true; + } break; default: From 0e7e861009721dd7f0c1c964d5732d4105dab8cf Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Thu, 26 Nov 2020 12:13:58 +0100 Subject: [PATCH 073/100] Fix TransformToVisual not dealing with non-invertible transforms. --- src/Avalonia.Visuals/Matrix.cs | 25 ++++++++++++++++--- src/Avalonia.Visuals/VisualExtensions.cs | 10 +++++++- .../Avalonia.Visuals.UnitTests/VisualTests.cs | 19 ++++++++++++++ 3 files changed, 49 insertions(+), 5 deletions(-) diff --git a/src/Avalonia.Visuals/Matrix.cs b/src/Avalonia.Visuals/Matrix.cs index 2ccfd43f03..ea7d0d63af 100644 --- a/src/Avalonia.Visuals/Matrix.cs +++ b/src/Avalonia.Visuals/Matrix.cs @@ -282,16 +282,16 @@ namespace Avalonia } /// - /// Inverts the Matrix. + /// Attempts to invert the Matrix. /// - /// The inverted matrix. - public Matrix Invert() + /// The inverted matrix or when matrix is not invertible. + public Matrix? TryInvert() { double d = GetDeterminant(); if (MathUtilities.IsZero(d)) { - throw new InvalidOperationException("Transform is not invertible."); + return null; } return new Matrix( @@ -303,6 +303,23 @@ namespace Avalonia ((_m12 * _m31) - (_m11 * _m32)) / d); } + /// + /// Inverts the Matrix. + /// + /// Matrix is not invertible. + /// The inverted matrix. + public Matrix Invert() + { + Matrix? inverted = TryInvert(); + + if (!inverted.HasValue) + { + throw new InvalidOperationException("Transform is not invertible."); + } + + return inverted.Value; + } + /// /// Parses a string. /// diff --git a/src/Avalonia.Visuals/VisualExtensions.cs b/src/Avalonia.Visuals/VisualExtensions.cs index 6079e5941f..6875e2c55a 100644 --- a/src/Avalonia.Visuals/VisualExtensions.cs +++ b/src/Avalonia.Visuals/VisualExtensions.cs @@ -50,7 +50,15 @@ namespace Avalonia { var thisOffset = GetOffsetFrom(common, from); var thatOffset = GetOffsetFrom(common, to); - return -thatOffset * thisOffset; + + var thatOffsetInverted = thatOffset.TryInvert(); + + if (!thatOffsetInverted.HasValue) + { + return null; + } + + return thatOffsetInverted.Value * thisOffset; } return null; diff --git a/tests/Avalonia.Visuals.UnitTests/VisualTests.cs b/tests/Avalonia.Visuals.UnitTests/VisualTests.cs index 447a68aa69..38131fbfca 100644 --- a/tests/Avalonia.Visuals.UnitTests/VisualTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/VisualTests.cs @@ -235,6 +235,25 @@ namespace Avalonia.Visuals.UnitTests Assert.Equal(new Point(100, 100), point); } + [Fact] + public void TransformToVisual_With_NonInvertible_RenderTransform_Should_Work() + { + var child = new Decorator + { + Width = 100, + Height = 100, + RenderTransform = new ScaleTransform() { ScaleX = 0, ScaleY = 0 } + }; + var root = new TestRoot() { Child = child, Width = 400, Height = 400 }; + + root.Measure(Size.Infinity); + root.Arrange(new Rect(new Point(), root.DesiredSize)); + + var tr = root.TransformToVisual(child); + + Assert.Null(tr); + } + [Fact] public void Should_Not_Log_Binding_Error_When_Not_Attached_To_Logical_Tree() { From 713d4698ee66fc524eb480e69e7f14778b682e56 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Thu, 26 Nov 2020 12:25:35 +0100 Subject: [PATCH 074/100] Use different Try convention. --- src/Avalonia.Visuals/Matrix.cs | 16 +++++++++------- src/Avalonia.Visuals/VisualExtensions.cs | 8 +++----- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Avalonia.Visuals/Matrix.cs b/src/Avalonia.Visuals/Matrix.cs index ea7d0d63af..8136f843df 100644 --- a/src/Avalonia.Visuals/Matrix.cs +++ b/src/Avalonia.Visuals/Matrix.cs @@ -285,22 +285,26 @@ namespace Avalonia /// Attempts to invert the Matrix. /// /// The inverted matrix or when matrix is not invertible. - public Matrix? TryInvert() + public bool TryInvert(out Matrix inverted) { double d = GetDeterminant(); if (MathUtilities.IsZero(d)) { - return null; + inverted = default; + + return false; } - return new Matrix( + inverted = new Matrix( _m22 / d, -_m12 / d, -_m21 / d, _m11 / d, ((_m21 * _m32) - (_m22 * _m31)) / d, ((_m12 * _m31) - (_m11 * _m32)) / d); + + return true; } /// @@ -310,14 +314,12 @@ namespace Avalonia /// The inverted matrix. public Matrix Invert() { - Matrix? inverted = TryInvert(); - - if (!inverted.HasValue) + if (!TryInvert(out Matrix inverted)) { throw new InvalidOperationException("Transform is not invertible."); } - return inverted.Value; + return inverted; } /// diff --git a/src/Avalonia.Visuals/VisualExtensions.cs b/src/Avalonia.Visuals/VisualExtensions.cs index 6875e2c55a..e6523a1469 100644 --- a/src/Avalonia.Visuals/VisualExtensions.cs +++ b/src/Avalonia.Visuals/VisualExtensions.cs @@ -51,14 +51,12 @@ namespace Avalonia var thisOffset = GetOffsetFrom(common, from); var thatOffset = GetOffsetFrom(common, to); - var thatOffsetInverted = thatOffset.TryInvert(); - - if (!thatOffsetInverted.HasValue) + if (!thatOffset.TryInvert(out var thatOffsetInverted)) { return null; } - - return thatOffsetInverted.Value * thisOffset; + + return thatOffsetInverted * thisOffset; } return null; From 55967506345e289c5328e54e7991988f314e8af7 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 26 Nov 2020 11:36:47 +0000 Subject: [PATCH 075/100] initial attempt at detecting chrome double click. --- src/Avalonia.Native/DoubleClickHelper.cs | 31 ++++++++++++++++++++++++ src/Avalonia.Native/WindowImpl.cs | 22 ++++++++++++++++- 2 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 src/Avalonia.Native/DoubleClickHelper.cs diff --git a/src/Avalonia.Native/DoubleClickHelper.cs b/src/Avalonia.Native/DoubleClickHelper.cs new file mode 100644 index 0000000000..7618d6976a --- /dev/null +++ b/src/Avalonia.Native/DoubleClickHelper.cs @@ -0,0 +1,31 @@ +using Avalonia.Platform; + +namespace Avalonia.Native +{ + internal class DoubleClickHelper + { + private int _clickCount; + private Rect _lastClickRect; + private ulong _lastClickTime; + + public bool IsDoubleClick( + ulong timestamp, + Point p) + { + var settings = AvaloniaLocator.Current.GetService(); + var doubleClickTime = settings.DoubleClickTime.TotalMilliseconds; + + if (!_lastClickRect.Contains(p) || timestamp - _lastClickTime > doubleClickTime) + { + _clickCount = 0; + } + + ++_clickCount; + _lastClickTime = timestamp; + _lastClickRect = new Rect(p, new Size()) + .Inflate(new Thickness(settings.DoubleClickSize.Width / 2, settings.DoubleClickSize.Height / 2)); + + return _clickCount == 2; + } + } +} diff --git a/src/Avalonia.Native/WindowImpl.cs b/src/Avalonia.Native/WindowImpl.cs index f60e83efe3..f3b60f07be 100644 --- a/src/Avalonia.Native/WindowImpl.cs +++ b/src/Avalonia.Native/WindowImpl.cs @@ -1,4 +1,5 @@ using System; +using Avalonia; using Avalonia.Controls; using Avalonia.Controls.Platform; using Avalonia.Input; @@ -17,6 +18,8 @@ namespace Avalonia.Native private readonly AvaloniaNativePlatformOpenGlInterface _glFeature; IAvnWindow _native; private double _extendTitleBarHeight = -1; + private DoubleClickHelper _doubleClickHelper; + internal WindowImpl(IAvaloniaNativeFactory factory, AvaloniaNativePlatformOptions opts, AvaloniaNativePlatformOpenGlInterface glFeature) : base(opts, glFeature) @@ -24,6 +27,8 @@ namespace Avalonia.Native _factory = factory; _opts = opts; _glFeature = glFeature; + _doubleClickHelper = new DoubleClickHelper(); + using (var e = new WindowEvents(this)) { var context = _opts.UseGpu ? glFeature?.MainContext : null; @@ -118,7 +123,22 @@ namespace Avalonia.Native if(visual == null) { - _native.BeginMoveDrag(); + if (_doubleClickHelper.IsDoubleClick(e.Timestamp, e.Position)) + { + // TOGGLE WINDOW STATE. + if (WindowState == WindowState.Maximized || WindowState == WindowState.FullScreen) + { + WindowState = WindowState.Normal; + } + else + { + WindowState = WindowState.Maximized; + } + } + else + { + _native.BeginMoveDrag(); + } } } } From aa8e0651ee995d61ccc18a932511414eef022adb Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 27 Nov 2020 09:26:32 +0100 Subject: [PATCH 076/100] Make ItemContainerInfo.Index setter public. It's needed to implement custom `ItemContainerGenerator`s. --- src/Avalonia.Controls/Generators/ItemContainerInfo.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/Generators/ItemContainerInfo.cs b/src/Avalonia.Controls/Generators/ItemContainerInfo.cs index 023b108061..31d9a5c02e 100644 --- a/src/Avalonia.Controls/Generators/ItemContainerInfo.cs +++ b/src/Avalonia.Controls/Generators/ItemContainerInfo.cs @@ -37,6 +37,6 @@ namespace Avalonia.Controls.Generators /// /// Gets the index of the item in the collection. /// - public int Index { get; internal set; } + public int Index { get; set; } } -} \ No newline at end of file +} From 7165fe6abb147e9d7dadb72a32dc7540497e0e45 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Fri, 27 Nov 2020 14:23:30 -0500 Subject: [PATCH 077/100] Thread safe SKTextBlobBuilder --- .../Avalonia.Skia/PlatformRenderInterface.cs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs index 72700fb8fd..6d0be9f64d 100644 --- a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs +++ b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs @@ -3,6 +3,8 @@ using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Threading; + using Avalonia.Controls.Platform.Surfaces; using Avalonia.Media; using Avalonia.OpenGL; @@ -166,12 +168,13 @@ namespace Avalonia.Skia LinearMetrics = true }; - private static readonly SKTextBlobBuilder s_textBlobBuilder = new SKTextBlobBuilder(); + private static readonly ThreadLocal s_textBlobBuilderThreadLocal = new ThreadLocal(() => new SKTextBlobBuilder()); /// public IGlyphRunImpl CreateGlyphRun(GlyphRun glyphRun, out double width) { var count = glyphRun.GlyphIndices.Length; + var textBlobBuilder = s_textBlobBuilderThreadLocal.Value; var glyphTypeface = (GlyphTypefaceImpl)glyphRun.GlyphTypeface.PlatformImpl; @@ -191,15 +194,15 @@ namespace Avalonia.Skia { if (glyphTypeface.IsFixedPitch) { - s_textBlobBuilder.AddRun(glyphRun.GlyphIndices.Buffer.Span, s_font); + textBlobBuilder.AddRun(glyphRun.GlyphIndices.Buffer.Span, s_font); - textBlob = s_textBlobBuilder.Build(); + textBlob = textBlobBuilder.Build(); width = glyphTypeface.GetGlyphAdvance(glyphRun.GlyphIndices[0]) * scale * glyphRun.GlyphIndices.Length; } else { - var buffer = s_textBlobBuilder.AllocateHorizontalRun(s_font, count, 0); + var buffer = textBlobBuilder.AllocateHorizontalRun(s_font, count, 0); var positions = buffer.GetPositionSpan(); @@ -219,12 +222,12 @@ namespace Avalonia.Skia buffer.SetGlyphs(glyphRun.GlyphIndices.Buffer.Span); - textBlob = s_textBlobBuilder.Build(); + textBlob = textBlobBuilder.Build(); } } else { - var buffer = s_textBlobBuilder.AllocatePositionedRun(s_font, count); + var buffer = textBlobBuilder.AllocatePositionedRun(s_font, count); var glyphPositions = buffer.GetPositionSpan(); @@ -250,7 +253,7 @@ namespace Avalonia.Skia width = currentX; - textBlob = s_textBlobBuilder.Build(); + textBlob = textBlobBuilder.Build(); } return new GlyphRunImpl(textBlob); From 960ae38ecdc1a6b5a75dce50a2f53c54fa1b9d1f Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Sat, 28 Nov 2020 16:51:30 +0100 Subject: [PATCH 078/100] Fix TextBox crashing when PointerMoved gets called and text presenter has 0 size. --- src/Avalonia.Controls/TextBox.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index 0fe3ac62e4..28426ee70f 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -867,7 +867,10 @@ namespace Avalonia.Controls { var point = e.GetPosition(_presenter); - point = new Point(MathUtilities.Clamp(point.X, 0, _presenter.Bounds.Width - 1), MathUtilities.Clamp(point.Y, 0, _presenter.Bounds.Height - 1)); + point = new Point( + MathUtilities.Clamp(point.X, 0, Math.Max(_presenter.Bounds.Width - 1, 0)), + MathUtilities.Clamp(point.Y, 0, Math.Max(_presenter.Bounds.Height - 1, 0))); + CaretIndex = SelectionEnd = _presenter.GetCaretIndex(point); } } From 37dda37e469631b0fbe852017261aa2459b8344d Mon Sep 17 00:00:00 2001 From: Max Katz Date: Sat, 28 Nov 2020 22:14:06 -0500 Subject: [PATCH 079/100] Move Fluent theme files --- samples/ControlCatalog/App.xaml.cs | 4 +- samples/RenderDemo/App.xaml | 2 +- samples/Sandbox/App.axaml | 2 +- .../ApiCompatBaseline.txt | 3 + .../Avalonia.Themes.Fluent.csproj | 1 + .../{ => Controls}/AutoCompleteBox.xaml | 0 .../{ => Controls}/Button.xaml | 0 .../{ => Controls}/ButtonSpinner.xaml | 0 .../{ => Controls}/Calendar.xaml | 0 .../{ => Controls}/CalendarButton.xaml | 0 .../{ => Controls}/CalendarDatePicker.xaml | 0 .../{ => Controls}/CalendarDayButton.xaml | 0 .../{ => Controls}/CalendarItem.xaml | 0 .../{ => Controls}/CaptionButtons.xaml | 0 .../{ => Controls}/Carousel.xaml | 0 .../{ => Controls}/CheckBox.xaml | 0 .../{ => Controls}/ComboBox.xaml | 0 .../{ => Controls}/ComboBoxItem.xaml | 0 .../{ => Controls}/Common.xaml | 0 .../{ => Controls}/ContentControl.xaml | 0 .../{ => Controls}/ContextMenu.xaml | 0 .../{ => Controls}/DataValidationErrors.xaml | 0 .../{ => Controls}/DatePicker.xaml | 0 .../{ => Controls}/EmbeddableControlRoot.xaml | 0 .../{ => Controls}/Expander.xaml | 0 .../Controls/FluentTheme.xaml | 62 +++++++++++++++++++ .../{ => Controls}/FluentTheme.xaml.cs | 2 +- .../{ => Controls}/FocusAdorner.xaml | 0 .../{ => Controls}/GridSplitter.xaml | 0 .../{ => Controls}/ItemsControl.xaml | 0 .../{ => Controls}/Label.xaml | 0 .../{ => Controls}/ListBox.xaml | 0 .../{ => Controls}/ListBoxItem.xaml | 0 .../{ => Controls}/Menu.xaml | 0 .../{ => Controls}/MenuItem.xaml | 0 .../{ => Controls}/NativeMenuBar.xaml | 0 .../{ => Controls}/NotificationCard.xaml | 0 .../{ => Controls}/NumericUpDown.xaml | 0 .../{ => Controls}/OverlayPopupHost.xaml | 0 .../{ => Controls}/PathIcon.xaml | 0 .../{ => Controls}/PopupRoot.xaml | 0 .../{ => Controls}/ProgressBar.xaml | 0 .../{ => Controls}/RadioButton.xaml | 0 .../{ => Controls}/RepeatButton.xaml | 0 .../{ => Controls}/ScrollBar.xaml | 0 .../{ => Controls}/ScrollViewer.xaml | 0 .../{ => Controls}/Separator.xaml | 0 .../{ => Controls}/Slider.xaml | 0 .../{ => Controls}/SplitView.xaml | 0 .../{ => Controls}/TabControl.xaml | 0 .../{ => Controls}/TabItem.xaml | 0 .../{ => Controls}/TabStrip.xaml | 0 .../{ => Controls}/TabStripItem.xaml | 0 .../{ => Controls}/TextBox.xaml | 0 .../{ => Controls}/TimePicker.xaml | 0 .../{ => Controls}/TitleBar.xaml | 0 .../{ => Controls}/ToggleButton.xaml | 0 .../{ => Controls}/ToggleSwitch.xaml | 0 .../{ => Controls}/ToolTip.xaml | 0 .../{ => Controls}/TreeView.xaml | 0 .../{ => Controls}/TreeViewItem.xaml | 0 .../{ => Controls}/UserControl.xaml | 0 .../{ => Controls}/Window.xaml | 0 .../WindowNotificationManager.xaml | 0 .../{Accents => }/FluentDark.xaml | 2 +- .../{Accents => }/FluentLight.xaml | 2 +- src/Avalonia.Themes.Fluent/FluentTheme.xaml | 62 ------------------- 67 files changed, 73 insertions(+), 69 deletions(-) create mode 100644 src/Avalonia.Themes.Fluent/ApiCompatBaseline.txt rename src/Avalonia.Themes.Fluent/{ => Controls}/AutoCompleteBox.xaml (100%) rename src/Avalonia.Themes.Fluent/{ => Controls}/Button.xaml (100%) rename src/Avalonia.Themes.Fluent/{ => Controls}/ButtonSpinner.xaml (100%) rename src/Avalonia.Themes.Fluent/{ => Controls}/Calendar.xaml (100%) rename src/Avalonia.Themes.Fluent/{ => Controls}/CalendarButton.xaml (100%) rename src/Avalonia.Themes.Fluent/{ => Controls}/CalendarDatePicker.xaml (100%) rename src/Avalonia.Themes.Fluent/{ => Controls}/CalendarDayButton.xaml (100%) rename src/Avalonia.Themes.Fluent/{ => Controls}/CalendarItem.xaml (100%) rename src/Avalonia.Themes.Fluent/{ => Controls}/CaptionButtons.xaml (100%) rename src/Avalonia.Themes.Fluent/{ => Controls}/Carousel.xaml (100%) rename src/Avalonia.Themes.Fluent/{ => Controls}/CheckBox.xaml (100%) rename src/Avalonia.Themes.Fluent/{ => Controls}/ComboBox.xaml (100%) rename src/Avalonia.Themes.Fluent/{ => Controls}/ComboBoxItem.xaml (100%) rename src/Avalonia.Themes.Fluent/{ => Controls}/Common.xaml (100%) rename src/Avalonia.Themes.Fluent/{ => Controls}/ContentControl.xaml (100%) rename src/Avalonia.Themes.Fluent/{ => Controls}/ContextMenu.xaml (100%) rename src/Avalonia.Themes.Fluent/{ => Controls}/DataValidationErrors.xaml (100%) rename src/Avalonia.Themes.Fluent/{ => Controls}/DatePicker.xaml (100%) rename src/Avalonia.Themes.Fluent/{ => Controls}/EmbeddableControlRoot.xaml (100%) rename src/Avalonia.Themes.Fluent/{ => Controls}/Expander.xaml (100%) create mode 100644 src/Avalonia.Themes.Fluent/Controls/FluentTheme.xaml rename src/Avalonia.Themes.Fluent/{ => Controls}/FluentTheme.xaml.cs (81%) rename src/Avalonia.Themes.Fluent/{ => Controls}/FocusAdorner.xaml (100%) rename src/Avalonia.Themes.Fluent/{ => Controls}/GridSplitter.xaml (100%) rename src/Avalonia.Themes.Fluent/{ => Controls}/ItemsControl.xaml (100%) rename src/Avalonia.Themes.Fluent/{ => Controls}/Label.xaml (100%) rename src/Avalonia.Themes.Fluent/{ => Controls}/ListBox.xaml (100%) rename src/Avalonia.Themes.Fluent/{ => Controls}/ListBoxItem.xaml (100%) rename src/Avalonia.Themes.Fluent/{ => Controls}/Menu.xaml (100%) rename src/Avalonia.Themes.Fluent/{ => Controls}/MenuItem.xaml (100%) rename src/Avalonia.Themes.Fluent/{ => Controls}/NativeMenuBar.xaml (100%) rename src/Avalonia.Themes.Fluent/{ => Controls}/NotificationCard.xaml (100%) rename src/Avalonia.Themes.Fluent/{ => Controls}/NumericUpDown.xaml (100%) rename src/Avalonia.Themes.Fluent/{ => Controls}/OverlayPopupHost.xaml (100%) rename src/Avalonia.Themes.Fluent/{ => Controls}/PathIcon.xaml (100%) rename src/Avalonia.Themes.Fluent/{ => Controls}/PopupRoot.xaml (100%) rename src/Avalonia.Themes.Fluent/{ => Controls}/ProgressBar.xaml (100%) rename src/Avalonia.Themes.Fluent/{ => Controls}/RadioButton.xaml (100%) rename src/Avalonia.Themes.Fluent/{ => Controls}/RepeatButton.xaml (100%) rename src/Avalonia.Themes.Fluent/{ => Controls}/ScrollBar.xaml (100%) rename src/Avalonia.Themes.Fluent/{ => Controls}/ScrollViewer.xaml (100%) rename src/Avalonia.Themes.Fluent/{ => Controls}/Separator.xaml (100%) rename src/Avalonia.Themes.Fluent/{ => Controls}/Slider.xaml (100%) rename src/Avalonia.Themes.Fluent/{ => Controls}/SplitView.xaml (100%) rename src/Avalonia.Themes.Fluent/{ => Controls}/TabControl.xaml (100%) rename src/Avalonia.Themes.Fluent/{ => Controls}/TabItem.xaml (100%) rename src/Avalonia.Themes.Fluent/{ => Controls}/TabStrip.xaml (100%) rename src/Avalonia.Themes.Fluent/{ => Controls}/TabStripItem.xaml (100%) rename src/Avalonia.Themes.Fluent/{ => Controls}/TextBox.xaml (100%) rename src/Avalonia.Themes.Fluent/{ => Controls}/TimePicker.xaml (100%) rename src/Avalonia.Themes.Fluent/{ => Controls}/TitleBar.xaml (100%) rename src/Avalonia.Themes.Fluent/{ => Controls}/ToggleButton.xaml (100%) rename src/Avalonia.Themes.Fluent/{ => Controls}/ToggleSwitch.xaml (100%) rename src/Avalonia.Themes.Fluent/{ => Controls}/ToolTip.xaml (100%) rename src/Avalonia.Themes.Fluent/{ => Controls}/TreeView.xaml (100%) rename src/Avalonia.Themes.Fluent/{ => Controls}/TreeViewItem.xaml (100%) rename src/Avalonia.Themes.Fluent/{ => Controls}/UserControl.xaml (100%) rename src/Avalonia.Themes.Fluent/{ => Controls}/Window.xaml (100%) rename src/Avalonia.Themes.Fluent/{ => Controls}/WindowNotificationManager.xaml (100%) rename src/Avalonia.Themes.Fluent/{Accents => }/FluentDark.xaml (86%) rename src/Avalonia.Themes.Fluent/{Accents => }/FluentLight.xaml (86%) delete mode 100644 src/Avalonia.Themes.Fluent/FluentTheme.xaml diff --git a/samples/ControlCatalog/App.xaml.cs b/samples/ControlCatalog/App.xaml.cs index 22f4e9be1f..020fb2fff3 100644 --- a/samples/ControlCatalog/App.xaml.cs +++ b/samples/ControlCatalog/App.xaml.cs @@ -23,7 +23,7 @@ namespace ControlCatalog { new StyleInclude(new Uri("avares://ControlCatalog/Styles")) { - Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/FluentDark.xaml") + Source = new Uri("avares://Avalonia.Themes.Fluent/FluentDark.xaml") }, DataGridFluent }; @@ -32,7 +32,7 @@ namespace ControlCatalog { new StyleInclude(new Uri("avares://ControlCatalog/Styles")) { - Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/FluentLight.xaml") + Source = new Uri("avares://Avalonia.Themes.Fluent/FluentLight.xaml") }, DataGridFluent }; diff --git a/samples/RenderDemo/App.xaml b/samples/RenderDemo/App.xaml index 61e4d2385b..eeeaf5b0ae 100644 --- a/samples/RenderDemo/App.xaml +++ b/samples/RenderDemo/App.xaml @@ -3,7 +3,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="RenderDemo.App"> - + diff --git a/samples/Sandbox/App.axaml b/samples/Sandbox/App.axaml index 699781eb94..a351ba93e9 100644 --- a/samples/Sandbox/App.axaml +++ b/samples/Sandbox/App.axaml @@ -3,6 +3,6 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="Sandbox.App"> - + diff --git a/src/Avalonia.Themes.Fluent/ApiCompatBaseline.txt b/src/Avalonia.Themes.Fluent/ApiCompatBaseline.txt new file mode 100644 index 0000000000..68dc477cbc --- /dev/null +++ b/src/Avalonia.Themes.Fluent/ApiCompatBaseline.txt @@ -0,0 +1,3 @@ +Compat issues with assembly Avalonia.Themes.Fluent: +TypesMustExist : Type 'Avalonia.Themes.Fluent.FluentTheme' does not exist in the implementation but it does exist in the contract. +Total Issues: 1 diff --git a/src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj b/src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj index 72f1fbf973..b710b5ef3b 100644 --- a/src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj +++ b/src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj @@ -1,5 +1,6 @@  + true netstandard2.0 diff --git a/src/Avalonia.Themes.Fluent/AutoCompleteBox.xaml b/src/Avalonia.Themes.Fluent/Controls/AutoCompleteBox.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/AutoCompleteBox.xaml rename to src/Avalonia.Themes.Fluent/Controls/AutoCompleteBox.xaml diff --git a/src/Avalonia.Themes.Fluent/Button.xaml b/src/Avalonia.Themes.Fluent/Controls/Button.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/Button.xaml rename to src/Avalonia.Themes.Fluent/Controls/Button.xaml diff --git a/src/Avalonia.Themes.Fluent/ButtonSpinner.xaml b/src/Avalonia.Themes.Fluent/Controls/ButtonSpinner.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/ButtonSpinner.xaml rename to src/Avalonia.Themes.Fluent/Controls/ButtonSpinner.xaml diff --git a/src/Avalonia.Themes.Fluent/Calendar.xaml b/src/Avalonia.Themes.Fluent/Controls/Calendar.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/Calendar.xaml rename to src/Avalonia.Themes.Fluent/Controls/Calendar.xaml diff --git a/src/Avalonia.Themes.Fluent/CalendarButton.xaml b/src/Avalonia.Themes.Fluent/Controls/CalendarButton.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/CalendarButton.xaml rename to src/Avalonia.Themes.Fluent/Controls/CalendarButton.xaml diff --git a/src/Avalonia.Themes.Fluent/CalendarDatePicker.xaml b/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/CalendarDatePicker.xaml rename to src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml diff --git a/src/Avalonia.Themes.Fluent/CalendarDayButton.xaml b/src/Avalonia.Themes.Fluent/Controls/CalendarDayButton.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/CalendarDayButton.xaml rename to src/Avalonia.Themes.Fluent/Controls/CalendarDayButton.xaml diff --git a/src/Avalonia.Themes.Fluent/CalendarItem.xaml b/src/Avalonia.Themes.Fluent/Controls/CalendarItem.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/CalendarItem.xaml rename to src/Avalonia.Themes.Fluent/Controls/CalendarItem.xaml diff --git a/src/Avalonia.Themes.Fluent/CaptionButtons.xaml b/src/Avalonia.Themes.Fluent/Controls/CaptionButtons.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/CaptionButtons.xaml rename to src/Avalonia.Themes.Fluent/Controls/CaptionButtons.xaml diff --git a/src/Avalonia.Themes.Fluent/Carousel.xaml b/src/Avalonia.Themes.Fluent/Controls/Carousel.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/Carousel.xaml rename to src/Avalonia.Themes.Fluent/Controls/Carousel.xaml diff --git a/src/Avalonia.Themes.Fluent/CheckBox.xaml b/src/Avalonia.Themes.Fluent/Controls/CheckBox.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/CheckBox.xaml rename to src/Avalonia.Themes.Fluent/Controls/CheckBox.xaml diff --git a/src/Avalonia.Themes.Fluent/ComboBox.xaml b/src/Avalonia.Themes.Fluent/Controls/ComboBox.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/ComboBox.xaml rename to src/Avalonia.Themes.Fluent/Controls/ComboBox.xaml diff --git a/src/Avalonia.Themes.Fluent/ComboBoxItem.xaml b/src/Avalonia.Themes.Fluent/Controls/ComboBoxItem.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/ComboBoxItem.xaml rename to src/Avalonia.Themes.Fluent/Controls/ComboBoxItem.xaml diff --git a/src/Avalonia.Themes.Fluent/Common.xaml b/src/Avalonia.Themes.Fluent/Controls/Common.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/Common.xaml rename to src/Avalonia.Themes.Fluent/Controls/Common.xaml diff --git a/src/Avalonia.Themes.Fluent/ContentControl.xaml b/src/Avalonia.Themes.Fluent/Controls/ContentControl.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/ContentControl.xaml rename to src/Avalonia.Themes.Fluent/Controls/ContentControl.xaml diff --git a/src/Avalonia.Themes.Fluent/ContextMenu.xaml b/src/Avalonia.Themes.Fluent/Controls/ContextMenu.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/ContextMenu.xaml rename to src/Avalonia.Themes.Fluent/Controls/ContextMenu.xaml diff --git a/src/Avalonia.Themes.Fluent/DataValidationErrors.xaml b/src/Avalonia.Themes.Fluent/Controls/DataValidationErrors.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/DataValidationErrors.xaml rename to src/Avalonia.Themes.Fluent/Controls/DataValidationErrors.xaml diff --git a/src/Avalonia.Themes.Fluent/DatePicker.xaml b/src/Avalonia.Themes.Fluent/Controls/DatePicker.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/DatePicker.xaml rename to src/Avalonia.Themes.Fluent/Controls/DatePicker.xaml diff --git a/src/Avalonia.Themes.Fluent/EmbeddableControlRoot.xaml b/src/Avalonia.Themes.Fluent/Controls/EmbeddableControlRoot.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/EmbeddableControlRoot.xaml rename to src/Avalonia.Themes.Fluent/Controls/EmbeddableControlRoot.xaml diff --git a/src/Avalonia.Themes.Fluent/Expander.xaml b/src/Avalonia.Themes.Fluent/Controls/Expander.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/Expander.xaml rename to src/Avalonia.Themes.Fluent/Controls/Expander.xaml diff --git a/src/Avalonia.Themes.Fluent/Controls/FluentTheme.xaml b/src/Avalonia.Themes.Fluent/Controls/FluentTheme.xaml new file mode 100644 index 0000000000..5205fc16a6 --- /dev/null +++ b/src/Avalonia.Themes.Fluent/Controls/FluentTheme.xaml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Avalonia.Themes.Fluent/FluentTheme.xaml.cs b/src/Avalonia.Themes.Fluent/Controls/FluentTheme.xaml.cs similarity index 81% rename from src/Avalonia.Themes.Fluent/FluentTheme.xaml.cs rename to src/Avalonia.Themes.Fluent/Controls/FluentTheme.xaml.cs index 9c65a7227c..bf880f2334 100644 --- a/src/Avalonia.Themes.Fluent/FluentTheme.xaml.cs +++ b/src/Avalonia.Themes.Fluent/Controls/FluentTheme.xaml.cs @@ -1,7 +1,7 @@ using Avalonia.Markup.Xaml; using Avalonia.Styling; -namespace Avalonia.Themes.Fluent +namespace Avalonia.Themes.Fluent.Controls { /// /// The default Avalonia theme. diff --git a/src/Avalonia.Themes.Fluent/FocusAdorner.xaml b/src/Avalonia.Themes.Fluent/Controls/FocusAdorner.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/FocusAdorner.xaml rename to src/Avalonia.Themes.Fluent/Controls/FocusAdorner.xaml diff --git a/src/Avalonia.Themes.Fluent/GridSplitter.xaml b/src/Avalonia.Themes.Fluent/Controls/GridSplitter.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/GridSplitter.xaml rename to src/Avalonia.Themes.Fluent/Controls/GridSplitter.xaml diff --git a/src/Avalonia.Themes.Fluent/ItemsControl.xaml b/src/Avalonia.Themes.Fluent/Controls/ItemsControl.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/ItemsControl.xaml rename to src/Avalonia.Themes.Fluent/Controls/ItemsControl.xaml diff --git a/src/Avalonia.Themes.Fluent/Label.xaml b/src/Avalonia.Themes.Fluent/Controls/Label.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/Label.xaml rename to src/Avalonia.Themes.Fluent/Controls/Label.xaml diff --git a/src/Avalonia.Themes.Fluent/ListBox.xaml b/src/Avalonia.Themes.Fluent/Controls/ListBox.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/ListBox.xaml rename to src/Avalonia.Themes.Fluent/Controls/ListBox.xaml diff --git a/src/Avalonia.Themes.Fluent/ListBoxItem.xaml b/src/Avalonia.Themes.Fluent/Controls/ListBoxItem.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/ListBoxItem.xaml rename to src/Avalonia.Themes.Fluent/Controls/ListBoxItem.xaml diff --git a/src/Avalonia.Themes.Fluent/Menu.xaml b/src/Avalonia.Themes.Fluent/Controls/Menu.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/Menu.xaml rename to src/Avalonia.Themes.Fluent/Controls/Menu.xaml diff --git a/src/Avalonia.Themes.Fluent/MenuItem.xaml b/src/Avalonia.Themes.Fluent/Controls/MenuItem.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/MenuItem.xaml rename to src/Avalonia.Themes.Fluent/Controls/MenuItem.xaml diff --git a/src/Avalonia.Themes.Fluent/NativeMenuBar.xaml b/src/Avalonia.Themes.Fluent/Controls/NativeMenuBar.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/NativeMenuBar.xaml rename to src/Avalonia.Themes.Fluent/Controls/NativeMenuBar.xaml diff --git a/src/Avalonia.Themes.Fluent/NotificationCard.xaml b/src/Avalonia.Themes.Fluent/Controls/NotificationCard.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/NotificationCard.xaml rename to src/Avalonia.Themes.Fluent/Controls/NotificationCard.xaml diff --git a/src/Avalonia.Themes.Fluent/NumericUpDown.xaml b/src/Avalonia.Themes.Fluent/Controls/NumericUpDown.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/NumericUpDown.xaml rename to src/Avalonia.Themes.Fluent/Controls/NumericUpDown.xaml diff --git a/src/Avalonia.Themes.Fluent/OverlayPopupHost.xaml b/src/Avalonia.Themes.Fluent/Controls/OverlayPopupHost.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/OverlayPopupHost.xaml rename to src/Avalonia.Themes.Fluent/Controls/OverlayPopupHost.xaml diff --git a/src/Avalonia.Themes.Fluent/PathIcon.xaml b/src/Avalonia.Themes.Fluent/Controls/PathIcon.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/PathIcon.xaml rename to src/Avalonia.Themes.Fluent/Controls/PathIcon.xaml diff --git a/src/Avalonia.Themes.Fluent/PopupRoot.xaml b/src/Avalonia.Themes.Fluent/Controls/PopupRoot.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/PopupRoot.xaml rename to src/Avalonia.Themes.Fluent/Controls/PopupRoot.xaml diff --git a/src/Avalonia.Themes.Fluent/ProgressBar.xaml b/src/Avalonia.Themes.Fluent/Controls/ProgressBar.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/ProgressBar.xaml rename to src/Avalonia.Themes.Fluent/Controls/ProgressBar.xaml diff --git a/src/Avalonia.Themes.Fluent/RadioButton.xaml b/src/Avalonia.Themes.Fluent/Controls/RadioButton.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/RadioButton.xaml rename to src/Avalonia.Themes.Fluent/Controls/RadioButton.xaml diff --git a/src/Avalonia.Themes.Fluent/RepeatButton.xaml b/src/Avalonia.Themes.Fluent/Controls/RepeatButton.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/RepeatButton.xaml rename to src/Avalonia.Themes.Fluent/Controls/RepeatButton.xaml diff --git a/src/Avalonia.Themes.Fluent/ScrollBar.xaml b/src/Avalonia.Themes.Fluent/Controls/ScrollBar.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/ScrollBar.xaml rename to src/Avalonia.Themes.Fluent/Controls/ScrollBar.xaml diff --git a/src/Avalonia.Themes.Fluent/ScrollViewer.xaml b/src/Avalonia.Themes.Fluent/Controls/ScrollViewer.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/ScrollViewer.xaml rename to src/Avalonia.Themes.Fluent/Controls/ScrollViewer.xaml diff --git a/src/Avalonia.Themes.Fluent/Separator.xaml b/src/Avalonia.Themes.Fluent/Controls/Separator.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/Separator.xaml rename to src/Avalonia.Themes.Fluent/Controls/Separator.xaml diff --git a/src/Avalonia.Themes.Fluent/Slider.xaml b/src/Avalonia.Themes.Fluent/Controls/Slider.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/Slider.xaml rename to src/Avalonia.Themes.Fluent/Controls/Slider.xaml diff --git a/src/Avalonia.Themes.Fluent/SplitView.xaml b/src/Avalonia.Themes.Fluent/Controls/SplitView.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/SplitView.xaml rename to src/Avalonia.Themes.Fluent/Controls/SplitView.xaml diff --git a/src/Avalonia.Themes.Fluent/TabControl.xaml b/src/Avalonia.Themes.Fluent/Controls/TabControl.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/TabControl.xaml rename to src/Avalonia.Themes.Fluent/Controls/TabControl.xaml diff --git a/src/Avalonia.Themes.Fluent/TabItem.xaml b/src/Avalonia.Themes.Fluent/Controls/TabItem.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/TabItem.xaml rename to src/Avalonia.Themes.Fluent/Controls/TabItem.xaml diff --git a/src/Avalonia.Themes.Fluent/TabStrip.xaml b/src/Avalonia.Themes.Fluent/Controls/TabStrip.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/TabStrip.xaml rename to src/Avalonia.Themes.Fluent/Controls/TabStrip.xaml diff --git a/src/Avalonia.Themes.Fluent/TabStripItem.xaml b/src/Avalonia.Themes.Fluent/Controls/TabStripItem.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/TabStripItem.xaml rename to src/Avalonia.Themes.Fluent/Controls/TabStripItem.xaml diff --git a/src/Avalonia.Themes.Fluent/TextBox.xaml b/src/Avalonia.Themes.Fluent/Controls/TextBox.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/TextBox.xaml rename to src/Avalonia.Themes.Fluent/Controls/TextBox.xaml diff --git a/src/Avalonia.Themes.Fluent/TimePicker.xaml b/src/Avalonia.Themes.Fluent/Controls/TimePicker.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/TimePicker.xaml rename to src/Avalonia.Themes.Fluent/Controls/TimePicker.xaml diff --git a/src/Avalonia.Themes.Fluent/TitleBar.xaml b/src/Avalonia.Themes.Fluent/Controls/TitleBar.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/TitleBar.xaml rename to src/Avalonia.Themes.Fluent/Controls/TitleBar.xaml diff --git a/src/Avalonia.Themes.Fluent/ToggleButton.xaml b/src/Avalonia.Themes.Fluent/Controls/ToggleButton.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/ToggleButton.xaml rename to src/Avalonia.Themes.Fluent/Controls/ToggleButton.xaml diff --git a/src/Avalonia.Themes.Fluent/ToggleSwitch.xaml b/src/Avalonia.Themes.Fluent/Controls/ToggleSwitch.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/ToggleSwitch.xaml rename to src/Avalonia.Themes.Fluent/Controls/ToggleSwitch.xaml diff --git a/src/Avalonia.Themes.Fluent/ToolTip.xaml b/src/Avalonia.Themes.Fluent/Controls/ToolTip.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/ToolTip.xaml rename to src/Avalonia.Themes.Fluent/Controls/ToolTip.xaml diff --git a/src/Avalonia.Themes.Fluent/TreeView.xaml b/src/Avalonia.Themes.Fluent/Controls/TreeView.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/TreeView.xaml rename to src/Avalonia.Themes.Fluent/Controls/TreeView.xaml diff --git a/src/Avalonia.Themes.Fluent/TreeViewItem.xaml b/src/Avalonia.Themes.Fluent/Controls/TreeViewItem.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/TreeViewItem.xaml rename to src/Avalonia.Themes.Fluent/Controls/TreeViewItem.xaml diff --git a/src/Avalonia.Themes.Fluent/UserControl.xaml b/src/Avalonia.Themes.Fluent/Controls/UserControl.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/UserControl.xaml rename to src/Avalonia.Themes.Fluent/Controls/UserControl.xaml diff --git a/src/Avalonia.Themes.Fluent/Window.xaml b/src/Avalonia.Themes.Fluent/Controls/Window.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/Window.xaml rename to src/Avalonia.Themes.Fluent/Controls/Window.xaml diff --git a/src/Avalonia.Themes.Fluent/WindowNotificationManager.xaml b/src/Avalonia.Themes.Fluent/Controls/WindowNotificationManager.xaml similarity index 100% rename from src/Avalonia.Themes.Fluent/WindowNotificationManager.xaml rename to src/Avalonia.Themes.Fluent/Controls/WindowNotificationManager.xaml diff --git a/src/Avalonia.Themes.Fluent/Accents/FluentDark.xaml b/src/Avalonia.Themes.Fluent/FluentDark.xaml similarity index 86% rename from src/Avalonia.Themes.Fluent/Accents/FluentDark.xaml rename to src/Avalonia.Themes.Fluent/FluentDark.xaml index 9ef92a44d5..48f3553b92 100644 --- a/src/Avalonia.Themes.Fluent/Accents/FluentDark.xaml +++ b/src/Avalonia.Themes.Fluent/FluentDark.xaml @@ -5,5 +5,5 @@ - + diff --git a/src/Avalonia.Themes.Fluent/Accents/FluentLight.xaml b/src/Avalonia.Themes.Fluent/FluentLight.xaml similarity index 86% rename from src/Avalonia.Themes.Fluent/Accents/FluentLight.xaml rename to src/Avalonia.Themes.Fluent/FluentLight.xaml index 8c92040122..565212328b 100644 --- a/src/Avalonia.Themes.Fluent/Accents/FluentLight.xaml +++ b/src/Avalonia.Themes.Fluent/FluentLight.xaml @@ -5,5 +5,5 @@ - + diff --git a/src/Avalonia.Themes.Fluent/FluentTheme.xaml b/src/Avalonia.Themes.Fluent/FluentTheme.xaml deleted file mode 100644 index 4e0e4fe862..0000000000 --- a/src/Avalonia.Themes.Fluent/FluentTheme.xaml +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From e8c40d4275b36ef03fbc3e1862cc304cd2bac95b Mon Sep 17 00:00:00 2001 From: Max Katz Date: Sat, 28 Nov 2020 22:24:34 -0500 Subject: [PATCH 080/100] Rename FluentControls --- samples/ControlCatalog/MainWindow.xaml.cs | 2 +- src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj | 1 - .../Controls/{FluentTheme.xaml => FluentControls.xaml} | 2 +- .../Controls/{FluentTheme.xaml.cs => FluentControls.xaml.cs} | 2 +- src/Avalonia.Themes.Fluent/FluentDark.xaml | 2 +- src/Avalonia.Themes.Fluent/FluentLight.xaml | 2 +- 6 files changed, 5 insertions(+), 6 deletions(-) rename src/Avalonia.Themes.Fluent/Controls/{FluentTheme.xaml => FluentControls.xaml} (98%) rename src/Avalonia.Themes.Fluent/Controls/{FluentTheme.xaml.cs => FluentControls.xaml.cs} (81%) diff --git a/samples/ControlCatalog/MainWindow.xaml.cs b/samples/ControlCatalog/MainWindow.xaml.cs index a316321cd1..723351ae57 100644 --- a/samples/ControlCatalog/MainWindow.xaml.cs +++ b/samples/ControlCatalog/MainWindow.xaml.cs @@ -67,7 +67,7 @@ namespace ControlCatalog if (Application.Current.Styles.Contains(App.FluentDark) || Application.Current.Styles.Contains(App.FluentLight)) { - var theme = new Avalonia.Themes.Fluent.FluentTheme(); + var theme = new Avalonia.Themes.Fluent.Controls.FluentControls(); theme.TryGetResource("Button", out _); } else diff --git a/src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj b/src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj index b710b5ef3b..72f1fbf973 100644 --- a/src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj +++ b/src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj @@ -1,6 +1,5 @@  - true netstandard2.0 diff --git a/src/Avalonia.Themes.Fluent/Controls/FluentTheme.xaml b/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml similarity index 98% rename from src/Avalonia.Themes.Fluent/Controls/FluentTheme.xaml rename to src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml index 5205fc16a6..0acb8f4f6e 100644 --- a/src/Avalonia.Themes.Fluent/Controls/FluentTheme.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml @@ -1,6 +1,6 @@ + x:Class="Avalonia.Themes.Fluent.Controls.FluentControls"> diff --git a/src/Avalonia.Themes.Fluent/Controls/FluentTheme.xaml.cs b/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml.cs similarity index 81% rename from src/Avalonia.Themes.Fluent/Controls/FluentTheme.xaml.cs rename to src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml.cs index bf880f2334..37822f5c8c 100644 --- a/src/Avalonia.Themes.Fluent/Controls/FluentTheme.xaml.cs +++ b/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml.cs @@ -6,7 +6,7 @@ namespace Avalonia.Themes.Fluent.Controls /// /// The default Avalonia theme. /// - public class FluentTheme : Styles + public class FluentControls : Styles { } } diff --git a/src/Avalonia.Themes.Fluent/FluentDark.xaml b/src/Avalonia.Themes.Fluent/FluentDark.xaml index 48f3553b92..74b583a240 100644 --- a/src/Avalonia.Themes.Fluent/FluentDark.xaml +++ b/src/Avalonia.Themes.Fluent/FluentDark.xaml @@ -5,5 +5,5 @@ - + diff --git a/src/Avalonia.Themes.Fluent/FluentLight.xaml b/src/Avalonia.Themes.Fluent/FluentLight.xaml index 565212328b..1bc51f655e 100644 --- a/src/Avalonia.Themes.Fluent/FluentLight.xaml +++ b/src/Avalonia.Themes.Fluent/FluentLight.xaml @@ -5,5 +5,5 @@ - + From 856ead263cf111f8a6fbd175fb9258850f2cfcf3 Mon Sep 17 00:00:00 2001 From: Friedrich von Never Date: Sun, 29 Nov 2020 13:56:46 +0700 Subject: [PATCH 081/100] EmbedXaml: remove items generated for XAML files by default These items are generated by Microsoft.Net.SDK.DefaultItems.props, and aren't supposed to be here. --- build/EmbedXaml.props | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build/EmbedXaml.props b/build/EmbedXaml.props index 7ce0366dea..0bb8da4f47 100644 --- a/build/EmbedXaml.props +++ b/build/EmbedXaml.props @@ -4,8 +4,9 @@ %(Filename) + Designer - \ No newline at end of file + From 3d131473164a352539e80f3abd6de057093485c4 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 30 Nov 2020 01:14:14 +0000 Subject: [PATCH 082/100] Sign assemblies --- build/SharedVersion.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/SharedVersion.props b/build/SharedVersion.props index a5c0aa1bea..43ec995ed9 100644 --- a/build/SharedVersion.props +++ b/build/SharedVersion.props @@ -16,7 +16,7 @@ https://github.com/AvaloniaUI/Avalonia/releases git $(MSBuildThisFileDirectory)\avalonia.snk - false + true $(DefineConstants);SIGNED_BUILD From 9a591f517eb59bb5c77dd15f81690cbdd40565b3 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 30 Nov 2020 15:10:06 +0000 Subject: [PATCH 083/100] sign test assemblies --- .../Avalonia.Direct2D1.RenderTests.csproj | 1 + tests/Avalonia.Skia.UnitTests/Avalonia.Skia.UnitTests.csproj | 3 ++- tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/Avalonia.Direct2D1.RenderTests/Avalonia.Direct2D1.RenderTests.csproj b/tests/Avalonia.Direct2D1.RenderTests/Avalonia.Direct2D1.RenderTests.csproj index 1cfc1a6ab8..c59e59be63 100644 --- a/tests/Avalonia.Direct2D1.RenderTests/Avalonia.Direct2D1.RenderTests.csproj +++ b/tests/Avalonia.Direct2D1.RenderTests/Avalonia.Direct2D1.RenderTests.csproj @@ -27,4 +27,5 @@ + diff --git a/tests/Avalonia.Skia.UnitTests/Avalonia.Skia.UnitTests.csproj b/tests/Avalonia.Skia.UnitTests/Avalonia.Skia.UnitTests.csproj index 584eaf6fa9..ef69865e32 100644 --- a/tests/Avalonia.Skia.UnitTests/Avalonia.Skia.UnitTests.csproj +++ b/tests/Avalonia.Skia.UnitTests/Avalonia.Skia.UnitTests.csproj @@ -8,6 +8,7 @@ + @@ -23,4 +24,4 @@ - \ No newline at end of file + diff --git a/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj b/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj index 6b5c7a66ff..d4abf9416a 100644 --- a/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj +++ b/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj @@ -28,4 +28,5 @@ + From 9eb7a1002fd29ed6b208e4492d9d7c4730bcc253 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 30 Nov 2020 15:17:25 +0000 Subject: [PATCH 084/100] fix test signing --- tests/Avalonia.Base.UnitTests/Avalonia.Base.UnitTests.csproj | 3 ++- .../Avalonia.Controls.DataGrid.UnitTests.csproj | 1 + .../Avalonia.Controls.UnitTests.csproj | 1 + .../Avalonia.Direct2D1.UnitTests.csproj | 3 ++- .../Avalonia.Visuals.UnitTests.csproj | 1 + 5 files changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/Avalonia.Base.UnitTests/Avalonia.Base.UnitTests.csproj b/tests/Avalonia.Base.UnitTests/Avalonia.Base.UnitTests.csproj index 0d87f0eb03..badfd09430 100644 --- a/tests/Avalonia.Base.UnitTests/Avalonia.Base.UnitTests.csproj +++ b/tests/Avalonia.Base.UnitTests/Avalonia.Base.UnitTests.csproj @@ -10,6 +10,7 @@ + @@ -17,4 +18,4 @@ - \ No newline at end of file + diff --git a/tests/Avalonia.Controls.DataGrid.UnitTests/Avalonia.Controls.DataGrid.UnitTests.csproj b/tests/Avalonia.Controls.DataGrid.UnitTests/Avalonia.Controls.DataGrid.UnitTests.csproj index 396dfee691..c0c9303767 100644 --- a/tests/Avalonia.Controls.DataGrid.UnitTests/Avalonia.Controls.DataGrid.UnitTests.csproj +++ b/tests/Avalonia.Controls.DataGrid.UnitTests/Avalonia.Controls.DataGrid.UnitTests.csproj @@ -12,6 +12,7 @@ + diff --git a/tests/Avalonia.Controls.UnitTests/Avalonia.Controls.UnitTests.csproj b/tests/Avalonia.Controls.UnitTests/Avalonia.Controls.UnitTests.csproj index 7a6d77ef46..6b17427eda 100644 --- a/tests/Avalonia.Controls.UnitTests/Avalonia.Controls.UnitTests.csproj +++ b/tests/Avalonia.Controls.UnitTests/Avalonia.Controls.UnitTests.csproj @@ -12,6 +12,7 @@ + diff --git a/tests/Avalonia.Direct2D1.UnitTests/Avalonia.Direct2D1.UnitTests.csproj b/tests/Avalonia.Direct2D1.UnitTests/Avalonia.Direct2D1.UnitTests.csproj index e346ce944d..42229ba456 100644 --- a/tests/Avalonia.Direct2D1.UnitTests/Avalonia.Direct2D1.UnitTests.csproj +++ b/tests/Avalonia.Direct2D1.UnitTests/Avalonia.Direct2D1.UnitTests.csproj @@ -7,6 +7,7 @@ + @@ -22,4 +23,4 @@ - \ No newline at end of file + diff --git a/tests/Avalonia.Visuals.UnitTests/Avalonia.Visuals.UnitTests.csproj b/tests/Avalonia.Visuals.UnitTests/Avalonia.Visuals.UnitTests.csproj index aee54e87a9..13a04be5db 100644 --- a/tests/Avalonia.Visuals.UnitTests/Avalonia.Visuals.UnitTests.csproj +++ b/tests/Avalonia.Visuals.UnitTests/Avalonia.Visuals.UnitTests.csproj @@ -13,6 +13,7 @@ + From 8ac71278091c0c2922f5e41c2e0857cb7f8b9191 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 30 Nov 2020 16:05:48 +0000 Subject: [PATCH 085/100] sign more test assemblies --- .../Avalonia.Animation.UnitTests.csproj | 1 + tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj | 3 ++- .../Avalonia.Markup.UnitTests/Avalonia.Markup.UnitTests.csproj | 1 + .../Avalonia.Markup.Xaml.UnitTests.csproj | 1 + .../Avalonia.Styling.UnitTests.csproj | 1 + 5 files changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/Avalonia.Animation.UnitTests/Avalonia.Animation.UnitTests.csproj b/tests/Avalonia.Animation.UnitTests/Avalonia.Animation.UnitTests.csproj index dd50eff2b6..5b686dea4c 100644 --- a/tests/Avalonia.Animation.UnitTests/Avalonia.Animation.UnitTests.csproj +++ b/tests/Avalonia.Animation.UnitTests/Avalonia.Animation.UnitTests.csproj @@ -10,6 +10,7 @@ + diff --git a/tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj b/tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj index 27f3223c6c..d49a859b89 100644 --- a/tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj +++ b/tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj @@ -7,6 +7,7 @@ + @@ -25,4 +26,4 @@ - \ No newline at end of file + diff --git a/tests/Avalonia.Markup.UnitTests/Avalonia.Markup.UnitTests.csproj b/tests/Avalonia.Markup.UnitTests/Avalonia.Markup.UnitTests.csproj index ec5b2f0ed1..7d1285c025 100644 --- a/tests/Avalonia.Markup.UnitTests/Avalonia.Markup.UnitTests.csproj +++ b/tests/Avalonia.Markup.UnitTests/Avalonia.Markup.UnitTests.csproj @@ -10,6 +10,7 @@ + diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj b/tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj index ad3592294d..44b604197d 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj @@ -10,6 +10,7 @@ + diff --git a/tests/Avalonia.Styling.UnitTests/Avalonia.Styling.UnitTests.csproj b/tests/Avalonia.Styling.UnitTests/Avalonia.Styling.UnitTests.csproj index 2b7f6263a7..a07ec050a6 100644 --- a/tests/Avalonia.Styling.UnitTests/Avalonia.Styling.UnitTests.csproj +++ b/tests/Avalonia.Styling.UnitTests/Avalonia.Styling.UnitTests.csproj @@ -11,6 +11,7 @@ + From 315d8be0ead4db017d2adaeb77e4c733629259ea Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 30 Nov 2020 16:34:46 +0000 Subject: [PATCH 086/100] sign more test assemblies --- tests/Avalonia.Layout.UnitTests/Avalonia.Layout.UnitTests.csproj | 1 + tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj | 1 + 2 files changed, 2 insertions(+) diff --git a/tests/Avalonia.Layout.UnitTests/Avalonia.Layout.UnitTests.csproj b/tests/Avalonia.Layout.UnitTests/Avalonia.Layout.UnitTests.csproj index 463bc50008..74cc6e292b 100644 --- a/tests/Avalonia.Layout.UnitTests/Avalonia.Layout.UnitTests.csproj +++ b/tests/Avalonia.Layout.UnitTests/Avalonia.Layout.UnitTests.csproj @@ -8,6 +8,7 @@ + diff --git a/tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj b/tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj index 65df5e7cb0..14d0f4debf 100644 --- a/tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj +++ b/tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj @@ -29,4 +29,5 @@ + From 289e787938a6e4d898ad46e01440fa0df3d8bc03 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 1 Dec 2020 12:14:39 +0100 Subject: [PATCH 087/100] Implement FluentTheme class. For easier selection of fluent theme in App.xaml. --- samples/RenderDemo/App.xaml | 2 +- samples/Sandbox/App.axaml | 2 +- src/Avalonia.Themes.Fluent/FluentTheme.cs | 114 ++++++++++++++++++ .../Properties/AssemblyInfo.cs | 3 + 4 files changed, 119 insertions(+), 2 deletions(-) create mode 100644 src/Avalonia.Themes.Fluent/FluentTheme.cs create mode 100644 src/Avalonia.Themes.Fluent/Properties/AssemblyInfo.cs diff --git a/samples/RenderDemo/App.xaml b/samples/RenderDemo/App.xaml index eeeaf5b0ae..7cdcea2e1d 100644 --- a/samples/RenderDemo/App.xaml +++ b/samples/RenderDemo/App.xaml @@ -3,7 +3,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="RenderDemo.App"> - + diff --git a/samples/Sandbox/App.axaml b/samples/Sandbox/App.axaml index a351ba93e9..9f97822a97 100644 --- a/samples/Sandbox/App.axaml +++ b/samples/Sandbox/App.axaml @@ -3,6 +3,6 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="Sandbox.App"> - + diff --git a/src/Avalonia.Themes.Fluent/FluentTheme.cs b/src/Avalonia.Themes.Fluent/FluentTheme.cs new file mode 100644 index 0000000000..09e9561cb1 --- /dev/null +++ b/src/Avalonia.Themes.Fluent/FluentTheme.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; +using Avalonia.Styling; + +#nullable enable + +namespace Avalonia.Themes.Fluent +{ + public enum FluentColorScheme + { + Light, + Dark, + } + + /// + /// Includes the fluent theme in an application. + /// + public class FluentTheme : IStyle, IResourceProvider + { + private readonly Uri _baseUri; + private IStyle[]? _loaded; + private bool _isLoading; + + /// + /// Initializes a new instance of the class. + /// + /// The base URL for the XAML context. + public FluentTheme(Uri baseUri) + { + _baseUri = baseUri; + } + + /// + /// Initializes a new instance of the class. + /// + /// The XAML service provider. + public FluentTheme(IServiceProvider serviceProvider) + { + _baseUri = ((IUriContext)serviceProvider.GetService(typeof(IUriContext))).BaseUri; + } + + /// + /// Gets or sets the color scheme to use (light, dark). + /// + public FluentColorScheme Scheme { get; set; } + + public IResourceHost? Owner => (Loaded as IResourceProvider)?.Owner; + + /// + /// Gets the loaded style. + /// + public IStyle Loaded + { + get + { + if (_loaded == null) + { + _isLoading = true; + var loaded = (IStyle)AvaloniaXamlLoader.Load(GetUri(), _baseUri); + _loaded = new[] { loaded }; + _isLoading = false; + } + + return _loaded?[0]!; + } + } + + bool IResourceNode.HasResources => (Loaded as IResourceProvider)?.HasResources ?? false; + + IReadOnlyList IStyle.Children => _loaded ?? Array.Empty(); + + public event EventHandler OwnerChanged + { + add + { + if (Loaded is IResourceProvider rp) + { + rp.OwnerChanged += value; + } + } + remove + { + if (Loaded is IResourceProvider rp) + { + rp.OwnerChanged -= value; + } + } + } + + public SelectorMatchResult TryAttach(IStyleable target, IStyleHost? host) => Loaded.TryAttach(target, host); + + public bool TryGetResource(object key, out object? value) + { + if (!_isLoading && Loaded is IResourceProvider p) + { + return p.TryGetResource(key, out value); + } + + value = null; + return false; + } + + void IResourceProvider.AddOwner(IResourceHost owner) => (Loaded as IResourceProvider)?.AddOwner(owner); + void IResourceProvider.RemoveOwner(IResourceHost owner) => (Loaded as IResourceProvider)?.RemoveOwner(owner); + + private Uri GetUri() => Scheme switch + { + FluentColorScheme.Dark => new Uri("avares://Avalonia.Themes.Fluent/FluentDark.xaml", UriKind.Absolute), + _ => new Uri("avares://Avalonia.Themes.Fluent/FluentLight.xaml", UriKind.Absolute), + }; + } +} diff --git a/src/Avalonia.Themes.Fluent/Properties/AssemblyInfo.cs b/src/Avalonia.Themes.Fluent/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..0bad226ba7 --- /dev/null +++ b/src/Avalonia.Themes.Fluent/Properties/AssemblyInfo.cs @@ -0,0 +1,3 @@ +using Avalonia.Metadata; + +[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Themes.Fluent")] From f08bfb329dfc601afc5a3ae1b78f3add2c68f7ea Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 1 Dec 2020 12:18:45 +0100 Subject: [PATCH 088/100] Update API compat baseline. --- src/Avalonia.Themes.Fluent/ApiCompatBaseline.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/ApiCompatBaseline.txt b/src/Avalonia.Themes.Fluent/ApiCompatBaseline.txt index 68dc477cbc..44e250b889 100644 --- a/src/Avalonia.Themes.Fluent/ApiCompatBaseline.txt +++ b/src/Avalonia.Themes.Fluent/ApiCompatBaseline.txt @@ -1,3 +1,4 @@ Compat issues with assembly Avalonia.Themes.Fluent: -TypesMustExist : Type 'Avalonia.Themes.Fluent.FluentTheme' does not exist in the implementation but it does exist in the contract. -Total Issues: 1 +CannotRemoveBaseTypeOrInterface : Type 'Avalonia.Themes.Fluent.FluentTheme' does not inherit from base type 'Avalonia.Styling.Styles' in the implementation but it does in the contract. +MembersMustExist : Member 'public void Avalonia.Themes.Fluent.FluentTheme..ctor()' does not exist in the implementation but it does exist in the contract. +Total Issues: 2 From 31e4d9b5156e7f0d9d7b41101fd53e30df5a5f9a Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 1 Dec 2020 13:53:38 +0100 Subject: [PATCH 089/100] Renamed Theme -> Mode in FluentTheme. --- samples/Sandbox/App.axaml | 2 +- src/Avalonia.Themes.Fluent/FluentTheme.cs | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/samples/Sandbox/App.axaml b/samples/Sandbox/App.axaml index 9f97822a97..f601f9f78f 100644 --- a/samples/Sandbox/App.axaml +++ b/samples/Sandbox/App.axaml @@ -3,6 +3,6 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="Sandbox.App"> - + diff --git a/src/Avalonia.Themes.Fluent/FluentTheme.cs b/src/Avalonia.Themes.Fluent/FluentTheme.cs index 09e9561cb1..43b71567fa 100644 --- a/src/Avalonia.Themes.Fluent/FluentTheme.cs +++ b/src/Avalonia.Themes.Fluent/FluentTheme.cs @@ -8,7 +8,7 @@ using Avalonia.Styling; namespace Avalonia.Themes.Fluent { - public enum FluentColorScheme + public enum FluentThemeMode { Light, Dark, @@ -42,9 +42,9 @@ namespace Avalonia.Themes.Fluent } /// - /// Gets or sets the color scheme to use (light, dark). + /// Gets or sets the mode of the fluent theme (light, dark). /// - public FluentColorScheme Scheme { get; set; } + public FluentThemeMode Mode { get; set; } public IResourceHost? Owner => (Loaded as IResourceProvider)?.Owner; @@ -105,9 +105,9 @@ namespace Avalonia.Themes.Fluent void IResourceProvider.AddOwner(IResourceHost owner) => (Loaded as IResourceProvider)?.AddOwner(owner); void IResourceProvider.RemoveOwner(IResourceHost owner) => (Loaded as IResourceProvider)?.RemoveOwner(owner); - private Uri GetUri() => Scheme switch + private Uri GetUri() => Mode switch { - FluentColorScheme.Dark => new Uri("avares://Avalonia.Themes.Fluent/FluentDark.xaml", UriKind.Absolute), + FluentThemeMode.Dark => new Uri("avares://Avalonia.Themes.Fluent/FluentDark.xaml", UriKind.Absolute), _ => new Uri("avares://Avalonia.Themes.Fluent/FluentLight.xaml", UriKind.Absolute), }; } From 3a247bde2afc4cc7a31f525a95e3aaf3fd9aaa19 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 2 Dec 2020 21:59:44 +0100 Subject: [PATCH 090/100] LayoutState should call LayoutStateCore. See upstream implementation at https://github.com/microsoft/microsoft-ui-xaml/blob/c6c8d084231f8cc4ffa978305e9f2a0f93a9a39c/dev/Repeater/LayoutContext.cpp#L13-L18 --- src/Avalonia.Layout/LayoutContext.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Layout/LayoutContext.cs b/src/Avalonia.Layout/LayoutContext.cs index 45a8048ea2..dadce58c0c 100644 --- a/src/Avalonia.Layout/LayoutContext.cs +++ b/src/Avalonia.Layout/LayoutContext.cs @@ -14,7 +14,11 @@ namespace Avalonia.Layout /// /// Gets or sets an object that represents the state of a layout. /// - public object LayoutState { get; set; } + public object LayoutState + { + get => LayoutStateCore; + set => LayoutStateCore = value; + } /// /// Implements the behavior of in a derived or custom LayoutContext. From f4eb53330c4b9a17abf575726fa36fa52abce2d0 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Wed, 2 Dec 2020 18:18:17 -0500 Subject: [PATCH 091/100] Add missing DataGridRowHeader template for DataGrid --- .../Themes/Default.xaml | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls.DataGrid/Themes/Default.xaml b/src/Avalonia.Controls.DataGrid/Themes/Default.xaml index cf062e0920..09d19c8e43 100644 --- a/src/Avalonia.Controls.DataGrid/Themes/Default.xaml +++ b/src/Avalonia.Controls.DataGrid/Themes/Default.xaml @@ -137,12 +137,37 @@ + +