Added console concept
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
#include <sophia/ui/debug_ui.hpp>
|
||||
|
||||
#include "panels/console_panel.hpp"
|
||||
#include "panels/frame_stats_panel.hpp"
|
||||
#include "panels/panel_registry_panel.hpp"
|
||||
|
||||
@@ -9,17 +10,20 @@ DebugUI::DebugUI(PanelRegistry& registry) : registry{registry} {
|
||||
// #ifndef NDEBUG
|
||||
this->panelRegistryHandle = registry.emplace<PanelRegistryPanel>(registry);
|
||||
this->frameStatsHandle = registry.emplace<FrameStatsPanel>();
|
||||
this->consoleHandle = registry.emplace<ConsolePanel>();
|
||||
// #endif // !NDEBUG
|
||||
}
|
||||
|
||||
void DebugUI::show() {
|
||||
showPanelRegistry(true);
|
||||
showFrameStats(true);
|
||||
showConsole(true);
|
||||
}
|
||||
|
||||
void DebugUI::hide() {
|
||||
showPanelRegistry(false);
|
||||
showFrameStats(false);
|
||||
showConsole(false);
|
||||
}
|
||||
|
||||
void DebugUI::showPanelRegistry(bool show) {
|
||||
@@ -34,4 +38,10 @@ void DebugUI::showFrameStats(bool show) {
|
||||
}
|
||||
}
|
||||
|
||||
void DebugUI::showConsole(bool show) {
|
||||
if (registry.isValid(consoleHandle)) {
|
||||
registry.get(consoleHandle).setVisible(show);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace sophia
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
#include "console_panel.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace sophia {
|
||||
|
||||
namespace {
|
||||
constexpr ImVec4 COLOR_DEFAULT = ImVec4(0.85f, 0.85f, 0.85f, 1.0f);
|
||||
constexpr ImVec4 COLOR_COMMAND = ImVec4(1.0f, 0.78f, 0.37f, 1.0f);
|
||||
constexpr ImVec4 COLOR_ERROR = ImVec4(1.0f, 0.4f, 0.4f, 1.0f);
|
||||
constexpr ImVec4 COLOR_INFO = ImVec4(0.4f, 0.7f, 1.0f, 1.0f);
|
||||
} // namespace
|
||||
|
||||
void ConsolePanel::registerDefaultCommands() {
|
||||
log("Console ready. Type 'help' for a list of commands.");
|
||||
}
|
||||
|
||||
void ConsolePanel::log(const std::string& message) {
|
||||
entries.push_back(Entry{message, COLOR_DEFAULT});
|
||||
scrollToBottom = true;
|
||||
}
|
||||
|
||||
void ConsolePanel::clear() { entries.clear(); }
|
||||
|
||||
void ConsolePanel::execute(const std::string& command) {
|
||||
entries.push_back(Entry{"> " + command, COLOR_COMMAND});
|
||||
scrollToBottom = true;
|
||||
|
||||
historyPos = -1;
|
||||
history.erase(std::remove(history.begin(), history.end(), command),
|
||||
history.end());
|
||||
history.push_back(command);
|
||||
|
||||
if (command == "help") {
|
||||
entries.push_back(
|
||||
Entry{"Available commands: help, clear, history", COLOR_INFO});
|
||||
} else if (command == "clear") {
|
||||
clear();
|
||||
} else if (command == "history") {
|
||||
for (const std::string& past : history) {
|
||||
entries.push_back(Entry{past, COLOR_DEFAULT});
|
||||
}
|
||||
} else if (commandCallback) {
|
||||
commandCallback(*this, command);
|
||||
} else {
|
||||
entries.push_back(Entry{"Unknown command: '" + command + "'", COLOR_ERROR});
|
||||
}
|
||||
}
|
||||
|
||||
int ConsolePanel::textEditCallback(ImGuiInputTextCallbackData* data) {
|
||||
if (data->EventFlag != ImGuiInputTextFlags_CallbackHistory) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const int previousPos = historyPos;
|
||||
if (data->EventKey == ImGuiKey_UpArrow) {
|
||||
if (historyPos == -1) {
|
||||
historyPos = static_cast<int>(history.size()) - 1;
|
||||
} else if (historyPos > 0) {
|
||||
historyPos--;
|
||||
}
|
||||
} else if (data->EventKey == ImGuiKey_DownArrow) {
|
||||
if (historyPos != -1 && ++historyPos >= static_cast<int>(history.size())) {
|
||||
historyPos = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (previousPos != historyPos) {
|
||||
const std::string& historyStr =
|
||||
historyPos >= 0 ? history[static_cast<size_t>(historyPos)] : "";
|
||||
data->DeleteChars(0, data->BufTextLen);
|
||||
data->InsertChars(0, historyStr.c_str());
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ConsolePanel::onUpdate(const FrameInfo&) {
|
||||
ImGui::Begin("Console");
|
||||
|
||||
if (ImGui::SmallButton("Clear")) {
|
||||
clear();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::Checkbox("Auto-scroll", &autoScroll);
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
const float footerHeight =
|
||||
ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing();
|
||||
ImGui::BeginChild("ScrollRegion", ImVec2(0, -footerHeight), false,
|
||||
ImGuiWindowFlags_HorizontalScrollbar);
|
||||
|
||||
for (const Entry& entry : entries) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, entry.color);
|
||||
ImGui::TextUnformatted(entry.text.c_str());
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
|
||||
if (scrollToBottom ||
|
||||
(autoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY())) {
|
||||
ImGui::SetScrollHereY(1.0f);
|
||||
}
|
||||
scrollToBottom = false;
|
||||
|
||||
ImGui::EndChild();
|
||||
ImGui::Separator();
|
||||
|
||||
ImGuiInputTextFlags inputFlags = ImGuiInputTextFlags_EnterReturnsTrue |
|
||||
ImGuiInputTextFlags_CallbackHistory;
|
||||
|
||||
bool reclaimFocus = false;
|
||||
if (ImGui::InputText(
|
||||
"Input", inputBuffer, sizeof(inputBuffer), inputFlags,
|
||||
[](ImGuiInputTextCallbackData* data) -> int {
|
||||
auto* console = static_cast<ConsolePanel*>(data->UserData);
|
||||
return console->textEditCallback(data);
|
||||
},
|
||||
this)) {
|
||||
std::string command{inputBuffer};
|
||||
if (!command.empty()) {
|
||||
execute(command);
|
||||
}
|
||||
inputBuffer[0] = '\0';
|
||||
reclaimFocus = true;
|
||||
}
|
||||
|
||||
ImGui::SetItemDefaultFocus();
|
||||
if (reclaimFocus) {
|
||||
ImGui::SetKeyboardFocusHere(-1);
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
} // namespace sophia
|
||||
@@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
#include <functional>
|
||||
#include <sophia/frame_info.hpp>
|
||||
#include <sophia/ui/panel.hpp>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace sophia {
|
||||
|
||||
class ConsolePanel : public Panel {
|
||||
public:
|
||||
using CommandCallback =
|
||||
std::function<void(ConsolePanel&, const std::string&)>;
|
||||
|
||||
ConsolePanel() : Panel{"Console"} { registerDefaultCommands(); }
|
||||
|
||||
void onUpdate(const FrameInfo& frameInfo) override;
|
||||
|
||||
void log(const std::string& message);
|
||||
void clear();
|
||||
|
||||
void setCommandCallback(CommandCallback callback) {
|
||||
this->commandCallback = std::move(callback);
|
||||
}
|
||||
|
||||
private:
|
||||
struct Entry {
|
||||
std::string text;
|
||||
ImVec4 color;
|
||||
};
|
||||
|
||||
void registerDefaultCommands();
|
||||
void execute(const std::string& command);
|
||||
int textEditCallback(struct ImGuiInputTextCallbackData* data);
|
||||
|
||||
std::vector<Entry> entries;
|
||||
std::vector<std::string> history;
|
||||
int historyPos = -1;
|
||||
|
||||
char inputBuffer[256] = {};
|
||||
bool autoScroll = true;
|
||||
bool scrollToBottom = false;
|
||||
|
||||
CommandCallback commandCallback;
|
||||
};
|
||||
|
||||
} // namespace sophia
|
||||
Reference in New Issue
Block a user