RXマイコンGUI関係更新

GUI 更新

  • 本当は、GUI の前に、優先度が高いタスクが色々あるのだが・・・
  • 気軽に実装出来るのが「要」なので、色々修正、追加を行った。
  • ソースコードを精査すると、何でこんな実装になってるの?(自分で実装したのに)部分もあり、修正した。
  • GUI Widget は、プリミティブの組み合わせで描画するので、新規に何か作る場合、見た目や操作性を踏まえて修正が続く。
  • 描画は、ソフトレンダーと DRW2D エンジンを選べるが、バグがあり、その修正も行った。
  • 追加した widget は、「スピンボックス」、「トグルスイッチ」、「プログレスバー」となっている。
  • 同時に GUI_sample で実装も追加して、機能を試している。(もうそろそろ画面が一杯になった感じ・・)
  • まだ足りない物があると思うが、これくらいの種類があれば、アプリを作る場合に困らないと思える。

スピンボックス(spinbox.hpp)

  • 範囲の決まった数値を増減する機能を提供する。
  • 「長押し」で増減が加速するようにしたので、そこそこ大きい範囲でも設定出来る。
  • 他の widget とは異なり、フレームレスにしているが、やはり無いと統一性が無いので、どうするか検討中・・
  • 増減は、ボックスの左側、又は右側押すのだが、サインが、小さい四角なので、そこも甘い。
  • 三角を描画するには、描画 API を追加する必要があるので、とりあえず四角にしている。

トグルスイッチ(toggle.hpp)

  • 実際は「スライドスイッチ」だが、「トグル」の方が馴染みが良いと思ったので「toggle クラス」とした。
  • この widget は「チェックボックス」と同等の機能だが、見た目や動作がモダンなので、必要と考えた。
  • 実装は、そんなに複雑な部分は無いが、見た目や動作を考えて、試行錯誤した。

プログレスバー(progress.hpp)

  • 実装は、表示のみなので簡単だった。
  • 基本的に、輝度のみで状態を示すので、動かしてみて、吟味はした。

全体の見直し

  • widget_director クラスや、前に実装した widget も見直して、気にくわない部分を修正した。
  • API の名称なども、間際らしい部分があり、色々考えて変更した。

drw2d_mgr クラスの修正

  • フォントの描画で、クリッピング領域を設定する部分にバグがあり、修正した。
  • この修正で、ソフトレンダリングと drw2d エンジンによる描画がほぼ切り替えても同じような見た目になった。
  • 実際は、アンチエリアスの表現などが異なり、完全に一致しない。
  • drw2d エンジンによる描画の方が品質が高いので、ソフトレンダリングもモディファイしないとならない・・・

まとめ

  • GUI Widget 関係のドキュメント(使い方や仕様)が不十分なので、この辺りも強化が必要と感じている。
  • ただ、「GUI_sample/main.cpp」を観れば、使い方は判ると思う。
  • 現状では、GUI Widget の配置ツールが無いが、C++ 11 以降 constexpr などの機能を使えば、かなり複雑な配置でも精妙に出来るので、自分としては必要性を感じない。
  • widget の応答についても、「ラムダ式」で実装して、シンプルに出来るように配慮してある。
  • 組み込み機器で C 言語ベースの GUI ライブラリを使っている人が本当に気の毒でならない。(今時、良く C 言語で作るよなぁー)
  • 下に示したコードが全て、widget の表示とそれに伴う動作に必要な実装のみ、これで全てとなっている。

GUI サンプル、widget の定義:

    typedef gui::button BUTTON;
    BUTTON      button_(vtx::srect(10, 10, 80, 32), "Button");
    BUTTON      button_stall_(vtx::srect(100, 10, 80, 32), "Stall");
    typedef gui::check CHECK;
    CHECK       check_(vtx::srect(   10, 10+50, 0, 0), "Check");  // サイズ0指定で標準サイズ
    typedef gui::group<3> GROUP3;
    GROUP3      group_(vtx::srect(   10, 10+50+40, 0, 0));
    typedef gui::radio RADIO;
    RADIO       radioR_(vtx::srect(   0, 40*0, 0, 0), "Red");
    RADIO       radioG_(vtx::srect(   0, 40*1, 0, 0), "Green");
    RADIO       radioB_(vtx::srect(   0, 40*2, 0, 0), "Blue");
    typedef gui::slider SLIDER;
    SLIDER      sliderh_(vtx::srect(200, 20, 200, 0), 0.5f);
    SLIDER      sliderv_(vtx::srect(460, 20, 0, 200), 0.0f);
    typedef gui::menu MENU;
    MENU        menu_(vtx::srect(120, 70, 100, 0), "ItemA,ItemB,ItemC,ItemD");
    typedef gui::text TEXT;
    TEXT        text_(vtx::srect(240, 70, 150, 20), "16ピクセル漢字の表示サンプル~");
    typedef gui::textbox TEXTBOX;
    TEXTBOX     textbox_(vtx::srect(240, 100, 160, 80), "");
    typedef gui::spinbox SPINBOX;
    SPINBOX     spinbox_(vtx::srect(20, 220, 120, 0),
                    { .min = -100, .value = 0, .max = 100, .step = 1, .accel = true });
    typedef gui::toggle TOGGLE;
    TOGGLE      toggle_(vtx::srect(160, 220, 0, 0));
    typedef gui::progress PROGRESS;
    PROGRESS    progress_(vtx::srect(240, 220, 150, 0));

    float       progress_ratio_ = 0.0f;

