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

9.21. コントロール(応用編)

この節では、 複数のコントロールを同時に使用する方法や、コントロール同士の連携などについて解説します。

9.21.1. フォーカス枠の色を変更する

図 9.77. 動作例

動作例

SFZTextButtonControlSFZImageButtonControl などのコントロールでは、 フォーカス状態の場合に描画されるフォーカス枠の色を変更できます。

この機能は、 コントロールがフォーカス状態にあることをより強調する場合や、 インターフェースの全体的な色のセットを変更する場合に利用します。

例 9.141. 宣言

SFMTYPEDEFRESPONDER(USRWindow)
class USRWindow: public SFZWindow {
    SFMSEALRESPONDER(USRWindow)
    SFMRESPONDERINSTANTIATEFOUR(USRWindow, SFZWindow, SFYContainer, SFYWidget, SFYResponder)
private:
    SFZTextButtonControlSmp _button;

    // ...(省略)...
private:
    SFCError Make(Void);
};

例 9.142. 実装

SFCError USRWindow::Make(Void)
{
    SFCError error(SFERR_NO_ERROR);

    if ((_button = SFZTextButtonControl::NewInstance(&error)) != null) {

        error = _button->SetParent(GetThis());
        if (error == SFERR_NO_ERROR) {

            error = _button->SetText("hello world");
            if (error == SFERR_NO_ERROR) {

                // デフォルトのフォーカス枠の色を取得し、RGB カラーの赤の要素を加算して設定する
                _button->SetFocusColor(SFXBevelColor(_button->GetFocusColor()).AddRed(0xFF));

                _button->SetRealBound(_button->GetSuitableBound().SetOrigin(10, 10));
                _button->SetState(true, true, true, true);
            }
        }
    }

    return error;
}

9.21.2. コントロールの色を変更する

図 9.78. 動作例

動作例

SFZTextButtonControlSFZImageButtonControl などのコントロールでは、 フォーカス枠の色に加え、コントロールの各個所の色を変更できます。

変更できる個所は、コントロールの種類や実装に依存します。

例 9.143. 宣言

SFMTYPEDEFRESPONDER(USRWindow)
class USRWindow: public SFZWindow {
    SFMSEALRESPONDER(USRWindow)
    SFMRESPONDERINSTANTIATEFOUR(USRWindow, SFZWindow, SFYContainer, SFYWidget, SFYResponder)
private:
    SFZTextButtonControlSmp _button;

    // ...(省略)...
private:
    SFCError Make(Void);
};

例 9.144. 実装

SFCError USRWindow::Make(Void)
{
    SFCError error(SFERR_NO_ERROR);

    if ((_button = SFZTextButtonControl::NewInstance(&error)) != null) {

        error = _button->SetParent(GetThis());
        if (error == SFERR_NO_ERROR) {

            error = _button->SetText("hello world");
            if (error == SFERR_NO_ERROR) {

                // デフォルトのフォーカス枠の色を取得し、RGB カラーの赤と緑の要素を減算して設定する
                _button->SetFocusColor(SFXBevelColor(_button->GetFocusColor()).SubRGB(0x33, 0x33, 0x00));

                // デフォルトのボタン色を取得し、RGB カラーの赤と緑の要素を減算して設定する
                _button->SetButtonColor(SFXBevelColor(_button->GetButtonColor()).SubRGB(0x33, 0x33, 0x00));

                _button->SetRealBound(_button->GetSuitableBound().SetOrigin(10, 10));
                _button->SetState(true, true, true, true);
            }
        }
    }

    return error;
}

9.21.3. 複数行のテキストを表示するレスポンダに最適な大きさを取得する

図 9.79. 動作例

動作例

SFZMultipleTextLabelControlSFZMultipleTextBoxControl などの複数行のテキストを表示するレスポンダでは、 矩形領域の引数を指定しない SFYResponder::GetSuitableBound 関数の振る舞いが他のレスポンダとは異なります。

このレスポンダでは、複数行のテキストを表示するために最適な領域を計算するためのヒント値が必要です。

具体的には、 これらのレスポンダの SFYResponder::GetSuitableBound 関数を呼び出すときに矩形領域の引数を指定しない場合、 SFYResponder::SetRealBound 関数で設定する実領域の横幅がヒント値として利用されます。

[Note] 注意
実領域の横幅(ヒント値)には使用しているフォントの横幅以上の値を設定します。

領域計算のヒント値として以下のように利用されます。

  1. 複数行のテキストは "\n(改行)" で区切られた複数の文字列に分割されます。
  2. ある文字列の長さがヒント値を超える場合はこのヒント値の幅で文字列を折り返します。
  3. 文字列を折り返す必要がなかった場合は、分割された文字列のなかで最長の横幅が計算結果の領域の横幅になります。 逆に文字列を折り返す必要があった場合は、 ヒント値を超えないが、 1 文字分の横幅の整数倍で最大値が計算結果の領域の横幅になります。
  4. ヒント値の幅で文字列の折り返し処理を行った後の複数行テキストの行数分の縦幅が計算結果の縦幅となります。

SFYResponder::GetSuitableBound 関数で矩形領域の引数を指定する場合、 領域計算をする直前に実領域をヒント値として設定する必要はありません。

表 9.31. 引数を指定せずに GetSuitableBound 関数を呼び出す直前に実領域の横幅を設定する必要のあるレスポンダ

クラス名 解説
SFYMultipleTextWidget 複数行の編集不可能なテキストを表示するウィジェットです。
SFYMultipleEditWidget 複数行の編集可能なテキストを表示するウィジェットです。
SFZMultipleTextLabelControl 複数行の編集不可能なテキストを表示するラベルコントロールです。
SFZMultipleEditLabelControl 複数行の編集可能なテキストを表示するラベルコントロールです。
SFZMultipleTextBoxControl 複数行の編集不可能なテキストを表示するボックスコントロールです。
SFZMultipleEditBoxControl 複数行の編集可能なテキストを表示するボックスコントロールです。
SFZMessageDialog 通知メッセージを表示するダイアログです。
SFZQuestionDialog 選択メッセージを表示するダイアログです。

例 9.145. 宣言

SFMTYPEDEFRESPONDER(USRWindow)
class USRWindow: public SFZWindow {
    SFMSEALRESPONDER(USRWindow)
    SFMRESPONDERINSTANTIATEFOUR(USRWindow, SFZWindow, SFYContainer, SFYWidget, SFYResponder)
private:
    SFZMultipleTextLabelControlSmp _label;

    // ...(省略)...
private:
    SFCError Make(Void);
};

例 9.146. 実装

SFCError USRWindow::Make(Void)
{
    SFCError error(SFERR_NO_ERROR);

    if ((_label = SFZMultipleTextLabelControl::NewInstance(&error)) != null) {

        error = _label->SetParent(GetThis());
        if (error == SFERR_NO_ERROR) {

            error = _label->SetText("hello world\n\n"
                                    "0          \n"
                                    " 1         \n"
                                    "  2        \n"
                                    "   3       \n"
                                    "    4      \n"
                                    "     5     \n"
                                    "      6    \n"
                                    "       7   \n"
                                    "        8  \n"
                                    "         9 \n"
                                    "          0");
            if (error == SFERR_NO_ERROR) {

                // ヒント値を設定する
                _label->SetRealBound(GetLocalBound().Deflate(10, 10));

                // 矩形を指定せずに最適な大きさを取得して設定する
                _label->SetRealBound(_label->GetSuitableBound().SetOrigin(10, 10));

                _label->SetState(true, true, true, true);
            }
        }
    }

    return error;
}

参照: 実領域 | ローカル領域 | 仮想領域

9.21.4. 複数のコントロールを連続して配置する

図 9.80. 動作例

動作例

1 つのコンテナ内に複数のコントロールを配置する場合、 SFYResponder::GetSuitableBound 関数を利用することで見栄えのよいインターフェースを作成できます。

例 9.147. 宣言

SFMTYPEDEFRESPONDER(USRWindow)
class USRWindow: public SFZWindow {
    SFMSEALRESPONDER(USRWindow)
    SFMRESPONDERINSTANTIATEFOUR(USRWindow, SFZWindow, SFYContainer, SFYWidget, SFYResponder)
private:
    SFZTextButtonControlSmp _button;
    SFZSingleTextBoxControlSmp _box;
    SFZCheckboxControlSmp _checkbox;

    // ...(省略)...
private:
    SFCError Make(Void);
};

例 9.148. 実装

SFCError USRWindow::Make(Void)
{
    SFXRectangle rectangle;
    SFCError error(SFERR_NO_ERROR);

    // 1 つ目のコントロールの左上の座標を設定する
    rectangle.Set(GetLocalBound().Deflate(10, 10));

    // 1 つ目のコントロールを作成する
    if ((_button = SFZTextButtonControl::NewInstance(&error)) != null) {

        error = _button->SetParent(GetThis());
        if (error == SFERR_NO_ERROR) {

            error = _button->SetText("first");
            if (error == SFERR_NO_ERROR) {

                _button->SetRealBound(_button->GetSuitableBound(rectangle));
                _button->SetState(true, true, true, true);

                // 2 つ目のコントロールの左上の座標を計算する
                rectangle.AddY(_button->GetRealBound().GetHeight() + 5);

                // 2 つ目のコントロールを作成する
                if ((_box = SFZSingleTextBoxControl::NewInstance(&error)) != null) {

                    error = _box->SetParent(GetThis());
                    if (error == SFERR_NO_ERROR) {

                        error = _box->SetText("second");
                        if (error == SFERR_NO_ERROR) {

                            _box->SetRealBound(_box->GetSuitableBound(rectangle));
                            _box->SetState(true, true, true, false);

                            //  3 つ目のコントロールの左上の座標を計算する
                            rectangle.AddY(_box->GetRealBound().GetHeight() + 5);

                            //  3 つ目のコントロールを作成する
                            if ((_checkbox = SFZCheckboxControl::NewInstance(&error)) != null) {

                                error = _checkbox->SetParent(GetThis());
                                if (error == SFERR_NO_ERROR) {

                                    error = _checkbox->SetText("third");
                                    if (error == SFERR_NO_ERROR) {

                                        _checkbox->SetRealBound(_checkbox->GetSuitableBound(rectangle));
                                        _checkbox->SetState(true, true, true, false);

                                        //  4 つ目のコントロールの左上の座標を計算する
                                        rectangle.AddY(_checkbox->GetRealBound().GetHeight() + 5);
                                        // ...(省略)...
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    return error;
}

9.21.5. ラジオボタンコントロールをグループ化する

図 9.81. 動作例

動作例

以下のコードでは、 複数のラジオボタンコントロールをグループ化することにより、 排他的なチェック状態の切り替えが自動的に実現されています。

[Note] 注意
自動的な排他処理はユーザーのキー操作によってのみ行われ、 SFYControl::SetCurrentValue 関数を使用してチェック状態を切り替える場合には行われません。

例 9.149. 宣言

SFMTYPEDEFRESPONDER(USRWindow)
class USRWindow: public SFZWindow {
    SFMSEALRESPONDER(USRWindow)
    SFMRESPONDERINSTANTIATEFOUR(USRWindow, SFZWindow, SFYContainer, SFYWidget, SFYResponder)
private:
    SFZRadiobuttonControlSmp _radiobutton[5];

    // ...(省略)...
private:
    SFCError Make(Void);
};

例 9.150. 実装

SFCError USRWindow::Make(Void)
{
    SFXRectangle rectangle;
    SInt16 i;
    SFCError error(SFERR_NO_ERROR);

    rectangle.Set(GetLocalBound().Deflate(10, 10));

    // ラジオボタンコントロールをまとめて作成する
    for (i = 0; i < 5 & error == SFERR_NO_ERROR; ++i) {

        if ((_radiobutton[i] = SFZRadiobuttonControl::NewInstance(&error)) != null) {

            error = _radiobutton[i]->SetParent(GetThis());
            if (error == SFERR_NO_ERROR) {

                error = _radiobutton[i]->SetText(SFXWideString::Format("number %d", i));
                if (error == SFERR_NO_ERROR) {

                    _radiobutton[i]->SetRealBound(_radiobutton[i]->GetSuitableBound(rectangle));
                    _radiobutton[i]->SetState(true, true, true, false);
                    rectangle.AddY(_radiobutton[i]->GetRealBound().GetHeight() + 5);
                }
            }
        }
    }
    if (error == SFERR_NO_ERROR) {

        // すべてのラジオボタンコントロールをグループ化する
        for (i = 1; i < 5; ++i) {

            _radiobutton[i]->Group(_radiobutton[0]);
        }

        // 1 つ目のラジオボタンコントロールをチェックされている状態に設定する
        _radiobutton[0]->SetCurrentValue(true);

        // 1 つ目のラジオボタンコントロールにフォーカスを移動する
        _radiobutton[0]->SetStateFocus(true);
    }

    return error;
}

9.21.6. チェックボックスコントロールの状態に応じて活性状態を切り替える

図 9.82. 動作例

動作例

以下のコードでは、 チェックボックスコントロールの状態に応じて、 関連するコントロールの活性状態を切り替えることによりユーザービリティの高いインターフェースを実現しています。

例 9.151. 宣言

SFMTYPEDEFRESPONDER(USRWindow)
class USRWindow: public SFZWindow {
    SFMSEALRESPONDER(USRWindow)
    SFMRESPONDERINSTANTIATEFOUR(USRWindow, SFZWindow, SFYContainer, SFYWidget, SFYResponder)
private:
    SFZCheckboxControlSmp _checkbox[2];
    SFZRadiobuttonControlSmp _radiobutton[3];
    SFZSingleEditBoxControlSmp _editbox;

    // ...(省略)...
private:
    SFCError Make(Void);
    SFCError Init(SFYResponderSmpConstRef responder, SFXRectanglePtr rectangle);

    // 1 つ目のチェックボックスコントロールのチェック変更イベントを受信するハンドラ
    XANDLER_DECLARE_VOIDVALUE(OnValue0)

    // 2 つ目のチェックボックスコントロールのチェック変更イベントを受信するハンドラ
    XANDLER_DECLARE_VOIDVALUE(OnValue1)
};

例 9.152. 実装

SFCError USRWindow::Make(Void)
{
    static ACharConst item[][8] = {
        "Unix", "Mac", "Windows"
    };
    SFXRectangle rectangle;
    SInt16 i;
    SFCError error(SFERR_NO_ERROR);

    rectangle.Set(GetLocalBound().Deflate(10, 10));
    if ((_checkbox[0] = SFZCheckboxControl::NewInstance(&error)) != null) {

        // チェックボックスコントロールのチェック変更イベントを受信するハンドラを登録する
        error = _checkbox[0]->RegisterHandler(
            SFXEventRange(SFEVT_RESPONDER_VALUE, SFEVT_RESPONDER_VALUE, SFP16_VALUE_CURRENT, SFP16_VALUE_CURRENT),
            XANDLER_INTERNAL(OnValue0)
        );
        if (error == SFERR_NO_ERROR) {

            error = _checkbox[0]->SetText("Use linebreak");
            if (error == SFERR_NO_ERROR) {

                error = Init(_checkbox[0], &rectangle);
                if (error == SFERR_NO_ERROR) {

                    rectangle.AddLeft(10);
                    for (i = 0; i < 3 & error == SFERR_NO_ERROR; ++i) {

                        if ((_radiobutton[i] = SFZRadiobuttonControl::NewInstance(&error)) != null) {

                            error = _radiobutton[i]->SetText(item[i]);
                            if (error == SFERR_NO_ERROR) {

                                error = Init(_radiobutton[i], &rectangle);
                            }
                        }
                    }
                    for (i = 1; i < 3 & error == SFERR_NO_ERROR; ++i) {

                        _radiobutton[i]->Group(_radiobutton[0]);
                    }
                    rectangle.SubLeft(10);
                }
            }
        }
    }
    if (error == SFERR_NO_ERROR) {

        if ((_checkbox[1] = SFZCheckboxControl::NewInstance(&error)) != null) {

            // チェックボックスコントロールのチェック変更イベントを受信するハンドラを登録する
            error = _checkbox[1]->RegisterHandler(
                SFXEventRange(SFEVT_RESPONDER_VALUE, SFEVT_RESPONDER_VALUE, SFP16_VALUE_CURRENT, SFP16_VALUE_CURRENT),
                XANDLER_INTERNAL(OnValue1)
            );
            if (error == SFERR_NO_ERROR) {

                error = _checkbox[1]->SetText("Use suffix");
                if (error == SFERR_NO_ERROR) {

                    error = Init(_checkbox[1], &rectangle);
                    if (error == SFERR_NO_ERROR) {

                        rectangle.AddLeft(10);
                        if ((_editbox = SFZSingleEditBoxControl::NewInstance(&error)) != null) {
                            error = _editbox->SetText(".txt");

                            if (error == SFERR_NO_ERROR) {

                                error = Init(_editbox, &rectangle);
                            }
                        }
                        rectangle.SubLeft(10);
                    }
                }
            }
        }
    }
    if (error == SFERR_NO_ERROR) {

        // 初期状態を調整する
        _checkbox[0]->SetCurrentValue(true);
        _radiobutton[0]->SetCurrentValue(true);
        _checkbox[1]->SetCurrentValue(true);
        _checkbox[0]->SetStateFocus(true);
    }

    return error;
}

SFCError USRWindow::Init(SFYResponderSmpConstRef responder, SFXRectanglePtr rectangle)
{
    SFCError error(SFERR_NO_ERROR);

    error = responder->SetParent(GetThis());
    if (error == SFERR_NO_ERROR) {

        responder->SetRealBound(responder->GetSuitableBound(*rectangle));
        responder->SetState(true, true, true, false);
        rectangle->AddY(responder->GetRealBound().GetHeight() + 5);
    }

    return error;
}

XANDLER_IMPLEMENT_VOIDVALUE(USRWindow, OnValue0, invoker, reason, value)
{
    SInt16 i;

    // チェックボックスコントロールの状態に応じて、
    // 活性状態を切り替える
    for (i = 0; i < 3; ++i) {

        _radiobutton[i]->SetStateActive(value != 0);
    }

    return;
}

XANDLER_IMPLEMENT_VOIDVALUE(USRWindow, OnValue1, invoker, reason, value)
{
    // チェックボックスコントロールの状態に応じて、
    // 活性状態を切り替える
    _editbox->SetStateActive(value != 0);

    return;
}