Okay so I’ve been working with Tonegodgui for a few months, and have just completed porting my old Nifty based UI to Tonegod. It was quite satisfying clicking on the button to remove the Nifty library (nothing against Nifty of course, I am just happy the porting is done with no loss of functionality). I am now past the point of no return
On the whole, I am very pleased with the result. My UI looks nicer, my code is WAY saner and the only XML in sight is the style map (this makes me very happy). I love the fact it is native to JME and properly part of the scene, with all the goodies that comes with.
In the course of porting I have built up a little collection of custom controls, observations, and one fairly major customisation that may just be of use to others. Basically, a Swing style “Layout Manager” system.
I see reading through the forums, that comments have been made about one of the disadvantages is the absolute positioning. I know about the Docking/Resize attributes, but these are not really flexible enough for my needs, and I think such functionality is better delegated to something separate from the components themselves.
After spending so many years using Swing and other toolkits that use layout managers, this approach is very natural to me and so I decided to try and apply the same to Tonegodgui. Now I’ve completed the porting of my game UI, I have a fairly reasonable working layout management system bolted on top of Tonegodgui Some basic points about how I implemented this …
Each existing control has a counterpart “LayoutAwareSomeControlName”. Most of these extend the core control and add layout features. You can sort of mix controls, but in most cases it’s best to either use LayoutAware controls in LayoutAware contains, or not at all. Note, I have converted MOST, not all components (i.e. the ones I actually use ;).
No changes to Tonegodgui core. Well, unfortunately there was 1 change I couldn’t avoid, more on this later. To achieve this there were a load of places where I had to resort to nasty hacks and reflection to get at some internals of Tonegodgui. All of this could have been avoided if I had modified the base source, but I wanted to treat my layout management code as something separate, as I didn’t know how much the core library would change.
All container like controls (e.g. LayoutAwarePanel) have a layout manager that may be set on them. addChild() is overidden so that each time a child is added the container laid out again (and also a “addChild(Element, Object)” where the 2nd argument is for layout manager specific constraints). This is a bit wasteful, and I will eventually find a better solution. Layout may of course also be invoked manually. When an element is laid out, all of it’s children are laid out to (by invoking their own layout managers when used).
Most compound controls (e.g. LayoutAwareCheckBox) also have a specific default layout manager (although it may be changed). In fact Checkbox is an odd one. It’s bounds are the bounds of the Check Icon, not the entire component. So setting the bounds on CheckBox doesn’t act as you’d expect it. It was for this reason I added component specific layout managers.
All components have the concept of min/max/preferred sizes. For the most part, preferred size is calculated by default. For example, Labels give a preferred size calculated from their text (a buttons width does the same). Containers take on the total preferred size of their child components (using their set layout manager). There are setters and getters added for each of these 3 sizes that may be used to override per element. Layout managers uses these sizes as hints and usually respect all 3 values, although ultimately it will calculate the components geometry as it sees fit.
Windows can be pack()'ed, i.e. all the components added and the window size determined automatically. One caveat here is text elements, who by default want to span a single line and so larger text will have a large preferred width. Combat this by setting a preferred size and only the height will be adjusted so all text fits.
I have written a number of layout managers, mostly similar to Swing. This includes the completely awesome MigLayout (http://www.miglayout.com/) which conveniently is toolkit agnostic, so was pretty easy to add it as all the hard actual layout work is done by the library. This is the layout manager I use the most as it covers an awful lot of my layout needs. Other provided managers include FlowLayout (layout left to right in a row, or top to bottom), BorderLayout (N,S,E,W or center), FillLayout (all children overlaid at full container dimensions), GridLayout (fixed size grid, all children equal sizes), Basic (standard absolute positions and sizes).
All LayoutAware constructors (except Window type ones) have their position arguments removed, and for the most part you shouldn’t use dimensions either, although the argument is still there.
All of this has meant I pretty much don’t worry about positioning AT ALL (well, except for root windows), and mostly sizing is taken care of. For example, the follow is all that’s need to have a form where the first two rows consist of 2 cells, the first being a label that grows to take up extra space, and a single button on the final row that spans the whole width of the contain (think a typical login screen).
[java]LayoutAwarePanel window = new LayoutAwarePanel(screen, new Vector2f(100, 100));
// Set layout. 2 cols, 3 rows. insets of 10 pixels around whole panel, gap of 4 between elements.
window.setLayoutManager(new MigLayout(screen, “gap 4, ins 10, wrap 2”, “[fill, grow]”, “”);
// Row 1
LayoutAwareLabel l1 = new LayoutAwareLabel(screen);
LayoutAwareLabel v1 = new LayoutAwareLabel(screen);
// Row 2
LayoutAwareLabel l2 = new LayoutAwareLabel(screen);
LayoutAwareLabel v2 = new LayoutAwareLabel(screen);
// Row 3
LayoutAwareButton b1 = new LayoutAwareButton(screen);
window.addChild(l2, “span 2”);
Soo … my question is, is there any interest in this code? If so, I can extract it out from my project and make it available somehow. This should be a pretty quick task. Turning it into a patch against the base Tonegodgui source would take a bit more time, but is where I am likely to go with this (for my own purposes).
As noted above, there are a couple of tweaks needs to the base Tonegodgui code to make this work properly. The main one is that in Element.java, the “elementChildren” member uses a HashMap. The problem with this, is if you hide elements, and make them visible again, the order in which they were added is effectively lost. Layout Managers rely on the order of the components when setting bounds, so to combat this I had to change elementChildren to be a LinkedHashMap instead. I don’t think that has any other adverse effects.