Browse Source

Merge branch 'master' into remove-obsolete-members-from-avalonia.base-namespace

pull/8707/head
Max Katz 4 years ago
committed by GitHub
parent
commit
1a23d44fb4
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      .github/PULL_REQUEST_TEMPLATE.md
  2. 6
      build/HarfBuzzSharp.props
  3. 6
      build/SkiaSharp.props
  4. 2
      native/Avalonia.Native/src/OSX/WindowImpl.mm
  5. 6
      samples/ControlCatalog/MainView.xaml.cs
  6. 8
      samples/ControlCatalog/Models/Person.cs
  7. 2
      samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml
  8. 24
      samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml.cs
  9. 25
      samples/ControlCatalog/Pages/ButtonSpinnerPage.xaml.cs
  10. 2
      samples/ControlCatalog/Pages/ButtonsPage.xaml.cs
  11. 2
      samples/ControlCatalog/Pages/CarouselPage.xaml.cs
  12. 70
      samples/ControlCatalog/Pages/ClipboardPage.xaml.cs
  13. 2
      samples/ControlCatalog/Pages/ComboBoxPage.xaml.cs
  14. 20
      samples/ControlCatalog/Pages/CompositionPage.axaml.cs
  15. 22
      samples/ControlCatalog/Pages/ContextFlyoutPage.xaml.cs
  16. 23
      samples/ControlCatalog/Pages/ContextMenuPage.xaml.cs
  17. 4
      samples/ControlCatalog/Pages/DataGridPage.xaml.cs
  18. 13
      samples/ControlCatalog/Pages/DialogsPage.xaml.cs
  19. 20
      samples/ControlCatalog/Pages/DragAndDropPage.xaml.cs
  20. 2
      samples/ControlCatalog/Pages/FlyoutsPage.axaml.cs
  21. 10
      samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml.cs
  22. 6
      samples/ControlCatalog/Pages/NumericUpDownPage.xaml.cs
  23. 6
      samples/ControlCatalog/Pages/OpenGlPage.xaml.cs
  24. 31
      samples/ControlCatalog/Pages/PointersPage.xaml.cs
  25. 6
      samples/ControlCatalog/ViewModels/TransitioningContentControlPageViewModel.cs
  26. 37
      samples/IntegrationTestApp/MainWindow.axaml.cs
  27. 3
      samples/IntegrationTestApp/ShowWindowTest.axaml
  28. 4
      samples/RenderDemo/Pages/TextFormatterPage.axaml.cs
  29. 12
      src/Android/Avalonia.Android/AndroidPlatform.cs
  30. 45
      src/Android/Avalonia.Android/AndroidThreadingInterface.cs
  31. 3
      src/Avalonia.Base/Input/PointerOverPreProcessor.cs
  32. 12
      src/Avalonia.Base/Platform/Storage/FileIO/BclStorageFile.cs
  33. 9
      src/Avalonia.Base/Platform/Storage/FileIO/BclStorageFolder.cs
  34. 7
      src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionTarget.cs
  35. 35
      src/Avalonia.Base/Utilities/MathUtilities.cs
  36. 30
      src/Avalonia.Controls.DataGrid/DataGrid.cs
  37. 11
      src/Avalonia.Controls/AutoCompleteBox.cs
  38. 77
      src/Avalonia.Controls/Calendar/Calendar.cs
  39. 71
      src/Avalonia.Controls/Calendar/CalendarItem.cs
  40. 2
      src/Avalonia.Controls/Calendar/SelectedDatesCollection.cs
  41. 2
      src/Avalonia.Controls/GridLength.cs
  42. 4
      src/Avalonia.Controls/Platform/InternalPlatformThreadingInterface.cs
  43. 36
      src/Avalonia.Controls/Shapes/Arc.cs
  44. 97
      src/Avalonia.Controls/Shapes/Sector.cs
  45. 48
      src/Avalonia.Headless/HeadlessPlatformThreadingInterface.cs
  46. 3
      src/Avalonia.OpenGL/Controls/OpenGlControlBase.cs
  47. 50
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDeferredResourceTransformer.cs
  48. 26
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTransformer.cs
  49. 152
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs
  50. 2
      src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github
  51. 14
      src/Skia/Avalonia.Skia/Gpu/OpenGl/OpenGlBitmapImpl.cs
  52. 3
      src/Windows/Avalonia.Win32/WindowImpl.cs
  53. 20
      tests/Avalonia.Controls.UnitTests/GridLengthTests.cs
  54. 42
      tests/Avalonia.IntegrationTests.Appium/WindowTests.cs
  55. 29
      tests/Avalonia.IntegrationTests.Appium/WindowTests_MacOS.cs

2
.github/PULL_REQUEST_TEMPLATE.md

@ -21,7 +21,7 @@
- [ ] Consider submitting a PR to https://github.com/AvaloniaUI/Documentation with user documentation - [ ] Consider submitting a PR to https://github.com/AvaloniaUI/Documentation with user documentation
## Breaking changes ## Breaking changes
<!--- List any breaking changes here. When the PR is merged please add an entry to https://github.com/AvaloniaUI/Avalonia/wiki/Breaking-Changes --> <!--- List any breaking changes here. -->
## Obsoletions / Deprecations ## Obsoletions / Deprecations
<!--- Obsolete and Deprecated attributes on APIs MUST only be included when discussed with Core team. @grokys, @kekekeks & @danwalmsley --> <!--- Obsolete and Deprecated attributes on APIs MUST only be included when discussed with Core team. @grokys, @kekekeks & @danwalmsley -->

6
build/HarfBuzzSharp.props

@ -1,7 +1,7 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup> <ItemGroup>
<PackageReference Include="HarfBuzzSharp" Version="2.8.2" /> <PackageReference Include="HarfBuzzSharp" Version="2.8.2.1-preview.108" />
<PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="HarfBuzzSharp.NativeAssets.Linux" Version="2.8.2" /> <PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="HarfBuzzSharp.NativeAssets.Linux" Version="2.8.2.1-preview.108" />
<PackageReference Condition="'$(IncludeWasmSkia)' == 'true'" Include="HarfBuzzSharp.NativeAssets.WebAssembly" Version="2.8.2" /> <PackageReference Condition="'$(IncludeWasmSkia)' == 'true'" Include="HarfBuzzSharp.NativeAssets.WebAssembly" Version="2.8.2.1-preview.108" />
</ItemGroup> </ItemGroup>
</Project> </Project>

6
build/SkiaSharp.props

@ -1,7 +1,7 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup> <ItemGroup>
<PackageReference Include="SkiaSharp" Version="2.88.1-preview.1" /> <PackageReference Include="SkiaSharp" Version="2.88.1-preview.108" />
<PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="SkiaSharp.NativeAssets.Linux" Version="2.88.1-preview.1" /> <PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="SkiaSharp.NativeAssets.Linux" Version="2.88.1-preview.108" />
<PackageReference Condition="'$(IncludeWasmSkia)' == 'true'" Include="SkiaSharp.NativeAssets.WebAssembly" Version="2.88.1-preview.1" /> <PackageReference Condition="'$(IncludeWasmSkia)' == 'true'" Include="SkiaSharp.NativeAssets.WebAssembly" Version="2.88.1-preview.108" />
</ItemGroup> </ItemGroup>
</Project> </Project>

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

