GEF编辑器遵循MVC模式。在这里Controller既是org.eclipse.gef.EditPart。它的行为是可以被一系列的策略(org.eclipse.gef.EditPolicy)确定的,见AbstractEditPolicy的方法:
/** * Creates the initial EditPolicies and/or reserves slots for dynamic ones. * Should be implemented to install the inital EditPolicies based on the * model's initial state. <code>null</code> can be used to reserve a "slot", * should there be some desire to guarantee the ordering of EditPolcies. * * @see EditPart#installEditPolicy(Object, EditPolicy) */ protected abstract void createEditPolicies();
我们自己定义的Policy可以在这个位置添加。
下面来定义一个鼠标移动到节点上即可以做某些事情的Policy,该Policy要实现如下功能:
1、鼠标移动到节点上的时候,绘制handle
2、鼠标点击任何位置,handle消失
3、鼠标移动到其他节点,绘制新的handle,之前的handle消失。
先看下效果图:
1、这是一个普通的GEF节点,红色和绿色手型标示着连线的端点
2、当鼠标移动到节点上,并且停留数秒的时候,绘制一个橙色手型覆盖红色手型:
3、当鼠标点击该橙色手型,则出现可拖拽的连线提示(该部分功能由handle实现,暂不讨论):
先来看看Policy的实现:
/** * * @author caiyu * */ public abstract class CommonHoverEditPolicy extends GraphicalEditPolicy { public static final String ROLE = "CommonLineAssistantEditPolicy"; protected IFigure handleLayer; /* the List of handles */ protected List<Handle> handles; /* the Hover Listener */ private MouseMotionListener hoverListener; /* listener for every handle */ private MouseMotionListener handleListener; private LayoutListener removeListener; private Timer timer; private boolean hovering = false; protected int TIME_INTERVAL = 1000;
public void activate() { super.activate(); addHoverListener(); addHandleListener(); } private void addHandleListener() { handleListener = new MouseMotionListener() { public void mouseDragged(MouseEvent me) { } public void mouseEntered(MouseEvent me) { } public void mouseExited(MouseEvent me) { cancelTimer(); startTimer(); } public void mouseHover(MouseEvent me) { } public void mouseMoved(MouseEvent me) { } }; } protected void addHoverListener() { final IFigure figure = getHostFigure(); hoverListener = new MouseMotionListener() { public void mouseDragged(MouseEvent me) { } public void mouseEntered(MouseEvent me) { } public void mouseExited(MouseEvent me) { startTimer(); } public void mouseHover(MouseEvent me) { cancelTimer(); addHoverHandles(); hovering = true; } public void mouseMoved(MouseEvent me) { } }; /** * 被删除的时候清除监听 */ removeListener = new LayoutListener() { @Override public void invalidate(IFigure container) { } @Override public boolean layout(IFigure container) { return false; } @Override public void postLayout(IFigure container) { } @Override public void remove(IFigure child) { if (child == figure) { clearHoverHandles(); cancelTimer(); figure.getParent().removeLayoutListener(removeListener); } } @Override public void setConstraint(IFigure child, Object constraint) { } }; if (figure != null) { figure.addMouseMotionListener(hoverListener); if (figure.getParent() != null) figure.getParent().addLayoutListener(removeListener); } } /** * Adds the handles to the handle layer. */ protected void addHoverHandles() { clearHoverHandles(); handleLayer = getLayer(LayerConstants.HANDLE_LAYER); handles = createHoverHandles(); for (int i = 0; i < handles.size(); i++) { IFigure handle = (IFigure) handles.get(i); handleLayer.add(handle); handle.addMouseMotionListener(handleListener); } } protected void clearHoverHandles() { if (handles == null) return; if (handleLayer == null) handleLayer = getLayer(LayerConstants.HANDLE_LAYER); for (int i = 0; i < handles.size(); i++) { IFigure handle = (IFigure) handles.get(i); try { handleLayer.remove(handle); } catch (Exception e) { e.printStackTrace(); } if (handleListener != null) handle.removeMouseMotionListener(handleListener); } handles = null; } protected void cancelTimer() { if (timer != null) { timer.cancel(); } } protected void startTimer() { if (!hovering) return; if (timer != null) { timer.cancel(); } timer = new Timer(true); timer.schedule(new TimerTask() { public void run() { Display.getDefault().syncExec(new Runnable() { public void run() { clearHoverHandles(); } }); } }, TIME_INTERVAL); } protected abstract List<Handle> createHoverHandles(); }
代码有点多,不熟悉Policy的话,可以先从activate()方法读起。这个Policy的作用是为hostFigure(就是MVC中的Viewer部分,也是该policy的宿主)添加监听,通过一个Timer来控制handle的展示和消失。
该类为abstract类型,其实现子类需要提供createHoverHandles()的实现,该方法返回一个List<Handle>。
如此,就可以把handle的效果和这个Policy结合起来了。
下面再提供一个利用CommonHoverEditPolicy实现预览效果的例子。
先看效果图:
1、这是一个普通的GEF编辑器:
2、其中的每一个节点内容是被另一个表格编辑器所确定的:
3、当鼠标移动到节点上,稍作停留,便会弹出一个预览框:
该预览框具备之前的表格编辑器的所有浏览功能,可以点击,可以切换子页。
实现方式:
1、重写CommonHoverEditPolicy的createHoverHandles()方法。
TablePreviewDialog dialog = null; @Override protected List<Handle> createHoverHandles() { if (dialog != null && !dialog.close()) { dialog.close(); } try { BtdFormEditor editor = new BtdFormEditor(false); ModelEditorInput input = new ModelEditorInput( (IBaseModel) getHost().getModel()); IWorkbenchWindow dw = PlatformUI.getWorkbench() .getActiveWorkbenchWindow(); editor.init(new MultiPageEditorSite((MultiPageEditorPart) dw .getActivePage().getActiveEditor(), editor), input); dialog = TablePreviewDialog.getTablePreviewDialog(editor); dialog.open(); } catch (PartInitException e) { e.printStackTrace(); } return new ArrayList<Handle>(); }
可以看到,这里并没有提供可用的Handle(List<Handle>的内容是空的)。但是我们提供了一个Dialog,该Dialog为单例模式,通过对它的close和open控制,来实现handle一样的效果。使用dialog而非handle的主要原因是,handle是基于draw2d的,内容需要绘制,但是我们需要的内容是一个SWT\Jface构成的表格编辑器。所以使用dialog比较合适。
TablePreviewDialog定义如下:
/** * 表格内容预览 * * @author caiyu * @date 2012-8-17 */ public class TablePreviewDialog extends Dialog { private static TablePreviewDialog INSTANCE = null; public static TablePreviewDialog getTablePreviewDialog(FormEditor editor) { if (INSTANCE != null) { INSTANCE.setEditor(editor); return INSTANCE; } INSTANCE = new TablePreviewDialog(editor); return INSTANCE; } private int x; private int y; private int width = 560; private int height = 420; private FormEditor editor; private TablePreviewDialog(FormEditor editor) { super(Display.getDefault().getActiveShell()); this.editor = editor; setRectangle(getRectangle()); } protected Control createContents(Composite parent) { // create the top level composite for the dialog Composite composite = new Composite(parent, 0); GridLayout layout = new GridLayout(); layout.marginHeight = 0; layout.marginWidth = 0; layout.verticalSpacing = 0; composite.setLayout(layout); composite.setLayoutData(new GridData(GridData.FILL_BOTH)); applyDialogFont(composite); // initialize the dialog units initializeDialogUnits(composite); // create the dialog area and button bar dialogArea = createDialogArea(composite); getShell().addShellListener(new ShellAdapter() { public void shellDeactivated(ShellEvent e) { close(); } }); return composite; } protected Rectangle getRectangle() { Rectangle rectangle = Display.getCurrent().getBounds(); rectangle.x = Display.getCurrent().getCursorLocation().x; rectangle.y = Display.getCurrent().getCursorLocation().y; return rectangle; } public void setEditor(FormEditor editor) { this.editor = editor; setRectangle(getRectangle()); } public void setRectangle(Rectangle rectangle) { this.x = rectangle.x; this.y = rectangle.y; int max_width = rectangle.width; int max_height = rectangle.height; if (x + width > max_width) { x = x - width; } if (y + height > max_height) { y = y - height; } } /* * (non-Javadoc) * * @see * org.eclipse.jface.dialogs.Dialog#createDialogArea(org.eclipse.swt.widgets * .Composite) */ protected Control createDialogArea(Composite parent) { parent.setLayout(new GridLayout(1, false)); parent.addMouseListener(new MouseAdapter() { public void mouseUp(MouseEvent e) { // okPressed(); } }); parent.addKeyListener(new KeyAdapter() { public void keyReleased(KeyEvent e) { if (e.keyCode == SWT.ESC) okPressed(); } }); Composite client = new Composite(parent, SWT.NONE); client.setLayout(new FillLayout()); client.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); editor.createPartControl(client); return parent; } protected Point getInitialLocation(Point initialSize) { return new Point(x, y); } protected Point getInitialSize() { return new Point(width, height); } protected int getShellStyle() { return SWT.NO_TRIM; } protected void createButtonsForButtonBar(Composite parent) { } protected void okPressed() { // toolkit.dispose(); super.okPressed(); } public void dispose() { okPressed(); } }
以上,即完成了为GEF添加一个预览策略及内容。