Eclipseのプラグイン開発のGEFでタブプロパティーを使う方法について。
|
TabbedPropertyは、リッチなプロパティービュー。
タブでページを選択し、それぞれのページでダイアログ(フォーム)の様なレイアウトでプロパティーの入力・変更ができる。
TabbedPropertyを使うには、必須プラグインに「org.eclipse.ui.views.properties.tabbed」を追加する必要がある。
※TabbedProperty自体はGEFでなくても使えるが、GEFの図形(モデル)に対して使うにはちょっとしたポイントがある。
TabbedPropertyの使用方法はEclipse Corner ArticleのThe Eclipse Tabbed Properties Viewに詳しい。
が、そこからサンプルとして『Tabbed Properties Logic Example』へのリンクが張ってあるのだが、これはCVSのサイトを指しており、古い。
ソースはCVSからGitへ移行されたらしく、今のCVSのサイトには「Gitへ移った」ということしか書かれていない。
という訳で、『Tabbed Properties Logic Example』はorg.eclipse.ui.examples.views.properties.tabbed.logicに置かれている。
モデルクラスのソースはorg.eclipse.gef.examples.logicにあるようだ。
Eclipseを起動したときにプロパティービューが出ていない場合は、以下の手順で表示できる。
一行だけプロパティーを表示してみる例。
プロパティービューにどのようなタブ(ページ)・行を表示するかについては、行単位でplugin.xmlに定義を書く必要がある。
<extension
point="org.eclipse.ui.editors">
<editor
class="com.example.MyEditor"
default="false"
filenames="*.mygui"
id="plugin.my-gui-editor"
name="My GUI Editor">
</editor>
</extension>
<extension
point="org.eclipse.ui.views.properties.tabbed.propertyContributor">
<propertyContributor
contributorId="plugin.my-gui-editor">
<propertyCategory
category="example.propertyTab.category"></propertyCategory>
</propertyContributor>
</extension>
<extension
point="org.eclipse.ui.views.properties.tabbed.propertyTabs">
<propertyTabs
contributorId="plugin.my-gui-editor">
<propertyTab
category="example.propertyTab.category"
id="example.genericTab"
label="generic">
</propertyTab>
</propertyTabs>
</extension>
<extension
point="org.eclipse.ui.views.properties.tabbed.propertySections">
<propertySections
contributorId="plugin.my-gui-editor">
<propertySection
class="com.example.property.NameSection"
id="example.nameSection"
tab="example.genericTab">
<input
type="com.example.editpart.MyModelEditPart">
</input>
</propertySection>
</propertySections>
</extension>
まず、propertyContributorでcontributorIdとカテゴリーIDを定義する。
contributorIdは何でもいいと思うが、『Tabbed
Properties Logic Example』ではエディターのIDと同じものにしていた。
次に、propertyTabsでタブ(ページ)を定義する。
タブにIDを付け、属するカテゴリーとラベルを指定する。
最後に、propertySectionsでタブ(ページ)内のセクションを定義する。
どうも、プロパティーの1行につき1つのセクションを定義しないといけないようだ。
セクションにIDを付け、属するタブを指定する。
classには、表示・編集方法を記述するクラスを指定する。
inputタグで、どのクラス(モデル)に対するものなのかを指定する。今回はEditPartクラスを指定してみた。
『Tabbed Properties Logic Example』では、inputにモデルクラスそのものを指定している。
この場合は、propertyContributorでtypeMapperを指定し、EditPartからモデルクラスへの変換を定義する必要がある。
EditorクラスはITabbedPropertySheetPageContributorインターフェースを実装する必要がある。
import org.eclipse.ui.views.properties.IPropertySheetPage; import org.eclipse.ui.views.properties.tabbed.ITabbedPropertySheetPageContributor; import org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetPage;
public class MyEditor extends GraphicalEditorWithFlyoutPalette implements ITabbedPropertySheetPageContributor {
〜
@Override
public String getContributorId() {
return getSite().getId();
}
ITabbedPropertySheetPageContributorで実装すべきメソッドは、getContributorId()のみ。
plugin.xmlに書かれているcontributoerIdを指定するが、「getSite().getId()」はエディターそのもののID。
『Tabbed Properties Logic Example』ではcontributoerIdをエディターのIDと同一にしているので、こういう実装になっている。
@Override
public Object getAdapter(@SuppressWarnings("rawtypes") Class adapter) {
if (IPropertySheetPage.class.isAssignableFrom(adapter)) {
return new TabbedPropertySheetPage(this);
}
return super.getAdapter(adapter);
}
}
そして、getAdapter()でIPropertySheetPageを返すようにする。
TabbedPropertySheetPageインスタンスを作って返せばよい。
(TabbedPropertySheetPageのコンストラクターの引数がITabbedPropertySheetPageContributorなので、thisはITabbedPropertySheetPageContributorを実装する必要がある)
TabbedPropertySheetPageではISelectionChangedListenerが実装されており、エディター上で図形を選択する度に呼ばれる。
選択された図形のEditPartに応じてplugin.xmlに書かれたタブ定義を選択し、それをプロパティービュー上に描画するようになっている。
ISection(AbstractPropertySection)を実装し、実際に入力するエリアを作成する。これにはSWTを使用する。
import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.CLabel; import org.eclipse.swt.events.ModifyEvent; import org.eclipse.swt.events.ModifyListener; import org.eclipse.swt.layout.FormAttachment; import org.eclipse.swt.layout.FormData; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Text; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.views.properties.tabbed.AbstractPropertySection; import org.eclipse.ui.views.properties.tabbed.ITabbedPropertyConstants; import org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetPage; import org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetWidgetFactory;
public class NameSection extends AbstractPropertySection {
private Text nameText;
private ModifyListener listener = new ModifyListener() {
@Override
public void modifyText(ModifyEvent event) {
String name = nameText.getText();
model.setName(name);
}
};
private MyModel model;
@Override
public void createControls(Composite parent, TabbedPropertySheetPage aTabbedPropertySheetPage) {
super.createControls(parent, aTabbedPropertySheetPage);
TabbedPropertySheetWidgetFactory factory = getWidgetFactory();
Composite composite = factory.createFlatFormComposite(parent);
{
nameText = factory.createText(composite, "");
FormData data = new FormData();
data.left = new FormAttachment(0, STANDARD_LABEL_WIDTH);
data.right = new FormAttachment(100, 0);
data.top = new FormAttachment(0, ITabbedPropertyConstants.VSPACE);
nameText.setLayoutData(data);
nameText.addModifyListener(listener);
}
{
CLabel nameLabel = factory.createCLabel(composite, "name:");
FormData data = new FormData();
data.left = new FormAttachment(0, 0);
data.right = new FormAttachment(nameText, -ITabbedPropertyConstants.HSPACE);
data.top = new FormAttachment(nameText, 0, SWT.CENTER);
nameLabel.setLayoutData(data);
}
}
createControls()でフォームを作成する。
基本的にはSWTだが、getWidgetFactory()でファクトリーオブジェクトを取得して使うことが出来る。
普通は「ラベル: 入力エリア」という行を作ろうと思ったらラベル→入力エリアの順でコンポーネントを作るのだが、ここでは逆の順序になっている。
FormDataで位置を指定しているので、その都合だろう。
@Override
public void setInput(IWorkbenchPart part, ISelection selection) {
super.setInput(part, selection);
IStructuredSelection ss = (IStructuredSelection) selection;
MyModelEditPart editPart = (MyModelEditPart) ss.getFirstElement();
model = editPart.getModel();
}
選択されたEditPartがsetInput()に渡ってくるので、モデル(プロパティー変更対象のオブジェクト)をフィールドに保持しておく。
@Override
public void refresh() {
nameText.removeModifyListener(listener);
nameText.setText(model.getName());
nameText.addModifyListener(listener);
}
}
refresh()でプロパティー上のコンポーネントを更新する。
リスナーでは、プロパティーの値を変更する度にモデルの値を更新している。
モデルの値(プロパティー)を変更しても、デフォルトではプロパティービューに反映されない。[2013-04-19]
モデルがPropertyChangeSupportを利用し、EditPartがPropertyChangeListenerを実装してプロパティー変更イベントを受け取るようになっている場合、
EditPartで変更を検知してプロパティービューを再描画(リフレッシュ)すればいい。
import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.views.properties.PropertySheet; import org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetPage;
public class HogeEditPart extends AbstractGraphicalEditPart implements PropertyChangeListener {
@Override
public void propertyChange(PropertyChangeEvent event) {
refreshPropertySheet();
}
private void refreshPropertySheet() {
IWorkbenchPage activePage = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
PropertySheet view =(PropertySheet) activePage.findView("org.eclipse.ui.views.PropertySheet");
if (view == null) {
return;
}
TabbedPropertySheetPage page = (TabbedPropertySheetPage) view.getCurrentPage();
page.refresh();
}
PlatformUIを使ってアクティブページを取り出し、その中からfindView()でプロパティーシート(プロパティービュー)を探す。
"org.eclipse.ui.views.PropertySheet"はPropertySheetビュー(クラス)のID。どこかに定数を用意してくれればいいのに。
参考: stackoverflowのIn the activator class of an Eclipse plugin how can I find view instances?
plugin.xmlにプロパティー(セクション)を表示すべき条件(クラス)を指定するのは、EditPartでなくモデルを指定する方が良い。[2013-05-01]
(プロパティーに表示したいのはモデルだから、モデルの種類に応じて表示するかどうかが変化する為。
また、アウトラインページにツリーを表示して、そのツリーの要素(モデル)のプロパティーを表示するにもモデルの方が都合が良い)
こういう場合に、EditPartからモデル(条件判定用のクラス)へ変換するのがtypeMapper。
<extension
point="org.eclipse.ui.views.properties.tabbed.propertyContributor">
<propertyContributor
contributorId="plugin.my-gui-editor"
typeMapper="com.example.MyTypeMapper">
<propertyCategory
category="example.propertyTab.category"></propertyCategory>
</propertyContributor>
</extension>
import org.eclipse.gef.editparts.AbstractGraphicalEditPart; import org.eclipse.ui.views.properties.tabbed.AbstractTypeMapper;
public class MyTypeMapper extends AbstractTypeMapper {
@Override
public Class<?> mapType(Object object) {
if (object instanceof AbstractGraphicalEditPart) {
AbstractGraphicalEditPart part = (AbstractGraphicalEditPart) object;
return part.getModel().getClass();
}
return super.mapType(object);
}
}
EditPart(具体的にはAbstractGraphicalEditPartのサブクラス)からはgetModel()でモデルが取得できるので、そのクラスを返している。
これで、plugin.xmlのセクションの条件にモデルクラス名を指定できる。
<extension
point="org.eclipse.ui.views.properties.tabbed.propertySections">
<propertySections
contributorId="plugin.my-gui-editor">
<propertySection
class="com.example.property.NameSection"
id="example.nameSection"
tab="example.genericTab">
<input
type="com.example.model.MyModel"> ←セクションの条件にモデルクラス名(typeMapperで返るもの)を指定する
</input>
</propertySection>
</propertySections>
</extension>