Layouts in Files

Started by Jervy, 23 October 2015, 14:14:24

Jervy

Hey there,

so I've been playing around with TGUI lately and I couldn't figure out how to load layouts from a file, for example:

position: "{&.w - 50, &.h + 5}";


I then implemented it myself and it works like a charm, so I thought you guys would be interested and it might even be implemented into TGUI in the future.

Here are the changes I made (only one file - WidgetLoader.cpp)

First the include:

#include <TGUI/Layout.hpp>


Other than that I only changed the function parseVector2f:

tgui::Layout2d parseVector2f(std::string str)
    {
if (str.empty())
throw tgui::Exception{ "Failed to parse position. String empty." };

if (str.front() == '(' && str.back() == ')'){
// it's a vector
str = str.substr(1, str.length() - 2);

auto commaPos = str.find(',');
if (commaPos == std::string::npos)
throw tgui::Exception{ "Failed to parse position '" + str + "'. Expected numbers separated with a comma." };

if (str.find(',', commaPos + 1) != std::string::npos)
throw tgui::Exception{ "Failed to parse position '" + str + "'. Expected only one comma." };

return{ tgui::stof(str.substr(0, commaPos)), tgui::stof(str.substr(commaPos + 1)) };
}
else if (str.front() == '"' && str.back() == '"'){
// it's a layout
str = str.substr(1, str.length() - 2);

return{ str };
}
else
throw tgui::Exception{"Failed to parse position '" + str + "'. Malformed."};
    }


It would be appropriate to change the name of the function, but I couldn't come up with a good name ;P

Cheers,
Jervy

texus

#1
Thanks. It is something that I was planning to add in the future but just has a very low priority.

Do you have a github account? Then you can send your change as a pull request and the commit will be linked to your account. Otherwise I will just commit it myself. Either way I'm first going to slightly change the code to fit my code style though.

The function can be renamed to parseLayout.

texus

After some changes (e.g. changing tabs to spaces) the final version looks like this:
Code (cpp) Select
    tgui::Layout2d parseLayout(std::string str)
    {
        if (str.empty())
            throw tgui::Exception{"Failed to parse layout. String was empty."};

        // Check if the layout is an (x, y) vector or a quoted string
        if ((str.front() == '(') && (str.back() == ')'))
        {
            str = str.substr(1, str.length() - 2);

            auto commaPos = str.find(',');
            if (commaPos == std::string::npos)
                throw tgui::Exception{"Failed to parse layout '" + str + "'. Expected numbers separated with a comma."};

            if (str.find(',', commaPos + 1) != std::string::npos)
                throw tgui::Exception{"Failed to parse layout '" + str + "'. Expected only one comma."};

            return {tgui::stof(str.substr(0, commaPos)), tgui::stof(str.substr(commaPos + 1))};
        }
        else if ((str.front() == '"') && (str.back() == '"'))
        {
            str = str.substr(1, str.length() - 2);
            return {str};
        }
        else
            throw tgui::Exception{"Failed to parse layout '" + str + "'. Expected (x,y) or a quoted layout string."};
    }


Including Layout.hpp is actually not needed since it is such an important part of the gui that almost any header will (indirectly) include it.

I remember by now why I never implemented this before myself. I was going to implemented saving to file together with loading from file but when the layout is not a string there are situations in which it can't be saved correctly. But I don't see a problem with adding this loading now and adding saving to file later on.

Jervy

Awesome!

Looking forward to see the saving work and yeah I didn't test if Layout.hpp was actually necessary ;)

Unfortunately I don't have a github account. But I'm glad that I could contribute a tiny bit to your project.

Keep up the good work!

texus

The latest tgui version (which you can download from github) can now load layouts from a file but it can also save layouts based on strings to a file.

I improved the code some more so that you can also load something like this from a file:
Code (ini) Select
Size: ("0.5 * parent.width", 50)

While adding the saving layouts to file I also found 2 bugs in tgui. One was just a minor thing but the second one was pretty serious. If you intent to call gui.remove or gui.removeAllWidgets in your code then I suggest you download the latest version because removing widgets could apparently result in hard to find crashes in certain situations.

Jervy

#5
Cool, I will definetly download the latest version. Good on you that you found the bugs (y)

fachrive

"_, _" is broken in master. ("_","_") shows different result.

texus

"_, _" never worked, you need to add braces: "{_,_}".
Could you provide an example of where it goes wrong with ("_","_")? Because there is a simple test in TGUI which indicates that the loading layouts like that is still working.

fachrive

Thanks for clarifying.
"{_,_}" is inconsistent with C++ code, so I didn't try it. I did try ("_,_") and {"_,_"}.
("_","_") works. I meant results different from "_,_".

texus

It is actually not inconsistent with c++, but I understand why you would think it is. In c++ there are 2 options for setting a layout, one is by passing an x and a y value while the other one is setting a string. The loading from file only provides the string representation, which even in c++ require the braces. Also when doing widget->setPosition(x,y) you are actually calling a shortcut to widget->setPosition({x,y}) (which is by itself a shorter notation for widget->setPosition(tgui::Layout{x,y})) so it is even closer related then you might think.

The only valid options are ("_","_") and "{_,_}". I think the reason I went for braces inside the string is because brackets are also used in the math expressions and it is thus easier to parse with braces. Maybe I could drop the braces and accept "_,_" as valid input, but the braces will always be needed when writing something like "2 * {20, button.height} + 50". But I can think of that later, for now only those 2 ways are supported.

The reason why the result is different is because "_,_" will silently fail. Parsing the string is done in the layouts themselves (so the code is not specific to loading from a file) and because it is sometimes intended to silently fail I cannot throw an exception there to warn you that the parsing failed.