Reworked property system completely.
Properties can now have values of different types, and they are registered with their name, either to a layout class or globally. Layout classes are also registered with their name.
This commit is contained in:
parent
29b5bda326
commit
d89ef83551
|
@ -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()
|
||||
endif ()
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
105
include/clay.h
105
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 <stdarg.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
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
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
#include "clay-base.h"
|
||||
#include <assert.h>
|
||||
|
||||
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
#ifndef CLAY_CLAY_COLOR_H
|
||||
#define CLAY_CLAY_COLOR_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
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
|
|
@ -1,125 +0,0 @@
|
|||
#include <clay.h>
|
||||
#include <stddef.h>
|
||||
#include <malloc.h>
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#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;
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
#ifndef CLAY_CLAY_CONTEXT_H
|
||||
#define CLAY_CLAY_CONTEXT_H
|
||||
|
||||
#include "clay-property.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
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
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
#include <malloc.h>
|
||||
#include <stdarg.h>
|
||||
#include <assert.h>
|
||||
#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);
|
||||
}
|
|
@ -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
|
92
src/clay-list.h
Normal file
92
src/clay-list.h
Normal file
|
@ -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 <stddef.h>
|
||||
#include <malloc.h>
|
||||
#include <assert.h>
|
||||
#include <memory.h>
|
||||
|
||||
|
||||
#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
|
167
src/clay-map.h
Normal file
167
src/clay-map.h
Normal file
|
@ -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 <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#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
|
|
@ -1,87 +0,0 @@
|
|||
#include <string.h>
|
||||
#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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
#ifndef CLAY_CLAY_PROPERTY_H
|
||||
#define CLAY_CLAY_PROPERTY_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <assert.h>
|
||||
#include <malloc.h>
|
||||
|
||||
|
||||
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
|
|
@ -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);
|
||||
}
|
20
src/clay.c
20
src/clay.c
|
@ -1,20 +0,0 @@
|
|||
#include "clay.h"
|
||||
|
||||
#include <cairo/cairo.h>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
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);
|
||||
}
|
63
src/cmoball.h
Normal file
63
src/cmoball.h
Normal file
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Copyright 2021 Jorengarenar <dev@joren.ga>
|
||||
*
|
||||
* 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_ */
|
|
@ -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;
|
||||
}
|
9
src/color.h
Normal file
9
src/color.h
Normal file
|
@ -0,0 +1,9 @@
|
|||
#ifndef CLAY_COLOR_H
|
||||
#define CLAY_COLOR_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "clay.h"
|
||||
|
||||
clay_color clay_color_rgba(uint8_t r, uint8_t g, uint8_t b, uint8_t a);
|
||||
|
||||
#endif //CLAY_COLOR_H
|
157
src/context.c
Normal file
157
src/context.c
Normal file
|
@ -0,0 +1,157 @@
|
|||
#include <clay.h>
|
||||
#include <stddef.h>
|
||||
#include <malloc.h>
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#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;
|
||||
}
|
30
src/context.h
Normal file
30
src/context.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
#ifndef CLAY_CONTEXT_H
|
||||
#define CLAY_CONTEXT_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#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
|
105
src/debug.c
Normal file
105
src/debug.c
Normal file
|
@ -0,0 +1,105 @@
|
|||
#include <inttypes.h>
|
||||
#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, "<unknown value>");
|
||||
}
|
||||
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;
|
||||
}
|
46
src/demo.c
46
src/demo.c
|
@ -1,6 +1,7 @@
|
|||
#include <stddef.h>
|
||||
#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);
|
||||
}
|
77
src/document.c
Normal file
77
src/document.c
Normal file
|
@ -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);
|
||||
}
|
144
src/flex.c
Normal file
144
src/flex.c
Normal file
|
@ -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);
|
||||
}
|
84
src/layout.c
Normal file
84
src/layout.c
Normal file
|
@ -0,0 +1,84 @@
|
|||
#include <malloc.h>
|
||||
#include <stdarg.h>
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#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;
|
||||
}
|
34
src/layout.h
Normal file
34
src/layout.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
#ifndef CLAY_LAYOUT_H
|
||||
#define CLAY_LAYOUT_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#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
|
281
src/property.c
Normal file
281
src/property.c
Normal file
|
@ -0,0 +1,281 @@
|
|||
#include "property.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <locale.h>
|
||||
|
||||
#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 <stdlib.h>
|
||||
|
||||
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);
|
||||
}
|
161
src/property.h
Normal file
161
src/property.h
Normal file
|
@ -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
|
8
src/render.c
Normal file
8
src/render.c
Normal file
|
@ -0,0 +1,8 @@
|
|||
#include "clay.h"
|
||||
|
||||
|
||||
void clay_render_to_png(clay doc, const char *path) {
|
||||
// todo
|
||||
}
|
||||
|
||||
|
130
src/text.c
Normal file
130
src/text.c
Normal file
|
@ -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);
|
||||
}
|
Loading…
Reference in a new issue