- Core interfaces for modular engine system - Resource management and registry system - Module system with sequential execution - ImGui-based UI implementation - Intra-process I/O communication - Data tree structures for hierarchical data - Serialization framework - Task scheduler interface - Debug engine implementation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
707 lines
28 KiB
C++
707 lines
28 KiB
C++
#pragma once
|
|
|
|
#include "IUI_Enums.h"
|
|
#include <imgui.h>
|
|
#include <imgui_impl_glfw.h>
|
|
#include <imgui_impl_opengl3.h>
|
|
#include <GLFW/glfw3.h>
|
|
#include <GL/gl.h>
|
|
|
|
#include <map>
|
|
#include <vector>
|
|
#include <string>
|
|
#include <functional>
|
|
#include <chrono>
|
|
|
|
namespace warfactory {
|
|
|
|
/**
|
|
* @brief ImGui implementation of IUI interface
|
|
*
|
|
* Provides full windowing system with docking, tabs, splits, and floating windows.
|
|
* Handles hybrid percentage + pixel sizing with automatic constraint enforcement.
|
|
*/
|
|
class ImGuiUI : public IUI {
|
|
private:
|
|
// ========================================
|
|
// CORE STATE
|
|
// ========================================
|
|
|
|
GLFWwindow* window = nullptr;
|
|
bool initialized = false;
|
|
bool should_close = false;
|
|
int frame_count = 0;
|
|
|
|
// Screen/parent sizes for percentage calculations
|
|
ImVec2 screen_size = {1400, 900};
|
|
ImVec2 previous_screen_size = {0, 0};
|
|
|
|
// ========================================
|
|
// WINDOW MANAGEMENT
|
|
// ========================================
|
|
|
|
struct WindowInfo {
|
|
std::string id;
|
|
std::string title;
|
|
std::string parent;
|
|
DockPosition dock_position = DockPosition::CENTER;
|
|
bool is_open = true;
|
|
bool is_floating = false;
|
|
bool resizable = true;
|
|
bool closeable = true;
|
|
|
|
// Size system
|
|
ImVec2 size = {400, 300};
|
|
ImVec2 min_size = {100, 100};
|
|
ImVec2 max_size = {2000, 1500};
|
|
ImVec2 position = {0, 0};
|
|
|
|
// Percentage tracking
|
|
std::string size_width_percent = "";
|
|
std::string size_height_percent = "";
|
|
std::string min_width_percent = "";
|
|
std::string min_height_percent = "";
|
|
std::string max_width_percent = "";
|
|
std::string max_height_percent = "";
|
|
|
|
// Content
|
|
DataType data_type = DataType::CUSTOM;
|
|
json content_data;
|
|
};
|
|
|
|
std::map<std::string, WindowInfo> windows;
|
|
|
|
struct DockInfo {
|
|
std::string id;
|
|
DockType type = DockType::DOCK;
|
|
DockPosition position = DockPosition::LEFT;
|
|
std::string parent;
|
|
bool collapsible = true;
|
|
bool resizable = true;
|
|
ImVec2 size = {300, 200};
|
|
ImVec2 min_size = {100, 100};
|
|
ImVec2 max_size = {1000, 800};
|
|
std::vector<std::string> child_windows;
|
|
};
|
|
|
|
std::map<std::string, DockInfo> docks;
|
|
|
|
// ========================================
|
|
// CALLBACKS
|
|
// ========================================
|
|
|
|
std::map<RequestType, std::function<void(const json&)>> request_callbacks;
|
|
std::map<std::string, std::function<void(const json&)>> custom_request_callbacks;
|
|
|
|
// ========================================
|
|
// MESSAGE SYSTEM
|
|
// ========================================
|
|
|
|
struct LogMessage {
|
|
EventLevel level;
|
|
std::string message;
|
|
std::chrono::steady_clock::time_point timestamp;
|
|
};
|
|
|
|
std::vector<LogMessage> log_messages;
|
|
static constexpr size_t MAX_LOG_MESSAGES = 100;
|
|
|
|
public:
|
|
ImGuiUI() = default;
|
|
~ImGuiUI() override { shutdown(); }
|
|
|
|
// ========================================
|
|
// LIFECYCLE IMPLEMENTATION
|
|
// ========================================
|
|
|
|
void initialize(const json& config) override {
|
|
if (initialized) return;
|
|
|
|
// Initialize GLFW
|
|
if (!glfwInit()) {
|
|
throw std::runtime_error("Failed to initialize GLFW");
|
|
}
|
|
|
|
// OpenGL 3.3 Core
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
|
|
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
|
|
|
// Create window
|
|
std::string title = config.value("title", "Warfactory ImGui UI");
|
|
auto window_size = config.value("window_size", json{{"width", 1400}, {"height", 900}});
|
|
if (window_size.is_object()) {
|
|
screen_size.x = window_size.value("width", 1400);
|
|
screen_size.y = window_size.value("height", 900);
|
|
} else {
|
|
screen_size.x = 1400;
|
|
screen_size.y = 900;
|
|
}
|
|
|
|
window = glfwCreateWindow(
|
|
static_cast<int>(screen_size.x),
|
|
static_cast<int>(screen_size.y),
|
|
title.c_str(), nullptr, nullptr
|
|
);
|
|
|
|
if (!window) {
|
|
glfwTerminate();
|
|
throw std::runtime_error("Failed to create GLFW window");
|
|
}
|
|
|
|
glfwMakeContextCurrent(window);
|
|
glfwSwapInterval(1); // VSync
|
|
|
|
// Initialize ImGui
|
|
IMGUI_CHECKVERSION();
|
|
ImGui::CreateContext();
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
|
|
// Note: Docking features depend on ImGui docking branch
|
|
// Using manual docking simulation for compatibility
|
|
|
|
// Basic style setup
|
|
ImGui::StyleColorsDark();
|
|
|
|
// Platform/Renderer backends
|
|
ImGui_ImplGlfw_InitForOpenGL(window, true);
|
|
ImGui_ImplOpenGL3_Init("#version 330 core");
|
|
|
|
initialized = true;
|
|
}
|
|
|
|
bool update() override {
|
|
if (!initialized || !window) return false;
|
|
|
|
if (glfwWindowShouldClose(window)) {
|
|
should_close = true;
|
|
return false;
|
|
}
|
|
|
|
// Update screen size for percentage calculations
|
|
int fb_width, fb_height;
|
|
glfwGetFramebufferSize(window, &fb_width, &fb_height);
|
|
ImVec2 new_screen_size = {static_cast<float>(fb_width), static_cast<float>(fb_height)};
|
|
|
|
// Detect screen size changes and recalculate if needed
|
|
if (new_screen_size.x != previous_screen_size.x || new_screen_size.y != previous_screen_size.y) {
|
|
if (frame_count > 0) { // Skip first frame (initialization)
|
|
debug("🔄 Screen size changed: " + std::to_string((int)new_screen_size.x) + "x" + std::to_string((int)new_screen_size.y));
|
|
recalculateAllSizes();
|
|
}
|
|
previous_screen_size = screen_size;
|
|
}
|
|
screen_size = new_screen_size;
|
|
|
|
frame_count++;
|
|
|
|
// Poll events
|
|
glfwPollEvents();
|
|
|
|
// Start ImGui frame
|
|
ImGui_ImplOpenGL3_NewFrame();
|
|
ImGui_ImplGlfw_NewFrame();
|
|
ImGui::NewFrame();
|
|
|
|
// Render all windows
|
|
renderAllWindows();
|
|
|
|
// Render ImGui
|
|
ImGui::Render();
|
|
|
|
// OpenGL rendering
|
|
glViewport(0, 0, static_cast<int>(screen_size.x), static_cast<int>(screen_size.y));
|
|
glClearColor(0.45f, 0.55f, 0.60f, 1.00f);
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
|
|
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
|
|
|
glfwSwapBuffers(window);
|
|
|
|
return !should_close;
|
|
}
|
|
|
|
void shutdown() override {
|
|
if (!initialized) return;
|
|
|
|
if (window) {
|
|
ImGui_ImplOpenGL3_Shutdown();
|
|
ImGui_ImplGlfw_Shutdown();
|
|
ImGui::DestroyContext();
|
|
|
|
glfwDestroyWindow(window);
|
|
glfwTerminate();
|
|
window = nullptr;
|
|
}
|
|
|
|
initialized = false;
|
|
}
|
|
|
|
private:
|
|
// ========================================
|
|
// SIZE CALCULATION HELPERS
|
|
// ========================================
|
|
|
|
/**
|
|
* @brief Parse size value - handles both pixels and percentages
|
|
*/
|
|
float parseSize(const json& size_value, float parent_size, float default_size) {
|
|
try {
|
|
if (size_value.is_number()) {
|
|
return size_value.get<float>();
|
|
}
|
|
|
|
if (size_value.is_string()) {
|
|
std::string size_str = size_value.get<std::string>();
|
|
if (!size_str.empty() && size_str.back() == '%') {
|
|
float percent = std::stof(size_str.substr(0, size_str.length() - 1));
|
|
return (percent / 100.0f) * parent_size;
|
|
} else {
|
|
// String but not percentage - try to parse as number
|
|
return std::stof(size_str);
|
|
}
|
|
}
|
|
} catch (...) {
|
|
// Any JSON or parsing error - return default
|
|
}
|
|
|
|
// Neither number nor string or error - return default
|
|
return default_size;
|
|
}
|
|
|
|
/**
|
|
* @brief Calculate effective size with hybrid constraints
|
|
*/
|
|
ImVec2 calculateEffectiveSize(const WindowInfo& win, ImVec2 parent_size) {
|
|
// Use already parsed sizes (converted in showData)
|
|
float target_width = win.size.x;
|
|
float target_height = win.size.y;
|
|
|
|
// Calculate constraint bounds
|
|
float min_width = win.min_size.x;
|
|
float min_height = win.min_size.y;
|
|
float max_width = win.max_size.x;
|
|
float max_height = win.max_size.y;
|
|
|
|
// Apply constraints (clamp)
|
|
float final_width = std::max(min_width, std::min(target_width, max_width));
|
|
float final_height = std::max(min_height, std::min(target_height, max_height));
|
|
|
|
return {final_width, final_height};
|
|
}
|
|
|
|
/**
|
|
* @brief Calculate window position based on docking
|
|
*/
|
|
ImVec2 calculateDockedPosition(const WindowInfo& win, ImVec2 size) {
|
|
if (win.parent.empty() || win.is_floating) {
|
|
// For windows without parent, use explicit position or calculate smart default
|
|
debug("🔍 Checking position for '" + win.id + "': pos=" +
|
|
std::to_string(win.position.x) + "," + std::to_string(win.position.y) +
|
|
" floating=" + (win.is_floating ? "true" : "false"));
|
|
|
|
// Only use explicit position if it was actually set by user (not just default values)
|
|
if (win.position.x > 10 && win.position.y > 10) {
|
|
debug("📌 Using explicit position for '" + win.id + "'");
|
|
return win.position; // Use explicit position
|
|
} else {
|
|
// Simple approach: use actual window sizes from economy_main window
|
|
float left_edge_end = 252; // Real end of economy_main (we know it's 252px wide)
|
|
float top_edge_end = 88; // Real end of toolbar + margin
|
|
|
|
// Find the right sidebar start by looking for info_panel_main
|
|
float right_edge_start = 1050; // We know info_panel starts at 1050px
|
|
|
|
debug("🔧 Simple positioning for window '" + win.id + "': left_end=" +
|
|
std::to_string(left_edge_end) + "px, right_start=" +
|
|
std::to_string(right_edge_start) + "px, top_end=" +
|
|
std::to_string(top_edge_end) + "px");
|
|
|
|
// Position directly against the real edge of existing windows
|
|
float x = left_edge_end; // Directly touching end of left sidebar (252px)
|
|
float y = top_edge_end; // Directly below toolbar (88px)
|
|
|
|
// If window would overlap with right sidebar, push it left to touch right edge
|
|
if (x + size.x > right_edge_start) {
|
|
x = right_edge_start - size.x; // Touch right sidebar windows
|
|
}
|
|
|
|
debug("🎯 Calculated position for '" + win.id + "': " +
|
|
std::to_string(x) + "," + std::to_string(y) +
|
|
" (touching real window edges)");
|
|
|
|
return {x, y};
|
|
}
|
|
}
|
|
|
|
// Find parent dock
|
|
auto dock_it = docks.find(win.parent);
|
|
if (dock_it == docks.end()) {
|
|
return {0, 0}; // Parent dock not found
|
|
}
|
|
|
|
const DockInfo& dock = dock_it->second;
|
|
|
|
// Calculate dock area based on position
|
|
switch (dock.position) {
|
|
case DockPosition::LEFT:
|
|
return {0, 80}; // Left edge but below toolbar (72px + margin)
|
|
case DockPosition::RIGHT:
|
|
return {screen_size.x - dock.size.x, 80}; // Right edge but below toolbar
|
|
case DockPosition::TOP:
|
|
// Top edge - if dock spans full width, start at 0, else offset
|
|
if (dock.size.x >= screen_size.x * 0.9f) {
|
|
return {0, 0}; // Full width toolbar starts at screen edge
|
|
} else {
|
|
return {280, 0}; // Partial width toolbar starts after sidebar
|
|
}
|
|
case DockPosition::BOTTOM:
|
|
return {0, screen_size.y - dock.size.y}; // Bottom edge
|
|
case DockPosition::CENTER:
|
|
default:
|
|
return {screen_size.x * 0.5f - size.x * 0.5f, screen_size.y * 0.5f - size.y * 0.5f}; // Center
|
|
}
|
|
}
|
|
|
|
// ========================================
|
|
// RECALCULATION METHODS
|
|
// ========================================
|
|
|
|
void recalculateAllSizes() {
|
|
// Recalculate dock sizes
|
|
for (auto& [dock_id, dock] : docks) {
|
|
// Recalculate dock size if it uses percentages
|
|
recalculateDockSize(dock);
|
|
}
|
|
|
|
// Recalculate window sizes
|
|
for (auto& [window_id, win] : windows) {
|
|
recalculateWindowSize(win);
|
|
}
|
|
}
|
|
|
|
void recalculateDockSize(DockInfo& dock) {
|
|
// Re-parse dock size with new screen size
|
|
// This would need the original JSON config, for now just log
|
|
debug("📐 Recalculating dock: " + dock.id);
|
|
// TODO: Store original percentage strings to recalculate properly
|
|
}
|
|
|
|
void recalculateWindowSize(WindowInfo& win) {
|
|
// Re-parse window size with new screen/parent sizes
|
|
debug("📐 Recalculating window: " + win.id);
|
|
|
|
// Recalculate width if percentage
|
|
if (!win.size_width_percent.empty()) {
|
|
float parent_width = screen_size.x;
|
|
if (!win.parent.empty() && docks.find(win.parent) != docks.end()) {
|
|
parent_width = docks[win.parent].size.x;
|
|
}
|
|
win.size.x = parseSize(win.size_width_percent, parent_width, 400);
|
|
}
|
|
|
|
// Recalculate height if percentage
|
|
if (!win.size_height_percent.empty()) {
|
|
float parent_height = screen_size.y;
|
|
if (!win.parent.empty() && docks.find(win.parent) != docks.end()) {
|
|
parent_height = docks[win.parent].size.y;
|
|
}
|
|
win.size.y = parseSize(win.size_height_percent, parent_height, 300);
|
|
}
|
|
}
|
|
|
|
// ========================================
|
|
// RENDERING IMPLEMENTATION
|
|
// ========================================
|
|
|
|
void renderAllWindows() {
|
|
// Log screen size for debugging (only first frame to avoid spam)
|
|
if (frame_count == 1) {
|
|
debug("🖥️ Screen Size: " + std::to_string((int)screen_size.x) + "x" + std::to_string((int)screen_size.y) + "px");
|
|
info("🏗️ Manual docking system active (simulated docking layout)");
|
|
}
|
|
|
|
for (auto& [window_id, win] : windows) {
|
|
if (!win.is_open) continue;
|
|
|
|
if (frame_count <= 5) { // Log first 5 frames for each window
|
|
debug("🪟 Window: " + window_id + " (" + win.title + ")");
|
|
debug(" 📐 Target Size: " + std::to_string((int)win.size.x) + "x" + std::to_string((int)win.size.y) + "px");
|
|
debug(" 📏 Size %: width='" + win.size_width_percent + "' height='" + win.size_height_percent + "'");
|
|
debug(" ⚖️ Constraints: min=" + std::to_string((int)win.min_size.x) + "x" + std::to_string((int)win.min_size.y) +
|
|
" max=" + std::to_string((int)win.max_size.x) + "x" + std::to_string((int)win.max_size.y));
|
|
debug(" 🔗 Docking: parent='" + win.parent + "' position=" + std::to_string((int)win.dock_position));
|
|
}
|
|
|
|
// Calculate effective size with constraints
|
|
ImVec2 effective_size = calculateEffectiveSize(win, screen_size);
|
|
if (frame_count <= 5) {
|
|
debug(" ✅ Effective Size: " + std::to_string((int)effective_size.x) + "x" + std::to_string((int)effective_size.y) + "px");
|
|
}
|
|
|
|
// Set window constraints
|
|
ImGui::SetNextWindowSizeConstraints(win.min_size, win.max_size);
|
|
|
|
// Set window size
|
|
if (win.is_floating) {
|
|
// For floating windows, force initial size and position
|
|
ImGuiCond size_condition = (frame_count <= 3) ? ImGuiCond_Always : ImGuiCond_FirstUseEver;
|
|
ImGui::SetNextWindowSize(effective_size, size_condition);
|
|
|
|
// Calculate smart position that avoids dock overlaps
|
|
ImVec2 floating_position = calculateDockedPosition(win, effective_size);
|
|
ImGuiCond position_condition = (frame_count <= 3) ? ImGuiCond_Always : ImGuiCond_FirstUseEver;
|
|
ImGui::SetNextWindowPos(floating_position, position_condition);
|
|
|
|
if (frame_count <= 5) {
|
|
debug(" 🎈 Floating Position: " + std::to_string((int)floating_position.x) + "," + std::to_string((int)floating_position.y));
|
|
}
|
|
} else {
|
|
// For docked windows, calculate position and force it during initial frames
|
|
ImVec2 dock_position = calculateDockedPosition(win, effective_size);
|
|
|
|
ImGuiCond condition = (frame_count <= 3) ? ImGuiCond_Always : ImGuiCond_FirstUseEver;
|
|
ImGui::SetNextWindowSize(effective_size, condition);
|
|
ImGui::SetNextWindowPos(dock_position, condition);
|
|
|
|
if (frame_count <= 5) {
|
|
debug(" 📍 Docked Position: " + std::to_string((int)dock_position.x) + "," + std::to_string((int)dock_position.y));
|
|
}
|
|
}
|
|
|
|
// Window flags
|
|
ImGuiWindowFlags flags = ImGuiWindowFlags_None;
|
|
if (!win.resizable) flags |= ImGuiWindowFlags_NoResize;
|
|
|
|
// Render window
|
|
if (ImGui::Begin(win.title.c_str(), win.closeable ? &win.is_open : nullptr, flags)) {
|
|
// Log actual ImGui window size after rendering (first 5 frames only)
|
|
if (frame_count <= 5) {
|
|
ImVec2 current_size = ImGui::GetWindowSize();
|
|
ImVec2 current_pos = ImGui::GetWindowPos();
|
|
debug(" 🎯 ImGui Actual: pos=" + std::to_string((int)current_pos.x) + "," + std::to_string((int)current_pos.y) +
|
|
" size=" + std::to_string((int)current_size.x) + "x" + std::to_string((int)current_size.y) + "px");
|
|
}
|
|
|
|
renderWindowContent(win);
|
|
}
|
|
ImGui::End();
|
|
}
|
|
}
|
|
|
|
void renderWindowContent(const WindowInfo& win) {
|
|
switch (win.data_type) {
|
|
case DataType::ECONOMY:
|
|
renderEconomyContent(win.content_data);
|
|
break;
|
|
case DataType::MAP:
|
|
renderMapContent(win.content_data);
|
|
break;
|
|
case DataType::INVENTORY:
|
|
renderInventoryContent(win.content_data);
|
|
break;
|
|
case DataType::CONSOLE:
|
|
renderConsoleContent(win.content_data);
|
|
break;
|
|
case DataType::PERFORMANCE:
|
|
renderPerformanceContent(win.content_data);
|
|
break;
|
|
case DataType::COMPANIES:
|
|
renderCompaniesContent(win.content_data);
|
|
break;
|
|
case DataType::ALERTS:
|
|
renderAlertsContent(win.content_data);
|
|
break;
|
|
case DataType::SETTINGS:
|
|
renderSettingsContent(win.content_data);
|
|
break;
|
|
default:
|
|
renderGenericContent(win.content_data);
|
|
break;
|
|
}
|
|
}
|
|
|
|
public:
|
|
// ========================================
|
|
// IUI INTERFACE IMPLEMENTATION - DATA DISPLAY
|
|
// ========================================
|
|
|
|
void showData(DataType dataType, const json& data) override {
|
|
// Extract window configuration
|
|
json window_config = data.value("window", json{});
|
|
json content = data.value("content", data);
|
|
|
|
// Generate ID if not provided
|
|
std::string window_id = window_config.value("id", "window_" + std::to_string(windows.size()));
|
|
|
|
// Create or update window info
|
|
WindowInfo& win = windows[window_id];
|
|
win.id = window_id;
|
|
win.title = window_config.value("title", toString(dataType));
|
|
win.data_type = dataType;
|
|
win.content_data = content;
|
|
win.is_open = true;
|
|
|
|
// Parse parent first (needed for size calculations)
|
|
win.parent = window_config.value("parent", "");
|
|
|
|
// Parse size configuration with percentage support
|
|
if (window_config.contains("size")) {
|
|
auto size_config = window_config["size"];
|
|
if (size_config.is_object()) {
|
|
if (size_config.contains("width")) {
|
|
auto width_val = size_config["width"];
|
|
if (width_val.is_string()) {
|
|
win.size_width_percent = width_val.get<std::string>();
|
|
debug("🔧 Processing width percentage '" + win.size_width_percent +
|
|
"' for window '" + win.id + "' with parent='" + win.parent + "'");
|
|
|
|
// Calculate parent size for percentage - use dock size if docked
|
|
float parent_width = screen_size.x;
|
|
if (!win.parent.empty() && docks.find(win.parent) != docks.end()) {
|
|
parent_width = docks[win.parent].size.x;
|
|
debug("🔍 Found parent dock '" + win.parent + "' with width=" +
|
|
std::to_string((int)parent_width) + "px");
|
|
} else if (!win.parent.empty()) {
|
|
debug("❌ Parent dock '" + win.parent + "' not found! Using screen width.");
|
|
}
|
|
|
|
win.size.x = parseSize(width_val, parent_width, 400);
|
|
} else if (width_val.is_number()) {
|
|
win.size.x = width_val.get<float>();
|
|
} else {
|
|
win.size.x = 400; // Default fallback
|
|
}
|
|
}
|
|
|
|
if (size_config.contains("height")) {
|
|
auto height_val = size_config["height"];
|
|
if (height_val.is_string()) {
|
|
win.size_height_percent = height_val.get<std::string>();
|
|
float parent_height = screen_size.y;
|
|
if (!win.parent.empty() && docks.find(win.parent) != docks.end()) {
|
|
parent_height = docks[win.parent].size.y;
|
|
}
|
|
win.size.y = parseSize(height_val, parent_height, 300);
|
|
} else if (height_val.is_number()) {
|
|
win.size.y = height_val.get<float>();
|
|
} else {
|
|
win.size.y = 300; // Default fallback
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Parse constraints
|
|
if (window_config.contains("min_size")) {
|
|
auto min_config = window_config["min_size"];
|
|
if (min_config.is_object()) {
|
|
if (min_config.contains("width")) {
|
|
win.min_size.x = parseSize(min_config["width"], screen_size.x, 100);
|
|
} else {
|
|
win.min_size.x = 100;
|
|
}
|
|
if (min_config.contains("height")) {
|
|
win.min_size.y = parseSize(min_config["height"], screen_size.y, 100);
|
|
} else {
|
|
win.min_size.y = 100;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (window_config.contains("max_size")) {
|
|
auto max_config = window_config["max_size"];
|
|
if (max_config.is_object()) {
|
|
if (max_config.contains("width")) {
|
|
win.max_size.x = parseSize(max_config["width"], screen_size.x, 2000);
|
|
} else {
|
|
win.max_size.x = 2000;
|
|
}
|
|
if (max_config.contains("height")) {
|
|
win.max_size.y = parseSize(max_config["height"], screen_size.y, 1500);
|
|
} else {
|
|
win.max_size.y = 1500;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Parse other properties
|
|
win.is_floating = window_config.value("floating", false);
|
|
win.resizable = window_config.value("resizable", true);
|
|
win.closeable = window_config.value("closeable", true);
|
|
|
|
// Parse dock position if specified
|
|
if (window_config.contains("dock")) {
|
|
std::string dock_str = window_config["dock"].get<std::string>();
|
|
if (dock_str == "left") win.dock_position = DockPosition::LEFT;
|
|
else if (dock_str == "right") win.dock_position = DockPosition::RIGHT;
|
|
else if (dock_str == "top") win.dock_position = DockPosition::TOP;
|
|
else if (dock_str == "bottom") win.dock_position = DockPosition::BOTTOM;
|
|
else if (dock_str == "tab") win.dock_position = DockPosition::CENTER; // tabs go in center
|
|
else win.dock_position = DockPosition::CENTER;
|
|
}
|
|
|
|
if (window_config.contains("position")) {
|
|
auto pos = window_config["position"];
|
|
if (pos.is_object()) {
|
|
win.position.x = pos.value("x", 0);
|
|
win.position.y = pos.value("y", 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
void showDataCustom(const std::string& customType, const json& data) override {
|
|
// Treat as generic data with custom type in title
|
|
json modified_data = data;
|
|
if (!modified_data.contains("window")) {
|
|
modified_data["window"] = json{};
|
|
}
|
|
if (!modified_data["window"].contains("title")) {
|
|
modified_data["window"]["title"] = customType;
|
|
}
|
|
|
|
showData(DataType::CUSTOM, modified_data);
|
|
}
|
|
|
|
// ========================================
|
|
// IUI INTERFACE IMPLEMENTATION - REQUESTS & EVENTS
|
|
// ========================================
|
|
|
|
void onRequest(RequestType requestType, std::function<void(const json&)> callback) override;
|
|
void onRequestCustom(const std::string& customType, std::function<void(const json&)> callback) override;
|
|
void showEvent(EventLevel level, const std::string& message) override;
|
|
|
|
// ========================================
|
|
// WINDOW MANAGEMENT IMPLEMENTATION
|
|
// ========================================
|
|
|
|
void createDock(const std::string& dockId, DockType type, DockPosition position, const json& config = {}) override;
|
|
void createSplit(const std::string& dockId, Orientation orientation, const json& config = {}) override;
|
|
void closeWindow(const std::string& windowId) override;
|
|
void focusWindow(const std::string& windowId) override;
|
|
|
|
// ========================================
|
|
// STATE MANAGEMENT
|
|
// ========================================
|
|
|
|
json getState() const override;
|
|
void setState(const json& state) override;
|
|
|
|
private:
|
|
// ========================================
|
|
// CONTENT RENDERING IMPLEMENTATIONS
|
|
// ========================================
|
|
|
|
void renderEconomyContent(const json& content);
|
|
void renderMapContent(const json& content);
|
|
void renderInventoryContent(const json& content);
|
|
void renderConsoleContent(const json& content);
|
|
void renderPerformanceContent(const json& content);
|
|
void renderCompaniesContent(const json& content);
|
|
void renderAlertsContent(const json& content);
|
|
void renderSettingsContent(const json& content);
|
|
void renderGenericContent(const json& content);
|
|
void renderLogConsole();
|
|
};
|
|
|
|
} // namespace warfactory
|