Merge branch 'refactor'
This commit is contained in:
@@ -26,6 +26,9 @@ find_package(RmlUi CONFIG REQUIRED)
|
|||||||
|
|
||||||
add_executable(${PROJECT_NAME}
|
add_executable(${PROJECT_NAME}
|
||||||
src/main.cpp
|
src/main.cpp
|
||||||
|
src/database.cpp
|
||||||
|
src/ui.cpp
|
||||||
|
|
||||||
vendor/rmlui_backend/RmlUi_Backend_GLFW_GL3.cpp
|
vendor/rmlui_backend/RmlUi_Backend_GLFW_GL3.cpp
|
||||||
vendor/rmlui_backend/RmlUi_Platform_GLFW.cpp
|
vendor/rmlui_backend/RmlUi_Platform_GLFW.cpp
|
||||||
vendor/rmlui_backend/RmlUi_Renderer_GL3.cpp
|
vendor/rmlui_backend/RmlUi_Renderer_GL3.cpp
|
||||||
|
|||||||
BIN
assets/local.db
BIN
assets/local.db
Binary file not shown.
@@ -7,13 +7,46 @@
|
|||||||
<body data-model="app_data">
|
<body data-model="app_data">
|
||||||
<h1>{{page_title}}</h1>
|
<h1>{{page_title}}</h1>
|
||||||
|
|
||||||
<h2>Critter Roster</h2>
|
<button data-event-click="is_dialog_open = true">Add New Critter</button>
|
||||||
|
|
||||||
|
<!-- Critter Cards -->
|
||||||
<div class="critter-list">
|
<div class="critter-list">
|
||||||
<div data-for="entry : critters" class="critter-card">
|
<div data-for="entry : critters" class="critter-card">
|
||||||
<h3>#{{entry.key}} - {{entry.value.name}}</h3>
|
<h3>#{{entry.key}} - {{entry.value.name}}</h3>
|
||||||
<p><strong>Species:</strong> {{entry.value.species}}</p>
|
<p><strong>Species:</strong> {{entry.value.species}}</p>
|
||||||
<p><strong>Gender:</strong> {{entry.value.gender}}</p>
|
<p><strong>Gender:</strong> {{entry.value.gender}}</p>
|
||||||
<p><strong>Notes:</strong> {{entry.value.notes}}</p>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Modal Dialog Overlay -->
|
||||||
|
<div class="modal-overlay" data-visible="is_dialog_open">
|
||||||
|
<div class="modal-content">
|
||||||
|
<h2>Add New Critter</h2>
|
||||||
|
|
||||||
|
<p class="error" data-visible="error_message != ''">{{error_message}}</p>
|
||||||
|
|
||||||
|
<label>Name:</label>
|
||||||
|
<input type="text" data-value="form.name" />
|
||||||
|
|
||||||
|
<label>Species:</label>
|
||||||
|
<input type="text" data-value="form.species" />
|
||||||
|
|
||||||
|
<label>Gender (M/F):</label>
|
||||||
|
<input type="text" data-value="form.gender" />
|
||||||
|
|
||||||
|
<label>Mother ID (Optional):</label>
|
||||||
|
<input type="text" data-value="form.mother_id" />
|
||||||
|
|
||||||
|
<label>Father ID (Optional):</label>
|
||||||
|
<input type="text" data-value="form.father_id" />
|
||||||
|
|
||||||
|
<label>Notes:</label>
|
||||||
|
<input type="text" data-value="form.notes" />
|
||||||
|
|
||||||
|
<div class="modal-buttons">
|
||||||
|
<button data-event-click="submit_add_critter">Save</button>
|
||||||
|
<button data-event-click="is_dialog_open = false">Cancel</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -266,3 +266,52 @@ progress-value {
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* ===================================
|
||||||
|
Specific app stuff
|
||||||
|
=================================== */
|
||||||
|
|
||||||
|
/* Critter cards */
|
||||||
|
|
||||||
|
/* Modals */
|
||||||
|
.modal-overlay {
|
||||||
|
position: absolute;
|
||||||
|
top: 0px;
|
||||||
|
left: 0px;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: rgba(0, 0, 0, 0.6);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-content {
|
||||||
|
background-color: #2b2b2b;
|
||||||
|
color: #ffffff;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 8px;
|
||||||
|
width: 350px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-content label {
|
||||||
|
display: block;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-content input {
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error {
|
||||||
|
color: #ff5555;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-buttons {
|
||||||
|
margin-top: 15px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
133
src/database.cpp
Normal file
133
src/database.cpp
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
#include "database.hpp"
|
||||||
|
#include <SQLiteCpp/Database.h>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
bool db_get_all_critters(std::map<int, Critter> &critters) {
|
||||||
|
try {
|
||||||
|
SQLite::Database db("./assets/local.db", SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE);
|
||||||
|
SQLite::Statement q(db, "SELECT * FROM critters");
|
||||||
|
|
||||||
|
critters.clear();
|
||||||
|
|
||||||
|
while (q.executeStep()) {
|
||||||
|
int id = q.getColumn(0);
|
||||||
|
|
||||||
|
critters.try_emplace(
|
||||||
|
id,
|
||||||
|
id,
|
||||||
|
q.getColumn(1).getText(),
|
||||||
|
q.getColumn(2).getText(),
|
||||||
|
q.getColumn(3).getInt(),
|
||||||
|
q.getColumn(4).getInt(),
|
||||||
|
q.getColumn(5).getText(),
|
||||||
|
q.getColumn(6).getText()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const std::exception &e) {
|
||||||
|
std::cout << "[ERROR] DB exception: " << e.what() << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool db_add_critter(const Critter &c, int &out_id) {
|
||||||
|
try {
|
||||||
|
SQLite::Database db("./assets/local.db", SQLite::OPEN_READWRITE);
|
||||||
|
SQLite::Statement q(db, "INSERT INTO critters (name, gender, mother_id, father_id, notes, species) VALUES (?, ?, ?, ?, ?, ?)");
|
||||||
|
|
||||||
|
q.bind(1, c.name);
|
||||||
|
q.bind(2, c.gender);
|
||||||
|
q.bind(3, c.mother_id);
|
||||||
|
q.bind(4, c.father_id);
|
||||||
|
q.bind(5, c.notes);
|
||||||
|
q.bind(6, c.species);
|
||||||
|
|
||||||
|
q.exec();
|
||||||
|
out_id = static_cast<int>(db.getLastInsertRowid());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (const std::exception &e) {
|
||||||
|
std::cout << "[ERROR] DB add exception: " << e.what() << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool db_delete_critter(int id) {
|
||||||
|
try {
|
||||||
|
SQLite::Database db("./assets/local.db", SQLite::OPEN_READWRITE);
|
||||||
|
SQLite::Statement q(db, "DELETE FROM critters WHERE id = ?");
|
||||||
|
q.bind(1, id);
|
||||||
|
q.exec();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (const std::exception &e) {
|
||||||
|
std::cout << "[ERROR] DB delete exception: " << e.what() << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void db_print_all_critters(const std::map<int, Critter> &critters) {
|
||||||
|
std::cout << "Current loaded critters:";
|
||||||
|
for (const auto &[key, c] : critters) {
|
||||||
|
std::cout << "\nCritter " << key << "\n";
|
||||||
|
std::cout << "ID: " << c.id << std::endl;
|
||||||
|
std::cout << "Name: " << c.name << std::endl;
|
||||||
|
std::cout << "Gender: " << c.gender << std::endl;
|
||||||
|
std::cout << "Mother ID: " << c.mother_id << std::endl;
|
||||||
|
std::cout << "Father ID: " << c.father_id << std::endl;
|
||||||
|
std::cout << "Notes: " << c.notes << std::endl;
|
||||||
|
std::cout << "Species: " << c.species << std::endl;
|
||||||
|
}
|
||||||
|
std::cout << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool validate_critter(const Critter &c, const std::map<int, Critter> &all_critters, std::string &out_error) {
|
||||||
|
if (c.mother_id != 0 && c.father_id != 0 && c.mother_id == c.father_id) {
|
||||||
|
out_error = "Mother ID and Father ID cannot be the same.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c.mother_id != 0) {
|
||||||
|
auto it = all_critters.find(c.mother_id);
|
||||||
|
if (it == all_critters.end()) {
|
||||||
|
out_error = "Mother ID " + std::to_string(c.mother_id) + " does not exist.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Critter &mother = it->second;
|
||||||
|
|
||||||
|
if (mother.gender != "F" && mother.gender != "Female") {
|
||||||
|
out_error = "Mother must have female gender ('F' or 'Female').";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mother.species != c.species) {
|
||||||
|
out_error = "Mother species ('" + mother.species + "') must match critter species ('" + c.species + "').";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c.father_id != 0) {
|
||||||
|
auto it = all_critters.find(c.father_id);
|
||||||
|
if (it == all_critters.end()) {
|
||||||
|
out_error = "Father ID " + std::to_string(c.father_id) + " does not exist.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Critter &father = it->second;
|
||||||
|
|
||||||
|
if (father.gender != "M" && father.gender != "Male") {
|
||||||
|
out_error = "Father must have male gender ('M' or 'Male').";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (father.species != c.species) {
|
||||||
|
out_error = "Father species ('" + father.species + "') must match critter species ('" + c.species + "').";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
109
src/database.hpp
109
src/database.hpp
@@ -1,104 +1,11 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
#include "critter.hpp"
|
#include "critter.hpp"
|
||||||
#include <SQLiteCpp/Database.h>
|
|
||||||
#include <iostream>
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
inline static bool db_get_all_critters(std::map<int, Critter> &critters) {
|
bool db_get_all_critters(std::map<int, Critter> &critters);
|
||||||
try {
|
bool db_add_critter(const Critter &c, int &out_id);
|
||||||
SQLite::Database db("./assets/local.db", SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE);
|
bool db_delete_critter(int id);
|
||||||
SQLite::Statement q(db, "SELECT * FROM critters");
|
void db_print_all_critters(const std::map<int, Critter> &critters);
|
||||||
|
bool validate_critter(const Critter &c, const std::map<int, Critter> &all_critters, std::string &out_error);
|
||||||
critters.clear();
|
|
||||||
|
|
||||||
while (q.executeStep()) {
|
|
||||||
int id = q.getColumn(0);
|
|
||||||
|
|
||||||
critters.try_emplace(
|
|
||||||
id,
|
|
||||||
id,
|
|
||||||
q.getColumn(1).getText(),
|
|
||||||
q.getColumn(2).getText(),
|
|
||||||
q.getColumn(3).getInt(),
|
|
||||||
q.getColumn(4).getInt(),
|
|
||||||
q.getColumn(5).getText(),
|
|
||||||
q.getColumn(6).getText()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (const std::exception &e) {
|
|
||||||
std::cout << "[ERROR] DB exception: " << e.what() << std::endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline static void db_print_all_critters(std::map<int, Critter> &critters) {
|
|
||||||
std::cout << "Current loaded critters:";
|
|
||||||
for (auto c : critters) {
|
|
||||||
std::cout << "\nCritter " << c.first << "\n";
|
|
||||||
std::cout << "ID: " << c.second.id << std::endl;
|
|
||||||
std::cout << "Name:" << c.second.name << std::endl;
|
|
||||||
std::cout << "Gender: " << c.second.gender << std::endl;
|
|
||||||
std::cout << "Mother ID: " << c.second.mother_id << std::endl;
|
|
||||||
std::cout << "Father ID: " << c.second.father_id << std::endl;
|
|
||||||
std::cout << "Notes: " << c.second.notes << std::endl;
|
|
||||||
std::cout << "Species: " << c.second.species << std::endl;
|
|
||||||
}
|
|
||||||
std::cout << "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
inline static bool validate_critter(const Critter &c, std::map<int, Critter> &all_critters, std::string &out_error) {
|
|
||||||
if (c.mother_id != 0 && c.father_id != 0 && c.mother_id == c.father_id) {
|
|
||||||
out_error = "Mother ID and Father ID cannot be the same.";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mother Validation
|
|
||||||
if (c.mother_id != 0) {
|
|
||||||
auto it = all_critters.find(c.mother_id);
|
|
||||||
if (it == all_critters.end()) {
|
|
||||||
out_error = "Mother ID " + std::to_string(c.mother_id) + " does not exist.";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Critter& mother = it->second;
|
|
||||||
|
|
||||||
if (mother.gender != "F" && mother.gender != "Female") {
|
|
||||||
out_error = "Mother must have female gender ('F' or 'Female').";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mother.species != c.species) {
|
|
||||||
out_error = "Mother species ('" + mother.species + "') must match critter species ('" + c.species + "').";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Father Validation
|
|
||||||
if (c.father_id != 0) {
|
|
||||||
auto it = all_critters.find(c.father_id);
|
|
||||||
if (it == all_critters.end()) {
|
|
||||||
out_error = "Father ID " + std::to_string(c.father_id) + " does not exist.";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Critter& father = it->second;
|
|
||||||
|
|
||||||
if (father.gender != "M" && father.gender != "Male") {
|
|
||||||
out_error = "Father must have male gender ('M' or 'Male').";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (father.species != c.species) {
|
|
||||||
out_error = "Father species ('" + father.species + "') must match critter species ('" + c.species + "').";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline static bool db_try_add_critter() {
|
|
||||||
|
|
||||||
}
|
|
||||||
188
src/main.cpp
188
src/main.cpp
@@ -1,162 +1,96 @@
|
|||||||
#include <RmlUi/Config/Config.h>
|
|
||||||
#include <RmlUi/Core/Core.h>
|
|
||||||
#include <RmlUi/Core/DataModelHandle.h>
|
|
||||||
#include <RmlUi/Core/ElementDocument.h>
|
|
||||||
#include <RmlUi/Core/Math.h>
|
|
||||||
#include <RmlUi/Lua/Interpreter.h>
|
|
||||||
#include <RmlUi/Lua/Lua.h>
|
|
||||||
#include <SQLiteCpp/Database.h>
|
|
||||||
#include <iostream>
|
|
||||||
#include <RmlUi/Core.h>
|
|
||||||
#include <RmlUi_Backend.h>
|
|
||||||
#include <RmlUi/Lua/Lua.h>
|
|
||||||
#include <RmlUi/Core/Context.h>
|
|
||||||
#include <lua.h>
|
|
||||||
#include <RmlUi/Debugger/Debugger.h>
|
|
||||||
#include "critter.hpp"
|
#include "critter.hpp"
|
||||||
#include <SQLiteCpp/SQLiteCpp.h>
|
#include "database.hpp"
|
||||||
|
#include "ui.hpp"
|
||||||
|
|
||||||
|
#include <RmlUi/Core.h>
|
||||||
|
#include <RmlUi/Debugger/Debugger.h>
|
||||||
|
#include <RmlUi/Lua/Lua.h>
|
||||||
|
#include <RmlUi_Backend.h>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "database.hpp"
|
|
||||||
|
|
||||||
static bool is_initalized = false;
|
static bool is_initalized = false;
|
||||||
static bool is_running = false;
|
static bool is_running = false;
|
||||||
static Rml::Context *ctx;
|
static Rml::Context *ctx = nullptr;
|
||||||
static std::map<int, Critter> critters = {};
|
static std::map<int, Critter> critters;
|
||||||
static std::vector<CritterEntry> critter_list = {};
|
static std::vector<CritterEntry> critter_list;
|
||||||
|
static AppData app_data;
|
||||||
|
|
||||||
struct AppData {
|
void shutdown() {
|
||||||
Rml::String page_title = "Main";
|
Rml::Shutdown();
|
||||||
} app_data;
|
Backend::Shutdown();
|
||||||
|
|
||||||
void SyncMapToList() {
|
|
||||||
critter_list.clear();
|
|
||||||
for (const auto &[id, critter] : critters) {
|
|
||||||
critter_list.push_back({id, critter});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Shutdown() {
|
bool initialize() {
|
||||||
Rml::Shutdown();
|
if (!Backend::Initialize("CritterFolio", 800, 600, true)) {
|
||||||
Backend::Shutdown();
|
std::cout << "[ERROR] Failed to initialize backend.\n";
|
||||||
}
|
return false;
|
||||||
|
}
|
||||||
|
std::cout << "[INFO] RmlUI Backend initialized.\n";
|
||||||
|
|
||||||
bool SetupDataBinding(Rml::Context *context, Rml::DataModelHandle &model_handle) {
|
Rml::SetSystemInterface(Backend::GetSystemInterface());
|
||||||
Rml::DataModelConstructor dmc = ctx->CreateDataModel("app_data");
|
Rml::SetRenderInterface(Backend::GetRenderInterface());
|
||||||
if (!dmc) {
|
Rml::Initialise();
|
||||||
std::cout << "[ERROR] could not create 'app_data' data binding.";
|
Rml::Lua::Initialise();
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (auto handle = dmc.RegisterStruct<Critter>()) {
|
std::cout << "[INFO] Loading local DB...\n";
|
||||||
handle.RegisterMember("id", &Critter::id);
|
if (!db_get_all_critters(critters)) {
|
||||||
handle.RegisterMember("name", &Critter::name);
|
std::cout << "[ERROR] Local DB failed to initialize.\n";
|
||||||
handle.RegisterMember("gender", &Critter::gender);
|
return false;
|
||||||
handle.RegisterMember("species", &Critter::species);
|
}
|
||||||
handle.RegisterMember("notes", &Critter::notes);
|
std::cout << "[INFO] Local DB initialized.\n";
|
||||||
handle.RegisterMember("mother_id", &Critter::mother_id);
|
|
||||||
handle.RegisterMember("father_id", &Critter::father_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (auto handle = dmc.RegisterStruct<CritterEntry>()) {
|
sync_map_to_list(critters, critter_list);
|
||||||
handle.RegisterMember("key", &CritterEntry::key);
|
|
||||||
handle.RegisterMember("value", &CritterEntry::value);
|
|
||||||
}
|
|
||||||
|
|
||||||
dmc.RegisterArray<std::vector<CritterEntry>>();
|
ctx = Rml::CreateContext("main", Rml::Vector2i(800, 600));
|
||||||
dmc.Bind("critters", &critter_list);
|
if (!ctx) {
|
||||||
|
std::cout << "[ERROR] Failed to create context.\n";
|
||||||
dmc.Bind("page_title", &app_data.page_title);
|
return false;
|
||||||
|
}
|
||||||
model_handle = dmc.GetModelHandle();
|
std::cout << "[INFO] Main context initialized.\n";
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Initalize() {
|
|
||||||
if (!Backend::Initialize("CritterFolio", 800, 600, true)) {
|
|
||||||
std::cout << "[ERROR] Failed to initalize the backend.";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
std::cout << "[INFO] RmlUI Backend initalized.\n";
|
|
||||||
|
|
||||||
Rml::SetSystemInterface(Backend::GetSystemInterface());
|
|
||||||
Rml::SetRenderInterface(Backend::GetRenderInterface());
|
|
||||||
Rml::Initialise();
|
|
||||||
Rml::Lua::Initialise();
|
|
||||||
|
|
||||||
// TODO: Add more error handling for initalization
|
|
||||||
|
|
||||||
std::cout << "[INFO] Loading local DB...\n";
|
|
||||||
bool db_result = db_get_all_critters(critters);
|
|
||||||
if (!db_result) {
|
|
||||||
std::cout << "[ERROR] Local DB failed to initalize.\n";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
std::cout << "[INFO] Local DB initalized.\n";
|
|
||||||
|
|
||||||
SyncMapToList();
|
|
||||||
|
|
||||||
// ------- DB TESTING
|
|
||||||
// db_print_all_critters(critters);
|
|
||||||
// ------- END DB TESTING
|
|
||||||
|
|
||||||
ctx = Rml::CreateContext("main", Rml::Vector2i(800, 600));
|
|
||||||
if (!ctx) {
|
|
||||||
std::cout << "[ERROR] Failed to create context.";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
std::cout << "[INFO] Main context initalized.\n";
|
|
||||||
|
|
||||||
Rml::Debugger::Initialise(ctx);
|
|
||||||
|
|
||||||
// lua_State *L = Rml::Lua::Interpreter::GetLuaState();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Update(Rml::DataModelHandle model_handle) {
|
|
||||||
|
|
||||||
|
Rml::Debugger::Initialise(ctx);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
std::cout << "[INFO] Starting critterfolio...\n";
|
std::cout << "[INFO] Starting CritterFolio...\n";
|
||||||
|
|
||||||
is_initalized = Initalize();
|
is_initalized = initialize();
|
||||||
if (!is_initalized) {
|
if (!is_initalized) {
|
||||||
std::cout << "[ERROR] Failed to initalize, shutting down.";
|
std::cout << "[ERROR] Failed to initialize, shutting down.\n";
|
||||||
Shutdown();
|
shutdown();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
std::cout << "[INFO] Application initalized, all systems running.\n";
|
|
||||||
|
|
||||||
is_running = true;
|
is_running = true;
|
||||||
|
|
||||||
Rml::DataModelHandle app_data_handle;
|
Rml::DataModelHandle app_data_handle;
|
||||||
|
if (!setup_data_binding(ctx, app_data_handle, app_data, critter_list, critters)) {
|
||||||
|
shutdown();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
SetupDataBinding(ctx, app_data_handle);
|
Rml::ElementDocument *doc = ctx->LoadDocument("./assets/main.rml");
|
||||||
|
|
||||||
Rml::ElementDocument *doc;
|
|
||||||
doc = ctx->LoadDocument("./assets/main.rml");
|
|
||||||
if (!doc) {
|
if (!doc) {
|
||||||
std::cout << "[ERROR] Could no load assets/main.rml document, shutting down.";
|
std::cout << "[ERROR] Could not load assets/main.rml document, shutting down.\n";
|
||||||
Shutdown();
|
shutdown();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
std::cout << "[INFO] Main page loaded.\n";
|
std::cout << "[INFO] Main page loaded.\n";
|
||||||
|
|
||||||
doc->Show();
|
doc->Show();
|
||||||
|
|
||||||
while(is_running) {
|
while (is_running) {
|
||||||
is_running = Backend::ProcessEvents(ctx);
|
is_running = Backend::ProcessEvents(ctx);
|
||||||
|
ctx->Update();
|
||||||
Update(app_data_handle);
|
Backend::BeginFrame();
|
||||||
|
ctx->Render();
|
||||||
ctx->Update();
|
Backend::PresentFrame();
|
||||||
Backend::BeginFrame();
|
|
||||||
ctx->Render();
|
|
||||||
Backend::PresentFrame();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Shutdown();
|
shutdown();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
88
src/ui.cpp
Normal file
88
src/ui.cpp
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
#include "ui.hpp"
|
||||||
|
#include "database.hpp"
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
void sync_map_to_list(const std::map<int, Critter> &critters, std::vector<CritterEntry> &critter_list) {
|
||||||
|
critter_list.clear();
|
||||||
|
for (const auto &[id, critter] : critters) {
|
||||||
|
critter_list.push_back({id, critter});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool setup_data_binding(Rml::Context *context, Rml::DataModelHandle &model_handle, AppData &app_data, std::vector<CritterEntry> &critter_list, std::map<int, Critter> &critters) {
|
||||||
|
Rml::DataModelConstructor dmc = context->CreateDataModel("app_data");
|
||||||
|
if (!dmc) {
|
||||||
|
std::cout << "[ERROR] Could not create 'app_data' data binding.\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto handle = dmc.RegisterStruct<Critter>()) {
|
||||||
|
handle.RegisterMember("id", &Critter::id);
|
||||||
|
handle.RegisterMember("name", &Critter::name);
|
||||||
|
handle.RegisterMember("gender", &Critter::gender);
|
||||||
|
handle.RegisterMember("species", &Critter::species);
|
||||||
|
handle.RegisterMember("notes", &Critter::notes);
|
||||||
|
handle.RegisterMember("mother_id", &Critter::mother_id);
|
||||||
|
handle.RegisterMember("father_id", &Critter::father_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto handle = dmc.RegisterStruct<CritterEntry>()) {
|
||||||
|
handle.RegisterMember("key", &CritterEntry::key);
|
||||||
|
handle.RegisterMember("value", &CritterEntry::value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto handle = dmc.RegisterStruct<NewCritterForm>()) {
|
||||||
|
handle.RegisterMember("name", &NewCritterForm::name);
|
||||||
|
handle.RegisterMember("gender", &NewCritterForm::gender);
|
||||||
|
handle.RegisterMember("mother_id", &NewCritterForm::mother_id);
|
||||||
|
handle.RegisterMember("father_id", &NewCritterForm::father_id);
|
||||||
|
handle.RegisterMember("notes", &NewCritterForm::notes);
|
||||||
|
handle.RegisterMember("species", &NewCritterForm::species);
|
||||||
|
}
|
||||||
|
|
||||||
|
dmc.RegisterArray<std::vector<CritterEntry>>();
|
||||||
|
|
||||||
|
dmc.Bind("critters", &critter_list);
|
||||||
|
dmc.Bind("page_title", &app_data.page_title);
|
||||||
|
dmc.Bind("is_dialog_open", &app_data.is_dialog_open);
|
||||||
|
dmc.Bind("error_message", &app_data.error_message);
|
||||||
|
dmc.Bind("form", &app_data.form);
|
||||||
|
|
||||||
|
dmc.BindEventCallback("submit_add_critter", [&app_data, &critters, &critter_list](Rml::DataModelHandle handle, Rml::Event &, const Rml::VariantList &) {
|
||||||
|
Critter temp;
|
||||||
|
temp.name = app_data.form.name;
|
||||||
|
temp.gender = app_data.form.gender;
|
||||||
|
temp.mother_id = app_data.form.mother_id;
|
||||||
|
temp.father_id = app_data.form.father_id;
|
||||||
|
temp.notes = app_data.form.notes;
|
||||||
|
temp.species = app_data.form.species;
|
||||||
|
|
||||||
|
std::string err;
|
||||||
|
if (!validate_critter(temp, critters, err)) {
|
||||||
|
app_data.error_message = err;
|
||||||
|
handle.DirtyVariable("error_message");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int new_id = 0;
|
||||||
|
if (db_add_critter(temp, new_id)) {
|
||||||
|
db_get_all_critters(critters);
|
||||||
|
sync_map_to_list(critters, critter_list);
|
||||||
|
|
||||||
|
app_data.form.Clear();
|
||||||
|
app_data.error_message.clear();
|
||||||
|
app_data.is_dialog_open = false;
|
||||||
|
|
||||||
|
handle.DirtyVariable("critters");
|
||||||
|
handle.DirtyVariable("form");
|
||||||
|
handle.DirtyVariable("error_message");
|
||||||
|
handle.DirtyVariable("is_dialog_open");
|
||||||
|
} else {
|
||||||
|
app_data.error_message = "Failed to write to database.";
|
||||||
|
handle.DirtyVariable("error_message");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
model_handle = dmc.GetModelHandle();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
35
src/ui.hpp
Normal file
35
src/ui.hpp
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "critter.hpp"
|
||||||
|
#include <RmlUi/Core/Context.h>
|
||||||
|
#include <RmlUi/Core/DataModelHandle.h>
|
||||||
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
struct NewCritterForm {
|
||||||
|
Rml::String name;
|
||||||
|
Rml::String gender = "F";
|
||||||
|
int mother_id = 0;
|
||||||
|
int father_id = 0;
|
||||||
|
Rml::String notes;
|
||||||
|
Rml::String species;
|
||||||
|
|
||||||
|
void Clear() {
|
||||||
|
name.clear();
|
||||||
|
gender = "F";
|
||||||
|
mother_id = 0;
|
||||||
|
father_id = 0;
|
||||||
|
notes.clear();
|
||||||
|
species.clear();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AppData {
|
||||||
|
Rml::String page_title = "Main";
|
||||||
|
bool is_dialog_open = false;
|
||||||
|
Rml::String error_message;
|
||||||
|
NewCritterForm form;
|
||||||
|
};
|
||||||
|
|
||||||
|
void sync_map_to_list(const std::map<int, Critter> &critters, std::vector<CritterEntry> &critter_list);
|
||||||
|
bool setup_data_binding(Rml::Context *context, Rml::DataModelHandle &model_handle, AppData &app_data, std::vector<CritterEntry> &critter_list, std::map<int, Critter> &critters);
|
||||||
Reference in New Issue
Block a user