[Eclipse]GEF入门系列(十二、自定义Request)

先简单回顾一下Request在GEF里的作用。Request是GEF里一个比较重要的角色,Tool将原 始的鼠标事件转换为EditPart可以识别的请求,Request则承载了这些请求信息。举例来说, 用户在调色板(Palette)里选择了创建节点工具(CreationTool),然后在画布区域按下鼠 标左键,这时产生在画布上的鼠标单击事件将被CreationTool转换为一个CreateRequest,它 里面包含了要创建的对象,坐标位置等信息。 EditPart上如果安装了能够处理 CreateRequest的EditPolicy,则相应的EditPolicy会根据这个 CreateRequest创建一个 Command,由后者实际执行创建新对象的必要操作。

GEF已经为我们提供了很多种类的Request,其中最常用的是CreateRequest及其子类 CreateConnectionRequest,其他比较常见的还有SelectionRequest,ChangeBoundsRequest 和 ReconnectRequest等等。要实现一个典型的图形化应用程序,例如UML类图编辑器,这些 预定义的Request基本够用了。然而各种稀奇古怪的需求我相信大家也见过不少,很多需求不 太符合约定俗成的使用习惯,因此实现起来更多依赖开发人员的编码,而不是开发框架带来 的便利。在这种时候,我们唯一的期望就是开发框架提供足够的扩展机制,以便让我们额外 编写的代码能和其他代码和平共处,幸好GEF是具有足够的扩展性的。有点跑题了,再回到 Request的问题上,为了说明什么情况下需要自定义 Request,我在前文“应用实例”里的示 例应用基础上假设一个新的需求:

在Palette里增加三个工具,作用分别是把选中节点的背景颜色改变为红色、绿色和蓝色 。

假如你用过Photoshop或类似软件,这个需求很像给节点上色的“油漆桶”或“上色工具 ”,当然在用户界面的背后,实际应用里这些颜色可能代表一个节点的重要程度,优先级或 是异常信息等等。现在,让我们通过创建一个自定义的Request来实现这个需求,还是以前文 中的示例项目为基础。

一、首先,原来的模型里节点(Node)类里没有反映颜色的成员变量,所以先要在Node类 里添加一个color属性,以及相应的 getter/setter方法,注意这个setter方法里要和其他成 员变量的setter方法一样传递模型改变的消息。仿照其他成员变量,还应该有一个静态字符 串变量,用来区分消息对应哪个属性。

final public static String PROP_COLOR = "COLOR";protected RGB color = new RGB(255, 255, 255);public RGB getColor() {   return color;}public void setColor(RGB color) {   if (this.color.equals(color)) {     return;   }   this.color = color;   firePropertyChange(PROP_COLOR, null, color);}

二、然后,要让Node的color属性变化能够反映到图形上,因此要修改NodePart里的 propertyChanged()和 refreshVisuals()方法,在前者里增加对color属性的响应,在后者里 将NodeFigure的背景颜色设置为Node的color属性对应的颜色。(注意,Color对象是系统资 源对象,实际使用里需要缓存以避免系统资源耗尽,为节约篇幅起见,示例代码直接new Color()了)

