Browse Source

Merge branch 'more-skia-caching' of https://github.com/DJGosnell/Avalonia into more-skia-caching

pull/10303/head
DJGosnell 4 years ago
parent
commit
57cb6263f8
  1. 4
      native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj
  2. 39
      native/Avalonia.Native/src/OSX/PlatformBehaviorInhibition.mm
  3. 1
      native/Avalonia.Native/src/OSX/common.h
  4. 11
      native/Avalonia.Native/src/OSX/main.mm
  5. 2
      readme.md
  6. 4
      src/Avalonia.Base/AvaloniaObject.cs
  7. 12
      src/Avalonia.Base/Platform/IPlatformBehaviorInhibition.cs
  8. 47
      src/Avalonia.Base/PropertyStore/BindingEntryBase.cs
  9. 49
      src/Avalonia.Base/PropertyStore/LocalValueBindingObserver.cs
  10. 25
      src/Avalonia.Base/PropertyStore/LocalValueUntypedBindingObserver.cs
  11. 2
      src/Avalonia.Base/PropertyStore/SourceUntypedBindingEntry.cs
  12. 2
      src/Avalonia.Base/PropertyStore/TypedBindingEntry.cs
  13. 5
      src/Avalonia.Base/PropertyStore/UntypedBindingEntry.cs
  14. 17
      src/Avalonia.Base/PropertyStore/ValueStore.cs
  15. 13
      src/Avalonia.Controls/PlatformInhibitionType.cs
  16. 25
      src/Avalonia.Controls/TopLevel.cs
  17. 21
      src/Avalonia.Native/AvaloniaNativePlatform.cs
  18. 20
      src/Avalonia.Native/PlatformBehaviorInhibition.cs
  19. 7
      src/Avalonia.Native/WindowImplBase.cs
  20. 7
      src/Avalonia.Native/avn.idl
  21. 36
      tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs
  22. 18
      tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Validation.cs
  23. 25
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ResourceDictionaryTests.cs

4
native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj

@ -43,6 +43,7 @@
523484CA26EA688F00EA0C2C /* trayicon.mm in Sources */ = {isa = PBXBuildFile; fileRef = 523484C926EA688F00EA0C2C /* trayicon.mm */; };
5B21A982216530F500CEE36E /* cursor.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5B21A981216530F500CEE36E /* cursor.mm */; };
5B8BD94F215BFEA6005ED2A7 /* clipboard.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5B8BD94E215BFEA6005ED2A7 /* clipboard.mm */; };
855EDC9F28C6546F00807998 /* PlatformBehaviorInhibition.mm in Sources */ = {isa = PBXBuildFile; fileRef = 855EDC9E28C6546F00807998 /* PlatformBehaviorInhibition.mm */; };
AB00E4F72147CA920032A60A /* main.mm in Sources */ = {isa = PBXBuildFile; fileRef = AB00E4F62147CA920032A60A /* main.mm */; };
AB1E522C217613570091CD71 /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB1E522B217613570091CD71 /* OpenGL.framework */; };
AB661C1E2148230F00291242 /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB661C1D2148230F00291242 /* AppKit.framework */; };
@ -95,6 +96,7 @@
5B21A981216530F500CEE36E /* cursor.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = cursor.mm; sourceTree = "<group>"; };
5B8BD94E215BFEA6005ED2A7 /* clipboard.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = clipboard.mm; sourceTree = "<group>"; };
5BF943652167AD1D009CAE35 /* cursor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = cursor.h; sourceTree = "<group>"; };
855EDC9E28C6546F00807998 /* PlatformBehaviorInhibition.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = PlatformBehaviorInhibition.mm; sourceTree = "<group>"; };
AB00E4F62147CA920032A60A /* main.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = main.mm; sourceTree = "<group>"; };
AB1E522B217613570091CD71 /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = System/Library/Frameworks/OpenGL.framework; sourceTree = SDKROOT; };
AB661C1D2148230F00291242 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
@ -140,6 +142,7 @@
AB7A61E62147C814003C5833 = {
isa = PBXGroup;
children = (
855EDC9E28C6546F00807998 /* PlatformBehaviorInhibition.mm */,
BC11A5BC2608D58F0017BAD0 /* automation.h */,
BC11A5BD2608D58F0017BAD0 /* automation.mm */,
1A1852DB23E05814008F0DED /* deadlock.mm */,
@ -288,6 +291,7 @@
1A3E5EAE23E9FB1300EDE661 /* cgl.mm in Sources */,
BC11A5BF2608D58F0017BAD0 /* automation.mm in Sources */,
37E2330F21583241000CB7E2 /* KeyTransform.mm in Sources */,
855EDC9F28C6546F00807998 /* PlatformBehaviorInhibition.mm in Sources */,
520624B322973F4100C4DCEF /* menu.mm in Sources */,
37A517B32159597E00FBA241 /* Screens.mm in Sources */,
1AFD334123E03C4F0042899B /* controlhost.mm in Sources */,

