diff --git a/README.md b/README.md index f04bdb8..9237953 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()` + `WithWin32State` para acceso directo al HWND y WndProc cuando lo necesites. +* **Válvula de escape Win32** — `UseWinApi()` habilita el uso del módulo de controles Win32 nativos de alto rendimiento (`Win32ControlsModule`) cuando lo necesites. ## Un vistazo al código @@ -159,19 +159,18 @@ 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 acceso total al HWND y WndProc, activa `UseWinApi()` en Resources y usa `WithWin32State`: +Para usar los controles nativos del módulo Win32, activa `UseWinApi()` en Resources: ```csharp WindowsApplication .Resources(r => r.Setting(s => s.UseWinApi())) .Layout(...) - .Behavior(b => b.WithWin32State(ctrl => { - var btn = ctrl.Get("BtnGuardar"); - btn.OnMessage(WM.COMMAND, (w, l) => { /* tu código Win32 puro */ }); - })) + .Behavior(bh => { ... }) .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. + ## ¿Desarrollas el framework? Toda la información para compilar desde el código fuente, entender la arquitectura de módulos, el pipeline de generación de iconos y las decisiones de diseño se encuentran en [`CONTRIBUTING.md`](CONTRIBUTING.md) y en la **Wiki del repositorio**. diff --git a/src/EasyWindowsApplication/CoreModule/Backend/Application.cs b/src/EasyWindowsApplication/CoreModule/Backend/Application.cs index 92e4a90..60d1427 100644 --- a/src/EasyWindowsApplication/CoreModule/Backend/Application.cs +++ b/src/EasyWindowsApplication/CoreModule/Backend/Application.cs @@ -63,8 +63,6 @@ public void Initialize() RegisterMain(window); } - BehaviorBuilder.ApplyPending(_registry); - if (BehaviorBuilder.Win32Configurator is not null) BehaviorBuilder.Win32Configurator(new Win32StateImpl(_registry)); diff --git a/src/EasyWindowsApplication/CoreModule/Frontend/BehaviorBuilderImpl.cs b/src/EasyWindowsApplication/CoreModule/Frontend/BehaviorBuilderImpl.cs index d3c0063..820cd81 100644 --- a/src/EasyWindowsApplication/CoreModule/Frontend/BehaviorBuilderImpl.cs +++ b/src/EasyWindowsApplication/CoreModule/Frontend/BehaviorBuilderImpl.cs @@ -1,7 +1,5 @@ using EasyWindowsApplication.CoreModule.Backend; using EasyWindowsApplication.Share.Infrastructure; -using EasyWindowsApplication.Win32ControlsModule.Frontend; -using EasyWindowsApplication.WindowingModule.Frontend; namespace EasyWindowsApplication.CoreModule.Frontend; @@ -9,49 +7,12 @@ internal sealed class BehaviorBuilderImpl : IBehaviorBuilder, ControlAccess.IBeh { internal Action? Win32Configurator { get; private set; } internal HandleRegistry? Registry { get; set; } - private readonly List<(string Name, Action Handler)> _pendingClicks = new(); - public IBehaviorBuilder OnClick(string controlName, Action handler) - { - var control = Registry?.GetByName(controlName); - if (control is IClickEventSource clickable) - { - clickable.AddClickHandler(handler); - } - else - { - _pendingClicks.Add((controlName, handler)); - } - return this; - } - - public IBehaviorBuilder WithWin32State(Action configure) - { - Win32Configurator = configure; - return this; - } - - public T Get(string name) where T : View - { - var control = Registry?.GetByName(name); - if (control is null) - throw new InvalidOperationException($"Control '{name}' not found."); - return (T)control; - } + internal void SetWin32Configurator(Action configure) => Win32Configurator = configure; T ControlAccess.IBehaviorServicesController.Get(string name) => (T)Registry!.GetByName(name)!; T ControlAccess.IBehaviorServicesController.GetWindow(string name) => (T)Registry!.GetWindow(name)!; - - internal void ApplyPending(HandleRegistry registry) - { - foreach (var (name, handler) in _pendingClicks) - { - if (registry.GetByName(name) is IClickEventSource clickable) - clickable.AddClickHandler(handler); - } - _pendingClicks.Clear(); - } } diff --git a/src/EasyWindowsApplication/CoreModule/Frontend/IBehaviorBuilder.cs b/src/EasyWindowsApplication/CoreModule/Frontend/IBehaviorBuilder.cs index 3196f51..0ffc43f 100644 --- a/src/EasyWindowsApplication/CoreModule/Frontend/IBehaviorBuilder.cs +++ b/src/EasyWindowsApplication/CoreModule/Frontend/IBehaviorBuilder.cs @@ -1,16 +1,3 @@ -using System.ComponentModel; -using EasyWindowsApplication.Share.Infrastructure; - namespace EasyWindowsApplication.CoreModule.Frontend; -public interface IBehaviorBuilder -{ - [EditorBrowsable(EditorBrowsableState.Never)] - IBehaviorBuilder OnClick(string controlName, Action handler); - - [EditorBrowsable(EditorBrowsableState.Never)] - IBehaviorBuilder WithWin32State(Action configure); - - [EditorBrowsable(EditorBrowsableState.Never)] - T Get(string name) where T : View; -} +public interface IBehaviorBuilder { } diff --git a/src/EasyWindowsApplication/CoreModule/Frontend/IWin32State.cs b/src/EasyWindowsApplication/CoreModule/Frontend/IWin32State.cs index 0c0f607..4fba86f 100644 --- a/src/EasyWindowsApplication/CoreModule/Frontend/IWin32State.cs +++ b/src/EasyWindowsApplication/CoreModule/Frontend/IWin32State.cs @@ -2,7 +2,7 @@ namespace EasyWindowsApplication.CoreModule.Frontend; -public interface IWin32State +internal interface IWin32State { T Get(string name) where T : View; } diff --git a/src/Sample/Program.cs b/src/Sample/Program.cs index 7059be0..333b816 100644 --- a/src/Sample/Program.cs +++ b/src/Sample/Program.cs @@ -22,11 +22,12 @@ ) ) ) - .Behavior(bh => bh - .BtnIncrement.OnClick(() => + .Behavior(bh => + { + bh.BtnIncrement.OnClick(() => { counter++; bh.BtnIncrement.Text = $"Click: {counter}"; - }) - ) + }); + }) .Initialize(); diff --git a/src/creators_vision.md b/src/creators_vision.md index 62a1c25..24e13c6 100644 --- a/src/creators_vision.md +++ b/src/creators_vision.md @@ -118,7 +118,7 @@ - **`Behavior`**: Enrutamiento de eventos y lógica. Gracias a los Source Generators, los nombres dados en el `Layout` (ej. `BtnGuardar`) aparecen aquí fuertemente tipados, eliminando los *Magic Strings*.Soporta dos modalidades: * Behavior (Implícito/Por defecto): Para un flujo moderno basado en estados y señales, ideal para la mayoría de aplicaciones. -* Behavior: Válvula de escape para desarrolladores avanzados. Expone el HWND y permite interceptar mensajes directamente en el WndProc (ej. WM_COMMAND, WM_PAINT) para cada control declarado en el Layout, ofreciendo el mismo nivel de control que C/C++ nativo. +* Behavior: Válvula de escape para desarrolladores avanzados. Expone el HWND y permite interceptar mensajes directamente en el WndProc (ej. WM_COMMAND, WM_PAINT) para cada control declarado en el Layout, ofreciendo el mismo nivel de control que C/C++ nativo. Es plomería interna (`IWin32State` es `internal`), se cablea automáticamente al activar `UseWinApi()`. - **`Initialize()`**: El nodo terminal que bloquea el hilo, cede el control al `CoreModule` y arranca el bucle de mensajes de Win32. En el flujo, es el nodo terminal de la Fluent API. No debe confundirse con un hook de ciclo de vida ni con las acciones del IDE (Build, Run). Su semántica es: en este punto la declaración está completa, todo ha sido definido y el sistema está habilitado para arrancar. Es el equivalente conceptual de `Build()` en el patrón Builder clásico, con la diferencia de que el nombre refleja que los Source Generators ya han actuado en tiempo de compilación, por lo que al llegar aquí no se construye nada, simplemente se lanza lo que ya está construido. - Plantilla para Visual Studio, esta debe servir como un "esqueleto" (scaffolding) para crear las carpetas y poner los archivos placeholder.