Este ejemplo de código pertenece a la serie de tutoriales Eclipse RCP.
Eclipse GEF (Graphical Editing Framework) contiene tres componentes: Draw2d (org.eclipse.draw2d), GEF (MVC) (org.eclipse.gef) y Zest (org.eclipse.zest). Este tutorial trata sobre GEF MVC.
Un poco sobre GEF MVC
La idea clave de GEF es su diseño MVC. En resumen, Ver es la apariencia y Model almacena los datos para mostrar. El siguiente diagrama muestra la relación entre Modelo, Vista y Controlador. EditPart desempeña el papel de controlador. Lo que hace es obtener datos de un modelo y mostrarlos apropiadamente como una figura.
* Este diagrama es de www.eclipse.org/gef/reference/GEF%20Tutorial%202005.ppt – un tutorial de lectura obligada para el diseño de GEF.
Objetivo de esta aplicación
Eclipse GEF no es nuevo, así que encontré muchos tutoriales sobre Eclipse GEF. Algunos de ellos son demasiado simples y solo hablan de agregar algunas cifras. Otros hablan de hacer un editor. Lo que quiero hacer es agregar algunas figuras a una vista (no un editor) y conectar esas figuras.
En este tutorial, crearé algunas formas y agregaré conexiones entre ellas. Finalmente, se parece a lo siguiente:
Ahora comencemos.
Paso 1: crear un proyecto de complemento
Cree un proyecto de complemento llamado «GEFTutorial» y luego use el asistente para agregar una vista. Si no sabe cómo hacer esto, aquí se explica cómo crear un complemento con una vista.
Esta vista se cambiará para contener nuestras figuras que se crearán a continuación.
Paso 2: crear modelos
Los modelos almacenan datos sobre formas. Hay 3 modelos diferentes en este ejemplo: modelo raíz, modelo de nodo y modelo de conexión.
Modelo raíz es la raíz o contenedor de otros modelos. Como se muestra en el siguiente código, puede crear algunos modelos de nodos y conexiones entre ellos.
package gefmvc; import java.util.ArrayList; import java.util.List; public class Model { private List<NodeModel> nodes; public Model() { nodes = new ArrayList<NodeModel>(); for (int i = 0; i < 10; i++) { NodeModel node = new NodeModel("Node " + i ); nodes.add(node); } //set the connection here for (int i = 0; i < 10 - 1; i++) { NodeConnectionModel connection = new NodeConnectionModel(); connection.setSource((NodeModel) nodes.get(i)); connection.setTarget((NodeModel) nodes.get(i + 1)); ((NodeModel) nodes.get(i)).addSourceConnection(connection); ((NodeModel) nodes.get(i + 1)).addTargetConnection(connection); } } public List<NodeModel> getNodes() { return nodes; } } |
Modelo de nodo corresponde a una figura o forma en una aplicación GEF. Almacena las propiedades requeridas, como etiqueta, tamaño, posiciones, etc.
package gefmvc; import java.util.ArrayList; import java.util.List; public class NodeModel { private List<NodeConnectionModel> sourceConnections = new ArrayList<NodeConnectionModel>(); private List<NodeConnectionModel> targetConnections = new ArrayList<NodeConnectionModel>(); public NodeModel(String s) { label = s; } public String getLabel() { return label; } private final String label; public List<NodeConnectionModel> getSourceConnections() { return sourceConnections; } public List<NodeConnectionModel> getTargetConnections() { return targetConnections; } public void addSourceConnection(NodeConnectionModel iConnection) { sourceConnections.add(iConnection); } public void addTargetConnection(NodeConnectionModel iConnection) { targetConnections.add(iConnection); } } |
Modelo de conexión corresponde a los enlaces de conexión.
package gefmvc; /** * This class is the model for connections between AnodeModel objects. */ public class NodeConnectionModel { private NodeModel source; private NodeModel target; public void setSource(NodeModel s) { source = s; } public void setTarget(NodeModel t) { target = t; } } |
Paso 3: crear vista y controlador
Hay 3 tipos de EditPart en este ejemplo. El primero es el EditPart ‘raíz’ que contiene el elemento raíz del modelo. Es responsable del modelo raíz. Los otros dos son verdaderos EditParts de ‘controlador’, cada uno responsable de controlar un modelo específico (NodeModel o ConnectionModel).
Parte superiorEditar
package gefmvc; import java.util.List; import org.eclipse.draw2d.Figure; import org.eclipse.draw2d.FreeformLayer; import org.eclipse.draw2d.FreeformLayout; import org.eclipse.draw2d.GridLayout; import org.eclipse.draw2d.IFigure; import org.eclipse.draw2d.MarginBorder; import org.eclipse.gef.editparts.AbstractGraphicalEditPart; public class TopEditPart extends AbstractGraphicalEditPart { protected IFigure createFigure() { Figure f = new FreeformLayer(); f.setLayoutManager(new FreeformLayout()); f.setBorder(new MarginBorder(1)); // Create a layout for the graphical screen GridLayout gridLayout = new GridLayout(); gridLayout.numColumns = 3; gridLayout.horizontalSpacing = 40; gridLayout.verticalSpacing = 40; gridLayout.marginHeight = 20; gridLayout.marginWidth = 20; f.setLayoutManager(gridLayout); f.setOpaque(true); return f; } protected void createEditPolicies() { } protected List<NodeModel> getModelChildren() { List<NodeModel> children = ((Model) getModel()).getNodes(); return children; } } |
ÁnodoEditarParte
package gefmvc; import java.beans.PropertyChangeEvent; import java.util.List; import org.eclipse.draw2d.ChopboxAnchor; import org.eclipse.draw2d.IFigure; import org.eclipse.draw2d.Label; import org.eclipse.draw2d.PositionConstants; import org.eclipse.draw2d.RectangleFigure; import org.eclipse.draw2d.geometry.Rectangle; import org.eclipse.gef.ConnectionEditPart; import org.eclipse.gef.editparts.AbstractGraphicalEditPart; import org.eclipse.swt.graphics.Color; public class AnodeEditPart extends AbstractGraphicalEditPart { /** The figure's anchor. */ private ChopboxAnchor m_anchor; protected IFigure createFigure() { System.out.println("Called HelloEditPart.createFigure()"); IFigure rectangle = new RectangleFigure(); rectangle.setBackgroundColor(new Color(null, 200, 200, 200)); m_anchor = new ChopboxAnchor(rectangle); return rectangle; } protected void createEditPolicies() { System.out.println("Called HelloEditPart.createEditPolicies()"); } protected void refreshVisuals() { NodeModel node = (NodeModel)getModel(); //This is where the actual drawing is done, // Simply a rectangle with text Rectangle bounds = new Rectangle(50, 50, 50, 50); getFigure().setBounds(bounds); Label label = new Label(node.getLabel()); label.setTextAlignment(PositionConstants.CENTER); label.setBounds(bounds.crop(IFigure.NO_INSETS)); getFigure().add(label); } public void propertyChange(PropertyChangeEvent evt) { } protected List getModelSourceConnections() { List sourceConnections = ((NodeModel) getModel()).getSourceConnections(); return sourceConnections; } protected List getModelTargetConnections() { List targetConnection = ((NodeModel) getModel()).getTargetConnections(); return targetConnection; } protected ConnectionEditPart createConnection(Object iModel) { NodeConnectionEditPart connectPart = (NodeConnectionEditPart) getRoot() .getViewer().getEditPartRegistry().get(iModel); if (connectPart == null) { connectPart = new NodeConnectionEditPart(); connectPart.setModel(iModel); } return connectPart; } } |
NodeConnectionEditPart
package gefmvc; import org.eclipse.gef.editparts.AbstractConnectionEditPart; public class NodeConnectionEditPart extends AbstractConnectionEditPart{ protected void createEditPolicies() { } } |
GraphicalPartFactory es la fábrica para producir diferentes EditPart. (Es un patrón de diseño típico de fábrica).
package gefmvc; import org.eclipse.gef.EditPart; import org.eclipse.gef.EditPartFactory; public class GraphicalPartFactory implements EditPartFactory { public EditPart createEditPart(EditPart iContext, Object iModel) { System.out.println("Called GraphicalPartFactory.createEditPart(" + iContext + "," + iModel + ")"); EditPart editPart = null; if (iModel instanceof Model) { editPart = new TopEditPart(); } else if (iModel instanceof NodeModel) { editPart = new AnodeEditPart(); } if (editPart != null) { editPart.setModel(iModel); } return editPart; } } |
Paso 4: agregar a la vista
En el primer paso, creamos una vista. Ahora las formas y conexiones se pueden agregar a la vista.
package geftutorial; import org.eclipse.gef.EditPartFactory; import org.eclipse.gef.RootEditPart; import org.eclipse.gef.editparts.FreeformGraphicalRootEditPart; import org.eclipse.gef.ui.parts.ScrollingGraphicalViewer; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.widgets.Composite; import org.eclipse.ui.part.ViewPart; import gefmvc.*; public class View extends ViewPart { public static final String ID = "GEFTutorial.view"; // Use a standard Viewer for the Draw2d canvas private ScrollingGraphicalViewer viewer = new ScrollingGraphicalViewer(); // Use standard RootEditPart as holder for all other edit parts private RootEditPart rootEditPart = new FreeformGraphicalRootEditPart(); // Custom made EditPartFactory, will automatically be called to create edit // parts for model elements private EditPartFactory editPartFactory = new GraphicalPartFactory(); // The model private Model model; /** * This is a callback that will allow us to create the viewer and initialize * it. */ public void createPartControl(Composite parent) { // Create a dummy model model = new Model(); // Initialize the viewer, 'parent' is the // enclosing RCP windowframe viewer.createControl(parent); viewer.setRootEditPart(rootEditPart); viewer.setEditPartFactory(editPartFactory); // Inject the model into the viewer, the viewer will // traverse the model automatically viewer.setContents(model); // Set the view's background to white viewer.getControl().setBackground(new Color(null, 255, 255, 255)); } /** * Passing the focus request to the viewer's control. */ public void setFocus() { viewer.getControl().setFocus(); } } |
El proyecto final en la vista del Explorador de proyectos debería tener el siguiente aspecto:
Referencias:
1. Un breve documento: http://wiki.eclipse.org/GEF_Description
2. Interacciones del FMAM: http://www.eclipse.org/gef/reference/interactions.html