|
There are many new versions of GWT appearing these days: 2.0.4, 2.1.0M1 and an upcoming 2.1.0M2. Because lib-gwt-svg integration with UiBinder uses internal GWT classes (alas there is no public API for extending UiBinder parsers yet), it needs to follow these changes very closely to remain compatible. I have issued a number of versions myself to keep up, and the following table explains which version of lib-gwt-svg you should use which a given GWT version. Functionally, the lib-gwt-svg versions are all equivalent.
GWT version |
lib-gwt-svg version |
2.0.3 |
0.4.6 |
2.0.4 |
0.4.7 |
2.1.0M1 |
0.4.8 |
You can obtain these versions from the maven repository (http://www.vectomatic.org/mvn/org/vectomatic/lib-gwt-svg/) or from the SVN repository (http://code.google.com/p/vectomatic/source/browse/)
I have also fixed a bug I introduced in the previous version of the Connect the dots game and which rendered the first level of the game unplayable.
Lukas
This posts gives an overview of the new developments which have been made available today
I am releasing a new version of lib-gwt-svg (version 0.4.6). As can be seen in the release notes, the emphasis is mostly on fixing bugs and making the API easier to use. Here is a high level view of the main changes:
Generics
I have finally bitten the bullet, read the excellent tutorial on Java Generics by G. Bracha and generified the API.
Collections
I have changed the implementation of the SVG collection classes. They have been promoted from pure overlay types to wrapper types (see my post on goals and design for more explanations of what I mean by that). They now implement java.lang.Iterable, so you can write code like this:
OMSVGDocument document = OMSVGParser.currentDocument();
OMNodeList<OMSVGRectElement> rectList = document.getElementsByTagNameNS(SVGConstants.SVG_NAMESPACE_URI, SVGConstants.SVG_RECT_TAG);
for (OMSVGRectElement rect : rectList) {
...
}
GWT XPath expressions
lib-gwt-svg now offers a new DOMHelper.evaluateXPath() XPath evaluation function, which lets you do really powerful computations with little code. Imagine you have an SVG drawing containing many circles and you want to increase their radii by 10%. Here you go:
OMSVGSVGElement svg = OMSVGParser.parse(...someURL...);
Iterator<OMSVGCircleElement> iterator = DOMHelper.evaluateXPath(svg, ".//svg:circle", new SVGPrefixResolver());
while (iterator.hasNext()) {
OMSVGLength radius = iterator.next().getR().getBaseVal();
radius.setValue(1.1f * radius.getValue());
}
Another problem I find XPath very good at is to replace getElementById(). getElementById does not work in all circumstances. If called too early (before the node is inserted in the DOM structure), it will fail. You can easily devise an XPath which will do the same thing and work is all circumstances. One area where I think this pattern should be applied is UiBinder HTML binding. UiBinder currently relies upon adding temporary “id” attributes to the elements you want to bind to, then removing it before it gives control back to you. However UiBinder will fail if the HTML node you want to bind to already has an id: it is either ui:field or ‘id’. If XPath where used instead, their less intrusive nature would let one use both ui:field and ‘id’ in the same DOM node. This is the way lib-gwt-svg UiBinder integration works.
One final note on XPath: they seem to work very well on all browsers: Webkit based, Firefox and Opera.
Events
As explained in the goals and design post, lib-gwt-svg uses overlay types internally. These overlay types are then embedded in wrapper types which provide a more java-friendly view of the SVG types and lets them implement interfaces, notably GWT event registration interfaces. Though it is straightforward to go from the wrapper type to the underlying overlay type, the other conversion is more costly: you need to instantiate a new wrapper and wrap the overlay type in it. Up to version 0.4.5 included, the wrapper type also carried the HandlerManager which stores event registration information. Thus, the wrapper was not truly hollow and you could end up with event registration information for the same overlay type contained in several instances of HandlerManager owned by several wrappers. With version 0.4.6, there is just one HandlerManager per SVG object, and it is carried by the overlay type itself.
The samples work correctly with all SVG enabled browsers (except the SMIL animation sample, which works only in Opera and FF3.7 alpha4).
The samples can now be resized, as should every SVG application.
I am releasing two new puzzle games, which leverage the same SVG feature: the ability to create arbitrary clip path. This is very convenient to cut one image into several pieces and layout the pieces in arbitrary order. If you want to explore the possibilities of this pattern, here is a trick which is not available from the UI: try to launch the game with the following parameters added to the URL: ?connector=SQUARE and ?connector=NONE.
I have also reorganized the program so that navigation works better: going back to the main menu no longer requires the game to restart. I am just swapping DOM elements to achieve the same result.
The program was originally developed and tested with FF3.x and works really well on this platform (except a minor bug in the display of the “push-push” game title, fixed by FF3.7a4). With this release, I have also tested it on Opera: there are no bugs I am aware of on this platform. I tried to do the same with Webkit-based browsers (Safari and Chrome) but there a currently several bugs and missing features and these browsers quickly crash when running the games. So I was faced with two possible choices: either recode the games to bypass these bugs and missing features, or file bug reports help these browser improve. As you guess, I chose the “lazy” path for the moment and went for the second option. You can check the browser bugs page for the list of bugs I have filed.
I am starting a Google Group to answer questions about lib-gwt-svg and discuss issues and possible uses of the library. The URL is: https://groups.google.com/group/lib-gwt-svg. Feel free to ask your questions there, I will do my best to answer them as quickly as possible
Many new exciting new features have been released today
A new version of lib-gwt-svg (version 0.4.5) has been released. It provides:
- support for SVG push buttons and toggle buttons. The widgets tab of lib-gwt-svg-samples has been upgraded to demonstrate these.
- UiBinder integration of SVG push buttons and toggle buttons. The article on UiBinder integration has been upgraded to explain how to create SVG buttons declaratively. You can also look at the widgets tab of lib-gwt-svg-samples which uses UiBinder to create the sample buttons.
- support for external SVG resources in client bundles. The parser tab of lib-gwt-svg-samples has been upgraded to use external resources
- A new sample has been added to lib-gwt-svg-samples to demonstrate SMIL animations. Currently the sample only works on browsers which support SMIL animation (Opera and FF3.7a4)
Lib-gwt-svg-edu has its share of improvements too. The program takes advantage of GWT code splitting for better modularity. A brand new labyrinth game has been added, featuring:
- DFS base algorithm and clean OO data structure to create labyrinths of arbitrary complexity.
- SVG rendering of the labyrinth
- Use of HTML5 canvas to rasterize the labyrinth to arbitrary shapes
- Beautiful artwork, courtesy of the Open Clip Art project
- SVG buttons
This post summarizes many new features and changes which have just been released today with an emphasis on ease of use.
GWT client bundle integration
A new SVGResource interface has been added for SVG resource. This lets developers embed their SVG images in the bulk of the GWT application code.
SVG Widgets and UiBinder integration
A new package has been created to group SVG widgets. Currently there is just a SVGImage widget class. This class provides very good integration with UiBinder, and lets developers specify their SVG as a resource or directly by embedding it into the UiBinder template. In the latter case, this makes it possible to bind GWT variables to arbitrary SVG elements, add event handlers declaratively to arbitrary SVG elements, localize SVG, use obfuscated CSS with SVG… The lib-gwt-svg-samples has been revamped to demonstrate these new features. It has been migrated from Java2Html to JHighlight and nows displays the UiBinder source code along with the Java Source code
Documentation
Three new articles are now available
- svg tag to gwt class mapping
- Shows how to svg tags map to lib-gwt-svg classes. Useful to know which type to use for your variables when writing UiBinder templates
- uibinder integration with lib gwt svg
- A tutorial on how to use UiBinder with lib-gwt-svg
- svg event mapping
- Shows how svg events map to lib-gwt-svg classes
This post summarizes many new features and changes which have just been released today.
Dependency cleanup and standardization
All projects now depend on GWT 2.0.3 (instead of a 2.x trunk snapshot), including vectomatic which has migrated from 1.7.1 to 2.0.3
New features
lib-gwt-svg has upgraded to version 0.3 and provides better event handling, helper methods to make the code more concise, the capability to serialize SVG from the DOM tree, and of course bug fixes. All SVG dependent projects now use this later version and have been migrated to take advantage of them
A new subproject called lib-gwt-svg-edu is introduced. The goal is to build educational games for your children, functionally equivalent to what exists sometimes in Flash, but using nothing but standard techniques (SVG + javascript)
Library goals
lib-gwt-svg tries to reach the following goals:
- Provide a clean, GWT-friendly API
- Hide the idiosyncrasies of vendor SVG implementations
- Reuse existing GWT features wherever possible to eliminate code duplication and impedance mismatch
- Stick to the W3C standard, unless it duplicates an existing GWT feature or the GWT feature is too incomplete
Compatibility with SVG, W3C standards and exitsing GWT APIs
The SVG 1.1 specification is built on top of several other specifications. The following table lists each specification, explains whether GWT already has an API addressing it, and whether lib-gwt-svg chooses to reuse it or not. The following subsections provide a more elaborate view of each table row.
DOM Level 2
GWT has two hierarchies. The low-level class hierarchy in com.google.gwt.dom.client consists mostly in overlay types for HTML elements. The high-level class hierarchy in com.google.gwt.user.client.ui consists in higher level widgets classes which wrap types of the first hierarchy.
lib-gwt-svg uses a similar architecture. The types in org.vectomatic.dom.svg.impl forms the low-level implementation based on overlay types. You should never have to use these types directly. The types in org.vectomatic.dom.svg form the high level implementation which end-users manipulate.
End-users only need to care about DOM Level 2 in the following scenarios:
- when they need to insert their DOM tree in the HTML tree: lib-gwt-svg automatically imports the node in the main document and OMElement.getElement() will return a GWT compatible class
- when they need to manipulate the DOM structure: the wrapper types provide DOM-like methods to manipulate the DOM tree (appendChild, removeChild, insertBefore, …). Users should be careful when they use methods to navigate the DOM tree (getNextSibling, getElementById, …). These methods automatically build a wrapper type from the underlying overlay type. While convenient, these method build a new wrapper every time they are called. Thus end-users should either store the returned reference for later use, or do their navigation on the overlay types and convert to wrapper types when needed using OMNode.convert
DOM Level 2 events
The GWT event model is roughly structured like this: a low-level model registers handlers and channels events through a central dispatcher. The dispatcher sends events to a new and extensible event model. The new event level is capable of emulating the deprecated event model. lib-gwt-svg cannot reuse low-level model: it is indeed not extensible and SVG has several events which are specific (onzoom, onrepeat, onfocusin…). Thus this part is re-developed, and you should not try to use event-related methods of com.google.gwt.user.client.DOM. lib-gwt-svg channels events to a central disptacher. The dispatcher sends events to the GWT new event model. This provides compatibilty with existing GWT APIs (notably, mouse event handlers are the same). lib-gwt-svg extends the GWT event model with new classes corresponding to new event types. No effort is made towards compatibility with the deprecated GWT event model.
SMIL
GWT offers com.google.gwt.animation.client. It consists mostly in timer management and is orthogonal to SMIL. Indeed, SVG SMIL provides support for declarative animation, using animation elements derived from ElementTimeControl. lib-gwt-svg integrates SMIL by providing animation specific events integrated in the GWT event model. However currently only Opera seems to implement them. lib-gwt-svg does not preclude using com.google.gwt.animation.client to update an SVG scene at periodic intervals. Actually, this seems like the only approach to SVG animation today given the limitations of current SVG implementations, Opera excepted.
CSS
SVG low-level class hierarchy in com.google.gwt.dom.client provide support for manipulating both the ‘style’ and the ‘className’ CSS attributes. These mechanisms can be reused, but only up to a certain point. Bug 374216 is Firefox breaks the Style.getProperty() and Style.setProperty() on SVG elements. The SVG model itself makes the className an OMSVGAnimatedString instead of a mere String. lib-gwt-svg defines an OMSVGStyle overlay type derived from Style. It provides a pair of getSVGProperty() and setSVGProperty() to bypass Bug 374216. lib-gwt-svg also defines a IStylable interface, which provides methods equivalent to GWT for classname manipulation, but adapted to the constraints of an OMSVGAnimatedString.
XLink
GWT provides a com.google.gwt.user.client.ui.Hyperlink which provides integration with History. This class cannot be reused, but the same principles will be implemented in a future version to provide integration between OMSVGAlement and com.google.gwt.user.client.History.
Exception mapping
Lib-gwt-svg relies on the browser native SVG engine and its javascript API. To invoke the API, all lib-gwt-svg methods invoke a JSNI method at the end of the call chain. Some of the native javascript APIs can raise exception. JNSI will catch these exceptions and report them as com.google.gwt.core.client.JavaScriptExceptions. com.google.gwt.core.client.JavaScriptExceptions are runtime exception, which means you do not have to catch them and declare them like checked exceptions. The W3C specifications distinguishes several kinds of exceptions (DOMExceptions, SVGExceptions), however from a GWT point of view they will all appear as JavaScriptException. It could have been possible to catch all the com.google.gwt.core.client.JavaScriptExceptions, wrapped them in a typed checked exception and rethrow that exception. However this would have been awkward to manipulate for the end user, would have degraded performance and would have brought little added value. Thus the choice has been made to keep the exception in the form of com.google.gwt.core.client.JavaScriptExceptions. It is very important information though for the end user to know when they invoke a method if it may throw an exception and in what circumstance. Thus, all the exceptions are documented and throws clauses have been used for added precision, though it does not change the fact that the thrown exceptions are not checked exceptions.
Vectomatic is a 2D vector graphics editor based on GWT and HTML 5 canvas. At the moment it is more of a demonstrator than a truly useful application.
What lessons did I learn from this first prototype ?
On the bright side, browsers are now quite fast and I think that a full featured editor is truly feasible. Browsers can even provide the basis for advances in the UI for this kind of programs: for instance, in vectomatic, the compass-like widget enables working on documents from any orientation ; transparency effects keep the drawing visible when one moves elements around. Developing such an application within a browser enables leveraging the browser native 2D functionalities and reduces the amount of code which has to be written. Newer GWT technologies like code splitting enable the application to grow in size and load incrementally.
Regarding things which ought to be done differently, I see mostly two. Lesson 1: canvas is not the best technology for this kind of program, SVG makes much more sense in this area. SVG offers features which are more advanced than canvas: things like pick correlation, gradients with an orientation, clipping, dashed lines, even animation; the persistence format is no longer an issue since SVG IS the format; it comes with a rich ecosystem of resources, libraries, tools and knowledgeable people. Lesson 2: GWT native library of widgets is the framework’s weak spot at the moment: there are not enough widgets and they are not good or customizable enough ; thus you need to rewrite a lot of code on your own (things like sliders or spinners); on the plus side, it now has the UiBinder, which does things the right way (like InterfaceBuilder on NeXT, but with open formats). I see a parallel between GWT widgets library and AWT in the Java world. To get started on a large scale project, a more powerful widget library is needed. At the moment GXT seems the more complete, but it comes with strings attached. I hope the GWT team comes up with a Swing or SWT of their own quickly.
lib-gwt-svg-chess was originally developed as a demonstrator for lib-gwt-svg. I began by looking for a chess engine with the following requirements: java-based, maven project if possible, minimum dependencies on non GWT-compatible libs, clean design to allow refactoring if needed. There were several possible contenders (chessbox port of GNU-chess, chesspresso), but only Carballo matched all my criteria.
My first step was to refactor the code so as to extract all the non-GWT compatible parts to an external project (applet UI mostly, configuration, opening book reader). I added extra hooks to let me provide alternative implementations for the parts which could not work in a GWT context (opening book, configuration). The alternative refactored version has number 0.3.1, though it does not have any extra functionality compared to the original 0.3 version.
I wrote my UI and started my debugging in developer mode. Everything went fine and I thought I was almost done. Then I tried production mode and discovered the program did not even start.
It turns out that the problem was mostly a performance problem. For all the competition occurring between JavaScript engines, JavaScript performance is still considerably slower than Java. Carballo starts by initializing what it calls “attack tables” (basically, a 100 000 element array of longs which represent chessboard configurations). In Java, this takes 0.5 seconds. If JavaScript, which lacks native longs and uses an emulation library, this step took 120 seconds. So I tried to trade time for space and decided to precompute the table as a large JSON structure which gets parsed at the beginning of the program. Contrary to the interpreters, the browsers javascript parsers are quite fast and I was down to 3 seconds for this part. However now the end user has to download a 3mb file full of longs just to initializa the program. Activating mod_deflate enables gaining some of this space back.
Another not yet solved issue is the issue of liveness. When the computer is thinking, one would like the UI to remain responsive. There are no threads in JavaScript, but the new web workers API introduced in HTML5 could theoretically do the trick. Maybe that could work with extra hacking, but not out of the box, since GWT requires special initialization code which is not compatible with web workers (my biggest complaint about GWT at the moment)
Ideally SVG ought to be embedded as a subtree inside an xhtml document, or be linked in an html document through an <a href=”my.svg”> tag. GWT would provide the logic to make these graphics dynamic…
However GWT initialization today is tightly linked to HTML and depends on a procedure involving iframes to bootstrap the process. This means you cannot currently use GWT code in another container than html. Too bad. If GWT code could live in other, javascript enabled containers (xhtml, svg, web workers), it would greatly broaden its potential.
The most thorough analysis (and even prototyping) of a generalized and open bootstrap process has been made by Archie Cobbs but alas it has not seemed to raise all the attention it deserves from the GWT team.
|