@ -121,7 +121,7 @@ void WindowImpl::BringToFront()
{ {
if(Window != nullptr) if(Window != nullptr)
{ {
if (![Window isMiniaturized]) if ([Window isVisible] && ![Window isMiniaturized])
{ {
if(IsDialog()) if(IsDialog())
{ {

6
samples/ControlCatalog/MainView.xaml.cs

@ -22,8 +22,8 @@ namespace ControlCatalog
if (AvaloniaLocator.Current?.GetService<IRuntimePlatform>()?.GetRuntimeInfo().IsDesktop == true) if (AvaloniaLocator.Current?.GetService<IRuntimePlatform>()?.GetRuntimeInfo().IsDesktop == true)
{ {
IList tabItems = ((IList)sideBar.Items); var tabItems = (sideBar.Items as IList);
tabItems.Add(new TabItem() tabItems?.Add(new TabItem()
{ {
Header = "Screens", Header = "Screens",
Content = new ScreenPage() Content = new ScreenPage()
@ -36,7 +36,7 @@ namespace ControlCatalog
{ {
if (themes.SelectedItem is CatalogTheme theme) if (themes.SelectedItem is CatalogTheme theme)
{ {
var themeStyle = Application.Current.Styles[0]; var themeStyle = Application.Current!.Styles[0];
if (theme == CatalogTheme.FluentLight) if (theme == CatalogTheme.FluentLight)
{ {
if (App.Fluent.Mode != FluentThemeMode.Light) if (App.Fluent.Mode != FluentThemeMode.Light)

8
samples/ControlCatalog/Models/Person.cs

@ -85,7 +85,7 @@ namespace ControlCatalog.Models
} }
else else
{ {
if (_errorLookup.TryGetValue(propertyName, out List<string> errorList)) if (_errorLookup.TryGetValue(propertyName, out var errorList))
{ {
errorList.Clear(); errorList.Clear();
errorList.Add(error!); errorList.Add(error!);
@ -114,12 +114,12 @@ namespace ControlCatalog.Models
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
} }
public IEnumerable? GetErrors(string propertyName) public IEnumerable GetErrors(string? propertyName)
{ {
if (_errorLookup.TryGetValue(propertyName, out List<string> errorList)) if (propertyName is { } && _errorLookup.TryGetValue(propertyName, out var errorList))
return errorList; return errorList;
else else
return null; return Array.Empty<object>();
} }
} }
} }

2
samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml

@ -28,7 +28,7 @@
</StackPanel> </StackPanel>
<StackPanel> <StackPanel>
<TextBlock Text="MinimumPopulateDelay: 1s" /> <TextBlock Text="MinimumPopulateDelay: 1s" />
<AutoCompleteBox MinimumPopulateDelay="1" /> <AutoCompleteBox MinimumPopulateDelay="00:00:01" />
</StackPanel> </StackPanel>
<StackPanel> <StackPanel>
<TextBlock Text="MaxDropDownHeight: 60" /> <TextBlock Text="MaxDropDownHeight: 60" />

24
samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml.cs

@ -1,8 +1,6 @@
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.LogicalTree; using Avalonia.LogicalTree;
using Avalonia.Markup;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.Markup.Data;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@ -161,23 +159,23 @@ namespace ControlCatalog.Pages
private bool LastWordContains(string? searchText, string? item) private bool LastWordContains(string? searchText, string? item)
{ {
var words = searchText?.Split(' ') ?? Array.Empty<string>(); var words = searchText?.Split(' ') ?? Array.Empty<string>();
var options = Sentences.Select(x => x.First).ToArray(); var options = Sentences.Select(x => x.First)
.ToArray<LinkedListNode<string>?>();
for (var i = 0; i < words.Length; ++i) for (var i = 0; i < words.Length; ++i)
{ {
var word = words[i]; var word = words[i];
for (var j = 0; word is { } && j < options.Length; ++j) for (var j = 0; word is { } && j < options.Length; ++j)
{ {
var option = options[j]; if (options[i] is { } option)
if (option == null)
continue;
if (i == words.Length - 1)
{
options[j] = option.Value.ToLower().Contains(word.ToLower()) ? option : null;
}
else
{ {
options[j] = option.Value.Equals(word, StringComparison.InvariantCultureIgnoreCase) ? option.Next : null; if (i == words.Length - 1)
{
options[j] = option.Value.ToLower().Contains(word.ToLower()) ? option : null;
}
else
{
options[j] = option.Value.Equals(word, StringComparison.InvariantCultureIgnoreCase) ? option.Next : null;
}
} }
} }
} }

25
samples/ControlCatalog/Pages/ButtonSpinnerPage.xaml.cs

@ -21,20 +21,23 @@ namespace ControlCatalog.Pages
public void OnSpin(object sender, SpinEventArgs e) public void OnSpin(object sender, SpinEventArgs e)
{ {
var spinner = (ButtonSpinner)sender; var spinner = (ButtonSpinner)sender;
var txtBox = (TextBlock)spinner.Content;
int value = Array.IndexOf(_mountains, txtBox?.Text); if (spinner.Content is TextBlock txtBox)
if (e.Direction == SpinDirection.Increase) {
value++; int value = Array.IndexOf(_mountains, txtBox.Text);
else if (e.Direction == SpinDirection.Increase)
value--; value++;
else
value--;
if (value < 0) if (value < 0)
value = _mountains.Length - 1; value = _mountains.Length - 1;
else if (value >= _mountains.Length) else if (value >= _mountains.Length)
value = 0; value = 0;
txtBox.Text = _mountains[value];
}
txtBox.Text = _mountains[value];
} }
private readonly string[] _mountains = new[] private readonly string[] _mountains = new[]

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

@ -19,7 +19,7 @@ namespace ControlCatalog.Pages
AvaloniaXamlLoader.Load(this); AvaloniaXamlLoader.Load(this);
} }
public void OnRepeatButtonClick(object sender, object args) public void OnRepeatButtonClick(object? sender, object args)
{ {
repeatButtonClickCount++; repeatButtonClickCount++;
var textBlock = this.Get<TextBlock>("RepeatButtonTextBlock"); var textBlock = this.Get<TextBlock>("RepeatButtonTextBlock");

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

@ -33,7 +33,7 @@ namespace ControlCatalog.Pages
} }
private void TransitionChanged(object sender, SelectionChangedEventArgs e) private void TransitionChanged(object? sender, SelectionChangedEventArgs e)
{ {
switch (_transition.SelectedIndex) switch (_transition.SelectedIndex)
{ {

70
samples/ControlCatalog/Pages/ClipboardPage.xaml.cs

@ -23,55 +23,79 @@ namespace ControlCatalog.Pages
AvaloniaXamlLoader.Load(this); AvaloniaXamlLoader.Load(this);
} }
private async void CopyText(object sender, RoutedEventArgs args) private async void CopyText(object? sender, RoutedEventArgs args)
{ {
await Application.Current.Clipboard.SetTextAsync(ClipboardContent.Text); if (Application.Current!.Clipboard is { } clipboard && ClipboardContent is { } clipboardContent)
await clipboard.SetTextAsync(clipboardContent.Text ?? String.Empty);
} }
private async void PasteText(object sender, RoutedEventArgs args) private async void PasteText(object? sender, RoutedEventArgs args)
{ {
ClipboardContent.Text = await Application.Current.Clipboard.GetTextAsync(); if(Application.Current!.Clipboard is { } clipboard)
{
ClipboardContent.Text = await clipboard.GetTextAsync();
}
} }
private async void CopyTextDataObject(object sender, RoutedEventArgs args) private async void CopyTextDataObject(object? sender, RoutedEventArgs args)
{ {
var dataObject = new DataObject(); if (Application.Current!.Clipboard is { } clipboard)
dataObject.Set(DataFormats.Text, ClipboardContent.Text ?? string.Empty); {
await Application.Current.Clipboard.SetDataObjectAsync(dataObject); var dataObject = new DataObject();
dataObject.Set(DataFormats.Text, ClipboardContent.Text ?? string.Empty);
await clipboard.SetDataObjectAsync(dataObject);
}
} }
private async void PasteTextDataObject(object sender, RoutedEventArgs args) private async void PasteTextDataObject(object? sender, RoutedEventArgs args)
{ {
ClipboardContent.Text = await Application.Current.Clipboard.GetDataAsync(DataFormats.Text) as string ?? string.Empty; if (Application.Current!.Clipboard is { } clipboard)
{
ClipboardContent.Text = await clipboard.GetDataAsync(DataFormats.Text) as string ?? string.Empty;
}
} }
private async void CopyFilesDataObject(object sender, RoutedEventArgs args) private async void CopyFilesDataObject(object? sender, RoutedEventArgs args)
{ {
var files = ClipboardContent.Text.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries); if (Application.Current!.Clipboard is { } clipboard)
if (files.Length == 0)
{ {
return; var files = (ClipboardContent.Text ?? String.Empty)
.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
if (files.Length == 0)
{
return;
}
var dataObject = new DataObject();
dataObject.Set(DataFormats.FileNames, files);
await clipboard.SetDataObjectAsync(dataObject);
} }
var dataObject = new DataObject();
dataObject.Set(DataFormats.FileNames, files);
await Application.Current.Clipboard.SetDataObjectAsync(dataObject);
} }
private async void PasteFilesDataObject(object sender, RoutedEventArgs args) private async void PasteFilesDataObject(object? sender, RoutedEventArgs args)
{ {
var fiels = await Application.Current.Clipboard.GetDataAsync(DataFormats.FileNames) as IEnumerable<string>; if (Application.Current!.Clipboard is { } clipboard)
ClipboardContent.Text = fiels != null ? string.Join(Environment.NewLine, fiels) : string.Empty; {
var fiels = await clipboard.GetDataAsync(DataFormats.FileNames) as IEnumerable<string>;
ClipboardContent.Text = fiels != null ? string.Join(Environment.NewLine, fiels) : string.Empty;
}
} }
private async void GetFormats(object sender, RoutedEventArgs args) private async void GetFormats(object sender, RoutedEventArgs args)
{ {
var formats = await Application.Current.Clipboard.GetFormatsAsync(); if (Application.Current!.Clipboard is { } clipboard)
ClipboardContent.Text = string.Join(Environment.NewLine, formats); {
var formats = await clipboard.GetFormatsAsync();
ClipboardContent.Text = string.Join(Environment.NewLine, formats);
}
} }
private async void Clear(object sender, RoutedEventArgs args) private async void Clear(object sender, RoutedEventArgs args)
{ {
await Application.Current.Clipboard.ClearAsync(); if (Application.Current!.Clipboard is { } clipboard)
{
await clipboard.ClearAsync();
}
} }
} }
} }

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

@ -17,7 +17,7 @@ namespace ControlCatalog.Pages
private void InitializeComponent() private void InitializeComponent()
{ {
AvaloniaXamlLoader.Load(this); AvaloniaXamlLoader.Load(this);
var fontComboBox = this.Find<ComboBox>("fontComboBox"); var fontComboBox = this.Get<ComboBox>("fontComboBox");
fontComboBox.Items = FontManager.Current.GetInstalledFontFamilyNames().Select(x => new FontFamily(x)); fontComboBox.Items = FontManager.Current.GetInstalledFontFamilyNames().Select(x => new FontFamily(x));
fontComboBox.SelectedIndex = 0; fontComboBox.SelectedIndex = 0;
} }

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

@ -1,14 +1,8 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Threading.Tasks;
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.Markup.Xaml.Templates;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Rendering.Composition; using Avalonia.Rendering.Composition;
using Avalonia.Rendering.Composition.Animations; using Avalonia.Rendering.Composition.Animations;
@ -18,7 +12,7 @@ namespace ControlCatalog.Pages;
public partial class CompositionPage : UserControl public partial class CompositionPage : UserControl
{ {
private ImplicitAnimationCollection _implicitAnimations; private ImplicitAnimationCollection? _implicitAnimations;
public CompositionPage() public CompositionPage()
{ {
@ -28,7 +22,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.FindControl<ItemsControl>("Items").Items = CreateColorItems(); this.Get<ItemsControl>("Items").Items = CreateColorItems();
} }
private List<CompositionPageColorItem> CreateColorItems() private List<CompositionPageColorItem> CreateColorItems()
@ -115,7 +109,6 @@ public partial class CompositionPage : UserControl
public static void SetEnableAnimations(Border border, bool value) public static void SetEnableAnimations(Border border, bool value)
{ {
var page = border.FindAncestorOfType<CompositionPage>(); var page = border.FindAncestorOfType<CompositionPage>();
if (page == null) if (page == null)
{ {
@ -127,8 +120,11 @@ public partial class CompositionPage : UserControl
return; return;
page.EnsureImplicitAnimations(); page.EnsureImplicitAnimations();
ElementComposition.GetElementVisual((Visual)border.GetVisualParent()).ImplicitAnimations = if (border.GetVisualParent() is Visual visualParent
page._implicitAnimations; && ElementComposition.GetElementVisual(visualParent) is CompositionVisual compositionVisual)
{
compositionVisual.ImplicitAnimations = page._implicitAnimations;
}
} }
} }
@ -150,4 +146,4 @@ public class CompositionPageColorItem
{ {
Color = color; Color = color;
} }
} }

22
samples/ControlCatalog/Pages/ContextFlyoutPage.xaml.cs

@ -52,13 +52,13 @@ namespace ControlCatalog.Pages
base.OnDataContextChanged(e); base.OnDataContextChanged(e);
} }
private void ContextFlyoutPage_Closing(object sender, CancelEventArgs e) private void ContextFlyoutPage_Closing(object? sender, CancelEventArgs e)
{ {
var cancelCloseCheckBox = this.FindControl<CheckBox>("CancelCloseCheckBox"); var cancelCloseCheckBox = this.FindControl<CheckBox>("CancelCloseCheckBox");
e.Cancel = cancelCloseCheckBox?.IsChecked ?? false; e.Cancel = cancelCloseCheckBox?.IsChecked ?? false;
} }
private void ContextFlyoutPage_Opening(object sender, EventArgs e) private void ContextFlyoutPage_Opening(object? sender, EventArgs e)
{ {
if (e is CancelEventArgs cancelArgs) if (e is CancelEventArgs cancelArgs)
{ {
@ -67,20 +67,20 @@ namespace ControlCatalog.Pages
} }
} }
private void CloseFlyout(object sender, RoutedEventArgs e) private void CloseFlyout(object? sender, RoutedEventArgs e)
{ {
_textBox.ContextFlyout?.Hide(); _textBox.ContextFlyout?.Hide();
} }
public void CustomContextRequested(object sender, ContextRequestedEventArgs e) public void CustomContextRequested(object? sender, ContextRequestedEventArgs e)
{ {
var border = (Border)sender; if (sender is Border border && border.Child is TextBlock textBlock)
var textBlock = (TextBlock)border.Child; {
textBlock.Text = e.TryGetPosition(border, out var point)
textBlock.Text = e.TryGetPosition(border, out var point) ? $"Context was requested with pointer at: {point.X:N0}, {point.Y:N0}"
? $"Context was requested with pointer at: {point.X:N0}, {point.Y:N0}" : "Context was requested without pointer";
: "Context was requested without pointer"; e.Handled = true;
e.Handled = true; }
} }
private void InitializeComponent() private void InitializeComponent()

23
samples/ControlCatalog/Pages/ContextMenuPage.xaml.cs

@ -35,30 +35,31 @@ namespace ControlCatalog.Pages
base.OnDataContextChanged(e); base.OnDataContextChanged(e);
} }
private void ContextFlyoutPage_Closing(object sender, CancelEventArgs e) private void ContextFlyoutPage_Closing(object? sender, CancelEventArgs e)
{ {
var cancelCloseCheckBox = this.FindControl<CheckBox>("CancelCloseCheckBox"); var cancelCloseCheckBox = this.FindControl<CheckBox>("CancelCloseCheckBox");
e.Cancel = cancelCloseCheckBox.IsChecked ?? false; e.Cancel = cancelCloseCheckBox?.IsChecked ?? false;
} }
private void ContextFlyoutPage_Opening(object sender, EventArgs e) private void ContextFlyoutPage_Opening(object? sender, EventArgs e)
{ {
if (e is CancelEventArgs cancelArgs) if (e is CancelEventArgs cancelArgs)
{ {
var cancelCloseCheckBox = this.FindControl<CheckBox>("CancelOpenCheckBox"); var cancelCloseCheckBox = this.FindControl<CheckBox>("CancelOpenCheckBox");
cancelArgs.Cancel = cancelCloseCheckBox.IsChecked ?? false; cancelArgs.Cancel = cancelCloseCheckBox?.IsChecked ?? false;
} }
} }
public void CustomContextRequested(object sender, ContextRequestedEventArgs e) public void CustomContextRequested(object? sender, ContextRequestedEventArgs e)
{ {
var border = (Border)sender; if (sender is Border border && border.Child is TextBlock textBlock)
var textBlock = (TextBlock)border.Child; {
textBlock.Text = e.TryGetPosition(border, out var point)
? $"Context was requested with pointer at: {point.X:N0}, {point.Y:N0}"
: "Context was requested without pointer";
e.Handled = true;
}
textBlock.Text = e.TryGetPosition(border, out var point)
? $"Context was requested with pointer at: {point.X:N0}, {point.Y:N0}"
: "Context was requested without pointer";
e.Handled = true;
} }
private void InitializeComponent() private void InitializeComponent()

4
samples/ControlCatalog/Pages/DataGridPage.xaml.cs

@ -62,7 +62,7 @@ namespace ControlCatalog.Pages
addButton.Click += (a, b) => collectionView3.AddNew(); addButton.Click += (a, b) => collectionView3.AddNew();
} }
private void Dg1_LoadingRow(object sender, DataGridRowEventArgs e) private void Dg1_LoadingRow(object? sender, DataGridRowEventArgs e)
{ {
e.Row.Header = e.Row.GetIndex() + 1; e.Row.Header = e.Row.GetIndex() + 1;
} }
@ -74,7 +74,7 @@ namespace ControlCatalog.Pages
private class ReversedStringComparer : IComparer<object>, IComparer private class ReversedStringComparer : IComparer<object>, IComparer
{ {
public int Compare(object x, object y) public int Compare(object? x, object? y)
{ {
if (x is string left && y is string right) if (x is string left && y is string right)
{ {

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

@ -111,9 +111,16 @@ namespace ControlCatalog.Pages
Title = "Select folder", Title = "Select folder",
Directory = lastSelectedDirectory?.TryGetUri(out var path) == true ? path.LocalPath : null Directory = lastSelectedDirectory?.TryGetUri(out var path) == true ? path.LocalPath : null
}.ShowAsync(GetWindow()); }.ShowAsync(GetWindow());
lastSelectedDirectory = new BclStorageFolder(new System.IO.DirectoryInfo(result)); if (string.IsNullOrEmpty(result))
results.Items = new [] { result }; {
resultsVisible.IsVisible = result != null; resultsVisible.IsVisible = false;
}
else
{
lastSelectedDirectory = new BclStorageFolder(new System.IO.DirectoryInfo(result));
results.Items = new[] { result };
resultsVisible.IsVisible = true;
}
}; };
this.Get<Button>("OpenBoth").Click += async delegate this.Get<Button>("OpenBoth").Click += async delegate
{ {

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

@ -1,9 +1,9 @@
using Avalonia.Controls; using System;
using Avalonia.Input;
using Avalonia.Markup.Xaml;
using System;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Markup.Xaml;
namespace ControlCatalog.Pages namespace ControlCatalog.Pages
{ {
@ -27,9 +27,9 @@ namespace ControlCatalog.Pages
void SetupDnd(string suffix, Action<DataObject> factory, DragDropEffects effects) void SetupDnd(string suffix, Action<DataObject> factory, DragDropEffects effects)
{ {
var dragMe = this.Get<Border>("DragMe" + suffix); var dragMe = this.Get<Border>("DragMe" + suffix);
var dragState = this.Get<TextBlock>("DragState"+suffix); var dragState = this.Get<TextBlock>("DragState" + suffix);
async void DoDrag(object sender, Avalonia.Input.PointerPressedEventArgs e) async void DoDrag(object? sender, Avalonia.Input.PointerPressedEventArgs e)
{ {
var dragData = new DataObject(); var dragData = new DataObject();
factory(dragData); factory(dragData);
@ -55,7 +55,7 @@ namespace ControlCatalog.Pages
} }
} }
void DragOver(object sender, DragEventArgs e) void DragOver(object? sender, DragEventArgs e)
{ {
if (e.Source is Control c && c.Name == "MoveTarget") if (e.Source is Control c && c.Name == "MoveTarget")
{ {
@ -73,7 +73,7 @@ namespace ControlCatalog.Pages
e.DragEffects = DragDropEffects.None; e.DragEffects = DragDropEffects.None;
} }
void Drop(object sender, DragEventArgs e) void Drop(object? sender, DragEventArgs e)
{ {
if (e.Source is Control c && c.Name == "MoveTarget") if (e.Source is Control c && c.Name == "MoveTarget")
{ {
@ -83,11 +83,11 @@ namespace ControlCatalog.Pages
{ {
e.DragEffects = e.DragEffects & (DragDropEffects.Copy); e.DragEffects = e.DragEffects & (DragDropEffects.Copy);
} }
if (e.Data.Contains(DataFormats.Text)) if (e.Data.Contains(DataFormats.Text))
_DropState.Text = e.Data.GetText(); _DropState.Text = e.Data.GetText();
else if (e.Data.Contains(DataFormats.FileNames)) else if (e.Data.Contains(DataFormats.FileNames))
_DropState.Text = string.Join(Environment.NewLine, e.Data.GetFileNames()); _DropState.Text = string.Join(Environment.NewLine, e.Data.GetFileNames() ?? Array.Empty<string>());
else if (e.Data.Contains(CustomFormat)) else if (e.Data.Contains(CustomFormat))
_DropState.Text = "Custom: " + e.Data.Get(CustomFormat); _DropState.Text = "Custom: " + e.Data.Get(CustomFormat);
} }

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

@ -20,7 +20,7 @@ namespace ControlCatalog.Pages
SetXamlTexts(); SetXamlTexts();
} }
private void Afp_DoubleTapped(object sender, RoutedEventArgs e) private void Afp_DoubleTapped(object? sender, RoutedEventArgs e)
{ {
if (sender is Panel p) if (sender is Panel p)
{ {

10
samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml.cs

@ -123,7 +123,7 @@ namespace ControlCatalog.Pages
element.BringIntoView(); element.BringIntoView();
} }
private void RepeaterClick(object sender, PointerPressedEventArgs e) private void RepeaterClick(object? sender, PointerPressedEventArgs e)
{ {
if ((e.Source as TextBlock)?.DataContext is ItemsRepeaterPageViewModel.Item item) if ((e.Source as TextBlock)?.DataContext is ItemsRepeaterPageViewModel.Item item)
{ {
@ -132,7 +132,7 @@ namespace ControlCatalog.Pages
} }
} }
private void RepeaterOnKeyDown(object sender, KeyEventArgs e) private void RepeaterOnKeyDown(object? sender, KeyEventArgs e)
{ {
if (e.Key == Key.F5) if (e.Key == Key.F5)
{ {
@ -140,17 +140,17 @@ namespace ControlCatalog.Pages
} }
} }
private void scrollToLast_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e) private void scrollToLast_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
{ {
ScrollTo(_viewModel.Items.Count - 1); ScrollTo(_viewModel.Items.Count - 1);
} }
private void scrollToRandom_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e) private void scrollToRandom_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
{ {
ScrollTo(_random.Next(_viewModel.Items.Count - 1)); ScrollTo(_random.Next(_viewModel.Items.Count - 1));
} }
private void scrollToSelected_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e) private void scrollToSelected_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
{ {
ScrollTo(_selectedIndex); ScrollTo(_selectedIndex);
} }

6
samples/ControlCatalog/Pages/NumericUpDownPage.xaml.cs

@ -2,9 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using MiniMvvm; using MiniMvvm;
@ -29,7 +27,7 @@ namespace ControlCatalog.Pages
public class NumbersPageViewModel : ViewModelBase public class NumbersPageViewModel : ViewModelBase
{ {
private IList<FormatObject>? _formats; private IList<FormatObject>? _formats;
private FormatObject _selectedFormat; private FormatObject? _selectedFormat;
private IList<Location>? _spinnerLocations; private IList<Location>? _spinnerLocations;
private double _doubleValue; private double _doubleValue;
@ -89,7 +87,7 @@ namespace ControlCatalog.Pages
.Where(c => new[] { "en-US", "en-GB", "fr-FR", "ar-DZ", "zh-CH", "cs-CZ" }.Contains(c.Name)) .Where(c => new[] { "en-US", "en-GB", "fr-FR", "ar-DZ", "zh-CH", "cs-CZ" }.Contains(c.Name))
.ToArray(); .ToArray();
public FormatObject SelectedFormat public FormatObject? SelectedFormat
{ {
get { return _selectedFormat; } get { return _selectedFormat; }
set { this.RaiseAndSetIfChanged(ref _selectedFormat, value); } set { this.RaiseAndSetIfChanged(ref _selectedFormat, value); }

6
samples/ControlCatalog/Pages/OpenGlPage.xaml.cs

@ -68,7 +68,7 @@ namespace ControlCatalog.Pages
set => SetAndRaise(DiscoProperty, ref _disco, value); set => SetAndRaise(DiscoProperty, ref _disco, value);
} }
private string _info; private string _info = string.Empty;
public static readonly DirectProperty<OpenGlPageControl, string> InfoProperty = public static readonly DirectProperty<OpenGlPageControl, string> InfoProperty =
AvaloniaProperty.RegisterDirect<OpenGlPageControl, string>("Info", o => o.Info, (o, v) => o.Info = v); AvaloniaProperty.RegisterDirect<OpenGlPageControl, string>("Info", o => o.Info, (o, v) => o.Info = v);
@ -205,7 +205,7 @@ namespace ControlCatalog.Pages
public OpenGlPageControl() public OpenGlPageControl()
{ {
var name = typeof(OpenGlPage).Assembly.GetManifestResourceNames().First(x => x.Contains("teapot.bin")); var name = typeof(OpenGlPage).Assembly.GetManifestResourceNames().First(x => x.Contains("teapot.bin"));
using (var sr = new BinaryReader(typeof(OpenGlPage).Assembly.GetManifestResourceStream(name))) using (var sr = new BinaryReader(typeof(OpenGlPage).Assembly.GetManifestResourceStream(name)!))
{ {
var buf = new byte[sr.ReadInt32()]; var buf = new byte[sr.ReadInt32()];
sr.Read(buf, 0, buf.Length); sr.Read(buf, 0, buf.Length);
@ -345,7 +345,7 @@ namespace ControlCatalog.Pages
0.01f, 1000); 0.01f, 1000);
var view = Matrix4x4.CreateLookAt(new Vector3(25, 25, 25), new Vector3(), new Vector3(0, -1, 0)); var view = Matrix4x4.CreateLookAt(new Vector3(25, 25, 25), new Vector3(), new Vector3(0, 1, 0));
var model = Matrix4x4.CreateFromYawPitchRoll(_yaw, _pitch, _roll); var model = Matrix4x4.CreateFromYawPitchRoll(_yaw, _pitch, _roll);
var modelLoc = GL.GetUniformLocationString(_shaderProgram, "uModel"); var modelLoc = GL.GetUniformLocationString(_shaderProgram, "uModel");
var viewLoc = GL.GetUniformLocationString(_shaderProgram, "uView"); var viewLoc = GL.GetUniformLocationString(_shaderProgram, "uView");

31
samples/ControlCatalog/Pages/PointersPage.xaml.cs

@ -1,8 +1,6 @@
using System; using System;
using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
namespace ControlCatalog.Pages; namespace ControlCatalog.Pages;
@ -31,28 +29,33 @@ public class PointersPage : UserControl
border2.PointerExited += Border_PointerUpdated; border2.PointerExited += Border_PointerUpdated;
} }
private void Border_PointerUpdated(object sender, PointerEventArgs e) private void Border_PointerUpdated(object? sender, PointerEventArgs e)
{ {
var textBlock = (TextBlock)((Border)sender).Child; if (sender is Border border && border.Child is TextBlock textBlock)
var position = e.GetPosition((Border)sender); {
textBlock.Text = @$"Type: {e.Pointer.Type} var position = e.GetPosition(border);
textBlock.Text = @$"Type: {e.Pointer.Type}
Captured: {e.Pointer.Captured == sender} Captured: {e.Pointer.Captured == sender}
PointerId: {e.Pointer.Id} PointerId: {e.Pointer.Id}
Position: {(int)position.X} {(int)position.Y}"; Position: {(int)position.X} {(int)position.Y}";
e.Handled = true; e.Handled = true;
}
} }
private void Border_PointerCaptureLost(object sender, PointerCaptureLostEventArgs e) private void Border_PointerCaptureLost(object? sender, PointerCaptureLostEventArgs e)
{ {
var textBlock = (TextBlock)((Border)sender).Child; if (sender is Border border && border.Child is TextBlock textBlock)
textBlock.Text = @$"Type: {e.Pointer.Type} {
textBlock.Text = @$"Type: {e.Pointer.Type}
Captured: {e.Pointer.Captured == sender} Captured: {e.Pointer.Captured == sender}
PointerId: {e.Pointer.Id} PointerId: {e.Pointer.Id}
Position: ??? ???"; Position: ??? ???";
e.Handled = true; e.Handled = true;
}
} }
private void Border_PointerReleased(object sender, PointerReleasedEventArgs e) private void Border_PointerReleased(object? sender, PointerReleasedEventArgs e)
{ {
if (e.Pointer.Captured == sender) if (e.Pointer.Captured == sender)
{ {
@ -65,9 +68,9 @@ Position: ??? ???";
} }
} }
private void Border_PointerPressed(object sender, PointerPressedEventArgs e) private void Border_PointerPressed(object? sender, PointerPressedEventArgs e)
{ {
e.Pointer.Capture((Border)sender); e.Pointer.Capture(sender as Border);
e.Handled = true; e.Handled = true;
} }

6
samples/ControlCatalog/ViewModels/TransitioningContentControlPageViewModel.cs

@ -117,9 +117,9 @@ namespace ControlCatalog.ViewModels
PageTransitions[3].Transition = new PageSlide(TimeSpan.FromMilliseconds(Duration), PageSlide.SlideAxis.Vertical); PageTransitions[3].Transition = new PageSlide(TimeSpan.FromMilliseconds(Duration), PageSlide.SlideAxis.Vertical);
var compositeTransition = new CompositePageTransition(); var compositeTransition = new CompositePageTransition();
compositeTransition.PageTransitions.Add(PageTransitions[1].Transition); compositeTransition.PageTransitions.Add(PageTransitions[1].Transition!);
compositeTransition.PageTransitions.Add(PageTransitions[2].Transition); compositeTransition.PageTransitions.Add(PageTransitions[2].Transition!);
compositeTransition.PageTransitions.Add(PageTransitions[3].Transition); compositeTransition.PageTransitions.Add(PageTransitions[3].Transition!);
PageTransitions[4].Transition = compositeTransition; PageTransitions[4].Transition = compositeTransition;
PageTransitions[5].Transition = new CustomTransition(TimeSpan.FromMilliseconds(Duration)); PageTransitions[5].Transition = new CustomTransition(TimeSpan.FromMilliseconds(Duration));

37
samples/IntegrationTestApp/MainWindow.axaml.cs

@ -1,4 +1,3 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Avalonia; using Avalonia;
@ -31,20 +30,23 @@ namespace IntegrationTestApp
private void InitializeViewMenu() private void InitializeViewMenu()
{ {
var mainTabs = this.FindControl<TabControl>("MainTabs"); var mainTabs = this.Get<TabControl>("MainTabs");
var viewMenu = (NativeMenuItem)NativeMenu.GetMenu(this).Items[1]; var viewMenu = (NativeMenuItem)NativeMenu.GetMenu(this).Items[1];
foreach (TabItem tabItem in mainTabs.Items) if (mainTabs.Items is not null)
{ {
var menuItem = new NativeMenuItem foreach (TabItem tabItem in mainTabs.Items)
{ {
Header = (string)tabItem.Header!, var menuItem = new NativeMenuItem
IsChecked = tabItem.IsSelected, {
ToggleType = NativeMenuItemToggleType.Radio, Header = (string)tabItem.Header!,
}; IsChecked = tabItem.IsSelected,
ToggleType = NativeMenuItemToggleType.Radio,
menuItem.Click += (s, e) => tabItem.IsSelected = true; };
viewMenu.Menu.Items.Add(menuItem);
menuItem.Click += (s, e) => tabItem.IsSelected = true;
viewMenu?.Menu?.Items.Add(menuItem);
}
} }
} }
@ -99,6 +101,7 @@ namespace IntegrationTestApp
foreach (var window in lifetime.Windows) foreach (var window in lifetime.Windows)
{ {
window.Show();
if (window.WindowState == WindowState.Minimized) if (window.WindowState == WindowState.Minimized)
window.WindowState = WindowState.Normal; window.WindowState = WindowState.Normal;
} }
@ -106,8 +109,8 @@ namespace IntegrationTestApp
private void MenuClicked(object? sender, RoutedEventArgs e) private void MenuClicked(object? sender, RoutedEventArgs e)
{ {
var clickedMenuItemTextBlock = this.FindControl<TextBlock>("ClickedMenuItem"); var clickedMenuItemTextBlock = this.Get<TextBlock>("ClickedMenuItem");
clickedMenuItemTextBlock.Text = ((MenuItem)sender!).Header.ToString(); clickedMenuItemTextBlock.Text = (sender as MenuItem)?.Header?.ToString();
} }
private void OnButtonClick(object? sender, RoutedEventArgs e) private void OnButtonClick(object? sender, RoutedEventArgs e)
@ -115,13 +118,13 @@ namespace IntegrationTestApp
var source = e.Source as Button; var source = e.Source as Button;
if (source?.Name == "ComboBoxSelectionClear") if (source?.Name == "ComboBoxSelectionClear")
this.FindControl<ComboBox>("BasicComboBox").SelectedIndex = -1; this.Get<ComboBox>("BasicComboBox").SelectedIndex = -1;
if (source?.Name == "ComboBoxSelectFirst") if (source?.Name == "ComboBoxSelectFirst")
this.FindControl<ComboBox>("BasicComboBox").SelectedIndex = 0; this.Get<ComboBox>("BasicComboBox").SelectedIndex = 0;
if (source?.Name == "ListBoxSelectionClear") if (source?.Name == "ListBoxSelectionClear")
this.FindControl<ListBox>("BasicListBox").SelectedIndex = -1; this.Get<ListBox>("BasicListBox").SelectedIndex = -1;
if (source?.Name == "MenuClickedMenuItemReset") if (source?.Name == "MenuClickedMenuItemReset")
this.FindControl<TextBlock>("ClickedMenuItem").Text = "None"; this.Get<TextBlock>("ClickedMenuItem").Text = "None";
if (source?.Name == "ShowWindow") if (source?.Name == "ShowWindow")
ShowWindow(); ShowWindow();
if (source?.Name == "SendToBack") if (source?.Name == "SendToBack")

3
samples/IntegrationTestApp/ShowWindowTest.axaml

@ -3,7 +3,7 @@
x:Class="IntegrationTestApp.ShowWindowTest" x:Class="IntegrationTestApp.ShowWindowTest"
Name="SecondaryWindow" Name="SecondaryWindow"
Title="Show Window Test"> Title="Show Window Test">
<Grid ColumnDefinitions="Auto,Auto" RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto"> <Grid ColumnDefinitions="Auto,Auto" RowDefinitions="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="ClientSize" Grid.Column="1" Grid.Row="1" IsReadOnly="True" <TextBox Name="ClientSize" Grid.Column="1" Grid.Row="1" IsReadOnly="True"
Text="{Binding ClientSize, Mode=OneWay}"/> Text="{Binding ClientSize, Mode=OneWay}"/>
@ -31,5 +31,6 @@
<ComboBoxItem>Maximized</ComboBoxItem> <ComboBoxItem>Maximized</ComboBoxItem>
<ComboBoxItem>Fullscreen</ComboBoxItem> <ComboBoxItem>Fullscreen</ComboBoxItem>
</ComboBox> </ComboBox>
<Button Name="HideButton" Grid.Row="8" Command="{Binding $parent[Window].Hide}">Hide</Button>
</Grid> </Grid>
</Window> </Window>

4
samples/RenderDemo/Pages/TextFormatterPage.axaml.cs

@ -78,7 +78,7 @@ namespace RenderDemo.Pages
_defaultProperties = defaultProperties; _defaultProperties = defaultProperties;
} }
public TextRun? GetTextRun(int textSourceIndex) public TextRun GetTextRun(int textSourceIndex)
{ {
if (textSourceIndex >= _text.Length * 2 + TextRun.DefaultTextSourceLength) if (textSourceIndex >= _text.Length * 2 + TextRun.DefaultTextSourceLength)
{ {
@ -107,7 +107,7 @@ namespace RenderDemo.Pages
public Control Control => _control; public Control Control => _control;
public override Size Size => _control.DesiredSize; public override Size Size => _control.DesiredSize;
public override double Baseline => 0; public override double Baseline => 0;
public override TextRunProperties? Properties { get; } public override TextRunProperties Properties { get; }
public override void Draw(DrawingContext drawingContext, Point origin) public override void Draw(DrawingContext drawingContext, Point origin)
{ {

12
src/Android/Avalonia.Android/AndroidPlatform.cs

@ -17,10 +17,8 @@ namespace Avalonia
{ {
public static T UseAndroid<T>(this T builder) where T : AppBuilderBase<T>, new() public static T UseAndroid<T>(this T builder) where T : AppBuilderBase<T>, new()
{ {
var options = AvaloniaLocator.Current.GetService<AndroidPlatformOptions>() ?? new AndroidPlatformOptions();
return builder return builder
.UseWindowingSubsystem(() => AndroidPlatform.Initialize(options), "Android") .UseWindowingSubsystem(() => AndroidPlatform.Initialize(), "Android")
.UseSkia(); .UseSkia();
} }
} }
@ -45,9 +43,9 @@ namespace Avalonia.Android
internal static Compositor Compositor { get; private set; } internal static Compositor Compositor { get; private set; }
public static void Initialize(AndroidPlatformOptions options) public static void Initialize()
{ {
Options = options; Options = AvaloniaLocator.Current.GetService<AndroidPlatformOptions>() ?? new AndroidPlatformOptions();
AvaloniaLocator.CurrentMutable AvaloniaLocator.CurrentMutable
.Bind<IClipboard>().ToTransient<ClipboardImpl>() .Bind<IClipboard>().ToTransient<ClipboardImpl>()
@ -61,12 +59,12 @@ namespace Avalonia.Android
.Bind<IRenderLoop>().ToConstant(new RenderLoop()) .Bind<IRenderLoop>().ToConstant(new RenderLoop())
.Bind<PlatformHotkeyConfiguration>().ToSingleton<PlatformHotkeyConfiguration>(); .Bind<PlatformHotkeyConfiguration>().ToSingleton<PlatformHotkeyConfiguration>();
if (options.UseGpu) if (Options.UseGpu)
{ {
EglPlatformOpenGlInterface.TryInitialize(); EglPlatformOpenGlInterface.TryInitialize();
} }
if (options.UseCompositor) if (Options.UseCompositor)
{ {
Compositor = new Compositor( Compositor = new Compositor(
AvaloniaLocator.Current.GetRequiredService<IRenderLoop>(), AvaloniaLocator.Current.GetRequiredService<IRenderLoop>(),

45
src/Android/Avalonia.Android/AndroidThreadingInterface.cs

@ -27,46 +27,33 @@ namespace Avalonia.Android
{ {
if (interval.TotalMilliseconds < 10) if (interval.TotalMilliseconds < 10)
interval = TimeSpan.FromMilliseconds(10); interval = TimeSpan.FromMilliseconds(10);
object l = new object();
var stopped = false; var stopped = false;
Timer timer = null; Timer timer = null;
var scheduled = false;
timer = new Timer(_ => timer = new Timer(_ =>
{ {
lock (l) if (stopped)
return;
EnsureInvokeOnMainThread(() =>
{ {
if (stopped) try
{ {
timer.Dispose(); tick();
return;
} }
if (scheduled) finally
return;
scheduled = true;
EnsureInvokeOnMainThread(() =>
{ {
try if (!stopped)
{ timer.Change(interval, Timeout.InfiniteTimeSpan);
tick(); }
} });
finally },
{ null, interval, Timeout.InfiniteTimeSpan);
lock (l)
{
scheduled = false;
}
}
});
}
}, null, TimeSpan.Zero, interval);
return Disposable.Create(() => return Disposable.Create(() =>
{ {
lock (l) stopped = true;
{ timer.Dispose();
stopped = true;
timer.Dispose();
}
}); });
} }

3
src/Avalonia.Base/Input/PointerOverPreProcessor.cs

@ -158,6 +158,8 @@ namespace Avalonia.Input
ClearPointerOver(pointer, root, timestamp, position, properties, inputModifiers); ClearPointerOver(pointer, root, timestamp, position, properties, inputModifiers);
} }
} }
_lastPointer = (pointer, root.PointToScreen(position));
} }
private void SetPointerOverToElement(IPointer pointer, IInputRoot root, IInputElement element, private void SetPointerOverToElement(IPointer pointer, IInputRoot root, IInputElement element,
@ -195,7 +197,6 @@ namespace Avalonia.Input
} }
el = root.PointerOverElement = element; el = root.PointerOverElement = element;
_lastPointer = (pointer, root.PointToScreen(position));
e.RoutedEvent = InputElement.PointerEnteredEvent; e.RoutedEvent = InputElement.PointerEnteredEvent;

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

@ -12,6 +12,11 @@ public class BclStorageFile : IStorageBookmarkFile
{ {
private readonly FileInfo _fileInfo; private readonly FileInfo _fileInfo;
public BclStorageFile(string fileName)
{
_fileInfo = new FileInfo(fileName);
}
public BclStorageFile(FileInfo fileInfo) public BclStorageFile(FileInfo fileInfo)
{ {
_fileInfo = fileInfo ?? throw new ArgumentNullException(nameof(fileInfo)); _fileInfo = fileInfo ?? throw new ArgumentNullException(nameof(fileInfo));
@ -27,15 +32,14 @@ public class BclStorageFile : IStorageBookmarkFile
public Task<StorageItemProperties> GetBasicPropertiesAsync() public Task<StorageItemProperties> GetBasicPropertiesAsync()
{ {
var props = new StorageItemProperties();
if (_fileInfo.Exists) if (_fileInfo.Exists)
{ {
props = new StorageItemProperties( return Task.FromResult(new StorageItemProperties(
(ulong)_fileInfo.Length, (ulong)_fileInfo.Length,
_fileInfo.CreationTimeUtc, _fileInfo.CreationTimeUtc,
_fileInfo.LastAccessTimeUtc); _fileInfo.LastAccessTimeUtc));
} }
return Task.FromResult(props); return Task.FromResult(new StorageItemProperties());
} }
public Task<IStorageFolder?> GetParentAsync() public Task<IStorageFolder?> GetParentAsync()

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

@ -14,6 +14,15 @@ public class BclStorageFolder : IStorageBookmarkFolder
{ {
private readonly DirectoryInfo _directoryInfo; private readonly DirectoryInfo _directoryInfo;
public BclStorageFolder(string path)
{
_directoryInfo = new DirectoryInfo(path);
if (!_directoryInfo.Exists)
{
throw new ArgumentException("Directory must exist");
}
}
public BclStorageFolder(DirectoryInfo directoryInfo) public BclStorageFolder(DirectoryInfo directoryInfo)
{ {
_directoryInfo = directoryInfo ?? throw new ArgumentNullException(nameof(directoryInfo)); _directoryInfo = directoryInfo ?? throw new ArgumentNullException(nameof(directoryInfo));

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

@ -78,6 +78,13 @@ namespace Avalonia.Rendering.Composition.Server
if (Root == null) if (Root == null)
return; return;
if ((_renderTarget as IRenderTargetWithCorruptionInfo)?.IsCorrupted == true)
{
_renderTarget!.Dispose();
_renderTarget = null;
}
_renderTarget ??= _renderTargetFactory(); _renderTarget ??= _renderTargetFactory();
Compositor.UpdateServerTime(); Compositor.UpdateServerTime();

35
src/Avalonia.Base/Utilities/MathUtilities.cs

@ -324,6 +324,41 @@ namespace Avalonia.Utilities
return angle * 2 * Math.PI; return angle * 2 * Math.PI;
} }
/// <summary>
/// Calculates the point of an angle on an ellipse.
/// </summary>
/// <param name="centre">The centre point of the ellipse.</param>
/// <param name="radiusX">The x radius of the ellipse.</param>
/// <param name="radiusY">The y radius of the ellipse.</param>
/// <param name="angle">The angle in radians.</param>
/// <returns>A point on the ellipse.</returns>
public static Point GetEllipsePoint(Point centre, double radiusX, double radiusY, double angle)
{
return new Point(radiusX * Math.Cos(angle) + centre.X, radiusY * Math.Sin(angle) + centre.Y);
}
/// <summary>
/// Gets the minimum and maximum from the specified numbers.
/// </summary>
/// <param name="a">The first number.</param>
/// <param name="b">The second number.</param>
/// <returns>A tuple containing the minimum and maximum of the two specified numbers.</returns>
public static (double min, double max) GetMinMax(double a, double b)
{
return a < b ? (a, b) : (b, a);
}
/// <summary>
/// Gets the minimum and maximum from the specified number and the difference with that number.
/// </summary>
/// <param name="initialValue">The initial value to use.</param>
/// <param name="delta">The difference for <paramref name="initialValue"/>.</param>
/// <returns>A tuple containing the minimum and maximum of the specified number and the difference with that number.</returns>
public static (double min, double max) GetMinMaxFromDelta(double initialValue, double delta)
{
return GetMinMax(initialValue, initialValue + delta);
}
private static void ThrowCannotBeGreaterThanException<T>(T min, T max) private static void ThrowCannotBeGreaterThanException<T>(T min, T max)
{ {
throw new ArgumentException($"{min} cannot be greater than {max}."); throw new ArgumentException($"{min} cannot be greater than {max}.");

30
src/Avalonia.Controls.DataGrid/DataGrid.cs

@ -2167,7 +2167,23 @@ namespace Avalonia.Controls
return desiredSize; return desiredSize;
} }
/// <inheritdoc/>
protected override void OnDataContextBeginUpdate()
{
base.OnDataContextBeginUpdate();
NotifyDataContextPropertyForAllRowCells(GetAllRows(), true);
}
/// <inheritdoc/>
protected override void OnDataContextEndUpdate()
{
base.OnDataContextEndUpdate();
NotifyDataContextPropertyForAllRowCells(GetAllRows(), false);
}
/// <summary> /// <summary>
/// Raises the BeginningEdit event. /// Raises the BeginningEdit event.
/// </summary> /// </summary>
@ -3165,6 +3181,20 @@ namespace Avalonia.Controls
} }
} }
private static void NotifyDataContextPropertyForAllRowCells(IEnumerable<DataGridRow> rowSource, bool arg2)
{
foreach (DataGridRow row in rowSource)
{
foreach (DataGridCell cell in row.Cells)
{
if (cell.Content is StyledElement cellContent)
{
DataContextProperty.Notifying?.Invoke(cellContent, arg2);
}
}
}
}
private void UpdateRowDetailsVisibilityMode(DataGridRowDetailsVisibilityMode newDetailsMode) private void UpdateRowDetailsVisibilityMode(DataGridRowDetailsVisibilityMode newDetailsMode)
{ {
int itemCount = DataConnection.Count; int itemCount = DataConnection.Count;

11
src/Avalonia.Controls/AutoCompleteBox.cs

@ -392,6 +392,8 @@ namespace Avalonia.Controls
private AutoCompleteSelector<object>? _itemSelector; private AutoCompleteSelector<object>? _itemSelector;
private AutoCompleteSelector<string?>? _textSelector; private AutoCompleteSelector<string?>? _textSelector;
private readonly EventHandler _populateDropDownHandler;
public static readonly RoutedEvent<SelectionChangedEventArgs> SelectionChangedEvent = public static readonly RoutedEvent<SelectionChangedEventArgs> SelectionChangedEvent =
RoutedEvent.Register<SelectionChangedEventArgs>(nameof(SelectionChanged), RoutingStrategies.Bubble, typeof(AutoCompleteBox)); RoutedEvent.Register<SelectionChangedEventArgs>(nameof(SelectionChanged), RoutingStrategies.Bubble, typeof(AutoCompleteBox));
@ -668,6 +670,7 @@ namespace Avalonia.Controls
if (newValue == TimeSpan.Zero) if (newValue == TimeSpan.Zero)
{ {
_delayTimer.Tick -= _populateDropDownHandler;
_delayTimer = null; _delayTimer = null;
} }
} }
@ -678,7 +681,7 @@ namespace Avalonia.Controls
if (_delayTimer == null) if (_delayTimer == null)
{ {
_delayTimer = new DispatcherTimer(); _delayTimer = new DispatcherTimer();
_delayTimer.Tick += PopulateDropDown; _delayTimer.Tick += _populateDropDownHandler;
} }
// Set the new tick interval // Set the new tick interval
@ -864,6 +867,7 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
public AutoCompleteBox() public AutoCompleteBox()
{ {
_populateDropDownHandler = PopulateDropDown;
ClearView(); ClearView();
} }
@ -1771,10 +1775,7 @@ namespace Avalonia.Controls
/// <param name="e">The event arguments.</param> /// <param name="e">The event arguments.</param>
private void PopulateDropDown(object? sender, EventArgs e) private void PopulateDropDown(object? sender, EventArgs e)
{ {
if (_delayTimer != null) _delayTimer?.Stop();
{
_delayTimer.Stop();
}
// Update the prefix/search text. // Update the prefix/search text.
SearchText = Text; SearchText = Text;

77
src/Avalonia.Controls/Calendar/Calendar.cs

@ -224,7 +224,7 @@ namespace Avalonia.Controls
/// </para> /// </para>
/// </remarks> /// </remarks>
[TemplatePart(PART_ElementMonth, typeof(CalendarItem))] [TemplatePart(PART_ElementMonth, typeof(CalendarItem))]
[TemplatePart(PART_ElementRoot, typeof(Panel))] [TemplatePart(PART_ElementRoot, typeof(Panel))]
public class Calendar : TemplatedControl public class Calendar : TemplatedControl
{ {
internal const int RowsPerMonth = 7; internal const int RowsPerMonth = 7;
@ -338,14 +338,11 @@ namespace Avalonia.Controls
/// <param name="e">The DependencyPropertyChangedEventArgs.</param> /// <param name="e">The DependencyPropertyChangedEventArgs.</param>
private void OnIsTodayHighlightedChanged(AvaloniaPropertyChangedEventArgs e) private void OnIsTodayHighlightedChanged(AvaloniaPropertyChangedEventArgs e)
{ {
if (DisplayDate != null) int i = DateTimeHelper.CompareYearMonth(DisplayDateInternal, DateTime.Today);
{
int i = DateTimeHelper.CompareYearMonth(DisplayDateInternal, DateTime.Today);
if (i > -2 && i < 2) if (i > -2 && i < 2)
{ {
UpdateMonths(); UpdateMonths();
}
} }
} }
@ -655,7 +652,7 @@ namespace Avalonia.Controls
SelectedDatesChanged?.Invoke(this, e); SelectedDatesChanged?.Invoke(this, e);
} }
} }
internal Collection<DateTime> RemovedItems { get; set; } internal Collection<DateTime> RemovedItems { get; set; }
internal DateTime? LastSelectedDateInternal { get; set; } internal DateTime? LastSelectedDateInternal { get; set; }
internal DateTime? LastSelectedDate internal DateTime? LastSelectedDate
@ -914,7 +911,7 @@ namespace Avalonia.Controls
o => o.DisplayDateEnd, o => o.DisplayDateEnd,
(o, v) => o.DisplayDateEnd = v, (o, v) => o.DisplayDateEnd = v,
defaultBindingMode: BindingMode.TwoWay); defaultBindingMode: BindingMode.TwoWay);
/// <summary> /// <summary>
/// Gets or sets the last date to be displayed. /// Gets or sets the last date to be displayed.
/// </summary> /// </summary>
@ -1242,7 +1239,7 @@ namespace Avalonia.Controls
{ {
b.IsSelected = false; b.IsSelected = false;
} }
} }
} }
} }
} }
@ -1278,7 +1275,7 @@ namespace Avalonia.Controls
internal void OnPreviousClick() internal void OnPreviousClick()
{ {
if (DisplayMode == CalendarMode.Month && DisplayDate != null) if (DisplayMode == CalendarMode.Month)
{ {
DateTime? d = DateTimeHelper.AddMonths(DateTimeHelper.DiscardDayTime(DisplayDate), -1); DateTime? d = DateTimeHelper.AddMonths(DateTimeHelper.DiscardDayTime(DisplayDate), -1);
if (d.HasValue) if (d.HasValue)
@ -1326,7 +1323,7 @@ namespace Avalonia.Controls
} }
internal void OnNextClick() internal void OnNextClick()
{ {
if (DisplayMode == CalendarMode.Month && DisplayDate != null) if (DisplayMode == CalendarMode.Month)
{ {
DateTime? d = DateTimeHelper.AddMonths(DateTimeHelper.DiscardDayTime(DisplayDate), 1); DateTime? d = DateTimeHelper.AddMonths(DateTimeHelper.DiscardDayTime(DisplayDate), 1);
if (d.HasValue) if (d.HasValue)
@ -1645,7 +1642,7 @@ namespace Avalonia.Controls
{ {
if (DisplayMode == CalendarMode.Month) if (DisplayMode == CalendarMode.Month)
{ {
if (LastSelectedDate.HasValue && DisplayDateInternal != null) if (LastSelectedDate.HasValue)
{ {
// If a blackout day is inactive, when clicked on it, the // If a blackout day is inactive, when clicked on it, the
// previous inactive day which is not a blackout day can get // previous inactive day which is not a blackout day can get
@ -1897,24 +1894,21 @@ namespace Avalonia.Controls
{ {
case CalendarMode.Month: case CalendarMode.Month:
{ {
if (DisplayDate != null) DateTime? selectedDate = new DateTime(DisplayDateInternal.Year, DisplayDateInternal.Month, 1);
{
DateTime? selectedDate = new DateTime(DisplayDateInternal.Year, DisplayDateInternal.Month, 1);
if (DateTimeHelper.CompareYearMonth(DateTime.MaxValue, selectedDate.Value) > 0) if (DateTimeHelper.CompareYearMonth(DateTime.MaxValue, selectedDate.Value) > 0)
{ {
// since DisplayDate is not equal to // since DisplayDate is not equal to
// DateTime.MaxValue we are sure selectedDate is\ // DateTime.MaxValue we are sure selectedDate is\
// not null // not null
selectedDate = DateTimeHelper.AddMonths(selectedDate.Value, 1)!.Value; selectedDate = DateTimeHelper.AddMonths(selectedDate.Value, 1)!.Value;
selectedDate = DateTimeHelper.AddDays(selectedDate.Value, -1)!.Value; selectedDate = DateTimeHelper.AddDays(selectedDate.Value, -1)!.Value;
}
else
{
selectedDate = DateTime.MaxValue;
}
ProcessSelection(shift, selectedDate, null);
} }
else
{
selectedDate = DateTime.MaxValue;
}
ProcessSelection(shift, selectedDate, null);
break; break;
} }
case CalendarMode.Year: case CalendarMode.Year:
@ -2026,7 +2020,6 @@ namespace Avalonia.Controls
focusDate = DisplayDate; focusDate = DisplayDate;
LastSelectedDate = DisplayDate; LastSelectedDate = DisplayDate;
} }
Debug.Assert(focusDate != null, "focusDate should not be null!");
FocusButton = FindDayButtonFromDay(focusDate); FocusButton = FindDayButtonFromDay(focusDate);
if (FocusButton != null) if (FocusButton != null)
@ -2091,17 +2084,17 @@ namespace Avalonia.Controls
static Calendar() static Calendar()
{ {
IsEnabledProperty.Changed.AddClassHandler<Calendar>((x,e) => x.OnIsEnabledChanged(e)); IsEnabledProperty.Changed.AddClassHandler<Calendar>((x, e) => x.OnIsEnabledChanged(e));
FirstDayOfWeekProperty.Changed.AddClassHandler<Calendar>((x,e) => x.OnFirstDayOfWeekChanged(e)); FirstDayOfWeekProperty.Changed.AddClassHandler<Calendar>((x, e) => x.OnFirstDayOfWeekChanged(e));
IsTodayHighlightedProperty.Changed.AddClassHandler<Calendar>((x,e) => x.OnIsTodayHighlightedChanged(e)); IsTodayHighlightedProperty.Changed.AddClassHandler<Calendar>((x, e) => x.OnIsTodayHighlightedChanged(e));
DisplayModeProperty.Changed.AddClassHandler<Calendar>((x,e) => x.OnDisplayModePropertyChanged(e)); DisplayModeProperty.Changed.AddClassHandler<Calendar>((x, e) => x.OnDisplayModePropertyChanged(e));
SelectionModeProperty.Changed.AddClassHandler<Calendar>((x,e) => x.OnSelectionModeChanged(e)); SelectionModeProperty.Changed.AddClassHandler<Calendar>((x, e) => x.OnSelectionModeChanged(e));
SelectedDateProperty.Changed.AddClassHandler<Calendar>((x,e) => x.OnSelectedDateChanged(e)); SelectedDateProperty.Changed.AddClassHandler<Calendar>((x, e) => x.OnSelectedDateChanged(e));
DisplayDateProperty.Changed.AddClassHandler<Calendar>((x,e) => x.OnDisplayDateChanged(e)); DisplayDateProperty.Changed.AddClassHandler<Calendar>((x, e) => x.OnDisplayDateChanged(e));
DisplayDateStartProperty.Changed.AddClassHandler<Calendar>((x,e) => x.OnDisplayDateStartChanged(e)); DisplayDateStartProperty.Changed.AddClassHandler<Calendar>((x, e) => x.OnDisplayDateStartChanged(e));
DisplayDateEndProperty.Changed.AddClassHandler<Calendar>((x,e) => x.OnDisplayDateEndChanged(e)); DisplayDateEndProperty.Changed.AddClassHandler<Calendar>((x, e) => x.OnDisplayDateEndChanged(e));
KeyDownEvent.AddClassHandler<Calendar>((x,e) => x.Calendar_KeyDown(e)); KeyDownEvent.AddClassHandler<Calendar>((x, e) => x.Calendar_KeyDown(e));
KeyUpEvent.AddClassHandler<Calendar>((x,e) => x.Calendar_KeyUp(e)); KeyUpEvent.AddClassHandler<Calendar>((x, e) => x.Calendar_KeyUp(e));
} }
/// <summary> /// <summary>

71
src/Avalonia.Controls/Calendar/CalendarItem.cs

@ -4,7 +4,6 @@
// All other rights reserved. // All other rights reserved.
using System; using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Globalization; using System.Globalization;
using Avalonia.Collections.Pooled; using Avalonia.Collections.Pooled;
@ -353,7 +352,6 @@ namespace Avalonia.Controls.Primitives
{ {
if (Owner != null) if (Owner != null)
{ {
Debug.Assert(Owner.DisplayDate != null, "The Owner Calendar's DisplayDate should not be null!");
_currentMonth = Owner.DisplayDateInternal; _currentMonth = Owner.DisplayDateInternal;
} }
else else
@ -361,17 +359,14 @@ namespace Avalonia.Controls.Primitives
_currentMonth = DateTime.Today; _currentMonth = DateTime.Today;
} }
if (_currentMonth != null) SetMonthModeHeaderButton();
{ SetMonthModePreviousButton(_currentMonth);
SetMonthModeHeaderButton(); SetMonthModeNextButton(_currentMonth);
SetMonthModePreviousButton(_currentMonth);
SetMonthModeNextButton(_currentMonth);
if (MonthView != null) if (MonthView != null)
{ {
SetDayTitles(); SetDayTitles();
SetCalendarDayButtons(_currentMonth); SetCalendarDayButtons(_currentMonth);
}
} }
} }
private void SetMonthModeHeaderButton() private void SetMonthModeHeaderButton()
@ -592,7 +587,6 @@ namespace Avalonia.Controls.Primitives
{ {
if (Owner != null) if (Owner != null)
{ {
Debug.Assert(Owner.SelectedMonth != null, "The Owner Calendar's SelectedMonth should not be null!");
_currentMonth = (DateTime)Owner.SelectedMonth; _currentMonth = (DateTime)Owner.SelectedMonth;
} }
else else
@ -600,16 +594,13 @@ namespace Avalonia.Controls.Primitives
_currentMonth = DateTime.Today; _currentMonth = DateTime.Today;
} }
if (_currentMonth != null) SetYearModeHeaderButton();
{ SetYearModePreviousButton();
SetYearModeHeaderButton(); SetYearModeNextButton();
SetYearModePreviousButton();
SetYearModeNextButton();
if (YearView != null) if (YearView != null)
{ {
SetMonthButtonsForYearMode(); SetMonthButtonsForYearMode();
}
} }
} }
private void SetYearModeHeaderButton() private void SetYearModeHeaderButton()
@ -660,7 +651,6 @@ namespace Avalonia.Controls.Primitives
childButton.IsCalendarButtonFocused = false; childButton.IsCalendarButtonFocused = false;
} }
Debug.Assert(Owner.DisplayDateInternal != null, "The Owner Calendar's DisplayDateInternal should not be null!");
childButton.IsSelected = (DateTimeHelper.CompareYearMonth(day, Owner.DisplayDateInternal) == 0); childButton.IsSelected = (DateTimeHelper.CompareYearMonth(day, Owner.DisplayDateInternal) == 0);
if (DateTimeHelper.CompareYearMonth(day, Owner.DisplayDateRangeStart) < 0 || DateTimeHelper.CompareYearMonth(day, Owner.DisplayDateRangeEnd) > 0) if (DateTimeHelper.CompareYearMonth(day, Owner.DisplayDateRangeStart) < 0 || DateTimeHelper.CompareYearMonth(day, Owner.DisplayDateRangeEnd) > 0)
@ -685,7 +675,6 @@ namespace Avalonia.Controls.Primitives
if (Owner != null) if (Owner != null)
{ {
Debug.Assert(Owner.SelectedYear != null, "The owning Calendar's selected year should not be null!");
selectedYear = Owner.SelectedYear; selectedYear = Owner.SelectedYear;
_currentMonth = (DateTime)Owner.SelectedMonth; _currentMonth = (DateTime)Owner.SelectedMonth;
} }
@ -695,19 +684,16 @@ namespace Avalonia.Controls.Primitives
selectedYear = DateTime.Today; selectedYear = DateTime.Today;
} }
if (_currentMonth != null) int decade = DateTimeHelper.DecadeOfDate(selectedYear);
{ int decadeEnd = DateTimeHelper.EndOfDecade(selectedYear);
int decade = DateTimeHelper.DecadeOfDate(selectedYear);
int decadeEnd = DateTimeHelper.EndOfDecade(selectedYear);
SetDecadeModeHeaderButton(decade, decadeEnd); SetDecadeModeHeaderButton(decade, decadeEnd);
SetDecadeModePreviousButton(decade); SetDecadeModePreviousButton(decade);
SetDecadeModeNextButton(decadeEnd); SetDecadeModeNextButton(decadeEnd);
if (YearView != null) if (YearView != null)
{ {
SetYearButtons(decade, decadeEnd); SetYearButtons(decade, decadeEnd);
}
} }
} }
internal void UpdateYearViewSelection(CalendarButton calendarButton) internal void UpdateYearViewSelection(CalendarButton calendarButton)
@ -822,22 +808,15 @@ namespace Avalonia.Controls.Primitives
{ {
if (Owner.DisplayMode == CalendarMode.Month) if (Owner.DisplayMode == CalendarMode.Month)
{ {
if (Owner.DisplayDate != null) d = Owner.DisplayDateInternal;
{ Owner.SelectedMonth = new DateTime(d.Year, d.Month, 1);
d = Owner.DisplayDateInternal;
Owner.SelectedMonth = new DateTime(d.Year, d.Month, 1);
}
Owner.DisplayMode = CalendarMode.Year; Owner.DisplayMode = CalendarMode.Year;
} }
else else
{ {
Debug.Assert(Owner.DisplayMode == CalendarMode.Year, "The Owner Calendar's DisplayMode should be Year!"); Debug.Assert(Owner.DisplayMode == CalendarMode.Year, "The Owner Calendar's DisplayMode should be Year!");
d = Owner.SelectedMonth;
if (Owner.SelectedMonth != null) Owner.SelectedYear = new DateTime(d.Year, d.Month, 1);
{
d = Owner.SelectedMonth;
Owner.SelectedYear = new DateTime(d.Year, d.Month, 1);
}
Owner.DisplayMode = CalendarMode.Decade; Owner.DisplayMode = CalendarMode.Decade;
} }
} }

2
src/Avalonia.Controls/Calendar/SelectedDatesCollection.cs

@ -297,7 +297,7 @@ namespace Avalonia.Controls.Primitives
} }
else else
{ {
if (item != null && DateTime.Compare(this[index], item) != 0 && Calendar.IsValidDateSelection(_owner, item)) if (DateTime.Compare(this[index], item) != 0 && Calendar.IsValidDateSelection(_owner, item))
{ {
removedItems.Add(this[index]); removedItems.Add(this[index]);
base.SetItem(index, item); base.SetItem(index, item);

2
src/Avalonia.Controls/GridLength.cs

@ -180,7 +180,7 @@ namespace Avalonia.Controls
return "Auto"; return "Auto";
} }
string s = _value.ToString(); string s = _value.ToString(CultureInfo.InvariantCulture);
return IsStar ? s + "*" : s; return IsStar ? s + "*" : s;
} }

4
src/Avalonia.Controls/Platform/InternalPlatformThreadingInterface.cs

@ -43,7 +43,7 @@ namespace Avalonia.Controls.Platform
_priority = priority; _priority = priority;
_interval = interval; _interval = interval;
_tick = tick; _tick = tick;
_timer = new Timer(OnTimer, null, interval, TimeSpan.FromMilliseconds(-1)); _timer = new Timer(OnTimer, null, interval, Timeout.InfiniteTimeSpan);
_handle = GCHandle.Alloc(_timer); _handle = GCHandle.Alloc(_timer);
} }
@ -57,7 +57,7 @@ namespace Avalonia.Controls.Platform
if (_timer == null) if (_timer == null)
return; return;
_tick(); _tick();
_timer?.Change(_interval, TimeSpan.FromMilliseconds(-1)); _timer?.Change(_interval, Timeout.InfiniteTimeSpan);
}); });
} }

36
src/Avalonia.Controls/Shapes/Arc.cs

@ -1,8 +1,12 @@
using System; using System;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Utilities;
namespace Avalonia.Controls.Shapes namespace Avalonia.Controls.Shapes
{ {
/// <summary>
/// Represents a circular or elliptical arc (a segment of a curve).
/// </summary>
public class Arc : Shape public class Arc : Shape
{ {
/// <summary> /// <summary>
@ -19,8 +23,12 @@ namespace Avalonia.Controls.Shapes
static Arc() static Arc()
{ {
StrokeThicknessProperty.OverrideDefaultValue<Arc>(1); StrokeThicknessProperty.OverrideDefaultValue<Arc>(1.0d);
AffectsGeometry<Arc>(BoundsProperty, StrokeThicknessProperty, StartAngleProperty, SweepAngleProperty); AffectsGeometry<Arc>(
BoundsProperty,
StrokeThicknessProperty,
StartAngleProperty,
SweepAngleProperty);
} }
/// <summary> /// <summary>
@ -42,10 +50,11 @@ namespace Avalonia.Controls.Shapes
set => SetValue(SweepAngleProperty, value); set => SetValue(SweepAngleProperty, value);
} }
/// <inheritdoc/>
protected override Geometry CreateDefiningGeometry() protected override Geometry CreateDefiningGeometry()
{ {
var angle1 = DegreesToRad(StartAngle); var angle1 = MathUtilities.Deg2Rad(StartAngle);
var angle2 = angle1 + DegreesToRad(SweepAngle); var angle2 = angle1 + MathUtilities.Deg2Rad(SweepAngle);
var startAngle = Math.Min(angle1, angle2); var startAngle = Math.Min(angle1, angle2);
var sweepAngle = Math.Max(angle1, angle2); var sweepAngle = Math.Max(angle1, angle2);
@ -80,24 +89,25 @@ namespace Avalonia.Controls.Shapes
var arcGeometry = new StreamGeometry(); var arcGeometry = new StreamGeometry();
using (var ctx = arcGeometry.Open()) using (StreamGeometryContext context = arcGeometry.Open())
{ {
ctx.BeginFigure(startPoint, false); context.BeginFigure(startPoint, false);
ctx.ArcTo(endPoint, new Size(radiusX, radiusY), angleGap, angleGap >= Math.PI, context.ArcTo(
endPoint,
new Size(radiusX, radiusY),
rotationAngle: angleGap,
isLargeArc: angleGap >= Math.PI,
SweepDirection.Clockwise); SweepDirection.Clockwise);
ctx.EndFigure(false); context.EndFigure(false);
} }
return arcGeometry; return arcGeometry;
} }
} }
static double DegreesToRad(double inAngle) => private static double RadToNormRad(double inAngle) => ((inAngle % (Math.PI * 2)) + (Math.PI * 2)) % (Math.PI * 2);
inAngle * Math.PI / 180;
static double RadToNormRad(double inAngle) => ((inAngle % (Math.PI * 2)) + (Math.PI * 2)) % (Math.PI * 2); private static Point GetRingPoint(double radiusX, double radiusY, double centerX, double centerY, double angle) =>
static Point GetRingPoint(double radiusX, double radiusY, double centerX, double centerY, double angle) =>
new Point((radiusX * Math.Cos(angle)) + centerX, (radiusY * Math.Sin(angle)) + centerY); new Point((radiusX * Math.Cos(angle)) + centerX, (radiusY * Math.Sin(angle)) + centerY);
} }
} }

