|
Joined: Feb 2014
Posts: 1,075 Likes: 151
Very Senior Member
|
OP
Very Senior Member
Joined: Feb 2014
Posts: 1,075 Likes: 151 |
Stupid realization of the day: (after trying over and over and over again) I kept trying to print "node->get_value()" and it kept being empty. I couldn't figure out how the image svg data node could have a value but other nodes had basically nothing. Then I realized that the value is it's what's *inside* the tag, and the CDATA comments out the interior tags (if you have tags inside).
<data><![CDATA[
<svg viewBox="-54 -41.5 158 158" xmlns="http://www.w3.org/2000/svg">
<g transform="rotate(~rotang~ 25 37.5)" fill="white" >
<polygon points="0,0 70,0 100,20 100,55 70,75 0,75" />
</g>
</svg>
]]></data>
any text between <data> and </data> is what you get when you call get_value(). So if you wanted a node to have get_value() text you can do:
<element ref="bigtrak" id="bigtrak">THIS IS A BUNCH OF <bounds x="10" y="10" width="20" height="20"/>INSIDE TEXT</element>
and if I do a get_value() of the element node I will get "THIS IS A BUNCH OF INSIDE TEXT".
|
|
|
|
Joined: Feb 2014
Posts: 1,075 Likes: 151
Very Senior Member
|
OP
Very Senior Member
Joined: Feb 2014
Posts: 1,075 Likes: 151 |
One other idea I had was to have a pasteblock for repeated tags. So you could do something like
<defineblock name="button0">
<element name="~paramref~">
<rect><color red="0.0" blue="0.0" green="0.0"/><bounds left="0.00" right="1.00" top="0.50" bottom="1.00" /></rect>
<rect state="0"><color red="~col0r~" green="~col0g~" blue="~col0b~"/>
<bounds left="0.05" right="0.95" top="0.55" bottom="0.95" /></rect>
<text state="0" string="~str0~"><color red="~col1r~" green="~col1g~" blue="~col1b~"/>
<bounds left="0.0" right="1.0" top="0.0" bottom="0.55" /></text>
<rect state="1"><color red="~col2r~" green="~col2g~" blue="~col2b~"/>
<bounds left="0.05" right="0.95" top="0.55" bottom="0.95" /></rect>
<text state="1" string="~str0~"><color red="~col3r~" green="~col3g~" blue="~col3b~"/>
<bounds left="0.0" right="1.0" top="0.0" bottom="0.55" /></text>
</element>
</defineblock>
Then you could have a pasteblock tag like:
<param name="str0" value="GO"/>
<param name="paramref" value="goref"/>
<pasteblock name="button0"/>
<param name="str0" value="STOP"/>
<param name="paramref" value="stopref"/>
<pasteblock name="button0"/>
I figure that when you hit a pasteblock you could copy the nodes into that position in the tree and then continue parsing.
|
|
|
|
Joined: Feb 2014
Posts: 1,075 Likes: 151
Very Senior Member
|
OP
Very Senior Member
Joined: Feb 2014
Posts: 1,075 Likes: 151 |
Okay, I was able to get a macro working, I called the tags "definemacro" and "macro" though that can be changed. It works at the top level at <mamelayout>.
<definemacro name="button0">
<element name="~ref~">
<rect><color red="0.0" blue="0.0" green="0.0"/><bounds left="0.00" right="1.00" top="0.50" bottom="1.00" /></rect>
<rect state="0"><color red="~col0r~" green="~col0g~" blue="~col0b~"/>
<bounds left="0.05" right="0.95" top="0.55" bottom="0.95" /></rect>
<text state="0" string="~str0~"><color red="~col1r~" green="~col1g~" blue="~col1b~"/>
<bounds left="0.0" right="1.0" top="0.0" bottom="0.55" /></text>
<rect state="1"><color red="~col2r~" green="~col2g~" blue="~col2b~"/>
<bounds left="0.05" right="0.95" top="0.55" bottom="0.95" /></rect>
<text state="1" string="~str0~"><color red="~col3r~" green="~col3g~" blue="~col3b~"/>
<bounds left="0.0" right="1.0" top="0.0" bottom="0.55" /></text>
</element>
</definemacro>
<param name="str0" value="<"/>
<param name="ref" value="keyleftref"/>
<macro name="button0"/>
<param name="str0" value="UP,UP,>,>"/>
<param name="ref" value="keyfwdref,keyrightref"/>
<repeat count="2">
<macro name="button0"/>
</repeat>
Note on the second one with the repeat, I doubled up the str0 values because they get used twice per pass of the repeat, since the macro uses ~str0~ in two places. It isn't a large amount of extra code to add this macro capability. We just start parsing the tree where the macro is defined whenever we want to insert the macro. No copying necessary. Just make an macro_map type and a m_macromap member of layout_file:
using macro_map = std::unordered_map<std::string, const util::xml::data_node *>;
macro_map m_macromap; // list of macro definitions
and then parse the "definemacro" and "macro" tags:
void layout_file::add_elements(
environment &env,
util::xml::data_node const &parentnode,
group_map &groupmap,
bool repeat,
bool init)
{
for (util::xml::data_node const *childnode = parentnode.get_first_child(); childnode; childnode = childnode->get_next_sibling())
{
if (!strcmp(childnode->get_name(), "definemacro"))
{
std::string_view const name(env.get_attribute_string(*childnode, "name"));
if (name.empty())
throw layout_syntax_error("definemacro must have non-empty name attribute");
if (!m_macromap.emplace(std::piecewise_construct, std::forward_as_tuple(name), std::forward_as_tuple(childnode)).second)
throw layout_syntax_error(util::string_format("duplicate macro name %s", name));
}
else if (!strcmp(childnode->get_name(), "macro"))
{
std::string_view const name(env.get_attribute_string(*childnode, "name"));
if (name.empty())
throw layout_syntax_error("macro must have non-empty name attribute");
macro_map::iterator it = m_macromap.find(std::string(name));
if (it == m_macromap.end())
throw layout_syntax_error(util::string_format("macro not defined %s", name));
add_elements(env, *(it->second), groupmap, false, false);
}
else if (!strcmp(childnode->get_name(), "param"))
...
Using these features, I can get the layout file down to around 6150 bytes which is not far from hap's simplified layout. edit: I figured out a workaround for the param str0 appearing twice in the macro, just make another param str1 that grabs the value of str0.
<definemacro name="button0">
<param name="str1" value="~str0~"/>
<element name="~ref~">
<rect><color red="0.0" blue="0.0" green="0.0"/><bounds left="0.00" right="1.00" top="0.50" bottom="1.00" /></rect>
<rect state="0"><color red="~col0r~" green="~col0g~" blue="~col0b~"/>
<bounds left="0.05" right="0.95" top="0.55" bottom="0.95" /></rect>
<text state="0" string="~str1~"><color red="~col1r~" green="~col1g~" blue="~col1b~"/>
<bounds left="0.0" right="1.0" top="0.0" bottom="0.55" /></text>
<rect state="1"><color red="~col2r~" green="~col2g~" blue="~col2b~"/>
<bounds left="0.05" right="0.95" top="0.55" bottom="0.95" /></rect>
<text state="1" string="~str1~"><color red="~col3r~" green="~col3g~" blue="~col3b~"/>
<bounds left="0.0" right="1.0" top="0.0" bottom="0.55" /></text>
</element>
</definemacro>
<param name="str0" value="<,UP,>,STOP,GO,1,2,3,4"/>
<param name="ref" value="keyleftref,keyfwdref,keyrightref,keystopref,keygoref,key1ref,key2ref,key3ref,key4ref"/>
<repeat count="9">
<macro name="button0"/>
</repeat>
Last edited by Golden Child; 10/09/23 12:55 PM.
|
|
|
|
Joined: Feb 2014
Posts: 1,075 Likes: 151
Very Senior Member
|
OP
Very Senior Member
Joined: Feb 2014
Posts: 1,075 Likes: 151 |
So I thought, what if the macro could reference other macros so you could do nested macros?
<define name="button0back">
<rect><color red="0.0" blue="0.0" green="0.0"/><bounds left="0.00" right="1.00" top="0.50" bottom="1.00" /></rect>
</define>
<define name="button0rect0">
<rect state="0"><macro name="color0"/><color red="~col0r~" green="~col0g~" blue="~col0b~"/><bounds left="0.05" right="0.95" top="0.55" bottom="0.95" /></rect>
</define>
<define name="button0text0">
<text state="0" string="~str1~"><macro name="color1"/><color red="~col1r~" green="~col1g~" blue="~col1b~"/>
<bounds left="0.0" right="1.0" top="0.0" bottom="0.55" /></text>
</define>
<define name="button0rect1">
<rect state="1"><macro name="color2"/><color red="~col2r~" green="~col2g~" blue="~col2b~"/><bounds left="0.05" right="0.95" top="0.55" bottom="0.95" /></rect>
</define>
<define name="button0text1">
<text state="1" string="~str1~"><macro name="color3"/><color red="~col3r~" green="~col3g~" blue="~col3b~"/>
<bounds left="0.0" right="1.0" top="0.0" bottom="0.55" /></text>
</define>
<define name="button0inside">
<param name="str1" value="~str0~"/>
<element name="key~ref~ref">
<macro name="button0back"/>
<macro name="button0rect0"/>
<macro name="button0text0"/>
<macro name="button0rect1"/>
<macro name="button0text1"/>
</element>
</define>
<define name="button0">
<macro name="button0inside"/>
</define>
<param name="str0" value="<,UP,>,STOP,GO,1,2,3,4"/>
<param name="ref" value="left,fwd,right,stop,go,1,2,3,4"/>
<repeat count="9">
<macro name="button0"/>
</repeat>
It was a nightmare to get access to the macro table for the different constructors and having the pass in the macromap. I've never seen so many cpp errors until I got it right. Note that I wanted to be able to embed macros like the one for <macro name="color0"/> but I have to add macro parsing for the inner components. I don't know if that's worth it to do it, but for now it works for an "entire element component" but not inside the component. And then this got me to thinking, what if you used that system to add in your own tags? So instead of calling <macro name="button0"/> you could just do <button0/> and for any tag that wasn't found as a "regular tag" it would look up the macro.
|
|
|
|
Joined: Feb 2014
Posts: 1,075 Likes: 151
Very Senior Member
|
OP
Very Senior Member
Joined: Feb 2014
Posts: 1,075 Likes: 151 |
I was working with parameters and wanted to see a version where you could do the parameters across instead of down. If you could define the parameters with setting multiple values for the name, then you can do something like:
<param
name=
"aref, atag,amask,ax,ay,awidth,aheight" value="
keyproref, IN.0, 0x2, 6, 0, 12, 8,
keygoref, IN.1, 0x8, 30, 0, 8, 8,
keyleftref, IN.1, 0x1, 0, 10, 8, 8,
keyfwdref, IN.1, 0x2, 10, 10, 8, 8,
keyrightref, IN.1, 0x4, 20, 10, 8, 8,
keystopref, IN.2, 0x8, 30, 10, 8, 8,
key1ref, IN.3, 0x2, 0, 20, 8, 8,
key2ref, IN.3, 0x8, 10, 20, 8, 8,
key3ref, IN.4, 0x2, 20, 20, 8, 8,
key4ref, IN.4, 0x8, 30, 20, 8, 8"/>
So it would be easy to parse this to be handled the same as:
<param name="aref" value="keyproref,keygoref,keyleftref,keyfwdref,keyrightref,keystopref,key1ref,key2ref,key3ref,key4ref"/>
<param name="atag" value="IN.0,IN.1,IN.1,IN.1,IN.1,IN.2,IN.3,IN.3,IN.4,IN.4"/>
<param name="amask" value="0x2,0x8,0x1,0x2,0x4,0x8,0x2,0x8,0x2,0x8"/>
<param name="ax" value="6,30,0,10,20,30,0,10,20,30"/>
<param name="ay" value="0,0,10,10,10,10,20,20,20,20"/>
<param name="awidth" value="12,8,8,8,8,8,8,8,8,8"/>
<param name="aheight" value="8"/>
Whitespace like returns and tabs get converted by xml into a space, so all we have to do is strip leading spaces.
|
|
|
|
Joined: Feb 2014
Posts: 1,075 Likes: 151
Very Senior Member
|
OP
Very Senior Member
Joined: Feb 2014
Posts: 1,075 Likes: 151 |
Ok, managed to get processing a "multi-param" param tag, converting it into multiple single param tags. String handling in cpp isn't easy. I wanted to make a std::vector<util::ovectorstream>myvector but was having trouble setting the size dynamically. I couldn't seem to use myvector.emplace_back() or myvector.resize(). You cpp wizards would probably know how to make it work, but I couldn't. I was able to get a constant size to work like std::vector<util::ovectorstream>myvector(10); and then just gave it the count of items needed std::vector<util::ovectorstream>myvector(paramcount); So now it's able to parse the param tag:
PARAM 0 = aref
PARAM 1 = atag
PARAM 2 = amask
PARAM 3 = ax
PARAM 4 = ay
PARAM 5 = awidth
PARAM 6 = aheight
PARAM count =7
param 0 = keyproref
param 1 = IN.0
param 2 = 0x2
param 3 = 6
param 4 = 0
param 5 = 12
param 6 = 8
param 0 = keygoref
param 1 = IN.1
param 2 = 0x8
param 3 = 30
param 4 = 0
param 5 = 8
param 6 = 8
...
|
|
|
|
Joined: Feb 2014
Posts: 1,075 Likes: 151
Very Senior Member
|
OP
Very Senior Member
Joined: Feb 2014
Posts: 1,075 Likes: 151 |
I was going great guns until I realized that some tags get processed and expanded multiple times.
layout_view_item::layout_view_item( view_environment &env, util::xml::data_node const &itemnode, element_map &elemmap, emu::render::detail::macro_map ¯omap, int orientation, layout_group::transform const &trans, render_color const &color) : m_element(find_element(env, itemnode, elemmap)) , m_output(env.device(), std::string(env.get_attribute_string(itemnode, "name"))) ... , m_have_output(!env.get_attribute_string(itemnode, "name").empty())
so every time it processes a layout_view_item it will call get_attribute_string on name twice, and if I advance my param item each time, then it gets advanced twice.
|
|
|
1 members (1 invisible),
413
guests, and
3
robots. |
Key:
Admin,
Global Mod,
Mod
|
|
Forums9
Topics9,308
Posts121,691
Members5,069
|
Most Online1,283 Dec 21st, 2022
|
|
These forums are sponsored by Superior Solitaire, an ad-free card game collection for macOS and iOS. Download it today!
|
|
|
|