diff --git a/Makefile b/Makefile index e00f59e..81f96f7 100644 --- a/Makefile +++ b/Makefile @@ -31,5 +31,5 @@ clean: %.o: %.cc @echo " CXX " $< - g++ $(CXXFLAGS) -c $< -o $@ + @g++ $(CXXFLAGS) -c $< -o $@ diff --git a/source/code.cpp b/source/code.cpp index ae0d2bf..e53165c 100644 --- a/source/code.cpp +++ b/source/code.cpp @@ -25,12 +25,12 @@ #include extern std::string tempFileName; -extern std::string statusMessage; extern stmdsp::device *m_device; +extern void log(const std::string& str); + TextEditor editor; // file.cpp static std::string editorCompiled; -static std::string compileMessage; static std::string newTempFileName(); static void compileEditorCode(); @@ -63,18 +63,13 @@ void codeRenderToolbar() void codeRenderWidgets() { - editor.Render("code", {WINDOW_WIDTH - 15, 635}, true); - - if (ImGui::BeginPopup("compile", ImGuiWindowFlags_AlwaysHorizontalScrollbar)) { - ImGui::Text(compileMessage.c_str()); - if (ImGui::Button("Close")) - ImGui::CloseCurrentPopup(); - ImGui::EndPopup(); - } + editor.Render("code", {WINDOW_WIDTH - 15, 450}, true); } void compileEditorCode() { + log("Compiling..."); + // Scrap cached build if there are changes if (editor.GetText().compare(editorCompiled) != 0) { std::filesystem::remove(tempFileName + ".o"); @@ -143,7 +138,7 @@ void compileEditorCode() std::ifstream result_file (makeOutput); std::ostringstream sstr; sstr << result_file.rdbuf(); - compileMessage = sstr.str(); + log(sstr.str().c_str()); std::filesystem::remove(tempFileName); std::filesystem::remove(tempFileName + script_ext); @@ -151,15 +146,16 @@ void compileEditorCode() if (result == 0) { editorCompiled = editor.GetText(); - statusMessage = "Compilation succeeded."; + log("Compilation succeeded."); } else { - statusMessage = "Compilation failed."; - ImGui::OpenPopup("compile"); + log("Compilation failed."); } } void disassembleCode() { + log("Disassembling..."); + if (tempFileName.size() == 0 || editor.GetText().compare(editorCompiled) != 0) { compileEditorCode(); } @@ -173,15 +169,15 @@ void disassembleCode() std::ifstream result_file (output); std::ostringstream sstr; sstr << result_file.rdbuf(); - compileMessage = sstr.str(); + log(sstr.str().c_str()); } ImGui::OpenPopup("compile"); std::filesystem::remove(output); - statusMessage = "Ready."; + log("Ready."); } else { - statusMessage = "Failed to load disassembly."; + log("Failed to load disassembly."); } } diff --git a/source/device.cpp b/source/device.cpp index 1dcacf0..cb4ee0a 100644 --- a/source/device.cpp +++ b/source/device.cpp @@ -14,7 +14,7 @@ #include "imgui.h" extern stmdsp::device *m_device; -extern std::string statusMessage; +extern void log(const std::string& str); static const char *sampleRateList[6] = { "8 kHz", @@ -91,35 +91,35 @@ void deviceConnect() m_device = new stmdsp::device(devices.front()); if (m_device->connected()) { sampleRatePreview = sampleRateList[m_device->get_sample_rate()]; - statusMessage = "Connected!"; + log("Connected!"); } else { delete m_device; m_device = nullptr; - statusMessage = "Failed to connect."; + log("Failed to connect."); } } else { - statusMessage = "No devices found."; + log("No devices found."); } } else { delete m_device; m_device = nullptr; - statusMessage = "Disconnected."; + log("Disconnected."); } } void deviceStart() { if (m_device == nullptr) { - statusMessage = "No device connected."; + log("No device connected."); return; } if (m_device->is_running()) { m_device->continuous_stop(); - statusMessage = "Ready."; + log("Ready."); } else { m_device->continuous_start(); - statusMessage = "Running."; + log("Running."); } } diff --git a/source/file.cpp b/source/file.cpp index 355ab7b..3e5284e 100644 --- a/source/file.cpp +++ b/source/file.cpp @@ -26,7 +26,7 @@ #include extern TextEditor editor; -extern std::string statusMessage; +extern void log(const std::string& str); enum class FileAction { None, @@ -43,7 +43,7 @@ static void saveCurrentFile() if (std::ofstream ofs (fileCurrentPath, std::ios::binary); ofs.good()) { const auto& text = editor.GetText(); ofs.write(text.data(), text.size()); - statusMessage = "Saved."; + log("Saved."); } } @@ -70,7 +70,7 @@ void fileRenderMenu() // TODO modified? fileCurrentPath.clear(); editor.SetText(stmdsp::file_content); - statusMessage = "Ready."; + log("Ready."); } if (ImGui::MenuItem("Open")) { @@ -87,7 +87,7 @@ void fileRenderMenu() // Treat like new file. fileCurrentPath.clear(); - statusMessage = "Ready."; + log("Ready."); } } @@ -110,6 +110,7 @@ void fileRenderMenu() "ChooseFileDlgKey", "Choose File", ".cpp", "."); } + ImGui::Separator(); if (ImGui::MenuItem("Quit")) { extern bool done; done = true; @@ -131,7 +132,7 @@ void fileRenderDialog() case FileAction::Open: fileCurrentPath = filePathName; openCurrentFile(); - statusMessage = "Ready."; + log("Ready."); break; case FileAction::SaveAs: fileCurrentPath = filePathName; diff --git a/source/logview.h b/source/logview.h new file mode 100644 index 0000000..cfe5011 --- /dev/null +++ b/source/logview.h @@ -0,0 +1,88 @@ +#ifndef LOGVIEW_H +#define LOGVIEW_H + +#include + +// Adapted from ExampleAppLog from imgui_demo.cpp +class LogView +{ +public: + LogView() + { + Clear(); + } + + void Clear() + { + Buf.clear(); + LineOffsets.clear(); + LineOffsets.push_back(0); + } + + void AddLog(const std::string& str) + { + AddLog(str.c_str()); + } + + void AddLog(const char* fmt, ...) IM_FMTARGS(2) + { + int old_size = Buf.size(); + va_list args; + va_start(args, fmt); + Buf.appendfv(fmt, args); + Buf.appendfv("\n", args); + va_end(args); + for (int new_size = Buf.size(); old_size < new_size; old_size++) + if (Buf[old_size] == '\n') + LineOffsets.push_back(old_size + 1); + } + + void Draw(const char* title, bool* p_open = NULL, ImGuiWindowFlags flags = 0) + { + if (!ImGui::Begin(title, p_open, flags)) + { + ImGui::End(); + return; + } + + if (ImGui::Button("Clear")) + Clear(); + ImGui::SameLine(); + if (ImGui::Button("Copy")) + ImGui::LogToClipboard(); + ImGui::Separator(); + ImGui::BeginChild("scrolling", ImVec2(0, 0), false, ImGuiWindowFlags_HorizontalScrollbar); + + + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); + const char* buf = Buf.begin(); + const char* buf_end = Buf.end(); + ImGuiListClipper clipper; + clipper.Begin(LineOffsets.Size); + while (clipper.Step()) + { + for (int line_no = clipper.DisplayStart; line_no < clipper.DisplayEnd; line_no++) + { + const char* line_start = buf + LineOffsets[line_no]; + const char* line_end = (line_no + 1 < LineOffsets.Size) ? (buf + LineOffsets[line_no + 1] - 1) : buf_end; + ImGui::TextUnformatted(line_start, line_end); + } + } + clipper.End(); + + ImGui::PopStyleVar(); + + if (ImGui::GetScrollY() >= ImGui::GetScrollMaxY()) + ImGui::SetScrollHereY(1.0f); + + ImGui::EndChild(); + ImGui::End(); + } + +private: + ImGuiTextBuffer Buf; + ImVector LineOffsets; // Index to lines offset. We maintain this with AddLog() calls. +}; + +#endif // LOGVIEW_H + diff --git a/source/main.cpp b/source/main.cpp index ca84713..dc5f789 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -14,6 +14,7 @@ #include "backends/imgui_impl_opengl2.h" #include "config.h" +#include "logview.h" #include "stmdsp.hpp" #include @@ -40,10 +41,16 @@ extern void deviceRenderToolbar(); // Globals that live here std::string tempFileName; -std::string statusMessage ("Ready."); bool done = false; stmdsp::device *m_device = nullptr; +static LogView logView; + +void log(const std::string& str) +{ + logView.AddLog(str); +} + int main(int, char **) { if (!guiInitialize()) @@ -69,7 +76,7 @@ int main(int, char **) // Begin the main view which the controls will be drawn onto. ImGui::SetNextWindowPos({0, 22}); - ImGui::SetNextWindowSize({WINDOW_WIDTH, WINDOW_HEIGHT - 22}); + ImGui::SetNextWindowSize({WINDOW_WIDTH, WINDOW_HEIGHT - 22 - 200}); ImGui::Begin("main", nullptr, ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoDecoration); ImGui::PushFont(font); @@ -80,9 +87,12 @@ int main(int, char **) fileRenderDialog(); codeRenderWidgets(); + ImGui::SetNextWindowPos({0, WINDOW_HEIGHT - 200}); + ImGui::SetNextWindowSize({WINDOW_WIDTH, 200}); + logView.Draw("log", nullptr, ImGuiWindowFlags_NoDecoration); + // Finish main view rendering. ImGui::PopFont(); - ImGui::Text(statusMessage.c_str()); ImGui::End(); // Draw everything to the screen.