Browse Source

Merge branch 'master' into fixes/Warnings/CS0618_Misc

pull/10583/head
Max Katz 3 years ago
committed by GitHub
parent
commit
2e6863081f
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      .gitignore
  2. 5
      .ncrunch/Avalonia.Generators.Tests.v3.ncrunchproject
  3. 5
      .ncrunch/Generators.Sandbox.v3.ncrunchproject
  4. 5
      Avalonia.Desktop.slnf
  5. 2
      native/Avalonia.Native/src/OSX/AvnWindow.mm
  6. 2
      native/Avalonia.Native/src/OSX/WindowBaseImpl.h
  7. 2
      native/Avalonia.Native/src/OSX/WindowImpl.h
  8. 12
      native/Avalonia.Native/src/OSX/WindowImpl.mm
  9. 4
      packages/Avalonia/Avalonia.csproj
  10. 1
      packages/Avalonia/Avalonia.props
  11. 4
      readme.md
  12. 2
      samples/ControlCatalog.Android/Properties/AndroidManifest.xml
  13. 2
      samples/ControlCatalog/MainView.xaml
  14. 2
      samples/ControlCatalog/Pages/ComboBoxPage.xaml.cs
  15. 2
      samples/ControlCatalog/Pages/CompositionPage.axaml.cs
  16. 2
      samples/ControlCatalog/Pages/ContextFlyoutPage.xaml
  17. 4
      samples/ControlCatalog/Pages/ContextMenuPage.xaml
  18. 2
      samples/ControlCatalog/Pages/CursorPage.xaml
  19. 16
      samples/ControlCatalog/Pages/DialogsPage.xaml.cs
  20. 8
      samples/ControlCatalog/Pages/DragAndDropPage.xaml.cs
  21. 2
      samples/ControlCatalog/Pages/ListBoxPage.xaml
  22. 6
      samples/ControlCatalog/Pages/MenuPage.xaml
  23. 8
      samples/ControlCatalog/Pages/NativeEmbedPage.xaml.cs
  24. 6
      samples/ControlCatalog/Pages/NumericUpDownPage.xaml
  25. 2
      samples/ControlCatalog/Pages/RefreshContainerPage.axaml
  26. 4
      samples/ControlCatalog/Pages/ScrollSnapPage.xaml
  27. 4
      samples/ControlCatalog/Pages/ScrollViewerPage.xaml
  28. 2
      samples/ControlCatalog/Pages/TabStripPage.xaml
  29. 2
      samples/ControlCatalog/Pages/ThemePage.axaml.cs
  30. 2
      samples/ControlCatalog/Pages/TransitioningContentControlPage.axaml
  31. 2
      samples/ControlCatalog/Pages/TreeViewPage.xaml
  32. 11
      samples/IntegrationTestApp/MainWindow.axaml
  33. 6
      samples/IntegrationTestApp/MainWindow.axaml.cs
  34. 24
      samples/IntegrationTestApp/ShowWindowTest.axaml
  35. 2
      samples/SampleControls/HamburgerMenu/HamburgerMenu.cs
  36. 1
      src/Android/Avalonia.Android/Avalonia.Android.csproj
  37. 247
      src/Android/Avalonia.Android/Platform/Storage/AndroidStorageItem.cs
  38. 78
      src/Avalonia.Base/AvaloniaProperty.cs
  39. 10
      src/Avalonia.Base/AvaloniaPropertyMetadata.cs
  40. 2
      src/Avalonia.Base/AvaloniaPropertyRegistry.cs
  41. 9
      src/Avalonia.Base/CornerRadius.cs
  42. 2
      src/Avalonia.Base/DirectPropertyMetadata`1.cs
  43. 2
      src/Avalonia.Base/Input/GestureRecognizers/VelocityTracker.cs
  44. 11
      src/Avalonia.Base/Media/BoxShadow.cs
  45. 4
      src/Avalonia.Base/Media/BoxShadows.cs
  46. 4
      src/Avalonia.Base/Media/DrawingGroup.cs
  47. 5
      src/Avalonia.Base/Media/FontFamily.cs
  48. 8
      src/Avalonia.Base/Media/FontManager.cs
  49. 15
      src/Avalonia.Base/Media/Fonts/EmbeddedFontCollection.cs
  50. 2
      src/Avalonia.Base/Media/Fonts/SystemFontCollection.cs
  51. 5
      src/Avalonia.Base/Media/FormattedText.cs
  52. 2
      src/Avalonia.Base/Media/ImageDrawing.cs
  53. 2
      src/Avalonia.Base/Media/Imaging/CroppedBitmap.cs
  54. 2
      src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs
  55. 2
      src/Avalonia.Base/Metadata/InheritDataTypeFromItemsAttribute.cs
  56. 19
      src/Avalonia.Base/PixelRect.cs
  57. 1
      src/Avalonia.Base/Platform/IDrawingContextImpl.cs
  58. 18
      src/Avalonia.Base/Platform/Storage/FileIO/BclStorageFile.cs
  59. 47
      src/Avalonia.Base/Platform/Storage/FileIO/BclStorageFolder.cs
  60. 16
      src/Avalonia.Base/Platform/Storage/IStorageFolder.cs
  61. 13
      src/Avalonia.Base/Platform/Storage/IStorageItem.cs
  62. 8
      src/Avalonia.Base/Point.cs
  63. 2
      src/Avalonia.Base/PropertyStore/EffectiveValue`1.cs
  64. 34
      src/Avalonia.Base/Rect.cs
  65. 12
      src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs
  66. 55
      src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs
  67. 2
      src/Avalonia.Base/Rendering/DirtyRects.cs
  68. 2
      src/Avalonia.Base/Rendering/DisplayDirtyRect.cs
  69. 1
      src/Avalonia.Base/Rendering/SceneGraph/GeometryNode.cs
  70. 1
      src/Avalonia.Base/Rendering/SceneGraph/LineNode.cs
  71. 1
      src/Avalonia.Base/Rendering/SceneGraph/OpacityMaskNode.cs
  72. 1
      src/Avalonia.Base/Rendering/SceneGraph/RectangleNode.cs
  73. 11
      src/Avalonia.Base/Size.cs
  74. 21
      src/Avalonia.Base/StyledProperty.cs
  75. 2
      src/Avalonia.Base/StyledPropertyMetadata`1.cs
  76. 2
      src/Avalonia.Base/Styling/TypeNameAndClassSelector.cs
  77. 10
      src/Avalonia.Base/Thickness.cs
  78. 8
      src/Avalonia.Base/Vector.cs
  79. 63
      src/Avalonia.Base/VisualTree/VisualExtensions.cs
  80. 2
      src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorPicker.xaml
  81. 2
      src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorView.xaml
  82. 2
      src/Avalonia.Controls.ColorPicker/Themes/Simple/ColorPicker.xaml
  83. 2
      src/Avalonia.Controls.ColorPicker/Themes/Simple/ColorView.xaml
  84. 5
      src/Avalonia.Controls.DataGrid/DataGrid.cs
  85. 8
      src/Avalonia.Controls.DataGrid/DataGridCheckBoxColumn.cs
  86. 5
      src/Avalonia.Controls.ItemsRepeater/Controls/ItemsRepeater.cs
  87. 4
      src/Avalonia.Controls.ItemsRepeater/Controls/ViewportManager.cs
  88. 12
      src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.Properties.cs
  89. 3
      src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs
  90. 29
      src/Avalonia.Controls/Automation/Peers/ScrollBarAutomationPeer.cs
  91. 2
      src/Avalonia.Controls/BorderVisual.cs
  92. 20
      src/Avalonia.Controls/ContainerClearingEventArgs.cs
  93. 32
      src/Avalonia.Controls/ContainerIndexChangedEventArgs.cs
  94. 26
      src/Avalonia.Controls/ContainerPreparedEventArgs.cs
  95. 103
      src/Avalonia.Controls/ContextMenu.cs
  96. 2
      src/Avalonia.Controls/DateTimePickers/DatePicker.cs
  97. 2
      src/Avalonia.Controls/DateTimePickers/TimePicker.cs
  98. 8
      src/Avalonia.Controls/Documents/InlineCollection.cs
  99. 4
      src/Avalonia.Controls/Flyouts/MenuFlyout.cs
  100. 16
      src/Avalonia.Controls/Flyouts/PopupFlyoutBase.cs

1
.gitignore

@ -102,6 +102,7 @@ csx
AppPackages/ AppPackages/
# NCrunch # NCrunch
.NCrunch_*/
_NCrunch_*/ _NCrunch_*/
*.ncrunchsolution.user *.ncrunchsolution.user
nCrunchTemp_* nCrunchTemp_*

5
.ncrunch/Avalonia.Generators.Tests.v3.ncrunchproject

@ -0,0 +1,5 @@
<ProjectConfiguration>
<Settings>
<IgnoreThisComponentCompletely>True</IgnoreThisComponentCompletely>
</Settings>
</ProjectConfiguration>

5
.ncrunch/Generators.Sandbox.v3.ncrunchproject

@ -0,0 +1,5 @@
<ProjectConfiguration>
<Settings>
<IgnoreThisComponentCompletely>True</IgnoreThisComponentCompletely>
</Settings>
</ProjectConfiguration>

5
Avalonia.Desktop.slnf

@ -8,9 +8,9 @@
"samples\\GpuInterop\\GpuInterop.csproj", "samples\\GpuInterop\\GpuInterop.csproj",
"samples\\IntegrationTestApp\\IntegrationTestApp.csproj", "samples\\IntegrationTestApp\\IntegrationTestApp.csproj",
"samples\\MiniMvvm\\MiniMvvm.csproj", "samples\\MiniMvvm\\MiniMvvm.csproj",
"samples\\ReactiveUIDemo\\ReactiveUIDemo.csproj",
"samples\\SampleControls\\ControlSamples.csproj", "samples\\SampleControls\\ControlSamples.csproj",
"samples\\Sandbox\\Sandbox.csproj", "samples\\Sandbox\\Sandbox.csproj",
"samples\\ReactiveUIDemo\\ReactiveUIDemo.csproj",
"src\\Avalonia.Base\\Avalonia.Base.csproj", "src\\Avalonia.Base\\Avalonia.Base.csproj",
"src\\Avalonia.Build.Tasks\\Avalonia.Build.Tasks.csproj", "src\\Avalonia.Build.Tasks\\Avalonia.Build.Tasks.csproj",
"src\\Avalonia.Controls.ColorPicker\\Avalonia.Controls.ColorPicker.csproj", "src\\Avalonia.Controls.ColorPicker\\Avalonia.Controls.ColorPicker.csproj",
@ -41,6 +41,7 @@
"src\\Windows\\Avalonia.Direct2D1\\Avalonia.Direct2D1.csproj", "src\\Windows\\Avalonia.Direct2D1\\Avalonia.Direct2D1.csproj",
"src\\Windows\\Avalonia.Win32.Interop\\Avalonia.Win32.Interop.csproj", "src\\Windows\\Avalonia.Win32.Interop\\Avalonia.Win32.Interop.csproj",
"src\\Windows\\Avalonia.Win32\\Avalonia.Win32.csproj", "src\\Windows\\Avalonia.Win32\\Avalonia.Win32.csproj",
"src\\tools\\Avalonia.Generators\\Avalonia.Generators.csproj",
"src\\tools\\DevAnalyzers\\DevAnalyzers.csproj", "src\\tools\\DevAnalyzers\\DevAnalyzers.csproj",
"src\\tools\\DevGenerators\\DevGenerators.csproj", "src\\tools\\DevGenerators\\DevGenerators.csproj",
"src\\tools\\PublicAnalyzers\\Avalonia.Analyzers.csproj", "src\\tools\\PublicAnalyzers\\Avalonia.Analyzers.csproj",
@ -63,4 +64,4 @@
"tests\\Avalonia.UnitTests\\Avalonia.UnitTests.csproj" "tests\\Avalonia.UnitTests\\Avalonia.UnitTests.csproj"
] ]
} }
} }

2
native/Avalonia.Native/src/OSX/AvnWindow.mm

@ -394,7 +394,7 @@
- (BOOL)windowShouldZoom:(NSWindow *_Nonnull)window toFrame:(NSRect)newFrame - (BOOL)windowShouldZoom:(NSWindow *_Nonnull)window toFrame:(NSRect)newFrame
{ {
return true; return _parent->CanZoom();
} }
-(void)windowDidResignKey:(NSNotification *)notification -(void)windowDidResignKey:(NSNotification *)notification

2
native/Avalonia.Native/src/OSX/WindowBaseImpl.h

@ -107,6 +107,8 @@ BEGIN_INTERFACE_MAP()
virtual HRESULT GetInputMethod(IAvnTextInputMethod **retOut) override; virtual HRESULT GetInputMethod(IAvnTextInputMethod **retOut) override;
virtual bool CanZoom() { return false; }
protected: protected:
virtual NSWindowStyleMask CalculateStyleMask() = 0; virtual NSWindowStyleMask CalculateStyleMask() = 0;
virtual void UpdateStyle(); virtual void UpdateStyle();

2
native/Avalonia.Native/src/OSX/WindowImpl.h

@ -97,6 +97,8 @@ BEGIN_INTERFACE_MAP()
bool CanBecomeKeyWindow (); bool CanBecomeKeyWindow ();
bool CanZoom() override { return _isEnabled && _canResize; }
protected: protected:
virtual NSWindowStyleMask CalculateStyleMask() override; virtual NSWindowStyleMask CalculateStyleMask() override;
void UpdateStyle () override; void UpdateStyle () override;

12
native/Avalonia.Native/src/OSX/WindowImpl.mm

@ -281,10 +281,13 @@ HRESULT WindowImpl::SetDecorations(SystemDecorations value) {
case SystemDecorationsFull: case SystemDecorationsFull:
[Window setHasShadow:YES]; [Window setHasShadow:YES];
[Window setTitleVisibility:NSWindowTitleVisible];
[Window setTitlebarAppearsTransparent:NO];
[Window setTitle:_lastTitle]; [Window setTitle:_lastTitle];
if (!_isClientAreaExtended) {
[Window setTitleVisibility:NSWindowTitleVisible];
[Window setTitlebarAppearsTransparent:NO];
}
if (currentWindowState == Maximized) { if (currentWindowState == Maximized) {
auto newFrame = [Window contentRectForFrameRect:[Window frame]].size; auto newFrame = [Window contentRectForFrameRect:[Window frame]].size;
@ -611,7 +614,8 @@ void WindowImpl::UpdateStyle() {
} }
bool wantsChrome = (_extendClientHints & AvnSystemChrome) || (_extendClientHints & AvnPreferSystemChrome); bool wantsChrome = (_extendClientHints & AvnSystemChrome) || (_extendClientHints & AvnPreferSystemChrome);
bool hasTrafficLights = _isClientAreaExtended ? wantsChrome : _decorations == SystemDecorationsFull; bool hasTrafficLights = (_decorations == SystemDecorationsFull) &&
(_isClientAreaExtended ? wantsChrome : true);
NSButton* closeButton = [Window standardWindowButton:NSWindowCloseButton]; NSButton* closeButton = [Window standardWindowButton:NSWindowCloseButton];
NSButton* miniaturizeButton = [Window standardWindowButton:NSWindowMiniaturizeButton]; NSButton* miniaturizeButton = [Window standardWindowButton:NSWindowMiniaturizeButton];
@ -622,5 +626,5 @@ void WindowImpl::UpdateStyle() {
[miniaturizeButton setHidden:!hasTrafficLights]; [miniaturizeButton setHidden:!hasTrafficLights];
[miniaturizeButton setEnabled:_isEnabled]; [miniaturizeButton setEnabled:_isEnabled];
[zoomButton setHidden:!hasTrafficLights]; [zoomButton setHidden:!hasTrafficLights];
[zoomButton setEnabled:_isEnabled && _canResize]; [zoomButton setEnabled:CanZoom()];
} }

4
packages/Avalonia/Avalonia.csproj

@ -43,6 +43,10 @@
<Pack>true</Pack> <Pack>true</Pack>
<PackagePath>build\;buildTransitive\</PackagePath> <PackagePath>build\;buildTransitive\</PackagePath>
</Content> </Content>
<Content Include="..\..\src\tools\Avalonia.Generators\Avalonia.Generators.props">
<Pack>true</Pack>
<PackagePath>build\;buildTransitive\</PackagePath>
</Content>
<Content Include="*.targets"> <Content Include="*.targets">
<Pack>true</Pack> <Pack>true</Pack>
<PackagePath>build\;buildTransitive\</PackagePath> <PackagePath>build\;buildTransitive\</PackagePath>

1
packages/Avalonia/Avalonia.props

@ -6,6 +6,7 @@
<AvaloniaUseExternalMSBuild>false</AvaloniaUseExternalMSBuild> <AvaloniaUseExternalMSBuild>false</AvaloniaUseExternalMSBuild>
</PropertyGroup> </PropertyGroup>
<Import Project="$(MSBuildThisFileDirectory)\AvaloniaBuildTasks.props"/> <Import Project="$(MSBuildThisFileDirectory)\AvaloniaBuildTasks.props"/>
<Import Project="$(MSBuildThisFileDirectory)\Avalonia.Generators.props"/>
<!-- Allow loading the AvaloniaVS extension when referencing the Avalonia nuget package --> <!-- Allow loading the AvaloniaVS extension when referencing the Avalonia nuget package -->
<ItemGroup> <ItemGroup>

4
readme.md

@ -5,6 +5,10 @@
<br /> <br />
[![NuGet](https://img.shields.io/nuget/v/Avalonia.svg)](https://www.nuget.org/packages/Avalonia) [![downloads](https://img.shields.io/nuget/dt/avalonia)](https://www.nuget.org/packages/Avalonia) ![Size](https://img.shields.io/github/repo-size/avaloniaui/avalonia.svg) [![NuGet](https://img.shields.io/nuget/v/Avalonia.svg)](https://www.nuget.org/packages/Avalonia) [![downloads](https://img.shields.io/nuget/dt/avalonia)](https://www.nuget.org/packages/Avalonia) ![Size](https://img.shields.io/github/repo-size/avaloniaui/avalonia.svg)
# ⚠️ **v11 Update - Pausing community contributions**
for more information see [this](https://github.com/AvaloniaUI/Avalonia/discussions/10599) discussion.
## 📖 About ## 📖 About
Avalonia is a cross-platform UI framework for dotnet, providing a flexible styling system and supporting a wide range of Operating Systems such as Windows, Linux, macOS. Avalonia is mature and production ready. We also have in beta release support for iOS, Android and in early stages support for browser via WASM. Avalonia is a cross-platform UI framework for dotnet, providing a flexible styling system and supporting a wide range of Operating Systems such as Windows, Linux, macOS. Avalonia is mature and production ready. We also have in beta release support for iOS, Android and in early stages support for browser via WASM.

2
samples/ControlCatalog.Android/Properties/AndroidManifest.xml

@ -2,4 +2,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="auto"> <manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="auto">
<application android:label="ControlCatalog.Android" android:icon="@drawable/Icon"></application> <application android:label="ControlCatalog.Android" android:icon="@drawable/Icon"></application>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE " />
</manifest> </manifest>

2
samples/ControlCatalog/MainView.xaml

@ -241,7 +241,7 @@
</ComboBox.Items> </ComboBox.Items>
</ComboBox> </ComboBox>
<ComboBox HorizontalAlignment="Stretch" <ComboBox HorizontalAlignment="Stretch"
Items="{Binding WindowStates}" ItemsSource="{Binding WindowStates}"
SelectedItem="{Binding WindowState}" /> SelectedItem="{Binding WindowState}" />
</StackPanel> </StackPanel>
</Flyout> </Flyout>

2
samples/ControlCatalog/Pages/ComboBoxPage.xaml.cs

@ -18,7 +18,7 @@ namespace ControlCatalog.Pages
{ {
AvaloniaXamlLoader.Load(this); AvaloniaXamlLoader.Load(this);
var fontComboBox = this.Get<ComboBox>("fontComboBox"); var fontComboBox = this.Get<ComboBox>("fontComboBox");
fontComboBox.Items = FontManager.Current.SystemFonts; fontComboBox.ItemsSource = FontManager.Current.SystemFonts;
fontComboBox.SelectedIndex = 0; fontComboBox.SelectedIndex = 0;
} }
} }

2
samples/ControlCatalog/Pages/CompositionPage.axaml.cs

@ -32,7 +32,7 @@ public partial class CompositionPage : UserControl
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{ {
base.OnAttachedToVisualTree(e); base.OnAttachedToVisualTree(e);
this.Get<ItemsControl>("Items").Items = CreateColorItems(); this.Get<ItemsControl>("Items").ItemsSource = CreateColorItems();
} }

2
samples/ControlCatalog/Pages/ContextFlyoutPage.xaml

@ -61,7 +61,7 @@
<Border.Styles> <Border.Styles>
<Style Selector="MenuFlyoutPresenter MenuItem" x:DataType="viewModels:MenuItemViewModel"> <Style Selector="MenuFlyoutPresenter MenuItem" x:DataType="viewModels:MenuItemViewModel">
<Setter Property="Header" Value="{Binding Header}"/> <Setter Property="Header" Value="{Binding Header}"/>
<Setter Property="Items" Value="{Binding Items}"/> <Setter Property="ItemsSource" Value="{Binding Items}"/>
<Setter Property="Command" Value="{Binding Command}"/> <Setter Property="Command" Value="{Binding Command}"/>
<Setter Property="CommandParameter" Value="{Binding CommandParameter}"/> <Setter Property="CommandParameter" Value="{Binding CommandParameter}"/>
</Style> </Style>

4
samples/ControlCatalog/Pages/ContextMenuPage.xaml

@ -51,13 +51,13 @@
<Border.Styles> <Border.Styles>
<Style Selector="ContextMenu MenuItem" x:DataType="viewModels:MenuItemViewModel"> <Style Selector="ContextMenu MenuItem" x:DataType="viewModels:MenuItemViewModel">
<Setter Property="Header" Value="{Binding Header}"/> <Setter Property="Header" Value="{Binding Header}"/>
<Setter Property="Items" Value="{Binding Items}"/> <Setter Property="ItemsSource" Value="{Binding Items}"/>
<Setter Property="Command" Value="{Binding Command}"/> <Setter Property="Command" Value="{Binding Command}"/>
<Setter Property="CommandParameter" Value="{Binding CommandParameter}"/> <Setter Property="CommandParameter" Value="{Binding CommandParameter}"/>
</Style> </Style>
</Border.Styles> </Border.Styles>
<Border.ContextMenu> <Border.ContextMenu>
<ContextMenu Items="{Binding MenuItems}" /> <ContextMenu ItemsSource="{Binding MenuItems}" />
</Border.ContextMenu> </Border.ContextMenu>
<TextBlock Text="Dynamically Generated"/> <TextBlock Text="Dynamically Generated"/>
</Border> </Border>

2
samples/ControlCatalog/Pages/CursorPage.xaml

@ -8,7 +8,7 @@
<TextBlock Classes="h2">Defines a cursor (mouse pointer)</TextBlock> <TextBlock Classes="h2">Defines a cursor (mouse pointer)</TextBlock>
</StackPanel> </StackPanel>
<ListBox Grid.Row="1" Items="{Binding StandardCursors}" Margin="0 8 8 8"> <ListBox Grid.Row="1" ItemsSource="{Binding StandardCursors}" Margin="0 8 8 8">
<ListBox.Styles> <ListBox.Styles>
<Style Selector="ListBoxItem"> <Style Selector="ListBoxItem">
<Setter Property="Cursor" Value="{Binding Cursor}" x:DataType="viewModels:StandardCursorModel"/> <Setter Property="Cursor" Value="{Binding Cursor}" x:DataType="viewModels:StandardCursorModel"/>

16
samples/ControlCatalog/Pages/DialogsPage.xaml.cs

@ -106,7 +106,7 @@ namespace ControlCatalog.Pages
Directory = initialDirectory, Directory = initialDirectory,
InitialFileName = initialFileName InitialFileName = initialFileName
}.ShowAsync(GetWindow()); }.ShowAsync(GetWindow());
results.Items = result; results.ItemsSource = result;
resultsVisible.IsVisible = result?.Any() == true; resultsVisible.IsVisible = result?.Any() == true;
}; };
this.Get<Button>("OpenMultipleFiles").Click += async delegate this.Get<Button>("OpenMultipleFiles").Click += async delegate
@ -118,7 +118,7 @@ namespace ControlCatalog.Pages
Directory = lastSelectedDirectory?.Path is {IsAbsoluteUri:true} path ? path.LocalPath : null, Directory = lastSelectedDirectory?.Path is {IsAbsoluteUri:true} path ? path.LocalPath : null,
AllowMultiple = true AllowMultiple = true
}.ShowAsync(GetWindow()); }.ShowAsync(GetWindow());
results.Items = result; results.ItemsSource = result;
resultsVisible.IsVisible = result?.Any() == true; resultsVisible.IsVisible = result?.Any() == true;
}; };
this.Get<Button>("SaveFile").Click += async delegate this.Get<Button>("SaveFile").Click += async delegate
@ -132,7 +132,7 @@ namespace ControlCatalog.Pages
DefaultExtension = filters?.Any() == true ? "txt" : null, DefaultExtension = filters?.Any() == true ? "txt" : null,
InitialFileName = "test.txt" InitialFileName = "test.txt"
}.ShowAsync(GetWindow()); }.ShowAsync(GetWindow());
results.Items = new[] { result }; results.ItemsSource = new[] { result };
resultsVisible.IsVisible = result != null; resultsVisible.IsVisible = result != null;
}; };
this.Get<Button>("SelectFolder").Click += async delegate this.Get<Button>("SelectFolder").Click += async delegate
@ -149,7 +149,7 @@ namespace ControlCatalog.Pages
else else
{ {
SetFolder(await GetStorageProvider().TryGetFolderFromPathAsync(result)); SetFolder(await GetStorageProvider().TryGetFolderFromPathAsync(result));
results.Items = new[] { result }; results.ItemsSource = new[] { result };
resultsVisible.IsVisible = true; resultsVisible.IsVisible = true;
} }
}; };
@ -164,7 +164,7 @@ namespace ControlCatalog.Pages
{ {
AllowDirectorySelection = true AllowDirectorySelection = true
}); });
results.Items = result; results.ItemsSource = result;
resultsVisible.IsVisible = result?.Any() == true; resultsVisible.IsVisible = result?.Any() == true;
}; };
this.Get<Button>("DecoratedWindow").Click += delegate this.Get<Button>("DecoratedWindow").Click += delegate
@ -324,15 +324,15 @@ namespace ControlCatalog.Pages
mappedResults.Add("+> " + FullPathOrName(selectedItem)); mappedResults.Add("+> " + FullPathOrName(selectedItem));
if (selectedItem is IStorageFolder folder) if (selectedItem is IStorageFolder folder)
{ {
foreach (var innerItems in await folder.GetItemsAsync()) await foreach (var innerItem in folder.GetItemsAsync())
{ {
mappedResults.Add("++> " + FullPathOrName(innerItems)); mappedResults.Add("++> " + FullPathOrName(innerItem));
} }
} }
} }
} }
results.Items = mappedResults; results.ItemsSource = mappedResults;
resultsVisible.IsVisible = mappedResults.Any(); resultsVisible.IsVisible = mappedResults.Any();
} }
} }

8
samples/ControlCatalog/Pages/DragAndDropPage.xaml.cs

@ -104,8 +104,12 @@ namespace ControlCatalog.Pages
} }
else if (item is IStorageFolder folder) else if (item is IStorageFolder folder)
{ {
var items = await folder.GetItemsAsync(); var childrenCount = 0;
contentStr += $"Folder {item.Name}: items {items.Count}{Environment.NewLine}{Environment.NewLine}"; await foreach (var _ in folder.GetItemsAsync())
{
childrenCount++;
}
contentStr += $"Folder {item.Name}: items {childrenCount}{Environment.NewLine}{Environment.NewLine}";
} }
} }

2
samples/ControlCatalog/Pages/ListBoxPage.xaml

@ -30,7 +30,7 @@
<Button Command="{Binding RemoveItemCommand}">Remove</Button> <Button Command="{Binding RemoveItemCommand}">Remove</Button>
<Button Command="{Binding SelectRandomItemCommand}">Select Random Item</Button> <Button Command="{Binding SelectRandomItemCommand}">Select Random Item</Button>
</StackPanel> </StackPanel>
<ListBox Items="{Binding Items}" <ListBox ItemsSource="{Binding Items}"
Selection="{Binding Selection}" Selection="{Binding Selection}"
DisplayMemberBinding="{Binding (viewModels:ItemModel).ID, StringFormat='{}Item {0:N0}'}" DisplayMemberBinding="{Binding (viewModels:ItemModel).ID, StringFormat='{}Item {0:N0}'}"
AutoScrollToSelectedItem="{Binding AutoScrollToSelectedItem}" AutoScrollToSelectedItem="{Binding AutoScrollToSelectedItem}"

6
samples/ControlCatalog/Pages/MenuPage.xaml

@ -45,11 +45,11 @@
<StackPanel> <StackPanel>
<TextBlock Classes="h3" Margin="4 8">Dyanamically generated</TextBlock> <TextBlock Classes="h3" Margin="4 8">Dyanamically generated</TextBlock>
<Menu Items="{Binding MenuItems}"> <Menu ItemsSource="{Binding MenuItems}">
<Menu.Styles> <Menu.Styles>
<Style Selector="MenuItem" x:DataType="viewModels:MenuItemViewModel"> <Style Selector="MenuItem" x:DataType="viewModels:MenuItemViewModel">
<Setter Property="Header" Value="{Binding Header}"/> <Setter Property="Header" Value="{Binding Header}"/>
<Setter Property="Items" Value="{Binding Items}"/> <Setter Property="ItemsSource" Value="{Binding Items}"/>
<Setter Property="Command" Value="{Binding Command}"/> <Setter Property="Command" Value="{Binding Command}"/>
<Setter Property="CommandParameter" Value="{Binding CommandParameter}"/> <Setter Property="CommandParameter" Value="{Binding CommandParameter}"/>
</Style> </Style>
@ -68,7 +68,7 @@
<Separator/> <Separator/>
<MenuItem Header="Execu_te Script..." /> <MenuItem Header="Execu_te Script..." />
<Separator/> <Separator/>
<MenuItem Header="_Recent" Items="{Binding RecentItems}"> <MenuItem Header="_Recent" ItemsSource="{Binding RecentItems}">
<MenuItem.Styles> <MenuItem.Styles>
<Style Selector="MenuItem" x:DataType="viewModels:MenuItemViewModel"> <Style Selector="MenuItem" x:DataType="viewModels:MenuItemViewModel">
<Setter Property="Header" Value="{Binding Header}"/> <Setter Property="Header" Value="{Binding Header}"/>

8
samples/ControlCatalog/Pages/NativeEmbedPage.xaml.cs

@ -33,10 +33,10 @@ namespace ControlCatalog.Pages
{ {
new ContextMenu() new ContextMenu()
{ {
Items = new List<MenuItem> Items =
{ {
new MenuItem() { Header = "Test" }, new MenuItem() { Header = "Test" } new MenuItem() { Header = "Test" }, new MenuItem() { Header = "Test" }
} }
}.Open((Control)sender); }.Open((Control)sender);
} }

6
samples/ControlCatalog/Pages/NumericUpDownPage.xaml

@ -27,7 +27,7 @@
</Grid> </Grid>
<Grid Grid.Row="0" Grid.Column="1" Margin="8" ColumnDefinitions="Auto, 120" RowDefinitions="Auto,Auto,Auto,Auto,Auto"> <Grid Grid.Row="0" Grid.Column="1" Margin="8" ColumnDefinitions="Auto, 120" RowDefinitions="Auto,Auto,Auto,Auto,Auto">
<TextBlock Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Margin="2">FormatString:</TextBlock> <TextBlock Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Margin="2">FormatString:</TextBlock>
<ComboBox Grid.Row="0" Grid.Column="1" Items="{Binding Formats}" SelectedItem="{Binding SelectedFormat}" <ComboBox Grid.Row="0" Grid.Column="1" ItemsSource="{Binding Formats}" SelectedItem="{Binding SelectedFormat}"
VerticalAlignment="Center" Margin="2"> VerticalAlignment="Center" Margin="2">
<ComboBox.ItemTemplate> <ComboBox.ItemTemplate>
<DataTemplate> <DataTemplate>
@ -41,11 +41,11 @@
</ComboBox> </ComboBox>
<TextBlock Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" Margin="2">ButtonSpinnerLocation:</TextBlock> <TextBlock Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" Margin="2">ButtonSpinnerLocation:</TextBlock>
<ComboBox Grid.Row="1" Grid.Column="1" Items="{Binding SpinnerLocations}" SelectedItem="{Binding #upDown.ButtonSpinnerLocation}" <ComboBox Grid.Row="1" Grid.Column="1" ItemsSource="{Binding SpinnerLocations}" SelectedItem="{Binding #upDown.ButtonSpinnerLocation}"
VerticalAlignment="Center" Margin="2"/> VerticalAlignment="Center" Margin="2"/>
<TextBlock Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" Margin="2">CultureInfo:</TextBlock> <TextBlock Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" Margin="2">CultureInfo:</TextBlock>
<ComboBox x:Name="CultureSelector" Grid.Row="2" Grid.Column="1" Items="{Binding Cultures}" <ComboBox x:Name="CultureSelector" Grid.Row="2" Grid.Column="1" ItemsSource="{Binding Cultures}"
VerticalAlignment="Center" Margin="2"/> VerticalAlignment="Center" Margin="2"/>
<TextBlock Grid.Row="3" Grid.Column="0" VerticalAlignment="Center" Margin="2">Watermark:</TextBlock> <TextBlock Grid.Row="3" Grid.Column="0" VerticalAlignment="Center" Margin="2">Watermark:</TextBlock>

2
samples/ControlCatalog/Pages/RefreshContainerPage.axaml

@ -21,7 +21,7 @@
Margin="5"> Margin="5">
<ListBox HorizontalAlignment="Stretch" <ListBox HorizontalAlignment="Stretch"
VerticalAlignment="Top" VerticalAlignment="Top"
Items="{Binding Items}"/> ItemsSource="{Binding Items}"/>
</RefreshContainer> </RefreshContainer>
</DockPanel> </DockPanel>
</UserControl> </UserControl>

4
samples/ControlCatalog/Pages/ScrollSnapPage.xaml

@ -16,14 +16,14 @@
<StackPanel Orientation="Vertical" <StackPanel Orientation="Vertical"
Spacing="4"> Spacing="4">
<TextBlock Text="Snap Point Type" /> <TextBlock Text="Snap Point Type" />
<ComboBox Items="{Binding AvailableSnapPointsType}" <ComboBox ItemsSource="{Binding AvailableSnapPointsType}"
SelectedItem="{Binding SnapPointsType}" /> SelectedItem="{Binding SnapPointsType}" />
</StackPanel> </StackPanel>
<StackPanel Orientation="Vertical" <StackPanel Orientation="Vertical"
Spacing="4"> Spacing="4">
<TextBlock Text="Snap Point Alignment" /> <TextBlock Text="Snap Point Alignment" />
<ComboBox Items="{Binding AvailableSnapPointsAlignment}" <ComboBox ItemsSource="{Binding AvailableSnapPointsAlignment}"
SelectedItem="{Binding SnapPointsAlignment}" /> SelectedItem="{Binding SnapPointsAlignment}" />
</StackPanel> </StackPanel>

4
samples/ControlCatalog/Pages/ScrollViewerPage.xaml

@ -13,12 +13,12 @@
<StackPanel Orientation="Vertical" Spacing="4"> <StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Text="Horizontal Scroll" /> <TextBlock Text="Horizontal Scroll" />
<ComboBox Items="{Binding AvailableVisibility}" SelectedItem="{Binding HorizontalScrollVisibility}" /> <ComboBox ItemsSource="{Binding AvailableVisibility}" SelectedItem="{Binding HorizontalScrollVisibility}" />
</StackPanel> </StackPanel>
<StackPanel Orientation="Vertical" Spacing="4"> <StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Text="Vertical Scroll" /> <TextBlock Text="Vertical Scroll" />
<ComboBox Items="{Binding AvailableVisibility}" SelectedItem="{Binding VerticalScrollVisibility}" /> <ComboBox ItemsSource="{Binding AvailableVisibility}" SelectedItem="{Binding VerticalScrollVisibility}" />
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>

2
samples/ControlCatalog/Pages/TabStripPage.xaml

@ -18,7 +18,7 @@
<Separator Margin="0 16"/> <Separator Margin="0 16"/>
<TextBlock Classes="h1">Dynamically generated</TextBlock> <TextBlock Classes="h1">Dynamically generated</TextBlock>
<TabStrip Items="{Binding Tabs}"> <TabStrip ItemsSource="{Binding Tabs}">
<TabStrip.Styles> <TabStrip.Styles>
<Style Selector="TabStripItem" x:DataType="viewModels:TabControlPageViewModelItem"> <Style Selector="TabStripItem" x:DataType="viewModels:TabControlPageViewModelItem">
<Setter Property="IsEnabled" Value="{Binding IsEnabled}"/> <Setter Property="IsEnabled" Value="{Binding IsEnabled}"/>

2
samples/ControlCatalog/Pages/ThemePage.axaml.cs

@ -12,7 +12,7 @@ namespace ControlCatalog.Pages
{ {
InitializeComponent(); InitializeComponent();
Selector.Items = new[] Selector.ItemsSource = new[]
{ {
ThemeVariant.Default, ThemeVariant.Default,
ThemeVariant.Dark, ThemeVariant.Dark,

2
samples/ControlCatalog/Pages/TransitioningContentControlPage.axaml

@ -53,7 +53,7 @@
<StackPanel Margin="5" Spacing="5" Grid.IsSharedSizeScope="True"> <StackPanel Margin="5" Spacing="5" Grid.IsSharedSizeScope="True">
<HeaderedContentControl Header="Select a transition"> <HeaderedContentControl Header="Select a transition">
<ComboBox Items="{Binding PageTransitions}" SelectedItem="{Binding SelectedTransition}" /> <ComboBox ItemsSource="{Binding PageTransitions}" SelectedItem="{Binding SelectedTransition}" />
</HeaderedContentControl> </HeaderedContentControl>
<HeaderedContentControl Header="Duration"> <HeaderedContentControl Header="Duration">
<NumericUpDown Value="{Binding Duration}" Increment="250" Minimum="100" /> <NumericUpDown Value="{Binding Duration}" Increment="250" Minimum="100" />

2
samples/ControlCatalog/Pages/TreeViewPage.xaml

@ -11,7 +11,7 @@
HorizontalAlignment="Center" HorizontalAlignment="Center"
Spacing="16"> Spacing="16">
<StackPanel Orientation="Vertical" Spacing="8"> <StackPanel Orientation="Vertical" Spacing="8">
<TreeView Items="{Binding Items}" SelectedItems="{Binding SelectedItems}" SelectionMode="{Binding SelectionMode}" Width="250" Height="350"> <TreeView ItemsSource="{Binding Items}" SelectedItems="{Binding SelectedItems}" SelectionMode="{Binding SelectionMode}" Width="250" Height="350">
<TreeView.ItemTemplate> <TreeView.ItemTemplate>
<TreeDataTemplate ItemsSource="{Binding Children}"> <TreeDataTemplate ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Header}"/> <TextBlock Text="{Binding Header}"/>

11
samples/IntegrationTestApp/MainWindow.axaml

@ -109,7 +109,7 @@
<StackPanel DockPanel.Dock="Bottom"> <StackPanel DockPanel.Dock="Bottom">
<Button Name="ListBoxSelectionClear">Clear Selection</Button> <Button Name="ListBoxSelectionClear">Clear Selection</Button>
</StackPanel> </StackPanel>
<ListBox Name="BasicListBox" Items="{Binding ListBoxItems}" SelectionMode="Multiple"/> <ListBox Name="BasicListBox" ItemsSource="{Binding ListBoxItems}" SelectionMode="Multiple"/>
</DockPanel> </DockPanel>
</TabItem> </TabItem>
@ -151,6 +151,12 @@
<ComboBoxItem Name="ShowWindowStateMaximized">Maximized</ComboBoxItem> <ComboBoxItem Name="ShowWindowStateMaximized">Maximized</ComboBoxItem>
<ComboBoxItem Name="ShowWindowStateFullScreen">FullScreen</ComboBoxItem> <ComboBoxItem Name="ShowWindowStateFullScreen">FullScreen</ComboBoxItem>
</ComboBox> </ComboBox>
<ComboBox Name="ShowWindowSystemDecorations" SelectedIndex="2">
<ComboBoxItem Name="ShowWindowSystemDecorationsNone">None</ComboBoxItem>
<ComboBoxItem Name="ShowWindowSystemDecorationsBorderOnly">BorderOnly</ComboBoxItem>
<ComboBoxItem Name="ShowWindowSystemDecorationsFull">Full</ComboBoxItem>
</ComboBox>
<CheckBox Name="ShowWindowExtendClientAreaToDecorationsHint">ExtendClientAreaToDecorationsHint</CheckBox>
<CheckBox Name="ShowWindowCanResize" IsChecked="True">Can Resize</CheckBox> <CheckBox Name="ShowWindowCanResize" IsChecked="True">Can Resize</CheckBox>
<Button Name="ShowWindow">Show Window</Button> <Button Name="ShowWindow">Show Window</Button>
<Button Name="SendToBack">Send to Back</Button> <Button Name="SendToBack">Send to Back</Button>
@ -167,6 +173,9 @@
<TabItem Header="SliderTab"> <TabItem Header="SliderTab">
<Slider VerticalAlignment="Top" Name="Slider" Value="30"/> <Slider VerticalAlignment="Top" Name="Slider" Value="30"/>
</TabItem> </TabItem>
<TabItem Header="ScrollBarTab">
<ScrollBar Name="MyScrollBar" Orientation="Horizontal" AllowAutoHide="False" Width="200" Height="30" Value="20"/>
</TabItem>
</TabControl> </TabControl>
</DockPanel> </DockPanel>
</Window> </Window>

6
samples/IntegrationTestApp/MainWindow.axaml.cs

@ -68,6 +68,8 @@ namespace IntegrationTestApp
var locationComboBox = this.GetControl<ComboBox>("ShowWindowLocation"); var locationComboBox = this.GetControl<ComboBox>("ShowWindowLocation");
var stateComboBox = this.GetControl<ComboBox>("ShowWindowState"); var stateComboBox = this.GetControl<ComboBox>("ShowWindowState");
var size = !string.IsNullOrWhiteSpace(sizeTextBox.Text) ? Size.Parse(sizeTextBox.Text) : (Size?)null; var size = !string.IsNullOrWhiteSpace(sizeTextBox.Text) ? Size.Parse(sizeTextBox.Text) : (Size?)null;
var systemDecorations = this.GetControl<ComboBox>("ShowWindowSystemDecorations");
var extendClientArea = this.GetControl<CheckBox>("ShowWindowExtendClientAreaToDecorationsHint");
var canResizeCheckBox = this.GetControl<CheckBox>("ShowWindowCanResize"); var canResizeCheckBox = this.GetControl<CheckBox>("ShowWindowCanResize");
var owner = (Window)this.GetVisualRoot()!; var owner = (Window)this.GetVisualRoot()!;
@ -95,6 +97,8 @@ namespace IntegrationTestApp
} }
sizeTextBox.Text = string.Empty; sizeTextBox.Text = string.Empty;
window.ExtendClientAreaToDecorationsHint = extendClientArea.IsChecked ?? false;
window.SystemDecorations = (SystemDecorations)systemDecorations.SelectedIndex;
window.WindowState = (WindowState)stateComboBox.SelectedIndex; window.WindowState = (WindowState)stateComboBox.SelectedIndex;
switch (modeComboBox.SelectedIndex) switch (modeComboBox.SelectedIndex)
@ -158,7 +162,7 @@ namespace IntegrationTestApp
var popup = new Popup var popup = new Popup
{ {
WindowManagerAddShadowHint = false, WindowManagerAddShadowHint = false,
PlacementMode = PlacementMode.AnchorAndGravity, Placement = PlacementMode.AnchorAndGravity,
PlacementAnchor = PopupAnchor.Top, PlacementAnchor = PopupAnchor.Top,
PlacementGravity = PopupGravity.Bottom, PlacementGravity = PopupGravity.Bottom,
Width= 200, Width= 200,

24
samples/IntegrationTestApp/ShowWindowTest.axaml

@ -6,7 +6,7 @@
x:DataType="Window" x:DataType="Window"
Title="Show Window Test"> Title="Show Window Test">
<integrationTestApp:MeasureBorder Name="MyBorder"> <integrationTestApp:MeasureBorder Name="MyBorder">
<Grid ColumnDefinitions="Auto,Auto" RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto"> <Grid ColumnDefinitions="Auto,Auto" RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto">
<Label Grid.Column="0" Grid.Row="1">Client Size</Label> <Label Grid.Column="0" Grid.Row="1">Client Size</Label>
<TextBox Name="CurrentClientSize" Grid.Column="1" Grid.Row="1" IsReadOnly="True" <TextBox Name="CurrentClientSize" Grid.Column="1" Grid.Row="1" IsReadOnly="True"
Text="{Binding ClientSize, Mode=OneWay}" /> Text="{Binding ClientSize, Mode=OneWay}" />
@ -35,13 +35,25 @@
<ComboBoxItem Name="WindowStateFullScreen">FullScreen</ComboBoxItem> <ComboBoxItem Name="WindowStateFullScreen">FullScreen</ComboBoxItem>
</ComboBox> </ComboBox>
<Label Grid.Column="0" Grid.Row="8">Order (mac)</Label> <Label Grid.Column="0" Grid.Row="8">SystemDecorations</Label>
<TextBox Name="CurrentOrder" Grid.Column="1" Grid.Row="8" IsReadOnly="True" /> <ComboBox Name="CurrentSystemDecorations" Grid.Column="1" Grid.Row="8" SelectedIndex="{Binding SystemDecorations}">
<ComboBoxItem Name="SystemDecorationsNone">None</ComboBoxItem>
<ComboBoxItem Name="SystemDecorationsBorderOnly">BorderOnly</ComboBoxItem>
<ComboBoxItem Name="SystemDecorationsFull">Full</ComboBoxItem>
</ComboBox>
<CheckBox Name="CurrentExtendClientAreaToDecorationsHint" Grid.ColumnSpan="2" Grid.Row="9"
IsChecked="{Binding ExtendClientAreaToDecorationsHint}">
ExtendClientAreaToDecorationsHint
</CheckBox>
<Label Grid.Column="0" Grid.Row="10">Order (mac)</Label>
<TextBox Name="CurrentOrder" Grid.Column="1" Grid.Row="10" IsReadOnly="True" />
<Label Grid.Row="9" Content="MeasuredWith:" /> <Label Grid.Row="11" Content="MeasuredWith:" />
<TextBlock Grid.Column="1" Grid.Row="9" Name="CurrentMeasuredWithText" Text="{Binding #MyBorder.MeasuredWith}" /> <TextBlock Grid.Column="1" Grid.Row="11" Name="CurrentMeasuredWithText" Text="{Binding #MyBorder.MeasuredWith}" />
<Button Name="HideButton" Grid.Row="10" Command="{Binding $parent[Window].Hide}">Hide</Button> <Button Name="HideButton" Grid.Row="12" Command="{Binding $parent[Window].Hide}">Hide</Button>
</Grid> </Grid>
</integrationTestApp:MeasureBorder> </integrationTestApp:MeasureBorder>

2
samples/SampleControls/HamburgerMenu/HamburgerMenu.cs

@ -57,7 +57,7 @@ namespace ControlSamples
{ {
if (_splitView is not null && _splitView.DisplayMode == SplitViewDisplayMode.Overlay) if (_splitView is not null && _splitView.DisplayMode == SplitViewDisplayMode.Overlay)
{ {
_splitView.SetValue(SplitView.IsPaneOpenProperty, false, Avalonia.Data.BindingPriority.Animation); _splitView.SetCurrentValue(SplitView.IsPaneOpenProperty, false);
} }
} }
} }

1
src/Android/Avalonia.Android/Avalonia.Android.csproj

@ -10,6 +10,7 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\..\packages\Avalonia\Avalonia.csproj" /> <ProjectReference Include="..\..\..\packages\Avalonia\Avalonia.csproj" />
<PackageReference Include="Xamarin.AndroidX.AppCompat" Version="1.3.1.3" /> <PackageReference Include="Xamarin.AndroidX.AppCompat" Version="1.3.1.3" />
<PackageReference Include="Xamarin.AndroidX.DocumentFile" Version="1.0.1.16" />
<PackageReference Include="Xamarin.AndroidX.Lifecycle.ViewModel" Version="2.3.1.3" /> <PackageReference Include="Xamarin.AndroidX.Lifecycle.ViewModel" Version="2.3.1.3" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

247
src/Android/Avalonia.Android/Platform/Storage/AndroidStorageItem.cs

@ -8,7 +8,10 @@ using System.Threading.Tasks;
using Android; using Android;
using Android.App; using Android.App;
using Android.Content; using Android.Content;
using Android.OS;
using Android.Provider; using Android.Provider;
using Android.Webkit;
using AndroidX.DocumentFile.Provider;
using Avalonia.Logging; using Avalonia.Logging;
using Avalonia.Platform.Storage; using Avalonia.Platform.Storage;
using Java.Lang; using Java.Lang;
@ -22,20 +25,25 @@ internal abstract class AndroidStorageItem : IStorageBookmarkItem
{ {
private Activity? _activity; private Activity? _activity;
private readonly bool _needsExternalFilesPermission; private readonly bool _needsExternalFilesPermission;
private readonly AndroidStorageFolder? _parent;
private readonly AndroidUri? _permissionRoot;
protected AndroidStorageItem(Activity activity, AndroidUri uri, bool needsExternalFilesPermission) protected AndroidStorageItem(Activity activity, AndroidUri uri, bool needsExternalFilesPermission, AndroidStorageFolder? parent = null, AndroidUri? permissionRoot = null)
{ {
_activity = activity; _activity = activity;
_needsExternalFilesPermission = needsExternalFilesPermission; _needsExternalFilesPermission = needsExternalFilesPermission;
_parent = parent;
_permissionRoot = permissionRoot ?? parent?.Uri ?? Uri;
Uri = uri; Uri = uri;
} }
internal AndroidUri Uri { get; } internal AndroidUri Uri { get; set; }
protected Activity Activity => _activity ?? throw new ObjectDisposedException(nameof(AndroidStorageItem)); protected Activity Activity => _activity ?? throw new ObjectDisposedException(nameof(AndroidStorageItem));
public virtual string Name => GetColumnValue(Activity, Uri, MediaStore.IMediaColumns.DisplayName) public virtual string Name => GetColumnValue(Activity, Uri, MediaStore.IMediaColumns.DisplayName)
?? Uri.PathSegments?.LastOrDefault() ?? string.Empty; ?? Document?.Name
?? Uri.PathSegments?.LastOrDefault()?.Split("/", StringSplitOptions.RemoveEmptyEntries).LastOrDefault() ?? string.Empty;
public Uri Path => new(Uri.ToString()!); public Uri Path => new(Uri.ToString()!);
@ -92,6 +100,23 @@ internal abstract class AndroidStorageItem : IStorageBookmarkItem
return null; return null;
} }
if(_parent != null)
{
return _parent;
}
var document = Document;
if (document == null)
{
return null;
}
if(document.ParentFile != null)
{
return new AndroidStorageFolder(Activity, document.ParentFile.Uri, true);
}
using var javaFile = new JavaFile(Uri.Path!); using var javaFile = new JavaFile(Uri.Path!);
// Java file represents files AND directories. Don't be confused. // Java file represents files AND directories. Don't be confused.
@ -118,12 +143,88 @@ internal abstract class AndroidStorageItem : IStorageBookmarkItem
{ {
_activity = null; _activity = null;
} }
internal DocumentFile? Document
{
get
{
if (this is AndroidStorageFile)
{
return DocumentFile.FromSingleUri(Activity, Uri);
}
else
{
return DocumentFile.FromTreeUri(Activity, Uri);
}
}
}
internal AndroidUri? PermissionRoot => _permissionRoot;
public abstract Task DeleteAsync();
public abstract Task<IStorageItem?> MoveAsync(IStorageFolder destination);
} }
internal class AndroidStorageFolder : AndroidStorageItem, IStorageBookmarkFolder internal class AndroidStorageFolder : AndroidStorageItem, IStorageBookmarkFolder
{ {
public AndroidStorageFolder(Activity activity, AndroidUri uri, bool needsExternalFilesPermission) : base(activity, uri, needsExternalFilesPermission) public AndroidStorageFolder(Activity activity, AndroidUri uri, bool needsExternalFilesPermission, AndroidStorageFolder? parent = null, AndroidUri? permissionRoot = null) : base(activity, uri, needsExternalFilesPermission, parent, permissionRoot)
{
}
public async Task<IStorageFile?> CreateFileAsync(string name)
{
var mimeType = MimeTypeMap.Singleton?.GetMimeTypeFromExtension(MimeTypeMap.GetFileExtensionFromUrl(name)) ?? "application/octet-stream";
var newFile = Document.CreateFile(mimeType, name);
if(newFile == null)
{
return null;
}
return new AndroidStorageFile(Activity, newFile.Uri, this);
}
public async Task<IStorageFolder?> CreateFolderAsync(string name)
{ {
var newFolder = Document?.CreateDirectory(name);
if (newFolder == null)
{
return null;
}
return new AndroidStorageFolder(Activity, newFolder.Uri, false, this, PermissionRoot);
}
public override async Task DeleteAsync()
{
if (!await EnsureExternalFilesPermission(false))
{
return;
}
if (Activity != null)
{
await DeleteContents(this);
}
async Task DeleteContents(AndroidStorageFolder storageFolder)
{
await foreach (var file in storageFolder.GetItemsAsync())
{
if(file is AndroidStorageFolder folder)
{
await DeleteContents(folder);
}
else if(file is AndroidStorageFile storageFile)
{
await storageFile.DeleteAsync();
}
}
DocumentFile.FromTreeUri(Activity, storageFolder.Uri)?.Delete();
}
} }
public override Task<StorageItemProperties> GetBasicPropertiesAsync() public override Task<StorageItemProperties> GetBasicPropertiesAsync()
@ -131,22 +232,22 @@ internal class AndroidStorageFolder : AndroidStorageItem, IStorageBookmarkFolder
return Task.FromResult(new StorageItemProperties()); return Task.FromResult(new StorageItemProperties());
} }
public async Task<IReadOnlyList<IStorageItem>> GetItemsAsync() public async IAsyncEnumerable<IStorageItem> GetItemsAsync()
{ {
if (!await EnsureExternalFilesPermission(false)) if (!await EnsureExternalFilesPermission(false))
{ {
return Array.Empty<IStorageItem>(); yield break;
} }
List<IStorageItem> files = new List<IStorageItem>();
var contentResolver = Activity.ContentResolver; var contentResolver = Activity.ContentResolver;
if (contentResolver == null) if (contentResolver == null)
{ {
return files; yield break;
} }
var childrenUri = DocumentsContract.BuildChildDocumentsUriUsingTree(Uri!, DocumentsContract.GetTreeDocumentId(Uri)); var root = PermissionRoot ?? Uri;
var folderId = root != Uri ? DocumentsContract.GetDocumentId(Uri) : DocumentsContract.GetTreeDocumentId(Uri);
var childrenUri = DocumentsContract.BuildChildDocumentsUriUsingTree(root, folderId);
var projection = new[] var projection = new[]
{ {
@ -162,19 +263,55 @@ internal class AndroidStorageFolder : AndroidStorageItem, IStorageBookmarkFolder
{ {
var mime = cursor.GetString(1); var mime = cursor.GetString(1);
var id = cursor.GetString(0); var id = cursor.GetString(0);
var uri = DocumentsContract.BuildDocumentUriUsingTree(Uri!, id);
bool isDirectory = mime == DocumentsContract.Document.MimeTypeDir;
var uri = DocumentsContract.BuildDocumentUriUsingTree(root, id);
if (uri == null) if (uri == null)
{ {
continue; continue;
} }
yield return isDirectory ? new AndroidStorageFolder(Activity, uri, false, this, root) :
files.Add(mime == DocumentsContract.Document.MimeTypeDir ? new AndroidStorageFolder(Activity, uri, false) : new AndroidStorageFile(Activity, uri, this, root);
new AndroidStorageFile(Activity, uri));
} }
} }
}
public override async Task<IStorageItem?> MoveAsync(IStorageFolder destination)
{
if (Activity != null)
{
return await MoveRecursively(this, (AndroidStorageFolder)destination);
}
return files; return null;
}
async Task<AndroidStorageFolder?> MoveRecursively(AndroidStorageFolder storageFolder, AndroidStorageFolder destination)
{
destination = await destination.CreateFolderAsync(storageFolder.Name) as AndroidStorageFolder;
if (destination == null)
{
return null;
}
await foreach (var file in storageFolder.GetItemsAsync())
{
if (file is AndroidStorageFolder folder)
{
await MoveRecursively(folder, destination);
}
else if (file is AndroidStorageFile)
{
await file.MoveAsync(destination);
}
}
await storageFolder.DeleteAsync();
return destination;
}
}
} }
internal sealed class WellKnownAndroidStorageFolder : AndroidStorageFolder internal sealed class WellKnownAndroidStorageFolder : AndroidStorageFolder
@ -186,14 +323,14 @@ internal sealed class WellKnownAndroidStorageFolder : AndroidStorageFolder
} }
public override string Name { get; } public override string Name { get; }
} }
internal sealed class AndroidStorageFile : AndroidStorageItem, IStorageBookmarkFile internal sealed class AndroidStorageFile : AndroidStorageItem, IStorageBookmarkFile
{ {
public AndroidStorageFile(Activity activity, AndroidUri uri) : base(activity, uri, false) public AndroidStorageFile(Activity activity, AndroidUri uri, AndroidStorageFolder? parent = null, AndroidUri? permissionRoot = null) : base(activity, uri, false, parent, permissionRoot)
{ {
} }
public Task<Stream> OpenReadAsync() => Task.FromResult(OpenContentStream(Activity, Uri, false) public Task<Stream> OpenReadAsync() => Task.FromResult(OpenContentStream(Activity, Uri, false)
?? throw new InvalidOperationException("Failed to open content stream")); ?? throw new InvalidOperationException("Failed to open content stream"));
@ -317,4 +454,76 @@ internal sealed class AndroidStorageFile : AndroidStorageItem, IStorageBookmarkF
return Task.FromResult(new StorageItemProperties(size, itemDate, dateModified)); return Task.FromResult(new StorageItemProperties(size, itemDate, dateModified));
} }
public override async Task DeleteAsync()
{
if (!await EnsureExternalFilesPermission(false))
{
return;
}
if (Activity != null)
{
DocumentsContract.DeleteDocument(Activity.ContentResolver!, Uri);
}
}
public override async Task<IStorageItem?> MoveAsync(IStorageFolder destination)
{
if (!await EnsureExternalFilesPermission(false))
{
return null;
}
if (Activity != null && destination is AndroidStorageFolder storageFolder)
{
if (Build.VERSION.SdkInt >= BuildVersionCodes.N)
{
try
{
var uri = DocumentsContract.MoveDocument(Activity.ContentResolver!, Uri, ((await GetParentAsync()) as AndroidStorageFolder)!.Uri, storageFolder.Document!.Uri);
return new AndroidStorageFile(Activity, uri, storageFolder);
}
catch (Exception ex)
{
// There are many reason why DocumentContract will fail to move a file. We fallback to copying.
return await MoveFileByCopy();
}
}
else
{
return await MoveFileByCopy();
}
}
async Task<AndroidStorageFile?> MoveFileByCopy()
{
var newFile = await storageFolder.CreateFileAsync(Name) as AndroidStorageFile;
try
{
if (newFile != null)
{
using var input = await OpenReadAsync();
using var output = await newFile.OpenWriteAsync();
await input.CopyToAsync(output);
await DeleteAsync();
return new AndroidStorageFile(Activity, newFile.Uri, storageFolder);
}
}
catch
{
newFile?.DeleteAsync();
}
return null;
}
return null;
}
} }

78
src/Avalonia.Base/AvaloniaProperty.cs

@ -4,7 +4,6 @@ using System.Diagnostics.CodeAnalysis;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.Data.Core; using Avalonia.Data.Core;
using Avalonia.PropertyStore; using Avalonia.PropertyStore;
using Avalonia.Styling;
using Avalonia.Utilities; using Avalonia.Utilities;
namespace Avalonia namespace Avalonia
@ -20,12 +19,20 @@ namespace Avalonia
public static readonly object UnsetValue = new UnsetValueType(); public static readonly object UnsetValue = new UnsetValueType();
private static int s_nextId; private static int s_nextId;
/// <summary>
/// Provides a metadata object for types which have no metadata of their own.
/// </summary>
private readonly AvaloniaPropertyMetadata _defaultMetadata; private readonly AvaloniaPropertyMetadata _defaultMetadata;
/// <summary>
/// Provides a fast path when the property has no metadata overrides.
/// </summary>
private KeyValuePair<Type, AvaloniaPropertyMetadata>? _singleMetadata;
private readonly Dictionary<Type, AvaloniaPropertyMetadata> _metadata; private readonly Dictionary<Type, AvaloniaPropertyMetadata> _metadata;
private readonly Dictionary<Type, AvaloniaPropertyMetadata> _metadataCache = new Dictionary<Type, AvaloniaPropertyMetadata>(); private readonly Dictionary<Type, AvaloniaPropertyMetadata> _metadataCache = new Dictionary<Type, AvaloniaPropertyMetadata>();
private bool _hasMetadataOverrides;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="AvaloniaProperty"/> class. /// Initializes a new instance of the <see cref="AvaloniaProperty"/> class.
/// </summary> /// </summary>
@ -57,7 +64,8 @@ namespace Avalonia
Id = s_nextId++; Id = s_nextId++;
_metadata.Add(ownerType, metadata ?? throw new ArgumentNullException(nameof(metadata))); _metadata.Add(ownerType, metadata ?? throw new ArgumentNullException(nameof(metadata)));
_defaultMetadata = metadata; _defaultMetadata = metadata.GenerateTypeSafeMetadata();
_singleMetadata = new(ownerType, metadata);
} }
/// <summary> /// <summary>
@ -80,9 +88,6 @@ namespace Avalonia
Id = source.Id; Id = source.Id;
_defaultMetadata = source._defaultMetadata; _defaultMetadata = source._defaultMetadata;
// Properties that have different owner can't use fast path for metadata.
_hasMetadataOverrides = true;
if (metadata != null) if (metadata != null)
{ {
_metadata.Add(ownerType, metadata); _metadata.Add(ownerType, metadata);
@ -257,7 +262,18 @@ namespace Avalonia
return result; return result;
} }
/// <inheritdoc cref="Register{TOwner, TValue}" /> /// <summary>
/// Registers an attached <see cref="AvaloniaProperty"/>.
/// </summary>
/// <typeparam name="TOwner">The type of the class that is registering the property.</typeparam>
/// <typeparam name="TValue">The type of the property's value.</typeparam>
/// <param name="name">The name of the property.</param>
/// <param name="defaultValue">The default value of the property.</param>
/// <param name="inherits">Whether the property inherits its value.</param>
/// <param name="defaultBindingMode">The default binding mode for the property.</param>
/// <param name="validate">A value validation callback.</param>
/// <param name="coerce">A value coercion callback.</param>
/// <param name="enableDataValidation">if is set to true enable data validation.</param>
/// <param name="notifying"> /// <param name="notifying">
/// A method that gets called before and after the property starts being notified on an /// A method that gets called before and after the property starts being notified on an
/// object; the bool argument will be true before and false afterwards. This callback is /// object; the bool argument will be true before and false afterwards. This callback is
@ -442,33 +458,14 @@ namespace Avalonia
} }
/// <summary> /// <summary>
/// Gets the property metadata for the specified type. /// Gets the <see cref="AvaloniaPropertyMetadata"/> which applies to this property when it is used with the specified type.
/// </summary> /// </summary>
/// <typeparam name="T">The type.</typeparam> /// <typeparam name="T">The type for which to retrieve metadata.</typeparam>
/// <returns> public AvaloniaPropertyMetadata GetMetadata<T>() where T : AvaloniaObject => GetMetadata(typeof(T));
/// The property metadata.
/// </returns>
public AvaloniaPropertyMetadata GetMetadata<T>() where T : AvaloniaObject
{
return GetMetadata(typeof(T));
}
/// <summary> /// <inheritdoc cref="GetMetadata{T}"/>
/// Gets the property metadata for the specified type. /// <param name="type">The type for which to retrieve metadata.</param>
/// </summary> public AvaloniaPropertyMetadata GetMetadata(Type type) => GetMetadataWithOverrides(type);
/// <param name="type">The type.</param>
/// <returns>
/// The property metadata.
/// </returns>
public AvaloniaPropertyMetadata GetMetadata(Type type)
{
if (!_hasMetadataOverrides)
{
return _defaultMetadata;
}
return GetMetadataWithOverrides(type);
}
/// <summary> /// <summary>
/// Checks whether the <paramref name="value"/> is valid for the property. /// Checks whether the <paramref name="value"/> is valid for the property.
@ -567,7 +564,7 @@ namespace Avalonia
_metadata.Add(type, metadata); _metadata.Add(type, metadata);
_metadataCache.Clear(); _metadataCache.Clear();
_hasMetadataOverrides = true; _singleMetadata = null;
} }
protected abstract IObservable<AvaloniaPropertyChangedEventArgs> GetChanged(); protected abstract IObservable<AvaloniaPropertyChangedEventArgs> GetChanged();
@ -584,7 +581,12 @@ namespace Avalonia
return result; return result;
} }
Type? currentType = type; if (_singleMetadata is { } singleMetadata)
{
return _metadataCache[type] = singleMetadata.Key.IsAssignableFrom(type) ? singleMetadata.Value : _defaultMetadata;
}
var currentType = type;
while (currentType != null) while (currentType != null)
{ {
@ -598,13 +600,11 @@ namespace Avalonia
currentType = currentType.BaseType; currentType = currentType.BaseType;
} }
_metadataCache[type] = _defaultMetadata; return _metadataCache[type] = _defaultMetadata;
return _defaultMetadata;
} }
bool IPropertyInfo.CanGet => true; bool IPropertyInfo.CanGet => true;
bool IPropertyInfo.CanSet => true; bool IPropertyInfo.CanSet => !IsReadOnly;
object? IPropertyInfo.Get(object target) => ((AvaloniaObject)target).GetValue(this); object? IPropertyInfo.Get(object target) => ((AvaloniaObject)target).GetValue(this);
void IPropertyInfo.Set(object target, object? value) => ((AvaloniaObject)target).SetValue(this, value); void IPropertyInfo.Set(object target, object? value) => ((AvaloniaObject)target).SetValue(this, value);
} }

10
src/Avalonia.Base/AvaloniaPropertyMetadata.cs

@ -5,7 +5,7 @@ namespace Avalonia
/// <summary> /// <summary>
/// Base class for avalonia property metadata. /// Base class for avalonia property metadata.
/// </summary> /// </summary>
public class AvaloniaPropertyMetadata public abstract class AvaloniaPropertyMetadata
{ {
private BindingMode _defaultBindingMode; private BindingMode _defaultBindingMode;
@ -61,5 +61,13 @@ namespace Avalonia
EnableDataValidation ??= baseMetadata.EnableDataValidation; EnableDataValidation ??= baseMetadata.EnableDataValidation;
} }
/// <summary>
/// Gets a copy of this object configured for use with any owner type.
/// </summary>
/// <remarks>
/// For example, delegates which receive the owner object should be removed.
/// </remarks>
public abstract AvaloniaPropertyMetadata GenerateTypeSafeMetadata();
} }
} }

2
src/Avalonia.Base/AvaloniaPropertyRegistry.cs

@ -364,7 +364,7 @@ namespace Avalonia
/// <param name="property">The property.</param> /// <param name="property">The property.</param>
/// <remarks> /// <remarks>
/// You won't usually want to call this method directly, instead use the /// You won't usually want to call this method directly, instead use the
/// <see cref="AvaloniaProperty.Register{TOwner, TValue}(string, TValue, bool, Data.BindingMode, Func{TValue, bool}, Func{AvaloniaObject, TValue, TValue}, Action{AvaloniaObject, bool})"/> /// <see cref="AvaloniaProperty.Register{TOwner, TValue}(string, TValue, bool, Data.BindingMode, Func{TValue, bool}, Func{AvaloniaObject, TValue, TValue}, bool)"/>
/// method. /// method.
/// </remarks> /// </remarks>
public void Register(Type type, AvaloniaProperty property) public void Register(Type type, AvaloniaProperty property)

9
src/Avalonia.Base/CornerRadius.cs

@ -60,15 +60,6 @@ namespace Avalonia
/// </summary> /// </summary>
public double BottomLeft { get; } public double BottomLeft { get; }
/// <summary>
/// Gets a value indicating whether the instance has default values (all corner radii are set to 0).
/// </summary>
public bool IsDefault => TopLeft == 0 && TopRight == 0 && BottomLeft == 0 && BottomRight == 0;
/// <inheritdoc cref="IsDefault"/>
[Obsolete("Use IsDefault instead.")]
public bool IsEmpty => IsDefault;
/// <summary> /// <summary>
/// Gets a value indicating whether all corner radii are equal. /// Gets a value indicating whether all corner radii are equal.
/// </summary> /// </summary>

2
src/Avalonia.Base/DirectPropertyMetadata`1.cs

@ -45,5 +45,7 @@ namespace Avalonia
UnsetValue ??= src.UnsetValue; UnsetValue ??= src.UnsetValue;
} }
} }
public override AvaloniaPropertyMetadata GenerateTypeSafeMetadata() => new DirectPropertyMetadata<TValue>(UnsetValue, DefaultBindingMode, EnableDataValidation);
} }
} }

2
src/Avalonia.Base/Input/GestureRecognizers/VelocityTracker.cs

@ -180,7 +180,7 @@ namespace Avalonia.Input.GestureRecognizers
internal Velocity GetVelocity() internal Velocity GetVelocity()
{ {
var estimate = GetVelocityEstimate(); var estimate = GetVelocityEstimate();
if (estimate == null || estimate.PixelsPerSecond.IsDefault) if (estimate == null || estimate.PixelsPerSecond == default(Vector))
{ {
return new Velocity(Vector.Zero); return new Velocity(Vector.Zero);
} }

11
src/Avalonia.Base/Media/BoxShadow.cs

@ -45,15 +45,6 @@ namespace Avalonia.Media
} }
} }
/// <summary>
/// Gets a value indicating whether the instance has default values.
/// </summary>
public bool IsDefault => OffsetX == 0 && OffsetY == 0 && Blur == 0 && Spread == 0;
/// <inheritdoc cref="IsDefault"/>
[Obsolete("Use IsDefault instead.")]
public bool IsEmpty => IsDefault;
private readonly static char[] s_Separator = new char[] { ' ', '\t' }; private readonly static char[] s_Separator = new char[] { ' ', '\t' };
struct ArrayReader struct ArrayReader
@ -89,7 +80,7 @@ namespace Avalonia.Media
{ {
var sb = StringBuilderCache.Acquire(); var sb = StringBuilderCache.Acquire();
if (IsDefault) if (this == default)
{ {
return "none"; return "none";
} }

4
src/Avalonia.Base/Media/BoxShadows.cs

@ -21,7 +21,7 @@ namespace Avalonia.Media
{ {
_first = shadow; _first = shadow;
_list = null; _list = null;
Count = _first.IsDefault ? 0 : 1; Count = _first == default ? 0 : 1;
} }
public BoxShadows(BoxShadow first, BoxShadow[] rest) public BoxShadows(BoxShadow first, BoxShadow[] rest)
@ -120,7 +120,7 @@ namespace Avalonia.Media
get get
{ {
foreach(var boxShadow in this) foreach(var boxShadow in this)
if (!boxShadow.IsDefault && boxShadow.IsInset) if (boxShadow != default && boxShadow.IsInset)
return true; return true;
return false; return false;
} }

4
src/Avalonia.Base/Media/DrawingGroup.cs

@ -117,10 +117,10 @@ namespace Avalonia.Media
// root DrawingGroup, and be the same value as the root _currentDrawingGroup. // root DrawingGroup, and be the same value as the root _currentDrawingGroup.
// //
// Either way, _rootDrawing always references the root drawing. // Either way, _rootDrawing always references the root drawing.
protected Drawing? _rootDrawing; private Drawing? _rootDrawing;
// Current DrawingGroup that new children are added to // Current DrawingGroup that new children are added to
protected DrawingGroup? _currentDrawingGroup; private DrawingGroup? _currentDrawingGroup;
// Previous values of _currentDrawingGroup // Previous values of _currentDrawingGroup
private Stack<DrawingGroup?>? _previousDrawingGroupStack; private Stack<DrawingGroup?>? _previousDrawingGroupStack;

5
src/Avalonia.Base/Media/FontFamily.cs

@ -79,11 +79,6 @@ namespace Avalonia.Media
/// <remarks>Key is only used for custom fonts.</remarks> /// <remarks>Key is only used for custom fonts.</remarks>
public FontFamilyKey? Key { get; } public FontFamilyKey? Key { get; }
/// <summary>
/// Returns <c>True</c> if this instance is the system's default.
/// </summary>
public bool IsDefault => Name.Equals(DefaultFontFamilyName);
/// <summary> /// <summary>
/// Implicit conversion of string to FontFamily /// Implicit conversion of string to FontFamily
/// </summary> /// </summary>

8
src/Avalonia.Base/Media/FontManager.cs

@ -119,7 +119,9 @@ namespace Avalonia.Media
} }
} }
if (fontCollection != null && fontCollection.TryGetGlyphTypeface(fontFamily.FamilyNames.PrimaryFamilyName, var familyName = fontFamily.FamilyNames.PrimaryFamilyName.ToUpperInvariant();
if (fontCollection != null && fontCollection.TryGetGlyphTypeface(familyName,
typeface.Style, typeface.Weight, typeface.Stretch, out glyphTypeface)) typeface.Style, typeface.Weight, typeface.Stretch, out glyphTypeface))
{ {
return true; return true;
@ -133,13 +135,13 @@ namespace Avalonia.Media
foreach (var familyName in fontFamily.FamilyNames) foreach (var familyName in fontFamily.FamilyNames)
{ {
if (SystemFonts.TryGetGlyphTypeface(familyName, typeface.Style, typeface.Weight, typeface.Stretch, out glyphTypeface)) if (SystemFonts.TryGetGlyphTypeface(familyName.ToUpperInvariant(), typeface.Style, typeface.Weight, typeface.Stretch, out glyphTypeface))
{ {
return true; return true;
} }
} }
return SystemFonts.TryGetGlyphTypeface(DefaultFontFamilyName, typeface.Style, typeface.Weight, typeface.Stretch, out glyphTypeface); return TryGetGlyphTypeface(new Typeface(DefaultFontFamilyName, typeface.Style, typeface.Weight, typeface.Stretch), out glyphTypeface);
} }
/// <summary> /// <summary>

