rewrite
This commit is contained in:
parent
14def875d6
commit
894b2763c0
3 changed files with 354 additions and 522 deletions
381
ugui.c
381
ugui.c
|
|
@ -1,23 +1,59 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <err.h>
|
||||
|
||||
#include "ugui.h"
|
||||
|
||||
|
||||
#define SALT 0xbabb0cac
|
||||
#define DEF_SCALE 1.0
|
||||
#define DEF_DPI 96.0
|
||||
#define E_STACK_STEP (1024 * 128)
|
||||
#define DEF_PPI 96.0
|
||||
#define STACK_STEP 64
|
||||
|
||||
#define PPI_PPM(ppi, scale) (ppi * scale * 0.03937008)
|
||||
#define PPI_PPD(ppi, scale) (PPI_PPM(ppi, scale) * 0.3528)
|
||||
#define IS_VALID_UNIT(u) (u==UG_UNIT_PX||u==UG_UNIT_MM||u==UG_UNIT_PT)
|
||||
#define UG_ERR(...) err(errno, "__FUNCTION__: " __VA_ARGS__)
|
||||
|
||||
// default style
|
||||
// TODO: fill default style
|
||||
static const ug_style_t default_style = {
|
||||
.text = {
|
||||
.color = RGB_FORMAT(0xffffff),
|
||||
.alt_color = RGB_FORMAT(0xbbbbbb),
|
||||
.size = SIZE_PX(16),
|
||||
.alt_size = SIZE_PX(12),
|
||||
},
|
||||
};
|
||||
|
||||
static const ug_vec2_t max_size = {10e6, 10e6};
|
||||
|
||||
static ug_style_t style_cache = {0};
|
||||
|
||||
|
||||
/*=============================================================================*
|
||||
* Common Functions *
|
||||
*=============================================================================*/
|
||||
|
||||
|
||||
// grow a stack
|
||||
#define grow(S) \
|
||||
{ \
|
||||
S.items = realloc(S.items, (S.size+STACK_STEP)*sizeof(*(S.items))); \
|
||||
if(!S.items) \
|
||||
UG_ERR("Could not allocate stack #S: %s", strerror(errno)); \
|
||||
memset(&(S.items[S.size]), 0, STACK_STEP*sizeof(*(S.items))); \
|
||||
S.size += STACK_STEP; \
|
||||
}
|
||||
|
||||
#define TO_PPM(dpi, scale) (dpi * scale * 0.03937008)
|
||||
|
||||
// https://en.wikipedia.org/wiki/Jenkins_hash_function
|
||||
unsigned int hash(void *data, unsigned int size)
|
||||
static ug_id_t hash(const void *data, unsigned int size)
|
||||
{
|
||||
if (!size)
|
||||
return 0;
|
||||
unsigned int hash = SALT;
|
||||
ug_id_t hash = SALT;
|
||||
unsigned char *v = (unsigned char *)data;
|
||||
|
||||
for (; size; size--) {
|
||||
|
|
@ -33,17 +69,44 @@ unsigned int hash(void *data, unsigned int size)
|
|||
}
|
||||
|
||||
|
||||
static void grow_stack(ug_ctx_t *ctx)
|
||||
// update the style cache with the correct sizes in pixels and colors
|
||||
static void update_style_cache(ug_ctx_t *ctx)
|
||||
{
|
||||
if (!ctx)
|
||||
err(EXIT_FAILURE, "__FUNCTION__:" "Cannot grow null context");
|
||||
ctx->e_size += E_STACK_STEP;
|
||||
ctx->e_stack = realloc(ctx->e_stack, ctx->e_size);
|
||||
if (!ctx->e_stack)
|
||||
err(errno, "__FUNCTION__:" "Could not grow stack: %s", strerror(errno));
|
||||
const ug_style_t *s = ctx->style;
|
||||
// TODO: do this shit
|
||||
(void)s;
|
||||
}
|
||||
|
||||
|
||||
ug_rect_t rect_to_px(ug_ctx_t *ctx, ug_rect_t rect)
|
||||
{
|
||||
float scale = 1.0;
|
||||
|
||||
switch (ctx->unit) {
|
||||
case UG_UNIT_PX:
|
||||
return rect;
|
||||
case UG_UNIT_MM:
|
||||
scale = ctx->ppm;
|
||||
break;
|
||||
case UG_UNIT_PT:
|
||||
scale = ctx->ppd;
|
||||
break;
|
||||
}
|
||||
|
||||
rect.x *= scale;
|
||||
rect.y *= scale;
|
||||
rect.w *= scale;
|
||||
rect.h *= scale;
|
||||
|
||||
return rect;
|
||||
}
|
||||
|
||||
|
||||
/*=============================================================================*
|
||||
* Context Operations *
|
||||
*=============================================================================*/
|
||||
|
||||
|
||||
// creates a new context, fills with default values, ctx is ready for ug_start()
|
||||
ug_ctx_t *ug_new_ctx(void)
|
||||
{
|
||||
|
|
@ -53,9 +116,15 @@ ug_ctx_t *ug_new_ctx(void)
|
|||
memset(ctx, 0, sizeof(ug_ctx_t));
|
||||
|
||||
ctx->scale = DEF_SCALE;
|
||||
ctx->dpi = DEF_DPI;
|
||||
ctx->ppm = TO_PPM(DEF_SCALE, DEF_DPI);
|
||||
ctx->ppi = DEF_PPI;
|
||||
ctx->ppm = PPI_PPM(DEF_SCALE, DEF_PPI);
|
||||
ctx->ppd = PPI_PPD(DEF_SCALE, DEF_PPI);
|
||||
|
||||
ctx->unit = UG_UNIT_PX;
|
||||
ctx->style = &default_style;
|
||||
ctx->style_px = &style_cache;
|
||||
|
||||
// TODO: allocate stacks
|
||||
return ctx;
|
||||
}
|
||||
|
||||
|
|
@ -66,173 +135,165 @@ void ug_free_ctx(ug_ctx_t *ctx)
|
|||
warn("__FUNCTION__:" "Trying to free a null context");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ctx->e_stack) {
|
||||
warn("__FUNCTION__:" "Context has null element stack");
|
||||
return;
|
||||
}
|
||||
|
||||
free(ctx->e_stack);
|
||||
// TODO: free stacks
|
||||
free(ctx);
|
||||
|
||||
// NOTE: do not free style since the default is statically allocated, let
|
||||
// the user take care of it instead
|
||||
}
|
||||
|
||||
|
||||
// updates the context with user information
|
||||
int ug_update_ctx(ug_ctx_t *ctx,
|
||||
unsigned int window_id,
|
||||
float scale,
|
||||
float dpi,
|
||||
float ppm
|
||||
)
|
||||
#define TEST_CTX(ctx) { if (!ctx) return -1; }
|
||||
|
||||
int ug_ctx_set_displayinfo(ug_ctx_t *ctx, float scale, float ppi)
|
||||
{
|
||||
if (!ctx)
|
||||
TEST_CTX(ctx);
|
||||
if (scale <= 0 || ppi < 20.0)
|
||||
return -1;
|
||||
ctx->window_id = window_id;
|
||||
|
||||
ctx->ppm = PPI_PPM(scale, ppi);
|
||||
ctx->ppd = PPI_PPM(scale, ppi);
|
||||
ctx->scale = scale;
|
||||
ctx->ppi = ppi;
|
||||
|
||||
update_style_cache(ctx);
|
||||
|
||||
if (scale)
|
||||
ctx->scale = scale;
|
||||
if (dpi)
|
||||
ctx->dpi = dpi;
|
||||
if (!ppm)
|
||||
ctx->ppm = TO_PPM(ctx->dpi, ctx->scale);
|
||||
else
|
||||
ctx->ppm = ppm;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int ug_ctx_set_drawableregion(ug_ctx_t *ctx, ug_vec2_t size)
|
||||
{
|
||||
TEST_CTX(ctx);
|
||||
if (size.w <= 0 || size.h <= 0)
|
||||
return -1;
|
||||
|
||||
ctx->size.w = size.w;
|
||||
ctx->size.h = size.h;
|
||||
|
||||
// FIXME: do I need to do something like update_container_size() here?
|
||||
// maybe it is redundant since each frame not every conatiner is
|
||||
// re-added
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int ug_ctx_set_style(ug_ctx_t *ctx, const ug_style_t *style)
|
||||
{
|
||||
TEST_CTX(ctx);
|
||||
if (!style)
|
||||
return -1;
|
||||
// TODO: validate style
|
||||
|
||||
ctx->style = style;
|
||||
update_style_cache(ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int ug_ctx_set_unit(ug_ctx_t *ctx, ug_unit_t unit)
|
||||
{
|
||||
TEST_CTX(ctx);
|
||||
if (!IS_VALID_UNIT(unit))
|
||||
return -1;
|
||||
|
||||
ctx->unit = unit;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*=============================================================================*
|
||||
* INPUT FUNCTIONS *
|
||||
* Container Operations *
|
||||
*=============================================================================*/
|
||||
|
||||
#define TEST_CTX(ctx) { \
|
||||
if (!ctx) { \
|
||||
warn("__FUNCTION__:" "trying to use a null context"); \
|
||||
return; \
|
||||
} \
|
||||
}
|
||||
|
||||
void ug_input_mousemove(ug_ctx_t *ctx, int x, int y)
|
||||
// get a new or existing container handle
|
||||
static ug_container_t *get_container(ug_ctx_t *ctx, ug_id_t id)
|
||||
{
|
||||
TEST_CTX(ctx);
|
||||
ctx->mouse.pos = (struct ug_vec2){ .x = x, .y = y };
|
||||
}
|
||||
|
||||
void ug_input_mousedown(ug_ctx_t *ctx, int x, int y, unsigned char btn)
|
||||
{
|
||||
TEST_CTX(ctx);
|
||||
ug_input_mousemove(ctx, x, y);
|
||||
ctx->mouse.down_mask |= btn;
|
||||
ctx->mouse.press_mask |= btn;
|
||||
}
|
||||
|
||||
|
||||
void ug_input_mouseup(ug_ctx_t *ctx, int x, int y, unsigned char btn)
|
||||
{
|
||||
TEST_CTX(ctx);
|
||||
ug_input_mousemove(ctx, x, y);
|
||||
ctx->mouse.down_mask &= ~btn;
|
||||
}
|
||||
|
||||
|
||||
void ug_input_scroll(ug_ctx_t *ctx, int x, int y)
|
||||
{
|
||||
TEST_CTX(ctx);
|
||||
ctx->mouse.scroll_delta.x += x;
|
||||
ctx->mouse.scroll_delta.y += y;
|
||||
}
|
||||
|
||||
|
||||
void ug_input_keydown(ug_ctx_t *ctx, int key)
|
||||
{
|
||||
TEST_CTX(ctx);
|
||||
ctx->key.down_mask |= key;
|
||||
ctx->key.press_mask |= key;
|
||||
}
|
||||
|
||||
|
||||
void ug_input_keyup(ug_ctx_t *ctx, int key)
|
||||
{
|
||||
TEST_CTX(ctx);
|
||||
ctx->key.down_mask &= ~key;
|
||||
}
|
||||
|
||||
|
||||
// append in input text
|
||||
void ug_input_text(ug_ctx_t *ctx, const char *text)
|
||||
{
|
||||
TEST_CTX(ctx);
|
||||
int size = strlen(ctx->input_text);
|
||||
int len = strlen(text);
|
||||
if ((unsigned long)(size+len) >= sizeof(ctx->input_text)) {
|
||||
warn("__FUNCTION__" "Input text exceeds context buffer");
|
||||
return;
|
||||
ug_container_t *c = NULL;
|
||||
for (int i = 0; i < ctx->container_stack.idx; i++) {
|
||||
if (ctx->container_stack.items[i].id == id) {
|
||||
c = &(ctx->container_stack.items[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
memcpy(ctx->input_text + size, text, len);
|
||||
// if the container was not already there allocate a new one
|
||||
if (!c) {
|
||||
if(ctx->container_stack.idx >= ctx->container_stack.size)
|
||||
grow(ctx->container_stack);
|
||||
c = &(ctx->container_stack.items[ctx->container_stack.idx++]);
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
/*=============================================================================*
|
||||
* BEGIN AND END *
|
||||
*=============================================================================*/
|
||||
|
||||
#undef TEST_CTX
|
||||
#define TEST_CTX(ctx) { \
|
||||
if (!ctx) { \
|
||||
warn("__FUNCTION__:" "trying to use a null context"); \
|
||||
return -1; \
|
||||
} \
|
||||
}
|
||||
|
||||
int ug_begin(ug_ctx_t *ctx)
|
||||
// update the container dimensions and position according to the context information,
|
||||
// also handle resizing, moving, ect. if allowed by the container
|
||||
static void update_container(ug_ctx_t *ctx, ug_container_t *cnt)
|
||||
{
|
||||
// it is the beginning of a new frame
|
||||
TEST_CTX(ctx);
|
||||
// if the container was just initialized the unit might not be pixels, this
|
||||
// is a problem since all mouse events are in pixels and convering back
|
||||
// and forth accumulates error and in heavy on the cpu
|
||||
if (cnt->unit != UG_UNIT_PX) {
|
||||
// FIXME: this takes the unit from the context but it should be fine
|
||||
cnt->rect = rect_to_px(ctx, cnt->rect);
|
||||
cnt->unit = UG_UNIT_PX;
|
||||
}
|
||||
|
||||
// reset the command stack
|
||||
ctx->e_idx = 0;
|
||||
// next frame
|
||||
ctx->frame++;
|
||||
// update the mouse delta
|
||||
ctx->mouse.delta.x = ctx->mouse.pos.x - ctx->mouse.last_pos.x;
|
||||
ctx->mouse.delta.y = ctx->mouse.pos.y - ctx->mouse.last_pos.y;
|
||||
// TODO: other stuff
|
||||
// recalculate position
|
||||
// FIXME: this is the right place to do some optimization, what if the
|
||||
// context didn't change?
|
||||
// the absoulute position of the container
|
||||
ug_rect_t rect_abs = cnt->rect;
|
||||
|
||||
// 0 -> take all the space, <0 -> take absolute
|
||||
if (rect_abs.w == 0) rect_abs.w = ctx->size.w;
|
||||
else if (rect_abs.w < 0) rect_abs.w = -rect_abs.w;
|
||||
if (rect_abs.h == 0) rect_abs.h = ctx->size.h;
|
||||
else if (rect_abs.h < 0) rect_abs.h = -rect_abs.h;
|
||||
|
||||
// <0 -> relative to the right margin
|
||||
if (rect_abs.x < 0) rect_abs.x = ctx->size.x - rect_abs.w + rect_abs.x;
|
||||
if (rect_abs.y < 0) rect_abs.y = ctx->size.y - rect_abs.h + rect_abs.y;
|
||||
|
||||
// if we had focus the frame before, then do shit
|
||||
if (ctx->hover.cnt == cnt->id) {
|
||||
// TODO: do stuff
|
||||
}
|
||||
|
||||
// push the appropriate rectangles to the drawing stack
|
||||
// TODO: ^ This ^
|
||||
}
|
||||
|
||||
|
||||
// a floating container can be placed anywhere and can be resized, acts like a
|
||||
// window inside another window
|
||||
int ug_container_floating(ug_ctx_t *ctx, const char *name, ug_rect_t rect)
|
||||
{
|
||||
TEST_CTX(ctx);
|
||||
// TODO: verify rect
|
||||
|
||||
ug_id_t id = name ? hash(name, strlen(name)) : hash(&rect, sizeof(ug_rect_t));
|
||||
ug_container_t *cnt = get_container(ctx, id);
|
||||
|
||||
if (cnt->id) {
|
||||
// nothing? maybe we can skip updating all dimensions and stuff
|
||||
} else {
|
||||
cnt->id = id;
|
||||
cnt->max_size = max_size;
|
||||
cnt->rect = rect;
|
||||
cnt->unit = ctx->unit;
|
||||
cnt->flags = UG_CNT_MOVABLE |
|
||||
UG_CNT_RESIZABLE |
|
||||
UG_CNT_SCROLL_X |
|
||||
UG_CNT_SCROLL_Y ;
|
||||
}
|
||||
|
||||
update_container(ctx, cnt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int ug_end(ug_ctx_t *ctx)
|
||||
{
|
||||
// end of a frame, check that all went well
|
||||
TEST_CTX(ctx);
|
||||
|
||||
// reset the inputs
|
||||
ctx->mouse.press_mask = 0;
|
||||
ctx->mouse.scroll_delta = (struct ug_vec2){0};
|
||||
ctx->mouse.last_pos = ctx->mouse.pos;
|
||||
ctx->key.press_mask = 0;
|
||||
ctx->input_text[0] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*=============================================================================*
|
||||
* UI ELEMENTS *
|
||||
*=============================================================================*/
|
||||
|
||||
#undef TEST_CTX
|
||||
#define TEST_CTX(ctx) { \
|
||||
if (!ctx) { \
|
||||
warn("__FUNCTION__:" "trying to use a null context"); \
|
||||
return 0; \
|
||||
} \
|
||||
}
|
||||
|
||||
// Slider element, a rectangle with some text and another rectangle inside
|
||||
// When used with a mouse the slider moves to the clicked area and stat gets
|
||||
// updated, in that case a non-zero value gets returned
|
||||
int ug_slider(ug_ctx_t *ctx, float *stat, float min, float max)
|
||||
{
|
||||
TEST_CTX(ctx);
|
||||
ug_draw_rect(ctx)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue