emu/render.cpp, emu/rendlay.cpp: Added scroll support for all elements.

This commit is contained in:
Vas Crabb 2021-11-18 10:34:38 +11:00
parent ded9ecfc1c
commit 5afce6cfab
10 changed files with 916 additions and 319 deletions

View File

@ -406,8 +406,8 @@ usually comes from an I/O port field or an emulated output (see
:ref:`layfile-interact-elemstate` for information on connecting an element to an
emulated I/O port or output). Any component of an element may be restricted to
only drawing when the elements state is a particular value. Some components
(e.g. multi-segment displays and reels) use the state directly to determine
their appearance.
(e.g. multi-segment displays) use the state directly to determine their
appearance.
Each element has its own internal coordinate system. The bounds of the
elements coordinate system are computed as the union of the bounds of the
@ -591,10 +591,6 @@ simplecounter
to set text alignment. If present, the ``align`` attribute must be an
integer, where 0 (zero) means centred, 1 (one) means left-aligned, and 2
(two) means right-aligned; if absent, the text will be centred.
reel
Used for drawing slot machine reels. Supported attributes include
``symbollist``, ``stateoffset``, ``numsymbolsvisible``, ``reelreversed``,
and ``beltreel``.
An example element that draws a static left-aligned text string:
@ -839,6 +835,48 @@ have their colour and position/size animated by supplying multiple ``color``
and/or ``bounds`` child elements with ``state`` attributes. See
:ref:`layfile-interact-itemanim` for details.
Layout elements (``element`` elements) may be configured to show only part of
the elements width or height using ``xscroll`` and/or ``yscroll`` child
elements. This can be used for devices like slot machine reels. The
``xscroll`` and ``yscroll`` elements support the same attributes:
size
The size of the horizontal or vertical scroll window, as a proportion of the
elements width or height, respectively. Must be in the range 0.01 to 1.0,
inclusive, if present (1% of the width/height to the full width/height). By
default, the entire width and height of the element is shown.
wrap
Whether the element should wrap horizontally or vertically. Must be either
``yes`` or ``no`` if present. By default, items do not wrap horizontally or
vertically.
inputtag
If present, the horizontal or vertical scroll position will be taken from
the value of the corresponding I/O port. Specifies the tag path of an I/O
port relative to the device that caused the layout file to be loaded. The
raw value from the input port is used, active-low switch values are not
normalised.
name
If present, the horizontal or vertical scroll position will be taken from
the correspondingly named output.
mask
If present, the horizontal or vertical scroll position will be masked with
the value and shifted to the right to remove trailing zeroes (for example a
mask of 0x05 will result in no shift, while a mask of 0x68 will result in
the value being shifted three bits to the right). Note that this applies to
output values (specified with the ``name`` attribute) as well as input port
values (specified with the ``inputtag`` attribute). Must be an integer
value if present. If not present, it is equivalent to all 32 bits being
set.
min
Minimum horizontal or vertical scroll position value. When the horizontal
or vertical scroll position has this value, the left or top edge or the
scroll window will be aligned with the left or top edge of the element.
Must be an integer value if present. Defaults to zero.
max
Maximum horizontal or vertical scroll position value. Must be an integer
value if present. Defaults to the ``mask`` value shifted to the right to
remove trailing zeroes.
.. _layfile-parts-collections:
@ -1149,9 +1187,8 @@ Clickable items
item will activate the emulated switch.
State-dependent components
Some components will be drawn differently depending on the containing
elements state. These include the dot matrix, multi-segment LED display,
simple counter and reel elements. See :ref:`layfile-parts-elements` for
details.
elements state. These include the dot matrix, multi-segment LED display
and simple counter elements. See :ref:`layfile-parts-elements` for details.
Conditionally-drawn components
Components may be conditionally drawn or hidden depending on the containing
elements state by supplying ``state`` and/or ``statemask`` attributes. See
@ -1298,7 +1335,7 @@ If the ``animate`` child element has a ``mask`` attribute, the items animatio
state will be masked with the ``mask`` value and shifted to the right to remove
trailing zeroes (for example a mask of 0x05 will result in no shift, while a
mask of 0xb0 will result in the value being shifted four bits to the right).
Note that the ``mask`` attribute applies to output value (specified with the
Note that the ``mask`` attribute applies to output values (specified with the
``name`` attribute) as well as input port values (specified with the
``inputtag`` attribute). If the ``mask`` attribute is present, it must be an
integer value. If the ``mask`` attribute is not present, it is equivalent to

View File

@ -259,7 +259,7 @@ Heres our layout file:
<bounds x="0" y="0" width="0.1" height="0.1" />
<color alpha="0" />
</rect>
<!-- draw the outlined of a square -->
<!-- draw the outline of a square -->
<rect><bounds x="0.02" y="0.02" width="0.06" height="0.01" /></rect>
<rect><bounds x="0.02" y="0.07" width="0.06" height="0.01" /></rect>
<rect><bounds x="0.02" y="0.02" width="0.01" height="0.06" /></rect>
@ -289,7 +289,7 @@ Heres our layout file:
<element id="vertical" ref="line">
<!-- element draws a vertical line, no need to rotate it -->
<orientation rotate="0" />
<!-- centre it in the square horizotnally, using the full height -->
<!-- centre it in the square horizontally, using the full height -->
<bounds x="4.55" y="1.9" width="0.1" height="1" />
</element>
@ -301,7 +301,7 @@ Heres our layout file:
<bounds x="4.1" y="2.35" width="1" height="0.1" />
</element>
<!-- draw a small box at the intersection of the vertical and horiztonal lines -->
<!-- draw a small box at the intersection of the vertical and horizontal lines -->
<element id="box" ref="box">
<bounds x="4.55" y="2.35" width="0.1" height="0.1" />
</element>
@ -595,7 +595,7 @@ Get item bounds
argument to restore the default bounds handler (based on the items
animation state and ``bounds`` child elements).
Get item colour
``item::set_color_callback(cb)``
``item:set_color_callback(cb)``
Set callback for getting the items colour (the element textures colours
multiplied by this colour). Do not attempt to access the items ``color``
@ -605,3 +605,59 @@ Get item colour
ARGB colour (usually created by calling ``emu.render_color``), and takes no
parameters. Call with ``nil`` as the argument to restore the default colour
handler (based on the items animation state and ``color`` child elements).
Get item horizontal scroll window size
``item:set_scroll_size_x_callback(cb)``
Set callback for getting the items horizontal scroll window size. This
allows the script to control how much of the element is displayed by the
item. Do not attempt to access the items ``scroll_size_x`` property from
the callback, as it will result in infinite recursion.
The callback function must return a floating-point number representing the
horizontal window size as a proportion of the associated elements width,
and takes no parameters. A value of 1.0 will display the entire width of
the element; smaller values will display proportionally smaller parts of the
element. Call with ``nil`` as the argument to to restore the default
horizontal scroll window size handler (based on the ``xscroll`` child
element).
Get item vertical scroll window size
``item:set_scroll_size_y_callback(cb)``
Set callback for getting the items vertical scroll window size. This
allows the script to control how much of the element is displayed by the
item. Do not attempt to access the items ``scroll_size_y`` property from
the callback, as it will result in infinite recursion.
The callback function must return a floating-point number representing the
vertical window size as a proportion of the associated elements height, and
takes no parameters. A value of 1.0 will display the entire height of the
element; smaller values will display proportionally smaller parts of the
element. Call with ``nil`` as the argument to to restore the default
vertical scroll window size handler (based on the ``xscroll`` child
element).
Get item horizontal scroll position
``item:set_scroll_pos_x_callback(cb)``
Set callback for getting the items horizontal scroll position. This allows
the script to control which part of the element is displayed by the item.
Do not attempt to access the items ``scroll_pos_x`` property from the
callback, as this will result in infinite recursion.
The callback must return a floating-point number, and takes no parameters.
A value of 0.0 aligns the left edge of the element with the left edge of the
item; larger values pan right. Call with ``nil`` as the argument to restore
the default horizontal scroll position handler (based on bindings in the
``xscroll`` child element).
Get item vertical scroll position
``item:set_scroll_pos_y_callback(cb)``
Set callback for getting the items vertical scroll position. This allows
the script to control which part of the element is displayed by the item.
Do not attempt to access the items ``scroll_pos_y`` property from the
callback, as this will result in infinite recursion.
The callback must return a floating-point number, and takes no parameters.
A value of 0.0 aligns the top edge of the element with the top edge of the
item; larger values pan down. Call with ``nil`` as the argument to restore
the default vertical scroll position handler (based on bindings in the
``yscroll`` child element).

View File

@ -2815,7 +2815,7 @@ view.has_art
Layout view item
~~~~~~~~~~~~~~~~
Wraps MAMEs ``layout_view::item`` class, representing an item in a view. An
Wraps MAMEs ``layout_view_item`` class, representing an item in a view. An
item is drawn as a rectangular textured surface. The texture is supplied by an
emulated screen or a layout element.
@ -2832,7 +2832,7 @@ Methods
item:set_state(state)
Set the value used as the element state and animation state in the absence
of bindings. The argument must be an integer.
item.set_element_state_callback(cb)
item:set_element_state_callback(cb)
Set a function to call to obtain the element state for the item. The
function must accept no arguments and return an integer. Call with ``nil``
to restore the default element state callback (based on bindings in the XML
@ -2844,7 +2844,7 @@ item.set_element_state_callback(cb)
This callback will not be used to obtain the animation state for the item,
even if the item lacks explicit animation state bindings in the XML layout
file.
item.set_animation_state_callback(cb)
item:set_animation_state_callback(cb)
Set a function to call to obtain the animation state for the item. The
function must accept no arguments and return an integer. Call with ``nil``
to restore the default animation state callback (based on bindings in the
@ -2852,7 +2852,7 @@ item.set_animation_state_callback(cb)
Note that the function must not access the items ``animation_state``
property, as this will result in infinite recursion.
item.set_bounds_callback(cb)
item:set_bounds_callback(cb)
Set a function to call to obtain the bounds for the item. The function must
accept no arguments and return a
:ref:`render bounds <luareference-render-bounds>` object in render target
@ -2862,7 +2862,7 @@ item.set_bounds_callback(cb)
Note that the function must not access the items ``bounds`` property, as
this will result in infinite recursion.
item.set_color_callback(cb)
item:set_color_callback(cb)
Set a function to call to obtain the multiplier colour for the item. The
function must accept no arguments and return a
:ref:`render colour <luareference-render-color>` object. Call with ``nil``
@ -2871,6 +2871,50 @@ item.set_color_callback(cb)
Note that the function must not access the items ``color`` property, as
this will result in infinite recursion.
item:set_scroll_size_x_callback(cb)
Set a function to call to obtain the size of the horizontal scroll window as
a proportion of the associated elements width. The function must accept no
arguments and return a floating-point value. Call with ``nil`` to restore
the default horizontal scroll window size callback (based on the ``xscroll``
child element in the XML layout file).
Note that the function must not access the items ``scroll_size_x``
property, as this will result in infinite recursion.
item:set_scroll_size_y_callback(cb)
Set a function to call to obtain the size of the vertical scroll window as a
proportion of the associated elements height. The function must accept no
arguments and return a floating-point value. Call with ``nil`` to restore
the default vertical scroll window size callback (based on the ``yscroll``
child element in the XML layout file).
Note that the function must not access the items ``scroll_size_y``
property, as this will result in infinite recursion.
item:set_scroll_pos_x_callback(cb)
Set a function to call to obtain the horizontal scroll position. A value of
zero places the horizontal scroll window at the left edge of the associated
element. If the item does not wrap horizontally, a value of 1.0 places the
horizontal scroll window at the right edge of the associated element; if the
item wraps horizontally, a value of 1.0 corresponds to wrapping back to the
left edge of the associated element. The function must accept no arguments
and return a floating-point value. Call with ``nil`` to restore the default
horizontal scroll position callback (based on bindings in the ``xscroll``
child element in the XML layout file).
Note that the function must not access the items ``scroll_pos_x`` property,
as this will result in infinite recursion.
item:set_scroll_pos_y_callback(cb)
Set a function to call to obtain the vertical scroll position. A value of
zero places the vertical scroll window at the top edge of the associated
element. If the item does not wrap vertically, a value of 1.0 places the
vertical scroll window at the bottom edge of the associated element; if the
item wraps vertically, a value of 1.0 corresponds to wrapping back to the
left edge of the associated element. The function must accept no arguments
and return a floating-point value. Call with ``nil`` to restore the default
vertical scroll position callback (based on bindings in the ``yscroll``
child element in the XML layout file).
Note that the function must not access the items ``scroll_pos_y`` property,
as this will result in infinite recursion.
Properties
^^^^^^^^^^
@ -2892,6 +2936,28 @@ item.color (read-only)
The items colour for the current state. The colour of the screen or
element texture is multiplied by this colour. This is a
:ref:`render colour <luareference-render-color>` object.
item.scroll_wrap_x (read-only)
A Boolean indicating whether the item wraps horizontally.
item.scroll_wrap_y (read-only)
A Boolean indicating whether the item wraps vertically.
item.scroll_size_x (read/write)
Get the items horizontal scroll window size for the current state, or set
the horizontal scroll window size to use in the absence of bindings. This
is a floating-point value representing a proportion of the associated
elements width.
item.scroll_size_y (read/write)
Get the items vertical scroll window size for the current state, or set the
vertical scroll window size to use in the absence of bindings. This is a
floating-point value representing a proportion of the associated elements
height.
item.scroll_pos_x (read/write)
Get the items horizontal scroll position for the current state, or set the
horizontal scroll position size to use in the absence of bindings. This is
a floating-point value.
item.scroll_pos_y (read/write)
Get the items vertical scroll position for the current state, or set the
vertical position size to use in the absence of bindings. This is a
floating-point value.
item.blend_mode (read-only)
Get the items blend mode. This is an integer value, where 0 means no
blending, 1 means alpha blending, 2 means RGB multiplication, 3 means

View File

@ -253,7 +253,7 @@ class LayoutChecker(Minifyer):
if self.checkIntAttribute('orientation', attrs, 'rotate', 0) not in self.ORIENTATIONS:
self.handleError('Element orientation attribute rotate "%s" is unsupported' % (attrs['rotate'], ))
for name in ('swapxy', 'flipx', 'flipy'):
if (attrs.get(name, 'no') not in self.YESNO) and (not self.VARPATTERN.match(attrs['yesno'])):
if (attrs.get(name, 'no') not in self.YESNO) and (not self.VARPATTERN.match(attrs[name])):
self.handleError('Element orientation attribute %s "%s" is not "yes" or "no"' % (name, attrs[name]))
def checkColor(self, attrs):
@ -332,6 +332,8 @@ class LayoutChecker(Minifyer):
self.have_bounds.append(None if 'group' == name else { })
self.have_orientation.append(False)
self.have_color.append(None if 'group' == name else { })
self.have_xscroll.append(None if ('group' == name) or ('screen' == name) else False)
self.have_yscroll.append(None if ('group' == name) or ('screen' == name) else False)
def rootStartHandler(self, name, attrs):
if 'mamelayout' != name:
@ -665,6 +667,24 @@ class LayoutChecker(Minifyer):
else:
self.handleError('Duplicate element color (previous %s)' % (self.have_color[-1], ))
self.checkColor(attrs)
elif ('xscroll' == name) or ('yscroll' == name):
have_scroll = self.have_xscroll if 'xscroll' == name else self.have_yscroll
if have_scroll[-1] is None:
self.handleError('Encountered unexpected element %s' % (name, ))
elif have_scroll[-1]:
self.handleError('Duplicate element %s' % (name, ))
else:
have_scroll[-1] = self.formatLocation()
self.checkFloatAttribute(name, attrs, 'size', 1.0)
if (attrs.get('wrap', 'no') not in self.YESNO) and (not self.VARPATTERN.match(attrs['wrap'])):
self.handleError('Element %s attribute wrap "%s" is not "yes" or "no"' % (name, attrs['wrap']))
if 'inputtag' in attrs:
if 'name' in attrs:
self.handleError('Element %s has both attribute inputtag and attribute name' % (name, ))
self.checkTag(attrs['inputtag'], name, 'inputtag')
self.checkIntAttribute(name, attrs, 'mask', None)
self.checkIntAttribute(name, attrs, 'min', None)
self.checkIntAttribute(name, attrs, 'max', None)
else:
self.handleError('Encountered unexpected element %s' % (name, ))
self.ignored_depth = 1
@ -673,6 +693,8 @@ class LayoutChecker(Minifyer):
self.have_bounds.pop()
self.have_orientation.pop()
self.have_color.pop()
self.have_xscroll.pop()
self.have_yscroll.pop()
self.handlers.pop()
def setDocumentLocator(self, locator):
@ -688,6 +710,8 @@ class LayoutChecker(Minifyer):
self.have_bounds = [ ]
self.have_orientation = [ ]
self.have_color = [ ]
self.have_xscroll = [ ]
self.have_yscroll = [ ]
self.generated_element_names = False
self.generated_group_names = False
super(LayoutChecker, self).startDocument()
@ -709,6 +733,8 @@ class LayoutChecker(Minifyer):
del self.have_bounds
del self.have_orientation
del self.have_color
del self.have_xscroll
del self.have_yscroll
del self.generated_element_names
del self.generated_group_names
super(LayoutChecker, self).endDocument()

View File

@ -193,9 +193,6 @@ class network_manager;
class output_manager;
// declared in render.h
class layout_element;
class layout_view;
class layout_file;
class render_container;
class render_manager;
class render_target;
@ -204,6 +201,12 @@ class render_texture;
// declared in rendfont.h
class render_font;
// declared in rendlay.h
class layout_element;
class layout_view_item;
class layout_view;
class layout_file;
// declared in romentry.h
class rom_entry;

View File

@ -1097,7 +1097,7 @@ unsigned render_target::configured_view(const char *viewname, int targetindex, i
screen_device const &screen = screens[index() % screens.size()];
for (unsigned i = 0; !view && (m_views.size() > i); ++i)
{
for (layout_view::item &viewitem : m_views[i].first.items())
for (layout_view_item &viewitem : m_views[i].first.items())
{
screen_device const *const viewscreen(viewitem.screen());
if (viewscreen == &screen)
@ -1286,7 +1286,7 @@ void render_target::compute_minimum_size(s32 &minwidth, s32 &minheight)
throw emu_fatalerror("Mandatory artwork is missing");
// scan the current view for all screens
for (layout_view::item &curitem : current_view().items())
for (layout_view_item &curitem : current_view().items())
{
screen_device const *const screen = curitem.screen();
if (screen)
@ -1370,7 +1370,7 @@ render_primitive_list &render_target::get_primitives()
{
// we're running - iterate over items in the view
current_view().prepare_items();
for (layout_view::item &curitem : current_view().visible_items())
for (layout_view_item &curitem : current_view().visible_items())
{
// first apply orientation to the bounds
render_bounds bounds = curitem.bounds();
@ -1391,7 +1391,7 @@ render_primitive_list &render_target::get_primitives()
if (curitem.screen())
add_container_primitives(list, root_xform, item_xform, curitem.screen()->container(), curitem.blend_mode());
else
add_element_primitives(list, item_xform, *curitem.element(), curitem.element_state(), curitem.blend_mode());
add_element_primitives(list, item_xform, curitem);
}
}
else
@ -1494,10 +1494,10 @@ bool render_target::map_point_container(s32 target_x, s32 target_y, render_conta
auto const found(std::find_if(
items.begin(),
items.end(),
[&container] (layout_view::item &item) { return &item.screen()->container() == &container; }));
[&container] (layout_view_item &item) { return &item.screen()->container() == &container; }));
if (items.end() != found)
{
layout_view::item &item(*found);
layout_view_item &item(*found);
render_bounds const bounds(item.bounds());
if (bounds.includes(target_f.first, target_f.second))
{
@ -1555,7 +1555,7 @@ bool render_target::map_point_input(s32 target_x, s32 target_y, ioport_port *&in
{
if (m_hit_test[i] && m_hit_test[items.size() + i])
{
layout_view::item &item(items[i]);
layout_view_item &item(items[i]);
render_bounds const bounds(item.bounds());
if (bounds.includes(target_f.first, target_f.second))
{
@ -2523,11 +2523,13 @@ void render_target::add_container_primitives(render_primitive_list &list, const
// for an element in the current state
//-------------------------------------------------
void render_target::add_element_primitives(render_primitive_list &list, const object_transform &xform, layout_element &element, int state, int blendmode)
void render_target::add_element_primitives(render_primitive_list &list, const object_transform &xform, layout_view_item &item)
{
layout_element &element(*item.element());
int const blendmode(item.blend_mode());
// limit state range to non-negative values
if (state < 0)
state = 0;
int const state((std::max)(item.element_state(), 0));
// get a pointer to the relevant texture
render_texture *texture = element.state_texture(state);
@ -2537,29 +2539,70 @@ void render_target::add_element_primitives(render_primitive_list &list, const ob
// configure the basics
prim->color = xform.color;
prim->flags = PRIMFLAG_TEXORIENT(xform.orientation) | PRIMFLAG_BLENDMODE(blendmode) | PRIMFLAG_TEXFORMAT(texture->format());
prim->flags =
PRIMFLAG_TEXORIENT(xform.orientation) |
PRIMFLAG_TEXFORMAT(texture->format()) |
PRIMFLAG_BLENDMODE(blendmode) |
PRIMFLAG_TEXWRAP((item.scroll_wrap_x() || item.scroll_wrap_y()) ? 1 : 0);
// compute the bounds
s32 width = render_round_nearest(xform.xscale);
s32 height = render_round_nearest(xform.yscale);
prim->bounds.set_wh(render_round_nearest(xform.xoffs), render_round_nearest(xform.yoffs), float(width), float(height));
float const primwidth(render_round_nearest(xform.xscale));
float const primheight(render_round_nearest(xform.yscale));
prim->bounds.set_wh(render_round_nearest(xform.xoffs), render_round_nearest(xform.yoffs), primwidth, primheight);
prim->full_bounds = prim->bounds;
if (xform.orientation & ORIENTATION_SWAP_XY)
std::swap(width, height);
width = (std::min)(width, m_maxtexwidth);
height = (std::min)(height, m_maxtexheight);
// get the scaled texture and append it
texture->get_scaled(width, height, prim->texture, list, prim->flags);
float const xsize(item.scroll_size_x());
float const ysize(item.scroll_size_y());
s32 texwidth = render_round_nearest(((xform.orientation & ORIENTATION_SWAP_XY) ? primwidth : primheight) / xsize);
s32 texheight = render_round_nearest(((xform.orientation & ORIENTATION_SWAP_XY) ? primheight : primwidth) / ysize);
texwidth = (std::min)(texwidth, m_maxtexwidth);
texheight = (std::min)(texheight, m_maxtexheight);
texture->get_scaled(texwidth, texheight, prim->texture, list, prim->flags);
// compute the clip rect
render_bounds cliprect = prim->bounds & m_bounds;
// determine UV coordinates and apply clipping
prim->texcoords = oriented_texcoords[xform.orientation];
bool clipped = render_clip_quad(&prim->bounds, &cliprect, &prim->texcoords);
float const xwindow((xform.orientation & ORIENTATION_SWAP_XY) ? primwidth : primheight);
float const ywindow((xform.orientation & ORIENTATION_SWAP_XY) ? primheight : primwidth);
float const xrange(float(texwidth) - (item.scroll_wrap_x() ? 0.0f : xwindow));
float const yrange(float(texheight) - (item.scroll_wrap_y() ? 0.0f : ywindow));
float const xoffset(render_round_nearest(item.scroll_pos_x() * xrange) / float(texwidth));
float const yoffset(render_round_nearest(item.scroll_pos_y() * yrange) / float(texheight));
float const xend(xoffset + (xwindow / float(texwidth)));
float const yend(yoffset + (ywindow / float(texheight)));
switch (xform.orientation)
{
default:
case 0:
prim->texcoords = render_quad_texuv{ { xoffset, yoffset }, { xend, yoffset }, { xoffset, yend }, { xend, yend } };
break;
case ORIENTATION_FLIP_X:
prim->texcoords = render_quad_texuv{ { xend, yoffset }, { xoffset, yoffset }, { xend, yend }, { xoffset, yend } };
break;
case ORIENTATION_FLIP_Y:
prim->texcoords = render_quad_texuv{ { xoffset, yend }, { xend, yend }, { xoffset, yoffset }, { xend, yoffset } };
break;
case ORIENTATION_FLIP_X | ORIENTATION_FLIP_Y:
prim->texcoords = render_quad_texuv{ { xend, yend }, { xoffset, yend }, { xend, yoffset }, { xoffset, yoffset } };
break;
case ORIENTATION_SWAP_XY:
prim->texcoords = render_quad_texuv{ { xoffset, yoffset }, { xoffset, yend }, { xend, yoffset }, { xend, yend } };
break;
case ORIENTATION_SWAP_XY | ORIENTATION_FLIP_X:
prim->texcoords = render_quad_texuv{ { xoffset, yend }, { xoffset, yoffset }, { xend, yend }, { xend, yoffset } };
break;
case ORIENTATION_SWAP_XY | ORIENTATION_FLIP_Y:
prim->texcoords = render_quad_texuv{ { xend, yoffset }, { xend, yend }, { xoffset, yoffset }, { xoffset, yend } };
break;
case ORIENTATION_SWAP_XY | ORIENTATION_FLIP_X | ORIENTATION_FLIP_Y:
prim->texcoords = render_quad_texuv{ { xend, yend }, { xend, yoffset }, { xoffset, yend }, { xoffset, yoffset } };
break;
}
// add to the list or free if we're clipped out
bool const clipped = render_clip_quad(&prim->bounds, &cliprect, &prim->texcoords);
list.append_or_return(*prim, clipped);
}
}

View File

@ -590,7 +590,7 @@ private:
bool load_layout_file(const char *dirname, const internal_layout &layout_data, device_t *device = nullptr);
bool load_layout_file(device_t &device, util::xml::data_node const &rootnode, const char *searchpath, const char *dirname);
void add_container_primitives(render_primitive_list &list, const object_transform &root_xform, const object_transform &xform, render_container &container, int blendmode);
void add_element_primitives(render_primitive_list &list, const object_transform &xform, layout_element &element, int state, int blendmode);
void add_element_primitives(render_primitive_list &list, const object_transform &xform, layout_view_item &item);
std::pair<float, float> map_point_internal(s32 target_x, s32 target_y);
// config callbacks

View File

@ -1120,6 +1120,121 @@ inline render_color interpolate_color(emu::render::detail::color_vector const &s
}
}
unsigned get_state_shift(ioport_value mask)
{
// get shift to right-align LSB
unsigned result(0U);
while (mask && !BIT(mask, 0))
{
++result;
mask >>= 1;
}
return result;
}
std::string make_child_output_tag(
emu::render::detail::view_environment &env,
util::xml::data_node const &itemnode,
char const *child)
{
util::xml::data_node const *const childnode(itemnode.get_child(child));
if (childnode)
return std::string(env.get_attribute_string(*childnode, "name"));
else
return std::string();
}
std::string make_child_input_tag(
emu::render::detail::view_environment &env,
util::xml::data_node const &itemnode,
char const *child)
{
util::xml::data_node const *const childnode(itemnode.get_child(child));
return childnode ? env.get_attribute_subtag(*childnode, "inputtag") : std::string();
}
ioport_value make_child_mask(
emu::render::detail::view_environment &env,
util::xml::data_node const &itemnode,
char const *child)
{
util::xml::data_node const *const childnode(itemnode.get_child(child));
return childnode ? env.get_attribute_int(*childnode, "mask", ~ioport_value(0)) : ~ioport_value(0);
}
bool make_child_wrap(
emu::render::detail::view_environment &env,
util::xml::data_node const &itemnode,
char const *child)
{
util::xml::data_node const *const childnode(itemnode.get_child(child));
return childnode ? env.get_attribute_bool(*childnode, "wrap", false) : false;
}
float make_child_size(
emu::render::detail::view_environment &env,
util::xml::data_node const &itemnode,
char const *child)
{
util::xml::data_node const *const childnode(itemnode.get_child(child));
return std::clamp(childnode ? env.get_attribute_float(*childnode, "size", 1.0f) : 1.0f, 0.01f, 1.0f);
}
ioport_value make_child_min(
emu::render::detail::view_environment &env,
util::xml::data_node const &itemnode,
char const *child)
{
util::xml::data_node const *const childnode(itemnode.get_child(child));
return childnode ? env.get_attribute_int(*childnode, "min", ioport_value(0)) : ioport_value(0);
}
ioport_value make_child_max(
emu::render::detail::view_environment &env,
util::xml::data_node const &itemnode,
char const *child,
ioport_value mask)
{
util::xml::data_node const *const childnode(itemnode.get_child(child));
ioport_value const dflt(mask >> get_state_shift(mask));
return childnode ? env.get_attribute_int(*childnode, "max", dflt) : dflt;
}
std::string make_input_tag(
emu::render::detail::view_environment &env,
util::xml::data_node const &itemnode)
{
return env.get_attribute_subtag(itemnode, "inputtag");
}
int get_blend_mode(emu::render::detail::view_environment &env, util::xml::data_node const &itemnode)
{
// see if there's a blend mode attribute
std::string const *const mode(itemnode.get_attribute_string_ptr("blend"));
if (mode)
{
if (*mode == "none")
return BLENDMODE_NONE;
else if (*mode == "alpha")
return BLENDMODE_ALPHA;
else if (*mode == "multiply")
return BLENDMODE_RGB_MULTIPLY;
else if (*mode == "add")
return BLENDMODE_ADD;
else
throw layout_syntax_error(util::string_format("unknown blend mode %s", *mode));
}
// fall back to implicit blend mode based on element type
if (!strcmp(itemnode.get_name(), "screen"))
return -1; // magic number recognised by render.cpp to allow per-element blend mode
else if (!strcmp(itemnode.get_name(), "overlay"))
return BLENDMODE_RGB_MULTIPLY;
else
return BLENDMODE_ALPHA;
}
} // anonymous namespace
@ -4096,7 +4211,7 @@ layout_view::~layout_view()
// get_item - get item by ID
//-------------------------------------------------
layout_view::item *layout_view::get_item(std::string const &id)
layout_view_item *layout_view::get_item(std::string const &id)
{
auto const found(m_items_by_id.find(id));
return (m_items_by_id.end() != found) ? &found->second : nullptr;
@ -4502,10 +4617,10 @@ std::string layout_view::make_name(layout_environment &env, util::xml::data_node
//**************************************************************************
//-------------------------------------------------
// item - constructor
// layout_view_item - constructor
//-------------------------------------------------
layout_view::item::item(
layout_view_item::layout_view_item(
view_environment &env,
util::xml::data_node const &itemnode,
element_map &elemmap,
@ -4514,11 +4629,29 @@ layout_view::item::item(
render_color const &color)
: m_element(find_element(env, itemnode, elemmap))
, m_output(env.device(), std::string(env.get_attribute_string(itemnode, "name")))
, m_animoutput(env.device(), make_animoutput_tag(env, itemnode))
, m_animoutput(env.device(), make_child_output_tag(env, itemnode, "animate"))
, m_scrollxoutput(env.device(), make_child_output_tag(env, itemnode, "xscroll"))
, m_scrollyoutput(env.device(), make_child_output_tag(env, itemnode, "yscroll"))
, m_animinput_port(nullptr)
, m_scrollxinput_port(nullptr)
, m_scrollyinput_port(nullptr)
, m_scrollwrapx(make_child_wrap(env, itemnode, "xscroll"))
, m_scrollwrapy(make_child_wrap(env, itemnode, "yscroll"))
, m_elem_state(m_element ? m_element->default_state() : 0)
, m_animmask(make_animmask(env, itemnode))
, m_scrollsizex(make_child_size(env, itemnode, "xscroll"))
, m_scrollsizey(make_child_size(env, itemnode, "yscroll"))
, m_scrollposx(0.0f)
, m_scrollposy(0.0f)
, m_animmask(make_child_mask(env, itemnode, "animate"))
, m_scrollxmask(make_child_mask(env, itemnode, "xscroll"))
, m_scrollymask(make_child_mask(env, itemnode, "yscroll"))
, m_scrollxmin(make_child_min(env, itemnode, "xscroll"))
, m_scrollymin(make_child_min(env, itemnode, "yscroll"))
, m_scrollxmax(make_child_max(env, itemnode, "xscroll", m_scrollxmask))
, m_scrollymax(make_child_max(env, itemnode, "yscroll", m_scrollymask))
, m_animshift(get_state_shift(m_animmask))
, m_scrollxshift(get_state_shift(m_scrollxmask))
, m_scrollyshift(get_state_shift(m_scrollymask))
, m_input_port(nullptr)
, m_input_field(nullptr)
, m_input_mask(env.get_attribute_int(itemnode, "inputmask", 0))
@ -4531,11 +4664,15 @@ layout_view::item::item(
, m_visibility_mask(env.visibility_mask())
, m_id(env.get_attribute_string(itemnode, "id"))
, m_input_tag(make_input_tag(env, itemnode))
, m_animinput_tag(make_animinput_tag(env, itemnode))
, m_animinput_tag(make_child_input_tag(env, itemnode, "animate"))
, m_scrollxinput_tag(make_child_input_tag(env, itemnode, "xscroll"))
, m_scrollyinput_tag(make_child_input_tag(env, itemnode, "yscroll"))
, m_rawbounds(make_bounds(env, itemnode, trans))
, m_have_output(!env.get_attribute_string(itemnode, "name").empty())
, m_input_raw(env.get_attribute_bool(itemnode, "inputraw", 0))
, m_have_animoutput(!make_animoutput_tag(env, itemnode).empty())
, m_have_animoutput(!make_child_output_tag(env, itemnode, "animate").empty())
, m_have_scrollxoutput(!make_child_output_tag(env, itemnode, "xscroll").empty())
, m_have_scrollyoutput(!make_child_output_tag(env, itemnode, "yscroll").empty())
, m_has_clickthrough(!env.get_attribute_string(itemnode, "clickthrough").empty())
{
// fetch common data
@ -4562,6 +4699,14 @@ layout_view::item::item(
{
throw layout_syntax_error(util::string_format("item of type %s requires an element tag", itemnode.get_name()));
}
else if (m_scrollxmin == m_scrollxmax)
{
throw layout_syntax_error(util::string_format("item X scroll minimum and maximum both equal to %u", m_scrollxmin));
}
else if (m_scrollymin == m_scrollymax)
{
throw layout_syntax_error(util::string_format("item Y scroll minimum and maximum both equal to %u", m_scrollymin));
}
// this can be called before resolving tags, make it return something valid
m_bounds = m_rawbounds;
@ -4570,10 +4715,10 @@ layout_view::item::item(
//-------------------------------------------------
// item - destructor
// layout_view_item - destructor
//-------------------------------------------------
layout_view::item::~item()
layout_view_item::~layout_view_item()
{
}
@ -4583,7 +4728,7 @@ layout_view::item::~item()
// obtain element state value
//-------------------------------------------------
void layout_view::item::set_element_state_callback(state_delegate &&handler)
void layout_view_item::set_element_state_callback(state_delegate &&handler)
{
if (!handler.isnull())
m_get_elem_state = std::move(handler);
@ -4597,7 +4742,7 @@ void layout_view::item::set_element_state_callback(state_delegate &&handler)
// obtain animation state
//-------------------------------------------------
void layout_view::item::set_animation_state_callback(state_delegate &&handler)
void layout_view_item::set_animation_state_callback(state_delegate &&handler)
{
if (!handler.isnull())
m_get_anim_state = std::move(handler);
@ -4611,7 +4756,7 @@ void layout_view::item::set_animation_state_callback(state_delegate &&handler)
// bounds
//-------------------------------------------------
void layout_view::item::set_bounds_callback(bounds_delegate &&handler)
void layout_view_item::set_bounds_callback(bounds_delegate &&handler)
{
if (!handler.isnull())
m_get_bounds = std::move(handler);
@ -4625,7 +4770,7 @@ void layout_view::item::set_bounds_callback(bounds_delegate &&handler)
// color
//-------------------------------------------------
void layout_view::item::set_color_callback(color_delegate &&handler)
void layout_view_item::set_color_callback(color_delegate &&handler)
{
if (!handler.isnull())
m_get_color = std::move(handler);
@ -4634,11 +4779,67 @@ void layout_view::item::set_color_callback(color_delegate &&handler)
}
//-------------------------------------------------
// set_scroll_size_x_callback - set callback to
// obtain horizontal scroll window size
//-------------------------------------------------
void layout_view_item::set_scroll_size_x_callback(scroll_size_delegate &&handler)
{
if (!handler.isnull())
m_get_scroll_size_x = std::move(handler);
else
m_get_scroll_size_x = default_get_scroll_size_x();
}
//-------------------------------------------------
// set_scroll_size_y_callback - set callback to
// obtain vertical scroll window size
//-------------------------------------------------
void layout_view_item::set_scroll_size_y_callback(scroll_size_delegate &&handler)
{
if (!handler.isnull())
m_get_scroll_size_y = std::move(handler);
else
m_get_scroll_size_y = default_get_scroll_size_y();
}
//-------------------------------------------------
// set_scroll_pos_x_callback - set callback to
// obtain horizontal scroll position
//-------------------------------------------------
void layout_view_item::set_scroll_pos_x_callback(scroll_pos_delegate &&handler)
{
if (!handler.isnull())
m_get_scroll_pos_x = std::move(handler);
else
m_get_scroll_pos_x = default_get_scroll_pos_x();
}
//-------------------------------------------------
// set_scroll_pos_y_callback - set callback to
// obtain vertical scroll position
//-------------------------------------------------
void layout_view_item::set_scroll_pos_y_callback(scroll_pos_delegate &&handler)
{
if (!handler.isnull())
m_get_scroll_pos_y = std::move(handler);
else
m_get_scroll_pos_y = default_get_scroll_pos_y();
}
//-------------------------------------------------
// resolve_tags - resolve tags, if any are set
//-------------------------------------------------
void layout_view::item::resolve_tags()
void layout_view_item::resolve_tags()
{
// resolve element state output and set default value
if (m_have_output)
@ -4648,13 +4849,21 @@ void layout_view::item::resolve_tags()
m_output = m_element->default_state();
}
// resolve animation state output
// resolve animation state and scroll outputs
if (m_have_animoutput)
m_animoutput.resolve();
if (m_have_scrollxoutput)
m_scrollxoutput.resolve();
if (m_have_scrollyoutput)
m_scrollyoutput.resolve();
// resolve animation state input
// resolve animation state and scroll inputs
if (!m_animinput_tag.empty())
m_animinput_port = m_element->machine().root_device().ioport(m_animinput_tag);
if (!m_scrollxinput_tag.empty())
m_scrollxinput_port = m_element->machine().root_device().ioport(m_scrollxinput_tag);
if (!m_scrollyinput_tag.empty())
m_scrollyinput_port = m_element->machine().root_device().ioport(m_scrollyinput_tag);
// resolve element state input
if (!m_input_tag.empty())
@ -4684,6 +4893,10 @@ void layout_view::item::resolve_tags()
m_get_anim_state = default_get_anim_state();
m_get_bounds = default_get_bounds();
m_get_color = default_get_color();
m_get_scroll_size_x = default_get_scroll_size_x();
m_get_scroll_size_y = default_get_scroll_size_y();
m_get_scroll_pos_x = default_get_scroll_pos_x();
m_get_scroll_pos_y = default_get_scroll_pos_y();
}
@ -4692,18 +4905,18 @@ void layout_view::item::resolve_tags()
// state handler
//-------------------------------------------------
layout_view::item::state_delegate layout_view::item::default_get_elem_state()
layout_view_item::state_delegate layout_view_item::default_get_elem_state()
{
if (m_have_output)
return state_delegate(&item::get_output, this);
return state_delegate(&layout_view_item::get_output, this);
else if (!m_input_port)
return state_delegate(&item::get_state, this);
return state_delegate(&layout_view_item::get_state, this);
else if (m_input_raw)
return state_delegate(&item::get_input_raw, this);
return state_delegate(&layout_view_item::get_input_raw, this);
else if (m_input_field)
return state_delegate(&item::get_input_field_cached, this);
return state_delegate(&layout_view_item::get_input_field_cached, this);
else
return state_delegate(&item::get_input_field_conditional, this);
return state_delegate(&layout_view_item::get_input_field_conditional, this);
}
@ -4712,12 +4925,12 @@ layout_view::item::state_delegate layout_view::item::default_get_elem_state()
// state handler
//-------------------------------------------------
layout_view::item::state_delegate layout_view::item::default_get_anim_state()
layout_view_item::state_delegate layout_view_item::default_get_anim_state()
{
if (m_have_animoutput)
return state_delegate(&item::get_anim_output, this);
return state_delegate(&layout_view_item::get_anim_output, this);
else if (m_animinput_port)
return state_delegate(&item::get_anim_input, this);
return state_delegate(&layout_view_item::get_anim_input, this);
else
return default_get_elem_state();
}
@ -4727,11 +4940,11 @@ layout_view::item::state_delegate layout_view::item::default_get_anim_state()
// default_get_bounds - get default bounds handler
//-------------------------------------------------
layout_view::item::bounds_delegate layout_view::item::default_get_bounds()
layout_view_item::bounds_delegate layout_view_item::default_get_bounds()
{
return (m_bounds.size() == 1U)
? bounds_delegate(&emu::render::detail::bounds_step::get, &m_bounds.front())
: bounds_delegate(&item::get_interpolated_bounds, this);
: bounds_delegate(&layout_view_item::get_interpolated_bounds, this);
}
@ -4739,11 +4952,65 @@ layout_view::item::bounds_delegate layout_view::item::default_get_bounds()
// default_get_color - get default color handler
//-------------------------------------------------
layout_view::item::color_delegate layout_view::item::default_get_color()
layout_view_item::color_delegate layout_view_item::default_get_color()
{
return (m_color.size() == 1U)
? color_delegate(&emu::render::detail::color_step::get, &const_cast<emu::render::detail::color_step &>(m_color.front()))
: color_delegate(&item::get_interpolated_color, this);
: color_delegate(&layout_view_item::get_interpolated_color, this);
}
//-------------------------------------------------
// default_get_scroll_size_x - get default
// horizontal scroll window size handler
//-------------------------------------------------
layout_view_item::scroll_size_delegate layout_view_item::default_get_scroll_size_x()
{
return scroll_size_delegate(&layout_view_item::get_scrollsizex, this);
}
//-------------------------------------------------
// default_get_scroll_size_y - get default
// vertical scroll window size handler
//-------------------------------------------------
layout_view_item::scroll_size_delegate layout_view_item::default_get_scroll_size_y()
{
return scroll_size_delegate(&layout_view_item::get_scrollsizey, this);
}
//-------------------------------------------------
// default_get_scroll_pos_x - get default
// horizontal scroll position handler
//-------------------------------------------------
layout_view_item::scroll_pos_delegate layout_view_item::default_get_scroll_pos_x()
{
if (m_have_scrollxoutput)
return scroll_pos_delegate(m_scrollwrapx ? &layout_view_item::get_scrollx_output<true> : &layout_view_item::get_scrollx_output<false>, this);
else if (m_scrollxinput_port)
return scroll_pos_delegate(m_scrollwrapx ? &layout_view_item::get_scrollx_input<true> : &layout_view_item::get_scrollx_input<false>, this);
else
return scroll_pos_delegate(&layout_view_item::get_scrollposx, this);
}
//-------------------------------------------------
// default_get_scroll_pos_y - get default
// vertical scroll position handler
//-------------------------------------------------
layout_view_item::scroll_pos_delegate layout_view_item::default_get_scroll_pos_y()
{
if (m_have_scrollyoutput)
return scroll_pos_delegate(m_scrollwrapy ? &layout_view_item::get_scrolly_output<true> : &layout_view_item::get_scrolly_output<false>, this);
else if (m_scrollyinput_port)
return scroll_pos_delegate(m_scrollwrapy ? &layout_view_item::get_scrolly_input<true> : &layout_view_item::get_scrolly_input<false>, this);
else
return scroll_pos_delegate(&layout_view_item::get_scrollposy, this);
}
@ -4751,7 +5018,7 @@ layout_view::item::color_delegate layout_view::item::default_get_color()
// get_state - get state when no bindings
//-------------------------------------------------
int layout_view::item::get_state() const
int layout_view_item::get_state() const
{
return m_elem_state;
}
@ -4761,7 +5028,7 @@ int layout_view::item::get_state() const
// get_output - get element state output
//-------------------------------------------------
int layout_view::item::get_output() const
int layout_view_item::get_output() const
{
assert(m_have_output);
return int(s32(m_output));
@ -4772,7 +5039,7 @@ int layout_view::item::get_output() const
// get_input_raw - get element state input
//-------------------------------------------------
int layout_view::item::get_input_raw() const
int layout_view_item::get_input_raw() const
{
assert(m_input_port);
return int(std::make_signed_t<ioport_value>((m_input_port->read() & m_input_mask) >> m_input_shift));
@ -4783,7 +5050,7 @@ int layout_view::item::get_input_raw() const
// get_input_field_cached - element state
//-------------------------------------------------
int layout_view::item::get_input_field_cached() const
int layout_view_item::get_input_field_cached() const
{
assert(m_input_port);
assert(m_input_field);
@ -4795,7 +5062,7 @@ int layout_view::item::get_input_field_cached() const
// get_input_field_conditional - element state
//-------------------------------------------------
int layout_view::item::get_input_field_conditional() const
int layout_view_item::get_input_field_conditional() const
{
assert(m_input_port);
assert(!m_input_field);
@ -4808,7 +5075,7 @@ int layout_view::item::get_input_field_conditional() const
// get_anim_output - get animation output
//-------------------------------------------------
int layout_view::item::get_anim_output() const
int layout_view_item::get_anim_output() const
{
assert(m_have_animoutput);
return int(unsigned((u32(s32(m_animoutput) & m_animmask) >> m_animshift)));
@ -4819,18 +5086,121 @@ int layout_view::item::get_anim_output() const
// get_anim_input - get animation input
//-------------------------------------------------
int layout_view::item::get_anim_input() const
int layout_view_item::get_anim_input() const
{
assert(m_animinput_port);
return int(std::make_signed_t<ioport_value>((m_animinput_port->read() & m_animmask) >> m_animshift));
}
//-------------------------------------------------
// get_scrollsizex - get horizontal scroll window
// size
//-------------------------------------------------
float layout_view_item::get_scrollsizex() const
{
return m_scrollsizex;
}
//-------------------------------------------------
// get_scrollsizey - get vertical scroll window
// size
//-------------------------------------------------
float layout_view_item::get_scrollsizey() const
{
return m_scrollsizey;
}
//-------------------------------------------------
// get_scrollposx - get horizontal scroll
// position
//-------------------------------------------------
float layout_view_item::get_scrollposx() const
{
return m_scrollposx;
}
//-------------------------------------------------
// get_scrollposy - get vertical scroll position
//-------------------------------------------------
float layout_view_item::get_scrollposy() const
{
return m_scrollposy;
}
//-------------------------------------------------
// get_scrollx_output - get scaled horizontal
// scroll output
//-------------------------------------------------
template <bool Wrap>
float layout_view_item::get_scrollx_output() const
{
assert(m_have_scrollxoutput);
u32 const unscaled(((u32(s32(m_scrollxoutput)) & m_scrollxmask) >> m_scrollxshift) - m_scrollxmin);
float const range(std::make_signed_t<ioport_value>(m_scrollxmax - m_scrollxmin) + (!Wrap ? 0 : (m_scrollxmin < m_scrollxmax) ? 1 : -1));
return float(s32(unscaled)) / range;
}
//-------------------------------------------------
// get_scrollx_input - get scaled horizontal
// scroll input
//-------------------------------------------------
template <bool Wrap>
float layout_view_item::get_scrollx_input() const
{
assert(m_scrollxinput_port);
ioport_value const unscaled(((m_scrollxinput_port->read() & m_scrollxmask) >> m_scrollxshift) - m_scrollxmin);
float const range(std::make_signed_t<ioport_value>(m_scrollxmax - m_scrollxmin) + (!Wrap ? 0 : (m_scrollxmin < m_scrollxmax) ? 1 : -1));
return float(std::make_signed_t<ioport_value>(unscaled)) / range;
}
//-------------------------------------------------
// get_scrolly_output - get scaled vertical
// scroll output
//-------------------------------------------------
template <bool Wrap>
float layout_view_item::get_scrolly_output() const
{
assert(m_have_scrollyoutput);
u32 const unscaled(((u32(s32(m_scrollyoutput)) & m_scrollymask) >> m_scrollyshift) - m_scrollymin);
float const range(std::make_signed_t<ioport_value>(m_scrollymax - m_scrollymin) + (!Wrap ? 0 : (m_scrollymin < m_scrollymax) ? 1 : -1));
return float(s32(unscaled)) / range;
}
//-------------------------------------------------
// get_scrolly_input - get scaled vertical scroll
// input
//-------------------------------------------------
template <bool Wrap>
float layout_view_item::get_scrolly_input() const
{
assert(m_scrollyinput_port);
ioport_value const unscaled(((m_scrollyinput_port->read() & m_scrollymask) >> m_scrollyshift) - m_scrollymin);
float const range(std::make_signed_t<ioport_value>(m_scrollymax - m_scrollymin) + (!Wrap ? 0 : (m_scrollymin < m_scrollymax) ? 1 : -1));
return float(std::make_signed_t<ioport_value>(unscaled)) / range;
}
//-------------------------------------------------
// get_interpolated_bounds - animated bounds
//-------------------------------------------------
render_bounds layout_view::item::get_interpolated_bounds() const
render_bounds layout_view_item::get_interpolated_bounds() const
{
assert(m_bounds.size() > 1U);
return interpolate_bounds(m_bounds, m_get_anim_state());
@ -4841,7 +5211,7 @@ render_bounds layout_view::item::get_interpolated_bounds() const
// get_interpolated_color - animated color
//-------------------------------------------------
render_color layout_view::item::get_interpolated_color() const
render_color layout_view_item::get_interpolated_color() const
{
assert(m_color.size() > 1U);
return interpolate_color(m_color, m_get_anim_state());
@ -4852,7 +5222,7 @@ render_color layout_view::item::get_interpolated_color() const
// find_element - find element definition
//-------------------------------------------------
layout_element *layout_view::item::find_element(view_environment &env, util::xml::data_node const &itemnode, element_map &elemmap)
layout_element *layout_view_item::find_element(view_environment &env, util::xml::data_node const &itemnode, element_map &elemmap)
{
std::string const name(env.get_attribute_string(itemnode, !strcmp(itemnode.get_name(), "element") ? "ref" : "element"));
if (name.empty())
@ -4871,7 +5241,7 @@ layout_element *layout_view::item::find_element(view_environment &env, util::xml
// make_bounds - get transformed bounds
//-------------------------------------------------
layout_view::item::bounds_vector layout_view::item::make_bounds(
layout_view_item::bounds_vector layout_view_item::make_bounds(
view_environment &env,
util::xml::data_node const &itemnode,
layout_group::transform const &trans)
@ -4904,7 +5274,7 @@ layout_view::item::bounds_vector layout_view::item::make_bounds(
// make_color - get color inflection points
//-------------------------------------------------
layout_view::item::color_vector layout_view::item::make_color(
layout_view_item::color_vector layout_view_item::make_color(
view_environment &env,
util::xml::data_node const &itemnode,
render_color const &mult)
@ -4934,101 +5304,6 @@ layout_view::item::color_vector layout_view::item::make_color(
}
//-------------------------------------------------
// make_animoutput_tag - get animation output tag
//-------------------------------------------------
std::string layout_view::item::make_animoutput_tag(view_environment &env, util::xml::data_node const &itemnode)
{
util::xml::data_node const *const animate(itemnode.get_child("animate"));
if (animate)
return std::string(env.get_attribute_string(*animate, "name"));
else
return std::string();
}
//-------------------------------------------------
// make_animmask - get animation state mask
//-------------------------------------------------
ioport_value layout_view::item::make_animmask(view_environment &env, util::xml::data_node const &itemnode)
{
util::xml::data_node const *const animate(itemnode.get_child("animate"));
return animate ? env.get_attribute_int(*animate, "mask", ~ioport_value(0)) : ~ioport_value(0);
}
//-------------------------------------------------
// make_animinput_tag - get absolute tag for
// animation input
//-------------------------------------------------
std::string layout_view::item::make_animinput_tag(view_environment &env, util::xml::data_node const &itemnode)
{
util::xml::data_node const *const animate(itemnode.get_child("animate"));
return animate ? env.get_attribute_subtag(*animate, "inputtag") : std::string();
}
//-------------------------------------------------
// make_input_tag - get absolute input tag
//-------------------------------------------------
std::string layout_view::item::make_input_tag(view_environment &env, util::xml::data_node const &itemnode)
{
return env.get_attribute_subtag(itemnode, "inputtag");
}
//-------------------------------------------------
// get_blend_mode - explicit or implicit blend
//-------------------------------------------------
int layout_view::item::get_blend_mode(view_environment &env, util::xml::data_node const &itemnode)
{
// see if there's a blend mode attribute
std::string const *const mode(itemnode.get_attribute_string_ptr("blend"));
if (mode)
{
if (*mode == "none")
return BLENDMODE_NONE;
else if (*mode == "alpha")
return BLENDMODE_ALPHA;
else if (*mode == "multiply")
return BLENDMODE_RGB_MULTIPLY;
else if (*mode == "add")
return BLENDMODE_ADD;
else
throw layout_syntax_error(util::string_format("unknown blend mode %s", *mode));
}
// fall back to implicit blend mode based on element type
if (!strcmp(itemnode.get_name(), "screen"))
return -1; // magic number recognised by render.cpp to allow per-element blend mode
else if (!strcmp(itemnode.get_name(), "overlay"))
return BLENDMODE_RGB_MULTIPLY;
else
return BLENDMODE_ALPHA;
}
//-------------------------------------------------
// get_state_shift - shift to right-align LSB
//-------------------------------------------------
unsigned layout_view::item::get_state_shift(ioport_value mask)
{
unsigned result(0U);
while (mask && !BIT(mask, 0))
{
++result;
mask >>= 1;
}
return result;
}
//**************************************************************************
// LAYOUT VIEW VISIBILITY TOGGLE

View File

@ -233,6 +233,178 @@ private:
};
/// \brief A single item in a view
///
/// Each view has a list of item structures describing the visual
/// elements to draw, where they are located, additional blending modes,
/// and bindings for inputs and outputs.
class layout_view_item
{
friend class layout_view;
public:
using view_environment = emu::render::detail::view_environment;
using element_map = std::unordered_map<std::string, layout_element>;
using state_delegate = delegate<int ()>;
using bounds_delegate = delegate<render_bounds ()>;
using color_delegate = delegate<render_color ()>;
using scroll_size_delegate = delegate<float ()>;
using scroll_pos_delegate = delegate<float ()>;
// construction/destruction
layout_view_item(
view_environment &env,
util::xml::data_node const &itemnode,
element_map &elemmap,
int orientation,
layout_group::transform const &trans,
render_color const &color);
~layout_view_item();
// getters
std::string const &id() const { return m_id; }
layout_element *element() const { return m_element; }
screen_device *screen() const { return m_screen; }
bool bounds_animated() const { return m_bounds.size() > 1U; }
bool color_animated() const { return m_color.size() > 1U; }
render_bounds bounds() const { return m_get_bounds(); }
render_color color() const { return m_get_color(); }
bool scroll_wrap_x() const { return m_scrollwrapx; }
bool scroll_wrap_y() const { return m_scrollwrapy; }
float scroll_size_x() const { return m_get_scroll_size_x(); }
float scroll_size_y() const { return m_get_scroll_size_y(); }
float scroll_pos_x() const { return m_get_scroll_pos_x(); }
float scroll_pos_y() const { return m_get_scroll_pos_y(); }
int blend_mode() const { return m_blend_mode; }
u32 visibility_mask() const { return m_visibility_mask; }
int orientation() const { return m_orientation; }
render_container *screen_container() const { return m_screen ? &m_screen->container() : nullptr; }
// interactivity
bool has_input() const { return bool(m_input_port); }
std::pair<ioport_port *, ioport_value> input_tag_and_mask() const { return std::make_pair(m_input_port, m_input_mask); };
bool clickthrough() const { return m_clickthrough; }
// fetch state based on configured source
int element_state() const { return m_get_elem_state(); }
int animation_state() const { return m_get_anim_state(); }
// set state
void set_state(int state) { m_elem_state = state; }
void set_scroll_size_x(float size) { m_scrollsizex = std::clamp(size, 0.01f, 1.0f); }
void set_scroll_size_y(float size) { m_scrollsizey = std::clamp(size, 0.01f, 1.0f); }
void set_scroll_pos_x(float pos) { m_scrollposx = pos; }
void set_scroll_pos_y(float pos) { m_scrollposy = pos; }
// set handlers
void set_element_state_callback(state_delegate &&handler);
void set_animation_state_callback(state_delegate &&handler);
void set_bounds_callback(bounds_delegate &&handler);
void set_color_callback(color_delegate &&handler);
void set_scroll_size_x_callback(scroll_size_delegate &&handler);
void set_scroll_size_y_callback(scroll_size_delegate &&handler);
void set_scroll_pos_x_callback(scroll_pos_delegate &&handler);
void set_scroll_pos_y_callback(scroll_pos_delegate &&handler);
// resolve tags, if any
void resolve_tags();
private:
using bounds_vector = emu::render::detail::bounds_vector;
using color_vector = emu::render::detail::color_vector;
state_delegate default_get_elem_state();
state_delegate default_get_anim_state();
bounds_delegate default_get_bounds();
color_delegate default_get_color();
scroll_size_delegate default_get_scroll_size_x();
scroll_size_delegate default_get_scroll_size_y();
scroll_pos_delegate default_get_scroll_pos_x();
scroll_pos_delegate default_get_scroll_pos_y();
int get_state() const;
int get_output() const;
int get_input_raw() const;
int get_input_field_cached() const;
int get_input_field_conditional() const;
int get_anim_output() const;
int get_anim_input() const;
float get_scrollsizex() const;
float get_scrollsizey() const;
float get_scrollposx() const;
float get_scrollposy() const;
template <bool Wrap> float get_scrollx_output() const;
template <bool Wrap> float get_scrolly_output() const;
template <bool Wrap> float get_scrollx_input() const;
template <bool Wrap> float get_scrolly_input() const;
render_bounds get_interpolated_bounds() const;
render_color get_interpolated_color() const;
static layout_element *find_element(view_environment &env, util::xml::data_node const &itemnode, element_map &elemmap);
static bounds_vector make_bounds(view_environment &env, util::xml::data_node const &itemnode, layout_group::transform const &trans);
static color_vector make_color(view_environment &env, util::xml::data_node const &itemnode, render_color const &mult);
// internal state
layout_element *const m_element; // pointer to the associated element (non-screens only)
state_delegate m_get_elem_state; // resolved element state function
state_delegate m_get_anim_state; // resolved animation state function
bounds_delegate m_get_bounds; // resolved bounds function
color_delegate m_get_color; // resolved color function
scroll_size_delegate m_get_scroll_size_x; // resolved horizontal scroll window size function
scroll_size_delegate m_get_scroll_size_y; // resolved vertical scroll window size function
scroll_pos_delegate m_get_scroll_pos_x; // resolved horizontal scroll position function
scroll_pos_delegate m_get_scroll_pos_y; // resolved vertical scroll position function
output_finder<> m_output; // associated output
output_finder<> m_animoutput; // associated output for animation if different
output_finder<> m_scrollxoutput; // associated output for horizontal scroll position
output_finder<> m_scrollyoutput; // associated output for vertical scroll position
ioport_port * m_animinput_port; // input port used for animation
ioport_port * m_scrollxinput_port; // input port used for horizontal scrolling
ioport_port * m_scrollyinput_port; // input port used for vertical scrolling
bool const m_scrollwrapx; // whether horizontal scrolling works like a loop
bool const m_scrollwrapy; // whether vertical scrolling works like a loop
int m_elem_state; // element state used in absence of bindings
float m_scrollsizex; // horizontal scroll window size used in absence of bindings
float m_scrollsizey; // vertical scroll window size used in absence of bindings
float m_scrollposx; // horizontal scroll position used in absence of bindings
float m_scrollposy; // vertical scroll position used in absence of bindings
ioport_value const m_animmask; // mask for animation state
ioport_value const m_scrollxmask; // mask for horizontal scroll position
ioport_value const m_scrollymask; // mask for vertical scroll position
ioport_value const m_scrollxmin; // minimum value for horizontal scroll position
ioport_value const m_scrollymin; // minimum value for vertical scroll position
ioport_value const m_scrollxmax; // maximum value for horizontal scroll position
ioport_value const m_scrollymax; // maximum value for vertical scroll position
u8 const m_animshift; // shift for animation state
u8 const m_scrollxshift; // shift for horizontal scroll position
u8 const m_scrollyshift; // shift for vertical scroll position
ioport_port * m_input_port; // input port of this item
ioport_field const * m_input_field; // input port field of this item
ioport_value const m_input_mask; // input mask of this item
u8 const m_input_shift; // input mask rightshift for raw (trailing 0s)
bool m_clickthrough; // should click pass through to lower elements
screen_device * m_screen; // pointer to screen
int const m_orientation; // orientation of this item
bounds_vector m_bounds; // bounds of the item
color_vector const m_color; // color of the item
int m_blend_mode; // blending mode to use when drawing
u32 m_visibility_mask; // combined mask of parent visibility groups
// cold items
std::string const m_id; // optional unique item identifier
std::string const m_input_tag; // input tag of this item
std::string const m_animinput_tag; // tag of input port for animation state
std::string const m_scrollxinput_tag; // tag of input port for horizontal scroll position
std::string const m_scrollyinput_tag; // tag of input port for vertical scroll position
bounds_vector const m_rawbounds; // raw (original) bounds of the item
bool const m_have_output; // whether we actually have an output
bool const m_input_raw; // get raw data from input port
bool const m_have_animoutput; // whether we actually have an output for animation
bool const m_have_scrollxoutput; // whether we actually have an output for horizontal scroll
bool const m_have_scrollyoutput; // whether we actually have an output for vertical scroll
bool const m_has_clickthrough; // whether clickthrough was explicitly configured
};
/// \brief A single view within a #layout_file
///
/// The view is described using arbitrary coordinates that are scaled to
@ -243,133 +415,14 @@ class layout_view
public:
using layout_environment = emu::render::detail::layout_environment;
using view_environment = emu::render::detail::view_environment;
using element_map = std::unordered_map<std::string, layout_element>;
using element_map = layout_view_item::element_map;
using group_map = std::unordered_map<std::string, layout_group>;
using screen_ref_vector = std::vector<std::reference_wrapper<screen_device const>>;
using prepare_items_delegate = delegate<void ()>;
using preload_delegate = delegate<void ()>;
using recomputed_delegate = delegate<void ()>;
/// \brief A single item in a view
///
/// Each view has a list of item structures describing the visual
/// elements to draw, where they are located, additional blending
/// modes, and bindings for inputs and outputs.
class item
{
friend class layout_view;
public:
using state_delegate = delegate<int ()>;
using bounds_delegate = delegate<render_bounds ()>;
using color_delegate = delegate<render_color ()>;
// construction/destruction
item(
view_environment &env,
util::xml::data_node const &itemnode,
element_map &elemmap,
int orientation,
layout_group::transform const &trans,
render_color const &color);
~item();
// getters
std::string const &id() const { return m_id; }
layout_element *element() const { return m_element; }
screen_device *screen() const { return m_screen; }
bool bounds_animated() const { return m_bounds.size() > 1U; }
bool color_animated() const { return m_color.size() > 1U; }
render_bounds bounds() const { return m_get_bounds(); }
render_color color() const { return m_get_color(); }
int blend_mode() const { return m_blend_mode; }
u32 visibility_mask() const { return m_visibility_mask; }
int orientation() const { return m_orientation; }
render_container *screen_container() const { return m_screen ? &m_screen->container() : nullptr; }
// interactivity
bool has_input() const { return bool(m_input_port); }
std::pair<ioport_port *, ioport_value> input_tag_and_mask() const { return std::make_pair(m_input_port, m_input_mask); };
bool clickthrough() const { return m_clickthrough; }
// fetch state based on configured source
int element_state() const { return m_get_elem_state(); }
int animation_state() const { return m_get_anim_state(); }
// set state
void set_state(int state) { m_elem_state = state; }
// set handlers
void set_element_state_callback(state_delegate &&handler);
void set_animation_state_callback(state_delegate &&handler);
void set_bounds_callback(bounds_delegate &&handler);
void set_color_callback(color_delegate &&handler);
// resolve tags, if any
void resolve_tags();
private:
using bounds_vector = emu::render::detail::bounds_vector;
using color_vector = emu::render::detail::color_vector;
state_delegate default_get_elem_state();
state_delegate default_get_anim_state();
bounds_delegate default_get_bounds();
color_delegate default_get_color();
int get_state() const;
int get_output() const;
int get_input_raw() const;
int get_input_field_cached() const;
int get_input_field_conditional() const;
int get_anim_output() const;
int get_anim_input() const;
render_bounds get_interpolated_bounds() const;
render_color get_interpolated_color() const;
static layout_element *find_element(view_environment &env, util::xml::data_node const &itemnode, element_map &elemmap);
static bounds_vector make_bounds(view_environment &env, util::xml::data_node const &itemnode, layout_group::transform const &trans);
static color_vector make_color(view_environment &env, util::xml::data_node const &itemnode, render_color const &mult);
static std::string make_animoutput_tag(view_environment &env, util::xml::data_node const &itemnode);
static std::string make_animinput_tag(view_environment &env, util::xml::data_node const &itemnode);
static ioport_value make_animmask(view_environment &env, util::xml::data_node const &itemnode);
static std::string make_input_tag(view_environment &env, util::xml::data_node const &itemnode);
static int get_blend_mode(view_environment &env, util::xml::data_node const &itemnode);
static unsigned get_state_shift(ioport_value mask);
// internal state
layout_element *const m_element; // pointer to the associated element (non-screens only)
state_delegate m_get_elem_state; // resolved element state function
state_delegate m_get_anim_state; // resolved animation state function
bounds_delegate m_get_bounds; // resolved bounds function
color_delegate m_get_color; // resolved color function
output_finder<> m_output; // associated output
output_finder<> m_animoutput; // associated output for animation if different
ioport_port * m_animinput_port; // input port used for animation
int m_elem_state; // element state used in absence of bindings
ioport_value const m_animmask; // mask for animation state
u8 const m_animshift; // shift for animation state
ioport_port * m_input_port; // input port of this item
ioport_field const * m_input_field; // input port field of this item
ioport_value const m_input_mask; // input mask of this item
u8 const m_input_shift; // input mask rightshift for raw (trailing 0s)
bool m_clickthrough; // should click pass through to lower elements
screen_device * m_screen; // pointer to screen
int const m_orientation; // orientation of this item
bounds_vector m_bounds; // bounds of the item
color_vector const m_color; // color of the item
int m_blend_mode; // blending mode to use when drawing
u32 m_visibility_mask; // combined mask of parent visibility groups
// cold items
std::string const m_id; // optional unique item identifier
std::string const m_input_tag; // input tag of this item
std::string const m_animinput_tag; // tag of input port for animation state
bounds_vector const m_rawbounds; // raw (original) bounds of the item
bool const m_have_output; // whether we actually have an output
bool const m_input_raw; // get raw data from input port
bool const m_have_animoutput; // whether we actually have an output for animation
bool const m_has_clickthrough; // whether clickthrough was explicitly configured
};
using item = layout_view_item;
using item_list = std::list<item>;
using item_ref_vector = std::vector<std::reference_wrapper<item> >;

View File

@ -39,7 +39,7 @@ struct layout_view_items
layout_view_items(layout_view &v) : view(v) { }
layout_view::item_list &items() { return view.items(); }
static layout_view::item &unwrap(layout_view::item_list::iterator const &it) { return *it; }
static layout_view_item &unwrap(layout_view::item_list::iterator const &it) { return *it; }
static int push_key(lua_State *L, layout_view::item_list::iterator const &it, std::size_t ix) { return sol::stack::push(L, ix + 1); }
layout_view &view;
@ -97,7 +97,7 @@ public:
{
layout_view_items &self(get_self(L));
char const *const id(stack::unqualified_get<char const *>(L));
layout_view::item *const item(self.view.get_item(id));
layout_view_item *const item(self.view.get_item(id));
if (item)
return stack::push_reference(L, *item);
else
@ -279,48 +279,86 @@ void lua_engine::initialize_render(sol::table &emu)
layout_view_type["has_art"] = sol::property(&layout_view::has_art);
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;
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,
&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,
&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,
&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,
&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["id"] = sol::property(
[] (layout_view::item &i, sol::this_state s) -> sol::object
[] (layout_view_item &i, sol::this_state s) -> sol::object
{
if (i.id().empty())
return sol::lua_nil;
else
return sol::make_object(s, i.id());
});
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);
layout_view_item_type["color"] = sol::property(&layout_view::item::color);
layout_view_item_type["blend_mode"] = sol::property(&layout_view::item::blend_mode);
layout_view_item_type["orientation"] = sol::property(&layout_view::item::orientation);
layout_view_item_type["element_state"] = sol::property(&layout_view::item::element_state);
layout_view_item_type["animation_state"] = sol::property(&layout_view::item::animation_state);
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);
layout_view_item_type["color"] = sol::property(&layout_view_item::color);
layout_view_item_type["scroll_wrap_x"] = sol::property(&layout_view_item::scroll_wrap_x);
layout_view_item_type["scroll_wrap_y"] = sol::property(&layout_view_item::scroll_wrap_y);
layout_view_item_type["scroll_size_x"] = sol::property(
&layout_view_item::scroll_size_x,
&layout_view_item::set_scroll_size_x);
layout_view_item_type["scroll_size_y"] = sol::property(
&layout_view_item::scroll_size_y,
&layout_view_item::set_scroll_size_y);
layout_view_item_type["scroll_pos_x"] = sol::property(
&layout_view_item::scroll_pos_x,
&layout_view_item::set_scroll_pos_y);
layout_view_item_type["scroll_pos_y"] = sol::property(
&layout_view_item::scroll_pos_y,
&layout_view_item::set_scroll_pos_y);
layout_view_item_type["blend_mode"] = sol::property(&layout_view_item::blend_mode);
layout_view_item_type["orientation"] = sol::property(&layout_view_item::orientation);
layout_view_item_type["element_state"] = sol::property(&layout_view_item::element_state);
layout_view_item_type["animation_state"] = sol::property(&layout_view_item::animation_state);
auto layout_file_type = sol().registry().new_usertype<layout_file>("layout_file", sol::no_constructor);