Main Menu
Menu

Show posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.

Show posts Menu

Messages - Kvaz1r

#26
Panel holder is a control, which manages multiple windows with associated tabs. This control also known either as Tab container or Notebook widget. It's popular widget so it would be good to have such control in TGUI.

I've made such simple implementation that I current use in own project:

class tgPanelHolder : public tgui::SubwidgetContainer
{
public:

    typedef std::shared_ptr<tgPanelHolder> Ptr; //!< Shared widget pointer
    typedef std::shared_ptr<const tgPanelHolder> ConstPtr; //!< Shared constant widget pointer

    tgPanelHolder(const tgui::Layout2d& size = { "100%", "100%" }) : m_index(-1)
    {
        m_type = "PanelHolder";
        setSize(size);
        m_tabs = tgui::Tabs::create();
        m_tabs->setSize({ size.x, m_tabs->getSize().y * 2 });
        m_tabs->onTabSelect([this]()
            {
                auto cur = m_tabs->getSelectedIndex();
                Select(cur);
                if (m_tabs->getSelectedIndex() != m_index)
                {
                    m_tabs->select(m_index);
                }
            });

        m_container->add(m_tabs, "Tabs");
    }

    static tgPanelHolder::Ptr create(const tgui::Layout2d& size = { "100%", "100%" })
    {
        return std::make_shared<tgPanelHolder>(size);
    }

    static tgPanelHolder::Ptr copy(tgPanelHolder::ConstPtr pholder)
    {
        if (pholder)
            return std::static_pointer_cast<tgPanelHolder>(pholder->clone());
        else
            return nullptr;
    }

    Widget::Ptr clone() const override
    {
        return std::make_shared<tgPanelHolder>(*this);
    }

    void addPanel(tgui::Panel::Ptr ptr, const tgui::String& name, bool select = true)
    {
        auto size = getSizeLayout();
        ptr->setSize({ size.x , size.y - m_tabs->getSize().y });
        ptr->setPosition({ tgui::bindLeft(m_tabs), tgui::bindBottom(m_tabs) });
        m_panels.push_back(ptr);
        m_tabs->add(name, select);
        if (select)
        {
            Select(m_panels.size() - 1, false);
        }
    }

    bool addPanelAt(tgui::Panel::Ptr ptr, const tgui::String& name, std::size_t index, bool select = true)
    {
        if (index > m_panels.size())
        {
            return false;
        }

        addPanel(ptr, name, select);
        auto size = m_panels.size();
        if (index != size)
        {
            std::swap(m_panels[index], m_panels[size - 1]);
        }
    }

    void RemovePanel(tgui::Panel::Ptr ptr)
    {
        if (ptr != nullptr)
        {
            auto idx = getIndex(ptr);
            if (idx != -1)
            {
                m_tabs->remove(idx);
                m_container->remove(m_panels[idx]);
                m_panels.erase(m_panels.begin() + idx);
                if (idx == 0)
                {
                    Select(m_panels.size() - 1);
                }
                else
                {
                    Select(idx - 1);
                }
            }
        }
    }

    void Select(std::size_t index, bool genEvents = true)
    {
        if (index >= m_panels.size() || index == static_cast<std::size_t>(m_index))
        {
            return;
        }
        if (genEvents)
        {
            bool isVetoed = false;
            onSelectionChanging.emit(this, index, &isVetoed);
            if (isVetoed)
            {
                return;
            }
        }

        if (m_container->getWidgets().size() == 2)
        {
            m_container->remove(m_panels[m_index]);
        }
        m_container->add(m_panels[index]);
        m_tabs->select(index);
        m_index = index;
        if (genEvents)
        {
            onSelectionChanged.emit(this, m_index);
        }
    }

    std::size_t Count() const
    {
        return m_panels.size();
    }

    int getIndex(tgui::Panel::Ptr ptr)
    {
        for (std::size_t i = 0; i < m_panels.size(); i++)
        {
            if (m_panels[i] == ptr)
            {
                return static_cast<int>(i);
            }
        }
        return -1;
    }

    tgui::Panel::Ptr getSelected()
    {
        return getPanel(m_index);
    }

    int getSelectedIndex() const
    {
        return m_index;
    }

    tgui::Panel::Ptr getPanel(int index)
    {
        if (index < 0 || index >= static_cast<int>(m_panels.size()))
        {
            return nullptr;
        }
        return m_panels[index];
    }

    tgui::Tabs::Ptr getTabs()
    {
        return m_tabs;
    }

    tgui::String getTabText(std::size_t index) const
    {
        return m_tabs->getText(index);
    }

    bool changeTabText(std::size_t index, const tgui::String& text)
    {
        return m_tabs->changeText(index, text);
    }

public:
    tgui::SignalInt onSelectionChanged = { "SelectionChanged" };
    tgui::SignalTyped2<int, bool*> onSelectionChanging = { "SelectionChanging" }; //can be vetoed

private:
    std::vector<tgui::Panel::Ptr> m_panels;
    int m_index;

    tgui::Tabs::Ptr m_tabs;
};


If it's OK - I'm going to open PR with proper changes (code style, tests, ...)
#27
Feature requests / Re: SpinCtrl
17 July 2020, 23:54:56
Wow, mouse cursors will be really cool addition.

Yeah, I'm sure that I missed something (except code style of course). I only now found that widgets based on SubwidgetContainer don't have load/save support out-of-the-box. And I don't see how it should be implemented (I haven't big experience with any DSL-based GUI therefore not use it at all so it's not a problem for me).
#28
Feature requests / Re: SpinCtrl
15 July 2020, 09:54:07
Quote from: texus on 14 July 2020, 19:31:51
Does it really matter that if the user typed "123text", we keep the "123" instead of resetting the value? I don't think anyone is even going to notice the behavior (because you typically do type actual digits in it). And I would kind of consider it a feature that it keeps the value I typed if I accidentally hit another button together with the enter key :).
Maybe the edit box should have Validator::Float set, that way you can't even type text or have multiple commas.
Yeah, I didn't think about that. Updated with validator. 
#29
Feature requests / Re: SpinCtrl
14 July 2020, 15:12:13
Quote from: texus on 10 July 2020, 23:25:12
Maybe instead of choosing Int vs Float, there should be an amount of digits behind the comma? If set to 0, only ints get accepted, if set to 2 then "x", "x.a" or "x.ab" would be accepted but "x.abc" would be rounded. The default could be -1 which allows any float input.
That's indeed better.

Quote from: texus on 10 July 2020, 23:25:12
Instead of using std::stof and exceptions, I think String::toFloat should be used. Whether we should depend on the locale to parse numbers is arguable, but for now it would probably be best to rely on the code always behaving the same on every pc.
Yeah, I though about that, but String::toFloat doesn't have any way to know was full string converted or only some first part. E.g. "123text" give 123  so one need or make extra conversion back to string or something else. For now I copied simplified version of toFloat as a private function, but guess String::toFloat should contains overloading for such cases. Maybe like std::stof with parameter for storing the number of characters processed or something else.
#30
Feature requests / Re: SpinCtrl
10 July 2020, 14:51:40
I've finally finished SpinCtrl, here is code and example:


#include <TGUI/TGUI.hpp>
#include <iostream>

class SpinCtrl : public tgui::SubwidgetContainer
{
public:
    typedef std::shared_ptr<SpinCtrl> Ptr; //!< Shared widget pointer
    typedef std::shared_ptr<const SpinCtrl> ConstPtr; //!< Shared constant widget pointer

    SpinCtrl(float min = 0.0F, float max = 10.0F, float step = 1.0F, float value = 0.0F)
    {
        m_type = "SpinCtrl";

        m_spinButton = tgui::SpinButton::create();
        m_spinText = tgui::EditBox::create();
        m_spinText->setText(tgui::String(m_spinButton->getValue()));

        m_spinButton->setPosition(tgui::bindRight(m_spinText), tgui::bindTop(m_spinText));
        m_spinButton->onValueChange([this](const float value)
            {
                m_spinText->setText(tgui::String(value));
                onValueChange.emit(this, value);
            });

        m_spinButton->setMinimum(min);
        m_spinButton->setMaximum(max);
        m_spinButton->setValue(value);
        m_spinButton->setStep(step);

        m_spinText->setSize(m_spinText->getSize().x, m_spinButton->getSize().y);
        m_spinText->onTextChange([this](const tgui::String& text)
            {
                auto curValue = m_spinButton->getValue();
                try
                {
                    std::size_t pos = 0;
                    float value = std::stof(text.toAnsiString(), &pos);
                    if (pos != text.size() || !inRange(value))
                    {
                        m_spinText->setText(tgui::String(curValue));
                    }
                    else if (curValue != value)
                    {
                        m_spinButton->setValue(value);
                    }
                }
                catch (...)
                {
                    m_spinText->setText(tgui::String(curValue));
                }
            });
        m_container->add(m_spinText);
        m_container->add(m_spinButton);
        auto butSize = m_spinButton->getSize();
        auto txtSize = m_spinText->getSize();
        setSize({ butSize.x + txtSize.x, butSize.y });
    }
    static SpinCtrl::Ptr create(float min = 0.0F, float max = 10.0F, float step = 1.0F, float value = 0.0F)
    {
        return std::make_shared<SpinCtrl>(min, max, step, value);
    }
    static SpinCtrl::Ptr copy(SpinCtrl::ConstPtr spinctrl)
    {
        if (spinctrl)
            return std::static_pointer_cast<SpinCtrl>(spinctrl->clone());
        else
            return nullptr;
    }
    Widget::Ptr clone() const override
    {
        return std::make_shared<SpinCtrl>(*this);
    }
    bool SetValue(const float value)
    {
        if (inRange(value) && m_spinButton->getValue() != value)
        {
            m_spinButton->setValue(value);
            m_spinText->setText(tgui::String(value));
            return true;
        }
        return false;
    }
    void setMinimum(const float min)
    {
        m_spinButton->setMinimum(min);
    }
    void setMaximum(const float max)
    {
        m_spinButton->setMaximum(max);
    }
    void setStep(const float inc)
    {
        m_spinButton->setStep(inc);
    }
    float getValue() const
    {
        return m_spinButton->getValue();
    }
    float getMinimum() const
    {
        return m_spinButton->getMinimum();
    }
    float getMaximum() const
    {
        return m_spinButton->getMaximum();
    }
    float getStep() const
    {
        return m_spinButton->getStep();
    }
    tgui::SpinButtonRenderer* getSpinButtonRenderer()
    {
        return m_spinButton->getRenderer();
    }
    tgui::SpinButtonRenderer* getSpinButtonSharedRenderer()
    {
        return m_spinButton->getSharedRenderer();
    }
    tgui::EditBoxRenderer* getSpinTextRenderer()
    {
        return m_spinText->getRenderer();
    }
    tgui::EditBoxRenderer* getSpinTextSharedRenderer()
    {
        return m_spinText->getSharedRenderer();
    }

    tgui::SignalTyped<float> onValueChange = { "ValueChanged" };

private:
    bool inRange(const float value) const
    {
        return m_spinButton->getMinimum() <= value && value <= m_spinButton->getMaximum();
    }

    tgui::SpinButton::Ptr m_spinButton;
    tgui::EditBox::Ptr m_spinText;
};

class MyFrame
{
public:
    MyFrame()
    {
        window.create(sf::VideoMode(800, 600), "SpinCtrl");
        gui.setTarget(window);
        auto spin = SpinCtrl::create(0, 10, 0.2F, 5);
        spin->getSpinTextRenderer()->setBackgroundColor(tgui::Color(205, 14, 98));
        spin->getSpinTextRenderer()->setBackgroundColorHover(tgui::Color(205, 14, 98));
        spin->getSpinButtonRenderer()->setBackgroundColor(tgui::Color(138, 250, 134));
        spin->getSpinButtonRenderer()->setBackgroundColorHover(tgui::Color(138, 250, 134));
        gui.add(spin);

        spin->onValueChange([](float value) { std::cout << "New value is " << value << '\n'; });
    }

    void main()
    {
        while (window.isOpen())
        {
            sf::Event event;
            while (window.pollEvent(event))
            {
                if (event.type == sf::Event::Closed)
                    window.close();
                gui.handleEvent(event);
            }
            gui.draw();
            window.display();
        }
    }
    sf::RenderWindow window;
    tgui::Gui gui;
};

int main()
{
    MyFrame().main();
}


