Rounded borders – and how to do it in IE

In qooxdoo 0.8 we will introduce the concept of widget decorators. This has already been described in an earlier blog article. Decorators allow a widget’s background to be styled independent of the widget’s content. To demonstrate the flexibility of the decorator concept I have written a decorator, which uses native CSS rounded borders in Firefox and Safari/Webkit and emulates the same behavior in Internet Explorer. The screenshot shows the same qooxdoo 0.8 application in Internet Explorer 7, Firefox 3 Beta 3, WebKit r30082 and Firefox 2.0.11.

Rounded borders screen shot

The widget shown in the screen shots is generated by the following snippet of qooxdoo code:

doc = new qx.ui.root.Application(document);
 
var border = new qx.ui.decoration.RoundedBorder().set({
  radius: [10, 20, 30, 40],
  width: [3, 10, 20, 5],
  color: ["red", "green", "yellow", "blue"],
  backgroundColor: "gray"
});
 
doc.add(new qx.ui.basic.Label().set({
  html: qx.bom.client.Engine.NAME,
  decorator : border,
  width: 140,
  height: 100,
  padding: 20
}), 10, 10);

I think the most interesting question is about the IE implementation. Since IE has no native CSS rounded border support, the borders must be rendered using a different technique. A common approach is to render the borders using pixel-sized DIV elements like e.g. RUZEE.Borders does. We have used a different and in my opinion much more powerful approach.

We use VML to render the background including the border. The VML code is dynamically created and inserted into the decoration DIV using plain innerHTML. Since the decorator is informed about size changes by the qooxdoo layout engine, it can update its borders accordingly. The rendering quality is amazing and looks pretty much like the best native browser implementation of Firefox 3. This is the code automatically generated and applied by qooxdoo:

