Add windowed terminal, basic text, rectangle rendering, and keyboard events

This commit is contained in:
Stone_Red
2026-06-08 17:42:08 +02:00
parent 57f011ffe8
commit fc71af028f
10 changed files with 284 additions and 12 deletions
+89 -6
View File
@@ -3,6 +3,7 @@ using System.Drawing;
using System.Collections.Generic;
using System.Linq;
using Cosmos.Kernel.System.Graphics;
using Cosmos.Kernel.System.Graphics.Fonts;
namespace RemSox.UI.GUI.Rendering;
@@ -12,15 +13,32 @@ public sealed class CanvasRenderSource : IRenderSource
private static readonly Dictionary<int, Point> windowPositions = new();
private static readonly Dictionary<int, int> windowZIndices = new();
private static bool isDirty = true;
private static Point lastPointerPosition = new Point(-1, -1);
private static List<int> orderedWindowsCache = new();
private static bool isZOrderDirty = true;
public void Render(IEnumerable<RenderCommand> commands)
{
bool changed = false;
foreach (RenderCommand command in commands)
{
changed = true;
if (command.ElementType == "WindowClose")
{
windowCanvases.Remove(command.WindowId);
windowPositions.Remove(command.WindowId);
windowZIndices.Remove(command.WindowId);
isZOrderDirty = true;
continue;
}
if (command.ElementType == "Window" || command.ElementType == "WindowMove")
{
if (command.Properties.TryGetValue("ZIndex", out object? rawZIndex) && rawZIndex is int z)
{
windowZIndices[command.WindowId] = z;
isZOrderDirty = true;
}
}
@@ -30,14 +48,14 @@ public sealed class CanvasRenderSource : IRenderSource
? windowSize
: new Size(160, 120);
if (!windowCanvases.TryGetValue(command.WindowId, out Canvas? currentCanvas) ||
currentCanvas.Mode.Width != size.Width ||
if (!windowCanvases.TryGetValue(command.WindowId, out Canvas? currentCanvas) ||
currentCanvas.Mode.Width != size.Width ||
currentCanvas.Mode.Height != size.Height)
{
windowCanvases[command.WindowId] = new Canvas(size.Width, size.Height);
}
}
if (!windowCanvases.ContainsKey(command.WindowId))
{
windowCanvases[command.WindowId] = new Canvas(160, 120);
@@ -62,16 +80,40 @@ public sealed class CanvasRenderSource : IRenderSource
{
RenderCircle(windowCanvas, command);
}
if (command.ElementType == "Rectangle")
{
RenderRectangle(windowCanvas, command);
}
if (command.ElementType == "Text")
{
RenderText(windowCanvas, command);
}
}
if (changed)
{
isDirty = true;
}
}
public static void CompositeAndDisplay(Canvas screenCanvas, Point pointerPosition)
{
screenCanvas.Clear(Color.Black);
if (!isDirty && pointerPosition == lastPointerPosition)
{
return;
}
var orderedWindows = windowPositions.Keys.OrderBy(id => windowZIndices.TryGetValue(id, out int z) ? z : 0);
if (isZOrderDirty)
{
orderedWindowsCache = windowPositions.Keys.OrderBy(id => windowZIndices.TryGetValue(id, out int z) ? z : 0).ToList();
isZOrderDirty = false;
}
foreach (var windowId in orderedWindows)
//screenCanvas.Clear(Color.Black);
foreach (var windowId in orderedWindowsCache)
{
if (windowPositions.TryGetValue(windowId, out Point position) && windowCanvases.TryGetValue(windowId, out Canvas? windowCanvas))
{
@@ -81,6 +123,9 @@ public sealed class CanvasRenderSource : IRenderSource
screenCanvas.DrawFilledCircle(Color.White, pointerPosition.X, pointerPosition.Y, 5);
screenCanvas.Display();
lastPointerPosition = pointerPosition;
isDirty = false;
}
private static void RenderWindow(Canvas canvas, RenderCommand command)
@@ -115,4 +160,42 @@ public sealed class CanvasRenderSource : IRenderSource
canvas.DrawFilledCircle(color, centerX, centerY, radius);
}
private static void RenderRectangle(Canvas canvas, RenderCommand command)
{
Color color = command.Properties.TryGetValue("Color", out object? rawColor) && rawColor is Color rectColor
? rectColor
: Color.White;
Size size = command.Properties.TryGetValue("Size", out object? rawSize) && rawSize is Size rectSize
? rectSize
: new Size(10, 10);
bool isFilled = command.Properties.TryGetValue("IsFilled", out object? rawFilled) && rawFilled is bool filled && filled;
if (isFilled)
{
canvas.DrawFilledRectangle(color, command.Position.X, command.Position.Y, size.Width, size.Height);
}
else
{
canvas.DrawRectangle(color, command.Position.X, command.Position.Y, size.Width, size.Height);
}
}
private static void RenderText(Canvas canvas, RenderCommand command)
{
Color color = command.Properties.TryGetValue("Color", out object? rawColor) && rawColor is Color textColor
? textColor
: Color.White;
string content = command.Properties.TryGetValue("Content", out object? rawContent) && rawContent is string textContent
? textContent
: string.Empty;
if (!string.IsNullOrEmpty(content))
{
canvas.DrawString(content, Cosmos.Kernel.System.Graphics.Fonts.PCScreenFont.DefaultFont, color, command.Position.X, command.Position.Y);
}
}
}
+18
View File
@@ -0,0 +1,18 @@
using System.Drawing;
namespace RemSox.UI.GUI.UIEelements.Shapes;
public class Rectangle() : Shape("Rectangle")
{
public Size Size
{
get;
set => SetProperty(nameof(Size), ref field, value);
}
public bool IsFilled
{
get;
set => SetProperty(nameof(IsFilled), ref field, value);
} = true;
}
+18
View File
@@ -0,0 +1,18 @@
using System.Drawing;
namespace RemSox.UI.GUI.UIEelements;
public class Text() : UIElement("Text")
{
public string Content
{
get;
set => SetProperty(nameof(Content), ref field, value);
} = string.Empty;
public Color Color
{
get;
set => SetProperty(nameof(Color), ref field, value);
} = Color.White;
}
+7
View File
@@ -18,6 +18,13 @@ public sealed class Window(string title, int processId, int id, IRenderSource re
public bool AutoFlush { get; set; } = false;
public event Action<Cosmos.Kernel.System.Keyboard.KeyEvent>? OnKeyEvent;
public void HandleKeyEvent(Cosmos.Kernel.System.Keyboard.KeyEvent keyEvent)
{
OnKeyEvent?.Invoke(keyEvent);
}
public bool IsFocused
{
get => WindowManager.IsWindowFocused(this);
+8
View File
@@ -5,6 +5,7 @@ using System.Drawing;
using System.Linq;
using Cosmos.Kernel.System.Graphics;
using Cosmos.Kernel.System.Mouse;
using Cosmos.Kernel.System.Keyboard;
using RemSox.Processing;
using RemSox.UI.GUI.Rendering;
@@ -51,6 +52,11 @@ public static class WindowManager
wasLeftButtonDown = leftButtonDown;
if (KeyboardManager.TryReadKey(out KeyEvent keyEvent))
{
focusedWindow?.HandleKeyEvent(keyEvent);
}
Canvas canvas = FullScreenCanvas.GetFullScreenCanvas();
CanvasRenderSource.CompositeAndDisplay(canvas, pointerPosition);
@@ -86,6 +92,8 @@ public static class WindowManager
{
processWindows.Remove(window);
}
renderSource.Render(new[] { new RenderCommand { WindowId = window.Id, ElementId = window.Id, ElementType = "WindowClose", Position = window.Position, Properties = new Dictionary<string, object?>() } });
}
public static List<Window> GetWindowsForProcess(Process process)