Today is a great day… I am done with school! I will finally get my M.S. in Computer Science. Today was also the day we finished our final assignment. Our goal was to write some sort of a component-oriented/network-distributed VNC clone. I ended up in charge of the UI that I had to crank out in very little time. Just for fun, here are some pictures from that application.
The following picture shows the mockup I did in Photoshop. Forget about that “Pause” icon, it sucks but I got lazy and didn’t want to find a better one. And yes, I did borrowed everything from various Mac OS X apps (the tabs come from Transmit and the slider from iPhoto.)
Next, here is a screenshot of the actual application running on Windows XP. Despite the Aqua-like components, the design works pretty well. It took me by surprise.
My only problem with this UI is that I couldn’t find an easy way to get a unified toolbar, as show in the mockup. While the toolbar looks fine on Windows, it looks a bit weird on Mac OS X, as shown below. The gradient in this picture appears while waiting for the first picture of the remote desktop to be received (or around the said picture if the window is too large.)
The user interface contains some other niceties. For instance, the toolbar icons turn black, as on Mac OS X, when pressed, pausing a remote connection dims the picture and tabs show cool mouse over/pressed effects.
If you are curious on how I implemented the mockup, I’d be more than happy to share the code with you. The tabs are drawn with a tabbed pane UI delegate, as is the slider. Anyhow, I though it’d be to show that a Swing UI does not have to look like Swing, and that can be achieved very easily.



