diff --git a/build-windows-visual-studio/sm64ex.vcxproj b/build-windows-visual-studio/sm64ex.vcxproj index 6cb9b4c5..4eee61f6 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 c67615fb..d9245326 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 1059ba4e..d964d7af 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 f965212f..00a12eb8 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 1f331ca2..fd6a3f5a 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 69326bf5..1f02bd77 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 00000000..e4d79937 --- /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 00000000..690b5d4a --- /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 64b8a38d..780ca7ae 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 fcbf79f6..c5448f76 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*));