sm64coopdx/src/pc/djui/djui_cursor.c
2024-03-19 19:48:48 +10:00

163 lines
5.5 KiB
C

#include "djui.h"
#include "djui_panel.h"
#include "pc/controller/controller_mouse.h"
#include "pc/controller/controller_sdl.h"
#include "pc/gfx/gfx_window_manager_api.h"
#include "pc/pc_main.h"
extern ALIGNED8 u8 gd_texture_hand_open[];
extern ALIGNED8 u8 gd_texture_hand_closed[];
struct DjuiImage* sMouseCursor = NULL;
static bool sCursorMouseControlled = false;
static struct DjuiBase* sInputControlledBase = NULL;
static f32 sSavedMouseX = 0;
static f32 sSavedMouseY = 0;
f32 gCursorX = 0;
f32 gCursorY = 0;
void djui_cursor_set_visible(bool visible) {
if (sMouseCursor) {
djui_base_set_visible(&sMouseCursor->base, visible);
}
if (wm_api) {
if (configWindow.fullscreen) {
wm_api->set_cursor_visible(false);
} else {
wm_api->set_cursor_visible(!visible);
}
}
sSavedMouseX = mouse_window_x;
sSavedMouseY = mouse_window_y;
}
bool djui_cursor_inside_base(struct DjuiBase* base) {
struct DjuiBaseRect* clip = &base->elem;
if (gCursorX < clip->x) { return false; }
if (gCursorX > clip->x + clip->width) { return false; }
if (gCursorY < clip->y) { return false; }
if (gCursorY > clip->y + clip->height) { return false; }
return true;
}
static void djui_cursor_base_hover_location(struct DjuiBase* base, f32* x, f32* y) {
base->get_cursor_hover_location(base, x, y);
}
void djui_cursor_input_controlled_center(struct DjuiBase* base) {
if (!sCursorMouseControlled && (!base || (base && base->interactable && base->interactable->enabled))) {
sInputControlledBase = base;
djui_cursor_set_visible(base != NULL);
}
}
static f32 djui_cursor_base_distance(struct DjuiBase* base, f32 xScale, f32 yScale) {
f32 x, y;
djui_cursor_base_hover_location(base, &x, &y);
x -= gCursorX;
y -= gCursorY;
return sqrtf((x * x) * xScale + (y * y) * yScale);
}
static void djui_cursor_move_check(s8 xDir, s8 yDir, struct DjuiBase** pick, struct DjuiBase* base) {
if (!base->visible) { return; }
if (base->interactable != NULL && base->interactable->enabled) {
f32 x1, y1, x2, y2;
x1 = base->elem.x;
y1 = base->elem.y;
x2 = base->elem.x + base->elem.width;
y2 = base->elem.y + base->elem.height;
bool xWithin = (gCursorX >= x1 && gCursorX <= x2) || sCursorMouseControlled;
bool yWithin = (gCursorY >= y1 && gCursorY <= y2) || sCursorMouseControlled;
bool valid = false;
if (yDir > 0 && gCursorY < y1 && xWithin) { valid = true; }
if (yDir < 0 && gCursorY > y2 && xWithin) { valid = true; }
if (xDir > 0 && gCursorX < x1 && yWithin) { valid = true; }
if (xDir < 0 && gCursorX > x2 && yWithin) { valid = true; }
f32 xH, yH;
djui_cursor_base_hover_location(base, &xH, &yH);
if (valid) {
if (*pick == NULL) {
*pick = base;
} else {
f32 pickDist = djui_cursor_base_distance(*pick, xDir ? 1.0f : 2.0f, yDir ? 1.0f : 2.0f);
f32 baseDist = djui_cursor_base_distance(base, xDir ? 1.0f : 2.0f, yDir ? 1.0f : 2.0f);
if (baseDist < pickDist) { *pick = base; }
}
}
}
// check all children
struct DjuiBaseChild* child = base->child;
while (child != NULL) {
djui_cursor_move_check(xDir, yDir, pick, child->base);
child = child->next;
}
}
void djui_cursor_move(s8 xDir, s8 yDir) {
if (xDir == 0 && yDir == 0) { return; }
struct DjuiBase* pick = NULL;
djui_cursor_move_check(xDir, yDir, &pick, &gDjuiRoot->base);
if (pick != NULL) {
sCursorMouseControlled = false;
djui_cursor_input_controlled_center(pick);
}
}
void djui_cursor_update(void) {
#if defined(CAPI_SDL2) || defined(CAPI_SDL1)
if (djui_interactable_is_binding()) { return; }
if (sMouseCursor == NULL) { return; }
if (!djui_panel_is_active()) { return; }
controller_sdl_read_mouse_window();
// check if mouse is in control again
static bool sFirstUpdate = true;
if (sFirstUpdate) {
sFirstUpdate = false;
sCursorMouseControlled = false;
sSavedMouseX = mouse_window_x;
sSavedMouseY = mouse_window_y;
} else if (!sCursorMouseControlled || (sMouseCursor && !sMouseCursor->base.visible)) {
f32 dist = sqrtf(powf(mouse_window_x - sSavedMouseX, 2) + powf(mouse_window_y - sSavedMouseY, 2));
if (dist > 5) {
sCursorMouseControlled = true;
djui_cursor_set_visible(true);
}
}
// update mouse cursor
if (sCursorMouseControlled) {
gCursorX = mouse_window_x / djui_gfx_get_scale();
gCursorY = mouse_window_y / djui_gfx_get_scale();
} else if (sInputControlledBase != NULL) {
djui_cursor_base_hover_location(sInputControlledBase, &gCursorX, &gCursorY);
}
// set cursor position
djui_base_set_location(&sMouseCursor->base, gCursorX - 13, gCursorY - 13);
// set cursor sprite
if ((gInteractablePad.button & PAD_BUTTON_A) || (mouse_window_buttons & MOUSE_BUTTON_1)) {
djui_image_set_image(sMouseCursor, gd_texture_hand_closed, 32, 32, 16);
} else {
djui_image_set_image(sMouseCursor, gd_texture_hand_open, 32, 32, 16);
}
#endif
djui_base_render(&sMouseCursor->base);
}
void djui_cursor_create(void) {
sMouseCursor = djui_image_create(NULL, gd_texture_hand_open, 32, 32, 16);
djui_base_set_location(&sMouseCursor->base, 0, 0);
djui_base_set_size(&sMouseCursor->base, 64, 64);
}