GEF中文教程 自学笔记 5

目标:1. 显示属性视图,并且可以在属性视图中修改属性
2. 直接编辑图形属性
3. 增加键盘事件响应

 

属性视图
在GEF中,使用属性视图修改的是图形模型的属性。也就是说图形模型是属性视图的数据源:当图形模型改变时,属性视图要反映相应改变;而在属性视图中修改属性时,图形也要发生相应改变。
1. 让AbstractModel实现IPropertySource接口,并重载这个接口中的方法。
PropertySheet视图的使用方法就不在这儿描述了。
2. 修改HelloModel,使其成为属性视图的数据源。
注意setText方法需要firePropertyChange以通知EditPart 模型发生了改变。
3. 修改HelloEditPart使其能响应HelloModel模型text属性发生改变这一事件。
4. 修改Perspective类,加上Property View。

 

可以Undo?运行该RCP项目时,发现修改属性视图的操作是可以Redo/Undo的,而我们刚才写的代码并没有牵涉到任何Command,这是怎么回事呢?
Redo/Undo肯定是由CommandStack类管理的,所以在它的execute(Command)方法中加断点,跟踪发现,当我修改了text属性时,的确产生了一个叫SetValueCommand的命令。
那又是谁负责生成这个命令的呢?观察Debug栈可知,是由UndoablePropertySheetEntry类创建的,这个类在GEF中取代了PropertySheetEntry类,它关联了CommandStack,进而提供了Redo/Undo功能。CellEditor(提供了Property View的修改功能)提交修改的操作最终交给了UndoablePropertySheetEntry类的valueChanged方法来处理,该方法创建一个新的SetValueCommand并把它交给CommandStack处理。

