Help us understand the problem. What is going on with this article?

SIv3DのGUIでスピンボックス作ってみた

More than 3 years have passed since last update.

Siv3Dの機能をいくつか組み合わせてスピンボックスを試作しました。
C#のFormだとNumericUpDownですが、名前をどっちにするかが悩みどころ。

Siv3Dのバージョンは2015(June)。

ソース

C++
#include <Siv3D.hpp>
template<typename T>
class GUIValueEditor
{
public:
    typedef std::shared_ptr<s3d::GUITextField> TextFiledPtr;
    typedef std::shared_ptr<s3d::GUIText> TextPtr;
    typedef std::shared_ptr<s3d::GUIButton> ButtonPtr;
    TextFiledPtr textField;
    TextPtr name;
    ButtonPtr plusButton;
    ButtonPtr minusButton;
    T value_ = T();
    T preValue = T();
public:
    T& value(){ return value_; }
    // 初回処理 ただし、複数回呼んでも別にバグらない
    void InitWidget()
    {
        {
            textField = std::make_shared<s3d::GUITextField>();
            textField->m_style.width = 56;
            textField->setMaxLength(6);
            textField->setText(s3d::Format(value_));
        }
        const s3d::int32 buttonSize = 30;
        {
            plusButton = std::make_shared<s3d::GUIButton>();
            plusButton->getText() = L'+';
            plusButton->m_style.margin = -5;
            plusButton->m_style.width = buttonSize;
            plusButton->m_style.padding = 10;
        }
        {
            minusButton = std::make_shared<s3d::GUIButton>();
            minusButton->getText() = L'-';
            minusButton->m_style = plusButton->m_style;
        }
    }
    void LinkGUI(const s3d::String& name, s3d::GUI& gui)
    {
        InitWidget();

        const bool linkOK = (textField != nullptr) && (plusButton != nullptr) && (minusButton != nullptr);
        if (linkOK)
        {
            gui.add(s3d::GUIText::Create(name));
            gui.add(textField);
            gui.add(plusButton);
            gui.addln(minusButton);
        }
    }

    bool InputComplete()const
    {
        return textField->hasChanged && (!textField->getActive());
    }
    // 入力内容に合わせて、数値とテキストを同期させる
    void TextUpdate()
    {
        const auto currentVal = s3d::FromStringOpt<T>(textField->getText());
        if (currentVal)
        {
            this->value_ = *currentVal;
        }
        else{
            textField->setText(s3d::Format(value_));
        }

    }
    void readText()
    {
        if (textField->getActive() && Input::MouseL.clicked)
        {
            if (!textField->mouseOver)
            {
                TextUpdate();
                return;
            }
        }

        if (InputComplete())
        {
            TextUpdate();
        }

        return;
    }


    void update()
    {
        assert(textField);
        assert(plusButton);
        assert(minusButton);
        if (plusButton->pushed){ ++value_; }
        if (minusButton->pushed){ --value_; }

        if (preValue != value_)
        {
            this->textField->setText(s3d::Format(value_));
        }
        readText();
        preValue = value_;
    }
};

void Main()
{
    const s3d::String girdSizeY = L"girdSizeY";
    const s3d::String girdSizeX = L"girdSizeX";
    GUIStyle st;
    GUI gui(GUIStyle::Default);
    gui.setTitle(L"サイズの設定");
    gui.style.movable = true;
    GUIValueEditor<int> gve,gve2;
    gve.LinkGUI(L"sizeX",gui);
    gve2.LinkGUI(L"sizeY",gui);
    while (System::Update())
    {
        gve.update();
        gve2.update();
    }

}

実行結果(数値は実際に入力して変更したもの)
spinbox.png

おまけ

GUITextFieldは(textField->hasChanged && (!textField->getActive()))で入力完了のタイミングを拾えた。
本来の入力完了以外に、何らかの入力をしたけどフォーカスを外して他を操作したいパターンで入力を保持するのに使える。
この書き方はGUIValueEditor::InputCompleteで使っている。

数値と文字列の変換はs3d::Formatとs3d::FromStringOptに任せている。
この部分をフックできるようにすれば、さらなる拡張も可能。
ただ、そこまでやるならSiv3D本体にこれを調整して入れられないか考えている。

今後の課題

プラスボタンとマイナスボタンがウィジェットと一体化できればかなりスッキリしそうなので、そこを何とかしたい。
TextFieldを使うのは必須だが、継承で動作を書き換えられないので中間層になるクラスが欲しくなる。

Sigureya
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした