前のページ次のページ上に戻るホーム SophiaFramework UNIVERSE 5.3

3.5. コントロール(基礎編)

コントロールは、 コンテナウィンドウ内に配置するレスポンダです。

コントロールクラスは、 SFYControl クラスを継承し、最大値、最小値、現在値を保持しています。 これらの値が表す意味はコントロールの種類毎に異なります。

以下、 テキストボタンコントロール、 チェックボックスコントロール、 ラジオボタンコントロール、 ラベルコントロール、 エディットボックスコントロール、 ラジオボタンコントロール、 コンボボックスコントロール、 リストボックスコントロールなどのコントロールについて解説します。

3.5.1. ボタンコントロール

下図のボタンコントロール(SFZTextButtonControl)を 3 つ作成します。

図 3.5. ボタンコントロール

ボタンコントロール

  1. ボタンコントロール 1(OK ボタン)とボタンコントロール 2(NG ボタン)は排他的に動作します。
  2. ボタンコントロール 3(ON / OFF ボタン)は、 文字列 "Hello Window" の表示と非表示の切り替えスイッチボタンです。

ボタンコントロールと結果ハンドラを宣言します。

例 3.16. ボタンコントロールと結果ハンドラの宣言

// MyWindow クラスの定義
SFMTYPEDEFRESPONDER(MyWindow)  //  便利な型を生成するマクロ
class MyWindow : public SFZWindow
{
    SFMSEALRESPONDER(MyWindow)  // インスタンスのコピーを禁止するマクロ

    // SFYResponder からこのクラスに至るまでの継承順序を指定するマクロ
    SFMRESPONDERINSTANTIATEFOUR(MyWindow, SFZWindow, SFYContainer, SFYWidget, SFYResponder)
public:
    enum CodeEnum {
        CODE_TYPE = four_char_code('M', 'W', 'N', 'D')
    };
    SFMTYPEDEFTYPE(CodeEnum)
private:

    // *** 太字が追加部分

    // ボタンコントロール
    SFZTextButtonControlSmp _button1;
    SFZTextButtonControlSmp _button2;
    SFZTextButtonControlSmp _button3;

    // "Hello Window" の表示・非表示切り替えフラグ
    Bool _flagShowHw; 
public:
    static MyWindowSmp NewInstance(SFCErrorPtr exception = null);

    // MyWindow 内のコントロール作成
    SFCError Make(Void);
protected:
    explicit MyWindow(Void) static_throws;
    virtual ~MyWindow(Void);
    virtual Void HandleRenderRequest(SFXGraphicsPtr graphics) const;
private:
    // ボタンコントロールを作成する関数
    SFCError CreateButtons(SFXGridPtr offs);

    XANDLER_DECLARE_BOOLEVENT(OnKey)  // キーハンドラ

    // ボタンコントロール 1 / 2 の結果ハンドラ
    XANDLER_DECLARE_VOIDRESULT(OnButton12)

    // ボタンコントロール 3 の結果ハンドラ
    XANDLER_DECLARE_VOIDRESULT(OnButton3)
};
[Note] SophiaFramework UNIVERSE 標準レスポンダの描画処理

SFZTextButtonControl などの SophiaFramework UNIVERSE 標準レスポンダでは、 親レスポンダ、実領域状態、 各種パラメータを設定したとき、レスポンダの再描画は自動的に行われます。

[Note] 結果ハンドラの宣言と実装

結果ハンドラは、 XANDLER_DECLARE_VOIDRESULT マクロで宣言し、 XANDLER_IMPLEMENT_VOIDRESULT マクロで実装します。

[Note] フォーカスの移動

MyWindow 内に配置したコントロール間のフォーカス移動に関する処理を記述する必要はありません。

フォーカス移動処理は、 MyWindow クラスが継承する SFYContainer クラス内の SFYContainer::ScrollUp / SFYContainer::ScrollDown 関数の中で実装されています。

デフォルトの設定では、 ↑/↓キーにより、 フォーカスは前/次のコントロールに移動できます。

フォーカス移動キーは、 SFYContainer::SetScrollUpKey / SFYContainer::SetScrollDownKey 関数により変更可能です。

参照: コンテナ(基礎編)

MyWindow 内に 3 つのボタンコントロールを作成します。

例 3.17. MyWindow 内のコントロール作成

#define X_MARGIN    8  // ボタンコントロール 1 の実領域の原点 (X 座標)
#define Y_MARGIN    8  // ボタンコントロール 1 の実領域の原点 (Y 座標)

enum {
    ID_BUTTON1 = 1,    // ボタンコントロール 1 の ID
    ID_BUTTON2         // ボタンコントロール 2 の ID
};

// MyWindow 内のコントロール作成
SFCError MyWindow::Make(Void)
{
    // MyWindow 上でコンテンツを描画する領域の左上端を設定する
    SFXGrid offset(X_MARGIN, Y_MARGIN);

    SFCError error(SFERR_NO_ERROR);

    // ボタンコントロールを作成する
    if ((error = CreateButtons(&offset)) != SFERR_NO_ERROR) {
        return error;
    }

    return error;
}

MyWindow 内にボタンコントロールを作成するコードを追加します。

ここでは、ラベルテキストや ID などのプロパティもボタンコントロールに設定されています。

例 3.18. ボタンコントロールの作成

// ボタンコントロールを作成する関数
SFCError MyWindow::CreateButtons(SFXGridPtr offs)
{
    SFXGraphicsPtr graphics = SFXGraphics::GetInstance();
    SInt16Const height = graphics->GetFontHeight() + 4;                  // ボタンコントロールの高さ
    SInt16Const width = (GetLocalBound().GetWidth() - 4 * X_MARGIN) / 3; // ボタンコントロールの横幅
    SFXRectangle rect(*offs, SFXSize(width, height));                    // ボタンコントロール 1 の実領域
    SFCError error(SFERR_NO_ERROR);

    // ボタンコントロール 1 を作成する
    if ((_button1 = SFZTextButtonControl::NewInstance(&error)) != null) {

        // ボタンコントロール 1 の親を MyWindow に設定する
        error = _button1->SetParent(GetThis());

        if (error == SFERR_NO_ERROR) {

            // ボタンコントロール 1 に結果ハンドラを登録する
            error = _button1->RegisterHandler(
                SFXEventRange(SFEVT_RESPONDER_RESULT, SFEVT_RESPONDER_RESULT, SFP16_BEGIN, SFP16_END),
                XANDLER_INTERNAL(OnButton12)
            );

            if (error == SFERR_NO_ERROR) {

                // ボタンコントロール 1 のテキストラベルを "OK" に初期設定する
                error = _button1->SetText("OK");

                if (error == SFERR_NO_ERROR) {

                    // ボタンコントロール 1 を識別する ID を設定する
                    _button1->SetID(ID_BUTTON1);

                    // ボタンコントロール 1 の実領域を設定する
                    _button1->SetRealBound(rect);

                    // ボタンコントロール 1 の状態を「可視+活性+操作可能+フォーカス」にまとめて設定する
                    _button1->SetState(true, true, true, true);

                    // コントロールは ToFront 関数を呼び出す必要がない(※)
                }
            }
        }
    }

    // ボタンコントロール 2 を作成する
    if ((_button2 = SFZTextButtonControl::NewInstance(&error)) != null) {

        // ボタンコントロール 2 の親を MyWindow に設定する
        error = _button2->SetParent(GetThis());

        if (error == SFERR_NO_ERROR) {

            // ボタンコントロール 2 に結果ハンドラを登録する
            error = _button2->RegisterHandler(
                SFXEventRange(SFEVT_RESPONDER_RESULT, SFEVT_RESPONDER_RESULT, SFP16_BEGIN, SFP16_END),
                XANDLER_INTERNAL(OnButton12)
            );

            if (error == SFERR_NO_ERROR) {

                // ボタンコントロール 2 のテキストラベルを "NG" に初期設定する
                error = _button2->SetText("NG");

                if (error == SFERR_NO_ERROR) {

                    // ボタンコントロール 2 を識別する ID を設定する
                    _button2->SetID(ID_BUTTON2);

                    // ボタンコントロール 2 の実領域を計算する
                    rect.Offset(X_MARGIN + rect.GetWidth(), 0);

                    // ボタンコントロール 2 の実領域を設定する
                    _button2->SetRealBound(rect);

                    // ボタンコントロール 2 の状態を「可視+不活性+操作不能+非フォーカス」にまとめて設定する
                    _button2->SetState(true, false, false, false);
                }
            }
        }
    }

    // ボタンコントロール 3 を作成する
    if ((_button3 = SFZTextButtonControl::NewInstance(&error)) != null) {

        // ボタンコントロール 3 の親を MyWindow に設定する
        error = _button3->SetParent(GetThis());

        if (error == SFERR_NO_ERROR) {

            // ボタンコントロール 3 に結果ハンドラを登録する
            error = _button3->RegisterHandler(
                SFXEventRange(SFEVT_RESPONDER_RESULT, SFEVT_RESPONDER_RESULT, SFP16_BEGIN, SFP16_END),
                XANDLER_INTERNAL(OnButton3)
            );

            if (error == SFERR_NO_ERROR) {

                // ボタンコントロール 3 のテキストラベルを "Off" に初期設定する
                error = _button3->SetText("Off");

                if (error == SFERR_NO_ERROR) {

                    // ボタンコントロール 3 の実領域を計算する
                    rect.Offset(X_MARGIN + rect.GetWidth(), 0);

                    // ボタンコントロール 3 の実領域を設定する
                    _button3->SetRealBound(rect);

                    // ボタンコントロール 3 の状態を「可視+活性+操作可能+非フォーカス」にまとめて設定する
                    _button3->SetState(true, true, true, false);
                }
            }
        }
    }

    // オフセットを更新する
    offs->AddY(rect.GetHeight() + Y_MARGIN);

    return error;
}
[Note] ボタンコントロールの操作

ボタンコントロールは、操作キーを使用して操作します。

操作キーが押下されると、 ボタンコントロールは結果イベントを受信します。

SFYResponder::RegisterHandler 関数を使用して、 結果イベントを受信する 結果ハンドラをボタンコントロールに登録することが可能です。

デフォルトでは、セレクトキー(AVK_SELECT)が操作キーに設定されています。 操作キーは、 SFYButtonControl::SetOperateKey 関数により変更可能です。

[Important] ToFront 関数

SFYResponder::ToFront 関数は、 SFYContainer クラスを継承する、 ウィンドウやダイアログ、メニューなどのコンテナレスポンダを 最前面に移動するために使います。

親レスポンダの SFYResponder::ToFront 関数を呼び出すことにより、 コンテナレスポンダ内の子レスポンダも自動的に最前面に移動します。

参照: SFXGraphics | SFXRectangle | SFXGrid | SFXSize | 図形と色

helloworld::Make 関数内の

_myWindow->Make();

を実行すると、 MyWindow 内に 3 つのボタンコントロールが作成されます。

例 3.19. MyWindow 作成

// MyWindow 作成
SFCError helloworld::Make(Void)
{
    SFCError error(SFERR_NO_ERROR);
    if ((_myWindow = MyWindow::NewInstance(&error)) != null) {
        error = _myWindow->SetParent(GetThis());
        if (error == SFERR_NO_ERROR) {

            // MyWindow の実領域として携帯電話画面を(15,20)だけ Deflate した領域を設定する
            _myWindow->SetRealBound(GetLocalBound().Deflate(15, 20));

            // MyWindow の状態を「可視+活性+操作可能+フォーカス」にまとめて設定する
            _myWindow->SetState(true, true, true, true);

            // MyWindow を最前面に移動する
            _myWindow->ToFront();

            // *** 太字が追加部分

            // 3 つのボタンコントロールを作成する
            _myWindow->Make();
        }
    }
    return error;
}

MyWindow のコンストラクタで、 _ifShowHw フラグを true に初期設定して文字列 "Hello Window" がデフォルトで表示されるようにします。

例 3.20. MyWindow コンストラクタの変更

// コンストラクタ

#define COLOR_MY_WINDOW_BACK        (SFXRGBColor(0xCC, 0xFF, 0xCC, 0x00)) // 薄緑色: MyWindow の背景色

// *** 太字が変更部分
MyWindow::MyWindow(Void) : _ifShowHw(true) static_throws // 文字列表示フラグ _ifShowHw を true に初期設定する
{
    if (static_try()) {

        // レスポンダのタイプを設定する
        SetType(CODE_TYPE);

        // MyWindow のキーハンドラを登録する
        static_throw(RegisterHandler(
            SFXEventRange(SFEVT_KEY, SFEVT_KEY, SFP16_BEGIN, SFP16_END),
            XANDLER_INTERNAL(OnKey)
        ));

        // MyWindow 自身の描画処理は MyWindow::HandleRenderRequest 関数をオーバーライドして実装する
        // MyWindow::HandleRenderRequest 関数は SFYWidget クラスに登録されているデフォルトの描画ハンドラから呼び出されるので
        // 描画ハンドラの登録は不要

        // MyWindow の背景色を薄緑色に設定する
        SetBackgroundColor(COLOR_MY_WINDOW_BACK);
    }
}

ボタンコントロールの結果ハンドラを実装します。

この結果ハンドラは、 ボタンコントロール 1 / 2 の活性状態フラグを排他的に true にします。

例 3.21. ボタンコントロール 1 / 2 の結果ハンドラの実装

// ボタンコントロール 1 / 2 の結果ハンドラの実装
XANDLER_IMPLEMENT_VOIDRESULT(MyWindow, OnButton12, invoker, reason, result)
{
    unused(reason);
    unused(result);

    // 押されたボタンコントロールを取得する
    // invoker は SFYResponderPtr 型なので SFZTextButtonControlPtr 型にダウンキャストする
    SFZTextButtonControlPtr pushed(static_cast<SFZTextButtonControlPtr>(invoker));

    // 対になるボタンコントロールを取得する
    UInt32 pairID = (pushed->GetID() == ID_BUTTON1) ? ID_BUTTON2 : ID_BUTTON1;
    SFZTextButtonControlSmp pair = static_pointer_cast<SFZTextButtonControl>(GetThis()->GetChildFront(pairID));

    // 押されたボタンコントロールのテキストラベルを "NG" に設定する
    pushed->SetText("NG");

    // 押されたボタンコントロールの状態を「不活性」にする
    // 操作可能、フォーカスよりも活性状態は優先順位が高いので、不活性にするだけでよい
    pushed->SetStateActive(false);

    // 対になるボタンコントロールのテキストラベルを "OK" に設定する
    pair->SetText("OK");

    // 対になるボタンコントロールの状態を「活性+操作可能+フォーカス」に設定する
    pair->SetStateActive(true);
    pair->SetStateEnable(true);
    pair->SetStateFocus(true);

    return;
}

SFYResponder::SetID 関数を使用してボタンコントロール 1 と ボタンコントロール 2 に ID を設定します。

設定した ID は、SFYResponder::GetID 関数を使用して取得できます。

ID を使用してレスポンダを検索するときは、SFYResponder::GetChildFront 関数を使用します。

SFYResponder::GetChildFront 関数は、 引数に設定された ID と同じ値が設定された子レスポンダが存在した場合、 そのなかで最前面に配置されているレスポンダを返します。

[Tip] ポインタのダウンキャスト演算子

invoker 引数は、 SFYResponderPtr 型として定義される呼び出し元レスポンダへのポインタです。

SFZTextButtonControl::SetText 関数を使用してボタンコントロールのテキストラベルを変更するには、 このポインタは SFZTextButtonControlPtr 型でなければいけません。

そのため、 static_cast 演算子を利用して SFYResponderPtr 型を SFZTextButtonControlPtr 型にダウンキャストしています。

[Tip] スマートポインタのダウンキャスト

SFYResponder::GetChildFront 関数の戻り値は SFYResponderSmp 型なので、 static_pointer_cast 演算子を利用して SFZTextButtonControlSmp 型にダウンキャストしています。

[Note] レスポンダの状態

レスポンダには 有効、可視、活性、操作可能、フォーカスの 5 種類の状態があります。

SFYResponder::SetState 関数で設定できるのは、 可視、活性、操作可能、フォーカスの 4 種類の状態です。 有効状態は SFYResponder::Initialize 関数で true に設定され、 SFYResponder::Terminate 関数で false に設定されます。

ボタンコントロール 3 は文字列 "Hello Window" の表示と非表示の切り替えを行います。

例 3.22. ボタンコントロール 3 の結果ハンドラの実装

// ボタンコントロール 3 の結果ハンドラの実装
XANDLER_IMPLEMENT_VOIDRESULT(MyWindow, OnButton3, invoker, reason, result)
{
    unused(invoker);
    unused(reason);
    unused(result);

    // ボタンコントロール 3 のテキストラベルを "Off" から "On"、または "On" から "Off" に切り替える
    _ifShowHw = !_ifShowHw;
    _button3->SetText(_ifShowHw ? "Off" : "On");

    // MyWindow を再描画領域として登録する
    Invalidate(); 

    // ※1. Invalidate 関数の引数に何も指定しない場合、MyWindow の実領域が再描画領域として登録される
    // ※2. 実際の再描画はイベントループ終わりに起動される描画エンジンによって行われる
    // ※3. そのとき、Invalidate 関数によって登録された再描画領域のうち可視であるものが再描画の対象となる
    return;
}

参照: 描画処理

表示・非表示切り替えフラグ _flagShowHw の値に応じて文字列 "Hello Window" の描画を行うように MyWindow::HandleRenderRequest 仮想関数(描画ハンドラ)を変更します。

例 3.23.

// 描画ハンドラ
Void MyWindow::HandleRenderRequest(SFXGraphicsPtr graphics) const
{
    // 描画を行うための SFXGraphics インスタンスは第 1 引数(graphics)として渡される
    // MyWindow のローカル領域の左上が描画領域の原点となる

    // MyWindow のローカル領域はコンストラクタ内で SFYWidget::SetBackgroundColor() を使用して
    // 設定した薄緑色で塗り潰される

    if (_flagShowHw) {
        // _flagShowHw フラグが true のとき、文字列 "Hello Window" を黒色で描画する
        graphics->DrawSingleText("Hello Window", GetLocalBound(), COLOR_BLACK);
    }

    return;
}

