diff --git a/clay.h b/clay.h index d4b2896..57a9d10 100644 --- a/clay.h +++ b/clay.h @@ -424,6 +424,11 @@ typedef struct { void* customData; } Clay_CustomRenderData; +typedef struct { + bool horizontal; + bool vertical; +} Clay_ScrollRenderData; + typedef struct { Clay_Color color; Clay_CornerRadius cornerRadius; @@ -436,6 +441,7 @@ typedef union { Clay_ImageRenderData image; Clay_CustomRenderData custom; Clay_BorderRenderData border; + Clay_ScrollRenderData scroll; } Clay_RenderData; // Miscellaneous Structs & Enums --------------------------------- @@ -2249,6 +2255,12 @@ void Clay__CalculateFinalLayout(void) { } case CLAY__ELEMENT_CONFIG_TYPE_SCROLL: { renderCommand.commandType = CLAY_RENDER_COMMAND_TYPE_SCISSOR_START; + renderCommand.renderData = CLAY__INIT(Clay_RenderData) { + .scroll = { + .horizontal = elementConfig->config.scrollElementConfig->horizontal, + .vertical = elementConfig->config.scrollElementConfig->vertical, + } + }; break; } case CLAY__ELEMENT_CONFIG_TYPE_IMAGE: { diff --git a/examples/clay-official-website/build/clay/index.html b/examples/clay-official-website/build/clay/index.html index 0eab546..1e07d31 100644 --- a/examples/clay-official-website/build/clay/index.html +++ b/examples/clay-official-website/build/clay/index.html @@ -86,6 +86,10 @@ ]; let elementCache = {}; let imageCache = {}; + let dimensionsDefinition = { type: 'struct', members: [ + {name: 'width', type: 'float'}, + {name: 'height', type: 'float'}, + ]}; let colorDefinition = { type: 'struct', members: [ {name: 'r', type: 'float' }, {name: 'g', type: 'float' }, @@ -101,9 +105,12 @@ {name: 'chars', type: 'uint32_t' }, {name: 'baseChars', type: 'uint32_t' }, ]}; - let borderDefinition = { type: 'struct', members: [ - {name: 'width', type: 'uint32_t'}, - {name: 'color', ...colorDefinition}, + let borderWidthDefinition = { type: 'struct', members: [ + {name: 'left', type: 'uint16_t'}, + {name: 'right', type: 'uint16_t'}, + {name: 'top', type: 'uint16_t'}, + {name: 'bottom', type: 'uint16_t'}, + {name: 'betweenChildren', type: 'uint16_t'}, ]}; let cornerRadiusDefinition = { type: 'struct', members: [ {name: 'topLeft', type: 'float'}, @@ -111,44 +118,53 @@ {name: 'bottomLeft', type: 'float'}, {name: 'bottomRight', type: 'float'}, ]}; - let rectangleConfigDefinition = { name: 'rectangle', type: 'struct', members: [ - { name: 'color', ...colorDefinition }, - { name: 'link', ...stringDefinition }, - { name: 'cursorPointer', type: 'uint8_t' }, - ]}; - let borderConfigDefinition = { name: 'text', type: 'struct', members: [ - { name: 'left', ...borderDefinition }, - { name: 'right', ...borderDefinition }, - { name: 'top', ...borderDefinition }, - { name: 'bottom', ...borderDefinition }, - { name: 'betweenChildren', ...borderDefinition }, - ]}; let textConfigDefinition = { name: 'text', type: 'struct', members: [ - { name: 'textColor', ...colorDefinition }, - { name: 'fontId', type: 'uint16_t' }, - { name: 'fontSize', type: 'uint16_t' }, - { name: 'letterSpacing', type: 'uint16_t' }, - { name: 'lineSpacing', type: 'uint16_t' }, - { name: 'wrapMode', type: 'uint32_t' }, - { name: 'disablePointerEvents', type: 'uint8_t' } + { name: 'textColor', ...colorDefinition }, + { name: 'fontId', type: 'uint16_t' }, + { name: 'fontSize', type: 'uint16_t' }, + { name: 'letterSpacing', type: 'uint16_t' }, + { name: 'lineSpacing', type: 'uint16_t' }, + { name: 'wrapMode', type: 'uint8_t' }, + { name: 'disablePointerEvents', type: 'uint8_t' }, + { name: '_padding', type: 'uint16_t' }, ]}; - let scrollConfigDefinition = { name: 'text', type: 'struct', members: [ + let textRenderDataDefinition = { type: 'struct', members: [ + { name: 'stringContents', ...stringSliceDefinition }, + { name: 'textColor', ...colorDefinition }, + { name: 'fontId', type: 'uint16_t' }, + { name: 'fontSize', type: 'uint16_t' }, + { name: 'letterSpacing', type: 'uint16_t' }, + { name: 'lineHeight', type: 'uint16_t' }, + ]}; + let rectangleRenderDataDefinition = { type: 'struct', members: [ + { name: 'backgroundColor', ...colorDefinition }, + { name: 'cornerRadius', ...cornerRadiusDefinition }, + ]}; + let imageRenderDataDefinition = { type: 'struct', members: [ + { name: 'backgroundColor', ...colorDefinition }, + { name: 'cornerRadius', ...cornerRadiusDefinition }, + { name: 'sourceDimensions', ...dimensionsDefinition }, + { name: 'imageData', type: 'uint32_t' }, + ]}; + let customRenderDataDefinition = { type: 'struct', members: [ + { name: 'backgroundColor', ...colorDefinition }, + { name: 'cornerRadius', ...cornerRadiusDefinition }, + { name: 'customData', type: 'uint32_t' }, + ]}; + let borderRenderDataDefinition = { type: 'struct', members: [ + { name: 'color', ...colorDefinition }, + { name: 'cornerRadius', ...cornerRadiusDefinition }, + { name: 'width', ...borderWidthDefinition }, + { name: 'padding', type: 'uint16_t'} + ]}; + let scrollRenderDataDefinition = { type: 'struct', members: [ { name: 'horizontal', type: 'bool' }, { name: 'vertical', type: 'bool' }, ]}; - let imageConfigDefinition = { name: 'image', type: 'struct', members: [ - { name: 'imageData', type: 'uint32_t' }, - { name: 'sourceDimensions', type: 'struct', members: [ - { name: 'width', type: 'float' }, - { name: 'height', type: 'float' }, - ]}, - { name: 'sourceURL', ...stringDefinition } - ]}; - let customConfigDefinition = { name: 'custom', type: 'struct', members: [ - { name: 'customData', type: 'uint32_t' }, - ]} - let sharedConfigDefinition = { name: 'shared', type: 'struct', members: [ - { name: 'cornerRadius', ...cornerRadiusDefinition }, + let customHTMLDataDefinition = { type: 'struct', members: [ + { name: 'link', ...stringDefinition }, + { name: 'cursorPointer', type: 'uint8_t' }, + { name: 'disablePointerEvents', type: 'uint8_t' }, ]}; let renderCommandDefinition = { name: 'CLay_RenderCommand', @@ -160,14 +176,19 @@ { name: 'width', type: 'float' }, { name: 'height', type: 'float' }, ]}, - { name: 'config', type: 'uint32_t'}, - { name: 'textOrSharedConfig', type: 'union', members: [ - { name: 'text', ...stringSliceDefinition }, - { name: 'sharedConfig', type: 'uint32_t' } + { name: 'renderData', type: 'union', members: [ + { name: 'rectangle', ...rectangleRenderDataDefinition }, + { name: 'text', ...textRenderDataDefinition }, + { name: 'image', ...imageRenderDataDefinition }, + { name: 'custom', ...customRenderDataDefinition }, + { name: 'border', ...borderRenderDataDefinition }, + { name: 'scroll', ...scrollRenderDataDefinition }, ]}, - { name: 'zIndex', type: 'int32_t' }, + { name: 'userData', type: 'uint32_t'}, { name: 'id', type: 'uint32_t' }, - { name: 'commandType', type: 'uint32_t', }, + { name: 'zIndex', type: 'int16_t' }, + { name: 'commandType', type: 'uint8_t' }, + { name: '_padding', type: 'uint8_t' }, ] }; @@ -190,6 +211,7 @@ case 'uint32_t': return 4; case 'int32_t': return 4; case 'uint16_t': return 2; + case 'int16_t': return 2; case 'uint8_t': return 1; case 'bool': return 1; default: { @@ -219,6 +241,7 @@ case 'uint32_t': return { value: memoryDataView.getUint32(address, true), __size: 4 }; case 'int32_t': return { value: memoryDataView.getUint32(address, true), __size: 4 }; case 'uint16_t': return { value: memoryDataView.getUint16(address, true), __size: 2 }; + case 'int16_t': return { value: memoryDataView.getInt16(address, true), __size: 2 }; case 'uint8_t': return { value: memoryDataView.getUint8(address, true), __size: 1 }; case 'bool': return { value: memoryDataView.getUint8(address, true), __size: 1 }; default: { @@ -340,7 +363,7 @@ memoryDataView.setFloat32(addressOfOffset, -container.scrollLeft, true); memoryDataView.setFloat32(addressOfOffset + 4, -container.scrollTop, true); } - } + }, }, }; const { instance } = await WebAssembly.instantiateStreaming( @@ -348,13 +371,15 @@ ); memoryDataView = new DataView(new Uint8Array(instance.exports.memory.buffer).buffer); scratchSpaceAddress = instance.exports.__heap_base.value; - heapSpaceAddress = instance.exports.__heap_base.value + 1024; + let clayScratchSpaceAddress = instance.exports.__heap_base.value + 1024; + heapSpaceAddress = instance.exports.__heap_base.value + 2048; let arenaAddress = scratchSpaceAddress + 8; window.instance = instance; createMainArena(arenaAddress, heapSpaceAddress); memoryDataView.setFloat32(instance.exports.__heap_base.value, window.innerWidth, true); memoryDataView.setFloat32(instance.exports.__heap_base.value + 4, window.innerHeight, true); instance.exports.Clay_Initialize(arenaAddress, instance.exports.__heap_base.value); + instance.exports.SetScratchMemory(arenaAddress, clayScratchSpaceAddress); renderCommandSize = getStructTotalSize(renderCommandDefinition); renderLoop(); } @@ -368,6 +393,22 @@ return false; } + function SetElementBackgroundColorAndRadius(element, cornerRadius, backgroundColor) { + element.style.backgroundColor = `rgba(${backgroundColor.r.value}, ${backgroundColor.g.value}, ${backgroundColor.b.value}, ${backgroundColor.a.value / 255})`; + if (cornerRadius.topLeft.value > 0) { + element.style.borderTopLeftRadius = cornerRadius.topLeft.value + 'px'; + } + if (cornerRadius.topRight.value > 0) { + element.style.borderTopRightRadius = cornerRadius.topRight.value + 'px'; + } + if (cornerRadius.bottomLeft.value > 0) { + element.style.borderBottomLeftRadius = cornerRadius.bottomLeft.value + 'px'; + } + if (cornerRadius.bottomRight.value > 0) { + element.style.borderBottomRightRadius = cornerRadius.bottomRight.value + 'px'; + } + } + function renderLoopHTML() { let capacity = memoryDataView.getInt32(scratchSpaceAddress, true); let length = memoryDataView.getInt32(scratchSpaceAddress + 4, true); @@ -375,7 +416,7 @@ let scissorStack = [{ nextAllocation: { x: 0, y: 0 }, element: htmlRoot, nextElementIndex: 0 }]; let previousId = 0; for (let i = 0; i < length; i++, arrayOffset += renderCommandSize) { - let entireRenderCommandMemory = new Uint32Array(memoryDataView.buffer.slice(arrayOffset, arrayOffset + renderCommandSize)); + let entireRenderCommandMemory = new Uint8Array(memoryDataView.buffer.slice(arrayOffset, arrayOffset + renderCommandSize)); let renderCommand = readStructAtAddress(arrayOffset, renderCommandDefinition); let parentElement = scissorStack[scissorStack.length - 1]; let element = null; @@ -384,13 +425,12 @@ let elementType = 'div'; switch (renderCommand.commandType.value & 0xff) { case CLAY_RENDER_COMMAND_TYPE_RECTANGLE: { - if (readStructAtAddress(renderCommand.config.value, rectangleConfigDefinition).link.length.value > 0) { - elementType = 'a'; - } + // if (readStructAtAddress(renderCommand.renderData.rectangle.value, rectangleRenderDataDefinition).link.length.value > 0) { TODO reimplement links + // elementType = 'a'; + // } break; } case CLAY_RENDER_COMMAND_TYPE_IMAGE: { - console.log('test5'); elementType = 'img'; break; } default: break; @@ -443,93 +483,82 @@ break; } case (CLAY_RENDER_COMMAND_TYPE_RECTANGLE): { - let config = readStructAtAddress(renderCommand.config.value, rectangleConfigDefinition); - let sharedConfig = readStructAtAddress( renderCommand.textOrSharedConfig.sharedConfig.value, sharedConfigDefinition); - let configMemory = new Uint8Array(memoryDataView.buffer.slice(renderCommand.config.value, renderCommand.config.value + config.__size)); - let linkContents = config.link.length.value > 0 ? textDecoder.decode(new Uint8Array(memoryDataView.buffer.slice(config.link.chars.value, config.link.chars.value + config.link.length.value))) : 0; - memoryDataView.setUint32(0, renderCommand.id.value, true); - if (linkContents.length > 0 && (window.mouseDownThisFrame || window.touchDown) && instance.exports.Clay_PointerOver(0)) { - window.location.href = linkContents; - } - if (!dirty && !MemoryIsDifferent(configMemory, elementData.previousMemoryConfig, config.__size)) { + let config = renderCommand.renderData.rectangle; + let configMemory = JSON.stringify(config); + if (configMemory === elementData.previousMemoryConfig) { break; } - if (linkContents.length > 0) { - element.href = linkContents; + + SetElementBackgroundColorAndRadius(element, config.cornerRadius, config.backgroundColor); + if (renderCommand.userData.value !== 0) { + let customData = readStructAtAddress(renderCommand.userData.value, customHTMLDataDefinition); + + let linkContents = customData.link.length.value > 0 ? textDecoder.decode(new Uint8Array(memoryDataView.buffer.slice(customData.link.chars.value, customData.link.chars.value + customData.link.length.value))) : 0; + memoryDataView.setUint32(0, renderCommand.id.value, true); + if (linkContents.length > 0 && (window.mouseDownThisFrame || window.touchDown) && instance.exports.Clay_PointerOver(0)) { + window.location.href = linkContents; + } + if (linkContents.length > 0) { + element.href = linkContents; + } + + if (linkContents.length > 0 || customData.cursorPointer.value) { + element.style.pointerEvents = 'all'; + element.style.cursor = 'pointer'; + } } - if (linkContents.length > 0 || config.cursorPointer.value) { - element.style.pointerEvents = 'all'; - element.style.cursor = 'pointer'; - } elementData.previousMemoryConfig = configMemory; - let color = config.color; - element.style.backgroundColor = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`; - if (sharedConfig.cornerRadius.topLeft.value > 0) { - element.style.borderTopLeftRadius = sharedConfig.cornerRadius.topLeft.value + 'px'; - } - if (sharedConfig.cornerRadius.topRight.value > 0) { - element.style.borderTopRightRadius = sharedConfig.cornerRadius.topRight.value + 'px'; - } - if (sharedConfig.cornerRadius.bottomLeft.value > 0) { - element.style.borderBottomLeftRadius = sharedConfig.cornerRadius.bottomLeft.value + 'px'; - } - if (sharedConfig.cornerRadius.bottomRight.value > 0) { - element.style.borderBottomRightRadius = sharedConfig.cornerRadius.bottomRight.value + 'px'; - } + break; } case (CLAY_RENDER_COMMAND_TYPE_BORDER): { - let config = readStructAtAddress(renderCommand.config.value, borderConfigDefinition); - let sharedConfig = readStructAtAddress( renderCommand.textOrSharedConfig.sharedConfig.value, sharedConfigDefinition); - let configMemory = new Uint8Array(memoryDataView.buffer.slice(renderCommand.config.value, renderCommand.config.value + config.__size)); - if (!dirty && !MemoryIsDifferent(configMemory, elementData.previousMemoryConfig, config.__size)) { + let config = renderCommand.renderData.border; + let configMemory = JSON.stringify(config); + if (configMemory === elementData.previousMemoryConfig) { break; } + let color = config.color; elementData.previousMemoryConfig = configMemory; - if (config.left.width.value > 0) { - let color = config.left.color; - element.style.borderLeft = `${config.left.width.value}px solid rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})` + if (config.width.left.value > 0) { + element.style.borderLeft = `${config.width.left.value}px solid rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})` } - if (config.right.width.value > 0) { - let color = config.right.color; - element.style.borderRight = `${config.right.width.value}px solid rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})` + if (config.width.right.value > 0) { + element.style.borderRight = `${config.width.right.value}px solid rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})` } - if (config.top.width.value > 0) { - let color = config.top.color; - element.style.borderTop = `${config.top.width.value}px solid rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})` + if (config.width.top.value > 0) { + element.style.borderTop = `${config.width.top.value}px solid rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})` } - if (config.bottom.width.value > 0) { - let color = config.bottom.color; - element.style.borderBottom = `${config.bottom.width.value}px solid rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})` + if (config.width.bottom.value > 0) { + element.style.borderBottom = `${config.width.bottom.value}px solid rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})` } - if (sharedConfig.cornerRadius.topLeft.value > 0) { - element.style.borderTopLeftRadius = sharedConfig.cornerRadius.topLeft.value + 'px'; + if (config.cornerRadius.topLeft.value > 0) { + element.style.borderTopLeftRadius = config.cornerRadius.topLeft.value + 'px'; } - if (sharedConfig.cornerRadius.topRight.value > 0) { - element.style.borderTopRightRadius = sharedConfig.cornerRadius.topRight.value + 'px'; + if (config.cornerRadius.topRight.value > 0) { + element.style.borderTopRightRadius = config.cornerRadius.topRight.value + 'px'; } - if (sharedConfig.cornerRadius.bottomLeft.value > 0) { - element.style.borderBottomLeftRadius = sharedConfig.cornerRadius.bottomLeft.value + 'px'; + if (config.cornerRadius.bottomLeft.value > 0) { + element.style.borderBottomLeftRadius = config.cornerRadius.bottomLeft.value + 'px'; } - if (sharedConfig.cornerRadius.bottomRight.value > 0) { - element.style.borderBottomRightRadius = sharedConfig.cornerRadius.bottomRight.value + 'px'; + if (config.cornerRadius.bottomRight.value > 0) { + element.style.borderBottomRightRadius = config.cornerRadius.bottomRight.value + 'px'; } break; } case (CLAY_RENDER_COMMAND_TYPE_TEXT): { - let config = readStructAtAddress(renderCommand.config.value, textConfigDefinition); - let configMemory = new Uint8Array(memoryDataView.buffer.slice(renderCommand.config.value, renderCommand.config.value + config.__size)); - let textContents = renderCommand.textOrSharedConfig.text; - let stringContents = new Uint8Array(memoryDataView.buffer.slice(textContents.chars.value, textContents.chars.value + textContents.length.value)); - if (MemoryIsDifferent(configMemory, elementData.previousMemoryConfig, config.__size)) { + let config = renderCommand.renderData.text; + let customData = readStructAtAddress(renderCommand.userData.value, customHTMLDataDefinition); + let configMemory = JSON.stringify(config); + let stringContents = new Uint8Array(memoryDataView.buffer.slice(config.stringContents.chars.value, config.stringContents.chars.value + config.stringContents.length.value)); + if (configMemory !== elementData.previousMemoryConfig) { element.className = 'text'; let textColor = config.textColor; let fontSize = Math.round(config.fontSize.value * GLOBAL_FONT_SCALING_FACTOR); element.style.color = `rgba(${textColor.r.value}, ${textColor.g.value}, ${textColor.b.value}, ${textColor.a.value})`; element.style.fontFamily = fontsById[config.fontId.value]; element.style.fontSize = fontSize + 'px'; - element.style.pointerEvents = config.disablePointerEvents.value ? 'none' : 'all'; + element.style.pointerEvents = customData.disablePointerEvents.value ? 'none' : 'all'; elementData.previousMemoryConfig = configMemory; } if (stringContents.length !== elementData.previousMemoryText.length || MemoryIsDifferent(stringContents, elementData.previousMemoryText, stringContents.length)) { @@ -540,7 +569,11 @@ } case (CLAY_RENDER_COMMAND_TYPE_SCISSOR_START): { scissorStack.push({ nextAllocation: { x: renderCommand.boundingBox.x.value, y: renderCommand.boundingBox.y.value }, element, nextElementIndex: 0 }); - let config = readStructAtAddress(renderCommand.config.value, scrollConfigDefinition); + let config = renderCommand.renderData.scroll; + let configMemory = JSON.stringify(config); + if (configMemory === elementData.previousMemoryConfig) { + break; + } if (config.horizontal.value) { element.style.overflowX = 'scroll'; element.style.pointerEvents = 'auto'; @@ -549,6 +582,7 @@ element.style.overflowY = 'scroll'; element.style.pointerEvents = 'auto'; } + elementData.previousMemoryConfig = configMemory; break; } case (CLAY_RENDER_COMMAND_TYPE_SCISSOR_END): { @@ -556,9 +590,9 @@ break; } case (CLAY_RENDER_COMMAND_TYPE_IMAGE): { - console.log('test1'); - let config = readStructAtAddress(renderCommand.config.value, imageConfigDefinition); - let srcContents = new Uint8Array(memoryDataView.buffer.slice(config.sourceURL.chars.value, config.sourceURL.chars.value + config.sourceURL.length.value)); + let config = renderCommand.renderData.image; + let imageURL = readStructAtAddress(config.imageData.value, stringDefinition); + let srcContents = new Uint8Array(memoryDataView.buffer.slice(imageURL.chars.value, imageURL.chars.value + imageURL.length.value)); if (srcContents.length !== elementData.previousMemoryText.length || MemoryIsDifferent(srcContents, elementData.previousMemoryText, srcContents.length)) { element.src = textDecoder.decode(srcContents); } @@ -566,6 +600,9 @@ break; } case (CLAY_RENDER_COMMAND_TYPE_CUSTOM): break; + default: { + console.log("Error: unhandled render command"); + } } } @@ -602,8 +639,8 @@ break; } case (CLAY_RENDER_COMMAND_TYPE_RECTANGLE): { - let config = readStructAtAddress(renderCommand.config.value, rectangleConfigDefinition); - let color = config.color; + let config = renderCommand.renderData.rectangle; + let color = config.backgroundColor; ctx.beginPath(); window.canvasContext.fillStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`; window.canvasContext.roundRect( @@ -615,33 +652,35 @@ ctx.fill(); ctx.closePath(); // Handle link clicks - let linkContents = config.link.length.value > 0 ? textDecoder.decode(new Uint8Array(memoryDataView.buffer.slice(config.link.chars.value, config.link.chars.value + config.link.length.value))) : 0; - memoryDataView.setUint32(0, renderCommand.id.value, true); - if (linkContents.length > 0 && (window.mouseDownThisFrame || window.touchDown) && instance.exports.Clay_PointerOver(0)) { - window.location.href = linkContents; + if (renderCommand.userData.value !== 0) { + let customData = readStructAtAddress(renderCommand.userData.value, customHTMLDataDefinition); + let linkContents = customData.link.length.value > 0 ? textDecoder.decode(new Uint8Array(memoryDataView.buffer.slice(customData.link.chars.value, customData.link.chars.value + customData.link.length.value))) : 0; + memoryDataView.setUint32(0, renderCommand.id.value, true); + if (linkContents.length > 0 && (window.mouseDownThisFrame || window.touchDown) && instance.exports.Clay_PointerOver(0)) { + window.location.href = linkContents; + } } break; } case (CLAY_RENDER_COMMAND_TYPE_BORDER): { - let config = readStructAtAddress(renderCommand.config.value, borderConfigDefinition); + let config = renderCommand.renderData.border; + let color = config.color; ctx.beginPath(); ctx.moveTo(boundingBox.x.value * scale, boundingBox.y.value * scale); // Top Left Corner if (config.cornerRadius.topLeft.value > 0) { - let lineWidth = config.top.width.value; + let lineWidth = config.width.top.value; let halfLineWidth = lineWidth / 2; ctx.moveTo((boundingBox.x.value + halfLineWidth) * scale, (boundingBox.y.value + config.cornerRadius.topLeft.value + halfLineWidth) * scale); - let color = config.top.color; ctx.lineWidth = lineWidth * scale; ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`; ctx.arcTo((boundingBox.x.value + halfLineWidth) * scale, (boundingBox.y.value + halfLineWidth) * scale, (boundingBox.x.value + config.cornerRadius.topLeft.value + halfLineWidth) * scale, (boundingBox.y.value + halfLineWidth) * scale, config.cornerRadius.topLeft.value * scale); ctx.stroke(); } // Top border - if (config.top.width.value > 0) { - let lineWidth = config.top.width.value; + if (config.width.top.value > 0) { + let lineWidth = config.width.top.value; let halfLineWidth = lineWidth / 2; - let color = config.top.color; ctx.lineWidth = lineWidth * scale; ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`; ctx.moveTo((boundingBox.x.value + config.cornerRadius.topLeft.value + halfLineWidth) * scale, (boundingBox.y.value + halfLineWidth) * scale); @@ -650,19 +689,17 @@ } // Top Right Corner if (config.cornerRadius.topRight.value > 0) { - let lineWidth = config.top.width.value; + let lineWidth = config.width.top.value; let halfLineWidth = lineWidth / 2; ctx.moveTo((boundingBox.x.value + boundingBox.width.value - config.cornerRadius.topRight.value - halfLineWidth) * scale, (boundingBox.y.value + halfLineWidth) * scale); - let color = config.top.color; ctx.lineWidth = lineWidth * scale; ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`; ctx.arcTo((boundingBox.x.value + boundingBox.width.value - halfLineWidth) * scale, (boundingBox.y.value + halfLineWidth) * scale, (boundingBox.x.value + boundingBox.width.value - halfLineWidth) * scale, (boundingBox.y.value + config.cornerRadius.topRight.value + halfLineWidth) * scale, config.cornerRadius.topRight.value * scale); ctx.stroke(); } // Right border - if (config.right.width.value > 0) { - let color = config.right.color; - let lineWidth = config.right.width.value; + if (config.width.right.value > 0) { + let lineWidth = config.width.right.value; let halfLineWidth = lineWidth / 2; ctx.lineWidth = lineWidth * scale; ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`; @@ -672,8 +709,7 @@ } // Bottom Right Corner if (config.cornerRadius.bottomRight.value > 0) { - let color = config.top.color; - let lineWidth = config.top.width.value; + let lineWidth = config.width.top.value; let halfLineWidth = lineWidth / 2; ctx.moveTo((boundingBox.x.value + boundingBox.width.value - halfLineWidth) * scale, (boundingBox.y.value + boundingBox.height.value - config.cornerRadius.bottomRight.value - halfLineWidth) * scale); ctx.lineWidth = lineWidth * scale; @@ -682,9 +718,8 @@ ctx.stroke(); } // Bottom Border - if (config.bottom.width.value > 0) { - let color = config.bottom.color; - let lineWidth = config.bottom.width.value; + if (config.width.bottom.value > 0) { + let lineWidth = config.width.bottom.value; let halfLineWidth = lineWidth / 2; ctx.lineWidth = lineWidth * scale; ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`; @@ -694,8 +729,7 @@ } // Bottom Left Corner if (config.cornerRadius.bottomLeft.value > 0) { - let color = config.bottom.color; - let lineWidth = config.bottom.width.value; + let lineWidth = config.width.bottom.value; let halfLineWidth = lineWidth / 2; ctx.moveTo((boundingBox.x.value + config.cornerRadius.bottomLeft.value + halfLineWidth) * scale, (boundingBox.y.value + boundingBox.height.value - halfLineWidth) * scale); ctx.lineWidth = lineWidth * scale; @@ -704,9 +738,8 @@ ctx.stroke(); } // Left Border - if (config.left.width.value > 0) { - let color = config.left.color; - let lineWidth = config.left.width.value; + if (config.width.left.value > 0) { + let lineWidth = config.width.left.value; let halfLineWidth = lineWidth / 2; ctx.lineWidth = lineWidth * scale; ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`; @@ -718,8 +751,8 @@ break; } case (CLAY_RENDER_COMMAND_TYPE_TEXT): { - let config = readStructAtAddress(renderCommand.config.value, textConfigDefinition); - let textContents = renderCommand.text; + let config = renderCommand.renderData.text; + let textContents = config.stringContents; let stringContents = new Uint8Array(memoryDataView.buffer.slice(textContents.chars.value, textContents.chars.value + textContents.length.value)); let fontSize = config.fontSize.value * GLOBAL_FONT_SCALING_FACTOR * scale; ctx.font = `${fontSize}px ${fontsById[config.fontId.value]}`; @@ -742,8 +775,9 @@ break; } case (CLAY_RENDER_COMMAND_TYPE_IMAGE): { - let config = readStructAtAddress(renderCommand.config.value, imageConfigDefinition); - let src = textDecoder.decode(new Uint8Array(memoryDataView.buffer.slice(config.sourceURL.chars.value, config.sourceURL.chars.value + config.sourceURL.length.value))); + let config = renderCommand.renderData.image; + let imageURL = readStructAtAddress(config.imageData.value, stringDefinition); + let src = textDecoder.decode(new Uint8Array(memoryDataView.buffer.slice(imageURL.chars.value, imageURL.chars.value + imageURL.length.value))); if (!imageCache[src]) { imageCache[src] = { image: new Image(), diff --git a/examples/clay-official-website/build/clay/index.wasm b/examples/clay-official-website/build/clay/index.wasm index 91fa130..1173dc4 100755 Binary files a/examples/clay-official-website/build/clay/index.wasm and b/examples/clay-official-website/build/clay/index.wasm differ diff --git a/examples/clay-official-website/index.html b/examples/clay-official-website/index.html index 0eab546..1e07d31 100644 --- a/examples/clay-official-website/index.html +++ b/examples/clay-official-website/index.html @@ -86,6 +86,10 @@ ]; let elementCache = {}; let imageCache = {}; + let dimensionsDefinition = { type: 'struct', members: [ + {name: 'width', type: 'float'}, + {name: 'height', type: 'float'}, + ]}; let colorDefinition = { type: 'struct', members: [ {name: 'r', type: 'float' }, {name: 'g', type: 'float' }, @@ -101,9 +105,12 @@ {name: 'chars', type: 'uint32_t' }, {name: 'baseChars', type: 'uint32_t' }, ]}; - let borderDefinition = { type: 'struct', members: [ - {name: 'width', type: 'uint32_t'}, - {name: 'color', ...colorDefinition}, + let borderWidthDefinition = { type: 'struct', members: [ + {name: 'left', type: 'uint16_t'}, + {name: 'right', type: 'uint16_t'}, + {name: 'top', type: 'uint16_t'}, + {name: 'bottom', type: 'uint16_t'}, + {name: 'betweenChildren', type: 'uint16_t'}, ]}; let cornerRadiusDefinition = { type: 'struct', members: [ {name: 'topLeft', type: 'float'}, @@ -111,44 +118,53 @@ {name: 'bottomLeft', type: 'float'}, {name: 'bottomRight', type: 'float'}, ]}; - let rectangleConfigDefinition = { name: 'rectangle', type: 'struct', members: [ - { name: 'color', ...colorDefinition }, - { name: 'link', ...stringDefinition }, - { name: 'cursorPointer', type: 'uint8_t' }, - ]}; - let borderConfigDefinition = { name: 'text', type: 'struct', members: [ - { name: 'left', ...borderDefinition }, - { name: 'right', ...borderDefinition }, - { name: 'top', ...borderDefinition }, - { name: 'bottom', ...borderDefinition }, - { name: 'betweenChildren', ...borderDefinition }, - ]}; let textConfigDefinition = { name: 'text', type: 'struct', members: [ - { name: 'textColor', ...colorDefinition }, - { name: 'fontId', type: 'uint16_t' }, - { name: 'fontSize', type: 'uint16_t' }, - { name: 'letterSpacing', type: 'uint16_t' }, - { name: 'lineSpacing', type: 'uint16_t' }, - { name: 'wrapMode', type: 'uint32_t' }, - { name: 'disablePointerEvents', type: 'uint8_t' } + { name: 'textColor', ...colorDefinition }, + { name: 'fontId', type: 'uint16_t' }, + { name: 'fontSize', type: 'uint16_t' }, + { name: 'letterSpacing', type: 'uint16_t' }, + { name: 'lineSpacing', type: 'uint16_t' }, + { name: 'wrapMode', type: 'uint8_t' }, + { name: 'disablePointerEvents', type: 'uint8_t' }, + { name: '_padding', type: 'uint16_t' }, ]}; - let scrollConfigDefinition = { name: 'text', type: 'struct', members: [ + let textRenderDataDefinition = { type: 'struct', members: [ + { name: 'stringContents', ...stringSliceDefinition }, + { name: 'textColor', ...colorDefinition }, + { name: 'fontId', type: 'uint16_t' }, + { name: 'fontSize', type: 'uint16_t' }, + { name: 'letterSpacing', type: 'uint16_t' }, + { name: 'lineHeight', type: 'uint16_t' }, + ]}; + let rectangleRenderDataDefinition = { type: 'struct', members: [ + { name: 'backgroundColor', ...colorDefinition }, + { name: 'cornerRadius', ...cornerRadiusDefinition }, + ]}; + let imageRenderDataDefinition = { type: 'struct', members: [ + { name: 'backgroundColor', ...colorDefinition }, + { name: 'cornerRadius', ...cornerRadiusDefinition }, + { name: 'sourceDimensions', ...dimensionsDefinition }, + { name: 'imageData', type: 'uint32_t' }, + ]}; + let customRenderDataDefinition = { type: 'struct', members: [ + { name: 'backgroundColor', ...colorDefinition }, + { name: 'cornerRadius', ...cornerRadiusDefinition }, + { name: 'customData', type: 'uint32_t' }, + ]}; + let borderRenderDataDefinition = { type: 'struct', members: [ + { name: 'color', ...colorDefinition }, + { name: 'cornerRadius', ...cornerRadiusDefinition }, + { name: 'width', ...borderWidthDefinition }, + { name: 'padding', type: 'uint16_t'} + ]}; + let scrollRenderDataDefinition = { type: 'struct', members: [ { name: 'horizontal', type: 'bool' }, { name: 'vertical', type: 'bool' }, ]}; - let imageConfigDefinition = { name: 'image', type: 'struct', members: [ - { name: 'imageData', type: 'uint32_t' }, - { name: 'sourceDimensions', type: 'struct', members: [ - { name: 'width', type: 'float' }, - { name: 'height', type: 'float' }, - ]}, - { name: 'sourceURL', ...stringDefinition } - ]}; - let customConfigDefinition = { name: 'custom', type: 'struct', members: [ - { name: 'customData', type: 'uint32_t' }, - ]} - let sharedConfigDefinition = { name: 'shared', type: 'struct', members: [ - { name: 'cornerRadius', ...cornerRadiusDefinition }, + let customHTMLDataDefinition = { type: 'struct', members: [ + { name: 'link', ...stringDefinition }, + { name: 'cursorPointer', type: 'uint8_t' }, + { name: 'disablePointerEvents', type: 'uint8_t' }, ]}; let renderCommandDefinition = { name: 'CLay_RenderCommand', @@ -160,14 +176,19 @@ { name: 'width', type: 'float' }, { name: 'height', type: 'float' }, ]}, - { name: 'config', type: 'uint32_t'}, - { name: 'textOrSharedConfig', type: 'union', members: [ - { name: 'text', ...stringSliceDefinition }, - { name: 'sharedConfig', type: 'uint32_t' } + { name: 'renderData', type: 'union', members: [ + { name: 'rectangle', ...rectangleRenderDataDefinition }, + { name: 'text', ...textRenderDataDefinition }, + { name: 'image', ...imageRenderDataDefinition }, + { name: 'custom', ...customRenderDataDefinition }, + { name: 'border', ...borderRenderDataDefinition }, + { name: 'scroll', ...scrollRenderDataDefinition }, ]}, - { name: 'zIndex', type: 'int32_t' }, + { name: 'userData', type: 'uint32_t'}, { name: 'id', type: 'uint32_t' }, - { name: 'commandType', type: 'uint32_t', }, + { name: 'zIndex', type: 'int16_t' }, + { name: 'commandType', type: 'uint8_t' }, + { name: '_padding', type: 'uint8_t' }, ] }; @@ -190,6 +211,7 @@ case 'uint32_t': return 4; case 'int32_t': return 4; case 'uint16_t': return 2; + case 'int16_t': return 2; case 'uint8_t': return 1; case 'bool': return 1; default: { @@ -219,6 +241,7 @@ case 'uint32_t': return { value: memoryDataView.getUint32(address, true), __size: 4 }; case 'int32_t': return { value: memoryDataView.getUint32(address, true), __size: 4 }; case 'uint16_t': return { value: memoryDataView.getUint16(address, true), __size: 2 }; + case 'int16_t': return { value: memoryDataView.getInt16(address, true), __size: 2 }; case 'uint8_t': return { value: memoryDataView.getUint8(address, true), __size: 1 }; case 'bool': return { value: memoryDataView.getUint8(address, true), __size: 1 }; default: { @@ -340,7 +363,7 @@ memoryDataView.setFloat32(addressOfOffset, -container.scrollLeft, true); memoryDataView.setFloat32(addressOfOffset + 4, -container.scrollTop, true); } - } + }, }, }; const { instance } = await WebAssembly.instantiateStreaming( @@ -348,13 +371,15 @@ ); memoryDataView = new DataView(new Uint8Array(instance.exports.memory.buffer).buffer); scratchSpaceAddress = instance.exports.__heap_base.value; - heapSpaceAddress = instance.exports.__heap_base.value + 1024; + let clayScratchSpaceAddress = instance.exports.__heap_base.value + 1024; + heapSpaceAddress = instance.exports.__heap_base.value + 2048; let arenaAddress = scratchSpaceAddress + 8; window.instance = instance; createMainArena(arenaAddress, heapSpaceAddress); memoryDataView.setFloat32(instance.exports.__heap_base.value, window.innerWidth, true); memoryDataView.setFloat32(instance.exports.__heap_base.value + 4, window.innerHeight, true); instance.exports.Clay_Initialize(arenaAddress, instance.exports.__heap_base.value); + instance.exports.SetScratchMemory(arenaAddress, clayScratchSpaceAddress); renderCommandSize = getStructTotalSize(renderCommandDefinition); renderLoop(); } @@ -368,6 +393,22 @@ return false; } + function SetElementBackgroundColorAndRadius(element, cornerRadius, backgroundColor) { + element.style.backgroundColor = `rgba(${backgroundColor.r.value}, ${backgroundColor.g.value}, ${backgroundColor.b.value}, ${backgroundColor.a.value / 255})`; + if (cornerRadius.topLeft.value > 0) { + element.style.borderTopLeftRadius = cornerRadius.topLeft.value + 'px'; + } + if (cornerRadius.topRight.value > 0) { + element.style.borderTopRightRadius = cornerRadius.topRight.value + 'px'; + } + if (cornerRadius.bottomLeft.value > 0) { + element.style.borderBottomLeftRadius = cornerRadius.bottomLeft.value + 'px'; + } + if (cornerRadius.bottomRight.value > 0) { + element.style.borderBottomRightRadius = cornerRadius.bottomRight.value + 'px'; + } + } + function renderLoopHTML() { let capacity = memoryDataView.getInt32(scratchSpaceAddress, true); let length = memoryDataView.getInt32(scratchSpaceAddress + 4, true); @@ -375,7 +416,7 @@ let scissorStack = [{ nextAllocation: { x: 0, y: 0 }, element: htmlRoot, nextElementIndex: 0 }]; let previousId = 0; for (let i = 0; i < length; i++, arrayOffset += renderCommandSize) { - let entireRenderCommandMemory = new Uint32Array(memoryDataView.buffer.slice(arrayOffset, arrayOffset + renderCommandSize)); + let entireRenderCommandMemory = new Uint8Array(memoryDataView.buffer.slice(arrayOffset, arrayOffset + renderCommandSize)); let renderCommand = readStructAtAddress(arrayOffset, renderCommandDefinition); let parentElement = scissorStack[scissorStack.length - 1]; let element = null; @@ -384,13 +425,12 @@ let elementType = 'div'; switch (renderCommand.commandType.value & 0xff) { case CLAY_RENDER_COMMAND_TYPE_RECTANGLE: { - if (readStructAtAddress(renderCommand.config.value, rectangleConfigDefinition).link.length.value > 0) { - elementType = 'a'; - } + // if (readStructAtAddress(renderCommand.renderData.rectangle.value, rectangleRenderDataDefinition).link.length.value > 0) { TODO reimplement links + // elementType = 'a'; + // } break; } case CLAY_RENDER_COMMAND_TYPE_IMAGE: { - console.log('test5'); elementType = 'img'; break; } default: break; @@ -443,93 +483,82 @@ break; } case (CLAY_RENDER_COMMAND_TYPE_RECTANGLE): { - let config = readStructAtAddress(renderCommand.config.value, rectangleConfigDefinition); - let sharedConfig = readStructAtAddress( renderCommand.textOrSharedConfig.sharedConfig.value, sharedConfigDefinition); - let configMemory = new Uint8Array(memoryDataView.buffer.slice(renderCommand.config.value, renderCommand.config.value + config.__size)); - let linkContents = config.link.length.value > 0 ? textDecoder.decode(new Uint8Array(memoryDataView.buffer.slice(config.link.chars.value, config.link.chars.value + config.link.length.value))) : 0; - memoryDataView.setUint32(0, renderCommand.id.value, true); - if (linkContents.length > 0 && (window.mouseDownThisFrame || window.touchDown) && instance.exports.Clay_PointerOver(0)) { - window.location.href = linkContents; - } - if (!dirty && !MemoryIsDifferent(configMemory, elementData.previousMemoryConfig, config.__size)) { + let config = renderCommand.renderData.rectangle; + let configMemory = JSON.stringify(config); + if (configMemory === elementData.previousMemoryConfig) { break; } - if (linkContents.length > 0) { - element.href = linkContents; + + SetElementBackgroundColorAndRadius(element, config.cornerRadius, config.backgroundColor); + if (renderCommand.userData.value !== 0) { + let customData = readStructAtAddress(renderCommand.userData.value, customHTMLDataDefinition); + + let linkContents = customData.link.length.value > 0 ? textDecoder.decode(new Uint8Array(memoryDataView.buffer.slice(customData.link.chars.value, customData.link.chars.value + customData.link.length.value))) : 0; + memoryDataView.setUint32(0, renderCommand.id.value, true); + if (linkContents.length > 0 && (window.mouseDownThisFrame || window.touchDown) && instance.exports.Clay_PointerOver(0)) { + window.location.href = linkContents; + } + if (linkContents.length > 0) { + element.href = linkContents; + } + + if (linkContents.length > 0 || customData.cursorPointer.value) { + element.style.pointerEvents = 'all'; + element.style.cursor = 'pointer'; + } } - if (linkContents.length > 0 || config.cursorPointer.value) { - element.style.pointerEvents = 'all'; - element.style.cursor = 'pointer'; - } elementData.previousMemoryConfig = configMemory; - let color = config.color; - element.style.backgroundColor = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`; - if (sharedConfig.cornerRadius.topLeft.value > 0) { - element.style.borderTopLeftRadius = sharedConfig.cornerRadius.topLeft.value + 'px'; - } - if (sharedConfig.cornerRadius.topRight.value > 0) { - element.style.borderTopRightRadius = sharedConfig.cornerRadius.topRight.value + 'px'; - } - if (sharedConfig.cornerRadius.bottomLeft.value > 0) { - element.style.borderBottomLeftRadius = sharedConfig.cornerRadius.bottomLeft.value + 'px'; - } - if (sharedConfig.cornerRadius.bottomRight.value > 0) { - element.style.borderBottomRightRadius = sharedConfig.cornerRadius.bottomRight.value + 'px'; - } + break; } case (CLAY_RENDER_COMMAND_TYPE_BORDER): { - let config = readStructAtAddress(renderCommand.config.value, borderConfigDefinition); - let sharedConfig = readStructAtAddress( renderCommand.textOrSharedConfig.sharedConfig.value, sharedConfigDefinition); - let configMemory = new Uint8Array(memoryDataView.buffer.slice(renderCommand.config.value, renderCommand.config.value + config.__size)); - if (!dirty && !MemoryIsDifferent(configMemory, elementData.previousMemoryConfig, config.__size)) { + let config = renderCommand.renderData.border; + let configMemory = JSON.stringify(config); + if (configMemory === elementData.previousMemoryConfig) { break; } + let color = config.color; elementData.previousMemoryConfig = configMemory; - if (config.left.width.value > 0) { - let color = config.left.color; - element.style.borderLeft = `${config.left.width.value}px solid rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})` + if (config.width.left.value > 0) { + element.style.borderLeft = `${config.width.left.value}px solid rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})` } - if (config.right.width.value > 0) { - let color = config.right.color; - element.style.borderRight = `${config.right.width.value}px solid rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})` + if (config.width.right.value > 0) { + element.style.borderRight = `${config.width.right.value}px solid rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})` } - if (config.top.width.value > 0) { - let color = config.top.color; - element.style.borderTop = `${config.top.width.value}px solid rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})` + if (config.width.top.value > 0) { + element.style.borderTop = `${config.width.top.value}px solid rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})` } - if (config.bottom.width.value > 0) { - let color = config.bottom.color; - element.style.borderBottom = `${config.bottom.width.value}px solid rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})` + if (config.width.bottom.value > 0) { + element.style.borderBottom = `${config.width.bottom.value}px solid rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})` } - if (sharedConfig.cornerRadius.topLeft.value > 0) { - element.style.borderTopLeftRadius = sharedConfig.cornerRadius.topLeft.value + 'px'; + if (config.cornerRadius.topLeft.value > 0) { + element.style.borderTopLeftRadius = config.cornerRadius.topLeft.value + 'px'; } - if (sharedConfig.cornerRadius.topRight.value > 0) { - element.style.borderTopRightRadius = sharedConfig.cornerRadius.topRight.value + 'px'; + if (config.cornerRadius.topRight.value > 0) { + element.style.borderTopRightRadius = config.cornerRadius.topRight.value + 'px'; } - if (sharedConfig.cornerRadius.bottomLeft.value > 0) { - element.style.borderBottomLeftRadius = sharedConfig.cornerRadius.bottomLeft.value + 'px'; + if (config.cornerRadius.bottomLeft.value > 0) { + element.style.borderBottomLeftRadius = config.cornerRadius.bottomLeft.value + 'px'; } - if (sharedConfig.cornerRadius.bottomRight.value > 0) { - element.style.borderBottomRightRadius = sharedConfig.cornerRadius.bottomRight.value + 'px'; + if (config.cornerRadius.bottomRight.value > 0) { + element.style.borderBottomRightRadius = config.cornerRadius.bottomRight.value + 'px'; } break; } case (CLAY_RENDER_COMMAND_TYPE_TEXT): { - let config = readStructAtAddress(renderCommand.config.value, textConfigDefinition); - let configMemory = new Uint8Array(memoryDataView.buffer.slice(renderCommand.config.value, renderCommand.config.value + config.__size)); - let textContents = renderCommand.textOrSharedConfig.text; - let stringContents = new Uint8Array(memoryDataView.buffer.slice(textContents.chars.value, textContents.chars.value + textContents.length.value)); - if (MemoryIsDifferent(configMemory, elementData.previousMemoryConfig, config.__size)) { + let config = renderCommand.renderData.text; + let customData = readStructAtAddress(renderCommand.userData.value, customHTMLDataDefinition); + let configMemory = JSON.stringify(config); + let stringContents = new Uint8Array(memoryDataView.buffer.slice(config.stringContents.chars.value, config.stringContents.chars.value + config.stringContents.length.value)); + if (configMemory !== elementData.previousMemoryConfig) { element.className = 'text'; let textColor = config.textColor; let fontSize = Math.round(config.fontSize.value * GLOBAL_FONT_SCALING_FACTOR); element.style.color = `rgba(${textColor.r.value}, ${textColor.g.value}, ${textColor.b.value}, ${textColor.a.value})`; element.style.fontFamily = fontsById[config.fontId.value]; element.style.fontSize = fontSize + 'px'; - element.style.pointerEvents = config.disablePointerEvents.value ? 'none' : 'all'; + element.style.pointerEvents = customData.disablePointerEvents.value ? 'none' : 'all'; elementData.previousMemoryConfig = configMemory; } if (stringContents.length !== elementData.previousMemoryText.length || MemoryIsDifferent(stringContents, elementData.previousMemoryText, stringContents.length)) { @@ -540,7 +569,11 @@ } case (CLAY_RENDER_COMMAND_TYPE_SCISSOR_START): { scissorStack.push({ nextAllocation: { x: renderCommand.boundingBox.x.value, y: renderCommand.boundingBox.y.value }, element, nextElementIndex: 0 }); - let config = readStructAtAddress(renderCommand.config.value, scrollConfigDefinition); + let config = renderCommand.renderData.scroll; + let configMemory = JSON.stringify(config); + if (configMemory === elementData.previousMemoryConfig) { + break; + } if (config.horizontal.value) { element.style.overflowX = 'scroll'; element.style.pointerEvents = 'auto'; @@ -549,6 +582,7 @@ element.style.overflowY = 'scroll'; element.style.pointerEvents = 'auto'; } + elementData.previousMemoryConfig = configMemory; break; } case (CLAY_RENDER_COMMAND_TYPE_SCISSOR_END): { @@ -556,9 +590,9 @@ break; } case (CLAY_RENDER_COMMAND_TYPE_IMAGE): { - console.log('test1'); - let config = readStructAtAddress(renderCommand.config.value, imageConfigDefinition); - let srcContents = new Uint8Array(memoryDataView.buffer.slice(config.sourceURL.chars.value, config.sourceURL.chars.value + config.sourceURL.length.value)); + let config = renderCommand.renderData.image; + let imageURL = readStructAtAddress(config.imageData.value, stringDefinition); + let srcContents = new Uint8Array(memoryDataView.buffer.slice(imageURL.chars.value, imageURL.chars.value + imageURL.length.value)); if (srcContents.length !== elementData.previousMemoryText.length || MemoryIsDifferent(srcContents, elementData.previousMemoryText, srcContents.length)) { element.src = textDecoder.decode(srcContents); } @@ -566,6 +600,9 @@ break; } case (CLAY_RENDER_COMMAND_TYPE_CUSTOM): break; + default: { + console.log("Error: unhandled render command"); + } } } @@ -602,8 +639,8 @@ break; } case (CLAY_RENDER_COMMAND_TYPE_RECTANGLE): { - let config = readStructAtAddress(renderCommand.config.value, rectangleConfigDefinition); - let color = config.color; + let config = renderCommand.renderData.rectangle; + let color = config.backgroundColor; ctx.beginPath(); window.canvasContext.fillStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`; window.canvasContext.roundRect( @@ -615,33 +652,35 @@ ctx.fill(); ctx.closePath(); // Handle link clicks - let linkContents = config.link.length.value > 0 ? textDecoder.decode(new Uint8Array(memoryDataView.buffer.slice(config.link.chars.value, config.link.chars.value + config.link.length.value))) : 0; - memoryDataView.setUint32(0, renderCommand.id.value, true); - if (linkContents.length > 0 && (window.mouseDownThisFrame || window.touchDown) && instance.exports.Clay_PointerOver(0)) { - window.location.href = linkContents; + if (renderCommand.userData.value !== 0) { + let customData = readStructAtAddress(renderCommand.userData.value, customHTMLDataDefinition); + let linkContents = customData.link.length.value > 0 ? textDecoder.decode(new Uint8Array(memoryDataView.buffer.slice(customData.link.chars.value, customData.link.chars.value + customData.link.length.value))) : 0; + memoryDataView.setUint32(0, renderCommand.id.value, true); + if (linkContents.length > 0 && (window.mouseDownThisFrame || window.touchDown) && instance.exports.Clay_PointerOver(0)) { + window.location.href = linkContents; + } } break; } case (CLAY_RENDER_COMMAND_TYPE_BORDER): { - let config = readStructAtAddress(renderCommand.config.value, borderConfigDefinition); + let config = renderCommand.renderData.border; + let color = config.color; ctx.beginPath(); ctx.moveTo(boundingBox.x.value * scale, boundingBox.y.value * scale); // Top Left Corner if (config.cornerRadius.topLeft.value > 0) { - let lineWidth = config.top.width.value; + let lineWidth = config.width.top.value; let halfLineWidth = lineWidth / 2; ctx.moveTo((boundingBox.x.value + halfLineWidth) * scale, (boundingBox.y.value + config.cornerRadius.topLeft.value + halfLineWidth) * scale); - let color = config.top.color; ctx.lineWidth = lineWidth * scale; ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`; ctx.arcTo((boundingBox.x.value + halfLineWidth) * scale, (boundingBox.y.value + halfLineWidth) * scale, (boundingBox.x.value + config.cornerRadius.topLeft.value + halfLineWidth) * scale, (boundingBox.y.value + halfLineWidth) * scale, config.cornerRadius.topLeft.value * scale); ctx.stroke(); } // Top border - if (config.top.width.value > 0) { - let lineWidth = config.top.width.value; + if (config.width.top.value > 0) { + let lineWidth = config.width.top.value; let halfLineWidth = lineWidth / 2; - let color = config.top.color; ctx.lineWidth = lineWidth * scale; ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`; ctx.moveTo((boundingBox.x.value + config.cornerRadius.topLeft.value + halfLineWidth) * scale, (boundingBox.y.value + halfLineWidth) * scale); @@ -650,19 +689,17 @@ } // Top Right Corner if (config.cornerRadius.topRight.value > 0) { - let lineWidth = config.top.width.value; + let lineWidth = config.width.top.value; let halfLineWidth = lineWidth / 2; ctx.moveTo((boundingBox.x.value + boundingBox.width.value - config.cornerRadius.topRight.value - halfLineWidth) * scale, (boundingBox.y.value + halfLineWidth) * scale); - let color = config.top.color; ctx.lineWidth = lineWidth * scale; ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`; ctx.arcTo((boundingBox.x.value + boundingBox.width.value - halfLineWidth) * scale, (boundingBox.y.value + halfLineWidth) * scale, (boundingBox.x.value + boundingBox.width.value - halfLineWidth) * scale, (boundingBox.y.value + config.cornerRadius.topRight.value + halfLineWidth) * scale, config.cornerRadius.topRight.value * scale); ctx.stroke(); } // Right border - if (config.right.width.value > 0) { - let color = config.right.color; - let lineWidth = config.right.width.value; + if (config.width.right.value > 0) { + let lineWidth = config.width.right.value; let halfLineWidth = lineWidth / 2; ctx.lineWidth = lineWidth * scale; ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`; @@ -672,8 +709,7 @@ } // Bottom Right Corner if (config.cornerRadius.bottomRight.value > 0) { - let color = config.top.color; - let lineWidth = config.top.width.value; + let lineWidth = config.width.top.value; let halfLineWidth = lineWidth / 2; ctx.moveTo((boundingBox.x.value + boundingBox.width.value - halfLineWidth) * scale, (boundingBox.y.value + boundingBox.height.value - config.cornerRadius.bottomRight.value - halfLineWidth) * scale); ctx.lineWidth = lineWidth * scale; @@ -682,9 +718,8 @@ ctx.stroke(); } // Bottom Border - if (config.bottom.width.value > 0) { - let color = config.bottom.color; - let lineWidth = config.bottom.width.value; + if (config.width.bottom.value > 0) { + let lineWidth = config.width.bottom.value; let halfLineWidth = lineWidth / 2; ctx.lineWidth = lineWidth * scale; ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`; @@ -694,8 +729,7 @@ } // Bottom Left Corner if (config.cornerRadius.bottomLeft.value > 0) { - let color = config.bottom.color; - let lineWidth = config.bottom.width.value; + let lineWidth = config.width.bottom.value; let halfLineWidth = lineWidth / 2; ctx.moveTo((boundingBox.x.value + config.cornerRadius.bottomLeft.value + halfLineWidth) * scale, (boundingBox.y.value + boundingBox.height.value - halfLineWidth) * scale); ctx.lineWidth = lineWidth * scale; @@ -704,9 +738,8 @@ ctx.stroke(); } // Left Border - if (config.left.width.value > 0) { - let color = config.left.color; - let lineWidth = config.left.width.value; + if (config.width.left.value > 0) { + let lineWidth = config.width.left.value; let halfLineWidth = lineWidth / 2; ctx.lineWidth = lineWidth * scale; ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`; @@ -718,8 +751,8 @@ break; } case (CLAY_RENDER_COMMAND_TYPE_TEXT): { - let config = readStructAtAddress(renderCommand.config.value, textConfigDefinition); - let textContents = renderCommand.text; + let config = renderCommand.renderData.text; + let textContents = config.stringContents; let stringContents = new Uint8Array(memoryDataView.buffer.slice(textContents.chars.value, textContents.chars.value + textContents.length.value)); let fontSize = config.fontSize.value * GLOBAL_FONT_SCALING_FACTOR * scale; ctx.font = `${fontSize}px ${fontsById[config.fontId.value]}`; @@ -742,8 +775,9 @@ break; } case (CLAY_RENDER_COMMAND_TYPE_IMAGE): { - let config = readStructAtAddress(renderCommand.config.value, imageConfigDefinition); - let src = textDecoder.decode(new Uint8Array(memoryDataView.buffer.slice(config.sourceURL.chars.value, config.sourceURL.chars.value + config.sourceURL.length.value))); + let config = renderCommand.renderData.image; + let imageURL = readStructAtAddress(config.imageData.value, stringDefinition); + let src = textDecoder.decode(new Uint8Array(memoryDataView.buffer.slice(imageURL.chars.value, imageURL.chars.value + imageURL.length.value))); if (!imageCache[src]) { imageCache[src] = { image: new Image(), diff --git a/examples/clay-official-website/main.c b/examples/clay-official-website/main.c index 19d9635..7847132 100644 --- a/examples/clay-official-website/main.c +++ b/examples/clay-official-website/main.c @@ -3,7 +3,7 @@ double windowWidth = 1024, windowHeight = 768; float modelPageOneZRotation = 0; -uint32_t ACTIVE_RENDERER_INDEX = 0; +uint32_t ACTIVE_RENDERER_INDEX = 1; const uint32_t FONT_ID_BODY_16 = 0; const uint32_t FONT_ID_TITLE_56 = 1; @@ -52,13 +52,21 @@ typedef struct { CustomHTMLData* FrameAllocateCustomData(CustomHTMLData data) { CustomHTMLData *customData = (CustomHTMLData *)(frameArena.memory + frameArena.offset); + *customData = data; frameArena.offset += sizeof(CustomHTMLData); return customData; } -void LandingPageBlob(int index, int fontSize, Clay_Color color, Clay_String text, char* imageURL) { +Clay_String* FrameAllocateString(Clay_String string) { + Clay_String *allocated = (Clay_String *)(frameArena.memory + frameArena.offset); + *allocated = string; + frameArena.offset += sizeof(Clay_String); + return allocated; +} + +void LandingPageBlob(int index, int fontSize, Clay_Color color, Clay_String text, Clay_String imageURL) { CLAY({ .id = CLAY_IDI("HeroBlob", index), .layout = { .sizing = { CLAY_SIZING_GROW(.max = 480) }, .padding = CLAY_PADDING_ALL(16), .childGap = 16, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER} }, .border = { .color = color, .width = { 2, 2, 2, 2 }}, .cornerRadius = CLAY_CORNER_RADIUS(10) }) { - CLAY({ .id = CLAY_IDI("CheckImage", index), .layout = { .sizing = { CLAY_SIZING_FIXED(32) } }, .image = { .sourceDimensions = { 128, 128 }, .imageData = imageURL } }) {} + CLAY({ .id = CLAY_IDI("CheckImage", index), .layout = { .sizing = { CLAY_SIZING_FIXED(32) } }, .image = { .sourceDimensions = { 128, 128 }, .imageData = FrameAllocateString(imageURL) } }) {} CLAY_TEXT(text, CLAY_TEXT_CONFIG({ .fontSize = fontSize, .fontId = FONT_ID_BODY_24, .textColor = color })); } } @@ -72,11 +80,11 @@ void LandingPageDesktop() { CLAY_TEXT(CLAY_STRING("Clay is laying out this webpage right now!"), CLAY_TEXT_CONFIG({ .fontSize = 36, .fontId = FONT_ID_TITLE_36, .textColor = COLOR_ORANGE })); } CLAY({ .id = CLAY_ID("HeroImageOuter"), .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { .width = CLAY_SIZING_PERCENT(0.45f) }, .childAlignment = { CLAY_ALIGN_X_CENTER }, .childGap = 16 } }) { - LandingPageBlob(1, 32, COLOR_BLOB_BORDER_5, CLAY_STRING("High performance"), "/clay/images/check_5.png"); - LandingPageBlob(2, 32, COLOR_BLOB_BORDER_4, CLAY_STRING("Flexbox-style responsive layout"), "/clay/images/check_4.png"); - LandingPageBlob(3, 32, COLOR_BLOB_BORDER_3, CLAY_STRING("Declarative syntax"), "/clay/images/check_3.png"); - LandingPageBlob(4, 32, COLOR_BLOB_BORDER_2, CLAY_STRING("Single .h file for C/C++"), "/clay/images/check_2.png"); - LandingPageBlob(5, 32, COLOR_BLOB_BORDER_1, CLAY_STRING("Compile to 15kb .wasm"), "/clay/images/check_1.png"); + LandingPageBlob(1, 32, COLOR_BLOB_BORDER_5, CLAY_STRING("High performance"), CLAY_STRING("/clay/images/check_5.png")); + LandingPageBlob(2, 32, COLOR_BLOB_BORDER_4, CLAY_STRING("Flexbox-style responsive layout"), CLAY_STRING("/clay/images/check_4.png")); + LandingPageBlob(3, 32, COLOR_BLOB_BORDER_3, CLAY_STRING("Declarative syntax"), CLAY_STRING("/clay/images/check_3.png")); + LandingPageBlob(4, 32, COLOR_BLOB_BORDER_2, CLAY_STRING("Single .h file for C/C++"), CLAY_STRING("/clay/images/check_2.png")); + LandingPageBlob(5, 32, COLOR_BLOB_BORDER_1, CLAY_STRING("Compile to 15kb .wasm"), CLAY_STRING("/clay/images/check_1.png")); } } } @@ -90,11 +98,11 @@ void LandingPageMobile() { CLAY_TEXT(CLAY_STRING("Clay is laying out this webpage right now!"), CLAY_TEXT_CONFIG({ .fontSize = 32, .fontId = FONT_ID_TITLE_36, .textColor = COLOR_ORANGE })); } CLAY({ .id = CLAY_ID("HeroImageOuter"), .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { .width = CLAY_SIZING_GROW(0) }, .childAlignment = { CLAY_ALIGN_X_CENTER }, .childGap = 16 } }) { - LandingPageBlob(1, 28, COLOR_BLOB_BORDER_5, CLAY_STRING("High performance"), "/clay/images/check_5.png"); - LandingPageBlob(2, 28, COLOR_BLOB_BORDER_4, CLAY_STRING("Flexbox-style responsive layout"), "/clay/images/check_4.png"); - LandingPageBlob(3, 28, COLOR_BLOB_BORDER_3, CLAY_STRING("Declarative syntax"), "/clay/images/check_3.png"); - LandingPageBlob(4, 28, COLOR_BLOB_BORDER_2, CLAY_STRING("Single .h file for C/C++"), "/clay/images/check_2.png"); - LandingPageBlob(5, 28, COLOR_BLOB_BORDER_1, CLAY_STRING("Compile to 15kb .wasm"), "/clay/images/check_1.png"); + LandingPageBlob(1, 28, COLOR_BLOB_BORDER_5, CLAY_STRING("High performance"), CLAY_STRING("/clay/images/check_5.png")); + LandingPageBlob(2, 28, COLOR_BLOB_BORDER_4, CLAY_STRING("Flexbox-style responsive layout"), CLAY_STRING("/clay/images/check_4.png")); + LandingPageBlob(3, 28, COLOR_BLOB_BORDER_3, CLAY_STRING("Declarative syntax"), CLAY_STRING("/clay/images/check_3.png")); + LandingPageBlob(4, 28, COLOR_BLOB_BORDER_2, CLAY_STRING("Single .h file for C/C++"), CLAY_STRING("/clay/images/check_2.png")); + LandingPageBlob(5, 28, COLOR_BLOB_BORDER_1, CLAY_STRING("Compile to 15kb .wasm"), CLAY_STRING("/clay/images/check_1.png")); } } } @@ -148,7 +156,7 @@ void DeclarativeSyntaxPageDesktop() { CLAY_TEXT(CLAY_STRING("Create your own library of re-usable components from UI primitives like text, images and rectangles."), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_RED })); } CLAY({ .id = CLAY_ID("SyntaxPageRightImage"), .layout = { .sizing = { CLAY_SIZING_PERCENT(0.50) }, .childAlignment = {.x = CLAY_ALIGN_X_CENTER} } }) { - CLAY({ .id = CLAY_ID("SyntaxPageRightImageInner"), .layout = { .sizing = { CLAY_SIZING_GROW(.max = 568) } }, .image = { .sourceDimensions = {1136, 1194}, .imageData = "/clay/images/declarative.png" } }) {} + CLAY({ .id = CLAY_ID("SyntaxPageRightImageInner"), .layout = { .sizing = { CLAY_SIZING_GROW(.max = 568) } }, .image = { .sourceDimensions = {1136, 1194}, .imageData = FrameAllocateString(CLAY_STRING("/clay/images/declarative.png")) } }) {} } } } @@ -164,7 +172,7 @@ void DeclarativeSyntaxPageMobile() { CLAY_TEXT(CLAY_STRING("Create your own library of re-usable components from UI primitives like text, images and rectangles."), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_RED })); } CLAY({ .id = CLAY_ID("SyntaxPageRightImage"), .layout = { .sizing = { CLAY_SIZING_GROW(0) }, .childAlignment = {.x = CLAY_ALIGN_X_CENTER} } }) { - CLAY({ .id = CLAY_ID("SyntaxPageRightImageInner"), .layout = { .sizing = { CLAY_SIZING_GROW(.max = 568) } }, .image = { .sourceDimensions = {1136, 1194}, .imageData = "/clay/images/declarative.png" } }) {} + CLAY({ .id = CLAY_ID("SyntaxPageRightImageInner"), .layout = { .sizing = { CLAY_SIZING_GROW(.max = 568) } }, .image = { .sourceDimensions = {1136, 1194}, .imageData = FrameAllocateString(CLAY_STRING("/clay/images/declarative.png")) } }) {} } } } @@ -237,7 +245,7 @@ void RendererButtonActive(Clay_String text) { .layout = { .sizing = {CLAY_SIZING_FIXED(300) }, .padding = CLAY_PADDING_ALL(16) }, .backgroundColor = Clay_Hovered() ? COLOR_RED_HOVER : COLOR_RED, .cornerRadius = CLAY_CORNER_RADIUS(10), - .custom = { .customData = FrameAllocateCustomData((CustomHTMLData) { .disablePointerEvents = true, .cursorPointer = true })} + .userData = FrameAllocateCustomData((CustomHTMLData) { .disablePointerEvents = true, .cursorPointer = true }) }) { CLAY_TEXT(text, CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_LIGHT })); } @@ -249,7 +257,7 @@ void RendererButtonInactive(Clay_String text, size_t rendererIndex) { .border = { .width = {2, 2, 2, 2}, .color = COLOR_RED }, .backgroundColor = Clay_Hovered() ? COLOR_LIGHT_HOVER : COLOR_LIGHT, .cornerRadius = CLAY_CORNER_RADIUS(10), - .custom = { .customData = FrameAllocateCustomData((CustomHTMLData) { .disablePointerEvents = true, .cursorPointer = true })} + .userData = FrameAllocateCustomData((CustomHTMLData) { .disablePointerEvents = true, .cursorPointer = true }) }) { Clay_OnHover(HandleRendererButtonInteraction, rendererIndex); CLAY_TEXT(text, CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_RED })); @@ -315,7 +323,7 @@ void DebuggerPageDesktop() { CLAY_TEXT(CLAY_STRING("Press the \"d\" key to try it out now!"), CLAY_TEXT_CONFIG({ .fontSize = 32, .fontId = FONT_ID_TITLE_36, .textColor = COLOR_ORANGE })); } CLAY({ .id = CLAY_ID("DebuggerRightImageOuter"), .layout = { .sizing = { CLAY_SIZING_PERCENT(0.50) }, .childAlignment = {CLAY_ALIGN_X_CENTER} } }) { - CLAY({ .id = CLAY_ID("DebuggerPageRightImageInner"), .layout = { .sizing = { CLAY_SIZING_GROW(.max = 558) } }, .image = { .sourceDimensions = {1620, 1474}, .imageData = "/clay/images/debugger.png" } }) {} + CLAY({ .id = CLAY_ID("DebuggerPageRightImageInner"), .layout = { .sizing = { CLAY_SIZING_GROW(.max = 558) } }, .image = { .sourceDimensions = {1620, 1474}, .imageData = FrameAllocateString(CLAY_STRING("/clay/images/debugger.png")) } }) {} } } } @@ -337,10 +345,10 @@ Clay_RenderCommandArray CreateLayout(bool mobileScreen, float lerpValue) { CLAY_TEXT(CLAY_STRING("Clay"), &headerTextConfig); CLAY({ .id = CLAY_ID("Spacer"), .layout = { .sizing = { .width = CLAY_SIZING_GROW(0) } } }) {} if (!mobileScreen) { - CLAY({ .id = CLAY_ID("LinkExamplesOuter"), .layout = { .padding = {8, 8} }, .custom = { .customData = FrameAllocateCustomData((CustomHTMLData) { .link = CLAY_STRING("https://github.com/nicbarker/clay/tree/main/examples") }) } }) { + CLAY({ .id = CLAY_ID("LinkExamplesOuter"), .layout = { .padding = {8, 8} }, .userData = FrameAllocateCustomData((CustomHTMLData) { .link = CLAY_STRING("https://github.com/nicbarker/clay/tree/main/examples") }) }) { CLAY_TEXT(CLAY_STRING("Examples"), CLAY_TEXT_CONFIG({ .fontId = FONT_ID_BODY_24, .fontSize = 24, .textColor = {61, 26, 5, 255} })); } - CLAY({ .id = CLAY_ID("LinkDocsOuter"), .layout = { .padding = {8, 8} }, .custom = { .customData = FrameAllocateCustomData((CustomHTMLData) { .link = CLAY_STRING("https://github.com/nicbarker/clay/blob/main/README.md") }) } }) { + CLAY({ .id = CLAY_ID("LinkDocsOuter"), .layout = { .padding = {8, 8} }, .userData = FrameAllocateCustomData((CustomHTMLData) { .link = CLAY_STRING("https://github.com/nicbarker/clay/blob/main/README.md") }) }) { CLAY_TEXT(CLAY_STRING("Docs"), CLAY_TEXT_CONFIG({ .fontId = FONT_ID_BODY_24, .fontSize = 24, .textColor = {61, 26, 5, 255} })); } } @@ -349,7 +357,7 @@ Clay_RenderCommandArray CreateLayout(bool mobileScreen, float lerpValue) { .backgroundColor = Clay_Hovered() ? COLOR_LIGHT_HOVER : COLOR_LIGHT, .border = { .width = {2, 2, 2, 2}, .color = COLOR_RED }, .cornerRadius = CLAY_CORNER_RADIUS(10), - .custom = { .customData = FrameAllocateCustomData((CustomHTMLData) { .link = CLAY_STRING("https://github.com/nicbarker/clay/tree/main/examples") }) }, + .userData = FrameAllocateCustomData((CustomHTMLData) { .link = CLAY_STRING("https://github.com/nicbarker/clay/tree/main/examples") }), }) { CLAY_TEXT(CLAY_STRING("Discord"), CLAY_TEXT_CONFIG({ .fontId = FONT_ID_BODY_24, .fontSize = 24, .textColor = {61, 26, 5, 255} })); } @@ -358,7 +366,7 @@ Clay_RenderCommandArray CreateLayout(bool mobileScreen, float lerpValue) { .backgroundColor = Clay_Hovered() ? COLOR_LIGHT_HOVER : COLOR_LIGHT, .border = { .width = {2, 2, 2, 2}, .color = COLOR_RED }, .cornerRadius = CLAY_CORNER_RADIUS(10), - .custom = { .customData = FrameAllocateCustomData((CustomHTMLData) { .link = CLAY_STRING("https://github.com/nicbarker/clay") }) }, + .userData = FrameAllocateCustomData((CustomHTMLData) { .link = CLAY_STRING("https://github.com/nicbarker/clay") }), }) { CLAY_TEXT(CLAY_STRING("Github"), CLAY_TEXT_CONFIG({ .fontId = FONT_ID_BODY_24, .fontSize = 24, .textColor = {61, 26, 5, 255} })); } @@ -413,7 +421,12 @@ Clay_RenderCommandArray CreateLayout(bool mobileScreen, float lerpValue) { bool debugModeEnabled = false; +CLAY_WASM_EXPORT("SetScratchMemory") void SetScratchMemory(void * memory) { + frameArena.memory = memory; +} + CLAY_WASM_EXPORT("UpdateDrawFrame") Clay_RenderCommandArray UpdateDrawFrame(float width, float height, float mouseWheelX, float mouseWheelY, float mousePositionX, float mousePositionY, bool isTouchDown, bool isMouseDown, bool arrowKeyDownPressedThisFrame, bool arrowKeyUpPressedThisFrame, bool dKeyPressedThisFrame, float deltaTime) { + frameArena.offset = 0; windowWidth = width; windowHeight = height; Clay_SetLayoutDimensions((Clay_Dimensions) { width, height });