298 lines
7.2 KiB
C
298 lines
7.2 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
|
|
#define DEFAULT_NOODLE_LENGTH 60
|
|
|
|
internal line ui_noodle(const window *wnd, ui_ctx *ctx, line ln,
|
|
ui_elem_colours colours, rect node);
|
|
internal bool aabb(rect rec, i32 x, i32 y);
|
|
internal line line_from_origin(point origin, f64 angle, i32 line_length);
|
|
|
|
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 = (ctx->count)++;
|
|
|
|
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;
|
|
}
|
|
|
|
ui_node_elem ui_node(const window *wnd, ui_ctx *ctx, ui_node_elem node,
|
|
ui_elem_colours colours) {
|
|
if (ctx->count + 1 >= MAX_UI_ELEMENTS) {
|
|
return (ui_node_elem){0};
|
|
}
|
|
|
|
u64 id = (ctx->count)++;
|
|
|
|
line ln = {0};
|
|
f64 angle = 90.0;
|
|
f64 angle_delta = 25.0;
|
|
i64 delta_multiplier = node.inputs % 2 == 0 ? -1 : 0;
|
|
|
|
for (u64 i = 0; i < node.inputs; ++i) {
|
|
if (node.noodles[i].p0.x == node.noodles[i].p1.x &&
|
|
node.noodles[i].p0.y == node.noodles[i].p1.y) {
|
|
point origin = {node.rec.topleft.x + node.rec.w / 2,
|
|
node.rec.topleft.y + node.rec.h / 2};
|
|
|
|
f64 new_angle = angle + angle_delta * delta_multiplier;
|
|
|
|
ln = line_from_origin(origin, new_angle, DEFAULT_NOODLE_LENGTH);
|
|
|
|
if (delta_multiplier > 0) {
|
|
angle = new_angle;
|
|
}
|
|
|
|
if (delta_multiplier == 0) {
|
|
delta_multiplier = -1;
|
|
} else {
|
|
delta_multiplier *= -1;
|
|
}
|
|
} else {
|
|
ln = node.noodles[i];
|
|
}
|
|
|
|
node.noodles[i] = ui_noodle(wnd, ctx, ln, colours, node.rec);
|
|
}
|
|
|
|
fill_rect(wnd, node.rec, colours.fill);
|
|
draw_rect(wnd, node.rec, colours.border);
|
|
|
|
if (wnd != ctx->wnd || (ctx->active >= 0 && ctx->active != id)) {
|
|
return node;
|
|
}
|
|
|
|
if (ctx->mouse_up) {
|
|
ctx->hovered = ctx->active = -1;
|
|
ctx->rel_x = ctx->rel_y = 0;
|
|
return node;
|
|
}
|
|
|
|
if (ctx->hovered == id && ctx->active == id) {
|
|
node.rec.topleft.x += ctx->rel_x;
|
|
node.rec.topleft.y += ctx->rel_y;
|
|
|
|
for (u64 i = 0; i < node.inputs; ++i) {
|
|
node.noodles[i].p0.x += ctx->rel_x;
|
|
node.noodles[i].p0.y += ctx->rel_y;
|
|
node.noodles[i].p1.x += ctx->rel_x;
|
|
node.noodles[i].p1.y += ctx->rel_y;
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
if (!aabb(node.rec, ctx->mouse_x, ctx->mouse_y)) {
|
|
return node;
|
|
}
|
|
|
|
ctx->hovered = id;
|
|
|
|
if (ctx->mouse_down) {
|
|
ctx->active = id;
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
internal line ui_noodle(const window *wnd, ui_ctx *ctx, line ln,
|
|
ui_elem_colours colours, rect node) {
|
|
if (ctx->count + 1 >= MAX_UI_ELEMENTS) {
|
|
return (line){0};
|
|
}
|
|
|
|
u64 id = (ctx->count)++;
|
|
|
|
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 ln;
|
|
}
|
|
|
|
if (ctx->mouse_up) {
|
|
ctx->hovered = ctx->active = -1;
|
|
ctx->rel_x = ctx->rel_y = 0;
|
|
return ln;
|
|
}
|
|
|
|
if (ctx->hovered == id && ctx->active == id) {
|
|
ln.p0.x += ctx->rel_x;
|
|
ln.p0.y += ctx->rel_y;
|
|
|
|
return ln;
|
|
}
|
|
|
|
if (!aabb(bounding_box, ctx->mouse_x, ctx->mouse_y) ||
|
|
aabb(node, ctx->mouse_x, ctx->mouse_y)) {
|
|
return ln;
|
|
}
|
|
|
|
ctx->hovered = id;
|
|
|
|
if (ctx->mouse_down) {
|
|
ctx->active = id;
|
|
}
|
|
|
|
return ln;
|
|
}
|
|
|
|
internal bool aabb(rect rec, i32 x, i32 y) {
|
|
return x > rec.topleft.x && x <= rec.topleft.x + rec.w && y > rec.topleft.y &&
|
|
y <= rec.topleft.y + rec.h;
|
|
}
|
|
|
|
internal line line_from_origin(point origin, f64 angle, i32 line_length) {
|
|
f64 rad = radians(angle);
|
|
f64 direction = angle / absolute(angle) * -1;
|
|
|
|
i32 adjacent = line_length * cos(rad) * direction; // dx
|
|
i32 opposite = line_length * sin(rad) * direction; // dy
|
|
|
|
return (line){
|
|
(point){origin.x + adjacent, origin.y + opposite},
|
|
origin,
|
|
};
|
|
}
|