ユーザ独自のメニューの作成方法

 
 
 

すべての自己インストール プラグインと同様、実装作業時にプラグインのインストール処理を行う必要があります。 メニューをインストールするには、登録対象のメニュー、メニューのリンク先、およびプラグインで実装されるコンポーネント(項目)を示すための、特殊なコールバックを使用する必要があります。

メニューのコンポーネントは、ユーザ メニューに表示される項目、および必要な特殊カスタム関数(コールバック メニュー項目の場合)で構成されます。

カスタム メニューをインストール(登録)するには、各メニュー プラグイン項目の名前、および関連するプラグイン項目(自己インストール コマンドやオペレータなど)を指定する XSILoadPlugin コールバックを組み込む必要があります。 各メニューのプラグイン項目は、次の情報を含む RegisterMenu(PluginRegistrar) メソッドを個別に呼び出して登録されます。

メニュー アンカー ポイントについて

「メニューの表示場所」 に概説されているように、メニュー アンカー ポイントとは、Softimage ユーザ インターフェイス内でメニュー プラグイン項目が表示される場所のことを意味します。 ユーザ メニューのプラグイン項目を挿入できる場所としては、次の 3 箇所があります。

  • ツールバー メニュー — ツールバー ボタンからドロップダウンされるメニュー(たとえば、siMenuTbGetPrimitiveSurfaceID メンバ(siMenuAnchorPoints 列挙型)は、[モデル](Model)ツールバーの[取得](Get)[プリミティブ](Primitive)[サーフェイス](Surface)に対応します)。

  • ウィンドウ メニュー — 固定されたメニュー バーからドロップダウンされるメニュー(たとえば、siMenuMainFileImportIDメンバ(siMenuAnchorPoints 列挙型)は、[ファイル](File)メニューの[読み込み](Import)サブメニューに対応します)。

  • コンテキスト メニュー: コンテキストに応じて、Scene Explorer または 3D ビューのいずれかで右クリックしたときに表示されるメニュー(たとえば、siMenuSELayersContextID メンバ(siMenuAnchorPoints 列挙型)は、Scene Explorer の[スコープ]メニューが[レイヤ]に設定されているときに表示されるポップアップ メニューに対応します)。

siMenuAnchorPoints 列挙型の形態を分析する

始めに、siMenuAnchorPoints 列挙型のすべてのメンバは、siMenu で開始し、ID で終了します。 各メンバは、アンカー ポイントのタイプを次の規則(ボールド体)のいずれかを基準に識別します。

また、ビュー コンテキストに対応しているコンテキスト メニューには、次の 2 種類があります。

  • Scene Explorer: siMenuSE___ContextID

  • 3D ビュー: siMenu3DView___ContextID

残りの列挙名(___)には、サブメニューのパスが含まれます。 たとえば、siMenuTbCreateRigidBodyConstraintID は、次のように分類されます。

数種類のアンカー ポイントに同じメニューを追加する

さまざまなメニューに同じメニュー プラグイン項目を追加する必要のある場合があります。 たとえば、プリミティブの新しいタイプを作成するカスタム コマンドを作成した場合、[アプリケーション][コマンド]および[モデル][作成] [ポリゴンメッシュ]。(Model > Create > Poly. Mesh) ウィンドウ メニュー、[取得](get)[プリミティブ](Primitive)[ポリゴン メッシュ](Polygon Mesh)ツールバー メニューおよび 1 つ以上のコンテキスト メニューにコマンド メニュー エントリを追加できます。

エントリをいくつかのポイントに追加するには、メニュー アンカー ポイントごとに 1 つの RegisterMenu(PluginRegistrar) 呼び出しを追加する必要がありますが、それぞれのアンカー ポイントに同じメニュー プラグイン項目を使用します。 たとえば、プラグインに MooMooCow という名前のカスタム コマンド プラグイン項目が実装されていて、そのコマンドを Scene Explorer の[アプリケーション](Application)[設定](Preferences)ウィンドウ メニューおよび[設定](Preferences)コンテキスト メニューに表示したい場合は、XSILoadPlugin コールバックには、次のような呼び出しを含める必要があります。

