Merge remote-tracking branch 'upstream/master' into dbus-codegen
This commit is contained in:
+11
-10
@@ -55,16 +55,17 @@ dotnet_naming_symbols.constant_fields.required_modifiers = const
|
||||
|
||||
dotnet_naming_style.pascal_case_style.capitalization = pascal_case
|
||||
|
||||
# static fields should have s_ prefix
|
||||
dotnet_naming_rule.static_fields_should_have_prefix.severity = suggestion
|
||||
dotnet_naming_rule.static_fields_should_have_prefix.symbols = static_fields
|
||||
dotnet_naming_rule.static_fields_should_have_prefix.style = static_prefix_style
|
||||
# private static fields should have s_ prefix
|
||||
dotnet_naming_rule.private_static_fields_should_have_prefix.severity = suggestion
|
||||
dotnet_naming_rule.private_static_fields_should_have_prefix.symbols = private_static_fields
|
||||
dotnet_naming_rule.private_static_fields_should_have_prefix.style = private_static_prefix_style
|
||||
|
||||
dotnet_naming_symbols.static_fields.applicable_kinds = field
|
||||
dotnet_naming_symbols.static_fields.required_modifiers = static
|
||||
dotnet_naming_symbols.private_static_fields.applicable_kinds = field
|
||||
dotnet_naming_symbols.private_static_fields.required_modifiers = static
|
||||
dotnet_naming_symbols.private_static_fields.applicable_accessibilities = private
|
||||
|
||||
dotnet_naming_style.static_prefix_style.required_prefix = s_
|
||||
dotnet_naming_style.static_prefix_style.capitalization = camel_case
|
||||
dotnet_naming_style.private_static_prefix_style.required_prefix = s_
|
||||
dotnet_naming_style.private_static_prefix_style.capitalization = camel_case
|
||||
|
||||
# internal and private fields should be _camelCase
|
||||
dotnet_naming_rule.camel_case_for_private_internal_fields.severity = suggestion
|
||||
@@ -117,7 +118,7 @@ csharp_space_after_dot = false
|
||||
csharp_space_after_keywords_in_control_flow_statements = true
|
||||
csharp_space_after_semicolon_in_for_statement = true
|
||||
csharp_space_around_binary_operators = before_and_after
|
||||
csharp_space_around_declaration_statements = do_not_ignore
|
||||
csharp_space_around_declaration_statements = false
|
||||
csharp_space_before_colon_in_inheritance_clause = true
|
||||
csharp_space_before_comma = false
|
||||
csharp_space_before_dot = false
|
||||
@@ -211,5 +212,5 @@ indent_size = 2
|
||||
# Shell scripts
|
||||
[*.sh]
|
||||
end_of_line = lf
|
||||
[*.{cmd, bat}]
|
||||
[*.{cmd,bat}]
|
||||
end_of_line = crlf
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
<ProjectConfiguration>
|
||||
<Settings>
|
||||
<XUnit2Enabled>False</XUnit2Enabled>
|
||||
</Settings>
|
||||
</ProjectConfiguration>
|
||||
@@ -0,0 +1,5 @@
|
||||
<ProjectConfiguration>
|
||||
<Settings>
|
||||
<IgnoreThisComponentCompletely>True</IgnoreThisComponentCompletely>
|
||||
</Settings>
|
||||
</ProjectConfiguration>
|
||||
@@ -47,6 +47,7 @@
|
||||
"tests\\Avalonia.Base.UnitTests\\Avalonia.Base.UnitTests.csproj",
|
||||
"tests\\Avalonia.Benchmarks\\Avalonia.Benchmarks.csproj",
|
||||
"tests\\Avalonia.Controls.DataGrid.UnitTests\\Avalonia.Controls.DataGrid.UnitTests.csproj",
|
||||
"tests\\Avalonia.Controls.ItemsRepeater.UnitTests\\Avalonia.Controls.ItemsRepeater.UnitTests.csproj",
|
||||
"tests\\Avalonia.Controls.UnitTests\\Avalonia.Controls.UnitTests.csproj",
|
||||
"tests\\Avalonia.DesignerSupport.TestApp\\Avalonia.DesignerSupport.TestApp.csproj",
|
||||
"tests\\Avalonia.DesignerSupport.Tests\\Avalonia.DesignerSupport.Tests.csproj",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<PackageReference Include="HarfBuzzSharp" Version="2.8.2.1-preview.108" />
|
||||
<PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="HarfBuzzSharp.NativeAssets.Linux" Version="2.8.2.1-preview.108" />
|
||||
<PackageReference Condition="'$(IncludeWasmSkia)' == 'true'" Include="HarfBuzzSharp.NativeAssets.WebAssembly" Version="2.8.2.1-preview.108" />
|
||||
<PackageReference Include="HarfBuzzSharp" Version="2.8.2.3" />
|
||||
<PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="HarfBuzzSharp.NativeAssets.Linux" Version="2.8.2.3" />
|
||||
<PackageReference Condition="'$(IncludeWasmSkia)' == 'true'" Include="HarfBuzzSharp.NativeAssets.WebAssembly" Version="2.8.2.3" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="2.1.1" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="2.1.3" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Moq" Version="4.14.1" />
|
||||
<PackageReference Include="Moq" Version="4.18.4" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<Product>Avalonia</Product>
|
||||
<Version>11.0.999</Version>
|
||||
<Authors>Avalonia Team</Authors>
|
||||
<Copyright>Copyright 2022 © The AvaloniaUI Project</Copyright>
|
||||
<PackageProjectUrl>https://avaloniaui.net</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/AvaloniaUI/Avalonia/</RepositoryUrl>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SkiaSharp" Version="2.88.1" />
|
||||
<PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="SkiaSharp.NativeAssets.Linux" Version="2.88.1" />
|
||||
<PackageReference Condition="'$(IncludeWasmSkia)' == 'true'" Include="SkiaSharp.NativeAssets.WebAssembly" Version="2.88.1" />
|
||||
<PackageReference Include="SkiaSharp" Version="2.88.3" />
|
||||
<PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="SkiaSharp.NativeAssets.Linux" Version="2.88.3" />
|
||||
<PackageReference Condition="'$(IncludeWasmSkia)' == 'true'" Include="SkiaSharp.NativeAssets.WebAssembly" Version="2.88.3" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
+7
-8
@@ -1,13 +1,12 @@
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.abstractions" Version="2.0.3" />
|
||||
<PackageReference Include="xunit.assert" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.core" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.extensibility.core" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.extensibility.execution" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.console" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
|
||||
<PackageReference Include="xunit" Version="2.4.2" />
|
||||
<PackageReference Include="xunit.assert" Version="2.4.2" />
|
||||
<PackageReference Include="xunit.core" Version="2.4.2" />
|
||||
<PackageReference Include="xunit.extensibility.core" Version="2.4.2" />
|
||||
<PackageReference Include="xunit.extensibility.execution" Version="2.4.2" />
|
||||
<PackageReference Include="xunit.runner.console" Version="2.4.2" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5" />
|
||||
<PackageReference Include="Xunit.SkippableFact" Version="1.4.13" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.0" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -70,6 +70,7 @@
|
||||
<ComboBoxItem>Item 0</ComboBoxItem>
|
||||
<ComboBoxItem>Item 1</ComboBoxItem>
|
||||
</ComboBox>
|
||||
<CheckBox Name="ComboBoxWrapSelection" IsChecked="{Binding #BasicComboBox.WrapSelection}">Wrap Selection</CheckBox>
|
||||
<Button Name="ComboBoxSelectionClear">Clear Selection</Button>
|
||||
<Button Name="ComboBoxSelectFirst">Select First</Button>
|
||||
</StackPanel>
|
||||
|
||||
@@ -79,15 +79,12 @@ namespace Avalonia.Animation
|
||||
/// <param name="culture">culture of the string</param>
|
||||
/// <exception cref="FormatException">Thrown if the string does not have 4 values</exception>
|
||||
/// <returns>A <see cref="KeySpline"/> with the appropriate values set</returns>
|
||||
public static KeySpline Parse(string value, CultureInfo culture)
|
||||
public static KeySpline Parse(string value, CultureInfo? culture)
|
||||
{
|
||||
if (culture is null)
|
||||
culture = CultureInfo.InvariantCulture;
|
||||
culture ??= CultureInfo.InvariantCulture;
|
||||
|
||||
using (var tokenizer = new StringTokenizer((string)value, culture, exceptionMessage: $"Invalid KeySpline string: \"{value}\"."))
|
||||
{
|
||||
return new KeySpline(tokenizer.ReadDouble(), tokenizer.ReadDouble(), tokenizer.ReadDouble(), tokenizer.ReadDouble());
|
||||
}
|
||||
using var tokenizer = new StringTokenizer(value, culture, exceptionMessage: $"Invalid KeySpline string: \"{value}\".");
|
||||
return new KeySpline(tokenizer.ReadDouble(), tokenizer.ReadDouble(), tokenizer.ReadDouble(), tokenizer.ReadDouble());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -152,7 +152,7 @@ namespace Avalonia
|
||||
property = property ?? throw new ArgumentNullException(nameof(property));
|
||||
VerifyAccess();
|
||||
|
||||
_values?.ClearLocalValue(property);
|
||||
_values.ClearLocalValue(property);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -242,7 +242,14 @@ namespace Avalonia
|
||||
return registered.InvokeGetter(this);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
/// <summary>
|
||||
/// Gets an <see cref="AvaloniaProperty"/> base value.
|
||||
/// </summary>
|
||||
/// <param name="property">The property.</param>
|
||||
/// <remarks>
|
||||
/// Gets the value of the property excluding animated values, otherwise <see cref="Optional{T}.Empty"/>.
|
||||
/// Note that this method does not return property values that come from inherited or default values.
|
||||
/// </remarks>
|
||||
public Optional<T> GetBaseValue<T>(StyledProperty<T> property)
|
||||
{
|
||||
_ = property ?? throw new ArgumentNullException(nameof(property));
|
||||
@@ -261,7 +268,7 @@ namespace Avalonia
|
||||
|
||||
VerifyAccess();
|
||||
|
||||
return _values?.IsAnimating(property) ?? false;
|
||||
return _values.IsAnimating(property);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -279,7 +286,7 @@ namespace Avalonia
|
||||
|
||||
VerifyAccess();
|
||||
|
||||
return _values?.IsSet(property) ?? false;
|
||||
return _values.IsSet(property);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -515,14 +522,12 @@ namespace Avalonia
|
||||
/// <param name="property">The property.</param>
|
||||
public void CoerceValue(AvaloniaProperty property) => _values.CoerceValue(property);
|
||||
|
||||
/// <inheritdoc/>
|
||||
internal void AddInheritanceChild(AvaloniaObject child)
|
||||
{
|
||||
_inheritanceChildren ??= new List<AvaloniaObject>();
|
||||
_inheritanceChildren.Add(child);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
||||
internal void RemoveInheritanceChild(AvaloniaObject child)
|
||||
{
|
||||
_inheritanceChildren?.Remove(child);
|
||||
@@ -541,24 +546,11 @@ namespace Avalonia
|
||||
return new AvaloniaPropertyValue(
|
||||
property,
|
||||
GetValue(property),
|
||||
BindingPriority.Unset,
|
||||
"Local Value");
|
||||
}
|
||||
else if (_values != null)
|
||||
{
|
||||
var result = _values.GetDiagnostic(property);
|
||||
|
||||
if (result != null)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
BindingPriority.LocalValue,
|
||||
null);
|
||||
}
|
||||
|
||||
return new AvaloniaPropertyValue(
|
||||
property,
|
||||
GetValue(property),
|
||||
BindingPriority.Unset,
|
||||
"Unset");
|
||||
return _values.GetDiagnostic(property);
|
||||
}
|
||||
|
||||
internal ValueStore GetValueStore() => _values;
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace Avalonia.Data.Converters
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
return targetType.IsValueType ? AvaloniaProperty.UnsetValue : null;
|
||||
return null;
|
||||
}
|
||||
|
||||
if (typeof(ICommand).IsAssignableFrom(targetType) && value is Delegate d && d.Method.GetParameters().Length <= 1)
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
using System;
|
||||
using Avalonia.Data;
|
||||
|
||||
namespace Avalonia.Diagnostics
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -1,36 +1,28 @@
|
||||
using System;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Metadata;
|
||||
using Avalonia.VisualTree;
|
||||
|
||||
namespace Avalonia.Input
|
||||
{
|
||||
public class DragEventArgs : RoutedEventArgs
|
||||
{
|
||||
private Interactive _target;
|
||||
private Point _targetLocation;
|
||||
private readonly Interactive _target;
|
||||
private readonly Point _targetLocation;
|
||||
|
||||
public DragDropEffects DragEffects { get; set; }
|
||||
|
||||
public IDataObject Data { get; private set; }
|
||||
public IDataObject Data { get; }
|
||||
|
||||
public KeyModifiers KeyModifiers { get; private set; }
|
||||
public KeyModifiers KeyModifiers { get; }
|
||||
|
||||
public Point GetPosition(Visual relativeTo)
|
||||
{
|
||||
var point = new Point(0, 0);
|
||||
|
||||
if (relativeTo == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(relativeTo));
|
||||
}
|
||||
|
||||
if (_target != null)
|
||||
{
|
||||
point = _target.TranslatePoint(_targetLocation, relativeTo) ?? point;
|
||||
}
|
||||
|
||||
return point;
|
||||
return _target.TranslatePoint(_targetLocation, relativeTo) ?? new Point(0, 0);
|
||||
}
|
||||
|
||||
[Unstable]
|
||||
|
||||
@@ -136,7 +136,7 @@ namespace Avalonia.Input
|
||||
return StringBuilderCache.GetStringAndRelease(s);
|
||||
}
|
||||
|
||||
public bool Matches(KeyEventArgs keyEvent) =>
|
||||
public bool Matches(KeyEventArgs? keyEvent) =>
|
||||
keyEvent != null &&
|
||||
keyEvent.KeyModifiers == KeyModifiers &&
|
||||
ResolveNumPadOperationKey(keyEvent.Key) == ResolveNumPadOperationKey(Key);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Avalonia.Input.Navigation;
|
||||
using Avalonia.VisualTree;
|
||||
|
||||
@@ -51,7 +50,7 @@ namespace Avalonia.Input
|
||||
|
||||
// If there's a custom keyboard navigation handler as an ancestor, use that.
|
||||
var custom = (element as Visual)?.FindAncestorOfType<ICustomKeyboardNavigation>(true);
|
||||
if (custom is object && HandlePreCustomNavigation(custom, element, direction, out var ce))
|
||||
if (custom is not null && HandlePreCustomNavigation(custom, element, direction, out var ce))
|
||||
return ce;
|
||||
|
||||
var result = direction switch
|
||||
@@ -117,32 +116,27 @@ namespace Avalonia.Input
|
||||
NavigationDirection direction,
|
||||
[NotNullWhen(true)] out IInputElement? result)
|
||||
{
|
||||
if (customHandler != null)
|
||||
var (handled, next) = customHandler.GetNext(element, direction);
|
||||
|
||||
if (handled)
|
||||
{
|
||||
var (handled, next) = customHandler.GetNext(element, direction);
|
||||
|
||||
if (handled)
|
||||
if (next is not null)
|
||||
{
|
||||
if (next != null)
|
||||
{
|
||||
result = next;
|
||||
return true;
|
||||
}
|
||||
else if (direction == NavigationDirection.Next || direction == NavigationDirection.Previous)
|
||||
{
|
||||
var r = direction switch
|
||||
{
|
||||
NavigationDirection.Next => TabNavigation.GetNextTabOutside(customHandler),
|
||||
NavigationDirection.Previous => TabNavigation.GetPrevTabOutside(customHandler),
|
||||
_ => throw new NotSupportedException(),
|
||||
};
|
||||
result = next;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (r is object)
|
||||
{
|
||||
result = r;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
var r = direction switch
|
||||
{
|
||||
NavigationDirection.Next => TabNavigation.GetNextTabOutside(customHandler),
|
||||
NavigationDirection.Previous => TabNavigation.GetPrevTabOutside(customHandler),
|
||||
_ => null
|
||||
};
|
||||
|
||||
if (r is not null)
|
||||
{
|
||||
result = r;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Avalonia.VisualTree;
|
||||
|
||||
namespace Avalonia.Input.Navigation
|
||||
@@ -54,8 +52,7 @@ namespace Avalonia.Input.Navigation
|
||||
// Avoid the endless loop here for Cycle groups
|
||||
if (loopStartElement == nextTabElement)
|
||||
break;
|
||||
if (loopStartElement == null)
|
||||
loopStartElement = nextTabElement;
|
||||
loopStartElement ??= nextTabElement;
|
||||
|
||||
var firstTabElementInside = GetNextTab(null, nextTabElement, true);
|
||||
if (firstTabElementInside != null)
|
||||
@@ -80,12 +77,9 @@ namespace Avalonia.Input.Navigation
|
||||
|
||||
public static IInputElement? GetNextTabOutside(ICustomKeyboardNavigation e)
|
||||
{
|
||||
if (e is IInputElement container)
|
||||
if (e is IInputElement container && GetLastInTree(container) is { } last)
|
||||
{
|
||||
var last = GetLastInTree(container);
|
||||
|
||||
if (last is object)
|
||||
return GetNextTab(last, false);
|
||||
return GetNextTab(last, false);
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -93,11 +87,8 @@ namespace Avalonia.Input.Navigation
|
||||
|
||||
public static IInputElement? GetPrevTab(IInputElement? e, IInputElement? container, bool goDownOnly)
|
||||
{
|
||||
if (e is null && container is null)
|
||||
throw new InvalidOperationException("Either 'e' or 'container' must be non-null.");
|
||||
|
||||
if (container is null)
|
||||
container = GetGroupParent(e!);
|
||||
container ??=
|
||||
GetGroupParent(e ?? throw new InvalidOperationException("Either 'e' or 'container' must be non-null."));
|
||||
|
||||
KeyboardNavigationMode tabbingType = GetKeyNavigationMode(container);
|
||||
|
||||
@@ -163,8 +154,7 @@ namespace Avalonia.Input.Navigation
|
||||
// Avoid the endless loop here
|
||||
if (loopStartElement == nextTabElement)
|
||||
break;
|
||||
if (loopStartElement == null)
|
||||
loopStartElement = nextTabElement;
|
||||
loopStartElement ??= nextTabElement;
|
||||
|
||||
// At this point nextTabElement is TabGroup
|
||||
var lastTabElementInside = GetPrevTab(null, nextTabElement, true);
|
||||
@@ -189,22 +179,18 @@ namespace Avalonia.Input.Navigation
|
||||
|
||||
public static IInputElement? GetPrevTabOutside(ICustomKeyboardNavigation e)
|
||||
{
|
||||
if (e is IInputElement container)
|
||||
if (e is IInputElement container && GetFirstChild(container) is { } first)
|
||||
{
|
||||
var first = GetFirstChild(container);
|
||||
|
||||
if (first is object)
|
||||
return GetPrevTab(first, null, false);
|
||||
return GetPrevTab(first, null, false);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static IInputElement? FocusedElement(IInputElement e)
|
||||
private static IInputElement? FocusedElement(IInputElement? e)
|
||||
{
|
||||
var iie = e;
|
||||
// Focus delegation is enabled only if keyboard focus is outside the container
|
||||
if (iie != null && !iie.IsKeyboardFocusWithin)
|
||||
if (e != null && !e.IsKeyboardFocusWithin)
|
||||
{
|
||||
var focusedElement = (FocusManager.Instance as FocusManager)?.GetFocusedElement(e);
|
||||
if (focusedElement != null)
|
||||
@@ -229,13 +215,11 @@ namespace Avalonia.Input.Navigation
|
||||
private static IInputElement? GetFirstChild(IInputElement e)
|
||||
{
|
||||
// If the element has a FocusedElement it should be its first child
|
||||
if (FocusedElement(e) is IInputElement focusedElement)
|
||||
if (FocusedElement(e) is { } focusedElement)
|
||||
return focusedElement;
|
||||
|
||||
// Return the first visible element.
|
||||
var uiElement = e as InputElement;
|
||||
|
||||
if (uiElement is null || IsVisibleAndEnabled(uiElement))
|
||||
if (e is not InputElement uiElement || IsVisibleAndEnabled(uiElement))
|
||||
{
|
||||
if (e is Visual elementAsVisual)
|
||||
{
|
||||
@@ -265,7 +249,7 @@ namespace Avalonia.Input.Navigation
|
||||
private static IInputElement? GetLastChild(IInputElement e)
|
||||
{
|
||||
// If the element has a FocusedElement it should be its last child
|
||||
if (FocusedElement(e) is IInputElement focusedElement)
|
||||
if (FocusedElement(e) is { } focusedElement)
|
||||
return focusedElement;
|
||||
|
||||
// Return the last visible element.
|
||||
@@ -273,9 +257,7 @@ namespace Avalonia.Input.Navigation
|
||||
|
||||
if (uiElement == null || IsVisibleAndEnabled(uiElement))
|
||||
{
|
||||
var elementAsVisual = e as Visual;
|
||||
|
||||
if (elementAsVisual != null)
|
||||
if (e is Visual elementAsVisual)
|
||||
{
|
||||
var children = elementAsVisual.VisualChildren;
|
||||
var count = children.Count;
|
||||
@@ -322,7 +304,7 @@ namespace Avalonia.Input.Navigation
|
||||
return firstTabElement;
|
||||
}
|
||||
|
||||
private static IInputElement? GetLastInTree(IInputElement container)
|
||||
private static IInputElement GetLastInTree(IInputElement container)
|
||||
{
|
||||
IInputElement? result;
|
||||
IInputElement? c = container;
|
||||
|
||||
@@ -6,9 +6,9 @@ namespace Avalonia.Input.Platform
|
||||
[NotClientImplementable]
|
||||
public interface IClipboard
|
||||
{
|
||||
Task<string> GetTextAsync();
|
||||
Task<string?> GetTextAsync();
|
||||
|
||||
Task SetTextAsync(string text);
|
||||
Task SetTextAsync(string? text);
|
||||
|
||||
Task ClearAsync();
|
||||
|
||||
@@ -16,6 +16,6 @@ namespace Avalonia.Input.Platform
|
||||
|
||||
Task<string[]> GetFormatsAsync();
|
||||
|
||||
Task<object> GetDataAsync(string format);
|
||||
Task<object?> GetDataAsync(string format);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ namespace Avalonia.LogicalTree
|
||||
/// <param name="logical">The logical.</param>
|
||||
/// <param name="includeSelf">If given logical should be included in search.</param>
|
||||
/// <returns>First ancestor of given type.</returns>
|
||||
public static T? FindLogicalAncestorOfType<T>(this ILogical logical, bool includeSelf = false) where T : class
|
||||
public static T? FindLogicalAncestorOfType<T>(this ILogical? logical, bool includeSelf = false) where T : class
|
||||
{
|
||||
if (logical is null)
|
||||
{
|
||||
@@ -120,7 +120,7 @@ namespace Avalonia.LogicalTree
|
||||
/// <param name="logical">The logical.</param>
|
||||
/// <param name="includeSelf">If given logical should be included in search.</param>
|
||||
/// <returns>First descendant of given type.</returns>
|
||||
public static T? FindLogicalDescendantOfType<T>(this ILogical logical, bool includeSelf = false) where T : class
|
||||
public static T? FindLogicalDescendantOfType<T>(this ILogical? logical, bool includeSelf = false) where T : class
|
||||
{
|
||||
if (logical is null)
|
||||
{
|
||||
@@ -185,7 +185,7 @@ namespace Avalonia.LogicalTree
|
||||
/// True if <paramref name="logical"/> is an ancestor of <paramref name="target"/>;
|
||||
/// otherwise false.
|
||||
/// </returns>
|
||||
public static bool IsLogicalAncestorOf(this ILogical logical, ILogical target)
|
||||
public static bool IsLogicalAncestorOf(this ILogical? logical, ILogical? target)
|
||||
{
|
||||
var current = target?.LogicalParent;
|
||||
|
||||
|
||||
@@ -147,16 +147,11 @@ namespace Avalonia.Media
|
||||
/// <param name="s">The color string.</param>
|
||||
/// <param name="color">The parsed color</param>
|
||||
/// <returns>The status of the operation.</returns>
|
||||
public static bool TryParse(string s, out Color color)
|
||||
public static bool TryParse(string? s, out Color color)
|
||||
{
|
||||
color = default;
|
||||
|
||||
if (s is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (s.Length == 0)
|
||||
if (string.IsNullOrEmpty(s))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -336,7 +331,7 @@ namespace Avalonia.Media
|
||||
/// <summary>
|
||||
/// Parses the given string representing a CSS color value into a new <see cref="Color"/>.
|
||||
/// </summary>
|
||||
private static bool TryParseCssFormat(string s, out Color color)
|
||||
private static bool TryParseCssFormat(string? s, out Color color)
|
||||
{
|
||||
bool prefixMatched = false;
|
||||
|
||||
|
||||
@@ -240,7 +240,7 @@ namespace Avalonia.Media
|
||||
/// </summary>
|
||||
/// <param name="foreground">The foreground brush.</param>
|
||||
/// <param name="glyphRun">The glyph run.</param>
|
||||
public void DrawGlyphRun(IBrush foreground, GlyphRun glyphRun)
|
||||
public void DrawGlyphRun(IBrush? foreground, GlyphRun glyphRun)
|
||||
{
|
||||
_ = glyphRun ?? throw new ArgumentNullException(nameof(glyphRun));
|
||||
|
||||
|
||||
@@ -13,14 +13,14 @@ namespace Avalonia.Media
|
||||
public static readonly StyledProperty<double> OpacityProperty =
|
||||
AvaloniaProperty.Register<DrawingGroup, double>(nameof(Opacity), 1);
|
||||
|
||||
public static readonly StyledProperty<Transform> TransformProperty =
|
||||
AvaloniaProperty.Register<DrawingGroup, Transform>(nameof(Transform));
|
||||
public static readonly StyledProperty<Transform?> TransformProperty =
|
||||
AvaloniaProperty.Register<DrawingGroup, Transform?>(nameof(Transform));
|
||||
|
||||
public static readonly StyledProperty<Geometry> ClipGeometryProperty =
|
||||
AvaloniaProperty.Register<DrawingGroup, Geometry>(nameof(ClipGeometry));
|
||||
public static readonly StyledProperty<Geometry?> ClipGeometryProperty =
|
||||
AvaloniaProperty.Register<DrawingGroup, Geometry?>(nameof(ClipGeometry));
|
||||
|
||||
public static readonly StyledProperty<IBrush> OpacityMaskProperty =
|
||||
AvaloniaProperty.Register<DrawingGroup, IBrush>(nameof(OpacityMask));
|
||||
public static readonly StyledProperty<IBrush?> OpacityMaskProperty =
|
||||
AvaloniaProperty.Register<DrawingGroup, IBrush?>(nameof(OpacityMask));
|
||||
|
||||
public static readonly DirectProperty<DrawingGroup, DrawingCollection> ChildrenProperty =
|
||||
AvaloniaProperty.RegisterDirect<DrawingGroup, DrawingCollection>(
|
||||
@@ -36,19 +36,19 @@ namespace Avalonia.Media
|
||||
set => SetValue(OpacityProperty, value);
|
||||
}
|
||||
|
||||
public Transform Transform
|
||||
public Transform? Transform
|
||||
{
|
||||
get => GetValue(TransformProperty);
|
||||
set => SetValue(TransformProperty, value);
|
||||
}
|
||||
|
||||
public Geometry ClipGeometry
|
||||
public Geometry? ClipGeometry
|
||||
{
|
||||
get => GetValue(ClipGeometryProperty);
|
||||
set => SetValue(ClipGeometryProperty, value);
|
||||
}
|
||||
|
||||
public IBrush OpacityMask
|
||||
public IBrush? OpacityMask
|
||||
{
|
||||
get => GetValue(OpacityMaskProperty);
|
||||
set => SetValue(OpacityMaskProperty, value);
|
||||
@@ -159,7 +159,7 @@ namespace Avalonia.Media
|
||||
|
||||
public void DrawGeometry(IBrush? brush, IPen? pen, IGeometryImpl geometry)
|
||||
{
|
||||
if (((brush == null) && (pen == null)) || (geometry == null))
|
||||
if ((brush == null) && (pen == null))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -167,9 +167,9 @@ namespace Avalonia.Media
|
||||
AddNewGeometryDrawing(brush, pen, new PlatformGeometry(geometry));
|
||||
}
|
||||
|
||||
public void DrawGlyphRun(IBrush foreground, IRef<IGlyphRunImpl> glyphRun)
|
||||
public void DrawGlyphRun(IBrush? foreground, IRef<IGlyphRunImpl> glyphRun)
|
||||
{
|
||||
if (foreground == null || glyphRun == null)
|
||||
if (foreground == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -184,7 +184,7 @@ namespace Avalonia.Media
|
||||
AddDrawing(glyphRunDrawing);
|
||||
}
|
||||
|
||||
public void DrawLine(IPen pen, Point p1, Point p2)
|
||||
public void DrawLine(IPen? pen, Point p1, Point p2)
|
||||
{
|
||||
if (pen == null)
|
||||
{
|
||||
|
||||
@@ -20,8 +20,8 @@ namespace Avalonia.Media
|
||||
/// <summary>
|
||||
/// Defines the <see cref="Drawing"/> property.
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<Drawing> DrawingProperty =
|
||||
AvaloniaProperty.Register<DrawingImage, Drawing>(nameof(Drawing));
|
||||
public static readonly StyledProperty<Drawing?> DrawingProperty =
|
||||
AvaloniaProperty.Register<DrawingImage, Drawing?>(nameof(Drawing));
|
||||
|
||||
/// <inheritdoc/>
|
||||
public event EventHandler? Invalidated;
|
||||
@@ -30,7 +30,7 @@ namespace Avalonia.Media
|
||||
/// Gets or sets the drawing content.
|
||||
/// </summary>
|
||||
[Content]
|
||||
public Drawing Drawing
|
||||
public Drawing? Drawing
|
||||
{
|
||||
get => GetValue(DrawingProperty);
|
||||
set => SetValue(DrawingProperty, value);
|
||||
|
||||
@@ -119,7 +119,7 @@ namespace Avalonia.Media
|
||||
|
||||
case 2:
|
||||
{
|
||||
var source = segments[0].StartsWith("/")
|
||||
var source = segments[0].StartsWith("/", StringComparison.Ordinal)
|
||||
? new Uri(segments[0], UriKind.Relative)
|
||||
: new Uri(segments[0], UriKind.RelativeOrAbsolute);
|
||||
|
||||
@@ -188,7 +188,7 @@ namespace Avalonia.Media
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
return ((FamilyNames != null ? FamilyNames.GetHashCode() : 0) * 397) ^ (Key != null ? Key.GetHashCode() : 0);
|
||||
return (FamilyNames.GetHashCode() * 397) ^ (Key is not null ? Key.GetHashCode() : 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,10 +41,7 @@ namespace Avalonia.Media.Fonts
|
||||
{
|
||||
var hash = (int)2166136261;
|
||||
|
||||
if (Source != null)
|
||||
{
|
||||
hash = (hash * 16777619) ^ Source.GetHashCode();
|
||||
}
|
||||
hash = (hash * 16777619) ^ Source.GetHashCode();
|
||||
|
||||
if (BaseUri != null)
|
||||
{
|
||||
|
||||
@@ -1354,7 +1354,7 @@ namespace Avalonia.Media
|
||||
{
|
||||
var highlightBounds = currentLine.GetTextBounds(x0,x1 - x0);
|
||||
|
||||
if (highlightBounds != null)
|
||||
if (highlightBounds.Count > 0)
|
||||
{
|
||||
foreach (var bound in highlightBounds)
|
||||
{
|
||||
@@ -1365,7 +1365,7 @@ namespace Avalonia.Media
|
||||
// Convert logical units (which extend leftward from the right edge
|
||||
// of the paragraph) to physical units.
|
||||
//
|
||||
// Note that since rect is in logical units, rect.Right corresponds to
|
||||
// Note that since rect is in logical units, rect.Right corresponds to
|
||||
// the visual *left* edge of the rectangle in the RTL case. Specifically,
|
||||
// is the distance leftward from the right edge of the formatting rectangle
|
||||
// whose width is the paragraph width passed to FormatLine.
|
||||
@@ -1384,7 +1384,7 @@ namespace Avalonia.Media
|
||||
else
|
||||
{
|
||||
accumulatedBounds = Geometry.Combine(accumulatedBounds, rectangleGeometry, GeometryCombineMode.Union);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,8 @@ namespace Avalonia.Media
|
||||
/// <summary>
|
||||
/// Defines the <see cref="Geometry"/> property.
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<Geometry> GeometryProperty =
|
||||
AvaloniaProperty.Register<GeometryDrawing, Geometry>(nameof(Geometry));
|
||||
public static readonly StyledProperty<Geometry?> GeometryProperty =
|
||||
AvaloniaProperty.Register<GeometryDrawing, Geometry?>(nameof(Geometry));
|
||||
|
||||
/// <summary>
|
||||
/// Defines the <see cref="Brush"/> property.
|
||||
@@ -34,7 +34,7 @@ namespace Avalonia.Media
|
||||
/// Gets or sets the <see cref="Avalonia.Media.Geometry"/> that describes the shape of this <see cref="GeometryDrawing"/>.
|
||||
/// </summary>
|
||||
[Content]
|
||||
public Geometry Geometry
|
||||
public Geometry? Geometry
|
||||
{
|
||||
get => GetValue(GeometryProperty);
|
||||
set => SetValue(GeometryProperty, value);
|
||||
|
||||
@@ -2,19 +2,19 @@
|
||||
{
|
||||
public class GlyphRunDrawing : Drawing
|
||||
{
|
||||
public static readonly StyledProperty<IBrush> ForegroundProperty =
|
||||
AvaloniaProperty.Register<GlyphRunDrawing, IBrush>(nameof(Foreground));
|
||||
public static readonly StyledProperty<IBrush?> ForegroundProperty =
|
||||
AvaloniaProperty.Register<GlyphRunDrawing, IBrush?>(nameof(Foreground));
|
||||
|
||||
public static readonly StyledProperty<GlyphRun> GlyphRunProperty =
|
||||
AvaloniaProperty.Register<GlyphRunDrawing, GlyphRun>(nameof(GlyphRun));
|
||||
public static readonly StyledProperty<GlyphRun?> GlyphRunProperty =
|
||||
AvaloniaProperty.Register<GlyphRunDrawing, GlyphRun?>(nameof(GlyphRun));
|
||||
|
||||
public IBrush Foreground
|
||||
public IBrush? Foreground
|
||||
{
|
||||
get => GetValue(ForegroundProperty);
|
||||
set => SetValue(ForegroundProperty, value);
|
||||
}
|
||||
|
||||
public GlyphRun GlyphRun
|
||||
public GlyphRun? GlyphRun
|
||||
{
|
||||
get => GetValue(GlyphRunProperty);
|
||||
set => SetValue(GlyphRunProperty, value);
|
||||
|
||||
@@ -254,7 +254,7 @@ namespace Avalonia.Media
|
||||
/// <param name="s">The HSL color string to parse.</param>
|
||||
/// <param name="hslColor">The parsed <see cref="HslColor"/>.</param>
|
||||
/// <returns>True if parsing was successful; otherwise, false.</returns>
|
||||
public static bool TryParse(string s, out HslColor hslColor)
|
||||
public static bool TryParse(string? s, out HslColor hslColor)
|
||||
{
|
||||
bool prefixMatched = false;
|
||||
|
||||
|
||||
@@ -254,7 +254,7 @@ namespace Avalonia.Media
|
||||
/// <param name="s">The HSV color string to parse.</param>
|
||||
/// <param name="hsvColor">The parsed <see cref="HsvColor"/>.</param>
|
||||
/// <returns>True if parsing was successful; otherwise, false.</returns>
|
||||
public static bool TryParse(string s, out HsvColor hsvColor)
|
||||
public static bool TryParse(string? s, out HsvColor hsvColor)
|
||||
{
|
||||
bool prefixMatched = false;
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Avalonia.Metadata;
|
||||
using Avalonia.VisualTree;
|
||||
|
||||
namespace Avalonia.Media
|
||||
{
|
||||
@@ -12,6 +11,6 @@ namespace Avalonia.Media
|
||||
/// <summary>
|
||||
/// Gets the visual to draw.
|
||||
/// </summary>
|
||||
Visual Visual { get; }
|
||||
Visual? Visual { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,17 +39,8 @@ namespace Avalonia.Media.Immutable
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (other is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Offset != other.Offset)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return SequenceEqual(Dashes, other.Dashes);
|
||||
return other is not null && Offset == other.Offset && SequenceEqual(_dashes, other.Dashes);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -58,30 +49,27 @@ namespace Avalonia.Media.Immutable
|
||||
var hashCode = 717868523;
|
||||
hashCode = hashCode * -1521134295 + Offset.GetHashCode();
|
||||
|
||||
if (_dashes != null)
|
||||
foreach (var i in _dashes)
|
||||
{
|
||||
foreach (var i in _dashes)
|
||||
{
|
||||
hashCode = hashCode * -1521134295 + i.GetHashCode();
|
||||
}
|
||||
hashCode = hashCode * -1521134295 + i.GetHashCode();
|
||||
}
|
||||
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
private static bool SequenceEqual(IReadOnlyList<double> left, IReadOnlyList<double>? right)
|
||||
private static bool SequenceEqual(double[] left, IReadOnlyList<double>? right)
|
||||
{
|
||||
if (ReferenceEquals(left, right))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (left == null || right == null || left.Count != right.Count)
|
||||
if (right is null || left.Length != right.Count)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var c = 0; c < left.Count; c++)
|
||||
for (var c = 0; c < left.Length; c++)
|
||||
{
|
||||
if (left[c] != right[c])
|
||||
{
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Avalonia.Media.Imaging;
|
||||
using Avalonia.VisualTree;
|
||||
|
||||
namespace Avalonia.Media.Immutable
|
||||
{
|
||||
@@ -31,11 +30,11 @@ namespace Avalonia.Media.Immutable
|
||||
RelativeRect? destinationRect = null,
|
||||
double opacity = 1,
|
||||
ImmutableTransform? transform = null,
|
||||
RelativePoint transformOrigin = new RelativePoint(),
|
||||
RelativePoint transformOrigin = default,
|
||||
RelativeRect? sourceRect = null,
|
||||
Stretch stretch = Stretch.Uniform,
|
||||
TileMode tileMode = TileMode.None,
|
||||
Imaging.BitmapInterpolationMode bitmapInterpolationMode = Imaging.BitmapInterpolationMode.Default)
|
||||
BitmapInterpolationMode bitmapInterpolationMode = BitmapInterpolationMode.Default)
|
||||
: base(
|
||||
alignmentX,
|
||||
alignmentY,
|
||||
@@ -62,6 +61,6 @@ namespace Avalonia.Media.Immutable
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Visual Visual { get; }
|
||||
public Visual? Visual { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,8 +22,8 @@ namespace Avalonia.Media
|
||||
/// <summary>
|
||||
/// Defines the <see cref="Stroke"/> property.
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<IBrush> StrokeProperty =
|
||||
AvaloniaProperty.Register<TextDecoration, IBrush>(nameof(Stroke));
|
||||
public static readonly StyledProperty<IBrush?> StrokeProperty =
|
||||
AvaloniaProperty.Register<TextDecoration, IBrush?>(nameof(Stroke));
|
||||
|
||||
/// <summary>
|
||||
/// Defines the <see cref="StrokeThicknessUnit"/> property.
|
||||
@@ -34,8 +34,8 @@ namespace Avalonia.Media
|
||||
/// <summary>
|
||||
/// Defines the <see cref="StrokeDashArray"/> property.
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<AvaloniaList<double>> StrokeDashArrayProperty =
|
||||
AvaloniaProperty.Register<TextDecoration, AvaloniaList<double>>(nameof(StrokeDashArray));
|
||||
public static readonly StyledProperty<AvaloniaList<double>?> StrokeDashArrayProperty =
|
||||
AvaloniaProperty.Register<TextDecoration, AvaloniaList<double>?>(nameof(StrokeDashArray));
|
||||
|
||||
/// <summary>
|
||||
/// Defines the <see cref="StrokeDashOffset"/> property.
|
||||
@@ -82,7 +82,7 @@ namespace Avalonia.Media
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="IBrush"/> that specifies how the <see cref="TextDecoration"/> is painted.
|
||||
/// </summary>
|
||||
public IBrush Stroke
|
||||
public IBrush? Stroke
|
||||
{
|
||||
get { return GetValue(StrokeProperty); }
|
||||
set { SetValue(StrokeProperty, value); }
|
||||
@@ -101,7 +101,7 @@ namespace Avalonia.Media
|
||||
/// Gets or sets a collection of <see cref="double"/> values that indicate the pattern of dashes and gaps
|
||||
/// that is used to draw the <see cref="TextDecoration"/>.
|
||||
/// </summary>
|
||||
public AvaloniaList<double> StrokeDashArray
|
||||
public AvaloniaList<double>? StrokeDashArray
|
||||
{
|
||||
get { return GetValue(StrokeDashArrayProperty); }
|
||||
set { SetValue(StrokeDashArrayProperty, value); }
|
||||
@@ -220,7 +220,7 @@ namespace Avalonia.Media
|
||||
|
||||
var intersections = glyphRun.PlatformImpl.Item.GetIntersections((float)(thickness * 0.5d - offsetY), (float)(thickness * 1.5d - offsetY));
|
||||
|
||||
if (intersections != null && intersections.Count > 0)
|
||||
if (intersections.Count > 0)
|
||||
{
|
||||
var last = baselineOrigin.X;
|
||||
var finalPos = last + glyphRun.Size.Width;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Avalonia.Media.Immutable;
|
||||
using Avalonia.VisualTree;
|
||||
|
||||
namespace Avalonia.Media
|
||||
{
|
||||
@@ -11,8 +10,8 @@ namespace Avalonia.Media
|
||||
/// <summary>
|
||||
/// Defines the <see cref="Visual"/> property.
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<Visual> VisualProperty =
|
||||
AvaloniaProperty.Register<VisualBrush, Visual>(nameof(Visual));
|
||||
public static readonly StyledProperty<Visual?> VisualProperty =
|
||||
AvaloniaProperty.Register<VisualBrush, Visual?>(nameof(Visual));
|
||||
|
||||
static VisualBrush()
|
||||
{
|
||||
@@ -38,7 +37,7 @@ namespace Avalonia.Media
|
||||
/// <summary>
|
||||
/// Gets or sets the visual to draw.
|
||||
/// </summary>
|
||||
public Visual Visual
|
||||
public Visual? Visual
|
||||
{
|
||||
get { return GetValue(VisualProperty); }
|
||||
set { SetValue(VisualProperty, value); }
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace Avalonia.Platform
|
||||
/// <param name="pen">The stroke pen.</param>
|
||||
/// <param name="p1">The first point of the line.</param>
|
||||
/// <param name="p2">The second point of the line.</param>
|
||||
void DrawLine(IPen pen, Point p1, Point p2);
|
||||
void DrawLine(IPen? pen, Point p1, Point p2);
|
||||
|
||||
/// <summary>
|
||||
/// Draws a geometry.
|
||||
@@ -91,7 +91,7 @@ namespace Avalonia.Platform
|
||||
/// </summary>
|
||||
/// <param name="foreground">The foreground.</param>
|
||||
/// <param name="glyphRun">The glyph run.</param>
|
||||
void DrawGlyphRun(IBrush foreground, IRef<IGlyphRunImpl> glyphRun);
|
||||
void DrawGlyphRun(IBrush? foreground, IRef<IGlyphRunImpl> glyphRun);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="IRenderTargetBitmapImpl"/> that can be used as a render layer
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
@@ -18,26 +19,21 @@ internal class AssemblyDescriptor : IAssemblyDescriptor
|
||||
{
|
||||
public AssemblyDescriptor(Assembly assembly)
|
||||
{
|
||||
Assembly = assembly;
|
||||
Assembly = assembly ?? throw new ArgumentNullException(nameof(assembly));
|
||||
Resources = assembly.GetManifestResourceNames()
|
||||
.ToDictionary(n => n, n => (IAssetDescriptor)new AssemblyResourceDescriptor(assembly, n));
|
||||
Name = assembly.GetName().Name;
|
||||
|
||||
if (assembly != null)
|
||||
using var resources = assembly.GetManifestResourceStream(Constants.AvaloniaResourceName);
|
||||
if (resources != null)
|
||||
{
|
||||
Resources = assembly.GetManifestResourceNames()
|
||||
.ToDictionary(n => n, n => (IAssetDescriptor)new AssemblyResourceDescriptor(assembly, n));
|
||||
Name = assembly.GetName().Name;
|
||||
using (var resources = assembly.GetManifestResourceStream(Constants.AvaloniaResourceName))
|
||||
{
|
||||
if (resources != null)
|
||||
{
|
||||
Resources.Remove(Constants.AvaloniaResourceName);
|
||||
Resources.Remove(Constants.AvaloniaResourceName);
|
||||
|
||||
var indexLength = new BinaryReader(resources).ReadInt32();
|
||||
var index = AvaloniaResourcesIndexReaderWriter.ReadIndex(new SlicedStream(resources, 4, indexLength));
|
||||
var baseOffset = indexLength + 4;
|
||||
AvaloniaResources = index.ToDictionary(r => GetPathRooted(r), r => (IAssetDescriptor)
|
||||
new AvaloniaResourceDescriptor(assembly, baseOffset + r.Offset, r.Size));
|
||||
}
|
||||
}
|
||||
var indexLength = new BinaryReader(resources).ReadInt32();
|
||||
var index = AvaloniaResourcesIndexReaderWriter.ReadIndex(new SlicedStream(resources, 4, indexLength));
|
||||
var baseOffset = indexLength + 4;
|
||||
AvaloniaResources = index.ToDictionary(GetPathRooted, r => (IAssetDescriptor)
|
||||
new AvaloniaResourceDescriptor(assembly, baseOffset + r.Offset, r.Size));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +41,7 @@ internal class AssemblyDescriptor : IAssemblyDescriptor
|
||||
public Dictionary<string, IAssetDescriptor>? Resources { get; }
|
||||
public Dictionary<string, IAssetDescriptor>? AvaloniaResources { get; }
|
||||
public string? Name { get; }
|
||||
|
||||
private static string GetPathRooted(AvaloniaResourcesIndexEntry r) =>
|
||||
r.Path![0] == '/' ? r.Path : '/' + r.Path;
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace Avalonia.Rendering.Composition.Animations
|
||||
public abstract class CompositionAnimation : CompositionObject, ICompositionAnimationBase
|
||||
{
|
||||
private readonly CompositionPropertySet _propertySet;
|
||||
internal CompositionAnimation(Compositor compositor) : base(compositor, null!)
|
||||
internal CompositionAnimation(Compositor compositor) : base(compositor, null)
|
||||
{
|
||||
_propertySet = new CompositionPropertySet(compositor);
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace Avalonia.Rendering.Composition.Animations
|
||||
public void Remove(CompositionAnimation value) => Animations.Remove(value);
|
||||
public void RemoveAll() => Animations.Clear();
|
||||
|
||||
public CompositionAnimationGroup(Compositor compositor) : base(compositor, null!)
|
||||
public CompositionAnimationGroup(Compositor compositor) : base(compositor, null)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace Avalonia.Rendering.Composition.Animations
|
||||
{
|
||||
private Dictionary<string, ICompositionAnimationBase> _inner = new Dictionary<string, ICompositionAnimationBase>();
|
||||
private IDictionary<string, ICompositionAnimationBase> _innerface;
|
||||
internal ImplicitAnimationCollection(Compositor compositor) : base(compositor, null!)
|
||||
internal ImplicitAnimationCollection(Compositor compositor) : base(compositor, null)
|
||||
{
|
||||
_innerface = _inner;
|
||||
}
|
||||
|
||||
@@ -20,15 +20,17 @@ public class CompositingRenderer : IRendererWithCompositor
|
||||
{
|
||||
private readonly IRenderRoot _root;
|
||||
private readonly Compositor _compositor;
|
||||
CompositionDrawingContext _recorder = new();
|
||||
DrawingContext _recordingContext;
|
||||
private HashSet<Visual> _dirty = new();
|
||||
private HashSet<Visual> _recalculateChildren = new();
|
||||
private bool _queuedUpdate;
|
||||
private Action _update;
|
||||
private bool _updating;
|
||||
private readonly CompositionDrawingContext _recorder = new();
|
||||
private readonly DrawingContext _recordingContext;
|
||||
private readonly HashSet<Visual> _dirty = new();
|
||||
private readonly HashSet<Visual> _recalculateChildren = new();
|
||||
private readonly Action _update;
|
||||
|
||||
internal CompositionTarget CompositionTarget;
|
||||
private bool _queuedUpdate;
|
||||
private bool _updating;
|
||||
private bool _isDisposed;
|
||||
|
||||
internal CompositionTarget CompositionTarget { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Asks the renderer to only draw frames on the render thread. Makes Paint to wait until frame is rendered.
|
||||
@@ -38,6 +40,17 @@ public class CompositingRenderer : IRendererWithCompositor
|
||||
/// <inheritdoc/>
|
||||
public RendererDiagnostics Diagnostics { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Compositor Compositor => _compositor;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="CompositingRenderer"/>
|
||||
/// </summary>
|
||||
/// <param name="root">The render root using this renderer.</param>
|
||||
/// <param name="compositor">The associated compositors.</param>
|
||||
/// <param name="surfaces">
|
||||
/// A function returning the list of native platform's surfaces that can be consumed by rendering subsystems.
|
||||
/// </param>
|
||||
public CompositingRenderer(IRenderRoot root, Compositor compositor, Func<IEnumerable<object>> surfaces)
|
||||
{
|
||||
_root = root;
|
||||
@@ -66,7 +79,7 @@ public class CompositingRenderer : IRendererWithCompositor
|
||||
/// <inheritdoc/>
|
||||
public event EventHandler<SceneInvalidatedEventArgs>? SceneInvalidated;
|
||||
|
||||
void QueueUpdate()
|
||||
private void QueueUpdate()
|
||||
{
|
||||
if(_queuedUpdate)
|
||||
return;
|
||||
@@ -77,9 +90,11 @@ public class CompositingRenderer : IRendererWithCompositor
|
||||
/// <inheritdoc/>
|
||||
public void AddDirty(Visual visual)
|
||||
{
|
||||
if (_isDisposed)
|
||||
return;
|
||||
if (_updating)
|
||||
throw new InvalidOperationException("Visual was invalidated during the render pass");
|
||||
_dirty.Add((Visual)visual);
|
||||
_dirty.Add(visual);
|
||||
QueueUpdate();
|
||||
}
|
||||
|
||||
@@ -126,9 +141,11 @@ public class CompositingRenderer : IRendererWithCompositor
|
||||
/// <inheritdoc/>
|
||||
public void RecalculateChildren(Visual visual)
|
||||
{
|
||||
if (_isDisposed)
|
||||
return;
|
||||
if (_updating)
|
||||
throw new InvalidOperationException("Visual was invalidated during the render pass");
|
||||
_recalculateChildren.Add((Visual)visual);
|
||||
_recalculateChildren.Add(visual);
|
||||
QueueUpdate();
|
||||
}
|
||||
|
||||
@@ -171,7 +188,7 @@ public class CompositingRenderer : IRendererWithCompositor
|
||||
if (sortedChildren != null)
|
||||
for (var c = 0; c < visualChildren.Count; c++)
|
||||
{
|
||||
if (!ReferenceEquals(compositionChildren[c], ((Visual)sortedChildren[c].visual).CompositionVisual))
|
||||
if (!ReferenceEquals(compositionChildren[c], sortedChildren[c].visual.CompositionVisual))
|
||||
{
|
||||
mismatch = true;
|
||||
break;
|
||||
@@ -179,7 +196,7 @@ public class CompositingRenderer : IRendererWithCompositor
|
||||
}
|
||||
else
|
||||
for (var c = 0; c < visualChildren.Count; c++)
|
||||
if (!ReferenceEquals(compositionChildren[c], ((Visual)visualChildren[c]).CompositionVisual))
|
||||
if (!ReferenceEquals(compositionChildren[c], visualChildren[c].CompositionVisual))
|
||||
{
|
||||
mismatch = true;
|
||||
break;
|
||||
@@ -201,7 +218,7 @@ public class CompositingRenderer : IRendererWithCompositor
|
||||
{
|
||||
foreach (var ch in sortedChildren)
|
||||
{
|
||||
var compositionChild = ((Visual)ch.visual).CompositionVisual;
|
||||
var compositionChild = ch.visual.CompositionVisual;
|
||||
if (compositionChild != null)
|
||||
compositionChildren.Add(compositionChild);
|
||||
}
|
||||
@@ -210,7 +227,7 @@ public class CompositingRenderer : IRendererWithCompositor
|
||||
else
|
||||
foreach (var ch in v.GetVisualChildren())
|
||||
{
|
||||
var compositionChild = ((Visual)ch).CompositionVisual;
|
||||
var compositionChild = ch.CompositionVisual;
|
||||
if (compositionChild != null)
|
||||
compositionChildren.Add(compositionChild);
|
||||
}
|
||||
@@ -289,13 +306,18 @@ public class CompositingRenderer : IRendererWithCompositor
|
||||
_updating = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Resized(Size size)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Paint(Rect rect)
|
||||
{
|
||||
if (_isDisposed)
|
||||
return;
|
||||
|
||||
QueueUpdate();
|
||||
CompositionTarget.RequestRedraw();
|
||||
if(RenderOnlyOnRenderThread && Compositor.Loop.RunsInBackground)
|
||||
@@ -304,17 +326,34 @@ public class CompositingRenderer : IRendererWithCompositor
|
||||
CompositionTarget.ImmediateUIThreadRender();
|
||||
}
|
||||
|
||||
public void Start() => CompositionTarget.IsEnabled = true;
|
||||
|
||||
public void Stop()
|
||||
/// <inheritdoc />
|
||||
public void Start()
|
||||
{
|
||||
CompositionTarget.IsEnabled = false;
|
||||
if (_isDisposed)
|
||||
return;
|
||||
|
||||
CompositionTarget.IsEnabled = true;
|
||||
}
|
||||
|
||||
public ValueTask<object?> TryGetRenderInterfaceFeature(Type featureType) => Compositor.TryGetRenderInterfaceFeature(featureType);
|
||||
/// <inheritdoc />
|
||||
public void Stop()
|
||||
=> CompositionTarget.IsEnabled = false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public ValueTask<object?> TryGetRenderInterfaceFeature(Type featureType)
|
||||
=> Compositor.TryGetRenderInterfaceFeature(featureType);
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDisposed)
|
||||
return;
|
||||
|
||||
_isDisposed = true;
|
||||
_dirty.Clear();
|
||||
_recalculateChildren.Clear();
|
||||
SceneInvalidated = null;
|
||||
|
||||
Stop();
|
||||
CompositionTarget.Dispose();
|
||||
|
||||
@@ -323,9 +362,4 @@ public class CompositingRenderer : IRendererWithCompositor
|
||||
if (Compositor.Loop.RunsInBackground)
|
||||
_compositor.Commit().Wait();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The associated <see cref="Avalonia.Rendering.Composition.Compositor"/> object
|
||||
/// </summary>
|
||||
public Compositor Compositor => _compositor;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Avalonia.Rendering.Composition.Server;
|
||||
using Avalonia.Threading;
|
||||
@@ -7,7 +6,7 @@ namespace Avalonia.Rendering.Composition;
|
||||
|
||||
public class CompositionDrawingSurface : CompositionSurface
|
||||
{
|
||||
internal new ServerCompositionDrawingSurface Server => (ServerCompositionDrawingSurface)base.Server;
|
||||
internal new ServerCompositionDrawingSurface Server => (ServerCompositionDrawingSurface)base.Server!;
|
||||
internal CompositionDrawingSurface(Compositor compositor) : base(compositor, new ServerCompositionDrawingSurface(compositor.Server))
|
||||
{
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace Avalonia.Rendering.Composition
|
||||
public ImplicitAnimationCollection? ImplicitAnimations { get; set; }
|
||||
|
||||
private protected InlineDictionary<CompositionProperty, IAnimationInstance> PendingAnimations;
|
||||
internal CompositionObject(Compositor compositor, ServerObject server)
|
||||
internal CompositionObject(Compositor compositor, ServerObject? server)
|
||||
{
|
||||
Compositor = compositor;
|
||||
Server = server;
|
||||
@@ -32,7 +32,7 @@ namespace Avalonia.Rendering.Composition
|
||||
/// The associated Compositor
|
||||
/// </summary>
|
||||
public Compositor Compositor { get; }
|
||||
internal ServerObject Server { get; }
|
||||
internal ServerObject? Server { get; }
|
||||
public bool IsDisposed { get; private set; }
|
||||
private bool _registeredForSerialization;
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace Avalonia.Rendering.Composition
|
||||
private readonly Dictionary<string, ExpressionVariant> _variants = new Dictionary<string, ExpressionVariant>();
|
||||
private readonly Dictionary<string, CompositionObject> _objects = new Dictionary<string, CompositionObject>();
|
||||
|
||||
internal CompositionPropertySet(Compositor compositor) : base(compositor, null!)
|
||||
internal CompositionPropertySet(Compositor compositor) : base(compositor, null)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -88,8 +88,13 @@ internal class CompositionDrawingContext : IDrawingContextImpl, IDrawingContextW
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void DrawLine(IPen pen, Point p1, Point p2)
|
||||
public void DrawLine(IPen? pen, Point p1, Point p2)
|
||||
{
|
||||
if (pen is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var next = NextDrawAs<LineNode>();
|
||||
|
||||
if (next == null || !next.Item.Equals(Transform, pen, p1, p2))
|
||||
@@ -159,8 +164,13 @@ internal class CompositionDrawingContext : IDrawingContextImpl, IDrawingContextW
|
||||
public object? GetFeature(Type t) => null;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void DrawGlyphRun(IBrush foreground, IRef<IGlyphRunImpl> glyphRun)
|
||||
public void DrawGlyphRun(IBrush? foreground, IRef<IGlyphRunImpl> glyphRun)
|
||||
{
|
||||
if (foreground is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var next = NextDrawAs<GlyphRunNode>();
|
||||
|
||||
if (next == null || !next.Item.Equals(Transform, foreground, glyphRun))
|
||||
|
||||
@@ -165,8 +165,6 @@ namespace Avalonia.Rendering.Composition.Expressions
|
||||
|
||||
public override ExpressionVariant Evaluate(ref ExpressionEvaluationContext context)
|
||||
{
|
||||
if (context.ForeignFunctionInterface == null)
|
||||
return default;
|
||||
var args = new List<ExpressionVariant>();
|
||||
foreach (var expr in Parameters)
|
||||
args.Add(expr.Evaluate(ref context));
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using Avalonia.Rendering.Composition.Server;
|
||||
|
||||
// Special license applies <see href="https://raw.githubusercontent.com/AvaloniaUI/Avalonia/master/src/Avalonia.Base/Rendering/Composition/License.md">License.md</see>
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ internal class CompositorDrawingContextProxy : IDrawingContextImpl, IDrawingCont
|
||||
_impl.DrawBitmap(source, opacityMask, opacityMaskRect, destRect);
|
||||
}
|
||||
|
||||
public void DrawLine(IPen pen, Point p1, Point p2)
|
||||
public void DrawLine(IPen? pen, Point p1, Point p2)
|
||||
{
|
||||
_impl.DrawLine(pen, p1, p2);
|
||||
}
|
||||
@@ -86,7 +86,7 @@ internal class CompositorDrawingContextProxy : IDrawingContextImpl, IDrawingCont
|
||||
_impl.DrawEllipse(brush, pen, rect);
|
||||
}
|
||||
|
||||
public void DrawGlyphRun(IBrush foreground, IRef<IGlyphRunImpl> glyphRun)
|
||||
public void DrawGlyphRun(IBrush? foreground, IRef<IGlyphRunImpl> glyphRun)
|
||||
{
|
||||
_impl.DrawGlyphRun(foreground, glyphRun);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using Avalonia.Metadata;
|
||||
using Avalonia.Platform;
|
||||
using Avalonia.VisualTree;
|
||||
|
||||
namespace Avalonia.Rendering
|
||||
{
|
||||
|
||||
@@ -90,6 +90,9 @@ namespace Avalonia.Rendering
|
||||
|
||||
public interface IRendererWithCompositor : IRenderer
|
||||
{
|
||||
/// <summary>
|
||||
/// The associated <see cref="Avalonia.Rendering.Composition.Compositor"/> object
|
||||
/// </summary>
|
||||
Compositor Compositor { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,8 +48,10 @@ namespace Avalonia.Rendering
|
||||
/// <inheritdoc/>
|
||||
void IVisualBrushRenderer.RenderVisualBrush(IDrawingContextImpl context, IVisualBrush brush)
|
||||
{
|
||||
var visual = brush.Visual;
|
||||
Render(new DrawingContext(context), visual, visual.Bounds);
|
||||
if (brush.Visual is { } visual)
|
||||
{
|
||||
Render(new DrawingContext(context), visual, visual.Bounds);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void Render(Visual visual, DrawingContext context, bool updateTransformedBounds)
|
||||
|
||||
@@ -80,11 +80,8 @@ namespace Avalonia.Rendering.SceneGraph
|
||||
{
|
||||
p *= Transform.Invert();
|
||||
|
||||
if (Material != null)
|
||||
{
|
||||
var rect = Rect.Rect;
|
||||
return rect.ContainsExclusive(p);
|
||||
}
|
||||
var rect = Rect.Rect;
|
||||
return rect.ContainsExclusive(p);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -212,7 +212,7 @@ namespace Avalonia.Utilities
|
||||
|
||||
var toTypeConverter = TypeDescriptor.GetConverter(toUnderl);
|
||||
|
||||
if (toTypeConverter.CanConvertFrom(from) == true)
|
||||
if (toTypeConverter.CanConvertFrom(from))
|
||||
{
|
||||
result = toTypeConverter.ConvertFrom(null, culture, value);
|
||||
return true;
|
||||
@@ -220,7 +220,7 @@ namespace Avalonia.Utilities
|
||||
|
||||
var fromTypeConverter = TypeDescriptor.GetConverter(from);
|
||||
|
||||
if (fromTypeConverter.CanConvertTo(toUnderl) == true)
|
||||
if (fromTypeConverter.CanConvertTo(toUnderl))
|
||||
{
|
||||
result = fromTypeConverter.ConvertTo(null, culture, value, toUnderl);
|
||||
return true;
|
||||
@@ -329,7 +329,7 @@ namespace Avalonia.Utilities
|
||||
}
|
||||
|
||||
[RequiresUnreferencedCode(TrimmingMessages.ImplicitTypeConvertionRequiresUnreferencedCodeMessage)]
|
||||
public static T ConvertImplicit<T>(object value)
|
||||
public static T ConvertImplicit<T>(object? value)
|
||||
{
|
||||
if (TryConvertImplicit(typeof(T), value, out var result))
|
||||
{
|
||||
@@ -369,11 +369,6 @@ namespace Avalonia.Utilities
|
||||
/// </remarks>
|
||||
public static bool IsNumeric(Type type)
|
||||
{
|
||||
if (type == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var underlyingType = Nullable.GetUnderlyingType(type);
|
||||
|
||||
if (underlyingType != null)
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Avalonia.Threading;
|
||||
|
||||
@@ -15,7 +11,7 @@ public class WeakEvent<TSender, TEventArgs> : WeakEvent where TEventArgs : Event
|
||||
{
|
||||
private readonly Func<TSender, EventHandler<TEventArgs>, Action> _subscribe;
|
||||
|
||||
readonly ConditionalWeakTable<object, Subscription> _subscriptions = new();
|
||||
private readonly ConditionalWeakTable<object, Subscription> _subscriptions = new();
|
||||
|
||||
internal WeakEvent(
|
||||
Action<TSender, EventHandler<TEventArgs>> subscribe,
|
||||
@@ -51,56 +47,6 @@ public class WeakEvent<TSender, TEventArgs> : WeakEvent where TEventArgs : Event
|
||||
private readonly WeakEvent<TSender, TEventArgs> _ev;
|
||||
private readonly TSender _target;
|
||||
private readonly Action _compact;
|
||||
|
||||
struct Entry
|
||||
{
|
||||
WeakReference<IWeakEventSubscriber<TEventArgs>>? _reference;
|
||||
int _hashCode;
|
||||
|
||||
public Entry(IWeakEventSubscriber<TEventArgs> r)
|
||||
{
|
||||
if (r == null)
|
||||
{
|
||||
_reference = null;
|
||||
_hashCode = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
_hashCode = r.GetHashCode();
|
||||
_reference = new WeakReference<IWeakEventSubscriber<TEventArgs>>(r);
|
||||
}
|
||||
|
||||
public bool IsEmpty
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_reference == null)
|
||||
return true;
|
||||
if (_reference.TryGetTarget(out _))
|
||||
return false;
|
||||
_reference = null;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryGetTarget([MaybeNullWhen(false)]out IWeakEventSubscriber<TEventArgs> target)
|
||||
{
|
||||
if (_reference == null)
|
||||
{
|
||||
target = null!;
|
||||
return false;
|
||||
}
|
||||
return _reference.TryGetTarget(out target);
|
||||
}
|
||||
|
||||
public bool Equals(IWeakEventSubscriber<TEventArgs> r)
|
||||
{
|
||||
if (_reference == null || r.GetHashCode() != _hashCode)
|
||||
return false;
|
||||
return _reference.TryGetTarget(out var target) && target == r;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly Action _unsubscribe;
|
||||
private readonly WeakHashList<IWeakEventSubscriber<TEventArgs>> _list = new();
|
||||
private bool _compactScheduled;
|
||||
@@ -114,7 +60,7 @@ public class WeakEvent<TSender, TEventArgs> : WeakEvent where TEventArgs : Event
|
||||
_unsubscribe = ev._subscribe(target, OnEvent);
|
||||
}
|
||||
|
||||
void Destroy()
|
||||
private void Destroy()
|
||||
{
|
||||
if(_destroyed)
|
||||
return;
|
||||
@@ -134,15 +80,15 @@ public class WeakEvent<TSender, TEventArgs> : WeakEvent where TEventArgs : Event
|
||||
ScheduleCompact();
|
||||
}
|
||||
|
||||
void ScheduleCompact()
|
||||
private void ScheduleCompact()
|
||||
{
|
||||
if(_compactScheduled || _destroyed)
|
||||
return;
|
||||
_compactScheduled = true;
|
||||
Dispatcher.UIThread.Post(_compact, DispatcherPriority.Background);
|
||||
}
|
||||
|
||||
void Compact()
|
||||
|
||||
private void Compact()
|
||||
{
|
||||
if(!_compactScheduled)
|
||||
return;
|
||||
@@ -152,7 +98,7 @@ public class WeakEvent<TSender, TEventArgs> : WeakEvent where TEventArgs : Event
|
||||
Destroy();
|
||||
}
|
||||
|
||||
void OnEvent(object? sender, TEventArgs eventArgs)
|
||||
private void OnEvent(object? sender, TEventArgs eventArgs)
|
||||
{
|
||||
var alive = _list.GetAlive();
|
||||
if(alive == null)
|
||||
@@ -196,4 +142,4 @@ public class WeakEvent
|
||||
return () => unsubscribe(s, handler);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,8 +60,7 @@ namespace Avalonia.Utilities
|
||||
private static class SubscriptionTypeStorage<TArgs, TSubscriber>
|
||||
where TArgs : EventArgs where TSubscriber : class
|
||||
{
|
||||
public static readonly ConditionalWeakTable<object, SubscriptionDic<TArgs, TSubscriber>> Subscribers
|
||||
= new ConditionalWeakTable<object, SubscriptionDic<TArgs, TSubscriber>>();
|
||||
public static readonly ConditionalWeakTable<object, SubscriptionDic<TArgs, TSubscriber>> Subscribers = new();
|
||||
}
|
||||
|
||||
private class SubscriptionDic<T, TSubscriber> : Dictionary<string, Subscription<T, TSubscriber>>
|
||||
@@ -69,8 +68,7 @@ namespace Avalonia.Utilities
|
||||
{
|
||||
}
|
||||
|
||||
private static readonly Dictionary<Type, Dictionary<string, EventInfo>> Accessors
|
||||
= new Dictionary<Type, Dictionary<string, EventInfo>>();
|
||||
private static readonly Dictionary<Type, Dictionary<string, EventInfo>> s_accessors = new();
|
||||
|
||||
private class Subscription<T, TSubscriber> where T : EventArgs where TSubscriber : class
|
||||
{
|
||||
@@ -81,18 +79,17 @@ namespace Avalonia.Utilities
|
||||
private readonly Delegate _delegate;
|
||||
|
||||
private Descriptor[] _data = new Descriptor[2];
|
||||
private int _count = 0;
|
||||
private int _count;
|
||||
|
||||
delegate void CallerDelegate(TSubscriber s, object sender, T args);
|
||||
|
||||
struct Descriptor
|
||||
private delegate void CallerDelegate(TSubscriber s, object? sender, T args);
|
||||
|
||||
private struct Descriptor
|
||||
{
|
||||
public WeakReference<TSubscriber> Subscriber;
|
||||
public CallerDelegate Caller;
|
||||
public WeakReference<TSubscriber>? Subscriber;
|
||||
public CallerDelegate? Caller;
|
||||
}
|
||||
|
||||
private static Dictionary<MethodInfo, CallerDelegate> s_Callers =
|
||||
new Dictionary<MethodInfo, CallerDelegate>();
|
||||
private static readonly Dictionary<MethodInfo, CallerDelegate> s_callers = new();
|
||||
|
||||
public Subscription(SubscriptionDic<T, TSubscriber> sdic,
|
||||
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents)] Type targetType,
|
||||
@@ -101,8 +98,8 @@ namespace Avalonia.Utilities
|
||||
_sdic = sdic;
|
||||
_target = target;
|
||||
_eventName = eventName;
|
||||
if (!Accessors.TryGetValue(targetType, out var evDic))
|
||||
Accessors[targetType] = evDic = new Dictionary<string, EventInfo>();
|
||||
if (!s_accessors.TryGetValue(targetType, out var evDic))
|
||||
s_accessors[targetType] = evDic = new Dictionary<string, EventInfo>();
|
||||
|
||||
if (evDic.TryGetValue(eventName, out var info))
|
||||
{
|
||||
@@ -123,12 +120,12 @@ namespace Avalonia.Utilities
|
||||
|
||||
var del = new Action<object, T>(OnEvent);
|
||||
_delegate = del.GetMethodInfo().CreateDelegate(_info.EventHandlerType!, del.Target);
|
||||
_info.AddMethod!.Invoke(target, new[] { _delegate });
|
||||
_info.AddMethod!.Invoke(target, new object?[] { _delegate });
|
||||
}
|
||||
|
||||
void Destroy()
|
||||
private void Destroy()
|
||||
{
|
||||
_info.RemoveMethod!.Invoke(_target, new[] { _delegate });
|
||||
_info.RemoveMethod!.Invoke(_target, new object?[] { _delegate });
|
||||
_sdic.Remove(_eventName);
|
||||
}
|
||||
|
||||
@@ -146,8 +143,8 @@ namespace Avalonia.Utilities
|
||||
MethodInfo method = s.Method;
|
||||
|
||||
var subscriber = (TSubscriber)s.Target!;
|
||||
if (!s_Callers.TryGetValue(method, out var caller))
|
||||
s_Callers[method] = caller =
|
||||
if (!s_callers.TryGetValue(method, out var caller))
|
||||
s_callers[method] = caller =
|
||||
(CallerDelegate)Delegate.CreateDelegate(typeof(CallerDelegate), null, method);
|
||||
_data[_count] = new Descriptor
|
||||
{
|
||||
@@ -178,7 +175,7 @@ namespace Avalonia.Utilities
|
||||
}
|
||||
}
|
||||
|
||||
void Compact(bool preventDestroy = false)
|
||||
private void Compact(bool preventDestroy = false)
|
||||
{
|
||||
int empty = -1;
|
||||
for (int c = 0; c < _count; c++)
|
||||
@@ -206,15 +203,15 @@ namespace Avalonia.Utilities
|
||||
Destroy();
|
||||
}
|
||||
|
||||
void OnEvent(object sender, T eventArgs)
|
||||
private void OnEvent(object? sender, T eventArgs)
|
||||
{
|
||||
var needCompact = false;
|
||||
for(var c=0; c<_count; c++)
|
||||
for (var c = 0; c < _count; c++)
|
||||
{
|
||||
var r = _data[c].Subscriber;
|
||||
var r = _data[c].Subscriber!;
|
||||
if (r.TryGetTarget(out var sub))
|
||||
{
|
||||
_data[c].Caller(sub, sender, eventArgs);
|
||||
_data[c].Caller!(sub, sender, eventArgs);
|
||||
}
|
||||
else
|
||||
needCompact = true;
|
||||
|
||||
+13
-21
@@ -348,7 +348,7 @@ namespace Avalonia
|
||||
/// </summary>
|
||||
public void InvalidateVisual()
|
||||
{
|
||||
VisualRoot?.Renderer?.AddDirty(this);
|
||||
VisualRoot?.Renderer.AddDirty(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -449,7 +449,7 @@ namespace Avalonia
|
||||
protected override void LogicalChildrenCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
base.LogicalChildrenCollectionChanged(sender, e);
|
||||
VisualRoot?.Renderer?.RecalculateChildren(this);
|
||||
VisualRoot?.Renderer.RecalculateChildren(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -477,23 +477,19 @@ namespace Avalonia
|
||||
OnAttachedToVisualTree(e);
|
||||
AttachedToVisualTree?.Invoke(this, e);
|
||||
InvalidateVisual();
|
||||
_visualRoot.Renderer?.RecalculateChildren(_visualParent!);
|
||||
_visualRoot.Renderer.RecalculateChildren(_visualParent!);
|
||||
|
||||
if (ZIndex != 0 && VisualParent is Visual parent)
|
||||
parent.HasNonUniformZIndexChildren = true;
|
||||
|
||||
var visualChildren = VisualChildren;
|
||||
var visualChildrenCount = visualChildren.Count;
|
||||
|
||||
if (visualChildren != null)
|
||||
for (var i = 0; i < visualChildrenCount; i++)
|
||||
{
|
||||
var visualChildrenCount = visualChildren.Count;
|
||||
|
||||
for (var i = 0; i < visualChildrenCount; i++)
|
||||
if (visualChildren[i] is { } child)
|
||||
{
|
||||
if (visualChildren[i] is Visual child)
|
||||
{
|
||||
child.OnAttachedToVisualTreeCore(e);
|
||||
}
|
||||
child.OnAttachedToVisualTreeCore(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -540,20 +536,16 @@ namespace Avalonia
|
||||
}
|
||||
|
||||
DetachedFromVisualTree?.Invoke(this, e);
|
||||
e.Root?.Renderer?.AddDirty(this);
|
||||
e.Root.Renderer.AddDirty(this);
|
||||
|
||||
var visualChildren = VisualChildren;
|
||||
var visualChildrenCount = visualChildren.Count;
|
||||
|
||||
if (visualChildren != null)
|
||||
for (var i = 0; i < visualChildrenCount; i++)
|
||||
{
|
||||
var visualChildrenCount = visualChildren.Count;
|
||||
|
||||
for (var i = 0; i < visualChildrenCount; i++)
|
||||
if (visualChildren[i] is { } child)
|
||||
{
|
||||
if (visualChildren[i] is Visual child)
|
||||
{
|
||||
child.OnDetachedFromVisualTreeCore(e);
|
||||
}
|
||||
child.OnDetachedFromVisualTreeCore(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -659,7 +651,7 @@ namespace Avalonia
|
||||
parentVisual.HasNonUniformZIndexChildren = true;
|
||||
|
||||
sender?.InvalidateVisual();
|
||||
parent?.VisualRoot?.Renderer?.RecalculateChildren(parent);
|
||||
parent?.VisualRoot?.Renderer.RecalculateChildren(parent);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -46,7 +46,7 @@ namespace Avalonia.VisualTree
|
||||
Visual? v = visual ?? throw new ArgumentNullException(nameof(visual));
|
||||
var result = 0;
|
||||
|
||||
v = v?.VisualParent;
|
||||
v = v.VisualParent;
|
||||
|
||||
while (v != null)
|
||||
{
|
||||
@@ -64,17 +64,13 @@ namespace Avalonia.VisualTree
|
||||
/// <param name="visual">The first visual.</param>
|
||||
/// <param name="target">The second visual.</param>
|
||||
/// <returns>The common ancestor, or null if not found.</returns>
|
||||
public static Visual? FindCommonVisualAncestor(this Visual visual, Visual target)
|
||||
public static Visual? FindCommonVisualAncestor(this Visual? visual, Visual? target)
|
||||
{
|
||||
Visual? v = visual ?? throw new ArgumentNullException(nameof(visual));
|
||||
|
||||
if (target is null)
|
||||
if (visual is null || target is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
Visual? t = target;
|
||||
|
||||
void GoUpwards(ref Visual? node, int count)
|
||||
{
|
||||
for (int i = 0; i < count; ++i)
|
||||
@@ -83,6 +79,9 @@ namespace Avalonia.VisualTree
|
||||
}
|
||||
}
|
||||
|
||||
Visual? v = visual;
|
||||
Visual? t = target;
|
||||
|
||||
// We want to find lowest node first, then make sure that both nodes are at the same height.
|
||||
// By doing that we can sometimes find out that other node is our lowest common ancestor.
|
||||
var firstHeight = CalculateDistanceFromRoot(v);
|
||||
@@ -144,7 +143,7 @@ namespace Avalonia.VisualTree
|
||||
/// <param name="visual">The visual.</param>
|
||||
/// <param name="includeSelf">If given visual should be included in search.</param>
|
||||
/// <returns>First ancestor of given type.</returns>
|
||||
public static T? FindAncestorOfType<T>(this Visual visual, bool includeSelf = false) where T : class
|
||||
public static T? FindAncestorOfType<T>(this Visual? visual, bool includeSelf = false) where T : class
|
||||
{
|
||||
if (visual is null)
|
||||
{
|
||||
@@ -173,7 +172,7 @@ namespace Avalonia.VisualTree
|
||||
/// <param name="visual">The visual.</param>
|
||||
/// <param name="includeSelf">If given visual should be included in search.</param>
|
||||
/// <returns>First descendant of given type.</returns>
|
||||
public static T? FindDescendantOfType<T>(this Visual visual, bool includeSelf = false) where T : class
|
||||
public static T? FindDescendantOfType<T>(this Visual? visual, bool includeSelf = false) where T : class
|
||||
{
|
||||
if (visual is null)
|
||||
{
|
||||
@@ -392,7 +391,7 @@ namespace Avalonia.VisualTree
|
||||
/// True if <paramref name="visual"/> is an ancestor of <paramref name="target"/>;
|
||||
/// otherwise false.
|
||||
/// </returns>
|
||||
public static bool IsVisualAncestorOf(this Visual visual, Visual target)
|
||||
public static bool IsVisualAncestorOf(this Visual? visual, Visual? target)
|
||||
{
|
||||
Visual? current = target?.VisualParent;
|
||||
|
||||
|
||||
@@ -41,18 +41,6 @@ namespace Avalonia.Controls.Primitives
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
base.OnAttachedToVisualTree(e);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
base.OnDetachedFromVisualTree(e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the visual state of the control by applying latest PseudoClasses.
|
||||
/// </summary>
|
||||
@@ -123,28 +111,25 @@ namespace Avalonia.Controls.Primitives
|
||||
IsAlphaMaxForced,
|
||||
IsSaturationValueMaxForced);
|
||||
|
||||
if (bgraPixelData != null)
|
||||
if (_backgroundBitmap != null)
|
||||
{
|
||||
if (_backgroundBitmap != null)
|
||||
{
|
||||
// TODO: CURRENTLY DISABLED DUE TO INTERMITTENT CRASHES IN SKIA/RENDERER
|
||||
//
|
||||
// Re-use the existing WriteableBitmap
|
||||
// This assumes the height, width and byte counts are the same and must be set to null
|
||||
// elsewhere if that assumption is ever not true.
|
||||
// ColorPickerHelpers.UpdateBitmapFromPixelData(_backgroundBitmap, bgraPixelData);
|
||||
// TODO: CURRENTLY DISABLED DUE TO INTERMITTENT CRASHES IN SKIA/RENDERER
|
||||
//
|
||||
// Re-use the existing WriteableBitmap
|
||||
// This assumes the height, width and byte counts are the same and must be set to null
|
||||
// elsewhere if that assumption is ever not true.
|
||||
// ColorPickerHelpers.UpdateBitmapFromPixelData(_backgroundBitmap, bgraPixelData);
|
||||
|
||||
// TODO: ALSO DISABLED DISPOSE DUE TO INTERMITTENT CRASHES
|
||||
//_backgroundBitmap?.Dispose();
|
||||
_backgroundBitmap = ColorPickerHelpers.CreateBitmapFromPixelData(bgraPixelData, pixelWidth, pixelHeight);
|
||||
}
|
||||
else
|
||||
{
|
||||
_backgroundBitmap = ColorPickerHelpers.CreateBitmapFromPixelData(bgraPixelData, pixelWidth, pixelHeight);
|
||||
}
|
||||
|
||||
Background = new ImageBrush(_backgroundBitmap);
|
||||
// TODO: ALSO DISABLED DISPOSE DUE TO INTERMITTENT CRASHES
|
||||
//_backgroundBitmap?.Dispose();
|
||||
_backgroundBitmap = ColorPickerHelpers.CreateBitmapFromPixelData(bgraPixelData, pixelWidth, pixelHeight);
|
||||
}
|
||||
else
|
||||
{
|
||||
_backgroundBitmap = ColorPickerHelpers.CreateBitmapFromPixelData(bgraPixelData, pixelWidth, pixelHeight);
|
||||
}
|
||||
|
||||
Background = new ImageBrush(_backgroundBitmap);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3979,7 +3979,7 @@ namespace Avalonia.Controls
|
||||
{
|
||||
if (focusedObject is Control element)
|
||||
{
|
||||
parent = element.Parent;
|
||||
parent = element.VisualParent;
|
||||
if (parent != null)
|
||||
{
|
||||
dataGridWillReceiveRoutedEvent = false;
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace Avalonia.Controls.Utils
|
||||
{
|
||||
if (child is Control childElement)
|
||||
{
|
||||
parent = childElement.Parent;
|
||||
parent = childElement.VisualParent;
|
||||
}
|
||||
}
|
||||
child = parent;
|
||||
|
||||
@@ -44,8 +44,8 @@ namespace Avalonia.Controls
|
||||
/// <summary>
|
||||
/// Defines the <see cref="Layout"/> property.
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<AttachedLayout> LayoutProperty =
|
||||
AvaloniaProperty.Register<ItemsRepeater, AttachedLayout>(nameof(Layout), new StackLayout());
|
||||
public static readonly StyledProperty<AttachedLayout?> LayoutProperty =
|
||||
AvaloniaProperty.Register<ItemsRepeater, AttachedLayout?>(nameof(Layout), new StackLayout());
|
||||
|
||||
/// <summary>
|
||||
/// Defines the <see cref="VerticalCacheLength"/> property.
|
||||
@@ -53,8 +53,8 @@ namespace Avalonia.Controls
|
||||
public static readonly StyledProperty<double> VerticalCacheLengthProperty =
|
||||
AvaloniaProperty.Register<ItemsRepeater, double>(nameof(VerticalCacheLength), 2.0);
|
||||
|
||||
private static readonly StyledProperty<VirtualizationInfo> VirtualizationInfoProperty =
|
||||
AvaloniaProperty.RegisterAttached<ItemsRepeater, Control, VirtualizationInfo>("VirtualizationInfo");
|
||||
private static readonly StyledProperty<VirtualizationInfo?> VirtualizationInfoProperty =
|
||||
AvaloniaProperty.RegisterAttached<ItemsRepeater, Control, VirtualizationInfo?>("VirtualizationInfo");
|
||||
|
||||
internal static readonly Rect InvalidRect = new Rect(-1, -1, -1, -1);
|
||||
internal static readonly Point ClearedElementsArrangePosition = new Point(-10000.0, -10000.0);
|
||||
@@ -63,7 +63,7 @@ namespace Avalonia.Controls
|
||||
private readonly ViewportManager _viewportManager;
|
||||
private readonly TargetWeakEventSubscriber<ItemsRepeater, EventArgs> _layoutWeakSubscriber;
|
||||
private IEnumerable? _items;
|
||||
private VirtualizingLayoutContext? _layoutContext;
|
||||
private RepeaterLayoutContext? _layoutContext;
|
||||
private EventHandler<ChildIndexChangedEventArgs>? _childIndexChanged;
|
||||
private bool _isLayoutInProgress;
|
||||
private NotifyCollectionChangedEventArgs? _processingItemsSourceChange;
|
||||
@@ -104,7 +104,7 @@ namespace Avalonia.Controls
|
||||
/// The layout used to size and position elements. The default is a StackLayout with
|
||||
/// vertical orientation.
|
||||
/// </value>
|
||||
public AttachedLayout Layout
|
||||
public AttachedLayout? Layout
|
||||
{
|
||||
get => GetValue(LayoutProperty);
|
||||
set => SetValue(LayoutProperty, value);
|
||||
@@ -164,18 +164,7 @@ namespace Avalonia.Controls
|
||||
|
||||
private bool IsProcessingCollectionChange => _processingItemsSourceChange != null;
|
||||
|
||||
private LayoutContext LayoutContext
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_layoutContext == null)
|
||||
{
|
||||
_layoutContext = new RepeaterLayoutContext(this);
|
||||
}
|
||||
|
||||
return _layoutContext;
|
||||
}
|
||||
}
|
||||
private RepeaterLayoutContext LayoutContext => _layoutContext ??= new RepeaterLayoutContext(this);
|
||||
|
||||
event EventHandler<ChildIndexChangedEventArgs>? IChildIndexProvider.ChildIndexChanged
|
||||
{
|
||||
@@ -269,39 +258,22 @@ namespace Avalonia.Controls
|
||||
|
||||
internal void UnpinElement(Control element) => _viewManager.UpdatePin(element, false);
|
||||
|
||||
internal static VirtualizationInfo? TryGetVirtualizationInfo(Control element)
|
||||
internal static VirtualizationInfo? TryGetVirtualizationInfo(Control? element)
|
||||
{
|
||||
return (element as AvaloniaObject)?.GetValue(VirtualizationInfoProperty);
|
||||
}
|
||||
|
||||
internal static VirtualizationInfo CreateAndInitializeVirtualizationInfo(Control element)
|
||||
{
|
||||
if (TryGetVirtualizationInfo(element) != null)
|
||||
{
|
||||
throw new InvalidOperationException("VirtualizationInfo already created.");
|
||||
}
|
||||
|
||||
var result = new VirtualizationInfo();
|
||||
element.SetValue(VirtualizationInfoProperty, result);
|
||||
return result;
|
||||
return element?.GetValue(VirtualizationInfoProperty);
|
||||
}
|
||||
|
||||
internal static VirtualizationInfo GetVirtualizationInfo(Control element)
|
||||
{
|
||||
if (element is AvaloniaObject ao)
|
||||
var result = element.GetValue(VirtualizationInfoProperty);
|
||||
|
||||
if (result == null)
|
||||
{
|
||||
var result = ao.GetValue(VirtualizationInfoProperty);
|
||||
|
||||
if (result == null)
|
||||
{
|
||||
result = new VirtualizationInfo();
|
||||
ao.SetValue(VirtualizationInfoProperty, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
result = new VirtualizationInfo();
|
||||
element.SetValue(VirtualizationInfoProperty, result);
|
||||
}
|
||||
|
||||
throw new NotSupportedException("Custom implementations of AvaloniaObject not supported.");
|
||||
return result;
|
||||
}
|
||||
|
||||
private protected override void InvalidateMeasureOnChildrenChanged()
|
||||
@@ -309,6 +281,7 @@ namespace Avalonia.Controls
|
||||
// Don't invalidate measure when children change.
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Size MeasureOverride(Size availableSize)
|
||||
{
|
||||
if (_isLayoutInProgress)
|
||||
@@ -334,7 +307,7 @@ namespace Avalonia.Controls
|
||||
|
||||
if (layout != null)
|
||||
{
|
||||
var layoutContext = GetLayoutContext();
|
||||
var layoutContext = LayoutContext;
|
||||
|
||||
desiredSize = layout.Measure(layoutContext, availableSize);
|
||||
extent = new Rect(LayoutOrigin.X, LayoutOrigin.Y, desiredSize.Width, desiredSize.Height);
|
||||
@@ -364,6 +337,7 @@ namespace Avalonia.Controls
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Size ArrangeOverride(Size finalSize)
|
||||
{
|
||||
if (_isLayoutInProgress)
|
||||
@@ -380,7 +354,7 @@ namespace Avalonia.Controls
|
||||
|
||||
try
|
||||
{
|
||||
var arrangeSize = Layout?.Arrange(GetLayoutContext(), finalSize) ?? default;
|
||||
var arrangeSize = Layout?.Arrange(LayoutContext, finalSize) ?? default;
|
||||
|
||||
// The view manager might clear elements during this call.
|
||||
// That's why we call it before arranging cleared elements
|
||||
@@ -421,6 +395,7 @@ namespace Avalonia.Controls
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
base.OnAttachedToVisualTree(e);
|
||||
@@ -428,11 +403,13 @@ namespace Avalonia.Controls
|
||||
_viewportManager.ResetScrollers();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
_viewportManager.ResetScrollers();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
|
||||
{
|
||||
if (change.Property == ItemsProperty)
|
||||
@@ -501,7 +478,7 @@ namespace Avalonia.Controls
|
||||
if (parent == this)
|
||||
{
|
||||
var virtInfo = TryGetVirtualizationInfo(element);
|
||||
return _viewManager.GetElementIndex(virtInfo!);
|
||||
return _viewManager.GetElementIndex(virtInfo);
|
||||
}
|
||||
|
||||
return -1;
|
||||
@@ -529,7 +506,7 @@ namespace Avalonia.Controls
|
||||
{
|
||||
if (index >= 0 && index >= (ItemsSourceView?.Count ?? 0))
|
||||
{
|
||||
throw new ArgumentException("Argument index is invalid.", "index");
|
||||
throw new ArgumentException("Argument index is invalid.", nameof(index));
|
||||
}
|
||||
|
||||
if (_isLayoutInProgress)
|
||||
@@ -547,7 +524,7 @@ namespace Avalonia.Controls
|
||||
throw new InvalidOperationException("Cannot make an Anchor when there is no attached layout.");
|
||||
}
|
||||
|
||||
element = (Control)GetLayoutContext().GetOrCreateElementAt(index);
|
||||
element = (Control)LayoutContext.GetOrCreateElementAt(index);
|
||||
element.Measure(Size.Infinity);
|
||||
}
|
||||
|
||||
@@ -647,9 +624,9 @@ namespace Avalonia.Controls
|
||||
|
||||
if (Layout is VirtualizingLayout virtualLayout)
|
||||
{
|
||||
virtualLayout.OnItemsChanged(GetLayoutContext(), newValue, args);
|
||||
virtualLayout.OnItemsChanged(LayoutContext, newValue, args);
|
||||
}
|
||||
else if (Layout is NonVirtualizingLayout nonVirtualLayout)
|
||||
else if (Layout is NonVirtualizingLayout)
|
||||
{
|
||||
// Walk through all the elements and make sure they are cleared for
|
||||
// non-virtualizing layouts.
|
||||
@@ -693,7 +670,7 @@ namespace Avalonia.Controls
|
||||
|
||||
try
|
||||
{
|
||||
virtualLayout.OnItemsChanged(GetLayoutContext(), newValue, args);
|
||||
virtualLayout.OnItemsChanged(LayoutContext, newValue, args);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -760,7 +737,7 @@ namespace Avalonia.Controls
|
||||
AttachedLayout.ArrangeInvalidatedWeakEvent.Subscribe(newValue, _layoutWeakSubscriber);
|
||||
}
|
||||
|
||||
bool isVirtualizingLayout = newValue != null && newValue is VirtualizingLayout;
|
||||
bool isVirtualizingLayout = newValue is VirtualizingLayout;
|
||||
_viewportManager.OnLayoutChanged(isVirtualizingLayout);
|
||||
InvalidateMeasure();
|
||||
}
|
||||
@@ -788,7 +765,7 @@ namespace Avalonia.Controls
|
||||
{
|
||||
if (Layout is VirtualizingLayout virtualLayout)
|
||||
{
|
||||
virtualLayout.OnItemsChanged(GetLayoutContext(), sender, args);
|
||||
virtualLayout.OnItemsChanged(LayoutContext, sender, args);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -807,15 +784,5 @@ namespace Avalonia.Controls
|
||||
{
|
||||
_viewportManager.OnBringIntoViewRequested(e);
|
||||
}
|
||||
|
||||
private VirtualizingLayoutContext GetLayoutContext()
|
||||
{
|
||||
if (_layoutContext == null)
|
||||
{
|
||||
_layoutContext = new RepeaterLayoutContext(this);
|
||||
}
|
||||
|
||||
return _layoutContext;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ namespace Avalonia.Controls
|
||||
}
|
||||
}
|
||||
}
|
||||
if (element == null) { element = GetElementFromUniqueIdResetPool(index); };
|
||||
if (element == null) { element = GetElementFromUniqueIdResetPool(index); }
|
||||
if (element == null) { element = GetElementFromPinnedElements(index); }
|
||||
if (element == null) { element = GetElementFromElementFactory(index); }
|
||||
|
||||
@@ -221,7 +221,7 @@ namespace Avalonia.Controls
|
||||
return nextElement;
|
||||
}
|
||||
|
||||
public int GetElementIndex(VirtualizationInfo virtInfo)
|
||||
public int GetElementIndex(VirtualizationInfo? virtInfo)
|
||||
{
|
||||
if (virtInfo == null)
|
||||
{
|
||||
@@ -627,11 +627,7 @@ namespace Avalonia.Controls
|
||||
|
||||
var element = GetElement();
|
||||
|
||||
var virtInfo = ItemsRepeater.TryGetVirtualizationInfo(element);
|
||||
if (virtInfo == null)
|
||||
{
|
||||
virtInfo = ItemsRepeater.CreateAndInitializeVirtualizationInfo(element);
|
||||
}
|
||||
var virtInfo = ItemsRepeater.GetVirtualizationInfo(element);
|
||||
// Clear flag
|
||||
virtInfo.MustClearDataContext = false;
|
||||
|
||||
@@ -710,9 +706,8 @@ namespace Avalonia.Controls
|
||||
{
|
||||
if (parent is ItemsRepeater repeater)
|
||||
{
|
||||
var element = child as Control;
|
||||
if (repeater == owner &&
|
||||
element is not null &&
|
||||
child is Control element &&
|
||||
ItemsRepeater.GetVirtualizationInfo(element).IsRealized)
|
||||
{
|
||||
focusedElement = element;
|
||||
@@ -722,7 +717,7 @@ namespace Avalonia.Controls
|
||||
}
|
||||
|
||||
child = parent;
|
||||
parent = child?.GetVisualParent();
|
||||
parent = child.GetVisualParent();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -166,7 +166,7 @@ namespace Avalonia.Controls
|
||||
if (Math.Abs(_expectedViewportShift.X) > 1 || Math.Abs(_expectedViewportShift.Y) > 1)
|
||||
{
|
||||
Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Expecting viewport shift of ({Shift})",
|
||||
_owner.Layout.LayoutId, _expectedViewportShift);
|
||||
_owner.Layout?.LayoutId, _expectedViewportShift);
|
||||
|
||||
// There are cases where we might be expecting a shift but not get it. We will
|
||||
// be waiting for the effective viewport event but if the scroll viewer is not able
|
||||
@@ -287,7 +287,7 @@ namespace Avalonia.Controls
|
||||
if (_pendingViewportShift.X != 0 || _pendingViewportShift.Y != 0)
|
||||
{
|
||||
Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Layout Updated with pending shift {Shift}- invalidating measure",
|
||||
_owner.Layout.LayoutId,
|
||||
_owner.Layout?.LayoutId,
|
||||
_pendingViewportShift);
|
||||
|
||||
// Assume this is never going to come.
|
||||
@@ -436,7 +436,7 @@ namespace Avalonia.Controls
|
||||
|
||||
private void OnEffectiveViewportChanged(object? sender, EffectiveViewportChangedEventArgs e)
|
||||
{
|
||||
Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: EffectiveViewportChanged event callback", _owner.Layout.LayoutId);
|
||||
Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: EffectiveViewportChanged event callback", _owner.Layout?.LayoutId);
|
||||
UpdateViewport(e.EffectiveViewport);
|
||||
|
||||
_pendingViewportShift = default;
|
||||
@@ -490,14 +490,14 @@ namespace Avalonia.Controls
|
||||
var previousVisibleWindow = _visibleWindow;
|
||||
|
||||
Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Effective Viewport: ({Before})->({After})",
|
||||
_owner.Layout.LayoutId,
|
||||
_owner.Layout?.LayoutId,
|
||||
previousVisibleWindow,
|
||||
viewport);
|
||||
|
||||
if (-currentVisibleWindow.X <= ItemsRepeater.ClearedElementsArrangePosition.X &&
|
||||
-currentVisibleWindow.Y <= ItemsRepeater.ClearedElementsArrangePosition.Y)
|
||||
{
|
||||
Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Viewport is invalid. visible window cleared", _owner.Layout.LayoutId);
|
||||
Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Viewport is invalid. visible window cleared", _owner.Layout?.LayoutId);
|
||||
// We got cleared.
|
||||
_visibleWindow = default;
|
||||
}
|
||||
@@ -509,7 +509,7 @@ namespace Avalonia.Controls
|
||||
if (_visibleWindow != previousVisibleWindow)
|
||||
{
|
||||
Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Used Viewport: ({Before})->({After})",
|
||||
_owner.Layout.LayoutId,
|
||||
_owner.Layout?.LayoutId,
|
||||
previousVisibleWindow,
|
||||
currentVisibleWindow);
|
||||
TryInvalidateMeasure();
|
||||
@@ -532,7 +532,7 @@ namespace Avalonia.Controls
|
||||
// We invalidate measure instead of just invalidating arrange because
|
||||
// we don't invalidate measure in UpdateViewport if the view is changing to
|
||||
// avoid layout cycles.
|
||||
Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Invalidating measure due to viewport change", _owner.Layout.LayoutId);
|
||||
Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "{LayoutId}: Invalidating measure due to viewport change", _owner.Layout?.LayoutId);
|
||||
_owner.InvalidateMeasure();
|
||||
}
|
||||
}
|
||||
|
||||
+15
-13
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
@@ -17,32 +16,34 @@ namespace Avalonia.Controls.ApplicationLifetimes
|
||||
private int _exitCode;
|
||||
private CancellationTokenSource? _cts;
|
||||
private bool _isShuttingDown;
|
||||
private HashSet<Window> _windows = new HashSet<Window>();
|
||||
private readonly HashSet<Window> _windows = new();
|
||||
|
||||
private static ClassicDesktopStyleApplicationLifetime? s_activeLifetime;
|
||||
|
||||
private static ClassicDesktopStyleApplicationLifetime? _activeLifetime;
|
||||
static ClassicDesktopStyleApplicationLifetime()
|
||||
{
|
||||
Window.WindowOpenedEvent.AddClassHandler(typeof(Window), OnWindowOpened);
|
||||
Window.WindowClosedEvent.AddClassHandler(typeof(Window), WindowClosedEvent);
|
||||
Window.WindowClosedEvent.AddClassHandler(typeof(Window), OnWindowClosed);
|
||||
}
|
||||
|
||||
private static void WindowClosedEvent(object? sender, RoutedEventArgs e)
|
||||
private static void OnWindowClosed(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
_activeLifetime?._windows.Remove((Window)sender!);
|
||||
_activeLifetime?.HandleWindowClosed((Window)sender!);
|
||||
var window = (Window)sender!;
|
||||
s_activeLifetime?._windows.Remove(window);
|
||||
s_activeLifetime?.HandleWindowClosed(window);
|
||||
}
|
||||
|
||||
private static void OnWindowOpened(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
_activeLifetime?._windows.Add((Window)sender!);
|
||||
s_activeLifetime?._windows.Add((Window)sender!);
|
||||
}
|
||||
|
||||
public ClassicDesktopStyleApplicationLifetime()
|
||||
{
|
||||
if (_activeLifetime != null)
|
||||
if (s_activeLifetime != null)
|
||||
throw new InvalidOperationException(
|
||||
"Can not have multiple active ClassicDesktopStyleApplicationLifetime instances and the previously created one was not disposed");
|
||||
_activeLifetime = this;
|
||||
s_activeLifetime = this;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -65,9 +66,10 @@ namespace Avalonia.Controls.ApplicationLifetimes
|
||||
/// <inheritdoc/>
|
||||
public Window? MainWindow { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public IReadOnlyList<Window> Windows => _windows.ToArray();
|
||||
|
||||
private void HandleWindowClosed(Window window)
|
||||
private void HandleWindowClosed(Window? window)
|
||||
{
|
||||
if (window == null)
|
||||
return;
|
||||
@@ -130,8 +132,8 @@ namespace Avalonia.Controls.ApplicationLifetimes
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_activeLifetime == this)
|
||||
_activeLifetime = null;
|
||||
if (s_activeLifetime == this)
|
||||
s_activeLifetime = null;
|
||||
}
|
||||
|
||||
private bool DoShutdown(
|
||||
|
||||
+4
-1
@@ -40,7 +40,10 @@ namespace Avalonia.Controls.ApplicationLifetimes
|
||||
/// The main window.
|
||||
/// </value>
|
||||
Window? MainWindow { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of all open windows in the application.
|
||||
/// </summary>
|
||||
IReadOnlyList<Window> Windows { get; }
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -792,7 +792,7 @@ namespace Avalonia.Controls
|
||||
Control? element = focused as Control;
|
||||
if (element != null)
|
||||
{
|
||||
parent = element.Parent;
|
||||
parent = element.VisualParent;
|
||||
}
|
||||
}
|
||||
focused = parent;
|
||||
@@ -1711,7 +1711,7 @@ namespace Avalonia.Controls
|
||||
/// <param name="predicate">The predicate to use for the partial or
|
||||
/// exact match.</param>
|
||||
/// <returns>Returns the object or null.</returns>
|
||||
private object? TryGetMatch(string? searchText, AvaloniaList<object> view, AutoCompleteFilterPredicate<string?>? predicate)
|
||||
private object? TryGetMatch(string? searchText, AvaloniaList<object>? view, AutoCompleteFilterPredicate<string?>? predicate)
|
||||
{
|
||||
if (predicate is null)
|
||||
return null;
|
||||
|
||||
@@ -38,8 +38,8 @@ namespace Avalonia.Automation
|
||||
/// <summary>
|
||||
/// Defines the AutomationProperties.AcceleratorKey attached property.
|
||||
/// </summary>
|
||||
public static readonly AttachedProperty<string> AcceleratorKeyProperty =
|
||||
AvaloniaProperty.RegisterAttached<StyledElement, string>(
|
||||
public static readonly AttachedProperty<string?> AcceleratorKeyProperty =
|
||||
AvaloniaProperty.RegisterAttached<StyledElement, string?>(
|
||||
"AcceleratorKey",
|
||||
typeof(AutomationProperties));
|
||||
|
||||
@@ -54,16 +54,16 @@ namespace Avalonia.Automation
|
||||
/// <summary>
|
||||
/// Defines the AutomationProperties.AccessKey attached property
|
||||
/// </summary>
|
||||
public static readonly AttachedProperty<string> AccessKeyProperty =
|
||||
AvaloniaProperty.RegisterAttached<StyledElement, string>(
|
||||
public static readonly AttachedProperty<string?> AccessKeyProperty =
|
||||
AvaloniaProperty.RegisterAttached<StyledElement, string?>(
|
||||
"AccessKey",
|
||||
typeof(AutomationProperties));
|
||||
|
||||
/// <summary>
|
||||
/// Defines the AutomationProperties.AutomationId attached property.
|
||||
/// </summary>
|
||||
public static readonly AttachedProperty<string> AutomationIdProperty =
|
||||
AvaloniaProperty.RegisterAttached<StyledElement, string>(
|
||||
public static readonly AttachedProperty<string?> AutomationIdProperty =
|
||||
AvaloniaProperty.RegisterAttached<StyledElement, string?>(
|
||||
"AutomationId",
|
||||
typeof(AutomationProperties));
|
||||
|
||||
@@ -78,8 +78,8 @@ namespace Avalonia.Automation
|
||||
/// <summary>
|
||||
/// Defines the AutomationProperties.HelpText attached property.
|
||||
/// </summary>
|
||||
public static readonly AttachedProperty<string> HelpTextProperty =
|
||||
AvaloniaProperty.RegisterAttached<StyledElement, string>(
|
||||
public static readonly AttachedProperty<string?> HelpTextProperty =
|
||||
AvaloniaProperty.RegisterAttached<StyledElement, string?>(
|
||||
"HelpText",
|
||||
typeof(AutomationProperties));
|
||||
|
||||
@@ -122,16 +122,16 @@ namespace Avalonia.Automation
|
||||
/// <summary>
|
||||
/// Defines the AutomationProperties.ItemStatus attached property.
|
||||
/// </summary>
|
||||
public static readonly AttachedProperty<string> ItemStatusProperty =
|
||||
AvaloniaProperty.RegisterAttached<StyledElement, string>(
|
||||
public static readonly AttachedProperty<string?> ItemStatusProperty =
|
||||
AvaloniaProperty.RegisterAttached<StyledElement, string?>(
|
||||
"ItemStatus",
|
||||
typeof(AutomationProperties));
|
||||
|
||||
/// <summary>
|
||||
/// Defines the AutomationProperties.ItemType attached property.
|
||||
/// </summary>
|
||||
public static readonly AttachedProperty<string> ItemTypeProperty =
|
||||
AvaloniaProperty.RegisterAttached<StyledElement, string>(
|
||||
public static readonly AttachedProperty<string?> ItemTypeProperty =
|
||||
AvaloniaProperty.RegisterAttached<StyledElement, string?>(
|
||||
"ItemType",
|
||||
typeof(AutomationProperties));
|
||||
|
||||
@@ -155,8 +155,8 @@ namespace Avalonia.Automation
|
||||
/// <summary>
|
||||
/// Defines the AutomationProperties.Name attached attached property.
|
||||
/// </summary>
|
||||
public static readonly AttachedProperty<string> NameProperty =
|
||||
AvaloniaProperty.RegisterAttached<StyledElement, string>(
|
||||
public static readonly AttachedProperty<string?> NameProperty =
|
||||
AvaloniaProperty.RegisterAttached<StyledElement, string?>(
|
||||
"Name",
|
||||
typeof(AutomationProperties));
|
||||
|
||||
@@ -193,25 +193,17 @@ namespace Avalonia.Automation
|
||||
/// </summary>
|
||||
public static void SetAcceleratorKey(StyledElement element, string value)
|
||||
{
|
||||
if (element == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(element));
|
||||
}
|
||||
|
||||
_ = element ?? throw new ArgumentNullException(nameof(element));
|
||||
element.SetValue(AcceleratorKeyProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper for reading AcceleratorKey property from a StyledElement.
|
||||
/// </summary>
|
||||
public static string GetAcceleratorKey(StyledElement element)
|
||||
public static string? GetAcceleratorKey(StyledElement element)
|
||||
{
|
||||
if (element == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(element));
|
||||
}
|
||||
|
||||
return ((string)element.GetValue(AcceleratorKeyProperty));
|
||||
_ = element ?? throw new ArgumentNullException(nameof(element));
|
||||
return element.GetValue(AcceleratorKeyProperty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -219,11 +211,7 @@ namespace Avalonia.Automation
|
||||
/// </summary>
|
||||
public static void SetAccessibilityView(StyledElement element, AccessibilityView value)
|
||||
{
|
||||
if (element == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(element));
|
||||
}
|
||||
|
||||
_ = element ?? throw new ArgumentNullException(nameof(element));
|
||||
element.SetValue(AccessibilityViewProperty, value);
|
||||
}
|
||||
|
||||
@@ -232,11 +220,7 @@ namespace Avalonia.Automation
|
||||
/// </summary>
|
||||
public static AccessibilityView GetAccessibilityView(StyledElement element)
|
||||
{
|
||||
if (element == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(element));
|
||||
}
|
||||
|
||||
_ = element ?? throw new ArgumentNullException(nameof(element));
|
||||
return element.GetValue(AccessibilityViewProperty);
|
||||
}
|
||||
|
||||
@@ -245,50 +229,34 @@ namespace Avalonia.Automation
|
||||
/// </summary>
|
||||
public static void SetAccessKey(StyledElement element, string value)
|
||||
{
|
||||
if (element == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(element));
|
||||
}
|
||||
|
||||
_ = element ?? throw new ArgumentNullException(nameof(element));
|
||||
element.SetValue(AccessKeyProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper for reading AccessKey property from a StyledElement.
|
||||
/// </summary>
|
||||
public static string GetAccessKey(StyledElement element)
|
||||
public static string? GetAccessKey(StyledElement element)
|
||||
{
|
||||
if (element == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(element));
|
||||
}
|
||||
|
||||
return ((string)element.GetValue(AccessKeyProperty));
|
||||
_ = element ?? throw new ArgumentNullException(nameof(element));
|
||||
return element.GetValue(AccessKeyProperty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper for setting AutomationId property on a StyledElement.
|
||||
/// </summary>
|
||||
public static void SetAutomationId(StyledElement element, string value)
|
||||
public static void SetAutomationId(StyledElement element, string? value)
|
||||
{
|
||||
if (element == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(element));
|
||||
}
|
||||
|
||||
_ = element ?? throw new ArgumentNullException(nameof(element));
|
||||
element.SetValue(AutomationIdProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper for reading AutomationId property from a StyledElement.
|
||||
/// </summary>
|
||||
public static string GetAutomationId(StyledElement element)
|
||||
public static string? GetAutomationId(StyledElement element)
|
||||
{
|
||||
if (element == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(element));
|
||||
}
|
||||
|
||||
_ = element ?? throw new ArgumentNullException(nameof(element));
|
||||
return element.GetValue(AutomationIdProperty);
|
||||
}
|
||||
|
||||
@@ -297,11 +265,7 @@ namespace Avalonia.Automation
|
||||
/// </summary>
|
||||
public static void SetControlTypeOverride(StyledElement element, AutomationControlType? value)
|
||||
{
|
||||
if (element == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(element));
|
||||
}
|
||||
|
||||
_ = element ?? throw new ArgumentNullException(nameof(element));
|
||||
element.SetValue(ControlTypeOverrideProperty, value);
|
||||
}
|
||||
|
||||
@@ -310,38 +274,26 @@ namespace Avalonia.Automation
|
||||
/// </summary>
|
||||
public static AutomationControlType? GetControlTypeOverride(StyledElement element)
|
||||
{
|
||||
if (element == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(element));
|
||||
}
|
||||
|
||||
_ = element ?? throw new ArgumentNullException(nameof(element));
|
||||
return element.GetValue(ControlTypeOverrideProperty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper for setting HelpText property on a StyledElement.
|
||||
/// </summary>
|
||||
public static void SetHelpText(StyledElement element, string value)
|
||||
public static void SetHelpText(StyledElement element, string? value)
|
||||
{
|
||||
if (element == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(element));
|
||||
}
|
||||
|
||||
_ = element ?? throw new ArgumentNullException(nameof(element));
|
||||
element.SetValue(HelpTextProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper for reading HelpText property from a StyledElement.
|
||||
/// </summary>
|
||||
public static string GetHelpText(StyledElement element)
|
||||
public static string? GetHelpText(StyledElement element)
|
||||
{
|
||||
if (element == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(element));
|
||||
}
|
||||
|
||||
return ((string)element.GetValue(HelpTextProperty));
|
||||
_ = element ?? throw new ArgumentNullException(nameof(element));
|
||||
return element.GetValue(HelpTextProperty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -349,11 +301,7 @@ namespace Avalonia.Automation
|
||||
/// </summary>
|
||||
public static void SetIsColumnHeader(StyledElement element, bool value)
|
||||
{
|
||||
if (element == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(element));
|
||||
}
|
||||
|
||||
_ = element ?? throw new ArgumentNullException(nameof(element));
|
||||
element.SetValue(IsColumnHeaderProperty, value);
|
||||
}
|
||||
|
||||
@@ -362,12 +310,8 @@ namespace Avalonia.Automation
|
||||
/// </summary>
|
||||
public static bool GetIsColumnHeader(StyledElement element)
|
||||
{
|
||||
if (element == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(element));
|
||||
}
|
||||
|
||||
return ((bool)element.GetValue(IsColumnHeaderProperty));
|
||||
_ = element ?? throw new ArgumentNullException(nameof(element));
|
||||
return element.GetValue(IsColumnHeaderProperty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -375,11 +319,7 @@ namespace Avalonia.Automation
|
||||
/// </summary>
|
||||
public static void SetIsRequiredForForm(StyledElement element, bool value)
|
||||
{
|
||||
if (element == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(element));
|
||||
}
|
||||
|
||||
_ = element ?? throw new ArgumentNullException(nameof(element));
|
||||
element.SetValue(IsRequiredForFormProperty, value);
|
||||
}
|
||||
|
||||
@@ -388,12 +328,8 @@ namespace Avalonia.Automation
|
||||
/// </summary>
|
||||
public static bool GetIsRequiredForForm(StyledElement element)
|
||||
{
|
||||
if (element == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(element));
|
||||
}
|
||||
|
||||
return ((bool)element.GetValue(IsRequiredForFormProperty));
|
||||
_ = element ?? throw new ArgumentNullException(nameof(element));
|
||||
return element.GetValue(IsRequiredForFormProperty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -401,12 +337,8 @@ namespace Avalonia.Automation
|
||||
/// </summary>
|
||||
public static bool GetIsRowHeader(StyledElement element)
|
||||
{
|
||||
if (element == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(element));
|
||||
}
|
||||
|
||||
return ((bool)element.GetValue(IsRowHeaderProperty));
|
||||
_ = element ?? throw new ArgumentNullException(nameof(element));
|
||||
return element.GetValue(IsRowHeaderProperty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -414,11 +346,7 @@ namespace Avalonia.Automation
|
||||
/// </summary>
|
||||
public static void SetIsRowHeader(StyledElement element, bool value)
|
||||
{
|
||||
if (element == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(element));
|
||||
}
|
||||
|
||||
_ = element ?? throw new ArgumentNullException(nameof(element));
|
||||
element.SetValue(IsRowHeaderProperty, value);
|
||||
}
|
||||
|
||||
@@ -427,11 +355,7 @@ namespace Avalonia.Automation
|
||||
/// </summary>
|
||||
public static void SetIsOffscreenBehavior(StyledElement element, IsOffscreenBehavior value)
|
||||
{
|
||||
if (element == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(element));
|
||||
}
|
||||
|
||||
_ = element ?? throw new ArgumentNullException(nameof(element));
|
||||
element.SetValue(IsOffscreenBehaviorProperty, value);
|
||||
}
|
||||
|
||||
@@ -440,64 +364,44 @@ namespace Avalonia.Automation
|
||||
/// </summary>
|
||||
public static IsOffscreenBehavior GetIsOffscreenBehavior(StyledElement element)
|
||||
{
|
||||
if (element == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(element));
|
||||
}
|
||||
|
||||
return ((IsOffscreenBehavior)element.GetValue(IsOffscreenBehaviorProperty));
|
||||
_ = element ?? throw new ArgumentNullException(nameof(element));
|
||||
return element.GetValue(IsOffscreenBehaviorProperty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper for setting ItemStatus property on a StyledElement.
|
||||
/// </summary>
|
||||
public static void SetItemStatus(StyledElement element, string value)
|
||||
public static void SetItemStatus(StyledElement element, string? value)
|
||||
{
|
||||
if (element == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(element));
|
||||
}
|
||||
|
||||
_ = element ?? throw new ArgumentNullException(nameof(element));
|
||||
element.SetValue(ItemStatusProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper for reading ItemStatus property from a StyledElement.
|
||||
/// </summary>
|
||||
public static string GetItemStatus(StyledElement element)
|
||||
public static string? GetItemStatus(StyledElement element)
|
||||
{
|
||||
if (element == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(element));
|
||||
}
|
||||
|
||||
return ((string)element.GetValue(ItemStatusProperty));
|
||||
_ = element ?? throw new ArgumentNullException(nameof(element));
|
||||
return element.GetValue(ItemStatusProperty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper for setting ItemType property on a StyledElement.
|
||||
/// </summary>
|
||||
public static void SetItemType(StyledElement element, string value)
|
||||
public static void SetItemType(StyledElement element, string? value)
|
||||
{
|
||||
if (element == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(element));
|
||||
}
|
||||
|
||||
_ = element ?? throw new ArgumentNullException(nameof(element));
|
||||
element.SetValue(ItemTypeProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper for reading ItemType property from a StyledElement.
|
||||
/// </summary>
|
||||
public static string GetItemType(StyledElement element)
|
||||
public static string? GetItemType(StyledElement element)
|
||||
{
|
||||
if (element == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(element));
|
||||
}
|
||||
|
||||
return ((string)element.GetValue(ItemTypeProperty));
|
||||
_ = element ?? throw new ArgumentNullException(nameof(element));
|
||||
return element.GetValue(ItemTypeProperty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -505,11 +409,7 @@ namespace Avalonia.Automation
|
||||
/// </summary>
|
||||
public static void SetLabeledBy(StyledElement element, Control value)
|
||||
{
|
||||
if (element == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(element));
|
||||
}
|
||||
|
||||
_ = element ?? throw new ArgumentNullException(nameof(element));
|
||||
element.SetValue(LabeledByProperty, value);
|
||||
}
|
||||
|
||||
@@ -518,11 +418,7 @@ namespace Avalonia.Automation
|
||||
/// </summary>
|
||||
public static Control GetLabeledBy(StyledElement element)
|
||||
{
|
||||
if (element == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(element));
|
||||
}
|
||||
|
||||
_ = element ?? throw new ArgumentNullException(nameof(element));
|
||||
return element.GetValue(LabeledByProperty);
|
||||
}
|
||||
|
||||
@@ -531,11 +427,7 @@ namespace Avalonia.Automation
|
||||
/// </summary>
|
||||
public static void SetLiveSetting(StyledElement element, AutomationLiveSetting value)
|
||||
{
|
||||
if (element == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(element));
|
||||
}
|
||||
|
||||
_ = element ?? throw new ArgumentNullException(nameof(element));
|
||||
element.SetValue(LiveSettingProperty, value);
|
||||
}
|
||||
|
||||
@@ -544,38 +436,26 @@ namespace Avalonia.Automation
|
||||
/// </summary>
|
||||
public static AutomationLiveSetting GetLiveSetting(StyledElement element)
|
||||
{
|
||||
if (element == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(element));
|
||||
}
|
||||
|
||||
return ((AutomationLiveSetting)element.GetValue(LiveSettingProperty));
|
||||
_ = element ?? throw new ArgumentNullException(nameof(element));
|
||||
return element.GetValue(LiveSettingProperty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper for setting Name property on a StyledElement.
|
||||
/// </summary>
|
||||
public static void SetName(StyledElement element, string value)
|
||||
public static void SetName(StyledElement element, string? value)
|
||||
{
|
||||
if (element == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(element));
|
||||
}
|
||||
|
||||
_ = element ?? throw new ArgumentNullException(nameof(element));
|
||||
element.SetValue(NameProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper for reading Name property from a StyledElement.
|
||||
/// </summary>
|
||||
public static string GetName(StyledElement element)
|
||||
public static string? GetName(StyledElement element)
|
||||
{
|
||||
if (element == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(element));
|
||||
}
|
||||
|
||||
return ((string)element.GetValue(NameProperty));
|
||||
_ = element ?? throw new ArgumentNullException(nameof(element));
|
||||
return element.GetValue(NameProperty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -583,11 +463,7 @@ namespace Avalonia.Automation
|
||||
/// </summary>
|
||||
public static void SetPositionInSet(StyledElement element, int value)
|
||||
{
|
||||
if (element == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(element));
|
||||
}
|
||||
|
||||
_ = element ?? throw new ArgumentNullException(nameof(element));
|
||||
element.SetValue(PositionInSetProperty, value);
|
||||
}
|
||||
|
||||
@@ -596,12 +472,8 @@ namespace Avalonia.Automation
|
||||
/// </summary>
|
||||
public static int GetPositionInSet(StyledElement element)
|
||||
{
|
||||
if (element == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(element));
|
||||
}
|
||||
|
||||
return ((int)element.GetValue(PositionInSetProperty));
|
||||
_ = element ?? throw new ArgumentNullException(nameof(element));
|
||||
return element.GetValue(PositionInSetProperty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -609,11 +481,7 @@ namespace Avalonia.Automation
|
||||
/// </summary>
|
||||
public static void SetSizeOfSet(StyledElement element, int value)
|
||||
{
|
||||
if (element == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(element));
|
||||
}
|
||||
|
||||
_ = element ?? throw new ArgumentNullException(nameof(element));
|
||||
element.SetValue(SizeOfSetProperty, value);
|
||||
}
|
||||
|
||||
@@ -622,12 +490,8 @@ namespace Avalonia.Automation
|
||||
/// </summary>
|
||||
public static int GetSizeOfSet(StyledElement element)
|
||||
{
|
||||
if (element == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(element));
|
||||
}
|
||||
|
||||
return ((int)element.GetValue(SizeOfSetProperty));
|
||||
_ = element ?? throw new ArgumentNullException(nameof(element));
|
||||
return element.GetValue(SizeOfSetProperty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
using System;
|
||||
using Avalonia.Automation.Peers;
|
||||
using Avalonia.Automation.Provider;
|
||||
using Avalonia.Controls.Primitives;
|
||||
|
||||
namespace Avalonia.Controls.Automation.Peers
|
||||
{
|
||||
public class ProgressBarAutomationPeer : RangeBaseAutomationPeer, IRangeValueProvider
|
||||
{
|
||||
public ProgressBarAutomationPeer(RangeBase owner) : base(owner)
|
||||
{
|
||||
}
|
||||
|
||||
protected override string GetClassNameCore()
|
||||
{
|
||||
return "ProgressBar";
|
||||
}
|
||||
|
||||
protected override AutomationControlType GetAutomationControlTypeCore()
|
||||
{
|
||||
return AutomationControlType.ProgressBar;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Request to set the value that this UI element is representing
|
||||
/// </summary>
|
||||
/// <param name="val">Value to set the UI to, as an object</param>
|
||||
/// <returns>true if the UI element was successfully set to the specified value</returns>
|
||||
void IRangeValueProvider.SetValue(double val)
|
||||
{
|
||||
throw new InvalidOperationException("ProgressBar is ReadOnly, value can't be set.");
|
||||
}
|
||||
|
||||
///<summary>Indicates that the value can only be read, not modified.
|
||||
///returns True if the control is read-only</summary>
|
||||
bool IRangeValueProvider.IsReadOnly
|
||||
{
|
||||
get
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
///<summary>Value of a Large Change</summary>
|
||||
double IRangeValueProvider.LargeChange
|
||||
{
|
||||
get
|
||||
{
|
||||
return double.NaN;
|
||||
}
|
||||
}
|
||||
|
||||
///<summary>Value of a Small Change</summary>
|
||||
double IRangeValueProvider.SmallChange
|
||||
{
|
||||
get
|
||||
{
|
||||
return double.NaN;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -394,10 +394,10 @@ namespace Avalonia.Controls
|
||||
if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
|
||||
{
|
||||
IsPressed = true;
|
||||
e.Handled = true;
|
||||
|
||||
if (ClickMode == ClickMode.Press)
|
||||
{
|
||||
e.Handled = true;
|
||||
OnClick();
|
||||
}
|
||||
}
|
||||
@@ -411,11 +411,11 @@ namespace Avalonia.Controls
|
||||
if (IsPressed && e.InitialPressMouseButton == MouseButton.Left)
|
||||
{
|
||||
IsPressed = false;
|
||||
e.Handled = true;
|
||||
|
||||
if (ClickMode == ClickMode.Release &&
|
||||
this.GetVisualsAt(e.GetPosition(this)).Any(c => this == c || this.IsVisualAncestorOf(c)))
|
||||
{
|
||||
e.Handled = true;
|
||||
OnClick();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -237,11 +237,11 @@ namespace Avalonia.Controls
|
||||
private DateTime _selectedYear;
|
||||
|
||||
private DateTime _displayDate = DateTime.Today;
|
||||
private DateTime? _displayDateStart = null;
|
||||
private DateTime? _displayDateEnd = null;
|
||||
private DateTime? _displayDateStart;
|
||||
private DateTime? _displayDateEnd;
|
||||
|
||||
private bool _isShiftPressed;
|
||||
private bool _displayDateIsChanging = false;
|
||||
private bool _displayDateIsChanging;
|
||||
|
||||
internal CalendarDayButton? FocusButton { get; set; }
|
||||
internal CalendarButton? FocusCalendarButton { get; set; }
|
||||
@@ -291,7 +291,7 @@ namespace Avalonia.Controls
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("d", "Invalid DayOfWeek");
|
||||
throw new ArgumentOutOfRangeException(nameof(e), "Invalid DayOfWeek");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -346,10 +346,10 @@ namespace Avalonia.Controls
|
||||
}
|
||||
}
|
||||
|
||||
public static readonly StyledProperty<IBrush> HeaderBackgroundProperty =
|
||||
AvaloniaProperty.Register<Calendar, IBrush>(nameof(HeaderBackground));
|
||||
public static readonly StyledProperty<IBrush?> HeaderBackgroundProperty =
|
||||
AvaloniaProperty.Register<Calendar, IBrush?>(nameof(HeaderBackground));
|
||||
|
||||
public IBrush HeaderBackground
|
||||
public IBrush? HeaderBackground
|
||||
{
|
||||
get { return GetValue(HeaderBackgroundProperty); }
|
||||
set { SetValue(HeaderBackgroundProperty, value); }
|
||||
@@ -478,7 +478,7 @@ namespace Avalonia.Controls
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("d", "Invalid SelectionMode");
|
||||
throw new ArgumentOutOfRangeException(nameof(e), "Invalid SelectionMode");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -574,7 +574,7 @@ namespace Avalonia.Controls
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("d", "SelectedDate value is not valid.");
|
||||
throw new ArgumentOutOfRangeException(nameof(e), "SelectedDate value is not valid.");
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace Avalonia.Controls.Primitives
|
||||
/// <summary>
|
||||
/// The Calendar whose dates this object represents.
|
||||
/// </summary>
|
||||
private Calendar _owner;
|
||||
private readonly Calendar _owner;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the
|
||||
@@ -79,13 +79,13 @@ namespace Avalonia.Controls.Primitives
|
||||
|
||||
if (DateTime.Compare(end, start) > -1)
|
||||
{
|
||||
rangeStart = DateTimeHelper.DiscardTime(start).Value;
|
||||
rangeEnd = DateTimeHelper.DiscardTime(end).Value;
|
||||
rangeStart = DateTimeHelper.DiscardTime(start);
|
||||
rangeEnd = DateTimeHelper.DiscardTime(end);
|
||||
}
|
||||
else
|
||||
{
|
||||
rangeStart = DateTimeHelper.DiscardTime(end).Value;
|
||||
rangeEnd = DateTimeHelper.DiscardTime(start).Value;
|
||||
rangeStart = DateTimeHelper.DiscardTime(end);
|
||||
rangeEnd = DateTimeHelper.DiscardTime(start);
|
||||
}
|
||||
|
||||
int count = Count;
|
||||
@@ -144,7 +144,7 @@ namespace Avalonia.Controls.Primitives
|
||||
|
||||
if (!IsValid(item))
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("Value is not valid.");
|
||||
throw new ArgumentOutOfRangeException(nameof(item), "Value is not valid.");
|
||||
}
|
||||
|
||||
base.InsertItem(index, item);
|
||||
@@ -186,7 +186,7 @@ namespace Avalonia.Controls.Primitives
|
||||
|
||||
if (!IsValid(item))
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("Value is not valid.");
|
||||
throw new ArgumentOutOfRangeException(nameof(item), "Value is not valid.");
|
||||
}
|
||||
|
||||
base.SetItem(index, item);
|
||||
|
||||
@@ -44,30 +44,30 @@ namespace Avalonia.Controls.Primitives
|
||||
private ITemplate<Control>? _dayTitleTemplate;
|
||||
|
||||
private DateTime _currentMonth;
|
||||
private bool _isMouseLeftButtonDown = false;
|
||||
private bool _isMouseLeftButtonDownYearView = false;
|
||||
private bool _isControlPressed = false;
|
||||
private bool _isMouseLeftButtonDown;
|
||||
private bool _isMouseLeftButtonDownYearView;
|
||||
private bool _isControlPressed;
|
||||
|
||||
private System.Globalization.Calendar _calendar = new System.Globalization.GregorianCalendar();
|
||||
|
||||
private PointerPressedEventArgs? _downEventArg;
|
||||
private PointerPressedEventArgs? _downEventArgYearView;
|
||||
private readonly System.Globalization.Calendar _calendar = new GregorianCalendar();
|
||||
|
||||
internal Calendar? Owner { get; set; }
|
||||
internal CalendarDayButton? CurrentButton { get; set; }
|
||||
|
||||
public static readonly StyledProperty<IBrush> HeaderBackgroundProperty = Calendar.HeaderBackgroundProperty.AddOwner<CalendarItem>();
|
||||
public IBrush HeaderBackground
|
||||
public static readonly StyledProperty<IBrush?> HeaderBackgroundProperty = Calendar.HeaderBackgroundProperty.AddOwner<CalendarItem>();
|
||||
|
||||
public IBrush? HeaderBackground
|
||||
{
|
||||
get { return GetValue(HeaderBackgroundProperty); }
|
||||
set { SetValue(HeaderBackgroundProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DirectProperty<CalendarItem, ITemplate<Control>?> DayTitleTemplateProperty =
|
||||
AvaloniaProperty.RegisterDirect<CalendarItem, ITemplate<Control>?>(
|
||||
nameof(DayTitleTemplate),
|
||||
o => o.DayTitleTemplate,
|
||||
(o,v) => o.DayTitleTemplate = v,
|
||||
defaultBindingMode: BindingMode.OneTime);
|
||||
|
||||
public ITemplate<Control>? DayTitleTemplate
|
||||
{
|
||||
get { return _dayTitleTemplate; }
|
||||
@@ -178,7 +178,7 @@ namespace Avalonia.Controls.Primitives
|
||||
{
|
||||
if (_dayTitleTemplate != null)
|
||||
{
|
||||
var cell = (Control) _dayTitleTemplate.Build();
|
||||
var cell = _dayTitleTemplate.Build();
|
||||
cell.DataContext = string.Empty;
|
||||
cell.SetValue(Grid.RowProperty, 0);
|
||||
cell.SetValue(Grid.ColumnProperty, i);
|
||||
@@ -308,16 +308,13 @@ namespace Avalonia.Controls.Primitives
|
||||
for (int childIndex = 0; childIndex < Calendar.ColumnsPerMonth; childIndex++)
|
||||
{
|
||||
var daytitle = MonthView!.Children[childIndex];
|
||||
if (daytitle != null)
|
||||
if (Owner != null)
|
||||
{
|
||||
if (Owner != null)
|
||||
{
|
||||
daytitle.DataContext = DateTimeHelper.GetCurrentDateFormat().ShortestDayNames[(childIndex + (int)Owner.FirstDayOfWeek) % NumberOfDaysPerWeek];
|
||||
}
|
||||
else
|
||||
{
|
||||
daytitle.DataContext = DateTimeHelper.GetCurrentDateFormat().ShortestDayNames[(childIndex + (int)DateTimeHelper.GetCurrentDateFormat().FirstDayOfWeek) % NumberOfDaysPerWeek];
|
||||
}
|
||||
daytitle.DataContext = DateTimeHelper.GetCurrentDateFormat().ShortestDayNames[(childIndex + (int)Owner.FirstDayOfWeek) % NumberOfDaysPerWeek];
|
||||
}
|
||||
else
|
||||
{
|
||||
daytitle.DataContext = DateTimeHelper.GetCurrentDateFormat().ShortestDayNames[(childIndex + (int)DateTimeHelper.GetCurrentDateFormat().FirstDayOfWeek) % NumberOfDaysPerWeek];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -527,7 +524,7 @@ namespace Avalonia.Controls.Primitives
|
||||
childButton.Content = dateToAdd.Day.ToString(DateTimeHelper.GetCurrentDateFormat());
|
||||
childButton.DataContext = dateToAdd;
|
||||
|
||||
if (DateTime.Compare((DateTime)DateTimeHelper.DiscardTime(DateTime.MaxValue), dateToAdd) > 0)
|
||||
if (DateTime.Compare(DateTimeHelper.DiscardTime(DateTime.MaxValue), dateToAdd) > 0)
|
||||
{
|
||||
// Since we are sure DisplayDate is not equal to
|
||||
// DateTime.MaxValue, it is safe to use AddDays
|
||||
@@ -587,7 +584,7 @@ namespace Avalonia.Controls.Primitives
|
||||
{
|
||||
if (Owner != null)
|
||||
{
|
||||
_currentMonth = (DateTime)Owner.SelectedMonth;
|
||||
_currentMonth = Owner.SelectedMonth;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -676,7 +673,7 @@ namespace Avalonia.Controls.Primitives
|
||||
if (Owner != null)
|
||||
{
|
||||
selectedYear = Owner.SelectedYear;
|
||||
_currentMonth = (DateTime)Owner.SelectedMonth;
|
||||
_currentMonth = Owner.SelectedMonth;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -696,9 +693,9 @@ namespace Avalonia.Controls.Primitives
|
||||
SetYearButtons(decade, decadeEnd);
|
||||
}
|
||||
}
|
||||
internal void UpdateYearViewSelection(CalendarButton calendarButton)
|
||||
internal void UpdateYearViewSelection(CalendarButton? calendarButton)
|
||||
{
|
||||
if (Owner != null && calendarButton != null && calendarButton.DataContext != null)
|
||||
if (Owner != null && calendarButton?.DataContext is DateTime selectedDate)
|
||||
{
|
||||
Owner.FocusCalendarButton!.IsCalendarButtonFocused = false;
|
||||
Owner.FocusCalendarButton = calendarButton;
|
||||
@@ -706,11 +703,11 @@ namespace Avalonia.Controls.Primitives
|
||||
|
||||
if (Owner.DisplayMode == CalendarMode.Year)
|
||||
{
|
||||
Owner.SelectedMonth = (DateTime)calendarButton.DataContext;
|
||||
Owner.SelectedMonth = selectedDate;
|
||||
}
|
||||
else
|
||||
{
|
||||
Owner.SelectedYear = (DateTime)calendarButton.DataContext;
|
||||
Owner.SelectedYear = selectedDate;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -719,7 +716,7 @@ namespace Avalonia.Controls.Primitives
|
||||
{
|
||||
int year;
|
||||
int count = -1;
|
||||
foreach (object child in YearView!.Children)
|
||||
foreach (var child in YearView!.Children)
|
||||
{
|
||||
CalendarButton childButton = (CalendarButton)child;
|
||||
year = decade + count;
|
||||
@@ -859,7 +856,8 @@ namespace Avalonia.Controls.Primitives
|
||||
{
|
||||
if (Owner != null)
|
||||
{
|
||||
if (_isMouseLeftButtonDown && sender is CalendarDayButton b && b.IsEnabled && !b.IsBlackout)
|
||||
if (_isMouseLeftButtonDown
|
||||
&& sender is CalendarDayButton { IsEnabled: true, IsBlackout: false, DataContext: DateTime selectedDate } b)
|
||||
{
|
||||
// Update the states of all buttons to be selected starting
|
||||
// from HoverStart to b
|
||||
@@ -867,7 +865,6 @@ namespace Avalonia.Controls.Primitives
|
||||
{
|
||||
case CalendarSelectionMode.SingleDate:
|
||||
{
|
||||
DateTime selectedDate = (DateTime)b.DataContext!;
|
||||
Owner.CalendarDatePickerDisplayDateFlag = true;
|
||||
if (Owner.SelectedDates.Count == 0)
|
||||
{
|
||||
@@ -882,10 +879,9 @@ namespace Avalonia.Controls.Primitives
|
||||
case CalendarSelectionMode.SingleRange:
|
||||
case CalendarSelectionMode.MultipleRange:
|
||||
{
|
||||
Debug.Assert(b.DataContext != null, "The DataContext should not be null!");
|
||||
Owner.UnHighlightDays();
|
||||
Owner.HoverEndIndex = b.Index;
|
||||
Owner.HoverEnd = (DateTime?)b.DataContext;
|
||||
Owner.HoverEnd = selectedDate;
|
||||
// Update the States of the buttons
|
||||
Owner.HighlightDays();
|
||||
return;
|
||||
@@ -904,22 +900,14 @@ namespace Avalonia.Controls.Primitives
|
||||
Owner.Focus();
|
||||
}
|
||||
|
||||
bool ctrl, shift;
|
||||
CalendarExtensions.GetMetaKeyState(e.KeyModifiers, out ctrl, out shift);
|
||||
CalendarDayButton b = (CalendarDayButton)sender!;
|
||||
CalendarExtensions.GetMetaKeyState(e.KeyModifiers, out var ctrl, out var shift);
|
||||
|
||||
if (b != null)
|
||||
if (sender is CalendarDayButton b)
|
||||
{
|
||||
_isControlPressed = ctrl;
|
||||
if (b.IsEnabled && !b.IsBlackout)
|
||||
if (b.IsEnabled && !b.IsBlackout && b.DataContext is DateTime selectedDate)
|
||||
{
|
||||
DateTime selectedDate = (DateTime)b.DataContext!;
|
||||
_isMouseLeftButtonDown = true;
|
||||
// null check is added for unit tests
|
||||
if (e != null)
|
||||
{
|
||||
_downEventArg = e;
|
||||
}
|
||||
|
||||
switch (Owner.SelectionMode)
|
||||
{
|
||||
@@ -1010,12 +998,12 @@ namespace Avalonia.Controls.Primitives
|
||||
}
|
||||
}
|
||||
}
|
||||
private void AddSelection(CalendarDayButton b)
|
||||
private void AddSelection(CalendarDayButton b, DateTime selectedDate)
|
||||
{
|
||||
if (Owner != null)
|
||||
{
|
||||
Owner.HoverEndIndex = b.Index;
|
||||
Owner.HoverEnd = (DateTime)b.DataContext!;
|
||||
Owner.HoverEnd = selectedDate;
|
||||
|
||||
if (Owner.HoverEnd != null && Owner.HoverStart != null)
|
||||
{
|
||||
@@ -1025,7 +1013,7 @@ namespace Avalonia.Controls.Primitives
|
||||
// SelectionMode
|
||||
Owner.IsMouseSelection = true;
|
||||
Owner.SelectedDates.AddRange(Owner.HoverStart.Value, Owner.HoverEnd.Value);
|
||||
Owner.OnDayClick((DateTime)b.DataContext);
|
||||
Owner.OnDayClick(selectedDate);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1039,11 +1027,11 @@ namespace Avalonia.Controls.Primitives
|
||||
Owner.OnDayButtonMouseUp(e);
|
||||
}
|
||||
_isMouseLeftButtonDown = false;
|
||||
if (b != null && b.DataContext != null)
|
||||
if (b != null && b.DataContext is DateTime selectedDate)
|
||||
{
|
||||
if (Owner.SelectionMode == CalendarSelectionMode.None || Owner.SelectionMode == CalendarSelectionMode.SingleDate)
|
||||
{
|
||||
Owner.OnDayClick((DateTime)b.DataContext);
|
||||
Owner.OnDayClick(selectedDate);
|
||||
return;
|
||||
}
|
||||
if (Owner.HoverStart.HasValue)
|
||||
@@ -1058,14 +1046,14 @@ namespace Avalonia.Controls.Primitives
|
||||
Owner.RemovedItems.Add(item);
|
||||
}
|
||||
Owner.SelectedDates.ClearInternal();
|
||||
AddSelection(b);
|
||||
AddSelection(b, selectedDate);
|
||||
return;
|
||||
}
|
||||
case CalendarSelectionMode.MultipleRange:
|
||||
{
|
||||
// add the selection (either single day or
|
||||
// SingleRange day)
|
||||
AddSelection(b);
|
||||
AddSelection(b, selectedDate);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -1076,7 +1064,7 @@ namespace Avalonia.Controls.Primitives
|
||||
// be able to switch months
|
||||
if (b.IsInactive && b.IsBlackout)
|
||||
{
|
||||
Owner.OnDayClick((DateTime)b.DataContext);
|
||||
Owner.OnDayClick(selectedDate);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1095,9 +1083,9 @@ namespace Avalonia.Controls.Primitives
|
||||
Owner.HoverStart = null;
|
||||
_isMouseLeftButtonDown = false;
|
||||
b.IsSelected = false;
|
||||
if (b.DataContext != null)
|
||||
if (b.DataContext is DateTime selectedDate)
|
||||
{
|
||||
Owner.SelectedDates.Remove((DateTime)b.DataContext);
|
||||
Owner.SelectedDates.Remove(selectedDate);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1107,35 +1095,26 @@ namespace Avalonia.Controls.Primitives
|
||||
|
||||
private void Month_CalendarButtonMouseDown(object? sender, PointerPressedEventArgs e)
|
||||
{
|
||||
CalendarButton b = (CalendarButton)sender!;
|
||||
|
||||
_isMouseLeftButtonDownYearView = true;
|
||||
|
||||
if (e != null)
|
||||
{
|
||||
_downEventArgYearView = e;
|
||||
}
|
||||
|
||||
UpdateYearViewSelection(b);
|
||||
UpdateYearViewSelection(sender as CalendarButton);
|
||||
}
|
||||
|
||||
internal void Month_CalendarButtonMouseUp(object? sender, PointerReleasedEventArgs e)
|
||||
{
|
||||
_isMouseLeftButtonDownYearView = false;
|
||||
|
||||
if (Owner != null)
|
||||
if (Owner != null && (sender as CalendarButton)?.DataContext is DateTime newMonth)
|
||||
{
|
||||
DateTime newmonth = (DateTime)((CalendarButton)sender!).DataContext!;
|
||||
|
||||
if (Owner.DisplayMode == CalendarMode.Year)
|
||||
{
|
||||
Owner.DisplayDate = newmonth;
|
||||
Owner.DisplayDate = newMonth;
|
||||
Owner.DisplayMode = CalendarMode.Month;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Assert(Owner.DisplayMode == CalendarMode.Decade, "The owning Calendar should be in decade mode!");
|
||||
Owner.SelectedMonth = newmonth;
|
||||
Owner.SelectedMonth = newMonth;
|
||||
Owner.DisplayMode = CalendarMode.Year;
|
||||
}
|
||||
}
|
||||
@@ -1145,8 +1124,7 @@ namespace Avalonia.Controls.Primitives
|
||||
{
|
||||
if (_isMouseLeftButtonDownYearView)
|
||||
{
|
||||
CalendarButton b = (CalendarButton)sender!;
|
||||
UpdateYearViewSelection(b);
|
||||
UpdateYearViewSelection(sender as CalendarButton);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ namespace Avalonia.Controls
|
||||
|
||||
public static int CompareDays(DateTime dt1, DateTime dt2)
|
||||
{
|
||||
return DateTime.Compare(DiscardTime(dt1).Value, DiscardTime(dt2).Value);
|
||||
return DateTime.Compare(DiscardTime(dt1), DiscardTime(dt2));
|
||||
}
|
||||
|
||||
public static int CompareYearMonth(DateTime dt1, DateTime dt2)
|
||||
@@ -71,14 +71,9 @@ namespace Avalonia.Controls
|
||||
return new DateTime(d.Year, d.Month, 1, 0, 0, 0);
|
||||
}
|
||||
|
||||
[return: NotNullIfNotNull("d")]
|
||||
public static DateTime? DiscardTime(DateTime? d)
|
||||
public static DateTime DiscardTime(DateTime d)
|
||||
{
|
||||
if (d == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return d.Value.Date;
|
||||
return d.Date;
|
||||
}
|
||||
|
||||
public static int EndOfDecade(DateTime date)
|
||||
@@ -127,28 +122,14 @@ namespace Avalonia.Controls
|
||||
|
||||
public static string ToYearMonthPatternString(DateTime date)
|
||||
{
|
||||
string result = string.Empty;
|
||||
DateTimeFormatInfo format = GetCurrentDateFormat();
|
||||
|
||||
if (format != null)
|
||||
{
|
||||
result = date.ToString(format.YearMonthPattern, format);
|
||||
}
|
||||
|
||||
return result;
|
||||
var format = GetCurrentDateFormat();
|
||||
return date.ToString(format.YearMonthPattern, format);
|
||||
}
|
||||
|
||||
public static string ToYearString(DateTime date)
|
||||
{
|
||||
string result = string.Empty;
|
||||
DateTimeFormatInfo format = GetCurrentDateFormat();
|
||||
|
||||
if (format != null)
|
||||
{
|
||||
result = date.Year.ToString(format);
|
||||
}
|
||||
|
||||
return result;
|
||||
var format = GetCurrentDateFormat();
|
||||
return date.Year.ToString(format);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,11 +51,11 @@ namespace Avalonia.Controls
|
||||
private bool _isDropDownOpen;
|
||||
private DateTime? _selectedDate;
|
||||
private string? _text;
|
||||
private bool _suspendTextChangeHandler = false;
|
||||
private bool _isPopupClosing = false;
|
||||
private bool _ignoreButtonClick = false;
|
||||
private bool _isFlyoutOpen = false;
|
||||
private bool _isPressed = false;
|
||||
private bool _suspendTextChangeHandler;
|
||||
private bool _isPopupClosing;
|
||||
private bool _ignoreButtonClick;
|
||||
private bool _isFlyoutOpen;
|
||||
private bool _isPressed;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the drop-down
|
||||
@@ -185,7 +185,7 @@ namespace Avalonia.Controls
|
||||
{
|
||||
_textBox.KeyDown += TextBox_KeyDown;
|
||||
_textBox.GotFocus += TextBox_GotFocus;
|
||||
_textBoxTextChangedSubscription = _textBox.GetObservable(TextBox.TextProperty).Subscribe(txt => TextBox_TextChanged());
|
||||
_textBoxTextChangedSubscription = _textBox.GetObservable(TextBox.TextProperty).Subscribe(_ => TextBox_TextChanged());
|
||||
|
||||
if(SelectedDate.HasValue)
|
||||
{
|
||||
@@ -292,7 +292,7 @@ namespace Avalonia.Controls
|
||||
// Text
|
||||
else if (change.Property == TextProperty)
|
||||
{
|
||||
var (oldValue, newValue) = change.GetOldAndNewValue<string>();
|
||||
var (_, newValue) = change.GetOldAndNewValue<string?>();
|
||||
|
||||
if (!_suspendTextChangeHandler)
|
||||
{
|
||||
@@ -595,9 +595,9 @@ namespace Avalonia.Controls
|
||||
|
||||
private void Calendar_KeyDown(object? sender, KeyEventArgs e)
|
||||
{
|
||||
Calendar? c = sender as Calendar ?? throw new ArgumentException("Sender must be Calendar.", nameof(sender));
|
||||
|
||||
if (!e.Handled && (e.Key == Key.Enter || e.Key == Key.Space || e.Key == Key.Escape) && c.DisplayMode == CalendarMode.Month)
|
||||
if (!e.Handled
|
||||
&& sender is Calendar { DisplayMode: CalendarMode.Month }
|
||||
&& (e.Key == Key.Enter || e.Key == Key.Space || e.Key == Key.Escape))
|
||||
{
|
||||
Focus();
|
||||
IsDropDownOpen = false;
|
||||
|
||||
@@ -17,28 +17,26 @@ namespace Avalonia.Controls.Chrome
|
||||
|
||||
private void UpdateSize(Window window)
|
||||
{
|
||||
if (window != null)
|
||||
Margin = new Thickness(
|
||||
window.OffScreenMargin.Left,
|
||||
window.OffScreenMargin.Top,
|
||||
window.OffScreenMargin.Right,
|
||||
window.OffScreenMargin.Bottom);
|
||||
|
||||
if (window.WindowState != WindowState.FullScreen)
|
||||
{
|
||||
Margin = new Thickness(
|
||||
window.OffScreenMargin.Left,
|
||||
window.OffScreenMargin.Top,
|
||||
window.OffScreenMargin.Right,
|
||||
window.OffScreenMargin.Bottom);
|
||||
Height = window.WindowDecorationMargin.Top;
|
||||
|
||||
if (window.WindowState != WindowState.FullScreen)
|
||||
if (_captionButtons != null)
|
||||
{
|
||||
Height = window.WindowDecorationMargin.Top;
|
||||
|
||||
if (_captionButtons != null)
|
||||
{
|
||||
_captionButtons.Height = Height;
|
||||
}
|
||||
_captionButtons.Height = Height;
|
||||
}
|
||||
|
||||
IsVisible = window.PlatformImpl?.NeedsManagedDecorations ?? false;
|
||||
}
|
||||
|
||||
IsVisible = window.PlatformImpl?.NeedsManagedDecorations ?? false;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||
{
|
||||
base.OnApplyTemplate(e);
|
||||
@@ -55,6 +53,7 @@ namespace Avalonia.Controls.Chrome
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
base.OnAttachedToVisualTree(e);
|
||||
@@ -64,13 +63,13 @@ namespace Avalonia.Controls.Chrome
|
||||
_disposables = new CompositeDisposable(6)
|
||||
{
|
||||
window.GetObservable(Window.WindowDecorationMarginProperty)
|
||||
.Subscribe(x => UpdateSize(window)),
|
||||
.Subscribe(_ => UpdateSize(window)),
|
||||
window.GetObservable(Window.ExtendClientAreaTitleBarHeightHintProperty)
|
||||
.Subscribe(x => UpdateSize(window)),
|
||||
.Subscribe(_ => UpdateSize(window)),
|
||||
window.GetObservable(Window.OffScreenMarginProperty)
|
||||
.Subscribe(x => UpdateSize(window)),
|
||||
.Subscribe(_ => UpdateSize(window)),
|
||||
window.GetObservable(Window.ExtendClientAreaChromeHintsProperty)
|
||||
.Subscribe(x => UpdateSize(window)),
|
||||
.Subscribe(_ => UpdateSize(window)),
|
||||
window.GetObservable(Window.WindowStateProperty)
|
||||
.Subscribe(x =>
|
||||
{
|
||||
@@ -80,11 +79,12 @@ namespace Avalonia.Controls.Chrome
|
||||
PseudoClasses.Set(":fullscreen", x == WindowState.FullScreen);
|
||||
}),
|
||||
window.GetObservable(Window.IsExtendedIntoWindowDecorationsProperty)
|
||||
.Subscribe(x => UpdateSize(window))
|
||||
.Subscribe(_ => UpdateSize(window))
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
base.OnDetachedFromVisualTree(e);
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using Avalonia.Automation.Peers;
|
||||
using Avalonia.Reactive;
|
||||
using Avalonia.Controls.Generators;
|
||||
using Avalonia.Controls.Mixins;
|
||||
using Avalonia.Controls.Presenters;
|
||||
using Avalonia.Controls.Metadata;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Controls.Selection;
|
||||
using Avalonia.Controls.Shapes;
|
||||
using Avalonia.Controls.Templates;
|
||||
using Avalonia.Controls.Utils;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Layout;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Reactive;
|
||||
using Avalonia.VisualTree;
|
||||
using Avalonia.Controls.Metadata;
|
||||
|
||||
namespace Avalonia.Controls
|
||||
{
|
||||
@@ -219,7 +219,7 @@ namespace Avalonia.Controls
|
||||
}
|
||||
else if (e.Key == Key.Up)
|
||||
{
|
||||
SelectPrev();
|
||||
SelectPrevious();
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
@@ -250,7 +250,7 @@ namespace Avalonia.Controls
|
||||
if (e.Delta.Y < 0)
|
||||
SelectNext();
|
||||
else
|
||||
SelectPrev();
|
||||
SelectPrevious();
|
||||
|
||||
e.Handled = true;
|
||||
}
|
||||
@@ -478,19 +478,40 @@ namespace Avalonia.Controls
|
||||
}
|
||||
}
|
||||
|
||||
private void SelectNext()
|
||||
{
|
||||
if (ItemCount >= 1)
|
||||
{
|
||||
MoveSelection(NavigationDirection.Next, WrapSelection);
|
||||
}
|
||||
}
|
||||
private void SelectNext() => MoveSelection(SelectedIndex, 1, WrapSelection);
|
||||
private void SelectPrevious() => MoveSelection(SelectedIndex, -1, WrapSelection);
|
||||
|
||||
private void SelectPrev()
|
||||
private void MoveSelection(int startIndex, int step, bool wrap)
|
||||
{
|
||||
if (ItemCount >= 1)
|
||||
static bool IsSelectable(object? o) => (o as AvaloniaObject)?.GetValue(IsEnabledProperty) ?? true;
|
||||
|
||||
var count = ItemCount;
|
||||
|
||||
for (int i = startIndex + step; i != startIndex; i += step)
|
||||
{
|
||||
MoveSelection(NavigationDirection.Previous, WrapSelection);
|
||||
if (i < 0 || i >= count)
|
||||
{
|
||||
if (wrap)
|
||||
{
|
||||
if (i < 0)
|
||||
i += count;
|
||||
else if (i >= count)
|
||||
i %= count;
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var item = ItemsView[i];
|
||||
var container = ContainerFromIndex(i);
|
||||
|
||||
if (IsSelectable(item) && IsSelectable(container))
|
||||
{
|
||||
SelectedIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,14 +2,12 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using Avalonia.Automation.Peers;
|
||||
using Avalonia.Controls.Documents;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Controls.Templates;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Input.Platform;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.LogicalTree;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Rendering;
|
||||
using Avalonia.Styling;
|
||||
using Avalonia.Threading;
|
||||
@@ -211,8 +209,6 @@ namespace Avalonia.Controls
|
||||
remove => RemoveHandler(SizeChangedEvent, value);
|
||||
}
|
||||
|
||||
public new Control? Parent => (Control?)base.Parent;
|
||||
|
||||
/// <inheritdoc/>
|
||||
bool IDataTemplateHost.IsDataTemplatesInitialized => _dataTemplates != null;
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Avalonia.Collections;
|
||||
|
||||
@@ -13,7 +14,7 @@ namespace Avalonia.Controls
|
||||
/// </summary>
|
||||
public Controls()
|
||||
{
|
||||
ResetBehavior = ResetBehavior.Remove;
|
||||
Configure();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -21,9 +22,22 @@ namespace Avalonia.Controls
|
||||
/// </summary>
|
||||
/// <param name="items">The initial items in the collection.</param>
|
||||
public Controls(IEnumerable<Control> items)
|
||||
: base(items)
|
||||
{
|
||||
Configure();
|
||||
AddRange(items); // virtual member call in ctor, ok for our current implementation
|
||||
}
|
||||
|
||||
private void Configure()
|
||||
{
|
||||
ResetBehavior = ResetBehavior.Remove;
|
||||
Validate = item =>
|
||||
{
|
||||
if (item is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(item),
|
||||
$"A null control cannot be added to a {nameof(Controls)} collection.");
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ namespace Avalonia.Controls.Converters
|
||||
public object? Convert(IList<object?> values, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
if (parameter == null ||
|
||||
values == null ||
|
||||
values.Count != 4 ||
|
||||
!(values[0] is ScrollBarVisibility visibility) ||
|
||||
!(values[1] is double offset) ||
|
||||
|
||||
@@ -21,9 +21,9 @@ namespace Avalonia.Controls
|
||||
/// <summary>
|
||||
/// SharedSizeGroup property.
|
||||
/// </summary>
|
||||
public string SharedSizeGroup
|
||||
public string? SharedSizeGroup
|
||||
{
|
||||
get { return (string)GetValue(SharedSizeGroupProperty); }
|
||||
get { return GetValue(SharedSizeGroupProperty); }
|
||||
set { SetValue(SharedSizeGroupProperty, value); }
|
||||
}
|
||||
|
||||
@@ -32,20 +32,15 @@ namespace Avalonia.Controls
|
||||
/// </summary>
|
||||
internal void OnEnterParentTree()
|
||||
{
|
||||
this.InheritanceParent = Parent;
|
||||
InheritanceParent = Parent;
|
||||
if (_sharedState == null)
|
||||
{
|
||||
// start with getting SharedSizeGroup value.
|
||||
// this property is NOT inherited which should result in better overall perf.
|
||||
string sharedSizeGroupId = SharedSizeGroup;
|
||||
if (sharedSizeGroupId != null)
|
||||
if (SharedSizeGroup is { } sharedSizeGroupId && PrivateSharedSizeScope is { } privateSharedSizeScope)
|
||||
{
|
||||
SharedSizeScope? privateSharedSizeScope = PrivateSharedSizeScope;
|
||||
if (privateSharedSizeScope != null)
|
||||
{
|
||||
_sharedState = privateSharedSizeScope.EnsureSharedState(sharedSizeGroupId);
|
||||
_sharedState.AddMember(this);
|
||||
}
|
||||
_sharedState = privateSharedSizeScope.EnsureSharedState(sharedSizeGroupId);
|
||||
_sharedState.AddMember(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -321,13 +316,12 @@ namespace Avalonia.Controls
|
||||
return ((_flags & flags) == flags);
|
||||
}
|
||||
|
||||
private static void OnSharedSizeGroupPropertyChanged(AvaloniaObject d, AvaloniaPropertyChangedEventArgs e)
|
||||
private static void OnSharedSizeGroupPropertyChanged(DefinitionBase definition,
|
||||
AvaloniaPropertyChangedEventArgs<string?> e)
|
||||
{
|
||||
DefinitionBase definition = (DefinitionBase)d;
|
||||
|
||||
if (definition.Parent != null)
|
||||
{
|
||||
string sharedSizeGroupId = (string)e.NewValue!;
|
||||
string? sharedSizeGroupId = e.NewValue.Value;
|
||||
|
||||
if (definition._sharedState != null)
|
||||
{
|
||||
@@ -337,16 +331,14 @@ namespace Avalonia.Controls
|
||||
definition._sharedState = null;
|
||||
}
|
||||
|
||||
if ((definition._sharedState == null) && (sharedSizeGroupId != null))
|
||||
if (definition._sharedState == null
|
||||
&& sharedSizeGroupId != null
|
||||
&& definition.PrivateSharedSizeScope is { } privateSharedSizeScope)
|
||||
{
|
||||
SharedSizeScope? privateSharedSizeScope = definition.PrivateSharedSizeScope;
|
||||
if (privateSharedSizeScope != null)
|
||||
{
|
||||
// if definition is not registered and both: shared size group id AND private shared scope
|
||||
// are available, then register definition.
|
||||
definition._sharedState = privateSharedSizeScope.EnsureSharedState(sharedSizeGroupId);
|
||||
definition._sharedState.AddMember(definition);
|
||||
}
|
||||
// if definition is not registered and both: shared size group id AND private shared scope
|
||||
// are available, then register definition.
|
||||
definition._sharedState = privateSharedSizeScope.EnsureSharedState(sharedSizeGroupId);
|
||||
definition._sharedState.AddMember(definition);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -357,17 +349,15 @@ namespace Avalonia.Controls
|
||||
/// b) contains only letters, digits and underscore ('_').
|
||||
/// c) does not start with a digit.
|
||||
/// </remarks>
|
||||
private static bool SharedSizeGroupPropertyValueValid(string value)
|
||||
private static bool SharedSizeGroupPropertyValueValid(string? id)
|
||||
{
|
||||
// null is default value
|
||||
if (value == null)
|
||||
if (id == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
string id = (string)value;
|
||||
|
||||
if (!string.IsNullOrEmpty(id))
|
||||
if (id.Length > 0)
|
||||
{
|
||||
int i = -1;
|
||||
while (++i < id.Length)
|
||||
@@ -397,14 +387,11 @@ namespace Avalonia.Controls
|
||||
/// existing scope just left. In both cases if the DefinitionBase object is already registered
|
||||
/// in SharedSizeState, it should un-register and register itself in a new one.
|
||||
/// </remark>
|
||||
private static void OnPrivateSharedSizeScopePropertyChanged(AvaloniaObject d, AvaloniaPropertyChangedEventArgs e)
|
||||
private static void OnPrivateSharedSizeScopePropertyChanged(DefinitionBase definition,
|
||||
AvaloniaPropertyChangedEventArgs<SharedSizeScope?> e)
|
||||
{
|
||||
DefinitionBase definition = (DefinitionBase)d;
|
||||
|
||||
if (definition.Parent != null)
|
||||
{
|
||||
SharedSizeScope privateSharedSizeScope = (SharedSizeScope)e.NewValue!;
|
||||
|
||||
if (definition._sharedState != null)
|
||||
{
|
||||
// if definition is already registered And shared size scope is changing,
|
||||
@@ -413,16 +400,14 @@ namespace Avalonia.Controls
|
||||
definition._sharedState = null;
|
||||
}
|
||||
|
||||
if ((definition._sharedState == null) && (privateSharedSizeScope != null))
|
||||
if (definition._sharedState == null
|
||||
&& e.NewValue.Value is { } privateSharedSizeScope
|
||||
&& definition.SharedSizeGroup is { } sharedSizeGroup)
|
||||
{
|
||||
string sharedSizeGroup = definition.SharedSizeGroup;
|
||||
if (sharedSizeGroup != null)
|
||||
{
|
||||
// if definition is not registered and both: shared size group id AND private shared scope
|
||||
// are available, then register definition.
|
||||
definition._sharedState = privateSharedSizeScope.EnsureSharedState(definition.SharedSizeGroup);
|
||||
definition._sharedState.AddMember(definition);
|
||||
}
|
||||
// if definition is not registered and both: shared size group id AND private shared scope
|
||||
// are available, then register definition.
|
||||
definition._sharedState = privateSharedSizeScope.EnsureSharedState(sharedSizeGroup);
|
||||
definition._sharedState.AddMember(definition);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -432,7 +417,7 @@ namespace Avalonia.Controls
|
||||
/// </summary>
|
||||
private SharedSizeScope? PrivateSharedSizeScope
|
||||
{
|
||||
get { return (SharedSizeScope?)GetValue(PrivateSharedSizeScopeProperty); }
|
||||
get { return GetValue(PrivateSharedSizeScopeProperty); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -465,7 +450,7 @@ namespace Avalonia.Controls
|
||||
|
||||
private SharedSizeState? _sharedState; // reference to shared state object this instance is registered with
|
||||
|
||||
[System.Flags]
|
||||
[Flags]
|
||||
private enum Flags : byte
|
||||
{
|
||||
//
|
||||
@@ -520,11 +505,10 @@ namespace Avalonia.Controls
|
||||
/// </summary>
|
||||
internal SharedSizeState(SharedSizeScope sharedSizeScope, string sharedSizeGroupId)
|
||||
{
|
||||
Debug.Assert(sharedSizeScope != null && sharedSizeGroupId != null);
|
||||
_sharedSizeScope = sharedSizeScope;
|
||||
_sharedSizeGroupId = sharedSizeGroupId;
|
||||
_registry = new List<DefinitionBase>();
|
||||
_layoutUpdated = new EventHandler(OnLayoutUpdated);
|
||||
_layoutUpdated = OnLayoutUpdated;
|
||||
_broadcastInvalidation = true;
|
||||
}
|
||||
|
||||
@@ -568,7 +552,7 @@ namespace Avalonia.Controls
|
||||
{
|
||||
for (int i = 0, count = _registry.Count; i < count; ++i)
|
||||
{
|
||||
Grid parentGrid = (Grid)(_registry[i].Parent!);
|
||||
Grid parentGrid = _registry[i].Parent!;
|
||||
parentGrid.Invalidate();
|
||||
}
|
||||
_broadcastInvalidation = false;
|
||||
@@ -703,7 +687,7 @@ namespace Avalonia.Controls
|
||||
// measure is invalid - it used the old shared size,
|
||||
// which is larger than d's (possibly changed) minSize
|
||||
measureIsValid = (definitionBase.LayoutWasUpdated &&
|
||||
MathUtilities.GreaterThanOrClose(definitionBase._minSize, this.MinSize));
|
||||
MathUtilities.GreaterThanOrClose(definitionBase._minSize, MinSize));
|
||||
}
|
||||
|
||||
if(!measureIsValid)
|
||||
@@ -786,8 +770,8 @@ namespace Avalonia.Controls
|
||||
/// </description></item>
|
||||
/// </list>
|
||||
/// </remarks>
|
||||
public static readonly AttachedProperty<string> SharedSizeGroupProperty =
|
||||
AvaloniaProperty.RegisterAttached<DefinitionBase, Control, string>(
|
||||
public static readonly AttachedProperty<string?> SharedSizeGroupProperty =
|
||||
AvaloniaProperty.RegisterAttached<DefinitionBase, Control, string?>(
|
||||
"SharedSizeGroup",
|
||||
validate: SharedSizeGroupPropertyValueValid);
|
||||
|
||||
@@ -796,8 +780,8 @@ namespace Avalonia.Controls
|
||||
/// </summary>
|
||||
static DefinitionBase()
|
||||
{
|
||||
SharedSizeGroupProperty.Changed.AddClassHandler<DefinitionBase>(OnSharedSizeGroupPropertyChanged);
|
||||
PrivateSharedSizeScopeProperty.Changed.AddClassHandler<DefinitionBase>(OnPrivateSharedSizeScopePropertyChanged);
|
||||
SharedSizeGroupProperty.Changed.AddClassHandler<DefinitionBase, string?>(OnSharedSizeGroupPropertyChanged);
|
||||
PrivateSharedSizeScopeProperty.Changed.AddClassHandler<DefinitionBase, SharedSizeScope?>(OnPrivateSharedSizeScopePropertyChanged);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -101,9 +101,6 @@ namespace Avalonia.Controls
|
||||
Size childConstraint; // Contains the suggested input constraint for this child.
|
||||
Size childDesiredSize; // Contains the return size from child measure.
|
||||
|
||||
if (child == null)
|
||||
{ continue; }
|
||||
|
||||
// Child constraint is the remaining size; this is total size minus size consumed by previous children.
|
||||
childConstraint = new Size(Math.Max(0.0, constraint.Width - accumulatedWidth),
|
||||
Math.Max(0.0, constraint.Height - accumulatedHeight));
|
||||
@@ -122,7 +119,7 @@ namespace Avalonia.Controls
|
||||
// will deal with computing our minimum size (parentSize) due to that accumulation.
|
||||
// Therefore, we only need to compute our minimum size (parentSize) in dimensions that this child does
|
||||
// not accumulate: Width for Top/Bottom, Height for Left/Right.
|
||||
switch (DockPanel.GetDock((Control)child))
|
||||
switch (GetDock(child))
|
||||
{
|
||||
case Dock.Left:
|
||||
case Dock.Right:
|
||||
@@ -164,8 +161,6 @@ namespace Avalonia.Controls
|
||||
for (int i = 0; i < totalChildrenCount; ++i)
|
||||
{
|
||||
var child = children[i];
|
||||
if (child == null)
|
||||
{ continue; }
|
||||
|
||||
Size childDesiredSize = child.DesiredSize;
|
||||
Rect rcChild = new Rect(
|
||||
@@ -176,7 +171,7 @@ namespace Avalonia.Controls
|
||||
|
||||
if (i < nonFillChildrenCount)
|
||||
{
|
||||
switch (DockPanel.GetDock((Control)child))
|
||||
switch (GetDock(child))
|
||||
{
|
||||
case Dock.Left:
|
||||
accumulatedLeft += childDesiredSize.Width;
|
||||
|
||||
@@ -13,8 +13,8 @@ namespace Avalonia.Controls.Documents
|
||||
/// <summary>
|
||||
/// AvaloniaProperty for <see cref="TextDecorations" /> property.
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<TextDecorationCollection> TextDecorationsProperty =
|
||||
AvaloniaProperty.Register<Inline, TextDecorationCollection>(
|
||||
public static readonly StyledProperty<TextDecorationCollection?> TextDecorationsProperty =
|
||||
AvaloniaProperty.Register<Inline, TextDecorationCollection?>(
|
||||
nameof(TextDecorations));
|
||||
|
||||
/// <summary>
|
||||
@@ -28,7 +28,7 @@ namespace Avalonia.Controls.Documents
|
||||
/// <summary>
|
||||
/// The TextDecorations property specifies decorations that are added to the text of an element.
|
||||
/// </summary>
|
||||
public TextDecorationCollection TextDecorations
|
||||
public TextDecorationCollection? TextDecorations
|
||||
{
|
||||
get { return GetValue(TextDecorationsProperty); }
|
||||
set { SetValue(TextDecorationsProperty, value); }
|
||||
@@ -83,7 +83,8 @@ namespace Avalonia.Controls.Documents
|
||||
return new GenericTextRunProperties(new Typeface(FontFamily, fontStyle, fontWeight), FontSize,
|
||||
textDecorations, Foreground, background, BaselineAlignment);
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
|
||||
{
|
||||
base.OnPropertyChanged(change);
|
||||
|
||||
@@ -64,5 +64,23 @@ namespace Avalonia.Controls.Documents
|
||||
internal override void AppendText(StringBuilder stringBuilder)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
|
||||
{
|
||||
base.OnPropertyChanged(change);
|
||||
|
||||
if (change.Property == ChildProperty)
|
||||
{
|
||||
if(change.OldValue is Control oldChild)
|
||||
{
|
||||
LogicalChildren.Remove(oldChild);
|
||||
}
|
||||
|
||||
if(change.NewValue is Control newChild)
|
||||
{
|
||||
LogicalChildren.Add(newChild);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Avalonia.Media.TextFormatting;
|
||||
@@ -51,6 +52,7 @@ namespace Avalonia.Controls.Documents
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
|
||||
{
|
||||
base.OnPropertyChanged(change);
|
||||
@@ -68,26 +70,26 @@ namespace Avalonia.Controls.Documents
|
||||
{
|
||||
base.OnInlineHostChanged(oldValue, newValue);
|
||||
|
||||
if (Inlines is not null)
|
||||
{
|
||||
Inlines.InlineHost = newValue;
|
||||
}
|
||||
Inlines.InlineHost = newValue;
|
||||
}
|
||||
|
||||
private void OnInlinesChanged(InlineCollection? oldValue, InlineCollection? newValue)
|
||||
{
|
||||
void OnInlinesInvalidated(object? sender, EventArgs e)
|
||||
=> InlineHost?.Invalidate();
|
||||
|
||||
if (oldValue is not null)
|
||||
{
|
||||
oldValue.LogicalChildren = null;
|
||||
oldValue.InlineHost = null;
|
||||
oldValue.Invalidated -= (s, e) => InlineHost?.Invalidate();
|
||||
oldValue.Invalidated -= OnInlinesInvalidated;
|
||||
}
|
||||
|
||||
if (newValue is not null)
|
||||
{
|
||||
newValue.LogicalChildren = LogicalChildren;
|
||||
newValue.InlineHost = InlineHost;
|
||||
newValue.Invalidated += (s, e) => InlineHost?.Invalidate();
|
||||
newValue.Invalidated += OnInlinesInvalidated;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+109
-127
@@ -164,20 +164,21 @@ namespace Avalonia.Controls
|
||||
/// <summary>
|
||||
/// Returns a ColumnDefinitions of column definitions.
|
||||
/// </summary>
|
||||
[MemberNotNull(nameof(_extData))]
|
||||
public ColumnDefinitions ColumnDefinitions
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_data == null) { _data = new ExtendedData(); }
|
||||
if (_data.ColumnDefinitions == null) { _data.ColumnDefinitions = new ColumnDefinitions() { Parent = this }; }
|
||||
if (_extData == null) { _extData = new ExtendedData(); }
|
||||
if (_extData.ColumnDefinitions == null) { _extData.ColumnDefinitions = new ColumnDefinitions() { Parent = this }; }
|
||||
|
||||
return (_data.ColumnDefinitions);
|
||||
return (_extData.ColumnDefinitions);
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_data == null) { _data = new ExtendedData(); }
|
||||
_data.ColumnDefinitions = value;
|
||||
_data.ColumnDefinitions.Parent = this;
|
||||
if (_extData == null) { _extData = new ExtendedData(); }
|
||||
_extData.ColumnDefinitions = value;
|
||||
_extData.ColumnDefinitions.Parent = this;
|
||||
InvalidateMeasure();
|
||||
}
|
||||
}
|
||||
@@ -185,20 +186,21 @@ namespace Avalonia.Controls
|
||||
/// <summary>
|
||||
/// Returns a RowDefinitions of row definitions.
|
||||
/// </summary>
|
||||
[MemberNotNull(nameof(_extData))]
|
||||
public RowDefinitions RowDefinitions
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_data == null) { _data = new ExtendedData(); }
|
||||
if (_data.RowDefinitions == null) { _data.RowDefinitions = new RowDefinitions() { Parent = this }; }
|
||||
if (_extData == null) { _extData = new ExtendedData(); }
|
||||
if (_extData.RowDefinitions == null) { _extData.RowDefinitions = new RowDefinitions() { Parent = this }; }
|
||||
|
||||
return (_data.RowDefinitions);
|
||||
return (_extData.RowDefinitions);
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_data == null) { _data = new ExtendedData(); }
|
||||
_data.RowDefinitions = value;
|
||||
_data.RowDefinitions.Parent = this;
|
||||
if (_extData == null) { _extData = new ExtendedData(); }
|
||||
_extData.RowDefinitions = value;
|
||||
_extData.RowDefinitions.Parent = this;
|
||||
InvalidateMeasure();
|
||||
}
|
||||
}
|
||||
@@ -211,7 +213,7 @@ namespace Avalonia.Controls
|
||||
protected override Size MeasureOverride(Size constraint)
|
||||
{
|
||||
Size gridDesiredSize;
|
||||
ExtendedData extData = ExtData;
|
||||
var extData = _extData;
|
||||
|
||||
try
|
||||
{
|
||||
@@ -221,17 +223,14 @@ namespace Avalonia.Controls
|
||||
if (extData == null)
|
||||
{
|
||||
gridDesiredSize = new Size();
|
||||
var children = this.Children;
|
||||
var children = Children;
|
||||
|
||||
for (int i = 0, count = children.Count; i < count; ++i)
|
||||
{
|
||||
var child = children[i];
|
||||
if (child != null)
|
||||
{
|
||||
child.Measure(constraint);
|
||||
gridDesiredSize = new Size(Math.Max(gridDesiredSize.Width, child.DesiredSize.Width),
|
||||
Math.Max(gridDesiredSize.Height, child.DesiredSize.Height));
|
||||
}
|
||||
child.Measure(constraint);
|
||||
gridDesiredSize = new Size(Math.Max(gridDesiredSize.Width, child.DesiredSize.Width),
|
||||
Math.Max(gridDesiredSize.Height, child.DesiredSize.Height));
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -512,17 +511,14 @@ namespace Avalonia.Controls
|
||||
{
|
||||
ArrangeOverrideInProgress = true;
|
||||
|
||||
if (_data == null)
|
||||
if (_extData is null)
|
||||
{
|
||||
var children = this.Children;
|
||||
var children = Children;
|
||||
|
||||
for (int i = 0, count = children.Count; i < count; ++i)
|
||||
{
|
||||
var child = children[i];
|
||||
if (child != null)
|
||||
{
|
||||
child.Arrange(new Rect(arrangeSize));
|
||||
}
|
||||
child.Arrange(new Rect(arrangeSize));
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -532,15 +528,11 @@ namespace Avalonia.Controls
|
||||
SetFinalSize(DefinitionsU, arrangeSize.Width, true);
|
||||
SetFinalSize(DefinitionsV, arrangeSize.Height, false);
|
||||
|
||||
var children = this.Children;
|
||||
var children = Children;
|
||||
|
||||
for (int currentCell = 0; currentCell < PrivateCells.Length; ++currentCell)
|
||||
{
|
||||
var cell = children[currentCell];
|
||||
if (cell == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int columnIndex = PrivateCells[currentCell].ColumnIndex;
|
||||
int rowIndex = PrivateCells[currentCell].RowIndex;
|
||||
@@ -599,7 +591,7 @@ namespace Avalonia.Controls
|
||||
{
|
||||
double value = 0.0;
|
||||
|
||||
Debug.Assert(_data != null);
|
||||
Debug.Assert(_extData != null);
|
||||
|
||||
// actual value calculations require structure to be up-to-date
|
||||
if (!ColumnDefinitionsDirty)
|
||||
@@ -621,7 +613,7 @@ namespace Avalonia.Controls
|
||||
{
|
||||
double value = 0.0;
|
||||
|
||||
Debug.Assert(_data != null);
|
||||
Debug.Assert(_extData != null);
|
||||
|
||||
// actual value calculations require structure to be up-to-date
|
||||
if (!RowDefinitionsDirty)
|
||||
@@ -654,18 +646,20 @@ namespace Avalonia.Controls
|
||||
/// <summary>
|
||||
/// Convenience accessor to ValidDefinitionsUStructure bit flag.
|
||||
/// </summary>
|
||||
[MemberNotNull(nameof(_extData))]
|
||||
internal bool ColumnDefinitionsDirty
|
||||
{
|
||||
get => ColumnDefinitions?.IsDirty ?? false;
|
||||
get => ColumnDefinitions.IsDirty;
|
||||
set => ColumnDefinitions.IsDirty = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convenience accessor to ValidDefinitionsVStructure bit flag.
|
||||
/// </summary>
|
||||
[MemberNotNull(nameof(_extData))]
|
||||
internal bool RowDefinitionsDirty
|
||||
{
|
||||
get => RowDefinitions?.IsDirty ?? false;
|
||||
get => RowDefinitions.IsDirty;
|
||||
set => RowDefinitions.IsDirty = value;
|
||||
}
|
||||
|
||||
@@ -686,8 +680,10 @@ namespace Avalonia.Controls
|
||||
/// </summary>
|
||||
private void ValidateCellsCore()
|
||||
{
|
||||
var children = this.Children;
|
||||
ExtendedData extData = ExtData;
|
||||
Debug.Assert(_extData is not null);
|
||||
|
||||
var children = Children;
|
||||
var extData = _extData!;
|
||||
|
||||
extData.CellCachesCollection = new CellCache[children.Count];
|
||||
extData.CellGroup1 = int.MaxValue;
|
||||
@@ -702,10 +698,6 @@ namespace Avalonia.Controls
|
||||
for (int i = PrivateCells.Length - 1; i >= 0; --i)
|
||||
{
|
||||
var child = children[i];
|
||||
if (child == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
CellCache cell = new CellCache();
|
||||
|
||||
@@ -713,19 +705,19 @@ namespace Avalonia.Controls
|
||||
// Read indices from the corresponding properties:
|
||||
// clamp to value < number_of_columns
|
||||
// column >= 0 is guaranteed by property value validation callback
|
||||
cell.ColumnIndex = Math.Min(GetColumn((Control)child), DefinitionsU.Count - 1);
|
||||
cell.ColumnIndex = Math.Min(GetColumn(child), DefinitionsU.Count - 1);
|
||||
// clamp to value < number_of_rows
|
||||
// row >= 0 is guaranteed by property value validation callback
|
||||
cell.RowIndex = Math.Min(GetRow((Control)child), DefinitionsV.Count - 1);
|
||||
cell.RowIndex = Math.Min(GetRow(child), DefinitionsV.Count - 1);
|
||||
|
||||
// Read span properties:
|
||||
// clamp to not exceed beyond right side of the grid
|
||||
// column_span > 0 is guaranteed by property value validation callback
|
||||
cell.ColumnSpan = Math.Min(GetColumnSpan((Control)child), DefinitionsU.Count - cell.ColumnIndex);
|
||||
cell.ColumnSpan = Math.Min(GetColumnSpan(child), DefinitionsU.Count - cell.ColumnIndex);
|
||||
|
||||
// clamp to not exceed beyond bottom side of the grid
|
||||
// row_span > 0 is guaranteed by property value validation callback
|
||||
cell.RowSpan = Math.Min(GetRowSpan((Control)child), DefinitionsV.Count - cell.RowIndex);
|
||||
cell.RowSpan = Math.Min(GetRowSpan(child), DefinitionsV.Count - cell.RowIndex);
|
||||
|
||||
Debug.Assert(0 <= cell.ColumnIndex && cell.ColumnIndex < DefinitionsU.Count);
|
||||
Debug.Assert(0 <= cell.RowIndex && cell.RowIndex < DefinitionsV.Count);
|
||||
@@ -792,7 +784,7 @@ namespace Avalonia.Controls
|
||||
{
|
||||
if (ColumnDefinitionsDirty)
|
||||
{
|
||||
ExtendedData extData = ExtData;
|
||||
var extData = _extData;
|
||||
|
||||
if (extData.ColumnDefinitions == null)
|
||||
{
|
||||
@@ -818,7 +810,7 @@ namespace Avalonia.Controls
|
||||
ColumnDefinitionsDirty = false;
|
||||
}
|
||||
|
||||
Debug.Assert(ExtData.DefinitionsU != null && ExtData.DefinitionsU.Count > 0);
|
||||
Debug.Assert(_extData is { DefinitionsU.Count: > 0 });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -833,7 +825,7 @@ namespace Avalonia.Controls
|
||||
{
|
||||
if (RowDefinitionsDirty)
|
||||
{
|
||||
ExtendedData extData = ExtData;
|
||||
var extData = _extData;
|
||||
|
||||
if (extData.RowDefinitions == null)
|
||||
{
|
||||
@@ -859,7 +851,7 @@ namespace Avalonia.Controls
|
||||
RowDefinitionsDirty = false;
|
||||
}
|
||||
|
||||
Debug.Assert(ExtData.DefinitionsV != null && ExtData.DefinitionsV.Count > 0);
|
||||
Debug.Assert(_extData is { DefinitionsV.Count: > 0 });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -965,8 +957,7 @@ namespace Avalonia.Controls
|
||||
bool ignoreDesiredSizeU,
|
||||
bool forceInfinityV)
|
||||
{
|
||||
bool unusedHasDesiredSizeUChanged;
|
||||
MeasureCellsGroup(cellsHead, referenceSize, ignoreDesiredSizeU, forceInfinityV, out unusedHasDesiredSizeUChanged);
|
||||
MeasureCellsGroup(cellsHead, referenceSize, ignoreDesiredSizeU, forceInfinityV, out _);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -994,7 +985,7 @@ namespace Avalonia.Controls
|
||||
return;
|
||||
}
|
||||
|
||||
var children = this.Children;
|
||||
var children = Children;
|
||||
Hashtable? spanStore = null;
|
||||
bool ignoreDesiredSizeV = forceInfinityV;
|
||||
|
||||
@@ -1101,8 +1092,6 @@ namespace Avalonia.Controls
|
||||
int cell,
|
||||
bool forceInfinityV)
|
||||
{
|
||||
|
||||
|
||||
double cellMeasureWidth;
|
||||
double cellMeasureHeight;
|
||||
|
||||
@@ -1144,15 +1133,9 @@ namespace Avalonia.Controls
|
||||
}
|
||||
|
||||
|
||||
var child = this.Children[cell];
|
||||
if (child != null)
|
||||
{
|
||||
Size childConstraint = new Size(cellMeasureWidth, cellMeasureHeight);
|
||||
child.Measure(childConstraint);
|
||||
}
|
||||
|
||||
|
||||
|
||||
var child = Children[cell];
|
||||
Size childConstraint = new Size(cellMeasureWidth, cellMeasureHeight);
|
||||
child.Measure(childConstraint);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1230,7 +1213,7 @@ namespace Avalonia.Controls
|
||||
// avoid processing when asked to distribute "0"
|
||||
if (!MathUtilities.IsZero(requestedSize))
|
||||
{
|
||||
DefinitionBase[] tempDefinitions = TempDefinitions; // temp array used to remember definitions for sorting
|
||||
DefinitionBase?[] tempDefinitions = TempDefinitions; // temp array used to remember definitions for sorting
|
||||
int end = start + count;
|
||||
int autoDefinitionsCount = 0;
|
||||
double rangeMinSize = 0;
|
||||
@@ -1288,20 +1271,24 @@ namespace Avalonia.Controls
|
||||
Array.Sort(tempDefinitions, 0, count, s_spanPreferredDistributionOrderComparer);
|
||||
for (i = 0, sizeToDistribute = requestedSize; i < autoDefinitionsCount; ++i)
|
||||
{
|
||||
var tempDefinition = tempDefinitions[i]!;
|
||||
|
||||
// sanity check: only auto definitions allowed in this loop
|
||||
Debug.Assert(tempDefinitions[i].UserSize.IsAuto);
|
||||
Debug.Assert(tempDefinition.UserSize.IsAuto);
|
||||
|
||||
// adjust sizeToDistribute value by subtracting auto definition min size
|
||||
sizeToDistribute -= (tempDefinitions[i].MinSize);
|
||||
sizeToDistribute -= (tempDefinition.MinSize);
|
||||
}
|
||||
|
||||
for (; i < count; ++i)
|
||||
{
|
||||
// sanity check: no auto definitions allowed in this loop
|
||||
Debug.Assert(!tempDefinitions[i].UserSize.IsAuto);
|
||||
var tempDefinition = tempDefinitions[i]!;
|
||||
|
||||
double newMinSize = Math.Min(sizeToDistribute / (count - i), tempDefinitions[i].PreferredSize);
|
||||
if (newMinSize > tempDefinitions[i].MinSize) { tempDefinitions[i].UpdateMinSize(newMinSize); }
|
||||
// sanity check: no auto definitions allowed in this loop
|
||||
Debug.Assert(!tempDefinition.UserSize.IsAuto);
|
||||
|
||||
double newMinSize = Math.Min(sizeToDistribute / (count - i), tempDefinition.PreferredSize);
|
||||
if (newMinSize > tempDefinition.MinSize) { tempDefinition.UpdateMinSize(newMinSize); }
|
||||
sizeToDistribute -= newMinSize;
|
||||
}
|
||||
|
||||
@@ -1325,24 +1312,28 @@ namespace Avalonia.Controls
|
||||
Array.Sort(tempDefinitions, 0, count, s_spanMaxDistributionOrderComparer);
|
||||
for (i = 0, sizeToDistribute = requestedSize - rangePreferredSize; i < count - autoDefinitionsCount; ++i)
|
||||
{
|
||||
// sanity check: no auto definitions allowed in this loop
|
||||
Debug.Assert(!tempDefinitions[i].UserSize.IsAuto);
|
||||
var tempDefinition = tempDefinitions[i]!;
|
||||
|
||||
double preferredSize = tempDefinitions[i].PreferredSize;
|
||||
// sanity check: no auto definitions allowed in this loop
|
||||
Debug.Assert(!tempDefinition.UserSize.IsAuto);
|
||||
|
||||
double preferredSize = tempDefinition.PreferredSize;
|
||||
double newMinSize = preferredSize + sizeToDistribute / (count - autoDefinitionsCount - i);
|
||||
tempDefinitions[i].UpdateMinSize(Math.Min(newMinSize, tempDefinitions[i].SizeCache));
|
||||
sizeToDistribute -= (tempDefinitions[i].MinSize - preferredSize);
|
||||
tempDefinition.UpdateMinSize(Math.Min(newMinSize, tempDefinition.SizeCache));
|
||||
sizeToDistribute -= (tempDefinition.MinSize - preferredSize);
|
||||
}
|
||||
|
||||
for (; i < count; ++i)
|
||||
{
|
||||
// sanity check: only auto definitions allowed in this loop
|
||||
Debug.Assert(tempDefinitions[i].UserSize.IsAuto);
|
||||
var tempDefinition = tempDefinitions[i]!;
|
||||
|
||||
double preferredSize = tempDefinitions[i].MinSize;
|
||||
// sanity check: only auto definitions allowed in this loop
|
||||
Debug.Assert(tempDefinition.UserSize.IsAuto);
|
||||
|
||||
double preferredSize = tempDefinition.MinSize;
|
||||
double newMinSize = preferredSize + sizeToDistribute / (count - i);
|
||||
tempDefinitions[i].UpdateMinSize(Math.Min(newMinSize, tempDefinitions[i].SizeCache));
|
||||
sizeToDistribute -= (tempDefinitions[i].MinSize - preferredSize);
|
||||
tempDefinition.UpdateMinSize(Math.Min(newMinSize, tempDefinition.SizeCache));
|
||||
sizeToDistribute -= (tempDefinition.MinSize - preferredSize);
|
||||
}
|
||||
|
||||
// sanity check: requested size must all be distributed
|
||||
@@ -1376,8 +1367,10 @@ namespace Avalonia.Controls
|
||||
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
double deltaSize = (maxMaxSize - tempDefinitions[i].SizeCache) * sizeToDistribute / totalRemainingSize;
|
||||
tempDefinitions[i].UpdateMinSize(tempDefinitions[i].SizeCache + deltaSize);
|
||||
var tempDefinition = tempDefinitions[i]!;
|
||||
|
||||
double deltaSize = (maxMaxSize - tempDefinition.SizeCache) * sizeToDistribute / totalRemainingSize;
|
||||
tempDefinition.UpdateMinSize(tempDefinition.SizeCache + deltaSize);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -1388,7 +1381,7 @@ namespace Avalonia.Controls
|
||||
//
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
tempDefinitions[i].UpdateMinSize(equalSize);
|
||||
tempDefinitions[i]!.UpdateMinSize(equalSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1429,7 +1422,7 @@ namespace Avalonia.Controls
|
||||
double availableSize)
|
||||
{
|
||||
int defCount = definitions.Count;
|
||||
DefinitionBase[] tempDefinitions = TempDefinitions;
|
||||
DefinitionBase?[] tempDefinitions = TempDefinitions;
|
||||
int minCount = 0, maxCount = 0;
|
||||
double takenSize = 0;
|
||||
double totalStarWeight = 0.0;
|
||||
@@ -1560,8 +1553,8 @@ namespace Avalonia.Controls
|
||||
remainingStarWeight = totalStarWeight - takenStarWeight;
|
||||
}
|
||||
|
||||
double minRatio = (minCount > 0) ? tempDefinitions[minCount - 1].MeasureSize : Double.PositiveInfinity;
|
||||
double maxRatio = (maxCount > 0) ? tempDefinitions[defCount + maxCount - 1].SizeCache : -1.0;
|
||||
double minRatio = (minCount > 0) ? tempDefinitions[minCount - 1]!.MeasureSize : Double.PositiveInfinity;
|
||||
double maxRatio = (maxCount > 0) ? tempDefinitions[defCount + maxCount - 1]!.SizeCache : -1.0;
|
||||
|
||||
// choose the def with larger ratio to the current proportion ("max discrepancy")
|
||||
double proportion = remainingStarWeight / remainingAvailableSize;
|
||||
@@ -1579,13 +1572,13 @@ namespace Avalonia.Controls
|
||||
double resolvedSize;
|
||||
if (chooseMin == true)
|
||||
{
|
||||
resolvedDef = tempDefinitions[minCount - 1];
|
||||
resolvedDef = tempDefinitions[minCount - 1]!;
|
||||
resolvedSize = resolvedDef.MinSize;
|
||||
--minCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
resolvedDef = tempDefinitions[defCount + maxCount - 1];
|
||||
resolvedDef = tempDefinitions[defCount + maxCount - 1]!;
|
||||
resolvedSize = Math.Max(resolvedDef.MinSize, resolvedDef.UserMaxSize);
|
||||
--maxCount;
|
||||
}
|
||||
@@ -1603,12 +1596,12 @@ namespace Avalonia.Controls
|
||||
|
||||
// advance to the next candidate defs, removing ones that have been resolved.
|
||||
// Both counts are advanced, as a def might appear in both lists.
|
||||
while (minCount > 0 && tempDefinitions[minCount - 1].MeasureSize < 0.0)
|
||||
while (minCount > 0 && tempDefinitions[minCount - 1]!.MeasureSize < 0.0)
|
||||
{
|
||||
--minCount;
|
||||
tempDefinitions[minCount] = null!;
|
||||
}
|
||||
while (maxCount > 0 && tempDefinitions[defCount + maxCount - 1].MeasureSize < 0.0)
|
||||
while (maxCount > 0 && tempDefinitions[defCount + maxCount - 1]!.MeasureSize < 0.0)
|
||||
{
|
||||
--maxCount;
|
||||
tempDefinitions[defCount + maxCount] = null!;
|
||||
@@ -1637,8 +1630,7 @@ namespace Avalonia.Controls
|
||||
// resolved as 'min'. Their allocation can be increased to make up the gap.
|
||||
for (int i = minCount; i < minCountPhase2; ++i)
|
||||
{
|
||||
DefinitionBase def = tempDefinitions[i];
|
||||
if (def != null)
|
||||
if (tempDefinitions[i] is { } def)
|
||||
{
|
||||
def.MeasureSize = 1.0; // mark as 'not yet resolved'
|
||||
++starCount;
|
||||
@@ -1653,8 +1645,7 @@ namespace Avalonia.Controls
|
||||
// resolved as 'max'. Their allocation can be decreased to make up the gap.
|
||||
for (int i = maxCount; i < maxCountPhase2; ++i)
|
||||
{
|
||||
DefinitionBase def = tempDefinitions[defCount + i];
|
||||
if (def != null)
|
||||
if (tempDefinitions[defCount + i] is { } def)
|
||||
{
|
||||
def.MeasureSize = 1.0; // mark as 'not yet resolved'
|
||||
++starCount;
|
||||
@@ -1695,7 +1686,7 @@ namespace Avalonia.Controls
|
||||
totalStarWeight = 0.0;
|
||||
for (int i = 0; i < starCount; ++i)
|
||||
{
|
||||
DefinitionBase def = tempDefinitions[i];
|
||||
DefinitionBase def = tempDefinitions[i]!;
|
||||
totalStarWeight += def.MeasureSize;
|
||||
def.SizeCache = totalStarWeight;
|
||||
}
|
||||
@@ -1703,7 +1694,7 @@ namespace Avalonia.Controls
|
||||
// resolve the defs, in decreasing order of weight
|
||||
for (int i = starCount - 1; i >= 0; --i)
|
||||
{
|
||||
DefinitionBase def = tempDefinitions[i];
|
||||
DefinitionBase def = tempDefinitions[i]!;
|
||||
double resolvedSize = (def.MeasureSize > 0.0) ? Math.Max(availableSize - takenSize, 0.0) * (def.MeasureSize / def.SizeCache) : 0.0;
|
||||
|
||||
// min and max should have no effect by now, but just in case...
|
||||
@@ -2095,7 +2086,7 @@ namespace Avalonia.Controls
|
||||
{
|
||||
// DpiScale dpiScale = GetDpi();
|
||||
// double dpi = columns ? dpiScale.DpiScaleX : dpiScale.DpiScaleY;
|
||||
var dpi = (VisualRoot as Layout.ILayoutRoot)?.LayoutScaling ?? 1.0;
|
||||
var dpi = (VisualRoot as ILayoutRoot)?.LayoutScaling ?? 1.0;
|
||||
double[] roundingErrors = RoundingErrors;
|
||||
double roundedTakenSize = 0.0;
|
||||
|
||||
@@ -2302,8 +2293,7 @@ namespace Avalonia.Controls
|
||||
/// </summary>
|
||||
private void SetValid()
|
||||
{
|
||||
ExtendedData extData = ExtData;
|
||||
if (extData != null)
|
||||
if (_extData is { } extData)
|
||||
{
|
||||
// for (int i = 0; i < PrivateColumnCount; ++i) DefinitionsU[i].SetValid ();
|
||||
// for (int i = 0; i < PrivateRowCount; ++i) DefinitionsV[i].SetValid ();
|
||||
@@ -2330,12 +2320,12 @@ namespace Avalonia.Controls
|
||||
if (ShowGridLines && (_gridLinesRenderer == null))
|
||||
{
|
||||
_gridLinesRenderer = new GridLinesRenderer();
|
||||
this.VisualChildren.Add(_gridLinesRenderer);
|
||||
VisualChildren.Add(_gridLinesRenderer);
|
||||
}
|
||||
|
||||
if ((!ShowGridLines) && (_gridLinesRenderer != null))
|
||||
{
|
||||
this.VisualChildren.Add(_gridLinesRenderer);
|
||||
VisualChildren.Add(_gridLinesRenderer);
|
||||
_gridLinesRenderer = null;
|
||||
}
|
||||
|
||||
@@ -2364,7 +2354,7 @@ namespace Avalonia.Controls
|
||||
{
|
||||
Grid grid = (Grid)d;
|
||||
|
||||
if (grid.ExtData != null // trivial grid is 1 by 1. there is no grid lines anyway
|
||||
if (grid._extData != null // trivial grid is 1 by 1. there is no grid lines anyway
|
||||
&& grid.ListenToNotifications)
|
||||
{
|
||||
grid.InvalidateVisual();
|
||||
@@ -2375,13 +2365,11 @@ namespace Avalonia.Controls
|
||||
|
||||
private static void OnCellAttachedPropertyChanged(AvaloniaObject d, AvaloniaPropertyChangedEventArgs e)
|
||||
{
|
||||
Visual? child = d as Visual;
|
||||
|
||||
if (child != null)
|
||||
if (d is Visual child)
|
||||
{
|
||||
Grid? grid = child.GetVisualParent<Grid>();
|
||||
if (grid != null
|
||||
&& grid.ExtData != null
|
||||
&& grid._extData != null
|
||||
&& grid.ListenToNotifications)
|
||||
{
|
||||
grid.CellsStructureDirty = true;
|
||||
@@ -2427,7 +2415,7 @@ namespace Avalonia.Controls
|
||||
/// </summary>
|
||||
private IReadOnlyList<DefinitionBase> DefinitionsU
|
||||
{
|
||||
get { return (ExtData.DefinitionsU!); }
|
||||
get { return _extData!.DefinitionsU!; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -2435,17 +2423,19 @@ namespace Avalonia.Controls
|
||||
/// </summary>
|
||||
private IReadOnlyList<DefinitionBase> DefinitionsV
|
||||
{
|
||||
get { return (ExtData.DefinitionsV!); }
|
||||
get { return _extData!.DefinitionsV!; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper accessor to layout time array of definitions.
|
||||
/// </summary>
|
||||
private DefinitionBase[] TempDefinitions
|
||||
private DefinitionBase?[] TempDefinitions
|
||||
{
|
||||
get
|
||||
{
|
||||
ExtendedData extData = ExtData;
|
||||
Debug.Assert(_extData is not null);
|
||||
|
||||
var extData = _extData!;
|
||||
int requiredLength = Math.Max(DefinitionsU.Count, DefinitionsV.Count) * 2;
|
||||
|
||||
if (extData.TempDefinitions == null
|
||||
@@ -2516,7 +2506,7 @@ namespace Avalonia.Controls
|
||||
/// </summary>
|
||||
private CellCache[] PrivateCells
|
||||
{
|
||||
get { return (ExtData.CellCachesCollection!); }
|
||||
get { return _extData!.CellCachesCollection!; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -2582,18 +2572,10 @@ namespace Avalonia.Controls
|
||||
set { SetFlags(value, Flags.HasGroup3CellsInAutoRows); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns reference to extended data bag.
|
||||
/// </summary>
|
||||
private ExtendedData ExtData
|
||||
{
|
||||
get { return (_data!); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns *-weight, adjusted for scale computed during Phase 1
|
||||
/// </summary>
|
||||
static double StarWeight(DefinitionBase def, double scale)
|
||||
private static double StarWeight(DefinitionBase def, double scale)
|
||||
{
|
||||
if (scale < 0.0)
|
||||
{
|
||||
@@ -2609,17 +2591,17 @@ namespace Avalonia.Controls
|
||||
}
|
||||
|
||||
// Extended data instantiated on demand, for non-trivial case handling only
|
||||
private ExtendedData? _data;
|
||||
private ExtendedData? _extData;
|
||||
|
||||
// Grid validity / property caches dirtiness flags
|
||||
private Flags _flags;
|
||||
private GridLinesRenderer? _gridLinesRenderer;
|
||||
|
||||
// Keeps track of definition indices.
|
||||
int[]? _definitionIndices;
|
||||
private int[]? _definitionIndices;
|
||||
|
||||
// Stores unrounded values and rounding errors during layout rounding.
|
||||
double[]? _roundingErrors;
|
||||
private double[]? _roundingErrors;
|
||||
|
||||
// 5 is an arbitrary constant chosen to end the measure loop
|
||||
private const int c_layoutLoopMaxCount = 5;
|
||||
@@ -2645,14 +2627,14 @@ namespace Avalonia.Controls
|
||||
internal int CellGroup2; // index of the first cell in second cell group
|
||||
internal int CellGroup3; // index of the first cell in third cell group
|
||||
internal int CellGroup4; // index of the first cell in forth cell group
|
||||
internal DefinitionBase[]? TempDefinitions; // temporary array used during layout for various purposes
|
||||
internal DefinitionBase?[]? TempDefinitions; // temporary array used during layout for various purposes
|
||||
// TempDefinitions.Length == Max(definitionsU.Length, definitionsV.Length)
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Grid validity / property caches dirtiness flags
|
||||
/// </summary>
|
||||
[System.Flags]
|
||||
[Flags]
|
||||
private enum Flags
|
||||
{
|
||||
//
|
||||
@@ -2768,7 +2750,7 @@ namespace Avalonia.Controls
|
||||
/// <summary>
|
||||
/// LayoutTimeSizeType is used internally and reflects layout-time size type.
|
||||
/// </summary>
|
||||
[System.Flags]
|
||||
[Flags]
|
||||
internal enum LayoutTimeSizeType : byte
|
||||
{
|
||||
None = 0x00,
|
||||
@@ -3317,7 +3299,7 @@ namespace Avalonia.Controls
|
||||
internal void UpdateRenderBounds(Size arrangeSize)
|
||||
{
|
||||
_lastArrangeSize = arrangeSize;
|
||||
this.InvalidateVisual();
|
||||
InvalidateVisual();
|
||||
}
|
||||
|
||||
private static Size _lastArrangeSize;
|
||||
|
||||
@@ -14,8 +14,8 @@ namespace Avalonia.Controls
|
||||
/// <summary>
|
||||
/// Defines the <see cref="Source"/> property.
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<IImage> SourceProperty =
|
||||
AvaloniaProperty.Register<Image, IImage>(nameof(Source));
|
||||
public static readonly StyledProperty<IImage?> SourceProperty =
|
||||
AvaloniaProperty.Register<Image, IImage?>(nameof(Source));
|
||||
|
||||
/// <summary>
|
||||
/// Defines the <see cref="Stretch"/> property.
|
||||
@@ -42,7 +42,7 @@ namespace Avalonia.Controls
|
||||
/// Gets or sets the image that will be displayed.
|
||||
/// </summary>
|
||||
[Content]
|
||||
public IImage Source
|
||||
public IImage? Source
|
||||
{
|
||||
get { return GetValue(SourceProperty); }
|
||||
set { SetValue(SourceProperty, value); }
|
||||
@@ -66,6 +66,7 @@ namespace Avalonia.Controls
|
||||
set { SetValue(StretchDirectionProperty, value); }
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override bool BypassFlowDirectionPolicies => true;
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -2,7 +2,6 @@ using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Avalonia.Automation.Peers;
|
||||
using Avalonia.Collections;
|
||||
using Avalonia.Controls.Generators;
|
||||
@@ -17,7 +16,6 @@ using Avalonia.Layout;
|
||||
using Avalonia.LogicalTree;
|
||||
using Avalonia.Metadata;
|
||||
using Avalonia.Styling;
|
||||
using Avalonia.VisualTree;
|
||||
|
||||
namespace Avalonia.Controls
|
||||
{
|
||||
@@ -91,10 +89,11 @@ namespace Avalonia.Controls
|
||||
/// Gets or sets the <see cref="IBinding"/> to use for binding to the display member of each item.
|
||||
/// </summary>
|
||||
[AssignBinding]
|
||||
[InheritDataTypeFromItems(nameof(Items))]
|
||||
public IBinding? DisplayMemberBinding
|
||||
{
|
||||
get { return GetValue(DisplayMemberBindingProperty); }
|
||||
set { SetValue(DisplayMemberBindingProperty, value); }
|
||||
get => GetValue(DisplayMemberBindingProperty);
|
||||
set => SetValue(DisplayMemberBindingProperty, value);
|
||||
}
|
||||
|
||||
private IEnumerable? _items = new AvaloniaList<object>();
|
||||
@@ -106,7 +105,6 @@ namespace Avalonia.Controls
|
||||
private Tuple<int, Control>? _containerBeingPrepared;
|
||||
private ScrollViewer? _scrollViewer;
|
||||
private ItemsPresenter? _itemsPresenter;
|
||||
private IScrollSnapPointsInfo? _scrolSnapPointInfo;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ItemsControl"/> class.
|
||||
@@ -134,8 +132,8 @@ namespace Avalonia.Controls
|
||||
[Content]
|
||||
public IEnumerable? Items
|
||||
{
|
||||
get { return _items; }
|
||||
set { SetAndRaise(ItemsProperty, ref _items, value); }
|
||||
get => _items;
|
||||
set => SetAndRaise(ItemsProperty, ref _items, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -143,8 +141,8 @@ namespace Avalonia.Controls
|
||||
/// </summary>
|
||||
public ControlTheme? ItemContainerTheme
|
||||
{
|
||||
get { return GetValue(ItemContainerThemeProperty); }
|
||||
set { SetValue(ItemContainerThemeProperty, value); }
|
||||
get => GetValue(ItemContainerThemeProperty);
|
||||
set => SetValue(ItemContainerThemeProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -161,8 +159,8 @@ namespace Avalonia.Controls
|
||||
/// </summary>
|
||||
public ITemplate<Panel> ItemsPanel
|
||||
{
|
||||
get { return GetValue(ItemsPanelProperty); }
|
||||
set { SetValue(ItemsPanelProperty, value); }
|
||||
get => GetValue(ItemsPanelProperty);
|
||||
set => SetValue(ItemsPanelProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -171,8 +169,8 @@ namespace Avalonia.Controls
|
||||
[InheritDataTypeFromItems(nameof(Items))]
|
||||
public IDataTemplate? ItemTemplate
|
||||
{
|
||||
get { return GetValue(ItemTemplateProperty); }
|
||||
set { SetValue(ItemTemplateProperty, value); }
|
||||
get => GetValue(ItemTemplateProperty);
|
||||
set => SetValue(ItemTemplateProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -221,6 +219,7 @@ namespace Avalonia.Controls
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<RoutedEventArgs> HorizontalSnapPointsChanged
|
||||
{
|
||||
add
|
||||
@@ -240,6 +239,7 @@ namespace Avalonia.Controls
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<RoutedEventArgs> VerticalSnapPointsChanged
|
||||
{
|
||||
add
|
||||
@@ -264,8 +264,8 @@ namespace Avalonia.Controls
|
||||
/// </summary>
|
||||
public bool AreHorizontalSnapPointsRegular
|
||||
{
|
||||
get { return GetValue(AreHorizontalSnapPointsRegularProperty); }
|
||||
set { SetValue(AreHorizontalSnapPointsRegularProperty, value); }
|
||||
get => GetValue(AreHorizontalSnapPointsRegularProperty);
|
||||
set => SetValue(AreHorizontalSnapPointsRegularProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -273,8 +273,8 @@ namespace Avalonia.Controls
|
||||
/// </summary>
|
||||
public bool AreVerticalSnapPointsRegular
|
||||
{
|
||||
get { return GetValue(AreVerticalSnapPointsRegularProperty); }
|
||||
set { SetValue(AreVerticalSnapPointsRegularProperty, value); }
|
||||
get => GetValue(AreVerticalSnapPointsRegularProperty);
|
||||
set => SetValue(AreVerticalSnapPointsRegularProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -424,13 +424,12 @@ namespace Avalonia.Controls
|
||||
/// <returns>true if the item is (or is eligible to be) its own container; otherwise, false.</returns>
|
||||
protected internal virtual bool IsItemItsOwnContainerOverride(Control item) => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||
{
|
||||
base.OnApplyTemplate(e);
|
||||
_scrollViewer = e.NameScope.Find<ScrollViewer>("PART_ScrollViewer");
|
||||
_itemsPresenter = e.NameScope.Find<ItemsPresenter>("PART_ItemsPresenter");
|
||||
|
||||
_scrolSnapPointInfo = _itemsPresenter as IScrollSnapPointsInfo;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -477,11 +476,13 @@ namespace Avalonia.Controls
|
||||
base.OnKeyDown(e);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override AutomationPeer OnCreateAutomationPeer()
|
||||
{
|
||||
return new ItemsControlAutomationPeer(this);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
|
||||
{
|
||||
base.OnPropertyChanged(change);
|
||||
@@ -748,11 +749,13 @@ namespace Avalonia.Controls
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IReadOnlyList<double> GetIrregularSnapPoints(Orientation orientation, SnapPointsAlignment snapPointsAlignment)
|
||||
{
|
||||
return _itemsPresenter?.GetIrregularSnapPoints(orientation, snapPointsAlignment) ?? new List<double>();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public double GetRegularSnapPoints(Orientation orientation, SnapPointsAlignment snapPointsAlignment, out double offset)
|
||||
{
|
||||
offset = 0;
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace Avalonia.Controls
|
||||
.AddClassHandler<LayoutTransformControl>((x, e) => x.OnLayoutTransformChanged(e));
|
||||
|
||||
ChildProperty.Changed
|
||||
.AddClassHandler<LayoutTransformControl>((x, e) => x.OnChildChanged(e));
|
||||
.AddClassHandler<LayoutTransformControl>((x, _) => x.OnChildChanged());
|
||||
|
||||
UseRenderTransformProperty.Changed
|
||||
.AddClassHandler<LayoutTransformControl>((x, e) => x.OnUseRenderTransformPropertyChanged(e));
|
||||
@@ -146,7 +146,7 @@ namespace Avalonia.Controls
|
||||
return transformedDesiredSize;
|
||||
}
|
||||
|
||||
IDisposable? _renderTransformChangedEvent;
|
||||
private IDisposable? _renderTransformChangedEvent;
|
||||
|
||||
private void OnUseRenderTransformPropertyChanged(AvaloniaPropertyChangedEventArgs e)
|
||||
{
|
||||
@@ -167,8 +167,7 @@ namespace Avalonia.Controls
|
||||
.Subscribe(
|
||||
(x) =>
|
||||
{
|
||||
var target2 = x.Sender as LayoutTransformControl;
|
||||
if (target2 != null)
|
||||
if (x.Sender is LayoutTransformControl target2)
|
||||
{
|
||||
target2.LayoutTransform = target2.RenderTransform;
|
||||
}
|
||||
@@ -182,7 +181,7 @@ namespace Avalonia.Controls
|
||||
}
|
||||
}
|
||||
|
||||
private void OnChildChanged(AvaloniaPropertyChangedEventArgs e)
|
||||
private void OnChildChanged()
|
||||
{
|
||||
if (null != TransformRoot)
|
||||
{
|
||||
@@ -206,18 +205,18 @@ namespace Avalonia.Controls
|
||||
/// <summary>
|
||||
/// Actual DesiredSize of Child element (the value it returned from its MeasureOverride method).
|
||||
/// </summary>
|
||||
private Size _childActualSize = default;
|
||||
private Size _childActualSize;
|
||||
|
||||
/// <summary>
|
||||
/// RenderTransform/MatrixTransform applied to TransformRoot.
|
||||
/// </summary>
|
||||
private MatrixTransform _matrixTransform = new MatrixTransform();
|
||||
private readonly MatrixTransform _matrixTransform = new();
|
||||
|
||||
/// <summary>
|
||||
/// Transformation matrix corresponding to _matrixTransform.
|
||||
/// </summary>
|
||||
private Matrix _transformation;
|
||||
private IDisposable? _transformChangedEvent = null;
|
||||
private IDisposable? _transformChangedEvent;
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if Size a is smaller than Size b in either dimension.
|
||||
@@ -263,10 +262,7 @@ namespace Avalonia.Controls
|
||||
// Get the transform matrix and apply it
|
||||
_transformation = RoundMatrix(LayoutTransform.Value, DecimalsAfterRound);
|
||||
|
||||
if (null != _matrixTransform)
|
||||
{
|
||||
_matrixTransform.Matrix = _transformation;
|
||||
}
|
||||
_matrixTransform.Matrix = _transformation;
|
||||
|
||||
// New transform means re-layout is necessary
|
||||
InvalidateMeasure();
|
||||
|
||||
@@ -104,6 +104,7 @@ namespace Avalonia.Controls
|
||||
public void UnselectAll() => Selection.Clear();
|
||||
|
||||
protected internal override Control CreateContainerForItemOverride() => new ListBoxItem();
|
||||
protected internal override bool IsItemItsOwnContainerOverride(Control item) => item is ListBoxItem;
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void OnGotFocus(GotFocusEventArgs e)
|
||||
|
||||
@@ -178,12 +178,11 @@ namespace Avalonia.Controls
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
Type IStyleable.StyleKey => typeof(TextBox);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnGotFocus(GotFocusEventArgs e)
|
||||
{
|
||||
if (HidePromptOnLeave == true && MaskProvider != null)
|
||||
@@ -193,6 +192,7 @@ namespace Avalonia.Controls
|
||||
base.OnGotFocus(e);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async void OnKeyDown(KeyEventArgs e)
|
||||
{
|
||||
if (MaskProvider == null)
|
||||
@@ -271,15 +271,17 @@ namespace Avalonia.Controls
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnLostFocus(RoutedEventArgs e)
|
||||
{
|
||||
if (HidePromptOnLeave == true && MaskProvider != null)
|
||||
if (HidePromptOnLeave && MaskProvider != null)
|
||||
{
|
||||
Text = MaskProvider.ToString(!HidePromptOnLeave, true);
|
||||
}
|
||||
base.OnLostFocus(e);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
|
||||
{
|
||||
void UpdateMaskProvider()
|
||||
@@ -357,6 +359,8 @@ namespace Avalonia.Controls
|
||||
}
|
||||
base.OnPropertyChanged(change);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnTextInput(TextInputEventArgs e)
|
||||
{
|
||||
_ignoreTextChanges = true;
|
||||
@@ -423,7 +427,7 @@ namespace Avalonia.Controls
|
||||
return startPosition;
|
||||
}
|
||||
|
||||
private void RefreshText(MaskedTextProvider provider, int position)
|
||||
private void RefreshText(MaskedTextProvider? provider, int position)
|
||||
{
|
||||
if (provider != null)
|
||||
{
|
||||
|
||||
@@ -16,19 +16,17 @@ namespace Avalonia.Controls
|
||||
private IPlatformHandle? _nativeControlHandle;
|
||||
private bool _queuedForDestruction;
|
||||
private bool _queuedForMoveResize;
|
||||
private readonly List<Visual> _propertyChangedSubscriptions = new List<Visual>();
|
||||
private readonly List<Visual> _propertyChangedSubscriptions = new();
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
_currentRoot = e.Root as TopLevel;
|
||||
var visual = (Visual)this;
|
||||
while (visual != null)
|
||||
{
|
||||
if (visual is Visual v)
|
||||
{
|
||||
v.PropertyChanged += PropertyChangedHandler;
|
||||
_propertyChangedSubscriptions.Add(v);
|
||||
}
|
||||
visual.PropertyChanged += PropertyChangedHandler;
|
||||
_propertyChangedSubscriptions.Add(visual);
|
||||
|
||||
visual = visual.GetVisualParent();
|
||||
}
|
||||
@@ -42,15 +40,13 @@ namespace Avalonia.Controls
|
||||
EnqueueForMoveResize();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
_currentRoot = null;
|
||||
if (_propertyChangedSubscriptions != null)
|
||||
{
|
||||
foreach (var v in _propertyChangedSubscriptions)
|
||||
v.PropertyChanged -= PropertyChangedHandler;
|
||||
_propertyChangedSubscriptions.Clear();
|
||||
}
|
||||
foreach (var v in _propertyChangedSubscriptions)
|
||||
v.PropertyChanged -= PropertyChangedHandler;
|
||||
_propertyChangedSubscriptions.Clear();
|
||||
UpdateHost();
|
||||
}
|
||||
|
||||
@@ -128,7 +124,7 @@ namespace Avalonia.Controls
|
||||
return new Rect(position.Value, bounds.Size);
|
||||
}
|
||||
|
||||
void EnqueueForMoveResize()
|
||||
private void EnqueueForMoveResize()
|
||||
{
|
||||
if(_queuedForMoveResize)
|
||||
return;
|
||||
|
||||
@@ -12,10 +12,10 @@ namespace Avalonia.Controls
|
||||
|
||||
public static bool GetIsNativeMenuExported(TopLevel tl) => tl.GetValue(IsNativeMenuExportedProperty);
|
||||
|
||||
private static readonly AttachedProperty<NativeMenuInfo> s_nativeMenuInfoProperty =
|
||||
AvaloniaProperty.RegisterAttached<NativeMenu, TopLevel, NativeMenuInfo>("___NativeMenuInfo");
|
||||
|
||||
class NativeMenuInfo
|
||||
private static readonly AttachedProperty<NativeMenuInfo?> s_nativeMenuInfoProperty =
|
||||
AvaloniaProperty.RegisterAttached<NativeMenu, TopLevel, NativeMenuInfo?>("___NativeMenuInfo");
|
||||
|
||||
private sealed class NativeMenuInfo
|
||||
{
|
||||
public bool ChangingIsExported { get; set; }
|
||||
public ITopLevelNativeMenuExporter? Exporter { get; }
|
||||
@@ -33,7 +33,7 @@ namespace Avalonia.Controls
|
||||
}
|
||||
}
|
||||
|
||||
static NativeMenuInfo GetInfo(TopLevel target)
|
||||
private static NativeMenuInfo GetInfo(TopLevel target)
|
||||
{
|
||||
var rv = target.GetValue(s_nativeMenuInfoProperty);
|
||||
if (rv == null)
|
||||
@@ -45,18 +45,18 @@ namespace Avalonia.Controls
|
||||
return rv;
|
||||
}
|
||||
|
||||
static void SetIsNativeMenuExported(TopLevel tl, bool value)
|
||||
private static void SetIsNativeMenuExported(TopLevel tl, bool value)
|
||||
{
|
||||
GetInfo(tl).ChangingIsExported = true;
|
||||
tl.SetValue(IsNativeMenuExportedProperty, value);
|
||||
}
|
||||
|
||||
public static readonly AttachedProperty<NativeMenu> MenuProperty
|
||||
= AvaloniaProperty.RegisterAttached<NativeMenu, AvaloniaObject, NativeMenu>("Menu");
|
||||
public static readonly AttachedProperty<NativeMenu?> MenuProperty
|
||||
= AvaloniaProperty.RegisterAttached<NativeMenu, AvaloniaObject, NativeMenu?>("Menu");
|
||||
|
||||
public static void SetMenu(AvaloniaObject o, NativeMenu menu) => o.SetValue(MenuProperty, menu);
|
||||
public static void SetMenu(AvaloniaObject o, NativeMenu? menu) => o.SetValue(MenuProperty, menu);
|
||||
|
||||
public static NativeMenu GetMenu(AvaloniaObject o) => o.GetValue(MenuProperty);
|
||||
public static NativeMenu? GetMenu(AvaloniaObject o) => o.GetValue(MenuProperty);
|
||||
|
||||
static NativeMenu()
|
||||
{
|
||||
|
||||
@@ -553,7 +553,7 @@ namespace Avalonia.Controls.Platform
|
||||
}
|
||||
}
|
||||
|
||||
protected static IMenuItem? GetMenuItem(Control? item)
|
||||
protected static IMenuItem? GetMenuItem(StyledElement? item)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
|
||||
@@ -34,8 +34,8 @@ namespace Avalonia.Controls.Primitives
|
||||
public static readonly AttachedProperty<Control?> AdornerProperty =
|
||||
AvaloniaProperty.RegisterAttached<AdornerLayer, Visual, Control?>("Adorner");
|
||||
|
||||
private static readonly AttachedProperty<AdornedElementInfo> s_adornedElementInfoProperty =
|
||||
AvaloniaProperty.RegisterAttached<AdornerLayer, Visual, AdornedElementInfo>("AdornedElementInfo");
|
||||
private static readonly AttachedProperty<AdornedElementInfo?> s_adornedElementInfoProperty =
|
||||
AvaloniaProperty.RegisterAttached<AdornerLayer, Visual, AdornedElementInfo?>("AdornedElementInfo");
|
||||
|
||||
private static readonly AttachedProperty<AdornerLayer?> s_savedAdornerLayerProperty =
|
||||
AvaloniaProperty.RegisterAttached<Visual, Visual, AdornerLayer?>("SavedAdornerLayer");
|
||||
@@ -159,8 +159,8 @@ namespace Avalonia.Controls.Primitives
|
||||
return;
|
||||
}
|
||||
|
||||
AdornerLayer.SetAdornedElement(adorner, visual);
|
||||
AdornerLayer.SetIsClipEnabled(adorner, false);
|
||||
SetAdornedElement(adorner, visual);
|
||||
SetIsClipEnabled(adorner, false);
|
||||
|
||||
((ISetLogicalParent) adorner).SetParent(visual);
|
||||
layer.Children.Add(adorner);
|
||||
@@ -177,6 +177,7 @@ namespace Avalonia.Controls.Primitives
|
||||
((ISetLogicalParent) adorner).SetParent(null);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Size MeasureOverride(Size availableSize)
|
||||
{
|
||||
foreach (var child in Children)
|
||||
@@ -199,6 +200,7 @@ namespace Avalonia.Controls.Primitives
|
||||
return default;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Size ArrangeOverride(Size finalSize)
|
||||
{
|
||||
foreach (var child in Children)
|
||||
@@ -217,7 +219,7 @@ namespace Avalonia.Controls.Primitives
|
||||
}
|
||||
else
|
||||
{
|
||||
ArrangeChild((Control) child, finalSize);
|
||||
ArrangeChild(child, finalSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Avalonia.Reactive;
|
||||
using Avalonia.Controls.Primitives.PopupPositioning;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Media;
|
||||
@@ -18,8 +17,8 @@ namespace Avalonia.Controls.Primitives
|
||||
PopupRoot.TransformProperty.AddOwner<OverlayPopupHost>();
|
||||
|
||||
private readonly OverlayLayer _overlayLayer;
|
||||
private PopupPositionerParameters _positionerParameters = new PopupPositionerParameters();
|
||||
private ManagedPopupPositioner _positioner;
|
||||
private readonly ManagedPopupPositioner _positioner;
|
||||
private PopupPositionerParameters _positionerParameters;
|
||||
private Point _lastRequestedPosition;
|
||||
private bool _shown;
|
||||
|
||||
@@ -29,13 +28,16 @@ namespace Avalonia.Controls.Primitives
|
||||
_positioner = new ManagedPopupPositioner(this);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SetChild(Control? control)
|
||||
{
|
||||
Content = control;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Visual? HostedVisualTreeRoot => null;
|
||||
|
||||
/// <inheritdoc />
|
||||
public Transform? Transform
|
||||
{
|
||||
get => GetValue(TransformProperty);
|
||||
@@ -48,23 +50,27 @@ namespace Avalonia.Controls.Primitives
|
||||
set { /* Not currently supported in overlay popups */ }
|
||||
}
|
||||
|
||||
protected internal override Interactive? InteractiveParent => Parent;
|
||||
/// <inheritdoc />
|
||||
protected internal override Interactive? InteractiveParent => (Interactive?)VisualParent;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose() => Hide();
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Show()
|
||||
{
|
||||
_overlayLayer.Children.Add(this);
|
||||
_shown = true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Hide()
|
||||
{
|
||||
_overlayLayer.Children.Remove(this);
|
||||
_shown = false;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ConfigurePosition(Visual target, PlacementMode placement, Point offset,
|
||||
PopupAnchor anchor = PopupAnchor.None, PopupGravity gravity = PopupGravity.None,
|
||||
PopupPositionerConstraintAdjustment constraintAdjustment = PopupPositionerConstraintAdjustment.All,
|
||||
@@ -75,6 +81,7 @@ namespace Avalonia.Controls.Primitives
|
||||
UpdatePosition();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Size ArrangeOverride(Size finalSize)
|
||||
{
|
||||
if (_positionerParameters.Size != finalSize)
|
||||
@@ -123,17 +130,18 @@ namespace Avalonia.Controls.Primitives
|
||||
|
||||
public static IPopupHost CreatePopupHost(Visual target, IAvaloniaDependencyResolver? dependencyResolver)
|
||||
{
|
||||
var platform = TopLevel.GetTopLevel(target)?.PlatformImpl?.CreatePopup();
|
||||
if (platform != null)
|
||||
return new PopupRoot((TopLevel)target.GetVisualRoot()!, platform, dependencyResolver);
|
||||
|
||||
var overlayLayer = OverlayLayer.GetOverlayLayer(target);
|
||||
if (overlayLayer == null)
|
||||
throw new InvalidOperationException(
|
||||
"Unable to create IPopupImpl and no overlay layer is found for the target control");
|
||||
if (TopLevel.GetTopLevel(target) is { } topLevel && topLevel.PlatformImpl?.CreatePopup() is { } popupImpl)
|
||||
{
|
||||
return new PopupRoot(topLevel, popupImpl, dependencyResolver);
|
||||
}
|
||||
|
||||
if (OverlayLayer.GetOverlayLayer(target) is { } overlayLayer)
|
||||
{
|
||||
return new OverlayPopupHost(overlayLayer);
|
||||
}
|
||||
|
||||
return new OverlayPopupHost(overlayLayer);
|
||||
throw new InvalidOperationException(
|
||||
"Unable to create IPopupImpl and no overlay layer is found for the target control");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ namespace Avalonia.Controls.Primitives
|
||||
public static readonly StyledProperty<bool> TopmostProperty =
|
||||
AvaloniaProperty.Register<Popup, bool>(nameof(Topmost));
|
||||
|
||||
private bool _isOpenRequested = false;
|
||||
private bool _isOpenRequested;
|
||||
private bool _isOpen;
|
||||
private bool _ignoreIsOpenChanged;
|
||||
private PopupOpenState? _openState;
|
||||
@@ -377,9 +377,9 @@ namespace Avalonia.Controls.Primitives
|
||||
popupHost.SetChild(Child);
|
||||
((ISetLogicalParent)popupHost).SetParent(this);
|
||||
|
||||
if (InheritsTransform && placementTarget is Control c)
|
||||
if (InheritsTransform)
|
||||
{
|
||||
TransformTrackingHelper.Track(c, PlacementTargetTransformChanged)
|
||||
TransformTrackingHelper.Track(placementTarget, PlacementTargetTransformChanged)
|
||||
.DisposeWith(handlerCleanup);
|
||||
}
|
||||
else
|
||||
@@ -518,6 +518,7 @@ namespace Avalonia.Controls.Primitives
|
||||
Close();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
|
||||
{
|
||||
base.OnPropertyChanged(change);
|
||||
@@ -579,7 +580,7 @@ namespace Avalonia.Controls.Primitives
|
||||
var scaleX = 1.0;
|
||||
var scaleY = 1.0;
|
||||
|
||||
if (InheritsTransform && placementTarget.TransformToVisual(topLevel) is Matrix m)
|
||||
if (InheritsTransform && placementTarget.TransformToVisual(topLevel) is { } m)
|
||||
{
|
||||
scaleX = Math.Sqrt(m.M11 * m.M11 + m.M12 * m.M12);
|
||||
scaleY = Math.Sqrt(m.M11 * m.M11 + m.M12 * m.M12);
|
||||
@@ -623,6 +624,7 @@ namespace Avalonia.Controls.Primitives
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override AutomationPeer OnCreateAutomationPeer()
|
||||
{
|
||||
return new PopupAutomationPeer(this);
|
||||
@@ -723,7 +725,7 @@ namespace Avalonia.Controls.Primitives
|
||||
|
||||
while (e is object && (!e.Focusable || !e.IsEffectivelyEnabled || !e.IsVisible))
|
||||
{
|
||||
e = e.Parent;
|
||||
e = e.VisualParent as Control;
|
||||
}
|
||||
|
||||
if (e is object)
|
||||
@@ -850,7 +852,7 @@ namespace Avalonia.Controls.Primitives
|
||||
|
||||
var popupHost = _openState.PopupHost;
|
||||
|
||||
return popupHost != null && ((Visual)popupHost).IsVisualAncestorOf(visual);
|
||||
return ((Visual)popupHost).IsVisualAncestorOf(visual);
|
||||
}
|
||||
|
||||
public bool IsPointerOverPopup => ((IInputElement?)_openState?.PopupHost)?.IsPointerOver ?? false;
|
||||
|
||||
@@ -72,12 +72,12 @@ namespace Avalonia.Controls.Primitives
|
||||
/// <remarks>
|
||||
/// Popup events are passed to their parent window. This facilitates this.
|
||||
/// </remarks>
|
||||
protected internal override Interactive? InteractiveParent => Parent;
|
||||
protected internal override Interactive? InteractiveParent => (Interactive?)Parent;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the control that is hosting the popup root.
|
||||
/// </summary>
|
||||
Visual? IHostedVisualTreeRoot.Host => Parent;
|
||||
Visual? IHostedVisualTreeRoot.Host => VisualParent;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the styling parent of the popup root.
|
||||
|
||||
@@ -3,16 +3,16 @@ using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Avalonia.Controls.Generators;
|
||||
using Avalonia.Controls.Selection;
|
||||
using Avalonia.Controls.Utils;
|
||||
using Avalonia.Data;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Input.Platform;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Metadata;
|
||||
using Avalonia.Threading;
|
||||
using Avalonia.VisualTree;
|
||||
|
||||
namespace Avalonia.Controls.Primitives
|
||||
{
|
||||
@@ -66,6 +66,19 @@ namespace Avalonia.Controls.Primitives
|
||||
(o, v) => o.SelectedItem = v,
|
||||
defaultBindingMode: BindingMode.TwoWay, enableDataValidation: true);
|
||||
|
||||
/// <summary>
|
||||
/// Defines the <see cref="SelectedValue"/> property
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<object?> SelectedValueProperty =
|
||||
AvaloniaProperty.Register<SelectingItemsControl, object?>(nameof(SelectedValue),
|
||||
defaultBindingMode: BindingMode.TwoWay);
|
||||
|
||||
/// <summary>
|
||||
/// Defines the <see cref="SelectedValueBinding"/> property
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<IBinding?> SelectedValueBindingProperty =
|
||||
AvaloniaProperty.Register<SelectingItemsControl, IBinding?>(nameof(SelectedValueBinding));
|
||||
|
||||
/// <summary>
|
||||
/// Defines the <see cref="SelectedItems"/> property.
|
||||
/// </summary>
|
||||
@@ -129,6 +142,8 @@ namespace Avalonia.Controls.Primitives
|
||||
private bool _ignoreContainerSelectionChanged;
|
||||
private UpdateState? _updateState;
|
||||
private bool _hasScrolledToSelectedItem;
|
||||
private BindingHelper? _bindingHelper;
|
||||
private bool _isSelectionChangeActive;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes static members of the <see cref="SelectingItemsControl"/> class.
|
||||
@@ -143,8 +158,8 @@ namespace Avalonia.Controls.Primitives
|
||||
/// </summary>
|
||||
public event EventHandler<SelectionChangedEventArgs>? SelectionChanged
|
||||
{
|
||||
add { AddHandler(SelectionChangedEvent, value); }
|
||||
remove { RemoveHandler(SelectionChangedEvent, value); }
|
||||
add => AddHandler(SelectionChangedEvent, value);
|
||||
remove => RemoveHandler(SelectionChangedEvent, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -152,8 +167,8 @@ namespace Avalonia.Controls.Primitives
|
||||
/// </summary>
|
||||
public bool AutoScrollToSelectedItem
|
||||
{
|
||||
get { return GetValue(AutoScrollToSelectedItemProperty); }
|
||||
set { SetValue(AutoScrollToSelectedItemProperty, value); }
|
||||
get => GetValue(AutoScrollToSelectedItemProperty);
|
||||
set => SetValue(AutoScrollToSelectedItemProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -209,6 +224,28 @@ namespace Avalonia.Controls.Primitives
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="IBinding"/> instance used to obtain the
|
||||
/// <see cref="SelectedValue"/> property
|
||||
/// </summary>
|
||||
[AssignBinding]
|
||||
[InheritDataTypeFromItems(nameof(Items))]
|
||||
public IBinding? SelectedValueBinding
|
||||
{
|
||||
get => GetValue(SelectedValueBindingProperty);
|
||||
set => SetValue(SelectedValueBindingProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the value of the selected item, obtained using
|
||||
/// <see cref="SelectedValueBinding"/>
|
||||
/// </summary>
|
||||
public object? SelectedValue
|
||||
{
|
||||
get => GetValue(SelectedValueProperty);
|
||||
set => SetValue(SelectedValueProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the selected items.
|
||||
/// </summary>
|
||||
@@ -255,6 +292,7 @@ namespace Avalonia.Controls.Primitives
|
||||
/// <summary>
|
||||
/// Gets or sets the model that holds the current selection.
|
||||
/// </summary>
|
||||
[AllowNull]
|
||||
protected ISelectionModel Selection
|
||||
{
|
||||
get
|
||||
@@ -322,8 +360,8 @@ namespace Avalonia.Controls.Primitives
|
||||
/// </summary>
|
||||
public bool IsTextSearchEnabled
|
||||
{
|
||||
get { return GetValue(IsTextSearchEnabledProperty); }
|
||||
set { SetValue(IsTextSearchEnabledProperty, value); }
|
||||
get => GetValue(IsTextSearchEnabledProperty);
|
||||
set => SetValue(IsTextSearchEnabledProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -332,8 +370,8 @@ namespace Avalonia.Controls.Primitives
|
||||
/// </summary>
|
||||
public bool WrapSelection
|
||||
{
|
||||
get { return GetValue(WrapSelectionProperty); }
|
||||
set { SetValue(WrapSelectionProperty, value); }
|
||||
get => GetValue(WrapSelectionProperty);
|
||||
set => SetValue(WrapSelectionProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -345,8 +383,8 @@ namespace Avalonia.Controls.Primitives
|
||||
/// </remarks>
|
||||
protected SelectionMode SelectionMode
|
||||
{
|
||||
get { return GetValue(SelectionModeProperty); }
|
||||
set { SetValue(SelectionModeProperty, value); }
|
||||
get => GetValue(SelectionModeProperty);
|
||||
set => SetValue(SelectionModeProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -399,6 +437,7 @@ namespace Avalonia.Controls.Primitives
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void ItemsCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
base.ItemsCollectionChanged(sender!, e);
|
||||
@@ -409,12 +448,14 @@ namespace Avalonia.Controls.Primitives
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
base.OnAttachedToVisualTree(e);
|
||||
AutoScrollToSelectedItemIfNecessary();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||
{
|
||||
base.OnApplyTemplate(e);
|
||||
@@ -431,6 +472,7 @@ namespace Avalonia.Controls.Primitives
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected internal override void PrepareContainerForItemOverride(Control element, object? item, int index)
|
||||
{
|
||||
base.PrepareContainerForItemOverride(element, item, index);
|
||||
@@ -447,12 +489,14 @@ namespace Avalonia.Controls.Primitives
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void ContainerIndexChangedOverride(Control container, int oldIndex, int newIndex)
|
||||
{
|
||||
base.ContainerIndexChangedOverride(container, oldIndex, newIndex);
|
||||
MarkContainerSelected(container, Selection.IsSelected(newIndex));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected internal override void ClearContainerForItemOverride(Control element)
|
||||
{
|
||||
base.ClearContainerForItemOverride(element);
|
||||
@@ -463,7 +507,7 @@ namespace Avalonia.Controls.Primitives
|
||||
KeyboardNavigation.SetTabOnceActiveElement(panel, null);
|
||||
}
|
||||
|
||||
if (element is ISelectable selectable)
|
||||
if (element is ISelectable)
|
||||
MarkContainerSelected(element, false);
|
||||
}
|
||||
|
||||
@@ -498,7 +542,8 @@ namespace Avalonia.Controls.Primitives
|
||||
DataValidationErrors.SetError(this, error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
base.OnInitialized();
|
||||
@@ -509,6 +554,7 @@ namespace Avalonia.Controls.Primitives
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnTextInput(TextInputEventArgs e)
|
||||
{
|
||||
if (!e.Handled)
|
||||
@@ -551,6 +597,7 @@ namespace Avalonia.Controls.Primitives
|
||||
base.OnTextInput(e);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnKeyDown(KeyEventArgs e)
|
||||
{
|
||||
base.OnKeyDown(e);
|
||||
@@ -582,6 +629,7 @@ namespace Avalonia.Controls.Primitives
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
|
||||
{
|
||||
base.OnPropertyChanged(change);
|
||||
@@ -592,7 +640,7 @@ namespace Avalonia.Controls.Primitives
|
||||
}
|
||||
if (change.Property == ItemsProperty && _updateState is null && _selection is object)
|
||||
{
|
||||
var newValue = change.GetNewValue<IEnumerable>();
|
||||
var newValue = change.GetNewValue<IEnumerable?>();
|
||||
_selection.Source = newValue;
|
||||
|
||||
if (newValue is null)
|
||||
@@ -609,6 +657,60 @@ namespace Avalonia.Controls.Primitives
|
||||
{
|
||||
WrapFocus = WrapSelection;
|
||||
}
|
||||
else if (change.Property == SelectedValueProperty)
|
||||
{
|
||||
if (_isSelectionChangeActive)
|
||||
return;
|
||||
|
||||
if (_updateState is not null)
|
||||
{
|
||||
_updateState.SelectedValue = change.NewValue;
|
||||
return;
|
||||
}
|
||||
|
||||
SelectItemWithValue(change.NewValue);
|
||||
}
|
||||
else if (change.Property == SelectedValueBindingProperty)
|
||||
{
|
||||
var idx = SelectedIndex;
|
||||
|
||||
// If no selection is active, don't do anything as SelectedValue is already null
|
||||
if (idx == -1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var value = change.GetNewValue<IBinding>();
|
||||
if (value is null)
|
||||
{
|
||||
// Clearing SelectedValueBinding makes the SelectedValue the item itself
|
||||
SelectedValue = SelectedItem;
|
||||
return;
|
||||
}
|
||||
|
||||
var selectedItem = SelectedItem;
|
||||
|
||||
try
|
||||
{
|
||||
_isSelectionChangeActive = true;
|
||||
|
||||
if (_bindingHelper is null)
|
||||
{
|
||||
_bindingHelper = new BindingHelper(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
_bindingHelper.UpdateBinding(value);
|
||||
}
|
||||
|
||||
// Re-evaluate SelectedValue with the new binding
|
||||
SelectedValue = _bindingHelper.Evaluate(selectedItem);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isSelectionChangeActive = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -695,7 +797,7 @@ namespace Avalonia.Controls.Primitives
|
||||
{
|
||||
if (multi)
|
||||
{
|
||||
if (Selection.IsSelected(index) == true)
|
||||
if (Selection.IsSelected(index))
|
||||
{
|
||||
Selection.Deselect(index);
|
||||
}
|
||||
@@ -716,12 +818,10 @@ namespace Avalonia.Controls.Primitives
|
||||
Selection.Select(index);
|
||||
}
|
||||
|
||||
if (Presenter?.Panel != null)
|
||||
if (Presenter?.Panel is { } panel)
|
||||
{
|
||||
var container = ContainerFromIndex(index);
|
||||
KeyboardNavigation.SetTabOnceActiveElement(
|
||||
(InputElement)Presenter.Panel,
|
||||
container);
|
||||
KeyboardNavigation.SetTabOnceActiveElement(panel, container);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -815,6 +915,10 @@ namespace Avalonia.Controls.Primitives
|
||||
new BindingValue<IList?>(SelectedItems));
|
||||
_oldSelectedItems = SelectedItems;
|
||||
}
|
||||
else if (e.PropertyName == nameof(ISelectionModel.Source))
|
||||
{
|
||||
ClearValue(SelectedValueProperty);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -845,6 +949,11 @@ namespace Avalonia.Controls.Primitives
|
||||
Mark(i, false);
|
||||
}
|
||||
|
||||
if (!_isSelectionChangeActive)
|
||||
{
|
||||
UpdateSelectedValueFromItem();
|
||||
}
|
||||
|
||||
var route = BuildEventRoute(SelectionChangedEvent);
|
||||
|
||||
if (route.HasHandlers)
|
||||
@@ -871,6 +980,109 @@ namespace Avalonia.Controls.Primitives
|
||||
}
|
||||
}
|
||||
|
||||
private void SelectItemWithValue(object? value)
|
||||
{
|
||||
if (ItemCount == 0 || _isSelectionChangeActive)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
_isSelectionChangeActive = true;
|
||||
var si = FindItemWithValue(value);
|
||||
if (si != AvaloniaProperty.UnsetValue)
|
||||
{
|
||||
SelectedItem = si;
|
||||
}
|
||||
else
|
||||
{
|
||||
SelectedItem = null;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isSelectionChangeActive = false;
|
||||
}
|
||||
}
|
||||
|
||||
private object FindItemWithValue(object? value)
|
||||
{
|
||||
if (ItemCount == 0 || value is null)
|
||||
{
|
||||
return AvaloniaProperty.UnsetValue;
|
||||
}
|
||||
|
||||
var items = Items;
|
||||
var binding = SelectedValueBinding;
|
||||
|
||||
if (binding is null)
|
||||
{
|
||||
// No SelectedValueBinding set, SelectedValue is the item itself
|
||||
// Still verify the value passed in is in the Items list
|
||||
var index = items!.IndexOf(value);
|
||||
|
||||
if (index >= 0)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
else
|
||||
{
|
||||
return AvaloniaProperty.UnsetValue;
|
||||
}
|
||||
}
|
||||
|
||||
_bindingHelper ??= new BindingHelper(binding);
|
||||
|
||||
// Matching UWP behavior, if duplicates are present, return the first item matching
|
||||
// the SelectedValue provided
|
||||
foreach (var item in items!)
|
||||
{
|
||||
var itemValue = _bindingHelper.Evaluate(item);
|
||||
|
||||
if (itemValue.Equals(value))
|
||||
{
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
return AvaloniaProperty.UnsetValue;
|
||||
}
|
||||
|
||||
private void UpdateSelectedValueFromItem()
|
||||
{
|
||||
if (_isSelectionChangeActive)
|
||||
return;
|
||||
|
||||
var binding = SelectedValueBinding;
|
||||
var item = SelectedItem;
|
||||
|
||||
if (binding is null || item is null)
|
||||
{
|
||||
// No SelectedValueBinding, SelectedValue is Item itself
|
||||
try
|
||||
{
|
||||
_isSelectionChangeActive = true;
|
||||
SelectedValue = item;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isSelectionChangeActive = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
_bindingHelper ??= new BindingHelper(binding);
|
||||
|
||||
try
|
||||
{
|
||||
_isSelectionChangeActive = true;
|
||||
SelectedValue = _bindingHelper.Evaluate(item);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isSelectionChangeActive = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void AutoScrollToSelectedItemIfNecessary()
|
||||
{
|
||||
if (AutoScrollToSelectedItem &&
|
||||
@@ -940,7 +1152,7 @@ namespace Avalonia.Controls.Primitives
|
||||
|
||||
private void UpdateContainerSelection()
|
||||
{
|
||||
if (Presenter?.Panel is Panel panel)
|
||||
if (Presenter?.Panel is { } panel)
|
||||
{
|
||||
foreach (var container in panel.Children)
|
||||
{
|
||||
@@ -1037,6 +1249,13 @@ namespace Avalonia.Controls.Primitives
|
||||
Selection.Clear();
|
||||
}
|
||||
|
||||
if (state.SelectedValue.HasValue)
|
||||
{
|
||||
var item = FindItemWithValue(state.SelectedValue.Value);
|
||||
if (item != AvaloniaProperty.UnsetValue)
|
||||
state.SelectedItem = item;
|
||||
}
|
||||
|
||||
if (state.SelectedIndex.HasValue)
|
||||
{
|
||||
SelectedIndex = state.SelectedIndex.Value;
|
||||
@@ -1098,6 +1317,7 @@ namespace Avalonia.Controls.Primitives
|
||||
{
|
||||
private Optional<int> _selectedIndex;
|
||||
private Optional<object?> _selectedItem;
|
||||
private Optional<object?> _selectedValue;
|
||||
|
||||
public int UpdateCount { get; set; }
|
||||
public Optional<ISelectionModel> Selection { get; set; }
|
||||
@@ -1122,6 +1342,54 @@ namespace Avalonia.Controls.Primitives
|
||||
_selectedIndex = default;
|
||||
}
|
||||
}
|
||||
|
||||
public Optional<object?> SelectedValue
|
||||
{
|
||||
get => _selectedValue;
|
||||
set
|
||||
{
|
||||
_selectedValue = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper class for evaluating a binding from an Item and IBinding instance
|
||||
/// </summary>
|
||||
private class BindingHelper : StyledElement
|
||||
{
|
||||
public BindingHelper(IBinding binding)
|
||||
{
|
||||
UpdateBinding(binding);
|
||||
}
|
||||
|
||||
public static readonly StyledProperty<object> ValueProperty =
|
||||
AvaloniaProperty.Register<BindingHelper, object>("Value");
|
||||
|
||||
public object Evaluate(object? dataContext)
|
||||
{
|
||||
dataContext = dataContext ?? throw new ArgumentNullException(nameof(dataContext));
|
||||
|
||||
// Only update the DataContext if necessary
|
||||
if (!dataContext.Equals(DataContext))
|
||||
DataContext = dataContext;
|
||||
|
||||
return GetValue(ValueProperty);
|
||||
}
|
||||
|
||||
public void UpdateBinding(IBinding binding)
|
||||
{
|
||||
_lastBinding = binding;
|
||||
var ib = binding.Initiate(this, ValueProperty);
|
||||
if (ib is null)
|
||||
{
|
||||
throw new InvalidOperationException("Unable to create binding");
|
||||
}
|
||||
|
||||
BindingOperations.Apply(this, ValueProperty, ib, null);
|
||||
}
|
||||
|
||||
private IBinding? _lastBinding;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user