Grid, HorizontalLayout, HorizontalWrap and VerticalLayout can now be saved to and loaded from a file

0.8
Bruno Van de Velde 2017-07-25 18:54:59 +02:00
parent 0742cd0231
commit d89648d4c1
14 changed files with 290 additions and 19 deletions

View File

@ -539,8 +539,6 @@ namespace tgui
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
protected:
sf::RenderTarget* m_target = nullptr;
friend class Gui; // Required to let Gui access protected members from container and Widget
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

View File

@ -100,7 +100,10 @@ namespace tgui
// Copy all the widgets
for (std::size_t i = 0; i < right.m_widgets.size(); ++i)
add(right.m_widgets[i]->clone(), right.m_widgetNames[i]);
{
// Don't allow the 'add' function of a derived class to be called, since its members are not copied yet
Container::add(right.m_widgets[i]->clone(), right.m_widgetNames[i]);
}
}
return *this;

View File

@ -60,7 +60,6 @@ namespace tgui
m_target (&window),
m_accessToWindow(true)
{
m_container->m_target = &window;
m_container->m_focused = true;
Clipboard::setWindowHandle(window.getSystemHandle());
@ -83,7 +82,6 @@ namespace tgui
m_accessToWindow(false)
#endif
{
m_container->m_target = &target;
m_container->m_focused = true;
setView(target.getDefaultView());
@ -99,7 +97,6 @@ namespace tgui
void Gui::setTarget(sf::RenderWindow& window)
{
m_target = &window;
m_container->m_target = &window;
m_accessToWindow = true;
Clipboard::setWindowHandle(window.getSystemHandle());
@ -117,7 +114,6 @@ namespace tgui
#endif
m_target = &target;
m_container->m_target = &target;
setView(target.getDefaultView());
}

View File

@ -212,6 +212,13 @@ namespace tgui
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
ObjectConverter deserializeLayout(const std::string& value)
{
return {sf::String(value)};
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
ObjectConverter deserializeOutline(const std::string& value)
{
std::string str = trim(value);
@ -387,6 +394,7 @@ namespace tgui
{ObjectConverter::Type::Color, deserializeColor},
{ObjectConverter::Type::String, deserializeString},
{ObjectConverter::Type::Number, deserializeNumber},
{ObjectConverter::Type::Layout, deserializeLayout},
{ObjectConverter::Type::Outline, deserializeOutline},
{ObjectConverter::Type::Texture, deserializeTexture},
{ObjectConverter::Type::TextStyle, deserializeTextStyle},

View File

@ -148,6 +148,13 @@ namespace tgui
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
std::string serializeLayout(ObjectConverter&& value)
{
return value.getLayout().toString();
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
std::string serializeOutline(ObjectConverter&& value)
{
return value.getOutline().toString();
@ -240,6 +247,7 @@ namespace tgui
{ObjectConverter::Type::Color, serializeColor},
{ObjectConverter::Type::String, serializeString},
{ObjectConverter::Type::Number, serializeNumber},
{ObjectConverter::Type::Layout, serializeLayout},
{ObjectConverter::Type::Outline, serializeOutline},
{ObjectConverter::Type::Texture, serializeTexture},
{ObjectConverter::Type::TextStyle, serializeTextStyle},

View File

@ -33,7 +33,10 @@
#include <TGUI/Widgets/ChildWindow.hpp>
#include <TGUI/Widgets/ComboBox.hpp>
#include <TGUI/Widgets/EditBox.hpp>
#include <TGUI/Widgets/Grid.hpp>
#include <TGUI/Widgets/Group.hpp>
#include <TGUI/Widgets/HorizontalLayout.hpp>
#include <TGUI/Widgets/HorizontalWrap.hpp>
#include <TGUI/Widgets/Knob.hpp>
#include <TGUI/Widgets/Label.hpp>
#include <TGUI/Widgets/ListBox.hpp>
@ -49,6 +52,7 @@
#include <TGUI/Widgets/SpinButton.hpp>
#include <TGUI/Widgets/Tabs.hpp>
#include <TGUI/Widgets/TextBox.hpp>
#include <TGUI/Widgets/VerticalLayout.hpp>
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -233,7 +237,7 @@ namespace tgui
if (widget)
return loadWidget(node, widget);
else
return loadWidget(node, std::make_shared<Canvas>());
return loadWidget(node, Canvas::create());
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -374,7 +378,7 @@ namespace tgui
if (widget)
return loadWidget(node, widget);
else
return loadWidget(node, std::make_shared<ClickableWidget>());
return loadWidget(node, ClickableWidget::create());
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -492,12 +496,140 @@ namespace tgui
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Widget::Ptr loadGrid(const std::unique_ptr<DataIO::Node>& node, Widget::Ptr widget)
{
Grid::Ptr grid;
if (widget)
grid = std::static_pointer_cast<Grid>(widget);
else
grid = Grid::create();
loadContainer(node, grid);
if (node->propertyValuePairs["autosize"])
grid->setAutoSize(parseBoolean(node->propertyValuePairs["autosize"]->value));
if (node->propertyValuePairs["gridwidgets"])
{
if (!node->propertyValuePairs["gridwidgets"]->listNode)
throw Exception{"Failed to parse 'GridWidgets' property, expected a list as value"};
const auto& elements = node->propertyValuePairs["gridwidgets"]->valueList;
if (elements.size() != grid->getWidgets().size())
throw Exception{"Failed to parse 'GridWidgets' property, the amount of items has to match with the amount of child widgets"};
for (unsigned int i = 0; i < elements.size(); ++i)
{
std::string str = elements[i].toAnsiString();
// Remove quotes
if ((str.size() >= 2) && (str[0] == '"') && (str[str.size()-1] == '"'))
str = str.substr(1, str.size()-2);
// Remove brackets
if ((str.size() >= 2) && (str[0] == '(') && (str[str.size()-1] == ')'))
str = str.substr(1, str.size()-2);
// Ignore empty values (which are widgets that have not been given a location in the grid)
if (str.empty())
continue;
int row;
int col;
Borders borders;
auto alignment = Grid::Alignment::Center;
auto index = 0;
auto pos = str.find(',');
if (pos == std::string::npos)
throw Exception{"Failed to parse 'GridWidgets' property. Expected list values to be in the form of '\"(row, column, (borders), alignment)\"'. Missing comma after row."};
row = tgui::stoi(str.substr(index, pos - index));
index = pos + 1;
pos = str.find(',', index);
if (pos == std::string::npos)
throw Exception{"Failed to parse 'GridWidgets' property. Expected list values to be in the form of '\"(row, column, (borders), alignment)\"'. Missing comma after column."};
col = tgui::stoi(str.substr(index, pos - index));
index = pos + 1;
if (row < 0 || col < 0)
throw Exception{"Failed to parse 'GridWidgets' property, row and column have to be positive integers"};
pos = str.find('(', index);
if (pos == std::string::npos)
throw Exception{"Failed to parse 'GridWidgets' property. Expected list values to be in the form of '\"(row, column, (borders), alignment)\"'. Missing opening bracket for borders."};
index = pos;
pos = str.find(')', index);
if (pos == std::string::npos)
throw Exception{"Failed to parse 'GridWidgets' property. Expected list values to be in the form of '\"(row, column, (borders), alignment)\"'. Missing closing bracket for borders."};
borders = Deserializer::deserialize(ObjectConverter::Type::Outline, str.substr(index, pos+1 - index)).getOutline();
index = pos + 1;
pos = str.find(',', index);
if (pos == std::string::npos)
throw Exception{"Failed to parse 'GridWidgets' property. Expected list values to be in the form of '\"(row, column, (borders), alignment)\"'. Missing comma after borders."};
std::string alignmentStr = toLower(trim(str.substr(pos + 1)));
if (alignmentStr == "center")
alignment = Grid::Alignment::Center;
else if (alignmentStr == "upperleft")
alignment = Grid::Alignment::UpperLeft;
else if (alignmentStr == "up")
alignment = Grid::Alignment::Up;
else if (alignmentStr == "upperright")
alignment = Grid::Alignment::UpperRight;
else if (alignmentStr == "right")
alignment = Grid::Alignment::Right;
else if (alignmentStr == "bottomright")
alignment = Grid::Alignment::BottomRight;
else if (alignmentStr == "bottom")
alignment = Grid::Alignment::Bottom;
else if (alignmentStr == "bottomleft")
alignment = Grid::Alignment::BottomLeft;
else if (alignmentStr == "left")
alignment = Grid::Alignment::Left;
else
throw Exception{"Failed to parse 'GridWidgets' property. Invalid alignment '" + alignmentStr + "'."};
grid->addWidget(grid->getWidgets()[i], static_cast<std::size_t>(row), static_cast<std::size_t>(col), borders, alignment);
}
}
return grid;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Widget::Ptr loadGroup(const std::unique_ptr<DataIO::Node>& node, Widget::Ptr widget)
{
if (widget)
return loadContainer(node, std::static_pointer_cast<Group>(widget));
else
return loadContainer(node, std::make_shared<Group>());
return loadContainer(node, Group::create());
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Widget::Ptr loadHorizontalLayout(const std::unique_ptr<DataIO::Node>& node, Widget::Ptr widget)
{
if (widget)
return loadContainer(node, std::static_pointer_cast<HorizontalLayout>(widget));
else
return loadContainer(node, HorizontalLayout::create());
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Widget::Ptr loadHorizontalWrap(const std::unique_ptr<DataIO::Node>& node, Widget::Ptr widget)
{
if (widget)
return loadContainer(node, std::static_pointer_cast<HorizontalWrap>(widget));
else
return loadContainer(node, HorizontalWrap::create());
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -704,7 +836,7 @@ namespace tgui
if (widget)
return loadContainer(node, std::static_pointer_cast<Panel>(widget));
else
return loadContainer(node, std::make_shared<Panel>());
return loadContainer(node, Panel::create());
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -718,7 +850,7 @@ namespace tgui
picture = Picture::create();
if (node->propertyValuePairs["filename"])
picture = std::make_shared<Picture>(DESERIALIZE_STRING("filename"));
picture = Picture::create(DESERIALIZE_STRING("filename"));
loadWidget(node, picture);
@ -801,7 +933,7 @@ namespace tgui
if (widget)
return loadContainer(node, std::static_pointer_cast<RadioButtonGroup>(widget));
else
return loadContainer(node, std::make_shared<RadioButtonGroup>());
return loadContainer(node, RadioButtonGroup::create());
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -931,6 +1063,18 @@ namespace tgui
return textBox;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Widget::Ptr loadVerticalLayout(const std::unique_ptr<DataIO::Node>& node, Widget::Ptr widget)
{
if (widget)
return loadContainer(node, std::static_pointer_cast<VerticalLayout>(widget));
else
return loadContainer(node, VerticalLayout::create());
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -947,7 +1091,10 @@ namespace tgui
{"clickablewidget", loadClickableWidget},
{"combobox", loadComboBox},
{"editbox", loadEditBox},
{"grid", loadGrid},
{"group", loadGroup},
{"horizontallayout", loadHorizontalLayout},
{"horizontalwrap", loadHorizontalWrap},
{"knob", loadKnob},
{"label", loadLabel},
{"listbox", loadListBox},
@ -962,7 +1109,8 @@ namespace tgui
{"slider", loadSlider},
{"spinbutton", loadSpinButton},
{"tabs", loadTabs},
{"textbox", loadTextBox}
{"textbox", loadTextBox},
{"verticallayout", loadVerticalLayout}
};
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

View File

@ -31,6 +31,7 @@
#include <TGUI/Widgets/ChildWindow.hpp>
#include <TGUI/Widgets/ComboBox.hpp>
#include <TGUI/Widgets/EditBox.hpp>
#include <TGUI/Widgets/Grid.hpp>
#include <TGUI/Widgets/Knob.hpp>
#include <TGUI/Widgets/Label.hpp>
#include <TGUI/Widgets/ListBox.hpp>
@ -315,6 +316,75 @@ namespace tgui
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
std::unique_ptr<DataIO::Node> saveGrid(Widget::Ptr widget)
{
auto grid = std::static_pointer_cast<Grid>(widget);
auto node = WidgetSaver::getSaveFunction("container")(grid);
const auto& children = grid->getWidgets();
auto widgetsMap = grid->getWidgetLocations();
if (!widgetsMap.empty())
{
auto alignmentToString = [](Grid::Alignment alignment) -> std::string {
switch (alignment)
{
case Grid::Alignment::Center:
return "Center";
case Grid::Alignment::UpperLeft:
return "UpperLeft";
case Grid::Alignment::Up:
return "Up";
case Grid::Alignment::UpperRight:
return "UpperRight";
case Grid::Alignment::Right:
return "Right";
case Grid::Alignment::BottomRight:
return "BottomRight";
case Grid::Alignment::Bottom:
return "Bottom";
case Grid::Alignment::BottomLeft:
return "BottomLeft";
case Grid::Alignment::Left:
return "Left";
default:
throw Exception{"Invalid grid alignment encountered."};
}
};
auto getWidgetsInGridString = [&](const auto& w) -> std::string {
auto it = widgetsMap.find(w);
if (it != widgetsMap.end())
{
const auto row = it->second.first;
const auto col = it->second.second;
return "\"(" + to_string(row)
+ ", " + to_string(col)
+ ", " + grid->getWidgetBorders(row, col).toString()
+ ", " + alignmentToString(grid->getWidgetAlignment(row, col))
+ ")\"";
}
else
return "\"()\"";
};
std::string str = "[" + getWidgetsInGridString(children[0]);
for (std::size_t i = 1; i < children.size(); ++i)
str += ", " + getWidgetsInGridString(children[i]);
str += "]";
SET_PROPERTY("GridWidgets", str);
}
if (grid->getAutoSize())
node->propertyValuePairs.erase("Size");
SET_PROPERTY("AutoSize", to_string(grid->getAutoSize()));
return node;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
std::unique_ptr<DataIO::Node> saveKnob(Widget::Ptr widget)
{
auto knob = std::static_pointer_cast<Knob>(widget);
@ -626,7 +696,10 @@ namespace tgui
{"clickablewidget", saveWidget},
{"combobox", saveComboBox},
{"editbox", saveEditBox},
{"grid", saveGrid},
{"group", saveContainer},
{"horizontallayout", saveContainer},
{"horizontalwrap", saveContainer},
{"knob", saveKnob},
{"label", saveLabel},
{"listbox", saveListBox},
@ -641,7 +714,8 @@ namespace tgui
{"slider", saveSlider},
{"spinbutton", saveSpinButton},
{"tabs", saveTabs},
{"textbox", saveTextBox}
{"textbox", saveTextBox},
{"verticallayout", saveContainer}
};
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

View File

@ -127,6 +127,7 @@ namespace tgui
{
Group::rendererChanged(property);
// Update the space between widgets as the padding is used when no space was explicitly set
m_spaceBetweenWidgetsCached = getRenderer()->getSpaceBetweenWidgets();
updateWidgets();
}

View File

@ -24,7 +24,6 @@
#include <TGUI/Widgets/HorizontalWrap.hpp>
#include <algorithm> // std::max
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

View File

@ -117,7 +117,7 @@ void testSavingWidget(std::string name, std::shared_ptr<WidgetType> widget, bool
parent = std::make_shared<tgui::GuiContainer>();
REQUIRE_NOTHROW(parent->loadWidgetsFromFile(name + "WidgetFile1.txt"));
REQUIRE_NOTHROW(parent->saveWidgetsToFile(name + "WidgetFile2.txt"));
REQUIRE(compareFiles(name + "WidgetFile1.txt", name + "WidgetFile2.txt"));
REQUIRE_NOTHROW(parent->saveWidgetsToFile(name + "WidgetFile3.txt"));
REQUIRE(compareFiles(name + "WidgetFile1.txt", name + "WidgetFile3.txt"));
}
}

View File

@ -108,5 +108,22 @@ TEST_CASE("[Grid]")
REQUIRE(grid->getWidgetAlignment(0, 0) == tgui::Grid::Alignment::Center);
}
/// TODO: Loading from and saving to file
SECTION("Saving and loading from file")
{
grid->setSize({800, 600});
grid->addWidget(tgui::ClickableWidget::create({40, 30}), 5, 4);
grid->addWidget(tgui::ClickableWidget::create({40, 30}), 2, 7, {1, 2, 3, 4});
grid->addWidget(tgui::ClickableWidget::create({40, 30}), 0, 0, {}, tgui::Grid::Alignment::Center);
grid->addWidget(tgui::ClickableWidget::create({40, 30}), 0, 1, {}, tgui::Grid::Alignment::Left);
grid->addWidget(tgui::ClickableWidget::create({40, 30}), 0, 2, {}, tgui::Grid::Alignment::UpperLeft);
grid->addWidget(tgui::ClickableWidget::create({40, 30}), 1, 0, {}, tgui::Grid::Alignment::Up);
grid->addWidget(tgui::ClickableWidget::create({40, 30}), 1, 1, {}, tgui::Grid::Alignment::UpperRight);
grid->addWidget(tgui::ClickableWidget::create({40, 30}), 1, 2, {}, tgui::Grid::Alignment::Right);
grid->addWidget(tgui::ClickableWidget::create({40, 30}), 2, 0, {}, tgui::Grid::Alignment::BottomRight);
grid->addWidget(tgui::ClickableWidget::create({40, 30}), 2, 1, {}, tgui::Grid::Alignment::Bottom);
grid->addWidget(tgui::ClickableWidget::create({40, 30}), 2, 2, {}, tgui::Grid::Alignment::BottomLeft);
grid->add(tgui::ClickableWidget::create({40, 30}));
testSavingWidget("Grid", grid, false);
}
}

View File

@ -97,4 +97,11 @@ TEST_CASE("[HorizontalLayout]")
REQUIRE(layout->getWidgetName(layout->get(2)) == "5");
REQUIRE(layout->get(5) == nullptr);
}
SECTION("Saving and loading from file")
{
layout->add(tgui::Button::create("Hello"));
layout->add(tgui::Button::create("Hi"));
testSavingWidget("HorizontalLayout", layout, false);
}
}

View File

@ -90,4 +90,9 @@ TEST_CASE("[HorizontalWrap]")
REQUIRE(pic6->getPosition() == sf::Vector2f(0, 80));
REQUIRE(pic7->getPosition() == sf::Vector2f(100, 80));
REQUIRE(pic8->getPosition() == sf::Vector2f(0, 160));
SECTION("Saving and loading from file")
{
testSavingWidget("HorizontalWrap", wrap, false);
}
}

View File

@ -97,4 +97,11 @@ TEST_CASE("[VerticalLayout]")
REQUIRE(layout->getWidgetName(layout->get(2)) == "5");
REQUIRE(layout->get(5) == nullptr);
}
SECTION("Saving and loading from file")
{
layout->add(tgui::Button::create("Hello"));
layout->add(tgui::Button::create("Hi"));
testSavingWidget("VerticalLayout", layout, false);
}
}