ChildWindow

Started by Garwin, 16 November 2023, 17:14:40

Garwin

I first tried to implement both minimalizing and maximizing buttons in ChildWindow Widget with TGUI 1.1, backend SFML on Windows with GCC 13.1 compiler. I got some strange behaviors (code attached at the end)

1. Movement of the widget inside a parent widget
If widget setPositionLocked(false) and setKeepInParent(true) is true then the movement of the widget is only possible on the edges of the parent widget. If setKeepInParent(false), than movement is freely available anywhere.

2. Implementing Maximize button

I have tried implemented it by lambda and it has some limitations (I will discuss later). But Maximize button does not work correctly as childWindow->setPosition(tgui::Vector2f{nonMaxPosition.x, nonMaxPosition.y}); is completely ignored. I stream the position of the widget before that and after that with information about variable nonMaxPosition to std::cout and it shows that variable nonMaxPosition is correct but the row mentioned above do not set new position of the widget so everytime maximize button is pushed when widget is already maximize, the new position is set to left top corner of parent widget ignoring setting new position of stored previous position.

3. Implementing Maximize and Minimize buttons
Using separated lambdas for each button which are not aware of status of the seconds is not ideal. Is there another way to do it?
I have only one idea and to create new widget which inherites from ChildWidget and have private members storing information about status of the widget (maximalized, minimalized), position and size before that.

#include <TGUI/Widgets/ChildWindow.hpp>
#include <TGUI/Widgets/Group.hpp>
#include <TGUI/Backend/SFML-Graphics.hpp>

#include <iostream>

int main ()
{
    sf::RenderWindow window({800, 600}, "TGUI example (SFML-Graphics)");
    tgui::Gui gui(window);

    auto group = tgui::Group::create();
    group->setSize({700,500});
    group->setPosition({50,50});
    gui.add(group);

    auto childWindow = tgui::ChildWindow::create("Settings");
    group->add(childWindow);
    childWindow->setSize({150,100});
    childWindow->setPosition(50,50);
    childWindow->setPositionLocked(false);
    childWindow->setTitleButtons(tgui::ChildWindow::TitleButton::Maximize
                                 | tgui::ChildWindow::TitleButton::Minimize
                                 | tgui::ChildWindow::TitleButton::Close);
    childWindow->setResizable(true);
    childWindow->setKeepInParent(true); // Prevents any part of the window to go outside the screen

    childWindow->onMaximize([&](){
        static bool maximized (false);
        static bool keepInParent {childWindow->isKeptInParent()};
        static tgui::Vector2f nonMaxSize {childWindow->getSize()};
        static tgui::Vector2f nonMaxPosition (childWindow->getPosition());

        if (maximized)
        {
            childWindow->setSize(nonMaxSize);
            childWindow->setPosition(tgui::Vector2f{nonMaxPosition.x, nonMaxPosition.y}); // DO NOT WORK
            childWindow->setKeepInParent(keepInParent);
            childWindow->setPositionLocked(false);
        }
        else
        {
            keepInParent = childWindow->isKeptInParent();
            nonMaxSize = childWindow->getSize();
            nonMaxPosition = childWindow->getPosition();
            childWindow->setPosition({0,0});
            childWindow->setSize(childWindow->getParent()->getSize());
            childWindow->setKeepInParent(true);
            childWindow->setPositionLocked(true);
        }
        maximized = !maximized;
    });

    childWindow->onMinimize([&](){
        static bool minimized {false};
        static tgui::Vector2f nonMinimizedSize {childWindow->getSize()};
        if (minimized)
            childWindow->setSize(nonMinimizedSize);
        else
        {
            if (childWindow->getInnerSize().y !=0 && childWindow->getMaximumSize() != childWindow->getSize())
                nonMinimizedSize = childWindow->getSize();
            childWindow->setClientSize({childWindow->getSize().x,0});
        }
        minimized = !minimized;
    });

    gui.mainLoop();

    return 0;
}


texus

Thanks for the detailed description and sample code, I was able to quickly track down the bug in TGUI. There is a branch in setPosition that doesn't actually set any position (when KeepInParent is true and the provided position isn't outside the parent).

A workaround would be to call setKeepInParent(false) right before you call setPosition.

The issue has now been fixed in the latest development version.

QuoteUsing separated lambdas for each button which are not aware of status of the seconds is not ideal. Is there another way to do it?
You can move the state outside the lambda and let the lambda functions take a reference to it. Your lambda already starts with "[&]", so it already has access to all variables outside it by reference. You just have to be careful that the referenced parameter doesn't go out of scope and gets destroyed before the lambda is called.

Code (cpp) Select
bool maximized = false;
bool minimized = false

childWindow->onMaximize([&](){
    // You can use 'minimized' here as well.
    // Make sure to remove the "static bool maximized (false);" line from this lambda
});

childWindow->onMinimize([&](){
    // You can use 'maximized' here as well.
    // Make sure to remove the "static bool minimized (false);" line from this lambda
});

You can do the same for the other static variables.

Garwin

Thanks Texus.
I would probably used inhereted class. For small example having variables outsides is easy but for anything a little more complex having all variables in one class seems to me better solution.