<更新記録>
2007年 12月 2日
作成

姉妹サイト検索 Web検索


contribution

SWTのウィジェット作成とイベント処理のAPIはパワフルなのですが、単純なイベントキャッチの処理にまで長いコードを記述しなければなりません。 そこで、次のような制約を課すことで、その範囲内のコードをもっと簡略化するために考え出されたのがコントリビューションです。

  • ボタン、ツールバー、メニューに限定
  • 1つのイベントのみを受け取る
  • 1つのイベントのみしかハンドルできない
アプリケーション開発では、これらの制約に沿って開発が可能な部分は極力コントリビューションフレームワークの形式でコードを記述することによって、 コードの簡略化が行えます。

contribution

+------- IContributionManager ----+
|                                 |
| ToolBarItem   MenuItem   ボタン |
|     ^            ^         ^    |   
+-----|------------|---------|----+   
      |具体化      |具体化   |
+------------------------+---+  +-------------------+
| ActionContributionItem |.....>| IContributionItem |
+------------------------+      +-------------------+
      ^
      |作成
+---------+
| IAction |
+---------+
コントリビューションの仕組みを簡単に言ってしまえば、ある1つの意味的な核を作成し、 必要に応じてその核からウィジェットとイベントハンドリングの構造を、簡単に作成するというものです。 その核の部分が、IActionインターフェイスです。

JFace APIでは、IActionインターフェイスを実装したActionクラスが用意されています。 必要に応じてIActionインターフェイスを継承したクラスを作成することも可能です。

Actionクラスは、それを継承することで使用します。 継承した際にオーバーライドしなければならないのが、runメソッドです。 この中に、イベント発生時に行う処理を記述します。 2つのAction継承クラスを作成しました。これらは後のサンプルで呼び出されます。

IncrementalAction.java
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.StatusLineManager;

public class IncrementalAction extends Action {
	private StatusLineManager status;
	
	public IncrementalAction(StatusLineManager status) {
		super("&Increment@Ctrl+I", AS_PUSH_BUTTON);
		this.status = status;
	}
	
	public void run() {
		status.setMessage(String.valueOf(++Counter.count));
	}
}
コンストラクタから、さらにActionクラスのコンストラクタを呼び出しています。 最初の引数で、そのテキストとショートカットキーを設定しています。 次の引数では、ボタンとして表示される際のスタイルを設定しています。 ここに指定できるスタイルは、IActionインターフェイスに定数として定義されています。
DecrementalAction.java
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.StatusLineManager;

public class DecrementalAction extends Action {
	private StatusLineManager status;
	
	public DecrementalAction(StatusLineManager status) {
		super("&Decrement@Ctrl+D", AS_PUSH_BUTTON);
		this.status = status;
	}
	
	public void run() {
		status.setMessage(String.valueOf(--Counter.count));
	}
}
前者はカウントをインクリメントし、後者はデクリメントします。 結果はコンストラクタの引数でとったStatusLineManagerオブジェクトに格納します。 StatusLineManagerもIContributionManagerインターフェイスの実装クラスです。

IncrementalActionクラス及びDecrememtalActionクラスで登場するCounterクラスを作成します。 public class Counter { public static int count; }

先ほど作成したIncrementalActionクラスとDecrementalActionクラスを使用して、 メニューを作成するコード片を示します。

IAction incAction = new IncrementalAction(statusLineManager);
IAction decAction = new DecrementalAction(statusLineManager);

MenuManager menuManager = new MenuManager();
MenuManager subMenuManager = new MenuManager("&Count");
menuManager.add(subMenuManager);

subMenuManager.add(incAction);
subMenuManager.add(decAction);
MenuManagerクラスは、IContributionManagerインターフェイスとIContributionItemインターフェイスを同時に実装しているので、 メニューマネージャ同士で格納しあうことができます。 そしてメニューマネージャには、add(IAction)メソッドでアクションを格納することができます。 これにより、メニューに先ほど作成したインクリメントするアクションとデクリメントするアクションがメニューアイテムとして登録されました。 登録されたメニューアイテムのテキストは、IncrementalActionクラス及びDecrementalActionクラスのコンストラクタで呼び出した Actionクラスのコンストラクタに指定したテキストが使用されます。

次はツールバーを作成するコード片を示します。 ツールバーの作成も、メニューのときとほぼ同じです。 ToolBarManagerのコンストラクタには、スタイルを指定することが可能です。 ここにはツールバーのスタイルを指定します。

ToolBarManager toolBarManager = new ToolBarManager(style);
toolBarManager.add(incAction);
toolBarManager.add(decAction);

普通のボタンを作成します。 メニューバーとツールバーのときは、IAction型のオブジェクトを直接MenuManagerやToolBarManagerクラスのaddメソッドに指定できましたが、 ボタンを作成する場合は「なんとかManager」クラスは使いません。 代わりに、IContributionItem型のオブジェクトを作成して、fillメソッドを呼び出すことでボタンを作成します。 ActionContributionItemクラスは、IContributionItemインターフェイスの実装サブクラスです。

Composite buttonArea = new Composite(parent, SWT.BORDER);
buttonArea.setLayout(new FillLayout());

ActionContributionItem incActItem = new ActionContributionItem(incAction);
incActItem.fill(buttonArea);
ActionContributionItem decActItem = new ActionContributionItem(decAction);
decActItem.fill(buttonArea);
fillメソッドで作成されるボタンは、指定した親のコンテナいっぱいに作成されてしまうので、 FillLayoutを設定して、お互いが表示されるように配慮しています。

ApplicationWindowクラスでは、createMenuManagerメソッド、createToolBarメソッド等が用意されており、 そのメソッドの戻り値として返されたMenuManagerオブジェクトとToolBarManagerオブジェクトがウィンドウに使用されます。 細かいことは気にしなくても、ApplicationWindowクラスのフレームワークが自動的にレイアウトを施してくれます。 メニューバーやツールバーを作成するときは、これらのメソッドをオーバーライドします。 ただし、それだけではだめで、 最後にaddMenuBarメソッドやaddToolBarメソッドを呼び出さなければ表示されないので、忘れないように呼び出します。 これには呼び出すタイミングがあって、シェルが作成される前でなければなりません。それ以降だと例外が発生します。。 そこが面倒くさい部分なのですが、サンプルで呼び出している位置を参考にして下さい。

すごくややこしいですね。 ここまでややこしいと、もう慣れの問題になってくることもあります。

FirstContributing.java
import org.eclipse.jface.action.ActionContributionItem;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.StatusLineManager;
import org.eclipse.jface.action.ToolBarManager;
import org.eclipse.jface.window.ApplicationWindow;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;

public class FirstContributing extends ApplicationWindow {
	StatusLineManager statLineMan = new StatusLineManager();
	IAction incAction = new IncrementalAction(statLineMan);
	IAction decAction = new DecrementalAction(statLineMan);
	
	private FirstContributing() {
		super(null);
		addMenuBar();
		addToolBar(SWT.WRAP);
		addStatusLine();
	}
	
	protected Control createContents(Composite parent) {
		parent.setSize(300, 300);
		
		Composite buttonArea = new Composite(parent, SWT.BORDER);
		buttonArea.setLayout(new FillLayout());
		
		ActionContributionItem incActItem = 
			new ActionContributionItem(incAction);
		incActItem.fill(buttonArea);
		ActionContributionItem decActItem = 
			new ActionContributionItem(decAction);
		decActItem.fill(buttonArea);
		
		return parent;
	}
	
	protected MenuManager createMenuManager() {
		MenuManager menuManager = new MenuManager();
		MenuManager subMenuManager = new MenuManager("&Count");
		menuManager.add(subMenuManager);
		
		subMenuManager.add(incAction);
		subMenuManager.add(decAction);
		
		return menuManager;
	}
	
	protected ToolBarManager createToolBarManager(int style) {
		ToolBarManager toolBarManager = new ToolBarManager(style);
		toolBarManager.add(incAction);
		toolBarManager.add(decAction);
		
		return toolBarManager;
	}
	
	protected StatusLineManager createStatusLineManager() {
		return statLineMan;
	}
	
	public static void main(String[] args) {
		ApplicationWindow w = new FirstContributing();
		w.setBlockOnOpen(true);
		
		w.open();
		Display.getCurrent().dispose();
	}
}
メニューにも、ツールバーにも、そしてボタンにも、インクリメントとデクリメントのボタンが作成されています。 これらを押下すると、IncrementalAction及びDecrementalActionクラスのrunメソッドで定義したイベントが動作しているのがわかると思います。 結果はステータスラインに表示されます。 メニューの[Ctrl]+[I]及び[Ctrl]+[D]のショートカットキーも有効です。

サンプルでは、ContributionManager#add(IAction)メソッドを使用しました。 MenuManagerクラスやToolBarManagerクラスは、ContributionManagerクラスを継承しているので呼び出すことができました。 ContributionManagerクラスには、add(IContributionItem)メソッドも用意されています。 これは、ボタンを作成するときに作成したように、まずIContributionItemオブジェクトを作成してから、 それをadd(IContributionItem)メソッドに指定することもできることを意味します。

IAction incAction = new IncrementalAction(status);
IContributionItem incActItem = new ActionContributionItem(incAction);
menuManager.add(incActItem);
ただしIContributionItemオブジェクトは、複数のaddメソッドで指定することはできないので、 必ずaddメソッドで指定される数分だけインスタンス化しなければなりません。

ContributionManager#add(IAction)メソッドは、渡されたIActionオブジェクトからIContributionManagerオブジェクトを作成し、 そしてContributionManager#add(IContributionManager)メソッドを呼び出しています。


Powered by VeryEasyCMS