From 303f783d484cf9f311d36a4af6ce0cc33ab384f6 Mon Sep 17 00:00:00 2001 From: Raul Avila Catala Date: Sun, 16 Aug 2026 11:09:27 -0600 Subject: [PATCH 1/2] fix: align Svg.Skia version of IcoGen subproject with framework --- CONTRIBUTING.md | 4 +++- .../EasyWinApp/EasyWinApp.csproj | 2 +- .../SimpleEasyWinApp/SimpleEasyWinApp.csproj | 2 +- src/Sample/Sample.csproj | 2 +- src/Sample/Sample.ico | Bin 19784 -> 20134 bytes 5 files changed, 6 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ad2055c..b5ea058 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -140,6 +140,8 @@ Los proyectos de plantilla incluyen un `Target` de MSBuild (`GenerateAppIcon`) q > **Nota**: SkiaSharp es dependencia de *build-time* solo (no se incluye en la app final). El developer puede reemplazar el procesador implementando `IEasyImageProcessor`. +> **Alineación de versiones**: el proyecto temporal `IcoGen` debe referenciar la **misma** versión de `Svg.Skia` que el framework (`EasyWindowsApplication.csproj` usa `*` flotante). La línea se declara como `Version="%2A"` en los tres `.csproj` con el target (`Sample`, `EasyWinApp`, `SimpleEasyWinApp`): `%2A` es el escapado MSBuild de `*`, necesario porque un `*` literal en un `Include` de ItemGroup se interpreta como *wildcard* y MSBuild descarta el item. El `IcoGen.csproj` generado recibe `Version="*"`, de modo que ambos resuelven al mismo `Svg.Skia` en el mismo build. Si se *pinea* el framework, hay que actualizar también las 3 líneas del `IcoGen`; un desajuste produce `System.IO.FileNotFoundException` al ejecutar `IcoGen` (código `-532462766`). + --- ## Native AOT @@ -235,7 +237,7 @@ Setup manual (una sola vez): ### Notas para mantenedores -- **`Svg.Skia` usa `*`** en `EasyWindowsApplication.csproj`. Si un build empieza a fallar tras una actualización de dependencias, considera *pinnear* la versión. +- **`Svg.Skia` usa `*`** en `EasyWindowsApplication.csproj`. Si un build empieza a fallar tras una actualización de dependencias, considera *pinnear* la versión — en ese caso actualiza también las 3 líneas del proyecto `IcoGen` (ver [Pipeline de iconos SVG→ICO](#pipeline-de-iconos-svg-ico)). - **Tags de GitHub**: `v{x.y.z}` (estable) o `v{x.y.z}-preview`. Si el workflow falla al crear el tag por duplicado, elimínalo manualmente (`git push origin --delete v{tag}` o desde la interfaz) y vuelve a disparar el workflow. --- diff --git a/src/ProjectTemplates/EasyWinApp/EasyWinApp.csproj b/src/ProjectTemplates/EasyWinApp/EasyWinApp.csproj index 1f7f1ac..c986b96 100644 --- a/src/ProjectTemplates/EasyWinApp/EasyWinApp.csproj +++ b/src/ProjectTemplates/EasyWinApp/EasyWinApp.csproj @@ -51,7 +51,7 @@ <_IcoProjLine Include=" <Nullable>enable</Nullable>" /> <_IcoProjLine Include=" </PropertyGroup>" /> <_IcoProjLine Include=" <ItemGroup>" /> - <_IcoProjLine Include=" <PackageReference Include="Svg.Skia" Version="5.1.1" />" /> + <_IcoProjLine Include=" <PackageReference Include="Svg.Skia" Version="%2A" />" /> <_IcoProjLine Include=" <ProjectReference Include="$(_EasyWAProj)" />" /> <_IcoProjLine Include=" </ItemGroup>" /> <_IcoProjLine Include="</Project>" /> diff --git a/src/ProjectTemplates/SimpleEasyWinApp/SimpleEasyWinApp.csproj b/src/ProjectTemplates/SimpleEasyWinApp/SimpleEasyWinApp.csproj index 9d08de1..9b1a1a5 100644 --- a/src/ProjectTemplates/SimpleEasyWinApp/SimpleEasyWinApp.csproj +++ b/src/ProjectTemplates/SimpleEasyWinApp/SimpleEasyWinApp.csproj @@ -51,7 +51,7 @@ <_IcoProjLine Include=" <Nullable>enable</Nullable>" /> <_IcoProjLine Include=" </PropertyGroup>" /> <_IcoProjLine Include=" <ItemGroup>" /> - <_IcoProjLine Include=" <PackageReference Include="Svg.Skia" Version="5.1.1" />" /> + <_IcoProjLine Include=" <PackageReference Include="Svg.Skia" Version="%2A" />" /> <_IcoProjLine Include=" <ProjectReference Include="$(_EasyWAProj)" />" /> <_IcoProjLine Include=" </ItemGroup>" /> <_IcoProjLine Include="</Project>" /> diff --git a/src/Sample/Sample.csproj b/src/Sample/Sample.csproj index 5772094..d76f94f 100644 --- a/src/Sample/Sample.csproj +++ b/src/Sample/Sample.csproj @@ -51,7 +51,7 @@ <_IcoProjLine Include=" <Nullable>enable</Nullable>" /> <_IcoProjLine Include=" </PropertyGroup>" /> <_IcoProjLine Include=" <ItemGroup>" /> - <_IcoProjLine Include=" <PackageReference Include="Svg.Skia" Version="5.1.1" />" /> + <_IcoProjLine Include=" <PackageReference Include="Svg.Skia" Version="%2A" />" /> <_IcoProjLine Include=" <ProjectReference Include="$(_EasyWAProj)" />" /> <_IcoProjLine Include=" </ItemGroup>" /> <_IcoProjLine Include="</Project>" /> diff --git a/src/Sample/Sample.ico b/src/Sample/Sample.ico index fae7f03d7d37a4349aea504a605e1c0a43d263b8..0f483cf1cc404166d09729d55dad153c2259986e 100644 GIT binary patch literal 20134 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 my87AV5TA;A8Cy}!wEth=JvbG(pnn4sbwQ;7 From f7a8e3eddf633a79ed485cd338e9e6bf92f7d9f4 Mon Sep 17 00:00:00 2001 From: Raul Avila Catala Date: Sun, 16 Aug 2026 11:10:12 -0600 Subject: [PATCH 2/2] feat: gate UseWinApi() at compile-time (EAWIN002) and remove escape valve --- CONTRIBUTING.md | 8 +- README.md | 9 +- .../EasyBehaviorGenerator.cs | 111 +++++++++++++++++- .../CoreModule/Backend/Application.cs | 16 --- .../Backend/ResourcesDictionaryImpl.cs | 2 - .../Frontend/BehaviorBuilderImpl.cs | 3 - .../CoreModule/Frontend/IWin32State.cs | 8 -- .../SimpleEasyWinApp/Program.cs | 1 + src/Sample/Program.cs | 14 ++- 9 files changed, 130 insertions(+), 42 deletions(-) delete mode 100644 src/EasyWindowsApplication/CoreModule/Frontend/IWin32State.cs diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b5ea058..a35f0c7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -119,12 +119,17 @@ El proyecto `EasyWindowsApplication.Generators` contiene `EasyBehaviorGenerator` } ``` 3. **Valida** duplicados en tiempo de compilación (`EAWIN001`: "Name '{0}' is already used..."). -4. **Genera propiedades `internal static`** en la clase envolvente del `Behavior` (cuando se usa un lambda `.Behavior(bh => ...)`), para acceso directo sin el prefijo `bh.`. +4. **Valida el gate `UseWinApi()`** en tiempo de compilación (`EAWIN002`): si se declara un control (`View` cuyo tipo hereda de `IControl` de `Win32ControlsModule`) sin activar `UseWinApi()`, emite un error y **no genera** ningún accessor de controles. Las ventanas no se gatean. +5. **Genera propiedades `internal static`** en la clase envolvente del `Behavior` (cuando se usa un lambda `.Behavior(bh => ...)`), para acceso directo sin el prefijo `bh.`. ### Detección de candidatos - **Nombre**: invocaciones `.Name("string")` dentro de lambdas `View()` / `Window()` / `AlternativeWindow()`. - **Behavior**: invocaciones `.Behavior(bh => ...)` con un lambda (no method-group). Si se pasa un method-group, el enclosing-type no se detecta, pero las extensiones globales sí se generan. +- **`UseWinApi()`**: invocaciones `.UseWinApi()` (0 args) cuya verificación semántica confirme el símbolo `ISettingsBuilder.UseWinApi` (solo existe dentro de `.Resources(...).Setting(...)`). +- **Control**: invocaciones `View(lambda)` cuyo `T` hereda de `EasyWindowsApplication.Win32ControlsModule.Frontend.IControl`. + +> **Naming**: referenciar un control desde `Behavior` requiere `.Name()` explícito; sin `.Name()` no se genera accessor (el control sigue funcionando en el Layout por HWND). El auto-naming fue evaluado y **rechazado** (deriva semántica silenciosa al editar el Layout). --- @@ -160,6 +165,7 @@ Documenta aquí las decisiones de arquitectura relevantes. Usa ADRs (Architectur - **Controles como interfaces** (`IButton`, `ILabel`, ...) — permite que el Source Generator genere tipos débilesmente acoplados y facilita testing/mocking. - **MasterRouter** — centraliza el bucle de mensajes (`WndProc` → `DefWindowProcW`) y despacha eventos tipados (`WM.COMMAND` → `Click`). - **ControlAccess** — punto de acceso público para resolver controles por nombre desde el Behavior (usado por las propiedades generadas). Vive en `Share/Infrastructure` (técnico, `[EditorBrowsable(Never)]`). `SetController` se cablea en `Application.Behavior(...)`. +- **`UseWinApi()` como bifurcación explícita** — elegir controles nativos por HWND (comctl32) frente a los futuros controles GDI/GDI+ personalizados. Es un gate **compile-time** (`EAWIN002`), no un flag decorativo: el consumidor es el Source Generator, que decide qué accessors existen. Con los módulos MVU futuros, lo que reciba el lambda de `.Behavior(...)` dependerá del mismo flag. --- diff --git a/README.md b/README.md index 9237953..2308226 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ * **UX simplificada** — 4 bloques secuenciales (`Resources → Layout → Behavior → Initialize`) que reducen el esfuerzo de cableado y aceleran la entrega temprana. * **Fluent API declarativa** — El IntelliSense te guía de Resources a Layout a Behavior a Initialize, impidiendo equivocarte de orden. * **Source Generators** — Acceso tipado a controles por su nombre. Si te equivocas, falla en compilación, no en runtime. -* **Válvula de escape Win32** — `UseWinApi()` habilita el uso del módulo de controles Win32 nativos de alto rendimiento (`Win32ControlsModule`) cuando lo necesites. +* **Controles Win32 nativos** — `UseWinApi()` es el *gate* obligatorio para declarar controles de `Win32ControlsModule` en `Layout`: sin él el compilador lo rechaza (EAWIN002); con él, el generador emite los accessors tipados. ## Un vistazo al código @@ -24,6 +24,7 @@ using EasyWindowsApplication.WindowingModule.Frontend; int counter = 0; WindowsApplication + .Resources(rd => rd.Setting(st => st.UseWinApi())) .Layout(ly => ly .Window(iw => iw .Name("MainWindow") @@ -119,7 +120,7 @@ El framework incluye **dos plantillas** oficiales: La fase `Resources` registra todo lo que tu app necesita antes de dibujar: * **Assets** — imágenes, iconos (`.svg` → `.ico` multi-resolución) y fuentes. -* **Settings** — configuración embebida y persistencia (`UseWinApi`, `AppConfigFile`). +* **Settings** — configuración embebida y persistencia (`UseWinApi`, `AppConfigFile`). `UseWinApi` es el *gate* obligatorio para usar controles Win32 nativos. * **Services** — contenedor de inyección de dependencias. ```csharp @@ -159,7 +160,7 @@ EasyWindowsApplication/src/ El **Source Generator** analiza tu `Layout` en tiempo de compilación: cada `.Name("BtnGuardar")` dentro de un `View` o `Window` genera una propiedad tipada (`bh.BtnGuardar`) en `Behavior`. Al compilar, ya no necesitas localizar controles por string — si el nombre no coincide, el compilador te lo dice. En runtime, el `MasterRouter` centraliza el bucle de mensajes de Win32 y despacha eventos tipados (`Click`, etc.) de forma automatica. -Para usar los controles nativos del módulo Win32, activa `UseWinApi()` en Resources: +Para usar los controles nativos del módulo Win32, activa `UseWinApi()` en Resources. **Es obligatorio**: si declaras un control (`View`) sin él, el compilador lo rechaza con `EAWIN002` y no se genera ningún accessor: ```csharp WindowsApplication @@ -169,7 +170,7 @@ WindowsApplication .Initialize(); ``` -> **Nota**: el acceso de bajo nivel al HWND/WndProc (`WithWin32State`) es plomería interna del framework; se cableará automáticamente según el patrón MVU cuando `UseWinApi()` esté activo. +> **Nota**: el acceso de bajo nivel al HWND/WndProc es plomería interna del framework. Con los futuros módulos MVU, lo que reciba el lambda de `.Behavior(...)` dependerá de que `UseWinApi()` esté activo — resuelto en compile-time por el Source Generator. ## ¿Desarrollas el framework? diff --git a/src/EasyWindowsApplication.Generators/EasyBehaviorGenerator.cs b/src/EasyWindowsApplication.Generators/EasyBehaviorGenerator.cs index 5680fff..b1f4fa8 100644 --- a/src/EasyWindowsApplication.Generators/EasyBehaviorGenerator.cs +++ b/src/EasyWindowsApplication.Generators/EasyBehaviorGenerator.cs @@ -20,6 +20,14 @@ public sealed class EasyBehaviorGenerator : IIncrementalGenerator DiagnosticSeverity.Error, isEnabledByDefault: true); + private static readonly DiagnosticDescriptor UseWinApiRequiredRule = new( + id: "EAWIN002", + title: "UseWinApi required", + messageFormat: "UseWinApi() is required to use Win32 controls in Layout. Add .Resources(s => s.Setting(x => x.UseWinApi())).", + category: "EasyWindowsApplication", + DiagnosticSeverity.Error, + isEnabledByDefault: true); + public void Initialize(IncrementalGeneratorInitializationContext context) { var namedItems = context.SyntaxProvider.CreateSyntaxProvider( @@ -37,9 +45,32 @@ public void Initialize(IncrementalGeneratorInitializationContext context) var enclosingType = behaviorCalls.Collect(); - context.RegisterSourceOutput(collected.Combine(enclosingType), static (spc, pair) => + var useWinApi = context.SyntaxProvider.CreateSyntaxProvider( + predicate: static (node, _) => IsUseWinApiCandidate(node), + transform: static (ctx, _) => IsUseWinApiSymbol(ctx)) + .Where(static enabled => enabled) + .Collect() + .Select(static (items, _) => !items.IsDefaultOrEmpty); + + var controlItems = context.SyntaxProvider.CreateSyntaxProvider( + predicate: static (node, _) => IsViewControlCandidate(node), + transform: static (ctx, _) => ExtractControlLocation(ctx)) + .Where(static location => location is not null); + + var controlLocations = controlItems.Collect(); + + context.RegisterSourceOutput( + collected.Combine(enclosingType).Combine(useWinApi).Combine(controlLocations), + static (spc, quadruple) => { - var (items, enclosing) = pair; + var (((items, enclosing), usesWinApi), controls) = quadruple; + + if (!usesWinApi && !controls.IsDefaultOrEmpty) + { + foreach (var location in controls) + spc.ReportDiagnostic(Diagnostic.Create(UseWinApiRequiredRule, location)); + } + if (items.IsDefaultOrEmpty) return; var seen = new HashSet(); @@ -74,6 +105,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) foreach (var item in unique) { + if (!usesWinApi && !item.IsWindowType) continue; var propName = SanitizeName(item.Name); if (item.IsWindowType) { @@ -113,6 +145,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) foreach (var item in unique) { + if (!usesWinApi && !item.IsWindowType) continue; var propName = SanitizeName(item.Name); if (item.IsWindowType) { @@ -226,6 +259,80 @@ private static bool IsBehaviorCandidate(SyntaxNode node) return true; } + private static bool IsUseWinApiCandidate(SyntaxNode node) + { + if (node is not InvocationExpressionSyntax inv) + return false; + if (inv.Expression is not MemberAccessExpressionSyntax ma) + return false; + if (ma.Name.Identifier.Text != "UseWinApi") + return false; + if (inv.ArgumentList.Arguments.Count != 0) + return false; + return true; + } + + private static bool IsUseWinApiSymbol(GeneratorSyntaxContext context) + { + var inv = (InvocationExpressionSyntax)context.Node; + if (inv.Expression is not MemberAccessExpressionSyntax ma) + return false; + var symbol = context.SemanticModel.GetSymbolInfo(ma).Symbol; + return symbol is IMethodSymbol method + && method.Name == "UseWinApi" + && method.ContainingType is { } containingType + && containingType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + == "global::EasyWindowsApplication.CoreModule.Frontend.ISettingsBuilder"; + } + + private static bool IsViewControlCandidate(SyntaxNode node) + { + if (node is not InvocationExpressionSyntax inv) + return false; + if (inv.Expression is not MemberAccessExpressionSyntax ma) + return false; + if (ma.Name is not GenericNameSyntax generic) + return false; + if (generic.Identifier.Text != "View") + return false; + if (generic.TypeArgumentList.Arguments.Count != 1) + return false; + if (inv.ArgumentList.Arguments.Count != 1) + return false; + if (inv.ArgumentList.Arguments[0].Expression is not LambdaExpressionSyntax) + return false; + return true; + } + + private static Location ExtractControlLocation(GeneratorSyntaxContext context) + { + var inv = (InvocationExpressionSyntax)context.Node; + if (inv.Expression is not MemberAccessExpressionSyntax ma) + return null; + if (ma.Name is not GenericNameSyntax generic) + return null; + + var typeSymbol = context.SemanticModel.GetTypeInfo(generic.TypeArgumentList.Arguments[0]).Type; + if (typeSymbol is null) + return null; + + var iControl = context.SemanticModel.Compilation.GetTypeByMetadataName( + "EasyWindowsApplication.Win32ControlsModule.Frontend.IControl"); + if (iControl is null) + return null; + + if (typeSymbol.Equals(iControl, SymbolEqualityComparer.Default)) + return inv.GetLocation(); + + foreach (var iface in typeSymbol.AllInterfaces) + { + if (iface.Equals(iControl, SymbolEqualityComparer.Default)) + return inv.GetLocation(); + } + + return null; + } + private static EnclosingTypeInfo ExtractEnclosingType(GeneratorSyntaxContext context) { var current = context.Node.Parent; diff --git a/src/EasyWindowsApplication/CoreModule/Backend/Application.cs b/src/EasyWindowsApplication/CoreModule/Backend/Application.cs index 60d1427..3f1e10f 100644 --- a/src/EasyWindowsApplication/CoreModule/Backend/Application.cs +++ b/src/EasyWindowsApplication/CoreModule/Backend/Application.cs @@ -63,9 +63,6 @@ public void Initialize() RegisterMain(window); } - if (BehaviorBuilder.Win32Configurator is not null) - BehaviorBuilder.Win32Configurator(new Win32StateImpl(_registry)); - Procedures.RunMessageLoop(); } @@ -91,17 +88,4 @@ private void RegisterAlternative(WindowModel window) _registry.RegisterWindow(new WindowingModule.Backend.AlternativeWindowImpl( 0, 0, window.Name, window.Title, window.Width, window.Height, window.Position)); } -} - -internal sealed class Win32StateImpl : IWin32State -{ - private readonly HandleRegistry _registry; - internal Win32StateImpl(HandleRegistry registry) => _registry = registry; - - public T Get(string name) where T : View - { - var control = _registry.GetByName(name) - ?? throw new InvalidOperationException($"Control '{name}' not found in the layout."); - return (T)control; - } } \ No newline at end of file diff --git a/src/EasyWindowsApplication/CoreModule/Backend/ResourcesDictionaryImpl.cs b/src/EasyWindowsApplication/CoreModule/Backend/ResourcesDictionaryImpl.cs index b2aa786..11e69dd 100644 --- a/src/EasyWindowsApplication/CoreModule/Backend/ResourcesDictionaryImpl.cs +++ b/src/EasyWindowsApplication/CoreModule/Backend/ResourcesDictionaryImpl.cs @@ -22,12 +22,10 @@ public IResourcesDictionary Services(Action configure) internal sealed class SettingsBuilderImpl : ISettingsBuilder { - internal bool IsWinApiEnabled { get; private set; } internal AppConfigFileConfig AppConfig { get; } = new(); public ISettingsBuilder UseWinApi() { - IsWinApiEnabled = true; return this; } diff --git a/src/EasyWindowsApplication/CoreModule/Frontend/BehaviorBuilderImpl.cs b/src/EasyWindowsApplication/CoreModule/Frontend/BehaviorBuilderImpl.cs index 820cd81..300834b 100644 --- a/src/EasyWindowsApplication/CoreModule/Frontend/BehaviorBuilderImpl.cs +++ b/src/EasyWindowsApplication/CoreModule/Frontend/BehaviorBuilderImpl.cs @@ -5,11 +5,8 @@ namespace EasyWindowsApplication.CoreModule.Frontend; internal sealed class BehaviorBuilderImpl : IBehaviorBuilder, ControlAccess.IBehaviorServicesController { - internal Action? Win32Configurator { get; private set; } internal HandleRegistry? Registry { get; set; } - internal void SetWin32Configurator(Action configure) => Win32Configurator = configure; - T ControlAccess.IBehaviorServicesController.Get(string name) => (T)Registry!.GetByName(name)!; diff --git a/src/EasyWindowsApplication/CoreModule/Frontend/IWin32State.cs b/src/EasyWindowsApplication/CoreModule/Frontend/IWin32State.cs deleted file mode 100644 index 4fba86f..0000000 --- a/src/EasyWindowsApplication/CoreModule/Frontend/IWin32State.cs +++ /dev/null @@ -1,8 +0,0 @@ -using EasyWindowsApplication.Share.Infrastructure; - -namespace EasyWindowsApplication.CoreModule.Frontend; - -internal interface IWin32State -{ - T Get(string name) where T : View; -} diff --git a/src/ProjectTemplates/SimpleEasyWinApp/Program.cs b/src/ProjectTemplates/SimpleEasyWinApp/Program.cs index abb24d2..d5f5044 100644 --- a/src/ProjectTemplates/SimpleEasyWinApp/Program.cs +++ b/src/ProjectTemplates/SimpleEasyWinApp/Program.cs @@ -5,6 +5,7 @@ int counter = 0; WindowsApplication + .Resources(rd => rd.Setting(st => st.UseWinApi())) .Layout(ly => ly .Window(iw => iw .Name("MainWindow") diff --git a/src/Sample/Program.cs b/src/Sample/Program.cs index 333b816..2e5e305 100644 --- a/src/Sample/Program.cs +++ b/src/Sample/Program.cs @@ -2,10 +2,10 @@ using EasyWindowsApplication.Win32ControlsModule.Frontend; using EasyWindowsApplication.WindowingModule.Frontend; - int counter = 0; WindowsApplication + .Resources(rd => rd.Setting(st => st.UseWinApi())) .Layout(ly => ly .Window(iw => iw .Name("MainWindow") @@ -13,12 +13,14 @@ .Dimensions(420, 280) .Position(WindowPositionOnScreen.Center) .Content(c => c - .Children(ch => ch - .View(btn => btn + .Children(ch => + { + ch.View(btn => btn .Name("BtnIncrement") .Text("Click me") - ) - ) + ); + ch.View(lb => lb.Name("LbShowIncrement").Text("0 Increment")); + }) ) ) ) @@ -27,7 +29,7 @@ bh.BtnIncrement.OnClick(() => { counter++; - bh.BtnIncrement.Text = $"Click: {counter}"; + bh.LbShowIncrement.Text = $"{counter} Increment"; }); }) .Initialize();