Friday, August 22, 2014

Graphical Views for Xtext

Xtext provides you with a powerful IDE and a rich featured text editor for your domain-specific language with little effort. But sometimes, a picture says more than a thousand words: You want to have some additional graphical representation of your models, a set of diagrams.

Diagrams are superior to code when it comes to high-level views. But while programmers can easily cope with files that contain several hundred lines of code, the same amount of information usually blows a diagram and destroys all its suggestiveness. Diagrams with just a few nodes and edges showing on a certain aspect of the models only are best fit for human readers. Such aspects are often spread across various model files. So in order to add the best value for the language users on top of an Xtext infrastructure, we need to allow them to create multiple diagrams, each highlighting just a subset of model information, picked from multiple model files. In other words, diagrams that have a completely different structure than their associated textual models.

Traditional graphical editing frameworks focus on editing the underling model through the diagram.
But synchronizing the model changes from textual and diagram editors is very hard if their content's structures differ. In most cases, the integration will lead to major usability quirks like unexpected editor behavior, forced save operations, blocked editors or even data loss.

So rather than working around the hard challenges of integrating graphical and textual model editing, we can leave model modification to Xtext. For the graphical stuff we can concentrate on diagram editing and leave the underlying model read-only. This way we can spend our energy on the things that really matter to the user, like easy and useful ways to populate diagrams or best visual appearance.

In Eclipse, one could build such graphical views using Zest or GEF. If model and diagram do not differ too much in structure, a small code generator targeting GraphViz is a very simple solution with high quality output.

The following screencast shows another solution for an Xtext-based domain model language. The graphical editor is using FXDiagram, a diagram framework based on JavaFX. FXDiagram offers a very smooth user experience, excellent rendering, support for touch-pad gestures, animated undo/redo, diagram persistence, export to scalable vector graphics and much more. If you are interested in learning more, feel free to contact me.



Friday, November 1, 2013

Where no diagram editor has ever gone before...

I should have been more suspicious about the Ferengi salesman I bought my LCARS system from. Ferengi have always been skeptical towards JavaFX. I should have known that this device had been tampered with in order to bring my system down in the middle of my demo at EclipseCon Europe. Ferengi petaq!

Nevertheless, I could organize a new working LCARS terminal now and here is my demo. It demonstrates JavaFX's flexibility to create a diagram editor that does neither look like nor behave like a dull lines and boxes editor.

I extracted the data from the StarTrek fansite Memory Alpha and stored it in a local mongoDB. The font is from GTJLCARS. SVG export extends some code from the JFXtras project initially written by Gerrit Grunwald. Thanks to all of you, guys.

EclipseCon Europe was once again big fun: I attended a number of cool presentations, met a lot of old friends and got acquainted with a bunch of new people. Thanks for the organizers to provide such a fine event. But without any further delay, here's the demo:

Tuesday, October 22, 2013

Eclipse Diagram Editors From A User's Perspective

I believe that the Eclipse's diagram editing frameworks have a lot to catch up when it comes to usability. For illustration I've created a screencast on how a typical Eclipse graphical editor appears to a user. I have used the Ecore Tools editor as an example, but most issues presented are caused by defaults of GMF or GEF which are pretty hard to change. So don't be surprised if you re-encounter these or similar issues in Graphiti, Sirius etc. The screencast doesn't even cover the rendering errors described in my previous blog post.



So what has gone wrong?

In my opinion, diagram editor behavior and editing metaphors haven't really changed since the first commits on the GEF project in 2002. Advances in UI design and usability have had almost no priority on Eclipse's graphical frameworks since then. They were built from a pure developer's perspective. So a lot of effort has been spent on:
  • Facilitating the development of new editors. This usually causes a restriction to very specific use cases. 
  • Establishing weird processes to implement new diagram editors. 
  • Repeatedly putting new abstraction layers on top of the existing ones. Hide the seemingly ugly or incomprehensible concepts of a base framework. The problem is to find better abstractions that don't leak. In the end you often have to learn both (or more) frameworks in order to use them. 
  • Building on top of existing frameworks with serious limitations, like integer coordinates, missing alpha channel, no hardware acceleration etc. 
  • Supporting very large diagrams. Yes, there are very big models. But diagrams with hundreds of nodes and connections only make good wallpapers.
  • Creating new compatibility APIs, e.g. to replace the rendering engine. These will limit the available functionality to the common minimum set of features of all backends. 
  • Changing a model graphically. Note the difference between editing a diagram – e.g. to make it look nicer – and changing the underlying model. Did you ever wonder why GEF Zest is the only Eclipse diagram framework with its own diagram layout algorithms and basic support for multi-touch gestures? 
Computers don't need diagrams. Diagrams are for humans. Their strength is to explain a part of a model in a very suggestive way. Taking the human user into focus changes the requirements for a graphical framework drastically. Issues like the following must have top priority:
  • Appealing visual design, 
  • Intuitive editing metaphors, 
  • Snappy behavior, 
  • Support for modern input devices, and 
  • The possibility for individual customization. 
How do we get there? Attend my talk Eclipse Diagram Editors - An Endangered Species at EclipseCon Europe for some propositions and an extensive demo.

EclipseCon Europe 2013

Friday, October 11, 2013

Multi-touch Gestures For JavaFX Apps Running in SWT

EclipseCon Europe 2013 I am currently preparing a demo for my talk at EclipseCon Europe 2013. Embedding my JavaFX based graphical editor into Eclipse went smoothly thanks to the FXCanvas that bridges SWT and JavaFX. But I realized that multi-touch gestures did no longer work.

I filed a bug for that, but I could not resist trying to find out what's wrong myself. You may not know that the sources of JavaFX are open now. So after cloning their mercurial repo and browsing a couple of classes I found out that SWT's GestureEvents are neither converted to JavaFX GestureEvents nor transferred to an embedded JavaFX canvas. The implementation was simply missing.

So with some help by Tom Schindl and Steve Northover (thanks a lot, guys!) I hacked a solution that works fine for me, as you can see in the screencast below. If you're interested you can download my the code from github.

Saturday, September 7, 2013

Obi-Wan at the Edge

There are two hard problems in computer science: cache validation, variable substitution, and off-by-one errors.      (unknown source)
The uncounted latter is often referred to as OBOE or, with a geek sense of humor, "Obi-Wan error".

Obi-Wan In Eclipse Diagram Editors

In an older post, I already mentioned one major weakness of GEF (3.x): All coordinates are integers. One problem with that is that it can easily result in off-by-one rendering bugs. That unfortunately holds for many frameworks built on top of GEF. Have a close look at the gradient fill, anchor points (where connections are touching the nodes), clipping bounds and the arrow head in the following screenshots from the standard examples:


Integer vs. double coordinates is comparable to raster vs. vector images. While in integer coordinates a point usually means a pixel, in double coordinates a point has no area and a line can be infinitesimally thin. In a world of double precision it is obvious that we need some rendering. We are forced to think about things like line width, stroke type, line caps, line joins, miters, etc. There are excellent explanatory pictures on these in the JavaDocs of JavaFX's Shape class.

"Smooth" Curves

You have to take round-off errors into account when approximating smooth curves (splines) with integer polylines. Otherwise your allegedly smooth curve will go zigzag. This might be the reason why GEF does not have any curves. GMF does:

 

Arrow Heads

I'd expect an edges arrow head them to be aligned with the tangent of the edge at the anchor point. For polylines this is the line connecting the anchor with the last bendpoint. Same for Bézier curves, but apparently not for splines like these:

  
It is also a good idea to prohibit bendpoints too close to the anchor: The inclination of the curve may become too steep:

The last screenshot also shows that the line cap style plays a role at the tip of the arrow head.

 

Use the Force

GEF 4 will have double coordinates and Bézier curves. But at the time of writing this blogpost, there hasn't been an official release yet. It is not clear either whether and when it will be adopted by downstream projects.

I came across all this when trying to calculate edges and arrow heads for my sparetime JavaFX diagram editor project. Luckily, JavaFX uses double coordinates only. JavaFX also includes quadratic and cubic Bézier curves. These curves are really smooth and drawn fast, though I must admit I'd  have expected B-Splines at least.

I am going to demonstrate this at EclipseCon Europe 2013. Stay tuned for more.

Wednesday, June 12, 2013

Using Xcore in Xtext

With Xtext you can easily create your own textual language. The structure of the language is defined in an Ecore model. This model can be automatically derived from the grammar, which allows quick turnarounds in the beginning. But in the long run, this model becomes an increasingly important API to the language. You want to fix it and make it a first class artifact: You are switching from an inferred Ecore model to an imported one in the grammar.

Traditionally, you use EMF's tree editors to edit Ecore models. That can be cumbersome as the tooling is not the nicest to use. Furthermore, the Ecore model only describes the declarative part. Behavior, such as the body of EOperations or the code for derived EStructuralFeatures has to be woven in either from additional models or directly in the generated code. These and more issues are addressed by Xcore. Xcore allows to specify Ecore models textually, including the behavioral parts and the code generation parameters. Having a single source of information facilitates the maintanance of such a model drastically. Xcore is shipped with EMF.

In the upcoming Eclipse release 4.3 (Kepler) Xcore and Xtext can be used together. Here is a step-by-step example how to do this with Xtext's "Greetings" language:
  1. Create an Xtext project File > New > Project > Xtext Project. The defaults are fine. This will create the usual four projects for your Xtext language in your workspace.
  2. By default, Xcore generates its code to the folder src-gen. The same folder is used by Xtext and will be wiped each time you regenerate the language from the grammar. To avoid loosing data, choose a separate output folder for Xcore: Eclipse/Window > Preferences > Xcore > Compiler > Output Folder > Directory > ./emf-gen

  3. Create a new folder model in the root of org.xtext.example.mydsl and file Greetings.xcore inside that folder. Our Ecore model consists of an EPackage org.xtext.example.mydsl, two EClasses Model and Greetings, a containment EReference greetings and an EAttribute name. In Xcore, this looks like
