Lay out the overlay panels in a fixed 3-column scheme
Panels used to Begin at cascade positions, so they overlapped and clipped. A new apply_panel_layout() in app_chrome positions/sizes each panel from the main viewport work area (ImGuiCond_FirstUseEver, still movable): Injection left column full height (room for hook diagnostics), Controllers/Video/Audio stacked in the center column, Log right edge full height (max room for the log stream). Added a "View -> Reset layout" menu item (request_layout_reset / apply_layout_end_frame re-apply the defaults once via ImGuiCond_Always). Each panel now calls apply_panel_layout(Panel::X) instead of its own ad-hoc SetNextWindowPos/Size. Verified live: captured the host overlay -- Injection (left, full height), Controllers/Video/Audio (center stack), Log (right, full height), no overlap among the panels. x64 build + ctest green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
11
README.md
11
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.
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
|
||||
#include <mmreg.h>
|
||||
|
||||
#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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
#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());
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <windows.h>
|
||||
|
||||
#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())
|
||||
|
||||
@@ -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"))
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "ui/app_chrome.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
|
||||
#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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user