Cooking with fire!

It’s been quite a while since I wrote a blog entry here – and it was for good reason; I thought it better to reserve this blog for times when there were real and significant updates to the development of the HaxeUI2 framework. Also I’ve been spending many a night writing the framework rather than writing about the framework. With that in mind this will be a fairly long post as there is quite a bit to catch up with! 

First I want to talk a little bit about “composites” and “native” components and what they mean inside the framework. A “composite” component is simply a component that is made entirely of other HaxeUI components – This is the default for everything.

To put that into perspective take the example of a button. By default, in HaxeUI a button is a component that can contain two other components. One is a Label component and the other is an Image component. As you can probably guess these make up the text and the icon of the button. In a similar fashion a composite vertical scrollbar (or any composite scrollbar) is a component with 3 sub components; one for the up and down buttons and another for the thumb. So in essence a scrollbar is a composite of composites. This is the default for all components in HaxeUI, unless explicitly specified (mentioned next) HaxeUI will assume a component is a composite.

Native components are different (of course!); a native component is one where HaxeUI takes no responsibility of the layout or the internals of the component, in fact it doesn’t have any idea about it. It simply says “I would like a native button of this size” delegating its creation to the backend, its then up to the backend to deliver that. For things like openfl, kha, luxe – all the custom drawn backends - they simply ignore this; in fact, since they haven’t got a “native” section setup in their configuration HaxeUI is smart enough to not even bother to try and ask.

So how does this get set? Well, it’s extremely simple its part of the styles: “native: true/false”. The reason I decided to go with the styles is because then you get the benefits of the style engine. Like only applying it to a certain type of component, or a single one. In fact, the new “native” theme is basically: 

* {
    native: true;
}

Another thing that makes it useful to be a style property is when you consider something like HTML5 and the DOM. HTML5 has quite a lot of “native” components ready for use; some I actually had no idea existed! So we want to be able to use them when you specify “native” in the styles. However, there are other things that HTML5 obviously doesn’t support, so for those you would want standard HaxeUI composites.

For example, take buttons and tabs. You might want buttons to be native, this way they match the look of the host OS, however, HTML5 doesn’t come with tabs (of course) and that is a composite of buttons! Solution? Simple: the main native theme defines all components as native, the html5 backend then “hijacks” that and adds its own bits to it:

.tabbar-button {
    native: false;
}

And that’s that, you now get native OS looking buttons but the tabs of the default theme! 

Another example would be sliders. HTML5 does support sliders natively, but they are far from functional. They are a little ugly (on windows especially) and cant handle ranges etc. So, lets say you want all the native “goodness” of html5 native components, but not the rubbish sliders. No probs: 

.slider {
    native: false;
}

And that’s it! The slider will now inherit whatever styles your chosen theme uses and since it will be a HaxeUI composite you have ranges, animations, etc, etc. Ive come to call these type of things “hybrids UIs”, that is a mix between native and composite controls. At first I thought it would just be “cool” but after a little fore-thought I realised its actually essential – as for example highlighted by the tabs in HTML5 – no hybrid UIs would have meant no tabs, or at least no useable tabs, for the HTML5 target. 

So now a little bit of information about implementation. Its achieved quite simply really, a backend can configure a “native” section in its config file. Some examples would be:

<native>
    <component id="haxe.ui.components.Button" class="haxe.ui.html5.native.NativeElement" nodeType="button">
        <behaviour id="text" class="haxe.ui.html5.native.behaviours.SpanText" style="margin-top:-2px;" />
        <behaviour id="icon" class="haxe.ui.html5.native.behaviours.ElementImage" />
        <layout class="haxe.ui.html5.native.layouts.ButtonLayout" />
    </component>
    <component id="haxe.ui.components.OptionBox" class="haxe.ui.html5.native.LabeledInputElement" type="radio">
        <behaviour id="text" class="haxe.ui.html5.native.behaviours.SpanText" index="last" />
        <behaviour id="selected" class="haxe.ui.html5.native.behaviours.ElementAttribute" child="input" name="checked" removeIfNegative="true" />
        <size class="haxe.ui.html5.native.size.TextSize" incrementWidthBy="20" incrementHeightBy="0" />
    </component>
    <component id="haxe.ui.components.VSlider" class="haxe.ui.html5.native.NativeElement" nodeType="input" type="range" style="-webkit-appearance: slider-vertical;-moz-orient: vertical;" orient="vertical">
        <behaviour id="min" class="haxe.ui.html5.native.behaviours.ElementAttribute" name="min" />
        <behaviour id="max" class="haxe.ui.html5.native.behaviours.ElementAttribute" name="max" />
        <behaviour id="pos" class="haxe.ui.html5.native.behaviours.ElementAttribute" name="value" />
        <size class="haxe.ui.html5.native.size.ComponentSize" defaultWidth="20" defaultHeight="140" />
    </component>