シミュレータでの実行結果は以下の通りです。

  1. 初期画面で 1 キー(AVK_1)を押すと、MyWindow が表示されます。
  2. ボタンコントロール 1 を選択して操作キー[=セレクトキー(AVK_SELECT)]押すと、 ボタンコントロール 1 が操作不能状態になり、 ボタンコントロール 2 が操作可能状態になります。
  3. ボタンコントロール 2 を選択して操作キー[=セレクトキー(AVK_SELECT)]押すと、 ボタンコントロール 2 が操作不能状態になり、 ボタンコントロール 1 が操作可能状態になります。
  4. ボタンコントロール 3 を選択して操作キー[=セレクトキー(AVK_SELECT)]押すと、 文字列 "Hello Window" がクリアされます。
  5. クリアキー(AVK_CLR)を押すと、MyWindow が閉じます。

図 3.6. 手順 2 の結果

手順 2 の結果

図 3.7. 手順 4 の結果

手順 4 の結果

参照: テキストを表示するボタンコントロール [SFZTextButtonControl]

3.5.2. チェックボックスコントロール

下図のチェックボックスコントロール(SFZCheckboxControl)を作成します。

図 3.8. チェックボックスコントロール

チェックボックスコントロール

チェックボックスコントロールと結果ハンドラを宣言します。

例 3.24. チェックボックスコントロールと結果ハンドラの宣言

// MyWindow クラスの定義
SFMTYPEDEFRESPONDER(MyWindow)  //  便利な型を生成するマクロ
class MyWindow : public SFZWindow {

    SFMSEALRESPONDER(MyWindow)  // インスタンスのコピーを禁止するマクロ

    // SFYResponder からこのクラスに至るまでの継承順序を指定するマクロ
    SFMRESPONDERINSTANTIATEFOUR(MyWindow, SFZWindow, SFYContainer, SFYWidget, SFYResponder)
public:

    // 小文字アルファベットまたは記号 4 文字からなるタイプは予約されているので
    // MyWindow のタイプを大文字アルファベット('M', 'W', 'N', 'D')で定義する
    enum CodeEnum {
        CODE_TYPE = four_char_code('M', 'W', 'N', 'D')
    };
    SFMTYPEDEFTYPE(CodeEnum)

private:
    SFZTextButtonControlSmp _button1;
    SFZTextButtonControlSmp _button2;
    SFZTextButtonControlSmp _button3;
    Bool _flagShowHw;

    // *** 太字が追加部分

    // チェックボックスコントロール
    SFZCheckboxControlSmp _checkbox1;
    SFZCheckboxControlSmp _checkbox2;
public:
    static MyWindowSmp NewInstance(SFCErrorPtr exception = null);

    // MyWindow 内のコントロール作成
    SFCError Make(Void);

protected:
    explicit MyWindow(Void) static_throws;
    virtual ~MyWindow(Void);
    virtual Void HandleRenderRequest(SFXGraphicsPtr graphics) const;
private:
    SFCError CreateButtons(SFXGridPtr offs);

    // チェックボックスコントロールを作成する関数
    SFCError CreateCheckBoxes(SFXGridPtr offs);

    XANDLER_DECLARE_BOOLEVENT(OnKey)        // キーハンドラ
    XANDLER_DECLARE_VOIDRESULT(OnButton12)  // 結果ハンドラ
    XANDLER_DECLARE_VOIDRESULT(OnButton3)   // 結果ハンドラ

    // 共通チェックボックスコントロール用ハンドラ
    XANDLER_DECLARE_VOIDRESULT(OnCheck)
};

チェックボックスコントロールが作成されるように MyWindow::Make 関数を変更します。

例 3.25. MyWindow 内のコントロール作成

// MyWindow 内のコントロール作成
SFCError MyWindow::Make(Void)
{
    SFXGrid offset(0, 0);
    SFCError error(SFERR_NO_ERROR);

    // MyWindow 上でコンテンツを描画する領域の左上端を設定する
    offset.Add(X_MARGIN, Y_MARGIN);

    // ボタンコントロールを作成する
    if ((error = CreateButtons(&offset)) != SFERR_NO_ERROR) {
        return error;
    }

    // *** 太字が追加部分

    // チェックボックスコントロールを作成する
    if ((error = CreateCheckBoxes(&offset)) != SFERR_NO_ERROR) {
        return error;
    }

    return error;
}

チェックボックスコントロールを作成するコードを追加します。 ラベルテキストや現在値などのプロパティも設定しています。

例 3.26. チェックボックスコントロールの作成

// チェックボックスコントロールを作成する関数
SFCError MyWindow::CreateCheckBoxes(SFXGridPtr offs)
{
    SFXGraphicsPtr graphics = SFXGraphics::GetInstance();
    SInt16Const height = graphics->GetFontHeight() + 4;                   // チェックボックスコントロールの高さ
    SInt16Const width = (GetLocalBound().GetWidth() - 3 * X_MARGIN) / 2;  // チェックボックスコントロールの横幅
    SFXRectangle rect(*offs, width, height);                              // チェックボックスコントロール 1 の実領域
    SFCError error(SFERR_NO_ERROR);

    // チェックボックスコントロール 1 を作成する
    if ((_checkbox1 = SFZCheckboxControl::NewInstance(error)) != null) {

        // チェックボックスコントロール 1 の親を MyWindow に設定する
        error = _checkbox1->SetParent(GetThis());

        if (error == SFERR_NO_ERROR) {

            // チェックボックスコントロール 1 に結果ハンドラを登録する
            error = _checkbox1->RegisterHandler(
                SFXEventRange(SFEVT_RESPONDER_RESULT, SFEVT_RESPONDER_RESULT, SFP16_BEGIN, SFP16_END),
                XANDLER_INTERNAL(OnCheck)
            );

            if (error == SFERR_NO_ERROR) {

                // チェックボックスコントロール 1 のテキストラベルを "Hit" に設定する
                error = _checkbox1->SetText("Hit");

                if (error == SFERR_NO_ERROR) {

                    // チェックボックスコントロール 1 にチェックを入れる
                    _checkbox1->SetCurrentValue(true);

                    // チェックボックスコントロール 1 を左詰めにする
                    _checkbox1->SetHorizontalAlign(SFZCheckboxControl::HORIZONTAL_LEFT);

                    // チェックボックスコントロール 1 の実領域を設定する
                    _checkbox1->SetRealBound(rect);

                    // チェックボックスコントロール 1 の状態を「可視+活性+操作可能+非フォーカス」にまとめて設定する
                    _checkbox1->SetState(true, true, true, false);
                }
            }
        }
    }

    // チェックボックスコントロール 2 を作成する
    if ((_checkbox2 = SFZCheckboxControl::NewInstance(error)) != null) {

        // チェックボックスコントロール 2 の親を MyWindow に設定する
        error = _checkbox2->SetParent(GetThis());

        if (error == SFERR_NO_ERROR) {

            // チェックボックスコントロール 2 に結果ハンドラを登録する
            error = _checkbox2->RegisterHandler(
              SFXEventRange(SFEVT_RESPONDER_RESULT, SFEVT_RESPONDER_RESULT, SFP16_BEGIN, SFP16_END),
              XANDLER_INTERNAL(OnCheck)
            );

            // チェックボックスコントロール 2 のテキストラベルを "Run" に設定する
            error = _checkbox2->SetText("Run");

            if (error == SFERR_NO_ERROR) {

                // チェックボックスコントロール 2 の実領域を計算する
                rect.Offset(X_MARGIN + rect.GetWidth(), 0);

                // チェックボックスコントロール 2 を左詰めにする
                _checkbox2->SetHorizontalAlign(SFZCheckboxControl::HORIZONTAL_LEFT);

                // チェックボックスコントロール 2 の実領域を設定する
                _checkbox2->SetRealBound(rect);

                // チェックボックスコントロール 2 の状態を「可視+活性+操作可能+非フォーカス」にまとめて設定する
                _checkbox2->SetState(true, true, true, false);
            }
        }
    }

    offs->AddY(rect.GetHeight() + Y_MARGIN);

    return error;
}
[Note] チェックボックスコントロールの操作

チェックボックスコントロールは、操作キーを使用して操作します。

チェック状態が変更されると、 チェックボックスコントロールは結果イベントを受信します。

SFYResponder::RegisterHandler 関数を使用して、 結果イベントを受信する 結果ハンドラをチェックボックスコントロールに登録することが可能です。

デフォルトでは、セレクトキー(AVK_SELECT)が操作キーに設定されています。 操作キーは、 SFYButtonControl::SetOperateKey 関数により変更可能です。

[Note] チェックボックスコントロールのアライメント

チェックボックスコントロールのテキストラベルは、 デフォルトでその領域内で水平方向と垂直方向に関して中央寄せされます。

この設定は、 SFZCheckboxControl::SetHorizontalAlign / SFZCheckboxControl::SetVerticalAlign 関数により変更可能です。

SFYResponder::GetSuitableBound 関数で最適なサイズを計算してから、 チェックボックスコントロールに実領域を割り当てる場合は、アライメントを意識する必要はありません。

チェックボックスコントロールのチェック状態が変更されたときに呼び出される結果ハンドラを実装します。

この結果ハンドラは、 チェックボックスコントロールのテキストラベルと現在値をデバッグウィンドウに表示します。

例 3.27. チェックボックスコントロールの結果ハンドラの実装

// チェックボックスコントロールの結果ハンドラの実装
XANDLER_IMPLEMENT_VOIDRESULT(MyWindow, OnCheck, invoker, reason, result)
{
    unused(invoker);
    unused(reason);
    unused(result);

    // テキストラベルを取得する
    SFXWideString label1 = _checkbox1->GetText();
    SFXWideString label2 = _checkbox2->GetText();

    // テキストラベルと現在値をデバッグウィンドウに表示する
    TRACE("%S: %d", label1.GetCString(), _checkbox1->GetCurrentValue());
    TRACE("%S: %d", label2.GetCString(), _checkbox2->GetCurrentValue());

    return;
}
[Note] チェックボックスコントロールの現在値

チェックボックスコントロールの現在値は SFYControl::GetCurrentValue 関数で取得できます。

[Note] ワイド型文字列をデバッグウィンドウに表示する方法

SFZCheckboxControl::GetText 関数の戻り値(テキストラベル)は、 SFXWideString 型です。

ワイド型文字列を TRACE 文の引数に指定する場合は、S パラメータを使用します。

描画イベント[SFEVT_RESPONDER_RENDER]発生時に呼び出される描画ハンドラ MyWindow::HandleRenderRequest() をオーバーライドします。

ここで、文字列 "Hello Window" を最初は表示しないように変更します。

例 3.28.

// 描画ハンドラ
Void MyWindow::HandleRenderRequest(SFXGraphicsPtr graphics) const
{
    // 描画を行うための SFXGraphics インスタンスは第 1 引数(graphics)として渡される
    // MyWindow のローカル領域の左上が描画領域の原点となる

    // MyWindow のローカル領域はコンストラクタ内で SFYWidget::SetBackgroundColor() を使用して
    // 設定した薄緑色で塗り潰される

    // 最初、テキストは何も表示されない

    return;
}

シミュレータでの実行結果は以下の通りです。

図 3.9. 実行結果

実行結果

図 3.10. チェックボックスコントロールにチェックを入れたときのデバッグウィンドウ

チェックボックスコントロールにチェックを入れたときのデバッグウィンドウ

チェックボックスコントロールのテキストラベルと現在値が出力されます。

参照: チェックボックスコントロール[SFZCheckboxControl]

3.5.3. ラジオボタンコントロール

下図の排他的に動作するグループ化されたラジオボタンコントロール(SFZRadiobuttonControl)を作成します。

図 3.11. ラジオボタンコントロール

ラジオボタンコントロール

ラジオボタンコントロールと結果ハンドラを宣言します。

例 3.29. ラジオボタンコントロールと結果ハンドラの宣言

// MyWindow クラスの定義
SFMTYPEDEFRESPONDER(MyWindow)  //  便利な型を生成するマクロ
class MyWindow : public SFZWindow {

    SFMSEALRESPONDER(MyWindow)  // インスタンスのコピーを禁止するマクロ

    // SFYResponder からこのクラスに至るまでの継承順序を指定するマクロ
    SFMRESPONDERINSTANTIATEFOUR(MyWindow, SFZWindow, SFYContainer, SFYWidget, SFYResponder)
public:

    // 小文字アルファベットまたは記号 4 文字からなるタイプは予約されているので
    // MyWindow のタイプを大文字アルファベット('M', 'W', 'N', 'D')で定義する
    enum CodeEnum {
        CODE_TYPE = four_char_code('M', 'W', 'N', 'D')
    };
    SFMTYPEDEFTYPE(CodeEnum)

private:
    SFZTextButtonControlSmp _button1;
    SFZTextButtonControlSmp _button2;
    SFZTextButtonControlSmp _button3;
    Bool _ifShowHw;
    SFZCheckboxControlSmp _checkbox1;
    SFZCheckboxControlSmp _checkbox2;

    // *** 太字が追加部分

    // ラジオボタンコントロール
    SFZRadiobuttonControlSmp _radio1;
    SFZRadiobuttonControlSmp _radio2;
public:
    static MyWindowSmp NewInstance(SFCErrorPtr exception = null);

    // MyWindow 内のコントロール作成
    SFCError Make(Void);

protected:
    explicit MyWindow(Void) static_throws;
    virtual ~MyWindow(Void);
    virtual Void HandleRenderRequest(SFXGraphicsPtr graphics) const;
private:
    SFCError CreateButtons(SFXGridPtr offs);
    SFCError CreateCheckBoxes(SFXGridPtr offs);

    // ラジオボタンコントロールを作成する関数
    SFCError CreateRadioButtons(SFXGridPtr offs);

    XANDLER_DECLARE_BOOLEVENT(OnKey)        // キーハンドラ
    XANDLER_DECLARE_VOIDRESULT(OnButton12)  // 結果ハンドラ
    XANDLER_DECLARE_VOIDRESULT(OnButton3)   // 結果ハンドラ
    XANDLER_DECLARE_VOIDRESULT(OnCheck)     // 結果ハンドラ

    // ラジオボタンコントロールの結果ハンドラ
    XANDLER_DECLARE_VOIDRESULT(OnRadio)
};

ラジオボタンコントロールが作成されるように MyWindow::Make 関数を変更します。

例 3.30. MyWindow 内のコントロール作成

// MyWindow 内のコントロール作成
SFCError MyWindow::Make(Void)
{
    SFXGrid offset(0, 0);
    SFCError error(SFERR_NO_ERROR);

    // MyWindow 上でコンテンツを描画する領域の左上端を設定する
    offset.Add(X_MARGIN, Y_MARGIN);

    // ボタンコントロールを作成する
    if ((error = CreateButtons(&offset)) != SFERR_NO_ERROR) {
        return error;
    }

    // チェックボックスコントロールを作成する
    if ((error = CreateCheckBoxes(&offset)) != SFERR_NO_ERROR) {
        return error;
    }

    // *** 太字が追加部分

    // ラジオボタンコントロールを作成する
    if ((error = CreateRadioButtons(&offset)) != SFERR_NO_ERROR) {
        return error;
    }

    return error;
}

ラジオボタンコントロールを作成します。

※ラジオボタンコントロールのラベルテキストや現在値などのプロパティも設定しています。

例 3.31. ラジオボタンコントロールの作成

// ラジオボタンコントロールを作成する関数
SFCError MyWindow::CreateRadioButtons(SFXGridPtr offs)
{
    SFXGraphicsPtr graphics = SFXGraphics::GetInstance();
    SInt16Const height = graphics->GetFontHeight() + 4;                  // ラジオボタンコントロールの高さ
    SInt16Const width = (GetLocalBound().GetWidth() - 3 * X_MARGIN) / 2; // ラジオボタンコントロールの横幅
    SFXRectangle rect(*offs, width, height);                             // ラジオボタンコントロール 1 の実領域
    SFCError error(SFERR_NO_ERROR);

    // ラジオボタンコントロール 1 を作成する
    if ((_radio1 = SFZRadiobuttonControl::NewInstance(error)) != null) {

        // ラジオボタンコントロール 1 の親を MyWindow に設定する
        error = _radio1->SetParent(GetThis());

        if (error == SFERR_NO_ERROR) {

            // ラジオボタンコントロール 1 に結果ハンドラを登録する
            error = _radio1->RegisterHandler(
                SFXEventRange(SFEVT_RESPONDER_RESULT, SFEVT_RESPONDER_RESULT, SFP16_BEGIN, SFP16_END),
                XANDLER_INTERNAL(OnRadio)
            );

            if (error == SFERR_NO_ERROR) {

                // ラジオボタンコントロール 1 のテキストラベルを "Fast" に設定する
                error = _radio1->SetText("Fast");

                if (error == SFERR_NO_ERROR) {

                    // ラジオボタンコントロール 1 にチェックを入れる
                    _radio1->SetCurrentValue(true);

                    // ラジオボタンコントロール 1 を左詰にする
                    _radio1->SetHorizontalAlign(SFZRadiobuttonControl::HORIZONTAL_LEFT);

                    // ラジオボタンコントロール 1 の実領域を設定する
                    _radio1->SetRealBound(rect);

                    // ラジオボタンコントロール 1 の状態を「可視+活性+操作可能+非フォーカス」にまとめて設定する
                    _radio1->SetState(true, true, true, false);
                }
            }
        }
    }

    // ラジオボタンコントロール 2 を作成する
    if ((_radio2 = SFZRadiobuttonControl::NewInstance(error)) != null) {

        // ラジオボタンコントロール 2 の親を MyWindow に設定する
        error = _radio2->SetParent(GetThis());

        if (error == SFERR_NO_ERROR) {

           // ラジオボタンコントロール 2 に結果ハンドラを登録する
           error = _radio2->RegisterHandler(
               SFXEventRange(SFEVT_RESPONDER_RESULT, SFEVT_RESPONDER_RESULT, SFP16_BEGIN, SFP16_END),
               XANDLER_INTERNAL(OnRadio)
           );

           if (error == SFERR_NO_ERROR) {

               // ラジオボタンコントロール 2 のテキストラベルを "Slow" に設定する
               error = _radio2->SetText("Slow");

               if (error == SFERR_NO_ERROR) {

                   // ラジオボタンコントロール 2 の実領域を計算する
                   rect.Offset(X_MARGIN + rect.GetWidth(), 0);

                   // ラジオボタンコントロール 2 を左詰にする
                   _radio2->SetHorizontalAlign(SFZRadiobuttonControl::HORIZONTAL_LEFT);

                   // ラジオボタンコントロール 2 の実領域を設定する
                   _radio2->SetRealBound(rect);

                   // ラジオボタンコントロール 2 の状態を「可視+活性+操作可能+非フォーカス」にまとめて設定する
                   _radio2->SetState(true, true, true, false);
               }
           }
        }
    }

    // ラジオボタンコントロール 1 と 2 をグループにする
    _radio1->Group(_radio2);

    offs->AddY(rect.GetHeight() + Y_MARGIN);

    return error;
}
[Note] ラジオボタンコントロールの操作

ラジオボタンコントロールは、操作キーを使用して操作します。

チェック状態が変更されると、 ラジオボタンコントロールは結果イベントを受信します。

SFYResponder::RegisterHandler 関数を使用して、 結果イベントを受信する 結果ハンドラをラジオボタンコントロールに登録することが可能です。

デフォルトでは、セレクトキー(AVK_SELECT)が操作キーに設定されています。 操作キーは、 SFYButtonControl::SetOperateKey 関数により変更可能です。

[Note] ラジオボタンコントロールのグループ化

複数のラジオボタンコントロールをグループ化し、 ラジオボタンコントロールを排他的に選択できるようにするには、 SFYRadiobuttonControl::Group 関数を使用します。

[Note] ラジオボタンコントロールのアライメント

デフォルトの設定では、 ラジオボタンコントロールのテキストラベルは、 水平方向と垂直方向に中央寄せされます。

この設定は、 SFZRadiobuttonControl::SetHorizontalAlign / SFZRadiobuttonControl::SetVerticalAlign 関数により変更可能です。

SFYResponder::GetSuitableBound 関数で最適なサイズを計算してからラジオボタンコントロールに実領域を割り当てる場合は、 アライメントを意識する必要はありません。

ラジオボタンコントロールのチェック状態が変更されたときに呼び出される結果ハンドラを実装します。

この結果ハンドラは、ラジオボタンコントロールのテキストラベルと現在値をデバッグウィンドウに表示します。

例 3.32. ラジオボタンコントロールの結果ハンドラの実装

// ラジオボタンコントロールの結果ハンドラの実装
XANDLER_IMPLEMENT_VOIDRESULT(MyWindow, OnRadio, invoker, reason, result)
{
    unused(invoker);
    unused(reason);
    unused(result);

    // テキストラベルを取得する
    SFXWideString label1 = _radio1->GetText();
    SFXWideString label2 = _radio2->GetText();

    // テキストラベルと現在値をデバッグウィンドウに表示する
    TRACE("%S: %d", label1.GetCString(), _radio1->GetCurrentValue());
    TRACE("%S: %d", label2.GetCString(), _radio2->GetCurrentValue());

    return;
}
[Note] ラジオボタンコントロールの現在値

ラジオボタンコントロールの現在値は、 SFYControl::GetCurrentValue 関数で取得できます。

シミュレータでの実行結果は以下の通りです。

図 3.12. 実行結果

実行結果

図 3.13. ラジオボタンコントロールを押下したときのデバッグウィンドウ

ラジオボタンコントロールを押下したときのデバッグウィンドウ

ラジオボタンコントロールのテキストラベルと現在値が出力されます。

参照: ラジオボタンコントロール [SFZRadiobuttonControl]

3.5.4. コンボボックスコントロール

下図のコンボボックスコントロール(SFZComboBoxControl)を作成します。

図 3.14. コンボボックスコントロール

コンボボックスコントロール
[Note] 自動スクロール機能

実領域内に一度に表示できない数の項目がある場合、 スクロールして項目を表示します(スクロールバーも表示されます)。

コンボボックスコントロールと結果ハンドラを宣言します。

例 3.33. コンボボックスコントロールと結果ハンドラの宣言

// MyWindow クラスの定義
SFMTYPEDEFRESPONDER(MyWindow)  //  便利な型を生成するマクロ
class MyWindow : public SFZWindow {

    SFMSEALRESPONDER(MyWindow)  // インスタンスのコピーを禁止するマクロ

    // SFYResponder からこのクラスに至るまでの継承順序を指定するマクロ
    SFMRESPONDERINSTANTIATEFOUR(MyWindow, SFZWindow, SFYContainer, SFYWidget, SFYResponder)
public:

    // 小文字アルファベットまたは記号 4 文字からなるタイプは予約されているので
    // MyWindow のタイプを大文字アルファベット('M', 'W', 'N', 'D')で定義する
    enum CodeEnum {
        CODE_TYPE = four_char_code('M', 'W', 'N', 'D')
    };
    SFMTYPEDEFTYPE(CodeEnum)

private:
    SFZTextButtonControlSmp _button1;
    SFZTextButtonControlSmp _button2;
    SFZTextButtonControlSmp _button3;
    SFZCheckboxControlSmp _checkbox1;
    SFZCheckboxControlSmp _checkbox2;
    SFZRadiobuttonControlSmp _radio1;
    SFZRadiobuttonControlSmp _radio2;
    Bool _ifShowHw;

    // *** 太字が追加部分

    // コンボボックスコントロール
    SFZComboBoxControlSmp _combobox;
public:
    static MyWindowSmp NewInstance(SFCErrorPtr exception = null);

    // MyWindow 内のコントロール作成
    SFCError Make(Void);

protected:
    explicit MyWindow(Void) static_throws;
    virtual ~MyWindow(Void);
    virtual Void HandleRenderRequest(SFXGraphicsPtr graphics) const;
private:
    SFCError CreateButtons(SFXGridPtr offs);
    SFCError CreateCheckBoxes(SFXGridPtr offs);
    SFCError CreateRadioButtons(SFXGridPtr offs);

    // コンボボックスコントロールを作成する関数
    SFCError CreateComboBox(SFXGridPtr offs);

    XANDLER_DECLARE_BOOLEVENT(OnKey)        // キーハンドラ
    XANDLER_DECLARE_VOIDRESULT(OnButton12)  // 結果ハンドラ
    XANDLER_DECLARE_VOIDRESULT(OnButton3)   // 結果ハンドラ
    XANDLER_DECLARE_VOIDRESULT(OnCheck)     // 結果ハンドラ
    XANDLER_DECLARE_VOIDRESULT(OnRadio)     // 結果ハンドラ

    // コンボボックスコントロールの結果ハンドラ
    XANDLER_DECLARE_VOIDRESULT(OnComboBox)

    // コンボボックスコントロールの値ハンドラ
    XANDLER_DECLARE_VOIDVALUE(OnComboListValue)
};

コンボボックスコントロールが作成されるように MyWindow::Make 関数を変更します

例 3.34. MyWindow 内のコントロール作成

// MyWindow 内のコントロール作成
SFCError MyWindow::Make(Void)
{
    SFXGrid offset(0, 0);
    SFCError error(SFERR_NO_ERROR);

    // MyWindow 上でコンテンツを描画する領域の左上端を設定する
    offset.Add(X_MARGIN, Y_MARGIN);

    // ボタンコントロールを作成する
    if ((error = CreateButtons(&offset)) != SFERR_NO_ERROR) {
        return error;
    }

    // チェックボックスコントロールを作成する
    if ((error = CreateCheckBoxes(&offset)) != SFERR_NO_ERROR) {
        return error;
    }

    // ラジオボタンコントロールを作成する
    if ((error = CreateRadioButtons(&offset)) != SFERR_NO_ERROR) {
        return error;
    }

    // *** 太字が追加部分

    // コンボボックスコントロールを作成する
    if ((error = CreateComboBox(&offset)) != SFERR_NO_ERROR) {
        return error;
    }

    return error;
}

コンボボックスコントロールを作成するコードを追加します。

また、コンボボックスコントロールの項目を追加します。

例 3.35. コンボボックスコントロールの作成

// コンボボックスコントロールを作成する関数
SFCError MyWindow::CreateComboBox(SFXGridPtr offs)
{
    SFXGraphicsPtr graphics = SFXGraphics::GetInstance();
    SInt16Const height = graphics->GetFontHeight() + 4;              // コンボボックスコントロールの高さ
    SInt16Const width = (GetLocalBound().GetWidth() - 2 * X_MARGIN); // コンボボックスコントロールの横幅
    SFXRectangle rect(*offs, width, height);                         // コンボボックスコントロールの実領域
    SFCError error(SFERR_NO_ERROR);

    // コンボボックスコントロールを作成する
    if ((_combobox = SFZComboBoxControl::NewInstance(&error)) != null) {

        // コンボボックスコントロールの親を MyWindow に設定する
        error = _combobox->SetParent(GetThis());

        if (error == SFERR_NO_ERROR) {

            // コンボボックスコントロールに結果ハンドラを登録する
            error = _combobox->RegisterHandler(
                SFXEventRange(SFEVT_RESPONDER_RESULT, SFEVT_RESPONDER_RESULT, SFP16_BEGIN, SFP16_END),
                XANDLER_INTERNAL(OnComboBox)
            );

            if (error == SFERR_NO_ERROR) {

                // コンボボックスコントロールが保持するフレックスリストボックスコントロールに値ハンドラを登録する
                error = _combobox->GetListBoxControl()->RegisterHandler(
                    SFXEventRange(SFEVT_RESPONDER_VALUE, SFEVT_RESPONDER_VALUE, SFP16_VALUE_CURRENT, SFP16_VALUE_CURRENT),
                    XANDLER_INTERNAL(OnComboListValue)
                );

                if (error == SFERR_NO_ERROR) {

                    // コンボボックスコントロールの項目のテキスト
                    SFXWideString item[] = {"First", "Second", "Third", "Fourth", "Fifth", "Sixth", "Eighth", "Ninth", "Tenth"};

                    // 配列で項目をまとめて追加する
                    error = _combobox->InsertLast(item, lengthof(item));

                    if (error == SFERR_NO_ERROR) {

                        // コンボボックスコントロールの実領域を設定する
                        _combobox->SetRealBound(rect);

                        // コンボボックスコントロールの状態を「可視+活性+操作可能+非フォーカス」にまとめて設定する
                        _combobox->SetState(true, true, true, false);
                    }
                }
            }
        }
    }
    offs->AddY(rect.GetHeight() + Y_MARGIN);
    return error;
}
[Note] コンボボックスコントロールの操作

コンボボックスコントロールは、操作キーを使用して選択項目を操作します(実際に選択します)。

操作キーが押下される(選択項目が実際に選択される)と、 コンボボックスコントロールは結果イベントを受信します。 また、 コンボボックスコントロールが内部で保持するフレックスリストボックスコントロールの選択項目を移動すると、 フレックスリストボックスコントロールは値イベントを受信します。

SFYResponder::RegisterHandler 関数を使用して、 結果イベント 値イベントを受信する 結果ハンドラ値ハンドラをコンボボックスコントロールに登録することが可能です。

デフォルトでは、セレクトキー(AVK_SELECT)が操作キーに設定されています。 操作キーは、 SFYButtonControl::SetOperateKey 関数により変更可能です。

コンボボックスコントロールの選択項目が操作されたときや、 コンボボックスコントロールが内部で保持するフレックスリストボックスコントロールの選択項目が移動したときに呼び出される結果ハンドラ/値ハンドラを実装します。

この結果ハンドラ/値ハンドラは、 コンボボックスコントロールの操作項目/選択項目の内容をデバッグウィンドウに表示します。

例 3.36. コンボボックスコントロールの結果ハンドラ/フレックスリストボックスコントロールの値ハンドラの実装

// コンボボックスコントロールの結果ハンドラの実装
XANDLER_IMPLEMENT_VOIDRESULT(MyWindow, OnComboBox, invoker, reason, result)
{
    unused(invoker);
    unused(reason);

    if (reason == SFP16_RESULT_OK) {

        // 操作項目(=選択項目が実際に選択された項目)のテキストを取得する
        // result: 操作項目のインデックス
        SFXWideString string(_combobox->GetItemText(result));

        // 操作項目のインデックスとテキストをデバッグウィンドウに表示する
        TRACE("'CB%d: %S' is selected.", result, string.GetCString());
    }
    return;
}

// フレックスリストボックスコントロールの値ハンドラの実装
XANDLER_IMPLEMENT_VOIDVALUE(MyWindow, OnComboListValue, invoker, reason, value)
{
    unused(invoker);
    unused(reason);

    // 選択項目のインデックスをデバッグウィンドウに表示する
    // value: 選択項目のインデックス
    TRACE("'CB%d'", value);

    return;
}

シミュレータでの実行結果は以下の通りです。

図 3.15. 実行結果

実行結果

図 3.16. コンボボックスコントロールの選択項目を操作した(実際に選択した)ときのデバッグウィンドウ

コンボボックスコントロールの選択項目を操作した(実際に選択した)ときのデバッグウィンドウ

1 行目に選択項目のインデックス、2 行目に操作した(実際に選択した)選択項目のインデックスとテキストが出力されます。

参照: コンボボックスコントロール[SFZComboBoxControl]

3.5.5. テキストラベルコントロールとエディットボックスコントロール

下図のテキストラベルコントロール(SFZSingleTextLabelControl) とエディットボックスコントロール(SFZSingleEditBoxControl)を作成します。

図 3.17. テキストラベルコントロールとエディットボックスコントロール

テキストラベルコントロールとエディットボックスコントロール

エディットボックスコントロールに入力されたテキストとパスワードを比較して認証を行います。

テキストラベルコントロール、エディットボックスコントロール、 およびエディットボックスコントロールの結果ハンドラを宣言します。

例 3.37. テキストラベルコントロール、エディットボックスコントロール、およびエディットボックスコントロールの結果ハンドラの宣言

// MyWindow クラスの定義
SFMTYPEDEFRESPONDER(MyWindow)  //  便利な型を生成するマクロ
class MyWindow : public SFZWindow {

    SFMSEALRESPONDER(MyWindow)  // インスタンスのコピーを禁止するマクロ

    // SFYResponder からこのクラスに至るまでの継承順序を指定するマクロ
    SFMRESPONDERINSTANTIATEFOUR(MyWindow, SFZWindow, SFYContainer, SFYWidget, SFYResponder)
public:

    // 小文字アルファベットまたは記号 4 文字からなるタイプは予約されているので
    // MyWindow のタイプを大文字アルファベット('M', 'W', 'N', 'D')で定義する
    enum CodeEnum {
        CODE_TYPE = four_char_code('M', 'W', 'N', 'D')
    };
    SFMTYPEDEFTYPE(CodeEnum)

private:
    SFZTextButtonControlSmp _button1;
    SFZTextButtonControlSmp _button2;
    SFZTextButtonControlSmp _button3;
    SFZCheckboxControlSmp _checkbox1;
    SFZCheckboxControlSmp _checkbox2;
    SFZRadiobuttonControlSmp _radio1;
    SFZRadiobuttonControlSmp _radio2;
    Bool _ifShowHw;
    SFZComboBoxControlSmp _combobox;

    // *** 太字が追加部分

    // テキストラベルコントロールと エディットボックスコントロール
    SFZSingleTextLabelControlSmp _singleTextLabel;
    SFZSingleEditBoxControlSmp _singleEditBox;
public:
    static MyWindowSmp NewInstance(SFCErrorPtr exception = null);

    // MyWindow 内のコントロール作成
    SFCError Make(Void);

protected:
    explicit MyWindow(Void) static_throws;
    virtual ~MyWindow(Void);
    virtual Void HandleRenderRequest(SFXGraphicsPtr graphics) const;
private:
    SFCError CreateButtons(SFXGridPtr offs);
    SFCError CreateCheckBoxes(SFXGridPtr offs);
    SFCError CreateRadioButtons(SFXGridPtr offs);
    SFCError CreateComboBox(SFXGridPtr offs);

    // テキストラベルコントロールとエディットボックスコントロールを作成する関数
    SFCError CreatePassLine(SFXGridPtr offs);

    XANDLER_DECLARE_BOOLEVENT(OnKey)             // キーハンドラ
    XANDLER_DECLARE_VOIDRESULT(OnButton12)       // 結果ハンドラ
    XANDLER_DECLARE_VOIDRESULT(OnButton3)        // 結果ハンドラ
    XANDLER_DECLARE_VOIDRESULT(OnCheck)          // 結果ハンドラ
    XANDLER_DECLARE_VOIDRESULT(OnRadio)          // 結果ハンドラ
    XANDLER_DECLARE_VOIDRESULT(OnComboBox)       // 結果ハンドラ
    XANDLER_DECLARE_VOIDVALUE(OnComboListValue)  // 値ハンドラ

    // エディットボックスコントロールの結果ハンドラ
    XANDLER_DECLARE_VOIDRESULT(OnSingleEditBox)
};

テキストラベルコントロールとエディットボックスコントロールが作成されるように MyWindow::Make 関数を変更します。

例 3.38. MyWindow 内のコントロール作成

// MyWindow 内のコントロール作成
SFCError MyWindow::Make(Void)
{
    SFCError error;

    SFXGrid offset(0, 0);

    // MyWindow 上でコンテンツを描画する領域の左上端を設定する
    offset.Add(X_MARGIN, Y_MARGIN);

    // ボタンコントロールを作成する
    if ((error = CreateButtons(&offset)) != SFERR_NO_ERROR) {
        return error;
    }

    // チェックボックスコントロールを作成する
    if ((error = CreateCheckBoxes(&offset)) != SFERR_NO_ERROR) {
        return error;
    }

    // ラジオボタンコントロールを作成する
    if ((error = CreateRadioButtons(&offset)) != SFERR_NO_ERROR) {
        return error;
    }

    // コンボボックスコントロールを作成する
    if ((error = CreateComboBox(&offset)) != SFERR_NO_ERROR) {
        return error;
    }

    // *** 太字が追加部分

    // テキストラベルコントロールとエディットボックスコントロールを作成する
    if ((error = CreatePassLine(&offset)) != SFERR_NO_ERROR) {
        return error;
    }

    return error;
}

テキストラベルコントロールとエディットボックスコントロールを作成するコードを追加します。

エディットボックスコントロールの最大文字長を 10 文字にし、 入力された文字を伏字("*")で表示します。 また、テキストラベルコントロールは操作不能の状態にしてフォーカスが移動しないようにします。

例 3.39. テキストラベルコントロールとエディットボックスコントロールの作成

