From e4ceeee56741ebf1e72c2d2a33f94dfa1d251c11 Mon Sep 17 00:00:00 2001 From: Vas Crabb Date: Mon, 30 Oct 2023 03:11:15 +1100 Subject: Added some additional layout functionality and script bindings. Also corrected some copy/paste errors in documentation, and bumped documentation version as it now describes features that will appear in an upcoming release. --- docs/source/conf.py | 4 +- docs/source/luascript/ref-render.rst | 69 +++++++- src/emu/rendlay.cpp | 53 +++++- src/emu/rendlay.h | 12 +- src/frontend/mame/luaengine.h | 4 +- src/frontend/mame/luaengine.ipp | 16 +- src/frontend/mame/luaengine_render.cpp | 291 +++++++++++++++++++++++++-------- 7 files changed, 355 insertions(+), 94 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 5a8c585f694..d0227f52c06 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -63,9 +63,9 @@ copyright = u'1997-2023, MAMEdev and contributors' # built documents. # # The short X.Y version. -version = '0.260' +version = '0.261' # The full version, including alpha/beta/rc tags. -release = '0.260' +release = '0.261' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/docs/source/luascript/ref-render.rst b/docs/source/luascript/ref-render.rst index f10f8710e26..265e52b5016 100644 --- a/docs/source/luascript/ref-render.rst +++ b/docs/source/luascript/ref-render.rst @@ -480,10 +480,9 @@ emu.bitmap_yuy16(source, [x0, y0, x1, y1]) format. Raises an error if coordinates are specified representing a rectangle not fully contained within the source bitmap’s clipping rectangle. emu.bitmap_rgb32(source, [x0, y0, x1, y1]) - Creates an RGB format bitmap with 4:2:2 chroma subsampling representing a - view of a portion of an existing bitmap. The initial clipping rectangle is - set to the bounds of the view. The source bitmap will be locked, preventing - resizing and reallocation. + Creates an RGB format bitmap representing a view of a portion of an existing + bitmap. The initial clipping rectangle is set to the bounds of the view. + The source bitmap will be locked, preventing resizing and reallocation. If no coordinates are specified, the new bitmap will represent a view of the source bitmap’s current clipping rectangle. If coordinates are specified, @@ -496,10 +495,10 @@ emu.bitmap_rgb32(source, [x0, y0, x1, y1]) format. Raises an error if coordinates are specified representing a rectangle not fully contained within the source bitmap’s clipping rectangle. emu.bitmap_argb32(source, [x0, y0, x1, y1]) - Creates an ARGB format bitmap with 4:2:2 chroma subsampling representing a - view of a portion of an existing bitmap. The initial clipping rectangle is - set to the bounds of the view. The source bitmap will be locked, preventing - resizing and reallocation. + Creates an ARGB format bitmap representing a view of a portion of an + existing bitmap. The initial clipping rectangle is set to the bounds of the + view. The source bitmap will be locked, preventing resizing and + reallocation. If no coordinates are specified, the new bitmap will represent a view of the source bitmap’s current clipping rectangle. If coordinates are specified, @@ -619,6 +618,12 @@ bitmap:plot_box(x, y, width, height, color) Fills the intersection of the clipping rectangle and the rectangle with top left (x, y) and the specified height and width with the specified colour value. Coordinates and dimensions are in units of pixels. +bitmap:resample(dest, [color]) + Copies the bitmap into the destination bitmap, scaling to fill the + destination bitmap and using a re-sampling filter. Only ARGB format source + and destination bitmaps are supported. The source pixel values will be + multiplied by the colour if it is supplied. It must be a + :ref:`render colour `. Properties ~~~~~~~~~~ @@ -987,6 +992,11 @@ Properties layout.device (read-only) The device that caused the layout file to be loaded. Usually the root machine device for external layouts. +layout.elements[] (read-only) + The :ref:`elements ` created from the layout + file. Elements are indexed by name (i.e. the value of the ``name`` + attribute). The index get method has O(1) complexity, and the ``at`` and + ``index_of`` methods have O(n) complexity. layout.views[] (read-only) The :ref:`views ` created from the layout file. Views are indexed by unqualified name (i.e. the value of the ``name`` @@ -1194,6 +1204,9 @@ Properties item.id (read-only) Get the optional item identifier. This is the value of the ``id`` attribute in the XML layout file if present, or ``nil``. +item.element (read-only) + The :ref:`element ` used to draw the item, or + ``nil`` for screen items. item.bounds_animated (read-only) A Boolean indicating whether the item’s bounds depend on its animation state. @@ -1246,3 +1259,43 @@ item.element_state (read-only) item.animation_state (read-only) Get the current animation state. This will call the animation state callback function to handle bindings. + + +.. _luascript-ref-renderlayelem: + +Layout element +-------------- + +Wraps MAME’s ``layout_element`` class, representing a visual element that can be +drawn in a :ref:`layout view `. Elements are +created from XML layout files, which may be loaded from external artwork or +internal to MAME. Note that layout element callbacks are not run as coroutines. + +Instantiation +~~~~~~~~~~~~~ + +layout.elements[name] + Gets a layout element by name. +layout.views[name].items[id].element + Gets the layout element used to draw a + :ref:`view item `. + +Methods +~~~~~~~ + +element:invalidate() + Invalidate all cached textures for the element, ensuring it will be redrawn + when the next video frame is drawn. +element.set_draw_callback(cb) + Set a function to call the perform additional drawing after the element’s + components have been drawn. The function is passed two arguments: the + element state (an integer) and the 32-bit ARGB + :ref:`bitmap ` at the required size. The function + must not attempt to resize the bitmap. Call with ``nil`` to remove the + callback. + +Properties +~~~~~~~~~~ + +element.default_state (read-only) + The integer default state for the element if set or ``nil``. diff --git a/src/emu/rendlay.cpp b/src/emu/rendlay.cpp index 9c720f2ae8e..d320999bd96 100644 --- a/src/emu/rendlay.cpp +++ b/src/emu/rendlay.cpp @@ -1277,6 +1277,7 @@ layout_element::layout_element(environment &env, util::xml::data_node const &ele , m_defstate(env.get_attribute_int(elemnode, "defstate", -1)) , m_statemask(0) , m_foldhigh(false) + , m_invalidated(false) { // parse components in order bool first = true; @@ -1646,6 +1647,17 @@ render_texture *layout_element::state_texture(int state) } +//------------------------------------------------- +// set_draw_callback - set handler called after +// drawing components +//------------------------------------------------- + +void layout_element::set_draw_callback(draw_delegate &&handler) +{ + m_draw = std::move(handler); +} + + //------------------------------------------------- // preload - perform expensive loading upfront // for all components @@ -1658,6 +1670,25 @@ void layout_element::preload() } +//------------------------------------------------- +// prepare - perform additional tasks before +// drawing a frame +//------------------------------------------------- + +void layout_element::prepare() +{ + if (m_invalidated) + { + m_invalidated = false; + for (texture &tex : m_elemtex) + { + machine().render().texture_free(tex.m_texture); + tex.m_texture = nullptr; + } + } +} + + //------------------------------------------------- // element_scale - scale an element by rendering // all the components at the appropriate @@ -1674,6 +1705,10 @@ void layout_element::element_scale(bitmap_argb32 &dest, bitmap_argb32 &source, c if ((elemtex.m_state & curcomp->statemask()) == curcomp->stateval()) curcomp->draw(elemtex.m_element->machine(), dest, elemtex.m_state); } + + // if there's a callback for additional drawing, invoke it + if (!elemtex.m_element->m_draw.isnull()) + elemtex.m_element->m_draw(elemtex.m_state, dest); } @@ -3490,7 +3525,7 @@ layout_element::texture::texture(texture &&that) : texture() layout_element::texture::~texture() { - if (m_element != nullptr) + if (m_element) m_element->machine().render().texture_free(m_texture); } @@ -3986,6 +4021,7 @@ layout_view::layout_view( : m_effaspect(1.0f) , m_name(make_name(env, viewnode)) , m_unqualified_name(env.get_attribute_string(viewnode, "name")) + , m_elemmap(elemmap) , m_defvismask(0U) , m_has_art(false) { @@ -4131,6 +4167,21 @@ bool layout_view::has_visible_screen(screen_device const &screen) const } +//------------------------------------------------- +// prepare_items - perform additional tasks +// before rendering a frame +//------------------------------------------------- + +void layout_view::prepare_items() +{ + if (!m_prepare_items.isnull()) + m_prepare_items(); + + for (auto &[name, element] : m_elemmap) + element.prepare(); +} + + //------------------------------------------------- // recompute - recompute the bounds and aspect // ratio of a view and all of its contained items diff --git a/src/emu/rendlay.h b/src/emu/rendlay.h index 29b38fdd5d0..0391bc6e961 100644 --- a/src/emu/rendlay.h +++ b/src/emu/rendlay.h @@ -71,6 +71,7 @@ class layout_element { public: using environment = emu::render::detail::layout_environment; + using draw_delegate = delegate; // construction/destruction layout_element(environment &env, util::xml::data_node const &elemnode); @@ -81,8 +82,13 @@ public: int default_state() const { return m_defstate; } render_texture *state_texture(int state); + // set handlers + void set_draw_callback(draw_delegate &&handler); + // operations void preload(); + void invalidate() { m_invalidated = true; } + void prepare(); private: /// \brief A drawing component within a layout element @@ -192,6 +198,8 @@ private: int m_statemask; // mask to apply to state values bool m_foldhigh; // whether we need to fold state values above the mask range std::vector m_elemtex; // array of element textures used for managing the scaled bitmaps + draw_delegate m_draw; // draw delegate (called after components are drawn) + bool m_invalidated; // force redrawing on next frame if set }; @@ -521,7 +529,7 @@ public: void set_recomputed_callback(recomputed_delegate &&handler); // operations - void prepare_items() { if (!m_prepare_items.isnull()) m_prepare_items(); } + void prepare_items(); void recompute(u32 visibility_mask, bool zoom_to_screens); void preload(); @@ -572,6 +580,7 @@ private: // cold items std::string m_name; // display name for the view std::string m_unqualified_name; // the name exactly as specified in the layout file + element_map & m_elemmap; // reference to shared elements item_id_map m_items_by_id; // items with non-empty ID indexed by ID visibility_toggle_vector m_vistoggles; // collections of items that can be shown/hidden render_bounds m_expbounds; // explicit bounds of the view @@ -598,6 +607,7 @@ public: // getters device_t &device() const { return m_device; } + element_map &elements() { return m_elemmap; } element_map const &elements() const { return m_elemmap; } view_list &views() { return m_viewlist; } view_list const &views() const { return m_viewlist; } diff --git a/src/frontend/mame/luaengine.h b/src/frontend/mame/luaengine.h index 37e02e5babe..de94e7ba729 100644 --- a/src/frontend/mame/luaengine.h +++ b/src/frontend/mame/luaengine.h @@ -182,8 +182,8 @@ private: template auto make_notifier_adder(util::notifier ¬ifier, const char *desc); - template - auto make_simple_callback_setter(void (T::*setter)(delegate &&), D &&dflt, const char *name, const char *desc); + template + auto make_simple_callback_setter(void (T::*setter)(delegate &&), D &&dflt, const char *name, const char *desc); running_machine &machine() const { return *m_machine; } diff --git a/src/frontend/mame/luaengine.ipp b/src/frontend/mame/luaengine.ipp index ae3ae6f3a05..27a68c6dd21 100644 --- a/src/frontend/mame/luaengine.ipp +++ b/src/frontend/mame/luaengine.ipp @@ -605,22 +605,22 @@ auto lua_engine::make_notifier_adder(util::notifier ¬ifier, const char // setter for simple cases //------------------------------------------------- -template -auto lua_engine::make_simple_callback_setter(void (T::*setter)(delegate &&), D &&dflt, const char *name, const char *desc) +template +auto lua_engine::make_simple_callback_setter(void (T::*setter)(delegate &&), D &&dflt, const char *name, const char *desc) { return [this, setter, dflt, name, desc] (T &self, sol::object cb) { if (cb == sol::lua_nil) { - (self.*setter)(delegate()); + (self.*setter)(delegate()); } else if (cb.is()) { - (self.*setter)(delegate( - [dflt, desc, cbfunc = sol::protected_function(m_lua_state, cb)] () -> R + (self.*setter)(delegate( + [dflt, desc, cbfunc = sol::protected_function(m_lua_state, cb)] (A... args) -> R { - auto status(invoke_direct(cbfunc)); + auto status(invoke_direct(cbfunc, std::forward(args)...)); if (status.valid()) { if constexpr (std::is_same_v) @@ -629,7 +629,7 @@ auto lua_engine::make_simple_callback_setter(void (T::*setter)(delegate && } else { - auto result(status.get >()); + auto result(status.template get >()); if (result) { return *result; @@ -643,7 +643,7 @@ auto lua_engine::make_simple_callback_setter(void (T::*setter)(delegate && } else { - auto err(status.get()); + auto err(status.template get()); osd_printf_error("[LUA ERROR] error in %s callback: %s\n", desc, err.what()); if constexpr (!std::is_same_v) return dflt(); diff --git a/src/frontend/mame/luaengine_render.cpp b/src/frontend/mame/luaengine_render.cpp index e4286a2493f..2568f72f95d 100644 --- a/src/frontend/mame/luaengine_render.cpp +++ b/src/frontend/mame/luaengine_render.cpp @@ -104,6 +104,15 @@ private: }; +struct layout_file_elements +{ + layout_file_elements(layout_file &f) : file(f) { } + layout_file::element_map &items() { return file.elements(); } + + layout_file &file; +}; + + struct layout_file_views { layout_file_views(layout_file &f) : file(f) { } @@ -192,11 +201,104 @@ auto make_bitmap_specific_type(sol::table registry, char const *name) namespace sol { +template <> struct is_container : std::true_type { }; template <> struct is_container : std::true_type { }; template <> struct is_container : std::true_type { }; template <> struct is_container : std::true_type { }; +template <> +struct usertype_container : lua_engine::immutable_collection_helper +{ +private: + template + static int next_pairs(lua_State *L) + { + usertype_container::indexed_iterator &i(stack::unqualified_get >(L, 1)); + if (i.src.end() == i.it) + return stack::push(L, lua_nil); + int result; + if constexpr (Indexed) + result = stack::push(L, i.ix + 1); + else + result = stack::push(L, i.it->first); + result += stack::push_reference(L, i.it->second); + ++i; + return result; + } + + template + static int start_pairs(lua_State *L) + { + layout_file_elements &self(usertype_container::get_self(L)); + stack::push(L, next_pairs); + stack::push >(L, self.items(), self.items().begin()); + stack::push(L, lua_nil); + return 3; + } + +public: + static int at(lua_State *L) + { + layout_file_elements &self(usertype_container::get_self(L)); + std::ptrdiff_t const index(stack::unqualified_get(L, 2)); + if ((0 >= index) || (self.items().size() < index)) + return stack::push(L, lua_nil); + auto const found(std::next(self.items().begin(), index - 1)); + return stack::push_reference(L, found->second); + } + + static int get(lua_State *L) + { + layout_file_elements &self(usertype_container::get_self(L)); + char const *const tag(stack::unqualified_get(L)); + auto const found(self.items().find(tag)); + if (self.items().end() == found) + return stack::push(L, lua_nil); + else + return stack::push_reference(L, found->second); + } + + static int index_get(lua_State *L) + { + return get(L); + } + + static int index_of(lua_State *L) + { + layout_file_elements &self(usertype_container::get_self(L)); + auto &obj(stack::unqualified_get(L, 2)); + auto it(self.items().begin()); + std::ptrdiff_t ix(0); + while ((self.items().end() != it) && (&it->second != &obj)) + { + ++it; + ++ix; + } + if (self.items().end() == it) + return stack::push(L, lua_nil); + else + return stack::push(L, ix + 1); + } + + static int size(lua_State *L) + { + layout_file_elements &self(usertype_container::get_self(L)); + return stack::push(L, self.items().size()); + } + + static int empty(lua_State *L) + { + layout_file_elements &self(usertype_container::get_self(L)); + return stack::push(L, self.items().empty()); + } + + static int next(lua_State *L) { return stack::push(L, next_pairs); } + static int pairs(lua_State *L) { return start_pairs(L); } + static int ipairs(lua_State *L) { return start_pairs(L); } +}; + + template <> struct usertype_container : lua_engine::immutable_sequence_helper { @@ -477,7 +579,7 @@ public: luaL_error(s, "Bounds exceed source clipping rectangle"); return std::make_shared(source, subrect); }), - sol::base_classes, sol::bases()); + sol::base_classes, sol::bases()); add_bitmap_members(result); return result; } @@ -738,8 +840,21 @@ void lua_engine::initialize_render(sol::table &emu) bitmap_helper::make_type(emu, "bitmap_rgb32"); // ARGB32 bitmaps get extra functionality - auto bitmap_argb32_type = bitmap_helper::make_type(emu, "bitmap_argb32"); - bitmap_argb32_type.set_function("load", + auto bitmap_argb32_type = emu.new_usertype( + "bitmap_argb32_t", + sol::no_constructor, + sol::base_classes, sol::bases()); + bitmap_argb32_type.set_function( + "resample", + [] (bitmap_argb32 &bitmap, bitmap_argb32 &dest, std::optional color) + { + render_resample_argb_bitmap_hq(dest, bitmap, color ? *color : render_color{ 1.0F, 1.0F, 1.0F, 1.0F }); + }); + + + auto bitmap_argb32_helper_type = bitmap_helper::make_type(emu, "bitmap_argb32"); + bitmap_argb32_helper_type.set_function( + "load", [] (sol::this_state s, std::string_view data) { auto stream(util::ram_read(data.data(), data.size())); @@ -774,26 +889,48 @@ void lua_engine::initialize_render(sol::table &emu) render_texture_type["valid"] = sol::property(&render_texture_helper::valid); + auto layout_element_type = sol().registry().new_usertype("layout_element", sol::no_constructor); + layout_element_type.set_function("invalidate", &layout_element::invalidate); + layout_element_type.set_function( + "set_draw_callback", + make_simple_callback_setter( + &layout_element::set_draw_callback, + nullptr, + "set_draw_callback", + "draw")); + layout_element_type["default_state"] = sol::property( + [] (layout_element const &e) -> std::optional + { + if (0 <= e.default_state()) + return e.default_state(); + else + return std::nullopt; + }); + + auto layout_view_type = sol().registry().new_usertype("layout_view", sol::no_constructor); - layout_view_type["has_screen"] = &layout_view::has_screen; - layout_view_type["set_prepare_items_callback"] = - make_simple_callback_setter( - &layout_view::set_prepare_items_callback, - nullptr, - "set_prepare_items_callback", - "prepare items"); - layout_view_type["set_preload_callback"] = - make_simple_callback_setter( - &layout_view::set_preload_callback, - nullptr, - "set_preload_callback", - "preload"); - layout_view_type["set_recomputed_callback"] = - make_simple_callback_setter( - &layout_view::set_recomputed_callback, - nullptr, - "set_recomputed_callback", - "recomputed"); + layout_view_type.set_function("has_screen", &layout_view::has_screen); + layout_view_type.set_function( + "set_prepare_items_callback", + make_simple_callback_setter( + &layout_view::set_prepare_items_callback, + nullptr, + "set_prepare_items_callback", + "prepare items")); + layout_view_type.set_function( + "set_preload_callback", + make_simple_callback_setter( + &layout_view::set_preload_callback, + nullptr, + "set_preload_callback", + "preload")); + layout_view_type.set_function( + "set_recomputed_callback", + make_simple_callback_setter( + &layout_view::set_recomputed_callback, + nullptr, + "set_recomputed_callback", + "recomputed")); layout_view_type["items"] = sol::property([] (layout_view &v) { return layout_view_items(v); }); layout_view_type["name"] = sol::property(&layout_view::name); layout_view_type["unqualified_name"] = sol::property(&layout_view::unqualified_name); @@ -804,55 +941,63 @@ void lua_engine::initialize_render(sol::table &emu) auto layout_view_item_type = sol().registry().new_usertype("layout_item", sol::no_constructor); - layout_view_item_type["set_state"] = &layout_view_item::set_state; - layout_view_item_type["set_element_state_callback"] = - make_simple_callback_setter( - &layout_view_item::set_element_state_callback, - [] () { return 0; }, - "set_element_state_callback", - "element state"); - layout_view_item_type["set_animation_state_callback"] = - make_simple_callback_setter( - &layout_view_item::set_animation_state_callback, - [] () { return 0; }, - "set_animation_state_callback", - "animation state"); - layout_view_item_type["set_bounds_callback"] = - make_simple_callback_setter( - &layout_view_item::set_bounds_callback, - [] () { return render_bounds{ 0.0f, 0.0f, 1.0f, 1.0f }; }, - "set_bounds_callback", - "bounds"); - layout_view_item_type["set_color_callback"] = - make_simple_callback_setter( - &layout_view_item::set_color_callback, - [] () { return render_color{ 1.0f, 1.0f, 1.0f, 1.0f }; }, - "set_color_callback", - "color"); - layout_view_item_type["set_scroll_size_x_callback"] = - make_simple_callback_setter( - &layout_view_item::set_scroll_size_x_callback, - [] () { return 1.0f; }, - "set_scroll_size_x_callback", - "horizontal scroll window size"); - layout_view_item_type["set_scroll_size_y_callback"] = - make_simple_callback_setter( - &layout_view_item::set_scroll_size_y_callback, - [] () { return 1.0f; }, - "set_scroll_size_y_callback", - "vertical scroll window size"); - layout_view_item_type["set_scroll_pos_x_callback"] = - make_simple_callback_setter( - &layout_view_item::set_scroll_pos_x_callback, - [] () { return 1.0f; }, - "set_scroll_pos_x_callback", - "horizontal scroll position"); - layout_view_item_type["set_scroll_pos_y_callback"] = - make_simple_callback_setter( - &layout_view_item::set_scroll_pos_y_callback, - [] () { return 1.0f; }, - "set_scroll_pos_y_callback", - "vertical scroll position"); + layout_view_item_type.set_function("set_state", &layout_view_item::set_state); + layout_view_item_type.set_function( + "set_element_state_callback", + make_simple_callback_setter( + &layout_view_item::set_element_state_callback, + [] () { return 0; }, + "set_element_state_callback", + "element state")); + layout_view_item_type.set_function( + "set_animation_state_callback", + make_simple_callback_setter( + &layout_view_item::set_animation_state_callback, + [] () { return 0; }, + "set_animation_state_callback", + "animation state")); + layout_view_item_type.set_function( + "set_bounds_callback", + make_simple_callback_setter( + &layout_view_item::set_bounds_callback, + [] () { return render_bounds{ 0.0f, 0.0f, 1.0f, 1.0f }; }, + "set_bounds_callback", + "bounds")); + layout_view_item_type.set_function( + "set_color_callback", + make_simple_callback_setter( + &layout_view_item::set_color_callback, + [] () { return render_color{ 1.0f, 1.0f, 1.0f, 1.0f }; }, + "set_color_callback", + "color")); + layout_view_item_type.set_function( + "set_scroll_size_x_callback", + make_simple_callback_setter( + &layout_view_item::set_scroll_size_x_callback, + [] () { return 1.0f; }, + "set_scroll_size_x_callback", + "horizontal scroll window size")); + layout_view_item_type.set_function( + "set_scroll_size_y_callback", + make_simple_callback_setter( + &layout_view_item::set_scroll_size_y_callback, + [] () { return 1.0f; }, + "set_scroll_size_y_callback", + "vertical scroll window size")); + layout_view_item_type.set_function( + "set_scroll_pos_x_callback", + make_simple_callback_setter( + &layout_view_item::set_scroll_pos_x_callback, + [] () { return 1.0f; }, + "set_scroll_pos_x_callback", + "horizontal scroll position")); + layout_view_item_type.set_function( + "set_scroll_pos_y_callback", + make_simple_callback_setter( + &layout_view_item::set_scroll_pos_y_callback, + [] () { return 1.0f; }, + "set_scroll_pos_y_callback", + "vertical scroll position")); layout_view_item_type["id"] = sol::property( [] (layout_view_item &i, sol::this_state s) -> sol::object { @@ -861,6 +1006,7 @@ void lua_engine::initialize_render(sol::table &emu) else return sol::make_object(s, i.id()); }); + layout_view_item_type["element"] = sol::property(&layout_view_item::element); layout_view_item_type["bounds_animated"] = sol::property(&layout_view_item::bounds_animated); layout_view_item_type["color_animated"] = sol::property(&layout_view_item::color_animated); layout_view_item_type["bounds"] = sol::property(&layout_view_item::bounds); @@ -887,12 +1033,13 @@ void lua_engine::initialize_render(sol::table &emu) auto layout_file_type = sol().registry().new_usertype("layout_file", sol::no_constructor); layout_file_type["set_resolve_tags_callback"] = - make_simple_callback_setter( + make_simple_callback_setter( &layout_file::set_resolve_tags_callback, nullptr, "set_resolve_tags_callback", "resolve tags"); layout_file_type["device"] = sol::property(&layout_file::device); + layout_file_type["elements"] = sol::property([] (layout_file &f) { return layout_file_elements(f); }); layout_file_type["views"] = sol::property([] (layout_file &f) { return layout_file_views(f); }); -- cgit v1.2.3