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!