// Register the Moo command 
	in_reg.RegisterCommand( "MooMooCow", "Moo" );
	
	// The Moo command will appear in the Application > Preferences
	// menu as well as the (SE) Preferences contextual menu
	in_reg.RegisterMenu( siMenuMainApplicationPreferencesID, "MooMenu", true );
	in_reg.RegisterMenu( siMenuSEPreferenceContextID, "MooMenu", false );

その後、MooMenu_Init コールバックで、メニュー プラグイン項目を一度だけ定義(そのメニュー プラグイン項目に関連付けられたメニュー項目またはエントリを指定)すれば済みます。

注:

このサンプルからわかるように、別々の場所に表示されるメニュー プラグイン項目どうしが必ずしも同じ形態のものである必要はありません。 このサンプルでは、[アプリケーション](Application)[設定](Preferences)ウィンドウ メニューに表示されるメニューはドロップダウン(2次)メニューとして表示されるのに対し、[設定](Preferences)コンテキスト メニューに表示されるメニューはメイン メニュー レベルに表示されます。

メニューの外観を調整する

ユーザ メニューの外観を定義するオプションとしては、次のようなものが用意されています。

  • メニュー項目(エントリ)を単一メニュー アンカー ポイントから離れたサブメニュー(2 次メニュー)に表示するか、それともアンカー ポイントと同じレベルに表示するかを、ユーザ自身で決めることができます。「ネスト構造: サブメニューを使用する」を参照してください。

  • 各エントリの外観(および関連している場合は、どのラベルでサブメニューをある程度まで示すか)を定義して、アクセス キーを割り当てることができます。「名前付け: ラベルを使用する」を参照してください。

  • 一部のメニュー項目を残りのメニュー項目と区別するには、セパレータ(項目どうしを区切る横線)を使用します。「系統的な編成: セパレータを使用する」を参照してください。

ヒント:

セパレータを明示的に追加する必要があるのは、単独メニューの内部のメニュー項目どうしを区切る場合だけです。これは、単独メニューは他のメニュー プラグイン項目またはネイティブ Softimage メニュー項目からすでに切り離されているためです。

ネスト構造: サブメニューを使用する

多くの場合、ユーザ メニューは既存メニューの終端部に表示されます。 エントリが 2、3 個しかない場合は、エントリを同じメニューに表示します。 同じメニューに表示しない場合、サブメニューを使用すれば、多数のエントリを容易に系統立てて編成することができます。

フラット エントリ(同一メニュー)

サブメニュー(ドロップダウン メニュー)

   

DisplayAsSubmenu パラメータ(PluginRegistrar.RegisterMenu メソッドの)でメニュー プラグイン アイテムに登録しているときに、メニュー プラグイン アイテムがアンカー メニュー内のフラットなリストとして表示するか、または次のドロップダウン メニューとして表示するかどうかを指定します。

// To register a flat menu (no submenu):
	in_reg.RegisterMenu( siMenuTbDeformID, "AnchorPoint", false );

	// To register a submenu:
	in_reg.RegisterMenu( siMenuTbDeformID, "AnchorPoint", true );
	// ... or the equivalent (true is the default):
	in_reg.RegisterMenu( siMenuTbDeformID, "AnchorPoint" );
注:

上のステートメントの例を挙げて、上の表のサブメニュー(ドロップダウン メニュー)サンプルを基本コードで生成する方法を示します。ここで 1 つ注意すべき点は、メニュー プラグイン項目の名前がサブメニューのアンカーポイントとして表示されることです。

もう 1 つ興味深い点は、サブメニューをいくつでもネストできることです。ただし、メニュー アンカー ポイントは 1 つしか必要ありません。他の従属サブメニューは、最上位レベル メニュー プラグインの Init コールバック内部で定義されます。この定義の際に使用される AddItem 呼び出しの Style パラメータには、値 siMenuItemSubmenu を指定します。

function NestedMenus_Init( in_context )
	{
		// Get the menu object from the Context input
		var oTopMnu = in_context.Source;

		// Add some regular menu items
		oTopMnu.AddItem( "Regular Item1", siMenuItem );
		oTopMnu.AddItem( "Regular Item2", siMenuItem );

		// Add the submenu item 
		var oSubLevel1 = oTopMnu.AddItem( "SubMenu Demo Level1", 
			siMenuItemSubmenu )
		oSubLevel1.AddItem( "SubLevel1 ItemA", siMenuItem );
		oSubLevel1.AddItem( "SubLevel1 ItemB", siMenuItem );

		// Nest another submenu item 
		var oSubLevel2 = oSubLevel1.AddItem( "SubMenu Demo Level2", 
			siMenuItemSubmenu )
		oSubLevel2.AddItem( "SubLevel2 ItemA", siMenuItem );
		oSubLevel2.AddItem( "SubLevel2 ItemB", siMenuItem );
		oSubLevel2.AddItem( "SubLevel2 ItemC", siMenuItem );
		oSubLevel2.AddItem( "SubLevel2 ItemD", siMenuItem );

		// ... and another ...
		var oSubLevel3 = oSubLevel2.AddItem( "SubMenu Demo Level3", 
			siMenuItemSubmenu )
		oSubLevel3.AddItem( "SubLevel3 ItemA", siMenuItem );
		oSubLevel3.AddItem( "SubLevel3 ItemB", siMenuItem );
		oSubLevel2.AddItem( "SubLevel2 ItemC", siMenuItem );

		// ... etc.

		// Finish with success notification
		return true;
	}

このサンプルでは、次のタイプのネストが生成されます。

メニュー オブジェクトに各サブメニューを追加することにより、AddItem(Menu)呼び出しから返されたメニュー オブジェクトが取得され、別のサブメニューがネストされます。 上に挙げたサンプルにおいて、oTopMnu オブジェクトを使用して各 SubLevel サブメニューを追加した場合は、代わりに 3 つのサブメニューが同一階層として作成されます。

名前付け: ラベルを使用する

フラット メニューの場合、メイン エントリ ポイントの名前は表示されませんが、トップ サブメニューの登録(RegisterMenu(PluginRegistrar))に使用した名前またはサブメニューの追加(AddItem(Menu))に使用した名前は、アンカー ポイントの名前として表示されます(サブメニューの作成の詳細については、「ネスト構造: サブメニューを使用する」を参照)。

メニュー項目(セパレータ以外)に Label パラメータを使用して項目名を定義します(AddItem(Menu)、AddCommandItem(Menu)またはAddItem(Menu))。

// Add an entry that appears as 'Daffy Duck' and calls a special 
	// function defined in the same plug-in file as 'QuackQuackQuack':
	oMnu.AddCallbackItem( "Daffy Duck", "QuackQuackQuack" );

	// Add an entry that appears as 'Launch Explorer' and invokes the
	// 'OpenXSIExplorer' command:
	oMnu.AddCommandItem( "Launch Explorer", "OpenXSIExplorer" );

	// Alternative ways to do the same things:
	var cbk = oMnu.AddItem( "Daffy Duck", siMenuItem );
	cbk.Callback = "QuackQuackQuack";

	var cmd = oMnu.AddItem( "Launch Explorer", siMenuItem );
	cmd.Command = "OpenXSIExplorer";

アクセス キーを割り当てる

またラベルに含まれる英字の1つをアクセス キーとして割り当てるには、その英字の前にアンパサンド(&)を挿入します。 アクセス キーは、下線付きで表示される英字です。アクセス キーを押せば、関連するコールバックまたはコマンドをアクティブにすることができます。 たとえば以下のようになります。

// Assign 'k' as the access key for the 'Daffy Duck' menu entry:
	oMnu.AddCallbackItem( "Daffy Duc&k", "QuackQuackQuack" );

	// Assign 'x' as the access key for the 'Launch Explorer' menu entry:
	oMnu.AddCommandItem( "Launch E&xplorer", "OpenXSIExplorer" );
ヒント:

Windows XP 上では、デフォルトではアクセスキーが指定されません(下線付きで表示されません)。 アクセス キーを表示するには、[Alt]キーを押します。

系統的な編成: セパレータを使用する

特定のメニュー項目を他のメニュー項目と見分けがつくようにする場合があります。 AddSeparatorItem(Menu) メソッドが用意されています。この特別なメソッドでは水平線を挿入できます。 たとえば以下のようになります。

// Of five items, separate the third and fourth
		oSepMnu.AddItem( "Menu Item 1", siMenuItem );
		oSepMnu.AddItem( "Menu Item 2", siMenuItem );

		oSepMnu.AddSeparatorItem();

		oSepMnu.AddItem( "Menu Item 3", siMenuItem );
		oSepMnu.AddItem( "Menu Item 4", siMenuItem );

		oSepMnu.AddSeparatorItem();

		oSepMnu.AddItem( "Menu Item 5", siMenuItem );
注:

項目が表示される順序は、それらの項目の並び順に応じて決定されます。 詳細については、「順序付け: メニュー項目の並び順を指定する」を参照してください。

順序付け: メニュー項目の並び順を指定する

最終的なメニュー内でのメニュー項目の並び順は、メニュー項目が定義された順序に応じて決定されます。 これは、すべてのメニュー項目(コマンド エントリ、コールバック エントリ、サブメニュー、およびセパレータ)に当てはまります。 前のセクションで挙げたサンプル(「系統的な編成: セパレータを使用する」)と次のサンプルを比較しましょう。

// Move the lines out of order
		oSepMnu.AddItem( "Menu Item 3", siMenuItem );
		oSepMnu.AddItem( "Menu Item 4", siMenuItem );

		oSepMnu.AddItem( "Menu Item 1", siMenuItem );
		oSepMnu.AddItem( "Menu Item 2", siMenuItem );

		oSepMnu.AddSeparatorItem();
		oSepMnu.AddSeparatorItem();

		oSepMnu.AddItem( "Menu Item 5", siMenuItem );

コールバックまたはコマンドの使用は必須ですか?

独自のカスタム コマンドを定義していて、コマンドの実装が本当に必要かどうか、また特殊なコールバック関数を定義せずに済むかどうか迷った場合は、次の点を考慮してください。

  • コマンドは Softimage コマンド マップに追加されますが、コールバックは追加されません。 これには、コールバックにホットキーを割り当てることができないという意味合いが含まれています。

  • コールバックは、そのコールバックが定義されているプラグイン モジュールの外部では再利用できません。それに対して、コマンドはいったん登録されてから明示的にアンロードされるまでは、Softimage 全体で利用できます。

  • Softimage においてコマンドは他と重複する名前を付けることができないのに対して、コールバックには Softimage の別のコマンドと同じ名前を使用できます(万一クラッシュが発生した場合は、コールバックが起動されます)。

  • コールバックがより簡単に実装されます: AddItem(Menu) 呼び出しで指定された 1 つのコールバックの中に、すべての機能が含まれているためです。 コマンドは AddCommandItem(Menu)呼び出しで識別するだけでなく、XSILoadPlugin コールバックで登録し、Init コールバックで定義した後、Execute コールバックで実装する必要があります。

ユーザ メニューにフィルタを追加する

一部の操作では、最小数の入力オブジェクトや特定のプリミティブ タイプのオブジェクトなど、特定のタイプの入力に絞り込む必要があります。 関数またはコマンドに必要な条件と一致するフィルタを追加して、選択したオブジェクトが条件と一致しない場合に、メニューが無効になる(淡色表示される)ようにします。

フィルタは MenuItem オブジェクトに適用されます。これは、登録されたフィルタ(フィルタの名前または Filter オブジェクト)を識別して適用します。このフィルタは、MenuItem.Filter プロパティで使用するフィルタです。

例: カスタム メニューでポリゴン メッシュ フィルタを使用する

このサンプルは、カスタム メニュー用フィルタの指定方法を例証したものです。 コードは Init コールバックで定義されます。 ヌルまたはサーフェイス メッシュ オブジェクトが選択された状態で、メニュー項目のどれかを起動しようとすると、メニューは無効になります。 ユーザがポリゴン メッシュ オブジェクトを選択するとすぐに、メニューはアクセス可能になります。

// Get the menu object from the Context input
	var oFMnu = in_context.Source;
	oFMnu.Filter = "polygonmesh";

	// Add three callback items but only enable them for
	// polygon meshes.
	oFMnu.AddCallbackItem( "Punch Out Hole", "PunchOutHole" );
	oFMnu.AddCallbackItem( "Remove Corner", "RemoveCorner" );
	oFMnu.AddCallbackItem( "Smooth Curves", "SmoothCurves" );