2026-05-10 23:23:40 +02:00
|
|
|
module;
|
|
|
|
|
|
|
|
|
|
#include <cassert>
|
|
|
|
|
#include <RmlUi/Core.h>
|
|
|
|
|
#include <RmlUi/Debugger.h>
|
|
|
|
|
|
|
|
|
|
export module rutile.core.ui.ui_server;
|
|
|
|
|
|
2026-05-16 01:58:45 +02:00
|
|
|
import std;
|
2026-05-10 23:23:40 +02:00
|
|
|
import rutile.core.ui.backend.rmlui_render_interface;
|
|
|
|
|
import rutile.core.ui.backend.rmlui_system_interface;
|
|
|
|
|
import rutile.core.ui.backend.rmlui_input_handler;
|
|
|
|
|
import wrappers.sdl;
|
|
|
|
|
|
|
|
|
|
export namespace rutile
|
|
|
|
|
{
|
|
|
|
|
class UIServer
|
|
|
|
|
{
|
|
|
|
|
// Whether this class is currently instantiated (to prevent multiple instances because we need to do
|
|
|
|
|
// Rml::Initialise() and Rml::Shutdown()).
|
|
|
|
|
static bool instantiated_;
|
|
|
|
|
|
|
|
|
|
RmlUiSystemInterface system_interface_;
|
|
|
|
|
RmlUiRenderInterface render_interface_;
|
|
|
|
|
RmlUiInputHandler input_handler_;
|
|
|
|
|
|
|
|
|
|
// RmlUi context (non-owning pointer, lifetime managed by library)
|
|
|
|
|
Rml::Context* context_;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
explicit UIServer(sdl::Renderer& renderer, sdl::Window& window)
|
|
|
|
|
: system_interface_{window},
|
|
|
|
|
render_interface_{renderer},
|
|
|
|
|
input_handler_{window}
|
|
|
|
|
{
|
|
|
|
|
// Prevent the class from being instantiated multiple times
|
|
|
|
|
assert(!instantiated_);
|
|
|
|
|
|
|
|
|
|
Rml::SetSystemInterface(&system_interface_);
|
|
|
|
|
Rml::SetRenderInterface(&render_interface_);
|
|
|
|
|
Rml::Initialise();
|
|
|
|
|
|
|
|
|
|
// Create the main RmlUi context
|
|
|
|
|
auto [window_width, window_height] = window.get_size();
|
|
|
|
|
context_ = Rml::CreateContext("main", Rml::Vector2i(window_width, window_height));
|
|
|
|
|
if (!context_) {
|
|
|
|
|
throw std::runtime_error("RmlUi: Couldn't create context");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO: Implement some kind of switch to disable debugger (just use config::debug?)
|
|
|
|
|
Rml::Debugger::Initialise(context_);
|
|
|
|
|
|
|
|
|
|
instantiated_ = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// No copy or move operations
|
|
|
|
|
UIServer(const UIServer&) = delete;
|
|
|
|
|
UIServer& operator=(const UIServer&) = delete;
|
|
|
|
|
UIServer(UIServer&&) = delete;
|
|
|
|
|
UIServer& operator=(UIServer&&) = delete;
|
|
|
|
|
|
|
|
|
|
~UIServer()
|
|
|
|
|
{
|
|
|
|
|
Rml::Shutdown();
|
|
|
|
|
instantiated_ = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
constexpr Rml::Context& get_context() const
|
|
|
|
|
{
|
|
|
|
|
// TODO: Can we assume context_ is always non-nullptr?
|
|
|
|
|
assert(context_);
|
|
|
|
|
return *context_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Handles an SDL event. Returns true if the event has been handled.
|
|
|
|
|
bool handle_event(const sdl::Event& event) const
|
|
|
|
|
{
|
|
|
|
|
// Toggle RmlUi debugger with F12
|
|
|
|
|
if (event.type == sdl::EventTypes::KeyDown && event.key.key == sdl::Key::F12) {
|
|
|
|
|
Rml::Debugger::SetVisible(!Rml::Debugger::IsVisible());
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Let RmlUi handle relevant events
|
|
|
|
|
if (input_handler_.handle_input_event(get_context(), event)) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void update() const
|
|
|
|
|
{
|
|
|
|
|
get_context().Update();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void render() const
|
|
|
|
|
{
|
|
|
|
|
render_interface_.prepare_render();
|
|
|
|
|
get_context().Render();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void load_font_face(const std::string& file_path)
|
|
|
|
|
{
|
|
|
|
|
if (!Rml::LoadFontFace(file_path)) {
|
|
|
|
|
throw std::runtime_error(std::format("RmlUi: Couldn't load font face \"{}\"", file_path));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO: Can/should we avoid the (non-owning) raw pointers here?
|
|
|
|
|
Rml::ElementDocument* load_document(const std::string& file_path) const
|
|
|
|
|
{
|
|
|
|
|
Rml::ElementDocument* document = get_context().LoadDocument(file_path);
|
|
|
|
|
|
|
|
|
|
if (!document) {
|
|
|
|
|
throw std::runtime_error(std::format("RmlUi: Couldn't load document \"{}\"", file_path));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return document;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
bool UIServer::instantiated_ = false;
|
|
|
|
|
}
|