Nice work! I’m very very curious about this application! Could you send it to me? :-P
Cheers,
Daniel.
Congrats on finishing the studies :)
Congratulations on finishing the studies. ;-) I’m also very interested on how you made your custom UI.
Looking forward to seeing your book as well!
Congratulations for your M.S.
Me too, i’m interested by your app., can you give the code to us ?
Definitely interested in seeing the code + app….a link to the source et al?
Congrats on your M.S.
Seems like this is your year of finishing things - Book’s done, studies’ done … care to share what’s next on the list? ;)
At last, studies finished! You were going to upset me with so many achievements and still a student :-)
I’m very interesting in your work. Please send me code example to complete my collections about your work.
thank you for this exelent blog
Félicitations Romain pour en avoir finit avec l’école !! ;o)
When do you start your internship at google ? I hope you will keep us informed about the things you’re doing there.
@+
hervé
Thanks everyone. I’ll post the source code as soon as possible. I will probably release only the code of the UI since the rest is very buggy and difficult to launch.
herve: I start on April 3! I’d love to keep you guys informed about my work there but I don’t even know myself what I’m gonna do there. I’m afraid I won’t be able to communicate about it :(
Thank you for shar your source code.
But if you need, i’m specially interested with the “remote desktop” oriented code (buggy or not)…
Reading the first paragraph I got very exited - could there finally be an implementation of the unified toolbar look in pure Java? That would have been extremely nice. But oh, only a mockup, what a pity… Maybe better to wait until Leopard anyway ;) The final outcome still looks very nice though, as aways.
Congrats for finishing your studies and best wishes for the future.
Cheers,
\o/ Congrats Romain!!! So, when are you coming back to work with us on Swing. ;-)
Great work, looking forward to the source! Of course many congratulations.
Its this kind of stuff that spurs innovation, sure post your code.
Also do you have a good link to a ‘unified toolbar’, cant seem to find something decent to explain it to me.
I suppose it is a Mac thing, personally I am for Linux - she is cuter…
http://blog.wired.com/cultofmac/2007/03/novell_launches.html
Romain, I’ve become a real fan of the work you do on GUIs and I’m definitely buying your book. Good luck at the big G.
P.S. still waiting for the code for this UI! :) j/k.
Hi,
this looks very nice. I wrote a similar TabbedPaneUI for our CRM-Application. Maybe you want to check it out. It even shows nice rollovers. Improvements are always welcome.
Regards,
Alex
Here is the source:
package de.webjazz.ui;
import javax.swing.*;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicTabbedPaneUI;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
public class AquaBarTabbedPaneUI extends BasicTabbedPaneUI {
private static final Insets NO_INSETS = new Insets(0, 0, 0, 0);
private ColorSet selectedColorSet;
private ColorSet defaultColorSet;
private ColorSet hoverColorSet;
private boolean contentTopBorderDrawn = true;
private Color lineColor = new Color(158, 158, 158);
private Color dividerColor = new Color(200, 200, 200);
private Insets contentInsets = new Insets(10,10,10,10);
private int lastRollOverTab = -1;
public static ComponentUI createUI(JComponent c) {
return new AquaBarTabbedPaneUI();
}
public AquaBarTabbedPaneUI() {
selectedColorSet = new ColorSet();
selectedColorSet.topGradColor1 = new Color(233, 237, 248);
selectedColorSet.topGradColor2 = new Color(158, 199, 240);
selectedColorSet.bottomGradColor1 = new Color(112, 173, 239);
selectedColorSet.bottomGradColor2 = new Color(183, 244, 253);
defaultColorSet = new ColorSet();
defaultColorSet.topGradColor1 = new Color(253, 253, 253);
defaultColorSet.topGradColor2 = new Color(237, 237, 237);
defaultColorSet.bottomGradColor1 = new Color(222, 222, 222);
defaultColorSet.bottomGradColor2 = new Color(255, 255, 255);
hoverColorSet = new ColorSet();
hoverColorSet.topGradColor1 = new Color(244, 244, 244);
hoverColorSet.topGradColor2 = new Color(223, 223, 223);
hoverColorSet.bottomGradColor1 = new Color(211, 211, 211);
hoverColorSet.bottomGradColor2 = new Color(235, 235, 235);
maxTabHeight = 20;
setContentInsets(0);
}
public void setContentTopBorderDrawn(boolean b) {
contentTopBorderDrawn = b;
}
public void setContentInsets(Insets i) {
contentInsets = i;
}
public void setContentInsets(int i) {
contentInsets = new Insets(i, i, i, i);
}
public int getTabRunCount(JTabbedPane pane) {
return 1;
}
protected void installDefaults() {
super.installDefaults();
RollOverListener l = new RollOverListener();
tabPane.addMouseListener(l);
tabPane.addMouseMotionListener(l);
tabAreaInsets = NO_INSETS;
tabInsets = new Insets(0,0,0,1);
}
protected boolean scrollableTabLayoutEnabled() {
return false;
}
protected Insets getContentBorderInsets(int tabPlacement) {
return contentInsets;
}
protected int calculateTabHeight(int tabPlacement, int tabIndex, int fontHeight) {
return 21;
}
protected int calculateTabWidth(int tabPlacement, int tabIndex, FontMetrics metrics) {
int w = super.calculateTabWidth(tabPlacement, tabIndex, metrics);
w += metrics.charWidth(’M') * 2;
return w;
}
protected int calculateMaxTabHeight(int tabPlacement) {
return 21;
}
protected void paintTabArea(Graphics g, int tabPlacement, int selectedIndex) {
Graphics2D g2d = (Graphics2D) g;
g2d.setPaint(new GradientPaint(0, 0, defaultColorSet.topGradColor1, 0, 10, defaultColorSet.topGradColor2));
g2d.fillRect(0, 0, tabPane.getWidth(), 10);
g2d.setPaint(new GradientPaint(0, 10, defaultColorSet.bottomGradColor1, 0, 21, defaultColorSet.bottomGradColor2));
g2d.fillRect(0, 10, tabPane.getWidth(), 11);
super.paintTabArea(g, tabPlacement, selectedIndex);
if (contentTopBorderDrawn) {
g2d.setColor(lineColor);
g2d.drawLine(0, 20, tabPane.getWidth() - 1, 20);
}
}
protected void paintTabBackground(Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h, boolean isSelected) {
Graphics2D g2d = (Graphics2D) g;
ColorSet colorSet;
Rectangle rect = rects[tabIndex];
if (isSelected) {
colorSet = selectedColorSet;
} else if(getRolloverTab() == tabIndex) {
colorSet = hoverColorSet;
} else {
colorSet = defaultColorSet;
}
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
int width = rect.width;
int xpos = rect.x;
if(tabIndex > 0){
width–;
xpos++;
}
g2d.setPaint(new GradientPaint(xpos, 0, colorSet.topGradColor1, xpos, 10, colorSet.topGradColor2));
g2d.fillRect(xpos, 0, width, 10);
g2d.setPaint(new GradientPaint(0, 10, colorSet.bottomGradColor1, 0, 21, colorSet.bottomGradColor2));
g2d.fillRect(xpos, 10, width, 11);
if (contentTopBorderDrawn) {
g2d.setColor(lineColor);
g2d.drawLine(rect.x, 20, rect.x + rect.width - 1, 20);
}
}
protected void paintTabBorder(Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h, boolean isSelected) {
Rectangle rect = getTabBounds(tabIndex, new Rectangle(x, y, w, h));
g.setColor(dividerColor);
g.drawLine(rect.x + rect.width, 0, rect.x + rect.width, 20);
}
protected void paintContentBorderTopEdge(Graphics g, int tabPlacement, int selectedIndex, int x, int y, int w, int h) {
}
protected void paintContentBorderRightEdge(Graphics g, int tabPlacement, int selectedIndex, int x, int y, int w, int h) {
// Do nothing
}
protected void paintContentBorderLeftEdge(Graphics g, int tabPlacement, int selectedIndex, int x, int y, int w, int h) {
// Do nothing
}
protected void paintContentBorderBottomEdge(Graphics g, int tabPlacement, int selectedIndex, int x, int y, int w, int h) {
// Do nothing
}
protected void paintFocusIndicator(Graphics g, int tabPlacement, Rectangle[] rects, int tabIndex, Rectangle iconRect, Rectangle textRect, boolean isSelected) {
// Do nothing
}
protected int getTabLabelShiftY(int tabPlacement, int tabIndex, boolean isSelected) {
return 0;
}
private class ColorSet {
Color topGradColor1;
Color topGradColor2;
Color bottomGradColor1;
Color bottomGradColor2;
}
private class RollOverListener implements MouseMotionListener, MouseListener {
public void mouseDragged(MouseEvent e) {
}
public void mouseMoved(MouseEvent e) {
checkRollOver();
}
public void mouseClicked(MouseEvent e) {
}
public void mousePressed(MouseEvent e) {
}
public void mouseReleased(MouseEvent e) {
}
public void mouseEntered(MouseEvent e) {
checkRollOver();
}
public void mouseExited(MouseEvent e) {
tabPane.repaint();
}
private void checkRollOver() {
int currentRollOver = getRolloverTab();
if (currentRollOver != lastRollOverTab) {
lastRollOverTab = currentRollOver;
Rectangle tabsRect = new Rectangle(0, 0, tabPane.getWidth(), 20);
tabPane.repaint(tabsRect);
}
}
}
}
Hi Romain,
I’ve been working on a way to get the unified tool look as in the mockup, as I wrote a similar looking application on Windows and when I moved it Mac OS X, I didn’t like the look at all. I should have it ready (time permitting) next week. I can share it with you if you want. :-)
Jon…
Jon: Sure, I’d love to see that. How did you proceed? Using native code, or using an undecorated JFrame?
Congratulations, Romain!
I used an JFrame with a custom JRootPaneUI. It’s still not perfect as there are some issues with flickering during resize and a lack of a drop shadow when used on a Mac, but it’s a start.
Romain,
Congrats,
vissu
bonjour,
je voudrais réaliser une interface pour un programme associatif. et j’aimerais vous poser une ou 2 questions .
est il possible de réaliser une application graphique java avec swinglabs ou autre sans qu’apparaisse les bords liés à l’OS (agrandissement,fermeture et le cadre) un peu comme un splash screen ?
quel logiciel de développement d’interface graphique en java conseiller vous pour exploiter swinglabs et toutes fonctionnalité de celui-ci ?
Merci d’avance
Well done Romain !
Congratulats Romain!
Thanks for your really nice swing works!
I really like the toolbar — can you post the source for the toolbar portion of the code? Also, where did those icons come from — are they free?
Thanks!
boxlight
Congrats on the degree. Now, go get em, Tiger.
I would definetely be happy to see your code. I am currently looking for doing exactly that kind of look in an swing application. Could you send me your code or post it somewhere? Many thanks and congrats for your studies
Romain,
Congratulations Romain! Can’t wait to see what you have next at JavaOne.
Dave
Well done!!!
keep going!!
Bravo Romain !
Bon courage chez Google.
The thing with this Internet is that people will always be in touch and we sure will continue to follow your work.
@Oliv
Essaie JFrame.setUndecorated(true)
Globalement lis le chapitre sur les JFrame et leurs décorations
Hi Romain,
First of all Congrates.
>Hope you will back to regular R&D kind of stuff in Visual effects.
>Ihave one Question for you.
>Hope you might have seen the JBookPanel in Java Desktop Community at the following link.
http://jroller.com/page/pieterjan?entry=jbookpanel_and_the_page_turn
>Can’t apply the page turn effect to our JComponents.
>It will much more beautiful to see if this kind of effect is applied to JPanels on back and next buttons clicks.
>To be more detailed :
>let us have 3 panels each panel contains back and next buttons .
>If I am on panel 1 and click next buttonPanel2 should be visible during each back and next buttons click I want to see Page turn kind of effect applied panels.
>I want to implement this with my JComponents .
>Can You suggest me how to proceed.
>Wish you could put your hands on it.
cheers,
Dhilshuk Reddy.
Congratulations on your graduation, Romain! Now come back to Sun :)
It may not look like a Swing GUI, but it doesn’t look like an OS X GUI either: the tabs are wrong.
If you were using native widgets, you wouldn’t even have to speculate about this. With Swing, this is always a guessing game.
@Mike: I think it never was the intention to recreate the OS X tabs. Hell, even “native” OS X apps use custom components more and more. Some of the work Romain is doing actually makes Swing a bit more bearable and easier to look at. ;-)
Romain, the bit I’m most interested in understanding is the scaling (via the slider). I know you can use g2d.scale(factor) on a Graphics2D object, but since Swing using pixels as it’s units, this seems to mess up mouse event co-ordinates, etc.
Is there a hack-free way to achive scaling of JComponents, including JContainers with children?
Cheers,
Bruce
PS: It’d be nice if you exposed the comments RSS feed on the site (I had to find it in the blog RSS).
Thanks everyone!
@Mike: The tabs are not wrong. I duplicated the tabs from the application called Transmit. This kind of tab is also used in many other places/applications (like Apple’s iWork palettes.)
xrtest
zrya ti tak
webmaster@excellservices.com
zrya ti tak
Givemepink Bambi
Mmf Cumswapping
George Washington Crossing The Delaware
Double Interracial Penetration
Dildo Squirt Movie
Free Hot Porn Xxx
Fisting Old
Purepov July
Panther Ridge Conservation Center
Fucking Mature Taboo
Old Tit Ugly
Spy Shop Toronto
Naruto Porn Sakura