Be Inc.の提供するプログラミング・チュートリアルの日本語訳第2弾でございます。この訳文の文責は、長田正彦にございます。翻訳には慎重を期したつもりですが、おかしな点、間違い等に気づかれた場合には、私までメールを頂けると幸いです。



プログラミングの手引:January


範囲

この手引は、初心者であるBe開発者向けのものです。 C++についての知識を仮定しています。 取り上げるのは、以下の話題です:

  • プロジェクトの生成
  • 基礎的なアプリケーション・フレームワーク
  • ウィンドウならびにウィンドウ内(ビュー階層)でのビューの生成
  • グラフィックスと動的にリサイズ可能なビュー

注: このファイルは、テキスト形式でftp://ftp.be.com/pub/Samples/jan_tutorial.txtに置いてあります。この手引きを読みながらBeBookを読む/復習する事によって、あなたの学習経験をより効果的なものにできます.....


序論

Januaryプログラムは、ウィンドウ内に、一月のカレンダーを表示します。 加えて、カレンダーはウィンドウがリサイズされた場合、動的に再描画します。

ソース・ファイル解説

  • GMCGraphics.cpp
    BTSMonthGraphicというオブジェクトを定義し、 これは、ビューに挿入することによって表示することができます。

  • MonthView.cpp
    BTSMonthGraphic型のオブジェクトを含むビューです。

  • January.cpp
    アプリケーション・ウィンドウおよびBTSApplication ウィンドウの定義、ならびにメイン・ルーチンを含みます。

  • StringLabel.cpp
    文字列を、センタリングとスタイリングを利用して表示するための ユーティリティー・クラスである、BTSSTringLabelです。

  • January.rsrc
    Januaryに関するアイコンや他の情報を含んでいるリソース・ファイル です。


プロジェクト

BeOS開発環境は、開発者が、プロジェクトファイルを作成し、これをIDEと共に用いる事により、プロジェクトやコンパイル情報やコマンドをグラフィカルに表現する事を可能にします。 UNIXの世界からやって来た人のために、makefileやコマンド・ラインでのコンパイルもサポートされています。 Januaryプロジェクトは、両方の例を与えます。

この場合、CodeWarriorユーザは「January.proj」ファイルを開く訳ですが、一方のUNIXファンは、ターミナルを立ち上げて、Januaryディレクトリにcd【ディレクトリ変更コマンド】する訳です。


アプリケーション・フレームワーク

ベース・アプリケーション・クラスは、BApplicationです。 あらゆるアプリケーションは、BApplicationのインスタンスを継承しなければなりません。 このオブジェクトへの参照は、利便性のために、自動的にグローバル変数be_appに置かれます。 Januaryの場合、デフォルトのアプリケーション・クラスの動作をオーバーライドすることは、必要ではありません。 また、我々がアプリケーションを起動するとき、我々はウィンドウを自動的に開きたい訳です。 よって、我々のメイン・ルーチンは、このようになります:

main()
{
	BTSApplicationWindow        *aWindow;
	BRect                        aRect;

	// We can use a standard BApplication object, because
	// we don't have anything special to add to what it does
	// by default.
	BApplication *myApplication;

	myApplication = new BApplication('JANR');

	// set up a rectangle and instantiate a new window
	aRect.Set(20, 20, 200, 200);
	aWindow = new BTSApplicationWindow(aRect);

	// Once everything is setup, start running the application.
	myApplication->Run();

	// Delete the application object before exiting.
	delete(myApplication);
	return(0);
}

アプリケーション固有のウィンドウ

ウィンドウを生成するすべてのアプリケーションは、BWindowクラスをサブクラス化する必要があります。 この場合、我々のウィンドウ・クラスは、BTSApplicationWindowです。 クラスの定義は、このようになります:


class BTSApplicationWindow : public BWindow
{

public:
				BTSApplicationWindow(BRect frame);
virtual	bool	QuitRequested();
};

メソッドは、二つだけ定義されます。 コンストラクタをオーバーライドすることにより、下記のように、ウィンドウ内にカレンダーを生成するコードを挿入する事が出来ます:


BTSApplicationWindow::BTSApplicationWindow(BRect frame)
	: BWindow(frame, "January", B_TITLED_WINDOW, 0)
{

	BRect            aRect = frame; // rect same size as window.
	BTSMonthView    *aView;

	// Move rect so top left is a (0,0)
	aRect.OffsetTo(B_ORIGIN);
	aView = new BTSMonthView(aRect, "MonthView");

	// Lock the window before altering contents.
	Lock();
	// add view to window
	AddChild(aView);
	// Unlock after done altering window contents.
	Unlock();

	// make window visible
	Show();
}

我々はBTSMonthView型のオブジェクトを生成し、それをウィンドウと同じサイズにし、さらにそれをウィンドウに挿入します。 あらゆるビューは、他のビューを保持する事が可能なコンテナである、と考える事が出来ます。 これは、ビュー階層と呼ばれています。 あらゆるビュー階層のルートにあるのは、BWindowか、もしくはBWindowのサブクラスであるオブジェクトです。 BWindow自身はビューではなく、ビューであるプライベートメンバーを保有します。 このビューは、プログラマがアクセス出来ません。 よって、あなたは直接ウィンドウに描画せずに、むしろあなた自身のビューを生成し、それに描画して、そのビューをウィンドウのビュー階層に挿入します。 そして、ここではまさにそうされる訳です。

この他の、通常と異なるものは、AddChild()コールに関連したLock()コールおよびUnlock()コールです。 あなたがウィンドウの内部状態を変えるときはいつでも、ウィンドウをロックすることが必要です。 これに関する例: ビュー階層を変更する事、明示的にDraw()をコールする事、もしくはウィンドウをリサイズする事。 これらのコールは、他のスレッドが同時に、同じ様にウィンドウを変えてしまい、予測不可能な【実行】結果に終わる事を防ぐためにも必要です。 しかし、システムが直接コールする関数(Draw(), WindowActivated(), MessageReceived() )についてはLock()を呼び出す必要はなく、これはシステムが関数コールの前にロックを行うからです。。 みんな、インターフェース・キットに関する文書のBView章中にある、「ウィンドウのロック」と称する節を読んでおくべきです。

描画は全て、実際にはBTSMonthViewの内部で発生します。 今のところそれを無視しましょう。 BTSApplicationWindow中の唯一の他のメソッドは、QuitRequestedです:

bool
BTSApplicationWindow::QuitRequested()
{
	// Tell the application to quit!
	be_app->PostMessage(B_QUIT_REQUESTED);
	return(TRUE);
}

ユーザがいかなる手段でウィンドウを閉じる場合でも、BWindowオブジェクトのフック関数であるQuitRequested()が呼ばれます。 この場合、適切な反応は、アプリケーションに「B_QUIT_REQUESTED」メッセージをポストすることにより、プログラムを完全に終了する事です。 一般的なウィンドウの振る舞いのために用意された、これと同じようなフック関数がたくさんあります;インターフェース・キット文書の「ユーザに応答すること」という節をチェックして下さい。

ユーザー・インタフェースを表示するいかなるアプリケーションも、最後のウィンドウが閉じる時に、終了しなくてはなりません。 これがされないと、アプリケーションのメインメニューは消えてしまい、ユーザの理解している方法でプログラムを終了することができなくなります。 あなたがこの種の機能を必要とする場合、あなたは「ダミーの」BWindowオブジェクトを生成し、これを隠し、プログラムが終了する時には、これを削除するだけにしなくてはなりません。 あなたが複数のウィンドウを持つのであれば、ウィンドウの数を勘定し続けて、カウントが0になった時、プログラムを終了する必要があります。 この問題は、BeOSの後のリリースで解消される予定です。

この時点で、我々は基礎的なアプリケーション・フレームワークを生成します; メインおよびアプリケーション・ウィンドウに対するのと同一の定義を、あらゆる単一ウィンドウのアプリケーションに使う事ができます。 唯一変更する必要があるのは、我々がウィンドウのコンストラクタ内で、ウィンドウに挿入する事になるビューです。