39
native/Avalonia.Native/src/OSX/PlatformBehaviorInhibition.mm

@ -0,0 +1,39 @@
#include "common.h"
namespace
{
id<NSObject> s_inhibitAppSleepHandle{};
}
class PlatformBehaviorInhibition : public ComSingleObject<IAvnPlatformBehaviorInhibition, &IID_IAvnCursorFactory>
{
public:
FORWARD_IUNKNOWN()
virtual void SetInhibitAppSleep(bool inhibitAppSleep, char* reason) override
{
START_COM_CALL;
@autoreleasepool
{
if (inhibitAppSleep && s_inhibitAppSleepHandle == nullptr)
{
NSActivityOptions options = NSActivityUserInitiatedAllowingIdleSystemSleep;
s_inhibitAppSleepHandle = [[NSProcessInfo processInfo] beginActivityWithOptions:options reason:[NSString stringWithUTF8String: reason]];
}
if (!inhibitAppSleep)
{
s_inhibitAppSleepHandle = nullptr;
}
}
}
};
extern IAvnPlatformBehaviorInhibition* CreatePlatformBehaviorInhibition()
{
@autoreleasepool
{
return new PlatformBehaviorInhibition();
}
}

1
native/Avalonia.Native/src/OSX/common.h

@ -26,6 +26,7 @@ extern IAvnTrayIcon* CreateTrayIcon();
extern IAvnMenuItem* CreateAppMenuItem();
extern IAvnMenuItem* CreateAppMenuItemSeparator();
extern IAvnApplicationCommands* CreateApplicationCommands();
extern IAvnPlatformBehaviorInhibition* CreatePlatformBehaviorInhibition();
extern IAvnNativeControlHost* CreateNativeControlHost(NSView* parent);
extern IAvnPlatformSettings* CreatePlatformSettings();
extern void SetAppMenu(IAvnMenu *menu);

11
native/Avalonia.Native/src/OSX/main.mm

@ -408,6 +408,17 @@ public:
return S_OK;
}
}
virtual HRESULT CreatePlatformBehaviorInhibition(IAvnPlatformBehaviorInhibition** ppv) override
{
START_COM_CALL;
@autoreleasepool
{
*ppv = ::CreatePlatformBehaviorInhibition();
return S_OK;
}
}
};
extern "C" IAvaloniaNativeFactory* CreateAvaloniaNative()

2
readme.md

@ -1,3 +1,5 @@
[![GH_Banner](https://user-images.githubusercontent.com/552074/218457976-92e76834-9e22-4e35-acfa-aa50281bc0f9.png)](https://avaloniaui.net/xpf)
[![Telegram](https://raw.githubusercontent.com/Patrolavia/telegram-badge/master/chat.svg)](https://t.me/Avalonia)
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/AvaloniaUI/Avalonia?utm_campaign=pr-badge&utm_content=badge&utm_medium=badge&utm_source=badge) [![Discord](https://img.shields.io/badge/discord-join%20chat-46BC99)]( https://aka.ms/dotnet-discord) [![Build Status](https://dev.azure.com/AvaloniaUI/AvaloniaUI/_apis/build/status/AvaloniaUI.Avalonia)](https://dev.azure.com/AvaloniaUI/AvaloniaUI/_build/latest?definitionId=4) [![Backers on Open Collective](https://opencollective.com/Avalonia/backers/badge.svg)](#backers) [![Sponsors on Open Collective](https://opencollective.com/Avalonia/sponsors/badge.svg)](#sponsors) ![License](https://img.shields.io/github/license/avaloniaui/avalonia.svg)
<br />

4
src/Avalonia.Base/AvaloniaObject.cs

@ -277,8 +277,8 @@ namespace Avalonia
/// <param name="property">The property.</param>
/// <returns>True if the property is set, otherwise false.</returns>
/// <remarks>
/// Checks whether a value is assigned to the property, or that there is a binding to the
/// property that is producing a value other than <see cref="AvaloniaProperty.UnsetValue"/>.
/// Returns true if <paramref name="property"/> is a styled property which has a value
/// assigned to it or a binding targeting it; otherwise false.
/// </remarks>
public bool IsSet(AvaloniaProperty property)
{

12
src/Avalonia.Base/Platform/IPlatformBehaviorInhibition.cs

@ -0,0 +1,12 @@
using System.Threading.Tasks;
namespace Avalonia.Platform
{
/// <summary>
/// Allows to inhibit platform specific behavior.
/// </summary>
public interface IPlatformBehaviorInhibition
{
Task SetInhibitAppSleep(bool inhibitAppSleep, string reason);
}
}

47
src/Avalonia.Base/PropertyStore/BindingEntryBase.cs

@ -16,6 +16,8 @@ namespace Avalonia.PropertyStore
private IDisposable? _subscription;
private bool _hasValue;
private TValue? _value;
private TValue? _defaultValue;
private bool _isDefaultValueInitialized;
protected BindingEntryBase(
ValueFrame frame,
@ -89,6 +91,7 @@ namespace Avalonia.PropertyStore
protected abstract BindingValue<TValue> ConvertAndValidate(TSource value);
protected abstract BindingValue<TValue> ConvertAndValidate(BindingValue<TSource> value);
protected abstract TValue GetDefaultValue(Type ownerType);
protected virtual void Start(bool produceValue)
{
@ -104,17 +107,6 @@ namespace Avalonia.PropertyStore
};
}
private void ClearValue()
{
if (_hasValue)
{
_hasValue = false;
_value = default;
if (_subscription is not null)
Frame.Owner?.OnBindingValueCleared(Property, Frame.Priority);
}
}
private void SetValue(BindingValue<TValue> value)
{
static void Execute(BindingEntryBase<TValue, TSource> instance, BindingValue<TValue> value)
@ -124,24 +116,20 @@ namespace Avalonia.PropertyStore
LoggingUtils.LogIfNecessary(instance.Frame.Owner.Owner, instance.Property, value);
if (value.HasValue)
{
if (!instance._hasValue || !EqualityComparer<TValue>.Default.Equals(instance._value, value.Value))
{
instance._value = value.Value;
instance._hasValue = true;
if (instance._subscription is not null && instance._subscription != s_creatingQuiet)
instance.Frame.Owner?.OnBindingValueChanged(instance, instance.Frame.Priority);
}
}
else if (value.Type != BindingValueType.DoNothing)
var effectiveValue = value.HasValue ? value.Value : instance.GetCachedDefaultValue();
if (!instance._hasValue || !EqualityComparer<TValue>.Default.Equals(instance._value, effectiveValue))
{
instance.ClearValue();
instance._value = effectiveValue;
instance._hasValue = true;
if (instance._subscription is not null && instance._subscription != s_creatingQuiet)
instance.Frame.Owner?.OnBindingValueCleared(instance.Property, instance.Frame.Priority);
instance.Frame.Owner?.OnBindingValueChanged(instance, instance.Frame.Priority);
}
}
if (value.Type == BindingValueType.DoNothing)
return;
if (Dispatcher.UIThread.CheckAccess())
{
Execute(this, value);
@ -161,5 +149,16 @@ namespace Avalonia.PropertyStore
_subscription = null;
Frame.OnBindingCompleted(this);
}
private TValue GetCachedDefaultValue()
{
if (!_isDefaultValueInitialized)
{
_defaultValue = GetDefaultValue(Frame.Owner!.Owner.GetType());
_isDefaultValueInitialized = true;
}
return _defaultValue!;
}
}
}

49
src/Avalonia.Base/PropertyStore/LocalValueBindingObserver.cs

@ -10,6 +10,8 @@ namespace Avalonia.PropertyStore
{
private readonly ValueStore _owner;
private IDisposable? _subscription;
private T? _defaultValue;
private bool _isDefaultValueInitialized;
public LocalValueBindingObserver(ValueStore owner, StyledProperty<T> property)
{
@ -41,26 +43,28 @@ namespace Avalonia.PropertyStore
public void OnNext(T value)
{
static void Execute(ValueStore owner, StyledProperty<T> property, T value)
static void Execute(LocalValueBindingObserver<T> instance, T value)
{
if (property.ValidateValue?.Invoke(value) != false)
owner.SetValue(property, value, BindingPriority.LocalValue);
else
owner.ClearLocalValue(property);
var owner = instance._owner;
var property = instance.Property;
if (property.ValidateValue?.Invoke(value) == false)
value = instance.GetCachedDefaultValue();
owner.SetValue(property, value, BindingPriority.LocalValue);
}
if (Dispatcher.UIThread.CheckAccess())
{
Execute(_owner, Property, value);
Execute(this, value);
}
else
{
// To avoid allocating closure in the outer scope we need to capture variables
// locally. This allows us to skip most of the allocations when on UI thread.
var instance = _owner;
var property = Property;
var instance = this;
var newValue = value;
Dispatcher.UIThread.Post(() => Execute(instance, property, newValue));
Dispatcher.UIThread.Post(() => Execute(instance, newValue));
}
}
@ -74,11 +78,21 @@ namespace Avalonia.PropertyStore
LoggingUtils.LogIfNecessary(owner.Owner, property, value);
if (value.HasValue)
owner.SetValue(property, value.Value, BindingPriority.LocalValue);
else if (value.Type != BindingValueType.DataValidationError)
owner.ClearLocalValue(property);
{
var effectiveValue = value.Value;
if (property.ValidateValue?.Invoke(effectiveValue) == false)
effectiveValue = instance.GetCachedDefaultValue();
owner.SetValue(property, effectiveValue, BindingPriority.LocalValue);
}
else
{
owner.SetValue(property, instance.GetCachedDefaultValue(), BindingPriority.LocalValue);
}
}
if (value.Type is BindingValueType.DoNothing or BindingValueType.DataValidationError)
return;
if (Dispatcher.UIThread.CheckAccess())
{
Execute(this, value);
@ -92,5 +106,16 @@ namespace Avalonia.PropertyStore
Dispatcher.UIThread.Post(() => Execute(instance, newValue));
}
}
private T GetCachedDefaultValue()
{
if (!_isDefaultValueInitialized)
{
_defaultValue = Property.GetDefaultValue(_owner.Owner.GetType());
_isDefaultValueInitialized = true;
}
return _defaultValue!;
}
}
}

25
src/Avalonia.Base/PropertyStore/LocalValueUntypedBindingObserver.cs

@ -1,5 +1,4 @@
using System;
using System.Security.Cryptography;
using Avalonia.Data;
using Avalonia.Threading;
@ -10,6 +9,8 @@ namespace Avalonia.PropertyStore
{
private readonly ValueStore _owner;
private IDisposable? _subscription;
private T? _defaultValue;
private bool _isDefaultValueInitialized;
public LocalValueUntypedBindingObserver(ValueStore owner, StyledProperty<T> property)
{
@ -49,11 +50,7 @@ namespace Avalonia.PropertyStore
if (value == AvaloniaProperty.UnsetValue)
{
owner.ClearLocalValue(property);
}
else if (value == BindingOperations.DoNothing)
{
// Do nothing!
owner.SetValue(property, instance.GetCachedDefaultValue(), BindingPriority.LocalValue);
}
else if (UntypedValueUtils.TryConvertAndValidate(property, value, out var typedValue))
{
@ -61,11 +58,14 @@ namespace Avalonia.PropertyStore
}
else
{
owner.ClearLocalValue(property);
owner.SetValue(property, instance.GetCachedDefaultValue(), BindingPriority.LocalValue);
LoggingUtils.LogInvalidValue(owner.Owner, property, typeof(T), value);
}
}
if (value == BindingOperations.DoNothing)
return;
if (Dispatcher.UIThread.CheckAccess())
{
Execute(this, value);
@ -79,5 +79,16 @@ namespace Avalonia.PropertyStore
Dispatcher.UIThread.Post(() => Execute(instance, newValue));
}
}
private T GetCachedDefaultValue()
{
if (!_isDefaultValueInitialized)
{
_defaultValue = Property.GetDefaultValue(_owner.Owner.GetType());
_isDefaultValueInitialized = true;
}
return _defaultValue!;
}
}
}

2
src/Avalonia.Base/PropertyStore/SourceUntypedBindingEntry.cs

@ -31,5 +31,7 @@ namespace Avalonia.PropertyStore
{
throw new NotSupportedException();
}
protected override TTarget GetDefaultValue(Type ownerType) => Property.GetDefaultValue(ownerType);
}
}

2
src/Avalonia.Base/PropertyStore/TypedBindingEntry.cs

@ -48,5 +48,7 @@ namespace Avalonia.PropertyStore
return value;
}
protected override T GetDefaultValue(Type ownerType) => Property.GetDefaultValue(ownerType);
}
}

5
src/Avalonia.Base/PropertyStore/UntypedBindingEntry.cs

@ -29,5 +29,10 @@ namespace Avalonia.PropertyStore
{
throw new NotSupportedException();
}
protected override object? GetDefaultValue(Type ownerType)
{
return ((IStyledPropertyMetadata)Property.GetMetadata(ownerType)).DefaultValue;
}
}
}

17
src/Avalonia.Base/PropertyStore/ValueStore.cs

@ -380,23 +380,6 @@ namespace Avalonia.PropertyStore
}
}
/// <summary>
/// Called by non-LocalValue binding entries to re-evaluate the effective value when the
/// binding produces an unset value.
/// </summary>
/// <param name="property">The bound property.</param>
/// <param name="priority">The priority of binding which produced a new value.</param>
public void OnBindingValueCleared(AvaloniaProperty property, BindingPriority priority)
{
Debug.Assert(priority != BindingPriority.LocalValue);
if (TryGetEffectiveValue(property, out var existing))
{
if (priority <= existing.Priority)
ReevaluateEffectiveValue(property, existing);
}
}
/// <summary>
/// Called by a <see cref="ValueFrame"/> when its <see cref="ValueFrame.IsActive"/>
/// state changes.

