Fuse/Swing Demo

Posted on Sunday February 12, 2006

Go take a look at Fuse's CVS, we just added a demo showing how to use Fuse with Swing. The demo shows custome components and lets you switch the UI theme at runtime. Better yet, you can edit the *.uitheme file yourself, click the buttons and see your changes. We have some performance glitches but we are working on that. You can also WebStart the demo. The demo requires full permissions but we'll try to fix that as well. Also, the web start demo does not let you edit the theme file but I'll update the demo one of these days :)

Black

Red

Green

All the injection/UI reloading is done that way in the code:

// loading/injecting
hive = new SwingHive();
hive.load("/swing/black.uitheme");
hive.inject(content);
hive.inject(header);
hive.inject(footer);
hive.inject(title);
// reloading the UI (load properties + repaints the UI)
hive.load("/swing/black.uitheme");

The Hive concept is still a work in progress but it basically keeps track of changes for you and call a default action, for instance repaint(). You can also create your own hive. Finally, the third screenshot was taken by editing the black.uitheme file. The edited version is here:

########################################################################
# Green UI Theme
# Fuse/Swing Demo
########################################################################
########################################################################
# COMMON
########################################################################
Common.shadowOpacity=0.1
Common.shadowColor=#000000
Common.shadowDirection=60
Common.lightColor=#235400
Common.darkColor=#002700
########################################################################
# HEADER
########################################################################
HeaderPanel.backgroundGradient=/swing/header-gradient.png
HeaderPanel.lightColor={Common.lightColor}
HeaderPanel.shadowColor={Common.shadowColor}
HeaderPanel.preferredHeight=32
########################################################################
# FOOTER
########################################################################
FooterPanel.preferredHeight=17
FooterPanel.backgroundGradient=0,0 | 0,15 | #236F00 | #002700
################################################################################
# CONTENT
################################################################################
ContentPanel.backgroundGradient=0,105 | 0,432 | #002700 | #239000
ContentPanel.light=/swing/content-light.png
ContentPanel.lightOpacity=0.5
################################################################################
# TITLE LABEL
################################################################################
TitleLabel.shadowOpacity={Common.shadowOpacity}
TitleLabel.shadowColor={Common.shadowColor}
TitleLabel.shadowDistance=2
TitleLabel.shadowDirection={Common.shadowDirection}
TitleLabel.titleFont=Helvetica Neue-BOLD-80
TitleLabel.titleColor=255, 255, 255, 127
gfx @ 3:07 AM
Filed under: Swing
Gone For Two Weeks

Posted on Saturday February 11, 2006

I'll be offline for the next two weeks. My best friend is coming to the US for the first time and we'll just hit the road. I should have really cool pictures when we come back. And after that I'm going back to France for a day. See you soon :)

gfx @ 12:51 AM
Filed under: General
Fuse 0.1 Released

Posted on Friday February 10, 2006

Fuse 0.1 is now available. You can go to the project page to read the documentation and short tutorial or go directly to the downloads page. Many thanks to Daniel Spiewak who helped me a *lot*. Also I'd like to thank Jeffrey Olson for his last minute documentation review.

You can learn more about Fuse in Fuse, UI Oriented Resource Injection and Even More Fuse. For future reference, please consult the Fuse Blog.

Release highlights:

  • Full documentation (javadoc and HTML manual)
  • Multiple resource injectors
  • Type loaders specific properties
  • Modular design
  • Support for both Swing and SWT
  • 13 data types supported in /Core
  • 14 data types supported in /Swing
  • 8 data types supported in /SWT
  • Override resources names (annotation parameters key and name)
  • Override default data loaders (annotation parameter loader)
  • Default properties (start with a *)
gfx @ 9:40 PM
Filed under: Java
IntelliJ IDEA is Really Cool

Posted on Friday February 10, 2006

