えー、長いことお休みしていてすみません。
実は最近BeOSプログラムから興味が離れて、つい最近またやってみたのですが、これがうまく動かない!
はまるところだらけで、何も資料がない場合やBe Bookだけでは非常につらいと思い
また始めることにしました。よろしくお願いします。
さて今回は、タイトルからもわかる通り「ボタン」です。
なんか進め方が王道中の王道ですが、お約束ってことでお願いします(^^;
今回からイベントドリブンという考え方を強く意識したプログラムになっていきます。
そのためBMessageというBeOSの強力なメッセージ機構を実現したクラスが出てきます。
このクラスは単なるメッセージ以外にもデータベース的な側面もあり、
それを利用したアーカイビングといったトピックもあるのですが、今回は扱いません。
BMessageは強力で多機能なので今回一回ですべてを説明するのは不可能なので、
部分的にしか解説していませんが、今回のプログラムを理解するには十分です。
今回も全体のソースを眺めることから始めましょう。
赤い部分に注目してください。
// sample05.cpp
#include <Application.h> // BApplicationを使うために必要
#include <Window.h> // BWindowを使うために必要
#include <View.h> // BViewを使うために必要
#include <Alert.h> // BAlertを使うために必要
#include <Button.h> // BButtonを使うために必要
// クラスの前方宣言
class MyApp;
class MyWindow;
class MyView;
const uint32 BUTTON_CLICKED = 'bucl'; // メッセージ定数
class MyApp : public BApplication
{
public:
MyApp();
private:
MyWindow* m_win;
};
class MyWindow : public BWindow
{
public:
MyWindow(BRect frame,const char* title,window_type type,uint32 flags);
virtual bool QuitRequested(); // 終了が要求されたときに呼ばれる関数
virtual void MessageReceived(BMessage* message); // メッセージが送られたときに呼ばれる関数
private:
MyView* m_view;
};
class MyView : public BView
{
public:
MyView(BRect frame,const char *name,uint32 resizingMode,uint32 flags);
~MyView();
private:
BButton* m_button; // 貼り付けるボタン
};
MyApp::MyApp()
:BApplication("application/x-vnd.big56-MyApp")
{
m_win = new MyWindow(BRect(100,100,250,150),"ボタンのテスト",B_TITLED_WINDOW,0);
m_win->Show();
}
MyWindow::MyWindow(BRect frame,const char* title,window_type type,uint32 flags)
:BWindow(frame,title,type,flags)
{
frame.OffsetTo(0,0); // 与えられたBRectの左上が(0,0)になるようにする
m_view = new MyView(frame,"theView",B_FOLLOW_ALL_SIDES,B_WILL_DRAW);
AddChild(m_view);
}
bool MyWindow::QuitRequested()
{
// アプリケーションを終了する
be_app->PostMessage(B_QUIT_REQUESTED);
return true;
}
void MyWindow::MessageReceived(BMessage* message)
{
BAlert* alert;
switch(message->what) // メッセージループ
{
case BUTTON_CLICKED:
alert = new BAlert("test","ボタンが押されました","OK");
alert->Go();
break;
default:
BWindow::MessageReceived(message);
break;
}
}
MyView::MyView(BRect frame,const char* title,uint32 resizingMode,uint32 flags)
:BView(frame,title,resizingMode,flags)
{
m_button = new BButton(BRect(25,15,125,35),"theButton","初めてのボタン",new BMessage(BUTTON_CLICKED));
AddChild(m_button);
}
MyView::~MyView()
{
}
int main(int argc,char** argv)
{
MyApp app;
app.Run();
return 0;
}
今回のプログラムを実行すると次の様な感じのウィンドウができます。
今回最重要なところは、MyWindow::MessageReceived()関数です。
BeOSのイベントドリブン型のプログラムの形態がやっと出てきます。
ちなみに、イベントドリブンとは、ユーザーからの入力に反応して動くプログラムです。
まずはボタンの作成から見ていきましょう。
const uint32 BUTTON_CLICKED = 'bucl'; // メッセージ定数
今回メッセージ定数として、BUTTON_CLICKEDという値を用意しました。
これはuint32(符号なしの32ビット型)の値なのですが、このメッセージ定数の値の書き方が
見慣れない人も多いと思います。
BeOSのメッセージ定数はシステムで用意しているメッセージ定数とバッティングしないように独特の書き方が推奨されています。
例えば、ビューに対してマウスがクリックされたことを表すB_MOUSE_DOWNは'_MDN'と定義され
今回のソースでも登場している、アプリケーションに終了を通知するB_QUIT_REQUESTEDは'_QRQ'と定義されます。
これらの定義場所は、/boot/develop/headers/be/app/AppDefs.hにあります。
システム定義のメッセージは、アンダーバー('_')から始まるか、またはすべて大文字の4文字定数で定義されています。
これとバッティングしないようにするには、小文字を混ぜるかアンダーバーから始めないなどの工夫が必要です。
C言語の文法の話になりますが、例えば'bucl'と書くと上位8ビットが'b'、次の8ビットが'c'、
次の8ビットが'c'、一番下位の8ビットが'l'の値ということになります。
この説明だけでは今いち理解できないかもしれませんが、メッセージ定数の具体的な値はあまり関係ありません。
MyWindow::MessageReceived()関数とBButtonの構築の時に同じ値が必要になるので、最初に定数として定義しただけです。
さて次にBButtonの構築のしかたを見てみましょう。
MyView::MyView(BRect frame,const char* title,uint32 resizingMode,uint32 flags)
:BView(frame,title,resizingMode,flags)
{
m_button = new BButton(BRect(25,15,125,35),"theButton","初めてのボタン",new BMessage(BUTTON_CLICKED));
AddChild(m_button);
}
BButtonのもともとのコンストラクタの引数並びは次の様になっています。
BButton(BRect frame,const char* name,const char* label,BMessage* message, uint32 resizingMode = B_FOLLOW_LEFT | B_FOLLOW_TOP,uint32 flags = B_WILL_DRAW | B_NAVIGATABLE);
BButtonのコンストラクタ指定するものは、
ここで、BButtonに特有なのはlabelとmessageです。ほかは第4回で解説しているのでそちらを参考にしてください。
また、resizingModeとflagsに関してはボタン特有のデフォルト値が用意されているので、それを利用したほうが
他のアプリケーションとのインターフェースの統一のためによいでしょう。
labelについては見ただけでわかると思うので説明は省略しますが、問題はmessageです。
ここでBMessageをnewでインスタンスを作成した先のポインタを直接渡しています。
鋭い人なら、「ん?メモリリークちゃうん?」と思われるかもしれませんが、それはありません。
BMessageのoperator newはオーバーロードされていて、メモリを効率的に再利用するようになっています。
したがって、メモリのことはBeOS側に任せて、プログラムの作成側はメッセージのやり取りだけに集中できます。
このmessageという変数は、BButton側でコピーされないので、逆にローカル変数にBMessageを構築して、
そのポインタを渡すと、ボタンがメッセージを送信しようとすることには、メッセージのオブジェクトそのものが破棄されてしまっていて
メモリ保護違反でほぼ確実に落ちます。
ついでに運が悪いとOSも落ちます(^^;
実際落ちたことが何度か・・・
BMessageのコンストラクタでは、メッセージを送信するときのメッセージ定数を指定します。
実際には、メッセージの送信先から返事をもらったりとか出来るのですが、今回は使用していません。
このメッセージ定数はBMessageのwhatというフィールドに格納されて、メッセージの送信先で
どこから来たメッセージなのかを識別するのに使用します。
またメッセージの送信先は、デフォルトではボタンが所属しているウィンドウです。
送信先は変更が可能ですが、今回はデフォルトのウィンドウでメッセージの処理をしています。
その次のAddChild()関数で、ボタンをMyViewの子供として登録します。
このあたりの話は、第4回で解説しているので、そちらもごらんください。
さてその識別部分を見てみましょう。
void MyWindow::MessageReceived(BMessage* message)
{
BAlert* alert;
switch(message->what) // メッセージループ
{
case BUTTON_CLICKED:
alert = new BAlert("test","ボタンが押されました","OK");
alert->Go();
break;
default:
BWindow::MessageReceived(message);
break;
}
}
メッセージが送信されると、このMyWindow::MessageReceived()関数が呼ばれます。
この引数messageに送信されたメッセージが入ってきます。
このswitch文で、メッセージの内容を判定し、ボタンが押されたというメッセージなら、アラートボックスを表示するようにしています。
もしボタンが押されたというメッセージでなかったら、もともとのウィンドウのMessageReceived()関数を呼び出しています。
これは、ウィンドウが受け取るメッセージはボタンが押されたというメッセージだけでなくウィンドウのリサイズやドラッグといった、
システム的なメッセージも含まれるので、デフォルトの振る舞いを指定しておく必要があるのです。
さて、今回は再描画時のDraw()関数の再定義をしていませんが、ボタンはきちんと再描画されます。
これは、BButton側でDraw()関数を再定義してあって、ボタンを適切に再描画するようになっています。
これは今後紹介するBRadioButton(ラジオボタン)や、BCheckButton(チェックボタン)などにも言えることです。
さて、これで説明を終えますがいかがでしたでしょうか?
今後、MessageReceivedに見られるメッセージループはBeOSプログラムをする上で、ずっと使用する方法なので、
きっちり押さえておいてください。
|
|||
|
|