mirror of
https://github.com/nicbarker/clay.git
synced 2025-05-14 14:28:06 +00:00
Compare commits
39 Commits
b97bf246c8
...
ed4b43f739
Author | SHA1 | Date | |
---|---|---|---|
|
ed4b43f739 | ||
|
1ef4d59601 | ||
|
75338f7357 | ||
|
c73dffbb6f | ||
|
eb553962e8 | ||
|
d9e02ab1d3 | ||
|
bc2548e3ec | ||
|
eeb4520f48 | ||
|
6d23a35d15 | ||
|
b4933a6e4c | ||
|
9f91450431 | ||
|
e35bba079e | ||
|
d637e2a122 | ||
|
e6e0cd5a46 | ||
|
82ca328ae2 | ||
|
72af2a4020 | ||
|
3961720ef0 | ||
|
dd1f018444 | ||
|
5a328da308 | ||
|
3030390038 | ||
|
92582f66d8 | ||
|
65d2122dd6 | ||
|
fd76ce62f3 | ||
|
a5983dee96 | ||
|
76c8e1f115 | ||
|
dcd6feda86 | ||
|
b4102400ff | ||
|
5b0e5ea456 | ||
|
e7bc3869f7 | ||
|
ddc20bc8f6 | ||
|
0a9122e78d | ||
|
e97031f234 | ||
|
7a84facec9 | ||
|
bd2ce4b833 | ||
|
0468243ac7 | ||
|
b9c5f8e47f | ||
|
95fcd85a2a | ||
|
9d940c1f8e | ||
|
40ae6d8894 |
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1 @@
|
||||
github: [nicbarker]
|
1
.github/workflows/cmake-multi-platform.yml
vendored
1
.github/workflows/cmake-multi-platform.yml
vendored
@ -63,6 +63,7 @@ jobs:
|
||||
- name: Install Dependencies
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
DEBIAN_FRONTEND=noninteractive sudo apt-get update -y
|
||||
DEBIAN_FRONTEND=noninteractive sudo apt-get install -y git
|
||||
DEBIAN_FRONTEND=noninteractive sudo apt-get install -y libwayland-dev
|
||||
DEBIAN_FRONTEND=noninteractive sudo apt-get install -y pkg-config
|
||||
|
1
bindings/csharp/README
Normal file
1
bindings/csharp/README
Normal file
@ -0,0 +1 @@
|
||||
https://github.com/Orcolom/clay-cs
|
@ -1,12 +1,12 @@
|
||||
cp ../../clay.h clay.c;
|
||||
# Intel Mac
|
||||
clang -c -DCLAY_IMPLEMENTATION -o clay.o -static -target x86_64-apple-darwin clay.c -fPIC && ar r clay-odin/macos/clay.a clay.o;
|
||||
clang -c -DCLAY_IMPLEMENTATION -o clay.o -ffreestanding -static -target x86_64-apple-darwin clay.c -fPIC && ar r clay-odin/macos/clay.a clay.o;
|
||||
# ARM Mac
|
||||
clang -c -DCLAY_IMPLEMENTATION -g -o clay.o -static clay.c -fPIC && ar r clay-odin/macos-arm64/clay.a clay.o;
|
||||
# x64 Windows
|
||||
clang -c -DCLAY_IMPLEMENTATION -o clay-odin/windows/clay.lib -target x86_64-pc-windows-msvc -fuse-ld=llvm-lib -static clay.c;
|
||||
clang -c -DCLAY_IMPLEMENTATION -o clay-odin/windows/clay.lib -ffreestanding -target x86_64-pc-windows-msvc -fuse-ld=llvm-lib -static clay.c;
|
||||
# Linux
|
||||
clang -c -DCLAY_IMPLEMENTATION -o clay.o -static -target x86_64-unknown-linux-gnu clay.c -fPIC && ar r clay-odin/linux/clay.a clay.o;
|
||||
clang -c -DCLAY_IMPLEMENTATION -o clay.o -ffreestanding -static -target x86_64-unknown-linux-gnu clay.c -fPIC && ar r clay-odin/linux/clay.a clay.o;
|
||||
# WASM
|
||||
clang -c -DCLAY_IMPLEMENTATION -o clay-odin/wasm/clay.o -target wasm32 -nostdlib -static clay.c;
|
||||
rm clay.o;
|
||||
|
@ -75,19 +75,6 @@ when ODIN_OS == .Windows {
|
||||
EnumBackingType :: u8
|
||||
}
|
||||
|
||||
ElementConfigType :: enum EnumBackingType {
|
||||
Rectangle = 1,
|
||||
Border = 2,
|
||||
Floating = 4,
|
||||
Scroll = 8,
|
||||
Image = 16,
|
||||
Text = 32,
|
||||
Custom = 64,
|
||||
// Odin specific enum types
|
||||
Id = 65,
|
||||
Layout = 66,
|
||||
}
|
||||
|
||||
RenderCommandType :: enum EnumBackingType {
|
||||
None,
|
||||
Rectangle,
|
||||
@ -101,7 +88,6 @@ RenderCommandType :: enum EnumBackingType {
|
||||
|
||||
RectangleElementConfig :: struct {
|
||||
color: Color,
|
||||
cornerRadius: CornerRadius,
|
||||
}
|
||||
|
||||
TextWrapMode :: enum EnumBackingType {
|
||||
@ -110,6 +96,12 @@ TextWrapMode :: enum EnumBackingType {
|
||||
None,
|
||||
}
|
||||
|
||||
TextAlignment :: enum EnumBackingType {
|
||||
Left,
|
||||
Center,
|
||||
Right,
|
||||
}
|
||||
|
||||
TextElementConfig :: struct {
|
||||
textColor: Color,
|
||||
fontId: u16,
|
||||
@ -117,6 +109,7 @@ TextElementConfig :: struct {
|
||||
letterSpacing: u16,
|
||||
lineHeight: u16,
|
||||
wrapMode: TextWrapMode,
|
||||
textAlignment: TextAlignment,
|
||||
hashStringContents: bool,
|
||||
}
|
||||
|
||||
@ -129,13 +122,17 @@ CustomElementConfig :: struct {
|
||||
customData: rawptr,
|
||||
}
|
||||
|
||||
BorderWidth :: struct {
|
||||
left: u16,
|
||||
right: u16,
|
||||
top: u16,
|
||||
bottom: u16,
|
||||
betweenChildren: u16,
|
||||
}
|
||||
|
||||
BorderElementConfig :: struct {
|
||||
left: BorderData,
|
||||
right: BorderData,
|
||||
top: BorderData,
|
||||
bottom: BorderData,
|
||||
betweenChildren: BorderData,
|
||||
cornerRadius: CornerRadius,
|
||||
color: Color,
|
||||
width: BorderWidth,
|
||||
}
|
||||
|
||||
ScrollElementConfig :: struct {
|
||||
@ -144,15 +141,15 @@ ScrollElementConfig :: struct {
|
||||
}
|
||||
|
||||
FloatingAttachPointType :: enum EnumBackingType {
|
||||
LEFT_TOP,
|
||||
LEFT_CENTER,
|
||||
LEFT_BOTTOM,
|
||||
CENTER_TOP,
|
||||
CENTER_CENTER,
|
||||
CENTER_BOTTOM,
|
||||
RIGHT_TOP,
|
||||
RIGHT_CENTER,
|
||||
RIGHT_BOTTOM,
|
||||
LeftTop,
|
||||
LeftCenter,
|
||||
LeftBottom,
|
||||
CenterTop,
|
||||
CenterCenter,
|
||||
CenterBottom,
|
||||
RightTop,
|
||||
RightCenter,
|
||||
RightBottom,
|
||||
}
|
||||
|
||||
FloatingAttachPoints :: struct {
|
||||
@ -161,33 +158,74 @@ FloatingAttachPoints :: struct {
|
||||
}
|
||||
|
||||
PointerCaptureMode :: enum EnumBackingType {
|
||||
CAPTURE,
|
||||
PASSTHROUGH,
|
||||
Capture,
|
||||
Passthrough,
|
||||
}
|
||||
|
||||
FloatingAttachToElement :: enum EnumBackingType {
|
||||
None,
|
||||
Parent,
|
||||
ElementWithId,
|
||||
Root,
|
||||
}
|
||||
|
||||
FloatingElementConfig :: struct {
|
||||
offset: Vector2,
|
||||
expand: Dimensions,
|
||||
zIndex: u16,
|
||||
parentId: u32,
|
||||
zIndex: i32,
|
||||
attachment: FloatingAttachPoints,
|
||||
pointerCaptureMode: PointerCaptureMode,
|
||||
attachTo: FloatingAttachToElement
|
||||
}
|
||||
|
||||
ElementConfigUnion :: struct #raw_union {
|
||||
rectangleElementConfig: ^RectangleElementConfig,
|
||||
textElementConfig: ^TextElementConfig,
|
||||
imageElementConfig: ^ImageElementConfig,
|
||||
customElementConfig: ^CustomElementConfig,
|
||||
borderElementConfig: ^BorderElementConfig,
|
||||
TextRenderData :: struct {
|
||||
stringContents: StringSlice,
|
||||
textColor: Color,
|
||||
fontId: u16,
|
||||
fontSize: u16,
|
||||
letterSpacing: u16,
|
||||
lineHeight: u16,
|
||||
}
|
||||
|
||||
RectangleRenderData :: struct {
|
||||
backgroundColor: Color,
|
||||
cornerRadius: CornerRadius,
|
||||
}
|
||||
|
||||
ImageRenderData :: struct {
|
||||
backgroundColor: Color,
|
||||
cornerRadius: CornerRadius,
|
||||
sourceDimensions: Dimensions,
|
||||
imageData: rawptr,
|
||||
}
|
||||
|
||||
CustomRenderData :: struct {
|
||||
backgroundColor: Color,
|
||||
cornerRadius: CornerRadius,
|
||||
customData: rawptr,
|
||||
}
|
||||
|
||||
BorderRenderData :: struct {
|
||||
color: Color,
|
||||
cornerRadius: CornerRadius,
|
||||
width: BorderWidth,
|
||||
}
|
||||
|
||||
RenderCommandData :: struct #raw_union {
|
||||
rectangle: RectangleRenderData,
|
||||
text: TextRenderData,
|
||||
image: ImageRenderData,
|
||||
custom: CustomRenderData,
|
||||
border: BorderRenderData,
|
||||
}
|
||||
|
||||
RenderCommand :: struct {
|
||||
boundingBox: BoundingBox,
|
||||
config: ElementConfigUnion,
|
||||
text: StringSlice,
|
||||
zIndex: i32,
|
||||
renderData: RenderCommandData,
|
||||
userData: rawptr,
|
||||
id: u32,
|
||||
zIndex: i16,
|
||||
commandType: RenderCommandType,
|
||||
}
|
||||
|
||||
@ -203,10 +241,10 @@ ScrollContainerData :: struct {
|
||||
}
|
||||
|
||||
SizingType :: enum EnumBackingType {
|
||||
FIT,
|
||||
GROW,
|
||||
PERCENT,
|
||||
FIXED,
|
||||
Fit,
|
||||
Grow,
|
||||
Percent,
|
||||
Fixed,
|
||||
}
|
||||
|
||||
SizingConstraintsMinMax :: struct {
|
||||
@ -238,20 +276,20 @@ Padding :: struct {
|
||||
}
|
||||
|
||||
LayoutDirection :: enum EnumBackingType {
|
||||
LEFT_TO_RIGHT,
|
||||
TOP_TO_BOTTOM,
|
||||
LeftToRight,
|
||||
TopToBottom,
|
||||
}
|
||||
|
||||
LayoutAlignmentX :: enum EnumBackingType {
|
||||
LEFT,
|
||||
RIGHT,
|
||||
CENTER,
|
||||
Left,
|
||||
Right,
|
||||
Center,
|
||||
}
|
||||
|
||||
LayoutAlignmentY :: enum EnumBackingType {
|
||||
TOP,
|
||||
BOTTOM,
|
||||
CENTER,
|
||||
Top,
|
||||
Bottom,
|
||||
Center,
|
||||
}
|
||||
|
||||
ChildAlignment :: struct {
|
||||
@ -273,20 +311,28 @@ ClayArray :: struct($type: typeid) {
|
||||
internalArray: [^]type,
|
||||
}
|
||||
|
||||
TypedConfig :: struct {
|
||||
type: ElementConfigType,
|
||||
config: rawptr,
|
||||
ElementDeclaration :: struct {
|
||||
id: ElementId,
|
||||
layout: LayoutConfig,
|
||||
backgroundColor: Color,
|
||||
cornerRadius: CornerRadius,
|
||||
image: ImageElementConfig,
|
||||
floating: FloatingElementConfig,
|
||||
custom: CustomElementConfig,
|
||||
scroll: ScrollElementConfig,
|
||||
border: BorderElementConfig,
|
||||
userData: rawptr
|
||||
}
|
||||
|
||||
ErrorType :: enum {
|
||||
TEXT_MEASUREMENT_FUNCTION_NOT_PROVIDED,
|
||||
ARENA_CAPACITY_EXCEEDED,
|
||||
ELEMENTS_CAPACITY_EXCEEDED,
|
||||
TEXT_MEASUREMENT_CAPACITY_EXCEEDED,
|
||||
DUPLICATE_ID,
|
||||
FLOATING_CONTAINER_PARENT_NOT_FOUND,
|
||||
INTERNAL_ERROR,
|
||||
ErrorType :: enum EnumBackingType {
|
||||
TextMeasurementFunctionNotProvided,
|
||||
ArenaCapacityExceeded,
|
||||
ElementsCapacityExceeded,
|
||||
TextMeasurementCapacityExceeded,
|
||||
DuplicateId,
|
||||
FloatingContainerParentNotFound,
|
||||
PercentageOver1,
|
||||
InternalError,
|
||||
}
|
||||
|
||||
ErrorData :: struct {
|
||||
@ -300,6 +346,8 @@ ErrorHandler :: struct {
|
||||
userData: rawptr
|
||||
}
|
||||
|
||||
Context :: struct {} // opaque structure, only use as a pointer
|
||||
|
||||
@(link_prefix = "Clay_", default_calling_convention = "c")
|
||||
foreign Clay {
|
||||
MinMemorySize :: proc() -> u32 ---
|
||||
@ -310,62 +358,41 @@ foreign Clay {
|
||||
SetLayoutDimensions :: proc(dimensions: Dimensions) ---
|
||||
BeginLayout :: proc() ---
|
||||
EndLayout :: proc() -> ClayArray(RenderCommand) ---
|
||||
Hovered :: proc() -> bool ---
|
||||
PointerOver :: proc(id: ElementId) -> bool ---
|
||||
GetElementId :: proc(id: String) -> ElementId ---
|
||||
GetScrollContainerData :: proc(id: ElementId) -> ScrollContainerData ---
|
||||
SetMeasureTextFunction :: proc(measureTextFunction: proc "c" (text: StringSlice, config: ^TextElementConfig, userData: uintptr) -> Dimensions, userData: uintptr) ---
|
||||
RenderCommandArray_Get :: proc(array: ^ClayArray(RenderCommand), index: i32) -> ^RenderCommand ---
|
||||
SetDebugModeEnabled :: proc(enabled: bool) ---
|
||||
GetCurrentContext :: proc() -> ^Context ---
|
||||
SetCurrentContext :: proc(ctx: ^Context) ---
|
||||
}
|
||||
|
||||
@(link_prefix = "Clay_", default_calling_convention = "c", private)
|
||||
foreign Clay {
|
||||
_OpenElement :: proc() ---
|
||||
_ConfigureOpenElement :: proc(config: ElementDeclaration) ---
|
||||
_CloseElement :: proc() ---
|
||||
_ElementPostConfiguration :: proc() ---
|
||||
_OpenTextElement :: proc(text: String, textConfig: ^TextElementConfig) ---
|
||||
_AttachId :: proc(id: ElementId) ---
|
||||
_AttachLayoutConfig :: proc(layoutConfig: ^LayoutConfig) ---
|
||||
_AttachElementConfig :: proc(config: rawptr, type: ElementConfigType) ---
|
||||
_StoreLayoutConfig :: proc(config: LayoutConfig) -> ^LayoutConfig ---
|
||||
_StoreRectangleElementConfig :: proc(config: RectangleElementConfig) -> ^RectangleElementConfig ---
|
||||
_StoreTextElementConfig :: proc(config: TextElementConfig) -> ^TextElementConfig ---
|
||||
_StoreImageElementConfig :: proc(config: ImageElementConfig) -> ^ImageElementConfig ---
|
||||
_StoreFloatingElementConfig :: proc(config: FloatingElementConfig) -> ^FloatingElementConfig ---
|
||||
_StoreCustomElementConfig :: proc(config: CustomElementConfig) -> ^CustomElementConfig ---
|
||||
_StoreScrollElementConfig :: proc(config: ScrollElementConfig) -> ^ScrollElementConfig ---
|
||||
_StoreBorderElementConfig :: proc(config: BorderElementConfig) -> ^BorderElementConfig ---
|
||||
_HashString :: proc(toHash: String, index: u32, seed: u32) -> ElementId ---
|
||||
_GetOpenLayoutElementId :: proc() -> u32 ---
|
||||
}
|
||||
|
||||
@(require_results, deferred_none = _CloseElement)
|
||||
UI :: proc(configs: ..TypedConfig) -> bool {
|
||||
ClayOpenElement :: struct {
|
||||
configure: proc (config: ElementDeclaration) -> bool
|
||||
}
|
||||
|
||||
ConfigureOpenElement :: proc(config: ElementDeclaration) -> bool {
|
||||
_ConfigureOpenElement(config)
|
||||
return true;
|
||||
}
|
||||
|
||||
@(deferred_none = _CloseElement)
|
||||
UI :: proc() -> ClayOpenElement {
|
||||
_OpenElement()
|
||||
for config in configs {
|
||||
#partial switch (config.type) {
|
||||
case ElementConfigType.Id:
|
||||
_AttachId(config.id)
|
||||
case ElementConfigType.Layout:
|
||||
_AttachLayoutConfig(cast(^LayoutConfig)config.config)
|
||||
case:
|
||||
_AttachElementConfig(config.config, config.type)
|
||||
}
|
||||
}
|
||||
_ElementPostConfiguration()
|
||||
return true
|
||||
}
|
||||
|
||||
Layout :: proc(config: LayoutConfig) -> TypedConfig {
|
||||
return {type = ElementConfigType.Layout, config = _StoreLayoutConfig(config) }
|
||||
}
|
||||
|
||||
PaddingAll :: proc (padding: u16) -> Padding {
|
||||
return { padding, padding, padding, padding }
|
||||
}
|
||||
|
||||
Rectangle :: proc(config: RectangleElementConfig) -> TypedConfig {
|
||||
return {type = ElementConfigType.Rectangle, config = _StoreRectangleElementConfig(config)}
|
||||
return { configure = ConfigureOpenElement }
|
||||
}
|
||||
|
||||
Text :: proc(text: string, config: ^TextElementConfig) {
|
||||
@ -376,44 +403,8 @@ TextConfig :: proc(config: TextElementConfig) -> ^TextElementConfig {
|
||||
return _StoreTextElementConfig(config)
|
||||
}
|
||||
|
||||
Image :: proc(config: ImageElementConfig) -> TypedConfig {
|
||||
return {type = ElementConfigType.Image, config = _StoreImageElementConfig(config)}
|
||||
}
|
||||
|
||||
Floating :: proc(config: FloatingElementConfig) -> TypedConfig {
|
||||
return {type = ElementConfigType.Floating, config = _StoreFloatingElementConfig(config)}
|
||||
}
|
||||
|
||||
Custom :: proc(config: CustomElementConfig) -> TypedConfig {
|
||||
return {type = ElementConfigType.Custom, config = _StoreCustomElementConfig(config)}
|
||||
}
|
||||
|
||||
Scroll :: proc(config: ScrollElementConfig) -> TypedConfig {
|
||||
return {type = ElementConfigType.Scroll, config = _StoreScrollElementConfig(config)}
|
||||
}
|
||||
|
||||
Border :: proc(config: BorderElementConfig) -> TypedConfig {
|
||||
return {type = ElementConfigType.Border, config = _StoreBorderElementConfig(config)}
|
||||
}
|
||||
|
||||
BorderOutside :: proc(outsideBorders: BorderData) -> TypedConfig {
|
||||
return { type = ElementConfigType.Border, config = _StoreBorderElementConfig((BorderElementConfig){left = outsideBorders, right = outsideBorders, top = outsideBorders, bottom = outsideBorders}) }
|
||||
}
|
||||
|
||||
BorderOutsideRadius :: proc(outsideBorders: BorderData, radius: f32) -> TypedConfig {
|
||||
return { type = ElementConfigType.Border, config = _StoreBorderElementConfig(
|
||||
(BorderElementConfig){left = outsideBorders, right = outsideBorders, top = outsideBorders, bottom = outsideBorders, cornerRadius = {radius, radius, radius, radius}},
|
||||
) }
|
||||
}
|
||||
|
||||
BorderAll :: proc(allBorders: BorderData) -> TypedConfig {
|
||||
return { type = ElementConfigType.Border, config = _StoreBorderElementConfig((BorderElementConfig){left = allBorders, right = allBorders, top = allBorders, bottom = allBorders, betweenChildren = allBorders}) }
|
||||
}
|
||||
|
||||
BorderAllRadius :: proc(allBorders: BorderData, radius: f32) -> TypedConfig {
|
||||
return { type = ElementConfigType.Border, config = _StoreBorderElementConfig(
|
||||
(BorderElementConfig){left = allBorders, right = allBorders, top = allBorders, bottom = allBorders, cornerRadius = {radius, radius, radius, radius}},
|
||||
) }
|
||||
PaddingAll :: proc(allPadding: u16) -> Padding {
|
||||
return { left = allPadding, right = allPadding, top = allPadding, bottom = allPadding }
|
||||
}
|
||||
|
||||
CornerRadiusAll :: proc(radius: f32) -> CornerRadius {
|
||||
@ -421,25 +412,25 @@ CornerRadiusAll :: proc(radius: f32) -> CornerRadius {
|
||||
}
|
||||
|
||||
SizingFit :: proc(sizeMinMax: SizingConstraintsMinMax) -> SizingAxis {
|
||||
return SizingAxis{type = SizingType.FIT, constraints = {sizeMinMax = sizeMinMax}}
|
||||
return SizingAxis{type = SizingType.Fit, constraints = {sizeMinMax = sizeMinMax}}
|
||||
}
|
||||
|
||||
SizingGrow :: proc(sizeMinMax: SizingConstraintsMinMax) -> SizingAxis {
|
||||
return SizingAxis{type = SizingType.GROW, constraints = {sizeMinMax = sizeMinMax}}
|
||||
return SizingAxis{type = SizingType.Grow, constraints = {sizeMinMax = sizeMinMax}}
|
||||
}
|
||||
|
||||
SizingFixed :: proc(size: c.float) -> SizingAxis {
|
||||
return SizingAxis{type = SizingType.FIXED, constraints = {sizeMinMax = {size, size}}}
|
||||
return SizingAxis{type = SizingType.Fixed, constraints = {sizeMinMax = {size, size}}}
|
||||
}
|
||||
|
||||
SizingPercent :: proc(sizePercent: c.float) -> SizingAxis {
|
||||
return SizingAxis{type = SizingType.PERCENT, constraints = {sizePercent = sizePercent}}
|
||||
return SizingAxis{type = SizingType.Percent, constraints = {sizePercent = sizePercent}}
|
||||
}
|
||||
|
||||
MakeString :: proc(label: string) -> String {
|
||||
return String{chars = raw_data(label), length = cast(c.int)len(label)}
|
||||
}
|
||||
|
||||
ID :: proc(label: string, index: u32 = 0) -> TypedConfig {
|
||||
return { type = ElementConfigType.Id, id = _HashString(MakeString(label), index, 0) }
|
||||
ID :: proc(label: string, index: u32 = 0) -> ElementId {
|
||||
return _HashString(MakeString(label), index, 0)
|
||||
}
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -57,46 +57,52 @@ headerTextConfig := clay.TextElementConfig {
|
||||
textColor = {61, 26, 5, 255},
|
||||
}
|
||||
|
||||
border2pxRed := clay.BorderElementConfig {
|
||||
width = { 2, 2, 2, 2, 0 },
|
||||
color = COLOR_RED
|
||||
}
|
||||
|
||||
LandingPageBlob :: proc(index: u32, fontSize: u16, fontId: u16, color: clay.Color, text: string, image: ^raylib.Texture2D) {
|
||||
if clay.UI(
|
||||
clay.ID("HeroBlob", index),
|
||||
clay.Layout({sizing = {width = clay.SizingGrow({max = 480})}, padding = clay.PaddingAll(16), childGap = 16, childAlignment = clay.ChildAlignment{y = .CENTER}}),
|
||||
clay.BorderOutsideRadius({2, color}, 10),
|
||||
) {
|
||||
if clay.UI(
|
||||
clay.ID("CheckImage", index),
|
||||
clay.Layout({sizing = {width = clay.SizingFixed(32)}}),
|
||||
clay.Image({imageData = image, sourceDimensions = {128, 128}}),
|
||||
) {}
|
||||
if clay.UI().configure({
|
||||
id = clay.ID("HeroBlob", index),
|
||||
layout = { sizing = { width = clay.SizingGrow({ max = 480 }) }, padding = clay.PaddingAll(16), childGap = 16, childAlignment = clay.ChildAlignment{ y = .Center } },
|
||||
border = border2pxRed,
|
||||
cornerRadius = clay.CornerRadiusAll(10)
|
||||
}) {
|
||||
if clay.UI().configure({
|
||||
id = clay.ID("CheckImage", index),
|
||||
layout = { sizing = { width = clay.SizingFixed(32) } },
|
||||
image = { imageData = image, sourceDimensions = { 128, 128 } },
|
||||
}) {}
|
||||
clay.Text(text, clay.TextConfig({fontSize = fontSize, fontId = fontId, textColor = color}))
|
||||
}
|
||||
}
|
||||
|
||||
LandingPageDesktop :: proc() {
|
||||
if clay.UI(
|
||||
clay.ID("LandingPage1Desktop"),
|
||||
clay.Layout({sizing = {width = clay.SizingGrow({}), height = clay.SizingFit({min = cast(f32)windowHeight - 70})}, childAlignment = {y = .CENTER}, padding = {left = 50, right = 50}}),
|
||||
) {
|
||||
if clay.UI(
|
||||
clay.ID("LandingPage1"),
|
||||
clay.Layout({sizing = {clay.SizingGrow({}), clay.SizingGrow({})}, childAlignment = {y = .CENTER}, padding = clay.PaddingAll(32), childGap = 32}),
|
||||
clay.Border({left = {2, COLOR_RED}, right = {2, COLOR_RED}}),
|
||||
) {
|
||||
if clay.UI(clay.ID("LeftText"), clay.Layout({sizing = {width = clay.SizingPercent(0.55)}, layoutDirection = .TOP_TO_BOTTOM, childGap = 8})) {
|
||||
if clay.UI().configure({
|
||||
id = clay.ID("LandingPage1Desktop"),
|
||||
layout = { sizing = { width = clay.SizingGrow({ }), height = clay.SizingFit({ min = cast(f32)windowHeight - 70 }) }, childAlignment = { y = .Center }, padding = { left = 50, right = 50 } },
|
||||
}) {
|
||||
if clay.UI().configure({
|
||||
id = clay.ID("LandingPage1"),
|
||||
layout = { sizing = { clay.SizingGrow({ }), clay.SizingGrow({ }) }, childAlignment = { y = .Center }, padding = clay.PaddingAll(32), childGap = 32 },
|
||||
border = { COLOR_RED, { left = 2, right = 2 } },
|
||||
}) {
|
||||
if clay.UI().configure({ id = clay.ID("LeftText"), layout = { sizing = { width = clay.SizingPercent(0.55) }, layoutDirection = .TopToBottom, childGap = 8 } }) {
|
||||
clay.Text(
|
||||
"Clay is a flex-box style UI auto layout library in C, with declarative syntax and microsecond performance.",
|
||||
clay.TextConfig({fontSize = 56, fontId = FONT_ID_TITLE_56, textColor = COLOR_RED}),
|
||||
)
|
||||
// if clay.UI(clay.Layout({sizing = {width = clay.SizingGrow({}), height = clay.SizingFixed(32)}})) {}
|
||||
if clay.UI().configure({ layout = { sizing = { width = clay.SizingGrow({}), height = clay.SizingFixed(32) } } }) {}
|
||||
clay.Text(
|
||||
"Clay is laying out this webpage right now!",
|
||||
clay.TextConfig({fontSize = 36, fontId = FONT_ID_TITLE_36, textColor = COLOR_ORANGE}),
|
||||
)
|
||||
}
|
||||
if clay.UI(
|
||||
clay.ID("HeroImageOuter"),
|
||||
clay.Layout({layoutDirection = .TOP_TO_BOTTOM, sizing = {width = clay.SizingPercent(0.45)}, childAlignment = {x = .CENTER}, childGap = 16}),
|
||||
) {
|
||||
if clay.UI().configure({
|
||||
id = clay.ID("HeroImageOuter"),
|
||||
layout = { layoutDirection = .TopToBottom, sizing = { width = clay.SizingPercent(0.45) }, childAlignment = { x = .Center }, childGap = 16 },
|
||||
}) {
|
||||
LandingPageBlob(1, 30, FONT_ID_BODY_30, COLOR_BLOB_BORDER_5, "High performance", &checkImage5)
|
||||
LandingPageBlob(2, 30, FONT_ID_BODY_30, COLOR_BLOB_BORDER_4, "Flexbox-style responsive layout", &checkImage4)
|
||||
LandingPageBlob(3, 30, FONT_ID_BODY_30, COLOR_BLOB_BORDER_3, "Declarative syntax", &checkImage3)
|
||||
@ -108,33 +114,31 @@ LandingPageDesktop :: proc() {
|
||||
}
|
||||
|
||||
LandingPageMobile :: proc() {
|
||||
if clay.UI(
|
||||
clay.ID("LandingPage1Mobile"),
|
||||
clay.Layout(
|
||||
{
|
||||
layoutDirection = .TOP_TO_BOTTOM,
|
||||
sizing = {width = clay.SizingGrow({}), height = clay.SizingFit({min = cast(f32)windowHeight - 70})},
|
||||
childAlignment = {x = .CENTER, y = .CENTER},
|
||||
padding = {16, 16, 32, 32},
|
||||
if clay.UI().configure({
|
||||
id = clay.ID("LandingPage1Mobile"),
|
||||
layout = {
|
||||
layoutDirection = .TopToBottom,
|
||||
sizing = { width = clay.SizingGrow({ }), height = clay.SizingFit({ min = cast(f32)windowHeight - 70 }) },
|
||||
childAlignment = { x = .Center, y = .Center },
|
||||
padding = { 16, 16, 32, 32 },
|
||||
childGap = 32,
|
||||
},
|
||||
),
|
||||
) {
|
||||
if clay.UI(clay.ID("LeftText"), clay.Layout({sizing = {width = clay.SizingGrow({})}, layoutDirection = .TOP_TO_BOTTOM, childGap = 8})) {
|
||||
}) {
|
||||
if clay.UI().configure({ id = clay.ID("LeftText"), layout = { sizing = { width = clay.SizingGrow({ }) }, layoutDirection = .TopToBottom, childGap = 8 } }) {
|
||||
clay.Text(
|
||||
"Clay is a flex-box style UI auto layout library in C, with declarative syntax and microsecond performance.",
|
||||
clay.TextConfig({fontSize = 48, fontId = FONT_ID_TITLE_48, textColor = COLOR_RED}),
|
||||
)
|
||||
if clay.UI(clay.Layout({sizing = {width = clay.SizingGrow({}), height = clay.SizingFixed(32)}})) {}
|
||||
if clay.UI().configure({ layout = { sizing = { width = clay.SizingGrow({}), height = clay.SizingFixed(32) } } }) {}
|
||||
clay.Text(
|
||||
"Clay is laying out this webpage right now!",
|
||||
clay.TextConfig({fontSize = 32, fontId = FONT_ID_TITLE_32, textColor = COLOR_ORANGE}),
|
||||
)
|
||||
}
|
||||
if clay.UI(
|
||||
clay.ID("HeroImageOuter"),
|
||||
clay.Layout({layoutDirection = .TOP_TO_BOTTOM, sizing = {width = clay.SizingGrow({})}, childAlignment = {x = .CENTER}, childGap = 16}),
|
||||
) {
|
||||
if clay.UI().configure({
|
||||
id = clay.ID("HeroImageOuter"),
|
||||
layout = { layoutDirection = .TopToBottom, sizing = { width = clay.SizingGrow({ }) }, childAlignment = { x = .Center }, childGap = 16 },
|
||||
}) {
|
||||
LandingPageBlob(1, 24, FONT_ID_BODY_24, COLOR_BLOB_BORDER_5, "High performance", &checkImage5)
|
||||
LandingPageBlob(2, 24, FONT_ID_BODY_24, COLOR_BLOB_BORDER_4, "Flexbox-style responsive layout", &checkImage4)
|
||||
LandingPageBlob(3, 24, FONT_ID_BODY_24, COLOR_BLOB_BORDER_3, "Declarative syntax", &checkImage3)
|
||||
@ -146,20 +150,20 @@ LandingPageMobile :: proc() {
|
||||
|
||||
FeatureBlocks :: proc(widthSizing: clay.SizingAxis, outerPadding: u16) {
|
||||
textConfig := clay.TextConfig({fontSize = 24, fontId = FONT_ID_BODY_24, textColor = COLOR_RED})
|
||||
if clay.UI(
|
||||
clay.ID("HFileBoxOuter"),
|
||||
clay.Layout({layoutDirection = .TOP_TO_BOTTOM, sizing = {width = widthSizing}, childAlignment = {y = .CENTER}, padding = {outerPadding, outerPadding, 32, 32}, childGap = 8}),
|
||||
) {
|
||||
if clay.UI(clay.ID("HFileIncludeOuter"), clay.Layout({padding = {8, 8, 4, 4}}), clay.Rectangle({color = COLOR_RED, cornerRadius = clay.CornerRadiusAll(8)})) {
|
||||
if clay.UI().configure({
|
||||
id = clay.ID("HFileBoxOuter"),
|
||||
layout = { layoutDirection = .TopToBottom, sizing = { width = widthSizing }, childAlignment = { y = .Center }, padding = { outerPadding, outerPadding, 32, 32 }, childGap = 8 },
|
||||
}) {
|
||||
if clay.UI().configure({ id = clay.ID("HFileIncludeOuter"), layout = { padding = { 8, 8, 4, 4 } }, backgroundColor = COLOR_RED, cornerRadius = clay.CornerRadiusAll(8) }) {
|
||||
clay.Text("#include clay.h", clay.TextConfig({fontSize = 24, fontId = FONT_ID_BODY_24, textColor = COLOR_LIGHT}))
|
||||
}
|
||||
clay.Text("~2000 lines of C99.", textConfig)
|
||||
clay.Text("Zero dependencies, including no C standard library.", textConfig)
|
||||
}
|
||||
if clay.UI(
|
||||
clay.ID("BringYourOwnRendererOuter"),
|
||||
clay.Layout({layoutDirection = .TOP_TO_BOTTOM, sizing = {width = widthSizing}, childAlignment = {y = .CENTER}, padding = {outerPadding, outerPadding, 32, 32}, childGap = 8}),
|
||||
) {
|
||||
if clay.UI().configure({
|
||||
id = clay.ID("BringYourOwnRendererOuter"),
|
||||
layout = { layoutDirection = .TopToBottom, sizing = { width = widthSizing }, childAlignment = { y = .Center }, padding = { outerPadding, outerPadding, 32, 32 }, childGap = 8 },
|
||||
}) {
|
||||
clay.Text("Renderer agnostic.", clay.TextConfig({fontId = FONT_ID_BODY_24, fontSize = 24, textColor = COLOR_ORANGE}))
|
||||
clay.Text("Layout with clay, then render with Raylib, WebGL Canvas or even as HTML.", textConfig)
|
||||
clay.Text("Flexible output for easy compositing in your custom engine or environment.", textConfig)
|
||||
@ -167,31 +171,31 @@ FeatureBlocks :: proc(widthSizing: clay.SizingAxis, outerPadding: u16) {
|
||||
}
|
||||
|
||||
FeatureBlocksDesktop :: proc() {
|
||||
if clay.UI(clay.ID("FeatureBlocksOuter"), clay.Layout({sizing = {width = clay.SizingGrow({})}})) {
|
||||
if clay.UI(
|
||||
clay.ID("FeatureBlocksInner"),
|
||||
clay.Layout({sizing = {width = clay.SizingGrow({})}, childAlignment = {y = .CENTER}}),
|
||||
clay.Border({betweenChildren = {width = 2, color = COLOR_RED}}),
|
||||
) {
|
||||
if clay.UI().configure({ id = clay.ID("FeatureBlocksOuter"), layout = { sizing = { width = clay.SizingGrow({}) } } }) {
|
||||
if clay.UI().configure({
|
||||
id = clay.ID("FeatureBlocksInner"),
|
||||
layout = { sizing = { width = clay.SizingGrow({ }) }, childAlignment = { y = .Center } },
|
||||
border = { width = { betweenChildren = 2}, color = COLOR_RED },
|
||||
}) {
|
||||
FeatureBlocks(clay.SizingPercent(0.5), 50)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FeatureBlocksMobile :: proc() {
|
||||
if clay.UI(
|
||||
clay.ID("FeatureBlocksInner"),
|
||||
clay.Layout({layoutDirection = .TOP_TO_BOTTOM, sizing = {width = clay.SizingGrow({})}}),
|
||||
clay.Border({betweenChildren = {width = 2, color = COLOR_RED}}),
|
||||
) {
|
||||
if clay.UI().configure({
|
||||
id = clay.ID("FeatureBlocksInner"),
|
||||
layout = { layoutDirection = .TopToBottom, sizing = { width = clay.SizingGrow({ }) } },
|
||||
border = { width = { betweenChildren = 2}, color = COLOR_RED },
|
||||
}) {
|
||||
FeatureBlocks(clay.SizingGrow({}), 16)
|
||||
}
|
||||
}
|
||||
|
||||
DeclarativeSyntaxPage :: proc(titleTextConfig: clay.TextElementConfig, widthSizing: clay.SizingAxis) {
|
||||
if clay.UI(clay.ID("SyntaxPageLeftText"), clay.Layout({sizing = {width = widthSizing}, layoutDirection = .TOP_TO_BOTTOM, childGap = 8})) {
|
||||
if clay.UI().configure({ id = clay.ID("SyntaxPageLeftText"), layout = { sizing = { width = widthSizing }, layoutDirection = .TopToBottom, childGap = 8 } }) {
|
||||
clay.Text("Declarative Syntax", clay.TextConfig(titleTextConfig))
|
||||
if clay.UI(clay.ID("SyntaxSpacer"), clay.Layout({sizing = {width = clay.SizingGrow({max = 16})}})) {}
|
||||
if clay.UI().configure({ id = clay.ID("SyntaxSpacer"), layout = { sizing = { width = clay.SizingGrow({ max = 16 }) } } }) {}
|
||||
clay.Text(
|
||||
"Flexible and readable declarative syntax with nested UI element hierarchies.",
|
||||
clay.TextConfig({fontSize = 28, fontId = FONT_ID_BODY_28, textColor = COLOR_RED}),
|
||||
@ -205,43 +209,41 @@ DeclarativeSyntaxPage :: proc(titleTextConfig: clay.TextElementConfig, widthSizi
|
||||
clay.TextConfig({fontSize = 28, fontId = FONT_ID_BODY_28, textColor = COLOR_RED}),
|
||||
)
|
||||
}
|
||||
if clay.UI(clay.ID("SyntaxPageRightImage"), clay.Layout({sizing = {width = widthSizing}, childAlignment = {x = .CENTER}})) {
|
||||
if clay.UI(
|
||||
clay.ID("SyntaxPageRightImageInner"),
|
||||
clay.Layout({sizing = {width = clay.SizingGrow({max = 568})}}),
|
||||
clay.Image({imageData = &syntaxImage, sourceDimensions = {1136, 1194}}),
|
||||
) {}
|
||||
if clay.UI().configure({ id = clay.ID("SyntaxPageRightImage"), layout = { sizing = { width = widthSizing }, childAlignment = { x = .Center } } }) {
|
||||
if clay.UI().configure({
|
||||
id = clay.ID("SyntaxPageRightImageInner"),
|
||||
layout = { sizing = { width = clay.SizingGrow({ max = 568 }) } },
|
||||
image = { imageData = &syntaxImage, sourceDimensions = { 1136, 1194 } },
|
||||
}) {}
|
||||
}
|
||||
}
|
||||
|
||||
DeclarativeSyntaxPageDesktop :: proc() {
|
||||
if clay.UI(
|
||||
clay.ID("SyntaxPageDesktop"),
|
||||
clay.Layout({sizing = {clay.SizingGrow({}), clay.SizingFit({min = cast(f32)windowHeight - 50})}, childAlignment = {y = .CENTER}, padding = {left = 50, right = 50}}),
|
||||
) {
|
||||
if clay.UI(
|
||||
clay.ID("SyntaxPage"),
|
||||
clay.Layout({sizing = {clay.SizingGrow({}), clay.SizingGrow({})}, childAlignment = {y = .CENTER}, padding = clay.PaddingAll(32), childGap = 32}),
|
||||
clay.Border({left = {2, COLOR_RED}, right = {2, COLOR_RED}}),
|
||||
) {
|
||||
if clay.UI().configure({
|
||||
id = clay.ID("SyntaxPageDesktop"),
|
||||
layout = { sizing = { clay.SizingGrow({ }), clay.SizingFit({ min = cast(f32)windowHeight - 50 }) }, childAlignment = { y = .Center }, padding = { left = 50, right = 50 } },
|
||||
}) {
|
||||
if clay.UI().configure({
|
||||
id = clay.ID("SyntaxPage"),
|
||||
layout = { sizing = { clay.SizingGrow({ }), clay.SizingGrow({ }) }, childAlignment = { y = .Center }, padding = clay.PaddingAll(32), childGap = 32 },
|
||||
border = border2pxRed,
|
||||
}) {
|
||||
DeclarativeSyntaxPage({fontSize = 52, fontId = FONT_ID_TITLE_52, textColor = COLOR_RED}, clay.SizingPercent(0.5))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DeclarativeSyntaxPageMobile :: proc() {
|
||||
if clay.UI(
|
||||
clay.ID("SyntaxPageMobile"),
|
||||
clay.Layout(
|
||||
{
|
||||
layoutDirection = .TOP_TO_BOTTOM,
|
||||
sizing = {clay.SizingGrow({}), clay.SizingFit({min = cast(f32)windowHeight - 50})},
|
||||
childAlignment = {x = .CENTER, y = .CENTER},
|
||||
padding = {16, 16, 32, 32},
|
||||
if clay.UI().configure({
|
||||
id = clay.ID("SyntaxPageMobile"),
|
||||
layout = {
|
||||
layoutDirection = .TopToBottom,
|
||||
sizing = { clay.SizingGrow({ }), clay.SizingFit({ min = cast(f32)windowHeight - 50 }) },
|
||||
childAlignment = { x = .Center, y = .Center },
|
||||
padding = { 16, 16, 32, 32 },
|
||||
childGap = 16,
|
||||
},
|
||||
),
|
||||
) {
|
||||
}) {
|
||||
DeclarativeSyntaxPage({fontSize = 48, fontId = FONT_ID_TITLE_48, textColor = COLOR_RED}, clay.SizingGrow({}))
|
||||
}
|
||||
}
|
||||
@ -253,9 +255,9 @@ ColorLerp :: proc(a: clay.Color, b: clay.Color, amount: f32) -> clay.Color {
|
||||
LOREM_IPSUM_TEXT := "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
|
||||
|
||||
HighPerformancePage :: proc(lerpValue: f32, titleTextConfig: clay.TextElementConfig, widthSizing: clay.SizingAxis) {
|
||||
if clay.UI(clay.ID("PerformanceLeftText"), clay.Layout({sizing = {width = widthSizing}, layoutDirection = .TOP_TO_BOTTOM, childGap = 8})) {
|
||||
if clay.UI().configure({ id = clay.ID("PerformanceLeftText"), layout = { sizing = { width = widthSizing }, layoutDirection = .TopToBottom, childGap = 8 } }) {
|
||||
clay.Text("High Performance", clay.TextConfig(titleTextConfig))
|
||||
if clay.UI(clay.Layout({sizing = {width = clay.SizingGrow({max = 16})}})) {}
|
||||
if clay.UI().configure({ layout = { sizing = { width = clay.SizingGrow({ max = 16 }) } }}) {}
|
||||
clay.Text(
|
||||
"Fast enough to recompute your entire UI every frame.",
|
||||
clay.TextConfig({fontSize = 28, fontId = FONT_ID_BODY_36, textColor = COLOR_LIGHT}),
|
||||
@ -269,24 +271,24 @@ HighPerformancePage :: proc(lerpValue: f32, titleTextConfig: clay.TextElementCon
|
||||
clay.TextConfig({fontSize = 28, fontId = FONT_ID_BODY_36, textColor = COLOR_LIGHT}),
|
||||
)
|
||||
}
|
||||
if clay.UI(clay.ID("PerformanceRightImageOuter"), clay.Layout({sizing = {width = widthSizing}, childAlignment = {x = .CENTER}})) {
|
||||
if clay.UI(
|
||||
clay.ID("PerformanceRightBorder"),
|
||||
clay.Layout({sizing = {clay.SizingGrow({}), clay.SizingFixed(400)}}),
|
||||
clay.BorderAll({width = 2, color = COLOR_LIGHT}),
|
||||
) {
|
||||
if clay.UI(
|
||||
clay.ID("AnimationDemoContainerLeft"),
|
||||
clay.Layout({sizing = {clay.SizingPercent(0.35 + 0.3 * lerpValue), clay.SizingGrow({})}, childAlignment = {y = .CENTER}, padding = clay.PaddingAll(16)}),
|
||||
clay.Rectangle({color = ColorLerp(COLOR_RED, COLOR_ORANGE, lerpValue)}),
|
||||
) {
|
||||
if clay.UI().configure({ id = clay.ID("PerformanceRightImageOuter"), layout = { sizing = { width = widthSizing }, childAlignment = { x = .Center } } }) {
|
||||
if clay.UI().configure({
|
||||
id = clay.ID("PerformanceRightBorder"),
|
||||
layout = { sizing = { clay.SizingGrow({ }), clay.SizingFixed(400) } },
|
||||
border = { COLOR_LIGHT, {2, 2, 2, 2, 2} },
|
||||
}) {
|
||||
if clay.UI().configure({
|
||||
id = clay.ID("AnimationDemoContainerLeft"),
|
||||
layout = { sizing = { clay.SizingPercent(0.35 + 0.3 * lerpValue), clay.SizingGrow({ }) }, childAlignment = { y = .Center }, padding = clay.PaddingAll(16) },
|
||||
backgroundColor = ColorLerp(COLOR_RED, COLOR_ORANGE, lerpValue),
|
||||
}) {
|
||||
clay.Text(LOREM_IPSUM_TEXT, clay.TextConfig({fontSize = 16, fontId = FONT_ID_BODY_16, textColor = COLOR_LIGHT}))
|
||||
}
|
||||
if clay.UI(
|
||||
clay.ID("AnimationDemoContainerRight"),
|
||||
clay.Layout({sizing = {clay.SizingGrow({}), clay.SizingGrow({})}, childAlignment = {y = .CENTER}, padding = clay.PaddingAll(16)}),
|
||||
clay.Rectangle({color = ColorLerp(COLOR_ORANGE, COLOR_RED, lerpValue)}),
|
||||
) {
|
||||
if clay.UI().configure({
|
||||
id = clay.ID("AnimationDemoContainerRight"),
|
||||
layout = { sizing = { clay.SizingGrow({ }), clay.SizingGrow({ }) }, childAlignment = { y = .Center }, padding = clay.PaddingAll(16) },
|
||||
backgroundColor = ColorLerp(COLOR_ORANGE, COLOR_RED, lerpValue),
|
||||
}) {
|
||||
clay.Text(LOREM_IPSUM_TEXT, clay.TextConfig({fontSize = 16, fontId = FONT_ID_BODY_16, textColor = COLOR_LIGHT}))
|
||||
}
|
||||
}
|
||||
@ -294,60 +296,58 @@ HighPerformancePage :: proc(lerpValue: f32, titleTextConfig: clay.TextElementCon
|
||||
}
|
||||
|
||||
HighPerformancePageDesktop :: proc(lerpValue: f32) {
|
||||
if clay.UI(
|
||||
clay.ID("PerformanceDesktop"),
|
||||
clay.Layout(
|
||||
{sizing = {clay.SizingGrow({}), clay.SizingFit({min = cast(f32)windowHeight - 50})}, childAlignment = {y = .CENTER}, padding = {82, 82, 32, 32}, childGap = 64},
|
||||
),
|
||||
clay.Rectangle({color = COLOR_RED}),
|
||||
) {
|
||||
if clay.UI().configure({
|
||||
id = clay.ID("PerformanceDesktop"),
|
||||
layout = { sizing = { clay.SizingGrow({ }), clay.SizingFit({ min = cast(f32)windowHeight - 50 }) }, childAlignment = { y = .Center }, padding = { 82, 82, 32, 32 }, childGap = 64 },
|
||||
backgroundColor = COLOR_RED,
|
||||
}) {
|
||||
HighPerformancePage(lerpValue, {fontSize = 52, fontId = FONT_ID_TITLE_52, textColor = COLOR_LIGHT}, clay.SizingPercent(0.5))
|
||||
}
|
||||
}
|
||||
|
||||
HighPerformancePageMobile :: proc(lerpValue: f32) {
|
||||
if clay.UI(
|
||||
clay.ID("PerformanceMobile"),
|
||||
clay.Layout(
|
||||
{
|
||||
layoutDirection = .TOP_TO_BOTTOM,
|
||||
sizing = {clay.SizingGrow({}), clay.SizingFit({min = cast(f32)windowHeight - 50})},
|
||||
childAlignment = {x = .CENTER, y = .CENTER},
|
||||
padding = { 16, 16, 32, 32},
|
||||
if clay.UI().configure({
|
||||
id = clay.ID("PerformanceMobile"),
|
||||
layout = {
|
||||
layoutDirection = .TopToBottom,
|
||||
sizing = { clay.SizingGrow({ }), clay.SizingFit({ min = cast(f32)windowHeight - 50 }) },
|
||||
childAlignment = { x = .Center, y = .Center },
|
||||
padding = { 16, 16, 32, 32 },
|
||||
childGap = 32,
|
||||
},
|
||||
),
|
||||
clay.Rectangle({color = COLOR_RED}),
|
||||
) {
|
||||
backgroundColor = COLOR_RED,
|
||||
}) {
|
||||
HighPerformancePage(lerpValue, {fontSize = 48, fontId = FONT_ID_TITLE_48, textColor = COLOR_LIGHT}, clay.SizingGrow({}))
|
||||
}
|
||||
}
|
||||
|
||||
RendererButtonActive :: proc(index: i32, text: string) {
|
||||
if clay.UI(
|
||||
clay.Layout({sizing = {width = clay.SizingFixed(300)}, padding = clay.PaddingAll(16)}),
|
||||
clay.Rectangle({color = COLOR_RED, cornerRadius = clay.CornerRadiusAll(10)}),
|
||||
) {
|
||||
if clay.UI().configure({
|
||||
layout = { sizing = { width = clay.SizingFixed(300) }, padding = clay.PaddingAll(16) },
|
||||
backgroundColor = COLOR_RED,
|
||||
cornerRadius = clay.CornerRadiusAll(10)
|
||||
}) {
|
||||
clay.Text(text, clay.TextConfig({fontSize = 28, fontId = FONT_ID_BODY_28, textColor = COLOR_LIGHT}))
|
||||
}
|
||||
}
|
||||
|
||||
RendererButtonInactive :: proc(index: u32, text: string) {
|
||||
if clay.UI(clay.Layout({}), clay.BorderOutsideRadius({2, COLOR_RED}, 10)) {
|
||||
if clay.UI(
|
||||
clay.ID("RendererButtonInactiveInner", index),
|
||||
clay.Layout({sizing = {width = clay.SizingFixed(300)}, padding = clay.PaddingAll(16)}),
|
||||
clay.Rectangle({color = COLOR_LIGHT, cornerRadius = clay.CornerRadiusAll(10)}),
|
||||
) {
|
||||
if clay.UI().configure({ border = border2pxRed }) {
|
||||
if clay.UI().configure({
|
||||
id = clay.ID("RendererButtonInactiveInner", index),
|
||||
layout = { sizing = { width = clay.SizingFixed(300) }, padding = clay.PaddingAll(16) },
|
||||
backgroundColor = COLOR_LIGHT,
|
||||
cornerRadius = clay.CornerRadiusAll(10)
|
||||
}) {
|
||||
clay.Text(text, clay.TextConfig({fontSize = 28, fontId = FONT_ID_BODY_28, textColor = COLOR_RED}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RendererPage :: proc(titleTextConfig: clay.TextElementConfig, widthSizing: clay.SizingAxis) {
|
||||
if clay.UI(clay.ID("RendererLeftText"), clay.Layout({sizing = {width = widthSizing}, layoutDirection = .TOP_TO_BOTTOM, childGap = 8})) {
|
||||
if clay.UI().configure({ id = clay.ID("RendererLeftText"), layout = { sizing = { width = widthSizing }, layoutDirection = .TopToBottom, childGap = 8 } }) {
|
||||
clay.Text("Renderer & Platform Agnostic", clay.TextConfig(titleTextConfig))
|
||||
if clay.UI(clay.Layout({sizing = {width = clay.SizingGrow({max = 16})}})) {}
|
||||
if clay.UI().configure({ layout = { sizing = { width = clay.SizingGrow({ max = 16 }) } } }) {}
|
||||
clay.Text(
|
||||
"Clay outputs a sorted array of primitive render commands, such as RECTANGLE, TEXT or IMAGE.",
|
||||
clay.TextConfig({fontSize = 28, fontId = FONT_ID_BODY_36, textColor = COLOR_RED}),
|
||||
@ -361,45 +361,43 @@ RendererPage :: proc(titleTextConfig: clay.TextElementConfig, widthSizing: clay.
|
||||
clay.TextConfig({fontSize = 28, fontId = FONT_ID_BODY_36, textColor = COLOR_RED}),
|
||||
)
|
||||
}
|
||||
if clay.UI(
|
||||
clay.ID("RendererRightText"),
|
||||
clay.Layout({sizing = {width = widthSizing}, childAlignment = {x = .CENTER}, layoutDirection = .TOP_TO_BOTTOM, childGap = 16}),
|
||||
) {
|
||||
if clay.UI().configure({
|
||||
id = clay.ID("RendererRightText"),
|
||||
layout = { sizing = { width = widthSizing }, childAlignment = { x = .Center }, layoutDirection = .TopToBottom, childGap = 16 },
|
||||
}) {
|
||||
clay.Text("Try changing renderer!", clay.TextConfig({fontSize = 36, fontId = FONT_ID_BODY_36, textColor = COLOR_ORANGE}))
|
||||
if clay.UI(clay.Layout({sizing = {width = clay.SizingGrow({max = 32})}})) {}
|
||||
if clay.UI().configure({ layout = { sizing = { width = clay.SizingGrow({ max = 32 }) } } }) {}
|
||||
RendererButtonActive(0, "Raylib Renderer")
|
||||
}
|
||||
}
|
||||
|
||||
RendererPageDesktop :: proc() {
|
||||
if clay.UI(
|
||||
clay.ID("RendererPageDesktop"),
|
||||
clay.Layout({sizing = {clay.SizingGrow({}), clay.SizingFit({min = cast(f32)windowHeight - 50})}, childAlignment = {y = .CENTER}, padding = {left = 50, right = 50}}),
|
||||
) {
|
||||
if clay.UI(
|
||||
clay.ID("RendererPage"),
|
||||
clay.Layout({sizing = {clay.SizingGrow({}), clay.SizingGrow({})}, childAlignment = {y = .CENTER}, padding = clay.PaddingAll(32), childGap = 32}),
|
||||
clay.Border({left = {2, COLOR_RED}, right = {2, COLOR_RED}}),
|
||||
) {
|
||||
if clay.UI().configure({
|
||||
id = clay.ID("RendererPageDesktop"),
|
||||
layout = { sizing = { clay.SizingGrow({ }), clay.SizingFit({ min = cast(f32)windowHeight - 50 }) }, childAlignment = { y = .Center }, padding = { left = 50, right = 50 } },
|
||||
}) {
|
||||
if clay.UI().configure({
|
||||
id = clay.ID("RendererPage"),
|
||||
layout = { sizing = { clay.SizingGrow({ }), clay.SizingGrow({ }) }, childAlignment = { y = .Center }, padding = clay.PaddingAll(32), childGap = 32 },
|
||||
border = { COLOR_RED, { left = 2, right = 2 } },
|
||||
}) {
|
||||
RendererPage({fontSize = 52, fontId = FONT_ID_TITLE_52, textColor = COLOR_RED}, clay.SizingPercent(0.5))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RendererPageMobile :: proc() {
|
||||
if clay.UI(
|
||||
clay.ID("RendererMobile"),
|
||||
clay.Layout(
|
||||
{
|
||||
layoutDirection = .TOP_TO_BOTTOM,
|
||||
sizing = {clay.SizingGrow({}), clay.SizingFit({min = cast(f32)windowHeight - 50})},
|
||||
childAlignment = {x = .CENTER, y = .CENTER},
|
||||
padding = {16, 16, 32, 32},
|
||||
if clay.UI().configure({
|
||||
id = clay.ID("RendererMobile"),
|
||||
layout = {
|
||||
layoutDirection = .TopToBottom,
|
||||
sizing = { clay.SizingGrow({ }), clay.SizingFit({ min = cast(f32)windowHeight - 50 }) },
|
||||
childAlignment = { x = .Center, y = .Center },
|
||||
padding = { 16, 16, 32, 32 },
|
||||
childGap = 32,
|
||||
},
|
||||
),
|
||||
clay.Rectangle({color = COLOR_LIGHT}),
|
||||
) {
|
||||
backgroundColor = COLOR_LIGHT,
|
||||
}) {
|
||||
RendererPage({fontSize = 48, fontId = FONT_ID_TITLE_48, textColor = COLOR_RED}, clay.SizingGrow({}))
|
||||
}
|
||||
}
|
||||
@ -416,47 +414,48 @@ animationLerpValue: f32 = -1.0
|
||||
createLayout :: proc(lerpValue: f32) -> clay.ClayArray(clay.RenderCommand) {
|
||||
mobileScreen := windowWidth < 750
|
||||
clay.BeginLayout()
|
||||
if clay.UI(
|
||||
clay.ID("OuterContainer"),
|
||||
clay.Layout({layoutDirection = .TOP_TO_BOTTOM, sizing = {clay.SizingGrow({}), clay.SizingGrow({})}}),
|
||||
clay.Rectangle({color = COLOR_LIGHT}),
|
||||
) {
|
||||
if clay.UI(
|
||||
clay.ID("Header"),
|
||||
clay.Layout({sizing = {clay.SizingGrow({}), clay.SizingFixed(50)}, childAlignment = {y = .CENTER}, childGap = 24, padding = {left = 32, right = 32}}),
|
||||
) {
|
||||
if clay.UI().configure({
|
||||
id = clay.ID("OuterContainer"),
|
||||
layout = { layoutDirection = .TopToBottom, sizing = { clay.SizingGrow({ }), clay.SizingGrow({ }) } },
|
||||
backgroundColor = COLOR_LIGHT,
|
||||
}) {
|
||||
if clay.UI().configure({
|
||||
id = clay.ID("Header"),
|
||||
layout = { sizing = { clay.SizingGrow({ }), clay.SizingFixed(50) }, childAlignment = { y = .Center }, childGap = 24, padding = { left = 32, right = 32 } },
|
||||
}) {
|
||||
clay.Text("Clay", &headerTextConfig)
|
||||
if clay.UI(clay.Layout({sizing = {width = clay.SizingGrow({})}})) {}
|
||||
if clay.UI().configure({ layout = { sizing = { width = clay.SizingGrow({ }) } } }) {}
|
||||
|
||||
if (!mobileScreen) {
|
||||
if clay.UI(clay.ID("LinkExamplesOuter"), clay.Layout({}), clay.Rectangle({color = {0, 0, 0, 0}})) {
|
||||
if clay.UI().configure({ id = clay.ID("LinkExamplesOuter"), backgroundColor = {0, 0, 0, 0} }) {
|
||||
clay.Text("Examples", clay.TextConfig({fontId = FONT_ID_BODY_24, fontSize = 24, textColor = {61, 26, 5, 255}}))
|
||||
}
|
||||
if clay.UI(clay.ID("LinkDocsOuter"), clay.Layout({}), clay.Rectangle({color = {0, 0, 0, 0}})) {
|
||||
if clay.UI().configure({ id = clay.ID("LinkDocsOuter"), backgroundColor = {0, 0, 0, 0} }) {
|
||||
clay.Text("Docs", clay.TextConfig({fontId = FONT_ID_BODY_24, fontSize = 24, textColor = {61, 26, 5, 255}}))
|
||||
}
|
||||
}
|
||||
if clay.UI(
|
||||
clay.ID("LinkGithubOuter"),
|
||||
clay.Layout({padding = {16, 16, 6, 6}}),
|
||||
clay.BorderOutsideRadius({2, COLOR_RED}, 10),
|
||||
clay.Rectangle({cornerRadius = clay.CornerRadiusAll(10), color = clay.PointerOver(clay.GetElementId(clay.MakeString("LinkGithubOuter"))) ? COLOR_LIGHT_HOVER : COLOR_LIGHT})
|
||||
) {
|
||||
if clay.UI().configure({
|
||||
id = clay.ID("LinkGithubOuter"),
|
||||
layout = { padding = { 16, 16, 6, 6 } },
|
||||
border = border2pxRed,
|
||||
backgroundColor = clay.Hovered() ? COLOR_LIGHT_HOVER : COLOR_LIGHT,
|
||||
cornerRadius = clay.CornerRadiusAll(10)
|
||||
}) {
|
||||
clay.Text("Github", clay.TextConfig({fontId = FONT_ID_BODY_24, fontSize = 24, textColor = {61, 26, 5, 255}}))
|
||||
}
|
||||
}
|
||||
if clay.UI(clay.ID("TopBorder1"), clay.Layout({sizing = {clay.SizingGrow({}), clay.SizingFixed(4)}}), clay.Rectangle({color = COLOR_TOP_BORDER_5})) {}
|
||||
if clay.UI(clay.ID("TopBorder2"), clay.Layout({sizing = {clay.SizingGrow({}), clay.SizingFixed(4)}}), clay.Rectangle({color = COLOR_TOP_BORDER_4})) {}
|
||||
if clay.UI(clay.ID("TopBorder3"), clay.Layout({sizing = {clay.SizingGrow({}), clay.SizingFixed(4)}}), clay.Rectangle({color = COLOR_TOP_BORDER_3})) {}
|
||||
if clay.UI(clay.ID("TopBorder4"), clay.Layout({sizing = {clay.SizingGrow({}), clay.SizingFixed(4)}}), clay.Rectangle({color = COLOR_TOP_BORDER_2})) {}
|
||||
if clay.UI(clay.ID("TopBorder5"), clay.Layout({sizing = {clay.SizingGrow({}), clay.SizingFixed(4)}}), clay.Rectangle({color = COLOR_TOP_BORDER_1})) {}
|
||||
if clay.UI(
|
||||
clay.ID("ScrollContainerBackgroundRectangle"),
|
||||
clay.Scroll({vertical = true}),
|
||||
clay.Layout({sizing = {clay.SizingGrow({}), clay.SizingGrow({})}, layoutDirection = clay.LayoutDirection.TOP_TO_BOTTOM}),
|
||||
clay.Rectangle({color = COLOR_LIGHT}),
|
||||
clay.Border({betweenChildren = {2, COLOR_RED}})
|
||||
) {
|
||||
if clay.UI().configure({ id = clay.ID("TopBorder1"), layout = { sizing = { clay.SizingGrow({ }), clay.SizingFixed(4) } }, backgroundColor = COLOR_TOP_BORDER_5 } ) {}
|
||||
if clay.UI().configure({ id = clay.ID("TopBorder2"), layout = { sizing = { clay.SizingGrow({ }), clay.SizingFixed(4) } }, backgroundColor = COLOR_TOP_BORDER_4 } ) {}
|
||||
if clay.UI().configure({ id = clay.ID("TopBorder3"), layout = { sizing = { clay.SizingGrow({ }), clay.SizingFixed(4) } }, backgroundColor = COLOR_TOP_BORDER_3 } ) {}
|
||||
if clay.UI().configure({ id = clay.ID("TopBorder4"), layout = { sizing = { clay.SizingGrow({ }), clay.SizingFixed(4) } }, backgroundColor = COLOR_TOP_BORDER_2 } ) {}
|
||||
if clay.UI().configure({ id = clay.ID("TopBorder5"), layout = { sizing = { clay.SizingGrow({ }), clay.SizingFixed(4) } }, backgroundColor = COLOR_TOP_BORDER_1 } ) {}
|
||||
if clay.UI().configure({
|
||||
id = clay.ID("ScrollContainerBackgroundRectangle"),
|
||||
scroll = { vertical = true },
|
||||
layout = { sizing = { clay.SizingGrow({ }), clay.SizingGrow({ }) }, layoutDirection = clay.LayoutDirection.TopToBottom },
|
||||
backgroundColor = COLOR_LIGHT,
|
||||
border = { COLOR_RED, { betweenChildren = 2} },
|
||||
}) {
|
||||
if (!mobileScreen) {
|
||||
LandingPageDesktop()
|
||||
FeatureBlocksDesktop()
|
||||
@ -484,8 +483,8 @@ loadFont :: proc(fontId: u16, fontSize: u16, path: cstring) {
|
||||
}
|
||||
|
||||
errorHandler :: proc "c" (errorData: clay.ErrorData) {
|
||||
if (errorData.errorType == clay.ErrorType.DUPLICATE_ID) {
|
||||
|
||||
if (errorData.errorType == clay.ErrorType.DuplicateId) {
|
||||
// etc
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -56,22 +56,28 @@ clayRaylibRender :: proc(renderCommands: ^clay.ClayArray(clay.RenderCommand), al
|
||||
case clay.RenderCommandType.None:
|
||||
{}
|
||||
case clay.RenderCommandType.Text:
|
||||
config := renderCommand.renderData.text
|
||||
// Raylib uses standard C strings so isn't compatible with cheap slices, we need to clone the string to append null terminator
|
||||
text := string(renderCommand.text.chars[:renderCommand.text.length])
|
||||
text := string(config.stringContents.chars[:config.stringContents.length])
|
||||
cloned := strings.clone_to_cstring(text, allocator)
|
||||
fontToUse: raylib.Font = raylibFonts[renderCommand.config.textElementConfig.fontId].font
|
||||
fontToUse: raylib.Font = raylibFonts[config.fontId].font
|
||||
raylib.DrawTextEx(
|
||||
fontToUse,
|
||||
cloned,
|
||||
raylib.Vector2{boundingBox.x, boundingBox.y},
|
||||
cast(f32)renderCommand.config.textElementConfig.fontSize,
|
||||
cast(f32)renderCommand.config.textElementConfig.letterSpacing,
|
||||
clayColorToRaylibColor(renderCommand.config.textElementConfig.textColor),
|
||||
cast(f32)config.fontSize,
|
||||
cast(f32)config.letterSpacing,
|
||||
clayColorToRaylibColor(config.textColor),
|
||||
)
|
||||
case clay.RenderCommandType.Image:
|
||||
config := renderCommand.renderData.image
|
||||
tintColor := config.backgroundColor
|
||||
if (tintColor.rgba == 0) {
|
||||
tintColor = { 255, 255, 255, 255 }
|
||||
}
|
||||
// TODO image handling
|
||||
imageTexture := cast(^raylib.Texture2D)renderCommand.config.imageElementConfig.imageData
|
||||
raylib.DrawTextureEx(imageTexture^, raylib.Vector2{boundingBox.x, boundingBox.y}, 0, boundingBox.width / cast(f32)imageTexture.width, raylib.WHITE)
|
||||
imageTexture := cast(^raylib.Texture2D)config.imageData
|
||||
raylib.DrawTextureEx(imageTexture^, raylib.Vector2{boundingBox.x, boundingBox.y}, 0, boundingBox.width / cast(f32)imageTexture.width, clayColorToRaylibColor(tintColor))
|
||||
case clay.RenderCommandType.ScissorStart:
|
||||
raylib.BeginScissorMode(
|
||||
cast(i32)math.round(boundingBox.x),
|
||||
@ -82,86 +88,86 @@ clayRaylibRender :: proc(renderCommands: ^clay.ClayArray(clay.RenderCommand), al
|
||||
case clay.RenderCommandType.ScissorEnd:
|
||||
raylib.EndScissorMode()
|
||||
case clay.RenderCommandType.Rectangle:
|
||||
config: ^clay.RectangleElementConfig = renderCommand.config.rectangleElementConfig
|
||||
config := renderCommand.renderData.rectangle
|
||||
if (config.cornerRadius.topLeft > 0) {
|
||||
radius: f32 = (config.cornerRadius.topLeft * 2) / min(boundingBox.width, boundingBox.height)
|
||||
raylib.DrawRectangleRounded(raylib.Rectangle{boundingBox.x, boundingBox.y, boundingBox.width, boundingBox.height}, radius, 8, clayColorToRaylibColor(config.color))
|
||||
raylib.DrawRectangleRounded(raylib.Rectangle{boundingBox.x, boundingBox.y, boundingBox.width, boundingBox.height}, radius, 8, clayColorToRaylibColor(config.backgroundColor))
|
||||
} else {
|
||||
raylib.DrawRectangle(cast(i32)boundingBox.x, cast(i32)boundingBox.y, cast(i32)boundingBox.width, cast(i32)boundingBox.height, clayColorToRaylibColor(config.color))
|
||||
raylib.DrawRectangle(cast(i32)boundingBox.x, cast(i32)boundingBox.y, cast(i32)boundingBox.width, cast(i32)boundingBox.height, clayColorToRaylibColor(config.backgroundColor))
|
||||
}
|
||||
case clay.RenderCommandType.Border:
|
||||
config := renderCommand.config.borderElementConfig
|
||||
config := renderCommand.renderData.border
|
||||
// Left border
|
||||
if (config.left.width > 0) {
|
||||
if (config.width.left > 0) {
|
||||
raylib.DrawRectangle(
|
||||
cast(i32)math.round(boundingBox.x),
|
||||
cast(i32)math.round(boundingBox.y + config.cornerRadius.topLeft),
|
||||
cast(i32)config.left.width,
|
||||
cast(i32)config.width.left,
|
||||
cast(i32)math.round(boundingBox.height - config.cornerRadius.topLeft - config.cornerRadius.bottomLeft),
|
||||
clayColorToRaylibColor(config.left.color),
|
||||
clayColorToRaylibColor(config.color),
|
||||
)
|
||||
}
|
||||
// Right border
|
||||
if (config.right.width > 0) {
|
||||
if (config.width.right > 0) {
|
||||
raylib.DrawRectangle(
|
||||
cast(i32)math.round(boundingBox.x + boundingBox.width - cast(f32)config.right.width),
|
||||
cast(i32)math.round(boundingBox.x + boundingBox.width - cast(f32)config.width.right),
|
||||
cast(i32)math.round(boundingBox.y + config.cornerRadius.topRight),
|
||||
cast(i32)config.right.width,
|
||||
cast(i32)config.width.right,
|
||||
cast(i32)math.round(boundingBox.height - config.cornerRadius.topRight - config.cornerRadius.bottomRight),
|
||||
clayColorToRaylibColor(config.right.color),
|
||||
clayColorToRaylibColor(config.color),
|
||||
)
|
||||
}
|
||||
// Top border
|
||||
if (config.top.width > 0) {
|
||||
if (config.width.top > 0) {
|
||||
raylib.DrawRectangle(
|
||||
cast(i32)math.round(boundingBox.x + config.cornerRadius.topLeft),
|
||||
cast(i32)math.round(boundingBox.y),
|
||||
cast(i32)math.round(boundingBox.width - config.cornerRadius.topLeft - config.cornerRadius.topRight),
|
||||
cast(i32)config.top.width,
|
||||
clayColorToRaylibColor(config.top.color),
|
||||
cast(i32)config.width.top,
|
||||
clayColorToRaylibColor(config.color),
|
||||
)
|
||||
}
|
||||
// Bottom border
|
||||
if (config.bottom.width > 0) {
|
||||
if (config.width.bottom > 0) {
|
||||
raylib.DrawRectangle(
|
||||
cast(i32)math.round(boundingBox.x + config.cornerRadius.bottomLeft),
|
||||
cast(i32)math.round(boundingBox.y + boundingBox.height - cast(f32)config.bottom.width),
|
||||
cast(i32)math.round(boundingBox.y + boundingBox.height - cast(f32)config.width.bottom),
|
||||
cast(i32)math.round(boundingBox.width - config.cornerRadius.bottomLeft - config.cornerRadius.bottomRight),
|
||||
cast(i32)config.bottom.width,
|
||||
clayColorToRaylibColor(config.bottom.color),
|
||||
cast(i32)config.width.bottom,
|
||||
clayColorToRaylibColor(config.color),
|
||||
)
|
||||
}
|
||||
if (config.cornerRadius.topLeft > 0) {
|
||||
raylib.DrawRing(
|
||||
raylib.Vector2{math.round(boundingBox.x + config.cornerRadius.topLeft), math.round(boundingBox.y + config.cornerRadius.topLeft)},
|
||||
math.round(config.cornerRadius.topLeft - cast(f32)config.top.width),
|
||||
math.round(config.cornerRadius.topLeft - cast(f32)config.width.top),
|
||||
config.cornerRadius.topLeft,
|
||||
180,
|
||||
270,
|
||||
10,
|
||||
clayColorToRaylibColor(config.top.color),
|
||||
clayColorToRaylibColor(config.color),
|
||||
)
|
||||
}
|
||||
if (config.cornerRadius.topRight > 0) {
|
||||
raylib.DrawRing(
|
||||
raylib.Vector2{math.round(boundingBox.x + boundingBox.width - config.cornerRadius.topRight), math.round(boundingBox.y + config.cornerRadius.topRight)},
|
||||
math.round(config.cornerRadius.topRight - cast(f32)config.top.width),
|
||||
math.round(config.cornerRadius.topRight - cast(f32)config.width.top),
|
||||
config.cornerRadius.topRight,
|
||||
270,
|
||||
360,
|
||||
10,
|
||||
clayColorToRaylibColor(config.top.color),
|
||||
clayColorToRaylibColor(config.color),
|
||||
)
|
||||
}
|
||||
if (config.cornerRadius.bottomLeft > 0) {
|
||||
raylib.DrawRing(
|
||||
raylib.Vector2{math.round(boundingBox.x + config.cornerRadius.bottomLeft), math.round(boundingBox.y + boundingBox.height - config.cornerRadius.bottomLeft)},
|
||||
math.round(config.cornerRadius.bottomLeft - cast(f32)config.top.width),
|
||||
math.round(config.cornerRadius.bottomLeft - cast(f32)config.width.top),
|
||||
config.cornerRadius.bottomLeft,
|
||||
90,
|
||||
180,
|
||||
10,
|
||||
clayColorToRaylibColor(config.bottom.color),
|
||||
clayColorToRaylibColor(config.color),
|
||||
)
|
||||
}
|
||||
if (config.cornerRadius.bottomRight > 0) {
|
||||
@ -170,12 +176,12 @@ clayRaylibRender :: proc(renderCommands: ^clay.ClayArray(clay.RenderCommand), al
|
||||
math.round(boundingBox.x + boundingBox.width - config.cornerRadius.bottomRight),
|
||||
math.round(boundingBox.y + boundingBox.height - config.cornerRadius.bottomRight),
|
||||
},
|
||||
math.round(config.cornerRadius.bottomRight - cast(f32)config.bottom.width),
|
||||
math.round(config.cornerRadius.bottomRight - cast(f32)config.width.bottom),
|
||||
config.cornerRadius.bottomRight,
|
||||
0.1,
|
||||
90,
|
||||
10,
|
||||
clayColorToRaylibColor(config.bottom.color),
|
||||
clayColorToRaylibColor(config.color),
|
||||
)
|
||||
}
|
||||
case clay.RenderCommandType.Custom:
|
||||
|
@ -45,10 +45,10 @@ target_link_libraries(SDL2_video_demo PUBLIC
|
||||
)
|
||||
|
||||
if(MSVC)
|
||||
set(CMAKE_C_FLAGS_DEBUG "/D CLAY_DEBUG")
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}")
|
||||
else()
|
||||
set(CMAKE_C_FLAGS_DEBUG "-Wall -Werror -Wno-error=missing-braces -DCLAY_DEBUG")
|
||||
set(CMAKE_C_FLAGS_RELEASE "-O3")
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}")
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}")
|
||||
endif()
|
||||
|
||||
add_custom_command(
|
||||
|
@ -9,278 +9,15 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
const int FONT_ID_BODY_16 = 0;
|
||||
Clay_Color COLOR_WHITE = { 255, 255, 255, 255};
|
||||
#include "../shared-layouts/clay-video-demo.c"
|
||||
|
||||
SDL_Surface *sample_image;
|
||||
|
||||
void RenderHeaderButton(Clay_String text) {
|
||||
CLAY(
|
||||
CLAY_LAYOUT({ .padding = { 16, 16, 8, 8 }}),
|
||||
CLAY_RECTANGLE({
|
||||
.color = { 140, 140, 140, 255 },
|
||||
.cornerRadius = 5
|
||||
})
|
||||
) {
|
||||
CLAY_TEXT(text, CLAY_TEXT_CONFIG({
|
||||
.fontId = FONT_ID_BODY_16,
|
||||
.fontSize = 16,
|
||||
.textColor = { 255, 255, 255, 255 }
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
void RenderDropdownMenuItem(Clay_String text) {
|
||||
CLAY(CLAY_LAYOUT({ .padding = CLAY_PADDING_ALL(16)})) {
|
||||
CLAY_TEXT(text, CLAY_TEXT_CONFIG({
|
||||
.fontId = FONT_ID_BODY_16,
|
||||
.fontSize = 16,
|
||||
.textColor = { 255, 255, 255, 255 }
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
Clay_String title;
|
||||
Clay_String contents;
|
||||
} Document;
|
||||
|
||||
typedef struct {
|
||||
Document *documents;
|
||||
uint32_t length;
|
||||
} DocumentArray;
|
||||
|
||||
DocumentArray documents = {
|
||||
.documents = NULL,
|
||||
.length = 5
|
||||
};
|
||||
|
||||
uint32_t selectedDocumentIndex = 0;
|
||||
|
||||
void HandleSidebarInteraction(
|
||||
Clay_ElementId elementId,
|
||||
Clay_PointerData pointerData,
|
||||
intptr_t userData
|
||||
) {
|
||||
// If this button was clicked
|
||||
if (pointerData.state == CLAY_POINTER_DATA_PRESSED_THIS_FRAME) {
|
||||
if (userData >= 0 && userData < documents.length) {
|
||||
// Select the corresponding document
|
||||
selectedDocumentIndex = userData;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Clay_RenderCommandArray CreateLayout() {
|
||||
Clay_BeginLayout();
|
||||
Clay_Sizing layoutExpand = {
|
||||
.width = CLAY_SIZING_GROW(0),
|
||||
.height = CLAY_SIZING_GROW(0)
|
||||
};
|
||||
|
||||
Clay_RectangleElementConfig contentBackgroundConfig = {
|
||||
.color = { 90, 90, 90, 255 },
|
||||
.cornerRadius = 8
|
||||
};
|
||||
|
||||
Clay_BeginLayout();
|
||||
// Build UI here
|
||||
CLAY(
|
||||
CLAY_ID("OuterContainer"),
|
||||
CLAY_RECTANGLE({ .color = { 43, 41, 51, 255 } }),
|
||||
CLAY_LAYOUT({
|
||||
.layoutDirection = CLAY_TOP_TO_BOTTOM,
|
||||
.sizing = layoutExpand,
|
||||
.padding = CLAY_PADDING_ALL(16),
|
||||
.childGap = 16
|
||||
})
|
||||
) {
|
||||
// Child elements go inside braces
|
||||
CLAY(
|
||||
CLAY_ID("HeaderBar"),
|
||||
CLAY_RECTANGLE(contentBackgroundConfig),
|
||||
CLAY_LAYOUT({
|
||||
.sizing = {
|
||||
.height = CLAY_SIZING_FIXED(60),
|
||||
.width = CLAY_SIZING_GROW(0)
|
||||
},
|
||||
.padding = { 16 },
|
||||
.childGap = 16,
|
||||
.childAlignment = {
|
||||
.y = CLAY_ALIGN_Y_CENTER
|
||||
}
|
||||
})
|
||||
) {
|
||||
// Header buttons go here
|
||||
CLAY(
|
||||
CLAY_LAYOUT({ .padding = { 16, 16, 8, 8 }}),
|
||||
CLAY_BORDER_ALL({ 2, COLOR_WHITE })
|
||||
) {
|
||||
CLAY(
|
||||
CLAY_LAYOUT({ .padding = { 8, 8, 8, 8 }}),
|
||||
CLAY_IMAGE({ sample_image, { 23, 42 } })
|
||||
) {}
|
||||
}
|
||||
CLAY(
|
||||
CLAY_ID("FileButton"),
|
||||
CLAY_LAYOUT({ .padding = { 16, 16, 8, 8 }}),
|
||||
CLAY_RECTANGLE({
|
||||
.color = { 140, 140, 140, 255 },
|
||||
.cornerRadius = 5
|
||||
})
|
||||
) {
|
||||
CLAY_TEXT(CLAY_STRING("File"), CLAY_TEXT_CONFIG({
|
||||
.fontId = FONT_ID_BODY_16,
|
||||
.fontSize = 16,
|
||||
.textColor = { 255, 255, 255, 255 }
|
||||
}));
|
||||
|
||||
bool fileMenuVisible =
|
||||
Clay_PointerOver(Clay_GetElementId(CLAY_STRING("FileButton")))
|
||||
||
|
||||
Clay_PointerOver(Clay_GetElementId(CLAY_STRING("FileMenu")));
|
||||
|
||||
if (fileMenuVisible) { // Below has been changed slightly to fix the small bug where the menu would dismiss when mousing over the top gap
|
||||
CLAY(
|
||||
CLAY_ID("FileMenu"),
|
||||
CLAY_FLOATING({
|
||||
.attachment = {
|
||||
.parent = CLAY_ATTACH_POINT_LEFT_BOTTOM
|
||||
},
|
||||
}),
|
||||
CLAY_LAYOUT({
|
||||
.padding = {0, 8 }
|
||||
})
|
||||
) {
|
||||
CLAY(
|
||||
CLAY_LAYOUT({
|
||||
.layoutDirection = CLAY_TOP_TO_BOTTOM,
|
||||
.sizing = {
|
||||
.width = CLAY_SIZING_FIXED(200)
|
||||
},
|
||||
}),
|
||||
CLAY_RECTANGLE({
|
||||
.color = { 40, 40, 40, 255 },
|
||||
.cornerRadius = 8
|
||||
})
|
||||
) {
|
||||
// Render dropdown items here
|
||||
RenderDropdownMenuItem(CLAY_STRING("New"));
|
||||
RenderDropdownMenuItem(CLAY_STRING("Open"));
|
||||
RenderDropdownMenuItem(CLAY_STRING("Close"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
RenderHeaderButton(CLAY_STRING("Edit"));
|
||||
CLAY(CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW(0) }})) {}
|
||||
RenderHeaderButton(CLAY_STRING("Upload"));
|
||||
RenderHeaderButton(CLAY_STRING("Media"));
|
||||
RenderHeaderButton(CLAY_STRING("Support"));
|
||||
}
|
||||
|
||||
CLAY(
|
||||
CLAY_ID("LowerContent"),
|
||||
CLAY_LAYOUT({ .sizing = layoutExpand, .childGap = 16 })
|
||||
) {
|
||||
CLAY(
|
||||
CLAY_ID("Sidebar"),
|
||||
CLAY_RECTANGLE(contentBackgroundConfig),
|
||||
CLAY_LAYOUT({
|
||||
.layoutDirection = CLAY_TOP_TO_BOTTOM,
|
||||
.padding = CLAY_PADDING_ALL(16),
|
||||
.childGap = 8,
|
||||
.sizing = {
|
||||
.width = CLAY_SIZING_FIXED(250),
|
||||
.height = CLAY_SIZING_GROW(0)
|
||||
}
|
||||
})
|
||||
) {
|
||||
for (int i = 0; i < documents.length; i++) {
|
||||
Document document = documents.documents[i];
|
||||
Clay_LayoutConfig sidebarButtonLayout = {
|
||||
.sizing = { .width = CLAY_SIZING_GROW(0) },
|
||||
.padding = CLAY_PADDING_ALL(16)
|
||||
};
|
||||
|
||||
if (i == selectedDocumentIndex) {
|
||||
CLAY(
|
||||
CLAY_LAYOUT(sidebarButtonLayout),
|
||||
CLAY_RECTANGLE({
|
||||
.color = { 120, 120, 120, 255 },
|
||||
.cornerRadius = 8,
|
||||
})
|
||||
) {
|
||||
CLAY_TEXT(document.title, CLAY_TEXT_CONFIG({
|
||||
.fontId = FONT_ID_BODY_16,
|
||||
.fontSize = 20,
|
||||
.textColor = { 255, 255, 255, 255 }
|
||||
}));
|
||||
}
|
||||
} else {
|
||||
CLAY(
|
||||
CLAY_LAYOUT(sidebarButtonLayout),
|
||||
Clay_OnHover(HandleSidebarInteraction, i),
|
||||
Clay_Hovered()
|
||||
? CLAY_RECTANGLE({
|
||||
.color = { 120, 120, 120, 120 },
|
||||
.cornerRadius = 8
|
||||
})
|
||||
: 0
|
||||
) {
|
||||
CLAY_TEXT(document.title, CLAY_TEXT_CONFIG({
|
||||
.fontId = FONT_ID_BODY_16,
|
||||
.fontSize = 20,
|
||||
.textColor = { 255, 255, 255, 255 }
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CLAY(
|
||||
CLAY_ID("MainContent"),
|
||||
CLAY_RECTANGLE(contentBackgroundConfig),
|
||||
CLAY_SCROLL({ .vertical = true }),
|
||||
CLAY_LAYOUT({
|
||||
.layoutDirection = CLAY_TOP_TO_BOTTOM,
|
||||
.childGap = 16,
|
||||
.padding = CLAY_PADDING_ALL(16),
|
||||
.sizing = layoutExpand
|
||||
})
|
||||
) {
|
||||
Document selectedDocument = documents.documents[selectedDocumentIndex];
|
||||
CLAY_TEXT(selectedDocument.title, CLAY_TEXT_CONFIG({
|
||||
.fontId = FONT_ID_BODY_16,
|
||||
.fontSize = 24,
|
||||
.textColor = COLOR_WHITE
|
||||
}));
|
||||
CLAY_TEXT(selectedDocument.contents, CLAY_TEXT_CONFIG({
|
||||
.fontId = FONT_ID_BODY_16,
|
||||
.fontSize = 24,
|
||||
.textColor = COLOR_WHITE
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Clay_EndLayout();
|
||||
}
|
||||
|
||||
void HandleClayErrors(Clay_ErrorData errorData) {
|
||||
printf("%s", errorData.errorText.chars);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
documents.documents = (Document[]) {
|
||||
{ .title = CLAY_STRING("Squirrels"), .contents = CLAY_STRING("The Secret Life of Squirrels: Nature's Clever Acrobats\n""Squirrels are often overlooked creatures, dismissed as mere park inhabitants or backyard nuisances. Yet, beneath their fluffy tails and twitching noses lies an intricate world of cunning, agility, and survival tactics that are nothing short of fascinating. As one of the most common mammals in North America, squirrels have adapted to a wide range of environments from bustling urban centers to tranquil forests and have developed a variety of unique behaviors that continue to intrigue scientists and nature enthusiasts alike.\n""\n""Master Tree Climbers\n""At the heart of a squirrel's skill set is its impressive ability to navigate trees with ease. Whether they're darting from branch to branch or leaping across wide gaps, squirrels possess an innate talent for acrobatics. Their powerful hind legs, which are longer than their front legs, give them remarkable jumping power. With a tail that acts as a counterbalance, squirrels can leap distances of up to ten times the length of their body, making them some of the best aerial acrobats in the animal kingdom.\n""But it's not just their agility that makes them exceptional climbers. Squirrels' sharp, curved claws allow them to grip tree bark with precision, while the soft pads on their feet provide traction on slippery surfaces. Their ability to run at high speeds and scale vertical trunks with ease is a testament to the evolutionary adaptations that have made them so successful in their arboreal habitats.\n""\n""Food Hoarders Extraordinaire\n""Squirrels are often seen frantically gathering nuts, seeds, and even fungi in preparation for winter. While this behavior may seem like instinctual hoarding, it is actually a survival strategy that has been honed over millions of years. Known as \"scatter hoarding,\" squirrels store their food in a variety of hidden locations, often burying it deep in the soil or stashing it in hollowed-out tree trunks.\n""Interestingly, squirrels have an incredible memory for the locations of their caches. Research has shown that they can remember thousands of hiding spots, often returning to them months later when food is scarce. However, they don't always recover every stash some forgotten caches eventually sprout into new trees, contributing to forest regeneration. This unintentional role as forest gardeners highlights the ecological importance of squirrels in their ecosystems.\n""\n""The Great Squirrel Debate: Urban vs. Wild\n""While squirrels are most commonly associated with rural or wooded areas, their adaptability has allowed them to thrive in urban environments as well. In cities, squirrels have become adept at finding food sources in places like parks, streets, and even garbage cans. However, their urban counterparts face unique challenges, including traffic, predators, and the lack of natural shelters. Despite these obstacles, squirrels in urban areas are often observed using human infrastructure such as buildings, bridges, and power lines as highways for their acrobatic escapades.\n""There is, however, a growing concern regarding the impact of urban life on squirrel populations. Pollution, deforestation, and the loss of natural habitats are making it more difficult for squirrels to find adequate food and shelter. As a result, conservationists are focusing on creating squirrel-friendly spaces within cities, with the goal of ensuring these resourceful creatures continue to thrive in both rural and urban landscapes.\n""\n""A Symbol of Resilience\n""In many cultures, squirrels are symbols of resourcefulness, adaptability, and preparation. Their ability to thrive in a variety of environments while navigating challenges with agility and grace serves as a reminder of the resilience inherent in nature. Whether you encounter them in a quiet forest, a city park, or your own backyard, squirrels are creatures that never fail to amaze with their endless energy and ingenuity.\n""In the end, squirrels may be small, but they are mighty in their ability to survive and thrive in a world that is constantly changing. So next time you spot one hopping across a branch or darting across your lawn, take a moment to appreciate the remarkable acrobat at work a true marvel of the natural world.\n") },
|
||||
{ .title = CLAY_STRING("Lorem Ipsum"), .contents = CLAY_STRING("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.") },
|
||||
{ .title = CLAY_STRING("Vacuum Instructions"), .contents = CLAY_STRING("Chapter 3: Getting Started - Unpacking and Setup\n""\n""Congratulations on your new SuperClean Pro 5000 vacuum cleaner! In this section, we will guide you through the simple steps to get your vacuum up and running. Before you begin, please ensure that you have all the components listed in the \"Package Contents\" section on page 2.\n""\n""1. Unboxing Your Vacuum\n""Carefully remove the vacuum cleaner from the box. Avoid using sharp objects that could damage the product. Once removed, place the unit on a flat, stable surface to proceed with the setup. Inside the box, you should find:\n""\n"" The main vacuum unit\n"" A telescoping extension wand\n"" A set of specialized cleaning tools (crevice tool, upholstery brush, etc.)\n"" A reusable dust bag (if applicable)\n"" A power cord with a 3-prong plug\n"" A set of quick-start instructions\n""\n""2. Assembling Your Vacuum\n""Begin by attaching the extension wand to the main body of the vacuum cleaner. Line up the connectors and twist the wand into place until you hear a click. Next, select the desired cleaning tool and firmly attach it to the wand's end, ensuring it is securely locked in.\n""\n""For models that require a dust bag, slide the bag into the compartment at the back of the vacuum, making sure it is properly aligned with the internal mechanism. If your vacuum uses a bagless system, ensure the dust container is correctly seated and locked in place before use.\n""\n""3. Powering On\n""To start the vacuum, plug the power cord into a grounded electrical outlet. Once plugged in, locate the power switch, usually positioned on the side of the handle or body of the unit, depending on your model. Press the switch to the \"On\" position, and you should hear the motor begin to hum. If the vacuum does not power on, check that the power cord is securely plugged in, and ensure there are no blockages in the power switch.\n""\n""Note: Before first use, ensure that the vacuum filter (if your model has one) is properly installed. If unsure, refer to \"Section 5: Maintenance\" for filter installation instructions.") },
|
||||
{ .title = CLAY_STRING("Article 4"), .contents = CLAY_STRING("Article 4") },
|
||||
{ .title = CLAY_STRING("Article 5"), .contents = CLAY_STRING("Article 5") },
|
||||
};
|
||||
|
||||
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
|
||||
fprintf(stderr, "Error: could not initialize SDL: %s\n", SDL_GetError());
|
||||
return 1;
|
||||
@ -323,11 +60,12 @@ int main(int argc, char *argv[]) {
|
||||
SDL_GetWindowSize(window, &windowWidth, &windowHeight);
|
||||
Clay_Initialize(clayMemory, (Clay_Dimensions) { (float)windowWidth, (float)windowHeight }, (Clay_ErrorHandler) { HandleClayErrors });
|
||||
|
||||
Clay_SetMeasureTextFunction(SDL2_MeasureText, (uintptr_t)&fonts);
|
||||
Clay_SetMeasureTextFunction(SDL2_MeasureText, &fonts);
|
||||
|
||||
Uint64 NOW = SDL_GetPerformanceCounter();
|
||||
Uint64 LAST = 0;
|
||||
double deltaTime = 0;
|
||||
ClayVideoDemo_Data demoData = ClayVideoDemo_Initialize();
|
||||
|
||||
while (true) {
|
||||
Clay_Vector2 scrollDelta = {};
|
||||
@ -345,6 +83,7 @@ int main(int argc, char *argv[]) {
|
||||
LAST = NOW;
|
||||
NOW = SDL_GetPerformanceCounter();
|
||||
deltaTime = (double)((NOW - LAST)*1000 / (double)SDL_GetPerformanceFrequency() );
|
||||
printf("%f\n", deltaTime);
|
||||
|
||||
int mouseX = 0;
|
||||
int mouseY = 0;
|
||||
@ -361,7 +100,7 @@ int main(int argc, char *argv[]) {
|
||||
SDL_GetWindowSize(window, &windowWidth, &windowHeight);
|
||||
Clay_SetLayoutDimensions((Clay_Dimensions) { (float)windowWidth, (float)windowHeight });
|
||||
|
||||
Clay_RenderCommandArray renderCommands = CreateLayout();
|
||||
Clay_RenderCommandArray renderCommands = ClayVideoDemo_CreateLayout(&demoData);
|
||||
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
|
||||
SDL_RenderClear(renderer);
|
||||
|
||||
|
@ -4,8 +4,8 @@ cmake_minimum_required(VERSION 3.27)
|
||||
project(clay_examples_sdl3_simple_demo C)
|
||||
set(CMAKE_C_STANDARD 99)
|
||||
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -g")
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O3")
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}")
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}")
|
||||
|
||||
include(FetchContent)
|
||||
set(FETCHCONTENT_QUIET FALSE)
|
||||
@ -14,7 +14,7 @@ set(FETCHCONTENT_QUIET FALSE)
|
||||
FetchContent_Declare(
|
||||
SDL
|
||||
GIT_REPOSITORY https://github.com/libsdl-org/SDL.git
|
||||
GIT_TAG preview-3.1.6
|
||||
GIT_TAG release-3.2.4
|
||||
GIT_SHALLOW TRUE
|
||||
GIT_PROGRESS TRUE
|
||||
)
|
||||
@ -34,11 +34,24 @@ message(STATUS "Using SDL_ttf via FetchContent")
|
||||
FetchContent_MakeAvailable(SDL_ttf)
|
||||
set_property(DIRECTORY "${sdl_ttf_SOURCE_DIR}" PROPERTY EXCLUDE_FROM_ALL TRUE)
|
||||
|
||||
# Download SDL_image
|
||||
FetchContent_Declare(
|
||||
SDL_image
|
||||
GIT_REPOSITORY "https://github.com/libsdl-org/SDL_image.git"
|
||||
GIT_TAG release-3.2.0 # Slightly risky to use main branch, but it's the only one available
|
||||
GIT_SHALLOW TRUE
|
||||
GIT_PROGRESS TRUE
|
||||
)
|
||||
message(STATUS "Using SDL_image via FetchContent")
|
||||
FetchContent_MakeAvailable(SDL_image)
|
||||
set_property(DIRECTORY "${SDL_image_SOURCE_DIR}" PROPERTY EXCLUDE_FROM_ALL TRUE)
|
||||
|
||||
# Example executable
|
||||
add_executable(${PROJECT_NAME} main.c)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE
|
||||
SDL3::SDL3
|
||||
SDL3_ttf::SDL3_ttf
|
||||
SDL3_image::SDL3_image
|
||||
)
|
||||
|
||||
add_custom_command(
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include "../../renderers/SDL3/clay_renderer_SDL3.c"
|
||||
#include "../shared-layouts/clay-video-demo.c"
|
||||
|
||||
static const Uint32 FONT_ID = 0;
|
||||
|
||||
@ -18,12 +19,16 @@ static const Clay_Color COLOR_LIGHT = (Clay_Color) {224, 215, 210, 255};
|
||||
|
||||
typedef struct app_state {
|
||||
SDL_Window *window;
|
||||
SDL_Renderer *renderer;
|
||||
Clay_SDL3RendererData rendererData;
|
||||
ClayVideoDemo_Data demoData;
|
||||
} AppState;
|
||||
|
||||
static inline Clay_Dimensions SDL_MeasureText(Clay_StringSlice text, Clay_TextElementConfig *config, uintptr_t userData)
|
||||
SDL_Surface *sample_image;
|
||||
|
||||
static inline Clay_Dimensions SDL_MeasureText(Clay_StringSlice text, Clay_TextElementConfig *config, void *userData)
|
||||
{
|
||||
TTF_Font *font = gFonts[config->fontId];
|
||||
TTF_Font **fonts = userData;
|
||||
TTF_Font *font = fonts[config->fontId];
|
||||
int width, height;
|
||||
|
||||
if (!TTF_GetStringSize(font, text.chars, text.length, &width, &height)) {
|
||||
@ -33,75 +38,6 @@ static inline Clay_Dimensions SDL_MeasureText(Clay_StringSlice text, Clay_TextEl
|
||||
return (Clay_Dimensions) { (float) width, (float) height };
|
||||
}
|
||||
|
||||
static void Label(const Clay_String text, const int cornerRadius)
|
||||
{
|
||||
CLAY(CLAY_LAYOUT({ .padding = {8, 8} }),
|
||||
CLAY_RECTANGLE({
|
||||
.color = Clay_Hovered() ? COLOR_BLUE : COLOR_ORANGE,
|
||||
.cornerRadius = cornerRadius,
|
||||
})) {
|
||||
CLAY_TEXT(text, CLAY_TEXT_CONFIG({
|
||||
.textColor = { 255, 255, 255, 255 },
|
||||
.fontId = FONT_ID,
|
||||
.fontSize = 24,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
static void LabelBorder(const Clay_String text, const int cornerRadius, const int thickness)
|
||||
{
|
||||
CLAY(
|
||||
CLAY_LAYOUT({
|
||||
.padding = {16, 16, 8, 8 } }),
|
||||
CLAY_BORDER_OUTSIDE_RADIUS(
|
||||
thickness,
|
||||
COLOR_BLUE,
|
||||
cornerRadius)
|
||||
){
|
||||
CLAY_TEXT(text, CLAY_TEXT_CONFIG({
|
||||
.textColor = { 255, 255, 255, 255 },
|
||||
.fontId = FONT_ID,
|
||||
.fontSize = 24,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
static Clay_RenderCommandArray Clay_CreateLayout()
|
||||
{
|
||||
Clay_BeginLayout();
|
||||
CLAY(CLAY_ID("MainContent"),
|
||||
CLAY_LAYOUT({
|
||||
.sizing = {
|
||||
.width = CLAY_SIZING_GROW(),
|
||||
.height = CLAY_SIZING_GROW(),
|
||||
},
|
||||
.childAlignment = {
|
||||
.x = CLAY_ALIGN_X_CENTER,
|
||||
.y = CLAY_ALIGN_Y_CENTER,
|
||||
},
|
||||
.childGap = 10,
|
||||
.padding = { 10, 10 },
|
||||
.layoutDirection = CLAY_TOP_TO_BOTTOM,
|
||||
}),
|
||||
CLAY_BORDER({
|
||||
.left = { 20, COLOR_BLUE },
|
||||
.right = { 20, COLOR_BLUE },
|
||||
.bottom = { 20, COLOR_BLUE }
|
||||
}),
|
||||
CLAY_RECTANGLE({
|
||||
.color = COLOR_LIGHT,
|
||||
})
|
||||
) {
|
||||
Label(CLAY_STRING("Rounded - Button 1"), 10);
|
||||
Label(CLAY_STRING("Straight - Button 2") , 0);
|
||||
Label(CLAY_STRING("Rounded+ - Button 3") , 20);
|
||||
LabelBorder(CLAY_STRING("Border - Button 4"), 0, 5);
|
||||
LabelBorder(CLAY_STRING("RoundedBorder - Button 5"), 10, 5);
|
||||
LabelBorder(CLAY_STRING("RoundedBorder - Button 6"), 40, 15);
|
||||
}
|
||||
return Clay_EndLayout();
|
||||
}
|
||||
|
||||
void HandleClayErrors(Clay_ErrorData errorData) {
|
||||
printf("%s", errorData.errorText.chars);
|
||||
}
|
||||
@ -121,19 +57,33 @@ SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
|
||||
}
|
||||
*appstate = state;
|
||||
|
||||
if (!SDL_CreateWindowAndRenderer("Clay Demo", 640, 480, 0, &state->window, &state->renderer)) {
|
||||
if (!SDL_CreateWindowAndRenderer("Clay Demo", 640, 480, 0, &state->window, &state->rendererData.renderer)) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "Failed to create window and renderer: %s", SDL_GetError());
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
SDL_SetWindowResizable(state->window, true);
|
||||
|
||||
state->rendererData.textEngine = TTF_CreateRendererTextEngine(state->rendererData.renderer);
|
||||
if (!state->rendererData.textEngine) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "Failed to create text engine from renderer: %s", SDL_GetError());
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
|
||||
state->rendererData.fonts = SDL_calloc(1, sizeof(TTF_Font *));
|
||||
if (!state->rendererData.fonts) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "Failed to allocate memory for the font array: %s", SDL_GetError());
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
|
||||
TTF_Font *font = TTF_OpenFont("resources/Roboto-Regular.ttf", 24);
|
||||
if (!font) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "Failed to load font: %s", SDL_GetError());
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
|
||||
gFonts[FONT_ID] = font;
|
||||
state->rendererData.fonts[FONT_ID] = font;
|
||||
|
||||
sample_image = IMG_Load("resources/sample.png");
|
||||
|
||||
/* Initialize Clay */
|
||||
uint64_t totalMemorySize = Clay_MinMemorySize();
|
||||
@ -145,7 +95,9 @@ SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
|
||||
int width, height;
|
||||
SDL_GetWindowSize(state->window, &width, &height);
|
||||
Clay_Initialize(clayMemory, (Clay_Dimensions) { (float) width, (float) height }, (Clay_ErrorHandler) { HandleClayErrors });
|
||||
Clay_SetMeasureTextFunction(SDL_MeasureText, 0);
|
||||
Clay_SetMeasureTextFunction(SDL_MeasureText, state->rendererData.fonts);
|
||||
|
||||
state->demoData = ClayVideoDemo_Initialize();
|
||||
|
||||
*appstate = state;
|
||||
return SDL_APP_CONTINUE;
|
||||
@ -164,10 +116,14 @@ SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event)
|
||||
break;
|
||||
case SDL_EVENT_MOUSE_MOTION:
|
||||
Clay_SetPointerState((Clay_Vector2) { event->motion.x, event->motion.y },
|
||||
event->motion.state & SDL_BUTTON_LEFT);
|
||||
event->motion.state & SDL_BUTTON_LMASK);
|
||||
break;
|
||||
case SDL_EVENT_MOUSE_BUTTON_DOWN:
|
||||
Clay_SetPointerState((Clay_Vector2) { event->button.x, event->button.y },
|
||||
event->button.button == SDL_BUTTON_LEFT);
|
||||
break;
|
||||
case SDL_EVENT_MOUSE_WHEEL:
|
||||
Clay_UpdateScrollContainers(true, (Clay_Vector2) { event->motion.xrel, event->motion.yrel }, 0.01f);
|
||||
Clay_UpdateScrollContainers(true, (Clay_Vector2) { event->wheel.x, event->wheel.y }, 0.01f);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@ -180,14 +136,14 @@ SDL_AppResult SDL_AppIterate(void *appstate)
|
||||
{
|
||||
AppState *state = appstate;
|
||||
|
||||
Clay_RenderCommandArray render_commands = Clay_CreateLayout();
|
||||
Clay_RenderCommandArray render_commands = ClayVideoDemo_CreateLayout(&state->demoData);
|
||||
|
||||
SDL_SetRenderDrawColor(state->renderer, 0, 0, 0, 255);
|
||||
SDL_RenderClear(state->renderer);
|
||||
SDL_SetRenderDrawColor(state->rendererData.renderer, 0, 0, 0, 255);
|
||||
SDL_RenderClear(state->rendererData.renderer);
|
||||
|
||||
SDL_RenderClayCommands(state->renderer, &render_commands);
|
||||
SDL_Clay_RenderClayCommands(&state->rendererData, &render_commands);
|
||||
|
||||
SDL_RenderPresent(state->renderer);
|
||||
SDL_RenderPresent(state->rendererData.renderer);
|
||||
|
||||
return SDL_APP_CONTINUE;
|
||||
}
|
||||
@ -203,12 +159,23 @@ void SDL_AppQuit(void *appstate, SDL_AppResult result)
|
||||
AppState *state = appstate;
|
||||
|
||||
if (state) {
|
||||
if (state->renderer)
|
||||
SDL_DestroyRenderer(state->renderer);
|
||||
if (state->rendererData.renderer)
|
||||
SDL_DestroyRenderer(state->rendererData.renderer);
|
||||
|
||||
if (state->window)
|
||||
SDL_DestroyWindow(state->window);
|
||||
|
||||
if (state->rendererData.fonts) {
|
||||
for(size_t i = 0; i < sizeof(state->rendererData.fonts) / sizeof(*state->rendererData.fonts); i++) {
|
||||
TTF_CloseFont(state->rendererData.fonts[i]);
|
||||
}
|
||||
|
||||
SDL_free(state->rendererData.fonts);
|
||||
}
|
||||
|
||||
if (state->rendererData.textEngine)
|
||||
TTF_DestroyRendererTextEngine(state->rendererData.textEngine);
|
||||
|
||||
SDL_free(state);
|
||||
}
|
||||
TTF_Quit();
|
||||
|
BIN
examples/SDL3-simple-demo/resources/sample.png
Normal file
BIN
examples/SDL3-simple-demo/resources/sample.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 850 B |
@ -4,7 +4,6 @@ set(CMAKE_C_STANDARD 99)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake")
|
||||
|
||||
|
||||
add_executable(clay_examples_cairo_pdf_rendering main.c)
|
||||
|
||||
find_package(Cairo REQUIRED)
|
||||
@ -13,8 +12,8 @@ target_compile_options(clay_examples_cairo_pdf_rendering PUBLIC)
|
||||
target_include_directories(clay_examples_cairo_pdf_rendering PUBLIC . ${CAIRO_INCLUDE_DIRS})
|
||||
|
||||
target_link_libraries(clay_examples_cairo_pdf_rendering PUBLIC Cairo::Cairo)
|
||||
set(CMAKE_C_FLAGS_DEBUG "-Wall -Werror -Wno-error=missing-braces")
|
||||
set(CMAKE_C_FLAGS_RELEASE "-O3")
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}")
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}")
|
||||
|
||||
add_custom_command(
|
||||
TARGET clay_examples_cairo_pdf_rendering POST_BUILD
|
||||
|
@ -31,78 +31,78 @@
|
||||
// e.g. render PNGs.
|
||||
#include <cairo/cairo-pdf.h>
|
||||
|
||||
const uint16_t FONT_CALLISTOGA = 0;
|
||||
const uint16_t FONT_QUICKSAND = 0;
|
||||
|
||||
// Layout the first page.
|
||||
void Layout() {
|
||||
static Clay_Color PRIMARY = { 0xa8, 0x42, 0x1c, 255 };
|
||||
static Clay_Color BACKGROUND = { 0xF4, 0xEB, 0xE6, 255 };
|
||||
static Clay_Color ACCENT = { 0xFA, 0xE0, 0xD4, 255 };
|
||||
|
||||
CLAY(CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0) },
|
||||
.layoutDirection = CLAY_TOP_TO_BOTTOM }),
|
||||
CLAY_RECTANGLE({ .color = BACKGROUND })) {
|
||||
CLAY(CLAY_ID("PageMargins"),
|
||||
CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0) },
|
||||
CLAY({
|
||||
.layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0) },
|
||||
.layoutDirection = CLAY_TOP_TO_BOTTOM },
|
||||
.backgroundColor = BACKGROUND
|
||||
}) {
|
||||
CLAY({ .id = CLAY_ID("PageMargins"), .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0) },
|
||||
.padding = { 70, 70, 50, 50 }, // Some nice looking page margins
|
||||
.layoutDirection = CLAY_TOP_TO_BOTTOM,
|
||||
.childGap = 10})) {
|
||||
|
||||
.childGap = 10}
|
||||
}) {
|
||||
// Section Title
|
||||
CLAY(CLAY_TEXT(
|
||||
CLAY_STRING("Features Overview"),
|
||||
CLAY_TEXT_CONFIG({
|
||||
.fontFamily = CLAY_STRING("Calistoga"),
|
||||
.textColor = PRIMARY,
|
||||
.fontSize = 24
|
||||
})
|
||||
));
|
||||
CLAY_TEXT(CLAY_STRING("Features Overview"), CLAY_TEXT_CONFIG({ .fontId = FONT_CALLISTOGA, .textColor = PRIMARY, .fontSize = 24 }));
|
||||
|
||||
// Feature Box
|
||||
CLAY(CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIT(0) }, .childGap = 10 })) {
|
||||
CLAY(CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIT(0) }}), CLAY_RECTANGLE({
|
||||
.color = ACCENT,
|
||||
.cornerRadius = CLAY_CORNER_RADIUS(12),
|
||||
})) {
|
||||
CLAY(CLAY_LAYOUT({.padding = CLAY_PADDING_ALL(20), .childGap = 4, .layoutDirection = CLAY_TOP_TO_BOTTOM })) {
|
||||
CLAY({ .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIT(0) }, .childGap = 10 }}) {
|
||||
CLAY({ .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIT(0) }}, .backgroundColor = ACCENT, .cornerRadius = CLAY_CORNER_RADIUS(12) }) {
|
||||
CLAY({ .layout = {.padding = CLAY_PADDING_ALL(20), .childGap = 4, .layoutDirection = CLAY_TOP_TO_BOTTOM }}) {
|
||||
CLAY_TEXT(CLAY_STRING("- High performance"),
|
||||
CLAY_TEXT_CONFIG({ .textColor = PRIMARY, .fontSize = 14, .fontFamily = CLAY_STRING("Quicksand SemiBold") }));
|
||||
CLAY_TEXT_CONFIG({ .textColor = PRIMARY, .fontSize = 14, .fontId = FONT_QUICKSAND }));
|
||||
CLAY_TEXT(CLAY_STRING("- Declarative syntax"),
|
||||
CLAY_TEXT_CONFIG({ .textColor = PRIMARY, .fontSize = 14, .fontFamily = CLAY_STRING("Quicksand SemiBold") }));
|
||||
CLAY_TEXT_CONFIG({ .textColor = PRIMARY, .fontSize = 14, .fontId = FONT_QUICKSAND }));
|
||||
CLAY_TEXT(CLAY_STRING("- Flexbox-style responsive layout"),
|
||||
CLAY_TEXT_CONFIG({ .textColor = PRIMARY, .fontSize = 14, .fontFamily = CLAY_STRING("Quicksand SemiBold") }));
|
||||
CLAY_TEXT_CONFIG({ .textColor = PRIMARY, .fontSize = 14, .fontId = FONT_QUICKSAND }));
|
||||
CLAY_TEXT(CLAY_STRING("- Single .h file for C/C++"),
|
||||
CLAY_TEXT_CONFIG({ .textColor = PRIMARY, .fontSize = 14, .fontFamily = CLAY_STRING("Quicksand SemiBold") }));
|
||||
CLAY_TEXT_CONFIG({ .textColor = PRIMARY, .fontSize = 14, .fontId = FONT_QUICKSAND }));
|
||||
CLAY_TEXT(CLAY_STRING("- And now with cairo!"),
|
||||
CLAY_TEXT_CONFIG({ .textColor = PRIMARY, .fontSize = 14, .fontFamily = CLAY_STRING("Quicksand SemiBold") }));
|
||||
CLAY_TEXT_CONFIG({ .textColor = PRIMARY, .fontSize = 14, .fontId = FONT_QUICKSAND }));
|
||||
}
|
||||
}
|
||||
CLAY(CLAY_LAYOUT({
|
||||
CLAY({
|
||||
.layout = {
|
||||
.sizing = {CLAY_SIZING_FIT(0), CLAY_SIZING_GROW(0)},
|
||||
.padding = CLAY_PADDING_ALL(10),
|
||||
.layoutDirection = CLAY_TOP_TO_BOTTOM,
|
||||
.childAlignment = { CLAY_ALIGN_X_CENTER, CLAY_ALIGN_Y_CENTER },
|
||||
.childGap = 4
|
||||
}), CLAY_RECTANGLE({ .color = ACCENT, .cornerRadius = CLAY_CORNER_RADIUS(8) })) {
|
||||
},
|
||||
.backgroundColor = ACCENT,
|
||||
.cornerRadius = CLAY_CORNER_RADIUS(8)
|
||||
}) {
|
||||
// Profile picture
|
||||
CLAY(CLAY_LAYOUT({
|
||||
CLAY({ .layout = {
|
||||
.sizing = {CLAY_SIZING_FIT(0), CLAY_SIZING_GROW(0)},
|
||||
.padding = { 30, 30, 0, 0 },
|
||||
.layoutDirection = CLAY_TOP_TO_BOTTOM,
|
||||
.childAlignment = { CLAY_ALIGN_X_CENTER, CLAY_ALIGN_Y_CENTER }}), CLAY_BORDER_OUTSIDE_RADIUS(2, PRIMARY, 10)) {
|
||||
CLAY(CLAY_LAYOUT({ .sizing = { CLAY_SIZING_FIXED(32), CLAY_SIZING_FIXED(32) } }), CLAY_IMAGE({ .sourceDimensions = { 32, 32 }, .path = CLAY_STRING("resources/check.png") }));
|
||||
.childAlignment = { CLAY_ALIGN_X_CENTER, CLAY_ALIGN_Y_CENTER }},
|
||||
.border = { .color = PRIMARY, .width = 2, 2, 2, 2 }, .cornerRadius = 10
|
||||
}) {
|
||||
CLAY({ .layout = { .sizing = { CLAY_SIZING_FIXED(32), CLAY_SIZING_FIXED(32) } }, .image = { .sourceDimensions = { 32, 32 }, .imageData = "resources/check.png" }});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CLAY(CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIXED(16) } }));
|
||||
CLAY({ .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIXED(16) } }});
|
||||
|
||||
CLAY(CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0) }, .childGap = 10, .layoutDirection = CLAY_TOP_TO_BOTTOM })) {
|
||||
CLAY_TEXT(CLAY_STRING("Cairo"), CLAY_TEXT_CONFIG({ .fontFamily = CLAY_STRING("Calistoga"), .fontSize = 24, .textColor = PRIMARY }));
|
||||
CLAY(CLAY_LAYOUT({ .padding = CLAY_PADDING_ALL(10) }), CLAY_RECTANGLE({ .color = ACCENT, .cornerRadius = CLAY_CORNER_RADIUS(10) })) {
|
||||
CLAY({ .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0) }, .childGap = 10, .layoutDirection = CLAY_TOP_TO_BOTTOM }}) {
|
||||
CLAY_TEXT(CLAY_STRING("Cairo"), CLAY_TEXT_CONFIG({ .fontId = FONT_CALLISTOGA, .fontSize = 24, .textColor = PRIMARY }));
|
||||
CLAY({ .layout = { .padding = CLAY_PADDING_ALL(10) }, .backgroundColor = ACCENT, .cornerRadius = 10 }) {
|
||||
CLAY_TEXT(CLAY_STRING("Officiis quia quia qui inventore ratione voluptas et. Quidem sunt unde similique. Qui est et exercitationem cumque harum illum. Numquam placeat aliquid quo voluptatem. "
|
||||
"Deleniti saepe nihil exercitationem nemo illo. Consequatur beatae repellat provident similique. Provident qui exercitationem deserunt sapiente. Quam qui dolor corporis odit. "
|
||||
"Assumenda corrupti sunt culpa pariatur. Vero sit ut minima. In est consequatur minus et cum sint illum aperiam. Qui ipsa quas nisi omnis aut quia nobis. "
|
||||
"Corporis deserunt eum mollitia modi rerum voluptas. Expedita non ab esse. Sit voluptates eos voluptatem labore aspernatur quia eum. Modi cumque atque non. Sunt officiis corrupti neque ut inventore excepturi rem minima. Possimus sed soluta qui ea aut ipsum laborum fugit. "
|
||||
"Voluptate eum consectetur non. Quo autem voluptate soluta atque dolorum maxime. Officiis inventore omnis eveniet beatae ipsa optio. Unde voluptatum ut autem quia sit sit et. Ut inventore qui quia totam consequatur. Sit ea consequatur omnis rerum nulla aspernatur deleniti."), CLAY_TEXT_CONFIG({ .fontFamily = CLAY_STRING("Quicksand SemiBold"), .fontSize = 16, .textColor = PRIMARY, .lineHeight = 16 }));
|
||||
"Voluptate eum consectetur non. Quo autem voluptate soluta atque dolorum maxime. Officiis inventore omnis eveniet beatae ipsa optio. Unde voluptatum ut autem quia sit sit et. Ut inventore qui quia totam consequatur. Sit ea consequatur omnis rerum nulla aspernatur deleniti."), CLAY_TEXT_CONFIG({ .fontId = FONT_QUICKSAND, .fontSize = 16, .textColor = PRIMARY, .lineHeight = 16 }));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -136,11 +136,17 @@ int main(void) {
|
||||
|
||||
uint64_t totalMemorySize = Clay_MinMemorySize();
|
||||
Clay_Arena clayMemory = Clay_CreateArenaWithCapacityAndMemory(totalMemorySize, malloc(totalMemorySize));
|
||||
Clay_SetMeasureTextFunction(Clay_Cairo_MeasureText);
|
||||
|
||||
// We initialize Clay with the same size
|
||||
Clay_Initialize(clayMemory, (Clay_Dimensions) { width, height }, (Clay_ErrorHandler) { HandleClayErrors });
|
||||
|
||||
char** fonts = (char*[]) {
|
||||
"Callistoga",
|
||||
"Quicksand Semibold"
|
||||
};
|
||||
|
||||
Clay_SetMeasureTextFunction(Clay_Cairo_MeasureText, (uintptr_t)fonts);
|
||||
|
||||
Clay_BeginLayout();
|
||||
|
||||
// Here you can now create the declarative clay layout.
|
||||
@ -149,7 +155,7 @@ int main(void) {
|
||||
|
||||
Clay_RenderCommandArray commands = Clay_EndLayout();
|
||||
// Pass our layout to the cairo backend
|
||||
Clay_Cairo_Render(commands);
|
||||
Clay_Cairo_Render(commands, fonts);
|
||||
|
||||
// To keep this example short, we will not emit a second page in the PDF.
|
||||
// But to do so, you have to
|
||||
|
@ -5,7 +5,5 @@ set(CMAKE_C_STANDARD 99)
|
||||
|
||||
add_executable(clay_official_website main.c)
|
||||
|
||||
target_compile_options(clay_official_website PUBLIC -Wall -Werror -Wno-unknown-pragmas -Wno-error=missing-braces)
|
||||
target_compile_options(clay_official_website PUBLIC)
|
||||
target_include_directories(clay_official_website PUBLIC .)
|
||||
|
||||
set(CMAKE_C_FLAGS_RELEASE "-O3")
|
@ -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,54 @@
|
||||
{name: 'bottomLeft', type: 'float'},
|
||||
{name: 'bottomRight', type: 'float'},
|
||||
]};
|
||||
let rectangleConfigDefinition = { name: 'rectangle', type: 'struct', members: [
|
||||
{ name: 'color', ...colorDefinition },
|
||||
{ name: 'cornerRadius', ...cornerRadiusDefinition },
|
||||
{ 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 },
|
||||
{ name: 'cornerRadius', ...cornerRadiusDefinition }
|
||||
]};
|
||||
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: '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 customHTMLDataDefinition = { type: 'struct', members: [
|
||||
{ name: 'link', ...stringDefinition },
|
||||
{ name: 'cursorPointer', type: 'uint8_t' },
|
||||
{ name: 'disablePointerEvents', type: 'uint8_t' },
|
||||
]};
|
||||
let customConfigDefinition = { name: 'custom', type: 'struct', members: [
|
||||
{ name: 'customData', type: 'uint32_t' },
|
||||
]}
|
||||
let renderCommandDefinition = {
|
||||
name: 'CLay_RenderCommand',
|
||||
type: 'struct',
|
||||
@ -159,11 +176,19 @@
|
||||
{ name: 'width', type: 'float' },
|
||||
{ name: 'height', type: 'float' },
|
||||
]},
|
||||
{ name: 'config', type: 'uint32_t'},
|
||||
{ name: 'text', ...stringSliceDefinition },
|
||||
{ name: 'zIndex', type: 'int32_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: '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' },
|
||||
]
|
||||
};
|
||||
|
||||
@ -186,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: {
|
||||
@ -215,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: {
|
||||
@ -336,7 +363,7 @@
|
||||
memoryDataView.setFloat32(addressOfOffset, -container.scrollLeft, true);
|
||||
memoryDataView.setFloat32(addressOfOffset + 4, -container.scrollTop, true);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
const { instance } = await WebAssembly.instantiateStreaming(
|
||||
@ -344,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();
|
||||
}
|
||||
@ -364,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);
|
||||
@ -371,18 +416,18 @@
|
||||
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;
|
||||
let isMultiConfigElement = previousId === renderCommand.id.value;
|
||||
if (!elementCache[renderCommand.id.value]) {
|
||||
let elementType = 'div';
|
||||
switch (renderCommand.commandType.value) {
|
||||
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: {
|
||||
@ -438,63 +483,54 @@
|
||||
break;
|
||||
}
|
||||
case (CLAY_RENDER_COMMAND_TYPE_RECTANGLE): {
|
||||
let config = readStructAtAddress(renderCommand.config.value, rectangleConfigDefinition);
|
||||
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;
|
||||
let config = renderCommand.renderData.rectangle;
|
||||
let configMemory = JSON.stringify(config);
|
||||
if (configMemory === elementData.previousMemoryConfig) {
|
||||
break;
|
||||
}
|
||||
|
||||
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 (!dirty && !MemoryIsDifferent(configMemory, elementData.previousMemoryConfig, config.__size)) {
|
||||
break;
|
||||
}
|
||||
if (linkContents.length > 0) {
|
||||
element.href = linkContents;
|
||||
}
|
||||
|
||||
if (linkContents.length > 0 || config.cursorPointer.value) {
|
||||
if (linkContents.length > 0 || customData.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 (config.cornerRadius.topLeft.value > 0) {
|
||||
element.style.borderTopLeftRadius = config.cornerRadius.topLeft.value + 'px';
|
||||
}
|
||||
if (config.cornerRadius.topRight.value > 0) {
|
||||
element.style.borderTopRightRadius = config.cornerRadius.topRight.value + 'px';
|
||||
}
|
||||
if (config.cornerRadius.bottomLeft.value > 0) {
|
||||
element.style.borderBottomLeftRadius = config.cornerRadius.bottomLeft.value + 'px';
|
||||
}
|
||||
if (config.cornerRadius.bottomRight.value > 0) {
|
||||
element.style.borderBottomRightRadius = config.cornerRadius.bottomRight.value + 'px';
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case (CLAY_RENDER_COMMAND_TYPE_BORDER): {
|
||||
let config = readStructAtAddress(renderCommand.config.value, borderConfigDefinition);
|
||||
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 (config.cornerRadius.topLeft.value > 0) {
|
||||
element.style.borderTopLeftRadius = config.cornerRadius.topLeft.value + 'px';
|
||||
@ -511,18 +547,18 @@
|
||||
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.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)) {
|
||||
@ -533,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';
|
||||
@ -542,6 +582,7 @@
|
||||
element.style.overflowY = 'scroll';
|
||||
element.style.pointerEvents = 'auto';
|
||||
}
|
||||
elementData.previousMemoryConfig = configMemory;
|
||||
break;
|
||||
}
|
||||
case (CLAY_RENDER_COMMAND_TYPE_SCISSOR_END): {
|
||||
@ -549,8 +590,9 @@
|
||||
break;
|
||||
}
|
||||
case (CLAY_RENDER_COMMAND_TYPE_IMAGE): {
|
||||
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);
|
||||
}
|
||||
@ -558,6 +600,9 @@
|
||||
break;
|
||||
}
|
||||
case (CLAY_RENDER_COMMAND_TYPE_CUSTOM): break;
|
||||
default: {
|
||||
console.log("Error: unhandled render command");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -594,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(
|
||||
@ -607,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;
|
||||
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);
|
||||
@ -642,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})`;
|
||||
@ -664,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;
|
||||
@ -674,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})`;
|
||||
@ -686,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;
|
||||
@ -696,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})`;
|
||||
@ -710,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]}`;
|
||||
@ -734,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(),
|
||||
|
Binary file not shown.
@ -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,54 @@
|
||||
{name: 'bottomLeft', type: 'float'},
|
||||
{name: 'bottomRight', type: 'float'},
|
||||
]};
|
||||
let rectangleConfigDefinition = { name: 'rectangle', type: 'struct', members: [
|
||||
{ name: 'color', ...colorDefinition },
|
||||
{ name: 'cornerRadius', ...cornerRadiusDefinition },
|
||||
{ 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 },
|
||||
{ name: 'cornerRadius', ...cornerRadiusDefinition }
|
||||
]};
|
||||
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: '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 customHTMLDataDefinition = { type: 'struct', members: [
|
||||
{ name: 'link', ...stringDefinition },
|
||||
{ name: 'cursorPointer', type: 'uint8_t' },
|
||||
{ name: 'disablePointerEvents', type: 'uint8_t' },
|
||||
]};
|
||||
let customConfigDefinition = { name: 'custom', type: 'struct', members: [
|
||||
{ name: 'customData', type: 'uint32_t' },
|
||||
]}
|
||||
let renderCommandDefinition = {
|
||||
name: 'CLay_RenderCommand',
|
||||
type: 'struct',
|
||||
@ -159,11 +176,19 @@
|
||||
{ name: 'width', type: 'float' },
|
||||
{ name: 'height', type: 'float' },
|
||||
]},
|
||||
{ name: 'config', type: 'uint32_t'},
|
||||
{ name: 'text', ...stringSliceDefinition },
|
||||
{ name: 'zIndex', type: 'int32_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: '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' },
|
||||
]
|
||||
};
|
||||
|
||||
@ -186,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: {
|
||||
@ -215,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: {
|
||||
@ -336,7 +363,7 @@
|
||||
memoryDataView.setFloat32(addressOfOffset, -container.scrollLeft, true);
|
||||
memoryDataView.setFloat32(addressOfOffset + 4, -container.scrollTop, true);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
const { instance } = await WebAssembly.instantiateStreaming(
|
||||
@ -344,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();
|
||||
}
|
||||
@ -364,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);
|
||||
@ -371,18 +416,18 @@
|
||||
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;
|
||||
let isMultiConfigElement = previousId === renderCommand.id.value;
|
||||
if (!elementCache[renderCommand.id.value]) {
|
||||
let elementType = 'div';
|
||||
switch (renderCommand.commandType.value) {
|
||||
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: {
|
||||
@ -438,63 +483,54 @@
|
||||
break;
|
||||
}
|
||||
case (CLAY_RENDER_COMMAND_TYPE_RECTANGLE): {
|
||||
let config = readStructAtAddress(renderCommand.config.value, rectangleConfigDefinition);
|
||||
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;
|
||||
let config = renderCommand.renderData.rectangle;
|
||||
let configMemory = JSON.stringify(config);
|
||||
if (configMemory === elementData.previousMemoryConfig) {
|
||||
break;
|
||||
}
|
||||
|
||||
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 (!dirty && !MemoryIsDifferent(configMemory, elementData.previousMemoryConfig, config.__size)) {
|
||||
break;
|
||||
}
|
||||
if (linkContents.length > 0) {
|
||||
element.href = linkContents;
|
||||
}
|
||||
|
||||
if (linkContents.length > 0 || config.cursorPointer.value) {
|
||||
if (linkContents.length > 0 || customData.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 (config.cornerRadius.topLeft.value > 0) {
|
||||
element.style.borderTopLeftRadius = config.cornerRadius.topLeft.value + 'px';
|
||||
}
|
||||
if (config.cornerRadius.topRight.value > 0) {
|
||||
element.style.borderTopRightRadius = config.cornerRadius.topRight.value + 'px';
|
||||
}
|
||||
if (config.cornerRadius.bottomLeft.value > 0) {
|
||||
element.style.borderBottomLeftRadius = config.cornerRadius.bottomLeft.value + 'px';
|
||||
}
|
||||
if (config.cornerRadius.bottomRight.value > 0) {
|
||||
element.style.borderBottomRightRadius = config.cornerRadius.bottomRight.value + 'px';
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case (CLAY_RENDER_COMMAND_TYPE_BORDER): {
|
||||
let config = readStructAtAddress(renderCommand.config.value, borderConfigDefinition);
|
||||
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 (config.cornerRadius.topLeft.value > 0) {
|
||||
element.style.borderTopLeftRadius = config.cornerRadius.topLeft.value + 'px';
|
||||
@ -511,18 +547,18 @@
|
||||
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.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)) {
|
||||
@ -533,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';
|
||||
@ -542,6 +582,7 @@
|
||||
element.style.overflowY = 'scroll';
|
||||
element.style.pointerEvents = 'auto';
|
||||
}
|
||||
elementData.previousMemoryConfig = configMemory;
|
||||
break;
|
||||
}
|
||||
case (CLAY_RENDER_COMMAND_TYPE_SCISSOR_END): {
|
||||
@ -549,8 +590,9 @@
|
||||
break;
|
||||
}
|
||||
case (CLAY_RENDER_COMMAND_TYPE_IMAGE): {
|
||||
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);
|
||||
}
|
||||
@ -558,6 +600,9 @@
|
||||
break;
|
||||
}
|
||||
case (CLAY_RENDER_COMMAND_TYPE_CUSTOM): break;
|
||||
default: {
|
||||
console.log("Error: unhandled render command");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -594,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(
|
||||
@ -607,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;
|
||||
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);
|
||||
@ -642,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})`;
|
||||
@ -664,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;
|
||||
@ -674,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})`;
|
||||
@ -686,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;
|
||||
@ -696,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})`;
|
||||
@ -710,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]}`;
|
||||
@ -734,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(),
|
||||
|
@ -1,6 +1,3 @@
|
||||
#define CLAY_EXTEND_CONFIG_RECTANGLE Clay_String link; bool cursorPointer;
|
||||
#define CLAY_EXTEND_CONFIG_IMAGE Clay_String sourceURL;
|
||||
#define CLAY_EXTEND_CONFIG_TEXT bool disablePointerEvents;
|
||||
#define CLAY_IMPLEMENTATION
|
||||
#include "../../clay.h"
|
||||
|
||||
@ -40,22 +37,49 @@ const Clay_Color COLOR_BLOB_BORDER_5 = (Clay_Color) {240, 189, 100, 255};
|
||||
Clay_TextElementConfig headerTextConfig = (Clay_TextElementConfig) { .fontId = 2, .fontSize = 24, .textColor = {61, 26, 5, 255} };
|
||||
Clay_TextElementConfig blobTextConfig = (Clay_TextElementConfig) { .fontId = 2, .fontSize = 30, .textColor = {244, 235, 230, 255} };
|
||||
|
||||
typedef struct {
|
||||
void* memory;
|
||||
uintptr_t offset;
|
||||
} Arena;
|
||||
|
||||
Arena frameArena = {};
|
||||
|
||||
typedef struct {
|
||||
Clay_String link;
|
||||
bool cursorPointer;
|
||||
bool disablePointerEvents;
|
||||
} CustomHTMLData;
|
||||
|
||||
CustomHTMLData* FrameAllocateCustomData(CustomHTMLData data) {
|
||||
CustomHTMLData *customData = (CustomHTMLData *)(frameArena.memory + frameArena.offset);
|
||||
*customData = data;
|
||||
frameArena.offset += sizeof(CustomHTMLData);
|
||||
return customData;
|
||||
}
|
||||
|
||||
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(CLAY_IDI("HeroBlob", index), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW(.max = 480) }, .padding = CLAY_PADDING_ALL(16), .childGap = 16, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER} }), CLAY_BORDER_OUTSIDE_RADIUS(2, color, 10)) {
|
||||
CLAY(CLAY_IDI("CheckImage", index), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_FIXED(32) } }), CLAY_IMAGE({ .sourceDimensions = { 128, 128 }, .sourceURL = 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 = FrameAllocateString(imageURL) } }) {}
|
||||
CLAY_TEXT(text, CLAY_TEXT_CONFIG({ .fontSize = fontSize, .fontId = FONT_ID_BODY_24, .textColor = color }));
|
||||
}
|
||||
}
|
||||
|
||||
void LandingPageDesktop() {
|
||||
CLAY(CLAY_ID("LandingPage1Desktop"), CLAY_LAYOUT({ .sizing = { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIT(.min = windowHeight - 70) }, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER}, .padding = { 50, 50 } })) {
|
||||
CLAY(CLAY_ID("LandingPage1"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0) }, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER}, .padding = CLAY_PADDING_ALL(32), .childGap = 32 }), CLAY_BORDER({ .left = { 2, COLOR_RED }, .right = { 2, COLOR_RED } })) {
|
||||
CLAY(CLAY_ID("LeftText"), CLAY_LAYOUT({ .sizing = { .width = CLAY_SIZING_PERCENT(0.55f) }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8 })) {
|
||||
CLAY({ .id = CLAY_ID("LandingPage1Desktop"), .layout = { .sizing = { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIT(.min = windowHeight - 70) }, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER}, .padding = { 50, 50 } } }) {
|
||||
CLAY({ .id = CLAY_ID("LandingPage1"), .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0) }, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER}, .padding = CLAY_PADDING_ALL(32), .childGap = 32 }, .border = { .width = { .left = 2, .right = 2 }, .color = COLOR_RED } }) {
|
||||
CLAY({ .id = CLAY_ID("LeftText"), .layout = { .sizing = { .width = CLAY_SIZING_PERCENT(0.55f) }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8 } }) {
|
||||
CLAY_TEXT(CLAY_STRING("Clay is a flex-box style UI auto layout library in C, with declarative syntax and microsecond performance."), CLAY_TEXT_CONFIG({ .fontSize = 56, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_RED }));
|
||||
CLAY(CLAY_ID("LandingPageSpacer"), CLAY_LAYOUT({ .sizing = { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED(32) } })) {}
|
||||
CLAY({ .id = CLAY_ID("LandingPageSpacer"), .layout = { .sizing = { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED(32) } } }) {}
|
||||
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(CLAY_ID("HeroImageOuter"), CLAY_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_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"));
|
||||
@ -67,13 +91,13 @@ void LandingPageDesktop() {
|
||||
}
|
||||
|
||||
void LandingPageMobile() {
|
||||
CLAY(CLAY_ID("LandingPage1Mobile"), CLAY_LAYOUT({ .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIT(.min = windowHeight - 70) }, .childAlignment = {CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER}, .padding = { 16, 16, 32, 32 }, .childGap = 32 })) {
|
||||
CLAY(CLAY_ID("LeftText"), CLAY_LAYOUT({ .sizing = { .width = CLAY_SIZING_GROW(0) }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8 })) {
|
||||
CLAY({ .id = CLAY_ID("LandingPage1Mobile"), .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIT(.min = windowHeight - 70) }, .childAlignment = {CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER}, .padding = { 16, 16, 32, 32 }, .childGap = 32 } }) {
|
||||
CLAY({ .id = CLAY_ID("LeftText"), .layout = { .sizing = { .width = CLAY_SIZING_GROW(0) }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8 } }) {
|
||||
CLAY_TEXT(CLAY_STRING("Clay is a flex-box style UI auto layout library in C, with declarative syntax and microsecond performance."), CLAY_TEXT_CONFIG({ .fontSize = 48, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_RED }));
|
||||
CLAY(CLAY_ID("LandingPageSpacer"), CLAY_LAYOUT({ .sizing = { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED(32) } })) {}
|
||||
CLAY({ .id = CLAY_ID("LandingPageSpacer"), .layout = { .sizing = { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED(32) } } }) {}
|
||||
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(CLAY_ID("HeroImageOuter"), CLAY_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_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"));
|
||||
@ -84,17 +108,17 @@ void LandingPageMobile() {
|
||||
}
|
||||
|
||||
void FeatureBlocksDesktop() {
|
||||
CLAY(CLAY_ID("FeatureBlocksOuter"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW(0) } })) {
|
||||
CLAY(CLAY_ID("FeatureBlocksInner"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW(0) }, .childAlignment = { .y = CLAY_ALIGN_Y_CENTER } }), CLAY_BORDER({ .betweenChildren = { .width = 2, .color = COLOR_RED } })) {
|
||||
CLAY({ .id = CLAY_ID("FeatureBlocksOuter"), .layout = { .sizing = { CLAY_SIZING_GROW(0) } } }) {
|
||||
CLAY({ .id = CLAY_ID("FeatureBlocksInner"), .layout = { .sizing = { CLAY_SIZING_GROW(0) }, .childAlignment = { .y = CLAY_ALIGN_Y_CENTER } }, .border = { .width = { .betweenChildren = 2 }, .color = COLOR_RED } }) {
|
||||
Clay_TextElementConfig *textConfig = CLAY_TEXT_CONFIG({ .fontSize = 24, .fontId = FONT_ID_BODY_24, .textColor = COLOR_RED });
|
||||
CLAY(CLAY_ID("HFileBoxOuter"), CLAY_LAYOUT({ .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { CLAY_SIZING_PERCENT(0.5f) }, .childAlignment = {0, CLAY_ALIGN_Y_CENTER}, .padding = {50, 50, 32, 32}, .childGap = 8 })) {
|
||||
CLAY(CLAY_ID("HFileIncludeOuter"), CLAY_LAYOUT({ .padding = {8, 4} }), CLAY_RECTANGLE({ .color = COLOR_RED, .cornerRadius = CLAY_CORNER_RADIUS(8) })) {
|
||||
CLAY({ .id = CLAY_ID("HFileBoxOuter"), .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { CLAY_SIZING_PERCENT(0.5f) }, .childAlignment = {0, CLAY_ALIGN_Y_CENTER}, .padding = {50, 50, 32, 32}, .childGap = 8 } }) {
|
||||
CLAY({ .id = CLAY_ID("HFileIncludeOuter"), .layout = { .padding = {8, 4} }, .backgroundColor = COLOR_RED, .cornerRadius = CLAY_CORNER_RADIUS(8) }) {
|
||||
CLAY_TEXT(CLAY_STRING("#include clay.h"), CLAY_TEXT_CONFIG({ .fontSize = 24, .fontId = FONT_ID_BODY_24, .textColor = COLOR_LIGHT }));
|
||||
}
|
||||
CLAY_TEXT(CLAY_STRING("~2000 lines of C99."), textConfig);
|
||||
CLAY_TEXT(CLAY_STRING("Zero dependencies, including no C standard library."), textConfig);
|
||||
}
|
||||
CLAY(CLAY_ID("BringYourOwnRendererOuter"), CLAY_LAYOUT({ .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { CLAY_SIZING_PERCENT(0.5f) }, .childAlignment = {0, CLAY_ALIGN_Y_CENTER}, .padding = {50, 50, 32, 32}, .childGap = 8 })) {
|
||||
CLAY({ .id = CLAY_ID("BringYourOwnRendererOuter"), .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { CLAY_SIZING_PERCENT(0.5f) }, .childAlignment = {0, CLAY_ALIGN_Y_CENTER}, .padding = {50, 50, 32, 32}, .childGap = 8 } }) {
|
||||
CLAY_TEXT(CLAY_STRING("Renderer agnostic."), CLAY_TEXT_CONFIG({ .fontId = FONT_ID_BODY_24, .fontSize = 24, .textColor = COLOR_ORANGE }));
|
||||
CLAY_TEXT(CLAY_STRING("Layout with clay, then render with Raylib, WebGL Canvas or even as HTML."), textConfig);
|
||||
CLAY_TEXT(CLAY_STRING("Flexible output for easy compositing in your custom engine or environment."), textConfig);
|
||||
@ -104,16 +128,16 @@ void FeatureBlocksDesktop() {
|
||||
}
|
||||
|
||||
void FeatureBlocksMobile() {
|
||||
CLAY(CLAY_ID("FeatureBlocksInner"), CLAY_LAYOUT({ .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { CLAY_SIZING_GROW(0) } }), CLAY_BORDER({ .betweenChildren = { .width = 2, .color = COLOR_RED } })) {
|
||||
CLAY({ .id = CLAY_ID("FeatureBlocksInner"), .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { CLAY_SIZING_GROW(0) } }, .border = { .width = { .betweenChildren = 2 }, .color = COLOR_RED } }) {
|
||||
Clay_TextElementConfig *textConfig = CLAY_TEXT_CONFIG({ .fontSize = 24, .fontId = FONT_ID_BODY_24, .textColor = COLOR_RED });
|
||||
CLAY(CLAY_ID("HFileBoxOuter"), CLAY_LAYOUT({ .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { CLAY_SIZING_GROW(0) }, .childAlignment = {0, CLAY_ALIGN_Y_CENTER}, .padding = {16, 16, 32, 32}, .childGap = 8 })) {
|
||||
CLAY(CLAY_ID("HFileIncludeOuter"), CLAY_LAYOUT({ .padding = {8, 4} }), CLAY_RECTANGLE({ .color = COLOR_RED, .cornerRadius = CLAY_CORNER_RADIUS(8) })) {
|
||||
CLAY({ .id = CLAY_ID("HFileBoxOuter"), .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { CLAY_SIZING_GROW(0) }, .childAlignment = {0, CLAY_ALIGN_Y_CENTER}, .padding = {16, 16, 32, 32}, .childGap = 8 } }) {
|
||||
CLAY({ .id = CLAY_ID("HFileIncludeOuter"), .layout = { .padding = {8, 4} }, .backgroundColor = COLOR_RED, .cornerRadius = CLAY_CORNER_RADIUS(8) }) {
|
||||
CLAY_TEXT(CLAY_STRING("#include clay.h"), CLAY_TEXT_CONFIG({ .fontSize = 24, .fontId = FONT_ID_BODY_24, .textColor = COLOR_LIGHT }));
|
||||
}
|
||||
CLAY_TEXT(CLAY_STRING("~2000 lines of C99."), textConfig);
|
||||
CLAY_TEXT(CLAY_STRING("Zero dependencies, including no C standard library."), textConfig);
|
||||
}
|
||||
CLAY(CLAY_ID("BringYourOwnRendererOuter"), CLAY_LAYOUT({ .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { CLAY_SIZING_GROW(0) }, .childAlignment = {0, CLAY_ALIGN_Y_CENTER}, .padding = {16, 16, 32, 32}, .childGap = 8 })) {
|
||||
CLAY({ .id = CLAY_ID("BringYourOwnRendererOuter"), .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { CLAY_SIZING_GROW(0) }, .childAlignment = {0, CLAY_ALIGN_Y_CENTER}, .padding = {16, 16, 32, 32}, .childGap = 8 } }) {
|
||||
CLAY_TEXT(CLAY_STRING("Renderer agnostic."), CLAY_TEXT_CONFIG({ .fontId = FONT_ID_BODY_24, .fontSize = 24, .textColor = COLOR_ORANGE }));
|
||||
CLAY_TEXT(CLAY_STRING("Layout with clay, then render with Raylib, WebGL Canvas or even as HTML."), textConfig);
|
||||
CLAY_TEXT(CLAY_STRING("Flexible output for easy compositing in your custom engine or environment."), textConfig);
|
||||
@ -122,33 +146,33 @@ void FeatureBlocksMobile() {
|
||||
}
|
||||
|
||||
void DeclarativeSyntaxPageDesktop() {
|
||||
CLAY(CLAY_ID("SyntaxPageDesktop"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIT(.min = windowHeight - 50) }, .childAlignment = {0, CLAY_ALIGN_Y_CENTER}, .padding = { 50, 50 } })) {
|
||||
CLAY(CLAY_ID("SyntaxPage"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0) }, .childAlignment = { 0, CLAY_ALIGN_Y_CENTER }, .padding = CLAY_PADDING_ALL(32), .childGap = 32 }), CLAY_BORDER({ .left = { 2, COLOR_RED }, .right = { 2, COLOR_RED } })) {
|
||||
CLAY(CLAY_ID("SyntaxPageLeftText"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_PERCENT(0.5) }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8 })) {
|
||||
CLAY({ .id = CLAY_ID("SyntaxPageDesktop"), .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIT(.min = windowHeight - 50) }, .childAlignment = {0, CLAY_ALIGN_Y_CENTER}, .padding = { 50, 50 } } }) {
|
||||
CLAY({ .id = CLAY_ID("SyntaxPage"), .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0) }, .childAlignment = { 0, CLAY_ALIGN_Y_CENTER }, .padding = CLAY_PADDING_ALL(32), .childGap = 32 }, .border = { .width = { .left = 2, .right = 2 }, .color = COLOR_RED }}) {
|
||||
CLAY({ .id = CLAY_ID("SyntaxPageLeftText"), .layout = { .sizing = { CLAY_SIZING_PERCENT(0.5) }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8 } }) {
|
||||
CLAY_TEXT(CLAY_STRING("Declarative Syntax"), CLAY_TEXT_CONFIG({ .fontSize = 52, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_RED }));
|
||||
CLAY(CLAY_ID("SyntaxSpacer"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW(.max = 16) } })) {}
|
||||
CLAY({ .id = CLAY_ID("SyntaxSpacer"), .layout = { .sizing = { CLAY_SIZING_GROW(.max = 16) } } }) {}
|
||||
CLAY_TEXT(CLAY_STRING("Flexible and readable declarative syntax with nested UI element hierarchies."), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_RED }));
|
||||
CLAY_TEXT(CLAY_STRING("Mix elements with standard C code like loops, conditionals and functions."), 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(CLAY_ID("SyntaxPageRightImage"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_PERCENT(0.50) }, .childAlignment = {.x = CLAY_ALIGN_X_CENTER} })) {
|
||||
CLAY(CLAY_ID("SyntaxPageRightImageInner"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW(.max = 568) } }), CLAY_IMAGE({ .sourceDimensions = {1136, 1194}, .sourceURL = CLAY_STRING("/clay/images/declarative.png") })) {}
|
||||
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 = FrameAllocateString(CLAY_STRING("/clay/images/declarative.png")) } }) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DeclarativeSyntaxPageMobile() {
|
||||
CLAY(CLAY_ID("SyntaxPageDesktop"), CLAY_LAYOUT({ .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIT(.min = windowHeight - 50) }, .childAlignment = {CLAY_ALIGN_X_CENTER, CLAY_ALIGN_Y_CENTER}, .padding = {16, 16, 32, 32}, .childGap = 16 })) {
|
||||
CLAY(CLAY_ID("SyntaxPageLeftText"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW(0) }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8 })) {
|
||||
CLAY({ .id = CLAY_ID("SyntaxPageDesktop"), .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIT(.min = windowHeight - 50) }, .childAlignment = {CLAY_ALIGN_X_CENTER, CLAY_ALIGN_Y_CENTER}, .padding = {16, 16, 32, 32}, .childGap = 16 } }) {
|
||||
CLAY({ .id = CLAY_ID("SyntaxPageLeftText"), .layout = { .sizing = { CLAY_SIZING_GROW(0) }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8 } }) {
|
||||
CLAY_TEXT(CLAY_STRING("Declarative Syntax"), CLAY_TEXT_CONFIG({ .fontSize = 48, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_RED }));
|
||||
CLAY(CLAY_ID("SyntaxSpacer"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW(.max = 16) } })) {}
|
||||
CLAY({ .id = CLAY_ID("SyntaxSpacer"), .layout = { .sizing = { CLAY_SIZING_GROW(.max = 16) } } }) {}
|
||||
CLAY_TEXT(CLAY_STRING("Flexible and readable declarative syntax with nested UI element hierarchies."), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_RED }));
|
||||
CLAY_TEXT(CLAY_STRING("Mix elements with standard C code like loops, conditionals and functions."), 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(CLAY_ID("SyntaxPageRightImage"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW(0) }, .childAlignment = {.x = CLAY_ALIGN_X_CENTER} })) {
|
||||
CLAY(CLAY_ID("SyntaxPageRightImageInner"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW(.max = 568) } }), CLAY_IMAGE({ .sourceDimensions = {1136, 1194}, .sourceURL = CLAY_STRING("/clay/images/declarative.png") } )) {}
|
||||
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 = FrameAllocateString(CLAY_STRING("/clay/images/declarative.png")) } }) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -165,20 +189,20 @@ Clay_Color ColorLerp(Clay_Color a, Clay_Color b, float amount) {
|
||||
Clay_String LOREM_IPSUM_TEXT = CLAY_STRING("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.");
|
||||
|
||||
void HighPerformancePageDesktop(float lerpValue) {
|
||||
CLAY(CLAY_ID("PerformanceOuter"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIT(.min = windowHeight - 50) }, .childAlignment = {0, CLAY_ALIGN_Y_CENTER}, .padding = {82, 82, 32, 32}, .childGap = 64 }), CLAY_RECTANGLE({ .color = COLOR_RED })) {
|
||||
CLAY(CLAY_ID("PerformanceLeftText"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_PERCENT(0.5) }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8 })) {
|
||||
CLAY({ .id = CLAY_ID("PerformanceOuter"), .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIT(.min = windowHeight - 50) }, .childAlignment = {0, CLAY_ALIGN_Y_CENTER}, .padding = {82, 82, 32, 32}, .childGap = 64 }, .backgroundColor = COLOR_RED }) {
|
||||
CLAY({ .id = CLAY_ID("PerformanceLeftText"), .layout = { .sizing = { CLAY_SIZING_PERCENT(0.5) }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8 } }) {
|
||||
CLAY_TEXT(CLAY_STRING("High Performance"), CLAY_TEXT_CONFIG({ .fontSize = 52, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_LIGHT }));
|
||||
CLAY(CLAY_ID("PerformanceSpacer"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW(.max = 16) }})) {}
|
||||
CLAY({ .id = CLAY_ID("PerformanceSpacer"), .layout = { .sizing = { CLAY_SIZING_GROW(.max = 16) }} }) {}
|
||||
CLAY_TEXT(CLAY_STRING("Fast enough to recompute your entire UI every frame."), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_LIGHT }));
|
||||
CLAY_TEXT(CLAY_STRING("Small memory footprint (3.5mb default) with static allocation & reuse. No malloc / free."), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_LIGHT }));
|
||||
CLAY_TEXT(CLAY_STRING("Simplify animations and reactive UI design by avoiding the standard performance hacks."), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_LIGHT }));
|
||||
}
|
||||
CLAY(CLAY_ID("PerformanceRightImageOuter"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_PERCENT(0.50) }, .childAlignment = {CLAY_ALIGN_X_CENTER} })) {
|
||||
CLAY(CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIXED(400) } }), CLAY_BORDER_ALL({ .width = 2, .color = COLOR_LIGHT })) {
|
||||
CLAY(CLAY_ID("AnimationDemoContainerLeft"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_PERCENT(0.3f + 0.4f * lerpValue), CLAY_SIZING_GROW(0) }, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER}, .padding = CLAY_PADDING_ALL(32) }), CLAY_RECTANGLE({ .color = ColorLerp(COLOR_RED, COLOR_ORANGE, lerpValue) })) {
|
||||
CLAY({ .id = CLAY_ID("PerformanceRightImageOuter"), .layout = { .sizing = { CLAY_SIZING_PERCENT(0.50) }, .childAlignment = {CLAY_ALIGN_X_CENTER} } }) {
|
||||
CLAY({ .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIXED(400) } }, .border = { .width = {2, 2, 2, 2}, .color = COLOR_LIGHT } }) {
|
||||
CLAY({ .id = CLAY_ID("AnimationDemoContainerLeft"), .layout = { .sizing = { CLAY_SIZING_PERCENT(0.3f + 0.4f * lerpValue), CLAY_SIZING_GROW(0) }, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER}, .padding = CLAY_PADDING_ALL(32) }, .backgroundColor = ColorLerp(COLOR_RED, COLOR_ORANGE, lerpValue) }) {
|
||||
CLAY_TEXT(LOREM_IPSUM_TEXT, CLAY_TEXT_CONFIG({ .fontSize = 24, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_LIGHT }));
|
||||
}
|
||||
CLAY(CLAY_ID("AnimationDemoContainerRight"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0) }, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER}, .padding = CLAY_PADDING_ALL(32) }), CLAY_RECTANGLE({ .color = ColorLerp(COLOR_ORANGE, COLOR_RED, lerpValue) })) {
|
||||
CLAY({ .id = CLAY_ID("AnimationDemoContainerRight"), .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0) }, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER}, .padding = CLAY_PADDING_ALL(32) }, .backgroundColor = ColorLerp(COLOR_ORANGE, COLOR_RED, lerpValue) }) {
|
||||
CLAY_TEXT(LOREM_IPSUM_TEXT, CLAY_TEXT_CONFIG({ .fontSize = 24, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_LIGHT }));
|
||||
}
|
||||
}
|
||||
@ -187,20 +211,20 @@ void HighPerformancePageDesktop(float lerpValue) {
|
||||
}
|
||||
|
||||
void HighPerformancePageMobile(float lerpValue) {
|
||||
CLAY(CLAY_ID("PerformanceOuter"), CLAY_LAYOUT({ .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIT(.min = windowHeight - 50) }, .childAlignment = {CLAY_ALIGN_X_CENTER, CLAY_ALIGN_Y_CENTER}, .padding = {16, 16, 32, 32}, .childGap = 32 }), CLAY_RECTANGLE({ .color = COLOR_RED })) {
|
||||
CLAY(CLAY_ID("PerformanceLeftText"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW(0) }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8 })) {
|
||||
CLAY({ .id = CLAY_ID("PerformanceOuter"), .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIT(.min = windowHeight - 50) }, .childAlignment = {CLAY_ALIGN_X_CENTER, CLAY_ALIGN_Y_CENTER}, .padding = {16, 16, 32, 32}, .childGap = 32 }, .backgroundColor = COLOR_RED }) {
|
||||
CLAY({ .id = CLAY_ID("PerformanceLeftText"), .layout = { .sizing = { CLAY_SIZING_GROW(0) }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8 } }) {
|
||||
CLAY_TEXT(CLAY_STRING("High Performance"), CLAY_TEXT_CONFIG({ .fontSize = 48, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_LIGHT }));
|
||||
CLAY(CLAY_ID("PerformanceSpacer"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW(.max = 16) }})) {}
|
||||
CLAY({ .id = CLAY_ID("PerformanceSpacer"), .layout = { .sizing = { CLAY_SIZING_GROW(.max = 16) }} }) {}
|
||||
CLAY_TEXT(CLAY_STRING("Fast enough to recompute your entire UI every frame."), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_LIGHT }));
|
||||
CLAY_TEXT(CLAY_STRING("Small memory footprint (3.5mb default) with static allocation & reuse. No malloc / free."), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_LIGHT }));
|
||||
CLAY_TEXT(CLAY_STRING("Simplify animations and reactive UI design by avoiding the standard performance hacks."), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_LIGHT }));
|
||||
}
|
||||
CLAY(CLAY_ID("PerformanceRightImageOuter"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW(0) }, .childAlignment = {CLAY_ALIGN_X_CENTER} })) {
|
||||
CLAY(CLAY_ID(""), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIXED(400) } }), CLAY_BORDER_ALL({ .width = 2, .color = COLOR_LIGHT })) {
|
||||
CLAY(CLAY_ID("AnimationDemoContainerLeft"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_PERCENT(0.35f + 0.3f * lerpValue), CLAY_SIZING_GROW(0) }, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER}, .padding = CLAY_PADDING_ALL(16) }), CLAY_RECTANGLE({ .color = ColorLerp(COLOR_RED, COLOR_ORANGE, lerpValue) })) {
|
||||
CLAY({ .id = CLAY_ID("PerformanceRightImageOuter"), .layout = { .sizing = { CLAY_SIZING_GROW(0) }, .childAlignment = {CLAY_ALIGN_X_CENTER} } }) {
|
||||
CLAY({ .id = CLAY_ID(""), .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIXED(400) } }, .border = { .width = { 2, 2, 2, 2 }, .color = COLOR_LIGHT }}) {
|
||||
CLAY({ .id = CLAY_ID("AnimationDemoContainerLeft"), .layout = { .sizing = { CLAY_SIZING_PERCENT(0.35f + 0.3f * lerpValue), CLAY_SIZING_GROW(0) }, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER}, .padding = CLAY_PADDING_ALL(16) }, .backgroundColor = ColorLerp(COLOR_RED, COLOR_ORANGE, lerpValue) }) {
|
||||
CLAY_TEXT(LOREM_IPSUM_TEXT, CLAY_TEXT_CONFIG({ .fontSize = 24, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_LIGHT }));
|
||||
}
|
||||
CLAY(CLAY_ID("AnimationDemoContainerRight"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0) }, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER}, .padding = CLAY_PADDING_ALL(16) }), CLAY_RECTANGLE({ .color = ColorLerp(COLOR_ORANGE, COLOR_RED, lerpValue) })) {
|
||||
CLAY({ .id = CLAY_ID("AnimationDemoContainerRight"), .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0) }, .childAlignment = {.y = CLAY_ALIGN_Y_CENTER}, .padding = CLAY_PADDING_ALL(16) }, .backgroundColor = ColorLerp(COLOR_ORANGE, COLOR_RED, lerpValue) }) {
|
||||
CLAY_TEXT(LOREM_IPSUM_TEXT, CLAY_TEXT_CONFIG({ .fontSize = 24, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_LIGHT }));
|
||||
}
|
||||
}
|
||||
@ -217,36 +241,42 @@ void HandleRendererButtonInteraction(Clay_ElementId elementId, Clay_PointerData
|
||||
}
|
||||
|
||||
void RendererButtonActive(Clay_String text) {
|
||||
CLAY(CLAY_LAYOUT({ .sizing = {CLAY_SIZING_FIXED(300) }, .padding = CLAY_PADDING_ALL(16) }),
|
||||
CLAY_RECTANGLE({ .color = Clay_Hovered() ? COLOR_RED_HOVER : COLOR_RED, .cornerRadius = CLAY_CORNER_RADIUS(10) })
|
||||
) {
|
||||
CLAY_TEXT(text, CLAY_TEXT_CONFIG({ .disablePointerEvents = true, .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_LIGHT }));
|
||||
CLAY({
|
||||
.layout = { .sizing = {CLAY_SIZING_FIXED(300) }, .padding = CLAY_PADDING_ALL(16) },
|
||||
.backgroundColor = Clay_Hovered() ? COLOR_RED_HOVER : COLOR_RED,
|
||||
.cornerRadius = CLAY_CORNER_RADIUS(10),
|
||||
.userData = FrameAllocateCustomData((CustomHTMLData) { .disablePointerEvents = true, .cursorPointer = true })
|
||||
}) {
|
||||
CLAY_TEXT(text, CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_LIGHT }));
|
||||
}
|
||||
}
|
||||
|
||||
void RendererButtonInactive(Clay_String text, size_t rendererIndex) {
|
||||
CLAY(CLAY_LAYOUT({ .sizing = {CLAY_SIZING_FIXED(300)}, .padding = CLAY_PADDING_ALL(16) }),
|
||||
CLAY_BORDER_OUTSIDE_RADIUS(2, COLOR_RED, 10),
|
||||
CLAY_RECTANGLE({ .color = Clay_Hovered() ? COLOR_LIGHT_HOVER : COLOR_LIGHT, .cornerRadius = CLAY_CORNER_RADIUS(10), .cursorPointer = true }),
|
||||
Clay_OnHover(HandleRendererButtonInteraction, rendererIndex)
|
||||
) {
|
||||
CLAY_TEXT(text, CLAY_TEXT_CONFIG({ .disablePointerEvents = true, .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_RED }));
|
||||
CLAY({
|
||||
.layout = { .sizing = {CLAY_SIZING_FIXED(300)}, .padding = CLAY_PADDING_ALL(16) },
|
||||
.border = { .width = {2, 2, 2, 2}, .color = COLOR_RED },
|
||||
.backgroundColor = Clay_Hovered() ? COLOR_LIGHT_HOVER : COLOR_LIGHT,
|
||||
.cornerRadius = CLAY_CORNER_RADIUS(10),
|
||||
.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 }));
|
||||
}
|
||||
}
|
||||
|
||||
void RendererPageDesktop() {
|
||||
CLAY(CLAY_ID("RendererPageDesktop"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIT(.min = windowHeight - 50) }, .childAlignment = {0, CLAY_ALIGN_Y_CENTER}, .padding = { 50, 50 } })) {
|
||||
CLAY(CLAY_ID("RendererPage"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0) }, .childAlignment = { 0, CLAY_ALIGN_Y_CENTER }, .padding = CLAY_PADDING_ALL(32), .childGap = 32 }), CLAY_BORDER({ .left = { 2, COLOR_RED }, .right = { 2, COLOR_RED } })) {
|
||||
CLAY(CLAY_ID("RendererLeftText"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_PERCENT(0.5) }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8 })) {
|
||||
CLAY({ .id = CLAY_ID("RendererPageDesktop"), .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIT(.min = windowHeight - 50) }, .childAlignment = {0, CLAY_ALIGN_Y_CENTER}, .padding = { 50, 50 } } }) {
|
||||
CLAY({ .id = CLAY_ID("RendererPage"), .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0) }, .childAlignment = { 0, CLAY_ALIGN_Y_CENTER }, .padding = CLAY_PADDING_ALL(32), .childGap = 32 }, .border = { .width = { .left = 2, .right = 2 }, .color = COLOR_RED } }) {
|
||||
CLAY({ .id = CLAY_ID("RendererLeftText"), .layout = { .sizing = { CLAY_SIZING_PERCENT(0.5) }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8 } }) {
|
||||
CLAY_TEXT(CLAY_STRING("Renderer & Platform Agnostic"), CLAY_TEXT_CONFIG({ .fontSize = 52, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_RED }));
|
||||
CLAY(CLAY_ID("RendererSpacerLeft"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW(.max = 16) }})) {}
|
||||
CLAY({ .id = CLAY_ID("RendererSpacerLeft"), .layout = { .sizing = { CLAY_SIZING_GROW(.max = 16) }} }) {}
|
||||
CLAY_TEXT(CLAY_STRING("Clay outputs a sorted array of primitive render commands, such as RECTANGLE, TEXT or IMAGE."), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_RED }));
|
||||
CLAY_TEXT(CLAY_STRING("Write your own renderer in a few hundred lines of code, or use the provided examples for Raylib, WebGL canvas and more."), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_RED }));
|
||||
CLAY_TEXT(CLAY_STRING("There's even an HTML renderer - you're looking at it right now!"), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_RED }));
|
||||
}
|
||||
CLAY(CLAY_ID("RendererRightText"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_PERCENT(0.5) }, .childAlignment = {CLAY_ALIGN_X_CENTER}, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 16 })) {
|
||||
CLAY({ .id = CLAY_ID("RendererRightText"), .layout = { .sizing = { CLAY_SIZING_PERCENT(0.5) }, .childAlignment = {CLAY_ALIGN_X_CENTER}, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 16 } }) {
|
||||
CLAY_TEXT(CLAY_STRING("Try changing renderer!"), CLAY_TEXT_CONFIG({ .fontSize = 36, .fontId = FONT_ID_BODY_36, .textColor = COLOR_ORANGE }));
|
||||
CLAY(CLAY_ID("RendererSpacerRight"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW(.max = 32) } })) {}
|
||||
CLAY({ .id = CLAY_ID("RendererSpacerRight"), .layout = { .sizing = { CLAY_SIZING_GROW(.max = 32) } } }) {}
|
||||
if (ACTIVE_RENDERER_INDEX == 0) {
|
||||
RendererButtonActive(CLAY_STRING("HTML Renderer"));
|
||||
RendererButtonInactive(CLAY_STRING("Canvas Renderer"), 1);
|
||||
@ -260,17 +290,17 @@ void RendererPageDesktop() {
|
||||
}
|
||||
|
||||
void RendererPageMobile() {
|
||||
CLAY(CLAY_ID("RendererMobile"), CLAY_LAYOUT({ .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIT(.min = windowHeight - 50) }, .childAlignment = {.x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER}, .padding = { 16, 16, 32, 32}, .childGap = 32 }), CLAY_RECTANGLE({ .color = COLOR_LIGHT })) {
|
||||
CLAY(CLAY_ID("RendererLeftText"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW(0) }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8 })) {
|
||||
CLAY({ .id = CLAY_ID("RendererMobile"), .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIT(.min = windowHeight - 50) }, .childAlignment = {.x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER}, .padding = { 16, 16, 32, 32}, .childGap = 32 }, .backgroundColor = COLOR_LIGHT }) {
|
||||
CLAY({ .id = CLAY_ID("RendererLeftText"), .layout = { .sizing = { CLAY_SIZING_GROW(0) }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8 } }) {
|
||||
CLAY_TEXT(CLAY_STRING("Renderer & Platform Agnostic"), CLAY_TEXT_CONFIG({ .fontSize = 48, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_RED }));
|
||||
CLAY(CLAY_ID("RendererSpacerLeft"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW(.max = 16) }})) {}
|
||||
CLAY({ .id = CLAY_ID("RendererSpacerLeft"), .layout = { .sizing = { CLAY_SIZING_GROW(.max = 16) }} }) {}
|
||||
CLAY_TEXT(CLAY_STRING("Clay outputs a sorted array of primitive render commands, such as RECTANGLE, TEXT or IMAGE."), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_RED }));
|
||||
CLAY_TEXT(CLAY_STRING("Write your own renderer in a few hundred lines of code, or use the provided examples for Raylib, WebGL canvas and more."), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_RED }));
|
||||
CLAY_TEXT(CLAY_STRING("There's even an HTML renderer - you're looking at it right now!"), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_RED }));
|
||||
}
|
||||
CLAY(CLAY_ID("RendererRightText"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW(0) }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 16 })) {
|
||||
CLAY({ .id = CLAY_ID("RendererRightText"), .layout = { .sizing = { CLAY_SIZING_GROW(0) }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 16 } }) {
|
||||
CLAY_TEXT(CLAY_STRING("Try changing renderer!"), CLAY_TEXT_CONFIG({ .fontSize = 36, .fontId = FONT_ID_BODY_36, .textColor = COLOR_ORANGE }));
|
||||
CLAY(CLAY_ID("RendererSpacerRight"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW(.max = 32) }})) {}
|
||||
CLAY({ .id = CLAY_ID("RendererSpacerRight"), .layout = { .sizing = { CLAY_SIZING_GROW(.max = 32) }} }) {}
|
||||
if (ACTIVE_RENDERER_INDEX == 0) {
|
||||
RendererButtonActive(CLAY_STRING("HTML Renderer"));
|
||||
RendererButtonInactive(CLAY_STRING("Canvas Renderer"), 1);
|
||||
@ -283,17 +313,17 @@ void RendererPageMobile() {
|
||||
}
|
||||
|
||||
void DebuggerPageDesktop() {
|
||||
CLAY(CLAY_ID("DebuggerDesktop"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIT(.min = windowHeight - 50) }, .childAlignment = {0, CLAY_ALIGN_Y_CENTER}, .padding = { 82, 82, 32, 32 }, .childGap = 64 }), CLAY_RECTANGLE({ .color = COLOR_RED })) {
|
||||
CLAY(CLAY_ID("DebuggerLeftText"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_PERCENT(0.5) }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8 })) {
|
||||
CLAY({ .id = CLAY_ID("DebuggerDesktop"), .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIT(.min = windowHeight - 50) }, .childAlignment = {0, CLAY_ALIGN_Y_CENTER}, .padding = { 82, 82, 32, 32 }, .childGap = 64 }, .backgroundColor = COLOR_RED }) {
|
||||
CLAY({ .id = CLAY_ID("DebuggerLeftText"), .layout = { .sizing = { CLAY_SIZING_PERCENT(0.5) }, .layoutDirection = CLAY_TOP_TO_BOTTOM, .childGap = 8 } }) {
|
||||
CLAY_TEXT(CLAY_STRING("Integrated Debug Tools"), CLAY_TEXT_CONFIG({ .fontSize = 52, .fontId = FONT_ID_TITLE_56, .textColor = COLOR_LIGHT }));
|
||||
CLAY(CLAY_ID("DebuggerSpacer"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW(.max = 16) }})) {}
|
||||
CLAY({ .id = CLAY_ID("DebuggerSpacer"), .layout = { .sizing = { CLAY_SIZING_GROW(.max = 16) }} }) {}
|
||||
CLAY_TEXT(CLAY_STRING("Clay includes built in \"Chrome Inspector\"-style debug tooling."), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_LIGHT }));
|
||||
CLAY_TEXT(CLAY_STRING("View your layout hierarchy and config in real time."), CLAY_TEXT_CONFIG({ .fontSize = 28, .fontId = FONT_ID_BODY_36, .textColor = COLOR_LIGHT }));
|
||||
CLAY(CLAY_ID("DebuggerPageSpacer"), CLAY_LAYOUT({ .sizing = { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED(32) } })) {}
|
||||
CLAY({ .id = CLAY_ID("DebuggerPageSpacer"), .layout = { .sizing = { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_FIXED(32) } } }) {}
|
||||
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(CLAY_ID("DebuggerRightImageOuter"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_PERCENT(0.50) }, .childAlignment = {CLAY_ALIGN_X_CENTER} })) {
|
||||
CLAY(CLAY_ID("DebuggerPageRightImageInner"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW(.max = 558) } }), CLAY_IMAGE({ .sourceDimensions = {1620, 1474}, .sourceURL = CLAY_STRING("/clay/images/debugger.png") })) {}
|
||||
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 = FrameAllocateString(CLAY_STRING("/clay/images/debugger.png")) } }) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -310,45 +340,48 @@ float animationLerpValue = -1.0f;
|
||||
|
||||
Clay_RenderCommandArray CreateLayout(bool mobileScreen, float lerpValue) {
|
||||
Clay_BeginLayout();
|
||||
CLAY(CLAY_ID("OuterContainer"), CLAY_LAYOUT({ .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0) } }), CLAY_RECTANGLE({ .color = COLOR_LIGHT })) {
|
||||
CLAY(CLAY_ID("Header"), CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIXED(50) }, .childAlignment = { 0, CLAY_ALIGN_Y_CENTER }, .childGap = 16, .padding = { 32, 32 } })) {
|
||||
CLAY({ .id = CLAY_ID("OuterContainer"), .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0) } }, .backgroundColor = COLOR_LIGHT }) {
|
||||
CLAY({ .id = CLAY_ID("Header"), .layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIXED(50) }, .childAlignment = { 0, CLAY_ALIGN_Y_CENTER }, .childGap = 16, .padding = { 32, 32 } } }) {
|
||||
CLAY_TEXT(CLAY_STRING("Clay"), &headerTextConfig);
|
||||
CLAY(CLAY_ID("Spacer"), CLAY_LAYOUT({ .sizing = { .width = CLAY_SIZING_GROW(0) } })) {}
|
||||
CLAY({ .id = CLAY_ID("Spacer"), .layout = { .sizing = { .width = CLAY_SIZING_GROW(0) } } }) {}
|
||||
if (!mobileScreen) {
|
||||
CLAY(CLAY_ID("LinkExamplesOuter"), CLAY_LAYOUT({ .padding = {8, 8} }), CLAY_RECTANGLE({ .link = CLAY_STRING("https://github.com/nicbarker/clay/tree/main/examples"), .color = {0,0,0,0} })) {
|
||||
CLAY_TEXT(CLAY_STRING("Examples"), CLAY_TEXT_CONFIG({ .disablePointerEvents = true, .fontId = FONT_ID_BODY_24, .fontSize = 24, .textColor = {61, 26, 5, 255} }));
|
||||
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(CLAY_ID("LinkDocsOuter"), CLAY_LAYOUT({ .padding = {8, 8} }), CLAY_RECTANGLE({ .link = CLAY_STRING("https://github.com/nicbarker/clay/blob/main/README.md"), .color = {0,0,0,0} })) {
|
||||
CLAY_TEXT(CLAY_STRING("Docs"), CLAY_TEXT_CONFIG({ .disablePointerEvents = true, .fontId = FONT_ID_BODY_24, .fontSize = 24, .textColor = {61, 26, 5, 255} }));
|
||||
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(CLAY_LAYOUT({ .padding = {16, 16, 6, 6} }),
|
||||
CLAY_RECTANGLE({
|
||||
CLAY({
|
||||
.layout = { .padding = {16, 16, 6, 6} },
|
||||
.backgroundColor = Clay_Hovered() ? COLOR_LIGHT_HOVER : COLOR_LIGHT,
|
||||
.border = { .width = {2, 2, 2, 2}, .color = COLOR_RED },
|
||||
.cornerRadius = CLAY_CORNER_RADIUS(10),
|
||||
.link = CLAY_STRING("https://discord.gg/b4FTWkxdvT"),
|
||||
.color = Clay_Hovered() ? COLOR_LIGHT_HOVER : COLOR_LIGHT }),
|
||||
CLAY_BORDER_OUTSIDE_RADIUS(2, COLOR_RED, 10)
|
||||
) {
|
||||
CLAY_TEXT(CLAY_STRING("Discord"), CLAY_TEXT_CONFIG({ .disablePointerEvents = true, .fontId = FONT_ID_BODY_24, .fontSize = 24, .textColor = {61, 26, 5, 255} }));
|
||||
.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(CLAY_LAYOUT({ .padding = {16, 16, 6, 6} }),
|
||||
CLAY_RECTANGLE({ .cornerRadius = CLAY_CORNER_RADIUS(10), .link = CLAY_STRING("https://github.com/nicbarker/clay"), .color = Clay_Hovered() ? COLOR_LIGHT_HOVER : COLOR_LIGHT }),
|
||||
CLAY_BORDER_OUTSIDE_RADIUS(2, COLOR_RED, 10)
|
||||
) {
|
||||
CLAY_TEXT(CLAY_STRING("Github"), CLAY_TEXT_CONFIG({ .disablePointerEvents = true, .fontId = FONT_ID_BODY_24, .fontSize = 24, .textColor = {61, 26, 5, 255} }));
|
||||
CLAY({
|
||||
.layout = { .padding = {16, 16, 6, 6} },
|
||||
.backgroundColor = Clay_Hovered() ? COLOR_LIGHT_HOVER : COLOR_LIGHT,
|
||||
.border = { .width = {2, 2, 2, 2}, .color = COLOR_RED },
|
||||
.cornerRadius = CLAY_CORNER_RADIUS(10),
|
||||
.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_LayoutConfig topBorderConfig = (Clay_LayoutConfig) { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_FIXED(4) }};
|
||||
CLAY(CLAY_ID("TopBorder1"), CLAY_LAYOUT(topBorderConfig), CLAY_RECTANGLE({ .color = COLOR_TOP_BORDER_5 })) {}
|
||||
CLAY(CLAY_ID("TopBorder2"), CLAY_LAYOUT(topBorderConfig), CLAY_RECTANGLE({ .color = COLOR_TOP_BORDER_4 })) {}
|
||||
CLAY(CLAY_ID("TopBorder3"), CLAY_LAYOUT(topBorderConfig), CLAY_RECTANGLE({ .color = COLOR_TOP_BORDER_3 })) {}
|
||||
CLAY(CLAY_ID("TopBorder4"), CLAY_LAYOUT(topBorderConfig), CLAY_RECTANGLE({ .color = COLOR_TOP_BORDER_2 })) {}
|
||||
CLAY(CLAY_ID("TopBorder5"), CLAY_LAYOUT(topBorderConfig), CLAY_RECTANGLE({ .color = COLOR_TOP_BORDER_1 })) {}
|
||||
CLAY(CLAY_ID("OuterScrollContainer"),
|
||||
CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0) }, .layoutDirection = CLAY_TOP_TO_BOTTOM }),
|
||||
CLAY_SCROLL({ .vertical = true }),
|
||||
CLAY_BORDER({ .betweenChildren = {2, COLOR_RED} })
|
||||
) {
|
||||
CLAY({ .id = CLAY_ID("TopBorder1"), .layout = topBorderConfig, .backgroundColor = COLOR_TOP_BORDER_5 }) {}
|
||||
CLAY({ .id = CLAY_ID("TopBorder2"), .layout = topBorderConfig, .backgroundColor = COLOR_TOP_BORDER_4 }) {}
|
||||
CLAY({ .id = CLAY_ID("TopBorder3"), .layout = topBorderConfig, .backgroundColor = COLOR_TOP_BORDER_3 }) {}
|
||||
CLAY({ .id = CLAY_ID("TopBorder4"), .layout = topBorderConfig, .backgroundColor = COLOR_TOP_BORDER_2 }) {}
|
||||
CLAY({ .id = CLAY_ID("TopBorder5"), .layout = topBorderConfig, .backgroundColor = COLOR_TOP_BORDER_1 }) {}
|
||||
CLAY({ .id = CLAY_ID("OuterScrollContainer"),
|
||||
.layout = { .sizing = { CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0) }, .layoutDirection = CLAY_TOP_TO_BOTTOM },
|
||||
.scroll = { .vertical = true },
|
||||
.border = { .width = { .betweenChildren = 2 }, .color = COLOR_RED }
|
||||
}) {
|
||||
if (mobileScreen) {
|
||||
LandingPageMobile();
|
||||
FeatureBlocksMobile();
|
||||
@ -375,18 +408,25 @@ Clay_RenderCommandArray CreateLayout(bool mobileScreen, float lerpValue) {
|
||||
scrollbarColor = (Clay_Color){225, 138, 50, 160};
|
||||
}
|
||||
float scrollHeight = scrollData.scrollContainerDimensions.height - 12;
|
||||
CLAY(CLAY_ID("ScrollBar"),
|
||||
CLAY_FLOATING({ .offset = { .x = -6, .y = -(scrollData.scrollPosition->y / scrollData.contentDimensions.height) * scrollHeight + 6}, .zIndex = 1, .parentId = Clay_GetElementId(CLAY_STRING("OuterScrollContainer")).id, .attachment = {.element = CLAY_ATTACH_POINT_RIGHT_TOP, .parent = CLAY_ATTACH_POINT_RIGHT_TOP }}),
|
||||
CLAY_LAYOUT({ .sizing = {CLAY_SIZING_FIXED(10), CLAY_SIZING_FIXED((scrollHeight / scrollData.contentDimensions.height) * scrollHeight)} }),
|
||||
CLAY_RECTANGLE({ .cornerRadius = CLAY_CORNER_RADIUS(5), .color = scrollbarColor })
|
||||
) {}
|
||||
CLAY({
|
||||
.id = CLAY_ID("ScrollBar"),
|
||||
.floating = { .offset = { .x = -6, .y = -(scrollData.scrollPosition->y / scrollData.contentDimensions.height) * scrollHeight + 6}, .zIndex = 1, .parentId = Clay_GetElementId(CLAY_STRING("OuterScrollContainer")).id, .attachPoints = {.element = CLAY_ATTACH_POINT_RIGHT_TOP, .parent = CLAY_ATTACH_POINT_RIGHT_TOP }, .attachTo = CLAY_ATTACH_TO_PARENT },
|
||||
.layout = { .sizing = {CLAY_SIZING_FIXED(10), CLAY_SIZING_FIXED((scrollHeight / scrollData.contentDimensions.height) * scrollHeight)} },
|
||||
.backgroundColor = scrollbarColor,
|
||||
.cornerRadius = CLAY_CORNER_RADIUS(5)
|
||||
}) {}
|
||||
}
|
||||
return Clay_EndLayout();
|
||||
}
|
||||
|
||||
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 });
|
||||
|
@ -11,6 +11,6 @@ add_executable(clay_examples_cpp_project_example main.cpp)
|
||||
target_include_directories(clay_examples_cpp_project_example PUBLIC .)
|
||||
|
||||
if(NOT MSVC)
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "-Werror -Wall")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "-O3")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}")
|
||||
endif()
|
||||
|
@ -13,7 +13,7 @@ int main(void) {
|
||||
Clay_Arena clayMemory = Clay_CreateArenaWithCapacityAndMemory(totalMemorySize, (char *)malloc(totalMemorySize));
|
||||
Clay_Initialize(clayMemory, Clay_Dimensions {1024,768}, Clay_ErrorHandler { HandleClayErrors });
|
||||
Clay_BeginLayout();
|
||||
CLAY(CLAY_RECTANGLE({ .color = {255,255,255,0} }), CLAY_LAYOUT(layoutElement)) {
|
||||
CLAY({ .layout = layoutElement, .backgroundColor = {255,255,255,0} }) {
|
||||
CLAY_TEXT(CLAY_STRING(""), CLAY_TEXT_CONFIG({ .fontId = 0 }));
|
||||
}
|
||||
Clay_EndLayout();
|
||||
|
@ -28,8 +28,8 @@ target_link_libraries(clay_examples_introducing_clay_video_demo PUBLIC raylib)
|
||||
if(MSVC)
|
||||
set(CMAKE_C_FLAGS_DEBUG "/D CLAY_DEBUG")
|
||||
else()
|
||||
set(CMAKE_C_FLAGS_DEBUG "-Wall -Werror -Wno-error=missing-braces -DCLAY_DEBUG")
|
||||
set(CMAKE_C_FLAGS_RELEASE "-O3")
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}")
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}")
|
||||
endif()
|
||||
|
||||
add_custom_command(
|
||||
|
@ -1,81 +1,14 @@
|
||||
#define CLAY_IMPLEMENTATION
|
||||
#include "../../clay.h"
|
||||
#include "../../renderers/raylib/clay_renderer_raylib.c"
|
||||
|
||||
const int FONT_ID_BODY_16 = 0;
|
||||
Clay_Color COLOR_WHITE = { 255, 255, 255, 255};
|
||||
|
||||
void RenderHeaderButton(Clay_String text) {
|
||||
CLAY(
|
||||
CLAY_LAYOUT({ .padding = { 16, 16, 8, 8 }}),
|
||||
CLAY_RECTANGLE({
|
||||
.color = { 140, 140, 140, 255 },
|
||||
.cornerRadius = 5
|
||||
})
|
||||
) {
|
||||
CLAY_TEXT(text, CLAY_TEXT_CONFIG({
|
||||
.fontId = FONT_ID_BODY_16,
|
||||
.fontSize = 16,
|
||||
.textColor = { 255, 255, 255, 255 }
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
void RenderDropdownMenuItem(Clay_String text) {
|
||||
CLAY(CLAY_LAYOUT({ .padding = CLAY_PADDING_ALL(16)})) {
|
||||
CLAY_TEXT(text, CLAY_TEXT_CONFIG({
|
||||
.fontId = FONT_ID_BODY_16,
|
||||
.fontSize = 16,
|
||||
.textColor = { 255, 255, 255, 255 }
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
Clay_String title;
|
||||
Clay_String contents;
|
||||
} Document;
|
||||
|
||||
typedef struct {
|
||||
Document *documents;
|
||||
uint32_t length;
|
||||
} DocumentArray;
|
||||
|
||||
DocumentArray documents = {
|
||||
.documents = NULL, // TODO figure out if it's possible to const init this list
|
||||
.length = 5
|
||||
};
|
||||
|
||||
uint32_t selectedDocumentIndex = 0;
|
||||
|
||||
void HandleSidebarInteraction(
|
||||
Clay_ElementId elementId,
|
||||
Clay_PointerData pointerData,
|
||||
intptr_t userData
|
||||
) {
|
||||
// If this button was clicked
|
||||
if (pointerData.state == CLAY_POINTER_DATA_PRESSED_THIS_FRAME) {
|
||||
if (userData >= 0 && userData < documents.length) {
|
||||
// Select the corresponding document
|
||||
selectedDocumentIndex = userData;
|
||||
}
|
||||
}
|
||||
}
|
||||
#include "../shared-layouts/clay-video-demo.c"
|
||||
|
||||
// This function is new since the video was published
|
||||
void HandleClayErrors(Clay_ErrorData errorData) {
|
||||
printf("%s", errorData.errorText.chars);
|
||||
}
|
||||
|
||||
|
||||
int main(void) {
|
||||
documents.documents = (Document[]) {
|
||||
{ .title = CLAY_STRING("Squirrels"), .contents = CLAY_STRING("The Secret Life of Squirrels: Nature's Clever Acrobats\n""Squirrels are often overlooked creatures, dismissed as mere park inhabitants or backyard nuisances. Yet, beneath their fluffy tails and twitching noses lies an intricate world of cunning, agility, and survival tactics that are nothing short of fascinating. As one of the most common mammals in North America, squirrels have adapted to a wide range of environments from bustling urban centers to tranquil forests and have developed a variety of unique behaviors that continue to intrigue scientists and nature enthusiasts alike.\n""\n""Master Tree Climbers\n""At the heart of a squirrel's skill set is its impressive ability to navigate trees with ease. Whether they're darting from branch to branch or leaping across wide gaps, squirrels possess an innate talent for acrobatics. Their powerful hind legs, which are longer than their front legs, give them remarkable jumping power. With a tail that acts as a counterbalance, squirrels can leap distances of up to ten times the length of their body, making them some of the best aerial acrobats in the animal kingdom.\n""But it's not just their agility that makes them exceptional climbers. Squirrels' sharp, curved claws allow them to grip tree bark with precision, while the soft pads on their feet provide traction on slippery surfaces. Their ability to run at high speeds and scale vertical trunks with ease is a testament to the evolutionary adaptations that have made them so successful in their arboreal habitats.\n""\n""Food Hoarders Extraordinaire\n""Squirrels are often seen frantically gathering nuts, seeds, and even fungi in preparation for winter. While this behavior may seem like instinctual hoarding, it is actually a survival strategy that has been honed over millions of years. Known as \"scatter hoarding,\" squirrels store their food in a variety of hidden locations, often burying it deep in the soil or stashing it in hollowed-out tree trunks.\n""Interestingly, squirrels have an incredible memory for the locations of their caches. Research has shown that they can remember thousands of hiding spots, often returning to them months later when food is scarce. However, they don't always recover every stash some forgotten caches eventually sprout into new trees, contributing to forest regeneration. This unintentional role as forest gardeners highlights the ecological importance of squirrels in their ecosystems.\n""\n""The Great Squirrel Debate: Urban vs. Wild\n""While squirrels are most commonly associated with rural or wooded areas, their adaptability has allowed them to thrive in urban environments as well. In cities, squirrels have become adept at finding food sources in places like parks, streets, and even garbage cans. However, their urban counterparts face unique challenges, including traffic, predators, and the lack of natural shelters. Despite these obstacles, squirrels in urban areas are often observed using human infrastructure such as buildings, bridges, and power lines as highways for their acrobatic escapades.\n""There is, however, a growing concern regarding the impact of urban life on squirrel populations. Pollution, deforestation, and the loss of natural habitats are making it more difficult for squirrels to find adequate food and shelter. As a result, conservationists are focusing on creating squirrel-friendly spaces within cities, with the goal of ensuring these resourceful creatures continue to thrive in both rural and urban landscapes.\n""\n""A Symbol of Resilience\n""In many cultures, squirrels are symbols of resourcefulness, adaptability, and preparation. Their ability to thrive in a variety of environments while navigating challenges with agility and grace serves as a reminder of the resilience inherent in nature. Whether you encounter them in a quiet forest, a city park, or your own backyard, squirrels are creatures that never fail to amaze with their endless energy and ingenuity.\n""In the end, squirrels may be small, but they are mighty in their ability to survive and thrive in a world that is constantly changing. So next time you spot one hopping across a branch or darting across your lawn, take a moment to appreciate the remarkable acrobat at work a true marvel of the natural world.\n") },
|
||||
{ .title = CLAY_STRING("Lorem Ipsum"), .contents = CLAY_STRING("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.") },
|
||||
{ .title = CLAY_STRING("Vacuum Instructions"), .contents = CLAY_STRING("Chapter 3: Getting Started - Unpacking and Setup\n""\n""Congratulations on your new SuperClean Pro 5000 vacuum cleaner! In this section, we will guide you through the simple steps to get your vacuum up and running. Before you begin, please ensure that you have all the components listed in the \"Package Contents\" section on page 2.\n""\n""1. Unboxing Your Vacuum\n""Carefully remove the vacuum cleaner from the box. Avoid using sharp objects that could damage the product. Once removed, place the unit on a flat, stable surface to proceed with the setup. Inside the box, you should find:\n""\n"" The main vacuum unit\n"" A telescoping extension wand\n"" A set of specialized cleaning tools (crevice tool, upholstery brush, etc.)\n"" A reusable dust bag (if applicable)\n"" A power cord with a 3-prong plug\n"" A set of quick-start instructions\n""\n""2. Assembling Your Vacuum\n""Begin by attaching the extension wand to the main body of the vacuum cleaner. Line up the connectors and twist the wand into place until you hear a click. Next, select the desired cleaning tool and firmly attach it to the wand's end, ensuring it is securely locked in.\n""\n""For models that require a dust bag, slide the bag into the compartment at the back of the vacuum, making sure it is properly aligned with the internal mechanism. If your vacuum uses a bagless system, ensure the dust container is correctly seated and locked in place before use.\n""\n""3. Powering On\n""To start the vacuum, plug the power cord into a grounded electrical outlet. Once plugged in, locate the power switch, usually positioned on the side of the handle or body of the unit, depending on your model. Press the switch to the \"On\" position, and you should hear the motor begin to hum. If the vacuum does not power on, check that the power cord is securely plugged in, and ensure there are no blockages in the power switch.\n""\n""Note: Before first use, ensure that the vacuum filter (if your model has one) is properly installed. If unsure, refer to \"Section 5: Maintenance\" for filter installation instructions.") },
|
||||
{ .title = CLAY_STRING("Article 4"), .contents = CLAY_STRING("Article 4") },
|
||||
{ .title = CLAY_STRING("Article 5"), .contents = CLAY_STRING("Article 5") },
|
||||
};
|
||||
Clay_Raylib_Initialize(1024, 768, "Introducing Clay Demo", FLAG_WINDOW_RESIZABLE | FLAG_WINDOW_HIGHDPI | FLAG_MSAA_4X_HINT | FLAG_VSYNC_HINT); // Extra parameters to this function are new since the video was published
|
||||
|
||||
uint64_t clayRequiredMemory = Clay_MinMemorySize();
|
||||
@ -84,12 +17,12 @@ int main(void) {
|
||||
.width = GetScreenWidth(),
|
||||
.height = GetScreenHeight()
|
||||
}, (Clay_ErrorHandler) { HandleClayErrors }); // This final argument is new since the video was published
|
||||
Clay_SetMeasureTextFunction(Raylib_MeasureText, 0);
|
||||
Raylib_fonts[FONT_ID_BODY_16] = (Raylib_Font) {
|
||||
.font = LoadFontEx("resources/Roboto-Regular.ttf", 48, 0, 400),
|
||||
.fontId = FONT_ID_BODY_16
|
||||
};
|
||||
SetTextureFilter(Raylib_fonts[FONT_ID_BODY_16].font.texture, TEXTURE_FILTER_BILINEAR);
|
||||
Font fonts[1];
|
||||
fonts[FONT_ID_BODY_16] = LoadFontEx("resources/Roboto-Regular.ttf", 48, 0, 400);
|
||||
SetTextureFilter(fonts[FONT_ID_BODY_16].texture, TEXTURE_FILTER_BILINEAR);
|
||||
Clay_SetMeasureTextFunction(Raylib_MeasureText, fonts);
|
||||
|
||||
ClayVideoDemo_Data data = ClayVideoDemo_Initialize();
|
||||
|
||||
while (!WindowShouldClose()) {
|
||||
// Run once per frame
|
||||
@ -110,193 +43,11 @@ int main(void) {
|
||||
GetFrameTime()
|
||||
);
|
||||
|
||||
Clay_Sizing layoutExpand = {
|
||||
.width = CLAY_SIZING_GROW(0),
|
||||
.height = CLAY_SIZING_GROW(0)
|
||||
};
|
||||
|
||||
Clay_RectangleElementConfig contentBackgroundConfig = {
|
||||
.color = { 90, 90, 90, 255 },
|
||||
.cornerRadius = CLAY_CORNER_RADIUS(8)
|
||||
};
|
||||
|
||||
Clay_BeginLayout();
|
||||
// Build UI here
|
||||
CLAY(
|
||||
CLAY_ID("OuterContainer"),
|
||||
CLAY_RECTANGLE({ .color = { 43, 41, 51, 255 } }),
|
||||
CLAY_LAYOUT({
|
||||
.layoutDirection = CLAY_TOP_TO_BOTTOM,
|
||||
.sizing = layoutExpand,
|
||||
.padding = CLAY_PADDING_ALL(16),
|
||||
.childGap = 16
|
||||
})
|
||||
) {
|
||||
// Child elements go inside braces
|
||||
CLAY(
|
||||
CLAY_ID("HeaderBar"),
|
||||
CLAY_RECTANGLE(contentBackgroundConfig),
|
||||
CLAY_LAYOUT({
|
||||
.sizing = {
|
||||
.height = CLAY_SIZING_FIXED(60),
|
||||
.width = CLAY_SIZING_GROW(0)
|
||||
},
|
||||
.padding = { 16, 16, 0, 0 },
|
||||
.childGap = 16,
|
||||
.childAlignment = {
|
||||
.y = CLAY_ALIGN_Y_CENTER
|
||||
}
|
||||
})
|
||||
) {
|
||||
// Header buttons go here
|
||||
CLAY(
|
||||
CLAY_ID("FileButton"),
|
||||
CLAY_LAYOUT({ .padding = { 16, 16, 8, 8 }}),
|
||||
CLAY_RECTANGLE({
|
||||
.color = { 140, 140, 140, 255 },
|
||||
.cornerRadius = CLAY_CORNER_RADIUS(5)
|
||||
})
|
||||
) {
|
||||
CLAY_TEXT(CLAY_STRING("File"), CLAY_TEXT_CONFIG({
|
||||
.fontId = FONT_ID_BODY_16,
|
||||
.fontSize = 16,
|
||||
.textColor = { 255, 255, 255, 255 }
|
||||
}));
|
||||
|
||||
bool fileMenuVisible =
|
||||
Clay_PointerOver(Clay_GetElementId(CLAY_STRING("FileButton")))
|
||||
||
|
||||
Clay_PointerOver(Clay_GetElementId(CLAY_STRING("FileMenu")));
|
||||
|
||||
if (fileMenuVisible) { // Below has been changed slightly to fix the small bug where the menu would dismiss when mousing over the top gap
|
||||
CLAY(
|
||||
CLAY_ID("FileMenu"),
|
||||
CLAY_FLOATING({
|
||||
.attachment = {
|
||||
.parent = CLAY_ATTACH_POINT_LEFT_BOTTOM
|
||||
},
|
||||
}),
|
||||
CLAY_LAYOUT({
|
||||
.padding = {0, 0, 8, 8 }
|
||||
})
|
||||
) {
|
||||
CLAY(
|
||||
CLAY_LAYOUT({
|
||||
.layoutDirection = CLAY_TOP_TO_BOTTOM,
|
||||
.sizing = {
|
||||
.width = CLAY_SIZING_FIXED(200)
|
||||
},
|
||||
}),
|
||||
CLAY_RECTANGLE({
|
||||
.color = { 40, 40, 40, 255 },
|
||||
.cornerRadius = CLAY_CORNER_RADIUS(8)
|
||||
})
|
||||
) {
|
||||
// Render dropdown items here
|
||||
RenderDropdownMenuItem(CLAY_STRING("New"));
|
||||
RenderDropdownMenuItem(CLAY_STRING("Open"));
|
||||
RenderDropdownMenuItem(CLAY_STRING("Close"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
RenderHeaderButton(CLAY_STRING("Edit"));
|
||||
CLAY(CLAY_LAYOUT({ .sizing = { CLAY_SIZING_GROW(0) }})) {}
|
||||
RenderHeaderButton(CLAY_STRING("Upload"));
|
||||
RenderHeaderButton(CLAY_STRING("Media"));
|
||||
RenderHeaderButton(CLAY_STRING("Support"));
|
||||
}
|
||||
|
||||
CLAY(
|
||||
CLAY_ID("LowerContent"),
|
||||
CLAY_LAYOUT({ .sizing = layoutExpand, .childGap = 16 })
|
||||
) {
|
||||
CLAY(
|
||||
CLAY_ID("Sidebar"),
|
||||
CLAY_RECTANGLE(contentBackgroundConfig),
|
||||
CLAY_LAYOUT({
|
||||
.layoutDirection = CLAY_TOP_TO_BOTTOM,
|
||||
.padding = CLAY_PADDING_ALL(16),
|
||||
.childGap = 8,
|
||||
.sizing = {
|
||||
.width = CLAY_SIZING_FIXED(250),
|
||||
.height = CLAY_SIZING_GROW(0)
|
||||
}
|
||||
})
|
||||
) {
|
||||
for (int i = 0; i < documents.length; i++) {
|
||||
Document document = documents.documents[i];
|
||||
Clay_LayoutConfig sidebarButtonLayout = {
|
||||
.sizing = { .width = CLAY_SIZING_GROW(0) },
|
||||
.padding = CLAY_PADDING_ALL(16)
|
||||
};
|
||||
|
||||
if (i == selectedDocumentIndex) {
|
||||
CLAY(
|
||||
CLAY_LAYOUT(sidebarButtonLayout),
|
||||
CLAY_RECTANGLE({
|
||||
.color = { 120, 120, 120, 255 },
|
||||
.cornerRadius = CLAY_CORNER_RADIUS(8),
|
||||
})
|
||||
) {
|
||||
CLAY_TEXT(document.title, CLAY_TEXT_CONFIG({
|
||||
.fontId = FONT_ID_BODY_16,
|
||||
.fontSize = 20,
|
||||
.textColor = { 255, 255, 255, 255 }
|
||||
}));
|
||||
}
|
||||
} else {
|
||||
CLAY(
|
||||
CLAY_LAYOUT(sidebarButtonLayout),
|
||||
Clay_OnHover(HandleSidebarInteraction, i),
|
||||
Clay_Hovered()
|
||||
? CLAY_RECTANGLE({
|
||||
.color = { 120, 120, 120, 120 },
|
||||
.cornerRadius = CLAY_CORNER_RADIUS(8)
|
||||
})
|
||||
: 0
|
||||
) {
|
||||
CLAY_TEXT(document.title, CLAY_TEXT_CONFIG({
|
||||
.fontId = FONT_ID_BODY_16,
|
||||
.fontSize = 20,
|
||||
.textColor = { 255, 255, 255, 255 }
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CLAY(
|
||||
CLAY_ID("MainContent"),
|
||||
CLAY_RECTANGLE(contentBackgroundConfig),
|
||||
CLAY_SCROLL({ .vertical = true }),
|
||||
CLAY_LAYOUT({
|
||||
.layoutDirection = CLAY_TOP_TO_BOTTOM,
|
||||
.childGap = 16,
|
||||
.padding = CLAY_PADDING_ALL(16),
|
||||
.sizing = layoutExpand
|
||||
})
|
||||
) {
|
||||
Document selectedDocument = documents.documents[selectedDocumentIndex];
|
||||
CLAY_TEXT(selectedDocument.title, CLAY_TEXT_CONFIG({
|
||||
.fontId = FONT_ID_BODY_16,
|
||||
.fontSize = 24,
|
||||
.textColor = COLOR_WHITE
|
||||
}));
|
||||
CLAY_TEXT(selectedDocument.contents, CLAY_TEXT_CONFIG({
|
||||
.fontId = FONT_ID_BODY_16,
|
||||
.fontSize = 24,
|
||||
.textColor = COLOR_WHITE
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Clay_RenderCommandArray renderCommands = Clay_EndLayout();
|
||||
Clay_RenderCommandArray renderCommands = ClayVideoDemo_CreateLayout(&data);
|
||||
|
||||
BeginDrawing();
|
||||
ClearBackground(BLACK);
|
||||
Clay_Raylib_Render(renderCommands);
|
||||
Clay_Raylib_Render(renderCommands, fonts);
|
||||
EndDrawing();
|
||||
}
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ target_include_directories(clay_examples_raylib_multi_context PUBLIC .)
|
||||
target_link_libraries(clay_examples_raylib_multi_context PUBLIC raylib)
|
||||
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}")
|
||||
set(CMAKE_C_FLAGS_RELEASE "-O3")
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}")
|
||||
|
||||
add_custom_command(
|
||||
TARGET clay_examples_raylib_multi_context POST_BUILD
|
||||
|
@ -1,87 +1,13 @@
|
||||
#define CLAY_IMPLEMENTATION
|
||||
#include "../../clay.h"
|
||||
#include "../../renderers/raylib/clay_renderer_raylib.c"
|
||||
#include "../shared-layouts/clay-video-demo.c"
|
||||
|
||||
const int FONT_ID_BODY_16 = 0;
|
||||
Clay_Color COLOR_WHITE = { 255, 255, 255, 255};
|
||||
|
||||
void RenderHeaderButton(Clay_String text) {
|
||||
CLAY(
|
||||
CLAY_LAYOUT({ .padding = { 16, 16, 8, 8 }}),
|
||||
CLAY_RECTANGLE({
|
||||
.color = { 140, 140, 140, 255 },
|
||||
.cornerRadius = CLAY_CORNER_RADIUS(5)
|
||||
})
|
||||
) {
|
||||
CLAY_TEXT(text, CLAY_TEXT_CONFIG({
|
||||
.fontId = FONT_ID_BODY_16,
|
||||
.fontSize = 16,
|
||||
.textColor = { 255, 255, 255, 255 }
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
void RenderDropdownMenuItem(Clay_String text) {
|
||||
CLAY(CLAY_LAYOUT({ .padding = CLAY_PADDING_ALL(16)})) {
|
||||
CLAY_TEXT(text, CLAY_TEXT_CONFIG({
|
||||
.fontId = FONT_ID_BODY_16,
|
||||
.fontSize = 16,
|
||||
.textColor = { 255, 255, 255, 255 }
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
Clay_String title;
|
||||
Clay_String contents;
|
||||
} Document;
|
||||
|
||||
typedef struct {
|
||||
Document *documents;
|
||||
int32_t length;
|
||||
} DocumentArray;
|
||||
|
||||
DocumentArray documents = {
|
||||
.documents = NULL, // TODO figure out if it's possible to const init this list
|
||||
.length = 5
|
||||
};
|
||||
|
||||
int32_t selectedDocumentIndexTop = 0;
|
||||
int32_t selectedDocumentIndexBottom = 0;
|
||||
|
||||
typedef struct {
|
||||
int32_t requestedDocumentIndex;
|
||||
int32_t* selectedDocumentIndex;
|
||||
} SidebarClickData;
|
||||
|
||||
void HandleSidebarInteraction(
|
||||
Clay_ElementId elementId,
|
||||
Clay_PointerData pointerData,
|
||||
intptr_t userData
|
||||
) {
|
||||
SidebarClickData *clickData = (SidebarClickData*)userData;
|
||||
// If this button was clicked
|
||||
if (pointerData.state == CLAY_POINTER_DATA_PRESSED_THIS_FRAME) {
|
||||
if (clickData->requestedDocumentIndex >= 0 && clickData->requestedDocumentIndex < documents.length) {
|
||||
// Select the corresponding document
|
||||
*clickData->selectedDocumentIndex = clickData->requestedDocumentIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This function is new since the video was published
|
||||
void HandleClayErrors(Clay_ErrorData errorData) {
|
||||
printf("%s", errorData.errorText.chars);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
intptr_t next;
|
||||
intptr_t memory;
|
||||
} Arena;
|
||||
|
||||
Arena frameArena = {0};
|
||||
|
||||
Clay_RenderCommandArray CreateLayout(Clay_Context* context, float yOffset, int32_t* documentIndex) {
|
||||
Clay_RenderCommandArray CreateLayout(Clay_Context* context, ClayVideoDemo_Data *data) {
|
||||
Clay_SetCurrentContext(context);
|
||||
Clay_SetDebugModeEnabled(true);
|
||||
// Run once per frame
|
||||
@ -89,9 +15,8 @@ Clay_RenderCommandArray CreateLayout(Clay_Context* context, float yOffset, int32
|
||||
.width = GetScreenWidth(),
|
||||
.height = GetScreenHeight() / 2
|
||||
});
|
||||
|
||||
Vector2 mousePosition = GetMousePosition();
|
||||
mousePosition.y -= yOffset;
|
||||
mousePosition.y -= data->yOffset;
|
||||
Vector2 scrollDelta = GetMouseWheelMoveV();
|
||||
Clay_SetPointerState(
|
||||
(Clay_Vector2) { mousePosition.x, mousePosition.y },
|
||||
@ -102,128 +27,10 @@ Clay_RenderCommandArray CreateLayout(Clay_Context* context, float yOffset, int32
|
||||
(Clay_Vector2) { scrollDelta.x, scrollDelta.y },
|
||||
GetFrameTime()
|
||||
);
|
||||
|
||||
Clay_Sizing layoutExpand = {
|
||||
.width = CLAY_SIZING_GROW(0),
|
||||
.height = CLAY_SIZING_GROW(0)
|
||||
};
|
||||
|
||||
Clay_RectangleElementConfig contentBackgroundConfig = {
|
||||
.color = { 90, 90, 90, 255 },
|
||||
.cornerRadius = CLAY_CORNER_RADIUS(8)
|
||||
};
|
||||
|
||||
Clay_BeginLayout();
|
||||
// Build UI here
|
||||
CLAY(
|
||||
CLAY_ID("OuterContainer"),
|
||||
CLAY_RECTANGLE({ .color = { 43, 41, 51, 255 } }),
|
||||
CLAY_LAYOUT({
|
||||
.layoutDirection = CLAY_TOP_TO_BOTTOM,
|
||||
.sizing = layoutExpand,
|
||||
.padding = CLAY_PADDING_ALL(16),
|
||||
.childGap = 16
|
||||
})
|
||||
) {
|
||||
CLAY(
|
||||
CLAY_ID("LowerContent"),
|
||||
CLAY_LAYOUT({ .sizing = layoutExpand, .childGap = 16 })
|
||||
) {
|
||||
CLAY(
|
||||
CLAY_ID("Sidebar"),
|
||||
CLAY_RECTANGLE(contentBackgroundConfig),
|
||||
CLAY_LAYOUT({
|
||||
.layoutDirection = CLAY_TOP_TO_BOTTOM,
|
||||
.padding = CLAY_PADDING_ALL(16),
|
||||
.childGap = 8,
|
||||
.sizing = {
|
||||
.width = CLAY_SIZING_FIXED(250),
|
||||
.height = CLAY_SIZING_GROW(0)
|
||||
}
|
||||
})
|
||||
) {
|
||||
for (int i = 0; i < documents.length; i++) {
|
||||
Document document = documents.documents[i];
|
||||
Clay_LayoutConfig sidebarButtonLayout = {
|
||||
.sizing = { .width = CLAY_SIZING_GROW(0) },
|
||||
.padding = CLAY_PADDING_ALL(16)
|
||||
};
|
||||
|
||||
if (i == *documentIndex) {
|
||||
CLAY(
|
||||
CLAY_LAYOUT(sidebarButtonLayout),
|
||||
CLAY_RECTANGLE({
|
||||
.color = { 120, 120, 120, 255 },
|
||||
.cornerRadius = CLAY_CORNER_RADIUS(8),
|
||||
})
|
||||
) {
|
||||
CLAY_TEXT(document.title, CLAY_TEXT_CONFIG({
|
||||
.fontId = FONT_ID_BODY_16,
|
||||
.fontSize = 20,
|
||||
.textColor = { 255, 255, 255, 255 }
|
||||
}));
|
||||
}
|
||||
} else {
|
||||
SidebarClickData *clickData = (SidebarClickData *)frameArena.next;
|
||||
*clickData = (SidebarClickData) { .requestedDocumentIndex = i, .selectedDocumentIndex = documentIndex };
|
||||
frameArena.next += sizeof(SidebarClickData);
|
||||
CLAY(
|
||||
CLAY_LAYOUT(sidebarButtonLayout),
|
||||
Clay_OnHover(HandleSidebarInteraction, (intptr_t)clickData),
|
||||
Clay_Hovered()
|
||||
? CLAY_RECTANGLE({
|
||||
.color = { 120, 120, 120, 120 },
|
||||
.cornerRadius = CLAY_CORNER_RADIUS(8)
|
||||
})
|
||||
: (void)0
|
||||
) {
|
||||
CLAY_TEXT(document.title, CLAY_TEXT_CONFIG({
|
||||
.fontId = FONT_ID_BODY_16,
|
||||
.fontSize = 20,
|
||||
.textColor = { 255, 255, 255, 255 }
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CLAY(
|
||||
CLAY_ID("MainContent"),
|
||||
CLAY_RECTANGLE(contentBackgroundConfig),
|
||||
CLAY_SCROLL({ .vertical = true }),
|
||||
CLAY_LAYOUT({
|
||||
.layoutDirection = CLAY_TOP_TO_BOTTOM,
|
||||
.childGap = 16,
|
||||
.padding = CLAY_PADDING_ALL(16),
|
||||
.sizing = layoutExpand
|
||||
})
|
||||
) {
|
||||
Document selectedDocument = documents.documents[*documentIndex];
|
||||
CLAY_TEXT(selectedDocument.title, CLAY_TEXT_CONFIG({
|
||||
.fontId = FONT_ID_BODY_16,
|
||||
.fontSize = 24,
|
||||
.textColor = COLOR_WHITE
|
||||
}));
|
||||
CLAY_TEXT(selectedDocument.contents, CLAY_TEXT_CONFIG({
|
||||
.fontId = FONT_ID_BODY_16,
|
||||
.fontSize = 24,
|
||||
.textColor = COLOR_WHITE
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Clay_RenderCommandArray renderCommands = Clay_EndLayout();
|
||||
for (int i = 0; i < renderCommands.length; ++i) {
|
||||
Clay_RenderCommandArray_Get(&renderCommands,i)->boundingBox.y += yOffset;
|
||||
}
|
||||
return renderCommands;
|
||||
return ClayVideoDemo_CreateLayout(data);
|
||||
}
|
||||
|
||||
|
||||
int main(void) {
|
||||
frameArena = (Arena) { .memory = (intptr_t)malloc(1024) };
|
||||
frameArena.next = frameArena.memory;
|
||||
documents.documents = (Document[]) {
|
||||
{ .title = CLAY_STRING("Squirrels"), .contents = CLAY_STRING("The Secret Life of Squirrels: Nature's Clever Acrobats\n""Squirrels are often overlooked creatures, dismissed as mere park inhabitants or backyard nuisances. Yet, beneath their fluffy tails and twitching noses lies an intricate world of cunning, agility, and survival tactics that are nothing short of fascinating. As one of the most common mammals in North America, squirrels have adapted to a wide range of environments from bustling urban centers to tranquil forests and have developed a variety of unique behaviors that continue to intrigue scientists and nature enthusiasts alike.\n""\n""Master Tree Climbers\n""At the heart of a squirrel's skill set is its impressive ability to navigate trees with ease. Whether they're darting from branch to branch or leaping across wide gaps, squirrels possess an innate talent for acrobatics. Their powerful hind legs, which are longer than their front legs, give them remarkable jumping power. With a tail that acts as a counterbalance, squirrels can leap distances of up to ten times the length of their body, making them some of the best aerial acrobats in the animal kingdom.\n""But it's not just their agility that makes them exceptional climbers. Squirrels' sharp, curved claws allow them to grip tree bark with precision, while the soft pads on their feet provide traction on slippery surfaces. Their ability to run at high speeds and scale vertical trunks with ease is a testament to the evolutionary adaptations that have made them so successful in their arboreal habitats.\n""\n""Food Hoarders Extraordinaire\n""Squirrels are often seen frantically gathering nuts, seeds, and even fungi in preparation for winter. While this behavior may seem like instinctual hoarding, it is actually a survival strategy that has been honed over millions of years. Known as \"scatter hoarding,\" squirrels store their food in a variety of hidden locations, often burying it deep in the soil or stashing it in hollowed-out tree trunks.\n""Interestingly, squirrels have an incredible memory for the locations of their caches. Research has shown that they can remember thousands of hiding spots, often returning to them months later when food is scarce. However, they don't always recover every stash some forgotten caches eventually sprout into new trees, contributing to forest regeneration. This unintentional role as forest gardeners highlights the ecological importance of squirrels in their ecosystems.\n""\n""The Great Squirrel Debate: Urban vs. Wild\n""While squirrels are most commonly associated with rural or wooded areas, their adaptability has allowed them to thrive in urban environments as well. In cities, squirrels have become adept at finding food sources in places like parks, streets, and even garbage cans. However, their urban counterparts face unique challenges, including traffic, predators, and the lack of natural shelters. Despite these obstacles, squirrels in urban areas are often observed using human infrastructure such as buildings, bridges, and power lines as highways for their acrobatic escapades.\n""There is, however, a growing concern regarding the impact of urban life on squirrel populations. Pollution, deforestation, and the loss of natural habitats are making it more difficult for squirrels to find adequate food and shelter. As a result, conservationists are focusing on creating squirrel-friendly spaces within cities, with the goal of ensuring these resourceful creatures continue to thrive in both rural and urban landscapes.\n""\n""A Symbol of Resilience\n""In many cultures, squirrels are symbols of resourcefulness, adaptability, and preparation. Their ability to thrive in a variety of environments while navigating challenges with agility and grace serves as a reminder of the resilience inherent in nature. Whether you encounter them in a quiet forest, a city park, or your own backyard, squirrels are creatures that never fail to amaze with their endless energy and ingenuity.\n""In the end, squirrels may be small, but they are mighty in their ability to survive and thrive in a world that is constantly changing. So next time you spot one hopping across a branch or darting across your lawn, take a moment to appreciate the remarkable acrobat at work a true marvel of the natural world.\n") },
|
||||
{ .title = CLAY_STRING("Lorem Ipsum"), .contents = CLAY_STRING("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.") },
|
||||
@ -233,6 +40,10 @@ int main(void) {
|
||||
};
|
||||
Clay_Raylib_Initialize(1024, 768, "Introducing Clay Demo", FLAG_WINDOW_RESIZABLE | FLAG_WINDOW_HIGHDPI | FLAG_MSAA_4X_HINT | FLAG_VSYNC_HINT); // Extra parameters to this function are new since the video was published
|
||||
|
||||
Font fonts[1];
|
||||
fonts[FONT_ID_BODY_16] = LoadFontEx("resources/Roboto-Regular.ttf", 48, 0, 400);
|
||||
SetTextureFilter(fonts[FONT_ID_BODY_16].texture, TEXTURE_FILTER_BILINEAR);
|
||||
|
||||
uint64_t clayRequiredMemory = Clay_MinMemorySize();
|
||||
|
||||
Clay_Arena clayMemoryTop = Clay_CreateArenaWithCapacityAndMemory(clayRequiredMemory, malloc(clayRequiredMemory));
|
||||
@ -240,28 +51,25 @@ int main(void) {
|
||||
.width = GetScreenWidth(),
|
||||
.height = GetScreenHeight() / 2
|
||||
}, (Clay_ErrorHandler) { HandleClayErrors }); // This final argument is new since the video was published
|
||||
ClayVideoDemo_Data dataTop = ClayVideoDemo_Initialize();
|
||||
Clay_SetMeasureTextFunction(Raylib_MeasureText, fonts);
|
||||
|
||||
Clay_Arena clayMemoryBottom = Clay_CreateArenaWithCapacityAndMemory(clayRequiredMemory, malloc(clayRequiredMemory));
|
||||
Clay_Context *clayContextBottom = Clay_Initialize(clayMemoryBottom, (Clay_Dimensions) {
|
||||
.width = GetScreenWidth(),
|
||||
.height = GetScreenHeight() / 2
|
||||
}, (Clay_ErrorHandler) { HandleClayErrors }); // This final argument is new since the video was published
|
||||
|
||||
Clay_SetMeasureTextFunction(Raylib_MeasureText, 0);
|
||||
Raylib_fonts[FONT_ID_BODY_16] = (Raylib_Font) {
|
||||
.font = LoadFontEx("resources/Roboto-Regular.ttf", 48, 0, 400),
|
||||
.fontId = FONT_ID_BODY_16
|
||||
};
|
||||
SetTextureFilter(Raylib_fonts[FONT_ID_BODY_16].font.texture, TEXTURE_FILTER_BILINEAR);
|
||||
ClayVideoDemo_Data dataBottom = ClayVideoDemo_Initialize();
|
||||
Clay_SetMeasureTextFunction(Raylib_MeasureText, fonts);
|
||||
|
||||
while (!WindowShouldClose()) {
|
||||
frameArena.next = frameArena.memory;
|
||||
Clay_RenderCommandArray renderCommandsTop = CreateLayout(clayContextTop, 0, &selectedDocumentIndexTop);
|
||||
Clay_RenderCommandArray renderCommandsBottom = CreateLayout(clayContextBottom, GetScreenHeight() / 2, &selectedDocumentIndexBottom);
|
||||
dataBottom.yOffset = GetScreenHeight() / 2;
|
||||
Clay_RenderCommandArray renderCommandsTop = CreateLayout(clayContextTop, &dataTop);
|
||||
Clay_RenderCommandArray renderCommandsBottom = CreateLayout(clayContextBottom, &dataBottom);
|
||||
BeginDrawing();
|
||||
ClearBackground(BLACK);
|
||||
Clay_Raylib_Render(renderCommandsTop);
|
||||
Clay_Raylib_Render(renderCommandsBottom);
|
||||
Clay_Raylib_Render(renderCommandsTop, fonts);
|
||||
Clay_Raylib_Render(renderCommandsBottom, fonts);
|
||||
EndDrawing();
|
||||
}
|
||||
}
|
||||
|
@ -25,10 +25,10 @@ target_include_directories(clay_examples_raylib_sidebar_scrolling_container PUBL
|
||||
|
||||
target_link_libraries(clay_examples_raylib_sidebar_scrolling_container PUBLIC raylib)
|
||||
if(MSVC)
|
||||
set(CMAKE_C_FLAGS_DEBUG "/D CLAY_DEBUG")
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}")
|
||||
else()
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}")
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O3")
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}")
|
||||
endif()
|
||||
|
||||
add_custom_command(
|
||||
|
File diff suppressed because one or more lines are too long
@ -2,8 +2,8 @@
|
||||
|
||||
// NOTE: This file only exists to make sure that clay works when included in multiple translation units.
|
||||
|
||||
void SatisfyCompiler() {
|
||||
CLAY(CLAY_ID("SatisfyCompiler"), CLAY_LAYOUT({})) {
|
||||
void SatisfyCompiler(void) {
|
||||
CLAY({ .id = CLAY_ID("SatisfyCompiler") }) {
|
||||
CLAY_TEXT(CLAY_STRING("Test"), CLAY_TEXT_CONFIG({ .fontId = 0, .fontSize = 24 }));
|
||||
}
|
||||
}
|
266
examples/shared-layouts/clay-video-demo.c
Normal file
266
examples/shared-layouts/clay-video-demo.c
Normal file
@ -0,0 +1,266 @@
|
||||
#include "../../clay.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
const int FONT_ID_BODY_16 = 0;
|
||||
Clay_Color COLOR_WHITE = { 255, 255, 255, 255};
|
||||
|
||||
void RenderHeaderButton(Clay_String text) {
|
||||
CLAY({
|
||||
.layout = { .padding = { 16, 16, 8, 8 }},
|
||||
.backgroundColor = { 140, 140, 140, 255 },
|
||||
.cornerRadius = CLAY_CORNER_RADIUS(5)
|
||||
}) {
|
||||
CLAY_TEXT(text, CLAY_TEXT_CONFIG({
|
||||
.fontId = FONT_ID_BODY_16,
|
||||
.fontSize = 16,
|
||||
.textColor = { 255, 255, 255, 255 }
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
void RenderDropdownMenuItem(Clay_String text) {
|
||||
CLAY({.layout = { .padding = CLAY_PADDING_ALL(16)}}) {
|
||||
CLAY_TEXT(text, CLAY_TEXT_CONFIG({
|
||||
.fontId = FONT_ID_BODY_16,
|
||||
.fontSize = 16,
|
||||
.textColor = { 255, 255, 255, 255 }
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
Clay_String title;
|
||||
Clay_String contents;
|
||||
} Document;
|
||||
|
||||
typedef struct {
|
||||
Document *documents;
|
||||
uint32_t length;
|
||||
} DocumentArray;
|
||||
|
||||
Document documentsRaw[5];
|
||||
|
||||
DocumentArray documents = {
|
||||
.length = 5,
|
||||
.documents = documentsRaw
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
intptr_t offset;
|
||||
intptr_t memory;
|
||||
} ClayVideoDemo_Arena;
|
||||
|
||||
typedef struct {
|
||||
int32_t selectedDocumentIndex;
|
||||
float yOffset;
|
||||
ClayVideoDemo_Arena frameArena;
|
||||
} ClayVideoDemo_Data;
|
||||
|
||||
typedef struct {
|
||||
int32_t requestedDocumentIndex;
|
||||
int32_t* selectedDocumentIndex;
|
||||
} SidebarClickData;
|
||||
|
||||
void HandleSidebarInteraction(
|
||||
Clay_ElementId elementId,
|
||||
Clay_PointerData pointerData,
|
||||
intptr_t userData
|
||||
) {
|
||||
SidebarClickData *clickData = (SidebarClickData*)userData;
|
||||
// If this button was clicked
|
||||
if (pointerData.state == CLAY_POINTER_DATA_PRESSED_THIS_FRAME) {
|
||||
if (clickData->requestedDocumentIndex >= 0 && clickData->requestedDocumentIndex < documents.length) {
|
||||
// Select the corresponding document
|
||||
*clickData->selectedDocumentIndex = clickData->requestedDocumentIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ClayVideoDemo_Data ClayVideoDemo_Initialize() {
|
||||
documents.documents[0] = (Document){ .title = CLAY_STRING("Squirrels"), .contents = CLAY_STRING("The Secret Life of Squirrels: Nature's Clever Acrobats\n""Squirrels are often overlooked creatures, dismissed as mere park inhabitants or backyard nuisances. Yet, beneath their fluffy tails and twitching noses lies an intricate world of cunning, agility, and survival tactics that are nothing short of fascinating. As one of the most common mammals in North America, squirrels have adapted to a wide range of environments from bustling urban centers to tranquil forests and have developed a variety of unique behaviors that continue to intrigue scientists and nature enthusiasts alike.\n""\n""Master Tree Climbers\n""At the heart of a squirrel's skill set is its impressive ability to navigate trees with ease. Whether they're darting from branch to branch or leaping across wide gaps, squirrels possess an innate talent for acrobatics. Their powerful hind legs, which are longer than their front legs, give them remarkable jumping power. With a tail that acts as a counterbalance, squirrels can leap distances of up to ten times the length of their body, making them some of the best aerial acrobats in the animal kingdom.\n""But it's not just their agility that makes them exceptional climbers. Squirrels' sharp, curved claws allow them to grip tree bark with precision, while the soft pads on their feet provide traction on slippery surfaces. Their ability to run at high speeds and scale vertical trunks with ease is a testament to the evolutionary adaptations that have made them so successful in their arboreal habitats.\n""\n""Food Hoarders Extraordinaire\n""Squirrels are often seen frantically gathering nuts, seeds, and even fungi in preparation for winter. While this behavior may seem like instinctual hoarding, it is actually a survival strategy that has been honed over millions of years. Known as \"scatter hoarding,\" squirrels store their food in a variety of hidden locations, often burying it deep in the soil or stashing it in hollowed-out tree trunks.\n""Interestingly, squirrels have an incredible memory for the locations of their caches. Research has shown that they can remember thousands of hiding spots, often returning to them months later when food is scarce. However, they don't always recover every stash some forgotten caches eventually sprout into new trees, contributing to forest regeneration. This unintentional role as forest gardeners highlights the ecological importance of squirrels in their ecosystems.\n""\n""The Great Squirrel Debate: Urban vs. Wild\n""While squirrels are most commonly associated with rural or wooded areas, their adaptability has allowed them to thrive in urban environments as well. In cities, squirrels have become adept at finding food sources in places like parks, streets, and even garbage cans. However, their urban counterparts face unique challenges, including traffic, predators, and the lack of natural shelters. Despite these obstacles, squirrels in urban areas are often observed using human infrastructure such as buildings, bridges, and power lines as highways for their acrobatic escapades.\n""There is, however, a growing concern regarding the impact of urban life on squirrel populations. Pollution, deforestation, and the loss of natural habitats are making it more difficult for squirrels to find adequate food and shelter. As a result, conservationists are focusing on creating squirrel-friendly spaces within cities, with the goal of ensuring these resourceful creatures continue to thrive in both rural and urban landscapes.\n""\n""A Symbol of Resilience\n""In many cultures, squirrels are symbols of resourcefulness, adaptability, and preparation. Their ability to thrive in a variety of environments while navigating challenges with agility and grace serves as a reminder of the resilience inherent in nature. Whether you encounter them in a quiet forest, a city park, or your own backyard, squirrels are creatures that never fail to amaze with their endless energy and ingenuity.\n""In the end, squirrels may be small, but they are mighty in their ability to survive and thrive in a world that is constantly changing. So next time you spot one hopping across a branch or darting across your lawn, take a moment to appreciate the remarkable acrobat at work a true marvel of the natural world.\n") };
|
||||
documents.documents[1] = (Document){ .title = CLAY_STRING("Lorem Ipsum"), .contents = CLAY_STRING("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.") };
|
||||
documents.documents[2] = (Document){ .title = CLAY_STRING("Vacuum Instructions"), .contents = CLAY_STRING("Chapter 3: Getting Started - Unpacking and Setup\n""\n""Congratulations on your new SuperClean Pro 5000 vacuum cleaner! In this section, we will guide you through the simple steps to get your vacuum up and running. Before you begin, please ensure that you have all the components listed in the \"Package Contents\" section on page 2.\n""\n""1. Unboxing Your Vacuum\n""Carefully remove the vacuum cleaner from the box. Avoid using sharp objects that could damage the product. Once removed, place the unit on a flat, stable surface to proceed with the setup. Inside the box, you should find:\n""\n"" The main vacuum unit\n"" A telescoping extension wand\n"" A set of specialized cleaning tools (crevice tool, upholstery brush, etc.)\n"" A reusable dust bag (if applicable)\n"" A power cord with a 3-prong plug\n"" A set of quick-start instructions\n""\n""2. Assembling Your Vacuum\n""Begin by attaching the extension wand to the main body of the vacuum cleaner. Line up the connectors and twist the wand into place until you hear a click. Next, select the desired cleaning tool and firmly attach it to the wand's end, ensuring it is securely locked in.\n""\n""For models that require a dust bag, slide the bag into the compartment at the back of the vacuum, making sure it is properly aligned with the internal mechanism. If your vacuum uses a bagless system, ensure the dust container is correctly seated and locked in place before use.\n""\n""3. Powering On\n""To start the vacuum, plug the power cord into a grounded electrical outlet. Once plugged in, locate the power switch, usually positioned on the side of the handle or body of the unit, depending on your model. Press the switch to the \"On\" position, and you should hear the motor begin to hum. If the vacuum does not power on, check that the power cord is securely plugged in, and ensure there are no blockages in the power switch.\n""\n""Note: Before first use, ensure that the vacuum filter (if your model has one) is properly installed. If unsure, refer to \"Section 5: Maintenance\" for filter installation instructions.") };
|
||||
documents.documents[3] = (Document){ .title = CLAY_STRING("Article 4"), .contents = CLAY_STRING("Article 4") };
|
||||
documents.documents[4] = (Document){ .title = CLAY_STRING("Article 5"), .contents = CLAY_STRING("Article 5") };
|
||||
|
||||
ClayVideoDemo_Data data = {
|
||||
.frameArena = { .memory = (intptr_t)malloc(1024) }
|
||||
};
|
||||
return data;
|
||||
}
|
||||
|
||||
Clay_RenderCommandArray ClayVideoDemo_CreateLayout(ClayVideoDemo_Data *data) {
|
||||
data->frameArena.offset = 0;
|
||||
|
||||
Clay_BeginLayout();
|
||||
|
||||
Clay_Sizing layoutExpand = {
|
||||
.width = CLAY_SIZING_GROW(0),
|
||||
.height = CLAY_SIZING_GROW(0)
|
||||
};
|
||||
|
||||
Clay_Color contentBackgroundColor = { 90, 90, 90, 255 };
|
||||
|
||||
// Build UI here
|
||||
CLAY({ .id = CLAY_ID("OuterContainer"),
|
||||
.backgroundColor = {43, 41, 51, 255 },
|
||||
.layout = {
|
||||
.layoutDirection = CLAY_TOP_TO_BOTTOM,
|
||||
.sizing = layoutExpand,
|
||||
.padding = CLAY_PADDING_ALL(16),
|
||||
.childGap = 16
|
||||
}
|
||||
}) {
|
||||
// Child elements go inside braces
|
||||
CLAY({ .id = CLAY_ID("HeaderBar"),
|
||||
.layout = {
|
||||
.sizing = {
|
||||
.height = CLAY_SIZING_FIXED(60),
|
||||
.width = CLAY_SIZING_GROW(0)
|
||||
},
|
||||
.padding = { 16, 16, 0, 0 },
|
||||
.childGap = 16,
|
||||
.childAlignment = {
|
||||
.y = CLAY_ALIGN_Y_CENTER
|
||||
}
|
||||
},
|
||||
.backgroundColor = contentBackgroundColor,
|
||||
.cornerRadius = CLAY_CORNER_RADIUS(8)
|
||||
}) {
|
||||
// Header buttons go here
|
||||
CLAY({ .id = CLAY_ID("FileButton"),
|
||||
.layout = { .padding = { 16, 16, 8, 8 }},
|
||||
.backgroundColor = {140, 140, 140, 255 },
|
||||
.cornerRadius = CLAY_CORNER_RADIUS(5)
|
||||
}) {
|
||||
CLAY_TEXT(CLAY_STRING("File"), CLAY_TEXT_CONFIG({
|
||||
.fontId = FONT_ID_BODY_16,
|
||||
.fontSize = 16,
|
||||
.textColor = { 255, 255, 255, 255 }
|
||||
}));
|
||||
|
||||
bool fileMenuVisible =
|
||||
Clay_PointerOver(Clay_GetElementId(CLAY_STRING("FileButton")))
|
||||
||
|
||||
Clay_PointerOver(Clay_GetElementId(CLAY_STRING("FileMenu")));
|
||||
|
||||
if (fileMenuVisible) { // Below has been changed slightly to fix the small bug where the menu would dismiss when mousing over the top gap
|
||||
CLAY({ .id = CLAY_ID("FileMenu"),
|
||||
.floating = {
|
||||
.attachTo = CLAY_ATTACH_TO_PARENT,
|
||||
.attachPoints = {
|
||||
.parent = CLAY_ATTACH_POINT_LEFT_BOTTOM
|
||||
},
|
||||
},
|
||||
.layout = {
|
||||
.padding = {0, 0, 8, 8 }
|
||||
}
|
||||
}) {
|
||||
CLAY({
|
||||
.layout = {
|
||||
.layoutDirection = CLAY_TOP_TO_BOTTOM,
|
||||
.sizing = {
|
||||
.width = CLAY_SIZING_FIXED(200)
|
||||
},
|
||||
},
|
||||
.backgroundColor = {40, 40, 40, 255 },
|
||||
.cornerRadius = CLAY_CORNER_RADIUS(8)
|
||||
}) {
|
||||
// Render dropdown items here
|
||||
RenderDropdownMenuItem(CLAY_STRING("New"));
|
||||
RenderDropdownMenuItem(CLAY_STRING("Open"));
|
||||
RenderDropdownMenuItem(CLAY_STRING("Close"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
RenderHeaderButton(CLAY_STRING("Edit"));
|
||||
CLAY({ .layout = { .sizing = { CLAY_SIZING_GROW(0) }}}) {}
|
||||
RenderHeaderButton(CLAY_STRING("Upload"));
|
||||
RenderHeaderButton(CLAY_STRING("Media"));
|
||||
RenderHeaderButton(CLAY_STRING("Support"));
|
||||
}
|
||||
|
||||
CLAY({
|
||||
.id = CLAY_ID("LowerContent"),
|
||||
.layout = { .sizing = layoutExpand, .childGap = 16 }
|
||||
}) {
|
||||
CLAY({
|
||||
.id = CLAY_ID("Sidebar"),
|
||||
.backgroundColor = contentBackgroundColor,
|
||||
.layout = {
|
||||
.layoutDirection = CLAY_TOP_TO_BOTTOM,
|
||||
.padding = CLAY_PADDING_ALL(16),
|
||||
.childGap = 8,
|
||||
.sizing = {
|
||||
.width = CLAY_SIZING_FIXED(250),
|
||||
.height = CLAY_SIZING_GROW(0)
|
||||
}
|
||||
}
|
||||
}) {
|
||||
for (int i = 0; i < documents.length; i++) {
|
||||
Document document = documents.documents[i];
|
||||
Clay_LayoutConfig sidebarButtonLayout = {
|
||||
.sizing = { .width = CLAY_SIZING_GROW(0) },
|
||||
.padding = CLAY_PADDING_ALL(16)
|
||||
};
|
||||
|
||||
if (i == data->selectedDocumentIndex) {
|
||||
CLAY({
|
||||
.layout = sidebarButtonLayout,
|
||||
.backgroundColor = {120, 120, 120, 255 },
|
||||
.cornerRadius = CLAY_CORNER_RADIUS(8)
|
||||
}) {
|
||||
CLAY_TEXT(document.title, CLAY_TEXT_CONFIG({
|
||||
.fontId = FONT_ID_BODY_16,
|
||||
.fontSize = 20,
|
||||
.textColor = { 255, 255, 255, 255 }
|
||||
}));
|
||||
}
|
||||
} else {
|
||||
SidebarClickData *clickData = (SidebarClickData *)(data->frameArena.memory + data->frameArena.offset);
|
||||
*clickData = (SidebarClickData) { .requestedDocumentIndex = i, .selectedDocumentIndex = &data->selectedDocumentIndex };
|
||||
data->frameArena.offset += sizeof(SidebarClickData);
|
||||
CLAY({ .layout = sidebarButtonLayout, .backgroundColor = (Clay_Color) { 120, 120, 120, Clay_Hovered() ? 120 : 0 }, .cornerRadius = CLAY_CORNER_RADIUS(8) }) {
|
||||
Clay_OnHover(HandleSidebarInteraction, (intptr_t)clickData);
|
||||
CLAY_TEXT(document.title, CLAY_TEXT_CONFIG({
|
||||
.fontId = FONT_ID_BODY_16,
|
||||
.fontSize = 20,
|
||||
.textColor = { 255, 255, 255, 255 }
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CLAY({ .id = CLAY_ID("MainContent"),
|
||||
.backgroundColor = contentBackgroundColor,
|
||||
.scroll = { .vertical = true },
|
||||
.layout = {
|
||||
.layoutDirection = CLAY_TOP_TO_BOTTOM,
|
||||
.childGap = 16,
|
||||
.padding = CLAY_PADDING_ALL(16),
|
||||
.sizing = layoutExpand
|
||||
}
|
||||
}) {
|
||||
Document selectedDocument = documents.documents[data->selectedDocumentIndex];
|
||||
CLAY_TEXT(selectedDocument.title, CLAY_TEXT_CONFIG({
|
||||
.fontId = FONT_ID_BODY_16,
|
||||
.fontSize = 24,
|
||||
.textColor = COLOR_WHITE
|
||||
}));
|
||||
CLAY_TEXT(selectedDocument.contents, CLAY_TEXT_CONFIG({
|
||||
.fontId = FONT_ID_BODY_16,
|
||||
.fontSize = 24,
|
||||
.textColor = COLOR_WHITE
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Clay_RenderCommandArray renderCommands = Clay_EndLayout();
|
||||
for (int32_t i = 0; i < renderCommands.length; i++) {
|
||||
Clay_RenderCommandArray_Get(&renderCommands, i)->boundingBox.y += data->yOffset;
|
||||
}
|
||||
return renderCommands;
|
||||
}
|
@ -1,7 +1,3 @@
|
||||
Please note, the SDL2 renderer is not 100% feature complete. It is currently missing:
|
||||
|
||||
- Rounded rectangle corners
|
||||
|
||||
Note: on Mac OSX, SDL2 for some reason decides to automatically disable momentum scrolling on macbook trackpads.
|
||||
You can re enable it in objective C using:
|
||||
|
||||
|
@ -4,6 +4,10 @@
|
||||
#include <SDL_image.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.14159
|
||||
#endif
|
||||
|
||||
#define CLAY_COLOR_TO_SDL_COLOR_ARGS(color) color.r, color.g, color.b, color.a
|
||||
|
||||
typedef struct
|
||||
@ -13,7 +17,7 @@ typedef struct
|
||||
} SDL2_Font;
|
||||
|
||||
|
||||
static Clay_Dimensions SDL2_MeasureText(Clay_StringSlice text, Clay_TextElementConfig *config, uintptr_t userData)
|
||||
static Clay_Dimensions SDL2_MeasureText(Clay_StringSlice text, Clay_TextElementConfig *config, void *userData)
|
||||
{
|
||||
SDL2_Font *fonts = (SDL2_Font*)userData;
|
||||
|
||||
@ -33,6 +37,229 @@ static Clay_Dimensions SDL2_MeasureText(Clay_StringSlice text, Clay_TextElementC
|
||||
};
|
||||
}
|
||||
|
||||
/* Global for convenience. Even in 4K this is enough for smooth curves (low radius or rect size coupled with
|
||||
* no AA or low resolution might make it appear as jagged curves) */
|
||||
static int NUM_CIRCLE_SEGMENTS = 16;
|
||||
|
||||
//all rendering is performed by a single SDL call, avoiding multiple RenderRect + plumbing choice for circles.
|
||||
static void SDL_RenderFillRoundedRect(SDL_Renderer* renderer, const SDL_FRect rect, const float cornerRadius, const Clay_Color _color) {
|
||||
const SDL_Color color = (SDL_Color) {
|
||||
.r = (Uint8)_color.r,
|
||||
.g = (Uint8)_color.g,
|
||||
.b = (Uint8)_color.b,
|
||||
.a = (Uint8)_color.a,
|
||||
};
|
||||
|
||||
int indexCount = 0, vertexCount = 0;
|
||||
|
||||
const float maxRadius = SDL_min(rect.w, rect.h) / 2.0f;
|
||||
const float clampedRadius = SDL_min(cornerRadius, maxRadius);
|
||||
|
||||
const int numCircleSegments = SDL_max(NUM_CIRCLE_SEGMENTS, (int)clampedRadius * 0.5f);
|
||||
|
||||
SDL_Vertex vertices[512];
|
||||
int indices[512];
|
||||
|
||||
//define center rectangle
|
||||
vertices[vertexCount++] = (SDL_Vertex){ {rect.x + clampedRadius, rect.y + clampedRadius}, color, {0, 0} }; //0 center TL
|
||||
vertices[vertexCount++] = (SDL_Vertex){ {rect.x + rect.w - clampedRadius, rect.y + clampedRadius}, color, {1, 0} }; //1 center TR
|
||||
vertices[vertexCount++] = (SDL_Vertex){ {rect.x + rect.w - clampedRadius, rect.y + rect.h - clampedRadius}, color, {1, 1} }; //2 center BR
|
||||
vertices[vertexCount++] = (SDL_Vertex){ {rect.x + clampedRadius, rect.y + rect.h - clampedRadius}, color, {0, 1} }; //3 center BL
|
||||
|
||||
indices[indexCount++] = 0;
|
||||
indices[indexCount++] = 1;
|
||||
indices[indexCount++] = 3;
|
||||
indices[indexCount++] = 1;
|
||||
indices[indexCount++] = 2;
|
||||
indices[indexCount++] = 3;
|
||||
|
||||
//define rounded corners as triangle fans
|
||||
const float step = (M_PI / 2) / numCircleSegments;
|
||||
for (int i = 0; i < numCircleSegments; i++) {
|
||||
const float angle1 = (float)i * step;
|
||||
const float angle2 = ((float)i + 1.0f) * step;
|
||||
|
||||
for (int j = 0; j < 4; j++) { // Iterate over four corners
|
||||
float cx, cy, signX, signY;
|
||||
|
||||
switch (j) {
|
||||
case 0: cx = rect.x + clampedRadius; cy = rect.y + clampedRadius; signX = -1; signY = -1; break; // Top-left
|
||||
case 1: cx = rect.x + rect.w - clampedRadius; cy = rect.y + clampedRadius; signX = 1; signY = -1; break; // Top-right
|
||||
case 2: cx = rect.x + rect.w - clampedRadius; cy = rect.y + rect.h - clampedRadius; signX = 1; signY = 1; break; // Bottom-right
|
||||
case 3: cx = rect.x + clampedRadius; cy = rect.y + rect.h - clampedRadius; signX = -1; signY = 1; break; // Bottom-left
|
||||
default: return;
|
||||
}
|
||||
|
||||
vertices[vertexCount++] = (SDL_Vertex){ {cx + SDL_cosf(angle1) * clampedRadius * signX, cy + SDL_sinf(angle1) * clampedRadius * signY}, color, {0, 0} };
|
||||
vertices[vertexCount++] = (SDL_Vertex){ {cx + SDL_cosf(angle2) * clampedRadius * signX, cy + SDL_sinf(angle2) * clampedRadius * signY}, color, {0, 0} };
|
||||
|
||||
indices[indexCount++] = j; // Connect to corresponding central rectangle vertex
|
||||
indices[indexCount++] = vertexCount - 2;
|
||||
indices[indexCount++] = vertexCount - 1;
|
||||
}
|
||||
}
|
||||
|
||||
//Define edge rectangles
|
||||
// Top edge
|
||||
vertices[vertexCount++] = (SDL_Vertex){ {rect.x + clampedRadius, rect.y}, color, {0, 0} }; //TL
|
||||
vertices[vertexCount++] = (SDL_Vertex){ {rect.x + rect.w - clampedRadius, rect.y}, color, {1, 0} }; //TR
|
||||
|
||||
indices[indexCount++] = 0;
|
||||
indices[indexCount++] = vertexCount - 2; //TL
|
||||
indices[indexCount++] = vertexCount - 1; //TR
|
||||
indices[indexCount++] = 1;
|
||||
indices[indexCount++] = 0;
|
||||
indices[indexCount++] = vertexCount - 1; //TR
|
||||
// Right edge
|
||||
vertices[vertexCount++] = (SDL_Vertex){ {rect.x + rect.w, rect.y + clampedRadius}, color, {1, 0} }; //RT
|
||||
vertices[vertexCount++] = (SDL_Vertex){ {rect.x + rect.w, rect.y + rect.h - clampedRadius}, color, {1, 1} }; //RB
|
||||
|
||||
indices[indexCount++] = 1;
|
||||
indices[indexCount++] = vertexCount - 2; //RT
|
||||
indices[indexCount++] = vertexCount - 1; //RB
|
||||
indices[indexCount++] = 2;
|
||||
indices[indexCount++] = 1;
|
||||
indices[indexCount++] = vertexCount - 1; //RB
|
||||
// Bottom edge
|
||||
vertices[vertexCount++] = (SDL_Vertex){ {rect.x + rect.w - clampedRadius, rect.y + rect.h}, color, {1, 1} }; //BR
|
||||
vertices[vertexCount++] = (SDL_Vertex){ {rect.x + clampedRadius, rect.y + rect.h}, color, {0, 1} }; //BL
|
||||
|
||||
indices[indexCount++] = 2;
|
||||
indices[indexCount++] = vertexCount - 2; //BR
|
||||
indices[indexCount++] = vertexCount - 1; //BL
|
||||
indices[indexCount++] = 3;
|
||||
indices[indexCount++] = 2;
|
||||
indices[indexCount++] = vertexCount - 1; //BL
|
||||
// Left edge
|
||||
vertices[vertexCount++] = (SDL_Vertex){ {rect.x, rect.y + rect.h - clampedRadius}, color, {0, 1} }; //LB
|
||||
vertices[vertexCount++] = (SDL_Vertex){ {rect.x, rect.y + clampedRadius}, color, {0, 0} }; //LT
|
||||
|
||||
indices[indexCount++] = 3;
|
||||
indices[indexCount++] = vertexCount - 2; //LB
|
||||
indices[indexCount++] = vertexCount - 1; //LT
|
||||
indices[indexCount++] = 0;
|
||||
indices[indexCount++] = 3;
|
||||
indices[indexCount++] = vertexCount - 1; //LT
|
||||
|
||||
// Render everything
|
||||
SDL_RenderGeometry(renderer, NULL, vertices, vertexCount, indices, indexCount);
|
||||
}
|
||||
|
||||
//all rendering is performed by a single SDL call, using twi sets of arcing triangles, inner and outer, that fit together; along with two tringles to fill the end gaps.
|
||||
void SDL_RenderCornerBorder(SDL_Renderer *renderer, Clay_BoundingBox* boundingBox, Clay_BorderRenderData* config, int cornerIndex, Clay_Color _color){
|
||||
/////////////////////////////////
|
||||
//The arc is constructed of outer triangles and inner triangles (if needed).
|
||||
//First three vertices are first outer triangle's vertices
|
||||
//Each two vertices after that are the inner-middle and second-outer vertex of
|
||||
//each outer triangle after the first, because there first-outer vertex is equal to the
|
||||
//second-outer vertex of the previous triangle. Indices set accordingly.
|
||||
//The final two vertices are the missing vertices for the first and last inner triangles (if needed)
|
||||
//Everything is in clockwise order (CW).
|
||||
/////////////////////////////////
|
||||
const SDL_Color color = (SDL_Color) {
|
||||
.r = (Uint8)_color.r,
|
||||
.g = (Uint8)_color.g,
|
||||
.b = (Uint8)_color.b,
|
||||
.a = (Uint8)_color.a,
|
||||
};
|
||||
|
||||
float centerX, centerY, outerRadius, clampedRadius, startAngle, borderWidth;
|
||||
const float maxRadius = SDL_min(boundingBox->width, boundingBox->height) / 2.0f;
|
||||
|
||||
SDL_Vertex vertices[512];
|
||||
int indices[512];
|
||||
int indexCount = 0, vertexCount = 0;
|
||||
|
||||
switch (cornerIndex) {
|
||||
case(0):
|
||||
startAngle = M_PI;
|
||||
outerRadius = SDL_min(config->cornerRadius.topLeft, maxRadius);
|
||||
centerX = boundingBox->x + outerRadius;
|
||||
centerY = boundingBox->y + outerRadius;
|
||||
borderWidth = config->width.top;
|
||||
break;
|
||||
case(1):
|
||||
startAngle = 3*M_PI/2;
|
||||
outerRadius = SDL_min(config->cornerRadius.topRight, maxRadius);
|
||||
centerX = boundingBox->x + boundingBox->width - outerRadius;
|
||||
centerY = boundingBox->y + outerRadius;
|
||||
borderWidth = config->width.top;
|
||||
break;
|
||||
case(2):
|
||||
startAngle = 0;
|
||||
outerRadius = SDL_min(config->cornerRadius.bottomRight, maxRadius);
|
||||
centerX = boundingBox->x + boundingBox->width - outerRadius;
|
||||
centerY = boundingBox->y + boundingBox->height - outerRadius;
|
||||
borderWidth = config->width.bottom;
|
||||
break;
|
||||
case(3):
|
||||
startAngle = M_PI/2;
|
||||
outerRadius = SDL_min(config->cornerRadius.bottomLeft, maxRadius);
|
||||
centerX = boundingBox->x + outerRadius;
|
||||
centerY = boundingBox->y + boundingBox->height - outerRadius;
|
||||
borderWidth = config->width.bottom;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
const float innerRadius = outerRadius - borderWidth;
|
||||
const int minNumOuterTriangles = NUM_CIRCLE_SEGMENTS;
|
||||
const int numOuterTriangles = SDL_max(minNumOuterTriangles, ceilf(outerRadius * 0.5f));
|
||||
const float angleStep = M_PI / (2.0*(float)numOuterTriangles);
|
||||
|
||||
//outer triangles, in CW order
|
||||
for (int i = 0; i < numOuterTriangles; i++) {
|
||||
float angle1 = startAngle + i*angleStep; //first-outer vertex angle
|
||||
float angle2 = startAngle + ((float)i + 0.5) * angleStep; //inner-middle vertex angle
|
||||
float angle3 = startAngle + (i+1)*angleStep; // second-outer vertex angle
|
||||
|
||||
if( i == 0){ //first outer triangle
|
||||
vertices[vertexCount++] = (SDL_Vertex){ {centerX + SDL_cosf(angle1) * outerRadius, centerY + SDL_sinf(angle1) * outerRadius}, color, {0, 0} }; //vertex index = 0
|
||||
}
|
||||
indices[indexCount++] = vertexCount - 1; //will be second-outer vertex of last outer triangle if not first outer triangle.
|
||||
|
||||
vertices[vertexCount++] = (innerRadius > 0)?
|
||||
(SDL_Vertex){ {centerX + SDL_cosf(angle2) * (innerRadius), centerY + SDL_sinf(angle2) * (innerRadius)}, color, {0, 0}}:
|
||||
(SDL_Vertex){ {centerX, centerY }, color, {0, 0}};
|
||||
indices[indexCount++] = vertexCount - 1;
|
||||
|
||||
vertices[vertexCount++] = (SDL_Vertex){ {centerX + SDL_cosf(angle3) * outerRadius, centerY + SDL_sinf(angle3) * outerRadius}, color, {0, 0} };
|
||||
indices[indexCount++] = vertexCount - 1;
|
||||
}
|
||||
|
||||
if(innerRadius > 0){
|
||||
// inner triangles in CW order (except the first and last)
|
||||
for (int i = 0; i < numOuterTriangles - 1; i++){ //skip the last outer triangle
|
||||
if(i==0){ //first outer triangle -> second inner triangle
|
||||
indices[indexCount++] = 1; //inner-middle vertex of first outer triangle
|
||||
indices[indexCount++] = 2; //second-outer vertex of first outer triangle
|
||||
indices[indexCount++] = 3; //innder-middle vertex of second-outer triangle
|
||||
}else{
|
||||
int baseIndex = 3; //skip first outer triangle
|
||||
indices[indexCount++] = baseIndex + (i-1)*2; // inner-middle vertex of current outer triangle
|
||||
indices[indexCount++] = baseIndex + (i-1)*2 + 1; // second-outer vertex of current outer triangle
|
||||
indices[indexCount++] = baseIndex + (i-1)*2 + 2; // inner-middle vertex of next outer triangle
|
||||
}
|
||||
}
|
||||
|
||||
float endAngle = startAngle + M_PI/2.0;
|
||||
|
||||
//last inner triangle
|
||||
indices[indexCount++] = vertexCount - 2; //inner-middle vertex of last outer triangle
|
||||
indices[indexCount++] = vertexCount - 1; //second-outer vertex of last outer triangle
|
||||
vertices[vertexCount++] = (SDL_Vertex){ {centerX + SDL_cosf(endAngle) * innerRadius, centerY + SDL_sinf(endAngle) * innerRadius}, color, {0, 0} }; //missing vertex
|
||||
indices[indexCount++] = vertexCount - 1;
|
||||
|
||||
// //first inner triangle
|
||||
indices[indexCount++] = 0; //first-outer vertex of first outer triangle
|
||||
indices[indexCount++] = 1; //inner-middle vertex of first outer triangle
|
||||
vertices[vertexCount++] = (SDL_Vertex){ {centerX + SDL_cosf(startAngle) * innerRadius, centerY + SDL_sinf(startAngle) * innerRadius}, color, {0, 0} }; //missing vertex
|
||||
indices[indexCount++] = vertexCount - 1;
|
||||
}
|
||||
|
||||
SDL_RenderGeometry(renderer, NULL, vertices, vertexCount, indices, indexCount);
|
||||
}
|
||||
|
||||
SDL_Rect currentClippingRectangle;
|
||||
|
||||
static void Clay_SDL2_Render(SDL_Renderer *renderer, Clay_RenderCommandArray renderCommands, SDL2_Font *fonts)
|
||||
@ -44,8 +271,8 @@ static void Clay_SDL2_Render(SDL_Renderer *renderer, Clay_RenderCommandArray ren
|
||||
switch (renderCommand->commandType)
|
||||
{
|
||||
case CLAY_RENDER_COMMAND_TYPE_RECTANGLE: {
|
||||
Clay_RectangleElementConfig *config = renderCommand->config.rectangleElementConfig;
|
||||
Clay_Color color = config->color;
|
||||
Clay_RectangleRenderData *config = &renderCommand->renderData.rectangle;
|
||||
Clay_Color color = config->backgroundColor;
|
||||
SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, color.a);
|
||||
SDL_FRect rect = (SDL_FRect) {
|
||||
.x = boundingBox.x,
|
||||
@ -53,14 +280,18 @@ static void Clay_SDL2_Render(SDL_Renderer *renderer, Clay_RenderCommandArray ren
|
||||
.w = boundingBox.width,
|
||||
.h = boundingBox.height,
|
||||
};
|
||||
if (config->cornerRadius.topLeft > 0) {
|
||||
SDL_RenderFillRoundedRect(renderer, rect, config->cornerRadius.topLeft, color);
|
||||
}
|
||||
else {
|
||||
SDL_RenderFillRectF(renderer, &rect);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CLAY_RENDER_COMMAND_TYPE_TEXT: {
|
||||
Clay_TextElementConfig *config = renderCommand->config.textElementConfig;
|
||||
Clay_StringSlice text = renderCommand->text;
|
||||
char *cloned = (char *)calloc(text.length + 1, 1);
|
||||
memcpy(cloned, text.chars, text.length);
|
||||
Clay_TextRenderData *config = &renderCommand->renderData.text;
|
||||
char *cloned = (char *)calloc(config->stringContents.length + 1, 1);
|
||||
memcpy(cloned, config->stringContents.chars, config->stringContents.length);
|
||||
TTF_Font* font = fonts[config->fontId].font;
|
||||
SDL_Surface *surface = TTF_RenderUTF8_Blended(font, cloned, (SDL_Color) {
|
||||
.r = (Uint8)config->textColor.r,
|
||||
@ -98,9 +329,9 @@ static void Clay_SDL2_Render(SDL_Renderer *renderer, Clay_RenderCommandArray ren
|
||||
break;
|
||||
}
|
||||
case CLAY_RENDER_COMMAND_TYPE_IMAGE: {
|
||||
SDL_Surface *image = (SDL_Surface *)renderCommand->config.imageElementConfig->imageData;
|
||||
Clay_ImageRenderData *config = &renderCommand->renderData.image;
|
||||
|
||||
SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer, image);
|
||||
SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer, config->imageData);
|
||||
|
||||
SDL_Rect destination = (SDL_Rect){
|
||||
.x = boundingBox.x,
|
||||
@ -110,39 +341,80 @@ static void Clay_SDL2_Render(SDL_Renderer *renderer, Clay_RenderCommandArray ren
|
||||
};
|
||||
|
||||
SDL_RenderCopy(renderer, texture, NULL, &destination);
|
||||
|
||||
SDL_DestroyTexture(texture);
|
||||
break;
|
||||
}
|
||||
case CLAY_RENDER_COMMAND_TYPE_BORDER: {
|
||||
Clay_BorderElementConfig *config = renderCommand->config.borderElementConfig;
|
||||
Clay_BorderRenderData *config = &renderCommand->renderData.border;
|
||||
SDL_SetRenderDrawColor(renderer, CLAY_COLOR_TO_SDL_COLOR_ARGS(config->color));
|
||||
|
||||
if (config->left.width > 0) {
|
||||
SDL_SetRenderDrawColor(renderer, CLAY_COLOR_TO_SDL_COLOR_ARGS(config->left.color));
|
||||
SDL_FRect rect = { boundingBox.x, boundingBox.y + config->cornerRadius.topLeft, config->left.width, boundingBox.height - config->cornerRadius.topLeft - config->cornerRadius.bottomLeft };
|
||||
if(boundingBox.width > 0 & boundingBox.height > 0){
|
||||
const float maxRadius = SDL_min(boundingBox.width, boundingBox.height) / 2.0f;
|
||||
|
||||
if (config->width.left > 0) {
|
||||
const float clampedRadiusTop = SDL_min((float)config->cornerRadius.topLeft, maxRadius);
|
||||
const float clampedRadiusBottom = SDL_min((float)config->cornerRadius.bottomLeft, maxRadius);
|
||||
SDL_FRect rect = {
|
||||
boundingBox.x,
|
||||
boundingBox.y + clampedRadiusTop,
|
||||
(float)config->width.left,
|
||||
(float)boundingBox.height - clampedRadiusTop - clampedRadiusBottom
|
||||
};
|
||||
SDL_RenderFillRectF(renderer, &rect);
|
||||
}
|
||||
|
||||
if (config->right.width > 0) {
|
||||
SDL_SetRenderDrawColor(renderer, CLAY_COLOR_TO_SDL_COLOR_ARGS(config->right.color));
|
||||
SDL_FRect rect = { boundingBox.x + boundingBox.width - config->right.width, boundingBox.y + config->cornerRadius.topRight, config->right.width, boundingBox.height - config->cornerRadius.topRight - config->cornerRadius.bottomRight };
|
||||
if (config->width.right > 0) {
|
||||
const float clampedRadiusTop = SDL_min((float)config->cornerRadius.topRight, maxRadius);
|
||||
const float clampedRadiusBottom = SDL_min((float)config->cornerRadius.bottomRight, maxRadius);
|
||||
SDL_FRect rect = {
|
||||
boundingBox.x + boundingBox.width - config->width.right,
|
||||
boundingBox.y + clampedRadiusTop,
|
||||
(float)config->width.right,
|
||||
(float)boundingBox.height - clampedRadiusTop - clampedRadiusBottom
|
||||
};
|
||||
SDL_RenderFillRectF(renderer, &rect);
|
||||
}
|
||||
|
||||
if (config->right.width > 0) {
|
||||
SDL_SetRenderDrawColor(renderer, CLAY_COLOR_TO_SDL_COLOR_ARGS(config->right.color));
|
||||
SDL_FRect rect = { boundingBox.x + boundingBox.width - config->right.width, boundingBox.y + config->cornerRadius.topRight, config->right.width, boundingBox.height - config->cornerRadius.topRight - config->cornerRadius.bottomRight };
|
||||
if (config->width.top > 0) {
|
||||
const float clampedRadiusLeft = SDL_min((float)config->cornerRadius.topLeft, maxRadius);
|
||||
const float clampedRadiusRight = SDL_min((float)config->cornerRadius.topRight, maxRadius);
|
||||
SDL_FRect rect = {
|
||||
boundingBox.x + clampedRadiusLeft,
|
||||
boundingBox.y,
|
||||
boundingBox.width - clampedRadiusLeft - clampedRadiusRight,
|
||||
(float)config->width.top };
|
||||
SDL_RenderFillRectF(renderer, &rect);
|
||||
}
|
||||
|
||||
if (config->top.width > 0) {
|
||||
SDL_SetRenderDrawColor(renderer, CLAY_COLOR_TO_SDL_COLOR_ARGS(config->right.color));
|
||||
SDL_FRect rect = { boundingBox.x + config->cornerRadius.topLeft, boundingBox.y, boundingBox.width - config->cornerRadius.topLeft - config->cornerRadius.topRight, config->top.width };
|
||||
if (config->width.bottom > 0) {
|
||||
const float clampedRadiusLeft = SDL_min((float)config->cornerRadius.bottomLeft, maxRadius);
|
||||
const float clampedRadiusRight = SDL_min((float)config->cornerRadius.bottomRight, maxRadius);
|
||||
SDL_FRect rect = {
|
||||
boundingBox.x + clampedRadiusLeft,
|
||||
boundingBox.y + boundingBox.height - config->width.bottom,
|
||||
boundingBox.width - clampedRadiusLeft - clampedRadiusRight,
|
||||
(float)config->width.bottom
|
||||
};
|
||||
SDL_RenderFillRectF(renderer, &rect);
|
||||
}
|
||||
|
||||
if (config->bottom.width > 0) {
|
||||
SDL_SetRenderDrawColor(renderer, CLAY_COLOR_TO_SDL_COLOR_ARGS(config->bottom.color));
|
||||
SDL_FRect rect = { boundingBox.x + config->cornerRadius.bottomLeft, boundingBox.y + boundingBox.height - config->bottom.width, boundingBox.width - config->cornerRadius.bottomLeft - config->cornerRadius.bottomRight, config->bottom.width };
|
||||
SDL_RenderFillRectF(renderer, &rect);
|
||||
//corner index: 0->3 topLeft -> CW -> bottonLeft
|
||||
if (config->width.top > 0 & config->cornerRadius.topLeft > 0) {
|
||||
SDL_RenderCornerBorder(renderer, &boundingBox, config, 0, config->color);
|
||||
}
|
||||
|
||||
if (config->width.top > 0 & config->cornerRadius.topRight> 0) {
|
||||
SDL_RenderCornerBorder(renderer, &boundingBox, config, 1, config->color);
|
||||
}
|
||||
|
||||
if (config->width.bottom > 0 & config->cornerRadius.bottomLeft > 0) {
|
||||
SDL_RenderCornerBorder(renderer, &boundingBox, config, 2, config->color);
|
||||
}
|
||||
|
||||
if (config->width.bottom > 0 & config->cornerRadius.bottomLeft > 0) {
|
||||
SDL_RenderCornerBorder(renderer, &boundingBox, config, 3, config->color);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -1,4 +0,0 @@
|
||||
Please note, the SDL3 renderer is not 100% feature complete. It is currently missing:
|
||||
|
||||
- Images
|
||||
- Scroll / Scissor handling
|
@ -2,16 +2,20 @@
|
||||
#include <SDL3/SDL_main.h>
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3_ttf/SDL_ttf.h>
|
||||
#include <SDL3_image/SDL_image.h>
|
||||
|
||||
typedef struct {
|
||||
SDL_Renderer *renderer;
|
||||
TTF_TextEngine *textEngine;
|
||||
TTF_Font **fonts;
|
||||
} Clay_SDL3RendererData;
|
||||
|
||||
/* This needs to be global because the "MeasureText" callback doesn't have a
|
||||
* user data parameter */
|
||||
static TTF_Font *gFonts[1];
|
||||
/* Global for convenience. Even in 4K this is enough for smooth curves (low radius or rect size coupled with
|
||||
* no AA or low resolution might make it appear as jagged curves) */
|
||||
static int NUM_CIRCLE_SEGMENTS = 16;
|
||||
|
||||
//all rendering is performed by a single SDL call, avoiding multiple RenderRect + plumbing choice for circles.
|
||||
static void SDL_RenderFillRoundedRect(SDL_Renderer *renderer, const SDL_FRect rect, const float cornerRadius, const Clay_Color _color) {
|
||||
static void SDL_Clay_RenderFillRoundedRect(Clay_SDL3RendererData *rendererData, const SDL_FRect rect, const float cornerRadius, const Clay_Color _color) {
|
||||
const SDL_FColor color = { _color.r/255, _color.g/255, _color.b/255, _color.a/255 };
|
||||
|
||||
int indexCount = 0, vertexCount = 0;
|
||||
@ -109,11 +113,11 @@ static void SDL_RenderFillRoundedRect(SDL_Renderer *renderer, const SDL_FRect re
|
||||
indices[indexCount++] = vertexCount - 1; //LT
|
||||
|
||||
// Render everything
|
||||
SDL_RenderGeometry(renderer, NULL, vertices, vertexCount, indices, indexCount);
|
||||
SDL_RenderGeometry(rendererData->renderer, NULL, vertices, vertexCount, indices, indexCount);
|
||||
}
|
||||
|
||||
static void SDL_RenderArc(SDL_Renderer *renderer, const SDL_FPoint center, const float radius, const float startAngle, const float endAngle, const float thickness, const Clay_Color color) {
|
||||
SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, color.a);
|
||||
static void SDL_Clay_RenderArc(Clay_SDL3RendererData *rendererData, const SDL_FPoint center, const float radius, const float startAngle, const float endAngle, const float thickness, const Clay_Color color) {
|
||||
SDL_SetRenderDrawColor(rendererData->renderer, color.r, color.g, color.b, color.a);
|
||||
|
||||
const float radStart = startAngle * (SDL_PI_F / 180.0f);
|
||||
const float radEnd = endAngle * (SDL_PI_F / 180.0f);
|
||||
@ -133,44 +137,39 @@ static void SDL_RenderArc(SDL_Renderer *renderer, const SDL_FPoint center, const
|
||||
SDL_roundf(center.x + SDL_cosf(angle) * clampedRadius),
|
||||
SDL_roundf(center.y + SDL_sinf(angle) * clampedRadius) };
|
||||
}
|
||||
SDL_RenderLines(renderer, points, numCircleSegments + 1);
|
||||
SDL_RenderLines(rendererData->renderer, points, numCircleSegments + 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void SDL_RenderClayCommands(SDL_Renderer *renderer, Clay_RenderCommandArray *rcommands)
|
||||
SDL_Rect currentClippingRectangle;
|
||||
|
||||
static void SDL_Clay_RenderClayCommands(Clay_SDL3RendererData *rendererData, Clay_RenderCommandArray *rcommands)
|
||||
{
|
||||
for (size_t i = 0; i < rcommands->length; i++) {
|
||||
Clay_RenderCommand *rcmd = Clay_RenderCommandArray_Get(rcommands, i);
|
||||
const Clay_BoundingBox bounding_box = rcmd->boundingBox;
|
||||
const SDL_FRect rect = { bounding_box.x, bounding_box.y, bounding_box.width, bounding_box.height };
|
||||
const SDL_FRect rect = { (int)bounding_box.x, (int)bounding_box.y, (int)bounding_box.width, (int)bounding_box.height };
|
||||
|
||||
switch (rcmd->commandType) {
|
||||
case CLAY_RENDER_COMMAND_TYPE_RECTANGLE: {
|
||||
const Clay_RectangleElementConfig *config = rcmd->config.rectangleElementConfig;
|
||||
const Clay_Color color = config->color;
|
||||
|
||||
SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, color.a);
|
||||
Clay_RectangleRenderData *config = &rcmd->renderData.rectangle;
|
||||
SDL_SetRenderDrawColor(rendererData->renderer, config->backgroundColor.r, config->backgroundColor.g, config->backgroundColor.b, config->backgroundColor.a);
|
||||
if (config->cornerRadius.topLeft > 0) {
|
||||
SDL_RenderFillRoundedRect(renderer, rect, config->cornerRadius.topLeft, color);
|
||||
SDL_Clay_RenderFillRoundedRect(rendererData, rect, config->cornerRadius.topLeft, config->backgroundColor);
|
||||
} else {
|
||||
SDL_RenderFillRect(renderer, &rect);
|
||||
SDL_RenderFillRect(rendererData->renderer, &rect);
|
||||
}
|
||||
} break;
|
||||
case CLAY_RENDER_COMMAND_TYPE_TEXT: {
|
||||
const Clay_TextElementConfig *config = rcmd->config.textElementConfig;
|
||||
const Clay_StringSlice *text = &rcmd->text;
|
||||
const SDL_Color color = { config->textColor.r, config->textColor.g, config->textColor.b, config->textColor.a };
|
||||
|
||||
TTF_Font *font = gFonts[config->fontId];
|
||||
SDL_Surface *surface = TTF_RenderText_Blended(font, text->chars, text->length, color);
|
||||
SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer, surface);
|
||||
SDL_RenderTexture(renderer, texture, NULL, &rect);
|
||||
|
||||
SDL_DestroySurface(surface);
|
||||
SDL_DestroyTexture(texture);
|
||||
Clay_TextRenderData *config = &rcmd->renderData.text;
|
||||
TTF_Font *font = rendererData->fonts[config->fontId];
|
||||
TTF_Text *text = TTF_CreateText(rendererData->textEngine, font, config->stringContents.chars, config->stringContents.length);
|
||||
TTF_SetTextColor(text, config->textColor.r, config->textColor.g, config->textColor.b, config->textColor.a);
|
||||
TTF_DrawRendererText(text, rect.x, rect.y);
|
||||
TTF_DestroyText(text);
|
||||
} break;
|
||||
case CLAY_RENDER_COMMAND_TYPE_BORDER: {
|
||||
const Clay_BorderElementConfig *config = rcmd->config.borderElementConfig;
|
||||
Clay_BorderRenderData *config = &rcmd->renderData.border;
|
||||
|
||||
const float minRadius = SDL_min(rect.w, rect.h) / 2.0f;
|
||||
const Clay_CornerRadius clampedRadii = {
|
||||
@ -180,61 +179,85 @@ static void SDL_RenderClayCommands(SDL_Renderer *renderer, Clay_RenderCommandArr
|
||||
.bottomRight = SDL_min(config->cornerRadius.bottomRight, minRadius)
|
||||
};
|
||||
//edges
|
||||
SDL_SetRenderDrawColor(renderer, config->left.color.r, config->left.color.g, config->left.color.b, config->left.color.a);
|
||||
if (config->left.width > 0) {
|
||||
SDL_SetRenderDrawColor(rendererData->renderer, config->color.r, config->color.g, config->color.b, config->color.a);
|
||||
if (config->width.left > 0) {
|
||||
const float starting_y = rect.y + clampedRadii.topLeft;
|
||||
const float length = rect.h - clampedRadii.topLeft - clampedRadii.bottomLeft;
|
||||
SDL_FRect line = { rect.x, starting_y, config->left.width, length };
|
||||
SDL_RenderFillRect(renderer, &line);
|
||||
SDL_FRect line = { rect.x, starting_y, config->width.left, length };
|
||||
SDL_RenderFillRect(rendererData->renderer, &line);
|
||||
}
|
||||
if (config->right.width > 0) {
|
||||
const float starting_x = rect.x + rect.w - (float)config->right.width;
|
||||
if (config->width.right > 0) {
|
||||
const float starting_x = rect.x + rect.w - (float)config->width.right;
|
||||
const float starting_y = rect.y + clampedRadii.topRight;
|
||||
const float length = rect.h - clampedRadii.topRight - clampedRadii.bottomRight;
|
||||
SDL_FRect line = { starting_x, starting_y, config->right.width, length };
|
||||
SDL_RenderFillRect(renderer, &line);
|
||||
SDL_FRect line = { starting_x, starting_y, config->width.right, length };
|
||||
SDL_RenderFillRect(rendererData->renderer, &line);
|
||||
}
|
||||
if (config->top.width > 0) {
|
||||
if (config->width.top > 0) {
|
||||
const float starting_x = rect.x + clampedRadii.topLeft;
|
||||
const float length = rect.w - clampedRadii.topLeft - clampedRadii.topRight;
|
||||
SDL_FRect line = { starting_x, rect.y, length, config->top.width };
|
||||
SDL_RenderFillRect(renderer, &line);
|
||||
SDL_FRect line = { starting_x, rect.y, length, config->width.top };
|
||||
SDL_RenderFillRect(rendererData->renderer, &line);
|
||||
}
|
||||
if (config->bottom.width > 0) {
|
||||
if (config->width.bottom > 0) {
|
||||
const float starting_x = rect.x + clampedRadii.bottomLeft;
|
||||
const float starting_y = rect.y + rect.h - (float)config->bottom.width;
|
||||
const float starting_y = rect.y + rect.h - (float)config->width.bottom;
|
||||
const float length = rect.w - clampedRadii.bottomLeft - clampedRadii.bottomRight;
|
||||
SDL_FRect line = { starting_x, starting_y, length, config->bottom.width };
|
||||
SDL_SetRenderDrawColor(renderer, config->bottom.color.r, config->bottom.color.g, config->bottom.color.b, config->bottom.color.a);
|
||||
SDL_RenderFillRect(renderer, &line);
|
||||
SDL_FRect line = { starting_x, starting_y, length, config->width.bottom };
|
||||
SDL_SetRenderDrawColor(rendererData->renderer, config->color.r, config->color.g, config->color.b, config->color.a);
|
||||
SDL_RenderFillRect(rendererData->renderer, &line);
|
||||
}
|
||||
//corners
|
||||
if (config->cornerRadius.topLeft > 0) {
|
||||
const float centerX = rect.x + clampedRadii.topLeft -1;
|
||||
const float centerY = rect.y + clampedRadii.topLeft;
|
||||
SDL_RenderArc(renderer, (SDL_FPoint){centerX, centerY}, clampedRadii.topLeft,
|
||||
180.0f, 270.0f, config->top.width, config->top.color);
|
||||
SDL_Clay_RenderArc(rendererData, (SDL_FPoint){centerX, centerY}, clampedRadii.topLeft,
|
||||
180.0f, 270.0f, config->width.top, config->color);
|
||||
}
|
||||
if (config->cornerRadius.topRight > 0) {
|
||||
const float centerX = rect.x + rect.w - clampedRadii.topRight -1;
|
||||
const float centerY = rect.y + clampedRadii.topRight;
|
||||
SDL_RenderArc(renderer, (SDL_FPoint){centerX, centerY}, clampedRadii.topRight,
|
||||
270.0f, 360.0f, config->top.width, config->top.color);
|
||||
SDL_Clay_RenderArc(rendererData, (SDL_FPoint){centerX, centerY}, clampedRadii.topRight,
|
||||
270.0f, 360.0f, config->width.top, config->color);
|
||||
}
|
||||
if (config->cornerRadius.bottomLeft > 0) {
|
||||
const float centerX = rect.x + clampedRadii.bottomLeft -1;
|
||||
const float centerY = rect.y + rect.h - clampedRadii.bottomLeft -1;
|
||||
SDL_RenderArc(renderer, (SDL_FPoint){centerX, centerY}, clampedRadii.bottomLeft,
|
||||
90.0f, 180.0f, config->bottom.width, config->bottom.color);
|
||||
SDL_Clay_RenderArc(rendererData, (SDL_FPoint){centerX, centerY}, clampedRadii.bottomLeft,
|
||||
90.0f, 180.0f, config->width.bottom, config->color);
|
||||
}
|
||||
if (config->cornerRadius.bottomRight > 0) {
|
||||
const float centerX = rect.x + rect.w - clampedRadii.bottomRight -1; //TODO: why need to -1 in all calculations???
|
||||
const float centerY = rect.y + rect.h - clampedRadii.bottomRight -1;
|
||||
SDL_RenderArc(renderer, (SDL_FPoint){centerX, centerY}, clampedRadii.bottomRight,
|
||||
0.0f, 90.0f, config->bottom.width, config->bottom.color);
|
||||
SDL_Clay_RenderArc(rendererData, (SDL_FPoint){centerX, centerY}, clampedRadii.bottomRight,
|
||||
0.0f, 90.0f, config->width.bottom, config->color);
|
||||
}
|
||||
|
||||
} break;
|
||||
case CLAY_RENDER_COMMAND_TYPE_SCISSOR_START: {
|
||||
Clay_BoundingBox boundingBox = rcmd->boundingBox;
|
||||
currentClippingRectangle = (SDL_Rect) {
|
||||
.x = boundingBox.x,
|
||||
.y = boundingBox.y,
|
||||
.w = boundingBox.width,
|
||||
.h = boundingBox.height,
|
||||
};
|
||||
SDL_SetRenderClipRect(rendererData->renderer, ¤tClippingRectangle);
|
||||
break;
|
||||
}
|
||||
case CLAY_RENDER_COMMAND_TYPE_SCISSOR_END: {
|
||||
SDL_SetRenderClipRect(rendererData->renderer, NULL);
|
||||
break;
|
||||
}
|
||||
case CLAY_RENDER_COMMAND_TYPE_IMAGE: {
|
||||
SDL_Surface *image = (SDL_Surface *)rcmd->renderData.image.imageData;
|
||||
SDL_Texture *texture = SDL_CreateTextureFromSurface(rendererData->renderer, image);
|
||||
const SDL_FRect dest = { rect.x, rect.y, rect.w, rect.h };
|
||||
|
||||
SDL_RenderTexture(rendererData->renderer, texture, NULL, &dest);
|
||||
SDL_DestroyTexture(texture);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
SDL_Log("Unknown render command type: %d", rcmd->commandType);
|
||||
}
|
||||
|
@ -26,14 +26,6 @@
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
// TODO: Regarding image support, currently this renderer only
|
||||
// supports PNG images, this is due to cairo having just PNG as it's
|
||||
// main file format. We maybe should introduce stb_image to load them
|
||||
// as bitmaps and feed cairo that way.
|
||||
#define CLAY_EXTEND_CONFIG_IMAGE Clay_String path; // Filesystem path
|
||||
|
||||
// TODO: We should use the given `uint16_t fontId` instead of doing this.
|
||||
#define CLAY_EXTEND_CONFIG_TEXT Clay_String fontFamily; // Font family
|
||||
#define CLAY_IMPLEMENTATION
|
||||
#include "../../clay.h"
|
||||
|
||||
@ -50,7 +42,7 @@ void Clay_Cairo_Initialize(cairo_t *cairo);
|
||||
|
||||
// Render the command queue to the `cairo_t*` instance you called
|
||||
// `Clay_Cairo_Initialize` on.
|
||||
void Clay_Cairo_Render(Clay_RenderCommandArray commands);
|
||||
void Clay_Cairo_Render(Clay_RenderCommandArray commands, char** fonts);
|
||||
////////////////////////////////
|
||||
|
||||
|
||||
@ -83,12 +75,13 @@ static inline char *Clay_Cairo__NullTerminate(Clay_String *str) {
|
||||
}
|
||||
|
||||
// Measure text using cairo's *toy* text API.
|
||||
static inline Clay_Dimensions Clay_Cairo_MeasureText(Clay_String *str, Clay_TextElementConfig *config, uintptr_t userData) {
|
||||
static inline Clay_Dimensions Clay_Cairo_MeasureText(Clay_StringSlice str, Clay_TextElementConfig *config, uintptr_t userData) {
|
||||
// Edge case: Clay computes the width of a whitespace character
|
||||
// once. Cairo does not factor in whitespaces when computing text
|
||||
// extents, this edge-case serves as a short-circuit to introduce
|
||||
// (somewhat) sensible values into Clay.
|
||||
if(str->length == 1 && str->chars[0] == ' ') {
|
||||
char** fonts = (char**)userData;
|
||||
if(str.length == 1 && str.chars[0] == ' ') {
|
||||
cairo_text_extents_t te;
|
||||
cairo_text_extents(Clay__Cairo, " ", &te);
|
||||
return (Clay_Dimensions) {
|
||||
@ -102,8 +95,9 @@ static inline Clay_Dimensions Clay_Cairo_MeasureText(Clay_String *str, Clay_Text
|
||||
}
|
||||
|
||||
// Ensure string is null-terminated for Cairo
|
||||
char *text = Clay_Cairo__NullTerminate(str);
|
||||
char *font_family = Clay_Cairo__NullTerminate(&config->fontFamily);
|
||||
Clay_String toTerminate = (Clay_String){ str.length, str.chars };
|
||||
char *text = Clay_Cairo__NullTerminate(&toTerminate);
|
||||
char *font_family = fonts[config->fontId];
|
||||
|
||||
// Save and reset the Cairo context to avoid unwanted transformations
|
||||
cairo_save(Clay__Cairo);
|
||||
@ -119,7 +113,6 @@ static inline Clay_Dimensions Clay_Cairo_MeasureText(Clay_String *str, Clay_Text
|
||||
fprintf(stderr, "Failed to get scaled font\n");
|
||||
cairo_restore(Clay__Cairo);
|
||||
free(text);
|
||||
free(font_family);
|
||||
return (Clay_Dimensions){0, 0};
|
||||
}
|
||||
|
||||
@ -133,7 +126,6 @@ static inline Clay_Dimensions Clay_Cairo_MeasureText(Clay_String *str, Clay_Text
|
||||
fprintf(stderr, "Failed to generate glyphs: %s\n", cairo_status_to_string(status));
|
||||
cairo_restore(Clay__Cairo);
|
||||
free(text);
|
||||
free(font_family);
|
||||
return (Clay_Dimensions){0, 0};
|
||||
}
|
||||
|
||||
@ -149,7 +141,6 @@ static inline Clay_Dimensions Clay_Cairo_MeasureText(Clay_String *str, Clay_Text
|
||||
|
||||
// Free temporary strings
|
||||
free(text);
|
||||
free(font_family);
|
||||
|
||||
// Return dimensions
|
||||
return (Clay_Dimensions){
|
||||
@ -191,18 +182,17 @@ void Clay_Cairo__Blit_Surface(cairo_surface_t *src_surface, cairo_surface_t *des
|
||||
cairo_destroy(cr);
|
||||
}
|
||||
|
||||
void Clay_Cairo_Render(Clay_RenderCommandArray commands) {
|
||||
void Clay_Cairo_Render(Clay_RenderCommandArray commands, char** fonts) {
|
||||
cairo_t *cr = Clay__Cairo;
|
||||
for(size_t i = 0; i < commands.length; i++) {
|
||||
Clay_RenderCommand *command = Clay_RenderCommandArray_Get(&commands, i);
|
||||
|
||||
switch(command->commandType) {
|
||||
case CLAY_RENDER_COMMAND_TYPE_RECTANGLE: {
|
||||
Clay_RectangleElementConfig *config = command->config.rectangleElementConfig;
|
||||
Clay_Color color = config->color;
|
||||
Clay_RectangleRenderData *config = &command->renderData.rectangle;
|
||||
Clay_BoundingBox bb = command->boundingBox;
|
||||
|
||||
cairo_set_source_rgba(cr, CLAY_TO_CAIRO(color));
|
||||
cairo_set_source_rgba(cr, CLAY_TO_CAIRO(config->backgroundColor));
|
||||
|
||||
cairo_new_sub_path(cr);
|
||||
cairo_arc(cr, bb.x + config->cornerRadius.topLeft,
|
||||
@ -229,14 +219,16 @@ void Clay_Cairo_Render(Clay_RenderCommandArray commands) {
|
||||
case CLAY_RENDER_COMMAND_TYPE_TEXT: {
|
||||
// Cairo expects null terminated strings, we need to clone
|
||||
// to temporarily introduce one.
|
||||
char *text = Clay_Cairo__NullTerminate(&command->text);
|
||||
char *font_family = Clay_Cairo__NullTerminate(&command->config.textElementConfig->fontFamily);
|
||||
Clay_TextRenderData *config = &command->renderData.text;
|
||||
Clay_String toTerminate = (Clay_String){ config->stringContents.length, config->stringContents.chars };
|
||||
char *text = Clay_Cairo__NullTerminate(&toTerminate);
|
||||
char *font_family = fonts[config->fontId];
|
||||
|
||||
Clay_BoundingBox bb = command->boundingBox;
|
||||
Clay_Color color = command->config.textElementConfig->textColor;
|
||||
Clay_Color color = config->textColor;
|
||||
|
||||
cairo_select_font_face(Clay__Cairo, font_family, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
|
||||
cairo_set_font_size(cr, command->config.textElementConfig->fontSize);
|
||||
cairo_set_font_size(cr, config->fontSize);
|
||||
|
||||
cairo_move_to(cr, bb.x, bb.y + bb.height);
|
||||
|
||||
@ -245,11 +237,10 @@ void Clay_Cairo_Render(Clay_RenderCommandArray commands) {
|
||||
cairo_close_path(cr);
|
||||
|
||||
free(text);
|
||||
free(font_family);
|
||||
break;
|
||||
}
|
||||
case CLAY_RENDER_COMMAND_TYPE_BORDER: {
|
||||
Clay_BorderElementConfig *config = command->config.borderElementConfig;
|
||||
Clay_BorderRenderData *config = &command->renderData.border;
|
||||
Clay_BoundingBox bb = command->boundingBox;
|
||||
|
||||
double top_left_radius = config->cornerRadius.topLeft / 2.0;
|
||||
@ -258,9 +249,9 @@ void Clay_Cairo_Render(Clay_RenderCommandArray commands) {
|
||||
double bottom_left_radius = config->cornerRadius.bottomLeft / 2.0;
|
||||
|
||||
// Draw the top border
|
||||
if (config->top.width > 0) {
|
||||
cairo_set_line_width(cr, config->top.width);
|
||||
cairo_set_source_rgba(cr, CLAY_TO_CAIRO(config->top.color));
|
||||
if (config->width.top > 0) {
|
||||
cairo_set_line_width(cr, config->width.top);
|
||||
cairo_set_source_rgba(cr, CLAY_TO_CAIRO(config->color));
|
||||
|
||||
cairo_new_sub_path(cr);
|
||||
|
||||
@ -277,9 +268,9 @@ void Clay_Cairo_Render(Clay_RenderCommandArray commands) {
|
||||
}
|
||||
|
||||
// Draw the right border
|
||||
if (config->right.width > 0) {
|
||||
cairo_set_line_width(cr, config->right.width);
|
||||
cairo_set_source_rgba(cr, CLAY_TO_CAIRO(config->right.color));
|
||||
if (config->width.right > 0) {
|
||||
cairo_set_line_width(cr, config->width.right);
|
||||
cairo_set_source_rgba(cr, CLAY_TO_CAIRO(config->color));
|
||||
|
||||
cairo_new_sub_path(cr);
|
||||
|
||||
@ -296,9 +287,9 @@ void Clay_Cairo_Render(Clay_RenderCommandArray commands) {
|
||||
}
|
||||
|
||||
// Draw the bottom border
|
||||
if (config->bottom.width > 0) {
|
||||
cairo_set_line_width(cr, config->bottom.width);
|
||||
cairo_set_source_rgba(cr, CLAY_TO_CAIRO(config->bottom.color));
|
||||
if (config->width.bottom > 0) {
|
||||
cairo_set_line_width(cr, config->width.bottom);
|
||||
cairo_set_source_rgba(cr, CLAY_TO_CAIRO(config->color));
|
||||
|
||||
cairo_new_sub_path(cr);
|
||||
|
||||
@ -315,9 +306,9 @@ void Clay_Cairo_Render(Clay_RenderCommandArray commands) {
|
||||
}
|
||||
|
||||
// Draw the left border
|
||||
if (config->left.width > 0) {
|
||||
cairo_set_line_width(cr, config->left.width);
|
||||
cairo_set_source_rgba(cr, CLAY_TO_CAIRO(config->left.color));
|
||||
if (config->width.left > 0) {
|
||||
cairo_set_line_width(cr, config->width.left);
|
||||
cairo_set_source_rgba(cr, CLAY_TO_CAIRO(config->color));
|
||||
|
||||
cairo_new_sub_path(cr);
|
||||
|
||||
@ -335,10 +326,10 @@ void Clay_Cairo_Render(Clay_RenderCommandArray commands) {
|
||||
break;
|
||||
}
|
||||
case CLAY_RENDER_COMMAND_TYPE_IMAGE: {
|
||||
Clay_ImageElementConfig *config = command->config.imageElementConfig;
|
||||
Clay_ImageRenderData *config = &command->renderData.image;
|
||||
Clay_BoundingBox bb = command->boundingBox;
|
||||
|
||||
char *path = Clay_Cairo__NullTerminate(&config->path);
|
||||
char *path = config->imageData;
|
||||
|
||||
cairo_surface_t *surf = cairo_image_surface_create_from_png(path),
|
||||
*origin = cairo_get_target(cr);
|
||||
@ -369,7 +360,6 @@ void Clay_Cairo_Render(Clay_RenderCommandArray commands) {
|
||||
|
||||
// Clean up the source surface
|
||||
cairo_surface_destroy(surf);
|
||||
free(path);
|
||||
break;
|
||||
}
|
||||
case CLAY_RENDER_COMMAND_TYPE_CUSTOM: {
|
||||
|
@ -8,13 +8,6 @@
|
||||
#define CLAY_RECTANGLE_TO_RAYLIB_RECTANGLE(rectangle) (Rectangle) { .x = rectangle.x, .y = rectangle.y, .width = rectangle.width, .height = rectangle.height }
|
||||
#define CLAY_COLOR_TO_RAYLIB_COLOR(color) (Color) { .r = (unsigned char)roundf(color.r), .g = (unsigned char)roundf(color.g), .b = (unsigned char)roundf(color.b), .a = (unsigned char)roundf(color.a) }
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t fontId;
|
||||
Font font;
|
||||
} Raylib_Font;
|
||||
|
||||
Raylib_Font Raylib_fonts[10];
|
||||
Camera Raylib_camera;
|
||||
|
||||
typedef enum
|
||||
@ -88,7 +81,7 @@ Ray GetScreenToWorldPointWithZDistance(Vector2 position, Camera camera, int scre
|
||||
}
|
||||
|
||||
|
||||
static inline Clay_Dimensions Raylib_MeasureText(Clay_StringSlice text, Clay_TextElementConfig *config, uintptr_t userData) {
|
||||
static inline Clay_Dimensions Raylib_MeasureText(Clay_StringSlice text, Clay_TextElementConfig *config, void *userData) {
|
||||
// Measure string size for Font
|
||||
Clay_Dimensions textSize = { 0 };
|
||||
|
||||
@ -96,7 +89,8 @@ static inline Clay_Dimensions Raylib_MeasureText(Clay_StringSlice text, Clay_Tex
|
||||
float lineTextWidth = 0;
|
||||
|
||||
float textHeight = config->fontSize;
|
||||
Font fontToUse = Raylib_fonts[config->fontId].font;
|
||||
Font* fonts = (Font*)userData;
|
||||
Font fontToUse = fonts[config->fontId];
|
||||
// Font failed to load, likely the fonts are in the wrong place relative to the execution dir
|
||||
if (!fontToUse.glyphs) return textSize;
|
||||
|
||||
@ -128,7 +122,7 @@ void Clay_Raylib_Initialize(int width, int height, const char *title, unsigned i
|
||||
// EnableEventWaiting();
|
||||
}
|
||||
|
||||
void Clay_Raylib_Render(Clay_RenderCommandArray renderCommands)
|
||||
void Clay_Raylib_Render(Clay_RenderCommandArray renderCommands, Font* fonts)
|
||||
{
|
||||
for (int j = 0; j < renderCommands.length; j++)
|
||||
{
|
||||
@ -138,23 +132,27 @@ void Clay_Raylib_Render(Clay_RenderCommandArray renderCommands)
|
||||
{
|
||||
case CLAY_RENDER_COMMAND_TYPE_TEXT: {
|
||||
// Raylib uses standard C strings so isn't compatible with cheap slices, we need to clone the string to append null terminator
|
||||
Clay_StringSlice text = renderCommand->text;
|
||||
char *cloned = (char *)malloc(text.length + 1);
|
||||
memcpy(cloned, text.chars, text.length);
|
||||
cloned[text.length] = '\0';
|
||||
Font fontToUse = Raylib_fonts[renderCommand->config.textElementConfig->fontId].font;
|
||||
DrawTextEx(fontToUse, cloned, (Vector2){boundingBox.x, boundingBox.y}, (float)renderCommand->config.textElementConfig->fontSize, (float)renderCommand->config.textElementConfig->letterSpacing, CLAY_COLOR_TO_RAYLIB_COLOR(renderCommand->config.textElementConfig->textColor));
|
||||
Clay_TextRenderData *textData = &renderCommand->renderData.text;
|
||||
char *cloned = (char *)malloc(textData->stringContents.length + 1);
|
||||
memcpy(cloned, textData->stringContents.chars, textData->stringContents.length);
|
||||
cloned[textData->stringContents.length] = '\0';
|
||||
Font fontToUse = fonts[textData->fontId];
|
||||
DrawTextEx(fontToUse, cloned, (Vector2){boundingBox.x, boundingBox.y}, (float)textData->fontSize, (float)textData->letterSpacing, CLAY_COLOR_TO_RAYLIB_COLOR(textData->textColor));
|
||||
free(cloned);
|
||||
break;
|
||||
}
|
||||
case CLAY_RENDER_COMMAND_TYPE_IMAGE: {
|
||||
Texture2D imageTexture = *(Texture2D *)renderCommand->config.imageElementConfig->imageData;
|
||||
Texture2D imageTexture = *(Texture2D *)renderCommand->renderData.image.imageData;
|
||||
Clay_Color tintColor = renderCommand->renderData.image.backgroundColor;
|
||||
if (tintColor.r == 0 && tintColor.g == 0 && tintColor.b == 0 && tintColor.a == 0) {
|
||||
tintColor = (Clay_Color) { 255, 255, 255, 255 };
|
||||
}
|
||||
DrawTextureEx(
|
||||
imageTexture,
|
||||
(Vector2){boundingBox.x, boundingBox.y},
|
||||
0,
|
||||
boundingBox.width / (float)imageTexture.width,
|
||||
WHITE);
|
||||
CLAY_COLOR_TO_RAYLIB_COLOR(tintColor));
|
||||
break;
|
||||
}
|
||||
case CLAY_RENDER_COMMAND_TYPE_SCISSOR_START: {
|
||||
@ -166,49 +164,50 @@ void Clay_Raylib_Render(Clay_RenderCommandArray renderCommands)
|
||||
break;
|
||||
}
|
||||
case CLAY_RENDER_COMMAND_TYPE_RECTANGLE: {
|
||||
Clay_RectangleElementConfig *config = renderCommand->config.rectangleElementConfig;
|
||||
Clay_RectangleRenderData *config = &renderCommand->renderData.rectangle;
|
||||
if (config->cornerRadius.topLeft > 0) {
|
||||
float radius = (config->cornerRadius.topLeft * 2) / (float)((boundingBox.width > boundingBox.height) ? boundingBox.height : boundingBox.width);
|
||||
DrawRectangleRounded((Rectangle) { boundingBox.x, boundingBox.y, boundingBox.width, boundingBox.height }, radius, 8, CLAY_COLOR_TO_RAYLIB_COLOR(config->color));
|
||||
DrawRectangleRounded((Rectangle) { boundingBox.x, boundingBox.y, boundingBox.width, boundingBox.height }, radius, 8, CLAY_COLOR_TO_RAYLIB_COLOR(config->backgroundColor));
|
||||
} else {
|
||||
DrawRectangle(boundingBox.x, boundingBox.y, boundingBox.width, boundingBox.height, CLAY_COLOR_TO_RAYLIB_COLOR(config->color));
|
||||
DrawRectangle(boundingBox.x, boundingBox.y, boundingBox.width, boundingBox.height, CLAY_COLOR_TO_RAYLIB_COLOR(config->backgroundColor));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CLAY_RENDER_COMMAND_TYPE_BORDER: {
|
||||
Clay_BorderElementConfig *config = renderCommand->config.borderElementConfig;
|
||||
Clay_BorderRenderData *config = &renderCommand->renderData.border;
|
||||
// Left border
|
||||
if (config->left.width > 0) {
|
||||
DrawRectangle((int)roundf(boundingBox.x), (int)roundf(boundingBox.y + config->cornerRadius.topLeft), (int)config->left.width, (int)roundf(boundingBox.height - config->cornerRadius.topLeft - config->cornerRadius.bottomLeft), CLAY_COLOR_TO_RAYLIB_COLOR(config->left.color));
|
||||
if (config->width.left > 0) {
|
||||
DrawRectangle((int)roundf(boundingBox.x), (int)roundf(boundingBox.y + config->cornerRadius.topLeft), (int)config->width.left, (int)roundf(boundingBox.height - config->cornerRadius.topLeft - config->cornerRadius.bottomLeft), CLAY_COLOR_TO_RAYLIB_COLOR(config->color));
|
||||
}
|
||||
// Right border
|
||||
if (config->right.width > 0) {
|
||||
DrawRectangle((int)roundf(boundingBox.x + boundingBox.width - config->right.width), (int)roundf(boundingBox.y + config->cornerRadius.topRight), (int)config->right.width, (int)roundf(boundingBox.height - config->cornerRadius.topRight - config->cornerRadius.bottomRight), CLAY_COLOR_TO_RAYLIB_COLOR(config->right.color));
|
||||
if (config->width.right > 0) {
|
||||
DrawRectangle((int)roundf(boundingBox.x + boundingBox.width - config->width.right), (int)roundf(boundingBox.y + config->cornerRadius.topRight), (int)config->width.right, (int)roundf(boundingBox.height - config->cornerRadius.topRight - config->cornerRadius.bottomRight), CLAY_COLOR_TO_RAYLIB_COLOR(config->color));
|
||||
}
|
||||
// Top border
|
||||
if (config->top.width > 0) {
|
||||
DrawRectangle((int)roundf(boundingBox.x + config->cornerRadius.topLeft), (int)roundf(boundingBox.y), (int)roundf(boundingBox.width - config->cornerRadius.topLeft - config->cornerRadius.topRight), (int)config->top.width, CLAY_COLOR_TO_RAYLIB_COLOR(config->top.color));
|
||||
if (config->width.top > 0) {
|
||||
DrawRectangle((int)roundf(boundingBox.x + config->cornerRadius.topLeft), (int)roundf(boundingBox.y), (int)roundf(boundingBox.width - config->cornerRadius.topLeft - config->cornerRadius.topRight), (int)config->width.top, CLAY_COLOR_TO_RAYLIB_COLOR(config->color));
|
||||
}
|
||||
// Bottom border
|
||||
if (config->bottom.width > 0) {
|
||||
DrawRectangle((int)roundf(boundingBox.x + config->cornerRadius.bottomLeft), (int)roundf(boundingBox.y + boundingBox.height - config->bottom.width), (int)roundf(boundingBox.width - config->cornerRadius.bottomLeft - config->cornerRadius.bottomRight), (int)config->bottom.width, CLAY_COLOR_TO_RAYLIB_COLOR(config->bottom.color));
|
||||
if (config->width.bottom > 0) {
|
||||
DrawRectangle((int)roundf(boundingBox.x + config->cornerRadius.bottomLeft), (int)roundf(boundingBox.y + boundingBox.height - config->width.bottom), (int)roundf(boundingBox.width - config->cornerRadius.bottomLeft - config->cornerRadius.bottomRight), (int)config->width.bottom, CLAY_COLOR_TO_RAYLIB_COLOR(config->color));
|
||||
}
|
||||
if (config->cornerRadius.topLeft > 0) {
|
||||
DrawRing((Vector2) { roundf(boundingBox.x + config->cornerRadius.topLeft), roundf(boundingBox.y + config->cornerRadius.topLeft) }, roundf(config->cornerRadius.topLeft - config->top.width), config->cornerRadius.topLeft, 180, 270, 10, CLAY_COLOR_TO_RAYLIB_COLOR(config->top.color));
|
||||
DrawRing((Vector2) { roundf(boundingBox.x + config->cornerRadius.topLeft), roundf(boundingBox.y + config->cornerRadius.topLeft) }, roundf(config->cornerRadius.topLeft - config->width.top), config->cornerRadius.topLeft, 180, 270, 10, CLAY_COLOR_TO_RAYLIB_COLOR(config->color));
|
||||
}
|
||||
if (config->cornerRadius.topRight > 0) {
|
||||
DrawRing((Vector2) { roundf(boundingBox.x + boundingBox.width - config->cornerRadius.topRight), roundf(boundingBox.y + config->cornerRadius.topRight) }, roundf(config->cornerRadius.topRight - config->top.width), config->cornerRadius.topRight, 270, 360, 10, CLAY_COLOR_TO_RAYLIB_COLOR(config->top.color));
|
||||
DrawRing((Vector2) { roundf(boundingBox.x + boundingBox.width - config->cornerRadius.topRight), roundf(boundingBox.y + config->cornerRadius.topRight) }, roundf(config->cornerRadius.topRight - config->width.top), config->cornerRadius.topRight, 270, 360, 10, CLAY_COLOR_TO_RAYLIB_COLOR(config->color));
|
||||
}
|
||||
if (config->cornerRadius.bottomLeft > 0) {
|
||||
DrawRing((Vector2) { roundf(boundingBox.x + config->cornerRadius.bottomLeft), roundf(boundingBox.y + boundingBox.height - config->cornerRadius.bottomLeft) }, roundf(config->cornerRadius.bottomLeft - config->top.width), config->cornerRadius.bottomLeft, 90, 180, 10, CLAY_COLOR_TO_RAYLIB_COLOR(config->bottom.color));
|
||||
DrawRing((Vector2) { roundf(boundingBox.x + config->cornerRadius.bottomLeft), roundf(boundingBox.y + boundingBox.height - config->cornerRadius.bottomLeft) }, roundf(config->cornerRadius.bottomLeft - config->width.top), config->cornerRadius.bottomLeft, 90, 180, 10, CLAY_COLOR_TO_RAYLIB_COLOR(config->color));
|
||||
}
|
||||
if (config->cornerRadius.bottomRight > 0) {
|
||||
DrawRing((Vector2) { roundf(boundingBox.x + boundingBox.width - config->cornerRadius.bottomRight), roundf(boundingBox.y + boundingBox.height - config->cornerRadius.bottomRight) }, roundf(config->cornerRadius.bottomRight - config->bottom.width), config->cornerRadius.bottomRight, 0.1, 90, 10, CLAY_COLOR_TO_RAYLIB_COLOR(config->bottom.color));
|
||||
DrawRing((Vector2) { roundf(boundingBox.x + boundingBox.width - config->cornerRadius.bottomRight), roundf(boundingBox.y + boundingBox.height - config->cornerRadius.bottomRight) }, roundf(config->cornerRadius.bottomRight - config->width.bottom), config->cornerRadius.bottomRight, 0.1, 90, 10, CLAY_COLOR_TO_RAYLIB_COLOR(config->color));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CLAY_RENDER_COMMAND_TYPE_CUSTOM: {
|
||||
CustomLayoutElement *customElement = (CustomLayoutElement *)renderCommand->config.customElementConfig->customData;
|
||||
Clay_CustomRenderData *config = &renderCommand->renderData.custom;
|
||||
CustomLayoutElement *customElement = (CustomLayoutElement *)config->customData;
|
||||
if (!customElement) continue;
|
||||
switch (customElement->type) {
|
||||
case CUSTOM_LAYOUT_ELEMENT_TYPE_3D_MODEL: {
|
||||
|
Loading…
Reference in New Issue
Block a user