From 7488c19a286c05b52a5b121113606ac70612d2e6 Mon Sep 17 00:00:00 2001 From: Stone_Red <56473591+Stone-Red-Code@users.noreply.github.com> Date: Tue, 16 Jun 2026 22:25:46 +0200 Subject: [PATCH] Rework YesNtWindowStatements.cs --- Utils/YesNtWindowStatements.cs | 1052 ++++++++++++++++++++------------ 1 file changed, 652 insertions(+), 400 deletions(-) diff --git a/Utils/YesNtWindowStatements.cs b/Utils/YesNtWindowStatements.cs index ef655ff..b52f2dd 100644 --- a/Utils/YesNtWindowStatements.cs +++ b/Utils/YesNtWindowStatements.cs @@ -1,4 +1,5 @@ using RemSox.Processing; +using RemSox.UI.GUI.UIEelements; using RemSox.UI.GUI.UIEelements.Controls; using RemSox.UI.GUI.UIEelements.Shapes; using RemSox.UI.GUI.Windows; @@ -8,49 +9,35 @@ using System.Drawing; using YesNt.Interpreter.Runtime; using YesNt.Interpreter.Utilities; +using ShapesRectangle = RemSox.UI.GUI.UIEelements.Shapes.Rectangle; + namespace RemSox.Utils; /// /// Registers all YesNt statements that expose the WindowManager / UI element API. /// -/// Window management: -/// win_create "My Window" 320 240 creates a window, read back id with %win_last_id -/// win_close ${myWinId} closes/destroys a window by id -/// win_flush ${myWinId} forces a redraw of the window -/// win_title ${myWinId} "New Title" changes the window title -/// win_autoflush ${myWinId} true enables/disables auto-flush -/// win_invalidate_all invalidates and redraws every window +/// All ui_* and win_* statements support two argument styles: +/// positional: ui_rect ${id} 20 20 100 60 0 128 255 +/// named: ui_rect ${id} x=20 y=20 w=100 h=60 r=0 g=128 b=255 /// -/// UI element creation (read back id with %ui_last_id after each call): -/// ui_button ${myWinId} 20 30 100 30 "Click Me" -/// ui_checkbox ${myWinId} 20 80 "Check Me" true -/// ui_label ${myWinId} 20 10 "Hello World" -/// ui_textbox ${myWinId} 20 50 160 24 "placeholder" -/// ui_line ${myWinId} 20 130 180 130 255 0 0 (x1 y1 x2 y2 R G B) -/// ui_rect ${myWinId} 20 20 100 60 0 128 255 (x y w h R G B) +/// Commas are treated as whitespace in both modes, so you can write: +/// ui_rect ${id} x=20,y=20,w=100,h=60,r=0,g=128,b=255 /// -/// Inline substitutions (use inside any line, like %read_line): +/// If any argument contains '=', the statement parses in named mode +/// (order-independent). Otherwise, positional mode is used. +/// +/// Substitutions: /// %win_last_id expands to the id of the last created window /// %ui_last_id expands to the id of the last created UI element /// public class YesNtWindowStatements(Process ownerProcess) { - // Maps script-visible integer ids to actual Window objects. private readonly Dictionary windows = []; private readonly Dictionary uiElements = []; - private int lastWindowId = 0; - private int lastUiId = 0; + private int lastWindowId; + private int lastUiId; - /// - /// Call this from to register - /// every window-related statement with the given interpreter. - /// - /// The live interpreter instance. - /// - /// The that will own created windows - /// (usually the itself). - /// public void Register(YesNtInterpreter interpreter) { RegisterWindowStatements(interpreter); @@ -58,174 +45,326 @@ public class YesNtWindowStatements(Process ownerProcess) RegisterCheckBoxStatement(interpreter); RegisterLabelStatement(interpreter); RegisterTextBoxStatement(interpreter); + RegisterRadioButtonStatement(interpreter); + RegisterProgressBarStatement(interpreter); + RegisterSliderStatement(interpreter); + RegisterPanelStatement(interpreter); RegisterLineStatement(interpreter); RegisterRectStatement(interpreter); + RegisterCircleStatement(interpreter); + RegisterRemoveStatement(interpreter); RegisterLastIdSubstitutions(interpreter); } + // --- Argument parser --- + + private static string[] ParseTokens(string input) + { + return input.Split([' ', ','], StringSplitOptions.RemoveEmptyEntries); + } + + private static bool IsNamed(string[] tokens) + { + foreach (string t in tokens) + { + if (t.Contains('=')) + { + return true; + } + } + return false; + } + + private static bool TryGetInt(string[] tokens, bool named, string name, int pos, out int value) + { + value = 0; + + if (named) + { + string prefix = name + '='; + foreach (string t in tokens) + { + if (t.StartsWith(prefix) && int.TryParse(t.AsSpan(prefix.Length), out value)) + { + return true; + } + } + return false; + } + + if (pos < tokens.Length && int.TryParse(tokens[pos], out value)) + { + return true; + } + return false; + } + + private static bool TryGetString(string[] tokens, bool named, string name, int pos, out string value) + { + value = string.Empty; + + if (named) + { + string prefix = name + '='; + foreach (string t in tokens) + { + if (t.StartsWith(prefix)) + { + value = t[prefix.Length..].FromSafeString(); + return true; + } + } + return false; + } + + if (pos < tokens.Length) + { + value = tokens[pos].FromSafeString(); + return true; + } + return false; + } + + // --- Window statements --- + private void RegisterWindowStatements(YesNtInterpreter interpreter) { - // win_create "Title" width height - // Sets %win_last_id to the new window's id. - // - // win_create "Control Test" 320 240 - // global winId = %win_last_id + // win_create "Title" w h [chrome=true|false] interpreter.AddStatement( - new StatementInformation( - "win_create", - YesNt.Interpreter.Enums.SearchMode.StartOfLine, - YesNt.Interpreter.Enums.SpaceAround.End) - { - Priority = YesNt.Interpreter.Enums.Priority.Normal - }, + new StatementInformation("win_create", YesNt.Interpreter.Enums.SearchMode.StartOfLine, YesNt.Interpreter.Enums.SpaceAround.End) + { Priority = YesNt.Interpreter.Enums.Priority.Normal }, (args, context) => { string trimmed = args.Trim(); - if (!TryParseWindowArgs(trimmed, out string title, out int w, out int h)) + string title; + int w, h; + bool chrome = true; + + if (trimmed.StartsWith('"')) { - context.Exit($"[win_create] Invalid arguments: {trimmed}", false); - return; + int end = trimmed.IndexOf('"', 1); + if (end < 0) + { + context.Exit("[win_create] Missing closing quote", false); + return; + } + title = trimmed[1..end]; + string[] tokens = ParseTokens(trimmed[(end + 1)..]); + bool named = IsNamed(tokens); + + if (!TryGetInt(tokens, named, "w", 0, out w) || !TryGetInt(tokens, named, "h", 1, out h)) + { + context.Exit("[win_create] Expected width and height", false); + return; + } + + if (TryGetString(tokens, named, "chrome", 2, out string chromeStr)) + { + _ = bool.TryParse(chromeStr, out chrome); + } + } + else + { + string[] tokens = ParseTokens(trimmed); + bool named = IsNamed(tokens); + + if (!TryGetString(tokens, named, "title", 0, out title) || + !TryGetInt(tokens, named, "w", 1, out w) || + !TryGetInt(tokens, named, "h", 2, out h)) + { + context.Exit("[win_create] Usage: win_create \"Title\" w h [chrome=false]", false); + return; + } + + if (TryGetString(tokens, named, "chrome", 3, out string chromeStr)) + { + _ = bool.TryParse(chromeStr, out chrome); + } } - Window win = WindowManager.CreateWindow( - ownerProcess, - title, - new Size(w, h)); + Window win = WindowManager.CreateWindow(ownerProcess, title, new Size(w, h)); + win.HasChrome = chrome; int id = lastWindowId = win.Id; windows[id] = win; }); - // win_close + // win_close id interpreter.AddStatement( - new StatementInformation( - "win_close", - YesNt.Interpreter.Enums.SearchMode.StartOfLine, - YesNt.Interpreter.Enums.SpaceAround.End) - { - Priority = YesNt.Interpreter.Enums.Priority.Normal - }, + new StatementInformation("win_close", YesNt.Interpreter.Enums.SearchMode.StartOfLine, YesNt.Interpreter.Enums.SpaceAround.End) + { Priority = YesNt.Interpreter.Enums.Priority.Normal }, (args, context) => { - string idStr = args.Trim(); - if (int.TryParse(idStr, out int id) && windows.TryGetValue(id, out Window? win)) + if (TryGetWindow(args, out Window? win)) { WindowManager.CloseWindow(win); - bool unused = windows.Remove(id); + _ = windows.Remove(win.Id); } }); - // win_flush + // win_flush id interpreter.AddStatement( - new StatementInformation( - "win_flush", - YesNt.Interpreter.Enums.SearchMode.StartOfLine, - YesNt.Interpreter.Enums.SpaceAround.End) - { - Priority = YesNt.Interpreter.Enums.Priority.Normal - }, + new StatementInformation("win_flush", YesNt.Interpreter.Enums.SearchMode.StartOfLine, YesNt.Interpreter.Enums.SpaceAround.End) + { Priority = YesNt.Interpreter.Enums.Priority.Normal }, (args, context) => { - string idStr = args.Trim(); - if (int.TryParse(idStr, out int id) && windows.TryGetValue(id, out Window? win)) + if (TryGetWindow(args, out Window? win)) { win.Flush(); } }); - // win_title "New Title" + // win_title id "New Title" interpreter.AddStatement( - new StatementInformation( - "win_title", - YesNt.Interpreter.Enums.SearchMode.StartOfLine, - YesNt.Interpreter.Enums.SpaceAround.End) - { - Priority = YesNt.Interpreter.Enums.Priority.Normal - }, + new StatementInformation("win_title", YesNt.Interpreter.Enums.SearchMode.StartOfLine, YesNt.Interpreter.Enums.SpaceAround.End) + { Priority = YesNt.Interpreter.Enums.Priority.Normal }, (args, context) => { - string rest = args.Trim(); - int spaceIdx = rest.IndexOf(' '); - if (spaceIdx < 0) { return; } + string trimmed = args.Trim(); + string[] tokens = ParseTokens(trimmed); + bool named = IsNamed(tokens); - string idStr = rest[..spaceIdx].Trim(); - string newTitle = rest[(spaceIdx + 1)..].Trim().Trim('"'); - - if (int.TryParse(idStr, out int id) && windows.TryGetValue(id, out Window? win)) + if (!TryGetInt(tokens, named, "id", 0, out int id) || !windows.TryGetValue(id, out Window? win)) { - win.Title = newTitle; + return; } + + string title; + if (trimmed.Contains('"')) + { + int start = trimmed.IndexOf('"') + 1; + int end = trimmed.IndexOf('"', start); + title = end >= 0 ? trimmed[start..end] : trimmed[start..]; + } + else + { + _ = TryGetString(tokens, named, "title", 1, out title); + } + + win.Title = title; }); - // win_autoflush true|false + // win_autoflush id true|false interpreter.AddStatement( - new StatementInformation( - "win_autoflush", - YesNt.Interpreter.Enums.SearchMode.StartOfLine, - YesNt.Interpreter.Enums.SpaceAround.End) - { - Priority = YesNt.Interpreter.Enums.Priority.Normal - }, + new StatementInformation("win_autoflush", YesNt.Interpreter.Enums.SearchMode.StartOfLine, YesNt.Interpreter.Enums.SpaceAround.End) + { Priority = YesNt.Interpreter.Enums.Priority.Normal }, (args, context) => { - string rest = args.Trim(); - string[] parts = rest.Split(' ', StringSplitOptions.RemoveEmptyEntries); - if (parts.Length == 2 - && int.TryParse(parts[0], out int id) - && bool.TryParse(parts[1], out bool enabled) - && windows.TryGetValue(id, out Window? win)) + string[] tokens = ParseTokens(args.Trim()); + bool named = IsNamed(tokens); + + if (TryGetInt(tokens, named, "id", 0, out int id) && + TryGetString(tokens, named, "enabled", 1, out string enabledStr) && + bool.TryParse(enabledStr, out bool enabled) && + windows.TryGetValue(id, out Window? win)) { win.AutoFlush = enabled; } }); - // win_invalidate_all + // win_chrome id true|false interpreter.AddStatement( - new StatementInformation( - "win_invalidate_all", - YesNt.Interpreter.Enums.SearchMode.Contains, - YesNt.Interpreter.Enums.SpaceAround.None) - { - Priority = YesNt.Interpreter.Enums.Priority.Normal - }, + new StatementInformation("win_chrome", YesNt.Interpreter.Enums.SearchMode.StartOfLine, YesNt.Interpreter.Enums.SpaceAround.End) + { Priority = YesNt.Interpreter.Enums.Priority.Normal }, (args, context) => { - WindowManager.InvalidateAll(); + string[] tokens = ParseTokens(args.Trim()); + bool named = IsNamed(tokens); + + if (TryGetInt(tokens, named, "id", 0, out int id) && + TryGetString(tokens, named, "chrome", 1, out string chromeStr) && + bool.TryParse(chromeStr, out bool chrome) && + windows.TryGetValue(id, out Window? win)) + { + win.HasChrome = chrome; + } }); + + // win_focus id + interpreter.AddStatement( + new StatementInformation("win_focus", YesNt.Interpreter.Enums.SearchMode.StartOfLine, YesNt.Interpreter.Enums.SpaceAround.End) + { Priority = YesNt.Interpreter.Enums.Priority.Normal }, + (args, context) => + { + if (TryGetWindow(args, out Window? win)) + { + WindowManager.FocusWindow(win); + } + }); + + // win_move id x y + interpreter.AddStatement( + new StatementInformation("win_move", YesNt.Interpreter.Enums.SearchMode.StartOfLine, YesNt.Interpreter.Enums.SpaceAround.End) + { Priority = YesNt.Interpreter.Enums.Priority.Normal }, + (args, context) => + { + string[] tokens = ParseTokens(args.Trim()); + bool named = IsNamed(tokens); + + if (TryGetInt(tokens, named, "id", 0, out int id) && + TryGetInt(tokens, named, "x", 1, out int x) && + TryGetInt(tokens, named, "y", 2, out int y) && + windows.TryGetValue(id, out Window? win)) + { + win.Position = new Point(x, y); + } + }); + + // win_resize id w h + interpreter.AddStatement( + new StatementInformation("win_resize", YesNt.Interpreter.Enums.SearchMode.StartOfLine, YesNt.Interpreter.Enums.SpaceAround.End) + { Priority = YesNt.Interpreter.Enums.Priority.Normal }, + (args, context) => + { + string[] tokens = ParseTokens(args.Trim()); + bool named = IsNamed(tokens); + + if (TryGetInt(tokens, named, "id", 0, out int id) && + TryGetInt(tokens, named, "w", 1, out int w) && + TryGetInt(tokens, named, "h", 2, out int h) && + windows.TryGetValue(id, out Window? win)) + { + win.Size = new Size(w, h); + } + }); + + // win_invalidate_all + interpreter.AddStatement( + new StatementInformation("win_invalidate_all", YesNt.Interpreter.Enums.SearchMode.Contains, YesNt.Interpreter.Enums.SpaceAround.None) + { Priority = YesNt.Interpreter.Enums.Priority.Normal }, + (args, context) => WindowManager.InvalidateAll()); } - // ui_button "Label" [R G B] - // ui_button ${winId} 20 30 100 30 "Click Me" - // ui_button ${winId} 20 30 100 30 "Click Me" 173 216 230 + // --- Control statements --- + private void RegisterButtonStatement(YesNtInterpreter interpreter) { interpreter.AddStatement( - new StatementInformation( - "ui_button", - YesNt.Interpreter.Enums.SearchMode.StartOfLine, - YesNt.Interpreter.Enums.SpaceAround.End) - { - Priority = YesNt.Interpreter.Enums.Priority.Normal - }, + new StatementInformation("ui_button", YesNt.Interpreter.Enums.SearchMode.StartOfLine, YesNt.Interpreter.Enums.SpaceAround.End) + { Priority = YesNt.Interpreter.Enums.Priority.Normal }, (args, context) => { - string rest = args.Trim(); - if (!TryParseUiArgs(rest, 5, out int winId, out int[] nums, out string label, out Color color)) + if (!TryGetWindowAndArgs(args, out Window? win, out string[] tokens, out bool named)) { - context.Exit($"[ui_button] Invalid arguments: {rest}", false); return; } - if (!windows.TryGetValue(winId, out Window? win)) + if (!TryGetInt(tokens, named, "x", 0, out int x) || + !TryGetInt(tokens, named, "y", 1, out int y) || + !TryGetInt(tokens, named, "w", 2, out int w) || + !TryGetInt(tokens, named, "h", 3, out int h) || + !TryGetString(tokens, named, "label", 4, out string label)) { - context.Exit($"[ui_button] No window with id {winId}", false); + context.Exit("[ui_button] Usage: ui_button winId x y w h \"Label\" [r g b]", false); return; } - Button button = win.CreateUIElement(b => + Color color = ParseColor(tokens, named, 5); + + Button button = win.CreateUIElement