1 #include "lxgui/gui_addon_registry.hpp"
2 #include "lxgui/gui_event_emitter.hpp"
3 #include "lxgui/gui_frame.hpp"
4 #include "lxgui/gui_layout_node.hpp"
5 #include "lxgui/gui_out.hpp"
6 #include "lxgui/gui_parser_common.hpp"
7 #include "lxgui/gui_root.hpp"
8 #include "lxgui/gui_virtual_root.hpp"
9 #include "lxgui/utils_file_system.hpp"
10 #include "lxgui/utils_string.hpp"
12 #include <lxgui/extern_sol2_state.hpp>
13 #if defined(LXGUI_ENABLE_XML_PARSER)
14 # include <lxgui/extern_pugixml.hpp>
16 #if defined(LXGUI_ENABLE_YAML_PARSER)
17 # include <lxgui/extern_ryml.hpp>
24 class file_line_mappings {
26 explicit file_line_mappings(
const std::string& file_name) : file_name_(file_name) {
27 std::ifstream stream(file_name);
28 if (!stream.is_open())
32 std::size_t prev_pos = 0u;
33 while (std::getline(stream, line)) {
34 file_content_ +=
'\n' + line;
35 line_offsets_.push_back(prev_pos);
36 prev_pos += line.size() + 1u;
39 line_offsets_.push_back(prev_pos);
44 bool is_open()
const {
48 const std::string& get_content()
const {
52 std::pair<std::size_t, std::size_t> get_line_info(std::size_t offset)
const {
53 auto iter = std::lower_bound(line_offsets_.begin(), line_offsets_.end(), offset);
54 if (iter == line_offsets_.end())
55 return std::make_pair(0, 0);
57 std::size_t line_nbr = iter - line_offsets_.begin();
58 std::size_t char_offset = offset - *iter + 1u;
60 return std::make_pair(line_nbr, char_offset);
63 std::string get_location(std::size_t offset)
const {
64 auto location = get_line_info(offset);
65 if (location.first == 0)
66 return file_name_ +
":?";
68 return file_name_ +
":" + utils::to_string(location.first);
72 bool is_open_ =
false;
73 std::string file_name_;
74 std::string file_content_;
75 std::vector<std::size_t> line_offsets_;
79 std::string normalized;
80 bool next_capitalize = capital_first;
85 next_capitalize = c ==
'_';
89 normalized.push_back(c);
95 #if defined(LXGUI_ENABLE_XML_PARSER)
96 void set_node(
const file_line_mappings& file, layout_node& node,
const pugi::xml_node& xml_node) {
97 auto location = file.get_location(xml_node.offset_debug());
98 node.set_location(location);
99 node.set_value_location(location);
102 for (
const auto& attr : xml_node.attributes()) {
104 if (
const auto* node_attr = node.try_get_attribute(name)) {
106 <<
"' duplicated; only first value will be used." << std::endl;
107 node_attr->mark_as_not_accessed();
111 auto& attrib = node.add_attribute();
112 attrib.set_location(location);
113 attrib.set_value_location(location);
114 attrib.set_name(std::move(name));
115 attrib.set_value(attr.value());
119 for (
const auto& elem_node : xml_node.children()) {
120 if (elem_node.type() == pugi::node_pcdata || elem_node.type() == pugi::node_cdata) {
121 value += elem_node.value();
123 auto& child = node.add_child();
124 set_node(file, child, elem_node);
128 node.set_value(value);
132 #if defined(LXGUI_ENABLE_YAML_PARSER)
133 std::string to_string(
const c4::csubstr& c_string) {
134 return std::string(c_string.data(), c_string.size());
138 const file_line_mappings& file,
139 const ryml::Tree& tree,
141 const ryml::NodeRef& yaml_node) {
142 std::string location;
143 if (yaml_node.has_key())
144 location = file.get_location(yaml_node.key().data() - tree.arena().data());
145 else if (yaml_node.has_val())
146 location = file.get_location(yaml_node.val().data() - tree.arena().data());
147 node.set_location(location);
148 node.set_value_location(location);
150 if (yaml_node.has_key())
153 for (
auto elem_node : yaml_node.children()) {
154 switch (elem_node.type()) {
157 std::string attr_location =
158 file.get_location(elem_node.key().data() - tree.arena().data());
159 if (
const auto* node_attr = node.try_get_attribute(name)) {
161 <<
"' duplicated; only first value will be used." << std::endl;
163 <<
" first occurence at: '" << std::endl;
165 << node_attr->get_location() << std::endl;
166 node_attr->mark_as_not_accessed();
170 auto& attrib = node.add_attribute();
171 attrib.set_location(std::move(attr_location));
172 attrib.set_value_location(
173 file.get_location(elem_node.val().data() - tree.arena().data()));
174 attrib.set_name(std::move(name));
175 attrib.set_value(to_string(elem_node.val()));
178 case ryml::KEYMAP: [[fallthrough]];
179 case ryml::MAP: [[fallthrough]];
181 auto& child = node.add_child();
182 set_node(file, tree, child, elem_node);
187 << elem_node.type_str() <<
"'." << std::endl;
195 void addon_registry::parse_layout_file_(
const std::string& file_name,
const addon& add_on) {
196 file_line_mappings file(file_name);
197 if (!file.is_open()) {
198 gui::out <<
gui::error << file_name <<
": could not open file for parsing." << std::endl;
207 #if defined(LXGUI_ENABLE_XML_PARSER)
208 if (extension ==
".xml") {
209 const unsigned int options = pugi::parse_ws_pcdata_single;
211 pugi::xml_document doc;
212 pugi::xml_parse_result result =
213 doc.load_buffer(file.get_content().c_str(), file.get_content().size(), options);
217 << result.description() << std::endl;
221 set_node(file, root, doc.first_child());
226 #if defined(LXGUI_ENABLE_YAML_PARSER)
227 if (extension ==
".yml" || extension ==
".yaml") {
228 ryml::Tree tree = ryml::parse(ryml::to_csubstr(file.get_content()));
229 set_node(file, tree, root, tree.rootref().first_child());
236 <<
": no parser registered for extension '" + extension +
"'." << std::endl;
240 for (
const auto& node : root.get_children()) {
241 if (node.get_name() ==
"Script") {
242 std::string script_file =
243 add_on.directory +
"/" + node.get_attribute_value<std::string>(
"file");
245 auto result = lua_.do_file(script_file);
246 if (!result.valid()) {
247 std::string err = result.get<sol::error>().what();
249 event_emitter_.
fire_event(
"LUA_ERROR", {err});
251 }
else if (node.get_name() ==
"Include") {
253 add_on.directory +
"/" + node.get_attribute_value<std::string>(
"file"), add_on);
259 utils::observer_ptr<frame> obj;
260 auto parent = attr.
parent;
262 obj = parent->create_child(std::move(attr));
264 if (attr.is_virtual) {
274 obj->set_addon(&add_on);
275 obj->parse_layout(node);
276 obj->notify_loaded();
277 }
catch (
const utils::exception& e) {
void fire_event(const std::string &event_name, event_data data=event_data{})
Emmit a new event.
utils::observer_ptr< frame > create_root_frame(frame_core_attributes attr)
Creates a new frame, ready for use, and owned by this frame_container.
registry & get_registry()
Returns the UI object registry, which keeps track of all objects in the UI.
virtual_registry & get_registry()
Returns the UI object registry, which keeps track of all objects in the UI.
region_core_attributes parse_core_attributes(registry ®, virtual_registry &vreg, const layout_node &node, utils::observer_ptr< frame > parent)
Parse "core" attributes from a layout node, before creating a frame.
std::string normalize_node_name(const std::string &name, bool capital_first)
const std::string warning
void warn_for_not_accessed_node(const layout_node &node)
Emit a warning if this node (or any of its attributes/children) was not read.
range_impl::value_range< T > value(T &container)
Expose the value rather than the (key,value) pair.
std::string get_file_extension(const std::string &file)
utils::observer_ptr< frame > parent