diff --git a/src/immdraw.h b/src/immdraw.h index 528146a..51caf88 100644 --- a/src/immdraw.h +++ b/src/immdraw.h @@ -4,18 +4,20 @@ #include"k3font.h" #include"k3menu.h" +extern uint16_t GameWndH; + static inline void immdraw_fill_rect(int16_t x, int16_t y, int16_t w, int16_t h, float r, float g, float b, float a, float borderRadius) { k3BatchAdd(NULL, (struct k3RectF) {0, 0, 1, 1}, (struct k3RectF) { - x, y, + x, GameWndH - y - h, w, h }, 0, (vec4) {r, g, b, a}, borderRadius); } static inline void immdraw_font_draw(struct k3Font *font, int16_t x, int16_t y, int16_t w, float sz, size_t len, const char *txt, int alignment, float r, float g, float b, float a) { + struct k3RectF txtsz; + k3FontSz(font, sz, txt, &txtsz); + if(alignment != k3M_ALIGN_LEFT) { - struct k3RectF txtsz; - k3FontSz(font, sz, txt, &txtsz); - if(alignment == k3M_ALIGN_CENTER) { x += w / 2.0f - txtsz.w / 2.0f; } else { @@ -23,5 +25,51 @@ static inline void immdraw_font_draw(struct k3Font *font, int16_t x, int16_t y, } } - k3FontDraw(font, x, y, sz, txt, (vec4) {r, g, b, a}); + k3FontDraw(font, x, GameWndH - y - sz, sz, txt, (vec4) {r, g, b, a}); +} +static inline void immdraw_font_size(struct k3Font *font, float sz, const char *txt, int16_t aabb[2]) { + struct k3RectF txtsz; + k3FontSz(font, sz, txt, &txtsz); + + aabb[0] = ceilf(txtsz.w); + aabb[1] = ceilf(txtsz.h); +} + +static inline void immdraw_image_draw(k3MImageData *data, int16_t x, int16_t y, int16_t w, int16_t h, float r, float g, float b, float a) { + struct k3Tex *tex = *data; + k3BatchAdd(tex, (struct k3RectF) {0, 0, 1, 1}, (struct k3RectF) {x, GameWndH - y - h, w, h}, 0, (vec4) {r, g, b, a}, 0); +} + +static int16_t crop_aabb[4] = {-1, -1, -1, -1}; +static inline void immdraw_crop_get(int16_t aabb[4]) { + memcpy(aabb, crop_aabb, sizeof(crop_aabb)); +} +static inline void immdraw_crop_set(int16_t aabb[4]) { + memcpy(crop_aabb, aabb, sizeof(crop_aabb)); + + if(aabb[0] == -1 && aabb[1] == -1 && aabb[2] == -1 && aabb[3] == -1) { + glDisable(GL_SCISSOR_TEST); + } else { + glEnable(GL_SCISSOR_TEST); + glScissor(aabb[0], GameWndH - aabb[3], aabb[2] - aabb[0], aabb[3] - aabb[1]); + } +} +static inline void immdraw_crop_add(int16_t aabb[4]) { + int16_t newaabb[4]; + memcpy(newaabb, crop_aabb, sizeof(newaabb)); + + if(aabb[0] > newaabb[0] || newaabb[0] == -1) { + newaabb[0] = aabb[0]; + } + if(aabb[1] > newaabb[1] || newaabb[1] == -1) { + newaabb[1] = aabb[1]; + } + if(aabb[2] < newaabb[2] || newaabb[2] == -1) { + newaabb[2] = aabb[2]; + } + if(aabb[3] < newaabb[3] || newaabb[3] == -1) { + newaabb[3] = aabb[3]; + } + + immdraw_crop_set(newaabb); } diff --git a/src/k3_internal.h b/src/k3_internal.h index da34da4..6b90155 100644 --- a/src/k3_internal.h +++ b/src/k3_internal.h @@ -14,6 +14,8 @@ #define GL_FROM_K3ARBFP(k3m) ((GLuint) (uintptr_t) (k3m)) #define GL_FROM_K3GLSL(k3m) ((GLuint) (uintptr_t) (k3m)) +extern bool k3IsCore; + struct k3Tex { GLuint tex; int cubemap; diff --git a/src/k3batch.c b/src/k3batch.c index f3cdd5d..c696843 100644 --- a/src/k3batch.c +++ b/src/k3batch.c @@ -2,6 +2,7 @@ #include"gl.h" #include +#include"k3_internal.h" struct S { struct k3RectF src; @@ -19,6 +20,10 @@ static struct S *S; static struct k3GLSLP *coreProg; static GLuint coreVBO; +static GLuint coreUResolution; + +static float ResolutionX; +static float ResolutionY; void k3BatchInit() { if(k3IsCore) { @@ -31,11 +36,12 @@ void k3BatchInit() { "out vec2 v_uv;\n" "out vec4 v_color;\n" "out vec2 v_size;\n" + "uniform vec2 u_resolution;\n" "void main() {\n" " v_uv = a_uv;\n" " v_color = a_color;\n" " v_size = a_size;\n" - " gl_Position = vec4(vec2(a_pos.x / 3600.0, a_pos.y / 2025.0) * 2.0 - 1.0, 0.0, 1.0);\n" + " gl_Position = vec4(vec2(a_pos.x / u_resolution.x, a_pos.y / u_resolution.y) * 2.0 - 1.0, 0.0, 1.0);\n" "}\n" , NULL), k3ShaderGLSLF( "#version 330\n" @@ -58,6 +64,8 @@ void k3BatchInit() { "}\n" , NULL), NULL); + coreUResolution = k3ProgramGetUId(coreProg, "u_resolution"); + glGenBuffers(1, &coreVBO); } } @@ -101,6 +109,7 @@ void k3BatchFlush() { } glDisable(GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); if(GLAD_GL_ARB_vertex_program) { glDisable(GL_VERTEX_PROGRAM_ARB); @@ -124,6 +133,7 @@ void k3BatchFlush() { if(k3IsCore) { glUseProgram((GLuint) coreProg); + glUniform2f(coreUResolution, ResolutionX, ResolutionY); float *farr = alloca(SCount * 60 * sizeof(*farr)); @@ -257,3 +267,16 @@ void k3BatchFlush() { SCount = 0; } + +void k3BatchSetResolution(float w, float h) { + ResolutionX = w; + ResolutionY = h; + + if(!k3IsCore) { + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, ResolutionX, 0, ResolutionY, -1, 1); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + } +} diff --git a/src/k3batch.h b/src/k3batch.h index 91b4030..adb2e6c 100644 --- a/src/k3batch.h +++ b/src/k3batch.h @@ -12,3 +12,5 @@ struct k3RectF { void k3BatchAdd(struct k3Tex *tex, struct k3RectF src, struct k3RectF dst, float rot, vec4 color, float borderRadius); void k3BatchFlush(); + +void k3BatchSetResolution(float w, float h); diff --git a/src/k3font.c b/src/k3font.c index 146ada1..ae3edff 100644 --- a/src/k3font.c +++ b/src/k3font.c @@ -98,12 +98,17 @@ static uint32_t read_utf8(const char **txt_) { } void k3FontSz(struct k3Font *this, float sz, const char *txt, struct k3RectF *ret) { - float x = 0, y = 0; + float x = 0, y = sz; while(1) { uint32_t cp = read_utf8(&txt); if(!cp) break; + if(cp == 10) { + x = 0; + y += sz; + } + struct k3FontGlyph *g = k3FontGetGlyph(this, cp); if(!g) continue; @@ -111,6 +116,7 @@ void k3FontSz(struct k3Font *this, float sz, const char *txt, struct k3RectF *re x += g->xadvance * this->lineScale * sz; } ret->w = x; + ret->h = y; } void k3FontDraw(struct k3Font *this, float xStart, float yStart, float sz, const char *txt, vec4 color) { float x = xStart, y = yStart; diff --git a/src/k3menu.c b/src/k3menu.c index dfa7c8d..3ba59bb 100644 --- a/src/k3menu.c +++ b/src/k3menu.c @@ -1,5 +1,6 @@ #include"k3menu.h" +#include"gl.h" #include #include #include @@ -36,6 +37,15 @@ void k3MArrange(struct k3MObj *this) { for(size_t c = 0; c < this->childCount; c++) { k3MArrange(this->children[c]); } + + newev = (struct k3MEvent) { + .code = k3M_EVENT_POST_ARRANGE, + .kind = k3M_EVENTKIND_DIRECT, + + .original = (void*) this, + .target = (void*) this, + }; + k3MEventSend(&newev); } static bool linear_measure(struct k3MEvent *ev, uint8_t *ud) { @@ -57,15 +67,27 @@ static bool linear_measure(struct k3MEvent *ev, uint8_t *ud) { static bool linear_arrange(struct k3MEvent *ev, uint8_t *ud) { struct k3MObj *o = ev->target; - size_t y = o->y; + intmax_t padding[4] = {}; + struct k3MProperty *paddingProp = k3MFindProperty(o, k3M_PROP_PADDING, true); + if(paddingProp) { + for(int s = 0; s < 4; s++) { + if(paddingProp->units[s] == k3M_UNIT_ABSOLUTE) { + padding[s] = paddingProp->f[s]; + } else if(paddingProp->units[s] == k3M_UNIT_PROPORTION && o->parent) { + padding[s] = paddingProp->f[s] * (s % 2 == 0 ? o->h : o->w); + } + } + } + + size_t y = o->y + padding[0]; for(size_t i = 0; i < o->childCount; i++) { struct k3MObj *c = o->children[i]; - c->x = o->x; + c->x = o->x + padding[3]; c->y = y; - c->w = c->wDesired; + c->w = c->wDesired - padding[1] - padding[3]; c->h = c->hDesired; y += c->h; @@ -115,14 +137,25 @@ int k3MRegisterEventHandler_(struct k3MObj *obj, uint16_t evcode, k3MEventHandle } bool k3MEventSend(struct k3MEvent *ev) { + struct k3MObj *target = ev->target; // ev->target can change + + int16_t aabb[4]; + if(ev->code == k3M_EVENT_DRAW && target->crop) { + immdraw_crop_get(aabb); + immdraw_crop_add(&(int16_t[4]) {target->x, target->y, target->x + target->w, target->y + target->h}); + } + + bool status; while(!event_send_nonrecur(ev)) { if(ev->kind == k3M_EVENTKIND_DIRECT) { - return false; + status = false; + goto ret; } else if(ev->kind == k3M_EVENTKIND_BUBBLE) { ev->target = ev->target->parent; if(ev->target == ev->original || !ev->target) { - return false; + status = false; + goto ret; } } else if(ev->kind == k3M_EVENTKIND_CAPTURE) { struct k3MObj *parent = ev->target; @@ -132,19 +165,28 @@ bool k3MEventSend(struct k3MEvent *ev) { ev->target = parent->children[i]; if(k3MEventSend(ev)) { - return true; + status = true; + goto ret; } } } - return false; + status = false; + goto ret; } } - return true; + status = true; + +ret: + if(ev->code == k3M_EVENT_DRAW && target->crop) { + immdraw_crop_set(aabb); + } + + return status; } -static void obj_draw(struct k3MObj *this) { +static void obj_draw_direct(struct k3MObj *this) { struct k3MProperty *borderRadiusProp = k3MFindProperty(this, k3M_PROP_BORDER_RADIUS, false); float borderRadius = borderRadiusProp ? borderRadiusProp->si[0] : 0; @@ -165,15 +207,19 @@ static void obj_draw(struct k3MObj *this) { static bool label_draw(struct k3MEvent *ev, uint8_t *ud) { struct k3MLabel *this = (void*) ev->target; - obj_draw((void*) this); + obj_draw_direct((void*) this); if(this->txt) { int alignment = k3M_ALIGN_CENTER; - struct k3MProperty *prop = k3MFindProperty(this, k3M_PROP_HORIZONTAL_ALIGNMENT, false); - if(prop) alignment = prop->si[0]; + struct k3MProperty *propHA = k3MFindProperty(this, k3M_PROP_HORIZONTAL_ALIGNMENT, false); + if(propHA) alignment = propHA->si[0]; - immdraw_font_draw(this->font, this->x, this->y, this->w, this->sz, strlen(this->txt), this->txt, alignment, + float sz = this->sz; + struct k3MProperty *fontSize = k3MFindProperty(this, k3M_PROP_FONT_SIZE, false); + if(fontSize) sz = fontSize->f[0]; + + immdraw_font_draw(this->font, this->x, this->y, this->w, sz, strlen(this->txt), this->txt, alignment, 1, 1, 1, 1); } @@ -188,6 +234,11 @@ struct k3MLabel *k3MLabel(struct k3Font *font, float sz, const char *txt) { k3MRegisterEventHandler(ret, k3M_EVENT_DRAW, label_draw, NULL, 0); + int16_t lblsz[2]; + immdraw_font_size(font, sz, txt, lblsz); + ret->w = lblsz[0]; + ret->h = lblsz[1]; + return ret; } @@ -273,6 +324,32 @@ static bool screen_ev(struct k3MEvent *ev, uint8_t *ud) { } if(innermost != this->lastHover) { + if(this->lastHover) { + k3MEventSend(&(struct k3MEvent) { + .code = k3M_EVENT_MOUSE_LEAVE, + + .kind = k3M_EVENTKIND_DIRECT, + + .original = ev->original, + .target = this->lastHover, + + .mouse = ev->mouse, + }); + } + + if(innermost != (void*) this) { + k3MEventSend(&(struct k3MEvent) { + .code = k3M_EVENT_MOUSE_ENTER, + + .kind = k3M_EVENTKIND_DIRECT, + + .original = ev->original, + .target = innermost, + + .mouse = ev->mouse, + }); + } + this->lastHover->hovered = false; innermost->hovered = true; this->lastHover = innermost; @@ -323,11 +400,57 @@ static bool screen_ev(struct k3MEvent *ev, uint8_t *ud) { return false; } +static bool screen_arrange(struct k3MEvent *ev, uint8_t *ud) { + struct k3MObj *this = ev->target; + + for(size_t c = 0; c < this->childCount; c++) { + struct k3MObj *child = this->children[c]; + + struct k3MProperty *left = k3MFindProperty(child, k3M_PROP_LEFT, true); + if(left) { + if(left->units[0] == k3M_UNIT_ABSOLUTE) { + child->x = this->x + left->f[0]; + } else if(left->units[0] == k3M_UNIT_PROPORTION) { + child->x = this->x + this->w * left->f[0]; + } + } + + struct k3MProperty *top = k3MFindProperty(child, k3M_PROP_TOP, true); + if(top) { + if(top->units[0] == k3M_UNIT_ABSOLUTE) { + child->y = this->y + top->f[0]; + } else if(top->units[0] == k3M_UNIT_PROPORTION) { + child->y = this->y + this->h * top->f[0]; + } + } + + struct k3MProperty *width = k3MFindProperty(child, k3M_PROP_WIDTH, true); + if(width) { + if(width->units[0] == k3M_UNIT_ABSOLUTE) { + child->w = width->f[0]; + } else if(width->units[0] == k3M_UNIT_PROPORTION) { + child->w = this->w * width->f[0]; + } + } + + struct k3MProperty *height = k3MFindProperty(child, k3M_PROP_HEIGHT, true); + if(height) { + if(height->units[0] == k3M_UNIT_ABSOLUTE) { + child->h = height->f[0]; + } else if(height->units[0] == k3M_UNIT_PROPORTION) { + child->h = this->h * height->f[0]; + } + } + } + + return false; +} struct k3MScreen *k3MScreen() { struct k3MScreen *ret = calloc(1, sizeof(*ret)); ret->lastHover = (void*) ret; + k3MRegisterEventHandler(ret, k3M_EVENT_ARRANGE, screen_arrange, NULL, 0); k3MRegisterEventHandler(ret, k3M_EVENT_ALL, screen_ev, NULL, 0); return ret; @@ -336,7 +459,7 @@ struct k3MScreen *k3MScreen() { static bool textbutton_draw(struct k3MEvent *ev, uint8_t *ud) { struct k3MTextButton *this = (void*) ev->target; - obj_draw((void*) this); + obj_draw_direct((void*) this); if(this->txt) { /*struct k3RectF txtsz; @@ -371,7 +494,7 @@ struct k3MTextButton *k3MTextButton(struct k3Font *font, float sz, const char *t static bool textinput_draw(struct k3MEvent *ev, uint8_t *ud) { struct k3MTextInput *this = (void*) ev->target; - obj_draw((void*) this); + obj_draw_direct((void*) this); if(this->txt) { bool isPlaceholder = strlen(this->txt) == 0; @@ -437,7 +560,162 @@ struct k3MTextInput *k3MTextInput(struct k3Font *font, float sz, const char *pla return ret; } +static bool obj_draw(struct k3MEvent *ev, uint8_t *ud) { + obj_draw_direct(ev->target); + return false; +} struct k3MObj *k3MObj() { struct k3MObj *ret = calloc(1, sizeof(*ret)); + k3MRegisterEventHandler(ret, k3M_EVENT_DRAW, obj_draw, NULL, 0); return ret; } + +static bool image_draw(struct k3MEvent *ev, uint8_t *ud) { + struct k3MImage *this = (void*) ev->target; + + struct k3MProperty *propFGColor = k3MFindProperty(ev->target, k3M_PROP_FG_COLOR, false); + + vec4 color = {1, 1, 1, 1}; + if(propFGColor) { + color[0] = propFGColor->si[0] / 255.0; + color[1] = propFGColor->si[1] / 255.0; + color[2] = propFGColor->si[2] / 255.0; + color[3] = propFGColor->si[3] / 255.0; + } + + immdraw_image_draw(&this->data, this->x, this->y, this->w, this->h, color[0], color[1], color[2], color[3]); + + return false; +} +struct k3MImage *k3MImage(k3MImageData data) { + struct k3MImage *ret = calloc(1, sizeof(*ret)); + ret->data = data; + k3MRegisterEventHandler(ret, k3M_EVENT_DRAW, image_draw, NULL, 0); + return ret; +} + +#define SCROLLBAR_WIDTH_DEFAULT 16 +static bool scrollbox_arrange(struct k3MEvent *ev, uint8_t *ud) { + struct k3MScrollbox *this = (void*) ev->target; + + struct k3MObj *contentBox = this->children[0]; + contentBox->x = this->x; + contentBox->y = this->y; + contentBox->w = this->w - 16; + contentBox->h = 16384; + + return false; +} +static bool scrollbox_post_arrange(struct k3MEvent *ev, uint8_t *ud) { + struct k3MScrollbox *this = (void*) ev->target; + + struct k3MObj *contentBox = this->children[0]; + + size_t maxY2 = 0; + for(size_t c = 0; c < contentBox->childCount; c++) { + size_t y2 = contentBox->children[c]->y + contentBox->children[c]->h; + + if(maxY2 < y2) { + maxY2 = y2; + } + } + + contentBox->h = maxY2 - contentBox->y; + + if(this->h > contentBox->h) { + this->scrollbarLength = this->h; + } else { + this->scrollbarLength = this->h * this->h / contentBox->h; + } + + return false; +} +static bool scrollbox_draw(struct k3MEvent *ev, uint8_t *ud) { + struct k3MScrollbox *this = (void*) ev->target; + + int scrollbarWidth = SCROLLBAR_WIDTH_DEFAULT; + struct k3MProperty *prop = k3MFindProperty(ev->target, k3M_PROP_SCROLLBAR_SIZE, false); + if(prop) { + scrollbarWidth = prop->si[0]; + } + + immdraw_fill_rect(this->x + this->w - scrollbarWidth, this->y + this->scrollbarOffset, scrollbarWidth, this->scrollbarLength, 1, 1, 1, 1, 0); + + return false; +} +static bool scrollbox_mouseshit(struct k3MEvent *ev, uint8_t *ud) { + struct k3MScrollbox *this = (void*) ev->target; + + if(ev->code == k3M_EVENT_MOUSE_PRESS) { + struct k3MProperty *prop = k3MFindProperty(ev->target, k3M_PROP_SCROLL_ANYWHERE, false); + bool scrollAnywhere = prop ? prop->si[0] : false; + + int scrollbarWidth = SCROLLBAR_WIDTH_DEFAULT; + prop = k3MFindProperty(ev->target, k3M_PROP_SCROLLBAR_SIZE, false); + if(prop) { + scrollbarWidth = prop->si[0]; + } + + if(ev->mouse.x >= this->x + this->w - scrollbarWidth) { + this->mouseHeld = 1; + } else if(scrollAnywhere) { + this->mouseHeld = 2; + } else { + this->mouseHeld = 0; + } + + this->mouseX = ev->mouse.x; + this->mouseY = ev->mouse.y; + } else if(ev->code == k3M_EVENT_MOUSE_RELEASE) { + this->mouseHeld = false; + } else if(ev->code == k3M_EVENT_MOUSE_MOTION) { + if(this->mouseHeld) { + + int16_t diffX = ev->mouse.x - this->mouseX; + int16_t diffY = ev->mouse.y - this->mouseY; + this->mouseX = ev->mouse.x; + this->mouseY = ev->mouse.y; + + struct k3MObj *contentBox = this->children[0]; + + if(this->mouseHeld == 1) { + this->scrollbarOffset += diffY; + contentBox->y = (float) this->y + this->scrollbarOffset * (-contentBox->h + this->h) / (this->h - this->scrollbarLength); + } else if(this->mouseHeld == 2) { + contentBox->y += diffY; + } + + if(contentBox->y > this->y) { + contentBox->y = this->y; + } else if(contentBox->y < this->y - contentBox->h + this->h) { + contentBox->y = this->y - contentBox->h + this->h; + } + this->scrollbarOffset = (float) (contentBox->y - this->y) * (this->h - this->scrollbarLength) / (this->h - contentBox->h); + + k3MArrange(contentBox); + + } + } + + return false; +} +struct k3MScrollbox *k3MScrollbox() { + struct k3MScrollbox *this = calloc(1, sizeof(*this)); + + struct k3MObj *contentBox = k3MObj(); + k3MAddChild(this, contentBox); + + k3MRegisterEventHandler(this, k3M_EVENT_ARRANGE, scrollbox_arrange, NULL, 0); + k3MRegisterEventHandler(this, k3M_EVENT_POST_ARRANGE, scrollbox_post_arrange, NULL, 0); + k3MRegisterEventHandler(this, k3M_EVENT_DRAW, scrollbox_draw, NULL, 0); + k3MRegisterEventHandler(this, k3M_EVENT_MOUSE_PRESS, scrollbox_mouseshit, NULL, 0); + k3MRegisterEventHandler(this, k3M_EVENT_MOUSE_MOTION, scrollbox_mouseshit, NULL, 0); + k3MRegisterEventHandler(this, k3M_EVENT_MOUSE_RELEASE, scrollbox_mouseshit, NULL, 0); + + this->crop = true; + + return this; +} +struct k3MObj *k3MScrollboxGetContent(struct k3MScrollbox *this) { + return this->children[0]; +} diff --git a/src/k3menu.h b/src/k3menu.h index 8c1b947..3f33586 100644 --- a/src/k3menu.h +++ b/src/k3menu.h @@ -21,7 +21,8 @@ struct k3MObj; #define k3M_EVENT_COMPLETE 10 #define k3M_EVENT_MEASURE 11 #define k3M_EVENT_ARRANGE 12 -#define k3M_EVENT_ALL 13 +#define k3M_EVENT_POST_ARRANGE 13 +#define k3M_EVENT_ALL 14 #define k3M_MOUSE_BUTTON_0 0 #define k3M_MOUSE_BUTTON_1 1 @@ -31,11 +32,18 @@ struct k3MObj; #define k3M_EVENTKIND_CAPTURE 1 #define k3M_EVENTKIND_BUBBLE 2 -#define k3M_USERDATA_SIZE 16 +#define k3M_USERDATA_SIZE 32 #define k3M_ALIGN_LEFT 0 +#define k3M_ALIGN_TOP 3 #define k3M_ALIGN_CENTER 1 #define k3M_ALIGN_RIGHT 2 +#define k3M_ALIGN_BOTTOM 4 + +#define k3M_IDX_TOP 0 +#define k3M_IDX_RIGHT 1 +#define k3M_IDX_BOTTOM 2 +#define k3M_IDX_LEFT 3 #ifdef k3M_FIXED_POINT typedef uint8_t k3MCC; @@ -43,6 +51,11 @@ typedef uint8_t k3MCC; typedef float k3MCC; #endif +#define k3M_UNIT_ABSOLUTE 0 +#define k3M_UNIT_PROPORTION 1 + +#include"k3menu_internal.h" + struct k3MEvent { uint16_t code; @@ -76,16 +89,28 @@ typedef struct k3MEventHandler { enum k3MPropertyType { k3M_PROP_BG_COLOR, + k3M_PROP_FG_COLOR, k3M_PROP_BORDER_RADIUS, k3M_PROP_MARGIN, + k3M_PROP_PADDING, k3M_PROP_HORIZONTAL_ALIGNMENT, + k3M_PROP_VERTICAL_ALIGNMENT, + k3M_PROP_LEFT, + k3M_PROP_TOP, + k3M_PROP_WIDTH, + k3M_PROP_HEIGHT, + k3M_PROP_FONT_SIZE, + k3M_PROP_SCROLL_ANYWHERE, + k3M_PROP_SCROLLBAR_SIZE, }; struct k3MProperty { enum k3MPropertyType type; + uint8_t units[4]; union { intmax_t si[4]; - void *ptr; + void *ptr[4]; uint8_t buf[32]; + float f[4]; }; }; @@ -103,7 +128,7 @@ struct k3MObj { int16_t wDesired; int16_t hDesired; - bool invisible, hovered, disabled; + bool invisible, hovered, disabled, crop; k3MEventHandler *handlers; size_t handlerCount; @@ -150,6 +175,8 @@ struct k3MScreen { struct k3MObj *keyboardFocus; struct k3MObj *lastHover; + + struct k3MObj *mouseCapture; }; struct k3MScreen *k3MScreen(); @@ -173,3 +200,23 @@ struct k3MTextInput { char *txt; }; struct k3MTextInput *k3MTextInput(struct k3Font *font, float sz, const char *placeholder, const char *txt); + +struct k3MImage { + struct k3MObj; + + k3MImageData data; +}; +struct k3MImage *k3MImage(k3MImageData); + +struct k3MScrollbox { + struct k3MObj; + + int mouseHeld; + int16_t mouseX; + int16_t mouseY; + + float scrollbarOffset; + int16_t scrollbarLength; +}; +struct k3MScrollbox *k3MScrollbox(); +struct k3MObj *k3MScrollboxGetContent(struct k3MScrollbox*);