GUI widget の応答部分:

    void setup_gui_()
    {
        button_.enable();
        button_.at_select_func() = [=](uint32_t id) {
            utils::format("Select Button: %d\n") % id;
            if(button_stall_.get_state() == BUTTON::STATE::STALL) {
                button_stall_.set_state(BUTTON::STATE::ENABLE);
                button_stall_.set_title("Active");
            } else if(button_stall_.get_state() == BUTTON::STATE::ENABLE) {
                button_stall_.set_state(BUTTON::STATE::STALL);
                button_stall_.set_title("Stall");
            }
        };
        button_stall_.enable();
        button_stall_.set_state(BUTTON::STATE::STALL);

        check_.enable();
        check_.at_select_func() = [=](bool ena) {
            utils::format("Select Check: %s\n") % (ena ? "On" : "Off");
            if(ena) {
                radioR_.set_base_color(DEF_COLOR::White);
                radioG_.set_base_color(DEF_COLOR::White);
                radioB_.set_base_color(DEF_COLOR::White);
                radioR_.set_font_color(DEF_COLOR::White);
                radioG_.set_font_color(DEF_COLOR::White);
                radioB_.set_font_color(DEF_COLOR::White);
            }
        };

        // グループにラジオボタンを登録
        group_ + radioR_ + radioG_ + radioB_;
        group_.enable();  // グループ登録された物が全て有効になる。
        radioR_.at_select_func() = [=](bool ena) {
            utils::format("Select Red: %s\n") % (ena ? "On" : "Off");
            if(ena) {
                radioR_.set_base_color(DEF_COLOR::Red);
                radioG_.set_base_color(DEF_COLOR::Red);
                radioB_.set_base_color(DEF_COLOR::Red);
                radioR_.set_font_color(DEF_COLOR::Red);
                radioG_.set_font_color(DEF_COLOR::Red);
                radioB_.set_font_color(DEF_COLOR::Red);
            }
        };
        radioG_.at_select_func() = [=](bool ena) {
            utils::format("Select Green: %s\n") % (ena ? "On" : "Off");
            if(ena) {
                radioR_.set_base_color(DEF_COLOR::Green);
                radioG_.set_base_color(DEF_COLOR::Green);
                radioB_.set_base_color(DEF_COLOR::Green);
                radioR_.set_font_color(DEF_COLOR::Green);
                radioG_.set_font_color(DEF_COLOR::Green);
                radioB_.set_font_color(DEF_COLOR::Green);
            }
        };
        radioB_.at_select_func() = [=](bool ena) {
            utils::format("Select Blue: %s\n") % (ena ? "On" : "Off");
            if(ena) {
                radioR_.set_base_color(DEF_COLOR::Blue);
                radioG_.set_base_color(DEF_COLOR::Blue);
                radioB_.set_base_color(DEF_COLOR::Blue);
                radioR_.set_font_color(DEF_COLOR::Blue);
                radioG_.set_font_color(DEF_COLOR::Blue);
                radioB_.set_font_color(DEF_COLOR::Blue);
            }
        };
        radioG_.exec_select();  // 最初に選択されるラジオボタン

        sliderh_.enable();
        sliderh_.at_select_func() = [=](float val) {
            utils::format("Slider H: %3.2f\n") % val;
        };
        sliderv_.enable();
        sliderv_.at_select_func() = [=](float val) {
            utils::format("Slider V: %3.2f\n") % val;
        };

        menu_.enable();
        menu_.at_select_func() = [=](uint32_t pos, uint32_t num) {
            char tmp[32];
            menu_.get_select_text(tmp, sizeof(tmp));
            utils::format("Menu: '%s', %u/%u\n") % tmp % pos % num;
        };

        text_.enable();

        textbox_.enable();
        textbox_.set_title("(1) 項目\n(2) GUI サンプルについて。\n(3) まとめ");
        textbox_.set_vertical_alignment(TEXTBOX::V_ALIGNMENT::CENTER);

        spinbox_.enable();
        spinbox_.at_select_func() = [=](SPINBOX::TOUCH_AREA area, int16_t value) {
            static const char* st[3] = { "Minus", "Stay", "Plus" };
            utils::format("Spinbox: %s Value: %d\n")
                % st[static_cast<uint8_t>(area)] % value;
        };

        toggle_.enable();
        toggle_.at_select_func() = [=](bool state) {
            utils::format("Toggle: %s\n") % (state ? "OFF" : "ON");
            if(!state) {
                progress_ratio_ = 0.0f;
            }
        };

        progress_.enable();
        progress_.at_update_func() = [=](float ratio) {
            if(toggle_.get_switch_state()) {
                ratio += 1.0f / 120.0f;  // 2 sec
                if(ratio > 1.0f) ratio = 1.0f;
            } else {
                ratio = 0.0f;
                progress_ratio_ = 0.0f;
            }
            return ratio;
        };
    }

GUI_sample