From 133c3d1a0194daa5469d9c4fce6f63f3f6d9127d Mon Sep 17 00:00:00 2001 From: TennesseeTrash Date: Sat, 14 Jun 2025 19:52:18 +0200 Subject: [PATCH 1/2] [Common] Split RGB into a new Common library --- Libraries/CMakeLists.txt | 1 + Libraries/Common/CMakeLists.txt | 11 +++++++++++ .../Kanimaji => Common/Include/Common}/RGB.hpp | 8 ++++---- Libraries/Kanimaji/CMakeLists.txt | 3 +++ Libraries/Kanimaji/Include/Kanimaji/Settings.hpp | 4 +++- Tools/KanimajiTool/CMakeLists.txt | 1 - 6 files changed, 22 insertions(+), 6 deletions(-) create mode 100644 Libraries/Common/CMakeLists.txt rename Libraries/{Kanimaji/Include/Kanimaji => Common/Include/Common}/RGB.hpp (96%) diff --git a/Libraries/CMakeLists.txt b/Libraries/CMakeLists.txt index 9018b17..94ea6b8 100644 --- a/Libraries/CMakeLists.txt +++ b/Libraries/CMakeLists.txt @@ -1,2 +1,3 @@ +add_subdirectory(Common) add_subdirectory(Kanimaji) add_subdirectory(Megane) diff --git a/Libraries/Common/CMakeLists.txt b/Libraries/Common/CMakeLists.txt new file mode 100644 index 0000000..923fc2b --- /dev/null +++ b/Libraries/Common/CMakeLists.txt @@ -0,0 +1,11 @@ +add_library(KVGToolsCommon INTERFACE) + +target_compile_features(KVGToolsCommon + INTERFACE + cxx_std_23 +) + +target_include_directories(KVGToolsCommon + INTERFACE + "Include" +) diff --git a/Libraries/Kanimaji/Include/Kanimaji/RGB.hpp b/Libraries/Common/Include/Common/RGB.hpp similarity index 96% rename from Libraries/Kanimaji/Include/Kanimaji/RGB.hpp rename to Libraries/Common/Include/Common/RGB.hpp index b322879..3ca82a5 100644 --- a/Libraries/Kanimaji/Include/Kanimaji/RGB.hpp +++ b/Libraries/Common/Include/Common/RGB.hpp @@ -1,11 +1,11 @@ -#ifndef KANIMAJI_RGB_HPP -#define KANIMAJI_RGB_HPP +#ifndef KVG_TOOLS_COMMON_RGB_HPP +#define KVG_TOOLS_COMMON_RGB_HPP #include #include #include -namespace Kanimaji +namespace Common { namespace Implementation { @@ -108,4 +108,4 @@ namespace Kanimaji }; } -#endif // KANIMAJI_RGB_HPP +#endif // KVG_TOOLS_COMMON_RGB_HPP diff --git a/Libraries/Kanimaji/CMakeLists.txt b/Libraries/Kanimaji/CMakeLists.txt index b7deb4f..7c72c1b 100644 --- a/Libraries/Kanimaji/CMakeLists.txt +++ b/Libraries/Kanimaji/CMakeLists.txt @@ -8,6 +8,9 @@ target_compile_features(Kanimaji ) target_link_libraries(Kanimaji + PUBLIC + KVGToolsCommon + PRIVATE pugixml ) diff --git a/Libraries/Kanimaji/Include/Kanimaji/Settings.hpp b/Libraries/Kanimaji/Include/Kanimaji/Settings.hpp index f2ee290..47496fa 100644 --- a/Libraries/Kanimaji/Include/Kanimaji/Settings.hpp +++ b/Libraries/Kanimaji/Include/Kanimaji/Settings.hpp @@ -1,13 +1,15 @@ #ifndef KANIMAJI_CONFIG_HPP #define KANIMAJI_CONFIG_HPP -#include "RGB.hpp" +#include #include #include namespace Kanimaji { + using Common::RGB; + enum class Flag { Enable, Disable, }; diff --git a/Tools/KanimajiTool/CMakeLists.txt b/Tools/KanimajiTool/CMakeLists.txt index 3724680..d194754 100644 --- a/Tools/KanimajiTool/CMakeLists.txt +++ b/Tools/KanimajiTool/CMakeLists.txt @@ -8,7 +8,6 @@ target_compile_features(KanimajiTool target_link_libraries(KanimajiTool PRIVATE Kanimaji - Megane ) target_sources(KanimajiTool From 35ce1d8e19b1901038f295a9f5b21c69dfcc761d Mon Sep 17 00:00:00 2001 From: TennesseeTrash Date: Sat, 14 Jun 2025 19:54:03 +0200 Subject: [PATCH 2/2] [Tablegen] Implement a basic table generator --- Libraries/CMakeLists.txt | 1 + Libraries/Tablegen/CMakeLists.txt | 27 +++ .../Tablegen/Include/Tablegen/Settings.hpp | 58 +++++ .../Tablegen/Include/Tablegen/Tablegen.hpp | 23 ++ Libraries/Tablegen/Source/Settings.cpp | 38 ++++ Libraries/Tablegen/Source/Tablegen.cpp | 206 ++++++++++++++++++ 6 files changed, 353 insertions(+) create mode 100644 Libraries/Tablegen/CMakeLists.txt create mode 100644 Libraries/Tablegen/Include/Tablegen/Settings.hpp create mode 100644 Libraries/Tablegen/Include/Tablegen/Tablegen.hpp create mode 100644 Libraries/Tablegen/Source/Settings.cpp create mode 100644 Libraries/Tablegen/Source/Tablegen.cpp diff --git a/Libraries/CMakeLists.txt b/Libraries/CMakeLists.txt index 94ea6b8..43b3004 100644 --- a/Libraries/CMakeLists.txt +++ b/Libraries/CMakeLists.txt @@ -1,3 +1,4 @@ add_subdirectory(Common) add_subdirectory(Kanimaji) add_subdirectory(Megane) +add_subdirectory(Tablegen) diff --git a/Libraries/Tablegen/CMakeLists.txt b/Libraries/Tablegen/CMakeLists.txt new file mode 100644 index 0000000..7b17c78 --- /dev/null +++ b/Libraries/Tablegen/CMakeLists.txt @@ -0,0 +1,27 @@ +include(${PROJECT_SOURCE_DIR}/CMake/pugixml.cmake) + +add_library(Tablegen STATIC) + +target_compile_features(Tablegen + PUBLIC + cxx_std_23 +) + +target_include_directories(Tablegen + PUBLIC + "Include" +) + +target_link_libraries(Tablegen + PUBLIC + KVGToolsCommon + + PRIVATE + pugixml +) + +target_sources(Tablegen + PRIVATE + "Source/Tablegen.cpp" + "Source/Settings.cpp" +) diff --git a/Libraries/Tablegen/Include/Tablegen/Settings.hpp b/Libraries/Tablegen/Include/Tablegen/Settings.hpp new file mode 100644 index 0000000..3e387fc --- /dev/null +++ b/Libraries/Tablegen/Include/Tablegen/Settings.hpp @@ -0,0 +1,58 @@ +#ifndef TABLEGEN_SETTINGS_HPP +#define TABLEGEN_SETTINGS_HPP + +#include + +#include +#include + +namespace Tablegen +{ + using Common::RGB; + + enum class Flag { + Enable, Disable, + }; + + enum class FontSizeUnits { + Px, Em, + }; + std::string ToString(FontSizeUnits units); + + struct CharacterInfo + { + std::string Label; + std::string ImagePath; + std::string AnimationPath; + }; + + struct Settings + { + Flag FullDocument; + + Flag OverrideIndentLevel; + std::size_t IndentLevel; + + double TableWidth; + double TableMargin; + + double TableItemWidth; + double TableItemPadding; + RGB TableItemColour; + + double LabelFontSize; + FontSizeUnits LabelFontUnits; + RGB ButtonColour; + RGB ButtonHoverColour; + double ButtonAnimationLength; + + std::size_t CharactersPerRow; + std::string ImageFormat; + std::string AnimationFormat; + std::vector Characters; + + static Settings Default(); + }; +} + +#endif // TABLEGEN_SETTINGS_HPP diff --git a/Libraries/Tablegen/Include/Tablegen/Tablegen.hpp b/Libraries/Tablegen/Include/Tablegen/Tablegen.hpp new file mode 100644 index 0000000..ade549e --- /dev/null +++ b/Libraries/Tablegen/Include/Tablegen/Tablegen.hpp @@ -0,0 +1,23 @@ +#ifndef TABLEGEN_TABLEGEN_HPP +#define TABLEGEN_TABLEGEN_HPP + +#include "Settings.hpp" + +#ifdef TABLEGEN_EXPOSE_XML +#include +#endif + +#include + +namespace Tablegen +{ + std::string GeneratePage(const Settings& settings = Settings::Default()); + + void GeneratePage(const std::string& path, const Settings& settings = Settings::Default()); + +# ifdef TABLEGEN_EXPOSE_XML + pugi::xml_document GenerateDocument(const Settings& settings = Settings::Default()); +# endif +} + +#endif // TABLEGEN_TABLEGEN_HPP diff --git a/Libraries/Tablegen/Source/Settings.cpp b/Libraries/Tablegen/Source/Settings.cpp new file mode 100644 index 0000000..2602eab --- /dev/null +++ b/Libraries/Tablegen/Source/Settings.cpp @@ -0,0 +1,38 @@ +#include "Tablegen/Settings.hpp" + +#include + +namespace Tablegen +{ + std::string ToString(FontSizeUnits units) + { + switch (units) { + case FontSizeUnits::Px: return "px"; + case FontSizeUnits::Em: return "em"; + default: std::unreachable(); + }; + } + + Settings Settings::Default() + { + return { + .FullDocument = Flag::Enable, + .OverrideIndentLevel = Flag::Disable, + .IndentLevel = 0, + .TableWidth = 90, + .TableMargin = 5, + .TableItemWidth = 30, + .TableItemPadding = 2, + .TableItemColour = RGB::FromHex("#666666"), + .LabelFontSize = 1.75, + .LabelFontUnits = FontSizeUnits::Em, + .ButtonColour = RGB::FromHex("#FF6347"), + .ButtonHoverColour = RGB::FromHex("#FFA500"), + .ButtonAnimationLength = 0.5, + .CharactersPerRow = 3, + .ImageFormat = "https://3011.io/Assets/Images/Kanji/Static/{}.png", + .AnimationFormat = "https://3011.io/Assets/Temp/{}.svg", + .Characters = {}, + }; + } +} diff --git a/Libraries/Tablegen/Source/Tablegen.cpp b/Libraries/Tablegen/Source/Tablegen.cpp new file mode 100644 index 0000000..1a42a31 --- /dev/null +++ b/Libraries/Tablegen/Source/Tablegen.cpp @@ -0,0 +1,206 @@ +#define TABLEGEN_EXPOSE_XML +#include "Tablegen/Tablegen.hpp" + +#include + +#include +#include +#include + +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; + } +}