メニュー項目の追加

多くのアプリは、ユーザーがメニューから項目を選択したことをきっかけとして動作します。Inkpodのメニューに項目を追加する方法について説明します。

メニュー拡張のタイミング

メニューへの操作は、inkpod.ui.addUIExtensionListener() で登録した関数内で行います。メニューを拡張すべきタイミングで、UIExtensionEventが発生し登録した関数が呼ばれます。他のタイミングで操作した場合、Inkpodがメニューを再構築するとき、追加したメニュー項目が消えたままになってしまいます。

アクション定義

アクションとは、ユーザーが実行可能なInkpod上での操作の事です。メニュー項目を選択したとき実行される処理や、そのアクションがユーザーに対してどのように表示されるか(メニュー項目のラベル等)なども、アクションによって決定されます。アプリに対してユーザーが行う操作も、アクションです。

メニュー項目の追加は、アクション定義の登録と、メニュー項目定義の登録の2段階で行います。まずアクション定義を登録し、その識別子と結びついたメニュー項目定義を追加します。

アクション定義オブジェクトをinkpod.ui.actions.addAction() で追加することで、アクションを登録します。アクション定義には、以下のようなプロパティが設定できます。

id (必須)アクションの識別子です。Inkpod全体で一意である必要があります。プラグイン名などをプレフィックスとしてつけることをおすすめします。
text (必須)メニュー項目に表示されるテキストです。
keyアクセラレータキーです
modifierアクセラレータキーの修飾子です。設定できる値は、SHIFT, CTRL, META, ALTの各文字列です。複数指定する場合は、","(コンマ)で区切ってください。
mnemonicメニューのニーモニックとして使用する1文字の文字です。
actionメニュー選択時に実行される関数オブジェクトです。または、以下のプロパティを持つオブジェクトです。
actionPerformedメニュー選択時に実行される関数オブジェクト
updateStateアクションの状態を更新する必要があるとき呼ばれる関数オブジェクト

actionプロパティで設定する関数オブジェクトは以下のような関数です。この関数に、アクションが実行されたときの処理を記述します。

function (frame, event) {
    
}
第1引数frameは、jp.carabiner.inkpod.pi.PInkpodFrameオブジェクトで、ユーザーが選択したメニュー項目が属するフレームです。第2引数eventは、java.awt.event.ActionEventオブジェクトです。

アクション定義からはjavax.swing.Actionオブジェクトが、メニュー項目定義からはjavax.swing.JMenuItemオブジェクトが、フレーム毎に生成されます。アプリから、javax.swing.Actionオブジェクトを操作することはありますが、javax.swing.JMenuItemを意識することはありません。

シンプルなメニュー項目の追加

シンプルなメニュー項目を追加するサンプルです。簡単なメッセージを表示する機能をメニューに追加します。

function extendUI(event) {
    inkpod.ui.actions.addAction({
        id     : "SAMPLE",
        text   : "SAMPLE",
    });
    
    inkpod.ui.actions.addAction({
        id     : "SAMPLE_HELLO",
        text   : "Hello",
        action : function(frame, event) {
            inkpod.showInfo("Hello Inkpod world!");
        }
    });
    
    var menu = inkpod.ui.menuBar.addMenu("SAMPLE");
    menu.addMenuItem("SAMPLE_HELLO");
}

function init()
{
    inkpod.ui.addUIExtensionListener(extendUI);
}

function dispose()
{
}

plugin = {
    init : init,
    dispose : dispose
}

まず、SAMPLEと、SAMPLE_HELLO という2つのアクションを登録します。SAMPLEはメニューバーに登録するメニューに対応するアクションで、SAMPLE_HELLOは、そのメニューの下に追加するメニュー項目に対応するアクションです。

アクションを登録したら、次に、実際のメニュー項目を追加します。inkpod.ui.menuBar.addMenu("SAMPLE"); で、メニューバーに"SAMPLE"というメニューが追加されます。addMenu() は、新しく作られたメニューを返すので、そのメニューに"SAMPLE_HELLO"アクションと結びついたメニュー項目を追加しています。

1つのアクションは、複数のメニュー項目と結びつけることができます。1つのアクション定義からは、1つのフレームごとにjava.awt.Actionオブジェクトが1つ生成されます。複数のメニュー項目と結びつけれらていても、javax.swing.Actionオブジェクトは1つのフレームあたりで1つです。

サンプルでは、メニューバーに追加していますが、他にもコンテキストメニューに登録することもできます。

inkpod.ui.menuBarメニューバーjp.carabiner.inkpod.pi.wiget.PDefaultMenuBar
inkpod.ui.objectPopupMenuオブジェクトを右クリックしたとき表示されるコンテキストメニューjp.carabiner.widget.PopupMenu
inkpod.ui.backgroundPopupMenuマップの背景を右クリックしたとき表示されるコンテキストメニューjp.carabiner.widget.PopupMenu

