From 011cb37712f897f11113383eb85681e879d92ba4 Mon Sep 17 00:00:00 2001 From: robloo Date: Sat, 20 Aug 2022 22:05:40 -0400 Subject: [PATCH 01/30] Update DatePicker template part names --- .../DateTimePickers/DatePicker.cs | 18 +++++++++--------- .../Controls/DatePicker.xaml | 18 +++++++++--------- .../Controls/DatePicker.xaml | 18 +++++++++--------- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/Avalonia.Controls/DateTimePickers/DatePicker.cs b/src/Avalonia.Controls/DateTimePickers/DatePicker.cs index f2b808fe0d..4b8fbaec14 100644 --- a/src/Avalonia.Controls/DateTimePickers/DatePicker.cs +++ b/src/Avalonia.Controls/DateTimePickers/DatePicker.cs @@ -14,15 +14,15 @@ namespace Avalonia.Controls /// /// A control to allow the user to select a date /// - [TemplatePart("ButtonContentGrid", typeof(Grid))] - [TemplatePart("DayText", typeof(TextBlock))] - [TemplatePart("FirstSpacer", typeof(Rectangle))] - [TemplatePart("FlyoutButton", typeof(Button))] - [TemplatePart("MonthText", typeof(TextBlock))] - [TemplatePart("PickerPresenter", typeof(DatePickerPresenter))] - [TemplatePart("Popup", typeof(Popup))] - [TemplatePart("SecondSpacer", typeof(Rectangle))] - [TemplatePart("YearText", typeof(TextBlock))] + [TemplatePart("PART_ButtonContentGrid", typeof(Grid))] + [TemplatePart("PART_DayText", typeof(TextBlock))] + [TemplatePart("PART_FirstSpacer", typeof(Rectangle))] + [TemplatePart("PART_FlyoutButton", typeof(Button))] + [TemplatePart("PART_MonthText", typeof(TextBlock))] + [TemplatePart("PART_PickerPresenter", typeof(DatePickerPresenter))] + [TemplatePart("PART_Popup", typeof(Popup))] + [TemplatePart("PART_SecondSpacer", typeof(Rectangle))] + [TemplatePart("PART_YearText", typeof(TextBlock))] [PseudoClasses(":hasnodate")] public class DatePicker : TemplatedControl { diff --git a/src/Avalonia.Themes.Fluent/Controls/DatePicker.xaml b/src/Avalonia.Themes.Fluent/Controls/DatePicker.xaml index bc47f3892e..c5afa3dfca 100644 --- a/src/Avalonia.Themes.Fluent/Controls/DatePicker.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/DatePicker.xaml @@ -101,7 +101,7 @@ HorizontalAlignment="Stretch" VerticalAlignment="Top"/> - - - + diff --git a/src/Avalonia.Themes.Simple/Controls/DatePicker.xaml b/src/Avalonia.Themes.Simple/Controls/DatePicker.xaml index a6da2ad9f8..f7310eba64 100644 --- a/src/Avalonia.Themes.Simple/Controls/DatePicker.xaml +++ b/src/Avalonia.Themes.Simple/Controls/DatePicker.xaml @@ -102,7 +102,7 @@ Content="{TemplateBinding Header}" ContentTemplate="{TemplateBinding HeaderTemplate}" /> - - - + From 66baf9c03835617f0e10dc8581bf870dfc872303 Mon Sep 17 00:00:00 2001 From: robloo Date: Sat, 20 Aug 2022 22:47:54 -0400 Subject: [PATCH 02/30] Update Date/TimePicker template part names --- .../DateTimePickers/DatePicker.cs | 18 ++--- .../DateTimePickers/DatePickerPresenter.cs | 68 +++++++++---------- .../DateTimePickers/TimePicker.cs | 48 ++++++------- .../DateTimePickers/TimePickerPresenter.cs | 56 +++++++-------- .../Controls/DatePicker.xaml | 38 +++++------ .../Controls/TimePicker.xaml | 60 ++++++++-------- .../Controls/DatePicker.xaml | 34 +++++----- .../Controls/TimePicker.xaml | 58 ++++++++-------- 8 files changed, 190 insertions(+), 190 deletions(-) diff --git a/src/Avalonia.Controls/DateTimePickers/DatePicker.cs b/src/Avalonia.Controls/DateTimePickers/DatePicker.cs index 4b8fbaec14..db59dbdb4a 100644 --- a/src/Avalonia.Controls/DateTimePickers/DatePicker.cs +++ b/src/Avalonia.Controls/DateTimePickers/DatePicker.cs @@ -280,15 +280,15 @@ namespace Avalonia.Controls } base.OnApplyTemplate(e); - _flyoutButton = e.NameScope.Find - - - + @@ -216,42 +216,42 @@ Padding="{DynamicResource DateTimeFlyoutBorderPadding}" MaxHeight="398"> - + - + - - - + + - + - - - + + - + - - - + + - - - - - - - + @@ -214,50 +214,50 @@ CornerRadius="{TemplateBinding CornerRadius}"> - + - - - - - - - - - - - - @@ -272,12 +272,12 @@ Color="{DynamicResource ThemeAccentColor}" /> - - - - - - +"; + var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); + var button = (Button)window.Content; + + window.ApplyTemplate(); + button.ApplyTemplate(); + var listBoxHierarchyLine = button.GetVisualChildren().ElementAt(0) as ListBoxHierachyLine; + Assert.Equal(1, listBoxHierarchyLine.LineDashStyle.Offset); + Assert.Equal(2, listBoxHierarchyLine.LineDashStyle.Dashes.Count); + Assert.Equal(2, listBoxHierarchyLine.LineDashStyle.Dashes[0]); + Assert.Equal(2, listBoxHierarchyLine.LineDashStyle.Dashes[1]); + } + + } + [Fact] public void Inline_ControlTemplate_Styled_Values_Are_Set_With_Style_Priority() { @@ -270,4 +309,15 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml Assert.Equal("Bar", bar.Name); } } + public class ListBoxHierachyLine : Panel + { + public static readonly StyledProperty LineDashStyleProperty = + AvaloniaProperty.Register(nameof(LineDashStyle)); + + public DashStyle LineDashStyle + { + get => GetValue(LineDashStyleProperty); + set => SetValue(LineDashStyleProperty, value); + } + } } From 99e02059a8c84af73c1d8e46f08e5a4d0c3a2ebb Mon Sep 17 00:00:00 2001 From: Takoooooo Date: Mon, 5 Sep 2022 13:14:38 +0300 Subject: [PATCH 20/30] Fix Test. --- tests/Avalonia.Base.UnitTests/Media/PenTests.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/Avalonia.Base.UnitTests/Media/PenTests.cs b/tests/Avalonia.Base.UnitTests/Media/PenTests.cs index fecdd30834..6016978fde 100644 --- a/tests/Avalonia.Base.UnitTests/Media/PenTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/PenTests.cs @@ -53,7 +53,10 @@ namespace Avalonia.Base.UnitTests.Media var raised = false; target.Invalidated += (s, e) => raised = true; - dashes.Dashes.Add(0.3); + dashes.Dashes = new AvaloniaList + { + 0.3 + }; Assert.True(raised); } From e64a5e3b0f9d8ebfdc1158b8d3486323e47c3d81 Mon Sep 17 00:00:00 2001 From: affederaffe <68356204+affederaffe@users.noreply.github.com> Date: Mon, 5 Sep 2022 20:35:13 +0200 Subject: [PATCH 21/30] Fix DBusSystemDialog when path contains spaces --- src/Avalonia.FreeDesktop/DBusSystemDialog.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.FreeDesktop/DBusSystemDialog.cs b/src/Avalonia.FreeDesktop/DBusSystemDialog.cs index 7974069184..8597f3922a 100644 --- a/src/Avalonia.FreeDesktop/DBusSystemDialog.cs +++ b/src/Avalonia.FreeDesktop/DBusSystemDialog.cs @@ -72,7 +72,7 @@ namespace Avalonia.FreeDesktop using var disposable = await request.WatchResponseAsync(x => tsc.SetResult(x.results["uris"] as string[]), tsc.SetException); var uris = await tsc.Task ?? Array.Empty(); - return uris.Select(path => new BclStorageFile(new FileInfo(new Uri(path).AbsolutePath))).ToList(); + return uris.Select(path => new BclStorageFile(new FileInfo(new Uri(path).LocalPath))).ToList(); } public override async Task SaveFilePickerAsync(FilePickerSaveOptions options) @@ -96,7 +96,7 @@ namespace Avalonia.FreeDesktop var tsc = new TaskCompletionSource(); using var disposable = await request.WatchResponseAsync(x => tsc.SetResult(x.results["uris"] as string[]), tsc.SetException); var uris = await tsc.Task; - var path = uris?.FirstOrDefault() is { } filePath ? new Uri(filePath).AbsolutePath : null; + var path = uris?.FirstOrDefault() is { } filePath ? new Uri(filePath).LocalPath : null; if (path is null) { @@ -126,7 +126,7 @@ namespace Avalonia.FreeDesktop var uris = await tsc.Task ?? Array.Empty(); return uris - .Select(path => new Uri(path).AbsolutePath) + .Select(path => new Uri(path).LocalPath) // WSL2 freedesktop allows to select files as well in directory picker, filter it out. .Where(Directory.Exists) .Select(path => new BclStorageFolder(new DirectoryInfo(path))).ToList(); From 019497c5d44a9a5abe85aff60527b74e71fcf02b Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Wed, 7 Sep 2022 12:18:57 +0200 Subject: [PATCH 22/30] fix(DevGenerators): Warning RS1024 Use 'SymbolEqualityComparer' when comparing symbols --- src/tools/DevGenerators/EnumMemberDictionaryGenerator.cs | 4 ++-- src/tools/DevGenerators/GetProcAddressInitialization.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tools/DevGenerators/EnumMemberDictionaryGenerator.cs b/src/tools/DevGenerators/EnumMemberDictionaryGenerator.cs index 42612c7ce0..86dbb3a452 100644 --- a/src/tools/DevGenerators/EnumMemberDictionaryGenerator.cs +++ b/src/tools/DevGenerators/EnumMemberDictionaryGenerator.cs @@ -32,7 +32,7 @@ public class EnumMemberDictionaryGenerator : IIncrementalGenerator ).Collect(); context.RegisterSourceOutput(all, static (context, methods) => { - foreach (var typeGroup in methods.GroupBy(f => f.ContainingType)) + foreach (var typeGroup in methods.GroupBy(f => f.ContainingType, SymbolEqualityComparer.Default)) { var classBuilder = new StringBuilder(); if (typeGroup.Key.ContainingNamespace != null) @@ -91,4 +91,4 @@ public class EnumMemberDictionaryGenerator : IIncrementalGenerator } -} \ No newline at end of file +} diff --git a/src/tools/DevGenerators/GetProcAddressInitialization.cs b/src/tools/DevGenerators/GetProcAddressInitialization.cs index 64698d1f2e..c7d03a9b50 100644 --- a/src/tools/DevGenerators/GetProcAddressInitialization.cs +++ b/src/tools/DevGenerators/GetProcAddressInitialization.cs @@ -34,7 +34,7 @@ public class GetProcAddressInitializationGenerator : IIncrementalGenerator var all = fieldsWithAttribute.Collect(); context.RegisterSourceOutput(all, static (context, methods) => { - foreach (var typeGroup in methods.GroupBy(f => f.ContainingType)) + foreach (var typeGroup in methods.GroupBy(f => f.ContainingType, SymbolEqualityComparer.Default)) { var nextContext = 0; var contexts = new Dictionary(); @@ -335,4 +335,4 @@ public class GetProcAddressInitializationGenerator : IIncrementalGenerator return name.ToString(); } -} \ No newline at end of file +} From 9545bbd6b08f38abdf2c9a877b4e0a2145b990d9 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Wed, 7 Sep 2022 15:22:51 +0200 Subject: [PATCH 23/30] fix(DevGenerators): Some null annotation --- .../CompositionRoslynGenerator.cs | 7 +++-- .../CompositionGenerator/Extensions.cs | 30 +++++++++---------- .../CompositionGenerator/Generator.Utils.cs | 4 +-- .../CompositionGenerator/Generator.cs | 16 +++++----- src/tools/DevGenerators/DevGenerators.csproj | 1 + .../GetProcAddressInitialization.cs | 11 ++++--- 6 files changed, 36 insertions(+), 33 deletions(-) diff --git a/src/tools/DevGenerators/CompositionGenerator/CompositionRoslynGenerator.cs b/src/tools/DevGenerators/CompositionGenerator/CompositionRoslynGenerator.cs index 72311b4d18..26bd4796df 100644 --- a/src/tools/DevGenerators/CompositionGenerator/CompositionRoslynGenerator.cs +++ b/src/tools/DevGenerators/CompositionGenerator/CompositionRoslynGenerator.cs @@ -11,8 +11,9 @@ namespace Avalonia.SourceGenerator.CompositionGenerator { var schema = context.AdditionalTextsProvider.Where(static file => file.Path.EndsWith("composition-schema.xml")); - var configs = schema.Select((t, _) => - (GConfig)new XmlSerializer(typeof(GConfig)).Deserialize(new StringReader(t.GetText().ToString()))); + var configs = schema.Select((t, _) => t.GetText()) + .Where(source => source is not null) + .Select((source, _) => (GConfig)new XmlSerializer(typeof(GConfig)).Deserialize(new StringReader(source!.ToString()))); context.RegisterSourceOutput(configs, (spc, config) => { var generator = new Generator(new RoslynCompositionGeneratorSink(spc), config); @@ -20,4 +21,4 @@ namespace Avalonia.SourceGenerator.CompositionGenerator }); } } -} \ No newline at end of file +} diff --git a/src/tools/DevGenerators/CompositionGenerator/Extensions.cs b/src/tools/DevGenerators/CompositionGenerator/Extensions.cs index d88e9b4600..f34765ec36 100644 --- a/src/tools/DevGenerators/CompositionGenerator/Extensions.cs +++ b/src/tools/DevGenerators/CompositionGenerator/Extensions.cs @@ -7,49 +7,47 @@ namespace Avalonia.SourceGenerator.CompositionGenerator { public static class Extensions { - public static ClassDeclarationSyntax AddModifiers(this ClassDeclarationSyntax cl, params SyntaxKind[] modifiers) + public static ClassDeclarationSyntax AddModifiers(this ClassDeclarationSyntax cl, params SyntaxKind[]? modifiers) { - if (modifiers == null) + if (modifiers is null) return cl; return cl.AddModifiers(modifiers.Select(x => SyntaxFactory.Token(x)).ToArray()); } - public static MethodDeclarationSyntax AddModifiers(this MethodDeclarationSyntax cl, params SyntaxKind[] modifiers) + public static MethodDeclarationSyntax AddModifiers(this MethodDeclarationSyntax cl, params SyntaxKind[]? modifiers) { - if (modifiers == null) + if (modifiers is null) return cl; return cl.AddModifiers(modifiers.Select(x => SyntaxFactory.Token(x)).ToArray()); } - public static PropertyDeclarationSyntax AddModifiers(this PropertyDeclarationSyntax cl, params SyntaxKind[] modifiers) + public static PropertyDeclarationSyntax AddModifiers(this PropertyDeclarationSyntax cl, params SyntaxKind[]? modifiers) { - if (modifiers == null) + if (modifiers is null) return cl; return cl.AddModifiers(modifiers.Select(x => SyntaxFactory.Token(x)).ToArray()); } - public static ConstructorDeclarationSyntax AddModifiers(this ConstructorDeclarationSyntax cl, params SyntaxKind[] modifiers) + public static ConstructorDeclarationSyntax AddModifiers(this ConstructorDeclarationSyntax cl, params SyntaxKind[]? modifiers) { - if (modifiers == null) + if (modifiers is null) return cl; return cl.AddModifiers(modifiers.Select(x => SyntaxFactory.Token(x)).ToArray()); } - public static AccessorDeclarationSyntax AddModifiers(this AccessorDeclarationSyntax cl, params SyntaxKind[] modifiers) + public static AccessorDeclarationSyntax AddModifiers(this AccessorDeclarationSyntax cl, params SyntaxKind[]? modifiers) { - if (modifiers == null) + if (modifiers is null) return cl; return cl.AddModifiers(modifiers.Select(x => SyntaxFactory.Token(x)).ToArray()); } - public static EnumDeclarationSyntax AddModifiers(this EnumDeclarationSyntax cl, params SyntaxKind[] modifiers) + public static EnumDeclarationSyntax AddModifiers(this EnumDeclarationSyntax cl, params SyntaxKind[]? modifiers) { - if (modifiers == null) + if (modifiers is null) return cl; return cl.AddModifiers(modifiers.Select(x => SyntaxFactory.Token(x)).ToArray()); } - - public static string WithLowerFirst(this string s) { @@ -58,7 +56,7 @@ namespace Avalonia.SourceGenerator.CompositionGenerator return char.ToLowerInvariant(s[0]) + s.Substring(1); } - public static ExpressionSyntax MemberAccess(params string[] identifiers) + public static ExpressionSyntax MemberAccess(params string[]? identifiers) { if (identifiers == null || identifiers.Length == 0) throw new ArgumentException(); @@ -96,4 +94,4 @@ namespace Avalonia.SourceGenerator.CompositionGenerator ? s.Substring(prefix.Length) : s; } -} \ No newline at end of file +} diff --git a/src/tools/DevGenerators/CompositionGenerator/Generator.Utils.cs b/src/tools/DevGenerators/CompositionGenerator/Generator.Utils.cs index b53c247991..16aa15d95a 100644 --- a/src/tools/DevGenerators/CompositionGenerator/Generator.Utils.cs +++ b/src/tools/DevGenerators/CompositionGenerator/Generator.Utils.cs @@ -54,7 +54,7 @@ namespace Avalonia.SourceGenerator.CompositionGenerator FieldDeclarationSyntax DeclareField(string type, string name, params SyntaxKind[] modifiers) => DeclareField(type, name, null, modifiers); - FieldDeclarationSyntax DeclareField(string type, string name, EqualsValueClauseSyntax initializer, + FieldDeclarationSyntax DeclareField(string type, string name, EqualsValueClauseSyntax? initializer, params SyntaxKind[] modifiers) => FieldDeclaration( VariableDeclaration(ParseTypeName(type), @@ -63,4 +63,4 @@ namespace Avalonia.SourceGenerator.CompositionGenerator .WithSemicolonToken(Semicolon()) .WithModifiers(TokenList(modifiers.Select(x => Token(x)))); } -} \ No newline at end of file +} diff --git a/src/tools/DevGenerators/CompositionGenerator/Generator.cs b/src/tools/DevGenerators/CompositionGenerator/Generator.cs index 18f1d1c1e5..cbcc55ada0 100644 --- a/src/tools/DevGenerators/CompositionGenerator/Generator.cs +++ b/src/tools/DevGenerators/CompositionGenerator/Generator.cs @@ -37,8 +37,8 @@ namespace Avalonia.SourceGenerator.CompositionGenerator - string ServerName(string c) => c != null ? ("Server" + c) : "ServerObject"; - string ChangesName(string c) => c != null ? (c + "Changes") : "ChangeSet"; + string ServerName(string? c) => c != null ? ("Server" + c) : "ServerObject"; + string ChangesName(string? c) => c != null ? (c + "Changes") : "ChangeSet"; string ChangedFieldsTypeName(GClass c) => c.Name + "ChangedFields"; string ChangedFieldsFieldName(GClass c) => "_changedFieldsOf" + c.Name; string PropertyBackingFieldName(GProperty prop) => "_" + prop.Name.WithLowerFirst(); @@ -298,10 +298,10 @@ namespace Avalonia.SourceGenerator.CompositionGenerator if(cl.Implements.Count > 0) foreach (var impl in cl.Implements) { - client = client.WithBaseList(client.BaseList.AddTypes(SimpleBaseType(ParseTypeName(impl.Name)))); + client = client.WithBaseList(client.BaseList?.AddTypes(SimpleBaseType(ParseTypeName(impl.Name)))); if (impl.ServerName != null) server = server.WithBaseList( - server.BaseList.AddTypes(SimpleBaseType(ParseTypeName(impl.ServerName)))); + server.BaseList?.AddTypes(SimpleBaseType(ParseTypeName(impl.ServerName)))); client = client.AddMembers( ParseMemberDeclaration($"{impl.ServerName} {impl.Name}.Server => Server;")); @@ -527,7 +527,7 @@ var changed = reader.Read<{ChangedFieldsTypeName(cl)}>(); body = body.AddStatements( ParseStatement("return base.GetPropertyForAnimation(name);")); var method = ((MethodDeclarationSyntax) ParseMemberDeclaration( - $"public override Avalonia.Rendering.Composition.Expressions.ExpressionVariant GetPropertyForAnimation(string name){{}}")) + $"public override Avalonia.Rendering.Composition.Expressions.ExpressionVariant GetPropertyForAnimation(string name){{}}")!) .WithBody(body); return cl.AddMembers(method); @@ -540,7 +540,7 @@ var changed = reader.Read<{ChangedFieldsTypeName(cl)}>(); body = body.AddStatements( ParseStatement("return base.GetCompositionProperty(name);")); var method = ((MethodDeclarationSyntax)ParseMemberDeclaration( - $"public override CompositionProperty? GetCompositionProperty(string name){{}}")) + $"public override CompositionProperty? GetCompositionProperty(string name){{}}")!) .WithBody(body); return cl.AddMembers(method); @@ -559,11 +559,11 @@ var changed = reader.Read<{ChangedFieldsTypeName(cl)}>(); ); return cl.AddMembers( ((MethodDeclarationSyntax) ParseMemberDeclaration( - "internal override void StartAnimation(string propertyName, CompositionAnimation animation, Avalonia.Rendering.Composition.Expressions.ExpressionVariant? finalValue){}")) + "internal override void StartAnimation(string propertyName, CompositionAnimation animation, Avalonia.Rendering.Composition.Expressions.ExpressionVariant? finalValue){}")!) .WithBody(body)); } } -} \ No newline at end of file +} diff --git a/src/tools/DevGenerators/DevGenerators.csproj b/src/tools/DevGenerators/DevGenerators.csproj index 30da940514..069ff159fc 100644 --- a/src/tools/DevGenerators/DevGenerators.csproj +++ b/src/tools/DevGenerators/DevGenerators.csproj @@ -4,6 +4,7 @@ netstandard2.0 enable false + 10 diff --git a/src/tools/DevGenerators/GetProcAddressInitialization.cs b/src/tools/DevGenerators/GetProcAddressInitialization.cs index c7d03a9b50..aedc13e7f6 100644 --- a/src/tools/DevGenerators/GetProcAddressInitialization.cs +++ b/src/tools/DevGenerators/GetProcAddressInitialization.cs @@ -131,10 +131,13 @@ public class GetProcAddressInitializationGenerator : IIncrementalGenerator .Append(".GetProcAddress(") .Append("getProcAddress, ") .Append(contextName); - - var syntaxNode = (AttributeSyntax)attr.ApplicationSyntaxReference.GetSyntax(); - foreach (var arg in syntaxNode.ArgumentList.Arguments) - initializeBody.Append(", ").Append(arg.GetText()); + + if (attr.ApplicationSyntaxReference?.GetSyntax() is AttributeSyntax syntaxNode + && syntaxNode.ArgumentList is { }) + { + foreach (var arg in syntaxNode.ArgumentList.Arguments) + initializeBody.Append(", ").Append(arg.GetText()); + } initializeBody.AppendLine(");"); } } From 5501010f2dab979366a3a24a22cfd514d399d360 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 7 Sep 2022 15:17:04 +0100 Subject: [PATCH 24/30] remove wwwroot --- .../wwwroot/Avalonia/Avalonia.js | 6 - .../wwwroot/Avalonia/DpiWatcher.js | 26 --- .../wwwroot/Avalonia/InputHelper.js | 19 -- .../wwwroot/Avalonia/NativeControlHost.js | 48 ----- .../wwwroot/Avalonia/SKHtmlCanvas.js | 172 --------------- .../wwwroot/Avalonia/SizeWatcher.js | 39 ---- .../wwwroot/Storage/IndexedDbWrapper.js | 72 ------- .../wwwroot/Storage/StorageProvider.js | 199 ------------------ 8 files changed, 581 deletions(-) delete mode 100644 src/Web/Avalonia.Web.Blazor/wwwroot/Avalonia/Avalonia.js delete mode 100644 src/Web/Avalonia.Web.Blazor/wwwroot/Avalonia/DpiWatcher.js delete mode 100644 src/Web/Avalonia.Web.Blazor/wwwroot/Avalonia/InputHelper.js delete mode 100644 src/Web/Avalonia.Web.Blazor/wwwroot/Avalonia/NativeControlHost.js delete mode 100644 src/Web/Avalonia.Web.Blazor/wwwroot/Avalonia/SKHtmlCanvas.js delete mode 100644 src/Web/Avalonia.Web.Blazor/wwwroot/Avalonia/SizeWatcher.js delete mode 100644 src/Web/Avalonia.Web.Blazor/wwwroot/Storage/IndexedDbWrapper.js delete mode 100644 src/Web/Avalonia.Web.Blazor/wwwroot/Storage/StorageProvider.js diff --git a/src/Web/Avalonia.Web.Blazor/wwwroot/Avalonia/Avalonia.js b/src/Web/Avalonia.Web.Blazor/wwwroot/Avalonia/Avalonia.js deleted file mode 100644 index 849ea089d5..0000000000 --- a/src/Web/Avalonia.Web.Blazor/wwwroot/Avalonia/Avalonia.js +++ /dev/null @@ -1,6 +0,0 @@ -export { DpiWatcher } from "./DpiWatcher"; -export { InputHelper } from "./InputHelper"; -export { NativeControlHost } from "./NativeControlHost"; -export { SizeWatcher } from "./SizeWatcher"; -export { SKHtmlCanvas } from "./SKHtmlCanvas"; -//# sourceMappingURL=Avalonia.js.map \ No newline at end of file diff --git a/src/Web/Avalonia.Web.Blazor/wwwroot/Avalonia/DpiWatcher.js b/src/Web/Avalonia.Web.Blazor/wwwroot/Avalonia/DpiWatcher.js deleted file mode 100644 index 17b6999108..0000000000 --- a/src/Web/Avalonia.Web.Blazor/wwwroot/Avalonia/DpiWatcher.js +++ /dev/null @@ -1,26 +0,0 @@ -export class DpiWatcher { - static getDpi() { - return window.devicePixelRatio; - } - static start(callback) { - DpiWatcher.lastDpi = window.devicePixelRatio; - DpiWatcher.timerId = window.setInterval(DpiWatcher.update, 1000); - DpiWatcher.callback = callback; - return DpiWatcher.lastDpi; - } - static stop() { - window.clearInterval(DpiWatcher.timerId); - DpiWatcher.callback = undefined; - } - static update() { - if (!DpiWatcher.callback) - return; - const currentDpi = window.devicePixelRatio; - const lastDpi = DpiWatcher.lastDpi; - DpiWatcher.lastDpi = currentDpi; - if (Math.abs(lastDpi - currentDpi) > 0.001) { - DpiWatcher.callback.invokeMethod('Invoke', lastDpi, currentDpi); - } - } -} -//# sourceMappingURL=DpiWatcher.js.map \ No newline at end of file diff --git a/src/Web/Avalonia.Web.Blazor/wwwroot/Avalonia/InputHelper.js b/src/Web/Avalonia.Web.Blazor/wwwroot/Avalonia/InputHelper.js deleted file mode 100644 index 97a4500a00..0000000000 --- a/src/Web/Avalonia.Web.Blazor/wwwroot/Avalonia/InputHelper.js +++ /dev/null @@ -1,19 +0,0 @@ -export class InputHelper { - static clear(inputElement) { - inputElement.value = ""; - } - static focus(inputElement) { - inputElement.focus(); - inputElement.setSelectionRange(0, 0); - } - static setCursor(inputElement, kind) { - inputElement.style.cursor = kind; - } - static hide(inputElement) { - inputElement.style.display = 'none'; - } - static show(inputElement) { - inputElement.style.display = 'block'; - } -} -//# sourceMappingURL=InputHelper.js.map \ No newline at end of file diff --git a/src/Web/Avalonia.Web.Blazor/wwwroot/Avalonia/NativeControlHost.js b/src/Web/Avalonia.Web.Blazor/wwwroot/Avalonia/NativeControlHost.js deleted file mode 100644 index 05f804ed8c..0000000000 --- a/src/Web/Avalonia.Web.Blazor/wwwroot/Avalonia/NativeControlHost.js +++ /dev/null @@ -1,48 +0,0 @@ -export class NativeControlHost { - static CreateDefaultChild(parent) { - return document.createElement("div"); - } - static GetReference(element) { - return element; - } - static CreateAttachment() { - return new NativeControlHostTopLevelAttachment(); - } -} -class NativeControlHostTopLevelAttachment { - InitializeWithChildHandle(child) { - this._child = child; - this._child.style.position = "absolute"; - } - AttachTo(host) { - if (this._host && this._child) { - this._host.removeChild(this._child); - } - this._host = host; - if (this._host && this._child) { - this._host.appendChild(this._child); - } - } - ShowInBounds(x, y, width, height) { - if (this._child) { - this._child.style.top = y + "px"; - this._child.style.left = x + "px"; - this._child.style.width = width + "px"; - this._child.style.height = height + "px"; - this._child.style.display = "block"; - } - } - HideWithSize(width, height) { - if (this._child) { - this._child.style.width = width + "px"; - this._child.style.height = height + "px"; - this._child.style.display = "none"; - } - } - ReleaseChild() { - if (this._child) { - this._child = undefined; - } - } -} -//# sourceMappingURL=NativeControlHost.js.map \ No newline at end of file diff --git a/src/Web/Avalonia.Web.Blazor/wwwroot/Avalonia/SKHtmlCanvas.js b/src/Web/Avalonia.Web.Blazor/wwwroot/Avalonia/SKHtmlCanvas.js deleted file mode 100644 index 5900026acd..0000000000 --- a/src/Web/Avalonia.Web.Blazor/wwwroot/Avalonia/SKHtmlCanvas.js +++ /dev/null @@ -1,172 +0,0 @@ -export class SKHtmlCanvas { - constructor(useGL, element, callback) { - this.renderLoopEnabled = false; - this.renderLoopRequest = 0; - this.htmlCanvas = element; - this.renderFrameCallback = callback; - if (useGL) { - const ctx = SKHtmlCanvas.createWebGLContext(this.htmlCanvas); - if (!ctx) { - console.error(`Failed to create WebGL context: err ${ctx}`); - return; - } - GL.makeContextCurrent(ctx); - const fbo = GLctx.getParameter(GLctx.FRAMEBUFFER_BINDING); - this.glInfo = { - context: ctx, - fboId: fbo ? fbo.id : 0, - stencil: GLctx.getParameter(GLctx.STENCIL_BITS), - sample: 0, - depth: GLctx.getParameter(GLctx.DEPTH_BITS), - }; - } - } - static initGL(element, elementId, callback) { - var view = SKHtmlCanvas.init(true, element, elementId, callback); - if (!view || !view.glInfo) - return null; - return view.glInfo; - } - static initRaster(element, elementId, callback) { - var view = SKHtmlCanvas.init(false, element, elementId, callback); - if (!view) - return false; - return true; - } - static init(useGL, element, elementId, callback) { - var htmlCanvas = element; - if (!htmlCanvas) { - console.error(`No canvas element was provided.`); - return null; - } - if (!SKHtmlCanvas.elements) - SKHtmlCanvas.elements = new Map(); - SKHtmlCanvas.elements.set(elementId, element); - const view = new SKHtmlCanvas(useGL, element, callback); - htmlCanvas.SKHtmlCanvas = view; - return view; - } - static deinit(elementId) { - if (!elementId) - return; - const element = SKHtmlCanvas.elements.get(elementId); - SKHtmlCanvas.elements.delete(elementId); - const htmlCanvas = element; - if (!htmlCanvas || !htmlCanvas.SKHtmlCanvas) - return; - htmlCanvas.SKHtmlCanvas.deinit(); - htmlCanvas.SKHtmlCanvas = undefined; - } - static requestAnimationFrame(element, renderLoop) { - const htmlCanvas = element; - if (!htmlCanvas || !htmlCanvas.SKHtmlCanvas) - return; - htmlCanvas.SKHtmlCanvas.requestAnimationFrame(renderLoop); - } - static setCanvasSize(element, width, height) { - const htmlCanvas = element; - if (!htmlCanvas || !htmlCanvas.SKHtmlCanvas) - return; - htmlCanvas.SKHtmlCanvas.setCanvasSize(width, height); - } - static setEnableRenderLoop(element, enable) { - const htmlCanvas = element; - if (!htmlCanvas || !htmlCanvas.SKHtmlCanvas) - return; - htmlCanvas.SKHtmlCanvas.setEnableRenderLoop(enable); - } - static putImageData(element, pData, width, height) { - const htmlCanvas = element; - if (!htmlCanvas || !htmlCanvas.SKHtmlCanvas) - return; - htmlCanvas.SKHtmlCanvas.putImageData(pData, width, height); - } - deinit() { - this.setEnableRenderLoop(false); - } - setCanvasSize(width, height) { - this.newWidth = width; - this.newHeight = height; - if (this.htmlCanvas.width != this.newWidth) { - this.htmlCanvas.width = this.newWidth; - } - if (this.htmlCanvas.height != this.newHeight) { - this.htmlCanvas.height = this.newHeight; - } - if (this.glInfo) { - GL.makeContextCurrent(this.glInfo.context); - } - } - requestAnimationFrame(renderLoop) { - if (renderLoop !== undefined && this.renderLoopEnabled !== renderLoop) - this.setEnableRenderLoop(renderLoop); - if (this.renderLoopRequest !== 0) - return; - this.renderLoopRequest = window.requestAnimationFrame(() => { - if (this.glInfo) { - GL.makeContextCurrent(this.glInfo.context); - } - if (this.htmlCanvas.width != this.newWidth) { - this.htmlCanvas.width = this.newWidth || 0; - } - if (this.htmlCanvas.height != this.newHeight) { - this.htmlCanvas.height = this.newHeight || 0; - } - this.renderFrameCallback.invokeMethod('Invoke'); - this.renderLoopRequest = 0; - if (this.renderLoopEnabled) - this.requestAnimationFrame(); - }); - } - setEnableRenderLoop(enable) { - this.renderLoopEnabled = enable; - if (enable) { - this.requestAnimationFrame(); - } - else if (this.renderLoopRequest !== 0) { - window.cancelAnimationFrame(this.renderLoopRequest); - this.renderLoopRequest = 0; - } - } - putImageData(pData, width, height) { - if (this.glInfo || !pData || width <= 0 || width <= 0) - return false; - var ctx = this.htmlCanvas.getContext('2d'); - if (!ctx) { - console.error(`Failed to obtain 2D canvas context.`); - return false; - } - this.htmlCanvas.width = width; - this.htmlCanvas.height = height; - var buffer = new Uint8ClampedArray(Module.HEAPU8.buffer, pData, width * height * 4); - var imageData = new ImageData(buffer, width, height); - ctx.putImageData(imageData, 0, 0); - return true; - } - static createWebGLContext(htmlCanvas) { - const contextAttributes = { - alpha: 1, - depth: 1, - stencil: 8, - antialias: 0, - premultipliedAlpha: 1, - preserveDrawingBuffer: 0, - preferLowPowerToHighPerformance: 0, - failIfMajorPerformanceCaveat: 0, - majorVersion: 2, - minorVersion: 0, - enableExtensionsByDefault: 1, - explicitSwapControl: 0, - renderViaOffscreenBackBuffer: 1, - }; - let ctx = GL.createContext(htmlCanvas, contextAttributes); - if (!ctx && contextAttributes.majorVersion > 1) { - console.warn('Falling back to WebGL 1.0'); - contextAttributes.majorVersion = 1; - contextAttributes.minorVersion = 0; - ctx = GL.createContext(htmlCanvas, contextAttributes); - } - return ctx; - } -} -//# sourceMappingURL=SKHtmlCanvas.js.map \ No newline at end of file diff --git a/src/Web/Avalonia.Web.Blazor/wwwroot/Avalonia/SizeWatcher.js b/src/Web/Avalonia.Web.Blazor/wwwroot/Avalonia/SizeWatcher.js deleted file mode 100644 index 1c6ce26155..0000000000 --- a/src/Web/Avalonia.Web.Blazor/wwwroot/Avalonia/SizeWatcher.js +++ /dev/null @@ -1,39 +0,0 @@ -export class SizeWatcher { - static observe(element, elementId, callback) { - if (!element || !callback) - return; - SizeWatcher.init(); - const watcherElement = element; - watcherElement.SizeWatcher = { - callback: callback - }; - SizeWatcher.elements.set(elementId, element); - SizeWatcher.observer.observe(element); - SizeWatcher.invoke(element); - } - static unobserve(elementId) { - if (!elementId || !SizeWatcher.observer) - return; - const element = SizeWatcher.elements.get(elementId); - SizeWatcher.elements.delete(elementId); - SizeWatcher.observer.unobserve(element); - } - static init() { - if (SizeWatcher.observer) - return; - SizeWatcher.elements = new Map(); - SizeWatcher.observer = new ResizeObserver((entries) => { - for (let entry of entries) { - SizeWatcher.invoke(entry.target); - } - }); - } - static invoke(element) { - const watcherElement = element; - const instance = watcherElement.SizeWatcher; - if (!instance || !instance.callback) - return; - return instance.callback.invokeMethod('Invoke', element.clientWidth, element.clientHeight); - } -} -//# sourceMappingURL=SizeWatcher.js.map \ No newline at end of file diff --git a/src/Web/Avalonia.Web.Blazor/wwwroot/Storage/IndexedDbWrapper.js b/src/Web/Avalonia.Web.Blazor/wwwroot/Storage/IndexedDbWrapper.js deleted file mode 100644 index bf9cab01bc..0000000000 --- a/src/Web/Avalonia.Web.Blazor/wwwroot/Storage/IndexedDbWrapper.js +++ /dev/null @@ -1,72 +0,0 @@ -class InnerDbConnection { - constructor(database) { - this.database = database; - } - openStore(store, mode) { - const tx = this.database.transaction(store, mode); - return tx.objectStore(store); - } - put(store, obj, key) { - const os = this.openStore(store, "readwrite"); - return new Promise((resolve, reject) => { - const response = os.put(obj, key); - response.onsuccess = () => { - resolve(response.result); - }; - response.onerror = () => { - reject(response.error); - }; - }); - } - get(store, key) { - const os = this.openStore(store, "readonly"); - return new Promise((resolve, reject) => { - const response = os.get(key); - response.onsuccess = () => { - resolve(response.result); - }; - response.onerror = () => { - reject(response.error); - }; - }); - } - delete(store, key) { - const os = this.openStore(store, "readwrite"); - return new Promise((resolve, reject) => { - const response = os.delete(key); - response.onsuccess = () => { - resolve(); - }; - response.onerror = () => { - reject(response.error); - }; - }); - } - close() { - this.database.close(); - } -} -export class IndexedDbWrapper { - constructor(databaseName, objectStores) { - this.databaseName = databaseName; - this.objectStores = objectStores; - } - connect() { - const conn = window.indexedDB.open(this.databaseName, 1); - conn.onupgradeneeded = event => { - const db = event.target.result; - this.objectStores.forEach(store => { - db.createObjectStore(store); - }); - }; - return new Promise((resolve, reject) => { - conn.onsuccess = event => { - resolve(new InnerDbConnection(event.target.result)); - }; - conn.onerror = event => { - reject(event.target.error); - }; - }); - } -} -//# sourceMappingURL=IndexedDbWrapper.js.map \ No newline at end of file diff --git a/src/Web/Avalonia.Web.Blazor/wwwroot/Storage/StorageProvider.js b/src/Web/Avalonia.Web.Blazor/wwwroot/Storage/StorageProvider.js deleted file mode 100644 index 042a83d177..0000000000 --- a/src/Web/Avalonia.Web.Blazor/wwwroot/Storage/StorageProvider.js +++ /dev/null @@ -1,199 +0,0 @@ -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __asyncValues = (this && this.__asyncValues) || function (o) { - if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); - var m = o[Symbol.asyncIterator], i; - return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i); - function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; } - function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); } -}; -import { IndexedDbWrapper } from "./IndexedDbWrapper"; -const fileBookmarksStore = "fileBookmarks"; -const avaloniaDb = new IndexedDbWrapper("AvaloniaDb", [ - fileBookmarksStore -]); -class StorageItem { - constructor(handle, bookmarkId) { - this.handle = handle; - this.bookmarkId = bookmarkId; - } - getName() { - return this.handle.name; - } - getKind() { - return this.handle.kind; - } - openRead() { - return __awaiter(this, void 0, void 0, function* () { - if (!(this.handle instanceof FileSystemFileHandle)) { - throw new Error("StorageItem is not a file"); - } - yield this.verityPermissions('read'); - const file = yield this.handle.getFile(); - return file; - }); - } - openWrite() { - return __awaiter(this, void 0, void 0, function* () { - if (!(this.handle instanceof FileSystemFileHandle)) { - throw new Error("StorageItem is not a file"); - } - yield this.verityPermissions('readwrite'); - return yield this.handle.createWritable({ keepExistingData: true }); - }); - } - getProperties() { - return __awaiter(this, void 0, void 0, function* () { - const file = this.handle instanceof FileSystemFileHandle - && (yield this.handle.getFile()); - if (!file) { - return null; - } - return { - Size: file.size, - LastModified: file.lastModified, - Type: file.type - }; - }); - } - getItems() { - var e_1, _a; - return __awaiter(this, void 0, void 0, function* () { - if (this.handle.kind !== "directory") { - return new StorageItems([]); - } - const items = []; - try { - for (var _b = __asyncValues(this.handle.entries()), _c; _c = yield _b.next(), !_c.done;) { - const [key, value] = _c.value; - items.push(new StorageItem(value)); - } - } - catch (e_1_1) { e_1 = { error: e_1_1 }; } - finally { - try { - if (_c && !_c.done && (_a = _b.return)) yield _a.call(_b); - } - finally { if (e_1) throw e_1.error; } - } - return new StorageItems(items); - }); - } - verityPermissions(mode) { - return __awaiter(this, void 0, void 0, function* () { - if ((yield this.handle.queryPermission({ mode })) === 'granted') { - return; - } - if ((yield this.handle.requestPermission({ mode })) === "denied") { - throw new Error("Read permissions denied"); - } - }); - } - saveBookmark() { - return __awaiter(this, void 0, void 0, function* () { - if (this.bookmarkId) { - return this.bookmarkId; - } - const connection = yield avaloniaDb.connect(); - try { - const key = yield connection.put(fileBookmarksStore, this.handle, this.generateBookmarkId()); - return key; - } - finally { - connection.close(); - } - }); - } - deleteBookmark() { - return __awaiter(this, void 0, void 0, function* () { - if (!this.bookmarkId) { - return; - } - const connection = yield avaloniaDb.connect(); - try { - const key = yield connection.delete(fileBookmarksStore, this.bookmarkId); - } - finally { - connection.close(); - } - }); - } - generateBookmarkId() { - return Date.now().toString(36) + Math.random().toString(36).substring(2); - } -} -class StorageItems { - constructor(items) { - this.items = items; - } - count() { - return this.items.length; - } - at(index) { - return this.items[index]; - } -} -export class StorageProvider { - static canOpen() { - return typeof window.showOpenFilePicker !== 'undefined'; - } - static canSave() { - return typeof window.showSaveFilePicker !== 'undefined'; - } - static canPickFolder() { - return typeof window.showDirectoryPicker !== 'undefined'; - } - static selectFolderDialog(startIn) { - return __awaiter(this, void 0, void 0, function* () { - const options = { - startIn: ((startIn === null || startIn === void 0 ? void 0 : startIn.handle) || undefined) - }; - const handle = yield window.showDirectoryPicker(options); - return new StorageItem(handle); - }); - } - static openFileDialog(startIn, multiple, types, excludeAcceptAllOption) { - return __awaiter(this, void 0, void 0, function* () { - const options = { - startIn: ((startIn === null || startIn === void 0 ? void 0 : startIn.handle) || undefined), - multiple, - excludeAcceptAllOption, - types: (types || undefined) - }; - const handles = yield window.showOpenFilePicker(options); - return new StorageItems(handles.map((handle) => new StorageItem(handle))); - }); - } - static saveFileDialog(startIn, suggestedName, types, excludeAcceptAllOption) { - return __awaiter(this, void 0, void 0, function* () { - const options = { - startIn: ((startIn === null || startIn === void 0 ? void 0 : startIn.handle) || undefined), - suggestedName: (suggestedName || undefined), - excludeAcceptAllOption, - types: (types || undefined) - }; - const handle = yield window.showSaveFilePicker(options); - return new StorageItem(handle); - }); - } - static openBookmark(key) { - return __awaiter(this, void 0, void 0, function* () { - const connection = yield avaloniaDb.connect(); - try { - const handle = yield connection.get(fileBookmarksStore, key); - return handle && new StorageItem(handle, key); - } - finally { - connection.close(); - } - }); - } -} -//# sourceMappingURL=StorageProvider.js.map \ No newline at end of file From baf29e273d3d72663453f5323221d49e71117cfe Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 7 Sep 2022 15:27:28 +0100 Subject: [PATCH 25/30] ignore wwwroot --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 0f0e153e9c..84faae1806 100644 --- a/.gitignore +++ b/.gitignore @@ -214,3 +214,4 @@ src/Web/Avalonia.Web.Blazor/wwwroot/*.js src/Web/Avalonia.Web.Blazor/Interop/Typescript/*.js node_modules src/Web/Avalonia.Web.Blazor/webapp/package-lock.json +src/Web/Avalonia.Web.Blazor/wwwroot From 33de9380a5e7399494db7fe64af75d8d755b7b72 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Wed, 7 Sep 2022 18:54:10 +0200 Subject: [PATCH 26/30] feat(DevAnalyzer): OnPropertyChanged override Analyzer --- .../OnPropertyChangedOverrideAnalyzer.cs | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 src/tools/DevAnalyzers/OnPropertyChangedOverrideAnalyzer.cs diff --git a/src/tools/DevAnalyzers/OnPropertyChangedOverrideAnalyzer.cs b/src/tools/DevAnalyzers/OnPropertyChangedOverrideAnalyzer.cs new file mode 100644 index 0000000000..a16542fdc0 --- /dev/null +++ b/src/tools/DevAnalyzers/OnPropertyChangedOverrideAnalyzer.cs @@ -0,0 +1,64 @@ +using System.Collections.Immutable; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace DevAnalyzers +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public class OnPropertyChangedOverrideAnalyzer : DiagnosticAnalyzer + { + public const string DiagnosticId = "AVADEV2001"; + + private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor( + DiagnosticId, + "Missing invoke base.OnPropertyChanged", + "Method '{0}' do not invoke base.{0}", + "Potential issue", + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: "The OnPropertyChanged of the base class was not invoked in the override method declaration, which could lead to unwanted behavior."); + + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Rule); + + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.EnableConcurrentExecution(); + context.RegisterSymbolAction(AnalyzeMethod, SymbolKind.Method); + } + + private static void AnalyzeMethod(SymbolAnalysisContext context) + { + if (context.Symbol is IMethodSymbol currentMethod + && currentMethod.Name == "OnPropertyChanged" + && currentMethod.OverriddenMethod is IMethodSymbol originalMethod) + { + var declaration = currentMethod.DeclaringSyntaxReferences.FirstOrDefault() + ?.GetSyntax(context.CancellationToken); + if (declaration is not null && context.Compilation.GetSemanticModel(declaration!.SyntaxTree) is { } semanticModel) + { + if (declaration.SyntaxTree.TryGetRoot(out var root)) + { + var baseInvocations = root.DescendantNodes().OfType(); + if (baseInvocations.Any()) + { + foreach (var baseInvocation in baseInvocations) + { + var parent = baseInvocation.Parent; + var targetSymbol = semanticModel.GetSymbolInfo(parent, context.CancellationToken); + if (SymbolEqualityComparer.Default.Equals(targetSymbol.Symbol, originalMethod)) + { + return; + } + } + } + context.ReportDiagnostic(Diagnostic.Create(Rule, currentMethod.Locations[0], currentMethod.Name)); + } + } + } + } + + } +} From f45617b23cfae909c0a533e2edf283f770b00445 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 26 Aug 2022 15:10:10 +0100 Subject: [PATCH 27/30] use composition on wasm. --- src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs | 5 +++-- src/Web/Avalonia.Web.Blazor/RazorViewTopLevelImpl.cs | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs b/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs index 09128d714d..1bba8327bf 100644 --- a/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs +++ b/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs @@ -6,6 +6,7 @@ using Avalonia.Input.Raw; using Avalonia.Input.TextInput; using Avalonia.Platform.Storage; using Avalonia.Rendering; +using Avalonia.Rendering.Composition; using Avalonia.Web.Blazor.Interop; using Avalonia.Web.Blazor.Interop.Storage; @@ -352,9 +353,9 @@ namespace Avalonia.Web.Blazor // We also don't want to have it as a meaningful public API. // Therefore we have InternalsVisibleTo hack here. - if (_topLevel.Renderer is DeferredRenderer dr) + if (_topLevel.Renderer is CompositingRenderer dr) { - dr.Render(true); + dr.CompositionTarget.ImmediateUIThreadRender(); } } diff --git a/src/Web/Avalonia.Web.Blazor/RazorViewTopLevelImpl.cs b/src/Web/Avalonia.Web.Blazor/RazorViewTopLevelImpl.cs index af9a07927e..86860ebd58 100644 --- a/src/Web/Avalonia.Web.Blazor/RazorViewTopLevelImpl.cs +++ b/src/Web/Avalonia.Web.Blazor/RazorViewTopLevelImpl.cs @@ -7,6 +7,7 @@ using Avalonia.Input.TextInput; using Avalonia.Platform; using Avalonia.Platform.Storage; using Avalonia.Rendering; +using Avalonia.Rendering.Composition; using Avalonia.Web.Blazor.Interop; using SkiaSharp; @@ -146,7 +147,7 @@ namespace Avalonia.Web.Blazor public IRenderer CreateRenderer(IRenderRoot root) { var loop = AvaloniaLocator.Current.GetRequiredService(); - return new DeferredRenderer(root, loop); + return new CompositingRenderer(root, new Compositor(loop, null)); } public void Invalidate(Rect rect) From 0f9273c3b53baeb7ff7f9f5f9c72489678976e49 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 7 Sep 2022 21:35:54 +0100 Subject: [PATCH 28/30] fix tab key handling, by making sure the canvas is re-focused when the ime is closed. --- src/Web/Avalonia.Web.Blazor/AvaloniaView.razor | 8 +++++++- src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs | 13 +++++++++++-- .../Interop/InputHelperInterop.cs | 4 ++-- .../webapp/modules/Avalonia/InputHelper.ts | 13 +++++++++++-- 4 files changed, 31 insertions(+), 7 deletions(-) diff --git a/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor b/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor index 4802191077..27a6acd0de 100644 --- a/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor +++ b/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor @@ -1,7 +1,11 @@ -
diff --git a/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs b/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs index 1bba8327bf..f3a45aac56 100644 --- a/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs +++ b/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs @@ -32,10 +32,12 @@ namespace Avalonia.Web.Blazor private AvaloniaModule? _avaloniaModule = null; private InputHelperInterop? _inputHelper = null; private InputHelperInterop? _canvasHelper = null; + private InputHelperInterop? _containerHelper = null; private NativeControlHostInterop? _nativeControlHost = null; private StorageProviderInterop? _storageProvider = null; private ElementReference _htmlCanvas; private ElementReference _inputElement; + private ElementReference _containerElement; private ElementReference _nativeControlsContainer; private double _dpi = 1; private SKSize _canvasSize = new (100, 100); @@ -247,8 +249,9 @@ namespace Avalonia.Web.Blazor _inputHelper = new InputHelperInterop(_avaloniaModule, _inputElement); _canvasHelper = new InputHelperInterop(_avaloniaModule, _htmlCanvas); + _containerHelper = new InputHelperInterop(_avaloniaModule, _containerElement); - _inputHelper.Hide(); + HideIme(); _canvasHelper.SetCursor("default"); _topLevelImpl.SetCssCursor = x => { @@ -387,6 +390,12 @@ namespace Avalonia.Web.Blazor } } + private void HideIme() + { + _inputHelper?.Hide(); + _containerHelper?.Focus(); + } + public void SetClient(ITextInputMethodClient? client) { if (_inputHelper is null) @@ -407,7 +416,7 @@ namespace Avalonia.Web.Blazor else { _inputElementFocused = false; - _inputHelper.Hide(); + HideIme(); } } diff --git a/src/Web/Avalonia.Web.Blazor/Interop/InputHelperInterop.cs b/src/Web/Avalonia.Web.Blazor/Interop/InputHelperInterop.cs index 294e71eb1f..ebc80618b1 100644 --- a/src/Web/Avalonia.Web.Blazor/Interop/InputHelperInterop.cs +++ b/src/Web/Avalonia.Web.Blazor/Interop/InputHelperInterop.cs @@ -13,10 +13,10 @@ namespace Avalonia.Web.Blazor.Interop private readonly AvaloniaModule _module; private readonly ElementReference _inputElement; - public InputHelperInterop(AvaloniaModule module, ElementReference element) + public InputHelperInterop(AvaloniaModule module, ElementReference inputElement) { _module = module; - _inputElement = element; + _inputElement = inputElement; } public void Clear() => _module.Invoke(ClearSymbol, _inputElement); diff --git a/src/Web/Avalonia.Web.Blazor/webapp/modules/Avalonia/InputHelper.ts b/src/Web/Avalonia.Web.Blazor/webapp/modules/Avalonia/InputHelper.ts index 2cce411376..8619fc8cee 100644 --- a/src/Web/Avalonia.Web.Blazor/webapp/modules/Avalonia/InputHelper.ts +++ b/src/Web/Avalonia.Web.Blazor/webapp/modules/Avalonia/InputHelper.ts @@ -3,9 +3,18 @@ inputElement.value = ""; } - public static focus(inputElement: HTMLInputElement) { + + public static isInputElement( element : HTMLInputElement | HTMLElement ) : element is HTMLInputElement { + return ( element as HTMLInputElement).setSelectionRange !== undefined; + } + + public static focus(inputElement: HTMLElement) { inputElement.focus(); - inputElement.setSelectionRange(0, 0); + + if(this.isInputElement(inputElement)) + { + (inputElement as HTMLInputElement).setSelectionRange(0,0); + } } public static setCursor(inputElement: HTMLInputElement, kind: string) { From b9f7270eb978258c60d95823cb5b263d17a32c38 Mon Sep 17 00:00:00 2001 From: Dmitry Zhelnin Date: Sun, 4 Sep 2022 22:22:09 +0300 Subject: [PATCH 29/30] TreeView: improve navigation with Left and Right keys --- src/Avalonia.Controls/TreeView.cs | 1 + src/Avalonia.Controls/TreeViewItem.cs | 19 ++++++++++++++----- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/Avalonia.Controls/TreeView.cs b/src/Avalonia.Controls/TreeView.cs index 7359f3cade..d78f9c82ef 100644 --- a/src/Avalonia.Controls/TreeView.cs +++ b/src/Avalonia.Controls/TreeView.cs @@ -495,6 +495,7 @@ namespace Avalonia.Controls break; case NavigationDirection.Down: + case NavigationDirection.Right: if (from?.IsExpanded == true && intoChildren && from.ItemCount > 0) { result = (TreeViewItem)from.ItemContainerGenerator.ContainerFromIndex(0)!; diff --git a/src/Avalonia.Controls/TreeViewItem.cs b/src/Avalonia.Controls/TreeViewItem.cs index 2e3aa037c2..ada081b808 100644 --- a/src/Avalonia.Controls/TreeViewItem.cs +++ b/src/Avalonia.Controls/TreeViewItem.cs @@ -157,17 +157,26 @@ namespace Avalonia.Controls switch (e.Key) { case Key.Right: - if (Items != null && Items.Cast().Any()) + if (Items != null && Items.Cast().Any() && !IsExpanded) { IsExpanded = true; + e.Handled = true; } - - e.Handled = true; break; case Key.Left: - IsExpanded = false; - e.Handled = true; + if (Items is not null && Items.Cast().Any() && IsExpanded) + { + if (IsFocused) + { + IsExpanded = false; + } + else + { + FocusManager.Instance?.Focus(this, NavigationMethod.Directional); + } + e.Handled = true; + } break; } } From 9fc7be195aff85da4f35f844681a233efea94b62 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Thu, 8 Sep 2022 10:28:46 +0200 Subject: [PATCH 30/30] fix: CS8667 Nullability of reference types in type of --- samples/ControlCatalog/Converter/HexConverter.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/ControlCatalog/Converter/HexConverter.cs b/samples/ControlCatalog/Converter/HexConverter.cs index 83a52212f6..31cce5ba67 100644 --- a/samples/ControlCatalog/Converter/HexConverter.cs +++ b/samples/ControlCatalog/Converter/HexConverter.cs @@ -7,7 +7,7 @@ namespace ControlCatalog.Converter; public class HexConverter : IValueConverter { - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) { var str = value?.ToString(); if (str == null) @@ -18,7 +18,7 @@ public class HexConverter : IValueConverter } - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) { try {