</native>

If the components’ native style is true and an entry is found in this section then certain information is gathered from here. Namely the component class, the behaviours and the size. The component class is actually only used by the backend (html5 in this case). The backend creates an instance of this class and uses it to create a DOM element. At first I was actually using things like “NativeButton”, “NativeCheckbox”, etc but eventually realised they were pretty much all doing the same thing.

The “behaviours” are as you would expect. For composite controls they come with a default set of behaviours. These all have “get” and “set” functions that can be overridden. So, for example in the case of a button when you call “.text” it now calls a “behaviour”, the default (composite) behaviour is to create a label (if its not there) and set its text. The overridden behaviour (for native) is to create a <span> element inside the <button> element and set its node value. The same goes for icons, slider values, everything.

The <size> property is needed for the native component to be able to report back to the HaxeUI core what its size is, as HaxeUI doesn’t know what that would be if its autosized.

Finally, with the <layout> is possible to create an entire layout that will be used for the component. Its not normally needed, <size> should really be sufficient as layout is usually handled by the native component, but there can be exceptions. The button is another good example, it uses a custom layout to position the text and image inside the component (they actually just changed the node order and add some css). There would be many ways of doing this of course, but taking advantage of the already existing layout and invalidation sequence kills two birds with one stone.

And that’s that! Hybrid and native UIs in HaxeUI2! Its probably worth mentioning that I have only been speaking about haxeui-html5 during this post. The reason is because it’s the only one that currently supports native components. However, working on it and making it work has totally opened haxeui-core to behaviours, composites, hybrids, delegated sizes and layouts, etc. This has totally paved the way for the upcoming hxWidgets backend which will allow fully native desktop applications using much of the same techniques described above. In fact, I suspect that because of the hybrid nature of the haxeui-html5 backend and the relative ease of using the hxWidgets lib the new haxeui-hxwidgets backend will be extremely simple to write and be much quicker than the HTML5 counterpart, especially since all the leg work has been done already!

Ive also updated the demos on the v2 section of the site with a new “HTML5 – Hybrid” demo! Sorry for such a long post and enjoy! 

Animations in HaxeUI2

Animations in version 1 of HaxeUI were a little bolted on as an afterthought, and therefore didn’t really work too well and weren’t really customizable. You could set a “type” of animation (slide, none, fade for example), but then it was up to the component whether it took any notice of it. More over if you wanted to change the animation you would have had to subclass the component or edit the code directly – all in all not a good sub system!

I’m not a massive fan of UI animations, I think they commonly get overused and have a detrimental effect on performance and the “professional feel” of a serious application. That said, I do think they have their uses, especially for applications that are supposed to be a little “funky” and not all about the raw data the app is presenting. Also, when done right I think they can increase the user engagement and generally leave a good taste in the users mouth about the app (we’ve all used some pointless app before but been sort of impressed by how the UX felt rather than what it actually did I’m sure).

The new animation sub system in HaxeUI2 is pretty flexible, first of all, here’s some mark-up:

<animation>
    <keyframe time="0">
        <button1 left="0" top="0" />
        <button2 left="100" top="0" />
        <button3 left="100" top="100" />
        <button4 left="0" top="100" />
    </keyframe>
    <keyframe time="500">
        <button1 left="100" top="0" />
        <button2 left="100" top="100" />
        <button3 left="0" top="100" />
        <button4 left="0" top="0" />
    </keyframe>
    <keyframe time="1000">
        <button1 left="100" top="100" />
        <button2 left="0" top="100" />
        <button3 left="0" top="0" />
        <button4 left="100" top="0" />
    </keyframe>
    <keyframe time="1500">
        <button1 left="0" top="100" />
        <button2 left="0" top="0" />
        <button3 left="100" top="0" />
        <button4 left="100" top="100" />
    </keyframe>
    <keyframe time="2000">
        <button1 left="0" top="0" />
        <button2 left="100" top="0" />
        <button3 left="100" top="100" />
        <button4 left="0" top="100" />
    </keyframe>
</animation>

As usual the mark up is just a shortcut for a macro that builds the code for you, you could certainly create all the key frames, properties, component refs, etc yourself in code. It’s not hard at all, but the mark-up is more re-useable. I toyed with implementing a CSS type animation framework, but I’ve always found it pretty unintuitive. I think the “key frame” approach is pretty simple. To run that previous animation, you simply need:

    animation.setComponent("button1", _button1);
    animation.setComponent("button2", _button2);
    animation.setComponent("button3", _button3);
    animation.setComponent("button4", _button4);
    animation.loop();

The result is this.

So that’s all fine, but I also wanted to be able to use this same sub system in the components themselves, so the module.xml can now define animations for components, an example of this from haxeui-core module.xml is:

	<properties>
		<property name="haxe.ui.components.hslider.animation.pos"
                    value="haxe.ui.components.animation.pos" />
		<property name="haxe.ui.components.hslider.animation.rangeStart"
                    value="haxe.ui.components.animation.rangeStart" />
		<property name="haxe.ui.components.hslider.animation.rangeEnd"
                    value="haxe.ui.components.animation.rangeEnd" />

		<property name="haxe.ui.components.vslider.animation.pos"
                    value="haxe.ui.components.animation.pos" />
		<property name="haxe.ui.components.vslider.animation.rangeStart"
                    value="haxe.ui.components.animation.rangeStart" />
		<property name="haxe.ui.components.vslider.animation.rangeEnd"
                    value="haxe.ui.components.animation.rangeEnd" />
		
		<property name="haxe.ui.components.hscroll.animation.pos"
                    value="haxe.ui.components.animation.pos" />
		
		<property name="haxe.ui.components.vscroll.animation.pos"
                    value="haxe.ui.components.animation.pos" />
	</properties>
	
	<animations>
		<animation id="haxe.ui.components.animation.pos" ease="Bounce.easeOut">
			<keyframe time="300">
				<target pos="{pos}" />
			</keyframe>
		</animation>
		<animation id="haxe.ui.components.animation.rangeStart" ease="Bounce.easeOut">
			<keyframe time="300">
				<target rangeStart="{rangeStart}" />
			</keyframe>
		</animation>
		<animation id="haxe.ui.components.animation.rangeEnd" ease="Bounce.easeOut">
			<keyframe time="300">
				<target rangeEnd="{rangeEnd}" />
			</keyframe>
		</animation>
	</animations>

Putting this in the module.xml means that other modules (ie, an app, a lib, etc) can easily override them to achieve different effects, by either creating a new animation with the same id, or by overwriting the property of a specific component class. It also means you have a central place to store animations should you wish to use it – you, of course, don’t need to – you could load them from somewhere else or create them in code. You can also override each component instances’ animation with a simple call to Component::setClassProperty(…) should you wish a non-standard animation for a single component.

You can see the component animations in action here by clicking on the background of the hslider in order to make it jump to a value, previously (without animations) it would simply set the value directly. The components on that page are also bound to each other, so changing one value results in changing most of them, this is also honoured with animations of course. For completness, and to show how easy it is to add animations to components, the code that runs the animation when you click the sliders background (and thus set its value) is:

AnimationManager.instance.run(getClassProperty("animation.pos"),
                              ["target" => this],
                              ["pos" => value]);

Finally, Iive also updated all the demos on http://haxeui.org/v2 to use these system wide animations, but I should probably note that ive used a “bounce” animation and that I have no intention of actually using this in the final released version of HaxeUI2. Im not sure if the default will have animations on or off yet (im leaning towards “off” with an ability to set groups of properties easily to turn them “on”) 

A note about configuration

The first version of HaxeUI uses xml to configure everything. I should probably note that i think XML is a great mark up language - I've aways liked it, its inherent hierarchy and self descriptive manner - to me its clean simple and pretty robust.

That said, I know that isnt the case for everyone, and in an effort to make HaxeUI version 2 as helpful as possible Ive decided to allow configuration and UI creation to be flexible. To that end the various parts of HaxeUI2 that allow configuration or markup Ive made flexible. Currently there is support for the three "big ones": XML, JSON and YAML, but there is scope of course to dynamically create your own. So for example, it would be possible to create your own parsing adapter and allow you to build HaxeUI2 user interfaces from some type of custom markup in pretty much any format you want.

Below are some examples that are functionally identical:

  • Backends - this configuration is used to confugure the backend and framework HaxeUI2 will work with
  • Modules - modules define components, themes (and theme extensions) and resources that will be compiled into the application as haxe resources (note: haxeui-core is itself a module)
  • UI Definitions - mark up that will be turned into a HaxeUI2 user interface

Its worth noting, that with a single exception, this change has had no effect on my personal opinion. I still think that XML is by far the clearest and simplest method here. The single exception is YAML, though i feel its cumbersome for UI markup, it is nice and clear for module configuration. However, many may disagree with me, and thats fine, you can use the markup of your preference with zero impact (everything ends up as a typed haxe object anyway).