深入浅出Web Page Editor之二:布局引擎的四个层次

1. 简介

既然WPE是个html可视化编辑器,那么必然也会像浏览器一样有一个排版引擎之类的东西存在着。WPE的排版引擎主要可分为这么四个层次:

  •  CSS层
  •  HTML层
  •  Tag Converter层
  •  Edit层

给各层的命名可能与它们真实的作用稍微有点出入,但也一时找不到其它命名了。下面就对各层分别说明。

2. CSS层

CSS层决定了控件是如何绘制在Desinger视图的画布上的。其大部分类都在org.eclipse.jst.pagedesigner.css2这个包里。

2.1. ICSSStyle

ICSSStyle是CSS层的核心接口,它实际上就是对html标签中style=””部分的建模。

2.2. Figure和Layout

在WPE中的Figure继承关系可如下图所示。

其中需要注意的是FlowPage、CSSFigure,CSSTextFigure。如果您对GEF有过了解,就会知道一般对于每个EditPart言,都会在其CreateFigure函数中创建一个对应的Figure。其对应规则如下:

EditPart层 Figure层
ElementEditPart CSSFigure
DocumentEditPart FlowPage
TextEditPart CSSTextFigure

而相应的,在每个Figure类的实现里,也要根据需要重写setLayoutManager()函数以设定相对应的layout。例如div就对应着CSSBlockFlowLayout,img对应着CSSWidgetLayout。

2.3. 一些不被由css所决定的绘制内容

ITagEditInfo接口可以提供一信息以让绘制引擎决定是否给一个元素绘制边框。至于如何绘制边框,可以找到BorderUtil:: drawBorderDecorator()中去一探究竟。默认情况下,BODY和HTML标签在任何状态下都会被一个虚线框包络着。一个标签是否需要画边框,是由它的TagConverter的_needBorderDecorator属性所决定的。在CSSFigure的paintBorder中有如下的代码:

	protected void paintBorder(Graphics graphics) {
		CSSLayout layout = (CSSLayout) getLayoutManager();
		if (layout != null && !layout.handlingBorderForBlock()) {
			return;
		}

		ICSSStyle style = this.getCSSStyle();
		if (style != null) {
			CSSBorder border1 = new CSSBorder(this.getCSSStyle());
			border1.paint(this, graphics, NO_INSETS);

			// draw a border for those special elements like <h:form>, etc.
			ITagEditInfo editInfo = (ITagEditInfo) style
					.getAdapter(ITagEditInfo.class);

			if (editInfo != null && editInfo.needBorderDecorator()) {
				BorderUtil.drawBorderDecorator(this, graphics);
			}
		}
	}

3.HTML层

这里的HTMl层指的是在布局引擎中的HTML部分,它实际上就是一个中间层,指导如何根据Element建模中的数据来初始化其对应Figure。对应到代码上,就是形形色色的****FigureHandler。其创建过程可以在FigureFactory:: createFigureHandler中找到。

	private static IFigureHandler createFigureHandler(Element ele) {
		String tag = ele.getTagName();
		if ("input".equalsIgnoreCase(tag)) { //$NON-NLS-1$
			return new InputFigureHandler();
		} else if ("select".equalsIgnoreCase(tag)) { //$NON-NLS-1$
			return new SelectFigureHandler();
		} else if ("img".equalsIgnoreCase(tag)) { //$NON-NLS-1$
			return new ImgFigureHandler();
		} else if ("object".equalsIgnoreCase(tag)) { //$NON-NLS-1$
			return new ObjectFigureHandler();
		} else if ("textarea".equalsIgnoreCase(tag)) { //$NON-NLS-1$
			return new TextareaFigureHandler();
		} else if ("br".equalsIgnoreCase(tag)) { //$NON-NLS-1$
			return new BRFigureHandler();
		} else if (!HTMLUtil.isVisualHtmlElement(tag)) {
			return new HiddenFigureHandler(getSharedHTMLImage(ele));
		}
		else {
			return new DefaultFigureHandler();
		}
	}

这里以ImgFigureHandler为例,它实际上就是继承自AbstractFigureHandler,而在AbstractFigureHandler中有这样一段代码:

public void updateFigure(Element node, CSSFigure oldFigure) {
    setCurrentFigure(oldFigure);
    ICSSWidgetProvider provider = initializeWidgetProvider(node);
    oldFigure.setCSSStyle(provider.getCSSStyle());
    oldFigure
    .setFixedLayoutManager(new CSSWidgetLayout(oldFigure, provider));
}

很明显可以看出,它的两个参数一个是model层的结点,一个是view层的figure。通过这个函数,就可以根据node中所含信息,初始化figure中的内容。例如,设置figure的layout,设置figure的CSSStyle等等。

4.Tag converter层

在WPE中有一个tag converter机制。这个机制可以把一些用户自定定的html标签转化到标准的标签上,以方便绘制。它是工作在GEF的model层的。
基本上这些TagConverter都继承自AbstractTagConverter,在这个父类中定义了一个_hostElement和一个_resultElement。前者代表的是原始的model层结点元素,后者代码转换后的元素。view层在进行绘制时,只会根据_resultElement中的内容进行绘制。而这个转换的逻辑,存在于那些TagConverter的doConvertRefresh()函数中,例如DumTagConverter中就可以看到:

	protected Element doConvertRefresh() {
		Element result = createElement(getHostElement().getTagName());
		ConverterUtil.copyAllAttributes(getHostElement(), result, null);
		if (!internalIsWidget(result)) {
			copyChildren(getHostElement(), result);
		} else {
			dumCopyChildren(getHostElement(), result);
		}
		return result;
	}

正如其名,DumTagConverter只是简单的把原始的element及其子结点复制到_resultElement上去。
例如,如果开发人员想要创建一个“<abc></abc>”控件。通过tag converter机制可以把它在绘制的时候转换成<div><input/></div>的形式,就不用再专门去为这个控件写绘图的代码了。虽然已经经过了转换,但是其实数据的真实模型并没有发生改变。始终谨记,convter之后的标签仅仅是为了绘图方便。
一个Element究竟对应到哪一个TagConverter上,是由HTMLConverterFactory的createConverter()函数来进行判断的。

5.Edit层

Edit层是与GEF中的EditPolicy紧密相关的。它决定了如何对结点进行拖动、改变大小等操作。比如说支持对控件的拖动操作的DragMoveEditPolicy(),支持控件创建的ItemCreationEditPolicy()和支持控件大小改变的ElementResizableEditPolicy。

对于部分标签而言,它们被认为是NonVisable的,如<body></body>。但是NonVisable并非就真的在设计视图上看不见。它们是像下图中一样,以一个不可移动的小方块,外加一个标名其名称的label的形式展现出来的。

3 Responses

发表评论

电子邮件地址不会被公开。 必填项已用*标注