グラフィックスと動的にリサイズするビュー

グラフィックスに関するしかけは、すべてBTSMonthViewオブジェクト内でなされます。 これがコンストラクタです:


BTSMonthView::BTSMonthView(BRect frame, char *name)
    : BView(frame, name, B_FOLLOW_ALL,
        B_FULL_UPDATE_ON_RESIZE|B_WILL_DRAW|B_FRAME_EVENTS)
{
	fBackgroundGraphic = new BTSMonthGraphic(this);
	SetViewColor(B_TRANSPARENT_32_BIT);
}

コンストラクタの中でなされる必要があるのは、カレンダーを表現しているグラフィックス・オブジェクトであるBTSMonthGraphicを生成する事だけです。 我々は、BTSMonthGraphicに、ビューに対する「this」ポインタを渡すことを注意しておいて下さい。 この理由は、BTSMonthGraphic自身がビュー・サブクラスではないためで、よって、それ【BTSMonthGraphic】は、描画を実行するためにグラフィックス・コンテキストを必要とするからです。 これは、ビューによって与えられる訳です。 多くの場合、単にグラフィックス・オブジェクト自身をBViewのサブクラスにする方が、より便利です。 しかし、ユーザと相互に作用しない様な単純なグラフィック(テキストラベルの様なもの)の場合、BViewをサブクラス化する必要はなく、オブジェクトに描画コンテキストを渡すやり方の方が、より速く、より効率的であり、グラフィックス・オブジェクトをより一般用途向けにします。

BViewコンストラクタの終わりで渡されるフラグに注意してください。 ビューの動作をカスタマイズするために、ビューに渡されるたくさんのフラグがあります; 詳細はインターフェース・キット文書を見てください。

ここで使われる特定のフラグは、以下の事を指定します:

  • FOLLOW_ALL − ビューがある方向からリサイズされる時、その方向にビューを 拡縮します。

  • FULL_UPDATE_ON_RESIZE − その時点で新たに表示される様になる部分以外の、ビュー全体を無効にします。 我々がこれを必要とするのは、新たな部分だけを再描画すればよいだけである一方で、この場合、サイズ変更があったら、ビュー全体を書き直して欲しいからです。

  • FRAME_EVENTS −ビューのサイズが変更される場合、我々は通知を必要とします。 我々がこのフラグを使わないと、FrameResizedメソッドは呼ばれず、我々はいつグラフィックのサイズを変えるべきか、決して分かりません。

  • WILL_DRAW −ビューは、アップデートイベントに対する応答として、自身のDraw()関数をコールされる必要があります。

動的なリサイズは簡単です; 我々はFRAME_EVENTSを指定したので、ウィンドウがリサイズされるにつれて、我々はビューのFrameResized()フック関数に対する、呼び出し【コール】のストリームを受けとり、そして、順番に、我々は埋め込まれたグラフィックをリサイズします:


void
BTSMonthView::FrameResized(float new_width, float new_height)
{
	fBackgroundGraphic->SetSize(BPoint(new_width, new_height));
}

描画

最後に、描画です! カレンダーのグラフィックは、グリッド【格子】によって表現されます。 ウィンドウのリサイズに伴って、グリッドの間隔も変ります。 新しい格子間隔に基づいて、カレンダーの上のいろいろなラベルのためのフォント・サイズとテキスト【書体の事か?】は選択されます。 我々は、BTSMonthViewDraw()メソッドによって呼ばれるSetSize()メソッドから始める事で、全ての動作に従うことができます:

void BTSMonthGraphic::SetSize(BPoint size)
{
	fSize = size;
	Invalidate();
}

void BTSMonthGraphic::Invalidate()
{
	// Do the resizing magic
	// Re-calculate the various size things
	CalculateParameters();
	CalculateDayLabels();

	// Make sure the view draws itself
	fImageNeedsUpdate = 1;

}

