Friday, July 10, 2009

UiXML

A year ago this week, the Metaplace folks flew four alpha testers, myself included, down to San Diego to meet the team and get a little insight into what was coming. It was a fantastic experience, very much appreciated, not only to meet the team (and the other three testers -- Chooseareality, Rboehme, and Scopique) but to see where they work (though they've moved since then) and how they worked (in which we got to participate). We also got a sneak-peak at a new feature that was released shortly after that, which was the UiXml() system.

The idea of this system is that instead of calling lots of UI API functions, you can create an XML segment, in a string, that will be passed to a single UiXml() function and will generate all of your UI elements for you. So, instead of typing

local outerrect=UiRect(0, "outer rect", 10, 10, 220, 140)
UiColor(outerrect, 125, 2, 2, 0.9)
local innerrect=UiRect(outerrect, "inner rect", 1, 1, 218, 138)
UiColor(innerrect, 50, 130, 130, 0.8)
local label=UiLabel(innerrect, "name", 1, 1, "label text")
UiColor(label, 255, 234, 0, 1)
UiAttachUser(self,outerrect)

you can type

local rectxml=[[
<rect name="outer rect" x="10" y="10" width="220" height="140" red="125" green="2" blue="2" alpha="0.9">
<rect name="inner rect" x="1" y="1" width="218" height="138" red="50" green="130" blue="130" alpha="0.8">
<label name="name" x="1" y="1" text="label text" red="255" green="234" blue="0" alpha="1"/>
</rect>
</rect>
]]
UiAttachUser(self,UiXml(rectxml))

What does this gain you? Quite a few things, actually. As you can see, you can set the colour of a UI element at the same time as defining it, instead of as a separate command. Not really a biggie. But the above rectxml string doesn't have to be created all in one go, and THAT makes things powerful.

For instance, you could have a loop that builds up the string, based on ... well, based on whatever: the number of entries in a table, whether a checkbox is or isn't checked, or the user's name. But, to be honest, you could do the same thing with if-, while- or for-statements and the standard Ui*() functions.

You could have objects which themselves know how to render the pertinent UI elements, and each might just have a property, "myui", which you can read at any time to insert into a block of the XML. But, of course, you could have a function or Trigger on an object which calls the related Ui*() functions.

Okay, you could call an external web service, which would supply the UI XML to render whateveritis that that web service wants you to render -- instead of having to fetch some text or JSON data or XML and then process it in your world, wouldn't it be nice if that service knew how to "talk UiXml()", and could give you a window, with buttons, and sliders, and textboxes, all laying out the data? Yes, even THIS could be done with Ui*() functions, but we're seeing some usefulness...

XML is processable; with an appropriate XML/XSLT library, or hell, even some interesting use of string.gsub(), I can change all of my UiRect()s to UiOval()s; decrease all of my red colours by 10; or widen all of my elements by 10%. Yes, some of these could be variables that get modified, and others could be done with if-chains or lookup tables, but I still say processing a string is easier...


Still not convinced?

UiXML supports "layouts"; these are basically containers for arranging UI elements (similar to Java Layouts if you know those). From the wiki:

<layout style="grid" grid_x="2" grid_y="1" padding_x="1" padding_y="2">
<image name="itemimage" width="32" height="32" spriteId="0:2"></image>
<rect width="68">
<layout style="grid" grid_x="1" grid_y="2" padding_x="0" padding_y="0">
<label name="itemname" text="Name" red="0" green="0" blue="0" ></label>
<label name="itemqty" text="Qty" red="0" green="0" blue="0"></label>
</layout>
</rect>
<layout>

See the grid_x and grid_y? This set up the organization of the elements as they were added. They even nested, so inside one of the grid elements was another layout. Oh, but there's a UiLayout() function (I bet I'm the only one who has ever used it).

Fine. How about "components"? Again, from the wiki:

component = [[
<ui xmlns:mp='http://www.metaplace.com/schema/ui'>
<define_component name="data_field">
<rect name="back" width="100" height="20" red="131" green="131" blue="131" expand="true">
<layout style="grid" grid_x="2" grid_y="1" padding_x="1" padding_y="1">
<label name="data_name" text="name:" width="100" />
<text_field name="data_value" command=" " text="value" red="0" green="0" blue="0"/>
</layout>
</rect>
</define_component>
</ui>
]]

-- Add this XML to the UiXml stack
UiXml(component)