I have been using IntelliJ IDEA 5.0 and 5.1 quite a lot recently and I must admit I understand why so many people say it's a very good IDE. There are things I sill need to get used to (like the four key shortcuts for the code completion) but overall I'm really pleased. As usual, it's hard to be fair after having used another tool for years so I kept my mouth shut until I was sure of which things I liked and disliked in IDEA. There are sill minor features I miss from Eclipse but to be honest, this is a wonderful and very productive tool. I have encountered a few bugs (hint: don't use CVS diff dialogs and a vertical dual screen setup :) and I have some griefs (why oh why no dialog offer a maximize/restore button?! and why the new class dialog doesn't offer a way to create an internal class?) but nothing really serious. Oh and I prefer NetBeans and Eclipse looks to IDEA's (no matter the look and feel selected in IDEA.) Apart from that the intentions, the static analysis, the refactorings, the usability, the camel humps in code completion are so cool that I can forgive those drawbacks easily. I'll post more details when I have more time but I am about to leave for two weeks so I wanted to say something :)

Last but not least, I'd like to thank JetBrains team and Maxim Shafirov in particular for the license they offered me as a thank you for working on Swing. That was a vert nice gesture guys, I truly appreciate it!

gfx @ 8:39 AM
Filed under: Java
Fuse 0.1: Very Soon

Posted on Tuesday February 07, 2006

Daniel Spiewak and I are working on Fuse 0.1. He's a SWT guy, I'm a Swing guy, so Fuse will be able to cover both toolkits :) In fact, we introduced API changes for SWT. You can now set properties on an injector with setProperty(). You can also set properties common to all injectors with setCommonProperty(). These properties are passed to the type loaders. For instance, SWT type loaders require some extra resources like a Display or whatever it is that SWT requires. The bottom line is you can "customize" type loaders. The doc needs to be updated to reflect this.

Daniel also implemented a new fallback feature for properties. If a property is not found, and if you did not use the key parameter in @InjectedResource, then Fuse will try to find a property named *.name. For instance, if you have a field named MyComponent.foreground but no corresponding property, *.foreground will match. You can see those properties as common or global properties.

Before releasing Fuse 0.1, Daniel and I are trying to figure out a way to have modules in Fuse. Fuse/Core will contain the basic API and the common type loaders (String, int, double, etc.) whereas Fuse/Swing and Fuse/SWT will contain a set of type loaders specific to the corresponding toolkit. We have to finalize the API but here is what you will do before using a module:

ResourceInjector.addModule("org.jdesktop.swing.SwingModule");

I am not really happy to force users to make another API call but it offers a good flexibility. Fuse will be distributed as several JAR files: one for /Core and one for each module. Ultimately, this would allow other people to write their own modules (for instance Fuse/JSF or Fuse/Whatever.)

The public API should now reach a stable state. I doubt we will add more features in the incoming releases. I really want this API to remain simple. More details when Fuse 0.1 is out :)

gfx @ 10:57 PM
Filed under: Swing
Another Annoying Eclipse Bug, So Long Eclipse!

Posted on Tuesday February 07, 2006

Gngngngngngngn. Eclipse 3.2M4 was getting so annoying with its bugs (and because I couldn't make AspectJ plugin work with it, even by using the appropriate version) that I swtiched back to Eclipse 3.1.2 today. At first I was glad: the IDE is much faster, I have less issues than earlier... but I ran into a compiler bug. Here is a kind of declaration I use with Fuse:

@InjectedResource
private Color backgroundColor, foregroundColor, ...;

I usually never use this style but with Fuse I end up using many lines of code by having the annotation on one line and the field on a second line. As I am not modifying the fields in my code, I decided to compact them into a single declaration. This compiles with with javac and Eclipse 3.2M4... but Eclipse 3.1.2 applies the annotation only to the first declared field.

So that's definitely it, I wasted way too much time with Eclipse. Welcome NetBeans 5.0 and IntelliJIDEA 5.1 (more on the latter in a future blog entry.)

gfx @ 9:08 AM
Filed under: Java
Living Off the EDT Thanks to AOP?

Posted on Tuesday February 07, 2006

Thanks to Ramnivas (see comments) for pointing me out what I was doing wrong :)

Ah, what a joyce to use cool acronyms in a blog entry title. But that's not the point. A few weeks ago, Alexander Potochkin wrote something about using an @OnEdt annotation to make Swing threading management easier. So today I wrote an AspectJ aspect to handle this and see by myself whether it could be a good solution to manage everything for the developer. First the annotation:

import ../../page/java.lang.annotation.ElementType.css;
import ../../page/java.lang.annotation.Retention.css;
import ../../page/java.lang.annotation.Retention.cssPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OnEDT {
}

Then, the aspect itself:

import ../../page/java.awt.EventQueue.css;
import ../../page/java.lang.reflect.InvocationTargetException.css;
import ../../page/java.lang.reflect.Method.css;
import ../../page/javax.swing.SwingUtilities.css;
public aspect OnEdtAspect {
    pointcut onEDT() : call(@OnEDT * *(..));
    void around() : onEDT() {
        if (!EventQueue.isDispatchThread()) {
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    proceed();
                }
            });
        }
    }
}

Whenever a method annotated by @OnEDT is called, it checks whether it is running in the EDT. When it's not, the method is invoked again, but in the EDT this time. The point is you can write this and be safe:

// In a thread
public void run() {
    while (...) {
        changeTheUI(timeValue);
    }
}
@OnEDT
private void changeTheUI(float timeValue) {
    // It's safe, we are in the EDT!
}

The aspect works only with methods returning void but I guess it's possible to work around this, maybe with generics. You can also go further and apply this aspect to any Swing method by defining the following pointcut:

pointcut inSwing() : call(void javax.swing.*.*(..));

Once again, it handles only void returning methods. As Joshua pointed out, this can be very dangerous when applied to the whole Swing library. Take this small example:

// In a thread
public void run() {
    textComponent.setText(...);
    Sting newText = textComponent.getText();
}

Woops. The first line goes into the EDT and the second line can therefore be executed before the first one!

That was only a quick and dirty hack but I think we should stick to detecting threading issues with AOP (or RepaintManager) rather than trying to address them for the developer. Performance problems left aside, there are way too many errors that can happen because of such a solution.

gfx @ 1:49 AM
Filed under: Swing
Another Java SE 6 Treat

Posted on Monday February 06, 2006

I use Java2D's AlphaComposite a lot. And when I mean a lot it's can be compared the amount of beer a football fan can drink when his team wins the Superbowl (sometimes you get the same hangover the next morning by the way). Anyway, as much as I like this class, it is one of the most verbose classes to use in Swing/Java2D alongside java.awt.Cursor. See how you can create a new alpha composite with a value of your choice:

Composite c = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, animatedTranslucency);

It is so tedious I set up macros for that in my IDEs (and likewise for SwingUtilities.invokeLater() and the cast of Graphics to Graphics2D). It also clutters painting code. Hopefully, I found a better way to do that in Java SE 6 by reading Chris' source code the other day:

Composite c = AlphaComposite.SrcOver.derive(animatedTranslucency)

This new method comes straight from java.awt.Font and makes the code not only a bit shorter but a heck lot easier to read.

gfx @ 6:23 AM
Filed under: Swing
JavaPolis 2005 Audio Talks

Posted on Friday February 03, 2006

The guys at JavaPolis have started to publish audio tracks of JavaPolis 2005. It seems they are publishing new content every week, so it should give us the time to watch everything we want to :) As usual, the audio is displayed along with the slides. That just rocks.

gfx @ 1:29 AM
Filed under: Java
NetBeans 5.0 is here!

Posted on Thursday February 02, 2006

They made it, NetBeans 5.0 is available for download. I already said it several times, but if you haven't already, give Matisse a try.

gfx @ 1:44 AM
Filed under: Swing
Even More Fuse

Posted on Thursday February 02, 2006

Following Alex suggestions I updated Fuse. Actually it's more an upheaval than anything else. First, ResourceInjector is not static anymore. Here is how to use it now:

ResourceInjector.get().load("/my/resources");
ResourceInjector.get().inject(myComponent);

Calling get() returns the default instance of ResourceInjector, always guaranteed to exist. You can also pass a key to get/create another injector: ResourceInjector.get("my_ui"). Two new methods are also available to better manage injectors life cycle: reset() to clear all the properties and dispose() to clear the properties and remove the injector from the internal registry. It is advised not to keep track of injectors yourself. This new API should be thread safe (I wrote the code but I need to test it). You can have multiple threads calling inject() but only one calling load() (not injection is allowed while a thread has the lock on the load method).

Type loaders have also been updated. The now use a third parameter to load the values, a Class representing the object in which you inject the value. This parameter can be used to resolve resources in the appropriate class loader. It is used for instance by the image and font type loaders. Finally, you can use a specific type loader in lieu of the default one to inject a resource:

class TextEditor {
  @InjectedResource(loader=StringFromFileTypeLoader.class)
  private String defaultTextContent;
}

I updated and extended the documentation on the project page to take into account these changes. I still need to write the section explaining how to create a custome type loader but it's pretty straightforward.

gfx @ 12:11 AM
Filed under: Swing
More Fuse

Posted on Wednesday February 01, 2006

I just updated the project page for Fuse. It contains a small step by step tutorial and describes the public API. It also shows all the supported data types and what format you should use for every one of them in the properties file.

gfx @ 8:18 AM
Filed under: Swing
Fuse available on java.net

Posted on Tuesday January 31, 2006

I just released Fuse on java.net. It already changed a bit since the first time I talked about it. Now, here is how you load and inject resources:

// Can be an InputStream, a URL, a File, a String or a Class/String pair
ResourceInjector.load("/resources/theme.properties");
ResourceInjector.inject(componentInstance);

Fuse now handles component hierarchies as in the following example:

class MyComponent extends JComponent {
  @InjectedResource
  private Color background;
}
class MyCoolComponent extends MyComponent {
  @InjectedResource
  private Color foreground;
}
// Injects resources in MyCoolComponent and MyComponent
ResourceInjector.inject(myCoolComponentInstance, true);

Finally, you can prevent namespace collisions in the properties file by using the two annotation parameters I introduced in @InjectedResource:

class MyComponent extends JComponent {
  @InjectedResource(key="Common.foreground")
  private Color foreground;
  @InjectedResource(name="backgroundColor")
  private Color background;
}

Without parameters, both properties keys would be resolved by Fuse to MyComponent.background and MyComponent.foreground. The key parameter lets you override the property key, in this case MyComponent.background becomes Common.foreground. The second parameter, name, lets you override the right part of the key. Thus, MyComponent.background becomes MyComponent.backgroundColor. If you use both parameters, key will supercedes name.

Fuse is not available in a binary form yet. You can grab its source code from the CVS. The repository contains project files for NetBeans, IntelliJ IDEA and Eclipse. Enjoy!

gfx @ 9:08 AM
Filed under: Swing
Find a Regression in Mustang, Win an Ultra20

Posted on Tuesday January 31, 2006

Sun is launching a contest to find regressions between J2SE 5.0 ("Tiger") and Java SE 6 ("Mustang"). David Herron explains the details but basically, if you help Sun find a regression, we will do our best to fix it before Mustang goes final. David clearly explains why, in some cases, a regression might no get fixed. If it ever happens, you will know why. Oh and there will be some cool rewards for some of you :)

The contest home page.

gfx @ 7:26 AM
Filed under: Java
Fuse, UI Oriented Resource Injection

Posted on Monday January 30, 2006

I spend a lot of time designing and creating graphical user interfaces. Hence, the bulk of my work is not to design object oriented architectures or think of complicated algorithms. Instead, I mostly tweak values: colors, opacities, gradients, rendering hints and so forth. Creating nice mockups in an image manipulation program is unfortunately not enough and I have to adapt the design to Java SE limitations, my liking and colleagues suggestions. As you can imagine, tuning a value for the umpteenth time in the day to rerun the application and check the result can become somewhat tedious. Unfortunately, this is exactly what I have been doing lately for an internal project at Sun. When the project started I knew I would soon beg for mercy or eat my keyboard out of despair if I didn't find something to ease my pain.

This is why I created Fuse a small library designed to change GUI resources easily. It has other advantages like the ability to reload values at runtime and decoupling the resource loading code from the GUI. (To sound smart and wise I say it's a resource injection library; but one day I'll have the same gray hair as Chet and I'll naturally look smart and wise even though I'll still be a fool - like Chet :) Anyway, Sun let me open source the project which will become a sub-project of SwingLabs as soon as I get the approval from java.net.

"Ye gods! Cut to the chase and show us some code!", are you probably yelling behind your screen by now. Before delving into this matter, I'd like to warn you the API is far from being finalized yet. The names are likely to change. (hitherto I was using the notion of "themes" and "theme resources" instead of the current broader definition) One last thing: you might hate the apparent trickery introduced by the use of Fuse. You will need two things: a Java class and a plain text file. The first step is to identify the resource you want to inject in your instances:

// A component showing a title on a colored background
class TitleComponent extends JComponent {
  @InjectedResource
  private Color foreground, background;
  @InjectedResource
  private Font font;
}

Each field marked by the annotation @InjectedResource will be handled by Fuse. The second step is to inject values into those resources:

public static void main(String... args) {
  TitleComponent t = new TitleComponent();
  ResourceInjecter.inject("classic_theme", t);
}

The call to inject() will browse the annotated fields from instance t and give them a value found in a properties file called /resource/classic_theme.uitheme (this will change, but this is how it works in the project from wich Fuse is born). As you can see, there is some black magic at work here since Fuse will assign a value to the private fields. Many might despise that, I think it's ok. You can also perform the injection in the component's constructor if you need the values right away. Now, here is the content of the properties file:

TitleComponent.background=#FFFFFF
TitleComponent.foreground=#000000
TitleComponent.font=Arial-BOLD-42

Depending on the type of each resource, Fuse will invoke a specific TypeLoader which role is to create the appropriate value from the plain text. Each value is identified by the field name prefixed by the class name. The library ships with many type loaders: boolean, byte, character, color, composite (and alpha composite), double, file, float, font, gradient paint, image icon, image (and buffered image), insets, int, long, rendering hints, short, string, stroke (and basic stroke) and URL. You can also easily create your own type loaders.

The good thing with these type loaders is they allow you to define values in an easier way. For instance you can define a font with the format Face-Style-Size but if Face is a TrueType font file, Fuse will load it for you. Another example is the rendering hints: if you want to use the value VALUE_ANTIALIASING_ON for the key KEY_ANTIALIASING, you can write one of the following:

MyComponent.hints=KEY_ANTIALIASING=VALUE_ANTIALIASING_ON
MyComponent.hints=key antialiasing=value antialiasing on
MyComponent.hints=antialiasing=antialiasing on
MyComponent.hints=antialiasing=on

Fuse will try to be smart and infer hints names (for instance a key "antialiasing" is actually KEY_ANTIALIASING, and its value "on" must be VALUE_ANTIALIASING_ON). Very often in a UI design you will reuse the same values over and over and Fuse makes this easy by allowing references:

Common.darkColor=#101010
MyComponent.shadowColor={Common.darkColor}
MyComponent.gradient=0,0 | 0,400 | {MyComponent.shadowColor} | #FFFFFF

A key surrounded by curly braces is interpreted as a reference. The library will detect circular references (for instance if Common.darkColor was defined by {MyComponent.shadowColor}) and will warn you about them. Speaking of which, every type loading can raise a TypeLoadingException which tells exactly which property is wrong and why. (and you don't have to catch this exception, but you can)

Another nicety is the ability for a single type loader to inject a value into different types of a same class hierarchy. Take these declarations for instance:

@InjectedResource
private Image smallLogo;
@InjectedResource
private BufferedImage bigLogo;

Both these values will be injected by ImageTypeLoader. Finally, you can reinject the values any time you want, and this is why I first named the ResourceInjecter a Theme, by calling inject(). This really saves quite some time when you just want to adjust a value without having to create some annoying GUI for debug purpose only.

I have many ideas for Fuse. For instance, it'd be nice if it let you define the naming pattern of your properties files. (so that they don't have to be /resource/*.uitheme) I also would like to introduce an optionnal component hive that would keep track of every component in which you injected resource. The hive would then be able to reinject values at runtime without you having to go through your instances.

No matter what, you will see this library in my future demos :)

As a bonus, here is an example of a real component I created for the aforementionned project:

class Footer extends JComponent {
    @InjectedResource
    private LinearGradientPaint backgroundGradient;
    @InjectedResource
    private int preferredHeight;
    @InjectedResource
    private Color lightColor;
    @InjectedResource
    private Color shadowColor;
    Footer(final Channel channel) {
        ResourceInjecter.inject(channel, this);
    }
    @Override
    public Dimension getPreferredSize() {
        Dimension size = super.getPreferredSize();
        size.height = preferredHeight;
        return size;
    }
    @Override
    public Dimension getMaximumSize() {
        Dimension size = super.getMaximumSize();
        size.height = preferredHeight;
        return size;
    }
    @Override
    protected void paintComponent(Graphics g) {
        if (!isVisible()) {
            return;
        }
        Graphics2D g2 = (Graphics2D) g;
        Paint paint = g2.getPaint();
        g2.setPaint(backgroundGradient);
        Rectangle clip = g2.getClipBounds();
        clip = clip.intersection(new Rectangle(0, 2, getWidth(), getHeight()));
        g2.fillRect(clip.x, clip.y, clip.width, clip.height);
        g2.setPaint(paint);
        g2.setColor(lightColor);
        g2.drawLine(0, 0, getWidth(), 0);
        g2.setColor(shadowColor);
        g2.drawLine(0, 1, getWidth(), 1);
    }
}

And the properties file:

Common.shadowOpacity=0.7
Common.shadowColor=#000000
Common.shadowDirection=60
Common.lightColor=#4B5461
Common.darkColor=#202737
Footer.preferredHeight=17
Footer.lightColor={Common.lightColor}
Footer.shadowColor={Common.darkColor}
Footer.backgroundGradient=0,0 | 0,15 | 0.0,#666F7F | 1.0,#202737
gfx @ 8:57 AM
Filed under: Swing