• [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() {
            oldColor = 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包里,实际项目里还是建议分别放在对应的包里。

    下载示例代码(在eclipse3.2.1和gef3.2下编译通过)

  • 相关阅读:
    CSS
    CSS样式
    CentOS/Ubuntu 搭载环境所遇问题
    XHTML 注意的地方
    HTML 全局属性 全局事件属性
    shell命令之---Linux文件权限
    shell命令之---使用Linux环境变量
    shell命令之---处理数据文件
    shell命令之---检测磁盘空间
    shell命令之---文件内容查看
  • 原文地址:https://www.cnblogs.com/bjzhanghao/p/792446.html
Copyright © 2020-2023  润新知