diff --git a/src/imgui/invitation_prompt_layer.cpp b/src/imgui/invitation_prompt_layer.cpp index 4553a7481..228dc6b4c 100644 --- a/src/imgui/invitation_prompt_layer.cpp +++ b/src/imgui/invitation_prompt_layer.cpp @@ -14,6 +14,7 @@ #include "core/libraries/np/np_handler.h" #include "imgui/imgui_layer.h" #include "imgui/invitation_prompt_layer.h" +#include "imgui/renderer/imgui_core.h" #include "imgui/shadnet_notifications_layer.h" namespace ImGui::InvitationPrompt { @@ -33,11 +34,16 @@ struct Prompt { State state = State::Waiting; }; -constexpr std::size_t kMaxPrompts = 4; // oldest dropped beyond this +constexpr std::size_t kMaxPrompts = 4; // oldest dropped beyond this +constexpr float kFocusHoldSeconds = 0.6f; // hold Share/Back this long to focus the prompt std::mutex g_mutex; std::deque g_prompts; +bool g_focused = false; +bool g_capture_held = false; +float g_back_held_secs = 0.0f; + class InvitationPromptUI final : public ImGui::Layer { public: void Draw() override; @@ -92,6 +98,20 @@ void RunDecline(Prompt prompt) { }).detach(); } +void SetFocused(bool focused) { + if (g_focused == focused) { + return; + } + g_focused = focused; + if (focused && !g_capture_held) { + ImGui::Core::AcquireGamepadInputCapture(); + g_capture_held = true; + } else if (!focused && g_capture_held) { + ImGui::Core::ReleaseGamepadInputCapture(); + g_capture_held = false; + } +} + } // namespace void Push(s32 user_id, std::string invitation_id, std::string session_id, std::string from_npid) { @@ -117,6 +137,7 @@ void Register() { } void Unregister() { + SetFocused(false); ImGui::Layer::RemoveLayer(&g_layer); } @@ -124,28 +145,49 @@ void InvitationPromptUI::Draw() { std::vector snapshot; { std::lock_guard lock(g_mutex); - if (g_prompts.empty()) { - return; - } snapshot.assign(g_prompts.begin(), g_prompts.end()); } + if (snapshot.empty()) { + SetFocused(false); + return; + } + + if (!g_focused) { + if (ImGui::IsKeyDown(ImGuiKey_GamepadBack)) { + g_back_held_secs += ImGui::GetIO().DeltaTime; + if (g_back_held_secs >= kFocusHoldSeconds) { + g_back_held_secs = 0.0f; + SetFocused(true); + } + } else { + g_back_held_secs = 0.0f; + } + } else { + // Stage 2 -> 1: Circle (nav cancel) or Escape returns control to the game. + if (ImGui::IsKeyPressed(ImGuiKey_GamepadFaceRight, false) || + ImGui::IsKeyPressed(ImGuiKey_Escape, false)) { + SetFocused(false); + } + } + auto& io = ImGui::GetIO(); const float scale = io.DisplaySize.x / 1920.0f; - const float width = 420.0f * scale; + const float width = 440.0f * scale; const float margin = 20.0f * scale; - // Top-center, below the top edge,the transient shadNet toasts live top-right so the two - // don't collide. Mouse-interactive but NoNav so gamepad navigation stays with the game. ImGui::SetNextWindowPos(ImVec2(io.DisplaySize.x / 2.0f, margin), ImGuiCond_Always, ImVec2(0.5f, 0.0f)); ImGui::SetNextWindowSize(ImVec2(width, 0.0f), ImGuiCond_Always); - ImGui::SetNextWindowBgAlpha(0.92f); + ImGui::SetNextWindowBgAlpha(g_focused ? 0.97f : 0.85f); - const ImGuiWindowFlags flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoNav | - ImGuiWindowFlags_NoFocusOnAppearing | - ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoMove | - ImGuiWindowFlags_AlwaysAutoResize; + ImGuiWindowFlags flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoSavedSettings | + ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize; + if (g_focused) { + ImGui::SetNextWindowFocus(); + } else { + flags |= ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoFocusOnAppearing; + } if (ImGui::Begin("##invitation_prompts", nullptr, flags)) { bool first = true; @@ -167,6 +209,9 @@ void InvitationPromptUI::Draw() { if (ImGui::Button(busy ? "Joining..." : "Accept", ImVec2(120.0f * scale, 0.0f))) { RunAccept(p); } + if (g_focused && &p == snapshot.data()) { + ImGui::SetItemDefaultFocus(); // nav lands on Accept of the newest-focused prompt + } ImGui::SameLine(); if (ImGui::Button("Decline", ImVec2(120.0f * scale, 0.0f))) { RunDecline(p); @@ -174,6 +219,10 @@ void InvitationPromptUI::Draw() { ImGui::PopID(); ImGui::EndDisabled(); } + + ImGui::Spacing(); + ImGui::TextDisabled(g_focused ? "D-Pad: select Cross: confirm Circle/Esc: back to game" + : "Hold Share/Back to respond, or click"); } ImGui::End(); }