Layouts in Files
« on: 23 October 2015, 14:14:24 »
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:
Code: [Select]
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:
Code: [Select]
#include <TGUI/Layout.hpp>

Other than that I only changed the function parseVector2f:
Code: [Select]
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
« Last Edit: 23 October 2015, 14:16:29 by Jervy »

*

texus

  • *****
  • 1172
    • View Profile
    • Texus's Blog
Re: Layouts in Files
« Reply #1 on: 23 October 2015, 14:30:08 »
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.
« Last Edit: 23 October 2015, 14:32:02 by texus »

*

texus

  • *****
  • 1172
    • View Profile
    • Texus's Blog
Re: Layouts in Files
« Reply #2 on: 23 October 2015, 16:48:35 »
After some changes (e.g. changing tabs to spaces) the final version looks like this:
    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.

Re: Layouts in Files
« Reply #3 on: 24 October 2015, 01:27:36 »
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

  • *****
  • 1172
    • View Profile
    • Texus's Blog
Re: Layouts in Files
« Reply #4 on: 24 October 2015, 13:32:27 »
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:
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.

Re: Layouts in Files
« Reply #5 on: 16 November 2015, 16:11:56 »
Cool, I will definetly download the latest version. Good on you that you found the bugs (y)
« Last Edit: 16 November 2015, 16:28:48 by Jervy »

Re: Layouts in Files
« Reply #6 on: 16 December 2015, 15:46:08 »
"_, _" is broken in master. ("_","_") shows different result.

*

texus

  • *****
  • 1172
    • View Profile
    • Texus's Blog
Re: Layouts in Files
« Reply #7 on: 16 December 2015, 15:51:15 »
"_, _" 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.

Re: Layouts in Files
« Reply #8 on: 16 December 2015, 16:30:40 »
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

  • *****
  • 1172
    • View Profile
    • Texus's Blog
Re: Layouts in Files
« Reply #9 on: 16 December 2015, 16:48:12 »
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.