diff --git a/.ncrunch/NativeEmbedSample.v3.ncrunchproject b/.ncrunch/NativeEmbedSample.v3.ncrunchproject
new file mode 100644
index 0000000000..319cd523ce
--- /dev/null
+++ b/.ncrunch/NativeEmbedSample.v3.ncrunchproject
@@ -0,0 +1,5 @@
+
+
+ True
+
+
\ No newline at end of file
diff --git a/Avalonia.sln b/Avalonia.sln
index 13ae06d2e6..4ab647a25e 100644
--- a/Avalonia.sln
+++ b/Avalonia.sln
@@ -201,9 +201,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Dialogs", "src\Ava
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.FreeDesktop", "src\Avalonia.FreeDesktop\Avalonia.FreeDesktop.csproj", "{4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Controls.DataGrid.UnitTests", "tests\Avalonia.Controls.DataGrid.UnitTests\Avalonia.Controls.DataGrid.UnitTests.csproj", "{351337F5-D66F-461B-A957-4EF60BDB4BA6}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Controls.DataGrid.UnitTests", "tests\Avalonia.Controls.DataGrid.UnitTests\Avalonia.Controls.DataGrid.UnitTests.csproj", "{351337F5-D66F-461B-A957-4EF60BDB4BA6}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NativeEmbedSample", "samples\interop\NativeEmbedSample\NativeEmbedSample.csproj", "{3C84E04B-36CF-4D0D-B965-C26DD649D1F3}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NativeEmbedSample", "samples\interop\NativeEmbedSample\NativeEmbedSample.csproj", "{3C84E04B-36CF-4D0D-B965-C26DD649D1F3}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Themes.Fluent", "src\Avalonia.Themes.Fluent\Avalonia.Themes.Fluent.csproj", "{C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}"
EndProject
@@ -215,8 +215,8 @@ Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
src\Shared\RenderHelpers\RenderHelpers.projitems*{3c4c0cb4-0c0f-4450-a37b-148c84ff905f}*SharedItemsImports = 13
src\Shared\RenderHelpers\RenderHelpers.projitems*{3e908f67-5543-4879-a1dc-08eace79b3cd}*SharedItemsImports = 5
- src\Shared\PlatformSupport\PlatformSupport.projitems*{4488ad85-1495-4809-9aa4-ddfe0a48527e}*SharedItemsImports = 4
- src\Shared\PlatformSupport\PlatformSupport.projitems*{7b92af71-6287-4693-9dcb-bd5b6e927e23}*SharedItemsImports = 4
+ src\Shared\PlatformSupport\PlatformSupport.projitems*{4488ad85-1495-4809-9aa4-ddfe0a48527e}*SharedItemsImports = 5
+ src\Shared\PlatformSupport\PlatformSupport.projitems*{7b92af71-6287-4693-9dcb-bd5b6e927e23}*SharedItemsImports = 5
src\Shared\RenderHelpers\RenderHelpers.projitems*{7d2d3083-71dd-4cc9-8907-39a0d86fb322}*SharedItemsImports = 5
src\Shared\PlatformSupport\PlatformSupport.projitems*{88060192-33d5-4932-b0f9-8bd2763e857d}*SharedItemsImports = 5
src\Shared\PlatformSupport\PlatformSupport.projitems*{e4d9629c-f168-4224-3f51-a5e482ffbc42}*SharedItemsImports = 13
diff --git a/native/Avalonia.Native/inc/avalonia-native.h b/native/Avalonia.Native/inc/avalonia-native.h
index 1cf3bc75b0..6800ff7d68 100644
--- a/native/Avalonia.Native/inc/avalonia-native.h
+++ b/native/Avalonia.Native/inc/avalonia-native.h
@@ -278,6 +278,7 @@ AVNCOM(IAvnWindow, 04) : virtual IAvnWindowBase
virtual HRESULT SetTitleBarColor (AvnColor color) = 0;
virtual HRESULT SetWindowState(AvnWindowState state) = 0;
virtual HRESULT GetWindowState(AvnWindowState*ret) = 0;
+ virtual HRESULT TakeFocusFromChildren() = 0;
};
AVNCOM(IAvnWindowBaseEvents, 05) : IUnknown
@@ -493,8 +494,8 @@ AVNCOM(IAvnNativeControlHostTopLevelAttachment, 21) : IUnknown
virtual void* GetParentHandle() = 0;
virtual HRESULT InitializeWithChildHandle(void* child) = 0;
virtual HRESULT AttachTo(IAvnNativeControlHost* host) = 0;
- virtual void MoveTo(float x, float y, float width, float height) = 0;
- virtual void Hide() = 0;
+ virtual void ShowInBounds(float x, float y, float width, float height) = 0;
+ virtual void HideWithSize(float width, float height) = 0;
virtual void ReleaseChild() = 0;
};
diff --git a/native/Avalonia.Native/src/OSX/controlhost.mm b/native/Avalonia.Native/src/OSX/controlhost.mm
index 315ec2f310..5ee2344ac7 100644
--- a/native/Avalonia.Native/src/OSX/controlhost.mm
+++ b/native/Avalonia.Native/src/OSX/controlhost.mm
@@ -97,7 +97,7 @@ public:
return S_OK;
};
- virtual void MoveTo(float x, float y, float width, float height) override
+ virtual void ShowInBounds(float x, float y, float width, float height) override
{
if(_child == nil)
return;
@@ -106,7 +106,7 @@ public:
IAvnNativeControlHostTopLevelAttachment* slf = this;
slf->AddRef();
dispatch_async(dispatch_get_main_queue(), ^{
- slf->MoveTo(x, y, width, height);
+ slf->ShowInBounds(x, y, width, height);
slf->Release();
});
return;
@@ -122,9 +122,24 @@ public:
[[_holder superview] setNeedsDisplay:true];
}
- virtual void Hide() override
+ virtual void HideWithSize(float width, float height) override
{
+ if(_child == nil)
+ return;
+ if(AvnInsidePotentialDeadlock::IsInside())
+ {
+ IAvnNativeControlHostTopLevelAttachment* slf = this;
+ slf->AddRef();
+ dispatch_async(dispatch_get_main_queue(), ^{
+ slf->HideWithSize(width, height);
+ slf->Release();
+ });
+ return;
+ }
+
+ NSRect frame = {0, 0, width, height};
[_holder setHidden: true];
+ [_child setFrame: frame];
}
virtual void ReleaseChild() override
diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm
index 86b3584681..7f8a6e1393 100644
--- a/native/Avalonia.Native/src/OSX/window.mm
+++ b/native/Avalonia.Native/src/OSX/window.mm
@@ -116,10 +116,15 @@ public:
{
SetPosition(lastPositionSet);
UpdateStyle();
-
- [Window makeKeyAndOrderFront:Window];
- [NSApp activateIgnoringOtherApps:YES];
-
+ if(ShouldTakeFocusOnShow())
+ {
+ [Window makeKeyAndOrderFront:Window];
+ [NSApp activateIgnoringOtherApps:YES];
+ }
+ else
+ {
+ [Window orderFront: Window];
+ }
[Window setTitle:_lastTitle];
_shown = true;
@@ -128,6 +133,11 @@ public:
}
}
+ virtual bool ShouldTakeFocusOnShow()
+ {
+ return true;
+ }
+
virtual HRESULT Hide () override
{
@autoreleasepool
@@ -774,6 +784,15 @@ private:
}
}
+ virtual HRESULT TakeFocusFromChildren () override
+ {
+ if(Window == nil)
+ return S_OK;
+ if([Window isKeyWindow])
+ [Window makeFirstResponder: View];
+ return S_OK;
+ }
+
void EnterFullScreenMode ()
{
_fullScreenActive = true;
@@ -1858,7 +1877,6 @@ private:
WindowEvents = events;
[Window setLevel:NSPopUpMenuWindowLevel];
}
-
protected:
virtual NSWindowStyleMask GetStyle() override
{
@@ -1876,6 +1894,11 @@ protected:
return S_OK;
}
}
+public:
+ virtual bool ShouldTakeFocusOnShow() override
+ {
+ return false;
+ }
};
extern IAvnPopup* CreateAvnPopup(IAvnWindowEvents*events, IAvnGlContext* gl)
diff --git a/nukebuild/Numerge b/nukebuild/Numerge
index 4464343aef..aef10ae67d 160000
--- a/nukebuild/Numerge
+++ b/nukebuild/Numerge
@@ -1 +1 @@
-Subproject commit 4464343aef5c8ab7a42fcb20a483a6058199f8b8
+Subproject commit aef10ae67dc55c95f49b52a505a0be33bfa297a5
diff --git a/packages/Avalonia/Avalonia.csproj b/packages/Avalonia/Avalonia.csproj
index 2c5a09bee7..cd3ce9adcd 100644
--- a/packages/Avalonia/Avalonia.csproj
+++ b/packages/Avalonia/Avalonia.csproj
@@ -41,6 +41,10 @@
true
build\
+
+ true
+ build\
+
diff --git a/packages/Avalonia/AvaloniaBuildTasks.props b/packages/Avalonia/AvaloniaBuildTasks.props
index 30bafa37ee..deea3aa391 100644
--- a/packages/Avalonia/AvaloniaBuildTasks.props
+++ b/packages/Avalonia/AvaloniaBuildTasks.props
@@ -1,3 +1,11 @@
-
+
+
+
+
+
+
+
+
+
diff --git a/packages/Avalonia/AvaloniaBuildTasks.targets b/packages/Avalonia/AvaloniaBuildTasks.targets
index 537495fcad..84a62bb5c0 100644
--- a/packages/Avalonia/AvaloniaBuildTasks.targets
+++ b/packages/Avalonia/AvaloniaBuildTasks.targets
@@ -4,6 +4,20 @@
<_AvaloniaUseExternalMSBuild Condition="'$(_AvaloniaForceInternalMSBuild)' == 'true'">false
low
+
+
+
+
+ %(Filename)
+ Code
+
+
+ %(Filename)
+ Code
+
+
+
+
+ DependsOnTargets="$(BuildAvaloniaResourcesDependsOn)">
+
+
+
+
diff --git a/packages/Avalonia/AvaloniaItemSchema.xaml b/packages/Avalonia/AvaloniaItemSchema.xaml
new file mode 100644
index 0000000000..a51ea3c0be
--- /dev/null
+++ b/packages/Avalonia/AvaloniaItemSchema.xaml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/readme.md b/readme.md
index a9263d4816..6a04c7e31e 100644
--- a/readme.md
+++ b/readme.md
@@ -68,7 +68,7 @@ Avalonia is licenced under the [MIT licence](licence.md).
## Contributors
-This project exists thanks to all the people who contribute. [[Contribute](http://avaloniaui.net/contributing/contributing)].
+This project exists thanks to all the people who contribute. [[Contribute](http://avaloniaui.net/contributing)].
### Backers
diff --git a/samples/ControlCatalog/Pages/ProgressBarPage.xaml b/samples/ControlCatalog/Pages/ProgressBarPage.xaml
index 13bae59805..2ec0b48c76 100644
--- a/samples/ControlCatalog/Pages/ProgressBarPage.xaml
+++ b/samples/ControlCatalog/Pages/ProgressBarPage.xaml
@@ -1,29 +1,19 @@
-
+
ProgressBar
A progress bar control
-
-
-
+
+
+
-
-
+
-
-
+
-
-
+
+
diff --git a/samples/interop/NativeEmbedSample/MainWindow.xaml b/samples/interop/NativeEmbedSample/MainWindow.xaml
index dcec9035e0..f2161a1bea 100644
--- a/samples/interop/NativeEmbedSample/MainWindow.xaml
+++ b/samples/interop/NativeEmbedSample/MainWindow.xaml
@@ -20,7 +20,16 @@
+
+
+
+ Text
+
+
+ Tooltip
+
+
diff --git a/src/Avalonia.Base/Collections/AvaloniaListExtensions.cs b/src/Avalonia.Base/Collections/AvaloniaListExtensions.cs
index d915887e4c..b52829a60f 100644
--- a/src/Avalonia.Base/Collections/AvaloniaListExtensions.cs
+++ b/src/Avalonia.Base/Collections/AvaloniaListExtensions.cs
@@ -140,6 +140,7 @@ namespace Avalonia.Collections
}
}
+ [Obsolete("Causes memory leaks. Use DynamicData or similar instead.")]
public static IAvaloniaReadOnlyList CreateDerivedList(
this IAvaloniaReadOnlyList collection,
Func select)
diff --git a/src/Avalonia.Base/Threading/DispatcherTimer.cs b/src/Avalonia.Base/Threading/DispatcherTimer.cs
index ebafc8b946..56cde9738e 100644
--- a/src/Avalonia.Base/Threading/DispatcherTimer.cs
+++ b/src/Avalonia.Base/Threading/DispatcherTimer.cs
@@ -14,7 +14,7 @@ namespace Avalonia.Threading
private readonly DispatcherPriority _priority;
private TimeSpan _interval;
-
+
///
/// Initializes a new instance of the class.
///
@@ -154,6 +154,8 @@ namespace Avalonia.Threading
TimeSpan interval,
DispatcherPriority priority = DispatcherPriority.Normal)
{
+ interval = (interval != TimeSpan.Zero) ? interval : TimeSpan.FromTicks(1);
+
var timer = new DispatcherTimer(priority) { Interval = interval };
timer.Tick += (s, e) =>
@@ -197,7 +199,7 @@ namespace Avalonia.Threading
}
}
-
+
///
/// Raises the event on the dispatcher thread.
diff --git a/src/Avalonia.Build.Tasks/GenerateAvaloniaResourcesTask.cs b/src/Avalonia.Build.Tasks/GenerateAvaloniaResourcesTask.cs
index 406abe6f99..ae2bf99d1e 100644
--- a/src/Avalonia.Build.Tasks/GenerateAvaloniaResourcesTask.cs
+++ b/src/Avalonia.Build.Tasks/GenerateAvaloniaResourcesTask.cs
@@ -107,7 +107,7 @@ namespace Avalonia.Build.Tasks
foreach (var s in sources.ToList())
{
- if (s.Path.ToLowerInvariant().EndsWith(".xaml") || s.Path.ToLowerInvariant().EndsWith(".paml"))
+ if (s.Path.ToLowerInvariant().EndsWith(".xaml") || s.Path.ToLowerInvariant().EndsWith(".paml") || s.Path.ToLowerInvariant().EndsWith(".axaml"))
{
XamlFileInfo info;
try
@@ -150,7 +150,7 @@ namespace Avalonia.Build.Tasks
BuildEngine.LogMessage($"GenerateAvaloniaResourcesTask -> Root: {Root}, {Resources?.Count()} resources, Output:{Output}", _reportImportance < MessageImportance.Low ? MessageImportance.High : _reportImportance);
- foreach (var r in EmbeddedResources.Where(r => r.ItemSpec.EndsWith(".xaml") || r.ItemSpec.EndsWith(".paml")))
+ foreach (var r in EmbeddedResources.Where(r => r.ItemSpec.EndsWith(".xaml") || r.ItemSpec.EndsWith(".paml") || r.ItemSpec.EndsWith(".axaml")))
BuildEngine.LogWarning(BuildEngineErrorCode.LegacyResmScheme, r.ItemSpec,
"XAML file is packed using legacy EmbeddedResource/resm scheme, relative URIs won't work");
var resources = BuildResourceSources();
diff --git a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
index 3b69109e68..30e8f120d7 100644
--- a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
+++ b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
@@ -25,7 +25,8 @@ namespace Avalonia.Build.Tasks
public static partial class XamlCompilerTaskExecutor
{
static bool CheckXamlName(IResource r) => r.Name.ToLowerInvariant().EndsWith(".xaml")
- || r.Name.ToLowerInvariant().EndsWith(".paml");
+ || r.Name.ToLowerInvariant().EndsWith(".paml")
+ || r.Name.ToLowerInvariant().EndsWith(".axaml");
public class CompileResult
{
diff --git a/src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs b/src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs
index 8a21d7aa4b..5ff0fd1feb 100644
--- a/src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs
+++ b/src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs
@@ -25,7 +25,7 @@ namespace Avalonia.Controls.Embedding
{
EnsureInitialized();
ApplyTemplate();
- LayoutManager.ExecuteInitialLayoutPass(this);
+ LayoutManager.ExecuteInitialLayoutPass();
}
private void EnsureInitialized()
diff --git a/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevel.cs b/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevel.cs
index d326ab5734..b037dd9901 100644
--- a/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevel.cs
+++ b/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevel.cs
@@ -18,7 +18,7 @@ namespace Avalonia.Controls.Embedding.Offscreen
{
EnsureInitialized();
ApplyTemplate();
- LayoutManager.ExecuteInitialLayoutPass(this);
+ LayoutManager.ExecuteInitialLayoutPass();
}
private void EnsureInitialized()
diff --git a/src/Avalonia.Controls/NativeControlHost.cs b/src/Avalonia.Controls/NativeControlHost.cs
index 94ef0b2284..20eac11c2c 100644
--- a/src/Avalonia.Controls/NativeControlHost.cs
+++ b/src/Avalonia.Controls/NativeControlHost.cs
@@ -1,7 +1,9 @@
+using System;
+using System.Collections.Generic;
using Avalonia.Controls.Platform;
-using Avalonia.LogicalTree;
using Avalonia.Platform;
using Avalonia.Threading;
+using Avalonia.VisualTree;
namespace Avalonia.Controls
{
@@ -12,14 +14,18 @@ namespace Avalonia.Controls
private INativeControlHostControlTopLevelAttachment _attachment;
private IPlatformHandle _nativeControlHandle;
private bool _queuedForDestruction;
+ private bool _queuedForMoveResize;
+ private readonly List _propertyChangedSubscriptions = new List();
+ private readonly EventHandler _propertyChangedHandler;
static NativeControlHost()
{
IsVisibleProperty.Changed.AddClassHandler(OnVisibleChanged);
- TransformedBoundsProperty.Changed.AddClassHandler(OnBoundsChanged);
}
- private static void OnBoundsChanged(NativeControlHost host, AvaloniaPropertyChangedEventArgs arg2)
- => host.UpdateHost();
+ public NativeControlHost()
+ {
+ _propertyChangedHandler = PropertyChangedHandler;
+ }
private static void OnVisibleChanged(NativeControlHost host, AvaloniaPropertyChangedEventArgs arg2)
=> host.UpdateHost();
@@ -27,21 +33,46 @@ namespace Avalonia.Controls
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
_currentRoot = e.Root as TopLevel;
+ var visual = (IVisual)this;
+ while (visual != _currentRoot)
+ {
+
+ if (visual is Visual v)
+ {
+ v.PropertyChanged += _propertyChangedHandler;
+ _propertyChangedSubscriptions.Add(v);
+ }
+
+ visual = visual.GetVisualParent();
+ }
+
UpdateHost();
}
+ private void PropertyChangedHandler(object sender, AvaloniaPropertyChangedEventArgs e)
+ {
+ if (e.IsEffectiveValueChange && e.Property == BoundsProperty)
+ EnqueueForMoveResize();
+ }
+
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
{
_currentRoot = null;
+ if (_propertyChangedSubscriptions != null)
+ {
+ foreach (var v in _propertyChangedSubscriptions)
+ v.PropertyChanged -= _propertyChangedHandler;
+ _propertyChangedSubscriptions.Clear();
+ }
UpdateHost();
}
- void UpdateHost()
+ private void UpdateHost()
{
+ _queuedForMoveResize = false;
_currentHost = (_currentRoot?.PlatformImpl as ITopLevelImplWithNativeControlHost)?.NativeControlHost;
var needsAttachment = _currentHost != null;
- var needsShow = needsAttachment && IsEffectivelyVisible && TransformedBounds.HasValue;
if (needsAttachment)
{
@@ -93,22 +124,46 @@ namespace Avalonia.Controls
}
}
- if (needsShow)
- _attachment?.ShowInBounds(TransformedBounds.Value);
- else if (needsAttachment)
- _attachment?.Hide();
+ if (_attachment?.AttachedTo != _currentHost)
+ return;
+
+ TryUpdateNativeControlPosition();
+ }
+
+
+ private Rect? GetAbsoluteBounds()
+ {
+ var bounds = Bounds;
+ var position = this.TranslatePoint(bounds.Position, _currentRoot);
+ if (position == null)
+ return null;
+ return new Rect(position.Value, bounds.Size);
+ }
+
+ void EnqueueForMoveResize()
+ {
+ if(_queuedForMoveResize)
+ return;
+ _queuedForMoveResize = true;
+ Dispatcher.UIThread.Post(UpdateHost, DispatcherPriority.Render);
}
public bool TryUpdateNativeControlPosition()
{
- var needsShow = _currentHost != null && IsEffectivelyVisible && TransformedBounds.HasValue;
+ if (_currentHost == null)
+ return false;
+
+ var bounds = GetAbsoluteBounds();
+ var needsShow = IsEffectivelyVisible && bounds.HasValue;
- if(needsShow)
- _attachment?.ShowInBounds(TransformedBounds.Value);
- return needsShow;
+ if (needsShow)
+ _attachment?.ShowInBounds(bounds.Value);
+ else
+ _attachment?.HideWithSize(Bounds.Size);
+ return false;
}
- void CheckDestruction()
+ private void CheckDestruction()
{
_queuedForDestruction = false;
if (_currentRoot == null)
@@ -117,10 +172,12 @@ namespace Avalonia.Controls
protected virtual IPlatformHandle CreateNativeControlCore(IPlatformHandle parent)
{
+ if (_currentHost == null)
+ throw new InvalidOperationException();
return _currentHost.CreateDefaultChild(parent);
}
- void DestroyNativeControl()
+ private void DestroyNativeControl()
{
if (_nativeControlHandle != null)
{
diff --git a/src/Avalonia.Controls/Platform/INativeControlHostImpl.cs b/src/Avalonia.Controls/Platform/INativeControlHostImpl.cs
index 7a4568abc6..c6b1d09849 100644
--- a/src/Avalonia.Controls/Platform/INativeControlHostImpl.cs
+++ b/src/Avalonia.Controls/Platform/INativeControlHostImpl.cs
@@ -21,8 +21,8 @@ namespace Avalonia.Controls.Platform
{
INativeControlHostImpl AttachedTo { get; set; }
bool IsCompatibleWith(INativeControlHostImpl host);
- void Hide();
- void ShowInBounds(TransformedBounds transformedBounds);
+ void HideWithSize(Size size);
+ void ShowInBounds(Rect rect);
}
public interface ITopLevelImplWithNativeControlHost
diff --git a/src/Avalonia.Controls/ProgressBar.cs b/src/Avalonia.Controls/ProgressBar.cs
index c7356f2b4d..e904957429 100644
--- a/src/Avalonia.Controls/ProgressBar.cs
+++ b/src/Avalonia.Controls/ProgressBar.cs
@@ -1,8 +1,7 @@
-
using System;
using Avalonia.Controls.Primitives;
-using Avalonia.Data;
using Avalonia.Layout;
+using Avalonia.Media;
namespace Avalonia.Controls
{
@@ -11,6 +10,92 @@ namespace Avalonia.Controls
///
public class ProgressBar : RangeBase
{
+ public class ProgressBarTemplateProperties : AvaloniaObject
+ {
+ private double _container2Width;
+ private double _containerWidth;
+ private double _containerAnimationStartPosition;
+ private double _containerAnimationEndPosition;
+ private double _container2AnimationStartPosition;
+ private double _container2AnimationEndPosition;
+
+ public static readonly DirectProperty ContainerAnimationStartPositionProperty =
+ AvaloniaProperty.RegisterDirect(
+ nameof(ContainerAnimationStartPosition),
+ p => p.ContainerAnimationStartPosition,
+ (p, o) => p.ContainerAnimationStartPosition = o, 0d);
+
+ public static readonly DirectProperty ContainerAnimationEndPositionProperty =
+ AvaloniaProperty.RegisterDirect(
+ nameof(ContainerAnimationEndPosition),
+ p => p.ContainerAnimationEndPosition,
+ (p, o) => p.ContainerAnimationEndPosition = o, 0d);
+
+ public static readonly DirectProperty Container2AnimationStartPositionProperty =
+ AvaloniaProperty.RegisterDirect(
+ nameof(Container2AnimationStartPosition),
+ p => p.Container2AnimationStartPosition,
+ (p, o) => p.Container2AnimationStartPosition = o, 0d);
+
+ public static readonly DirectProperty Container2AnimationEndPositionProperty =
+ AvaloniaProperty.RegisterDirect(
+ nameof(Container2AnimationEndPosition),
+ p => p.Container2AnimationEndPosition,
+ (p, o) => p.Container2AnimationEndPosition = o);
+
+ public static readonly DirectProperty Container2WidthProperty =
+ AvaloniaProperty.RegisterDirect(
+ nameof(Container2Width),
+ p => p.Container2Width,
+ (p, o) => p.Container2Width = o);
+
+ public static readonly DirectProperty ContainerWidthProperty =
+ AvaloniaProperty.RegisterDirect(
+ nameof(ContainerWidth),
+ p => p.ContainerWidth,
+ (p, o) => p.ContainerWidth = o);
+
+ public double ContainerAnimationStartPosition
+ {
+ get => _containerAnimationStartPosition;
+ set => SetAndRaise(ContainerAnimationStartPositionProperty, ref _containerAnimationStartPosition, value);
+ }
+
+ public double ContainerAnimationEndPosition
+ {
+ get => _containerAnimationEndPosition;
+ set => SetAndRaise(ContainerAnimationEndPositionProperty, ref _containerAnimationEndPosition, value);
+ }
+
+ public double Container2AnimationStartPosition
+ {
+ get => _container2AnimationStartPosition;
+ set => SetAndRaise(Container2AnimationStartPositionProperty, ref _container2AnimationStartPosition, value);
+ }
+
+ public double Container2Width
+ {
+ get => _container2Width;
+ set => SetAndRaise(Container2WidthProperty, ref _container2Width, value);
+ }
+
+ public double ContainerWidth
+ {
+ get => _containerWidth;
+ set => SetAndRaise(ContainerWidthProperty, ref _containerWidth, value);
+ }
+
+ public double Container2AnimationEndPosition
+ {
+ get => _container2AnimationEndPosition;
+ set => SetAndRaise(Container2AnimationEndPositionProperty, ref _container2AnimationEndPosition, value);
+ }
+ }
+
+ private double _indeterminateStartingOffset;
+ private double _indeterminateEndingOffset;
+ private Border _indicator;
+
public static readonly StyledProperty IsIndeterminateProperty =
AvaloniaProperty.Register(nameof(IsIndeterminate));
@@ -20,19 +105,33 @@ namespace Avalonia.Controls
public static readonly StyledProperty OrientationProperty =
AvaloniaProperty.Register(nameof(Orientation), Orientation.Horizontal);
- private static readonly DirectProperty IndeterminateStartingOffsetProperty =
+ [Obsolete("To be removed when Avalonia.Themes.Default is discontinued.")]
+ public static readonly DirectProperty IndeterminateStartingOffsetProperty =
AvaloniaProperty.RegisterDirect(
nameof(IndeterminateStartingOffset),
p => p.IndeterminateStartingOffset,
(p, o) => p.IndeterminateStartingOffset = o);
- private static readonly DirectProperty IndeterminateEndingOffsetProperty =
+ [Obsolete("To be removed when Avalonia.Themes.Default is discontinued.")]
+ public static readonly DirectProperty IndeterminateEndingOffsetProperty =
AvaloniaProperty.RegisterDirect(
nameof(IndeterminateEndingOffset),
p => p.IndeterminateEndingOffset,
(p, o) => p.IndeterminateEndingOffset = o);
- private Border _indicator;
+ [Obsolete("To be removed when Avalonia.Themes.Default is discontinued.")]
+ public double IndeterminateStartingOffset
+ {
+ get => _indeterminateStartingOffset;
+ set => SetAndRaise(IndeterminateStartingOffsetProperty, ref _indeterminateStartingOffset, value);
+ }
+
+ [Obsolete("To be removed when Avalonia.Themes.Default is discontinued.")]
+ public double IndeterminateEndingOffset
+ {
+ get => _indeterminateEndingOffset;
+ set => SetAndRaise(IndeterminateEndingOffsetProperty, ref _indeterminateEndingOffset, value);
+ }
static ProgressBar()
{
@@ -45,6 +144,8 @@ namespace Avalonia.Controls
UpdatePseudoClasses(IsIndeterminate, Orientation);
}
+ public ProgressBarTemplateProperties TemplateProperties { get; } = new ProgressBarTemplateProperties();
+
public bool IsIndeterminate
{
get => GetValue(IsIndeterminateProperty);
@@ -62,19 +163,6 @@ namespace Avalonia.Controls
get => GetValue(OrientationProperty);
set => SetValue(OrientationProperty, value);
}
- private double _indeterminateStartingOffset;
- private double IndeterminateStartingOffset
- {
- get => _indeterminateStartingOffset;
- set => SetAndRaise(IndeterminateStartingOffsetProperty, ref _indeterminateStartingOffset, value);
- }
-
- private double _indeterminateEndingOffset;
- private double IndeterminateEndingOffset
- {
- get => _indeterminateEndingOffset;
- set => SetAndRaise(IndeterminateEndingOffsetProperty, ref _indeterminateEndingOffset, value);
- }
///
protected override Size ArrangeOverride(Size finalSize)
@@ -111,21 +199,33 @@ namespace Avalonia.Controls
{
if (IsIndeterminate)
{
- if (Orientation == Orientation.Horizontal)
- {
- var width = bounds.Width / 5.0;
- IndeterminateStartingOffset = -width;
- _indicator.Width = width;
- IndeterminateEndingOffset = bounds.Width;
+ // Pulled from ModernWPF.
- }
- else
- {
- var height = bounds.Height / 5.0;
- IndeterminateStartingOffset = -bounds.Height;
- _indicator.Height = height;
- IndeterminateEndingOffset = height;
- }
+ var dim = Orientation == Orientation.Horizontal ? bounds.Width : bounds.Height;
+ var barIndicatorWidth = dim * 0.4; // Indicator width at 40% of ProgressBar
+ var barIndicatorWidth2 = dim * 0.6; // Indicator width at 60% of ProgressBar
+
+ TemplateProperties.ContainerWidth = barIndicatorWidth;
+ TemplateProperties.Container2Width = barIndicatorWidth2;
+
+ TemplateProperties.ContainerAnimationStartPosition = barIndicatorWidth * -1.8; // Position at -180%
+ TemplateProperties.ContainerAnimationEndPosition = barIndicatorWidth * 3.0; // Position at 300%
+
+ TemplateProperties.Container2AnimationStartPosition = barIndicatorWidth2 * -1.5; // Position at -150%
+ TemplateProperties.Container2AnimationEndPosition = barIndicatorWidth2 * 1.66; // Position at 166%
+
+ // Remove these properties when we switch to fluent as default and removed the old one.
+ IndeterminateStartingOffset = -(dim / 5d);
+ IndeterminateEndingOffset = dim;
+
+ var padding = Padding;
+ var rectangle = new RectangleGeometry(
+ new Rect(
+ padding.Left,
+ padding.Top,
+ bounds.Width - (padding.Right + padding.Left),
+ bounds.Height - (padding.Bottom + padding.Top)
+ ));
}
else
{
diff --git a/src/Avalonia.Controls/Repeater/ViewportManager.cs b/src/Avalonia.Controls/Repeater/ViewportManager.cs
index b705a518ff..0d22187b34 100644
--- a/src/Avalonia.Controls/Repeater/ViewportManager.cs
+++ b/src/Avalonia.Controls/Repeater/ViewportManager.cs
@@ -49,8 +49,8 @@ namespace Avalonia.Controls
// For non-virtualizing layouts, we do not need to keep
// updating viewports and invalidating measure often. So when
// a non virtualizing layout is used, we stop doing all that work.
- bool _managingViewportDisabled;
- private IDisposable _effectiveViewportChangedRevoker;
+ private bool _managingViewportDisabled;
+ private bool _effectiveViewportChangedSubscribed;
private bool _layoutUpdatedSubscribed;
public ViewportManager(ItemsRepeater owner)
@@ -228,11 +228,15 @@ namespace Avalonia.Controls
_pendingViewportShift = default;
_unshiftableShift = default;
- _effectiveViewportChangedRevoker?.Dispose();
-
- if (!_managingViewportDisabled)
+ if (_managingViewportDisabled && _effectiveViewportChangedSubscribed)
{
- _effectiveViewportChangedRevoker = SubscribeToEffectiveViewportChanged(_owner);
+ _owner.EffectiveViewportChanged -= OnEffectiveViewportChanged;
+ _effectiveViewportChangedSubscribed = false;
+ }
+ else if (!_managingViewportDisabled && !_effectiveViewportChangedSubscribed)
+ {
+ _owner.EffectiveViewportChanged += OnEffectiveViewportChanged;
+ _effectiveViewportChangedSubscribed = true;
}
}
@@ -340,6 +344,11 @@ namespace Avalonia.Controls
// Note that the element being brought into view could be a descendant.
var targetChild = GetImmediateChildOfRepeater((IControl)args.TargetObject);
+ if (targetChild is null)
+ {
+ return;
+ }
+
// Make sure that only the target child can be the anchor during the bring into view operation.
foreach (var child in _owner.Children)
{
@@ -373,7 +382,7 @@ namespace Avalonia.Controls
if (parent == null)
{
- throw new InvalidOperationException("OnBringIntoViewRequested called with args.target element not under the ItemsRepeater that recieved the call");
+ return null;
}
return targetChild;
@@ -415,15 +424,15 @@ namespace Avalonia.Controls
_scroller = null;
}
- _effectiveViewportChangedRevoker?.Dispose();
- _effectiveViewportChangedRevoker = null;
+ _owner.EffectiveViewportChanged -= OnEffectiveViewportChanged;
+ _effectiveViewportChangedSubscribed = false;
_ensuredScroller = false;
}
- private void OnEffectiveViewportChanged(Rect effectiveViewport)
+ private void OnEffectiveViewportChanged(object sender, EffectiveViewportChangedEventArgs e)
{
Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: EffectiveViewportChanged event callback", _owner.Layout.LayoutId);
- UpdateViewport(effectiveViewport);
+ UpdateViewport(e.EffectiveViewport);
_pendingViewportShift = default;
_unshiftableShift = default;
@@ -468,8 +477,8 @@ namespace Avalonia.Controls
}
else if (!_managingViewportDisabled)
{
- _effectiveViewportChangedRevoker?.Dispose();
- _effectiveViewportChangedRevoker = SubscribeToEffectiveViewportChanged(_owner);
+ _owner.EffectiveViewportChanged += OnEffectiveViewportChanged;
+ _effectiveViewportChangedSubscribed = true;
}
_ensuredScroller = true;
@@ -529,38 +538,6 @@ namespace Avalonia.Controls
}
}
- private IDisposable SubscribeToEffectiveViewportChanged(IControl control)
- {
- // HACK: This is a bit of a hack. We need the effective viewport of the ItemsRepeater -
- // we can get this from TransformedBounds, but this property is updated after layout has
- // run, which is too late. Instead, for now lets just hook into an internal event on
- // ScrollContentPresenter to find out what the offset and viewport will be after arrange
- // and use those values. Note that this doesn't handle nested ScrollViewers at all, but
- // it's enough to get scrolling to non-uniformly sized items working for now.
- //
- // UWP uses the EffectiveViewportChanged event (which I think was implemented specially
- // for this case): we need to implement that in Avalonia, but the semantics of it aren't
- // clear to me. Hopefully the source for this event will be released with WinUI 3.
- if (control.VisualParent is ScrollContentPresenter scp)
- {
- scp.PreArrange += ScrollContentPresenterPreArrange;
- return Disposable.Create(() => scp.PreArrange -= ScrollContentPresenterPreArrange);
- }
-
- return Disposable.Empty;
- }
-
- private void ScrollContentPresenterPreArrange(object sender, VectorEventArgs e)
- {
- var scp = (ScrollContentPresenter)sender;
- var effectiveViewport = new Rect((Point)scp.Offset, new Size(e.Vector.X, e.Vector.Y));
-
- if (effectiveViewport != _visibleWindow)
- {
- OnEffectiveViewportChanged(effectiveViewport);
- }
- }
-
private class ScrollerInfo
{
public ScrollerInfo(ScrollViewer scroller)
diff --git a/src/Avalonia.Controls/SelectionModel.cs b/src/Avalonia.Controls/SelectionModel.cs
index ff1c0260bb..aa6552579f 100644
--- a/src/Avalonia.Controls/SelectionModel.cs
+++ b/src/Avalonia.Controls/SelectionModel.cs
@@ -189,8 +189,6 @@ namespace Avalonia.Controls
}
set
{
- var isSelected = IsSelectedWithPartialAt(value);
-
if (!IsSelectedAt(value) || SelectedItems.Count > 1)
{
using var operation = new Operation(this);
diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs
index c5491746a3..f058942116 100644
--- a/src/Avalonia.Controls/TopLevel.cs
+++ b/src/Avalonia.Controls/TopLevel.cs
@@ -318,7 +318,7 @@ namespace Avalonia.Controls
///
/// Creates the layout manager for this .
///
- protected virtual ILayoutManager CreateLayoutManager() => new LayoutManager();
+ protected virtual ILayoutManager CreateLayoutManager() => new LayoutManager(this);
///
/// Handles a paint notification from .
@@ -340,6 +340,9 @@ namespace Avalonia.Controls
_globalStyles.GlobalStylesRemoved -= ((IStyleHost)this).StylesRemoved;
}
+ Renderer?.Dispose();
+ Renderer = null;
+
var logicalArgs = new LogicalTreeAttachmentEventArgs(this, this, null);
((ILogical)this).NotifyDetachedFromLogicalTree(logicalArgs);
@@ -349,8 +352,8 @@ namespace Avalonia.Controls
(this as IInputRoot).MouseDevice?.TopLevelClosed(this);
PlatformImpl = null;
OnClosed(EventArgs.Empty);
- Renderer?.Dispose();
- Renderer = null;
+
+ LayoutManager?.Dispose();
}
///
diff --git a/src/Avalonia.Controls/Utils/IEnumerableUtils.cs b/src/Avalonia.Controls/Utils/IEnumerableUtils.cs
index 9b6444fc66..9614d079d9 100644
--- a/src/Avalonia.Controls/Utils/IEnumerableUtils.cs
+++ b/src/Avalonia.Controls/Utils/IEnumerableUtils.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections;
+using System.Collections.Generic;
using System.Linq;
namespace Avalonia.Controls.Utils
@@ -15,12 +16,14 @@ namespace Avalonia.Controls.Utils
{
if (items != null)
{
- var collection = items as ICollection;
-
- if (collection != null)
+ if (items is ICollection collection)
{
return collection.Count;
}
+ else if (items is IReadOnlyCollection