惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
GbyAI
GbyAI
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
博客园 - 三生石上(FineUI控件)
美团技术团队
Last Week in AI
Last Week in AI
WordPress大学
WordPress大学
L
LangChain Blog
雷峰网
雷峰网
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
博客园 - 叶小钗
Engineering at Meta
Engineering at Meta
腾讯CDC
Recent Announcements
Recent Announcements
The Register - Security
The Register - Security
有赞技术团队
有赞技术团队
Blog — PlanetScale
Blog — PlanetScale
博客园 - Franky
博客园 - 司徒正美
The Cloudflare Blog
Google DeepMind News
Google DeepMind News
T
Tailwind CSS Blog
C
Check Point Blog
小众软件
小众软件
V
Visual Studio Blog
V
V2EX
F
Full Disclosure
J
Java Code Geeks
MongoDB | Blog
MongoDB | Blog
罗磊的独立博客
人人都是产品经理
人人都是产品经理
量子位
Apple Machine Learning Research
Apple Machine Learning Research
F
Fortinet All Blogs
Microsoft Security Blog
Microsoft Security Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
博客园 - 【当耐特】
博客园_首页
Y
Y Combinator Blog
N
Netflix TechBlog - Medium
酷 壳 – CoolShell
酷 壳 – CoolShell
Stack Overflow Blog
Stack Overflow Blog
Recorded Future
Recorded Future
G
Google Developers Blog
Vercel News
Vercel News
大猫的无限游戏
大猫的无限游戏
Microsoft Azure Blog
Microsoft Azure Blog
U
Unit 42
爱范儿
爱范儿
Jina AI
Jina AI

博客园 - 刘刚

Google Map [导入]Editor中嵌入Editor [导入]GEF中修改默认的FeedBack [导入](接上篇)关于GEF Feedback的补充 [导入]GEF中导视图的使用 [导入]修改GEF中Connection的端点 [导入]Eclipse中Job API使用总结如下三 [导入]Eclipse中Job API使用总结如下二 [导入]Eclipse中Job API使用总结如下一 [导入]Eclipse调试框架的学习与理解二 [导入]Eclipse调试框架的学习与理解一 [导入]使用JET自动生成代码 [导入]定义自己的Common Navigator三 [导入]定义自己的Common Navigator二 [导入]定义自己的Common Navigator一 [导入]Eclipse运行(launcher)框架三 [导入]Eclipse运行(launcher)框架二 [导入]Eclipse运行(launcher)框架一 [导入]Eclipse属性页的支持
[导入]GEF理解系列九
刘刚 · 2008-01-01 · via 博客园 - 刘刚

网站: JavaEye  作者: liugang594  链接:http://liugang594.javaeye.com/blog/152149  发表时间: 2008年01月01日

声明:本文系JavaEye网站发布的原创博客文章,未经作者书面许可,严禁任何网站转载本文,否则必将追究法律责任!

到目前为止,其实我们已经有了一个虽然简单,但还算完整的应用。不过总是有点单调。

这一节我们给我们的Editor加一点其他的东西。严格来说已经不属性GEF的范围了。

一、增加属性页的支持

      在Eclipse里要支持属性页,你选择的对象必须是实现了ISelectionProvider接口。幸好,在GEF里,所有的选择都返回的是一个EditPart,而这些EditPart已经默认实现了这个接口,所以我们就省了很多事了。

      要想在属性页里显示内容,除了对象实现ISelectionProvider接口以外,我们还需要实现一个IPropertySource接口,用来提供属性页要显示的内容。更具体的可以看另一篇文章。这里我就直接进入主题了。

      如果你运行方式是RCP,那么要使得属性页可见,你需要在Perspective类的createInitialLayout(IPageLayout layout)方法里追加一句:

layout.addView("org.eclipse.ui.views.PropertySheet", IPageLayout.BOTTOM, 0.7f, layout.getEditorArea());

我们的Editor只是一个HelloWorld模型,所以没得选只能是它了,而且好像我们也只有一个值text好编辑,当然了,你也可以编辑大小,这里为了显得内容多一些,我把宽高和X、Y都加上了。

按照我们上面的说法:每次选中的都是HelloWorldEditPart。所以我们可以设置HelloWorldEditPart实现IPropertySource接口。然后实现它的五个方法如下(具体的就不介绍了):

  public Object getEditableValue() {
  return null;
 }

 public IPropertyDescriptor[] getPropertyDescriptors() {
  IPropertyDescriptor[] descriptors = new IPropertyDescriptor[5];
  descriptors[0] = new TextPropertyDescriptor(HelloWorldModel.P_TEXT,
    "name");
  descriptors[1] = new TextPropertyDescriptor("x", "x");
  descriptors[2] = new TextPropertyDescriptor("y", "y");
  descriptors[3] = new TextPropertyDescriptor("width", "width");
  descriptors[4] = new TextPropertyDescriptor("height", "height");
  return descriptors;
 }

 public Object getPropertyValue(Object id) {
  HelloWorldModel model = (HelloWorldModel) getModel();
  Rectangle constraints = model.getConstraints();
  if (id.equals(HelloWorldModel.P_TEXT)) {
   return model.getText();
  } else if (id.equals("x")) {
   return constraints.x+"";
  } else if (id.equals("y")) {
   return constraints.y+"";
  } else if (id.equals("width")) {
   return constraints.width+"";
  } else if (id.equals("height")) {
   return constraints.height+"";
  }
  return null;
 }

 public boolean isPropertySet(Object id) {
  return true;
 }

 public void resetPropertyValue(Object id) {
  HelloWorldModel model = (HelloWorldModel) getModel();
  if (id.equals(HelloWorldModel.P_TEXT)) {
   (model).setText("<unname>");
  } else {
   model.setConstraints(new Rectangle(0, 0, 100, 20));
  }
 }

 public void setPropertyValue(Object id, Object value) {
  HelloWorldModel model = (HelloWorldModel) getModel();
  Rectangle constraints = model.getConstraints();
  if (id.equals(HelloWorldModel.P_TEXT)) {
   ((HelloWorldModel) getModel()).setText(value.toString());
  } else {
   int parseInt = Integer.parseInt(value.toString());
   if (id.equals("x")) {
    constraints = new Rectangle(parseInt, constraints.y,
      constraints.width, constraints.height);
   } else if (id.equals("y")) {
    constraints = new Rectangle(constraints.x, parseInt,
      constraints.width, constraints.height);
   } else if (id.equals("width")) {
    constraints = new Rectangle(constraints.x, constraints.y,
      parseInt, constraints.height);
   } else if (id.equals("height")) {
    constraints = new Rectangle(constraints.x, constraints.y,
      constraints.width, parseInt);
   }
   model.setConstraints(constraints);
  }
 }

运行一下,这时属性页上已经可以显示我们选中的HelloWorldEditPart的信息了。通过属性,我们可以修改text值,还可以进行精确的定位:

propertyPage

图一

二、增加大纲视图

这里我们再来做一下增加大纲视图。如果你运行方式是RCP,那么要使得大纲页可见,你需要在Perspective类的createInitialLayout(IPageLayout layout)方法里追加一句:

layout.addView("org.eclipse.ui.views.ContentOutline", IPageLayout.RIGHT, 0.7f, layout.getEditorArea());

Outline视图和Property视图有点不一样的是:一般来说Property视图都是针对一个选中的对象。而Outline视图通常是针对当前激活的编辑器的整体。

要想让编辑器支持Outline视图,我们需要让重写一个方法:getAdapter(Class)。返回一个对应需要的类。例如这里我们的Outline视图要想被支持,我们需要如下完成:

 @Override
 public Object getAdapter(Class type) {
  if(type.equals(IContentOutlinePage.class)){
   return new DiagramEditorOutlinePage(getGraphicalViewer());
  }
  return super.getAdapter(type);
 }

这里的DiagramEditorOutlinePage是我们自定义一个类,它需要继承ContentOutlinePage。你可以有两个选择,一个就是普通的Eclipse提供的ContentOutlinePage类;另一个就是GEF提供的ContentOutlinePage。一般来说都需要继承GEF提供的那个。不过这里因为我只是想显示一个导航图。所以我还是用那个普通的。

我们给DiagramEditorOutlinePage传入了一个参数:GraphicalViewer。这样我们在我们的OutlinePage里就可以得到Editor的信息了。

最后就是完成我们DiagramEditorOutlinePage里的createControl方法。具体的就不说了。我的实现如下:

public class DiagramEditorOutlinePage extends ContentOutlinePage {

 private GraphicalViewer viewer;
 private Composite control;

 public DiagramEditorOutlinePage(GraphicalViewer graphicalViewer) {
  this.viewer = graphicalViewer;
 }

