diff --git a/examples/raylib-sidebar-scrolling-container/build.zig b/examples/clay-official-website/build.zig similarity index 99% rename from examples/raylib-sidebar-scrolling-container/build.zig rename to examples/clay-official-website/build.zig index 7e7e9e0..e7794b0 100644 --- a/examples/raylib-sidebar-scrolling-container/build.zig +++ b/examples/clay-official-website/build.zig @@ -12,7 +12,6 @@ pub fn build(b: *B) void { .target = target, .optimize = optimize, }); - addDependencies(exe, b, target, optimize); b.installArtifact(exe); @@ -33,7 +32,6 @@ pub fn build(b: *B) void { .target = target, .optimize = optimize, }); - addDependencies(exe_unit_tests, b, target, optimize); const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests); @@ -48,7 +46,6 @@ pub fn build(b: *B) void { .target = target, .optimize = optimize, }); - addDependencies(exe_check, b, target, optimize); const tests_check = b.addTest(.{ @@ -57,7 +54,6 @@ pub fn build(b: *B) void { .target = target, .optimize = optimize, }); - addDependencies(tests_check, b, target, optimize); const check = b.step("check", "Check if exe and tests compile"); @@ -72,6 +68,13 @@ fn addDependencies( target: B.ResolvedTarget, optimize: std.builtin.OptimizeMode, ) void { + const zclay_dep = b.dependency("zclay", .{ + .target = target, + .optimize = optimize, + }); + const zclay = zclay_dep.module("zclay"); + compile_step.root_module.addImport("zclay", zclay); + const raylib_dep = b.dependency("raylib-zig", .{ .target = target, .optimize = optimize, @@ -80,11 +83,4 @@ fn addDependencies( compile_step.root_module.addImport("raylib", raylib); const raylib_artifact = raylib_dep.artifact("raylib"); compile_step.linkLibrary(raylib_artifact); - - const zclay_dep = b.dependency("zclay", .{ - .target = target, - .optimize = optimize, - }); - const zclay = zclay_dep.module("zclay"); - compile_step.root_module.addImport("zclay", zclay); } diff --git a/examples/raylib-sidebar-scrolling-container/build.zig.zon b/examples/clay-official-website/build.zig.zon similarity index 72% rename from examples/raylib-sidebar-scrolling-container/build.zig.zon rename to examples/clay-official-website/build.zig.zon index 1a88022..a2aaa56 100644 --- a/examples/raylib-sidebar-scrolling-container/build.zig.zon +++ b/examples/clay-official-website/build.zig.zon @@ -6,8 +6,8 @@ .path = "../../", }, .@"raylib-zig" = .{ - .url = "https://github.com/Not-Nik/raylib-zig/archive/01b6e1a2e26486cd8ee20dae275fadb51f67b73d.zip", - .hash = "12206dbd1510ce7e5270c725f980ca3a5ff3db47adeeda8d51ebe8527d6b27dd60a8", + .url = "https://github.com/Not-Nik/raylib-zig/archive/90109ff8041229ae8b615dca362f646586f20bd3.tar.gz", + .hash = "122062d2421175ba6bf89382c86621270e4883f1074ee4cc09da282e231bdef57328", }, }, .paths = .{ diff --git a/examples/clay-official-website/src/main.zig b/examples/clay-official-website/src/main.zig new file mode 100644 index 0000000..55654eb --- /dev/null +++ b/examples/clay-official-website/src/main.zig @@ -0,0 +1,306 @@ +const std = @import("std"); +const rl = @import("raylib"); +const cl = @import("zclay"); +const renderer = @import("raylib_render_clay.zig"); + +var syntaxImage: rl.Texture2D = undefined; +var checkImage1: rl.Texture2D = undefined; +var checkImage2: rl.Texture2D = undefined; +var checkImage3: rl.Texture2D = undefined; +var checkImage4: rl.Texture2D = undefined; +var checkImage5: rl.Texture2D = undefined; + +const FONT_ID_BODY_16 = 0; +const FONT_ID_TITLE_56 = 9; +const FONT_ID_TITLE_52 = 1; +const FONT_ID_TITLE_48 = 2; +const FONT_ID_TITLE_36 = 3; +const FONT_ID_TITLE_32 = 4; +const FONT_ID_BODY_36 = 5; +const FONT_ID_BODY_30 = 6; +const FONT_ID_BODY_28 = 7; +const FONT_ID_BODY_24 = 8; + +const COLOR_LIGHT = cl.Color{ 244, 235, 230, 255 }; +const COLOR_LIGHT_HOVER = cl.Color{ 224, 215, 210, 255 }; +const COLOR_BUTTON_HOVER = cl.Color{ 238, 227, 225, 255 }; +const COLOR_BROWN = cl.Color{ 61, 26, 5, 255 }; +//COLOR_R= :: cl.Color {252, 67, 27, 255}; +const COLOR_RED = cl.Color{ 168, 66, 28, 255 }; +const COLOR_RED_HOVER = cl.Color{ 148, 46, 8, 255 }; +const COLOR_ORANGE = cl.Color{ 225, 138, 50, 255 }; +const COLOR_BLUE = cl.Color{ 111, 173, 162, 255 }; +const COLOR_TEAL = cl.Color{ 111, 173, 162, 255 }; +const COLOR_BLUE_DARK = cl.Color{ 2, 32, 82, 255 }; + +// Colors for top stripe +const COLORS_TOP_BORDER = [_]cl.Color{ + .{ 240, 213, 137, 255 }, + .{ 236, 189, 80, 255 }, + .{ 225, 138, 50, 255 }, + .{ 223, 110, 44, 255 }, + .{ 168, 66, 28, 255 }, +}; + +const COLOR_BLOB_BORDER_1 = cl.Color{ 168, 66, 28, 255 }; +const COLOR_BLOB_BORDER_2 = cl.Color{ 203, 100, 44, 255 }; +const COLOR_BLOB_BORDER_3 = cl.Color{ 225, 138, 50, 255 }; +const COLOR_BLOB_BORDER_4 = cl.Color{ 236, 159, 70, 255 }; +const COLOR_BLOB_BORDER_5 = cl.Color{ 240, 189, 100, 255 }; + +const border_data = cl.BorderData{ .width = 2, .color = COLOR_RED }; + +var window_height: isize = 0; +var window_width: isize = 0; + +fn LandingPageBlob(index: u32, fontSize: u16, fontId: u16, color: cl.Color, text: []const u8, image: *rl.Texture2D) void { + cl.UI(&.{ + .IDI("HeroBlob", index), + .layout(.{ .size = .{ .w = .fGrow(.{ .max = 480 }) }, .padding = .uniform(16), .gap = 16, .alignment = .{ .y = .CENTER } }), + .border(.outside(color, 2, 10)), + }); + { + defer cl.CLOSE(); + cl.UI(&.{ + .IDI("CheckImage", index), + .layout(.{ .size = .{ .w = .fixed(32) } }), + .image(.{ .image_data = image, .source_dimensions = .{ .w = 128, .h = 128 } }), + }); + cl.CLOSE(); + cl.text(text, cl.Config.text(.{ .font_size = fontSize, .font_id = fontId, .color = color })); + } +} + +fn landingPageDesktop() void { + cl.UI(&.{ + .ID("LandingPage1Desktop"), + .layout(.{ + .size = .{ .w = .grow, .h = .fFit(.{ .min = @floatFromInt(window_height - 70) }) }, + .alignment = .{ .y = .CENTER }, + .padding = .{ .x = 50 }, + .gap = 16, + }), + }); + { + defer cl.CLOSE(); + cl.UI(&.{ + .ID("LandingPage1"), + .layout(.{ .size = .grow, .alignment = .{ .y = .CENTER }, .padding = .uniform(32), .gap = 32 }), + .border(.{ .left = border_data, .right = border_data }), + }); + { + defer cl.CLOSE(); + cl.UI(&.{ + .ID("LeftText"), + .layout(.{ .size = .{ .w = .percent(0.55) }, .direction = .TOP_TO_BOTTOM, .gap = 8 }), + }); + { + defer cl.CLOSE(); + cl.text( + "Clay is a flex-box style UI auto layout library in C, with declarative syntax and microsecond performance.", + cl.Config.text(.{ .font_size = 56, .font_id = FONT_ID_TITLE_56, .color = COLOR_RED }), + ); + cl.UI(&.{ .ID("Spacer"), .layout(.{ .size = .{ .w = .grow, .h = .fixed(32) } }) }); + cl.CLOSE(); + cl.text( + "Clay is laying out this webpage right now!", + cl.Config.text(.{ .font_size = 36, .font_id = FONT_ID_BODY_36, .color = COLOR_ORANGE }), + ); + } + + cl.UI(&.{ + .ID("HeroImageOuter"), + .layout(.{ .size = .{ .w = .percent(0.45) }, .direction = .TOP_TO_BOTTOM, .alignment = .{ .x = .CENTER }, .gap = 16 }), + }); + { + defer cl.CLOSE(); + 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); + LandingPageBlob(4, 30, FONT_ID_BODY_30, COLOR_BLOB_BORDER_2, "Single .h file for C/C++", &checkImage2); + LandingPageBlob(5, 30, FONT_ID_BODY_30, COLOR_BLOB_BORDER_1, "Compile to 15kb .wasm", &checkImage1); + } + } + } +} + +const ScrollbarData = struct { + clickOrigin: cl.Vector2, + positionOrigin: cl.Vector2, + mouseDown: bool, +}; + +const scrollbarData = ScrollbarData{}; +const animationLerpValue: f32 = -1.0; + +fn createLayout(lerpValue: f32) cl.ClayArray(cl.RenderCommand) { + _ = lerpValue; // autofix + const mobileScreen = window_width < 750; + cl.beginLayout(); + + cl.UI(&.{ + .ID("OuterContainer"), + .layout(.{ .size = .grow, .direction = .TOP_TO_BOTTOM }), + .rectangle(.{ .color = COLOR_LIGHT }), + }); + { + defer cl.CLOSE(); + cl.UI(&.{ + .ID("Header"), + .layout(.{ + .size = .{ .h = .fixed(50), .w = .grow }, + .alignment = .{ .y = .CENTER }, + .padding = .{ .x = 32 }, + .gap = 24, + }), + }); + { + defer cl.CLOSE(); + cl.text("Clay", cl.Config.text(.{ + .font_id = FONT_ID_BODY_24, + .font_size = 24, + .color = .{ 61, 26, 5, 255 }, + })); + cl.UI(&.{ .ID("LinkExamplesOuter"), .layout(.{ .size = .{ .w = .grow } }) }); + cl.CLOSE(); + + if (!mobileScreen) { + cl.UI(&.{ .ID("LinkExamplesOuter"), .layout(.{}), .rectangle(.{ .color = .{ 0, 0, 0, 0 } }) }); + { + defer cl.CLOSE(); + cl.text("Examples", cl.Config.text(.{ .font_id = FONT_ID_BODY_24, .font_size = 24, .color = .{ 61, 26, 5, 255 } })); + } + cl.UI(&.{ .ID("LinkDocsOuter"), .layout(.{}), .rectangle(.{ .color = .{ 0, 0, 0, 0 } }) }); + { + defer cl.CLOSE(); + cl.text("Docs", cl.Config.text(.{ .font_id = FONT_ID_BODY_24, .font_size = 24, .color = .{ 61, 26, 5, 255 } })); + } + } + + // clay.Rectangle({cornerRadius = clay.CornerRadiusAll(10), color = clay.PointerOver(clay.GetElementId(clay.MakeString("LinkGithubOuter"))) ? COLOR_LIGHT_HOVER : COLOR_LIGHT}) + cl.UI(&.{ + .ID("LinkGithubOuter"), + .layout(.{ .padding = .{ .x = 32, .y = 6 } }), + .border(.outside(COLOR_RED, 2, 10)), + .rectangle(.{ + .corner_radius = .all(10), + .color = if (cl.pointerOver(cl.getElementId("LinkGithubOuter"))) COLOR_LIGHT_HOVER else COLOR_LIGHT, + }), + }); + { + defer cl.CLOSE(); + cl.text( + "Github", + cl.Config.text(.{ .font_id = FONT_ID_BODY_24, .font_size = 24, .color = .{ 61, 26, 5, 255 } }), + ); + } + } + inline for (COLORS_TOP_BORDER, 0..) |color, i| { + cl.UI(&.{ + .ID("TopBorder" ++ .{i}), + .layout(.{ .size = .{ .h = .fixed(4), .w = .grow } }), + .rectangle(.{ .color = color }), + }); + cl.CLOSE(); + } + + cl.UI(&.{ + .ID("ScrollContainerBackgroundRectangle"), + .scroll(.{ .vertical = true }), + .layout(.{ .size = .grow, .direction = .TOP_TO_BOTTOM }), + .rectangle(.{ .color = COLOR_LIGHT }), + .border(.{ .between_children = .{ .width = 2, .color = COLOR_RED } }), + }); + { + defer cl.CLOSE(); + // if (!mobileScreen) { + if (true) { + landingPageDesktop(); + // FeatureBlocksDesktop() + // DeclarativeSyntaxPageDesktop() + // HighPerformancePageDesktop(lerpValue) + // RendererPageDesktop() + } else { + // LandingPageMobile() + // FeatureBlocksMobile() + // DeclarativeSyntaxPageMobile() + // HighPerformancePageMobile(lerpValue) + // RendererPageMobile() + } + } + } + return cl.endLayout(); +} + +fn loadFont(file_data: ?[]const u8, font_id: u16, font_size: i32) void { + renderer.raylib_fonts[font_id] = rl.loadFontFromMemory(".ttf", file_data, font_size * 2, null); + rl.setTextureFilter(renderer.raylib_fonts[font_id].?.texture, .texture_filter_bilinear); +} + +pub fn main() anyerror!void { + const allocator = std.heap.page_allocator; + + // init clay + const min_memory_size: u32 = cl.minMemorySize(); + const memory = try allocator.alloc(u8, min_memory_size); + defer allocator.free(memory); + const arena: cl.Arena = cl.createArenaWithCapacityAndMemory(min_memory_size, @ptrCast(memory)); + cl.initialize(arena, .{ .h = 1000, .w = 1000 }); + cl.setMeasureTextFunction(renderer.measureText); + + // init raylib + rl.setConfigFlags(.{ + .msaa_4x_hint = true, + .vsync_hint = true, + .window_highdpi = true, + .window_resizable = true, + }); + rl.initWindow(1000, 1000, "Raylib zig Example"); + rl.setTargetFPS(60); + + // load assets + loadFont(@embedFile("resources/Calistoga-Regular.ttf"), FONT_ID_TITLE_56, 56); + loadFont(@embedFile("resources/Calistoga-Regular.ttf"), FONT_ID_TITLE_52, 52); + loadFont(@embedFile("resources/Calistoga-Regular.ttf"), FONT_ID_TITLE_48, 48); + loadFont(@embedFile("resources/Calistoga-Regular.ttf"), FONT_ID_TITLE_36, 36); + loadFont(@embedFile("resources/Calistoga-Regular.ttf"), FONT_ID_TITLE_32, 32); + loadFont(@embedFile("resources/Quicksand-Semibold.ttf"), FONT_ID_BODY_36, 36); + loadFont(@embedFile("resources/Quicksand-Semibold.ttf"), FONT_ID_BODY_30, 30); + loadFont(@embedFile("resources/Quicksand-Semibold.ttf"), FONT_ID_BODY_28, 28); + loadFont(@embedFile("resources/Quicksand-Semibold.ttf"), FONT_ID_BODY_24, 24); + loadFont(@embedFile("resources/Quicksand-Semibold.ttf"), FONT_ID_BODY_16, 16); + + syntaxImage = rl.loadTextureFromImage(rl.loadImageFromMemory(".png", @embedFile("resources/declarative.png"))); + checkImage1 = rl.loadTextureFromImage(rl.loadImageFromMemory(".png", @embedFile("resources/check_1.png"))); + checkImage2 = rl.loadTextureFromImage(rl.loadImageFromMemory(".png", @embedFile("resources/check_2.png"))); + checkImage3 = rl.loadTextureFromImage(rl.loadImageFromMemory(".png", @embedFile("resources/check_3.png"))); + checkImage4 = rl.loadTextureFromImage(rl.loadImageFromMemory(".png", @embedFile("resources/check_4.png"))); + checkImage5 = rl.loadTextureFromImage(rl.loadImageFromMemory(".png", @embedFile("resources/check_5.png"))); + + var debug_mode_enabled = false; + while (!rl.windowShouldClose()) { + if (rl.isKeyPressed(.key_d)) { + debug_mode_enabled = !debug_mode_enabled; + cl.setDebugModeEnabled(debug_mode_enabled); + } + + window_width = rl.getScreenWidth(); + window_height = rl.getScreenHeight(); + + const mouse_pos = rl.getMousePosition(); + cl.setPointerState(.{ + .x = mouse_pos.x, + .y = mouse_pos.y, + }, rl.isMouseButtonDown(.mouse_button_left)); + + cl.setLayoutDimensions(.{ + .w = @floatFromInt(window_width), + .h = @floatFromInt(window_height), + }); + var render_commands = createLayout(0); + + rl.beginDrawing(); + renderer.clayRaylibRender(&render_commands, allocator); + rl.endDrawing(); + } +} diff --git a/examples/clay-official-website/src/raylib_render_clay.zig b/examples/clay-official-website/src/raylib_render_clay.zig new file mode 100644 index 0000000..8cdc865 --- /dev/null +++ b/examples/clay-official-website/src/raylib_render_clay.zig @@ -0,0 +1,232 @@ +const std = @import("std"); +const rl = @import("raylib"); +const cl = @import("zclay"); +const math = std.math; + +pub fn clayColorToRaylibColor(color: cl.Color) rl.Color { + return rl.Color{ + .r = @intFromFloat(color[0]), + .g = @intFromFloat(color[1]), + .b = @intFromFloat(color[2]), + .a = @intFromFloat(color[3]), + }; +} + +pub var raylib_fonts: [10]?rl.Font = .{null} ** 10; + +pub fn clayRaylibRender(render_commands: *cl.ClayArray(cl.RenderCommand), allocator: std.mem.Allocator) void { + var i: usize = 0; + while (i < render_commands.length) : (i += 1) { + const render_command = cl.renderCommandArrayGet(render_commands, @intCast(i)); + const bounding_box = render_command.bounding_box; + switch (render_command.command_type) { + .None => {}, + .Text => { + const text = render_command.text.chars[0..@intCast(render_command.text.length)]; + const cloned = allocator.dupeZ(c_char, text) catch unreachable; + defer allocator.free(cloned); + const fontToUse: rl.Font = raylib_fonts[render_command.config.text_element_config.font_id].?; + rl.setTextLineSpacing(render_command.config.text_element_config.line_height); + rl.drawTextEx( + fontToUse, + @ptrCast(@alignCast(cloned.ptr)), + rl.Vector2{ .x = bounding_box.x, .y = bounding_box.y }, + @floatFromInt(render_command.config.text_element_config.font_size), + @floatFromInt(render_command.config.text_element_config.letter_spacing), + clayColorToRaylibColor(render_command.config.text_element_config.color), + ); + }, + .Image => { + const image_texture: *const rl.Texture2D = @ptrCast( + @alignCast(render_command.config.image_element_config.image_data), + ); + rl.drawTextureEx( + image_texture.*, + rl.Vector2{ .x = bounding_box.x, .y = bounding_box.y }, + 0, + bounding_box.width / @as(f32, @floatFromInt(image_texture.width)), + rl.Color.white, + ); + }, + .ScissorStart => { + rl.beginScissorMode( + @intFromFloat(math.round(bounding_box.x)), + @intFromFloat(math.round(bounding_box.y)), + @intFromFloat(math.round(bounding_box.width)), + @intFromFloat(math.round(bounding_box.height)), + ); + }, + .ScissorEnd => rl.endScissorMode(), + .Rectangle => { + const config = render_command.config.rectangle_element_config; + if (config.corner_radius.top_left > 0) { + const radius: f32 = (config.corner_radius.top_left * 2) / @min(bounding_box.width, bounding_box.height); + rl.drawRectangleRounded( + rl.Rectangle{ + .x = bounding_box.x, + .y = bounding_box.y, + .width = bounding_box.width, + .height = bounding_box.height, + }, + radius, + 8, + clayColorToRaylibColor(config.color), + ); + } else { + rl.drawRectangle( + @intFromFloat(bounding_box.x), + @intFromFloat(bounding_box.y), + @intFromFloat(bounding_box.width), + @intFromFloat(bounding_box.height), + clayColorToRaylibColor(config.color), + ); + } + }, + .Border => { + const config = render_command.config.border_element_config; + if (config.left.width > 0) { + rl.drawRectangle( + @intFromFloat(math.round(bounding_box.x)), + @intFromFloat(math.round(bounding_box.y + config.corner_radius.top_left)), + @intCast(config.left.width), + @intFromFloat(math.round(bounding_box.height - config.corner_radius.top_left - config.corner_radius.bottom_left)), + clayColorToRaylibColor(config.left.color), + ); + } + if (config.right.width > 0) { + rl.drawRectangle( + @intFromFloat(math.round(bounding_box.x + bounding_box.width - @as(f32, @floatFromInt(config.right.width)))), + @intFromFloat(math.round(bounding_box.y + config.corner_radius.top_right)), + @intCast(config.right.width), + @intFromFloat(math.round(bounding_box.height - config.corner_radius.top_right - config.corner_radius.bottom_right)), + clayColorToRaylibColor(config.right.color), + ); + } + if (config.top.width > 0) { + rl.drawRectangle( + @intFromFloat(math.round(bounding_box.x + config.corner_radius.top_left)), + @intFromFloat(math.round(bounding_box.y)), + @intFromFloat(math.round(bounding_box.width - config.corner_radius.top_left - config.corner_radius.top_right)), + @intCast(config.top.width), + clayColorToRaylibColor(config.top.color), + ); + } + if (config.bottom.width > 0) { + rl.drawRectangle( + @intFromFloat(math.round(bounding_box.x + config.corner_radius.bottom_left)), + @intFromFloat(math.round(bounding_box.y + bounding_box.height - @as(f32, @floatFromInt(config.bottom.width)))), + @intFromFloat(math.round(bounding_box.width - config.corner_radius.bottom_left - config.corner_radius.bottom_right)), + @intCast(config.bottom.width), + clayColorToRaylibColor(config.bottom.color), + ); + } + + if (config.corner_radius.top_left > 0) { + rl.drawRing( + rl.Vector2{ + .x = math.round(bounding_box.x + config.corner_radius.top_left), + .y = math.round(bounding_box.y + config.corner_radius.top_left), + }, + math.round(config.corner_radius.top_left - @as(f32, @floatFromInt(config.top.width))), + config.corner_radius.top_left, + 180, + 270, + 10, + clayColorToRaylibColor(config.top.color), + ); + } + if (config.corner_radius.top_right > 0) { + rl.drawRing( + rl.Vector2{ + .x = math.round(bounding_box.x + bounding_box.width - config.corner_radius.top_right), + .y = math.round(bounding_box.y + config.corner_radius.top_right), + }, + math.round(config.corner_radius.top_right - @as(f32, @floatFromInt(config.top.width))), + config.corner_radius.top_right, + 270, + 360, + 10, + clayColorToRaylibColor(config.top.color), + ); + } + if (config.corner_radius.bottom_left > 0) { + rl.drawRing( + rl.Vector2{ + .x = math.round(bounding_box.x + config.corner_radius.bottom_left), + .y = math.round(bounding_box.y + bounding_box.height - config.corner_radius.bottom_left), + }, + math.round(config.corner_radius.bottom_left - @as(f32, @floatFromInt(config.top.width))), + config.corner_radius.bottom_left, + 90, + 180, + 10, + clayColorToRaylibColor(config.bottom.color), + ); + } + if (config.corner_radius.bottom_right > 0) { + rl.drawRing( + rl.Vector2{ + .x = math.round(bounding_box.x + bounding_box.width - config.corner_radius.bottom_right), + .y = math.round(bounding_box.y + bounding_box.height - config.corner_radius.bottom_right), + }, + math.round(config.corner_radius.bottom_right - @as(f32, @floatFromInt(config.top.width))), + config.corner_radius.bottom_right, + 0.1, + 90, + 10, + clayColorToRaylibColor(config.bottom.color), + ); + } + }, + .Custom => { + // Implement custom element rendering here + }, + } + } +} + +pub fn measureText(clay_text: []const u8, config: *cl.TextElementConfig) cl.Dimensions { + const font = raylib_fonts[config.font_id].?; + const text: []const u8 = clay_text; + const font_size: f32 = @floatFromInt(config.font_size); + const letter_spacing: f32 = @floatFromInt(config.letter_spacing); + const line_height = config.line_height; + + var temp_byte_counter: usize = 0; + var byte_counter: usize = 0; + var text_width: f32 = 0.0; + var temp_text_width: f32 = 0.0; + var text_height: f32 = font_size; + const scale_factor: f32 = font_size / @as(f32, @floatFromInt(font.baseSize)); + + var utf8 = std.unicode.Utf8View.initUnchecked(text).iterator(); + + while (utf8.nextCodepoint()) |codepoint| { + byte_counter += std.unicode.utf8CodepointSequenceLength(codepoint) catch 1; + const index: usize = @intCast( + rl.getGlyphIndex(font, @as(i32, @intCast(codepoint))), + ); + + if (codepoint != '\n') { + if (font.glyphs[index].advanceX != 0) { + text_width += @floatFromInt(font.glyphs[index].advanceX); + } else { + text_width += font.recs[index].width + @as(f32, @floatFromInt(font.glyphs[index].offsetX)); + } + } else { + if (temp_text_width < text_width) temp_text_width = text_width; + byte_counter = 0; + text_width = 0; + text_height += font_size + @as(f32, @floatFromInt(line_height)); + } + + if (temp_byte_counter < byte_counter) temp_byte_counter = byte_counter; + } + + if (temp_text_width < text_width) temp_text_width = text_width; + + return cl.Dimensions{ + .h = text_height, + .w = temp_text_width * scale_factor + (@as(f32, @floatFromInt(temp_byte_counter)) - 1) * letter_spacing, + }; +} diff --git a/examples/clay-official-website/src/resources/Calistoga-Regular.ttf b/examples/clay-official-website/src/resources/Calistoga-Regular.ttf new file mode 100644 index 0000000..3fc1c1e Binary files /dev/null and b/examples/clay-official-website/src/resources/Calistoga-Regular.ttf differ diff --git a/examples/clay-official-website/src/resources/Quicksand-Semibold.ttf b/examples/clay-official-website/src/resources/Quicksand-Semibold.ttf new file mode 100644 index 0000000..27106d0 Binary files /dev/null and b/examples/clay-official-website/src/resources/Quicksand-Semibold.ttf differ diff --git a/examples/clay-official-website/src/resources/check_1.png b/examples/clay-official-website/src/resources/check_1.png new file mode 100644 index 0000000..280fd23 Binary files /dev/null and b/examples/clay-official-website/src/resources/check_1.png differ diff --git a/examples/clay-official-website/src/resources/check_2.png b/examples/clay-official-website/src/resources/check_2.png new file mode 100644 index 0000000..7972581 Binary files /dev/null and b/examples/clay-official-website/src/resources/check_2.png differ diff --git a/examples/clay-official-website/src/resources/check_3.png b/examples/clay-official-website/src/resources/check_3.png new file mode 100644 index 0000000..fb60187 Binary files /dev/null and b/examples/clay-official-website/src/resources/check_3.png differ diff --git a/examples/clay-official-website/src/resources/check_4.png b/examples/clay-official-website/src/resources/check_4.png new file mode 100644 index 0000000..a938f81 Binary files /dev/null and b/examples/clay-official-website/src/resources/check_4.png differ diff --git a/examples/clay-official-website/src/resources/check_5.png b/examples/clay-official-website/src/resources/check_5.png new file mode 100644 index 0000000..ea6cfca Binary files /dev/null and b/examples/clay-official-website/src/resources/check_5.png differ diff --git a/examples/clay-official-website/src/resources/declarative.png b/examples/clay-official-website/src/resources/declarative.png new file mode 100644 index 0000000..5cd4abb Binary files /dev/null and b/examples/clay-official-website/src/resources/declarative.png differ diff --git a/examples/raylib-sidebar-container/build.zig b/examples/raylib-sidebar-container/build.zig new file mode 100644 index 0000000..e7794b0 --- /dev/null +++ b/examples/raylib-sidebar-container/build.zig @@ -0,0 +1,86 @@ +const std = @import("std"); +const B = std.Build; + +pub fn build(b: *B) void { + const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); + + { + const exe = b.addExecutable(.{ + .name = "zclay-example", + .root_source_file = b.path("src/main.zig"), + .target = target, + .optimize = optimize, + }); + addDependencies(exe, b, target, optimize); + + b.installArtifact(exe); + const run_cmd = b.addRunArtifact(exe); + run_cmd.step.dependOn(b.getInstallStep()); + + if (b.args) |args| { + run_cmd.addArgs(args); + } + + const run_step = b.step("run", "Run the app"); + run_step.dependOn(&run_cmd.step); + } + + { + const exe_unit_tests = b.addTest(.{ + .root_source_file = b.path("src/main.zig"), + .target = target, + .optimize = optimize, + }); + addDependencies(exe_unit_tests, b, target, optimize); + + const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests); + const test_step = b.step("test", "Run unit tests"); + test_step.dependOn(&run_exe_unit_tests.step); + } + + { + const exe_check = b.addExecutable(.{ + .name = "check", + .root_source_file = b.path("src/main.zig"), + .target = target, + .optimize = optimize, + }); + addDependencies(exe_check, b, target, optimize); + + const tests_check = b.addTest(.{ + .name = "check", + .root_source_file = b.path("src/main.zig"), + .target = target, + .optimize = optimize, + }); + addDependencies(tests_check, b, target, optimize); + + const check = b.step("check", "Check if exe and tests compile"); + check.dependOn(&exe_check.step); + check.dependOn(&tests_check.step); + } +} + +fn addDependencies( + compile_step: *B.Step.Compile, + b: *B, + target: B.ResolvedTarget, + optimize: std.builtin.OptimizeMode, +) void { + const zclay_dep = b.dependency("zclay", .{ + .target = target, + .optimize = optimize, + }); + const zclay = zclay_dep.module("zclay"); + compile_step.root_module.addImport("zclay", zclay); + + const raylib_dep = b.dependency("raylib-zig", .{ + .target = target, + .optimize = optimize, + }); + const raylib = raylib_dep.module("raylib"); + compile_step.root_module.addImport("raylib", raylib); + const raylib_artifact = raylib_dep.artifact("raylib"); + compile_step.linkLibrary(raylib_artifact); +} diff --git a/examples/raylib-sidebar-container/build.zig.zon b/examples/raylib-sidebar-container/build.zig.zon new file mode 100644 index 0000000..a2aaa56 --- /dev/null +++ b/examples/raylib-sidebar-container/build.zig.zon @@ -0,0 +1,20 @@ +.{ + .name = "zig-exe-template", + .version = "0.0.0", + .dependencies = .{ + .zclay = .{ + .path = "../../", + }, + .@"raylib-zig" = .{ + .url = "https://github.com/Not-Nik/raylib-zig/archive/90109ff8041229ae8b615dca362f646586f20bd3.tar.gz", + .hash = "122062d2421175ba6bf89382c86621270e4883f1074ee4cc09da282e231bdef57328", + }, + }, + .paths = .{ + "build.zig", + "build.zig.zon", + "src", + "LICENSE", + "README.md", + }, +} diff --git a/examples/raylib-sidebar-scrolling-container/src/main.zig b/examples/raylib-sidebar-container/src/main.zig similarity index 95% rename from examples/raylib-sidebar-scrolling-container/src/main.zig rename to examples/raylib-sidebar-container/src/main.zig index c95c60d..03968e5 100644 --- a/examples/raylib-sidebar-scrolling-container/src/main.zig +++ b/examples/raylib-sidebar-container/src/main.zig @@ -35,7 +35,7 @@ fn createLayout(profile_picture: *const rl.Texture2D) cl.ClayArray(cl.RenderComm .direction = .TOP_TO_BOTTOM, .size = .{ .h = .grow, .w = .fixed(300) }, .padding = .uniform(16), - .alignment = .{ .x = .CENTER, .y = .TOP }, + .alignment = .top_center, .gap = 16, }), .rectangle(.{ .color = light_grey }), @@ -44,7 +44,7 @@ fn createLayout(profile_picture: *const rl.Texture2D) cl.ClayArray(cl.RenderComm defer cl.CLOSE(); cl.UI(&.{ .ID("ProfilePictureOuter"), - .layout(.{ .size = .{ .w = .grow }, .padding = .uniform(16), .alignment = .{ .y = .CENTER }, .gap = 16 }), + .layout(.{ .size = .{ .w = .grow }, .padding = .uniform(16), .alignment = .center_left, .gap = 16 }), .rectangle(.{ .color = red }), }); { @@ -98,7 +98,7 @@ pub fn main() anyerror!void { rl.setTargetFPS(60); // load assets - loadFont(@embedFile("./resources/Roboto-Regular.ttf"), 0, 100); + loadFont(@embedFile("./resources/Roboto-Regular.ttf"), 0, 24); const profile_picture = rl.loadTextureFromImage(rl.loadImageFromMemory(".png", @embedFile("./resources/profile-picture.png"))); var debug_mode_enabled = false; diff --git a/examples/raylib-sidebar-scrolling-container/src/raylib_render_clay.zig b/examples/raylib-sidebar-container/src/raylib_render_clay.zig similarity index 100% rename from examples/raylib-sidebar-scrolling-container/src/raylib_render_clay.zig rename to examples/raylib-sidebar-container/src/raylib_render_clay.zig diff --git a/examples/raylib-sidebar-scrolling-container/src/resources/Roboto-Regular.ttf b/examples/raylib-sidebar-container/src/resources/Roboto-Regular.ttf similarity index 100% rename from examples/raylib-sidebar-scrolling-container/src/resources/Roboto-Regular.ttf rename to examples/raylib-sidebar-container/src/resources/Roboto-Regular.ttf diff --git a/examples/raylib-sidebar-scrolling-container/src/resources/RobotoMono-Medium.ttf b/examples/raylib-sidebar-container/src/resources/RobotoMono-Medium.ttf similarity index 100% rename from examples/raylib-sidebar-scrolling-container/src/resources/RobotoMono-Medium.ttf rename to examples/raylib-sidebar-container/src/resources/RobotoMono-Medium.ttf diff --git a/examples/raylib-sidebar-scrolling-container/src/resources/profile-picture.png b/examples/raylib-sidebar-container/src/resources/profile-picture.png similarity index 100% rename from examples/raylib-sidebar-scrolling-container/src/resources/profile-picture.png rename to examples/raylib-sidebar-container/src/resources/profile-picture.png diff --git a/src/root.zig b/src/root.zig index 9f473f0..ade6b8e 100644 --- a/src/root.zig +++ b/src/root.zig @@ -13,6 +13,7 @@ pub const cdefs = struct { extern "c" fn Clay_BeginLayout() void; extern "c" fn Clay_EndLayout() ClayArray(RenderCommand); extern "c" fn Clay_PointerOver(id: ElementId) bool; + extern "c" fn Clay_GetElementId(id: String) ElementId; extern "c" fn Clay_GetScrollContainerData(id: ElementId) ScrollContainerData; extern "c" fn Clay_SetMeasureTextFunction(measureTextFunction: *const fn (*String, *TextElementConfig) callconv(.C) Dimensions) void; extern "c" fn Clay_RenderCommandArray_Get(array: *ClayArray(RenderCommand), index: i32) *RenderCommand; @@ -73,11 +74,20 @@ pub const CornerRadius = extern struct { top_right: f32 = 0, bottom_left: f32 = 0, bottom_right: f32 = 0, + + pub fn all(radius: f32) CornerRadius { + return .{ + .top_left = radius, + .top_right = radius, + .bottom_left = radius, + .bottom_right = radius, + }; + } }; pub const BorderData = extern struct { width: u32 = 0, - color: Color = .{ 255, 255, 255, 255 }, + color: Color = .{ 0, 0, 0, 0 }, }; pub const ElementId = extern struct { @@ -169,7 +179,7 @@ pub const SizingConstraints = extern union { pub const SizingAxis = extern struct { // Note: `min` is used for CLAY_SIZING_PERCENT, slightly different to clay.h due to lack of C anonymous unions - constraints: SizingConstraints = .{ .size_percent = 100 }, + constraints: SizingConstraints = .{ .size_minmax = .{} }, type: SizingType = .FIT, pub const grow = SizingAxis{ .type = .GROW, .constraints = .{ .size_minmax = .{ .min = 0, .max = 0 } } }; @@ -231,16 +241,22 @@ pub const LayoutAlignmentY = enum(EnumBackingType) { }; pub const ChildAlignment = extern struct { - x: LayoutAlignmentX = .CENTER, - y: LayoutAlignmentY = .CENTER, + x: LayoutAlignmentX = .LEFT, + y: LayoutAlignmentY = .TOP, + pub const top_center = ChildAlignment{ .x = .CENTER, .y = .TOP }; + pub const bottom_center = ChildAlignment{ .x = .LEFT, .y = .BOTTOM }; + pub const top_left = ChildAlignment{ .x = .LEFT, .y = .TOP }; + pub const top_right = ChildAlignment{ .x = .RIGHT, .y = .TOP }; + pub const bottom_right = ChildAlignment{ .x = .RIGHT, .y = .BOTTOM }; + pub const bottom_left = ChildAlignment{ .x = .LEFT, .y = .BOTTOM }; + pub const center_left = ChildAlignment{ .x = .LEFT, .y = .CENTER }; + pub const center_right = ChildAlignment{ .x = .RIGHT, .y = .CENTER }; + pub const centered = ChildAlignment{ .x = .CENTER, .y = .CENTER }; }; pub const LayoutConfig = extern struct { /// sizing of the element - size: Sizing = .{ - .h = .{ .constraints = .{ .size_percent = 100 }, .type = .GROW }, - .w = .{ .constraints = .{ .size_percent = 100 }, .type = .GROW }, - }, + size: Sizing = .{}, /// padding arround children padding: Padding = .{}, /// gap between the children @@ -265,12 +281,36 @@ pub const RectangleElementConfig = extern struct { }; pub const BorderElementConfig = extern struct { - left: BorderData, - right: BorderData, - top: BorderData, - bottom: BorderData, - between_children: BorderData, - corner_radius: CornerRadius, + left: BorderData = .{}, + right: BorderData = .{}, + top: BorderData = .{}, + bottom: BorderData = .{}, + between_children: BorderData = .{}, + corner_radius: CornerRadius = .{}, + + pub fn outside(color: Color, width: u32, radius: f32) BorderElementConfig { + const data = BorderData{ .color = color, .width = width }; + return BorderElementConfig{ + .left = data, + .right = data, + .top = data, + .bottom = data, + .between_children = .{}, + .corner_radius = .all(radius), + }; + } + + pub fn all(color: Color, width: u32, radius: f32) BorderElementConfig { + const data = BorderData{ .color = color, .width = width }; + return BorderElementConfig{ + .left = data, + .right = data, + .top = data, + .bottom = data, + .between_children = data, + .corner_radius = .{ radius, radius, radius, radius }, + }; + } }; pub const TextElementConfig = extern struct { @@ -279,7 +319,7 @@ pub const TextElementConfig = extern struct { font_size: u16 = 20, letter_spacing: u16 = 0, line_height: u16 = 0, - wrap_mode: TextWrapMode = .Newlines, + wrap_mode: TextWrapMode = .Words, }; pub const ImageElementConfig = extern struct { @@ -300,8 +340,8 @@ pub const CustomElementConfig = extern struct { }; pub const ScrollElementConfig = extern struct { - horizontal: bool, - vertical: bool, + horizontal: bool = false, + vertical: bool = false, }; pub const ElementConfigType = enum(EnumBackingType) { @@ -414,3 +454,15 @@ pub fn makeClayString(string: []const u8) String { pub fn text(string: []const u8, config: Config) void { cdefs.Clay__OpenTextElement(makeClayString(string), config.Text); } + +pub fn ID(string: []const u8) ElementId { + return hashString(makeClayString(string), 0, 0); +} + +pub fn IDI(string: []const u8, index: u32) ElementId { + return hashString(makeClayString(string), index, 0); +} + +pub fn getElementId(string: []const u8) ElementId { + return cdefs.Clay_GetElementId(makeClayString(string)); +}