<style>v\: * { behavior:url(#default#VML);display:inline-block }</style>
<xml:namespace ns="urn:schemas-microsoft-com:vml" prefix="v" />
 
<div style="left: 10px; width: 140px; position: absolute; top: 10px; height: 100px">
  <div style="z-index: 10; left: 25px; width: 85px; position: relative; top: 23px; height: 37px" usehtml="true">mshtml</div>
  <div style="z-index: 5; left: 0px; position: absolute; top: 0px">
    <v:group style="left: 0px; width: 140px; position: absolute; top: 0px; height: 100px; antialias: true" coordsize = "140,100">
    <v:shape style="width: 140px; height: 100px" coordsize = "140,100" fillcolor = "gray" path = " m10,1 ns l120,1 qx139,20 l139,70 qy110,99 l40,99 qx1,60 l1,10 qy10,1 x e"></v:shape>
    <v:shape style="width: 140px; height: 100px" coordsize = "140,100" fillcolor = "red" path = " m10,3 ns at5,3,15,13,10,3,5,3 wa0,0,20,20,0,0,10,0 wa100,0,140,40,120,0,140,0 at110,3,130,23,130,3,120,3 x e"></v:shape>
    <v:shape style="width: 140px; height: 100px" coordsize = "140,100" fillcolor = "green" path = " m130,13 ns at110,3,130,23,130,13,130,3 wa100,0,140,40,140,0,140,20 wa80,40,140,100,140,70,140,100 at110,60,130,80,130,80,130,70 x e"></v:shape>
    <v:shape style="width: 140px; height: 100px" coordsize = "140,100" fillcolor = "yellow" path = " m120,80 ns at110,60,130,80,120,80,130,80 wa80,40,140,100,140,100,110,100 wa0,20,80,100,40,100,0,100 at5,40,45,80,5,80,25,80 x e"></v:shape>
    <v:shape style="width: 140px; height: 100px" coordsize = "140,100" fillcolor = "blue" path = " m5,60 ns at5,40,45,80,5,60,5,80 wa0,20,80,100,0,100,0,60 wa0,0,20,20,0,10,0,0 at5,3,15,13,5,3,5,8 x e"></v:shape>
    </v:group>
  </div>
</div>

Generated HTML for Internet Explorer

The first two lines are required to enable VML support and are only added once into a page. Note that only one of the DIV elements contains VML code. The rest is typical HTML code generated by the qooxdoo GUI toolkit, which does all the layouting itself, just using JavaScript. It consists of a couple of nested, absolutely positioned DIV elements with the appropriate CSS styles applied.

The code for Firefox is much simpler and just sets the browser-specific CSS styles:

<div style="position: absolute; z-index: 0; left: 10px; top: 10px; width: 140px; height: 100px;">
  <div style="position: relative; z-index: 10; left: 25px; top: 23px; width: 85px; height: 37px;">gecko</div>
  <div style="border-style: solid; border-color: red green yellow blue; border-width: 3px 10px 20px 5px; z-index: 5; position: absolute; left: 0pt; top: 0pt; -moz-box-sizing: border-box; width: 100%; height: 100%; background-color: gray; -moz-border-radius-topleft: 10px; -moz-border-radius-topright: 20px; -moz-border-radius-bottomright: 30px; -moz-border-radius-bottomleft: 40px;"></div>
  </div>

Generated HTML for Firefox

Native browser support

The quality of rounded border implementations differ a lot between browsers. The CSS3 draft defines the property border-radius and a property for each corner like border-top-left-radius. These properties take two values, one for the horizontal radius and one for the vertical radius. That way it is possible to define (quarter-)elliptical borders. If only a single value is given, the border is (quarter-)circular.

Current browser implementations

  • Firefox 2: Firefox supports only circular borders. Border definitions always take a single parameter – the radius. The CSS property is called -moz-border-radius and -moz-border-radius-topleft, respectively. -moz-border-radius supports the CSS shorthand mode. If only one parameter is given, the radius of all four border is identical. If four parameters are defined, the radius for all four corners is set individually. The rendering quality is very poor since Firefox 2 does not use anti-aliasing to render the border (cf. screen shot).
  • Firefox 3: The same as Firefox 2 but borders are rendered using anti-aliasing, which looks much better (thanks to the Cairo 2D graphics library).
  • Safari/WebKit: The CSS property names differ from the W3C spec (-webkit-border-radius and -webkit-border-top-left-radius). Besides that, WebKit implements pretty much the CSS3 standard. Rendering looks fine as long as all border widths are equal. If they differ, the rendering looks quite ugly (cf. screen shot). This has already been reported in the WebKit Bugzilla and hopefully is going to be fixed soon.
  • Opera: Opera also does not support CSS rounded borders but there is a description online on how to emulate them using SVG backgrounds.
  • Internet Explorer: No native support either. I suppose the VML approach presented here works only well in the controlled environment of the qooxdoo widget toolkit, but I may be proved wrong.

Parsing Atom Feeds

The Feedreader is one of the standard applications that ship with qooxdoo. Behind a rather unostentatious surface it features quite an interesting showcase of networked OO programming. Feeds are retrieved from a server, processed and displayed in a three-pane layout. The feed retrieval works through a small proxy (to overcome same-origin issues) that delivers the data in a JSONP format which contains feed meta information and a list of individual feed entries. When you look at an individual feed entry in Feedreader you might notice the “read more…” link at its bottom. The idea behind this item is to link to the original feed entry and open it in a new window when clicked. To populate this link for an RSS entry is straight-forward, and the current Feedreader offers a couple of them.

But two of the feeds are atom feeds, and here the story is different. The RFC defining this protocol, 4287, lists a whole bunch of elements a feed entry can have, but it seems none of them is the definitive place for a URI that could hold the link to the entry’s source. There are things like “atom:content@src” or “atom:id“, but their semantics are often vague or their values are restricted to IRI‘s, “International Resource Identifiers”, which are – roughly speaking – arbitrary strings as long as they are world-wide unique. Often the processor is explicitely required not to assume they are dereferencable (e.g. with atom:id).

This leaves it up to the feed provider if and where they are providing a source URI with every feed entry. One of the Feedreader’s sample atom feeds, daringfireball.net, embeds them in the atom:content element of the feed entry. That means they are somewhere in a string (CDATA) section with a ‘href=’ prefix, where you might be lucky enough to pick them up reliably. Another atom feed, from blog.whatwg.org, uses the optional “xml:base” attribute of the content element (with full URIs in contrast to daringfireball.net, which only provides a true base URI in this attribute), but also uses the “atom:link” feature to provide links to the entry’s source, but it does so specifying atom:link twice for every entry – where the one with the “alternate” rel attribute is what you are interested in. (And, in case you wondered, the RFC in general “assigns no meaning to the content (if any) of this element”[*]). daringfireball.net, on the other hand, uses atom:link too, but only to link to articles the blog entry is about, not the entry itself. Go figure!

qooxdoo 0.8 Event Layer

qooxdoo 0.8 will include a new, sophisticated low-level event layer. Not only does this separate layer allow for a clean architectural design (with a high-level GUI toolkit on top), it will also make it possible to use this reduced set of low-level DOM features without qooxdoo’s advanced widget system. Particularly for rather traditional web pages instead of full-featured rich internet applications, such a light-weight, standalone qooxdoo built might be more favorable. One central element for such a low-level layer is DOM event handling.

In qooxdoo 0.7, event handling is tightly integrated, rather intermingled, with the widget system. We have now taken the best parts of the old event handler, like key and mouse event normalization, and integrated them into a new standalone event layer.

This event layer has many interesting features. Unlike event layers commonly seen in other JavaScript libraries it does much more than just providing a cross-browser API wrapper for attaching event listeners:

We just did a presentation about this new event layer. Take a look at the Slides: qooxdoo 0.8 Event Layer [PDF] or at the corresponding wiki pages [5] for more information.

[1] http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-flow
[2] http://msdn2.microsoft.com/en-us/library/ms537630.aspx
[3] http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-interface
[4] http://qooxdoo.org/documentation/0.7/keyboard_events
[5] http://qooxdoo.org/documentation/general/dom_event_layer

qooxdoo.org re-org

To streamline our web site and simplify the way information is delivered to you, we have merged the blog and the news section into one, under the news label. The old blog URL (blog.qooxdoo.org) is still available, but all new posts will show up under the news URL. Please update your bookmarks and feeds.