Browse Source

Merge branch 'master' into cleanup-platformsupport

pull/8183/head
Max Katz 4 years ago
committed by GitHub
parent
commit
93b55ef54c
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 17
      native/Avalonia.Native/src/OSX/AvnWindow.mm
  2. 6
      native/Avalonia.Native/src/OSX/PopupImpl.mm
  3. 3
      native/Avalonia.Native/src/OSX/WindowBaseImpl.h
  4. 52
      native/Avalonia.Native/src/OSX/WindowBaseImpl.mm
  5. 3
      native/Avalonia.Native/src/OSX/WindowImpl.h
  6. 16
      native/Avalonia.Native/src/OSX/WindowImpl.mm
  7. 11
      native/Avalonia.Native/src/OSX/app.mm
  8. 10
      samples/ControlCatalog/Converter/MathSubtractConverter.cs
  9. 10
      samples/ControlCatalog/DecoratedWindow.xaml.cs
  10. 16
      samples/ControlCatalog/MainView.xaml.cs
  11. 8
      samples/ControlCatalog/MainWindow.xaml.cs
  12. 4
      samples/ControlCatalog/Models/Countries.cs
  13. 4
      samples/ControlCatalog/Models/GDPValueConverter.cs
  14. 16
      samples/ControlCatalog/Models/Person.cs
  15. 33
      samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml.cs
  16. 2
      samples/ControlCatalog/Pages/ButtonSpinnerPage.xaml.cs
  17. 4
      samples/ControlCatalog/Pages/ButtonsPage.xaml.cs
  18. 12
      samples/ControlCatalog/Pages/CalendarDatePickerPage.xaml.cs
  19. 4
      samples/ControlCatalog/Pages/CalendarPage.xaml.cs
  20. 11
      samples/ControlCatalog/Pages/CarouselPage.xaml.cs
  21. 22
      samples/ControlCatalog/Pages/ContextFlyoutPage.xaml.cs
  22. 6
      samples/ControlCatalog/Pages/ContextMenuPage.xaml.cs
  23. 8
      samples/ControlCatalog/Pages/DataGridPage.xaml.cs
  24. 4
      samples/ControlCatalog/Pages/DateTimePickerPage.xaml.cs
  25. 32
      samples/ControlCatalog/Pages/DialogsPage.xaml.cs
  26. 6
      samples/ControlCatalog/Pages/DragAndDropPage.xaml.cs
  27. 8
      samples/ControlCatalog/Pages/FlyoutsPage.axaml.cs
  28. 13
      samples/ControlCatalog/Pages/ImagePage.xaml.cs
  29. 26
      samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml.cs
  30. 2
      samples/ControlCatalog/Pages/LabelsPage.axaml.cs
  31. 2
      samples/ControlCatalog/Pages/MenuPage.xaml.cs
  32. 2
      samples/ControlCatalog/Pages/NativeEmbedPage.xaml.cs
  33. 2
      samples/ControlCatalog/Pages/NumericUpDownPage.xaml
  34. 10
      samples/ControlCatalog/Pages/NumericUpDownPage.xaml.cs
  35. 2
      samples/ControlCatalog/Pages/ScreenPage.cs
  36. 12
      samples/ControlCatalog/Pages/TabControlPage.xaml.cs
  37. 2
      samples/ControlCatalog/Pages/TabStripPage.xaml.cs
  38. 2
      samples/ControlCatalog/ViewModels/ApplicationViewModel.cs
  39. 2
      samples/ControlCatalog/ViewModels/ContextPageViewModel.cs
  40. 2
      samples/ControlCatalog/ViewModels/CursorPageViewModel.cs
  41. 19
      samples/ControlCatalog/ViewModels/ItemsRepeaterPageViewModel.cs
  42. 13
      samples/ControlCatalog/ViewModels/MainWindowViewModel.cs
  43. 8
      samples/ControlCatalog/ViewModels/MenuItemViewModel.cs
  44. 2
      samples/ControlCatalog/ViewModels/MenuPageViewModel.cs
  45. 4
      samples/ControlCatalog/ViewModels/NotificationViewModel.cs
  46. 16
      samples/ControlCatalog/ViewModels/TransitioningContentControlPageViewModel.cs
  47. 4
      samples/ControlCatalog/ViewModels/TreeViewPageViewModel.cs
  48. 2
      src/Android/Avalonia.Android/AvaloniaView.cs
  49. 2
      src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidKeyboardEventsHelper.cs
  50. 76
      src/Avalonia.Base/Matrix.cs
  51. 3
      src/Avalonia.Base/Media/GeometryDrawing.cs
  52. 6
      src/Avalonia.Base/Reactive/TypedBindingAdapter.cs
  53. 3
      src/Avalonia.Controls/Avalonia.Controls.csproj
  54. 7
      src/Avalonia.Controls/Button.cs
  55. 2
      src/Avalonia.Controls/Calendar/CalendarItem.cs
  56. 6
      src/Avalonia.Controls/Carousel.cs
  57. 111
      src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs
  58. 6
      src/Avalonia.Controls/NumericUpDown/NumericUpDownValueChangedEventArgs.cs
  59. 4
      src/Avalonia.Controls/Presenters/CarouselPresenter.cs
  60. 2
      src/Avalonia.Themes.Default/SimpleTheme.cs
  61. 14
      src/Avalonia.Themes.Fluent/FluentTheme.cs
  62. 1
      src/Avalonia.X11/X11Atoms.cs
  63. 10
      src/Avalonia.X11/X11Info.cs
  64. 46
      src/Avalonia.X11/X11Window.cs
  65. 17
      src/Avalonia.X11/XLib.cs
  66. 4
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/PropertyInfoAccessorFactory.cs
  67. 4
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/ResourceInclude.cs
  68. 2
      src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs
  69. 20
      src/Web/Avalonia.Web.Blazor/AvaloniaView.razor
  70. 38
      src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs
  71. 2
      src/Windows/Avalonia.Win32/Automation/RootAutomationNode.cs
  72. 4
      src/Windows/Avalonia.Win32/Avalonia.Win32.csproj
  73. 1
      src/Windows/Avalonia.Win32/Interop/Automation/ISelectionItemProvider.cs
  74. 2
      src/Windows/Avalonia.Win32/TrayIconImpl.cs
  75. 2
      src/Windows/Avalonia.Win32/WindowImpl.CustomCaptionProc.cs
  76. 10
      tests/Avalonia.Base.UnitTests/Media/MatrixTests.cs
  77. 3
      tests/Avalonia.Benchmarks/TestBindingObservable.cs
  78. 74
      tests/Avalonia.Controls.UnitTests/ButtonTests.cs
  79. 52
      tests/Avalonia.RenderTests/Media/GeometryDrawingTests.cs
  80. 4
      tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs
  81. 4
      tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs

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

@ -33,6 +33,7 @@
bool _isEnabled;
bool _canBecomeKeyWindow;
bool _isExtended;
bool _isTransitioningToFullScreen;
AvnMenu* _menu;
}
@ -175,6 +176,7 @@
[self setBackgroundColor: [NSColor clearColor]];
_isExtended = false;
_isTransitioningToFullScreen = false;
if(self.isDialog)
{
@ -282,6 +284,14 @@
- (void)windowDidBecomeKey:(NSNotification *_Nonnull)notification
{
_parent->BringToFront();
dispatch_async(dispatch_get_main_queue(), ^{
@try {
[self invalidateShadow];
}
@finally{
}
});
}
- (void)windowDidMiniaturize:(NSNotification *_Nonnull)notification
@ -349,6 +359,7 @@
- (void)windowWillEnterFullScreen:(NSNotification *_Nonnull)notification
{
_isTransitioningToFullScreen = true;
auto parent = dynamic_cast<IWindowStateChanged*>(_parent.operator->());
if(parent != nullptr)
@ -359,6 +370,7 @@
- (void)windowDidEnterFullScreen:(NSNotification *_Nonnull)notification
{
_isTransitioningToFullScreen = false;
auto parent = dynamic_cast<IWindowStateChanged*>(_parent.operator->());
if(parent != nullptr)
@ -441,7 +453,10 @@
_parent->BaseEvents->RawMouseEvent(NonClientLeftButtonDown, static_cast<uint32>([event timestamp] * 1000), AvnInputModifiersNone, point, delta);
}
_parent->BringToFront();
if(!_isTransitioningToFullScreen)
{
_parent->BringToFront();
}
}
break;

6
native/Avalonia.Native/src/OSX/PopupImpl.mm

@ -26,17 +26,13 @@ private:
PopupImpl(IAvnWindowEvents* events, IAvnGlContext* gl) : WindowBaseImpl(events, gl)
{
WindowEvents = events;
[Window setLevel:NSPopUpMenuWindowLevel];
}
protected:
virtual NSWindowStyleMask GetStyle() override
{
return NSWindowStyleMaskBorderless;
}
virtual void OnInitialiseNSWindow () override
{
[Window setLevel:NSPopUpMenuWindowLevel];
}
public:
virtual bool ShouldTakeFocusOnShow() override

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

@ -106,13 +106,10 @@ protected:
virtual NSWindowStyleMask GetStyle();
void UpdateStyle();
virtual void OnInitialiseNSWindow ();
private:
void CreateNSWindow (bool isDialog);
void CleanNSWindow ();
void InitialiseNSWindow ();
NSCursor *cursor;
ComPtr<IAvnGlContext> _glContext;

52
native/Avalonia.Native/src/OSX/WindowBaseImpl.mm

@ -39,7 +39,16 @@ WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl,
lastMenu = nullptr;
CreateNSWindow(usePanel);
InitialiseNSWindow();
[Window setContentView:StandardContainer];
[Window setStyleMask:NSWindowStyleMaskBorderless];
[Window setBackingType:NSBackingStoreBuffered];
[Window setContentMinSize:lastMinSize];
[Window setContentMaxSize:lastMaxSize];
[Window setOpaque:false];
[Window setHasShadow:true];
}
HRESULT WindowBaseImpl::ObtainNSViewHandle(void **ret) {
@ -90,8 +99,8 @@ HRESULT WindowBaseImpl::Show(bool activate, bool isDialog) {
START_COM_CALL;
@autoreleasepool {
InitialiseNSWindow();
[Window setContentSize:lastSize];
if(hasPosition)
{
SetPosition(lastPositionSet);
@ -101,6 +110,8 @@ HRESULT WindowBaseImpl::Show(bool activate, bool isDialog) {
}
UpdateStyle();
[Window invalidateShadow];
if (ShouldTakeFocusOnShow() && activate) {
[Window orderFront:Window];
@ -292,8 +303,7 @@ HRESULT WindowBaseImpl::Resize(double x, double y, AvnPlatformResizeReason reaso
if (!_shown) {
BaseEvents->Resized(AvnSize{x, y}, reason);
}
if(Window != nullptr) {
else if(Window != nullptr) {
[Window setContentSize:lastSize];
[Window invalidateShadow];
}
@ -569,38 +579,6 @@ void WindowBaseImpl::CreateNSWindow(bool isDialog) {
}
}
void WindowBaseImpl::OnInitialiseNSWindow()
{
}
void WindowBaseImpl::InitialiseNSWindow() {
if(Window != nullptr) {
[Window setContentView:StandardContainer];
[Window setStyleMask:NSWindowStyleMaskBorderless];
[Window setBackingType:NSBackingStoreBuffered];
[Window setContentSize:lastSize];
[Window setContentMinSize:lastMinSize];
[Window setContentMaxSize:lastMaxSize];
[Window setOpaque:false];
[Window setHasShadow:true];
[Window invalidateShadow];
if (lastMenu != nullptr) {
[GetWindowProtocol() applyMenu:lastMenu];
if ([Window isKeyWindow]) {
[GetWindowProtocol() showWindowMenuWithAppMenu];
}
}
OnInitialiseNSWindow();
}
}
id <AvnWindowProtocol> WindowBaseImpl::GetWindowProtocol() {
if(Window == nullptr)
{

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

@ -93,8 +93,6 @@ BEGIN_INTERFACE_MAP()
virtual bool IsDialog() override;
virtual void OnInitialiseNSWindow() override;
virtual void BringToFront () override;
bool CanBecomeKeyWindow ();
@ -103,6 +101,7 @@ protected:
virtual NSWindowStyleMask GetStyle() override;
private:
void OnInitialiseNSWindow();
NSString *_lastTitle;
};

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

@ -24,6 +24,8 @@ WindowImpl::WindowImpl(IAvnWindowEvents *events, IAvnGlContext *gl) : WindowBase
_lastTitle = @"";
_parent = nullptr;
WindowEvents = events;
OnInitialiseNSWindow();
}
void WindowImpl::HideOrShowTrafficLights() {
@ -32,15 +34,16 @@ void WindowImpl::HideOrShowTrafficLights() {
}
bool wantsChrome = (_extendClientHints & AvnSystemChrome) || (_extendClientHints & AvnPreferSystemChrome);
bool hasTrafficLights = _isClientAreaExtended ? !wantsChrome : _decorations != SystemDecorationsFull;
bool hasTrafficLights = _isClientAreaExtended ? wantsChrome : _decorations == SystemDecorationsFull;
[[Window standardWindowButton:NSWindowCloseButton] setHidden:hasTrafficLights];
[[Window standardWindowButton:NSWindowMiniaturizeButton] setHidden:hasTrafficLights];
[[Window standardWindowButton:NSWindowZoomButton] setHidden:hasTrafficLights];
[[Window standardWindowButton:NSWindowCloseButton] setHidden:!hasTrafficLights];
[[Window standardWindowButton:NSWindowMiniaturizeButton] setHidden:!hasTrafficLights];
[[Window standardWindowButton:NSWindowZoomButton] setHidden:!hasTrafficLights];
}
void WindowImpl::OnInitialiseNSWindow(){
[GetWindowProtocol() setCanBecomeKeyWindow:true];
[Window disableCursorRects];
[Window setTabbingMode:NSWindowTabbingModeDisallowed];
[Window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
@ -52,11 +55,6 @@ void WindowImpl::OnInitialiseNSWindow(){
[GetWindowProtocol() setIsExtended:true];
SetExtendClientArea(true);
}
if(_parent != nullptr)
{
SetParent(_parent);
}
}
HRESULT WindowImpl::Show(bool activate, bool isDialog) {

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

@ -82,6 +82,17 @@ ComPtr<IAvnApplicationEvents> _events;
_isHandlingSendEvent = oldHandling;
}
}
// This is needed for certain embedded controls DO NOT REMOVE..
- (BOOL) isHandlingSendEvent
{
return _isHandlingSendEvent;
}
- (void)setHandlingSendEvent:(BOOL)handlingSendEvent
{
_isHandlingSendEvent = handlingSendEvent;
}
@end
extern void InitializeAvnApp(IAvnApplicationEvents* events)

10
samples/ControlCatalog/Converter/MathSubtractConverter.cs

@ -6,12 +6,16 @@ namespace ControlCatalog.Converter;
public class MathSubtractConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
return (double)value - (double)parameter;
if (value is double dv && parameter is double dp)
{
return dv - dp;
}
return double.NaN;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
throw new NotSupportedException();
}

10
samples/ControlCatalog/DecoratedWindow.xaml.cs

@ -15,7 +15,7 @@ namespace ControlCatalog
void SetupSide(string name, StandardCursorType cursor, WindowEdge edge)
{
var ctl = this.FindControl<Control>(name);
var ctl = this.Get<Control>(name);
ctl.Cursor = new Cursor(cursor);
ctl.PointerPressed += (i, e) =>
{
@ -26,7 +26,7 @@ namespace ControlCatalog
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
this.FindControl<Control>("TitleBar").PointerPressed += (i, e) =>
this.Get<Control>("TitleBar").PointerPressed += (i, e) =>
{
PlatformImpl?.BeginMoveDrag(e);
};
@ -38,12 +38,12 @@ namespace ControlCatalog
SetupSide("TopRight", StandardCursorType.TopRightCorner, WindowEdge.NorthEast);
SetupSide("BottomLeft", StandardCursorType.BottomLeftCorner, WindowEdge.SouthWest);
SetupSide("BottomRight", StandardCursorType.BottomRightCorner, WindowEdge.SouthEast);
this.FindControl<Button>("MinimizeButton").Click += delegate { this.WindowState = WindowState.Minimized; };
this.FindControl<Button>("MaximizeButton").Click += delegate
this.Get<Button>("MinimizeButton").Click += delegate { this.WindowState = WindowState.Minimized; };
this.Get<Button>("MaximizeButton").Click += delegate
{
WindowState = WindowState == WindowState.Maximized ? WindowState.Normal : WindowState.Maximized;
};
this.FindControl<Button>("CloseButton").Click += delegate
this.Get<Button>("CloseButton").Click += delegate
{
Close();
};

16
samples/ControlCatalog/MainView.xaml.cs

@ -18,9 +18,9 @@ namespace ControlCatalog
{
AvaloniaXamlLoader.Load(this);
var sideBar = this.FindControl<TabControl>("Sidebar");
var sideBar = this.Get<TabControl>("Sidebar");
if (AvaloniaLocator.Current.GetService<IRuntimePlatform>().GetRuntimeInfo().IsDesktop)
if (AvaloniaLocator.Current?.GetService<IRuntimePlatform>()?.GetRuntimeInfo().IsDesktop == true)
{
IList tabItems = ((IList)sideBar.Items);
tabItems.Add(new TabItem()
@ -36,7 +36,7 @@ namespace ControlCatalog
}
var themes = this.Find<ComboBox>("Themes");
var themes = this.Get<ComboBox>("Themes");
themes.SelectionChanged += (sender, e) =>
{
if (themes.SelectedItem is CatalogTheme theme)
@ -80,7 +80,7 @@ namespace ControlCatalog
}
};
var flowDirections = this.Find<ComboBox>("FlowDirection");
var flowDirections = this.Get<ComboBox>("FlowDirection");
flowDirections.SelectionChanged += (sender, e) =>
{
if (flowDirections.SelectedItem is FlowDirection flowDirection)
@ -89,7 +89,7 @@ namespace ControlCatalog
}
};
var decorations = this.Find<ComboBox>("Decorations");
var decorations = this.Get<ComboBox>("Decorations");
decorations.SelectionChanged += (sender, e) =>
{
if (VisualRoot is Window window
@ -99,8 +99,8 @@ namespace ControlCatalog
}
};
var transparencyLevels = this.Find<ComboBox>("TransparencyLevels");
IDisposable backgroundSetter = null, paneBackgroundSetter = null;
var transparencyLevels = this.Get<ComboBox>("TransparencyLevels");
IDisposable? backgroundSetter = null, paneBackgroundSetter = null;
transparencyLevels.SelectionChanged += (sender, e) =>
{
backgroundSetter?.Dispose();
@ -118,7 +118,7 @@ namespace ControlCatalog
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
var decorations = this.Find<ComboBox>("Decorations");
var decorations = this.Get<ComboBox>("Decorations");
if (VisualRoot is Window window)
decorations.SelectedIndex = (int)window.SystemDecorations;
}

8
samples/ControlCatalog/MainWindow.xaml.cs

@ -12,7 +12,7 @@ namespace ControlCatalog
public class MainWindow : Window
{
private WindowNotificationManager _notificationArea;
private NativeMenu _recentMenu;
private NativeMenu? _recentMenu;
public MainWindow()
{
@ -28,9 +28,7 @@ namespace ControlCatalog
};
DataContext = new MainWindowViewModel(_notificationArea);
_recentMenu = ((NativeMenu.GetMenu(this).Items[0] as NativeMenuItem).Menu.Items[2] as NativeMenuItem).Menu;
ExtendClientAreaChromeHints = Avalonia.Platform.ExtendClientAreaChromeHints.OSXThickTitleBar;
_recentMenu = ((NativeMenu.GetMenu(this)?.Items[0] as NativeMenuItem)?.Menu?.Items[2] as NativeMenuItem)?.Menu;
}
public static string MenuQuitHeader => RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? "Quit Avalonia" : "E_xit";
@ -41,7 +39,7 @@ namespace ControlCatalog
public void OnOpenClicked(object sender, EventArgs args)
{
_recentMenu.Items.Insert(0, new NativeMenuItem("Item " + (_recentMenu.Items.Count + 1)));
_recentMenu?.Items.Insert(0, new NativeMenuItem("Item " + (_recentMenu.Items.Count + 1)));
}
public void OnCloseClicked(object sender, EventArgs args)

4
samples/ControlCatalog/Models/Countries.cs

@ -237,7 +237,7 @@ namespace ControlCatalog.Models
yield return new Country("Zimbabwe", "SUB-SAHARAN AFRICA", 12236805, 390580, 31.3, 0, 0, 67.69, 1900, 90.7, 26.8, 28.01, 21.84);
}
static IReadOnlyList<Country> _all;
static IReadOnlyList<Country>? _all;
public static IReadOnlyList<Country> All
{
@ -253,4 +253,4 @@ namespace ControlCatalog.Models
}
}
}
}

4
samples/ControlCatalog/Models/GDPValueConverter.cs

@ -14,7 +14,7 @@ namespace ControlCatalog.Models
{
public class GDPValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
if (value is int gdp)
{
@ -29,7 +29,7 @@ namespace ControlCatalog.Models
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
throw new NotImplementedException();
}

16
samples/ControlCatalog/Models/Person.cs

@ -13,8 +13,8 @@ namespace ControlCatalog.Models
{
public class Person : INotifyDataErrorInfo, INotifyPropertyChanged
{
string _firstName;
string _lastName;
string _firstName = string.Empty;
string _lastName = string.Empty;
bool _isBanned;
private int _age;
@ -76,7 +76,7 @@ namespace ControlCatalog.Models
Dictionary<string, List<string>> _errorLookup = new Dictionary<string, List<string>>();
void SetError(string propertyName, string error)
void SetError(string propertyName, string? error)
{
if (string.IsNullOrEmpty(error))
{
@ -88,11 +88,11 @@ namespace ControlCatalog.Models
if (_errorLookup.TryGetValue(propertyName, out List<string> errorList))
{
errorList.Clear();
errorList.Add(error);
errorList.Add(error!);
}
else
{
var errors = new List<string> { error };
var errors = new List<string> { error! };
_errorLookup.Add(propertyName, errors);
}
@ -102,8 +102,8 @@ namespace ControlCatalog.Models
public bool HasErrors => _errorLookup.Count > 0;
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
public event PropertyChangedEventHandler PropertyChanged;
public event EventHandler<DataErrorsChangedEventArgs>? ErrorsChanged;
public event PropertyChangedEventHandler? PropertyChanged;
void OnErrorsChanged(string propertyName)
{
@ -114,7 +114,7 @@ namespace ControlCatalog.Models
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public IEnumerable GetErrors(string propertyName)
public IEnumerable? GetErrors(string propertyName)
{
if (_errorLookup.TryGetValue(propertyName, out List<string> errorList))
return errorList;

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

@ -126,13 +126,13 @@ namespace ControlCatalog.Pages
binding.Bindings.Add(new Binding("Name"));
binding.Bindings.Add(new Binding("Abbreviation"));
var multibindingBox = this.FindControl<AutoCompleteBox>("MultiBindingBox");
var multibindingBox = this.Get<AutoCompleteBox>("MultiBindingBox");
multibindingBox.ValueMemberBinding = binding;
var asyncBox = this.FindControl<AutoCompleteBox>("AsyncBox");
var asyncBox = this.Get<AutoCompleteBox>("AsyncBox");
asyncBox.AsyncPopulator = PopulateAsync;
var customAutocompleteBox = this.FindControl<AutoCompleteBox>("CustomAutocompleteBox");
var customAutocompleteBox = this.Get<AutoCompleteBox>("CustomAutocompleteBox");
customAutocompleteBox.Items = Sentences.SelectMany(x => x);
customAutocompleteBox.TextFilter = LastWordContains;
customAutocompleteBox.TextSelector = AppendWord;
@ -144,11 +144,12 @@ namespace ControlCatalog.Pages
.OfType<AutoCompleteBox>();
}
private bool StringContains(string str, string query)
private bool StringContains(string str, string? query)
{
if (query == null) return false;
return str.IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0;
}
private async Task<IEnumerable<object>> PopulateAsync(string searchText, CancellationToken cancellationToken)
private async Task<IEnumerable<object>> PopulateAsync(string? searchText, CancellationToken cancellationToken)
{
await Task.Delay(TimeSpan.FromSeconds(1.5), cancellationToken);
@ -157,14 +158,14 @@ namespace ControlCatalog.Pages
.ToList();
}
private bool LastWordContains(string searchText, string item)
private bool LastWordContains(string? searchText, string? item)
{
var words = searchText.Split(' ');
var words = searchText?.Split(' ') ?? Array.Empty<string>();
var options = Sentences.Select(x => x.First).ToArray();
for (var i = 0; i < words.Length; ++i)
{
var word = words[i];
for (var j = 0; j < options.Length; ++j)
for (var j = 0; word is { } && j < options.Length; ++j)
{
var option = options[j];
if (option == null)
@ -183,14 +184,18 @@ namespace ControlCatalog.Pages
return options.Any(x => x != null && x.Value == item);
}
private string AppendWord(string text, string item)
private string AppendWord(string? text, string? item)
{
string[] parts = text.Split(' ');
if (parts.Length == 0)
return item;
if (item is { })
{
string[] parts = text?.Split(' ') ?? Array.Empty<string>();
if (parts.Length == 0)
return item;
parts[parts.Length - 1] = item;
return string.Join(" ", parts);
parts[parts.Length - 1] = item;
return string.Join(" ", parts);
}
return string.Empty;
}
private void InitializeComponent()

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

@ -23,7 +23,7 @@ namespace ControlCatalog.Pages
var spinner = (ButtonSpinner)sender;
var txtBox = (TextBlock)spinner.Content;
int value = Array.IndexOf(_mountains, txtBox.Text);
int value = Array.IndexOf(_mountains, txtBox?.Text);
if (e.Direction == SpinDirection.Increase)
value++;
else

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

@ -11,7 +11,7 @@ namespace ControlCatalog.Pages
{
InitializeComponent();
this.FindControl<RepeatButton>("RepeatButton").Click += OnRepeatButtonClick;
this.Get<RepeatButton>("RepeatButton").Click += OnRepeatButtonClick;
}
private void InitializeComponent()
@ -22,7 +22,7 @@ namespace ControlCatalog.Pages
public void OnRepeatButtonClick(object sender, object args)
{
repeatButtonClickCount++;
var textBlock = this.FindControl<TextBlock>("RepeatButtonTextBlock");
var textBlock = this.Get<TextBlock>("RepeatButtonTextBlock");
textBlock.Text = $"Repeat Button: {repeatButtonClickCount}";
}
}

12
samples/ControlCatalog/Pages/CalendarDatePickerPage.xaml.cs

@ -10,11 +10,11 @@ namespace ControlCatalog.Pages
{
InitializeComponent();
var dp1 = this.FindControl<CalendarDatePicker>("DatePicker1");
var dp2 = this.FindControl<CalendarDatePicker>("DatePicker2");
var dp3 = this.FindControl<CalendarDatePicker>("DatePicker3");
var dp4 = this.FindControl<CalendarDatePicker>("DatePicker4");
var dp5 = this.FindControl<CalendarDatePicker>("DatePicker5");
var dp1 = this.Get<CalendarDatePicker>("DatePicker1");
var dp2 = this.Get<CalendarDatePicker>("DatePicker2");
var dp3 = this.Get<CalendarDatePicker>("DatePicker3");
var dp4 = this.Get<CalendarDatePicker>("DatePicker4");
var dp5 = this.Get<CalendarDatePicker>("DatePicker5");
dp1.SelectedDate = DateTime.Today;
dp2.SelectedDate = DateTime.Today.AddDays(10);
@ -23,7 +23,7 @@ namespace ControlCatalog.Pages
dp4.TemplateApplied += (s, e) =>
{
dp4.BlackoutDates.AddDatesInPast();
dp4.BlackoutDates?.AddDatesInPast();
};
}

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

@ -11,11 +11,11 @@ namespace ControlCatalog.Pages
this.InitializeComponent();
var today = DateTime.Today;
var cal1 = this.FindControl<Calendar>("DisplayDatesCalendar");
var cal1 = this.Get<Calendar>("DisplayDatesCalendar");
cal1.DisplayDateStart = today.AddDays(-25);
cal1.DisplayDateEnd = today.AddDays(25);
var cal2 = this.FindControl<Calendar>("BlackoutDatesCalendar");
var cal2 = this.Get<Calendar>("BlackoutDatesCalendar");
cal2.BlackoutDates.AddDatesInPast();
cal2.BlackoutDates.Add(new CalendarDateRange(today.AddDays(6)));
}

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

@ -16,6 +16,11 @@ namespace ControlCatalog.Pages
public CarouselPage()
{
this.InitializeComponent();
_carousel = this.Get<Carousel>("carousel");
_left = this.Get<Button>("left");
_right = this.Get<Button>("right");
_transition = this.Get<ComboBox>("transition");
_orientation = this.Get<ComboBox>("orientation");
_left.Click += (s, e) => _carousel.Previous();
_right.Click += (s, e) => _carousel.Next();
_transition.SelectionChanged += TransitionChanged;
@ -25,11 +30,7 @@ namespace ControlCatalog.Pages
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
_carousel = this.FindControl<Carousel>("carousel");
_left = this.FindControl<Button>("left");
_right = this.FindControl<Button>("right");
_transition = this.FindControl<ComboBox>("transition");
_orientation = this.FindControl<ComboBox>("orientation");
}
private void TransitionChanged(object sender, SelectionChangedEventArgs e)

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

@ -18,29 +18,29 @@ namespace ControlCatalog.Pages
DataContext = new ContextPageViewModel();
_textBox = this.FindControl<TextBox>("TextBox");
_textBox = this.Get<TextBox>("TextBox");
var cutButton = this.FindControl<Button>("CutButton");
var cutButton = this.Get<Button>("CutButton");
cutButton.Click += CloseFlyout;
var copyButton = this.FindControl<Button>("CopyButton");
var copyButton = this.Get<Button>("CopyButton");
copyButton.Click += CloseFlyout;
var pasteButton = this.FindControl<Button>("PasteButton");
var pasteButton = this.Get<Button>("PasteButton");
pasteButton.Click += CloseFlyout;
var clearButton = this.FindControl<Button>("ClearButton");
var clearButton = this.Get<Button>("ClearButton");
clearButton.Click += CloseFlyout;
var customContextRequestedBorder = this.FindControl<Border>("CustomContextRequestedBorder");
var customContextRequestedBorder = this.Get<Border>("CustomContextRequestedBorder");
customContextRequestedBorder.AddHandler(ContextRequestedEvent, CustomContextRequested, RoutingStrategies.Tunnel);
var cancellableContextBorder = this.FindControl<Border>("CancellableContextBorder");
var cancellableContextBorder = this.Get<Border>("CancellableContextBorder");
cancellableContextBorder.ContextFlyout!.Closing += ContextFlyoutPage_Closing;
cancellableContextBorder.ContextFlyout!.Opening += ContextFlyoutPage_Opening;
}
private ContextPageViewModel _model;
private ContextPageViewModel? _model;
protected override void OnDataContextChanged(EventArgs e)
{
if (_model != null)
@ -55,7 +55,7 @@ namespace ControlCatalog.Pages
private void ContextFlyoutPage_Closing(object sender, CancelEventArgs e)
{
var cancelCloseCheckBox = this.FindControl<CheckBox>("CancelCloseCheckBox");
e.Cancel = cancelCloseCheckBox.IsChecked ?? false;
e.Cancel = cancelCloseCheckBox?.IsChecked ?? false;
}
private void ContextFlyoutPage_Opening(object sender, EventArgs e)
@ -63,13 +63,13 @@ namespace ControlCatalog.Pages
if (e is CancelEventArgs cancelArgs)
{
var cancelCloseCheckBox = this.FindControl<CheckBox>("CancelOpenCheckBox");
cancelArgs.Cancel = cancelCloseCheckBox.IsChecked ?? false;
cancelArgs.Cancel = cancelCloseCheckBox?.IsChecked ?? false;
}
}
private void CloseFlyout(object sender, RoutedEventArgs e)
{
_textBox.ContextFlyout.Hide();
_textBox.ContextFlyout?.Hide();
}
public void CustomContextRequested(object sender, ContextRequestedEventArgs e)

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

@ -15,15 +15,15 @@ namespace ControlCatalog.Pages
this.InitializeComponent();
DataContext = new ContextPageViewModel();
var customContextRequestedBorder = this.FindControl<Border>("CustomContextRequestedBorder");
var customContextRequestedBorder = this.Get<Border>("CustomContextRequestedBorder");
customContextRequestedBorder.AddHandler(ContextRequestedEvent, CustomContextRequested, RoutingStrategies.Tunnel);
var cancellableContextBorder = this.FindControl<Border>("CancellableContextBorder");
var cancellableContextBorder = this.Get<Border>("CancellableContextBorder");
cancellableContextBorder.ContextMenu!.ContextMenuClosing += ContextFlyoutPage_Closing;
cancellableContextBorder.ContextMenu!.ContextMenuOpening += ContextFlyoutPage_Opening;
}
private ContextPageViewModel _model;
private ContextPageViewModel? _model;
protected override void OnDataContextChanged(EventArgs e)
{
if (_model != null)

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

@ -21,7 +21,7 @@ namespace ControlCatalog.Pages
var dataGridSortDescription = DataGridSortDescription.FromPath(nameof(Country.Region), ListSortDirection.Ascending, new ReversedStringComparer());
var collectionView1 = new DataGridCollectionView(Countries.All);
collectionView1.SortDescriptions.Add(dataGridSortDescription);
var dg1 = this.FindControl<DataGrid>("dataGrid1");
var dg1 = this.Get<DataGrid>("dataGrid1");
dg1.IsReadOnly = true;
dg1.LoadingRow += Dg1_LoadingRow;
dg1.Sorting += (s, a) =>
@ -37,7 +37,7 @@ namespace ControlCatalog.Pages
};
dg1.Items = collectionView1;
var dg2 = this.FindControl<DataGrid>("dataGridGrouping");
var dg2 = this.Get<DataGrid>("dataGridGrouping");
dg2.IsReadOnly = true;
var collectionView2 = new DataGridCollectionView(Countries.All);
@ -45,7 +45,7 @@ namespace ControlCatalog.Pages
dg2.Items = collectionView2;
var dg3 = this.FindControl<DataGrid>("dataGridEdit");
var dg3 = this.Get<DataGrid>("dataGridEdit");
dg3.IsReadOnly = false;
var items = new List<Person>
@ -58,7 +58,7 @@ namespace ControlCatalog.Pages
dg3.Items = collectionView3;
var addButton = this.FindControl<Button>("btnAdd");
var addButton = this.Get<Button>("btnAdd");
addButton.Click += (a, b) => collectionView3.AddNew();
}

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

@ -9,12 +9,12 @@ namespace ControlCatalog.Pages
public DateTimePickerPage()
{
this.InitializeComponent();
this.FindControl<TextBlock>("DatePickerDesc").Text = "Use a DatePicker to let users set a date in your app, " +
this.Get<TextBlock>("DatePickerDesc").Text = "Use a DatePicker to let users set a date in your app, " +
"for example to schedule an appointment. The DatePicker displays three controls for month, day, and year. " +
"These controls are easy to use with touch or mouse, and they can be styled and configured in several different ways. " +
"Order of month, day, and year is dynamically set based on user date settings";
this.FindControl<TextBlock>("TimePickerDesc").Text = "Use a TimePicker to let users set a time in your app, for example " +
this.Get<TextBlock>("TimePickerDesc").Text = "Use a TimePicker to let users set a time in your app, for example " +
"to set a reminder. The TimePicker displays three controls for hour, minute, and AM / PM(if necessary).These controls " +
"are easy to use with touch or mouse, and they can be styled and configured in several different ways. " +
"12 - hour or 24 - hour clock and visiblility of AM / PM is dynamically set based on user time settings, or can be overridden.";

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

@ -16,14 +16,14 @@ namespace ControlCatalog.Pages
{
this.InitializeComponent();
var results = this.FindControl<ItemsPresenter>("PickerLastResults");
var resultsVisible = this.FindControl<TextBlock>("PickerLastResultsVisible");
var results = this.Get<ItemsPresenter>("PickerLastResults");
var resultsVisible = this.Get<TextBlock>("PickerLastResultsVisible");
string lastSelectedDirectory = null;
string? lastSelectedDirectory = null;
List<FileDialogFilter>? GetFilters()
{
if (this.FindControl<CheckBox>("UseFilters").IsChecked != true)
if (this.Get<CheckBox>("UseFilters").IsChecked != true)
return null;
return new List<FileDialogFilter>
{
@ -39,7 +39,7 @@ namespace ControlCatalog.Pages
};
}
this.FindControl<Button>("OpenFile").Click += async delegate
this.Get<Button>("OpenFile").Click += async delegate
{
// Almost guaranteed to exist
var fullPath = Assembly.GetEntryAssembly()?.GetModules().FirstOrDefault()?.FullyQualifiedName;
@ -56,7 +56,7 @@ namespace ControlCatalog.Pages
results.Items = result;
resultsVisible.IsVisible = result?.Any() == true;
};
this.FindControl<Button>("OpenMultipleFiles").Click += async delegate
this.Get<Button>("OpenMultipleFiles").Click += async delegate
{
var result = await new OpenFileDialog()
{
@ -68,7 +68,7 @@ namespace ControlCatalog.Pages
results.Items = result;
resultsVisible.IsVisible = result?.Any() == true;
};
this.FindControl<Button>("SaveFile").Click += async delegate
this.Get<Button>("SaveFile").Click += async delegate
{
var result = await new SaveFileDialog()
{
@ -80,7 +80,7 @@ namespace ControlCatalog.Pages
results.Items = new[] { result };
resultsVisible.IsVisible = result != null;
};
this.FindControl<Button>("SelectFolder").Click += async delegate
this.Get<Button>("SelectFolder").Click += async delegate
{
var result = await new OpenFolderDialog()
{
@ -96,7 +96,7 @@ namespace ControlCatalog.Pages
results.Items = new [] { result };
resultsVisible.IsVisible = result != null;
};
this.FindControl<Button>("OpenBoth").Click += async delegate
this.Get<Button>("OpenBoth").Click += async delegate
{
var result = await new OpenFileDialog()
{
@ -110,35 +110,35 @@ namespace ControlCatalog.Pages
results.Items = result;
resultsVisible.IsVisible = result?.Any() == true;
};
this.FindControl<Button>("DecoratedWindow").Click += delegate
this.Get<Button>("DecoratedWindow").Click += delegate
{
new DecoratedWindow().Show();
};
this.FindControl<Button>("DecoratedWindowDialog").Click += delegate
this.Get<Button>("DecoratedWindowDialog").Click += delegate
{
new DecoratedWindow().ShowDialog(GetWindow());
};
this.FindControl<Button>("Dialog").Click += delegate
this.Get<Button>("Dialog").Click += delegate
{
var window = CreateSampleWindow();
window.Height = 200;
window.ShowDialog(GetWindow());
};
this.FindControl<Button>("DialogNoTaskbar").Click += delegate
this.Get<Button>("DialogNoTaskbar").Click += delegate
{
var window = CreateSampleWindow();
window.Height = 200;
window.ShowInTaskbar = false;
window.ShowDialog(GetWindow());
};
this.FindControl<Button>("OwnedWindow").Click += delegate
this.Get<Button>("OwnedWindow").Click += delegate
{
var window = CreateSampleWindow();
window.Show(GetWindow());
};
this.FindControl<Button>("OwnedWindowNoTaskbar").Click += delegate
this.Get<Button>("OwnedWindowNoTaskbar").Click += delegate
{
var window = CreateSampleWindow();
@ -191,7 +191,7 @@ namespace ControlCatalog.Pages
return window;
}
Window GetWindow() => (Window)this.VisualRoot;
Window GetWindow() => this.VisualRoot as Window ?? throw new NullReferenceException("Invalid Owner");
private void InitializeComponent()
{

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

@ -14,7 +14,7 @@ namespace ControlCatalog.Pages
public DragAndDropPage()
{
this.InitializeComponent();
_DropState = this.Find<TextBlock>("DropState");
_DropState = this.Get<TextBlock>("DropState");
int textCount = 0;
SetupDnd("Text", d => d.Set(DataFormats.Text,
@ -26,8 +26,8 @@ namespace ControlCatalog.Pages
void SetupDnd(string suffix, Action<DataObject> factory, DragDropEffects effects)
{
var dragMe = this.Find<Border>("DragMe" + suffix);
var dragState = this.Find<TextBlock>("DragState"+suffix);
var dragMe = this.Get<Border>("DragMe" + suffix);
var dragState = this.Get<TextBlock>("DragState"+suffix);
async void DoDrag(object sender, Avalonia.Input.PointerPressedEventArgs e)
{

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

@ -35,7 +35,7 @@ namespace ControlCatalog.Pages
private void SetXamlTexts()
{
var bfxt = this.FindControl<TextBlock>("ButtonFlyoutXamlText");
var bfxt = this.Get<TextBlock>("ButtonFlyoutXamlText");
bfxt.Text = "<Button Content=\"Click me!\">\n" +
" <Button.Flyout>\n" +
" <Flyout>\n" +
@ -45,7 +45,7 @@ namespace ControlCatalog.Pages
" </Flyout>\n" +
" </Button.Flyout>\n</Button>";
var mfxt = this.FindControl<TextBlock>("MenuFlyoutXamlText");
var mfxt = this.Get<TextBlock>("MenuFlyoutXamlText");
mfxt.Text = "<Button Content=\"Click me!\">\n" +
" <Button.Flyout>\n" +
" <MenuFlyout>\n" +
@ -54,7 +54,7 @@ namespace ControlCatalog.Pages
" </MenuFlyout>\n" +
" </Button.Flyout>\n</Button>";
var afxt = this.FindControl<TextBlock>("AttachedFlyoutXamlText");
var afxt = this.Get<TextBlock>("AttachedFlyoutXamlText");
afxt.Text = "<Panel Name=\"AttachedFlyoutPanel\">\n" +
" <FlyoutBase.AttachedFlyout>\n" +
" <Flyout>\n" +
@ -66,7 +66,7 @@ namespace ControlCatalog.Pages
"\n\n In DoubleTapped handler:\n" +
"FlyoutBase.ShowAttachedFlyout(AttachedFlyoutPanel);";
var sfxt = this.FindControl<TextBlock>("SharedFlyoutXamlText");
var sfxt = this.Get<TextBlock>("SharedFlyoutXamlText");
sfxt.Text = "Declare a flyout in Resources:\n" +
"<Window.Resources>\n" +
" <Flyout x:Key=\"SharedFlyout\">\n" +

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

@ -17,9 +17,9 @@ namespace ControlCatalog.Pages
public ImagePage()
{
InitializeComponent();
_bitmapImage = this.FindControl<Image>("bitmapImage");
_drawingImage = this.FindControl<Image>("drawingImage");
_croppedImage = this.FindControl<Image>("croppedImage");
_bitmapImage = this.Get<Image>("bitmapImage");
_drawingImage = this.Get<Image>("drawingImage");
_croppedImage = this.Get<Image>("croppedImage");
}
private void InitializeComponent()
@ -50,8 +50,11 @@ namespace ControlCatalog.Pages
if (_croppedImage != null)
{
var comboxBox = (ComboBox)sender;
var croppedBitmap = _croppedImage.Source as CroppedBitmap;
croppedBitmap.SourceRect = GetCropRect(comboxBox.SelectedIndex);
if (_croppedImage.Source is CroppedBitmap croppedBitmap)
{
croppedBitmap.SourceRect = GetCropRect(comboxBox.SelectedIndex);
}
}
}

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

@ -24,11 +24,11 @@ namespace ControlCatalog.Pages
public ItemsRepeaterPage()
{
this.InitializeComponent();
_repeater = this.FindControl<ItemsRepeater>("repeater");
_scroller = this.FindControl<ScrollViewer>("scroller");
_scrollToLast = this.FindControl<Button>("scrollToLast");
_scrollToRandom = this.FindControl<Button>("scrollToRandom");
_scrollToSelected = this.FindControl<Button>("scrollToSelected");
_repeater = this.Get<ItemsRepeater>("repeater");
_scroller = this.Get<ScrollViewer>("scroller");
_scrollToLast = this.Get<Button>("scrollToLast");
_scrollToRandom = this.Get<Button>("scrollToRandom");
_scrollToSelected = this.Get<Button>("scrollToSelected");
_repeater.PointerPressed += RepeaterClick;
_repeater.KeyDown += RepeaterOnKeyDown;
_scrollToLast.Click += scrollToLast_Click;
@ -44,8 +44,10 @@ namespace ControlCatalog.Pages
public void OnSelectTemplateKey(object sender, SelectTemplateEventArgs e)
{
var item = (ItemsRepeaterPageViewModel.Item)e.DataContext;
e.TemplateKey = (item.Index % 2 == 0) ? "even" : "odd";
if (e.DataContext is ItemsRepeaterPageViewModel.Item item)
{
e.TemplateKey = (item.Index % 2 == 0) ? "even" : "odd";
}
}
private void LayoutChanged(object sender, SelectionChangedEventArgs e)
@ -115,7 +117,7 @@ namespace ControlCatalog.Pages
private void ScrollTo(int index)
{
System.Diagnostics.Debug.WriteLine("Scroll to " + index);
var layoutManager = ((TopLevel)VisualRoot).LayoutManager;
var layoutManager = ((TopLevel)VisualRoot!).LayoutManager;
var element = _repeater.GetOrCreateElement(index);
layoutManager.ExecuteLayoutPass();
element.BringIntoView();
@ -123,9 +125,11 @@ namespace ControlCatalog.Pages
private void RepeaterClick(object sender, PointerPressedEventArgs e)
{
var item = (e.Source as TextBlock)?.DataContext as ItemsRepeaterPageViewModel.Item;
_viewModel.SelectedItem = item;
_selectedIndex = _viewModel.Items.IndexOf(item);
if ((e.Source as TextBlock)?.DataContext is ItemsRepeaterPageViewModel.Item item)
{
_viewModel.SelectedItem = item;
_selectedIndex = _viewModel.Items.IndexOf(item);
}
}
private void RepeaterOnKeyDown(object sender, KeyEventArgs e)

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

@ -7,7 +7,7 @@ namespace ControlCatalog.Pages
{
public class LabelsPage : UserControl
{
private Person _person;
private Person? _person;
public LabelsPage()
{

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

@ -22,7 +22,7 @@ namespace ControlCatalog.Pages
AvaloniaXamlLoader.Load(this);
}
private MenuPageViewModel _model;
private MenuPageViewModel? _model;
protected override void OnDataContextChanged(EventArgs e)
{
if (_model != null)

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

@ -79,6 +79,8 @@ namespace ControlCatalog.Pages
public interface INativeDemoControl
{
/// <param name="isSecond">Used to specify which control should be displayed as a demo</param>
/// <param name="parent"></param>
/// <param name="createDefault"></param>
IPlatformHandle CreateControl(bool isSecond, IPlatformHandle parent, Func<IPlatformHandle> createDefault);
}
}

2
samples/ControlCatalog/Pages/NumericUpDownPage.xaml

@ -65,7 +65,7 @@
Margin="2" HorizontalAlignment="Center"/>
<TextBlock Grid.Row="3" Grid.Column="0" VerticalAlignment="Center" Margin="10,2,2,2">Value:</TextBlock>
<NumericUpDown Grid.Row="3" Grid.Column="1" Value="{Binding #upDown.Value}" VerticalAlignment="Center"
<NumericUpDown Grid.Row="3" Grid.Column="1" Value="{Binding DecimalValue}" VerticalAlignment="Center"
Margin="2" HorizontalAlignment="Center"/>

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

@ -28,16 +28,16 @@ namespace ControlCatalog.Pages
public class NumbersPageViewModel : ViewModelBase
{
private IList<FormatObject> _formats;
private IList<FormatObject>? _formats;
private FormatObject _selectedFormat;
private IList<Location> _spinnerLocations;
private IList<Location>? _spinnerLocations;
private double _doubleValue;
private decimal _decimalValue;
public NumbersPageViewModel()
{
SelectedFormat = Formats.FirstOrDefault();
_selectedFormat = Formats.FirstOrDefault();
}
public double DoubleValue
@ -103,7 +103,7 @@ namespace ControlCatalog.Pages
public class FormatObject
{
public string Value { get; set; }
public string Name { get; set; }
public string? Value { get; set; }
public string? Name { get; set; }
}
}

2
samples/ControlCatalog/Pages/ScreenPage.cs

@ -18,7 +18,7 @@ namespace ControlCatalog.Pages
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
Window w = (Window)VisualRoot;
Window w = (Window)VisualRoot!;
w.PositionChanged += (sender, args) => InvalidateVisual();
}

12
samples/ControlCatalog/Pages/TabControlPage.xaml.cs

@ -52,7 +52,7 @@ namespace ControlCatalog.Pages
private IBitmap LoadBitmap(string uri)
{
var assets = AvaloniaLocator.Current.GetService<IAssetLoader>();
var assets = AvaloniaLocator.Current!.GetService<IAssetLoader>()!;
return new Bitmap(assets.Open(new Uri(uri)));
}
@ -60,7 +60,7 @@ namespace ControlCatalog.Pages
{
private Dock _tabPlacement;
public TabItemViewModel[] Tabs { get; set; }
public TabItemViewModel[]? Tabs { get; set; }
public Dock TabPlacement
{
@ -71,10 +71,10 @@ namespace ControlCatalog.Pages
private class TabItemViewModel
{
public string Header { get; set; }
public string Text { get; set; }
public IBitmap Image { get; set; }
public bool IsEnabled { get; set; } = true;
public string? Header { get; set; }
public string? Text { get; set; }
public IBitmap? Image { get; set; }
public bool IsEnabled { get; set; } = true;
}
}
}

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

@ -38,7 +38,7 @@ namespace ControlCatalog.Pages
private class TabStripItemViewModel
{
public string Header { get; set; }
public string? Header { get; set; }
public bool IsEnabled { get; set; } = true;
}
}

2
samples/ControlCatalog/ViewModels/ApplicationViewModel.cs

@ -10,7 +10,7 @@ namespace ControlCatalog.ViewModels
{
ExitCommand = MiniCommand.Create(() =>
{
if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime lifetime)
if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime lifetime)
{
lifetime.Shutdown();
}

2
samples/ControlCatalog/ViewModels/ContextPageViewModel.cs

@ -9,7 +9,7 @@ namespace ControlCatalog.ViewModels
{
public class ContextPageViewModel
{
public Control View { get; set; }
public Control? View { get; set; }
public ContextPageViewModel()
{
OpenCommand = MiniCommand.CreateFromTask(Open);

2
samples/ControlCatalog/ViewModels/CursorPageViewModel.cs

@ -18,7 +18,7 @@ namespace ControlCatalog.ViewModels
.Select(x => new StandardCursorModel(x))
.ToList();
var loader = AvaloniaLocator.Current.GetService<IAssetLoader>();
var loader = AvaloniaLocator.Current!.GetService<IAssetLoader>()!;
var s = loader.Open(new Uri("avares://ControlCatalog/Assets/avalonia-32.png"));
var bitmap = new Bitmap(s);
CustomCursor = new Cursor(bitmap, new PixelPoint(16, 16));

19
samples/ControlCatalog/ViewModels/ItemsRepeaterPageViewModel.cs

@ -14,7 +14,7 @@ namespace ControlCatalog.ViewModels
public ItemsRepeaterPageViewModel()
{
Items = CreateItems();
_items = CreateItems();
}
public ObservableCollection<Item> Items
@ -23,12 +23,12 @@ namespace ControlCatalog.ViewModels
set => this.RaiseAndSetIfChanged(ref _items, value);
}
public Item SelectedItem { get; set; }
public Item? SelectedItem { get; set; }
public void AddItem()
{
var index = SelectedItem != null ? Items.IndexOf(SelectedItem) : -1;
Items.Insert(index + 1, new Item(index + 1) { Text = $"New Item {_newItemIndex++}" });
Items.Insert(index + 1, new Item(index + 1, $"New Item {_newItemIndex++}"));
}
public void RemoveItem()
@ -66,19 +66,20 @@ namespace ControlCatalog.ViewModels
_newGenerationIndex++;
return new ObservableCollection<Item>(
Enumerable.Range(1, 100000).Select(i => new Item(i)
{
Text = $"Item {i.ToString()} {suffix}"
}));
Enumerable.Range(1, 100000).Select(i => new Item(i, $"Item {i.ToString()} {suffix}")));
}
public class Item : ViewModelBase
{
private double _height = double.NaN;
public Item(int index) => Index = index;
public Item(int index, string text)
{
Index = index;
Text = text;
}
public int Index { get; }
public string Text { get; set; }
public string Text { get; }
public double Height
{

13
samples/ControlCatalog/ViewModels/MainWindowViewModel.cs

@ -16,11 +16,11 @@ namespace ControlCatalog.ViewModels
private bool _isMenuItemChecked = true;
private WindowState _windowState;
private WindowState[] _windowStates;
private WindowState[] _windowStates = Array.Empty<WindowState>();
private int _transparencyLevel;
private ExtendClientAreaChromeHints _chromeHints = ExtendClientAreaChromeHints.PreferSystemChrome;
private bool _extendClientAreaEnabled;
private bool _systemTitleBarEnabled;
private bool _systemTitleBarEnabled;
private bool _preferSystemChromeEnabled;
private double _titleBarHeight;
@ -47,14 +47,15 @@ namespace ControlCatalog.ViewModels
{
var dialog = new AboutAvaloniaDialog();
var mainWindow = (App.Current.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime)?.MainWindow;
await dialog.ShowDialog(mainWindow);
if ((App.Current?.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime)?.MainWindow is { } mainWindow)
{
await dialog.ShowDialog(mainWindow);
}
});
ExitCommand = MiniCommand.Create(() =>
{
(App.Current.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime).Shutdown();
(App.Current?.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime)?.Shutdown();
});
ToggleMenuItemCheckedCommand = MiniCommand.Create(() =>

8
samples/ControlCatalog/ViewModels/MenuItemViewModel.cs

@ -5,9 +5,9 @@ namespace ControlCatalog.ViewModels
{
public class MenuItemViewModel
{
public string Header { get; set; }
public ICommand Command { get; set; }
public object CommandParameter { get; set; }
public IList<MenuItemViewModel> Items { get; set; }
public string? Header { get; set; }
public ICommand? Command { get; set; }
public object? CommandParameter { get; set; }
public IList<MenuItemViewModel>? Items { get; set; }
}
}

2
samples/ControlCatalog/ViewModels/MenuPageViewModel.cs

@ -10,7 +10,7 @@ namespace ControlCatalog.ViewModels
{
public class MenuPageViewModel
{
public Control View { get; set; }
public Control? View { get; set; }
public MenuPageViewModel()
{
OpenCommand = MiniCommand.CreateFromTask(Open);

4
samples/ControlCatalog/ViewModels/NotificationViewModel.cs

@ -19,8 +19,8 @@ namespace ControlCatalog.ViewModels
});
}
public string Title { get; set; }
public string Message { get; set; }
public string? Title { get; set; }
public string? Message { get; set; }
public MiniCommand YesCommand { get; }

16
samples/ControlCatalog/ViewModels/TransitioningContentControlPageViewModel.cs

@ -19,7 +19,7 @@ namespace ControlCatalog.ViewModels
{
public TransitioningContentControlPageViewModel()
{
var assetLoader = AvaloniaLocator.Current.GetService<IAssetLoader>();
var assetLoader = AvaloniaLocator.Current?.GetService<IAssetLoader>()!;
var images = new string[]
{
@ -36,8 +36,8 @@ namespace ControlCatalog.ViewModels
SetupTransitions();
SelectedTransition = PageTransitions[1];
SelectedImage = Images[0];
_SelectedTransition = PageTransitions[1];
_SelectedImage = Images[0];
}
public List<PageTransition> PageTransitions { get; } = new List<PageTransition>();
@ -45,12 +45,12 @@ namespace ControlCatalog.ViewModels
public List<Bitmap> Images { get; } = new List<Bitmap>();
private Bitmap? _SelectedImage;
private Bitmap _SelectedImage;
/// <summary>
/// Gets or Sets the selected image
/// </summary>
public Bitmap? SelectedImage
public Bitmap SelectedImage
{
get { return _SelectedImage; }
set { this.RaiseAndSetIfChanged(ref _SelectedImage, value); }
@ -160,12 +160,12 @@ namespace ControlCatalog.ViewModels
public string DisplayTitle { get; }
private IPageTransition _Transition;
private IPageTransition? _Transition;
/// <summary>
/// Gets or sets the transition
/// </summary>
public IPageTransition Transition
public IPageTransition? Transition
{
get { return _Transition; }
set { this.RaiseAndSetIfChanged(ref _Transition, value); }
@ -201,7 +201,7 @@ namespace ControlCatalog.ViewModels
/// </summary>
public TimeSpan Duration { get; set; }
public async Task Start(Visual from, Visual to, bool forward, CancellationToken cancellationToken)
public async Task Start(Visual? from, Visual? to, bool forward, CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested)
{

4
samples/ControlCatalog/ViewModels/TreeViewPageViewModel.cs

@ -92,7 +92,7 @@ namespace ControlCatalog.ViewModels
public class Node
{
private ObservableCollection<Node> _children;
private ObservableCollection<Node>? _children;
private int _childIndex = 10;
public Node()
@ -106,7 +106,7 @@ namespace ControlCatalog.ViewModels
Header = parent.Header + ' ' + index;
}
public Node Parent { get; }
public Node? Parent { get; }
public string Header { get; }
public bool AreChildrenInitialized => _children != null;
public ObservableCollection<Node> Children => _children ??= CreateChildren();

2
src/Android/Avalonia.Android/AvaloniaView.cs

@ -15,7 +15,7 @@ namespace Avalonia.Android
private EmbeddableControlRoot _root;
private readonly ViewImpl _view;
private IDisposable? _timerSubscription;
private IDisposable _timerSubscription;
public AvaloniaView(Context context) : base(context)
{

2
src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidKeyboardEventsHelper.cs

@ -30,7 +30,7 @@ namespace Avalonia.Android.Platform.Specific.Helpers
return DispatchKeyEventInternal(e, out callBase);
}
string? UnicodeTextInput(KeyEvent keyEvent)
string UnicodeTextInput(KeyEvent keyEvent)
{
return keyEvent.Action == KeyEventActions.Multiple
&& keyEvent.RepeatCount == 0

76
src/Avalonia.Base/Matrix.cs

@ -9,14 +9,14 @@ namespace Avalonia
/// <summary>
/// A 3x3 matrix.
/// </summary>
/// <remakrs>Matrix layout:
/// <remarks>Matrix layout:
/// | 1st col | 2nd col | 3r col |
/// 1st row | scaleX | skrewY | persX |
/// 2nd row | skrewX | scaleY | persY |
/// 3rd row | transX | transY | persZ |
/// 1st row | scaleX | skewY | perspX |
/// 2nd row | skewX | scaleY | perspY |
/// 3rd row | transX | transY | perspZ |
///
/// Note: Skia.SkMatrix uses a transposed layout (where for example skrewX/skrewY and perspp0/tranX are swapped).
/// </remakrs>
/// Note: Skia.SkMatrix uses a transposed layout (where for example skewX/skewY and persp0/transX are swapped).
/// </remarks>
#if !BUILDTASK
public
#endif
@ -36,18 +36,18 @@ namespace Avalonia
/// Initializes a new instance of the <see cref="Matrix"/> struct (equivalent to a 2x3 Matrix without perspective).
/// </summary>
/// <param name="scaleX">The first element of the first row.</param>
/// <param name="skrewY">The second element of the first row.</param>
/// <param name="skrewX">The first element of the second row.</param>
/// <param name="skewY">The second element of the first row.</param>
/// <param name="skewX">The first element of the second row.</param>
/// <param name="scaleY">The second element of the second row.</param>
/// <param name="offsetX">The first element of the third row.</param>
/// <param name="offsetY">The second element of the third row.</param>
public Matrix(
double scaleX,
double skrewY,
double skrewX,
double skewY,
double skewX,
double scaleY,
double offsetX,
double offsetY) : this( scaleX, skrewY, 0, skrewX, scaleY, 0, offsetX, offsetY, 1)
double offsetY) : this( scaleX, skewY, 0, skewX, scaleY, 0, offsetX, offsetY, 1)
{
}
@ -57,34 +57,34 @@ namespace Avalonia
/// Initializes a new instance of the <see cref="Matrix"/> struct.
/// </summary>
/// <param name="scaleX">The first element of the first row.</param>
/// <param name="skrewY">The second element of the first row.</param>
/// <param name="persX">The third element of the first row.</param>
/// <param name="skrewX">The first element of the second row.</param>
/// <param name="skewY">The second element of the first row.</param>
/// <param name="perspX">The third element of the first row.</param>
/// <param name="skewX">The first element of the second row.</param>
/// <param name="scaleY">The second element of the second row.</param>
/// <param name="persY">The third element of the second row.</param>
/// <param name="perspY">The third element of the second row.</param>
/// <param name="offsetX">The first element of the third row.</param>
/// <param name="offsetY">The second element of the third row.</param>
/// <param name="persZ">The third element of the third row.</param>
/// <param name="perspZ">The third element of the third row.</param>
public Matrix(
double scaleX,
double skrewY,
double persX,
double skrewX,
double skewY,
double perspX,
double skewX,
double scaleY,
double persY,
double perspY,
double offsetX,
double offsetY,
double persZ)
double perspZ)
{
_m11 = scaleX;
_m12 = skrewY;
_m13 = persX;
_m21 = skrewX;
_m12 = skewY;
_m13 = perspX;
_m21 = skewX;
_m22 = scaleY;
_m23 = persY;
_m23 = perspY;
_m31 = offsetX;
_m32 = offsetY;
_m33 = persZ;
_m33 = perspZ;
}
/// <summary>
@ -111,17 +111,17 @@ namespace Avalonia
public double M11 => _m11;
/// <summary>
/// The second element of the first row (skrewY).
/// The second element of the first row (skewY).
/// </summary>
public double M12 => _m12;
/// <summary>
/// The third element of the first row (persX: input x-axis perspective factor).
/// The third element of the first row (perspX: input x-axis perspective factor).
/// </summary>
public double M13 => _m13;
/// <summary>
/// The first element of the second row (skrewX).
/// The first element of the second row (skewX).
/// </summary>
public double M21 => _m21;
@ -131,7 +131,7 @@ namespace Avalonia
public double M22 => _m22;
/// <summary>
/// The third element of the second row (persY: input y-axis perspective factor).
/// The third element of the second row (perspY: input y-axis perspective factor).
/// </summary>
public double M23 => _m23;
@ -146,7 +146,7 @@ namespace Avalonia
public double M32 => _m32;
/// <summary>
/// The third element of the third row (persZ: perspective scale factor).
/// The third element of the third row (perspZ: perspective scale factor).
/// </summary>
public double M33 => _m33;
@ -450,13 +450,13 @@ namespace Avalonia
inverted = new Matrix(
(_m22 * _m33 - _m32 * _m23) * invdet,
(_m13 * _m31 - _m12 * _m33) * invdet,
(_m13 * _m32 - _m12 * _m33) * invdet,
(_m12 * _m23 - _m13 * _m22) * invdet,
(_m23 * _m31 - _m21 * _m33) * invdet,
(_m11 * _m33 - _m13 * _m31) * invdet,
(_m21 * _m13 - _m11 * _m23) * invdet,
(_m21 * _m32 - _m31 * _m22) * invdet,
(_m21 * _m12 - _m11 * _m32) * invdet,
(_m31 * _m12 - _m11 * _m32) * invdet,
(_m11 * _m22 - _m21 * _m12) * invdet
);
@ -481,7 +481,7 @@ namespace Avalonia
/// <summary>
/// Parses a <see cref="Matrix"/> string.
/// </summary>
/// <param name="s">Six or nine comma-delimited double values (m11, m12, m21, m22, offsetX, offsetY[, persX, persY, persZ]) that describe the new <see cref="Matrix"/></param>
/// <param name="s">Six or nine comma-delimited double values (m11, m12, m21, m22, offsetX, offsetY[, perspX, perspY, perspZ]) that describe the new <see cref="Matrix"/></param>
/// <returns>The <see cref="Matrix"/>.</returns>
public static Matrix Parse(string s)
{
@ -497,11 +497,11 @@ namespace Avalonia
var v4 = tokenizer.ReadDouble();
var v5 = tokenizer.ReadDouble();
var v6 = tokenizer.ReadDouble();
var pers = tokenizer.TryReadDouble(out var v7);
pers = pers && tokenizer.TryReadDouble(out v8);
pers = pers && tokenizer.TryReadDouble(out v9);
var persp = tokenizer.TryReadDouble(out var v7);
persp = persp && tokenizer.TryReadDouble(out v8);
persp = persp && tokenizer.TryReadDouble(out v9);
if (pers)
if (persp)
return new Matrix(v1, v2, v7, v3, v4, v8, v5, v6, v9);
else
return new Matrix(v1, v2, v3, v4, v5, v6);

3
src/Avalonia.Base/Media/GeometryDrawing.cs

@ -68,7 +68,8 @@ namespace Avalonia.Media
public override Rect GetBounds()
{
return Geometry?.GetRenderBounds(s_boundsPen) ?? Rect.Empty;
IPen pen = Pen ?? s_boundsPen;
return Geometry?.GetRenderBounds(pen) ?? Rect.Empty;
}
}
}

6
src/Avalonia.Base/Reactive/TypedBindingAdapter.cs

@ -30,13 +30,15 @@ namespace Avalonia.Reactive
}
catch (InvalidCastException e)
{
var unwrappedValue = value.HasValue ? value.Value : null;
Logger.TryGet(LogEventLevel.Error, LogArea.Binding)?.Log(
_target,
"Binding produced invalid value for {$Property} ({$PropertyType}): {$Value} ({$ValueType})",
_property.Name,
_property.PropertyType,
value.Value,
value.Value?.GetType());
unwrappedValue,
unwrappedValue?.GetType());
PublishNext(BindingValue<T>.BindingError(e));
}
}

3
src/Avalonia.Controls/Avalonia.Controls.csproj

@ -2,9 +2,6 @@
<PropertyGroup>
<TargetFrameworks>net6.0;netstandard2.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<Compile Include="..\Avalonia.Base\Metadata\NullableAttributes.cs" Link="NullableAttributes.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Avalonia.Base\Avalonia.Base.csproj" />
<ProjectReference Include="..\Avalonia.Remote.Protocol\Avalonia.Remote.Protocol.csproj" />

7
src/Avalonia.Controls/Button.cs

@ -232,6 +232,13 @@ namespace Avalonia.Controls
StopListeningForDefault(inputElement);
}
}
if (IsCancel)
{
if (e.Root is IInputElement inputElement)
{
StopListeningForCancel(inputElement);
}
}
}
/// <inheritdoc/>

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

@ -218,7 +218,7 @@ namespace Avalonia.Controls.Primitives
if (YearView != null)
{
var childCount = Calendar.RowsPerYear * Calendar.ColumnsPerYear;
var children = new List<IControl>(childCount);
using var children = new PooledList<IControl>(childCount);
EventHandler<PointerPressedEventArgs> monthCalendarButtonMouseDown = Month_CalendarButtonMouseDown;
EventHandler<PointerReleasedEventArgs> monthCalendarButtonMouseUp = Month_CalendarButtonMouseUp;

6
src/Avalonia.Controls/Carousel.cs

@ -20,8 +20,8 @@ namespace Avalonia.Controls
/// <summary>
/// Defines the <see cref="PageTransition"/> property.
/// </summary>
public static readonly StyledProperty<IPageTransition> PageTransitionProperty =
AvaloniaProperty.Register<Carousel, IPageTransition>(nameof(PageTransition));
public static readonly StyledProperty<IPageTransition?> PageTransitionProperty =
AvaloniaProperty.Register<Carousel, IPageTransition?>(nameof(PageTransition));
/// <summary>
/// The default value of <see cref="ItemsControl.ItemsPanelProperty"/> for
@ -54,7 +54,7 @@ namespace Avalonia.Controls
/// <summary>
/// Gets or sets the transition to use when moving between pages.
/// </summary>
public IPageTransition PageTransition
public IPageTransition? PageTransition
{
get { return GetValue(PageTransitionProperty); }
set { SetValue(PageTransitionProperty, value); }

111
src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs

@ -107,8 +107,8 @@ namespace Avalonia.Controls
/// <summary>
/// Defines the <see cref="Value"/> property.
/// </summary>
public static readonly DirectProperty<NumericUpDown, decimal> ValueProperty =
AvaloniaProperty.RegisterDirect<NumericUpDown, decimal>(nameof(Value), updown => updown.Value,
public static readonly DirectProperty<NumericUpDown, decimal?> ValueProperty =
AvaloniaProperty.RegisterDirect<NumericUpDown, decimal?>(nameof(Value), updown => updown.Value,
(updown, v) => updown.Value = v, defaultBindingMode: BindingMode.TwoWay, enableDataValidation: true);
/// <summary>
@ -131,7 +131,7 @@ namespace Avalonia.Controls
private IDisposable? _textBoxTextChangedSubscription;
private decimal _value;
private decimal? _value;
private string? _text;
private bool _internalValueSet;
private bool _clipValueToMinMax;
@ -277,7 +277,7 @@ namespace Avalonia.Controls
/// <summary>
/// Gets or sets the value.
/// </summary>
public decimal Value
public decimal? Value
{
get { return _value; }
set
@ -351,7 +351,7 @@ namespace Avalonia.Controls
/// <inheritdoc />
protected override void OnLostFocus(RoutedEventArgs e)
{
CommitInput();
CommitInput(true);
base.OnLostFocus(e);
}
@ -489,9 +489,9 @@ namespace Avalonia.Controls
{
SetValidSpinDirection();
}
if (ClipValueToMinMax)
if (ClipValueToMinMax && Value.HasValue)
{
Value = MathUtilities.Clamp(Value, Minimum, Maximum);
Value = MathUtilities.Clamp(Value.Value, Minimum, Maximum);
}
}
@ -506,9 +506,9 @@ namespace Avalonia.Controls
{
SetValidSpinDirection();
}
if (ClipValueToMinMax)
if (ClipValueToMinMax && Value.HasValue)
{
Value = MathUtilities.Clamp(Value, Minimum, Maximum);
Value = MathUtilities.Clamp(Value.Value, Minimum, Maximum);
}
}
@ -530,7 +530,7 @@ namespace Avalonia.Controls
/// </summary>
/// <param name="oldValue">The old value.</param>
/// <param name="newValue">The new value.</param>
protected virtual void OnValueChanged(decimal oldValue, decimal newValue)
protected virtual void OnValueChanged(decimal? oldValue, decimal? newValue)
{
if (!_internalValueSet && IsInitialized)
{
@ -573,7 +573,7 @@ namespace Avalonia.Controls
/// Called when the <see cref="Value"/> property has to be coerced.
/// </summary>
/// <param name="baseValue">The value.</param>
protected virtual decimal OnCoerceValue(decimal baseValue)
protected virtual decimal? OnCoerceValue(decimal? baseValue)
{
return baseValue;
}
@ -607,7 +607,7 @@ namespace Avalonia.Controls
/// </summary>
/// <param name="oldValue">The old value.</param>
/// <param name="newValue">The new value.</param>
protected virtual void RaiseValueChangedEvent(decimal oldValue, decimal newValue)
protected virtual void RaiseValueChangedEvent(decimal? oldValue, decimal? newValue)
{
var e = new NumericUpDownValueChangedEventArgs(ValueChangedEvent, oldValue, newValue);
RaiseEvent(e);
@ -616,9 +616,9 @@ namespace Avalonia.Controls
/// <summary>
/// Converts the formatted text to a value.
/// </summary>
private decimal ConvertTextToValue(string text)
private decimal? ConvertTextToValue(string? text)
{
decimal result = 0;
decimal? result = null;
if (string.IsNullOrEmpty(text))
{
@ -635,9 +635,9 @@ namespace Avalonia.Controls
result = ConvertTextToValueCore(currentValueText, text);
if (ClipValueToMinMax)
if (ClipValueToMinMax && result.HasValue)
{
return MathUtilities.Clamp(result, Minimum, Maximum);
return MathUtilities.Clamp(result.Value, Minimum, Maximum);
}
ValidateMinMax(result);
@ -649,7 +649,7 @@ namespace Avalonia.Controls
/// Converts the value to formatted text.
/// </summary>
/// <returns></returns>
private string ConvertValueToText()
private string? ConvertValueToText()
{
//Manage FormatString of type "{}{0:N2} °" (in xaml) or "{0:N2} °" in code-behind.
if (FormatString.Contains("{0"))
@ -657,7 +657,7 @@ namespace Avalonia.Controls
return string.Format(NumberFormat, FormatString, Value);
}
return Value.ToString(FormatString, NumberFormat);
return Value?.ToString(FormatString, NumberFormat);
}
/// <summary>
@ -665,7 +665,16 @@ namespace Avalonia.Controls
/// </summary>
private void OnIncrement()
{
var result = Value + Increment;
decimal result;
if (Value.HasValue)
{
result = Value.Value + Increment;
}
else
{
result = Minimum;
}
Value = MathUtilities.Clamp(result, Minimum, Maximum);
}
@ -674,7 +683,17 @@ namespace Avalonia.Controls
/// </summary>
private void OnDecrement()
{
var result = Value - Increment;
decimal result;
if (Value.HasValue)
{
result = Value.Value - Increment;
}
else
{
result = Maximum;
}
Value = MathUtilities.Clamp(result, Minimum, Maximum);
}
@ -688,6 +707,11 @@ namespace Avalonia.Controls
// Zero increment always prevents spin.
if (Increment != 0 && !IsReadOnly)
{
if (!Value.HasValue)
{
validDirections = ValidSpinDirections.Increase | ValidSpinDirections.Decrease;
}
if (Value < Maximum)
{
validDirections = validDirections | ValidSpinDirections.Increase;
@ -825,13 +849,13 @@ namespace Avalonia.Controls
{
if (e.Sender is NumericUpDown upDown)
{
var oldValue = (decimal)e.OldValue!;
var newValue = (decimal)e.NewValue!;
var oldValue = (decimal?)e.OldValue;
var newValue = (decimal?)e.NewValue;
upDown.OnValueChanged(oldValue, newValue);
}
}
private void SetValueInternal(decimal value)
private void SetValueInternal(decimal? value)
{
_internalValueSet = true;
try
@ -946,9 +970,9 @@ namespace Avalonia.Controls
remove { RemoveHandler(ValueChangedEvent, value); }
}
private bool CommitInput()
private bool CommitInput(bool forceTextUpdate = false)
{
return SyncTextAndValueProperties(true, Text);
return SyncTextAndValueProperties(true, Text, forceTextUpdate);
}
/// <summary>
@ -978,28 +1002,24 @@ namespace Avalonia.Controls
{
if (updateValueFromText)
{
if (!string.IsNullOrEmpty(text))
try
{
try
var newValue = ConvertTextToValue(text);
if (!Equals(newValue, Value))
{
var newValue = ConvertTextToValue(text);
if (!Equals(newValue, Value))
{
SetValueInternal(newValue);
}
}
catch
{
parsedTextIsValid = false;
SetValueInternal(newValue);
}
}
catch
{
parsedTextIsValid = false;
}
}
// Do not touch the ongoing text input from user.
if (!_isTextChangedFromUI)
{
var keepEmpty = !forceTextUpdate && string.IsNullOrEmpty(Text);
if (!keepEmpty)
if (forceTextUpdate)
{
var newText = ConvertValueToText();
if (!Equals(Text, newText))
@ -1036,10 +1056,15 @@ namespace Avalonia.Controls
return parsedTextIsValid;
}
private decimal ConvertTextToValueCore(string currentValueText, string text)
private decimal? ConvertTextToValueCore(string? currentValueText, string? text)
{
decimal result;
if (string.IsNullOrEmpty(text))
{
return null;
}
if (IsPercent(FormatString))
{
result = ParsePercent(text, NumberFormat);
@ -1052,7 +1077,7 @@ namespace Avalonia.Controls
var shouldThrow = true;
// Check if CurrentValueText is also failing => it also contains special characters. ex : 90°
if (!decimal.TryParse(currentValueText, ParsingNumberStyle, NumberFormat, out var _))
if (!string.IsNullOrEmpty(currentValueText) && !decimal.TryParse(currentValueText, ParsingNumberStyle, NumberFormat, out var _))
{
// extract non-digit characters
var currentValueTextSpecialCharacters = currentValueText.Where(c => !char.IsDigit(c));
@ -1082,8 +1107,12 @@ namespace Avalonia.Controls
return result;
}
private void ValidateMinMax(decimal value)
private void ValidateMinMax(decimal? value)
{
if (!value.HasValue)
{
return;
}
if (value < Minimum)
{
throw new ArgumentOutOfRangeException(nameof(value), string.Format("Value must be greater than Minimum value of {0}", Minimum));

6
src/Avalonia.Controls/NumericUpDown/NumericUpDownValueChangedEventArgs.cs

@ -4,13 +4,13 @@ namespace Avalonia.Controls
{
public class NumericUpDownValueChangedEventArgs : RoutedEventArgs
{
public NumericUpDownValueChangedEventArgs(RoutedEvent routedEvent, decimal oldValue, decimal newValue) : base(routedEvent)
public NumericUpDownValueChangedEventArgs(RoutedEvent routedEvent, decimal? oldValue, decimal? newValue) : base(routedEvent)
{
OldValue = oldValue;
NewValue = newValue;
}
public decimal OldValue { get; }
public decimal NewValue { get; }
public decimal? OldValue { get; }
public decimal? NewValue { get; }
}
}

4
src/Avalonia.Controls/Presenters/CarouselPresenter.cs

@ -31,7 +31,7 @@ namespace Avalonia.Controls.Presenters
/// <summary>
/// Defines the <see cref="PageTransition"/> property.
/// </summary>
public static readonly StyledProperty<IPageTransition> PageTransitionProperty =
public static readonly StyledProperty<IPageTransition?> PageTransitionProperty =
Carousel.PageTransitionProperty.AddOwner<CarouselPresenter>();
private int _selectedIndex = -1;
@ -85,7 +85,7 @@ namespace Avalonia.Controls.Presenters
/// <summary>
/// Gets or sets a transition to use when switching pages.
/// </summary>
public IPageTransition PageTransition
public IPageTransition? PageTransition
{
get { return GetValue(PageTransitionProperty); }
set { SetValue(PageTransitionProperty, value); }

2
src/Avalonia.Themes.Default/SimpleTheme.cs

@ -44,7 +44,7 @@ namespace Avalonia.Themes.Default
InitStyles(_baseUri);
}
public event EventHandler OwnerChanged
public event EventHandler? OwnerChanged
{
add
{

14
src/Avalonia.Themes.Fluent/FluentTheme.cs

@ -50,7 +50,9 @@ namespace Avalonia.Themes.Fluent
/// <param name="serviceProvider">The XAML service provider.</param>
public FluentTheme(IServiceProvider serviceProvider)
{
_baseUri = ((IUriContext)serviceProvider.GetService(typeof(IUriContext))).BaseUri;
var ctx = serviceProvider.GetService(typeof(IUriContext)) as IUriContext
?? throw new NullReferenceException("Unable retrive UriContext");
_baseUri = ctx.BaseUri;
InitStyles(_baseUri);
}
@ -81,6 +83,14 @@ namespace Avalonia.Themes.Fluent
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);
if (_loaded is null)
{
// If style wasn't yet loaded, no need to change children styles,
// it will be applied later in Loaded getter.
return;
}
if (change.Property == ModeProperty)
{
if (Mode == FluentThemeMode.Dark)
@ -146,7 +156,7 @@ namespace Avalonia.Themes.Fluent
IReadOnlyList<IStyle> IStyle.Children => _loaded?.Children ?? Array.Empty<IStyle>();
public event EventHandler OwnerChanged
public event EventHandler? OwnerChanged
{
add
{

1
src/Avalonia.X11/X11Atoms.cs

@ -155,6 +155,7 @@ namespace Avalonia.X11
public readonly IntPtr _NET_FRAME_EXTENTS;
public readonly IntPtr _NET_WM_PING;
public readonly IntPtr _NET_WM_SYNC_REQUEST;
public readonly IntPtr _NET_WM_SYNC_REQUEST_COUNTER;
public readonly IntPtr _NET_SYSTEM_TRAY_S;
public readonly IntPtr _NET_SYSTEM_TRAY_ORIENTATION;
public readonly IntPtr _NET_SYSTEM_TRAY_OPCODE;

10
src/Avalonia.X11/X11Info.cs

@ -33,6 +33,7 @@ namespace Avalonia.X11
public IntPtr LastActivityTimestamp { get; set; }
public XVisualInfo? TransparentVisualInfo { get; set; }
public bool HasXim { get; set; }
public bool HasXSync { get; set; }
public IntPtr DefaultFontSet { get; set; }
public unsafe X11Info(IntPtr display, IntPtr deferredDisplay, bool useXim)
@ -101,6 +102,15 @@ namespace Avalonia.X11
{
//Ignore, XI is not supported
}
try
{
HasXSync = XSyncInitialize(display, out _, out _) != Status.Success;
}
catch
{
//Ignore, XSync is not supported
}
}
}
}

46
src/Avalonia.X11/X11Window.cs

@ -45,6 +45,8 @@ namespace Avalonia.X11
private IntPtr _handle;
private IntPtr _xic;
private IntPtr _renderHandle;
private IntPtr _xSyncCounter;
private XSyncValue _xSyncValue;
private bool _mapped;
private bool _wasMappedAtLeastOnce = false;
private double? _scalingOverride;
@ -190,6 +192,16 @@ namespace Avalonia.X11
NativeMenuExporter = DBusMenuExporter.TryCreateTopLevelNativeMenu(_handle);
NativeControlHost = new X11NativeControlHost(_platform, this);
InitializeIme();
XChangeProperty(_x11.Display, _handle, _x11.Atoms.WM_PROTOCOLS, _x11.Atoms.XA_ATOM, 32,
PropertyMode.Replace, new[] { _x11.Atoms.WM_DELETE_WINDOW, _x11.Atoms._NET_WM_SYNC_REQUEST }, 2);
if (_x11.HasXSync)
{
_xSyncCounter = XSyncCreateCounter(_x11.Display, _xSyncValue);
XChangeProperty(_x11.Display, _handle, _x11.Atoms._NET_WM_SYNC_REQUEST_COUNTER,
_x11.Atoms.XA_CARDINAL, 32, PropertyMode.Replace, ref _xSyncCounter, 1);
}
}
class SurfaceInfo : EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo
@ -383,15 +395,7 @@ namespace Avalonia.X11
(ev.type == XEventName.VisibilityNotify &&
ev.VisibilityEvent.state < 2))
{
if (!_triggeredExpose)
{
_triggeredExpose = true;
Dispatcher.UIThread.Post(() =>
{
_triggeredExpose = false;
DoPaint();
}, DispatcherPriority.Render);
}
EnqueuePaint();
}
else if (ev.type == XEventName.FocusIn)
{
@ -503,6 +507,7 @@ namespace Avalonia.X11
if (_useRenderWindow)
XConfigureResizeWindow(_x11.Display, _renderHandle, ev.ConfigureEvent.width,
ev.ConfigureEvent.height);
EnqueuePaint();
}
else if (ev.type == XEventName.DestroyNotify
&& ev.DestroyWindowEvent.window == _handle)
@ -518,7 +523,11 @@ namespace Avalonia.X11
if (Closing?.Invoke() != true)
Dispose();
}
else if (ev.ClientMessageEvent.ptr1 == _x11.Atoms._NET_WM_SYNC_REQUEST)
{
_xSyncValue.Lo = new UIntPtr(ev.ClientMessageEvent.ptr3.ToPointer()).ToUInt32();
_xSyncValue.Hi = ev.ClientMessageEvent.ptr4.ToInt32();
}
}
}
else if (ev.type == XEventName.KeyPress || ev.type == XEventName.KeyRelease)
@ -730,9 +739,24 @@ namespace Avalonia.X11
ScheduleInput(mev, ref ev);
}
void EnqueuePaint()
{
if (!_triggeredExpose)
{
_triggeredExpose = true;
Dispatcher.UIThread.Post(() =>
{
_triggeredExpose = false;
DoPaint();
}, DispatcherPriority.Render);
}
}
void DoPaint()
{
Paint?.Invoke(new Rect());
if (_xSyncCounter != IntPtr.Zero)
XSyncSetCounter(_x11.Display, _xSyncCounter, _xSyncValue);
}
public void Invalidate(Rect rect)
@ -1160,7 +1184,7 @@ namespace Avalonia.X11
}
public IntPtr Handle => _owner._renderHandle;
public string? HandleDescriptor => "XID";
public string HandleDescriptor => "XID";
}
}
}

17
src/Avalonia.X11/XLib.cs

@ -542,6 +542,18 @@ namespace Avalonia.X11
public static extern int XRRQueryExtension (IntPtr dpy,
out int event_base_return,
out int error_base_return);
[DllImport(libX11Ext)]
public static extern Status XSyncInitialize(IntPtr dpy, out int event_base_return, out int error_base_return);
[DllImport(libX11Ext)]
public static extern IntPtr XSyncCreateCounter(IntPtr dpy, XSyncValue initialValue);
[DllImport(libX11Ext)]
public static extern int XSyncDestroyCounter(IntPtr dpy, IntPtr counter);
[DllImport(libX11Ext)]
public static extern int XSyncSetCounter(IntPtr dpy, IntPtr counter, XSyncValue value);
[DllImport(libX11Randr)]
public static extern int XRRQueryVersion(IntPtr dpy,
@ -627,6 +639,11 @@ namespace Avalonia.X11
public int bw;
public int d;
}
public struct XSyncValue {
public int Hi;
public uint Lo;
}
public static bool XGetGeometry(IntPtr display, IntPtr window, out XGeometry geo)
{

4
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/PropertyInfoAccessorFactory.cs

@ -1,8 +1,6 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Text;
using Avalonia.Data;
using Avalonia.Data.Core;
using Avalonia.Data.Core.Plugins;
@ -174,7 +172,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
WeakEvents.CollectionChanged.Unsubscribe(incc, this);
}
public void OnEvent(object? sender, WeakEvent ev, NotifyCollectionChangedEventArgs args)
public void OnEvent(object sender, WeakEvent ev, NotifyCollectionChangedEventArgs args)
{
if (ShouldNotifyListeners(args))
{

4
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/ResourceInclude.cs

@ -42,7 +42,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
bool IResourceNode.HasResources => Loaded.HasResources;
public event EventHandler OwnerChanged
public event EventHandler? OwnerChanged
{
add => Loaded.OwnerChanged += value;
remove => Loaded.OwnerChanged -= value;
@ -52,7 +52,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
{
if (!_isLoading)
{
return Loaded.TryGetResource(key, out value);
return Loaded.TryGetResource(key, out value);
}
value = null;

2
src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs

@ -64,7 +64,7 @@ namespace Avalonia.Markup.Xaml.Styling
IReadOnlyList<IStyle> IStyle.Children => _loaded ?? Array.Empty<IStyle>();
public event EventHandler OwnerChanged
public event EventHandler? OwnerChanged
{
add
{

20
src/Web/Avalonia.Web.Blazor/AvaloniaView.razor

@ -1,24 +1,26 @@
<div id="container" class="avalonia-container" tabindex="0" oncontextmenu="return false;"
ontouchcancel="@OnTouchCancel"
ontouchmove="@OnTouchMove"
onwheel="@OnWheel"
onkeydown="@OnKeyDown"
onkeyup="@OnKeyUp"
onpointerdown="@OnPointerDown"
onpointerup="@OnPointerUp"
onpointermove="@OnPointerMove">
@onwheel="OnWheel"
@onkeydown="OnKeyDown"
@onkeyup="OnKeyUp"
@onpointerdown="OnPointerDown"
@onpointerup="OnPointerUp"
@onpointermove="OnPointerMove"
@onpointercancel="OnPointerCancel">
<canvas id="htmlCanvas" @ref="_htmlCanvas" @attributes="AdditionalAttributes"/>
<div id="nativeControlsContainer" @ref="_nativeControlsContainer" />
<input id="inputElement" @ref="_inputElement" type="text" oninput="@OnInput"
<input id="inputElement" @ref="_inputElement" type="text" @oninput="OnInput"
onpaste="return false;"
oncopy="return false;"
oncut="return false;"/>
</div>
<style>
#container{
touch-action: none;
}
#htmlCanvas {
opacity: 1;
background-color: #ccc;

38
src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs

@ -57,27 +57,23 @@ namespace Avalonia.Web.Blazor
return _nativeControlHost ?? throw new InvalidOperationException("Blazor View wasn't initialized yet");
}
private void OnTouchCancel(TouchEventArgs e)
private void OnPointerCancel(Microsoft.AspNetCore.Components.Web.PointerEventArgs e)
{
foreach (var touch in e.ChangedTouches)
if (e.PointerType == "touch")
{
_topLevelImpl.RawTouchEvent(RawPointerEventType.TouchCancel, new Point(touch.ClientX, touch.ClientY),
GetModifiers(e), touch.Identifier);
_topLevelImpl.RawTouchEvent(RawPointerEventType.TouchCancel, new Point(e.ClientX, e.ClientY),
GetModifiers(e), e.PointerId);
}
}
private void OnTouchMove(TouchEventArgs e)
private void OnPointerMove(Microsoft.AspNetCore.Components.Web.PointerEventArgs e)
{
foreach (var touch in e.ChangedTouches)
if (e.PointerType == "touch")
{
_topLevelImpl.RawTouchEvent(RawPointerEventType.TouchUpdate, new Point(touch.ClientX, touch.ClientY),
GetModifiers(e), touch.Identifier);
_topLevelImpl.RawTouchEvent(RawPointerEventType.TouchUpdate, new Point(e.ClientX, e.ClientY),
GetModifiers(e), e.PointerId);
}
}
private void OnPointerMove(Microsoft.AspNetCore.Components.Web.PointerEventArgs e)
{
if (e.PointerType != "touch")
else
{
_topLevelImpl.RawMouseEvent(RawPointerEventType.Move, new Point(e.ClientX, e.ClientY), GetModifiers(e));
}
@ -174,22 +170,6 @@ namespace Avalonia.Web.Blazor
return modifiers;
}
private static RawInputModifiers GetModifiers(TouchEventArgs e)
{
var modifiers = RawInputModifiers.None;
if (e.CtrlKey)
modifiers |= RawInputModifiers.Control;
if (e.AltKey)
modifiers |= RawInputModifiers.Alt;
if (e.ShiftKey)
modifiers |= RawInputModifiers.Shift;
if (e.MetaKey)
modifiers |= RawInputModifiers.Meta;
return modifiers;
}
private static RawInputModifiers GetModifiers(Microsoft.AspNetCore.Components.Web.PointerEventArgs e)
{
var modifiers = RawInputModifiers.None;

2
src/Windows/Avalonia.Win32/Automation/RootAutomationNode.cs

@ -42,7 +42,7 @@ namespace Avalonia.Win32.Automation
return GetOrCreate(focus);
}
public void FocusChanged(object sender, EventArgs e)
public void FocusChanged(object? sender, EventArgs e)
{
RaiseFocusChanged(GetOrCreate(Peer.GetFocus()));
}

4
src/Windows/Avalonia.Win32/Avalonia.Win32.csproj

@ -18,4 +18,8 @@
</ItemGroup>
<Import Project="$(MSBuildThisFileDirectory)\..\..\..\build\System.Drawing.Common.props" />
<Import Project="..\..\..\build\DevAnalyzers.props" />
<PropertyGroup Label="Warnings">
<NoWarn Condition="'$(NoWarn)' == ''">CA1416</NoWarn>
<NoWarn Condition="'$(NoWarn)' != ''">$(NoWarn),CA1416</NoWarn>
</PropertyGroup>
</Project>

1
src/Windows/Avalonia.Win32/Interop/Automation/ISelectionItemProvider.cs

@ -1,3 +1,4 @@
#nullable enable
using System;
using System.Runtime.InteropServices;

2
src/Windows/Avalonia.Win32/TrayIconImpl.cs

@ -195,7 +195,7 @@ namespace Avalonia.Win32
ShowActivated = true;
}
private void TrayPopupRoot_Deactivated(object sender, EventArgs e)
private void TrayPopupRoot_Deactivated(object? sender, EventArgs e)
{
Close();
}

2
src/Windows/Avalonia.Win32/WindowImpl.CustomCaptionProc.cs

@ -109,7 +109,7 @@ namespace Avalonia.Win32
if (_owner is Window window)
{
var visual = window.Renderer.HitTestFirst(position, _owner as Window, x =>
var visual = window.Renderer.HitTestFirst(position, _owner, x =>
{
if (x is IInputElement ie && (!ie.IsHitTestVisible || !ie.IsVisible))
{

10
tests/Avalonia.Base.UnitTests/Media/MatrixTests.cs

@ -30,6 +30,16 @@ namespace Avalonia.Base.UnitTests.Media
Assert.True(matrix.HasInverse);
}
[Fact]
public void Invert_Should_Work()
{
var matrix = new Matrix(1, 2, 3, 0, 1, 4, 5, 6, 0);
var inverted = matrix.Invert();
Assert.Equal(matrix * inverted, Matrix.Identity);
Assert.Equal(inverted * matrix, Matrix.Identity);
}
[Fact]
public void Can_Decompose_Translation()
{

3
tests/Avalonia.Benchmarks/TestBindingObservable.cs

@ -1,4 +1,5 @@
using System;
#nullable enable
using System;
using Avalonia.Data;
namespace Avalonia.Benchmarks

74
tests/Avalonia.Controls.UnitTests/ButtonTests.cs

@ -309,6 +309,80 @@ namespace Avalonia.Controls.UnitTests
Assert.Equal(0, raised);
}
[Fact]
public void Button_IsDefault_Works()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var raised = 0;
var target = new Button();
var window = new Window { Content = target };
window.Show();
target.Click += (s, e) => ++raised;
target.IsDefault = false;
window.RaiseEvent(CreateKeyDownEvent(Key.Enter));
Assert.Equal(0, raised);
target.IsDefault = true;
window.RaiseEvent(CreateKeyDownEvent(Key.Enter));
Assert.Equal(1, raised);
target.IsDefault = false;
window.RaiseEvent(CreateKeyDownEvent(Key.Enter));
Assert.Equal(1, raised);
target.IsDefault = true;
window.RaiseEvent(CreateKeyDownEvent(Key.Enter));
Assert.Equal(2, raised);
window.Content = null;
// To check if handler was raised on the button, when it's detached, we need to pass it as a source manually.
window.RaiseEvent(CreateKeyDownEvent(Key.Enter, target));
Assert.Equal(2, raised);
}
}
[Fact]
public void Button_IsCancel_Works()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var raised = 0;
var target = new Button();
var window = new Window { Content = target };
window.Show();
target.Click += (s, e) => ++raised;
target.IsCancel = false;
window.RaiseEvent(CreateKeyDownEvent(Key.Escape));
Assert.Equal(0, raised);
target.IsCancel = true;
window.RaiseEvent(CreateKeyDownEvent(Key.Escape));
Assert.Equal(1, raised);
target.IsCancel = false;
window.RaiseEvent(CreateKeyDownEvent(Key.Escape));
Assert.Equal(1, raised);
target.IsCancel = true;
window.RaiseEvent(CreateKeyDownEvent(Key.Escape));
Assert.Equal(2, raised);
window.Content = null;
window.RaiseEvent(CreateKeyDownEvent(Key.Escape, target));
Assert.Equal(2, raised);
}
}
private KeyEventArgs CreateKeyDownEvent(Key key, IInteractive source = null)
{
return new KeyEventArgs { RoutedEvent = InputElement.KeyDownEvent, Key = key, Source = source };
}
private class TestButton : Button, IRenderRoot
{

52
tests/Avalonia.RenderTests/Media/GeometryDrawingTests.cs

@ -0,0 +1,52 @@
using Avalonia.Media;
using Xunit;
#if AVALONIA_SKIA
namespace Avalonia.Skia.RenderTests
#else
using Avalonia.Direct2D1.RenderTests;
namespace Avalonia.Direct2D1.RenderTests.Media
#endif
{
public class GeometryDrawingTests : TestBase
{
public GeometryDrawingTests()
: base(@"Media\GeometryDrawing")
{
}
private GeometryDrawing CreateGeometryDrawing()
{
GeometryDrawing geometryDrawing = new GeometryDrawing();
EllipseGeometry ellipse = new EllipseGeometry();
ellipse.RadiusX = 100;
ellipse.RadiusY = 100;
geometryDrawing.Geometry = ellipse;
return geometryDrawing;
}
[Fact]
public void DrawingGeometry_WithPen()
{
GeometryDrawing geometryDrawing = CreateGeometryDrawing();
geometryDrawing.Pen = new Pen(new SolidColorBrush(Color.FromArgb(255, 0, 0, 0)), 10);
Assert.Equal(210, geometryDrawing.GetBounds().Height);
Assert.Equal(210, geometryDrawing.GetBounds().Width);
}
[Fact]
public void DrawingGeometry_WithoutPen()
{
GeometryDrawing geometryDrawing = CreateGeometryDrawing();
Assert.Equal(200, geometryDrawing.GetBounds().Height);
Assert.Equal(200, geometryDrawing.GetBounds().Width);
}
}
}

4
tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs

@ -602,7 +602,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
private class EndOfLineTextSource : ITextSource
{
public TextRun? GetTextRun(int textSourceIndex)
public TextRun GetTextRun(int textSourceIndex)
{
return new TextEndOfLine();
}
@ -617,7 +617,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
_text = text;
}
public TextRun? GetTextRun(int textSourceIndex)
public TextRun GetTextRun(int textSourceIndex)
{
if (textSourceIndex >= _text.Length + TextRun.DefaultTextSourceLength + _text.Length)
{

4
tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs

@ -639,7 +639,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
{
const string Text = "_A_A";
public TextRun? GetTextRun(int textSourceIndex)
public TextRun GetTextRun(int textSourceIndex)
{
switch (textSourceIndex)
{
@ -907,7 +907,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
_textRuns = textRuns;
}
public TextRun? GetTextRun(int textSourceIndex)
public TextRun GetTextRun(int textSourceIndex)
{
var currentPosition = 0;

Loading…
Cancel
Save