これまでは、上下が二重線になっているボックスで示される、ノーマルエクスターナルオブジェクトの書き方についてだけを説明してきました。
ここでは、パッチャーウィンドウ内で、カスタムメイドの外観と動作を持つエクスターナルオブジェクトを書くための秘訣について学んでいきます。
ノーマルオブジェクトは、パッチャーウィンドウでパレットから選択されるオブジェクトのうちの1つに過ぎません。他にも、
ボタン(button)
スライダ(slider)
ナンバーボックス(number boxes)
などがあります。
これらのオブジェクトは、ノーマルオブジェクトと同じようにインレット、アウトレットを持っています。しかし、より親切なインターフェイスを持っています。書き始める前に、そのタスクにとって、ウィンドウを持ったノーマルオブジェクトより、カスタムユーザインターフェイスオブジェクトの方がより適しているという点を確認して下さい。あなたのユーザインターフェイスオブジェクトは、ユーザがパッチャーウィンドウ内で行えることを、より強化するようなものでなければなりません。
この章では、Max のためのユーザインターフェイスオブジェクトの書き方について説明しています。このAPI は Macintosh の QuickDraw 環境から 始まりました。Windows XP では、ネイティブな WIN32 による描画、もしくは、QuickDraw のエミュレーションを含む、アップルの QuickTime SDK for Windows を利用することができます。この章の終わりの方には、Max 環境で QTML を使う上での重要な情報について書かれた、XQT and QTML for Windows UI Externals というタイトルのセクションがありますので、参照して下さい。
パッチャーウィンドウは t_box 構造体のコレクションによって作られています。
t_box 構造体には、ユーザインターフェイスオブジェクトを取り囲む長方形領域と、オブジェクトのアピアランスと動作を決定する多くのフラグが含まれています。ユーザインターフェイスオブジェクトの構造体を作る場合、Box はその中の最初に含まれなければなりません。これは、t_box へのポインタではなく、t_box 構造体それ自身です。この構造体は、通常のエクスターナルオブジェクト構造体定義の最初に置く t_object の代わりになります。 t_box および、他の有用なユーザインターフェスデータ型と定数は、Max のインクルードファイル ext_user.h で定義されています。下記のものは、ユーザインタフェースオブジェクト構造体定義の例です。この例は、これ以降の、ユーザインターフェイスオブジェクトを書くための助けとなる様々なルーチンの説明を通して使われています。
typedef struct myuserobject { t_box my_box; long my_data1; long my_data2; void *my_qelem; } t_myuserobject;
このテクニックを使うと、追加のフィールドを持っている場合でも、パッチャーウィンドウはあなたのユーザインターフェイスオブジェクトを t_box として処理することができるようになります。さらに、Max はこれを t_object として扱うことができます。
あなたのオブジェクトがユーザインターフェイスオブジェクトであることを、どのように Maxに告げるのでしょうか?あなたは、SICN リソースを、共有ライブラリのファイルに含めます。コードウォーリアでは、リソースファイルは、ファイルタイプが rsrc であれば、プロジェクトに追加することができます。MPW 環境では、リソースファイル(rsrcタイプ)が Derez を通されて、テキストベースのリソースファイル(.r)が作られ、それがRez によってリビルドされます。
SICN リソースはオブジェクトのアピアランスのアイコンでなければなりません。デザインを行う際には、アイコンのサイズやスタイルは既存の Max オブジェクトのアイコンからヒントを得るようにして下さい(Max アプリケーションファイルの中の SICN リソースを ResEdit で見てみて下さい)。SICN には、関連づけられる mAxL リソースとして、同じリソースID を与えて下さい。
加えて、SICN アイコンリソースには、“Horizontal Slicer(水平スライダー)”のように、オブジェクトの簡単な説明になるような名前をつけなければなりません。この説明は、ユーザがパッチャーのパレットであなたのSICN をクリックした時に、パッチャーのアシスタンスの部分に表示されます。
Windows XP 上では、このリソースを作成し、QuickTime SDK for Windows tools にある reswack を使ってエクスターナルオブジェクトの DLL に追加する必要があります。MAX SDK の将来のバージョンでは、アイコンを、Windows の ICON リソースを使って指定することが可能になるでしょう。
初期化の際に呼び出される setup 関数の 引数 menufun を覚えているでしょうか? menufun は、ユーザがパッチャーウィンドウのパレットからユーザインターフェイスオブジェクトを選択した場合に呼び出される関数です。これは、「デフォルト」 のサイズとアピアランスによるオブジェクトを生成し、新しく生成されたオブジェクトへのポインタを返します。次のように宣言されます。
void *myuserobject_menu (t_patcher *p, long left, long top, long font); | ||
p | オブジェクトが含まれるパッチャー | |
left | オブジェクトが生成される位置の左端の座標. | |
top | オブジェクトが生成される位置の上端の座標. | |
font |
下位16ビットは、ウィンドウのデフォルトのフォントサイズ、上位16ビットはカレントのデフォルトフォントインデックスを表します。オブジェクトがテキストを表示する場合にのみ、この情報を使う必要があります。 |
ユーザインターフェイスオブジェクトは2つのインスタンス生成ルーチンを作ります。1つは上のように宣言され、デフォルトのオブジェクトを作るために呼び出されます。そして、もう1つは、パッチャーファイルが読み込まれた際、オブジェクトのインスタンスが生成されるときに呼び出されます。正確な2番目のインスタンス生成ルーチンのフォーマットは場合によって、多少、異なる部分があります。これは、オブジェクトが psave メッセージに応答して、パッチャー内でのその状態と位置を書き出すためです。psave メソッドについて説明する前に、 ファイルベースのインスタンス生成ルーチンがどのように宣言されるかを見てみましょう。
void *myuserobject_new (t_symbol *sym, short argc, t_atom *argv); | ||
sym | オブジェクトの名前 | |
argc | argv の t_atoms の数 . | |
argv | あなたのオブジェクトを記述する t_atoms の 配列 (座標、その他) |
配列 argv の最初の t_atom はオブジェクトが存在するパッチャーへのポインタです。すべてのユーザインターフェイスオブジェクトのヘッダとなる t_box 構造体を初期化するためにこの情報が必要になります。ユーザインターフェイスオブジェクトのインスタンスを作る場合に、オブジェクトが最初に行わなければならないことは、次のようなものです。
void *myuserobject_new (t_symbol *sym, short argc, t_atom *argv) { t_myuserobject *x; void *p; // あなたのパッチャー short left, top, right, bottom; x = newobject(myuserobject_class); p = argv->a_w.w_obj; // パッチャーを argv から取り出す
パッチャーを取得したら、あなたのルーチンに渡された他の引数に注目しなければなりません。特に、その次に続くいくつかの引数で、パッチャー内でのオブジェクトの長方形領域の座標を指定することを推奨します。後で、psave メソッドの中でこれを行う方法について説明しますが、ここで作るオブジェクトはその座標が 左、上、右、下、という順序で保存されていると仮定しておきましょう。
myuserobject_new の続きですが、ここでは 配列 argv から座標を取り出しています。
left = argv[1]->a_w.w_long; top = argv[2]->a_w.w_long; right = argv[3]->a_w.w_long; bottom = argv[4]->a_w.w_long;
これで、box_new について学ぶための準備が整いました。box_new は、ユーザインターフェイスオブジェクトが持つ t_box を初期化するものです。
box_new |
||||||||||||||||||||||||||||||||||||||||||||||||||||||
Box の初期化を行うために、 box_new を使います。 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
void box_new (t_box *b, t_patcher *p, short flags, short left, short top, short right, short bottom); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
b | ユーザインターフェイスオブジェクトのヘッダである t_box 構造体。あなたのオブジェクトへのポインタをここで渡します。 | |||||||||||||||||||||||||||||||||||||||||||||||||||||
p | あなたの生成関数に渡されるパッチャーのアーギュメント | |||||||||||||||||||||||||||||||||||||||||||||||||||||
flags | box のアピアランスと動作をコントロールするための定数の組み合わせ。下記を参照。 | |||||||||||||||||||||||||||||||||||||||||||||||||||||
left | box の左座標;あなたの生成関数に渡される左端座標になります。 | |||||||||||||||||||||||||||||||||||||||||||||||||||||
top | box の上座標:あなたの生成関数に渡される上端座標になります。 | |||||||||||||||||||||||||||||||||||||||||||||||||||||
right | box の右座標:左座標を基準として、生成関数に幅が渡されます。 | |||||||||||||||||||||||||||||||||||||||||||||||||||||
bottom | box の下座標:上座標を基準として、生成関数に高さが渡されます。 | |||||||||||||||||||||||||||||||||||||||||||||||||||||
box_new は t_box のためのメモリ割り当ては行いません。代わりに、あなたのオブジェクトの中にすでに存在する t_box を初期化します。引数 flags は次のような定数の組み合わせになります。
最も普通に使用されるフラグは、F_DRAWFIRSTIN、および F_GROWY か F_GROWBOTH です。次のものは、今までの例の続きです。t_myuserobject が box_new を呼び出しています。 box_new((t_box *)x, p, F_DRAWFIRSTIN | F_SAVVY, left, top, right, bottom); Box の初期化を行った後、オブジェクトの他のフィールドの初期化を考えるでしょう。割り込みレベルを考慮した場合、初期化は行っておかなければなりません。ユーザからオブジェクトに送られるメッセージ(int や bang など)に応答して、オブジェクトの状態の変化を再描画するためにQelem を使う場合が、この「割り込みレベルの考慮」にあたります。オブジェクトはメッセージを受け取ってから直ちに描画を行うことはできないため、多くの場合、オブジェクトの論理的な値と直前に描画された値の両方の情報を把握していることが助けになります。このようにして、オブジェクトが新しい値を反映して更新される必要がある場合を識別します。 ユーザインターフェイスオブジェクトの生成ルーチンでの次のステップは、アウトレットの作成、あるいは、必要ならばインレットを追加することです。次に、Box の b_firstin フィールドにオブジェクト自身へのポインタを割り当てなければなりません。次の例では、Qelem をセットし、必要な割り当てを実行しています。後ほど、関数 myobject_redraw については簡単に説明します。 x->my_qelem = qelem_new(x, (method)myobject_redraw); x->my_box.b_firstin = (void *)x; これで、パッチャーウィンドウは新しく初期化されたオブジェクトの描き方を知っていることになるため、次に box_ready を呼び出したいと思います。 |
box_ready |
||
あなたのオブジェクトの描画を準備するために、box_ready を使います。 | ||
void box_ready (t_box *b); | ||
b | あなたのユーザインターフェイスオブジェクトのヘッダとなる t_box 構造体。あなたのオブジェクトへのポインタをここへ渡すだけです。 | |
box_ready は、あなたの新しい Box のインレットとアウトレットを描画する場所を計算します。そして、新しいオブジェクトが(ファイルから読み込まれるというよりは)ユーザによって作られると、box_ready はパッチャーウィンドウ上でそれを目に見える形で選択された状態にします。これは、ユーザが最初に Max メニューから、Get info... を選択して、付加的なパラメータ(例えばスライダー型のオブジェクトのレンジのようなもの)の設定を行いたい場合に便利です。 box_ready を呼び出した後、ユーザインターフェイスオブジェクトの生成ルーチンは、新しく作られたインスタンスへのポインタを返さなければなりません。 box_ready((t_box *)x); return x; ユーザインターフェイスオブジェクトの生成ルーチンの基本的なステップについて説明してきました。ここでもう一度、オブジェクトのデフォルトのインスタンスが作られたときにパッチャーによって呼び出される最初の生成ルーチンに戻ってみましょう。このルーチンは、デフォルトの値を持つ適切な配列 argv をセットし、私たちが書いた通りの生成関数を呼び出すので、私たちはこのルーチンを実装することができます。これは、私たちのコードで重複が起きるのを防ぎます。 void *myuserobject_menu (t_patcher *p, long left,long top, long font) { t_atom argv[20]; // myuserobject_new が必要とする argv のセットアップ // 最初に、パッチャーに関する値 argv[0].a_type = A_OBJ; argv[0].a_w.w_obj = (t_object *)p; // 座標に関する値(フォント情報は使用されません) argv[1].a_type = argv[2].a_type = argv[3].a_type = argv[4].a_type = A_LONG; argv[1].a_w.w_long = left; argv[2].a_w.w_long = top; argv[3].a_w.w_long = right; argv[4].a_w.w_long = bottom; return myuserobject_new(0, 5, argv); } |
ユーザインターフェイスオブジェクトはその 消滅関数(free 関数) を定義し、box_free を呼び出すことによって、Box に関連するすべてのデータを解放しなければなりません。Box で freeobject を呼び出してはいけません。freeobject の呼出しを行うと、無限の再帰によるループが生じてしまいます。その理由は、ユーザインターフェイスオブジェクトの消滅関数は freeobject の中で呼び出されますが、それ自身が同じポインタで freeobject を呼出し、それによって、再びあなたの開放関数が呼び出され、そしてそれが・・・という形で無限に続いてしまうからです。一方、ユーザインターフェイスオブジェクトでないオブジェクトは、決して box_free を呼び出すことはありません。
box_free 関数を呼び出した後、消滅関数では、ノーマルオブジェクトの開放関数の場合と同様、必ず他のメモリを破棄、またはクリーンアップしておかなければなりません。
box_free |
||
Box によって使用されるデータ構造体を解放するために、box_free を使います。 | ||
void box_free (t_box *b) | ||
b | ユーザインターフェイスオブジェクトのヘッダとなる t_box 構造体。あなたのオブジェクトへのポインタをここへ渡すだけです | |
この関数は、パッチコードのような、box に結びつけられているデータ構造体を解放します。box が可視である場合、パッチャーウィンドウ上の box があった領域を再描画します。 |
ユーザインターフェイスオブジェクトには、実装しなければならない3つのメッセージ、click、update、psave、があります。初期化の際には、addmess を使ってこれらのメッセージに応答するメソッドを追加します。オブションとして追加できる2つのメッセージ、key、bfont、は、テキストや数値を表示するオブジェクトの場合に役立ちます。
click |
||
click メッセージはユーザがオブジェクトの上でマウスをクリックした時に送られます。 | ||
バインディング |
||
addmess (myobject_click, "click", A_CANT, 0); | ||
宣言 |
||
void myobject_click (myObject *x, Point pt, short modifiers); | ||
pt | マウスがクリックされた場所 (ローカル座標で表されます) |
|
modifiers | このマウスアップイベントのためにMaxOS のGetNextEvent によって返されるイベントレコードの modifiers フィールド。shift、option、command、capslock、control キーが押されていたかどうかを示します。 | |
パッチャーウィンドウがあなたの Box の長方形領域内でクリックを検知した時にこのメッセージが送られます。適切な方法でマウスを追跡し、これに応答しなければなりません。pt はローカル座標で表されるマウス位置、modifires は標準の Macintosh イベントレコードの modifiers フィールドです。マウスボタンが上げられるのを待つループを実装するよりは、マウスのトラッキングを行うために wind_drag を使って下さい。描画やマウストラッキングの前にカレントグラフポートをセットするためには、patcher_setport を使います。 重要:click メソッドの中でアウトレットからメッセージを送る場合、送信の前に lockout フラグをセットし、終了後に復元しておかなければなりません。これは、アウトレットから0を送信する click ルーチンのサンプルです。 void myObject_click (t_myuserobject *x, Point pt, short modifiers) { short savelock; savelock = lockout_set(1); outlet_int (x->m_ob.o_outlet,0); lockout_set (savelock); } |
update |
||
オブジェクトが再描画される必要がある場合に、update メッセージが送られます。 | ||
バインディング |
||
addmess (myobject_update, "update", A_CANT, 0); | ||
宣言 |
||
void myobject_update (t_myuserobject *x); | ||
update メソッドは、 box の中でユーザインターフェイスオブジェクトを描画します。加えて、ユーザがオブジェクトの box 長方形領域を拡大、縮小できる場合、update メソッドで、オブジェクトのサイズ変更が行われたかどうかをチェックしなければなりません。例えば、スライダーオブジェクトならば、新しい幅に基づいた「スライダーハンドル」のビットマップを再構築したいと考えるでしょう。このような比較を行うために、オブジェクト内に「古い」Boxのサイズに関する状況を保存する必要があります。また、box_size 関数を使って、大き過ぎたり、小さ過ぎたりしないようにサイズ変更を行うことが可能です。 ハイライトが可能なユーザインターフェイスオブジェクトを作りたい場合には、update メソッドで t_box の b_hilited フィールドを見に行く必要があります。このフィールドが0以外ならばオブジェクトをハイライト状態で、そうでなければ、ハイライトしていない状態で描画しなければなりません。加えて、後述するような key メッセージに応答するメソッドを実装しなければなりません。これによって、ユーザはオブジェクト内でタイプ入力できるようになります。ナンバーボックスはハイライト可能なユーザインターフェイスオブジェクトの例です。 update メソッド以外のルーチンで描画を行う計画がある場合には、patcher_setport および box_nodraw というルーチンについての情報を参照して下さい。これについては後述します。また、ユーザインターフェイスオブジェクトでインデックスカラーをサポートしようとする場合には、「ユーザインターフェイスオブジェクトと色」というセクションを参照して、color および wcolor というメッセージに関する情報を得る必要があるでしょう。。 |
psave |
||
パッチャーがあなたのオブジェクトを保存しようとする際に、psave メッセージが送られます。これは、オブジェクトがクリップボードにコピーされた場合、またはファイルに保存された場合です。 | ||
バインディング |
||
addmess (myobject_psave, "psave", A_CANT, 0); | ||
宣言 |
||
void myobject_psave (t_myuserobject *x, Binbuf *dest); | ||
dest | オブジェクトを保存するためにメッセージを書き出さなければならない Binbuf | |
ユーザインターフェイスオブジェクトを書く場合に行わなければならないことの中で、psave メソッドを書くことはもっとも不可解なことでしょう。Max ファイルが開かれる際にオブジェクトを再現することができるようにするメッセージを作成するためには、binbuf_vinsert(Chapter 7 で説明しています)または、binbuf_insert を使います。最初に、あなたのオブジェクトの t_box の b_hidden フィールドがチェックされて、隠された状態に設定されているかどうかを判定する必要があります。ユーザは、Max メニューの Hide on Lock を選択することで、box やパッチコードを隠すことができます。 その後、あなたのオブジェクトの内部データを下のサンプルのような方法で保存します。このサンプルから変更しなければならない点は、binbuf_vinsert に渡すフォーマット文字列に加え、追加したアーギュメントを保存することくらいです。また、あなたのオブジェクトのクラス名は知っておかなければなりません。下のサンプルの中で、私たちのオブジェクトは myuserobject という名前を持っています。ユーザインターフェイスオブジェクトを次のような構造体と共に保存し、オブジェクトの座標と共に my_range と my_value を保存することを仮定しています。 typedef struct myuserobject { t_box my_box; long my_range; long my_value; } t_myuserobject; これは、適切な psave メソッドです。 void myObject_psave(myObject *x, void *w) { short hidden; Rect *r; hidden = myObj.my_box.b_hidden; r = &myObj.my_box.b_rect; if (hidden) { binbuf_vinsert(w,"sssslllll", gensym("#P"),gensym("hidden"), gensym("user"),gensym("myobject"), (long)(r->left), (long)(r->top), (long)(r->right - r->left), (long)(r->bottom - r->top), (long)(x->my_range)); } else { binbuf_vinsert(w,"ssslllll", gensym("#P"),gensym("user"), gensym("myobject"), (long)(r->left),(long)(r->top), (long)(r->right - r->left), (long)(r->bottom - r->top), (long)(x->my_range)); } } まず最初に、前に定義した myuserobject_new 関数が使うことのできる形(最初に左、上、...という順です)で、座標を保存します。 いくつか説明しておく点があります。hidden は Max に b_hidden アトリビュートがセットされた Box を作るように命じるための特別なキーワードです。これは、Max ファイルが開かれた際、自動的に行われます。user はエクスターナルとして書かれたユーザインターフェイスオブジェクトが必要とする特別なキーワードです。user を含めるのを忘れてしまうと、オブジェクトは、次のようなエラーを引き起こすでしょう。 patcher: doesn’t understand ‘myobject’ これは、ファイルからロードされる際に起こります。 binbuf_vinsert への残りの引数は、オブジェクトボックスの座標、および保存したい他の情報です。 上のような psave メソッドを実行した結果を文字として見た場合、次のようになるでしょう。 #P user myobject 200 200 18 18 128; |
bfont |
||
bfont メッセージは、オブジェクトが選択され、ユーザが Font メニューから new font または font size を選んだ場合に送られます。 | ||
バインディング |
||
addmess (myobject_bfont, "bfont", A_CANT, 0); | ||
宣言 |
||
void myobject_bfont (myObject *x, short size, short font); | ||
size | ユーザに選ばれた新しいサイズ, または、フォントサイズが変更されない場合は -1 | |
font | ユーザに選ばれた新しいフォントの番号, または、フォントが変更されない場合は -1. | |
ユーザがインターフェイスオブジェクトを選択し、Font メニューから new font または font size を選んだ際に、オブジェクトの Box のサイズやアピアランスを変更したい場合、bfont メソッドを実装しなければなりません。このメソッドは、ユーザのこのような操作が実行された後に呼び出されます。IncDec や umenu は このメソッドが実装された Max ユーザインターフェイスオブジェクトの例です。 |
key |
||
key メッセージは、あなたのオブジェクトがハイライト表示され、ユーザがキーを押した際に送られます。 | ||
バインディング |
||
addmess (myobject_key, "key", A_CANT, 0); | ||
宣言 |
||
void myobject_key (myObject *x, short keyvalue); | ||
keyvalue | 押されたキーの ASCII 値 | |
box_new の 引数 flags で F_HILITE ビットをセットされたインターフェイスオブジェクトは、ユーザがオブジェクトをハイライト表示させ、Macintosh キーボードのキーを押した際に key メッセージを受け取ります。オブジェクトは、keyvalue によって渡されるキーのASCII値に応答して、それに適したようにオブジェクトの状態を変更し、オブジェクト自身を再描画しなければなりません。 |
enter |
||
enter メッセージは、ユーザがオブジェクトにタイプして表示させるための操作をし、これが完全に「エンターされた」場合に送られます。 | ||
バインディング |
||
addmess (myobject_enter, "enter", A_CANT, 0); | ||
宣言 |
||
void myobject_enter (myObject *x); | ||
このメッセージは、ユーザが [Return] または [Enter] キーを押し、他のボックスをハイライト表示させるか、パッチャーウィンドウ内の box の外でクリックした時に送られます。このメッセージへの応答では、表示が「ペンディング」を表す状態のもの(Number Box で見られる3つのドットのようなもの)を取り除き、タイプされた値を受け入れなければなりません。 |
clipregion |
||
clipregion メッセージは、パッチャーがあなたのオブジェクトが他のオブジェクトとの重なりをどのように処理すべきか知りたい場合に送られます。これを実装しない場合には、あなたのオブジェクトが自分自身の領域をすべて満たしていると仮定します。 | ||
バインディング |
||
addmess (myobject_clipregion, "clipregion", A_CANT, 0); | ||
宣言 |
||
f void myobject_clipregion (t_myobject *x, RgnHandle *rgn, short *result); | ||
このメッセージによって、あなたのオブジェクトが描画領域として、通常の長方形領域のサブセットである非長方形領域を定義することができます。例えば、umenu オブジェクトは角丸長方形によって現在選択されているアイテムを表示します。引数 result には次の4つの値のうちの1つを設定します。
次の例では、オブジェクトはclipregion メソッドの中で円形のクリッピング領域を定義しています。 void myobject_clipregion (t_myobject *x, RgnHandle *rgn,short *result) { *result = CLIPRGN_RGN; OpenRgn(); FrameOval(&x->my_box.b_rect); // box の長方形領域を使います。 CloseRgn(*rgn = NewRgn()); } clipregion メソッドに関するより詳しい情報は、後述の 透過オブジェクトに関するセクションを参照して下さい。 また、現在 Macintosh 用 でない UI オブジェクトの領域を指定する方法はありません。したがって、 CLIRGN_RGN モードは目下のところ Windows XP ではサポートされていない点にも注意して下さい。 |
bidle |
||
bidle メッセージは、ロックされたパッチャーウィンドウで、カーソルがあなたのオブジェクトの長方形領域にある場合に送られます。すべてのオブジェクトがカーソル位置の調整やカーソルの追跡を必要とするわけではないため、このメソッドはオプションです。 | ||
バインディング |
||
addmess (myobject_bidle, "bidle", A_CANT, 0); | ||
宣言 |
||
void myobject_bidle (t_myobject *x, Point pt, short modifiers, short active); | ||
pt | 現在のマウス位置。ローカル座標で表されます。 | |
modifiers | このキーイベントのためにMaxOS のGetNextEvent によって返されるイベントレコードの modifiers フィールド。shift,option,command,capslock,control キーが押されていたかどうかを示します。 | |
active | あなたのウィンドウがアクティブ(最前面に表示される)ウィンドウなら0、そうでない場合は0以外。 | |
bidle メッセージは、ロックされたパッチャーウィンドウでカーソルがあなたのオブジェクトの上にある場合に送られます。一般的にカーソルの追跡は、例えばMacintosh 上でマウス位置を探す場合ならば、Mac OS の関数 GetMouse を呼び出すでしょう。カーソルを違う形のものに設定したい場合には、値を返す前に wind_setcursor に引数 -1 を与えて呼び出して下さい。 |
bidleout |
||
bidleout メッセージは、ロックされたパッチャーウィンドウで、カーソルがあなたのオブジェクトの長方形領域から出た時に送られます。これによって、bidle メッセージに応答して行ったことを止めます。 | ||
バインディング |
||
addmess (myobject_bidleout, "bidleout", A_CANT, 0); | ||
宣言 |
||
void myobject_bidleout (t_myobject *x, Point pt); | ||
pt | マウスがオブジェクトの長方形領域を離れた位置。ローカル座標で表されます。 | |
bidleout メッセージは、ロックされたパッチャーウィンドウで、カーソルがあなたのオブジェクトの長方形領域から出たときに送られます。 |
ユーザインタフェイスオブジェクトを書く上で役に立つ、いくつかの関数があります。今までに述べたように、ほとんどのユーザインターフェイスオブジェクトは int や set のようなメッセージに応答して表示されている状態を変えるために Qelemを使う必要があります。 あなたのオブジェクトが閉じられたパッチャーウィンドウにあったり、ユーザによって非表示にされていたりすることも考えられるため、queue 関数の中で何らかの描画を行なう前に patcher_setport および box_nodraw を呼び出すことは重要です。しかし、これらのルーチンを update メソッドの中で使う必要はありません。もしも、queue 関数の中から update メソッドを呼び出したい場合には、update メソッドの外で必要な準備を行なってから update メソッドを呼び出します。これに関しては、color メッセージの説明の後に例を示してあります。
box_ownerlocked |
||
box が含まれているパッチャーがロックされているかどうかを判定するために、box_ownerlocked を使います。 | ||
short box_ownerlocked (t_box *b); | ||
b | あなたのユーザインターフェイスオブジェクトのヘッダである t_box 構造体。ここへ、あなたのオブジェクトへのポインタを渡すことだけが可能です。 | |
arg2 | ||
この関数は、box を持っているパッチャーウィンドウがロックされていれば0以外、ロックされていなければ0の値を返します。(通常、ロックされていないパッチャーでは、パッチャーウィンドウの最上部に UIオブジェクトツールバーが表示されています。 ユーザインターフェイスオブジェクトは、パッチャーウィンドウがロックされた場合、自分自身を違う形で描画したい場合もあるでしょう。box のアウトレットがパッチャーがロックされると1ピクセル下方に広げられることに気がつくかもしれません。これは、あなたのために自動的に行なわれています。 |
box_size |
||
box のサイズ変更のために、box_size を使います。 | ||
void box_size (t_box *b, short hsize, short vsize); | ||
b | あなたのユーザインターフェイスオブジェクトのヘッダである t_box 構造体。ここへ、あなたのオブジェクトへのポインタを渡すことだけが可能です。 | |
hsize | box の新しい水平サイズ。 | |
vsize | box の新しい垂直サイズ。 |
box_nodraw |
||
ユーザインターフェイスオブジェクトが描画されているかどうかを判定するために、box_nodraw を使います。 | ||
short box_nodraw (t_box *b); | ||
b | あなたのユーザインターフェイスオブジェクトのヘッダである t_box 構造体。ここへ、あなたのオブジェクトへのポインタを渡すことだけが可能です。 | |
box_nodraw は描画の際に、ユーザインターフェイスオブジェクトの update メソッドの外で使われなければなりません。この関数は、Box の存在するパッチャーがロックされていて、Box が非表示の場合(ユーザが Max メニューから Hide on Lock を選んだ場合)、0以外の値を返します。この関数はクリッピング領域をセットすることもできるので、あなたのオブジェクトを他のオブジェクトに対して正しく描画することができます。例えば、あなたのオブジェクトが部分的に bpactcher の端で隠されている場合、box_nodraw はクリッピング処理を行なってくれます。しかし、これらのサービスは、box_new に F_SAVVY フラグを渡した場合にしか提供されません。このフラグは、box_nodraw と box_enddraw を、update 以外の描画で使っていたことを示しています。 オブジェクトは型指定されたメッセージ(すなわち、A_CANT アーギュメント型指定子を持つメッセージ以外のメッセージ) を受け取ったときに描画してはいけません。例えば、slider は int メッセージに応答してセットされた queue 関数の中で値が変わった場合に描画されます。queue 関数の中では、描画の前に box_nodraw がチェックされます。box_nodraw はまずオブジェクトのパッチャーウィンドウが可視であると(patcher_setport で)判定した後にのみ、呼び出されなくてはなりません。box_nodraw は、ユーザインターフェイスオブジェクトの update メソッドの中でを呼び出されるとクラッシュを引き起こします。すべてのクリッピングは、box_new に F_SAVVY フラグを渡したかどうかにかかわらず update メソッドの中でセットアップされます。 |
box_enddraw |
||
box_nodraw を呼び出した後、描画を完了したことをパッチャーに対して報告する場合に、box_enddraw を使います。 | ||
void box_enddraw (t_box *b); | ||
b | あなたのユーザインターフェイスオブジェクトのヘッダである t_box 構造体。ここへ、あなたのオブジェクトへのポインタを渡すことだけが可能です。 | |
box_enddraw は、update メソッドの外で描画が行なわれた場合に使います。これは、描画が完了し、その前のbox_nodraw の呼び出しで False が返されたときに呼び出されます。描画ルーチンの例は次のようなものになるでしょう。 if (gp = patcher_setport(x->m_box.b_patcher)) { if (!box_nodraw((t_box *)x)) { // ここで描画を行ないます box_enddraw((t_box *)x); } patcher_restoreport(gp); } box_nodraw と box_enddraw から最大の便益を引き出すために、インスタンス生成ルーチンの中でbox_new を呼び出す場合にF_SAVVY フラグを追加して下さい。 box_new((t_box *)x, patcher, F_DRAWFIRSTIN | F_NODRAWBOX | F_GROWBOTH | F_SAVVY, l, t, r, b); |
box_redraw |
||
box のフレームと内容の再描画を実行させるために、box_redraw を使います。 | ||
void box_redraw (t_box *b); | ||
b | あなたのユーザインターフェイスオブジェクトのヘッダである t_box 構造体。ここへ、あなたのオブジェクトへのポインタを渡すことだけが可能です。 | |
この関数は、影響の及ぶ範囲にある他の Box や パッチコードと同様に、Box 全体を消し、再描画します。あなたのオブジェクトは結果として update メッセージを受け取ります。 |
box_visible |
||
box が可視かどうか(パッチャーが可視かどうか、または、bpacther の中で可視かどうか)を判定するために、box_visible を使います。 | ||
short box_visible (t_box *b); | ||
b | あなたのユーザインターフェイスオブジェクトのヘッダである t_box 構造体。ここへ、あなたのオブジェクトへのポインタを渡すことだけが可能です。 | |
この関数は、スクリーン上でBox が見える場合は0以外の値を、見えない場合は0を返します。 |
patcher_deselectbox |
||
パッチャー内の box を視覚的に選択されていない状態にするために、patcher_deselectbox を使います。 | ||
void patcher_deselectbox (t_patcher *p, t_box *b); | ||
p | box が含まれるパッチャー(b->b_patcher) | |
b | あなたのユーザインターフェイスオブジェクトのヘッダである t_box 構造体。ここへ、あなたのオブジェクトへのポインタを渡すことだけが可能です。 |
patcher_dirty |
||
パッチャーに、保存されていないデータを持っていることを表すマークをつけるために、patcher_diry を使います。 | ||
void patcher_dirty (t_patcher *p); | ||
p | オブジェクトが含まれるパッチャー | |
この関数は、パッチャーウィンドウの dirty ビットを設定します。これにより、ウィンドウが閉じられるときに、ユーザに対して変更を保存するかどうか尋ねます。これを行なう必要があるのは、あなたのオブジェクトが、データをパッチャーの中に保存していて(例えば、table オブジェクトはその要素をパッチャーの中に保存することができます)、そのデータが変更された場合だけです。 注:ユーザインタフェイスオブジェクト以外のオブジェクトの場合、Box を「所有するパッチャー」の識別は、オブジェクト生成関数(これが可能な唯一の時です)の中で、シンボル #P に結び付けられたオブジェクトを取得することによって可能になります。 void *mypatcher; mypatcher = ((t_symbol *)gensym("#P"))->s_thing; |
patcher_selectbox |
||
視覚的に box を選択したい場合に、patcher_selectbox を使います。 | ||
void patcher_selectbox (t_patcher *p, t_box *b); | ||
p | box が含まれるパッチャー (b->b_patcher) | |
b | あなたのユーザインターフェイスオブジェクトのヘッダである t_box 構造体。ここへ、あなたのオブジェクトへのポインタを渡すことだけが可能です。 |
patcher_setport |
||
カレント(現在)のグラフポートをパッチャーウィンドウに設定する場合に、patcher_setport を使います。 | ||
GrafPtr patcher_setport (t_patcher *p); | ||
p | box が含まれるパッチャー (b->b_patcher) | |
この関数はカレントグラフポートをパッチャーウィンドウに設定します。patcher_setport はそれまでのポートを返しますが、パッチャーウィンドウがその時点で可視になっていない場合0を返します。可視でない場合は何も描画すべきではありません。patcher_setport が成功した場合、パッチャーウィンドウでの一連の描画が終わった後、その戻り値で SetPort を呼び出します。この関数は、wind_setport と同じではない点に注意して下さい。この関数の使い方は、Chapter 10 の中で示した、wind_setportの例と類似していますが、ユーザインタフェイスオブジェクトで patcher_setport の代わりに wind_setport を使わないで下さい。あなたが賢い考えだと思って、パッチャーの p_wind フィールドを wind_setport に渡したとしたら、結果的に火傷をすることになるでしょう。それは、bpatcher オブジェクトに含まれるパッチャーは、このフィールドに無効な情報を持っているからです。 注:Windows XP 上でも、クリッピングリージョンが正しく働くために、この関数を呼び出すことは重要です。しかし、返される GrafPtr は不透明な型を持っているため、これは patcher_restoreport の呼び出しによってポート復元するときにだけ使用されるべきです。 注(追加):Windows XP 上で、QTML を使っている場合、XQT_patcher_setportおよび XQT_patcher_restoreport を使う必要があります。これ以上の詳細については、後述の XQT API を参照して下さい。 |
patcher_restoreport |
||
patcher_setport の戻り値にグラフポートをセットする場合に、patcher_restoreport を使います。Macintosh 上では、現在この関数は、C のマクロを介して MacSetPort() を呼び出しています。 | ||
void patcher_restoreport (GrafPtr gp); | ||
gp | patcher_setport によって返される不透明な構造体 | |
この関数は、カレントのグラフポートを、patcher_setport によって返されたポートにセットします。 |
patcher_okclose |
||
パッチャーウィンドウの okclose メッセージのレシーバをセットする場合に、patcher_okclose を使います。 | ||
void patcher_okclose (t_patcher *p, t_object*target); | ||
p | あなたのオブジェクトを含んでいるパッチャー | |
target | あなたのオブジェクト | |
patcher_okclose を呼び出した後、パッチャーウィンドウは、オブジェクトターゲットで受け取ったあらゆる okcloseメッセージを送り出します。あなたのオブジェクトがパッチャーウィンドウそれ自身を開いたケースでは、ウィンドウの保存、またはクローズを標準でない方法で処理したい場合もあるでしょう。okclock メソッドを実装し、パッチャーウィンドウを開いた後、オブジェクト自身を patcher_okclose に渡します。okclock メソッドの書き方の説明は、Chapter 10 を参照して下さい。 |
ispatcher |
||
オブジェクトがパッチャーかどうか判定するために、ispatcher を使います。 | ||
short ispatcher (t_object *obj); | ||
obj | パッチャーではないかと思われる謎のオブジェクト | |
オブジェクトがパッチャーである場合はゼロ以外の値を、そうでない場合は0を返します。ウィンドウリストから、すべてのパッチャーウィンドウを見つけるために、これを使いたいと思うかも知れません。Macintosh上では、最初に、Macintosh ウィンドウに結び付けられた Max の t_wind オブジェクトへのポインタを得るために GetWRefCon を使います。Windows XP では、Windows ウィンドウに結び付けられた Max の t_wind へのポインタを得るために、wind_fromhwnd を使うことができます。その後、有効な t_wind が返された場合、その t_wind の w_assoc フィールド で ispatcher を呼び出します。 あるいは、あなたがパッチャーの box のリストを横断的に見る場合、(b_firstin フィールドで) box に結び付けられているオブジェクトが、サブパッチャーかどうかを知ることに興味を持つかもしれません。box それ自身を渡す代わりに、box の b_firstin フィールドがゼロ以外の値であるかどうかをチェックし、そうである場合に、ispatcher に box を渡します。ispatcher には実際の Max オブジェクトを渡す必要があります。 |
isnewex |
||
オブジェクトがオブジェクトボックスかどうかを判定するために、isnewex を使います。 | ||
short isnewex (t_object *obj) | ||
obj | オブジェクトボックスではないかと思われる謎のオブジェクト | |
オブジェクトがノーマルオブジェクトのテキストを持つオブジェクトボックスの場合、0以外の値が返されます。そして、t_box の b_firstin フィールドでオブジェクト自身へのポインタを知ることができます。isnewex には、実際の Max オブジェクトを渡すことを確認して下さい。 注:どのような場合でも、ext_mess.h で定義されている ob_name または ob_class というマクロを使って、オブジェクトの型を見つけることができます。その後、文字列(ob_name の場合)またはシンボル(ob_class の場合)を探しているものと比較します。ispatcher と isnewex は若干速く動作します。 |
newex_knows |
||
オブジェクトボックスのオブジェクトがメッセージに応答できるかどうかを判定するために、newex_knows を使います。 | ||
short newex_knows (t_box *b, t_symbol *msg); | ||
b | あなたのユーザインターフェイスオブジェクトのヘッダである t_box 構造体。ここへ、あなたのオブジェクトへのポインタを渡すことだけが可能です。 | |
msg | オブジェクトが理解する、またはしないメッセージのセレクタ | |
この関数は、オブジェクトがノーマルオブジェクトのテキストを持つオブジェクトで、これがバインドされているオブジェクトが msg で与えられたメソッドをもつ場合、0以外の値を返します。 |
patcher_eachdo |
||||||||||
メモリにあるパッチャーごとに関数を呼び出す場合、patcher_eachdo を使います。 | ||||||||||
void patcher_eachdo (method fun, void *arg); | ||||||||||
fun | 呼び出したい関数。宣言のしかたは下記を参照して下さい。 | |||||||||
arg | 関数に渡したい引数 | |||||||||
関数 fun は非表示のサブパッチを含むメモリ上のパッチャーごとに呼び出されます。これは、次のように宣言されます。
あなたの関数は、他のパッチャーのために引き続き呼び出しを行ないたい場合は0を、止める場合は0以外の値を返さなければなりません。patcher_eachdo は特定のオブジェクトのための探索ルーチンとして使うこともできるでしょう。これは、ビルトインオブジェクトである send や receiveで dblclick メッセージへの応答として使われています。これらのオブジェクトは同じ t_symbol にバインドされている他の send や receive オブジェクトがないかどうか全てのパッチャーを探索します。そして、該当のオブジェクトのウィンドウを前面に移動させます。 |
assist_drawstring |
||
パッチャーウィンドウのアシスタンスエリアに文字列を描画するために、assist_drawstring を使います。 | ||
void assist_drawstring (void *patcher, char *string); | ||
patcher | 文字列を描画したいパッチャー | |
string | 描画するC文字列 | |
パッチャーウィンドウのアシスタンスエリアには、ユーザがオブジェクトをクリックしている間、役に立つメッセージを表示するために使われます。bpatcher オブジェクトの内側に見えるものをスクロールしているとき、その時点での水平、および垂直方向のオフセット値が表示されるのは、その一例です。 |
あなたのユーザインターフェイスオブジェクトが color メッセージに応答する場合、ユーザは16色のうちの1つを割り当てることができる標準のサブメニューを利用することができます。color メニューは個々の色をプレビューし、色の選択を行なうことができます。
color メニューはオブジェクトの t_box の b_color フィールドを 0 〜 15 の値に設定できます。その後、オブジェクトを再描画するために、update メッセージが送られます。そのため、color 機能を実装した場合、update メソッドで box の b_color フィールドの変化をチェックする必要があります。box_color 関数を使うことにより、b_color フィールド値に関連付けられた、その時点での RGB カラーの値を得ることができます。
color |
||
color メッセージにはいくつかの使い方があります。第1に、オブジェクトが選択された時 color メニューが有効ならば、このメッセージに応答しなければなりません。第2に、ユーザが color を 0 に設定する(これは、たいてい黒またはグレーになります)と、オブジェクトに color メッセージが送られます。このメッセージは、ユーザがオブジェクトの色を変更するために、直接送信することも可能です。このメッセージを受け取った場合には、オブジェクトの再描画をキューに入れるだけでなく、引数として受けとった値を box の b_color フィールドの値に設定します。Max ユーザは color メッセージに対してどのような引数でも送ることができるため、この値を 0 〜 15(標準の色セットのインデックス値)に制限する必要があります。 |
||
バインディング |
||
addmess (myobject_color, "color", A_LONG, 0); | ||
宣言 |
||
void myobject_color (myObject *x, long color); | ||
color | 選択された色を表す 0 〜 15 の数値 | |
このメッセージに応答して、オブジェクトの b_color フィールド に指定された色の値をセットしなければなりません。その後、オブジェクトの再描画を行ないます。これは、defer 関数を使ってオブジェクトの再描画を行なう方法の例です。 void myuserobject_color (t_myuserobject *x, long color) { x->m_box.b_color = color; // box の色フィールドを設定 defer(x,(method)myuserobject_docolor,0,0,0); // 再描画の実行 } void myuserobject_docolor (t_myuserobject *x) { GrafPtr gp; if (gp = patcher_setport(x->m_box.b_patcher)) { if (!box_nodraw((t_box *)x)) { myuserobject_update(x); box_enddraw((t_box *)x); } SetPort(gp); } } update メッセージでは、box の b_color フィールドの色の値を、標準の色セットを参照するためのインデックスに変換する必要があります。これは、box_color 関数によって行なうことができます。 |
frgb, brgb, rgb1 etc. |
||
これらのメッセージは、オブジェクトの様々な外観に対して色を設定します。これは Max によって送られるのではなく、取り決めとして存在しています。そのため、ユーザは、オブジェクトが RGB をサポートしている場合には、このようなメッセージによって動作することを期待するでしょう。インスペクタの標準のコンポーネントには、これらのメッセージで動作するように設計された swatch オブジェクト が含まれています。frgb メッセージはオブジェクトの「内容(例えば、テキストの色など)」に対応し、brgb メッセージは内容の背後にある背景色を設定します。オブジェクトがそれ以上に色を使用する場合には、rgb1、rgb2、などによって設定しなければなりません。 | ||
バインディング |
||
addmess (myobject_frgb, "frgb", A_LONG, A_LONG, A_LONG, 0); | ||
宣言 |
||
void myobject_frgb (t_myuserobject *x, long r, long g, long b); | ||
r, g, b | 0 - 255の値による RGB カラーの構成要素 | |
これらのメッセージはオブジェクトの構成要素の色を変更します。そして、低い優先度でオブジェクトの再描画を行います。これはその例です。ここでは、オブジェクトに m_fg というフィールドがあり、このフィールドが持つ RGB カラーの値によって、オブジェクトが使用する前景色が決められるという前提になっています。下記の frgb メソッドでは、Max の色表現(各要素あたり8ビット)と Mac OS の色表現(各要素あたり16ビット)の間での変換を行い、その後、オブジェクトの再描画を待機状態にさせています。 void myuserobject_frgb (t_myuserobject *x, long r, long g, long b) { x->m_fg.red = CLIP(r*257, 0, 65535); x->m_fg.green = CLIP(g*257, 0, 65535); x->m_fg.blue = CLIP(b*257, 0, 65535); defer(x,(method)myuserobject_doredraw,0,0,0); } これは、myuserobject_doredraw 関数です。これまでの、他のサンプルでもおなじみのものです。 void myuserobject_doredraw(t_myuserobject *x) { GrafPtr gp; if (gp = patcher_setport(x->m_box.b_patcher)) { if (!box_nodraw((t_box *)x)) { myuserobject_update(x); box_enddraw((t_box *)x); } SetPort(gp); } } |
box_color |
||
box の色をカレントの前景色で設定する場合に、box_color を使います。 | ||
バインディング |
||
void box_color (t_box *b); | ||
b | あなたのユーザインターフェイスオブジェクトのヘッダである t_box 構造体。ここへ、あなたのオブジェクトへのポインタを渡すことだけが可能です。 | |
box_color は box の b_color フィールドにカラーインデックス(0-15)が格納されていることを前提にしています。この関数はパッチャーウィンドウがカラーで表示されているかどうかをチェックし、そうであれば b_color フィールドに対応した RGB 値を調べて、その値を前景色にセットします。box_color を呼び出した後、オブジェクトの色をつけたい部分の描画ができるようになります。その際、RGBForeColor を呼び出すことによって、前景色を黒に復元することができます。 Windows XP では、適切なネイティブ Win32 API 呼び出し、または適切な QTML QuickTime 呼び出しと共に、 box_getcolor を使わなければならない点に注意して下さい。Windows XP で box_color を呼び出すと、予想外の結果を引き起こします。これは、前景色を黒にリセットするための RGBForeColor の呼び出しが存在しないためです。 |
box_usecolor |
||
box がカラーで描画されるようになっているかどうかを判定するために、box_usecolor を使います。 | ||
long box_usecolor (t_box *b); | ||
b | あなたのユーザインターフェイスオブジェクトのヘッダである t_box 構造体。ここへ、あなたのオブジェクトへのポインタを渡すことだけが可能です。 | |
box_usecolor は、あなたのオブジェクトボックスがカラーで描画されるようになっている場合には0以外の値を、そうでない場合は0を返します。この決定を行う際、異なる色深度をもつマルチモニタについての考慮がなされます。あなたのオブジェクトが update メッセージを受け取るたびに、この関数を呼び出すことは良い方法であると言えます。 |
box_getcolor |
||
box のカラーインデックスと結び付けられた色の RGB 値を取得する場合に、box_getcolor を使います。 | ||
void box_usecolor (t_box *b, short index, RGBColor *rgb); | ||
b | あなたのユーザインターフェイスオブジェクトのヘッダである t_box 構造体。ここへ、あなたのオブジェクトへのポインタを渡すことだけが可能です。 | |
index | RGB 値を得ようと思うカラーインデックス (0-15) | |
rgb | インデックスに対応した RGB 値はここに保存されます。 | |
box_getcolor は、引数 index で与えられたカラーインデックス(0-15)の色の RGB 値を、rgb に返します。 |
矩形領域をすべて描画するのではなく、その下にある他のオブジェクトが見えるようなオブジェクトを作ることができます。よく知られている例では、comment オブジェクトが挙げられます。これは、その下にある他のオブジェクトの上にテキストを描画します。
最初に、シンプルなケースを見てみましょう。矩形領域を消したり、塗りつぶしたりせずに、バックグラウンド上にテキストを描画したいオブジェクトを考えます。このサンプルオブジェクトは、矩形領域の中に、決められた文字列 “Max”を描画します。また、”frgb”メッセージに応答して、テキストの色を変えることも行います。したがって、qelem の中で自分自身の再描画を行う必要があります。オブジェクトのデータ構造体は次のようなものになるでしょう。
typedef struct _drawmax { t_box d_box; void *d_qelem; RGBColor d_color; } t_drawmax;
次は、インスタンス生成ルーチンです。オブジェクトは矩形領域の座標と赤、青、緑の値を保存しています。
void *drawmax_new(t_symbol *s, short argc, t_atom *argv) { t_drawmax *x = (t_drawmax *)newobject(drawmax_class); t_patcher *p = argv[0]->a_w.w_obj; short top, left, bottom, right; top = argv[1]->a_w.w_long; left = argv[2]->a_w.w_long; bottom = argv[3]->a_w.w_long; right = argv[4]->a_w.w_long; box_new ( x,p, DRAWFIRSTIN | F_NODRAWBOX | F_GROWBOTH | F_SAVVY | F_TRANSPARENT, left,top,right,bottom ); x->d_color.red = argv[5]->a_w.w_long; x->d_color.green = argv[6]->a_w.w_long; x->d_color.blue = argv[7]->a_w.w_long; x->d_qelem = qelem_new(x,(method)drawmax_redraw); // qelem の定義 box_ready((t_box *)x); return x; }
次は、オブジェクトの update メソッドです。これは box の矩形領域をクリップし、box の外側に “Max”という語が書かれないようにします。これが、最初に box を消していないことに注目して下さい。テキストは、既存の背景の上に現れます。
void drawmax_update(t_drawmax *x) { RgnHandle old,boxclip; RGBColor saveColor; GetClip(old = NewRgn()); // 既存のクリップリージョンを取得 RectRgn(boxclip = NewRgn(), &x->d_box.b_rect); // box の矩形領域を // リージョンにします SectRgn(old,boxclip,boxclip); //2つの交差する領域 SetClip(boxclip); // カレントのクリップリージョンにします // テキスト描画の準備 MoveTo(x->d_box.left + 4, x->d_box.bottom ? 4); TextFont(0); TextSize(12); GetForeColor(&saveColor); // 既存の色の値を取得 RGBForeColor(&x->d_color);// 使用する色の設定 DrawString("\pMax"); // テキストの描画 RGBForeColor(&saveColor); // すべての設定を復元します… SetClip(old); DisposeRgn(old); DisposeRgn(boxclip); }
これは、オブジェクトが色の変更を処理する方法です。
void drawmax_frgb(t_drawmax *x, long r, long g, long b) { x->d_color.red = CLIP(r*257, 0, 65535); // 新しい色を取得 x->d_color.green = CLIP(g*257, 0, 65535); x->d_color.blue = CLIP(b*257, 0, 65535); qelem_set(x->d_qelem); // 再描画 }
次は、drawmax_redraw ルーチンです。このルーチンは qelem の中から呼び出されます。これは、drawmax_update と、その前後の box_nodraw や box_enddraw といった処理すべてを含むラッパーになっています。
void drawmax_redraw(t_drawmax *x) { GrafPtr gp; if (gp = patcher_setport(x->d_box.b_patcher)) { if (!box_nodraw((t_box *)x)) { drawmax_update(x); box_enddraw((t_box *)x); } SetPort(gp); } }
Max の最適化処理を助けるために、透過オブジェクトは再描画の際に「clipresion メソッドを持つこと」ができます。あなたのオブジェクトの下にあるオブジェクトが再描画を行う際、Max はあなたのオブジェクトをクリップアウトします。あなたのオブジェクトが透過オブジェクトの場合、あなたのオブジェクトの box すべてがクリップアウトされることは望まないはずです。 clipregion メソッドによって、クリップして欲しい場所を指定することができます。
addmess((method)myobject_clipregion, "clipregion", A_CANT, 0);
このメソッドは、Max に対してあなたのオブジェクトが持っている中身のリージョン(領域)を報告します。テキストを描画している場合、テキストのマスキングを行うためのリージョンの定義は複雑過ぎるため、特別な定数 CLIPRGN_COMPLEX を返します。このことは、基本的に、再描画処理が行われる際、一番上のオブジェクトのテキストの再描画を回避することが不可能であるということを意味しています。透過でないオブジェクトの場合、この定数を使うべきではありません。実際、clipregion メソッドに対してデフォルトの CLIPRGN_RECT を与えることには全く何の意味もないからです。
void myobject_clipregion(t_drawmax *x, RgnHandle *rgn, short *result) { *result = CLIPRGN_COMPLEX; }
オブジェクトが clipregion メソッドを定義していない場合、オブジェクトの矩形領域をクリップすることが前提となります。
別の可能性として、矩形領域全体を満たすわけではないが、ある形を描画するオブジェクトが考えられます。umenu オブジェクトはその一例でしょう。これは、矩形領域の境界より3ピクセル小さい長方形を描くオブジェクトです。
void smallrect_update(t_smallrect *x) { Rect r = x->s_box.b_rect; InsetRect(&r,3,3); if (!EmptyRect(&r)) PaintRect(&r); }
このオブジェクトの clipregion メソッドは次のようなコードを使います。これは、リージョンハンドルを小さな長方形によって定義された領域にセットし、結果を CLIPRGN_REGION にセットすることによって、考慮しなければならない領域が存在することを示しています。メソッドはリージョンハンドルを定義する必要がありますが、これは他の処理で使ってはいけません。Max は あなたの clipregion メソッドを呼び出した直後に、これを破棄してしまいます。
void smallrect_clipregion(t_smallrect *r, RgnHandle *r, short *result) { Rect rect; rect = x->s_box.b_rect; InsetRect(&rect,3,3); // 矩形領域を、描画したいサイズに設定します *r = NewRgn(); RectRgn(*r,&rect); // その領域をリージョンにします *result = CLIPRGN_REGION; }
SDK にはcircle オブジェクトの例があり、コメントが豊富につけられています。このサンプルでは、他の、より精巧なクリッピングリージョンの使用例を見ることができます。サンプルは box_invalnow メソッドを qelem ルーチンの中で使って、移動によって現れるものを描画させ、透過オブジェクトでドラッグを処理する正しい方法を示しています。
ユーザインターフェイスオブジェクトは(そして、おそらく独自のウィンドウを持つ、ある種の非ユーザインターフェイスオブジェクトの場合も)、ユーザがパッチャーを使ってではなく、ダイアログボックスを使って、その設定を変える方法を実装することが望まれるでしょう。これは、Max 4 のインスペクタ・メカニズムを使うことで可能になります。インスペクタは特別に名付けられたパッチで、ユーザがロックされていないパッチャーでオブジェクトを選択し、”Get info...”を選んだときに開きます。これによって、オブジェクトのインスタンスの設定を編集することができます。
インスペクタは、Max をよりカスタマイズしやすくすることを目的にしています(ユーザはインスペクタパッチを希望どおりに変更することができます、そして/または、独自のパッチからその機能を使うこともできます)。同時に、ソフトウェアを他のプラットフォームへ移植しやすくすることも目的としています。インスペクタを実装するためには、オブジェクトは以下のようなことを行わなければなりません。
オブジェクトが正しく準備された後、myobject-insp.pat(myobject の代わりにオブジェクト名を使います)という名前のインスペクタパッチを書くことができます。そして、そのインスペクタパッチをサーチパス内(patchers フォルダの inspectors サブフォルダに置くことが望ましい)に置きます。インスペクタ・パッチは、thisobject オブジェクトを使って、あなたのオブジェクトのインスタンスと通信します。thisobject はあなたのオブジェクトの psave または pstate メソッドによって生成されたメッセージを出力します。この情報は、オブジェクトの現在の状態を示すために使われます。パッチは、インスタンスの設定変更のためにthisobject メッセージを送ることができます。このメッセージは単にオブジェクトに渡されるだけです。SDK では、hslider オブジェクトのソースコードと共に、コメントを付けたインスペクタパッチを提供しています。
inspector_open |
||
インスペクタを実装するために、オブジェクトは、次のように info メッセージを 関数 inspector_open にバインドします。 | ||
addmess(inspector_open, "info", A_CANT, 0); | ||
inspector_open 関数は Max に組み込まれています。これは、myobject-insp.pat を、サーチパスから探索して開きます。 inspector_open を使ってパッチを開くことによって、特別なバインディングが実行されます。これによって、パッチ内の thisobject オブジェクトは、調べられるインスタンスとリンクされます。インスペクタパッチは、特別なテクニックを使って書かれる必要があります。これについては、 hslider の例の中で書かれています。 |
notify_free |
||
inspector_open を使うオブジェクトの free メソッドの中で、notify_free を使います。 | ||
void notify_free(t_object *owner); | ||
owner | インスペクタと関連づけられているオブジェクト | |
この関数は、インスペクタを持ったあらゆるオブジェクトの free メソッドで呼び出すようにして下さい。これによって、開いているインスペクタからオブジェクトを切り離します。notify_free は box_free の前に呼び出されなければなりません。 |
pstate |
||
既存の psave メソッドの代わりに、pstate を使うことができます。これにより、インスペクタパッチをより簡単に書くことができるようになります。例えば、psave メソッドの中には、インスペクタに適用されない大量のデータを含んでいる必要があるものや、解析が難しいビットフィールドフォーマットのデータを保存するものがあります。オブジェクトが pstate メソッドを持っていれば、psave メソッドや save メソッドに優先して呼び出されます。 | ||
バインディング |
||
addmess (myobject_pstate, "pstate", A_CANT, 0); | ||
宣言 |
||
void myobject_pstate (myobject *x, void *w); | ||
w | あなたのオブジェクトが作成する、保存のために出力されるメッセージである binbuf へのポインタ | |
pstate はあなたのオブジェクトのクラスの名前となるシンボルを含みます。その後に、設定を反映した値が続きます。これらは保存、復元することができます。例えば、次はオブジェクトの内部にある3つの設定、m_multipler、m_weight、m_length を保存する pstate メソッドです。 void myobject_pstate(t_myobject *x, void *w) { binbuf_vinsert(w, "slll", gensym("myobject"), x->m_multiplier, x->m_weight, x->m_length); } |
エクスターナルがファイルを開くためにQuickTime の機能を使う必要がある場合、Max の QuickTime イメージルーチンを使用することができます。QuickTime は、イメージファイル(jpeg,gif,pict,photoshopファイル)、時系列メディアファイル(ムービー、Flash、3Dファイル)、非イメージファイル(テキスト、オーディオ)などを含む、多くのファイルタイプを開くことができ、それらをスケーリング、タイミング操作するための関数を持っています。QuickTime が開くことができるファイルの完全なリストは、QuickTime デベロッパドキュメンテーションを参照して下さい。
qtimage_open |
||
ファイルを読み込み、GWorld の中へレンダリングする場合に、qtimage_open を使います。 | ||
long qtimage_open(char *name, short path, CGrafPtr *gp, void *extra); | ||
name | 開くファイルの名前 | |
path | 選択されたファイルのファイルパス | |
gp | レンダリングされたファイルを受け取る GWorld | |
extra | ファイルをレンダリングする場合に使用される情報(qti_extra を参照) | |
ファイル名、パス、および GWorld(gp) を与えられると、qtimage_open はファイルを開き、与えられた GWorld の中へレンダリングします。イメージの描画時に、スケーリングや時間のオフセット(時系列メディアの場合)などの処理のために、extra の情報が使われる場合があります gp が NULL の場合、qtimage_open は新規に32ビットオフスクリーン gworld を生成し、その中にイメージを(必要ならスケーリング処理を行って)レンダリングします。呼出しを行ったオブジェクトには、このメモリを解放する責任があります。呼出しを行うオブジェクトが、オンスクリーン GWorld、非32ビット GWorld、あるいはオートフィットスケーリングモードを使用したい場合、gp は有効なポインタでなければなりません。 QuickTime がインストールされていない場合、このルーチンは PICT ファイルだけを開きます。Windows XP 上では、CGrafPtr が QTML グラフィックワールドで動作します。このため、QTI_FLAG_QTML_GWORLD フィールドが extra->flags にセットされている必要があります。後述する qtimage_extra_flags_set メソッドを使って、これをセットすることができます。 |
qtimage_getrect |
||
イメージファイルを(必要ならスケーリング処理を行って)レンダリングするために必要なサイズを取得する場合、qtimaage_getrect を使います。 | ||
long qtimage_getrect(char *name, short path, Rect *r, void *extra); | ||
name | 問い合わせを受けるファイル名 | |
path | 選択されたファイルのファイルパス | |
r | 要求されたレンダリングサイズを書き込まれる Rect 構造体 | |
extra | ファイルをレンダリングする場合に使用される情報(qti_extra を参照) |
qti_extra_new |
||
新規の qti_extra オブジェクトを作る場合に、qti_extra_new を使います。 | ||
void *qti_extra_new(void); |
qti_extra_free |
||
qti_extra_new によって割当てられたメモリを解放する場合に、qti_extra_free を使います。 | ||
void qti_extra_free(void *extra) | ||
extra | 解放する qti_extra オブジェクト |
qti_extra_matrix_get |
||
qti_extra オブジェクトの matrix メンバの内容を MatrixRecord にコピーする場合に、qti_extra_matrix_get を使います。 | ||
long qti_extra_matrix_get(void *extra, MatrixRecord *matrix); | ||
extra | コピー元(ソース)となる qti_extra オブジェクト | |
matrix | コピー先(デスティネーション)となる MatrixRecord | |
qti_matrix_get は、qti_extra オブジェクトの matrix メンバの内容を、MatrixRecord の中にコピーします。MatrixRecord や、それを使ってイメージを変形する方法に関しての詳しい情報は、Apple QuickTime デベロッパドキュメンテーションを参照して下さい。extra と matrix は共に有効なポインタでなければなりません。 |
qti_extra_matrix_set |
||
MatrixRecord の内容を qti_extra オブジェクトの matrix メンバにコピーする場合に、qti_extra_matrix_set を使います。 | ||
long qti_extra_matrix_set(void *extra, MatrixRecord matrix); | ||
extra | コピー先(デスティネーション)となる qti_extra オブジェクト. | |
matrix | コピー元(ソース)となる MatrixRecord | |
qti_extra_matrix_set は MatrixRecord の内容を qti_extra オブジェクトの matrix メンバにコピーします。MatrixRecord や、それを使ってイメージを変形する方法に関しての詳しい情報は、Apple QuickTime デベロッパドキュメンテーションを参照して下さい。qtimage_open や qtimage_rect でスケーリングに使われる qti_extra オブジェクトの matrix メンバのために、qti_extra オブジェクトの スケールモードは QTI_SCALEMODE_MATRIX にセットされていなければなりません(qti_extra_scalemode_set を参照)。 extra と matrix は共に有効なポインタでなければなりません。 |
qti_extra_rect_get |
||
qti_extra オブジェクトの rect メンバを Rect 構造体へコピーする場合に、qti_extra_rect_get を使います。 | ||
long qti_extra_rect_get(void *extra, Rect *rct); | ||
extra | コピー元(ソース)となる qti_extra オブジェクト | |
rct | コピー先(デスティネーション)となる Rect 構造体 | |
qti_extra_rect_get は qti_extra オブジェクトの rect メンバの内容を、標準の Rect 構造体にコピーします。Rect 構造体を使ってイメージを変形する方法に関する詳しい情報は Apple QuickTime デベロッパドキュメンテーションを参照して下さい。 extra と rct は共に有効なポインタでなければなりません。 |
qti_extra_rect_set |
||
Rect 構造体の内容を qti_extra オブジェクトの rect メンバにコピーする場合に、qti_extra_rect_set を使います。 | ||
long qti_extra_rect_set(void *extra, Rect *rct); | ||
extra | コピー先(デスティネーション)となる qti_extra オブジェクト. | |
rct | コピー元(ソース)となる Rect 構造体 | |
qti_extra_rect_set は Rect 構造体の内容を、qti_extra オブジェクトの rect メンバにコピーします。Rect 構造体を使ってイメージを変形する方法に関する詳しい情報は Apple QuickTime デベロッパドキュメンテーションを参照して下さい。qtimage_open や qtimage_rect でスケーリングに使われる qti_extra オブジェクトの rect メンバのために、qti_extra オブジェクトのスケールモード・メンバ変数は QTI_SCALEMODE_RECT にセットされていなければなりません(qti_extra_scalemode_set を参照)。 extra と rct は共に有効なポインタでなければなりません。 |
qti_extra_scalemode_get |
||
qti_extra オブジェクトの rect メンバの内容を scalemode (long) にコピーする場合に、qti_extra_scalemode_get を使います。 | ||
long qti_extra_scalemode_get(void *extra, long *scalemode); | ||
extra | コピー元(ソース)となるqti_extra オブジェクト | |
scalemode | コピー先(デスティネーション)となる scalemode (long として実装) | |
extra と scalemode は共に有効なポインタでなければなりません。 |
qti_extra_scalemode_set |
||
scalemode を qti_extra オブジェクトの rect メンバ変数にコピーする場合に、qti_extra_scalemode_set を使います。 | ||
long qti_extra_scalemode_set(void *extra, long scalemode); | ||
extra | コピー先(デスティネーション)となる qti_extra オブジェクト | |
scalemode | コピー元(ソース)となる scalemode (long として実装) | |
qti_extra_scalemode_set は、long の値を qti_extra オブジェクトの rect メンバ変数にコピーします。extra は有効なポインタでなければなりません。scalemode の可能な値は次のようになっています。 QTI_SCALEMODE_NONE QTI_SCALEMODE_MATRIX QTI_SCALEMODE_RECT QTI_SCALEMODE_AUTOFIT QTI_SCLEMODE_AUTOFIT を使った場合、qtimage_opne は有効な GWorld へのポインタ(gp)を必要とします。また、QTI_SCALEMODE_AUTOFIT を使った場合、qtimage_rect 関数は使用されません。これは、rect が、 qtimage_open に渡された GWorld と同じ大きさでイメージをレンダリングすることを要求するためです。 |
qti_extra_time_get |
||
qti_extra の time メンバ変数を、double (型の変数)にコピーする場合に、qti_extra_time_get を使います。 | ||
long qti_extra_time_get(void *extra, double *time); | ||
extra | コピー元(ソース)となる qti_extra オブジェクト | |
time | コピー先(デスティネーション)となる double(型の変数) | |
タイムユニットに関する詳しい情報は、Apple QuickTime デベロッパドキュメンテーションを参照して下さい。 extra と time は共に有効なポインタでなければなりません。 |
qti_extra_time_set |
||
double の値を qti_extra の time メンバ変数にコピーする場合に、qti_extra_time_set を使います。 | ||
long qti_extra_time_set(void *extra, double time); | ||
extra | コピー先(デスティネーション)となる qti_extra オブジェクト | |
time | コピー元(ソース)となる double の値 | |
extra は有効なポインタでなければなりません。タイムユニットに関する詳しい情報は、Apple QuickTime デベロッパドキュメンテーションを参照して下さい。 |
qti_extra_flags_get |
||
qti_extra の flag メンバ変数を、long(型の変数)にコピーする場合に、qti_extra_flags_get を使います。 | ||
long qti_extra_flags_get(void *extra, long *flags); | ||
extra | コピー元(ソース)となるqti_extra オブジェクト. | |
flags | コピー先(デスティネーション)となる long(型の変数) | |
extra と flags は共に有効なポインタでなければなりません。 |
qti_extra_flags_set |
||
long の値を qti_extra の flag メンバ変数にコピーする場合に、qti_extra_flags_set を使います。 | ||
long qti_extra_flags_set(void *extra, long flags); | ||
extra | コピー先(デスティネーション)となる qti_extra オブジェクト. | |
flags | コピー元(ソース)となる long の値 | |
extraは有効なポインタでなければなりません。 |
QuickDraw に大きな信頼を寄せる Macitosh デベロッパのために、QuickTime、あるいは他の Macintosh ToolBox の一部が、Apple の QuickTime Media Layer (QTML) for Windows によってサポートされています。あなたのオブジェクトを Windows 環境に移植する場合に、QTML ライブラリを使うことは道理にかなっているかもしれません。SDK の “qtxgui”サンプルプロジェクトでは、QTML の QuickDraw の実装を Max の UI オブジェクトの中で利用する方法について示しています。これが正しく動作するために、いくつかのヘルパー関数と関数を呼び出す方法に関する注意があります。これらのヘルパー関数は、ext_qtstubs.h で定義されていて、”XQT”というプリフィックスを持っています。そのため、これらの呼出しを XQT 呼出し、または XQT API と呼ぶことにしましょう。
XQT_GetQuickTimeVersion |
||
カレントの QuickTime のバージョンを取得する場合、XQT_GetQuickTimeVersion を使います。 | ||
long XQT_GetQuickTimeVersion (void); | ||
QuickTime がインストールされていれば、そのバージョンが返されます。インストールされていない場合は 0 が返されます。 |
XQT_CheckFunPtr |
||
QuickTime 関数のポインタをチェックするために、XQT_CheckFunPtr を使います。 | ||
long XQT_CheckFunPtr(FARPROC *ppfn, const char *name); | ||
ppfn | 関数へのポインタ | |
name | 関数の名前 | |
ppfn によって参照される関数ポインタが NULL の場合、QuickTime ライブラリから“name”で指定された関数をロードしようと試みます。戻り値はエラーコードです。 |
XQT_OpenMovieFilePathSpec |
||
ムービーファイルを開く場合に、XQT_OpenMovieFilePathSpec を使います。 | ||
OSErr XQT_OpenMovieFilePathSpec(const PATH_SPEC *pathSpec, short *resRefNum, SInt8 permission); | ||
pathSpec | PATH_SPEC | |
resRefNum | 参照ナンバ | |
permission | 読み込み/書き出しを許可するフラグ | |
OpenMovieFile と同じですが、QT FSSpec ではなく Max の PATH_SPEC を使います。この2つは Macintosh 上では同じものですが、Windows 上では異なります。戻り値はエラーコードです。 |
XQT_QT2MaxSpecCopy |
||
QuickTime の FSSpec を Max の PATH_SPEC へコピーする場合に、XQT_QT2MaxSpecCopy を使います。 | ||
long XQT_QT2MaxSpecCopy(FSSpec *qt_spec, PATH_SPEC *max_spec); | ||
qt_spec | コピー元(ソース)となる Quicktime FSSpec へのポインタ | |
max_spec | コピー先(デスティネーション)となる Max独自の PATH_SPEC へのポインタ | |
QT の FSSpect を Max の PATH_SPEC に変換します。戻り値はエラーコードです。 |
XQT_ Max2QTSpecCopy |
||
Max の PATH_SPEC を QT の FSSpec へコピーする場合に、XQT_Max2QTSpecCopy を使います。 (訳注:原文は“Use XQT_Max2QTSpecCopy to open a movie file.”となっていますが、誤植と思われます) |
||
long XQT_Max2QTSpecCopy(PATH_SPEC *max_spec, FSSpec *qt_spec); | ||
max_spec | コピー元(ソース)となる Max独自の PATH_SPEC へのポインタ | |
qt_spec | コピー先(デスティネーション)となる Quicktime FSSpec へのポインタ | |
Max の PATH_SPEC を QT の FSSpec に変換します。 戻り値はエラーコードです。 |
XQT_patcher_setport |
||
QTML ベースの UI エクスターナルを作る場合、patcher_setport の代わりに XQT_patcher_setport を使います。 | ||
GrafPtr XQT_patcher_setport(struct patcher *p) | ||
p | あなたの UI オブジェクトを含んでいるパッチャーへのポインタ | |
QTML ベースの UI エクスターナルのために、patcher_setport を置き換えるもの。Macintosh バージョンと違って、これは update メソッドの中でも呼び出されなければなりません。 |
XQT_patcher_getport |
||
Max パッチャーの QT ポートwを取得するために、XQT_patcher_getport を使います。 | ||
GrafPtr XQT_patcher_getport(struct patcher *p) | ||
p | あなたの UI オブジェクトを含んでいるパッチャーへのポインタ | |
Max パッチャーの QT ポートを返します。 |
XQT_patcher_restoreport |
||
QTML ベースの UI エクスターナルを作る場合、patcher_restoreport の代わりに XQT_patcher_restoreport を使います。 | ||
void XQT_patcher_restoreport(GrafPtr gp) | ||
gp | XQT_patcher_setport によって返される、不透明な GrafPort 構造体へのポインタ | |
以前のポートをリストア(復元)するための SetPort を置き換えるもの。これは、QTML ベースの UI エクスターナルが必要とするもので、XQT_patcher_setport が使われた場合、必ず一対の処理として用いなければなりません。 |
XQT_wind_setport |
||
QTML ベースの UI エクスターナルを作る場合に、wind_setport の代わりに XQT_wind_setport を使います。 | ||
GrafPtr XQT_wind_setport(struct wind *w) | ||
w | あなたの UI オブジェクトを含んでいるパッチャーのウィンドウへのポインタ | |
QTML ベースの UI エクスターナルのために、wind_setport を置き換えるもの。Macintosh バージョンと違って、これは update メソッドの中でも呼び出されなければなりません。 |
XQT_wind_getport |
||
Max パッチャーの QT ポートを取得するために、XQT_wind_getport を使います。 | ||
GrafPtr XQT_wind_getport(struct wind *w) | ||
w | あなたの UI オブジェクトを含んでいるパッチャーのウィンドウへのポインタ | |
Max ウィンドウの QT ポートを返します。 注:XQT_SetPortによってポートを保存していることを確認して下さい。wind_restoreport でリストア(復元処理)を行うと、予想外の結果を引き起こしてしまいます。 |
XQT_box_nodraw |
||
QTML ベースの UI エクスターナルを作る場合、box_nodraw の代わりに XQT_box_nodraw を使います。 |
||
GrafPtr XQT_box_nodraw(struct box *b) | ||
b | あなたの UI オブジェクトの Box へのポインタ | |
QTML ベースの UI エクスターナルの場合に必要とされ、 box_nodraw を置き換えるものです。 |
XQT_box_enddraw |
||
QTML ベースの UI エクスターナルを作る場合、box_enddraw の代わりに XQT_box_enddraw を使います。 (訳注:原文は、”Use XQT_box_enddraw to get a Max patcher’s QT port”となっています) |
||
GrafPtr XQT_box_enddraw(struct box *b) | ||
b | あなたの UI オブジェクトの Box へのポインタ | |
QTML ベースの UI エクスターナルの場合に必要とされ、box_enddraw を置き換えるものです。 |