13
src/Avalonia.Controls/PlatformInhibitionType.cs

@ -0,0 +1,13 @@
namespace Avalonia.Controls
{
/// <summary>
/// A platform specific behavior that can be inhibited.
/// </summary>
public enum PlatformInhibitionType
{
/// <summary>
/// When inhibited, prevents the app from being put to sleep or being given a lower priority when not in focus.
/// </summary>
AppSleep
}
}

25
src/Avalonia.Controls/TopLevel.cs

@ -20,6 +20,7 @@ using Avalonia.Styling;
using Avalonia.Utilities;
using Avalonia.Input.Platform;
using System.Linq;
using System.Threading.Tasks;
namespace Avalonia.Controls
{
@ -570,6 +571,30 @@ namespace Avalonia.Controls
/// <param name="e">The event args.</param>
protected virtual void OnClosed(EventArgs e) => Closed?.Invoke(this, e);
/// <summary>
/// Requests a <see cref="PlatformInhibitionType"/> to be inhibited.
/// The behavior remains inhibited until the return value is disposed.
/// The available set of <see cref="PlatformInhibitionType"/>s depends on the platform.
/// If a behavior is inhibited on a platform where this type is not supported the request will have no effect.
/// </summary>
protected async Task<IDisposable> RequestPlatformInhibition(PlatformInhibitionType type, string reason)
{
var platformBehaviorInhibition = PlatformImpl?.TryGetFeature<IPlatformBehaviorInhibition>();
if (platformBehaviorInhibition == null)
{
return Disposable.Create(() => { });
}
switch (type)
{
case PlatformInhibitionType.AppSleep:
await platformBehaviorInhibition.SetInhibitAppSleep(true, reason);
return Disposable.Create(() => platformBehaviorInhibition.SetInhibitAppSleep(false, reason).Wait());
default:
return Disposable.Create(() => { });
}
}
/// <summary>
/// Tries to get a service from an <see cref="IAvaloniaDependencyResolver"/>, logging a
/// warning if not found.

21
src/Avalonia.Native/AvaloniaNativePlatform.cs

@ -158,25 +158,4 @@ namespace Avalonia.Native
throw new NotImplementedException();
}
}
public class AvaloniaNativeMacOptions
{
private readonly IAvnMacOptions _opts;
private bool _showInDock;
internal AvaloniaNativeMacOptions(IAvnMacOptions opts)
{
_opts = opts;
ShowInDock = true;
}
public bool ShowInDock
{
get => _showInDock;
set
{
_showInDock = value;
_opts.SetShowInDock(value ? 1 : 0);
}
}
}
}

20
src/Avalonia.Native/PlatformBehaviorInhibition.cs

@ -0,0 +1,20 @@
using System.Threading.Tasks;
using Avalonia.Native.Interop;
using Avalonia.Platform;
namespace Avalonia.Native
{
internal class PlatformBehaviorInhibition : IPlatformBehaviorInhibition
{
readonly IAvnPlatformBehaviorInhibition _native;
internal PlatformBehaviorInhibition(IAvnPlatformBehaviorInhibition native)
=> _native = native;
public Task SetInhibitAppSleep(bool inhibitAppSleep, string reason)
{
_native.SetInhibitAppSleep(inhibitAppSleep ? 1 : 0, reason);
return Task.CompletedTask;
}
}
}

7
src/Avalonia.Native/WindowImplBase.cs

@ -63,6 +63,7 @@ namespace Avalonia.Native
private GlPlatformSurface _glSurface;
private NativeControlHostImpl _nativeControlHost;
private IStorageProvider _storageProvider;
private PlatformBehaviorInhibition _platformBehaviorInhibition;
internal WindowBaseImpl(IAvaloniaNativeFactory factory, AvaloniaNativePlatformOptions opts,
AvaloniaNativeGlPlatformGraphics glFeature)
@ -88,6 +89,7 @@ namespace Avalonia.Native
_savedScaling = RenderScaling;
_nativeControlHost = new NativeControlHostImpl(_native.CreateNativeControlHost());
_storageProvider = new SystemDialogs(this, _factory.CreateSystemDialogs());
_platformBehaviorInhibition = new PlatformBehaviorInhibition(_factory.CreatePlatformBehaviorInhibition());
var monitor = Screen.AllScreens.OrderBy(x => x.Scaling)
.FirstOrDefault(m => m.Bounds.Contains(Position));
@ -521,6 +523,11 @@ namespace Avalonia.Native
return _storageProvider;
}
if (featureType == typeof(IPlatformBehaviorInhibition))
{
return _platformBehaviorInhibition;
}
return null;
}

7
src/Avalonia.Native/avn.idl

@ -503,6 +503,7 @@ interface IAvaloniaNativeFactory : IUnknown
HRESULT CreateTrayIcon(IAvnTrayIcon** ppv);
HRESULT CreateApplicationCommands(IAvnApplicationCommands** ppv);
HRESULT CreatePlatformSettings(IAvnPlatformSettings** ppv);
HRESULT CreatePlatformBehaviorInhibition(IAvnPlatformBehaviorInhibition** ppv);
}
[uuid(233e094f-9b9f-44a3-9a6e-6948bbdd9fb1)]
@ -924,3 +925,9 @@ interface IAvnPlatformSettings : IUnknown
uint GetAccentColor();
void RegisterColorsChange(IAvnActionCallback* callback);
}
[uuid(12edf00d-5803-4d3f-9947-b4840e5e9372)]
interface IAvnPlatformBehaviorInhibition : IUnknown
{
void SetInhibitAppSleep(bool inhibitAppSleep, char* reason);
}

36
tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs

@ -372,6 +372,20 @@ namespace Avalonia.Base.UnitTests
Assert.Null(target.GetValue(property));
}
[Fact]
public void LocalValue_Bind_Generic_To_ValueType_Accepts_UnsetValue()
{
var target = new Class1();
var source = new Subject<BindingValue<double>>();
target.Bind(Class1.QuxProperty, source);
source.OnNext(6.7);
source.OnNext(BindingValue<double>.Unset);
Assert.Equal(5.6, target.GetValue(Class1.QuxProperty));
Assert.True(target.IsSet(Class1.QuxProperty));
}
[Fact]
public void LocalValue_Bind_NonGeneric_To_ValueType_Accepts_UnsetValue()
{
@ -383,7 +397,7 @@ namespace Avalonia.Base.UnitTests
source.OnNext(AvaloniaProperty.UnsetValue);
Assert.Equal(5.6, target.GetValue(Class1.QuxProperty));
Assert.False(target.IsSet(Class1.QuxProperty));
Assert.True(target.IsSet(Class1.QuxProperty));
}
[Fact]
@ -397,7 +411,7 @@ namespace Avalonia.Base.UnitTests
source.OnNext(AvaloniaProperty.UnsetValue);
Assert.Equal(5.6, target.GetValue(Class1.QuxProperty));
Assert.False(target.IsSet(Class1.QuxProperty));
Assert.True(target.IsSet(Class1.QuxProperty));
}
[Fact]
@ -1285,6 +1299,24 @@ namespace Avalonia.Base.UnitTests
subscription.Dispose();
}
[Theory]
[InlineData(BindingPriority.LocalValue)]
[InlineData(BindingPriority.Style)]
public void Binding_Producing_UnsetValue_Does_Not_Cause_Unsubscribe(BindingPriority priority)
{
var target = new Class1();
var source = new Subject<BindingValue<string>>();
target.Bind(Class1.FooProperty, source, priority);
source.OnNext("foo");
Assert.Equal("foo", target.GetValue(Class1.FooProperty));
source.OnNext(BindingValue<string>.Unset);
Assert.Equal("foodefault", target.GetValue(Class1.FooProperty));
source.OnNext("bar");
Assert.Equal("bar", target.GetValue(Class1.FooProperty));
}
[Fact]
public void Produces_Correct_Values_And_Base_Values_With_Multiple_Animation_Bindings()
{

18
tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Validation.cs

@ -66,7 +66,7 @@ namespace Avalonia.Base.UnitTests
}
[Fact]
public void Reverts_To_Lower_Priority_If_Style_Binding_Fails_Validation()
public void Reverts_To_DefaultValue_If_Style_Binding_Fails_Validation_2()
{
var target = new Class1();
var source = new Subject<int>();
@ -75,7 +75,21 @@ namespace Avalonia.Base.UnitTests
target.Bind(Class1.FooProperty, source, BindingPriority.StyleTrigger);
source.OnNext(150);
Assert.Equal(10, target.GetValue(Class1.FooProperty));
Assert.Equal(11, target.GetValue(Class1.FooProperty));
}
[Theory]
[InlineData(BindingPriority.LocalValue)]
[InlineData(BindingPriority.Style)]
public void Reverts_To_DefaultValue_If_Style_Binding_Fails_Validation_3(BindingPriority priority)
{
var target = new Class1();
var source = new Subject<BindingValue<int>>();
target.Bind(Class1.FooProperty, source, priority);
source.OnNext(150);
Assert.Equal(11, target.GetValue(Class1.FooProperty));
}
[Fact]

25
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ResourceDictionaryTests.cs

@ -307,7 +307,30 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
Assert.True(buttonResources.ContainsDeferredKey("Red"));
}
}
[Fact]
public void Dynamically_Changing_Referenced_Resources_Works_With_DynamicResource()
{
var xaml = @"
<UserControl xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<UserControl.Resources>
<Color x:Key='color'>Red</Color>
<SolidColorBrush x:Key='brush' Color='{DynamicResource color}' />
</UserControl.Resources>
</UserControl>";
var userControl = (UserControl)AvaloniaRuntimeXamlLoader.Load(xaml);
Assert.Equal(Colors.Red, ((ISolidColorBrush)userControl.FindResource("brush")!).Color);
userControl.Resources.Remove("color");
Assert.Equal(default, ((ISolidColorBrush)userControl.FindResource("brush")!).Color);
userControl.Resources.Add("color", Colors.Blue);
Assert.Equal(Colors.Blue, ((ISolidColorBrush)userControl.FindResource("brush")!).Color);
}
private IDisposable StyledWindow(params (string, string)[] assets)
{
var services = TestServices.StyledWindow.With(

Loading…
Cancel
Save