More control components

This commit is contained in:
Bobby Lucero 2025-07-07 21:04:03 -04:00
parent deda09a4a5
commit d0ff5d2929
2 changed files with 337 additions and 27 deletions

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}