Skip to content

Instantly share code, notes, and snippets.

@farism
Created January 31, 2025 17:01
Show Gist options
  • Save farism/deb09a8c21ae5589d944e41a3c7930a3 to your computer and use it in GitHub Desktop.
Save farism/deb09a8c21ae5589d944e41a3c7930a3 to your computer and use it in GitHub Desktop.
clay-beef
using System;
using System.Diagnostics;
namespace Clay;
public static class Clay
{
[CRepr]
public struct Array<T>
{
public int32 capacity;
public int32 length;
public T* internalArray;
}
[CRepr]
public struct ClayString
{
public int32 length;
public char8* chars;
public this(int32 length, char8* chars)
{
this.length = length;
this.chars = chars;
}
}
[CRepr]
public struct StringSlice
{
public int32 length;
public char8* chars;
public char8* baseChars;
}
[CRepr]
public struct Arena
{
public uint* nextAllocation;
public uint* capacity;
public char8* memory;
}
[CRepr]
public struct Dimensions
{
public float width = 0, height = 0;
public this(float width, float height)
{
this.width = width;
this.height = height;
}
}
[CRepr]
public struct Vector2
{
public float x, y;
}
[CRepr]
public struct Color
{
public float r = 0, g = 0, b = 0, a = 0;
public this(float r = 0, float g = 0, float b = 0, float a = 0)
{
this.r = r;
this.g = g;
this.b = b;
this.a = a;
}
}
[CRepr]
public struct BoundingBox
{
public float x = 0, y = 0, width = 0, height = 0;
}
[CRepr]
public struct ElementId
{
public uint32 id, offset, baseId;
public ClayString stringId;
public this(uint32 id, uint32 offset, uint32 baseId, ClayString stringId)
{
this.id = id;
this.offset = offset;
this.baseId = baseId;
this.stringId = stringId;
}
public this()
{
this.id = this.offset = this.baseId = 0;
this.stringId = ClayString(0, "");
}
}
[CRepr]
public struct CornerRadius
{
public float topLeft = 0, topRight = 0, bottomLeft = 0, bottomRight = 0;
public this(float topLeft, float topRight, float bottomLeft, float bottomRight)
{
this.topLeft = topLeft;
this.topRight = topRight;
this.bottomLeft = bottomLeft;
this.bottomRight = bottomRight;
}
public this(float radius)
{
this.topLeft = this.topRight = this.bottomLeft = this.bottomRight = radius;
}
public this() { }
}
public enum ElementConfigType
{
Rectangle = 1,
Border = 2,
Floating = 4,
Scroll = 8,
Image = 16,
Text = 32,
Custom = 64,
// Odin specific enum types
Id = 65,
Layout = 66,
Children = 67,
}
public enum LayoutDirection
{
LeftToRight,
TopToBottom,
}
public enum LayoutAlignmentX
{
Left,
Right,
Center,
}
public enum LayoutAlignmentY
{
Top,
Bottom,
Center,
}
public enum SizingType
{
Fit,
Grow,
Percent,
Fixed,
}
[CRepr]
public struct ChildAlignment
{
public LayoutAlignmentX x = .Left;
public LayoutAlignmentY y = .Top;
public this(LayoutAlignmentX x = .Left, LayoutAlignmentY y = .Top)
{
this.x = x;
this.y = y;
}
}
[CRepr]
public struct SizingMinMax
{
public float min = 0, max = 0;
public this(float min, float max)
{
this.min = min;
this.max = max;
}
public this() { }
}
[CRepr, Union]
public struct SizingConstraints
{
public SizingMinMax minMax;
public float percent;
public this(SizingMinMax minMax)
{
this.minMax = minMax;
}
public this(float percent)
{
this.percent = percent;
}
}
[CRepr]
public struct SizingAxis
{
public SizingConstraints constraints;
public SizingType type;
public this(SizingType type, SizingConstraints constraints)
{
this.constraints = constraints;
this.type = type;
}
public this(SizingType type, SizingMinMax minMax)
{
this.constraints = .(minMax);
this.type = type;
}
public this(SizingType type, float percent)
{
this.constraints = .(percent);
this.type = type;
}
}
[CRepr]
public struct Sizing
{
public SizingAxis width = SizingAxis(.Fixed, 0);
public SizingAxis height = SizingAxis(.Fixed, 0);
public this(SizingAxis width, SizingAxis height)
{
this.width = width;
this.height = height;
}
public this() { }
}
[CRepr]
public struct Padding
{
public uint16 left, right, top, bottom;
public this(uint16 left, uint16 right, uint16 top, uint16 bottom)
{
this.left = left;
this.right = right;
this.top = top;
this.bottom = bottom;
}
public this(uint16 padding)
{
this.left = this.right = this.top = this.bottom = padding;
}
}
[CRepr]
public struct LayoutConfig
{
public Sizing sizing = .();
public Padding padding = .(0);
public uint16 childGap = 0;
public ChildAlignment childAlignment = .();
public LayoutDirection layoutDirection = .LeftToRight;
}
[CRepr]
public struct RectangleElementConfig
{
public Color color;
public CornerRadius cornerRadius;
}
public enum TextWrapMode
{
Words,
Newlines,
None,
}
[CRepr]
public struct TextElementConfig
{
public Color textColor;
public uint16 fontId, fontSize, letterSpacing, lineHeight;
public TextWrapMode wrapMode;
public bool hashStringContents;
}
[CRepr]
public struct ImageElementConfig
{
public void* imageData;
public Dimensions sourceDimensions;
}
public enum FloatingAttachPointType
{
LeftTop,
LeftCenter,
LeftBottom,
CenterTop,
CenterCenter,
CenterBottom,
RightTop,
RightCenter,
RightBottom,
}
[CRepr]
public struct FloatingAttachPoints
{
public FloatingAttachPointType element;
public FloatingAttachPointType parent;
}
public enum PointerCaptureMode
{
Capture,
Passthrough,
}
[CRepr]
public struct FloatingElementConfig
{
public Vector2 offset;
public Dimensions expand;
public uint16 zIndex;
public uint32 parentId;
public FloatingAttachPoints attachment;
public PointerCaptureMode pointerCaptureMode;
}
[CRepr]
public struct CustomElementConfig
{
public void* customData;
}
[CRepr]
public struct ScrollElementConfig
{
public bool horizontal;
public bool vertical;
}
[CRepr]
public struct Border
{
public uint32 width = 0;
public Color color = .();
}
[CRepr]
public struct BorderElementConfig
{
public Border left = .();
public Border right = .();
public Border top = .();
public Border bottom = .();
public Border betweenChildren = .();
public CornerRadius cornerRadius = .();
public this(Border left, Border right, Border top, Border bottom, Border betweenChildren, CornerRadius cornerRadius)
{
this.left = left;
this.right = right;
this.top = top;
this.bottom = bottom;
this.betweenChildren = betweenChildren;
this.cornerRadius = cornerRadius;
}
public this(Border all, float radius)
{
this.left = this.right = this.top = this.bottom = this.betweenChildren = all;
this.cornerRadius = .(radius);
}
public this(Border all)
{
this.left = this.right = this.top = this.bottom = this.betweenChildren = all;
}
}
[CRepr, Union]
public struct ElementConfigUnion
{
public RectangleElementConfig* rectangleElementConfig;
public TextElementConfig* textElementConfig;
public ImageElementConfig* imageElementConfig;
public FloatingElementConfig* floatingElementConfig;
public CustomElementConfig* customElementConfig;
public ScrollElementConfig* scrollElementConfig;
public BorderElementConfig* borderElementConfig;
};
[CRepr]
public struct ElementConfig
{
public ElementConfigType type;
public ElementConfigUnion config;
};
[CRepr]
public struct ScrollContainerData
{
// Note: This is a pointer to the real internal scroll position, mutating it may cause a change in final layout.
// Intended for use with external functionality that modifies scroll position, such as scroll bars or auto scrolling.
public Vector2* scrollPosition;
public Dimensions scrollContainerDimensions;
public Dimensions contentDimensions;
public ScrollElementConfig config; // Indicates whether an actual scroll container matched the provided ID or if the default struct was returned.
public bool found;
}
[CRepr]
public struct ElementData
{
public BoundingBox boundingBox;
// Indicates whether an actual Element matched the provided ID or if the default struct was returned.
public bool found;
};
public enum RenderCommandType
{
None,
Rectangle,
Border,
Text,
Image,
ScissorStart,
ScissorEnd,
Custom,
}
[CRepr]
public struct RenderCommand
{
public BoundingBox boundingBox;
public ElementConfigUnion config;
public StringSlice text;
public int32 zIndex;
public uint32 id;
public RenderCommandType commandType;
}
public enum ErrorType
{
TextMeasurementFunctionNotProvided,
ArenaCapacityExceeded,
ElementsCapacityExceeded,
TextMeasurementCapacityExceeded,
DuplicateId,
FloatingContainerParentNotFound,
InternalError,
}
[CRepr]
public struct ErrorData
{
public ErrorType errorType;
public ClayString errorText;
public void* userData;
}
public function void ErrorHandlerCallback(ErrorData errorData);
[CRepr]
public struct ErrorHandler
{
public ErrorHandlerCallback handler;
public void* userData;
public this(ErrorHandlerCallback handler, void* userData)
{
this.handler = handler;
this.userData = userData;
}
}
[CLink]
public static extern uint32 Clay_MinMemorySize();
[CLink]
public static extern Arena Clay_CreateArenaWithCapacityAndMemory(uint32 capacity, uint8* offset);
[CLink]
public static extern void Clay_SetPointerState(Vector2 position, bool pointerDown);
[CLink]
public static extern void Clay_Initialize(Arena arena, Dimensions layoutDimensions, ErrorHandler errorHandler);
[CLink]
public static extern void Clay_UpdateScrollContainers(bool enableDragScrolling, Vector2 scrollDelta, float deltaTime);
[CLink]
public static extern void Clay_SetLayoutDimensions(Dimensions dimensions);
[CLink]
public static extern void Clay_BeginLayout();
[CLink]
public static extern Array<RenderCommand> Clay_EndLayout();
[CLink]
public static extern bool Clay_PointerOver(ElementId id);
[CLink]
public static extern ElementId Clay_GetElementId(ClayString id);
[CLink]
public static extern ScrollContainerData Clay_GetScrollContainerData(ElementId id);
[CLink]
public static extern void Clay_SetMeasureTextFunction(function Dimensions(StringSlice text, TextElementConfig* config, uint64* userData) measureTextFunction, uint64* userData);
[CLink]
public static extern RenderCommand* Clay_RenderCommandArray_Get(Array<RenderCommand>* array, int32 index);
[CLink]
public static extern void Clay_SetDebugModeEnabled(bool enabled);
[CLink]
public static extern void Clay__OpenElement();
[CLink]
public static extern void Clay__CloseElement();
[CLink]
public static extern void Clay__ElementPostConfiguration();
[CLink]
public static extern void Clay__OpenTextElement(ClayString text, TextElementConfig* textConfig);
[CLink]
public static extern void Clay__AttachId(ElementId id);
[CLink]
public static extern void Clay__AttachLayoutConfig(LayoutConfig* layoutConfig);
[CLink]
public static extern void Clay__AttachElementConfig(void* config, ElementConfigType type);
[CLink]
public static extern LayoutConfig* Clay__StoreLayoutConfig(LayoutConfig config);
[CLink]
public static extern RectangleElementConfig* Clay__StoreRectangleElementConfig(RectangleElementConfig config);
[CLink]
public static extern TextElementConfig* Clay__StoreTextElementConfig(TextElementConfig config);
[CLink]
public static extern ImageElementConfig* Clay__StoreImageElementConfig(ImageElementConfig config);
[CLink]
public static extern FloatingElementConfig* Clay__StoreFloatingElementConfig(FloatingElementConfig config);
[CLink]
public static extern CustomElementConfig* Clay__StoreCustomElementConfig(CustomElementConfig config);
[CLink]
public static extern ScrollElementConfig* Clay__StoreScrollElementConfig(ScrollElementConfig config);
[CLink]
public static extern BorderElementConfig* Clay__StoreBorderElementConfig(BorderElementConfig config);
[CLink]
public static extern ElementId Clay__HashString(ClayString toHash, uint32 index, uint32 seed);
[CLink]
public static extern uint32 Clay__GetOpenLayoutElementId();
function void ChildrenCallback();
public struct ChildrenConfig
{
function void callback();
}
public struct TypedConfig
{
public ElementConfigType type;
public void* config = null;
public ElementId id = ElementId();
public this(ElementConfigType type, void* config, ElementId id)
{
this.type = type;
this.config = config;
this.id = id;
}
public this(ElementConfigType type, void* config)
{
this.type = type;
this.config = config;
}
public this(ElementConfigType type, ElementId id)
{
this.type = type;
this.id = id;
}
}
public static void UI(params TypedConfig[] configs)
{
Clay__OpenElement();
defer Clay__CloseElement();
for (let config in configs)
{
switch (config.type) {
case ElementConfigType.Id:
Clay__AttachId(config.id);
case ElementConfigType.Layout:
Clay__AttachLayoutConfig((LayoutConfig*)config.config);
case ElementConfigType.Children:
(ChildrenConfig*)config.config;
default:
Clay__AttachElementConfig(config.config, config.type);
}
}
Clay__ElementPostConfiguration();
}
public struct UIConfig
{
public ElementId id;
public LayoutConfig layout;
public RectangleElementConfig rectangle;
public TextElementConfig text;
public ImageElementConfig image;
public FloatingElementConfig floating;
public CustomElementConfig custom;
public ScrollElementConfig scroll;
public BorderElementConfig border;
};
public static void UI2(...)
{
Clay__OpenElement();
defer Clay__CloseElement();
/*switch (config.id) {
case ElementConfigType.Id:
Clay__AttachId(config.id);
case ElementConfigType.Layout:
Clay__AttachLayoutConfig((LayoutConfig*)config.config);
default:
Clay__AttachElementConfig(config.config, config.type);
}*/
Clay__ElementPostConfiguration();
}
public static TypedConfig Layout(LayoutConfig config)
{
return .(ElementConfigType.Layout, Clay__StoreLayoutConfig(config));
}
public static TypedConfig Rectangle(RectangleElementConfig config)
{
return .(ElementConfigType.Rectangle, Clay__StoreRectangleElementConfig(config));
}
public static void Text(String text, TextElementConfig* config)
{
Clay__OpenTextElement(MakeClayString(text), config);
}
public static TextElementConfig* TextConfig(TextElementConfig config)
{
return Clay__StoreTextElementConfig(config);
}
public static TypedConfig Image(ImageElementConfig config)
{
return .(ElementConfigType.Image, Clay__StoreImageElementConfig(config));
}
public static TypedConfig Floating(FloatingElementConfig config)
{
return .(ElementConfigType.Floating, Clay__StoreFloatingElementConfig(config));
}
public static TypedConfig Custom(CustomElementConfig config)
{
return .(ElementConfigType.Custom, Clay__StoreCustomElementConfig(config));
}
public static TypedConfig Scroll(ScrollElementConfig config)
{
return .(ElementConfigType.Scroll, Clay__StoreScrollElementConfig(config));
}
public static TypedConfig Border(BorderElementConfig config)
{
return .(ElementConfigType.Border, Clay__StoreBorderElementConfig(config));
}
public static TypedConfig BorderOutside(Border border)
{
return .(ElementConfigType.Border, Clay__StoreBorderElementConfig(.(border)));
}
public static TypedConfig BorderOutsideRadius(Border outsideBorders, float radius)
{
return .(ElementConfigType.Border, Clay__StoreBorderElementConfig(.(outsideBorders, .(radius))));
}
public static TypedConfig BorderAll(Border allBorders)
{
return .(ElementConfigType.Border, Clay__StoreBorderElementConfig(.(allBorders)));
}
public static TypedConfig BorderAllRadius(Border allBorders, float radius)
{
return .(ElementConfigType.Border, Clay__StoreBorderElementConfig(.(allBorders, radius)));
}
public static CornerRadius CornerRadiusAll(float radius)
{
return .(radius);
}
public static SizingAxis SizingFit(SizingMinMax sizeMinMax = .())
{
return SizingAxis(.Fit, sizeMinMax);
}
public static SizingAxis SizingGrow(SizingMinMax sizeMinMax = .())
{
return SizingAxis(.Grow, sizeMinMax);
}
public static SizingAxis SizingExpand()
{
return SizingAxis(.Grow, 100);
}
public static SizingAxis SizingFixed(float size)
{
return SizingAxis(.Fixed, .(size, size));
}
public static SizingAxis SizingPercent(float sizePercent)
{
return SizingAxis(.Percent, .(sizePercent));
}
public static ClayString MakeClayString(String label)
{
return .((int32)label.Length, label);
}
public static TypedConfig ID(String label, uint32 index = 0)
{
return TypedConfig(ElementConfigType.Id, Clay__HashString(MakeClayString(label), index, 0));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment