From a57f0875f8394cca6a64ef5eb5bae7be150ee769 Mon Sep 17 00:00:00 2001 From: John McCardle Date: Tue, 6 Jan 2026 22:42:20 -0500 Subject: [PATCH] Code editor window, lockable positions; send console output to the code editor to select and cut/copy output. Closes #170 --- src/ImGuiConsole.cpp | 136 ++++++++++++++++++++++++++++++++++++++++++- src/ImGuiConsole.h | 10 +++- 2 files changed, 142 insertions(+), 4 deletions(-) diff --git a/src/ImGuiConsole.cpp b/src/ImGuiConsole.cpp index 24b793c..931acbf 100644 --- a/src/ImGuiConsole.cpp +++ b/src/ImGuiConsole.cpp @@ -3,6 +3,8 @@ #include "McRFPy_API.h" #include #include +#include +#include // Static member initialization bool ImGuiConsole::enabled = true; @@ -141,23 +143,92 @@ _stderr_val = _console_stderr.getvalue() void ImGuiConsole::render() { if (!visible || !enabled) return; + // Render the code editor window if visible + if (editorVisible) { + renderCodeEditor(); + } + // Set up console window ImGuiIO& io = ImGui::GetIO(); ImGui::SetNextWindowSize(ImVec2(io.DisplaySize.x, io.DisplaySize.y * 0.4f), ImGuiCond_FirstUseEver); ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_FirstUseEver); - ImGuiWindowFlags flags = ImGuiWindowFlags_NoCollapse; + ImGuiWindowFlags flags = ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_MenuBar; + if (consoleLocked) flags |= ImGuiWindowFlags_NoMove; if (!ImGui::Begin("Console", &visible, flags)) { ImGui::End(); return; } - // Output area (scrollable, no horizontal scrollbar - use word wrap) + // Apply font scale + ImGui::SetWindowFontScale(fontScale); + + // Menu bar with toolbar buttons + if (ImGui::BeginMenuBar()) { + // Font size controls + if (ImGui::SmallButton("-")) { + fontScale = std::max(0.5f, fontScale - 0.1f); + } + if (ImGui::IsItemHovered()) ImGui::SetTooltip("Decrease text size"); + + ImGui::Text("%.0f%%", fontScale * 100); + + if (ImGui::SmallButton("+")) { + fontScale = std::min(2.0f, fontScale + 0.1f); + } + if (ImGui::IsItemHovered()) ImGui::SetTooltip("Increase text size"); + + ImGui::Separator(); + + // Clear console output + if (ImGui::SmallButton("Clr")) { + outputHistory.clear(); + } + if (ImGui::IsItemHovered()) ImGui::SetTooltip("Clear console output"); + + // Send console output to code editor + if (ImGui::SmallButton("Snd")) { + // Build text from output history and copy to code editor + std::string allOutput; + for (const auto& line : outputHistory) { + allOutput += line.text; + allOutput += "\n"; + } + // Copy to code editor buffer (truncate if too long) + size_t copyLen = std::min(allOutput.size(), sizeof(codeBuffer) - 1); + memcpy(codeBuffer, allOutput.c_str(), copyLen); + codeBuffer[copyLen] = '\0'; + editorVisible = true; // Show editor when sending + } + if (ImGui::IsItemHovered()) ImGui::SetTooltip("Send console output to code editor"); + + ImGui::Separator(); + + // Multi-line editor toggle + if (ImGui::SmallButton("T")) { + editorVisible = !editorVisible; + } + if (ImGui::IsItemHovered()) ImGui::SetTooltip("Toggle multi-line code editor"); + + ImGui::Separator(); + + // Lock/unlock toggle + if (ImGui::SmallButton(consoleLocked ? "U" : "L")) { + consoleLocked = !consoleLocked; + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip(consoleLocked ? "Unlock window movement" : "Lock window position"); + } + + ImGui::EndMenuBar(); + } + + // Output area (scrollable) float footerHeight = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing(); ImGui::BeginChild("ScrollingRegion", ImVec2(0, -footerHeight), false, ImGuiWindowFlags_None); - // Render output lines with word wrap + // Render output lines with color coding for (const auto& line : outputHistory) { if (line.isInput) { // User input - yellow/gold color @@ -244,3 +315,62 @@ void ImGuiConsole::render() { ImGui::End(); } + +void ImGuiConsole::renderCodeEditor() { + ImGuiIO& io = ImGui::GetIO(); + + // Position editor below the console by default + ImGui::SetNextWindowSize(ImVec2(io.DisplaySize.x * 0.6f, io.DisplaySize.y * 0.4f), ImGuiCond_FirstUseEver); + ImGui::SetNextWindowPos(ImVec2(io.DisplaySize.x * 0.2f, io.DisplaySize.y * 0.45f), ImGuiCond_FirstUseEver); + + ImGuiWindowFlags flags = ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_MenuBar; + if (editorLocked) flags |= ImGuiWindowFlags_NoMove; + + if (!ImGui::Begin("Code Editor", &editorVisible, flags)) { + ImGui::End(); + return; + } + + // Apply same font scale as console + ImGui::SetWindowFontScale(fontScale); + + // Menu bar + if (ImGui::BeginMenuBar()) { + // Run button + if (ImGui::SmallButton("Run") || (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows) && + io.KeyCtrl && ImGui::IsKeyPressed(ImGuiKey_Enter))) { + std::string code(codeBuffer); + if (!code.empty()) { + executeCommand(code); + } + } + if (ImGui::IsItemHovered()) ImGui::SetTooltip("Execute code (Ctrl+Enter)"); + + // Clear button + if (ImGui::SmallButton("Clear")) { + codeBuffer[0] = '\0'; + } + if (ImGui::IsItemHovered()) ImGui::SetTooltip("Clear editor"); + + ImGui::Separator(); + + // Lock/unlock toggle + if (ImGui::SmallButton(editorLocked ? "U" : "L")) { + editorLocked = !editorLocked; + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip(editorLocked ? "Unlock window movement" : "Lock window position"); + } + + ImGui::EndMenuBar(); + } + + // Multi-line text input - fills available space + ImVec2 contentSize = ImGui::GetContentRegionAvail(); + ImGuiInputTextFlags textFlags = ImGuiInputTextFlags_AllowTabInput; + + ImGui::InputTextMultiline("##CodeEditor", codeBuffer, sizeof(codeBuffer), + contentSize, textFlags); + + ImGui::End(); +} diff --git a/src/ImGuiConsole.h b/src/ImGuiConsole.h index 4c0aa83..ba57afa 100644 --- a/src/ImGuiConsole.h +++ b/src/ImGuiConsole.h @@ -30,13 +30,21 @@ public: private: void executeCommand(const std::string& command); void addOutput(const std::string& text, bool isError = false); + void renderCodeEditor(); // Separate multi-line code editor window // State bool visible = false; static bool enabled; // Global enable/disable (for shipping games) - // Input buffer + // UI state + bool editorVisible = false; // Multi-line editor window + bool consoleLocked = false; // Prevent console dragging + bool editorLocked = false; // Prevent editor dragging + float fontScale = 1.0f; // Text size multiplier (0.5 - 2.0) + + // Input buffers char inputBuffer[1024] = {0}; + char codeBuffer[16384] = {0}; // 16KB for multi-line code // Output history struct OutputLine {