lxgui
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 
12 namespace lxgui::gui {
13 
15  parse_attributes_(node);
16 
17  parse_size_node_(node);
19  parse_anchor_node_(node);
23 
24  parse_layers_node_(node);
25 }
26 
27 void frame::parse_layout(const layout_node& node) {
29  parse_frames_node_(node);
30  parse_scripts_node_(node);
31 }
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 
306 utils::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 
360 utils::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.
Definition: gui_frame.cpp:1310
void set_backdrop(std::unique_ptr< backdrop > bdrop)
Sets this frames' backdrop.
Definition: gui_frame.cpp:1188
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.
Definition: gui_frame.cpp:1272
void set_clamped_to_screen(bool is_clamped_to_screen)
Sets if this frame is clamped to screen.
Definition: gui_frame.cpp:1174
void set_max_width(float max_width)
Sets this frame's maximum width.
Definition: gui_frame.cpp:1233
void set_level(int level_id)
Sets this frame's level.
Definition: gui_frame.cpp:1201
void create_title_region()
Creates a new title region for this frame.
Definition: gui_frame.cpp:275
void set_max_height(float max_height)
Sets this frame's maximum height.
Definition: gui_frame.cpp:1222
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.
Definition: gui_frame.cpp:1193
void set_min_height(float min_height)
Sets this frame's minimum height.
Definition: gui_frame.cpp:1244
void parse_attributes_(const layout_node &node) override
void set_top_level(bool is_top_level)
Sets if this frame is at top level.
Definition: gui_frame.cpp:1282
void set_mouse_move_enabled(bool is_mouse_enabled)
Sets if this frame can receive mouse move input.
Definition: gui_frame.cpp:446
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).
Definition: gui_frame.hpp:1125
void set_mouse_enabled(bool is_mouse_enabled)
Sets if this frame can receive mouse input (click & move).
Definition: gui_frame.cpp:437
utils::observer_ptr< layered_region > create_layered_region(layer layer_id, region_core_attributes attr)
Creates a new region as child of this frame.
Definition: gui_frame.cpp:652
void set_min_width(float min_width)
Sets this frame's minimum width.
Definition: gui_frame.cpp:1252
void set_keyboard_enabled(bool is_keyboard_enabled)
Sets if this frame can receive any keyboard input.
Definition: gui_frame.cpp:454
void set_min_dimensions(const vector2f &min)
Sets this frame's minimum size.
Definition: gui_frame.cpp:1217
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).
Definition: gui_frame.hpp:1233
void set_update_rate(float rate)
Sets a maximum update rate (in updates per seconds).
Definition: gui_frame.cpp:1146
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.
Definition: gui_frame.cpp:1197
void set_strata(std::optional< strata > strata_id)
Sets this frame's strata.
Definition: gui_frame.cpp:1178
void set_mouse_click_enabled(bool is_mouse_enabled)
Sets if this frame can receive mouse click input.
Definition: gui_frame.cpp:442
utils::observer_ptr< frame > parse_child_(const layout_node &node, const std::string &type)
utils::owner_ptr< region > title_region_
Definition: gui_frame.hpp:1848
void set_mouse_wheel_enabled(bool is_mouse_wheel_enabled)
Sets if this frame can receive mouse wheel input.
Definition: gui_frame.cpp:450
void set_movable(bool is_movable)
Sets if this frame can be moved by the user.
Definition: gui_frame.cpp:1260
utils::observer_ptr< frame > create_child(frame_core_attributes attr)
Creates a new frame as child of this frame.
Definition: gui_frame.cpp:666
virtual void parse_frames_node_(const layout_node &node)
void set_max_dimensions(const vector2f &max)
Sets this frame's maximum size.
Definition: gui_frame.cpp:1212
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.
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.
children_view get_children() const noexcept
Returns a view to the list of children.
const layout_attribute * try_get_attribute(std::string_view name) const noexcept
Returns the attribute with the provided name, or null if none.
const layout_node * try_get_child(std::string_view name) const noexcept
Returns the first child with a given name, or null if none.
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...
void set_shown(bool is_shown)
shows/hides this region.
Definition: gui_region.cpp:196
void set_all_anchors(const utils::observer_ptr< region > &obj)
Adjusts this regions anchors to fit the provided region.
Definition: gui_region.cpp:437
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.
Definition: gui_region.cpp:850
virtual void parse_size_node_(const layout_node &node)
std::pair< anchor_type, vector2< std::optional< float > > > parse_dimension_node_(const layout_node &node)
manager & get_manager()
Returns this region's manager.
Definition: gui_region.hpp:693
void set_alpha(float alpha)
Changes this region's alpha (opacity).
Definition: gui_region.cpp:169
std::string name_
Definition: gui_region.hpp:804
const addon * get_addon() const
Returns this frame's addon.
Definition: gui_region.cpp:843
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.
Definition: gui_region.cpp:818
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).
utils::observer_ptr< ObjectType > observer_from(ObjectType *self)
Obtain an observer pointer from a raw pointer (typically 'this')
Definition: gui_region.hpp:946
std::ostream out
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