97
src/Avalonia.Controls/Shapes/Sector.cs

@ -0,0 +1,97 @@
using System;
using Avalonia.Media;
using Avalonia.Utilities;
namespace Avalonia.Controls.Shapes
{
/// <summary>
/// Represents a circular or elliptical sector (a pie-shaped closed region of a circle or ellipse).
/// </summary>
public class Sector : Shape
{
/// <summary>
/// Defines the <see cref="StartAngle"/> property.
/// </summary>
public static readonly StyledProperty<double> StartAngleProperty =
AvaloniaProperty.Register<Sector, double>(nameof(StartAngle), 0.0d);
/// <summary>
/// Defines the <see cref="SweepAngle"/> property.
/// </summary>
public static readonly StyledProperty<double> SweepAngleProperty =
AvaloniaProperty.Register<Sector, double>(nameof(SweepAngle), 0.0d);
/// <summary>
/// Gets or sets the angle at which the sector's arc starts, in degrees.
/// </summary>
public double StartAngle
{
get => GetValue(StartAngleProperty);
set => SetValue(StartAngleProperty, value);
}
/// <summary>
/// Gets or sets the angle, in degrees, added to the <see cref="StartAngle"/> defining where the sector's arc ends.
/// A positive value is clockwise, negative is counter-clockwise.
/// </summary>
public double SweepAngle
{
get => GetValue(SweepAngleProperty);
set => SetValue(SweepAngleProperty, value);
}
static Sector()
{
StrokeThicknessProperty.OverrideDefaultValue<Sector>(1.0d);
AffectsGeometry<Sector>(
BoundsProperty,
StrokeThicknessProperty,
StartAngleProperty,
SweepAngleProperty);
}
/// <inheritdoc/>
protected override Geometry? CreateDefiningGeometry()
{
Rect rect = new Rect(Bounds.Size);
Rect deflatedRect = rect.Deflate(StrokeThickness * 0.5d);
if (SweepAngle >= 360.0d || SweepAngle <= -360.0d)
{
return new EllipseGeometry(deflatedRect);
}
if (SweepAngle == 0.0d)
{
return new StreamGeometry();
}
(double startAngle, double endAngle) = MathUtilities.GetMinMaxFromDelta(
MathUtilities.Deg2Rad(StartAngle),
MathUtilities.Deg2Rad(SweepAngle));
Point centre = new Point(rect.Width * 0.5d, rect.Height * 0.5d);
double radiusX = deflatedRect.Width * 0.5d;
double radiusY = deflatedRect.Height * 0.5d;
Point startCurvePoint = MathUtilities.GetEllipsePoint(centre, radiusX, radiusY, startAngle);
Point endCurvePoint = MathUtilities.GetEllipsePoint(centre, radiusX, radiusY, endAngle);
Size size = new Size(radiusX, radiusY);
var streamGeometry = new StreamGeometry();
using (StreamGeometryContext context = streamGeometry.Open())
{
context.BeginFigure(startCurvePoint, false);
context.ArcTo(
endCurvePoint,
size,
rotationAngle: 0.0d,
isLargeArc: Math.Abs(SweepAngle) > 180.0d,
SweepDirection.Clockwise);
context.LineTo(centre);
context.EndFigure(true);
}
return streamGeometry;
}
}
}

