57 45 4c 4c 20 44 4f 4e 45 21
                   
Jan 6

XAML in SWT / Where The Ribbon Is

Posted on Wednesday, January 6, 2010 in Articles, Eclipse Articles.

I get loads of traffic and email thanks the Ribbon. It’s truly an alpha project that went completely beyond what I had imagined. It’s exciting of course, but as much as it should give me a warm fuzzy feeling, it actually makes me rather nauseous. The thing is, a lot of the code is a total mess, it’s embarrassing, and I’ll freely admit it. It was never written as a “thought out” project, it was simply written as a not-so-quick “test” to see if it was possible to do a Ribbon component in pure SWT. The answer was “kind of”, and that’s where it got left off.

After having worked with Microsoft Silverlight and XAML lately I realized how simple XAML makes it to do stuff that I spent hours writing code for in the Ribbon. For example; rounded corners, gradient borders, multi-gradient fills, etc. It’s a pleasure to work with, easy to change, and it moves you forward at a good pace – and that’s how I feel it should be. I doubt anyone would download the Ribbon source as it is now, look at the render classes and think “great, I’ll make a change riiiight here”, it’s just too complicated and non-customizable. A user wanting to change colors would have to spend hours, days even, just to figure out what pixel is drawn where, and when I look at the code now, I get the same feelings.

Instead of continuing work on the Ribbon to finish “the mess”, I decided that if it was ever going to finish the project and take it out of Alpha, I was going to do all the rendering stuff the right way. And thus started a little test project with Brushes with the intention that if I could make things as easy as writing XAML, I could write all the Ribbon widgets in no-time.


Brushes

A Brush object in XAML (and in a lot of other languages) is like a paint brush, it defines a direction and a stroke, colors, etc. The Ribbon basically uses a whole ton of Brushes.

Here’s a quick list of the things that the Ribbon currently uses lots of:

  • 2-color and Multi-color gradients in Horizontal and Vertical orientation, there are no diagonal or radial gradients (although brushes in XAML support any angle).
  • “Border” controls that contain other controls (pretty much all controls have some sort of border around them). A Border control in WPF is a control that can contain only 1 other control, and it can display a border and a background and allows for rounding of the borders.
  • Various paddings and margins between controls to space them out between one another.
  • Colors, colors and more colors.

If you think about it, that’s pretty much any control in an normal GUI library as at some level of the OS, there will be a programmatic “pen” that is drawing everything you see, pixel by pixel. Everything built on top of that is convenience to the user. And how many people know how to programatically draw a gradient between 2 colors if there isn’t an API to do it with? Probably very few.

(If you want to see a fully functional Ribbon in Silverlight (built on XAML) do a google search for “SandRibbon”. I’d link to it here but it’s commercial and costs a fortune, so I don’t feel like promoting it with a cross-link.)

Multi-color Gradients

My “Pseudo” code that I started from to “realize” this was the following:

LinearGradientBrush yrb = new LinearGradientBrush(new DPoint(0.5, 0), new DPoint(0.5, 1));
yrb.addGradientStop(new AlphaColor("#FFfff900"), 0);
yrb.addGradientStop(new AlphaColor("#FFf90005"), 0.5);
yrb.addGradientStop(new AlphaColor("#FF1100ad"), 1);

It’s very WPF/XAML, and what it says is “Create a brush that will draw a gradient vertically (each DPoint is x/y coordinates of how the fill should draw), add 3 color stops, one at 0%, one at 50% and one at 100%”.

XAML, by the way, for a typical LinearGradientBrush looks like this:

<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0" x:Key="ToolbarButtonBackgroundStrokeNormal">
     	<GradientStop Color="#FFfff900" Offset="0"/>
        <GradientStop Color="#FFf90005" Offset="0.5"/>
        <GradientStop Color="#FF1100ad" Offset="1"/>
</LinearGradientBrush>

If we were to do this in Photoshop it’d look exactly like this (on a 400×400 canvas):

gradient

To support multi-color gradients when using pattern fills (as SWT doesn’t), I had to write some code that split each section that I intend to fill into smaller rectangles and fill each one. Each section is 2 colors. So in the above case, we have 2 patterns we need to create, one from 0% to 50% and one from 50% to 100%. In the picture this would be from 1 to 2, and from 2 to 3.

Implementing it wasn’t that hard, and despite the code not being that long I think it’s easier you look at the source itself if you want the details as it’s a bit spread out. The source is at the bottom of the article.

A SWT Border Control

The “Border” control in WPF takes a few interesting attributes. One is the Brush that is used to fill the border, the second is the Brush used to fill the background. Then it allows for a setting the border “roundness”, the padding and the margin, and a few other things. All of that is mostly getters/setters and in the end we will have a GraphicsObjectRenderer class that deals with actually drawing it. If there was a border control in SWT it might look a bit like this:

Border b = new Border(parentControl);
b.setBorderBrush(ourBorderBrush);
b.setFillBrush(ourFillBrush);
b.setBorderThickness(1);
b.setCornerRadius(20);
b.setWidth(100);
b.setHeight(100);
b.setMargin(new Margin(50));

Pretty straight forward.

The result from running this code looks something like this:

bordertest

Oh no, Issues…

This is how far I got before I hit a wall. I actually started writing the article as I wrote the code because I was quite excited how seemingly well it worked. So what halted me? A few bugs in SWT. Normally a lot of things can be worked around if they don’t work 100% as intended, however, the bugs I came across were not of that type. The main issues are as follows:

  • Pattern fills in SWT start drawing the pattern in the wrong place. I’ve filed a bug for this which can be found here. This one is rather critical as most of the brush drawing depends on it.
  • A rounded rectangle is not drawn in the same way as a round rectangle, causing the border around it to be in weird places around the rectangle itself. An old bug is filed here.
  • Other GC draw-related bugs.

Conclusion

Probably not the conclusion you or I wanted, but as I see it, I cannot really continue this project until the bugs are fixed (or worked around), and considering GC-related bugs are probably low on the fix-list, the Ribbon will have to sleep for a while longer, along with this project. AWT has a very good MultipleGradientPaint implementation and I successfully manged to convert AWT drawings to SWT, but the AWT code is rather slow and converting it over is also slow, so it’s not really an option.

Code

If you want to play with the code I have so far, you can download it here. Under the “test” package directory you’ll find various classes. BorderTester.java is the main test class. The code is truly “as is”, it’s just zipped up from what I had at the time I ran into issues.

Download Source

  1. Glazed Lists + SWT Tables == True If you haven’t heard about Glazed Lists until now, it’s...
  2. Printing SWT Tables with PaperClips In this article I will explain how to print a...
  3. MenuManagers and You If you’re using Eclipse RCP (or even standalone SWT with...

13 Responses to “XAML in SWT / Where The Ribbon Is”

  1. Tom Schindl - January 6th, 2010

    Why XAML and SVG definitions? I think CWT of Nebula has prelimenary SVG support and your gradient, … would make sense to get merged into it

  2. Tweets that mention XAML in SWT / Where The Ribbon Is | Hexapixel -- Topsy.com - January 6th, 2010

    [...] This post was mentioned on Twitter by EclipsePlanet, Larry King. Larry King said: XAML in SWT / Where The Ribbon Is | Hexapixel http://bit.ly/71wHrR #SL #RIA [...]

  3. Wim Jongman - January 6th, 2010

    Nice blog Emil, you really are a graphics guru. Can’t you fix the bugs yourself?

  4. Cole - January 6th, 2010

    Have you looked into draw2d? I know they have border controls and the padding features you mentioned. You might have similar issues with drawing the gradients as with SWT since it uses that to do the drawing.

  5. Bogdan Gheorghe - January 6th, 2010

    We still have some of the SWT team away until sometime next week. I’ll bring up the 2 bugs you mentioned when we’re back to full strength.

  6. Boris Bokowski - January 9th, 2010

    Hi Emil,

    You also mention “other GC draw-related bugs” – please let us know about them, or give us pointers to bug reports, so that we can help you make progress.

  7. Yves YANG - January 11th, 2010

    Hi Emil,

    It is really nice stuffs. It is aligned completely with XWT. How about to integrate it in XWT?

  8. Joe - May 20th, 2010

    Any progress?

  9. Emil - June 14th, 2010

    Looks like some issues are fixed in the next version of SWT, so we’ll see.

  10. Lars Vogel - July 8th, 2010

    Emil, I would be cool to use this ribbon as an example for an alternative renderer in Eclipse e4.

    I start looking at this. If you are interested please contact me under Lars.Vogel@gmail.com

  11. Eric - August 19th, 2010

    What about non-horizontal/vertical brushes?
    Any work on those?
    Or is this dead due to XWT or somesuch.

  12. Emil - August 20th, 2010

    Hi Eric,

    First, this was mostly a proof of concept, but as far as continuing goes; it’s on hold until the next SWT version as some bugs that I mentioned in the article have hopefully been fixed in the next version (such as the gradient issue).

    Regards,
    Emil

  13. Koen - March 7th, 2011

    Some time ago, I also encountered the `patterns start at wrong place’ bug. I think you should see creating a pattern as painting ‘invisibly’ onto the entire canvas. Your subsequent fill/draw command then ‘unmasks’ that part of the canvas.

    If I remember correctly, I could work around this by using a translation transformation before drawing each shape, i.e., translate to the origin before drawing, then draw the shape at 0,0 (and optionally translate back).