15
src/Avalonia.Base/Media/Fonts/EmbeddedFontCollection.cs

@ -3,7 +3,6 @@ using System.Collections;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using Avalonia.Platform; using Avalonia.Platform;
namespace Avalonia.Media.Fonts namespace Avalonia.Media.Fonts
@ -43,11 +42,13 @@ namespace Avalonia.Media.Fonts
if (fontManager.TryCreateGlyphTypeface(stream, out var glyphTypeface)) if (fontManager.TryCreateGlyphTypeface(stream, out var glyphTypeface))
{ {
if (!_glyphTypefaceCache.TryGetValue(glyphTypeface.FamilyName, out var glyphTypefaces)) var familyName = glyphTypeface.FamilyName.ToUpperInvariant();
if (!_glyphTypefaceCache.TryGetValue(familyName, out var glyphTypefaces))
{ {
glyphTypefaces = new ConcurrentDictionary<FontCollectionKey, IGlyphTypeface>(); glyphTypefaces = new ConcurrentDictionary<FontCollectionKey, IGlyphTypeface>();
if (_glyphTypefaceCache.TryAdd(glyphTypeface.FamilyName, glyphTypefaces)) if (_glyphTypefaceCache.TryAdd(familyName, glyphTypefaces))
{ {
_fontFamilies.Add(new FontFamily(_key, glyphTypeface.FamilyName)); _fontFamilies.Add(new FontFamily(_key, glyphTypeface.FamilyName));
} }
@ -86,6 +87,8 @@ namespace Avalonia.Media.Fonts
public bool TryGetGlyphTypeface(string familyName, FontStyle style, FontWeight weight, public bool TryGetGlyphTypeface(string familyName, FontStyle style, FontWeight weight,
FontStretch stretch, [NotNullWhen(true)] out IGlyphTypeface? glyphTypeface) FontStretch stretch, [NotNullWhen(true)] out IGlyphTypeface? glyphTypeface)
{ {
familyName = familyName.ToUpperInvariant();
var key = new FontCollectionKey(style, weight, stretch); var key = new FontCollectionKey(style, weight, stretch);
if (_glyphTypefaceCache.TryGetValue(familyName, out var glyphTypefaces)) if (_glyphTypefaceCache.TryGetValue(familyName, out var glyphTypefaces))
@ -101,9 +104,11 @@ namespace Avalonia.Media.Fonts
{ {
var fontFamily = _fontFamilies[i]; var fontFamily = _fontFamilies[i];
if (fontFamily.Name.ToLower(CultureInfo.InvariantCulture).StartsWith(familyName.ToLower(CultureInfo.InvariantCulture))) if (fontFamily.Name.ToUpperInvariant().StartsWith(familyName.ToUpperInvariant()))
{ {
if (_glyphTypefaceCache.TryGetValue(fontFamily.Name, out glyphTypefaces) && familyName = fontFamily.Name.ToUpperInvariant();
if (_glyphTypefaceCache.TryGetValue(familyName, out glyphTypefaces) &&
TryGetNearestMatch(glyphTypefaces, key, out glyphTypeface)) TryGetNearestMatch(glyphTypefaces, key, out glyphTypeface))
{ {
return true; return true;

2
src/Avalonia.Base/Media/Fonts/SystemFontCollection.cs

@ -42,6 +42,8 @@ namespace Avalonia.Media.Fonts
familyName = _fontManager.DefaultFontFamilyName; familyName = _fontManager.DefaultFontFamilyName;
} }
familyName = familyName.ToUpperInvariant();
var key = new FontCollectionKey(style, weight, stretch); var key = new FontCollectionKey(style, weight, stretch);
if (_glyphTypefaceCache.TryGetValue(familyName, out var glyphTypefaces)) if (_glyphTypefaceCache.TryGetValue(familyName, out var glyphTypefaces))

5
src/Avalonia.Base/Media/FormattedText.cs

@ -1393,10 +1393,11 @@ namespace Avalonia.Media
} }
} }
if (accumulatedBounds?.PlatformImpl == null || accumulatedBounds.PlatformImpl.Bounds.IsDefault) if (accumulatedBounds?.PlatformImpl == null ||
(accumulatedBounds.PlatformImpl.Bounds.Width == 0 && accumulatedBounds.PlatformImpl.Bounds.Height == 0))
{ {
return null; return null;
} }
return accumulatedBounds; return accumulatedBounds;
} }

2
src/Avalonia.Base/Media/ImageDrawing.cs

@ -42,7 +42,7 @@ namespace Avalonia.Media
var imageSource = ImageSource; var imageSource = ImageSource;
var rect = Rect; var rect = Rect;
if (imageSource is object && !rect.IsDefault) if (imageSource is object && (rect.Width != 0 || rect.Height != 0))
{ {
context.DrawImage(imageSource, rect); context.DrawImage(imageSource, rect);
} }

2
src/Avalonia.Base/Media/Imaging/CroppedBitmap.cs

@ -77,7 +77,7 @@ namespace Avalonia.Media.Imaging
{ {
if (Source is not IBitmap bmp) if (Source is not IBitmap bmp)
return default; return default;
if (SourceRect.IsDefault) if (SourceRect.Width == 0 && SourceRect.Height == 0)
return Source.Size; return Source.Size;
return SourceRect.Size.ToSizeWithDpi(bmp.Dpi); return SourceRect.Size.ToSizeWithDpi(bmp.Dpi);
} }

2
src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs

@ -658,7 +658,7 @@ namespace Avalonia.Media.TextFormatting
/// Performs text wrapping returns a list of text lines. /// Performs text wrapping returns a list of text lines.
/// </summary> /// </summary>
/// <param name="textRuns"></param> /// <param name="textRuns"></param>
/// <param name="canReuseTextRunList">Whether <see cref="textRuns"/> can be reused to store the split runs.</param> /// <param name="canReuseTextRunList">Whether <see cref="TextRun"/> can be reused to store the split runs.</param>
/// <param name="firstTextSourceIndex">The first text source index.</param> /// <param name="firstTextSourceIndex">The first text source index.</param>
/// <param name="paragraphWidth">The paragraph width.</param> /// <param name="paragraphWidth">The paragraph width.</param>
/// <param name="paragraphProperties">The text paragraph properties.</param> /// <param name="paragraphProperties">The text paragraph properties.</param>

2
src/Avalonia.Base/Metadata/InheritDataTypeFromItemsAttribute.cs

@ -9,7 +9,7 @@ namespace Avalonia.Metadata;
/// A typical usage example is a ListBox control, where <see cref="InheritDataTypeFromItemsAttribute"/> is defined on the ItemTemplate property, /// A typical usage example is a ListBox control, where <see cref="InheritDataTypeFromItemsAttribute"/> is defined on the ItemTemplate property,
/// allowing the template to inherit the data type from the Items collection binding. /// allowing the template to inherit the data type from the Items collection binding.
/// </remarks> /// </remarks>
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)] [AttributeUsage(AttributeTargets.Property, AllowMultiple = true, Inherited = true)]
public sealed class InheritDataTypeFromItemsAttribute : Attribute public sealed class InheritDataTypeFromItemsAttribute : Attribute
{ {
/// <summary> /// <summary>

19
src/Avalonia.Base/PixelRect.cs

@ -9,12 +9,6 @@ namespace Avalonia
/// </summary> /// </summary>
public readonly struct PixelRect : IEquatable<PixelRect> public readonly struct PixelRect : IEquatable<PixelRect>
{ {
/// <summary>
/// An empty rectangle.
/// </summary>
[Obsolete("Use the default keyword instead.")]
public static readonly PixelRect Empty = default;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="PixelRect"/> structure. /// Initializes a new instance of the <see cref="PixelRect"/> structure.
/// </summary> /// </summary>
@ -133,15 +127,6 @@ namespace Avalonia
/// </summary> /// </summary>
public PixelPoint Center => new PixelPoint(X + (Width / 2), Y + (Height / 2)); public PixelPoint Center => new PixelPoint(X + (Width / 2), Y + (Height / 2));
/// <summary>
/// Gets a value indicating whether the instance has default values (the rectangle is empty).
/// </summary>
public bool IsDefault => Width == 0 && Height == 0;
/// <inheritdoc cref="IsDefault"/>
[Obsolete("Use IsDefault instead.")]
public bool IsEmpty => IsDefault;
/// <summary> /// <summary>
/// Checks for equality between two <see cref="PixelRect"/>s. /// Checks for equality between two <see cref="PixelRect"/>s.
/// </summary> /// </summary>
@ -295,11 +280,11 @@ namespace Avalonia
/// <returns>The union.</returns> /// <returns>The union.</returns>
public PixelRect Union(PixelRect rect) public PixelRect Union(PixelRect rect)
{ {
if (IsDefault) if (Width == 0 && Height == 0)
{ {
return rect; return rect;
} }
else if (rect.IsDefault) else if (rect.Width == 0 && rect.Height == 0)
{ {
return this; return this;
} }

1
src/Avalonia.Base/Platform/IDrawingContextImpl.cs

@ -128,6 +128,7 @@ namespace Avalonia.Platform
/// Pushes an opacity value. /// Pushes an opacity value.
/// </summary> /// </summary>
/// <param name="opacity">The opacity.</param> /// <param name="opacity">The opacity.</param>
/// <param name="bounds">where to apply the opacity.</param>
void PushOpacity(double opacity, Rect bounds); void PushOpacity(double opacity, Rect bounds);
/// <summary> /// <summary>

18
src/Avalonia.Base/Platform/Storage/FileIO/BclStorageFile.cs

@ -92,4 +92,22 @@ internal class BclStorageFile : IStorageBookmarkFile
Dispose(disposing: true); Dispose(disposing: true);
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
} }
public async Task DeleteAsync()
{
FileInfo.Delete();
}
public async Task<IStorageItem?> MoveAsync(IStorageFolder destination)
{
if (destination is BclStorageFolder storageFolder)
{
var newPath = System.IO.Path.Combine(storageFolder.DirectoryInfo.FullName, FileInfo.Name);
FileInfo.MoveTo(newPath);
return new BclStorageFile(new FileInfo(newPath));
}
return null;
}
} }

47
src/Avalonia.Base/Platform/Storage/FileIO/BclStorageFolder.cs

@ -57,14 +57,16 @@ internal class BclStorageFolder : IStorageBookmarkFolder
return Task.FromResult<IStorageFolder?>(null); return Task.FromResult<IStorageFolder?>(null);
} }
public Task<IReadOnlyList<IStorageItem>> GetItemsAsync() public async IAsyncEnumerable<IStorageItem> GetItemsAsync()
{ {
var items = DirectoryInfo.GetDirectories() var items = DirectoryInfo.EnumerateDirectories()
.Select(d => (IStorageItem)new BclStorageFolder(d)) .Select(d => (IStorageItem)new BclStorageFolder(d))
.Concat(DirectoryInfo.GetFiles().Select(f => new BclStorageFile(f))) .Concat(DirectoryInfo.EnumerateFiles().Select(f => new BclStorageFile(f)));
.ToArray();
return Task.FromResult<IReadOnlyList<IStorageItem>>(items); foreach (var item in items)
{
yield return item;
}
} }
public virtual Task<string?> SaveBookmarkAsync() public virtual Task<string?> SaveBookmarkAsync()
@ -92,4 +94,39 @@ internal class BclStorageFolder : IStorageBookmarkFolder
Dispose(disposing: true); Dispose(disposing: true);
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
} }
public async Task DeleteAsync()
{
DirectoryInfo.Delete(true);
}
public async Task<IStorageItem?> MoveAsync(IStorageFolder destination)
{
if (destination is BclStorageFolder storageFolder)
{
var newPath = System.IO.Path.Combine(storageFolder.DirectoryInfo.FullName, DirectoryInfo.Name);
DirectoryInfo.MoveTo(newPath);
return new BclStorageFolder(new DirectoryInfo(newPath));
}
return null;
}
public async Task<IStorageFile?> CreateFileAsync(string name)
{
var fileName = System.IO.Path.Combine(DirectoryInfo.FullName, name);
var newFile = new FileInfo(fileName);
using var stream = newFile.Create();
return new BclStorageFile(newFile);
}
public async Task<IStorageFolder?> CreateFolderAsync(string name)
{
var newFolder = DirectoryInfo.CreateSubdirectory(name);
return new BclStorageFolder(newFolder);
}
} }

