From e93b04648936d1ba968b623fea7fe20405e6cf26 Mon Sep 17 00:00:00 2001 From: Caleb Burke Date: Wed, 16 Sep 2026 01:59:18 -0700 Subject: [PATCH] Made console command registry a better usage pattern --- engine/include/sophia/application.hpp | 3 + .../console/console_command_registry.hpp | 35 ++++++++++ engine/include/sophia/ui/debug_ui.hpp | 3 +- engine/src/application.cpp | 2 +- .../src/console/console_command_registry.cpp | 31 +++++++++ engine/src/ui/debug_ui.cpp | 5 +- engine/src/ui/panels/console_panel.cpp | 66 ++++++++++++++----- engine/src/ui/panels/console_panel.hpp | 19 ++---- 8 files changed, 131 insertions(+), 33 deletions(-) create mode 100644 engine/include/sophia/console/console_command_registry.hpp create mode 100644 engine/src/console/console_command_registry.cpp diff --git a/engine/include/sophia/application.hpp b/engine/include/sophia/application.hpp index afdba72..4870fcb 100644 --- a/engine/include/sophia/application.hpp +++ b/engine/include/sophia/application.hpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -52,6 +53,8 @@ class Application { Camera2D camera; std::vector args; + ConsoleCommandRegistry consoleCommandRegistry; + void drawMesh(MeshHandle handle, const glm::mat4& transform); private: diff --git a/engine/include/sophia/console/console_command_registry.hpp b/engine/include/sophia/console/console_command_registry.hpp new file mode 100644 index 0000000..3d83645 --- /dev/null +++ b/engine/include/sophia/console/console_command_registry.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include +#include +#include +#include + +namespace sophia { + +class ConsoleCommandRegistry { + public: + using Handler = std::function&)>; + + struct CommandInfo { + std::string name; + std::string help; + }; + + void registerCommand(std::string name, std::string help, Handler handler); + + bool dispatch(const std::string& name, const std::vector& args, + std::string& outResult) const; + + std::vector list() const; + + private: + struct Command { + std::string help; + Handler handler; + }; + + std::map commands; +}; + +} // namespace sophia diff --git a/engine/include/sophia/ui/debug_ui.hpp b/engine/include/sophia/ui/debug_ui.hpp index 09b4c17..0037196 100644 --- a/engine/include/sophia/ui/debug_ui.hpp +++ b/engine/include/sophia/ui/debug_ui.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -10,7 +11,7 @@ class DebugUI { DebugUI(const DebugUI&) = delete; DebugUI& operator=(const DebugUI&) = delete; - explicit DebugUI(PanelRegistry& registry); + DebugUI(PanelRegistry& registry, ConsoleCommandRegistry& commands); ~DebugUI() = default; void show(); diff --git a/engine/src/application.cpp b/engine/src/application.cpp index f3c7852..1123bcc 100644 --- a/engine/src/application.cpp +++ b/engine/src/application.cpp @@ -206,7 +206,7 @@ Application::Application(const Config& config) canvas{this}, args{std::move(commandLineArgs)}, assetRegistry{impl->getDevice()}, - _debugUI{this->panelRegistry} {} + _debugUI{this->panelRegistry, this->consoleCommandRegistry} {} Application::~Application() = default; diff --git a/engine/src/console/console_command_registry.cpp b/engine/src/console/console_command_registry.cpp new file mode 100644 index 0000000..fc12aab --- /dev/null +++ b/engine/src/console/console_command_registry.cpp @@ -0,0 +1,31 @@ +#include + +namespace sophia { + +void ConsoleCommandRegistry::registerCommand(std::string name, std::string help, + Handler handler) { + commands[std::move(name)] = Command{std::move(help), std::move(handler)}; +} + +bool ConsoleCommandRegistry::dispatch(const std::string& name, + const std::vector& args, + std::string& outResult) const { + auto it = commands.find(name); + if (it == commands.end()) { + return false; + } + outResult = it->second.handler(args); + return true; +} + +std::vector ConsoleCommandRegistry::list() + const { + std::vector result; + result.reserve(commands.size()); + for (const auto& [name, command] : commands) { + result.push_back(CommandInfo{name, command.help}); + } + return result; +} + +} // namespace sophia diff --git a/engine/src/ui/debug_ui.cpp b/engine/src/ui/debug_ui.cpp index fe3a19a..f52cf17 100644 --- a/engine/src/ui/debug_ui.cpp +++ b/engine/src/ui/debug_ui.cpp @@ -6,11 +6,12 @@ namespace sophia { -DebugUI::DebugUI(PanelRegistry& registry) : registry{registry} { +DebugUI::DebugUI(PanelRegistry& registry, ConsoleCommandRegistry& commands) + : registry{registry} { // #ifndef NDEBUG this->panelRegistryHandle = registry.emplace(registry); this->frameStatsHandle = registry.emplace(); - this->consoleHandle = registry.emplace(); + this->consoleHandle = registry.emplace(commands); // #endif // !NDEBUG } diff --git a/engine/src/ui/panels/console_panel.cpp b/engine/src/ui/panels/console_panel.cpp index b9962dc..4e9f192 100644 --- a/engine/src/ui/panels/console_panel.cpp +++ b/engine/src/ui/panels/console_panel.cpp @@ -1,6 +1,7 @@ #include "console_panel.hpp" #include +#include namespace sophia { @@ -11,10 +12,6 @@ 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; @@ -22,6 +19,16 @@ void ConsolePanel::log(const std::string& message) { void ConsolePanel::clear() { entries.clear(); } +std::vector ConsolePanel::tokenize(const std::string& command) { + std::istringstream stream(command); + std::vector tokens; + std::string token; + while (stream >> token) { + tokens.push_back(std::move(token)); + } + return tokens; +} + void ConsolePanel::execute(const std::string& command) { entries.push_back(Entry{"> " + command, COLOR_COMMAND}); scrollToBottom = true; @@ -31,19 +38,47 @@ void ConsolePanel::execute(const std::string& 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") { + std::vector tokens = tokenize(command); + if (tokens.empty()) { + return; + } + + const std::string& name = tokens.front(); + const std::vector args(tokens.begin() + 1, tokens.end()); + + if (name == "help") { + entries.push_back(Entry{"help - List available commands", COLOR_INFO}); + entries.push_back(Entry{"clear - Clear the console output", COLOR_INFO}); + entries.push_back(Entry{"history - Show command history", COLOR_INFO}); + for (const auto& info : consoleCommandRegistry.list()) { + entries.push_back(Entry{info.name + " - " + info.help, COLOR_INFO}); + } + return; + } + + if (name == "clear") { clear(); - } else if (command == "history") { + return; + } + + if (name == "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}); + return; + } + + std::string result; + try { + if (consoleCommandRegistry.dispatch(name, args, result)) { + if (!result.empty()) { + entries.push_back(Entry{result, COLOR_INFO}); + } + } else { + entries.push_back(Entry{"Unknown command: '" + name + "'", COLOR_ERROR}); + } + } catch (const std::exception& e) { + entries.push_back(Entry{e.what(), COLOR_ERROR}); } } @@ -81,8 +116,6 @@ void ConsolePanel::onUpdate(const FrameInfo&) { if (ImGui::SmallButton("Clear")) { clear(); } - ImGui::SameLine(); - ImGui::Checkbox("Auto-scroll", &autoScroll); ImGui::Separator(); @@ -97,8 +130,7 @@ void ConsolePanel::onUpdate(const FrameInfo&) { ImGui::PopStyleColor(); } - if (scrollToBottom || - (autoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY())) { + if (scrollToBottom) { ImGui::SetScrollHereY(1.0f); } scrollToBottom = false; diff --git a/engine/src/ui/panels/console_panel.hpp b/engine/src/ui/panels/console_panel.hpp index 0afb993..c35ef5f 100644 --- a/engine/src/ui/panels/console_panel.hpp +++ b/engine/src/ui/panels/console_panel.hpp @@ -2,7 +2,7 @@ #include -#include +#include #include #include #include @@ -12,39 +12,34 @@ namespace sophia { class ConsolePanel : public Panel { public: - using CommandCallback = - std::function; - - ConsolePanel() : Panel{"Console"} { registerDefaultCommands(); } + explicit ConsolePanel(ConsoleCommandRegistry& consoleCommandRegistry) + : Panel{"Console"}, consoleCommandRegistry{consoleCommandRegistry} { + log("Console ready. Type 'help' for a list of commands."); + } 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); + static std::vector tokenize(const std::string& command); std::vector entries; std::vector history; int historyPos = -1; char inputBuffer[256] = {}; - bool autoScroll = true; bool scrollToBottom = false; - CommandCallback commandCallback; + ConsoleCommandRegistry& consoleCommandRegistry; }; } // namespace sophia