diff --git a/Networking/TcpRpcServer.cs b/Networking/TcpRpcServer.cs
index ce2542e..6e25b23 100644
--- a/Networking/TcpRpcServer.cs
+++ b/Networking/TcpRpcServer.cs
@@ -87,6 +87,24 @@ public class TcpRpcServer(IPacketCrypto? crypto = null) : TcpRpcBase(crypto)
return Task.WhenAll(sendTasks);
}
+ /// Broadcasts raw binary payload to all connected clients.
+ public Task SendRawToAll(string type, byte[] payload)
+ {
+ List sendTasks = [];
+
+ foreach (TcpConnection conn in connections.Keys)
+ {
+ sendTasks.Add(SendRaw(conn, new TcpMessage
+ {
+ Type = type,
+ RequestId = Guid.NewGuid().ToString(),
+ Payload = payload
+ }));
+ }
+
+ return Task.WhenAll(sendTasks);
+ }
+
protected override void OnConnectionClosed(TcpConnection conn)
{
_ = connections.TryRemove(conn, out _);
diff --git a/UI/GUI/Rendering/CanvasRenderSource.cs b/UI/GUI/Rendering/CanvasRenderSource.cs
index a50a7c8..5088a18 100644
--- a/UI/GUI/Rendering/CanvasRenderSource.cs
+++ b/UI/GUI/Rendering/CanvasRenderSource.cs
@@ -1,6 +1,8 @@
using Cosmos.Kernel.System.Graphics;
using Cosmos.Kernel.System.Graphics.Fonts;
+using Cosmos.Kernel.System.Mouse;
+using RemSox.UI.GUI.UIEelements;
using RemSox.Utils;
using System.Drawing;
@@ -14,77 +16,97 @@ public sealed class CanvasRenderSource : IRenderSource
private static readonly Dictionary windowZIndices = [];
// Sorted list keeps windows in Z-order without re-sorting.
- // Key = (zIndex << 16 | windowId) so equal Z stays insertion-stable.
+ // Key = (zIndex << 32 | windowId) so equal Z stays insertion-stable.
private static readonly SortedList zOrderedWindows = [];
- private static bool isContentDirty = true; // pixel content changed
- private static bool isPositionDirty = true; // only layout changed
+ // Accumulated drawing primitives per window (in draw order).
+ private static readonly Dictionary> windowPrimitives = [];
+
+ private static readonly HashSet dirtyWindows = [];
+ private static bool isPositionDirty = true;
private static Point lastPointerPosition = new(-1, -1);
private static readonly Lock renderLock = new();
- private static readonly Dictionary> elementRenderers = new()
- {
- ["Circle"] = RenderCircle,
- ["Rectangle"] = RenderRectangle,
- ["Text"] = RenderText,
- ["Line"] = RenderLine,
- ["Button"] = RenderButton,
- ["CheckBox"] = RenderCheckBox,
- };
-
public void Render(IEnumerable commands)
{
lock (renderLock)
{
foreach (RenderCommand command in commands)
{
- switch (command.ElementType)
+ switch (command.Type)
{
- case "WindowClose":
- RemoveWindow(command.WindowId);
- continue;
+ case RenderCommandType.CreateWindow:
+ CreateOrUpdateWindow(command);
+ break;
- case "WindowMove":
+ case RenderCommandType.DestroyWindow:
+ RemoveWindow(command.WindowId);
+ break;
+
+ case RenderCommandType.MoveWindow:
if (windowCanvases.ContainsKey(command.WindowId))
{
windowPositions[command.WindowId] = command.Position;
isPositionDirty = true;
}
- continue;
+ break;
- case "Window":
- ProcessWindowCommand(command);
- isContentDirty = true;
- continue;
- }
+ case RenderCommandType.RemovePrimitives:
+ RemovePrimitives(command.WindowId, command.ElementId);
+ break;
- if (!windowCanvases.TryGetValue(command.WindowId, out Canvas? canvas))
- {
- continue;
- }
-
- if (elementRenderers.TryGetValue(command.ElementType, out Action