Electron-icon

Electronでメニューへ動的にチェックを入れる


ElectronでMacOSアプリ作っています。

当サイトで「Open source & Free software」などと、標榜しておきながら、MacOSの方で有料アプリとして作って公開しています。(→気になる方はコチラからどうぞ)。

言い訳がましいですが、Appleのディベロッパーアカウントは1万円/年ほどかかっており、フリーで公開中のWindowsアプリのコードサイニング証明書更新など6万円/年と、すでに出銭の方が多いのではないかという状況でして。どうか忖度、願います。

そんなことはさておき、表題の件。

意外とサンプルが見つからなかったんですよね。

アプリ起動時のメニュー生成は、サンプルもいくつかみつかり、比較的簡単です。

let template = [
    {
      label: i18n.__('View'),
      submenu: [
        {
          label: i18n.__('Encoding'),
          id: 'Encoding',
          submenu: [
            {
              label: 'UTF-8',
              type: 'checkbox',
              checked: true
            },
            {
              label: 'EUC-JP',
              type: 'checkbox',
              checked: false
            },
            {
              label: 'Shift_JIS',
              type: 'checkbox',
              checked: false
            }
          ]
        }
      }
    }

  const menu = Menu.buildFromTemplate(template);
  Menu.setApplicationMenu(menu);

JSON形式でテンプレートを作っておいて、それをアプリケーションメニューとしてセットするだけです。

ただ、これですと、セットした後にメニュー内容を変更するにはどうすれば良いのしょうか? たとえば、以下のような場合です。

sample-menu

チェックボックスの変更です。たとえば、上記の例でいえば、エンコーディングの種類の変更を行いたい(その選択したメニューにチェックを入れ直したい)場合です。

これはもう構築したメニュー全体から、変更したい該当のメニューを探し出すしかありません。そこで、前述のソースコードの7行目に注目してほしいのですが、メニューアイテムの属性値に「id」を追加しています。これを頼りに検索する関数をつくります。

function getSubMenuItem(subMenuItems, id) {
  if (subMenuItems) {
    for (let i = 0; i < subMenuItems.length; i++) {
      if (subMenuItems[i].id === id) {
        return subMenuItems[i];
      }
      else if (subMenuItems[i].submenu) {
        let found = getSubMenuItem(subMenuItems[i].submenu.items, id);
        if (found) return found;
      }
    }
  }
}

const menu = Menu.getApplicationMenu();
let encodingMenu = getSubMenuItem(menu.items, 'Encoding');

これにより、変数encodingMenuには、「エンコーディング」以下のサブメニューオブジェクトが入ります。

これも関数にまとめてしまいましょう。

function clickEncodingMenu(item){
  const menu = Menu.getApplicationMenu();
  let encodingMenu = getSubMenuItem(menu.items, 'Encoding');

  // 一度、全チェックを外す
  for(let i = 0; i < encodingMenu.submenu.items.length; i++){
    encodingMenu.submenu.items[i].checked = false;
  }
  // 選択したエンコーディングをチェック
  item.checked = true;
}

ここで注意したいのが、6行目のループで使われているsubmenu.itemsです。ここ、submenuだけにしがちなので、ご注意ください。

まとめるとこうなります。

function getSubMenuItem(subMenuItems, id) {
  if (subMenuItems) {
    for (let i = 0; i < subMenuItems.length; i++) {
      if (subMenuItems[i].id === id) {
        return subMenuItems[i];
      }
      else if (subMenuItems[i].submenu) {
        let found = getSubMenuItem(subMenuItems[i].submenu.items, id);
        if (found) return found;
      }
    }
  }
}

let template = [
{
  label: i18n.__('View'),
  submenu: [
	{
	  label: i18n.__('Encoding'),
	  id: 'Encoding',
	  submenu: [
		{
		  label: 'UTF-8',
		  type: 'checkbox',
		  checked: true,
		  click: function (item) {
			clickEncodingMenu(item);
		  }
		},
		{
		  label: 'EUC-JP',
		  type: 'checkbox',
		  checked: false,
		  click: function (item) {
			clickEncodingMenu(item);
		  }
		},
		{
		  label: 'Shift_JIS',
		  type: 'checkbox',
		  checked: false,
		  click: function (item) {
			clickEncodingMenu(item);
		  }
		}
	  ]
	}
  }
}
const menu = Menu.getApplicationMenu();
let encodingMenu = getSubMenuItem(menu.items, 'Encoding');

本来ならよくあるように、’id’だけでズバッと該当のメニューを指定して変更できるのがベターなのですが、現状ではその方法がないので、メニュー内を探索して特定するしかなさそうですね。

 

お気軽にコメントをどうぞ〜

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください

コメントフィード

s