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