48
src/Avalonia.Headless/HeadlessPlatformThreadingInterface.cs

@ -36,35 +36,35 @@ namespace Avalonia.Headless
public IDisposable StartTimer(DispatcherPriority priority, TimeSpan interval, Action tick) public IDisposable StartTimer(DispatcherPriority priority, TimeSpan interval, Action tick)
{ {
var cancelled = false; if (interval.TotalMilliseconds < 10)
var enqueued = false; interval = TimeSpan.FromMilliseconds(10);
var l = new object();
var timer = new Timer(_ => var stopped = false;
Timer timer = null;
timer = new Timer(_ =>
{ {
lock (l) if (stopped)
return;
Dispatcher.UIThread.Post(() =>
{ {
if (cancelled || enqueued) try
return;
enqueued = true;
Dispatcher.UIThread.Post(() =>
{ {
lock (l) tick();
{ }
enqueued = false; finally
if (cancelled) {
return; if (!stopped)
tick(); timer.Change(interval, Timeout.InfiniteTimeSpan);
} }
}, priority); });
} },
}, null, interval, interval); null, interval, Timeout.InfiniteTimeSpan);
return Disposable.Create(() => return Disposable.Create(() =>
{ {
lock (l) stopped = true;
{ timer.Dispose();
timer.Dispose();
cancelled = true;
}
}); });
} }