16
src/Avalonia.Base/Platform/Storage/IStorageFolder.cs

@ -16,5 +16,19 @@ public interface IStorageFolder : IStorageItem
/// <returns> /// <returns>
/// When this method completes successfully, it returns a list of the files and folders in the current folder. Each item in the list is represented by an <see cref="IStorageItem"/> implementation object. /// When this method completes successfully, it returns a list of the files and folders in the current folder. Each item in the list is represented by an <see cref="IStorageItem"/> implementation object.
/// </returns> /// </returns>
Task<IReadOnlyList<IStorageItem>> GetItemsAsync(); IAsyncEnumerable<IStorageItem> GetItemsAsync();
/// <summary>
/// Creates a file with specified name as a child of the current storage folder
/// </summary>
/// <param name="name">The display name</param>
/// <returns>A new <see cref="IStorageFile"/> pointing to the moved file. If not null, the current storage item becomes invalid</returns>
Task<IStorageFile?> CreateFileAsync(string name);
/// <summary>
/// Creates a folder with specified name as a child of the current storage folder
/// </summary>
/// <param name="name">The display name</param>
/// <returns>A new <see cref="IStorageFolder"/> pointing to the moved file. If not null, the current storage item becomes invalid</returns>
Task<IStorageFolder?> CreateFolderAsync(string name);
} }

13
src/Avalonia.Base/Platform/Storage/IStorageItem.cs

@ -50,4 +50,17 @@ public interface IStorageItem : IDisposable
/// Gets the parent folder of the current storage item. /// Gets the parent folder of the current storage item.
/// </summary> /// </summary>
Task<IStorageFolder?> GetParentAsync(); Task<IStorageFolder?> GetParentAsync();
/// <summary>
/// Deletes the current storage item and it's contents
/// </summary>
/// <returns></returns>
Task DeleteAsync();
/// <summary>
/// Moves the current storage item and it's contents to a <see cref="IStorageFolder"/>
/// </summary>
/// <param name="destination">The <see cref="IStorageFolder"/> to move the item into</param>
/// <returns></returns>
Task<IStorageItem?> MoveAsync(IStorageFolder destination);
} }

8
src/Avalonia.Base/Point.cs

@ -288,13 +288,5 @@ namespace Avalonia
x = this._x; x = this._x;
y = this._y; y = this._y;
} }
/// <summary>
/// Gets a value indicating whether the X and Y coordinates are zero.
/// </summary>
public bool IsDefault
{
get { return (_x == 0) && (_y == 0); }
}
} }
} }

2
src/Avalonia.Base/PropertyStore/EffectiveValue`1.cs

@ -31,7 +31,7 @@ namespace Avalonia.PropertyStore
var value = inherited is null ? _metadata.DefaultValue : inherited.Value; var value = inherited is null ? _metadata.DefaultValue : inherited.Value;
if (property.HasCoercion && _metadata.CoerceValue is { } coerce) if (_metadata.CoerceValue is { } coerce)
{ {
_uncommon = new() _uncommon = new()
{ {

34
src/Avalonia.Base/Rect.cs

@ -16,12 +16,6 @@ namespace Avalonia
Animation.Animation.RegisterAnimator<RectAnimator>(prop => typeof(Rect).IsAssignableFrom(prop.PropertyType)); Animation.Animation.RegisterAnimator<RectAnimator>(prop => typeof(Rect).IsAssignableFrom(prop.PropertyType));
} }
/// <summary>
/// An empty rectangle.
/// </summary>
[Obsolete("Use the default keyword instead.")]
public static readonly Rect Empty = default;
/// <summary> /// <summary>
/// The X position. /// The X position.
/// </summary> /// </summary>
@ -170,17 +164,6 @@ namespace Avalonia
/// </summary> /// </summary>
public Point Center => new Point(_x + (_width / 2), _y + (_height / 2)); public Point Center => new Point(_x + (_width / 2), _y + (_height / 2));
/// <summary>
/// Gets a value indicating whether the instance has default values (the rectangle is empty).
/// </summary>
// ReSharper disable CompareOfFloatsByEqualityOperator
public bool IsDefault => _width == 0 && _height == 0;
// ReSharper restore CompareOfFloatsByEqualityOperator
/// <inheritdoc cref="IsDefault"/>
[Obsolete("Use IsDefault instead.")]
public bool IsEmpty => IsDefault;
/// <summary> /// <summary>
/// Checks for equality between two <see cref="Rect"/>s. /// Checks for equality between two <see cref="Rect"/>s.
/// </summary> /// </summary>
@ -517,19 +500,18 @@ namespace Avalonia
return rect; return rect;
} }
/// <summary>
/// <summary> /// Gets the union of two rectangles.
/// Gets the union of two rectangles. /// </summary>
/// </summary> /// <param name="rect">The other rectangle.</param>
/// <param name="rect">The other rectangle.</param> /// <returns>The union.</returns>
/// <returns>The union.</returns> public Rect Union(Rect rect)
public Rect Union(Rect rect)
{ {
if (IsDefault) if (Width == 0 && Height == 0)
{ {
return rect; return rect;
} }
else if (rect.IsDefault) else if (rect.Width == 0 && rect.Height == 0)
{ {
return this; return this;
} }

12
src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs

@ -130,8 +130,8 @@ namespace Avalonia.Rendering.Composition.Server
} }
_renderTarget ??= _compositor.CreateRenderTarget(_surfaces()); _renderTarget ??= _compositor.CreateRenderTarget(_surfaces());
if(_dirtyRect.IsDefault && !_redrawRequested) if ((_dirtyRect.Width == 0 && _dirtyRect.Height == 0) && !_redrawRequested)
return; return;
Revision++; Revision++;
@ -163,7 +163,7 @@ namespace Avalonia.Rendering.Composition.Server
_dirtyRect = new Rect(0, 0, layerSize.Width, layerSize.Height); _dirtyRect = new Rect(0, 0, layerSize.Width, layerSize.Height);
} }
if (!_dirtyRect.IsDefault) if (_dirtyRect.Width != 0 || _dirtyRect.Height != 0)
{ {
using (var context = _layer.CreateDrawingContext()) using (var context = _layer.CreateDrawingContext())
{ {
@ -260,7 +260,7 @@ namespace Avalonia.Rendering.Composition.Server
public void AddDirtyRect(Rect rect) public void AddDirtyRect(Rect rect)
{ {
if(rect.IsDefault) if (rect.Width == 0 && rect.Height == 0)
return; return;
var snapped = SnapToDevicePixels(rect, Scaling); var snapped = SnapToDevicePixels(rect, Scaling);
DebugEvents?.RectInvalidated(rect); DebugEvents?.RectInvalidated(rect);
@ -275,7 +275,7 @@ namespace Avalonia.Rendering.Composition.Server
public void Dispose() public void Dispose()
{ {
if(_disposed) if (_disposed)
return; return;
_disposed = true; _disposed = true;
using (_compositor.RenderInterface.EnsureCurrent()) using (_compositor.RenderInterface.EnsureCurrent())
@ -302,7 +302,7 @@ namespace Avalonia.Rendering.Composition.Server
{ {
if (_attachedVisuals.Remove(visual) && IsEnabled) if (_attachedVisuals.Remove(visual) && IsEnabled)
visual.Deactivate(); visual.Deactivate();
if(visual.IsVisibleInFrame) if (visual.IsVisibleInFrame)
AddDirtyRect(visual.TransformedOwnContentBounds); AddDirtyRect(visual.TransformedOwnContentBounds);
} }

55
src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs

@ -23,21 +23,20 @@ namespace Avalonia.Rendering.Composition.Server
private bool _isBackface; private bool _isBackface;
private Rect? _transformedClipBounds; private Rect? _transformedClipBounds;
private Rect _combinedTransformedClipBounds; private Rect _combinedTransformedClipBounds;
protected virtual void RenderCore(CompositorDrawingContextProxy canvas, Rect currentTransformedClip) protected virtual void RenderCore(CompositorDrawingContextProxy canvas, Rect currentTransformedClip)
{ {
} }
public void Render(CompositorDrawingContextProxy canvas, Rect currentTransformedClip) public void Render(CompositorDrawingContextProxy canvas, Rect currentTransformedClip)
{ {
if(Visible == false || IsVisibleInFrame == false) if (Visible == false || IsVisibleInFrame == false)
return; return;
if(Opacity == 0) if (Opacity == 0)
return; return;
currentTransformedClip = currentTransformedClip.Intersect(_combinedTransformedClipBounds); currentTransformedClip = currentTransformedClip.Intersect(_combinedTransformedClipBounds);
if(currentTransformedClip.IsDefault) if (currentTransformedClip.Width == 0 && currentTransformedClip.Height == 0)
return; return;
Root!.RenderedVisuals++; Root!.RenderedVisuals++;
@ -61,7 +60,7 @@ namespace Avalonia.Rendering.Composition.Server
canvas.PushClip(Root!.SnapToDevicePixels(boundsRect)); canvas.PushClip(Root!.SnapToDevicePixels(boundsRect));
if (Clip != null) if (Clip != null)
canvas.PushGeometryClip(Clip); canvas.PushGeometryClip(Clip);
if(OpacityMaskBrush != null) if (OpacityMaskBrush != null)
canvas.PushOpacityMask(OpacityMaskBrush, boundsRect); canvas.PushOpacityMask(OpacityMaskBrush, boundsRect);
RenderCore(canvas, currentTransformedClip); RenderCore(canvas, currentTransformedClip);
@ -78,12 +77,12 @@ namespace Avalonia.Rendering.Composition.Server
canvas.PopClip(); canvas.PopClip();
if (AdornedVisual != null && AdornerIsClipped) if (AdornedVisual != null && AdornerIsClipped)
canvas.PopClip(); canvas.PopClip();
if(Opacity != 1) if (Opacity != 1)
canvas.PopOpacity(); canvas.PopOpacity();
} }
protected virtual bool HandlesClipToBounds => false; protected virtual bool HandlesClipToBounds => false;
private ReadbackData _readback0, _readback1, _readback2; private ReadbackData _readback0, _readback1, _readback2;
/// <summary> /// <summary>
@ -98,17 +97,17 @@ namespace Avalonia.Rendering.Composition.Server
return ref _readback1; return ref _readback1;
return ref _readback2; return ref _readback2;
} }
public Matrix4x4 CombinedTransformMatrix { get; private set; } = Matrix4x4.Identity; public Matrix4x4 CombinedTransformMatrix { get; private set; } = Matrix4x4.Identity;
public Matrix4x4 GlobalTransformMatrix { get; private set; } public Matrix4x4 GlobalTransformMatrix { get; private set; }
public virtual void Update(ServerCompositionTarget root) public virtual void Update(ServerCompositionTarget root)
{ {
if(Parent == null && Root == null) if (Parent == null && Root == null)
return; return;
var wasVisible = IsVisibleInFrame; var wasVisible = IsVisibleInFrame;
// Calculate new parent-relative transform // Calculate new parent-relative transform
if (_combinedTransformDirty) if (_combinedTransformDirty)
{ {
@ -122,7 +121,7 @@ namespace Avalonia.Rendering.Composition.Server
var parentTransform = (AdornedVisual ?? Parent)?.GlobalTransformMatrix ?? Matrix4x4.Identity; var parentTransform = (AdornedVisual ?? Parent)?.GlobalTransformMatrix ?? Matrix4x4.Identity;
var newTransform = CombinedTransformMatrix * parentTransform; var newTransform = CombinedTransformMatrix * parentTransform;
// Check if visual was moved and recalculate face orientation // Check if visual was moved and recalculate face orientation
var positionChanged = false; var positionChanged = false;
if (GlobalTransformMatrix != newTransform) if (GlobalTransformMatrix != newTransform)
@ -134,23 +133,23 @@ namespace Avalonia.Rendering.Composition.Server
var oldTransformedContentBounds = TransformedOwnContentBounds; var oldTransformedContentBounds = TransformedOwnContentBounds;
var oldCombinedTransformedClipBounds = _combinedTransformedClipBounds; var oldCombinedTransformedClipBounds = _combinedTransformedClipBounds;
if (_parent?.IsDirtyComposition == true) if (_parent?.IsDirtyComposition == true)
{ {
IsDirtyComposition = true; IsDirtyComposition = true;
_isDirtyForUpdate = true; _isDirtyForUpdate = true;
} }
var invalidateOldBounds = _isDirtyForUpdate; var invalidateOldBounds = _isDirtyForUpdate;
var invalidateNewBounds = _isDirtyForUpdate; var invalidateNewBounds = _isDirtyForUpdate;
GlobalTransformMatrix = newTransform; GlobalTransformMatrix = newTransform;
var ownBounds = OwnContentBounds; var ownBounds = OwnContentBounds;
if (ownBounds != _oldOwnContentBounds || positionChanged) if (ownBounds != _oldOwnContentBounds || positionChanged)
{ {
_oldOwnContentBounds = ownBounds; _oldOwnContentBounds = ownBounds;
if (ownBounds.IsDefault) if (ownBounds.Width == 0 && ownBounds.Height == 0)
TransformedOwnContentBounds = default; TransformedOwnContentBounds = default;
else else
TransformedOwnContentBounds = TransformedOwnContentBounds =
@ -171,16 +170,16 @@ namespace Avalonia.Rendering.Composition.Server
AdornedVisual?._combinedTransformedClipBounds AdornedVisual?._combinedTransformedClipBounds
?? Parent?._combinedTransformedClipBounds ?? Parent?._combinedTransformedClipBounds
?? new Rect(Root!.Size); ?? new Rect(Root!.Size);
if (_transformedClipBounds != null) if (_transformedClipBounds != null)
_combinedTransformedClipBounds = _combinedTransformedClipBounds.Intersect(_transformedClipBounds.Value); _combinedTransformedClipBounds = _combinedTransformedClipBounds.Intersect(_transformedClipBounds.Value);
EffectiveOpacity = Opacity * (Parent?.EffectiveOpacity ?? 1); EffectiveOpacity = Opacity * (Parent?.EffectiveOpacity ?? 1);
IsHitTestVisibleInFrame = _parent?.IsHitTestVisibleInFrame != false IsHitTestVisibleInFrame = _parent?.IsHitTestVisibleInFrame != false
&& Visible && Visible
&& !_isBackface && !_isBackface
&& !_combinedTransformedClipBounds.IsDefault; && (_combinedTransformedClipBounds.Width != 0 || _combinedTransformedClipBounds.Height != 0);
IsVisibleInFrame = IsHitTestVisibleInFrame IsVisibleInFrame = IsHitTestVisibleInFrame
&& _parent?.IsVisibleInFrame != false && _parent?.IsVisibleInFrame != false
@ -213,11 +212,11 @@ namespace Avalonia.Rendering.Composition.Server
void AddDirtyRect(Rect rc) void AddDirtyRect(Rect rc)
{ {
if(rc == default) if (rc == default)
return; return;
Root?.AddDirtyRect(rc); Root?.AddDirtyRect(rc);
} }
/// <summary> /// <summary>
/// Data that can be read from the UI thread /// Data that can be read from the UI thread
/// </summary> /// </summary>
@ -228,7 +227,7 @@ namespace Avalonia.Rendering.Composition.Server
public long TargetId; public long TargetId;
public bool Visible; public bool Visible;
} }
partial void DeserializeChangesExtra(BatchStreamReader c) partial void DeserializeChangesExtra(BatchStreamReader c)
{ {
ValuesInvalidated(); ValuesInvalidated();
@ -245,9 +244,8 @@ namespace Avalonia.Rendering.Composition.Server
protected virtual void OnDetachedFromRoot(ServerCompositionTarget target) protected virtual void OnDetachedFromRoot(ServerCompositionTarget target)
{ {
} }
partial void OnRootChanged() partial void OnRootChanged()
{ {
if (Root != null) if (Root != null)
@ -256,12 +254,11 @@ namespace Avalonia.Rendering.Composition.Server
OnAttachedToRoot(Root); OnAttachedToRoot(Root);
} }
} }
protected virtual void OnAttachedToRoot(ServerCompositionTarget target) protected virtual void OnAttachedToRoot(ServerCompositionTarget target)
{ {
} }
protected override void ValuesInvalidated() protected override void ValuesInvalidated()
{ {
_isDirtyForUpdate = true; _isDirtyForUpdate = true;
@ -274,6 +271,4 @@ namespace Avalonia.Rendering.Composition.Server
public Rect TransformedOwnContentBounds { get; set; } public Rect TransformedOwnContentBounds { get; set; }
public virtual Rect OwnContentBounds => new Rect(0, 0, Size.X, Size.Y); public virtual Rect OwnContentBounds => new Rect(0, 0, Size.X, Size.Y);
} }
} }

2
src/Avalonia.Base/Rendering/DirtyRects.cs

@ -30,7 +30,7 @@ namespace Avalonia.Rendering
/// </remarks> /// </remarks>
public void Add(Rect rect) public void Add(Rect rect)
{ {
if (!rect.IsDefault) if (rect.Width != 0 || rect.Height != 0)
{ {
for (var i = 0; i < _rects.Count; ++i) for (var i = 0; i < _rects.Count; ++i)
{ {

2
src/Avalonia.Base/Rendering/DisplayDirtyRect.cs

@ -3,7 +3,7 @@
namespace Avalonia.Rendering namespace Avalonia.Rendering
{ {
/// <summary> /// <summary>
/// Holds the state for a dirty rect rendered when <see cref="IRenderer.DrawDirtyRects"/> is set. /// Holds the state for a dirty rect rendered when <see cref="IRenderer.SceneInvalidated"/> is set.
/// </summary> /// </summary>
internal class DisplayDirtyRect internal class DisplayDirtyRect
{ {

1
src/Avalonia.Base/Rendering/SceneGraph/GeometryNode.cs

@ -17,7 +17,6 @@ namespace Avalonia.Rendering.SceneGraph
/// <param name="brush">The fill brush.</param> /// <param name="brush">The fill brush.</param>
/// <param name="pen">The stroke pen.</param> /// <param name="pen">The stroke pen.</param>
/// <param name="geometry">The geometry.</param> /// <param name="geometry">The geometry.</param>
/// <param name="aux">Auxiliary data required to draw the brush.</param>
public GeometryNode(Matrix transform, public GeometryNode(Matrix transform,
IImmutableBrush? brush, IImmutableBrush? brush,
IPen? pen, IPen? pen,

1
src/Avalonia.Base/Rendering/SceneGraph/LineNode.cs

@ -17,7 +17,6 @@ namespace Avalonia.Rendering.SceneGraph
/// <param name="pen">The stroke pen.</param> /// <param name="pen">The stroke pen.</param>
/// <param name="p1">The start point of the line.</param> /// <param name="p1">The start point of the line.</param>
/// <param name="p2">The end point of the line.</param> /// <param name="p2">The end point of the line.</param>
/// <param name="aux">Auxiliary data required to draw the brush.</param>
public LineNode( public LineNode(
Matrix transform, Matrix transform,
IPen pen, IPen pen,

1
src/Avalonia.Base/Rendering/SceneGraph/OpacityMaskNode.cs

@ -17,7 +17,6 @@ namespace Avalonia.Rendering.SceneGraph
/// </summary> /// </summary>
/// <param name="mask">The opacity mask to push.</param> /// <param name="mask">The opacity mask to push.</param>
/// <param name="bounds">The bounds of the mask.</param> /// <param name="bounds">The bounds of the mask.</param>
/// <param name="aux">Auxiliary data required to draw the brush.</param>
public OpacityMaskNode(IImmutableBrush mask, Rect bounds) public OpacityMaskNode(IImmutableBrush mask, Rect bounds)
: base(default, Matrix.Identity, mask) : base(default, Matrix.Identity, mask)
{ {

1
src/Avalonia.Base/Rendering/SceneGraph/RectangleNode.cs

@ -20,7 +20,6 @@ namespace Avalonia.Rendering.SceneGraph
/// <param name="pen">The stroke pen.</param> /// <param name="pen">The stroke pen.</param>
/// <param name="rect">The rectangle to draw.</param> /// <param name="rect">The rectangle to draw.</param>
/// <param name="boxShadows">The box shadow parameters</param> /// <param name="boxShadows">The box shadow parameters</param>
/// <param name="aux">Auxiliary data required to draw the brush.</param>
public RectangleNode( public RectangleNode(
Matrix transform, Matrix transform,
IImmutableBrush? brush, IImmutableBrush? brush,

11
src/Avalonia.Base/Size.cs

@ -27,12 +27,6 @@ namespace Avalonia
/// </summary> /// </summary>
public static readonly Size Infinity = new Size(double.PositiveInfinity, double.PositiveInfinity); public static readonly Size Infinity = new Size(double.PositiveInfinity, double.PositiveInfinity);
/// <summary>
/// A size representing zero.
/// </summary>
[Obsolete("Use the default keyword instead.")]
public static readonly Size Empty = new Size(0, 0);
/// <summary> /// <summary>
/// The width. /// The width.
/// </summary> /// </summary>
@ -306,10 +300,5 @@ namespace Avalonia
width = this._width; width = this._width;
height = this._height; height = this._height;
} }
/// <summary>
/// Gets a value indicating whether the Width and Height values are zero.
/// </summary>
public bool IsDefault => (_width == 0) && (_height == 0);
} }
} }

21
src/Avalonia.Base/StyledProperty.cs

@ -18,7 +18,10 @@ namespace Avalonia
/// <param name="ownerType">The type of the class that registers the property.</param> /// <param name="ownerType">The type of the class that registers the property.</param>
/// <param name="metadata">The property metadata.</param> /// <param name="metadata">The property metadata.</param>
/// <param name="inherits">Whether the property inherits its value.</param> /// <param name="inherits">Whether the property inherits its value.</param>
/// <param name="validate">A value validation callback.</param> /// <param name="validate">
/// <para>A method which returns "false" for values that are never valid for this property.</para>
/// <para>This method is not part of the property's metadata and so cannot be changed after registration.</para>
/// </param>
/// <param name="notifying">A <see cref="AvaloniaProperty.Notifying"/> callback.</param> /// <param name="notifying">A <see cref="AvaloniaProperty.Notifying"/> callback.</param>
public StyledProperty( public StyledProperty(
string name, string name,
@ -31,7 +34,6 @@ namespace Avalonia
{ {
Inherits = inherits; Inherits = inherits;
ValidateValue = validate; ValidateValue = validate;
HasCoercion |= metadata.CoerceValue != null;
if (validate?.Invoke(metadata.DefaultValue) == false) if (validate?.Invoke(metadata.DefaultValue) == false)
{ {
@ -41,16 +43,10 @@ namespace Avalonia
} }
/// <summary> /// <summary>
/// Gets the value validation callback for the property. /// A method which returns "false" for values that are never valid for this property.
/// </summary> /// </summary>
public Func<TValue, bool>? ValidateValue { get; } public Func<TValue, bool>? ValidateValue { get; }
/// <summary>
/// Gets a value indicating whether this property has any value coercion callbacks defined
/// in its metadata.
/// </summary>
internal bool HasCoercion { get; private set; }
/// <summary> /// <summary>
/// Registers the property on another type. /// Registers the property on another type.
/// </summary> /// </summary>
@ -127,10 +123,7 @@ namespace Avalonia
/// </summary> /// </summary>
/// <typeparam name="T">The type.</typeparam> /// <typeparam name="T">The type.</typeparam>
/// <param name="metadata">The metadata.</param> /// <param name="metadata">The metadata.</param>
public void OverrideMetadata<T>(StyledPropertyMetadata<TValue> metadata) where T : AvaloniaObject public void OverrideMetadata<T>(StyledPropertyMetadata<TValue> metadata) where T : AvaloniaObject => OverrideMetadata(typeof(T), metadata);
{
base.OverrideMetadata(typeof(T), metadata);
}
/// <summary> /// <summary>
/// Overrides the metadata for the property on the specified type. /// Overrides the metadata for the property on the specified type.
@ -148,8 +141,6 @@ namespace Avalonia
} }
} }
HasCoercion |= metadata.CoerceValue != null;
base.OverrideMetadata(type, metadata); base.OverrideMetadata(type, metadata);
} }

2
src/Avalonia.Base/StyledPropertyMetadata`1.cs

@ -58,5 +58,7 @@ namespace Avalonia
} }
} }
} }
public override AvaloniaPropertyMetadata GenerateTypeSafeMetadata() => new StyledPropertyMetadata<TValue>(DefaultValue, DefaultBindingMode, enableDataValidation: EnableDataValidation ?? false);
} }
} }

2
src/Avalonia.Base/Styling/TypeNameAndClassSelector.cs

@ -52,7 +52,7 @@ namespace Avalonia.Styling
return result; return result;
} }
protected TypeNameAndClassSelector(Selector? previous) TypeNameAndClassSelector(Selector? previous)
{ {
_previous = previous; _previous = previous;
} }

10
src/Avalonia.Base/Thickness.cs

@ -97,10 +97,6 @@ namespace Avalonia
/// </summary> /// </summary>
public double Bottom => _bottom; public double Bottom => _bottom;
/// <inheritdoc cref="IsDefault"/>
[Obsolete("Use IsDefault instead.")]
public bool IsEmpty => IsDefault;
/// <summary> /// <summary>
/// Gets a value indicating whether all sides are equal. /// Gets a value indicating whether all sides are equal.
/// </summary> /// </summary>
@ -293,11 +289,5 @@ namespace Avalonia
right = this._right; right = this._right;
bottom = this._bottom; bottom = this._bottom;
} }
/// <summary>
/// Gets a value indicating whether the instance has default values
/// (the left, top, right and bottom values are zero).
/// </summary>
public bool IsDefault => (_left == 0) && (_top == 0) && (_right == 0) && (_bottom == 0);
} }
} }

