Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions src/EasyWindowsApplication/Common/ControlActivatorRegistry.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
using System.Diagnostics.CodeAnalysis;
using EasyWindowsApplication.Win32ControlsModule.Frontend;

namespace EasyWindowsApplication.Common;

internal sealed partial class ControlActivatorRegistry
{
private readonly Dictionary<Type, Func<object>> _factories = new();
private readonly Dictionary<Type, INativeHandleFactory> _handleFactories = new();
private static bool _inited;
private static readonly object _initLock = new();

Expand All @@ -11,24 +15,79 @@ internal void Register<T>(Func<T> factory) where T : class
_factories[typeof(T)] = () => factory()!;
}

internal void RegisterFactory<T>(INativeHandleFactory factory) where T : class
{
_handleFactories[typeof(T)] = factory;
}

internal T Create<T>() where T : class
{
EnsureInitialized();
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)
{
EnsureInitialized();
if (_factories.TryGetValue(type, out var f))
return f();
throw new InvalidOperationException($"No control implementation registered for '{type.FullName}'.");
}

internal INativeHandleFactory? TryGetFactory(Type type)
{
EnsureInitialized();
return _handleFactories.TryGetValue(type, out var f) ? f : null;
}

internal INativeHandleFactory CreateFactory<T>() where T : class
{
EnsureInitialized();
if (_handleFactories.TryGetValue(typeof(T), out var f))
return f;
throw new InvalidOperationException($"No handle factory registered for '{typeof(T).FullName}'. Did you forget to register Win32NativeHandleFactory?");
}

internal INativeHandleFactory CreateFactoryFor(Type type)
{
EnsureInitialized();
if (_handleFactories.TryGetValue(type, out var f))
return f;
throw new InvalidOperationException($"No handle factory registered for '{type.FullName}'.");
}

internal bool TryGetFactoryForControl(IControl control, out INativeHandleFactory? factory)
{
EnsureInitialized();
var t = control.GetType();
if (_handleFactories.TryGetValue(t, out factory))
return true;
#pragma warning disable IL2075 // trimming: GetInterfaces usado solo para lookup de factories registradas (AOT: factories registradas vía source generator)
foreach (var iface in t.GetInterfaces())
{
if (_handleFactories.TryGetValue(iface, out factory))
return true;
}
#pragma warning restore IL2075
// fallback por base types
var bt = t.BaseType;
while (bt != null && bt != typeof(object))
{
if (_handleFactories.TryGetValue(bt, out factory))
return true;
bt = bt.BaseType;
}
factory = null;
return false;
}

internal static ControlActivatorRegistry Shared { get; } = new();

internal static void EnsureInitialized()
{
if (_inited) return;
lock (_initLock)
{
if (_inited) return;
Expand Down
13 changes: 13 additions & 0 deletions src/EasyWindowsApplication/Common/INativeHandleFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using EasyWindowsApplication.Core;
using EasyWindowsApplication.Win32ControlsModule.Frontend;

namespace EasyWindowsApplication.Common;

internal interface INativeHandleFactory
{
/// <summary>
/// Crea el HWND nativo para el control y lo asigna a control.Hwnd.
/// Debe registrar el control en HandleRegistry (hwnd + name).
/// </summary>
nint CreateHandle(nint parentHwnd, IControl control, HandleRegistry registry);
}
37 changes: 36 additions & 1 deletion src/EasyWindowsApplication/Core/Application.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,13 @@ internal void AddWindow(WindowModel window)

public void Initialize()
{
var icc = new INITCOMMONCONTROLSEX
{
dwSize = (uint)System.Runtime.InteropServices.Marshal.SizeOf<INITCOMMONCONTROLSEX>(),
dwICC = ICC.STANDARD_CLASSES
};
Win32.InitCommonControlsEx(ref icc);

ControlActivatorRegistry.EnsureInitialized();
_router = new MasterRouter(_registry);

Expand Down Expand Up @@ -80,7 +87,13 @@ private void RegisterMain(WindowModel window)
}

_registry.RegisterWindow(win);
// Fase 2: materializar contenido (creación de HWNDs hijos + layout batching sin flicker)
win.MaterializeContent(window, _registry, _router);
// Si Position == Center, centrar antes de mostrar (multi-monitor correcto)
if (window.Position == WindowPositionOnScreen.Center)
win.Center();
win.Show();
win.RaiseLoaded();
}

private void RegisterAlternative(WindowModel window)
Expand All @@ -93,6 +106,28 @@ private void RegisterAlternative(WindowModel window)
_router.RegisterWindowBackgroundBrush(hwnd, brush);
}
_registry.RegisterWindow(win);
win.Show();
// Fase 2: materializar contenido para AlternativeWindow también
win.MaterializeContent(window, _registry, _router);
if (window.Position == WindowPositionOnScreen.Center)
{
// Center para AlternativeWindow (pre-calcula posición pero no muestra)
nint monitor = Win32.MonitorFromWindow(hwnd, MONITOR.DEFAULTTONEAREST);
if (monitor == 0) monitor = Win32.MonitorFromWindow(hwnd, MONITOR.DEFAULTTOPRIMARY);
if (monitor != 0)
{
MONITORINFO mi = new() { cbSize = (uint)System.Runtime.InteropServices.Marshal.SizeOf<MONITORINFO>() };
if (Win32.GetMonitorInfoW(monitor, ref mi))
{
Win32.GetWindowRect(hwnd, out RECT wr);
int winW = wr.Right - wr.Left;
int winH = wr.Bottom - wr.Top;
int x = mi.rcWork.Left + ((mi.rcWork.Right - mi.rcWork.Left - winW) / 2);
int y = mi.rcWork.Top + ((mi.rcWork.Bottom - mi.rcWork.Top - winH) / 2);
Win32.SetWindowPos(hwnd, 0, x, y, 0, 0, SWP.NOZORDER | SWP.NOACTIVATE | SWP.NOSIZE);
}
}
}
// AlternativeWindow inicia oculta — se muestra explícitamente via GetWindow<IAlternativeWindow>(name).Show()
// No se llama win.Show() ni win.RaiseLoaded() aquí (Loaded se disparará en el Show manual)
}
}
72 changes: 72 additions & 0 deletions src/EasyWindowsApplication/Core/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,31 @@ internal static class WM
internal const uint HSCROLL = 0x0114;
internal const uint VSCROLL = 0x0115;
internal const uint MOUSEWHEEL = 0x020A;
internal const uint ENTERSIZEMOVE = 0x0231;
internal const uint EXITSIZEMOVE = 0x0232;
}

internal static class WMSIZE
{
internal const int RESTORED = 0;
internal const int MINIMIZED = 1;
internal const int MAXIMIZED = 2;
internal const int MAXSHOW = 3;
internal const int MAXHIDE = 4;
}

internal static class WA
{
internal const int INACTIVE = 0;
internal const int ACTIVE = 1;
internal const int CLICKACTIVE = 2;
}

internal static class MONITOR
{
internal const uint DEFAULTTONULL = 0x00000000;
internal const uint DEFAULTTOPRIMARY = 0x00000001;
internal const uint DEFAULTTONEAREST = 0x00000002;
}

internal static class DT
Expand Down Expand Up @@ -133,7 +158,18 @@ internal static class EN

internal static class SW
{
internal const int HIDE = 0;
internal const int SHOWNORMAL = 1;
internal const int SHOWMINIMIZED = 2;
internal const int SHOWMAXIMIZED = 3;
internal const int SHOWNOACTIVATE = 4;
internal const int SHOW = 5;
internal const int MINIMIZE = 6;
internal const int SHOWMINNOACTIVE = 7;
internal const int SHOWNORMAL2 = 8;
internal const int RESTORE = 9;
internal const int SHOWDEFAULT = 10;
internal const int MAXIMIZE = 3;
}

internal static class GWL
Expand All @@ -142,3 +178,39 @@ internal static class GWL
internal const int EXSTYLE = -20;
internal const int ID = -12;
}

internal static class ICC
{
internal const uint LISTVIEW_CLASSES = 0x00000001;
internal const uint TREEVIEW_CLASSES = 0x00000002;
internal const uint BAR_CLASSES = 0x00000004;
internal const uint TAB_CLASSES = 0x00000008;
internal const uint UPDOWN_CLASS = 0x00000010;
internal const uint PROGRESS_CLASS = 0x00000020;
internal const uint HOTKEY_CLASS = 0x00000040;
internal const uint ANIMATE_CLASS = 0x00000080;
internal const uint WIN95_CLASSES = 0x000000FF;
internal const uint DATE_CLASSES = 0x00000100;
internal const uint USEREX_CLASSES = 0x00000200;
internal const uint COOL_CLASSES = 0x00000400;
internal const uint INTERNET_CLASSES = 0x00000800;
internal const uint PAGESCROLLER_CLASS = 0x00001000;
internal const uint NATIVEFNTCTL_CLASS = 0x00002000;
internal const uint STANDARD_CLASSES = 0x00004000;
internal const uint LINK_CLASS = 0x00008000;
}

internal static class SWP
{
internal const uint NOSIZE = 0x0001;
internal const uint NOMOVE = 0x0002;
internal const uint NOZORDER = 0x0004;
internal const uint NOREDRAW = 0x0008;
internal const uint NOACTIVATE = 0x0010;
internal const uint FRAMECHANGED = 0x0020;
internal const uint SHOWWINDOW = 0x0040;
internal const uint HIDEWINDOW = 0x0080;
internal const uint NOCOPYBITS = 0x0100;
internal const uint NOOWNERZORDER = 0x0200;
internal const uint NOSENDCHANGING = 0x0400;
}
9 changes: 8 additions & 1 deletion src/EasyWindowsApplication/Core/ControlBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ internal ControlBase() { }
public nint Hwnd { get; internal set; }
public string Name { get; set; } = "";

// IBaseWindow lifecycle events (requeridos por IBaseWindow pero no usados en controles)
public event EventHandler? Loaded { add { } remove { } }
public event EventHandler<Share.CancelEventArgs>? Closing { add { } remove { } }
public event EventHandler? Closed { add { } remove { } }
public event EventHandler? Activated { add { } remove { } }
public event EventHandler? Deactivated { add { } remove { } }

private float _x, _y, _w, _h;

internal event Action? InternalClick;
Expand Down Expand Up @@ -142,7 +149,7 @@ internal void DestroyBackgroundBrush()
float ILayoutable.MeasuredHeight => _measuredH;

private float _measuredW, _measuredH;
private float _arrangedX, _arrangedY, _arrangedW, _arrangedH;
internal float _arrangedX, _arrangedY, _arrangedW, _arrangedH;

void ILayoutable.Measure(float availableWidth, float availableHeight)
{
Expand Down
16 changes: 16 additions & 0 deletions src/EasyWindowsApplication/Core/Entities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,22 @@ internal struct SCROLLINFO
public int nTrackPos;
}

[StructLayout(LayoutKind.Sequential)]
internal struct INITCOMMONCONTROLSEX
{
public uint dwSize;
public uint dwICC;
}

[StructLayout(LayoutKind.Sequential)]
internal struct MONITORINFO
{
public uint cbSize;
public RECT rcMonitor;
public RECT rcWork;
public uint dwFlags;
}

[StructLayout(LayoutKind.Sequential)]
internal struct DRAWITEMSTRUCT
{
Expand Down
Loading
Loading