lxgui
Loading...
Searching...
No Matches
gui_frame_parser.cpp
1#include "lxgui/gui_backdrop.hpp"
2#include "lxgui/gui_frame.hpp"
3#include "lxgui/gui_layered_region.hpp"
4#include "lxgui/gui_layout_node.hpp"
5#include "lxgui/gui_manager.hpp"
6#include "lxgui/gui_out.hpp"
7#include "lxgui/gui_parser_common.hpp"
8#include "lxgui/gui_root.hpp"
9#include "lxgui/gui_virtual_root.hpp"
10#include "lxgui/utils_string.hpp"
11
12namespace lxgui::gui {
13
26
32
34 if (const auto attr = node.try_get_attribute_value<bool>("hidden"))
35 set_shown(!attr.value());
36
37 if (node.get_attribute_value_or<bool>("setAllAnchors", false))
38 set_all_anchors("$parent");
39
40 if (const auto attr = node.try_get_attribute_value<float>("alpha"))
41 set_alpha(attr.value());
42 if (const auto attr = node.try_get_attribute_value<bool>("topLevel"))
43 set_top_level(attr.value());
44 if (const auto attr = node.try_get_attribute_value<bool>("movable"))
45 set_movable(attr.value());
46 if (const auto attr = node.try_get_attribute_value<bool>("resizable"))
47 set_resizable(attr.value());
48 if (const auto attr = node.try_get_attribute_value<strata>("strata"))
49 set_strata(attr.value());
50
51 if (const auto* attr = node.try_get_attribute("level")) {
52 if (!is_virtual_) {
53 std::string level = utils::to_lower(std::string(attr->get_value()));
54 if (level != "parent") {
55 if (const auto val = attr->try_get_value<int>())
56 set_level(val.value());
57 }
58 } else {
59 gui::out << gui::warning << node.get_location() << ": "
60 << "\"level\" is not allowed for virtual regions. Ignored." << std::endl;
61 }
62 }
63
64 if (const auto attr = node.try_get_attribute_value<bool>("enableMouse"))
65 set_mouse_enabled(attr.value());
66 if (const auto attr = node.try_get_attribute_value<bool>("enableMouseMove"))
67 set_mouse_move_enabled(attr.value());
68 if (const auto attr = node.try_get_attribute_value<bool>("enableMouseClick"))
69 set_mouse_click_enabled(attr.value());
70 if (const auto attr = node.try_get_attribute_value<bool>("enableMouseWheel"))
71 set_mouse_wheel_enabled(attr.value());
72 if (const auto attr = node.try_get_attribute_value<bool>("enableKeyboard"))
73 set_keyboard_enabled(attr.value());
74 if (const auto attr = node.try_get_attribute_value<bool>("clampedToScreen"))
75 set_clamped_to_screen(attr.value());
76 if (const auto attr = node.try_get_attribute_value<bool>("autoFocus"))
77 enable_auto_focus(attr.value());
78 if (const auto attr = node.try_get_attribute_value<float>("updateRate"))
79 set_update_rate(attr.value());
80}
81
83 if (const layout_node* resize_bounds_node = node.try_get_child("ResizeBounds")) {
84 if (const layout_node* min_node = resize_bounds_node->try_get_child("Min")) {
85 auto dimensions = parse_dimension_node_(*min_node);
86 bool has_x = dimensions.second.x.has_value();
87 bool has_y = dimensions.second.y.has_value();
88 if (dimensions.first == anchor_type::abs) {
89 if (has_x && has_y) {
91 vector2f(dimensions.second.x.value(), dimensions.second.y.value()));
92 } else if (has_x)
93 set_min_width(dimensions.second.x.value());
94 else if (has_y)
95 set_min_height(dimensions.second.y.value());
96 } else {
97 gui::out << gui::warning << min_node->get_location() << ": "
98 << "\"RelDimension\" for ResizeBounds:Min is not yet supported. Skipped."
99 << std::endl;
100 }
101 }
102
103 if (const layout_node* max_node = resize_bounds_node->try_get_child("Max")) {
104 auto dimensions = parse_dimension_node_(*max_node);
105 bool has_x = dimensions.second.x.has_value();
106 bool has_y = dimensions.second.y.has_value();
107 if (dimensions.first == anchor_type::abs) {
108 if (has_x && has_y) {
110 vector2f(dimensions.second.x.value(), dimensions.second.y.value()));
111 } else if (has_x)
112 set_max_width(dimensions.second.x.value());
113 else if (has_y)
114 set_max_height(dimensions.second.y.value());
115 } else {
116 gui::out << gui::warning << max_node->get_location() << ": "
117 << "\"RelDimension\" for ResizeBounds:Max is not yet supported. Skipped."
118 << std::endl;
119 }
120 }
121 }
122}
123
125 if (const layout_node* title_region_node = node.try_get_child("TitleRegion")) {
127
128 if (title_region_)
129 title_region_->parse_layout(*title_region_node);
130 }
131}
132
134 if (const layout_node* backdrop_node = node.try_get_child("Backdrop")) {
135 std::unique_ptr<backdrop> bdrop(new backdrop(*this));
136
137 bdrop->set_background(
138 parse_file_name(backdrop_node->get_attribute_value_or<std::string>("bgFile", "")));
139
140 bdrop->set_edge(
141 parse_file_name(backdrop_node->get_attribute_value_or<std::string>("edgeFile", "")));
142
143 bdrop->set_background_tilling(backdrop_node->get_attribute_value_or<bool>("tile", false));
144
145 if (const layout_node* bg_insets_node = backdrop_node->try_get_child("BackgroundInsets")) {
146 const layout_node* abs_inset_node = bg_insets_node->try_get_child("AbsInset");
147 const layout_node* rel_inset_node = bg_insets_node->try_get_child("RelInset");
148
149 if (abs_inset_node && rel_inset_node) {
150 gui::out << gui::warning << bg_insets_node->get_location()
151 << ": BackgroundInsets node can only contain one of AbsInset or "
152 "RelInset, but not both. RelInset ignored."
153 << std::endl;
154 }
155
156 if (!abs_inset_node && !rel_inset_node) {
157 gui::out << gui::warning << bg_insets_node->get_location()
158 << ": BackgroundInsets node must contain one of AbsInset or RelInset."
159 << std::endl;
160 return;
161 }
162
163 if (abs_inset_node) {
164 bdrop->set_background_insets(bounds2f(
165 abs_inset_node->get_attribute_value_or<float>("left", 0.0f),
166 abs_inset_node->get_attribute_value_or<float>("right", 0.0f),
167 abs_inset_node->get_attribute_value_or<float>("top", 0.0f),
168 abs_inset_node->get_attribute_value_or<float>("bottom", 0.0f)));
169 } else {
170 gui::out << gui::warning << rel_inset_node->get_location() << ": "
171 << "RelInset for Backdrop:BackgroundInsets is not yet supported (" << name_
172 << ")." << std::endl;
173 }
174 }
175
176 if (const layout_node* edge_insets_node = backdrop_node->try_get_child("EdgeInsets")) {
177 const layout_node* abs_inset_node = edge_insets_node->try_get_child("AbsInset");
178 const layout_node* rel_inset_node = edge_insets_node->try_get_child("RelInset");
179
180 if (abs_inset_node && rel_inset_node) {
181 gui::out << gui::warning << edge_insets_node->get_location()
182 << ": EdgeInsets node can only contain one of AbsInset or RelInset, but "
183 "not both. RelInset ignored."
184 << std::endl;
185 }
186
187 if (!abs_inset_node && !rel_inset_node) {
188 gui::out << gui::warning << edge_insets_node->get_location()
189 << ": EdgeInsets node must contain one of AbsInset or RelInset."
190 << std::endl;
191 return;
192 }
193
194 if (abs_inset_node) {
195 bdrop->set_edge_insets(bounds2f(
196 abs_inset_node->get_attribute_value_or<float>("left", 0.0f),
197 abs_inset_node->get_attribute_value_or<float>("right", 0.0f),
198 abs_inset_node->get_attribute_value_or<float>("top", 0.0f),
199 abs_inset_node->get_attribute_value_or<float>("bottom", 0.0f)));
200 } else {
201 gui::out << gui::warning << rel_inset_node->get_location() << ": "
202 << "RelInset for Backdrop:EdgeInsets is not yet supported (" << name_
203 << ")." << std::endl;
204 }
205 }
206
207 if (const layout_node* color_node = backdrop_node->try_get_child("BackgroundColor"))
208 bdrop->set_background_color(parse_color_node_(*color_node));
209
210 if (const layout_node* color_node = backdrop_node->try_get_child("EdgeColor"))
211 bdrop->set_edge_color(parse_color_node_(*color_node));
212
213 if (const layout_node* edge_size_node = backdrop_node->try_get_child("EdgeSize")) {
214 const layout_node* abs_value_node = edge_size_node->try_get_child("AbsValue");
215 const layout_node* rel_value_node = edge_size_node->try_get_child("RelValue");
216
217 if (abs_value_node && rel_value_node) {
218 gui::out << gui::warning << edge_size_node->get_location()
219 << ": EdgeSize node can only contain one of AbsValue or RelValue, but "
220 "not both. RelValue ignored."
221 << std::endl;
222 }
223
224 if (!abs_value_node && !rel_value_node) {
225 gui::out << gui::warning << edge_size_node->get_location()
226 << ": EdgeSize node must contain one of AbsValue or RelValue."
227 << std::endl;
228 return;
229 }
230
231 if (abs_value_node) {
232 bdrop->set_edge_size(abs_value_node->get_attribute_value_or("x", 0.0f));
233 } else {
234 gui::out << gui::warning << rel_value_node->get_location() << ": "
235 << "RelValue for Backdrop:EdgeSize is not yet supported (" << name_ << ")."
236 << std::endl;
237 }
238 }
239
240 if (const layout_node* tile_size_node = backdrop_node->try_get_child("TileSize")) {
241 const layout_node* abs_value_node = tile_size_node->try_get_child("AbsValue");
242 const layout_node* rel_value_node = tile_size_node->try_get_child("RelValue");
243
244 if (abs_value_node && rel_value_node) {
245 gui::out << gui::warning << tile_size_node->get_location()
246 << ": TileSize node can only contain one of AbsValue or RelValue, but "
247 "not both. RelValue ignored."
248 << std::endl;
249 }
250
251 if (!abs_value_node && !rel_value_node) {
252 gui::out << gui::warning << tile_size_node->get_location()
253 << ": TileSize node must contain one of AbsValue or RelValue."
254 << std::endl;
255 return;
256 }
257
258 if (abs_value_node) {
259 bdrop->set_tile_size(abs_value_node->get_attribute_value_or("x", 0.0f));
260 } else {
261 gui::out << gui::warning << rel_value_node->get_location() << ": "
262 << "RelValue for Backdrop:TileSize is not yet supported (" << name_ << ")."
263 << std::endl;
264 }
265 }
266
267 set_backdrop(std::move(bdrop));
268 }
269}
270
272 if (const layout_node* hit_rect_block = node.try_get_child("HitRectInsets")) {
273 const layout_node* abs_inset_node = hit_rect_block->try_get_child("AbsInset");
274 const layout_node* rel_inset_node = hit_rect_block->try_get_child("RelInset");
275
276 if (abs_inset_node && rel_inset_node) {
277 gui::out << gui::warning << hit_rect_block->get_location()
278 << ": HitRectInsets node can only contain one of AbsInset or RelInset, but "
279 "not both. RelInset ignored."
280 << std::endl;
281 }
282
283 if (!abs_inset_node && !rel_inset_node) {
284 gui::out << gui::warning << hit_rect_block->get_location()
285 << ": HitRectInsets node must contain one of AbsInset or RelInset."
286 << std::endl;
287 return;
288 }
289
290 if (abs_inset_node) {
292 abs_inset_node->get_attribute_value_or<float>("left", 0.0f),
293 abs_inset_node->get_attribute_value_or<float>("right", 0.0f),
294 abs_inset_node->get_attribute_value_or<float>("top", 0.0f),
295 abs_inset_node->get_attribute_value_or<float>("bottom", 0.0f)));
296 } else {
298 rel_inset_node->get_attribute_value_or<float>("left", 0.0f),
299 rel_inset_node->get_attribute_value_or<float>("right", 0.0f),
300 rel_inset_node->get_attribute_value_or<float>("top", 0.0f),
301 rel_inset_node->get_attribute_value_or<float>("bottom", 0.0f)));
302 }
303 }
304}
305
306utils::observer_ptr<layered_region> frame::parse_region_(
307 const layout_node& node, const std::string& layer_name, const std::string& type) {
308 try {
309 auto attr = parse_core_attributes(
310 get_manager().get_root().get_registry(),
311 get_manager().get_virtual_root().get_registry(), node, observer_from(this));
312
313 if (!type.empty())
314 attr.object_type = type;
315
316 auto layer_id = utils::from_string<layer>(layer_name);
317 if (!layer_id.has_value()) {
318 gui::out << gui::warning << node.get_location() << ": "
319 << "Unknown layer type: \"" << layer_name << "\". Using \"ARTWORK\"."
320 << std::endl;
321 }
322
323 auto reg = create_layered_region(layer_id.value_or(layer::artwork), attr);
324 if (!reg)
325 return nullptr;
326
327 try {
328 reg->parse_layout(node);
329 reg->notify_loaded();
330 return reg;
331 } catch (...) {
332 reg->release_from_parent();
333 throw;
334 }
335 } catch (const exception& e) {
336 gui::out << gui::error << e.get_description() << std::endl;
337 }
338
339 return nullptr;
340}
341
343 if (const layout_node* layers_node = node.try_get_child("Layers")) {
344 for (const layout_node& layer_node : layers_node->get_children()) {
345 if (layer_node.get_name() != "Layer" && layer_node.get_name() != "") {
346 gui::out << gui::warning << layer_node.get_location() << ": "
347 << "unexpected node '" << layer_node.get_name() << "'; ignored."
348 << std::endl;
349 continue;
350 }
351
352 std::string level = layer_node.get_attribute_value_or<std::string>("level", "ARTWORK");
353 for (const layout_node& region_node : layer_node.get_children()) {
354 parse_region_(region_node, level, "");
355 }
356 }
357 }
358}
359
360utils::observer_ptr<frame> frame::parse_child_(const layout_node& node, const std::string& type) {
361 try {
362 auto attr = parse_core_attributes(
363 get_manager().get_root().get_registry(),
364 get_manager().get_virtual_root().get_registry(), node, observer_from(this));
365
366 if (!type.empty())
367 attr.object_type = type;
368
369 auto obj = create_child(frame_core_attributes{attr});
370 if (!obj)
371 return nullptr;
372
373 try {
374 obj->set_addon(get_addon());
375 obj->parse_layout(node);
376 obj->notify_loaded();
377 return obj;
378 } catch (...) {
379 obj->release_from_parent();
380 throw;
381 }
382 } catch (const exception& e) {
383 gui::out << gui::error << e.get_description() << std::endl;
384 }
385
386 return nullptr;
387}
388
390 if (const layout_node* frames_node = node.try_get_child("Frames")) {
391 for (const layout_node& elem_node : frames_node->get_children()) {
392 parse_child_(elem_node, "");
393 }
394 }
395}
396
398 if (const layout_node* scripts_node = node.try_get_child("Scripts")) {
399 for (const layout_node& script_node : scripts_node->get_children()) {
400 std::string name = std::string(script_node.get_name());
401
402 const layout_attribute* actual_script_node = &script_node;
403 if (const layout_attribute* run_node = script_node.try_get_attribute("run"))
404 actual_script_node = run_node;
405
406 std::string script = std::string(actual_script_node->get_value());
407 script_info info{
408 std::string(actual_script_node->get_filename()),
409 static_cast<std::size_t>(actual_script_node->get_value_line_number())};
410
411 if (script_node.get_attribute_value_or<bool>("override", false))
412 set_script(name, std::move(script), std::move(info));
413 else
414 add_script(name, std::move(script), std::move(info));
415 }
416 }
417}
418
419} // namespace lxgui::gui
Draws borders and background of a frame.
Exception to be thrown by GUI code.
virtual void parse_all_nodes_before_children_(const layout_node &node)
virtual void parse_hit_rect_insets_node_(const layout_node &node)
virtual void parse_scripts_node_(const layout_node &node)
void enable_auto_focus(bool enable)
Enables automatic focus when this frame is shown or raised.
void set_backdrop(std::unique_ptr< backdrop > bdrop)
Sets this frames' backdrop.
virtual void parse_layers_node_(const layout_node &node)
void set_resizable(bool is_resizable)
Sets if this frame can be resized by the user.
void set_clamped_to_screen(bool is_clamped_to_screen)
Sets if this frame is clamped to screen.
void set_max_width(float max_width)
Sets this frame's maximum width.
void set_level(int level_id)
Sets this frame's level.
void create_title_region()
Creates a new title region for this frame.
void set_max_height(float max_height)
Sets this frame's maximum height.
virtual void parse_title_region_node_(const layout_node &node)
void set_abs_hit_rect_insets(const bounds2f &insets)
Sets this frame's absolute hit rect insets.
void set_min_height(float min_height)
Sets this frame's minimum height.
void parse_attributes_(const layout_node &node) override
void set_top_level(bool is_top_level)
Sets if this frame is at top level.
void set_mouse_move_enabled(bool is_mouse_enabled)
Sets if this frame can receive mouse move input.
utils::observer_ptr< layered_region > parse_region_(const layout_node &node, const std::string &layer_name, const std::string &type)
utils::connection add_script(const std::string &script_name, std::string content, script_info info=script_info{})
Adds an additional handler script to this frame (executed after existing scripts).
void set_mouse_enabled(bool is_mouse_enabled)
Sets if this frame can receive mouse input (click & move).
utils::observer_ptr< layered_region > create_layered_region(layer layer_id, region_core_attributes attr)
Creates a new region as child of this frame.
void set_min_width(float min_width)
Sets this frame's minimum width.
void set_keyboard_enabled(bool is_keyboard_enabled)
Sets if this frame can receive any keyboard input.
void set_min_dimensions(const vector2f &min)
Sets this frame's minimum size.
virtual void parse_resize_bounds_node_(const layout_node &node)
utils::connection set_script(const std::string &script_name, std::string content, script_info info=script_info{})
Sets a new handler script for this frame (replacing existing scripts).
void set_update_rate(float rate)
Sets a maximum update rate (in updates per seconds).
void parse_layout(const layout_node &node) final
Parses data from a layout_node.
virtual void parse_backdrop_node_(const layout_node &node)
void set_rel_hit_rect_insets(const bounds2f &insets)
Sets this frame's relative hit rect insets.
void set_strata(std::optional< strata > strata_id)
Sets this frame's strata.
void set_mouse_click_enabled(bool is_mouse_enabled)
Sets if this frame can receive mouse click input.
utils::observer_ptr< frame > parse_child_(const layout_node &node, const std::string &type)
utils::owner_ptr< region > title_region_
void set_mouse_wheel_enabled(bool is_mouse_wheel_enabled)
Sets if this frame can receive mouse wheel input.
void set_movable(bool is_movable)
Sets if this frame can be moved by the user.
utils::observer_ptr< frame > create_child(frame_core_attributes attr)
Creates a new frame as child of this frame.
virtual void parse_frames_node_(const layout_node &node)
void set_max_dimensions(const vector2f &max)
Sets this frame's maximum size.
An attribute in a layout file.
std::size_t get_value_line_number() const noexcept
Returns the line number on which this node's value is located.
std::string_view get_value() const noexcept
Returns this node's value as string.
std::string_view get_filename() const noexcept
Returns the file in which this node is located.
std::string_view get_location() const noexcept
Returns this node's location in the file as {file}:{line}.
An node in a layout file.
const layout_node * try_get_child(std::string_view name) const noexcept
Returns the first child with a given name, or null if none.
std::optional< std::string_view > try_get_attribute_value(std::string_view name) const noexcept
Returns the value of the attribute with the provided name, nullopt if not found.
T get_attribute_value_or(std::string_view name, T fallback) const noexcept
Returns the value of the attribute with the provided name, or a default if not found or parsing faile...
const layout_attribute * try_get_attribute(std::string_view name) const noexcept
Returns the attribute with the provided name, or null if none.
void set_shown(bool is_shown)
shows/hides this region.
void set_all_anchors(const utils::observer_ptr< region > &obj)
Adjusts this regions anchors to fit the provided region.
manager & get_manager()
Returns this region's manager.
color parse_color_node_(const layout_node &node)
registry & get_registry()
Returns the UI object registry, which keeps track of all objects in the UI.
virtual void parse_size_node_(const layout_node &node)
std::pair< anchor_type, vector2< std::optional< float > > > parse_dimension_node_(const layout_node &node)
void set_alpha(float alpha)
Changes this region's alpha (opacity).
const addon * get_addon() const
Returns this frame's addon.
virtual void parse_anchor_node_(const layout_node &node)
std::string parse_file_name(const std::string &file_name) const
Convert an addon-relative file path to a application-relative path.
const std::string & get_description() const
Returns the message of the exception.
vector2< float > vector2f
Holds 2D coordinates (as floats)
bounds2< float > bounds2f
Holds 2D bounds of a region (as floats).
std::ostream out
utils::observer_ptr< ObjectType > observer_from(ObjectType *self)
Obtain an observer pointer from a raw pointer (typically 'this')
region_core_attributes parse_core_attributes(registry &reg, virtual_registry &vreg, const layout_node &node, utils::observer_ptr< frame > parent)
Parse "core" attributes from a layout node, before creating a frame.
const std::string warning
Definition gui_out.cpp:6
const std::string error
Definition gui_out.cpp:7
Struct holding all the core information about a frame necessary for its creation.
Holds file/line information for a script.
Definition gui_frame.hpp:39