Using A Component:
myUI=[[
<ui xmlns:mp='http://www.metaplace.com/schema/ui'>
<window name="item_detail" x="10" y="10" width="400" height="140" style="20">
<layout style="grid" grid_x="2" grid_y="2" padding_x="1" padding_y="5">
<component type="data_field" name="df1" height="20" width="200"/>
<component type="data_field" name="df2" height="20" width="200"/>
<component type="data_field" name="df3" height="20" width="200"/>
<component type="data_field" name="df4" height="20" width="200"/>
</layout>
</window>
</ui>
]]
UiXml(myUI)

Basically, components are "templates" for UI layout, allowing you to group together multiple UI items into a single conceptual block, and then reuse them as often as you like. This example provides a rectangle with a label and a textfield as a nice convenient unit that can be placed anywhere with a single line. Note how the height and width can be supplied afterwards, leaving some variability to the component's configuration.

In fact, you could go even further with the <override/> tag:

<component type="data_field" height="20">
<override target="data_name" text="Strength:"/>
<override target="data_value" text="100"/>
</component>

Fantastic! Customized label/field pairs, at your fingertips. Sure, I could write a function that provided this functionality -- the function could take any number of parameters that it would then use to make a specific component, and return a handle... which do you think is easier to use?

Okay, if you're still not convinced about the power of UiXML over regular UI function calls, Data Binding will win you over. Data binding allows you to specify an alternate source to a constant for filling in values. So instead of specifying a static value of "100" for an element's colour,

<rect name="red rect" x="0" y="0" width="100" height="100" red="100" blue="0" green="0"/>

we can use a constant in our script:

<rect name="red rect" x="0" y="0" width="100" height="100" red="{RED_VALUE}" blue="0" green="0"/>

Or even something more advanced:

<rect name="red rect" x="0" y="0" width="100" height="100" red="{stylesheet.places["0:0"].red_value}" blue="0" green="0"/>

Anything you can imagine that you can code, could be put into that { } definition. (This has the unfortunate distinction, however, of being one of the few ways to hide malicious code -- a future blog post). I hope you can get an idea of how powerful that is. Sure, whatever code you put in there could also be run separately, and then passed to a Ui*() function as a parameter, but this string of code itself can be generated dynamically - which is why it can also be dangerous.

There are also other types of data binding. Use "% %" for values passed in during the UiXml() call, such as

<ui xmlns:mp='http://www.metaplace.com/schema/ui' scriptId='0:8'>
<component type="data" height="20">
<override target="data_name" text="Strength:"/>
<override target="data_value" text="%foo%"/>
</component>
</ui>

values = {foo='a value here'}
winId = UiXml(xml, parentWindowId, values)

And even better, use "# #" to pull data from object properties:

<ui xmlns:mp='http://www.metaplace.com/schema/ui'>
<component type="data" height="20">
<override target="data_name" text="Health:"/>
<override target="data_value" text="#health#"/>
</component>
<component type="data" height="20">
<override target="data_name" text="Mana:"/>
<override target="data_value" text="#mana#"/>
</component>
</ui>

winId = UiXml(xml)
UiBindObject(winId, self)

the only thing that would make the above better is if the #values# automatically updated any time the properties changed; as it stands, you have to call UiBindObject() every so often to have the changes reflected.


So, have I convinced you that UiXml() is THE way to do UI in Metaplace? I hope not. That's right, I hope not, because... they just removed it.


Apparently I was the only person using it, and instead of letting me go on doing so, it got removed. I agree, it had some problems - some of the later UI elements, such as sliders, weren't supported, and you couldn't set the text on certain items from within the XML - but it also worked well for the parts that did work. Even more stinging is the fact that a new system, UI Styles, was introduced as a replacement to UI XML. It's not the same thing at all (though it's an interesting system in its own right, one that I may blog about).

So why did I blog about it at all? Well, I have a Google Doc which is a long list of future blog posts, and that was on it from before they stole my baby from me. And, to be honest, I had already started working on a replacement version which DOES support sliders, and text initialization (but which, can't do the cool things such as the data binding) before it was taken away, so those who might want this functionality can have it once I publish my version. And, perhaps, just perhaps, all of the bitching I do about this (and other things) will get others to bitch (about this, and other things), and my beloved UiXml() might be returned to me.

Hey, stop laughing. I can dream.

No comments:

Post a Comment