57 45 4c 4c 20 44 4f 4e 45 21
                   
Jun 30

Creating a Notification Popup Widget

Posted on Tuesday, June 30, 2009 in Articles, Eclipse Articles.

In this article I will explain how to create a custom widget that displays a popup notification dialog in the bottom right corner of your screen (usually above the toolbar on Windows). Here’s what it will look like when we’re done:

Example Image

We could custom draw everything which would allow us more control over the widget, but for the sake of this article lets stick to using basic SWT components inside a normal Shell that we will make a bit prettier than your typical SWT Shell.

(Do note this has only been tested on Windows, it’s quite possible tweaks are necessary for other platforms).


The Shell

What we want is a Shell that stays on top of our application (but not on top of all applications) and doesn’t steal the active focus or else it would show up in your task manager or move focus away from where the user is now. We also don’t any normal Shell “trim” borders, we’ll draw our own. Creating it is easy as all of our requirements are available via SWT flags. Thus we can create the Shell as follows:

_shell = new Shell(Display.getDefault().getActiveShell(), SWT.NO_FOCUS | SWT.NO_TRIM);

As we have SWT widgets inside our shell and we intend to use a gradient background on the shell itself, all the widgets need to be transparent. This is done by setting the Background Mode on the shell to SWT.INHERIT_DEFAULT. This tells the widgets to inherit the background of their parents, and gives us what we want. So after we create the shell we simply call;

_shell.setBackgroundMode(SWT.INHERIT_DEFAULT);

So how do we get a gradient background on a shell? It certainly don’t support it by default. You can set a foreground and a background but that’s about it. The trick is to paint an image with the colors we want and use that as the shell’s background. To set it at the right time, we listen to the SWT.Resize event on the shell and do it then with the following code;

_shell.addListener(SWT.Resize, new Listener() {
    @Override
    public void handleEvent(Event e) {
	try {
	    // get the size of the drawing area
	    Rectangle rect = _shell.getClientArea();                    
	    // create a new image with that size
	    Image newImage = new Image(Display.getDefault(), Math.max(1, rect.width), rect.height);
	    // create a GC object we can use to draw with
	    GC gc = new GC(newImage);

	    // fill background
	    gc.setForeground(_bgFgGradient);
	    gc.setBackground(_bgBgGradient);
	    gc.fillGradientRectangle(rect.x, rect.y, rect.width, rect.height, true);

	    // draw shell edge
	    gc.setLineWidth(2);
	    gc.setForeground(_borderColor);
	    gc.drawRectangle(rect.x + 1, rect.y + 1, rect.width - 2, rect.height - 2);
	    // remember to dipose the GC object!
	    gc.dispose();

	    // now set the background image on the shell
	    _shell.setBackgroundImage(newImage);

	    // remember/dispose old used iamge
	    if (_oldImage != null) {
		_oldImage.dispose();
	    }
	    _oldImage = newImage;
	}
	catch (Exception err) {
	    err.printStackTrace();
	}
    }
});

The Contents

Our shell consists of 3 sections, an image at the top left (which is a normal CLabel). Then a label that contains the header text (also a CLabel), and finally a label which represents our message (a normal Label). We use a Label at the end instead of a CLabel as Labels support wrapping and multi-lines better than the CLabel does. You could of course use pretty much any widgets you want. Here’s an outline of what section each label covers:

tray_popup_example_lines

We use a GridLayout with some 5px margins (except at the top) and the widgets are created normally without any special magic, so I won’t show that code here (it’s all at the bottom for you to download).

Some Eye Candy – Fading

Shells support alpha values as of SWT 3.4, so we’ll use this to make our “appearing” and “disappearing” a bit prettier. All we need to do to make a shell fade in and out is to have a thread that loops and increases/decreases the alpha channel value for the shell. As we need to be on the Display thread to do this it’s easiest to do it all via a runnable that we let the Display object run for us at a given interval.

We need to remember to continuously check to see if the shell is disposed as it can happen when we’re on a thread. When we’re finished fading in, we want the shell to stay visible for a certain amount of time, and then we want to fade out. So basically the chain is this:

  • Create shell
  • Fade in shell
  • Stay visible for X seconds
  • Fade out shell
  • Dispose shell

All these timers are more or less similar in terms of how the code looks, so I’ll show one example here and for the rest you can look at the complete source downloadable below. We have some global variables here that should be explained.

  • FADE_IN_STEP is how much we increase the alpha value for each iteration.
  • FINAL_ALPHA is a value that declares what the final alpha value will be for the shell when it stays visible. I find that a slightly transparent shell gives a better effect, so by default this value is 225 (out of 255 which is a solid [opaque] shell).
  • FADE_TIMER is how long we wait until increasing the Alpha value again. When the shell has reached it’s max-alpha value we call the startTimer() method, which is a thread that sleeps for a certain number of seconds before calling the fadeOut() method.
private static void fadeIn(final Shell _shell) {
    Runnable run = new Runnable() {

        @Override
        public void run() {
            try {
                if (_shell == null || _shell.isDisposed()) { return; }

                int cur = _shell.getAlpha();
                cur += FADE_IN_STEP;

                if (cur > FINAL_ALPHA) {
                    _shell.setAlpha(FINAL_ALPHA);
                    startTimer(_shell);
                    return;
                }

                _shell.setAlpha(cur);
                Display.getDefault().timerExec(FADE_TIMER, this);
            }
            catch (Exception err) {
                err.printStackTrace();
            }
        }

    };
    Display.getDefault().timerExec(FADE_TIMER, run);
}

Stacked Notifications

There’s one last thing we want to support, which is stacked notifications. Assume our notification shell’s entire life-span is about 5 seconds, what if we have another notify call before the first one has finished showing? We could dispose the first shell and replace it with the new one of course, but a much prettier approach is to simply move the previous shell up, and show the new notification below it. The effect of this is something like this (this screenshot was taken just as the old shells started to fade out, hence the higher alpha on them):

tray_stacked

What we do to achieve this effect is rather simple. Every time a notification shell is opened, we keep a reference of it in a static array of shells. When it’s faded out (and disposed), it’s removed from the list. Thus, when a new shell is created we simply check to see what’s already in the array and then set the location for each old shell in the array to -heightOfNewNotificationShell (so to speak). Thus, the old shells move up to make room, but continue their fade-in/fade-out cycles just like before.

Further Improvements

Everything could be made prettier than it already is of course, perhaps you want an “X” close button, or different colored text, or rounded shell corners. None of that is in the current code, but the code should give you enough to go on to implement most of that without any major problems.

Also note that I currently use Display.getDefault().getActiveShell() for the parent shell of the popup. You may want to refactor it to use a more permanent shell as otherwise a disposal of whatever the parent shell was whenever the popup was displayed will cause the notification to dispose as well.

The code is not perfect, it’s an example to build on (if you so wish).

Download Code

I’ve created an Eclipse project with the code and also the ImageCache/FontCache/ColorCache classes I use in the code (all they do is keep already used images etc in memory so that they don’t need to be re-created). There’s also a jar with the images I’ve used in this example. If you like them and want more like it, there’s a massive image pack with exactly these icons and more here: Gnome Colors Icons.

Do note you may need to adjust your CLASSPATH settings for the project to reflect the location of your SWT jars.

To run the code, simply run the Tester.java class and push the big button to create a notification. Push it again before the first one fades away to see the stacking. A random image will be used every time you push it.

DOWNLOAD CODE

Conclusion

Hopefully this gives you an insight into how to create a “custom widget” (without much custom painting). Have fun!

  1. Glazed Lists + SWT Tables == True If you haven’t heard about Glazed Lists until now, it’s...

