Skip to content

How to use IconManager

Mikle edited this page Jan 30, 2020 · 4 revisions

Available since WebLaF v1.2.11 release, updated for WebLaF v1.2.12 release
Requires weblaf-ui module and Java 6 update 30 or any later to run


What is it for?

Managing icon resources is always a problem that requires some sort of solution. It might not be a problem initially or for very small projects, but once you get to the point of having hundreds of icons scattered across your codebase and/or resources - it becomes impossible to maintain.

WebLaF offers it's own solution for that problem - IconManager that can be used to manage skin/extension-related or global icon sets. Each icon set can contain a list of icons with their own unique identifiers through which those icons can be easily referenced and retrieved in the code. Where you store actual icon resources, how you group them by icon sets and how you name them is completely up to you.

There is one more thing that IconManager supports - overriding previously added icons. This is an important feature because it allows you to replace even icons that are used internally by WebLaF itself in various components like JFileChooser.


How to use it?

There are a few important classes you will be working with:

  • IconSource - interface that represents source for actual Icon.
    Currently there are two implementations available: ImageIconSource and SvgIconSource.

  • IconSet - interface that represent different available icon set implementations.
    Currently there are two implementations available: XmlIconSet and RuntimeIconSet.

  • IconManager can be used to add/remove IconSets in runtime and retrieve actual Icons.

The whole thing is pretty simple - IconSources are grouped in IconSets which in turn are added to IconManager which provides global access to any icons from those sets. Actual icons are only loaded on demand so you can add as many icons as you want and it won't affect performance in any noticeable way until you actually request or use them.

Now that you know the basics - let's move to more practical examples.

Creating IconSet from code

For creating IconSet in runtime you can use RuntimeIconSet implementation:

final IconSet iconSet = new RuntimeIconSet ( "my-custom-set" );

The only thing you need to provide in it is a custom identifier that can be used to find that set in IconManager later on. Each IconSet added to IconManager must have unique identifier, otherwise you will receive an exception.

Next step - you can add IconSources to that IconSet:

iconSet.addIcon ( new SvgIconSource (
        "github-logo",
        new UrlResource ( "https://github.com/mgarin/weblaf/raw/master/resources/github.svg" )
) );
iconSet.addIcon ( new SvgIconSource (
        "firefox-logo",
        new UrlResource ( "https://github.com/mgarin/weblaf/raw/master/resources/firefox.svg" )
) );
iconSet.addIcon ( new ImageIconSource (
        "weblaf-logo",
        new UrlResource ( "https://github.com/mgarin/weblaf/raw/master/resources/weblaf.png" )
) );

Note: I've used existing URL addresses:
https://github.com/mgarin/weblaf/raw/master/resources/github.svg
https://github.com/mgarin/weblaf/raw/master/resources/firefox.svg
https://github.com/mgarin/weblaf/raw/master/resources/weblaf.png
So you can actually use those icons for testing.

There two reasons why IconSet contains IconSource and not final Icons:

  • Performance - having an actual Icon in memory often means it has to be loaded
  • Convenience - in case you want to modify some icon you can load it as many times as needed through the source

Now that you have actual icons in your icon set - we can add it into the IconManager:

IconManager.addIconSet ( iconSet );

That's it, now your icons are populated and available through LazyIcon implementation anywhere in the code.

Note: You can still add new icons to your icon set after you've added it to IconManager and they will become available as well, it's just a good practice to add icons first as this will cause less potential updates.

Now, there are a few ways how you can access your icons from code:

  1. Directly through IconManager:
final Icon icon = IconManager.getIcon ( "github-logo" );
  1. Through LazyIcon:
final Icon icon = new LazyIcon ( "firefox-logo" );
  1. In XML styles you can use it through SetIcon content:
    (for more information on styling overall you might want to read this article first)
<SetIcon icon="weblaf-logo" />

You can also access original Icon type:

final SvgIcon icon = IconManager.getIcon ( "github-logo" );
final SvgIcon icon = new LazyIcon ( "firefox-logo" ).getIcon ();
final ImageIcon icon = new LazyIcon ( "weblaf-logo" ).getIcon ();

Although generally it shouldn't be necessary in most cases when you are working with Swing and you can simply use LazyIcon - it will be both easier to use and faster on performance side because it only loads actual Icon when necessary.

Using IconSet with Skin and SkinExtension

Now let's say you want to have an IconSet that is attached to existing Skin or even your own custom Skin. The reason for having one is pretty simple - it will be automatically added and removed as the application Skin is changed. This allows you to swap between different icons for different skins quite easily - this is pretty much what WebLaF already does for it's own skins internally.

Let's look at each of two cases separately.

Attaching IconSet to existing skin

In case you have a small application that doesn't have it's own skin (or maybe even custom styles), but you want to provide separate icon sets for existing WebLightSkin and WebDarkSkin - you will need to create two extensions that each include one of your custom IconSets.

To avoid a hassle of creating XmlSkinExtension override class and empty XML for it with only icon set include(s) I've added new SkinExtension implementation specifically for this case - IconSetExtension. We will be using it:

final SkinExtension lightExtension = new IconSetExtension ( "my-custom-icon-set", "weblaf.light.skin", lightIconSet );
final SkinExtension darkExtension = new IconSetExtension ( "my-custom-icon-set", "weblaf.dark.skin", darkIconSet );

A few important thing to note here:

  • I'm using the same identifier for both IconSetExtension s because they will never be used together since they support different skins that will never be crossed
  • I'm using only one skin supported for each IconSetExtension, but you can specify multiple ones
  • I'm using only one IconSet for each IconSetExtension, but you can specify multiple ones

Once we have these two extensions - all we need is to register them in StyleManager:

StyleManager.addExtensions ( lightExtension, darkExtension );

They will be automatically used by the skins that are supported, either right away or when new supported skin is installed as current one in StyleManager.

One important thing to mention here - you can still modify icons in your IconSets at any point and even if they are currently used - those changes will instantly become available.

Attaching IconSet to custom Skin or SkinExtension

If you aren't sure about how custom Skins and SkinExtensions can be created - I strongly recommend reading Styling introduction wiki article first and then come back to this part.

So once you have your own Skin and XML that comes with it - this is a rather simple task. You can specify any custom IconSet class as include in your Skin's XML, like the default one is specified in WebLightSkin:

<iconSet>com.alee.iconset.LightIconSet</iconSet>

All you need for your custom MyIconSet class to work as include - it needs to be of IconSet type and it needs to have an empty constructor which will be used for it's loading when Skin is initialized.

Once you have specified your custom IconSet include in Skin or SkinExtension - it will be used automatically whenever those are active. So generally this is a better approach compared to specifying IconSets in the code, but only if you are expecting your project to grow in size, otherwise more simple previous approach might be worth it.

Replacing existing icons

Now to the last topic that haven't been covered yet - how can existing icons be replaced? For instance WebLaF uses lots of custom icons (check Icons.java) for various UI elements and all of them can be replaced if you don't like or want to customize some of them.

To replace any existing icon all you need to do is provide another one with the same identifier - any "user"-added icon sets and their icons will have priority over skin and extension icon sets. Extension icon sets and their icons will have priority over skin icon sets. The rest is up to the order of icon set addition

For instance, here is a small example with RuntimeIconSet use and DemoApplication:

// Creating custom IconSet
final IconSet iconSet = new RuntimeIconSet ( "my-custom-set" );

// Adding replacement icon
iconSet.addIcon ( new SvgIconSource (
        "github19",
        new UrlResource ( "https://github.com/mgarin/weblaf/raw/master/resources/firefox.svg" ),
        new Dimension ( 19, 19 )
) );

// Registering new IconSetExtension with our custom IconSet for default light skin
StyleManager.addExtensions ( new IconSetExtension ( 
        "my-custom-icon-set", 
        "weblaf.light.skin", 
        iconSet 
) );

DemoApplication uses icon with github19 identifier which I am replacing and it forces IconManager to provide a different Icon for that particular use case in DemoApplication:

Replaced icon

Original icon:

Original icon

This approach works for IconSets provided through Skins and SkinExtensions as well.