using Cosmos.Kernel.System.Graphics; using Cosmos.Kernel.System.Keyboard; using Cosmos.Kernel.System.Mouse; using RemSox.Processing; using RemSox.UI.GUI.Rendering; using System.Drawing; namespace RemSox.UI.GUI.Windows; /// /// Manages window creation, focus, interaction, and rendering orchestration for the GUI system. /// public static class WindowManager { private static readonly Lock windowsLock = new(); // Process ID to list of windows private static readonly Dictionary> windows = []; private static int nextWindowId = 1; private static int nextZIndex = 1; private static Window? focusedWindow; private static Window? activeInteractWindow = null; private static Point lastPointerPosition = Point.Empty; private static bool wasLeftButtonDown = false; private static readonly MuliRenderSource renderSource = new([]); /// /// Processes input, updates interaction state, and triggers rendering of all windows. /// internal static void Update() { Point pointerPosition = new(MouseManager.X, MouseManager.Y); bool leftButtonDown = MouseManager.LeftButton; Canvas canvas = FullScreenCanvas.GetFullScreenCanvas(); if (leftButtonDown && !wasLeftButtonDown) { activeInteractWindow = TryBeginInteract(pointerPosition); } else if (leftButtonDown && activeInteractWindow is not null) { activeInteractWindow.UpdateInteraction(pointerPosition, new Size((int)canvas.Mode.Width, (int)canvas.Mode.Height)); } else if (!leftButtonDown && activeInteractWindow is not null) { activeInteractWindow.EndInteraction(); activeInteractWindow = null; } wasLeftButtonDown = leftButtonDown; while (KeyboardManager.TryReadKey(out KeyEvent? keyEvent) && keyEvent is not null) { focusedWindow?.HandleKeyEvent(keyEvent); } CanvasRenderSource.CompositeAndDisplay(canvas, pointerPosition); lastPointerPosition = pointerPosition; } /// /// Adds a new rendering source to the compositor. /// public static void AddRenderSource(IRenderSource source) { renderSource.AddSource(source); } /// /// Removes an existing rendering source from the compositor. /// public static void RemoveRenderSource(IRenderSource source) { renderSource.RemoveSource(source); } /// /// Creates and registers a new window for the specified process. /// public static Window CreateWindow(Process process, string title, Size size, Point position) { Window window = new(title, process.Id, GetNextWindowId(), renderSource) { Position = position, Size = size, ZIndex = nextZIndex++ }; lock (windowsLock) { if (!windows.ContainsKey(process.Id)) { windows[process.Id] = []; } windows[process.Id].Add(window); } return window; } /// /// Creates and registers a new window, automatically finding the most spacious, least overlapping position. /// public static Window CreateWindow(Process process, string title, Size size) { Canvas canvas = FullScreenCanvas.GetFullScreenCanvas(); int screenWidth = (int)canvas.Mode.Width; int screenHeight = (int)canvas.Mode.Height; List existingWindows; lock (windowsLock) { existingWindows = windows.Values.SelectMany(w => w).ToList(); } int bestX = 50; int bestY = 50; int minOverlap = int.MaxValue; int maxSpaciousness = -1; const int step = 40; int maxX = Math.Max(0, screenWidth - size.Width); int maxY = Math.Max(0, screenHeight - size.Height); for (int y = 0; y <= maxY; y += step) { for (int x = 0; x <= maxX; x += step) { int currentOverlap = 0; int minWindowDist = int.MaxValue; // Edges of the proposed window int rectLeft = x; int rectTop = y; int rectRight = x + size.Width; int rectBottom = y + size.Height; foreach (Window win in existingWindows) { int winLeft = win.Position.X; int winTop = win.Position.Y; int winRight = win.Position.X + win.Size.Width; int winBottom = win.Position.Y + win.Size.Height; // Calculate Overlap (AABB) int intersectLeft = Math.Max(rectLeft, winLeft); int intersectTop = Math.Max(rectTop, winTop); int intersectRight = Math.Min(rectRight, winRight); int intersectBottom = Math.Min(rectBottom, winBottom); if (intersectRight > intersectLeft && intersectBottom > intersectTop) { currentOverlap += (intersectRight - intersectLeft) * (intersectBottom - intersectTop); } // Calculate True Edge-to-Edge Distance (Manhattan) // If the windows overlap, dx and dy will be 0. int dx = Math.Max(0, Math.Max(winLeft - rectRight, rectLeft - winRight)); int dy = Math.Max(0, Math.Max(winTop - rectBottom, rectTop - winBottom)); int dist = dx + dy; if (dist < minWindowDist) { minWindowDist = dist; } } // Calculate Distance to Screen Borders int borderDistLeft = x; int borderDistTop = y; int borderDistRight = screenWidth - rectRight; int borderDistBottom = screenHeight - rectBottom; int minBorderDist = Math.Min(Math.Min(borderDistLeft, borderDistTop), Math.Min(borderDistRight, borderDistBottom)); // Spaciousness evaluates the closest boundary (either a screen edge or another window edge) int currentSpaciousness = existingWindows.Count == 0 ? minBorderDist : Math.Min(minBorderDist, minWindowDist); // Score Evaluation if (currentOverlap < minOverlap) { minOverlap = currentOverlap; maxSpaciousness = currentSpaciousness; bestX = x; bestY = y; } else if (currentOverlap == minOverlap) { // If overlap is tied (e.g., both are 0), pick the most spacious position if (currentSpaciousness > maxSpaciousness) { maxSpaciousness = currentSpaciousness; bestX = x; bestY = y; } } } } return CreateWindow(process, title, size, new Point(bestX, bestY)); } /// /// Closes a specific window and notifies the renderer. /// public static void CloseWindow(Window window) { lock (windowsLock) { if (windows.TryGetValue(window.ProcessId, out List? processWindows)) { _ = processWindows.Remove(window); } } if (focusedWindow == window) { focusedWindow = null; } if (activeInteractWindow == window) { activeInteractWindow = null; } renderSource.Render([new RenderCommand { WindowId = window.Id, ElementId = window.Id, ElementType = "WindowClose", Position = window.Position, Properties = new Dictionary() }]); } /// /// Returns a list of all windows belonging to the given process. /// public static List GetWindowsForProcess(Process process) { lock (windowsLock) { if (windows.TryGetValue(process.Id, out List? processWindows)) { return processWindows.ToList(); } return []; } } /// /// Closes all windows belonging to the given process. /// public static void CloseWindowsForProcess(Process process) { CloseWindowsForProcess(process.Id); } /// /// Closes all windows belonging to the given process ID. /// public static void CloseWindowsForProcess(int processId) { List windowsToClose = []; lock (windowsLock) { if (windows.TryGetValue(processId, out List? processWindows)) { windowsToClose.AddRange(processWindows); _ = windows.Remove(processId); } } if (windowsToClose.Count > 0) { if (focusedWindow != null && windowsToClose.Contains(focusedWindow)) { focusedWindow = null; } if (activeInteractWindow != null && windowsToClose.Contains(activeInteractWindow)) { activeInteractWindow = null; } List closeCommands = []; foreach (Window window in windowsToClose) { closeCommands.Add(new RenderCommand { WindowId = window.Id, ElementId = window.Id, ElementType = "WindowClose", Position = window.Position, Properties = new Dictionary() }); } renderSource.Render(closeCommands); } } /// /// Sets the focus to the specified window, bringing it to the foreground. /// public static void FocusWindow(Window? window) { if (focusedWindow == window) { return; } _ = window?.ZIndex = nextZIndex++; Window? previousFocusedWindow = focusedWindow; focusedWindow = window; previousFocusedWindow?.Flush(); focusedWindow?.Flush(); } /// /// Checks if the specified window is currently focused. /// public static bool IsWindowFocused(Window window) { return focusedWindow == window; } /// /// Attempts to begin interaction (drag/resize) with a window at the specified pointer position. /// public static Window? TryBeginInteract(Point pointerPosition) { List allWindows; lock (windowsLock) { allWindows = windows.Values.SelectMany(w => w).OrderByDescending(w => w.ZIndex).ToList(); } foreach (Window window in allWindows) { if (window.TryBeginInteract(pointerPosition)) { return window; } } return null; } /// /// Forces a full redraw of all windows in the system. /// public static void InvalidateAll() { List allWindows; lock (windowsLock) { allWindows = windows.Values.SelectMany(w => w).ToList(); } foreach (Window window in allWindows) { window.Invalidate(); } } private static int GetNextWindowId() { return nextWindowId++; } private sealed class MuliRenderSource(List sources) : IRenderSource { private readonly Lock sourcesLock = new(); public void AddSource(IRenderSource source) { lock (sourcesLock) { sources.Add(source); } } public void RemoveSource(IRenderSource source) { lock (sourcesLock) { _ = sources.Remove(source); } } public void Render(IEnumerable commands) { List sourcesCopy; lock (sourcesLock) { sourcesCopy = sources.ToList(); } foreach (IRenderSource source in sourcesCopy) { source.Render(commands); } } } }