diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 31890bc..b0c4feb 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -67,20 +67,22 @@ El framework está organizado en módulos bajo `src/EasyWindowsApplication/`: | Módulo | Capa | Responsabilidad | |---|---|---| -| `CoreModule` | Backend + Frontend | Punto de entrada (`WindowsApplication`), fases del Fluent API, Resources, Behavior, MasterRouter, HandleRegistry | -| `LayoutModule` | Backend + Frontend | `ILayoutBuilder`, layouts (Grid, Stack, Dock), `IContentBuilder`, `IChildrenBuilder`, `IViewBuilder` | -| `Share` | Frontend (público) | Tipos públicos de la API de usuario: `Color`, `Thickness`, `IContentBuilder`, `LayoutLength`, `LayoutOptions` | -| `Share/Infrastructure` | Frontend (público técnico) | Tipos `public` **por razones técnicas** con `[EditorBrowsable(Never)]`: `ControlAccess`, `IconGenerator`, `ControlBase`, `View` | -| `Win32ControlsModule` | Backend + Frontend | Interfaces de controles (`IButton`, `ILabel`, `IEdit`, ...) y sus implementaciones Win32 | -| `WindowingModule` | Backend + Frontend | Ventanas (`IWindow`, `IAlternativeWindow`), posicionamiento (`WindowPositionOnScreen`) | +| `Core` | `internal` (100%) | Punto de entrada (`WindowsApplication` → `Share/IApplicationLayoutPhase`), `Application`, `MasterRouter` (trampolín HWND-based), `HandleRegistry` (ConcurrentDictionary HWND→Router), `ControlBase`, `IconGenerator` | +| `Core/Windowing` | `internal` | `WindowImpl`, `AlternativeWindowImpl`, `Procedures` (CreateMainWindow/CreateAlternativeWindow con `[UnmanagedCallersOnly]`), `UserControl` | +| `Core/LayoutEngine` | `internal` | `LayoutEngine`, `ILayoutBuilder`, layouts (Grid, Stack, Dock), `ChildrenBuilderImpl` (usa `ControlActivatorRegistry`), `ViewBuilderImpl` | +| `Common` | `internal` | `ControlActivatorRegistry` + `IControlActivator` (registry genérico `Register/Create`, `EnsureInitialized()` + `ControlActivatorGenerator` `IIncrementalGenerator` con `InternalsVisibleTo`, MEF compile-time) — rompe ciclo `Common ↔ Core` sin `ModuleInitializer` (`CA2255`) | +| `Share` | `public` | API usuario: `IWindow`, `IAlternativeWindow`, `IWindowConfig`, `IChildrenBuilder` (dual overload `Action>` + `Func,View>`), `View` struct readonly (`Name/Margin/Width/Height/Background/Dock/Grid/OnClick/Text`), `LayoutLength`, `GridDefinitions`, `IResourcesDictionary`, etc. | +| `Share/Infrastructure` | `public` técnico | `[EditorBrowsable(Never)]`: `ControlAccess` (punto usado por Source Generator), `ViewBase` base para controles custom (ej. `IpAddress`) | +| `Win32ControlsModule` | Frontend `public` / Backend `internal` | Interfaces (`IButton`, `ILabel` …) heredando `IControl` + Backend (`Button`, `Label` → `Core.ControlBase`) + `Win32ControlActivator` (`IControlActivator`, `internal`, auto-registrado vía `ControlActivatorGenerator` + `EnsureInitialized()` en `Core/Application`) | +| `Generators` | `Analyzer` | `EasyBehaviorGenerator` (FQN `Share` + `GetTypeByMetadataName`) + `ControlActivatorGenerator` (descubre `IControlActivator` en `Compilation`, genera `ControlActivatorRegistrations.g.cs`, `InternalsVisibleTo`) | ### Principios de organización -- **`Common`** — código compartido entre módulos, pero de acceso **`internal`** (no visible para consumidores). -- **`Share`** — código compartido entre módulos, pero de acceso **`public`** (expuesto a consumidores del framework). -- **`Share/Infrastructure`** — código compartido entre módulos, `public` **por razones técnicas** (lo alcanzan el código generado, los targets de build o la herencia interna), pero **NO** parte de la API de usuario → obligatorio `[EditorBrowsable(EditorBrowsableState.Never)]`. Si un archivo ahí no lo tiene, es un bug. -- **`CoreModule/Frontend`** — interfaces de las fases del Fluent API (`IApplicationLayoutPhase` → `IApplicationPostLayoutPhase` → `IApplicationPostBehaviorPhase`) que guían por IntelliSense el orden correcto. -- **`Backend`** — implementaciones `internal` de esas interfaces. +- **`Common`** — código compartido `internal` (no visible para consumidores). Contiene `ControlActivatorRegistry` (evita reflexión, factorías genéricas). +- **`Share`** — API `public` para consumidores. Todo lo que devuelve `WindowsApplication` (`IApplicationLayoutPhase` etc.) vive aquí. +- **`Share/Infrastructure`** — `public` por razones técnicas (`[EditorBrowsable(Never)]`). Solo `ControlAccess` y `ViewBase` permanecen aquí; `ControlBase`/`IconGenerator` migraron a `Core/`. +- **`Core/**`** — 100% `internal`. Nada `public` aquí (si aparece `public` es bug, debe ser `internal` o moverse a `Share`). Implementaciones puras. +- **`Win32ControlsModule/Frontend`** — `public` interfaces controles; `Backend` — `internal` implementaciones que heredan `Core.ControlBase` y se registran vía `IControlActivator`. --- @@ -118,10 +120,10 @@ Resources/ ## Source Generators -El proyecto `EasyWindowsApplication.Generators` contiene `EasyBehaviorGenerator`, un `IIncrementalGenerator` que: +El proyecto `EasyWindowsApplication.Generators` contiene `EasyBehaviorGenerator`, un `IIncrementalGenerator` que (FQN actualizados a `EasyWindowsApplication.Share.*` + robustez `GetTypeByMetadataName`): -1. **Detecta** llamadas `.Name("...")`** dentro de lambdas `View(...)` y `Window(...)` en el `Layout`. -2. **Genera** `EasyBehaviorExtensions` — un *extension property* para cada nombre, tipado con la interfaz del control: +1. **Detecta** llamadas `.Name("...")`** dentro de lambdas `View(...)` (ahora `View` es `Share/View` struct) y `Window(...)` / `AlternativeWindow(...)` en el `Layout`. +2. **Genera** `EasyBehaviorExtensions` — un *extension property* para cada nombre, tipado con la interfaz del control (FQN `global::EasyWindowsApplication.Share.IWindow` / `IAlternativeWindow` / `global::EasyWindowsApplication.Share.IBehaviorBuilder`): ```csharp // Auto-generado (ejemplo) extension(IBehaviorBuilder bh) { @@ -141,6 +143,9 @@ El proyecto `EasyWindowsApplication.Generators` contiene `EasyBehaviorGenerator` > **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). +- **Robustez FQN**: `ISettingsBuilder` se resuelve vía `Compilation.GetTypeByMetadataName("EasyWindowsApplication.Share.ISettingsBuilder")` + `SymbolEqualityComparer.Default` (no string comparison). Si `GetTypeByMetadataName` retorna `null`, el gate `UseWinApi` no se activa. +- **View**: el generador detecta `View` genérico con `T : IControl`; `IWindow`/`IAlternativeWindow` usan FQN `Share` (antes `WindowingModule.Frontend`). + --- ## Pipeline de iconos SVG→ICO @@ -171,11 +176,13 @@ Los proyectos de plantilla incluyen un `Target` de MSBuild (`GenerateAppIcon`) q Documenta aquí las decisiones de arquitectura relevantes. Usa ADRs (Architecture Decision Records) en `docs/adr/`. -- **Fluent API con interfaces de fase** — El IntelliSense guía el orden `Resources → Layout → Behavior → Initialize` mediante interfaces (`IApplicationLayoutPhase`, `IApplicationPostLayoutPhase`, `IApplicationPostBehaviorPhase`). -- **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. +- **Fluent API con interfaces de fase** — El IntelliSense guía el orden `Resources → Layout → Behavior → Initialize` mediante interfaces (`IApplicationLayoutPhase`, `IApplicationPostLayoutPhase`, `IApplicationPostBehaviorPhase`) ahora en `Share/` (antes `CoreModule/Frontend`). `WindowsApplication` devuelve `IApplicationLayoutPhase` público. +- **Controles como interfaces** (`IButton`, `ILabel`, ...) — permite que el Source Generator genere tipos débilesmente acoplados y facilita testing/mocking. `Button`/`Label` heredan `Core.ControlBase` (antes `Share.Infrastructure.ControlBase`). +- **MasterRouter** — centraliza el bucle de mensajes con trampolín `[UnmanagedCallersOnly(CallConvs=[Stdcall])]` + lookup HWND-based (`HandleRegistry.ConcurrentDictionary`), `try/catch` que evita tumbar proceso, despacha eventos tipados (`WM.COMMAND` → `Click`). El delegado estático sobrescrito fue eliminado (evita GC). +- **ControlAccess / ControlActivatorRegistry / ControlActivatorGenerator** — `ControlAccess` sigue en `Share/Infrastructure` (usado por `EasyBehaviorGenerator`). `ControlActivatorRegistry` en `Common/` (genérico `Register/Create`, `Shared` + `EnsureInitialized()` + `partial RegisterGeneratedActivators()`) rompe acoplamiento `Core → Win32ControlsModule` sin `ModuleInitializer` (`CA2255` en `Library`). `ControlActivatorGenerator` (`IIncrementalGenerator` en `Generators`, descubre `IControlActivator` en `Compilation` vía `GetTypeByMetadataName` + `AllInterfaces`, genera `ControlActivatorRegistrations.g.cs` con `new Win32ControlActivator().RegisterActivators(Shared)` y futuros plugins; usa `` para mantener `internal`. +- **View struct** — `Share/View` readonly struct `where T : class, IControl` con `Instance` + fluents (`Name/Margin/Width/Height/Background/Dock/Grid/OnClick/Text`). Dual overload en `IChildrenBuilder`: `View(Action>)` + `View(Func,View>)` instanciando `View(control)` vía `ControlActivatorRegistry`. +- **`UseWinApi()` como bifurcación explícita** — gate **compile-time** (`EAWIN002`), consumidor Source Generator decide accessors. `IconGenerator` migró `Share/Infrastructure` → `Core/` (ahora `Core.IconGenerator`, IcoGen usa `EasyWindowsApplication.Core`). +- **AlternativeWindow** — ahora funcional: `Procedures.CreateAlternativeWindow` registra clase `WS_POPUP|CAPTION|SYSMENU` y `HandleRegistry.RegisterRouter(hwnd, router)`; `Application.RegisterAlternative` crea hwnd real, aplica BackgroundBrush y `Show()`. --- diff --git a/README.md b/README.md index 2bb4b70..9007104 100644 --- a/README.md +++ b/README.md @@ -18,8 +18,8 @@ Crear una aplicación nativa nunca fue tan limpio. Todo se divide en 4 bloques l ```csharp using EasyWindowsApplication; +using EasyWindowsApplication.Share; using EasyWindowsApplication.Win32ControlsModule.Frontend; -using EasyWindowsApplication.WindowingModule.Frontend; int counter = 0; @@ -43,7 +43,7 @@ WindowsApplication ) .Behavior(bh => { - var btn = bh.BtnGuardar; // tipado por Source Generator + var btn = bh.BtnGuardar; // tipado por Source Generator (View wrapper) btn.OnClick(() => { counter++; @@ -152,14 +152,15 @@ WindowsApplication ``` EasyWindowsApplication/src/ ├── EasyWindowsApplication/ # Framework principal -│ ├── CoreModule/ # Fases del Fluent API, Resources, Behavior -│ ├── LayoutModule/ # ILayoutBuilder, Grid, Stack layouts -│ ├── Share/ # Tipos públicos (Color, Thickness, IContentBuilder) -│ │ └── Infrastructure/ # Tipos públicos técnicos ([EditorBrowsable(Never)]) -│ ├── Win32ControlsModule/ # IButton, ILabel, IEdit, ICheckBox, ... -│ ├── WindowingModule/ # IWindow, WindowPositionOnScreen -│ └── WindowsApplication.cs # Punto de entrada del Fluent API -├── EasyWindowsApplication.Generators/ # Source Generator (EasyBehaviorGenerator) +│ ├── Core/ # Fases Fluent API, Resources, Behavior, MasterRouter (100% internal) +│ │ ├── Windowing/ # WindowImpl, AlternativeWindowImpl, Procedures (HWND-based WndProc) +│ │ └── LayoutEngine/ # ILayoutBuilder, Grid/Stack/Dock layouts, ViewBuilder +│ ├── Common/ # ControlActivatorRegistry + ControlActivatorGenerator (IIncrementalGenerator, InternalsVisibleTo) +│ ├── Share/ # API pública usuario (IWindow, ILayoutBuilder, View struct, Color, Thickness) +│ │ └── Infrastructure/ # Técnicos [EditorBrowsable(Never)]: ControlAccess +│ ├── Win32ControlsModule/ # IButton, ILabel... + Backend (Button, Label → Core.ControlBase) +│ └── WindowsApplication.cs # Punto de entrada Fluent API (devuelve Share/IApplicationLayoutPhase) +├── EasyWindowsApplication.Generators/ # Source Generator (FQN Share.* + GetTypeByMetadataName) └── ProjectTemplates/ ├── EasyWinApp/ # Plantilla `dotnet new easywinapp` └── SimpleEasyWinApp/ # Plantilla `dotnet new simpleeasywinapp` @@ -167,7 +168,7 @@ EasyWindowsApplication/src/ ## Cómo funciona -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. +El **Source Generator** `EasyBehaviorGenerator` analiza tu `Layout` en tiempo de compilación: cada `.Name("BtnGuardar")` dentro de un `View` (struct `View` wrapper con `Action>` + `Func,View>`) o `Window` genera una propiedad tipada (`bh.BtnGuardar`) en `Behavior`. En runtime, el `MasterRouter` usa un trampolín `[UnmanagedCallersOnly]` con lookup HWND-based (`HandleRegistry.ConcurrentDictionary`) y despacha eventos tipados (`Click`, etc.); `ControlActivatorRegistry` en `Common/` (poblado en `Application.Initialize()` vía `ControlActivatorGenerator` — `IIncrementalGenerator` con `InternalsVisibleTo`, MEF compile-time) elimina el acoplamiento `Core → Win32Controls` sin `ModuleInitializer` (`CA2255`). 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: diff --git a/src/EasyWindowsApplication.Generators/ControlActivatorGenerator.cs b/src/EasyWindowsApplication.Generators/ControlActivatorGenerator.cs new file mode 100644 index 0000000..3e772cd --- /dev/null +++ b/src/EasyWindowsApplication.Generators/ControlActivatorGenerator.cs @@ -0,0 +1,97 @@ +using Microsoft.CodeAnalysis; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace EasyWindowsApplication.Generators; + +[Generator(LanguageNames.CSharp)] +public sealed class ControlActivatorGenerator : IIncrementalGenerator +{ + public void Initialize(IncrementalGeneratorInitializationContext context) + { + context.RegisterSourceOutput(context.CompilationProvider, (spc, compilation) => + { + // Solo generar en el assembly que define ControlActivatorRegistry (EasyWindowsApplication) + var registryType = compilation.Assembly.GetTypeByMetadataName("EasyWindowsApplication.Common.ControlActivatorRegistry"); + if (registryType is null) return; + + var activatorInterface = compilation.GetTypeByMetadataName("EasyWindowsApplication.Common.IControlActivator"); + if (activatorInterface is null) return; + + var candidates = new List(); + + // Recorre tipos del assembly principal (EasyWindowsApplication) — incluye Win32ControlActivator y futuros plugins en mismo ensamblado + CollectTypes(compilation.Assembly.GlobalNamespace, activatorInterface, candidates); + + var sb = new StringBuilder(); + sb.AppendLine("// "); + sb.AppendLine("#pragma warning disable CS0612, CS0618, CS1591, CA2255"); + sb.AppendLine("using EasyWindowsApplication.Common;"); + sb.AppendLine(); + sb.AppendLine("namespace EasyWindowsApplication.Common;"); + sb.AppendLine(); + sb.AppendLine("internal sealed partial class ControlActivatorRegistry"); + sb.AppendLine("{"); + sb.AppendLine(" static partial void RegisterGeneratedActivators()"); + sb.AppendLine(" {"); + + // Fallback hardcode para Win32ControlsModule (garantiza registro incluso si escaneo falla) + var hasWin32 = candidates.Any(c => c.Name == "Win32ControlActivator"); + if (!hasWin32) + { + // Verificación por nombre si el escaneo por AllInterfaces falló por InternalsVisibleTo timing + sb.AppendLine(" new global::EasyWindowsApplication.Win32ControlsModule.Backend.Win32ControlActivator().RegisterActivators(Shared);"); + } + + foreach (var type in candidates.Distinct(SymbolEqualityComparer.Default).OrderBy(t => t.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat))) + { + // Evitar generar la interfaz misma y evitar duplicar Win32 si ya hardcodeado + if (SymbolEqualityComparer.Default.Equals(type, activatorInterface)) continue; + if (type.Name == "Win32ControlActivator" && !hasWin32) continue; // ya emitido arriba + var fqn = type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); + sb.AppendLine($" new {fqn}().RegisterActivators(Shared);"); + } + + sb.AppendLine(" }"); + sb.AppendLine("}"); + + spc.AddSource("ControlActivatorRegistrations.g.cs", sb.ToString()); + }); + } + + private static void CollectTypes(INamespaceSymbol ns, INamedTypeSymbol activatorInterface, List candidates) + { + foreach (var member in ns.GetMembers()) + { + if (member is INamespaceSymbol nestedNs) + { + CollectTypes(nestedNs, activatorInterface, candidates); + } + else if (member is INamedTypeSymbol type) + { + CollectNestedTypes(type, activatorInterface, candidates); + } + } + } + + private static void CollectNestedTypes(INamedTypeSymbol type, INamedTypeSymbol activatorInterface, List candidates) + { + // Incluir el tipo si implementa IControlActivator + foreach (var iface in type.AllInterfaces) + { + if (SymbolEqualityComparer.Default.Equals(iface, activatorInterface)) + { + if (!type.IsAbstract) + candidates.Add(type); + break; + } + } + + // Recursión en tipos anidados + foreach (var nested in type.GetTypeMembers()) + { + CollectNestedTypes(nested, activatorInterface, candidates); + } + } +} diff --git a/src/EasyWindowsApplication.Generators/EasyBehaviorGenerator.cs b/src/EasyWindowsApplication.Generators/EasyBehaviorGenerator.cs index ec43c69..c609258 100644 --- a/src/EasyWindowsApplication.Generators/EasyBehaviorGenerator.cs +++ b/src/EasyWindowsApplication.Generators/EasyBehaviorGenerator.cs @@ -1,4 +1,4 @@ - + using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; @@ -93,7 +93,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) sb.AppendLine("#pragma warning disable CS0612, CS0618, CS1591"); sb.AppendLine(); - var ib = "global::EasyWindowsApplication.CoreModule.Frontend.IBehaviorBuilder"; + var ib = "global::EasyWindowsApplication.Share.IBehaviorBuilder"; sb.AppendLine("namespace EasyWindowsApplication"); sb.AppendLine("{"); @@ -233,12 +233,12 @@ private static bool IsNameCandidate(SyntaxNode node) if (parentMa.Name.Identifier.Text == "Window") { - return new NamedInfo(name, "global::EasyWindowsApplication.WindowingModule.Frontend.IWindow", isWindowType: true, location); + return new NamedInfo(name, "global::EasyWindowsApplication.Share.IWindow", isWindowType: true, location); } if (parentMa.Name.Identifier.Text == "AlternativeWindow") { - return new NamedInfo(name, "global::EasyWindowsApplication.WindowingModule.Frontend.IAlternativeWindow", isWindowType: true, location); + return new NamedInfo(name, "global::EasyWindowsApplication.Share.IAlternativeWindow", isWindowType: true, location); } return null; @@ -278,11 +278,17 @@ private static bool IsUseWinApiSymbol(GeneratorSyntaxContext context) 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"; + if (symbol is not IMethodSymbol method || method.Name != "UseWinApi") + return false; + if (method.ContainingType is not { } containingType) + return false; + var iSettingsBuilder = context.SemanticModel.Compilation.GetTypeByMetadataName( + "EasyWindowsApplication.Share.ISettingsBuilder"); + if (iSettingsBuilder is null) + { + return false; + } + return containingType.Equals(iSettingsBuilder, SymbolEqualityComparer.Default); } private static bool IsViewControlCandidate(SyntaxNode node) diff --git a/src/EasyWindowsApplication/Common/ControlActivatorRegistry.cs b/src/EasyWindowsApplication/Common/ControlActivatorRegistry.cs new file mode 100644 index 0000000..dbf9314 --- /dev/null +++ b/src/EasyWindowsApplication/Common/ControlActivatorRegistry.cs @@ -0,0 +1,41 @@ +namespace EasyWindowsApplication.Common; + +internal sealed partial class ControlActivatorRegistry +{ + private readonly Dictionary> _factories = new(); + private static bool _inited; + private static readonly object _initLock = new(); + + internal void Register(Func factory) where T : class + { + _factories[typeof(T)] = () => factory()!; + } + + internal T Create() where T : class + { + if (_factories.TryGetValue(typeof(T), out var f)) + return (T)f(); + throw new InvalidOperationException($"No control implementation registered for '{typeof(T).FullName}'. Did you forget to add InternalsVisibleTo or reference the plugin assembly?"); + } + + internal object CreateFor(Type type) + { + if (_factories.TryGetValue(type, out var f)) + return f(); + throw new InvalidOperationException($"No control implementation registered for '{type.FullName}'."); + } + + internal static ControlActivatorRegistry Shared { get; } = new(); + + internal static void EnsureInitialized() + { + lock (_initLock) + { + if (_inited) return; + RegisterGeneratedActivators(); + _inited = true; + } + } + + static partial void RegisterGeneratedActivators(); +} diff --git a/src/EasyWindowsApplication/Common/IControlActivator.cs b/src/EasyWindowsApplication/Common/IControlActivator.cs new file mode 100644 index 0000000..7452353 --- /dev/null +++ b/src/EasyWindowsApplication/Common/IControlActivator.cs @@ -0,0 +1,6 @@ +namespace EasyWindowsApplication.Common; + +internal interface IControlActivator +{ + void RegisterActivators(ControlActivatorRegistry registry); +} diff --git a/src/EasyWindowsApplication/CoreModule/Backend/Application.cs b/src/EasyWindowsApplication/Core/Application.cs similarity index 71% rename from src/EasyWindowsApplication/CoreModule/Backend/Application.cs rename to src/EasyWindowsApplication/Core/Application.cs index 3f1e10f..ebee4eb 100644 --- a/src/EasyWindowsApplication/CoreModule/Backend/Application.cs +++ b/src/EasyWindowsApplication/Core/Application.cs @@ -1,11 +1,10 @@ -using EasyWindowsApplication.CoreModule.Frontend; -using EasyWindowsApplication.LayoutModule.Backend; -using EasyWindowsApplication.LayoutModule.Frontend; +using EasyWindowsApplication.Common; using EasyWindowsApplication.Share; +using EasyWindowsApplication.Core.LayoutEngine; +using EasyWindowsApplication.Core.Windowing; using EasyWindowsApplication.Share.Infrastructure; -using EasyWindowsApplication.WindowingModule.Frontend; -namespace EasyWindowsApplication.CoreModule.Backend; +namespace EasyWindowsApplication.Core; internal sealed class Application : IApplicationLayoutPhase, @@ -53,6 +52,7 @@ internal void AddWindow(WindowModel window) public void Initialize() { + ControlActivatorRegistry.EnsureInitialized(); _router = new MasterRouter(_registry); foreach (var window in _windows) @@ -68,9 +68,9 @@ public void Initialize() private void RegisterMain(WindowModel window) { - var hwnd = WindowingModule.Backend.Procedures.CreateMainWindow(_router, window.Title, window.Width, window.Height); + var hwnd = Core.Windowing.Procedures.CreateMainWindow(_router, window.Title, window.Width, window.Height); - var win = new WindowingModule.Backend.WindowImpl( + var win = new Core.Windowing.WindowImpl( hwnd, window.Name, window.Title, window.Width, window.Height, window.Position); if (window.Background.HasValue) @@ -85,7 +85,14 @@ private void RegisterMain(WindowModel window) private void RegisterAlternative(WindowModel window) { - _registry.RegisterWindow(new WindowingModule.Backend.AlternativeWindowImpl( - 0, 0, window.Name, window.Title, window.Width, window.Height, window.Position)); + var hwnd = Core.Windowing.Procedures.CreateAlternativeWindow(_router, 0, window.Title, window.Width, window.Height); + var win = new Core.Windowing.AlternativeWindowImpl(hwnd, 0, window.Name, window.Title, window.Width, window.Height, window.Position); + if (window.Background.HasValue) + { + var brush = Win32.CreateSolidBrush(window.Background.Value.ToCOLORREF()); + _router.RegisterWindowBackgroundBrush(hwnd, brush); + } + _registry.RegisterWindow(win); + win.Show(); } -} \ No newline at end of file +} diff --git a/src/EasyWindowsApplication/CoreModule/Frontend/BehaviorBuilderImpl.cs b/src/EasyWindowsApplication/Core/BehaviorBuilderImpl.cs similarity index 81% rename from src/EasyWindowsApplication/CoreModule/Frontend/BehaviorBuilderImpl.cs rename to src/EasyWindowsApplication/Core/BehaviorBuilderImpl.cs index 300834b..5d2852b 100644 --- a/src/EasyWindowsApplication/CoreModule/Frontend/BehaviorBuilderImpl.cs +++ b/src/EasyWindowsApplication/Core/BehaviorBuilderImpl.cs @@ -1,7 +1,7 @@ -using EasyWindowsApplication.CoreModule.Backend; +using EasyWindowsApplication.Share; using EasyWindowsApplication.Share.Infrastructure; -namespace EasyWindowsApplication.CoreModule.Frontend; +namespace EasyWindowsApplication.Core; internal sealed class BehaviorBuilderImpl : IBehaviorBuilder, ControlAccess.IBehaviorServicesController { diff --git a/src/EasyWindowsApplication/CoreModule/Backend/Constants.cs b/src/EasyWindowsApplication/Core/Constants.cs similarity index 98% rename from src/EasyWindowsApplication/CoreModule/Backend/Constants.cs rename to src/EasyWindowsApplication/Core/Constants.cs index d49f152..dd7eb01 100644 --- a/src/EasyWindowsApplication/CoreModule/Backend/Constants.cs +++ b/src/EasyWindowsApplication/Core/Constants.cs @@ -1,4 +1,4 @@ -namespace EasyWindowsApplication.CoreModule.Backend; +namespace EasyWindowsApplication.Core; internal static class WS { diff --git a/src/EasyWindowsApplication/Share/Infrastructure/ControlBase.cs b/src/EasyWindowsApplication/Core/ControlBase.cs similarity index 96% rename from src/EasyWindowsApplication/Share/Infrastructure/ControlBase.cs rename to src/EasyWindowsApplication/Core/ControlBase.cs index 939f888..0f33e1c 100644 --- a/src/EasyWindowsApplication/Share/Infrastructure/ControlBase.cs +++ b/src/EasyWindowsApplication/Core/ControlBase.cs @@ -1,13 +1,12 @@ -using System.ComponentModel; +using System.ComponentModel; using System.Runtime.InteropServices; -using EasyWindowsApplication.CoreModule.Backend; -using EasyWindowsApplication.LayoutModule.Backend; -using EasyWindowsApplication.LayoutModule.Frontend; + +using EasyWindowsApplication.Core.LayoutEngine; using EasyWindowsApplication.Share; using EasyWindowsApplication.Win32ControlsModule.Frontend; -using static EasyWindowsApplication.CoreModule.Backend.Win32; +using static EasyWindowsApplication.Core.Win32; -namespace EasyWindowsApplication.Share.Infrastructure; +namespace EasyWindowsApplication.Core; [EditorBrowsable(EditorBrowsableState.Never)] public abstract class ControlBase : IControl, IClickEventSource, ILayoutable, IDockable diff --git a/src/EasyWindowsApplication/CoreModule/Backend/Entities.cs b/src/EasyWindowsApplication/Core/Entities.cs similarity index 95% rename from src/EasyWindowsApplication/CoreModule/Backend/Entities.cs rename to src/EasyWindowsApplication/Core/Entities.cs index 6857490..e6e84c1 100644 --- a/src/EasyWindowsApplication/CoreModule/Backend/Entities.cs +++ b/src/EasyWindowsApplication/Core/Entities.cs @@ -1,6 +1,6 @@ -using System.Runtime.InteropServices; +using System.Runtime.InteropServices; -namespace EasyWindowsApplication.CoreModule.Backend; +namespace EasyWindowsApplication.Core; [StructLayout(LayoutKind.Sequential)] internal struct SIZE diff --git a/src/EasyWindowsApplication/CoreModule/Backend/HandleRegistry.cs b/src/EasyWindowsApplication/Core/HandleRegistry.cs similarity index 71% rename from src/EasyWindowsApplication/CoreModule/Backend/HandleRegistry.cs rename to src/EasyWindowsApplication/Core/HandleRegistry.cs index c25b5e0..d9c8a63 100644 --- a/src/EasyWindowsApplication/CoreModule/Backend/HandleRegistry.cs +++ b/src/EasyWindowsApplication/Core/HandleRegistry.cs @@ -1,10 +1,17 @@ +using System.Collections.Concurrent; using EasyWindowsApplication.Win32ControlsModule.Frontend; -using EasyWindowsApplication.WindowingModule.Frontend; +using EasyWindowsApplication.Share; -namespace EasyWindowsApplication.CoreModule.Backend; +namespace EasyWindowsApplication.Core; internal sealed class HandleRegistry { + private static readonly ConcurrentDictionary _routersByHwnd = new(); + + internal static void RegisterRouter(nint hwnd, MasterRouter router) => _routersByHwnd[hwnd] = router; + internal static void UnregisterRouter(nint hwnd) => _routersByHwnd.TryRemove(hwnd, out _); + internal static MasterRouter? GetRouter(nint hwnd) => _routersByHwnd.TryGetValue(hwnd, out var r) ? r : null; + private readonly Dictionary _hwndToControl = new(); private readonly Dictionary _nameToControl = new(StringComparer.Ordinal); private readonly Dictionary _windowByName = new(StringComparer.Ordinal); diff --git a/src/EasyWindowsApplication/Share/Infrastructure/IconGenerator.cs b/src/EasyWindowsApplication/Core/IconGenerator.cs similarity index 96% rename from src/EasyWindowsApplication/Share/Infrastructure/IconGenerator.cs rename to src/EasyWindowsApplication/Core/IconGenerator.cs index b1c99eb..92db279 100644 --- a/src/EasyWindowsApplication/Share/Infrastructure/IconGenerator.cs +++ b/src/EasyWindowsApplication/Core/IconGenerator.cs @@ -1,8 +1,8 @@ -using System.ComponentModel; +using System.ComponentModel; using SkiaSharp; using Svg.Skia; -namespace EasyWindowsApplication.Share.Infrastructure; +namespace EasyWindowsApplication.Core; [EditorBrowsable(EditorBrowsableState.Never)] public static class IconGenerator diff --git a/src/EasyWindowsApplication/LayoutModule/Backend/ChildrenBuilderImpl.cs b/src/EasyWindowsApplication/Core/LayoutEngine/ChildrenBuilderImpl.cs similarity index 54% rename from src/EasyWindowsApplication/LayoutModule/Backend/ChildrenBuilderImpl.cs rename to src/EasyWindowsApplication/Core/LayoutEngine/ChildrenBuilderImpl.cs index b5b6001..68cfeeb 100644 --- a/src/EasyWindowsApplication/LayoutModule/Backend/ChildrenBuilderImpl.cs +++ b/src/EasyWindowsApplication/Core/LayoutEngine/ChildrenBuilderImpl.cs @@ -1,9 +1,9 @@ -using EasyWindowsApplication.CoreModule.Backend; +using EasyWindowsApplication.Common; +using EasyWindowsApplication.Core; using EasyWindowsApplication.Share; using EasyWindowsApplication.Win32ControlsModule.Frontend; -using EasyWindowsApplication.WindowingModule.Frontend; -namespace EasyWindowsApplication.LayoutModule.Backend; +namespace EasyWindowsApplication.Core.LayoutEngine; internal sealed class ChildrenBuilderImpl : IChildrenBuilder { @@ -11,15 +11,30 @@ internal sealed class ChildrenBuilderImpl : IChildrenBuilder internal ChildrenBuilderImpl(ContentModel content) => _content = content; - public IChildrenBuilder View(Action configure) where T : IControl + public IChildrenBuilder View(Action> configure) where T : class, IControl { - var control = ControlInstanceProvider.Create(); - configure(control); + var control = ControlActivatorRegistry.Shared.Create(); + var view = new View(control); + configure(view); + + _content.Children.Add(new ViewModel + { + Name = view.Instance.Name, + Control = view.Instance + }); + return this; + } + + public IChildrenBuilder View(Func, View> configure) where T : class, IControl + { + var control = ControlActivatorRegistry.Shared.Create(); + var view = new View(control); + var result = configure(view); _content.Children.Add(new ViewModel { - Name = control.Name, - Control = control + Name = result.Instance.Name, + Control = result.Instance }); return this; } @@ -40,4 +55,4 @@ public IChildrenBuilder View(Action configure) public IChildrenBuilder DockTop() => this; public IChildrenBuilder DockRight() => this; public IChildrenBuilder DockBottom() => this; -} \ No newline at end of file +} diff --git a/src/EasyWindowsApplication/LayoutModule/Backend/ContentBuilderImpl.cs b/src/EasyWindowsApplication/Core/LayoutEngine/ContentBuilderImpl.cs similarity index 88% rename from src/EasyWindowsApplication/LayoutModule/Backend/ContentBuilderImpl.cs rename to src/EasyWindowsApplication/Core/LayoutEngine/ContentBuilderImpl.cs index 6de9d10..22cc175 100644 --- a/src/EasyWindowsApplication/LayoutModule/Backend/ContentBuilderImpl.cs +++ b/src/EasyWindowsApplication/Core/LayoutEngine/ContentBuilderImpl.cs @@ -1,9 +1,7 @@ -using EasyWindowsApplication.CoreModule.Backend; -using EasyWindowsApplication.LayoutModule.Frontend; +using EasyWindowsApplication.Core; using EasyWindowsApplication.Share; -using EasyWindowsApplication.WindowingModule.Frontend; -namespace EasyWindowsApplication.LayoutModule.Backend; +namespace EasyWindowsApplication.Core.LayoutEngine; internal sealed class ContentBuilderImpl : IContentBuilder { @@ -50,4 +48,4 @@ public IContentBuilder Children(Action configure) public IContentBuilder ColumnSpacing(float pixels) => this; public IContentBuilder DefaultDock(DockPosition position) => this; public IContentBuilder ShouldExpandLastChild(bool expand) => this; -} \ No newline at end of file +} diff --git a/src/EasyWindowsApplication/LayoutModule/Backend/DockLayoutStrategy.cs b/src/EasyWindowsApplication/Core/LayoutEngine/DockLayoutStrategy.cs similarity index 96% rename from src/EasyWindowsApplication/LayoutModule/Backend/DockLayoutStrategy.cs rename to src/EasyWindowsApplication/Core/LayoutEngine/DockLayoutStrategy.cs index 6a9065a..5cbcb95 100644 --- a/src/EasyWindowsApplication/LayoutModule/Backend/DockLayoutStrategy.cs +++ b/src/EasyWindowsApplication/Core/LayoutEngine/DockLayoutStrategy.cs @@ -1,7 +1,6 @@ -using EasyWindowsApplication.LayoutModule.Frontend; -using EasyWindowsApplication.Share; +using EasyWindowsApplication.Share; -namespace EasyWindowsApplication.LayoutModule.Backend; +namespace EasyWindowsApplication.Core.LayoutEngine; internal sealed class DockLayoutStrategy : ILayoutStrategy { diff --git a/src/EasyWindowsApplication/LayoutModule/Backend/GridLayoutStrategy.cs b/src/EasyWindowsApplication/Core/LayoutEngine/GridLayoutStrategy.cs similarity index 96% rename from src/EasyWindowsApplication/LayoutModule/Backend/GridLayoutStrategy.cs rename to src/EasyWindowsApplication/Core/LayoutEngine/GridLayoutStrategy.cs index 8fe80c4..2dbc678 100644 --- a/src/EasyWindowsApplication/LayoutModule/Backend/GridLayoutStrategy.cs +++ b/src/EasyWindowsApplication/Core/LayoutEngine/GridLayoutStrategy.cs @@ -1,7 +1,6 @@ -using EasyWindowsApplication.LayoutModule.Frontend; -using EasyWindowsApplication.Share; +using EasyWindowsApplication.Share; -namespace EasyWindowsApplication.LayoutModule.Backend; +namespace EasyWindowsApplication.Core.LayoutEngine; internal sealed class GridLayoutStrategy : ILayoutStrategy { diff --git a/src/EasyWindowsApplication/LayoutModule/Backend/HorizontalStackLayoutStrategy.cs b/src/EasyWindowsApplication/Core/LayoutEngine/HorizontalStackLayoutStrategy.cs similarity index 95% rename from src/EasyWindowsApplication/LayoutModule/Backend/HorizontalStackLayoutStrategy.cs rename to src/EasyWindowsApplication/Core/LayoutEngine/HorizontalStackLayoutStrategy.cs index d5c692d..8e47659 100644 --- a/src/EasyWindowsApplication/LayoutModule/Backend/HorizontalStackLayoutStrategy.cs +++ b/src/EasyWindowsApplication/Core/LayoutEngine/HorizontalStackLayoutStrategy.cs @@ -1,7 +1,6 @@ -using EasyWindowsApplication.LayoutModule.Frontend; -using EasyWindowsApplication.Share; +using EasyWindowsApplication.Share; -namespace EasyWindowsApplication.LayoutModule.Backend; +namespace EasyWindowsApplication.Core.LayoutEngine; internal sealed class HorizontalStackLayoutStrategy : ILayoutStrategy { diff --git a/src/EasyWindowsApplication/LayoutModule/Backend/ILayoutStrategy.cs b/src/EasyWindowsApplication/Core/LayoutEngine/ILayoutStrategy.cs similarity index 75% rename from src/EasyWindowsApplication/LayoutModule/Backend/ILayoutStrategy.cs rename to src/EasyWindowsApplication/Core/LayoutEngine/ILayoutStrategy.cs index b557a58..9c6fe4b 100644 --- a/src/EasyWindowsApplication/LayoutModule/Backend/ILayoutStrategy.cs +++ b/src/EasyWindowsApplication/Core/LayoutEngine/ILayoutStrategy.cs @@ -1,6 +1,6 @@ -using EasyWindowsApplication.Share; +using EasyWindowsApplication.Share; -namespace EasyWindowsApplication.LayoutModule.Backend; +namespace EasyWindowsApplication.Core.LayoutEngine; internal interface ILayoutStrategy { diff --git a/src/EasyWindowsApplication/LayoutModule/Backend/ILayoutable.cs b/src/EasyWindowsApplication/Core/LayoutEngine/ILayoutable.cs similarity index 75% rename from src/EasyWindowsApplication/LayoutModule/Backend/ILayoutable.cs rename to src/EasyWindowsApplication/Core/LayoutEngine/ILayoutable.cs index 0b057a1..d26d8b3 100644 --- a/src/EasyWindowsApplication/LayoutModule/Backend/ILayoutable.cs +++ b/src/EasyWindowsApplication/Core/LayoutEngine/ILayoutable.cs @@ -1,7 +1,6 @@ -using EasyWindowsApplication.LayoutModule.Frontend; -using EasyWindowsApplication.Share; +using EasyWindowsApplication.Share; -namespace EasyWindowsApplication.LayoutModule.Backend; +namespace EasyWindowsApplication.Core.LayoutEngine; internal interface ILayoutable { diff --git a/src/EasyWindowsApplication/LayoutModule/Backend/LayoutBuilderImpl.cs b/src/EasyWindowsApplication/Core/LayoutEngine/LayoutBuilderImpl.cs similarity index 82% rename from src/EasyWindowsApplication/LayoutModule/Backend/LayoutBuilderImpl.cs rename to src/EasyWindowsApplication/Core/LayoutEngine/LayoutBuilderImpl.cs index 7346bf9..e7556e4 100644 --- a/src/EasyWindowsApplication/LayoutModule/Backend/LayoutBuilderImpl.cs +++ b/src/EasyWindowsApplication/Core/LayoutEngine/LayoutBuilderImpl.cs @@ -1,8 +1,7 @@ -using EasyWindowsApplication.CoreModule.Backend; -using EasyWindowsApplication.LayoutModule.Frontend; -using EasyWindowsApplication.WindowingModule.Frontend; +using EasyWindowsApplication.Core; +using EasyWindowsApplication.Share; -namespace EasyWindowsApplication.LayoutModule.Backend; +namespace EasyWindowsApplication.Core.LayoutEngine; internal sealed class LayoutBuilderImpl : ILayoutBuilder, ILayoutBuilderAfterWindow { @@ -39,4 +38,4 @@ public ILayoutBuilderAfterWindow AlternativeWindow(Action configu _app.AddWindow(model); return this; } -} \ No newline at end of file +} diff --git a/src/EasyWindowsApplication/LayoutModule/Backend/LayoutEngine.cs b/src/EasyWindowsApplication/Core/LayoutEngine/LayoutEngine.cs similarity index 87% rename from src/EasyWindowsApplication/LayoutModule/Backend/LayoutEngine.cs rename to src/EasyWindowsApplication/Core/LayoutEngine/LayoutEngine.cs index 5d207c4..e6b629a 100644 --- a/src/EasyWindowsApplication/LayoutModule/Backend/LayoutEngine.cs +++ b/src/EasyWindowsApplication/Core/LayoutEngine/LayoutEngine.cs @@ -1,6 +1,6 @@ -using EasyWindowsApplication.Share; +using EasyWindowsApplication.Share; -namespace EasyWindowsApplication.LayoutModule.Backend; +namespace EasyWindowsApplication.Core.LayoutEngine; internal sealed class LayoutEngine { diff --git a/src/EasyWindowsApplication/LayoutModule/Backend/VerticalStackLayoutStrategy.cs b/src/EasyWindowsApplication/Core/LayoutEngine/VerticalStackLayoutStrategy.cs similarity index 96% rename from src/EasyWindowsApplication/LayoutModule/Backend/VerticalStackLayoutStrategy.cs rename to src/EasyWindowsApplication/Core/LayoutEngine/VerticalStackLayoutStrategy.cs index d6c9209..c541b82 100644 --- a/src/EasyWindowsApplication/LayoutModule/Backend/VerticalStackLayoutStrategy.cs +++ b/src/EasyWindowsApplication/Core/LayoutEngine/VerticalStackLayoutStrategy.cs @@ -1,7 +1,6 @@ -using EasyWindowsApplication.LayoutModule.Frontend; -using EasyWindowsApplication.Share; +using EasyWindowsApplication.Share; -namespace EasyWindowsApplication.LayoutModule.Backend; +namespace EasyWindowsApplication.Core.LayoutEngine; internal sealed class VerticalStackLayoutStrategy : ILayoutStrategy { diff --git a/src/EasyWindowsApplication/LayoutModule/Backend/ViewBuilderImpl.cs b/src/EasyWindowsApplication/Core/LayoutEngine/ViewBuilderImpl.cs similarity index 86% rename from src/EasyWindowsApplication/LayoutModule/Backend/ViewBuilderImpl.cs rename to src/EasyWindowsApplication/Core/LayoutEngine/ViewBuilderImpl.cs index 18076a6..0042b07 100644 --- a/src/EasyWindowsApplication/LayoutModule/Backend/ViewBuilderImpl.cs +++ b/src/EasyWindowsApplication/Core/LayoutEngine/ViewBuilderImpl.cs @@ -1,9 +1,7 @@ -using EasyWindowsApplication.CoreModule.Backend; -using EasyWindowsApplication.LayoutModule.Frontend; +using EasyWindowsApplication.Core; using EasyWindowsApplication.Share; -using EasyWindowsApplication.WindowingModule.Frontend; -namespace EasyWindowsApplication.LayoutModule.Backend; +namespace EasyWindowsApplication.Core.LayoutEngine; internal sealed class ViewBuilderImpl : IViewBuilder { @@ -36,4 +34,4 @@ public IViewBuilder Content(Action configure) Model.SubContent = content.Model; return this; } -} \ No newline at end of file +} diff --git a/src/EasyWindowsApplication/LayoutModule/Backend/WindowConfigBuilder.cs b/src/EasyWindowsApplication/Core/LayoutEngine/WindowConfigBuilder.cs similarity index 87% rename from src/EasyWindowsApplication/LayoutModule/Backend/WindowConfigBuilder.cs rename to src/EasyWindowsApplication/Core/LayoutEngine/WindowConfigBuilder.cs index 1d4481d..f1f5251 100644 --- a/src/EasyWindowsApplication/LayoutModule/Backend/WindowConfigBuilder.cs +++ b/src/EasyWindowsApplication/Core/LayoutEngine/WindowConfigBuilder.cs @@ -1,9 +1,7 @@ -using EasyWindowsApplication.CoreModule.Backend; -using EasyWindowsApplication.LayoutModule.Frontend; +using EasyWindowsApplication.Core; using EasyWindowsApplication.Share; -using EasyWindowsApplication.WindowingModule.Frontend; -namespace EasyWindowsApplication.LayoutModule.Backend; +namespace EasyWindowsApplication.Core.LayoutEngine; internal sealed class WindowConfigBuilder : IWindowConfig { @@ -59,4 +57,4 @@ private IWindowContentConfig RegisterContent(Action configure) } } -internal sealed class WindowContentConfig : IWindowContentConfig { } \ No newline at end of file +internal sealed class WindowContentConfig : IWindowContentConfig { } diff --git a/src/EasyWindowsApplication/CoreModule/Backend/LayoutModels.cs b/src/EasyWindowsApplication/Core/LayoutModels.cs similarity index 86% rename from src/EasyWindowsApplication/CoreModule/Backend/LayoutModels.cs rename to src/EasyWindowsApplication/Core/LayoutModels.cs index b689221..ebfb15b 100644 --- a/src/EasyWindowsApplication/CoreModule/Backend/LayoutModels.cs +++ b/src/EasyWindowsApplication/Core/LayoutModels.cs @@ -1,8 +1,7 @@ -using EasyWindowsApplication.Share; +using EasyWindowsApplication.Share; using EasyWindowsApplication.Win32ControlsModule.Frontend; -using EasyWindowsApplication.WindowingModule.Frontend; -namespace EasyWindowsApplication.CoreModule.Backend; +namespace EasyWindowsApplication.Core; internal interface IContentModel; internal interface IViewModel; @@ -31,4 +30,4 @@ internal sealed class ViewModel : IViewModel internal string Name { get; set; } = ""; internal IControl? Control { get; set; } internal ContentModel? SubContent { get; set; } -} \ No newline at end of file +} diff --git a/src/EasyWindowsApplication/CoreModule/Backend/MasterRouter.cs b/src/EasyWindowsApplication/Core/MasterRouter.cs similarity index 90% rename from src/EasyWindowsApplication/CoreModule/Backend/MasterRouter.cs rename to src/EasyWindowsApplication/Core/MasterRouter.cs index 375eb20..7eef885 100644 --- a/src/EasyWindowsApplication/CoreModule/Backend/MasterRouter.cs +++ b/src/EasyWindowsApplication/Core/MasterRouter.cs @@ -1,9 +1,10 @@ +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using EasyWindowsApplication.Common; using EasyWindowsApplication.Share; using EasyWindowsApplication.Win32ControlsModule.Frontend; -namespace EasyWindowsApplication.CoreModule.Backend; +namespace EasyWindowsApplication.Core; internal sealed class MasterRouter { @@ -66,6 +67,21 @@ internal void ClearScrollOffset(nint hwnd) _scrollOffsets.Remove(hwnd); } + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] + internal static nint WndProcTrampoline(nint hwnd, uint msg, nint wParam, nint lParam) + { + try + { + var router = HandleRegistry.GetRouter(hwnd); + return router?.WndProc(hwnd, msg, wParam, lParam) ?? Win32.DefWindowProcW(hwnd, msg, wParam, lParam); + } + catch (Exception ex) + { + System.Diagnostics.Debug.Fail(ex.ToString()); + return Win32.DefWindowProcW(hwnd, msg, wParam, lParam); + } + } + internal nint WndProc(nint hwnd, uint msg, nint wParam, nint lParam) { if (msg == WM.DESTROY && hwnd == _mainHwnd) diff --git a/src/EasyWindowsApplication/CoreModule/Backend/Procedures.cs b/src/EasyWindowsApplication/Core/Procedures.cs similarity index 83% rename from src/EasyWindowsApplication/CoreModule/Backend/Procedures.cs rename to src/EasyWindowsApplication/Core/Procedures.cs index fe22424..8db87fa 100644 --- a/src/EasyWindowsApplication/CoreModule/Backend/Procedures.cs +++ b/src/EasyWindowsApplication/Core/Procedures.cs @@ -1,4 +1,4 @@ -namespace EasyWindowsApplication.CoreModule.Backend; +namespace EasyWindowsApplication.Core; internal static class Procedures { diff --git a/src/EasyWindowsApplication/CoreModule/Backend/RECT.cs b/src/EasyWindowsApplication/Core/RECT.cs similarity index 62% rename from src/EasyWindowsApplication/CoreModule/Backend/RECT.cs rename to src/EasyWindowsApplication/Core/RECT.cs index cc6ac0a..b4287fd 100644 --- a/src/EasyWindowsApplication/CoreModule/Backend/RECT.cs +++ b/src/EasyWindowsApplication/Core/RECT.cs @@ -1,6 +1,6 @@ -using System.Runtime.InteropServices; +using System.Runtime.InteropServices; -namespace EasyWindowsApplication.CoreModule.Backend; +namespace EasyWindowsApplication.Core; [StructLayout(LayoutKind.Sequential)] internal struct RECT diff --git a/src/EasyWindowsApplication/CoreModule/Backend/ResourcesDictionaryImpl.cs b/src/EasyWindowsApplication/Core/ResourcesDictionaryImpl.cs similarity index 94% rename from src/EasyWindowsApplication/CoreModule/Backend/ResourcesDictionaryImpl.cs rename to src/EasyWindowsApplication/Core/ResourcesDictionaryImpl.cs index 11e69dd..58ee463 100644 --- a/src/EasyWindowsApplication/CoreModule/Backend/ResourcesDictionaryImpl.cs +++ b/src/EasyWindowsApplication/Core/ResourcesDictionaryImpl.cs @@ -1,6 +1,6 @@ -namespace EasyWindowsApplication.CoreModule.Backend; +namespace EasyWindowsApplication.Core; -using EasyWindowsApplication.CoreModule.Frontend; +using EasyWindowsApplication.Share; internal sealed class ResourcesDictionaryImpl : IResourcesDictionary { diff --git a/src/EasyWindowsApplication/CoreModule/Backend/Win32.cs b/src/EasyWindowsApplication/Core/Win32.cs similarity index 98% rename from src/EasyWindowsApplication/CoreModule/Backend/Win32.cs rename to src/EasyWindowsApplication/Core/Win32.cs index 058b414..cd324c3 100644 --- a/src/EasyWindowsApplication/CoreModule/Backend/Win32.cs +++ b/src/EasyWindowsApplication/Core/Win32.cs @@ -1,6 +1,6 @@ -using System.Runtime.InteropServices; +using System.Runtime.InteropServices; -namespace EasyWindowsApplication.CoreModule.Backend; +namespace EasyWindowsApplication.Core; internal static partial class Win32 { diff --git a/src/EasyWindowsApplication/WindowingModule/Backend/AlternativeWindowImpl.cs b/src/EasyWindowsApplication/Core/Windowing/AlternativeWindowImpl.cs similarity index 84% rename from src/EasyWindowsApplication/WindowingModule/Backend/AlternativeWindowImpl.cs rename to src/EasyWindowsApplication/Core/Windowing/AlternativeWindowImpl.cs index 221fa35..9be6eac 100644 --- a/src/EasyWindowsApplication/WindowingModule/Backend/AlternativeWindowImpl.cs +++ b/src/EasyWindowsApplication/Core/Windowing/AlternativeWindowImpl.cs @@ -1,7 +1,6 @@ -using EasyWindowsApplication.Share; -using EasyWindowsApplication.WindowingModule.Frontend; +using EasyWindowsApplication.Share; -namespace EasyWindowsApplication.WindowingModule.Backend; +namespace EasyWindowsApplication.Core.Windowing; internal sealed class AlternativeWindowImpl : IAlternativeWindow { diff --git a/src/EasyWindowsApplication/Core/Windowing/Constants.cs b/src/EasyWindowsApplication/Core/Windowing/Constants.cs new file mode 100644 index 0000000..399115f --- /dev/null +++ b/src/EasyWindowsApplication/Core/Windowing/Constants.cs @@ -0,0 +1,2 @@ +// Window-specific constants are in Core (shared by all modules). +// This file reserved for Windowing-only constants in the future. diff --git a/src/EasyWindowsApplication/WindowingModule/Backend/Entities.cs b/src/EasyWindowsApplication/Core/Windowing/Entities.cs similarity index 84% rename from src/EasyWindowsApplication/WindowingModule/Backend/Entities.cs rename to src/EasyWindowsApplication/Core/Windowing/Entities.cs index cce9301..1ed19a1 100644 --- a/src/EasyWindowsApplication/WindowingModule/Backend/Entities.cs +++ b/src/EasyWindowsApplication/Core/Windowing/Entities.cs @@ -1,7 +1,7 @@ -using System.Runtime.InteropServices; -using EasyWindowsApplication.CoreModule.Backend; +using System.Runtime.InteropServices; +using EasyWindowsApplication.Core; -namespace EasyWindowsApplication.WindowingModule.Backend; +namespace EasyWindowsApplication.Core.Windowing; [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] internal struct WNDCLASSEXW diff --git a/src/EasyWindowsApplication/Core/Windowing/Enums.cs b/src/EasyWindowsApplication/Core/Windowing/Enums.cs new file mode 100644 index 0000000..323328b --- /dev/null +++ b/src/EasyWindowsApplication/Core/Windowing/Enums.cs @@ -0,0 +1,2 @@ +// Placeholder — implementación futura +namespace EasyWindowsApplication.Core.Windowing; diff --git a/src/EasyWindowsApplication/WindowingModule/Backend/Procedures.cs b/src/EasyWindowsApplication/Core/Windowing/Procedures.cs similarity index 77% rename from src/EasyWindowsApplication/WindowingModule/Backend/Procedures.cs rename to src/EasyWindowsApplication/Core/Windowing/Procedures.cs index 8e7efe7..c63c7f0 100644 --- a/src/EasyWindowsApplication/WindowingModule/Backend/Procedures.cs +++ b/src/EasyWindowsApplication/Core/Windowing/Procedures.cs @@ -1,20 +1,15 @@ -using System.Runtime.InteropServices; -using EasyWindowsApplication.CoreModule.Backend; +using System.Runtime.InteropServices; +using EasyWindowsApplication.Core; -namespace EasyWindowsApplication.WindowingModule.Backend; - -internal delegate nint WNDPROC(nint hWnd, uint Msg, nint wParam, nint lParam); +namespace EasyWindowsApplication.Core.Windowing; internal static class Procedures { private const int SW_SHOWNORMAL = 1; - private static WNDPROC? _wndProcDelegate; - private static MasterRouter? _router; - - internal static void SetRouter(MasterRouter router) => _router = router; + internal static void SetRouter(MasterRouter router) { } - internal static nint CreateMainWindow( + internal static unsafe nint CreateMainWindow( MasterRouter router, string title, float width, @@ -35,8 +30,7 @@ internal static nint CreateMainWindow( string className = $"EasyWinApp_{Guid.NewGuid():N}"; nint classNamePtr = Marshal.StringToHGlobalUni(className); - _wndProcDelegate = WndProcCallback; - nint wndProcPtr = Marshal.GetFunctionPointerForDelegate(_wndProcDelegate); + nint wndProcPtr = (nint)(delegate* unmanaged[Stdcall])&MasterRouter.WndProcTrampoline; var wndClass = new WNDCLASSEXW { @@ -74,13 +68,13 @@ internal static nint CreateMainWindow( if (hwnd == 0) throw new InvalidOperationException($"CreateWindowExW failed: {Marshal.GetLastWin32Error()}"); - _router = router; + HandleRegistry.RegisterRouter(hwnd, router); router.RegisterMainHwnd(hwnd); return hwnd; } - internal static nint CreateAlternativeWindow( + internal static unsafe nint CreateAlternativeWindow( MasterRouter router, nint ownerHwnd, string title, @@ -92,8 +86,7 @@ internal static nint CreateAlternativeWindow( string className = $"EasyWinAlt_{Guid.NewGuid():N}"; nint classNamePtr = Marshal.StringToHGlobalUni(className); - _wndProcDelegate = WndProcCallback; - nint wndProcPtr = Marshal.GetFunctionPointerForDelegate(_wndProcDelegate); + nint wndProcPtr = (nint)(delegate* unmanaged[Stdcall])&MasterRouter.WndProcTrampoline; var wndClass = new WNDCLASSEXW { @@ -131,16 +124,8 @@ internal static nint CreateAlternativeWindow( if (hwnd == 0) throw new InvalidOperationException($"CreateWindowExW failed: {Marshal.GetLastWin32Error()}"); - _router = router; + HandleRegistry.RegisterRouter(hwnd, router); return hwnd; } - - private static nint WndProcCallback(nint hwnd, uint msg, nint wParam, nint lParam) - { - if (_router is not null) - return _router.WndProc(hwnd, msg, wParam, lParam); - - return CoreModule.Backend.Win32.DefWindowProcW(hwnd, msg, wParam, lParam); - } } diff --git a/src/EasyWindowsApplication/WindowingModule/Backend/UserControl.cs b/src/EasyWindowsApplication/Core/Windowing/UserControl.cs similarity index 88% rename from src/EasyWindowsApplication/WindowingModule/Backend/UserControl.cs rename to src/EasyWindowsApplication/Core/Windowing/UserControl.cs index 18d4d07..1faa651 100644 --- a/src/EasyWindowsApplication/WindowingModule/Backend/UserControl.cs +++ b/src/EasyWindowsApplication/Core/Windowing/UserControl.cs @@ -1,8 +1,7 @@ -using EasyWindowsApplication.CoreModule.Backend; -using EasyWindowsApplication.LayoutModule.Backend; -using EasyWindowsApplication.Share.Infrastructure; +using EasyWindowsApplication.Core; +using EasyWindowsApplication.Core.LayoutEngine; -namespace EasyWindowsApplication.WindowingModule.Backend; +namespace EasyWindowsApplication.Core.Windowing; internal sealed class UserControl : ControlBase { diff --git a/src/EasyWindowsApplication/WindowingModule/Backend/Win32.cs b/src/EasyWindowsApplication/Core/Windowing/Win32.cs similarity index 93% rename from src/EasyWindowsApplication/WindowingModule/Backend/Win32.cs rename to src/EasyWindowsApplication/Core/Windowing/Win32.cs index be6b908..07073d3 100644 --- a/src/EasyWindowsApplication/WindowingModule/Backend/Win32.cs +++ b/src/EasyWindowsApplication/Core/Windowing/Win32.cs @@ -1,7 +1,7 @@ -using System.Runtime.InteropServices; -using EasyWindowsApplication.CoreModule.Backend; +using System.Runtime.InteropServices; +using EasyWindowsApplication.Core; -namespace EasyWindowsApplication.WindowingModule.Backend; +namespace EasyWindowsApplication.Core.Windowing; internal static partial class Win32 { diff --git a/src/EasyWindowsApplication/WindowingModule/Backend/WindowImpl.cs b/src/EasyWindowsApplication/Core/Windowing/WindowImpl.cs similarity index 82% rename from src/EasyWindowsApplication/WindowingModule/Backend/WindowImpl.cs rename to src/EasyWindowsApplication/Core/Windowing/WindowImpl.cs index c97da6e..f34f90b 100644 --- a/src/EasyWindowsApplication/WindowingModule/Backend/WindowImpl.cs +++ b/src/EasyWindowsApplication/Core/Windowing/WindowImpl.cs @@ -1,7 +1,6 @@ -using EasyWindowsApplication.Share; -using EasyWindowsApplication.WindowingModule.Frontend; +using EasyWindowsApplication.Share; -namespace EasyWindowsApplication.WindowingModule.Backend; +namespace EasyWindowsApplication.Core.Windowing; internal sealed class WindowImpl : IWindow { diff --git a/src/EasyWindowsApplication/CoreModule/Backend/ControlInstanceProvider.cs b/src/EasyWindowsApplication/CoreModule/Backend/ControlInstanceProvider.cs deleted file mode 100644 index 122d8c9..0000000 --- a/src/EasyWindowsApplication/CoreModule/Backend/ControlInstanceProvider.cs +++ /dev/null @@ -1,23 +0,0 @@ -using EasyWindowsApplication.Win32ControlsModule.Frontend; - -namespace EasyWindowsApplication.CoreModule.Backend; - -internal static class ControlInstanceProvider -{ - internal static T Create() where T : IControl - => (T)CreateFor(typeof(T)); - - internal static IControl CreateFor(Type type) - { - if (type == typeof(IButton)) - return new Win32ControlsModule.Backend.Button(); - if (type == typeof(Win32ControlsModule.Backend.Button)) - return new Win32ControlsModule.Backend.Button(); - if (type == typeof(ILabel)) - return new Win32ControlsModule.Backend.Label(); - if (type == typeof(Win32ControlsModule.Backend.Label)) - return new Win32ControlsModule.Backend.Label(); - - throw new InvalidOperationException($"No control implementation registered for '{type.FullName}'."); - } -} \ No newline at end of file diff --git a/src/EasyWindowsApplication/CoreModule/Frontend/IBehaviorBuilder.cs b/src/EasyWindowsApplication/CoreModule/Frontend/IBehaviorBuilder.cs deleted file mode 100644 index 0ffc43f..0000000 --- a/src/EasyWindowsApplication/CoreModule/Frontend/IBehaviorBuilder.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace EasyWindowsApplication.CoreModule.Frontend; - -public interface IBehaviorBuilder { } diff --git a/src/EasyWindowsApplication/EasyWindowsApplication.csproj b/src/EasyWindowsApplication/EasyWindowsApplication.csproj index 8f46634..3cfa231 100644 --- a/src/EasyWindowsApplication/EasyWindowsApplication.csproj +++ b/src/EasyWindowsApplication/EasyWindowsApplication.csproj @@ -45,8 +45,16 @@ + + + + + + + + diff --git a/src/EasyWindowsApplication/LayoutModule/Frontend/Enums.cs b/src/EasyWindowsApplication/LayoutModule/Frontend/Enums.cs deleted file mode 100644 index 4d7e953..0000000 --- a/src/EasyWindowsApplication/LayoutModule/Frontend/Enums.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace EasyWindowsApplication.LayoutModule.Frontend; - - -public enum GridUnitType { Absolute, Auto, Star } - -public enum LayoutAlignment { Start, Center, End, Fill } - -public enum DockPosition { Left, Top, Right, Bottom } diff --git a/src/EasyWindowsApplication/LayoutModule/Frontend/IDockable.cs b/src/EasyWindowsApplication/LayoutModule/Frontend/IDockable.cs deleted file mode 100644 index 3888d87..0000000 --- a/src/EasyWindowsApplication/LayoutModule/Frontend/IDockable.cs +++ /dev/null @@ -1,8 +0,0 @@ -using EasyWindowsApplication.Share; - -namespace EasyWindowsApplication.LayoutModule.Frontend; - -public interface IDockable -{ - DockPosition Dock { get; } -} diff --git a/src/EasyWindowsApplication/LayoutModule/Frontend/IStackLayout.cs b/src/EasyWindowsApplication/LayoutModule/Frontend/IStackLayout.cs deleted file mode 100644 index 40452c2..0000000 --- a/src/EasyWindowsApplication/LayoutModule/Frontend/IStackLayout.cs +++ /dev/null @@ -1,8 +0,0 @@ -using EasyWindowsApplication.Share; - -namespace EasyWindowsApplication.LayoutModule.Frontend; - -public interface IStackLayout : ILayout -{ - float Spacing { get; set; } -} diff --git a/src/EasyWindowsApplication/Share/Enums.cs b/src/EasyWindowsApplication/Share/Enums.cs index 2f42878..3df1ef0 100644 --- a/src/EasyWindowsApplication/Share/Enums.cs +++ b/src/EasyWindowsApplication/Share/Enums.cs @@ -1,3 +1,14 @@ namespace EasyWindowsApplication.Share; +public enum WindowPositionOnScreen { Center, Manual } +public enum ScrollBarVisibility { Default, Always, Never } + +public enum ScrollOrientation { Vertical, Horizontal, Both, Neither } + + +public enum GridUnitType { Absolute, Auto, Star } + +public enum LayoutAlignment { Start, Center, End, Fill } + +public enum DockPosition { Left, Top, Right, Bottom } \ No newline at end of file diff --git a/src/EasyWindowsApplication/LayoutModule/Frontend/GridDefinitions.cs b/src/EasyWindowsApplication/Share/GridDefinitions.cs similarity index 84% rename from src/EasyWindowsApplication/LayoutModule/Frontend/GridDefinitions.cs rename to src/EasyWindowsApplication/Share/GridDefinitions.cs index 92aad1a..07bb407 100644 --- a/src/EasyWindowsApplication/LayoutModule/Frontend/GridDefinitions.cs +++ b/src/EasyWindowsApplication/Share/GridDefinitions.cs @@ -1,4 +1,4 @@ -namespace EasyWindowsApplication.LayoutModule.Frontend; +namespace EasyWindowsApplication.Share; public readonly struct RowDefinition { diff --git a/src/EasyWindowsApplication/WindowingModule/Frontend/IAlternativeWindow.cs b/src/EasyWindowsApplication/Share/IAlternativeWindow.cs similarity index 70% rename from src/EasyWindowsApplication/WindowingModule/Frontend/IAlternativeWindow.cs rename to src/EasyWindowsApplication/Share/IAlternativeWindow.cs index 0bc15d5..d883fe8 100644 --- a/src/EasyWindowsApplication/WindowingModule/Frontend/IAlternativeWindow.cs +++ b/src/EasyWindowsApplication/Share/IAlternativeWindow.cs @@ -1,6 +1,5 @@ -using EasyWindowsApplication.Share; - -namespace EasyWindowsApplication.WindowingModule.Frontend; + +namespace EasyWindowsApplication.Share; public interface IAlternativeWindow : IBaseWindow { diff --git a/src/EasyWindowsApplication/CoreModule/Frontend/IApplicationLayoutPhase.cs b/src/EasyWindowsApplication/Share/IApplicationLayoutPhase.cs similarity index 51% rename from src/EasyWindowsApplication/CoreModule/Frontend/IApplicationLayoutPhase.cs rename to src/EasyWindowsApplication/Share/IApplicationLayoutPhase.cs index 74078af..8e68bee 100644 --- a/src/EasyWindowsApplication/CoreModule/Frontend/IApplicationLayoutPhase.cs +++ b/src/EasyWindowsApplication/Share/IApplicationLayoutPhase.cs @@ -1,8 +1,7 @@ -using EasyWindowsApplication.LayoutModule.Frontend; - -namespace EasyWindowsApplication.CoreModule.Frontend; + +namespace EasyWindowsApplication.Share; public interface IApplicationLayoutPhase { IApplicationPostLayoutPhase Layout(Action configure); -} \ No newline at end of file +} diff --git a/src/EasyWindowsApplication/CoreModule/Frontend/IApplicationPostBehaviorPhase.cs b/src/EasyWindowsApplication/Share/IApplicationPostBehaviorPhase.cs similarity index 57% rename from src/EasyWindowsApplication/CoreModule/Frontend/IApplicationPostBehaviorPhase.cs rename to src/EasyWindowsApplication/Share/IApplicationPostBehaviorPhase.cs index 8fee916..3376736 100644 --- a/src/EasyWindowsApplication/CoreModule/Frontend/IApplicationPostBehaviorPhase.cs +++ b/src/EasyWindowsApplication/Share/IApplicationPostBehaviorPhase.cs @@ -1,4 +1,4 @@ -namespace EasyWindowsApplication.CoreModule.Frontend; +namespace EasyWindowsApplication.Share; public interface IApplicationPostBehaviorPhase { diff --git a/src/EasyWindowsApplication/CoreModule/Frontend/IApplicationPostLayoutPhase.cs b/src/EasyWindowsApplication/Share/IApplicationPostLayoutPhase.cs similarity index 73% rename from src/EasyWindowsApplication/CoreModule/Frontend/IApplicationPostLayoutPhase.cs rename to src/EasyWindowsApplication/Share/IApplicationPostLayoutPhase.cs index 7c5ffae..acf69d4 100644 --- a/src/EasyWindowsApplication/CoreModule/Frontend/IApplicationPostLayoutPhase.cs +++ b/src/EasyWindowsApplication/Share/IApplicationPostLayoutPhase.cs @@ -1,4 +1,4 @@ -namespace EasyWindowsApplication.CoreModule.Frontend; +namespace EasyWindowsApplication.Share; public interface IApplicationPostLayoutPhase { diff --git a/src/EasyWindowsApplication/WindowingModule/Frontend/IBaseWindow.cs b/src/EasyWindowsApplication/Share/IBaseWindow.cs similarity index 69% rename from src/EasyWindowsApplication/WindowingModule/Frontend/IBaseWindow.cs rename to src/EasyWindowsApplication/Share/IBaseWindow.cs index c7d9ab7..76f5705 100644 --- a/src/EasyWindowsApplication/WindowingModule/Frontend/IBaseWindow.cs +++ b/src/EasyWindowsApplication/Share/IBaseWindow.cs @@ -1,4 +1,4 @@ -namespace EasyWindowsApplication.WindowingModule.Frontend; +namespace EasyWindowsApplication.Share; public interface IBaseWindow { diff --git a/src/EasyWindowsApplication/Share/IBehaviorBuilder.cs b/src/EasyWindowsApplication/Share/IBehaviorBuilder.cs new file mode 100644 index 0000000..f26de30 --- /dev/null +++ b/src/EasyWindowsApplication/Share/IBehaviorBuilder.cs @@ -0,0 +1,3 @@ +namespace EasyWindowsApplication.Share; + +public interface IBehaviorBuilder { } diff --git a/src/EasyWindowsApplication/WindowingModule/Frontend/IChildrenBuilder.cs b/src/EasyWindowsApplication/Share/IChildrenBuilder.cs similarity index 58% rename from src/EasyWindowsApplication/WindowingModule/Frontend/IChildrenBuilder.cs rename to src/EasyWindowsApplication/Share/IChildrenBuilder.cs index 1738b93..e2c7184 100644 --- a/src/EasyWindowsApplication/WindowingModule/Frontend/IChildrenBuilder.cs +++ b/src/EasyWindowsApplication/Share/IChildrenBuilder.cs @@ -1,10 +1,11 @@ -using EasyWindowsApplication.Win32ControlsModule.Frontend; +using EasyWindowsApplication.Win32ControlsModule.Frontend; -namespace EasyWindowsApplication.WindowingModule.Frontend; +namespace EasyWindowsApplication.Share; public interface IChildrenBuilder { - IChildrenBuilder View(Action configure) where T : IControl; + IChildrenBuilder View(Action> configure) where T : class, IControl; + IChildrenBuilder View(Func, View> configure) where T : class, IControl; IChildrenBuilder View(Action configure); IChildrenBuilder Row(int row); diff --git a/src/EasyWindowsApplication/Share/IContentBuilder.cs b/src/EasyWindowsApplication/Share/IContentBuilder.cs index a78300e..29057c8 100644 --- a/src/EasyWindowsApplication/Share/IContentBuilder.cs +++ b/src/EasyWindowsApplication/Share/IContentBuilder.cs @@ -1,6 +1,4 @@ -using EasyWindowsApplication.LayoutModule.Frontend; -using EasyWindowsApplication.WindowingModule.Frontend; - + namespace EasyWindowsApplication.Share; public interface IContentBuilder diff --git a/src/EasyWindowsApplication/LayoutModule/Frontend/IDockStackLayout.cs b/src/EasyWindowsApplication/Share/IDockStackLayout.cs similarity index 56% rename from src/EasyWindowsApplication/LayoutModule/Frontend/IDockStackLayout.cs rename to src/EasyWindowsApplication/Share/IDockStackLayout.cs index 0a148cf..b7da142 100644 --- a/src/EasyWindowsApplication/LayoutModule/Frontend/IDockStackLayout.cs +++ b/src/EasyWindowsApplication/Share/IDockStackLayout.cs @@ -1,6 +1,5 @@ -using EasyWindowsApplication.Share; - -namespace EasyWindowsApplication.LayoutModule.Frontend; + +namespace EasyWindowsApplication.Share; public interface IDockLayout : ILayout { diff --git a/src/EasyWindowsApplication/Share/IDockable.cs b/src/EasyWindowsApplication/Share/IDockable.cs new file mode 100644 index 0000000..ba5ef98 --- /dev/null +++ b/src/EasyWindowsApplication/Share/IDockable.cs @@ -0,0 +1,7 @@ + +namespace EasyWindowsApplication.Share; + +public interface IDockable +{ + DockPosition Dock { get; } +} diff --git a/src/EasyWindowsApplication/LayoutModule/Frontend/IGridLayout.cs b/src/EasyWindowsApplication/Share/IGridLayout.cs similarity index 68% rename from src/EasyWindowsApplication/LayoutModule/Frontend/IGridLayout.cs rename to src/EasyWindowsApplication/Share/IGridLayout.cs index 51c878f..4aa7e98 100644 --- a/src/EasyWindowsApplication/LayoutModule/Frontend/IGridLayout.cs +++ b/src/EasyWindowsApplication/Share/IGridLayout.cs @@ -1,6 +1,6 @@ -using EasyWindowsApplication.LayoutModule.Backend; +using EasyWindowsApplication.Core.LayoutEngine; -namespace EasyWindowsApplication.LayoutModule.Frontend; +namespace EasyWindowsApplication.Share; public interface IGridLayout : IStackLayout { diff --git a/src/EasyWindowsApplication/LayoutModule/Frontend/IHorizontalStackLayout.cs b/src/EasyWindowsApplication/Share/IHorizontalStackLayout.cs similarity index 51% rename from src/EasyWindowsApplication/LayoutModule/Frontend/IHorizontalStackLayout.cs rename to src/EasyWindowsApplication/Share/IHorizontalStackLayout.cs index ccb2cbf..98ae25a 100644 --- a/src/EasyWindowsApplication/LayoutModule/Frontend/IHorizontalStackLayout.cs +++ b/src/EasyWindowsApplication/Share/IHorizontalStackLayout.cs @@ -1,4 +1,4 @@ -namespace EasyWindowsApplication.LayoutModule.Frontend; +namespace EasyWindowsApplication.Share; public interface IHorizontalStackLayout : IStackLayout { diff --git a/src/EasyWindowsApplication/LayoutModule/Frontend/ILayout.cs b/src/EasyWindowsApplication/Share/ILayout.cs similarity index 64% rename from src/EasyWindowsApplication/LayoutModule/Frontend/ILayout.cs rename to src/EasyWindowsApplication/Share/ILayout.cs index 8c48cc3..b37bef7 100644 --- a/src/EasyWindowsApplication/LayoutModule/Frontend/ILayout.cs +++ b/src/EasyWindowsApplication/Share/ILayout.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Text; -namespace EasyWindowsApplication.LayoutModule.Frontend; +namespace EasyWindowsApplication.Share; public interface ILayout { diff --git a/src/EasyWindowsApplication/LayoutModule/Frontend/ILayoutBuilder.cs b/src/EasyWindowsApplication/Share/ILayoutBuilder.cs similarity index 55% rename from src/EasyWindowsApplication/LayoutModule/Frontend/ILayoutBuilder.cs rename to src/EasyWindowsApplication/Share/ILayoutBuilder.cs index 386669d..fedb52d 100644 --- a/src/EasyWindowsApplication/LayoutModule/Frontend/ILayoutBuilder.cs +++ b/src/EasyWindowsApplication/Share/ILayoutBuilder.cs @@ -1,9 +1,8 @@ -using EasyWindowsApplication.WindowingModule.Frontend; - -namespace EasyWindowsApplication.LayoutModule.Frontend; + +namespace EasyWindowsApplication.Share; public interface ILayoutBuilder { ILayoutBuilderAfterWindow Window(); ILayoutBuilderAfterWindow Window(Action configure); -} \ No newline at end of file +} diff --git a/src/EasyWindowsApplication/LayoutModule/Frontend/ILayoutBuilderAfterWindow.cs b/src/EasyWindowsApplication/Share/ILayoutBuilderAfterWindow.cs similarity index 72% rename from src/EasyWindowsApplication/LayoutModule/Frontend/ILayoutBuilderAfterWindow.cs rename to src/EasyWindowsApplication/Share/ILayoutBuilderAfterWindow.cs index b6165f1..80591d5 100644 --- a/src/EasyWindowsApplication/LayoutModule/Frontend/ILayoutBuilderAfterWindow.cs +++ b/src/EasyWindowsApplication/Share/ILayoutBuilderAfterWindow.cs @@ -1,6 +1,5 @@ -using EasyWindowsApplication.WindowingModule.Frontend; - -namespace EasyWindowsApplication.LayoutModule.Frontend; + +namespace EasyWindowsApplication.Share; public interface ILayoutBuilderAfterWindow { diff --git a/src/EasyWindowsApplication/CoreModule/Frontend/IResourcesDictionary.cs b/src/EasyWindowsApplication/Share/IResourcesDictionary.cs similarity index 90% rename from src/EasyWindowsApplication/CoreModule/Frontend/IResourcesDictionary.cs rename to src/EasyWindowsApplication/Share/IResourcesDictionary.cs index 32110ac..ea4db29 100644 --- a/src/EasyWindowsApplication/CoreModule/Frontend/IResourcesDictionary.cs +++ b/src/EasyWindowsApplication/Share/IResourcesDictionary.cs @@ -1,4 +1,4 @@ -namespace EasyWindowsApplication.CoreModule.Frontend; +namespace EasyWindowsApplication.Share; public interface IResourcesDictionary { diff --git a/src/EasyWindowsApplication/Share/IStackLayout.cs b/src/EasyWindowsApplication/Share/IStackLayout.cs new file mode 100644 index 0000000..55abc28 --- /dev/null +++ b/src/EasyWindowsApplication/Share/IStackLayout.cs @@ -0,0 +1,7 @@ + +namespace EasyWindowsApplication.Share; + +public interface IStackLayout : ILayout +{ + float Spacing { get; set; } +} diff --git a/src/EasyWindowsApplication/LayoutModule/Frontend/IVerticalStackLayout.cs b/src/EasyWindowsApplication/Share/IVerticalStackLayout.cs similarity index 50% rename from src/EasyWindowsApplication/LayoutModule/Frontend/IVerticalStackLayout.cs rename to src/EasyWindowsApplication/Share/IVerticalStackLayout.cs index 19c5f19..e492ff7 100644 --- a/src/EasyWindowsApplication/LayoutModule/Frontend/IVerticalStackLayout.cs +++ b/src/EasyWindowsApplication/Share/IVerticalStackLayout.cs @@ -1,4 +1,4 @@ -namespace EasyWindowsApplication.LayoutModule.Frontend; +namespace EasyWindowsApplication.Share; public interface IVerticalStackLayout : IStackLayout { diff --git a/src/EasyWindowsApplication/Share/IView.cs b/src/EasyWindowsApplication/Share/IView.cs new file mode 100644 index 0000000..54c9d1f --- /dev/null +++ b/src/EasyWindowsApplication/Share/IView.cs @@ -0,0 +1,5 @@ +namespace EasyWindowsApplication.Share; + +public interface IView : IBaseWindow +{ +} diff --git a/src/EasyWindowsApplication/WindowingModule/Frontend/IViewBuilder.cs b/src/EasyWindowsApplication/Share/IViewBuilder.cs similarity index 83% rename from src/EasyWindowsApplication/WindowingModule/Frontend/IViewBuilder.cs rename to src/EasyWindowsApplication/Share/IViewBuilder.cs index ff3c8ef..e8282ab 100644 --- a/src/EasyWindowsApplication/WindowingModule/Frontend/IViewBuilder.cs +++ b/src/EasyWindowsApplication/Share/IViewBuilder.cs @@ -1,7 +1,5 @@ -using EasyWindowsApplication.LayoutModule.Frontend; -using EasyWindowsApplication.Share; - -namespace EasyWindowsApplication.WindowingModule.Frontend; + +namespace EasyWindowsApplication.Share; public interface IViewBuilder { diff --git a/src/EasyWindowsApplication/WindowingModule/Frontend/IWindow.cs b/src/EasyWindowsApplication/Share/IWindow.cs similarity index 66% rename from src/EasyWindowsApplication/WindowingModule/Frontend/IWindow.cs rename to src/EasyWindowsApplication/Share/IWindow.cs index 99f551a..f1e543c 100644 --- a/src/EasyWindowsApplication/WindowingModule/Frontend/IWindow.cs +++ b/src/EasyWindowsApplication/Share/IWindow.cs @@ -1,6 +1,5 @@ -using EasyWindowsApplication.Share; - -namespace EasyWindowsApplication.WindowingModule.Frontend; + +namespace EasyWindowsApplication.Share; public interface IWindow : IBaseWindow { diff --git a/src/EasyWindowsApplication/WindowingModule/Frontend/IWindowConfig.cs b/src/EasyWindowsApplication/Share/IWindowConfig.cs similarity index 78% rename from src/EasyWindowsApplication/WindowingModule/Frontend/IWindowConfig.cs rename to src/EasyWindowsApplication/Share/IWindowConfig.cs index 114dd9d..2e4eae0 100644 --- a/src/EasyWindowsApplication/WindowingModule/Frontend/IWindowConfig.cs +++ b/src/EasyWindowsApplication/Share/IWindowConfig.cs @@ -1,7 +1,5 @@ -using EasyWindowsApplication.LayoutModule.Frontend; -using EasyWindowsApplication.Share; - -namespace EasyWindowsApplication.WindowingModule.Frontend; + +namespace EasyWindowsApplication.Share; public interface IWindowConfig { diff --git a/src/EasyWindowsApplication/WindowingModule/Frontend/IWindowsScrollConfig.cs b/src/EasyWindowsApplication/Share/IWindowsScrollConfig.cs similarity index 79% rename from src/EasyWindowsApplication/WindowingModule/Frontend/IWindowsScrollConfig.cs rename to src/EasyWindowsApplication/Share/IWindowsScrollConfig.cs index a413bfb..e4d3c11 100644 --- a/src/EasyWindowsApplication/WindowingModule/Frontend/IWindowsScrollConfig.cs +++ b/src/EasyWindowsApplication/Share/IWindowsScrollConfig.cs @@ -1,6 +1,5 @@ -using EasyWindowsApplication.Share; - -namespace EasyWindowsApplication.WindowingModule.Frontend; + +namespace EasyWindowsApplication.Share; public interface IWindowsScrollConfig { diff --git a/src/EasyWindowsApplication/Share/Infrastructure/ControlAccess.cs b/src/EasyWindowsApplication/Share/Infrastructure/ControlAccess.cs index 1ff78a5..8330329 100644 --- a/src/EasyWindowsApplication/Share/Infrastructure/ControlAccess.cs +++ b/src/EasyWindowsApplication/Share/Infrastructure/ControlAccess.cs @@ -1,6 +1,6 @@ -using System.ComponentModel; +using System.ComponentModel; using EasyWindowsApplication.Win32ControlsModule.Frontend; -using EasyWindowsApplication.WindowingModule.Frontend; +using EasyWindowsApplication.Share; namespace EasyWindowsApplication.Share.Infrastructure; diff --git a/src/EasyWindowsApplication/Share/Infrastructure/View.cs b/src/EasyWindowsApplication/Share/Infrastructure/View.cs deleted file mode 100644 index 569bbf5..0000000 --- a/src/EasyWindowsApplication/Share/Infrastructure/View.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.ComponentModel; -using EasyWindowsApplication.Win32ControlsModule.Frontend; - -namespace EasyWindowsApplication.Share.Infrastructure; - -[EditorBrowsable(EditorBrowsableState.Never)] -public abstract class View : ControlBase where TSelf : View -{ - internal View() { } - - public new TSelf OnMessage(uint msg, Win32MessageHandler handler) - { - base.OnMessage(msg, handler); - return (TSelf)this; - } - - public new TSelf OnClick(Action handler) - { - base.OnClick(handler); - return (TSelf)this; - } -} diff --git a/src/EasyWindowsApplication/LayoutModule/Frontend/LayoutLength.cs b/src/EasyWindowsApplication/Share/LayoutLength.cs similarity index 83% rename from src/EasyWindowsApplication/LayoutModule/Frontend/LayoutLength.cs rename to src/EasyWindowsApplication/Share/LayoutLength.cs index 207ad34..073fc59 100644 --- a/src/EasyWindowsApplication/LayoutModule/Frontend/LayoutLength.cs +++ b/src/EasyWindowsApplication/Share/LayoutLength.cs @@ -1,6 +1,5 @@ -using EasyWindowsApplication.Share; - -namespace EasyWindowsApplication.LayoutModule.Frontend; + +namespace EasyWindowsApplication.Share; public readonly struct LayoutLength { diff --git a/src/EasyWindowsApplication/LayoutModule/Frontend/LayoutOptions.cs b/src/EasyWindowsApplication/Share/LayoutOptions.cs similarity index 68% rename from src/EasyWindowsApplication/LayoutModule/Frontend/LayoutOptions.cs rename to src/EasyWindowsApplication/Share/LayoutOptions.cs index 21cb4e8..47b5595 100644 --- a/src/EasyWindowsApplication/LayoutModule/Frontend/LayoutOptions.cs +++ b/src/EasyWindowsApplication/Share/LayoutOptions.cs @@ -1,6 +1,5 @@ -using EasyWindowsApplication.Share; - -namespace EasyWindowsApplication.LayoutModule.Frontend; + +namespace EasyWindowsApplication.Share; public class LayoutOptions { diff --git a/src/EasyWindowsApplication/Share/View.cs b/src/EasyWindowsApplication/Share/View.cs new file mode 100644 index 0000000..98cd7cb --- /dev/null +++ b/src/EasyWindowsApplication/Share/View.cs @@ -0,0 +1,70 @@ +using System.ComponentModel; +using EasyWindowsApplication.Core; +using EasyWindowsApplication.Win32ControlsModule.Frontend; + +namespace EasyWindowsApplication.Share; + +[EditorBrowsable(EditorBrowsableState.Never)] +public abstract class ViewBase : ControlBase where TSelf : ViewBase +{ + internal ViewBase() { } + + public new TSelf OnMessage(uint msg, Win32MessageHandler handler) + { + base.OnMessage(msg, handler); + return (TSelf)this; + } + + public new TSelf OnClick(Action handler) + { + base.OnClick(handler); + return (TSelf)this; + } +} + +public readonly struct View where T : class, IControl +{ + public T Instance { get; } + + public View(T instance) => Instance = instance; + + public View Name(string name) { Instance.Name = name; return this; } + + public View Margin(float uniform) { Instance.Margin = new Thickness(uniform); return this; } + public View Margin(float vertical, float horizontal) { Instance.Margin = new Thickness(vertical, horizontal); return this; } + public View Margin(float top, float right, float bottom, float left) { Instance.Margin = new Thickness(top, right, bottom, left); return this; } + + public View Padding(float uniform) { Instance.Padding = new Thickness(uniform); return this; } + public View Padding(float vertical, float horizontal) { Instance.Padding = new Thickness(vertical, horizontal); return this; } + public View Padding(float top, float right, float bottom, float left) { Instance.Padding = new Thickness(top, right, bottom, left); return this; } + + public View Width(float width) { Instance.LayoutWidth = LayoutLength.Absolute(width); return this; } + public View Height(float height) { Instance.LayoutHeight = LayoutLength.Absolute(height); return this; } + + public View Background(Color color) { Instance.BackgroundColor = color; return this; } + + public View Dock(DockPosition dock) { Instance.Dock = dock; return this; } + public View DockLeft() { Instance.Dock = DockPosition.Left; return this; } + public View DockTop() { Instance.Dock = DockPosition.Top; return this; } + public View DockRight() { Instance.Dock = DockPosition.Right; return this; } + public View DockBottom() { Instance.Dock = DockPosition.Bottom; return this; } + + public View Row(int row) { Instance.GridRow = row; return this; } + public View Column(int column) { Instance.GridColumn = column; return this; } + public View RowSpan(int span) { Instance.GridRowSpan = span; return this; } + public View ColumnSpan(int span) { Instance.GridColumnSpan = span; return this; } + + public View OnClick(Action handler) { Instance.OnClick(handler); return this; } + + public View Text(string text) + { + if (Instance is IText t) t.Text = text; + return this; + } + + public View OnMessage(uint msg, Win32MessageHandler handler) + { + if (Instance is ControlBase cb) cb.OnMessage(msg, handler); + return this; + } +} diff --git a/src/EasyWindowsApplication/WindowingModule/Frontend/WindowsScroll.cs b/src/EasyWindowsApplication/Share/WindowsScroll.cs similarity index 92% rename from src/EasyWindowsApplication/WindowingModule/Frontend/WindowsScroll.cs rename to src/EasyWindowsApplication/Share/WindowsScroll.cs index 176445e..98c66ab 100644 --- a/src/EasyWindowsApplication/WindowingModule/Frontend/WindowsScroll.cs +++ b/src/EasyWindowsApplication/Share/WindowsScroll.cs @@ -1,6 +1,5 @@ -using EasyWindowsApplication.Share; - -namespace EasyWindowsApplication.WindowingModule.Frontend; + +namespace EasyWindowsApplication.Share; internal sealed class WindowsScroll : IWindowsScrollConfig { diff --git a/src/EasyWindowsApplication/Win32ControlsModule/Backend/Controls.cs b/src/EasyWindowsApplication/Win32ControlsModule/Backend/Controls.cs index 591b3cc..bc0fa9b 100644 --- a/src/EasyWindowsApplication/Win32ControlsModule/Backend/Controls.cs +++ b/src/EasyWindowsApplication/Win32ControlsModule/Backend/Controls.cs @@ -1,5 +1,4 @@ -using EasyWindowsApplication.CoreModule.Backend; -using EasyWindowsApplication.Share.Infrastructure; +using EasyWindowsApplication.Core; using EasyWindowsApplication.Win32ControlsModule.Frontend; namespace EasyWindowsApplication.Win32ControlsModule.Backend; @@ -15,4 +14,4 @@ public void SetStyle(uint style, bool redraw = true) { } public sealed class Label : ControlBase, ILabel { public string Text { get; set; } = ""; -} \ No newline at end of file +} diff --git a/src/EasyWindowsApplication/Win32ControlsModule/Backend/Procedures.cs b/src/EasyWindowsApplication/Win32ControlsModule/Backend/Procedures.cs index 41a75b0..6eae7be 100644 --- a/src/EasyWindowsApplication/Win32ControlsModule/Backend/Procedures.cs +++ b/src/EasyWindowsApplication/Win32ControlsModule/Backend/Procedures.cs @@ -1,5 +1,5 @@ -using System.Runtime.InteropServices; -using EasyWindowsApplication.CoreModule.Backend; +using System.Runtime.InteropServices; +using EasyWindowsApplication.Core; namespace EasyWindowsApplication.Win32ControlsModule.Backend; diff --git a/src/EasyWindowsApplication/Win32ControlsModule/Backend/Win32ControlActivator.cs b/src/EasyWindowsApplication/Win32ControlsModule/Backend/Win32ControlActivator.cs new file mode 100644 index 0000000..e529d03 --- /dev/null +++ b/src/EasyWindowsApplication/Win32ControlsModule/Backend/Win32ControlActivator.cs @@ -0,0 +1,16 @@ +using EasyWindowsApplication.Common; +using EasyWindowsApplication.Win32ControlsModule.Frontend; + +namespace EasyWindowsApplication.Win32ControlsModule.Backend; + +internal sealed class Win32ControlActivator : IControlActivator +{ + public void RegisterActivators(ControlActivatorRegistry registry) + { + registry.Register(() => new Button()); + registry.Register(() => new Label()); + // Register concrete types as well for direct resolution if needed + registry.Register