3
src/Avalonia.OpenGL/Controls/OpenGlControlBase.cs

@ -34,7 +34,7 @@ namespace Avalonia.OpenGL.Controls
_attachment.Present(); _attachment.Present();
} }
context.DrawImage(_bitmap, new Rect(_bitmap.Size), Bounds); context.DrawImage(_bitmap, new Rect(_bitmap.Size), new Rect(Bounds.Size));
base.Render(context); base.Render(context);
} }
@ -84,6 +84,7 @@ namespace Avalonia.OpenGL.Controls
using (_context.MakeCurrent()) using (_context.MakeCurrent())
{ {
var gl = _context.GlInterface; var gl = _context.GlInterface;
gl.ActiveTexture(GL_TEXTURE0);
gl.BindTexture(GL_TEXTURE_2D, 0); gl.BindTexture(GL_TEXTURE_2D, 0);
gl.BindFramebuffer(GL_FRAMEBUFFER, 0); gl.BindFramebuffer(GL_FRAMEBUFFER, 0);
gl.DeleteFramebuffer(_fb); gl.DeleteFramebuffer(_fb);

50
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDeferredResourceTransformer.cs

@ -1,4 +1,5 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using XamlX.Ast; using XamlX.Ast;
using XamlX.Emit; using XamlX.Emit;
@ -47,7 +48,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
return !node.Type.GetClrType().IsValueType; return !node.Type.GetClrType().IsValueType;
} }
class AdderSetter : IXamlPropertySetter, IXamlEmitablePropertySetter<IXamlILEmitter> class AdderSetter : IXamlILOptimizedEmitablePropertySetter, IEquatable<AdderSetter>
{ {
private readonly IXamlMethod _getter; private readonly IXamlMethod _getter;
private readonly IXamlMethod _adder; private readonly IXamlMethod _adder;
@ -58,16 +59,22 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
_adder = adder; _adder = adder;
TargetType = getter.DeclaringType; TargetType = getter.DeclaringType;
Parameters = adder.ParametersWithThis().Skip(1).ToList(); Parameters = adder.ParametersWithThis().Skip(1).ToList();
bool allowNull = Parameters.Last().AcceptsNull();
BinderParameters = new PropertySetterBinderParameters
{
AllowMultiple = true,
AllowXNull = allowNull,
AllowRuntimeNull = allowNull
};
} }
public IXamlType TargetType { get; } public IXamlType TargetType { get; }
public PropertySetterBinderParameters BinderParameters { get; } = new PropertySetterBinderParameters public PropertySetterBinderParameters BinderParameters { get; }
{
AllowMultiple = true
};
public IReadOnlyList<IXamlType> Parameters { get; } public IReadOnlyList<IXamlType> Parameters { get; }
public void Emit(IXamlILEmitter emitter) public void Emit(IXamlILEmitter emitter)
{ {
var locals = new Stack<XamlLocalsPool.PooledLocal>(); var locals = new Stack<XamlLocalsPool.PooledLocal>();
@ -80,11 +87,40 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
} }
emitter.EmitCall(_getter); emitter.EmitCall(_getter);
while (locals.Count > 0) while (locals.Count>0)
using (var loc = locals.Pop()) using (var loc = locals.Pop())
emitter.Ldloc(loc.Local); emitter.Ldloc(loc.Local);
emitter.EmitCall(_adder, true); emitter.EmitCall(_adder, true);
} }
public void EmitWithArguments(
XamlEmitContextWithLocals<IXamlILEmitter, XamlILNodeEmitResult> context,
IXamlILEmitter emitter,
IReadOnlyList<IXamlAstValueNode> arguments)
{
emitter.EmitCall(_getter);
for (var i = 0; i < arguments.Count; ++i)
context.Emit(arguments[i], emitter, Parameters[i]);
emitter.EmitCall(_adder, true);
}
public bool Equals(AdderSetter other)
{
if (ReferenceEquals(null, other))
return false;
if (ReferenceEquals(this, other))
return true;
return _getter.Equals(other._getter) && _adder.Equals(other._adder);
}
public override bool Equals(object obj)
=> Equals(obj as AdderSetter);
public override int GetHashCode()
=> (_getter.GetHashCode() * 397) ^ _adder.GetHashCode();
} }
} }
} }

26
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTransformer.cs

@ -75,17 +75,17 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
{ {
Getter = setterType.Methods.First(m => m.Name == "get_Value"); Getter = setterType.Methods.First(m => m.Name == "get_Value");
var method = setterType.Methods.First(m => m.Name == "set_Value"); var method = setterType.Methods.First(m => m.Name == "set_Value");
Setters.Add(new XamlIlDirectCallPropertySetter(method, types.IBinding)); Setters.Add(new XamlIlDirectCallPropertySetter(method, types.IBinding, false));
Setters.Add(new XamlIlDirectCallPropertySetter(method, types.UnsetValueType)); Setters.Add(new XamlIlDirectCallPropertySetter(method, types.UnsetValueType, false));
Setters.Add(new XamlIlDirectCallPropertySetter(method, targetType)); Setters.Add(new XamlIlDirectCallPropertySetter(method, targetType, targetType.AcceptsNull()));
} }
class XamlIlDirectCallPropertySetter : IXamlPropertySetter, IXamlEmitablePropertySetter<IXamlILEmitter> sealed class XamlIlDirectCallPropertySetter : IXamlPropertySetter, IXamlEmitablePropertySetter<IXamlILEmitter>
{ {
private readonly IXamlMethod _method; private readonly IXamlMethod _method;
private readonly IXamlType _type; private readonly IXamlType _type;
public IXamlType TargetType { get; } public IXamlType TargetType { get; }
public PropertySetterBinderParameters BinderParameters { get; } = new PropertySetterBinderParameters(); public PropertySetterBinderParameters BinderParameters { get; }
public IReadOnlyList<IXamlType> Parameters { get; } public IReadOnlyList<IXamlType> Parameters { get; }
public void Emit(IXamlILEmitter codegen) public void Emit(IXamlILEmitter codegen)
{ {
@ -94,13 +94,27 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
codegen.EmitCall(_method, true); codegen.EmitCall(_method, true);
} }
public XamlIlDirectCallPropertySetter(IXamlMethod method, IXamlType type) public XamlIlDirectCallPropertySetter(IXamlMethod method, IXamlType type, bool allowNull)
{ {
_method = method; _method = method;
_type = type; _type = type;
Parameters = new[] {type}; Parameters = new[] {type};
TargetType = method.ThisOrFirstParameter(); TargetType = method.ThisOrFirstParameter();
BinderParameters = new PropertySetterBinderParameters
{
AllowXNull = allowNull,
AllowRuntimeNull = allowNull
};
} }
private bool Equals(XamlIlDirectCallPropertySetter other)
=> Equals(_method, other._method) && Equals(_type, other._type);
public override bool Equals(object obj)
=> Equals(obj as XamlIlDirectCallPropertySetter);
public override int GetHashCode()
=> (_method.GetHashCode() * 397) ^ _type.GetHashCode();
} }
} }
} }

