mirror of
https://github.com/nicbarker/clay.git
synced 2025-04-22 06:08:03 +00:00
Compare commits
4 Commits
f2c9bf2cb7
...
5d772d3d46
Author | SHA1 | Date | |
---|---|---|---|
|
5d772d3d46 | ||
|
e89f8901a8 | ||
|
1dfa54af6d | ||
|
3286ef532a |
@ -9,6 +9,7 @@ option(CLAY_INCLUDE_CPP_EXAMPLE "Build C++ example" OFF)
|
|||||||
option(CLAY_INCLUDE_RAYLIB_EXAMPLES "Build raylib examples" OFF)
|
option(CLAY_INCLUDE_RAYLIB_EXAMPLES "Build raylib examples" OFF)
|
||||||
option(CLAY_INCLUDE_SDL2_EXAMPLES "Build SDL 2 examples" OFF)
|
option(CLAY_INCLUDE_SDL2_EXAMPLES "Build SDL 2 examples" OFF)
|
||||||
option(CLAY_INCLUDE_SDL3_EXAMPLES "Build SDL 3 examples" OFF)
|
option(CLAY_INCLUDE_SDL3_EXAMPLES "Build SDL 3 examples" OFF)
|
||||||
|
option(CLAY_INCLUDE_SOKOL_EXAMPLES "Build Sokol examples" OFF)
|
||||||
|
|
||||||
message(STATUS "CLAY_INCLUDE_DEMOS: ${CLAY_INCLUDE_DEMOS}")
|
message(STATUS "CLAY_INCLUDE_DEMOS: ${CLAY_INCLUDE_DEMOS}")
|
||||||
|
|
||||||
@ -36,6 +37,10 @@ endif ()
|
|||||||
if(NOT MSVC AND (CLAY_INCLUDE_ALL_EXAMPLES OR CLAY_INCLUDE_SDL3_EXAMPLES))
|
if(NOT MSVC AND (CLAY_INCLUDE_ALL_EXAMPLES OR CLAY_INCLUDE_SDL3_EXAMPLES))
|
||||||
add_subdirectory("examples/SDL3-simple-demo")
|
add_subdirectory("examples/SDL3-simple-demo")
|
||||||
endif()
|
endif()
|
||||||
|
if(CLAY_INCLUDE_ALL_EXAMPLES OR CLAY_INCLUDE_SOKOL_EXAMPLES)
|
||||||
|
add_subdirectory("examples/sokol-video-demo")
|
||||||
|
add_subdirectory("examples/sokol-corner-radius")
|
||||||
|
endif()
|
||||||
|
|
||||||
# add_subdirectory("examples/cairo-pdf-rendering") Some issue with github actions populating cairo, disable for now
|
# add_subdirectory("examples/cairo-pdf-rendering") Some issue with github actions populating cairo, disable for now
|
||||||
|
|
||||||
|
5
examples/sokol-corner-radius/CMakeLists.txt
Normal file
5
examples/sokol-corner-radius/CMakeLists.txt
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.27)
|
||||||
|
project(sokol_corner_radius C)
|
||||||
|
|
||||||
|
add_executable(sokol_corner_radius main.c)
|
||||||
|
target_link_libraries(sokol_corner_radius PUBLIC sokol)
|
110
examples/sokol-corner-radius/main.c
Normal file
110
examples/sokol-corner-radius/main.c
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
#include "sokol_app.h"
|
||||||
|
#include "sokol_gfx.h"
|
||||||
|
#include "sokol_glue.h"
|
||||||
|
#include "sokol_log.h"
|
||||||
|
|
||||||
|
#define CLAY_IMPLEMENTATION
|
||||||
|
#include "../../clay.h"
|
||||||
|
|
||||||
|
#include "util/sokol_gl.h"
|
||||||
|
#include "fontstash.h"
|
||||||
|
#include "util/sokol_fontstash.h"
|
||||||
|
#define SOKOL_CLAY_IMPL
|
||||||
|
#include "../../renderers/sokol/sokol_clay.h"
|
||||||
|
|
||||||
|
static void init() {
|
||||||
|
sg_setup(&(sg_desc){
|
||||||
|
.environment = sglue_environment(),
|
||||||
|
.logger.func = slog_func,
|
||||||
|
});
|
||||||
|
sgl_setup(&(sgl_desc_t){
|
||||||
|
.logger.func = slog_func,
|
||||||
|
});
|
||||||
|
sclay_setup();
|
||||||
|
uint64_t totalMemorySize = Clay_MinMemorySize();
|
||||||
|
Clay_Arena clayMemory = Clay_CreateArenaWithCapacityAndMemory(totalMemorySize, malloc(totalMemorySize));
|
||||||
|
Clay_Initialize(clayMemory, (Clay_Dimensions){ (float)sapp_width(), (float)sapp_height() }, (Clay_ErrorHandler){});
|
||||||
|
Clay_SetMeasureTextFunction(sclay_measure_text, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
Clay_RenderCommandArray CornerRadiusTest(){
|
||||||
|
Clay_BeginLayout();
|
||||||
|
Clay_Sizing layoutExpand = {
|
||||||
|
.width = CLAY_SIZING_GROW(0),
|
||||||
|
.height = CLAY_SIZING_GROW(0)
|
||||||
|
};
|
||||||
|
CLAY({ .id = CLAY_ID("OuterContainer"),
|
||||||
|
.backgroundColor = {43, 41, 51, 255},
|
||||||
|
.layout = {
|
||||||
|
.layoutDirection = CLAY_TOP_TO_BOTTOM,
|
||||||
|
.sizing = layoutExpand,
|
||||||
|
.padding = {0, 0, 20, 20},
|
||||||
|
.childGap = 20
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
for(int i = 0; i < 6; ++i){
|
||||||
|
CLAY({ .id = CLAY_IDI("Row", i),
|
||||||
|
.layout = {
|
||||||
|
.layoutDirection = CLAY_LEFT_TO_RIGHT,
|
||||||
|
.sizing = layoutExpand,
|
||||||
|
.padding = {20, 20, 0, 0},
|
||||||
|
.childGap = 20
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
for(int j = 0; j < 6; ++j){
|
||||||
|
CLAY({ .id = CLAY_IDI("Tile", i*6+j),
|
||||||
|
.backgroundColor = {120, 140, 255, 128},
|
||||||
|
.cornerRadius = {(i%3)*15, (j%3)*15, (i/2)*15, (j/2)*15},
|
||||||
|
.border = {
|
||||||
|
.color = {120, 140, 255, 255},
|
||||||
|
.width = {3, 9, 6, 12, 0},
|
||||||
|
},
|
||||||
|
.layout = { .sizing = layoutExpand }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Clay_EndLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void frame() {
|
||||||
|
sclay_new_frame();
|
||||||
|
Clay_RenderCommandArray renderCommands = CornerRadiusTest();
|
||||||
|
|
||||||
|
sg_begin_pass(&(sg_pass){ .swapchain = sglue_swapchain() });
|
||||||
|
sgl_matrix_mode_modelview();
|
||||||
|
sgl_load_identity();
|
||||||
|
sclay_render(renderCommands, NULL);
|
||||||
|
sgl_draw();
|
||||||
|
sg_end_pass();
|
||||||
|
sg_commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void event(const sapp_event *ev) {
|
||||||
|
if(ev->type == SAPP_EVENTTYPE_KEY_DOWN && ev->key_code == SAPP_KEYCODE_D){
|
||||||
|
Clay_SetDebugModeEnabled(true);
|
||||||
|
} else {
|
||||||
|
sclay_handle_event(ev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cleanup() {
|
||||||
|
sclay_shutdown();
|
||||||
|
sgl_shutdown();
|
||||||
|
sg_shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
sapp_desc sokol_main(int argc, char **argv) {
|
||||||
|
return (sapp_desc){
|
||||||
|
.init_cb = init,
|
||||||
|
.frame_cb = frame,
|
||||||
|
.event_cb = event,
|
||||||
|
.cleanup_cb = cleanup,
|
||||||
|
.window_title = "Clay - Corner Radius Test",
|
||||||
|
.width = 800,
|
||||||
|
.height = 600,
|
||||||
|
.icon.sokol_default = true,
|
||||||
|
.logger.func = slog_func,
|
||||||
|
};
|
||||||
|
}
|
61
examples/sokol-video-demo/CMakeLists.txt
Normal file
61
examples/sokol-video-demo/CMakeLists.txt
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.27)
|
||||||
|
project(sokol_video_demo C)
|
||||||
|
|
||||||
|
include(FetchContent)
|
||||||
|
set(FETCHCONTENT_QUIET FALSE)
|
||||||
|
|
||||||
|
FetchContent_Declare(
|
||||||
|
fontstash
|
||||||
|
GIT_REPOSITORY "https://github.com/memononen/fontstash.git"
|
||||||
|
GIT_TAG "b5ddc9741061343740d85d636d782ed3e07cf7be"
|
||||||
|
GIT_PROGRESS TRUE
|
||||||
|
GIT_SHALLOW TRUE
|
||||||
|
)
|
||||||
|
FetchContent_MakeAvailable(fontstash)
|
||||||
|
|
||||||
|
FetchContent_Declare(
|
||||||
|
sokol
|
||||||
|
GIT_REPOSITORY "https://github.com/floooh/sokol.git"
|
||||||
|
GIT_TAG "da9de496f938b7575eff7f01ab774d77469bd390"
|
||||||
|
GIT_PROGRESS TRUE
|
||||||
|
GIT_SHALLOW TRUE
|
||||||
|
)
|
||||||
|
FetchContent_MakeAvailable(sokol)
|
||||||
|
set(sokol_HEADERS
|
||||||
|
${sokol_SOURCE_DIR}/sokol_app.h
|
||||||
|
${sokol_SOURCE_DIR}/sokol_gfx.h
|
||||||
|
${sokol_SOURCE_DIR}/sokol_glue.h
|
||||||
|
${sokol_SOURCE_DIR}/sokol_log.h
|
||||||
|
${sokol_SOURCE_DIR}/util/sokol_gl.h
|
||||||
|
${fontstash_SOURCE_DIR}/src/fontstash.h
|
||||||
|
${sokol_SOURCE_DIR}/util/sokol_fontstash.h)
|
||||||
|
if(CMAKE_SYSTEM_NAME STREQUAL Darwin)
|
||||||
|
add_library(sokol STATIC sokol.c ${sokol_HEADERS})
|
||||||
|
target_compile_options(sokol PRIVATE -x objective-c)
|
||||||
|
target_link_libraries(sokol PUBLIC
|
||||||
|
"-framework QuartzCore"
|
||||||
|
"-framework Cocoa"
|
||||||
|
"-framework MetalKit"
|
||||||
|
"-framework Metal")
|
||||||
|
else()
|
||||||
|
add_library(sokol STATIC sokol.c ${sokol_HEADERS})
|
||||||
|
if (CMAKE_SYSTEM_NAME STREQUAL Linux)
|
||||||
|
target_compile_definitions(sokol PRIVATE SOKOL_GLCORE=1)
|
||||||
|
target_link_libraries(sokol INTERFACE X11 Xi Xcursor GL dl m)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
target_include_directories(sokol INTERFACE ${sokol_SOURCE_DIR} ${fontstash_SOURCE_DIR}/src
|
||||||
|
PRIVATE ${sokol_SOURCE_DIR} ${fontstash_SOURCE_DIR}/src)
|
||||||
|
|
||||||
|
if(MSVC)
|
||||||
|
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /ENTRY:mainCRTStartup")
|
||||||
|
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /ENTRY:mainCRTStartup")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_executable(sokol_video_demo main.c)
|
||||||
|
target_link_libraries(sokol_video_demo PUBLIC sokol)
|
||||||
|
add_custom_command(
|
||||||
|
TARGET sokol_video_demo POST_BUILD
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/resources
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/resources)
|
79
examples/sokol-video-demo/main.c
Normal file
79
examples/sokol-video-demo/main.c
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
#include "sokol_app.h"
|
||||||
|
#include "sokol_gfx.h"
|
||||||
|
#include "sokol_glue.h"
|
||||||
|
#include "sokol_log.h"
|
||||||
|
|
||||||
|
#define CLAY_IMPLEMENTATION
|
||||||
|
#include "../../clay.h"
|
||||||
|
|
||||||
|
#include "util/sokol_gl.h"
|
||||||
|
#include "fontstash.h"
|
||||||
|
#include "util/sokol_fontstash.h"
|
||||||
|
#define SOKOL_CLAY_IMPL
|
||||||
|
#include "../../renderers/sokol/sokol_clay.h"
|
||||||
|
|
||||||
|
#include "../shared-layouts/clay-video-demo.c"
|
||||||
|
|
||||||
|
static ClayVideoDemo_Data demoData;
|
||||||
|
static sclay_font_t fonts[1];
|
||||||
|
|
||||||
|
static void init() {
|
||||||
|
sg_setup(&(sg_desc){
|
||||||
|
.environment = sglue_environment(),
|
||||||
|
.logger.func = slog_func,
|
||||||
|
});
|
||||||
|
sgl_setup(&(sgl_desc_t){
|
||||||
|
.logger.func = slog_func,
|
||||||
|
});
|
||||||
|
sclay_setup();
|
||||||
|
uint64_t totalMemorySize = Clay_MinMemorySize();
|
||||||
|
Clay_Arena clayMemory = Clay_CreateArenaWithCapacityAndMemory(totalMemorySize, malloc(totalMemorySize));
|
||||||
|
Clay_Initialize(clayMemory, (Clay_Dimensions){ (float)sapp_width(), (float)sapp_height() }, (Clay_ErrorHandler){});
|
||||||
|
fonts[FONT_ID_BODY_16] = sclay_add_font("resources/Roboto-Regular.ttf");
|
||||||
|
Clay_SetMeasureTextFunction(sclay_measure_text, &fonts);
|
||||||
|
demoData = ClayVideoDemo_Initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void frame() {
|
||||||
|
sclay_new_frame();
|
||||||
|
Clay_RenderCommandArray renderCommands = ClayVideoDemo_CreateLayout(&demoData);
|
||||||
|
|
||||||
|
sg_begin_pass(&(sg_pass){ .swapchain = sglue_swapchain() });
|
||||||
|
sgl_matrix_mode_modelview();
|
||||||
|
sgl_load_identity();
|
||||||
|
sclay_render(renderCommands, fonts);
|
||||||
|
sgl_draw();
|
||||||
|
sg_end_pass();
|
||||||
|
sg_commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void event(const sapp_event *ev) {
|
||||||
|
if(ev->type == SAPP_EVENTTYPE_KEY_DOWN && ev->key_code == SAPP_KEYCODE_D){
|
||||||
|
Clay_SetDebugModeEnabled(true);
|
||||||
|
} else {
|
||||||
|
sclay_handle_event(ev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cleanup() {
|
||||||
|
sclay_shutdown();
|
||||||
|
sgl_shutdown();
|
||||||
|
sg_shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
sapp_desc sokol_main(int argc, char **argv) {
|
||||||
|
return (sapp_desc){
|
||||||
|
.init_cb = init,
|
||||||
|
.frame_cb = frame,
|
||||||
|
.event_cb = event,
|
||||||
|
.cleanup_cb = cleanup,
|
||||||
|
.window_title = "Clay - Sokol Renderer Example",
|
||||||
|
.width = 800,
|
||||||
|
.height = 600,
|
||||||
|
.high_dpi = true,
|
||||||
|
.icon.sokol_default = true,
|
||||||
|
.logger.func = slog_func,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
int main();
|
BIN
examples/sokol-video-demo/resources/Roboto-Regular.ttf
Normal file
BIN
examples/sokol-video-demo/resources/Roboto-Regular.ttf
Normal file
Binary file not shown.
24
examples/sokol-video-demo/sokol.c
Normal file
24
examples/sokol-video-demo/sokol.c
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#define SOKOL_IMPL
|
||||||
|
#if defined(_WIN32)
|
||||||
|
#define SOKOL_D3D11
|
||||||
|
#elif defined(__EMSCRIPTEN__)
|
||||||
|
#define SOKOL_GLES2
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
#define SOKOL_METAL
|
||||||
|
#else
|
||||||
|
#define SOKOL_GLCORE33
|
||||||
|
#endif
|
||||||
|
#include "sokol_app.h"
|
||||||
|
#include "sokol_gfx.h"
|
||||||
|
#include "sokol_time.h"
|
||||||
|
#include "sokol_fetch.h"
|
||||||
|
#include "sokol_glue.h"
|
||||||
|
#include "sokol_log.h"
|
||||||
|
|
||||||
|
#include "util/sokol_gl.h"
|
||||||
|
#include <stdio.h> // fontstash requires this
|
||||||
|
#include <stdlib.h> // fontstash requires this
|
||||||
|
#define FONTSTASH_IMPLEMENTATION
|
||||||
|
#include "fontstash.h"
|
||||||
|
#define SOKOL_FONTSTASH_IMPL
|
||||||
|
#include "util/sokol_fontstash.h"
|
450
renderers/sokol/sokol_clay.h
Normal file
450
renderers/sokol/sokol_clay.h
Normal file
@ -0,0 +1,450 @@
|
|||||||
|
#ifndef SOKOL_CLAY_INCLUDED
|
||||||
|
#define SOKOL_CLAY_INCLUDED (1)
|
||||||
|
/*
|
||||||
|
sokol_clay.h -- drop-in Clay renderer for sokol_gfx.h
|
||||||
|
|
||||||
|
Do this:
|
||||||
|
#define SOKOL_CLAY_IMPL
|
||||||
|
|
||||||
|
before you include this file in *one* C file to create the
|
||||||
|
implementation.
|
||||||
|
|
||||||
|
Optionally provide the following configuration define both before including the
|
||||||
|
the declaration and implementation:
|
||||||
|
|
||||||
|
SOKOL_CLAY_NO_SOKOL_APP - don't depend on sokol_app.h (see below for details)
|
||||||
|
|
||||||
|
Include the following headers before sokol_clay.h (both before including
|
||||||
|
the declaration and implementation):
|
||||||
|
|
||||||
|
sokol_gl.h
|
||||||
|
sokol_fontstash.h
|
||||||
|
sokol_app.h (except SOKOL_CLAY_NO_SOKOL_APP)
|
||||||
|
clay.h
|
||||||
|
|
||||||
|
FEATURE OVERVIEW:
|
||||||
|
=================
|
||||||
|
sokol_clay.h implements the rendering and event-handling code for Clay
|
||||||
|
(https://github.com/nicbarker/clay) on top of sokol_gl.h and (optionally)
|
||||||
|
sokol_app.h.
|
||||||
|
|
||||||
|
Since sokol_fontstash.h already depends on sokol_gl.h, the rendering is
|
||||||
|
implemented using sokol_gl calls. (TODO: make fontstash optional?)
|
||||||
|
|
||||||
|
The sokol_app.h dependency is optional and used for input event handling.
|
||||||
|
If you only use sokol_gfx.h but not sokol_app.h in your application,
|
||||||
|
define SOKOL_CLAY_NO_SOKOL_APP before including the implementation
|
||||||
|
of sokol_clay.h, this will remove any dependency to sokol_app.h, but
|
||||||
|
you must call sclay_set_layout_dimensions and handle input yourself.
|
||||||
|
|
||||||
|
sokol_clay.h is not thread-safe, all calls must be made from the
|
||||||
|
same thread where sokol_gfx.h is running.
|
||||||
|
|
||||||
|
HOWTO:
|
||||||
|
======
|
||||||
|
|
||||||
|
--- To initialize sokol-clay, call sclay_setup(). This can be done
|
||||||
|
before or after Clay_Initialize.
|
||||||
|
|
||||||
|
--- Create an array of sclay_font_t and fill it by calling one of:
|
||||||
|
|
||||||
|
sclay_font_t sclay_add_font(const char *filename);
|
||||||
|
sclay_font_t sclay_add_font_mem(unsigned char *data, int dataLen);
|
||||||
|
|
||||||
|
The fontId value in Clay corresponds to indices in this array. After calling
|
||||||
|
Clay_Initialize but before calling any layout code, do this:
|
||||||
|
|
||||||
|
Clay_SetMeasureTextFunction(sclay_measure_text, &fonts);
|
||||||
|
|
||||||
|
where `fonts` is the abovementioned array.
|
||||||
|
|
||||||
|
--- At the start of a frame, call sclay_new_frame() if you're using sokol_app.h.
|
||||||
|
If you're not using sokol_app.h, call:
|
||||||
|
|
||||||
|
void sclay_set_layout_dimensions(Clay_Dimensions size, float dpi_scale);
|
||||||
|
|
||||||
|
at the start of the frame (or just when the window is resized.)
|
||||||
|
|
||||||
|
Either way, do some layout, then at the end of the frame call sclay_render:
|
||||||
|
|
||||||
|
sg_begin_pass(...)
|
||||||
|
// other rendering...
|
||||||
|
sclay_render(renderCommands, &fonts);
|
||||||
|
// other rendering...
|
||||||
|
sgl_draw();
|
||||||
|
sg_end_pass();
|
||||||
|
sg_commit();
|
||||||
|
|
||||||
|
One caveat: sclay_render assumes the default gl view matrix, and handles scaling
|
||||||
|
automatically. If you've adjusted the view matrix, remember to first call:
|
||||||
|
|
||||||
|
sgl_matrix_mode_modelview();
|
||||||
|
sgl_load_identity();
|
||||||
|
|
||||||
|
before calling sclay_render.
|
||||||
|
|
||||||
|
--- if you're using sokol_app.h, from inside the sokol_app.h event callback,
|
||||||
|
call:
|
||||||
|
|
||||||
|
void sclay_handle_event(const sapp_event* ev);
|
||||||
|
|
||||||
|
Unfortunately Clay does not currently provide feedback on whether a mouse
|
||||||
|
click was handled or not.
|
||||||
|
|
||||||
|
--- finally, on application shutdown, call
|
||||||
|
|
||||||
|
sclay_shutdown()
|
||||||
|
*/
|
||||||
|
#if !defined(SOKOL_CLAY_NO_SOKOL_APP) && !defined(SOKOL_APP_INCLUDED)
|
||||||
|
#error "Please include sokol_app.h before sokol_clay.h (or define SOKOL_CLAY_NO_SOKOL_APP)"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef int sclay_font_t;
|
||||||
|
|
||||||
|
void sclay_setup();
|
||||||
|
void sclay_shutdown();
|
||||||
|
|
||||||
|
sclay_font_t sclay_add_font(const char *filename);
|
||||||
|
sclay_font_t sclay_add_font_mem(unsigned char *data, int dataLen);
|
||||||
|
Clay_Dimensions sclay_measure_text(Clay_StringSlice text, Clay_TextElementConfig *config, void *userData);
|
||||||
|
|
||||||
|
#ifndef SOKOL_CLAY_NO_SOKOL_APP
|
||||||
|
void sclay_new_frame();
|
||||||
|
void sclay_handle_event(const sapp_event *ev);
|
||||||
|
#endif /* SOKOL_CLAY_NO_SOKOL_APP */
|
||||||
|
|
||||||
|
/* Use this if you don't call sclay_new_frame. `size` is the "virtual" size which
|
||||||
|
* your layout is relative to (ie. the actual framebuffer size divided by dpi_scale.)
|
||||||
|
* Set dpi_scale to 1 if you're not using high-dpi support. */
|
||||||
|
void sclay_set_layout_dimensions(Clay_Dimensions size, float dpi_scale);
|
||||||
|
|
||||||
|
void sclay_render(Clay_RenderCommandArray renderCommands, sclay_font_t *fonts);
|
||||||
|
|
||||||
|
#endif /* SOKOL_CLAY_INCLUDED */
|
||||||
|
|
||||||
|
#ifdef SOKOL_CLAY_IMPL
|
||||||
|
#define SOKOL_CLAY_IMPL_INCLUDED (1)
|
||||||
|
#ifndef SOKOL_GL_INCLUDED
|
||||||
|
#error "Please include sokol_gl.h before sokol_clay.h"
|
||||||
|
#endif
|
||||||
|
#ifndef SOKOL_FONTSTASH_INCLUDED
|
||||||
|
#error "Please include sokol_fontstash.h before sokol_clay.h"
|
||||||
|
#endif
|
||||||
|
#ifndef CLAY_HEADER
|
||||||
|
#error "Please include clay.h before sokol_clay.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
sgl_pipeline pip;
|
||||||
|
#ifndef SOKOL_CLAY_NO_SOKOL_APP
|
||||||
|
Clay_Vector2 mouse_pos, scroll;
|
||||||
|
bool mouse_down;
|
||||||
|
#endif
|
||||||
|
Clay_Dimensions size;
|
||||||
|
float dpi_scale;
|
||||||
|
FONScontext *fonts;
|
||||||
|
} _sclay_state_t;
|
||||||
|
static _sclay_state_t _sclay;
|
||||||
|
|
||||||
|
void sclay_setup() {
|
||||||
|
_sclay.pip = sgl_make_pipeline(&(sg_pipeline_desc){
|
||||||
|
.colors[0] = {
|
||||||
|
.blend = {
|
||||||
|
.enabled = true,
|
||||||
|
.src_factor_rgb = SG_BLENDFACTOR_SRC_ALPHA,
|
||||||
|
.dst_factor_rgb = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
});
|
||||||
|
#ifndef SOKOL_CLAY_NO_SOKOL_APP
|
||||||
|
_sclay.mouse_pos = (Clay_Vector2){0, 0};
|
||||||
|
_sclay.scroll = (Clay_Vector2){0, 0};
|
||||||
|
_sclay.mouse_down = false;
|
||||||
|
#endif
|
||||||
|
_sclay.size = (Clay_Dimensions){1, 1};
|
||||||
|
_sclay.dpi_scale = 1;
|
||||||
|
_sclay.fonts = sfons_create(&(sfons_desc_t){ 0 });
|
||||||
|
//TODO clay error handler?
|
||||||
|
}
|
||||||
|
|
||||||
|
void sclay_shutdown() {
|
||||||
|
sgl_destroy_pipeline(_sclay.pip);
|
||||||
|
sfons_destroy(_sclay.fonts);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef SOKOL_CLAY_NO_SOKOL_APP
|
||||||
|
void sclay_handle_event(const sapp_event* ev) {
|
||||||
|
switch(ev->type){
|
||||||
|
case SAPP_EVENTTYPE_MOUSE_MOVE:
|
||||||
|
_sclay.mouse_pos.x = ev->mouse_x / _sclay.dpi_scale;
|
||||||
|
_sclay.mouse_pos.y = ev->mouse_y / _sclay.dpi_scale;
|
||||||
|
break;
|
||||||
|
case SAPP_EVENTTYPE_MOUSE_DOWN:
|
||||||
|
_sclay.mouse_down = true;
|
||||||
|
break;
|
||||||
|
case SAPP_EVENTTYPE_MOUSE_UP:
|
||||||
|
_sclay.mouse_down = false;
|
||||||
|
break;
|
||||||
|
case SAPP_EVENTTYPE_MOUSE_SCROLL:
|
||||||
|
_sclay.scroll.x += ev->scroll_x;
|
||||||
|
_sclay.scroll.y += ev->scroll_y;
|
||||||
|
break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void sclay_new_frame() {
|
||||||
|
sclay_set_layout_dimensions((Clay_Dimensions){ (float)sapp_width(), (float)sapp_height() },
|
||||||
|
sapp_dpi_scale());
|
||||||
|
Clay_SetPointerState(_sclay.mouse_pos, _sclay.mouse_down);
|
||||||
|
Clay_UpdateScrollContainers(true, _sclay.scroll, sapp_frame_duration());
|
||||||
|
_sclay.scroll = (Clay_Vector2){0, 0};
|
||||||
|
}
|
||||||
|
#endif /* SOKOL_CLAY_NO_SOKOL_APP */
|
||||||
|
|
||||||
|
void sclay_set_layout_dimensions(Clay_Dimensions size, float dpi_scale) {
|
||||||
|
size.width /= dpi_scale;
|
||||||
|
size.height /= dpi_scale;
|
||||||
|
_sclay.size = size;
|
||||||
|
if(_sclay.dpi_scale != dpi_scale){
|
||||||
|
_sclay.dpi_scale = dpi_scale;
|
||||||
|
Clay_ResetMeasureTextCache();
|
||||||
|
}
|
||||||
|
Clay_SetLayoutDimensions(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
sclay_font_t sclay_add_font(const char *filename) {
|
||||||
|
//TODO log something if we get FONS_INVALID
|
||||||
|
return fonsAddFont(_sclay.fonts, "", filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
sclay_font_t sclay_add_font_mem(unsigned char *data, int dataLen) {
|
||||||
|
//TODO log something if we get FONS_INVALID
|
||||||
|
return fonsAddFontMem(_sclay.fonts, "", data, dataLen, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
Clay_Dimensions sclay_measure_text(Clay_StringSlice text, Clay_TextElementConfig *config, void *userData) {
|
||||||
|
sclay_font_t *fonts = (sclay_font_t *)userData;
|
||||||
|
if(!fonts) return (Clay_Dimensions){ 0 };
|
||||||
|
fonsSetFont(_sclay.fonts, fonts[config->fontId]);
|
||||||
|
fonsSetSize(_sclay.fonts, config->fontSize);
|
||||||
|
fonsSetSpacing(_sclay.fonts, config->letterSpacing);
|
||||||
|
float ascent, descent, lineh;
|
||||||
|
fonsVertMetrics(_sclay.fonts, &ascent, &descent, &lineh);
|
||||||
|
return (Clay_Dimensions) {
|
||||||
|
.width = fonsTextBounds(_sclay.fonts, 0, 0, text.chars, text.chars + text.length, NULL),
|
||||||
|
.height = ascent - descent
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _draw_rect(float x, float y, float w, float h){
|
||||||
|
sgl_v2f(x, y);
|
||||||
|
sgl_v2f(x, y);
|
||||||
|
sgl_v2f(x+w, y);
|
||||||
|
sgl_v2f(x, y+h);
|
||||||
|
sgl_v2f(x+w, y+h);
|
||||||
|
sgl_v2f(x+w, y+h);
|
||||||
|
}
|
||||||
|
|
||||||
|
static float _SIN[16] = {
|
||||||
|
0.000000f, 0.104528f, 0.207912f, 0.309017f,
|
||||||
|
0.406737f, 0.500000f, 0.587785f, 0.669131f,
|
||||||
|
0.743145f, 0.809017f, 0.866025f, 0.913545f,
|
||||||
|
0.951057f, 0.978148f, 0.994522f, 1.000000f,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* rx,ry = radius */
|
||||||
|
static void _draw_corner(float x, float y, float rx, float ry){
|
||||||
|
x -= rx;
|
||||||
|
y -= ry;
|
||||||
|
sgl_v2f(x, y);
|
||||||
|
for(int i = 0; i < 16; ++i){
|
||||||
|
sgl_v2f(x, y);
|
||||||
|
sgl_v2f(x+(rx*_SIN[15-i]), y+(ry*_SIN[i]));
|
||||||
|
}
|
||||||
|
sgl_v2f(x+(rx*_SIN[0]), y+(ry*_SIN[15]));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* rx,ry = radius ix,iy = inner radius */
|
||||||
|
static void _draw_corner_border(float x, float y, float rx, float ry, float ix, float iy){
|
||||||
|
x -= rx;
|
||||||
|
y -= ry;
|
||||||
|
sgl_v2f(x+(ix*_SIN[15]), y+(iy*_SIN[0]));
|
||||||
|
for(int i = 0; i < 16; ++i){
|
||||||
|
sgl_v2f(x+(ix*_SIN[15-i]), y+(iy*_SIN[i]));
|
||||||
|
sgl_v2f(x+(rx*_SIN[15-i]), y+(ry*_SIN[i]));
|
||||||
|
}
|
||||||
|
sgl_v2f(x+(rx*_SIN[0]), y+(ry*_SIN[15]));
|
||||||
|
}
|
||||||
|
|
||||||
|
void sclay_render(Clay_RenderCommandArray renderCommands, sclay_font_t *fonts) {
|
||||||
|
sgl_matrix_mode_modelview();
|
||||||
|
sgl_translate(-1.0f, 1.0f, 0.0f);
|
||||||
|
sgl_scale(2.0f/_sclay.size.width, -2.0f/_sclay.size.height, 1.0f);
|
||||||
|
sgl_disable_texture();
|
||||||
|
sgl_push_pipeline();
|
||||||
|
sgl_load_pipeline(_sclay.pip);
|
||||||
|
for (uint32_t i = 0; i < renderCommands.length; i++) {
|
||||||
|
Clay_RenderCommand *renderCommand = Clay_RenderCommandArray_Get(&renderCommands, i);
|
||||||
|
Clay_BoundingBox bbox = renderCommand->boundingBox;
|
||||||
|
switch (renderCommand->commandType) {
|
||||||
|
case CLAY_RENDER_COMMAND_TYPE_RECTANGLE: {
|
||||||
|
Clay_RectangleRenderData *config = &renderCommand->renderData.rectangle;
|
||||||
|
sgl_c4f(config->backgroundColor.r / 255.0f,
|
||||||
|
config->backgroundColor.g / 255.0f,
|
||||||
|
config->backgroundColor.b / 255.0f,
|
||||||
|
config->backgroundColor.a / 255.0f);
|
||||||
|
Clay_CornerRadius r = config->cornerRadius;
|
||||||
|
sgl_begin_triangle_strip();
|
||||||
|
if(r.topLeft > 0 || r.topRight > 0){
|
||||||
|
_draw_corner(bbox.x, bbox.y, -r.topLeft, -r.topLeft);
|
||||||
|
_draw_corner(bbox.x+bbox.width, bbox.y, r.topRight, -r.topRight);
|
||||||
|
_draw_rect(bbox.x+r.topLeft, bbox.y,
|
||||||
|
bbox.width-r.topLeft-r.topRight, CLAY__MAX(r.topLeft, r.topRight));
|
||||||
|
}
|
||||||
|
if(r.bottomLeft > 0 || r.bottomRight > 0){
|
||||||
|
_draw_corner(bbox.x, bbox.y+bbox.height, -r.bottomLeft, r.bottomLeft);
|
||||||
|
_draw_corner(bbox.x+bbox.width, bbox.y+bbox.height, r.bottomRight, r.bottomRight);
|
||||||
|
_draw_rect(bbox.x+r.bottomLeft,
|
||||||
|
bbox.y+bbox.height-CLAY__MAX(r.bottomLeft, r.bottomRight),
|
||||||
|
bbox.width-r.bottomLeft-r.bottomRight, CLAY__MAX(r.bottomLeft, r.bottomRight));
|
||||||
|
}
|
||||||
|
if(r.topLeft < r.bottomLeft){
|
||||||
|
if(r.topLeft < r.topRight){
|
||||||
|
_draw_rect(bbox.x, bbox.y+r.topLeft, r.topLeft, bbox.height-r.topLeft-r.bottomLeft);
|
||||||
|
_draw_rect(bbox.x+r.topLeft, bbox.y+r.topRight,
|
||||||
|
r.bottomLeft-r.topLeft, bbox.height-r.topRight-r.bottomLeft);
|
||||||
|
} else {
|
||||||
|
_draw_rect(bbox.x, bbox.y+r.topLeft, r.bottomLeft, bbox.height-r.topLeft-r.bottomLeft);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(r.bottomLeft < r.bottomRight){
|
||||||
|
_draw_rect(bbox.x, bbox.y+r.topLeft, r.bottomLeft, bbox.height-r.topLeft-r.bottomLeft);
|
||||||
|
_draw_rect(bbox.x+r.bottomLeft, bbox.y+r.topLeft,
|
||||||
|
r.topLeft-r.bottomLeft, bbox.height-r.topLeft-r.bottomRight);
|
||||||
|
} else {
|
||||||
|
_draw_rect(bbox.x, bbox.y+r.topLeft, r.topLeft, bbox.height-r.topLeft-r.bottomLeft);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(r.topRight < r.bottomRight){
|
||||||
|
if(r.topRight < r.topLeft){
|
||||||
|
_draw_rect(bbox.x+bbox.width-r.bottomRight, bbox.y+r.topLeft,
|
||||||
|
r.bottomRight-r.topRight, bbox.height-r.topLeft-r.bottomRight);
|
||||||
|
_draw_rect(bbox.x+bbox.width-r.topRight, bbox.y+r.topRight,
|
||||||
|
r.topRight, bbox.height-r.topRight-r.bottomRight);
|
||||||
|
} else {
|
||||||
|
_draw_rect(bbox.x+bbox.width-r.bottomRight, bbox.y+r.topRight,
|
||||||
|
r.bottomRight, bbox.height-r.topRight-r.bottomRight);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(r.bottomRight < r.bottomLeft){
|
||||||
|
_draw_rect(bbox.x+bbox.width-r.topRight, bbox.y+r.topRight,
|
||||||
|
r.topRight-r.bottomRight, bbox.height-r.topRight-r.bottomLeft);
|
||||||
|
_draw_rect(bbox.x+bbox.width-r.bottomRight, bbox.y+r.topRight,
|
||||||
|
r.bottomRight, bbox.height-r.topRight-r.bottomRight);
|
||||||
|
} else {
|
||||||
|
_draw_rect(bbox.x+bbox.width-r.topRight, bbox.y+r.topRight,
|
||||||
|
r.topRight, bbox.height-r.topRight-r.bottomRight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_draw_rect(bbox.x+CLAY__MAX(r.topLeft, r.bottomLeft),
|
||||||
|
bbox.y+CLAY__MAX(r.topLeft, r.topRight),
|
||||||
|
bbox.width-CLAY__MAX(r.topLeft, r.bottomLeft)-CLAY__MAX(r.topRight, r.bottomRight),
|
||||||
|
bbox.height-CLAY__MAX(r.topLeft, r.topRight)-CLAY__MAX(r.bottomLeft, r.bottomRight));
|
||||||
|
sgl_end();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CLAY_RENDER_COMMAND_TYPE_TEXT: {
|
||||||
|
if(!fonts) break;
|
||||||
|
Clay_TextRenderData *config = &renderCommand->renderData.text;
|
||||||
|
Clay_StringSlice text = config->stringContents;
|
||||||
|
fonsSetFont(_sclay.fonts, fonts[config->fontId]);
|
||||||
|
uint32_t color = sfons_rgba(
|
||||||
|
config->textColor.r,
|
||||||
|
config->textColor.g,
|
||||||
|
config->textColor.b,
|
||||||
|
config->textColor.a);
|
||||||
|
fonsSetColor(_sclay.fonts, color);
|
||||||
|
fonsSetSpacing(_sclay.fonts, config->letterSpacing * _sclay.dpi_scale);
|
||||||
|
fonsSetAlign(_sclay.fonts, FONS_ALIGN_LEFT | FONS_ALIGN_TOP);
|
||||||
|
fonsSetSize(_sclay.fonts, config->fontSize * _sclay.dpi_scale);
|
||||||
|
sgl_matrix_mode_modelview();
|
||||||
|
sgl_push_matrix();
|
||||||
|
sgl_scale(1.0f/_sclay.dpi_scale, 1.0f/_sclay.dpi_scale, 1.0f);
|
||||||
|
fonsDrawText(_sclay.fonts, bbox.x*_sclay.dpi_scale, bbox.y*_sclay.dpi_scale,
|
||||||
|
text.chars, text.chars + text.length);
|
||||||
|
sgl_pop_matrix();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CLAY_RENDER_COMMAND_TYPE_SCISSOR_START: {
|
||||||
|
sgl_scissor_rectf(bbox.x*_sclay.dpi_scale, bbox.y*_sclay.dpi_scale,
|
||||||
|
bbox.width*_sclay.dpi_scale, bbox.height*_sclay.dpi_scale,
|
||||||
|
true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CLAY_RENDER_COMMAND_TYPE_SCISSOR_END: {
|
||||||
|
sgl_scissor_rectf(0, 0,
|
||||||
|
_sclay.size.width*_sclay.dpi_scale, _sclay.size.height*_sclay.dpi_scale,
|
||||||
|
true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CLAY_RENDER_COMMAND_TYPE_IMAGE: {
|
||||||
|
//TODO
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CLAY_RENDER_COMMAND_TYPE_BORDER: {
|
||||||
|
Clay_BorderRenderData *config = &renderCommand->renderData.border;
|
||||||
|
sgl_c4f(config->color.r / 255.0f,
|
||||||
|
config->color.g / 255.0f,
|
||||||
|
config->color.b / 255.0f,
|
||||||
|
config->color.a / 255.0f);
|
||||||
|
Clay_BorderWidth w = config->width;
|
||||||
|
Clay_CornerRadius r = config->cornerRadius;
|
||||||
|
sgl_begin_triangle_strip();
|
||||||
|
if(w.left > 0){
|
||||||
|
_draw_rect(bbox.x, bbox.y + r.topLeft,
|
||||||
|
w.left, bbox.height - r.topLeft - r.bottomLeft);
|
||||||
|
}
|
||||||
|
if(w.right > 0){
|
||||||
|
_draw_rect(bbox.x + bbox.width - w.right, bbox.y + r.topRight,
|
||||||
|
w.right, bbox.height - r.topRight - r.bottomRight);
|
||||||
|
}
|
||||||
|
if(w.top > 0){
|
||||||
|
_draw_rect(bbox.x + r.topLeft, bbox.y,
|
||||||
|
bbox.width - r.topLeft - r.topRight, w.top);
|
||||||
|
}
|
||||||
|
if(w.bottom > 0){
|
||||||
|
_draw_rect(bbox.x + r.bottomLeft, bbox.y + bbox.height - w.bottom,
|
||||||
|
bbox.width - r.bottomLeft - r.bottomRight, w.bottom);
|
||||||
|
}
|
||||||
|
if(r.topLeft > 0 && (w.top > 0 || w.left > 0)){
|
||||||
|
_draw_corner_border(bbox.x, bbox.y,
|
||||||
|
-r.topLeft, -r.topLeft,
|
||||||
|
-r.topLeft+w.left, -r.topLeft+w.top);
|
||||||
|
}
|
||||||
|
if(r.topRight > 0 && (w.top > 0 || w.right > 0)){
|
||||||
|
_draw_corner_border(bbox.x+bbox.width, bbox.y,
|
||||||
|
r.topRight, -r.topRight,
|
||||||
|
r.topRight-w.right, -r.topRight+w.top);
|
||||||
|
}
|
||||||
|
if(r.bottomLeft > 0 && (w.bottom > 0 || w.left > 0)){
|
||||||
|
_draw_corner_border(bbox.x, bbox.y+bbox.height,
|
||||||
|
-r.bottomLeft, r.bottomLeft,
|
||||||
|
-r.bottomLeft+w.left, r.bottomLeft-w.bottom);
|
||||||
|
}
|
||||||
|
if(r.bottomRight > 0 && (w.bottom > 0 || w.right > 0)){
|
||||||
|
_draw_corner_border(bbox.x+bbox.width, bbox.y+bbox.height,
|
||||||
|
r.bottomRight, r.bottomRight,
|
||||||
|
r.bottomRight-w.right, r.bottomRight-w.bottom);
|
||||||
|
}
|
||||||
|
sgl_end();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sgl_pop_pipeline();
|
||||||
|
sfons_flush(_sclay.fonts);
|
||||||
|
}
|
||||||
|
#endif /* SOKOL_CLAY_IMPL */
|
Loading…
Reference in New Issue
Block a user