mirror of
https://github.com/nicbarker/clay.git
synced 2025-11-01 07:16:17 +00:00
Compare commits
14 commits
e15045714d
...
9853cba679
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9853cba679 | ||
|
|
fd97d8179e | ||
|
|
7216815536 | ||
|
|
83129995f7 | ||
|
|
588b93196c | ||
|
|
382dcde89d | ||
|
|
c6442bd192 | ||
|
|
7874cdb085 | ||
|
|
38bb241ced | ||
|
|
f4933c6669 | ||
|
|
61ba36753b | ||
|
|
4f4605eff9 | ||
|
|
7c65f31f46 | ||
|
|
01025e9157 |
7
.gitignore
vendored
7
.gitignore
vendored
|
|
@ -4,4 +4,9 @@ cmake-build-release/
|
|||
.idea/
|
||||
node_modules/
|
||||
*.dSYM
|
||||
.vs/
|
||||
.vs/
|
||||
bindings/odin/clay-odin/tmp/
|
||||
|
||||
generator/__pycache__/
|
||||
|
||||
generator/generators/__pycache__/
|
||||
|
|
|
|||
275
README.md
275
README.md
|
|
@ -59,7 +59,7 @@ Clay_ElementDeclaration sidebarItemConfig = (Clay_ElementDeclaration) {
|
|||
|
||||
// Re-useable components are just normal functions
|
||||
void SidebarItemComponent() {
|
||||
CLAY(sidebarItemConfig) {
|
||||
CLAY(id, sidebarItemConfig) {
|
||||
// children go here...
|
||||
}
|
||||
}
|
||||
|
|
@ -85,14 +85,13 @@ int main() {
|
|||
Clay_BeginLayout();
|
||||
|
||||
// An example of laying out a UI with a fixed width sidebar and flexible width main content
|
||||
CLAY({ .id = CLAY_ID("OuterContainer"), .layout = { .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0)}, .padding = CLAY_PADDING_ALL(16), .childGap = 16 }, .backgroundColor = {250,250,255,255} }) {
|
||||
CLAY({
|
||||
.id = CLAY_ID("SideBar"),
|
||||
CLAY(CLAY_ID("OuterContainer"), { .layout = { .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0)}, .padding = CLAY_PADDING_ALL(16), .childGap = 16 }, .backgroundColor = {250,250,255,255} }) {
|
||||
CLAY(CLAY_ID("SideBar"), {
|
||||
.layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { .width = CLAY_SIZING_FIXED(300), .height = CLAY_SIZING_GROW(0) }, .padding = CLAY_PADDING_ALL(16), .childGap = 16 },
|
||||
.backgroundColor = COLOR_LIGHT
|
||||
}) {
|
||||
CLAY({ .id = CLAY_ID("ProfilePictureOuter"), .layout = { .sizing = { .width = CLAY_SIZING_GROW(0) }, .padding = CLAY_PADDING_ALL(16), .childGap = 16, .childAlignment = { .y = CLAY_ALIGN_Y_CENTER } }, .backgroundColor = COLOR_RED }) {
|
||||
CLAY({ .id = CLAY_ID("ProfilePicture"), .layout = { .sizing = { .width = CLAY_SIZING_FIXED(60), .height = CLAY_SIZING_FIXED(60) }}, .image = { .imageData = &profilePicture } }) {}
|
||||
CLAY(CLAY_ID("ProfilePictureOuter"), { .layout = { .sizing = { .width = CLAY_SIZING_GROW(0) }, .padding = CLAY_PADDING_ALL(16), .childGap = 16, .childAlignment = { .y = CLAY_ALIGN_Y_CENTER } }, .backgroundColor = COLOR_RED }) {
|
||||
CLAY(CLAY_ID("ProfilePicture"), {.layout = { .sizing = { .width = CLAY_SIZING_FIXED(60), .height = CLAY_SIZING_FIXED(60) }}, .image = { .imageData = &profilePicture } }) {}
|
||||
CLAY_TEXT(CLAY_STRING("Clay - UI Library"), CLAY_TEXT_CONFIG({ .fontSize = 24, .textColor = {255, 255, 255, 255} }));
|
||||
}
|
||||
|
||||
|
|
@ -101,7 +100,7 @@ int main() {
|
|||
SidebarItemComponent();
|
||||
}
|
||||
|
||||
CLAY({ .id = CLAY_ID("MainContent"), .layout = { .sizing = { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0) } }, .backgroundColor = COLOR_LIGHT }) {}
|
||||
CLAY(CLAY_ID("MainContent"), { .layout = { .sizing = { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0) } }, .backgroundColor = COLOR_LIGHT }) {}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -194,16 +193,16 @@ For help starting out or to discuss clay, considering joining [the discord serve
|
|||
## High Level Documentation
|
||||
|
||||
### Building UI Hierarchies
|
||||
Clay UIs are built using the C macro `CLAY({ configuration })`. This macro creates a new empty element in the UI hierarchy, and supports modular customisation of layout, styling and functionality. The `CLAY()` macro can also be _nested_, similar to other declarative UI systems like HTML.
|
||||
Clay UIs are built using the C macro `CLAY(id, { configuration })`. This macro creates a new empty element in the UI hierarchy, and supports modular customisation of layout, styling and functionality. The `CLAY()` macro can also be _nested_, similar to other declarative UI systems like HTML.
|
||||
|
||||
Child elements are added by opening a block: `{}` after calling the `CLAY()` macro (exactly like you would with an `if` statement or `for` loop), and declaring child components inside the braces.
|
||||
```C
|
||||
// Parent element with 8px of padding
|
||||
CLAY({ .layout = { .padding = CLAY_PADDING_ALL(8) } }) {
|
||||
CLAY(CLAY_ID("parent"), { .layout = { .padding = CLAY_PADDING_ALL(8) } }) {
|
||||
// Child element 1
|
||||
CLAY_TEXT(CLAY_STRING("Hello World"), CLAY_TEXT_CONFIG({ .fontSize = 16 }));
|
||||
// Child element 2 with red background
|
||||
CLAY({ .backgroundColor = COLOR_RED }) {
|
||||
CLAY((CLAY_ID("child"), { .backgroundColor = COLOR_RED }) {
|
||||
// etc
|
||||
}
|
||||
}
|
||||
|
|
@ -214,13 +213,13 @@ However, unlike HTML and other declarative DSLs, this macro is just C. As a resu
|
|||
// Re-usable "components" are just functions that declare more UI
|
||||
void ButtonComponent(Clay_String buttonText) {
|
||||
// Red box button with 8px of padding
|
||||
CLAY({ .layout = { .padding = CLAY_PADDING_ALL(8) }, .backgroundColor = COLOR_RED }) {
|
||||
CLAY_AUTO_ID({ .layout = { .padding = CLAY_PADDING_ALL(8) }, .backgroundColor = COLOR_RED }) {
|
||||
CLAY_TEXT(buttonText, textConfig);
|
||||
}
|
||||
}
|
||||
|
||||
// Parent element
|
||||
CLAY({ .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM } }) {
|
||||
CLAY(CLAY_ID("parent"), { .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM } }) {
|
||||
// Render a bunch of text elements
|
||||
for (int i = 0; i < textArray.length; i++) {
|
||||
CLAY_TEXT(textArray.elements[i], textConfig);
|
||||
|
|
@ -240,7 +239,7 @@ CLAY({ .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM } }) {
|
|||
### Configuring Layout and Styling UI Elements
|
||||
The layout and style of clay elements is configured with the [Clay_ElementDeclaration](#clay_elementdeclaration) struct passed to the `CLAY()` macro.
|
||||
```C
|
||||
CLAY({ .layout = { .padding = { 8, 8, 8, 8 }, .layoutDirection = CLAY_TOP_TO_BOTTOM } }) {
|
||||
CLAY(CLAY_ID("box"), { .layout = { .padding = { 8, 8, 8, 8 }, .layoutDirection = CLAY_TOP_TO_BOTTOM } }) {
|
||||
// Children are 8px inset into parent, and laid out top to bottom
|
||||
}
|
||||
```
|
||||
|
|
@ -257,18 +256,21 @@ Clay_ElementDeclaration reuseableStyle = (Clay_ElementDeclaration) {
|
|||
.cornerRadius = { 12, 12, 12, 12 }
|
||||
};
|
||||
|
||||
CLAY(reuseableStyle) {
|
||||
CLAY(CLAY_ID("box"), reuseableStyle) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### Element IDs
|
||||
|
||||
Clay elements can optionally be tagged with a unique identifier using the `.id` field of an element declaration, and with the [CLAY_ID()](#clay_id) convenience macro.
|
||||
The Clay macro by default accepts an ID as its first argument, which is usually provided by the [CLAY_ID()](#clay_id) convenience macro. Elements can also be created with auto generated IDs, by using the [CLAY_AUTO_ID()](#clay-auto-id) macro.
|
||||
|
||||
```C
|
||||
// Will always produce the same ID from the same input string
|
||||
CLAY({ .id = CLAY_ID("OuterContainer") }) {}
|
||||
CLAY(CLAY_ID("OuterContainer"), { ...configuration }) {}
|
||||
|
||||
// Generates a unique ID that may not be the same between two layout calls
|
||||
CLAY_AUTO_ID({ ...configuration }) {}
|
||||
```
|
||||
|
||||
Element IDs have two main use cases. Firstly, tagging an element with an ID allows you to query information about the element later, such as its [mouseover state](#clay_pointerover) or dimensions.
|
||||
|
|
@ -279,11 +281,11 @@ To avoid having to construct dynamic strings at runtime to differentiate ids in
|
|||
```C
|
||||
// This is the equivalent of calling CLAY_ID("Item0"), CLAY_ID("Item1") etc
|
||||
for (int index = 0; index < items.length; index++) {
|
||||
CLAY({ .id = CLAY_IDI("Item", index) }) {}
|
||||
CLAY(CLAY_IDI("Item", index), { ..configuration }) {}
|
||||
}
|
||||
```
|
||||
|
||||
This ID (or, if not provided, an auto generated ID) will be forwarded to the final `Clay_RenderCommandArray` for use in retained mode UIs. Using duplicate IDs may cause some functionality to misbehave (i.e. if you're trying to attach a floating container to a specific element with ID that is duplicated, it may not attach to the one you expect)
|
||||
This ID will be forwarded to the final `Clay_RenderCommandArray` for use in retained mode UIs. Using duplicate IDs may cause some functionality to misbehave (i.e. if you're trying to attach a floating container to a specific element with ID that is duplicated, it may not attach to the one you expect)
|
||||
|
||||
### Mouse, Touch and Pointer Interactions
|
||||
|
||||
|
|
@ -297,7 +299,7 @@ The function `bool Clay_Hovered()` can be called during element construction or
|
|||
|
||||
```C
|
||||
// An orange button that turns blue when hovered
|
||||
CLAY({ .backgroundColor = Clay_Hovered() ? COLOR_BLUE : COLOR_ORANGE }) {
|
||||
CLAY(CLAY_ID("Button"), { .backgroundColor = Clay_Hovered() ? COLOR_BLUE : COLOR_ORANGE }) {
|
||||
bool buttonHovered = Clay_Hovered();
|
||||
CLAY_TEXT(buttonHovered ? CLAY_STRING("Hovered") : CLAY_STRING("Hover me!"), headerTextConfig);
|
||||
}
|
||||
|
|
@ -318,7 +320,7 @@ void HandleButtonInteraction(Clay_ElementId elementId, Clay_PointerData pointerI
|
|||
ButtonData linkButton = (ButtonData) { .link = "https://github.com/nicbarker/clay" };
|
||||
|
||||
// HandleButtonInteraction will be called for each frame the mouse / pointer / touch is inside the button boundaries
|
||||
CLAY({ .layout = { .padding = CLAY_PADDING_ALL(8) } }) {
|
||||
CLAY(CLAY_ID("Button"), { .layout = { .padding = CLAY_PADDING_ALL(8) } }) {
|
||||
Clay_OnHover(HandleButtonInteraction, &linkButton);
|
||||
CLAY_TEXT(CLAY_STRING("Button"), &headerTextConfig);
|
||||
}
|
||||
|
|
@ -360,11 +362,11 @@ Clay_UpdateScrollContainers(
|
|||
);
|
||||
// ...
|
||||
// Clay internally tracks the scroll containers offset, and Clay_GetScrollOffset returns the x,y offset of the currently open element
|
||||
CLAY({ .clip = { .vertical = true, .childOffset = Clay_GetScrollOffset() } }) {
|
||||
CLAY(CLAY_ID("ScrollContainer"), { .clip = { .vertical = true, .childOffset = Clay_GetScrollOffset() } }) {
|
||||
// Scrolling contents
|
||||
}
|
||||
// .childOffset can be provided directly if you would prefer to manage scrolling outside of clay
|
||||
CLAY({ .clip = { .vertical = true, .childOffset = myData.scrollContainer.offset } }) {
|
||||
CLAY(CLAY_ID("ScrollContainer"), { .clip = { .vertical = true, .childOffset = myData.scrollContainer.offset } }) {
|
||||
// Scrolling contents
|
||||
}
|
||||
```
|
||||
|
|
@ -383,10 +385,10 @@ A classic example use case for floating elements is tooltips and modals.
|
|||
```C
|
||||
// The two text elements will be laid out top to bottom, and the floating container
|
||||
// will be attached to "Outer"
|
||||
CLAY({ .id = CLAY_ID("Outer"), .layout = { .layoutDirection = TOP_TO_BOTTOM } }) {
|
||||
CLAY_TEXT(CLAY_ID("Button"), text, &headerTextConfig);
|
||||
CLAY({ .id = CLAY_ID("Tooltip"), .floating = { .attachTo = CLAY_ATTACH_TO_PARENT } }) {}
|
||||
CLAY_TEXT(CLAY_ID("Button"), text, &headerTextConfig);
|
||||
CLAY(CLAY_ID("Outer"), { .layout = { .layoutDirection = TOP_TO_BOTTOM } }) {
|
||||
CLAY_TEXT(text, &headerTextConfig);
|
||||
CLAY(CLAY_ID("Tooltip"), { .floating = { .attachTo = CLAY_ATTACH_TO_PARENT } }) {}
|
||||
CLAY_TEXT(text, &headerTextConfig);
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -424,14 +426,11 @@ typedef struct {
|
|||
// During init
|
||||
Arena frameArena = (Arena) { .memory = malloc(1024) };
|
||||
|
||||
// ...
|
||||
CLAY(0) {
|
||||
// Custom elements only take a single pointer, so we need to store the data somewhere
|
||||
CustomElementData *modelData = (CustomElementData *)(frameArena.memory + frameArena.offset);
|
||||
*modelData = (CustomElementData) { .type = CUSTOM_ELEMENT_TYPE_MODEL, .model = myModel };
|
||||
frameArena.offset += sizeof(CustomElementData);
|
||||
CLAY({ .custom = { .customData = modelData } }) {}
|
||||
}
|
||||
// Custom elements only take a single pointer, so we need to store the data somewhere
|
||||
CustomElementData *modelData = (CustomElementData *)(frameArena.memory + frameArena.offset);
|
||||
*modelData = (CustomElementData) { .type = CUSTOM_ELEMENT_TYPE_MODEL, .model = myModel };
|
||||
frameArena.offset += sizeof(CustomElementData);
|
||||
CLAY(CLAY_ID("3DModelViewer"), { .custom = { .customData = modelData } }) {}
|
||||
|
||||
// Later during your rendering
|
||||
switch (renderCommand->commandType) {
|
||||
|
|
@ -682,7 +681,7 @@ See [Scrolling Elements](#scrolling-elements) for more details.
|
|||
|
||||
```C
|
||||
// Create a horizontally scrolling container
|
||||
CLAY({
|
||||
CLAY(CLAY_ID("ScrollContainer"), {
|
||||
.clip = { .horizontal = true, .childOffset = Clay_GetScrollOffset() }
|
||||
})
|
||||
```
|
||||
|
|
@ -732,9 +731,9 @@ void HandleButtonInteraction(Clay_ElementId elementId, Clay_PointerData pointerD
|
|||
ButtonData linkButton = (ButtonData) { .link = "https://github.com/nicbarker/clay" };
|
||||
|
||||
// HandleButtonInteraction will be called for each frame the mouse / pointer / touch is inside the button boundaries
|
||||
CLAY({ .layout = { .padding = CLAY_PADDING_ALL(8) } }) {
|
||||
CLAY(CLAY_ID("Button"), { .layout = { .padding = CLAY_PADDING_ALL(8) } }) {
|
||||
Clay_OnHover(HandleButtonInteraction, &buttonData);
|
||||
CLAY_TEXT(CLAY_STRING("Button"), &headerTextConfig);
|
||||
CLAY_TEXT(CLAY_STRING("Click me!"), &headerTextConfig);
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -788,13 +787,40 @@ Returns a [Clay_ElementId](#clay_elementid) for the provided id string, used for
|
|||
**Examples**
|
||||
```C
|
||||
// Define an element with 16px of x and y padding
|
||||
CLAY({ .id = CLAY_ID("Outer"), .layout = { .padding = CLAY_PADDING_ALL(16) } }) {
|
||||
CLAY(CLAY_ID("Outer"), { .layout = { .padding = CLAY_PADDING_ALL(16) } }) {
|
||||
// A nested child element
|
||||
CLAY({ .id = CLAY_ID("SideBar"), .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 16 } }) {
|
||||
CLAY(CLAY_ID("SideBar"), { .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 16 } }) {
|
||||
// Children laid out top to bottom with a 16 px gap between them
|
||||
}
|
||||
// A vertical scrolling container with a colored background
|
||||
CLAY({
|
||||
CLAY(CLAY_ID("ScrollContainer"), {
|
||||
.layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 16 },
|
||||
.backgroundColor = { 200, 200, 100, 255 },
|
||||
.cornerRadius = CLAY_CORNER_RADIUS(10),
|
||||
.clip = { .vertical = true, .childOffset = Clay_GetScrollOffset() }
|
||||
}) {
|
||||
// child elements
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### CLAY_AUTO_ID()
|
||||
|
||||
A version of the core [CLAY()](#clay) element creation macro that generates an ID automatically instead of requiring it as the first argument.
|
||||
|
||||
Note that under the hood this ID is generated in the same way as [CLAY_ID_LOCAL()](#clay_id_local), which is based on the element's position in the hierarchy, and may chance between layout calls if elements are added / removed from the hierarchy before the element is defined. As a result, for transitions & retained mode backends to work correctly, IDs should be specified.
|
||||
|
||||
```C
|
||||
// Note that CLAY_AUTO_ID only takes one argument: the configuration
|
||||
CLAY_AUTO_ID({ .layout = { .padding = CLAY_PADDING_ALL(16) } }) {
|
||||
// A nested child element
|
||||
CLAY_AUTO_ID({ .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 16 } }) {
|
||||
// Children laid out top to bottom with a 16 px gap between them
|
||||
}
|
||||
// A vertical scrolling container with a colored background
|
||||
CLAY_AUTO_ID({
|
||||
.layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 16 },
|
||||
.backgroundColor = { 200, 200, 100, 255 },
|
||||
.cornerRadius = CLAY_CORNER_RADIUS(10),
|
||||
|
|
@ -931,8 +957,7 @@ To regenerate the same ID outside of layout declaration when using utility funct
|
|||
|
||||
```C
|
||||
// Tag a button with the Id "Button"
|
||||
CLAY({
|
||||
.id = CLAY_ID("Button"),
|
||||
CLAY(CLAY_ID("Button"), {
|
||||
.layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { .width = CLAY_SIZING_GROW(0) }, .padding = CLAY_PADDING_ALL(16), .childGap = 16 }
|
||||
}) {
|
||||
// ...children
|
||||
|
|
@ -1065,18 +1090,9 @@ typedef struct {
|
|||
|
||||
**Fields**
|
||||
|
||||
**`.id`** - `Clay_ElementID`
|
||||
|
||||
`CLAY({ .id = CLAY_ID("FileButton") })`
|
||||
|
||||
Uses [Clay_ElementId](#clay_elementid). Tags the element with an ID that can be later used to query data about the element, and gives it a human readable name in the debug tools.
|
||||
IDs are typically generated using the [CLAY_ID](#clay_id), [CLAY_IDI](#clay_idi), [CLAY_ID_LOCAL](#clay_id_local) and [CLAY_IDI_LOCAL](#clay_idi_local) macros.
|
||||
|
||||
---
|
||||
|
||||
**`.layout`** - `Clay_LayoutConfig`
|
||||
|
||||
`CLAY({ .layout = { .padding = { 16, 16, 12, 12 }, .layoutDirection = CLAY_TOP_TO_BOTTOM } })`
|
||||
`CLAY(CLAY_ID("Element"), { .layout = { .padding = { 16, 16, 12, 12 }, .layoutDirection = CLAY_TOP_TO_BOTTOM } })`
|
||||
|
||||
Uses [Clay_LayoutConfig](#clay_layoutconfig). Controls various settings related to _layout_, which can be thought of as "the size and position of this element and its children".
|
||||
|
||||
|
|
@ -1084,7 +1100,7 @@ Uses [Clay_LayoutConfig](#clay_layoutconfig). Controls various settings related
|
|||
|
||||
**`.backgroundColor`** - `Clay_Color`
|
||||
|
||||
`CLAY({ .backgroundColor = {120, 120, 120, 255} } })`
|
||||
`CLAY(CLAY_ID("Element"), { .backgroundColor = {120, 120, 120, 255} } })`
|
||||
|
||||
Uses [Clay_Color](#clay_color). Conventionally accepts `rgba` float values between 0 and 255, but interpretation is left up to the renderer and does not affect layout.
|
||||
|
||||
|
|
@ -1092,7 +1108,7 @@ Uses [Clay_Color](#clay_color). Conventionally accepts `rgba` float values betwe
|
|||
|
||||
**`.cornerRadius`** - `float`
|
||||
|
||||
`CLAY({ .cornerRadius = { .topLeft = 16, .topRight = 16, .bottomLeft = 16, .bottomRight = 16 } })`
|
||||
`CLAY(CLAY_ID("Element"), { .cornerRadius = { .topLeft = 16, .topRight = 16, .bottomLeft = 16, .bottomRight = 16 } })`
|
||||
|
||||
Defines the radius in pixels for the arc of rectangle corners (`0` is square, `rectangle.width / 2` is circular).
|
||||
|
||||
|
|
@ -1102,7 +1118,7 @@ Note that the `CLAY_CORNER_RADIUS(radius)` function-like macro is available to p
|
|||
|
||||
**`.aspectRatio`** - `Clay_AspectRatioElementConfig`
|
||||
|
||||
`CLAY({ .aspectRatio = 1 })`
|
||||
`CLAY(CLAY_ID("Element"), { .aspectRatio = 1 })`
|
||||
|
||||
Uses [Clay_AspectRatioElementConfig](#clay_aspectratioelementconfig). Configures the element as an aspect ratio scaling element. Especially useful for rendering images, but can also be used to enforce a fixed width / height ratio of other elements.
|
||||
|
||||
|
|
@ -1110,7 +1126,7 @@ Uses [Clay_AspectRatioElementConfig](#clay_aspectratioelementconfig). Configures
|
|||
|
||||
**`.image`** - `Clay_ImageElementConfig`
|
||||
|
||||
`CLAY({ .image = { .imageData = &myImage } })`
|
||||
`CLAY(CLAY_ID("Element"), { .image = { .imageData = &myImage } })`
|
||||
|
||||
Uses [Clay_ImageElementConfig](#clay_imageelementconfig). Configures the element as an image element. Causes a render command with type `IMAGE` to be emitted.
|
||||
|
||||
|
|
@ -1118,7 +1134,7 @@ Uses [Clay_ImageElementConfig](#clay_imageelementconfig). Configures the element
|
|||
|
||||
**`.floating`** - `Clay_FloatingElementConfig`
|
||||
|
||||
`CLAY({ .floating = { .attachTo = CLAY_ATTACH_TO_PARENT } })`
|
||||
`CLAY(CLAY_ID("Element"), { .floating = { .attachTo = CLAY_ATTACH_TO_PARENT } })`
|
||||
|
||||
Uses [Clay_FloatingElementConfig](#clay_floatingelementconfig). Configures the element as an floating element, which allows it to stack "in front" and "on top" of other elements without affecting sibling or parent size or position.
|
||||
|
||||
|
|
@ -1126,7 +1142,7 @@ Uses [Clay_FloatingElementConfig](#clay_floatingelementconfig). Configures the e
|
|||
|
||||
**`.custom`** - `Clay_CustomElementConfig`
|
||||
|
||||
`CLAY({ .custom = { .customData = &my3DModel } })`
|
||||
`CLAY(CLAY_ID("Element"), { .custom = { .customData = &my3DModel } })`
|
||||
|
||||
Uses [Clay_CustomElementConfig](#clay_customelementconfig). Configures the element as a custom element, which allows you to pass custom data through to the renderer. Causes a render command with type `CUSTOM` to be emitted.
|
||||
|
||||
|
|
@ -1134,7 +1150,7 @@ Uses [Clay_CustomElementConfig](#clay_customelementconfig). Configures the eleme
|
|||
|
||||
**`.clip`** - `Clay_ClipElementConfig`
|
||||
|
||||
`CLAY({ .clip = { .vertical = true, .childOffset = Clay_GetScrollOffset() } })`
|
||||
`CLAY(CLAY_ID("Element"), { .clip = { .vertical = true, .childOffset = Clay_GetScrollOffset() } })`
|
||||
|
||||
Uses [Clay_ClipElementConfig](#clay_scrollelementconfig). Configures the element as a clip element, which causes child elements to be clipped / masked if they overflow, and together with the functions listed in [Scrolling Elements](#scrolling-elements) enables scrolling of child contents.
|
||||
|
||||
|
|
@ -1144,7 +1160,7 @@ Uses [Clay_ClipElementConfig](#clay_scrollelementconfig). Configures the element
|
|||
|
||||
**`.border`** - `Clay_BorderElementConfig`
|
||||
|
||||
`CLAY({ .border = { .width = { .left = 5 }, .color = COLOR_BLUE } })`
|
||||
`CLAY(CLAY_ID("Element"), { .border = { .width = { .left = 5 }, .color = COLOR_BLUE } })`
|
||||
|
||||
Uses [Clay_BorderElementConfig](#clay_borderelementconfig). Configures the element as a border element, which instructs the renderer to draw coloured border lines along the perimeter of this element's bounding box. Causes a render command with type `BORDER` to be emitted.
|
||||
|
||||
|
|
@ -1152,7 +1168,7 @@ Uses [Clay_BorderElementConfig](#clay_borderelementconfig). Configures the eleme
|
|||
|
||||
**`.userData`** - `void *`
|
||||
|
||||
`CLAY({ .userData = &extraData })`
|
||||
`CLAY(CLAY_ID("Element"), { .userData = &extraData })`
|
||||
|
||||
Transparently passes a pointer through to the corresponding [Clay_RenderCommands](#clay_rendercommand)s generated by this element.
|
||||
|
||||
|
|
@ -1164,13 +1180,13 @@ Transparently passes a pointer through to the corresponding [Clay_RenderCommands
|
|||
// Declare a reusable rectangle config, with a purple color and 10px rounded corners
|
||||
Clay_RectangleElementConfig rectangleConfig = (Clay_RectangleElementConfig) { .color = { 200, 200, 100, 255 }, .cornerRadius = CLAY_CORNER_RADIUS(10) };
|
||||
// Declare a rectangle element using a reusable config
|
||||
CLAY(rectangleConfig)) {}
|
||||
CLAY(CLAY_ID("Box"), rectangleConfig) {}
|
||||
// Declare a retangle element using an inline config
|
||||
CLAY({ .color = { 200, 200, 100, 255 }, .cornerRadius = CLAY_CORNER_RADIUS(10) })) {
|
||||
CLAY(CLAY_ID("BoxInline"), { .color = { 200, 200, 100, 255 }, .cornerRadius = CLAY_CORNER_RADIUS(10) })) {
|
||||
// child elements
|
||||
}
|
||||
// Declare a scrolling container with a colored background
|
||||
CLAY({
|
||||
CLAY(CLAY_ID("ScrollingContainer"), {
|
||||
.backgroundColor = { 200, 200, 100, 255 },
|
||||
.cornerRadius = CLAY_CORNER_RADIUS(10)
|
||||
.clip = { .vertical = true, .childOffset = Clay_GetScrollOffset() }
|
||||
|
|
@ -1210,7 +1226,7 @@ Clay_LayoutConfig {
|
|||
|
||||
**`.layoutDirection`** - `Clay_LayoutDirection`
|
||||
|
||||
`CLAY({ .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM } })`
|
||||
`CLAY(CLAY_ID("Element"), { .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM } })`
|
||||
|
||||
Controls the axis / direction in which child elements are laid out. Available options are `CLAY_LEFT_TO_RIGHT` (default) and `CLAY_TOP_TO_BOTTOM`.
|
||||
|
||||
|
|
@ -1222,7 +1238,7 @@ _Did you know that "left to right" and "top to bottom" both have 13 letters?_
|
|||
|
||||
**`.padding`** - `Clay_Padding`
|
||||
|
||||
`CLAY({ .layout = { .padding = { .left = 16, .right = 16, .top = 8, .bottom = 8 } } })`
|
||||
`CLAY(CLAY_ID("Element"), { .layout = { .padding = { .left = 16, .right = 16, .top = 8, .bottom = 8 } } })`
|
||||
|
||||
Controls white-space "padding" around the **outside** of child elements.
|
||||
|
||||
|
|
@ -1232,7 +1248,7 @@ Controls white-space "padding" around the **outside** of child elements.
|
|||
|
||||
**`.childGap`** - `uint16_t`
|
||||
|
||||
`CLAY({ .layout = { .childGap = 16 } })`
|
||||
`CLAY(CLAY_ID("Element"), { .layout = { .childGap = 16 } })`
|
||||
|
||||
Controls the white-space **between** child elements as they are laid out. When `.layoutDirection` is `CLAY_LEFT_TO_RIGHT` (default), this will be horizontal space, whereas for `CLAY_TOP_TO_BOTTOM` it will be vertical space.
|
||||
|
||||
|
|
@ -1242,7 +1258,7 @@ Controls the white-space **between** child elements as they are laid out. When `
|
|||
|
||||
**`.childAlignment`** - `Clay_ChildAlignment`
|
||||
|
||||
`CLAY({ .layout = { .childAlignment = { .x = CLAY_ALIGN_X_LEFT, .y = CLAY_ALIGN_Y_CENTER } } })`
|
||||
`CLAY(CLAY_ID("Element"), { .layout = { .childAlignment = { .x = CLAY_ALIGN_X_LEFT, .y = CLAY_ALIGN_Y_CENTER } } })`
|
||||
|
||||
Controls the alignment of children relative to the height and width of the parent container. Available options are:
|
||||
```C
|
||||
|
|
@ -1256,7 +1272,7 @@ Controls the alignment of children relative to the height and width of the paren
|
|||
|
||||
**`.sizing`** - `Clay_Sizing`
|
||||
|
||||
`CLAY({ .layout = { .sizing = { .width = CLAY_SIZING_FIXED(300), .height = CLAY_SIZING_PERCENT(0.5) } } })`
|
||||
`CLAY(CLAY_ID("Element"), { .layout = { .sizing = { .width = CLAY_SIZING_FIXED(300), .height = CLAY_SIZING_PERCENT(0.5) } } })`
|
||||
|
||||
Controls how final width and height of element are calculated. The same configurations are available for both the `.width` and `.height` axis. There are several options:
|
||||
|
||||
|
|
@ -1276,7 +1292,7 @@ Controls how final width and height of element are calculated. The same configur
|
|||
**Example Usage**
|
||||
|
||||
```C
|
||||
CLAY({ .id = CLAY_ID("Button"), .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { .width = CLAY_SIZING_GROW(0) }, .padding = CLAY_PADDING_ALL(16, .childGap = 16) } }) {
|
||||
CLAY(CLAY_ID("Button"), { .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { .width = CLAY_SIZING_GROW(0) }, .padding = CLAY_PADDING_ALL(16, .childGap = 16) } }) {
|
||||
// Children will be laid out vertically with 16px of padding around and between
|
||||
}
|
||||
```
|
||||
|
|
@ -1286,7 +1302,7 @@ CLAY({ .id = CLAY_ID("Button"), .layout = { .layoutDirection = CLAY_TOP_TO_BOTTO
|
|||
### Clay_ImageElementConfig
|
||||
**Usage**
|
||||
|
||||
`CLAY({ .image = { ...image config } }) {}`
|
||||
`CLAY(CLAY_ID("Element"), { .image = { ...image config } }) {}`
|
||||
|
||||
**Clay_ImageElementConfig** configures a clay element to render an image as its background.
|
||||
|
||||
|
|
@ -1302,7 +1318,7 @@ Clay_ImageElementConfig {
|
|||
|
||||
**`.imageData`** - `void *`
|
||||
|
||||
`CLAY({ .image = { .imageData = &myImage } }) {}`
|
||||
`CLAY(CLAY_ID("Image"), { .image = { .imageData = &myImage } }) {}`
|
||||
|
||||
`.imageData` is a generic void pointer that can be used to pass through image data to the renderer.
|
||||
|
||||
|
|
@ -1310,7 +1326,7 @@ Clay_ImageElementConfig {
|
|||
// Load an image somewhere in your code
|
||||
YourImage profilePicture = LoadYourImage("profilePicture.png");
|
||||
// Note that when rendering, .imageData will be void* type.
|
||||
CLAY({ .image = { .imageData = &profilePicture } }) {}
|
||||
CLAY(CLAY_ID("Image"), { .image = { .imageData = &profilePicture } }) {}
|
||||
```
|
||||
|
||||
**Examples**
|
||||
|
|
@ -1321,9 +1337,9 @@ YourImage profilePicture = LoadYourImage("profilePicture.png");
|
|||
// Declare a reusable image config
|
||||
Clay_ImageElementConfig imageConfig = (Clay_ImageElementConfig) { .imageData = &profilePicture };
|
||||
// Declare an image element using a reusable config
|
||||
CLAY({ .image = imageConfig }) {}
|
||||
CLAY(CLAY_ID("Image"), { .image = imageConfig }) {}
|
||||
// Declare an image element using an inline config
|
||||
CLAY({ .image = { .imageData = &profilePicture }, .aspectRatio = 16.0 / 9.0 }) {}
|
||||
CLAY(CLAY_ID("ImageInline"), { .image = { .imageData = &profilePicture }, .aspectRatio = 16.0 / 9.0 }) {}
|
||||
// Rendering example
|
||||
YourImage *imageToRender = renderCommand->elementConfig.imageElementConfig->imageData;
|
||||
```
|
||||
|
|
@ -1338,7 +1354,7 @@ Element is subject to [culling](#visibility-culling). Otherwise, a single `Clay_
|
|||
|
||||
**Usage**
|
||||
|
||||
`CLAY({ .aspectRatio = 16.0 / 9.0 }) {}`
|
||||
`CLAY(CLAY_ID("Aspect"), { .aspectRatio = 16.0 / 9.0 }) {}`
|
||||
|
||||
**Clay_AspectRatioElementConfig** configures a clay element to enforce a fixed width / height ratio in its final dimensions. Mostly used for image elements, but can also be used for non image elements.
|
||||
|
||||
|
|
@ -1354,11 +1370,11 @@ Clay_AspectRatioElementConfig {
|
|||
|
||||
**`.aspectRatio`** - `float`
|
||||
|
||||
`CLAY({ .aspectRatio = { .aspectRatio = 16.0 / 9.0 } }) {}`
|
||||
`CLAY(CLAY_ID("Aspect"), { .aspectRatio = { .aspectRatio = 16.0 / 9.0 } }) {}`
|
||||
|
||||
or alternatively, as C will automatically pass the value to the first nested struct field:
|
||||
|
||||
`CLAY({ .aspectRatio = 16.0 / 9.0 }) {}`
|
||||
`CLAY(CLAY_ID("Aspect"), { .aspectRatio = 16.0 / 9.0 }) {}`
|
||||
|
||||
**Examples**
|
||||
|
||||
|
|
@ -1366,7 +1382,7 @@ or alternatively, as C will automatically pass the value to the first nested str
|
|||
// Load an image somewhere in your code
|
||||
YourImage profilePicture = LoadYourImage("profilePicture.png");
|
||||
// Declare an image element that will grow along the X axis while maintaining its original aspect ratio
|
||||
CLAY({
|
||||
CLAY(CLAY_ID("ProfilePicture"), {
|
||||
.layout = { .width = CLAY_SIZING_GROW() },
|
||||
.aspectRatio = profilePicture.width / profilePicture.height,
|
||||
.image = { .imageData = &profilePicture },
|
||||
|
|
@ -1378,7 +1394,7 @@ CLAY({
|
|||
### Clay_ImageElementConfig
|
||||
**Usage**
|
||||
|
||||
`CLAY({ .image = { ...image config } }) {}`
|
||||
`CLAY(CLAY_ID("Image"), { .image = { ...image config } }) {}`
|
||||
|
||||
**Clay_ImageElementConfig** configures a clay element to render an image as its background.
|
||||
|
||||
|
|
@ -1394,7 +1410,7 @@ Clay_ImageElementConfig {
|
|||
|
||||
**`.imageData`** - `void *`
|
||||
|
||||
`CLAY({ .image = { .imageData = &myImage } }) {}`
|
||||
`CLAY(CLAY_ID("Image"), { .image = { .imageData = &myImage } }) {}`
|
||||
|
||||
`.imageData` is a generic void pointer that can be used to pass through image data to the renderer.
|
||||
|
||||
|
|
@ -1402,7 +1418,7 @@ Clay_ImageElementConfig {
|
|||
// Load an image somewhere in your code
|
||||
YourImage profilePicture = LoadYourImage("profilePicture.png");
|
||||
// Note that when rendering, .imageData will be void* type.
|
||||
CLAY({ .image = { .imageData = &profilePicture } }) {}
|
||||
CLAY(CLAY_ID("Image"), { .image = { .imageData = &profilePicture } }) {}
|
||||
```
|
||||
|
||||
Note: for an image to maintain its original aspect ratio when using dynamic scaling, the [.aspectRatio](#clay_aspectratioelementconfig) config option must be used.
|
||||
|
|
@ -1415,9 +1431,9 @@ YourImage profilePicture = LoadYourImage("profilePicture.png");
|
|||
// Declare a reusable image config
|
||||
Clay_ImageElementConfig imageConfig = (Clay_ImageElementConfig) { .imageData = &profilePicture };
|
||||
// Declare an image element using a reusable config
|
||||
CLAY({ .image = imageConfig }) {}
|
||||
CLAY(CLAY_ID("Image"), { .image = imageConfig }) {}
|
||||
// Declare an image element using an inline config
|
||||
CLAY({ .image = { .imageData = &profilePicture }, .aspectRatio = 16.0 / 9.0 }) {}
|
||||
CLAY(CLAY_ID("ImageInline"), { .image = { .imageData = &profilePicture }, .aspectRatio = 16.0 / 9.0 }) {}
|
||||
// Rendering example
|
||||
YourImage *imageToRender = renderCommand->elementConfig.imageElementConfig->imageData;
|
||||
```
|
||||
|
|
@ -1432,7 +1448,7 @@ Element is subject to [culling](#visibility-culling). Otherwise, a single `Clay_
|
|||
|
||||
**Usage**
|
||||
|
||||
`CLAY({ .clip = { ...clip config } }) {}`
|
||||
`CLAY(CLAY_ID("ScrollBox"), { .clip = { ...clip config } }) {}`
|
||||
|
||||
**Notes**
|
||||
|
||||
|
|
@ -1453,7 +1469,7 @@ Clay_ClipElementConfig {
|
|||
|
||||
**`.horizontal`** - `bool`
|
||||
|
||||
`CLAY({ .clip = { .horizontal = true } })`
|
||||
`CLAY(CLAY_ID("HorizontalScroll"), { .clip = { .horizontal = true } })`
|
||||
|
||||
Enables or disables horizontal clipping for this container element.
|
||||
|
||||
|
|
@ -1461,7 +1477,7 @@ Enables or disables horizontal clipping for this container element.
|
|||
|
||||
**`.vertical`** - `bool`
|
||||
|
||||
`CLAY({ .clip = { .vertical = true } })`
|
||||
`CLAY(LAY_ID("VerticalScroll"), { .clip = { .vertical = true } })`
|
||||
|
||||
Enables or disables vertical clipping for this container element.
|
||||
|
||||
|
|
@ -1476,9 +1492,9 @@ Enabling clip for an element will result in two additional render commands:
|
|||
**Examples**
|
||||
|
||||
```C
|
||||
CLAY({ .clip = { .vertical = true } }) {
|
||||
CLAY(CLAY_ID("ScrollOuter"), { .clip = { .vertical = true } }) {
|
||||
// Create child content with a fixed height of 5000
|
||||
CLAY({ .id = CLAY_ID("ScrollInner"), .layout = { .sizing = { .height = CLAY_SIZING_FIXED(5000) } } }) {}
|
||||
CLAY(CLAY_ID("ScrollInner"), { .layout = { .sizing = { .height = CLAY_SIZING_FIXED(5000) } } }) {}
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -1488,7 +1504,7 @@ CLAY({ .clip = { .vertical = true } }) {
|
|||
|
||||
**Usage**
|
||||
|
||||
`CLAY({ .border = { ...border config } }) {}`
|
||||
`CLAY(CLAY_ID("Border"), { .border = { ...border config } }) {}`
|
||||
|
||||
**Notes**
|
||||
|
||||
|
|
@ -1516,7 +1532,7 @@ typedef struct Clay_BorderElementConfig
|
|||
|
||||
**`.color`** - `Clay_Color`
|
||||
|
||||
`CLAY({ .border = { .color = { 255, 0, 0, 255 } } })`
|
||||
`CLAY(CLAY_ID("Border"), { .border = { .color = { 255, 0, 0, 255 } } })`
|
||||
|
||||
Uses [Clay_Color](#clay_color). Specifies the shared color for all borders configured by this element. Conventionally accepts `rgba` float values between 0 and 255, but interpretation is left up to the renderer and does not affect layout.
|
||||
|
||||
|
|
@ -1524,7 +1540,7 @@ Uses [Clay_Color](#clay_color). Specifies the shared color for all borders confi
|
|||
|
||||
**`.width`** - `Clay_BorderWidth`
|
||||
|
||||
`CLAY({ .border = { .width = { .left = 2, .right = 10 } } })`
|
||||
`CLAY(CLAY_ID("Border"), { .border = { .width = { .left = 2, .right = 10 } } })`
|
||||
|
||||
Indicates to the renderer that a border of `.color` should be draw at the specified edges of the bounding box, **inset and overlapping the box contents by `.width`**.
|
||||
|
||||
|
|
@ -1534,7 +1550,7 @@ Note:
|
|||
|
||||
**`.width.betweenChildren`**
|
||||
|
||||
`CLAY({ .border = { .width = { .betweenChildren = 2 } }, .color = COLOR_RED })`
|
||||
`CLAY(CLAY_ID("Border"), { .border = { .width = { .betweenChildren = 2 } }, .color = COLOR_RED })`
|
||||
|
||||
Configures the width and color of borders to be drawn between children. These borders will be vertical lines if the parent uses `.layoutDirection = CLAY_LEFT_TO_RIGHT` and horizontal lines if the parent uses `CLAY_TOP_TO_BOTTOM`. Unlike `.left, .top` etc, this option **will generate additional rectangle render commands representing the borders between children.** As a result, the renderer does not need to specifically implement rendering for these border elements.
|
||||
|
||||
|
|
@ -1544,8 +1560,7 @@ Configures the width and color of borders to be drawn between children. These bo
|
|||
|
||||
```C
|
||||
// 300x300 container with a 1px red border around all the edges
|
||||
CLAY({
|
||||
.id = CLAY_ID("OuterBorder"),
|
||||
CLAY(CLAY_ID("OuterBorder"), {
|
||||
.layout = { .sizing = { .width = CLAY_SIZING_FIXED(300), .height = CLAY_SIZING_FIXED(300) } },
|
||||
.border = { .width = { 1, 1, 1, 1, 0 }, .color = COLOR_RED }
|
||||
}) {
|
||||
|
|
@ -1553,16 +1568,14 @@ CLAY({
|
|||
}
|
||||
|
||||
// Container with a 3px yellow bottom border
|
||||
CLAY({
|
||||
.id = CLAY_ID("OuterBorder"),
|
||||
CLAY(CLAY_ID("OuterBorder"), {
|
||||
.border = { .width = { .bottom = 3 }, .color = COLOR_YELLOW }
|
||||
}) {
|
||||
// ...
|
||||
}
|
||||
|
||||
// Container with a 5px curved border around the edges, and a 5px blue border between all children laid out top to bottom
|
||||
CLAY({
|
||||
.id = CLAY_ID("OuterBorder"),
|
||||
CLAY(CLAY_ID("OuterBorder"), {
|
||||
.layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM },
|
||||
.border = { .width = { 5, 5, 5, 5, 5 }, .color = COLOR_BLUE }
|
||||
}) {
|
||||
|
|
@ -1585,7 +1598,7 @@ Rendering of borders and rounded corners is left up to the user. See the provide
|
|||
|
||||
**Usage**
|
||||
|
||||
`CLAY({ .floating = { ...floating config } }) {}`
|
||||
`CLAY(CLAY_ID("Floating"), { .floating = { ...floating config } }) {}`
|
||||
|
||||
**Notes**
|
||||
|
||||
|
|
@ -1636,7 +1649,7 @@ Clay_FloatingElementConfig {
|
|||
|
||||
**`.offset`** - `Clay_Vector2`
|
||||
|
||||
`CLAY({ .floating = { .offset = { -24, -24 } } })`
|
||||
`CLAY(CLAY_ID("Floating"), { .floating = { .offset = { -24, -24 } } })`
|
||||
|
||||
Used to apply a position offset to the floating container _after_ all other layout has been calculated.
|
||||
|
||||
|
|
@ -1644,7 +1657,7 @@ Used to apply a position offset to the floating container _after_ all other layo
|
|||
|
||||
**`.expand`** - `Clay_Dimensions`
|
||||
|
||||
`CLAY({ .floating = { .expand = { 16, 16 } } })`
|
||||
`CLAY(CLAY_ID("Floating"), { .floating = { .expand = { 16, 16 } } })`
|
||||
|
||||
Used to expand the width and height of the floating container _before_ laying out child elements.
|
||||
|
||||
|
|
@ -1652,7 +1665,7 @@ Used to expand the width and height of the floating container _before_ laying ou
|
|||
|
||||
**`.zIndex`** - `float`
|
||||
|
||||
`CLAY({ .floating = { .zIndex = 1 } })`
|
||||
`CLAY(CLAY_ID("Floating"), { .floating = { .zIndex = 1 } })`
|
||||
|
||||
All floating elements (as well as their entire child hierarchies) will be sorted by `.zIndex` order before being converted to render commands. If render commands are drawn in order, elements with higher `.zIndex` values will be drawn on top.
|
||||
|
||||
|
|
@ -1660,41 +1673,41 @@ All floating elements (as well as their entire child hierarchies) will be sorted
|
|||
|
||||
**`.parentId`** - `uint32_t`
|
||||
|
||||
`CLAY({ .floating = { .parentId = Clay_GetElementId("HeaderButton").id } })`
|
||||
`CLAY(CLAY_ID("Floating"), { .floating = { .parentId = Clay_GetElementId("HeaderButton").id } })`
|
||||
|
||||
By default, floating containers will "attach" to the parent element that they are declared inside. However, there are cases where this limitation could cause significant performance or ergonomics problems. `.parentId` allows you to specify a `CLAY_ID().id` to attach the floating container to. The parent element with the matching id can be declared anywhere in the hierarchy, it doesn't need to be declared before or after the floating container in particular.
|
||||
|
||||
Consider the following case:
|
||||
```C
|
||||
// Load an image somewhere in your code
|
||||
CLAY({ .id = CLAY_IDI("SidebarButton", 1) }) {
|
||||
CLAY(CLAY_IDI("SidebarButton", 1), { }) {
|
||||
// .. some button contents
|
||||
if (tooltip.attachedButtonIndex == 1) {
|
||||
CLAY({ /* floating config... */ })
|
||||
CLAY(CLAY_ID("OptionTooltip"), { /* floating config... */ })
|
||||
}
|
||||
}
|
||||
CLAY({ .id = CLAY_IDI("SidebarButton", 2) }) {
|
||||
CLAY(CLAY_IDI("SidebarButton", 2), { }) {
|
||||
// .. some button contents
|
||||
if (tooltip.attachedButtonIndex == 2) {
|
||||
CLAY({ /* floating config... */ })
|
||||
CLAY(CLAY_ID("OptionTooltip"), { /* floating config... */ })
|
||||
}
|
||||
}
|
||||
CLAY({ .id = CLAY_IDI("SidebarButton", 3) }) {
|
||||
CLAY(CLAY_IDI("SidebarButton", 3), { }) {
|
||||
// .. some button contents
|
||||
if (tooltip.attachedButtonIndex == 3) {
|
||||
CLAY({ /* floating config... */ })
|
||||
CLAY(CLAY_ID("OptionTooltip"), { /* floating config... */ })
|
||||
}
|
||||
}
|
||||
CLAY({ .id = CLAY_IDI("SidebarButton", 4) }) {
|
||||
CLAY(CLAY_IDI("SidebarButton", 4), { }) {
|
||||
// .. some button contents
|
||||
if (tooltip.attachedButtonIndex == 4) {
|
||||
CLAY({ /* floating config... */ })
|
||||
CLAY(CLAY_ID("OptionTooltip"), { /* floating config... */ })
|
||||
}
|
||||
}
|
||||
CLAY({ .id = CLAY_IDI("SidebarButton", 5) }) {
|
||||
CLAY(CLAY_IDI("SidebarButton", 5), { }) {
|
||||
// .. some button contents
|
||||
if (tooltip.attachedButtonIndex == 5) {
|
||||
CLAY({ /* floating config... */ })
|
||||
CLAY(CLAY_ID("OptionTooltip"), { /* floating config... */ })
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
@ -1703,24 +1716,24 @@ The definition of the above UI is significantly polluted by the need to conditio
|
|||
|
||||
```C
|
||||
// Load an image somewhere in your code
|
||||
CLAY({ .id = CLAY_IDI("SidebarButton", 1) }) {
|
||||
CLAY(CLAY_IDI("SidebarButton", 1), { }) {
|
||||
// .. some button contents
|
||||
}
|
||||
CLAY({ .id = CLAY_IDI("SidebarButton", 2) }) {
|
||||
CLAY(CLAY_IDI("SidebarButton", 2), { }) {
|
||||
// .. some button contents
|
||||
}
|
||||
CLAY({ .id = CLAY_IDI("SidebarButton", 3) }) {
|
||||
CLAY(CLAY_IDI("SidebarButton", 3), { }) {
|
||||
// .. some button contents
|
||||
}
|
||||
CLAY({ .id = CLAY_IDI("SidebarButton", 4) }) {
|
||||
CLAY(CLAY_IDI("SidebarButton", 4), { }) {
|
||||
// .. some button contents
|
||||
}
|
||||
CLAY({ .id = CLAY_IDI("SidebarButton", 5) }) {
|
||||
CLAY(CLAY_IDI("SidebarButton", 5), { }) {
|
||||
// .. some button contents
|
||||
}
|
||||
|
||||
// Any other point in the hierarchy
|
||||
CLAY({ .id = CLAY_ID("OptionTooltip"), .floating = { .attachTo = CLAY_ATTACH_TO_ELEMENT_ID, .parentId = CLAY_IDI("SidebarButton", tooltip.attachedButtonIndex).id }) {
|
||||
CLAY(CLAY_ID("OptionTooltip"), { .floating = { .attachTo = CLAY_ATTACH_TO_ELEMENT_ID, .parentId = CLAY_IDI("SidebarButton", tooltip.attachedButtonIndex).id }) {
|
||||
// Tooltip contents...
|
||||
}
|
||||
```
|
||||
|
|
@ -1729,7 +1742,7 @@ CLAY({ .id = CLAY_ID("OptionTooltip"), .floating = { .attachTo = CLAY_ATTACH_TO_
|
|||
|
||||
**`.attachment`** - `Clay_FloatingAttachPoints`
|
||||
|
||||
`CLAY({ .floating = { .attachment = { .element = CLAY_ATTACH_POINT_LEFT_CENTER, .parent = CLAY_ATTACH_POINT_RIGHT_TOP } } }) {}`
|
||||
`CLAY(CLAY_ID("Floating"), { .floating = { .attachment = { .element = CLAY_ATTACH_POINT_LEFT_CENTER, .parent = CLAY_ATTACH_POINT_RIGHT_TOP } } }) {}`
|
||||
|
||||
In terms of positioning the floating container, `.attachment` specifies
|
||||
|
||||
|
|
@ -1744,7 +1757,7 @@ For example:
|
|||
|
||||
"Attach the LEFT_CENTER of the floating container to the RIGHT_TOP of the parent"
|
||||
|
||||
`CLAY({ .floating = { .attachment = { .element = CLAY_ATTACH_POINT_LEFT_CENTER, .parent = CLAY_ATTACH_POINT_RIGHT_TOP } } });`
|
||||
`CLAY(CLAY_ID("Floating"), { .floating = { .attachment = { .element = CLAY_ATTACH_POINT_LEFT_CENTER, .parent = CLAY_ATTACH_POINT_RIGHT_TOP } } });`
|
||||
|
||||

|
||||
|
||||
|
|
@ -1758,31 +1771,31 @@ Controls whether pointer events like hover and click should pass through to cont
|
|||
|
||||
```C
|
||||
// Horizontal container with three option buttons
|
||||
CLAY({ .id = CLAY_ID("OptionsList"), .layout = { childGap = 16 } }) {
|
||||
CLAY({ .id = CLAY_IDI("Option", 1), .layout = { padding = CLAY_PADDING_ALL(16)), .backgroundColor = COLOR_BLUE } }) {
|
||||
CLAY(CLAY_ID("OptionsList"), { .layout = { childGap = 16 } }) {
|
||||
CLAY(CLAY_IDI("Option", 1), { .layout = { padding = CLAY_PADDING_ALL(16)), .backgroundColor = COLOR_BLUE } }) {
|
||||
CLAY_TEXT(CLAY_STRING("Option 1"), CLAY_TEXT_CONFIG());
|
||||
}
|
||||
CLAY({ .id = CLAY_IDI("Option", 2), .layout = { padding = CLAY_PADDING_ALL(16)), .backgroundColor = COLOR_BLUE } }) {
|
||||
CLAY(CLAY_IDI("Option", 2), { .layout = { padding = CLAY_PADDING_ALL(16)), .backgroundColor = COLOR_BLUE } }) {
|
||||
CLAY_TEXT(CLAY_STRING("Option 2"), CLAY_TEXT_CONFIG());
|
||||
// Floating tooltip will attach above the "Option 2" container and not affect widths or positions of other elements
|
||||
CLAY({ .id = CLAY_ID("OptionTooltip"), .floating = { .zIndex = 1, .attachment = { .element = CLAY_ATTACH_POINT_CENTER_BOTTOM, .parent = CLAY_ATTACH_POINT_CENTER_TOP } } }) {
|
||||
CLAY(CLAY_ID("OptionTooltip"), { .floating = { .zIndex = 1, .attachment = { .element = CLAY_ATTACH_POINT_CENTER_BOTTOM, .parent = CLAY_ATTACH_POINT_CENTER_TOP } } }) {
|
||||
CLAY_TEXT(CLAY_STRING("Most popular!"), CLAY_TEXT_CONFIG());
|
||||
}
|
||||
}
|
||||
CLAY({ .id = CLAY_IDI("Option", 3), .layout = { padding = CLAY_PADDING_ALL(16)), .backgroundColor = COLOR_BLUE } }) {
|
||||
CLAY(CLAY_IDI("Option", 3), { .layout = { padding = CLAY_PADDING_ALL(16)), .backgroundColor = COLOR_BLUE } }) {
|
||||
CLAY_TEXT(CLAY_STRING("Option 3"), CLAY_TEXT_CONFIG());
|
||||
}
|
||||
}
|
||||
|
||||
// Floating containers can also be declared elsewhere in a layout, to avoid branching or polluting other UI
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
CLAY({ .id = CLAY_IDI("Option", i + 1) }) {
|
||||
CLAY(CLAY_IDI("Option", i + 1), { }) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
// Note the use of "parentId".
|
||||
// Floating tooltip will attach above the "Option 2" container and not affect widths or positions of other elements
|
||||
CLAY({ .id = CLAY_ID("OptionTooltip"), .floating = { .parentId = CLAY_IDI("Option", 2).id, .zIndex = 1, .attachment = { .element = CLAY_ATTACH_POINT_CENTER_BOTTOM, .parent = CLAY_ATTACH_POINT_TOP_CENTER } } }) {
|
||||
CLAY(CLAY_ID("OptionTooltip"), { .floating = { .parentId = CLAY_IDI("Option", 2).id, .zIndex = 1, .attachment = { .element = CLAY_ATTACH_POINT_CENTER_BOTTOM, .parent = CLAY_ATTACH_POINT_TOP_CENTER } } }) {
|
||||
CLAY_TEXT(CLAY_STRING("Most popular!"), CLAY_TEXT_CONFIG());
|
||||
}
|
||||
```
|
||||
|
|
@ -1799,7 +1812,7 @@ When using `.parentId`, the floating container can be declared anywhere after `B
|
|||
|
||||
**Usage**
|
||||
|
||||
`CLAY({ .custom = { .customData = &something } }) {}`
|
||||
`CLAY(CLAY_ID("Custom"), { .custom = { .customData = &something } }) {}`
|
||||
|
||||
**Notes**
|
||||
|
||||
|
|
@ -1858,7 +1871,7 @@ CLAY(0) {
|
|||
CustomElementData *modelData = (CustomElementData *)(frameArena.memory + frameArena.offset);
|
||||
*modelData = (CustomElementData) { .type = CUSTOM_ELEMENT_TYPE_MODEL, .model = myModel };
|
||||
frameArena.offset += sizeof(CustomElementData);
|
||||
CLAY({ .custom = { .customData = modelData } }) {}
|
||||
CLAY(CLAY_ID("3DModelViewer"), { .custom = { .customData = modelData } }) {}
|
||||
}
|
||||
|
||||
// Later during your rendering
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
12
clay.h
12
clay.h
|
|
@ -1146,6 +1146,7 @@ typedef struct {
|
|||
Clay_LayoutConfig *layoutConfig;
|
||||
Clay__ElementConfigArraySlice elementConfigs;
|
||||
uint32_t id;
|
||||
uint16_t floatingChildrenCount;
|
||||
} Clay_LayoutElement;
|
||||
|
||||
CLAY__ARRAY_DEFINE(Clay_LayoutElement, Clay_LayoutElementArray)
|
||||
|
|
@ -1776,7 +1777,8 @@ Clay_LayoutElementHashMapItem *Clay__GetHashMapItem(uint32_t id) {
|
|||
Clay_ElementId Clay__GenerateIdForAnonymousElement(Clay_LayoutElement *openLayoutElement) {
|
||||
Clay_Context* context = Clay_GetCurrentContext();
|
||||
Clay_LayoutElement *parentElement = Clay_LayoutElementArray_Get(&context->layoutElements, Clay__int32_tArray_GetValue(&context->openLayoutElementStack, context->openLayoutElementStack.length - 2));
|
||||
Clay_ElementId elementId = Clay__HashNumber(parentElement->childrenOrTextContent.children.length, parentElement->id);
|
||||
uint32_t offset = parentElement->childrenOrTextContent.children.length + parentElement->floatingChildrenCount;
|
||||
Clay_ElementId elementId = Clay__HashNumber(offset, parentElement->id);
|
||||
openLayoutElement->id = elementId.id;
|
||||
Clay__AddHashMapItem(elementId, openLayoutElement);
|
||||
Clay__StringArray_Add(&context->layoutElementIdStrings, elementId.stringId);
|
||||
|
|
@ -1917,9 +1919,15 @@ void Clay__CloseElement(void) {
|
|||
|
||||
// Close the currently open element
|
||||
int32_t closingElementIndex = Clay__int32_tArray_RemoveSwapback(&context->openLayoutElementStack, (int)context->openLayoutElementStack.length - 1);
|
||||
|
||||
// Get the currently open parent
|
||||
openLayoutElement = Clay__GetOpenLayoutElement();
|
||||
|
||||
if (!elementIsFloating && context->openLayoutElementStack.length > 1) {
|
||||
if (context->openLayoutElementStack.length > 1) {
|
||||
if(elementIsFloating) {
|
||||
openLayoutElement->floatingChildrenCount++;
|
||||
return;
|
||||
}
|
||||
openLayoutElement->childrenOrTextContent.children.length++;
|
||||
Clay__int32_tArray_Add(&context->layoutElementChildrenBuffer, closingElementIndex);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,5 +15,5 @@ mkdir -p build/clay \
|
|||
-Wl,--initial-memory=6553600 \
|
||||
-o build/clay/index.wasm \
|
||||
main.c \
|
||||
&& cp index.html build/clay/index.html && cp -r fonts/ build/clay/fonts \
|
||||
&& cp index.html build/clay/index.html && cp -r images/ build/clay/images
|
||||
&& cp index.html build/index.html && cp -r fonts/ build/clay/fonts \
|
||||
&& cp -r images/ build/clay/images
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ set(FETCHCONTENT_QUIET FALSE)
|
|||
FetchContent_Declare(
|
||||
termbox2
|
||||
GIT_REPOSITORY "https://github.com/termbox/termbox2.git"
|
||||
GIT_TAG "9c9281a9a4c971a2be57f8645e828ec99fd555e8"
|
||||
GIT_TAG "ffd159c2a6106dd5eef338a6702ad15d4d4aa809"
|
||||
GIT_PROGRESS TRUE
|
||||
GIT_SHALLOW TRUE
|
||||
)
|
||||
|
|
@ -17,7 +17,7 @@ FetchContent_MakeAvailable(termbox2)
|
|||
FetchContent_Declare(
|
||||
stb
|
||||
GIT_REPOSITORY "https://github.com/nothings/stb.git"
|
||||
GIT_TAG "f58f558c120e9b32c217290b80bad1a0729fbb2c"
|
||||
GIT_TAG "fede005abaf93d9d7f3a679d1999b2db341b360f"
|
||||
GIT_PROGRESS TRUE
|
||||
GIT_SHALLOW TRUE
|
||||
)
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ void component_text_pair(const char *key, const char *value)
|
|||
|
||||
void component_termbox_settings(void)
|
||||
{
|
||||
CLAY_AUTO_ID({
|
||||
CLAY(CLAY_ID("Termbox Settings"), {
|
||||
.floating = {
|
||||
.attachTo = CLAY_ATTACH_TO_PARENT,
|
||||
.zIndex = 1,
|
||||
|
|
@ -509,13 +509,18 @@ Clay_RenderCommandArray CreateLayout(clay_tb_image *image1, clay_tb_image *image
|
|||
{
|
||||
Clay_BeginLayout();
|
||||
CLAY_AUTO_ID({
|
||||
.clip = {
|
||||
.vertical = false,
|
||||
.horizontal = true,
|
||||
.childOffset = Clay_GetScrollOffset(),
|
||||
},
|
||||
.layout = {
|
||||
.sizing = {
|
||||
.width = CLAY_SIZING_GROW(),
|
||||
.height = CLAY_SIZING_GROW()
|
||||
},
|
||||
.childAlignment = {
|
||||
.x = CLAY_ALIGN_X_CENTER,
|
||||
.x = CLAY_ALIGN_X_LEFT,
|
||||
.y = CLAY_ALIGN_Y_CENTER
|
||||
},
|
||||
.childGap = 64
|
||||
|
|
@ -714,12 +719,12 @@ void handle_termbox_events(void)
|
|||
break;
|
||||
}
|
||||
case TB_KEY_MOUSE_WHEEL_UP: {
|
||||
Clay_Vector2 scrollDelta = { 0, 1 * Clay_Termbox_Cell_Height() };
|
||||
Clay_Vector2 scrollDelta = { 0.5 * Clay_Termbox_Cell_Width(), 0 };
|
||||
Clay_UpdateScrollContainers(false, scrollDelta, 1);
|
||||
break;
|
||||
}
|
||||
case TB_KEY_MOUSE_WHEEL_DOWN: {
|
||||
Clay_Vector2 scrollDelta = { 0, -1 * Clay_Termbox_Cell_Height() };
|
||||
Clay_Vector2 scrollDelta = { -0.5 * Clay_Termbox_Cell_Width(), 0 };
|
||||
Clay_UpdateScrollContainers(false, scrollDelta, 1);
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ set(FETCHCONTENT_QUIET FALSE)
|
|||
FetchContent_Declare(
|
||||
termbox2
|
||||
GIT_REPOSITORY "https://github.com/termbox/termbox2.git"
|
||||
GIT_TAG "9c9281a9a4c971a2be57f8645e828ec99fd555e8"
|
||||
GIT_TAG "ffd159c2a6106dd5eef338a6702ad15d4d4aa809"
|
||||
GIT_PROGRESS TRUE
|
||||
GIT_SHALLOW TRUE
|
||||
)
|
||||
|
|
@ -19,7 +19,7 @@ FetchContent_MakeAvailable(termbox2)
|
|||
FetchContent_Declare(
|
||||
stb
|
||||
GIT_REPOSITORY "https://github.com/nothings/stb.git"
|
||||
GIT_TAG "f58f558c120e9b32c217290b80bad1a0729fbb2c"
|
||||
GIT_TAG "fede005abaf93d9d7f3a679d1999b2db341b360f"
|
||||
GIT_PROGRESS TRUE
|
||||
GIT_SHALLOW TRUE
|
||||
)
|
||||
|
|
|
|||
0
generator/__init__.py
Normal file
0
generator/__init__.py
Normal file
83
generator/cli.py
Normal file
83
generator/cli.py
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
import argparse
|
||||
import logging
|
||||
import json
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from generators.base_generator import BaseGenerator
|
||||
from generators.odin_generator import OdinGenerator
|
||||
from parser import parse_headers
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
GeneratorMap = dict[str, type[BaseGenerator]]
|
||||
GENERATORS = {
|
||||
'odin': OdinGenerator,
|
||||
}
|
||||
|
||||
def main() -> None:
|
||||
arg_parser = argparse.ArgumentParser(description='Generate clay bindings')
|
||||
|
||||
# Directories
|
||||
arg_parser.add_argument('input_files', nargs='+', type=str, help='Input header files')
|
||||
arg_parser.add_argument('--output-dir', type=str, help='Output directory', required=True)
|
||||
arg_parser.add_argument('--tmp-dir', type=str, help='Temporary directory')
|
||||
|
||||
# Generators
|
||||
arg_parser.add_argument('--generator', type=str, choices=list(GENERATORS.keys()), help='Generators to run', required=True)
|
||||
|
||||
# Logging
|
||||
arg_parser.add_argument('--verbose', action='store_true', help='Verbose logging')
|
||||
|
||||
args = arg_parser.parse_args()
|
||||
|
||||
log_formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
||||
log_handler = logging.StreamHandler()
|
||||
log_handler.setFormatter(log_formatter)
|
||||
if args.verbose:
|
||||
logging.basicConfig(level=logging.DEBUG, handlers=[log_handler])
|
||||
else:
|
||||
logging.basicConfig(level=logging.INFO, handlers=[log_handler])
|
||||
|
||||
output_dir = Path(args.output_dir)
|
||||
output_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
if args.tmp_dir:
|
||||
tmp_dir = Path(args.tmp_dir)
|
||||
else:
|
||||
tmp_dir = output_dir / 'tmp'
|
||||
tmp_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
fake_libc_include_path = Path(__file__).parent / 'fake_libc_include'
|
||||
input_files = list(fake_libc_include_path.glob('*.h')) + [Path(f) for f in args.input_files]
|
||||
|
||||
logger.info(f'Input files: {input_files}')
|
||||
logger.info(f'Output directory: {output_dir}')
|
||||
logger.info(f'Temporary directory: {tmp_dir}')
|
||||
logger.info(f'Generator: {args.generator}')
|
||||
|
||||
logger.info('Parsing headers')
|
||||
extracted_symbols = parse_headers(input_files, tmp_dir)
|
||||
with open(tmp_dir / 'extracted_symbols.json', 'w') as f:
|
||||
f.write(json.dumps({
|
||||
'structs': extracted_symbols.structs,
|
||||
'enums': extracted_symbols.enums,
|
||||
'functions': extracted_symbols.functions,
|
||||
}, indent=2))
|
||||
|
||||
logger.info('Generating bindings')
|
||||
generator = GENERATORS[args.generator](extracted_symbols)
|
||||
generator.generate()
|
||||
logger.debug(f'Generated bindings:')
|
||||
# for file_name, content in generator.get_outputs().items():
|
||||
# logger.debug(f'{file_name}:')
|
||||
# logger.debug(content)
|
||||
# logger.debug('\n')
|
||||
|
||||
tmp_outputs_dir = tmp_dir / 'generated'
|
||||
tmp_outputs_dir.mkdir(parents=True, exist_ok=True)
|
||||
generator.write_outputs(tmp_outputs_dir)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
262
generator/fake_libc_include/_fake_defines.h
Normal file
262
generator/fake_libc_include/_fake_defines.h
Normal file
|
|
@ -0,0 +1,262 @@
|
|||
#ifndef _FAKE_DEFINES_H
|
||||
#define _FAKE_DEFINES_H
|
||||
|
||||
#define NULL 0
|
||||
#define BUFSIZ 1024
|
||||
#define FOPEN_MAX 20
|
||||
#define FILENAME_MAX 1024
|
||||
|
||||
#ifndef SEEK_SET
|
||||
#define SEEK_SET 0 /* set file offset to offset */
|
||||
#endif
|
||||
#ifndef SEEK_CUR
|
||||
#define SEEK_CUR 1 /* set file offset to current plus offset */
|
||||
#endif
|
||||
#ifndef SEEK_END
|
||||
#define SEEK_END 2 /* set file offset to EOF plus offset */
|
||||
#endif
|
||||
|
||||
#define __LITTLE_ENDIAN 1234
|
||||
#define LITTLE_ENDIAN __LITTLE_ENDIAN
|
||||
#define __BIG_ENDIAN 4321
|
||||
#define BIG_ENDIAN __BIG_ENDIAN
|
||||
#define __BYTE_ORDER __LITTLE_ENDIAN
|
||||
#define BYTE_ORDER __BYTE_ORDER
|
||||
|
||||
#define EXIT_FAILURE 1
|
||||
#define EXIT_SUCCESS 0
|
||||
|
||||
#define SCHAR_MIN -128
|
||||
#define SCHAR_MAX 127
|
||||
#define CHAR_MIN -128
|
||||
#define CHAR_MAX 127
|
||||
#define UCHAR_MAX 255
|
||||
#define SHRT_MIN -32768
|
||||
#define SHRT_MAX 32767
|
||||
#define USHRT_MAX 65535
|
||||
#define INT_MIN -2147483648
|
||||
#define INT_MAX 2147483647
|
||||
#define UINT_MAX 4294967295U
|
||||
#define LONG_MIN -9223372036854775808L
|
||||
#define LONG_MAX 9223372036854775807L
|
||||
#define ULONG_MAX 18446744073709551615UL
|
||||
#define RAND_MAX 32767
|
||||
|
||||
/* C99 inttypes.h defines */
|
||||
#define PRId8 "d"
|
||||
#define PRIi8 "i"
|
||||
#define PRIo8 "o"
|
||||
#define PRIu8 "u"
|
||||
#define PRIx8 "x"
|
||||
#define PRIX8 "X"
|
||||
#define PRId16 "d"
|
||||
#define PRIi16 "i"
|
||||
#define PRIo16 "o"
|
||||
#define PRIu16 "u"
|
||||
#define PRIx16 "x"
|
||||
#define PRIX16 "X"
|
||||
#define PRId32 "d"
|
||||
#define PRIi32 "i"
|
||||
#define PRIo32 "o"
|
||||
#define PRIu32 "u"
|
||||
#define PRIx32 "x"
|
||||
#define PRIX32 "X"
|
||||
#define PRId64 "d"
|
||||
#define PRIi64 "i"
|
||||
#define PRIo64 "o"
|
||||
#define PRIu64 "u"
|
||||
#define PRIx64 "x"
|
||||
#define PRIX64 "X"
|
||||
#define PRIdLEAST8 "d"
|
||||
#define PRIiLEAST8 "i"
|
||||
#define PRIoLEAST8 "o"
|
||||
#define PRIuLEAST8 "u"
|
||||
#define PRIxLEAST8 "x"
|
||||
#define PRIXLEAST8 "X"
|
||||
#define PRIdLEAST16 "d"
|
||||
#define PRIiLEAST16 "i"
|
||||
#define PRIoLEAST16 "o"
|
||||
#define PRIuLEAST16 "u"
|
||||
#define PRIxLEAST16 "x"
|
||||
#define PRIXLEAST16 "X"
|
||||
#define PRIdLEAST32 "d"
|
||||
#define PRIiLEAST32 "i"
|
||||
#define PRIoLEAST32 "o"
|
||||
#define PRIuLEAST32 "u"
|
||||
#define PRIxLEAST32 "x"
|
||||
#define PRIXLEAST32 "X"
|
||||
#define PRIdLEAST64 "d"
|
||||
#define PRIiLEAST64 "i"
|
||||
#define PRIoLEAST64 "o"
|
||||
#define PRIuLEAST64 "u"
|
||||
#define PRIxLEAST64 "x"
|
||||
#define PRIXLEAST64 "X"
|
||||
#define PRIdFAST8 "d"
|
||||
#define PRIiFAST8 "i"
|
||||
#define PRIoFAST8 "o"
|
||||
#define PRIuFAST8 "u"
|
||||
#define PRIxFAST8 "x"
|
||||
#define PRIXFAST8 "X"
|
||||
#define PRIdFAST16 "d"
|
||||
#define PRIiFAST16 "i"
|
||||
#define PRIoFAST16 "o"
|
||||
#define PRIuFAST16 "u"
|
||||
#define PRIxFAST16 "x"
|
||||
#define PRIXFAST16 "X"
|
||||
#define PRIdFAST32 "d"
|
||||
#define PRIiFAST32 "i"
|
||||
#define PRIoFAST32 "o"
|
||||
#define PRIuFAST32 "u"
|
||||
#define PRIxFAST32 "x"
|
||||
#define PRIXFAST32 "X"
|
||||
#define PRIdFAST64 "d"
|
||||
#define PRIiFAST64 "i"
|
||||
#define PRIoFAST64 "o"
|
||||
#define PRIuFAST64 "u"
|
||||
#define PRIxFAST64 "x"
|
||||
#define PRIXFAST64 "X"
|
||||
#define PRIdPTR "d"
|
||||
#define PRIiPTR "i"
|
||||
#define PRIoPTR "o"
|
||||
#define PRIuPTR "u"
|
||||
#define PRIxPTR "x"
|
||||
#define PRIXPTR "X"
|
||||
#define PRIdMAX "d"
|
||||
#define PRIiMAX "i"
|
||||
#define PRIoMAX "o"
|
||||
#define PRIuMAX "u"
|
||||
#define PRIxMAX "x"
|
||||
#define PRIXMAX "X"
|
||||
#define SCNd8 "d"
|
||||
#define SCNi8 "i"
|
||||
#define SCNo8 "o"
|
||||
#define SCNu8 "u"
|
||||
#define SCNx8 "x"
|
||||
#define SCNd16 "d"
|
||||
#define SCNi16 "i"
|
||||
#define SCNo16 "o"
|
||||
#define SCNu16 "u"
|
||||
#define SCNx16 "x"
|
||||
#define SCNd32 "d"
|
||||
#define SCNi32 "i"
|
||||
#define SCNo32 "o"
|
||||
#define SCNu32 "u"
|
||||
#define SCNx32 "x"
|
||||
#define SCNd64 "d"
|
||||
#define SCNi64 "i"
|
||||
#define SCNo64 "o"
|
||||
#define SCNu64 "u"
|
||||
#define SCNx64 "x"
|
||||
#define SCNdLEAST8 "d"
|
||||
#define SCNiLEAST8 "i"
|
||||
#define SCNoLEAST8 "o"
|
||||
#define SCNuLEAST8 "u"
|
||||
#define SCNxLEAST8 "x"
|
||||
#define SCNdLEAST16 "d"
|
||||
#define SCNiLEAST16 "i"
|
||||
#define SCNoLEAST16 "o"
|
||||
#define SCNuLEAST16 "u"
|
||||
#define SCNxLEAST16 "x"
|
||||
#define SCNdLEAST32 "d"
|
||||
#define SCNiLEAST32 "i"
|
||||
#define SCNoLEAST32 "o"
|
||||
#define SCNuLEAST32 "u"
|
||||
#define SCNxLEAST32 "x"
|
||||
#define SCNdLEAST64 "d"
|
||||
#define SCNiLEAST64 "i"
|
||||
#define SCNoLEAST64 "o"
|
||||
#define SCNuLEAST64 "u"
|
||||
#define SCNxLEAST64 "x"
|
||||
#define SCNdFAST8 "d"
|
||||
#define SCNiFAST8 "i"
|
||||
#define SCNoFAST8 "o"
|
||||
#define SCNuFAST8 "u"
|
||||
#define SCNxFAST8 "x"
|
||||
#define SCNdFAST16 "d"
|
||||
#define SCNiFAST16 "i"
|
||||
#define SCNoFAST16 "o"
|
||||
#define SCNuFAST16 "u"
|
||||
#define SCNxFAST16 "x"
|
||||
#define SCNdFAST32 "d"
|
||||
#define SCNiFAST32 "i"
|
||||
#define SCNoFAST32 "o"
|
||||
#define SCNuFAST32 "u"
|
||||
#define SCNxFAST32 "x"
|
||||
#define SCNdFAST64 "d"
|
||||
#define SCNiFAST64 "i"
|
||||
#define SCNoFAST64 "o"
|
||||
#define SCNuFAST64 "u"
|
||||
#define SCNxFAST64 "x"
|
||||
#define SCNdPTR "d"
|
||||
#define SCNiPTR "i"
|
||||
#define SCNoPTR "o"
|
||||
#define SCNuPTR "u"
|
||||
#define SCNxPTR "x"
|
||||
#define SCNdMAX "d"
|
||||
#define SCNiMAX "i"
|
||||
#define SCNoMAX "o"
|
||||
#define SCNuMAX "u"
|
||||
#define SCNxMAX "x"
|
||||
|
||||
/* C99 stdbool.h defines */
|
||||
#define __bool_true_false_are_defined 1
|
||||
#define false 0
|
||||
#define true 1
|
||||
|
||||
/* va_arg macros and type*/
|
||||
#define va_start(_ap, _type) __builtin_va_start((_ap))
|
||||
#define va_arg(_ap, _type) __builtin_va_arg((_ap))
|
||||
#define va_end(_list)
|
||||
|
||||
/* Vectors */
|
||||
#define __m128 int
|
||||
#define __m128_u int
|
||||
#define __m128d int
|
||||
#define __m128d_u int
|
||||
#define __m128i int
|
||||
#define __m128i_u int
|
||||
#define __m256 int
|
||||
#define __m256_u int
|
||||
#define __m256d int
|
||||
#define __m256d_u int
|
||||
#define __m256i int
|
||||
#define __m256i_u int
|
||||
#define __m512 int
|
||||
#define __m512_u int
|
||||
#define __m512d int
|
||||
#define __m512d_u int
|
||||
#define __m512i int
|
||||
#define __m512i_u int
|
||||
|
||||
/* C11 stdnoreturn.h defines */
|
||||
#define __noreturn_is_defined 1
|
||||
#define noreturn _Noreturn
|
||||
|
||||
/* C11 threads.h defines */
|
||||
#define thread_local _Thread_local
|
||||
|
||||
/* C11 assert.h defines */
|
||||
#define static_assert _Static_assert
|
||||
|
||||
/* C11 stdatomic.h defines */
|
||||
#define ATOMIC_BOOL_LOCK_FREE 0
|
||||
#define ATOMIC_CHAR_LOCK_FREE 0
|
||||
#define ATOMIC_CHAR16_T_LOCK_FREE 0
|
||||
#define ATOMIC_CHAR32_T_LOCK_FREE 0
|
||||
#define ATOMIC_WCHAR_T_LOCK_FREE 0
|
||||
#define ATOMIC_SHORT_LOCK_FREE 0
|
||||
#define ATOMIC_INT_LOCK_FREE 0
|
||||
#define ATOMIC_LONG_LOCK_FREE 0
|
||||
#define ATOMIC_LLONG_LOCK_FREE 0
|
||||
#define ATOMIC_POINTER_LOCK_FREE 0
|
||||
#define ATOMIC_VAR_INIT(value) (value)
|
||||
#define ATOMIC_FLAG_INIT { 0 }
|
||||
#define kill_dependency(y) (y)
|
||||
|
||||
/* C11 stdalign.h defines */
|
||||
#define alignas _Alignas
|
||||
#define alignof _Alignof
|
||||
#define __alignas_is_defined 1
|
||||
#define __alignof_is_defined 1
|
||||
|
||||
#endif
|
||||
222
generator/fake_libc_include/_fake_typedefs.h
Normal file
222
generator/fake_libc_include/_fake_typedefs.h
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
#ifndef _FAKE_TYPEDEFS_H
|
||||
#define _FAKE_TYPEDEFS_H
|
||||
|
||||
typedef int size_t;
|
||||
typedef int __builtin_va_list;
|
||||
typedef int __gnuc_va_list;
|
||||
typedef int va_list;
|
||||
typedef int __int8_t;
|
||||
typedef int __uint8_t;
|
||||
typedef int __int16_t;
|
||||
typedef int __uint16_t;
|
||||
typedef int __int_least16_t;
|
||||
typedef int __uint_least16_t;
|
||||
typedef int __int32_t;
|
||||
typedef int __uint32_t;
|
||||
typedef int __int64_t;
|
||||
typedef int __uint64_t;
|
||||
typedef int __int_least32_t;
|
||||
typedef int __uint_least32_t;
|
||||
typedef int __s8;
|
||||
typedef int __u8;
|
||||
typedef int __s16;
|
||||
typedef int __u16;
|
||||
typedef int __s32;
|
||||
typedef int __u32;
|
||||
typedef int __s64;
|
||||
typedef int __u64;
|
||||
typedef int _LOCK_T;
|
||||
typedef int _LOCK_RECURSIVE_T;
|
||||
typedef int _off_t;
|
||||
typedef int __dev_t;
|
||||
typedef int __uid_t;
|
||||
typedef int __gid_t;
|
||||
typedef int _off64_t;
|
||||
typedef int _fpos_t;
|
||||
typedef int _ssize_t;
|
||||
typedef int wint_t;
|
||||
typedef int _mbstate_t;
|
||||
typedef int _flock_t;
|
||||
typedef int _iconv_t;
|
||||
typedef int __ULong;
|
||||
typedef int __FILE;
|
||||
typedef int ptrdiff_t;
|
||||
typedef int wchar_t;
|
||||
typedef int char16_t;
|
||||
typedef int char32_t;
|
||||
typedef int __off_t;
|
||||
typedef int __pid_t;
|
||||
typedef int __loff_t;
|
||||
typedef int u_char;
|
||||
typedef int u_short;
|
||||
typedef int u_int;
|
||||
typedef int u_long;
|
||||
typedef int ushort;
|
||||
typedef int uint;
|
||||
typedef int clock_t;
|
||||
typedef int time_t;
|
||||
typedef int daddr_t;
|
||||
typedef int caddr_t;
|
||||
typedef int ino_t;
|
||||
typedef int off_t;
|
||||
typedef int dev_t;
|
||||
typedef int uid_t;
|
||||
typedef int gid_t;
|
||||
typedef int pid_t;
|
||||
typedef int key_t;
|
||||
typedef int ssize_t;
|
||||
typedef int mode_t;
|
||||
typedef int nlink_t;
|
||||
typedef int fd_mask;
|
||||
typedef int _types_fd_set;
|
||||
typedef int clockid_t;
|
||||
typedef int timer_t;
|
||||
typedef int useconds_t;
|
||||
typedef int suseconds_t;
|
||||
typedef int FILE;
|
||||
typedef int fpos_t;
|
||||
typedef int cookie_read_function_t;
|
||||
typedef int cookie_write_function_t;
|
||||
typedef int cookie_seek_function_t;
|
||||
typedef int cookie_close_function_t;
|
||||
typedef int cookie_io_functions_t;
|
||||
typedef int div_t;
|
||||
typedef int ldiv_t;
|
||||
typedef int lldiv_t;
|
||||
typedef int sigset_t;
|
||||
typedef int __sigset_t;
|
||||
typedef int _sig_func_ptr;
|
||||
typedef int sig_atomic_t;
|
||||
typedef int __tzrule_type;
|
||||
typedef int __tzinfo_type;
|
||||
typedef int mbstate_t;
|
||||
typedef int sem_t;
|
||||
typedef int pthread_t;
|
||||
typedef int pthread_attr_t;
|
||||
typedef int pthread_mutex_t;
|
||||
typedef int pthread_mutexattr_t;
|
||||
typedef int pthread_cond_t;
|
||||
typedef int pthread_condattr_t;
|
||||
typedef int pthread_key_t;
|
||||
typedef int pthread_once_t;
|
||||
typedef int pthread_rwlock_t;
|
||||
typedef int pthread_rwlockattr_t;
|
||||
typedef int pthread_spinlock_t;
|
||||
typedef int pthread_barrier_t;
|
||||
typedef int pthread_barrierattr_t;
|
||||
typedef int jmp_buf;
|
||||
typedef int rlim_t;
|
||||
typedef int sa_family_t;
|
||||
typedef int sigjmp_buf;
|
||||
typedef int stack_t;
|
||||
typedef int siginfo_t;
|
||||
typedef int z_stream;
|
||||
|
||||
/* C99 exact-width integer types */
|
||||
typedef int int8_t;
|
||||
typedef int uint8_t;
|
||||
typedef int int16_t;
|
||||
typedef int uint16_t;
|
||||
typedef int int32_t;
|
||||
typedef int uint32_t;
|
||||
typedef int int64_t;
|
||||
typedef int uint64_t;
|
||||
|
||||
/* C99 minimum-width integer types */
|
||||
typedef int int_least8_t;
|
||||
typedef int uint_least8_t;
|
||||
typedef int int_least16_t;
|
||||
typedef int uint_least16_t;
|
||||
typedef int int_least32_t;
|
||||
typedef int uint_least32_t;
|
||||
typedef int int_least64_t;
|
||||
typedef int uint_least64_t;
|
||||
|
||||
/* C99 fastest minimum-width integer types */
|
||||
typedef int int_fast8_t;
|
||||
typedef int uint_fast8_t;
|
||||
typedef int int_fast16_t;
|
||||
typedef int uint_fast16_t;
|
||||
typedef int int_fast32_t;
|
||||
typedef int uint_fast32_t;
|
||||
typedef int int_fast64_t;
|
||||
typedef int uint_fast64_t;
|
||||
|
||||
/* C99 integer types capable of holding object pointers */
|
||||
typedef int intptr_t;
|
||||
typedef int uintptr_t;
|
||||
|
||||
/* C99 greatest-width integer types */
|
||||
typedef int intmax_t;
|
||||
typedef int uintmax_t;
|
||||
|
||||
/* C99 stdbool.h bool type. _Bool is built-in in C99 */
|
||||
typedef _Bool bool;
|
||||
|
||||
/* Mir typedefs */
|
||||
typedef void* MirEGLNativeWindowType;
|
||||
typedef void* MirEGLNativeDisplayType;
|
||||
typedef struct MirConnection MirConnection;
|
||||
typedef struct MirSurface MirSurface;
|
||||
typedef struct MirSurfaceSpec MirSurfaceSpec;
|
||||
typedef struct MirScreencast MirScreencast;
|
||||
typedef struct MirPromptSession MirPromptSession;
|
||||
typedef struct MirBufferStream MirBufferStream;
|
||||
typedef struct MirPersistentId MirPersistentId;
|
||||
typedef struct MirBlob MirBlob;
|
||||
typedef struct MirDisplayConfig MirDisplayConfig;
|
||||
|
||||
/* xcb typedefs */
|
||||
typedef struct xcb_connection_t xcb_connection_t;
|
||||
typedef uint32_t xcb_window_t;
|
||||
typedef uint32_t xcb_visualid_t;
|
||||
|
||||
/* C11 stdatomic.h types */
|
||||
typedef _Atomic(_Bool) atomic_bool;
|
||||
typedef _Atomic(char) atomic_char;
|
||||
typedef _Atomic(signed char) atomic_schar;
|
||||
typedef _Atomic(unsigned char) atomic_uchar;
|
||||
typedef _Atomic(short) atomic_short;
|
||||
typedef _Atomic(unsigned short) atomic_ushort;
|
||||
typedef _Atomic(int) atomic_int;
|
||||
typedef _Atomic(unsigned int) atomic_uint;
|
||||
typedef _Atomic(long) atomic_long;
|
||||
typedef _Atomic(unsigned long) atomic_ulong;
|
||||
typedef _Atomic(long long) atomic_llong;
|
||||
typedef _Atomic(unsigned long long) atomic_ullong;
|
||||
typedef _Atomic(uint_least16_t) atomic_char16_t;
|
||||
typedef _Atomic(uint_least32_t) atomic_char32_t;
|
||||
typedef _Atomic(wchar_t) atomic_wchar_t;
|
||||
typedef _Atomic(int_least8_t) atomic_int_least8_t;
|
||||
typedef _Atomic(uint_least8_t) atomic_uint_least8_t;
|
||||
typedef _Atomic(int_least16_t) atomic_int_least16_t;
|
||||
typedef _Atomic(uint_least16_t) atomic_uint_least16_t;
|
||||
typedef _Atomic(int_least32_t) atomic_int_least32_t;
|
||||
typedef _Atomic(uint_least32_t) atomic_uint_least32_t;
|
||||
typedef _Atomic(int_least64_t) atomic_int_least64_t;
|
||||
typedef _Atomic(uint_least64_t) atomic_uint_least64_t;
|
||||
typedef _Atomic(int_fast8_t) atomic_int_fast8_t;
|
||||
typedef _Atomic(uint_fast8_t) atomic_uint_fast8_t;
|
||||
typedef _Atomic(int_fast16_t) atomic_int_fast16_t;
|
||||
typedef _Atomic(uint_fast16_t) atomic_uint_fast16_t;
|
||||
typedef _Atomic(int_fast32_t) atomic_int_fast32_t;
|
||||
typedef _Atomic(uint_fast32_t) atomic_uint_fast32_t;
|
||||
typedef _Atomic(int_fast64_t) atomic_int_fast64_t;
|
||||
typedef _Atomic(uint_fast64_t) atomic_uint_fast64_t;
|
||||
typedef _Atomic(intptr_t) atomic_intptr_t;
|
||||
typedef _Atomic(uintptr_t) atomic_uintptr_t;
|
||||
typedef _Atomic(size_t) atomic_size_t;
|
||||
typedef _Atomic(ptrdiff_t) atomic_ptrdiff_t;
|
||||
typedef _Atomic(intmax_t) atomic_intmax_t;
|
||||
typedef _Atomic(uintmax_t) atomic_uintmax_t;
|
||||
typedef struct atomic_flag { atomic_bool _Value; } atomic_flag;
|
||||
typedef enum memory_order {
|
||||
memory_order_relaxed,
|
||||
memory_order_consume,
|
||||
memory_order_acquire,
|
||||
memory_order_release,
|
||||
memory_order_acq_rel,
|
||||
memory_order_seq_cst
|
||||
} memory_order;
|
||||
|
||||
#endif
|
||||
2
generator/fake_libc_include/_syslist.h
Normal file
2
generator/fake_libc_include/_syslist.h
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
#include "_fake_defines.h"
|
||||
#include "_fake_typedefs.h"
|
||||
2
generator/fake_libc_include/stdbool.h
Normal file
2
generator/fake_libc_include/stdbool.h
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
#include "_fake_defines.h"
|
||||
#include "_fake_typedefs.h"
|
||||
2
generator/fake_libc_include/stddef.h
Normal file
2
generator/fake_libc_include/stddef.h
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
#include "_fake_defines.h"
|
||||
#include "_fake_typedefs.h"
|
||||
2
generator/fake_libc_include/stdint.h
Normal file
2
generator/fake_libc_include/stdint.h
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
#include "_fake_defines.h"
|
||||
#include "_fake_typedefs.h"
|
||||
5
generator/gen_repo_bindings.sh
Normal file
5
generator/gen_repo_bindings.sh
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
#!/usr/bin/bash
|
||||
REPO_ROOT=$(realpath $(dirname $(dirname $0)))
|
||||
|
||||
# Generate odin bindings
|
||||
python $REPO_ROOT/generator/cli.py $REPO_ROOT/clay.h --output-dir $REPO_ROOT/bindings/odin/clay-odin --generator odin --verbose
|
||||
0
generator/generators/__init__.py
Normal file
0
generator/generators/__init__.py
Normal file
47
generator/generators/base_generator.py
Normal file
47
generator/generators/base_generator.py
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
|
||||
from parser import ExtractedSymbols, ExtractedEnum, ExtractedStruct, ExtractedFunction
|
||||
from typing import Any, Callable, DefaultDict, Literal, NotRequired, Optional, TypedDict
|
||||
from pathlib import Path
|
||||
from dataclasses import dataclass
|
||||
|
||||
SymbolType = Literal['enum', 'struct', 'function']
|
||||
|
||||
class BaseGenerator:
|
||||
def __init__(self, extracted_symbols: ExtractedSymbols):
|
||||
self.extracted_symbols = extracted_symbols
|
||||
self.output_content: dict[str, list[str]] = dict()
|
||||
|
||||
def generate(self) -> None:
|
||||
pass
|
||||
|
||||
def has_symbol(self, symbol: str) -> bool:
|
||||
return (
|
||||
symbol in self.extracted_symbols.enums or
|
||||
symbol in self.extracted_symbols.structs or
|
||||
symbol in self.extracted_symbols.functions
|
||||
)
|
||||
|
||||
def get_symbol_type(self, symbol: str) -> SymbolType:
|
||||
if symbol in self.extracted_symbols.enums:
|
||||
return 'enum'
|
||||
elif symbol in self.extracted_symbols.structs:
|
||||
return 'struct'
|
||||
elif symbol in self.extracted_symbols.functions:
|
||||
return 'function'
|
||||
raise ValueError(f'Unknown symbol: {symbol}')
|
||||
|
||||
def _write(self, file_name: str, content: str) -> None:
|
||||
if file_name not in self.output_content:
|
||||
self.output_content[file_name] = []
|
||||
self.output_content[file_name].append(content)
|
||||
|
||||
def write_outputs(self, output_dir: Path) -> None:
|
||||
for file_name, content in self.output_content.items():
|
||||
(output_dir / file_name).parent.mkdir(parents=True, exist_ok=True)
|
||||
with open(output_dir / file_name, 'w') as f:
|
||||
f.write("\n".join(content))
|
||||
|
||||
def get_outputs(self) -> dict[str, str]:
|
||||
return {file_name: "\n".join(content) for file_name, content in self.output_content.items()}
|
||||
|
||||
|
||||
164
generator/generators/odin/clay.template.odin
Normal file
164
generator/generators/odin/clay.template.odin
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
package clay
|
||||
|
||||
import "core:c"
|
||||
import "core:strings"
|
||||
|
||||
when ODIN_OS == .Windows {
|
||||
foreign import Clay "windows/clay.lib"
|
||||
} else when ODIN_OS == .Linux {
|
||||
foreign import Clay "linux/clay.a"
|
||||
} else when ODIN_OS == .Darwin {
|
||||
when ODIN_ARCH == .arm64 {
|
||||
foreign import Clay "macos-arm64/clay.a"
|
||||
} else {
|
||||
foreign import Clay "macos/clay.a"
|
||||
}
|
||||
} else when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 {
|
||||
foreign import Clay "wasm/clay.o"
|
||||
}
|
||||
|
||||
when ODIN_OS == .Windows {
|
||||
EnumBackingType :: u32
|
||||
} else {
|
||||
EnumBackingType :: u8
|
||||
}
|
||||
|
||||
{{enums}}
|
||||
|
||||
Context :: struct {
|
||||
|
||||
}
|
||||
|
||||
ClayArray :: struct($type: typeid) {
|
||||
capacity: i32,
|
||||
length: i32,
|
||||
internalArray: [^]type,
|
||||
}
|
||||
|
||||
SizingConstraints :: struct #raw_union {
|
||||
sizeMinMax: SizingConstraintsMinMax,
|
||||
sizePercent: c.float,
|
||||
}
|
||||
|
||||
TypedConfig :: struct {
|
||||
type: ElementConfigType,
|
||||
config: rawptr,
|
||||
id: ElementId,
|
||||
}
|
||||
|
||||
{{structs}}
|
||||
|
||||
@(link_prefix = "Clay_", default_calling_convention = "c")
|
||||
foreign Clay {
|
||||
{{public_functions}}
|
||||
}
|
||||
|
||||
@(link_prefix = "Clay_", default_calling_convention = "c", private)
|
||||
foreign Clay {
|
||||
{{private_functions}}
|
||||
}
|
||||
|
||||
@(require_results, deferred_none = _CloseElement)
|
||||
UI :: proc(configs: ..TypedConfig) -> bool {
|
||||
_OpenElement()
|
||||
for config in configs {
|
||||
#partial switch (config.type) {
|
||||
case ElementConfigType.Id:
|
||||
_AttachId(config.id)
|
||||
case ElementConfigType.Layout:
|
||||
_AttachLayoutConfig(cast(^LayoutConfig)config.config)
|
||||
case:
|
||||
_AttachElementConfig(config.config, config.type)
|
||||
}
|
||||
}
|
||||
_ElementPostConfiguration()
|
||||
return true
|
||||
}
|
||||
|
||||
Layout :: proc(config: LayoutConfig) -> TypedConfig {
|
||||
return {type = ElementConfigType.Layout, config = _StoreLayoutConfig(config) }
|
||||
}
|
||||
|
||||
PaddingAll :: proc (padding: u16) -> Padding {
|
||||
return { padding, padding, padding, padding }
|
||||
}
|
||||
|
||||
Rectangle :: proc(config: RectangleElementConfig) -> TypedConfig {
|
||||
return {type = ElementConfigType.Rectangle, config = _StoreRectangleElementConfig(config)}
|
||||
}
|
||||
|
||||
Text :: proc(text: string, config: ^TextElementConfig) {
|
||||
_OpenTextElement(MakeString(text), config)
|
||||
}
|
||||
|
||||
TextConfig :: proc(config: TextElementConfig) -> ^TextElementConfig {
|
||||
return _StoreTextElementConfig(config)
|
||||
}
|
||||
|
||||
Image :: proc(config: ImageElementConfig) -> TypedConfig {
|
||||
return {type = ElementConfigType.Image, config = _StoreImageElementConfig(config)}
|
||||
}
|
||||
|
||||
Floating :: proc(config: FloatingElementConfig) -> TypedConfig {
|
||||
return {type = ElementConfigType.Floating, config = _StoreFloatingElementConfig(config)}
|
||||
}
|
||||
|
||||
Custom :: proc(config: CustomElementConfig) -> TypedConfig {
|
||||
return {type = ElementConfigType.Custom, config = _StoreCustomElementConfig(config)}
|
||||
}
|
||||
|
||||
Scroll :: proc(config: ScrollElementConfig) -> TypedConfig {
|
||||
return {type = ElementConfigType.Scroll, config = _StoreScrollElementConfig(config)}
|
||||
}
|
||||
|
||||
Border :: proc(config: BorderElementConfig) -> TypedConfig {
|
||||
return {type = ElementConfigType.Border, config = _StoreBorderElementConfig(config)}
|
||||
}
|
||||
|
||||
BorderOutside :: proc(outsideBorders: BorderData) -> TypedConfig {
|
||||
return { type = ElementConfigType.Border, config = _StoreBorderElementConfig((BorderElementConfig){left = outsideBorders, right = outsideBorders, top = outsideBorders, bottom = outsideBorders}) }
|
||||
}
|
||||
|
||||
BorderOutsideRadius :: proc(outsideBorders: BorderData, radius: f32) -> TypedConfig {
|
||||
return { type = ElementConfigType.Border, config = _StoreBorderElementConfig(
|
||||
(BorderElementConfig){left = outsideBorders, right = outsideBorders, top = outsideBorders, bottom = outsideBorders, cornerRadius = {radius, radius, radius, radius}},
|
||||
) }
|
||||
}
|
||||
|
||||
BorderAll :: proc(allBorders: BorderData) -> TypedConfig {
|
||||
return { type = ElementConfigType.Border, config = _StoreBorderElementConfig((BorderElementConfig){left = allBorders, right = allBorders, top = allBorders, bottom = allBorders, betweenChildren = allBorders}) }
|
||||
}
|
||||
|
||||
BorderAllRadius :: proc(allBorders: BorderData, radius: f32) -> TypedConfig {
|
||||
return { type = ElementConfigType.Border, config = _StoreBorderElementConfig(
|
||||
(BorderElementConfig){left = allBorders, right = allBorders, top = allBorders, bottom = allBorders, cornerRadius = {radius, radius, radius, radius}},
|
||||
) }
|
||||
}
|
||||
|
||||
CornerRadiusAll :: proc(radius: f32) -> CornerRadius {
|
||||
return CornerRadius{radius, radius, radius, radius}
|
||||
}
|
||||
|
||||
SizingFit :: proc(sizeMinMax: SizingConstraintsMinMax) -> SizingAxis {
|
||||
return SizingAxis{type = SizingType.FIT, constraints = {sizeMinMax = sizeMinMax}}
|
||||
}
|
||||
|
||||
SizingGrow :: proc(sizeMinMax: SizingConstraintsMinMax) -> SizingAxis {
|
||||
return SizingAxis{type = SizingType.GROW, constraints = {sizeMinMax = sizeMinMax}}
|
||||
}
|
||||
|
||||
SizingFixed :: proc(size: c.float) -> SizingAxis {
|
||||
return SizingAxis{type = SizingType.FIXED, constraints = {sizeMinMax = {size, size}}}
|
||||
}
|
||||
|
||||
SizingPercent :: proc(sizePercent: c.float) -> SizingAxis {
|
||||
return SizingAxis{type = SizingType.PERCENT, constraints = {sizePercent = sizePercent}}
|
||||
}
|
||||
|
||||
MakeString :: proc(label: string) -> String {
|
||||
return String{chars = raw_data(label), length = cast(c.int)len(label)}
|
||||
}
|
||||
|
||||
ID :: proc(label: string, index: u32 = 0) -> TypedConfig {
|
||||
return { type = ElementConfigType.Id, id = _HashString(MakeString(label), index, 0) }
|
||||
}
|
||||
315
generator/generators/odin_generator.py
Normal file
315
generator/generators/odin_generator.py
Normal file
|
|
@ -0,0 +1,315 @@
|
|||
from pathlib import Path
|
||||
import logging
|
||||
|
||||
from parser import ExtractedSymbolType
|
||||
from generators.base_generator import BaseGenerator
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def get_common_prefix(keys: list[str]) -> str:
|
||||
# find a prefix that's shared between all keys
|
||||
prefix = ""
|
||||
for i in range(min(map(len, keys))):
|
||||
if len(set(key[i] for key in keys)) > 1:
|
||||
break
|
||||
prefix += keys[0][i]
|
||||
return prefix
|
||||
|
||||
def snake_case_to_pascal_case(snake_case: str) -> str:
|
||||
return ''.join(word.lower().capitalize() for word in snake_case.split('_'))
|
||||
|
||||
|
||||
SYMBOL_NAME_OVERRIDES = {
|
||||
'Clay_TextElementConfigWrapMode': 'TextWrapMode',
|
||||
'Clay_Border': 'BorderData',
|
||||
'Clay_SizingMinMax': 'SizingConstraintsMinMax',
|
||||
}
|
||||
SYMBOL_COMPLETE_OVERRIDES = {
|
||||
'Clay_RenderCommandArray': 'ClayArray(RenderCommand)',
|
||||
'Clay_Context': 'Context',
|
||||
'Clay_ElementConfig': None,
|
||||
# 'Clay_SetQueryScrollOffsetFunction': None,
|
||||
}
|
||||
|
||||
# These enums should have output binding members that are PascalCase instead of UPPER_SNAKE_CASE.
|
||||
ENUM_MEMBER_PASCAL = {
|
||||
'Clay_RenderCommandType',
|
||||
'Clay_TextElementConfigWrapMode',
|
||||
'Clay__ElementConfigType',
|
||||
}
|
||||
ENUM_MEMBER_OVERRIDES = {
|
||||
'Clay__ElementConfigType': {
|
||||
'CLAY__ELEMENT_CONFIG_TYPE_BORDER_CONTAINER': 'Border',
|
||||
'CLAY__ELEMENT_CONFIG_TYPE_FLOATING_CONTAINER': 'Floating',
|
||||
'CLAY__ELEMENT_CONFIG_TYPE_SCROLL_CONTAINER': 'Scroll',
|
||||
}
|
||||
}
|
||||
ENUM_ADDITIONAL_MEMBERS = {
|
||||
'Clay__ElementConfigType': {
|
||||
'Id': 65,
|
||||
'Layout': 66,
|
||||
}
|
||||
}
|
||||
|
||||
TYPE_MAPPING = {
|
||||
'*char': '[^]c.char',
|
||||
'const *char': '[^]c.char',
|
||||
'*void': 'rawptr',
|
||||
'bool': 'bool',
|
||||
'float': 'c.float',
|
||||
'uint16_t': 'u16',
|
||||
'uint32_t': 'u32',
|
||||
'int32_t': 'c.int32_t',
|
||||
'uintptr_t': 'rawptr',
|
||||
'intptr_t': 'rawptr',
|
||||
'void': 'void',
|
||||
}
|
||||
STRUCT_TYPE_OVERRIDES = {
|
||||
'Clay_Arena': {
|
||||
'nextAllocation': 'uintptr',
|
||||
'capacity': 'uintptr',
|
||||
},
|
||||
'Clay_SizingAxis': {
|
||||
'size': 'SizingConstraints',
|
||||
},
|
||||
"Clay_RenderCommand": {
|
||||
"zIndex": 'i32',
|
||||
},
|
||||
}
|
||||
STRUCT_MEMBER_OVERRIDES = {
|
||||
'Clay_ErrorHandler': {
|
||||
'errorHandlerFunction': 'handler',
|
||||
},
|
||||
'Clay_SizingAxis': {
|
||||
'size': 'constraints',
|
||||
},
|
||||
}
|
||||
STRUCT_OVERRIDE_AS_FIXED_ARRAY = {
|
||||
'Clay_Color',
|
||||
'Clay_Vector2',
|
||||
}
|
||||
|
||||
FUNCTION_PARAM_OVERRIDES = {
|
||||
'Clay_SetCurrentContext': {
|
||||
'context': 'ctx',
|
||||
},
|
||||
}
|
||||
FUNCTION_TYPE_OVERRIDES = {
|
||||
'Clay_CreateArenaWithCapacityAndMemory': {
|
||||
'offset': '[^]u8',
|
||||
},
|
||||
'Clay_SetMeasureTextFunction': {
|
||||
'userData': 'uintptr',
|
||||
},
|
||||
'Clay_RenderCommandArray_Get': {
|
||||
'index': 'i32',
|
||||
},
|
||||
"Clay__AttachElementConfig": {
|
||||
"config": 'rawptr',
|
||||
},
|
||||
}
|
||||
|
||||
class OdinGenerator(BaseGenerator):
|
||||
|
||||
def generate(self) -> None:
|
||||
self.generate_structs()
|
||||
self.generate_enums()
|
||||
self.generate_functions()
|
||||
|
||||
odin_template_path = Path(__file__).parent / 'odin' / 'clay.template.odin'
|
||||
with open(odin_template_path, 'r') as f:
|
||||
template = f.read()
|
||||
self.output_content['clay.odin'] = (
|
||||
template
|
||||
.replace('{{structs}}', '\n'.join(self.output_content['struct']))
|
||||
.replace('{{enums}}', '\n'.join(self.output_content['enum']))
|
||||
.replace('{{public_functions}}', '\n'.join(self.output_content['public_function']))
|
||||
.replace('{{private_functions}}', '\n'.join(self.output_content['private_function']))
|
||||
.splitlines()
|
||||
)
|
||||
del self.output_content['struct']
|
||||
del self.output_content['enum']
|
||||
del self.output_content['private_function']
|
||||
del self.output_content['public_function']
|
||||
|
||||
def get_symbol_name(self, symbol: str) -> str:
|
||||
if symbol in SYMBOL_NAME_OVERRIDES:
|
||||
return SYMBOL_NAME_OVERRIDES[symbol]
|
||||
symbol_type = self.get_symbol_type(symbol)
|
||||
base_name = symbol.removeprefix('Clay_')
|
||||
if symbol_type == 'enum':
|
||||
return base_name.removeprefix('_') # Clay_ and Clay__ are exported as public types.
|
||||
elif symbol_type == 'struct':
|
||||
return base_name
|
||||
elif symbol_type == 'function':
|
||||
return base_name
|
||||
raise ValueError(f'Unknown symbol: {symbol}')
|
||||
|
||||
def format_type(self, type: ExtractedSymbolType) -> str:
|
||||
if isinstance(type, str):
|
||||
return type
|
||||
|
||||
parameter_strs = []
|
||||
for param_name, param_type in type['params']:
|
||||
parameter_strs.append(f"{param_name}: {self.format_type(param_type or 'unknown')}")
|
||||
return_type_str = ''
|
||||
if type['return_type'] is not None and type['return_type'] != 'void':
|
||||
return_type_str = ' -> ' + self.format_type(type['return_type'])
|
||||
return f"proc \"c\" ({', '.join(parameter_strs)}){return_type_str}"
|
||||
|
||||
def resolve_binding_type(self, symbol: str, member: str | None, member_type: ExtractedSymbolType | None, type_overrides: dict[str, dict[str, str]]) -> str | None:
|
||||
if isinstance(member_type, str):
|
||||
if member_type in SYMBOL_COMPLETE_OVERRIDES:
|
||||
return SYMBOL_COMPLETE_OVERRIDES[member_type]
|
||||
if symbol in type_overrides and member in type_overrides[symbol]:
|
||||
return type_overrides[symbol][member]
|
||||
if member_type in TYPE_MAPPING:
|
||||
return TYPE_MAPPING[member_type]
|
||||
if member_type and self.has_symbol(member_type):
|
||||
return self.get_symbol_name(member_type)
|
||||
if member_type and member_type.startswith('*'):
|
||||
result = self.resolve_binding_type(symbol, member, member_type[1:], type_overrides)
|
||||
if result:
|
||||
return f"^{result}"
|
||||
return None
|
||||
if member_type is None:
|
||||
return None
|
||||
|
||||
resolved_parameters = []
|
||||
for param_name, param_type in member_type['params']:
|
||||
resolved_param = self.resolve_binding_type(symbol, param_name, param_type, type_overrides)
|
||||
if resolved_param is None:
|
||||
return None
|
||||
resolved_parameters.append((param_name, resolved_param))
|
||||
resolved_return_type = self.resolve_binding_type(symbol, None, member_type['return_type'], type_overrides)
|
||||
if resolved_return_type is None:
|
||||
return None
|
||||
return self.format_type({
|
||||
"params": resolved_parameters,
|
||||
"return_type": resolved_return_type,
|
||||
})
|
||||
|
||||
def generate_structs(self) -> None:
|
||||
for struct, struct_data in sorted(self.extracted_symbols.structs.items(), key=lambda x: x[0]):
|
||||
members = struct_data['attrs']
|
||||
if not struct.startswith('Clay_'):
|
||||
continue
|
||||
if struct in SYMBOL_COMPLETE_OVERRIDES:
|
||||
continue
|
||||
|
||||
binding_name = self.get_symbol_name(struct)
|
||||
if binding_name.startswith('_'):
|
||||
continue
|
||||
|
||||
if struct in STRUCT_OVERRIDE_AS_FIXED_ARRAY:
|
||||
array_size = len(members)
|
||||
first_elem = list(members.values())[0]
|
||||
array_type = None
|
||||
if 'type' in first_elem:
|
||||
array_type = first_elem['type']
|
||||
|
||||
if array_type in TYPE_MAPPING:
|
||||
array_binding_type = TYPE_MAPPING[array_type]
|
||||
elif array_type and self.has_symbol(self.format_type(array_type)):
|
||||
array_binding_type = self.get_symbol_name(self.format_type(array_type))
|
||||
else:
|
||||
self._write('struct', f"// {struct} ({array_type}) - has no mapping")
|
||||
continue
|
||||
|
||||
self._write('struct', f"// {struct} (overridden as fixed array)")
|
||||
self._write('struct', f"{binding_name} :: [{array_size}]{array_binding_type}")
|
||||
self._write('struct', "")
|
||||
continue
|
||||
|
||||
raw_union = ' #raw_union' if struct_data.get('is_union', False) else ''
|
||||
|
||||
self._write('struct', f"// {struct}")
|
||||
self._write('struct', f"{binding_name} :: struct{raw_union} {{")
|
||||
|
||||
for member, member_info in members.items():
|
||||
if struct in STRUCT_TYPE_OVERRIDES and member in STRUCT_TYPE_OVERRIDES[struct]:
|
||||
member_type = 'unknown'
|
||||
elif not 'type' in member_info:
|
||||
self._write('struct', f" // {member} (unknown type)")
|
||||
continue
|
||||
else:
|
||||
member_type = member_info['type']
|
||||
|
||||
binding_member_name = member
|
||||
if struct in STRUCT_MEMBER_OVERRIDES and member in STRUCT_MEMBER_OVERRIDES[struct]:
|
||||
binding_member_name = STRUCT_MEMBER_OVERRIDES[struct][member]
|
||||
|
||||
member_binding_type = self.resolve_binding_type(struct, member, member_type, STRUCT_TYPE_OVERRIDES)
|
||||
if member_binding_type is None:
|
||||
self._write('struct', f" // {binding_member_name} ({member_type}) - has no mapping")
|
||||
continue
|
||||
self._write('struct', f" {binding_member_name}: {member_binding_type}, // {member} ({member_type})")
|
||||
self._write('struct', "}")
|
||||
self._write('struct', '')
|
||||
|
||||
def generate_enums(self) -> None:
|
||||
for enum, members in sorted(self.extracted_symbols.enums.items(), key=lambda x: x[0]):
|
||||
if not enum.startswith('Clay_'):
|
||||
continue
|
||||
if enum in SYMBOL_COMPLETE_OVERRIDES:
|
||||
continue
|
||||
|
||||
binding_name = self.get_symbol_name(enum)
|
||||
common_member_prefix = get_common_prefix(list(members.keys()))
|
||||
self._write('enum', f"// {enum}")
|
||||
self._write('enum', f"{binding_name} :: enum EnumBackingType {{")
|
||||
for member in members:
|
||||
if enum in ENUM_MEMBER_OVERRIDES and member in ENUM_MEMBER_OVERRIDES[enum]:
|
||||
binding_member_name = ENUM_MEMBER_OVERRIDES[enum][member]
|
||||
else:
|
||||
binding_member_name = member.removeprefix(common_member_prefix)
|
||||
if enum in ENUM_MEMBER_PASCAL:
|
||||
binding_member_name = snake_case_to_pascal_case(binding_member_name)
|
||||
|
||||
if members[member] is not None:
|
||||
self._write('enum', f" {binding_member_name} = {members[member]}, // {member}")
|
||||
else:
|
||||
self._write('enum', f" {binding_member_name}, // {member}")
|
||||
|
||||
if enum in ENUM_ADDITIONAL_MEMBERS:
|
||||
self._write('enum', ' // Odin specific enum types')
|
||||
for member, value in ENUM_ADDITIONAL_MEMBERS[enum].items():
|
||||
self._write('enum', f" {member} = {value},")
|
||||
self._write('enum', "}")
|
||||
self._write('enum', '')
|
||||
|
||||
def generate_functions(self) -> None:
|
||||
for function, function_info in sorted(self.extracted_symbols.functions.items(), key=lambda x: x[0]):
|
||||
if not function.startswith('Clay_'):
|
||||
continue
|
||||
if function in SYMBOL_COMPLETE_OVERRIDES:
|
||||
continue
|
||||
is_private = function.startswith('Clay__')
|
||||
write_to = 'private_function' if is_private else 'public_function'
|
||||
|
||||
binding_name = self.get_symbol_name(function)
|
||||
|
||||
return_type = function_info['return_type']
|
||||
binding_return_type = self.resolve_binding_type(function, None, return_type, {})
|
||||
if binding_return_type is None:
|
||||
self._write(write_to, f" // {function} ({return_type}) - has no mapping")
|
||||
continue
|
||||
|
||||
skip = False
|
||||
binding_params = []
|
||||
for param_name, param_type in function_info['params']:
|
||||
binding_param_name = param_name
|
||||
if function in FUNCTION_PARAM_OVERRIDES and param_name in FUNCTION_PARAM_OVERRIDES[function]:
|
||||
binding_param_name = FUNCTION_PARAM_OVERRIDES[function][param_name]
|
||||
binding_param_type = self.resolve_binding_type(function, param_name, param_type, FUNCTION_TYPE_OVERRIDES)
|
||||
if binding_param_type is None:
|
||||
skip = True
|
||||
binding_params.append(f"{binding_param_name}: {binding_param_type}")
|
||||
if skip:
|
||||
self._write(write_to, f" // {function} - has no mapping")
|
||||
continue
|
||||
|
||||
binding_params_str = ', '.join(binding_params)
|
||||
return_str = f" -> {binding_return_type}" if binding_return_type != 'void' else ''
|
||||
self._write(write_to, f" {binding_name} :: proc({binding_params_str}){return_str} --- // {function}")
|
||||
|
||||
173
generator/parser.py
Normal file
173
generator/parser.py
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
from dataclasses import dataclass
|
||||
from typing import Optional, TypedDict, NotRequired, Union
|
||||
from pycparser import c_ast, parse_file, preprocess_file
|
||||
from pathlib import Path
|
||||
import os
|
||||
import json
|
||||
import shutil
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
ExtractedSymbolType = Union[str, "ExtractedFunction"]
|
||||
|
||||
class ExtractedStructAttributeUnion(TypedDict):
|
||||
type: Optional[ExtractedSymbolType]
|
||||
|
||||
class ExtractedStructAttribute(TypedDict):
|
||||
type: NotRequired[ExtractedSymbolType]
|
||||
union: NotRequired[dict[str, Optional[ExtractedSymbolType]]]
|
||||
|
||||
class ExtractedStruct(TypedDict):
|
||||
attrs: dict[str, ExtractedStructAttribute]
|
||||
is_union: NotRequired[bool]
|
||||
|
||||
ExtractedEnum = dict[str, Optional[str]]
|
||||
ExtractedFunctionParam = tuple[str, Optional[ExtractedSymbolType]]
|
||||
|
||||
class ExtractedFunction(TypedDict):
|
||||
return_type: Optional["ExtractedSymbolType"]
|
||||
params: list[ExtractedFunctionParam]
|
||||
|
||||
@dataclass
|
||||
class ExtractedSymbols:
|
||||
structs: dict[str, ExtractedStruct]
|
||||
enums: dict[str, ExtractedEnum]
|
||||
functions: dict[str, ExtractedFunction]
|
||||
|
||||
def get_type_names(node: c_ast.Node, prefix: str="") -> Optional[ExtractedSymbolType]:
|
||||
if isinstance(node, c_ast.TypeDecl) and hasattr(node, 'quals') and node.quals:
|
||||
prefix = " ".join(node.quals) + " " + prefix
|
||||
if isinstance(node, c_ast.PtrDecl):
|
||||
prefix = "*" + prefix
|
||||
if isinstance(node, c_ast.FuncDecl):
|
||||
func: ExtractedFunction = {
|
||||
'return_type': get_type_names(node.type),
|
||||
'params': [],
|
||||
}
|
||||
for param in node.args.params:
|
||||
if param.name is None:
|
||||
continue
|
||||
func['params'].append((param.name, get_type_names(param)))
|
||||
return func
|
||||
|
||||
if hasattr(node, 'names'):
|
||||
return prefix + node.names[0] # type: ignore
|
||||
elif hasattr(node, 'type'):
|
||||
return get_type_names(node.type, prefix) # type: ignore
|
||||
return None
|
||||
|
||||
class Visitor(c_ast.NodeVisitor):
|
||||
def __init__(self):
|
||||
self.structs: dict[str, ExtractedStruct] = {}
|
||||
self.enums: dict[str, ExtractedEnum] = {}
|
||||
self.functions: dict[str, ExtractedFunction] = {}
|
||||
|
||||
def visit_FuncDecl(self, node: c_ast.FuncDecl):
|
||||
# node.show()
|
||||
# logger.debug(node)
|
||||
node_type = node.type
|
||||
is_pointer = False
|
||||
if isinstance(node.type, c_ast.PtrDecl):
|
||||
node_type = node.type.type
|
||||
is_pointer = True
|
||||
|
||||
if hasattr(node_type, "declname"):
|
||||
return_type = get_type_names(node_type.type)
|
||||
if return_type is not None and isinstance(return_type, str) and is_pointer:
|
||||
return_type = "*" + return_type
|
||||
func: ExtractedFunction = {
|
||||
'return_type': return_type,
|
||||
'params': [],
|
||||
}
|
||||
for param in node.args.params:
|
||||
if param.name is None:
|
||||
continue
|
||||
func['params'].append((param.name, get_type_names(param)))
|
||||
self.functions[node_type.declname] = func
|
||||
self.generic_visit(node)
|
||||
|
||||
def visit_Struct(self, node: c_ast.Struct):
|
||||
# node.show()
|
||||
if node.name and node.decls:
|
||||
struct = {}
|
||||
for decl in node.decls:
|
||||
struct[decl.name] = {
|
||||
"type": get_type_names(decl),
|
||||
}
|
||||
self.structs[node.name] = {
|
||||
'attrs': struct,
|
||||
}
|
||||
self.generic_visit(node)
|
||||
|
||||
def visit_Typedef(self, node: c_ast.Typedef):
|
||||
# node.show()
|
||||
if hasattr(node.type, 'type') and hasattr(node.type.type, 'decls') and node.type.type.decls:
|
||||
struct = {}
|
||||
for decl in node.type.type.decls:
|
||||
if hasattr(decl, 'type') and hasattr(decl.type, 'type') and isinstance(decl.type.type, c_ast.Union):
|
||||
union = {}
|
||||
for field in decl.type.type.decls:
|
||||
union[field.name] = get_type_names(field)
|
||||
struct[decl.name] = {
|
||||
'union': union
|
||||
}
|
||||
else:
|
||||
struct[decl.name] = {
|
||||
"type": get_type_names(decl),
|
||||
}
|
||||
|
||||
self.structs[node.name] = {
|
||||
'attrs': struct,
|
||||
'is_union': isinstance(node.type.type, c_ast.Union),
|
||||
}
|
||||
if hasattr(node.type, 'type') and isinstance(node.type.type, c_ast.Enum):
|
||||
enum = {}
|
||||
for enumerator in node.type.type.values.enumerators:
|
||||
if enumerator.value is None:
|
||||
enum[enumerator.name] = None
|
||||
else:
|
||||
enum[enumerator.name] = enumerator.value.value
|
||||
self.enums[node.name] = enum
|
||||
self.generic_visit(node)
|
||||
|
||||
|
||||
def parse_headers(input_files: list[Path], tmp_dir: Path) -> ExtractedSymbols:
|
||||
cpp_args = ["-nostdinc", "-D__attribute__(x)=", "-E"]
|
||||
|
||||
# Make a new clay.h that combines the provided input files, so that we can add bindings for customized structs
|
||||
with open(tmp_dir / 'merged_clay.h', 'w') as f:
|
||||
for input_file in input_files:
|
||||
with open(input_file, 'r') as f2:
|
||||
for line in f2:
|
||||
# Ignore includes, as they should be manually included in input_files.
|
||||
if line.startswith("#include"):
|
||||
continue
|
||||
|
||||
# Ignore the CLAY_IMPLEMENTATION define, because we only want to parse the public api code.
|
||||
# This is helpful so that the user can provide their implementation code, which will contain any custom extensions
|
||||
if "#define CLAY_IMPLEMENTATION" in line:
|
||||
continue
|
||||
|
||||
f.write(line)
|
||||
|
||||
# Preprocess the file
|
||||
logger.info("Preprocessing file")
|
||||
preprocessed = preprocess_file(tmp_dir / 'merged_clay.h', cpp_path="cpp", cpp_args=cpp_args) # type: ignore
|
||||
with open(tmp_dir / 'clay.preprocessed.h', 'w') as f:
|
||||
f.write(preprocessed)
|
||||
|
||||
# Parse the file
|
||||
logger.info("Parsing file")
|
||||
ast = parse_file(tmp_dir / 'clay.preprocessed.h', use_cpp=False) # type: ignore
|
||||
|
||||
# Extract symbols
|
||||
visitor = Visitor()
|
||||
visitor.visit(ast)
|
||||
|
||||
result = ExtractedSymbols(
|
||||
structs=visitor.structs,
|
||||
enums=visitor.enums,
|
||||
functions=visitor.functions
|
||||
)
|
||||
return result
|
||||
1
generator/requirements.txt
Normal file
1
generator/requirements.txt
Normal file
|
|
@ -0,0 +1 @@
|
|||
pycparser==2.22
|
||||
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
Copyright (c) 2025 Mivirl
|
||||
|
||||
altered by Godje (Sep 2025)
|
||||
|
||||
This software is provided 'as-is', without any express or implied warranty.
|
||||
In no event will the authors be held liable for any damages arising from the
|
||||
use of this software.
|
||||
|
|
@ -1616,6 +1618,20 @@ void Clay_Termbox_Render(Clay_RenderCommandArray commands)
|
|||
|
||||
Clay_StringSlice *text = &render_data.stringContents;
|
||||
int32_t i = 0;
|
||||
|
||||
// culling text characters that are outside of the layout
|
||||
int h_clip = 0 - cell_box.x;
|
||||
while(h_clip > 0 && i < text->length){
|
||||
uint32_t ch = ' ';
|
||||
int codepoint_length = tb_utf8_char_to_unicode(&ch, text->chars + i);
|
||||
if (0 > codepoint_length) {
|
||||
clay_tb_assert(false, "Invalid utf8");
|
||||
}
|
||||
i += codepoint_length;
|
||||
h_clip -= 1;
|
||||
}
|
||||
|
||||
// printing the rest of the characters
|
||||
for (int y = box_begin_y; y < box_end_y; ++y) {
|
||||
for (int x = box_begin_x; x < box_end_x;) {
|
||||
uint32_t ch = ' ';
|
||||
|
|
|
|||
Loading…
Reference in a new issue