diff --git a/src/.nuspec/Uno.Resizetizer.targets b/src/.nuspec/Uno.Resizetizer.targets
index 9cd40b93..bbd23fb5 100644
--- a/src/.nuspec/Uno.Resizetizer.targets
+++ b/src/.nuspec/Uno.Resizetizer.targets
@@ -48,10 +48,6 @@
AssemblyFile="$(_UnoResizetizerTaskAssemblyName)"
TaskName="Uno.Resizetizer.GenerateWasmSplashAssets_v0"/>
-
-
@@ -231,6 +227,8 @@
_ComputeAndroidResourcePaths;
$(UnoResizetizeCollectItemsAfterTargets);
UnoAssetsGeneration;
+ GenerateMSBuildEditorConfigFileShouldRun;
+ GenerateMSBuildEditorConfigFileCore;
@@ -418,6 +416,7 @@
WriteOnlyWhenDifferent="true"/>
+
@@ -589,25 +588,12 @@
-
-
-
- $([MSBuild]::ValueOrDefault('$(ApplicationTitle)', '$(AssemblyName)'))
-
-
-
-
-
-
+
+
+
+
+
+
+ netstandard2.0
+ false
+ Uno.Resizetizer
+ true
+ analyzers/dotnet/cs
+ latest
+ false
+
+ System.Runtime.CompilerServices.IsExternalInit;
+ System.Diagnostics.CodeAnalysis.NotNullWhenAttribute;
+
+ true
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
diff --git a/src/Resizetizer/Resizetizer.Generators/WindowTitleGenerator.cs b/src/Resizetizer/Resizetizer.Generators/WindowTitleGenerator.cs
new file mode 100644
index 00000000..10d1ff95
--- /dev/null
+++ b/src/Resizetizer/Resizetizer.Generators/WindowTitleGenerator.cs
@@ -0,0 +1,181 @@
+using System;
+using System.IO;
+using System.Linq;
+using System.Text;
+using CodeGenHelpers;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Diagnostics;
+using Microsoft.CodeAnalysis.Text;
+
+namespace Resizetizer.Generators;
+
+[Generator(LanguageNames.CSharp)]
+internal sealed class WindowTitleGenerator : IIncrementalGenerator
+{
+ private const string UnoResizetizerIcon = nameof(UnoResizetizerIcon);
+ private const string IsUnoHead = nameof(IsUnoHead);
+
+ public void Initialize(IncrementalGeneratorInitializationContext context)
+ {
+ // Get the AnalyzerConfigOptionsProvider
+ var optionsProvider = context.AnalyzerConfigOptionsProvider;
+ var assemblyNameProvider = context.CompilationProvider.Select((compilation, _) => compilation.Assembly.Name);
+ var additionalTextsProvider = context.AdditionalTextsProvider;
+
+ var extensionPropertiesProvider = optionsProvider.Combine(assemblyNameProvider).Select((x, cancellationToken) =>
+ {
+ var (options, assemblyName) = x;
+ if (!GetProperty(options.GlobalOptions, IsUnoHead))
+ {
+ return null;
+ }
+
+ var rootNamespace = GetPropertyValue(options.GlobalOptions, "RootNamespace");
+ var windowTitle = GetPropertyValue(options.GlobalOptions, "ApplicationTitle");
+ if (string.IsNullOrEmpty(windowTitle))
+ {
+ windowTitle = assemblyName;
+ }
+
+ return string.IsNullOrEmpty(rootNamespace) || string.IsNullOrEmpty(windowTitle) ? null : new ExtensionPropertiesContext(rootNamespace, windowTitle);
+ });
+
+ // Combine optionsProvider and compilationProvider
+ var iconNameProvider = additionalTextsProvider
+ .Where(x => Path.GetFileName(x.Path).Equals("UnoImage.inputs", StringComparison.InvariantCultureIgnoreCase))
+ .Select((additionalText, cancellationToken) =>
+ {
+ var sourceText = additionalText.GetText(cancellationToken);
+ return ParseFile(sourceText.ToString());
+ })
+ .Where(x => !string.IsNullOrEmpty(x))
+ .Select((x, _) => Path.GetFileNameWithoutExtension(x));
+
+ // Define the source generator logic
+ var sourceCodeProvider = iconNameProvider.Combine(extensionPropertiesProvider).Select((x, _) =>
+ {
+ var (iconName, coreContext) = x;
+ if (string.IsNullOrEmpty(iconName) || string.IsNullOrEmpty(coreContext?.RootNamespace) || string.IsNullOrEmpty(coreContext?.WindowTitle))
+ return null;
+
+ return new ExtensionGenerationContext(coreContext.RootNamespace, iconName, coreContext.WindowTitle);
+ }).Where(result => result != null);
+
+ // Register the source generator logic to add the generated source code
+ context.RegisterSourceOutput(sourceCodeProvider, (sourceContext, extensionContext) =>
+ {
+ if (!string.IsNullOrEmpty(extensionContext.WindowTitle))
+ {
+ AddSource(sourceContext, GenerateLegacyNamespaceCompat());
+ AddSource(sourceContext, GenerateWindowTitleExtension(extensionContext.RootNamespace, extensionContext.IconName, extensionContext.WindowTitle));
+ }
+ });
+ }
+
+ internal record ExtensionPropertiesContext(string RootNamespace, string WindowTitle);
+
+ internal record ExtensionGenerationContext(string RootNamespace, string IconName, string WindowTitle);
+
+ private static string ParseFile(string content)
+ {
+ // Split the content into lines
+ var lines = content.Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
+
+ foreach (var line in lines)
+ {
+ // Split the line into key-value pairs
+ var properties = line.Split(';').Select(property => property.Split('=')).ToDictionary(parts => parts[0], parts => parts.Length > 1 ? parts[1] : null);
+
+ // Check if IsAppIcon is true
+ if (properties.TryGetValue("IsAppIcon", out var isAppIcon) && bool.TryParse(isAppIcon, out var isAppIconValue) && isAppIconValue)
+ {
+ // Return the file path
+ if (properties.TryGetValue("File", out var filePath))
+ {
+ return filePath;
+ }
+ }
+ }
+
+ // Return null if no app icon is found
+ return null;
+ }
+
+ private static ClassBuilder GenerateWindowTitleExtension(string rootNamespace, string iconName, string windowTitle)
+ {
+ var builder = CodeBuilder.Create(rootNamespace)
+ .AddClass("WindowExtensions")
+ .MakeStaticClass()
+ .WithSummary(@"Extension methods for the class.");
+
+ builder.AddMethod("SetWindowIcon")
+ .AddParameter("this global::Microsoft.UI.Xaml.Window", "window")
+ .MakeStaticMethod()
+ .MakePublicMethod()
+ .WithSummary(@"This will set the Window Icon for the given using the provided UnoIcon.")
+ .WithBody(w =>
+ {
+ w.AppendUnindentedLine("#if WINDOWS && !HAS_UNO");
+ w.AppendLine("var hWnd = global::WinRT.Interop.WindowNative.GetWindowHandle(window);");
+ w.NewLine();
+ w.AppendLine("// Retrieve the WindowId that corresponds to hWnd.");
+ w.AppendLine("global::Microsoft.UI.WindowId windowId = global::Microsoft.UI.Win32Interop.GetWindowIdFromWindow(hWnd);");
+ w.NewLine();
+ w.AppendLine("// Lastly, retrieve the AppWindow for the current (XAML) WinUI 3 window.");
+ w.AppendLine("global::Microsoft.UI.Windowing.AppWindow appWindow = global::Microsoft.UI.Windowing.AppWindow.GetFromWindowId(windowId);");
+ w.AppendLine($@"appWindow.SetIcon(""{iconName}.ico"");");
+ w.NewLine();
+ w.AppendLine("// Set the Window Title Only if it has the Default WinUI Desktop value and we are running Unpackaged");
+ // We're no longer checking for IsPackaged as this seems to be needed when Packaged as well.
+ w.If(@"appWindow.Title == ""WinUI Desktop""")
+ .WithBody(b =>
+ {
+ b.AppendLine($@"appWindow.Title = ""{windowTitle}"";");
+ })
+ .EndIf();
+ w.AppendUnindentedLine("#endif");
+ });
+
+ // NOTE: This method has been removed as it seems WinUI isn't setting the title when Packaged. Keeping in case we need this in the future.
+ //builder.AddMethod("IsPackaged")
+ // .WithReturnType("bool")
+ // .MakePrivateMethod()
+ // .MakeStaticMethod()
+ // .WithBody(w =>
+ // {
+ // using (w.Block("try"))
+ // {
+ // w.AppendLine("return global::Windows.ApplicationModel.Package.Current != null;");
+ // }
+ // using (w.Block("catch"))
+ // {
+ // w.AppendLine("return false;");
+ // }
+ // });
+
+ return builder;
+ }
+
+ private static string GetPropertyValue(AnalyzerConfigOptions options, string key) =>
+ options.TryGetValue($"build_property.{key}", out var value) ? value : string.Empty;
+
+ private static bool GetProperty(AnalyzerConfigOptions options, string key) =>
+ bool.TryParse(GetPropertyValue(options, key), out var result) && result;
+
+ private static bool HasUnoIcon(AnalyzerConfigOptions options, out string unoIcon)
+ {
+ unoIcon = GetPropertyValue(options, UnoResizetizerIcon);
+ return !string.IsNullOrEmpty(unoIcon) && !unoIcon.Contains(",");
+ }
+
+ private static ClassBuilder GenerateLegacyNamespaceCompat()
+ {
+ return CodeBuilder.Create("Uno.Resizetizer")
+ .AddClass("__LegacyResizetizerSupport__")
+ .WithSummary("This is added to ensure the Uno.Resizetizer namespace is present to avoid breaking any applications.")
+ .MakeStaticClass();
+ }
+
+ private static void AddSource(SourceProductionContext context, ClassBuilder builder) =>
+ context.AddSource($"{builder.FullyQualifiedName}.g.cs", SourceText.From(builder.Build(), Encoding.UTF8));
+}
diff --git a/src/Resizetizer/src/Resizetizer.csproj b/src/Resizetizer/src/Resizetizer.csproj
index dff9209b..de4c8946 100644
--- a/src/Resizetizer/src/Resizetizer.csproj
+++ b/src/Resizetizer/src/Resizetizer.csproj
@@ -1,4 +1,4 @@
-
+
netstandard2.0
@@ -21,7 +21,7 @@
- Uno.Resizetizer_v0
+ Uno.Resizetizer_v0
$(GITVERSION_SHA)
Uno.Resizetizer
Uno Platform package support for images.
@@ -44,7 +44,7 @@
-
+
@@ -72,6 +72,12 @@
+
+
+ false
+
+
+
@@ -119,4 +125,13 @@
Condition="Exists('$(NuGetPackageRoot)$(PackageId.ToLowerInvariant())') And '$(OS)' != 'Windows_NT'" />
+
+
+
+
+
+
diff --git a/src/Resizetizer/src/WindowIconGeneratorTask.cs b/src/Resizetizer/src/WindowIconGeneratorTask.cs
deleted file mode 100644
index 257d3834..00000000
--- a/src/Resizetizer/src/WindowIconGeneratorTask.cs
+++ /dev/null
@@ -1,108 +0,0 @@
-using System;
-using System.IO;
-using Microsoft.Build.Framework;
-using Microsoft.Build.Utilities;
-
-namespace Uno.Resizetizer;
-
-public class WindowIconGeneratorTask_V0 : Task
-{
- private const string FileName = "Uno.Resizetizer.WindowIconExtensions.g.cs";
-
- public ITaskItem[] UnoIcons { get; set; }
-
- [Required]
- public string IntermediateOutputDirectory { get; set; }
-
- public string WindowTitle { get; set; }
-
- [Output]
- public ITaskItem[] GeneratedClass { get; private set; } = Array.Empty();
-
- public override bool Execute()
- {
- if (UnoIcons is null || UnoIcons.Length == 0)
- {
- return true;
- }
-
- if(string.IsNullOrEmpty(IntermediateOutputDirectory))
- {
- Log.LogError("The IntermediateOutputDirectory (typically the obj directory) is a required parameter but was null or empty.");
- return false;
- }
-
- var iconPath = UnoIcons[0].ItemSpec;
- var iconName = Path.GetFileNameWithoutExtension(iconPath);
-
- var code = @$"//------------------------------------------------------------------------------
-//
-// This code was auto-generated.
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-//
-//------------------------------------------------------------------------------
-
-namespace Uno.Resizetizer
-{{
- ///
- /// Extension methods for the class.
- ///
- public static class WindowExtensions
- {{
- ///
- /// This will set the Window Icon for the given using
- /// the provided UnoIcon.
- ///
- public static void SetWindowIcon(this global::Microsoft.UI.Xaml.Window window)
- {{
-#if WINDOWS && !HAS_UNO
- var hWnd =
- global::WinRT.Interop.WindowNative.GetWindowHandle(window);
-
- // Retrieve the WindowId that corresponds to hWnd.
- global::Microsoft.UI.WindowId windowId =
- global::Microsoft.UI.Win32Interop.GetWindowIdFromWindow(hWnd);
-
- // Lastly, retrieve the AppWindow for the current (XAML) WinUI 3 window.
- global::Microsoft.UI.Windowing.AppWindow appWindow =
- global::Microsoft.UI.Windowing.AppWindow.GetFromWindowId(windowId);
- appWindow.SetIcon(""{iconName}.ico"");
-
- // Set the Window Title Only if it has the Default WinUI Desktop value and we are running Unpackaged
- if (!IsPackaged() && appWindow.Title == ""WinUI Desktop"")
- {{
- appWindow.Title = ""{WindowTitle}"";
- }}
-
- static bool IsPackaged()
- {{
- try
- {{
- if (global::Windows.ApplicationModel.Package.Current != null)
- return true;
- }}
- catch
- {{
- // no-op
- }}
-
- return false;
- }}
-#endif
- }}
- }}
-}}";
-
- if(!Directory.Exists(IntermediateOutputDirectory))
- {
- Directory.CreateDirectory(IntermediateOutputDirectory);
- }
-
- var item = new TaskItem(Path.Combine(IntermediateOutputDirectory, FileName));
- File.WriteAllText(item.ItemSpec, code);
- GeneratedClass = new [] { item };
- return true;
- }
-}
diff --git a/src/Resizetizer/uno.resizetizer.sln b/src/Resizetizer/uno.resizetizer.sln
index a80b9860..721110fc 100644
--- a/src/Resizetizer/uno.resizetizer.sln
+++ b/src/Resizetizer/uno.resizetizer.sln
@@ -12,6 +12,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
..\.nuspec\Uno.Resizetizer.targets = ..\.nuspec\Uno.Resizetizer.targets
EndProjectSection
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Resizetizer.Generators", "Resizetizer.Generators\Resizetizer.Generators.csproj", "{9CDB1CAA-293A-434C-A092-29D0EE03496D}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
DEBUG_RESIZETIZER|Any CPU = DEBUG_RESIZETIZER|Any CPU
@@ -31,6 +33,12 @@ Global
{7695AB69-4414-4539-8172-A78D8460F663}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7695AB69-4414-4539-8172-A78D8460F663}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7695AB69-4414-4539-8172-A78D8460F663}.Release|Any CPU.Build.0 = Release|Any CPU
+ {9CDB1CAA-293A-434C-A092-29D0EE03496D}.DEBUG_RESIZETIZER|Any CPU.ActiveCfg = Debug|Any CPU
+ {9CDB1CAA-293A-434C-A092-29D0EE03496D}.DEBUG_RESIZETIZER|Any CPU.Build.0 = Debug|Any CPU
+ {9CDB1CAA-293A-434C-A092-29D0EE03496D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {9CDB1CAA-293A-434C-A092-29D0EE03496D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {9CDB1CAA-293A-434C-A092-29D0EE03496D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {9CDB1CAA-293A-434C-A092-29D0EE03496D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE