rewrite
This commit is contained in:
parent
14def875d6
commit
894b2763c0
381
ugui.c
381
ugui.c
@ -1,23 +1,59 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <err.h>
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <err.h>
|
||||||
|
|
||||||
#include "ugui.h"
|
#include "ugui.h"
|
||||||
|
|
||||||
|
|
||||||
#define SALT 0xbabb0cac
|
#define SALT 0xbabb0cac
|
||||||
#define DEF_SCALE 1.0
|
#define DEF_SCALE 1.0
|
||||||
#define DEF_DPI 96.0
|
#define DEF_PPI 96.0
|
||||||
#define E_STACK_STEP (1024 * 128)
|
#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
|
// 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)
|
if (!size)
|
||||||
return 0;
|
return 0;
|
||||||
unsigned int hash = SALT;
|
ug_id_t hash = SALT;
|
||||||
unsigned char *v = (unsigned char *)data;
|
unsigned char *v = (unsigned char *)data;
|
||||||
|
|
||||||
for (; size; size--) {
|
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)
|
const ug_style_t *s = ctx->style;
|
||||||
err(EXIT_FAILURE, "__FUNCTION__:" "Cannot grow null context");
|
// TODO: do this shit
|
||||||
ctx->e_size += E_STACK_STEP;
|
(void)s;
|
||||||
ctx->e_stack = realloc(ctx->e_stack, ctx->e_size);
|
|
||||||
if (!ctx->e_stack)
|
|
||||||
err(errno, "__FUNCTION__:" "Could not grow stack: %s", strerror(errno));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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()
|
// creates a new context, fills with default values, ctx is ready for ug_start()
|
||||||
ug_ctx_t *ug_new_ctx(void)
|
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));
|
memset(ctx, 0, sizeof(ug_ctx_t));
|
||||||
|
|
||||||
ctx->scale = DEF_SCALE;
|
ctx->scale = DEF_SCALE;
|
||||||
ctx->dpi = DEF_DPI;
|
ctx->ppi = DEF_PPI;
|
||||||
ctx->ppm = TO_PPM(DEF_SCALE, DEF_DPI);
|
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;
|
return ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,173 +135,165 @@ void ug_free_ctx(ug_ctx_t *ctx)
|
|||||||
warn("__FUNCTION__:" "Trying to free a null context");
|
warn("__FUNCTION__:" "Trying to free a null context");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ctx->e_stack) {
|
|
||||||
warn("__FUNCTION__:" "Context has null element stack");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
free(ctx->e_stack);
|
// TODO: free stacks
|
||||||
free(ctx);
|
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
|
#define TEST_CTX(ctx) { if (!ctx) return -1; }
|
||||||
int ug_update_ctx(ug_ctx_t *ctx,
|
|
||||||
unsigned int window_id,
|
int ug_ctx_set_displayinfo(ug_ctx_t *ctx, float scale, float ppi)
|
||||||
float scale,
|
|
||||||
float dpi,
|
|
||||||
float ppm
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
if (!ctx)
|
TEST_CTX(ctx);
|
||||||
|
if (scale <= 0 || ppi < 20.0)
|
||||||
return -1;
|
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)
|
return 0;
|
||||||
ctx->scale = scale;
|
}
|
||||||
if (dpi)
|
|
||||||
ctx->dpi = dpi;
|
|
||||||
if (!ppm)
|
int ug_ctx_set_drawableregion(ug_ctx_t *ctx, ug_vec2_t size)
|
||||||
ctx->ppm = TO_PPM(ctx->dpi, ctx->scale);
|
{
|
||||||
else
|
TEST_CTX(ctx);
|
||||||
ctx->ppm = ppm;
|
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;
|
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);
|
ug_container_t *c = NULL;
|
||||||
ctx->mouse.pos = (struct ug_vec2){ .x = x, .y = y };
|
for (int i = 0; i < ctx->container_stack.idx; i++) {
|
||||||
}
|
if (ctx->container_stack.items[i].id == id) {
|
||||||
|
c = &(ctx->container_stack.items[i]);
|
||||||
void ug_input_mousedown(ug_ctx_t *ctx, int x, int y, unsigned char btn)
|
break;
|
||||||
{
|
}
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*=============================================================================*
|
// update the container dimensions and position according to the context information,
|
||||||
* BEGIN AND END *
|
// also handle resizing, moving, ect. if allowed by the container
|
||||||
*=============================================================================*/
|
static void update_container(ug_ctx_t *ctx, ug_container_t *cnt)
|
||||||
|
|
||||||
#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)
|
|
||||||
{
|
{
|
||||||
// it is the beginning of a new frame
|
// if the container was just initialized the unit might not be pixels, this
|
||||||
TEST_CTX(ctx);
|
// 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
|
// recalculate position
|
||||||
ctx->e_idx = 0;
|
// FIXME: this is the right place to do some optimization, what if the
|
||||||
// next frame
|
// context didn't change?
|
||||||
ctx->frame++;
|
// the absoulute position of the container
|
||||||
// update the mouse delta
|
ug_rect_t rect_abs = cnt->rect;
|
||||||
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;
|
// 0 -> take all the space, <0 -> take absolute
|
||||||
// TODO: other stuff
|
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;
|
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)
|
|
||||||
}
|
|
495
ugui.h
495
ugui.h
@ -1,243 +1,121 @@
|
|||||||
#ifndef _UGUI_H
|
#ifndef _UG_HEADER
|
||||||
#define _UGUI_H
|
#define _UG_HEADER
|
||||||
|
|
||||||
|
#define UG_STACK(T) struct { T *items; int idx; int size; }
|
||||||
|
#define BIT(n) (1 << n)
|
||||||
|
#define RGBA_FORMAT(x) { .a=x&0xff, .b=(x>>8)&0xff, .g=(x>>16)&0xff, .r=(x>>24)&0xff }
|
||||||
|
#define RGB_FORMAT(x) { .a=0xff, .b=x&0xff, .g=(x>>8)&0xff, .r=(x>>16)&0xff }
|
||||||
|
#define SIZE_PX(x) { .size=x, .unit=UG_UNIT_PX }
|
||||||
|
#define SIZE_MM(x) { .size=x, .unit=UG_UNIT_MM }
|
||||||
|
#define SIZE_PT(x) { .size=x, .unit=UG_UNIT_PT }
|
||||||
|
|
||||||
// Macros
|
// basic types
|
||||||
#define UG_STACK(T) struct { T *items; int idx; int size; }
|
typedef unsigned int ug_id_t;
|
||||||
|
typedef struct { union {int x, w;}; union {int y, h;}; } ug_vec2_t;
|
||||||
|
typedef struct { int x, y, w, h; } ug_rect_t;
|
||||||
|
typedef struct { unsigned char a, b, g, r; } ug_color_t;
|
||||||
|
typedef struct { int size, unit; } ug_size_t;
|
||||||
|
|
||||||
/* Point Coordinate */
|
typedef enum {
|
||||||
// coordinate type, or how it is measured
|
UG_UNIT_PX = 0,
|
||||||
enum {
|
UG_UNIT_MM,
|
||||||
UG_CTYPE_PX = 0x0,
|
UG_UNIT_PT,
|
||||||
UG_CTYPE_MM = 0x1,
|
} ug_unit_t;
|
||||||
UG_CTYPE_REL = 0x2,
|
|
||||||
};
|
|
||||||
|
|
||||||
// coordinate anchor, or where it refers
|
|
||||||
enum {
|
|
||||||
UG_CANCHOR_TOP = 0x0, // x coordinates
|
|
||||||
UG_CANCHOR_LEFT = 0x0,
|
|
||||||
UG_CANCHOR_BOTTOM = 0x1, // y coordinates
|
|
||||||
UG_CANCHOR_RIGHT = 0x1,
|
|
||||||
UG_CANCHOR_VCENTER = 0x2, // for positions it should be centered
|
|
||||||
UG_CANCHOR_HCENTER = 0x2,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct _ug_pt_flags {
|
|
||||||
unsigned long int _:56;
|
|
||||||
unsigned long int t:4;
|
|
||||||
unsigned long int a:4;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
// container type, a container is an entity that contains layouts, a container has
|
||||||
|
// a haight a width and their maximum values, in essence a container is a rectangular
|
||||||
|
// area that can be resized and sits somewhere on the drawable region
|
||||||
|
// the z index of a container is determined by it's position on the stack
|
||||||
typedef struct {
|
typedef struct {
|
||||||
// first coordinate
|
ug_id_t id;
|
||||||
union {
|
ug_unit_t unit;
|
||||||
union {
|
|
||||||
int x;
|
|
||||||
struct _ug_pt_flags fx;
|
|
||||||
};
|
|
||||||
union {
|
|
||||||
int w;
|
|
||||||
struct _ug_pt_flags fw;
|
|
||||||
};
|
|
||||||
union {
|
|
||||||
int m;
|
|
||||||
struct _ug_pt_flags fm;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
// second coordinate
|
|
||||||
union {
|
|
||||||
union {
|
|
||||||
int y;
|
|
||||||
struct _ug_pt_flags fy;
|
|
||||||
};
|
|
||||||
union {
|
|
||||||
int h;
|
|
||||||
struct _ug_pt_flags fh;
|
|
||||||
};
|
|
||||||
union {
|
|
||||||
int a;
|
|
||||||
struct _ug_pt_flags fa;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
} ug_coord_t;
|
|
||||||
|
|
||||||
// TODO: define stroke style (f.s)
|
|
||||||
typedef struct {
|
|
||||||
int size;
|
|
||||||
struct {
|
|
||||||
// type of size
|
|
||||||
unsigned char t:4;
|
|
||||||
// stroke style
|
|
||||||
unsigned char s:4;
|
|
||||||
} f;
|
|
||||||
} ug_stroke_t;
|
|
||||||
//---//
|
|
||||||
|
|
||||||
/* RGBA Color type */
|
|
||||||
typedef struct {
|
|
||||||
unsigned char a;
|
|
||||||
unsigned char b;
|
|
||||||
unsigned char g;
|
|
||||||
unsigned char r;
|
|
||||||
} ug_color_t;
|
|
||||||
//---//
|
|
||||||
|
|
||||||
/* simple position structure */
|
|
||||||
struct ug_vec2 {
|
|
||||||
int x, y;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
int x, y;
|
|
||||||
int w, h;
|
|
||||||
} ug_rect_t;
|
|
||||||
//---//
|
|
||||||
|
|
||||||
/* Mouse button mask */
|
|
||||||
enum ug_mouse_btn {
|
|
||||||
UG_MOUSEBTN_LEFT = 1,
|
|
||||||
UG_MOUSEBTN_MIDDLE = 2,
|
|
||||||
UG_MOUSEBTN_RIGHT = 4,
|
|
||||||
UG_MOUSEBTN_4 = 8,
|
|
||||||
UG_MOUSEBTN_5 = 16,
|
|
||||||
};
|
|
||||||
//---//
|
|
||||||
|
|
||||||
/* event structure */
|
|
||||||
typedef enum {
|
|
||||||
UG_EV_WINDOWMOVE,
|
|
||||||
UG_EV_WINDOWRESIZE,
|
|
||||||
UG_EV_WINDOWSHOW,
|
|
||||||
UG_EV_MOUSEMOVE,
|
|
||||||
UG_EV_MOUSEDOWN,
|
|
||||||
UG_EV_MOUSEUP,
|
|
||||||
UG_EV_KEYDOWN,
|
|
||||||
UG_EV_KEYUP,
|
|
||||||
} ug_event_type_t;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
ug_event_type_t type;
|
|
||||||
union {
|
|
||||||
int x;
|
|
||||||
int w;
|
|
||||||
unsigned int code;
|
|
||||||
};
|
|
||||||
union {
|
|
||||||
int y;
|
|
||||||
int h;
|
|
||||||
unsigned int mask;
|
|
||||||
};
|
|
||||||
} ug_event_t;
|
|
||||||
//---//
|
|
||||||
|
|
||||||
/* Primitive element type */
|
|
||||||
typedef enum {
|
|
||||||
UG_E_RECT = 0,
|
|
||||||
UG_E_TRIANGLE,
|
|
||||||
UG_E_DOT,
|
|
||||||
UG_E_LINE,
|
|
||||||
UG_E_ARC,
|
|
||||||
UG_E_TEXT,
|
|
||||||
UG_E_SPRITE,
|
|
||||||
} ug_element_type_t;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
ug_coord_t size;
|
|
||||||
ug_coord_t pos;
|
|
||||||
ug_color_t color;
|
|
||||||
} ug_rect_t;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
ug_coord_t size;
|
|
||||||
ug_coord_t pos;
|
|
||||||
ug_color_t color;
|
|
||||||
} ug_triangle_t;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
ug_stroke_t stroke;
|
|
||||||
ug_coord_t pos;
|
|
||||||
ug_color_t color;
|
|
||||||
} ug_dot_t;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
ug_stroke_t stroke;
|
|
||||||
ug_coord_t vert[2];
|
|
||||||
ug_color_t color;
|
|
||||||
} ug_line_t;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
ug_stroke_t stroke;
|
|
||||||
ug_coord_t vert[3];
|
|
||||||
ug_color_t color;
|
|
||||||
} ug_arc_t;
|
|
||||||
|
|
||||||
// draw text from view[start] to view[end]
|
|
||||||
typedef struct {
|
|
||||||
ug_stroke_t stroke;
|
|
||||||
ug_coord_t pos;
|
|
||||||
ug_color_t color;
|
|
||||||
const char *view;
|
|
||||||
int start;
|
|
||||||
int end;
|
|
||||||
} ug_text_t;
|
|
||||||
|
|
||||||
// all sprites have to be mapped into memory, map is an array of width x height
|
|
||||||
// bytes, the type is specified by the user
|
|
||||||
typedef struct {
|
|
||||||
ug_coord_t pos;
|
|
||||||
void *map;
|
|
||||||
int width;
|
|
||||||
int height;
|
|
||||||
} ug_sprite_t;
|
|
||||||
|
|
||||||
|
|
||||||
// data is the actual element data, it has to be cast to the right element specified
|
|
||||||
// by type and is variable size so that no memory is wasted, size is the size in
|
|
||||||
// bytes of data + id + size + type
|
|
||||||
typedef struct {
|
|
||||||
ug_element_type_t type;
|
|
||||||
unsigned int size;
|
|
||||||
unsigned int id;
|
|
||||||
unsigned char data[];
|
|
||||||
} ug_element_t;
|
|
||||||
//---//
|
|
||||||
|
|
||||||
/* Layout region */
|
|
||||||
typedef struct {
|
|
||||||
ug_rect_t rect;
|
ug_rect_t rect;
|
||||||
// FIXME: is this needed?
|
ug_vec2_t max_size;
|
||||||
unsigned int id;
|
unsigned int flags;
|
||||||
// indentation level, used in trees
|
} ug_container_t;
|
||||||
// FIXME: is this needed?
|
|
||||||
unsigned int indent;
|
|
||||||
} ug_layout_region_t;
|
|
||||||
//---//s
|
|
||||||
|
|
||||||
/* Global context */
|
// the container flags
|
||||||
// one context per phisical window
|
enum {
|
||||||
// a phisical window can be anything, but here it basically represents the true
|
UG_CNT_MOVABLE = BIT(0), // can be moved
|
||||||
// surface where things are drawn into, a window has a scale, dpi and ppm
|
UG_CNT_RESIZABLE = BIT(1), // can be resized
|
||||||
// (pixel per mm), only the latter is truly needed, the user sets these variables
|
UG_CNT_SCROLL_X = BIT(2), // can have horizontal scrolling
|
||||||
// and has to take care of updating them when needed
|
UG_CNT_SCROLL_Y = BIT(3), // can have vertical scrolling
|
||||||
|
};
|
||||||
|
|
||||||
|
// style, defines default height, width, color, margins, borders, etc
|
||||||
|
// all dimensions should be taken as a reference when doing the layout since
|
||||||
|
// ultimately it's the layout that decides them. For example when deciding how to
|
||||||
|
// allocate space one can say that the default size of a region that allocates a
|
||||||
|
// slider has the style's default dimensions for a slider
|
||||||
typedef struct {
|
typedef struct {
|
||||||
unsigned int window_id;
|
struct {
|
||||||
float scale, dpi, ppm;
|
ug_color_t color, alt_color;
|
||||||
// the user pushes the things that he wants to draw onto the element stack,
|
ug_size_t size, alt_size;
|
||||||
// which contains all the primitives needed to draw the wanted element
|
} text;
|
||||||
UG_STACK(unsigned char) e_stack;
|
ug_color_t bg_color;
|
||||||
// the element which has focus
|
// base sizes for all elements, some elements should be different, like
|
||||||
unsigned int focus_id;
|
// buttons and other things that need to stand out
|
||||||
// the element that we are hovering
|
struct {
|
||||||
unsigned int hover_id;
|
ug_size_t width, height, border_width;
|
||||||
|
ug_color_t color, hover_color, active_color;
|
||||||
|
} base;
|
||||||
|
|
||||||
|
// a button should stand out, hence the different colors
|
||||||
|
struct {
|
||||||
|
ug_color_t bg_color, hover_color, active_color;
|
||||||
|
} button;
|
||||||
|
|
||||||
|
// a checkbox should be smaller than a button
|
||||||
|
struct {
|
||||||
|
ug_size_t width, height, tick_size;
|
||||||
|
ug_color_t tick_color;
|
||||||
|
} checkbox;
|
||||||
|
|
||||||
|
// a slider can be thinner and generally wider than a button
|
||||||
|
struct {
|
||||||
|
ug_size_t width, height;
|
||||||
|
} slider;
|
||||||
|
|
||||||
|
// the text color, dimension and the background of a text display can be
|
||||||
|
// different
|
||||||
|
struct {
|
||||||
|
ug_color_t text_color, bg_color;
|
||||||
|
ug_size_t text_size;
|
||||||
|
} textdisplay;
|
||||||
|
|
||||||
|
} ug_style_t;
|
||||||
|
|
||||||
|
// context
|
||||||
|
typedef struct {
|
||||||
|
// some style information
|
||||||
|
const ug_style_t *style;
|
||||||
|
// style_px is a style cache where all measurements are already in pixels
|
||||||
|
const ug_style_t *style_px;
|
||||||
|
// ppi: pixels per inch
|
||||||
|
// ppm: pixels per millimeter
|
||||||
|
// ppd: pixels per dot
|
||||||
|
float scale, ppi, ppm, ppd;
|
||||||
|
// containers need to know how big the "main container" is so that all
|
||||||
|
// the relative positioning work
|
||||||
|
ug_vec2_t size;
|
||||||
|
// which context and element we are hovering
|
||||||
|
struct {
|
||||||
|
ug_id_t cnt, elem;
|
||||||
|
} hover;
|
||||||
|
// the id of the "active" element, active means different things for
|
||||||
|
// different elements, for exaple active for a button means to be pressed,
|
||||||
|
// and for a text box it means to be focused
|
||||||
|
ug_id_t active;
|
||||||
// count the frames for fun
|
// count the frames for fun
|
||||||
unsigned long int frame;
|
unsigned long int frame;
|
||||||
|
// current measurement unit
|
||||||
|
ug_unit_t unit;
|
||||||
// mouse data
|
// mouse data
|
||||||
struct {
|
struct {
|
||||||
struct ug_vec2 pos;
|
ug_vec2_t pos;
|
||||||
struct ug_vec2 last_pos;
|
ug_vec2_t last_pos;
|
||||||
struct ug_vec2 delta;
|
ug_vec2_t delta;
|
||||||
struct ug_vec2 scroll_delta;
|
ug_vec2_t scroll_delta;
|
||||||
// down/pressed masks get updated on mousedown, whereas down_mask
|
// down/pressed masks get updated on mousedown, whereas down_mask
|
||||||
// only on mouseup, so the masks differ by the buttons that were
|
// only on mouseup, so the masks differ by the buttons that were
|
||||||
// released
|
// released
|
||||||
@ -252,146 +130,39 @@ typedef struct {
|
|||||||
} key;
|
} key;
|
||||||
// input text buffer
|
// input text buffer
|
||||||
char input_text[32];
|
char input_text[32];
|
||||||
|
// stacks
|
||||||
|
UG_STACK(ug_container_t) container_stack;
|
||||||
} ug_ctx_t;
|
} ug_ctx_t;
|
||||||
//---//
|
|
||||||
|
|
||||||
// creates a new context, fills with default values, ctx is ready for ug_start()
|
|
||||||
|
// context initialization
|
||||||
ug_ctx_t *ug_new_ctx(void);
|
ug_ctx_t *ug_new_ctx(void);
|
||||||
void ug_free_ctx(ug_ctx_t *ctx);
|
void ug_free_ctx(ug_ctx_t *ctx);
|
||||||
// updates the context with user information
|
// updates the context with user information
|
||||||
int ug_update_ctx(ug_ctx_t *ctx,
|
int ug_ctx_set_displayinfo(ug_ctx_t *ctx, float scale, float ppi);
|
||||||
unsigned int window_id,
|
int ug_ctx_set_drawableregion(ug_ctx_t *ctx, ug_vec2_t size);
|
||||||
float scale,
|
int ug_ctx_set_style(ug_ctx_t *ctx, const ug_style_t *style);
|
||||||
float dpi,
|
int ug_ctx_set_unit(ug_ctx_t *ctx, ug_unit_t unit);
|
||||||
float ppm
|
|
||||||
);
|
|
||||||
|
|
||||||
// register inputs
|
|
||||||
void ug_input_mousedown(ug_ctx_t *ctx, int x, int y, unsigned char btn);
|
|
||||||
void ug_input_mouseup(ug_ctx_t *ctx, int x, int y, unsigned char btn);
|
|
||||||
void ug_input_mousemove(ug_ctx_t *ctx, int x, int y);
|
|
||||||
void ug_input_scroll(ug_ctx_t *ctx, int x, int y);
|
|
||||||
void ug_input_keydown(ug_ctx_t *ctx, int key);
|
|
||||||
void ug_input_keyup(ug_ctx_t *ctx, int key);
|
|
||||||
void ug_input_text(ug_ctx_t *ctx, const char *text);
|
|
||||||
|
|
||||||
// between begin and end the user draws
|
|
||||||
int ug_begin(ug_ctx_t *ctx);
|
|
||||||
int ug_end(ug_ctx_t *ctx);
|
|
||||||
|
|
||||||
// layout control
|
|
||||||
// Split the layout vertically and horizontally, n times each time with the
|
|
||||||
// supplied width/height
|
|
||||||
|
|
||||||
/* Window space
|
|
||||||
* +-----------------------------+
|
|
||||||
* | |
|
|
||||||
* | |
|
|
||||||
* | |
|
|
||||||
* | |
|
|
||||||
* | |
|
|
||||||
* | |
|
|
||||||
* | |
|
|
||||||
* | |
|
|
||||||
* | |
|
|
||||||
* | |
|
|
||||||
* | |
|
|
||||||
* +-----------------------------+
|
|
||||||
*
|
|
||||||
* ug_layout_vsplit(ctx, 2, (int[]){10, 20});
|
|
||||||
* create two vertical zones
|
|
||||||
* +-----------------------------+
|
|
||||||
* | } 10px |
|
|
||||||
* +-----------------------------+
|
|
||||||
* | |> 20px |
|
|
||||||
* | | |
|
|
||||||
* +-----------------------------+
|
|
||||||
* | |
|
|
||||||
* | |
|
|
||||||
* | remainder |
|
|
||||||
* | |
|
|
||||||
* | |
|
|
||||||
* | |
|
|
||||||
* +-----------------------------+
|
|
||||||
*
|
|
||||||
* ug_layout_hsplit(ctx, 3, NULL);
|
|
||||||
* equally spaced divisions
|
|
||||||
* +-----------------------------+
|
|
||||||
* | | | |
|
|
||||||
* +-----------------------------+
|
|
||||||
* | |> 20px |
|
|
||||||
* | | |
|
|
||||||
* +-----------------------------+
|
|
||||||
* | |
|
|
||||||
* | |
|
|
||||||
* | remainder |
|
|
||||||
* | |
|
|
||||||
* | |
|
|
||||||
* | |
|
|
||||||
* +-----------------------------+
|
|
||||||
*
|
|
||||||
* ug_button(ctx, "ciao");
|
|
||||||
* ug_layout_next(ctx); // skip second square
|
|
||||||
* ug_button(ctx, "quit");
|
|
||||||
* float val = 0.3
|
|
||||||
* ug_slider(ctx, &val, 0, 1);
|
|
||||||
* ug_text(ctx, "lorem ipsum\ndolor et");
|
|
||||||
*
|
|
||||||
* +-----------------------------+
|
|
||||||
* | ciao | | quit |
|
|
||||||
* +-----------------------------+
|
|
||||||
* | # 0.3 |
|
|
||||||
* | # |
|
|
||||||
* +-----------------------------+
|
|
||||||
* | lorem ipsum |
|
|
||||||
* | dolor et |
|
|
||||||
* | |
|
|
||||||
* | |
|
|
||||||
* | |
|
|
||||||
* | |
|
|
||||||
* +-----------------------------+
|
|
||||||
*
|
|
||||||
* ug_layout_popup(ctx, {100x50, centrato});
|
|
||||||
* ug_layout_vsplit(ctx, 1, NULL);
|
|
||||||
* ug_button(ctx, "btn1");
|
|
||||||
* ug_button(ctx, "btn2");
|
|
||||||
*
|
|
||||||
* +-----------------------------+
|
|
||||||
* | ciao | | quit |
|
|
||||||
* +-----------------------------+
|
|
||||||
* | # 0.3 |
|
|
||||||
* | # +----------+ |
|
|
||||||
* +--------| btn1 |---------+
|
|
||||||
* | lorem i| | |
|
|
||||||
* | dolor e+----------+ |
|
|
||||||
* | | btn2 | |
|
|
||||||
* | | | |
|
|
||||||
* | +----------+ |
|
|
||||||
* | |
|
|
||||||
* | |
|
|
||||||
* +-----------------------------+
|
|
||||||
*/
|
|
||||||
int ug_layout_vsplit(ug_ctx_t *ctx, int n, const int *heights);
|
|
||||||
int ug_layout_hsplit(ug_ctx_t *ctx, int n, const int *widths);
|
|
||||||
// popup layout, create a rectangle that sits over the view and has a higher
|
|
||||||
// z-index, this allows it to have any position and have focus priority
|
|
||||||
// creating a popup selects it as current layout
|
|
||||||
int ug_layout_popup(ug_ctx_t *ctx, ug_rect_t rect);
|
|
||||||
// get the next rectangular region in the layout context
|
|
||||||
ug_rect_t ug_layout_next(ug_ctx_t *ctx);
|
|
||||||
// get the current layout rectangle area, but do not pop it (select the next)
|
|
||||||
// this is useful to get information about the area's width height and position
|
|
||||||
ug_rect_t ug_layout_get_current(ug_ctx_t *ctx);
|
|
||||||
// set the unit of the dimensions, in the context all units are pixels but
|
|
||||||
// when regions are defined units can be in pixels, millimiters, etc
|
|
||||||
int ui_layout_set_mm(ug_ctx_t *ctx);
|
|
||||||
int ui_layout_set_px(ug_ctx_t *ctx);
|
|
||||||
|
|
||||||
// ui elements
|
|
||||||
int ug_slider(ug_ctx_t *ctx, float *stat, float min, float max);
|
|
||||||
|
|
||||||
|
|
||||||
// un-define macros that we don't want the user to access
|
// define containers, name is used as a salt for the container id, all sizes are
|
||||||
|
// the default ones, resizing is done automagically with internal state
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
// like a floating container but cannot be resized
|
||||||
|
int ug_container_popup(ug_ctx_t *ctx, const char *name, ug_rect_t rect);
|
||||||
|
// a menu bar is a container of fixed height, cannot be resized and sits at the
|
||||||
|
// top of the window
|
||||||
|
int ug_container_menu_bar(ug_ctx_t *ctx, const char *name, int height);
|
||||||
|
// a sidebar is a variable size container anchored to one side of the window
|
||||||
|
int ug_container_sidebar(ug_ctx_t *ctx, const char *name, int width);
|
||||||
|
// a body is a container that scales with the window, sits at it's center and cannot
|
||||||
|
// be resized
|
||||||
|
int ug_container_body(ug_ctx_t *ctx, const char *name);
|
||||||
|
|
||||||
#undef UG_STACK
|
#undef UG_STACK
|
||||||
|
#undef BIT
|
||||||
|
|
||||||
#endif
|
#endif
|
Loading…
Reference in New Issue
Block a user