Replaced C style array manipulation

This commit is contained in:
Bobby Lucero 2025-07-12 00:12:50 -04:00
parent e194ec7a3f
commit cdc54751a3
3 changed files with 111 additions and 239 deletions

View File

@ -149,37 +149,37 @@ namespace MicroUI
[System.Flags] [System.Flags]
public enum Options public enum Options
{ {
AlignCenter = 1 << 0, // 1 AlignCenter = 1 << 0,
AlignRight = 1 << 1, // 2 AlignRight = 1 << 1,
NoInteract = 1 << 2, // 4 NoInteract = 1 << 2,
NoFrame = 1 << 3, // 8 NoFrame = 1 << 3,
NoResize = 1 << 4, // 16 NoResize = 1 << 4,
NoScroll = 1 << 5, // 32 NoScroll = 1 << 5,
NoClose = 1 << 6, // 64 NoClose = 1 << 6,
NoTitle = 1 << 7, // 128 NoTitle = 1 << 7,
HoldFocus = 1 << 8, // 256 HoldFocus = 1 << 8,
AutoSize = 1 << 9, // 512 AutoSize = 1 << 9,
Popup = 1 << 10, // 1024 Popup = 1 << 10,
Closed = 1 << 11, // 2048 Closed = 1 << 11,
Expanded = 1 << 12 // 4096 Expanded = 1 << 12
} }
[System.Flags] [System.Flags]
public enum MouseButton public enum MouseButton
{ {
Left = 1 << 0, // 1 Left = 1 << 0,
Right = 1 << 1, // 2 Right = 1 << 1,
Middle = 1 << 2 // 4 Middle = 1 << 2
} }
[System.Flags] [System.Flags]
public enum KeyModifiers public enum KeyModifiers
{ {
Shift = 1 << 0, // 1 Shift = 1 << 0,
Ctrl = 1 << 1, // 2 Ctrl = 1 << 1,
Alt = 1 << 2, // 4 Alt = 1 << 2,
Backspace = 1 << 3, // 8 Backspace = 1 << 3,
Return = 1 << 4 // 16 Return = 1 << 4
} }
public struct MuVec2 public struct MuVec2
@ -364,7 +364,7 @@ namespace MicroUI
public class MuStyle 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 MuVec2 Size { get; set; }
public int Padding { get; set; } public int Padding { get; set; }
public int Spacing { get; set; } public int Spacing { get; set; }
@ -386,16 +386,13 @@ namespace MicroUI
public class MuContext public class MuContext
{ {
// Callbacks
public TextWidthDelegate? TextWidth; public TextWidthDelegate? TextWidth;
public TextHeightDelegate? TextHeight; public TextHeightDelegate? TextHeight;
public DrawFrameDelegate? DrawFrame; public DrawFrameDelegate? DrawFrame;
// Styles
public MuStyle Style { get; set; } = new MuStyle(); public MuStyle Style { get; set; } = new MuStyle();
// IDs and last widget info public mu_Id Hover { get; set; }
public mu_Id Hover { get; set; } // assuming mu_Id is uint
public mu_Id Focus { get; set; } public mu_Id Focus { get; set; }
public mu_Id LastId { get; set; } public mu_Id LastId { get; set; }
public MuRect LastRect { get; set; } public MuRect LastRect { get; set; }
@ -403,16 +400,13 @@ namespace MicroUI
public int UpdatedFocus { get; set; } public int UpdatedFocus { get; set; }
public int Frame { get; set; } public int Frame { get; set; }
// Containers
public MuContainer? HoverRoot { get; set; } public MuContainer? HoverRoot { get; set; }
public MuContainer? NextHoverRoot { get; set; } public MuContainer? NextHoverRoot { get; set; }
public MuContainer? ScrollTarget { 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; } public mu_Id NumberEdit { get; set; }
public string numberEditText = "";
// Stacks
public FixedStack<MuCommand> CommandList { get; } = new FixedStack<MuCommand>(Constants.CommandListSize); public FixedStack<MuCommand> CommandList { get; } = new FixedStack<MuCommand>(Constants.CommandListSize);
public FixedStack<MuContainer> RootList { get; } = new FixedStack<MuContainer>(Constants.RootListSize); public FixedStack<MuContainer> RootList { get; } = new FixedStack<MuContainer>(Constants.RootListSize);
public FixedStack<MuContainer> ContainerStack { get; } = new FixedStack<MuContainer>(Constants.ContainerStackSize); public FixedStack<MuContainer> ContainerStack { get; } = new FixedStack<MuContainer>(Constants.ContainerStackSize);
@ -420,12 +414,10 @@ namespace MicroUI
public FixedStack<uint> IdStack { get; } = new FixedStack<uint>(Constants.IdStackSize); public FixedStack<uint> IdStack { get; } = new FixedStack<uint>(Constants.IdStackSize);
public FixedStack<MuLayout> LayoutStack { get; } = new FixedStack<MuLayout>(Constants.LayoutStackSize); public FixedStack<MuLayout> LayoutStack { get; } = new FixedStack<MuLayout>(Constants.LayoutStackSize);
// Retained State Pools
public MuPoolItem[] ContainerPool { get; } = new MuPoolItem[Constants.ContainerPoolSize]; public MuPoolItem[] ContainerPool { get; } = new MuPoolItem[Constants.ContainerPoolSize];
public MuContainer[] Containers { get; } = new MuContainer[Constants.ContainerPoolSize]; public MuContainer[] Containers { get; } = new MuContainer[Constants.ContainerPoolSize];
public MuPoolItem[] TreeNodePool { get; } = new MuPoolItem[Constants.TreeNodePoolSize]; public MuPoolItem[] TreeNodePool { get; } = new MuPoolItem[Constants.TreeNodePoolSize];
// Input State
public MuVec2 MousePos { get; set; } public MuVec2 MousePos { get; set; }
public MuVec2 LastMousePos { get; set; } public MuVec2 LastMousePos { get; set; }
public MuVec2 MouseDelta { get; set; } public MuVec2 MouseDelta { get; set; }
@ -434,7 +426,7 @@ namespace MicroUI
public int MousePressed { get; set; } public int MousePressed { get; set; }
public int KeyDown { get; set; } public int KeyDown { get; set; }
public int KeyPressed { get; set; } public int KeyPressed { get; set; }
public char[] inputText = new char[32]; public string inputText = "";
} }
public static class MuAssert public static class MuAssert
@ -473,20 +465,20 @@ namespace MicroUI
ThumbSize = 8, ThumbSize = 8,
Colors = new MuColor[] Colors = new MuColor[]
{ {
new MuColor(230, 230, 230, 255), // MU_COLOR_TEXT new MuColor(230, 230, 230, 255),
new MuColor(25, 25, 25, 255), // MU_COLOR_BORDER new MuColor(25, 25, 25, 255),
new MuColor(50, 50, 50, 255), // MU_COLOR_WINDOWBG new MuColor(50, 50, 50, 255),
new MuColor(25, 25, 25, 255), // MU_COLOR_TITLEBG new MuColor(25, 25, 25, 255),
new MuColor(240, 240, 240, 255), // MU_COLOR_TITLETEXT new MuColor(240, 240, 240, 255),
new MuColor(0, 0, 0, 0), // MU_COLOR_PANELBG new MuColor(0, 0, 0, 0),
new MuColor(75, 75, 75, 255), // MU_COLOR_BUTTON new MuColor(75, 75, 75, 255),
new MuColor(95, 95, 95, 255), // MU_COLOR_BUTTONHOVER new MuColor(95, 95, 95, 255),
new MuColor(115, 115, 115, 255), // MU_COLOR_BUTTONFOCUS new MuColor(115, 115, 115, 255),
new MuColor(30, 30, 30, 255), // MU_COLOR_BASE new MuColor(30, 30, 30, 255),
new MuColor(35, 35, 35, 255), // MU_COLOR_BASEHOVER new MuColor(35, 35, 35, 255),
new MuColor(40, 40, 40, 255), // MU_COLOR_BASEFOCUS new MuColor(40, 40, 40, 255),
new MuColor(43, 43, 43, 255), // MU_COLOR_SCROLLBASE new MuColor(43, 43, 43, 255),
new MuColor(30, 30, 30, 255) // MU_COLOR_SCROLLTHUMB new MuColor(30, 30, 30, 255)
} }
}; };
@ -521,13 +513,10 @@ namespace MicroUI
public static void DrawFrame(MuContext ctx, MuRect rect, ColorType colorId) 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); 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]); MuCommandList.DrawRect(ctx, rect, ctx.Style.Colors[colorIndex]);
// Early return for certain color IDs (skip border)
if (colorId == ColorType.ScrollBase || if (colorId == ColorType.ScrollBase ||
colorId == ColorType.ScrollThumb || colorId == ColorType.ScrollThumb ||
colorId == ColorType.TitleBg) colorId == ColorType.TitleBg)
@ -535,7 +524,6 @@ namespace MicroUI
return; return;
} }
// Draw border if alpha > 0
if (ctx.Style.Colors[(int)ColorType.Border].A > 0) if (ctx.Style.Colors[(int)ColorType.Border].A > 0)
{ {
MuCommandList.DrawBox(ctx, ExpandRect(rect, 1), ctx.Style.Colors[(int)ColorType.Border]); MuCommandList.DrawBox(ctx, ExpandRect(rect, 1), ctx.Style.Colors[(int)ColorType.Border]);
@ -544,21 +532,16 @@ namespace MicroUI
public static void Init(MuContext ctx) 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; ctx.DrawFrame = DrawFrame;
// Copy the default style (make sure it's cloned, not shared!)
ctx.Style = DefaultStyle; ctx.Style = DefaultStyle;
// Initialize containers array with actual MuContainer objects
for (int i = 0; i < ctx.Containers.Length; i++) for (int i = 0; i < ctx.Containers.Length; i++)
{ {
ctx.Containers[i] = new MuContainer(); ctx.Containers[i] = new MuContainer();
} }
// Initialize container pool items
for (int i = 0; i < ctx.ContainerPool.Length; i++) for (int i = 0; i < ctx.ContainerPool.Length; i++)
{ {
ctx.ContainerPool[i] = new MuPoolItem(0, 0); ctx.ContainerPool[i] = new MuPoolItem(0, 0);
@ -616,7 +599,7 @@ namespace MicroUI
} }
ctx.KeyPressed = 0; ctx.KeyPressed = 0;
ctx.inputText[0] = '\0'; ctx.inputText = "";
ctx.MousePressed = 0; ctx.MousePressed = 0;
ctx.ScrollDelta = new MuVec2(0, 0); ctx.ScrollDelta = new MuVec2(0, 0);
ctx.LastMousePos = ctx.MousePos; ctx.LastMousePos = ctx.MousePos;
@ -636,13 +619,11 @@ namespace MicroUI
else else
{ {
var prev = ctx.RootList.Items[i - 1]; 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; ((MuJumpCommand)ctx.CommandList.Items[prev.TailIndex]).DestinationIndex = cnt.HeadIndex + 1;
} }
if (i == n - 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; ((MuJumpCommand)ctx.CommandList.Items[cnt.TailIndex]).DestinationIndex = ctx.CommandList.Index;
} }
} }
@ -707,42 +688,35 @@ namespace MicroUI
{ {
MuRect cr = GetClipRect(ctx); MuRect cr = GetClipRect(ctx);
// Completely outside clip rect
if (r.X > cr.X + cr.W || r.X + r.W < cr.X || 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) 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 && 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) r.Y >= cr.Y && r.Y + r.H <= cr.Y + cr.H)
{ {
return 0; // no clipping return 0;
} }
// Partially clipped return ClipMode.Part;
return ClipMode.Part; // MU_CLIP_PART
} }
public static void PushLayout(MuContext ctx, MuRect body, MuVec2 scroll) public static void PushLayout(MuContext ctx, MuRect body, MuVec2 scroll)
{ {
MuLayout layout = new MuLayout(); MuLayout layout = new MuLayout();
// Adjust body position by subtracting scroll offset
layout.Body = new MuRect( layout.Body = new MuRect(
body.X - scroll.X, body.X - scroll.X,
body.Y - scroll.Y, body.Y - scroll.Y,
body.W, body.W,
body.H); body.H);
// Initialize max to very negative values (like C's -0x1000000)
layout.Max = new MuVec2(-0x1000000, -0x1000000); layout.Max = new MuVec2(-0x1000000, -0x1000000);
// Push the layout struct onto the layout stack
ctx.LayoutStack.Push(layout); ctx.LayoutStack.Push(layout);
// Call mu_layout_row with count=1, widths pointer to width var, height=0
int[] widths = { 0 }; int[] widths = { 0 };
MuLayoutUtil.LayoutRow(ctx, 1, widths, 0); MuLayoutUtil.LayoutRow(ctx, 1, widths, 0);
} }
@ -765,7 +739,6 @@ namespace MicroUI
layout.Max.Y - layout.Body.Y layout.Max.Y - layout.Body.Y
); );
// Pop container, layout, and id
ctx.ContainerStack.Pop(); ctx.ContainerStack.Pop();
ctx.LayoutStack.Pop(); ctx.LayoutStack.Pop();
PopId(ctx); PopId(ctx);
@ -786,7 +759,6 @@ namespace MicroUI
public static MuContainer? GetContainer(MuContext ctx, uint id, int opt) 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); int idx = MuPool.Get(ctx, ctx.ContainerPool, id);
if (idx >= 0) if (idx >= 0)
{ {
@ -797,17 +769,14 @@ namespace MicroUI
return ctx.Containers[idx]; 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) if ((opt & (int)Options.Closed) != 0)
{ {
return null; return null;
} }
// Container not found in pool: init new container
idx = MuPool.Init(ctx, ctx.ContainerPool, id); idx = MuPool.Init(ctx, ctx.ContainerPool, id);
var cnt = ctx.Containers[idx]; var cnt = ctx.Containers[idx];
// Reset all fields (equivalent to memset(cnt, 0, sizeof(*cnt)))
cnt.HeadIndex = -1; cnt.HeadIndex = -1;
cnt.TailIndex = -1; cnt.TailIndex = -1;
cnt.Rect = new MuRect(0, 0, 0, 0); cnt.Rect = new MuRect(0, 0, 0, 0);
@ -845,7 +814,6 @@ namespace MicroUI
{ {
int n = -1; int n = -1;
int f = ctx.Frame; int f = ctx.Frame;
// Find the item with the oldest last_update
for (int i = 0; i < items.Length; i++) for (int i = 0; i < items.Length; i++)
{ {
if (items[i].LastUpdate < f) if (items[i].LastUpdate < f)
@ -904,21 +872,7 @@ namespace MicroUI
public static void InputText(MuContext ctx, string text) public static void InputText(MuContext ctx, string text)
{ {
// Find the current length of text in the buffer (up to first null terminator) ctx.inputText += text;
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';
} }
} }
@ -939,16 +893,14 @@ namespace MicroUI
var cmd = commands[index]; var cmd = commands[index];
if (cmd is MuJumpCommand jump) if (cmd is MuJumpCommand jump)
{ {
// Jump to the destination index
if (jump.DestinationIndex < 0 || jump.DestinationIndex >= count) if (jump.DestinationIndex < 0 || jump.DestinationIndex >= count)
return null; // Invalid jump, end iteration return null;
index = jump.DestinationIndex; index = jump.DestinationIndex;
continue; continue;
} }
// Return the current command and advance index
return commands[index++]; return commands[index++];
} }
return null; // End of command list return null;
} }
public static MuJumpCommand PushJump(MuContext ctx, int destinationIndex = -1) 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) public static void DrawRect(MuContext ctx, MuRect rect, MuColor color)
{ {
// Intersect with current clip rect
rect = MicroUI.IntersectRects(rect, MicroUI.GetClipRect(ctx)); rect = MicroUI.IntersectRects(rect, MicroUI.GetClipRect(ctx));
if (rect.W > 0 && rect.H > 0) if (rect.W > 0 && rect.H > 0)
{ {
@ -992,10 +943,8 @@ namespace MicroUI
if (clipped == ClipMode.All) return; if (clipped == ClipMode.All) return;
if (clipped == ClipMode.Part) SetClip(ctx, MicroUI.GetClipRect(ctx)); if (clipped == ClipMode.Part) SetClip(ctx, MicroUI.GetClipRect(ctx));
// Add command
MuCommandList.Push(ctx, new MuTextCommand(font, pos, color, str)); MuCommandList.Push(ctx, new MuTextCommand(font, pos, color, str));
// Reset clipping if it was set
if (clipped != 0) SetClip(ctx, MicroUI.UnclippedRect); 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 enum LayoutNextType
{ {
Relative = 1, Relative = 1,
@ -1028,10 +976,8 @@ namespace MicroUI
public static void LayoutEndColumn(MuContext ctx) public static void LayoutEndColumn(MuContext ctx)
{ {
// Get the child layout (top of stack)
ref var b = ref MicroUI.GetLayout(ctx); ref var b = ref MicroUI.GetLayout(ctx);
ctx.LayoutStack.Pop(); ctx.LayoutStack.Pop();
// Get the parent layout (now top of stack)
ref var a = ref MicroUI.GetLayout(ctx); ref var a = ref MicroUI.GetLayout(ctx);
a.Position = new MuVec2( a.Position = new MuVec2(
@ -1056,7 +1002,7 @@ namespace MicroUI
} }
layout.Items = items; layout.Items = items;
layout.Position = new MuVec2(layout.Indent, layout.NextRow); 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; layout.ItemIndex = 0;
} }
@ -1098,17 +1044,14 @@ namespace MicroUI
} }
else else
{ {
// handle next row
if (layout.ItemIndex == layout.Items) if (layout.ItemIndex == layout.Items)
{ {
LayoutRow(ctx, layout.Items, null, layout.Size.Y); LayoutRow(ctx, layout.Items, null, layout.Size.Y);
} }
// position
res.X = layout.Position.X; res.X = layout.Position.X;
res.Y = layout.Position.Y; res.Y = layout.Position.Y;
// size
res.W = layout.Items > 0 ? layout.Widths[layout.ItemIndex] : layout.Size.X; res.W = layout.Items > 0 ? layout.Widths[layout.ItemIndex] : layout.Size.X;
res.H = layout.Size.Y; res.H = layout.Size.Y;
if (res.W == 0) res.W = style.Size.X + style.Padding * 2; if (res.W == 0) res.W = style.Size.X + style.Padding * 2;
@ -1119,18 +1062,15 @@ namespace MicroUI
layout.ItemIndex++; layout.ItemIndex++;
} }
// update position
layout.Position = new MuVec2( layout.Position = new MuVec2(
layout.Position.X + res.W + style.Spacing, layout.Position.X + res.W + style.Spacing,
layout.Position.Y layout.Position.Y
); );
layout.NextRow = MathUtil.Max(layout.NextRow, res.Y + res.H + style.Spacing); layout.NextRow = MathUtil.Max(layout.NextRow, res.Y + res.H + style.Spacing);
// apply body offset
res.X += layout.Body.X; res.X += layout.Body.X;
res.Y += layout.Body.Y; res.Y += layout.Body.Y;
// update max position
layout.Max = new MuVec2( layout.Max = new MuVec2(
MathUtil.Max(layout.Max.X, res.X + res.W), MathUtil.Max(layout.Max.X, res.X + res.W),
MathUtil.Max(layout.Max.Y, res.Y + res.H) 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) 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) 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); MicroUI.PushLayout(ctx, MicroUI.ExpandRect(body, -ctx.Style.Padding), cnt.Scroll);
// Store the body rect in the container
cnt.Body = body; cnt.Body = body;
} }
public static void BeginRootContainer(MuContext ctx, MuContainer cnt) public static void BeginRootContainer(MuContext ctx, MuContainer cnt)
{ {
// Push container onto container stack
ctx.ContainerStack.Push(cnt); ctx.ContainerStack.Push(cnt);
// Push container to roots list and push head command
ctx.RootList.Push(cnt); ctx.RootList.Push(cnt);
cnt.HeadIndex = ctx.CommandList.Index; // Store the current command index as head cnt.HeadIndex = ctx.CommandList.Index;
MuCommandList.PushJump(ctx); // Push a jump command (will be set up later) 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) && if (MicroUI.RectOverlapsVec2(cnt.Rect, ctx.MousePos) &&
(ctx.NextHoverRoot == null || cnt.ZIndex > ctx.NextHoverRoot.ZIndex)) (ctx.NextHoverRoot == null || cnt.ZIndex > ctx.NextHoverRoot.ZIndex))
{ {
ctx.NextHoverRoot = cnt; 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); MicroUI.PushClipRect(ctx, MicroUI.UnclippedRect);
} }
public static void EndRootContainer(MuContext ctx) 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); MuContainer cnt = MicroUI.GetCurrentContainer(ctx);
cnt.TailIndex = ctx.CommandList.Index; // Store the current command index as tail cnt.TailIndex = ctx.CommandList.Index;
MuCommandList.PushJump(ctx); // Push a jump command (will be set up later) 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) if (cnt.HeadIndex >= 0 && cnt.HeadIndex < ctx.CommandList.Index)
{ {
var headJump = ctx.CommandList.Items[cnt.HeadIndex] as MuJumpCommand; 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.PopClipRect(ctx);
MicroUI.PopContainer(ctx); MicroUI.PopContainer(ctx);
} }
public static bool BeginWindowEx(MuContext ctx, string title, MuRect rect, int opt) 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)); uint id = MicroUI.GetId(ctx, System.Text.Encoding.UTF8.GetBytes(title));
MuContainer? cnt = MicroUI.GetContainer(ctx, id, opt); MuContainer? cnt = MicroUI.GetContainer(ctx, id, opt);
if (cnt == null || !cnt.Open) return false; if (cnt == null || !cnt.Open) return false;
// 2. Push ID
MicroUI.PushId(ctx, System.Text.Encoding.UTF8.GetBytes(title)); MicroUI.PushId(ctx, System.Text.Encoding.UTF8.GetBytes(title));
// 3. Set Initial Rect
if (cnt.Rect.W == 0) cnt.Rect = rect; if (cnt.Rect.W == 0) cnt.Rect = rect;
// 4. Begin Root Container
MuControl.BeginRootContainer(ctx, cnt); MuControl.BeginRootContainer(ctx, cnt);
rect = cnt.Rect; rect = cnt.Rect;
var body = rect; var body = rect;
// 5. Draw Window Frame
if ((opt & (int)Options.NoFrame) == 0) if ((opt & (int)Options.NoFrame) == 0)
{ {
ctx.DrawFrame?.Invoke(ctx, rect, ColorType.WindowBg); ctx.DrawFrame?.Invoke(ctx, rect, ColorType.WindowBg);
} }
// 6. Draw Title Bar
if ((opt & (int)Options.NoTitle) == 0) if ((opt & (int)Options.NoTitle) == 0)
{ {
var tr = rect; var tr = rect;
tr.H = ctx.Style.TitleHeight; tr.H = ctx.Style.TitleHeight;
ctx.DrawFrame?.Invoke(ctx, tr, ColorType.TitleBg); ctx.DrawFrame?.Invoke(ctx, tr, ColorType.TitleBg);
// Title text and drag
uint titleId = MicroUI.GetId(ctx, System.Text.Encoding.UTF8.GetBytes("!title")); uint titleId = MicroUI.GetId(ctx, System.Text.Encoding.UTF8.GetBytes("!title"));
MuControl.UpdateControl(ctx, titleId, tr, opt); MuControl.UpdateControl(ctx, titleId, tr, opt);
MuControl.DrawControlText(ctx, title, tr, ColorType.TitleText, 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) if (titleId == ctx.Focus && (ctx.MouseDown & (int)MouseButton.Left) != 0)
{ {
cnt.Rect = new MuRect( cnt.Rect = new MuRect(
@ -1252,11 +1168,9 @@ namespace MicroUI
); );
} }
// Adjust body for title bar
body.Y += tr.H; body.Y += tr.H;
body.H -= tr.H; body.H -= tr.H;
// Close button
if ((opt & (int)Options.NoClose) == 0) if ((opt & (int)Options.NoClose) == 0)
{ {
uint closeId = MicroUI.GetId(ctx, System.Text.Encoding.UTF8.GetBytes("!close")); 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);
MuControl.PushContainerBody(ctx, cnt, body, opt); // Implement or comment out
// 8. Resize handle
if ((opt & (int)Options.NoResize) == 0) if ((opt & (int)Options.NoResize) == 0)
{ {
int sz = ctx.Style.TitleHeight; int sz = ctx.Style.TitleHeight;
uint resizeId = MicroUI.GetId(ctx, System.Text.Encoding.UTF8.GetBytes("!resize")); 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); var r = new MuRect(rect.X + rect.W - sz, rect.Y + rect.H - sz, sz, sz);
MuControl.UpdateControl(ctx, resizeId, r, opt); MuControl.UpdateControl(ctx, resizeId, r, opt);
// Chevron/arrow design for resize handle
int pad = 4; int pad = 4;
var baseColor = ctx.Style.Colors[(int)ColorType.Base]; var baseColor = ctx.Style.Colors[(int)ColorType.Base];
// Horizontal bar (bottom edge)
MuCommandList.Push(ctx, new MuRectCommand( MuCommandList.Push(ctx, new MuRectCommand(
new MuRect(r.X + r.W - pad - 8, r.Y + r.H - pad - 2, 8, 2), baseColor)); 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( MuCommandList.Push(ctx, new MuRectCommand(
new MuRect(r.X + r.W - pad - 2, r.Y + r.H - pad - 8, 2, 8), baseColor)); 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) 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) if ((opt & (int)Options.AutoSize) != 0)
{ {
var r = MicroUI.GetLayout(ctx).Body; 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) if ((opt & (int)Options.Popup) != 0 && ctx.MousePressed != 0 && ctx.HoverRoot != cnt)
{ {
cnt.Open = false; cnt.Open = false;
} }
// 11. Push Clip Rect
MicroUI.PushClipRect(ctx, cnt.Body); MicroUI.PushClipRect(ctx, cnt.Body);
// 12. Return Active
return true; return true;
} }
@ -1333,21 +1238,18 @@ namespace MicroUI
MuControl.EndRootContainer(ctx); MuControl.EndRootContainer(ctx);
} }
// Helper functions for controls
public static void DrawControlFrame(MuContext ctx, uint id, MuRect rect, ColorType colorId, int opt) public static void DrawControlFrame(MuContext ctx, uint id, MuRect rect, ColorType colorId, int opt)
{ {
if ((opt & (int)Options.NoFrame) != 0) { return; } if ((opt & (int)Options.NoFrame) != 0) { return; }
// Adjust color based on state: normal, hover, focus
if (ctx.Focus == id) if (ctx.Focus == id)
{ {
colorId += 2; // Focus state colorId += 2;
} }
else if (ctx.Hover == id) else if (ctx.Hover == id)
{ {
colorId += 1; // Hover state colorId += 1;
} }
// else: normal state (colorId unchanged)
ctx.DrawFrame?.Invoke(ctx, rect, colorId); ctx.DrawFrame?.Invoke(ctx, rect, colorId);
} }
@ -1391,7 +1293,6 @@ namespace MicroUI
private static bool InHoverRoot(MuContext ctx) private static bool InHoverRoot(MuContext ctx)
{ {
// Check if we're in the hover root container
return ctx.HoverRoot == null || ctx.HoverRoot == MicroUI.GetCurrentContainer(ctx); return ctx.HoverRoot == null || ctx.HoverRoot == MicroUI.GetCurrentContainer(ctx);
} }
@ -1439,13 +1340,11 @@ namespace MicroUI
MuRect r = MuLayoutUtil.LayoutNext(ctx); MuRect r = MuLayoutUtil.LayoutNext(ctx);
UpdateControl(ctx, id, r, opt); UpdateControl(ctx, id, r, opt);
// Handle click
if ((ctx.MousePressed & (int)MouseButton.Left) != 0 && ctx.Focus == id) if ((ctx.MousePressed & (int)MouseButton.Left) != 0 && ctx.Focus == id)
{ {
res |= ResultFlags.Submit; res |= ResultFlags.Submit;
} }
// Draw
DrawControlFrame(ctx, id, r, ColorType.Button, opt); DrawControlFrame(ctx, id, r, ColorType.Button, opt);
if (!string.IsNullOrEmpty(label)) if (!string.IsNullOrEmpty(label))
{ {
@ -1464,7 +1363,6 @@ namespace MicroUI
ResultFlags res = 0; ResultFlags res = 0;
uint id; uint id;
// Create ID that includes both label and state
if (!string.IsNullOrEmpty(label)) if (!string.IsNullOrEmpty(label))
{ {
var idData = System.Text.Encoding.UTF8.GetBytes(label + state.ToString()); var idData = System.Text.Encoding.UTF8.GetBytes(label + state.ToString());
@ -1478,14 +1376,12 @@ namespace MicroUI
MuRect r = MuLayoutUtil.LayoutNext(ctx); MuRect r = MuLayoutUtil.LayoutNext(ctx);
UpdateControl(ctx, id, r, 0); UpdateControl(ctx, id, r, 0);
// Handle click
if ((ctx.MousePressed & (int)MouseButton.Left) != 0 && ctx.Focus == id) if ((ctx.MousePressed & (int)MouseButton.Left) != 0 && ctx.Focus == id)
{ {
state = !state; state = !state;
res |= ResultFlags.Change; res |= ResultFlags.Change;
} }
// Draw checkbox box
var box = new MuRect(r.X, r.Y, r.H, r.H); var box = new MuRect(r.X, r.Y, r.H, r.H);
DrawControlFrame(ctx, id, box, ColorType.Base, 0); 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]); MuCommandList.DrawIcon(ctx, (int)IconType.Check, box, ctx.Style.Colors[(int)ColorType.Text]);
} }
// Draw label
if (!string.IsNullOrEmpty(label)) if (!string.IsNullOrEmpty(label))
{ {
var labelRect = new MuRect(r.X + box.W, r.Y, r.W - box.W, r.H); 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); 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) 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 && if ((ctx.MousePressed & (int)MouseButton.Left) != 0 &&
(ctx.KeyDown & (int)KeyModifiers.Shift) != 0 && (ctx.KeyDown & (int)KeyModifiers.Shift) != 0 &&
ctx.Hover == id) ctx.Hover == id)
{ {
ctx.NumberEdit = id; ctx.NumberEdit = id;
// Convert number to string for editing using the same format as C version ctx.numberEditText = value.ToString("G3");
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];
}
} }
if (ctx.NumberEdit == id) if (ctx.NumberEdit == id)
{ {
// For now, we'll implement a simplified textbox var result = TextboxRaw(ctx, ref ctx.numberEditText, id, r, 0);
// In a full implementation, you'd use TextboxRaw here
var result = TextboxRaw(ctx, ctx.inputText, ctx.inputText.Length, id, r, 0);
if ((result & ResultFlags.Submit) != 0 || ctx.Focus != id) if ((result & ResultFlags.Submit) != 0 || ctx.Focus != id)
{ {
// Parse the number back if (float.TryParse(ctx.numberEditText, out float newValue))
if (float.TryParse(new string(ctx.inputText).Trim('\0'), out float newValue))
{ {
value = newValue; value = newValue;
} }
@ -1580,51 +1465,31 @@ namespace MicroUI
} }
else else
{ {
return true; // Still in text input mode return true;
} }
} }
return false; 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; ResultFlags res = 0;
UpdateControl(ctx, id, r, opt | (int)Options.HoldFocus); UpdateControl(ctx, id, r, opt | (int)Options.HoldFocus);
if (ctx.Focus == id) if (ctx.Focus == id)
{ {
// Handle text input if (ctx.inputText.Length > 0)
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)
{ {
for (int i = 0; i < n; i++) value += ctx.inputText;
{
if (len + i < bufSize)
buf[len + i] = ctx.inputText[i];
}
len += n;
if (len < bufSize) buf[len] = '\0';
res |= ResultFlags.Change; res |= ResultFlags.Change;
} }
// Handle backspace if ((ctx.KeyPressed & (int)KeyModifiers.Backspace) != 0 && value.Length > 0)
if ((ctx.KeyPressed & (int)KeyModifiers.Backspace) != 0 && len > 0)
{ {
// Skip UTF-8 continuation bytes (simplified for C#) value = value.Length > 0 ? value.Substring(0, value.Length - 1) : "";
while (len > 0 && (buf[len - 1] & 0xC0) == 0x80) len--;
if (len > 0) len--;
buf[len] = '\0';
res |= ResultFlags.Change; res |= ResultFlags.Change;
} }
// Handle return
if ((ctx.KeyPressed & (int)KeyModifiers.Return) != 0) if ((ctx.KeyPressed & (int)KeyModifiers.Return) != 0)
{ {
MicroUI.SetFocus(ctx, 0); MicroUI.SetFocus(ctx, 0);
@ -1632,37 +1497,30 @@ namespace MicroUI
} }
} }
// Draw
DrawControlFrame(ctx, id, r, ColorType.Base, opt); DrawControlFrame(ctx, id, r, ColorType.Base, opt);
string displayText = new string(buf).Trim('\0'); DrawControlText(ctx, value, r, ColorType.Text, opt);
DrawControlText(ctx, displayText, r, ColorType.Text, opt);
return new MuResult(res); 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) 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; MuRect thumb;
int x, w; int x, w;
ResultFlags res = 0; ResultFlags res = 0;
float last = value, v = last; 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)); uint id = MicroUI.GetId(ctx, System.Text.Encoding.UTF8.GetBytes(name));
MuRect baseRect = MuLayoutUtil.LayoutNext(ctx); MuRect baseRect = MuLayoutUtil.LayoutNext(ctx);
// Handle text input mode
if (NumberTextbox(ctx, ref v, baseRect, id)) if (NumberTextbox(ctx, ref v, baseRect, id))
{ {
return new MuResult(res); return new MuResult(res);
} }
// Handle normal mode
UpdateControl(ctx, id, baseRect, opt); UpdateControl(ctx, id, baseRect, opt);
// Handle input - EXACTLY match C version logic
if (ctx.Focus == id && (ctx.MouseDown | ctx.MousePressed) == (int)MouseButton.Left) if (ctx.Focus == id && (ctx.MouseDown | ctx.MousePressed) == (int)MouseButton.Left)
{ {
v = low + (ctx.MousePos.X - baseRect.X) * (high - low) / baseRect.W; 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); value = v = MathUtil.Clamp(v, low, high);
if (last != v) { res |= ResultFlags.Change; } if (last != v) { res |= ResultFlags.Change; }
// Draw base
DrawControlFrame(ctx, id, baseRect, ColorType.Base, opt); DrawControlFrame(ctx, id, baseRect, ColorType.Base, opt);
// Draw thumb
w = ctx.Style.ThumbSize; w = ctx.Style.ThumbSize;
x = (int)((v - low) * (baseRect.W - w) / (high - low)); x = (int)((v - low) * (baseRect.W - w) / (high - low));
thumb = new MuRect(baseRect.X + x, baseRect.Y, w, baseRect.H); thumb = new MuRect(baseRect.X + x, baseRect.Y, w, baseRect.H);
DrawControlFrame(ctx, id, thumb, ColorType.Button, opt); DrawControlFrame(ctx, id, thumb, ColorType.Button, opt);
// Draw text
string text = v.ToString(format); string text = v.ToString(format);
DrawControlText(ctx, text, baseRect, ColorType.Text, opt); DrawControlText(ctx, text, baseRect, ColorType.Text, opt);
return new MuResult(res); 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) 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; ResultFlags res = 0;
// Generate ID from the name, just like BeginWindowEx does
uint id = MicroUI.GetId(ctx, System.Text.Encoding.UTF8.GetBytes(name)); uint id = MicroUI.GetId(ctx, System.Text.Encoding.UTF8.GetBytes(name));
MuRect baseRect = MuLayoutUtil.LayoutNext(ctx); MuRect baseRect = MuLayoutUtil.LayoutNext(ctx);
float last = value; float last = value;
// Handle text input mode
if (NumberTextbox(ctx, ref value, baseRect, id)) if (NumberTextbox(ctx, ref value, baseRect, id))
{ {
return new MuResult(res); return new MuResult(res);
} }
// Handle normal mode
UpdateControl(ctx, id, baseRect, opt); UpdateControl(ctx, id, baseRect, opt);
// Handle input - EXACTLY match C version logic
if (ctx.Focus == id && ctx.MouseDown == (int)MouseButton.Left) if (ctx.Focus == id && ctx.MouseDown == (int)MouseButton.Left)
{ {
value += ctx.MouseDelta.X * step; value += ctx.MouseDelta.X * step;
} }
// Set flag if value changed
if (value != last) { res |= ResultFlags.Change; } if (value != last) { res |= ResultFlags.Change; }
// Draw base
DrawControlFrame(ctx, id, baseRect, ColorType.Base, opt); DrawControlFrame(ctx, id, baseRect, ColorType.Base, opt);
// Draw text
string text = value.ToString(format); string text = value.ToString(format);
DrawControlText(ctx, text, baseRect, ColorType.Text, opt); DrawControlText(ctx, text, baseRect, ColorType.Text, opt);

View File

@ -8,9 +8,10 @@ class Program
{ {
// Key state tracking for key up events // Key state tracking for key up events
private static HashSet<int> pressedKeys = new HashSet<int>(); private static HashSet<int> pressedKeys = new HashSet<int>();
// Persistent textbox buffer // Persistent textbox value
private static char[] textboxBuf = new char[32]; private static string textboxValue = "test";
private static bool textboxBufInitialized = false; // Second number value for testing multiple number controls
private static float secondValue = 25.0f;
// Track focus changes // Track focus changes
private static uint lastFocus = 0; 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 // Handle key up events for modifier keys
var keysToRemove = new List<int>(); var keysToRemove = new List<int>();
foreach (int pressedKey in pressedKeys) foreach (int pressedKey in pressedKeys)
@ -254,26 +265,36 @@ class Program
{ {
MuLayoutUtil.LayoutRow(ctx, 1, new int[] { 120 }, 0); MuLayoutUtil.LayoutRow(ctx, 1, new int[] { 120 }, 0);
float test = 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); MuControl.SliderEx(ctx, "test_slider", ref test, 0.0f, 100.0f, 1.0f, "F1", 0);
if ((sliderResult & ResultFlags.Change) != 0) // if ((sliderResult & ResultFlags.Change) != 0)
{ // {
Console.WriteLine($"Slider value changed to: {sliderValue}"); // Console.WriteLine($"Slider value changed to: {sliderValue}");
} // }
MuControl.Text(ctx, "Other controls here..."); MuControl.Text(ctx, "Other controls here...");
// Test TextboxRaw // 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 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) if ((textboxResult & ResultFlags.Change) != 0)
{ {
string value = new string(textboxBuf).TrimEnd('\0'); Console.WriteLine($"Textbox changed: {textboxValue}");
Console.WriteLine($"Textbox changed: {value}"); }
// 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}");
} }
} }

View File

@ -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)) { if (mu_textbox_raw(ctx, textbox_buf, sizeof(textbox_buf), 12345, textbox_rect, 0)) {
printf("Textbox changed: %s\n", textbox_buf); 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 --- // --- Right column ---