Page 1 of 4

Automatic Layouting (horizontal, vertical, grid, ...)

Posted: Tue Jul 27, 2010 20:44
by Kulik
I need some sort of automatic layouting container like the ones in Qt, Java, gtk, etc... I can't find it in CEGUI itself, I could have missed something but I think it isn't there.

Why do I need it:
I am creating an ingame material editor with instant preview for our Ogre based engine for artists to play with (they hate text editing, lazy bastards). And Ogre materials are pretty complex, you have the material with some basic properties, then techniques, inside techniques there are passes and inside passes there are texture units... Hope I haven't missed anything :lol: My idea is that I will have collapsible boxes for each layer of complexity, so you can show/hide techniques, passes inside them, etc... This should allow me to fit a material to one FrameWindow that won't overlay the whole screen.
Going around this by popping up a new FrameWindow each time (one window for material, you click on any Technique and another window just for the technique is created, etc) would really hurt usability (plus I need to make the artists believe that materials are easy so I don't have to do them :lol: )

It could be implemented as a descendant of CEGUI::Window. If I created something like CEGUI::LayoutingContainer and derived CEGUI::VerticalLayoutingContainer and CEGUI::HorizontalLayoutingContainer from it. The container would subscribe to EventSized of every new window that is added to it (via addChild_impl and removeChild_impl). For the sake of simplicity, the layouting will only alter positions of widgets inside it, handling sizing and rotation could be implemented later.

Possible problems:
1) where do I store margins for each CEGUI::Window inside a layouting container?
2) where do I store the "position" of the window inside the layout? what comes first, what comes last. CEGUI stores child windows inside a vector, this could get messy when rearranging windows inside a container.
3) performance? I am planning to do an unclever approach, I will get all child windows, sort them (for vertical layout, top most window comes first) and then move from the first to the last, altering each windows position so that they fit next to each other.
4) the name clashes with .layout files and CELayoutEditor

I could solve problem 1 and 2 by creating something like CEGUI::LayoutingItem that would store margin of it's inside widget and it would store something like weight to represent "position" inside the layout. But this is another layer of complexity.

I found an old topic but there is no recent activity there, so instead of ressurecting, I created my own -

Could somebody provide some insights/ideas? Would like a little discussion before I start the patch. I think this feature would really push CEGUI forward, because it would allow very complex and dynamic UIs with less effort.

Re: Automatic Layouting (horizontal, vertical, grid, ...)

Posted: Tue Jul 27, 2010 20:53
by Kulik
hmm, I was being blind and posted this in a wrong section :-/ please don't stone me :lol:

moderators: please move to CEGUI Library Development Discussion.

Re: Automatic Layouting (horizontal, vertical, grid, ...)

Posted: Thu Jul 29, 2010 09:09
by CrazyEddie
Yeah this is basically a TODO for the next version: ;)

For implementation, I don't have many specific ideas at the moment. For storing information about margins or what have you, without extending the Window interface, there are two real options. One is to store that information with the child window by way of the named user strings (which is exactly how we implement PropertyDefinition in Falagard). Another way is to maintain a collection of lightweight structs in the LayoutContainer (I would drop the -ing there, btw), rather that wrapping the window. Each of these approaches has good and bad points, the main bad point on the named string approach is that it feels like a kludge (and it would be ;)).

I guess a question there has to be do we want margins and such to be per-window and apply to that window whichever container they get added to, or set on the LayoutContainer and apply to whatever window(s) get added to the container? If it's the former, then I think there should be settings and properties added directly to Window to support those preferences rather than kludging it some other way. If the setting is at the container level, then maintaining that information locally in the container is the way to go.

The order / sequence of windows on the parent is actually held in two places. One is a drawing order and is somewhat volatile, the other is a vector based on the order of addition, this list is stable, and I'd be inclined to use that - i.e. windows are laid out in the sequence that they were added.

As regards to performance, I think it's not something to worry about too much. The (re)layout of content should/would only happen in response to certain criteria (child added, removed or resized and container resized), so unless it becomes a problem in reality, it's best not to worry about that now (IMO).