public void propertyChange(PropertyChangeEvent evt) {   if (evt.getPropertyName().equals(Node.PROP_COLOR))//Response to color change     refreshVisuals();}protected void refreshVisuals() {   ((NodeFigure) this.getFigure()).setBackgroundColor(new Color(null, node.getColor()));//TODO cache color instances}

三、现在来创建我们自己的Request,因为目的是改变颜色,所以不妨叫做 ChangeColorRequest。它应当继承自org.eclipse.gef.Request,我们需要 ChangeColorRequest上带有两样信息:1.需要改变颜色的节点;2.目标颜色。因此它应该有 这两个成员变量。

import org.eclipse.gef.Request;import org.eclipse.swt.graphics.RGB;import com.example.model.Node;public class ChangeColorRequest extends Request{   final static public String REQ_CHANGE_COLOR="REQ_CHANGE_COLOR";   private Node node;   private RGB color;   public ChangeColorRequest(Node node, RGB color) {     super();     this.color = color;     this.node = node;     setType(REQ_CHANGE_COLOR);   }   public RGB getColor() {     return color;   }   public Node getNode() {     return node;   }   public void setNode(Node node) {     this.node = node;   }   public void setColor(RGB color) {     this.color = color;   }}

ChangeColorRequest看起来和一个JavaBean差不多,的确如此,因为Request的作用就是 传递翻译后的鼠标事件。如果你看一下org.eclipse.gef.Request的代码,你会发现Request 还有一个type属性,这个属性一般是一个字符串(在gef的RequestConstants里预定义了一些 ,如RequestConstants.REQ_SELECTION_HOVER), EditPolicy可以根据它决定是否处理这个 Request。在我们的例子里,顺便定义了这样一个常量字符串REQ_CHANGE_COLOR,在后面的 ChangeColorEditPolicy里会用到它。

四、现在有一个问题,这个Request的实例应该在哪里生成?答案是在Tool里,用户在画 布区域按下鼠标左键时,当前 Palette里被选中的Tool负责创建一个Request。我们现在面对 的这个需求需要我们创建一种新的Tool:ChangeColorTool。我们让ChangeColorTool继承 org.eclipse.gef.tools.SelectionTool,因为“上色工具”的用法和“选择工具”基本上差 不多。显然,我们需要覆盖的是handleButtonDown()方法,用来告诉gef如果用户当前选择了 这个工具,在画布区域按下鼠标会发生什么事情。代码如下:

import org.eclipse.gef.EditPart;import org.eclipse.gef.commands.Command;import org.eclipse.gef.tools.SelectionTool;import org.eclipse.swt.graphics.RGB;import com.example.model.Node;import com.example.parts.NodePart;public class ChangeColorTool extends SelectionTool {   private RGB color;   public ChangeColorTool(RGB color) {     super();     this.color = color;   }   /**   * If target editpart is an {@link NodePart}, create a {@link ChangeColorRequest} instance,   * get command from target editpart with this request and execute.   */   @Override   protected boolean handleButtonDown(int button) {     //Get selected editpart     EditPart editPart = this.getTargetEditPart();     if (editPart instanceof NodePart) {       NodePart nodePart = (NodePart) editPart;       Node node = (Node) nodePart.getModel();       //Create an instance of ChangeColorRequest       ChangeColorRequest request = new ChangeColorRequest(node, color);       //Get command from the editpart       Command command = editPart.getCommand(request);       //Execute the command       this.getDomain().getCommandStack().execute(command);       return true;     }     return false;   }}

五、有了Tool,还需要用ToolEntry把它包装起来添加到Palette里。所以我们创建一个名 为 ChangeColorToolEntry并继承org.eclipse.gef.palette.ToolEntry的类,覆盖 createTool ()方法,让它返回我们的ChangeColorTool实例。这个ChangeColorToolEntry代 码应该很容易理解:

import org.eclipse.gef.SharedCursors;import org.eclipse.gef.Tool;import org.eclipse.gef.palette.ToolEntry;import org.eclipse.jface.resource.ImageDescripTor;import org.eclipse.swt.graphics.RGB;public class ChangeColorToolEntry extends ToolEntry {   private RGB color;   public ChangeColorToolEntry(RGB color, String label, String shortDesc, ImageDescripTor iconSmall,       ImageDescripTor iconLarge) {     super(label, shortDesc, iconSmall, iconLarge);     this.color = color;   }   @Override   public Tool createTool() {     ChangeColorTool tool = new ChangeColorTool(color);     tool.setUnloadWhenFinished(false);//Switch to selection tool after performed?     tool.setDefaultCursor(SharedCursors.CROSS);//Any cursor you like     return tool;   }}

六、要把三个这样的ToolEntry添加到Palette里,当然是通过修改原来的PaletteFacTory 类。为节约篇幅,这里就不帖它的代码了,可以下载并参考示例代码PaletteFacTory.java里 的createCategories()和 createColorDrawer()方法。

到目前为止,ChangeColorRequest已经可以发出了,接下来要解决的问题是如何让 EditPart处理这个请求。

七、我们知道,gef里任何对模型的修改都是通过command完成的,因此一个 ChangeColorCommand肯定是需要的。它的execute()方法和undo()方法如下所示:

public class ChangeColorCommand extends Command{   private RGB oldColor;   @Override   public void execute() {     ldColor = node.getColor();     node.setColor(color);   }   @Override   public void undo() {     node.setColor(oldColor);   }}

八、EditPolicy负责接收所有的Request,所以还要创建一个 ChangeColorEditPolicy。在下面列出的代码里,你会看到我们定义了一个新的“Role”字符 串,过一会儿我们在EditPart上安装这个EditPolicy的时候要以这个字符串作为Key,以避免 覆盖EditPart上已有的其他EditPolicy。

import org.eclipse.gef.Request;import org.eclipse.gef.commands.Command;import org.eclipse.gef.editpolicies.AbstractEditPolicy;import org.eclipse.swt.graphics.RGB;import com.example.model.Node;public class ChangeColorEditPolicy extends AbstractEditPolicy {   final static public String CHANGE_COLOR_ROLE = "CHANGE_COLOR_ROLE";   @Override   public Command getCommand(Request request) {     //Judge whether this request is intersting by its type     if (request.getType() == ChangeColorRequest.REQ_CHANGE_COLOR) {       ChangeColorRequest theRequest = (ChangeColorRequest) request;       //Get information from request       Node node = theRequest.getNode();       RGB color = theRequest.getColor();       //Create corresponding command and return it       ChangeColorCommand command = new ChangeColorCommand(node, color);       return command;     }     return null;   }}

九、最后还是回到EditPart,前面在第二个步骤里我们曾经修改过的NodePart里还有最后 一处需要添加,那就是在installEditPolicies()方法里添加刚刚创建的 ChangeColorEditPolicy:

protected void createEditPolicies() {   //Add change color editpolicy   installEditPolicy(ChangeColorEditPolicy.CHANGE_COLOR_ROLE, new ChangeColorEditPolicy());}

现在我们已经完成了所有必要的修改,来看一下运行结果。

总结一下,需要创建的类有:ChangeColorRequest, ChangeColorTool, ChangeColorToolEntry, ChangeColorCommand, ChangeColorEditPolicy;需要修改的类有: Node, NodePart, PaletteFacTory。在实例项目里,为了方便大家浏览,所有新创建的类都 放在com.example.request包里,实际项目里还是建议分别放在对应的包里。

本文配套源码

也有伤心的,既有令人兴奋的,也有令人灰心的,

[Eclipse]GEF入门系列(十二、自定义Request)

相关文章:

你感兴趣的文章:

标签云: