Initial work in progress
This commit is contained in:
commit
a911d5310c
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
MicroUI.cs/obj
|
||||||
|
MicroUI.cs/bin
|
||||||
|
.idea
|
||||||
16
MicroUI.cs.sln
Normal file
16
MicroUI.cs.sln
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MicroUI.cs", "MicroUI.cs\MicroUI.cs.csproj", "{7668C69C-9286-4EB9-BA74-5F9CC2CF953C}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{7668C69C-9286-4EB9-BA74-5F9CC2CF953C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{7668C69C-9286-4EB9-BA74-5F9CC2CF953C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{7668C69C-9286-4EB9-BA74-5F9CC2CF953C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{7668C69C-9286-4EB9-BA74-5F9CC2CF953C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
808
MicroUI.cs/MicroUI.cs
Normal file
808
MicroUI.cs/MicroUI.cs
Normal file
@ -0,0 +1,808 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace MicroUI
|
||||||
|
{
|
||||||
|
using mu_Id = System.UInt32;
|
||||||
|
|
||||||
|
|
||||||
|
public static class Constants
|
||||||
|
{
|
||||||
|
public const string Version = "1.0.0";
|
||||||
|
public const int CommandListSize = 256 * 1024;
|
||||||
|
public const int RootListSize = 32;
|
||||||
|
public const int ContainerStackSize = 32;
|
||||||
|
public const int ClipStackSize = 32;
|
||||||
|
public const int IdStackSize = 32;
|
||||||
|
public const int LayoutStackSize = 16;
|
||||||
|
public const int ContainerPoolSize = 48;
|
||||||
|
public const int TreeNodePoolSize = 48;
|
||||||
|
public const int MaxWidths = 16;
|
||||||
|
public const int MaxFormatLength = 127;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Format
|
||||||
|
{
|
||||||
|
public static string Real(float val) => val.ToString("G3");
|
||||||
|
public static string Slider(float val) => val.ToString("F2");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class MathUtil
|
||||||
|
{
|
||||||
|
public static int Min(int a, int b) => a < b ? a : b;
|
||||||
|
public static int Max(int a, int b) => a > b ? a : b;
|
||||||
|
public static int Clamp(int x, int a, int b) => Min(b, Max(a, x));
|
||||||
|
|
||||||
|
public static float Min(float a, float b) => a < b ? a : b;
|
||||||
|
public static float Max(float a, float b) => a > b ? a : b;
|
||||||
|
public static float Clamp(float x, float a, float b) => Min(b, Max(a, x));
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct FixedStack<T>
|
||||||
|
{
|
||||||
|
public int Index;
|
||||||
|
public T[] Items;
|
||||||
|
|
||||||
|
public FixedStack(int capacity)
|
||||||
|
{
|
||||||
|
Items = new T[capacity];
|
||||||
|
Index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Push(T val)
|
||||||
|
{
|
||||||
|
if (Index >= Items.Length)
|
||||||
|
{
|
||||||
|
MuAssert.Expect(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Items[Index] = val;
|
||||||
|
Index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T Pop()
|
||||||
|
{
|
||||||
|
MuAssert.Expect(Index > 0);
|
||||||
|
Index--;
|
||||||
|
return Items[Index];
|
||||||
|
}
|
||||||
|
|
||||||
|
public T Peek()
|
||||||
|
{
|
||||||
|
MuAssert.Expect(Index > 0);
|
||||||
|
return Items[Index - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear() => Index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ClipMode
|
||||||
|
{
|
||||||
|
Part = 1,
|
||||||
|
All
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum CommandType
|
||||||
|
{
|
||||||
|
Jump = 1,
|
||||||
|
Clip,
|
||||||
|
Rect,
|
||||||
|
Text,
|
||||||
|
Icon,
|
||||||
|
Max
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ColorType
|
||||||
|
{
|
||||||
|
Text,
|
||||||
|
Border,
|
||||||
|
WindowBg,
|
||||||
|
TitleBg,
|
||||||
|
TitleText,
|
||||||
|
PanelBg,
|
||||||
|
Button,
|
||||||
|
ButtonHover,
|
||||||
|
ButtonFocus,
|
||||||
|
Base,
|
||||||
|
BaseHover,
|
||||||
|
BaseFocus,
|
||||||
|
ScrollBase,
|
||||||
|
ScrollThumb,
|
||||||
|
Max
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum IconType
|
||||||
|
{
|
||||||
|
Close = 1,
|
||||||
|
Check = 2,
|
||||||
|
Collapsed = 3,
|
||||||
|
Expanded = 4,
|
||||||
|
Max = 5
|
||||||
|
}
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
public enum ResultFlags
|
||||||
|
{
|
||||||
|
Active = 1 << 0, // 1
|
||||||
|
Submit = 1 << 1, // 2
|
||||||
|
Change = 1 << 2 // 4
|
||||||
|
}
|
||||||
|
|
||||||
|
[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
|
||||||
|
}
|
||||||
|
|
||||||
|
[System.Flags]
|
||||||
|
public enum MouseButton
|
||||||
|
{
|
||||||
|
Left = 1 << 0, // 1
|
||||||
|
Right = 1 << 1, // 2
|
||||||
|
Middle = 1 << 2 // 4
|
||||||
|
}
|
||||||
|
|
||||||
|
[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
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct MuVec2
|
||||||
|
{
|
||||||
|
public int X;
|
||||||
|
public int Y;
|
||||||
|
|
||||||
|
public MuVec2(int x, int y)
|
||||||
|
{
|
||||||
|
X = x;
|
||||||
|
Y = y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct MuRect
|
||||||
|
{
|
||||||
|
public int X;
|
||||||
|
public int Y;
|
||||||
|
public int W;
|
||||||
|
public int H;
|
||||||
|
|
||||||
|
public MuRect(int x, int y, int w, int h)
|
||||||
|
{
|
||||||
|
X = x;
|
||||||
|
Y = y;
|
||||||
|
W = w;
|
||||||
|
H = h;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct MuColor
|
||||||
|
{
|
||||||
|
public byte R;
|
||||||
|
public byte G;
|
||||||
|
public byte B;
|
||||||
|
public byte A;
|
||||||
|
|
||||||
|
public MuColor(byte r, byte g, byte b, byte a)
|
||||||
|
{
|
||||||
|
R = r;
|
||||||
|
G = g;
|
||||||
|
B = b;
|
||||||
|
A = a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct MuPoolItem
|
||||||
|
{
|
||||||
|
public mu_Id Id;
|
||||||
|
public int LastUpdate;
|
||||||
|
|
||||||
|
public MuPoolItem(uint id, int lastUpdate)
|
||||||
|
{
|
||||||
|
Id = id;
|
||||||
|
LastUpdate = lastUpdate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class MuCommand
|
||||||
|
{
|
||||||
|
public abstract CommandType Type { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MuJumpCommand : MuCommand
|
||||||
|
{
|
||||||
|
public int DestinationIndex { get; set; }
|
||||||
|
public override CommandType Type => CommandType.Jump;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MuClipCommand : MuCommand
|
||||||
|
{
|
||||||
|
public MuRect Rect { get; }
|
||||||
|
|
||||||
|
public MuClipCommand(MuRect rect)
|
||||||
|
{
|
||||||
|
Rect = rect;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override CommandType Type => CommandType.Clip;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MuRectCommand : MuCommand
|
||||||
|
{
|
||||||
|
public MuRect Rect { get; }
|
||||||
|
public MuColor Color { get; }
|
||||||
|
|
||||||
|
public MuRectCommand(MuRect rect, MuColor color)
|
||||||
|
{
|
||||||
|
Rect = rect;
|
||||||
|
Color = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override CommandType Type => CommandType.Rect;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MuTextCommand : MuCommand
|
||||||
|
{
|
||||||
|
public object? Font { get; } // TODO: Idk how this works yet
|
||||||
|
public MuVec2 Position { get; }
|
||||||
|
public MuColor Color { get; }
|
||||||
|
public string Text { get; }
|
||||||
|
|
||||||
|
public MuTextCommand(object? font, MuVec2 position, MuColor color, string text)
|
||||||
|
{
|
||||||
|
Font = font;
|
||||||
|
Position = position;
|
||||||
|
Color = color;
|
||||||
|
Text = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override CommandType Type => CommandType.Text;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MuIconCommand : MuCommand
|
||||||
|
{
|
||||||
|
public MuRect Rect { get; }
|
||||||
|
public int IconId { get; }
|
||||||
|
public MuColor Color { get; }
|
||||||
|
|
||||||
|
public MuIconCommand(MuRect rect, int iconId, MuColor color)
|
||||||
|
{
|
||||||
|
Rect = rect;
|
||||||
|
IconId = iconId;
|
||||||
|
Color = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override CommandType Type => CommandType.Icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MuCommandBuffer
|
||||||
|
{
|
||||||
|
public List<MuCommand> Commands { get; } = new();
|
||||||
|
|
||||||
|
public void Add(MuCommand command)
|
||||||
|
{
|
||||||
|
Commands.Add(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
Commands.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct MuLayout
|
||||||
|
{
|
||||||
|
public MuRect Body { get; set; }
|
||||||
|
public MuRect Next { get; set; }
|
||||||
|
public MuVec2 Position { get; set; }
|
||||||
|
public MuVec2 Size { get; set; }
|
||||||
|
public MuVec2 Max { get; set; }
|
||||||
|
public int[] Widths { get; }
|
||||||
|
public int Items { get; set; }
|
||||||
|
public int ItemIndex { get; set; }
|
||||||
|
public int NextRow { get; set; }
|
||||||
|
public int NextType { get; set; }
|
||||||
|
public int Indent { get; set; }
|
||||||
|
|
||||||
|
public MuLayout()
|
||||||
|
{
|
||||||
|
Widths = new int[Constants.MaxWidths];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MuContainer
|
||||||
|
{
|
||||||
|
public int HeadIndex { get; set; } = -1;
|
||||||
|
public int TailIndex { get; set; } = -1;
|
||||||
|
|
||||||
|
public MuRect Rect { get; set; }
|
||||||
|
public MuRect Body { get; set; }
|
||||||
|
public MuVec2 ContentSize { get; set; }
|
||||||
|
public MuVec2 Scroll { get; set; }
|
||||||
|
public int ZIndex { get; set; }
|
||||||
|
public bool Open { get; set; }
|
||||||
|
|
||||||
|
public MuContainer()
|
||||||
|
{
|
||||||
|
Open = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MuStyle
|
||||||
|
{
|
||||||
|
public object? Font { get; set; } // Replace 'object' with actual font type as needed
|
||||||
|
public MuVec2 Size { get; set; }
|
||||||
|
public int Padding { get; set; }
|
||||||
|
public int Spacing { get; set; }
|
||||||
|
public int Indent { get; set; }
|
||||||
|
public int TitleHeight { get; set; }
|
||||||
|
public int ScrollbarSize { get; set; }
|
||||||
|
public int ThumbSize { get; set; }
|
||||||
|
public MuColor[] Colors { get; set; }
|
||||||
|
|
||||||
|
public MuStyle()
|
||||||
|
{
|
||||||
|
Colors = new MuColor[(int)ColorType.Max];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public delegate int TextWidthDelegate(object font, string str, int len);
|
||||||
|
public delegate int TextHeightDelegate(object font);
|
||||||
|
public delegate void DrawFrameDelegate(MuContext ctx, MuRect rect, ColorType colorId);
|
||||||
|
|
||||||
|
public class MuContext
|
||||||
|
{
|
||||||
|
// Callbacks
|
||||||
|
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 Focus { get; set; }
|
||||||
|
public mu_Id LastId { get; set; }
|
||||||
|
public MuRect LastRect { get; set; }
|
||||||
|
public int LastZIndex { get; set; }
|
||||||
|
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 FixedStack<MuCommand> CommandList { get; } = new FixedStack<MuCommand>(Constants.CommandListSize);
|
||||||
|
public FixedStack<MuContainer> RootList { get; } = new FixedStack<MuContainer>(Constants.RootListSize);
|
||||||
|
public FixedStack<MuContainer> ContainerStack { get; } = new FixedStack<MuContainer>(Constants.ContainerStackSize);
|
||||||
|
public FixedStack<MuRect> ClipStack { get; } = new FixedStack<MuRect>(Constants.ClipStackSize);
|
||||||
|
public FixedStack<uint> IdStack { get; } = new FixedStack<uint>(Constants.IdStackSize);
|
||||||
|
public FixedStack<MuLayout> LayoutStack { get; } = new FixedStack<MuLayout>(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; }
|
||||||
|
public MuVec2 ScrollDelta { get; set; }
|
||||||
|
public int MouseDown { get; set; }
|
||||||
|
public int MousePressed { get; set; }
|
||||||
|
public int KeyDown { get; set; }
|
||||||
|
public int KeyPressed { get; set; }
|
||||||
|
public char[] inputText = new char[32];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class MuAssert
|
||||||
|
{
|
||||||
|
public static void Expect(bool condition,
|
||||||
|
[CallerFilePath] string file = "",
|
||||||
|
[CallerLineNumber] int line = 0,
|
||||||
|
[CallerMemberName] string member = "")
|
||||||
|
{
|
||||||
|
if (!condition)
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine($"Fatal error at {file}:{line} in {member}: assertion failed.");
|
||||||
|
Environment.FailFast("Expectation failed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MicroUI
|
||||||
|
{
|
||||||
|
public static readonly MuRect UnclippedRect = new MuRect(0,0,0x1000000,0x1000000);
|
||||||
|
|
||||||
|
public static readonly MuStyle DefaultStyle = new MuStyle
|
||||||
|
{
|
||||||
|
Font = null,
|
||||||
|
Size = new MuVec2(68, 10),
|
||||||
|
Padding = 5,
|
||||||
|
Spacing = 4,
|
||||||
|
Indent = 24,
|
||||||
|
TitleHeight = 24,
|
||||||
|
ScrollbarSize = 12,
|
||||||
|
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
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public static MuRect ExpandRect(MuRect rect, int n)
|
||||||
|
{
|
||||||
|
return new MuRect(
|
||||||
|
rect.X - n,
|
||||||
|
rect.Y - n,
|
||||||
|
rect.W + 2 * n,
|
||||||
|
rect.H + 2 * n
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MuRect IntersectRects(MuRect r1, MuRect r2)
|
||||||
|
{
|
||||||
|
int x1 = MathUtil.Max(r1.X, r2.X);
|
||||||
|
int y1 = MathUtil.Max(r1.Y, r2.Y);
|
||||||
|
int x2 = MathUtil.Min(r1.X + r1.W, r2.X + r2.W);
|
||||||
|
int y2 = MathUtil.Min(r1.Y + r1.H, r2.Y + r2.H);
|
||||||
|
|
||||||
|
if (x2 < x1) x2 = x1;
|
||||||
|
if (y2 < y1) y2 = y1;
|
||||||
|
|
||||||
|
return new MuRect(x1, y1, x2 - x1, y2 - y1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool RectOverlapsVec2(MuRect r, MuVec2 p)
|
||||||
|
{
|
||||||
|
return p.X >= r.X && p.X < r.X + r.W &&
|
||||||
|
p.Y >= r.Y && p.Y < r.Y + r.H;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void DrawFrame(MuContext ctx, MuRect rect, ColorType colorId)
|
||||||
|
{
|
||||||
|
// Draw filled rectangle with given color
|
||||||
|
DrawRect(ctx, rect, ctx.Style.Colors[(int)colorId]);
|
||||||
|
|
||||||
|
// Early return for certain color IDs (skip border)
|
||||||
|
if (colorId == ColorType.ScrollBase ||
|
||||||
|
colorId == ColorType.ScrollThumb ||
|
||||||
|
colorId == ColorType.TitleBg)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw border if alpha > 0
|
||||||
|
if (ctx.Style.Colors[(int)ColorType.Border].A > 0)
|
||||||
|
{
|
||||||
|
DrawBox(ctx, ExpandRect(rect, 1), ctx.Style.Colors[(int)ColorType.Border]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Begin(MuContext ctx)
|
||||||
|
{
|
||||||
|
MuAssert.Expect(ctx.TextWidth != null && ctx.TextHeight != null);
|
||||||
|
|
||||||
|
ctx.CommandList.Clear();
|
||||||
|
ctx.RootList.Clear();
|
||||||
|
ctx.ScrollTarget = null;
|
||||||
|
|
||||||
|
ctx.HoverRoot = ctx.NextHoverRoot;
|
||||||
|
ctx.NextHoverRoot = null;
|
||||||
|
|
||||||
|
ctx.MouseDelta = new MuVec2(ctx.MousePos.X - ctx.LastMousePos.X, ctx.MousePos.Y - ctx.LastMousePos.Y);
|
||||||
|
|
||||||
|
ctx.Frame++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int CompareZIndex(MuContainer a, MuContainer b)
|
||||||
|
{
|
||||||
|
return a.ZIndex.CompareTo(b.ZIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly IComparer<MuContainer> ZIndexComparerInstance = Comparer<MuContainer>.Create(CompareZIndex);
|
||||||
|
|
||||||
|
public static void End(MuContext ctx){
|
||||||
|
MuAssert.Expect(ctx.ContainerStack.Index == 0);
|
||||||
|
MuAssert.Expect(ctx.ClipStack.Index == 0);
|
||||||
|
MuAssert.Expect(ctx.IdStack.Index == 0);
|
||||||
|
MuAssert.Expect(ctx.LayoutStack.Index == 0);
|
||||||
|
|
||||||
|
if (ctx.ScrollTarget != null)
|
||||||
|
{
|
||||||
|
ctx.ScrollTarget.Scroll = new MuVec2(
|
||||||
|
ctx.ScrollTarget.Scroll.X + ctx.ScrollDelta.X,
|
||||||
|
ctx.ScrollTarget.Scroll.Y + ctx.ScrollDelta.Y
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx.UpdatedFocus == 0)
|
||||||
|
{
|
||||||
|
ctx.Focus = 0;
|
||||||
|
}
|
||||||
|
ctx.UpdatedFocus = 0;
|
||||||
|
|
||||||
|
if (ctx.MousePressed != 0 && ctx.NextHoverRoot != null &&
|
||||||
|
ctx.NextHoverRoot.ZIndex < ctx.LastZIndex &&
|
||||||
|
ctx.NextHoverRoot.ZIndex >= 0)
|
||||||
|
{
|
||||||
|
BringToFront(ctx.NextHoverRoot);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.KeyPressed = 0;
|
||||||
|
ctx.inputText = new char[32];
|
||||||
|
ctx.MousePressed = 0;
|
||||||
|
ctx.ScrollDelta = new MuVec2(0, 0);
|
||||||
|
ctx.LastMousePos = ctx.MousePos;
|
||||||
|
|
||||||
|
int n = ctx.RootList.Index;
|
||||||
|
Array.Sort(ctx.RootList.Items, 0, n, ZIndexComparerInstance);
|
||||||
|
|
||||||
|
|
||||||
|
for (int i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
var cnt = ctx.RootList.Items[i];
|
||||||
|
|
||||||
|
if (i == 0)
|
||||||
|
{
|
||||||
|
((MuJumpCommand)ctx.CommandList.Items[0]).DestinationIndex = cnt.HeadIndex + 1;
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
// Last container tail jump destination points to end of command list (beyond last command)
|
||||||
|
((MuJumpCommand)ctx.CommandList.Items[cnt.TailIndex]).DestinationIndex = cnt.HeadIndex + 1;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SetFocus(MuContext ctx, mu_Id id)
|
||||||
|
{
|
||||||
|
ctx.Focus = id;
|
||||||
|
ctx.UpdatedFocus = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public const uint HashInitial = 2166136261;
|
||||||
|
|
||||||
|
public static void Hash(ref uint hash, ReadOnlySpan<byte> data)
|
||||||
|
{
|
||||||
|
const uint fnvPrime = 16777619;
|
||||||
|
foreach (byte b in data)
|
||||||
|
{
|
||||||
|
hash = (hash ^ b) * fnvPrime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static uint GetId(MuContext ctx, ReadOnlySpan<byte> data)
|
||||||
|
{
|
||||||
|
uint seed = ctx.IdStack.Index > 0 ? ctx.IdStack.Items[ctx.IdStack.Index - 1] : HashInitial;
|
||||||
|
Hash(ref seed, data);
|
||||||
|
ctx.LastId = seed;
|
||||||
|
return seed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void PushId(MuContext ctx, ReadOnlySpan<byte> data)
|
||||||
|
{
|
||||||
|
ctx.IdStack.Push(GetId(ctx, data));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void PopId(MuContext ctx)
|
||||||
|
{
|
||||||
|
ctx.IdStack.Pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void PushClipRect(MuContext ctx, MuRect rect) {
|
||||||
|
MuRect last = GetClipRect(ctx);
|
||||||
|
ctx.ClipStack.Push( IntersectRects(rect, last));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void PopClipRect(MuContext ctx)
|
||||||
|
{
|
||||||
|
ctx.ClipStack.Pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MuRect GetClipRect(MuContext ctx)
|
||||||
|
{
|
||||||
|
MuAssert.Expect(ctx.ClipStack.Index > 0);
|
||||||
|
return ctx.ClipStack.Items[ctx.ClipStack.Index - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ClipMode CheckClip(MuContext ctx, MuRect r)
|
||||||
|
{
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Partially clipped
|
||||||
|
return ClipMode.Part; // MU_CLIP_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 };
|
||||||
|
LayoutRow(ctx, 1, widths, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ref MuLayout GetLayout(MuContext ctx)
|
||||||
|
{
|
||||||
|
if (ctx.LayoutStack.Index == 0)
|
||||||
|
throw new InvalidOperationException("Layout stack is empty.");
|
||||||
|
|
||||||
|
return ref ctx.LayoutStack.Items[ctx.LayoutStack.Index - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PopContainer(MuContext ctx)
|
||||||
|
{
|
||||||
|
MuContainer cnt = GetCurrentContainer(ctx);
|
||||||
|
MuLayout layout = GetLayout(ctx);
|
||||||
|
|
||||||
|
cnt.ContentSize = new MuVec2(
|
||||||
|
layout.Max.X - layout.Body.X,
|
||||||
|
layout.Max.Y - layout.Body.Y
|
||||||
|
);
|
||||||
|
|
||||||
|
// Pop container, layout, and id
|
||||||
|
ctx.ContainerStack.Pop();
|
||||||
|
ctx.LayoutStack.Pop();
|
||||||
|
PopId(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static MuContainer GetCurrentContainer(MuContext ctx)
|
||||||
|
{
|
||||||
|
MuAssert.Expect(ctx.ContainerStack.Index > 0);
|
||||||
|
return ctx.ContainerStack.Items[ctx.ContainerStack.Index - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
/*============================================================================
|
||||||
|
** layout
|
||||||
|
**============================================================================*/
|
||||||
|
|
||||||
|
|
||||||
|
public static void LayoutRow(MuContext ctx, int items, int[] widths, int height)
|
||||||
|
{
|
||||||
|
var layout = GetLayout(ctx); // get current layout (implement accordingly)
|
||||||
|
|
||||||
|
if (items > Constants.MaxWidths)
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(items), $"Items cannot be more than {Constants.MaxWidths}.");
|
||||||
|
|
||||||
|
// Copy widths into layout.Widths
|
||||||
|
Array.Copy(widths, layout.Widths, items);
|
||||||
|
|
||||||
|
layout.Items = items;
|
||||||
|
layout.Position = new MuVec2(layout.Indent, layout.NextRow);
|
||||||
|
layout.Size = new MuVec2(layout.Size.X, height); // assuming Size is MuVec2, updating only Y component
|
||||||
|
layout.ItemIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static MuContainer? GetContainer(MuContext ctx, uint id, int opt)
|
||||||
|
{
|
||||||
|
// Try to get an existing container from the pool
|
||||||
|
int idx = MuPool.Get(ctx, ctx.ContainerPool, ctx.Containers.Length, id);
|
||||||
|
if (idx >= 0)
|
||||||
|
{
|
||||||
|
if (ctx.Containers[idx].Open || (opt & (int)Option.Closed) == 0)
|
||||||
|
{
|
||||||
|
MuPool.Update(ctx, ctx.ContainerPool, idx);
|
||||||
|
}
|
||||||
|
return ctx.Containers[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
// If container is closed, return null
|
||||||
|
if ((opt & (int)Option.Closed) != 0)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new container
|
||||||
|
idx = MuPool.Init(ctx, ctx.ContainerPool, ctx.Containers.Length, id);
|
||||||
|
ref MuContainer cnt = ref ctx.Containers[idx];
|
||||||
|
cnt = new MuContainer(); // Reset all fields
|
||||||
|
cnt.Open = true;
|
||||||
|
BringToFront(ctx, ref cnt);
|
||||||
|
return cnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int Get(MuContext ctx, MuPoolItem[] items, int length, uint id)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < length; i++)
|
||||||
|
{
|
||||||
|
if (items[i].Id == id)
|
||||||
|
{
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
14
MicroUI.cs/MicroUI.cs.csproj
Normal file
14
MicroUI.cs/MicroUI.cs.csproj
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Raylib-cs" Version="7.0.1" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
25
MicroUI.cs/Program.cs
Normal file
25
MicroUI.cs/Program.cs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
using Raylib_cs;
|
||||||
|
|
||||||
|
namespace HelloWorld;
|
||||||
|
|
||||||
|
class Program
|
||||||
|
{
|
||||||
|
// STAThread is required if you deploy using NativeAOT on Windows - See https://github.com/raylib-cs/raylib-cs/issues/301
|
||||||
|
[STAThread]
|
||||||
|
public static void Main()
|
||||||
|
{
|
||||||
|
Raylib.InitWindow(800, 480, "Hello World");
|
||||||
|
|
||||||
|
while (!Raylib.WindowShouldClose())
|
||||||
|
{
|
||||||
|
Raylib.BeginDrawing();
|
||||||
|
Raylib.ClearBackground(Color.White);
|
||||||
|
|
||||||
|
Raylib.DrawText("Hello, world!", 12, 12, 20, Color.Black);
|
||||||
|
|
||||||
|
Raylib.EndDrawing();
|
||||||
|
}
|
||||||
|
|
||||||
|
Raylib.CloseWindow();
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user