Adobe AIRで作るデスクトップアプリケーション

第13回メニューの実装

AIR APIがサポートするメニュー

デスクトップアプリケーションには、インタフェースの一部として独自のメニューを備えているものが多くあります。AIRアプリケーションにもメニューAPIが用意されており、OSネイティブのメニューを利用できます。今回はメニューの実装方法について解説します。

AIRアプリケーションがサポートしているメニューには次のものがあります。

  • アプリケーションメニュー(Mac OS Xのみ)
  • ウィンドウメニュー(Windowsのみ)
  • Dockアイコンメニュー(Mac OS Xのみ)
  • システムトレイアイコンメニュー(Windowsのみ)
  • コンテキストメニュー
  • ポップアップメニュー

これらのメニューは表示場所こそ違うものの、作成方法はすべて共通です。例えば、同じ内容のメニューをアプリケーションメニューとDockメニューで使いたい、という場合でも簡単に実装できます。

メニューの構成

では、メニューの構成について見ていきましょう。メニューの作成には、NativeMenuクラスとNativeMenuItemクラス(いずれもflash.displayパッケージ)を使用します。メニューの個々のアイテムにあたるNativeMenuItemオブジェクトと、それらのコンテナとなるNativeMenuオブジェクトで1セットです。サブメニューが必要であれば、その分だけセットを作ります。図にすると次のようなイメージです。

メニューの構成
メニューの構成

NativeMenuItemオブジェクトは通常のコマンドとして使う他に、必要に応じてセパレータにしたり、サブメニューにすることができます。

なお、アプリケーションメニューおよびウィンドウメニューでは、ルートとなるNativeMenuオブジェクトの直下をサブメニューにする必要があります。いきなりコマンドやセパレータを追加しないよう注意してください。

メニューの作成とアサイン

実際にメニューを作成してみましょう。Mac OS Xではアプリケーションメニュー、Windowsではウィンドウメニューとして表示されるようにします。ウィンドウメニューはシステムクロームでのみ有効です。次のサンプルでは[File]メニューを作成し、その下に[Exit]メニューを追加します。

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" applicationComplete="buildMenu()">
  <mx:Script>
    <![CDATA[
      private function buildMenu():void {
        var rootMenu:NativeMenu = new NativeMenu();
        var fileMenu:NativeMenuItem = rootMenu.addSubmenu(new NativeMenu(), "File");
        var exitMenu:NativeMenuItem = new NativeMenuItem("Exit");
        fileMenu.submenu.addItem(exitMenu);
        if (Shell.supportsMenu) {
          Shell.shell.menu = rootMenu;
        } else if (NativeWindow.supportsMenu) {
          stage.nativeWindow.menu = rootMenu;
        }
      }
    ]]>
  </mx:Script>
</mx:WindowedApplication>

まず、メニューのルートとなるNativeMenuオブジェクトを作成し、addSubmenu()メソッドを使って"File"というサブメニューを追加しています。addSubmenu()の第1パラメータはサブメニューとして使うNativeMenuオブジェクトです。サブメニューもこれから定義するので、新規オブジェクトを渡しています。第2パラメータがメニューのラベルです。

サブメニューの追加は、addSubmenu()メソッドの代わりにaddItem()メソッドでも可能です。その場合は、NativeMenuItemオブジェクトのsubmenuプロパティにサブメニューとなるNativeMenuオブジェクトを指定します。

var fileMenu:NativeMenuItem = new NativeMenuItem("File");
fileMenu.submenu = new NativeMenu();
rootMenu.addItem(fileMenu);

次に、[File]メニューの下に表示される[Exit]メニューを作成しています。"Exit"というラベルのNativeMenuItemオブジェクトを作成して[File]メニューのsubmenuプロパティに追加するだけです。

最後に、作成したメニューをアプリケーションにアサインします。Mac OS XのアプリケーションメニューであればShell.shell.menuプロパティに、Windowsのウィンドウメニューであればstage.nativeWindow.menuプロパティにアサインする必要があります。この判別に利用しているのがShell.supportsMenuプロパティとNativeWindow.supportsMenuプロパティです。前者はアプリケーションメニューをサポートしている場合にtrueとなり、後者はウィンドウメニューをサポートしている場合にtrueとなります。これでメニューが表示されるようになりました。

