めのん@ひとりプログラミング同好会

このブログはすべてフィクションであり、実在の人物、団体とは一切関係ありません

OpenSiv3DのSimpleGUI::Button

こんにちは、めのんです!

いつも土日はOpenSiv3Dの話題だったんですけど、いろいろ事情があって土曜日は別の話題になってしまいました。 でも今日はOpenSiv3Dの話題ですよ。

先週、OpenSiv3DとBoost.Asioを使ってsACNでLED器具(に見立てたエミュレーション画面)を制御してみました。

menonfled.hateblo.jp

前回は受信側だけOpenSiv3Dを使ったので、次の目標としては送信側にOpenSiv3Dで操作部を作ろうと考えています。 必要になるのはボタンとフェーダー(スライダーで実現できると思っています)なんですが、まずは簡単そうなボタンから調べていくことにします。

OpenSiv3Dのボタンについては、9. GUI - Siv3Dを参考にしました。 SimpleGUIというだけあって本当にシンプルです。

設定できることがほとんどなくて、ボタンの外観やフォント、色なんかも設定できそうな雰囲気がありません。 それより一番困ったのが、ボタンを押下したことしかわからないことです。

LED器具の制御の場合、「ピアノ」というらしいんですけど、ボタンを押したときに点灯させて離したときに消灯させるという操作が普通にあるようです。 押したことはわかっても離したことがわからないと実現できませんね。

しかたがないのでOpenSiv3Dのソースコードを読んでみました。 Siv3D/src/Siv3D/SimpleGUI/SimpleGUI.cppにSimpleGUI::Button関数の定義は見つかりました。

bool Button(const String& label, const Vec2& pos, const Optional<double>& _width, const bool enabled)
{
    const Vec2 center = ButtonRegion(label, pos, _width).center();

    return ButtonAt(label, center, _width, enabled);
}

bool ButtonAt(const String& label, const Vec2& center, const Optional<double>& _width, const bool enabled)
{
    const Font font = detail::GetSimpleGUIFont();

    const int32 labelWidth = font(label).region().w;
    const double width = _width.value_or(labelWidth + 40);

    const RectF rect(Arg::center = center, width, 36);
    const Vec2 labelPos(static_cast<int32>(rect.x + (width - labelWidth) / 2), center.y - font.height() / 2);

    const bool mouseOver = enabled && rect.mouseOver();
    const bool pushed = mouseOver && Cursor::OnClientRect() && MouseL.down();

    if (enabled)
    {
        rect.rounded(4.8)
            .draw(mouseOver ? ColorF(0.92, 0.96, 1.0) : ColorF(1.0))
            .drawFrame(1, 0, ColorF(0.67, pushed ? 0.0 : 1.0));

        font(label).draw(labelPos, ColorF(0.2));
    }
    else
    {
        rect.rounded(4.8)
            .draw(ColorF(0.92))
            .drawFrame(1, 0, ColorF(0.67));

        font(label).draw(labelPos, ColorF(0.67));
    }

    if (mouseOver)
    {
        Cursor::RequestStyle(CursorStyle::Hand);
    }

    return pushed;
}

ButtonAt関数はボタンの中心座標でボタンの位置を指定するようで、Button関数は左上の頂点座標で指定するようです。

それはそうと、ButtonAt関数の次の部分がヒントになりそうです。

 const bool mouseOver = enabled && rect.mouseOver();
    const bool pushed = mouseOver && Cursor::OnClientRect() && MouseL.down();

これを見様見真似すれば、ボタンを離したときだけじゃなくて、右ボタンのクリックやマウスカーソルを動かしたときのイベントも取れそうな気がしてきました。

繰り返しになりますが、SimpleGUIは本当にシンプルな機能しかありません。 実際の製品で使うには、商品らしい高級感あふれるデザインにしないといけませんし、機能的にももう少し充実させる必要があります。 でも、SimpleGUIのソースコードを読めば、それらを実現するためのヒントが詰まっているように思いました。

将来的にはSimpleGUIならぬRichGUIみたいなのを自作してみたいですね。

それでは!!