8
src/Avalonia.Base/Vector.cs

@ -360,13 +360,5 @@ namespace Avalonia
x = this._x; x = this._x;
y = this._y; y = this._y;
} }
/// <summary>
/// Gets a value indicating whether the X and Y components are zero.
/// </summary>
public bool IsDefault
{
get { return (_x == 0) && (_y == 0); }
}
} }
} }

63
src/Avalonia.Base/VisualTree/VisualExtensions.cs

@ -204,6 +204,69 @@ namespace Avalonia.VisualTree
} }
} }
public static TransformedBounds? GetTransformedBounds(this Visual visual)
{
Rect clip = default;
var transform = Matrix.Identity;
bool Visit(Visual visual)
{
if (!visual.IsVisible)
return false;
// The visual's bounds in local coordinates.
var bounds = new Rect(visual.Bounds.Size);
// If the visual has no parent, we've reached the root. We start the clip
// rectangle with these bounds.
if (visual.GetVisualParent() is not { } parent)
{
clip = bounds;
return true;
}
// Otherwise recurse until the root visual is found, exiting early if one of the
// ancestors is invisible.
if (!Visit(parent))
return false;
// Calculate the transform for this control from its offset and render transform.
var renderTransform = Matrix.Identity;
if (visual.HasMirrorTransform)
{
var mirrorMatrix = new Matrix(-1.0, 0.0, 0.0, 1.0, visual.Bounds.Width, 0);
renderTransform *= mirrorMatrix;
}
if (visual.RenderTransform != null)
{
var origin = visual.RenderTransformOrigin.ToPixels(bounds.Size);
var offset = Matrix.CreateTranslation(origin);
var finalTransform = (-offset) * visual.RenderTransform.Value * offset;
renderTransform *= finalTransform;
}
transform = renderTransform *
Matrix.CreateTranslation(visual.Bounds.Position) *
transform;
// If the visual is clipped, update the clip bounds.
if (visual.ClipToBounds)
{
var globalBounds = bounds.TransformToAABB(transform);
var clipBounds = visual.ClipToBounds ?
globalBounds.Intersect(clip) :
clip;
clip = clip.Intersect(clipBounds);
}
return true;
}
return Visit(visual) ? new(new(visual.Bounds.Size), clip, transform) : null;
}
/// <summary> /// <summary>
/// Gets the first visual in the visual tree whose bounds contain a point. /// Gets the first visual in the visual tree whose bounds contain a point.
/// </summary> /// </summary>

2
src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorPicker.xaml

@ -165,7 +165,7 @@
</TabItem.Header> </TabItem.Header>
<ListBox Theme="{StaticResource ColorViewPaletteListBoxTheme}" <ListBox Theme="{StaticResource ColorViewPaletteListBoxTheme}"
ItemContainerTheme="{StaticResource ColorViewPaletteListBoxItemTheme}" ItemContainerTheme="{StaticResource ColorViewPaletteListBoxItemTheme}"
Items="{TemplateBinding PaletteColors}" ItemsSource="{TemplateBinding PaletteColors}"
SelectedItem="{Binding Color, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource DoNothingForNullConverter}, Mode=TwoWay}" SelectedItem="{Binding Color, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource DoNothingForNullConverter}, Mode=TwoWay}"
UseLayoutRounding="False" UseLayoutRounding="False"
Margin="12"> Margin="12">

2
src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorView.xaml

@ -414,7 +414,7 @@
</TabItem.Header> </TabItem.Header>
<ListBox Theme="{StaticResource ColorViewPaletteListBoxTheme}" <ListBox Theme="{StaticResource ColorViewPaletteListBoxTheme}"
ItemContainerTheme="{StaticResource ColorViewPaletteListBoxItemTheme}" ItemContainerTheme="{StaticResource ColorViewPaletteListBoxItemTheme}"
Items="{TemplateBinding PaletteColors}" ItemsSource="{TemplateBinding PaletteColors}"
SelectedItem="{Binding Color, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource DoNothingForNullConverter}, Mode=TwoWay}" SelectedItem="{Binding Color, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource DoNothingForNullConverter}, Mode=TwoWay}"
UseLayoutRounding="False" UseLayoutRounding="False"
Margin="12"> Margin="12">

2
src/Avalonia.Controls.ColorPicker/Themes/Simple/ColorPicker.xaml

@ -165,7 +165,7 @@
</TabItem.Header> </TabItem.Header>
<ListBox Theme="{StaticResource ColorViewPaletteListBoxTheme}" <ListBox Theme="{StaticResource ColorViewPaletteListBoxTheme}"
ItemContainerTheme="{StaticResource ColorViewPaletteListBoxItemTheme}" ItemContainerTheme="{StaticResource ColorViewPaletteListBoxItemTheme}"
Items="{TemplateBinding PaletteColors}" ItemsSource="{TemplateBinding PaletteColors}"
SelectedItem="{Binding Color, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource DoNothingForNullConverter}, Mode=TwoWay}" SelectedItem="{Binding Color, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource DoNothingForNullConverter}, Mode=TwoWay}"
UseLayoutRounding="False" UseLayoutRounding="False"
Margin="12"> Margin="12">

2
src/Avalonia.Controls.ColorPicker/Themes/Simple/ColorView.xaml

@ -376,7 +376,7 @@
</TabItem.Header> </TabItem.Header>
<ListBox Theme="{StaticResource ColorViewPaletteListBoxTheme}" <ListBox Theme="{StaticResource ColorViewPaletteListBoxTheme}"
ItemContainerTheme="{StaticResource ColorViewPaletteListBoxItemTheme}" ItemContainerTheme="{StaticResource ColorViewPaletteListBoxItemTheme}"
Items="{TemplateBinding PaletteColors}" ItemsSource="{TemplateBinding PaletteColors}"
SelectedItem="{Binding Color, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource DoNothingForNullConverter}, Mode=TwoWay}" SelectedItem="{Binding Color, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource DoNothingForNullConverter}, Mode=TwoWay}"
UseLayoutRounding="False" UseLayoutRounding="False"
Margin="12"> Margin="12">

5
src/Avalonia.Controls.DataGrid/DataGrid.cs

