diff --git a/Processes/DesktopProcess.cs b/Processes/DesktopProcess.cs index a1a2ba7..c49148b 100644 --- a/Processes/DesktopProcess.cs +++ b/Processes/DesktopProcess.cs @@ -1,5 +1,3 @@ -using Cosmos.Kernel.System.Graphics; - using RemSox.Processing; using RemSox.UI.GUI.Rendering; using RemSox.UI.GUI.Windows; @@ -18,17 +16,11 @@ internal class DesktopProcess() : Process("Desktop Manager") isGraphicsInitialized = true; } - // Trigger Canvas initialization - _ = FullScreenCanvas.GetFullScreenCanvas(); - // Force existing windows to redraw onto the new canvas renderer WindowManager.InvalidateAll(); - // Start the terminal process within the desktop environment - _ = ProcessManager.SpawnProcess(); - // Create a test window for new UI controls - Window testWindow = WindowManager.CreateWindow(this, "UI Controls Test", new System.Drawing.Point(500, 50), new System.Drawing.Size(200, 180)); + Window testWindow = WindowManager.CreateWindow(this, "UI Controls Test", new System.Drawing.Size(200, 180)); _ = testWindow.CreateUIElement(b => { diff --git a/Processes/TerminalProcess.cs b/Processes/TerminalProcess.cs index d29c39b..7ac94af 100644 --- a/Processes/TerminalProcess.cs +++ b/Processes/TerminalProcess.cs @@ -9,7 +9,7 @@ using System.Drawing; namespace RemSox.Processes; -public class TerminalProcess : Process +public class TerminalProcess() : Process("Terminal") { private Window window = null!; private readonly List history = []; @@ -19,13 +19,9 @@ public class TerminalProcess : Process private const int LineHeight = 30; private Size lastSize = new(-1, -1); - public TerminalProcess() : base("Terminal") - { - } - internal override void Start(string[] args) { - window = WindowManager.CreateWindow(this, "Terminal", new Point(50, 50), new Size(400, 300)); + window = WindowManager.CreateWindow(this, "Terminal", new Size(400, 300)); PrintLine("RemSox GUI Terminal v1.0"); PrintLine("Type 'help' for commands."); @@ -58,10 +54,18 @@ public class TerminalProcess : Process if (!string.IsNullOrWhiteSpace(cmd)) { - if (cmd == "exit") + if (cmd.Trim() == "exit") { RequestStop(); } + else if (cmd.Trim() == "clear") + { + lock (textLinesLock) + { + history.Clear(); + UpdateDisplay(); + } + } else { bool found = CommandManager.TryExecute(cmd, PrintLine); diff --git a/Processes/TestProcess.cs b/Processes/TestProcess.cs index 8f20592..473230e 100644 --- a/Processes/TestProcess.cs +++ b/Processes/TestProcess.cs @@ -11,7 +11,7 @@ public class TestProcess() : Process("Test Process") { internal override void Start(string[] args) { - Window window = WindowManager.CreateWindow(this, "Test Window", Point.Empty, new Size(200, 150)); + Window window = WindowManager.CreateWindow(this, "Test Window", new Size(200, 150)); window.AutoFlush = true; Circle circle = window.CreateUIElement(rect => diff --git a/UI/GUI/Windows/Window.cs b/UI/GUI/Windows/Window.cs index ea7df6a..a8e124a 100644 --- a/UI/GUI/Windows/Window.cs +++ b/UI/GUI/Windows/Window.cs @@ -288,7 +288,7 @@ public sealed class Window(string title, int processId, int id, IRenderSource re /// /// Updates the drag or resize interaction based on the current pointer position. /// - public void UpdateInteraction(Point pointerPosition, Point screenSize) + public void UpdateInteraction(Point pointerPosition, Size screenSize) { if (currentInteraction == InteractionMode.Drag) { @@ -300,8 +300,8 @@ public sealed class Window(string title, int processId, int id, IRenderSource re newY = Math.Max(0, newY); // Clamp right and bottom edges - newX = Math.Min(screenSize.X - Size.Width, newX); - newY = Math.Min(screenSize.Y - Size.Height, newY); + newX = Math.Min(screenSize.Width - Size.Width, newX); + newY = Math.Min(screenSize.Height - Size.Height, newY); Position = new Point(newX, newY); Flush(); @@ -322,12 +322,12 @@ public sealed class Window(string title, int processId, int id, IRenderSource re if (currentInteraction is InteractionMode.ResizeRight or InteractionMode.ResizeBottomRight or InteractionMode.ResizeTopRight) { newW = Math.Max(minWidth, interactionStartBounds.Width + dx); - newW = Math.Min(newW, screenSize.X - newX); + newW = Math.Min(newW, screenSize.Width - newX); } if (currentInteraction is InteractionMode.ResizeBottom or InteractionMode.ResizeBottomRight or InteractionMode.ResizeBottomLeft) { newH = Math.Max(minHeight, interactionStartBounds.Height + dy); - newH = Math.Min(newH, screenSize.Y - newY); + newH = Math.Min(newH, screenSize.Height - newY); } if (currentInteraction is InteractionMode.ResizeLeft or InteractionMode.ResizeBottomLeft or InteractionMode.ResizeTopLeft) { diff --git a/UI/GUI/Windows/WindowManager.cs b/UI/GUI/Windows/WindowManager.cs index 2907ef0..13fa2fd 100644 --- a/UI/GUI/Windows/WindowManager.cs +++ b/UI/GUI/Windows/WindowManager.cs @@ -45,7 +45,7 @@ public static class WindowManager } else if (leftButtonDown && activeInteractWindow is not null) { - activeInteractWindow.UpdateInteraction(pointerPosition, new Point((int)canvas.Mode.Width, (int)canvas.Mode.Height)); + activeInteractWindow.UpdateInteraction(pointerPosition, new Size((int)canvas.Mode.Width, (int)canvas.Mode.Height)); } else if (!leftButtonDown && activeInteractWindow is not null) { @@ -106,6 +106,111 @@ public static class WindowManager 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, new Point(bestX, bestY), size); + } + /// /// Closes a specific window and notifies the renderer. ///