152
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlAvaloniaPropertyHelper.cs

@ -206,38 +206,64 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
Setters.Insert(0, new UnsetValueSetter(types, original.DeclaringType, field)); Setters.Insert(0, new UnsetValueSetter(types, original.DeclaringType, field));
} }
abstract class AvaloniaPropertyCustomSetter : IXamlPropertySetter, IXamlEmitablePropertySetter<IXamlILEmitter> abstract class AvaloniaPropertyCustomSetter : IXamlILOptimizedEmitablePropertySetter, IEquatable<AvaloniaPropertyCustomSetter>
{ {
protected AvaloniaXamlIlWellKnownTypes Types; protected readonly AvaloniaXamlIlWellKnownTypes Types;
protected IXamlField AvaloniaProperty; protected readonly IXamlField AvaloniaProperty;
public AvaloniaPropertyCustomSetter(AvaloniaXamlIlWellKnownTypes types, protected AvaloniaPropertyCustomSetter(
AvaloniaXamlIlWellKnownTypes types,
IXamlType declaringType, IXamlType declaringType,
IXamlField avaloniaProperty) IXamlField avaloniaProperty,
bool allowNull)
{ {
Types = types; Types = types;
AvaloniaProperty = avaloniaProperty; AvaloniaProperty = avaloniaProperty;
TargetType = declaringType; TargetType = declaringType;
BinderParameters = new PropertySetterBinderParameters
{
AllowXNull = allowNull,
AllowRuntimeNull = allowNull
};
} }
public IXamlType TargetType { get; } public IXamlType TargetType { get; }
public PropertySetterBinderParameters BinderParameters { get; } = new PropertySetterBinderParameters public PropertySetterBinderParameters BinderParameters { get; }
{
AllowXNull = false
};
public IReadOnlyList<IXamlType> Parameters { get; set; } public IReadOnlyList<IXamlType> Parameters { get; set; }
public abstract void Emit(IXamlILEmitter codegen);
public abstract void Emit(IXamlILEmitter emitter);
public abstract void EmitWithArguments(
XamlEmitContextWithLocals<IXamlILEmitter, XamlILNodeEmitResult> context,
IXamlILEmitter emitter,
IReadOnlyList<IXamlAstValueNode> arguments);
public bool Equals(AvaloniaPropertyCustomSetter other)
{
if (ReferenceEquals(null, other))
return false;
if (ReferenceEquals(this, other))
return true;
return GetType() == other.GetType() && AvaloniaProperty.Equals(other.AvaloniaProperty);
}
public override bool Equals(object obj)
=> Equals(obj as AvaloniaPropertyCustomSetter);
public override int GetHashCode()
=> AvaloniaProperty.GetHashCode();
} }
class BindingSetter : AvaloniaPropertyCustomSetter class BindingSetter : AvaloniaPropertyCustomSetter
{ {
public BindingSetter(AvaloniaXamlIlWellKnownTypes types, public BindingSetter(AvaloniaXamlIlWellKnownTypes types,
IXamlType declaringType, IXamlType declaringType,
IXamlField avaloniaProperty) : base(types, declaringType, avaloniaProperty) IXamlField avaloniaProperty) : base(types, declaringType, avaloniaProperty, false)
{ {
Parameters = new[] {types.IBinding}; Parameters = new[] { types.IBinding };
} }
public override void Emit(IXamlILEmitter emitter) public override void Emit(IXamlILEmitter emitter)
@ -246,10 +272,25 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
emitter emitter
.Stloc(bloc.Local) .Stloc(bloc.Local)
.Ldsfld(AvaloniaProperty) .Ldsfld(AvaloniaProperty)
.Ldloc(bloc.Local) .Ldloc(bloc.Local);
// TODO: provide anchor? EmitAnchorAndBind(emitter);
.Ldnull(); }
emitter.EmitCall(Types.AvaloniaObjectBindMethod, true);
public override void EmitWithArguments(
XamlEmitContextWithLocals<IXamlILEmitter, XamlILNodeEmitResult> context,
IXamlILEmitter emitter,
IReadOnlyList<IXamlAstValueNode> arguments)
{
emitter.Ldsfld(AvaloniaProperty);
context.Emit(arguments[0], emitter, Parameters[0]);
EmitAnchorAndBind(emitter);
}
private void EmitAnchorAndBind(IXamlILEmitter emitter)
{
emitter
.Ldnull() // TODO: provide anchor?
.EmitCall(Types.AvaloniaObjectBindMethod, true);
} }
} }
@ -257,7 +298,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
{ {
public BindingWithPrioritySetter(AvaloniaXamlIlWellKnownTypes types, public BindingWithPrioritySetter(AvaloniaXamlIlWellKnownTypes types,
IXamlType declaringType, IXamlType declaringType,
IXamlField avaloniaProperty) : base(types, declaringType, avaloniaProperty) IXamlField avaloniaProperty) : base(types, declaringType, avaloniaProperty, false)
{ {
Parameters = new[] { types.BindingPriority, types.IBinding }; Parameters = new[] { types.BindingPriority, types.IBinding };
} }
@ -265,15 +306,29 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
public override void Emit(IXamlILEmitter emitter) public override void Emit(IXamlILEmitter emitter)
{ {
using (var bloc = emitter.LocalsPool.GetLocal(Types.IBinding)) using (var bloc = emitter.LocalsPool.GetLocal(Types.IBinding))
using (var priorityLocal = emitter.LocalsPool.GetLocal(Types.Int))
emitter emitter
.Stloc(bloc.Local) .Stloc(bloc.Local)
.Stloc(priorityLocal.Local) .Pop() // ignore priority
.Ldsfld(AvaloniaProperty) .Ldsfld(AvaloniaProperty)
.Ldloc(bloc.Local) .Ldloc(bloc.Local);
// TODO: provide anchor? EmitAnchorAndBind(emitter);
.Ldnull(); }
emitter.EmitCall(Types.AvaloniaObjectBindMethod, true);
public override void EmitWithArguments(
XamlEmitContextWithLocals<IXamlILEmitter, XamlILNodeEmitResult> context,
IXamlILEmitter emitter,
IReadOnlyList<IXamlAstValueNode> arguments)
{
emitter.Ldsfld(AvaloniaProperty);
context.Emit(arguments[1], emitter, Parameters[1]);
EmitAnchorAndBind(emitter);
}
private void EmitAnchorAndBind(IXamlILEmitter emitter)
{
emitter
.Ldnull() // TODO: provide anchor?
.EmitCall(Types.AvaloniaObjectBindMethod, true);
} }
} }
@ -281,7 +336,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
{ {
public SetValueWithPrioritySetter(AvaloniaXamlIlWellKnownTypes types, IXamlType declaringType, IXamlField avaloniaProperty, public SetValueWithPrioritySetter(AvaloniaXamlIlWellKnownTypes types, IXamlType declaringType, IXamlField avaloniaProperty,
IXamlType propertyType) IXamlType propertyType)
: base(types, declaringType, avaloniaProperty) : base(types, declaringType, avaloniaProperty, propertyType.AcceptsNull())
{ {
Parameters = new[] { types.BindingPriority, propertyType }; Parameters = new[] { types.BindingPriority, propertyType };
} }
@ -295,9 +350,6 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
- value - value
*/ */
var method = Types.AvaloniaObjectSetStyledPropertyValue
.MakeGenericMethod(new[] { Parameters[1] });
using (var valueLocal = emitter.LocalsPool.GetLocal(Parameters[1])) using (var valueLocal = emitter.LocalsPool.GetLocal(Parameters[1]))
using (var priorityLocal = emitter.LocalsPool.GetLocal(Types.Int)) using (var priorityLocal = emitter.LocalsPool.GetLocal(Types.Int))
emitter emitter
@ -305,25 +357,57 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
.Stloc(priorityLocal.Local) .Stloc(priorityLocal.Local)
.Ldsfld(AvaloniaProperty) .Ldsfld(AvaloniaProperty)
.Ldloc(valueLocal.Local) .Ldloc(valueLocal.Local)
.Ldloc(priorityLocal.Local) .Ldloc(priorityLocal.Local);
.EmitCall(method, true);
EmitSetStyledPropertyValue(emitter);
}
public override void EmitWithArguments(
XamlEmitContextWithLocals<IXamlILEmitter, XamlILNodeEmitResult> context,
IXamlILEmitter emitter,
IReadOnlyList<IXamlAstValueNode> arguments)
{
emitter.Ldsfld(AvaloniaProperty);
context.Emit(arguments[1], emitter, Parameters[1]);
context.Emit(arguments[0], emitter, Parameters[0]);
EmitSetStyledPropertyValue(emitter);
}
private void EmitSetStyledPropertyValue(IXamlILEmitter emitter)
{
var method = Types.AvaloniaObjectSetStyledPropertyValue.MakeGenericMethod(new[] { Parameters[1] });
emitter.EmitCall(method, true);
} }
} }
class UnsetValueSetter : AvaloniaPropertyCustomSetter class UnsetValueSetter : AvaloniaPropertyCustomSetter
{ {
public UnsetValueSetter(AvaloniaXamlIlWellKnownTypes types, IXamlType declaringType, IXamlField avaloniaProperty) public UnsetValueSetter(AvaloniaXamlIlWellKnownTypes types, IXamlType declaringType, IXamlField avaloniaProperty)
: base(types, declaringType, avaloniaProperty) : base(types, declaringType, avaloniaProperty, false)
{ {
Parameters = new[] {types.UnsetValueType}; Parameters = new[] { types.UnsetValueType };
} }
public override void Emit(IXamlILEmitter codegen) public override void Emit(IXamlILEmitter codegen)
{ {
codegen.Pop();
EmitSetValue(codegen);
}
public override void EmitWithArguments(
XamlEmitContextWithLocals<IXamlILEmitter, XamlILNodeEmitResult> context,
IXamlILEmitter emitter,
IReadOnlyList<IXamlAstValueNode> arguments)
{
EmitSetValue(emitter);
}
private void EmitSetValue(IXamlILEmitter emitter)
{
// Ignore the instance and load one from the static field to avoid extra local variable
var unsetValue = Types.AvaloniaProperty.Fields.First(f => f.Name == "UnsetValue"); var unsetValue = Types.AvaloniaProperty.Fields.First(f => f.Name == "UnsetValue");
codegen
// Ignore the instance and load one from the static field to avoid extra local variable emitter
.Pop()
.Ldsfld(AvaloniaProperty) .Ldsfld(AvaloniaProperty)
.Ldsfld(unsetValue) .Ldsfld(unsetValue)
.Ldc_I4(0) .Ldc_I4(0)

2
src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github

@ -1 +1 @@
Subproject commit a4e6be2d1407abec4f35fcb208848830ce513ead Subproject commit c1c0594ec2c35b08988183b1a5b3e34dfa19179d

14
src/Skia/Avalonia.Skia/Gpu/OpenGl/OpenGlBitmapImpl.cs

@ -41,7 +41,7 @@ namespace Avalonia.Skia
new GRGlTextureInfo( new GRGlTextureInfo(
GlConsts.GL_TEXTURE_2D, (uint)_surface.GetTextureId(), GlConsts.GL_TEXTURE_2D, (uint)_surface.GetTextureId(),
(uint)_surface.InternalFormat))) (uint)_surface.InternalFormat)))
using (var surface = SKSurface.Create(context.GrContext, backendTexture, GRSurfaceOrigin.TopLeft, using (var surface = SKSurface.Create(context.GrContext, backendTexture, GRSurfaceOrigin.BottomLeft,
SKColorType.Rgba8888)) SKColorType.Rgba8888))
{ {
// Again, silently ignore, if something went wrong it's not our fault // Again, silently ignore, if something went wrong it's not our fault
@ -118,7 +118,7 @@ namespace Avalonia.Skia
{ {
var gl = _context.GlInterface; var gl = _context.GlInterface;
var textures = new int[2]; Span<int> textures = stackalloc int[2];
fixed (int* ptex = textures) fixed (int* ptex = textures)
gl.GenTextures(2, ptex); gl.GenTextures(2, ptex);
_texture = textures[0]; _texture = textures[0];
@ -139,7 +139,6 @@ namespace Avalonia.Skia
gl.FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _texture, 0); gl.FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _texture, 0);
gl.BindTexture(GL_TEXTURE_2D, oldTexture); gl.BindTexture(GL_TEXTURE_2D, oldTexture);
} }
} }
} }
@ -161,15 +160,15 @@ namespace Avalonia.Skia
gl.GetIntegerv(GL_ACTIVE_TEXTURE, out var oldActive); gl.GetIntegerv(GL_ACTIVE_TEXTURE, out var oldActive);
gl.BindFramebuffer(GL_FRAMEBUFFER, _fbo); gl.BindFramebuffer(GL_FRAMEBUFFER, _fbo);
gl.BindTexture(GL_TEXTURE_2D, _frontBuffer);
gl.ActiveTexture(GL_TEXTURE0); gl.ActiveTexture(GL_TEXTURE0);
gl.BindTexture(GL_TEXTURE_2D, _frontBuffer);
gl.CopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, _bitmap.PixelSize.Width, gl.CopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, _bitmap.PixelSize.Width,
_bitmap.PixelSize.Height); _bitmap.PixelSize.Height);
gl.BindFramebuffer(GL_FRAMEBUFFER, oldFbo); gl.BindFramebuffer(GL_FRAMEBUFFER, oldFbo);
gl.BindTexture(GL_TEXTURE_2D, oldTexture);
gl.ActiveTexture(oldActive); gl.ActiveTexture(oldActive);
gl.BindTexture(GL_TEXTURE_2D, oldTexture);
gl.Finish(); gl.Finish();
} }
@ -192,9 +191,8 @@ namespace Avalonia.Skia
if(_disposed) if(_disposed)
return; return;
_disposed = true; _disposed = true;
var tex = new[] { _texture, _frontBuffer }; var ptex = stackalloc[] { _texture, _frontBuffer };
fixed (int* ptex = tex) gl.DeleteTextures(2, ptex);
gl.DeleteTextures(2, ptex);
} }
} }

