[Examples/clay-official-website] Update web renderer example to latest API
Some checks are pending
CMake on multiple platforms / build (Release, cl, cl, windows-latest) (push) Waiting to run
CMake on multiple platforms / build (Release, clang, clang++, ubuntu-latest) (push) Waiting to run
CMake on multiple platforms / build (Release, gcc, g++, ubuntu-latest) (push) Waiting to run

This commit is contained in:
Nic Barker 2025-02-10 16:14:13 +13:00
parent dcd6feda86
commit 76c8e1f115
5 changed files with 401 additions and 308 deletions

12
clay.h
View File

@ -424,6 +424,11 @@ typedef struct {
void* customData; void* customData;
} Clay_CustomRenderData; } Clay_CustomRenderData;
typedef struct {
bool horizontal;
bool vertical;
} Clay_ScrollRenderData;
typedef struct { typedef struct {
Clay_Color color; Clay_Color color;
Clay_CornerRadius cornerRadius; Clay_CornerRadius cornerRadius;
@ -436,6 +441,7 @@ typedef union {
Clay_ImageRenderData image; Clay_ImageRenderData image;
Clay_CustomRenderData custom; Clay_CustomRenderData custom;
Clay_BorderRenderData border; Clay_BorderRenderData border;
Clay_ScrollRenderData scroll;
} Clay_RenderData; } Clay_RenderData;
// Miscellaneous Structs & Enums --------------------------------- // Miscellaneous Structs & Enums ---------------------------------
@ -2249,6 +2255,12 @@ void Clay__CalculateFinalLayout(void) {
} }
case CLAY__ELEMENT_CONFIG_TYPE_SCROLL: { case CLAY__ELEMENT_CONFIG_TYPE_SCROLL: {
renderCommand.commandType = CLAY_RENDER_COMMAND_TYPE_SCISSOR_START; 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; break;
} }
case CLAY__ELEMENT_CONFIG_TYPE_IMAGE: { case CLAY__ELEMENT_CONFIG_TYPE_IMAGE: {

View File

@ -86,6 +86,10 @@
]; ];
let elementCache = {}; let elementCache = {};
let imageCache = {}; let imageCache = {};
let dimensionsDefinition = { type: 'struct', members: [
{name: 'width', type: 'float'},
{name: 'height', type: 'float'},
]};
let colorDefinition = { type: 'struct', members: [ let colorDefinition = { type: 'struct', members: [
{name: 'r', type: 'float' }, {name: 'r', type: 'float' },
{name: 'g', type: 'float' }, {name: 'g', type: 'float' },
@ -101,9 +105,12 @@
{name: 'chars', type: 'uint32_t' }, {name: 'chars', type: 'uint32_t' },
{name: 'baseChars', type: 'uint32_t' }, {name: 'baseChars', type: 'uint32_t' },
]}; ]};
let borderDefinition = { type: 'struct', members: [ let borderWidthDefinition = { type: 'struct', members: [
{name: 'width', type: 'uint32_t'}, {name: 'left', type: 'uint16_t'},
{name: 'color', ...colorDefinition}, {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: [ let cornerRadiusDefinition = { type: 'struct', members: [
{name: 'topLeft', type: 'float'}, {name: 'topLeft', type: 'float'},
@ -111,44 +118,53 @@
{name: 'bottomLeft', type: 'float'}, {name: 'bottomLeft', type: 'float'},
{name: 'bottomRight', 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: [ let textConfigDefinition = { name: 'text', type: 'struct', members: [
{ name: 'textColor', ...colorDefinition }, { name: 'textColor', ...colorDefinition },
{ name: 'fontId', type: 'uint16_t' }, { name: 'fontId', type: 'uint16_t' },
{ name: 'fontSize', type: 'uint16_t' }, { name: 'fontSize', type: 'uint16_t' },
{ name: 'letterSpacing', type: 'uint16_t' }, { name: 'letterSpacing', type: 'uint16_t' },
{ name: 'lineSpacing', type: 'uint16_t' }, { name: 'lineSpacing', type: 'uint16_t' },
{ name: 'wrapMode', type: 'uint32_t' }, { name: 'wrapMode', type: 'uint8_t' },
{ name: 'disablePointerEvents', 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: 'horizontal', type: 'bool' },
{ name: 'vertical', type: 'bool' }, { name: 'vertical', type: 'bool' },
]}; ]};
let imageConfigDefinition = { name: 'image', type: 'struct', members: [ let customHTMLDataDefinition = { type: 'struct', members: [
{ name: 'imageData', type: 'uint32_t' }, { name: 'link', ...stringDefinition },
{ name: 'sourceDimensions', type: 'struct', members: [ { name: 'cursorPointer', type: 'uint8_t' },
{ name: 'width', type: 'float' }, { name: 'disablePointerEvents', type: 'uint8_t' },
{ 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 renderCommandDefinition = { let renderCommandDefinition = {
name: 'CLay_RenderCommand', name: 'CLay_RenderCommand',
@ -160,14 +176,19 @@
{ name: 'width', type: 'float' }, { name: 'width', type: 'float' },
{ name: 'height', type: 'float' }, { name: 'height', type: 'float' },
]}, ]},
{ name: 'config', type: 'uint32_t'}, { name: 'renderData', type: 'union', members: [
{ name: 'textOrSharedConfig', type: 'union', members: [ { name: 'rectangle', ...rectangleRenderDataDefinition },
{ name: 'text', ...stringSliceDefinition }, { name: 'text', ...textRenderDataDefinition },
{ name: 'sharedConfig', type: 'uint32_t' } { 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: '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 'uint32_t': return 4;
case 'int32_t': return 4; case 'int32_t': return 4;
case 'uint16_t': return 2; case 'uint16_t': return 2;
case 'int16_t': return 2;
case 'uint8_t': return 1; case 'uint8_t': return 1;
case 'bool': return 1; case 'bool': return 1;
default: { default: {
@ -219,6 +241,7 @@
case 'uint32_t': return { value: memoryDataView.getUint32(address, true), __size: 4 }; case 'uint32_t': return { value: memoryDataView.getUint32(address, true), __size: 4 };
case 'int32_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 '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 'uint8_t': return { value: memoryDataView.getUint8(address, true), __size: 1 };
case 'bool': return { value: memoryDataView.getUint8(address, true), __size: 1 }; case 'bool': return { value: memoryDataView.getUint8(address, true), __size: 1 };
default: { default: {
@ -340,7 +363,7 @@
memoryDataView.setFloat32(addressOfOffset, -container.scrollLeft, true); memoryDataView.setFloat32(addressOfOffset, -container.scrollLeft, true);
memoryDataView.setFloat32(addressOfOffset + 4, -container.scrollTop, true); memoryDataView.setFloat32(addressOfOffset + 4, -container.scrollTop, true);
} }
} },
}, },
}; };
const { instance } = await WebAssembly.instantiateStreaming( const { instance } = await WebAssembly.instantiateStreaming(
@ -348,13 +371,15 @@
); );
memoryDataView = new DataView(new Uint8Array(instance.exports.memory.buffer).buffer); memoryDataView = new DataView(new Uint8Array(instance.exports.memory.buffer).buffer);
scratchSpaceAddress = instance.exports.__heap_base.value; 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; let arenaAddress = scratchSpaceAddress + 8;
window.instance = instance; window.instance = instance;
createMainArena(arenaAddress, heapSpaceAddress); createMainArena(arenaAddress, heapSpaceAddress);
memoryDataView.setFloat32(instance.exports.__heap_base.value, window.innerWidth, true); memoryDataView.setFloat32(instance.exports.__heap_base.value, window.innerWidth, true);
memoryDataView.setFloat32(instance.exports.__heap_base.value + 4, window.innerHeight, true); memoryDataView.setFloat32(instance.exports.__heap_base.value + 4, window.innerHeight, true);
instance.exports.Clay_Initialize(arenaAddress, instance.exports.__heap_base.value); instance.exports.Clay_Initialize(arenaAddress, instance.exports.__heap_base.value);
instance.exports.SetScratchMemory(arenaAddress, clayScratchSpaceAddress);
renderCommandSize = getStructTotalSize(renderCommandDefinition); renderCommandSize = getStructTotalSize(renderCommandDefinition);
renderLoop(); renderLoop();
} }
@ -368,6 +393,22 @@
return false; 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() { function renderLoopHTML() {
let capacity = memoryDataView.getInt32(scratchSpaceAddress, true); let capacity = memoryDataView.getInt32(scratchSpaceAddress, true);
let length = memoryDataView.getInt32(scratchSpaceAddress + 4, true); let length = memoryDataView.getInt32(scratchSpaceAddress + 4, true);
@ -375,7 +416,7 @@
let scissorStack = [{ nextAllocation: { x: 0, y: 0 }, element: htmlRoot, nextElementIndex: 0 }]; let scissorStack = [{ nextAllocation: { x: 0, y: 0 }, element: htmlRoot, nextElementIndex: 0 }];
let previousId = 0; let previousId = 0;
for (let i = 0; i < length; i++, arrayOffset += renderCommandSize) { 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 renderCommand = readStructAtAddress(arrayOffset, renderCommandDefinition);
let parentElement = scissorStack[scissorStack.length - 1]; let parentElement = scissorStack[scissorStack.length - 1];
let element = null; let element = null;
@ -384,13 +425,12 @@
let elementType = 'div'; let elementType = 'div';
switch (renderCommand.commandType.value & 0xff) { switch (renderCommand.commandType.value & 0xff) {
case CLAY_RENDER_COMMAND_TYPE_RECTANGLE: { case CLAY_RENDER_COMMAND_TYPE_RECTANGLE: {
if (readStructAtAddress(renderCommand.config.value, rectangleConfigDefinition).link.length.value > 0) { // if (readStructAtAddress(renderCommand.renderData.rectangle.value, rectangleRenderDataDefinition).link.length.value > 0) { TODO reimplement links
elementType = 'a'; // elementType = 'a';
} // }
break; break;
} }
case CLAY_RENDER_COMMAND_TYPE_IMAGE: { case CLAY_RENDER_COMMAND_TYPE_IMAGE: {
console.log('test5');
elementType = 'img'; break; elementType = 'img'; break;
} }
default: break; default: break;
@ -443,93 +483,82 @@
break; break;
} }
case (CLAY_RENDER_COMMAND_TYPE_RECTANGLE): { case (CLAY_RENDER_COMMAND_TYPE_RECTANGLE): {
let config = readStructAtAddress(renderCommand.config.value, rectangleConfigDefinition); let config = renderCommand.renderData.rectangle;
let sharedConfig = readStructAtAddress( renderCommand.textOrSharedConfig.sharedConfig.value, sharedConfigDefinition); let configMemory = JSON.stringify(config);
let configMemory = new Uint8Array(memoryDataView.buffer.slice(renderCommand.config.value, renderCommand.config.value + config.__size)); if (configMemory === elementData.previousMemoryConfig) {
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)) {
break; 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; 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; break;
} }
case (CLAY_RENDER_COMMAND_TYPE_BORDER): { case (CLAY_RENDER_COMMAND_TYPE_BORDER): {
let config = readStructAtAddress(renderCommand.config.value, borderConfigDefinition); let config = renderCommand.renderData.border;
let sharedConfig = readStructAtAddress( renderCommand.textOrSharedConfig.sharedConfig.value, sharedConfigDefinition); let configMemory = JSON.stringify(config);
let configMemory = new Uint8Array(memoryDataView.buffer.slice(renderCommand.config.value, renderCommand.config.value + config.__size)); if (configMemory === elementData.previousMemoryConfig) {
if (!dirty && !MemoryIsDifferent(configMemory, elementData.previousMemoryConfig, config.__size)) {
break; break;
} }
let color = config.color;
elementData.previousMemoryConfig = configMemory; elementData.previousMemoryConfig = configMemory;
if (config.left.width.value > 0) { if (config.width.left.value > 0) {
let color = config.left.color; element.style.borderLeft = `${config.width.left.value}px solid rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`
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.right.width.value > 0) { if (config.width.right.value > 0) {
let color = config.right.color; element.style.borderRight = `${config.width.right.value}px solid rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`
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.top.width.value > 0) { if (config.width.top.value > 0) {
let color = config.top.color; element.style.borderTop = `${config.width.top.value}px solid rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`
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.bottom.width.value > 0) { if (config.width.bottom.value > 0) {
let color = config.bottom.color; element.style.borderBottom = `${config.width.bottom.value}px solid rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`
element.style.borderBottom = `${config.bottom.width.value}px solid rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`
} }
if (sharedConfig.cornerRadius.topLeft.value > 0) { if (config.cornerRadius.topLeft.value > 0) {
element.style.borderTopLeftRadius = sharedConfig.cornerRadius.topLeft.value + 'px'; element.style.borderTopLeftRadius = config.cornerRadius.topLeft.value + 'px';
} }
if (sharedConfig.cornerRadius.topRight.value > 0) { if (config.cornerRadius.topRight.value > 0) {
element.style.borderTopRightRadius = sharedConfig.cornerRadius.topRight.value + 'px'; element.style.borderTopRightRadius = config.cornerRadius.topRight.value + 'px';
} }
if (sharedConfig.cornerRadius.bottomLeft.value > 0) { if (config.cornerRadius.bottomLeft.value > 0) {
element.style.borderBottomLeftRadius = sharedConfig.cornerRadius.bottomLeft.value + 'px'; element.style.borderBottomLeftRadius = config.cornerRadius.bottomLeft.value + 'px';
} }
if (sharedConfig.cornerRadius.bottomRight.value > 0) { if (config.cornerRadius.bottomRight.value > 0) {
element.style.borderBottomRightRadius = sharedConfig.cornerRadius.bottomRight.value + 'px'; element.style.borderBottomRightRadius = config.cornerRadius.bottomRight.value + 'px';
} }
break; break;
} }
case (CLAY_RENDER_COMMAND_TYPE_TEXT): { case (CLAY_RENDER_COMMAND_TYPE_TEXT): {
let config = readStructAtAddress(renderCommand.config.value, textConfigDefinition); let config = renderCommand.renderData.text;
let configMemory = new Uint8Array(memoryDataView.buffer.slice(renderCommand.config.value, renderCommand.config.value + config.__size)); let customData = readStructAtAddress(renderCommand.userData.value, customHTMLDataDefinition);
let textContents = renderCommand.textOrSharedConfig.text; let configMemory = JSON.stringify(config);
let stringContents = new Uint8Array(memoryDataView.buffer.slice(textContents.chars.value, textContents.chars.value + textContents.length.value)); let stringContents = new Uint8Array(memoryDataView.buffer.slice(config.stringContents.chars.value, config.stringContents.chars.value + config.stringContents.length.value));
if (MemoryIsDifferent(configMemory, elementData.previousMemoryConfig, config.__size)) { if (configMemory !== elementData.previousMemoryConfig) {
element.className = 'text'; element.className = 'text';
let textColor = config.textColor; let textColor = config.textColor;
let fontSize = Math.round(config.fontSize.value * GLOBAL_FONT_SCALING_FACTOR); 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.color = `rgba(${textColor.r.value}, ${textColor.g.value}, ${textColor.b.value}, ${textColor.a.value})`;
element.style.fontFamily = fontsById[config.fontId.value]; element.style.fontFamily = fontsById[config.fontId.value];
element.style.fontSize = fontSize + 'px'; element.style.fontSize = fontSize + 'px';
element.style.pointerEvents = config.disablePointerEvents.value ? 'none' : 'all'; element.style.pointerEvents = customData.disablePointerEvents.value ? 'none' : 'all';
elementData.previousMemoryConfig = configMemory; elementData.previousMemoryConfig = configMemory;
} }
if (stringContents.length !== elementData.previousMemoryText.length || MemoryIsDifferent(stringContents, elementData.previousMemoryText, stringContents.length)) { if (stringContents.length !== elementData.previousMemoryText.length || MemoryIsDifferent(stringContents, elementData.previousMemoryText, stringContents.length)) {
@ -540,7 +569,11 @@
} }
case (CLAY_RENDER_COMMAND_TYPE_SCISSOR_START): { case (CLAY_RENDER_COMMAND_TYPE_SCISSOR_START): {
scissorStack.push({ nextAllocation: { x: renderCommand.boundingBox.x.value, y: renderCommand.boundingBox.y.value }, element, nextElementIndex: 0 }); 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) { if (config.horizontal.value) {
element.style.overflowX = 'scroll'; element.style.overflowX = 'scroll';
element.style.pointerEvents = 'auto'; element.style.pointerEvents = 'auto';
@ -549,6 +582,7 @@
element.style.overflowY = 'scroll'; element.style.overflowY = 'scroll';
element.style.pointerEvents = 'auto'; element.style.pointerEvents = 'auto';
} }
elementData.previousMemoryConfig = configMemory;
break; break;
} }
case (CLAY_RENDER_COMMAND_TYPE_SCISSOR_END): { case (CLAY_RENDER_COMMAND_TYPE_SCISSOR_END): {
@ -556,9 +590,9 @@
break; break;
} }
case (CLAY_RENDER_COMMAND_TYPE_IMAGE): { case (CLAY_RENDER_COMMAND_TYPE_IMAGE): {
console.log('test1'); let config = renderCommand.renderData.image;
let config = readStructAtAddress(renderCommand.config.value, imageConfigDefinition); let imageURL = readStructAtAddress(config.imageData.value, stringDefinition);
let srcContents = new Uint8Array(memoryDataView.buffer.slice(config.sourceURL.chars.value, config.sourceURL.chars.value + config.sourceURL.length.value)); 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)) { if (srcContents.length !== elementData.previousMemoryText.length || MemoryIsDifferent(srcContents, elementData.previousMemoryText, srcContents.length)) {
element.src = textDecoder.decode(srcContents); element.src = textDecoder.decode(srcContents);
} }
@ -566,6 +600,9 @@
break; break;
} }
case (CLAY_RENDER_COMMAND_TYPE_CUSTOM): break; case (CLAY_RENDER_COMMAND_TYPE_CUSTOM): break;
default: {
console.log("Error: unhandled render command");
}
} }
} }
@ -602,8 +639,8 @@
break; break;
} }
case (CLAY_RENDER_COMMAND_TYPE_RECTANGLE): { case (CLAY_RENDER_COMMAND_TYPE_RECTANGLE): {
let config = readStructAtAddress(renderCommand.config.value, rectangleConfigDefinition); let config = renderCommand.renderData.rectangle;
let color = config.color; let color = config.backgroundColor;
ctx.beginPath(); ctx.beginPath();
window.canvasContext.fillStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`; window.canvasContext.fillStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`;
window.canvasContext.roundRect( window.canvasContext.roundRect(
@ -615,33 +652,35 @@
ctx.fill(); ctx.fill();
ctx.closePath(); ctx.closePath();
// Handle link clicks // 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; if (renderCommand.userData.value !== 0) {
memoryDataView.setUint32(0, renderCommand.id.value, true); let customData = readStructAtAddress(renderCommand.userData.value, customHTMLDataDefinition);
if (linkContents.length > 0 && (window.mouseDownThisFrame || window.touchDown) && instance.exports.Clay_PointerOver(0)) { 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;
window.location.href = linkContents; 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; break;
} }
case (CLAY_RENDER_COMMAND_TYPE_BORDER): { 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.beginPath();
ctx.moveTo(boundingBox.x.value * scale, boundingBox.y.value * scale); ctx.moveTo(boundingBox.x.value * scale, boundingBox.y.value * scale);
// Top Left Corner // Top Left Corner
if (config.cornerRadius.topLeft.value > 0) { if (config.cornerRadius.topLeft.value > 0) {
let lineWidth = config.top.width.value; let lineWidth = config.width.top.value;
let halfLineWidth = lineWidth / 2; let halfLineWidth = lineWidth / 2;
ctx.moveTo((boundingBox.x.value + halfLineWidth) * scale, (boundingBox.y.value + config.cornerRadius.topLeft.value + halfLineWidth) * scale); 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.lineWidth = lineWidth * scale;
ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`; 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.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(); ctx.stroke();
} }
// Top border // Top border
if (config.top.width.value > 0) { if (config.width.top.value > 0) {
let lineWidth = config.top.width.value; let lineWidth = config.width.top.value;
let halfLineWidth = lineWidth / 2; let halfLineWidth = lineWidth / 2;
let color = config.top.color;
ctx.lineWidth = lineWidth * scale; ctx.lineWidth = lineWidth * scale;
ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`; 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); ctx.moveTo((boundingBox.x.value + config.cornerRadius.topLeft.value + halfLineWidth) * scale, (boundingBox.y.value + halfLineWidth) * scale);
@ -650,19 +689,17 @@
} }
// Top Right Corner // Top Right Corner
if (config.cornerRadius.topRight.value > 0) { if (config.cornerRadius.topRight.value > 0) {
let lineWidth = config.top.width.value; let lineWidth = config.width.top.value;
let halfLineWidth = lineWidth / 2; let halfLineWidth = lineWidth / 2;
ctx.moveTo((boundingBox.x.value + boundingBox.width.value - config.cornerRadius.topRight.value - halfLineWidth) * scale, (boundingBox.y.value + halfLineWidth) * scale); 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.lineWidth = lineWidth * scale;
ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`; 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.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(); ctx.stroke();
} }
// Right border // Right border
if (config.right.width.value > 0) { if (config.width.right.value > 0) {
let color = config.right.color; let lineWidth = config.width.right.value;
let lineWidth = config.right.width.value;
let halfLineWidth = lineWidth / 2; let halfLineWidth = lineWidth / 2;
ctx.lineWidth = lineWidth * scale; ctx.lineWidth = lineWidth * scale;
ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`; ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`;
@ -672,8 +709,7 @@
} }
// Bottom Right Corner // Bottom Right Corner
if (config.cornerRadius.bottomRight.value > 0) { if (config.cornerRadius.bottomRight.value > 0) {
let color = config.top.color; let lineWidth = config.width.top.value;
let lineWidth = config.top.width.value;
let halfLineWidth = lineWidth / 2; 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.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; ctx.lineWidth = lineWidth * scale;
@ -682,9 +718,8 @@
ctx.stroke(); ctx.stroke();
} }
// Bottom Border // Bottom Border
if (config.bottom.width.value > 0) { if (config.width.bottom.value > 0) {
let color = config.bottom.color; let lineWidth = config.width.bottom.value;
let lineWidth = config.bottom.width.value;
let halfLineWidth = lineWidth / 2; let halfLineWidth = lineWidth / 2;
ctx.lineWidth = lineWidth * scale; ctx.lineWidth = lineWidth * scale;
ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`; ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`;
@ -694,8 +729,7 @@
} }
// Bottom Left Corner // Bottom Left Corner
if (config.cornerRadius.bottomLeft.value > 0) { if (config.cornerRadius.bottomLeft.value > 0) {
let color = config.bottom.color; let lineWidth = config.width.bottom.value;
let lineWidth = config.bottom.width.value;
let halfLineWidth = lineWidth / 2; let halfLineWidth = lineWidth / 2;
ctx.moveTo((boundingBox.x.value + config.cornerRadius.bottomLeft.value + halfLineWidth) * scale, (boundingBox.y.value + boundingBox.height.value - halfLineWidth) * scale); ctx.moveTo((boundingBox.x.value + config.cornerRadius.bottomLeft.value + halfLineWidth) * scale, (boundingBox.y.value + boundingBox.height.value - halfLineWidth) * scale);
ctx.lineWidth = lineWidth * scale; ctx.lineWidth = lineWidth * scale;
@ -704,9 +738,8 @@
ctx.stroke(); ctx.stroke();
} }
// Left Border // Left Border
if (config.left.width.value > 0) { if (config.width.left.value > 0) {
let color = config.left.color; let lineWidth = config.width.left.value;
let lineWidth = config.left.width.value;
let halfLineWidth = lineWidth / 2; let halfLineWidth = lineWidth / 2;
ctx.lineWidth = lineWidth * scale; ctx.lineWidth = lineWidth * scale;
ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`; ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`;
@ -718,8 +751,8 @@
break; break;
} }
case (CLAY_RENDER_COMMAND_TYPE_TEXT): { case (CLAY_RENDER_COMMAND_TYPE_TEXT): {
let config = readStructAtAddress(renderCommand.config.value, textConfigDefinition); let config = renderCommand.renderData.text;
let textContents = renderCommand.text; let textContents = config.stringContents;
let stringContents = new Uint8Array(memoryDataView.buffer.slice(textContents.chars.value, textContents.chars.value + textContents.length.value)); 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; let fontSize = config.fontSize.value * GLOBAL_FONT_SCALING_FACTOR * scale;
ctx.font = `${fontSize}px ${fontsById[config.fontId.value]}`; ctx.font = `${fontSize}px ${fontsById[config.fontId.value]}`;
@ -742,8 +775,9 @@
break; break;
} }
case (CLAY_RENDER_COMMAND_TYPE_IMAGE): { case (CLAY_RENDER_COMMAND_TYPE_IMAGE): {
let config = readStructAtAddress(renderCommand.config.value, imageConfigDefinition); let config = renderCommand.renderData.image;
let src = textDecoder.decode(new Uint8Array(memoryDataView.buffer.slice(config.sourceURL.chars.value, config.sourceURL.chars.value + config.sourceURL.length.value))); 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]) { if (!imageCache[src]) {
imageCache[src] = { imageCache[src] = {
image: new Image(), image: new Image(),

View File

@ -86,6 +86,10 @@
]; ];
let elementCache = {}; let elementCache = {};
let imageCache = {}; let imageCache = {};
let dimensionsDefinition = { type: 'struct', members: [
{name: 'width', type: 'float'},
{name: 'height', type: 'float'},
]};
let colorDefinition = { type: 'struct', members: [ let colorDefinition = { type: 'struct', members: [
{name: 'r', type: 'float' }, {name: 'r', type: 'float' },
{name: 'g', type: 'float' }, {name: 'g', type: 'float' },
@ -101,9 +105,12 @@
{name: 'chars', type: 'uint32_t' }, {name: 'chars', type: 'uint32_t' },
{name: 'baseChars', type: 'uint32_t' }, {name: 'baseChars', type: 'uint32_t' },
]}; ]};
let borderDefinition = { type: 'struct', members: [ let borderWidthDefinition = { type: 'struct', members: [
{name: 'width', type: 'uint32_t'}, {name: 'left', type: 'uint16_t'},
{name: 'color', ...colorDefinition}, {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: [ let cornerRadiusDefinition = { type: 'struct', members: [
{name: 'topLeft', type: 'float'}, {name: 'topLeft', type: 'float'},
@ -111,44 +118,53 @@
{name: 'bottomLeft', type: 'float'}, {name: 'bottomLeft', type: 'float'},
{name: 'bottomRight', 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: [ let textConfigDefinition = { name: 'text', type: 'struct', members: [
{ name: 'textColor', ...colorDefinition }, { name: 'textColor', ...colorDefinition },
{ name: 'fontId', type: 'uint16_t' }, { name: 'fontId', type: 'uint16_t' },
{ name: 'fontSize', type: 'uint16_t' }, { name: 'fontSize', type: 'uint16_t' },
{ name: 'letterSpacing', type: 'uint16_t' }, { name: 'letterSpacing', type: 'uint16_t' },
{ name: 'lineSpacing', type: 'uint16_t' }, { name: 'lineSpacing', type: 'uint16_t' },
{ name: 'wrapMode', type: 'uint32_t' }, { name: 'wrapMode', type: 'uint8_t' },
{ name: 'disablePointerEvents', 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: 'horizontal', type: 'bool' },
{ name: 'vertical', type: 'bool' }, { name: 'vertical', type: 'bool' },
]}; ]};
let imageConfigDefinition = { name: 'image', type: 'struct', members: [ let customHTMLDataDefinition = { type: 'struct', members: [
{ name: 'imageData', type: 'uint32_t' }, { name: 'link', ...stringDefinition },
{ name: 'sourceDimensions', type: 'struct', members: [ { name: 'cursorPointer', type: 'uint8_t' },
{ name: 'width', type: 'float' }, { name: 'disablePointerEvents', type: 'uint8_t' },
{ 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 renderCommandDefinition = { let renderCommandDefinition = {
name: 'CLay_RenderCommand', name: 'CLay_RenderCommand',
@ -160,14 +176,19 @@
{ name: 'width', type: 'float' }, { name: 'width', type: 'float' },
{ name: 'height', type: 'float' }, { name: 'height', type: 'float' },
]}, ]},
{ name: 'config', type: 'uint32_t'}, { name: 'renderData', type: 'union', members: [
{ name: 'textOrSharedConfig', type: 'union', members: [ { name: 'rectangle', ...rectangleRenderDataDefinition },
{ name: 'text', ...stringSliceDefinition }, { name: 'text', ...textRenderDataDefinition },
{ name: 'sharedConfig', type: 'uint32_t' } { 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: '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 'uint32_t': return 4;
case 'int32_t': return 4; case 'int32_t': return 4;
case 'uint16_t': return 2; case 'uint16_t': return 2;
case 'int16_t': return 2;
case 'uint8_t': return 1; case 'uint8_t': return 1;
case 'bool': return 1; case 'bool': return 1;
default: { default: {
@ -219,6 +241,7 @@
case 'uint32_t': return { value: memoryDataView.getUint32(address, true), __size: 4 }; case 'uint32_t': return { value: memoryDataView.getUint32(address, true), __size: 4 };
case 'int32_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 '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 'uint8_t': return { value: memoryDataView.getUint8(address, true), __size: 1 };
case 'bool': return { value: memoryDataView.getUint8(address, true), __size: 1 }; case 'bool': return { value: memoryDataView.getUint8(address, true), __size: 1 };
default: { default: {
@ -340,7 +363,7 @@
memoryDataView.setFloat32(addressOfOffset, -container.scrollLeft, true); memoryDataView.setFloat32(addressOfOffset, -container.scrollLeft, true);
memoryDataView.setFloat32(addressOfOffset + 4, -container.scrollTop, true); memoryDataView.setFloat32(addressOfOffset + 4, -container.scrollTop, true);
} }
} },
}, },
}; };
const { instance } = await WebAssembly.instantiateStreaming( const { instance } = await WebAssembly.instantiateStreaming(
@ -348,13 +371,15 @@
); );
memoryDataView = new DataView(new Uint8Array(instance.exports.memory.buffer).buffer); memoryDataView = new DataView(new Uint8Array(instance.exports.memory.buffer).buffer);
scratchSpaceAddress = instance.exports.__heap_base.value; 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; let arenaAddress = scratchSpaceAddress + 8;
window.instance = instance; window.instance = instance;
createMainArena(arenaAddress, heapSpaceAddress); createMainArena(arenaAddress, heapSpaceAddress);
memoryDataView.setFloat32(instance.exports.__heap_base.value, window.innerWidth, true); memoryDataView.setFloat32(instance.exports.__heap_base.value, window.innerWidth, true);
memoryDataView.setFloat32(instance.exports.__heap_base.value + 4, window.innerHeight, true); memoryDataView.setFloat32(instance.exports.__heap_base.value + 4, window.innerHeight, true);
instance.exports.Clay_Initialize(arenaAddress, instance.exports.__heap_base.value); instance.exports.Clay_Initialize(arenaAddress, instance.exports.__heap_base.value);
instance.exports.SetScratchMemory(arenaAddress, clayScratchSpaceAddress);
renderCommandSize = getStructTotalSize(renderCommandDefinition); renderCommandSize = getStructTotalSize(renderCommandDefinition);
renderLoop(); renderLoop();
} }
@ -368,6 +393,22 @@
return false; 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() { function renderLoopHTML() {
let capacity = memoryDataView.getInt32(scratchSpaceAddress, true); let capacity = memoryDataView.getInt32(scratchSpaceAddress, true);
let length = memoryDataView.getInt32(scratchSpaceAddress + 4, true); let length = memoryDataView.getInt32(scratchSpaceAddress + 4, true);
@ -375,7 +416,7 @@
let scissorStack = [{ nextAllocation: { x: 0, y: 0 }, element: htmlRoot, nextElementIndex: 0 }]; let scissorStack = [{ nextAllocation: { x: 0, y: 0 }, element: htmlRoot, nextElementIndex: 0 }];
let previousId = 0; let previousId = 0;
for (let i = 0; i < length; i++, arrayOffset += renderCommandSize) { 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 renderCommand = readStructAtAddress(arrayOffset, renderCommandDefinition);
let parentElement = scissorStack[scissorStack.length - 1]; let parentElement = scissorStack[scissorStack.length - 1];
let element = null; let element = null;
@ -384,13 +425,12 @@
let elementType = 'div'; let elementType = 'div';
switch (renderCommand.commandType.value & 0xff) { switch (renderCommand.commandType.value & 0xff) {
case CLAY_RENDER_COMMAND_TYPE_RECTANGLE: { case CLAY_RENDER_COMMAND_TYPE_RECTANGLE: {
if (readStructAtAddress(renderCommand.config.value, rectangleConfigDefinition).link.length.value > 0) { // if (readStructAtAddress(renderCommand.renderData.rectangle.value, rectangleRenderDataDefinition).link.length.value > 0) { TODO reimplement links
elementType = 'a'; // elementType = 'a';
} // }
break; break;
} }
case CLAY_RENDER_COMMAND_TYPE_IMAGE: { case CLAY_RENDER_COMMAND_TYPE_IMAGE: {
console.log('test5');
elementType = 'img'; break; elementType = 'img'; break;
} }
default: break; default: break;
@ -443,93 +483,82 @@
break; break;
} }
case (CLAY_RENDER_COMMAND_TYPE_RECTANGLE): { case (CLAY_RENDER_COMMAND_TYPE_RECTANGLE): {
let config = readStructAtAddress(renderCommand.config.value, rectangleConfigDefinition); let config = renderCommand.renderData.rectangle;
let sharedConfig = readStructAtAddress( renderCommand.textOrSharedConfig.sharedConfig.value, sharedConfigDefinition); let configMemory = JSON.stringify(config);
let configMemory = new Uint8Array(memoryDataView.buffer.slice(renderCommand.config.value, renderCommand.config.value + config.__size)); if (configMemory === elementData.previousMemoryConfig) {
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)) {
break; 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; 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; break;
} }
case (CLAY_RENDER_COMMAND_TYPE_BORDER): { case (CLAY_RENDER_COMMAND_TYPE_BORDER): {
let config = readStructAtAddress(renderCommand.config.value, borderConfigDefinition); let config = renderCommand.renderData.border;
let sharedConfig = readStructAtAddress( renderCommand.textOrSharedConfig.sharedConfig.value, sharedConfigDefinition); let configMemory = JSON.stringify(config);
let configMemory = new Uint8Array(memoryDataView.buffer.slice(renderCommand.config.value, renderCommand.config.value + config.__size)); if (configMemory === elementData.previousMemoryConfig) {
if (!dirty && !MemoryIsDifferent(configMemory, elementData.previousMemoryConfig, config.__size)) {
break; break;
} }
let color = config.color;
elementData.previousMemoryConfig = configMemory; elementData.previousMemoryConfig = configMemory;
if (config.left.width.value > 0) { if (config.width.left.value > 0) {
let color = config.left.color; element.style.borderLeft = `${config.width.left.value}px solid rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`
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.right.width.value > 0) { if (config.width.right.value > 0) {
let color = config.right.color; element.style.borderRight = `${config.width.right.value}px solid rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`
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.top.width.value > 0) { if (config.width.top.value > 0) {
let color = config.top.color; element.style.borderTop = `${config.width.top.value}px solid rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`
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.bottom.width.value > 0) { if (config.width.bottom.value > 0) {
let color = config.bottom.color; element.style.borderBottom = `${config.width.bottom.value}px solid rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`
element.style.borderBottom = `${config.bottom.width.value}px solid rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`
} }
if (sharedConfig.cornerRadius.topLeft.value > 0) { if (config.cornerRadius.topLeft.value > 0) {
element.style.borderTopLeftRadius = sharedConfig.cornerRadius.topLeft.value + 'px'; element.style.borderTopLeftRadius = config.cornerRadius.topLeft.value + 'px';
} }
if (sharedConfig.cornerRadius.topRight.value > 0) { if (config.cornerRadius.topRight.value > 0) {
element.style.borderTopRightRadius = sharedConfig.cornerRadius.topRight.value + 'px'; element.style.borderTopRightRadius = config.cornerRadius.topRight.value + 'px';
} }
if (sharedConfig.cornerRadius.bottomLeft.value > 0) { if (config.cornerRadius.bottomLeft.value > 0) {
element.style.borderBottomLeftRadius = sharedConfig.cornerRadius.bottomLeft.value + 'px'; element.style.borderBottomLeftRadius = config.cornerRadius.bottomLeft.value + 'px';
} }
if (sharedConfig.cornerRadius.bottomRight.value > 0) { if (config.cornerRadius.bottomRight.value > 0) {
element.style.borderBottomRightRadius = sharedConfig.cornerRadius.bottomRight.value + 'px'; element.style.borderBottomRightRadius = config.cornerRadius.bottomRight.value + 'px';
} }
break; break;
} }
case (CLAY_RENDER_COMMAND_TYPE_TEXT): { case (CLAY_RENDER_COMMAND_TYPE_TEXT): {
let config = readStructAtAddress(renderCommand.config.value, textConfigDefinition); let config = renderCommand.renderData.text;
let configMemory = new Uint8Array(memoryDataView.buffer.slice(renderCommand.config.value, renderCommand.config.value + config.__size)); let customData = readStructAtAddress(renderCommand.userData.value, customHTMLDataDefinition);
let textContents = renderCommand.textOrSharedConfig.text; let configMemory = JSON.stringify(config);
let stringContents = new Uint8Array(memoryDataView.buffer.slice(textContents.chars.value, textContents.chars.value + textContents.length.value)); let stringContents = new Uint8Array(memoryDataView.buffer.slice(config.stringContents.chars.value, config.stringContents.chars.value + config.stringContents.length.value));
if (MemoryIsDifferent(configMemory, elementData.previousMemoryConfig, config.__size)) { if (configMemory !== elementData.previousMemoryConfig) {
element.className = 'text'; element.className = 'text';
let textColor = config.textColor; let textColor = config.textColor;
let fontSize = Math.round(config.fontSize.value * GLOBAL_FONT_SCALING_FACTOR); 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.color = `rgba(${textColor.r.value}, ${textColor.g.value}, ${textColor.b.value}, ${textColor.a.value})`;
element.style.fontFamily = fontsById[config.fontId.value]; element.style.fontFamily = fontsById[config.fontId.value];
element.style.fontSize = fontSize + 'px'; element.style.fontSize = fontSize + 'px';
element.style.pointerEvents = config.disablePointerEvents.value ? 'none' : 'all'; element.style.pointerEvents = customData.disablePointerEvents.value ? 'none' : 'all';
elementData.previousMemoryConfig = configMemory; elementData.previousMemoryConfig = configMemory;
} }
if (stringContents.length !== elementData.previousMemoryText.length || MemoryIsDifferent(stringContents, elementData.previousMemoryText, stringContents.length)) { if (stringContents.length !== elementData.previousMemoryText.length || MemoryIsDifferent(stringContents, elementData.previousMemoryText, stringContents.length)) {
@ -540,7 +569,11 @@
} }
case (CLAY_RENDER_COMMAND_TYPE_SCISSOR_START): { case (CLAY_RENDER_COMMAND_TYPE_SCISSOR_START): {
scissorStack.push({ nextAllocation: { x: renderCommand.boundingBox.x.value, y: renderCommand.boundingBox.y.value }, element, nextElementIndex: 0 }); 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) { if (config.horizontal.value) {
element.style.overflowX = 'scroll'; element.style.overflowX = 'scroll';
element.style.pointerEvents = 'auto'; element.style.pointerEvents = 'auto';
@ -549,6 +582,7 @@
element.style.overflowY = 'scroll'; element.style.overflowY = 'scroll';
element.style.pointerEvents = 'auto'; element.style.pointerEvents = 'auto';
} }
elementData.previousMemoryConfig = configMemory;
break; break;
} }
case (CLAY_RENDER_COMMAND_TYPE_SCISSOR_END): { case (CLAY_RENDER_COMMAND_TYPE_SCISSOR_END): {
@ -556,9 +590,9 @@
break; break;
} }
case (CLAY_RENDER_COMMAND_TYPE_IMAGE): { case (CLAY_RENDER_COMMAND_TYPE_IMAGE): {
console.log('test1'); let config = renderCommand.renderData.image;
let config = readStructAtAddress(renderCommand.config.value, imageConfigDefinition); let imageURL = readStructAtAddress(config.imageData.value, stringDefinition);
let srcContents = new Uint8Array(memoryDataView.buffer.slice(config.sourceURL.chars.value, config.sourceURL.chars.value + config.sourceURL.length.value)); 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)) { if (srcContents.length !== elementData.previousMemoryText.length || MemoryIsDifferent(srcContents, elementData.previousMemoryText, srcContents.length)) {
element.src = textDecoder.decode(srcContents); element.src = textDecoder.decode(srcContents);
} }
@ -566,6 +600,9 @@
break; break;
} }
case (CLAY_RENDER_COMMAND_TYPE_CUSTOM): break; case (CLAY_RENDER_COMMAND_TYPE_CUSTOM): break;
default: {
console.log("Error: unhandled render command");
}
} }
} }
@ -602,8 +639,8 @@
break; break;
} }
case (CLAY_RENDER_COMMAND_TYPE_RECTANGLE): { case (CLAY_RENDER_COMMAND_TYPE_RECTANGLE): {
let config = readStructAtAddress(renderCommand.config.value, rectangleConfigDefinition); let config = renderCommand.renderData.rectangle;
let color = config.color; let color = config.backgroundColor;
ctx.beginPath(); ctx.beginPath();
window.canvasContext.fillStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`; window.canvasContext.fillStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`;
window.canvasContext.roundRect( window.canvasContext.roundRect(
@ -615,33 +652,35 @@
ctx.fill(); ctx.fill();
ctx.closePath(); ctx.closePath();
// Handle link clicks // 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; if (renderCommand.userData.value !== 0) {
memoryDataView.setUint32(0, renderCommand.id.value, true); let customData = readStructAtAddress(renderCommand.userData.value, customHTMLDataDefinition);
if (linkContents.length > 0 && (window.mouseDownThisFrame || window.touchDown) && instance.exports.Clay_PointerOver(0)) { 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;
window.location.href = linkContents; 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; break;
} }
case (CLAY_RENDER_COMMAND_TYPE_BORDER): { 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.beginPath();
ctx.moveTo(boundingBox.x.value * scale, boundingBox.y.value * scale); ctx.moveTo(boundingBox.x.value * scale, boundingBox.y.value * scale);
// Top Left Corner // Top Left Corner
if (config.cornerRadius.topLeft.value > 0) { if (config.cornerRadius.topLeft.value > 0) {
let lineWidth = config.top.width.value; let lineWidth = config.width.top.value;
let halfLineWidth = lineWidth / 2; let halfLineWidth = lineWidth / 2;
ctx.moveTo((boundingBox.x.value + halfLineWidth) * scale, (boundingBox.y.value + config.cornerRadius.topLeft.value + halfLineWidth) * scale); 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.lineWidth = lineWidth * scale;
ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`; 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.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(); ctx.stroke();
} }
// Top border // Top border
if (config.top.width.value > 0) { if (config.width.top.value > 0) {
let lineWidth = config.top.width.value; let lineWidth = config.width.top.value;
let halfLineWidth = lineWidth / 2; let halfLineWidth = lineWidth / 2;
let color = config.top.color;
ctx.lineWidth = lineWidth * scale; ctx.lineWidth = lineWidth * scale;
ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`; 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); ctx.moveTo((boundingBox.x.value + config.cornerRadius.topLeft.value + halfLineWidth) * scale, (boundingBox.y.value + halfLineWidth) * scale);
@ -650,19 +689,17 @@
} }
// Top Right Corner // Top Right Corner
if (config.cornerRadius.topRight.value > 0) { if (config.cornerRadius.topRight.value > 0) {
let lineWidth = config.top.width.value; let lineWidth = config.width.top.value;
let halfLineWidth = lineWidth / 2; let halfLineWidth = lineWidth / 2;
ctx.moveTo((boundingBox.x.value + boundingBox.width.value - config.cornerRadius.topRight.value - halfLineWidth) * scale, (boundingBox.y.value + halfLineWidth) * scale); 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.lineWidth = lineWidth * scale;
ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`; 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.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(); ctx.stroke();
} }
// Right border // Right border
if (config.right.width.value > 0) { if (config.width.right.value > 0) {
let color = config.right.color; let lineWidth = config.width.right.value;
let lineWidth = config.right.width.value;
let halfLineWidth = lineWidth / 2; let halfLineWidth = lineWidth / 2;
ctx.lineWidth = lineWidth * scale; ctx.lineWidth = lineWidth * scale;
ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`; ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`;
@ -672,8 +709,7 @@
} }
// Bottom Right Corner // Bottom Right Corner
if (config.cornerRadius.bottomRight.value > 0) { if (config.cornerRadius.bottomRight.value > 0) {
let color = config.top.color; let lineWidth = config.width.top.value;
let lineWidth = config.top.width.value;
let halfLineWidth = lineWidth / 2; 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.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; ctx.lineWidth = lineWidth * scale;
@ -682,9 +718,8 @@
ctx.stroke(); ctx.stroke();
} }
// Bottom Border // Bottom Border
if (config.bottom.width.value > 0) { if (config.width.bottom.value > 0) {
let color = config.bottom.color; let lineWidth = config.width.bottom.value;
let lineWidth = config.bottom.width.value;
let halfLineWidth = lineWidth / 2; let halfLineWidth = lineWidth / 2;
ctx.lineWidth = lineWidth * scale; ctx.lineWidth = lineWidth * scale;
ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`; ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`;
@ -694,8 +729,7 @@
} }
// Bottom Left Corner // Bottom Left Corner
if (config.cornerRadius.bottomLeft.value > 0) { if (config.cornerRadius.bottomLeft.value > 0) {
let color = config.bottom.color; let lineWidth = config.width.bottom.value;
let lineWidth = config.bottom.width.value;
let halfLineWidth = lineWidth / 2; let halfLineWidth = lineWidth / 2;
ctx.moveTo((boundingBox.x.value + config.cornerRadius.bottomLeft.value + halfLineWidth) * scale, (boundingBox.y.value + boundingBox.height.value - halfLineWidth) * scale); ctx.moveTo((boundingBox.x.value + config.cornerRadius.bottomLeft.value + halfLineWidth) * scale, (boundingBox.y.value + boundingBox.height.value - halfLineWidth) * scale);
ctx.lineWidth = lineWidth * scale; ctx.lineWidth = lineWidth * scale;
@ -704,9 +738,8 @@
ctx.stroke(); ctx.stroke();
} }
// Left Border // Left Border
if (config.left.width.value > 0) { if (config.width.left.value > 0) {
let color = config.left.color; let lineWidth = config.width.left.value;
let lineWidth = config.left.width.value;
let halfLineWidth = lineWidth / 2; let halfLineWidth = lineWidth / 2;
ctx.lineWidth = lineWidth * scale; ctx.lineWidth = lineWidth * scale;
ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`; ctx.strokeStyle = `rgba(${color.r.value}, ${color.g.value}, ${color.b.value}, ${color.a.value / 255})`;
@ -718,8 +751,8 @@
break; break;
} }
case (CLAY_RENDER_COMMAND_TYPE_TEXT): { case (CLAY_RENDER_COMMAND_TYPE_TEXT): {
let config = readStructAtAddress(renderCommand.config.value, textConfigDefinition); let config = renderCommand.renderData.text;
let textContents = renderCommand.text; let textContents = config.stringContents;
let stringContents = new Uint8Array(memoryDataView.buffer.slice(textContents.chars.value, textContents.chars.value + textContents.length.value)); 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; let fontSize = config.fontSize.value * GLOBAL_FONT_SCALING_FACTOR * scale;
ctx.font = `${fontSize}px ${fontsById[config.fontId.value]}`; ctx.font = `${fontSize}px ${fontsById[config.fontId.value]}`;
@ -742,8 +775,9 @@
break; break;
} }
case (CLAY_RENDER_COMMAND_TYPE_IMAGE): { case (CLAY_RENDER_COMMAND_TYPE_IMAGE): {
let config = readStructAtAddress(renderCommand.config.value, imageConfigDefinition); let config = renderCommand.renderData.image;
let src = textDecoder.decode(new Uint8Array(memoryDataView.buffer.slice(config.sourceURL.chars.value, config.sourceURL.chars.value + config.sourceURL.length.value))); 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]) { if (!imageCache[src]) {
imageCache[src] = { imageCache[src] = {
image: new Image(), image: new Image(),

View File

@ -3,7 +3,7 @@
double windowWidth = 1024, windowHeight = 768; double windowWidth = 1024, windowHeight = 768;
float modelPageOneZRotation = 0; 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_BODY_16 = 0;
const uint32_t FONT_ID_TITLE_56 = 1; const uint32_t FONT_ID_TITLE_56 = 1;
@ -52,13 +52,21 @@ typedef struct {
CustomHTMLData* FrameAllocateCustomData(CustomHTMLData data) { CustomHTMLData* FrameAllocateCustomData(CustomHTMLData data) {
CustomHTMLData *customData = (CustomHTMLData *)(frameArena.memory + frameArena.offset); CustomHTMLData *customData = (CustomHTMLData *)(frameArena.memory + frameArena.offset);
*customData = data;
frameArena.offset += sizeof(CustomHTMLData); frameArena.offset += sizeof(CustomHTMLData);
return customData; 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("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 })); 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_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 } }) { 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(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/images/check_4.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/images/check_3.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/images/check_2.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/images/check_1.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_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 } }) { 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(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/images/check_4.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/images/check_3.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/images/check_2.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/images/check_1.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_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("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_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("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) }, .layout = { .sizing = {CLAY_SIZING_FIXED(300) }, .padding = CLAY_PADDING_ALL(16) },
.backgroundColor = Clay_Hovered() ? COLOR_RED_HOVER : COLOR_RED, .backgroundColor = Clay_Hovered() ? COLOR_RED_HOVER : COLOR_RED,
.cornerRadius = CLAY_CORNER_RADIUS(10), .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 })); 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 }, .border = { .width = {2, 2, 2, 2}, .color = COLOR_RED },
.backgroundColor = Clay_Hovered() ? COLOR_LIGHT_HOVER : COLOR_LIGHT, .backgroundColor = Clay_Hovered() ? COLOR_LIGHT_HOVER : COLOR_LIGHT,
.cornerRadius = CLAY_CORNER_RADIUS(10), .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_OnHover(HandleRendererButtonInteraction, rendererIndex);
CLAY_TEXT(text, CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_RED })); 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_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("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_TEXT(CLAY_STRING("Clay"), &headerTextConfig);
CLAY({ .id = CLAY_ID("Spacer"), .layout = { .sizing = { .width = CLAY_SIZING_GROW(0) } } }) {} CLAY({ .id = CLAY_ID("Spacer"), .layout = { .sizing = { .width = CLAY_SIZING_GROW(0) } } }) {}
if (!mobileScreen) { 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_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} })); 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, .backgroundColor = Clay_Hovered() ? COLOR_LIGHT_HOVER : COLOR_LIGHT,
.border = { .width = {2, 2, 2, 2}, .color = COLOR_RED }, .border = { .width = {2, 2, 2, 2}, .color = COLOR_RED },
.cornerRadius = CLAY_CORNER_RADIUS(10), .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} })); 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, .backgroundColor = Clay_Hovered() ? COLOR_LIGHT_HOVER : COLOR_LIGHT,
.border = { .width = {2, 2, 2, 2}, .color = COLOR_RED }, .border = { .width = {2, 2, 2, 2}, .color = COLOR_RED },
.cornerRadius = CLAY_CORNER_RADIUS(10), .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} })); 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; 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) { 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; windowWidth = width;
windowHeight = height; windowHeight = height;
Clay_SetLayoutDimensions((Clay_Dimensions) { width, height }); Clay_SetLayoutDimensions((Clay_Dimensions) { width, height });