gef编辑器详述

工作以来一直都在做IDE,写篇文章记录下自己对gef的理解,再粗糙也是自己的理解,呵呵!

gef主要是用于实现图形化编辑器的一个框架,尽管它主要用于实现编辑器,但是它还是可以做其它的东西的,例如官方提供的tree节点的大纲视图,就是建立在gef框架基础之上的。

1.Tool

EditDomain:

对于一个gef应用来说,EditDomain对象是全局唯一的,对于一个gef应用来说,EditDomain基本上是顶层控制器。它管理者EditPartViewer,PaletteViewer,CommandStack,activeTool。这里觉得奇怪的是为啥要有PaletteViewer,PaletteViewer至少应该在子类里面存在才是合适的吧,个人觉得PaletteViewer并不是一个gef应用必须的。

在EditDomain里面,EditDomain会转发来自EditPartViewer,PaletteViewer的请求,把它们转发给当前的activeTool,让activeTool处理这些事件。CommandStack命令栈,这个就不说了,不知道的别玩了,洗洗睡算了。

EditDomain自己是不构建Tool的,它都是从响应的Viewer里面获取的,也就是说Viewer提供了事件以为,还提供了处理事件的tool。

Tool:

想理解Tool是干嘛的,Tool接口上有一段详细的英文说明。Tool具备以下功能:

(1)获取EditDomain和EditPartViewer的事件,并把这些事件转换成请求,让相应的EditPart进行响应和处理。

(2)它自己本身也可以处理一些事件,例如切换选中状态,让viewer滚动,执行一个命令之类的。

说白了,Tool就是对界面事件的第一道处理工序,至于是自己直接处理,还是转发给EditPart,完全在于此事件的复杂度。

注意:官方说明,不要直接实现Tool接口,而是继承其AbstractTool,这是个建议。

Tool的继承关系,可以直接通过F4查看,整个结构比较简洁清晰,基本上gef所有的行为都能找到对应的Tool。eclipse最大的特点就是见文识意,基本上当你需要的时候,直接看实现类的名字就能找到地方了。具体的我自己也没研究。

常见Tool:

DragTracker:一个继承自Tool的接口,主要是用于响应拖拽操作的。

MarqueeSelectionTool:MarqueeToolEntry用的,选中编辑器里面的多个组件

ConnectionBendpointTracker:移动或创建连线上面的拐点的Tool

ResizeTracker:改变组件size的tool

RulerDragTracker:移动游标的,这个跟编辑器的Ruler效果相关

TargetingTool:是所有需要把事件请求转发给Editpart执行的tool的基类。

ConnectionCreationTool:用于连线的tool,ConnectionDragCreationTool与其功能相仿

SelectEditPartTracker:操作EditPart的,只是响应选中,编辑,打开等操作,拖拽由其子类实现。

DragEditPartsTracker:在父的基础上实现了移动的能力

DragTreeItemsTracker:拖拽树节点,大纲视图用到

SelectionTool:这个tool是很关键的一个tool,它是EditDomain默认提供的一个tool,我们说过EditDomain会把事件给相应的tool进行处理。但是事实上PaletteViewer获取当前活动的tool是有一个方法的:paletteViewer.getActiveTool(),但是EditPartViewer却没有这样的方法,而且EditDomain也没有什么调用特殊的方法获取EditPartViewer的tool。最后发现其实SelectionTool这个默认的tool会到EditPart里面获取相应的tool。

2.PaletteViewer

PaletteViewer:

在编辑器里面除了EditPartViewer以外还有PaletteViewer。PaletteViewer就是我们看到的面板,它也是用gef实现的。在PaletteViewer中有一套数据模型,其根类是PaletteEntry,它相当于gef里面的模型层,记录这界面的显示信息,以及实体的操作信息。

PaletteEntry的子类:

PaletteContainer:所有继承此类的类,都表示的是palette上的容器节点,例如 PaletteDrawer 就是我们常用的可折叠容器,PaletteRoot代表着palette的根节点,也是继承这个类。

ToolEntry:这个类的含义是,所有继承了它的类,都能够在编辑器中使用。例如gef默认提供的SelectionToolEntry:选中节点的那个tool,MarqueeToolEntry:画一个矩形框选中多个组件的tool,CreationToolEntry:以及我们创建一个界面元素常用的tool

其他:PaletteSeparator:分隔符,PaletteTemplateEntry:模板(这个没用过)

刚才提到ToolEntry可以在编辑器使用,也就是说它能够在编辑器上做一些行为,例如拖拽创建,点击选中,拖拽选中之类的操作。之所以能有这些能力,主要是因为在构造一个ToolEntry的时候同事会传一个Tool类型进去,之后使用的时候会创建一个Tool。

CreationToolEntry构造方法里面的代码:它会传一个CreationTool类型的tool进去,它会把基本的拖拽事件转换为创建请求,传递给EditPart处理

super(label, shortDesc, iconSmall, iconLarge, CreationTool.class);

 SelectionToolEntry的构造方法里面的代码类似,不同的是它给的是一个SelectionTool用于选中的。

super(label, shortDesc, SharedImages.DESC_SELECTION_TOOL_16,
				SharedImages.DESC_SELECTION_TOOL_24, SelectionTool.class);
 

3.EditPartViewer

ISelectionProvider:

ISelectionProvider是一个jface的接口,EditPartViewer继承于ISelectionProvider,ISelectionProvider主要用于管理选中节点的,这套机制并不属于gef或者编辑器的,而是eclipse的内部机制,也可以说是SWT的选中节点的管理机制。

EditPartViewer:

EditPartViewer继承了ISelectionProvider,标志着所有的EditPartViewer都可以作为一个ISelection源在eclipse内部进行流转。一个gef架构的图形化编辑器是,建立在一套EditPartViewer的基础之上的,用我的理解称EditPartViewer为一个业务单元。EditPartViewer接口里面的方法很多,大致分为几个方向:操作(菜单,快捷键,光标之类),事件(拖拽之类的),对EditPart的管理(EditDomain,EditPartFactory,提供根据鼠标位置获得EditPart的接口,flush),createControl方法提供对原始的SWT组件界面的支持(验证了在大纲视图中使用的能力)

可以说EditPartViewer就像gef的脸,它是直接面对用户的,用户所有的操作和请求,都是从它这里进行转发的。

EditPartViewer接口也是不建议自己直接实现的,而是继承此类AbstractEditPartViewer,在AbstractEditPartViewer里面有一个SelectionManager对象,用于管理当前Viewer的选中模型,在SelectionManager里面有一个appendSelection(EditPart editpart)方法,从参数名就可以看出。我们在Viewer上选中的是一个个EditPart 对象,这也是为啥,我们在属性视图会获取到EditPart 而不是Viewer的原因。

4.上述三者之间的联系

上述三者肯定是由EditDomain组织起来的,而最终的结果肯定是:Viewer监听事件,并响应事件,把事件传递给EditDomain,EditDomain找到相应的tool来进行处理,而这个tool又是Viewer自己提供的。EditDomain起到了一个中枢的作用。那么整个过程是怎样的呢?

PaletteViewer:

PaletteViewer提供了获取ActiveTool的方法,所以对于PaletteViewer来说,处理方式很简单。

private void handlePaletteToolChanged() {
		PaletteViewer paletteViewer = getPaletteViewer();
		if (paletteViewer != null) {
			ToolEntry entry = paletteViewer.getActiveTool();
			if (entry != null)
				setActiveTool(entry.createTool());
			else
				setActiveTool(getDefaultTool());
		}
	}
 

在EditDomain里面有一个handlePaletteToolChanged方法,在选中面板上的节点的时候会进入到这个方法。它会把获取当前的PaletteTool,当面板上的选中失去的时候,又会进入到这个方法。但执行的是setActiveTool(getDefaultTool());

EditPartViewer:

上面也说道了一点,EditPartViewer是不直接提供tool的,在EditDomain里面所有跟EditPartViewer相关的操作都会给SelectionTool(默认提供的tool),然后在SelectionTool里面会找到EditPartViewer相应的EditPart,EditPart会提供可用的ActiveTool。

SelectionTool这整个类就是想办法获取EditPart里面的那个可用的Tool,针对同步的事件类型,选择不同的获取方式,这里贴出其中一个方法,其他的方法与其类似:

protected boolean handleButtonDown(int button) {
		if (!stateTransition(STATE_INITIAL, STATE_DRAG)) {
			resetHover();
			return true;
		}
		resetHover();
		EditPartViewer viewer = getCurrentViewer();
		Point p = getLocation();

		if (getDragTracker() != null)
			getDragTracker().deactivate();

		if (viewer instanceof GraphicalViewer) {
			Handle handle = ((GraphicalViewer) viewer).findHandleAt(p);
			if (handle != null) {
				setDragTracker(handle.getDragTracker());
				return true;
			}
		}
		updateTargetRequest();
		((SelectionRequest) getTargetRequest()).setLastButtonPressed(button);
		updateTargetUnderMouse();
		EditPart editpart = getTargetEditPart();
		if (editpart != null) {
			setDragTracker(editpart.getDragTracker(getTargetRequest()));
			lockTargetEditPart(editpart);
			return true;
		}
		return false;
	}
 

其中最关键的一步:setDragTracker,然后其他相应事件的方法就调用这个Tool来对事件进行处理了。

附:

我们在刚入门的时候,通常只会关注如何扩展EditPart,以及它里面的东西,很少关注EditPart由谁管理,如何管理。这篇文章很浅,因为作者很浅,但是希望对不清楚的人有点帮助。

猜你喜欢

转载自lengbingteng-163-com.iteye.com/blog/1526894