Resizable Child Windows/Right-Click Menu

Started by Ruckamongus, 09 December 2015, 05:09:34

Ruckamongus

Hey Texus,

Great GUI! I'd like to make two requests (one of which I made quite a while ago, I'm hoping to hear your more recent thoughts).

First, would you allow child windows to be resized like this:

I realize there is a setSize() function, but it feels limited not being able to drag the borders dynamically.


Second, can we have a right-click menu for the mouse? Even if it's super simple (doesn't even have to have sub-menus, icons, or dividers) for now?


Also, an unrelated bug report, it seems that the TextBox widget doesn't obey setOpacity() (0.7dev).

Thanks for your time!!

texus

#1
I'm not making as much progress with v0.7 as I wanted, even now it looks that as soon as I get a lot of time again (next year July/August) I will be spending multiple weeks rewriting stuff instead of adding new widgets.

The main problem with resizing has always been that I want the cursor to change, but there should be some way for the user to set which cursor he wants or whether he doesn't want it to change. I haven't taken the time yet to think about a proper way to implement it. And I'm also kindof waiting for this before I even consider starting.

There still hasn't been any progress on the right click menu.
The best I can do for now would be to add a right click callback with the mouse position and possible the widget below the mouse as parameters. You would have to create a ListBox in this callback function which serves as popup menu and remove it yourself when clicking somewhere else on the screen afterwards. I'll have a bit time in one or two weeks, it the above solution is enough then I can try to add it (and give a small example code on how to use it to get the popup menu). But a real popup menu won't arrive anywhere soon.

TextBox indeed seems to be missing a few lines of code in setOpacity, I'll fix this later today.
Edit: Fixed

Ruckamongus

Texus,

I appreciate the timely reply!

I can definitely understand wanting to wait for the cursor changing thing. I just hope that out friends on the SFML side can get a decision made (hopefully itit doesn't turn into a clipboard scenario, haha). Perhaps I'll look into doing something makeshift for the time being.

As for the menu, that sounds perfect. I really don't need anything fancy at all; a list box would be sufficient as long as I can guarantee it stays within the view and has the correct depth (Imagine right clicking just under a window; the menu should draw over everything). Definitely don't set yourself back on this, but I'd be extremely grateful if you could whip up something for now; the right click menu is pretty integral in my interface and one main reason I haven't been motivated enough to port my project to C++ (prototyping in GameMaker... Eh).

As for the textbox thing, thanks for looking into it. Definitely no rush. I haven't updated my source for a couple of weeks either, so you may have fixed it since then.

Thanks a ton!

texus

Are you using Visual Studio? I already found some time to add the right click callback, but I just realized that it relies on the optional parameters in the signal system which are not supported in Visual Studio. So if you are using a different compiler then I will just leave it like it is now and write an example based on this, otherwise I'll add some more code to support using it with the tgui::Callback class as well.

texus

While writing the example I actually figured out that you can do it without any change in TGUI with exactly the same amount of lines of code:
Code (cpp) Select
#include <TGUI/TGUI.hpp>

tgui::ListBox::Ptr popumMenu;

void popupMenuCallback(std::string item)
{
    std::cout << item << std::endl;
}

void rightClickCallback(tgui::Gui& gui, sf::Vector2f position)
{
    popumMenu = std::make_shared<tgui::ListBox>();
    popumMenu->addItem("Option 1");
    popumMenu->addItem("Option 2");
    popumMenu->addItem("Option 3");
    popumMenu->addItem("Option 4");
    popumMenu->setItemHeight(20);
    popumMenu->setPosition(position);
    popumMenu->setSize(120, popumMenu->getItemHeight() * popumMenu->getItemCount());
    popumMenu->connect("ItemSelected", popupMenuCallback);
    gui.add(popumMenu);
}

int main()
{
    sf::RenderWindow window(sf::VideoMode(800, 600), "TGUI window");
    tgui::Gui gui(window);
    gui.setFont("TGUI/fonts/DejaVuSans.ttf");

    while(window.isOpen())
    {
        sf::Event event;
        while(window.pollEvent(event))
        {
            if(event.type == sf::Event::Closed)
                window.close();

            // Check if there is a pop-up menu
            if (popumMenu)
            {
                // When mouse is released, remove the pop-up menu
                if (event.type == sf::Event::MouseButtonReleased && event.mouseButton.button == sf::Mouse::Left)
                {
                    gui.remove(popumMenu);
                    popumMenu = nullptr;
                }

                // When mouse is pressed, remove the pop-up menu only when the mouse is not on top of the menu
                if (event.type == sf::Event::MouseButtonPressed)
                {
                    if (!popumMenu->mouseOnWidget(event.mouseButton.x, event.mouseButton.y))
                    {
                        gui.remove(popumMenu);
                        popumMenu = nullptr;
                    }
                }
            }

            // Perhaps we have to open a menu
            else if (event.type == sf::Event::MouseButtonPressed && event.mouseButton.button == sf::Mouse::Right)
            {
                rightClickCallback(gui, sf::Vector2f(event.mouseButton.x, event.mouseButton.y));
            }

            gui.handleEvent(event);
        }

        window.clear();
        gui.draw();
        window.display();
    }
}

Ruckamongus

I'm a G++ guy, so no worries. I just tried it, and it works great! I can definitely work with this. Thanks so much!

texus

#6
I have found some time to implement a basic version of the resizable child windows. The mouse currently has to stand exactly on the border in order to start dragging it (so make sure the borders are thick enough). Since the cursor cannot be changed yet you also won't be able to see if you are actually on top of the correct pixel or right next to it. But I can add the changing cursor later without you having to change your code.

In order to use the feature, use the latest TGUI version from github and call the setResizable function on the child window (by default they are not resizable).
Code (cpp) Select
childWindow->setResizable(true);

Ruckamongus

#7
I just tried it out, thanks a ton! It's a bit odd that the titlebar isn't included and as you said it needs large borders. Nonetheless, it gives me a good foundation to tweak some things to my project. I really appreciate your help, Texus!

I do have another question related to the ListBox widget. I'm trying to size it automatically:


void rightClickMenuCreate(tgui::Gui& gui, sf::Vector2f position, tgui::Theme::Ptr& theme, std::vector<std::string>&& options)
{
    popumMenu = theme->load("ListBox");
    std::size_t Max = 0;
    for (const auto& i: options)
    {
         popumMenu->addItem(i);
         Max = std::max(Max, WIDTH_OF_I);//<-------
    }

    popumMenu->setItemHeight(25);
    popumMenu->setPosition(position);
    popumMenu->setOpacity(0.8f);
    popumMenu->setSize(Max, popumMenu->getItemHeight() * popumMenu->getItemCount() + popumMenu->getRenderer()->getPadding().top * 2);
    popumMenu->connectEx("MouseReleased", [&](const tgui::Callback& e){rightClickMenuHandle(e.text); gui.remove(popumMenu); popumMenu = nullptr;});
    popumMenu->showWithEffect(tgui::ShowAnimationType::Fade, sf::milliseconds(275));
    gui.add(popumMenu);
}


How would you go about determining "WIDTH_OF_I"? I figured I'd create a sf::Text before the loop and set it's font to the ListBox's renderer's font. Then set the text in the loop and get its bounds. Is there a better way to do this?

texus

There isn't really a better way. It might be easier if you created a Label instead of an sf::Text and use label->getSize().x though.

If there is padding then it should also be included, the line should thus become:
Code (cpp) Select
std::max(max, popumMenu->getRenderer()->getPadding().left + label->getSize().x + popumMenu->getRenderer()->getPadding().right)

serhio

#9
Quote from: texus on 09 December 2015, 23:58:33
While writing the example I actually figured out that you can do it without any change in TGUI with exactly the same amount of lines of code:
Code (cpp) Select
#include <TGUI/TGUI.hpp>

tgui::ListBox::Ptr popumMenu;

void popupMenuCallback(std::string item)
{
    std::cout << item << std::endl;
}

void rightClickCallback(tgui::Gui& gui, sf::Vector2f position)
{
    popumMenu = std::make_shared<tgui::ListBox>();
    popumMenu->addItem("Option 1");
    popumMenu->addItem("Option 2");
    popumMenu->addItem("Option 3");
    popumMenu->addItem("Option 4");
    popumMenu->setItemHeight(20);
    popumMenu->setPosition(position);
    popumMenu->setSize(120, popumMenu->getItemHeight() * popumMenu->getItemCount());
    popumMenu->connect("ItemSelected", popupMenuCallback);
    gui.add(popumMenu);
}

int main()
{
    sf::RenderWindow window(sf::VideoMode(800, 600), "TGUI window");
    tgui::Gui gui(window);
    gui.setFont("TGUI/fonts/DejaVuSans.ttf");

    while(window.isOpen())
    {
        sf::Event event;
        while(window.pollEvent(event))
        {
            if(event.type == sf::Event::Closed)
                window.close();

            // Check if there is a pop-up menu
            if (popumMenu)
            {
                // When mouse is released, remove the pop-up menu
                if (event.type == sf::Event::MouseButtonReleased && event.mouseButton.button == sf::Mouse::Left)
                {
                    gui.remove(popumMenu);
                    popumMenu = nullptr;
                }

                // When mouse is pressed, remove the pop-up menu only when the mouse is not on top of the menu
                if (event.type == sf::Event::MouseButtonPressed)
                {
                    if (!popumMenu->mouseOnWidget(event.mouseButton.x, event.mouseButton.y))
                    {
                        gui.remove(popumMenu);
                        popumMenu = nullptr;
                    }
                }
            }

            // Perhaps we have to open a menu
            else if (event.type == sf::Event::MouseButtonPressed && event.mouseButton.button == sf::Mouse::Right)
            {
                rightClickCallback(gui, sf::Vector2f(event.mouseButton.x, event.mouseButton.y));
            }

            gui.handleEvent(event);
        }

        window.clear();
        gui.draw();
        window.display();
    }
}


hello all

i'm unable to get

int pop_item=popumMenu->getSelectedItemIndex();                   
cout << " popUP item is " << pop_item << endl;


pop_item is always -1

Debian stable 32 bit
sfml 2.4.1
tgui 0.8.4

thnx


texus

You should show the code you are using because it isn't the same as the example you quoted.

serhio

Quote from: texus on 13 March 2019, 19:51:41
You should show the code you are using because it isn't the same as the example you quoted.

the issue with eHandler has been resolved.

sorry for bother you.

ps: nice lib. thnx.