よろしい...あまり詳細には立ち入らない様にする事にして、CalculateParameters()は新しい格子サイズと間隔を決定します。 CalculateDayLabels()は、週のうちの特定の日に対して、そのラベルが何でなければならないかを決めます; 格子間隔が大きいならば、「Sunday【日曜日】」が有効になります; もし【格子間隔が】小さければ、それは単に「Su【日】」となります。fImageNeedsUpdateはフラグであり、グラフィックのDraw()関数に何らかの仕事をする様に伝えます。 ビューと共に、Draw()関数は、周囲のコンテナがアップデート・メッセージを受け取ると、自動的に呼ばれます。このコンテナは、ビューに占有される領域を部分的に、あるいは全て含みます。 この場合、我々のグラフィック・オブジェクトはビューではないので、我々は自前でこの情報をやりくりしなければなりません。

Draw()メソッド自体は、かなり簡単です。 背景が描画され、カレンダーの輪郭が描画され、月の名前と日の名前が描画され、月の各日が描画されます。 全てのテキストは、Init()メソッドにおいて生成されたStringLabelオブジェクトによって、表現されます。 StringLabelオブジェクトは、BTSMonthGraphicと同じ様に、ビューではない、というよりはむしろ、ビュー内で自分自身を描画する方法を知っています。