@ -6066,8 +6066,9 @@ namespace Avalonia.Controls
var numberOfItem = clipboardRowContent.Count; var numberOfItem = clipboardRowContent.Count;
for (int cellIndex = 0; cellIndex < numberOfItem; cellIndex++) for (int cellIndex = 0; cellIndex < numberOfItem; cellIndex++)
{ {
var cellContent = clipboardRowContent[cellIndex]; var cellContent = clipboardRowContent[cellIndex].Content?.ToString();
text.Append(cellContent.Content); cellContent = cellContent?.Replace("\"", "\"\"");
text.Append($"\"{cellContent}\"");
if (cellIndex < numberOfItem - 1) if (cellIndex < numberOfItem - 1)
{ {
text.Append('\t'); text.Append('\t');

8
src/Avalonia.Controls.DataGrid/DataGridCheckBoxColumn.cs

@ -177,14 +177,14 @@ namespace Avalonia.Controls
} }
bool? uneditedValue = editingCheckBox.IsChecked; bool? uneditedValue = editingCheckBox.IsChecked;
if(editingEventArgs is PointerPressedEventArgs args) if (editingEventArgs is PointerPressedEventArgs args)
{ {
void ProcessPointerArgs() void ProcessPointerArgs()
{ {
// Editing was triggered by a mouse click // Editing was triggered by a mouse click
Point position = args.GetPosition(editingCheckBox); Point position = args.GetPosition(editingCheckBox);
Rect rect = new Rect(0, 0, editingCheckBox.Bounds.Width, editingCheckBox.Bounds.Height); Rect rect = new Rect(0, 0, editingCheckBox.Bounds.Width, editingCheckBox.Bounds.Height);
if(rect.Contains(position)) if (rect.Contains(position))
{ {
EditValue(); EditValue();
} }
@ -192,14 +192,14 @@ namespace Avalonia.Controls
void OnLayoutUpdated(object sender, EventArgs e) void OnLayoutUpdated(object sender, EventArgs e)
{ {
if(!editingCheckBox.Bounds.IsDefault) if (editingCheckBox.Bounds.Width != 0 || editingCheckBox.Bounds.Height != 0)
{ {
editingCheckBox.LayoutUpdated -= OnLayoutUpdated; editingCheckBox.LayoutUpdated -= OnLayoutUpdated;
ProcessPointerArgs(); ProcessPointerArgs();
} }
} }
if(editingCheckBox.Bounds.IsDefault) if (editingCheckBox.Bounds.Width == 0 && editingCheckBox.Bounds.Height == 0)
{ {
editingCheckBox.LayoutUpdated += OnLayoutUpdated; editingCheckBox.LayoutUpdated += OnLayoutUpdated;
} }

5
src/Avalonia.Controls.ItemsRepeater/Controls/ItemsRepeater.cs

@ -39,7 +39,10 @@ namespace Avalonia.Controls
/// Defines the <see cref="Items"/> property. /// Defines the <see cref="Items"/> property.
/// </summary> /// </summary>
public static readonly DirectProperty<ItemsRepeater, IEnumerable?> ItemsProperty = public static readonly DirectProperty<ItemsRepeater, IEnumerable?> ItemsProperty =
ItemsControl.ItemsProperty.AddOwner<ItemsRepeater>(o => o.Items, (o, v) => o.Items = v); AvaloniaProperty.RegisterDirect<ItemsRepeater, IEnumerable?>(
nameof(Items),
o => o.Items,
(o, v) => o.Items = v);
/// <summary> /// <summary>
/// Defines the <see cref="Layout"/> property. /// Defines the <see cref="Layout"/> property.

4
src/Avalonia.Controls.ItemsRepeater/Controls/ViewportManager.cs

@ -441,7 +441,7 @@ namespace Avalonia.Controls
_pendingViewportShift = default; _pendingViewportShift = default;
_unshiftableShift = default; _unshiftableShift = default;
if (_visibleWindow.IsDefault) if (_visibleWindow.Width == 0 && _visibleWindow.Height == 0)
{ {
// We got cleared. // We got cleared.
_layoutExtent = default; _layoutExtent = default;
@ -527,7 +527,7 @@ namespace Avalonia.Controls
private void TryInvalidateMeasure() private void TryInvalidateMeasure()
{ {
// Don't invalidate measure if we have an invalid window. // Don't invalidate measure if we have an invalid window.
if (!_visibleWindow.IsDefault) if (_visibleWindow.Width != 0 || _visibleWindow.Height != 0)
{ {
// We invalidate measure instead of just invalidating arrange because // We invalidate measure instead of just invalidating arrange because
// we don't invalidate measure in UpdateViewport if the view is changing to // we don't invalidate measure in UpdateViewport if the view is changing to

12
src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.Properties.cs

@ -87,12 +87,10 @@ namespace Avalonia.Controls
/// Identifies the <see cref="Text" /> property. /// Identifies the <see cref="Text" /> property.
/// </summary> /// </summary>
/// <value>The identifier for the <see cref="Text" /> property.</value> /// <value>The identifier for the <see cref="Text" /> property.</value>
public static readonly DirectProperty<AutoCompleteBox, string?> TextProperty = public static readonly StyledProperty<string?> TextProperty =
TextBlock.TextProperty.AddOwnerWithDataValidation<AutoCompleteBox>( TextBlock.TextProperty.AddOwner<AutoCompleteBox>(new(string.Empty,
o => o.Text,
(o, v) => o.Text = v,
defaultBindingMode: BindingMode.TwoWay, defaultBindingMode: BindingMode.TwoWay,
enableDataValidation: true); enableDataValidation: true));
/// <summary> /// <summary>
/// Identifies the <see cref="SearchText" /> property. /// Identifies the <see cref="SearchText" /> property.
@ -317,8 +315,8 @@ namespace Avalonia.Controls
/// <see cref="AutoCompleteBox" /> control.</value> /// <see cref="AutoCompleteBox" /> control.</value>
public string? Text public string? Text
{ {
get => _text; get => GetValue(TextProperty);
set => SetAndRaise(TextProperty, ref _text, value); set => SetValue(TextProperty, value);
} }
/// <summary> /// <summary>

3
src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs

@ -198,7 +198,6 @@ namespace Avalonia.Controls
private bool _isDropDownOpen; private bool _isDropDownOpen;
private bool _isFocused = false; private bool _isFocused = false;
private string? _text = string.Empty;
private string? _searchText = string.Empty; private string? _searchText = string.Empty;
private AutoCompleteFilterPredicate<object?>? _itemFilter; private AutoCompleteFilterPredicate<object?>? _itemFilter;
@ -1275,7 +1274,7 @@ namespace Avalonia.Controls
if ((userInitiated ?? true) && Text != value) if ((userInitiated ?? true) && Text != value)
{ {
_ignoreTextPropertyChange++; _ignoreTextPropertyChange++;
Text = value; SetCurrentValue(TextProperty, value);
callTextChanged = true; callTextChanged = true;
} }

29
src/Avalonia.Controls/Automation/Peers/ScrollBarAutomationPeer.cs

@ -0,0 +1,29 @@
using Avalonia.Controls.Primitives;
namespace Avalonia.Automation.Peers
{
public class ScrollBarAutomationPeer : RangeBaseAutomationPeer
{
public ScrollBarAutomationPeer(ScrollBar owner) : base(owner)
{
}
override protected string GetClassNameCore()
{
return "ScrollBar";
}
override protected AutomationControlType GetAutomationControlTypeCore()
{
return AutomationControlType.ScrollBar;
}
// AutomationControlType.ScrollBar must return IsContentElement false.
// See http://msdn.microsoft.com/en-us/library/ms743712.aspx
override protected bool IsContentElementCore()
{
return false;
}
}
}

2
src/Avalonia.Controls/BorderVisual.cs

@ -50,7 +50,7 @@ class CompositionBorderVisual : CompositionDrawListVisual
if (ClipToBounds) if (ClipToBounds)
{ {
var clipRect = Root!.SnapToDevicePixels(new Rect(new Size(Size.X, Size.Y))); var clipRect = Root!.SnapToDevicePixels(new Rect(new Size(Size.X, Size.Y)));
if (_cornerRadius.IsDefault) if (_cornerRadius == default)
canvas.PushClip(clipRect); canvas.PushClip(clipRect);
else else
canvas.PushClip(new RoundedRect(clipRect, _cornerRadius)); canvas.PushClip(new RoundedRect(clipRect, _cornerRadius));

20
src/Avalonia.Controls/ContainerClearingEventArgs.cs

@ -0,0 +1,20 @@
using System;
namespace Avalonia.Controls
{
/// <summary>
/// Provides data for the <see cref="ItemsControl.ContainerClearing"/> event.
/// </summary>
public class ContainerClearingEventArgs : EventArgs
{
public ContainerClearingEventArgs(Control container)
{
Container = container;
}
/// <summary>
/// Gets the prepared container.
/// </summary>
public Control Container { get; }
}
}

32
src/Avalonia.Controls/ContainerIndexChangedEventArgs.cs

@ -0,0 +1,32 @@
using System;
namespace Avalonia.Controls
{
/// <summary>
/// Provides data for the <see cref="ItemsControl.ContainerIndexChanged"/> event.
/// </summary>
public class ContainerIndexChangedEventArgs : EventArgs
{
public ContainerIndexChangedEventArgs(Control container, int oldIndex, int newIndex)
{
Container = container;
OldIndex = oldIndex;
NewIndex = newIndex;
}
/// <summary>
/// Get the container for which the index changed.
/// </summary>
public Control Container { get; }
/// <summary>
/// Gets the index of the container after the change.
/// </summary>
public int NewIndex { get; }
/// <summary>
/// Gets the index of the container before the change.
/// </summary>
public int OldIndex { get; }
}
}

26
src/Avalonia.Controls/ContainerPreparedEventArgs.cs

@ -0,0 +1,26 @@
using System;
namespace Avalonia.Controls
{
/// <summary>
/// Provides data for the <see cref="ItemsControl.ContainerPrepared"/> event.
/// </summary>
public class ContainerPreparedEventArgs : EventArgs
{
public ContainerPreparedEventArgs(Control container, int index)
{
Container = container;
Index = index;
}
/// <summary>
/// Gets the prepared container.
/// </summary>
public Control Container { get; }
/// <summary>
/// Gets the index of the item the container was prepared for.
/// </summary>
public int Index { get; }
}
}

103
src/Avalonia.Controls/ContextMenu.cs

@ -54,11 +54,17 @@ namespace Avalonia.Controls
public static readonly StyledProperty<PopupGravity> PlacementGravityProperty = public static readonly StyledProperty<PopupGravity> PlacementGravityProperty =
Popup.PlacementGravityProperty.AddOwner<ContextMenu>(); Popup.PlacementGravityProperty.AddOwner<ContextMenu>();
/// <summary>
/// Defines the <see cref="Placement"/> property.
/// </summary>
public static readonly StyledProperty<PlacementMode> PlacementProperty =
Popup.PlacementProperty.AddOwner<ContextMenu>();
/// <summary> /// <summary>
/// Defines the <see cref="PlacementMode"/> property. /// Defines the <see cref="PlacementMode"/> property.
/// </summary> /// </summary>
public static readonly StyledProperty<PlacementMode> PlacementModeProperty = [Obsolete("Use the Placement property instead.")]
Popup.PlacementModeProperty.AddOwner<ContextMenu>(); public static readonly StyledProperty<PlacementMode> PlacementModeProperty = PlacementProperty;
/// <summary> /// <summary>
/// Defines the <see cref="PlacementRect"/> property. /// Defines the <see cref="PlacementRect"/> property.
@ -108,99 +114,80 @@ namespace Avalonia.Controls
static ContextMenu() static ContextMenu()
{ {
ItemsPanelProperty.OverrideDefaultValue<ContextMenu>(DefaultPanel); ItemsPanelProperty.OverrideDefaultValue<ContextMenu>(DefaultPanel);
PlacementModeProperty.OverrideDefaultValue<ContextMenu>(PlacementMode.Pointer); PlacementProperty.OverrideDefaultValue<ContextMenu>(PlacementMode.Pointer);
ContextMenuProperty.Changed.Subscribe(ContextMenuChanged); ContextMenuProperty.Changed.Subscribe(ContextMenuChanged);
AutomationProperties.AccessibilityViewProperty.OverrideDefaultValue<ContextMenu>(AccessibilityView.Control); AutomationProperties.AccessibilityViewProperty.OverrideDefaultValue<ContextMenu>(AccessibilityView.Control);
AutomationProperties.ControlTypeOverrideProperty.OverrideDefaultValue<ContextMenu>(AutomationControlType.Menu); AutomationProperties.ControlTypeOverrideProperty.OverrideDefaultValue<ContextMenu>(AutomationControlType.Menu);
} }
/// <summary> /// <inheritdoc cref="Popup.HorizontalOffset"/>
/// Gets or sets the Horizontal offset of the context menu in relation to the <see cref="PlacementTarget"/>.
/// </summary>
public double HorizontalOffset public double HorizontalOffset
{ {
get { return GetValue(HorizontalOffsetProperty); } get => GetValue(HorizontalOffsetProperty);
set { SetValue(HorizontalOffsetProperty, value); } set => SetValue(HorizontalOffsetProperty, value);
} }
/// <summary> /// <inheritdoc cref="Popup.VerticalOffset"/>
/// Gets or sets the Vertical offset of the context menu in relation to the <see cref="PlacementTarget"/>.
/// </summary>
public double VerticalOffset public double VerticalOffset
{ {
get { return GetValue(VerticalOffsetProperty); } get => GetValue(VerticalOffsetProperty);
set { SetValue(VerticalOffsetProperty, value); } set => SetValue(VerticalOffsetProperty, value);
} }
/// <summary> /// <inheritdoc cref="Popup.PlacementAnchor"/>
/// Gets or sets the anchor point on the <see cref="PlacementRect"/> when <see cref="PlacementMode"/>
/// is <see cref="PlacementMode.AnchorAndGravity"/>.
/// </summary>
public PopupAnchor PlacementAnchor public PopupAnchor PlacementAnchor
{ {
get { return GetValue(PlacementAnchorProperty); } get => GetValue(PlacementAnchorProperty);
set { SetValue(PlacementAnchorProperty, value); } set => SetValue(PlacementAnchorProperty, value);
} }
/// <summary> /// <inheritdoc cref="Popup.PlacementConstraintAdjustment"/>
/// Gets or sets a value describing how the context menu position will be adjusted if the
/// unadjusted position would result in the context menu being partly constrained.
/// </summary>
public PopupPositionerConstraintAdjustment PlacementConstraintAdjustment public PopupPositionerConstraintAdjustment PlacementConstraintAdjustment
{ {
get { return GetValue(PlacementConstraintAdjustmentProperty); } get => GetValue(PlacementConstraintAdjustmentProperty);
set { SetValue(PlacementConstraintAdjustmentProperty, value); } set => SetValue(PlacementConstraintAdjustmentProperty, value);
} }
/// <summary> /// <inheritdoc cref="Popup.PlacementGravity"/>
/// Gets or sets a value which defines in what direction the context menu should open
/// when <see cref="PlacementMode"/> is <see cref="PlacementMode.AnchorAndGravity"/>.
/// </summary>
public PopupGravity PlacementGravity public PopupGravity PlacementGravity
{ {
get { return GetValue(PlacementGravityProperty); } get => GetValue(PlacementGravityProperty);
set { SetValue(PlacementGravityProperty, value); } set => SetValue(PlacementGravityProperty, value);
} }
/// <summary> /// <inheritdoc cref="Placement"/>
/// Gets or sets the placement mode of the context menu in relation to the<see cref="PlacementTarget"/>. [Obsolete("Use the Placement property instead.")]
/// </summary>
public PlacementMode PlacementMode public PlacementMode PlacementMode
{ {
get { return GetValue(PlacementModeProperty); } get => GetValue(PlacementProperty);
set { SetValue(PlacementModeProperty, value); } set => SetValue(PlacementProperty, value);
}
/// <inheritdoc cref="Popup.Placement"/>
public PlacementMode Placement
{
get => GetValue(PlacementProperty);
set => SetValue(PlacementProperty, value);
} }
public bool WindowManagerAddShadowHint public bool WindowManagerAddShadowHint
{ {
get { return GetValue(WindowManagerAddShadowHintProperty); } get => GetValue(WindowManagerAddShadowHintProperty);
set { SetValue(WindowManagerAddShadowHintProperty, value); } set => SetValue(WindowManagerAddShadowHintProperty, value);
} }
/// <summary> /// <inheritdoc cref="Popup.PlacementRect"/>
/// Gets or sets the the anchor rectangle within the parent that the context menu will be placed
/// relative to when <see cref="PlacementMode"/> is <see cref="PlacementMode.AnchorAndGravity"/>.
/// </summary>
/// <remarks>
/// The placement rect defines a rectangle relative to <see cref="PlacementTarget"/> around
/// which the popup will be opened, with <see cref="PlacementAnchor"/> determining which edge
/// of the placement target is used.
///
/// If unset, the anchor rectangle will be the bounds of the <see cref="PlacementTarget"/>.
/// </remarks>
public Rect? PlacementRect public Rect? PlacementRect
{ {
get { return GetValue(PlacementRectProperty); } get => GetValue(PlacementRectProperty);
set { SetValue(PlacementRectProperty, value); } set => SetValue(PlacementRectProperty, value);
} }
/// <summary> /// <inheritdoc cref="Popup.PlacementTarget"/>
/// Gets or sets the control that is used to determine the popup's position.
/// </summary>
public Control? PlacementTarget public Control? PlacementTarget
{ {
get { return GetValue(PlacementTargetProperty); } get => GetValue(PlacementTargetProperty);
set { SetValue(PlacementTargetProperty, value); } set => SetValue(PlacementTargetProperty, value);
} }
/// <summary> /// <summary>
@ -343,9 +330,9 @@ namespace Avalonia.Controls
((ISetLogicalParent)_popup).SetParent(control); ((ISetLogicalParent)_popup).SetParent(control);
} }
_popup.PlacementMode = !requestedByPointer && PlacementMode == PlacementMode.Pointer _popup.Placement = !requestedByPointer && Placement == PlacementMode.Pointer
? PlacementMode.Bottom ? PlacementMode.Bottom
: PlacementMode; : Placement;
//Position of the line below is really important. //Position of the line below is really important.
//All styles are being applied only when control has logical parent. //All styles are being applied only when control has logical parent.

2
src/Avalonia.Controls/DateTimePickers/DatePicker.cs

@ -389,7 +389,7 @@ namespace Avalonia.Controls
_presenter.Date = SelectedDate ?? DateTimeOffset.Now; _presenter.Date = SelectedDate ?? DateTimeOffset.Now;
_popup.PlacementMode = PlacementMode.AnchorAndGravity; _popup.Placement = PlacementMode.AnchorAndGravity;
_popup.PlacementAnchor = Primitives.PopupPositioning.PopupAnchor.Bottom; _popup.PlacementAnchor = Primitives.PopupPositioning.PopupAnchor.Bottom;
_popup.PlacementGravity = Primitives.PopupPositioning.PopupGravity.Bottom; _popup.PlacementGravity = Primitives.PopupPositioning.PopupGravity.Bottom;
_popup.PlacementConstraintAdjustment = Primitives.PopupPositioning.PopupPositionerConstraintAdjustment.SlideY; _popup.PlacementConstraintAdjustment = Primitives.PopupPositioning.PopupPositionerConstraintAdjustment.SlideY;

2
src/Avalonia.Controls/DateTimePickers/TimePicker.cs

@ -255,7 +255,7 @@ namespace Avalonia.Controls
_presenter.Time = SelectedTime ?? DateTime.Now.TimeOfDay; _presenter.Time = SelectedTime ?? DateTime.Now.TimeOfDay;
_popup.PlacementMode = PlacementMode.AnchorAndGravity; _popup.Placement = PlacementMode.AnchorAndGravity;
_popup.PlacementAnchor = Primitives.PopupPositioning.PopupAnchor.Bottom; _popup.PlacementAnchor = Primitives.PopupPositioning.PopupAnchor.Bottom;
_popup.PlacementGravity = Primitives.PopupPositioning.PopupGravity.Bottom; _popup.PlacementGravity = Primitives.PopupPositioning.PopupGravity.Bottom;
_popup.PlacementConstraintAdjustment = Primitives.PopupPositioning.PopupPositionerConstraintAdjustment.SlideY; _popup.PlacementConstraintAdjustment = Primitives.PopupPositioning.PopupPositionerConstraintAdjustment.SlideY;

8
src/Avalonia.Controls/Documents/InlineCollection.cs

@ -91,11 +91,11 @@ namespace Avalonia.Controls.Documents
public override void Add(Inline inline) public override void Add(Inline inline)
{ {
if (InlineHost is TextBlock textBlock && !string.IsNullOrEmpty(textBlock._text)) if (InlineHost is TextBlock textBlock && !string.IsNullOrEmpty(textBlock.Text))
{ {
base.Add(new Run(textBlock._text)); base.Add(new Run(textBlock.Text));
textBlock._text = null; textBlock.ClearTextInternal();
} }
base.Add(inline); base.Add(inline);
@ -113,7 +113,7 @@ namespace Avalonia.Controls.Documents
{ {
if (InlineHost is TextBlock textBlock && !textBlock.HasComplexContent) if (InlineHost is TextBlock textBlock && !textBlock.HasComplexContent)
{ {
textBlock._text += text; textBlock.Text += text;
} }
else else
{ {

4
src/Avalonia.Controls/Flyouts/MenuFlyout.cs

@ -19,7 +19,9 @@ namespace Avalonia.Controls
/// Defines the <see cref="Items"/> property /// Defines the <see cref="Items"/> property
/// </summary> /// </summary>
public static readonly DirectProperty<MenuFlyout, IEnumerable?> ItemsProperty = public static readonly DirectProperty<MenuFlyout, IEnumerable?> ItemsProperty =
ItemsControl.ItemsProperty.AddOwner<MenuFlyout>(x => x.Items, AvaloniaProperty.RegisterDirect<MenuFlyout, IEnumerable?>(
nameof(Items),
x => x.Items,
(x, v) => x.Items = v); (x, v) => x.Items = v);
/// <summary> /// <summary>

16
src/Avalonia.Controls/Flyouts/PopupFlyoutBase.cs

@ -14,9 +14,9 @@ namespace Avalonia.Controls.Primitives
{ {
public abstract class PopupFlyoutBase : FlyoutBase, IPopupHostProvider public abstract class PopupFlyoutBase : FlyoutBase, IPopupHostProvider
{ {
/// <inheritdoc cref="Popup.PlacementModeProperty"/> /// <inheritdoc cref="Popup.PlacementProperty"/>
public static readonly StyledProperty<PlacementMode> PlacementProperty = public static readonly StyledProperty<PlacementMode> PlacementProperty =
Popup.PlacementModeProperty.AddOwner<PopupFlyoutBase>(); Popup.PlacementProperty.AddOwner<PopupFlyoutBase>();
/// <inheritdoc cref="Popup.HorizontalOffsetProperty"/> /// <inheritdoc cref="Popup.HorizontalOffsetProperty"/>
public static readonly StyledProperty<double> HorizontalOffsetProperty = public static readonly StyledProperty<double> HorizontalOffsetProperty =
@ -64,15 +64,13 @@ namespace Avalonia.Controls.Primitives
protected Popup Popup => _popupLazy.Value; protected Popup Popup => _popupLazy.Value;
/// <summary> /// <inheritdoc cref="Popup.Placement"/>
/// Gets or sets the desired placement.
/// </summary>
public PlacementMode Placement public PlacementMode Placement
{ {
get => GetValue(PlacementProperty); get => GetValue(PlacementProperty);
set => SetValue(PlacementProperty, value); set => SetValue(PlacementProperty, value);
} }
/// <inheritdoc cref="Popup.PlacementGravity"/> /// <inheritdoc cref="Popup.PlacementGravity"/>
public PopupGravity PlacementGravity public PopupGravity PlacementGravity
{ {
@ -407,7 +405,7 @@ namespace Avalonia.Controls.Primitives
{ {
Size sz; Size sz;
// Popup.Child can't be null here, it was set in ShowAtCore. // Popup.Child can't be null here, it was set in ShowAtCore.
if (Popup.Child!.DesiredSize.IsDefault) if (Popup.Child!.DesiredSize == default)
{ {
// Popup may not have been shown yet. Measure content // Popup may not have been shown yet. Measure content
sz = LayoutHelper.MeasureChild(Popup.Child, Size.Infinity, new Thickness()); sz = LayoutHelper.MeasureChild(Popup.Child, Size.Infinity, new Thickness());
@ -423,11 +421,11 @@ namespace Avalonia.Controls.Primitives
Popup.PlacementGravity = PlacementGravity; Popup.PlacementGravity = PlacementGravity;
if (showAtPointer) if (showAtPointer)
{ {
Popup.PlacementMode = PlacementMode.Pointer; Popup.Placement = PlacementMode.Pointer;
} }
else else
{ {
Popup.PlacementMode = Placement; Popup.Placement = Placement;
Popup.PlacementConstraintAdjustment = Popup.PlacementConstraintAdjustment =
PopupPositioning.PopupPositionerConstraintAdjustment.SlideX | PopupPositioning.PopupPositionerConstraintAdjustment.SlideX |
PopupPositioning.PopupPositionerConstraintAdjustment.SlideY; PopupPositioning.PopupPositionerConstraintAdjustment.SlideY;

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save