3
src/Windows/Avalonia.Win32/WindowImpl.cs

@ -285,11 +285,12 @@ namespace Avalonia.Win32
set set
{ {
if (IsWindowVisible(_hwnd)) if (IsWindowVisible(_hwnd) && _lastWindowState != value)
{ {
ShowWindow(value, value != WindowState.Minimized); // If the window is minimized, it shouldn't be activated ShowWindow(value, value != WindowState.Minimized); // If the window is minimized, it shouldn't be activated
} }
_lastWindowState = value;
_showWindowState = value; _showWindowState = value;
} }
} }

20
tests/Avalonia.Controls.UnitTests/GridLengthTests.cs

@ -1,6 +1,8 @@
using System; using System;
using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using Xunit; using Xunit;
namespace Avalonia.Controls.UnitTests namespace Avalonia.Controls.UnitTests
@ -100,5 +102,23 @@ namespace Avalonia.Controls.UnitTests
}, },
result); result);
} }
[Theory]
[InlineData(1.2d, GridUnitType.Pixel, "1.2")]
[InlineData(1.2d, GridUnitType.Star, "1.2*")]
[InlineData(1.2d, GridUnitType.Auto, "Auto")]
public async void ToString_AllCulture_Should_Pass(double d, GridUnitType type, string result)
{
List<CultureInfo> cultureInfos = CultureInfo.GetCultures(CultureTypes.AllCultures).ToList();
GridLength length = new GridLength(d, type);
foreach(var culture in cultureInfos)
{
await Task.Run(() =>
{
CultureInfo.CurrentCulture = culture;
Assert.Equal(result, length.ToString());
});
}
}
} }
} }

42
tests/Avalonia.IntegrationTests.Appium/WindowTests.cs

@ -1,7 +1,9 @@
using System; using System;
using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
using Avalonia.Controls; using Avalonia.Controls;
using OpenQA.Selenium;
using OpenQA.Selenium.Appium; using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Interactions; using OpenQA.Selenium.Interactions;
using Xunit; using Xunit;
@ -55,6 +57,43 @@ namespace Avalonia.IntegrationTests.Appium
} }
} }
} }
[PlatformFact(TestPlatforms.Windows)]
public void OnWindows_Docked_Windows_Retain_Size_Position_When_Restored()
{
using (OpenWindow(new Size(400, 400), ShowWindowMode.NonOwned, WindowStartupLocation.Manual))
{
var windowState = _session.FindElementByAccessibilityId("WindowState");
Assert.Equal("Normal", windowState.GetComboBoxValue());
var window = _session.FindElements(By.XPath("//Window")).First();
new Actions(_session)
.KeyDown(Keys.Meta)
.SendKeys(Keys.Left)
.KeyUp(Keys.Meta)
.Perform();
var original = GetWindowInfo();
windowState.Click();
_session.FindElementByName("Minimized").SendClick();
new Actions(_session)
.KeyDown(Keys.Alt)
.SendKeys(Keys.Tab)
.KeyUp(Keys.Alt)
.Perform();
var current = GetWindowInfo();
Assert.Equal(original.Position, current.Position);
Assert.Equal(original.FrameSize, current.FrameSize);
}
}
[Theory] [Theory]
@ -92,7 +131,8 @@ namespace Avalonia.IntegrationTests.Appium
Assert.True(clientSize.Width >= current.ScreenRect.Width); Assert.True(clientSize.Width >= current.ScreenRect.Width);
Assert.True(clientSize.Height >= current.ScreenRect.Height); Assert.True(clientSize.Height >= current.ScreenRect.Height);
windowState.Click(); windowState.SendClick();
_session.FindElementByName("Normal").SendClick(); _session.FindElementByName("Normal").SendClick();
current = GetWindowInfo(); current = GetWindowInfo();

29
tests/Avalonia.IntegrationTests.Appium/WindowTests_MacOS.cs

@ -211,6 +211,35 @@ namespace Avalonia.IntegrationTests.Appium
} }
} }
[PlatformFact(TestPlatforms.MacOS)]
public void Hidden_Child_Window_Is_Not_Reshown_When_Parent_Clicked()
{
var mainWindow = _session.FindElementByAccessibilityId("MainWindow");
// We don't use dispose to close the window here, because it seems that hiding and re-showing a window
// causes Appium to think it's a different window.
OpenWindow(null, ShowWindowMode.Owned, WindowStartupLocation.Manual);
var secondaryWindow = FindWindow(_session, "SecondaryWindow");
var hideButton = secondaryWindow.FindElementByAccessibilityId("HideButton");
hideButton.Click();
var windows = _session.FindElementsByXPath("XCUIElementTypeWindow");
Assert.Single(windows);
mainWindow.Click();
windows = _session.FindElementsByXPath("XCUIElementTypeWindow");
Assert.Single(windows);
_session.FindElementByAccessibilityId("RestoreAll").Click();
// Close the window manually.
secondaryWindow = FindWindow(_session, "SecondaryWindow");
secondaryWindow.GetChromeButtons().close.Click();
}
private IDisposable OpenWindow(PixelSize? size, ShowWindowMode mode, WindowStartupLocation location) private IDisposable OpenWindow(PixelSize? size, ShowWindowMode mode, WindowStartupLocation location)
{ {
var sizeTextBox = _session.FindElementByAccessibilityId("ShowWindowSize"); var sizeTextBox = _session.FindElementByAccessibilityId("ShowWindowSize");

Loading…
Cancel
Save