[Tablegen] Implement a basic table generator

This commit is contained in:
TennesseeTrash 2025-06-14 19:54:03 +02:00
parent 133c3d1a01
commit 35ce1d8e19
6 changed files with 353 additions and 0 deletions

View file

@ -0,0 +1,206 @@
#define TABLEGEN_EXPOSE_XML
#include "Tablegen/Tablegen.hpp"
#include <pugixml.hpp>
#include <format>
#include <ranges>
#include <sstream>
namespace Tablegen
{
namespace vw = std::views;
namespace
{
constexpr std::string_view generalStylesFormat(
"\n"
"{}.kanji-table {{ interpolate-size: allow-keywords; width: {}%; margin: {}%; }}\n"
"{}.kanji-table-row {{ width: 100%; vertical-align: top; }}\n"
"{}.kanji-table-item {{ width: {}%; padding: {}%; }}\n"
"{}.kanji-container {{ background: {}; border-radius: 25px; }}\n"
"{}.kanji-button {{ display: flex; justify-content: center; align-items: center; "
"border-radius: 25px; border: 0; height: 2em; width: 100%; "
"padding: 2%; background: {}; font-size: {}{}; }}\n"
"{}.kanji-button:hover {{ background: {}; }}\n"
"{}.kanji-image-wrap {{ width: 100%; border-radius: 25px; overflow: hidden; "
"box-sizing: border-box; height: 0; transition: height {}s; "
"&.kanji-show {{ height: max-content; }} }}\n"
"{}.kanji-image {{ width: 100%; height: 100%; display: block; appearance: none; }}\n"
);
constexpr std::string_view imageStyleFormat(
"{}#kanji-image-{}{} {{ content: url({}); }}\n"
);
constexpr std::string_view buttonScriptFormat(
"{}document.getElementById(\"kanji-button-{}\")"
".addEventListener(\"click\", e => {{ "
"document.getElementById(\"kanji-{}\")"
".classList.toggle(\"kanji-show\"); }});\n"
);
constexpr std::string_view scriptFormat(
"\n{}document.body.onload = () => {{\n{}{}}}\n{}"
);
constexpr std::string_view stylesFormat(
"{}{}{}{}"
);
std::string GeneralStyles(const Settings& settings, std::string_view indent)
{
return std::format(generalStylesFormat,
indent, settings.TableWidth, settings.TableMargin,
indent,
indent, settings.TableItemWidth, settings.TableItemPadding,
indent, settings.TableItemColour.ToHex(),
indent, settings.ButtonColour.ToHex(), settings.LabelFontSize,
ToString(settings.LabelFontUnits),
indent, settings.ButtonHoverColour.ToHex(),
indent, settings.ButtonAnimationLength,
indent);
}
constexpr std::string ImageStyle(std::string_view indent,
std::size_t index,
std::string_view imageName,
std::string_view imageFormat,
std::string_view extraSpecifier = "")
{
std::string imagePath = std::vformat(imageFormat, std::make_format_args(imageName));
return std::format(imageStyleFormat, indent, index, extraSpecifier, imagePath);
}
constexpr std::string ButtonScript(std::string_view indent, std::size_t index)
{
return std::format(buttonScriptFormat, indent, index, index);
}
constexpr std::string Script(std::string_view indent, std::string_view tagIndent,
std::string_view buttonStatements)
{
return std::format(scriptFormat,
indent, buttonStatements, indent, tagIndent);
}
constexpr std::string Styles(const Settings& settings,
std::string_view indent,
std::string_view tagIndent,
std::string_view staticImageStyles,
std::string_view animatedImageStyles)
{
return std::format(stylesFormat, GeneralStyles(settings, indent),
staticImageStyles, animatedImageStyles, tagIndent);
}
}
std::string GeneratePage(const Settings& settings)
{
pugi::xml_document doc = GenerateDocument(settings);
std::uint32_t formatOptions;
formatOptions = pugi::format_indent | pugi::format_no_escapes | pugi::format_no_declaration;
std::stringstream str;
doc.save(str, " ", formatOptions);
return str.str();
}
void GeneratePage(const std::string& path, const Settings& settings)
{
pugi::xml_document doc = GenerateDocument(settings);
std::uint32_t formatOptions;
formatOptions = pugi::format_indent | pugi::format_no_escapes | pugi::format_no_declaration;
doc.save_file("test.html", " ", formatOptions);
}
pugi::xml_document GenerateDocument(const Settings& settings)
{
pugi::xml_document doc;
pugi::xml_node tableRoot = doc;
if (settings.FullDocument == Flag::Enable) {
doc.append_child(pugi::node_doctype).set_value("html");
pugi::xml_node html = doc.append_child("html");
pugi::xml_node head = html.append_child("head");\
head.append_child("title").append_child(pugi::node_pcdata).set_value("Kanji Table");
tableRoot = html.append_child("body").append_child("main");
}
pugi::xml_node table = tableRoot.append_child("table");
table.append_attribute("class") = "kanji-table";
pugi::xml_node comment = table.append_child(pugi::node_comment);
comment.set_value(" Autogenerated by Tablegen, please avoid editing manually. ");
pugi::xml_node script = table.append_child("script");
pugi::xml_node styles = table.append_child("style");
std::string staticImg;
std::string animatedImg;
std::string buttons;
std::size_t indentLevel = settings.OverrideIndentLevel == Flag::Enable
? settings.IndentLevel
: settings.FullDocument == Flag::Enable ? 5 : 2;
std::string indent(indentLevel * 4, ' ');
std::string tagIndent((indentLevel > 0 ? indentLevel - 1 : 0) * 4, ' ');
pugi::xml_node tableBody = table.append_child("tbody");
for (pugi::xml_node row; const auto& [i, character] : vw::enumerate(settings.Characters)) {
////////////////////////////////////////////////////////////////////
// HTML
////////////////////////////////////////////////////////////////////
if (i % 3 == 0) {
row = tableBody.append_child("tr");
row.append_attribute("class") = "kanji-table-row";
}
pugi::xml_node element = row.append_child("td");
element.append_attribute("class") = "kanji-table-item";
pugi::xml_node container = element.append_child("div");
container.append_attribute("class") = "kanji-container";
pugi::xml_node button = container.append_child("button");
button.append_attribute("class") = "kanji-button";
button.append_attribute("id") = std::format("kanji-button-{}", i);
pugi::xml_node label = button.append_child("span");
label.append_attribute("class") = "kanji-button-text";
label.append_child(pugi::node_pcdata).set_value(character.Label);
pugi::xml_node imageWrap = container.append_child("div");
imageWrap.append_attribute("class") = "kanji-image-wrap";
imageWrap.append_attribute("id") = std::format("kanji-{}", i);
pugi::xml_node image = imageWrap.append_child("input");
image.append_attribute("type") = "checkbox";
image.append_attribute("class") = "kanji-image";
image.append_attribute("id") = std::format("kanji-image-{}", i);
////////////////////////////////////////////////////////////////////
// CSS
////////////////////////////////////////////////////////////////////
staticImg.append(ImageStyle(indent, i, character.ImagePath, settings.ImageFormat));
animatedImg.append(ImageStyle(indent, i, character.AnimationPath,
settings.AnimationFormat, ":checked"));
////////////////////////////////////////////////////////////////////
// JavaScript
////////////////////////////////////////////////////////////////////
buttons.append(ButtonScript(indent, i));
}
styles.append_child(pugi::node_pcdata).set_value(Styles(settings, indent, tagIndent,
staticImg, animatedImg));
script.append_child(pugi::node_pcdata).set_value(Script(indent, tagIndent, buttons));
return doc;
}
}