SophiaFramework UNIVERSE 5.3 |
ウィンドウは、コントロールやコンテナを配置するためのレスポンダです。 1 つのアプリケーションクラスに同時に複数のウィンドウを持たせて重ねて表示できます。
下図のカスタムウィンドウ MyWindow を定義します。
SFZWindow クラスを継承させてカスタムウィンドウ MyWindow を定義します。
例 3.10. MyWindow の定義
// MyWindow の定義 SFMTYPEDEFRESPONDER(MyWindow) // 便利な型を生成するマクロ class MyWindow : public SFZWindow // 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') }; public: // スマートポインタで管理される MyWindow インスタンスを生成するための関数 static MyWindowSmp NewInstance(SFCErrorPtr exception = null); protected: explicit MyWindow(Void) static_throws; virtual ~MyWindow(Void); // 描画ハンドラ: 最初に呼び出される SFYWidget クラス登録されたデフォルトの描画ハンドラから呼び出される // MyWindow 自身の描画処理は HandleRenderRequest 仮想関数をオーバーライドして実装する virtual Void HandleRenderRequest(SFXGraphicsPtr graphics) const; private: // キーハンドラ XANDLER_DECLARE_BOOLEVENT(OnKey) };
SFMTYPEDEFRESPONDER マクロ | |
---|---|
SFMTYPEDEFRESPONDER は、 引数に指定したレスポンダクラスに関する便利なユーザー定義型を自動生成するマクロです。 例えば、引数に指定されたレスポンダのスマートポインタの型 MyWindowSmp はこのマクロで定義されます。 |
SFMRESPONDERINSTANTIATE マクロ | |
---|---|
SFMRESPONDERINSTANTIATE は、 RealView Compilation Tools for BREW 1.2 コンパイラの不具合を回避するためのマクロです。 RealView Compilation Tools for BREW 1.2 以外のコンパイラでは無視されます。 新たにレスポンダクラスを定義する場合、 SFMRESPONDERINSTANTIATE マクロを利用して SFYResponder から新たに定義するクラスまでの継承順序を記述する必要があります。 MyWindow は SFZWindow、SFZWindow は SFYContainer、 SFYContainer は SFYWidget、SFYWidget は SFYResponder というように SFYResponder から MyWindow まで 4 階層に渡って継承するので SFMRESPONDERINSTANTIATEFOUR マクロを使います。 SFYResponder からの継承が 3 階層の場合は SFMRESPONDERINSTANTIATETHREE マクロ、 5 階層の場合は SFMRESPONDERINSTANTIATEFIVE マクロを使います。 7 階層継承する場合の SFMRESPONDERINSTANTIATESEVEN マクロまで用意されています。 |
NewInstance 関数 | |
---|---|
レスポンダのインスタンスは new 演算子ではなく、 NewInstance 関数を使用して生成します。 NewInstance 関数で生成されたレスポンダのインスタンスは、 スマートポインタ(SFXResponderPointer)で管理されます。 具象レスポンダクラスの NewInstance 関数は、 SFYResponder::Factory 関数を利用して実装します。 詳細は、MyWindow の実装を参照してください。 |
レスポンダの描画 | |
---|---|
描画イベントを受信すると、 レスポンダは最初に起動される SFYWidget クラスに登録されたデフォルトの描画ハンドラ内から SFYWidget::HandleRenderRequest 仮想関数を呼び出します。 このとき、レスポンダクラスで SFYWidget::HandleRenderRequest 仮想関数をオーバーライドしていれば、 この関数が呼び出されます。 別途描画ハンドラを定義して登録することも可能ですが、 通常、レスポンダの描画は SFYWidget::HandleRenderRequest 関数をオーバーライドして記述します。 このとき、以下の点に注意してください。
|
レスポンダのタイプ | |
---|---|
レスポンダのタイプは SFYResponder クラスを継承する新しいレスポンダクラスを定義するときに、 four_char_code マクロ関数を使用して「 4 文字リテラル」として設定します。 小文字アルファベットまたは記号からなる 4 文字リテラルは SophiaFramework UNIVERSE で予約されています。 アプリ開発用には、大文字アルファベット 4 文字からなる「 4 文字リテラル」を利用してください。 |
helloworld アプリケーションクラスのメンバとして MyWindow のスマートポインタと MyWindow を作成する関数を追加します。
例 3.11. helloworld アプリケーションクラスの定義
// helloworld アプリケーションクラスの定義 SFMTYPEDEFCLASS(helloworld) // 便利な型を生成するマクロ class helloworld : public SFYApplication { SFMSEALCOPY(helloworld) // インスタンスのコピーを禁止するマクロ private: // *** 太字が追加部分 MyWindowSmp _myWindow; // MyWindow のスマートポインタ public: static SFCInvokerPtr Factory(Void); private: explicit helloworld(Void) static_throws; virtual ~helloworld(Void); SFCError Make(Void); // MyWindow を作成する関数 XANDLER_DECLARE_VOIDRENDER(OnRenderRequest) // 描画ハンドラ XANDLER_DECLARE_BOOLEVENT(OnKey) // キーハンドラ };
スマートポインタ | |
---|---|
レスポンダは NewInstance 関数を使用して生成し、 クラス名の末尾が Smp であるスマートポインタのインスタンスとして処理します。 スマートポインタの型はクラス定義文の直前で宣言する SFMTYPEDEFRESPONDER マクロの内部で定義されます。 たとえば、MyWindowSmp 型は SFMTYPEDEFRESPONDER(MyWindow) の内部で定義されます。 |
スマートポインタで管理される MyWindow インスタンスを生成する NewInstance 関数と、 MyWindow のコンストラクタ、デストラクタ、描画ハンドラ、キーハンドラを実装します。
例 3.12. MyWindow の実装
// NewInstance 関数: MyWindow インスタンスを生成する MyWindowSmp MyWindow::NewInstance(SFCErrorPtr exception) { // Factory 関数を使用して MyWindow インスタンスを生成する // Factory 関数は SFYResponderSmp 型を返すので // static_pointer_cast 演算子を利用して MyWindowSmp 型にダウンキャストする return static_pointer_cast<MyWindow>(Factory(:: new MyWindow, exception)); } // 配色定義 #define COLOR_MY_WINDOW_BACK (SFXRGBColor(0xCC, 0xFF, 0xCC, 0x00)) // 薄緑色: MyWindow の背景色 // コンストラクタ MyWindow::MyWindow(Void) static_throws { if (static_try()) { // レスポンダのタイプを設定する // four_char_code('M', 'W', 'N', 'D')がセットされる SetType(CODE_TYPE); // キーハンドラを登録する 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); } } // デストラクタ MyWindow::~MyWindow(Void) { } // 配色定義 #define COLOR_BLACK (SFXRGBColor(0x00, 0x00, 0x00, 0x00)) // 黒色: 文字列の色 // MyWindow の描画ハンドラの実装 Void MyWindow::HandleRenderRequest(SFXGraphicsPtr graphics) const { // 描画を行うための SFXGraphics インスタンスは第 1 引数(graphics)として渡される // MyWindow のローカル領域の左上が描画領域の原点となる // MyWindow のローカル領域はコンストラクタ内で SFYWidget::SetBackgroundColor() を使用して // 設定した薄緑色で塗り潰される // MyWindow のローカル領域中央に文字列 "Hello Window" を黒色で描画する // ローカル領域は GetLocalBound()で取得する graphics->DrawSingleText("Hello Window", GetLocalBound(), COLOR_BLACK); // SFYResponder クラスを継承するレスポンダの描画ハンドラでは、下記の実行文は不要 // graphics->Update(); return; } // MyWindow のキーハンドラの実装 XANDLER_IMPLEMENT_BOOLEVENT(MyWindow, OnKey, invoker, event) { // invoker: キーハンドラを呼び出したレスポンダ MyWindow (SFYResponderPtr 型) // event: キーイベント (SFXEventConstRef 型) switch (event.GetP16()) { case AVK_CLR: // クリアキーの SFEVT_KEY イベントを受信したとき // MyWindow を閉じる invoker->Terminate(); // キーイベントを処理したので true を返す return true; case AVK_1: // 1 キーの SFEVT_KEY イベントを受信したとき // "1-key" をシミュレータのデバッグウィンドウに表示する TRACE("1-key"); // キーイベントを処理したので true を返す return true; } // キーイベントを処理していないので false を返す return false; }
static_pointer_cast 演算子 | |
---|---|
SFYResponder::Factory 関数は SFYResponderSmp 型を返すので、 static_pointer_cast 演算子を利用して MyWindowSmp 型にダウンキャストします。 |
SFYWidget::SetBackgroundColor 関数 | |
---|---|
SFYWidget クラスを継承するレスポンダでは、 ローカル領域の背景は SFYWidget::HandleRenderRequest 関数が実行される前に SFYWidget::SetBackgroundColor 関数で設定された色(デフォルトは白色)で塗り潰されます。 MyWindow のコンストラクタ内でこの関数を使用して背景色を薄緑色に設定しています。 |
HandleRenderRequest 関数 | |
---|---|
レスポンダは描画イベントを受信すると、 最初に実行される SFYWidget クラスに登録されたデフォルトの描画ハンドラ内から SFYWidget::HandleRenderRequest 関数を呼び出します。 このとき、レスポンダクラスで SFYWidget::HandleRenderRequest 仮想関数をオーバーライドしていれば、 この関数が呼び出されます。 別途描画ハンドラを定義して登録することも可能ですが、 通常、レスポンダの描画は SFYWidget::HandleRenderRequest 関数をオーバーライドして記述します。 |
描画イベント | |
---|---|
helloworld アプリの場合、 アプリ開始イベント(SFEVT_APP_START)を処理する SFYApplication::HandleEvent 関数の内部で SFYResponder::Render 関数を呼び出します。 このときに、レスポンダは描画イベントを受信し、 再描画を行います。 |
MyWindow の描画領域 | |
---|---|
MyWindow の描画は、graphics 引数(SFXGraphics 型)を使用して MyWindow のローカル領域に対して行います。 |
レスポンダを生成してから配置するまでの一連の処理は以下の通りです。
例 3.13. MyWindow の作成
// MyWindow を作成する関数 SFCError helloworld::Make(Void) { SFCError error(SFERR_NO_ERROR); // MyWindow インスタンスを生成する if ((_myWindow = MyWindow::NewInstance(&error)) != null) { // MyWindow の親レスポンダを helloworld アプリケーションクラスが保持するルートに設定する // ※SFYApplication::GetThis() により helloworld アプリケーションクラスが保持するルートを取得できる error = _myWindow->SetParent(GetThis()); if (error == SFERR_NO_ERROR) { // MyWindow の実領域をルートのローカル領域から(10, 10)だけ Deflate した領域に設定する // ※ルートのローカル領域はデフォルトでデバイス画面領域に初期設定されている _myWindow->SetRealBound(GetLocalBound().Deflate(10, 10)); // MyWindow の状態を「可視+活性+操作可能+フォーカス」にまとめて設定する _myWindow->SetState(true, true, true, true); // MyWindow を最前面に移動する _myWindow->ToFront(); } } return error; }
SFYApplication::GetThis 関数 | |
---|---|
SFYApplication::GetThis 関数は、 アプリケーションクラスがデフォルトで保持するルート(SFZRoot)を返します。 |
SFYApplication.GetLocalBound 関数 | |
---|---|
SFYApplication::GetLocalBound 関数は、 アプリケーションクラスがデフォルトで保持するルート(SFZRoot) のローカル領域(画面全体の矩形領域)を返します。 |
Deflate 関数 | |
---|---|
SFXRectangle(x, y, w, h)で表される矩形を SFXSize(a, b)だけ Deflate すると、 左上端の開始点座標が SFXGrid(x + a, y + b)、領域サイズが SFXSize(w - 2 * a, h - 2 * b)の矩形領域になります。 たとえば、 SFYApplication::GetLocalBound()で返されるアプリケーションクラス(ルート)のローカル領域(画面全体)が SFXRectangle(0, 0, 240, 320)であったとすると、 SFXSize(10, 10)だけ Deflate した後の領域は SFXRectangle(10, 10, 220, 300)となります。 |
デフォルトの仮想領域とローカル領域 | |
---|---|
実領域しか設定していないレスポンダの仮想領域は、 実領域と物理的に同じになります。 仮想領域はローカル領域と物理的に同じなので、 このときローカル領域も実領域と等しくなります。 MyWindow のローカル領域は、 左上端を原点とする相対座標系を使用して表現すると SFXRectangle(0, 0, 220, 300)になります。 ※レスポンダの描画はローカル領域に対して行います。 |
実領域 | |
---|---|
実領域とは、 親レスポンダのローカル領域の左上端を原点とする相対座標系を使用して表された、 レスポンダのクリッピング領域です。 仮想領域が設定されていない場合、 レスポンダのローカル領域は実領域と同じになります。 仮想領域が設定されている場合は、 ローカル領域に描画されたレスポンダの内容は、実領域でクリッピングされて画面に表示されます。 |
レスポンダ間の順序関係 | |
---|---|
レスポンダはレスポンダツリーと呼ばれる木構造で管理され、 画面の描画は木構造の順序関係に従って行われます。 レスポンダツリーの順序関係では、 子レスポンダは親レスポンダよりも前面、 姉レスポンダは妹レスポンダよりも前面にあります(子レスポンダは親レスポンダに設定した順に前面から配置されます)。 また、姉レスポンダの子レスポンダは妹レスポンダよりも前面にあります。 姉妹関係にあるレスポンダの 1 つを最前面に持ってくると、 その子レスポンダは自動的に他の姉妹レスポンダよりも前面に配置されます。 レスポンダを姉妹レスポンダのなかで最前面にする関数として SFYResponder::ToFront 関数が用意されています。 ウィンドウやダイアログに対して SFYResponder::ToFront 関数を呼び出すと、 その中に含まれるボタンコントロールやラベルコントロール、タブコントロールなどの子レスポンダもまとめて最前面に配置されます。 |
SFYResponder::ToFront / SFYResponder::ToBack 関数 | |
---|---|
SFYResponder::ToFront や SFYResponder::ToBack 関数はウィンドウやダイアログなどのレスポンダを姉妹レスポンダ間で最前面、最背面に配置するために使います。 |
注意 | |
---|---|
レスポンダツリーにレスポンダを追加したとき、 そのレスポンダは姉妹レスポンダの中で最背面に配置されます。 画面に表示するには SFYResponder::ToFront 関数を呼び出してレスポンダを最前面に移動させます。 |
初期画面で 1 キー(AVK_1)が押されると MyWindow を表示するように helloworld アプリケーションクラスのキーハンドラを変更します。
例 3.14. helloworld アプリケーションクラスのキーハンドラの変更
// helloworld アプリケーションクラスのキーハンドラの実装 XANDLER_IMPLEMENT_BOOLEVENT(helloworld, OnKey, invoker, event) { unused(invoker); // キーハンドラの処理 switch (event.GetP16()) { case AVK_SELECT: // セレクトキーの SFEVT_KEY イベントを受信したとき Terminate(); // helloworld アプリを終了する return true; // *** 太字が追加部分 case AVK_1: // 1 キーの SFEVT_KEY イベントを受信したとき // MyWindow を作成する if (Make() != SFERR_NO_ERROR) { return false; } // キーイベントを処理したので true を返す return true; } // キーイベントを処理しなかったときは false を返す return false; }
ウィンドウやメニュー、ダイアログを閉じる(終了する)には、 SFYResponder::Terminate 関数を呼び出します。
例 3.15. MyWindow のキーハンドラの実装
// MyWindow のキーハンドラの実装 XANDLER_IMPLEMENT_BOOLEVENT(MyWindow, OnKey, invoker, event) { switch (event.GetP16()) { case AVK_CLR: // クリアキーの SFEVT_KEY イベントを受信したとき // MyWindow を閉じる(終了する) // invoker: MyWindow のポインタ (SFYResponderPtr 型) invoker->Terminate(); // この段階では MyWindow はヒープメモリ上にまだ残っている // MyWindow は参照カウントが 0 になったときにヒープメモリから解放される // helloworld アプリケーションクラスの _myWindow によって参照されているので // MyWindow は helloworld アプリが終了するときにヒープメモリから解放される return true; case AVK_1: // ...(省略)... }
Terminate 関数 | |
---|---|
レスポンダの終了処理を行うSFYResponder::Terminate 関数を呼び出すと、 レスポンダはレスポンダツリーから切り離され、 子レスポンダとの親子関係も解消されます。 通常、SFYResponder::Terminate 関数は、 スマートポインタで管理されるレスポンダの参照カウントが 0 になったタイミングで自動的に呼び出されるので、 ほとんどのレスポンダでこの関数を呼び出す必要はありません。 ダイアログやメニューなどユーザーの選択アクションに応じて直ちに閉じる(終了する)必要のあるレスポンダでは、 明示的にこの関数を呼び出してレスポンダの終了処理をその場で行います。 ただし、閉じたレスポンダがスマートポインタの変数によって参照されていると、 この関数を実行した後もヒープメモリ上にそのまま残ります。 強制的にヒープメモリから解放するには、 レスポンダを参照しているスマートポインタの変数に別のレスポンダを代入する、 あるいは、明示的に SFXResponderPointer::Release 関数を呼び出すなどして参照カウントを 0 にする必要があります。 |
Tip | |
---|---|
SFCApplication::Terminate 関数はアプリの終了処理を行う関数です。 |
delete 演算子 | |
---|---|
レスポンダツリー上のレスポンダに対して delete 演算子を実行してはいけません。 delete 演算子を実行した後もレスポンダツリー上にレスポンダは残ったままなので、 delete 演算子を実行すれば不整合が生じます。 レスポンダを明示的に閉じる(終了する)には、 SFYResponder::Terminate 関数を呼び出します。 |
参照: SFYResponder::Terminate | SFCApplication::Terminate | SFXResponderPointer::Release | レスポンダツリー
デバッグウィンドウの表示方法 | |
---|---|
デバッグウィンドウを表示するには、 BREW シミュレータのメニューバーから [表示]→[出力ウィンドウ] を選択します。 |
参照: ウィンドウ(基礎編)
Copyright(c) 2002 - 2024 Sophia Cradle Incorporated All Rights Reserved. |