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.