前のページ次のページ上に戻るホーム BREW C++ ライブラリ & GUI フレームワーク : SophiaFramework 3.0

4.7. 複数ウィンドウの制御

複数のウィンドウを扱ってみます。

4.7.1. 異なる種類のウィンドウ

異なる種類のウィンドウを2枚作ります。

ウィンドウ(やコントロール)には、検索、識別のための「アトリビュート」を設定することができます。

// アトリビュートの設定
SFMRESPONDERATTRIBUTE(MYWINDOW1, four_char_code('W', 'd', 'w', '1'))

SFMTYPEDEFCLASS(MyWindow1)
class MyWindow1 : public SFRTitleWindow {
    SFMSEALCOPY(MyWindow1)
public:
    MyWindow1(Void) static_throws;
    virtual ~MyWindow1(Void);
    HANDLER_DECLARE_VOIDRENDER(MyWindow1, OnRenderContent)
    HANDLER_DECLARE_BOOLEVENT(MyWindow1, OnKey)
    HANDLER_DECLARE_VOIDCONTROL(MyWindow1, OnButtonControl)
    HANDLER_DECLARE_VOIDCONTROL(MyWindow1, OnCheckboxControl)
};

SFMRESPONDERATTRIBUTE(MYWINDOW2, four_char_code('W', 'd', 'w', '2'))

SFMTYPEDEFCLASS(MyWindow2)
class MyWindow2 : public SFRTitleWindow {
    SFMSEALCOPY(MyWindow2)
public:
    MyWindow2(Void) static_throws;
    virtual ~MyWindow2(Void);
    HANDLER_DECLARE_VOIDRENDER(MyWindow2, OnRenderContent)
    HANDLER_DECLARE_BOOLEVENT(MyWindow2, OnKey)
    HANDLER_DECLARE_VOIDCONTROL(MyWindow2, OnButtonControl1)
    HANDLER_DECLARE_VOIDCONTROL(MyWindow2, OnButtonControl2)
};

// コンストラクタ
MyWindow1::MyWindow1(Void) : SFRTitleWindow(SFRApplication::GetInstance(),
                                            SFXRectangle(10, 10, 220, 140),
                                            "my window1", ALIGN_CENTER,
                                            BEHAVIOR_TITLEWINDOW,
                                            ATTRIBUTE_MYWINDOW1  // アトリビュートの設定
                                            ) static_throws
{
    // 描画ハンドラの登録
    if (static_try()) {
        static_throw(
            RegisterHandler(SREVT_RESPONDER_RENDER, SRP16_RENDER_CONTENT,
                HANDLER_BEFORE, HANDLER_FUNCTION(OnRenderContent)));
    }
    // キーハンドラの登録
    if (static_try()) {
        static_throw(
            RegisterHandler(SFEVT_KEY, HANDLER_AFTER, HANDLER_FUNCTION(OnKey)));
    }

    // ボタンの作成
    SFRButtonControlPtr button = new SFRButtonControl(this,
        SFXRectangle(10, 10, 100, 25), "change");

    // ボタンを押したときのハンドラを登録
    if (static_try()) {
        static_throw(
            button->RegisterHandler(
                SREVT_CONTROL, HANDLER_BEFORE, HANDLER_FUNCTION(OnButtonControl))
        );
    }

    // チェックボックスの作成
    SFRCheckboxControlPtr checkbox;
    checkbox = new SFRCheckboxControl(this, SFXRectangle(10, 44, 100, 20), "check1");

    // チェックボックスのチェックを変更したときのハンドラ
    if (static_try()) {
        static_throw(
            checkbox->RegisterHandler(
                SREVT_CONTROL, HANDLER_BEFORE, HANDLER_FUNCTION(OnCheckboxControl))
        );
    }

    // チェックボックスの作成
    checkbox = new SFRCheckboxControl(this, SFXRectangle(10, 70, 100, 20), "check2");
    
    // チェックボックスのチェックを変更したときのハンドラ
    if (static_try()) {
        static_throw(
            checkbox->RegisterHandler(
                SREVT_CONTROL, HANDLER_BEFORE, HANDLER_FUNCTION(OnCheckboxControl))
        );
    }
    return;
}

// デストラクタ
MyWindow1::~MyWindow1(Void)
{
    return;
}

// 描画ハンドラ
HANDLER_IMPLEMENT_VOIDRENDER(MyWindow1, OnRenderContent, graphics)
{
    // 背景の塗りつぶし
    graphics->FillRectangle(GetContentWorld(), SFXRGBColor(0xCC, 0xFF, 0xFF, 0x00));

    // ウィンドウの検索(MyWindow2 を探す)
    SFRWindowPtr window = static_cast<SFRWindowPtr>(
        GetNext(TYPE_WINDOW, ATTRIBUTE_MYWINDOW2));

    if (window != null) { // 見つかったなら
        SFRBrewTextControlPtr textcontrol;
        // MyWindow2 にあるテキストコントロールの検索
        textcontrol = static_cast<SFRBrewTextControlPtr>(
            window->GetFront(TYPE_CONTROL, ATTRIBUTE_BREWTEXTCONTROL));

        if (textcontrol != null) { // 見つかったなら
            // そのテキストを描画
            graphics->DrawText(textcontrol->GetText(), SFXRectangle(120, 10, 100, 30),
                SFXRGBColor(0x00, 0x00, 0x00, 0x00));
        }
    }
    
    return;
}

// キー ハンドラ
HANDLER_IMPLEMENT_BOOLEVENT(MyWindow1, OnKey, event)
{
    switch (event.GetP16()) {
        case AVK_CLR:
            // ウィンドウを閉じる
            return Invoke(SFXEvent(SREVT_RESPONDER_TERMINATE, SRP16_TERMINATE_INVOKE, true));
        case AVK_LEFT:
            // フォーカスの移動
            FocusLeft();
            return true;
        case AVK_RIGHT:
            FocusRight();
            return true;
        case AVK_UP:
            FocusUp();
            return true;
        case AVK_DOWN:
            FocusDown();
            return true;
    }
    return false;
}

// ボタンハンドラ
HANDLER_IMPLEMENT_VOIDCONTROL(MyWindow1, OnButtonControl, result, control)
{
    // MyWindow2 の検索
    SFRWindowPtr window = static_cast<SFRWindowPtr>(
        GetNext(TYPE_WINDOW, ATTRIBUTE_MYWINDOW2));
    
    if (window != null) { // 見つかったなら
        window->Select(); // それを最前面にする
        window->SetStatusFocus(true); // MyWindow2 にフォーカスを当てる
    }
}

// チェックボックスハンドラ(チェックボックスのチェックが変更されたときに実行される)
HANDLER_IMPLEMENT_VOIDCONTROL(MyWindow1, OnCheckboxControl, result, control)
{
    // MyWindow2 の検索
    SFRWindowPtr window = static_cast<SFRWindowPtr>(
        GetNext(TYPE_WINDOW, ATTRIBUTE_MYWINDOW2));

    if (window != null) { // 見つかったなら
        window->InvalidateContent(); // MyWindow2 の再描画
    }
}

// コンストラクタ
MyWindow2::MyWindow2(Void) : SFRTitleWindow(SFRApplication::GetInstance(),
                                            SFXRectangle(10, 160, 220, 110),
                                            "my window2", ALIGN_CENTER,
                                            BEHAVIOR_TITLEWINDOW,
                                            ATTRIBUTE_MYWINDOW2  // アトリビュートの設定
                                            ) static_throws
{
    // 描画ハンドラの登録
    if (static_try()) {
        static_throw(
            RegisterHandler(
                SREVT_RESPONDER_RENDER, SRP16_RENDER_CONTENT,
                HANDLER_BEFORE, HANDLER_FUNCTION(OnRenderContent)));
    }
    // キーハンドラの登録
    if (static_try()) {
        static_throw(
            RegisterHandler(SFEVT_KEY, HANDLER_AFTER, HANDLER_FUNCTION(OnKey)));
    }

    // ボタンの作成
    SFRButtonControlPtr button;
    button = new SFRButtonControl(this, 
        SFXRectangle(10, 10, 40, 25), "変");

    // ボタンハンドラの登録
    if (static_try()) {
        static_throw(
            button->RegisterHandler(
                SREVT_CONTROL, HANDLER_BEFORE, HANDLER_FUNCTION(OnButtonControl1))
        );
    }

    // ボタンの作成
    button = new SFRButtonControl(this, 
        SFXRectangle(60, 10, 40, 25), "描");

    // ボタンハンドラの登録
    if (static_try()) {
        static_throw(
            button->RegisterHandler(
                SREVT_CONTROL, HANDLER_BEFORE, HANDLER_FUNCTION(OnButtonControl2))
        );
    }

    // テキストコントロールの作成
    new SFRBrewTextControl(this, SFXRectangle(10, 40, 100, 28), "text");

    // セレクトキーで入力モードに移るようにする
    if (static_try()) {
        static_throw(
            RegisterHandler(
                SFEVT_KEY, AVK_SELECT, HANDLER_BEFORE, HANDLER_FUNCTION(SelectHandler)));
    }
    return;
}

// デストラクタ
MyWindow2::~MyWindow2(Void)
{
    return;
}

// 描画ハンドラ
HANDLER_IMPLEMENT_VOIDRENDER(MyWindow2, OnRenderContent, graphics)
{
    // 背景を塗りつぶす
    graphics->FillRectangle(GetContentWorld(), SFXRGBColor(0xFF, 0xCC, 0xFF, 0x00));

    // MyWindow1 の検索
    SFRWindowPtr window = static_cast<SFRWindowPtr>(
        GetNext(TYPE_WINDOW, ATTRIBUTE_MYWINDOW1));

    if (window != null) { // 見つかったなら
        SFRCheckboxControlPtr checkbox;
        // チェックボックスの検索
        // (チェックボックスは複数あるが、そのうち1番目のものを取得)
        checkbox = static_cast<SFRCheckboxControlPtr>(
            window->GetFront(TYPE_CONTROL, ATTRIBUTE_CHECKBOXCONTROL));
        if (checkbox != null) {
            if (checkbox->GetStatusCheck()) { // チェックボックスがチェックされているか
                graphics->DrawText("true", SFXRectangle(120, 10, 100, 30),
                    SFXRGBColor(0x00, 0x00, 0x00, 0x00));
            } else {
                graphics->DrawText("false", SFXRectangle(120, 10, 100, 30),
                    SFXRGBColor(0x00, 0x00, 0x00, 0x00));
            }

            // 次のチェックボックスを検索
            // (2番目のチェックボックス)
            checkbox = static_cast<SFRCheckboxControlPtr>(
                checkbox->GetNext(TYPE_CONTROL, ATTRIBUTE_CHECKBOXCONTROL));
            if (checkbox != null) { // 見つかったなら
            
                // チェックボックスがチェックされているか
                if (checkbox->GetStatusCheck()) {
                    graphics->DrawText("true", SFXRectangle(120, 45, 100, 30),
                        SFXRGBColor(0x00, 0x00, 0x00, 0x00));
                } else {
                    graphics->DrawText("false", SFXRectangle(120, 45, 100, 30),
                        SFXRGBColor(0x00, 0x00, 0x00, 0x00));
                }
            }
        }
    }
    
    return;
}

// キー ハンドラ
HANDLER_IMPLEMENT_BOOLEVENT(MyWindow2, OnKey, event)
{
    switch (event.GetP16()) {
        case AVK_CLR:
            // ウィンドウを閉じる
            return Invoke(SFXEvent(SREVT_RESPONDER_TERMINATE, SRP16_TERMINATE_INVOKE, true));
        case AVK_LEFT:
            FocusLeft();
            return true;
        case AVK_RIGHT:
            FocusRight();
            return true;
        case AVK_UP:
            FocusUp();
            return true;
        case AVK_DOWN:
            FocusDown();
            return true;
    }
    return false;
}

// ボタンハンドラ
HANDLER_IMPLEMENT_VOIDCONTROL(MyWindow2, OnButtonControl1, result, control)
{
    // MyWindow1 の検索
    SFRWindowPtr window = static_cast<SFRWindowPtr>(
        GetNext(TYPE_WINDOW, ATTRIBUTE_MYWINDOW1));

    if (window != null) { // 見つかったなら
        window->Select(); // MyWindow1 を最前面に
        window->SetStatusFocus(true); // MyWindow1 にフォーカスを当てる
    }
    return;
}

// ボタンハンドラ
HANDLER_IMPLEMENT_VOIDCONTROL(MyWindow2, OnButtonControl2, result, control)
{
    // MyWindow1 の検索
    SFRWindowPtr window = static_cast<SFRWindowPtr>(
        GetNext(TYPE_WINDOW, ATTRIBUTE_MYWINDOW1));

    if (window != null) { // 見つかったなら
        window->InvalidateContent(); // MyWindow1 を再描画
    }
    return;
}

このウィンドウを生成するためのコード

// HelloWorld のキー ハンドラ
HANDLER_IMPLEMENT_BOOLEVENT(HelloWorld, OnKey, event)
{
    switch (event.GetP16()) {
        ...
        
        // ↓追加
        case AVK_4:
            if (static_try()) {
                SFRWindowPtr window;
                if ((window = ::new MyWindow1()) != null) {
                    static_throw(*window);
                    if (static_try()) {
                        if ((window = ::new MyWindow2()) != null) {
                            static_throw(*window);
                            if (!static_try()) {
                                ::delete window;
                            }
                        } else {
                            static_throw(SFERR_NO_MEMORY);
                        }
                    } else {
                        ::delete window;
                    }
                } else {
                    static_throw(SFERR_NO_MEMORY);
                }
            }
            return true;
    }
}

MyWindow1 の中でMyWindow2 にアクセスするためには、

    // MyWindow2 の検索
    SFRWindowPtr window = static_cast<SFRWindowPtr>(
        GetNext(TYPE_WINDOW, ATTRIBUTE_MYWINDOW2));

とします。GetNext は自分(MyWindow1)と同じ階層にあるレスポンダを検索します。

テキストコントロールやチェックボックスなどのコントロールも同じように検索できます。

    // window は MyWindow2 を指しているとする
    
    SFRBrewTextControlPtr textcontrol;
    // MyWindow2 にあるテキストコントロールの検索
    textcontrol = static_cast<SFRBrewTextControlPtr>(
        window->GetFront(TYPE_CONTROL, ATTRIBUTE_BREWTEXTCONTROL));

GetFront はwindow の子階層にあるレスポンダを検索します。

ATTRIBUTE_BREWTEXTCONTROL はテキストコントロールにデフォルトで設定されているアトリビュートです(もちろんユーザがアトリビュートを設定することもできます)。

4.7.2. 同じ種類のウィンドウ

同じ種類のウィンドウを複数作ることもできます。その場合、個々のウィンドウを区別するための機能をウィンドウクラスに持たせる必要があります。

SFMTYPEDEFCLASS(MyNumberingWindow)
class MyNumberingWindow : public SFRTitleWindow {
    SFMSEALCOPY(MyNumberingWindow)
public:
    MyNumberingWindow(SInt32 number, SFXRectangleConstRef rectangle) static_throws;
    virtual ~MyNumberingWindow(Void);
    HANDLER_DECLARE_VOIDRENDER(MyNumberingWindow, OnRenderContent)
    HANDLER_DECLARE_BOOLEVENT(MyNumberingWindow, OnKey)
    HANDLER_DECLARE_VOIDCONTROL(MyNumberingWindow, OnButtonControl)
    SInt32 GetNumber() { return _number; } // ウィンドウの番号を返す
private:
    SInt32 _number; // ウィンドウの番号。番号で個々のウィンドウを区別する
};

// コンストラクタ
// コンストラクタの引数でウィンドウの番号を設定します。
// SFXAnsiString::Format はフォーマット文字列を返す(C言語のprintf と同じ書式)
MyNumberingWindow::MyNumberingWindow(SInt32 number, SFXRectangleConstRef rectangle) :
    SFRTitleWindow(SFRApplication::GetInstance(),
                   rectangle,
                   SFXAnsiString::Format("window%d", number)), 
    _number(number) static_throws
{
    // 描画ハンドラ
    if (static_try()) {
        static_throw(
            RegisterHandler(SREVT_RESPONDER_RENDER, SRP16_RENDER_CONTENT,
                HANDLER_BEFORE, HANDLER_FUNCTION(OnRenderContent)));
    }
    // キーハンドラ
    if (static_try()) {
        static_throw(
            RegisterHandler(SFEVT_KEY, HANDLER_AFTER, HANDLER_FUNCTION(OnKey)));
    }

    // ボタンの作成
    SFRButtonControlPtr button = new SFRButtonControl(this,
        SFXRectangle(10, 5, 30, 25), "1");

    // ボタンハンドラの登録
    if (static_try()) {
        static_throw(
            button->RegisterHandler(
                SREVT_CONTROL, HANDLER_BEFORE, HANDLER_FUNCTION(OnButtonControl))
        );
    }

    button = new SFRButtonControl(this, SFXRectangle(10, 35, 30, 25), "2");

    if (static_try()) {
        static_throw(
            button->RegisterHandler(
                SREVT_CONTROL, HANDLER_BEFORE, HANDLER_FUNCTION(OnButtonControl))
        );
    }

    button = new SFRButtonControl(this, SFXRectangle(10, 65, 30, 25), "3");

    if (static_try()) {
        static_throw(
            button->RegisterHandler(
                SREVT_CONTROL, HANDLER_BEFORE, HANDLER_FUNCTION(OnButtonControl))
        );
    }

    // ラジオボタン
    SFRRadiobuttonControlPtr radiobutton1 = new SFRRadiobuttonControl(
        this, SFXRectangle(60, 10, 90, 20), "相対");
    SFRRadiobuttonControlPtr radiobutton2 = new SFRRadiobuttonControl(
        this, SFXRectangle(60, 40, 90, 20), "絶対");
    // ラジオボタンをグループ化(一方を選択すると、他方の選択が外れる)
    radiobutton2->Group(radiobutton1);
    // radiobutton1 を選択状態にする
    radiobutton1->SetStatusCheck(true);

    return;
}

// デストラクタ
MyNumberingWindow::~MyNumberingWindow(Void)
{
    return;
}

// 描画ハンドラ
HANDLER_IMPLEMENT_VOIDRENDER(MyNumberingWindow, OnRenderContent, graphics)
{
    // 背景を塗りつぶす
    graphics->FillRectangle(GetContentWorld(), SFXRGBColor(0xCC, 0xFF, 0xCC, 0x00));
    return;
}

// キー ハンドラ
HANDLER_IMPLEMENT_BOOLEVENT(MyNumberingWindow, OnKey, event)
{
    switch (event.GetP16()) {
        case AVK_CLR:
            // ウィンドウを閉じる
            return Invoke(SFXEvent(
                SREVT_RESPONDER_TERMINATE, SRP16_TERMINATE_INVOKE, true));
        case AVK_LEFT:
            // フォーカスの移動
            FocusLeft();
            return true;
        case AVK_RIGHT:
            FocusRight();
            return true;
        case AVK_UP:
            FocusUp();
            return true;
        case AVK_DOWN:
            FocusDown();
            return true;
        case AVK_2:
            // ウィンドウの移動
            Move(SFXSize(0, -10));
            return true;
        case AVK_4:
            Move(SFXSize(-10, 0));
            return true;
        case AVK_6:
            Move(SFXSize(10, 0));
            return true;
        case AVK_8:
            Move(SFXSize(0, 10));
            return true;
    }
    return false;
}

// ボタンハンドラ
HANDLER_IMPLEMENT_VOIDCONTROL(MyNumberingWindow, OnButtonControl, result, control)
{
    // フォーカスがあたっているレスポンダ(ボタン)が
    // 前から何番目にあるかを返す(0から始まる)。
    SInt32 index = GetFocus()->GetIndexForward();
    MyNumberingWindowPtr window = this;

    // 1つめのラジオボタンを検索
    SFRRadiobuttonControlPtr radiobutton = static_cast<SFRRadiobuttonControlPtr>(
        GetFront(TYPE_CONTROL, ATTRIBUTE_RADIOBUTTONCONTROL));

    // ラジオボタンが見つかり、かつチェックが入っているか
    if (radiobutton != null && radiobutton->GetStatusCheck()) {
        // index + 1 個後ろのウィンドウを最前面にする
        SInt32 i;
        for (i = 0 ; i < index + 1 ; ++i) {
            // window の次(後ろ)のウィンドウを検索
            window = static_cast<MyNumberingWindowPtr>(
                window->GetNext(TYPE_WINDOW, ATTRIBUTE_TITLEWINDOW));
            if (window == null) { // 見つからなかったら
                return; // 中止
            }
        }
        window->Select(); // ウィンドウを最前面に
        window->SetStatusFocus(true); // フォーカスを当てる
    } else {
        // index + 1 番目のウィンドウを最前面にする
        while (window != null) {
            // ウィンドウの番号が index + 1 であるか
            if (window->GetNumber() == index + 1) {
                window->Select(); // ウィンドウを最前面に
                window->SetStatusFocus(true); // フォーカスを当てる
                break;
            }
            // window の次(後ろ)のウィンドウを検索
            window = dynamic_cast<MyNumberingWindowPtr>(window->GetNext());
            if (window == this) {
                break; // 検索終了
            }
        }
    }
}

このウィンドウを4つ生成するためのコード

// キー ハンドラ
HANDLER_IMPLEMENT_BOOLEVENT(HelloWorld, OnKey, event)
{
    switch (event.GetP16()) {
        ...
        
        // ↓追加
        case AVK_5:
            if (static_try()) {
                if ((window = ::new MyNumberingWindow(1,
                    SFXRectangle(10, 10, 150, 130))) != null) {
                    static_throw(*window);
                    if (static_try()) {
                        if ((window = ::new MyNumberingWindow(2,
                            SFXRectangle(40, 40, 150, 130))) != null) {
                            static_throw(*window);
                            if (static_try()) {
                                if ((window = ::new MyNumberingWindow(3,
                                    SFXRectangle(70, 70, 150, 130))) != null) {
                                    static_throw(*window);
                                    if (static_try()) {
                                        if ((window = ::new MyNumberingWindow(4,
                                            SFXRectangle(100, 100, 150, 130))) != null) {
                                            static_throw(*window);
                                            if (!static_try()) {
                                                ::delete window;
                                            }
                                        }
                                        else {
                                            static_throw(SFERR_NO_MEMORY);
                                        }
                                    }
                                    else {
                                        ::delete window;
                                    }
                                }
                                else {
                                    static_throw(SFERR_NO_MEMORY);
                                }
                            }
                            else {
                                ::delete window;
                            }
                        }
                        else {
                            static_throw(SFERR_NO_MEMORY);
                        }
                    }
                    else {
                        ::delete window;
                    }
                }
                else {
                    static_throw(SFERR_NO_MEMORY);
                }
            }
            return true;
    }
}