inital nodes and props
This commit is contained in:
1 parent
98f798ad45
commit
5dad34f2e3
12 files changed
+1107
-9
No files matched your search
@@ -1,4 +1,5 @@
|
||||
#include "internal.h"
|
||||
#include "regolith.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
@@ -102,6 +103,11 @@ rg_context *rg_create(const rg_config *config) {
|
||||
ctx->viewport_h = cfg.viewport_h;
|
||||
ctx->display_scale = cfg.display_scale;
|
||||
|
||||
if(rg__nodes_init(ctx) != RG_OK) {
|
||||
rg_destroy(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rg__log(ctx, RG_LOG_DEBUG, "clay minimum memory: %zu bytes",
|
||||
rg__clay_min_memory());
|
||||
rg__log(ctx, RG_LOG_INFO, "context created (%dx%d @ %.2fx)",
|
||||
@@ -116,7 +122,31 @@ void rg_destroy(rg_context *ctx) {
|
||||
if (!ctx)
|
||||
return;
|
||||
rg__lua_close(ctx);
|
||||
rg__nodes_free(ctx);
|
||||
rg__log(ctx, RG_LOG_INFO, "context destroyed");
|
||||
a = ctx->alloc;
|
||||
a.free(ctx, sizeof *ctx, a.user);
|
||||
}
|
||||
|
||||
void *rg__alloc(rg_context *ctx, size_t size) {
|
||||
return ctx->alloc.alloc(size, ctx->alloc.user);
|
||||
}
|
||||
|
||||
void *rg__zalloc(rg_context *ctx, size_t size) {
|
||||
void *p = rg__alloc(ctx, size);
|
||||
if (p)
|
||||
memset(p, 0, size);
|
||||
return p;
|
||||
}
|
||||
|
||||
void *rg__realloc(rg_context *ctx, void *ptr, size_t old_size,
|
||||
size_t new_size) {
|
||||
if (!ptr)
|
||||
return rg__alloc(ctx, new_size);
|
||||
return ctx->alloc.realloc(ptr, old_size, new_size, ctx->alloc.user);
|
||||
}
|
||||
|
||||
void rg__free(rg_context *ctx, void *ptr, size_t size) {
|
||||
if (ptr)
|
||||
ctx->alloc.free(ptr, size, ctx->alloc.user);
|
||||
}
|
||||
+74
-8
@@ -1,33 +1,99 @@
|
||||
#ifndef RG_INTERNAL_H
|
||||
#define RG_INTERNAL_H
|
||||
|
||||
|
||||
#include "regolith.h"
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#define RG_PRINTF(f, a) __attribute__((format(printf, f, a)))
|
||||
#define RG_PRINTF(f, a) __attribute__((format(printf, f, a)))
|
||||
#else
|
||||
#define RG_PRINTF(f, a)
|
||||
#define RG_PRINTF(f, a)
|
||||
#endif
|
||||
|
||||
#define RG_NIL 0xFFFFFFFFu
|
||||
|
||||
enum { RG_PF_INHERIT = 1, RG_PF_LAYOUT = 2, RG_PF_ANIMATE = 4 };
|
||||
enum { RG_TF_FOCUSABLE = 1, RG_TF_TEXT = 2 };
|
||||
|
||||
#define RG_V_BOOL(v) {RG_VAL_BOOL, (v), {0, 0, 0, 0}, NULL}
|
||||
#define RG_V_ENUM(v) {RG_VAL_ENUM, (v), {0, 0, 0, 0}, NULL}
|
||||
#define RG_V_FLOAT(v) {RG_VAL_FLOAT, 0, {(v), 0, 0, 0}, NULL}
|
||||
#define RG_V_COLOR(r, g, b, a) {RG_VAL_COLOR, 0, {(r), (g), (b), (a)}, NULL}
|
||||
#define RG_V_VEC4(a, b, c, d) {RG_VAL_VEC4, 0, {(a), (b), (c), (d)}, NULL}
|
||||
#define RG_V_SIZE(mode, v) {RG_VAL_SIZE, (mode), {(v), 0, 0, 0}, NULL}
|
||||
#define RG_V_STRING(s) {RG_VAL_STRING, 0, {0, 0, 0, 0}, (s)}
|
||||
|
||||
typedef struct rg_override {
|
||||
int32_t prop;
|
||||
rg_value value;
|
||||
} rg_override;
|
||||
|
||||
typedef struct rg_prop_info {
|
||||
const char *name;
|
||||
int32_t type;
|
||||
uint32_t flags;
|
||||
rg_value def;
|
||||
} rg_prop_info;
|
||||
|
||||
typedef struct rg_type_info {
|
||||
const char *name;
|
||||
uint32_t flags;
|
||||
const rg_override *defaults;
|
||||
int32_t default_count;
|
||||
} rg_type_info;
|
||||
|
||||
typedef struct rg_node_data {
|
||||
uint32_t index;
|
||||
uint32_t generation;
|
||||
uint32_t alive;
|
||||
uint32_t parent, first_child, last_child, prev, next;
|
||||
uint32_t next_free;
|
||||
int32_t type;
|
||||
rg_override *ov;
|
||||
uint32_t ov_count, ov_cap;
|
||||
} rg_node_data;
|
||||
|
||||
struct lua_State;
|
||||
|
||||
struct rg_context {
|
||||
rg_allocator alloc;
|
||||
rg_log_fn log;
|
||||
void* log_user;
|
||||
void *log_user;
|
||||
int32_t log_level;
|
||||
int32_t viewport_w;
|
||||
int32_t viewport_h;
|
||||
float display_scale;
|
||||
char last_error[256];
|
||||
struct lua_State* L;
|
||||
struct lua_State *L;
|
||||
|
||||
rg_node_data **chunks;
|
||||
uint32_t chunk_count;
|
||||
uint32_t chunk_cap;
|
||||
uint32_t slot_count;
|
||||
uint32_t free_head;
|
||||
uint32_t node_count;
|
||||
rg_node root;
|
||||
};
|
||||
|
||||
void rg__log(rg_context* ctx, int32_t level, const char* fmt, ...) RG_PRINTF(3, 4);
|
||||
int rg__fail(rg_context* ctx, int code, const char* fmt, ...) RG_PRINTF(3, 4);
|
||||
void rg__log(rg_context *ctx, int32_t level, const char *fmt, ...)
|
||||
RG_PRINTF(3, 4);
|
||||
int rg__fail(rg_context *ctx, int code, const char *fmt, ...) RG_PRINTF(3, 4);
|
||||
|
||||
void *rg__alloc(rg_context *ctx, size_t size);
|
||||
void *rg__zalloc(rg_context *ctx, size_t size);
|
||||
void *rg__realloc(rg_context *ctx, void *ptr, size_t old_size, size_t new_size);
|
||||
void rg__free(rg_context *ctx, void *ptr, size_t size);
|
||||
|
||||
int rg__nodes_init(rg_context *ctx);
|
||||
void rg__nodes_free(rg_context *ctx);
|
||||
rg_node rg__node_create(rg_context *ctx, int32_t type);
|
||||
rg_node_data *rg__node_resolve(const rg_context *ctx, rg_node node);
|
||||
|
||||
const rg_prop_info *rg__prop_info(int32_t prop);
|
||||
const rg_type_info *rg__type_info(int32_t type);
|
||||
const rg_value *rg__default_value(int32_t type, int32_t prop);
|
||||
void rg__overrides_free(rg_context *ctx, rg_node_data *n);
|
||||
|
||||
size_t rg__clay_min_memory(void);
|
||||
void rg__lua_close(rg_context* ctx);
|
||||
void rg__lua_close(rg_context *ctx);
|
||||
|
||||
#endif
|
||||
+276
@@ -0,0 +1,276 @@
|
||||
#include "internal.h"
|
||||
|
||||
#define CHUNK_SHIFT 8
|
||||
#define CHUNK_SIZE (1u << CHUNK_SHIFT)
|
||||
#define CHUNK_MASK (CHUNK_SIZE - 1u)
|
||||
|
||||
static rg_node_data *slot_at(const rg_context *ctx, uint32_t idx) {
|
||||
return &ctx->chunks[idx >> CHUNK_SHIFT][idx & CHUNK_MASK];
|
||||
}
|
||||
|
||||
static rg_node make_handle(const rg_node_data *n) {
|
||||
return ((rg_node)n->generation << 32) | (rg_node)n->index;
|
||||
}
|
||||
|
||||
static rg_node handle_of(const rg_context *ctx, uint32_t idx) {
|
||||
return idx == RG_NIL ? RG_NODE_NONE : make_handle(slot_at(ctx, idx));
|
||||
}
|
||||
|
||||
rg_node_data *rg__node_resolve(const rg_context *ctx, rg_node h) {
|
||||
uint32_t idx = (uint32_t)(h & 0xFFFFFFFFu);
|
||||
uint32_t gen = (uint32_t)(h >> 32);
|
||||
rg_node_data *n;
|
||||
|
||||
if (h == RG_NODE_NONE || idx >= ctx->slot_count)
|
||||
return NULL;
|
||||
n = slot_at(ctx, idx);
|
||||
if (!n->alive || n->generation != gen)
|
||||
return NULL;
|
||||
return n;
|
||||
}
|
||||
|
||||
static int grow_chunks(rg_context *ctx) {
|
||||
rg_node_data *chunk;
|
||||
|
||||
if (ctx->chunk_count == ctx->chunk_cap) {
|
||||
uint32_t cap = ctx->chunk_cap ? ctx->chunk_cap * 2 : 4;
|
||||
rg_node_data **arr = rg__realloc(
|
||||
ctx, ctx->chunks, ctx->chunk_cap * sizeof *arr, cap * sizeof *arr);
|
||||
if (!arr)
|
||||
return RG_ERR_NO_MEMORY;
|
||||
ctx->chunks = arr;
|
||||
ctx->chunk_cap = cap;
|
||||
}
|
||||
chunk = rg__zalloc(ctx, CHUNK_SIZE * sizeof *chunk);
|
||||
if (!chunk)
|
||||
return RG_ERR_NO_MEMORY;
|
||||
ctx->chunks[ctx->chunk_count++] = chunk;
|
||||
return RG_OK;
|
||||
}
|
||||
|
||||
static rg_node_data *slot_alloc(rg_context *ctx) {
|
||||
rg_node_data *n;
|
||||
uint32_t idx;
|
||||
|
||||
if (ctx->free_head != RG_NIL) {
|
||||
idx = ctx->free_head;
|
||||
n = slot_at(ctx, idx);
|
||||
ctx->free_head = n->next_free;
|
||||
} else {
|
||||
idx = ctx->slot_count;
|
||||
if ((idx >> CHUNK_SHIFT) == ctx->chunk_count && grow_chunks(ctx) != RG_OK)
|
||||
return NULL;
|
||||
n = slot_at(ctx, idx);
|
||||
ctx->slot_count++;
|
||||
}
|
||||
|
||||
n->index = idx;
|
||||
if (n->generation == 0)
|
||||
n->generation = 1;
|
||||
n->alive = 1;
|
||||
n->parent = n->first_child = n->last_child = RG_NIL;
|
||||
n->prev = n->next = n->next_free = RG_NIL;
|
||||
n->ov = NULL;
|
||||
n->ov_count = n->ov_cap = 0;
|
||||
ctx->node_count++;
|
||||
return n;
|
||||
}
|
||||
|
||||
static void release_slot(rg_context *ctx, rg_node_data *n) {
|
||||
rg__log(ctx, RG_LOG_TRACE, "node destroyed: %s (slot %u)",
|
||||
rg_type_name(n->type), (unsigned)n->index);
|
||||
rg__overrides_free(ctx, n);
|
||||
n->alive = 0;
|
||||
if (++n->generation == 0)
|
||||
n->generation = 1;
|
||||
n->next_free = ctx->free_head;
|
||||
ctx->free_head = n->index;
|
||||
ctx->node_count--;
|
||||
}
|
||||
|
||||
static void unlink_node(rg_context *ctx, rg_node_data *n) {
|
||||
rg_node_data *p;
|
||||
|
||||
if (n->parent == RG_NIL)
|
||||
return;
|
||||
p = slot_at(ctx, n->parent);
|
||||
if (n->prev != RG_NIL)
|
||||
slot_at(ctx, n->prev)->next = n->next;
|
||||
else
|
||||
p->first_child = n->next;
|
||||
if (n->next != RG_NIL)
|
||||
slot_at(ctx, n->next)->prev = n->prev;
|
||||
else
|
||||
p->last_child = n->prev;
|
||||
n->parent = n->prev = n->next = RG_NIL;
|
||||
}
|
||||
|
||||
static void destroy_subtree(rg_context *ctx, rg_node_data *top) {
|
||||
uint32_t top_idx = top->index;
|
||||
uint32_t cur = top_idx;
|
||||
|
||||
for (;;) {
|
||||
rg_node_data *n = slot_at(ctx, cur);
|
||||
uint32_t parent;
|
||||
|
||||
if (n->first_child != RG_NIL) {
|
||||
cur = n->first_child;
|
||||
continue;
|
||||
}
|
||||
parent = n->parent;
|
||||
unlink_node(ctx, n);
|
||||
release_slot(ctx, n);
|
||||
if (cur == top_idx)
|
||||
break;
|
||||
cur = parent;
|
||||
}
|
||||
}
|
||||
|
||||
static int is_self_or_ancestor(const rg_context *ctx, const rg_node_data *anc,
|
||||
const rg_node_data *n) {
|
||||
while (n) {
|
||||
if (n == anc)
|
||||
return 1;
|
||||
n = n->parent == RG_NIL ? NULL : slot_at(ctx, n->parent);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
rg_node rg__node_create(rg_context *ctx, int32_t type) {
|
||||
rg_node_data *n = slot_alloc(ctx);
|
||||
|
||||
if (!n) {
|
||||
rg__fail(ctx, RG_ERR_NO_MEMORY, "node allocation failed");
|
||||
return RG_NODE_NONE;
|
||||
}
|
||||
n->type = type;
|
||||
rg__log(ctx, RG_LOG_TRACE, "node created: %s (slot %u)", rg_type_name(type),
|
||||
(unsigned)n->index);
|
||||
return make_handle(n);
|
||||
}
|
||||
|
||||
int rg__nodes_init(rg_context *ctx) {
|
||||
ctx->free_head = RG_NIL;
|
||||
ctx->root = rg__node_create(ctx, RG_NODE_ROOT);
|
||||
return ctx->root == RG_NODE_NONE ? RG_ERR_NO_MEMORY : RG_OK;
|
||||
}
|
||||
|
||||
void rg__nodes_free(rg_context *ctx) {
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < ctx->slot_count; i++) {
|
||||
rg_node_data *n = slot_at(ctx, i);
|
||||
if (n->alive)
|
||||
rg__overrides_free(ctx, n);
|
||||
}
|
||||
for (i = 0; i < ctx->chunk_count; i++)
|
||||
rg__free(ctx, ctx->chunks[i], CHUNK_SIZE * sizeof(rg_node_data));
|
||||
rg__free(ctx, ctx->chunks, ctx->chunk_cap * sizeof *ctx->chunks);
|
||||
ctx->chunks = NULL;
|
||||
ctx->chunk_count = ctx->chunk_cap = ctx->slot_count = ctx->node_count = 0;
|
||||
ctx->root = RG_NODE_NONE;
|
||||
}
|
||||
|
||||
rg_node rg_root(const rg_context *ctx) { return ctx->root; }
|
||||
|
||||
rg_node rg_node_create(rg_context *ctx, int32_t type) {
|
||||
if (type <= RG_NODE_ROOT || type >= RG_NODE_TYPE_COUNT) {
|
||||
rg__fail(ctx, RG_ERR_TYPE, "cannot create node of type %d", (int)type);
|
||||
return RG_NODE_NONE;
|
||||
}
|
||||
return rg__node_create(ctx, type);
|
||||
}
|
||||
|
||||
void rg_node_destroy(rg_context *ctx, rg_node node) {
|
||||
rg_node_data *n = rg__node_resolve(ctx, node);
|
||||
|
||||
if (!n) {
|
||||
rg__log(ctx, RG_LOG_WARN, "destroy of a stale or invalid handle ignored");
|
||||
return;
|
||||
}
|
||||
if (node == ctx->root) {
|
||||
rg__log(ctx, RG_LOG_WARN, "the root node cannot be destroyed");
|
||||
return;
|
||||
}
|
||||
unlink_node(ctx, n);
|
||||
destroy_subtree(ctx, n);
|
||||
}
|
||||
|
||||
int rg_node_valid(const rg_context *ctx, rg_node node) {
|
||||
return rg__node_resolve(ctx, node) != NULL;
|
||||
}
|
||||
|
||||
int32_t rg_node_type(const rg_context *ctx, rg_node node) {
|
||||
rg_node_data *n = rg__node_resolve(ctx, node);
|
||||
return n ? n->type : -1;
|
||||
}
|
||||
|
||||
int rg_node_insert_before(rg_context *ctx, rg_node parent, rg_node child,
|
||||
rg_node ref) {
|
||||
rg_node_data *p = rg__node_resolve(ctx, parent);
|
||||
rg_node_data *c = rg__node_resolve(ctx, child);
|
||||
rg_node_data *r = NULL;
|
||||
|
||||
if (!p || !c)
|
||||
return rg__fail(ctx, RG_ERR_HANDLE, "stale or invalid handle");
|
||||
if (child == ctx->root)
|
||||
return rg__fail(ctx, RG_ERR_STATE, "the root cannot be a child");
|
||||
if (ref != RG_NODE_NONE) {
|
||||
r = rg__node_resolve(ctx, ref);
|
||||
if (!r)
|
||||
return rg__fail(ctx, RG_ERR_HANDLE, "stale or invalid reference handle");
|
||||
if (r == c || r->parent != p->index)
|
||||
return rg__fail(ctx, RG_ERR_STATE,
|
||||
"reference is not a child of the parent");
|
||||
}
|
||||
if (is_self_or_ancestor(ctx, c, p))
|
||||
return rg__fail(ctx, RG_ERR_STATE, "insert would create a cycle");
|
||||
|
||||
unlink_node(ctx, c);
|
||||
c->parent = p->index;
|
||||
if (r) {
|
||||
c->next = r->index;
|
||||
c->prev = r->prev;
|
||||
if (r->prev != RG_NIL)
|
||||
slot_at(ctx, r->prev)->next = c->index;
|
||||
else
|
||||
p->first_child = c->index;
|
||||
r->prev = c->index;
|
||||
} else {
|
||||
c->prev = p->last_child;
|
||||
if (p->last_child != RG_NIL)
|
||||
slot_at(ctx, p->last_child)->next = c->index;
|
||||
else
|
||||
p->first_child = c->index;
|
||||
p->last_child = c->index;
|
||||
}
|
||||
return RG_OK;
|
||||
}
|
||||
|
||||
int rg_node_append(rg_context *ctx, rg_node parent, rg_node child) {
|
||||
return rg_node_insert_before(ctx, parent, child, RG_NODE_NONE);
|
||||
}
|
||||
|
||||
int rg_node_detach(rg_context *ctx, rg_node node) {
|
||||
rg_node_data *n = rg__node_resolve(ctx, node);
|
||||
|
||||
if (!n)
|
||||
return rg__fail(ctx, RG_ERR_HANDLE, "stale or invalid handle");
|
||||
unlink_node(ctx, n);
|
||||
return RG_OK;
|
||||
}
|
||||
|
||||
rg_node rg_node_parent(const rg_context *ctx, rg_node node) {
|
||||
rg_node_data *n = rg__node_resolve(ctx, node);
|
||||
return n ? handle_of(ctx, n->parent) : RG_NODE_NONE;
|
||||
}
|
||||
|
||||
rg_node rg_node_first_child(const rg_context *ctx, rg_node node) {
|
||||
rg_node_data *n = rg__node_resolve(ctx, node);
|
||||
return n ? handle_of(ctx, n->first_child) : RG_NODE_NONE;
|
||||
}
|
||||
|
||||
rg_node rg_node_next_sibling(const rg_context *ctx, rg_node node) {
|
||||
rg_node_data *n = rg__node_resolve(ctx, node);
|
||||
return n ? handle_of(ctx, n->next) : RG_NODE_NONE;
|
||||
}
|
||||
+251
@@ -0,0 +1,251 @@
|
||||
#include "internal.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
static const rg_prop_info prop_table[RG_PROP_COUNT] = {
|
||||
[RG_PROP_DIRECTION] = {"direction", RG_VAL_ENUM, RG_PF_LAYOUT,
|
||||
RG_V_ENUM(RG_DIR_ROW)},
|
||||
[RG_PROP_WIDTH] = {"width", RG_VAL_SIZE, RG_PF_LAYOUT | RG_PF_ANIMATE,
|
||||
RG_V_SIZE(RG_SIZE_FIT, 0)},
|
||||
[RG_PROP_HEIGHT] = {"height", RG_VAL_SIZE, RG_PF_LAYOUT | RG_PF_ANIMATE,
|
||||
RG_V_SIZE(RG_SIZE_FIT, 0)},
|
||||
[RG_PROP_PADDING] = {"padding", RG_VAL_VEC4, RG_PF_LAYOUT | RG_PF_ANIMATE,
|
||||
RG_V_VEC4(0, 0, 0, 0)},
|
||||
[RG_PROP_GAP] = {"gap", RG_VAL_FLOAT, RG_PF_LAYOUT | RG_PF_ANIMATE,
|
||||
RG_V_FLOAT(0)},
|
||||
[RG_PROP_ALIGN_X] = {"align_x", RG_VAL_ENUM, RG_PF_LAYOUT,
|
||||
RG_V_ENUM(RG_ALIGN_START)},
|
||||
[RG_PROP_ALIGN_Y] = {"align_y", RG_VAL_ENUM, RG_PF_LAYOUT,
|
||||
RG_V_ENUM(RG_ALIGN_START)},
|
||||
[RG_PROP_BACKGROUND] = {"background", RG_VAL_COLOR, RG_PF_ANIMATE,
|
||||
RG_V_COLOR(0, 0, 0, 0)},
|
||||
[RG_PROP_BORDER_COLOR] = {"border_color", RG_VAL_COLOR, RG_PF_ANIMATE,
|
||||
RG_V_COLOR(0, 0, 0, 0)},
|
||||
[RG_PROP_BORDER_WIDTH] = {"border_width", RG_VAL_VEC4,
|
||||
RG_PF_LAYOUT | RG_PF_ANIMATE,
|
||||
RG_V_VEC4(0, 0, 0, 0)},
|
||||
[RG_PROP_CORNER_RADIUS] = {"corner_radius", RG_VAL_VEC4, RG_PF_ANIMATE,
|
||||
RG_V_VEC4(0, 0, 0, 0)},
|
||||
[RG_PROP_OPACITY] = {"opacity", RG_VAL_FLOAT, RG_PF_ANIMATE, RG_V_FLOAT(1)},
|
||||
[RG_PROP_VISIBLE] = {"visible", RG_VAL_BOOL, RG_PF_LAYOUT, RG_V_BOOL(1)},
|
||||
[RG_PROP_TEXT] = {"text", RG_VAL_STRING, RG_PF_LAYOUT, RG_V_STRING("")},
|
||||
[RG_PROP_FONT] = {"font", RG_VAL_STRING, RG_PF_LAYOUT | RG_PF_INHERIT,
|
||||
RG_V_STRING("")},
|
||||
[RG_PROP_FONT_SIZE] = {"font_size", RG_VAL_FLOAT,
|
||||
RG_PF_LAYOUT | RG_PF_INHERIT, RG_V_FLOAT(16)},
|
||||
[RG_PROP_COLOR] = {"color", RG_VAL_COLOR, RG_PF_INHERIT | RG_PF_ANIMATE,
|
||||
RG_V_COLOR(1, 1, 1, 1)},
|
||||
};
|
||||
|
||||
static const char *val_type_name(int32_t t) {
|
||||
static const char *names[] = {"none", "bool", "int", "float", "enum",
|
||||
"color", "size", "vec4", "string"};
|
||||
return t >= 0 && t <= RG_VAL_STRING ? names[t] : "invalid";
|
||||
}
|
||||
|
||||
static void value_init(rg_value *v, int32_t type) {
|
||||
memset(v, 0, sizeof *v);
|
||||
v->type = type;
|
||||
}
|
||||
|
||||
const rg_prop_info *rg__prop_info(int32_t prop) {
|
||||
if (prop < 0 || prop >= RG_PROP_COUNT)
|
||||
return NULL;
|
||||
return &prop_table[prop];
|
||||
}
|
||||
|
||||
int32_t rg_prop_find(const char *name) {
|
||||
int32_t i;
|
||||
|
||||
if (!name)
|
||||
return -1;
|
||||
for (i = 0; i < RG_PROP_COUNT; i++)
|
||||
if (strcmp(prop_table[i].name, name) == 0)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char *rg_prop_name(int32_t prop) {
|
||||
const rg_prop_info *info = rg__prop_info(prop);
|
||||
return info ? info->name : NULL;
|
||||
}
|
||||
|
||||
int32_t rg_prop_type(int32_t prop) {
|
||||
const rg_prop_info *info = rg__prop_info(prop);
|
||||
return info ? info->type : RG_VAL_NONE;
|
||||
}
|
||||
|
||||
const rg_value *rg__default_value(int32_t type, int32_t prop) {
|
||||
const rg_type_info *t = rg__type_info(type);
|
||||
int32_t i;
|
||||
|
||||
if (t)
|
||||
for (i = 0; i < t->default_count; i++)
|
||||
if (t->defaults[i].prop == prop)
|
||||
return &t->defaults[i].value;
|
||||
return &prop_table[prop].def;
|
||||
}
|
||||
|
||||
static char *dup_string(rg_context *ctx, const char *s) {
|
||||
size_t len = strlen(s) + 1;
|
||||
char *p = rg__alloc(ctx, len);
|
||||
|
||||
if (p)
|
||||
memcpy(p, s, len);
|
||||
return p;
|
||||
}
|
||||
|
||||
static void drop_value(rg_context *ctx, rg_value *v) {
|
||||
if (v->type == RG_VAL_STRING && v->s) {
|
||||
rg__free(ctx, (void *)v->s, strlen(v->s) + 1);
|
||||
v->s = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void rg__overrides_free(rg_context *ctx, rg_node_data *n) {
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < n->ov_count; i++)
|
||||
drop_value(ctx, &n->ov[i].value);
|
||||
rg__free(ctx, n->ov, n->ov_cap * sizeof *n->ov);
|
||||
n->ov = NULL;
|
||||
n->ov_count = n->ov_cap = 0;
|
||||
}
|
||||
|
||||
static rg_override *find_override(const rg_node_data *n, int32_t prop) {
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < n->ov_count; i++)
|
||||
if (n->ov[i].prop == prop)
|
||||
return &n->ov[i];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int rg_set(rg_context *ctx, rg_node node, int32_t prop, const rg_value *value) {
|
||||
rg_node_data *n = rg__node_resolve(ctx, node);
|
||||
const rg_prop_info *info = rg__prop_info(prop);
|
||||
rg_override *o;
|
||||
rg_value v;
|
||||
|
||||
if (!n)
|
||||
return rg__fail(ctx, RG_ERR_HANDLE, "set: stale or invalid handle");
|
||||
if (!info)
|
||||
return rg__fail(ctx, RG_ERR_PROP, "set: unknown property %d", (int)prop);
|
||||
if (!value || value->type != info->type)
|
||||
return rg__fail(ctx, RG_ERR_TYPE, "set: property '%s' expects %s",
|
||||
info->name, val_type_name(info->type));
|
||||
if (value->type == RG_VAL_STRING && !value->s)
|
||||
return rg__fail(ctx, RG_ERR_TYPE, "set: property '%s' got a null string",
|
||||
info->name);
|
||||
if (value->type == RG_VAL_SIZE &&
|
||||
(value->i < RG_SIZE_FIT || value->i > RG_SIZE_PERCENT))
|
||||
return rg__fail(ctx, RG_ERR_TYPE,
|
||||
"set: property '%s' got an invalid size mode", info->name);
|
||||
|
||||
v = *value;
|
||||
if (v.type == RG_VAL_STRING) {
|
||||
v.s = dup_string(ctx, value->s);
|
||||
if (!v.s)
|
||||
return rg__fail(ctx, RG_ERR_NO_MEMORY, "set: string allocation failed");
|
||||
}
|
||||
|
||||
o = find_override(n, prop);
|
||||
if (o) {
|
||||
drop_value(ctx, &o->value);
|
||||
o->value = v;
|
||||
} else {
|
||||
if (n->ov_count == n->ov_cap) {
|
||||
uint32_t cap = n->ov_cap ? n->ov_cap * 2 : 4;
|
||||
rg_override *arr =
|
||||
rg__realloc(ctx, n->ov, n->ov_cap * sizeof *arr, cap * sizeof *arr);
|
||||
if (!arr) {
|
||||
drop_value(ctx, &v);
|
||||
return rg__fail(ctx, RG_ERR_NO_MEMORY,
|
||||
"set: override allocation failed");
|
||||
}
|
||||
n->ov = arr;
|
||||
n->ov_cap = cap;
|
||||
}
|
||||
n->ov[n->ov_count].prop = prop;
|
||||
n->ov[n->ov_count].value = v;
|
||||
n->ov_count++;
|
||||
}
|
||||
rg__log(ctx, RG_LOG_TRACE, "set %s on %s", info->name, rg_type_name(n->type));
|
||||
return RG_OK;
|
||||
}
|
||||
|
||||
int rg_get(rg_context *ctx, rg_node node, int32_t prop, rg_value *out) {
|
||||
rg_node_data *n = rg__node_resolve(ctx, node);
|
||||
const rg_override *o;
|
||||
|
||||
if (!n)
|
||||
return rg__fail(ctx, RG_ERR_HANDLE, "get: stale or invalid handle");
|
||||
if (!rg__prop_info(prop))
|
||||
return rg__fail(ctx, RG_ERR_PROP, "get: unknown property %d", (int)prop);
|
||||
if (!out)
|
||||
return rg__fail(ctx, RG_ERR_STATE, "get: null output");
|
||||
|
||||
o = find_override(n, prop);
|
||||
*out = o ? o->value : *rg__default_value(n->type, prop);
|
||||
return RG_OK;
|
||||
}
|
||||
|
||||
int rg_unset(rg_context *ctx, rg_node node, int32_t prop) {
|
||||
rg_node_data *n = rg__node_resolve(ctx, node);
|
||||
rg_override *o;
|
||||
|
||||
if (!n)
|
||||
return rg__fail(ctx, RG_ERR_HANDLE, "unset: stale or invalid handle");
|
||||
if (!rg__prop_info(prop))
|
||||
return rg__fail(ctx, RG_ERR_PROP, "unset: unknown property %d", (int)prop);
|
||||
|
||||
o = find_override(n, prop);
|
||||
if (o) {
|
||||
drop_value(ctx, &o->value);
|
||||
*o = n->ov[--n->ov_count];
|
||||
}
|
||||
return RG_OK;
|
||||
}
|
||||
|
||||
int rg_set_int(rg_context *ctx, rg_node node, int32_t prop, int32_t v) {
|
||||
const rg_prop_info *info = rg__prop_info(prop);
|
||||
rg_value val;
|
||||
|
||||
if (!info)
|
||||
return rg__fail(ctx, RG_ERR_PROP, "set: unknown property %d", (int)prop);
|
||||
if (info->type != RG_VAL_BOOL && info->type != RG_VAL_INT &&
|
||||
info->type != RG_VAL_ENUM)
|
||||
return rg__fail(ctx, RG_ERR_TYPE,
|
||||
"set: property '%s' is not an integer type", info->name);
|
||||
value_init(&val, info->type);
|
||||
val.i = v;
|
||||
return rg_set(ctx, node, prop, &val);
|
||||
}
|
||||
|
||||
int rg_set_float(rg_context *ctx, rg_node node, int32_t prop, float v) {
|
||||
rg_value val;
|
||||
|
||||
value_init(&val, RG_VAL_FLOAT);
|
||||
val.f[0] = v;
|
||||
return rg_set(ctx, node, prop, &val);
|
||||
}
|
||||
|
||||
int rg_set_string(rg_context *ctx, rg_node node, int32_t prop, const char *v) {
|
||||
rg_value val;
|
||||
|
||||
value_init(&val, RG_VAL_STRING);
|
||||
val.s = v;
|
||||
return rg_set(ctx, node, prop, &val);
|
||||
}
|
||||
|
||||
int rg_set_color(rg_context *ctx, rg_node node, int32_t prop, float r, float g,
|
||||
float b, float a) {
|
||||
rg_value val;
|
||||
|
||||
value_init(&val, RG_VAL_COLOR);
|
||||
val.f[0] = r;
|
||||
val.f[1] = g;
|
||||
val.f[2] = b;
|
||||
val.f[3] = a;
|
||||
return rg_set(ctx, node, prop, &val);
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
#include "internal.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#define COUNT(a) ((int32_t)(sizeof(a) / sizeof((a)[0])))
|
||||
|
||||
static const rg_override root_defaults[] = {
|
||||
{RG_PROP_WIDTH, RG_V_SIZE(RG_SIZE_GROW, 0)},
|
||||
{RG_PROP_HEIGHT, RG_V_SIZE(RG_SIZE_GROW, 0)},
|
||||
};
|
||||
|
||||
static const rg_override hbox_defaults[] = {
|
||||
{RG_PROP_DIRECTION, RG_V_ENUM(RG_DIR_ROW)},
|
||||
};
|
||||
|
||||
static const rg_override vbox_defaults[] = {
|
||||
{RG_PROP_DIRECTION, RG_V_ENUM(RG_DIR_COLUMN)},
|
||||
};
|
||||
|
||||
static const rg_override panel_defaults[] = {
|
||||
{RG_PROP_PADDING, RG_V_VEC4(8, 8, 8, 8)},
|
||||
{RG_PROP_BACKGROUND, RG_V_COLOR(0.15f, 0.15f, 0.18f, 1.0f)},
|
||||
};
|
||||
|
||||
static const rg_override button_defaults[] = {
|
||||
{RG_PROP_PADDING, RG_V_VEC4(8, 16, 8, 16)},
|
||||
{RG_PROP_BACKGROUND, RG_V_COLOR(0.25f, 0.25f, 0.30f, 1.0f)},
|
||||
{RG_PROP_CORNER_RADIUS, RG_V_VEC4(4, 4, 4, 4)},
|
||||
};
|
||||
|
||||
static const rg_type_info type_table[RG_NODE_TYPE_COUNT] = {
|
||||
[RG_NODE_ROOT] = {"Root", 0, root_defaults, COUNT(root_defaults)},
|
||||
[RG_NODE_BOX] = {"Box", 0, NULL, 0},
|
||||
[RG_NODE_HBOX] = {"HBox", 0, hbox_defaults, COUNT(hbox_defaults)},
|
||||
[RG_NODE_VBOX] = {"VBox", 0, vbox_defaults, COUNT(vbox_defaults)},
|
||||
[RG_NODE_PANEL] = {"Panel", 0, panel_defaults, COUNT(panel_defaults)},
|
||||
[RG_NODE_LABEL] = {"Label", RG_TF_TEXT, NULL, 0},
|
||||
[RG_NODE_BUTTON] = {"Button", RG_TF_TEXT | RG_TF_FOCUSABLE, button_defaults,
|
||||
COUNT(button_defaults)},
|
||||
};
|
||||
|
||||
const rg_type_info *rg__type_info(int32_t type) {
|
||||
if (type < 0 || type >= RG_NODE_TYPE_COUNT)
|
||||
return NULL;
|
||||
return &type_table[type];
|
||||
}
|
||||
|
||||
int32_t rg_type_find(const char *name) {
|
||||
int32_t i;
|
||||
|
||||
if (!name)
|
||||
return -1;
|
||||
for (i = 0; i < RG_NODE_TYPE_COUNT; i++)
|
||||
if (strcmp(type_table[i].name, name) == 0)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char *rg_type_name(int32_t type) {
|
||||
const rg_type_info *t = rg__type_info(type);
|
||||
return t ? t->name : "invalid";
|
||||
}
|
||||
Reference in new issue
Block a user