diff --git a/extern/aurora b/extern/aurora index 22351fb0b7..509021de0a 160000 --- a/extern/aurora +++ b/extern/aurora @@ -1 +1 @@ -Subproject commit 22351fb0b76a4f4f2c4a4dff95aa300101e861aa +Subproject commit 509021de0a45f3318769a0f15265b432747bc103 diff --git a/flake.nix b/flake.nix index ec87c5526a..8abce3969a 100644 --- a/flake.nix +++ b/flake.nix @@ -16,37 +16,37 @@ ]; forAllSystems = lib.genAttrs supportedSystems; - dawnVersion = "v20260423.175430"; - nodVersion = "v2.0.0-alpha.8"; + dawnVersion = "v20260618.032059"; + nodVersion = "v2.0.0-alpha.10"; versionSuffix = "nix-" + (self.shortRev or self.dirtyShortRev or "dirty"); dawnInfo = { "x86_64-linux" = { triple = "linux-x86_64"; - hash = "sha256-HXfKTLHtMPwupnFnaflCARtXVPuS/0PoCePXidjE5xs="; + hash = "sha256-GFSd573b+VQx/VmFdNQgWDd0V9ayQlcw0Zuopke12ak="; }; "aarch64-linux" = { triple = "linux-aarch64"; - hash = "sha256-34yyFpfqBZUwoFXQ41F0AwAU78FaNihOSY0oriwn6B0="; + hash = "sha256-ZaoP7BAjBMnfAv2/AMRi3FNH2ZtyqASCSFyU/oB2Mzg="; }; "aarch64-darwin" = { triple = "darwin-arm64"; - hash = "sha256-eQnzrBp6gjiBek1VYQ9A5W13ClYWrDDKjIqv/7eNTR4="; + hash = "sha256-HT+qtlLaSHyoXPrUcXgcTGa877X5YfzbxRD4bJb7i1Y="; }; "x86_64-darwin" = { triple = "darwin-x86_64"; - hash = "sha256-QGWiGdxiI9kci3NPXH6QFFirxn16851zB/w3jqhIBJ4="; + hash = "sha256-cUNaCbA7rlKSukDVKGaVEVw0Zt1+mSbaHbmUCMvMVWc="; }; }; nodPrebuiltInfo = { "x86_64-linux" = { triple = "linux-x86_64"; - hash = "sha256-mUqvLsbsqaZ+HAjMmHYPYO+MgtanGRTw7Gzn5uXR5rE="; + hash = "sha256-FVQWECVA2gWdc+n5OQ/Tvwn8z0qdgjSd1WlFt5HKOec="; }; "aarch64-darwin" = { triple = "macos-arm64"; - hash = "sha256-UPy1ywCcv0K6VJOU3uUelJuUdBh3UNaPRlyP5LOBeDw="; + hash = "sha256-8ZEejxksVgShNKUVRCBYaLOp9x/qOC9pAeVrElQUGUk="; }; }; @@ -75,7 +75,7 @@ ''; dawn = pkgs.fetchzip { - url = "https://github.com/encounter/dawn-build/releases/download/${dawnVersion}/dawn-${dawnInfo.${system}.triple}.tar.gz"; + url = "https://github.com/encounter/dawn/releases/download/${dawnVersion}/dawn-${dawnInfo.${system}.triple}.tar.gz"; hash = dawnInfo.${system}.hash; stripRoot = false; }; @@ -94,7 +94,7 @@ owner = "encounter"; repo = "nod"; rev = nodVersion; - hash = "sha256-+zrtVzjo0+X/6uMcNUn1+FaSR+jOhrcQSDNBFjw0NDs="; + hash = "sha256-r8qDlOVxv5iKiFjJQrcBuL9HVoOM3yEjRVnQIMqaICs="; }; patches = [ ./fix-cmake-paths.patch ]; cargoDeps = pkgs.rustPlatform.importCargoLock { @@ -141,12 +141,12 @@ XXHASH = pkgs.xxhash.src; ZSTD = pkgs.zstd.src; FMT = pkgs.fetchzip { - url = "https://github.com/fmtlib/fmt/archive/refs/tags/11.1.4.tar.gz"; - hash = "sha256-sUbxlYi/Aupaox3JjWFqXIjcaQa0LFjclQAOleT+FRA="; + url = "https://github.com/fmtlib/fmt/archive/refs/tags/12.1.0.tar.gz"; + hash = "sha256-ZmI1Dv0ZabPlxa02OpERI47jp7zFfjpeWCy1WyuPYZ0="; }; TRACY = pkgs.fetchzip { - url = "https://github.com/wolfpld/tracy/archive/a64b9a20294d59421a2f57aeca3c6383d8c48169.tar.gz"; - hash = "sha256-hbNGOsGeyGSvCJ2No8RkwOib1lX2on3vNZSzyVkZdXw="; + url = "https://github.com/wolfpld/tracy/archive/6789e7d6f9a65ec98926b602097a33a9676d2606.tar.gz"; + hash = "sha256-Xxyd7G/mnXEPpN+ehmwl0AkAhS3CwObpJNDgcqbdUJg="; }; IMGUI = pkgs.fetchFromGitHub { owner = "ocornut"; diff --git a/include/d/d_menu_ring.h b/include/d/d_menu_ring.h index 1836a4538a..99c75ccd78 100644 --- a/include/d/d_menu_ring.h +++ b/include/d/d_menu_ring.h @@ -219,6 +219,7 @@ private: bool mCursorInterpPrevAngular; bool mCursorInterpCurrAngular; bool mCursorInterpInit; + bool mPointerTouchPressHoveredCurrent; J2DPicture* mDpadIcon; #endif }; diff --git a/include/dusk/menu_pointer.h b/include/dusk/menu_pointer.h index 17546877d6..86123f1163 100644 --- a/include/dusk/menu_pointer.h +++ b/include/dusk/menu_pointer.h @@ -6,6 +6,9 @@ class CPaneMgr; namespace dusk::menu_pointer { +using TargetId = u16; +constexpr TargetId InvalidTarget = 0xffff; + enum class Context { None, FileSelect, @@ -43,12 +46,14 @@ bool active() noexcept; bool enabled() noexcept; bool mouse_capture_active() noexcept; const State& state() noexcept; +void set_hover_target(TargetId target) noexcept; bool consume_click() noexcept; +bool peek_click() noexcept; void set_dialog_choice(u8 choice, bool clicked) noexcept; bool get_dialog_choice(u8& choice) noexcept; bool consume_dialog_click(u8& choice) noexcept; -void defer_activation(Context context, u8 target) noexcept; -bool consume_deferred_activation(Context context, u8 target) noexcept; +void defer_activation(Context context, TargetId target) noexcept; +bool consume_deferred_activation(Context context, TargetId target) noexcept; void clear_deferred_activation(Context context) noexcept; u32 suppressed_pad_buttons(u32 port) noexcept; void finish_pad_suppression_read(u32 port) noexcept; diff --git a/src/d/d_file_select.cpp b/src/d/d_file_select.cpp index 6fa90818f5..93ac1f91d6 100644 --- a/src/d/d_file_select.cpp +++ b/src/d/d_file_select.cpp @@ -784,6 +784,7 @@ bool dFile_select_c::pointerDataSelect() { if (!dusk::menu_pointer::hit_pane(mSelFilePanes[i], 8.0f)) { continue; } + dusk::menu_pointer::set_hover_target(pointer_target(s_pointerDataSelectTarget, i)); const bool clicked = dusk::menu_pointer::consume_click(); if (mSelectNum != i) { mDoAud_seStart(Z2SE_FILE_SELECT_CURSOR, NULL, 0, 0); @@ -812,6 +813,7 @@ bool dFile_select_c::pointerMenuSelect() { if (!dusk::menu_pointer::hit_pane(m3mSelPane[i], 8.0f)) { continue; } + dusk::menu_pointer::set_hover_target(pointer_target(s_pointerMenuSelectTarget, i)); const bool clicked = dusk::menu_pointer::consume_click(); if (!mIsDataNew[mSelectNum] && mSelectMenuNum != i) { mDoAud_seStart(Z2SE_SY_MENU_CURSOR_COMMON, NULL, 0, 0); @@ -840,6 +842,7 @@ bool dFile_select_c::pointerCopyDataToSelect() { if (!dusk::menu_pointer::hit_pane(mCpSelPane[i], 8.0f)) { continue; } + dusk::menu_pointer::set_hover_target(pointer_target(s_pointerCopySelectTarget, i)); const bool clicked = dusk::menu_pointer::consume_click(); if (field_0x026b != i) { mDoAud_seStart(Z2SE_FILE_SELECT_CURSOR, NULL, 0, 0); @@ -868,6 +871,7 @@ bool dFile_select_c::pointerYesNoSelect(bool errorSelect) { if (!dusk::menu_pointer::hit_pane(mYnSelPane[i], 8.0f)) { continue; } + dusk::menu_pointer::set_hover_target(pointer_target(s_pointerYesNoSelectTarget, i)); const bool clicked = (!errorSelect || field_0x0268 == i) && dusk::menu_pointer::consume_click(); if (field_0x0268 != i) { @@ -1131,12 +1135,13 @@ void dFile_select_c::dataSelectAnmSet() { void dFile_select_c::dataSelectMoveAnime() { #if TARGET_PC dusk::menu_pointer::begin_context(dusk::menu_pointer::Context::FileSelect); - if (mSelectNum != 0xFF && dusk::menu_pointer::hit_pane(mSelFilePanes[mSelectNum], 8.0f) && - dusk::menu_pointer::consume_click()) - { - dusk::menu_pointer::defer_activation( - dusk::menu_pointer::Context::FileSelect, - pointer_target(s_pointerDataSelectTarget, mSelectNum)); + if (mSelectNum != 0xFF && dusk::menu_pointer::hit_pane(mSelFilePanes[mSelectNum], 8.0f)) { + dusk::menu_pointer::set_hover_target(pointer_target(s_pointerDataSelectTarget, mSelectNum)); + if (dusk::menu_pointer::consume_click()) { + dusk::menu_pointer::defer_activation( + dusk::menu_pointer::Context::FileSelect, + pointer_target(s_pointerDataSelectTarget, mSelectNum)); + } } #endif bool iVar7 = true; @@ -1609,12 +1614,14 @@ void dFile_select_c::menuSelectMoveAnm() { #if TARGET_PC dusk::menu_pointer::begin_context(dusk::menu_pointer::Context::FileSelect); if (mSelectMenuNum != 0xFF && - dusk::menu_pointer::hit_pane(m3mSelPane[mSelectMenuNum], 8.0f) && - dusk::menu_pointer::consume_click()) + dusk::menu_pointer::hit_pane(m3mSelPane[mSelectMenuNum], 8.0f)) { - dusk::menu_pointer::defer_activation( - dusk::menu_pointer::Context::FileSelect, - pointer_target(s_pointerMenuSelectTarget, mSelectMenuNum)); + dusk::menu_pointer::set_hover_target(pointer_target(s_pointerMenuSelectTarget, mSelectMenuNum)); + if (dusk::menu_pointer::consume_click()) { + dusk::menu_pointer::defer_activation( + dusk::menu_pointer::Context::FileSelect, + pointer_target(s_pointerMenuSelectTarget, mSelectMenuNum)); + } } #endif bool tmp1 = true; @@ -2126,12 +2133,14 @@ void dFile_select_c::copyDataToSelectMoveAnm() { #if TARGET_PC dusk::menu_pointer::begin_context(dusk::menu_pointer::Context::FileSelect); if (field_0x026b != 0xFF && - dusk::menu_pointer::hit_pane(mCpSelPane[field_0x026b], 8.0f) && - dusk::menu_pointer::consume_click()) + dusk::menu_pointer::hit_pane(mCpSelPane[field_0x026b], 8.0f)) { - dusk::menu_pointer::defer_activation( - dusk::menu_pointer::Context::FileSelect, - pointer_target(s_pointerCopySelectTarget, field_0x026b)); + dusk::menu_pointer::set_hover_target(pointer_target(s_pointerCopySelectTarget, field_0x026b)); + if (dusk::menu_pointer::consume_click()) { + dusk::menu_pointer::defer_activation( + dusk::menu_pointer::Context::FileSelect, + pointer_target(s_pointerCopySelectTarget, field_0x026b)); + } } #endif bool iVar7 = true; @@ -2651,12 +2660,14 @@ void dFile_select_c::yesNoCursorMoveAnm() { #if TARGET_PC dusk::menu_pointer::begin_context(dusk::menu_pointer::Context::FileSelect); if (field_0x0268 != 0xFF && - dusk::menu_pointer::hit_pane(mYnSelPane[field_0x0268], 8.0f) && - dusk::menu_pointer::consume_click()) + dusk::menu_pointer::hit_pane(mYnSelPane[field_0x0268], 8.0f)) { - dusk::menu_pointer::defer_activation( - dusk::menu_pointer::Context::FileSelect, - pointer_target(s_pointerYesNoSelectTarget, field_0x0268)); + dusk::menu_pointer::set_hover_target(pointer_target(s_pointerYesNoSelectTarget, field_0x0268)); + if (dusk::menu_pointer::consume_click()) { + dusk::menu_pointer::defer_activation( + dusk::menu_pointer::Context::FileSelect, + pointer_target(s_pointerYesNoSelectTarget, field_0x0268)); + } } #endif bool isYnSelMove = yesnoSelectMoveAnm(); diff --git a/src/d/d_menu_collect.cpp b/src/d/d_menu_collect.cpp index 3aea57e1d3..4e3ddf25f2 100644 --- a/src/d/d_menu_collect.cpp +++ b/src/d/d_menu_collect.cpp @@ -1960,6 +1960,7 @@ bool dMenu_Collect2D_c::pointerWait() { if (getItemTag(x, y, true) == 0 || !dusk::menu_pointer::hit_pane(mpSelPm[x][y], 8.0f)) { continue; } + dusk::menu_pointer::set_hover_target(static_cast(x + y * 7)); if (mCursorX != x || mCursorY != y) { mDoAud_seStart(Z2SE_SY_MENU_CURSOR_COMMON, NULL, 0, 0); mCursorX = x; diff --git a/src/d/d_menu_insect.cpp b/src/d/d_menu_insect.cpp index 7967ca6cdf..aa18407b09 100644 --- a/src/d/d_menu_insect.cpp +++ b/src/d/d_menu_insect.cpp @@ -320,6 +320,7 @@ bool dMenu_Insect_c::pointerWait() { if (!isGetInsect(x, y) || !dusk::menu_pointer::hit_pane(mpINSParent[index], 8.0f)) { continue; } + dusk::menu_pointer::set_hover_target(index); if (field_0xf4 != x || field_0xf5 != y) { field_0xf4 = x; diff --git a/src/d/d_menu_letter.cpp b/src/d/d_menu_letter.cpp index 0d7efd3adb..3f997bbda5 100644 --- a/src/d/d_menu_letter.cpp +++ b/src/d/d_menu_letter.cpp @@ -482,6 +482,7 @@ bool dMenu_Letter_c::pointerWait() { if (!dusk::menu_pointer::hit_pane(mpLetterParent[i], 8.0f)) { continue; } + dusk::menu_pointer::set_hover_target(i); if (mIndex != i) { mIndex = i; diff --git a/src/d/d_menu_option.cpp b/src/d/d_menu_option.cpp index cc4bb01f2a..94babc8342 100644 --- a/src/d/d_menu_option.cpp +++ b/src/d/d_menu_option.cpp @@ -83,6 +83,12 @@ enum SelectType { SelectType8, }; +#if TARGET_PC +static dusk::menu_pointer::TargetId option_yes_no_target(u8 index) noexcept { + return static_cast(0x100 + index); +} +#endif + dMenu_Option_c::dMenu_Option_c(JKRArchive* i_archive, STControl* i_stick) { mUseFlag = 0; mBarScale[0] = g_drawHIO.mOptionScreen.mBarScale[0]; @@ -1098,18 +1104,28 @@ void dMenu_Option_c::confirm_move_move() { if (!dusk::menu_pointer::hit_pane(mpYesNoSelBase_c[i], 8.0f)) { continue; } + dusk::menu_pointer::set_hover_target(option_yes_no_target(i)); + const bool clicked = dusk::menu_pointer::consume_click(); if (field_0x3f9 != i) { Z2GetAudioMgr()->seStart(Z2SE_SY_MENU_CURSOR_COMMON, NULL, 0, 0, 1.0f, 1.0f, -1.0f, -1.0f, 0); field_0x3fa = field_0x3f9; field_0x3f9 = i; + if (clicked) { + yesNoSelectStart(); + field_0x3ef = SelectType7; + dMeter2Info_set2DVibrationM(); + mpWarning->_move(); + setAnimation(); + return; + } yesnoSelectAnmSet(); field_0x3ef = SelectType6; mpWarning->_move(); setAnimation(); return; } - if (dusk::menu_pointer::consume_click()) { + if (clicked) { yesNoSelectStart(); field_0x3ef = SelectType7; dMeter2Info_set2DVibrationM(); @@ -1156,11 +1172,36 @@ void dMenu_Option_c::confirm_select_init() { } void dMenu_Option_c::confirm_select_move() { +#if TARGET_PC + dusk::menu_pointer::begin_context(dusk::menu_pointer::Context::Options); + if (field_0x3f9 != 0xff && + dusk::menu_pointer::hit_pane(mpYesNoSelBase_c[field_0x3f9], 8.0f)) + { + const dusk::menu_pointer::TargetId target = option_yes_no_target(field_0x3f9); + dusk::menu_pointer::set_hover_target(target); + if (dusk::menu_pointer::consume_click()) { + dusk::menu_pointer::defer_activation(dusk::menu_pointer::Context::Options, target); + } + } +#endif u8 selectMoveAnm = yesnoSelectMoveAnm(); u8 wakuAlphaAnm = yesnoWakuAlpahAnm(field_0x3fa); if (selectMoveAnm == 1 && wakuAlphaAnm == 1) { yesnoCursorShow(); +#if TARGET_PC + if (field_0x3f9 != 0xff && + dusk::menu_pointer::consume_deferred_activation( + dusk::menu_pointer::Context::Options, option_yes_no_target(field_0x3f9))) + { + yesNoSelectStart(); + field_0x3ef = SelectType7; + dMeter2Info_set2DVibrationM(); + mpWarning->_move(); + setAnimation(); + return; + } +#endif field_0x3ef = SelectType5; } mpWarning->_move(); @@ -2196,16 +2237,14 @@ bool dMenu_Option_c::isRumbleSupported() { #if TARGET_PC bool dMenu_Option_c::pointerConfirmSelect() { dusk::menu_pointer::begin_context(dusk::menu_pointer::Context::Options); - if (!dusk::menu_pointer::state().clicked) { - return false; - } - for (u8 i = 0; i < SelectType3; ++i) { if (dusk::menu_pointer::hit_pane(mpMenuPane[i], 8.0f)) { + dusk::menu_pointer::set_hover_target(i); return false; } } + dusk::menu_pointer::set_hover_target(0x200); if (!dusk::menu_pointer::consume_click()) { return false; } @@ -2226,6 +2265,7 @@ bool dMenu_Option_c::dpdMenuMove() { if (!dusk::menu_pointer::hit_pane(mpMenuPane[i], 8.0f)) { continue; } + dusk::menu_pointer::set_hover_target(i); if (getSelectType() != i) { field_0x3ef = i; setCursorPos(i); diff --git a/src/d/d_menu_ring.cpp b/src/d/d_menu_ring.cpp index 194df9f4e0..8d44ce0d52 100644 --- a/src/d/d_menu_ring.cpp +++ b/src/d/d_menu_ring.cpp @@ -202,6 +202,7 @@ dMenu_Ring_c::dMenu_Ring_c(JKRExpHeap* i_heap, STControl* i_stick, CSTControl* i mCursorInterpPrevAngular = false; mCursorInterpCurrAngular = false; mCursorInterpInit = false; + mPointerTouchPressHoveredCurrent = false; mDpadIcon = JKR_NEW J2DPicture((ResTIMG*)dComIfGp_getMain2DArchive()->getResource('TIMG', "font_51.bti")); #endif for (int i = 0; i < 4; i++) { @@ -1584,6 +1585,10 @@ bool dMenu_Ring_c::pointerMove() { if (hoveredSlot < 0) { return false; } + if (pointer.pressed) { + mPointerTouchPressHoveredCurrent = pointer.touch && hoveredSlot == mCurrentSlot; + } + dusk::menu_pointer::set_hover_target(static_cast(hoveredSlot)); if (mCurrentSlot != hoveredSlot) { mDirectSelectCursorPos.x = mItemSlotPosX[mCurrentSlot]; @@ -1596,10 +1601,27 @@ bool dMenu_Ring_c::pointerMove() { return true; } - if (dusk::menu_pointer::consume_click()) { + const bool clickOpensExplain = !pointer.touch || mPointerTouchPressHoveredCurrent; + if (clickOpensExplain && dusk::menu_pointer::consume_click()) { + const u8 item = dComIfGs_getItem(mItemSlots[mCurrentSlot], false); + if (!dMeter2Info_isTouchKeyCheck(0xe) && openExplain(item)) { + dMeter2Info_setItemExplainWindowStatus(1); + field_0x6c4 = mCurrentSlot; + setStatus(STATUS_EXPLAIN); + dMeter2Info_set2DVibration(); + setDoStatus(0); + } else { + Z2GetAudioMgr()->seStart(Z2SE_SYS_ERROR, NULL, 0, 0, 1.0f, 1.0f, -1.0f, + -1.0f, 0); + } + mPointerTouchPressHoveredCurrent = false; return true; } + if (pointer.released) { + mPointerTouchPressHoveredCurrent = false; + } + return false; } #endif diff --git a/src/d/d_menu_save.cpp b/src/d/d_menu_save.cpp index 7c75e466d1..a0bf7d8fcb 100644 --- a/src/d/d_menu_save.cpp +++ b/src/d/d_menu_save.cpp @@ -1830,6 +1830,7 @@ bool dMenu_save_c::pointerSaveSelect() { if (!dusk::menu_pointer::hit_pane(mpSelData[i], 8.0f)) { continue; } + dusk::menu_pointer::set_hover_target(pointer_target(s_pointerSaveSelectTarget, i)); const bool clicked = dusk::menu_pointer::consume_click(); if (mSelectedFile != i) { mDoAud_seStart(Z2SE_FILE_SELECT_CURSOR, NULL, 0, 0); @@ -1858,6 +1859,7 @@ bool dMenu_save_c::pointerYesNoSelect(bool errorSelect, u8 errParam, u8 soundPar if (!dusk::menu_pointer::hit_pane(mpNoYes[i], 8.0f)) { continue; } + dusk::menu_pointer::set_hover_target(pointer_target(s_pointerYesNoSelectTarget, i)); const bool clicked = (!errorSelect || mYesNoCursor == i) && dusk::menu_pointer::consume_click(); if (mYesNoCursor != i) { @@ -1962,12 +1964,14 @@ void dMenu_save_c::saveSelectMoveAnime() { #if TARGET_PC dusk::menu_pointer::begin_context(dusk::menu_pointer::Context::Save); if (mSelectedFile != 0xFF && - dusk::menu_pointer::hit_pane(mpSelData[mSelectedFile], 8.0f) && - dusk::menu_pointer::consume_click()) + dusk::menu_pointer::hit_pane(mpSelData[mSelectedFile], 8.0f)) { - dusk::menu_pointer::defer_activation( - dusk::menu_pointer::Context::Save, - pointer_target(s_pointerSaveSelectTarget, mSelectedFile)); + dusk::menu_pointer::set_hover_target(pointer_target(s_pointerSaveSelectTarget, mSelectedFile)); + if (dusk::menu_pointer::consume_click()) { + dusk::menu_pointer::defer_activation( + dusk::menu_pointer::Context::Save, + pointer_target(s_pointerSaveSelectTarget, mSelectedFile)); + } } #endif bool bookWakuAnmComplete = true; @@ -2140,12 +2144,14 @@ void dMenu_save_c::yesNoCursorMoveAnm() { #if TARGET_PC dusk::menu_pointer::begin_context(dusk::menu_pointer::Context::Save); if (mYesNoCursor != 0xFF && - dusk::menu_pointer::hit_pane(mpNoYes[mYesNoCursor], 8.0f) && - dusk::menu_pointer::consume_click()) + dusk::menu_pointer::hit_pane(mpNoYes[mYesNoCursor], 8.0f)) { - dusk::menu_pointer::defer_activation( - dusk::menu_pointer::Context::Save, - pointer_target(s_pointerYesNoSelectTarget, mYesNoCursor)); + dusk::menu_pointer::set_hover_target(pointer_target(s_pointerYesNoSelectTarget, mYesNoCursor)); + if (dusk::menu_pointer::consume_click()) { + dusk::menu_pointer::defer_activation( + dusk::menu_pointer::Context::Save, + pointer_target(s_pointerYesNoSelectTarget, mYesNoCursor)); + } } #endif bool selAnmComplete = yesnoSelectMoveAnm(0); diff --git a/src/d/d_menu_skill.cpp b/src/d/d_menu_skill.cpp index 601bae1eb6..0c970e76c3 100644 --- a/src/d/d_menu_skill.cpp +++ b/src/d/d_menu_skill.cpp @@ -316,6 +316,7 @@ bool dMenu_Skill_c::pointerWait() { if (!dusk::menu_pointer::hit_pane(mpLetterParent[i], 8.0f)) { continue; } + dusk::menu_pointer::set_hover_target(i); if (mIndex != i) { mIndex = i; diff --git a/src/d/d_msg_scrn_3select.cpp b/src/d/d_msg_scrn_3select.cpp index b021b52dec..fd85da423f 100644 --- a/src/d/d_msg_scrn_3select.cpp +++ b/src/d/d_msg_scrn_3select.cpp @@ -560,7 +560,8 @@ bool dMsgScrn3Select_c::pointerMove() { mDPDPoint = choice; field_0x110 = paneIndex; - dusk::menu_pointer::set_dialog_choice(choice, dusk::menu_pointer::state().clicked); + dusk::menu_pointer::set_hover_target(choice); + dusk::menu_pointer::set_dialog_choice(choice, dusk::menu_pointer::peek_click()); return true; } diff --git a/src/dusk/menu_pointer.cpp b/src/dusk/menu_pointer.cpp index 91b66cce55..83ed7afc84 100644 --- a/src/dusk/menu_pointer.cpp +++ b/src/dusk/menu_pointer.cpp @@ -1,16 +1,34 @@ #include "dusk/menu_pointer.h" -#include "m_Do/m_Do_graphic.h" #include "d/d_pane_class.h" #include "dusk/settings.h" +#include "m_Do/m_Do_graphic.h" #include #include #include +#include namespace dusk::menu_pointer { namespace { +using Clock = std::chrono::steady_clock; + +constexpr auto kTapMaxDuration = std::chrono::milliseconds(300); +constexpr f32 kTapMoveThresholdDp = 12.0f; + +struct Gesture { + bool active = false; + bool movedTooFar = false; + bool crossedTarget = false; + bool pressTargetValid = false; + Context pressContext = Context::None; + TargetId pressTarget = InvalidTarget; + f32 startX = 0.0f; + f32 startY = 0.0f; + Clock::time_point startedAt{}; +}; + State s_state; bool s_clickConsumed = false; Context s_lastContext = Context::None; @@ -27,7 +45,14 @@ s32 s_mouseButton = -1; u32 s_suppressedPadHoldMask = 0; u32 s_suppressedPadNextReadMask = 0; Context s_deferredActivationContext = Context::None; -u8 s_deferredActivationTarget = 0xFF; +TargetId s_deferredActivationTarget = InvalidTarget; +Gesture s_gesture; +bool s_hoverTargetValid = false; +TargetId s_hoverTarget = InvalidTarget; +bool s_clickPending = false; +Context s_clickContext = Context::None; +TargetId s_clickTarget = InvalidTarget; +bool s_clickTargetValid = false; s32 scancode_from_rml_button(s32 button) noexcept { switch (button) { @@ -104,6 +129,37 @@ void suppress_pad_for_mouse_button(s32 button, bool held) noexcept { } } +f32 tap_move_threshold() noexcept { + auto* context = aurora::rmlui::get_context(); + if (context == nullptr) { + return kTapMoveThresholdDp; + } + + return kTapMoveThresholdDp * std::max(context->GetDensityIndependentPixelRatio(), 1.0f); +} + +void update_gesture_movement(f32 x, f32 y) noexcept { + if (!s_gesture.active || s_gesture.movedTooFar) { + return; + } + + const f32 dx = x - s_gesture.startX; + const f32 dy = y - s_gesture.startY; + const f32 threshold = tap_move_threshold(); + if (dx * dx + dy * dy > threshold * threshold) { + s_gesture.movedTooFar = true; + } +} + +void clear_click_state() noexcept { + s_clickConsumed = false; + s_clickPending = false; + s_clickContext = Context::None; + s_clickTarget = InvalidTarget; + s_clickTargetValid = false; + s_state.clicked = false; +} + void set_position_from_rml(f32 x, f32 y) noexcept { auto* context = aurora::rmlui::get_context(); if (context == nullptr) { @@ -121,7 +177,7 @@ void set_position_from_rml(f32 x, f32 y) noexcept { void clear_input_state() noexcept { s_state = {}; - s_clickConsumed = false; + clear_click_state(); s_lastDialogChoice = 0xFF; s_currentDialogChoice = 0xFF; s_lastDialogChoiceValid = false; @@ -134,7 +190,10 @@ void clear_input_state() noexcept { s_suppressedPadHoldMask = 0; s_suppressedPadNextReadMask = 0; s_deferredActivationContext = Context::None; - s_deferredActivationTarget = 0xFF; + s_deferredActivationTarget = InvalidTarget; + s_gesture = {}; + s_hoverTargetValid = false; + s_hoverTarget = InvalidTarget; } } // namespace @@ -144,8 +203,6 @@ bool handle_fallthrough_pointer(f32 x, f32 y, Phase phase, bool touch, s32 mouse return false; } - s_clickConsumed = false; - if (!touch) { if (phase == Phase::Press) { if (!mouse_button_is_menu_confirm(mouseButton)) { @@ -174,21 +231,41 @@ bool handle_fallthrough_pointer(f32 x, f32 y, Phase phase, bool touch, s32 mouse } if (phase != Phase::Cancel) { + update_gesture_movement(x, y); set_position_from_rml(x, y); } s_state.touch = touch; switch (phase) { case Phase::Press: + clear_click_state(); + s_gesture = { + .active = true, + .startX = x, + .startY = y, + .startedAt = Clock::now(), + }; s_state.down = true; s_state.pressed = true; break; - case Phase::Release: + case Phase::Release: { + const bool shortEnough = + s_gesture.active && Clock::now() - s_gesture.startedAt <= kTapMaxDuration; + const bool stillEnough = s_gesture.active && !s_gesture.movedTooFar; + const bool targetClean = s_gesture.active && !s_gesture.crossedTarget; + s_clickContext = s_gesture.pressContext; + s_clickTarget = s_gesture.pressTarget; + s_clickTargetValid = s_gesture.pressTargetValid; + s_clickPending = shortEnough && stillEnough && targetClean; s_state.down = false; s_state.released = true; - s_state.clicked = true; + s_state.clicked = s_clickPending; + s_gesture = {}; break; + } case Phase::Cancel: + clear_click_state(); + s_gesture = {}; s_state.down = false; break; case Phase::Move: @@ -211,6 +288,12 @@ void begin_game_frame() noexcept { } void end_game_frame() noexcept { + if (s_gesture.active && s_gesture.pressTargetValid && + s_currentContext == s_gesture.pressContext && !s_hoverTargetValid) + { + s_gesture.crossedTarget = true; + } + s_lastContext = s_currentContext; s_lastDialogChoice = s_currentDialogChoice; s_lastDialogChoiceValid = s_currentDialogChoiceValid; @@ -222,6 +305,12 @@ void end_game_frame() noexcept { s_state.valid = false; } s_clickConsumed = false; + s_clickPending = false; + s_clickContext = Context::None; + s_clickTarget = InvalidTarget; + s_clickTargetValid = false; + s_hoverTargetValid = false; + s_hoverTarget = InvalidTarget; } void begin_context(Context context) noexcept { @@ -237,7 +326,11 @@ void begin_context(Context context) noexcept { s_suppressedPadHoldMask = 0; s_suppressedPadNextReadMask = 0; s_deferredActivationContext = Context::None; - s_deferredActivationTarget = 0xFF; + s_deferredActivationTarget = InvalidTarget; + s_gesture = {}; + s_hoverTargetValid = false; + s_hoverTarget = InvalidTarget; + clear_click_state(); } s_currentContext = context; @@ -259,15 +352,50 @@ const State& state() noexcept { return s_state; } +void set_hover_target(TargetId target) noexcept { + s_hoverTargetValid = true; + s_hoverTarget = target; + + if (s_gesture.active && !s_gesture.pressTargetValid && s_state.down) { + s_gesture.pressContext = s_currentContext; + s_gesture.pressTarget = target; + s_gesture.pressTargetValid = true; + } + + if (s_gesture.active && s_gesture.pressTargetValid && + (s_currentContext != s_gesture.pressContext || target != s_gesture.pressTarget)) + { + s_gesture.crossedTarget = true; + } +} + +bool click_matches_hover_target() noexcept { + if (!s_clickPending || !s_hoverTargetValid) { + return false; + } + + if (!s_clickTargetValid) { + return true; + } + + return s_currentContext == s_clickContext && s_hoverTarget == s_clickTarget; +} + bool consume_click() noexcept { - if (!s_state.clicked || s_clickConsumed) { + if (s_clickConsumed || !click_matches_hover_target()) { return false; } s_clickConsumed = true; + s_clickPending = false; + s_state.clicked = false; return true; } +bool peek_click() noexcept { + return !s_clickConsumed && click_matches_hover_target(); +} + void set_dialog_choice(u8 choice, bool clicked) noexcept { s_currentDialogChoice = choice; s_currentDialogChoiceValid = true; @@ -300,18 +428,18 @@ bool consume_dialog_click(u8& choice) noexcept { return false; } -void defer_activation(Context context, u8 target) noexcept { +void defer_activation(Context context, TargetId target) noexcept { s_deferredActivationContext = context; s_deferredActivationTarget = target; } -bool consume_deferred_activation(Context context, u8 target) noexcept { +bool consume_deferred_activation(Context context, TargetId target) noexcept { if (s_deferredActivationContext != context || s_deferredActivationTarget != target) { return false; } s_deferredActivationContext = Context::None; - s_deferredActivationTarget = 0xFF; + s_deferredActivationTarget = InvalidTarget; return true; } @@ -321,7 +449,7 @@ void clear_deferred_activation(Context context) noexcept { } s_deferredActivationContext = Context::None; - s_deferredActivationTarget = 0xFF; + s_deferredActivationTarget = InvalidTarget; } u32 suppressed_pad_buttons(u32 port) noexcept { diff --git a/src/dusk/ui/settings.cpp b/src/dusk/ui/settings.cpp index 1a21d4ae34..f4f075a6dc 100644 --- a/src/dusk/ui/settings.cpp +++ b/src/dusk/ui/settings.cpp @@ -668,7 +668,7 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) { leftPane.register_control( leftPane.add_select_button({ .key = "Graphics Backend", - .getValue = [] { return Rml::String{backend_name(configured_backend())}; }, + .getValue = [] { return Rml::String{backend_name(aurora_get_backend())}; }, .isModified = [] { return getSettings().backend.graphicsBackend.getValue() !=