diff --git a/MicroUI.cs/MicroUI.cs b/MicroUI.cs/MicroUI.cs index 47bb042..b6592ba 100644 --- a/MicroUI.cs/MicroUI.cs +++ b/MicroUI.cs/MicroUI.cs @@ -149,37 +149,37 @@ namespace MicroUI [System.Flags] public enum Options { - AlignCenter = 1 << 0, // 1 - AlignRight = 1 << 1, // 2 - NoInteract = 1 << 2, // 4 - NoFrame = 1 << 3, // 8 - NoResize = 1 << 4, // 16 - NoScroll = 1 << 5, // 32 - NoClose = 1 << 6, // 64 - NoTitle = 1 << 7, // 128 - HoldFocus = 1 << 8, // 256 - AutoSize = 1 << 9, // 512 - Popup = 1 << 10, // 1024 - Closed = 1 << 11, // 2048 - Expanded = 1 << 12 // 4096 + AlignCenter = 1 << 0, + AlignRight = 1 << 1, + NoInteract = 1 << 2, + NoFrame = 1 << 3, + NoResize = 1 << 4, + NoScroll = 1 << 5, + NoClose = 1 << 6, + NoTitle = 1 << 7, + HoldFocus = 1 << 8, + AutoSize = 1 << 9, + Popup = 1 << 10, + Closed = 1 << 11, + Expanded = 1 << 12 } [System.Flags] public enum MouseButton { - Left = 1 << 0, // 1 - Right = 1 << 1, // 2 - Middle = 1 << 2 // 4 + Left = 1 << 0, + Right = 1 << 1, + Middle = 1 << 2 } [System.Flags] public enum KeyModifiers { - Shift = 1 << 0, // 1 - Ctrl = 1 << 1, // 2 - Alt = 1 << 2, // 4 - Backspace = 1 << 3, // 8 - Return = 1 << 4 // 16 + Shift = 1 << 0, + Ctrl = 1 << 1, + Alt = 1 << 2, + Backspace = 1 << 3, + Return = 1 << 4 } public struct MuVec2 @@ -364,7 +364,7 @@ namespace MicroUI public class MuStyle { - public object? Font { get; set; } // Replace 'object' with actual font type as needed + public object? Font { get; set; } public MuVec2 Size { get; set; } public int Padding { get; set; } public int Spacing { get; set; } @@ -386,16 +386,13 @@ namespace MicroUI public class MuContext { - // Callbacks - public TextWidthDelegate? TextWidth; + public TextWidthDelegate? TextWidth; public TextHeightDelegate? TextHeight; public DrawFrameDelegate? DrawFrame; - // Styles public MuStyle Style { get; set; } = new MuStyle(); - // IDs and last widget info - public mu_Id Hover { get; set; } // assuming mu_Id is uint + public mu_Id Hover { get; set; } public mu_Id Focus { get; set; } public mu_Id LastId { get; set; } public MuRect LastRect { get; set; } @@ -403,16 +400,13 @@ namespace MicroUI public int UpdatedFocus { get; set; } public int Frame { get; set; } - // Containers public MuContainer? HoverRoot { get; set; } public MuContainer? NextHoverRoot { get; set; } public MuContainer? ScrollTarget { get; set; } - // Number edit buffer - private char[] numberEditBuf = new char[Constants.MaxFormatLength]; // e.g. 127 or 128 public mu_Id NumberEdit { get; set; } - - // Stacks + public string numberEditText = ""; + public FixedStack CommandList { get; } = new FixedStack(Constants.CommandListSize); public FixedStack RootList { get; } = new FixedStack(Constants.RootListSize); public FixedStack ContainerStack { get; } = new FixedStack(Constants.ContainerStackSize); @@ -420,12 +414,10 @@ namespace MicroUI public FixedStack IdStack { get; } = new FixedStack(Constants.IdStackSize); public FixedStack LayoutStack { get; } = new FixedStack(Constants.LayoutStackSize); - // Retained State Pools public MuPoolItem[] ContainerPool { get; } = new MuPoolItem[Constants.ContainerPoolSize]; public MuContainer[] Containers { get; } = new MuContainer[Constants.ContainerPoolSize]; public MuPoolItem[] TreeNodePool { get; } = new MuPoolItem[Constants.TreeNodePoolSize]; - - // Input State + public MuVec2 MousePos { get; set; } public MuVec2 LastMousePos { get; set; } public MuVec2 MouseDelta { get; set; } @@ -434,7 +426,7 @@ namespace MicroUI public int MousePressed { get; set; } public int KeyDown { get; set; } public int KeyPressed { get; set; } - public char[] inputText = new char[32]; + public string inputText = ""; } public static class MuAssert @@ -473,20 +465,20 @@ namespace MicroUI ThumbSize = 8, Colors = new MuColor[] { - new MuColor(230, 230, 230, 255), // MU_COLOR_TEXT - new MuColor(25, 25, 25, 255), // MU_COLOR_BORDER - new MuColor(50, 50, 50, 255), // MU_COLOR_WINDOWBG - new MuColor(25, 25, 25, 255), // MU_COLOR_TITLEBG - new MuColor(240, 240, 240, 255), // MU_COLOR_TITLETEXT - new MuColor(0, 0, 0, 0), // MU_COLOR_PANELBG - new MuColor(75, 75, 75, 255), // MU_COLOR_BUTTON - new MuColor(95, 95, 95, 255), // MU_COLOR_BUTTONHOVER - new MuColor(115, 115, 115, 255), // MU_COLOR_BUTTONFOCUS - new MuColor(30, 30, 30, 255), // MU_COLOR_BASE - new MuColor(35, 35, 35, 255), // MU_COLOR_BASEHOVER - new MuColor(40, 40, 40, 255), // MU_COLOR_BASEFOCUS - new MuColor(43, 43, 43, 255), // MU_COLOR_SCROLLBASE - new MuColor(30, 30, 30, 255) // MU_COLOR_SCROLLTHUMB + new MuColor(230, 230, 230, 255), + new MuColor(25, 25, 25, 255), + new MuColor(50, 50, 50, 255), + new MuColor(25, 25, 25, 255), + new MuColor(240, 240, 240, 255), + new MuColor(0, 0, 0, 0), + new MuColor(75, 75, 75, 255), + new MuColor(95, 95, 95, 255), + new MuColor(115, 115, 115, 255), + new MuColor(30, 30, 30, 255), + new MuColor(35, 35, 35, 255), + new MuColor(40, 40, 40, 255), + new MuColor(43, 43, 43, 255), + new MuColor(30, 30, 30, 255) } }; @@ -521,13 +513,10 @@ namespace MicroUI public static void DrawFrame(MuContext ctx, MuRect rect, ColorType colorId) { - // Clamp color ID to prevent index out of range int colorIndex = MathUtil.Clamp((int)colorId, 0, (int)ColorType.Max - 1); - // Draw filled rectangle with given color MuCommandList.DrawRect(ctx, rect, ctx.Style.Colors[colorIndex]); - // Early return for certain color IDs (skip border) if (colorId == ColorType.ScrollBase || colorId == ColorType.ScrollThumb || colorId == ColorType.TitleBg) @@ -535,7 +524,6 @@ namespace MicroUI return; } - // Draw border if alpha > 0 if (ctx.Style.Colors[(int)ColorType.Border].A > 0) { MuCommandList.DrawBox(ctx, ExpandRect(rect, 1), ctx.Style.Colors[(int)ColorType.Border]); @@ -544,21 +532,16 @@ namespace MicroUI public static void Init(MuContext ctx) { - // Zeroing is not needed — C# classes are automatically zeroed/null-initialized. - // Assign the draw function delegate ctx.DrawFrame = DrawFrame; - // Copy the default style (make sure it's cloned, not shared!) ctx.Style = DefaultStyle; - // Initialize containers array with actual MuContainer objects for (int i = 0; i < ctx.Containers.Length; i++) { ctx.Containers[i] = new MuContainer(); } - // Initialize container pool items for (int i = 0; i < ctx.ContainerPool.Length; i++) { ctx.ContainerPool[i] = new MuPoolItem(0, 0); @@ -616,7 +599,7 @@ namespace MicroUI } ctx.KeyPressed = 0; - ctx.inputText[0] = '\0'; + ctx.inputText = ""; ctx.MousePressed = 0; ctx.ScrollDelta = new MuVec2(0, 0); ctx.LastMousePos = ctx.MousePos; @@ -636,13 +619,11 @@ namespace MicroUI else { var prev = ctx.RootList.Items[i - 1]; - // Set previous container's tail jump destination to current container's head + 1 ((MuJumpCommand)ctx.CommandList.Items[prev.TailIndex]).DestinationIndex = cnt.HeadIndex + 1; } if (i == n - 1) { - // Set last container's tail jump to the end of the command list ((MuJumpCommand)ctx.CommandList.Items[cnt.TailIndex]).DestinationIndex = ctx.CommandList.Index; } } @@ -707,42 +688,35 @@ namespace MicroUI { MuRect cr = GetClipRect(ctx); - // Completely outside clip rect if (r.X > cr.X + cr.W || r.X + r.W < cr.X || r.Y > cr.Y + cr.H || r.Y + r.H < cr.Y) { - return ClipMode.All; // MU_CLIP_ALL + return ClipMode.All; } - // Completely inside clip rect if (r.X >= cr.X && r.X + r.W <= cr.X + cr.W && r.Y >= cr.Y && r.Y + r.H <= cr.Y + cr.H) { - return 0; // no clipping + return 0; } - // Partially clipped - return ClipMode.Part; // MU_CLIP_PART + return ClipMode.Part; } public static void PushLayout(MuContext ctx, MuRect body, MuVec2 scroll) { MuLayout layout = new MuLayout(); - // Adjust body position by subtracting scroll offset layout.Body = new MuRect( body.X - scroll.X, body.Y - scroll.Y, body.W, body.H); - // Initialize max to very negative values (like C's -0x1000000) layout.Max = new MuVec2(-0x1000000, -0x1000000); - // Push the layout struct onto the layout stack ctx.LayoutStack.Push(layout); - // Call mu_layout_row with count=1, widths pointer to width var, height=0 int[] widths = { 0 }; MuLayoutUtil.LayoutRow(ctx, 1, widths, 0); } @@ -765,7 +739,6 @@ namespace MicroUI layout.Max.Y - layout.Body.Y ); - // Pop container, layout, and id ctx.ContainerStack.Pop(); ctx.LayoutStack.Pop(); PopId(ctx); @@ -786,7 +759,6 @@ namespace MicroUI public static MuContainer? GetContainer(MuContext ctx, uint id, int opt) { - // Try to get existing container from pool int idx = MuPool.Get(ctx, ctx.ContainerPool, id); if (idx >= 0) { @@ -797,17 +769,14 @@ namespace MicroUI return ctx.Containers[idx]; } - // If we're looking for a closed container and it doesn't exist, return null if ((opt & (int)Options.Closed) != 0) { return null; } - // Container not found in pool: init new container idx = MuPool.Init(ctx, ctx.ContainerPool, id); var cnt = ctx.Containers[idx]; - // Reset all fields (equivalent to memset(cnt, 0, sizeof(*cnt))) cnt.HeadIndex = -1; cnt.TailIndex = -1; cnt.Rect = new MuRect(0, 0, 0, 0); @@ -845,7 +814,6 @@ namespace MicroUI { int n = -1; int f = ctx.Frame; - // Find the item with the oldest last_update for (int i = 0; i < items.Length; i++) { if (items[i].LastUpdate < f) @@ -904,21 +872,7 @@ namespace MicroUI public static void InputText(MuContext ctx, string text) { - // Find the current length of text in the buffer (up to first null terminator) - int len = 0; - while (len < ctx.inputText.Length && ctx.inputText[len] != '\0') - len++; - - int size = text.Length; - if (len + size + 1 > ctx.inputText.Length) // +1 for null terminator - throw new Exception("Input text buffer overflow"); - - // Copy text into the buffer at the current position - for (int i = 0; i < size && (len + i) < ctx.inputText.Length - 1; i++) - ctx.inputText[len + i] = text[i]; - - // Null-terminate - ctx.inputText[len + size] = '\0'; + ctx.inputText += text; } } @@ -939,16 +893,14 @@ namespace MicroUI var cmd = commands[index]; if (cmd is MuJumpCommand jump) { - // Jump to the destination index if (jump.DestinationIndex < 0 || jump.DestinationIndex >= count) - return null; // Invalid jump, end iteration + return null; index = jump.DestinationIndex; continue; } - // Return the current command and advance index return commands[index++]; } - return null; // End of command list + return null; } public static MuJumpCommand PushJump(MuContext ctx, int destinationIndex = -1) @@ -965,7 +917,6 @@ namespace MicroUI public static void DrawRect(MuContext ctx, MuRect rect, MuColor color) { - // Intersect with current clip rect rect = MicroUI.IntersectRects(rect, MicroUI.GetClipRect(ctx)); if (rect.W > 0 && rect.H > 0) { @@ -992,10 +943,8 @@ namespace MicroUI if (clipped == ClipMode.All) return; if (clipped == ClipMode.Part) SetClip(ctx, MicroUI.GetClipRect(ctx)); - // Add command MuCommandList.Push(ctx, new MuTextCommand(font, pos, color, str)); - // Reset clipping if it was set if (clipped != 0) SetClip(ctx, MicroUI.UnclippedRect); } @@ -1011,7 +960,6 @@ namespace MicroUI } } - // Layout next type enum (matches C enum { RELATIVE = 1, ABSOLUTE = 2 }) enum LayoutNextType { Relative = 1, @@ -1028,10 +976,8 @@ namespace MicroUI public static void LayoutEndColumn(MuContext ctx) { - // Get the child layout (top of stack) ref var b = ref MicroUI.GetLayout(ctx); ctx.LayoutStack.Pop(); - // Get the parent layout (now top of stack) ref var a = ref MicroUI.GetLayout(ctx); a.Position = new MuVec2( @@ -1056,7 +1002,7 @@ namespace MicroUI } layout.Items = items; layout.Position = new MuVec2(layout.Indent, layout.NextRow); - layout.Size = new MuVec2(layout.Size.X, height); // Only Y is updated + layout.Size = new MuVec2(layout.Size.X, height); layout.ItemIndex = 0; } @@ -1098,17 +1044,14 @@ namespace MicroUI } else { - // handle next row if (layout.ItemIndex == layout.Items) { LayoutRow(ctx, layout.Items, null, layout.Size.Y); } - // position res.X = layout.Position.X; res.Y = layout.Position.Y; - // size res.W = layout.Items > 0 ? layout.Widths[layout.ItemIndex] : layout.Size.X; res.H = layout.Size.Y; if (res.W == 0) res.W = style.Size.X + style.Padding * 2; @@ -1119,18 +1062,15 @@ namespace MicroUI layout.ItemIndex++; } - // update position layout.Position = new MuVec2( layout.Position.X + res.W + style.Spacing, layout.Position.Y ); layout.NextRow = MathUtil.Max(layout.NextRow, res.Y + res.H + style.Spacing); - // apply body offset res.X += layout.Body.X; res.Y += layout.Body.Y; - // update max position layout.Max = new MuVec2( MathUtil.Max(layout.Max.X, res.X + res.W), MathUtil.Max(layout.Max.Y, res.Y + res.H) @@ -1146,52 +1086,37 @@ namespace MicroUI public static void PushContainerBody(MuContext ctx, MuContainer cnt, MuRect body, int opt) { - // If scrolling is enabled, handle scrollbars (stub for now) if ((opt & (int)Options.NoScroll) == 0) { - // TODO: Implement scrollbars(ctx, cnt, ref body); } - // Push layout for the container body, with padding and scroll offset MicroUI.PushLayout(ctx, MicroUI.ExpandRect(body, -ctx.Style.Padding), cnt.Scroll); - // Store the body rect in the container cnt.Body = body; } public static void BeginRootContainer(MuContext ctx, MuContainer cnt) { - // Push container onto container stack ctx.ContainerStack.Push(cnt); - // Push container to roots list and push head command ctx.RootList.Push(cnt); - cnt.HeadIndex = ctx.CommandList.Index; // Store the current command index as head - MuCommandList.PushJump(ctx); // Push a jump command (will be set up later) + cnt.HeadIndex = ctx.CommandList.Index; + MuCommandList.PushJump(ctx); - // Set as hover root if the mouse is overlapping this container and it has a - // higher zindex than the current hover root if (MicroUI.RectOverlapsVec2(cnt.Rect, ctx.MousePos) && (ctx.NextHoverRoot == null || cnt.ZIndex > ctx.NextHoverRoot.ZIndex)) { ctx.NextHoverRoot = cnt; } - // Clipping is reset here in case a root-container is made within - // another root-container's begin/end block; this prevents the inner - // root-container being clipped to the outer MicroUI.PushClipRect(ctx, MicroUI.UnclippedRect); } public static void EndRootContainer(MuContext ctx) { - // Push tail 'goto' jump command and set head 'skip' command. The final steps - // on setting up these are done in MicroUI.End() MuContainer cnt = MicroUI.GetCurrentContainer(ctx); - cnt.TailIndex = ctx.CommandList.Index; // Store the current command index as tail - MuCommandList.PushJump(ctx); // Push a jump command (will be set up later) + cnt.TailIndex = ctx.CommandList.Index; + MuCommandList.PushJump(ctx); - // Set the head jump command to point to the current command index - // (This will be finalized in MicroUI.End()) if (cnt.HeadIndex >= 0 && cnt.HeadIndex < ctx.CommandList.Index) { var headJump = ctx.CommandList.Items[cnt.HeadIndex] as MuJumpCommand; @@ -1201,47 +1126,38 @@ namespace MicroUI } } - // Pop base clip rect and container MicroUI.PopClipRect(ctx); MicroUI.PopContainer(ctx); } public static bool BeginWindowEx(MuContext ctx, string title, MuRect rect, int opt) { - // 1. Get/Init Container uint id = MicroUI.GetId(ctx, System.Text.Encoding.UTF8.GetBytes(title)); MuContainer? cnt = MicroUI.GetContainer(ctx, id, opt); if (cnt == null || !cnt.Open) return false; - // 2. Push ID MicroUI.PushId(ctx, System.Text.Encoding.UTF8.GetBytes(title)); - // 3. Set Initial Rect if (cnt.Rect.W == 0) cnt.Rect = rect; - // 4. Begin Root Container MuControl.BeginRootContainer(ctx, cnt); rect = cnt.Rect; var body = rect; - // 5. Draw Window Frame if ((opt & (int)Options.NoFrame) == 0) { ctx.DrawFrame?.Invoke(ctx, rect, ColorType.WindowBg); } - // 6. Draw Title Bar if ((opt & (int)Options.NoTitle) == 0) { var tr = rect; tr.H = ctx.Style.TitleHeight; ctx.DrawFrame?.Invoke(ctx, tr, ColorType.TitleBg); - // Title text and drag uint titleId = MicroUI.GetId(ctx, System.Text.Encoding.UTF8.GetBytes("!title")); MuControl.UpdateControl(ctx, titleId, tr, opt); MuControl.DrawControlText(ctx, title, tr, ColorType.TitleText, opt); - // Move window if dragging title bar if (titleId == ctx.Focus && (ctx.MouseDown & (int)MouseButton.Left) != 0) { cnt.Rect = new MuRect( @@ -1252,11 +1168,9 @@ namespace MicroUI ); } - // Adjust body for title bar body.Y += tr.H; body.H -= tr.H; - // Close button if ((opt & (int)Options.NoClose) == 0) { uint closeId = MicroUI.GetId(ctx, System.Text.Encoding.UTF8.GetBytes("!close")); @@ -1271,24 +1185,19 @@ namespace MicroUI } } - // 7. Push Container Body Layout - MuControl.PushContainerBody(ctx, cnt, body, opt); // Implement or comment out + MuControl.PushContainerBody(ctx, cnt, body, opt); - // 8. Resize handle if ((opt & (int)Options.NoResize) == 0) { int sz = ctx.Style.TitleHeight; uint resizeId = MicroUI.GetId(ctx, System.Text.Encoding.UTF8.GetBytes("!resize")); var r = new MuRect(rect.X + rect.W - sz, rect.Y + rect.H - sz, sz, sz); MuControl.UpdateControl(ctx, resizeId, r, opt); - // Chevron/arrow design for resize handle int pad = 4; var baseColor = ctx.Style.Colors[(int)ColorType.Base]; - // Horizontal bar (bottom edge) MuCommandList.Push(ctx, new MuRectCommand( new MuRect(r.X + r.W - pad - 8, r.Y + r.H - pad - 2, 8, 2), baseColor)); - // Vertical bar (right edge) MuCommandList.Push(ctx, new MuRectCommand( new MuRect(r.X + r.W - pad - 2, r.Y + r.H - pad - 8, 2, 8), baseColor)); if (resizeId == ctx.Focus && (ctx.MouseDown & (int)MouseButton.Left) != 0) @@ -1302,7 +1211,6 @@ namespace MicroUI } } - // 9. Auto-size if ((opt & (int)Options.AutoSize) != 0) { var r = MicroUI.GetLayout(ctx).Body; @@ -1314,16 +1222,13 @@ namespace MicroUI ); } - // 10. Popup close if ((opt & (int)Options.Popup) != 0 && ctx.MousePressed != 0 && ctx.HoverRoot != cnt) { cnt.Open = false; } - // 11. Push Clip Rect MicroUI.PushClipRect(ctx, cnt.Body); - // 12. Return Active return true; } @@ -1333,21 +1238,18 @@ namespace MicroUI MuControl.EndRootContainer(ctx); } - // Helper functions for controls public static void DrawControlFrame(MuContext ctx, uint id, MuRect rect, ColorType colorId, int opt) { if ((opt & (int)Options.NoFrame) != 0) { return; } - // Adjust color based on state: normal, hover, focus if (ctx.Focus == id) { - colorId += 2; // Focus state + colorId += 2; } else if (ctx.Hover == id) { - colorId += 1; // Hover state + colorId += 1; } - // else: normal state (colorId unchanged) ctx.DrawFrame?.Invoke(ctx, rect, colorId); } @@ -1391,7 +1293,6 @@ namespace MicroUI private static bool InHoverRoot(MuContext ctx) { - // Check if we're in the hover root container return ctx.HoverRoot == null || ctx.HoverRoot == MicroUI.GetCurrentContainer(ctx); } @@ -1439,13 +1340,11 @@ namespace MicroUI MuRect r = MuLayoutUtil.LayoutNext(ctx); UpdateControl(ctx, id, r, opt); - // Handle click if ((ctx.MousePressed & (int)MouseButton.Left) != 0 && ctx.Focus == id) { res |= ResultFlags.Submit; } - // Draw DrawControlFrame(ctx, id, r, ColorType.Button, opt); if (!string.IsNullOrEmpty(label)) { @@ -1464,7 +1363,6 @@ namespace MicroUI ResultFlags res = 0; uint id; - // Create ID that includes both label and state if (!string.IsNullOrEmpty(label)) { var idData = System.Text.Encoding.UTF8.GetBytes(label + state.ToString()); @@ -1478,14 +1376,12 @@ namespace MicroUI MuRect r = MuLayoutUtil.LayoutNext(ctx); UpdateControl(ctx, id, r, 0); - // Handle click if ((ctx.MousePressed & (int)MouseButton.Left) != 0 && ctx.Focus == id) { state = !state; res |= ResultFlags.Change; } - // Draw checkbox box var box = new MuRect(r.X, r.Y, r.H, r.H); DrawControlFrame(ctx, id, box, ColorType.Base, 0); @@ -1494,7 +1390,6 @@ namespace MicroUI MuCommandList.DrawIcon(ctx, (int)IconType.Check, box, ctx.Style.Colors[(int)ColorType.Text]); } - // Draw label if (!string.IsNullOrEmpty(label)) { var labelRect = new MuRect(r.X + box.W, r.Y, r.W - box.W, r.H); @@ -1546,33 +1441,23 @@ namespace MicroUI DrawControlText(ctx, text, r, ColorType.Text, 0); } - // Helper method for number textbox functionality private static bool NumberTextbox(MuContext ctx, ref float value, MuRect r, uint id) { - // Check if we should enter text input mode (Shift+Click) if ((ctx.MousePressed & (int)MouseButton.Left) != 0 && (ctx.KeyDown & (int)KeyModifiers.Shift) != 0 && ctx.Hover == id) { ctx.NumberEdit = id; - // Convert number to string for editing using the same format as C version - var numberStr = value.ToString("G3"); // MU_REAL_FMT is "%.3g" - // Copy to input buffer (simplified - in real implementation you'd need a proper buffer) - for (int i = 0; i < Math.Min(numberStr.Length, ctx.inputText.Length); i++) - { - ctx.inputText[i] = numberStr[i]; - } + ctx.numberEditText = value.ToString("G3"); } if (ctx.NumberEdit == id) { - // For now, we'll implement a simplified textbox - // In a full implementation, you'd use TextboxRaw here - var result = TextboxRaw(ctx, ctx.inputText, ctx.inputText.Length, id, r, 0); + var result = TextboxRaw(ctx, ref ctx.numberEditText, id, r, 0); + if ((result & ResultFlags.Submit) != 0 || ctx.Focus != id) { - // Parse the number back - if (float.TryParse(new string(ctx.inputText).Trim('\0'), out float newValue)) + if (float.TryParse(ctx.numberEditText, out float newValue)) { value = newValue; } @@ -1580,51 +1465,31 @@ namespace MicroUI } else { - return true; // Still in text input mode + return true; } } return false; } - public static MuResult TextboxRaw(MuContext ctx, char[] buf, int bufSize, uint id, MuRect r, int opt) + public static MuResult TextboxRaw(MuContext ctx, ref string value, uint id, MuRect r, int opt) { ResultFlags res = 0; UpdateControl(ctx, id, r, opt | (int)Options.HoldFocus); if (ctx.Focus == id) { - // Handle text input - int len = 0; - while (len < bufSize && buf[len] != '\0') len++; - - // Get actual length of text in input buffer (up to first null terminator) - int inputLen = 0; - while (inputLen < ctx.inputText.Length && ctx.inputText[inputLen] != '\0') inputLen++; - - int n = Math.Min(bufSize - len - 1, inputLen); - if (n > 0) + if (ctx.inputText.Length > 0) { - for (int i = 0; i < n; i++) - { - if (len + i < bufSize) - buf[len + i] = ctx.inputText[i]; - } - len += n; - if (len < bufSize) buf[len] = '\0'; + value += ctx.inputText; res |= ResultFlags.Change; } - // Handle backspace - if ((ctx.KeyPressed & (int)KeyModifiers.Backspace) != 0 && len > 0) + if ((ctx.KeyPressed & (int)KeyModifiers.Backspace) != 0 && value.Length > 0) { - // Skip UTF-8 continuation bytes (simplified for C#) - while (len > 0 && (buf[len - 1] & 0xC0) == 0x80) len--; - if (len > 0) len--; - buf[len] = '\0'; + value = value.Length > 0 ? value.Substring(0, value.Length - 1) : ""; res |= ResultFlags.Change; } - // Handle return if ((ctx.KeyPressed & (int)KeyModifiers.Return) != 0) { MicroUI.SetFocus(ctx, 0); @@ -1632,37 +1497,30 @@ namespace MicroUI } } - // Draw DrawControlFrame(ctx, id, r, ColorType.Base, opt); - string displayText = new string(buf).Trim('\0'); - DrawControlText(ctx, displayText, r, ColorType.Text, opt); + DrawControlText(ctx, value, r, ColorType.Text, opt); return new MuResult(res); } public static MuResult SliderEx(MuContext ctx, string name, ref float value, float low, float high, float step, string format, int opt) { - char[] buf = new char[Constants.MaxFormatLength + 1]; MuRect thumb; int x, w; ResultFlags res = 0; float last = value, v = last; - // Generate ID from the name, just like BeginWindowEx does uint id = MicroUI.GetId(ctx, System.Text.Encoding.UTF8.GetBytes(name)); MuRect baseRect = MuLayoutUtil.LayoutNext(ctx); - // Handle text input mode if (NumberTextbox(ctx, ref v, baseRect, id)) { return new MuResult(res); } - // Handle normal mode UpdateControl(ctx, id, baseRect, opt); - // Handle input - EXACTLY match C version logic if (ctx.Focus == id && (ctx.MouseDown | ctx.MousePressed) == (int)MouseButton.Left) { v = low + (ctx.MousePos.X - baseRect.X) * (high - low) / baseRect.W; @@ -1672,61 +1530,48 @@ namespace MicroUI } } - // Clamp and store value, update res value = v = MathUtil.Clamp(v, low, high); if (last != v) { res |= ResultFlags.Change; } - // Draw base DrawControlFrame(ctx, id, baseRect, ColorType.Base, opt); - // Draw thumb w = ctx.Style.ThumbSize; x = (int)((v - low) * (baseRect.W - w) / (high - low)); thumb = new MuRect(baseRect.X + x, baseRect.Y, w, baseRect.H); DrawControlFrame(ctx, id, thumb, ColorType.Button, opt); - // Draw text string text = v.ToString(format); DrawControlText(ctx, text, baseRect, ColorType.Text, opt); return new MuResult(res); } - // Remove the caller information overload since we're using explicit names now public static MuResult NumberEx(MuContext ctx, string name, ref float value, float step, string format, int opt) { - char[] buf = new char[Constants.MaxFormatLength + 1]; ResultFlags res = 0; - // Generate ID from the name, just like BeginWindowEx does uint id = MicroUI.GetId(ctx, System.Text.Encoding.UTF8.GetBytes(name)); MuRect baseRect = MuLayoutUtil.LayoutNext(ctx); float last = value; - // Handle text input mode if (NumberTextbox(ctx, ref value, baseRect, id)) { return new MuResult(res); } - // Handle normal mode UpdateControl(ctx, id, baseRect, opt); - // Handle input - EXACTLY match C version logic if (ctx.Focus == id && ctx.MouseDown == (int)MouseButton.Left) { value += ctx.MouseDelta.X * step; } - // Set flag if value changed if (value != last) { res |= ResultFlags.Change; } - // Draw base DrawControlFrame(ctx, id, baseRect, ColorType.Base, opt); - // Draw text string text = value.ToString(format); DrawControlText(ctx, text, baseRect, ColorType.Text, opt); diff --git a/MicroUI.cs/Program.cs b/MicroUI.cs/Program.cs index 2de702e..6501e01 100644 --- a/MicroUI.cs/Program.cs +++ b/MicroUI.cs/Program.cs @@ -8,9 +8,10 @@ class Program { // Key state tracking for key up events private static HashSet pressedKeys = new HashSet(); - // Persistent textbox buffer - private static char[] textboxBuf = new char[32]; - private static bool textboxBufInitialized = false; + // Persistent textbox value + private static string textboxValue = "test"; + // Second number value for testing multiple number controls + private static float secondValue = 25.0f; // Track focus changes private static uint lastFocus = 0; @@ -89,6 +90,16 @@ class Program } } + // Handle Shift key state (for Shift+Click functionality) + if (Raylib.IsKeyDown(KeyboardKey.LeftShift) || Raylib.IsKeyDown(KeyboardKey.RightShift)) + { + ctx.KeyDown |= (int)KeyModifiers.Shift; + } + else + { + ctx.KeyDown &= ~(int)KeyModifiers.Shift; + } + // Handle key up events for modifier keys var keysToRemove = new List(); foreach (int pressedKey in pressedKeys) @@ -254,26 +265,36 @@ class Program { MuLayoutUtil.LayoutRow(ctx, 1, new int[] { 120 }, 0); float test = 0; - var sliderResult = MuControl.SliderEx(ctx, "main_slider", ref sliderValue, 0.0f, 100.0f, 1.0f, "F1", 0); + //var sliderResult = MuControl.SliderEx(ctx, "main_slider", ref sliderValue, 0.0f, 100.0f, 1.0f, "F1", 0); MuControl.SliderEx(ctx, "test_slider", ref test, 0.0f, 100.0f, 1.0f, "F1", 0); - if ((sliderResult & ResultFlags.Change) != 0) - { - Console.WriteLine($"Slider value changed to: {sliderValue}"); - } + // if ((sliderResult & ResultFlags.Change) != 0) + // { + // Console.WriteLine($"Slider value changed to: {sliderValue}"); + // } MuControl.Text(ctx, "Other controls here..."); // Test TextboxRaw - if (!textboxBufInitialized) { - string initial = "Edit me!"; - for (int i = 0; i < initial.Length && i < textboxBuf.Length; i++) textboxBuf[i] = initial[i]; - textboxBufInitialized = true; - } var textboxRect = MuLayoutUtil.LayoutNext(ctx); - var textboxResult = MuControl.TextboxRaw(ctx, textboxBuf, textboxBuf.Length, 12345, textboxRect, 0); + var textboxResult = MuControl.TextboxRaw(ctx, ref textboxValue, 12345, textboxRect, 0); if ((textboxResult & ResultFlags.Change) != 0) { - string value = new string(textboxBuf).TrimEnd('\0'); - Console.WriteLine($"Textbox changed: {value}"); + Console.WriteLine($"Textbox changed: {textboxValue}"); + } + + // Test NumberEx (number textbox) + MuControl.Text(ctx, "Number control (Shift+Click to edit):"); + var numberResult = MuControl.NumberEx(ctx, "test_number", ref sliderValue, 1.0f, "F2", 0); + if ((numberResult & ResultFlags.Change) != 0) + { + Console.WriteLine($"Number changed to: {sliderValue}"); + } + + // Test second NumberEx (number textbox) + MuControl.Text(ctx, "Second number control (Shift+Click to edit):"); + var secondNumberResult = MuControl.NumberEx(ctx, "test_number2", ref secondValue, 0.5f, "F2", 0); + if ((secondNumberResult & ResultFlags.Change) != 0) + { + Console.WriteLine($"Second number changed to: {secondValue}"); } } diff --git a/MicroUICVersion/main.c b/MicroUICVersion/main.c index 2e7b6ff..3777211 100644 --- a/MicroUICVersion/main.c +++ b/MicroUICVersion/main.c @@ -358,6 +358,12 @@ void window(mu_Context* ctx, float* value) { if (mu_textbox_raw(ctx, textbox_buf, sizeof(textbox_buf), 12345, textbox_rect, 0)) { printf("Textbox changed: %s\n", textbox_buf); } + + // Test NumberEx (number textbox) + mu_layout_row(ctx, 1, (int[]){120}, 0); + if (mu_number_ex(ctx, value, 1.0f, "%.1f", 0) & MU_RES_SUBMIT) { + printf("Number changed to: %.1f\n", *value); + } } // --- Right column ---