compositor-test/src/ui.c
2024-02-25 20:31:11 +00:00

256 lines
5.7 KiB
C

#include "ui.h"
#include "SDL_events.h"
#include "aliases.h"
#include "math_utils.h"
#include "window.h"
#include <math.h>
#include <stdbool.h>
#include <stdlib.h>
#define NOODLE_HALF_WIDTH 2
internal u64 get_id(ui_ctx *ctx);
void init_ui_ctx(ui_ctx *ctx) {
*ctx = (ui_ctx){0};
ctx->hovered = -1;
ctx->active = -1;
}
void reset_ui_ctx(ui_ctx *ctx) {
ctx->count = 0;
ctx->mouse_down = false;
ctx->mouse_up = false;
ctx->rel_x = 0;
ctx->rel_y = 0;
}
void handle_ui_events(const window *wnd, ui_ctx *ctx, const SDL_Event *event) {
switch (event->type) {
case SDL_MOUSEMOTION:
if (wnd->id == event->motion.windowID) {
ctx->mouse_x = event->motion.x;
ctx->mouse_y = event->motion.y;
ctx->rel_x += event->motion.xrel;
ctx->rel_y += event->motion.yrel;
ctx->wnd = wnd;
break;
}
case SDL_MOUSEBUTTONDOWN:
if (wnd->id == event->button.windowID) {
ctx->mouse_x = event->button.x;
ctx->mouse_y = event->button.y;
ctx->mouse_down = true;
ctx->wnd = wnd;
break;
}
case SDL_MOUSEBUTTONUP:
if (wnd->id == event->button.windowID) {
ctx->mouse_x = event->button.x;
ctx->mouse_y = event->button.y;
ctx->mouse_up = true;
ctx->wnd = wnd;
break;
}
}
}
bool ui_button(const window *wnd, ui_ctx *ctx, rect rec,
ui_elem_colours colours) {
if (ctx->count + 1 >= MAX_UI_ELEMENTS) {
return false;
}
u64 id = get_id(ctx);
ctx->elements[id] = (ui_elem){
.id = id,
.rec = rec,
.type = UI_ELEM_BUTTON,
};
fill_rect(wnd, rec, colours.fill);
draw_rect(wnd, rec, colours.border);
if (wnd != ctx->wnd || (ctx->active >= 0 && ctx->active != id)) {
return false;
}
if (!aabb(rec, ctx->mouse_x, ctx->mouse_y)) {
return false;
}
ctx->hovered = id;
if (ctx->mouse_down) {
ctx->active = id;
}
if (ctx->mouse_up && ctx->hovered == id && ctx->active == id) {
ctx->hovered = ctx->active = -1;
return true;
}
return false;
}
bool ui_node(const window *wnd, ui_ctx *ctx, rect rect,
ui_elem_colours colours) {
if (ctx->count + 1 >= MAX_UI_ELEMENTS) {
return false;
}
u64 id = get_id(ctx);
ctx->elements[id] = (ui_elem){
.id = id,
.rec = rect,
.type = UI_ELEM_NODE,
};
fill_rect(wnd, rect, colours.fill);
draw_rect(wnd, rect, colours.border);
if (wnd != ctx->wnd || (ctx->active >= 0 && ctx->active != id)) {
return false;
}
if (ctx->mouse_up) {
if (ctx->hovered == ctx->active && ctx->hovered == id) {
ctx->hovered = ctx->active = -1;
}
return false;
}
if (ctx->hovered == id && ctx->active == id) {
return true;
}
if (!aabb(rect, ctx->mouse_x, ctx->mouse_y)) {
return false;
}
ctx->hovered = id;
if (ctx->mouse_down) {
ctx->active = id;
}
return false;
}
internal u64 get_id(ui_ctx *ctx) {
// This will always keep the 0 slot empty
return ++(ctx->count);
}
bool ui_noodle(const window *wnd, ui_ctx *ctx, line ln, ui_elem_colours colours,
rect parent_node) {
if (ctx->count + 1 >= MAX_UI_ELEMENTS) {
return false;
}
u64 id = get_id(ctx);
ctx->elements[id] = (ui_elem){
.id = id,
.ln = ln,
.type = UI_ELEM_NOODLE,
};
bool horizontal = ln.p0.y == ln.p1.y;
rect bounding_box = (rect){0};
if (horizontal) {
i32 x = min(ln.p0.x, ln.p1.x);
bounding_box.topleft = (point){x, ln.p0.y - NOODLE_HALF_WIDTH};
bounding_box.w = abs(ln.p1.x - ln.p0.x);
bounding_box.h = NOODLE_HALF_WIDTH * 2;
fill_rect(wnd, bounding_box, colours.fill);
} else {
vec2 direction = line_direction(&ln);
quad qd = (quad){0};
if (direction.x == 0) {
qd = (quad){
.p0 = (point){ln.p0.x - NOODLE_HALF_WIDTH, ln.p0.y},
.p1 = (point){ln.p0.x + NOODLE_HALF_WIDTH, ln.p0.y},
.p2 = (point){ln.p1.x - NOODLE_HALF_WIDTH, ln.p1.y},
.p3 = (point){ln.p1.x + NOODLE_HALF_WIDTH, ln.p1.y},
};
} else {
f32 slope = (f32)(direction.y) / direction.x;
f32 perpendicular_dy =
sqrtf(square(NOODLE_HALF_WIDTH) / (square((f32)slope) + 1.0f));
f32 perpendicular_dx = -1.0f * slope * perpendicular_dy;
qd = (quad){
.p0 = (point){ln.p0.x - perpendicular_dx, ln.p0.y - perpendicular_dy},
.p1 = (point){ln.p0.x + perpendicular_dx, ln.p0.y + perpendicular_dy},
.p2 = (point){ln.p1.x - perpendicular_dx, ln.p1.y - perpendicular_dy},
.p3 = (point){ln.p1.x + perpendicular_dx, ln.p1.y + perpendicular_dy},
};
}
fill_quad(wnd, qd, colours.fill);
i32 _min_x_1 = min(qd.p0.x, qd.p1.x);
i32 _min_x_2 = min(qd.p2.x, qd.p3.x);
i32 min_x = min(_min_x_1, _min_x_2);
i32 _max_x_1 = max(qd.p0.x, qd.p1.x);
i32 _max_x_2 = max(qd.p2.x, qd.p3.x);
i32 max_x = max(_max_x_1, _max_x_2);
i32 _min_y_1 = min(qd.p0.y, qd.p1.y);
i32 _min_y_2 = min(qd.p2.y, qd.p3.y);
i32 min_y = min(_min_y_1, _min_y_2);
i32 _max_y_1 = max(qd.p0.y, qd.p1.y);
i32 _max_y_2 = max(qd.p2.y, qd.p3.y);
i32 max_y = max(_max_y_1, _max_y_2);
bounding_box = (rect){
.topleft.x = min_x,
.topleft.y = min_y,
.w = max_x - min_x,
.h = max_y - min_y,
};
}
if (wnd != ctx->wnd || (ctx->active >= 0 && ctx->active != id)) {
return false;
}
if (ctx->mouse_up) {
if (ctx->hovered == ctx->active && ctx->hovered == id) {
ctx->hovered = ctx->active = -1;
}
return true;
}
if (ctx->hovered == id && ctx->active == id) {
return true;
}
if (!aabb(bounding_box, ctx->mouse_x, ctx->mouse_y) ||
aabb(parent_node, ctx->mouse_x, ctx->mouse_y)) {
return false;
}
ctx->hovered = id;
if (ctx->mouse_down) {
ctx->active = id;
}
return false;
}