diff --git a/CMakeLists.txt b/CMakeLists.txt index 90d9055..f2a92f2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,33 +3,36 @@ project(clay C) set(CMAKE_C_STANDARD 17) +include(FetchContent) -# todo: use FetchContent instead https://cmake.org/cmake/help/latest/module/FetchContent.html#examples -find_package(PkgConfig) -pkg_check_modules(PC_CAIRO QUIET cairo) - -find_path(CAIRO_INCLUDE_DIRS - NAMES cairo.h - HINTS ${PC_CAIRO_INCLUDEDIR} - ${PC_CAIRO_INCLUDE_DIRS} - PATH_SUFFIXES cairo +FetchContent_Declare( + cairo + GIT_REPOSITORY https://gitlab.freedesktop.org/cairo/cairo.git + GIT_TAG c3b672634f0635af1ad0ffa8c15b34fc7c1035cf # 1.17.8 ) -find_library(CAIRO_LIBRARIES - NAMES cairo - HINTS ${PC_CAIRO_LIBDIR} - ${PC_CAIRO_LIBRARY_DIRS} -) +#FetchContent_MakeAvailable(cairo) -add_library(clay src/clay.c src/clay-base.c src/clay-context.c src/clay-layout.c src/clay-property.c src/clay-text.c - src/clay-flex.c src/clay-document.c src/clay-color.c) +add_library(clay + src/color.c + src/context.c + src/debug.c + src/document.c + src/flex.c + src/layout.c + src/property.c + src/render.c + src/text.c + ) + target_include_directories(clay PUBLIC include) -target_include_directories(clay PRIVATE ${CAIRO_INCLUDE_DIRS}) -target_link_libraries(clay PRIVATE ${CAIRO_LIBRARIES}) + +target_link_libraries(clay PRIVATE cairo) + target_compile_options(clay PRIVATE -Wall -Werror) -if(PROJECT_IS_TOP_LEVEL) +if (PROJECT_IS_TOP_LEVEL) add_executable(clay-demo src/demo.c) target_link_libraries(clay-demo PRIVATE clay) -endif() \ No newline at end of file +endif () \ No newline at end of file diff --git a/include/clay-base.h b/include/clay-base.h deleted file mode 100644 index 9a0cd9a..0000000 --- a/include/clay-base.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef CLAYOUT_CLAY_BASE_H -#define CLAYOUT_CLAY_BASE_H - - -typedef struct clay_ctx_t *clay_ctx; -typedef struct clay_t *clay; - - -clay_ctx clay_create_context(void); -void clay_destroy_context(clay_ctx); - - -void clay_set(clay, const char*, ...); - -clay clay_clone(clay); - - -#endif //CLAYOUT_CLAY_BASE_H diff --git a/include/clay-flex.h b/include/clay-flex.h deleted file mode 100644 index 5b4db8f..0000000 --- a/include/clay-flex.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef CLAYOUT_CLAY_FLEX_H -#define CLAYOUT_CLAY_FLEX_H - - -enum clay_flex_direction_e { - CLAY_FLEX_DIRECTION_ROW = 0, - CLAY_FLEX_DIRECTION_ROW_REVERSE = 1, - CLAY_FLEX_DIRECTION_COLUMN = 2, - CLAY_FLEX_DIRECTION_COLUMN_REVERSE = 3, -}; - -enum clay_flex_wrap_e { - CLAY_FLEX_WRAP_NO_WRAP = 0, - CLAY_FLEX_WRAP_WRAP = 1, - CLAY_FLEX_WRAP_WRAP_REVERSE = 2, -}; - -enum clay_flex_align_items_e { - CLAY_FLEX_ALIGN_ITEMS_START = 0, - CLAY_FLEX_ALIGN_ITEMS_END = 1, - CLAY_FLEX_ALIGN_ITEMS_CENTER = 2, - CLAY_FLEX_ALIGN_ITEMS_STRETCH = 3, -}; - -enum clay_flex_align_content_e { - CLAY_FLEX_ALIGN_CONTENT_START = 0, - CLAY_FLEX_ALIGN_CONTENT_END = 1, - CLAY_FLEX_ALIGN_CONTENT_CENTER = 2, - CLAY_FLEX_ALIGN_CONTENT_STRETCH = 3, - CLAY_FLEX_ALIGN_CONTENT_SPACE_BETWEEN = 4, - CLAY_FLEX_ALIGN_CONTENT_SPACE_AROUND = 5, - CLAY_FLEX_ALIGN_CONTENT_SPACE_EVENLY = 6, -}; - - -clay clay_create_flex(clay_ctx); - -void clay_flex_register_props(clay_ctx ctx); // todo: should be private - -#endif //CLAYOUT_CLAY_FLEX_H diff --git a/include/clay-properties.h b/include/clay-properties.h deleted file mode 100644 index 6a22090..0000000 --- a/include/clay-properties.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef CLAYOUT_CLAY_PROPERTIES_H -#define CLAYOUT_CLAY_PROPERTIES_H - - -#define CLAY_PROPERTY_TEXT 1 -#define CLAY_PROPERTY_BG_COLOR 2 -#define CLAY_PROPERTY_WIDTH 3 -#define CLAY_PROPERTY_FLEX_GROW 4 -#define CLAY_PROPERTY_FLEX_SHRINK 5 -#define CLAY_PROPERTY_TEXT_ALIGN 6 -#define CLAY_PROPERTY_TEXT_VERTICAL_ALIGN 7 -#define CLAY_PROPERTY_FLEX_DIRECTION 8 -#define CLAY_PROPERTY_FLEX_WRAP 9 -#define CLAY_PROPERTY_FLEX_ALIGN_ITEMS 10 -#define CLAY_PROPERTY_FLEX_ALIGN_CONTENT 11 -#define CLAY_PROPERTY_FLEX_GAP 12 -#define CLAY_PROPERTY_PADDING 13 -#define CLAY_PROPERTY_CONTENTS 14 -#define CLAY_PROPERTY_HEIGHT 15 -#define CLAY_PROPERTY_CONTENT 16 - - -#endif \ No newline at end of file diff --git a/include/clay-text.h b/include/clay-text.h deleted file mode 100644 index ad6074b..0000000 --- a/include/clay-text.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef CLAYOUT_CLAY_TEXT_H -#define CLAYOUT_CLAY_TEXT_H - -enum clay_text_align_e { - CLAY_TEXT_ALIGN_LEFT = 0, - CLAY_TEXT_ALIGN_CENTER = 1, - CLAY_TEXT_ALIGN_RIGHT = 2, -}; - -enum clay_text_vertical_align_e { - CLAY_TEXT_VERTICAL_ALIGN_TOP = 0, - CLAY_TEXT_VERTICAL_ALIGN_MIDDLE = 1, - CLAY_TEXT_VERTICAL_ALIGN_BOTTOM = 2, -}; - -clay clay_create_text(clay_ctx); - -void clay_text_register_props(clay_ctx ctx); // todo: should be private - -#endif //CLAYOUT_CLAY_TEXT_H diff --git a/include/clay.h b/include/clay.h index 46610f2..1fb7bea 100644 --- a/include/clay.h +++ b/include/clay.h @@ -2,19 +2,110 @@ #define CLAYOUT_CLAY_H -#include "clay-base.h" -#include "clay-properties.h" -#include "clay-flex.h" -#include "clay-text.h" +#include +#include -clay clay_create_document(clay_ctx); +typedef struct clay_ctx_t *clay_ctx; +typedef struct clay_t *clay; + +clay_ctx clay_create_context(void); + +void clay_destroy_context(clay_ctx); + +clay clay_create(clay_ctx ctx, const char *layout_class_name); + +clay clay_clone(clay); + +clay clay_clone_recursive(clay); + +void clay_layout_set_property(clay layout, const char *prop_name, int num_values, ...); + +/* + * A layout can only be child of one parent layout. + * If a layout that is already a child of a parent layout is appended as a child to another layout, + * it is automatically removed from its previous parent. + */ +void clay_append_child(clay parent, clay child); + +void clay_layout_destroy(clay layout); + void clay_render_to_png(clay, const char *); -void clay_debug_layout(clay doc); +void clay_print_layout_tree(clay doc); -void clay_document_register_props(clay_ctx ctx); // todo: should be private +typedef struct { + uint8_t red; + uint8_t green; + uint8_t blue; + uint8_t alpha; +} clay_color; +enum clay_prop_val_in_type { + TYPE_INT, + TYPE_DOUBLE, + TYPE_STRING, + TYPE_COLOR, +}; + +struct clay_prop_val_in { + enum clay_prop_val_in_type type; + union { + int value_int; + double value_double; + const char *value_string; + clay_color value_color; + }; +}; + +static inline struct clay_prop_val_in clay_make_prop_val(enum clay_prop_val_in_type type, ...) { + struct clay_prop_val_in result = {.type=type}; + va_list args; + va_start(args, type); + switch (type) { + case TYPE_INT: + result.value_int = va_arg(args, int); + break; + case TYPE_DOUBLE: + result.value_double = va_arg(args, double); + break; + case TYPE_STRING: + result.value_string = va_arg(args, const char *); + break; + case TYPE_COLOR: + result.value_color = va_arg(args, clay_color); + break; + } + va_end(args); + return result; +} + + +#define _clay_value(v) _Generic(v, \ + char *: clay_make_prop_val(TYPE_STRING, v), \ + const char *: clay_make_prop_val(TYPE_STRING, v), \ + int: clay_make_prop_val(TYPE_INT, v), \ + double: clay_make_prop_val(TYPE_DOUBLE, v), \ + clay_color: clay_make_prop_val(TYPE_COLOR, v) \ + ) + +#define _clay_set1(l, n, v1) clay_layout_set_property(l, n, 1, _clay_value(v1)) +#define _clay_set2(l, n, v1, v2) clay_layout_set_property(l, n, 2, _clay_value(v1), _clay_value(v2)) +#define _clay_set3(l, n, v1, v2, v3) clay_layout_set_property(l, n, 3, _clay_value(v1), _clay_value(v2), _clay_value(v3)) +#define _clay_set4(l, n, v1, v2, v3, v4) clay_layout_set_property(l, n, 4, _clay_value(v1), _clay_value(v2), _clay_value(v3), _clay_value(v4)) + +#define _clay_macro_overload(_1, _2, _3, _4, NAME, ...) NAME + +#define clay_set(l, n, ...) _clay_macro_overload(__VA_ARGS__, _clay_set4, _clay_set3, _clay_set2, _clay_set1)(l, n, __VA_ARGS__) + + +/////////////////////////////////////////////////////////////////////// + +void clay_document_register(clay_ctx ctx); // todo: should be private + +void clay_flex_register(clay_ctx ctx); // todo: should be private + +void clay_text_register(clay_ctx ctx); // todo: should be private #endif //CLAYOUT_CLAY_H diff --git a/src/clay-base.c b/src/clay-base.c deleted file mode 100644 index eef9230..0000000 --- a/src/clay-base.c +++ /dev/null @@ -1,7 +0,0 @@ -#include -#include -#include "clay-base.h" -#include - - - diff --git a/src/clay-color.h b/src/clay-color.h deleted file mode 100644 index 693544f..0000000 --- a/src/clay-color.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef CLAY_CLAY_COLOR_H -#define CLAY_CLAY_COLOR_H - -#include - -typedef struct { - uint8_t r; - uint8_t g; - uint8_t b; - uint8_t a; -} clay_color; - -clay_color clay_color_rgba(uint8_t r, uint8_t g, uint8_t b, uint8_t a); - -#endif //CLAY_CLAY_COLOR_H diff --git a/src/clay-context.c b/src/clay-context.c deleted file mode 100644 index bffd2bb..0000000 --- a/src/clay-context.c +++ /dev/null @@ -1,125 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include "clay-layout.h" - -#include "clay-context.h" - -// todo: thread safety? maybe as an option CLAY_ENABLE_THREADSAFE - -struct clay_ctx_t { - clay *layouts_ptrs; - size_t layouts_length; - size_t layouts_size; - clay_property_desc_s *property_descs; - size_t property_descs_length; - size_t property_descs_size; - int property_tag_cnt; -}; - - -clay_ctx clay_create_context(void) { - clay_ctx ctx = malloc(sizeof *ctx); - assert(ctx != NULL); - ctx->layouts_size = 16; - ctx->layouts_length = 0; - ctx->layouts_ptrs = malloc(sizeof(*ctx->layouts_ptrs) * ctx->layouts_size); - assert(ctx->layouts_ptrs != NULL); - ctx->property_tag_cnt = 0; - ctx->property_descs_size = 16; - ctx->property_descs_length = 0; - ctx->property_descs = malloc(sizeof(*ctx->property_descs) * ctx->property_descs_size); - assert(ctx->property_descs != NULL); - - - clay_text_register_props(ctx); - clay_flex_register_props(ctx); - clay_document_register_props(ctx); - - return ctx; -} - -void clay_destroy_context(clay_ctx ctx) { - // TODO: free everything owned by ctx - for (size_t i = 0; i < ctx->layouts_length; ++i) { - clay layout = ctx->layouts_ptrs[i]; - clay_cleanup_layout(layout); - free(layout); - } - free(ctx->layouts_ptrs); - free(ctx->property_descs); - free(ctx); -} - - -void clay_ctx_register_layout(clay_ctx ctx, clay layout) { - assert(ctx->layouts_length <= ctx->layouts_size); - if (ctx->layouts_length == ctx->layouts_size) { - size_t new_size = ctx->layouts_size * 2; - ctx->layouts_ptrs = realloc(ctx->layouts_ptrs, sizeof(*ctx->layouts_ptrs) * new_size); - assert(ctx->layouts_ptrs != NULL); - ctx->layouts_size = new_size; - } - ctx->layouts_length += 1; - ctx->layouts_ptrs[ctx->layouts_length - 1] = layout; -} - -void clay_ctx_unregister_layout(clay_ctx ctx, clay layout) { - for (size_t i = 0; i < ctx->layouts_length; ++i) { - if (ctx->layouts_ptrs[i] == layout) { - ctx->layouts_ptrs[i] = ctx->layouts_ptrs[ctx->layouts_length - 1]; - ctx->layouts_length -= 1; - } - } -} - -int clay_ctx_register_property(clay_ctx ctx, const char *name, clay_property_type type) { - assert (!clay_ctx_has_property(ctx, name)); - int tag = ++ctx->property_tag_cnt; - assert(ctx->property_descs_length <= ctx->property_descs_size); - if (ctx->property_descs_length == ctx->property_descs_size) { - size_t new_size = ctx->property_descs_size * 2; - ctx->property_descs = realloc(ctx->property_descs, sizeof(*ctx->property_descs) * new_size); - assert(ctx->property_descs != NULL); - ctx->property_descs_size = new_size; - } - ctx->property_descs_length += 1; - ctx->property_descs[ctx->property_descs_length - 1] = (clay_property_desc_s) { - .type = type, - .tag = tag, - .name = name - }; - return tag; -} - -void clay_ctx_unregister_property(clay_ctx ctx, int tag) { - for (size_t i = 0; i < ctx->property_descs_length; ++i) { - if (ctx->property_descs[i].tag == tag) { - ctx->property_descs[i] = ctx->property_descs[ctx->property_descs_length - 1]; - ctx->property_descs_length -= 1; - return; - } - } -} - -bool clay_ctx_has_property(clay_ctx ctx, const char *name) { - for (size_t i = 0; i < ctx->property_descs_length; ++i) { - if (strcmp(ctx->property_descs[i].name, name) == 0) { - return true; - } - } - return false; -} - -clay_property_desc clay_ctx_get_property_desc(clay_ctx ctx, const char *name) { - for (size_t i = 0; i < ctx->property_descs_length; ++i) { - if (strcmp(ctx->property_descs[i].name, name) == 0) { - return &ctx->property_descs[i]; - } - } - return NULL; -} diff --git a/src/clay-context.h b/src/clay-context.h deleted file mode 100644 index 9650e9e..0000000 --- a/src/clay-context.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef CLAY_CLAY_CONTEXT_H -#define CLAY_CLAY_CONTEXT_H - -#include "clay-property.h" -#include - -typedef struct { - const char * name; - int tag; - clay_property_type type; -} clay_property_desc_s, *clay_property_desc; -// TODO: properties should not keep track of the type and allow any type, or something? - - -void clay_ctx_register_layout(clay_ctx ctx, clay layout); -void clay_ctx_unregister_layout(clay_ctx ctx, clay layout); - -int clay_ctx_register_property(clay_ctx ctx, const char * name, clay_property_type type); -void clay_ctx_unregister_property(clay_ctx ctx, int tag); -bool clay_ctx_has_property(clay_ctx ctx, const char *name); -clay_property_desc clay_ctx_get_property_desc(clay_ctx ctx, const char * name); - -#endif //CLAY_CLAY_CONTEXT_H diff --git a/src/clay-document.c b/src/clay-document.c deleted file mode 100644 index 1749373..0000000 --- a/src/clay-document.c +++ /dev/null @@ -1,70 +0,0 @@ -#include "clay.h" -#include "clay-layout.h" -#include "clay-context.h" -#include "clay-color.h" - -static void cleanup_document(clay layout); - -static void init_document(clay layout); - -static void debug_document(clay layout); - -static struct layout_class layout_class_document = { - .init = &init_document, - .cleanup = &cleanup_document, - .debug = &debug_document, -}; - -static void init_document(clay layout) { - // todo: anything? -} - -static void cleanup_document(clay layout) { - // todo: anything? -} - -static void debug_document(clay layout) { - printf("document:\n"); - clay_property_value width_prop = clay_get_prop(layout, "width"); - clay_property_value height_prop = clay_get_prop(layout, "height"); - clay_property_value content_prop = clay_get_prop(layout, "content"); - clay_property_value bgcolor_prop = clay_get_prop(layout, "bg-color"); - - if (width_prop->type == CLAY_PROPERTY_INT) { - printf(" width: %d\n", width_prop->int_val); - } - if (height_prop->type == CLAY_PROPERTY_INT) { - printf(" height: %d\n", height_prop->int_val); - } - if (bgcolor_prop->type == CLAY_PROPERTY_POINTER) { - clay_color *color = (clay_color*)bgcolor_prop->pointer_val; - printf(" bg-color: (%d, %d, %d, %d)\n", color->r, color->g, color->b, color->a); - } - - printf("\n"); - - if (content_prop->type == CLAY_PROPERTY_POINTER && content_prop->pointer_val != NULL) { - clay content = (clay) content_prop->pointer_val; - content->class.debug(content); - } -} - - -clay clay_create_document(clay_ctx ctx) { - return clay_create_layout(ctx, layout_class_document); -} - -void clay_render_to_png(clay doc, const char *path) { - // todo -} -void clay_debug_layout(clay doc) { - doc->class.debug(doc); -} - - -void clay_document_register_props(clay_ctx ctx) { - clay_ctx_register_property(ctx, "width", CLAY_PROPERTY_INT); - clay_ctx_register_property(ctx, "height", CLAY_PROPERTY_INT); - clay_ctx_register_property(ctx, "content", CLAY_PROPERTY_POINTER); - clay_ctx_register_property(ctx, "bg-color", CLAY_PROPERTY_POINTER); -} \ No newline at end of file diff --git a/src/clay-flex.c b/src/clay-flex.c deleted file mode 100644 index 83d6901..0000000 --- a/src/clay-flex.c +++ /dev/null @@ -1,82 +0,0 @@ -#include "clay.h" -#include "clay-layout.h" -#include "clay-context.h" - -static void cleanup_flex(clay layout); - -static void init_flex(clay layout); - -static void debug_flex(clay layout); - -static struct layout_class layout_class_flex = { - .init = &init_flex, - .cleanup = &cleanup_flex, - .debug = &debug_flex, -}; - -static void init_flex(clay layout) { - // todo: anything? -} - -static void cleanup_flex(clay layout) { - // todo: anything? -} - -static void debug_flex(clay layout) { - printf("flex:\n"); - clay_property_value direction_prop = clay_get_prop(layout, "direction"); - clay_property_value wrap_prop = clay_get_prop(layout, "wrap"); - clay_property_value align_items_prop = clay_get_prop(layout, "align-items"); - clay_property_value align_content_prop = clay_get_prop(layout, "align-content"); - clay_property_value gap_prop = clay_get_prop(layout, "gap"); - clay_property_value padding_prop = clay_get_prop(layout, "padding"); - clay_property_value contents_prop = clay_get_prop(layout, "contents"); - - if (direction_prop->type == CLAY_PROPERTY_POINTER) { - printf(" direction: %s\n", (char*)direction_prop->pointer_val); - } - if (wrap_prop->type == CLAY_PROPERTY_POINTER) { - printf(" wrap: %s\n", (char*)wrap_prop->pointer_val); - } - if (align_items_prop->type == CLAY_PROPERTY_POINTER) { - printf(" align-items: %s\n", (char*)align_items_prop->pointer_val); - } - if (align_content_prop->type == CLAY_PROPERTY_POINTER) { - printf(" align-content: %s\n", (char*)align_content_prop->pointer_val); - } - if (gap_prop->type == CLAY_PROPERTY_INT) { - printf(" gap: %d\n", gap_prop->int_val); - } - if (padding_prop->type == CLAY_PROPERTY_INT) { - printf(" padding: %d\n", padding_prop->int_val); - } - - printf("\n"); - - if (contents_prop->type == CLAY_PROPERTY_POINTER) { - clay* ptr = (clay*)contents_prop->pointer_val; - while(*ptr != NULL) { - clay content = *ptr; - content->class.debug(content); - ptr += 1; - } - } -} - - -clay clay_create_flex(clay_ctx ctx) { - return clay_create_layout(ctx, layout_class_flex); -} - - -void clay_flex_register_props(clay_ctx ctx) { - clay_ctx_register_property(ctx, "direction", CLAY_PROPERTY_POINTER); - clay_ctx_register_property(ctx, "wrap", CLAY_PROPERTY_POINTER); - clay_ctx_register_property(ctx, "align-items", CLAY_PROPERTY_POINTER); - clay_ctx_register_property(ctx, "align-content", CLAY_PROPERTY_POINTER); - clay_ctx_register_property(ctx, "gap", CLAY_PROPERTY_INT); - clay_ctx_register_property(ctx, "padding", CLAY_PROPERTY_INT); - clay_ctx_register_property(ctx, "contents", CLAY_PROPERTY_POINTER); - clay_ctx_register_property(ctx, "flex:grow", CLAY_PROPERTY_POINTER); - clay_ctx_register_property(ctx, "flex:shrink", CLAY_PROPERTY_POINTER); -} \ No newline at end of file diff --git a/src/clay-layout.c b/src/clay-layout.c deleted file mode 100644 index 08ebf74..0000000 --- a/src/clay-layout.c +++ /dev/null @@ -1,78 +0,0 @@ -#include -#include -#include -#include "clay-layout.h" -#include "clay-context.h" -#include "clay-property.h" - - -clay clay_create_layout(clay_ctx ctx, struct layout_class class) { - clay layout = malloc(sizeof(*layout)); - assert(layout != NULL); - layout->properties = clay_propset_create(); - layout->class = class; - layout->ctx = ctx; - if (layout->class.init != NULL) { - layout->class.init(layout); - } - clay_ctx_register_layout(ctx, layout); - return layout; -} - -void clay_destroy_layout(clay layout) { - clay_ctx_unregister_layout(layout->ctx, layout); - clay_cleanup_layout(layout); - free(layout); -} - -void clay_cleanup_layout(clay layout) { - if (layout->class.cleanup != NULL) { - layout->class.cleanup(layout); - } - clay_propset_destroy(layout->properties); -} - -clay clay_clone(clay layout) { - clay cloned = malloc(sizeof(*cloned)); - cloned->class = layout->class; - cloned->ctx = layout->ctx; - cloned->properties = clay_propset_clone(layout->properties); - return cloned; -} - -clay_property_value clay_get_prop(clay layout, const char *prop) { - clay_ctx ctx = layout->ctx; - clay_property_desc desc = clay_ctx_get_property_desc(ctx, prop); - assert(desc != NULL); - assert(desc->type != CLAY_PROPERTY_NOT_SET); - return clay_property_get_by_tag(layout->properties, desc->tag); -} - -void clay_set(clay layout, const char *prop, ...) { - // TODO: use a macro so that a property can have values of different types - // the property registry should not keep the type then - // also the property registry can have some kind of alias mechanism for translating strings to enums or something - clay_ctx ctx = layout->ctx; - clay_property_desc desc = clay_ctx_get_property_desc(ctx, prop); - assert(desc != NULL); - assert(desc->type != CLAY_PROPERTY_NOT_SET); - struct clay_property_value value; - value.type = desc->type; - va_list args; - va_start(args, prop); - switch (desc->type) { - case CLAY_PROPERTY_NOT_SET: - break; - case CLAY_PROPERTY_INT: - value.int_val = va_arg(args, int); - break; - case CLAY_PROPERTY_FLOAT: - value.float_val = va_arg(args, double); - break; - case CLAY_PROPERTY_POINTER: - value.pointer_val = va_arg(args, void*); - break; - } - va_end(args); - clay_property_set_by_tag(layout->properties, desc->tag, value); -} \ No newline at end of file diff --git a/src/clay-layout.h b/src/clay-layout.h deleted file mode 100644 index 6f4099c..0000000 --- a/src/clay-layout.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef CLAY_CLAY_LAYOUT_H -#define CLAY_CLAY_LAYOUT_H - -#include "clay.h" -#include "clay-property.h" - -typedef void (*init_fn)(clay); - -typedef void (*cleanup_fn)(clay); -typedef void (*debug_fn)(clay); - - -struct layout_class { - init_fn init; - cleanup_fn cleanup; - debug_fn debug; -}; - -struct clay_t { - struct layout_class class; - clay_property_set properties; - clay_ctx ctx; -}; - -clay clay_create_layout(clay_ctx ctx, struct layout_class class); - -void clay_destroy_layout(clay layout); - -void clay_cleanup_layout(clay layout); - -clay_property_value clay_get_prop(clay layout, const char *prop); - -#endif //CLAY_CLAY_LAYOUT_H diff --git a/src/clay-list.h b/src/clay-list.h new file mode 100644 index 0000000..fa53826 --- /dev/null +++ b/src/clay-list.h @@ -0,0 +1,92 @@ +#ifndef CLAY_CLAY_LIST_H +#define CLAY_CLAY_LIST_H + +#ifndef __GNUC__ +#error "this requires GCC" // todo: make clang-compatible if possible +#endif + +#include +#include +#include +#include + + +#ifndef CLAY_LIST_DEFAULT_INITIAL_SIZE +# define CLAY_LIST_DEFAULT_INITIAL_SIZE 16 +#endif + +#define CLAY_LIST_TYPE(list$name, list$element_type) \ + typedef struct { \ + list$element_type *elements; \ + size_t length; \ + size_t size;\ + } list$name ## _s, * list$name; \ + typedef const list$name ## _s * list$name ## _const; + + +#define CLAY_LIST_FOREACH(foreach$list, foreach$elptr, foreach$idx) \ + for (int list_foreach$next = 1; list_foreach$next; (list_foreach$next = 0)) \ + for (size_t foreach$idx = 0; list_foreach$next && foreach$idx < (foreach$list)->length; ++foreach$idx) \ + for (typeof((foreach$list)->elements) foreach$elptr = &(foreach$list)->elements[foreach$idx]; list_foreach$next;) \ + for (; !(list_foreach$next = 0); ({list_foreach$next = 1;break;})) + + +#define clay_list_create_1(list_create_1$type) clay_list_create_2(list_create_1$type, CLAY_LIST_DEFAULT_INITIAL_SIZE) + +#define clay_list_create_2(list_create_2$type, list_create_2$initial_size) ({ \ + assert((list_create_2$initial_size) > 0); \ + list_create_2$type list_create_2$list = malloc(sizeof(*list_create_2$list)); \ + assert(list_create_2$list != NULL); \ + clay_list_init(list_create_2$list, (list_create_2$initial_size)); \ + list_create_2$list; \ +}) + +#define clay_list_create(...) CMOBALL(clay_list_create, __VA_ARGS__) + + + +#define clay_list_init(list_init$list, list_init$initial_size) ({ \ + (list_init$list)->length = 0; \ + (list_init$list)->size = (list_init$initial_size); \ + (list_init$list)->elements = malloc(sizeof(*(list_init$list)->elements) * (list_init$list)->size); \ + assert((list_init$list)->elements != NULL); \ +}) + +#define clay_list_append(list_append$list, list_append$el) ({ \ + assert((list_append$list)->length <= (list_append$list)->size); \ + if ((list_append$list)->length == (list_append$list)->size) { \ + size_t list_append$new_size = (list_append$list)->size * 2; \ + (list_append$list)->elements = realloc((list_append$list)->elements, sizeof(*(list_append$list)->elements) * list_append$new_size); \ + (list_append$list)->size = list_append$new_size; \ + assert((list_append$list)->elements != NULL); \ + } \ + (list_append$list)->length += 1; \ + (list_append$list)->elements[(list_append$list)->length - 1] = (list_append$el); \ +}) + +#define clay_list_remove(list_remove$list, list_remove$idx) ({ \ + assert((list_remove$idx) < (list_remove$list)->length); \ + if ((list_remove$idx) < (list_remove$list)->length - 1) { \ + memmove((list_remove$list)->elements + (list_remove$idx), (list_remove$list)->elements + (list_remove$idx) + 1, ((list_remove$list)->length - (list_remove$idx) - 1) * sizeof(*(list_remove$list)->elements)); \ + } \ + (list_remove$list)->length -= 1; \ +}) + +#define clay_list_destroy(list_destroy$list) ({ \ + clay_list_empty(list_destroy$list); \ + free(list_destroy$list); \ +}) + +#define clay_list_empty(list_empty$list) ({ \ + free((list_empty$list)->elements); \ + (list_empty$list)->elements = NULL; \ +}) + +#define clay_list_clone(list_clone$list) ({ \ + typeof(list_clone$list) list_clone$new = clay_list_create(typeof(list_clone$list), (list_clone$list)->length); \ + memcpy(list_clone$new->elements, (list_clone$list)->elements, (list_clone$list)->length); \ + list_clone$new->length = (list_clone$list)->length; \ + list_clone$new; \ +}) + +#endif //CLAY_CLAY_LIST_H diff --git a/src/clay-map.h b/src/clay-map.h new file mode 100644 index 0000000..db7124c --- /dev/null +++ b/src/clay-map.h @@ -0,0 +1,167 @@ +#ifndef CLAY_CLAY_MAP_H +#define CLAY_CLAY_MAP_H + +#ifndef __GNUC__ +#error "this requires GCC" // todo: make clang-compatible if possible +#endif + + +#include +#include + +#include "clay-list.h" +#include "cmoball.h" + +#ifndef CLAY_MAP_DEFAULT_INITIAL_SIZE +# define CLAY_MAP_DEFAULT_INITIAL_SIZE 16 +#endif + +#define CLAY_MAP_TYPE(map_type$name, map_type$key_type, map_type$value_type) \ + typedef struct { \ + map_type$key_type key; \ + map_type$value_type value; \ + } map_type$name ## _entry_s, *map_type$name ## _entry; \ + typedef int (* map_type$name ## _compare_fn)(map_type$key_type, map_type$key_type); \ + typedef struct { \ + map_type$name ## _entry_s *elements; \ + size_t length; \ + size_t size; \ + map_type$name ## _compare_fn compare_fn; \ + } map_type$name ## _s, *map_type$name; \ + typedef const map_type$name ## _s * map_type$name ## _const; + + +#define CLAY_STRING_MAP_TYPE(map_type$name, map_type$value_type) CLAY_MAP_TYPE(map_type$name, const char *, map_type$value_type) + + +#define CLAY_MAP_FOREACH(map_foreach$map, map_foreach$key, map_foreach$valueptr) \ + for (int map_foreach$next = 1; map_foreach$next; (map_foreach$next = 0)) \ + for (size_t map_foreach$i = 0; map_foreach$next && map_foreach$i < (map_foreach$map)->length; ++map_foreach$i) \ + for (typeof((map_foreach$map)->elements) map_foreach$el = &(map_foreach$map)->elements[map_foreach$i]; map_foreach$next; ({ break; })) \ + for (typeof(map_foreach$el->key) map_foreach$key = map_foreach$el->key; map_foreach$next; ({ break; })) \ + for (typeof(map_foreach$el->value) *map_foreach$valueptr = &map_foreach$el->value; map_foreach$next;) \ + for (; !(map_foreach$next = 0); ({map_foreach$next = 1;break;})) + + +#define clay_map_init(map$, map$initial_size) clay_list_init((map$), (map$initial_size)) + + + +// note: map$type is never in parantheses in the substitution, because it is a type and not an expression! +#define clay_map_create_1(map$type) clay_map_create_default_cmp(map$type, CLAY_MAP_DEFAULT_INITIAL_SIZE) +#define clay_map_create_2(map$type, map$size_or_cmp) _Generic((map$size_or_cmp), int: clay_map_create_default_cmp(map$type, (size_t)(map$size_or_cmp)), default: clay_map_create_3(map$type, CLAY_MAP_DEFAULT_INITIAL_SIZE, (void*)(map$size_or_cmp))) +#define clay_map_create_default_cmp(map$type, map$size) clay_map_create_3(map$type, map$size, ({ map$type map$dummy; _Generic(map$dummy->elements->key, const char *: strcmp, default: NULL);})) +#define clay_map_create_3(map$type, map$size, map$cmp) ({ \ + assert((map$size) > 0); \ + map$type map$map = malloc(sizeof(*map$map)); \ + clay_map_init(map$map, (map$size)); \ + map$map->compare_fn = map$cmp; \ + map$map; \ +}) + +#define clay_map_create(...) CMOBALL(clay_map_create, __VA_ARGS__) + +CLAY_MAP_TYPE(some_map, int, int) + +CLAY_STRING_MAP_TYPE(other_map, int) + + +#define clay_map_empty(map$) free((map$)->elements) + +#define clay_map_destroy(map$) ({ \ + clay_map_empty(map$); \ + free(map$); \ +}) + +#define clay_map_set(map_set$map, map_set$key, map_set$value) ({ \ + bool map_set$found = false; \ + CLAY_LIST_FOREACH((map_set$map), map_set$el, map_set$_) { \ + if (((map_set$map)->compare_fn == NULL && map_set$el->key == map_set$key) || \ + ((map_set$map)->compare_fn != NULL && (map_set$map)->compare_fn(map_set$el->key, (map_set$key)) == 0)) { \ + map_set$el->value = (map_set$value); \ + map_set$found = true; \ + break; \ + } \ + } \ + if (!map_set$found) { \ + typeof(*(map_set$map)->elements) map_set$el = {.key = (map_set$key), .value = (map_set$value)}; \ + clay_list_append((map_set$map), map_set$el); \ + } \ +}) + +#define clay_map_get(map_get$map, map_get$key, map_get$value) ({ \ + bool map_get$found = false; \ + CLAY_LIST_FOREACH((map_get$map), map_get$el, map_get$_) { \ + if (((map_get$map)->compare_fn == NULL && map_get$el->key == (map_get$key)) || \ + ((map_get$map)->compare_fn != NULL && (map_get$map)->compare_fn(map_get$el->key, (map_get$key)) == 0)) { \ + *(map_get$value) = map_get$el->value; \ + map_get$found = true; \ + break; \ + } \ + } \ + map_get$found; \ +}) + +#define clay_map_get_ptr(map_get$map, map_get$key, map_get$value) ({ \ + bool map_get$found = false; \ + CLAY_LIST_FOREACH((map_get$map), map_get$el, map_get$_) { \ + if (((map_get$map)->compare_fn == NULL && map_get$el->key == (map_get$key)) || \ + ((map_get$map)->compare_fn != NULL && (map_get$map)->compare_fn(map_get$el->key, (map_get$key)) == 0)) { \ + *(map_get$value) = &map_get$el->value; \ + map_get$found = true; \ + break; \ + } \ + } \ + map_get$found; \ +}) + + +#define clay_map_get_default(map_get$map, map_get$key, map_get$default) ({ \ + typeof((map_get$map)->elements->value) map_get$value = map_get$default; \ + CLAY_LIST_FOREACH((map_get$map), map_get$el, map_get$_) { \ + if (((map_get$map)->compare_fn == NULL && map_get$el->key == (map_get$key)) || \ + ((map_get$map)->compare_fn != NULL && (map_get$map)->compare_fn(map_get$el->key, (map_get$key)) == 0)) { \ + map_get$value = map_get$el->value; \ + break; \ + } \ + } \ + map_get$value; \ +}) + + +#define clay_map_has_key(map_has_key$map, map_has_key$key) ({ \ + bool map_has_key$found = false; \ + CLAY_LIST_FOREACH((map_has_key$map), map_has_key$el, map_has_key$) { \ + if (((map_has_key$map)->compare_fn == NULL && map_has_key$el->key == (map_has_key$key)) || \ + ((map_has_key$map)->compare_fn != NULL && (map_has_key$map)->compare_fn(map_has_key$el->key, (map_has_key$key)) == 0)) { \ + map_has_key$found = true; \ + break; \ + } \ + } \ + map_has_key$found; \ +}) + + +#define clay_map_remove(map_remove$map, map_remove$key) ({ \ + bool map_remove$found = false; \ + CLAY_LIST_FOREACH((map_remove$map), map_remove$el, map_remove$idx) { \ + if (((map_remove$map)->compare_fn == NULL && map_remove$el->key == (map_remove$key)) || \ + ((map_remove$map)->compare_fn != NULL && (map_remove$map)->compare_fn(map_remove$el->key, (map_remove$key)) == 0)) { \ + clay_list_remove((map_remove$map), map_remove$idx); \ + map_remove$found = true; \ + } \ + } \ + map_remove$found; \ +}) + + +#define clay_map_clone(map_clone$map) ({ \ + typeof(map_clone$map) map_clone$map_new = clay_map_create(typeof(map_clone$map), (map_clone$map)->length); \ + memcpy(map_clone$map_new->elements, (map_clone$map)->elements, (map_clone$map)->length * sizeof(*map_clone$map_new->elements)); \ + map_clone$map_new->length = (map_clone$map)->length; \ + map_clone$map_new->compare_fn = (map_clone$map)->compare_fn; \ + map_clone$map_new; \ +}) + + +#endif //CLAY_CLAY_MAP_H diff --git a/src/clay-property.c b/src/clay-property.c deleted file mode 100644 index 4503601..0000000 --- a/src/clay-property.c +++ /dev/null @@ -1,87 +0,0 @@ -#include -#include "clay-property.h" - - -struct clay_property { - int tag; - struct clay_property_value value; -}; - -struct clay_property_set_t { - struct clay_property *props; - size_t length; - size_t size; -}; - -static struct clay_property_value empty_property_value = { - .type = CLAY_PROPERTY_NOT_SET, -}; - - -clay_property_set clay_propset_create(void) { - clay_property_set propset = malloc(sizeof(*propset)); - assert(propset != NULL); - propset->length = 0; - propset->size = 16; - propset->props = malloc(sizeof(*propset->props) * 16); - assert(propset->props != NULL); - return propset; -} - -clay_property_set clay_propset_clone(clay_property_set propset) { - clay_property_set cloned = malloc(sizeof(*cloned)); - assert(propset != NULL); - cloned->length = propset->length; - cloned->size = propset->size; - cloned->props = malloc(sizeof(*cloned->props) * cloned->size); - memcpy(cloned->props, propset->props, sizeof(*cloned->props) * cloned->size); - return cloned; -} - -void clay_propset_destroy(clay_property_set propset) { - free(propset->props); - free(propset); -} - - -clay_property_value clay_property_get_by_tag(clay_property_set propset, int tag) { - for (size_t i = 0; i < propset->length; ++i) { - struct clay_property *prop = &propset->props[i]; - if (prop->tag == tag) { - return &prop->value; - } - } - return &empty_property_value; -} - -void clay_property_set_by_tag(clay_property_set propset, int tag, struct clay_property_value value) { - struct clay_property *prop = NULL; - for (size_t i = 0; i < propset->length; ++i) { - if (propset->props[i].tag == tag) { - prop = &propset->props[i]; - break; - } - } - if (prop == NULL) { - assert(propset->length <= propset->size); - if (propset->length == propset->size) { - size_t new_size = propset->size * 2; - propset->props = realloc(propset->props, sizeof(*propset->props) * new_size); - assert(propset->props != NULL); - propset->size = new_size; - } - propset->length += 1; - prop = &propset->props[propset->length - 1]; - } - prop->tag = tag; - prop->value = value; -} - -void clay_property_delete_by_tag(clay_property_set propset, int tag) { - for (size_t i = 0; i < propset->length; ++i) { - struct clay_property *prop = &propset->props[i]; - if (prop->tag == tag) { - prop->value = empty_property_value; - } - } -} diff --git a/src/clay-property.h b/src/clay-property.h deleted file mode 100644 index 3d70c93..0000000 --- a/src/clay-property.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef CLAY_CLAY_PROPERTY_H -#define CLAY_CLAY_PROPERTY_H - -#include -#include -#include - - -typedef enum { - CLAY_PROPERTY_NOT_SET, - CLAY_PROPERTY_INT, - CLAY_PROPERTY_FLOAT, - CLAY_PROPERTY_POINTER, -} clay_property_type; - -struct clay_property_value { - clay_property_type type; - union { - int int_val; - double float_val; - void *pointer_val; - }; -}; - -typedef const struct clay_property_value *clay_property_value; - -typedef struct clay_property_set_t *clay_property_set; - - -clay_property_set clay_propset_create(void); -clay_property_set clay_propset_clone(clay_property_set); -void clay_propset_destroy(clay_property_set propset); - -clay_property_value clay_property_get_by_tag(clay_property_set propset, int tag); - -void clay_property_set_by_tag(clay_property_set propset, int tag, struct clay_property_value value); - -void clay_property_delete_by_tag(clay_property_set propset, int tag); - - -#endif //CLAY_CLAY_PROPERTY_H diff --git a/src/clay-text.c b/src/clay-text.c deleted file mode 100644 index 80d4036..0000000 --- a/src/clay-text.c +++ /dev/null @@ -1,67 +0,0 @@ -#include "clay.h" -#include "clay-layout.h" -#include "clay-context.h" -#include "clay-color.h" - -static void cleanup_text(clay layout); - -static void init_text(clay layout); -static void debug_text(clay layout); - -static struct layout_class layout_class_text = { - .cleanup = &cleanup_text, - .init = &init_text, - .debug = &debug_text, -}; - -static void init_text(clay layout) { - // todo: anything? -} - -static void cleanup_text(clay layout) { - // todo: anything? -} - -static void debug_text(clay layout) { - printf("text:\n"); - clay_property_value content_prop = clay_get_prop(layout, "content"); - clay_property_value bgcolor_prop = clay_get_prop(layout, "bg-color"); - clay_property_value width_prop = clay_get_prop(layout, "width"); - clay_property_value flex_grow_prop = clay_get_prop(layout, "flex:grow"); - clay_property_value flex_shrink_prop = clay_get_prop(layout, "flex:shrink"); - clay_property_value align_prop = clay_get_prop(layout, "align"); - clay_property_value vertical_align_prop = clay_get_prop(layout, "vertical-align"); - - if (content_prop->type == CLAY_PROPERTY_POINTER) { - printf(" content: %s\n", (char*)content_prop->pointer_val); - } - if (bgcolor_prop->type == CLAY_PROPERTY_POINTER) { - clay_color *color = (clay_color*)bgcolor_prop->pointer_val; - printf(" bg-color: (%d, %d, %d, %d)\n", color->r, color->g, color->b, color->a); - } - if (width_prop->type == CLAY_PROPERTY_INT) { - printf(" width: %d\n", width_prop->int_val); - } - if (flex_grow_prop->type == CLAY_PROPERTY_INT) { - printf(" flex-grow: %d\n", flex_grow_prop->int_val); - } - if (flex_shrink_prop->type == CLAY_PROPERTY_INT) { - printf(" flex-shrink: %d\n", flex_shrink_prop->int_val); - } - if (align_prop->type == CLAY_PROPERTY_POINTER) { - printf(" align: %s\n", (char*)align_prop->pointer_val); - } - if (vertical_align_prop->type == CLAY_PROPERTY_POINTER) { - printf(" vertical-align: %s\n", (char*)vertical_align_prop->pointer_val); - } -} - - -clay clay_create_text(clay_ctx ctx) { - return clay_create_layout(ctx, layout_class_text); -} - -void clay_text_register_props(clay_ctx ctx) { - clay_ctx_register_property(ctx, "align", CLAY_PROPERTY_POINTER); - clay_ctx_register_property(ctx, "vertical-align", CLAY_PROPERTY_POINTER); -} \ No newline at end of file diff --git a/src/clay.c b/src/clay.c deleted file mode 100644 index 11f6d60..0000000 --- a/src/clay.c +++ /dev/null @@ -1,20 +0,0 @@ -#include "clay.h" - -#include - -#include - -void hello(void) { - cairo_surface_t *surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 240, 80); - cairo_t *cr = cairo_create (surface); - - cairo_select_font_face (cr, "sans-serif", CAIRO_FONT_SLANT_ITALIC, CAIRO_FONT_WEIGHT_BOLD); - cairo_set_font_size (cr, 10.0); - cairo_set_source_rgb (cr, 0.0, 0.0, 1.0); - cairo_move_to (cr, 10.0, 50.0); - cairo_show_text (cr, "Hello, world"); - - cairo_destroy (cr); - cairo_surface_write_to_png (surface, "hello.png"); - cairo_surface_destroy (surface); -} diff --git a/src/cmoball.h b/src/cmoball.h new file mode 100644 index 0000000..d799e35 --- /dev/null +++ b/src/cmoball.h @@ -0,0 +1,63 @@ +/* + * Copyright 2021 Jorengarenar + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +/** + * from https://github.com/Jorengarenar/CMObALL/ + */ + +#ifndef CMOBALL_H_ +#define CMOBALL_H_ + +#define CMOBALL(NAME, ...) CMOBALL_OVR(NAME, CMOBALL_NUM_ARGS(__VA_ARGS__))(__VA_ARGS__) + +#define CMOBALL_OVR(name, num) CMOBALL__OVR(name, num) +#define CMOBALL__OVR(name, num) name ## _ ## num + +/* *INDENT-OFF* */ +#define CMOBALL_ARG_N( \ + _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, \ + _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, \ + _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, \ + _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, \ + _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, \ + _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, \ + _60, _61, _62, _63, \ + N, ...) N + +#define CMOBALL_RSEQ_N \ + 64, 63, 62, 61, 60, \ + 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, \ + 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, \ + 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, \ + 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, \ + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, \ + 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 +/* *INDENT-ON* */ + +#define CMOBALL_DETECT_0_ARGS(...) CMOBALL__DETECT_0 ## __VA_ARGS__ ## _ +#define CMOBALL__DETECT_0_ ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,0 + +#define CMOBALL__NUM_ARGS(ARGS) CMOBALL_ARG_N ARGS +#define CMOBALL_NUM_ARGS(...) CMOBALL__NUM_ARGS((CMOBALL_DETECT_0_ARGS(__VA_ARGS__), CMOBALL_RSEQ_N)) + +#endif /* CMOBALL_H_ */ diff --git a/src/clay-color.c b/src/color.c similarity index 60% rename from src/clay-color.c rename to src/color.c index 445e366..81c4623 100644 --- a/src/clay-color.c +++ b/src/color.c @@ -2,9 +2,9 @@ // Created by gwendolyn on 2/2/23. // -#include "clay-color.h" +#include "color.h" clay_color clay_color_rgba(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { - clay_color color = {.r = r, .g = g, .b = b, .a = a}; + clay_color color = {.red = r, .green = g, .blue = b, .alpha = a}; return color; } diff --git a/src/color.h b/src/color.h new file mode 100644 index 0000000..7d1dd49 --- /dev/null +++ b/src/color.h @@ -0,0 +1,9 @@ +#ifndef CLAY_COLOR_H +#define CLAY_COLOR_H + +#include +#include "clay.h" + +clay_color clay_color_rgba(uint8_t r, uint8_t g, uint8_t b, uint8_t a); + +#endif //CLAY_COLOR_H diff --git a/src/context.c b/src/context.c new file mode 100644 index 0000000..f86e6f6 --- /dev/null +++ b/src/context.c @@ -0,0 +1,157 @@ +#include +#include +#include +#include +#include +#include + +#include "layout.h" +#include "context.h" +#include "clay-map.h" +#include "clay-list.h" + + +// todo: thread safety? maybe as a macro option CLAY_ENABLE_THREADSAFE + + + +CLAY_STRING_MAP_TYPE(property_def_map, clay_prop_definition) + + +typedef struct { + clay_layout_class class; + property_def_map property_definitions; +} layout_class_container; + + +CLAY_STRING_MAP_TYPE(layout_class_map, layout_class_container) + + +struct clay_ctx_t { + layout_class_map layout_classes; + property_def_map global_property_definitions; + clay_layout_list layouts; +}; + + +clay_ctx clay_create_context(void) { + clay_ctx ctx = malloc(sizeof *ctx); + assert(ctx != NULL); + + ctx->layouts = clay_list_create(clay_layout_list); + ctx->layout_classes = clay_map_create(layout_class_map); + ctx->global_property_definitions = clay_map_create(property_def_map); + + // todo: need to figure out how to do that properly + // every module should register itself somehow, or be manually registered by the user + // the context implementation should not know about the different modules + clay_text_register(ctx); + clay_flex_register(ctx); + clay_document_register(ctx); + + return ctx; +} + +void clay_destroy_context(clay_ctx ctx) { + CLAY_LIST_FOREACH(ctx->layouts, layout, _) { + clay_layout_cleanup(*layout); + free(*layout); + } + clay_list_destroy(ctx->layouts); + clay_map_destroy(ctx->layout_classes); + clay_map_destroy(ctx->global_property_definitions); + free(ctx); +} + + +void clay_ctx_register_layout(clay_ctx ctx, clay layout) { + clay_list_append(ctx->layouts, layout); +} + +void clay_ctx_unregister_layout(clay_ctx ctx, clay layout) { + CLAY_LIST_FOREACH(ctx->layouts, l, idx) { + if (*l == layout) { + clay_list_remove(ctx->layouts, idx); + break; + } + } +} + +static clay_prop_definition transform_property_definition(clay_prop_definition_in *prop_def_in, bool global) { + clay_prop_definition prop_def = { + .allow_int = prop_def_in->allow_int, + .allow_float = prop_def_in->allow_float, + .allow_units = prop_def_in->allow_units, + .allow_color = prop_def_in->allow_color, + .allow_string = prop_def_in->allow_string, + .keywords = clay_map_create(clay_prop_keyword_map), + .inherits = prop_def_in->inherits, + .global = global, + .max_values = (prop_def_in->max_values == 0 ? 1 : prop_def_in->max_values), + }; + for (size_t i = 0; i < prop_def_in->keywords.num_entries; ++i) { + clay_map_set(prop_def.keywords, prop_def_in->keywords.entries[i].keyword, prop_def_in->keywords.entries[i].value); + } + return prop_def; +} + +void clay_ctx_register_global_property(clay_ctx ctx, const char *name, clay_prop_definition_in *prop_def) { + assert(!clay_map_has_key(ctx->global_property_definitions, name)); + + clay_map_set(ctx->global_property_definitions, name, transform_property_definition(prop_def, true)); +} + +void clay_ctx_register_class_property(clay_ctx ctx, const char *class_name, const char *name, + clay_prop_definition_in *prop_def) { + layout_class_container *class_container; + bool class_exists = clay_map_get_ptr(ctx->layout_classes, class_name, &class_container); + assert(class_exists); + if (class_exists) { + assert(!clay_map_has_key(class_container->property_definitions, name)); + clay_map_set(class_container->property_definitions, name, transform_property_definition(prop_def, false)); + } +} + +const clay_prop_definition *clay_ctx_get_property_definition(clay_ctx ctx, clay_layout_class class, const char *name) { + clay_prop_definition *definition; + CLAY_MAP_FOREACH(ctx->layout_classes, _, container) { + (void)_; + if (container->class == class) { + if (clay_map_get_ptr(container->property_definitions, name, &definition)) { + return definition; + } + break; + } + } + if (clay_map_get_ptr(ctx->global_property_definitions, name, &definition)) { + return definition; + } + return NULL; +} + +void clay_ctx_register_layout_class(clay_ctx ctx, const char *name, clay_layout_class class) { + assert(!clay_map_has_key(ctx->layout_classes, name)); + layout_class_container container = { + .class = class, + .property_definitions = clay_map_create(property_def_map), + }; + clay_map_set(ctx->layout_classes, name, container); +} + +clay_layout_class clay_ctx_get_layout_class(clay_ctx ctx, const char *name) { + layout_class_container container; + if (clay_map_get(ctx->layout_classes, name, &container)) { + return container.class; + } else { + return NULL; + } +} + +const char * clay_ctx_get_layout_class_name(clay_ctx ctx, clay_layout_class class) { + CLAY_MAP_FOREACH(ctx->layout_classes, name, container) { + if (container->class == class){ + return name; + } + } + return NULL; +} \ No newline at end of file diff --git a/src/context.h b/src/context.h new file mode 100644 index 0000000..97e4917 --- /dev/null +++ b/src/context.h @@ -0,0 +1,30 @@ +#ifndef CLAY_CONTEXT_H +#define CLAY_CONTEXT_H + +#include + +#include "clay-list.h" +#include "property.h" +#include "layout.h" + +void clay_ctx_register_layout(clay_ctx ctx, clay layout); +void clay_ctx_unregister_layout(clay_ctx ctx, clay layout); + +const clay_prop_definition *clay_ctx_get_property_definition(clay_ctx ctx, clay_layout_class class, const char *name); + + +void clay_ctx_register_layout_class(clay_ctx ctx, const char *name, clay_layout_class class); + +clay_layout_class clay_ctx_get_layout_class(clay_ctx ctx, const char *name); + +const char * clay_ctx_get_layout_class_name(clay_ctx ctx, clay_layout_class class); + +void clay_ctx_register_global_property(clay_ctx ctx, const char *name, clay_prop_definition_in *prop_def); + + +void clay_ctx_register_class_property(clay_ctx ctx, const char *class_name, const char *name, + clay_prop_definition_in *prop_def); + + + +#endif //CLAY_CONTEXT_H diff --git a/src/debug.c b/src/debug.c new file mode 100644 index 0000000..1c5e43a --- /dev/null +++ b/src/debug.c @@ -0,0 +1,105 @@ +#include +#include "clay.h" +#include "layout.h" + +static size_t stringify_prop_value(struct clay_prop_val_s val, clay_prop_definition propdef, char *out, size_t max_len); + +static void stringify_prop_values(clay_prop_vals_const vals, clay_prop_definition propdef, char *out, size_t max_len); + +#define DEBUG_MAX_PROP_STR_LEN 2047 + +void clay_print_layout_tree(clay doc) { + clay_prop_map props = clay_layout_get_properties(doc); + int depth = 0; + for (clay cur = doc->parent; cur != NULL; cur = cur->parent, depth++); + int indent = depth * 4; + printf("%*s<%s>:\n", indent, "", clay_ctx_get_layout_class_name(doc->ctx, doc->class)); + char *prop_string = malloc(DEBUG_MAX_PROP_STR_LEN + 1); + CLAY_MAP_FOREACH(props, prop_name, prop_val_ptr) { + const clay_prop_definition *propdef = clay_ctx_get_property_definition(doc->ctx, doc->class, + prop_name); + stringify_prop_values(prop_val_ptr->values, *propdef, prop_string, DEBUG_MAX_PROP_STR_LEN); + if (prop_val_ptr->inheritance_level == 0) { + printf("%*s %s: %s\n", indent, "", prop_name, prop_string); + } else { + printf("%*s [%s]: %s\n", indent, "", prop_name, prop_string); + } + } + free(prop_string); + clay_layout_list_const children = clay_layout_get_children(doc); + if (children->length > 0) { + printf("%*s children:\n", indent, ""); + CLAY_LIST_FOREACH(children, childptr, _) { + clay_print_layout_tree(*childptr); + } + } +} + + +static void stringify_prop_values(clay_prop_vals_const vals, clay_prop_definition propdef, char *out, size_t max_len) { + size_t pos = 0; + CLAY_LIST_FOREACH(vals, valptr, _) { + pos += stringify_prop_value(*valptr, propdef, out + pos, max_len - pos); + pos += snprintf(out + pos, max_len - pos, " "); + } +} + +static size_t +stringify_prop_value(struct clay_prop_val_s val, clay_prop_definition propdef, char *out, size_t max_len) { + size_t len = 0; + switch (val.type) { + case CLAY_PROP_TYPE_KEYWORD: { + bool found = false; + CLAY_MAP_FOREACH(propdef.keywords, keyword_string, numptr) { + if (*numptr == val.keyword) { + len += snprintf(out, max_len - len, "%s", keyword_string); + found = true; + break; + } + } + if (!found) { + len += snprintf(out, max_len - len, ""); + } + break; + } + case CLAY_PROP_TYPE_NUMBER: { + if (val.number.type == CLAY_PROP_NUMBER_INT) { + len += snprintf(out, max_len - len, "%" PRId64, val.number.value_int); + } else { + len += snprintf(out, max_len - len, "%f", val.number.value_double); + } + const char *unit_str; + switch (val.number.unit) { + case CLAY_PROP_UNIT_PX: + unit_str = "px"; + break; + case CLAY_PROP_UNIT_PT: + unit_str = "pt"; + break; + case CLAY_PROP_UNIT_CM: + unit_str = "cm"; + break; + case CLAY_PROP_UNIT_MM: + unit_str = "mm"; + break; + case CLAY_PROP_UNIT_PERCENT: + unit_str = "%"; + break; + default: + unit_str = ""; + } + len += snprintf(out + len, max_len - len, "%s", unit_str); + break; + } + case CLAY_PROP_TYPE_COLOR: { + len += snprintf(out, max_len - len, "rgba(%" PRId8 ", %" PRId8 ", %" PRId8 ", %" PRId8 ")", + val.color.red, val.color.green, val.color.blue, val.color.alpha); + break; + } + case CLAY_PROP_TYPE_STRING: { + len += snprintf(out, max_len - len, "'%s'", val.string); + break; + } + } + return len; +} \ No newline at end of file diff --git a/src/demo.c b/src/demo.c index 4676049..bd3684f 100644 --- a/src/demo.c +++ b/src/demo.c @@ -1,6 +1,7 @@ #include #include "clay.h" -#include "clay-color.h" +#include "color.h" +#include "layout.h" int main(int argc, char **argv) { @@ -11,55 +12,58 @@ int main(int argc, char **argv) { clay_color c3 = clay_color_rgba(220, 0, 115, 128); clay_color c4 = clay_color_rgba(0, 139, 248, 128); clay_color c5 = clay_color_rgba(71, 0, 99, 128); - clay_color cbg = clay_color_rgba(206, 249, 242, 255); - clay t1 = clay_create_text(ctx); - clay_set(t1, "content", "ITEM 1"); - clay_set(t1, "bg-color", &c1); + clay t1 = clay_create(ctx, "text"); + clay_set(t1, "text", "ITEM 1"); + clay_set(t1, "background-color", c1); clay_set(t1, "width", 100); - clay_set(t1, "flex:grow", 1); - clay_set(t1, "flex:shrink", 1); + clay_set(t1, "flex-grow", 1); + clay_set(t1, "flex-shrink", 1); clay_set(t1, "align", "center"); clay_set(t1, "vertical-align", "middle"); clay t2 = clay_clone(t1); - clay_set(t2, "content", "ITEM 2"); - clay_set(t2, "bg-color", &c2); + clay_set(t2, "text", "ITEM 2"); + clay_set(t2, "background-color", c2); clay_set(t2, "width", 300); clay t3 = clay_clone(t1); - clay_set(t3, "content", "ITEM 3"); - clay_set(t3, "bg-color", &c3); + clay_set(t3, "text", "ITEM 3"); + clay_set(t3, "background-color", c3); clay_set(t3, "width", 200); clay t4 = clay_clone(t1); - clay_set(t4, "content", "ITEM 4"); - clay_set(t4, "bg-color", &c4); + clay_set(t4, "text", "ITEM 4"); + clay_set(t4, "background-color", c4); clay_set(t4, "width", 400); clay t5 = clay_clone(t1); - clay_set(t5, "content", "ITEM 5"); - clay_set(t5, "bg-color", &c5); + clay_set(t5, "text", "ITEM 5"); + clay_set(t5, "background-color", c5); clay_set(t5, "width", 250); - clay flex = clay_create_flex(ctx); + clay flex = clay_create(ctx, "flex"); clay_set(flex, "direction", "row"); clay_set(flex, "wrap", "wrap"); clay_set(flex, "align-items", "stretch"); clay_set(flex, "align-content", "stretch"); clay_set(flex, "gap", 20); clay_set(flex, "padding", 20); - clay_set(flex, "contents", (clay[]) {t1, t2, t3, t4, t5, NULL}); + clay_append_child(flex, t1); + clay_append_child(flex, t2); + clay_append_child(flex, t3); + clay_append_child(flex, t4); + clay_append_child(flex, t5); - clay doc = clay_create_document(ctx); + clay doc = clay_create(ctx, "document"); clay_set(doc, "width", 800); clay_set(doc, "height", 400); - clay_set(doc, "bg-color", &cbg); - clay_set(doc, "content", flex); + clay_set(doc, "background-color", "#cef9f2ff"); + clay_append_child(doc, flex); - clay_debug_layout(doc); + clay_print_layout_tree(doc); clay_destroy_context(ctx); } \ No newline at end of file diff --git a/src/document.c b/src/document.c new file mode 100644 index 0000000..913c08b --- /dev/null +++ b/src/document.c @@ -0,0 +1,77 @@ +#include "clay.h" +#include "layout.h" +#include "context.h" + + +// TODO: what is the document layout class even needed for? couldn't we just have any layout as the root? +static struct clay_layout_class_s layout_class_document = {}; + + +static clay_prop_definition_in prop_numeric_single = { + .allow_int = true, + .allow_float = true, + .allow_units = CLAY_PROP_UNIT_ANY_UNIT, +}; + +static clay_prop_definition_in prop_numeric_two = { + .allow_int = true, + .allow_float = true, + .allow_units = CLAY_PROP_UNIT_ANY_UNIT, + .max_values = 2, +}; + +static clay_prop_definition_in prop_numeric_four = { + .allow_int = true, + .allow_float = true, + .allow_units = CLAY_PROP_UNIT_ANY_UNIT, + .max_values = 4, +}; + +static clay_prop_definition_in prop_numeric_eight = { + .allow_int = true, + .allow_float = true, + .allow_units = CLAY_PROP_UNIT_ANY_UNIT, + .max_values = 8, +}; + +static clay_prop_definition_in prop_color_single = { + .allow_color = true, +}; +static clay_prop_definition_in prop_color_four = { + .allow_color = true, + .max_values = 4, +}; + + +void clay_document_register(clay_ctx ctx) { + clay_ctx_register_layout_class(ctx, "document", &layout_class_document); + + clay_ctx_register_global_property(ctx, "width", &prop_numeric_single); + clay_ctx_register_global_property(ctx, "height", &prop_numeric_single); + clay_ctx_register_global_property(ctx, "padding", &prop_numeric_four); + clay_ctx_register_global_property(ctx, "padding-left", &prop_numeric_single); + clay_ctx_register_global_property(ctx, "padding-right", &prop_numeric_single); + clay_ctx_register_global_property(ctx, "padding-top", &prop_numeric_single); + clay_ctx_register_global_property(ctx, "padding-bottom", &prop_numeric_single); + clay_ctx_register_global_property(ctx, "margin", &prop_numeric_four); + clay_ctx_register_global_property(ctx, "margin-left", &prop_numeric_single); + clay_ctx_register_global_property(ctx, "margin-right", &prop_numeric_single); + clay_ctx_register_global_property(ctx, "margin-top", &prop_numeric_single); + clay_ctx_register_global_property(ctx, "margin-bottom", &prop_numeric_single); + clay_ctx_register_global_property(ctx, "background-color", &prop_color_single); + clay_ctx_register_global_property(ctx, "border-width", &prop_numeric_four); + clay_ctx_register_global_property(ctx, "border-width-left", &prop_numeric_single); + clay_ctx_register_global_property(ctx, "border-width-right", &prop_numeric_single); + clay_ctx_register_global_property(ctx, "border-width-top", &prop_numeric_single); + clay_ctx_register_global_property(ctx, "border-width-bottom", &prop_numeric_single); + clay_ctx_register_global_property(ctx, "border-color", &prop_color_four); + clay_ctx_register_global_property(ctx, "border-color-left", &prop_color_single); + clay_ctx_register_global_property(ctx, "border-color-right", &prop_color_single); + clay_ctx_register_global_property(ctx, "border-color-top", &prop_color_single); + clay_ctx_register_global_property(ctx, "border-color-bottom", &prop_color_single); + clay_ctx_register_global_property(ctx, "border-radius", &prop_numeric_eight); + clay_ctx_register_global_property(ctx, "border-radius-top-left", &prop_numeric_two); + clay_ctx_register_global_property(ctx, "border-radius-top-right", &prop_numeric_two); + clay_ctx_register_global_property(ctx, "border-radius-bottom-right", &prop_numeric_two); + clay_ctx_register_global_property(ctx, "border-radius-bottom-left", &prop_numeric_two); +} \ No newline at end of file diff --git a/src/flex.c b/src/flex.c new file mode 100644 index 0000000..0cc7340 --- /dev/null +++ b/src/flex.c @@ -0,0 +1,144 @@ +#include "clay.h" +#include "layout.h" +#include "context.h" + +static struct clay_layout_class_s layout_class_flex = {}; + + +enum flex_direction { + FLEX_DIRECTION_ROW, + FLEX_DIRECTION_ROW_REVERSE, + FLEX_DIRECTION_COLUMN, + FLEX_DIRECTION_COLUMN_REVERSE, +}; + +static clay_prop_definition_in prop_direction = { + .keywords = CLAY_PROP_DEF_KEYWORDS( + CLAY_PROP_DEF_KEYWORD("row", FLEX_DIRECTION_ROW), + CLAY_PROP_DEF_KEYWORD("row-reverse", FLEX_DIRECTION_ROW_REVERSE), + CLAY_PROP_DEF_KEYWORD("column", FLEX_DIRECTION_COLUMN), + CLAY_PROP_DEF_KEYWORD("column-reverse", FLEX_DIRECTION_COLUMN_REVERSE), + ), +}; + +enum flex_wrap { + FLEX_WRAP_NOWRAP, + FLEX_WRAP_WRAP, + FLEX_WRAP_WRAP_REVERSE, +}; + +static clay_prop_definition_in prop_wrap = { + .keywords = CLAY_PROP_DEF_KEYWORDS( + CLAY_PROP_DEF_KEYWORD("nowrap", FLEX_WRAP_NOWRAP), + CLAY_PROP_DEF_KEYWORD("wrap", FLEX_WRAP_WRAP), + CLAY_PROP_DEF_KEYWORD("wrap-reverse", FLEX_WRAP_WRAP_REVERSE), + ), +}; + +enum justify_content { + JUSTIFY_CONTENT_START, + JUSTIFY_CONTENT_END, + JUSTIFY_CONTENT_CENTER, + JUSTIFY_CONTENT_SPACE_BETWEEN, + JUSTIFY_CONTENT_SPACE_AROUND, + JUSTIFY_CONTENT_SPACE_EVENLY, +}; + +static clay_prop_definition_in prop_justify_content = { + .keywords = CLAY_PROP_DEF_KEYWORDS( + CLAY_PROP_DEF_KEYWORD("start", JUSTIFY_CONTENT_START), + CLAY_PROP_DEF_KEYWORD("end", JUSTIFY_CONTENT_END), + CLAY_PROP_DEF_KEYWORD("center", JUSTIFY_CONTENT_CENTER), + CLAY_PROP_DEF_KEYWORD("space-between", JUSTIFY_CONTENT_SPACE_BETWEEN), + CLAY_PROP_DEF_KEYWORD("space-around", JUSTIFY_CONTENT_SPACE_AROUND), + CLAY_PROP_DEF_KEYWORD("space-evenly", JUSTIFY_CONTENT_SPACE_EVENLY), + ), +}; + +enum align_items { + ALIGN_ITEMS_START, + ALIGN_ITEMS_END, + ALIGN_ITEMS_CENTER, + ALIGN_ITEMS_STRETCH, +}; + +static clay_prop_definition_in prop_align_items = { + .keywords = CLAY_PROP_DEF_KEYWORDS( + CLAY_PROP_DEF_KEYWORD("start", ALIGN_ITEMS_START), + CLAY_PROP_DEF_KEYWORD("end", ALIGN_ITEMS_END), + CLAY_PROP_DEF_KEYWORD("center", ALIGN_ITEMS_CENTER), + CLAY_PROP_DEF_KEYWORD("stretch", ALIGN_ITEMS_STRETCH), + ), +}; + +enum align_content { + ALIGN_CONTENT_START, + ALIGN_CONTENT_END, + ALIGN_CONTENT_CENTER, + ALIGN_CONTENT_STRETCH, + ALIGN_CONTENT_SPACE_BETWEEN, + ALIGN_CONTENT_SPACE_AROUND, + ALIGN_CONTENT_SPACE_EVENLY, +}; + +static clay_prop_definition_in prop_align_content = { + .keywords = CLAY_PROP_DEF_KEYWORDS( + CLAY_PROP_DEF_KEYWORD("start", ALIGN_CONTENT_START), + CLAY_PROP_DEF_KEYWORD("end", ALIGN_CONTENT_END), + CLAY_PROP_DEF_KEYWORD("center", ALIGN_CONTENT_CENTER), + CLAY_PROP_DEF_KEYWORD("stretch", ALIGN_CONTENT_STRETCH), + CLAY_PROP_DEF_KEYWORD("space-between", ALIGN_CONTENT_SPACE_BETWEEN), + CLAY_PROP_DEF_KEYWORD("space-around", ALIGN_CONTENT_SPACE_AROUND), + CLAY_PROP_DEF_KEYWORD("space-evenly", ALIGN_CONTENT_SPACE_EVENLY), + ), +}; + +static clay_prop_definition_in prop_gap = { + .allow_float = true, + .allow_int = true, + .allow_units = CLAY_PROP_UNIT_ABSOLUTE | CLAY_PROP_UNIT_PX, + .max_values = 2, +}; + +static clay_prop_definition_in prop_gap_single = { + .allow_float = true, + .allow_int = true, + .allow_units = CLAY_PROP_UNIT_ABSOLUTE | CLAY_PROP_UNIT_PX, +}; + +static clay_prop_definition_in prop_flex_order = { + .allow_int = true, + .allow_units = CLAY_PROP_UNIT_NONE, +}; + +static clay_prop_definition_in prop_flex_grow_shrink = { + .allow_int = true, + .allow_float = true, + .allow_units = CLAY_PROP_UNIT_NONE, +}; + +static clay_prop_definition_in prop_flex_basis = { + .allow_int = true, + .allow_float = true, + .allow_units = CLAY_PROP_UNIT_ANY_UNIT, +}; + + +void clay_flex_register(clay_ctx ctx) { + clay_ctx_register_layout_class(ctx, "flex", &layout_class_flex); + + clay_ctx_register_class_property(ctx, "flex", "direction", &prop_direction); + clay_ctx_register_class_property(ctx, "flex", "wrap", &prop_wrap); + clay_ctx_register_class_property(ctx, "flex", "justify-content", &prop_justify_content); + clay_ctx_register_class_property(ctx, "flex", "align-items", &prop_align_items); + clay_ctx_register_class_property(ctx, "flex", "align-content", &prop_align_content); + clay_ctx_register_class_property(ctx, "flex", "gap", &prop_gap); + clay_ctx_register_class_property(ctx, "flex", "column-gap", &prop_gap_single); + clay_ctx_register_class_property(ctx, "flex", "row-gap", &prop_gap_single); + + clay_ctx_register_global_property(ctx, "order", &prop_flex_order); + clay_ctx_register_global_property(ctx, "flex-grow", &prop_flex_grow_shrink); + clay_ctx_register_global_property(ctx, "flex-shrink", &prop_flex_grow_shrink); + clay_ctx_register_global_property(ctx, "flex-basis", &prop_flex_basis); + clay_ctx_register_global_property(ctx, "align-self", &prop_align_items); +} \ No newline at end of file diff --git a/src/layout.c b/src/layout.c new file mode 100644 index 0000000..fc1513e --- /dev/null +++ b/src/layout.c @@ -0,0 +1,84 @@ +#include +#include +#include +#include +#include "layout.h" +#include "context.h" +#include "clay.h" + + +clay clay_create(clay_ctx ctx, const char *layout_class_name) { + clay_layout_class class = clay_ctx_get_layout_class(ctx, layout_class_name); + return clay_layout_create(ctx, class); +} + +clay clay_layout_create(clay_ctx ctx, clay_layout_class class) { + clay layout = malloc(sizeof(*layout)); + assert(layout != NULL); + layout->properties = clay_map_create(clay_property_map, 5); + layout->children = clay_list_create(clay_layout_list); + layout->class = class; + layout->ctx = ctx; + if (layout->class->init != NULL) { + layout->class->init(layout); + } + clay_ctx_register_layout(ctx, layout); + return layout; +} + +void clay_layout_destroy(clay layout) { + clay_ctx_unregister_layout(layout->ctx, layout); + clay_layout_cleanup(layout); + free(layout); +} + +void clay_layout_cleanup(clay layout) { + if (layout->class->cleanup != NULL) { + layout->class->cleanup(layout); + } + clay_map_destroy(layout->properties); + clay_list_destroy(layout->children); +} + +clay clay_clone(clay layout) { + clay cloned = malloc(sizeof(*cloned)); + cloned->class = layout->class; + cloned->ctx = layout->ctx; + cloned->properties = clay_map_clone(layout->properties); + cloned->children = clay_list_create(clay_layout_list); + clay_ctx_register_layout(layout->ctx, cloned); + return cloned; +} + +clay clay_clone_recursive(clay layout) { + // todo: should this function be made non-recursive (i.e. replace with loop + manual stack)? + clay cloned = clay_clone(layout); + CLAY_LIST_FOREACH(layout->children, child, _) { + clay cloned_child = clay_clone_recursive(*child); + clay_list_append(cloned->children, cloned_child); + } + return cloned; +} + + + +void clay_append_child(clay parent, clay child) { + if (child->parent != NULL) { + CLAY_LIST_FOREACH(child->parent->children, el_ptr, idx) { + if (*el_ptr == child) { + clay_list_remove(child->parent->children, idx); + break; + } + } + } + clay_list_append(parent->children, child); + child->parent = parent; +} + +clay_layout_list_const clay_layout_get_children(clay layout) { + return layout->children; +} + +clay clay_layout_get_parent(clay layout) { + return layout->parent; +} diff --git a/src/layout.h b/src/layout.h new file mode 100644 index 0000000..d854729 --- /dev/null +++ b/src/layout.h @@ -0,0 +1,34 @@ +#ifndef CLAY_LAYOUT_H +#define CLAY_LAYOUT_H + +#include + +#include "clay.h" +#include "clay-map.h" +#include "clay-list.h" + +typedef struct clay_layout_class_s *clay_layout_class; +CLAY_LIST_TYPE(clay_layout_list, clay) + +#include "color.h" +#include "context.h" + +typedef void (*clay_layout_init_fn)(clay); + +typedef void (*clay_layout_cleanup_fn)(clay); + +struct clay_layout_class_s { + clay_layout_init_fn init; + clay_layout_cleanup_fn cleanup; +}; + + +clay clay_layout_create(clay_ctx ctx, clay_layout_class class); + +void clay_layout_cleanup(clay layout); + +clay_layout_list_const clay_layout_get_children(clay layout); + +clay clay_layout_get_parent(clay layout); + +#endif //CLAY_LAYOUT_H diff --git a/src/property.c b/src/property.c new file mode 100644 index 0000000..22a3d79 --- /dev/null +++ b/src/property.c @@ -0,0 +1,281 @@ +#include "property.h" + +#include +#include +#include + +#include "clay.h" +#include "context.h" +#include "clay-map.h" + + +static struct clay_prop_val_s parse_prop_val_int(int val, const clay_prop_definition *def); +static struct clay_prop_val_s parse_prop_val_double(double val, const clay_prop_definition *def); +static struct clay_prop_val_s parse_prop_val_string_numeric(const char *val, const clay_prop_definition *def); +static struct clay_prop_val_s parse_prop_val_string_notnumeric(const char *val, const clay_prop_definition *def); +static struct clay_prop_val_s parse_prop_val_string(const char *val, const clay_prop_definition *def); +static struct clay_prop_val_s parse_prop_val_color(clay_color val, const clay_prop_definition *def); +static struct clay_prop_val_s parse_prop_val(struct clay_prop_val_in in, const clay_prop_definition *def); + +clay_prop clay_layout_get_property(clay layout, const char *prop_name) { + const clay_prop_definition *prop_def = clay_ctx_get_property_definition(layout->ctx, layout->class, prop_name); + clay_prop prop; + if (!prop_def->inherits) { + if (!clay_map_get(layout->properties, prop_name, &(prop.values))) { + prop.values = NULL; + } + return prop; + } + bool found = false; + clay cur = layout; + prop.inheritance_level = 0; + while (cur != NULL) { + if (prop_def->global || layout->class == cur->class) { + if (clay_map_get(cur->properties, prop_name, &prop.values)) { + found = true; + break; + } + } + cur = cur->parent; + prop.inheritance_level += 1; + } + if (!found) { + prop.values = NULL; + prop.inheritance_level = 0; + } + return prop; +} + +clay_prop_map clay_layout_get_properties(clay layout) { + clay_prop_map props = clay_map_create(clay_prop_map); + clay cur = layout; + int inheritance_level = 0; + while (cur != NULL) { + CLAY_MAP_FOREACH(cur->properties, propname, propvalueptr) { + const clay_prop_definition *propdef = clay_ctx_get_property_definition(cur->ctx, cur->class, propname); + if (inheritance_level == 0 || (propdef->inherits && (propdef->global || cur->class == layout->class))) { + if (!clay_map_has_key(props, propname)) { + clay_prop prop = {.values = *propvalueptr, .inheritance_level = inheritance_level}; + clay_map_set(props, propname, prop); + } + } + } + cur = cur->parent; + inheritance_level += 1; + } + return props; +} + +void clay_layout_set_property(clay layout, const char *prop_name, int num_values, ...) { + clay_ctx ctx = layout->ctx; + const clay_prop_definition *def = clay_ctx_get_property_definition(ctx, layout->class, prop_name); + assert(def != NULL); + assert(num_values <= def->max_values); + clay_prop_vals vals = clay_list_create(clay_prop_vals, num_values); + if (num_values > 0) { + va_list args; + va_start(args, num_values); + for (int i = 0; i < num_values; ++i) { + struct clay_prop_val_in inval = va_arg(args, struct clay_prop_val_in); + struct clay_prop_val_s val = parse_prop_val(inval, def); + clay_list_append(vals, val); + } + va_end(args); + } + clay_map_set(layout->properties, prop_name, vals); +} + +static struct clay_prop_val_s parse_prop_val_int(int val, const clay_prop_definition *def) { + assert(def->allow_int); + clay_prop_val_unit unit; + if (!(def->allow_units & CLAY_PROP_UNIT_NONE) && def->allow_units & CLAY_PROP_UNIT_PX) { + unit = CLAY_PROP_UNIT_PX; + } else { + unit = CLAY_PROP_UNIT_NONE; + } + assert(def->allow_units & unit); + return (struct clay_prop_val_s) { + .type = CLAY_PROP_TYPE_NUMBER, + .number = (struct clay_prop_val_number) { + .type = CLAY_PROP_NUMBER_INT, + .unit = unit, + .value_int = val + } + }; +} + +static struct clay_prop_val_s parse_prop_val_double(double val, const clay_prop_definition *def) { + assert(def->allow_float); + assert(def->allow_units & CLAY_PROP_UNIT_NONE); + return (struct clay_prop_val_s) { + .type = CLAY_PROP_TYPE_NUMBER, + .number = (struct clay_prop_val_number) { + .type = CLAY_PROP_NUMBER_FLOAT, + .unit = CLAY_PROP_UNIT_NONE, + .value_double = val + } + }; +} + +#include + +static clay_prop_val parse_prop_val_string_numeric(const char *val, const clay_prop_definition *def) { + // TODO: it is wasteful to call newlocale for every parse, cache globally + // or find a locale-independent correct implementation of the strto* functions + clay_prop_val result = { + .type = CLAY_PROP_TYPE_NUMBER, + .number = {} + }; + locale_t locale = newlocale(LC_ALL, "C", NULL); + locale_t original = uselocale(locale); + + char * endptr; + + int64_t int_val = strtoll(val, &endptr, 10); + ptrdiff_t int_len = endptr - val; + double float_val = strtod(val, &endptr); + ptrdiff_t float_len = endptr - val; + const char * nextptr; + if (float_len > int_len || (float_len == int_len && !def->allow_int)) { + assert(def->allow_float); + result.number.type = CLAY_PROP_NUMBER_FLOAT; + result.number.value_double = float_val; + nextptr = val + float_len; + } else { + assert(def->allow_int); + result.number.type = CLAY_PROP_NUMBER_INT; + result.number.value_int = int_val; + nextptr = val + int_len; + } + + // consume spaces after number + while(isspace(*nextptr)) ++nextptr; + + if (strncmp(nextptr, "px", 2) == 0) { + result.number.unit = CLAY_PROP_UNIT_PX; + assert(def->allow_units & CLAY_PROP_UNIT_PX); + nextptr += 2; + } else if (strncmp(nextptr, "pt", 2) == 0) { + result.number.unit = CLAY_PROP_UNIT_PT; + assert(def->allow_units & CLAY_PROP_UNIT_PT); + nextptr += 2; + } else if(strncmp(nextptr, "cm", 2) == 0) { + result.number.unit = CLAY_PROP_UNIT_CM; + assert(def->allow_units & CLAY_PROP_UNIT_CM); + nextptr += 2; + } else if(strncmp(nextptr, "mm", 2) == 0) { + result.number.unit = CLAY_PROP_UNIT_MM; + assert(def->allow_units & CLAY_PROP_UNIT_MM); + nextptr += 2; + } else if(strncmp(nextptr, "%", 1) == 0) { + result.number.unit = CLAY_PROP_UNIT_PERCENT; + assert(def->allow_units & CLAY_PROP_UNIT_PERCENT); + nextptr += 1; + } else { + if (def->allow_units & CLAY_PROP_UNIT_NONE) { + result.number.unit = CLAY_PROP_UNIT_NONE; + } else if (def->allow_units & CLAY_PROP_UNIT_PX) { + result.number.unit = CLAY_PROP_UNIT_PX; + } else { + assert(false); + } + } + + // consume spaces after unit + while(isspace(*nextptr)) ++nextptr; + + // ensure nothing except whitespace appeared after the number(+unit) + assert(*nextptr == '\0'); + + uselocale(original); + return result; +} + +static struct clay_prop_val_s parse_prop_val_string_notnumeric(const char *val, const clay_prop_definition *def) { + if (val[0] == '#') { + size_t len = strlen(val) - 1; + if (len == 3 || len == 4 || len == 6 || len == 8) { + bool is_color = true; + uint8_t parts[8] = {0}; + for (size_t i = 0; i < len; ++i) { + uint8_t ch = val[1 + i]; + if (ch >= '0' && ch <= '9') { + parts[i] = ch - '0'; + } else if (ch >= 'a' && ch <= 'f') { + parts[i] = ch - 'a' + 0xA; + } else if (ch >= 'A' && ch <= 'F') { + parts[i] = ch - 'A' + 0xA; + } else { + is_color = false; + break; + } + } + if (is_color) { + clay_color color = {}; + switch (len) { + case 3: + color.red = parts[0] << 4 | parts[0]; + color.green = parts[1] << 4 | parts[1]; + color.blue = parts[2] << 4 | parts[2]; + color.alpha = 255; + break; + case 4: + color.red = parts[0] << 4 | parts[0]; + color.green = parts[1] << 4 | parts[1]; + color.blue = parts[2] << 4 | parts[2]; + color.alpha = parts[3] << 4 | parts[3]; + break; + case 6: + color.red = parts[0] << 4 | parts[1]; + color.green = parts[2] << 4 | parts[3]; + color.blue = parts[4] << 4 | parts[5]; + color.alpha = 255; + break; + case 8: + color.red = parts[0] << 4 | parts[1]; + color.green = parts[2] << 4 | parts[3]; + color.blue = parts[4] << 4 | parts[5]; + color.alpha = parts[6] << 4 | parts[7]; + break; + default: + assert(false); + } + return (struct clay_prop_val_s) {.type=CLAY_PROP_TYPE_COLOR, .color=color}; + } + } + } + int keyword_val; + if (clay_map_get(def->keywords, val, &keyword_val)) { + return (struct clay_prop_val_s) {.type=CLAY_PROP_TYPE_KEYWORD, .keyword=keyword_val}; + } + assert(def->allow_string); + return (struct clay_prop_val_s) {.type=CLAY_PROP_TYPE_STRING, .string=val}; +} + +static struct clay_prop_val_s parse_prop_val_string(const char *val, const clay_prop_definition *def) { + assert(val != NULL && val[0] != '\0'); // no null refs and no empty strings + if (isdigit(val[0]) || (val[0] == '-' && isdigit(val[0]))) { + return parse_prop_val_string_numeric(val, def); + } else { + return parse_prop_val_string_notnumeric(val, def); + } +} + +static struct clay_prop_val_s parse_prop_val_color(clay_color val, const clay_prop_definition *def) { + assert(def->allow_color); + return (struct clay_prop_val_s) {.type = CLAY_PROP_TYPE_COLOR, .color = val}; +} + +static struct clay_prop_val_s parse_prop_val(struct clay_prop_val_in in, const clay_prop_definition *def) { + switch (in.type) { + case TYPE_INT: + return parse_prop_val_int(in.value_int, def); + case TYPE_DOUBLE: + return parse_prop_val_double(in.value_double, def); + case TYPE_STRING: + return parse_prop_val_string(in.value_string, def); + case TYPE_COLOR: + return parse_prop_val_color(in.value_color, def); + } + assert(false); +} \ No newline at end of file diff --git a/src/property.h b/src/property.h new file mode 100644 index 0000000..73049d7 --- /dev/null +++ b/src/property.h @@ -0,0 +1,161 @@ +#ifndef CLAY_PROPERTY_H +#define CLAY_PROPERTY_H + +#include "clay.h" +#include "clay-list.h" +#include "clay-map.h" + +CLAY_STRING_MAP_TYPE(clay_prop_keyword_map, int) + +typedef struct clay_prop_val_s clay_prop_val; + +CLAY_LIST_TYPE(clay_prop_vals, clay_prop_val) + +typedef struct clay_prop_s clay_prop; + +typedef struct clay_prop_definition_s clay_prop_definition; + +typedef struct clay_prop_definition_in_s clay_prop_definition_in; + + +#include "layout.h" + + +struct clay_prop_s { + clay_prop_vals_const values; + int inheritance_level; +} ; + +CLAY_STRING_MAP_TYPE(clay_property_map, clay_prop_vals) +CLAY_STRING_MAP_TYPE(clay_prop_map, clay_prop) + +struct clay_t { + clay_ctx ctx; + clay_layout_class class; + clay_property_map properties; + clay parent; + clay_layout_list children; +}; + + +enum clay_prop_val_type { + CLAY_PROP_TYPE_KEYWORD, + CLAY_PROP_TYPE_NUMBER, + CLAY_PROP_TYPE_COLOR, + CLAY_PROP_TYPE_STRING +}; + +enum clay_prop_val_number_type { + CLAY_PROP_NUMBER_INT, + CLAY_PROP_NUMBER_FLOAT, +}; + + +typedef enum { + CLAY_PROP_UNIT_NONE = 1, + CLAY_PROP_UNIT_PX = 2, + CLAY_PROP_UNIT_PT = 4, + CLAY_PROP_UNIT_CM = 8, + CLAY_PROP_UNIT_MM = 16, + CLAY_PROP_UNIT_PERCENT = 32, +} clay_prop_val_unit; + +#define CLAY_PROP_UNIT_ABSOLUTE (CLAY_PROP_UNIT_PT | CLAY_PROP_UNIT_CM | CLAY_PROP_UNIT_MM) +#define CLAY_PROP_UNIT_ANY_UNIT (CLAY_PROP_UNIT_PX | CLAY_PROP_UNIT_PT | CLAY_PROP_UNIT_CM | CLAY_PROP_UNIT_MM | CLAY_PROP_UNIT_PERCENT) +#define CLAY_PROP_UNIT_ANY (CLAY_PROP_UNIT_NONE | CLAY_PROP_UNIT_ANY_UNIT) + +struct clay_prop_val_number { + enum clay_prop_val_number_type type; + clay_prop_val_unit unit; + union { + int64_t value_int; + double value_double; + }; +}; + +struct clay_prop_val_s { + enum clay_prop_val_type type; + union { + int keyword; + struct clay_prop_val_number number; + clay_color color; + const char *string; + }; +}; + +struct clay_prop_definition_s { + bool allow_int; + bool allow_float; + bool allow_string; + clay_prop_val_unit allow_units; + bool allow_color; + clay_prop_keyword_map keywords; + int max_values; + bool inherits; + bool global; +}; + + +struct clay_prop_keyword_map_entry_in { + const char *keyword; + int value; +}; + +struct clay_prop_keyword_map_in { + struct clay_prop_keyword_map_entry_in *entries; + size_t num_entries; +}; + +struct clay_prop_definition_in_s { + bool allow_int; + bool allow_string; + bool allow_float; + clay_prop_val_unit allow_units; + bool allow_color; + struct clay_prop_keyword_map_in keywords; + int max_values; + bool inherits; +} ; + + +#define CLAY_PP_NARG(...) \ + CLAY_PP_NARG_(__VA_ARGS__,CLAY_PP_RSEQ_N()) +#define CLAY_PP_NARG_(...) \ + CLAY_PP_ARG_N(__VA_ARGS__) +#define CLAY_PP_ARG_N(\ + _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, \ + _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, \ + _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, \ + _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, \ + _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, \ + _61, _62, _63, N, ...) N +#define CLAY_PP_RSEQ_N() \ + 63,62,61,60, \ + 59,58,57,56,55,54,53,52,51,50, \ + 49,48,47,46,45,44,43,42,41,40, \ + 39,38,37,36,35,34,33,32,31,30, \ + 29,28,27,26,25,24,23,22,21,20, \ + 19,18,17,16,15,14,13,12,11,10, \ + 9,8,7,6,5,4,3,2,1,0 + +#define CLAY_PROP_DEF_KEYWORD(k, v) {.keyword = k, .value = v} +#define CLAY_PROP_DEF_KEYWORDS(...) (struct clay_prop_keyword_map_in) { \ + .entries = (struct clay_prop_keyword_map_entry_in[]) { \ + __VA_ARGS__ \ + }, \ + .num_entries = CLAY_PP_NARG(__VA_ARGS__)/2 \ +} + + +clay_prop clay_layout_get_property(clay layout, const char *prop_name); + + +/* + * this function allocates a clay_property_map, make sure to free it with clay_map_destroy + * the values in the returned map are pointers to the actual property values and should not be modified + */ +clay_prop_map clay_layout_get_properties(clay layout); + + +#endif //CLAY_PROPERTY_H diff --git a/src/render.c b/src/render.c new file mode 100644 index 0000000..10db5c7 --- /dev/null +++ b/src/render.c @@ -0,0 +1,8 @@ +#include "clay.h" + + +void clay_render_to_png(clay doc, const char *path) { + // todo +} + + diff --git a/src/text.c b/src/text.c new file mode 100644 index 0000000..5a4c27a --- /dev/null +++ b/src/text.c @@ -0,0 +1,130 @@ +#include "clay.h" +#include "layout.h" + +static struct clay_layout_class_s layout_class_text = {}; + + +static clay_prop_definition_in prop_align = { + .keywords = CLAY_PROP_DEF_KEYWORDS( + CLAY_PROP_DEF_KEYWORD("left", 0), + CLAY_PROP_DEF_KEYWORD("center", 1), + CLAY_PROP_DEF_KEYWORD("right", 2), + CLAY_PROP_DEF_KEYWORD("justify", 3), + ), +}; + +static clay_prop_definition_in prop_vertical_align = { + .keywords = CLAY_PROP_DEF_KEYWORDS( + CLAY_PROP_DEF_KEYWORD("top", 0), + CLAY_PROP_DEF_KEYWORD("middle", 1), + CLAY_PROP_DEF_KEYWORD("bottom", 2), + ), +}; + +static clay_prop_definition_in prop_font_size = { + .allow_int = true, + .allow_float = true, + .allow_units = CLAY_PROP_UNIT_PT | CLAY_PROP_UNIT_NONE, + .inherits = true, +}; + +static clay_prop_definition_in prop_line_height = { + .allow_int = true, + .allow_float = true, + .allow_units = CLAY_PROP_UNIT_PT | CLAY_PROP_UNIT_NONE, + .keywords = CLAY_PROP_DEF_KEYWORDS(CLAY_PROP_DEF_KEYWORD("auto", 1)), + .inherits = true, +}; + + +static clay_prop_definition_in prop_font_weight = { + .allow_int = true, + .allow_units = CLAY_PROP_UNIT_NONE, + .keywords = CLAY_PROP_DEF_KEYWORDS( + CLAY_PROP_DEF_KEYWORD("thin", 100), + CLAY_PROP_DEF_KEYWORD("extra-light", 200), + CLAY_PROP_DEF_KEYWORD("ultra-light", 200), + CLAY_PROP_DEF_KEYWORD("light", 300), + CLAY_PROP_DEF_KEYWORD("normal", 400), + CLAY_PROP_DEF_KEYWORD("regular", 400), + CLAY_PROP_DEF_KEYWORD("medium", 500), + CLAY_PROP_DEF_KEYWORD("semi-bold", 600), + CLAY_PROP_DEF_KEYWORD("demi-bold", 600), + CLAY_PROP_DEF_KEYWORD("bold", 700), + CLAY_PROP_DEF_KEYWORD("extra-bold", 800), + CLAY_PROP_DEF_KEYWORD("black", 900), + ), + .inherits = true, +}; + + +enum text_font_style { + FONT_STYLE_NORMAL, + FONT_STYLE_ITALIC, +}; + +static clay_prop_definition_in prop_font_style = { + .allow_int = true, + .allow_units = CLAY_PROP_UNIT_NONE, + .keywords = CLAY_PROP_DEF_KEYWORDS( + CLAY_PROP_DEF_KEYWORD("normal", FONT_STYLE_NORMAL), + CLAY_PROP_DEF_KEYWORD("italic", FONT_STYLE_ITALIC), + ), + .inherits = true, +}; + +static clay_prop_definition_in prop_language = { + .keywords = CLAY_PROP_DEF_KEYWORDS( + CLAY_PROP_DEF_KEYWORD("revert", 1), + ), + .allow_string = true, + .inherits = true, +}; + +enum text_font_family { + FONT_FAMILY_SERIF, + FONT_FAMILY_SANS_SERIF, + FONT_FAMILY_MONOSPACE, + FONT_FAMILY_EMOJI, + FONT_FAMILY_REVERT, +}; + +static clay_prop_definition_in prop_font_family = { + .keywords = CLAY_PROP_DEF_KEYWORDS( + CLAY_PROP_DEF_KEYWORD("serif", FONT_FAMILY_SERIF), + CLAY_PROP_DEF_KEYWORD("sans-serif", FONT_FAMILY_SANS_SERIF), + CLAY_PROP_DEF_KEYWORD("monospace", FONT_FAMILY_MONOSPACE), + CLAY_PROP_DEF_KEYWORD("emoji", FONT_FAMILY_EMOJI), + CLAY_PROP_DEF_KEYWORD("revert", FONT_FAMILY_REVERT), + ), + .allow_string = true, + .inherits = true, +}; + + +static clay_prop_definition_in prop_text_color = { + .allow_color = true, + .inherits = true, +}; + + +static clay_prop_definition_in prop_text_text = { + .allow_string = true, +}; + + +void clay_text_register(clay_ctx ctx) { + clay_ctx_register_layout_class(ctx, "text", &layout_class_text); + + clay_ctx_register_class_property(ctx, "text", "align", &prop_align); + clay_ctx_register_class_property(ctx, "text", "vertical-align", &prop_vertical_align); + clay_ctx_register_class_property(ctx, "text", "text", &prop_text_text); + + clay_ctx_register_global_property(ctx, "font-size", &prop_font_size); + clay_ctx_register_global_property(ctx, "font-weight", &prop_font_weight); + clay_ctx_register_global_property(ctx, "font-family", &prop_font_family); + clay_ctx_register_global_property(ctx, "font-style", &prop_font_style); + clay_ctx_register_global_property(ctx, "line-height", &prop_line_height); + clay_ctx_register_global_property(ctx, "language", &prop_language); + clay_ctx_register_global_property(ctx, "text-color", &prop_text_color); +} \ No newline at end of file