-
Notifications
You must be signed in to change notification settings - Fork 235
Window decoration
Available since WebLaF v1.2.9 release, updated for WebLaF v1.2.12 release
Requires weblaf-ui module and Java 6 update 30 or any later to run
Up to this point I've seen a lot of questions about window decorations (either native or custom ones) - how does it work? how it could be configured and customized? why I cannot do X, Y and Z with the native decoration? how does custom decoration affect the performance of the application?.. etc.
So I want to summarize answers for all of those questions and more in this wiki article and also show some practical examples of custom window decoration using WebLaF styling system.
First of all you need to understand what window decoration is and how it works, especially when coupled with Swing window implementations, most commonly - JFrame
and JDialog
. When you create an instance of a JFrame
and display it without any specific settings - you will see it being decorated according to the native OS frame decoration. For example if you would launch this piece of code:
public class SimpleFrame
{
public static void main ( final String[] args )
{
CoreSwingUtils.invokeLater ( new Runnable ()
{
@Override
public void run ()
{
final JFrame frame = new JFrame ();
frame.setSize ( 200, 200 );
frame.setLocationRelativeTo ( null );
frame.setVisible ( true );
}
} );
}
}
you will see this result under Windows 8:
Result will always be different with a different OS:
because native frame decoration depends on the system you are running it on and system window manager settings.
Because it is a native decoration you do not have any real access to its visual representation settings except maybe just a few options which might affect it (but not always do):
-
setType(Window.Type)
- Changes window type, usually affect window visual appearance and behavior -
setResizable(boolean)
- Makes frame/dialog resizable or non-resizable, usually affects window buttons and borders -
setTitle(String)
- Changes frame/dialog title usually displayed in window header
There is also one more really important setting I would like to mention separately:
-
setUndecorated(boolean)
- Disables or enables native frame/dialog decoration
This is exactly the setting that allows you to provide custom window decorations in Swing. "How would disabling decoration will help us to configure it?" - you would ask, - and the answer is simple: disable native decoration and make your own using graphics tools Swing gives us! That is exactly how all of the custom window decorations work in different Swing Look and Feels, including WebLaF.
As soon as you disable native decoration:
public class SimpleFrame
{
public static void main ( final String[] args )
{
CoreSwingUtils.invokeLater ( new Runnable ()
{
@Override
public void run ()
{
final JFrame frame = new JFrame ();
frame.setUndecorated ( true );
frame.setSize ( 200, 200 );
frame.setLocationRelativeTo ( null );
frame.setVisible ( true );
}
} );
}
}
you are left out with a flat square window floating on the screen:
What you see there is pretty much the JRootPane
and its content pane with some gray background placed in the frame. That JRootPane
is exactly what you want to configure to make your own custom window decoration.
Normally you would need to create a custom RootPaneUI
implementation, provide all of the components like window title and buttons manually, but WebLaF does all that work for you and only leaves the actual styling process to you without getting you too deep into additional window configurations needed like opaque
and such. With WebLaF you actually don't even need to make the frame/dialog undecorated - it will automatically be configured upon WebLaF style installation.
You should avoid making window displayable before setting up its style, it is really important to avoid any kinds of issues. Window becomes displayable when its native peer initializes, which might occur from various window method calls, for example calling window.pack()
would initialize window peer.
Let's try adding some basic frame styling instead of the default one:
<skin xmlns="http://weblookandfeel.com/XmlSkinExtension">
<!-- Extension information -->
<id>my.extension</id>
<extends>weblaf.light.skin</extends>
<!-- Decorated frame -->
<style type="rootpane" id="dark" extends="frame-decorated">
<painter>
<decorations>
<decoration>
<WebShape round="0" />
<WebShadow type="outer" opacity="0.3" width="40" />
<LineBorder color="20,20,20" />
<ColorBackground color="68,68,68" />
</decoration>
</decorations>
</painter>
</style>
</skin>
And the small launch class:
public class SimpleDecoration
{
public static void main ( final String[] args )
{
// Installing L&F
WebLookAndFeel.install ();
// Installing our extension for default skin
StyleManager.addExtensions ( new XmlSkinExtension (
new ClassResource ( SimpleDecoration.class, "SimpleExtension.xml" )
) );
// Creating decorated frame
final WebFrame frame = new WebFrame ( StyleId.of ( "dark" ), "Dark frame" );
frame.setDefaultCloseOperation ( WindowConstants.EXIT_ON_CLOSE );
frame.setSize ( 400, 300 );
frame.setLocationRelativeTo ( null );
frame.setVisible ( true );
}
}
I used WebFrame
here instead of JFrame
for convenience, but you can provide styles into base Swing components as well, check out this article for more information.
Let's have a look at the result:
Not too bad, but we still have title and buttons from the light skin. Let's switch our extension to the dark skin:
<skin xmlns="http://weblookandfeel.com/XmlSkinExtension">
<!-- Extension information -->
<id>my.extension</id>
<extends>weblaf.dark.skin</extends>
...
</skin>
And ask our application to use dark skin by default:
WebLookAndFeel.install ( WebDarkSkin.class );
And have a look at the frame again:
Now that is already something!
Now let's add a bit of customization, for example round diagonal corners - for that we will need to slightly modify window shape in our extension:
<WebShape round="15,0,15,0" />
And the result can be observed right away:
Through the style you can also gain access to the decoration elements:
<skin xmlns="http://weblookandfeel.com/XmlSkinExtension">
<!-- Extension information -->
<id>my.extension</id>
<extends>weblaf.dark.skin</extends>
<!-- Decorated frame -->
<style type="rootpane" id="dark" extends="frame-decorated">
<painter>
<decorations>
<decoration>
<WebShape round="15,0,15,0" />
<WebShadow type="outer" opacity="0.3" width="40" />
<LineBorder color="20,20,20" />
<ColorBackground color="68,68,68" />
</decoration>
</decorations>
</painter>
<!-- Frame buttons panel -->
<style type="panel" id="buttons">
<!-- Close button -->
<style type="button" id="close">
<painter>
<decorations>
<decoration>
<GradientBackground>
<color>140,50,50</color>
<color>110,50,50</color>
</GradientBackground>
</decoration>
</decorations>
</painter>
</style>
</style>
<!-- Content panel -->
<style type="panel" id="content" extends="transparent" />
</style>
</skin>
This will change close button background to be dark-red and the result will look like this:
You can also change all buttons at once overriding the intermediate default frame decoration style:
<!-- Control buttons -->
<style type="button" id="control">
<painter>
<decorations>
<decoration>
<GradientBackground>
<color>50,70,130</color>
<color>50,70,90</color>
</GradientBackground>
</decoration>
</decorations>
</painter>
</style>
This should be replacing close
style from above and the result will be:
You can also combine both style overrides:
<skin xmlns="http://weblookandfeel.com/XmlSkinExtension">
<!-- Extension information -->
<id>my.extension</id>
<extends>weblaf.dark.skin</extends>
<!-- Decorated frame -->
<style type="rootpane" id="dark" extends="frame-decorated">
<painter>
<decorations>
<decoration>
<WebShape round="15,0,15,0" />
<WebShadow type="outer" opacity="0.3" width="40" />
<LineBorder color="20,20,20" />
<ColorBackground color="68,68,68" />
</decoration>
</decorations>
</painter>
<!-- Frame buttons panel -->
<style type="panel" id="buttons">
<!-- Control buttons -->
<style type="button" id="control">
<painter>
<decorations>
<decoration>
<GradientBackground>
<color>50,70,130</color>
<color>50,70,90</color>
</GradientBackground>
</decoration>
</decorations>
</painter>
</style>
<style type="button" id="close">
<painter>
<decorations>
<decoration>
<GradientBackground>
<color>140,50,50</color>
<color>110,50,50</color>
</GradientBackground>
</decoration>
</decorations>
</painter>
</style>
</style>
<!-- Content panel -->
<style type="panel" id="content" extends="transparent" />
</style>
</skin>
to achieve an expected result:
To wrap up with this example let's put some components onto the frame to create a complete UI piece:
public class SimpleDecoration
{
public static void main ( final String[] args )
{
CoreSwingUtils.invokeLater ( new Runnable ()
{
@Override
public void run ()
{
// Installing L&F
WebLookAndFeel.install ( WebDarkSkin.class );
// Installing our extension for default skin
StyleManager.addExtensions ( new XmlSkinExtension (
new ClassResource ( SimpleDecoration.class, "SimpleExtension.xml" )
) );
// Creating decorated frame
final WebFrame frame = new WebFrame ( StyleId.of ( "dark" ), "Authorize" );
frame.setLayout ( new FormLayout ( 10, 5 ) );
frame.add ( new WebLabel ( StyleId.of ( "form", frame ), "Login:" ) );
frame.add ( new WebTextField ( 15 ) );
frame.add ( new WebLabel ( StyleId.of ( "form", frame ), "Password:" ) );
frame.add ( new WebPasswordField ( 15 ) );
final WebPanel buttons = new WebPanel ( new FlowLayout ( FlowLayout.CENTER, 5, 0 ) );
buttons.add ( new WebButton ( StyleId.of ( "form", frame ), "Enter" ) );
buttons.add ( new WebButton ( StyleId.of ( "form", frame ), "Exit" ) );
frame.add ( buttons.equalizeComponentsWidth (), FormLayout.LINE );
frame.setDefaultCloseOperation ( WindowConstants.EXIT_ON_CLOSE );
frame.pack ();
frame.setLocationRelativeTo ( null );
frame.setVisible ( true );
}
} );
}
}
And also add a few styles and improvements:
<skin xmlns="http://weblookandfeel.com/XmlSkinExtension">
<!-- Extension information -->
<id>my.extension</id>
<extends>weblaf.dark.skin</extends>
<!-- Decorated frame -->
<style type="rootpane" id="dark" extends="frame-decorated">
<painter>
<decorations>
<decoration>
<WebShape round="15,0,15,0" />
<WebShadow type="outer" opacity="0.3" width="40" />
<LineBorder color="20,20,20" />
<ColorBackground color="68,68,68" />
</decoration>
</decorations>
</painter>
<!-- Frame buttons panel -->
<style type="panel" id="buttons">
<!-- Control buttons -->
<style type="button" id="control">
<painter>
<decorations>
<decoration>
<GradientBackground>
<color>50,70,130</color>
<color>50,70,90</color>
</GradientBackground>
</decoration>
</decorations>
</painter>
</style>
<style type="button" id="close">
<painter>
<decorations>
<decoration>
<GradientBackground>
<color>140,50,50</color>
<color>110,50,50</color>
</GradientBackground>
</decoration>
</decorations>
</painter>
</style>
</style>
<!-- Content panel -->
<style type="panel" id="content" padding="20,40,20,40" extends="transparent" />
<!-- Form elements -->
<style type="label" id="form" extends="shadow" />
<style type="button" id="form" padding="3,15,3,15" />
</style>
</skin>
I have added additional padding to "content" style, which is applied to frame's content pane automatically, and also two "form" styles for labels and buttons in my form to add shadow and paddings. Nothing special, just making it look slightly better.
And the final result is:
That should be the bare minimum required to start bringing your own custom decorations to live, but keep in mind that any styling process is trial and error - you might end up spending much more time on picking the right colors, spacings and shapes rather than writing actual style/java code :)
After trying some basic decoration options offered by WebLaF you will probably end up with a question "Is that all I can do with this system?" - and the answer is of course "no!". There is no actual limit to the possible styling options, but you might have to come up with your own unique solutions in most cases, for example use some textures for background, provide custom shape or shadow etc.
To actually wrap this topic up I want to show you a small decoration I made for one of my personal apps. Just so you know - it took around 20 minutes to implement, though I already had the idea of this decoration for quite a while.
First of all, I needed some awesome background for my frame, so I asked Google and found this piece of art:
So I took these 5 megabytes of awesomeness and put into a tricky background implementation:
@XStreamAlias ( "GalaxyBackground" )
public class GalaxyBackground<E extends JComponent, D extends IDecoration<E, D>, I extends AbstractBackground<E, D, I>>
extends AbstractBackground<E, D, I>
{
/**
* Custom background image.
*/
public static final ImageIcon bg = ImageUtils.loadImageIcon (
new ClassResource ( GalaxyBackground.class, "background.gif" )
);
@Override
public void paint ( final Graphics2D g2d, final Rectangle bounds, final E c, final D d, final Shape shape )
{
// Fill-in the shape, color doesn't matter
g2d.fill ( shape );
// Drawing background image with SRC_IN composite
final Composite oc = g2d.getComposite ();
g2d.setComposite ( AlphaComposite.getInstance ( AlphaComposite.SRC_IN ) );
g2d.drawImage ( bg.getImage (), 0, 0, c );
g2d.setComposite ( oc );
}
}
Created a small sample frame:
public class MyFrame extends WebFrame
{
public MyFrame ()
{
super ( StyleId.of ( "galaxy" ), "Custom frame" );
setIconImages ( WebLookAndFeel.getImages () );
StyleId.panelTransparent.set ( ( JComponent ) getContentPane () );
final WebPanel container = new WebPanel ( StyleId.panelTransparent, new AlignLayout () );
add ( container );
final ComponentMoveBehavior moveBehavior = new ComponentMoveBehavior ( container );
moveBehavior.install ();
final WebPanel form = new WebPanel ( StyleId.panelTransparent, new FormLayout ( false, false, 10, 10 ) );
container.add ( form, SwingConstants.CENTER + "," + SwingConstants.CENTER );
final WebLabel firstLine = new WebLabel ( StyleId.labelShadow, "Here is our small fancy frame", WebLabel.CENTER );
form.add ( firstLine.changeFontSize ( 2 ).setBoldFont (), FormLayout.LINE );
final WebLabel secondLine = new WebLabel ( StyleId.labelShadow, "Created by Mikle Garin", WebLabel.CENTER );
form.add ( secondLine.changeFontSize ( 1 ).setBoldFont (), FormLayout.LINE );
setResizable ( false );
setDefaultCloseOperation ( WindowConstants.EXIT_ON_CLOSE );
setSize ( GalaxyBackground.bg.getIconWidth (), GalaxyBackground.bg.getIconHeight () );
setLocationRelativeTo ( null );
}
public static void main ( final String[] args )
{
CoreSwingUtils.invokeLater ( new Runnable ()
{
@Override
public void run ()
{
// Process our custom alias first
XmlUtils.processAnnotations ( GalaxyBackground.class );
// Initializing L&F
WebLookAndFeel.install ( WebDarkSkin.class );
StyleManager.addExtensions ( new XmlSkinExtension (
new ClassResource ( MyFrame.class, "MyExtension.xml" )
) );
// Starting application
final MyFrame app = new MyFrame ();
app.setVisible ( true );
}
} );
}
}
Note that the size of frame shouldn't be larger than background image size, otherwise you will get ugly artifacts on the sides. I set a fixed size to make sure that doesn't happen in the example.
And last but not least - custom frame style put into dark skin extension:
<skin xmlns="http://weblookandfeel.com/XmlSkinExtension">
<!-- Extension information -->
<id>my.extension</id>
<extends>weblaf.dark.skin</extends>
<!-- WebLaF-decorated frame -->
<style type="rootpane" id="galaxy" extends="frame-decorated">
<painter>
<decorations>
<decoration>
<WebShadow type="outer" opacity="0.5" width="50" />
<LineBorder color="10,10,10" />
<GalaxyBackground />
</decoration>
<decoration states="focused">
<WebShadow type="outer" opacity="0.8" />
</decoration>
</decorations>
</painter>
<!-- Title panel -->
<style type="panel" id="title">
<!-- Title label -->
<style type="label" id="title" extends="shadow">
<component>
<foreground>black</foreground>
</component>
<painter>
<decorations>
<decoration>
<LabelLayout>
<LabelText shadow="true" shadowColor="210,210,210" shadowSize="3" />
</LabelLayout>
</decoration>
</decorations>
</painter>
</style>
</style>
</style>
</skin>
And here is the great result of graphics magic:
This image is static though, try running the example to see it in action ;)
Now when you know how to decorate windows using WebLaF you might still have a few questions left related to custom window decorations, so I will answer some of the most frequently asked ones here:
What are pros and cons of using custom window decoration?
I would like to answer this one first as it leads to pretty much all other questions around custom window decorations. So here we go:
Pros
- Best integration with application look and feel
- Unified application look and feel between different systems
- Possibility to extend or remove some window decoration features
- Possibility of seamless application UI integration into window decoration
- Possibility to have multiple window decoration styles
Cons
- Minor UI performance losses in some specific cases
- Missing window transparency mode support on some Linux system
- Missing subpixel text rendering support on transparent windows on all systems
- Window decoration disjointed from the native system look and feel
- Missing some native decoration features like snapping to screen sides to maximize
- Non lag-free window resize unlike native decoration resize
To summ up - custom window decoration might give your application a completely unique look and feel which sometimes is almost necessary to emphasize the application goals, for example have a look at gaming platform applications like Steam or Uplay - mostly all of those applications use customized window and elements decorations, but sometimes it is important to preserve native window decoration along with more or less native UI elements look and feel to make sure user can easily adapt to your application and will feel comfortable using it.
Does custom window decoration affect application performance?
Both yes and no. The thing is - in most cases you won't see big or any difference at all while running your application with a custom window decoration compared to native one.
The only big visual difference would probably be the resize of the window - native decoration usually doesn't have any visual "lags", but custom decoration is not updated as frequently and therefore will make an impression of being "laggy" while it is resized.
Performance issues will only appear if your whole application is semi-transparent and has a lot of UI elements - that would cause a lot of extra repaint calls on all parent components when a top-level element has to be repainted because it is transparent (non-opaque) as well as all of its parents - that can be easily addressed by setting your window content pane opaque
to true
. You have to be careful with it though to avoid repainting issues.
Possible solutions:
-
Make sure that you have performance issues only with custom window decoration enabled and that without it everything works perfectly fine. If not - you probably need to fix something else first before optimizing window decorations.
-
You can always find out which part of the UI takes longer to paint using either
MagnifierGlass
or any other measurement tool from other libraries. That will help you a lot to find out the source of performance loss if it is painting-related. -
Make sure that you have as many opaque elements on your interface as possible (with opaque setting set to
true
either manually or in the component style) - that will dramatically decrease amount of repaints your UI suffers when any separate component like button or a label decides to repaint itself. Current WebLaF styles mostly go against this to provide usage convenience instead.
Will my custom decoration work on any OS?
Theoretically it can, but most probably it won't. It will certainly run well on any Windows version and most probably on any Mac OS X version. Problems will start when you will try to run it on any Linux system, for example Ubuntu. Since features like window opacity heavily depend on their native implementation - they might cause graphical glitches on systems where they are not supported, not properly supported or not supported in JDK.
Most probably you would have to create a rectangular-shaped fallback style for your window decoration for the Linux systems (like WebLaF does) to avoid your application being unusable under those OS. There are OS-related decoration states available for that case: win
, mac
, unix
and solaris
.
Possible solutions:
-
Even though window opacity might not be supported on some Linux systems you can still do awesome rectangular-shaped custom decorations - simply do not use shadows and shape rounding and you will keep the general window shape rectangular. On top of that you can provide any kinds of decoration.
-
Simply don't use custom decorations. If you really want your application to run well on Linux systems you might either go with the simplified styling or disable it completely there. That is the option I generally stick with with my own Swing-based applications.
Why any text on a window with custom decoration doesn't look as crisp as it does on natively-decorated window?
This is a really old issue with text rendering in Swing. Commonly text is rendered using a native subpixel rendering mechanism which makes even a thin text look sharp and crisp. But that mechanism is getting disabled if underlying image that provides graphics is non-opaque or to put it simple - doesn't have a preset background under the text. I wouldn't go too deep into explanation, but there is currently no workaround or fix for that issue as it is heavily related to the way Swing renders window content.
So how is that related to custom window decoration? - Pretty simple, every Swing window uses a buffer image to paint its content on before passing it further to the system and in some cases with custom decoration - specifically when you make your window non-opaque - window buffer image becomes non-opaque causing the subpixel rendering mechanism to halt.
That is also the reason why WebLaF menus might have a different font rendering depending on where it is displayed - when menu gets outside of the window bounds it is displayed on a separate non-opaque window causing changes to the text rendering.
So you have to be aware that for the pretty custom window decoration you will have to sacrifice native text subpixel rendering. Though it isn't a big problem if your font is not too thin - common Swing grayscale text antialias would look just as good on a text with a thick font. So in some cases that might be a good trade-off.
Possible solutions:
- The best option right now is to find a font for your application that will work decent with native subpixel and Swing grayscale text antialias. There is no good direct solution for this problem so far, I tried lots of options, even crazy things like painting every single line of text on an opaque buffer image before rendering it onto the component, - none of them worth it.
If you found any mistakes or inconsistency in this article, feel that it is lacking explanation or simply want to request an additional wiki article covering some topic:
I will do my best to answer and provide assistance as soon as possible!