mirror of
https://github.com/nicbarker/clay.git
synced 2025-04-14 02:08:04 +00:00
Merge branch 'main' of https://github.com/nicbarker/clay into fix_clay_macro_latch_increment
This commit is contained in:
commit
8d165e271a
@ -4,7 +4,7 @@
|
||||
### Major Features
|
||||
- Microsecond layout performance
|
||||
- Flex-box like layout model for complex, responsive layouts including text wrapping, scrolling containers and aspect ratio scaling
|
||||
- Single ~2k LOC **clay.h** file with **zero** dependencies (including no standard library)
|
||||
- Single ~4k LOC **clay.h** file with **zero** dependencies (including no standard library)
|
||||
- Wasm support: compile with clang to a 15kb uncompressed **.wasm** file for use in the browser
|
||||
- Static arena based memory use with no malloc / free, and low total memory overhead (e.g. ~3.5mb for 8192 layout elements).
|
||||
- React-like nested declarative syntax
|
||||
|
@ -17,6 +17,7 @@ when ODIN_OS == .Windows {
|
||||
}
|
||||
|
||||
String :: struct {
|
||||
isStaticallyAllocated: c.bool,
|
||||
length: c.int32_t,
|
||||
chars: [^]c.char,
|
||||
}
|
||||
@ -102,6 +103,7 @@ TextAlignment :: enum EnumBackingType {
|
||||
}
|
||||
|
||||
TextElementConfig :: struct {
|
||||
userData: rawptr,
|
||||
textColor: Color,
|
||||
fontId: u16,
|
||||
fontSize: u16,
|
||||
@ -172,7 +174,7 @@ FloatingElementConfig :: struct {
|
||||
offset: Vector2,
|
||||
expand: Dimensions,
|
||||
parentId: u32,
|
||||
zIndex: i32,
|
||||
zIndex: i16,
|
||||
attachment: FloatingAttachPoints,
|
||||
pointerCaptureMode: PointerCaptureMode,
|
||||
attachTo: FloatingAttachToElement,
|
||||
@ -366,6 +368,8 @@ Context :: struct {} // opaque structure, only use as a pointer
|
||||
|
||||
@(link_prefix = "Clay_", default_calling_convention = "c")
|
||||
foreign Clay {
|
||||
_OpenElement :: proc() ---
|
||||
_CloseElement :: proc() ---
|
||||
MinMemorySize :: proc() -> u32 ---
|
||||
CreateArenaWithCapacityAndMemory :: proc(capacity: c.size_t, offset: [^]u8) -> Arena ---
|
||||
SetPointerState :: proc(position: Vector2, pointerDown: bool) ---
|
||||
@ -398,9 +402,7 @@ foreign Clay {
|
||||
|
||||
@(link_prefix = "Clay_", default_calling_convention = "c", private)
|
||||
foreign Clay {
|
||||
_OpenElement :: proc() ---
|
||||
_ConfigureOpenElement :: proc(config: ElementDeclaration) ---
|
||||
_CloseElement :: proc() ---
|
||||
_HashString :: proc(key: String, offset: u32, seed: u32) -> ElementId ---
|
||||
_OpenTextElement :: proc(text: String, textConfig: ^TextElementConfig) ---
|
||||
_StoreTextElementConfig :: proc(config: TextElementConfig) -> ^TextElementConfig ---
|
||||
@ -418,7 +420,13 @@ UI :: proc() -> proc (config: ElementDeclaration) -> bool {
|
||||
return ConfigureOpenElement
|
||||
}
|
||||
|
||||
Text :: proc(text: string, config: ^TextElementConfig) {
|
||||
Text :: proc($text: string, config: ^TextElementConfig) {
|
||||
wrapped := MakeString(text)
|
||||
wrapped.isStaticallyAllocated = true
|
||||
_OpenTextElement(wrapped, config)
|
||||
}
|
||||
|
||||
TextDynamic :: proc(text: string, config: ^TextElementConfig) {
|
||||
_OpenTextElement(MakeString(text), config)
|
||||
}
|
||||
|
||||
@ -457,3 +465,7 @@ MakeString :: proc(label: string) -> String {
|
||||
ID :: proc(label: string, index: u32 = 0) -> ElementId {
|
||||
return _HashString(MakeString(label), index, 0)
|
||||
}
|
||||
|
||||
ID_LOCAL :: proc(label: string, index: u32 = 0) -> ElementId {
|
||||
return _HashString(MakeString(label), index, _GetParentElementId())
|
||||
}
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -62,7 +62,7 @@ border2pxRed := clay.BorderElementConfig {
|
||||
color = COLOR_RED
|
||||
}
|
||||
|
||||
LandingPageBlob :: proc(index: u32, fontSize: u16, fontId: u16, color: clay.Color, text: string, image: ^raylib.Texture2D) {
|
||||
LandingPageBlob :: proc(index: u32, fontSize: u16, fontId: u16, color: clay.Color, $text: string, image: ^raylib.Texture2D) {
|
||||
if clay.UI()({
|
||||
id = clay.ID("HeroBlob", index),
|
||||
layout = { sizing = { width = clay.SizingGrow({ max = 480 }) }, padding = clay.PaddingAll(16), childGap = 16, childAlignment = clay.ChildAlignment{ y = .Center } },
|
||||
@ -252,7 +252,7 @@ ColorLerp :: proc(a: clay.Color, b: clay.Color, amount: f32) -> clay.Color {
|
||||
return clay.Color{a.r + (b.r - a.r) * amount, a.g + (b.g - a.g) * amount, a.b + (b.b - a.b) * amount, a.a + (b.a - a.a) * amount}
|
||||
}
|
||||
|
||||
LOREM_IPSUM_TEXT := "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
|
||||
LOREM_IPSUM_TEXT :: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
|
||||
|
||||
HighPerformancePage :: proc(lerpValue: f32, titleTextConfig: clay.TextElementConfig, widthSizing: clay.SizingAxis) {
|
||||
if clay.UI()({ id = clay.ID("PerformanceLeftText"), layout = { sizing = { width = widthSizing }, layoutDirection = .TopToBottom, childGap = 8 } }) {
|
||||
@ -321,7 +321,7 @@ HighPerformancePageMobile :: proc(lerpValue: f32) {
|
||||
}
|
||||
}
|
||||
|
||||
RendererButtonActive :: proc(index: i32, text: string) {
|
||||
RendererButtonActive :: proc(index: i32, $text: string) {
|
||||
if clay.UI()({
|
||||
layout = { sizing = { width = clay.SizingFixed(300) }, padding = clay.PaddingAll(16) },
|
||||
backgroundColor = COLOR_RED,
|
||||
@ -331,7 +331,7 @@ RendererButtonActive :: proc(index: i32, text: string) {
|
||||
}
|
||||
}
|
||||
|
||||
RendererButtonInactive :: proc(index: u32, text: string) {
|
||||
RendererButtonInactive :: proc(index: u32, $text: string) {
|
||||
if clay.UI()({ border = border2pxRed }) {
|
||||
if clay.UI()({
|
||||
id = clay.ID("RendererButtonInactiveInner", index),
|
||||
@ -489,7 +489,7 @@ errorHandler :: proc "c" (errorData: clay.ErrorData) {
|
||||
}
|
||||
|
||||
main :: proc() {
|
||||
minMemorySize: u32 = clay.MinMemorySize()
|
||||
minMemorySize: c.size_t = cast(c.size_t)clay.MinMemorySize()
|
||||
memory := make([^]u8, minMemorySize)
|
||||
arena: clay.Arena = clay.CreateArenaWithCapacityAndMemory(minMemorySize, memory)
|
||||
clay.Initialize(arena, {cast(f32)raylib.GetScreenWidth(), cast(f32)raylib.GetScreenHeight()}, { handler = errorHandler })
|
||||
|
276
clay.h
276
clay.h
@ -1,4 +1,4 @@
|
||||
// VERSION: 0.12
|
||||
// VERSION: 0.13
|
||||
|
||||
/*
|
||||
NOTE: In order to use this library you must define
|
||||
@ -96,12 +96,16 @@
|
||||
#define CLAY__ENSURE_STRING_LITERAL(x) ("" x "")
|
||||
|
||||
// Note: If an error led you here, it's because CLAY_STRING can only be used with string literals, i.e. CLAY_STRING("SomeString") and not CLAY_STRING(yourString)
|
||||
#define CLAY_STRING(string) (CLAY__INIT(Clay_String) { .length = CLAY__STRING_LENGTH(CLAY__ENSURE_STRING_LITERAL(string)), .chars = (string) })
|
||||
#define CLAY_STRING(string) (CLAY__INIT(Clay_String) { .isStaticallyAllocated = true, .length = CLAY__STRING_LENGTH(CLAY__ENSURE_STRING_LITERAL(string)), .chars = (string) })
|
||||
|
||||
#define CLAY_STRING_CONST(string) { .length = CLAY__STRING_LENGTH(CLAY__ENSURE_STRING_LITERAL(string)), .chars = (string) }
|
||||
#define CLAY_STRING_CONST(string) { .isStaticallyAllocated = true, .length = CLAY__STRING_LENGTH(CLAY__ENSURE_STRING_LITERAL(string)), .chars = (string) }
|
||||
|
||||
static uint8_t CLAY__ELEMENT_DEFINITION_LATCH;
|
||||
|
||||
// GCC marks the above CLAY__ELEMENT_DEFINITION_LATCH as an unused variable for files that include clay.h but don't declare any layout
|
||||
// This is to suppress that warning
|
||||
static inline void Clay__SuppressUnusedLatchDefinitionVariableWarning(void) { (void) CLAY__ELEMENT_DEFINITION_LATCH; }
|
||||
|
||||
// Publicly visible layout element macros -----------------------------------------------------
|
||||
|
||||
/* This macro looks scary on the surface, but is actually quite simple.
|
||||
@ -181,6 +185,9 @@ extern "C" {
|
||||
// Note: Clay_String is not guaranteed to be null terminated. It may be if created from a literal C string,
|
||||
// but it is also used to represent slices.
|
||||
typedef struct {
|
||||
// Set this boolean to true if the char* data underlying this string will live for the entire lifetime of the program.
|
||||
// This will automatically be set for strings created with CLAY_STRING, as the macro requires a string literal.
|
||||
bool isStaticallyAllocated;
|
||||
int32_t length;
|
||||
// The underlying character memory. Note: this will not be copied and will not extend the lifetime of the underlying memory.
|
||||
const char *chars;
|
||||
@ -266,7 +273,7 @@ typedef CLAY_PACKED_ENUM {
|
||||
CLAY_ALIGN_Y_TOP,
|
||||
// Aligns child elements to the bottom of this element, offset by padding.width.bottom
|
||||
CLAY_ALIGN_Y_BOTTOM,
|
||||
// Aligns child elements vertiically to the center of this element
|
||||
// Aligns child elements vertically to the center of this element
|
||||
CLAY_ALIGN_Y_CENTER,
|
||||
} Clay_LayoutAlignmentY;
|
||||
|
||||
@ -357,6 +364,8 @@ typedef CLAY_PACKED_ENUM {
|
||||
|
||||
// Controls various functionality related to text elements.
|
||||
typedef struct {
|
||||
// A pointer that will be transparently passed through to the resulting render command.
|
||||
void *userData;
|
||||
// The RGBA color of the font to render, conventionally specified as 0-255.
|
||||
Clay_Color textColor;
|
||||
// An integer transparently passed to Clay_MeasureText to identify the font to use.
|
||||
@ -378,10 +387,6 @@ typedef struct {
|
||||
// CLAY_TEXT_ALIGN_CENTER - Horizontally aligns wrapped lines of text to the center of their bounding box.
|
||||
// CLAY_TEXT_ALIGN_RIGHT - Horizontally aligns wrapped lines of text to the right hand side of their bounding box.
|
||||
Clay_TextAlignment textAlignment;
|
||||
// When set to true, clay will hash the entire text contents of this string as an identifier for its internal
|
||||
// text measurement cache, rather than just the pointer and length. This will incur significant performance cost for
|
||||
// long bodies of text.
|
||||
bool hashStringContents;
|
||||
} Clay_TextElementConfig;
|
||||
|
||||
CLAY__WRAPPER_STRUCT(Clay_TextElementConfig);
|
||||
@ -870,14 +875,14 @@ CLAY_DLL_EXPORT int32_t Clay_GetMaxMeasureTextCacheWordCount(void);
|
||||
// Modifies the maximum number of measured "words" (whitespace seperated runs of characters) that Clay can store in its internal text measurement cache.
|
||||
// This may require reallocating additional memory, and re-calling Clay_Initialize();
|
||||
CLAY_DLL_EXPORT void Clay_SetMaxMeasureTextCacheWordCount(int32_t maxMeasureTextCacheWordCount);
|
||||
// Resets Clay's internal text measurement cache, useful if memory to represent strings is being re-used.
|
||||
// Similar behaviour can be achieved on an individual text element level by using Clay_TextElementConfig.hashStringContents
|
||||
// Resets Clay's internal text measurement cache. Useful if font mappings have changed or fonts have been reloaded.
|
||||
CLAY_DLL_EXPORT void Clay_ResetMeasureTextCache(void);
|
||||
|
||||
// Internal API functions required by macros ----------------------
|
||||
|
||||
CLAY_DLL_EXPORT void Clay__OpenElement(void);
|
||||
CLAY_DLL_EXPORT void Clay__ConfigureOpenElement(const Clay_ElementDeclaration config);
|
||||
CLAY_DLL_EXPORT void Clay__ConfigureOpenElementPtr(const Clay_ElementDeclaration *config);
|
||||
CLAY_DLL_EXPORT void Clay__CloseElement(void);
|
||||
CLAY_DLL_EXPORT Clay_ElementId Clay__HashString(Clay_String key, uint32_t offset, uint32_t seed);
|
||||
CLAY_DLL_EXPORT void Clay__OpenTextElement(Clay_String text, Clay_TextElementConfig *textConfig);
|
||||
@ -1149,6 +1154,7 @@ CLAY__ARRAY_DEFINE(Clay__MeasuredWord, Clay__MeasuredWordArray)
|
||||
typedef struct {
|
||||
Clay_Dimensions unwrappedDimensions;
|
||||
int32_t measuredWordsStartIndex;
|
||||
float minWidth;
|
||||
bool containsNewlines;
|
||||
// Hash map data
|
||||
uint32_t id;
|
||||
@ -1340,26 +1346,140 @@ Clay_ElementId Clay__HashString(Clay_String key, const uint32_t offset, const ui
|
||||
return CLAY__INIT(Clay_ElementId) { .id = hash + 1, .offset = offset, .baseId = base + 1, .stringId = key }; // Reserve the hash result of zero as "null id"
|
||||
}
|
||||
|
||||
uint32_t Clay__HashTextWithConfig(Clay_String *text, Clay_TextElementConfig *config) {
|
||||
uint32_t hash = 0;
|
||||
uintptr_t pointerAsNumber = (uintptr_t)text->chars;
|
||||
#if !defined(CLAY_DISABLE_SIMD) && (defined(__x86_64__) || defined(_M_X64) || defined(_M_AMD64))
|
||||
static inline __m128i Clay__SIMDRotateLeft(__m128i x, int r) {
|
||||
return _mm_or_si128(_mm_slli_epi64(x, r), _mm_srli_epi64(x, 64 - r));
|
||||
}
|
||||
|
||||
if (config->hashStringContents) {
|
||||
uint32_t maxLengthToHash = CLAY__MIN(text->length, 256);
|
||||
for (uint32_t i = 0; i < maxLengthToHash; i++) {
|
||||
hash += text->chars[i];
|
||||
hash += (hash << 10);
|
||||
hash ^= (hash >> 6);
|
||||
static inline void Clay__SIMDARXMix(__m128i* a, __m128i* b) {
|
||||
*a = _mm_add_epi64(*a, *b);
|
||||
*b = _mm_xor_si128(Clay__SIMDRotateLeft(*b, 17), *a);
|
||||
}
|
||||
|
||||
uint64_t Clay__HashData(const uint8_t* data, size_t length) {
|
||||
// Pinched these constants from the BLAKE implementation
|
||||
__m128i v0 = _mm_set1_epi64x(0x6a09e667f3bcc908ULL);
|
||||
__m128i v1 = _mm_set1_epi64x(0xbb67ae8584caa73bULL);
|
||||
__m128i v2 = _mm_set1_epi64x(0x3c6ef372fe94f82bULL);
|
||||
__m128i v3 = _mm_set1_epi64x(0xa54ff53a5f1d36f1ULL);
|
||||
|
||||
uint8_t overflowBuffer[16] = { 0 }; // Temporary buffer for small inputs
|
||||
|
||||
while (length > 0) {
|
||||
__m128i msg;
|
||||
if (length >= 16) {
|
||||
msg = _mm_loadu_si128((const __m128i*)data);
|
||||
data += 16;
|
||||
length -= 16;
|
||||
}
|
||||
} else {
|
||||
hash += pointerAsNumber;
|
||||
else {
|
||||
for (int i = 0; i < length; i++) {
|
||||
overflowBuffer[i] = data[i];
|
||||
}
|
||||
msg = _mm_loadu_si128((const __m128i*)overflowBuffer);
|
||||
length = 0;
|
||||
}
|
||||
|
||||
v0 = _mm_xor_si128(v0, msg);
|
||||
Clay__SIMDARXMix(&v0, &v1);
|
||||
Clay__SIMDARXMix(&v2, &v3);
|
||||
|
||||
v0 = _mm_add_epi64(v0, v2);
|
||||
v1 = _mm_add_epi64(v1, v3);
|
||||
}
|
||||
|
||||
Clay__SIMDARXMix(&v0, &v1);
|
||||
Clay__SIMDARXMix(&v2, &v3);
|
||||
v0 = _mm_add_epi64(v0, v2);
|
||||
v1 = _mm_add_epi64(v1, v3);
|
||||
|
||||
uint64_t result[2];
|
||||
_mm_storeu_si128((__m128i*)result, v0);
|
||||
|
||||
return result[0] ^ result[1];
|
||||
}
|
||||
#elif !defined(CLAY_DISABLE_SIMD) && defined(__aarch64__)
|
||||
static inline uint64x2_t Clay__SIMDRotateLeft(uint64x2_t x, int r) {
|
||||
return vorrq_u64(vshlq_n_u64(x, 17), vshrq_n_u64(x, 64 - 17));
|
||||
}
|
||||
|
||||
static inline void Clay__SIMDARXMix(uint64x2_t* a, uint64x2_t* b) {
|
||||
*a = vaddq_u64(*a, *b);
|
||||
*b = veorq_u64(Clay__SIMDRotateLeft(*b, 17), *a);
|
||||
}
|
||||
|
||||
uint64_t Clay__HashData(const uint8_t* data, size_t length) {
|
||||
// Pinched these constants from the BLAKE implementation
|
||||
uint64x2_t v0 = vdupq_n_u64(0x6a09e667f3bcc908ULL);
|
||||
uint64x2_t v1 = vdupq_n_u64(0xbb67ae8584caa73bULL);
|
||||
uint64x2_t v2 = vdupq_n_u64(0x3c6ef372fe94f82bULL);
|
||||
uint64x2_t v3 = vdupq_n_u64(0xa54ff53a5f1d36f1ULL);
|
||||
|
||||
uint8_t overflowBuffer[8] = { 0 };
|
||||
|
||||
while (length > 0) {
|
||||
uint64x2_t msg;
|
||||
if (length > 16) {
|
||||
msg = vld1q_u64((const uint64_t*)data);
|
||||
data += 16;
|
||||
length -= 16;
|
||||
}
|
||||
else if (length > 8) {
|
||||
msg = vcombine_u64(vld1_u64((const uint64_t*)data), vdup_n_u64(0));
|
||||
data += 8;
|
||||
length -= 8;
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < length; i++) {
|
||||
overflowBuffer[i] = data[i];
|
||||
}
|
||||
uint8x8_t lower = vld1_u8(overflowBuffer);
|
||||
msg = vcombine_u8(lower, vdup_n_u8(0));
|
||||
length = 0;
|
||||
}
|
||||
v0 = veorq_u64(v0, msg);
|
||||
Clay__SIMDARXMix(&v0, &v1);
|
||||
Clay__SIMDARXMix(&v2, &v3);
|
||||
|
||||
v0 = vaddq_u64(v0, v2);
|
||||
v1 = vaddq_u64(v1, v3);
|
||||
}
|
||||
|
||||
Clay__SIMDARXMix(&v0, &v1);
|
||||
Clay__SIMDARXMix(&v2, &v3);
|
||||
v0 = vaddq_u64(v0, v2);
|
||||
v1 = vaddq_u64(v1, v3);
|
||||
|
||||
uint64_t result[2];
|
||||
vst1q_u64(result, v0);
|
||||
|
||||
return result[0] ^ result[1];
|
||||
}
|
||||
#else
|
||||
uint64_t Clay__HashData(const uint8_t* data, size_t length) {
|
||||
uint64_t hash = 0;
|
||||
|
||||
for (int32_t i = 0; i < length; i++) {
|
||||
hash += data[i];
|
||||
hash += (hash << 10);
|
||||
hash ^= (hash >> 6);
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
#endif
|
||||
|
||||
hash += text->length;
|
||||
hash += (hash << 10);
|
||||
hash ^= (hash >> 6);
|
||||
uint32_t Clay__HashStringContentsWithConfig(Clay_String *text, Clay_TextElementConfig *config) {
|
||||
uint32_t hash = 0;
|
||||
if (text->isStaticallyAllocated) {
|
||||
hash += (uintptr_t)text->chars;
|
||||
hash += (hash << 10);
|
||||
hash ^= (hash >> 6);
|
||||
hash += text->length;
|
||||
hash += (hash << 10);
|
||||
hash ^= (hash >> 6);
|
||||
} else {
|
||||
hash = Clay__HashData((const uint8_t *)text->chars, text->length) % UINT32_MAX;
|
||||
}
|
||||
|
||||
hash += config->fontId;
|
||||
hash += (hash << 10);
|
||||
@ -1369,18 +1489,10 @@ uint32_t Clay__HashTextWithConfig(Clay_String *text, Clay_TextElementConfig *con
|
||||
hash += (hash << 10);
|
||||
hash ^= (hash >> 6);
|
||||
|
||||
hash += config->lineHeight;
|
||||
hash += (hash << 10);
|
||||
hash ^= (hash >> 6);
|
||||
|
||||
hash += config->letterSpacing;
|
||||
hash += (hash << 10);
|
||||
hash ^= (hash >> 6);
|
||||
|
||||
hash += config->wrapMode;
|
||||
hash += (hash << 10);
|
||||
hash ^= (hash >> 6);
|
||||
|
||||
hash += (hash << 3);
|
||||
hash ^= (hash >> 11);
|
||||
hash += (hash << 15);
|
||||
@ -1415,7 +1527,7 @@ Clay__MeasureTextCacheItem *Clay__MeasureTextCached(Clay_String *text, Clay_Text
|
||||
return &Clay__MeasureTextCacheItem_DEFAULT;
|
||||
}
|
||||
#endif
|
||||
uint32_t id = Clay__HashTextWithConfig(text, config);
|
||||
uint32_t id = Clay__HashStringContentsWithConfig(text, config);
|
||||
uint32_t hashBucket = id % (context->maxMeasureTextCacheWordCount / 32);
|
||||
int32_t elementIndexPrevious = 0;
|
||||
int32_t elementIndex = context->measureTextHashMap.internalArray[hashBucket];
|
||||
@ -1497,6 +1609,7 @@ Clay__MeasureTextCacheItem *Clay__MeasureTextCached(Clay_String *text, Clay_Text
|
||||
if (current == ' ' || current == '\n') {
|
||||
int32_t length = end - start;
|
||||
Clay_Dimensions dimensions = Clay__MeasureText(CLAY__INIT(Clay_StringSlice) { .length = length, .chars = &text->chars[start], .baseChars = text->chars }, config, context->measureTextUserData);
|
||||
measured->minWidth = CLAY__MAX(dimensions.width, measured->minWidth);
|
||||
measuredHeight = CLAY__MAX(measuredHeight, dimensions.height);
|
||||
if (current == ' ') {
|
||||
dimensions.width += spaceWidth;
|
||||
@ -1522,6 +1635,7 @@ Clay__MeasureTextCacheItem *Clay__MeasureTextCached(Clay_String *text, Clay_Text
|
||||
Clay__AddMeasuredWord(CLAY__INIT(Clay__MeasuredWord) { .startOffset = start, .length = end - start, .width = dimensions.width, .next = -1 }, previousWord);
|
||||
lineWidth += dimensions.width;
|
||||
measuredHeight = CLAY__MAX(measuredHeight, dimensions.height);
|
||||
measured->minWidth = CLAY__MAX(dimensions.width, measured->minWidth);
|
||||
}
|
||||
measuredWidth = CLAY__MAX(lineWidth, measuredWidth);
|
||||
|
||||
@ -1651,24 +1765,30 @@ void Clay__CloseElement(void) {
|
||||
elementHasScrollVertical = config->config.scrollElementConfig->vertical;
|
||||
context->openClipElementStack.length--;
|
||||
break;
|
||||
} else if (config->type == CLAY__ELEMENT_CONFIG_TYPE_FLOATING) {
|
||||
context->openClipElementStack.length--;
|
||||
}
|
||||
}
|
||||
|
||||
float leftRightPadding = (float)(layoutConfig->padding.left + layoutConfig->padding.right);
|
||||
float topBottomPadding = (float)(layoutConfig->padding.top + layoutConfig->padding.bottom);
|
||||
|
||||
// Attach children to the current open element
|
||||
openLayoutElement->childrenOrTextContent.children.elements = &context->layoutElementChildren.internalArray[context->layoutElementChildren.length];
|
||||
if (layoutConfig->layoutDirection == CLAY_LEFT_TO_RIGHT) {
|
||||
openLayoutElement->dimensions.width = (float)(layoutConfig->padding.left + layoutConfig->padding.right);
|
||||
openLayoutElement->dimensions.width = leftRightPadding;
|
||||
openLayoutElement->minDimensions.width = leftRightPadding;
|
||||
for (int32_t i = 0; i < openLayoutElement->childrenOrTextContent.children.length; i++) {
|
||||
int32_t childIndex = Clay__int32_tArray_GetValue(&context->layoutElementChildrenBuffer, (int)context->layoutElementChildrenBuffer.length - openLayoutElement->childrenOrTextContent.children.length + i);
|
||||
Clay_LayoutElement *child = Clay_LayoutElementArray_Get(&context->layoutElements, childIndex);
|
||||
openLayoutElement->dimensions.width += child->dimensions.width;
|
||||
openLayoutElement->dimensions.height = CLAY__MAX(openLayoutElement->dimensions.height, child->dimensions.height + layoutConfig->padding.top + layoutConfig->padding.bottom);
|
||||
openLayoutElement->dimensions.height = CLAY__MAX(openLayoutElement->dimensions.height, child->dimensions.height + topBottomPadding);
|
||||
// Minimum size of child elements doesn't matter to scroll containers as they can shrink and hide their contents
|
||||
if (!elementHasScrollHorizontal) {
|
||||
openLayoutElement->minDimensions.width += child->minDimensions.width;
|
||||
}
|
||||
if (!elementHasScrollVertical) {
|
||||
openLayoutElement->minDimensions.height = CLAY__MAX(openLayoutElement->minDimensions.height, child->minDimensions.height + layoutConfig->padding.top + layoutConfig->padding.bottom);
|
||||
openLayoutElement->minDimensions.height = CLAY__MAX(openLayoutElement->minDimensions.height, child->minDimensions.height + topBottomPadding);
|
||||
}
|
||||
Clay__int32_tArray_Add(&context->layoutElementChildren, childIndex);
|
||||
}
|
||||
@ -1677,18 +1797,19 @@ void Clay__CloseElement(void) {
|
||||
openLayoutElement->minDimensions.width += childGap;
|
||||
}
|
||||
else if (layoutConfig->layoutDirection == CLAY_TOP_TO_BOTTOM) {
|
||||
openLayoutElement->dimensions.height = (float)(layoutConfig->padding.top + layoutConfig->padding.bottom);
|
||||
openLayoutElement->dimensions.height = topBottomPadding;
|
||||
openLayoutElement->minDimensions.height = topBottomPadding;
|
||||
for (int32_t i = 0; i < openLayoutElement->childrenOrTextContent.children.length; i++) {
|
||||
int32_t childIndex = Clay__int32_tArray_GetValue(&context->layoutElementChildrenBuffer, (int)context->layoutElementChildrenBuffer.length - openLayoutElement->childrenOrTextContent.children.length + i);
|
||||
Clay_LayoutElement *child = Clay_LayoutElementArray_Get(&context->layoutElements, childIndex);
|
||||
openLayoutElement->dimensions.height += child->dimensions.height;
|
||||
openLayoutElement->dimensions.width = CLAY__MAX(openLayoutElement->dimensions.width, child->dimensions.width + layoutConfig->padding.left + layoutConfig->padding.right);
|
||||
openLayoutElement->dimensions.width = CLAY__MAX(openLayoutElement->dimensions.width, child->dimensions.width + leftRightPadding);
|
||||
// Minimum size of child elements doesn't matter to scroll containers as they can shrink and hide their contents
|
||||
if (!elementHasScrollVertical) {
|
||||
openLayoutElement->minDimensions.height += child->minDimensions.height;
|
||||
}
|
||||
if (!elementHasScrollHorizontal) {
|
||||
openLayoutElement->minDimensions.width = CLAY__MAX(openLayoutElement->minDimensions.width, child->minDimensions.width + layoutConfig->padding.left + layoutConfig->padding.right);
|
||||
openLayoutElement->minDimensions.width = CLAY__MAX(openLayoutElement->minDimensions.width, child->minDimensions.width + leftRightPadding);
|
||||
}
|
||||
Clay__int32_tArray_Add(&context->layoutElementChildren, childIndex);
|
||||
}
|
||||
@ -1769,7 +1890,7 @@ bool Clay__MemCmp(const char *s1, const char *s2, int32_t length);
|
||||
uint8x16_t v2 = vld1q_u8((const uint8_t *)s2);
|
||||
|
||||
// Compare vectors
|
||||
if (vminvq_u32(vceqq_u8(v1, v2)) != 0xFFFFFFFF) { // If there's a difference
|
||||
if (vminvq_u32(vreinterpretq_u32_u8(vceqq_u8(v1, v2))) != 0xFFFFFFFF) { // If there's a difference
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1840,7 +1961,7 @@ void Clay__OpenTextElement(Clay_String text, Clay_TextElementConfig *textConfig)
|
||||
Clay__StringArray_Add(&context->layoutElementIdStrings, elementId.stringId);
|
||||
Clay_Dimensions textDimensions = { .width = textMeasured->unwrappedDimensions.width, .height = textConfig->lineHeight > 0 ? (float)textConfig->lineHeight : textMeasured->unwrappedDimensions.height };
|
||||
textElement->dimensions = textDimensions;
|
||||
textElement->minDimensions = CLAY__INIT(Clay_Dimensions) { .width = textMeasured->unwrappedDimensions.height, .height = textDimensions.height }; // TODO not sure this is the best way to decide min width for text
|
||||
textElement->minDimensions = CLAY__INIT(Clay_Dimensions) { .width = textMeasured->minWidth, .height = textDimensions.height };
|
||||
textElement->childrenOrTextContent.textElementData = Clay__TextElementDataArray_Add(&context->textElementData, CLAY__INIT(Clay__TextElementData) { .text = text, .preferredDimensions = textMeasured->unwrappedDimensions, .elementIndex = context->layoutElements.length - 1 });
|
||||
textElement->elementConfigs = CLAY__INIT(Clay__ElementConfigArraySlice) {
|
||||
.length = 1,
|
||||
@ -1863,58 +1984,58 @@ Clay_ElementId Clay__AttachId(Clay_ElementId elementId) {
|
||||
return elementId;
|
||||
}
|
||||
|
||||
void Clay__ConfigureOpenElement(const Clay_ElementDeclaration declaration) {
|
||||
void Clay__ConfigureOpenElementPtr(const Clay_ElementDeclaration *declaration) {
|
||||
Clay_Context* context = Clay_GetCurrentContext();
|
||||
Clay_LayoutElement *openLayoutElement = Clay__GetOpenLayoutElement();
|
||||
openLayoutElement->layoutConfig = Clay__StoreLayoutConfig(declaration.layout);
|
||||
if ((declaration.layout.sizing.width.type == CLAY__SIZING_TYPE_PERCENT && declaration.layout.sizing.width.size.percent > 1) || (declaration.layout.sizing.height.type == CLAY__SIZING_TYPE_PERCENT && declaration.layout.sizing.height.size.percent > 1)) {
|
||||
openLayoutElement->layoutConfig = Clay__StoreLayoutConfig(declaration->layout);
|
||||
if ((declaration->layout.sizing.width.type == CLAY__SIZING_TYPE_PERCENT && declaration->layout.sizing.width.size.percent > 1) || (declaration->layout.sizing.height.type == CLAY__SIZING_TYPE_PERCENT && declaration->layout.sizing.height.size.percent > 1)) {
|
||||
context->errorHandler.errorHandlerFunction(CLAY__INIT(Clay_ErrorData) {
|
||||
.errorType = CLAY_ERROR_TYPE_PERCENTAGE_OVER_1,
|
||||
.errorText = CLAY_STRING("An element was configured with CLAY_SIZING_PERCENT, but the provided percentage value was over 1.0. Clay expects a value between 0 and 1, i.e. 20% is 0.2."),
|
||||
.userData = context->errorHandler.userData });
|
||||
}
|
||||
|
||||
Clay_ElementId openLayoutElementId = declaration.id;
|
||||
Clay_ElementId openLayoutElementId = declaration->id;
|
||||
|
||||
openLayoutElement->elementConfigs.internalArray = &context->elementConfigs.internalArray[context->elementConfigs.length];
|
||||
Clay_SharedElementConfig *sharedConfig = NULL;
|
||||
if (declaration.backgroundColor.a > 0) {
|
||||
sharedConfig = Clay__StoreSharedElementConfig(CLAY__INIT(Clay_SharedElementConfig) { .backgroundColor = declaration.backgroundColor });
|
||||
if (declaration->backgroundColor.a > 0) {
|
||||
sharedConfig = Clay__StoreSharedElementConfig(CLAY__INIT(Clay_SharedElementConfig) { .backgroundColor = declaration->backgroundColor });
|
||||
Clay__AttachElementConfig(CLAY__INIT(Clay_ElementConfigUnion) { .sharedElementConfig = sharedConfig }, CLAY__ELEMENT_CONFIG_TYPE_SHARED);
|
||||
}
|
||||
if (!Clay__MemCmp((char *)(&declaration.cornerRadius), (char *)(&Clay__CornerRadius_DEFAULT), sizeof(Clay_CornerRadius))) {
|
||||
if (!Clay__MemCmp((char *)(&declaration->cornerRadius), (char *)(&Clay__CornerRadius_DEFAULT), sizeof(Clay_CornerRadius))) {
|
||||
if (sharedConfig) {
|
||||
sharedConfig->cornerRadius = declaration.cornerRadius;
|
||||
sharedConfig->cornerRadius = declaration->cornerRadius;
|
||||
} else {
|
||||
sharedConfig = Clay__StoreSharedElementConfig(CLAY__INIT(Clay_SharedElementConfig) { .cornerRadius = declaration.cornerRadius });
|
||||
sharedConfig = Clay__StoreSharedElementConfig(CLAY__INIT(Clay_SharedElementConfig) { .cornerRadius = declaration->cornerRadius });
|
||||
Clay__AttachElementConfig(CLAY__INIT(Clay_ElementConfigUnion) { .sharedElementConfig = sharedConfig }, CLAY__ELEMENT_CONFIG_TYPE_SHARED);
|
||||
}
|
||||
}
|
||||
if (declaration.userData != 0) {
|
||||
if (declaration->userData != 0) {
|
||||
if (sharedConfig) {
|
||||
sharedConfig->userData = declaration.userData;
|
||||
sharedConfig->userData = declaration->userData;
|
||||
} else {
|
||||
sharedConfig = Clay__StoreSharedElementConfig(CLAY__INIT(Clay_SharedElementConfig) { .userData = declaration.userData });
|
||||
sharedConfig = Clay__StoreSharedElementConfig(CLAY__INIT(Clay_SharedElementConfig) { .userData = declaration->userData });
|
||||
Clay__AttachElementConfig(CLAY__INIT(Clay_ElementConfigUnion) { .sharedElementConfig = sharedConfig }, CLAY__ELEMENT_CONFIG_TYPE_SHARED);
|
||||
}
|
||||
}
|
||||
if (declaration.image.imageData) {
|
||||
Clay__AttachElementConfig(CLAY__INIT(Clay_ElementConfigUnion) { .imageElementConfig = Clay__StoreImageElementConfig(declaration.image) }, CLAY__ELEMENT_CONFIG_TYPE_IMAGE);
|
||||
if (declaration->image.imageData) {
|
||||
Clay__AttachElementConfig(CLAY__INIT(Clay_ElementConfigUnion) { .imageElementConfig = Clay__StoreImageElementConfig(declaration->image) }, CLAY__ELEMENT_CONFIG_TYPE_IMAGE);
|
||||
Clay__int32_tArray_Add(&context->imageElementPointers, context->layoutElements.length - 1);
|
||||
}
|
||||
if (declaration.floating.attachTo != CLAY_ATTACH_TO_NONE) {
|
||||
Clay_FloatingElementConfig floatingConfig = declaration.floating;
|
||||
if (declaration->floating.attachTo != CLAY_ATTACH_TO_NONE) {
|
||||
Clay_FloatingElementConfig floatingConfig = declaration->floating;
|
||||
// This looks dodgy but because of the auto generated root element the depth of the tree will always be at least 2 here
|
||||
Clay_LayoutElement *hierarchicalParent = Clay_LayoutElementArray_Get(&context->layoutElements, Clay__int32_tArray_GetValue(&context->openLayoutElementStack, context->openLayoutElementStack.length - 2));
|
||||
if (hierarchicalParent) {
|
||||
uint32_t clipElementId = 0;
|
||||
if (declaration.floating.attachTo == CLAY_ATTACH_TO_PARENT) {
|
||||
if (declaration->floating.attachTo == CLAY_ATTACH_TO_PARENT) {
|
||||
// Attach to the element's direct hierarchical parent
|
||||
floatingConfig.parentId = hierarchicalParent->id;
|
||||
if (context->openClipElementStack.length > 0) {
|
||||
clipElementId = Clay__int32_tArray_GetValue(&context->openClipElementStack, (int)context->openClipElementStack.length - 1);
|
||||
}
|
||||
} else if (declaration.floating.attachTo == CLAY_ATTACH_TO_ELEMENT_WITH_ID) {
|
||||
} else if (declaration->floating.attachTo == CLAY_ATTACH_TO_ELEMENT_WITH_ID) {
|
||||
Clay_LayoutElementHashMapItem *parentItem = Clay__GetHashMapItem(floatingConfig.parentId);
|
||||
if (!parentItem) {
|
||||
context->errorHandler.errorHandlerFunction(CLAY__INIT(Clay_ErrorData) {
|
||||
@ -1922,14 +2043,17 @@ void Clay__ConfigureOpenElement(const Clay_ElementDeclaration declaration) {
|
||||
.errorText = CLAY_STRING("A floating element was declared with a parentId, but no element with that ID was found."),
|
||||
.userData = context->errorHandler.userData });
|
||||
} else {
|
||||
clipElementId = Clay__int32_tArray_GetValue(&context->layoutElementClipElementIds, parentItem->layoutElement - context->layoutElements.internalArray);
|
||||
clipElementId = Clay__int32_tArray_GetValue(&context->layoutElementClipElementIds, (int32_t)(parentItem->layoutElement - context->layoutElements.internalArray));
|
||||
}
|
||||
} else if (declaration.floating.attachTo == CLAY_ATTACH_TO_ROOT) {
|
||||
} else if (declaration->floating.attachTo == CLAY_ATTACH_TO_ROOT) {
|
||||
floatingConfig.parentId = Clay__HashString(CLAY_STRING("Clay__RootContainer"), 0, 0).id;
|
||||
}
|
||||
if (!openLayoutElementId.id) {
|
||||
openLayoutElementId = Clay__HashString(CLAY_STRING("Clay__FloatingContainer"), context->layoutElementTreeRoots.length, 0);
|
||||
}
|
||||
int32_t currentElementIndex = Clay__int32_tArray_GetValue(&context->openLayoutElementStack, context->openLayoutElementStack.length - 1);
|
||||
Clay__int32_tArray_Set(&context->layoutElementClipElementIds, currentElementIndex, clipElementId);
|
||||
Clay__int32_tArray_Add(&context->openClipElementStack, clipElementId);
|
||||
Clay__LayoutElementTreeRootArray_Add(&context->layoutElementTreeRoots, CLAY__INIT(Clay__LayoutElementTreeRoot) {
|
||||
.layoutElementIndex = Clay__int32_tArray_GetValue(&context->openLayoutElementStack, context->openLayoutElementStack.length - 1),
|
||||
.parentId = floatingConfig.parentId,
|
||||
@ -1939,8 +2063,8 @@ void Clay__ConfigureOpenElement(const Clay_ElementDeclaration declaration) {
|
||||
Clay__AttachElementConfig(CLAY__INIT(Clay_ElementConfigUnion) { .floatingElementConfig = Clay__StoreFloatingElementConfig(floatingConfig) }, CLAY__ELEMENT_CONFIG_TYPE_FLOATING);
|
||||
}
|
||||
}
|
||||
if (declaration.custom.customData) {
|
||||
Clay__AttachElementConfig(CLAY__INIT(Clay_ElementConfigUnion) { .customElementConfig = Clay__StoreCustomElementConfig(declaration.custom) }, CLAY__ELEMENT_CONFIG_TYPE_CUSTOM);
|
||||
if (declaration->custom.customData) {
|
||||
Clay__AttachElementConfig(CLAY__INIT(Clay_ElementConfigUnion) { .customElementConfig = Clay__StoreCustomElementConfig(declaration->custom) }, CLAY__ELEMENT_CONFIG_TYPE_CUSTOM);
|
||||
}
|
||||
|
||||
if (openLayoutElementId.id != 0) {
|
||||
@ -1949,8 +2073,8 @@ void Clay__ConfigureOpenElement(const Clay_ElementDeclaration declaration) {
|
||||
openLayoutElementId = Clay__GenerateIdForAnonymousElement(openLayoutElement);
|
||||
}
|
||||
|
||||
if (declaration.scroll.horizontal | declaration.scroll.vertical) {
|
||||
Clay__AttachElementConfig(CLAY__INIT(Clay_ElementConfigUnion) { .scrollElementConfig = Clay__StoreScrollElementConfig(declaration.scroll) }, CLAY__ELEMENT_CONFIG_TYPE_SCROLL);
|
||||
if (declaration->scroll.horizontal | declaration->scroll.vertical) {
|
||||
Clay__AttachElementConfig(CLAY__INIT(Clay_ElementConfigUnion) { .scrollElementConfig = Clay__StoreScrollElementConfig(declaration->scroll) }, CLAY__ELEMENT_CONFIG_TYPE_SCROLL);
|
||||
Clay__int32_tArray_Add(&context->openClipElementStack, (int)openLayoutElement->id);
|
||||
// Retrieve or create cached data to track scroll position across frames
|
||||
Clay__ScrollContainerDataInternal *scrollOffset = CLAY__NULL;
|
||||
@ -1969,11 +2093,15 @@ void Clay__ConfigureOpenElement(const Clay_ElementDeclaration declaration) {
|
||||
scrollOffset->scrollPosition = Clay__QueryScrollOffset(scrollOffset->elementId, context->queryScrollOffsetUserData);
|
||||
}
|
||||
}
|
||||
if (!Clay__MemCmp((char *)(&declaration.border.width), (char *)(&Clay__BorderWidth_DEFAULT), sizeof(Clay_BorderWidth))) {
|
||||
Clay__AttachElementConfig(CLAY__INIT(Clay_ElementConfigUnion) { .borderElementConfig = Clay__StoreBorderElementConfig(declaration.border) }, CLAY__ELEMENT_CONFIG_TYPE_BORDER);
|
||||
if (!Clay__MemCmp((char *)(&declaration->border.width), (char *)(&Clay__BorderWidth_DEFAULT), sizeof(Clay_BorderWidth))) {
|
||||
Clay__AttachElementConfig(CLAY__INIT(Clay_ElementConfigUnion) { .borderElementConfig = Clay__StoreBorderElementConfig(declaration->border) }, CLAY__ELEMENT_CONFIG_TYPE_BORDER);
|
||||
}
|
||||
}
|
||||
|
||||
void Clay__ConfigureOpenElement(const Clay_ElementDeclaration declaration) {
|
||||
Clay__ConfigureOpenElementPtr(&declaration);
|
||||
}
|
||||
|
||||
void Clay__InitializeEphemeralMemory(Clay_Context* context) {
|
||||
int32_t maxElementCount = context->maxElementCount;
|
||||
// Ephemeral Memory - reset every frame
|
||||
@ -2221,25 +2349,25 @@ void Clay__SizeContainersAlongAxis(bool xAxis) {
|
||||
for (int32_t childOffset = 0; childOffset < resizableContainerBuffer.length; childOffset++) {
|
||||
Clay_LayoutElement *childElement = Clay_LayoutElementArray_Get(&context->layoutElements, Clay__int32_tArray_GetValue(&resizableContainerBuffer, childOffset));
|
||||
Clay_SizingAxis childSizing = xAxis ? childElement->layoutConfig->sizing.width : childElement->layoutConfig->sizing.height;
|
||||
float minSize = xAxis ? childElement->minDimensions.width : childElement->minDimensions.height;
|
||||
float *childSize = xAxis ? &childElement->dimensions.width : &childElement->dimensions.height;
|
||||
|
||||
if (!xAxis && Clay__ElementHasConfig(childElement, CLAY__ELEMENT_CONFIG_TYPE_IMAGE)) {
|
||||
continue; // Currently we don't support resizing aspect ratio images on the Y axis because it would break the ratio
|
||||
}
|
||||
|
||||
// If we're laying out the children of a scroll panel, grow containers expand to the height of the inner content, not the outer container
|
||||
float maxSize = parentSize - parentPadding;
|
||||
// If we're laying out the children of a scroll panel, grow containers expand to the size of the inner content, not the outer container
|
||||
if (Clay__ElementHasConfig(parent, CLAY__ELEMENT_CONFIG_TYPE_SCROLL)) {
|
||||
Clay_ScrollElementConfig *scrollElementConfig = Clay__FindElementConfigWithType(parent, CLAY__ELEMENT_CONFIG_TYPE_SCROLL).scrollElementConfig;
|
||||
if (((xAxis && scrollElementConfig->horizontal) || (!xAxis && scrollElementConfig->vertical))) {
|
||||
maxSize = CLAY__MAX(maxSize, innerContentSize);
|
||||
}
|
||||
}
|
||||
if (childSizing.type == CLAY__SIZING_TYPE_FIT) {
|
||||
*childSize = CLAY__MAX(childSizing.size.minMax.min, CLAY__MIN(*childSize, maxSize));
|
||||
} else if (childSizing.type == CLAY__SIZING_TYPE_GROW) {
|
||||
if (childSizing.type == CLAY__SIZING_TYPE_GROW) {
|
||||
*childSize = CLAY__MIN(maxSize, childSizing.size.minMax.max);
|
||||
}
|
||||
*childSize = CLAY__MAX(minSize, CLAY__MIN(*childSize, maxSize));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2692,7 +2820,7 @@ void Clay__CalculateFinalLayout(void) {
|
||||
.letterSpacing = textElementConfig->letterSpacing,
|
||||
.lineHeight = textElementConfig->lineHeight,
|
||||
}},
|
||||
.userData = sharedConfig->userData,
|
||||
.userData = textElementConfig->userData,
|
||||
.id = Clay__HashNumber(lineIndex, currentElement->id).id,
|
||||
.zIndex = root->zIndex,
|
||||
.commandType = CLAY_RENDER_COMMAND_TYPE_TEXT,
|
||||
@ -3627,7 +3755,7 @@ uint32_t Clay_MinMemorySize(void) {
|
||||
Clay__Context_Allocate_Arena(&fakeContext.internalArena);
|
||||
Clay__InitializePersistentMemory(&fakeContext);
|
||||
Clay__InitializeEphemeralMemory(&fakeContext);
|
||||
return fakeContext.internalArena.nextAllocation + 128;
|
||||
return (uint32_t)fakeContext.internalArena.nextAllocation + 128;
|
||||
}
|
||||
|
||||
CLAY_WASM_EXPORT("Clay_CreateArenaWithCapacityAndMemory")
|
||||
@ -3680,7 +3808,7 @@ void Clay_SetPointerState(Clay_Vector2 position, bool isPointerDown) {
|
||||
context->treeNodeVisited.internalArray[dfsBuffer.length - 1] = true;
|
||||
Clay_LayoutElement *currentElement = Clay_LayoutElementArray_Get(&context->layoutElements, Clay__int32_tArray_GetValue(&dfsBuffer, (int)dfsBuffer.length - 1));
|
||||
Clay_LayoutElementHashMapItem *mapItem = Clay__GetHashMapItem(currentElement->id); // TODO think of a way around this, maybe the fact that it's essentially a binary tree limits the cost, but the worst case is not great
|
||||
int32_t clipElementId = Clay__int32_tArray_GetValue(&context->layoutElementClipElementIds, currentElement - context->layoutElements.internalArray);
|
||||
int32_t clipElementId = Clay__int32_tArray_GetValue(&context->layoutElementClipElementIds, (int32_t)(currentElement - context->layoutElements.internalArray));
|
||||
Clay_LayoutElementHashMapItem *clipItem = Clay__GetHashMapItem(clipElementId);
|
||||
Clay_BoundingBox elementBox = mapItem->boundingBox;
|
||||
elementBox.x -= root->pointerOffset.x;
|
||||
|
@ -17,6 +17,41 @@ void HandleClayErrors(Clay_ErrorData errorData) {
|
||||
printf("%s", errorData.errorText.chars);
|
||||
}
|
||||
|
||||
|
||||
struct ResizeRenderData_ {
|
||||
SDL_Window* window;
|
||||
int windowWidth;
|
||||
int windowHeight;
|
||||
ClayVideoDemo_Data demoData;
|
||||
SDL_Renderer* renderer;
|
||||
SDL2_Font* fonts;
|
||||
};
|
||||
typedef struct ResizeRenderData_ ResizeRenderData;
|
||||
|
||||
int resizeRendering(void* userData, SDL_Event* event) {
|
||||
ResizeRenderData *actualData = userData;
|
||||
if (event->type == SDL_WINDOWEVENT && event->window.event == SDL_WINDOWEVENT_EXPOSED) {
|
||||
SDL_Window* window = actualData->window;
|
||||
int windowWidth = actualData->windowWidth;
|
||||
int windowHeight = actualData->windowHeight;
|
||||
ClayVideoDemo_Data demoData = actualData->demoData;
|
||||
SDL_Renderer* renderer = actualData->renderer;
|
||||
SDL2_Font* fonts = actualData->fonts;
|
||||
|
||||
SDL_GetWindowSize(window, &windowWidth, &windowHeight);
|
||||
Clay_SetLayoutDimensions((Clay_Dimensions) { (float)windowWidth, (float)windowHeight });
|
||||
|
||||
Clay_RenderCommandArray renderCommands = ClayVideoDemo_CreateLayout(&demoData);
|
||||
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
|
||||
SDL_RenderClear(renderer);
|
||||
|
||||
Clay_SDL2_Render(renderer, renderCommands, fonts);
|
||||
|
||||
SDL_RenderPresent(renderer);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
|
||||
fprintf(stderr, "Error: could not initialize SDL: %s\n", SDL_GetError());
|
||||
@ -73,6 +108,18 @@ int main(int argc, char *argv[]) {
|
||||
double deltaTime = 0;
|
||||
ClayVideoDemo_Data demoData = ClayVideoDemo_Initialize();
|
||||
|
||||
|
||||
ResizeRenderData userData = {
|
||||
window, // SDL_Window*
|
||||
windowWidth, // int
|
||||
windowHeight, // int
|
||||
demoData, // CustomShit
|
||||
renderer, // SDL_Renderer*
|
||||
fonts // SDL2_Font[1]
|
||||
};
|
||||
// add an event watcher that will render the screen while youre dragging the window to different sizes
|
||||
SDL_AddEventWatch(resizeRendering, &userData);
|
||||
|
||||
while (true) {
|
||||
Clay_Vector2 scrollDelta = {};
|
||||
SDL_Event event;
|
||||
|
@ -119,6 +119,7 @@
|
||||
{name: 'bottomRight', type: 'float'},
|
||||
]};
|
||||
let textConfigDefinition = { name: 'text', type: 'struct', members: [
|
||||
{ name: 'userData', type: 'uint32_t' },
|
||||
{ name: 'textColor', ...colorDefinition },
|
||||
{ name: 'fontId', type: 'uint16_t' },
|
||||
{ name: 'fontSize', type: 'uint16_t' },
|
||||
|
Binary file not shown.
@ -119,6 +119,7 @@
|
||||
{name: 'bottomRight', type: 'float'},
|
||||
]};
|
||||
let textConfigDefinition = { name: 'text', type: 'struct', members: [
|
||||
{ name: 'userData', type: 'uint32_t' },
|
||||
{ name: 'textColor', ...colorDefinition },
|
||||
{ name: 'fontId', type: 'uint16_t' },
|
||||
{ name: 'fontSize', type: 'uint16_t' },
|
||||
|
@ -50,4 +50,6 @@ int main(void) {
|
||||
Clay_Raylib_Render(renderCommands, fonts);
|
||||
EndDrawing();
|
||||
}
|
||||
// This function is new since the video was published
|
||||
Clay_Raylib_Close();
|
||||
}
|
||||
|
@ -72,4 +72,6 @@ int main(void) {
|
||||
Clay_Raylib_Render(renderCommandsBottom, fonts);
|
||||
EndDrawing();
|
||||
}
|
||||
|
||||
Clay_Raylib_Close();
|
||||
}
|
||||
|
@ -250,5 +250,6 @@ int main(void) {
|
||||
}
|
||||
UpdateDrawFrame(fonts);
|
||||
}
|
||||
Clay_Raylib_Close();
|
||||
return 0;
|
||||
}
|
||||
|
4
examples/win32_gdi/build.ps1
Normal file
4
examples/win32_gdi/build.ps1
Normal file
@ -0,0 +1,4 @@
|
||||
|
||||
# to build this, install mingw
|
||||
|
||||
gcc main.c -ggdb -omain -lgdi32 -lmingw32 # -mwindows # comment -mwindows out for console output
|
218
examples/win32_gdi/main.c
Normal file
218
examples/win32_gdi/main.c
Normal file
@ -0,0 +1,218 @@
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <windowsx.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include "../../renderers/win32_gdi/clay_renderer_gdi.c"
|
||||
|
||||
#define CLAY_IMPLEMENTATION
|
||||
#include "../../clay.h"
|
||||
|
||||
#include "../shared-layouts/clay-video-demo.c"
|
||||
|
||||
ClayVideoDemo_Data demo_data;
|
||||
|
||||
#define APPNAME "Clay GDI Example"
|
||||
char szAppName[] = APPNAME; // The name of this application
|
||||
char szTitle[] = APPNAME; // The title bar text
|
||||
|
||||
void CenterWindow(HWND hWnd);
|
||||
|
||||
long lastMsgTime = 0;
|
||||
bool ui_debug_mode;
|
||||
|
||||
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
|
||||
switch (message)
|
||||
{
|
||||
|
||||
// ----------------------- first and last
|
||||
case WM_CREATE:
|
||||
CenterWindow(hwnd);
|
||||
break;
|
||||
|
||||
case WM_DESTROY:
|
||||
PostQuitMessage(0);
|
||||
break;
|
||||
|
||||
case WM_MOUSEWHEEL: // scrolling data
|
||||
{
|
||||
short zDelta = GET_WHEEL_DELTA_WPARAM(wParam);
|
||||
// todo: i think GetMessageTime can roll over, so something like if(lastmsgtime > now) ... may be needed
|
||||
long now = GetMessageTime();
|
||||
float dt = (now - lastMsgTime) / 1000.00;
|
||||
|
||||
lastMsgTime = now;
|
||||
|
||||
// little hacky hack to make scrolling *feel* right
|
||||
if (abs(zDelta) > 100)
|
||||
{
|
||||
zDelta = zDelta * .012;
|
||||
}
|
||||
|
||||
Clay_UpdateScrollContainers(true, (Clay_Vector2){.x = 0, .y = zDelta}, dt);
|
||||
|
||||
InvalidateRect(hwnd, NULL, false); // force a wm_paint event
|
||||
break;
|
||||
}
|
||||
case WM_RBUTTONUP:
|
||||
case WM_LBUTTONUP:
|
||||
case WM_LBUTTONDOWN:
|
||||
case WM_RBUTTONDOWN:
|
||||
case WM_MOUSEMOVE: // mouse events
|
||||
{
|
||||
short mouseX = GET_X_LPARAM(lParam);
|
||||
short mouseY = GET_Y_LPARAM(lParam);
|
||||
short mouseButtons = LOWORD(wParam);
|
||||
|
||||
Clay_SetPointerState((Clay_Vector2){mouseX, mouseY}, mouseButtons & 0b01);
|
||||
|
||||
InvalidateRect(hwnd, NULL, false); // force a wm_paint event
|
||||
break;
|
||||
}
|
||||
|
||||
case WM_SIZE: // resize events
|
||||
{
|
||||
|
||||
RECT r = {0};
|
||||
if (GetClientRect(hwnd, &r))
|
||||
{
|
||||
Clay_Dimensions dim = (Clay_Dimensions){.height = r.bottom - r.top, .width = r.right - r.left};
|
||||
Clay_SetLayoutDimensions(dim);
|
||||
}
|
||||
|
||||
InvalidateRect(hwnd, NULL, false); // force a wm_paint event
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case WM_KEYDOWN:
|
||||
if (VK_ESCAPE == wParam)
|
||||
{
|
||||
DestroyWindow(hwnd);
|
||||
break;
|
||||
}
|
||||
|
||||
if (wParam == VK_F12)
|
||||
{
|
||||
Clay_SetDebugModeEnabled(ui_debug_mode = !ui_debug_mode);
|
||||
break;
|
||||
}
|
||||
|
||||
printf("Key Pressed: %d\r\n", wParam);
|
||||
InvalidateRect(hwnd, NULL, false); // force a wm_paint event
|
||||
break;
|
||||
|
||||
// ----------------------- render
|
||||
case WM_PAINT:
|
||||
{
|
||||
Clay_RenderCommandArray renderCommands = ClayVideoDemo_CreateLayout(&demo_data);
|
||||
Clay_Win32_Render(hwnd, renderCommands);
|
||||
break;
|
||||
}
|
||||
|
||||
// ----------------------- let windows do all other stuff
|
||||
default:
|
||||
return DefWindowProc(hwnd, message, wParam, lParam);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool didAllocConsole = false;
|
||||
|
||||
void HandleClayErrors(Clay_ErrorData errorData)
|
||||
{
|
||||
if (!didAllocConsole)
|
||||
{
|
||||
didAllocConsole = AllocConsole();
|
||||
}
|
||||
|
||||
printf("Handle Clay Errors: %s\r\n", errorData.errorText.chars);
|
||||
}
|
||||
|
||||
int APIENTRY WinMain(
|
||||
HINSTANCE hInstance,
|
||||
HINSTANCE hPrevInstance,
|
||||
LPSTR lpCmdLine,
|
||||
int nCmdShow)
|
||||
{
|
||||
MSG msg;
|
||||
WNDCLASS wc;
|
||||
HWND hwnd;
|
||||
|
||||
demo_data = ClayVideoDemo_Initialize();
|
||||
|
||||
uint64_t clayRequiredMemory = Clay_MinMemorySize();
|
||||
Clay_Arena clayMemory = Clay_CreateArenaWithCapacityAndMemory(clayRequiredMemory, malloc(clayRequiredMemory));
|
||||
Clay_Initialize(clayMemory, (Clay_Dimensions){.width = 800, .height = 600}, (Clay_ErrorHandler){HandleClayErrors}); // This final argument is new since the video was published
|
||||
Clay_SetMeasureTextFunction(Clay_Win32_MeasureText, NULL);
|
||||
|
||||
ZeroMemory(&wc, sizeof wc);
|
||||
wc.hInstance = hInstance;
|
||||
wc.lpszClassName = szAppName;
|
||||
wc.lpfnWndProc = (WNDPROC)WndProc;
|
||||
wc.style = CS_DBLCLKS | CS_VREDRAW | CS_HREDRAW;
|
||||
wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
|
||||
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
|
||||
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
|
||||
|
||||
if (FALSE == RegisterClass(&wc))
|
||||
return 0;
|
||||
|
||||
|
||||
hwnd = CreateWindow(
|
||||
szAppName,
|
||||
szTitle,
|
||||
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
|
||||
CW_USEDEFAULT,
|
||||
CW_USEDEFAULT,
|
||||
800, // CW_USEDEFAULT,
|
||||
600, // CW_USEDEFAULT,
|
||||
0,
|
||||
0,
|
||||
hInstance,
|
||||
0);
|
||||
|
||||
if (hwnd == NULL)
|
||||
return 0;
|
||||
|
||||
// Main message loop:
|
||||
while (GetMessage(&msg, NULL, 0, 0) > 0)
|
||||
{
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
|
||||
return msg.wParam;
|
||||
}
|
||||
|
||||
void CenterWindow(HWND hwnd_self)
|
||||
{
|
||||
HWND hwnd_parent;
|
||||
RECT rw_self, rc_parent, rw_parent;
|
||||
int xpos, ypos;
|
||||
|
||||
hwnd_parent = GetParent(hwnd_self);
|
||||
if (NULL == hwnd_parent)
|
||||
hwnd_parent = GetDesktopWindow();
|
||||
|
||||
GetWindowRect(hwnd_parent, &rw_parent);
|
||||
GetClientRect(hwnd_parent, &rc_parent);
|
||||
GetWindowRect(hwnd_self, &rw_self);
|
||||
|
||||
xpos = rw_parent.left + (rc_parent.right + rw_self.left - rw_self.right) / 2;
|
||||
ypos = rw_parent.top + (rc_parent.bottom + rw_self.top - rw_self.bottom) / 2;
|
||||
|
||||
SetWindowPos(
|
||||
hwnd_self, NULL,
|
||||
xpos, ypos, 0, 0,
|
||||
SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
|
||||
}
|
||||
|
||||
//+---------------------------------------------------------------------------
|
@ -3,6 +3,7 @@
|
||||
#include <SDL_ttf.h>
|
||||
#include <SDL_image.h>
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.14159
|
||||
@ -408,7 +409,7 @@ static void Clay_SDL2_Render(SDL_Renderer *renderer, Clay_RenderCommandArray ren
|
||||
SDL_RenderCornerBorder(renderer, &boundingBox, config, 1, config->color);
|
||||
}
|
||||
|
||||
if (config->width.bottom > 0 & config->cornerRadius.bottomLeft > 0) {
|
||||
if (config->width.bottom > 0 & config->cornerRadius.bottomRight > 0) {
|
||||
SDL_RenderCornerBorder(renderer, &boundingBox, config, 2, config->color);
|
||||
}
|
||||
|
||||
|
@ -125,6 +125,21 @@ void Clay_Raylib_Initialize(int width, int height, const char *title, unsigned i
|
||||
// EnableEventWaiting();
|
||||
}
|
||||
|
||||
// A MALLOC'd buffer, that we keep modifying inorder to save from so many Malloc and Free Calls.
|
||||
// Call Clay_Raylib_Close() to free
|
||||
static char *temp_render_buffer = NULL;
|
||||
static int temp_render_buffer_len = 0;
|
||||
|
||||
// Call after closing the window to clean up the render buffer
|
||||
void Clay_Raylib_Close()
|
||||
{
|
||||
if(temp_render_buffer) free(temp_render_buffer);
|
||||
temp_render_buffer_len = 0;
|
||||
|
||||
CloseWindow();
|
||||
}
|
||||
|
||||
|
||||
void Clay_Raylib_Render(Clay_RenderCommandArray renderCommands, Font* fonts)
|
||||
{
|
||||
for (int j = 0; j < renderCommands.length; j++)
|
||||
@ -134,14 +149,23 @@ void Clay_Raylib_Render(Clay_RenderCommandArray renderCommands, Font* fonts)
|
||||
switch (renderCommand->commandType)
|
||||
{
|
||||
case CLAY_RENDER_COMMAND_TYPE_TEXT: {
|
||||
// Raylib uses standard C strings so isn't compatible with cheap slices, we need to clone the string to append null terminator
|
||||
Clay_TextRenderData *textData = &renderCommand->renderData.text;
|
||||
char *cloned = (char *)malloc(textData->stringContents.length + 1);
|
||||
memcpy(cloned, textData->stringContents.chars, textData->stringContents.length);
|
||||
cloned[textData->stringContents.length] = '\0';
|
||||
Font fontToUse = fonts[textData->fontId];
|
||||
DrawTextEx(fontToUse, cloned, (Vector2){boundingBox.x, boundingBox.y}, (float)textData->fontSize, (float)textData->letterSpacing, CLAY_COLOR_TO_RAYLIB_COLOR(textData->textColor));
|
||||
free(cloned);
|
||||
|
||||
int strlen = textData->stringContents.length + 1;
|
||||
|
||||
if(strlen > temp_render_buffer_len) {
|
||||
// Grow the temp buffer if we need a larger string
|
||||
if(temp_render_buffer) free(temp_render_buffer);
|
||||
temp_render_buffer = malloc(strlen);
|
||||
temp_render_buffer_len = strlen;
|
||||
}
|
||||
|
||||
// Raylib uses standard C strings so isn't compatible with cheap slices, we need to clone the string to append null terminator
|
||||
memcpy(temp_render_buffer, textData->stringContents.chars, textData->stringContents.length);
|
||||
temp_render_buffer[textData->stringContents.length] = '\0';
|
||||
DrawTextEx(fontToUse, temp_render_buffer, (Vector2){boundingBox.x, boundingBox.y}, (float)textData->fontSize, (float)textData->letterSpacing, CLAY_COLOR_TO_RAYLIB_COLOR(textData->textColor));
|
||||
|
||||
break;
|
||||
}
|
||||
case CLAY_RENDER_COMMAND_TYPE_IMAGE: {
|
||||
|
5
renderers/win32_gdi/README.md
Normal file
5
renderers/win32_gdi/README.md
Normal file
@ -0,0 +1,5 @@
|
||||
The windows GDI renderer example is missing the following:
|
||||
|
||||
- Images
|
||||
- Rendering Rounded Rectangle borders
|
||||
- Custom Fonts (font size)
|
241
renderers/win32_gdi/clay_renderer_gdi.c
Normal file
241
renderers/win32_gdi/clay_renderer_gdi.c
Normal file
@ -0,0 +1,241 @@
|
||||
#include <Windows.h>
|
||||
#include "../../clay.h"
|
||||
|
||||
HDC renderer_hdcMem = {0};
|
||||
HBITMAP renderer_hbmMem = {0};
|
||||
HANDLE renderer_hOld = {0};
|
||||
|
||||
void Clay_Win32_Render(HWND hwnd, Clay_RenderCommandArray renderCommands)
|
||||
{
|
||||
bool is_clipping = false;
|
||||
HRGN clipping_region = {0};
|
||||
|
||||
PAINTSTRUCT ps;
|
||||
HDC hdc;
|
||||
RECT rc; // Top left of our window
|
||||
|
||||
GetWindowRect(hwnd, &rc);
|
||||
|
||||
hdc = BeginPaint(hwnd, &ps);
|
||||
|
||||
int win_width = rc.right - rc.left,
|
||||
win_height = rc.bottom - rc.top;
|
||||
|
||||
// Create an off-screen DC for double-buffering
|
||||
renderer_hdcMem = CreateCompatibleDC(hdc);
|
||||
renderer_hbmMem = CreateCompatibleBitmap(hdc, win_width, win_height);
|
||||
|
||||
renderer_hOld = SelectObject(renderer_hdcMem, renderer_hbmMem);
|
||||
|
||||
// draw
|
||||
|
||||
for (int j = 0; j < renderCommands.length; j++)
|
||||
{
|
||||
Clay_RenderCommand *renderCommand = Clay_RenderCommandArray_Get(&renderCommands, j);
|
||||
Clay_BoundingBox boundingBox = renderCommand->boundingBox;
|
||||
|
||||
switch (renderCommand->commandType)
|
||||
{
|
||||
case CLAY_RENDER_COMMAND_TYPE_TEXT:
|
||||
{
|
||||
Clay_Color c = renderCommand->renderData.text.textColor;
|
||||
SetTextColor(renderer_hdcMem, RGB(c.r, c.g, c.b));
|
||||
SetBkMode(renderer_hdcMem, TRANSPARENT);
|
||||
|
||||
RECT r = rc;
|
||||
r.left = boundingBox.x;
|
||||
r.top = boundingBox.y;
|
||||
r.right = boundingBox.x + boundingBox.width + r.right;
|
||||
r.bottom = boundingBox.y + boundingBox.height + r.bottom;
|
||||
|
||||
DrawTextA(renderer_hdcMem, renderCommand->renderData.text.stringContents.chars,
|
||||
renderCommand->renderData.text.stringContents.length,
|
||||
&r, DT_TOP | DT_LEFT);
|
||||
|
||||
break;
|
||||
}
|
||||
case CLAY_RENDER_COMMAND_TYPE_RECTANGLE:
|
||||
{
|
||||
Clay_RectangleRenderData rrd = renderCommand->renderData.rectangle;
|
||||
RECT r = rc;
|
||||
|
||||
r.left = boundingBox.x;
|
||||
r.top = boundingBox.y;
|
||||
r.right = boundingBox.x + boundingBox.width;
|
||||
r.bottom = boundingBox.y + boundingBox.height;
|
||||
|
||||
HBRUSH recColor = CreateSolidBrush(RGB(rrd.backgroundColor.r, rrd.backgroundColor.g, rrd.backgroundColor.b));
|
||||
|
||||
if (rrd.cornerRadius.topLeft > 0)
|
||||
{
|
||||
HRGN roundedRectRgn = CreateRoundRectRgn(
|
||||
r.left, r.top, r.right + 1, r.bottom + 1,
|
||||
rrd.cornerRadius.topLeft * 2, rrd.cornerRadius.topLeft * 2);
|
||||
|
||||
FillRgn(renderer_hdcMem, roundedRectRgn, recColor);
|
||||
DeleteObject(roundedRectRgn);
|
||||
}
|
||||
else
|
||||
{
|
||||
FillRect(renderer_hdcMem, &r, recColor);
|
||||
}
|
||||
|
||||
DeleteObject(recColor);
|
||||
break;
|
||||
}
|
||||
|
||||
// The renderer should begin clipping all future draw commands, only rendering content that falls within the provided boundingBox.
|
||||
case CLAY_RENDER_COMMAND_TYPE_SCISSOR_START:
|
||||
{
|
||||
is_clipping = true;
|
||||
|
||||
clipping_region = CreateRectRgn(boundingBox.x,
|
||||
boundingBox.y,
|
||||
boundingBox.x + boundingBox.width,
|
||||
boundingBox.y + boundingBox.height);
|
||||
|
||||
SelectClipRgn(renderer_hdcMem, clipping_region);
|
||||
break;
|
||||
}
|
||||
|
||||
// The renderer should finish any previously active clipping, and begin rendering elements in full again.
|
||||
case CLAY_RENDER_COMMAND_TYPE_SCISSOR_END:
|
||||
{
|
||||
SelectClipRgn(renderer_hdcMem, NULL);
|
||||
|
||||
if (clipping_region)
|
||||
{
|
||||
DeleteObject(clipping_region);
|
||||
}
|
||||
|
||||
is_clipping = false;
|
||||
clipping_region = NULL;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// The renderer should draw a colored border inset into the bounding box.
|
||||
case CLAY_RENDER_COMMAND_TYPE_BORDER:
|
||||
{
|
||||
Clay_BorderRenderData brd = renderCommand->renderData.border;
|
||||
RECT r = rc;
|
||||
|
||||
r.left = boundingBox.x;
|
||||
r.top = boundingBox.y;
|
||||
r.right = boundingBox.x + boundingBox.width;
|
||||
r.bottom = boundingBox.y + boundingBox.height;
|
||||
|
||||
HPEN topPen = CreatePen(PS_SOLID, brd.width.top, RGB(brd.color.r, brd.color.g, brd.color.b));
|
||||
HPEN leftPen = CreatePen(PS_SOLID, brd.width.left, RGB(brd.color.r, brd.color.g, brd.color.b));
|
||||
HPEN bottomPen = CreatePen(PS_SOLID, brd.width.bottom, RGB(brd.color.r, brd.color.g, brd.color.b));
|
||||
HPEN rightPen = CreatePen(PS_SOLID, brd.width.right, RGB(brd.color.r, brd.color.g, brd.color.b));
|
||||
|
||||
HPEN oldPen = SelectObject(renderer_hdcMem, topPen);
|
||||
|
||||
if (brd.cornerRadius.topLeft == 0)
|
||||
{
|
||||
MoveToEx(renderer_hdcMem, r.left, r.top, NULL);
|
||||
LineTo(renderer_hdcMem, r.right, r.top);
|
||||
|
||||
SelectObject(renderer_hdcMem, leftPen);
|
||||
MoveToEx(renderer_hdcMem, r.left, r.top, NULL);
|
||||
LineTo(renderer_hdcMem, r.left, r.bottom);
|
||||
|
||||
SelectObject(renderer_hdcMem, bottomPen);
|
||||
MoveToEx(renderer_hdcMem, r.left, r.bottom, NULL);
|
||||
LineTo(renderer_hdcMem, r.right, r.bottom);
|
||||
|
||||
SelectObject(renderer_hdcMem, rightPen);
|
||||
MoveToEx(renderer_hdcMem, r.right, r.top, NULL);
|
||||
LineTo(renderer_hdcMem, r.right, r.bottom);
|
||||
}
|
||||
else
|
||||
{
|
||||
// todo: i should be rounded
|
||||
MoveToEx(renderer_hdcMem, r.left, r.top, NULL);
|
||||
LineTo(renderer_hdcMem, r.right, r.top);
|
||||
|
||||
SelectObject(renderer_hdcMem, leftPen);
|
||||
MoveToEx(renderer_hdcMem, r.left, r.top, NULL);
|
||||
LineTo(renderer_hdcMem, r.left, r.bottom);
|
||||
|
||||
SelectObject(renderer_hdcMem, bottomPen);
|
||||
MoveToEx(renderer_hdcMem, r.left, r.bottom, NULL);
|
||||
LineTo(renderer_hdcMem, r.right, r.bottom);
|
||||
|
||||
SelectObject(renderer_hdcMem, rightPen);
|
||||
MoveToEx(renderer_hdcMem, r.right, r.top, NULL);
|
||||
LineTo(renderer_hdcMem, r.right, r.bottom);
|
||||
|
||||
}
|
||||
|
||||
SelectObject(renderer_hdcMem, oldPen);
|
||||
DeleteObject(topPen);
|
||||
DeleteObject(leftPen);
|
||||
DeleteObject(bottomPen);
|
||||
DeleteObject(rightPen);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// case CLAY_RENDER_COMMAND_TYPE_IMAGE:
|
||||
// {
|
||||
// // TODO: i couldnt get the win 32 api to load a bitmap.... So im punting on this one :(
|
||||
// break;
|
||||
// }
|
||||
|
||||
default:
|
||||
printf("Unhandled render command %d\r\n", renderCommand->commandType);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
BitBlt(hdc, 0, 0, win_width, win_height, renderer_hdcMem, 0, 0, SRCCOPY);
|
||||
|
||||
// Free-up the off-screen DC
|
||||
SelectObject(renderer_hdcMem, renderer_hOld);
|
||||
DeleteObject(renderer_hbmMem);
|
||||
DeleteDC(renderer_hdcMem);
|
||||
|
||||
EndPaint(hwnd, &ps);
|
||||
}
|
||||
|
||||
/*
|
||||
Hacks due to the windows api not making sence to use.... may measure too large, but never too small
|
||||
*/
|
||||
|
||||
#ifndef WIN32_FONT_HEIGHT
|
||||
#define WIN32_FONT_HEIGHT (16)
|
||||
#endif
|
||||
|
||||
#ifndef WIN32_FONT_WIDTH
|
||||
#define WIN32_FONT_WIDTH (8)
|
||||
#endif
|
||||
|
||||
static inline Clay_Dimensions Clay_Win32_MeasureText(Clay_StringSlice text, Clay_TextElementConfig *config, void *userData)
|
||||
{
|
||||
Clay_Dimensions textSize = {0};
|
||||
|
||||
float maxTextWidth = 0.0f;
|
||||
float lineTextWidth = 0;
|
||||
float textHeight = WIN32_FONT_HEIGHT;
|
||||
|
||||
for (int i = 0; i < text.length; ++i)
|
||||
{
|
||||
if (text.chars[i] == '\n')
|
||||
{
|
||||
maxTextWidth = fmax(maxTextWidth, lineTextWidth);
|
||||
lineTextWidth = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
lineTextWidth += WIN32_FONT_WIDTH;
|
||||
}
|
||||
|
||||
maxTextWidth = fmax(maxTextWidth, lineTextWidth);
|
||||
|
||||
textSize.width = maxTextWidth;
|
||||
textSize.height = textHeight;
|
||||
|
||||
return textSize;
|
||||
}
|
Loading…
Reference in New Issue
Block a user