最后一点说明是关于IAdaptable的。PropertySheet创建页面时会先尝试调用属性源(input,这儿是所选中的HelloHditPart)所在的视图或编辑器的getAdapter方法来获得一个特定的IPropertySheetPage,而GraphicalEditor的getAdapter方法在遇到IPropertySheetPage.class这种类型的请求时,就返回一个设置了root entry为UndoablePropertySheetEntry的PropertySheetPage。
public Object getAdapter(Class type) {
    if (type == org.eclipse.ui.views.properties.IPropertySheetPage.class) {
        PropertySheetPage page = new PropertySheetPage();
        page.setRootEntry(new UndoablePropertySheetEntry(getCommandStack()));
        return page;
    }
    if (type == ActionRegistry.class)
        return getActionRegistry();
    ......
通过这种途径,GEF用自己定义的UndoablePropertySheetEntry来管理PropertySheet上的修改操作以实现Redo/Undo。<这为如何编写有特定行为方式的PropertySheet提供了一种思路>

 

Delete工具按钮的defect:当点击text属性值时,Delete工具按钮就变成了enabled状态,但是操作无效果。调试代码,在calculateEnabled方法上加断点,发现根本没有进入该方法,这是怎么回事呢?如果有朋友知道的话,恳请告知。

一些思路:DeleteRetargetAction的id是delete。当焦点在DiagramEditor时,该RetargetAction的handler是org.eclipse.gef.ui.actions.DeleteAction,因为DeleteAction的id也是delete,(GraphicalEditor的createActions方法注册了6个actions到ActionRegistry里面,它们是undo,redo,save,delete,selectAll和print。当GraphicalEditor被激活时,DiagramActionBarContributor响应该事件,在setActiveEditor方法里把RetargetAction通过id关联上真正的Action)
当切换到PropertySheet时,该RetargetAction的handler变为CellEditorActionHandler$DeleteActionHandler。

 

直接在Graphical Editor中编辑文本
1. 创建DirectEditManager
DirectEditManager通过创建、维护CellEditor,并在其值改变的情况下执行相应的Command来管理直接修改图形的操作。DirectEditManager是一个抽象类,我们定义一个自己的CustomDirectEditManager继承它并实现它的initCellEditor方法。
2. 创建一个CellEditorLocator接口的实现类
CellEditorLocator用来确定CellEditor放置的位置。该接口的实现类可以通过构造函数获得figure,在relocate方法中根据figure的相对位置放置CellEditor。
public void relocate(CellEditor celleditor) {
    Text text = (Text) celleditor.getControl();
    Rectangle rect = figure.getBounds().getCopy();
    figure.translateToAbsolute(rect);
    text.setBounds(rect.x, rect.y, rect.width, rect.height);
}
3. 重载AbstractEditPart的performRequest方法,处理REQ_DIRECT_EDIT类型的请求。
可以在performRequest方法中处理一些特定的请求,比如直接编辑图形这种请求。
具体的方法是创建一个CustomDirectEditManager的实例并调用其show方法显示出CellEditor。
    directEditManager = new CustomDirectEditManager(this, TextCellEditor.class, new CustomCellEditorLocator(getFigure())); //参数分别为源EditPart;CellEditor的类型;用来指定CellEditor位置的CellEditorLocator接口的实现类
    directEditManager.show();
4. 创建Command与DirectEditPolicy,并安装该EditPolicy
首先创建一个DirectEditCommand类用来修改模型的text属性。
REQ_DIRECT_EDIT类型的请求由DirectEditPolicy来处理,实现类重载getDirectEditCommand方法创建DirectEditCommand即可。
安装该EditPolicy时使用的Role是EditPolicy.DIRECT_EDIT_ROLE
DirectEditPolicy只负责在cell editor失去焦点并且值发生改变时(dirty状态)生成相应的Command去修改模型。实际上,是DirectEditManager在其管理的cell editor值发生改变并且丢失焦点时调用其关联的EditPart的getCommand方法(进一步委托给EditPolicies的getCommand方法)触发DirectEditPolicy工作的

所以不是单独用EditPolicy,而是用DirectEditManager+EditPolicy的策略来处理直接编辑图形的操作是合理的。EditPolicy生成Command处理各种请求,潜台词是使用EditPolicy处理模型的改变。而当我们通过鼠标单击进行直接编辑图形操作时,模型并没有发生改变,这时用EditPolicy是不合适的。


键盘操作直接编辑文本
1. 将DirectEditAction注册到DiagramEditor中
GraphicalEditor的createActions方法用来把actions注册到ActionRegistry。我们需要在DiagramEditor类中重载该方法,把DirectEditAction注册进去,以供下一步的KeyHandler使用。
protected void createActions() {
    super.createActions();
    ActionRegistry registry = getActionRegistry();

    IAction action = new DirectEditAction(this);
    registry.registerAction(action);
    getSelectionActions().add(action.getId());
}
2. 为graphical viewer设置KeyHandler
在DiagramEditor的configureGraphicalViewer方法中为graphical viewer设置KeyHandler。
    KeyHandler keyHandler = new KeyHandler();
    keyHandler.put(KeyStroke.getPressed(SWT.DEL, 127, SWT.NONE), getActionRegistry().getAction(ActionFactory.DELETE.getId()));
    keyHandler.put(KeyStroke.getPressed(SWT.F2, SWT.NONE), getActionRegistry().getAction(GEFActionConstants.DIRECT_EDIT));
    getGraphicalViewer().setKeyHandler(new GraphicalViewerKeyHandler(getGraphicalViewer()).setParent(keyHandler));

KeyHandler在EditPartViewer上处理键盘事件(不包括菜单加速键),所以我们用getGraphicalViewer().setKeyHandler...
KeyHandler可以设置parent handler。当前handler无法响应的键盘事件会自动传递给parent handler。上面代码里面设定了key handler为GraphicalViewerKeyHandler,一个GEF预定义的key handler,可以用方向键和正反斜杠键来遍历或选择图形与连接。我们自己创建的keyHandler实例设置为GraphicalViewerKeyHandler的parent handler,当GraphicalViewerKeyHandler不能处理某键盘事件时(比如F2或Shift+K),会转交给我们的keyHandler去处理。
有两种策略去实现KeyHandler,一种是使用put(KeyStroke, IAction)方法添加KeyStroke与IAction的映射,例如我们自己创建的keyHandler实例;另一种是继承KeyHandler类,重载keyPressed之类的方法去实现,例如GraphicalViewerKeyHandler。当然也可以同时使用两种策略。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值