scrollbar example code

Started by starkhorn, 28 March 2015, 03:11:25

starkhorn

Hey,

I've followed the scroll bar example code (really awesome example btw).
https://tgui.eu/example-code/v06/scrollable-panel/

I got it working as it was exactly defined, but when I tried to put the scrollPanel function in a class, I get hundreds of errors. I'm using v0.6, sfml2.2 and visual studio express 2012 on windows.

I get this error:-

Code (cpp) Select

Error 2 error C3867: 'scrollTest::scrollPanel': function call missing argument list; use '&scrollTest::scrollPanel' to create a pointer to member


Then the bind throw lots of errors like below. Any ideas?

Code (cpp) Select

Error 3 error C2780: 'enable_if<!std::is_same<_Ty1,_Ty2>::value,std::_Bind<true,_Ret,std::_Pmf_wrap<_Rx(__thiscall _Farg0::* )(_V0_t,_V1_t,_V2_t,_V3_t,_V4_t) volatile const,_Rx,_Farg0,_V0_t,_V1_t,_V2_t,_V3_t,_V4_t>,_Vx0_t,_Vx1_t,_Vx2_t,_Vx3_t,_Vx4_t>>::type std::bind(_Rx (__thiscall _Farg0::* const )(_V0_t,_V1_t,_V2_t,_V3_t,_V4_t) volatile const,_Vx0_t &&,_Vx1_t &&,_Vx2_t &&,_Vx3_t &&,_Vx4_t &&)' : expects 6 arguments - 3 provided



Here is the modified code, basically, I put the function into a class.

Code (cpp) Select


#include <TGUI/TGUI.hpp>

class scrollTest
{
public:
scrollTest();
void scrollPanel(tgui::Panel::Ptr panel, const tgui::Callback& callback);

private:
int previousScrolbarValue;
};

scrollTest::scrollTest()
{
previousScrolbarValue = 0;
}


// Function that will be called when scrolling
void scrollTest::scrollPanel(tgui::Panel::Ptr panel, const tgui::Callback& callback)
{
    int distanceToMove = previousScrolbarValue - callback.value;

    // Move all widgets that are inside the panel
    for (auto& widget : panel->getWidgets())
        widget->setPosition(widget->getPosition().x, widget->getPosition().y + distanceToMove);

    previousScrolbarValue = callback.value;
}

int main()
{
    sf::RenderWindow window(sf::VideoMode(800, 600), "TGUI window");
    tgui::Gui gui(window);
scrollTest scrollClass;

    if (gui.setGlobalFont("C:/Users/mcgoldrickb/Documents/TGUI-0.6/fonts/DejaVuSans.ttf") == false)
        return 1;

    // Create the panel
    tgui::Panel::Ptr panel(gui);
    panel->setPosition(50, 50);
    panel->setSize(240, 360);
    panel->setBackgroundColor(sf::Color(200, 200, 200));

    // Add some widgets to it (image1.png to image5.png)
    for (unsigned int i = 1; i <= 5; ++i)
    {
        tgui::Picture::Ptr pic(*panel);
        pic->load("C:/Users/mcgoldrickb/Documents/Visual Studio 2012/Projects/romeGameGUI_noViews/images/arrow_" + std::to_string(i) + ".png");
        pic->setSize(240, 180);
        pic->setPosition(0, (i-1) * 180);
    }

    // Add a scrollbar
    // Note that we add it to the gui and not to the panel.
    // Doing so allows us to easily move everything inside the panel when scrolling
    tgui::Scrollbar::Ptr scrollbar(gui);
    scrollbar->load("C:/Users/mcgoldrickb/Documents/TGUI-0.6/widgets/Black.conf");
    scrollbar->setSize(20, 360);
    scrollbar->setPosition(panel->getPosition() + sf::Vector2f(panel->getSize().x, 0));
    scrollbar->setArrowScrollAmount(30);
    scrollbar->setLowValue(360); // Viewable area (height of the panel)
    scrollbar->setMaximum(5 * 180); // Total area (height of the 5 images)

    // Call the scrollPanel function that we defined above when scrolling
    scrollbar->bindCallbackEx(std::bind(scrollClass.scrollPanel, panel, std::placeholders::_1), tgui::Scrollbar::ValueChanged);

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

            gui.handleEvent(event);
        }

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

    return 0;
}


texus

There are two mistakes with the bindCallbackEx call (both are details about how c++ works).

In order to reference to a function inside a class, you must pass "&ClassName::functionName".

The second mistake is really an internal detail of c++. I will quote from my v0.7 tutorial where I explained it better than in the v0.6 one (where I just briefly mention it).
Quote from: https://tgui.eu/tutorials/v07/signals-introduction/Member functions are special. You must first understand a technical detail in c++ in order to bind them.

Suppose you have the following class:

class MyClass
{
    void function(int i);
}

That function takes one parameter, right? Wrong.
That function takes 2 parameters. Behind the scenes the function looks like this:

void function(MyClass* this, int i);

That is where your 'this' pointer is coming from.

So the function you want to call does not has 2 parameters, it has 3. You have to provide the "this" pointer which is a pointer to your class instance.

Which means that the line should be the following:
scrollbar->bindCallbackEx(std::bind(&scrollTest::scrollPanel, &scrollClass, panel, std::placeholders::_1), tgui::Scrollbar::ValueChanged);

starkhorn

many thanks again Texus - worked a treat. Thanks for explaining why as well.

starkhorn

Hi Texus,

Sorry to ask another question. I'm having a situation with the scroll-bars, where I only want the scrollbar to appear when the content expands to a size that is outside of the panel. So this means, I'm updating the maximum as the content grows. However if I then call the setMaximum, the callback function gets called as I assume the trigger of tgui::ScrollBar::ValueChanged gets hit when I update the maximum.

so one idea I had was prior to changing the maximum, was to unbind the function callback, change the maximum and then rebind it again. Question is how do I get the callback function details? i.e. as per the below pseudo code. I temporarily stored the original scrollbar prior to changes. I unbind and change the maximum. How can I get the the bind function from the original scrollbar so that I can re-set it? (BTW getScrollbar just retrieves the scrollbar ptr from a std::vector based on widget name.

Code (cpp) Select


if (IsScollBarAlreadyInList(scrollbarName))
{
tgui::Scrollbar origScrollbar = *getScrollbar(scrollbarName);

getScrollbar(scrollbarName)->unbindAllCallback();
getScrollbar(scrollbarName)->setMaximum(passed_MaxAmount);
}


texus

QuoteHowever if I then call the setMaximum, the callback function gets called as I assume the trigger of tgui::ScrollBar::ValueChanged gets hit when I update the maximum.
This should not happen in the first place. I just checked the code and although it tries to set the value to 0, the callback function will only be called if the value is actually changed, so when it wasn't 0 already. So calling just setMaximum should not trigger the callback function. So can you write a minimal code that reproduces it?

Is it so bad that the callback function gets called?
You could always compare scrollbar->getMaximum and scrollbar->getLowValue in the callback function and simply return from the function when you find the scrollbar is invisible.

QuoteQuestion is how do I get the callback function details?
In v0.6 it is not yet possible to unbind a specific function. Right now you can only unbind all callbacks connected to a certain trigger. But normally you don't have multiple callback connected to ValueChanged, so you can just do "scrollbar->unbindCallback(tgui::Scrollbar::ValueChanged);".

But I will make the bindCallback function return an id and add a unbindCallbackById function in v0.6.8 to have a bit more control.

QuoteHow can I get the the bind function from the original scrollbar so that I can re-set it?
You can't but I will see if I can add a "retrieveCallback(id)" function as well.

starkhorn

Ok so here is the code where i can see it happeneing.

Basically, what I figured out (after my post last night), is that during a function call I set the maximum so that the scrollbar appears. Then later the maximum amount gets updated where it is less than the low amount. At this point the callback function gets called which in turn updates the previousRightPanelScrollbarValueX value so all of the widgets got back to their original position.

Obviously I should likely ensure that I only update the maximum if it my update value is larger than the current maximum value. However I thought you just let you know how/where I see this behaviour. Unsure if this is what you'd expect?

Code (cpp) Select

#include <TGUI/TGUI.hpp>

class scrollTest
{
public:
scrollTest();

void scrollRightPanelHorizontal(tgui::Panel::Ptr panel, const tgui::Callback& callback);
void updateScrollBarMaxAmount(int passed_MaxAmount, tgui::Scrollbar::Ptr passed_scrollbar);
int previousRightPanelScrollbarValueX;

private:


};

scrollTest::scrollTest()
{
previousRightPanelScrollbarValueX = 0;
}

void scrollTest::scrollRightPanelHorizontal(tgui::Panel::Ptr panel, const tgui::Callback& callback)
{
    int distanceToMove = previousRightPanelScrollbarValueX - callback.value;

    // Move all widgets that are inside the panel
    for (auto& widget : panel->getWidgets())
{
        widget->setPosition(widget->getPosition().x + distanceToMove, widget->getPosition().y);
}

    previousRightPanelScrollbarValueX = callback.value;
}

void scrollTest::updateScrollBarMaxAmount(int passed_MaxAmount, tgui::Scrollbar::Ptr passed_scrollbar)
{
passed_scrollbar->setMaximum(passed_MaxAmount);
}




int main()
{
    sf::RenderWindow window(sf::VideoMode(800, 600), "TGUI window");
    tgui::Gui gui(window);
scrollTest scrollClass;

    if (gui.setGlobalFont("E:/programming/TGUI-0.6/fonts/DejaVuSans.ttf") == false)
        return 1;

    // Create the panel
    tgui::Panel::Ptr panel(gui);
    panel->setPosition(50, 50);
    panel->setSize(240, 360);
    panel->setBackgroundColor(sf::Color(200, 200, 200));

    // Add some widgets to it (image1.png to image5.png)
    for (unsigned int i = 1; i <= 5; ++i)
    {
        tgui::Picture::Ptr pic(*panel);
        pic->load("E:/programming/projects/romeGameGUI_noViews/images/arrow_" + std::to_string(i) + ".png");
        pic->setSize(240, 180);
        pic->setPosition(0, (i-1) * 180);
    }

    // Add a scrollbar
    // Note that we add it to the gui and not to the panel.
    // Doing so allows us to easily move everything inside the panel when scrolling
    tgui::Scrollbar::Ptr scrollbar(gui);
    scrollbar->load("E:/programming/TGUI-0.6/widgets/Black.conf");
    scrollbar->setSize(20, 360);
    scrollbar->setPosition(panel->getPosition() + sf::Vector2f(panel->getSize().x, 0));
scrollbar->setVerticalScroll(false);
    scrollbar->setArrowScrollAmount(15);
    scrollbar->setLowValue(360); // Viewable area (height of the panel)
    scrollbar->setMaximum(360); // so scroll-bar should not go visible

    // Call the scrollPanel function that we defined above when scrolling
scrollbar->bindCallbackEx(std::bind(&scrollTest::scrollRightPanelHorizontal, &scrollClass, panel, std::placeholders::_1), tgui::Scrollbar::ValueChanged);
 

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

            gui.handleEvent(event);
        }

scrollClass.updateScrollBarMaxAmount(580, scrollbar); //becomes visible

if (scrollClass.previousRightPanelScrollbarValueX > 30)
{
scrollClass.updateScrollBarMaxAmount(360, scrollbar); //here where the max amount goes down. when the set maximum is called it calls the bindCallbackEx function
}

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

    return 0;
}

