diff --git a/README.md b/README.md index 78d1af8..d5ca9cd 100644 --- a/README.md +++ b/README.md @@ -75,17 +75,6 @@ is removed from this list once done — so the top item is always next. The self-verifiable tooling / UI / input items come first; the game-pipeline items that need a real game (and Remote Play) to fully validate come last. -- **Auto-size and lay out the overlay windows so none need manual resizing.** Panels - currently `Begin` at default cascade positions, so they overlap and clip. Give each - `ImGuiWindowFlags_AlwaysAutoResize` and an initial position computed from - `ImGui::GetMainViewport()->WorkPos/WorkSize`, applied with `ImGuiCond_FirstUseEver` - (still movable), plus a **View → Reset layout** menu item that re-applies it. Target - layout: **Injection** left/top (room to grow downward for hook diagnostics); - **Controllers** top-center; **Video mirror** center, below Controllers; **Audio - mirror** below Video; **Log** right edge, full height (most room for the log - stream). Auto-resize fits these because they're all control/debug panels — the live - mirror image is drawn to the whole host window *behind* the overlay, not inside a - panel. - **Fix the Audio panel "live" column.** It overlays a green dot and grey "idle" because liveness is recomputed each frame from the per-stream `frames_rendered` delta, which is zero on most frames (buffers release in bursts), so it flickers. diff --git a/host/src/audio_panel.cpp b/host/src/audio_panel.cpp index 2fe70b3..8086688 100644 --- a/host/src/audio_panel.cpp +++ b/host/src/audio_panel.cpp @@ -6,6 +6,8 @@ #include +#include "ui/app_chrome.hpp" + namespace coop { @@ -38,8 +40,7 @@ void AudioPanel::draw_ui(const HookStatusView& status, bool debug_details) GetWindowThreadProcessId(target_, &pid); } - ImGui::SetNextWindowPos(ImVec2(460, 300), ImGuiCond_FirstUseEver); - ImGui::SetNextWindowSize(ImVec2(360, 0), ImGuiCond_FirstUseEver); + apply_panel_layout(Panel::Audio); ImGui::Begin("Audio mirror"); ImGui::BeginDisabled(!have_target); diff --git a/host/src/capture_panel.cpp b/host/src/capture_panel.cpp index a72ccb6..0afc168 100644 --- a/host/src/capture_panel.cpp +++ b/host/src/capture_panel.cpp @@ -4,6 +4,7 @@ #include "imgui.h" #include "injection_panel.hpp" +#include "ui/app_chrome.hpp" namespace coop { @@ -23,8 +24,7 @@ bool CapturePanel::init(ID3D11Device* device) void CapturePanel::draw_ui(const FrameStats& stats) { - ImGui::SetNextWindowPos(ImVec2(460, 40), ImGuiCond_FirstUseEver); - ImGui::SetNextWindowSize(ImVec2(360, 0), ImGuiCond_FirstUseEver); + apply_panel_layout(Panel::Video); ImGui::Begin("Video mirror"); const unsigned long hook_pid = injection_ != nullptr ? injection_->target_pid() : 0; diff --git a/host/src/controllers_panel.cpp b/host/src/controllers_panel.cpp index 4aed542..76cfe88 100644 --- a/host/src/controllers_panel.cpp +++ b/host/src/controllers_panel.cpp @@ -2,6 +2,8 @@ #include +#include "ui/app_chrome.hpp" + namespace coop { @@ -67,8 +69,7 @@ void draw_pad(int index, const PadInfo& pad, bool debug_details) void ControllersPanel::draw(const InputSource& input, const HookStatusView& status, bool debug_details) { - ImGui::SetNextWindowPos(ImVec2(24, 40), ImGuiCond_FirstUseEver); - ImGui::SetNextWindowSize(ImVec2(420, 0), ImGuiCond_FirstUseEver); + apply_panel_layout(Panel::Controllers); ImGui::Begin("Controllers"); ImGui::Text("Input backend: %s", input.name()); diff --git a/host/src/injection_panel.cpp b/host/src/injection_panel.cpp index d869a52..d483a30 100644 --- a/host/src/injection_panel.cpp +++ b/host/src/injection_panel.cpp @@ -5,6 +5,7 @@ #include #include "inject/injector.hpp" +#include "ui/app_chrome.hpp" namespace coop { @@ -472,8 +473,7 @@ void InjectionPanel::draw_hook_status(bool debug_details) void InjectionPanel::draw(bool debug_details) { - ImGui::SetNextWindowPos(ImVec2(24, 360), ImGuiCond_FirstUseEver); - ImGui::SetNextWindowSize(ImVec2(420, 380), ImGuiCond_FirstUseEver); + apply_panel_layout(Panel::Injection); ImGui::Begin("Injection"); if (server_.running()) diff --git a/host/src/log_panel.cpp b/host/src/log_panel.cpp index bbff3bb..8daff56 100644 --- a/host/src/log_panel.cpp +++ b/host/src/log_panel.cpp @@ -6,6 +6,7 @@ #include "imgui.h" #include "injection_panel.hpp" +#include "ui/app_chrome.hpp" namespace coop { @@ -34,8 +35,7 @@ void LogPanel::pull(InjectionPanel& injection) void LogPanel::draw() { - ImGui::SetNextWindowPos(ImVec2(24, 760), ImGuiCond_FirstUseEver); - ImGui::SetNextWindowSize(ImVec2(720, 240), ImGuiCond_FirstUseEver); + apply_panel_layout(Panel::Log); ImGui::Begin("Log"); if (ImGui::Button("Clear")) diff --git a/host/src/main.cpp b/host/src/main.cpp index 5d02598..98418da 100644 --- a/host/src/main.cpp +++ b/host/src/main.cpp @@ -191,6 +191,7 @@ int run() { draw_overlay_hidden_hint(ImGui::GetTime() - overlay_hidden_at); } + coop::apply_layout_end_frame(); // clear the one-shot "Reset layout" force RECT client = {}; GetClientRect(window.hwnd(), &client); diff --git a/host/src/ui/app_chrome.cpp b/host/src/ui/app_chrome.cpp index ae9beec..478cfca 100644 --- a/host/src/ui/app_chrome.cpp +++ b/host/src/ui/app_chrome.cpp @@ -1,5 +1,6 @@ #include "ui/app_chrome.hpp" +#include #include #include "imgui.h" @@ -7,6 +8,72 @@ namespace coop { +namespace +{ +// Set by "View -> Reset layout"; true for the one frame in which panels re-apply +// their default position/size, then cleared by apply_layout_end_frame(). +bool g_layout_reset = false; +} // namespace + +void request_layout_reset() +{ + g_layout_reset = true; +} + +void apply_layout_end_frame() +{ + g_layout_reset = false; +} + +void apply_panel_layout(Panel panel) +{ + const ImGuiViewport* vp = ImGui::GetMainViewport(); + const ImVec2 o = vp->WorkPos; // below the main menu bar + const ImVec2 s = vp->WorkSize; + const float m = 12.0f; // outer margin + const float gap = 10.0f; // between panels + + const float left_w = 440.0f; // Injection (window list + hook diagnostics) + const float right_w = 380.0f; // Log + const float center_x = o.x + m + left_w + gap; + const float right_x = o.x + s.x - right_w - m; + const float center_w = std::max(360.0f, right_x - gap - center_x); + const float full_h = std::max(200.0f, s.y - 2.0f * m); + const float ctrl_h = 210.0f; // Controllers + const float vid_h = 250.0f; // Video mirror + const float audio_y = o.y + m + ctrl_h + gap + vid_h + gap; + const float bottom = o.y + m + full_h; + + ImVec2 pos, size; + switch (panel) + { + case Panel::Injection: + pos = ImVec2(o.x + m, o.y + m); + size = ImVec2(left_w, full_h); + break; + case Panel::Controllers: + pos = ImVec2(center_x, o.y + m); + size = ImVec2(center_w, ctrl_h); + break; + case Panel::Video: + pos = ImVec2(center_x, o.y + m + ctrl_h + gap); + size = ImVec2(center_w, vid_h); + break; + case Panel::Audio: + pos = ImVec2(center_x, audio_y); + size = ImVec2(center_w, std::max(180.0f, bottom - audio_y)); + break; + case Panel::Log: + pos = ImVec2(right_x, o.y + m); + size = ImVec2(right_w, full_h); + break; + } + + const ImGuiCond cond = g_layout_reset ? ImGuiCond_Always : ImGuiCond_FirstUseEver; + ImGui::SetNextWindowPos(pos, cond); + ImGui::SetNextWindowSize(size, cond); +} + float draw_main_menu_bar(UiState& ui, const FrameStats& stats) { float height = 0.0f; @@ -27,6 +94,10 @@ float draw_main_menu_bar(UiState& ui, const FrameStats& stats) ImGui::MenuItem("Log", nullptr, &ui.show_log); ImGui::Separator(); ImGui::MenuItem("Debug details", nullptr, &ui.debug_details); + if (ImGui::MenuItem("Reset layout")) + { + request_layout_reset(); + } ImGui::EndMenu(); } diff --git a/host/src/ui/app_chrome.hpp b/host/src/ui/app_chrome.hpp index 4620a41..588de8c 100644 --- a/host/src/ui/app_chrome.hpp +++ b/host/src/ui/app_chrome.hpp @@ -22,6 +22,27 @@ struct UiState bool debug_details = false; // off = general status; on = full diagnostics }; +// The overlay panels, for the shared default layout below. +enum class Panel +{ + Injection, // left column, full height (room for hook diagnostics) + Controllers, // center column, top + Video, // center column, below Controllers + Audio, // center column, below Video + Log, // right column, full height (max room for the log stream) +}; + +// Position + size the next ImGui window per the default 3-column layout, so panels +// open without overlapping or needing a manual resize. First-use only, unless a +// layout reset was just requested (then it re-applies once). Call right before the +// panel's ImGui::Begin. +void apply_panel_layout(Panel panel); + +// "View -> Reset layout" sets this; it forces apply_panel_layout to re-place every +// panel on the next frame. apply_layout_end_frame() clears it after the panels draw. +void request_layout_reset(); +void apply_layout_end_frame(); + // Rolling frame-timing over a ~1 s window, recomputed each window so the status // bar can show a stable FPS plus the min/max frame time (jitter) underneath it. class FrameStats