Switch .scroll usage to .clip

This commit is contained in:
Nic Barker 2025-05-01 13:41:42 +12:00
parent f9ebeced60
commit fd281f4f86
4 changed files with 59 additions and 34 deletions

View File

@ -178,18 +178,10 @@ For help starting out or to discuss clay, considering joining [the discord serve
- [Clay_GetScrollContainerData](#clay_getscrollcontainerdata)
- [Clay_GetElementId](#clay_getelementid)
- [Element Macros](#element-macros)
- [CLAY](#clay-1)
- [CLAY](#clay)
- [CLAY_ID](#clay_id)
- [CLAY_IDI](#clay_idi)
- [CLAY_LAYOUT](#clay_layout)
- [CLAY_RECTANGLE](#clay_rectangle)
- [CLAY_TEXT](#clay_text)
- [CLAY_IMAGE](#clay_image)
- [CLAY_SCROLL](#clay_scroll)
- [CLAY_BORDER](#clay_border)
- [CLAY_FLOATING](#clay_floating)
- [CLAY_CUSTOM_ELEMENT](#clay_custom_element)
- [Data Structures & Defs](data-structures--definitions)
- [Data Structures & Defs](#data-structures--definitions)
- [Clay_String](#clay_string)
- [Clay_ElementId](#clay_elementid)
- [Clay_RenderCommandArray](#clay_rendercommandarray)
@ -350,7 +342,11 @@ If this is an issue for you, performing layout twice per frame with the same dat
### Scrolling Elements
Elements are configured as scrollable with the `CLAY_SCROLL` macro. To make scroll containers respond to mouse wheel and scroll events, two functions need to be called before `BeginLayout()`:
Elements are configured as scrollable with the `.clip` configuration. Clipping instructs the renderer to not draw any pixels outside the clipped element's boundaries, and by specifying the `.childOffset` field, the clipped element's contents can be shifted around to provide "scrolling" behaviour.
You can either calculate scrolling yourself and simply provide the current offset each frame to `.childOffset`, or alternatively, Clay provides a built in mechanism for tracking and updating scroll container offsets, detailed below.
To make scroll containers respond to mouse wheel and scroll events, two functions need to be called before `BeginLayout()`:
```C
Clay_Vector2 mousePosition = { x, y };
// Reminder: Clay_SetPointerState must be called before Clay_UpdateScrollContainers otherwise it will have no effect
@ -362,9 +358,17 @@ Clay_UpdateScrollContainers(
float deltaTime, // Time since last frame in seconds as a float e.g. 8ms is 0.008f
);
// ...
// Clay internally tracks the scroll containers offset, and Clay_GetScrollOffset returns the x,y offset of the currently open element
CLAY({ .clip = vertical, .childOffset = Clay_GetScrollOffset() }) {
// Scrolling contents
}
// .childOffset can be provided directly if you would prefer to manage scrolling outside of clay
CLAY({ .clip = vertical, .childOffset = myData.scrollContainer.offset }) {
// Scrolling contents
}
```
More specific details can be found in the full [Scroll API](#clay_scroll).
More specific details can be found in the docs for [Clay_UpdateScrollContainers](#clay_updatescrollcontainers), [Clay_SetPointerState](#clay_setpointerstate), [Clay_ClipElementConfig](#clay_clipelementconfig) and [Clay_GetScrollOffset](#clay_getscrolloffset).
### Floating Elements ("Absolute" Positioning)
@ -658,6 +662,25 @@ Touch / drag scrolling only occurs if the `enableDragScrolling` parameter is `tr
---
### Clay_GetScrollOffset
`Clay_Vector2 Clay_GetScrollOffset()`
Returns the internally stored scroll offset for the currently open element.
Generally intended for use with [clip elements](#clay_clipelementconfig) and the `.childOffset` field to create scrolling containers.
See [Scrolling Elements](#scrolling-elements) for more details.
```C
// Create a horizontally scrolling container
CLAY({
.clip = { .horizontal = true, .childOffset = Clay_GetScrollOffset() }
})
```
---
### Clay_BeginLayout
`void Clay_BeginLayout()`
@ -767,7 +790,7 @@ CLAY({ .id = CLAY_ID("Outer"), .layout = { .padding = CLAY_PADDING_ALL(16) } })
.layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 16 },
.backgroundColor = { 200, 200, 100, 255 },
.cornerRadius = CLAY_CORNER_RADIUS(10),
.scroll = { .vertical = true }
.clip = { .vertical = true, .childOffset = Clay_GetScrollOffset() }
}) {
// child elements
}
@ -1034,7 +1057,7 @@ typedef struct {
Clay_ImageElementConfig image;
Clay_FloatingElementConfig floating;
Clay_CustomElementConfig custom;
Clay_ScrollElementConfig scroll;
Clay_ClipElementConfig clip;
Clay_BorderElementConfig border;
void *userData;
} Clay_ElementDeclaration;
@ -1101,11 +1124,13 @@ Uses [Clay_CustomElementConfig](#clay_customelementconfig). Configures the eleme
---
**`.scroll`** - `Clay_ScrollElementConfig`
**`.clip`** - `Clay_ClipElementConfig`
`CLAY({ .scroll = { .vertical = true } })`
`CLAY({ .clip = { .vertical = true, .childOffset = Clay_GetScrollOffset() } })`
Uses [Clay_ScrollElementConfig](#clay_scrollelementconfig). Configures the element as a scroll element, which causes child elements to be clipped / masked if they overflow, and together with [Clay_UpdateScrollContainer](#clay_updatescrollcontainers) enables scrolling of child contents.
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.
<img width="580" alt="An image demonstrating the concept of clipping which prevents rendering of a child elements pixels if they fall outside the bounds of the parent element." src="https://github.com/user-attachments/assets/2eb83ff9-e186-4ea4-8a87-d90cbc0838b5">
---
@ -1140,7 +1165,7 @@ CLAY({ .color = { 200, 200, 100, 255 }, .cornerRadius = CLAY_CORNER_RADIUS(10) }
CLAY({
.backgroundColor = { 200, 200, 100, 255 },
.cornerRadius = CLAY_CORNER_RADIUS(10)
CLAY_SCROLL({ .vertical = true })
.clip = { .vertical = true, .childOffset = Clay_GetScrollOffset() }
) {
// child elements
}
@ -1312,22 +1337,22 @@ Element is subject to [culling](#visibility-culling). Otherwise, a single `Clay_
---
### Clay_ScrollElementConfig
### Clay_ClipElementConfig
**Usage**
`CLAY({ .scroll = { ...scroll config } }) {}`
`CLAY({ .clip = { ...clip config } }) {}`
**Notes**
`Clay_ScrollElementConfig` configures the element as a scrolling container, enabling masking of children that extend beyond its boundaries.
`Clay_ClipElementConfig` configures the element as a clipping container, enabling masking of children that extend beyond its boundaries.
Note: In order to process scrolling based on pointer position and mouse wheel or touch interactions, you must call `Clay_SetPointerState()` and `Clay_UpdateScrollContainers()` _before_ calling `BeginLayout`.
**Struct Definition (Pseudocode)**
```C
Clay_ScrollElementConfig {
Clay_ClipElementConfig {
bool horizontal;
bool vertical;
};
@ -1337,30 +1362,30 @@ Clay_ScrollElementConfig {
**`.horizontal`** - `bool`
`CLAY({ .scroll = { .horizontal = true } })`
`CLAY({ .clip = { .horizontal = true } })`
Enables or disables horizontal scrolling for this container element.
Enables or disables horizontal clipping for this container element.
---
**`.vertical`** - `bool`
`CLAY({ .scroll = { .vertical = true } })`
`CLAY({ .clip = { .vertical = true } })`
Enables or disables vertical scrolling for this container element.
Enables or disables vertical clipping for this container element.
---
**Rendering**
Enabling scroll for an element will result in two additional render commands:
Enabling clip for an element will result in two additional render commands:
- `commandType = CLAY_RENDER_COMMAND_TYPE_SCISSOR_START`, which should create a rectangle mask with its `boundingBox` and is **not** subject to [culling](#visibility-culling)
- `commandType = CLAY_RENDER_COMMAND_TYPE_SCISSOR_END`, which disables the previous rectangle mask and is **not** subject to [culling](#visibility-culling)
**Examples**
```C
CLAY({ .scroll = { .vertical = true } }) {
CLAY({ .clip = { .vertical = true } }) {
// Create child content with a fixed height of 5000
CLAY({ .id = CLAY_ID("ScrollInner"), .layout = { .sizing = { .height = CLAY_SIZING_FIXED(5000) } } }) {}
}
@ -2031,7 +2056,7 @@ typedef struct {
// The outer dimensions of the inner scroll container content, including the padding of the parent scroll container.
Clay_Dimensions contentDimensions;
// The config that was originally passed to the scroll element.
Clay_ScrollElementConfig config;
Clay_ClipElementConfig config;
// Indicates whether an actual scroll container matched the provided ID or if the default struct was returned.
bool found;
} Clay_ScrollContainerData;
@ -2081,9 +2106,9 @@ Dimensions representing the inner width and height of the content _inside_ the s
---
**`.config`** - `Clay_ScrollElementConfig`
**`.config`** - `Clay_ClipElementConfig`
The [Clay_ScrollElementConfig](#clay_scroll) for the matching scroll container element.
The [Clay_ClipElementConfig](#clay_scroll) for the matching scroll container element.
---

View File

@ -379,7 +379,7 @@ Clay_RenderCommandArray CreateLayout(bool mobileScreen, float lerpValue) {
CLAY({ .id = CLAY_ID("TopBorder5"), .layout = topBorderConfig, .backgroundColor = COLOR_TOP_BORDER_1 }) {}
CLAY({ .id = CLAY_ID("OuterScrollContainer"),
.layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0) }, .layoutDirection = CLAY_TOP_TO_BOTTOM },
.scroll = { .vertical = true },
.clip = { .vertical = true, .childOffset = Clay_GetScrollOffset() },
.border = { .width = { .betweenChildren = 2 }, .color = COLOR_RED }
}) {
if (mobileScreen) {

View File

@ -127,7 +127,7 @@ Clay_RenderCommandArray CreateLayout(void) {
.attachTo = CLAY_ATTACH_TO_ELEMENT_WITH_ID,
.offset = { .y = -(scrollData.scrollPosition->y / scrollData.contentDimensions.height) * scrollData.scrollContainerDimensions.height },
.zIndex = 1,
.parentId = Clay_GetElementId(CLAY_STRING("MainContent")).id,
.parentId = 55,
.attachPoints = { .element = CLAY_ATTACH_POINT_RIGHT_TOP, .parent = CLAY_ATTACH_POINT_RIGHT_TOP }
}
}) {

View File

@ -235,7 +235,7 @@ Clay_RenderCommandArray ClayVideoDemo_CreateLayout(ClayVideoDemo_Data *data) {
CLAY({ .id = CLAY_ID("MainContent"),
.backgroundColor = contentBackgroundColor,
.scroll = { .vertical = true },
.clip = { .vertical = true, .childOffset = Clay_GetScrollOffset() },
.layout = {
.layoutDirection = CLAY_TOP_TO_BOTTOM,
.childGap = 16,