:

  4. Open the grammar MyDsl.xtext. By default, the Ecore model myDsl is generated from the grammar rules. We want to replace this with the Ecore model specified in the Xcore file:

  5. We have to load the Xcore model in the workflow GenerateMyDsl.mwe2 of the language generator. Add an appropriate loadedResource entry to the language section:

    module org.xtext.example.mydsl.GenerateMyDsl
    ...
    Workflow {
      ...
      component = Generator {
        ...
        language = auto-inject {
          loadedResource =
    "platform:/resource/${projectName}/model/Greetings.xcore"
          uri = ...
    
  6. You have to add a dependency to org.eclipse.emf.xcore in the MANIFEST.MF of the project.
  7. Now generate the language infrastructure Run As > MWE2 Workflow. There should not be any error.
  8. Make sure that all folders will be correctly deployed. Your build.properties file should look like this
:
  9. Launch a new Eclipse workbench Run > Run Configurations > Eclipse Application > Launch Runtime Eclipse and enjoy your language in action.
For the geeky fun of it: As Xcore is itself implemented in Xtext with an imported Ecore model, this lays the foundation to replace Xcore.ecore with Xcore.xcore and thereby bootstraping it.
Sorry, I couldn't resist ;-)

Monday, May 20, 2013

Another Xtend and JavaFX Story

For my spare time JavaFX project, I was looking for a way to export a scene to SVG. Googling a bit I stumbled over a blog entry by Gerrit Grunwald who implemented an JavaFX shape to SVG path converter class for the JFXtras project. It looked good so I decided to give it a try.

 

Java to Xtend

The class is of course written in Java. Even though there is no technical reason - Java and Xtend classes can coexist in the same project without any problems - I wanted to convert it to Xtend as well. I am a lazy guy. Luckily Krzysztof Rzymkowski had recently posted on the Xtend group that he had started to implement a Java to Xtend converter. It even has a web interface, and except for one slight issue with a for-loop it worked like a charm. Great work, Krzysztof!

Of course the resulting code is pretty Java-like, so I wanted to improve on it using more of the cool Xtend features. I found quite a few spots to do so, reducing the amount of code significantly and enhancing readability a lot. The remainder of this post is about this ongoing love of JavaFX and Xtend. The complete source code will likely be made open-source soon.

 

Dispatch Methods

The class starts with a method that only contains an instanceof-cascade to delegate to the conversion method for the specific subclass of Shape the parameter has:
public static String shapeToSvgString(final Shape SHAPE) {
   final StringBuilder fxPath = new StringBuilder();
   if (Line.class.equals(SHAPE.getClass())) {
      fxPath.append(convertLine((Line) SHAPE));
   } else if (Arc.class.equals(SHAPE.getClass())) { 
      fxPath.append(convertArc((Arc) SHAPE)); 
   } else if (QuadCurve.class.equals(SHAPE.getClass())) {
      fxPath.append(convertQuadCurve((QuadCurve) SHAPE));
   } ... 
In Xtend I can use dispatch methods to realize this: They must have the same method and number of parameters, but different parameter types. The Xtend compiler then generates the dispatcher method with the instanceof-cascade automatically. So renaming the delegate methods and marking them as dispatch made the dispatcher method obsolete.
def dispatch String toSvgString(Line line) ...
def dispatch String toSvgString(Arc arc) ...
def dispatch String toSvgString(QuadCurve quadCurve) ...
Tip: If you want to delegate from one dispatch case to another, the original methods are available with an underscore preceeding their name.

Templates

An SVG path is kind of a cryptic string. In the Java code it is assembled using a StringBuilder, e.g.
final StringBuilder fxPath = new StringBuilder();
fxPath.append("M ").append(CENTER_X).append(" ")
   .append(CENTER_Y - RADIUS).append(" ");
fxPath.append("C ")
   .append(CENTER_X + CONTROL_DISTANCE)
   .append(" ").append(CENTER_Y - RADIUS).append(" ")
   .append(CENTER_X + RADIUS).append(" ")
   .append(CENTER_Y - CONTROL_DISTANCE)
   .append(" ")
   .append(CENTER_X + RADIUS).append(" ")
   .append(CENTER_Y).append(" ");
...
That's the best you can do with Java. In Xtend we have template expressions - multiline strings which can be interrupted with values from expressions. Even IF-conditions and FOR-loops are supported. With carefully chosen regular expressions for find/replace and some manual fine-tuning the above becomes nicely readable
'''M «centerX» «centerY - radius»
   C «centerX + controlDistance» «centerY - radius» 
   «centerX + radius» «centerY - controlDistance» 
   «centerX + radius» «centerY»
...

Switch Expression

I found another finer-grained instanceof-cascade in the convertPath method:
final StringBuilder fxPath = new StringBuilder();
for (PathElement element : PATH.getElements()) {
   if (MoveTo.class.equals(element.getClass())) {
      fxPath.append("M ")
         .append(((MoveTo) element).getX()).append(" ")
         .append(((MoveTo) element).getY()).append(" ");
   } else if (LineTo.class.equals(element.getClass())) {
      fxPath.append("L ")
         .append(((LineTo) element).getX()).append(" ")
         .append(((LineTo) element).getY()).append(" ");
   } else if (CubicCurveTo.class.equals(element.getClass())) { 
      fxPath.append("C ")
...
As the bodies of the if-statements are so simple, I decided to use Xtend's switch instead. It allows to use type guards for the cases and automatically cast the switch variable to that type inside the case's body:
val it = new StringBuilder
for (element : path.elements) {
   switch element {
      MoveTo: append('''M «element.x» «element.y» ''')
      LineTo: append('''L «element.x» «element.y» ''')
      CubicCurveTo: append('''C «element.controlX1»...
I could further use the operator => instead of the Builder class and the operator <=> replacing Double.compare.

Extension Import (Client Side)

From a client side, the shapeToSvgString method is a utility method for Shapes. In Xtend, you can import such methods using a static extension import. That makes them callable in extension syntax, as if the method was defined in the class of the first parameter. To further improve readability, I renamed the methods to toSvgString such that I can now write
import static extension ...ShapeConverter.*
... 
new Rectangle.toSvgString