BeOS上での描画は、アプリケーション自身以外によってされるのではなく、アプリケーション・サーバーでなされます。 app serverは、別個のプロセスであり、これのコピーの一つは、常に動いています。 (あなたは、ターミナル・ウインドウを立ち上げて、コマンド「ps | grep app_server」を実行する事で、これを見ることができます。 全ての描画コマンドは、実行のためにapp serverに伝えられます。 この速度を上げるために、線描コマンドは配列としてまとめる事が出来るので、app serverはもっと迅速な方法でこれら【のコマンド】を実行する事が出来ます。 これは、BeginLineArray()メソッド、AddLine()メソッド、およびEndLineArray()メソッドを使うことで可能です。 BeginLineArray()を呼んた後に、AddLine()へのコールは、コマンドを配列に加えます。 EndLineArray()を呼ぶ事で、app serverに、線描コマンドの全配列が実行される様に知らせます。 BTSMonthGraphic::Draw()以下のコードの部分は、これを説明します:

	fView->BeginLineArray(7);
	for (counter = 1; counter < 7; counter++)
	{
		BPoint startPoint(xStart+(counter*GridCellSize.x)-2,yStart);
		BPoint endPoint(xStart+(counter*GridCellSize.x)-2,yEnd);
		fView->AddLine(startPoint, endPoint, blackColor);
	}
	fView->EndLineArray();
上記コードは、結果として、app serverとの七つの小さな通信の代わりに、一つの大きな通信となります。 しかし、EndLineArray()を呼ばずにAddLine()を余りに何度も呼び出すと、配列のメモリ・オーバーヘッドのために、パフォーマンスが落ちるかもしれません。 (BeBookでは、256以下を推奨しています)。

描画コマンドからの実際の描画の分離は、重要な問題をもたらします; プログラマは、描画命令が実際に何時実行されたのかをどうやって知るのでしょう? 多くの場合、app serverは受け取って直ぐには描画コマンドを実行しないかもしれません。 app serverに、特定のビューに対する全ての描画要求の完了を強制するために、ビューのSync()メソッドが、呼ばれます。 このメソッドは即実行され、そして、これが【あなたのアプリの実行空間に】戻って来れば、ビューのための全ての描画コマンドが実行を完了した事が保証されます。 対話的な描画をしている場合、これは特に重要です; 例えば、ユーザがウィンドウ内でグラフィックをドラッグしている場合、描画の後Sync()が呼ばれていないと、ドラッギングは飛び跳ねるように見えるかもしれません。

BTSStringLabel

BTSStringLabelは、描画に必要なフォントのパラメターを記憶する事の出来るオブジェクトであり、このパラメターに含まれているのは、ビュー内での位置、フォント名、サイズ、シアー、回転、および色です。個々のテキストに個別のビューを持たせる代わりに、BTSStringLabelがビューと関連付けられています。 描画する時点になると、BTSStringLabelオブジェクトはビューのフォント・パラメターを、それ【BTSStringLabel】自身の持つパラメターに合う様に変更し、関連した文字列を描画します:

void
BTSStringLabel::Draw(BView* aView)
{
	if (fNeedsCalculation)
		Recalculate(aView);

	// Setting font info
	aView->SetFontName(fFontInfo.name);
	aView->SetFontSize(fFontInfo.size);
	aView->SetFontShear(fFontInfo.shear);
	aView->SetFontRotation(fFontInfo.rotation);

	aView->SetHighColor(fColor);
	aView->MovePenTo(fStartPoint);
	aView->DrawString(fString);
}


IconWorld

IconWorldは、あなたのアプリのための、基礎的なリソースの構成をするために使われます。 BeOSユーザーズ・ガイドのIconWorldについての節には、この事が非常に詳しく書かれています。 マックの開発者であれば、これをBeOS用のResEditであると考えて下さい。扱えるリソースの数ははるかに少ないですが。

IconWorldを使って、January.rsrcを開いて下さい。 「App Info」メニューの下で、フラグ「単一の起動[Single Launch]」がセットされている事に注意してください。 これが意味する事は、一実行可能ファイル当り一つの、【セットがなされた】アプリケーションのコピーが実行可能だ、という事であって、これは「排他的起動[Exclusive Launch]」に対するもので、これはシステム空間、および実行期間中、ただ一つのアプリケーションだけが実行可能というものです。【意味不明】 もちろん、「複数の起動」が意味するのは、同一のアプリが同一システム上で複数回起動出来るという事です。 この場合、「単一の起動」の選択は任意でしたが、これが非常に重要になるアプリもあるかもしれません。

アイコンの型が、それがアプリケーションと関連することを意味する『BAPP』である点に注意してください。 このアプリがそれに関連した文書を持つのであれば、文書を表現する、別のアイコンを作る事も出来ますし、そのアイコンに、文書ファイルのシグネチャに合った型を与える事も出来ます。

IconWorldを閉じてください。


プロジェクトのビルドと実行

あなたは、BeOSユーザーズ・ガイドとMetrowerks文書を、アプリケーションの構築に関する完全な詳述として参照することができます。 下記は、手順の短い概要です。

今、あなたは基礎的なアプリケーション・フレームワークを手にしたので、これをあなた自身のアプリケーションの出発点として利用出来ます。 Beの従業員も彼らの子も、この手引の執筆中に、エピソード(発射出来ようとそうでなかろうと)を吐露することに熱中していませんでした【意味不明】。 我々は、同じことがあなたにもあてはまることを望みます。

UNIXユーザのために

Januaryディレクトリの中で、「make」とタイプしてください。 必要ならば、あなたはmakefileを開いて、それを確認することができます。 馴染みが無い様に見えるかもしれない唯一のものは、これらの行です:

	copyres $(RESOURCE_FORK) $@
	setfile $@

最初の行は、単にファイルの最後にリソース情報を追加します。 二番目の行は、BeOSに、新しく生成されたプログラムが実行可能形式である事を知らせます。

CodeWarriorユーザのために

プロジェクト・メニューから「Make」を選んでください。 あなたがそれを実行したいならば、「Run」を選択してください。


著作権©;1997年 Be社、 Beは登録商標です、そして、BeOS、BeBox、BeWare、GeekPort、BeロゴとBeOSロゴはBe社の商標です。 他に言及された全ての商標は、それらの各々の所有者の所有物です。
このサイトについてコメントがありますか? webmaster@be.com宛に書いてください。
ここで使われたアイコンは、Be社の所有物です。不許複製。


Copyright ©1997 Be, Inc. Be is a registered trademark, and BeOS, BeBox, BeWare, GeekPort, the Be logo and the BeOS logo are trademarks of Be, Inc. All other trademarks mentioned are the property of their respective owners.
Comments about this site? Please write us at webmaster@be.com.
Icons used herein are the property of Be Inc. All rights reserved.




日本語訳:長田 正彦(osada@st.rim.or.jp)
翻訳文中 【○○○】 の形式で記された部分は、訳者のコメントおよび補足です。