SWTのメニュー定義がめんどくさい。
SWTのメニュー
普通にSWTでメニューバーを作ろうとすると、例えばこんな感じになります。
// メニューバーの作成とウインドウ(Shell)への設定 Menu bar = new Menu (shell, SWT.BAR); shell.setMenuBar (bar); // "File"メニューの作成 MenuItem fileItem = new MenuItem (bar, SWT.CASCADE); fileItem.setText ("&File"); Menu submenu = new Menu (shell, SWT.DROP_DOWN); fileItem.setMenu (submenu); // File下の"Select All"メニューの作成 MenuItem item = new MenuItem (submenu, SWT.PUSH); item.addListener (SWT.Selection, (e)->System.out.println ("Select All")); item.setText ("Select &All\tCtrl+A"); item.setAccelerator (SWT.MOD1 + 'A');
元のソースに対して、コメントを追加し、メニュー選択時処理をラムダで定義しましたが、本質的には同じです。
- "File"の作成に4行(ラベル作成2行、内包設定2行)
- "Select All"作成に4行
これは長い。メニューで通常求められる要素は限定的なので、その範囲で済むならシンプルにしたいところです。
そこで、基本的に1項目1行で作成できるように補助クラスを作ってみます。「他を内包する項目」と「最終的に選ばれる項目」それぞれをクラスにして、構造を定義して、最後に一気にMenuやMenuItemクラスのインスタンスを構築する線でいきます。また、Accelerator(ショートカットキー)はsetAcceleratorとsetText後半が被るので、Acceleratorからテキストラベルを設定することで設定を簡略化してみます。
// 一気に構築するための共通インタフェース public interface MenuElement { void build(MenuFolder parent); String getLabel(); }
// 最終的に選ぶ項目 public class MenuButton implements MenuElement { private String myLabel; private int myAccelerator; private MenuItem myMenuItem; private Listener myListener; public MenuButton(String label) { this(label, 0, null); } public MenuButton(String label, int accelerator) { this(label, accelerator, null); } public MenuButton(String label, int accelerator, Listener listener) { myLabel = label; myAccelerator = accelerator; myListener = listener; } public MenuItem getMenuItem() { return myMenuItem; } public String getLabel() { return myLabel; } public void build(MenuFolder parent) { myMenuItem = new MenuItem(parent.getMenu(), SWT.PUSH); String label = myLabel; if (myAccelerator > 0) { myMenuItem.setAccelerator(myAccelerator); label += "\t"; if ((myAccelerator & SWT.CTRL) != 0) { label += "Ctrl+"; } if ((myAccelerator & SWT.SHIFT) != 0) { label += "Shift+"; } if ((myAccelerator & SWT.ALT) != 0) { label += "Alt+"; } label += String.valueOf((char)(myAccelerator & (~SWT.MODIFIER_MASK))); } if (myListener != null) { myMenuItem.addListener(SWT.Selection, myListener); } myMenuItem.setText(label); } }
// 他を内包する項目(名前はもっと違った方がよかったかも) public class MenuFolder implements MenuElement { private String myLabel; private MenuItem myMenuItem; private Menu myMenu; private List<MenuElement> myItems; public static MenuFolder makeRootMenu() { return new MenuFolder(); } private MenuFolder() { this(""); } public MenuFolder(String label) { myLabel = label; init(); } private void init() { myItems = new ArrayList<MenuElement>(); } public MenuFolder addFolder(String label) { MenuFolder child = new MenuFolder(label); myItems.add(child); return child; } public MenuFolder addButton(String label) { return addButton(label, 0, null); } public MenuFolder addButton(String label, int accelerator) { return addButton(label, accelerator, null); } public MenuFolder addButton(String label, int accelerator, Listener listener) { MenuButton child = new MenuButton(label,accelerator,listener); myItems.add(child); return this; } public Menu getMenu() { return myMenu; } public String getLabel() { return myLabel; } public void buildMenuBar(Shell parent) throws IllegalStateException { if (myLabel != null && myLabel.length()>0) { throw new IllegalStateException("this is not root menu!"); } if (myItems == null || myItems.size() < 1) { throw new IllegalStateException("no child"); } myMenu = new Menu(parent,SWT.BAR); parent.setMenuBar(myMenu); for (MenuElement element : myItems) { element.build(this); } } public void build(MenuFolder parent) { myMenuItem = new MenuItem(parent.getMenu(), SWT.CASCADE); myMenuItem.setText(myLabel); if (myItems == null || myItems.size() < 1) return; myMenu = new Menu(myMenuItem); myMenuItem.setMenu(myMenu); for (MenuElement element : myItems) { element.build(this); } } public MenuFolder getSubFolder(String label) throws IllegalArgumentException { for (MenuElement element : myItems) { if (element instanceof MenuFolder) { MenuFolder f = (MenuFolder)element; if (f.getLabel().equals(label)) return f; } } throw new IllegalArgumentException("no item found"); } public MenuElement getElement(String label) throws IllegalArgumentException { for (MenuElement element : myItems) { if (element.getLabel().equals(label)) return element; } throw new IllegalArgumentException("no item found"); } }
これらを使った場合のメニュー作成は、次のようになります。
MenuFolder menuBar = MenuFolder.makeRootMenu(); menuBar.addFolder("&File") .addButton("Select &All", SWT.MOD1 + 'A', (e)->System.out.println ("Select All")); menuBar.buildMenuBar(myShell);
1項目1行なので、ソースからメニュー構造がわかると思います。ここではラジオボタン式メニューなどのクラスを設定していませんが、その辺も同様に作れるのではないかと思っています。