The name clash is a non-issue for me. That's basically the right name for the classes (except without the -ing). So, LayoutContainer, VerticalLayoutContainer, HorizontalLayoutContainer and GridLayoutContainer, or what have you.

Hope this helps, I'm happy to discuss further and/or answer specific questions.


Re: Automatic Layouting (horizontal, vertical, grid, ...)

Posted: Thu Jul 29, 2010 09:25
by Kulik
Oops, missed the ticket.

Yeah, I added the -ing to differentiate from .layout files :D It saves fingers to drop it.

The other problem that I have is relative sizes. Suppose I have a FrameWindow with relative size 1, 1 and VerticalLayoutContainer inside it. The layout container should be position less. It should "inherit" this from it's parent (at leasts thats how it works in other libraries I worked with). So now I add several Windows to the layout container and resize the container if they don't fit (to allow scrolling and what not). If they have absolute sizes, it's not a problem. If they have relative sizes, I lay them so that they fit but that may cause increasing/decreasing the layout container's size which will cause the windows inside that have relative sizes to change size as well. I think allowing relative sizes is possible if they are relative to the parent of the layout container, not the container itself.
EDIT3: The relative sizes could be implemented by overriding getUnclippedInnerRect_impl in the LayoutContainer. This is just an idea, not tested.

Basically the only added property that I can think of is margin (left, right, top, bottom). I don't know if it is worth it to add it to CEGUI::Window class. That depends on how many users would use the layout containers. And the margins have to be per-window, having one margin per layout container is too limiting I think... Some libraries don't support margins but instead they have Spacers, thats basically transparent widgets that don't react to anything but take space. It's simpler implementation wise and allows more variations but probably a bit harder for user to grasp. I would probably go that way. Implementing a method that would add them automatically if margin > 0 would simplify it.
EDIT: Spacers wouldn't work in this case: Vertical Layout with indents separate for each row :-(
EDIT2: Perhaps something like std::map<CEGUI::Window*, CEGUI::LayoutParameters> inside LayoutContainer would be a way to go.

If I can somewhat sort the relative sizing problem, I can start to work on the patch. Vertical and Horizontal layouts should be fairly easy.

Re: Automatic Layouting (horizontal, vertical, grid, ...)

Posted: Thu Jul 29, 2010 11:54
by CrazyEddie
For the relative sizing thing, and Editt3 suggestion, look at the ScrollablePane/ScrolledContainer, because this encounters (and solves) the same issue and indeed does use those functions to return the parent rect area(s) instead of our own.

The scrolling case is somewhat complex. This will (and would however it was implemented) then mean the layout containers will need looknfeel definitions in order that the appropriate widget types - whether that be a ScrollablePane or Scrollbar widgets - can be specified. IMO as far as this goes layout widgets should just layout and not scroll - scrolling should be done by using the existing ScrollablePane. The precise way that this should be done, I'm not sure about at the moment, but I don't think having a dependency there is a good thing.

I imagine the layout containers would become used quite a lot. Though Edit2 is certainly a valid alternative approach.


Re: Automatic Layouting (horizontal, vertical, grid, ...)

Posted: Thu Jul 29, 2010 12:51
by Kulik
WOW, CEGUI is really nicely designed, because I've got a working Vertical Layout example!
It seems I don't have to take care of anything regarding relative/absolute sizes and positions, just return parent's rect and use operators of UDim and everything just works.

It needs some cleaning before I can submit a patch though.

I am now wondering how am I going to sort EventMoved events on child windows. I think it's user responsibility to not be stupid and not move windows that are layouted around... What do you think? Should I relayout when Window is moved inside a layout, practically setting it's position back?
EDIT: This would cause infinite loops :-( So either users must not be stupid or it has to be implemented differently...

Stay tuned 8)

Re: Automatic Layouting (horizontal, vertical, grid, ...)

Posted: Thu Jul 29, 2010 15:04
by Kulik
OK, this is the first demo: It's a FrameWindow with VerticalLayout that has FrameWindows inside and these also have a Vertical Layout that again has FrameWindows in them :D

I will implement HorizontalLayoutContainer and GridLayoutContainer and brush some rough edges...

