mirror of
https://github.com/Stone-Red-Code/RemSox.git
synced 2026-09-04 00:56:19 +02:00
Add automatic window positioning to avoid overlap and maximize spaciousness
This commit is contained in:
@@ -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<TerminalProcess>();
|
||||
|
||||
// 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<UI.GUI.UIEelements.Controls.Button>(b =>
|
||||
{
|
||||
|
||||
@@ -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<string> 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);
|
||||
|
||||
@@ -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<Circle>(rect =>
|
||||
|
||||
@@ -288,7 +288,7 @@ public sealed class Window(string title, int processId, int id, IRenderSource re
|
||||
/// <summary>
|
||||
/// Updates the drag or resize interaction based on the current pointer position.
|
||||
/// </summary>
|
||||
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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates and registers a new window, automatically finding the most spacious, least overlapping position.
|
||||
/// </summary>
|
||||
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<Window> 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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes a specific window and notifies the renderer.
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user