|
|
||
Romain Guy's BlogCommunity: JavaDesktop ArchivesJava2D Gradients PerformancePosted by gfx on September 26, 2006 at 05:07 PM | Permalink | Comments (42)I feel weird. My hands are sweaty and my throat is sore. My heart's beating too fast... What's happening to me?! Wait, I know. I haven't blogged here for the past 7 months! Dammit. Sorry for that folks. Back to gradients. I use Java2D gradients a lot. I mean, A LOT. Most of the time, gradients work perfectly fine and performance are more than enough, especially when you honor the clipping rectangle. In some rare occasions, a gradients does take quite some time to be painted. This happens when you are painting large areas with a gradients as in the following screenshot: In this picture, the background gradient is about 700x500 pixels large. That's a lot of pixels. Since we were drawing full frame animations (60fps) on top of this gradient, the JVM wa a tad CPU hungry. We optimized this by painting the gradient in a buffer image that we use instead every time a repaint event occurs. Problem solved. Or is it? Using a large picture was fine in this case because it was after all just a demo. But is it worth wasting 1,5MB or so of RAM? I think not. There's a very nifty trick you can use to optimize the drawing of large vertical and horizontal gradients: image resizing. Instead of painting the gradient in a picture large enough to cover the area you want to repaint, paint it in a 1 pixel wide picture for vertical gradients or in a 1 pixel high picture for horizontal gradients. Then, at runtime, paint the picture with Here is an example of how you can use this trick:
@Override
protected void instrumentedPaintComponent(Graphics2D g2) {
if (cache == null || cache.getHeight() != getHeight()) {
cache = new BufferedImage(2, getHeight(),
BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = cache.createGraphics();
GradientPaint paint = new GradientPaint(0, 0, Color.BLACK,
0, getHeight(), Color.WHITE);
g2d.setPaint(paint);
g2d.fillRect(0, 0, 2, getHeight());
g2d.dispose();
}
g2.drawImage(cache, 0, 0, getWidth(), getHeight(), null);
}
But how does this trick compare to the two previous techniques? Well, here I have made surprising discoveries. I wrote a simple app to test the speed difference between the 3 approaches on Windows and Mac OS X. I first ran the tests on Windows at two different sizes (small gradients, about 200x200 pixels, versus large gradients, about 300x800) with various command line flags to change the rendering pipeline. I then ran the tests on Mac OS X at two different sizes. All tests were using Java SE 6 b88. On the following diagrams, smaller numbers (even negatives due to the logarithmic scales) are better. ![]() ![]() ![]() From these diagrams, two things are for sure on Windows: images are faster than regular gradients and the new OpenGL pipeline in Mustang is freakingly good. On Mac OS X though, all techniques are roughly the same. The main advantages of pictures over regular gradients is that whatever rendering pipeline you use, the results are almost the same on Windows. Even though using 1 pixel wide/tall pictures is a bit slower than full size pictures, this technique is the best in terms of speed and memory usage. At least for horizontal and vertical gradients. P.S: By the way, this is only a benchmark that continuously paints gradients using all three techniques for a given number of seconds and finally spits out the time spent in each implementation. It might not (and surely does not) reflect real world use at all :) GroupLayout is integrated into MustangPosted by gfx on March 18, 2006 at 01:07 AM | Permalink | Comments (4)I'm just relaying the information from JavaLobby but GroupLayout has been integrated into Mustang b76. GroupLayout is the layout manager that makes NetBeans' Matisse GUI builder shine. So, go grab Mustang, download NetBeans 5.0 and enjoy! Please note that this new features still required the approval from the JSR 270 EG. You can find more information about Java SE 6 JSR on the JCP web site: http://www.jcp.org/en/jsr/detail?id=270 Make Swing... er... Swing!Posted by gfx on February 07, 2006 at 01:24 AM | Permalink | Comments (21)Most GUI are really boring. And I really mean it. Admit it, you'd rather listen to a French stammerer trying to recite a bad English translation of War and Peace during a rainy Sunday afternoon (and boy what a long afternoon it would be) than look yet again at some applications. Besides cool aesthetics, a way to make a GUI for appealing (just talking about the look here) is to introduce animations. Some get it right (Apple), and some get it really wrong (the first thing I do with a fresh Windows install - after muting the sounds - is to shut down all the animations). But that's not the point. Bad or good, we just want animations in our Swing application.
Timer crudeTimer = new Timer(1000 / 30, new ActionListener() {
public void actionPerformed(ActionEvent e) {
}
});
crudeTimer.start();
That, is a simple timer firing an event 30 times per second (or so it claims at least.) So, what is missing here? Too many things to make animations easy to develop. First, this timer doesn't track the elapsed time. You have to do that by yourself using Let me introduce you to the solution of the wise man, Chet Haase's timing framework. Before you run away bewildered by such a powerful imagination to name a framework, take a deep breath, sit down and stay focused for a couple more minutes, will you? It's worth it. I swear. Chet ran into the exact same problems as the aforementioned ones. But Chet is not lazy, Chet is full of resources. Chet built a solution to this problem. Bestow our eternal gratitude on Chet (or whatever that is you want to bestow on Chet.) Anyway, Chet wrote a comprehensive and really interesting introduction to his framework that I urge you to read. I won't delve into the details here but the point is, hitherto you were striving to create animations for Swing UI and now it's much easier. How so? Let's take a look at a simple example a nice looking but boring, static button: ![]() Evidence #1. A boring button. Usually, Swing developers make buttons more interactive by introducing a rollover effect. The nice thing is you just need to call somethin like When the mouse exits the button, the contrary happens, and the highlights fades out. In this particular case, I took care of animation consistency. For instance, if you enter the button then exits halfway throught the animation then enter again, you will see the animation react properly. It won't jump to its extreme values. Believe me, that's a pain to handle with
private final class HiglightHandler extends MouseAdapter {
private TimingController timer;
private Cycle cycle = new Cycle(300, 1000 / 30);
private Envelope envelope = new Envelope(1, 0,
RepeatBehavior.FORWARD,
EndBehavior.HOLD);
@Override
public void mouseEntered(MouseEvent e) {
animate(true);
}
@Override
public void mouseExited(MouseEvent e) {
animate(false);
}
private void animate(boolean forward) {
if (timer != null && timer.isRunning()) {
timer.stop();
}
timer = new TimingController(cycle, envelope, new AnimateGhost(true));
timer.start();
}
}
private final class AnimateGhost implements TimingTarget {
private boolean forward;
private float oldValue;
AnimateGhost(boolean forward) {
this.forward = forward;
oldValue = ghostValue;
}
public void timingEvent(long cycleElapsedTime,
long totalElapsedTime,
float fraction) {
ghostValue = oldValue + fraction * (forward ? 1.0f : -1.0f);
repaint();
}
public void begin() {
}
public void end() {
}
}
This code might seem a bit long but when you look more closely you'll see it is very easy. You can see a mouse adapter that handle the mouse enter/exit events and start the animation accordingly. All three fields from As you can see, this small framework makes things easier both for the author of the code but also for the readers. Chet, Chris and I are currently using this framework almost everyday and as we go along, Chet is discovering new things to integrate into it. You can expect really nice things in a near future, like non-linear progressions. In the meantime, give it a try, I'm sure you will love it. I couldn't post a webstart demo of the small effect I presented here but I'll try to do that this week if I have some time. Yet, you can download the animation (QuickTime format, 46kb) to see what it looks like. You can also check the talk Desktop Java in Action Richard Bair and I gave at JavaPolis. At the end of the presentation, in the FX section, I talk more about how to use Swing's timers for animations and the related problems. Video Presentation: Desktop Java in ActionPosted by gfx on January 04, 2006 at 11:25 PM | Permalink | Comments (1)Richard Bair and I have created a presentation entitled Desktop Java in Action for JavaPolis. I also presented this talk in Paris at Sun Microsystems. I recently made the slids of the talk available online but as every slide set, they lack a lot of information given by the speaker(s). If you speak french, you are very lucky because Developpez, a french programming network, offers a video of the presentation online for free. The talk is 3 hours long but they did a great job by slicing it into more than 20 parts. Each part has a short description that will help you find your favorite topics. If you do not speak french, don't worry, all JavaPolis talks will be available as videos within a couple of months. Twinkle TeaserPosted by gfx on November 10, 2005 at 05:09 PM | Permalink | Comments (1)I recently posted a screenshot of Twinkle, a demo involving Java2D, Swing and OpenGL. Here is a video showing Twinkle in action (so far): I will never thank enough Chris Campbell and Ken Russel for giving us these realy cool feature. Twinkle, a Java2D/OpenGL DemoPosted by gfx on November 09, 2005 at 01:24 AM | Permalink | Comments (8)The new OpenGL pipeline in Mustang let external API composite a Swing UI with an OpenGL scene. This allows JOGL to efficiently mix Swing and 3D. Twinkle, a simple photo viewer (it looks like Microsoft Max :), benefit from this. Take a look for yourself: In this screenshot, the black background, the pictures strip on top and the caption at the bottom are drawn with Java2D. You can think of this scene as a composition of three layers: a 2D background, a 3D layer and a 2D foreground. I'm working on another demo called BackSeat using this pipeline. It is a bit more complicated but still remains in the "2.5D space". Chris Campbell and I are also trying to figure out the basic utility methods and classes one might need to easily create such scenes. I already have a few things I will release along with Twinkle source code. Even though you need to know a bit about OpenGL (I started learning for these demos), they let you quickly set up a scene containing billboards and reflected textured quads using anti-aliasing, depth of field or motion blur. Stay tuned. Unexpected Java ToolPosted by gfx on October 10, 2005 at 01:49 PM | Permalink | Comments (6)When I'm home and need to tweak and plot an equation, I use an excellent tool that ships with MacOS X 10.4, Grapher. Easy to use, it fits my purpose and provides a good user experience: As I don't want to carry both my laptops to the office every day, I bring only my Windows box, on which I have yet to find a tool to replace Grapher. I have tried several tools and none convinced me yet. Nevertheless, I decided to give Maple 10 a try. I learned how to use Maple a few years ago at school and I was wondering what new stuff it could offer. Well, first of all, it seems to be written mostly in Java (at leat the whole UI is). The UI is also much easier for newcomers and now provides completion, context sensitive menus, and so forth. But that's not my point. Maple proved to be a valuable Java development tool. It turns out Maple offers a language translation feature that can turn calculations, equations and even "Maple script" procedures into another language. Among the available targets are C, FORTRAN, MATLAB, Visual Basic and... Java. Take this example for instance: To use this equation in my Java source code I can just right click on it and go to the Language Conversion menu: And here is the result: I'll grand you this feature is not the best one I have ever seen to boost my productivity but it's sure really handy, especially when you don't want to mess up parenthesis and other weird mathematical stuff. You can also optimize the computation and then generate the code: This version makes it easier to break the computation into several methods which can be very useful when you know some parts won't change at runtime. I was happily surprised to find a good Java asset with Maple :) SwingFX: Cancelable Infinite ProgressPosted by gfx on October 06, 2005 at 04:18 PM | Permalink | Comments (3)Many months ago, Craig and I started the SwingFX project. One of the first components to be added was the infinite progress panel I described in a blog entry: This component was quite successful (I've recently seen a variation in ZValley's ZEN) but it remains quite simple. Michael Bushe just added a very valuable feature, the ability to cancel the current running task: You can get it today in SwingFX binaries, source and documentation. I also suggest you to take a look at the rubberband API, it can prove to be useful in some applications. Synth Subtlety, Style that ComboBox @#!Posted by gfx on September 28, 2005 at 04:08 PM | Permalink | Comments (14)Synth can be tricky (JScrollbar) sometimes (JTree). So tricky that I actually fell into a trap today. When styling a combo box, I ran into a rather curious problem. A combo box is made of a popup menu, a list, a label, an arrow button and, in case of editable combo boxes, a text field. Since these components are styled independantly, it can be difficult to give them a specific style when in the combo box. Usually, components are styled by regions. That means you bind a style to generic region for every component instance, like a ScrollBar which would be styled for scroll panes, text areas, etc. So, what happens when you style your scrollbars and use a combo box? Behold the disaster: ![]() Hopefully, Synth also allows you to style components by name instead of by region: <style id="arrowStyle"> <imagePainter method="arrowButtonForeground" path="images/upArrow.png" sourceInsets="0 0 0 0" stretch="false" direction="north" /> <imagePainter method="arrowButtonForeground" path="images/downArrow.png" sourceInsets="0 0 0 0" stretch="false" direction="south" /> <imagePainter method="arrowButtonForeground" path="images/leftArrow.png" sourceInsets="0 0 0 0" stretch="false" direction="west" /> <imagePainter method="arrowButtonForeground" path="images/rightArrow.png" sourceInsets="0 0 0 0" stretch="false" direction="east" /> </style> <bind style="arrowStyle" type="region" key="Arrowbutton" /> <style id="comboArrowStyle"> <imagePainter method="arrowButtonForeground" path="images/comboboxArrow.png" sourceInsets="0 0 0 0" stretch="false" /> </style> <bind style="comboArrowStyle" type="name" key="ComboBox.arrowButton" /> Oh how delightful to know we solved yet another issue! But wait... no we didn't @!# The combo box still uses scrollbars' arrow button. Try to remove the style called "arrowStyle" and the combo box will be fine but not the scrollbars. Try to change the order of the styles and nothing will change. If you take a closer look at comboArrowStyle, you'll see it doesn't define a direction for the painter. Why should we after all? A combo box as only one arrow button, there's no need to identify it by its direction. Unfortunately, Synth will internally merge both styles and, at rendering time, it will pick the most specific painter, the one with a direction. That's right, it will pick the painter from the region. Hence, the final solution is very simple: <style id="comboArrowStyle"> <imagePainter method="arrowButtonForeground" path="images/comboboxArrow.png" sourceInsets="0 0 0 0" stretch="false" direction="south" /> </style> <bind style="comboArrowStyle" type="name" key="ComboBox.arrowButton" /> We finally have the appropriate result: ![]() Physics Laws in Swing ApplicationsPosted by gfx on September 27, 2005 at 04:09 PM | Permalink | Comments (8)I just finished (well almost) a Swing demo using real world physics to animate a drag and drop gesture. You can try the demo or read more about its implementation. If you're very curious you can also read the source code. Finally, here are two screenshots from the demo: Native Look? I'm puzzled...Posted by gfx on September 26, 2005 at 06:58 PM | Permalink | Comments (5)Important update: R. J. Lorimer has the solution!. Be damned you stupid manifest files! :) Disclaimer: It's not in my intent to start another flame war about IDE. Also please note that, as a user, I don't care about native look fidelity. Finally, please, please understand that I' talking about the look only, not the feel. Every now and then, when I read articles about why SWT and/or Eclipse are better than Swing and/or NetBeans, I see the same argument: look and feel fidelity is better. I love Eclipse, I don't like to program with SWT and I have to admit that the result looks closer to Windows native look and feel than Swing. Especially the feel. Yet, until last week I was running my Windows XP box with the Classic theme (that is a slightly improved Windows 2000 look). So, tonight, I decided to take a look at the infamous file chooser. I used Eclipse 3.2 M1, NetBeans 5.0 daily 20050925, Notepad.exe and Java SE 6 1.6 b53. Here are the results: ![]() Fig. 1. Notepad.exe ![]() Fig. 2. NetBeans 5.0 daily 20050925 ![]() Fig. 3. Eclipse 3.2 M1 Now here is my question: did I do something wrong while configuring Eclipse? I usually run with the Eclipse 2.1 appearance but I also tried with the default. I even tried to run a clean install on a new workspace, just in case. I also find weird that scrollbars have Windows XP look but not the buttons. Is this because of SWT or because of me? Synth Week, File ChooserPosted by gfx on September 23, 2005 at 10:59 PM | Permalink | Comments (3)When you bring up a Swing file chooser in Tiger with the Synth look and feel you get a perfectly blank window. Styling Fear not my friends, for this issue has been addressed by bug fix #4972060. You can now declare a few properties to draw the appropriate icons: <defaultsProperty key="FileView.directoryIcon" value="directoryIcon" /> <defaultsProperty key="FileView.fileIcon" value="fileIcon" /> <defaultsProperty key="FileView.computerIcon" value="computerIcon" /> <defaultsProperty key="FileView.hardDriveIcon" value="hardDriveIcon" /> <defaultsProperty key="FileView.floppyDriveIcon" value="floppyDriveIcon" /> Note that each value is the ID of an <imageIcon /> declared in the same style element. Anyway, everything's fine, right? Well... almost. Contrary to most file choosers, especially Basic, Metal and Windows ones, Synth file chooser is lacking four features: create a new folder, browse to parent folder, go to home directory and the choice between the list view and the detailled view. We therefore tackled bug #4972060. These features appear as three buttons and a couple of toggle buttons on top of the file chooser. You can set their icons in a similar fashion as file view icons: <defaultsProperty key="FileChooser.newFolderIcon" value="newFolderIcon" /> <defaultsProperty key="FileChooser.upFolderIcon" value="upFolderIcon" /> <defaultsProperty key="FileChooser.homeFolderIcon" value="homeFolderIcon" /> <defaultsProperty key="FileChooser.detailsViewIcon" value="detailsViewIcon" /> <defaultsProperty key="FileChooser.listViewIcon" value="listViewIcon" /> You should now be able to give file choosers the exact appearance you were wishing for. Synth Week, Components Orientation SupportPosted by gfx on September 23, 2005 at 12:32 AM | Permalink | Comments (7)Many Swing components can be oriented according to your needs. Scrollbars are among the most common oriented components. Despite some support in Synth for oriented components, it is far from being exhaustive in Tiger. While some left aside components can be easily forgotten, for instance the window of a floatable toolbar being dragged, Synth lacked support for important oriented components, like scrollbars. Scrollbars are a great example because many users ran into the problem of skinning both horizontal and vertical scrollbars with a different picture. The following snippet shows how to style scrollbars' track:
<style id="scrollbarTrackStyle">
<state>
<imagePainter method="scrollBarTrackBackground"
path="images/scrollBar-track.png" sourceInsets="0 7 0 7" />
</state>
</style>
<bind style="scrollbarTrackStyle" type="REGION" key="ScrollBarTrack" />
This code works fine when you use a picture which can be stretched horizontally or vertically. Unfortunately, most modern look and feels require the use of gradients to show good looking scrollbars and this is where the problem arise. Take a look at the following screenshot and compare the two scrollbars: ![]() Fig. 1. Ouch, scrollbars look... weird. The result is not really surprising since we use only one picture for both scrollbars. This picture is a gradient meant to be stretched on the vertical axis, thus the glitch in the horizontal scrollbar. By taking a closer look at Synth documentation we discover a special attribute for painters, called "direction". On the screenshot, you can notice the four arrow buttons (two for each scrollbar) are rendered perfectly, thanks to the direction attribute:
<style id="scrollBarArrowStyle">
<state>
<imagePainter method="arrowButtonForeground"
path="images/scrollBar-up.png" center="true" direction="north" />
<imagePainter method="arrowButtonForeground"
path="images/scrollBar-down.png" center="true" direction="south" />
<imagePainter method="arrowButtonForeground"
path="images/scrollBar-left.png" center="true" direction="west" />
<imagePainter method="arrowButtonForeground"
path="images/scrollBar-right.png" center="true" direction="east" />
</state>
</style>
<bind style="scrollBarArrowStyle" type="REGION" key="ArrowButton" />
It would be logical to do the same for the track but you can't because Synth doesn't support direction for scrollbars tracks. And for many other components and methods for that matter. Filed as bug #5033822, this issue is fixed in Mustang b53. Instead of fixing scrollbars only, we took a look at all Swing components and added orientation support wherever we could. This means you can now skin oriented tabbed panes (and their sub-regions), toolbars, etc. The following snippet addresses the issue for our scrollbars:
<style id="scrollbarTrackStyle">
<state>
<imagePainter method="scrollBarTrackBackground"
path="images/scrollBar-track-horizontal.png"
direction="horizontal" sourceInsets="0 7 0 7" />
<imagePainter method="scrollBarTrackBackground"
path="images/scrollBar-track-vertical.png"
direction="vertical" sourceInsets="7 0 7 0" />
</state>
</style>
<bind style="scrollbarTrackStyle" type="REGION" key="ScrollBarTrack" />
Synth can now render the scrollbars properly, as in the following screenshot: ![]() Fig. 2. Thanks to Mustang, scrollbars (and other components) look great. Adding extensive oriented components support in Synth required a lot of changes in SynthPainter. Therefore, some methods might have been forgotten. In such a case, just send me an email and I'll add the missing method as soon as possible. Synth Week, Custom Lines StylePosted by gfx on September 21, 2005 at 11:41 PM | Permalink | Comments (6)Every Swing look and feel relies on properties to customize the rendering. One of these properties allows you, With
UIManager.put("Tree.lineTypeDashed", Boolean.TRUE);
The following screenshot shows the difference between dashed tree lines on the left and regular solid tree lines on the right. ![]() Fig. 1. Regular and dashed lines style in a JTree. Although not vital, this feature was used from time to time and was unfortunately unsupported by Synth. Actually, bug #6258272 reporting this problem had even 3 votes. As part of Mustang b53, this bug is now fixed but instead of just supporting dashed lines we went a step further and added an opportunity for later enhancements. The following snippets enables dashed tree lines in Synth: <style id="treeStyle"> <property key="Tree.linesStyle" type="string" value="dashed" /> </style> As you can see, the property used in Synth is more generic than <style id="myStyle"> <object class="MyGraphicsUtils" id="graphics" /> <graphicsUtils idref="graphics" /> </style> The specified class, in this case
void drawLine(SynthContext context, Object paintKey, Graphics g,
int x1, int y1, int x2, int y2)
The
void drawLine(SynthContext context, Object paintKey, Graphics g,
int x1, int y1, int x2, int y2, Objet styleKey)
A new parameter called ![]() Fig. 2. Curved lines style. Happy styling! Synth Week, Load Themes From AnywherePosted by gfx on September 21, 2005 at 03:10 AM | Permalink | Comments (4)Chase Away Those Fierce NightmaresIn Tiger and early Mustang builds, Synth falls short providing a versatile, efficient and easy to use way to load a theme. Only one method is offered to load a Synth theme, the mischievous When your program invokes it, two parameters must be specified: an input stream containing the XML theme and a
SynthLookAndFeel.load(getClass().getResourceAsStream(“synth.xml”),
getClass());
This snippet fits very well in most applications, those in which Synth themes are embedded. Unfortunately this method implies that you must ship a ![]() Fig. 1. A pissed developer. Mustang beta will hopefully chase away your worst nightmares; at least if you are insane enough to dream of cunning and bad tempered Synth look and feels. Whether you don't sleep at night or you just want to know more about Synth, behold the new public void load(URL url) throws ParseException, IOException Thus Freed from the Darkness, You Shall PrevailBut a greater peril lies ahead! To achieve your journey you will need to tame an ancient beast, the ![]() Fig. 2. A developer approaching the dreaded deadline. With the addition of this single new method you can now load Synth themes from pretty much anywhere. At one condition, you must know how to use URL in Java. Here is a short list of possible locations for your themes:
Possibilities are endless and they all prove to be very interesting. You can for instance imagine a servlet dynamically generating Synth files and resources. Whenever you load a theme from a URL, Synth will find the required resources by generating paths relative to the base URL you specify. Actually, Synth doesn't do much of the work here, thanks to the mighty
SynthLookAndFeel laf = new SynthLookAndFeel();
laf.load(new URL("file:///C:/java/synth/laf/laf.xml"));
UIManager.setLookAndFeel(laf);
Loading the same look and feel from an HTTP server, or a servlet for that matter, is exactly as easy:
laf.load(new URL("http://host/laf.xml"));
I stressed the fact the
laf.load(new URL("jar:file:///C:/synth-laf.jar!/laf.xml"));
The “jar:” protocol can be used to access a specific file within a jar. Thus, the first part of the URL is another URL, pointing to the jar file itself, and the second part, beginning with an exclamation mark, is a path to the requested file. In this example we tell Synth to load the file “synth.xml” in the jar “synth-laf.jar”. If you look closely at the file path, you'll notice it's an absolute path. You must not forget the leading slash or you might encounter a cryptic exception. Don't worry about the paths specified in the XML file itself as you can use relative and absolute paths. Even tough the latest example uses a local jar file this is not the only kind of location you can reach. Remember the first part of a “jar:” location is a full URL. That means you can write this to load the theme from a jar delivered by an HTTP server:
laf.load(new URL("jar:http://host/synth-laf.jar!/laf.xml"));
This is Your New Power, Use it WiselyThe new Default protocols in Java SE should prove to be enough for most applications. Nevertheless, if you need to support an exotic protocol or if you need to create your very own (what about “db:” to load from a database or “kde:” to generate a look and feel from a KDE theme) do not forget you can plug your own protocol handlers in the JDK. I won't enter in the details here but you basically need to create a new This new feature will be available in Mustang b53 and is brought to you by bug fix #5056424. Synth Week, Bonus DayPosted by gfx on September 16, 2005 at 03:15 PM | Permalink | Comments (4)Painters AggregationSynth lets you define a painter for each region of a component. A painter can be used, for instance, to draw a button's border or a tabbed pane's tabs. Here is a short example:
<style id="buttonStyle">
<state>
<imagePainter method="buttonBackground"
path="images/button.png"
sourceInsets="5 6 7 6" />
</state>
</style>
This style declaration draws the background region of a button with an image painter. A painter is identified by its method (or the component's region to paint) and sometimes a direction. Scrollbars are a perfect example for the direction property:
<style id="scrollBarThumbStyle">
<state>
<imagePainter method="scrollBarThumbBackground"
direction="horizontal"
path="images/scrollBar-thumb-horizontal.png"
sourceInsets="0 7 0 7" />
<imagePainter method="scrollBarThumbBackground"
direction="vertical"
path="images/scrollBar-thumb-vertical.png"
sourceInsets="7 0 7 0" />
</state>
</style>
This style defines the painters for scrollbars thumbs. The pictures are stretched to match the size of the thumb they are associated to. The following pictures represent the image used as a source by the first painter and the result once applied to the component itself: ![]() Everything works as expected. Unfortunately there is no way, using this stretching technique, to also draw a grip on the scrollbar thumb. Yet I managed to do it in the following application: This application takes advantage of a new Sytnh feature in Mustang, painters aggregation. I you declare several equal painters in Tiger, only the last one will be used. Two painters are equal when they have the same method and the same direction. Mustang changes this behavior by aggregating them into a single painter. The resulting painter can be seen as a stack of painters, the first one declared in the XML file being at the bottom of the stack. Thus, we can achieve our "grip" effect in the scrollbar by changing our previous style:
<style id="scrollBarThumbStyle">
<state>
<imagePainter method="scrollBarThumbBackground"
direction="horizontal"
path="images/scrollBar-thumb-horizontal.png"
sourceInsets="0 7 0 7" />
<imagePainter method="scrollBarThumbBackground"
direction="vertical"
path="images/scrollBar-thumb-vertical.png"
sourceInsets="7 0 7 0" />
<imagePainter method="scrollBarThumbBackground"
direction="horizontal"
path="images/scrollBar-thumb-horizontal-grip.png"
center="true" />
<imagePainter method="scrollBarThumbBackground"
direction="vertical"
path="images/scrollBar-thumb-vertical-grip.png"
center="true" />
</state>
</style>
As you can see, we just create two new painters of the same method and direction as the original ones only in this case they use the property Scrollbars thumbs are only one example of where aggregated painters come in handy and you can think of many other examples. For instance, you can easily compose a button with a gradient and a transparent gloss to create an Aqua style. By aggregating painters instead of compositing everything in one picture gives your more flexibility in case you need to modify part of the style. And remember you can aggregate more than two painters. Zoom Pictures with SwingLabs FXPosted by gfx on September 03, 2005 at 01:25 AM | Permalink | Comments (13)Zoomable Image PanelI read many Java forums and answer to a lot of questions about Java and Swing people send me by email. During the past few years, I've often seen users asking how to create a component which would let you display an image but also zoom in and out. I decided to take advantage of the SwingLabs FX module I recently introduced to provide such a component. As FX contains nice effects, I took my chance to mix everything together. The Demo (run it)This new demo is very similar to the one I made to present the When you run the application, a picture is loaded into a The APIWhile the component is still a bit crude, the API is really simple to use. Here is how you can create a new zoomable image with a zoom factor of 10% and a zoom level comprised between 25% and 400%:
Image image = ImageIO.read(new File("~/yosemite1.jpg"));
ZoomableImagePanel panel = new ZoomableImagePanel(image);
Getters and setters give you access to the zoom controls: minimum and maximum zoom level, zoom factor and current zoom level. The panel provides by default a support for the mouse wheel but you might want to disable it in certain cases (for instance, if you want the mouse wheel to scroll the enclosing scroll pane instead of zooming in and out): panel.setMouseWheelSupportEnabled(false); Finally you can change the displayed image at anytime: panel.setImage(newImageInstance); Do not forget that, as a panel, this component can embed children. The demo does not use this ability but puts the zoomable panel within a drop shadow panel, which in turn sits on top of a checkboard panel thanks to the stack layout. Synth StudioPosted by gfx on September 01, 2005 at 02:47 AM | Permalink | Comments (14)Synth Studio EA1Synth Studio Early Access 1 is a package of three very simple tools I wrote to help me port existing look and feels to Synth. The downloadable archive contains everything you need to run them but I haven't included the source code yet, for I need to figure out which license to use. I might even start a new java.net project if some of you are interested into helping creating a suite of tools to easy Synth look and feels creation. But let's take a look at what Synth Studio already offers. Visual Look and Feel GrinderFor my first experiences with Synth I tried to port existing look and feels. Doing such a thing can be really painful if you take the time to capture screenshots of every component, in each possible state. It's even harder because you usually have to extract the component from its background. In the case of non rectangular components, like Alloy or Windows XP buttons, things can get pretty nasty. Visual Look and Feel Grinder helps a lot: In this window, you can selected the look and feel of your choice in the bottom combo box. Look and feels are seeked within the classpath used when you run the application. If you use one of the startup scripts included in the archive, you can just set the CLASSPATH environment variable so that it includes the look and feel you want to "grind". Once you've selected the look and feel of your choice, you can pick a Swing component in the left palette and see it in the central area. Next, you can use the properties sheet on the right to change properties until the component is in the appropriate state. You can finally click either of the two buttons on the lower right to export the component as a PNG file with a transparent background. The result is incredibly easy to use in any good picture editing software and it can serve as excellent basis for a brand new look and feel of your own. Look and Feel GrinderWhile useful, Visual Look and Feel Grinder is quite tedious to use when you want to extract pictures for every Swing component. Look and Feel Grinder works in a very similar fashion but solely with a command line interface. By default, it will use the default platform look and feel (Metal or Ocean on Windows and Linux, Aqua on MacOS X) but you can choose your own by specifying its class name as a parameter: $ ./LookAndFeelGrinder com.sun.java.swing.plaf.motif.MotifLookAndFeel The tool will then create a new directory named after the look and feel and put all the generated pictures in it. Some components will have several pictures, one per state. For instance, a ![]() To achieve its work, Look and Feel Grinder relies on a resource called <beans>
<bean name="JButton">
<state type="disabled" />
<state type="empty" />
<state type="pressed" />
</bean>
<!-- ... -->
</beans>
Each couple bean/state is mapped to a method of public abstract JComponent createJButtonDefault(ComponentContext context); public abstract JComponent createJButtonDisabled(ComponentContext context); public abstract JComponent createJButtonEmpty(ComponentContext context); public abstract JComponent createJButtonPressed(ComponentContext context); The public JComponent createJScrollBarDefault(ComponentContext context) {
JScrollBar scrollBar = new JScrollBar(JScrollBar.HORIZONTAL);
scrollBar.setValue(50);
scrollBar.setPreferredSize(new Dimension(300,
scrollBar.getPreferredSize().height));
return scrollBar;
}
Finally, the grinder calls a renderer on each component. It is, by default, a In the end, both grinders are really useful when used with the third tool, Insets Worker. Insets WorkerThe most annoying part of creating a Synth look and feel (apart from understanding how Synth works :) is to define the insets of each picture used for each state of each component. It is a tedious job we can't really automate and for which picture editing tool aren't really meant for. Insets Worker is a very simple application which helps you define them by loading a picture of a component and dragging guidelines: ![]() You can load a picture by using PNG files and the load button, but you can also drag them directly from Visual Look and Feel Grinder (initiate the drag gesture on the checkboard panel beneath the component). Just drag the four guidelines until they match what you want, read the insets at the bottom and write them down in your Synth XML file. Defining the insets often require single-pixel precision and you can hardly do that with components shown at 100% of their size. Just roll your mouse wheel to zoom in and out: ![]() Is it really a Synth Studio? Not yet...Even though they proved to be really useful in my work, those three tools are still very crude and should be somehow integrated together. They should also be extended so that you can visually design a Synth look and feel without ever writing XML code. Such a graphical tool could even help masking a lot of Synth subtelties and make this skinning technology much easier to use. If you are interested in contributing to such a project, say so and we'll start a new java.net project. Kickin', rockin', jammin' FX for SwingPosted by gfx on August 25, 2005 at 04:42 PM | Permalink | Comments (11)Special Effects?SwingX, one of the SwingLabs projects, offers a very interesting set of components sitting on top of Swing. Just before JavaOne, Richard Bair and I came up with a new Swing border you can use to cast shadows around your components. Creating this simple graphical effect gave us a lot of weird ideas and we decided it would be great to have a separate incubator dedicated to that kind of special effects. Enter FX, a new SwingX module you can check out today in the incubator. If you have a java.net login, you can follow the instructions to get the source code from the CVS. You'll find the FX module in the directory FX ComponentsFX aims to become a playground for developer to try out new graphical effects for Swing user interfaces. To start this new project we picked a few things I did for previous demos, cleaned them and, best of all, documented them. As of today, FX offers the following components:
Here is a detailled view showing how the stack layout is used to build this UI: FX Demo (run it)But enough about the past, what can we build today with FX? The CVS repository contains a demo called You can also run this demo right now. In this demo, a stack layout contains a checkboard panel and a drop shadow panel which contains all the Swing widgets. You can change the shadows appearance by using the controls on the right. You can even change the displayed picture. The two buttons at the bottom do not work, they are just here to desmontrate how well this panel works with Swing components. Despite the complex visual result, building this UI is very easy:
JPanel panel = new JPanel(new StackLayout());
ImageIcon icon = new ImageIcon(getClass().getResource("images/subject.png"));
picture = new JLabel(icon);
dropShadowPanel = new DropShadowPanel(new BorderLayout());
dropShadowPanel.setDistance(10);
dropShadowPanel.add(picture);
JPanel buttons = new JPanel();
buttons.setOpaque(false);
buttons.add(new JButton("About"));
buttons.add(new JButton("Exit"));
dropShadowPanel.add(buttons, BorderLayout.SOUTH);
JPanel debugPane = buildDebugPane();
debugPane.setOpaque(false);
dropShadowPanel.add(debugPane, BorderLayout.EAST);
panel.add(new CheckboardPanel(), StackLayout.BOTTOM);
panel.add(dropShadowPanel, StackLayout.TOP);
If you ignore the clutter and concentrate on the FX API, you'll see you need one line to create a stacked panel, one line to create a drop shadow panel and one line to create the checkboard panel. Please refer to dropShadowPanel.setAngle(90); dropShadowPanel.getShadowFactory().setColor(Color.GREEN); This is just the beginning and we have a lot of ideas for FX as well as (almost) ready to roll code. Don't be shy and come join us :) About Plastic Look and FeelPosted by gfx on July 24, 2005 at 12:12 AM | Permalink | Comments (15)The Plastic look and feel family is very well known and widely used among Java community. That said I am often disappointed to see some programmers plug it into their UI without taking a look at the available options. Let's take a look at a sample, rather bad looking Swing application: ![]() "Yiech!" And you're right. One way to fix this UI is to change the look and feel. Now, it has many other issues, like colors and icons, but we won't bother about this today. I know Plastic looks great and I also know a lot of people like. Best of all it's free and open source, so why not use it? Here we go: PlasticLookAndFeel laf = new PlasticXPLookAndFeel(); PlasticLookAndFeel.setMyCurrentTheme(new ExperienceBlue()); UIManager.setLookAndFeel(laf); Note I chose a theme for the look and feel because, after all, I really do care about the result: ![]() Ah much better, isn't it? Many programmers would just stop here and go back to more interesting stuff. Yet we can go a bit further with three extra lines of code:
getToolBar().putClientProperty("JToolBar.isRollover",
Boolean.TRUE);
getToolBar().putClientProperty(Options.HEADER_STYLE_KEY,
HeaderStyle.BOTH);
getJMenuBar().putClientProperty(Options.HEADER_STYLE_KEY,
HeaderStyle.BOTH);
As a result, Plastic applies a different style to both the menu bar and the tool bar, giving them a nice, modern rounded look: ![]() I also use Swing's JToolBar.isRollover property to hide buttons borders. Even if the result is better looking, both bars show unecessary borders which clutter the screen, mainly on the left and right of the frame. I'll let you get rid of them as an exercise :) And we can go even further by adding drop shadows to popup menus:Options.setPopupDropShadowEnabled(true); Those menus integrate nicely with Windows XP: ![]() Finally you might not like tabs look. The last feature of Plastic I'm about to use let you give tabs an Eclipse look:
tabbedPane.putClientProperty(Options.EMBEDDED_TABS_KEY,
Boolean.TRUE);
The only problem is that selected tab is a bit difficult to spot when there are many tabs: ![]() Take a look at PlasticLookAndFeel and Options classes and try all the options. You might find nice things for your UI :)
Apple moves to x86 and the winner is JavaPosted by gfx on June 07, 2005 at 12:45 AM | Permalink | Comments (4)Yesterday, Steve Jobs proudly announced the 3rd major transition in Apple's history. After the move from the 68000 family to the PowerPC and after the move from MacOS 9 to beloved MacOS X, Apple will move from PowerPC to Intel x86 family. And Steve Jobs confirmed the rumors: Apple compiled and built all its applications and releases of MacOS X for x86. "Just in case" said Jobs. Why does it has to do with Java anyway? Well moving from one CPU architecture to another will demand a lot of work from the Mac community. Sure it won't be as hard as moving from MacOS 9 to MacOS X, but still, it's huge. As usual, Apple takes care of easing the pain: Xcode 2.1 can generate PowerPC and x86 binaries. Best, you can generate them in one single universal (or "fat") executable. MacOS X will also be shipped with Rosetta (Steve Jobs maybe want to be seen as the Champollion of IT), a runtime library to run PowerPC binaries on x86. Despite these (wonderful) tools, nothing will be as easy as porting your Java apps from MacOS X PPC to MacOS X x86. You'll just have nothing to do. Jobs even emphasized this point when, speaking about the amount of work required to port existing applications to x86, he explained that neither Dashboard Widgets nor Java apps will need the slighest change. Unfortunately he deceived us. Some Dashboard Widgets will need a bit of work, at least a new build. That's why we are the winners in this transition. We can rejoice to get a user friendly, stable and elegant OS on our well known x86 hardware and still enjoy our favorite development platform without bothering a minute about it. If you needed a proof of the advantage of hardware decoupled bytecode, you got it right in front of you my friends. We live interesting times and I'm glad I'm not talking about server side or mobile stuff. P.S: This is one more reason to check out the Quaqua Look and Feel and to contribute. As WinLAF, this look and feel aims to fix the glitches and minor bugs in comparison to the native look and feel. Sub-pixel Anti Aliasing in MustangPosted by gfx on June 06, 2005 at 12:21 AM | Permalink | Comments (2)Ever since Microsoft added support for sub pixel anti aliasing in Windows (a well known technology they decided to label ClearType) many people wanted to get it in Swing. Sure we had access to anti-aliased text (and in J2SE 5.0 we could even use the tricky -Dswing.aatext=true to activate it globally) but it was far from good for small font sizes. Thankfully, Sun's guys did a great job here and recently closed RFE #4726365 Java 2D to support LCD optimized anti-aliased text (sub-pixel resolution). LCD optimized text is even turned on or off according to the host settings. We can expect a lot of problems at first (like custom components or UI using API not supporting the new AA algorithm) but eventually we'll have nice looking text in our Swing apps. Mustang is definitely exciting for rich clients developers \o/ | ||
|
|