今回はメニューです。
メニューってのは、ウィンドウの上の方にあって、
「ファイル」とか「ヘルプ」とか書いてあるやつのことです。
メニューの作成には、BMenuBarとBMenuとBMenuItemというクラスが密接にかかわっています。
基本的には、BMenuBarの中にBMenuが複数あり、
BMenuの中にBMenuItemが複数あるといった、ネスト構造になっています。
// sample10.cpp
#include <Application.h> // BApplicationを使うために必要
#include <Window.h> // BWindowを使うために必要
#include <View.h> // BViewを使うために必要
#include <Alert.h> // BAlertを使うために必要
#include <MenuBar.h> // BMenuBarを使うために必要
#include <Menu.h> // BMenuを使うために必要
#include <MenuItem.h> // BMenuItemを使うために必要
// クラスの前方宣言
class MyApp;
class MyWindow;
class MyView;
// メッセージ定数
const uint32 QUIT_WITH_CHECK = 'qwch';
const uint32 PLEASE_HELP = 'plhe';
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(); // 終了が要求されたときに呼ばれる関数
private:
MyView* m_view;
};
class MyView : public BView
{
public:
MyView(BRect frame,const char *name,uint32 resizingMode,uint32 flags);
~MyView();
virtual void AttachedToWindow(); // ビューがウィンドウに取り付けられたとき(子供として登録された後)呼ばれる関数
virtual void MessageReceived(BMessage* _msg);
private:
void InitMenu();
BMenuBar* m_menubar; // メニューバー
};
MyApp::MyApp()
:BApplication("application/x-vnd.big56-MyApp")
{
m_win = new MyWindow(BRect(100,100,300,170),"メニュー",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->Quit();
return true;
}
MyView::MyView(BRect frame,const char* title,uint32 resizingMode,uint32 flags)
:BView(frame,title,resizingMode,flags)
{
}
MyView::~MyView()
{
}
void MyView::AttachedToWindow()
{
InitMenu();
}
// メニューの初期化
void MyView::InitMenu()
{
m_menubar = new BMenuBar(BRect(0,0,100,20),"theMenu");
BMenu* p_menu;
BMenu* p_menu2;
BMenuItem* p_menuitem;
p_menu = new BMenu("ファイル");
p_menuitem = new BMenuItem("終了",new BMessage(B_QUIT_REQUESTED),'Q');
p_menu->AddItem(p_menuitem); // 「終了」の追加
// 仕切り線の追加
p_menu->AddSeparatorItem();
// ネストした、「終了しようかな?」メニューの追加
p_menu2 = new BMenu("終了しようかな?");
p_menuitem = new BMenuItem("確認して終了",new BMessage(QUIT_WITH_CHECK));
p_menuitem->SetTarget(this);
p_menu2->AddItem(p_menuitem); // 「確認して終了」の追加
p_menuitem = new BMenuItem("確認せずに終了",new BMessage(B_QUIT_REQUESTED));
p_menu2->AddItem(p_menuitem);
p_menu->AddItem(p_menu2); // 「終了しようかな?」メニューの追加
m_menubar->AddItem(p_menu); // 「ファイル」全体の追加
p_menu = new BMenu("ダミー");
m_menubar->AddItem(p_menu); // ダミーメニューの追加
p_menuitem = new BMenuItem("ヘルプ",new BMessage(PLEASE_HELP));
p_menuitem->SetTarget(this);
m_menubar->AddItem(p_menuitem); // 「ヘルプ」の追加
AddChild(m_menubar); // メニューバー全体をビューの子供にする
}
// ビューの処理関数はチェックボックスが押されたときの処理を記述する
void MyView::MessageReceived(BMessage* message)
{
BAlert* alert;
int32 index;
switch(message->what)
{
case QUIT_WITH_CHECK:
alert = new BAlert("quit_alert","終了しますか?","はい","いいえ");
// アラートボックスを表示して、押されたボタンの番号を調べる
index = alert->Go();
// 「はい」が押されたらウィンドウにB_QUIT_REQUESTEDを送信
if (index == 0) Window()->PostMessage(B_QUIT_REQUESTED);
break;
case PLEASE_HELP:
(new BAlert("help","Please Help me","OK"))->Go();
break;
default:
BView::MessageReceived(message);
break;
}
}
int main(int argc,char** argv)
{
MyApp app;
app.Run();
return 0;
}
今回のアプリケーションの実行結果です。
前回のアプリより随分とシンプルになりました。
今回はメニューをつけただけなので、解説項目も少ないです。
ファイルメニューをクリックすると次の様になります。
ダミーメニューはメニューアイテム項目を設定していない、BMenuです。
クリックすると次の様に"<empty>"と表示されます。
ヘルプをクリックした時には・・・
今回は、メニューを初期化する関数InitMenu()関数が最重要です。
まずは、メニューバー、BMenuBarの構築についてです。
m_menubar = new BMenuBar(BRect(0,0,100,20),"theMenu");
BMenuBarの実際のコンストラクタは次の様になっています。
BMenuBar(BRect frame, const char *title, uint32 resizeMask = B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP, menu_layout layout = B_ITEMS_IN_ROW, bool resizeToFit = true);
指定するものは
となっています。
frameに関しては、resizeToFitをtrueに指定した場合、左上の座標しか気にしないようです。
ですから、メニューをウィンドウの端にくっつけないメニューも可能です。
さらに注目なのが第4引数のlayoutで、
BeOSでは縦にメニューを配置することも出来るようになっています。
layoutに指定できる引数は次の通りです。
| 値 | 説明 |
|---|---|
| B_ITEMS_IN_ROW | メニューを横に並べる |
| B_ITEMS_IN_COLUMN | メニューを縦に並べる |
| B_ITEMS_IN_MATRIX | 格子状に並べる(現在は設定不可) |
B_ITEMS_IN_COLUMNを指定すると、メニューは次のような感じになります。
B_ITEMS_IN_MATRIXは試しにやってみたところ、プログラム自体が落ちてしまいました。
そして次に、BMenuの構築です。今回のプログラムでは、一時領域として、BMenuのポインタ2つと
BMenuItemのポインタを宣言しています。
BMenu* p_menu; BMenu* p_menu2; BMenuItem* p_menuitem;
そして次に、BMenuを構築しています。
p_menu = new BMenu("ファイル");
BMenuの実際のコンストラクタです。
BMenu(const char *title, menu_layout layout = B_ITEMS_IN_COLUMN);
第1引数にはビューの名前、第2引数はメニューが横に伸びて行くのが、縦に伸びていくのかを指定します。
第2引数に関しては、BMenuItemのlayoutと同じ引数が指定出来ます。
デフォルトでは、B_ITEMS_IN_COLUMN(縦に伸びる)が指定されていますが、これはどういうことかというと、
「ファイル」メニューをクリックすると、「終了」などの項目は下の方に列挙されて出てきます。
これが、B_ITEMS_IN_ROWだと、右の方に列挙されて出てきます。
(実際には、「ファイル」と書かれているすぐ下から、右側に向かって列挙される)
言葉より画像にして出したほうが分かりやすいと思うので、
試しにnew BMenu("ファイル",B_ITEMS_IN_ROW)とすると
となります。見づらいですね。
次に、BMenuItemの構築です。
p_menuitem = new BMenuItem("終了",new BMessage(B_QUIT_REQUESTED),'Q');
BMenuItemの実際のコンストラクタです。
BMenuItem(const char *label, BMessage *message, char shortcut = 0, uint32 modifiers = 0);
指定するものは、
となっています。
labelについては説明不要だと思いますが、messageはクリックされた時に、
所属するウィンドウに送信するメッセージの種類を指定します。
送信先は、ボタンやチェックボックスと同様に、
SetTarget()関数で変えることが出来ます。
以前の繰り返しになりますが、SetTarget()関数はビューのコンストラクタ内で行うと失敗します。
shortcutには、いわゆるショートカットに使用するキーボードのキーを指定します。
この場合でいうと、ショートカットキーとQと同時に押すと、このメニューがクリックされた(終了させた)のと
同じ効果を出すことが出来ます。
(ちなみに終了はわざわざ指定しなくても、システム側でショートカット+Qが押されると、終了しますが
メニューの右側にショートカットキーが表示されるので、ユーザーに対して分かりやすいという効果があります。)
コマンドキーとは、Beメニューの[Preference]-[Menu]で設定出来るキーです。
私の場合、一般的な「コピー」や「貼り付け」などの動作をWindowsと同じにするために、
ショートカットキーをコントロールキーに割り当てています。
次に、modifierには、ショートカットに使用する装飾キー(ShiftやAltやCtrlなど)を指定します。
指定できる値は次の通りです。
| 値 | 説明 |
|---|---|
| B_SHIFT_KEY | シフトキー |
| B_CONTROL_KEY | コントロールキー(後述) |
| B_OPTION_KEY | オプションキー(Intel版はWIndowsキー) |
ここでコントロールキーとは、Ctrlをショートカットキーに指定していたら、Altキーで、
Altキーをショートカットキーに指定していたらCtrlキーのことになります。
Macの場合については手元にMacが無いのでわからないです。すみません。
これで、BMenuBarとBMenuとBMenuItemのインスタンスの構築が出来るようになりました。
あとは、これらを適切にAddItem()関数を使って、追加していくだけです。
最初の方でメニューはネスト構造といいましたが、BMenu::AddItem関数を何回か呼び出して、
BMenuItemを複数持つBMenuを作成して、そのBMenuをBMenuBar::AddItem関数で
BMenuBarに追加するといった感じになります。
言葉で説明すると難しいのですが、プログラムと実行結果を見比べれば、
雰囲気だけはわかっていただけるのではないかと思います。
AddItemについてもう少し詳しく説明すると、BMenuのAddItemは次の様になっています。
bool AddItem(BMenuItem *item); bool AddItem(BMenuItem *item, int32 index); bool AddItem(BMenu *menu, int32 index); bool AddItem(BMenu *menu, BRect frame);
BMenuItemを指定するバージョンと、BMenuを指定するバージョンがあります。
今回の「終了しようかな?」メニューなど、ネスト構造を作りたい場合は、
BMenuのポインタをAddItemで指定して実現させます。
あと、indexを指定するバージョンと指定しないバージョンがありますが、
indexを指定した場合は、メニューがindex番目に追加されますが、
指定しない場合はメニューが最後に追加されます。
あとは、仕切り線の追加についてです。
// 仕切り線の追加 p_menu->AddSeparatorItem();
BMenu::AddSeparatorItem()関数を呼び出すと、メニューの最後に仕切り線が追加されます。
この仕切り線は、先程の[Preference]-[Menu]のSeparator Styleで指定できるものと同じものが追加されます。
さて、次に「確認して終了する」を選んだ時の動作について説明します。
case QUIT_WITH_CHECK:
alert = new BAlert("quit_alert","終了しますか?","はい","いいえ");
// アラートボックスを表示して、押されたボタンの番号を調べる
index = alert->Go();
// 「はい」が押されたらウィンドウにB_QUIT_REQUESTEDを送信
if (index == 0) Window()->PostMessage(B_QUIT_REQUESTED);
break;
今までのBAlertを使用したコードとなんら変わりませんが、BAlert::Go()関数の戻り値を使用しています。
これは、第2回でもちょっと説明しましたが、BAlert:Go()では、
押されたボタンの番号が返ってきます。
今回は、左から「はい」、「いいえ」の順に並べていますので、「はい」が押されると、
0が返ってくるわけです。
「はい」が押された場合にのみ、B_QUIT_REQUESTEDを送信します。
「いいえ」が押されたときには何もしません。
今回はサンプルなので、このようなコードになりましたが、
実際、エディタなどで作成中の文章が保存されていない時の、終了確認などは、
BWindow::QuitRequested()関数内などでやるのが一般的だと思います。
case PLEASE_HELP:
(new BAlert("help","Please Help me","OK"))->Go();
break;
上の部分に「ヘルプ」ボタンが押されたときの処理が書いてあります。
今までやったBAlertと同じ処理なので説明は省略させていただきます。
さて、今回はメニューについて簡単に説明しました。
地味ですけど必ず書く部分です。
今回のサンプルで基本的なことは網羅していると思うので、書き方を忘れたときは
今回のサンプルを参考にしていただけると幸いです。
|
|||
|
|