28 Responses to “Creating a Notification Popup Widget”

  1. elvisd - June 30th, 2009

    Should this awesome widget work in linux gtk too?

  2. Emil - June 30th, 2009

    I see no reason why not. It doesn’t use any strange API or magic apart from what’s available in SWT. It’s possible there may need to be some minor tweak (I say that as I haven’t tested it on Linux) but it’s equally possible it works exactly like on Windows.

  3. Chris - June 30th, 2009

    This is cool stuff. There is ongoing discussion here https://bugs.eclipse.org/bugs/show_bug.cgi?id=229823 to introduce general pop-up mechanism to platform (currently it is owned by mylyn).

  4. Nicolas Richeton - June 30th, 2009

    Nice stuff,

    You could replace your animation code by the nebula animation package which supports fading and moving/resizing with non linear increments

    See http://blog.richeton.com/swt-animation-toolkit/ and
    http://www.eclipse.org/nebula/widgets/gallery/javadoc/r_0_20090418/index.html

    But to use this, you’ll have to add a dependency on nebula gallery OR nebula CWT…

  5. Mike - July 2nd, 2009

    Is there a reason all variables start with an underscore?

  6. Emil - July 2nd, 2009

    It’s the way I write class variables, everyone has their own preferred way. In this case it also shows clearly that in any copy/pasted method clause it’s a declared class variable as opposed to a method variable.

  7. philk - July 3rd, 2009

    Emil, nice work! Is there any reason why all this is static? Couldn’t that be a normal class with class vars? Whats the oldImage used for anyway? Its only remembered to be disposed later, but why? Do you need it for anything else beside the buffering of the background?

  8. Emil - July 3rd, 2009

    Yeah, the oldImage is kind of left-over, I was considering reusing it across dialogs but it didn’t end up being done and it still made it into the article :) You can refactor that part. As for why it’s static; it’s just convenience, you could surely change that to fit whatever you prefer.

  9. philk - July 3rd, 2009

    Thanks for your reply. I have just created a nice OSGi service around your popup widget. Now you can just send Notification objects to this service and also register your service in a white-board approach as listener for incoming Notifications. That way I have another bundle now that provides an Eclipse view with a history of events that have been sent. I have modified your code to support StyledString and images, and even Hyperlinks in the Notifications.
    Thanks for giving me a start on this issue.

  10. philk - July 3rd, 2009

    And also added the ability to configure the popups colours and fonts using the theme preference page using the JFace Colour&Font registry. I just had to drop your “SWT only” idea and refactor it to use JFace. But its still pretty easy pluggable into any Eclipse installation. Maybe I will blog about it too.

  11. Leandro Santinho - July 23rd, 2009

    Hi, its so cool, its all I need!!!

    But if the program is minimized or other program is active?
    How I can show de popup anyaway?

    Thanks for all man, good work!!!!!

  12. Leandro Santinho - July 27th, 2009

    Forget my question:

    Resolution is, when create a Shell use SWT.ON_TOP

    THANKS MAN!!!!!!!!!!

  13. Stefan Hoehn - July 29th, 2009

    Just wanted to let you know what an great job you did. As little as it is, it is awesome easy to integrate and exactly what we needed. Thanks for sharing and it is good to have you on the nebula team! :-)

  14. Vahid Vafaei - August 31st, 2009

    Great Pop-Up widget, great work! Thank you very much for sharing this.

  15. Agent Smith - November 17th, 2009

    Hi there,
    Thanks for the good work and for sharing !!
    I would like to reuse your custom widget code in an app that I am willing to make freely available with its sources. Is it possible ? what’s the licence of your code if any ? How can I point back to you in the sources of my app. Cheers.

  16. Emil - November 17th, 2009

    Hiya,

    Go ahead. The code is EPL, so you’re pretty much free to do whatever you want with it. Hope that helps :-) . If you wish to leave credit, just put a comment at the top of the source file(s) that contain a link to Hexapixel, but as it’s EPL that’s completely up to you. Glad you find a good use for it.

    Emil

  17. Phil Kursawe - December 16th, 2009

    Emil, I took the freedom and used this blog entry to start my new series about an OSGi notification service framework.
    http://philondev.blogspot.com/2009/12/notification-framework.html

  18. Emil - December 16th, 2009

    Very cool Phil, thanks for using it :)

  19. vikash - March 1st, 2010

    Emil,

    Nice Article and very useful. I am also a very enthusiastic developer and keep on creating a custom controls same like you did here .
    I saw in your code that you are using lots of FONTS, COLOR and IMAGES. so I am not sure if you are following MSAA standards (Microsoft Active Accessibility Support). Did you tested your custom widget with different screen resolution?
    If this is not working how would you handle the Accessibility events for different OS screen resolution. Kepping in mind that we are not using SWT/JFACE provided fonts and color and creating our new instance of these.

    I am currently working in a RCP application which is going to use by more than 50k users worldwide (and keeo growing). Our Testing team has tested newly created custom controls by IBM JAW’s and unfortunately they failed in the test.

    So can you please advise me how we can create custom controls with our own fonts and Colors which should also work on accessibility point of view (In other words if we can use JFACEResource/Font/Color Registry in appropriate way).

    Would really appreciate if you can help me out. Thanks in bunch

    Vikash

  20. Emil - March 5th, 2010

    Hi Vikash,

    I’m not focusing on any Accessibility Support, especially not for widgets that I create for articles. It’s possible some of the widgets I have as permanent widgets on the site will have support down the line once I get time for it.

    I believe there is articles on Eclipse.org for Accessibility, and I’m no expert on it, so I think you will find better resources there than what I could provide.

    Cheers,
    Emil

  21. The Ghost in the shell – Transparency » Eclipse Papercuts - March 16th, 2010

    [...] 6 Comments   by Lars Vogel / March 16th, 2010 I recently re-read the post Creating a Notification Popup Widget from Emil [...]

  22. Example Eclipse client for StackOverflow.com | Zsolt Török - June 13th, 2010

    [...] will be migrated to the main Eclipse Platform code base sometime in the future. However, there is a short and sweet example of how to implement a notification popup widget from scratch,which I plan to use for this pet [...]

  23. Aubrey Bourke - September 6th, 2011

    Hi,

    I seem to be getting some unusual behaviour. I imported the project into Eclipse on openSuse 11.4 Linux (GNOME). It compiles and runs. The notification appears, but doesnt fade in/out or even time out! All that happens is they stack up and stay on the screen. No icon images either!

    Any ideas as to how to fix this?

    Regards
    Aubrey.

  24. Emil - September 6th, 2011

    Hi Aubrey,

    No idea off hand, but I suppose fading not working could be some sort of graphics issue as usually if a Shell doesn’t support alpha values, it’s due to either the graphics driver not supporting it or the OS / Window OS.

    Other than that I can’t really say, I don’t have a linux version handy at the moment so I can’t really test if for you.

    Regards,
    Emil

  25. Aubrey Bourke - September 7th, 2011

    Hi Emil,

    Yes I had the same problem on my previous Acer Laptop. I have an Acer Desktop now.

    And ofcourse im still using the same linux distro (OpenSuse).

    So I think it probably isn’t a Java bug, but a linux bug. Must go report it now!

    Thanks for the feedback, and for the code – wont give up on it just yet!

    Regards
    Aubrey.

  26. Kourtney - January 14th, 2012

    Your article perfectly shows what I needed to know, thnaks!

  27. Mathieu Garcia - April 13th, 2012

    Very nice!
    I’ve just test your code and it works perfectly with Ubuntu.

  28. Blake Bartenbach - June 26th, 2012

    Great! Exactly what I was looking for. Thanks for this!