// テキストラベルコントロールとエディットボックスコントロールを作成する関数
SFCError MyWindow::CreatePassLine(SFXGridPtr offs)
{
    SFCError error;
    SFXRectangle rect;

    // テキストラベルコントロールを作成する
    if ((_singleTextLabel = SFZSingleTextLabelControl::NewInstance(&error)) != null) {

        // テキストラベルコントロールの親を MyWindow に設定する
        error = _singleTextLabel->SetParent(GetThis());

        if (error == SFERR_NO_ERROR) {

            // テキストラベルコントロールのテキストラベルを "PW:" に設定する
            error = _singleTextLabel->SetText("PW:");

            if (error == SFERR_NO_ERROR) {

                // テキストラベルコントロールの最適な実領域を設定する
                // ※1. GetSuitableBound 関数でテキストラベルコントロールの最適な領域サイズを計算する
                //      (テキストラベルコントロールの場合、設定されたテキストラベルの文字列から領域サイズが自動的に計算される)
                // ※2. GetSuitableBound 関数が計算する内容は領域サイズだけであり、始点は SetOrigin 関数などを使用して設定しなければいけない
                rect = _singleTextLabel->GetSuitableBound().SetOrigin(*offs)
                _singleTextLabel->SetRealBound(rect);

                // テキストラベルコントロールの状態を「可視+活性+操作不可+非フォーカス」にまとめて設定する
                _singleTextLabel->SetState(true, true, false, false);
            }
        }
    }

    // エディットボックスコントロールを作成する
    if ((_singleEditBox = SFZSingleEditBoxControl::NewInstance(&error)) != null) {

        // エディットボックスコントロールの親を MyWindow に設定する
        error = _singleEditBox->SetParent(GetThis());

        if (error == SFERR_NO_ERROR) {

            // エディットボックスコントロールに結果ハンドラを登録する
            error = _singleEditBox->RegisterHandler(
                SFXEventRange(SFEVT_RESPONDER_RESULT, SFEVT_RESPONDER_RESULT, SFP16_BEGIN, SFP16_END),
                XANDLER_INTERNAL(OnSingleEditBox)
            );

            if (error == SFERR_NO_ERROR) {

                // パスワードモードに設定する()
                error = _singleEditBox->SetPasswordMode(true);

                if (error == SFERR_NO_ERROR) {

                    // 最大文字長を 10 文字にする
                    error = _singleEditBox->SetMaximumLength(10);

                    if (error == SFERR_NO_ERROR) {

                        // 英数字の入力に限定する
                        _singleEditBox->SetInputMode(AEE_TM_LETTERS | AEE_TM_NUMBERS);

                        // テキストを左にアラインする
                        _singleEditBox->SetHorizontalAlign(SFZSingleEditBoxControl::HORIZONTAL_LEFT);

                        // エディットボックスコントロールの実領域を設定する
                        rect.Offset(rect.GetWidth(), 0);
                        rect.SetRight(GetLocalBound().GetWidth() - X_MARGIN);
                        _singleEditBox->SetRealBound(rect);

                        // エディットボックスコントロールの状態を「可視+活性+操作可能+非フォーカス」にまとめて設定する
                        _singleEditBox->SetState(true, true, true, false);
                    }
                }
            }
        }
    }
    offs->AddY(rect.GetHeight() + Y_MARGIN);
    return error;
}
[Note] エディットボックスコントロールの操作

エディットボックスコントロールは、操作キーを使用して操作します (フォーカス状態のときに操作キーを押下すると、テキスト入力が可能になります)。

テキストの内容が変更されると、 エディットボックスコントロールは結果イベントを受信します。

SFYResponder::RegisterHandler 関数を使用して、 結果イベントを受信する 結果ハンドラをエディットボックスコントロールに登録することが可能です。

デフォルトでは、セレクトキー(AVK_SELECT)が操作キーに設定されています。 操作キーは、 SFYBoxControl::SetOperateKey 関数により変更可能です。

エディットボックスコントロールのテキストが変更されたときに呼び出される結果ハンドラを追加します。

この結果ハンドラは、 パスワード認証の結果をデバッグウィンドウに表示します。

例 3.40. エディットボックスコントロールの結果ハンドラの実装

// エディットボックスコントロールの結果ハンドラの実装
XANDLER_IMPLEMENT_VOIDRESULT(MyWindow, OnSingleEditBox, invoker, reason, result)
{
    unused(invoker);
    unused(reason);
    unused(result);

    // 入力されたテキストとパスワードを比較する
    if (_singleEditBox->GetText().Compare("pass") == 0) {
        TRACE("password: authenticated!");
    } else {
        TRACE("password: can't be authenticated...");
    }

    return;
}

シミュレータでの実行結果は以下の通りです。

図 3.18. 実行結果

実行結果

図 3.19. テキスト入力画面

テキスト入力画面

図 3.20. パスワードを入力したときのデバッグウィンドウ

パスワードを入力したときのデバッグウィンドウ

パスワードの認証結果が出力されます。

参照: 単一行の編集不可能なテキストを表示するラベルコントロール [SFZSingleTextLabelControl] | 単一行の編集可能なテキストを表示するボックスコントロール[SFZSingleEditBoxControl]

3.5.6. リストボックスコントロール

下図のリストボックスコントロール(SFZListBoxControl)を作成します。

図 3.21. リストボックスコントロール

リストボックスコントロール

リストボックスコントロールとコンボボックスコントロールに関して、 項目のリストと選択項目は同期させます。

[Note] 2 種類のリストボックスコントロール

項目のリストから 1 つの項目を選択するリストボックスコントロールのクラスは、 SFZListBoxControl クラスと SFZFlexListBoxControl クラスの 2 種類あります。

SFZFlexListBoxControl クラスは、画像が扱えるなど SFZListBoxControl クラスよりも高機能な仕様になっています。

以下のサンプルコードでは、SFZListBoxControl クラスを使用しています。 なお、 SFZFlexListBoxControl クラスはコンボボックスコントロール (SFZComboBoxControl)で内部的に利用されています。

以下のコードでは、 リストボックスコントロールとそのハンドラを宣言しています。

例 3.41. リストボックスコントロールとハンドラの宣言

// MyWindow クラスの定義
SFMTYPEDEFRESPONDER(MyWindow)  //  便利な型を生成するマクロ
class MyWindow : public SFZWindow {

    SFMSEALRESPONDER(MyWindow)  // インスタンスのコピーを禁止するマクロ

    // SFYResponder からこのクラスに至るまでの継承順序を指定するマクロ
    SFMRESPONDERINSTANTIATEFOUR(MyWindow, SFZWindow, SFYContainer, SFYWidget, SFYResponder)
public:

    // 小文字アルファベットまたは記号 4 文字からなるタイプは予約されているので
    // MyWindow のタイプを大文字アルファベット('M', 'W', 'N', 'D')で定義する
    enum CodeEnum {
        CODE_TYPE = four_char_code('M', 'W', 'N', 'D')
    };
    SFMTYPEDEFTYPE(CodeEnum)

private:
    SFZTextButtonControlSmp _button1;
    SFZTextButtonControlSmp _button2;
    SFZTextButtonControlSmp _button3;
    SFZCheckboxControlSmp _checkbox1;
    SFZCheckboxControlSmp _checkbox2;
    SFZRadiobuttonControlSmp _radio1;
    SFZRadiobuttonControlSmp _radio2;
    Bool _ifShowHw;
    SFZComboBoxControlSmp _combobox;
    SFZSingleTextLabelControlSmp _singleTextLabel;
    SFZSingleEditBoxControlSmp _singleEditBox;

    // *** 太字が追加部分

    // リストボックスコントロール
    SFZListBoxControlSmp _listbox;
public:
    static MyWindowSmp NewInstance(SFCErrorPtr exception = null);

    // MyWindow 内のコントロール作成
    SFCError Make(Void);

protected:
    explicit MyWindow(Void) static_throws;
    virtual ~MyWindow(Void);
    virtual Void HandleRenderRequest(SFXGraphicsPtr graphics) const;
private:
    SFCError CreateButtons(SFXGridPtr offs);
    SFCError CreateCheckBoxes(SFXGridPtr offs);
    SFCError CreateRadioButtons(SFXGridPtr offs);
    SFCError CreateComboBox(SFXGridPtr offs);
    SFCError CreatePassLine(SFXGridPtr offs);

    // リストボックスコントロールを作成する関数
    SFCError CreateListBox(SFXGridPtr offs);

    XANDLER_DECLARE_BOOLEVENT(OnKey)             // キーハンドラ
    XANDLER_DECLARE_VOIDRESULT(OnButton12)       // 結果ハンドラ
    XANDLER_DECLARE_VOIDRESULT(OnButton3)        // 結果ハンドラ
    XANDLER_DECLARE_VOIDRESULT(OnCheck)          // 結果ハンドラ
    XANDLER_DECLARE_VOIDRESULT(OnRadio)          // 結果ハンドラ
    XANDLER_DECLARE_VOIDRESULT(OnComboBox)       // 結果ハンドラ
    XANDLER_DECLARE_VOIDVALUE(OnComboListValue)  // 値ハンドラ
    XANDLER_DECLARE_VOIDRESULT(OnSingleEditBox)  // 結果ハンドラ

    // リストボックスコントロールの結果ハンドラ
    XANDLER_DECLARE_VOIDRESULT(OnListBox)

    // リストボックスコントロールの値ハンドラ
    XANDLER_DECLARE_VOIDVALUE(OnListValue)
};

リストボックスコントロールが作成されるように MyWindow::Make 関数を変更します。

例 3.42. MyWindow 内のコントロール作成

