From d0ff5d292945c9fa1fd3b5a30223e83a99e83fd7 Mon Sep 17 00:00:00 2001 From: Bobby Lucero Date: Mon, 7 Jul 2025 21:04:03 -0400 Subject: [PATCH] More control components --- MicroUI.cs/MicroUI.cs | 209 ++++++++++++++++++++++++++++++++++++++++-- MicroUI.cs/Program.cs | 155 +++++++++++++++++++++++++++---- 2 files changed, 337 insertions(+), 27 deletions(-) diff --git a/MicroUI.cs/MicroUI.cs b/MicroUI.cs/MicroUI.cs index e8751a2..ac5f425 100644 --- a/MicroUI.cs/MicroUI.cs +++ b/MicroUI.cs/MicroUI.cs @@ -124,9 +124,26 @@ namespace MicroUI [Flags] public enum ResultFlags { - Active = 1 << 0, // 1 - Submit = 1 << 1, // 2 - Change = 1 << 2 // 4 + Active = 1 << 0, + Submit = 1 << 1, + Change = 1 << 2 + } + + public struct MuResult + { + public ResultFlags Flags; + + public MuResult(ResultFlags flags) + { + Flags = flags; + } + + public static implicit operator bool(MuResult result) => result.Flags != 0; + public static implicit operator ResultFlags(MuResult result) => result.Flags; + public static implicit operator MuResult(ResultFlags flags) => new MuResult(flags); + + public static MuResult operator |(MuResult a, ResultFlags b) => new MuResult(a.Flags | b); + public static MuResult operator |(ResultFlags a, MuResult b) => new MuResult(a | b.Flags); } [System.Flags] @@ -504,8 +521,11 @@ 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[(int)colorId]); + MuCommandList.DrawRect(ctx, rect, ctx.Style.Colors[colorIndex]); // Early return for certain color IDs (skip border) if (colorId == ColorType.ScrollBase || @@ -1213,8 +1233,8 @@ namespace MicroUI // Title text and drag uint titleId = MicroUI.GetId(ctx, System.Text.Encoding.UTF8.GetBytes("!title")); - //MuControl.UpdateControl(ctx, titleId, tr, opt); // Implement or comment out - //MuControl.DrawControlText(ctx, title, tr, ColorType.TitleText, opt); // Implement or comment out + 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) @@ -1237,8 +1257,8 @@ namespace MicroUI uint closeId = MicroUI.GetId(ctx, System.Text.Encoding.UTF8.GetBytes("!close")); var r = new MuRect(tr.X + tr.W - tr.H, tr.Y, tr.H, tr.H); tr.W -= r.W; - //MuControl.DrawIcon(ctx, (int)IconType.Close, r, ctx.Style.Colors[(int)ColorType.TitleText]); // Implement or comment out - //MuControl.UpdateControl(ctx, closeId, r, opt); // Implement or comment out + MuCommandList.DrawIcon(ctx, (int)IconType.Close, r, ctx.Style.Colors[(int)ColorType.TitleText]); + MuControl.UpdateControl(ctx, closeId, r, opt); if ((ctx.MousePressed & (int)MouseButton.Left) != 0 && closeId == ctx.Focus) { cnt.Open = false; @@ -1255,7 +1275,7 @@ namespace MicroUI 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); // Implement or comment out + MuControl.UpdateControl(ctx, resizeId, r, opt); if (resizeId == ctx.Focus && (ctx.MouseDown & (int)MouseButton.Left) != 0) { cnt.Rect = new MuRect( @@ -1297,6 +1317,177 @@ namespace MicroUI MicroUI.PopClipRect(ctx); 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 + } + else if (ctx.Hover == id) + { + colorId += 1; // Hover state + } + // else: normal state (colorId unchanged) + + ctx.DrawFrame?.Invoke(ctx, rect, colorId); + } + + public static void DrawControlText(MuContext ctx, string str, MuRect rect, ColorType colorId, int opt) + { + if (ctx.TextWidth == null || ctx.TextHeight == null) return; + + MuVec2 pos; + object? font = ctx.Style.Font; + int tw = ctx.TextWidth(font, str, -1); + int th = ctx.TextHeight(font); + + MicroUI.PushClipRect(ctx, rect); + + pos.Y = rect.Y + (rect.H - th) / 2; + + if ((opt & (int)Options.AlignCenter) != 0) + { + pos.X = rect.X + (rect.W - tw) / 2; + } + else if ((opt & (int)Options.AlignRight) != 0) + { + pos.X = rect.X + rect.W - tw - ctx.Style.Padding; + } + else + { + pos.X = rect.X + ctx.Style.Padding; + } + + MuCommandList.DrawText(ctx, font, str, pos, ctx.Style.Colors[(int)colorId]); + MicroUI.PopClipRect(ctx); + } + + public static bool MouseOver(MuContext ctx, MuRect rect) + { + return MicroUI.RectOverlapsVec2(rect, ctx.MousePos) && + MicroUI.RectOverlapsVec2(MicroUI.GetClipRect(ctx), ctx.MousePos) && + InHoverRoot(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); + } + + public static void UpdateControl(MuContext ctx, uint id, MuRect rect, int opt) + { + bool mouseover = MouseOver(ctx, rect); + + if (ctx.Focus == id) { ctx.UpdatedFocus = 1; } + if ((opt & (int)Options.NoInteract) != 0) { return; } + if (mouseover && ctx.MouseDown == 0) { ctx.Hover = id; } + + if (ctx.Focus == id) + { + if (ctx.MousePressed != 0 && !mouseover) { MicroUI.SetFocus(ctx, 0); } + if (ctx.MouseDown == 0 && (opt & (int)Options.HoldFocus) == 0) { MicroUI.SetFocus(ctx, 0); } + } + + if (ctx.Hover == id) + { + if (ctx.MousePressed != 0) + { + MicroUI.SetFocus(ctx, id); + } + else if (!mouseover) + { + ctx.Hover = 0; + } + } + } + + public static MuResult ButtonEx(MuContext ctx, string? label, int icon, int opt) + { + ResultFlags res = 0; + uint id; + + if (!string.IsNullOrEmpty(label)) + { + id = MicroUI.GetId(ctx, System.Text.Encoding.UTF8.GetBytes(label)); + } + else + { + id = MicroUI.GetId(ctx, BitConverter.GetBytes(icon)); + } + + 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)) + { + DrawControlText(ctx, label, r, ColorType.Text, opt); + } + if (icon != 0) + { + MuCommandList.DrawIcon(ctx, icon, r, ctx.Style.Colors[(int)ColorType.Text]); + } + + return new MuResult(res); + } + + public static MuResult Checkbox(MuContext ctx, string? label, ref bool state) + { + 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()); + id = MicroUI.GetId(ctx, idData); + } + else + { + id = MicroUI.GetId(ctx, BitConverter.GetBytes(state ? 1 : 0)); + } + + 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); + + if (state) + { + 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); + DrawControlText(ctx, label, labelRect, ColorType.Text, 0); + } + + return new MuResult(res); + } } } \ No newline at end of file diff --git a/MicroUI.cs/Program.cs b/MicroUI.cs/Program.cs index 0423b90..2053df5 100644 --- a/MicroUI.cs/Program.cs +++ b/MicroUI.cs/Program.cs @@ -1,5 +1,6 @@ using Raylib_cs; using MicroUI; +using System.Numerics; namespace MicroUI; @@ -27,22 +28,34 @@ class Program // Initialize microui MicroUI.Init(ctx); + bool isChecked = false; + + while (!Raylib.WindowShouldClose()) { + // Handle input + var mousePos = Raylib.GetMousePosition(); + MuInput.MouseMove(ctx, (int)mousePos.X, (int)mousePos.Y); + + if (Raylib.IsMouseButtonPressed(Raylib_cs.MouseButton.Left)) + { + Console.WriteLine("Mouse down"); + MuInput.MouseDown(ctx, (int)mousePos.X, (int)mousePos.Y, 1); + } + if (Raylib.IsMouseButtonReleased(Raylib_cs.MouseButton.Left)) + { + MuInput.MouseUp(ctx, (int)mousePos.X, (int)mousePos.Y, 1); + } + Raylib.BeginDrawing(); - Raylib.ClearBackground(Color.White); + Raylib.ClearBackground(Color.Black); // Begin frame MicroUI.Begin(ctx); - if(MuControl.BeginWindowEx(ctx, "Test Window", new MuRect(100, 100, 100, 100), 0)){ - - MuControl.EndWindow(ctx); - } - - if(MuControl.BeginWindowEx(ctx, "Test Window 2", new MuRect(120, 120, 300, 200), 0)){ - MuControl.EndWindow(ctx); - } + for(int i = 0; i < 10; i++){ + Window(ctx, i * 15, i * 15, i, ref isChecked); + } // End frame MicroUI.End(ctx); @@ -51,36 +64,142 @@ class Program MuCommand? cmd = null; while ((cmd = MuCommandList.NextCommand(ctx, ref cmdIndex)) != null) { - Console.Write($"Command: {cmd.Type}"); + //Console.Write($"Command: {cmd.Type}"); switch (cmd) { case MuRectCommand rectCmd: - Console.Write($" | Rect: ({rectCmd.Rect.X},{rectCmd.Rect.Y},{rectCmd.Rect.W},{rectCmd.Rect.H}) Color: {rectCmd.Color.R},{rectCmd.Color.G},{rectCmd.Color.B},{rectCmd.Color.A}"); + //Console.WriteLine($" | Rect: ({rectCmd.Rect.X},{rectCmd.Rect.Y},{rectCmd.Rect.W},{rectCmd.Rect.H}) Color: {rectCmd.Color.R},{rectCmd.Color.G},{rectCmd.Color.B},{rectCmd.Color.A}"); Raylib.DrawRectangle(rectCmd.Rect.X, rectCmd.Rect.Y, rectCmd.Rect.W, rectCmd.Rect.H, new Color(rectCmd.Color.R, rectCmd.Color.G, rectCmd.Color.B, rectCmd.Color.A)); break; case MuClipCommand clipCmd: - Console.Write($" | Clip: ({clipCmd.Rect.X},{clipCmd.Rect.Y},{clipCmd.Rect.W},{clipCmd.Rect.H})"); + //Console.Write($" | Clip: ({clipCmd.Rect.X},{clipCmd.Rect.Y},{clipCmd.Rect.W},{clipCmd.Rect.H})"); break; case MuJumpCommand jumpCmd: - Console.Write($" | Jump to: {jumpCmd.DestinationIndex}"); + //Console.Write($" | Jump to: {jumpCmd.DestinationIndex}"); break; case MuTextCommand textCmd: - Console.Write($" | Text: '{textCmd.Text}' at ({textCmd.Position.X},{textCmd.Position.Y}) Color: {textCmd.Color.R},{textCmd.Color.G},{textCmd.Color.B},{textCmd.Color.A}"); + Raylib.DrawText(textCmd.Text, textCmd.Position.X, textCmd.Position.Y, 16, new Color(textCmd.Color.R, textCmd.Color.G, textCmd.Color.B, textCmd.Color.A)); + //Console.Write($" | Text: '{textCmd.Text}' at ({textCmd.Position.X},{textCmd.Position.Y}) Color: {textCmd.Color.R},{textCmd.Color.G},{textCmd.Color.B},{textCmd.Color.A}"); break; case MuIconCommand iconCmd: - Console.Write($" | Icon: {iconCmd.IconId} Rect: ({iconCmd.Rect.X},{iconCmd.Rect.Y},{iconCmd.Rect.W},{iconCmd.Rect.H}) Color: {iconCmd.Color.R},{iconCmd.Color.G},{iconCmd.Color.B},{iconCmd.Color.A}"); + if (iconCmd.IconId == (int)IconType.Check) + { + // Draw a check mark + int centerX = iconCmd.Rect.X + iconCmd.Rect.W / 2; + int centerY = iconCmd.Rect.Y + iconCmd.Rect.H / 2; + int size = Math.Min(iconCmd.Rect.W, iconCmd.Rect.H) / 3; + + // Draw check mark lines + Raylib.DrawLine( + centerX - size, centerY - size/2, + centerX - size/3, centerY + size/2, + new Color(iconCmd.Color.R, iconCmd.Color.G, iconCmd.Color.B, iconCmd.Color.A) + ); + Raylib.DrawLine( + centerX - size/3, centerY + size/2, + centerX + size, centerY - size, + new Color(iconCmd.Color.R, iconCmd.Color.G, iconCmd.Color.B, iconCmd.Color.A) + ); + } + else if (iconCmd.IconId == (int)IconType.Close) + { + // Draw an X for close + int centerX = iconCmd.Rect.X + iconCmd.Rect.W / 2; + int centerY = iconCmd.Rect.Y + iconCmd.Rect.H / 2; + int size = Math.Min(iconCmd.Rect.W, iconCmd.Rect.H) / 3; + + // Draw X lines + Raylib.DrawLine( + centerX - size, centerY - size, + centerX + size, centerY + size, + new Color(iconCmd.Color.R, iconCmd.Color.G, iconCmd.Color.B, iconCmd.Color.A) + ); + Raylib.DrawLine( + centerX - size, centerY + size, + centerX + size, centerY - size, + new Color(iconCmd.Color.R, iconCmd.Color.G, iconCmd.Color.B, iconCmd.Color.A) + ); + } + else if (iconCmd.IconId == (int)IconType.Collapsed) + { + // Draw a right-pointing triangle for collapsed + int centerX = iconCmd.Rect.X + iconCmd.Rect.W / 2; + int centerY = iconCmd.Rect.Y + iconCmd.Rect.H / 2; + int size = Math.Min(iconCmd.Rect.W, iconCmd.Rect.H) / 3; + + // Draw triangle pointing right + Raylib.DrawTriangle( + new Vector2(centerX - size/2, centerY - size), + new Vector2(centerX + size/2, centerY), + new Vector2(centerX - size/2, centerY + size), + new Color(iconCmd.Color.R, iconCmd.Color.G, iconCmd.Color.B, iconCmd.Color.A) + ); + } + else if (iconCmd.IconId == (int)IconType.Expanded) + { + // Draw a down-pointing triangle for expanded + int centerX = iconCmd.Rect.X + iconCmd.Rect.W / 2; + int centerY = iconCmd.Rect.Y + iconCmd.Rect.H / 2; + int size = Math.Min(iconCmd.Rect.W, iconCmd.Rect.H) / 3; + + // Draw triangle pointing down + Raylib.DrawTriangle( + new Vector2(centerX - size, centerY - size/2), + new Vector2(centerX + size, centerY - size/2), + new Vector2(centerX, centerY + size/2), + new Color(iconCmd.Color.R, iconCmd.Color.G, iconCmd.Color.B, iconCmd.Color.A) + ); + } + else + { + // For unknown icons, draw a colored rectangle + Raylib.DrawRectangle(iconCmd.Rect.X, iconCmd.Rect.Y, iconCmd.Rect.W, iconCmd.Rect.H, + new Color(iconCmd.Color.R, iconCmd.Color.G, iconCmd.Color.B, iconCmd.Color.A)); + } break; } - Console.WriteLine(""); } Raylib.EndDrawing(); - //break; - } Raylib.CloseWindow(); } + + + public static void Window(MuContext ctx, int x, int y, int index, ref bool isChecked){ + if(MuControl.BeginWindowEx(ctx, $"Test Window {index}", new MuRect(x, y, 300, 200), 0)){ + + MuLayoutUtil.LayoutBeginColumn(ctx); + // Test button + // if (MuControl.ButtonEx(ctx, "Click Me!", 0, 0)) + // { + // Console.WriteLine("Button clicked!"); + // } + MuLayoutUtil.LayoutRow(ctx, 2, new int[]{200, 50}, 0); + if(MuControl.Checkbox(ctx, "Checkbox1", ref isChecked)){ + Console.WriteLine("Checkbox clicked!"); + } + if(MuControl.ButtonEx(ctx, null, (int)IconType.Check, 0)){ + Console.WriteLine("Button clicked!"); + } + + // if(MuControl.Checkbox(ctx, "Checkbox2", ref isChecked)){ + // Console.WriteLine("Checkbox clicked!"); + // } + // if(MuControl.Checkbox(ctx, "Checkbox3", ref isChecked)){ + // Console.WriteLine("Checkbox clicked!"); + // } + // if(MuControl.Checkbox(ctx, "Checkbox4", ref isChecked)){ + // Console.WriteLine("Checkbox clicked!"); + // } + + MuLayoutUtil.LayoutEndColumn(ctx); + MuControl.EndWindow(ctx); + } + + + } } \ No newline at end of file