If such implementation is ok, I am going to open PR (will split onto .h/.cpp and add tests).

As an option maybe it make sense to add method createIntegerSpinCtrl and set in that method Integer Validator.
#31
Help requests / Re: listView and symbols
28 May 2020, 17:50:08
Do you use unicode character set in your project?
About the “Character set” option in Visual Studio
#32
Help requests / Re: listView and symbols
28 May 2020, 16:59:34
It has nothing to do with ListView.
Use wide string for any non-Ascii symbols:

#include <TGUI/TGUI.hpp>

class MyFrame
{
public:
    MyFrame()
    {
        window.create(sf::VideoMode(800, 600), "MCVE");
        gui.setTarget(window);

        auto listView = tgui::ListView::create();
        listView->setSize(window.getSize().x, window.getSize().y);

        listView->addColumn(L"Key");
        listView->addColumn(L"Value");
        sf::String test = "test";
        test += L"â,¬";
        listView->addItem({"0" ,test});

        gui.add(listView);
    }
    void main()
    {
        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();
        }
    }
    sf::RenderWindow window;
    tgui::Gui gui;
};

int main()
{
    MyFrame().main();
}
#33
Yes, it exactly the reason, thank you.
#34
Interesting topic, I (too?) not understand how to draw on tgui::Canvas.

I thought this code :

#include <TGUI/TGUI.hpp>

sf::VertexArray test_draw()
{
    sf::VertexArray test(sf::Quads, 4);
   
    test[0].position = sf::Vector2f(0, 0);
    test[0].color = sf::Color::Red;
    test[1].position = sf::Vector2f(50, 0);
    test[1].color = sf::Color::Red;
    test[2].position = sf::Vector2f(50, 70);
    test[2].color = sf::Color::Red;
    test[3].position = sf::Vector2f(0, 70);
    test[3].color = sf::Color::Red;
    return test;
}

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

    auto panel = tgui::Panel::create();

    auto canvas = tgui::Canvas::create();
    canvas->clear();
    canvas->draw(test_draw());
    canvas->display();
    panel->add(canvas);

    gui.add(panel);

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

gui.handleEvent(event); // Pass the event to the widgets
}

window.clear();
gui.draw(); // Draw all widgets
        //window.draw(test_draw()); //draw fine
window.display();
}
}


will draw red square but it doesn't draw anything. What am I missing?
Drawing via window (commented line) works fine. 
#35
Why not define class and keep your objects there?
#36
Quote from: texus on 18 May 2020, 22:27:58
The exception that you had in ProgressBar has nothing to do with this. That issue was already fixed.
You were also getting the GL_INVALID_OPERATION error, but there are two possibilities:
- Or it had nothing to do with the exception that you were getting and the GL_INVALID_OPERATION error was thus never fixed (I only fixed the exception issue).

I understand that that issue was fixed :)
And yes I was indeed wrong I remembered your comment about OpenGL error in Picture.cpp but not details so thought it was related.
#37
I guess it's somewhere in Picture widget.
Could you run the tests and see which widget throw the exception to confirm? Or remove them one by one from your MCVE.

I have been getting this exception earlier (on older version of VS19) in tests but not always.

Maybe it have similar root to this one - Exception when creating ProgressBar
#38
Help requests / Re: Slider thumb position
27 April 2020, 17:56:51
Can you provide more info about use case or maybe code  with demonstration example ? It's not very clear what exactly do you want.

As an option you can set some initial value and forbid change value less than this value in handling "valueChanged" signal.
#39
Excellent, first option is easy so I'll go with it ;-)
#40
I want to create listview that will place full width of window but without scrollbar when it's redundant.

MCVE:

#include <TGUI/TGUI.hpp>

class MyFrame
{
public:
    MyFrame()
    {
        window.create(sf::VideoMode(800, 600), "MCVE");
        gui.setTarget(window);

        auto listView = tgui::ListView::create();
        listView->setSize(window.getSize().x, window.getSize().y);

        listView->addColumn(L"Key");
        listView->addColumn(L"Value");

        listView->setColumnWidth(0, window.getSize().x * 0.2f);
        listView->setColumnWidth(1, window.getSize().x * 0.8f);

        gui.add(listView);
    }
    void main()
    {
        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();
        }
    }
    sf::RenderWindow window;
    tgui::Gui gui;
};

int main()
{
    MyFrame().main();
}


Is it possible?
#41
Help requests / Re: Highlighting Keywords
31 January 2020, 21:33:33
As for Q2.
You could easily extend standard widget that it will fits your needs. 
For example so(here I just added custom callback for onEnterPressed):

#include <TGUI/TGUI.hpp>
#include <iostream>
#include <functional>

template<typename Func>
class MyEditBox : public tgui::EditBox
{
public:
    using Ptr = std::shared_ptr<MyEditBox<Func>> ; ///< Shared widget pointer

    MyEditBox(Func f) : tgui::EditBox() , m_callback(f) {}

    static Ptr create(Func f)
    {
        return std::make_shared<MyEditBox>(f);
    }

    void keyPressed(const sf::Event::KeyEvent& event) override
    {
        if (event.code == sf::Keyboard::Enter)
        {
            m_callback();
        }
        EditBox::keyPressed(event);
    }

private:
    Func m_callback;
};

class MyFrame
{
public:
    MyFrame()
    {
        window.create(sf::VideoMode(800, 600), "TGUI window");
        gui.setTarget(window);
        //you could activate here callback for button with usual connect
        //in runtime
        //and disconnect it after
        //or use bool variable as flag
        auto onEnter = []()
        {
            std::cout << "Entered was pressed";
        };
        auto edit = MyEditBox<decltype(onEnter)>::create(onEnter);

        gui.add(edit);
    }
    void main()
    {
        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();
        }
    }

private:
    sf::RenderWindow window;
    tgui::Gui gui;
};

int main()
{
    MyFrame().main();
}


Another option for anti-spam would be usual Timer that runs and made "Send" button enabled only after timeout. And handler for "pressed" signal made button disabled after sending a message.

Or, for example, all messages writes to internal buffer first and it flushed by timer.

Well there are really many ways to do it ;-)
#42
Help requests / Re: Combobox signal
30 January 2020, 09:31:45
Code works well for me.
Could you rebuild TGUI with current master and check it again? This commit seems related to your issue.
#43
Help requests / Re: Combobox signal
30 January 2020, 09:16:10
Could you provide minimal reproducible example?
#44
It's not really crucial but I can try to implement it on this week.
#45
It's a bit strange that multiline input widget hasn't support setting placeholder because some users really need a hint what they should input even for big textboxes. So it would be great to have such feature.

#46
Right, "vetoing" mechanism. Maybe it worth to mention about the behaviour in documentation or gather such non-always obvious questions in special FAQ topic on the forum.
#47
I have to retrieve some information from ChildWindow when it closing, but without calling destroy/0 the window doesn't disappear.

Here is MCVE:


#include <TGUI/TGUI.hpp>
#include <iostream>

class MyFrame
{
public:
    MyFrame()
    {
        window.create(sf::VideoMode(800, 600), "TGUI window");
        gui.setTarget(window);
        panel = tgui::Panel::create();
       
        auto b = tgui::Button::create();
        b->setText("Click");
        b->connect("pressed", [this]()
            {
                auto ptr = tgui::ChildWindow::create("Test");
                ptr->connect("Closed", [this, ptr]()
                    {
                        std::cout << ptr->getTitle().toAnsiString() << '\n';
                        //ptr->destroy();  // all works if uncomment
                    });
                panel->add(ptr);
            });
        panel->add(b);
        gui.add(panel);
    }
    void main()
    {
        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();
        }
    }

private:
    sf::RenderWindow window;
    tgui::Gui gui;

    tgui::Panel::Ptr panel;
};

int main()
{
    MyFrame().main();
}


Is it correct behaviour or I am doing something wrong and there is the right way for such situation?
#48
Yes it's totally understandable that such widget is very hard to implement.
As for RichTextControl is it possible to have also image support in it? Such type also can be used quite wide. 
#49
In GUI application often need provide some report for user and supporting HTML or at least md format so to have such widget would be really great. 
#50
Help requests / Re: Close button
06 October 2019, 23:21:15
I've never used own themes so I've no idea why your code crashed, but in standard theme them defined separately. What if you also do so?
In any case better provide sample for reproducing the behavior.