SIv3DのGUIでスピンボックス作ってみた
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();
}
}
#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();
}
}
おまけ
GUITextFieldは(textField->hasChanged && (!textField->getActive()))で入力完了のタイミングを拾えた。
本来の入力完了以外に、何らかの入力をしたけどフォーカスを外して他を操作したいパターンで入力を保持するのに使える。
この書き方はGUIValueEditor::InputCompleteで使っている。
数値と文字列の変換はs3d::Formatとs3d::FromStringOptに任せている。
この部分をフックできるようにすれば、さらなる拡張も可能。
ただ、そこまでやるならSiv3D本体にこれを調整して入れられないか考えている。
今後の課題
プラスボタンとマイナスボタンがウィジェットと一体化できればかなりスッキリしそうなので、そこを何とかしたい。
TextFieldを使うのは必須だが、継承で動作を書き換えられないので中間層になるクラスが欲しくなる。
Author And Source
この問題について(SIv3DのGUIでスピンボックス作ってみた), 我々は、より多くの情報をここで見つけました https://qiita.com/Sigureya/items/7eb87232fab80cc207a7著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .