From 29b5bda326e84036de71ca9209f87988e8f37354 Mon Sep 17 00:00:00 2001 From: Gwendolyn Date: Thu, 2 Feb 2023 22:15:44 +0100 Subject: [PATCH] first attempt for the layout class and property system --- .gitignore | 2 + CMakeLists.txt | 35 +++++++++++ include/clay-base.h | 18 ++++++ include/clay-flex.h | 40 ++++++++++++ include/clay-properties.h | 23 +++++++ include/clay-text.h | 20 ++++++ include/clay.h | 20 ++++++ src/clay-base.c | 7 +++ src/clay-color.c | 10 +++ src/clay-color.h | 15 +++++ src/clay-context.c | 125 ++++++++++++++++++++++++++++++++++++++ src/clay-context.h | 23 +++++++ src/clay-document.c | 70 +++++++++++++++++++++ src/clay-flex.c | 82 +++++++++++++++++++++++++ src/clay-layout.c | 78 ++++++++++++++++++++++++ src/clay-layout.h | 33 ++++++++++ src/clay-property.c | 87 ++++++++++++++++++++++++++ src/clay-property.h | 41 +++++++++++++ src/clay-text.c | 67 ++++++++++++++++++++ src/clay.c | 20 ++++++ src/demo.c | 65 ++++++++++++++++++++ 21 files changed, 881 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 include/clay-base.h create mode 100644 include/clay-flex.h create mode 100644 include/clay-properties.h create mode 100644 include/clay-text.h create mode 100644 include/clay.h create mode 100644 src/clay-base.c create mode 100644 src/clay-color.c create mode 100644 src/clay-color.h create mode 100644 src/clay-context.c create mode 100644 src/clay-context.h create mode 100644 src/clay-document.c create mode 100644 src/clay-flex.c create mode 100644 src/clay-layout.c create mode 100644 src/clay-layout.h create mode 100644 src/clay-property.c create mode 100644 src/clay-property.h create mode 100644 src/clay-text.c create mode 100644 src/clay.c create mode 100644 src/demo.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..125f2b2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/.idea +/cmake-* \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..90d9055 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,35 @@ +cmake_minimum_required(VERSION 3.24) +project(clay C) + +set(CMAKE_C_STANDARD 17) + + +# 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 +) + +find_library(CAIRO_LIBRARIES + NAMES cairo + HINTS ${PC_CAIRO_LIBDIR} + ${PC_CAIRO_LIBRARY_DIRS} +) + + +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) +target_include_directories(clay PUBLIC include) +target_include_directories(clay PRIVATE ${CAIRO_INCLUDE_DIRS}) +target_link_libraries(clay PRIVATE ${CAIRO_LIBRARIES}) +target_compile_options(clay PRIVATE -Wall -Werror) + +if(PROJECT_IS_TOP_LEVEL) + add_executable(clay-demo src/demo.c) + target_link_libraries(clay-demo PRIVATE clay) +endif() \ No newline at end of file diff --git a/include/clay-base.h b/include/clay-base.h new file mode 100644 index 0000000..9a0cd9a --- /dev/null +++ b/include/clay-base.h @@ -0,0 +1,18 @@ +#ifndef CLAYOUT_CLAY_BASE_H +#define CLAYOUT_CLAY_BASE_H + + +typedef struct clay_ctx_t *clay_ctx; +typedef struct clay_t *clay; + + +clay_ctx clay_create_context(void); +void clay_destroy_context(clay_ctx); + + +void clay_set(clay, const char*, ...); + +clay clay_clone(clay); + + +#endif //CLAYOUT_CLAY_BASE_H diff --git a/include/clay-flex.h b/include/clay-flex.h new file mode 100644 index 0000000..5b4db8f --- /dev/null +++ b/include/clay-flex.h @@ -0,0 +1,40 @@ +#ifndef CLAYOUT_CLAY_FLEX_H +#define CLAYOUT_CLAY_FLEX_H + + +enum clay_flex_direction_e { + CLAY_FLEX_DIRECTION_ROW = 0, + CLAY_FLEX_DIRECTION_ROW_REVERSE = 1, + CLAY_FLEX_DIRECTION_COLUMN = 2, + CLAY_FLEX_DIRECTION_COLUMN_REVERSE = 3, +}; + +enum clay_flex_wrap_e { + CLAY_FLEX_WRAP_NO_WRAP = 0, + CLAY_FLEX_WRAP_WRAP = 1, + CLAY_FLEX_WRAP_WRAP_REVERSE = 2, +}; + +enum clay_flex_align_items_e { + CLAY_FLEX_ALIGN_ITEMS_START = 0, + CLAY_FLEX_ALIGN_ITEMS_END = 1, + CLAY_FLEX_ALIGN_ITEMS_CENTER = 2, + CLAY_FLEX_ALIGN_ITEMS_STRETCH = 3, +}; + +enum clay_flex_align_content_e { + CLAY_FLEX_ALIGN_CONTENT_START = 0, + CLAY_FLEX_ALIGN_CONTENT_END = 1, + CLAY_FLEX_ALIGN_CONTENT_CENTER = 2, + CLAY_FLEX_ALIGN_CONTENT_STRETCH = 3, + CLAY_FLEX_ALIGN_CONTENT_SPACE_BETWEEN = 4, + CLAY_FLEX_ALIGN_CONTENT_SPACE_AROUND = 5, + CLAY_FLEX_ALIGN_CONTENT_SPACE_EVENLY = 6, +}; + + +clay clay_create_flex(clay_ctx); + +void clay_flex_register_props(clay_ctx ctx); // todo: should be private + +#endif //CLAYOUT_CLAY_FLEX_H diff --git a/include/clay-properties.h b/include/clay-properties.h new file mode 100644 index 0000000..6a22090 --- /dev/null +++ b/include/clay-properties.h @@ -0,0 +1,23 @@ +#ifndef CLAYOUT_CLAY_PROPERTIES_H +#define CLAYOUT_CLAY_PROPERTIES_H + + +#define CLAY_PROPERTY_TEXT 1 +#define CLAY_PROPERTY_BG_COLOR 2 +#define CLAY_PROPERTY_WIDTH 3 +#define CLAY_PROPERTY_FLEX_GROW 4 +#define CLAY_PROPERTY_FLEX_SHRINK 5 +#define CLAY_PROPERTY_TEXT_ALIGN 6 +#define CLAY_PROPERTY_TEXT_VERTICAL_ALIGN 7 +#define CLAY_PROPERTY_FLEX_DIRECTION 8 +#define CLAY_PROPERTY_FLEX_WRAP 9 +#define CLAY_PROPERTY_FLEX_ALIGN_ITEMS 10 +#define CLAY_PROPERTY_FLEX_ALIGN_CONTENT 11 +#define CLAY_PROPERTY_FLEX_GAP 12 +#define CLAY_PROPERTY_PADDING 13 +#define CLAY_PROPERTY_CONTENTS 14 +#define CLAY_PROPERTY_HEIGHT 15 +#define CLAY_PROPERTY_CONTENT 16 + + +#endif \ No newline at end of file diff --git a/include/clay-text.h b/include/clay-text.h new file mode 100644 index 0000000..ad6074b --- /dev/null +++ b/include/clay-text.h @@ -0,0 +1,20 @@ +#ifndef CLAYOUT_CLAY_TEXT_H +#define CLAYOUT_CLAY_TEXT_H + +enum clay_text_align_e { + CLAY_TEXT_ALIGN_LEFT = 0, + CLAY_TEXT_ALIGN_CENTER = 1, + CLAY_TEXT_ALIGN_RIGHT = 2, +}; + +enum clay_text_vertical_align_e { + CLAY_TEXT_VERTICAL_ALIGN_TOP = 0, + CLAY_TEXT_VERTICAL_ALIGN_MIDDLE = 1, + CLAY_TEXT_VERTICAL_ALIGN_BOTTOM = 2, +}; + +clay clay_create_text(clay_ctx); + +void clay_text_register_props(clay_ctx ctx); // todo: should be private + +#endif //CLAYOUT_CLAY_TEXT_H diff --git a/include/clay.h b/include/clay.h new file mode 100644 index 0000000..46610f2 --- /dev/null +++ b/include/clay.h @@ -0,0 +1,20 @@ +#ifndef CLAYOUT_CLAY_H +#define CLAYOUT_CLAY_H + + +#include "clay-base.h" +#include "clay-properties.h" +#include "clay-flex.h" +#include "clay-text.h" + + +clay clay_create_document(clay_ctx); + +void clay_render_to_png(clay, const char *); + +void clay_debug_layout(clay doc); + +void clay_document_register_props(clay_ctx ctx); // todo: should be private + + +#endif //CLAYOUT_CLAY_H diff --git a/src/clay-base.c b/src/clay-base.c new file mode 100644 index 0000000..eef9230 --- /dev/null +++ b/src/clay-base.c @@ -0,0 +1,7 @@ +#include +#include +#include "clay-base.h" +#include + + + diff --git a/src/clay-color.c b/src/clay-color.c new file mode 100644 index 0000000..445e366 --- /dev/null +++ b/src/clay-color.c @@ -0,0 +1,10 @@ +// +// Created by gwendolyn on 2/2/23. +// + +#include "clay-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}; + return color; +} diff --git a/src/clay-color.h b/src/clay-color.h new file mode 100644 index 0000000..693544f --- /dev/null +++ b/src/clay-color.h @@ -0,0 +1,15 @@ +#ifndef CLAY_CLAY_COLOR_H +#define CLAY_CLAY_COLOR_H + +#include + +typedef struct { + uint8_t r; + uint8_t g; + uint8_t b; + uint8_t a; +} clay_color; + +clay_color clay_color_rgba(uint8_t r, uint8_t g, uint8_t b, uint8_t a); + +#endif //CLAY_CLAY_COLOR_H diff --git a/src/clay-context.c b/src/clay-context.c new file mode 100644 index 0000000..bffd2bb --- /dev/null +++ b/src/clay-context.c @@ -0,0 +1,125 @@ +#include +#include +#include +#include +#include +#include + +#include "clay-layout.h" + +#include "clay-context.h" + +// todo: thread safety? maybe as an option CLAY_ENABLE_THREADSAFE + +struct clay_ctx_t { + clay *layouts_ptrs; + size_t layouts_length; + size_t layouts_size; + clay_property_desc_s *property_descs; + size_t property_descs_length; + size_t property_descs_size; + int property_tag_cnt; +}; + + +clay_ctx clay_create_context(void) { + clay_ctx ctx = malloc(sizeof *ctx); + assert(ctx != NULL); + ctx->layouts_size = 16; + ctx->layouts_length = 0; + ctx->layouts_ptrs = malloc(sizeof(*ctx->layouts_ptrs) * ctx->layouts_size); + assert(ctx->layouts_ptrs != NULL); + ctx->property_tag_cnt = 0; + ctx->property_descs_size = 16; + ctx->property_descs_length = 0; + ctx->property_descs = malloc(sizeof(*ctx->property_descs) * ctx->property_descs_size); + assert(ctx->property_descs != NULL); + + + clay_text_register_props(ctx); + clay_flex_register_props(ctx); + clay_document_register_props(ctx); + + return ctx; +} + +void clay_destroy_context(clay_ctx ctx) { + // TODO: free everything owned by ctx + for (size_t i = 0; i < ctx->layouts_length; ++i) { + clay layout = ctx->layouts_ptrs[i]; + clay_cleanup_layout(layout); + free(layout); + } + free(ctx->layouts_ptrs); + free(ctx->property_descs); + free(ctx); +} + + +void clay_ctx_register_layout(clay_ctx ctx, clay layout) { + assert(ctx->layouts_length <= ctx->layouts_size); + if (ctx->layouts_length == ctx->layouts_size) { + size_t new_size = ctx->layouts_size * 2; + ctx->layouts_ptrs = realloc(ctx->layouts_ptrs, sizeof(*ctx->layouts_ptrs) * new_size); + assert(ctx->layouts_ptrs != NULL); + ctx->layouts_size = new_size; + } + ctx->layouts_length += 1; + ctx->layouts_ptrs[ctx->layouts_length - 1] = layout; +} + +void clay_ctx_unregister_layout(clay_ctx ctx, clay layout) { + for (size_t i = 0; i < ctx->layouts_length; ++i) { + if (ctx->layouts_ptrs[i] == layout) { + ctx->layouts_ptrs[i] = ctx->layouts_ptrs[ctx->layouts_length - 1]; + ctx->layouts_length -= 1; + } + } +} + +int clay_ctx_register_property(clay_ctx ctx, const char *name, clay_property_type type) { + assert (!clay_ctx_has_property(ctx, name)); + int tag = ++ctx->property_tag_cnt; + assert(ctx->property_descs_length <= ctx->property_descs_size); + if (ctx->property_descs_length == ctx->property_descs_size) { + size_t new_size = ctx->property_descs_size * 2; + ctx->property_descs = realloc(ctx->property_descs, sizeof(*ctx->property_descs) * new_size); + assert(ctx->property_descs != NULL); + ctx->property_descs_size = new_size; + } + ctx->property_descs_length += 1; + ctx->property_descs[ctx->property_descs_length - 1] = (clay_property_desc_s) { + .type = type, + .tag = tag, + .name = name + }; + return tag; +} + +void clay_ctx_unregister_property(clay_ctx ctx, int tag) { + for (size_t i = 0; i < ctx->property_descs_length; ++i) { + if (ctx->property_descs[i].tag == tag) { + ctx->property_descs[i] = ctx->property_descs[ctx->property_descs_length - 1]; + ctx->property_descs_length -= 1; + return; + } + } +} + +bool clay_ctx_has_property(clay_ctx ctx, const char *name) { + for (size_t i = 0; i < ctx->property_descs_length; ++i) { + if (strcmp(ctx->property_descs[i].name, name) == 0) { + return true; + } + } + return false; +} + +clay_property_desc clay_ctx_get_property_desc(clay_ctx ctx, const char *name) { + for (size_t i = 0; i < ctx->property_descs_length; ++i) { + if (strcmp(ctx->property_descs[i].name, name) == 0) { + return &ctx->property_descs[i]; + } + } + return NULL; +} diff --git a/src/clay-context.h b/src/clay-context.h new file mode 100644 index 0000000..9650e9e --- /dev/null +++ b/src/clay-context.h @@ -0,0 +1,23 @@ +#ifndef CLAY_CLAY_CONTEXT_H +#define CLAY_CLAY_CONTEXT_H + +#include "clay-property.h" +#include + +typedef struct { + const char * name; + int tag; + clay_property_type type; +} clay_property_desc_s, *clay_property_desc; +// TODO: properties should not keep track of the type and allow any type, or something? + + +void clay_ctx_register_layout(clay_ctx ctx, clay layout); +void clay_ctx_unregister_layout(clay_ctx ctx, clay layout); + +int clay_ctx_register_property(clay_ctx ctx, const char * name, clay_property_type type); +void clay_ctx_unregister_property(clay_ctx ctx, int tag); +bool clay_ctx_has_property(clay_ctx ctx, const char *name); +clay_property_desc clay_ctx_get_property_desc(clay_ctx ctx, const char * name); + +#endif //CLAY_CLAY_CONTEXT_H diff --git a/src/clay-document.c b/src/clay-document.c new file mode 100644 index 0000000..1749373 --- /dev/null +++ b/src/clay-document.c @@ -0,0 +1,70 @@ +#include "clay.h" +#include "clay-layout.h" +#include "clay-context.h" +#include "clay-color.h" + +static void cleanup_document(clay layout); + +static void init_document(clay layout); + +static void debug_document(clay layout); + +static struct layout_class layout_class_document = { + .init = &init_document, + .cleanup = &cleanup_document, + .debug = &debug_document, +}; + +static void init_document(clay layout) { + // todo: anything? +} + +static void cleanup_document(clay layout) { + // todo: anything? +} + +static void debug_document(clay layout) { + printf("document:\n"); + clay_property_value width_prop = clay_get_prop(layout, "width"); + clay_property_value height_prop = clay_get_prop(layout, "height"); + clay_property_value content_prop = clay_get_prop(layout, "content"); + clay_property_value bgcolor_prop = clay_get_prop(layout, "bg-color"); + + if (width_prop->type == CLAY_PROPERTY_INT) { + printf(" width: %d\n", width_prop->int_val); + } + if (height_prop->type == CLAY_PROPERTY_INT) { + printf(" height: %d\n", height_prop->int_val); + } + if (bgcolor_prop->type == CLAY_PROPERTY_POINTER) { + clay_color *color = (clay_color*)bgcolor_prop->pointer_val; + printf(" bg-color: (%d, %d, %d, %d)\n", color->r, color->g, color->b, color->a); + } + + printf("\n"); + + if (content_prop->type == CLAY_PROPERTY_POINTER && content_prop->pointer_val != NULL) { + clay content = (clay) content_prop->pointer_val; + content->class.debug(content); + } +} + + +clay clay_create_document(clay_ctx ctx) { + return clay_create_layout(ctx, layout_class_document); +} + +void clay_render_to_png(clay doc, const char *path) { + // todo +} +void clay_debug_layout(clay doc) { + doc->class.debug(doc); +} + + +void clay_document_register_props(clay_ctx ctx) { + clay_ctx_register_property(ctx, "width", CLAY_PROPERTY_INT); + clay_ctx_register_property(ctx, "height", CLAY_PROPERTY_INT); + clay_ctx_register_property(ctx, "content", CLAY_PROPERTY_POINTER); + clay_ctx_register_property(ctx, "bg-color", CLAY_PROPERTY_POINTER); +} \ No newline at end of file diff --git a/src/clay-flex.c b/src/clay-flex.c new file mode 100644 index 0000000..83d6901 --- /dev/null +++ b/src/clay-flex.c @@ -0,0 +1,82 @@ +#include "clay.h" +#include "clay-layout.h" +#include "clay-context.h" + +static void cleanup_flex(clay layout); + +static void init_flex(clay layout); + +static void debug_flex(clay layout); + +static struct layout_class layout_class_flex = { + .init = &init_flex, + .cleanup = &cleanup_flex, + .debug = &debug_flex, +}; + +static void init_flex(clay layout) { + // todo: anything? +} + +static void cleanup_flex(clay layout) { + // todo: anything? +} + +static void debug_flex(clay layout) { + printf("flex:\n"); + clay_property_value direction_prop = clay_get_prop(layout, "direction"); + clay_property_value wrap_prop = clay_get_prop(layout, "wrap"); + clay_property_value align_items_prop = clay_get_prop(layout, "align-items"); + clay_property_value align_content_prop = clay_get_prop(layout, "align-content"); + clay_property_value gap_prop = clay_get_prop(layout, "gap"); + clay_property_value padding_prop = clay_get_prop(layout, "padding"); + clay_property_value contents_prop = clay_get_prop(layout, "contents"); + + if (direction_prop->type == CLAY_PROPERTY_POINTER) { + printf(" direction: %s\n", (char*)direction_prop->pointer_val); + } + if (wrap_prop->type == CLAY_PROPERTY_POINTER) { + printf(" wrap: %s\n", (char*)wrap_prop->pointer_val); + } + if (align_items_prop->type == CLAY_PROPERTY_POINTER) { + printf(" align-items: %s\n", (char*)align_items_prop->pointer_val); + } + if (align_content_prop->type == CLAY_PROPERTY_POINTER) { + printf(" align-content: %s\n", (char*)align_content_prop->pointer_val); + } + if (gap_prop->type == CLAY_PROPERTY_INT) { + printf(" gap: %d\n", gap_prop->int_val); + } + if (padding_prop->type == CLAY_PROPERTY_INT) { + printf(" padding: %d\n", padding_prop->int_val); + } + + printf("\n"); + + if (contents_prop->type == CLAY_PROPERTY_POINTER) { + clay* ptr = (clay*)contents_prop->pointer_val; + while(*ptr != NULL) { + clay content = *ptr; + content->class.debug(content); + ptr += 1; + } + } +} + + +clay clay_create_flex(clay_ctx ctx) { + return clay_create_layout(ctx, layout_class_flex); +} + + +void clay_flex_register_props(clay_ctx ctx) { + clay_ctx_register_property(ctx, "direction", CLAY_PROPERTY_POINTER); + clay_ctx_register_property(ctx, "wrap", CLAY_PROPERTY_POINTER); + clay_ctx_register_property(ctx, "align-items", CLAY_PROPERTY_POINTER); + clay_ctx_register_property(ctx, "align-content", CLAY_PROPERTY_POINTER); + clay_ctx_register_property(ctx, "gap", CLAY_PROPERTY_INT); + clay_ctx_register_property(ctx, "padding", CLAY_PROPERTY_INT); + clay_ctx_register_property(ctx, "contents", CLAY_PROPERTY_POINTER); + clay_ctx_register_property(ctx, "flex:grow", CLAY_PROPERTY_POINTER); + clay_ctx_register_property(ctx, "flex:shrink", CLAY_PROPERTY_POINTER); +} \ No newline at end of file diff --git a/src/clay-layout.c b/src/clay-layout.c new file mode 100644 index 0000000..08ebf74 --- /dev/null +++ b/src/clay-layout.c @@ -0,0 +1,78 @@ +#include +#include +#include +#include "clay-layout.h" +#include "clay-context.h" +#include "clay-property.h" + + +clay clay_create_layout(clay_ctx ctx, struct layout_class class) { + clay layout = malloc(sizeof(*layout)); + assert(layout != NULL); + layout->properties = clay_propset_create(); + layout->class = class; + layout->ctx = ctx; + if (layout->class.init != NULL) { + layout->class.init(layout); + } + clay_ctx_register_layout(ctx, layout); + return layout; +} + +void clay_destroy_layout(clay layout) { + clay_ctx_unregister_layout(layout->ctx, layout); + clay_cleanup_layout(layout); + free(layout); +} + +void clay_cleanup_layout(clay layout) { + if (layout->class.cleanup != NULL) { + layout->class.cleanup(layout); + } + clay_propset_destroy(layout->properties); +} + +clay clay_clone(clay layout) { + clay cloned = malloc(sizeof(*cloned)); + cloned->class = layout->class; + cloned->ctx = layout->ctx; + cloned->properties = clay_propset_clone(layout->properties); + return cloned; +} + +clay_property_value clay_get_prop(clay layout, const char *prop) { + clay_ctx ctx = layout->ctx; + clay_property_desc desc = clay_ctx_get_property_desc(ctx, prop); + assert(desc != NULL); + assert(desc->type != CLAY_PROPERTY_NOT_SET); + return clay_property_get_by_tag(layout->properties, desc->tag); +} + +void clay_set(clay layout, const char *prop, ...) { + // TODO: use a macro so that a property can have values of different types + // the property registry should not keep the type then + // also the property registry can have some kind of alias mechanism for translating strings to enums or something + clay_ctx ctx = layout->ctx; + clay_property_desc desc = clay_ctx_get_property_desc(ctx, prop); + assert(desc != NULL); + assert(desc->type != CLAY_PROPERTY_NOT_SET); + struct clay_property_value value; + value.type = desc->type; + va_list args; + va_start(args, prop); + switch (desc->type) { + case CLAY_PROPERTY_NOT_SET: + break; + case CLAY_PROPERTY_INT: + value.int_val = va_arg(args, int); + break; + case CLAY_PROPERTY_FLOAT: + value.float_val = va_arg(args, double); + break; + case CLAY_PROPERTY_POINTER: + value.pointer_val = va_arg(args, void*); + break; + } + va_end(args); + clay_property_set_by_tag(layout->properties, desc->tag, value); +} \ No newline at end of file diff --git a/src/clay-layout.h b/src/clay-layout.h new file mode 100644 index 0000000..6f4099c --- /dev/null +++ b/src/clay-layout.h @@ -0,0 +1,33 @@ +#ifndef CLAY_CLAY_LAYOUT_H +#define CLAY_CLAY_LAYOUT_H + +#include "clay.h" +#include "clay-property.h" + +typedef void (*init_fn)(clay); + +typedef void (*cleanup_fn)(clay); +typedef void (*debug_fn)(clay); + + +struct layout_class { + init_fn init; + cleanup_fn cleanup; + debug_fn debug; +}; + +struct clay_t { + struct layout_class class; + clay_property_set properties; + clay_ctx ctx; +}; + +clay clay_create_layout(clay_ctx ctx, struct layout_class class); + +void clay_destroy_layout(clay layout); + +void clay_cleanup_layout(clay layout); + +clay_property_value clay_get_prop(clay layout, const char *prop); + +#endif //CLAY_CLAY_LAYOUT_H diff --git a/src/clay-property.c b/src/clay-property.c new file mode 100644 index 0000000..4503601 --- /dev/null +++ b/src/clay-property.c @@ -0,0 +1,87 @@ +#include +#include "clay-property.h" + + +struct clay_property { + int tag; + struct clay_property_value value; +}; + +struct clay_property_set_t { + struct clay_property *props; + size_t length; + size_t size; +}; + +static struct clay_property_value empty_property_value = { + .type = CLAY_PROPERTY_NOT_SET, +}; + + +clay_property_set clay_propset_create(void) { + clay_property_set propset = malloc(sizeof(*propset)); + assert(propset != NULL); + propset->length = 0; + propset->size = 16; + propset->props = malloc(sizeof(*propset->props) * 16); + assert(propset->props != NULL); + return propset; +} + +clay_property_set clay_propset_clone(clay_property_set propset) { + clay_property_set cloned = malloc(sizeof(*cloned)); + assert(propset != NULL); + cloned->length = propset->length; + cloned->size = propset->size; + cloned->props = malloc(sizeof(*cloned->props) * cloned->size); + memcpy(cloned->props, propset->props, sizeof(*cloned->props) * cloned->size); + return cloned; +} + +void clay_propset_destroy(clay_property_set propset) { + free(propset->props); + free(propset); +} + + +clay_property_value clay_property_get_by_tag(clay_property_set propset, int tag) { + for (size_t i = 0; i < propset->length; ++i) { + struct clay_property *prop = &propset->props[i]; + if (prop->tag == tag) { + return &prop->value; + } + } + return &empty_property_value; +} + +void clay_property_set_by_tag(clay_property_set propset, int tag, struct clay_property_value value) { + struct clay_property *prop = NULL; + for (size_t i = 0; i < propset->length; ++i) { + if (propset->props[i].tag == tag) { + prop = &propset->props[i]; + break; + } + } + if (prop == NULL) { + assert(propset->length <= propset->size); + if (propset->length == propset->size) { + size_t new_size = propset->size * 2; + propset->props = realloc(propset->props, sizeof(*propset->props) * new_size); + assert(propset->props != NULL); + propset->size = new_size; + } + propset->length += 1; + prop = &propset->props[propset->length - 1]; + } + prop->tag = tag; + prop->value = value; +} + +void clay_property_delete_by_tag(clay_property_set propset, int tag) { + for (size_t i = 0; i < propset->length; ++i) { + struct clay_property *prop = &propset->props[i]; + if (prop->tag == tag) { + prop->value = empty_property_value; + } + } +} diff --git a/src/clay-property.h b/src/clay-property.h new file mode 100644 index 0000000..3d70c93 --- /dev/null +++ b/src/clay-property.h @@ -0,0 +1,41 @@ +#ifndef CLAY_CLAY_PROPERTY_H +#define CLAY_CLAY_PROPERTY_H + +#include +#include +#include + + +typedef enum { + CLAY_PROPERTY_NOT_SET, + CLAY_PROPERTY_INT, + CLAY_PROPERTY_FLOAT, + CLAY_PROPERTY_POINTER, +} clay_property_type; + +struct clay_property_value { + clay_property_type type; + union { + int int_val; + double float_val; + void *pointer_val; + }; +}; + +typedef const struct clay_property_value *clay_property_value; + +typedef struct clay_property_set_t *clay_property_set; + + +clay_property_set clay_propset_create(void); +clay_property_set clay_propset_clone(clay_property_set); +void clay_propset_destroy(clay_property_set propset); + +clay_property_value clay_property_get_by_tag(clay_property_set propset, int tag); + +void clay_property_set_by_tag(clay_property_set propset, int tag, struct clay_property_value value); + +void clay_property_delete_by_tag(clay_property_set propset, int tag); + + +#endif //CLAY_CLAY_PROPERTY_H diff --git a/src/clay-text.c b/src/clay-text.c new file mode 100644 index 0000000..80d4036 --- /dev/null +++ b/src/clay-text.c @@ -0,0 +1,67 @@ +#include "clay.h" +#include "clay-layout.h" +#include "clay-context.h" +#include "clay-color.h" + +static void cleanup_text(clay layout); + +static void init_text(clay layout); +static void debug_text(clay layout); + +static struct layout_class layout_class_text = { + .cleanup = &cleanup_text, + .init = &init_text, + .debug = &debug_text, +}; + +static void init_text(clay layout) { + // todo: anything? +} + +static void cleanup_text(clay layout) { + // todo: anything? +} + +static void debug_text(clay layout) { + printf("text:\n"); + clay_property_value content_prop = clay_get_prop(layout, "content"); + clay_property_value bgcolor_prop = clay_get_prop(layout, "bg-color"); + clay_property_value width_prop = clay_get_prop(layout, "width"); + clay_property_value flex_grow_prop = clay_get_prop(layout, "flex:grow"); + clay_property_value flex_shrink_prop = clay_get_prop(layout, "flex:shrink"); + clay_property_value align_prop = clay_get_prop(layout, "align"); + clay_property_value vertical_align_prop = clay_get_prop(layout, "vertical-align"); + + if (content_prop->type == CLAY_PROPERTY_POINTER) { + printf(" content: %s\n", (char*)content_prop->pointer_val); + } + if (bgcolor_prop->type == CLAY_PROPERTY_POINTER) { + clay_color *color = (clay_color*)bgcolor_prop->pointer_val; + printf(" bg-color: (%d, %d, %d, %d)\n", color->r, color->g, color->b, color->a); + } + if (width_prop->type == CLAY_PROPERTY_INT) { + printf(" width: %d\n", width_prop->int_val); + } + if (flex_grow_prop->type == CLAY_PROPERTY_INT) { + printf(" flex-grow: %d\n", flex_grow_prop->int_val); + } + if (flex_shrink_prop->type == CLAY_PROPERTY_INT) { + printf(" flex-shrink: %d\n", flex_shrink_prop->int_val); + } + if (align_prop->type == CLAY_PROPERTY_POINTER) { + printf(" align: %s\n", (char*)align_prop->pointer_val); + } + if (vertical_align_prop->type == CLAY_PROPERTY_POINTER) { + printf(" vertical-align: %s\n", (char*)vertical_align_prop->pointer_val); + } +} + + +clay clay_create_text(clay_ctx ctx) { + return clay_create_layout(ctx, layout_class_text); +} + +void clay_text_register_props(clay_ctx ctx) { + clay_ctx_register_property(ctx, "align", CLAY_PROPERTY_POINTER); + clay_ctx_register_property(ctx, "vertical-align", CLAY_PROPERTY_POINTER); +} \ No newline at end of file diff --git a/src/clay.c b/src/clay.c new file mode 100644 index 0000000..11f6d60 --- /dev/null +++ b/src/clay.c @@ -0,0 +1,20 @@ +#include "clay.h" + +#include + +#include + +void hello(void) { + cairo_surface_t *surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 240, 80); + cairo_t *cr = cairo_create (surface); + + cairo_select_font_face (cr, "sans-serif", CAIRO_FONT_SLANT_ITALIC, CAIRO_FONT_WEIGHT_BOLD); + cairo_set_font_size (cr, 10.0); + cairo_set_source_rgb (cr, 0.0, 0.0, 1.0); + cairo_move_to (cr, 10.0, 50.0); + cairo_show_text (cr, "Hello, world"); + + cairo_destroy (cr); + cairo_surface_write_to_png (surface, "hello.png"); + cairo_surface_destroy (surface); +} diff --git a/src/demo.c b/src/demo.c new file mode 100644 index 0000000..4676049 --- /dev/null +++ b/src/demo.c @@ -0,0 +1,65 @@ +#include +#include "clay.h" +#include "clay-color.h" + + +int main(int argc, char **argv) { + clay_ctx ctx = clay_create_context(); + + clay_color c1 = clay_color_rgba(4, 231, 98, 128); + clay_color c2 = clay_color_rgba(34, 49, 39, 128); + 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_set(t1, "width", 100); + 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, "width", 300); + + clay t3 = clay_clone(t1); + clay_set(t3, "content", "ITEM 3"); + clay_set(t3, "bg-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, "width", 400); + + clay t5 = clay_clone(t1); + clay_set(t5, "content", "ITEM 5"); + clay_set(t5, "bg-color", &c5); + clay_set(t5, "width", 250); + + + clay flex = clay_create_flex(ctx); + 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 doc = clay_create_document(ctx); + clay_set(doc, "width", 800); + clay_set(doc, "height", 400); + clay_set(doc, "bg-color", &cbg); + clay_set(doc, "content", flex); + + clay_debug_layout(doc); + + clay_destroy_context(ctx); +} \ No newline at end of file