feat: Implement ROM version management and proposal approval system
- Introduced `RomVersionManager` for managing ROM snapshots, including automatic backups, manual checkpoints, and corruption detection. - Added `ProposalApprovalManager` to facilitate collaborative proposal submissions and voting, enhancing team workflows. - Updated `CollaborationPanel` to integrate version management features, allowing users to track changes and manage proposals effectively. - Enhanced documentation to reflect new functionalities and usage instructions for version management and collaboration features.
This commit is contained in:
@@ -12,7 +12,10 @@ namespace app {
|
||||
namespace gui {
|
||||
|
||||
CollaborationPanel::CollaborationPanel()
|
||||
: selected_tab_(0),
|
||||
: rom_(nullptr),
|
||||
version_mgr_(nullptr),
|
||||
approval_mgr_(nullptr),
|
||||
selected_tab_(0),
|
||||
selected_rom_sync_(-1),
|
||||
selected_snapshot_(-1),
|
||||
selected_proposal_(-1),
|
||||
@@ -45,6 +48,15 @@ CollaborationPanel::~CollaborationPanel() {
|
||||
}
|
||||
}
|
||||
|
||||
void CollaborationPanel::Initialize(
|
||||
Rom* rom,
|
||||
net::RomVersionManager* version_mgr,
|
||||
net::ProposalApprovalManager* approval_mgr) {
|
||||
rom_ = rom;
|
||||
version_mgr_ = version_mgr;
|
||||
approval_mgr_ = approval_mgr;
|
||||
}
|
||||
|
||||
void CollaborationPanel::Render(bool* p_open) {
|
||||
if (!ImGui::Begin("Collaboration", p_open, ImGuiWindowFlags_None)) {
|
||||
ImGui::End();
|
||||
@@ -59,18 +71,30 @@ void CollaborationPanel::Render(bool* p_open) {
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
if (ImGui::BeginTabItem("Snapshots")) {
|
||||
if (ImGui::BeginTabItem("Version History")) {
|
||||
selected_tab_ = 1;
|
||||
RenderVersionHistoryTab();
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
if (ImGui::BeginTabItem("Snapshots")) {
|
||||
selected_tab_ = 2;
|
||||
RenderSnapshotsTab();
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
if (ImGui::BeginTabItem("Proposals")) {
|
||||
selected_tab_ = 2;
|
||||
selected_tab_ = 3;
|
||||
RenderProposalsTab();
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
if (ImGui::BeginTabItem("🔒 Approvals")) {
|
||||
selected_tab_ = 4;
|
||||
RenderApprovalTab();
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
|
||||
@@ -440,6 +464,201 @@ ImVec4 CollaborationPanel::GetProposalStatusColor(const std::string& status) {
|
||||
return ImVec4(0.7f, 0.7f, 0.7f, 1.0f);
|
||||
}
|
||||
|
||||
void CollaborationPanel::RenderVersionHistoryTab() {
|
||||
if (!version_mgr_) {
|
||||
ImGui::TextWrapped("Version management not initialized");
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui::TextWrapped("ROM Version History & Protection");
|
||||
ImGui::Separator();
|
||||
|
||||
// Stats
|
||||
auto stats = version_mgr_->GetStats();
|
||||
ImGui::Text("Total Snapshots: %zu", stats.total_snapshots);
|
||||
ImGui::SameLine();
|
||||
ImGui::TextColored(colors_.sync_applied, "Safe Points: %zu", stats.safe_points);
|
||||
ImGui::SameLine();
|
||||
ImGui::TextColored(colors_.sync_pending, "Auto-Backups: %zu", stats.auto_backups);
|
||||
|
||||
ImGui::Text("Storage Used: %s", FormatFileSize(stats.total_storage_bytes).c_str());
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
// Toolbar
|
||||
if (ImGui::Button("💾 Create Checkpoint")) {
|
||||
auto result = version_mgr_->CreateSnapshot(
|
||||
"Manual checkpoint",
|
||||
"user",
|
||||
true);
|
||||
// TODO: Show result in UI
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("🛡️ Mark Current as Safe Point")) {
|
||||
std::string current_hash = version_mgr_->GetCurrentHash();
|
||||
// TODO: Find snapshot with this hash and mark as safe
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("🔍 Check for Corruption")) {
|
||||
auto result = version_mgr_->DetectCorruption();
|
||||
// TODO: Show result
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
// Version list
|
||||
if (ImGui::BeginChild("VersionList", ImVec2(0, 0), true)) {
|
||||
auto snapshots = version_mgr_->GetSnapshots();
|
||||
|
||||
for (size_t i = 0; i < snapshots.size(); ++i) {
|
||||
RenderVersionSnapshot(snapshots[i], i);
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
void CollaborationPanel::RenderApprovalTab() {
|
||||
if (!approval_mgr_) {
|
||||
ImGui::TextWrapped("Approval management not initialized");
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui::TextWrapped("Proposal Approval System");
|
||||
ImGui::Separator();
|
||||
|
||||
// Pending proposals that need votes
|
||||
auto pending = approval_mgr_->GetPendingProposals();
|
||||
|
||||
if (pending.empty()) {
|
||||
ImGui::TextWrapped("No proposals pending approval.");
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui::Text("Pending Proposals: %zu", pending.size());
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::BeginChild("ApprovalList", ImVec2(0, 0), true)) {
|
||||
for (size_t i = 0; i < pending.size(); ++i) {
|
||||
RenderApprovalProposal(pending[i], i);
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
void CollaborationPanel::RenderVersionSnapshot(
|
||||
const net::RomSnapshot& snapshot, int index) {
|
||||
ImGui::PushID(index);
|
||||
|
||||
// Icon based on type
|
||||
const char* icon;
|
||||
ImVec4 color;
|
||||
|
||||
if (snapshot.is_safe_point) {
|
||||
icon = "🛡️";
|
||||
color = colors_.sync_applied;
|
||||
} else if (snapshot.is_checkpoint) {
|
||||
icon = "💾";
|
||||
color = colors_.proposal_approved;
|
||||
} else {
|
||||
icon = "📝";
|
||||
color = colors_.sync_pending;
|
||||
}
|
||||
|
||||
ImGui::TextColored(color, "%s", icon);
|
||||
ImGui::SameLine();
|
||||
|
||||
// Collapsible header
|
||||
bool is_open = ImGui::TreeNode(snapshot.description.c_str());
|
||||
|
||||
if (is_open) {
|
||||
ImGui::Indent();
|
||||
|
||||
ImGui::Text("Creator: %s", snapshot.creator.c_str());
|
||||
ImGui::Text("Time: %s", FormatTimestamp(snapshot.timestamp).c_str());
|
||||
ImGui::Text("Hash: %s", snapshot.rom_hash.substr(0, 16).c_str());
|
||||
ImGui::Text("Size: %s", FormatFileSize(snapshot.compressed_size).c_str());
|
||||
|
||||
if (snapshot.is_safe_point) {
|
||||
ImGui::TextColored(colors_.sync_applied, "✓ Safe Point (Host Verified)");
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
// Actions
|
||||
if (ImGui::Button("↩️ Restore This Version")) {
|
||||
auto result = version_mgr_->RestoreSnapshot(snapshot.snapshot_id);
|
||||
// TODO: Show result
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (!snapshot.is_safe_point && ImGui::Button("🛡️ Mark as Safe")) {
|
||||
version_mgr_->MarkAsSafePoint(snapshot.snapshot_id);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (!snapshot.is_safe_point && ImGui::Button("🗑️ Delete")) {
|
||||
version_mgr_->DeleteSnapshot(snapshot.snapshot_id);
|
||||
}
|
||||
|
||||
ImGui::Unindent();
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
void CollaborationPanel::RenderApprovalProposal(
|
||||
const net::ProposalApprovalManager::ApprovalStatus& status, int index) {
|
||||
ImGui::PushID(index);
|
||||
|
||||
// Status indicator
|
||||
ImGui::TextColored(colors_.proposal_pending, "[⏳]");
|
||||
ImGui::SameLine();
|
||||
|
||||
// Proposal ID (shortened)
|
||||
std::string short_id = status.proposal_id.substr(0, 8);
|
||||
bool is_open = ImGui::TreeNode(absl::StrFormat("Proposal %s", short_id.c_str()).c_str());
|
||||
|
||||
if (is_open) {
|
||||
ImGui::Indent();
|
||||
|
||||
ImGui::Text("Created: %s", FormatTimestamp(status.created_at).c_str());
|
||||
ImGui::Text("Snapshot Before: %s", status.snapshot_before.substr(0, 8).c_str());
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::TextWrapped("Votes:");
|
||||
|
||||
for (const auto& [username, approved] : status.votes) {
|
||||
ImVec4 vote_color = approved ? colors_.proposal_approved : colors_.proposal_rejected;
|
||||
const char* vote_icon = approved ? "✓" : "✗";
|
||||
ImGui::TextColored(vote_color, " %s %s", vote_icon, username.c_str());
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
// Voting actions
|
||||
if (ImGui::Button("✓ Approve")) {
|
||||
// TODO: Send approval vote
|
||||
// approval_mgr_->VoteOnProposal(status.proposal_id, "current_user", true);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("✗ Reject")) {
|
||||
// TODO: Send rejection vote
|
||||
// approval_mgr_->VoteOnProposal(status.proposal_id, "current_user", false);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("↩️ Rollback")) {
|
||||
// Restore snapshot from before this proposal
|
||||
version_mgr_->RestoreSnapshot(status.snapshot_before);
|
||||
}
|
||||
|
||||
ImGui::Unindent();
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
} // namespace gui
|
||||
} // namespace app
|
||||
} // namespace yaze
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include <vector>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "app/net/rom_version_manager.h"
|
||||
#include "app/rom.h"
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
#ifdef YAZE_WITH_JSON
|
||||
@@ -80,6 +82,12 @@ class CollaborationPanel {
|
||||
CollaborationPanel();
|
||||
~CollaborationPanel();
|
||||
|
||||
/**
|
||||
* Initialize with ROM and version manager
|
||||
*/
|
||||
void Initialize(Rom* rom, net::RomVersionManager* version_mgr,
|
||||
net::ProposalApprovalManager* approval_mgr);
|
||||
|
||||
/**
|
||||
* Render the collaboration panel
|
||||
*/
|
||||
@@ -119,10 +127,19 @@ class CollaborationPanel {
|
||||
void RenderRomSyncTab();
|
||||
void RenderSnapshotsTab();
|
||||
void RenderProposalsTab();
|
||||
void RenderVersionHistoryTab();
|
||||
void RenderApprovalTab();
|
||||
|
||||
void RenderRomSyncEntry(const RomSyncEntry& entry, int index);
|
||||
void RenderSnapshotEntry(const SnapshotEntry& entry, int index);
|
||||
void RenderProposalEntry(const ProposalEntry& entry, int index);
|
||||
void RenderVersionSnapshot(const net::RomSnapshot& snapshot, int index);
|
||||
void RenderApprovalProposal(const net::ProposalApprovalManager::ApprovalStatus& status, int index);
|
||||
|
||||
// Integration components
|
||||
Rom* rom_;
|
||||
net::RomVersionManager* version_mgr_;
|
||||
net::ProposalApprovalManager* approval_mgr_;
|
||||
|
||||
// Tab selection
|
||||
int selected_tab_;
|
||||
|
||||
278
src/app/gui/widgets/widget_state_capture.cc
Normal file
278
src/app/gui/widgets/widget_state_capture.cc
Normal file
@@ -0,0 +1,278 @@
|
||||
#include "app/gui/widgets/widget_state_capture.h"
|
||||
|
||||
#include "absl/strings/str_format.h"
|
||||
#if defined(YAZE_ENABLE_IMGUI_TEST_ENGINE) && YAZE_ENABLE_IMGUI_TEST_ENGINE
|
||||
#include "imgui.h"
|
||||
#include "imgui_internal.h"
|
||||
#else
|
||||
#include "imgui/imgui.h"
|
||||
#endif
|
||||
#include <string>
|
||||
|
||||
#if defined(YAZE_WITH_JSON)
|
||||
#include "nlohmann/json.hpp"
|
||||
#endif
|
||||
|
||||
namespace yaze {
|
||||
namespace core {
|
||||
|
||||
#if !defined(YAZE_WITH_JSON)
|
||||
namespace {
|
||||
|
||||
std::string EscapeJsonString(const std::string& value) {
|
||||
std::string escaped;
|
||||
escaped.reserve(value.size() + 2);
|
||||
escaped.push_back('"');
|
||||
|
||||
for (unsigned char c : value) {
|
||||
switch (c) {
|
||||
case '"':
|
||||
escaped.append("\\\"");
|
||||
break;
|
||||
case '\\':
|
||||
escaped.append("\\\\");
|
||||
break;
|
||||
case '\b':
|
||||
escaped.append("\\b");
|
||||
break;
|
||||
case '\f':
|
||||
escaped.append("\\f");
|
||||
break;
|
||||
case '\n':
|
||||
escaped.append("\\n");
|
||||
break;
|
||||
case '\r':
|
||||
escaped.append("\\r");
|
||||
break;
|
||||
case '\t':
|
||||
escaped.append("\\t");
|
||||
break;
|
||||
default:
|
||||
if (c <= 0x1F) {
|
||||
escaped.append(absl::StrFormat("\\\\u%04X", static_cast<int>(c)));
|
||||
} else {
|
||||
escaped.push_back(static_cast<char>(c));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
escaped.push_back('"');
|
||||
return escaped;
|
||||
}
|
||||
|
||||
const char* BoolToJson(bool value) { return value ? "true" : "false"; }
|
||||
|
||||
std::string FormatFloat(float value) {
|
||||
// Match typical JSON formatting without trailing zeros when possible.
|
||||
return absl::StrFormat("%.4f", value);
|
||||
}
|
||||
|
||||
std::string FormatFloatCompact(float value) {
|
||||
std::string formatted = FormatFloat(value);
|
||||
|
||||
// Trim trailing zeros while keeping at least one decimal place.
|
||||
if (formatted.find('.') != std::string::npos) {
|
||||
while (!formatted.empty() && formatted.back() == '0') {
|
||||
formatted.pop_back();
|
||||
}
|
||||
if (!formatted.empty() && formatted.back() == '.') {
|
||||
formatted.push_back('0');
|
||||
}
|
||||
}
|
||||
return formatted;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
#endif // !defined(YAZE_WITH_JSON)
|
||||
|
||||
std::string CaptureWidgetState() {
|
||||
WidgetState state;
|
||||
|
||||
#if defined(YAZE_ENABLE_IMGUI_TEST_ENGINE) && YAZE_ENABLE_IMGUI_TEST_ENGINE
|
||||
// Check if ImGui context is available
|
||||
ImGuiContext* ctx = ImGui::GetCurrentContext();
|
||||
if (!ctx) {
|
||||
return R"({"error": "ImGui context not available"})";
|
||||
}
|
||||
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
// Capture frame information
|
||||
state.frame_count = ImGui::GetFrameCount();
|
||||
state.frame_rate = io.Framerate;
|
||||
|
||||
// Capture focused window
|
||||
ImGuiWindow* current = ImGui::GetCurrentWindow();
|
||||
if (current && !current->Hidden) {
|
||||
state.focused_window = current->Name;
|
||||
}
|
||||
|
||||
// Capture active widget (focused for input)
|
||||
ImGuiID active_id = ImGui::GetActiveID();
|
||||
if (active_id != 0) {
|
||||
state.focused_widget = absl::StrFormat("0x%08X", active_id);
|
||||
}
|
||||
|
||||
// Capture hovered widget
|
||||
ImGuiID hovered_id = ImGui::GetHoveredID();
|
||||
if (hovered_id != 0) {
|
||||
state.hovered_widget = absl::StrFormat("0x%08X", hovered_id);
|
||||
}
|
||||
|
||||
// Traverse visible windows
|
||||
for (ImGuiWindow* window : ctx->Windows) {
|
||||
if (window && window->Active && !window->Hidden) {
|
||||
state.visible_windows.push_back(window->Name);
|
||||
}
|
||||
}
|
||||
|
||||
// Capture open popups
|
||||
for (int i = 0; i < ctx->OpenPopupStack.Size; i++) {
|
||||
ImGuiPopupData& popup = ctx->OpenPopupStack[i];
|
||||
if (popup.Window && !popup.Window->Hidden) {
|
||||
state.open_popups.push_back(popup.Window->Name);
|
||||
}
|
||||
}
|
||||
|
||||
// Capture navigation state
|
||||
state.nav_id = ctx->NavId;
|
||||
state.nav_active = ctx->NavWindow != nullptr;
|
||||
|
||||
// Capture mouse state
|
||||
for (int i = 0; i < 5; i++) {
|
||||
state.mouse_down[i] = io.MouseDown[i];
|
||||
}
|
||||
state.mouse_pos_x = io.MousePos.x;
|
||||
state.mouse_pos_y = io.MousePos.y;
|
||||
|
||||
// Capture keyboard modifiers
|
||||
state.ctrl_pressed = io.KeyCtrl;
|
||||
state.shift_pressed = io.KeyShift;
|
||||
state.alt_pressed = io.KeyAlt;
|
||||
|
||||
#else
|
||||
// When UI test engine / ImGui internals aren't available, provide a minimal
|
||||
// payload so downstream systems still receive structured JSON. This keeps
|
||||
// builds that exclude the UI test engine (e.g., Windows release) working.
|
||||
return "{\"warning\": \"Widget state capture unavailable (UI test engine disabled)\"}";
|
||||
#endif
|
||||
|
||||
return SerializeWidgetStateToJson(state);
|
||||
}
|
||||
|
||||
std::string SerializeWidgetStateToJson(const WidgetState& state) {
|
||||
#if defined(YAZE_WITH_JSON)
|
||||
nlohmann::json j;
|
||||
|
||||
j["frame_count"] = state.frame_count;
|
||||
j["frame_rate"] = state.frame_rate;
|
||||
j["focused_window"] = state.focused_window;
|
||||
j["focused_widget"] = state.focused_widget;
|
||||
j["hovered_widget"] = state.hovered_widget;
|
||||
j["visible_windows"] = state.visible_windows;
|
||||
j["open_popups"] = state.open_popups;
|
||||
j["navigation"] = {
|
||||
{"nav_id", absl::StrFormat("0x%08X", state.nav_id)},
|
||||
{"nav_active", state.nav_active}};
|
||||
|
||||
nlohmann::json mouse_buttons;
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
mouse_buttons.push_back(state.mouse_down[i]);
|
||||
}
|
||||
|
||||
j["input"] = {
|
||||
{"mouse_buttons", mouse_buttons},
|
||||
{"mouse_pos", {state.mouse_pos_x, state.mouse_pos_y}},
|
||||
{"modifiers",
|
||||
{{"ctrl", state.ctrl_pressed},
|
||||
{"shift", state.shift_pressed},
|
||||
{"alt", state.alt_pressed}}}};
|
||||
|
||||
return j.dump(2);
|
||||
#else
|
||||
std::string json;
|
||||
json.reserve(512);
|
||||
|
||||
json.append("{\n");
|
||||
json.append(" \"frame_count\": ");
|
||||
json.append(std::to_string(state.frame_count));
|
||||
json.append(",\n");
|
||||
|
||||
json.append(" \"frame_rate\": ");
|
||||
json.append(FormatFloatCompact(state.frame_rate));
|
||||
json.append(",\n");
|
||||
|
||||
json.append(" \"focused_window\": ");
|
||||
json.append(EscapeJsonString(state.focused_window));
|
||||
json.append(",\n");
|
||||
|
||||
json.append(" \"focused_widget\": ");
|
||||
json.append(EscapeJsonString(state.focused_widget));
|
||||
json.append(",\n");
|
||||
|
||||
json.append(" \"hovered_widget\": ");
|
||||
json.append(EscapeJsonString(state.hovered_widget));
|
||||
json.append(",\n");
|
||||
|
||||
json.append(" \"visible_windows\": [");
|
||||
for (size_t i = 0; i < state.visible_windows.size(); ++i) {
|
||||
if (i > 0) {
|
||||
json.append(", ");
|
||||
}
|
||||
json.append(EscapeJsonString(state.visible_windows[i]));
|
||||
}
|
||||
json.append("],\n");
|
||||
|
||||
json.append(" \"open_popups\": [");
|
||||
for (size_t i = 0; i < state.open_popups.size(); ++i) {
|
||||
if (i > 0) {
|
||||
json.append(", ");
|
||||
}
|
||||
json.append(EscapeJsonString(state.open_popups[i]));
|
||||
}
|
||||
json.append("],\n");
|
||||
|
||||
json.append(" \"navigation\": {\n");
|
||||
json.append(" \"nav_id\": ");
|
||||
json.append(EscapeJsonString(absl::StrFormat("0x%08X", state.nav_id)));
|
||||
json.append(",\n");
|
||||
json.append(" \"nav_active\": ");
|
||||
json.append(BoolToJson(state.nav_active));
|
||||
json.append("\n },\n");
|
||||
|
||||
json.append(" \"input\": {\n");
|
||||
json.append(" \"mouse_buttons\": [");
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
if (i > 0) {
|
||||
json.append(", ");
|
||||
}
|
||||
json.append(BoolToJson(state.mouse_down[i]));
|
||||
}
|
||||
json.append("],\n");
|
||||
|
||||
json.append(" \"mouse_pos\": [");
|
||||
json.append(FormatFloatCompact(state.mouse_pos_x));
|
||||
json.append(", ");
|
||||
json.append(FormatFloatCompact(state.mouse_pos_y));
|
||||
json.append("],\n");
|
||||
|
||||
json.append(" \"modifiers\": {\n");
|
||||
json.append(" \"ctrl\": ");
|
||||
json.append(BoolToJson(state.ctrl_pressed));
|
||||
json.append(",\n");
|
||||
json.append(" \"shift\": ");
|
||||
json.append(BoolToJson(state.shift_pressed));
|
||||
json.append(",\n");
|
||||
json.append(" \"alt\": ");
|
||||
json.append(BoolToJson(state.alt_pressed));
|
||||
json.append("\n }\n");
|
||||
json.append(" }\n");
|
||||
json.append("}\n");
|
||||
|
||||
return json;
|
||||
#endif // defined(YAZE_WITH_JSON)
|
||||
}
|
||||
|
||||
} // namespace core
|
||||
} // namespace yaze
|
||||
54
src/app/gui/widgets/widget_state_capture.h
Normal file
54
src/app/gui/widgets/widget_state_capture.h
Normal file
@@ -0,0 +1,54 @@
|
||||
#ifndef YAZE_APP_GUI_WIDGETS_WIDGET_STATE_CAPTURE_H_
|
||||
#define YAZE_APP_GUI_WIDGETS_WIDGET_STATE_CAPTURE_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#if defined(YAZE_ENABLE_IMGUI_TEST_ENGINE) && YAZE_ENABLE_IMGUI_TEST_ENGINE
|
||||
#include "imgui.h"
|
||||
#include "imgui_internal.h"
|
||||
#else
|
||||
#include "imgui/imgui.h"
|
||||
#endif
|
||||
|
||||
namespace yaze {
|
||||
namespace core {
|
||||
|
||||
// Widget state snapshot for debugging test failures
|
||||
struct WidgetState {
|
||||
std::string focused_window;
|
||||
std::string focused_widget;
|
||||
std::string hovered_widget;
|
||||
std::vector<std::string> visible_windows;
|
||||
std::vector<std::string> open_popups;
|
||||
int frame_count = 0;
|
||||
float frame_rate = 0.0f;
|
||||
|
||||
// Navigation state
|
||||
ImGuiID nav_id = 0;
|
||||
bool nav_active = false;
|
||||
|
||||
// Input state
|
||||
bool mouse_down[5] = {false};
|
||||
float mouse_pos_x = 0.0f;
|
||||
float mouse_pos_y = 0.0f;
|
||||
|
||||
// Keyboard state
|
||||
bool ctrl_pressed = false;
|
||||
bool shift_pressed = false;
|
||||
bool alt_pressed = false;
|
||||
};
|
||||
|
||||
// Capture current ImGui widget state for debugging
|
||||
// Returns JSON-formatted string representing the widget hierarchy and state
|
||||
// When ImGui internals are unavailable (UI test engine disabled), returns a
|
||||
// short diagnostic JSON payload.
|
||||
std::string CaptureWidgetState();
|
||||
|
||||
// Serialize widget state to JSON format
|
||||
std::string SerializeWidgetStateToJson(const WidgetState& state);
|
||||
|
||||
} // namespace core
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_GUI_WIDGETS_WIDGET_STATE_CAPTURE_H_
|
||||
Reference in New Issue
Block a user