Shell + page loading
And: - some basic stylesheet i found for basic html elements - a global lua method for navigating pages
This commit is contained in:
181
src/app.cpp
Normal file
181
src/app.cpp
Normal file
@@ -0,0 +1,181 @@
|
||||
#include "app.hpp"
|
||||
#include <RmlUi/Core/Core.h>
|
||||
#include <RmlUi/Core/Factory.h>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
App::App(Rml::Context *ctx, ApplicationData *appData)
|
||||
: ctx(ctx), appData(appData), doc(nullptr) {}
|
||||
|
||||
|
||||
App::~App() {
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
void App::SetupListeners(std::function<void(Rml::ElementDocument*)> bindCallback) {
|
||||
onBindListeners = bindCallback;
|
||||
}
|
||||
|
||||
void App::RegisterListener(Rml::Element* element, std::unique_ptr<Rml::EventListener> listener) {
|
||||
if (element && listener) {
|
||||
element->AddEventListener(Rml::EventId::Click, listener.get());
|
||||
listeners.push_back(std::move(listener));
|
||||
}
|
||||
}
|
||||
|
||||
PageBase* App::GetOrCreatePage(const std::string& pageName) {
|
||||
if (auto it = pages.find(pageName); it != pages.end()) {
|
||||
return it->second.get();
|
||||
}
|
||||
|
||||
std::string path = GetAssetsPath() + "/pages/" + pageName + ".rml";
|
||||
if (!std::filesystem::exists(path)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto page = std::make_unique<PageBase>(pageName, this);
|
||||
PageBase* raw = page.get();
|
||||
pages.emplace(pageName, std::move(page));
|
||||
return raw;
|
||||
}
|
||||
|
||||
bool App::ReadFile(const std::string& path, std::string& outContent) {
|
||||
std::ifstream file(path, std::ios::in | std::ios::binary);
|
||||
if (!file) return false;
|
||||
|
||||
std::ostringstream ss;
|
||||
ss << file.rdbuf();
|
||||
outContent = ss.str();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool App::NavigateTo(const std::string &pageName) {
|
||||
|
||||
if (!pageOutlet) {
|
||||
std::cout << "[ERROR] no page outlet - is main.rml missing id=\"page-content\"?\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
PageBase* page = GetOrCreatePage(pageName);
|
||||
if (!page) {
|
||||
std::cout << "[ERROR] no page registered or found on disk for '" << pageName << "'\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string path = GetAssetsPath() + "/pages/" + pageName + ".rml";
|
||||
std::string content;
|
||||
if (!ReadFile(path, content)) {
|
||||
std::cout << "[ERROR] could not read page file: " << path << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
TeardownCurrentPage();
|
||||
|
||||
if (Rml::DataModelConstructor constructor = ctx->CreateDataModel(pageName)) {
|
||||
page->SetupDataModel(constructor);
|
||||
page->model = constructor.GetModelHandle();
|
||||
page->hasModel = true;
|
||||
} else {
|
||||
std::cout << "[WARN] failed to create data model for page '" << pageName << "'\n";
|
||||
}
|
||||
|
||||
pageOutlet->SetInnerRML(content);
|
||||
|
||||
page->root = pageOutlet->GetElementById("page-root");
|
||||
if (!page->root) {
|
||||
std::cout << "[WARN] page '" << pageName << "' has no element with id=\"page-root\"\n";
|
||||
}
|
||||
|
||||
currentPage = page;
|
||||
page->OnEnter(page->root);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool App::LoadUi() {
|
||||
if (!ctx) return false;
|
||||
|
||||
doc = ctx->LoadDocument(GetAssetsPath() + "/main.rml");
|
||||
if (!doc) return false;
|
||||
|
||||
doc->Show();
|
||||
|
||||
pageOutlet = doc->GetElementById("page-content");
|
||||
if (!pageOutlet) {
|
||||
std::cout << "[WARN] main.rml has no element with id=\"page-content\"\n";
|
||||
}
|
||||
|
||||
listeners.clear();
|
||||
if (onBindListeners) {
|
||||
onBindListeners(doc);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void App::Reload() {
|
||||
Rml::Factory::ClearStyleSheetCache();
|
||||
Rml::Factory::ClearTemplateCache();
|
||||
|
||||
std::string previousPage = currentPage ? currentPage->GetName() : "";
|
||||
|
||||
TeardownCurrentPage();
|
||||
|
||||
if (doc) {
|
||||
doc->Close();
|
||||
doc = nullptr;
|
||||
pageOutlet = nullptr;
|
||||
}
|
||||
|
||||
LoadUi();
|
||||
|
||||
if (!previousPage.empty()) {
|
||||
NavigateTo(previousPage);
|
||||
}
|
||||
}
|
||||
|
||||
std::string App::GetAssetsPath() {
|
||||
std::string install_path = std::string(ASSETS_DIR) + "/main.rml";
|
||||
if (std::filesystem::exists(install_path)) {
|
||||
return std::string(ASSETS_DIR);
|
||||
}
|
||||
if (std::filesystem::exists("./assets/main.rml")) {
|
||||
return "./assets";
|
||||
}
|
||||
return ".";
|
||||
}
|
||||
|
||||
void App::TeardownCurrentPage() {
|
||||
if (!currentPage) return;
|
||||
|
||||
currentPage->OnExit();
|
||||
|
||||
if (currentPage->hasModel) {
|
||||
ctx->RemoveDataModel(currentPage->GetName());
|
||||
currentPage->hasModel = false;
|
||||
}
|
||||
|
||||
currentPage->root = nullptr;
|
||||
|
||||
if (pageOutlet) {
|
||||
pageOutlet->SetInnerRML("");
|
||||
}
|
||||
|
||||
currentPage->pageListeners.clear();
|
||||
currentPage = nullptr;
|
||||
}
|
||||
|
||||
void App::Shutdown() {
|
||||
if (hasShutdown) return;
|
||||
hasShutdown = true;
|
||||
|
||||
TeardownCurrentPage(); // uses ctx->RemoveDataModel - must run while ctx is still valid
|
||||
|
||||
if (doc) {
|
||||
doc->Close(); // deferred destruction, but marks it for cleanup on next Update/Shutdown
|
||||
doc = nullptr;
|
||||
pageOutlet = nullptr;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user