Layout container

Started by Dnake, 30 May 2015, 19:40:09

Dnake

Hey Texus, I wrote a class that allow user of TGUI to layout widget more easily than with bindWidth, bindHeight and so on.
It's juste like QBoxLayout in Qt, there is two class: HorizontalLayout and VerticalLayout. Both class have the same interface as Container, but automatically resize and position widgets, allowing this kind of code:

auto layout = tgui::VerticalLayout::create();
layout->setSize(bindWidth(gui), bindHeight(gui));
gui.add(layout);
//...
layout->add(button1);
layout->add(button2);
layout->add(button3);

And now the three buttons fill the whole window, juste like bindWidth(gui, 0.33f) but in a more intuitive way, and without the need to write floating literal.
It is also possible to insert widget, between two already added widgets.

If you're interested in theses classes, I can implement more features, like weight for each element, borders, fixed size widget and space elements.
This is not redundant with the Grid, because the grid actually only position elements, and theses layouts also resize them.

Edit: I know that the copy constructor/assignement operator sounds bad, because widgets are copied, removed and copied again, but that's the only way I seen to don't have to write a addWidget method like you did with Grid.
And maybe I written some methods the wrong way, like mouseOnWidget, I copy-pasted alot and I don't understand all mechanisms of TGUI. This is just a design proposal.

texus

Wow, this is really cool. I always wanted to add classes like that but I never found the time to do it.

QuoteIf you're interested in theses classes, I can implement more features, like weight for each element, borders, fixed size widget and space elements.
I'm definitely interested in such classes.

QuoteEdit: I know that the copy constructor/assignement operator sounds bad, because widgets are copied, removed and copied again, but that's the only way I seen to don't have to write a addWidget method like you did with Grid.
I think it can be done easier if I make a small change in the Container class. The reason you have to write the copy constructor and assignement operator is because the ones in Container just copy their own variables. If instead I make it call the add function for all its widgets then your code should work without needing to implement these functions yourself.

Btw, perhaps it is just a typo and you already know this, but std::remove (which you use in BoxLayout::remove) doesn't remove anything.

Dnake

Quote from: texus on 30 May 2015, 22:32:35
Btw, perhaps it is just a typo and you already know this, but std::remove (which you use in BoxLayout::remove) doesn't remove anything.
No that wasn't a typo, I didn't read the doc enough to understand how std::remove works, I fixed that.

So I'll work a bit more on the features I said, and then maybe should I pull the TGUI github and make a pull request ?
I never used github on a real project before...

texus

Quoteand then maybe should I pull the TGUI github and make a pull request ?
That would probably be the easiest way for me, yes.
You just make a fork on github, make the changes in your forked repo and then send a pull request so that I can review the code before merging it.

Dnake

#4
I already implemented the ratio system for distribute differently the space between elements, and adding/inserting space between elements
The interface is like this:

layout->add(widget);
layout->add(widget2);
layout->insertSpace(1, "spaceID");
layout->setRatio("spaceID", 0.5f);


The ratio is not the size used by the widget in the layout, but the size that the widget will have relatively to others.
So in a layout 500 px, with 2 elements of a ratio equal to 2.f and another element with a ratio equal to 1.f, the two
first widgets will have 200px and the last one will have 100px.

texus

#5
What is the reason that you have the ratio identified as a string instead of passing 0.5 directly to insertSpace?
What function would you have to call to set the ratio of a widget?

Dnake

#6
Actually, there is 4 methods for addings things to the layout:

void insert(unsigned int position, const tgui::Widget::Ptr& widget, const sf::String& widgetName = "");
void add(const tgui::Widget::Ptr& widget, const sf::String& widgetName = "");
void insertSpace(unsigned int position, const sf::String& spaceName = "");
void addSpace(const sf::String& spaceName = "");

These four functions respectively insert a widget at the specified position in the layout, add a widget at the end of the layout, insert a space at the specified position and add a space at the end of the layout.
Spaces and widgets are handled the same way internally (spaces are hidden widgets).
So the three last are just convenience functions, and finally call insert.

And I didn't add a parameter for directly setting the ratio because sometimes the user would want to give an identifier and let the ratio to default, and sometimes he would wand to just set the ratio but let an empty identifier. So have these two parameters in the same function call with default value cannot satisfy these two cases at the same time.
But if you find anyway that it is better to add ratio to the parameters of add/insert methods, I will do so :)

texus

Disclaimer: I didn't think before starting to write this post, so it is mostly random thoughts that crossed my mind. You can pick the design that you like the most, these are just some ideas that I had.

Ok, I see why you do it like that now.

People might also want to set the ratio for a widget that they did not name:
Code (cpp) Select
bool setRatio(Widget::Ptr widget, float ratio);

That way they don't need to give the widget a name just to do this:
Code (cpp) Select
layout->add(widget);
layout->setRatio(widget, 0.5f);


For convenience the following functions could be added for the ratio:
Code (cpp) Select

void insertSpace(unsigned int position, float ratio);
void addSpace(float ratio);


But then it might also be interesting to have the ratio parameter like that in the 'add' and 'insert' functions, and then you could also have functions with both the name and the ratio. That way people can choose between the name, the ratio or both. But then we start having a lot of functions for a simple thing, so this probably isn't feasible.


So perhaps the design should get a more radical change. What about not using the widget name and making space implicit?
Code (cpp) Select
void insert(unsigned int position, const tgui::Widget::Ptr& widget, const sf::String& widgetName = "");
void add(const tgui::Widget::Ptr& widget, const sf::String& widgetName = "");
void setRatio(const tgui::Widget::Ptr& widget, float ratio);
void setRatio(unsigned int position, float ratio);


It would be used like this:
Code (cpp) Select
layout->add(widget);
layout->insert(2, widget2);
layout->setRatio(1, 0.5f); // Set the ratio of the space
layout->setRatio(0, 0.5f); // Set ratio of the first widget
layout->setRatio(widget2, 0.5f); // Set ratio of the second widget


The downside of this is of course that for the second widget you have to use the insert function because the add function would have inserted it at position 1. But perhaps we could still use the addSpace functions with a ratio:
Code (cpp) Select
void insertSpace(unsigned int position, float ratio);
void addSpace(float ratio);


Then the code could look like this:
Code (cpp) Select
layout->add(widget);
layout->addSpace(0.5f);
layout->add(widget2);
layout->setRatio(0, 0.5f); // Set ratio of the first widget
layout->setRatio(widget2, 0.5f); // Set ratio of the second widget

Dnake

#8
The interface you finally proposed is mostly the one I already made, I just learn how to fork/push and you'll can see what I've made.
I'll add addSpace(float ratio, const sf::String widgetName = ""); I think it's a good idea.

Edit: Ok, I made a pull request.