texus

Ok, that explains why the callback gets created. But the behavior of the gui seems to be correct because the value really does change during the process.

But what exactly are you trying to solve here? The callback function is called because you first set the maximum too high and then you set it back. But for what situation are you putting it higher first?

Also it shouldn't be a problem that the callback function gets called again. If it is then there is probably some code in there that doesn't belong there. A function like the scrollRightPanelHorizontal in your example can be called multiple times without problems.

starkhorn

Well when scrollRightPanelHorizontal gets called when I put the max amount to a value under low amount, it resets previousRightPanelScrollbarValueX to 0 as the callback.value is zero. So all of the widgets move back to the original position.

So the scenario I have is:-

1:- 1 large panel with a vertical scrollbar.
2:- There are 2 columns of labels within this single panel that I want to display. Each column can have different number of rows independently
3:- So column 1, has say 4 rows which is less than the low amount. I call the updateMaxAmount script which sets to that value and the scrollbar doesn't get displayed. Normal.
4:- Column 2, has 10 rows and this exceeds the low amount. So I call the updateMaxAmount script. Maxamount gets updates. Scrollbar displays - all perfect.
5:- I push the scrolldown button. It moves down. The labels in both columns moved down by the move amount - again exactly what I want.
6:- However during the next pass of the function, updateMaxAmount gets called for column 1 again which is less than lowermount and all of the widgets have their position reset back to zero.

I'm pretty sure my solution is not to update maxamount at step3 and only do so if the update amount if greater than current max amount.

texus

I would expect there to be a step 7: updateMaxAmount gets called for column 2 again and everything is correct again.

What about finding out which column has the most rows and set the maximum once with that value?

I guess you should just pick any method that works, even if it is a bit of a hack.