 @Override
 public void createControl(Composite parent) {
  control = new Composite(parent, SWT.NONE);
  control.setLayout(new GridLayout(1, false));
  control.setLayoutData(new GridData(GridData.FILL_BOTH));

  Canvas canvas = new Canvas(control, SWT.NONE);
  canvas.setLayoutData(new GridData(GridData.FILL_BOTH));

  LightweightSystem liSystem = new LightweightSystem(canvas);

  ScalableFreeformRootEditPart rootEditPart = ((ScalableFreeformRootEditPart) viewer
    .getRootEditPart());
  ScrollableThumbnail thumbnail = new ScrollableThumbnail(
    (Viewport) rootEditPart.getFigure());
  thumbnail.setSource(rootEditPart
    .getLayer(LayerConstants.SCALABLE_LAYERS));

  liSystem.setContents(thumbnail);
 }

 @Override
 public Control getControl() {
  return control;
 }

 @Override
 public void setFocus() {
  control.setFocus();
 }
}

其中Thumbnail的写法可以参考别人的实现;GMF中,默认Editor的实现就完成了这个功能。在GMF中,你可以去以下位置找到缺省的实现:

org.eclipse.gmf.runtime.diagram.ui.parts.DiagramEditor的方法:initializeOverview()。实现如下:

        protected void initializeOverview() {
            LightweightSystem lws = new LightweightSystem(overview);
            RootEditPart rep = getGraphicalViewer().getRootEditPart();
            DiagramRootEditPart root = (DiagramRootEditPart) rep;
            thumbnail = new ScrollableThumbnail((Viewport) root.getFigure());
            // thumbnail.setSource(root.getLayer(LayerConstants.PRINTABLE_LAYERS));
            thumbnail.setSource(root.getLayer(LayerConstants.SCALABLE_LAYERS));

            lws.setContents(thumbnail);
            disposeListener = new DisposeListener() {

                public void widgetDisposed(DisposeEvent e) {
                    if (thumbnail != null) {
                        thumbnail.deactivate();
                        thumbnail = null;
                    }
                }
            };
            getEditor().addDisposeListener(disposeListener);
            this.overviewInitialized = true;
        }

具体的就不过多探讨了。最后我们的实现图如下:

outlinePage

图二

 三、dirty标记的增加

一般来说,在Eclipse里,当Editor的内容发生改变时,会有个dirty标记,例如:dirty。但是目前我们的Editor上有编辑时并不会出现dirty标记。所以我们也应该给我们的Editor提供一个。

其实目前也不是对编辑完全无知。当我们做了一些修改,然后关闭时,会得到一个提示:

needsSave

说明我们的编辑器已经知道了有改动。之所没有显示dirty标记,是因为我们没有通知它。在Eclipse里,要想显示dirty标记是需要发出通知的。下面我们就来完成我们的dirty标记。

我们先理解几点:

1.我们都知道(假设)Eclipse里的Editor是通过方法:isDirty()的返回值来判断是否为脏。

2.Eclipse里的Editor并不是一直轮询isDirty()方法的,它是需要一个事件触发的。

3.我们都知道,所有的GEF里的操作都是通过command来完成的,而所有的command都是放在一个command堆栈中的。这个堆栈可以由editor直接得到。

有这三条,我觉得就够了:isDirty()方法的返回值就由命令栈是否为空决定了。所以就只剩下一个问题了:怎么发出通知机制?简单的想应该是在每个command执行和撤消时都需要发出这个通知。那如果真要这么实现的话,我想大家都会觉得很不好了。幸好,在GEF的Editor里就有一个方法可以完成这件事:commandStackChanged(EventObject event)。 在这个方法在每次命令栈有变化的时候都被会调用。

不知道大家有没有注意到:虽然我们的脏标记没有出来。但是我们的Actions是否可用的状态总是正确。说明这个action的状态总是被更新了。其实看看源码就知道是怎么回事了。commandStackChanged(EventObject event)的默认实现如下:

public void commandStackChanged(EventObject event) {
 updateActions(stackActions);
}

所以action的状态总是会被更新的。

刚才上面我也说到了,如果我们要自己完成发出事件机制,那么我们需要在每个command有执行或撤消的地方都发出一个事件。事实上GEF本身也是这样做的,我们看一下commandStackChanged(EventObject event)方法被调用的地方就知道了:

callStack

图三

那现在知道怎么做了没?好了,不说了,直接给出代码:

 @Override
 public boolean isDirty() {
  return getCommandStack().isDirty();
 }

 @Override
 public void commandStackChanged(EventObject event) {
  super.commandStackChanged(event);
  firePropertyChange(PROP_DIRTY);
 }

最后再编辑的时候:

isDirty

图四

本文的讨论也很精彩,浏览讨论>>

JavaEye推荐

文章来源:http://liugang594.javaeye.com/blog/152149