btw: I currently relayout immediately when a child window gets resized. It all works, but I would like to just set something like d_needsLayouting = true and then before the any of the child windows of the container are drawn, I would relayout if necessary. This would also allow me to disallow movement of child windows inside the layout. What's the preferred way to do this?

Re: Automatic Layouting (horizontal, vertical, grid, ...)

Posted: Thu Jul 29, 2010 16:50
by Kulik
Hmm, I've hit a wall. Everything works suspiciously perfect until I place LayoutContainer inside a ScrollablePane. I've got a lot of clipping bugs, the scrolling basically moves the layout to the side but nothing new shows up, the rest is clipped.

I have overridden getUnclippedInnerRect_impl(void) const, virtual Rect getOuterRectClipper_impl() const and virtual Rect getInnerRectClipper_impl() const and for each I return the value that d_parent has (if d_parent == 0, I fallback to Window default implementation).

EDIT: It seems my parent Window (ScrolledContainer) is clipping me. No matter what Rect I return, I am always clipped the same way, even for Rect(0,0,1000,1000).
I will study how ScrollablePane works before starting GridLayoutContainer.

But I've finished HorizontalLayoutContainer and it works as expected.

Re: Automatic Layouting (horizontal, vertical, grid, ...)

Posted: Thu Jul 29, 2010 18:22
by CrazyEddie
Umm, ok. Let me see if I can cover most of these points - there will be some irrelevancies and overlap in these answers :)

Firstly, it's cool that you have things working so quickly!! The video looks good :)

With regards to the user moving the content, aside from infinite loops and such I would have said ignore the user doing that anyway, but provide a public function in order that the user may cause the immediate (re)layout of content (you probably already provided this anyway?!).

To perform the 'late' layout optimisation, you should be able to do that by overriding the Window::drawSelf function and doing the layout as needed in there (before calling the default implementation). I think it should be fine, there's an outside chance some things may have to be tweaked there - so let me know if that works ok ;)

With regards to ScrollablePane clipping issues, which version of the code are you working with? There are/were some clipping bugs with ScrollablePane though these were fixed (I can't remember off hand whether these were fixed in 0.7.1 or afterwards). The issue could be related to invalidation not happening for content that is texture backed (with AutoRenderingSurface set to True), you could disable that in the looknfeel to confirm that one way or the other. If it is that, let me know and I'll fix it!


Re: Automatic Layouting (horizontal, vertical, grid, ...)

Posted: Fri Jul 30, 2010 10:08
by Kulik
I am using 0.7.1 stable. I've read somewhere that trunk is unusable or something... Can't recall where. What branch should I work on?

Anyways, this is what it's doing I've done late layouting the way you suggested. I will do one more optimization for sequential layouts in the future, so that only windows that need to be moved are moved via layoutAfter(..) method. Or perhaps I could always relayout everything and set the position of windows only if it's different from the old one?

This is the code I have so far - (EDIT: patch against v-0-7 I still don't like the interface I made, any ideas? I'd like to make it as simple as possible to integrate it with existing tools, so Window::addChildWindow() works as expected and defaults to no margin. I will have to provide some means for rearranging the windows.

I will have to break this with grid though... There are practically two options.
1) layout->setGridWidth(3); and then add windows sequentially
2) layout->addChildWindow(window, params, gridX, gridY) - probably nicer to work with
3) a combination of both? if I do addChildWindow(window), it will just take the next free spot, if I specify gridX and gridY, it will go there and if there already is something, I will remove it first. This could make it easy to create "galleries" in code with very little effort.

EDIT: I've tried setUsingAutoRenderingSurface(false) on pretty much all windows involved and it didn't change a thing :-(

Re: Automatic Layouting (horizontal, vertical, grid, ...)

Posted: Fri Jul 30, 2010 13:46
by CrazyEddie
Well, trunk is generally considered unstable, not unusable :) Aside from that I recommend either a snapshot release (viewtopic.php?f=6&t=4704) or the branches/v0-7 code from subversion (preferred for patch generation reasons). I had a quick look at the svn logs and I think the fixes I mentioned were done for 0.7.1, so it seems there may be more to do there...

I have not looked at your code yet - but I will do tomorrow morning, so may make suggestions and such then :) I agree that the interface should be kept as uniform as possible - so as not to break existing tools and expectations. Overall I think the one thing last thing to consider (but having not looked at the code yet, though), is how to handle the margins and such in xml layout files. This was one (perhaps the only?!) plus point for having the margin be a setting on Window. I'll consider this issue some more once I've seen the code :)

With regards to the standard addChildWindow and margins, I think it would be a good idea to allow the default margin to be settable to some user preferred value (with an initial value of 0).

With regards to the grid, it's a tough call. I think perhaps option 3 also, since it offers the most flexibility.

But anyway, I'll get back to with any ideas and suggestions, and hopefully a fix for the scrollable pane thing tomorrow...


Re: Automatic Layouting (horizontal, vertical, grid, ...)

Posted: Fri Jul 30, 2010 20:41
by Kulik
I've read an announcement that said that trunk used to be "pretty stable" but it's not going to be any more, so I was afraid of using it. I've migrated my cegui sources to subversion access and I use branch v-0-7, so I can generate patches with ease now. (The clipping bug is still there :-( )

I think having Margin as a property would be the simplest most painless way to do it, because CELayoutEditor could then edit margins out of the box (just ini change to support the properties) and there is no need for additional layers of complexity in the layout containers. On the other hand, that means that each CEGUI::Window class will take more memory (it will need 4 more UDim for margin top, left, bottom and right). But handling margin in XML will be free! I'll wait for your opinion but I think this is the way to go. CELayoutEditor would definitely need a major modification to support margins as I have implemented them now.

As for default margin - you mean per layout container settable default? How would that be possible with margin properties? Some special UDim value would mean default?

With margin properties I wouldn't need to add a special addChildWindow methods either. Everything would fit into the interface that's there. You wouldn't even have to cast the layout container for most of the simple cases!

I agree with option 3 for the grid, I will see start it tomorrow. I will have to somehow support vertical and horizontal ordering. Probably some enum would work.
1 2 3 vs 1 3 5
4 5 6 vs 2 4 6


Re: Automatic Layouting (horizontal, vertical, grid, ...)

Posted: Sat Jul 31, 2010 12:44
by CrazyEddie
Oh, right, that announcement! I should probably have updated it to say that the period of major instability had passed (for now).

I'll sort out the clipping bug, you'll just have to ignore it for the moment :)

For the margins, having the extra state and properties for WIndow is a small price to pay in order to maintain uniformity with the existing approaches (everything would 'just work') - so my opinion on that is to go with the Window Margin properties.

With regards to what I said about a default margin, that would only have made sense without the Window Margin properties, so since you'll like go with the new properties, there's no need to worry about what I said before :)

I'll have a play with the work-in-progress patch in a little while :)


Re: Automatic Layouting (horizontal, vertical, grid, ...)

Posted: Sat Jul 31, 2010 16:42
by CrazyEddie
Ok, I have looked over the patch from earlier, and so far everything seems fine to me. I tried a few things out with combinations and such, and my general reaction was "awesome!" because everything I tried seemed to work with no hassles at all.

I did, of course, encounter the clipping issue when combining things with the ScrollablePane. I'm looking into that at the moment and will fix it as soon as I track it down.


Re: Automatic Layouting (horizontal, vertical, grid, ...)

Posted: Sat Jul 31, 2010 17:25
by Kulik
Thanks :) . I definitely have to clean the layouting code itself, it's a bit unreadable at the moment. I will do that when I convert it to the margin properties. I want to create separate methods that will calculate offset and bounding size for each layouting window, that will make it much more readable.

I've even tried layout container inside layout container and other crazy stuff and everything apart from the scrollable pane seemed to work fine :-)

What do you think about layoutAfter? Is it worth the hassle? It might speed things up for really long sequential layouts but it complicates things.

Gonna work on grid tomorrow.

I will need a new event on CEGUI::Window - EventMarginChanged. Should I split it to EventMarginTopChange, etc? Probably not worth the hassle. I am planning to define margin as 4 UDim, because URect doesn't really fit.