有効/無効状態付きメニュー項目の追加

メニューの中には、特定の条件がそろっているときしか実行できないものがあります。その場合、メニュー項目が使用可能なときは有効状態にし、そうでなければ無効状態にすると、ユーザーにわかりやすくなります。メニュー項目の状態は、アクションに状態を持たせて、アクションの状態を変更することで間接的に行います。

以下は、選択中のオブジェクトのテキストを表示する機能を、「オブジェクト」メニューに追加するサンプルです。選択中のオブジェクトがなければ実行できないので、その場合、メニュー項目の状態を無効にするようになっています。

function extendUI(event) {
    inkpod.ui.actions.addAction({
        id     : "SHOW_OBJECT_TEXT",
        text   : "テキストを表示",
        action : {
            actionPerformed : function (frame, event) {
                var model = frame.model;
                inkpod.showInfo(model.singleSelectedObject.text);
            },
            updateState : function (frame, action) {
                var model = frame.model;
                action.enabled = (model.singleSelectedObject != null);
            }
        }
    });
    
    inkpod.ui.menuBar.objectMenu.addSeparator();
    inkpod.ui.menuBar.objectMenu.addMenuItem("SHOW_OBJECT_TEXT");
}

function init()
{
    inkpod.ui.addUIExtensionListener(extendUI);
}

function dispose()
{
}

plugin = {
    init : init,
    dispose : dispose
}

シンプルなメニュー項目と異なる点は、アクションの登録で、actionプロパティに、actionPerformedプロパティと、updateStateプロパティを持ったオブジェクトを設定する点です。actionPerformedプロパティには、メニューが選択されたときに呼ばれる関数を指定します。updateStateプロパティには、メニューの状態を更新する必要があるときに呼ばれる関数を指定します。updateStateは、以下のような形の関数になります。

function (frame, action) {
}

第1引数のframeは、jp.carabiner.inkpod.pi.PInkpodFrame オブジェクトで、アクションが関連づけられたフレームです。第2引数のactionは、java.awt.Action オブジェクトです。java.awt.Actionオブジェクトの、enableプロパティを変更すると、メニュー項目の有効/無効の状態が変化します。

Inkpodは、メニュー項目の状態を更新する必要があるとき、このupdateState関数を呼び出します。呼び出されたタイミングで、メニュー項目が使用可能か調べて、Actionオブジェクトのenableプロパティの値を更新します。サンプルでは、frameから現在のモデルを取得し、現在選択されているオブジェクトを調べています。オブジェクトが選択されていればactionのenableのプロパティをtrueに、そうでなければfalseにします。

選択状態付きのメニュー項目の追加

有効/無効状態とは別に、選択状態を持つメニュー項目もあります。これも、アクションの状態を変更することでメニュー項目の状態を変更します。ただし、通常のメニュー項目は選択状態を表現する機能を持ちません。選択状態を表示させるには、メニュー項目定義の方でも、選択状態を表現可能なメニュー項目を追加する必要があります。

以下のサンプルは、アプリのある内部状態に従って、ヘルプメニューに追加されたメニュー項目の選択状態をトグルするものです。選択状態になると、メニュー項目の先頭にチェックが入ります。

var toggleStatus = false;

function extendUI(event) {
    inkpod.ui.actions.addAction({
        id     : "SAMPLE_TOGGLE_MENUITEM",
        text   : "選択状態のテスト",
        action : {
            actionPerformed : function (frame, event) {
                toggleStatus = !toggleStatus;
                frame.showInfo(toggleStatus);
            },
            updateState : function (frame, action) {
                action.putValue(javax.swing.SELECTED_KEY, toggleStatus);
            }
        }
    });
    
    inkpod.ui.menuBar.helpMenu.addSeparator();
    inkpod.ui.menuBar.helpMenu.addCheckBoxButtonMenuItem("SAMPLE_TOGGLE_MENUITEM");
}

function init()
{
    inkpod.ui.addUIExtensionListener(extendUI);
}

function dispose()
{
}

plugin = {
    init : init,
    dispose : dispose
}

addMenuItem() の代わりに、addCheckBoxButtonMenuItem() を使用することで、チェック付きのメニュー項目を作れます。

有効/無効状態の制御では、javax.swing.Actionオブジェクトのenableプロパティを変更しましたが、選択状態の場合はjavax.swing.Action.SELECTED_KEYをキーとする、アクションキーを変更します。詳細は、javax.swing.Action のjavadocを参照してください。

関連するクラス/インタフェース