Compare commits

...

2 Commits

Author SHA1 Message Date
burkkyy 169ff4a277 Made console command registry a better usage pattern 2026-09-16 01:59:34 -07:00
burkkyy 8824f64225 Merge pull request 'Huge changes' (#3) from dev into main
Reviewed-on: #3
2026-09-15 02:36:50 -07:00
8 changed files with 131 additions and 33 deletions
+3
View File
@@ -6,6 +6,7 @@
#include <sophia/asset_registry.hpp> #include <sophia/asset_registry.hpp>
#include <sophia/camera_2d.hpp> #include <sophia/camera_2d.hpp>
#include <sophia/canvas.hpp> #include <sophia/canvas.hpp>
#include <sophia/console/console_command_registry.hpp>
#include <sophia/frame_info.hpp> #include <sophia/frame_info.hpp>
#include <sophia/types.hpp> #include <sophia/types.hpp>
#include <sophia/ui/debug_ui.hpp> #include <sophia/ui/debug_ui.hpp>
@@ -52,6 +53,8 @@ class Application {
Camera2D camera; Camera2D camera;
std::vector<std::string> args; std::vector<std::string> args;
ConsoleCommandRegistry consoleCommandRegistry;
void drawMesh(MeshHandle handle, const glm::mat4& transform); void drawMesh(MeshHandle handle, const glm::mat4& transform);
private: private:
@@ -0,0 +1,35 @@
#pragma once
#include <functional>
#include <map>
#include <string>
#include <vector>
namespace sophia {
class ConsoleCommandRegistry {
public:
using Handler = std::function<std::string(const std::vector<std::string>&)>;
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<std::string>& args,
std::string& outResult) const;
std::vector<CommandInfo> list() const;
private:
struct Command {
std::string help;
Handler handler;
};
std::map<std::string, Command> commands;
};
} // namespace sophia
+2 -1
View File
@@ -1,5 +1,6 @@
#pragma once #pragma once
#include <sophia/console/console_command_registry.hpp>
#include <sophia/handles.hpp> #include <sophia/handles.hpp>
#include <sophia/ui/panel_registry.hpp> #include <sophia/ui/panel_registry.hpp>
@@ -10,7 +11,7 @@ class DebugUI {
DebugUI(const DebugUI&) = delete; DebugUI(const DebugUI&) = delete;
DebugUI& operator=(const DebugUI&) = delete; DebugUI& operator=(const DebugUI&) = delete;
explicit DebugUI(PanelRegistry& registry); DebugUI(PanelRegistry& registry, ConsoleCommandRegistry& commands);
~DebugUI() = default; ~DebugUI() = default;
void show(); void show();
+1 -1
View File
@@ -206,7 +206,7 @@ Application::Application(const Config& config)
canvas{this}, canvas{this},
args{std::move(commandLineArgs)}, args{std::move(commandLineArgs)},
assetRegistry{impl->getDevice()}, assetRegistry{impl->getDevice()},
_debugUI{this->panelRegistry} {} _debugUI{this->panelRegistry, this->consoleCommandRegistry} {}
Application::~Application() = default; Application::~Application() = default;
@@ -0,0 +1,31 @@
#include <sophia/console/console_command_registry.hpp>
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<std::string>& 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::CommandInfo> ConsoleCommandRegistry::list()
const {
std::vector<CommandInfo> result;
result.reserve(commands.size());
for (const auto& [name, command] : commands) {
result.push_back(CommandInfo{name, command.help});
}
return result;
}
} // namespace sophia
+3 -2
View File
@@ -6,11 +6,12 @@
namespace sophia { namespace sophia {
DebugUI::DebugUI(PanelRegistry& registry) : registry{registry} { DebugUI::DebugUI(PanelRegistry& registry, ConsoleCommandRegistry& commands)
: registry{registry} {
// #ifndef NDEBUG // #ifndef NDEBUG
this->panelRegistryHandle = registry.emplace<PanelRegistryPanel>(registry); this->panelRegistryHandle = registry.emplace<PanelRegistryPanel>(registry);
this->frameStatsHandle = registry.emplace<FrameStatsPanel>(); this->frameStatsHandle = registry.emplace<FrameStatsPanel>();
this->consoleHandle = registry.emplace<ConsolePanel>(); this->consoleHandle = registry.emplace<ConsolePanel>(commands);
// #endif // !NDEBUG // #endif // !NDEBUG
} }
+48 -16
View File
@@ -1,6 +1,7 @@
#include "console_panel.hpp" #include "console_panel.hpp"
#include <algorithm> #include <algorithm>
#include <sstream>
namespace sophia { 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); constexpr ImVec4 COLOR_INFO = ImVec4(0.4f, 0.7f, 1.0f, 1.0f);
} // namespace } // namespace
void ConsolePanel::registerDefaultCommands() {
log("Console ready. Type 'help' for a list of commands.");
}
void ConsolePanel::log(const std::string& message) { void ConsolePanel::log(const std::string& message) {
entries.push_back(Entry{message, COLOR_DEFAULT}); entries.push_back(Entry{message, COLOR_DEFAULT});
scrollToBottom = true; scrollToBottom = true;
@@ -22,6 +19,16 @@ void ConsolePanel::log(const std::string& message) {
void ConsolePanel::clear() { entries.clear(); } void ConsolePanel::clear() { entries.clear(); }
std::vector<std::string> ConsolePanel::tokenize(const std::string& command) {
std::istringstream stream(command);
std::vector<std::string> tokens;
std::string token;
while (stream >> token) {
tokens.push_back(std::move(token));
}
return tokens;
}
void ConsolePanel::execute(const std::string& command) { void ConsolePanel::execute(const std::string& command) {
entries.push_back(Entry{"> " + command, COLOR_COMMAND}); entries.push_back(Entry{"> " + command, COLOR_COMMAND});
scrollToBottom = true; scrollToBottom = true;
@@ -31,19 +38,47 @@ void ConsolePanel::execute(const std::string& command) {
history.end()); history.end());
history.push_back(command); history.push_back(command);
if (command == "help") { std::vector<std::string> tokens = tokenize(command);
entries.push_back( if (tokens.empty()) {
Entry{"Available commands: help, clear, history", COLOR_INFO}); return;
} else if (command == "clear") { }
const std::string& name = tokens.front();
const std::vector<std::string> 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(); clear();
} else if (command == "history") { return;
}
if (name == "history") {
for (const std::string& past : history) { for (const std::string& past : history) {
entries.push_back(Entry{past, COLOR_DEFAULT}); entries.push_back(Entry{past, COLOR_DEFAULT});
} }
} else if (commandCallback) { return;
commandCallback(*this, command); }
std::string result;
try {
if (consoleCommandRegistry.dispatch(name, args, result)) {
if (!result.empty()) {
entries.push_back(Entry{result, COLOR_INFO});
}
} else { } else {
entries.push_back(Entry{"Unknown command: '" + command + "'", COLOR_ERROR}); 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")) { if (ImGui::SmallButton("Clear")) {
clear(); clear();
} }
ImGui::SameLine();
ImGui::Checkbox("Auto-scroll", &autoScroll);
ImGui::Separator(); ImGui::Separator();
@@ -97,8 +130,7 @@ void ConsolePanel::onUpdate(const FrameInfo&) {
ImGui::PopStyleColor(); ImGui::PopStyleColor();
} }
if (scrollToBottom || if (scrollToBottom) {
(autoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY())) {
ImGui::SetScrollHereY(1.0f); ImGui::SetScrollHereY(1.0f);
} }
scrollToBottom = false; scrollToBottom = false;
+7 -12
View File
@@ -2,7 +2,7 @@
#include <imgui.h> #include <imgui.h>
#include <functional> #include <sophia/console/console_command_registry.hpp>
#include <sophia/frame_info.hpp> #include <sophia/frame_info.hpp>
#include <sophia/ui/panel.hpp> #include <sophia/ui/panel.hpp>
#include <string> #include <string>
@@ -12,39 +12,34 @@ namespace sophia {
class ConsolePanel : public Panel { class ConsolePanel : public Panel {
public: public:
using CommandCallback = explicit ConsolePanel(ConsoleCommandRegistry& consoleCommandRegistry)
std::function<void(ConsolePanel&, const std::string&)>; : Panel{"Console"}, consoleCommandRegistry{consoleCommandRegistry} {
log("Console ready. Type 'help' for a list of commands.");
ConsolePanel() : Panel{"Console"} { registerDefaultCommands(); } }
void onUpdate(const FrameInfo& frameInfo) override; void onUpdate(const FrameInfo& frameInfo) override;
void log(const std::string& message); void log(const std::string& message);
void clear(); void clear();
void setCommandCallback(CommandCallback callback) {
this->commandCallback = std::move(callback);
}
private: private:
struct Entry { struct Entry {
std::string text; std::string text;
ImVec4 color; ImVec4 color;
}; };
void registerDefaultCommands();
void execute(const std::string& command); void execute(const std::string& command);
int textEditCallback(struct ImGuiInputTextCallbackData* data); int textEditCallback(struct ImGuiInputTextCallbackData* data);
static std::vector<std::string> tokenize(const std::string& command);
std::vector<Entry> entries; std::vector<Entry> entries;
std::vector<std::string> history; std::vector<std::string> history;
int historyPos = -1; int historyPos = -1;
char inputBuffer[256] = {}; char inputBuffer[256] = {};
bool autoScroll = true;
bool scrollToBottom = false; bool scrollToBottom = false;
CommandCallback commandCallback; ConsoleCommandRegistry& consoleCommandRegistry;
}; };
} // namespace sophia } // namespace sophia