Mac OS XのアプリケーションメニューとWindowsのウィンドウメニュー
Mac OS XのアプリケーションメニューとWindowsのウィンドウメニュー

様々なタイプのメニュー

冒頭で紹介したように、アプリケーションメニューやウィンドウメニュー以外でも同様のメニューを表示できます。先のサンプルのアサイン部分だけを変更して確認してみましょう。Mac OS XのDockアイコンメニューとWindowsのシステムトレイアイコンメニューに表示するには、次のように変更します。

if (Shell.supportsDockIcon) {
  var dockIcon:DockIcon = Shell.shell.icon as DockIcon;
  dockIcon.menu = rootMenu;
} else if (Shell.supportsSystemTrayIcon) {
  var sysTrayIcon:SystemTrayIcon = Shell.shell.icon as SystemTrayIcon;
  sysTrayIcon.bitmaps = [new BitmapData(16, 16, false, 0xff0000)];
  sysTrayIcon.menu = rootMenu;
}

どちらの場合も結果的にはShell.shell.icon.menuプロパティにアサインするのですが、サポートしているアイコンのタイプによってShell.shell.iconプロパティの値が異なる点に注意してください。Dockアイコンの場合はDockIcon.menuプロパティ、システムトレイアイコンの場合はSystemTrayIcon.menuプロパティにメニューをアサインすることになります。この判別にはShell.supportsDockIconプロパティとShell.supportsSystemTrayIconプロパティを使います。前者がtrueの場合はDockアイコンをサポートしており、後者がtrueの場合はシステムトレイアイコンをサポートしています。なお、ここではシステムトレイアイコンのイメージとして16×16の赤い矩形を指定しています。

Mac OS XのDockアイコンメニューとWindowsのシステムトレイアイコンメニュー
Mac OS XのDockアイコンメニューとWindowsのシステムトレイアイコンメニュー

コンテキストメニューとして使いたい場合は、任意のインタラクティブオブジェクトのcontextMenuプロパティにアサインするだけです。すると、そのオブジェクト上でコンテキストメニューを表示したときに指定のメニューが表示されるようになります。

myMovieClip.contextMenu = rootMenu;

ポップアップメニューとして使いたい場合はこれまでと異なり、NativeMenu.display()メソッドを使います。第1パラメータにはメニューを表示するステージを、第2/第3パラメータにはステージを基準としたメニューの座標を指定します。

rootMenu.display(stage, 32, 32);

ただし、確認した範囲ではMac版での座標指定は無視され、マウス座標に表示されます。

セパレータの挿入

メニューのアイテムの間にはセパレータを挿入できます。セパレータもNativeMenuItemオブジェクトです。NativeMenuItemコンストラクタの第2パラメータにtrueを指定すると、そのアイテムはセパレータになります。

var separator:NativeMenuItem = new NativeMenuItem("", true);
fileMenu.submenu.addItem(separator);

メニュー選択時のイベント

ここまでメニューの表示について見てきましたが、表示するだけではメニューの役割を果たしません。選択されたときに発生するイベントを受けて、何らかのアクションを起こす必要があります。メニューの選択に関するイベントは次の2つがあります(flash.eventsパッケージ⁠⁠。

イベント発生タイミング
Event.DISPLAYINGメニューが表示される直前
Event.SELECTメニューが選択された時

Event.DISPLAYINGは、ユーザがメニューをクリックして表示しようとした時に発生しますが、まだメニューは表示されていない状態です。例えば、⁠最近開いたドキュメント」のように動的に内容を変更したいメニューに利用できます。実際に選択された時はEvent.SELECTが発生します。次のサンプルは、[File]メニューから[Exit]を選択するとアプリケーションを終了します。

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" applicationComplete="buildMenu()">
  <mx:Script>
    <![CDATA[
      private function buildMenu():void {
        var rootMenu:NativeMenu = new NativeMenu();
        var fileMenu:NativeMenuItem = rootMenu.addSubmenu(new NativeMenu(), "File");
        var exitMenu:NativeMenuItem = new NativeMenuItem("Exit");
        exitMenu.addEventListener(Event.SELECT, selectExitHandler);
        fileMenu.submenu.addItem(exitMenu);
        if (Shell.supportsMenu) {
          Shell.shell.menu = rootMenu;
        } else if (NativeWindow.supportsMenu) {
          stage.nativeWindow.menu = rootMenu;
        }
      }
      private function selectExitHandler(e:Event):void {
        Shell.shell.exit();
      }
    ]]>
  </mx:Script>
</mx:WindowedApplication>

メニューのオン/オフ

NativeMenuItemクラスには、checkedプロパティとenabledプロパティがあります。前者はメニューのアイテムがチェックされているかどうかを示すもので、trueにするとアイテムにチェックマークが付きます。後者はメニューのアイテムが現在選択可能かどうかを示すもので、falseにするとアイテムがグレーアウトの状態になります。

次のサンプルは、先のコードに[Lock]メニューを追加しています。[Lock]メニューを選択する度にチェックの有無が切り替わり、チェックが付いているときは[Exit]メニューを無効にします。

確認した範囲では、Windowsで一旦chekedプロパティを設定するとその後値が変更できません。
<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" applicationComplete="buildMenu()">
  <mx:Script>
    <![CDATA[
      private var _exitMenu:NativeMenuItem;
      private function buildMenu():void {
        var rootMenu:NativeMenu = new NativeMenu();
        var fileMenu:NativeMenuItem = rootMenu.addSubmenu(new NativeMenu(), "File");
        var lockMenu:NativeMenuItem = new NativeMenuItem("Lock");
        lockMenu.addEventListener(Event.SELECT, selectLockHandler);
        fileMenu.submenu.addItem(lockMenu);
        _exitMenu = new NativeMenuItem("Exit");
        _exitMenu.addEventListener(Event.SELECT, selectExitHandler);
        fileMenu.submenu.addItem(_exitMenu);
        if (Shell.supportsMenu) {
          Shell.shell.menu = rootMenu;
        } else if (NativeWindow.supportsMenu) {
          stage.nativeWindow.menu = rootMenu;
        }
      }
      private function selectLockHandler(e:Event):void {
        e.target.checked = !e.target.checked;
        _exitMenu.enabled = !e.target.checked;
      }
      private function selectExitHandler(e:Event):void {
        Shell.shell.exit();
      }
    ]]>
  </mx:Script>
</mx:WindowedApplication>
メニューのチェックや無効化が可能
メニューのチェックや無効化が可能

メニューにデータを設定する

NativeMenuItemクラスのdataプロパティを使うと、メニューアイテムに任意の値を持たせることができます。次のサンプルでは[Help]メニューの下に[LiveDocs]メニューを設け、そのdataプロパティにURLを持たせてあります。[LiveDocs]メニューが選択されると、dataプロパティからURLを取得してページにジャンプします。

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" applicationComplete="buildMenu()">
  <mx:Script>
    <![CDATA[
      private function buildMenu():void {
        var rootMenu:NativeMenu = new NativeMenu();
        var helpMenu:NativeMenuItem = rootMenu.addSubmenu(new NativeMenu(), "Help");
        var docMenu:NativeMenuItem = new NativeMenuItem("LiveDocs");
        docMenu.data = "http://livedocs.adobe.com/flash/9.0_jp/ActionScriptLangRefV3/";
        docMenu.addEventListener(Event.SELECT, selectDocHandler);
        helpMenu.submenu.addItem(docMenu);
        if (Shell.supportsMenu) {
          Shell.shell.menu = rootMenu;
        } else if (NativeWindow.supportsMenu) {
          stage.nativeWindow.menu = rootMenu;
        }
      }
      private function selectDocHandler(e:Event):void {
        navigateToURL(new URLRequest(e.target.data));
      }
    ]]>
  </mx:Script>
</mx:WindowedApplication>

dataプロパティには様々なオブジェクトを設定できるので、例えばドキュメントに関するメニューにFileオブジェクトを設定しておくといった使い方ができます。

おすすめ記事

記事・ニュース一覧