diff --git a/build-windows-visual-studio/sm64ex.vcxproj b/build-windows-visual-studio/sm64ex.vcxproj
index 6cb9b4c58..4eee61f68 100644
--- a/build-windows-visual-studio/sm64ex.vcxproj
+++ b/build-windows-visual-studio/sm64ex.vcxproj
@@ -3944,6 +3944,7 @@
+
@@ -4371,6 +4372,7 @@
+
diff --git a/build-windows-visual-studio/sm64ex.vcxproj.filters b/build-windows-visual-studio/sm64ex.vcxproj.filters
index c67615fbf..d92453262 100644
--- a/build-windows-visual-studio/sm64ex.vcxproj.filters
+++ b/build-windows-visual-studio/sm64ex.vcxproj.filters
@@ -15207,6 +15207,9 @@
Source Files\src\pc\djui\panel
+
+ Source Files\src\pc\djui\component
+
@@ -16234,5 +16237,8 @@
Source Files\src\pc\djui\panel
+
+ Source Files\src\pc\djui\component
+
\ No newline at end of file
diff --git a/src/pc/controller/controller_keyboard.c b/src/pc/controller/controller_keyboard.c
index 1059ba4e3..d964d7afb 100644
--- a/src/pc/controller/controller_keyboard.c
+++ b/src/pc/controller/controller_keyboard.c
@@ -18,6 +18,7 @@
#include "engine/math_util.h"
#include "menu/file_select.h"
#include "game/chat.h"
+#include "src/pc/djui/djui.h"
// TODO: use some common lookup header
#define SCANCODE_BACKSPACE 0x0E
@@ -86,6 +87,8 @@ bool keyboard_on_key_down(int scancode) {
// alter the held value of modifier keys
keyboard_alter_modifier(scancode, true);
+ djui_interactable_on_key_down(scancode);
+
#ifdef DEBUG
if (!sInTextInput) {
debug_keyboard_on_key_down(scancode);
@@ -132,6 +135,8 @@ bool keyboard_on_key_up(int scancode) {
// alter the held value of modifier keys
keyboard_alter_modifier(scancode, false);
+ djui_interactable_on_key_up(scancode);
+
if (sInTextInput) {
// ignore any key up event if we're in text-input mode
return FALSE;
diff --git a/src/pc/djui/djui.c b/src/pc/djui/djui.c
index f965212f2..00a12eb8a 100644
--- a/src/pc/djui/djui.c
+++ b/src/pc/djui/djui.c
@@ -1,49 +1,17 @@
#include "djui.h"
#include "../debuglog.h"
-#include "src/pc/controller/controller_sdl.h"
-#include "src/pc/controller/controller_mouse.h"
-
-ALIGNED8 static u8 texture_hand_open[] = {
-#include "textures/intro_raw/hand_open.rgba16.inc.c"
-};
-
-ALIGNED8 static u8 texture_hand_closed[] = {
-#include "textures/intro_raw/hand_closed.rgba16.inc.c"
-};
-
static Gfx* sSavedDisplayListHead = NULL;
struct DjuiRoot* gDjuiRoot = NULL;
-static struct DjuiImage* sMouseCursor = NULL;
struct DjuiFlowLayout* buttonContainer;
static void djui_init(void) {
gDjuiRoot = djui_root_create();
- sMouseCursor = djui_image_create(NULL, texture_hand_open, 32, 32, 16);
- djui_base_set_location(&sMouseCursor->base, 0, 0);
- djui_base_set_size(&sMouseCursor->base, 64, 64);
-
djui_panel_main_create();
//djui_panel_debug_create();
-}
-
-static void djui_mouse_update(void) {
-#if defined(CAPI_SDL2) || defined(CAPI_SDL1)
- controller_sdl_read_mouse_window();
-
- djui_interactable_update();
-
- // adjust mouse cursor
- djui_base_set_location(&sMouseCursor->base, mouse_window_x - 13, mouse_window_y - 13);
-
- if (mouse_window_buttons & 0b0001) {
- djui_image_set_image(sMouseCursor, texture_hand_closed, 32, 32, 16);
- } else {
- djui_image_set_image(sMouseCursor, texture_hand_open, 32, 32, 16);
- }
-#endif
+ djui_cursor_create();
}
void djui_render_patch(void) {
@@ -60,8 +28,8 @@ void djui_render(void) {
sSavedDisplayListHead = gDisplayListHead;
create_dl_ortho_matrix();
- djui_mouse_update();
-
djui_base_render(&gDjuiRoot->base);
- djui_base_render(&sMouseCursor->base);
+
+ djui_cursor_update();
+ djui_interactable_update();
}
diff --git a/src/pc/djui/djui.h b/src/pc/djui/djui.h
index 1f331ca23..fd6a3f5a9 100644
--- a/src/pc/djui/djui.h
+++ b/src/pc/djui/djui.h
@@ -14,6 +14,7 @@
#include "djui_interactable.h"
#include "djui_root.h"
+#include "djui_cursor.h"
#include "djui_rect.h"
#include "djui_text.h"
#include "djui_image.h"
diff --git a/src/pc/djui/djui_button.c b/src/pc/djui/djui_button.c
index 69326bf5e..1f02bd779 100644
--- a/src/pc/djui/djui_button.c
+++ b/src/pc/djui/djui_button.c
@@ -18,14 +18,14 @@ static void djui_button_on_hover_end(struct DjuiBase* base) {
djui_button_set_default_style(base);
}
-static void djui_button_on_mouse_down_begin(struct DjuiBase* base) {
+static void djui_button_on_cursor_down_begin(struct DjuiBase* base) {
struct DjuiButton* button = (struct DjuiButton*)base;
djui_base_set_border_color(base, 0, 84, 153, 255);
djui_base_set_color(&button->rect->base, 204, 228, 247, 255);
djui_base_set_location(&button->text->base, 0.5f, 0.5f);
}
-static void djui_button_on_mouse_down_end(struct DjuiBase* base) {
+static void djui_button_on_cursor_down_end(struct DjuiBase* base) {
djui_button_set_default_style(base);
}
@@ -44,8 +44,8 @@ struct DjuiButton* djui_button_create(struct DjuiBase* parent, const char* messa
djui_interactable_create(base,
djui_button_on_hover_begin,
djui_button_on_hover_end,
- djui_button_on_mouse_down_begin,
- djui_button_on_mouse_down_end);
+ djui_button_on_cursor_down_begin,
+ djui_button_on_cursor_down_end);
struct DjuiRect* rect = djui_rect_create(&button->base);
djui_base_set_size_type(&rect->base, DJUI_SVT_RELATIVE, DJUI_SVT_RELATIVE);
diff --git a/src/pc/djui/djui_cursor.c b/src/pc/djui/djui_cursor.c
new file mode 100644
index 000000000..e4d79937c
--- /dev/null
+++ b/src/pc/djui/djui_cursor.c
@@ -0,0 +1,123 @@
+#include "djui.h"
+
+#include "src/pc/controller/controller_sdl.h"
+#include "src/pc/controller/controller_mouse.h"
+
+ALIGNED8 static u8 texture_hand_open[] = {
+#include "textures/intro_raw/hand_open.rgba16.inc.c"
+};
+
+ALIGNED8 static u8 texture_hand_closed[] = {
+#include "textures/intro_raw/hand_closed.rgba16.inc.c"
+};
+
+static struct DjuiImage* sMouseCursor = NULL;
+
+static bool sCursorMouseControlled = false;
+static f32 sSavedMouseX = 0;
+static f32 sSavedMouseY = 0;
+static f32 sCursorX = 0;
+static f32 sCursorY = 0;
+
+bool djui_cursor_inside_base(struct DjuiBase* base) {
+ struct DjuiBaseRect* clip = &base->elem;
+ if (sCursorX < clip->x) { return false; }
+ if (sCursorX > clip->x + clip->width) { return false; }
+ if (sCursorY < clip->y) { return false; }
+ if (sCursorY > clip->y + clip->height) { return false; }
+ return true;
+}
+
+static void djui_cursor_base_hover_location(struct DjuiBase* base, f32* x, f32* y) {
+ *x = (base->elem.x + base->elem.width * 3.0f / 4.0f);
+ *y = (base->elem.y + base->elem.height * 3.0f / 4.0f);
+}
+
+static f32 djui_cursor_base_distance(struct DjuiBase* base) {
+ f32 x, y;
+ djui_cursor_base_hover_location(base, &x, &y);
+ x -= sCursorX;
+ y -= sCursorY;
+ return sqrtf((x * x) + (y * y));
+}
+
+static void djui_cursor_move_check(s8 xDir, s8 yDir, struct DjuiBase** pick, struct DjuiBase* base) {
+ if (!base->visible) { return; }
+ if (!base->enabled) { return; }
+
+ if (base->interactable != NULL) {
+ f32 xH, yH;
+ djui_cursor_base_hover_location(base, &xH, &yH);
+ bool valid = true;
+ if (xDir > 0 && sCursorX >= xH) { valid = false; }
+ if (xDir < 0 && sCursorX <= xH) { valid = false; }
+ if (yDir > 0 && sCursorY >= yH) { valid = false; }
+ if (yDir < 0 && sCursorY <= yH) { valid = false; }
+ if (valid) {
+ if (*pick == NULL) {
+ *pick = base;
+ } else {
+ f32 pickDist = djui_cursor_base_distance(*pick);
+ f32 baseDist = djui_cursor_base_distance(base);
+ 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) {
+ if (sCursorMouseControlled) {
+ sCursorMouseControlled = false;
+ sSavedMouseX = sCursorX;
+ sSavedMouseY = sCursorY;
+ }
+ djui_cursor_base_hover_location(pick, &sCursorX, &sCursorY);
+ }
+}
+
+void djui_cursor_update(void) {
+#if defined(CAPI_SDL2) || defined(CAPI_SDL1)
+ controller_sdl_read_mouse_window();
+
+ // adjust mouse cursor
+ if (!sCursorMouseControlled) {
+ f32 dist = sqrtf(powf(mouse_window_x - sSavedMouseX, 2) + powf(mouse_window_y - sSavedMouseY, 2));
+ if (dist > 2) {
+ sCursorMouseControlled = true;
+ }
+ }
+ if (sCursorMouseControlled) {
+ sCursorX = mouse_window_x;
+ sCursorY = mouse_window_y;
+ }
+ djui_base_set_location(&sMouseCursor->base, sCursorX - 13, sCursorY - 13);
+
+ if (mouse_window_buttons & 0b0001) {
+ djui_image_set_image(sMouseCursor, texture_hand_closed, 32, 32, 16);
+ }
+ else {
+ djui_image_set_image(sMouseCursor, texture_hand_open, 32, 32, 16);
+ }
+#endif
+ djui_base_render(&sMouseCursor->base);
+}
+
+void djui_cursor_create(void) {
+ sMouseCursor = djui_image_create(NULL, texture_hand_open, 32, 32, 16);
+ djui_base_set_location(&sMouseCursor->base, 0, 0);
+ djui_base_set_size(&sMouseCursor->base, 64, 64);
+}
\ No newline at end of file
diff --git a/src/pc/djui/djui_cursor.h b/src/pc/djui/djui_cursor.h
new file mode 100644
index 000000000..690b5d4ac
--- /dev/null
+++ b/src/pc/djui/djui_cursor.h
@@ -0,0 +1,8 @@
+#pragma once
+#include "djui.h"
+#include "djui_base.h"
+
+bool djui_cursor_inside_base(struct DjuiBase* base);
+void djui_cursor_move(s8 xDir, s8 yDir);
+void djui_cursor_update(void);
+void djui_cursor_create(void);
diff --git a/src/pc/djui/djui_interactable.c b/src/pc/djui/djui_interactable.c
index 64b8a38d3..780ca7ae1 100644
--- a/src/pc/djui/djui_interactable.c
+++ b/src/pc/djui/djui_interactable.c
@@ -1,20 +1,28 @@
#include
+#include
#include "djui.h"
#include "src/pc/controller/controller_sdl.h"
#include "src/pc/controller/controller_mouse.h"
+#include "src/pc/controller/controller_keyboard.h"
-struct DjuiBase* sHovered = NULL;
-struct DjuiBase* sMouseDown = NULL;
+#define PAD_BUTTON_A (1 << 15)
-static bool djui_interactable_mouse_inside(struct DjuiBase* base) {
- struct DjuiBaseRect* clip = &base->elem;
- if (mouse_window_x < clip->x) { return false; }
- if (mouse_window_x > clip->x + clip->width) { return false; }
- if (mouse_window_y < clip->y) { return false; }
- if (mouse_window_y > clip->y + clip->height) { return false; }
- return true;
-}
+#define SCANCODE_UP 328
+#define SCANCODE_DOWN 336
+#define SCANCODE_LEFT 331
+#define SCANCODE_RIGHT 333
+
+#define SCANCODE_ENTER 28
+#define SCANCODE_SPACE 57
+#define SCANCODE_ESCAPE 1
+
+enum PadHoldDirection { PAD_HOLD_DIR_NONE, PAD_HOLD_DIR_UP, PAD_HOLD_DIR_DOWN, PAD_HOLD_DIR_LEFT, PAD_HOLD_DIR_RIGHT };
+static enum PadHoldDirection sKeyboardHoldDirection = PAD_HOLD_DIR_NONE;
+
+static struct DjuiBase* sHovered = NULL;
+static struct DjuiBase* sMouseDown = NULL;
+static u8 sInputCursorDown = false;
static void djui_interactable_on_click(struct DjuiBase* base) {
if (base == NULL) { return; }
@@ -37,37 +45,37 @@ static void djui_interactable_on_hover_end(struct DjuiBase* base) {
base->interactable->on_hover_end(base);
}
-static void djui_interactable_on_mouse_down_begin(struct DjuiBase* base) {
+static void djui_interactable_on_cursor_down_begin(struct DjuiBase* base) {
if (base == NULL) { return; }
if (base->interactable == NULL) { return; }
- if (base->interactable->on_mouse_down_begin == NULL) { return; }
+ if (base->interactable->on_cursor_down_begin == NULL) { return; }
if (sHovered != NULL) {
djui_interactable_on_hover_end(sHovered);
sHovered = NULL;
}
- base->interactable->on_mouse_down_begin(base);
+ base->interactable->on_cursor_down_begin(base);
}
-static void djui_interactable_on_mouse_down_end(struct DjuiBase* base) {
+static void djui_interactable_on_cursor_down_end(struct DjuiBase* base) {
if (base == NULL) { return; }
if (base->interactable == NULL) { return; }
- if (base->interactable->on_mouse_down_end == NULL) { return; }
- base->interactable->on_mouse_down_end(base);
+ if (base->interactable->on_cursor_down_end == NULL) { return; }
+ base->interactable->on_cursor_down_end(base);
- if (djui_interactable_mouse_inside(base)) {
+ if (djui_cursor_inside_base(base)) {
djui_interactable_on_click(base);
}
}
-static void djui_interactable_mouse_update_active(struct DjuiBase* base) {
+static void djui_interactable_cursor_update_active(struct DjuiBase* base) {
if (!base->visible) { return; }
if (!base->enabled) { return; }
static struct DjuiBase* insideParent = NULL;
- if (!djui_interactable_mouse_inside(base)) { return; }
+ if (!djui_cursor_inside_base(base)) { return; }
if (base->interactable != NULL) {
sHovered = base;
@@ -79,7 +87,7 @@ static void djui_interactable_mouse_update_active(struct DjuiBase* base) {
// check all children
struct DjuiBaseChild* child = base->child;
while (child != NULL) {
- djui_interactable_mouse_update_active(child->base);
+ djui_interactable_cursor_update_active(child->base);
child = child->next;
}
@@ -88,21 +96,93 @@ static void djui_interactable_mouse_update_active(struct DjuiBase* base) {
}
}
+void djui_interactable_on_key_down(int scancode) {
+ switch (scancode) {
+ case SCANCODE_UP: sKeyboardHoldDirection = PAD_HOLD_DIR_UP; break;
+ case SCANCODE_DOWN: sKeyboardHoldDirection = PAD_HOLD_DIR_DOWN; break;
+ case SCANCODE_LEFT: sKeyboardHoldDirection = PAD_HOLD_DIR_LEFT; break;
+ case SCANCODE_RIGHT: sKeyboardHoldDirection = PAD_HOLD_DIR_RIGHT; break;
+ case SCANCODE_ENTER: sInputCursorDown |= (1 << 0); break;
+ }
+}
+
+void djui_interactable_on_key_up(int scancode) {
+ switch (scancode) {
+ case SCANCODE_UP: if (sKeyboardHoldDirection == PAD_HOLD_DIR_UP) { sKeyboardHoldDirection = PAD_HOLD_DIR_NONE; } break;
+ case SCANCODE_DOWN: if (sKeyboardHoldDirection == PAD_HOLD_DIR_DOWN) { sKeyboardHoldDirection = PAD_HOLD_DIR_NONE; } break;
+ case SCANCODE_LEFT: if (sKeyboardHoldDirection == PAD_HOLD_DIR_LEFT) { sKeyboardHoldDirection = PAD_HOLD_DIR_NONE; } break;
+ case SCANCODE_RIGHT: if (sKeyboardHoldDirection == PAD_HOLD_DIR_RIGHT) { sKeyboardHoldDirection = PAD_HOLD_DIR_NONE; } break;
+ case SCANCODE_ENTER: sInputCursorDown &= ~(1 << 0); break;
+ }
+ if (scancode == SCANCODE_ENTER) {
+ sInputCursorDown &= ~(1 << 0);
+ }
+}
+
+void djui_interactable_update_pad(void) {
+ OSContPad* pad = &gControllerPads[0];
+
+ sInputCursorDown &= ~(1 << 1);
+ if (pad->button & PAD_BUTTON_A) {
+ sInputCursorDown |= (1 << 1);
+ }
+
+ static enum PadHoldDirection lastPadHoldDirection = PAD_HOLD_DIR_NONE;
+ static clock_t padHoldTimer = 0;
+
+ enum PadHoldDirection padHoldDirection = PAD_HOLD_DIR_NONE;
+ if (pad->stick_x == 0 && pad->stick_y == 0) {
+ padHoldDirection = PAD_HOLD_DIR_NONE;
+ } else if (abs(pad->stick_x) > abs(pad->stick_y)) {
+ padHoldDirection = (pad->stick_x < 0) ? PAD_HOLD_DIR_LEFT : PAD_HOLD_DIR_RIGHT;
+ } else {
+ padHoldDirection = (pad->stick_y > 0) ? PAD_HOLD_DIR_UP : PAD_HOLD_DIR_DOWN;
+ }
+
+ if (sKeyboardHoldDirection != PAD_HOLD_DIR_NONE) {
+ padHoldDirection = sKeyboardHoldDirection;
+ }
+
+ bool validPadHold = false;
+ if (padHoldDirection == PAD_HOLD_DIR_NONE) {
+ // nothing to do
+ } else if (padHoldDirection != lastPadHoldDirection) {
+ padHoldTimer = clock() + CLOCKS_PER_SEC * 0.25f;
+ validPadHold = true;
+ } else if (clock() > padHoldTimer) {
+ padHoldTimer = clock() + CLOCKS_PER_SEC * 0.10f;
+ validPadHold = true;
+ }
+
+ if (validPadHold) {
+ switch (padHoldDirection) {
+ case PAD_HOLD_DIR_UP: djui_cursor_move( 0, -1); break;
+ case PAD_HOLD_DIR_DOWN: djui_cursor_move( 0, 1); break;
+ case PAD_HOLD_DIR_LEFT: djui_cursor_move(-1, 0); break;
+ case PAD_HOLD_DIR_RIGHT: djui_cursor_move( 1, 0); break;
+ default: break;
+ }
+ }
+
+ lastPadHoldDirection = padHoldDirection;
+}
+
void djui_interactable_update(void) {
- if (mouse_window_buttons & 0b0001) {
+ djui_interactable_update_pad();
+ if ((sInputCursorDown) || (mouse_window_buttons & 0b0001)) {
if (sHovered != NULL) {
sMouseDown = sHovered;
sHovered = NULL;
- djui_interactable_on_mouse_down_begin(sMouseDown);
+ djui_interactable_on_cursor_down_begin(sMouseDown);
}
} else {
if (sMouseDown != NULL) {
- djui_interactable_on_mouse_down_end(sMouseDown);
+ djui_interactable_on_cursor_down_end(sMouseDown);
sMouseDown = NULL;
}
struct DjuiBase* lastHovered = sHovered;
sHovered = NULL;
- djui_interactable_mouse_update_active(&gDjuiRoot->base);
+ djui_interactable_cursor_update_active(&gDjuiRoot->base);
if (lastHovered != sHovered) {
djui_interactable_on_hover_end(lastHovered);
}
@@ -114,8 +194,8 @@ void djui_interactable_update(void) {
void djui_interactable_create(struct DjuiBase* base,
void (*on_hover_begin)(struct DjuiBase*),
void (*on_hover_end)(struct DjuiBase*),
- void (*on_mouse_down_begin)(struct DjuiBase*),
- void (*on_mouse_down_end)(struct DjuiBase*)) {
+ void (*on_cursor_down_begin)(struct DjuiBase*),
+ void (*on_cursor_down_end)(struct DjuiBase*)) {
if (base->interactable != NULL) {
free(base->interactable);
@@ -124,8 +204,8 @@ void djui_interactable_create(struct DjuiBase* base,
struct DjuiInteractable* interactable = malloc(sizeof(struct DjuiInteractable));
interactable->on_hover_begin = on_hover_begin;
interactable->on_hover_end = on_hover_end;
- interactable->on_mouse_down_begin = on_mouse_down_begin;
- interactable->on_mouse_down_end = on_mouse_down_end;
+ interactable->on_cursor_down_begin = on_cursor_down_begin;
+ interactable->on_cursor_down_end = on_cursor_down_end;
interactable->on_click = NULL;
base->interactable = interactable;
diff --git a/src/pc/djui/djui_interactable.h b/src/pc/djui/djui_interactable.h
index fcbf79f6d..c5448f76b 100644
--- a/src/pc/djui/djui_interactable.h
+++ b/src/pc/djui/djui_interactable.h
@@ -7,15 +7,17 @@ struct DjuiInteractable {
bool enabled;
void (*on_hover_begin)(struct DjuiBase*);
void (*on_hover_end)(struct DjuiBase*);
- void (*on_mouse_down_begin)(struct DjuiBase*);
- void (*on_mouse_down_end)(struct DjuiBase*);
+ void (*on_cursor_down_begin)(struct DjuiBase*);
+ void (*on_cursor_down_end)(struct DjuiBase*);
void (*on_click)(struct DjuiBase*);
};
+void djui_interactable_on_key_down(int scancode);
+void djui_interactable_on_key_up(int scancode);
void djui_interactable_update(void);
void djui_interactable_create(struct DjuiBase* base,
void (*on_hover_begin)(struct DjuiBase*),
void (*on_hover_end)(struct DjuiBase*),
- void (*on_mouse_down_begin)(struct DjuiBase*),
- void (*on_mouse_down_end)(struct DjuiBase*));
+ void (*on_cursor_down_begin)(struct DjuiBase*),
+ void (*on_cursor_down_end)(struct DjuiBase*));