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.Processing;
|
||||||
using RemSox.UI.GUI.Rendering;
|
using RemSox.UI.GUI.Rendering;
|
||||||
using RemSox.UI.GUI.Windows;
|
using RemSox.UI.GUI.Windows;
|
||||||
@@ -18,17 +16,11 @@ internal class DesktopProcess() : Process("Desktop Manager")
|
|||||||
isGraphicsInitialized = true;
|
isGraphicsInitialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trigger Canvas initialization
|
|
||||||
_ = FullScreenCanvas.GetFullScreenCanvas();
|
|
||||||
|
|
||||||
// Force existing windows to redraw onto the new canvas renderer
|
// Force existing windows to redraw onto the new canvas renderer
|
||||||
WindowManager.InvalidateAll();
|
WindowManager.InvalidateAll();
|
||||||
|
|
||||||
// Start the terminal process within the desktop environment
|
|
||||||
_ = ProcessManager.SpawnProcess<TerminalProcess>();
|
|
||||||
|
|
||||||
// Create a test window for new UI controls
|
// 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 =>
|
_ = testWindow.CreateUIElement<UI.GUI.UIEelements.Controls.Button>(b =>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ using System.Drawing;
|
|||||||
|
|
||||||
namespace RemSox.Processes;
|
namespace RemSox.Processes;
|
||||||
|
|
||||||
public class TerminalProcess : Process
|
public class TerminalProcess() : Process("Terminal")
|
||||||
{
|
{
|
||||||
private Window window = null!;
|
private Window window = null!;
|
||||||
private readonly List<string> history = [];
|
private readonly List<string> history = [];
|
||||||
@@ -19,13 +19,9 @@ public class TerminalProcess : Process
|
|||||||
private const int LineHeight = 30;
|
private const int LineHeight = 30;
|
||||||
private Size lastSize = new(-1, -1);
|
private Size lastSize = new(-1, -1);
|
||||||
|
|
||||||
public TerminalProcess() : base("Terminal")
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
internal override void Start(string[] args)
|
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("RemSox GUI Terminal v1.0");
|
||||||
PrintLine("Type 'help' for commands.");
|
PrintLine("Type 'help' for commands.");
|
||||||
@@ -58,10 +54,18 @@ public class TerminalProcess : Process
|
|||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(cmd))
|
if (!string.IsNullOrWhiteSpace(cmd))
|
||||||
{
|
{
|
||||||
if (cmd == "exit")
|
if (cmd.Trim() == "exit")
|
||||||
{
|
{
|
||||||
RequestStop();
|
RequestStop();
|
||||||
}
|
}
|
||||||
|
else if (cmd.Trim() == "clear")
|
||||||
|
{
|
||||||
|
lock (textLinesLock)
|
||||||
|
{
|
||||||
|
history.Clear();
|
||||||
|
UpdateDisplay();
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
bool found = CommandManager.TryExecute(cmd, PrintLine);
|
bool found = CommandManager.TryExecute(cmd, PrintLine);
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ public class TestProcess() : Process("Test Process")
|
|||||||
{
|
{
|
||||||
internal override void Start(string[] args)
|
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;
|
window.AutoFlush = true;
|
||||||
|
|
||||||
Circle circle = window.CreateUIElement<Circle>(rect =>
|
Circle circle = window.CreateUIElement<Circle>(rect =>
|
||||||
|
|||||||
@@ -288,7 +288,7 @@ public sealed class Window(string title, int processId, int id, IRenderSource re
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates the drag or resize interaction based on the current pointer position.
|
/// Updates the drag or resize interaction based on the current pointer position.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void UpdateInteraction(Point pointerPosition, Point screenSize)
|
public void UpdateInteraction(Point pointerPosition, Size screenSize)
|
||||||
{
|
{
|
||||||
if (currentInteraction == InteractionMode.Drag)
|
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);
|
newY = Math.Max(0, newY);
|
||||||
|
|
||||||
// Clamp right and bottom edges
|
// Clamp right and bottom edges
|
||||||
newX = Math.Min(screenSize.X - Size.Width, newX);
|
newX = Math.Min(screenSize.Width - Size.Width, newX);
|
||||||
newY = Math.Min(screenSize.Y - Size.Height, newY);
|
newY = Math.Min(screenSize.Height - Size.Height, newY);
|
||||||
|
|
||||||
Position = new Point(newX, newY);
|
Position = new Point(newX, newY);
|
||||||
Flush();
|
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)
|
if (currentInteraction is InteractionMode.ResizeRight or InteractionMode.ResizeBottomRight or InteractionMode.ResizeTopRight)
|
||||||
{
|
{
|
||||||
newW = Math.Max(minWidth, interactionStartBounds.Width + dx);
|
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)
|
if (currentInteraction is InteractionMode.ResizeBottom or InteractionMode.ResizeBottomRight or InteractionMode.ResizeBottomLeft)
|
||||||
{
|
{
|
||||||
newH = Math.Max(minHeight, interactionStartBounds.Height + dy);
|
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)
|
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)
|
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)
|
else if (!leftButtonDown && activeInteractWindow is not null)
|
||||||
{
|
{
|
||||||
@@ -106,6 +106,111 @@ public static class WindowManager
|
|||||||
return window;
|
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>
|
/// <summary>
|
||||||
/// Closes a specific window and notifies the renderer.
|
/// Closes a specific window and notifies the renderer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
Reference in New Issue
Block a user