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.
This commit is contained in:
Vas Crabb 2023-10-30 03:11:15 +11:00
parent 08a9011cfb
commit e4ceeee567
7 changed files with 355 additions and 94 deletions

View File

@ -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.

View File

@ -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 bitmaps 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 bitmaps 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 bitmaps 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 bitmaps 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 <luascript-ref-rendercolor>`.
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 <luascript-ref-renderlayelem>` 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 <luascript-ref-renderlayview>` 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 <luascript-ref-renderlayelem>` used to draw the item, or
``nil`` for screen items.
item.bounds_animated (read-only)
A Boolean indicating whether the items 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 MAMEs ``layout_element`` class, representing a visual element that can be
drawn in a :ref:`layout view <luascript-ref-renderlayview>`. 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 <luascript-ref-renderlayitem>`.
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 elements
components have been drawn. The function is passed two arguments: the
element state (an integer) and the 32-bit ARGB
:ref:`bitmap <luascript-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``.

View File

@ -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

View File

@ -71,6 +71,7 @@ class layout_element
{
public:
using environment = emu::render::detail::layout_environment;
using draw_delegate = delegate<void (int, bitmap_argb32 &)>;
// 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<texture> 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; }

View File

@ -182,8 +182,8 @@ private:
template <typename... T>
auto make_notifier_adder(util::notifier<T...> &notifier, const char *desc);
template <typename R, typename T, typename D>
auto make_simple_callback_setter(void (T::*setter)(delegate<R ()> &&), D &&dflt, const char *name, const char *desc);
template <typename T, typename D, typename R, typename... A>
auto make_simple_callback_setter(void (T::*setter)(delegate<R (A...)> &&), D &&dflt, const char *name, const char *desc);
running_machine &machine() const { return *m_machine; }

View File

@ -605,22 +605,22 @@ auto lua_engine::make_notifier_adder(util::notifier<T...> &notifier, const char
// setter for simple cases
//-------------------------------------------------
template <typename R, typename T, typename D>
auto lua_engine::make_simple_callback_setter(void (T::*setter)(delegate<R ()> &&), D &&dflt, const char *name, const char *desc)
template <typename T, typename D, typename R, typename... A>
auto lua_engine::make_simple_callback_setter(void (T::*setter)(delegate<R (A...)> &&), 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<R ()>());
(self.*setter)(delegate<R (A...)>());
}
else if (cb.is<sol::protected_function>())
{
(self.*setter)(delegate<R ()>(
[dflt, desc, cbfunc = sol::protected_function(m_lua_state, cb)] () -> R
(self.*setter)(delegate<R (A...)>(
[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<A>(args)...));
if (status.valid())
{
if constexpr (std::is_same_v<R, void>)
@ -629,7 +629,7 @@ auto lua_engine::make_simple_callback_setter(void (T::*setter)(delegate<R ()> &&
}
else
{
auto result(status.get<std::optional<R> >());
auto result(status.template get<std::optional<R> >());
if (result)
{
return *result;
@ -643,7 +643,7 @@ auto lua_engine::make_simple_callback_setter(void (T::*setter)(delegate<R ()> &&
}
else
{
auto err(status.get<sol::error>());
auto err(status.template get<sol::error>());
osd_printf_error("[LUA ERROR] error in %s callback: %s\n", desc, err.what());
if constexpr (!std::is_same_v<R, void>)
return dflt();

View File

@ -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<layout_file_elements> : std::true_type { };
template <> struct is_container<layout_file_views> : std::true_type { };
template <> struct is_container<layout_view_items> : std::true_type { };
template <> struct is_container<render_target_view_names> : std::true_type { };
template <>
struct usertype_container<layout_file_elements> : lua_engine::immutable_collection_helper<layout_file_elements, layout_file::element_map>
{
private:
template <bool Indexed>
static int next_pairs(lua_State *L)
{
usertype_container::indexed_iterator &i(stack::unqualified_get<user<usertype_container::indexed_iterator> >(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 <bool Indexed>
static int start_pairs(lua_State *L)
{
layout_file_elements &self(usertype_container::get_self(L));
stack::push(L, next_pairs<Indexed>);
stack::push<user<usertype_container::indexed_iterator> >(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<std::ptrdiff_t>(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<char const *>(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<layout_element>(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<false>); }
static int pairs(lua_State *L) { return start_pairs<false>(L); }
static int ipairs(lua_State *L) { return start_pairs<true>(L); }
};
template <>
struct usertype_container<layout_file_views> : lua_engine::immutable_sequence_helper<layout_file_views, layout_file::view_list>
{
@ -477,7 +579,7 @@ public:
luaL_error(s, "Bounds exceed source clipping rectangle");
return std::make_shared<bitmap_helper>(source, subrect);
}),
sol::base_classes, sol::bases<B, bitmap_t>());
sol::base_classes, sol::bases<T, B, bitmap_t>());
add_bitmap_members(result);
return result;
}
@ -738,8 +840,21 @@ void lua_engine::initialize_render(sol::table &emu)
bitmap_helper<bitmap_rgb32>::make_type<bitmap32_t>(emu, "bitmap_rgb32");
// ARGB32 bitmaps get extra functionality
auto bitmap_argb32_type = bitmap_helper<bitmap_argb32>::make_type<bitmap32_t>(emu, "bitmap_argb32");
bitmap_argb32_type.set_function("load",
auto bitmap_argb32_type = emu.new_usertype<bitmap_argb32>(
"bitmap_argb32_t",
sol::no_constructor,
sol::base_classes, sol::bases<bitmap32_t, bitmap_t>());
bitmap_argb32_type.set_function(
"resample",
[] (bitmap_argb32 &bitmap, bitmap_argb32 &dest, std::optional<render_color> 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<bitmap_argb32>::make_type<bitmap32_t>(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>("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<int>
{
if (0 <= e.default_state())
return e.default_state();
else
return std::nullopt;
});
auto layout_view_type = sol().registry().new_usertype<layout_view>("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<void>(
&layout_view::set_prepare_items_callback,
nullptr,
"set_prepare_items_callback",
"prepare items");
layout_view_type["set_preload_callback"] =
make_simple_callback_setter<void>(
&layout_view::set_preload_callback,
nullptr,
"set_preload_callback",
"preload");
layout_view_type["set_recomputed_callback"] =
make_simple_callback_setter<void>(
&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_view_item>("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<int>(
&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<int>(
&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<render_bounds>(
&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<render_color>(
&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<float>(
&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<float>(
&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<float>(
&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<float>(
&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>("layout_file", sol::no_constructor);
layout_file_type["set_resolve_tags_callback"] =
make_simple_callback_setter<void>(
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); });