// MyWindow 内のコントロール作成
SFCError MyWindow::Make(Void)
{
    SFCError error;

    SFXGrid offset(0, 0);

    // MyWindow 上でコンテンツを描画する領域の左上端を設定する
    offset.Add(X_MARGIN, Y_MARGIN);

    // ボタンコントロールを作成する
    if ((error = CreateButtons(&offset)) != SFERR_NO_ERROR) {
        TRACE("button error");
        return error;
    }

    // チェックボックスコントロールを作成する
    if ((error = CreateCheckBoxes(&offset)) != SFERR_NO_ERROR) {
        TRACE("check box error");
        return error;
    }

    // ラジオボタンコントロールを作成する
    if ((error = CreateRadioButtons(&offset)) != SFERR_NO_ERROR) {
        TRACE("radio button error");
        return error;
    }

    // コンボボックスコントロールを作成する
    if ((error = CreateComboBox(&offset)) != SFERR_NO_ERROR) {
        TRACE("combo error");
        return error;
    }

    // テキストラベルコントロールとエディットボックスコントロールを作成する
    if ((error = CreatePassLine(&offset)) != SFERR_NO_ERROR) {
        return error;
    }

    // *** 太字が追加部分

    // リストボックスコントロールを作成する
    if ((error = CreateListBox(&offset)) != SFERR_NO_ERROR) {
        return error;
    }

    return error;
}

リストボックスコントロールを作成するコードを追加します。 リストボックスコントロールの項目も追加します。

例 3.43. リストボックスコントロールの作成

// リストボックスコントロールを作成する関数
SFCError MyWindow::CreateListBox(SFXGridPtr offs)
{
    SFXEventRange::AtomRecConst range[] = {                          // リストボックスコントロールに登録するハンドラのイベント範囲
        {SFEVT_RESPONDER_RESULT, SFEVT_RESPONDER_RESULT, SFP16_BEGIN, SFP16_END},
        {SFEVT_RESPONDER_VALUE, SFEVT_RESPONDER_VALUE, SFP16_VALUE_CURRENT, SFP16_VALUE_CURRENT}
    };
    SFYHandler::RuleRec rule[lengthof(range)];                       // ハンドラルール
    SInt16Const width = (GetLocalBound().GetWidth() - 2 * X_MARGIN); // リストボックスコントロールの横幅
    SFXRectangle rect(*offs, width, 0);                              // リストボックスコントロールの実領域
    SFCError error(SFERR_NO_ERROR);

    // リストボックスコントロールを作成する
    if ((_listbox = SFZListBoxControl::NewInstance(&error)) != null) {

        // リストボックスコントロールの親を MyWindow に設定する
        error = _listbox->SetParent(GetThis());

        if (error == SFERR_NO_ERROR) {

            // リストボックスコントロールのハンドラルールを設定する
            rule[0].spp = XANDLER_FUNCTION(OnListBox);               // リストボックスコントロールの結果ハンドラを設定
            rule[0].reference = this;                                // MyWindow のポインタ(OnListBox の実体があるインスタンスのポインタ)を設定
            rule[1].spp = XANDLER_FUNCTION(OnListValue);             // リストボックスコントロールの値ハンドラを設定
            rule[1].reference = this;                                // MyWindow のポインタ(OnListValue の実体があるインスタンスのポインタ)を設定

            // リストボックスコントロールのハンドラをまとめて登録する
            error = _listbox->RegisterHandler(atomic_cast(range), rule, lengthof(range));

            if (error == SFERR_NO_ERROR) {

                // リストボックスコントロールに表示する項目
                SFXWideString item[] = {"First", "Second", "Third", "Fourth", "Fifth", "Sixth", "Eighth", "Ninth", "Tenth"};

                for (SInt16 i = 0; i < lengthof(item); ++i) {

                    // リストボックスコントロールに項目を追加する
                    error = _listbox->InsertLast(item[i]);

                    if (error != SFERR_NO_ERROR) {
                        break;
                    }
                }
                if (error == SFERR_NO_ERROR) {

                    // 選択項目を 1 つ上の項目に移動させる選択キーを←キーに設定する
                    _listbox->SetUpKey(AVK_LEFT);

                    // 選択項目を 1 つ下の項目に移動させる選択キーを→キーに設定する
                    _listbox->SetDownKey(AVK_RIGHT);

                    // 実領域の高さを最大で 80 ピクセルに設定する
                    rect.SetHeight(80);

                    // リストボックスコントロールの実領域を設定する
                    // ※ GetSuitableBound 関数の引数に rect を指定しているので計算結果の領域サイズの高さは 80 ピクセル以下の最適な値になる
                    _listbox->SetRealBound(_listbox->GetSuitableBound(rect).SetWidth(rect.GetWidth()));

                    // リストボックスコントロールの状態を「可視+活性+操作可能+非フォーカス」にまとめて設定する
                    _listbox->SetState(true, true, true, false);
                }
            }
        }
    }
    offs->AddY(rect.GetHeight() + Y_MARGIN);
    return error;
}
[Note] リストボックスコントロールの操作

リストボックスコントロールは、選択キーにより選択項目を移動し、 操作キーまたはアクセスキーにより操作します(選択項目を実際に選択します)。

操作キーまたはアクセスキーを押下すると、 リストボックスコントロールは結果イベントを受信します。

選択項目が移動すると、 リストボックスコントロールは値イベントを受信します。

SFYResponder::RegisterHandler 関数を使用して、 結果イベント値イベントを受信する 結果ハンドラ値ハンドラをリストボックスコントロールに登録することが可能です。

デフォルトでは、↑/↓キーが選択キーに設定されています。 選択キーは、 SFZListBoxControl::SetUpKey / SFZListBoxControl::SetDownKey 関数により変更可能です。

デフォルトでは、 セレクトキー(AVK_SELECT)が操作キーに設定されています。 操作キーは、 SFYBoxControl::SetOperateKey 関数により変更可能です。

アクセスキーとは、項目にキーを割り当て、 そのキーが押下されると、割り当てた項目を選択項目にし、かつ実際に選択する(操作する)機能です。 SFZListBoxControl::SetItemAccessKey 関数を使用して設定します。

[Note] 自動スクロール機能

リストボックスコントロールでは、 実領域内に一度に表示できない数の項目がある場合、 スクロールして項目を表示します(スクロールバーも表示されます)。

[Caution] コンテナとリストボックスコントロールのキー設定のバッティング

リストボックスコントロールの選択項目を移動させる選択キーと、 コンテナのスクロールキー(フォーカス移動キー)は、 デフォルトでは共に↑/↓キーに設定されています。

このままでは、 リストボックスコントロールから MyWindow 内の他のコントロールにフォーカスを移動できません。

そこで、上の例では SFZListBoxControl::SetUpKeySFZListBoxControl::SetDownKey 関数を使用してリストボックスコントロールの選択キーの設定を ←/→キーに変更しています。

参照: SFZListBoxControl::SetUpKey | SFZListBoxControl::SetDownKey | SFYContainer::SetScrollUpKey | SFYContainer::SetScrollDownKey

以下のように各ハンドラを実装します。

  1. リストボックスコントロールの結果ハンドラの実装: [1]リストボックスコントロールの項目のインデックスとテキストをデバッグウィンドウに表示する [2]コンボボックスコントロールをリストボックスコントロールに同期させる
  2. リストボックスコントロールの値ハンドラの実装: フォーカスのある項目のインデックスをデバッグウィンドウに表示する
  3. コンボボックスコントロールの結果ハンドラの実装の変更: リストボックスコントロールをコンボボックスコントロールに同期させる

例 3.44. リストボックスコントロールとコンボボックスコントロールのハンドラの実装

// リストボックスコントロールの結果ハンドラの実装
XANDLER_IMPLEMENT_VOIDRESULT(MyWindow, OnListBox, invoker, reason, result)
{
    unused(invoker);
    unused(reason);

    if (reason == SFP16_RESULT_OK) {

        // 選択された項目のテキストを取得する
        // result: 選択項目のインデックス
        SFXWideString string(_listbox->GetItemText(result));

        // 選択項目のインデックスとテキストをデバッグウィンドウに表示する
        TRACE("'LB%d: %S' is selected.", result, string.GetCString());

        // コンボボックスコントロールをリストボックスコントロールに同期させる
        _combobox->SetCurrentValue(result);
    }
    return;
}

// リストボックスコントロールの値ハンドラの実装
XANDLER_IMPLEMENT_VOIDVALUE(MyWindow, OnListValue, invoker, reason, value)
{
    unused(invoker);
    unused(reason);

    // 選択項目のインデックスをデバッグウィンドウに表示する
    // value: 選択項目のインデックス
    TRACE("'LB%d'", value);

    return;
}

// コンボボックスコントロールの結果ハンドラの実装
XANDLER_IMPLEMENT_VOIDRESULT(MyWindow, OnComboBox, invoker, reason, result)
{
    unused(invoker);
    unused(reason);

    if (reason == SFP16_RESULT_OK) {

        // 選択項目のテキストを取得する
        // result: 選択項目のインデックス
        SFXWideString string(_combobox->GetItemText(result));

        // 選択項目のインデックスとテキストをデバッグウィンドウに表示する
        TRACE("'CB%d: %S' is selected.", result, string.GetCString());

        // *** 太字が追加部分

        // リストボックスコントロールをコンボボックスコントロールに同期させる
        _listbox->SetCurrentValue(result);
    }
    return;
}

シミュレータでの実行結果は以下の通りです。

図 3.22. 実行結果

実行結果

図 3.23. リストボックスコントロールの選択項目を操作した(実際に選択した)ときのデバッグウィンドウ

リストボックスコントロールの選択項目を操作した(実際に選択した)ときのデバッグウィンドウ

操作した(実際に選択した)選択項目のインデックスとテキストが出力されます。 同期させているコンボボックスコントロールについてもデバッグ出力されます。

参照: リストボックスコントロール[SFZListBoxControl]